Inheritance: IConfigNode
Exemple #1
0
        void AddTank(FuelTank tank)
        {
            GUILayout.Label(addLabelCache[tank.name], GUILayout.Width(150));

            if (GUILayout.Button("Add", GUILayout.Width(120)))
            {
                tank.maxAmount = tank_module.AvailableVolume * tank.utilization;
                tank.amount    = tank.fillable ? tank.maxAmount : 0;

                tank.maxAmountExpression = tank.maxAmount.ToString();
                GameEvents.onEditorShipModified.Fire(EditorLogic.fetch.ship);
                tank_module.MarkWindowDirty();
                //Debug.LogWarning ("[MFT] Adding tank " + tank.name + " maxAmount: " + tank.maxAmountExpression ?? "null");
            }
        }
        protected void ChangeResources(double volumeRatio, bool propagate = false)
        {
            // The used volume will rescale automatically when setting maxAmount
            for (int i = 0; i < tankList.Count; i++)
            {
                FuelTank tank = tankList[i];

                bool save_propagate = tank.propagate;
                tank.propagate = propagate;

                tank.maxAmount *= volumeRatio;

                tank.propagate = save_propagate;
            }
        }
        void AddTank(FuelTank tank)
        {
            string extraData = "Max: " + (tank_module.AvailableVolume * tank.utilization).ToStringExt("S3") + "L (+" + ModuleFuelTanks.FormatMass((float)(tank_module.AvailableVolume * tank.mass)) + " )";

            GUILayout.Label(extraData, GUILayout.Width(150));

            if (GUILayout.Button("Add", GUILayout.Width(120)))
            {
                tank.maxAmount = tank_module.AvailableVolume * tank.utilization;
                tank.amount    = tank.fillable ? tank.maxAmount : 0;

                tank.maxAmountExpression = tank.maxAmount.ToString();
                //Debug.LogWarning ("[MFT] Adding tank " + tank.name + " maxAmount: " + tank.maxAmountExpression ?? "null");
            }
        }
Exemple #4
0
        private bool CalculateLowestTankTemperature()
        {
            bool result = false;

            lowestTankTemperature = 300;
            for (int i = tankList.Count - 1; i >= 0; --i)
            {
                FuelTank tank = tankList[i];
                if (tank.maxAmount > 0d && (tank.vsp > 0.0 || tank.loss_rate > 0d))
                {
                    lowestTankTemperature = Math.Min(lowestTankTemperature, tank.temperature);
                    result = true;
                }
            }
            return(result);
        }
        void RecordTankTypeResources(HashSet <string> resources, string type)
        {
            TankDefinition def;

            if (!MFSSettings.tankDefinitions.Contains(type))
            {
                return;
            }
            def = MFSSettings.tankDefinitions[type];

            for (int i = 0; i < def.tankList.Count; i++)
            {
                FuelTank tank = def.tankList[i];
                resources.Add(tank.name);
            }
        }
Exemple #6
0
        partial void OnStartRF(StartState state)
        {
            base.OnStart(state);

            GameEvents.onVesselWasModified.Add(OnVesselWasModified);
            GameEvents.onPartDestroyed.Add(OnPartDestroyed);
            if (HighLogic.LoadedSceneIsFlight)
            {
                for (int i = 0; i < vessel.vesselModules.Count; i++)
                {
                    if (vessel.vesselModules[i] is FlightIntegrator)
                    {
                        _flightIntegrator = vessel.vesselModules[i] as FlightIntegrator;
                    }
                }
            }


            CalculateTankArea(out totalTankArea);

            if (outerInsulationFactor > 0.0)
            {
                // TODO Deprecated! Leave in place for legacy purposes but this is moving back to part.skinInternalConductionMult based on calculated Lockheed MLI equations
                // changed from skin-internal to part.heatConductivity which affects only skin-internal
                // part.heatConductivity = Math.Min(part.heatConductivity, outerInsulationFactor);
                // affects how fast internal temperatures change during analytic mode
                // part.analyticInternalInsulationFactor *= outerInsulationFactor;
            }
            for (int i = tankList.Count - 1; i >= 0; --i)
            {
                FuelTank tank = tankList[i];
                if (tank.maxAmount > 0.0 && (tank.vsp > 0.0 || tank.loss_rate > 0.0))
                {
                    supportsBoiloff = true;
                    break;
                }
            }

            Fields[nameof(debug0Display)].guiActive = RFSettings.Instance.debugBoilOff && this.supportsBoiloff;
            Fields[nameof(debug1Display)].guiActive = RFSettings.Instance.debugBoilOff && this.supportsBoiloff;
            Fields[nameof(debug2Display)].guiActive = RFSettings.Instance.debugBoilOff && this.supportsBoiloff;

            //numberOfAddedMLILayers = Mathf.Round(numberOfAddedMLILayers);
            CalculateInsulation();
        }
Exemple #7
0
        public void CalculateTankArea(out float totalTankArea)
        {
            totalTankArea = 0f;

            for (int i = 0; i < 6; ++i)
            {
                totalTankArea += part.DragCubes.WeightedArea[i];
            }
#if DEBUG
            Debug.Log("[RF] Part WeightedArea: " + part.name + " = " + totalTankArea.ToString("F2"));
            Debug.Log("[RF] Part Area: " + part.name + " = " + part.DragCubes.Area.ToString("F2"));
#endif
            // This allows a rough guess as to individual tank surface area based on ratio of tank volume to total volume but it breaks down at very small fractions
            // So use greater of spherical calculation and tank ratio of total area.
            if (totalTankArea > 0.0)
            {
                double tankMaxAmount;
                for (int i = tankList.Count - 1; i >= 0; --i)
                {
                    FuelTank tank = tankList[i];
                    if (tank.maxAmount > 0.0)
                    {
                        tankMaxAmount = tank.maxAmount;

                        if (tank.utilization > 1.0)
                        {
                            tankMaxAmount /= utilization;
                        }

                        tank.tankRatio = tankMaxAmount / volume;

                        tank.totalArea = Math.Max(Math.Pow(Math.PI, 1.0 / 3.0) * Math.Pow((tankMaxAmount / 1000.0) * 6, 2.0 / 3.0), tank.totalArea = totalTankArea * tank.tankRatio);

                        if (RFSettings.Instance.debugBoilOff)
                        {
                            Debug.Log("[RF] " + tank.name + ".tankRatio = " + tank.tankRatio.ToString());
                            Debug.Log("[RF] " + tank.name + ".maxAmount = " + tankMaxAmount.ToString());
                            Debug.Log("[RF] " + part.name + ".totalTankArea = " + totalTankArea.ToString());
                            Debug.Log("[RF] Tank surface area = " + tank.totalArea.ToString());
                        }
                    }
                }
            }
        }
        internal FuelTank CreateCopy(ModuleFuelTanks toModule, ConfigNode overNode, bool initializeAmounts)
        {
            FuelTank clone = (FuelTank)MemberwiseClone();

            clone.module = toModule;

            if (overNode != null)
            {
                clone.Load(overNode);
            }
            if (initializeAmounts)
            {
                clone.InitializeAmounts();
            }
            else
            {
                clone.amountExpression = clone.maxAmountExpression = null;
            }
            return(clone);
        }
        public override string GetInfo()
        {
            if (!compatible)
            {
                return("");
            }

            UpdateTankType();

            StringBuilder info = new StringBuilder();

            info.AppendLine("Modular Fuel Tank:");
            info.Append("	Max Volume: ").AppendLine(KSPUtil.PrintSI(volume, MFSSettings.unitLabel));
            info.AppendLine("	Tank can hold:");
            for (int i = 0; i < tankList.Count; i++)
            {
                FuelTank tank = tankList[i];
                info.Append("		").Append(tank).Append(" ").AppendLine(tank.note);
            }
            return(info.ToString());
        }
Exemple #10
0
        void EditTank(FuelTank tank)
        {
            GUILayout.Label(" ", GUILayout.Width(5));

            GUIStyle style = unchanged;

            if (tank.maxAmountExpression == null)
            {
                tank.maxAmountExpression = tank.maxAmount.ToString();
                //Debug.LogWarning ("[MFT] Adding tank from API " + tank.name + " amount: " + tank.maxAmountExpression ?? "null");
            }
            else if (tank.maxAmountExpression.Length > 0 && tank.maxAmountExpression != tank.maxAmount.ToString())
            {
                style = changed;
            }

            tank.maxAmountExpression = GUILayout.TextField(tank.maxAmountExpression, style, GUILayout.Width(127));

            UpdateTank(tank);
            RemoveTank(tank);
        }
        private void CalculateTankLossFunction(double deltaTime)
        {
            for (int i = 0; i < tankList.Count; i++)
            {
                FuelTank tank = tankList[i];

                if (tank.loss_rate > 0 && tank.amount > 0)
                {
                    double deltaTemp = part.temperature - tank.temperature;
                    if (deltaTemp > 0)
                    {
                        double loss = tank.maxAmount * tank.loss_rate * deltaTemp * deltaTime;                         // loss_rate is calibrated to 300 degrees.
                        if (loss > tank.amount)
                        {
                            tank.amount = 0;
                        }
                        else
                        {
                            tank.amount -= loss;
                        }
                    }
                }
            }
        }
Exemple #12
0
        partial void OnStartRF(StartState state)
        {
            base.OnStart(state);

            if (HighLogic.LoadedSceneIsFlight)
            {
                for (int i = 0; i < vessel.vesselModules.Count; i++)
                {
                    if (vessel.vesselModules[i] is FlightIntegrator)
                    {
                        _flightIntegrator = vessel.vesselModules[i] as FlightIntegrator;
                    }
                }
            }


            CalculateTankArea(out tankArea);
            // changed from skin-internal to part.heatConductivity which affects
            part.heatConductivity = Math.Min(part.heatConductivity, outerInsulationFactor);
            // affects how fast internal temperatures change during analytic mode
            part.analyticInternalInsulationFactor *= outerInsulationFactor;

            for (int i = tankList.Count - 1; i >= 0; --i)
            {
                FuelTank tank = tankList[i];
                if (tank.maxAmount > 0.0 && (tank.vsp > 0.0 || tank.loss_rate > 0.0))
                {
                    supportsBoiloff = true;
                    break;
                }
            }

            Fields[nameof(debug0Display)].guiActive = RFSettings.Instance.debugBoilOff && this.supportsBoiloff;
            Fields[nameof(debug1Display)].guiActive = RFSettings.Instance.debugBoilOff && this.supportsBoiloff;
            Fields[nameof(debug2Display)].guiActive = RFSettings.Instance.debugBoilOff && this.supportsBoiloff;
        }
        private void UpdateTankType(bool initializeAmounts = true)
        {
            if (oldType == type || type == null)
            {
                return;
            }

            // Copy the tank list from the tank definitiion
            TankDefinition def;

            if (!MFSSettings.tankDefinitions.Contains(type))
            {
                Debug.LogError("Unable to find tank definition for type \"" + type + "\" reverting.");
                type = oldType;
                return;
            }
            def = MFSSettings.tankDefinitions[type];
            if (!def.canHave)
            {
                type = oldType;
                if (oldType != null && oldType != "") // we have an old type
                {
                    def = MFSSettings.tankDefinitions[type];
                    if (def.canHave)
                    {
                        return; // go back to old type
                    }
                }
                // else find one that does work
                foreach (TankDefinition newDef in MFSSettings.tankDefinitions)
                {
                    if (newDef.canHave)
                    {
                        def  = newDef;
                        type = newDef.name;
                        break;
                    }
                }
                if (type == oldType) // if we didn't find a new one
                {
                    Debug.LogError("Unable to find a type that is tech-available for part " + part.name);
                    return;
                }
            }

            oldType = type;

            // Build the new tank list.
            tankList = new FuelTankList();
            for (int i = 0; i < def.tankList.Count; i++)
            {
                FuelTank tank = def.tankList[i];
                // Pull the override from the list of overrides
                ConfigNode overNode = MFSSettings.GetOverrideList(part).FirstOrDefault(n => n.GetValue("name") == tank.name);

                tankList.Add(tank.CreateCopy(this, overNode, initializeAmounts));
            }
            tankList.TechAmounts(); // update for current techs

            // Destroy any managed resources that are not in the new type.
            HashSet <string> managed     = MFSSettings.managedResources[part.name];             // if this throws, we have some big fish to fry
            bool             needsMesage = false;

            for (int i = part.Resources.Count - 1; i >= 0; --i)
            {
                PartResource partResource = part.Resources[i];
                string       resname      = partResource.resourceName;
                if (!managed.Contains(resname) || tankList.Contains(resname))
                {
                    continue;
                }
                part.Resources.Remove(partResource.info.id);
                needsMesage = true;
            }
            if (needsMesage)
            {
                RaiseResourceListChanged();
            }
            if (!basemassOverride)
            {
                ParseBaseMass(def.basemass);
            }
            if (!baseCostOverride)
            {
                ParseBaseCost(def.baseCost);
            }


            if (!isDatabaseLoad)
            {
                // being called in the SpaceCenter scene is assumed to be a database reload
                //FIXME is this really needed?

                massDirty = true;
            }

            UpdateTankTypeRF(def);
            UpdateTestFlight();
        }
        void UpdateTank(FuelTank tank)
        {
            if (GUILayout.Button ("Update", GUILayout.Width (53))) {
                string trimmed = tank.maxAmountExpression.Trim ();

                if (trimmed == "") {
                    tank.maxAmount = 0;
                    //Debug.LogWarning ("[MFT] Removing tank as empty input " + tank.name + " amount: " + tank.maxAmountExpression ?? "null");
                } else {
                    double tmp;
                    if (MathUtils.TryParseExt (trimmed, out tmp)) {
                        tank.maxAmount = tmp;

                        if (tmp != 0) {
                            tank.amount = tank.fillable ? tank.maxAmount : 0;

                            // Need to round-trip the value
                            tank.maxAmountExpression = tank.maxAmount.ToString ();
                            //Debug.LogWarning ("[MFT] Updating maxAmount " + tank.name + " amount: " + tank.maxAmountExpression ?? "null");
                        }
                    }
                }
            }
        }
        void TankLine(FuelTank tank)
        {
            GUILayout.BeginHorizontal ();
            GUILayout.Label (" " + tank, GUILayout.Width (115));

            // So our states here are:
            //   Not being edited currently (empty):   maxAmountExpression = null, maxAmount = 0
            //   Have updated the field, no user edit: maxAmountExpression == maxAmount.ToStringExt
            //   Other non UI updated maxAmount:       maxAmountExpression = null (set), maxAmount = non-zero
            //   User has updated the field:           maxAmountExpression != null, maxAmountExpression != maxAmount.ToStringExt

            if (tank_module.part.Resources.Contains (tank.name) && tank_module.part.Resources[tank.name].maxAmount > 0) {
                EditTank (tank);
            } else if (tank_module.AvailableVolume >= 0.001) {
                AddTank (tank);
            } else {
                NoRoom ();
            }
            GUILayout.EndHorizontal ();
        }
Exemple #16
0
        private IEnumerator CalculateTankLossFunction(double deltaTime, bool analyticalMode = false)
        {
            // Need to ensure that all heat compensation (radiators, heat pumps, etc) run first.
            if (!analyticalMode)
            {
                yield return(new WaitForFixedUpdate());
            }

            boiloffMass = 0d;

            previewInternalFluxAdjust = 0;

            for (int i = tankList.Count - 1; i >= 0; --i)
            {
                FuelTank tank = tankList[i];
                if (tank.amount > 0d && (tank.vsp > 0.0 || tank.loss_rate > 0d))
                {
                    lowestTankTemperature = Math.Min(lowestTankTemperature, tank.temperature);
                }
            }

            if (tankList.Count > 0 && lowestTankTemperature < 300d && MFSSettings.radiatorMinTempMult >= 0d)
            {
                part.radiatorMax = (lowestTankTemperature * MFSSettings.radiatorMinTempMult) / part.maxTemp;
            }

            if (vessel != null && vessel.situation == Vessel.Situations.PRELAUNCH)
            {
                part.temperature     = lowestTankTemperature;
                part.skinTemperature = lowestTankTemperature;
            }
            else
            {
                partPrevTemperature = part.temperature;

                double deltaTimeRecip = 1d / deltaTime;
                //Debug.Log("internalFlux = " + part.thermalInternalFlux.ToString() + ", thermalInternalFluxPrevious =" + part.thermalInternalFluxPrevious.ToString() + ", analytic internal flux = " + previewInternalFluxAdjust.ToString());

                double cooling = analyticalMode ? Math.Max(0, part.thermalInternalFluxPrevious) : 0;

                for (int i = tankList.Count - 1; i >= 0; --i)
                {
                    FuelTank tank = tankList[i];

                    if (tank.amount > 0d)
                    {
                        if (tank.vsp > 0.0)
                        {
                            // Opposite of original boil off code. Finds massLost first.
                            double massLost = 0.0;
                            double deltaTemp;
                            double hotTemp   = part.temperature - (cooling * part.thermalMassReciprocal);
                            double tankRatio = tank.maxAmount / volume;

                            if (RFSettings.Instance.ferociousBoilOff)
                            {
                                hotTemp = Math.Max(((hotTemp * part.thermalMass) - (tank.temperature * part.resourceThermalMass)) / (part.thermalMass - part.resourceThermalMass), part.temperature);
                            }

                            deltaTemp = hotTemp - tank.temperature;

                            if (RFSettings.Instance.debugBoilOff)
                            {
                                if (debug2Display != "")
                                {
                                    debug2Display += " / ";
                                }

                                if (debug1Display != "")
                                {
                                    debug1Display += " / ";
                                }

                                if (debug0Display != "")
                                {
                                    debug0Display += " / ";
                                }
                            }

                            if (RFSettings.Instance.debugBoilOff)
                            {
                                debug0Display += hotTemp.ToString("F6");
                            }

                            if (deltaTemp > 0)
                            {
                                double wettedArea = tank.totalArea * (tank.amount / tank.maxAmount);

                                double Q = deltaTemp /
                                           ((tank.wallThickness / (tank.wallConduction * wettedArea))
                                            + (tank.insulationThickness / (tank.insulationConduction * wettedArea))
                                            + (tank.resourceConductivity > 0 ? (0.01 / (tank.resourceConductivity * wettedArea)) : 0));

                                Q *= 0.001d; // convert to kilowatts

                                massLost = Q / tank.vsp;

                                if (RFSettings.Instance.debugBoilOff)
                                {
                                    // Only do debugging displays if debugging enabled in RFSettings

                                    debug1Display += Utilities.FormatFlux(Q);
                                    debug2Display += (massLost * 1000 * 3600).ToString("F4") + "kg/hr";
                                }
                                massLost *= deltaTime; // Frame scaling
                            }

                            double lossAmount = massLost / tank.density;

                            if (double.IsNaN(lossAmount))
                            {
                                print("[RF] " + tank.name + " lossAmount is NaN!");
                            }
                            else
                            {
                                double heatLost = 0d;
                                if (lossAmount > tank.amount)
                                {
                                    tank.amount = 0d;
                                }
                                else
                                {
                                    tank.amount -= lossAmount;

                                    heatLost = -massLost * tank.vsp;

                                    heatLost *= ConductionFactors;

                                    // See if there is boiloff byproduct and see if any other parts want to accept it.
                                    if (tank.boiloffProductResource != null)
                                    {
                                        double boiloffProductAmount = -(massLost / tank.boiloffProductResource.density);
                                        double retainedAmount       = part.RequestResource(tank.boiloffProductResource.id, boiloffProductAmount, ResourceFlowMode.STAGE_PRIORITY_FLOW);
                                        massLost -= retainedAmount * tank.boiloffProductResource.density;
                                    }

                                    boiloffMass += massLost;
                                }
                                // subtract heat from boiloff
                                // subtracting heat in analytic mode is tricky: Analytic flux handling is 'cheaty' and tricky to predict.
                                if (!analyticalMode)
                                {
                                    part.AddThermalFlux(heatLost * deltaTimeRecip * 2.0d); // double because there is a bug in FlightIntegrator that cuts internal flux in half
                                }
                                else
                                {
                                    analyticInternalTemp       = analyticInternalTemp + (heatLost * part.thermalMassReciprocal);
                                    previewInternalFluxAdjust -= heatLost * deltaTimeRecip * 2d;
                                    if (deltaTime > 0)
                                    {
                                        print(part.name + " deltaTime = " + deltaTime + ", heat lost = " + heatLost + ", thermalMassReciprocal = " + part.thermalMassReciprocal);
                                    }
                                }
                            }
                        }
                        else if (tank.loss_rate > 0 && tank.amount > 0)
                        {
                            double deltaTemp = part.temperature - tank.temperature;
                            if (deltaTemp > 0)
                            {
                                double lossAmount = tank.maxAmount * tank.loss_rate * deltaTemp * deltaTime;
                                if (lossAmount > tank.amount)
                                {
                                    lossAmount  = -tank.amount;
                                    tank.amount = 0d;
                                }
                                else
                                {
                                    lossAmount   = -lossAmount;
                                    tank.amount += lossAmount;
                                }
                                double massLost = tank.density * lossAmount;
                                boiloffMass += massLost;
                            }
                        }
                    }
                }
            }
        }
Exemple #17
0
        public void CalculateTankArea()
        {
            // TODO: Codify a more accurate tank area calculator.
            // Thought: cube YN/YP can be used to find the part diameter / circumference... X or Z finds the length
            // Also should try to determine if tank has a common bulkhead - and adjust heat flux into individual tanks accordingly
#if DEBUG
            print("CalculateTankArea() running");
#endif

            if (HighLogic.LoadedSceneIsEditor)
            {
                if (!this.part.DragCubes.None && this.oldTotalVolume != this.totalVolume)
                {
                    if (this.IsProcedural())
                    {
                        bool origProceduralValue = this.part.DragCubes.Procedural;
                        this.part.DragCubes.Procedural = true;
                        this.part.DragCubes.ForceUpdate(true, true, true);
                        this.part.DragCubes.SetDragWeights();
                        this.part.DragCubes.RequestOcclusionUpdate();
                        this.part.DragCubes.SetPartOcclusion();
                        this.part.DragCubes.Procedural = origProceduralValue;
                        this.oldTotalVolume            = this.totalVolume;
                    }
                }
            }

            totalTankArea = 0f;

            for (int i = 0; i < 6; ++i)
            {
                totalTankArea += part.DragCubes.WeightedArea[i];
            }
#if DEBUG
            Debug.Log("[RealFuels.ModuleFuelTankRF] Part WeightedArea: " + part.name + " = " + totalTankArea.ToString("F2"));
            Debug.Log("[RealFuels.ModuleFuelTankRF] Part Area: " + part.name + " = " + part.DragCubes.Area.ToString("F2"));
#endif
            // This allows a rough guess as to individual tank surface area based on ratio of tank volume to total volume but it breaks down at very small fractions
            // So use greater of spherical calculation and tank ratio of total area.
            // if for any reason our totalTankArea is still 0 (no drag cubes available yet or analytic temp routines executed first)
            // then we're going to be defaulting to spherical calculation
            double tankMaxAmount;
            double tempTotal = 0;

            if (RFSettings.Instance.debugBoilOff)
            {
                Debug.Log("[RealFuels.ModuleFuelTankRF] Initializing " + part.name + ".totalTankArea as " + totalTankArea.ToString());
            }

            for (int i = tankList.Count - 1; i >= 0; --i)
            {
                FuelTank tank = tankList[i];
                if (tank.maxAmount > 0.0)
                {
                    tankMaxAmount = tank.maxAmount;

                    if (tank.utilization > 1.0)
                    {
                        tankMaxAmount /= tank.utilization;
                    }

                    tank.tankRatio = tankMaxAmount / volume;

                    tank.totalArea = Math.Max(Math.Pow(Math.PI, 1.0 / 3.0) * Math.Pow((tankMaxAmount / 1000.0) * 6, 2.0 / 3.0), totalTankArea * tank.tankRatio);
                    tempTotal     += tank.totalArea;

                    if (RFSettings.Instance.debugBoilOff)
                    {
                        Debug.Log("[RealFuels.ModuleFuelTankRF] " + tank.name + ".tankRatio = " + tank.tankRatio.ToString());
                        Debug.Log("[RealFuels.ModuleFuelTankRF] " + tank.name + ".maxAmount = " + tankMaxAmount.ToString());
                        Debug.Log("[RealFuels.ModuleFuelTankRF] Tank surface area = " + tank.totalArea.ToString());
                        DebugLog("tank Dewar status = " + tank.isDewar.ToString());
                    }
                }
            }
            if (!(totalTankArea > 0) || tempTotal > totalTankArea)
            {
                totalTankArea = tempTotal;
            }
            if (RFSettings.Instance.debugBoilOff)
            {
                Debug.Log("[RealFuels.ModuleFuelTankRF] " + part.name + ".totalTankArea = " + totalTankArea.ToString());
                Debug.Log("[RealFuels.ModuleFuelTankRF] " + part.name + ".GetModuleSize()" + part.GetModuleSize(Vector3.zero).ToString("F2"));
            }
        }
Exemple #18
0
        partial void OnStartRF(StartState state)
        {
            GameEvents.onVesselWasModified.Add(OnVesselWasModified);
            GameEvents.onEditorShipModified.Add(OnEditorShipModified);
            GameEvents.onPartDestroyed.Add(OnPartDestroyed);
            if (HighLogic.LoadedSceneIsFlight)
            {
                for (int i = 0; i < vessel.vesselModules.Count; i++)
                {
                    if (vessel.vesselModules[i] is FlightIntegrator)
                    {
                        _flightIntegrator = vessel.vesselModules[i] as FlightIntegrator;
                    }
                }
            }

            // Wait to calculate tank area because it depends on drag cubes
            // MLI depends on tank area so mass will also be recalculated
            IEnumerator WaitAndRecalculateMass()
            {
                yield return(null);

                yield return(null);

                yield return(null);

                CalculateTankArea();
                massDirty = true;
                CalculateMass();
            }

            if (HighLogic.LoadedSceneIsFlight)
            {
                StartCoroutine(WaitAndRecalculateMass());
            }

            for (int i = tankList.Count - 1; i >= 0; --i)
            {
                FuelTank tank = tankList[i];
                if (tank.maxAmount > 0.0 && (tank.vsp > 0.0 || tank.loss_rate > 0.0))
                {
                    supportsBoiloff = true;
                    break;
                }
            }

            if (state == StartState.Editor)
            {
                if (maxMLILayers > 0)
                {
                    ((UI_FloatRange)Fields[nameof(_numberOfAddedMLILayers)].uiControlEditor).maxValue = maxMLILayers;
                }
                else
                {
                    Fields[nameof(_numberOfAddedMLILayers)].guiActiveEditor = false;
                }

                Fields[nameof(_numberOfAddedMLILayers)].uiControlEditor.onFieldChanged = delegate(BaseField field, object value)
                {
                    massDirty = true;
                    CalculateMass();
                };
            }

            Fields[nameof(debug0Display)].guiActive = this.supportsBoiloff && (RFSettings.Instance.debugBoilOff || RFSettings.Instance.debugBoilOffPAW);
            Fields[nameof(debug1Display)].guiActive = this.supportsBoiloff && (RFSettings.Instance.debugBoilOff || RFSettings.Instance.debugBoilOffPAW);
            Fields[nameof(debug2Display)].guiActive = this.supportsBoiloff && (RFSettings.Instance.debugBoilOff || RFSettings.Instance.debugBoilOffPAW);

            //numberOfAddedMLILayers = Mathf.Round(numberOfAddedMLILayers);
            //CalculateInsulation();
        }
Exemple #19
0
        private void FillAttachedTanks(double deltaTime)
        {
            // sanity check
            if (deltaTime <= 0)
            {
                return;
            }

            // now, let's look at what we're connected to.
            foreach (Part p in vessel.parts)  // look through all parts
            {
                Tanks.ModuleFuelTanks m = p.FindModuleImplementing <Tanks.ModuleFuelTanks>();
                if (m != null)
                {
                    m.fueledByLaunchClamp = true;
                    // look through all tanks inside this part
                    for (int j = m.tankList.Count - 1; j >= 0; --j)
                    {
                        Tanks.FuelTank tank = m.tankList[j];
                        // if a tank isn't full, start filling it.

                        if (tank.maxAmount <= 0)
                        {
                            continue;
                        }

                        PartResource r = tank.resource;
                        if (r == null)
                        {
                            continue;
                        }

                        PartResourceDefinition d = PartResourceLibrary.Instance.GetDefinition(r.resourceName);
                        if (d == null)
                        {
                            continue;
                        }

                        if (tank.amount < tank.maxAmount && tank.fillable && r.flowMode != PartResource.FlowMode.None && d.resourceTransferMode == ResourceTransferMode.PUMP && r.flowState)
                        {
                            double amount = Math.Min(deltaTime * pump_rate * tank.utilization, tank.maxAmount - tank.amount);
                            var    game   = HighLogic.CurrentGame;

                            if (d.unitCost > 0 && game.Mode == Game.Modes.CAREER && Funding.Instance != null)
                            {
                                double funds = Funding.Instance.Funds;
                                double cost  = amount * d.unitCost;
                                if (cost > funds)
                                {
                                    amount = funds / d.unitCost;
                                    cost   = funds;
                                }
                                Funding.Instance.AddFunds(-cost, TransactionReasons.VesselRollout);
                            }
                            //tank.amount = tank.amount + amount;
                            p.TransferResource(r, amount, this.part);
                        }
                    }
                }
                else
                {
                    for (int j = p.Resources.Count - 1; j >= 0; --j)
                    {
                        PartResource partResource = p.Resources[j];
                        if (partResource.info.name == "ElectricCharge")
                        {
                            if (partResource.flowMode != PartResource.FlowMode.None && partResource.info.resourceTransferMode == ResourceTransferMode.PUMP && partResource.flowState)
                            {
                                double amount = deltaTime * pump_rate;
                                amount = Math.Min(amount, partResource.maxAmount - partResource.amount);
                                p.TransferResource(partResource, amount, this.part);
                            }
                        }
                    }
                }
            }
        }
Exemple #20
0
        private IEnumerator CalculateTankBoiloff(double deltaTime, bool analyticalMode = false)
        {
            // Need to ensure that all heat compensation (radiators, heat pumps, etc) run first.
            if (totalTankArea <= 0)
            {
                CalculateTankArea(out totalTankArea);
            }

            if (!analyticalMode)
            {
                yield return(new WaitForFixedUpdate());
            }

            boiloffMass = 0d;

            previewInternalFluxAdjust = 0;

            bool hasCryoFuels = CalculateLowestTankTemperature();

            if (tankList.Count > 0 && lowestTankTemperature < 300d && MFSSettings.radiatorMinTempMult >= 0d)
            {
                part.radiatorMax = (lowestTankTemperature * MFSSettings.radiatorMinTempMult) / part.maxTemp;
            }

            if (fueledByLaunchClamp)
            {
                if (hasCryoFuels)
                {
                    part.temperature     = lowestTankTemperature;
                    part.skinTemperature = lowestTankTemperature;
                }
                fueledByLaunchClamp = false;
                yield break;
            }

            partPrevTemperature = part.temperature;

            double deltaTimeRecip = 1d / deltaTime;
            //Debug.Log("internalFlux = " + part.thermalInternalFlux.ToString() + ", thermalInternalFluxPrevious =" + part.thermalInternalFluxPrevious.ToString() + ", analytic internal flux = " + previewInternalFluxAdjust.ToString());

            double cooling = analyticalMode ? Math.Min(0, part.thermalInternalFluxPrevious) : 0;

            if (RFSettings.Instance.debugBoilOff)
            {
                debug0Display = part.temperature.ToString("F4") + "(" + GetMLITransferRate(part.skinTemperature, part.temperature).ToString("F4") + " * " + (part.radiativeArea * part.skinExposedAreaFrac).ToString("F2") + "m2)";
            }

            for (int i = tankList.Count - 1; i >= 0; --i)
            {
                FuelTank tank = tankList[i];
                if (tank.amount <= 0)
                {
                    continue;
                }

                if (tank.vsp > 0.0 && tank.totalArea > 0)
                {
                    // Opposite of original boil off code. Finds massLost first.
                    double massLost = 0.0;
                    double deltaTemp;
                    double hotTemp   = part.temperature;
                    double tankRatio = tank.maxAmount / volume;

                    if (RFSettings.Instance.ferociousBoilOff)
                    {
                        hotTemp = Math.Max(((hotTemp * part.thermalMass) - (tank.temperature * part.resourceThermalMass)) / (part.thermalMass - part.resourceThermalMass), part.temperature);
                    }

                    deltaTemp = hotTemp - tank.temperature;

                    if (RFSettings.Instance.debugBoilOff)
                    {
                        if (debug2Display != "")
                        {
                            debug2Display += " / ";
                        }

                        if (debug1Display != "")
                        {
                            debug1Display += " / ";
                        }
                    }

                    if (deltaTemp > 0)
                    {
#if DEBUG
                        if (analyticalMode)
                        {
                            print("Tank " + tank.name + " surface area = " + tank.totalArea);
                        }
#endif

                        double wettedArea = tank.totalArea;// disabled until proper wetted vs ullage conduction can be done (tank.amount / tank.maxAmount);

                        double Q = 0;

                        if (tank.isDewar)
                        {
                            Q = GetDewarTransferRate(hotTemp, tank.temperature, tank.totalArea);
                        }
                        else
                        {
                            Q = deltaTemp /
                                ((tank.wallThickness / (tank.wallConduction * wettedArea))
                                 + (tank.insulationThickness / (tank.insulationConduction * wettedArea))
                                 + (tank.resourceConductivity > 0 ? (0.01 / (tank.resourceConductivity * wettedArea)) : 0));
                        }

                        Q *= 0.001d; // convert to kilowatts

                        massLost = Q / tank.vsp;

                        if (RFSettings.Instance.debugBoilOff)
                        {
                            // Only do debugging displays if debugging enabled in RFSettings

                            debug1Display += Utilities.FormatFlux(Q);
                            debug2Display += (massLost * 1000 * 3600).ToString("F4") + "kg/hr";
                        }
                        massLost *= deltaTime; // Frame scaling
                    }

                    double lossAmount = massLost / tank.density;

                    if (double.IsNaN(lossAmount))
                    {
                        print("[RF] " + tank.name + " lossAmount is NaN!");
                    }
                    else
                    {
                        double heatLost = 0d;
                        if (lossAmount > tank.amount)
                        {
                            tank.amount = 0d;
                        }
                        else
                        {
                            tank.amount -= lossAmount;

                            heatLost = -massLost * tank.vsp;

                            // See if there is boiloff byproduct and see if any other parts want to accept it.
                            if (tank.boiloffProductResource != null)
                            {
                                double boiloffProductAmount = -(massLost / tank.boiloffProductResource.density);
                                double retainedAmount       = part.RequestResource(tank.boiloffProductResource.id, boiloffProductAmount, ResourceFlowMode.STAGE_PRIORITY_FLOW);
                                massLost -= retainedAmount * tank.boiloffProductResource.density;
                            }

                            boiloffMass += massLost;
                        }
                        // subtract heat from boiloff
                        // subtracting heat in analytic mode is tricky: Analytic flux handling is 'cheaty' and tricky to predict.
                        // scratch sheet: example
                        // [RealFuels.ModuleFuelTankRF] proceduralTankRealFuels Analytic Temp = 256.679360297684, Analytic Internal = 256.679360297684, Analytic Skin = 256.679360297684
                        // [RealFuels.ModuleFuelTankRF] proceduralTankRealFuels deltaTime = 17306955.5092776, heat lost = 6638604.21227684, thermalMassReciprocal = 0.00444787360733243

                        if (!analyticalMode)
                        {
                            heatLost *= ConductionFactors;

                            part.AddThermalFlux(heatLost * deltaTimeRecip);
                        }
                        else
                        {
                            analyticInternalTemp       = analyticInternalTemp + (heatLost * part.thermalMassReciprocal);
                            previewInternalFluxAdjust += heatLost * deltaTimeRecip;
#if DEBUG
                            if (deltaTime > 0)
                            {
                                print(part.name + " deltaTime = " + deltaTime + ", heat lost = " + heatLost + ", thermalMassReciprocal = " + part.thermalMassReciprocal);
                            }
#endif
                        }
                    }
                }
                else if (tank.loss_rate > 0 && tank.amount > 0)
                {
                    double deltaTemp = part.temperature - tank.temperature;
                    if (deltaTemp > 0)
                    {
                        double lossAmount = tank.maxAmount * tank.loss_rate * deltaTemp * deltaTime;
                        if (lossAmount > tank.amount)
                        {
                            lossAmount  = -tank.amount;
                            tank.amount = 0d;
                        }
                        else
                        {
                            lossAmount   = -lossAmount;
                            tank.amount += lossAmount;
                        }
                        double massLost = tank.density * lossAmount;
                        boiloffMass += massLost;
                    }
                }
            }
        }
Exemple #21
0
        private IEnumerator CalculateTankBoiloff(double deltaTime, bool analyticalMode = false)
        {
            // Need to ensure that all heat compensation (radiators, heat pumps, etc) run first.
            if (totalTankArea <= 0)
            {
                CalculateTankArea();
            }

            if (!analyticalMode)
            {
                yield return(new WaitForFixedUpdate());
            }

            boiloffMass = 0d;

            previewInternalFluxAdjust = 0;

            bool hasCryoFuels = CalculateLowestTankTemperature();

            if (tankList.Count > 0 && lowestTankTemperature < 300d && MFSSettings.radiatorMinTempMult >= 0d)
            {
                part.radiatorMax = (lowestTankTemperature * MFSSettings.radiatorMinTempMult) / part.maxTemp;
            }

            if (fueledByLaunchClamp)
            {
                if (hasCryoFuels)
                {
                    part.temperature     = lowestTankTemperature;
                    part.skinTemperature = lowestTankTemperature;
                }
                fueledByLaunchClamp = false;
                yield break;
            }

            if (!double.IsNaN(part.temperature))
            {
                partPrevTemperature = part.temperature;
            }
            else
            {
                part.temperature = partPrevTemperature;
            }

            if (deltaTime > 0)
            {
                double deltaTimeRecip = 1d / deltaTime;
                //Debug.Log("internalFlux = " + part.thermalInternalFlux.ToString() + ", thermalInternalFluxPrevious =" + part.thermalInternalFluxPrevious.ToString() + ", analytic internal flux = " + previewInternalFluxAdjust.ToString());

                if (RFSettings.Instance.debugBoilOff || RFSettings.Instance.debugBoilOffPAW)
                {
                    string MLIText = totalMLILayers > 0 ? GetMLITransferRate(part.skinTemperature, part.temperature).ToString("F4") : "No MLI";
                    debug0Display = part.temperature.ToString("F4") + "(" + MLIText + " * " + (part.radiativeArea * part.skinExposedAreaFrac).ToString("F2") + "m2)";
                }


                double cooling = 0;

                if (analyticalMode)
                {
                    if (part.thermalInternalFlux < 0)
                    {
                        cooling = part.thermalInternalFlux;
                    }
                    else if (part.thermalInternalFluxPrevious < 0)
                    {
                        cooling = part.thermalInternalFluxPrevious;
                    }

                    if (cooling < 0)
                    {
                        // in analytic mode, MFTRF interprets this as an attempt to cool the tanks
                        analyticInternalTemp += cooling * part.thermalMassReciprocal * deltaTime;
                        // because of what we're doing in CalculateAnalyticInsulationFactor(), it will take too much time to reach that temperature so
                        part.temperature += cooling * part.thermalMassReciprocal * deltaTime;
                    }
                }

                debug3Display = Utilities.FormatFlux(cooling);

                for (int i = tankList.Count - 1; i >= 0; --i)
                {
                    FuelTank tank = tankList[i];
                    if (tank.amount <= 0)
                    {
                        continue;
                    }

                    if (tank.vsp > 0.0 && tank.totalArea > 0)
                    {
                        // Opposite of original boil off code. Finds massLost first.
                        double massLost = 0.0;
                        double deltaTemp;
                        double hotTemp   = part.temperature;
                        double tankRatio = tank.maxAmount / volume;

                        if (RFSettings.Instance.ferociousBoilOff)
                        {
                            hotTemp = Math.Max(((hotTemp * part.thermalMass) - (tank.temperature * part.resourceThermalMass)) / (part.thermalMass - part.resourceThermalMass), part.temperature);
                        }

                        deltaTemp = hotTemp - tank.temperature;

                        if (RFSettings.Instance.debugBoilOff || RFSettings.Instance.debugBoilOffPAW)
                        {
                            if (debug2Display != "")
                            {
                                debug2Display += " / ";
                            }

                            if (debug1Display != "")
                            {
                                debug1Display += " / ";
                            }
                        }

                        double Q = 0;
                        if (deltaTemp > 0)
                        {
#if DEBUG
                            if (analyticalMode)
                            {
                                print("Tank " + tank.name + " surface area = " + tank.totalArea);
                            }
#endif

                            double wettedArea = tank.totalArea; // disabled until proper wetted vs ullage conduction can be done (tank.amount / tank.maxAmount);

                            if (tank.isDewar)
                            {
                                Q = GetDewarTransferRate(hotTemp, tank.temperature, tank.totalArea);
                            }
                            else
                            {
                                Q = deltaTemp /
                                    ((tank.wallThickness / (tank.wallConduction * wettedArea))
                                     + (tank.insulationThickness / (tank.insulationConduction * wettedArea))
                                     + (tank.resourceConductivity > 0 ? (0.01 / (tank.resourceConductivity * wettedArea)) : 0));
                            }

                            Q *= 0.001d; // convert to kilowatts

                            if (!double.IsNaN(Q))
                            {
                                massLost = Q / tank.vsp;
                            }
                            else
                            {
                                DebugLog("Q = NaN! W - T - F!!!");
                            }

                            if (RFSettings.Instance.debugBoilOff || RFSettings.Instance.debugBoilOffPAW)
                            {
                                // Only do debugging displays if debugging enabled in RFSettings

                                debug1Display += Utilities.FormatFlux(Q);
                                debug2Display += (massLost * 1000 * 3600).ToString("F4") + "kg/hr";
                            }
                            massLost *= deltaTime; // Frame scaling
                        }

                        double lossAmount = massLost / tank.density;

                        if (double.IsNaN(lossAmount))
                        {
                            print(tank.name + " lossAmount is NaN!");
                        }
                        else
                        {
                            if (lossAmount > tank.amount)
                            {
                                if (!CheatOptions.InfinitePropellant)
                                {
                                    tank.amount = 0d;
                                }
                            }
                            else
                            {
                                if (!CheatOptions.InfinitePropellant)
                                {
                                    tank.amount -= lossAmount;
                                }

                                // See if there is boiloff byproduct and see if any other parts want to accept it.
                                if (tank.boiloffProductResource != null)
                                {
                                    double boiloffProductAmount = -(massLost / tank.boiloffProductResource.density);
                                    double retainedAmount       = part.RequestResource(tank.boiloffProductResource.id, boiloffProductAmount, ResourceFlowMode.STAGE_PRIORITY_FLOW);
                                    massLost -= retainedAmount * tank.boiloffProductResource.density;
                                }

                                boiloffMass += massLost;
                            }
                            // subtract heat from boiloff
                            // subtracting heat in analytic mode is tricky: Analytic flux handling is 'cheaty' and tricky to predict.
                            // scratch sheet: example
                            // [RealFuels.ModuleFuelTankRF] proceduralTankRealFuels Analytic Temp = 256.679360297684, Analytic Internal = 256.679360297684, Analytic Skin = 256.679360297684
                            // [RealFuels.ModuleFuelTankRF] proceduralTankRealFuels deltaTime = 17306955.5092776, heat lost = 6638604.21227684, thermalMassReciprocal = 0.00444787360733243

                            if (!double.IsNaN(Q))
                            {
                                double heatLost = -Q;
                                if (!analyticalMode)
                                {
                                    part.AddThermalFlux(heatLost);
                                }
                                else
                                {
                                    analyticInternalTemp = analyticInternalTemp + (heatLost * part.thermalMassReciprocal * deltaTime);
                                    // Don't try to adjust flux if significant time has passed; it never works out.
                                    // Analytic mode flux only gets applied if timewarping AND analytic mode was set.
                                    if (TimeWarp.CurrentRate > 1)
                                    {
                                        previewInternalFluxAdjust += heatLost;
                                    }
                                    else
                                    {
                                        print("boiloff function running with delta time of " + deltaTime.ToString() + "(vessel.lastUT =" + (Planetarium.GetUniversalTime() - vessel.lastUT).ToString("F4") + " seconds ago)");
                                    }
#if DEBUG
                                    if (deltaTime > 0)
                                    {
                                        print(part.name + " deltaTime = " + deltaTime + ", heat lost = " + heatLost + ", thermalMassReciprocal = " + part.thermalMassReciprocal);
                                    }
#endif
                                }
                            }
                            else
                            {
                                DebugLog("WHO WOULD WIN? Some Well Written Code or One Misplaced NaN?");
                                DebugLog("heatLost = " + Q.ToString());
                                DebugLog("deltaTime = " + deltaTime.ToString());
                                DebugLog("deltaTimeRecip = " + deltaTimeRecip.ToString());
                                DebugLog("massLost = " + massLost.ToString());
                                DebugLog("tank.vsp = " + tank.vsp.ToString());
                            }
                        }
                    }
                    else if (tank.loss_rate > 0 && tank.amount > 0)
                    {
                        double deltaTemp = part.temperature - tank.temperature;
                        if (deltaTemp > 0)
                        {
                            double lossAmount = tank.maxAmount * tank.loss_rate * deltaTemp * deltaTime;
                            if (lossAmount > tank.amount)
                            {
                                lossAmount  = -tank.amount;
                                tank.amount = 0d;
                            }
                            else
                            {
                                lossAmount   = -lossAmount;
                                tank.amount += lossAmount;
                            }
                            double massLost = tank.density * lossAmount;
                            boiloffMass += massLost;
                        }
                    }
                }
            }
        }
        private void UpdateTankType(bool initializeAmounts = true)
        {
            if (oldType == type || type == null)
            {
                return;
            }

            // Copy the tank list from the tank definitiion
            TankDefinition def;

            if (!MFSSettings.tankDefinitions.Contains(type))
            {
                Debug.LogError("Unable to find tank definition for type \"" + type + "\" reverting.");
                type = oldType;
                return;
            }
            def = MFSSettings.tankDefinitions[type];

            oldType = type;
            // Build the new tank list.
            tankList = new FuelTankList();
            for (int i = 0; i < def.tankList.Count; i++)
            {
                FuelTank tank = def.tankList[i];
                // Pull the override from the list of overrides
                ConfigNode overNode = overrideListNodes.FirstOrDefault(n => n.GetValue("name") == tank.name);

                tankList.Add(tank.CreateCopy(this, overNode, initializeAmounts));
            }

            // Destroy any managed resources that are not in the new type.
            HashSet <string> managed     = MFSSettings.managedResources[part.name];             // if this throws, we have some big fish to fry
            bool             needsMesage = false;

            for (int i = part.Resources.Count - 1; i >= 0; --i)
            {
                PartResource partResource = part.Resources[i];
                string       resname      = partResource.resourceName;
                if (!managed.Contains(resname) || tankList.Contains(resname))
                {
                    continue;
                }
                part.Resources.list.RemoveAt(i);
                DestroyImmediate(partResource);
                needsMesage = true;
            }
            if (needsMesage)
            {
                RaiseResourceListChanged();
            }
            if (!basemassOverride)
            {
                ParseBaseMass(def.basemass);
            }
            if (!baseCostOverride)
            {
                ParseBaseCost(def.baseCost);
            }

            if (isDatabaseLoad)
            {
                // being called in the SpaceCenter scene is assumed to be a database reload
                //FIXME is this really needed?
                return;
            }

            UpdateEngineIgnitor(def);

            massDirty = true;
        }
        void AddTank(FuelTank tank)
        {
            string extraData = "Max: " + (tank_module.AvailableVolume * tank.utilization).ToStringExt ("S3") + "L (+" + ModuleFuelTanks.FormatMass ((float) (tank_module.AvailableVolume * tank.mass)) + " )";

            GUILayout.Label (extraData, GUILayout.Width (150));

            if (GUILayout.Button ("Add", GUILayout.Width (120))) {
                tank.maxAmount = tank_module.AvailableVolume * tank.utilization;
                tank.amount = tank.fillable ? tank.maxAmount : 0;

                tank.maxAmountExpression = tank.maxAmount.ToString ();
                //Debug.LogWarning ("[MFT] Adding tank " + tank.name + " maxAmount: " + tank.maxAmountExpression ?? "null");
            }
        }
 void RemoveTank(FuelTank tank)
 {
     if (GUILayout.Button ("Remove", GUILayout.Width (58))) {
         tank.maxAmount = 0;
         GameEvents.onEditorShipModified.Fire (EditorLogic.fetch.ship);
         //Debug.LogWarning ("[MFT] Removing tank from button " + tank.name + " amount: " + tank.maxAmountExpression ?? "null");
     }
 }
Exemple #25
0
        private void FillAttachedTanks(double deltaTime)
        {
            // sanity check
            if (deltaTime <= 0)
            {
                return;
            }

            // now, let's look at what we're connected to.
            for (int i = vessel.parts.Count - 1; i >= 0; --i)  // look through all parts
            {
                Part p = vessel.parts[i];
                if (p.Modules.Contains("ModuleFuelTanks"))
                {
                    Tanks.ModuleFuelTanks m = (Tanks.ModuleFuelTanks)p.Modules["ModuleFuelTanks"];
                    double minTemp          = p.temperature;
                    // look through all tanks inside this part
                    for (int j = m.tankList.Count - 1; j >= 0; --j)
                    {
                        Tanks.FuelTank tank = m.tankList[j];
                        // if a tank isn't full, start filling it.
                        PartResource r = tank.resource;
                        if (r == null)
                        {
                            continue;
                        }
                        PartResourceDefinition d = PartResourceLibrary.Instance.GetDefinition(r.resourceName);
                        if (d == null)
                        {
                            continue;
                        }
                        if (tank.maxAmount > 0d)
                        {
                            if (tank.loss_rate > 0d)
                            {
                                minTemp = Math.Min(p.temperature, tank.temperature);
                            }
                            if (tank.amount < tank.maxAmount && tank.fillable && r.flowMode != PartResource.FlowMode.None && d.resourceTransferMode == ResourceTransferMode.PUMP && r.flowState)
                            {
                                double amount = Math.Min(deltaTime * pump_rate * tank.utilization, tank.maxAmount - tank.amount);
                                var    game   = HighLogic.CurrentGame;

                                if (d.unitCost > 0 && game.Mode == Game.Modes.CAREER && Funding.Instance != null)
                                {
                                    double funds = Funding.Instance.Funds;
                                    double cost  = amount * d.unitCost;
                                    if (cost > funds)
                                    {
                                        amount = funds / d.unitCost;
                                        cost   = funds;
                                    }
                                    Funding.Instance.AddFunds(-cost, TransactionReasons.VesselRollout);
                                }
                                //tank.amount = tank.amount + amount;
                                p.TransferResource(r, amount, this.part);
                            }
                        }
                    }
                    p.temperature = minTemp;
                }
                else
                {
                    for (int j = p.Resources.Count - 1; j >= 0; --j)
                    {
                        PartResource r = p.Resources[j];
                        if (r.info.name == "ElectricCharge")
                        {
                            if (r.flowMode != PartResource.FlowMode.None && r.info.resourceTransferMode == ResourceTransferMode.PUMP && r.flowState)
                            {
                                double amount = deltaTime * pump_rate;
                                amount = Math.Min(amount, r.maxAmount - r.amount);
                                p.TransferResource(r, amount, this.part);
                            }
                        }
                    }
                }
            }
        }
        void EditTank(FuelTank tank)
        {
            GUILayout.Label (" ", GUILayout.Width (5));

            GUIStyle style = unchanged;
            if (tank.maxAmountExpression == null) {
                tank.maxAmountExpression = tank.maxAmount.ToString ();
                //Debug.LogWarning ("[MFT] Adding tank from API " + tank.name + " amount: " + tank.maxAmountExpression ?? "null");
            } else if (tank.maxAmountExpression.Length > 0 && tank.maxAmountExpression != tank.maxAmount.ToString ()) {
                style = changed;
            }

            tank.maxAmountExpression = GUILayout.TextField (tank.maxAmountExpression, style, GUILayout.Width (127));

            UpdateTank (tank);
            RemoveTank (tank);
        }