Implemented Padishar's simulation alterations.
Implemented Padishar's simulation alterations.

--- a/KerbalEngineer/BuildEngineer/BuildAdvanced.cs
+++ b/KerbalEngineer/BuildEngineer/BuildAdvanced.cs
@@ -179,21 +179,21 @@
                 }

 

                 // Configure the simulation parameters based on the selected reference body.

-                SimulationManager.Instance.Gravity = CelestialBodies.Instance.SelectedBodyInfo.Gravity;

+                SimulationManager.Gravity = CelestialBodies.Instance.SelectedBodyInfo.Gravity;

                 if (this.useAtmosphericDetails)

                 {

-                    SimulationManager.Instance.Atmosphere = CelestialBodies.Instance.SelectedBodyInfo.Atmosphere *

+                    SimulationManager.Atmosphere = CelestialBodies.Instance.SelectedBodyInfo.Atmosphere *

                                                             0.01d;

                 }

                 else

                 {

-                    SimulationManager.Instance.Atmosphere = 0;

-                }

-

-                SimulationManager.Instance.TryStartSimulation();

+                    SimulationManager.Atmosphere = 0;

+                }

+

+                SimulationManager.TryStartSimulation();

 

                 // Reset the window size when the staging or something else has changed.

-                if (this.hasChanged || SimulationManager.Instance.StagingChanged)

+                if (this.hasChanged || SimulationManager.StagingChanged)

                 {

                     this.hasChanged = false;

 

@@ -216,7 +216,7 @@
                     return;

                 }

 

-                SimulationManager.Instance.RequestSimulation();

+                SimulationManager.RequestSimulation();

 

                 // Change the window title based on whether in compact mode or not.

                 string title;

@@ -355,11 +355,11 @@
         {

             GUILayout.BeginVertical(GUILayout.Width(30.0f));

             GUILayout.Label("", this.titleStyle);

-            foreach (var stage in SimulationManager.Instance.Stages)

-            {

-                if (this.showAllStages || stage.deltaV > 0)

-                {

-                    GUILayout.Label(stage.Number, this.titleStyle);

+            foreach (var stage in SimulationManager.Stages)

+            {

+                if (this.showAllStages || stage.DeltaV > 0)

+                {

+                    GUILayout.Label(stage.Number.ToString(), this.titleStyle);

                 }

             }

             GUILayout.EndVertical();

@@ -372,11 +372,11 @@
         {

             GUILayout.BeginVertical(GUILayout.Width(50.0f));

             GUILayout.Label("PARTS", this.titleStyle);

-            foreach (var stage in SimulationManager.Instance.Stages)

-            {

-                if (this.showAllStages || stage.deltaV > 0)

-                {

-                    GUILayout.Label(stage.Parts, this.infoStyle);

+            foreach (var stage in SimulationManager.Stages)

+            {

+                if (this.showAllStages || stage.DeltaV > 0)

+                {

+                    //GUILayout.Label(stage.Parts, this.infoStyle);

                 }

             }

             GUILayout.EndVertical();

@@ -389,11 +389,11 @@
         {

             GUILayout.BeginVertical(GUILayout.Width(100.0f));

             GUILayout.Label("COST", this.titleStyle);

-            foreach (var stage in SimulationManager.Instance.Stages)

-            {

-                if (this.showAllStages || stage.deltaV > 0)

-                {

-                    GUILayout.Label(stage.Cost, this.infoStyle);

+            foreach (var stage in SimulationManager.Stages)

+            {

+                if (this.showAllStages || stage.DeltaV > 0)

+                {

+                    GUILayout.Label(stage.Cost.ToString(), this.infoStyle);

                 }

             }

             GUILayout.EndVertical();

@@ -406,11 +406,11 @@
         {

             GUILayout.BeginVertical(GUILayout.Width(100.0f));

             GUILayout.Label("MASS", this.titleStyle);

-            foreach (var stage in SimulationManager.Instance.Stages)

-            {

-                if (this.showAllStages || stage.deltaV > 0)

-                {

-                    GUILayout.Label(stage.Mass, this.infoStyle);

+            foreach (var stage in SimulationManager.Stages)

+            {

+                if (this.showAllStages || stage.DeltaV > 0)

+                {

+                    GUILayout.Label(stage.Mass.ToMass(), this.infoStyle);

                 }

             }

             GUILayout.EndVertical();

@@ -423,11 +423,11 @@
         {

             GUILayout.BeginVertical(GUILayout.Width(50.0f));

             GUILayout.Label("ISP", this.titleStyle);

-            foreach (var stage in SimulationManager.Instance.Stages)

-            {

-                if (this.showAllStages || stage.deltaV > 0)

-                {

-                    GUILayout.Label(stage.Isp, this.infoStyle);

+            foreach (var stage in SimulationManager.Stages)

+            {

+                if (this.showAllStages || stage.DeltaV > 0)

+                {

+                    GUILayout.Label(stage.Isp.ToString("0."), this.infoStyle);

                 }

             }

             GUILayout.EndVertical();

@@ -440,11 +440,11 @@
         {

             GUILayout.BeginVertical(GUILayout.Width(75.0f));

             GUILayout.Label("THRUST", this.titleStyle);

-            foreach (var stage in SimulationManager.Instance.Stages)

-            {

-                if (this.showAllStages || stage.deltaV > 0)

-                {

-                    GUILayout.Label(stage.Thrust, this.infoStyle);

+            foreach (var stage in SimulationManager.Stages)

+            {

+                if (this.showAllStages || stage.DeltaV > 0)

+                {

+                    GUILayout.Label(stage.Thrust.ToForce(), this.infoStyle);

                 }

             }

             GUILayout.EndVertical();

@@ -457,11 +457,11 @@
         {

             GUILayout.BeginVertical(GUILayout.Width(50.0f));

             GUILayout.Label("TWR", this.titleStyle);

-            foreach (var stage in SimulationManager.Instance.Stages)

-            {

-                if (this.showAllStages || stage.deltaV > 0)

-                {

-                    GUILayout.Label(stage.TWR, this.infoStyle);

+            foreach (var stage in SimulationManager.Stages)

+            {

+                if (this.showAllStages || stage.DeltaV > 0)

+                {

+                    GUILayout.Label(stage.ThrustToWeight.ToString("0.00"), this.infoStyle);

                 }

             }

             GUILayout.EndVertical();

@@ -474,11 +474,11 @@
         {

             GUILayout.BeginVertical(GUILayout.Width(100.0f));

             GUILayout.Label("DELTA-V", this.titleStyle);

-            foreach (var stage in SimulationManager.Instance.Stages)

-            {

-                if (this.showAllStages || stage.deltaV > 0)

-                {

-                    GUILayout.Label(stage.DeltaV, this.infoStyle);

+            foreach (var stage in SimulationManager.Stages)

+            {

+                if (this.showAllStages || stage.DeltaV > 0)

+                {

+                    GUILayout.Label(stage.DeltaV.ToSpeed(), this.infoStyle);

                 }

             }

             GUILayout.EndVertical();

@@ -491,11 +491,11 @@
         {

             GUILayout.BeginVertical(GUILayout.Width(75.0f));

             GUILayout.Label("BURN", this.titleStyle);

-            foreach (var stage in SimulationManager.Instance.Stages)

-            {

-                if (this.showAllStages || stage.deltaV > 0)

-                {

-                    GUILayout.Label(stage.Time, this.infoStyle);

+            foreach (var stage in SimulationManager.Stages)

+            {

+                if (this.showAllStages || stage.DeltaV > 0)

+                {

+                    GUILayout.Label(stage.Time.ToTime(), this.infoStyle);

                 }

             }

             GUILayout.EndVertical();


--- a/KerbalEngineer/BuildEngineer/BuildOverlay.cs
+++ b/KerbalEngineer/BuildEngineer/BuildOverlay.cs
@@ -146,18 +146,18 @@
                 }

 

                 // Configure the simulation parameters based on the selected reference body.

-                SimulationManager.Instance.Gravity = CelestialBodies.Instance.SelectedBodyInfo.Gravity;

+                SimulationManager.Gravity = CelestialBodies.Instance.SelectedBodyInfo.Gravity;

 

                 if (BuildAdvanced.Instance.UseAtmosphericDetails)

                 {

-                    SimulationManager.Instance.Atmosphere = CelestialBodies.Instance.SelectedBodyInfo.Atmosphere * 0.01d;

+                    SimulationManager.Atmosphere = CelestialBodies.Instance.SelectedBodyInfo.Atmosphere * 0.01d;

                 }

                 else

                 {

-                    SimulationManager.Instance.Atmosphere = 0;

-                }

-

-                SimulationManager.Instance.TryStartSimulation();

+                    SimulationManager.Atmosphere = 0;

+                }

+

+                SimulationManager.TryStartSimulation();

             }

             catch

             {

@@ -174,7 +174,7 @@
                     return;

                 }

 

-                SimulationManager.Instance.RequestSimulation();

+                SimulationManager.RequestSimulation();

 

                 this.windowPosition = GUILayout.Window(this.windowId, this.windowPosition, this.Window, string.Empty, this.windowStyle);

 

@@ -244,9 +244,9 @@
 

             // Details

             GUILayout.BeginVertical(GUILayout.Width(100.0f));

-            GUILayout.Label(SimulationManager.Instance.LastStage.partCount.ToString(), this.infoStyle);

-            GUILayout.Label(SimulationManager.Instance.LastStage.totalDeltaV.ToString("#,0.") + " m/s", this.infoStyle);

-            GUILayout.Label(SimulationManager.Instance.LastStage.TWR, this.infoStyle);

+            //GUILayout.Label(SimulationManager.Instance.LastStage.partCount.ToString(), this.infoStyle);

+            GUILayout.Label(SimulationManager.LastStage.TotalDeltaV.ToString("#,0.") + " m/s", this.infoStyle);

+            //GUILayout.Label(SimulationManager.Instance.LastStage.TWR, this.infoStyle);

             GUILayout.EndVertical();

 

             GUILayout.EndHorizontal();


--- a/KerbalEngineer/Extensions/PartExtensions.cs
+++ b/KerbalEngineer/Extensions/PartExtensions.cs
@@ -60,6 +60,11 @@
         public static T GetModule<T>(this Part part, int classId) where T : PartModule

         {

             return (T)Convert.ChangeType(part.Modules[classId], typeof (T));

+        }

+

+        public static List<T> GetModules<T>(this Part part) where T : PartModule

+        {

+            return part.Modules.OfType<T>().ToList();

         }

 

         /// <summary>


--- a/KerbalEngineer/FlightEngineer/SectionList.cs
+++ b/KerbalEngineer/FlightEngineer/SectionList.cs
@@ -236,9 +236,9 @@
             }

 

             AtmosphericDetails.Instance.Update();

-            SimulationManager.Instance.Gravity = FlightGlobals.getGeeForceAtPosition(FlightGlobals.ActiveVessel.GetWorldPos3D()).magnitude;

-            SimulationManager.Instance.Atmosphere = FlightGlobals.getAtmDensity(FlightGlobals.ActiveVessel.staticPressure);

-            SimulationManager.Instance.TryStartSimulation();

+            SimulationManager.Gravity = FlightGlobals.getGeeForceAtPosition(FlightGlobals.ActiveVessel.GetWorldPos3D()).magnitude;

+            SimulationManager.Atmosphere = FlightGlobals.getAtmDensity(FlightGlobals.ActiveVessel.staticPressure);

+            SimulationManager.TryStartSimulation();

         }

 

         #endregion


--- a/KerbalEngineer/FlightEngineer/Vessel/DeltaVStaged.cs
+++ b/KerbalEngineer/FlightEngineer/Vessel/DeltaVStaged.cs
@@ -4,6 +4,7 @@
 

 #region Using Directives

 

+using KerbalEngineer.Extensions;

 using KerbalEngineer.Simulation;

 

 #endregion

@@ -23,20 +24,20 @@
 

         public override void Update()

         {

-            SimulationManager.Instance.RequestSimulation();

+            SimulationManager.RequestSimulation();

         }

 

         public override void Draw()

         {

             var stageCount = 0;

 

-            for (var i = SimulationManager.Instance.Stages.Length - 1; i > -1; i--)

+            for (var i = SimulationManager.Stages.Length - 1; i > -1; i--)

             {

-                var stage = SimulationManager.Instance.Stages[i];

-                if (stage.thrust > 0)

+                var stage = SimulationManager.Stages[i];

+                if (stage.Thrust > 0)

                 {

                     stageCount++;

-                    this.DrawLine("DeltaV (" + stage.Number + ")", stage.DeltaV);

+                    this.DrawLine("DeltaV (" + stage.Number + ")", stage.DeltaV.ToSpeed());

                 }

             }

 


--- a/KerbalEngineer/FlightEngineer/Vessel/DeltaVTotal.cs
+++ b/KerbalEngineer/FlightEngineer/Vessel/DeltaVTotal.cs
@@ -4,6 +4,7 @@
 

 #region Using Directives

 

+using KerbalEngineer.Extensions;

 using KerbalEngineer.Simulation;

 

 #endregion

@@ -21,12 +22,12 @@
 

         public override void Update()

         {

-            SimulationManager.Instance.RequestSimulation();

+            SimulationManager.RequestSimulation();

         }

 

         public override void Draw()

         {

-            this.DrawLine(SimulationManager.Instance.LastStage.TotalDeltaV);

+            this.DrawLine(SimulationManager.LastStage.TotalDeltaV.ToSpeed());

         }

     }

 }

--- a/KerbalEngineer/FlightEngineer/Vessel/SpecificImpulse.cs
+++ b/KerbalEngineer/FlightEngineer/Vessel/SpecificImpulse.cs
@@ -21,12 +21,12 @@
 

         public override void Update()

         {

-            SimulationManager.Instance.RequestSimulation();

+            SimulationManager.RequestSimulation();;

         }

 

         public override void Draw()

-        {

-            this.DrawLine(SimulationManager.Instance.LastStage.Isp);

+        {          

+            this.DrawLine(SimulationManager.LastStage.Isp.ToString());

         }

     }

 }

--- a/KerbalEngineer/FlightEngineer/Vessel/ThrustActual.cs
+++ b/KerbalEngineer/FlightEngineer/Vessel/ThrustActual.cs
@@ -4,6 +4,7 @@
 

 #region Using Directives

 

+using KerbalEngineer.Extensions;

 using KerbalEngineer.Simulation;

 

 #endregion

@@ -21,12 +22,12 @@
 

         public override void Update()

         {

-            SimulationManager.Instance.RequestSimulation();

+            SimulationManager.RequestSimulation();

         }

 

         public override void Draw()

         {

-            this.DrawLine(SimulationManager.Instance.LastStage.ActualThrust);

+            this.DrawLine(SimulationManager.LastStage.ActualThrust.ToForce());

         }

     }

 }

--- a/KerbalEngineer/FlightEngineer/Vessel/ThrustToWeight.cs
+++ b/KerbalEngineer/FlightEngineer/Vessel/ThrustToWeight.cs
@@ -21,12 +21,12 @@
 

         public override void Update()

         {

-            SimulationManager.Instance.RequestSimulation();

+            SimulationManager.RequestSimulation();

         }

 

         public override void Draw()

         {

-            this.DrawLine(SimulationManager.Instance.LastStage.TWR);

+            this.DrawLine(SimulationManager.LastStage.ThrustToWeight.ToString("0.00"));

         }

     }

 }

--- a/KerbalEngineer/FlightEngineer/Vessel/ThrustTotal.cs
+++ b/KerbalEngineer/FlightEngineer/Vessel/ThrustTotal.cs
@@ -4,6 +4,7 @@
 

 #region Using Directives

 

+using KerbalEngineer.Extensions;

 using KerbalEngineer.Simulation;

 

 #endregion

@@ -21,12 +22,12 @@
 

         public override void Update()

         {

-            SimulationManager.Instance.RequestSimulation();

+            SimulationManager.RequestSimulation();

         }

 

         public override void Draw()

         {

-            this.DrawLine(SimulationManager.Instance.LastStage.Thrust);

+            this.DrawLine(SimulationManager.LastStage.Thrust.ToForce());

         }

     }

 }

--- a/KerbalEngineer/FlightEngineer/Vessel/TotalMass.cs
+++ b/KerbalEngineer/FlightEngineer/Vessel/TotalMass.cs
@@ -4,6 +4,7 @@
 

 #region Using Directives

 

+using KerbalEngineer.Extensions;

 using KerbalEngineer.Simulation;

 

 #endregion

@@ -21,12 +22,12 @@
 

         public override void Update()

         {

-            SimulationManager.Instance.RequestSimulation();

+            SimulationManager.RequestSimulation();

         }

 

         public override void Draw()

         {

-            this.DrawLine(SimulationManager.Instance.LastStage.Mass);

+            this.DrawLine(SimulationManager.LastStage.TotalMass.ToMass());

         }

     }

 }

--- a/KerbalEngineer/KerbalEngineer.csproj
+++ b/KerbalEngineer/KerbalEngineer.csproj
@@ -98,9 +98,12 @@
     <Compile Include="FlightEngineer\Vessel\SpecificImpulse.cs" />

     <Compile Include="FlightEngineer\Vessel\DeltaVTotal.cs" />

     <Compile Include="FlightEngineer\Vessel\DeltaVStaged.cs" />

+    <Compile Include="LogMsg.cs" />

     <Compile Include="Properties\AssemblyInfo.cs" />

     <Compile Include="Settings\Setting.cs" />

     <Compile Include="Settings\SettingList.cs" />

+    <Compile Include="Simulation\AttachNodeSim.cs" />

+    <Compile Include="Simulation\EngineSim.cs" />

     <Compile Include="Simulation\PartSim.cs" />

     <Compile Include="Simulation\ResourceContainer.cs" />

     <Compile Include="Simulation\SimulationManager.cs" />


--- /dev/null
+++ b/KerbalEngineer/LogMsg.cs
@@ -1,1 +1,23 @@
+using System.Text;

+

+using UnityEngine;

+

+namespace KerbalEngineer
+{
+    public class LogMsg
+    {
+        public StringBuilder buf;
 
+        public LogMsg()
+        {
+            this.buf = new StringBuilder();
+        }
+
+        public void Flush()
+        {
+            MonoBehaviour.print(this.buf);
+            this.buf.Length = 0;
+        }
+    }
+}
+

--- /dev/null
+++ b/KerbalEngineer/Simulation/AttachNodeSim.cs
@@ -1,1 +1,39 @@
+using System;

+

+namespace KerbalEngineer.Simulation
+{
+    class AttachNodeSim
+    {
+        public PartSim attachedPartSim;
+        public AttachNode.NodeType nodeType;
+        public String id;
 
+        public AttachNodeSim(PartSim partSim, String newId, AttachNode.NodeType newNodeType)
+        {
+            this.attachedPartSim = partSim;
+            this.nodeType = newNodeType;
+            this.id = newId;
+        }
+
+#if LOG
+        public void DumpToBuffer(StringBuilder buffer)
+        {
+            if (attachedPartSim == null)
+            {
+                buffer.Append("<staged>:<n>");
+            }
+            else
+            {
+                buffer.Append(attachedPartSim.name);
+                buffer.Append(":");
+                buffer.Append(attachedPartSim.partId);
+            }
+            buffer.Append("#");
+            buffer.Append(nodeType);
+            buffer.Append(":");
+            buffer.Append(id);
+        }
+#endif
+    }
+}
+

--- /dev/null
+++ b/KerbalEngineer/Simulation/EngineSim.cs
@@ -1,1 +1,183 @@
+// Kerbal Engineer Redux
+// Author:  CYBUTEK
+// License: Attribution-NonCommercial-ShareAlike 3.0 Unported

+

+using System;

+using System.Collections.Generic;

+using System.Text;

+

+using UnityEngine;

+

+namespace KerbalEngineer.Simulation
+{
+    public class EngineSim
+    {
+        ResourceContainer resourceConsumptions = new ResourceContainer();
 
+        public PartSim partSim;
+
+        public double thrust = 0;
+        public double actualThrust = 0;
+        public double isp = 0;
+
+        // Add thrust vector to account for directional losses
+        //public Vector3d thrustVec;
+
+        public EngineSim(PartSim theEngine, double atmosphere,
+                            float maxThrust,
+                            float thrustPercentage,
+                            float requestedThrust,
+                            FloatCurve atmosphereCurve,
+                            bool throttleLocked,
+                            List<Propellant> propellants)
+        {
+            this.partSim = theEngine;
+
+            this.thrust = maxThrust * (thrustPercentage / 100f);
+
+            double flowRate = 0d;
+            if (this.partSim.hasVessel)
+            {
+                this.actualThrust = requestedThrust;
+                this.isp = atmosphereCurve.Evaluate((float)this.partSim.part.staticPressureAtm);
+
+                if (throttleLocked)
+                {
+                    flowRate = this.thrust / (this.isp * 9.81d);
+                }
+                else
+                {
+                    if (this.partSim.isLanded)
+                    {
+                        // Why does it force a non-zero flow rate when landed?
+                        flowRate = Math.Max(0.000001d, this.thrust * FlightInputHandler.state.mainThrottle) / (this.isp * 9.81d);
+                    }
+                    else
+                    {
+                        if (requestedThrust > 0)
+                            flowRate = requestedThrust / (this.isp * 9.81d);
+                        else
+                            flowRate = this.thrust / (this.isp * 9.81d);
+                    }
+                }
+            }
+            else
+            {
+                this.isp = atmosphereCurve.Evaluate((float)atmosphere);
+                flowRate = this.thrust / (this.isp * 9.81d);
+            }
+
+            StringBuilder buffer = new StringBuilder(1024);
+            buffer.AppendFormat("flowRate = {0:g6}\n", flowRate);
+
+            float flowMass = 0f;
+
+            foreach (Propellant propellant in propellants)
+                flowMass += propellant.ratio * ResourceContainer.GetResourceDensity(propellant.id);
+
+            buffer.AppendFormat("flowMass = {0:g6}\n", flowMass);
+
+            foreach (Propellant propellant in propellants)
+            {
+                if (propellant.name == "ElectricCharge" || propellant.name == "IntakeAir")
+                    continue;
+
+                double consumptionRate = propellant.ratio * flowRate / flowMass;
+                buffer.AppendFormat("Add consumption({0}, {1}:{2:d}) = {3:g6}\n", ResourceContainer.GetResourceName(propellant.id), theEngine.name, theEngine.partId, consumptionRate);
+                this.resourceConsumptions.Add(propellant.id, consumptionRate);
+            }
+            MonoBehaviour.print(buffer);
+        }
+
+
+        public bool SetResourceDrains(List<PartSim> allParts, List<PartSim> allFuelLines, HashSet<PartSim> drainingParts)
+        {
+            // A dictionary to hold a set of parts for each resource
+            Dictionary<int, HashSet<PartSim>> sourcePartSets = new Dictionary<int, HashSet<PartSim>>();
+
+            foreach (int type in this.resourceConsumptions.Types)
+            {
+                HashSet<PartSim> sourcePartSet = null;
+                switch (ResourceContainer.GetResourceFlowMode(type))
+                {
+                    case ResourceFlowMode.NO_FLOW:
+                        if (this.partSim.resources[type] > 1f)
+                        {
+                            sourcePartSet = new HashSet<PartSim>();
+                            //MonoBehaviour.print("SetResourceDrains(" + name + ":" + partId + ") setting sources to just this");
+                            sourcePartSet.Add(this.partSim);
+                        }
+                        break;
+
+                    case ResourceFlowMode.ALL_VESSEL:
+                        foreach (PartSim aPartSim in allParts)
+                        {
+                            if (aPartSim.resources[type] > 1f)
+                            {
+                                if (sourcePartSet == null)
+                                    sourcePartSet = new HashSet<PartSim>();
+
+                                sourcePartSet.Add(aPartSim);
+                            }
+                        }
+                        break;
+
+                    case ResourceFlowMode.STACK_PRIORITY_SEARCH:
+                        HashSet<PartSim> visited = new HashSet<PartSim>();
+                        sourcePartSet = this.partSim.GetSourceSet(type, allParts, allFuelLines, visited);
+                        break;
+
+                    default:
+                        MonoBehaviour.print("SetResourceDrains(" + this.partSim.name + ":" + this.partSim.partId + ") No flow type for " + ResourceContainer.GetResourceName(type) + ")");
+                        break;
+                }
+
+                if (sourcePartSet != null && sourcePartSet.Count > 0)
+                    sourcePartSets[type] = sourcePartSet;
+            }
+
+            // If we don't have sources for all the needed resources then return false without setting up any drains
+            foreach (int type in this.resourceConsumptions.Types)
+            {
+                if (!sourcePartSets.ContainsKey(type))
+                    return false;
+            }
+
+            // Now we set the drains on the members of the sets and update the draining parts set
+            foreach (int type in this.resourceConsumptions.Types)
+            {
+                HashSet<PartSim> sourcePartSet = sourcePartSets[type];
+                // Loop through the members of the set 
+                double amount = this.resourceConsumptions[type] / sourcePartSet.Count;
+                foreach (PartSim partSim in sourcePartSet)
+                {
+#if LOG
+                    MonoBehaviour.print("Adding drain of " + amount + " " + ResourceContainer.GetResourceName(type) + " to " + partSim.name + ":" + partSim.partId);
+#endif
+                    partSim.resourceDrains.Add(type, amount);
+                    drainingParts.Add(partSim);
+                }
+            }
+
+            return true;
+        }
+
+
+        public ResourceContainer ResourceConsumptions
+        {
+            get
+            {
+                return this.resourceConsumptions;
+            }
+        }
+
+#if LOG
+        public void DumpEngineToBuffer(StringBuilder buffer, String prefix)
+        {
+            buffer.Append(prefix);
+            buffer.AppendFormat("[thrust = {0:g6}, actual = {1:g6}, isp = {2:g6}\n", thrust, actualThrust, isp);
+        }
+#endif
+    }
+}
+

--- a/KerbalEngineer/Simulation/PartSim.cs
+++ b/KerbalEngineer/Simulation/PartSim.cs
@@ -1,525 +1,537 @@
-// Name:    Kerbal Engineer Redux
-// Author:  CYBUTEK
-// License: Attribution-NonCommercial-ShareAlike 3.0 Unported
-//
-// This class has taken a lot of inspiration from r4m0n's MuMech FuelFlowSimulator.  Although extremely
-// similar to the code used within MechJeb, it is a clean re-write.  The similarities are a testiment
-// to how well the MuMech code works and the robustness of the simulation algorithem used.
+// Kerbal Engineer Redux

+// Author:  CYBUTEK

+// License: Attribution-NonCommercial-ShareAlike 3.0 Unported

+//

+// This class has taken a lot of inspiration from r4m0n's MuMech FuelFlowSimulator.  Although extremely

+// similar to the code used within MechJeb, it is a clean re-write.  The similarities are a testiment

+// to how well the MuMech code works and the robustness of the simulation algorithem used.

+

+using System;

+using System.Collections.Generic;

+using System.Linq;

+

+using KerbalEngineer.Extensions;

+

+using UnityEngine;

+

+namespace KerbalEngineer.Simulation

+{

+    public class PartSim

+    {

+        public ResourceContainer resources = new ResourceContainer();

+        public ResourceContainer resourceDrains = new ResourceContainer();

+        ResourceContainer resourceFlowStates = new ResourceContainer();

+        ResourceContainer resourceConsumptions = new ResourceContainer();

+

+        Dictionary<int, bool> resourceCanSupply = new Dictionary<int, bool>();

+

+        List<AttachNodeSim> attachNodes = new List<AttachNodeSim>();

+

+        public Part part;              // This is only set while the data structures are being initialised

+        public int partId = 0;

+        public String name;

+        public PartSim parent;

+        public PartSim fuelLineTarget;

+        public bool hasVessel;

+        public bool isLanded;

+        public int decoupledInStage;

+        public int inverseStage;

+        public int cost;

+        double baseMass = 0d;

+        double startMass = 0d;

+        public double thrust = 0;

+        public double actualThrust = 0;

+        public double isp = 0;

+        public String noCrossFeedNodeKey;

+        public bool fuelCrossFeed;

+        public bool isEngine;

+        public bool isFuelLine;

+        public bool isFuelTank;

+        public bool isDecoupler;

+        public bool isDockingNode;

+        public bool isStrutOrFuelLine;

+        public bool isSolidMotor;

+        public bool isSepratron;

+        public bool hasMultiModeEngine;

+        public bool hasModuleEnginesFX;

+        public bool hasModuleEngines;

+

+        public PartSim(Part thePart, int id, double atmosphere)

+        {

+            this.part = thePart;

+            this.partId = id;

+            this.name = this.part.partInfo.name;

+            //MonoBehaviour.print("Create PartSim for " + name);

+            

+            this.parent = null;

+            this.fuelCrossFeed = this.part.fuelCrossFeed;

+            this.noCrossFeedNodeKey = this.part.NoCrossFeedNodeKey;

+            this.decoupledInStage = this.DecoupledInStage(this.part);

+            this.isDecoupler = this.IsDecoupler(this.part);

+            this.isDockingNode = this.IsDockingNode();

+            this.isFuelLine = this.part is FuelLine;

+            this.isFuelTank = this.part is FuelTank;

+            this.isStrutOrFuelLine = this.IsStrutOrFuelLine();

+            this.isSolidMotor = this.IsSolidMotor();

+            this.isSepratron = this.IsSepratron();

+            this.inverseStage = this.part.inverseStage;

+            //MonoBehaviour.print("inverseStage = " + inverseStage);

+

+            this.cost = this.part.partInfo.cost;

+

+            if (!this.part.Modules.Contains("LaunchClamp") && this.part.physicalSignificance == Part.PhysicalSignificance.FULL)

+                this.baseMass = this.part.mass;

+

+            foreach (PartResource resource in this.part.Resources)

+            {

+                // Make sure it isn't NaN as this messes up the part mass and hence most of the values

+                // This can happen if a resource capacity is 0 and tweakable

+                if (!Double.IsNaN(resource.amount))

+                {

+                    //MonoBehaviour.print(resource.resourceName + " = " + resource.amount);

+                    this.resources.Add(resource.info.id, resource.amount);

+                    this.resourceFlowStates.Add(resource.info.id, resource.flowState ? 1 : 0);

+                }

+                else

+                {

+                    MonoBehaviour.print(resource.resourceName + " is NaN. Skipping.");

+                }

+            }

+

+            this.startMass = this.GetMass();

+

+            this.hasVessel = (this.part.vessel != null);

+            this.isLanded = this.hasVessel && this.part.vessel.Landed;

+

+            this.hasMultiModeEngine = this.part.HasModule<MultiModeEngine>();

+            this.hasModuleEnginesFX = this.part.HasModule<ModuleEnginesFX>();

+            this.hasModuleEngines = this.part.HasModule<ModuleEngines>();

+

+            this.isEngine = this.hasMultiModeEngine || this.hasModuleEnginesFX || this.hasModuleEngines;

+

+            //MonoBehaviour.print("Created " + name + ". Decoupled in stage " + decoupledInStage);

+        }

+

+        public void CreateEngineSims(List<EngineSim> allEngines, double atmosphere)

+        {

+            LogMsg log = new LogMsg();

+            log.buf.AppendLine("CreateEngineSims for " + this.name);

+

+            foreach (PartModule partMod in this.part.Modules)

+            {

+                log.buf.AppendLine("Module: " + partMod.moduleName);

+            }

+

+            if (this.hasMultiModeEngine)

+            {

+                // A multi-mode engine has multiple ModuleEnginesFX but only one is active at any point

+                // The mode of the engine is the engineID of the ModuleEnginesFX that is active

+                string mode = this.part.GetModule<MultiModeEngine>().mode;

+

+                foreach (ModuleEnginesFX engine in this.part.GetModules<ModuleEnginesFX>())

+                {

+                    if (engine.engineID == mode)

+                    {

+                        EngineSim engineSim = new EngineSim(this, atmosphere,

+                                                            engine.maxThrust,

+                                                            engine.thrustPercentage,

+                                                            engine.requestedThrust,

+                                                            engine.atmosphereCurve,

+                                                            engine.throttleLocked,

+                                                            engine.propellants);

+                        allEngines.Add(engineSim);

+                    }

+                }

+            }

+            else

+            {

+

+                if (this.hasModuleEnginesFX)

+                {

+                    foreach (ModuleEnginesFX engine in this.part.GetModules<ModuleEnginesFX>())

+                    {

+                        EngineSim engineSim = new EngineSim(this, atmosphere,

+                                                            engine.maxThrust,

+                                                            engine.thrustPercentage,

+                                                            engine.requestedThrust,

+                                                            engine.atmosphereCurve,

+                                                            engine.throttleLocked,

+                                                            engine.propellants);

+                        allEngines.Add(engineSim);

+                    }

+                }

+

+                if (this.hasModuleEngines)

+                {

+                    foreach (ModuleEngines engine in this.part.GetModules<ModuleEngines>())

+                    {

+                        EngineSim engineSim = new EngineSim(this, atmosphere,

+                                                            engine.maxThrust,

+                                                            engine.thrustPercentage,

+                                                            engine.requestedThrust,

+                                                            engine.atmosphereCurve,

+                                                            engine.throttleLocked,

+                                                            engine.propellants);

+                        allEngines.Add(engineSim);

+                    }

+                }

+            }

+

+            log.Flush();

+        }

+

+

+        public void SetupAttachNodes(Dictionary<Part, PartSim> partSimLookup)

+        {

+            this.attachNodes.Clear();

+            foreach (AttachNode attachNode in this.part.attachNodes)

+            {

+                if (attachNode.attachedPart != null)

+                {

+                    PartSim attachedSim;

+                    if (partSimLookup.TryGetValue(attachNode.attachedPart, out attachedSim))

+                    {

+                        this.attachNodes.Add(new AttachNodeSim(attachedSim, attachNode.id, attachNode.nodeType));

+                    }

+                    else

+                    {

+                        MonoBehaviour.print("No PartSim for attached part (" + attachNode.attachedPart.partInfo.name + ")");

+                    }

+                }

+            }

+

+            if (this.isFuelLine)

+            {

+                if ((this.part as FuelLine).target != null)

+                {

+                    PartSim targetSim;

+                    if (partSimLookup.TryGetValue((this.part as FuelLine).target, out targetSim))

+                    {

+                        this.fuelLineTarget = targetSim;

+                    }

+                }

+                else

+                {

+                    this.fuelLineTarget = null;

+                }

+            }

+

+            if (this.part.parent != null)

+            {

+                this.parent = null;

+                if (!partSimLookup.TryGetValue(this.part.parent, out this.parent))

+                {

+                    MonoBehaviour.print("No PartSim for parent part (" + this.part.parent.partInfo.name + ")");

+                }

+            }

+        }

+

+        public int DecoupledInStage(Part thePart, int stage = -1)

+        {

+            if (this.IsDecoupler(thePart))

+            {

+                if (thePart.inverseStage > stage)

+                {

+                    stage = thePart.inverseStage;

+                }

+            }

+

+            if (thePart.parent != null)

+            {

+                stage = this.DecoupledInStage(thePart.parent, stage);

+            }

+

+            return stage;

+        }

+

+        private bool IsDecoupler(Part thePart)

+        {

+            return thePart is Decoupler || thePart is RadialDecoupler || thePart.Modules.OfType<ModuleDecouple>().Count() > 0 || thePart.Modules.OfType<ModuleAnchoredDecoupler>().Count() > 0;

+        }

+

+        private bool IsDockingNode()

+        {

+            return this.part.Modules.OfType<ModuleDockingNode>().Count() > 0;

+        }

+

+        private bool IsStrutOrFuelLine()

+        {

+            return (this.part is StrutConnector || this.part is FuelLine) ? true : false;

+        }

+

+        private bool IsSolidMotor()

+        {

+            foreach (ModuleEngines engine in this.part.Modules.OfType<ModuleEngines>())

+            {

+                if (engine.throttleLocked)

+                    return true;

+            }

+

+            return false;

+        }

+

+        private bool IsSepratron()

+        {

+            if (!this.part.ActivatesEvenIfDisconnected)

+                return false;

+

+            if (this.part is SolidRocket)

+                return true;

+

+            if (this.part.Modules.OfType<ModuleEngines>().Count() == 0)

+                return false;

+

+            if (this.part.Modules.OfType<ModuleEngines>().First().throttleLocked == true)

+                return true;

+

+            return false;

+        }

+

+        public void ReleasePart()

+        {

+            this.part = null;

+        }

+

+

+        // All functions below this point must not rely on the part member (it may be null)

+        //

+

+        public HashSet<PartSim> GetSourceSet(int type, List<PartSim> allParts, List<PartSim> allFuelLines, HashSet<PartSim> visited)

+        {

+            //MonoBehaviour.print("GetSourceSet(" + ResourceContainer.GetResourceName(type) + ") for " + name + ":" + partId);

+

+            HashSet<PartSim> allSources = new HashSet<PartSim>();

+            HashSet<PartSim> partSources = new HashSet<PartSim>();

+

+            // Rule 1: Each part can be only visited once, If it is visited for second time in particular search it returns empty list.

+            if (visited.Contains(this))

+            {

+                //MonoBehaviour.print("Returning empty set, already visited (" + name + ":" + partId + ")");

+                return allSources;

+            }

+

+            //MonoBehaviour.print("Adding this to visited");

+            visited.Add(this);

+

+            // Rule 2: Part performs scan on start of every fuel pipe ending in it. This scan is done in order in which pipes were installed. Then it makes an union of fuel tank sets each pipe scan returned. If the resulting list is not empty, it is returned as result.

+            //MonoBehaviour.print("foreach fuel line");

+            foreach (PartSim partSim in allFuelLines)

+            {

+                if (partSim.fuelLineTarget == this)

+                {

+                    //MonoBehaviour.print("Adding fuel line as source (" + partSim.name + ":" + partSim.partId + ")");

+                    partSources = partSim.GetSourceSet(type, allParts, allFuelLines, visited);

+                    if (partSources.Count > 0)

+                    {

+                        allSources.UnionWith(partSources);

+                        partSources.Clear();

+                    }

+                }

+            }

+

+            if (allSources.Count > 0)

+            {

+                //MonoBehaviour.print("Returning " + allSources.Count + " fuel line sources (" + name + ":" + partId + ")");

+                return allSources;

+            }

+

+            // Rule 3: If the part is not crossfeed capable, it returns empty list.

+            //MonoBehaviour.print("Test crossfeed");

+            if (!this.fuelCrossFeed)

+            {

+                //MonoBehaviour.print("Returning empty set, no cross feed (" + name + ":" + partId + ")");

+                return allSources;

+            }

+

+            // Rule 4: Part performs scan on each of its axially mounted neighbors. 

+            //  Couplers (bicoupler, tricoupler, ...) are an exception, they only scan one attach point on the single attachment side, skip the points on the side where multiple points are. [Experiment]

+            //  Again, the part creates union of scan lists from each of its neighbor and if it is not empty, returns this list. 

+            //  The order in which mount points of a part are scanned appears to be fixed and defined by the part specification file. [Experiment]

+            //MonoBehaviour.print("foreach attach node");

+            foreach (AttachNodeSim attachSim in this.attachNodes)

+            {

+                if (attachSim.attachedPartSim != null)

+                {

+                    if (attachSim.nodeType == AttachNode.NodeType.Stack &&

+                        (attachSim.attachedPartSim.fuelCrossFeed || attachSim.attachedPartSim.isFuelTank) &&

+                        !(this.noCrossFeedNodeKey != null && this.noCrossFeedNodeKey.Length > 0 && attachSim.id.Contains(this.noCrossFeedNodeKey)))

+                    {

+                        //MonoBehaviour.print("Adding attached part as source (" + attachSim.attachedPartSim.name + ":" + attachSim.attachedPartSim.partId + ")");

+                        partSources = attachSim.attachedPartSim.GetSourceSet(type, allParts, allFuelLines, visited);

+                        if (partSources.Count > 0)

+                        {

+                            allSources.UnionWith(partSources);

+                            partSources.Clear();

+                        }

+                    }

+                }

+            }

+

+            if (allSources.Count > 0)

+            {

+                //MonoBehaviour.print("Returning " + allSources.Count + " attached sources (" + name + ":" + partId + ")");

+                return allSources;

+            }

+

+            // Rule 5: If the part is fuel container for searched type of fuel (i.e. it has capability to contain that type of fuel and the fuel type was not disabled [Experiment]) and it contains fuel, it returns itself.

+            // Rule 6: If the part is fuel container for searched type of fuel (i.e. it has capability to contain that type of fuel and the fuel type was not disabled) but it does not contain the requested fuel, it returns empty list. [Experiment]

+            //MonoBehaviour.print("testing enabled container");

+            if (this.resources.HasType(type) && this.resourceFlowStates[type] != 0)

+            {

+                if (this.resources[type] > 1f)

+                    allSources.Add(this);

+

+                //MonoBehaviour.print("Returning this as only source (" + name + ":" + partId + ")");

+                return allSources;

+            }

+

+            // Rule 7: If the part is radially attached to another part and it is child of that part in the ship's tree structure, it scans its parent and returns whatever the parent scan returned. [Experiment] [Experiment]

+            if (this.parent != null)

+            {

+                allSources = this.parent.GetSourceSet(type, allParts, allFuelLines, visited);

+                if (allSources.Count > 0)

+                {

+                    //MonoBehaviour.print("Returning " + allSources.Count + " parent sources (" + name + ":" + partId + ")");

+                    return allSources;

+                }

+            }

+

+            // Rule 8: If all preceding rules failed, part returns empty list.

+            //MonoBehaviour.print("Returning empty set, no sources found (" + name + ":" + partId + ")");

+            return allSources;

+        }

+

+

+        public void RemoveAttachedParts(HashSet<PartSim> partSims)

+        {

+            // Loop through the attached parts

+            foreach (AttachNodeSim attachSim in this.attachNodes)

+            {

+                // If the part is in the set then "remove" it by clearing the PartSim reference

+                if (partSims.Contains(attachSim.attachedPartSim))

+                    attachSim.attachedPartSim = null;

+            }

+        }

+

+

+        public void DrainResources(double time)

+        {

+            //MonoBehaviour.print("DrainResources(" + name + ":" + partId + ", " + time + ")");

+            foreach (int type in this.resourceDrains.Types)

+            {

+                //MonoBehaviour.print("draining " + (time * resourceDrains[type]) + " " + ResourceContainer.GetResourceName(type));

+                this.resources.Add(type, -time * this.resourceDrains[type]);

+                //MonoBehaviour.print(ResourceContainer.GetResourceName(type) + " left = " + resources[type]);

+            }

+        }

+

+        public double TimeToDrainResource()

+        {

+            //MonoBehaviour.print("TimeToDrainResource(" + name + ":" + partId + ")");

+            double time = double.MaxValue;

+

+            foreach (int type in this.resourceDrains.Types)

+            {

+                //MonoBehaviour.print("type = " + ResourceContainer.GetResourceName(type) + "  amount = " + resources[type] + "  rate = " + resourceDrains[type]);

+                if (this.resourceDrains[type] > 0)

+                    time = Math.Min(time, this.resources[type] / this.resourceDrains[type]);

+            }

+

+            //if (time < double.MaxValue)

+            //    MonoBehaviour.print("TimeToDrainResource(" + name + ":" + partId + ") = " + time);

+            return time;

+        }

+

+        public double GetStartMass()

+        {

+            return this.startMass;

+        }

+

+        public double GetMass()

+        {

+            double mass = this.baseMass;

+

+            foreach (int type in this.resources.Types)

+                mass += this.resources.GetResourceMass(type);

+

+            return mass;

+        }

+

+        public ResourceContainer Resources

+        {

+            get

+            {

+                return this.resources;

+            }

+        }

+

+        public ResourceContainer ResourceConsumptions

+        {

+            get

+            {

+                return this.resourceConsumptions;

+            }

+        }

+

+        public ResourceContainer ResourceDrains

+        {

+            get

+            {

+                return this.resourceDrains;

+            }

+        }

+

+#if LOG

+        public String DumpPartAndParentsToBuffer(StringBuilder buffer, String prefix)

+        {

+            if (parent != null)

+            {

+                prefix = parent.DumpPartAndParentsToBuffer(buffer, prefix) + " ";

+            }

+

+            DumpPartToBuffer(buffer, prefix);

+

+            return prefix;

+        }

+

+        public void DumpPartToBuffer(StringBuilder buffer, String prefix, List<PartSim> allParts = null)

+        {

+            buffer.Append(prefix);

+            buffer.Append(name);

+            buffer.AppendFormat(":[id = {0:d}, decouple = {1:d}, invstage = {2:d}", partId, decoupledInStage, inverseStage);

+

+            buffer.AppendFormat(", isSep = {0}", isSepratron);

+

+            foreach (int type in resources.Types)

+                buffer.AppendFormat(", {0} = {1:g6}", ResourceContainer.GetResourceName(type), resources[type]);

+

+            if (attachNodes.Count > 0)

+            {

+                buffer.Append(", attached = <");

+                attachNodes[0].DumpToBuffer(buffer);

+                for (int i = 1; i < attachNodes.Count; i++)

+                {

+                    buffer.Append(", ");

+                    attachNodes[i].DumpToBuffer(buffer);

+                }

+                buffer.Append(">");

+            }

+

+            // Add more info here

+

+            buffer.Append("]\n");

+

+            if (allParts != null)

+            {

+                String newPrefix = prefix + " ";

+                foreach (PartSim partSim in allParts)

+                {

+                    if (partSim.parent == this)

+                        partSim.DumpPartToBuffer(buffer, newPrefix, allParts);

+                }

+            }

+        }

+#endif

+    }

+}

 
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Linq;
-using KerbalEngineer.Extensions;
-using UnityEngine;
-
-namespace KerbalEngineer.Simulation
-{
-    public class PartSim
-    {
-        ResourceContainer resources = new ResourceContainer();
-        ResourceContainer resourceConsumptions = new ResourceContainer();
-        ResourceContainer resourceDrains = new ResourceContainer();
-
-        List<PartSim> sourceParts = new List<PartSim>();
-
-        public Part part;
-        public int decoupledInStage;
-        public double thrust = 0;
-        public double actualThrust = 0;
-        public double isp = 0;
-
-        public PartSim(Part part, double atmosphere)
-        {
-            this.part = part;
-
-            foreach (PartResource resource in part.Resources)
-            {
-                resources.Add(resource.info.id, resource.amount);
-            }
-
-            foreach (ModuleEngines engine in part.Modules.OfType<ModuleEngines>())
-                SetEngineInfo(engine.requestedThrust, engine.maxThrust, engine.thrustPercentage, engine.atmosphereCurve, atmosphere);
-
-            if (part.HasModule<MultiModeEngine>())
-            {
-                string mode = part.GetModule<MultiModeEngine>().mode;
-
-                foreach (ModuleEnginesFX engine in part.Modules.OfType<ModuleEnginesFX>())
-                {
-                    if (engine.engineID == mode)
-                        SetEngineInfo(engine.requestedThrust, engine.maxThrust, engine.thrustPercentage, engine.atmosphereCurve, atmosphere);
-                }
-            }
-
-            decoupledInStage = DecoupledInStage();
-        }
-
-        public void SetEngineInfo(float requestedThrust, float maxThrust, float thrustPercentage, FloatCurve atmosphereCurve, double atmosphere)
-        {
-            float thrustLimiter = (thrustPercentage > 0) ? thrustPercentage / 100f : 0f;
-
-            if (part.vessel != null)
-            {
-                actualThrust += requestedThrust * thrustLimiter;
-                isp = atmosphereCurve.Evaluate((float)part.staticPressureAtm);
-            }
-            else
-            {
-                isp = atmosphereCurve.Evaluate((float)atmosphere);
-            }
-
-            thrust += maxThrust * thrustLimiter;
-        }
-
-        public bool CanDrawNeededResources(List<PartSim> partSims)
-        {
-            foreach (int type in resourceConsumptions.Types)
-            {
-                switch (ResourceContainer.GetResourceFlowMode(type))
-                {
-                    case ResourceFlowMode.NO_FLOW:
-                        if (resources[type] < 1f)
-                        {
-                            return false;
-                        }
-                        break;
-
-                    case ResourceFlowMode.ALL_VESSEL:
-                        foreach (PartSim partSim in partSims)
-                        {
-                            if (partSim.resources[type] > 1f)
-                            {
-                                return true;
-                            }
-                        }
-                        return false;
-
-                    case ResourceFlowMode.STACK_PRIORITY_SEARCH:
-                        if (!CanSupplyResourceRecursive(type))
-                        {
-                            return false;
-                        }
-                        break;
-
-                    default:
-                        return false;
-                }
-            }
-            return true;
-        }
-
-        public void SetResourceConsumptions()
-        {
-            foreach (ModuleEngines engine in part.Modules.OfType<ModuleEngines>())
-            {
-                float thrustLimiter = (engine.thrustPercentage > 0) ? engine.thrustPercentage / 100f : 0f;
-                double flowRate = GetPropellantFlowRate(engine.requestedThrust * thrustLimiter, engine.maxThrust * thrustLimiter, engine.throttleLocked);
-                double flowMass = GetPropellantFlowMass(engine.propellants);
-                SetPropellantConsumptionRate(engine.propellants, flowRate, flowMass);
-            }
-
-            if (part.HasModule<MultiModeEngine>())
-            {
-                string mode = part.GetModule<MultiModeEngine>().mode;
-
-                foreach (ModuleEnginesFX engine in part.Modules.OfType<ModuleEnginesFX>())
-                {
-                    if (engine.engineID == mode)
-                    {
-                        float thrustLimiter = (engine.thrustPercentage > 0) ? engine.thrustPercentage / 100f : 0f;
-                        double flowRate = GetPropellantFlowRate(engine.requestedThrust * thrustLimiter, engine.maxThrust * thrustLimiter, engine.throttleLocked);
-                        double flowMass = GetPropellantFlowMass(engine.propellants);
-                        SetPropellantConsumptionRate(engine.propellants, flowRate, flowMass);
-                    }
-                }
-            }
-        }
-
-        public double GetPropellantFlowRate(float requestedThrust, float maxThrust, bool throttleLocked)
-        {
-            if (part.vessel != null)
-            {
-                if (throttleLocked)
-                {
-                    return maxThrust / (isp * Simulation.STD_GRAVITY);
-                }
-                else
-                {
-                    if (part.vessel.Landed)
-                    {
-                        return Math.Max(0.000001d, maxThrust * FlightInputHandler.state.mainThrottle) / (isp * Simulation.STD_GRAVITY);
-                    }
-                    else
-                    {
-                        if (requestedThrust > 0)
-                            return requestedThrust / (isp * Simulation.STD_GRAVITY);
-                        else
-                            return maxThrust / (isp * Simulation.STD_GRAVITY);
-                    }
-                }
-            }
-            else
-            {
-                return maxThrust / (isp * Simulation.STD_GRAVITY);
-            }
-        }
-
-        public double GetPropellantFlowMass(List<Propellant> propellants)
-        {
-            double flowMass = 0d;
-            foreach (Propellant propellant in propellants)
-            {
-                flowMass += propellant.ratio * ResourceContainer.GetResourceDensity(propellant.id);
-            }
-            return flowMass;
-        }
-
-        public void SetPropellantConsumptionRate(List<Propellant> propellants, double flowRate, double flowMass)
-        {
-            foreach (Propellant propellant in propellants)
-            {
-                if (propellant.name == "ElectricCharge" || propellant.name == "IntakeAir")
-                {
-                    continue;
-                }
-
-                double consumptionRate = propellant.ratio * flowRate / flowMass;
-                resourceConsumptions.Add(propellant.id, consumptionRate);
-            }
-        }
-
-        public void SetResourceDrainRates(List<PartSim> partSims)
-        {
-            foreach (int type in resourceConsumptions.Types)
-            {
-                double amount = resourceConsumptions[type];
-
-                switch (ResourceContainer.GetResourceFlowMode(type))
-                {
-                    case ResourceFlowMode.NO_FLOW:
-                        this.resourceDrains.Add(type, amount);
-                        break;
-
-                    case ResourceFlowMode.ALL_VESSEL:
-                        SetResourceDrainRateAllVessel(type, amount, partSims);
-                        break;
-
-                    case ResourceFlowMode.STACK_PRIORITY_SEARCH:
-                        SetResourceDrainRateRecursive(type, amount);
-                        break;
-                }
-            }
-        }
-
-        public void SetSourceNodes(Hashtable partSimLookup)
-        {
-            foreach (PartSim partSim in partSimLookup.Values)
-            {
-                if (partSim.part is FuelLine && (partSim.part as FuelLine).target == this.part)
-                {
-                    sourceParts.Add(partSim);
-                }
-            }
-
-            foreach (AttachNode attachNode in this.part.attachNodes)
-            {
-                if (attachNode.attachedPart != null && attachNode.nodeType == AttachNode.NodeType.Stack &&
-                    (attachNode.attachedPart.fuelCrossFeed || attachNode.attachedPart is FuelTank) &&
-                    !(this.part.NoCrossFeedNodeKey.Length > 0 && attachNode.id.Contains(this.part.NoCrossFeedNodeKey)))
-                {
-                    sourceParts.Add((PartSim)partSimLookup[attachNode.attachedPart]);
-                }
-            }
-
-            if (this.part.parent != null && this.part.parent.fuelCrossFeed == true && !IsDecoupler(this.part.parent))
-            {
-                sourceParts.Add((PartSim)partSimLookup[this.part.parent]);
-            }
-        }
-
-        public void RemoveSourcePart(PartSim part)
-        {
-            if (sourceParts.Contains(part))
-            {
-                sourceParts.Remove(part);
-            }
-        }
-
-        public int DecoupledInStage(Part part = null, int stage = -1)
-        {
-            if (part == null)
-            {
-                part = this.part;
-            }
-
-            if (IsDecoupler(part))
-            {
-                if (part.inverseStage > stage)
-                {
-                    stage = part.inverseStage;
-                }
-            }
-
-            if (part.parent != null)
-            {
-                stage = DecoupledInStage(part.parent, stage);
-            }
-
-            return stage;
-        }
-
-        public void DrainResources(double time)
-        {
-            foreach (int type in resourceDrains.Types)
-            {
-                resources.Add(type, -time * resourceDrains[type]);
-            }
-        }
-
-        public double TimeToDrainResource()
-        {
-            double time = double.MaxValue;
-
-            foreach (int type in resourceDrains.Types)
-            {
-                if (resourceDrains[type] > 0)
-                {
-                    time = Math.Min(time, resources[type] / resourceDrains[type]);
-                }
-            }
-
-            return time;
-        }
-
-        public bool IsDecoupler(Part part = null)
-        {
-
-            if (part == null)
-            {
-                part = this.part;
-            }
-
-            return part is Decoupler || part is RadialDecoupler || part.Modules.OfType<ModuleDecouple>().Count() > 0 || part.Modules.OfType<ModuleAnchoredDecoupler>().Count() > 0;
-        }
-
-        private void SetResourceDrainRateAllVessel(int type, double amount, List<PartSim> partSims)
-        {
-            PartSim source = null;
-
-            foreach (PartSim partSim in partSims)
-            {
-                if (partSim.resources[type] > 1f)
-                {
-                    if (source == null || partSim.InverseStage > source.InverseStage)
-                    {
-                        source = partSim;
-                    }
-                }
-            }
-
-            if (source != null)
-            {
-                source.resourceDrains.Add(type, amount);
-            }
-        }
-
-        private void SetResourceDrainRateRecursive(int type, double amount, List<PartSim> visited = null)
-        {
-            if (visited == null)
-            {
-                visited = new List<PartSim>();
-            }
-
-            List<PartSim> newVisited = new List<PartSim>(visited);
-            newVisited.Add(this);
-
-            List<PartSim> fuelLines = new List<PartSim>();
-            foreach (PartSim partSim in sourceParts)
-            {
-                if (partSim.part is FuelLine && !visited.Contains(partSim))
-                {
-                    if (partSim.CanSupplyResourceRecursive(type, newVisited))
-                    {
-                        fuelLines.Add(partSim);
-                    }
-                }
-            }
-
-            if (fuelLines.Count > 0)
-            {
-                foreach (PartSim fuelLine in fuelLines)
-                {
-                    fuelLine.SetResourceDrainRateRecursive(type, amount / fuelLines.Count, newVisited);
-                }
-                return;
-            }
-
-            foreach (PartSim partSim in sourceParts)
-            {
-                if (!visited.Contains(partSim) && partSim.CanSupplyResourceRecursive(type, newVisited))
-                {
-                    if (DrainFromSourceBeforeSelf(type, partSim))
-                    {
-                        partSim.SetResourceDrainRateRecursive(type, amount, newVisited);
-                        return;
-                    }
-                }
-            }
-
-            if (this.resources[type] > 0)
-            {
-                resourceDrains.Add(type, amount);
-            }
-        }
-
-        private bool CanSupplyResourceRecursive(int type, List<PartSim> visited = null)
-        {
-            if (visited == null)
-            {
-                visited = new List<PartSim>();
-            }
-
-            if (this.resources[type] > 1f)
-            {
-                return true;
-            }
-
-            List<PartSim> newVisited = new List<PartSim>(visited);
-            newVisited.Add(this);
-
-            foreach (PartSim partSim in sourceParts)
-            {
-                if (!visited.Contains(partSim))
-                {
-                    if (partSim.CanSupplyResourceRecursive(type, newVisited))
-                    {
-                        return true;
-                    }
-                }
-            }
-
-            return false;
-        }
-
-        private bool DrainFromSourceBeforeSelf(int type, PartSim source)
-        {
-            if (resources[type] < 1f)
-            {
-                return true;
-            }
-
-            if (source.part != this.part.parent)
-            {
-                return true;
-            }
-
-            if (this.part.parent == null)
-            {
-                return true;
-            }
-
-            foreach (AttachNode attachNode in this.part.parent.attachNodes)
-            {
-                if (attachNode.attachedPart == this.part && attachNode.nodeType != AttachNode.NodeType.Stack)
-                {
-                    return false;
-                }
-            }
-
-            return true;
-        }
-
-        public double GetStartMass(int currentStage)
-        {
-            double mass = 0d;
-
-            if (part.Modules.Contains("LaunchClamp"))
-            {
-                return 0d;
-            }
-
-            if (part.physicalSignificance == Part.PhysicalSignificance.NONE)
-            {
-                mass = part.GetResourceMass();
-            }
-            else
-            {
-                mass = part.mass + part.GetResourceMass();
-            }
-
-            return mass;
-        }
-
-        public double GetMass(int currentStage)
-        {
-            double mass = 0d;
-
-            if (part.Modules.Contains("LaunchClamp"))
-            {
-                return 0d;
-            }
-
-            if (part.physicalSignificance == Part.PhysicalSignificance.FULL)
-            {
-                mass = part.mass;
-            }
-
-            foreach (int type in resources.Types)
-            {
-                mass += resources.GetResourceMass(type);
-            }
-
-            return mass;
-        }
-
-        public ResourceContainer Resources
-        {
-            get
-            {
-                return this.resources;
-            }
-        }
-
-        public ResourceContainer ResourceConsumptions
-        {
-            get
-            {
-                return this.resourceConsumptions;
-            }
-        }
-
-        public ResourceContainer ResourceDrains
-        {
-            get
-            {
-                return this.resourceDrains;
-            }
-        }
-
-        public int InverseStage
-        {
-            get
-            {
-                return part.inverseStage;
-            }
-        }
-
-        public bool IsSolidMotor
-        {
-            get
-            {
-                foreach (ModuleEngines engine in part.Modules.OfType<ModuleEngines>())
-                {
-                    if (engine.throttleLocked)
-                    {
-                        return true;
-                    }
-                }
-
-                return false;
-            }
-        }
-    }
-}

--- a/KerbalEngineer/Simulation/ResourceContainer.cs
+++ b/KerbalEngineer/Simulation/ResourceContainer.cs
@@ -1,11 +1,10 @@
-// Name:    Kerbal Engineer Redux
+// Kerbal Engineer Redux
 // Author:  CYBUTEK
-// License: Attribution-NonCommercial-ShareAlike 3.0 Unported
-
-using System;
-using System.Collections;
-using System.Collections.Generic;
-
+// License: Attribution-NonCommercial-ShareAlike 3.0 Unported

+

+using System.Collections;

+using System.Collections.Generic;

+

 namespace KerbalEngineer.Simulation
 {
     public class ResourceContainer
@@ -16,9 +15,9 @@
         {
             get
             {
-                if (resources.ContainsKey(type))
+                if (this.resources.ContainsKey(type))
                 {
-                    return (double)resources[type];
+                    return (double)this.resources[type];
                 }
                 else
                 {
@@ -27,15 +26,20 @@
             }
             set
             {
-                if (resources.ContainsKey(type))
+                if (this.resources.ContainsKey(type))
                 {
-                    resources[type] = value;
+                    this.resources[type] = value;
                 }
                 else
                 {
-                    resources.Add(type, value);
+                    this.resources.Add(type, value);
                 }
             }
+        }
+
+        public bool HasType(int type)
+        {
+            return this.resources.ContainsKey(type);
         }
 
         public List<int> Types
@@ -44,7 +48,7 @@
             {
                 List<int> types = new List<int>();
 
-                foreach (int key in resources.Keys)
+                foreach (int key in this.resources.Keys)
                 {
                     types.Add(key);
                 }
@@ -59,7 +63,7 @@
             {
                 double mass = 0d;
 
-                foreach (double resource in resources.Values)
+                foreach (double resource in this.resources.Values)
                 {
                     mass += resource;
                 }
@@ -72,9 +76,9 @@
         {
             get
             {
-                foreach (int type in resources.Keys)
+                foreach (int type in this.resources.Keys)
                 {
-                    if ((double)resources[type] > 1d)
+                    if ((double)this.resources[type] > 1d)
                     {
                         return false;
                     }
@@ -86,32 +90,32 @@
 
         public void Add(int type, double amount)
         {
-            if (resources.ContainsKey(type))
+            if (this.resources.ContainsKey(type))
             {
-                resources[type] = (double)resources[type] + amount;
+                this.resources[type] = (double)this.resources[type] + amount;
             }
             else
             {
-                resources.Add(type, amount);
+                this.resources.Add(type, amount);
             }
         }
 
         public void Reset()
         {
-            resources = new Hashtable();
+            this.resources = new Hashtable();
         }
 
         public void Debug()
         {
-            foreach (int key in resources.Keys)
+            foreach (int key in this.resources.Keys)
             {
-                UnityEngine.MonoBehaviour.print(" -> " + GetResourceName(key) + " = " + resources[key]);
+                UnityEngine.MonoBehaviour.print(" -> " + GetResourceName(key) + " = " + this.resources[key]);
             }
         }
 
         public double GetResourceMass(int type)
         {
-            return (double)resources[type] * GetResourceDensity(type);
+            return (double)this.resources[type] * GetResourceDensity(type);
         }
 
         public static ResourceFlowMode GetResourceFlowMode(int type)

--- a/KerbalEngineer/Simulation/Simulation.cs
+++ b/KerbalEngineer/Simulation/Simulation.cs
@@ -1,68 +1,165 @@
-// Name:    Kerbal Engineer Redux
+// Kerbal Engineer Redux
 // Author:  CYBUTEK
 // License: Attribution-NonCommercial-ShareAlike 3.0 Unported
 //
 // This class has taken a lot of inspiration from r4m0n's MuMech FuelFlowSimulator.  Although extremely
 // similar to the code used within MechJeb, it is a clean re-write.  The similarities are a testiment
-// to how well the MuMech code works and the robustness of the simulation algorithem used.
-
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Diagnostics;
-using KerbalEngineer.Extensions;
-using UnityEngine;
-
+// to how well the MuMech code works and the robustness of the simulation algorithem used.

+

+using System;

+using System.Collections.Generic;

+

+using UnityEngine;

+

 namespace KerbalEngineer.Simulation
 {
     public class Simulation
     {
-        List<PartSim> partSims;
-        List<Part> partList;
-
-        int currentStage = 0;
-        bool firstSimulation = true;
-
-        public const double STD_GRAVITY = 9.81d;
-
-        public bool FirstSimulation
-        {
-            get
-            {
-                if (firstSimulation)
-                {
-                    firstSimulation = false;
-                    return true;
-                }
-
-                return false;
-            }
-        }
-
-        public Simulation(List<Part> parts)
-        {
+        private List<Part> partList;
+
+        private List<PartSim> allParts;
+        private List<PartSim> allFuelLines;
+        private HashSet<PartSim> drainingParts;
+        private List<EngineSim> allEngines;
+        private List<EngineSim> activeEngines;
+
+        private int lastStage = 0;
+        private int currentStage = 0;
+
+        private double gravity = 0;
+        private double atmosphere = 0;
+#if LOG
+        private Stopwatch _timer = new Stopwatch();
+#endif
+        private const double STD_GRAVITY = 9.81d;
+        private const double SECONDS_PER_DAY = 86400;
+        
+        public Simulation()
+        {
+#if LOG
+            MonoBehaviour.print("Simulation created");
+#endif
+        }
+
+        // This function prepares the simulation by creating all the necessary data structures it will 
+        // need during the simulation.  All required data is copied from the core game data structures 
+        // so that the simulation itself can be run in a background thread without having issues with 
+        // the core game changing the data while the simulation is running.
+        public bool PrepareSimulation(List<Part> parts, double theGravity, double theAtmosphere = 0)
+        {
+#if LOG
+            MonoBehaviour.print("PrepareSimulation started");
+            _timer.Start();
+#endif
+            // Store the parameters in members for ease of access in other functions
             this.partList = parts;
-        }
-
-        public Stage[] RunSimulation(double gravity, double atmosphere = 0)
-        {
-            currentStage = Staging.lastStage;
-            Stage[] stages = new Stage[currentStage + 1];
-
-            BuildVessel(this.partList, atmosphere);
-
-            while (currentStage >= 0)
-            {
-                Stage stage = new Stage(currentStage);
+            this.gravity = theGravity;
+            this.atmosphere = theAtmosphere;
+            this.lastStage = Staging.lastStage;
+            //MonoBehaviour.print("lastStage = " + lastStage);
+
+            // Create the lists for our simulation parts
+            this.allParts = new List<PartSim>();
+            this.allFuelLines = new List<PartSim>();
+            this.drainingParts = new HashSet<PartSim>();
+            this.allEngines = new List<EngineSim>();
+            this.activeEngines = new List<EngineSim>();
+
+            // A dictionary for fast lookup of Part->PartSim during the preparation phase
+            Dictionary<Part, PartSim> partSimLookup = new Dictionary<Part, PartSim>();
+
+            // First we create a PartSim for each Part (giving each a unique id)
+            int partId = 1;
+            foreach (Part part in this.partList)
+            {
+                // If the part is already in the lookup dictionary then log it and skip to the next part
+                if (partSimLookup.ContainsKey(part))
+                {
+                    MonoBehaviour.print("Part " + part.name + " appears in vessel list more than once");
+                    continue;
+                }
+
+                // Create the PartSim
+                PartSim partSim = new PartSim(part, partId, this.atmosphere);
+
+                // Add it to the Part lookup dictionary and the necessary lists
+                partSimLookup.Add(part, partSim);
+                this.allParts.Add(partSim);
+                if (partSim.isFuelLine)
+                    this.allFuelLines.Add(partSim);
+                if (partSim.isEngine)
+                    partSim.CreateEngineSims(this.allEngines, this.atmosphere);
+
+                partId++;
+            }
+
+            // Now that all the PartSims have been created we can do any set up that needs access to other parts
+            //MonoBehaviour.print("SetupAttachNodes and count stages");
+            foreach (PartSim partSim in this.allParts)
+            {
+                partSim.SetupAttachNodes(partSimLookup);
+                if (partSim.decoupledInStage >= this.lastStage)
+                    this.lastStage = partSim.decoupledInStage + 1;
+            }
+
+            // And finally release the Part references from all the PartSims
+            //MonoBehaviour.print("ReleaseParts");
+            foreach (PartSim partSim in this.allParts)
+                partSim.ReleasePart();
+
+            // And dereference the core's part list
+            this.partList = null;
+#if LOG
+            _timer.Stop();
+            MonoBehaviour.print("PrepareSimulation took " + _timer.ElapsedMilliseconds + "ms");
+            Dump();
+#endif
+            return true;
+        }
+
+        
+        // This function runs the simulation and returns a newly created array of Stage objects
+        public Stage[] RunSimulation()
+        {
+#if LOG
+            MonoBehaviour.print("RunSimulation started");
+#endif
+            // Start with the last stage to simulate
+            // (this is in a member variable so it can be accessed by AllowedToStage and ActiveStage)
+            this.currentStage = this.lastStage;
+
+            // Create the array of stages that will be returned
+            Stage[] stages = new Stage[this.currentStage + 1];
+
+            // Loop through the stages
+            while (this.currentStage >= 0)
+            {
+#if LOG
+                MonoBehaviour.print("Simulating stage " + currentStage);
+                MonoBehaviour.print("ShipMass = " + ShipMass);
+                _timer.Reset();
+                _timer.Start();
+#endif
+                // Update active engines and resource drains
+                this.UpdateResourceDrains();
+
+                // Create the Stage object for this stage
+                Stage stage = new Stage();
+
                 double stageTime = 0d;
-                double stageDeltaV = 0d;
+                double stageDeltaV = 0d;            
                 double totalStageThrust = 0d;
                 double totalStageActualThrust = 0d;
 
                 double totalStageFlowRate = 0d;
                 double totalStageIspFlowRate = 0d;
-
-                foreach (PartSim engine in ActiveEngines)
+                double currentisp = 0;
+                double stageStartMass = this.ShipMass;
+                double stepStartMass = stageStartMass;
+                double stepEndMass = 0;
+
+                // Loop through all the active engines totalling the thrust, actual thrust and mass flow rates
+                foreach (EngineSim engine in this.activeEngines)
                 {
                     totalStageActualThrust += engine.actualThrust;
                     totalStageThrust += engine.thrust;
@@ -71,271 +168,338 @@
                     totalStageIspFlowRate += engine.ResourceConsumptions.Mass * engine.isp;
                 }
 
+                // Calculate the effective isp at this point
                 if (totalStageFlowRate > 0d && totalStageIspFlowRate > 0d)
-                {
-                    stage.isp = totalStageIspFlowRate / totalStageFlowRate;
-                }
-
-                stage.thrust = totalStageThrust;
-                stage.thrustToWeight = (double)(totalStageThrust / (ShipMass * gravity));
-                stage.actualThrust = totalStageActualThrust;
-                stage.actualThrustToWeight = (double)(totalStageActualThrust / (ShipMass * gravity));
-
-                foreach (PartSim partSim in partSims)
-                {
-                    if (partSim.decoupledInStage == currentStage - 1)
-                    {
-                        stage.cost += partSim.part.partInfo.cost;
-                        stage.mass += partSim.GetStartMass(currentStage);
-                    }
-                }
-
+                    currentisp = totalStageIspFlowRate / totalStageFlowRate;
+                else
+                    currentisp = 0;
+
+                // Store various things in the Stage object
+                stage.Thrust = totalStageThrust;
+                stage.ThrustToWeight = (double)(totalStageThrust / (stageStartMass * this.gravity));
+                stage.ActualThrust = totalStageActualThrust;
+                stage.ActualThrustToWeight = (double)(totalStageActualThrust / (stageStartMass * this.gravity));
+
+                // Calculate the cost and mass of this stage
+                foreach (PartSim partSim in this.allParts)
+                {
+                    if (partSim.decoupledInStage == this.currentStage - 1)
+                    {
+                        stage.Cost += partSim.cost;
+                        stage.Mass += partSim.GetStartMass();
+                    }
+                }
+#if LOG
+                MonoBehaviour.print("Stage setup took " + _timer.ElapsedMilliseconds + "ms");
+#endif
+                // Now we will loop until we are allowed to stage
                 int loopCounter = 0;
-                while (!AllowedToStage())
+                while (!this.AllowedToStage())
                 {
                     loopCounter++;
-
-                    List<PartSim> engines = ActiveEngines;
-                    totalStageThrust = 0d;
-                    foreach (PartSim engine in engines)
-                    {
-                        if (engine.actualThrust > 0d)
+                    //MonoBehaviour.print("loop = " + loopCounter);
+
+                    // Calculate how long each draining tank will take to drain and run for the minimum time
+                    double resourceDrainTime = double.MaxValue;
+                    foreach (PartSim partSim in this.drainingParts)
+                    {
+                        double time = partSim.TimeToDrainResource();
+                        if (time < resourceDrainTime)
+                            resourceDrainTime = time;
+                    }
+#if LOG
+                    MonoBehaviour.print("Drain time = " + resourceDrainTime);
+#endif
+                    foreach (PartSim partSim in this.drainingParts)
+                        partSim.DrainResources(resourceDrainTime);
+
+                    // Get the mass after draining
+                    stepEndMass = this.ShipMass;
+                    stageTime += resourceDrainTime;
+
+                    // If we have drained anything and the masses make sense then add this step's deltaV to the stage total
+                    if (resourceDrainTime > 0d && stepStartMass > stepEndMass && stepStartMass > 0d && stepEndMass > 0d)
+                        stageDeltaV += (currentisp * STD_GRAVITY) * Math.Log(stepStartMass / stepEndMass);
+
+                    // Update the active engines and resource drains for the next step
+                    this.UpdateResourceDrains();
+
+                    // Recalculate the current isp for the next step
+                    totalStageFlowRate = 0d;
+                    totalStageIspFlowRate = 0d;
+                    foreach (EngineSim engine in this.activeEngines)
+                    {
+                        totalStageFlowRate += engine.ResourceConsumptions.Mass;
+                        totalStageIspFlowRate += engine.ResourceConsumptions.Mass * engine.isp;
+                    }
+
+                    if (totalStageFlowRate > 0d && totalStageIspFlowRate > 0d)
+                        currentisp = totalStageIspFlowRate / totalStageFlowRate;
+                    else
+                        currentisp = 0;
+
+                    // Check if we actually changed anything
+                    if (stepStartMass == stepEndMass)
+                    {
+                        MonoBehaviour.print("No change in mass");
+                        break;
+                    }
+
+                    // Check to stop rampant looping
+                    if (loopCounter == 1000)
+                    {
+                        MonoBehaviour.print("exceeded loop count");
+                        MonoBehaviour.print("stageStartMass = " + stageStartMass);
+                        MonoBehaviour.print("stepStartMass = " + stepStartMass);
+                        MonoBehaviour.print("StepEndMass   = " + stepEndMass);
+                        break;
+                    }
+
+                    // The next step starts at the mass this one ended at
+                    stepStartMass = stepEndMass;
+                }
+
+                // Store more values in the Stage object and stick it in the array
+                // Recalculate effective stage isp from the stageDeltaV (flip the standard deltaV calculation around)
+                stage.Isp = stageDeltaV / (STD_GRAVITY * Math.Log(stageStartMass / this.ShipMass));
+                stage.DeltaV = stageDeltaV;
+                // Zero stage time if more than a day (this should be moved into the window code)
+                stage.Time = (stageTime < SECONDS_PER_DAY) ? stageTime : 0d;
+                stage.Number = this.currentStage;
+                stages[this.currentStage] = stage;
+
+                // Now activate the next stage
+                this.currentStage--;
+#if LOG
+                // Log how long the stage took
+                _timer.Stop();
+                MonoBehaviour.print("Simulating stage took " + _timer.ElapsedMilliseconds + "ms");
+                stage.Dump();
+                _timer.Reset();
+                _timer.Start();
+#endif
+                // Activate the next stage
+                this.ActivateStage();
+#if LOG
+                // Log home long it took to activate
+                _timer.Stop();
+                MonoBehaviour.print("ActivateStage took " + _timer.ElapsedMilliseconds + "ms");
+#endif
+            }
+
+            // Now we add up the various total fields in the stages
+            for (int i = 0; i < stages.Length; i++)
+            {
+                // For each stage we total up the cost, mass, deltaV and time for this stage and all the stages above
+                for (int j = i; j >= 0; j--)
+                {
+                    stages[i].TotalCost += stages[j].Cost;
+                    stages[i].TotalMass += stages[j].Mass;
+                    stages[i].TotalDeltaV += stages[j].DeltaV;
+                    stages[i].TotalTime += stages[j].Time;
+                }
+                // We also total up the deltaV for stage and all stages below
+                for (int j = i; j < stages.Length; j++)
+                {
+                    stages[i].InverseTotalDeltaV += stages[j].DeltaV;
+                }
+
+                // Zero the total time if the value will be huge (24 hours?) to avoid the display going weird
+                // (this should be moved into the window code)
+                if (stages[i].TotalTime > SECONDS_PER_DAY)
+                    stages[i].TotalTime = 0d;
+            }
+
+            return stages;
+        }
+
+
+        // This function does all the hard work of working out which engines are burning, which tanks are being drained 
+        // and setting the drain rates
+        private void UpdateResourceDrains()
+        {
+            // Empty the active engines list
+            this.activeEngines.Clear();
+
+            // Reset the resource drains of all draining parts
+            foreach (PartSim partSim in this.drainingParts)
+                partSim.ResourceDrains.Reset();
+
+            // Empty the draining parts set
+            this.drainingParts.Clear();
+
+            // Loop through all the engine modules in the ship
+            foreach (EngineSim engine in this.allEngines)
+            {
+                // If the engine is active in the current stage
+                if (engine.partSim.inverseStage >= this.currentStage)
+                {
+                    // Set the resource drains for this engine and add it to the active list if it is active
+                    if (engine.SetResourceDrains(this.allParts, this.allFuelLines, this.drainingParts))
+                        this.activeEngines.Add(engine);
+                }
+            }
+#if LOG
+            StringBuilder buffer = new StringBuilder(1024);
+            buffer.AppendFormat("Active engines = {0:d}\n", activeEngines.Count);
+            int i = 0;
+            foreach (EngineSim engine in activeEngines)
+                engine.DumpEngineToBuffer(buffer, "Engine " + (i++) + ":");
+            MonoBehaviour.print(buffer);
+#endif
+        }
+
+        // This function works out if it is time to stage
+        private bool AllowedToStage()
+        {
+            //StringBuilder buffer = new StringBuilder(1024);
+            //buffer.Append("AllowedToStage\n");
+            //buffer.AppendFormat("currentStage = {0:d}\n", currentStage);
+
+            if (this.activeEngines.Count == 0)
+            {
+                //buffer.Append("No active engines => true\n");
+                //MonoBehaviour.print(buffer);
+                return true;
+            }
+
+            foreach (PartSim partSim in this.allParts)
+            {
+                //partSim.DumpPartToBuffer(buffer, "Testing: ");
+                //buffer.AppendFormat("isSepratron = {0}\n", partSim.isSepratron ? "true" : "false");
+                if (partSim.decoupledInStage == (this.currentStage - 1) && (!partSim.isSepratron || partSim.decoupledInStage < partSim.inverseStage))
+                {
+                    if (!partSim.Resources.Empty)
+                    {
+                        //buffer.Append("Decoupled part not empty => false\n");
+                        //MonoBehaviour.print(buffer);
+                        return false;
+                    }
+                    foreach (EngineSim engine in this.activeEngines)
+                    {
+                        if (engine.partSim == partSim)
                         {
-                            totalStageThrust += engine.actualThrust;
+                            //buffer.Append("Decoupled part is active engine => false\n");
+                            //MonoBehaviour.print(buffer);
+                            return false;
                         }
-                        else
-                        {
-                            totalStageThrust += engine.thrust;
-                        }
-                    }
-
-                    SetResourceDrainRates();
-
-                    double resourceDrainTime = double.MaxValue;
-                    foreach (PartSim partSim in partSims)
-                    {
-                        double time = 0d;
-                        time = partSim.TimeToDrainResource();
-                        if (time < resourceDrainTime)
-                        {
-                            resourceDrainTime = time;
-                        }
-                    }
-
-                    double startMass = ShipMass;
-                    foreach (PartSim partSim in partSims)
-                    {
-                        partSim.DrainResources(resourceDrainTime);
-                    }
-                    double endMass = ShipMass;
-                    stageTime += resourceDrainTime;
-
-                    if (resourceDrainTime > 0d && startMass > endMass && startMass > 0d && endMass > 0d)
-                    {
-                        stageDeltaV += (stage.isp * STD_GRAVITY) * Math.Log(startMass / endMass);
-                    }
-
-                    if (loopCounter == 1000)
-                    {
-                        break;
-                    }
-                }
-
-                stage.partCount = partSims.Count;
-                stage.deltaV = stageDeltaV;
-                if (stageTime < 9999)
-                {
-                    stage.time = stageTime;
-                }
-                else
-                {
-                    stage.time = 0d;
-                }
-                stages[currentStage] = stage;
-
-                currentStage--;
-                ActivateStage();
-            }
-
-            for (int i = 0; i < stages.Length; i++)
-            {
-                for (int j = i; j >= 0; j--)
-                {
-                    stages[i].totalCost += stages[j].cost;
-                    stages[i].totalMass += stages[j].mass;
-                    stages[i].totalDeltaV += stages[j].deltaV;
-                    stages[i].totalTime += stages[j].time;
-                }
-                for (int j = i; j < stages.Length; j++)
-                {
-                    stages[i].inverseTotalDeltaV += stages[j].deltaV;
-                }
-
-                if (stages[i].totalTime > 9999d)
-                {
-                    stages[i].totalTime = 0d;
-                }
-            }
-
-            return stages;
-        }
-
-        private void BuildVessel(List<Part> parts, double atmosphere)
-        {
-            partSims = new List<PartSim>();
-            Hashtable partSimLookup = new Hashtable();
-            foreach (Part part in parts)
-            {
-                PartSim partSim = new PartSim(part, atmosphere);
-
-                if (partSim.decoupledInStage < currentStage)
-                {
-                    partSim.SetResourceConsumptions();
-                    partSims.Add(partSim);
-                    partSimLookup.Add(part, partSim);
-                }
-            }
-
-            foreach (PartSim partSim in partSims)
-            {
-                partSim.SetSourceNodes(partSimLookup);
-            }
-        }
-
-        private bool AllowedToStage()
-        {
-            List<PartSim> engines = ActiveEngines;
-
-            if (engines.Count == 0)
-            {
+                    }
+                }
+            }
+
+            if (this.currentStage > 0)
+            {
+                //buffer.Append("Current stage > 0 => true\n");
+                //MonoBehaviour.print(buffer);
                 return true;
             }
 
-            foreach (PartSim partSim in partSims)
-            {
-                if (partSim.decoupledInStage == (currentStage - 1) && !partSim.part.IsSepratron())
-                {    
-
-                    if (!partSim.Resources.Empty || engines.Contains(partSim))
-                    {
-                        return false;
-                    }
-                }
-            }
-
-            if (currentStage > 0)
-            {
-                return true;
-            }
-
+            //buffer.Append("Returning false\n");
+            //MonoBehaviour.print(buffer);
             return false;
         }
 
-        private void SetResourceDrainRates()
-        {
-            foreach (PartSim partSim in partSims)
-            {
-                partSim.ResourceDrains.Reset();
-            }
-
-            List<PartSim> engines = ActiveEngines;
-
-            foreach (PartSim engine in engines)
-            {
-                engine.SetResourceDrainRates(partSims);
-            }
-        }
-
+        // This function activates the next stage
+        // currentStage must be updated before calling this function
         private void ActivateStage()
         {
-            List<PartSim> decoupledParts = new List<PartSim>();
-
-            foreach (PartSim partSim in partSims)
-            {
-                if (partSim.decoupledInStage == currentStage)
-                {
+            // Build a set of all the parts that will be decoupled
+            HashSet<PartSim> decoupledParts = new HashSet<PartSim>();
+            foreach (PartSim partSim in this.allParts)
+            {
+                if (partSim.decoupledInStage >= this.currentStage)
                     decoupledParts.Add(partSim);
-                }
             }
 
             foreach (PartSim partSim in decoupledParts)
             {
-                partSims.Remove(partSim);
-            }
-
-            foreach (PartSim partSim in partSims)
-            {
-                foreach (PartSim decoupledPart in decoupledParts)
-                {
-                    partSim.RemoveSourcePart(decoupledPart);
-                }
-            }
-        }
-
-        private List<PartSim> ActiveEngines
+                // Remove it from the all parts list
+                this.allParts.Remove(partSim);
+                if (partSim.isEngine)
+                {
+                    // If it is an engine then loop through all the engine modules and remove all the ones from this engine part
+                    for (int i = this.allEngines.Count - 1; i >= 0; i--)
+                    {
+                        if (this.allEngines[i].partSim == partSim)
+                            this.allEngines.RemoveAt(i);
+                    }
+                }
+                // If it is a fuel line then remove it from the list of all fuel lines
+                if (partSim.isFuelLine)
+                    this.allFuelLines.Remove(partSim);
+            }
+
+            // Loop through all the (remaining) parts
+            foreach (PartSim partSim in this.allParts)
+            {
+                // Ask the part to remove all the parts that are decoupled
+                partSim.RemoveAttachedParts(decoupledParts);
+            }
+        }
+
+
+        private bool StageHasSolids
         {
             get
             {
-                List<PartSim> engines = new List<PartSim>();
-                {
-                    foreach (PartSim partSim in partSims)
-                    {
-                        if (partSim.part.IsEngine() && partSim.InverseStage >= currentStage && partSim.CanDrawNeededResources(partSims))
-                        {
-                            engines.Add(partSim);
-                        }
-                    }
-                }
-
-                return engines;
-            }
-        }
-
-        private bool StageHasSolids
+                foreach (EngineSim engine in this.activeEngines)
+                {
+                    if (engine.partSim.isSolidMotor)
+                        return true;
+                }
+
+                return false;
+            }
+        }
+
+        private double ShipStartMass
         {
             get
             {
-                foreach (PartSim engine in ActiveEngines)
-                {
-                    if (engine.IsSolidMotor)
-                    {
-                        return true;
-                    }
-                }
-
-                return false;
-            }
-        }
-
-        private double ShipStartMass
+                double mass = 0d;
+
+                foreach (PartSim partSim in this.allParts)
+                {
+                    mass += partSim.GetStartMass();
+                }
+
+                return mass;
+            }
+        }
+
+        private double ShipMass
         {
             get
             {
                 double mass = 0d;
 
-                foreach (PartSim partSim in partSims)
-                {
-                    mass += partSim.GetStartMass(currentStage);
+                foreach (PartSim partSim in this.allParts)
+                {
+                    mass += partSim.GetMass();
                 }
 
                 return mass;
             }
         }
-
-        private double ShipMass
-        {
-            get
-            {
-                double mass = 0d;
-
-                foreach (PartSim partSim in partSims)
-                {
-                    mass += partSim.GetMass(currentStage);
-                }
-
-                return mass;
-            }
-        }
+#if LOG
+        public void Dump()
+        {
+            StringBuilder buffer = new StringBuilder(1024);
+            buffer.AppendFormat("Part count = {0:d}\n", allParts.Count);
+
+            // Output a nice tree view of the rocket
+            if (allParts.Count > 0)
+            {
+                PartSim root = allParts[0];
+                while (root.parent != null)
+                    root = root.parent;
+
+                root.DumpPartToBuffer(buffer, "", allParts);
+            }
+
+            MonoBehaviour.print(buffer);
+        }
+#endif
     }
 }
 

--- a/KerbalEngineer/Simulation/SimulationManager.cs
+++ b/KerbalEngineer/Simulation/SimulationManager.cs
@@ -1,155 +1,151 @@
-// Name:    Kerbal Engineer Redux
-// Author:  CYBUTEK
-// License: Attribution-NonCommercial-ShareAlike 3.0 Unported
-
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Threading;
-using System.Linq;
-
-namespace KerbalEngineer.Simulation
-{
-    public class SimulationManager
-    {
-        #region Instance
-
-        private static SimulationManager _instance;
-        /// <summary>
-        /// Gets the current instance of the simulation manager.
-        /// </summary>
-        public static SimulationManager Instance
-        {
-            get
-            {
-                if (_instance == null) _instance = new SimulationManager();
-                return _instance;
-            }
-        }
-
-        #endregion
-
-        #region Fields
-
-        private bool _simRequested = false;
-        private bool _simRunning = false;
-        private Stopwatch _timer = new Stopwatch();
-        private long _millisecondsBetweenSimulations = 0;
-        private int _numberOfStages = 0;
-
-        #endregion
-
-        #region Properties
-
-        public Stage[] Stages { get; private set; }
-        public Stage LastStage { get; private set; }
-
-        public double Gravity { get; set; }
-        public double Atmosphere { get; set; }
-
-        private bool _stagingChanged = false;
-        public bool StagingChanged
-        {
-            get
-            {
-                if (_stagingChanged)
-                {
-                    _stagingChanged = false;
-                    return true;
-                }
-                return false;
-            }
-        }
-
-        #endregion
-
-        #region Initialisation
-
-        public SimulationManager()
-        {
-            Stages = new Stage[0];
-            LastStage = new Stage();
-
-            Gravity = 9.81d;
-            Atmosphere = 0d;
-        }
-
-        #endregion
-
-        #region Updating
-
-        public void RequestSimulation()
-        {
-            _simRequested = true;
-            if (!_timer.IsRunning) _timer.Start();
-        }
-
-        public void TryStartSimulation()
-        {
-            if ((HighLogic.LoadedSceneIsEditor || FlightGlobals.ActiveVessel != null) && !_simRunning)
-            {
-                if (_timer.ElapsedMilliseconds > _millisecondsBetweenSimulations)
-                {
-                    if (_simRequested)
-                    {
-                        _simRequested = false;
-                        _timer.Reset();
-
-                        StartSimulation();
-                    }
-                }
-            }
-        }
-
-        #endregion
-
-        #region Processing
-
-        private void StartSimulation()
-        {
-            _simRunning = true;
-            _timer.Start();
-
-            List<Part> parts = HighLogic.LoadedSceneIsEditor ? EditorLogic.fetch.ship.Parts : FlightGlobals.ActiveVessel.Parts;
-
-            if (parts.Count > 0)
-            {
-                ThreadPool.QueueUserWorkItem(RunSimulation, new Simulation(parts));
-                //RunSimulation(new Simulation(parts));
-            }
-            else
-            {
-                this.Stages = new Stage[0];
-                this.LastStage = new Stage();
-            }
-        }
-
-        private void RunSimulation(object simObject)
-        {
-            try
-            {
-                int stagesWithDeltaV = this.Stages.Where(s => s.deltaV > 0d).Count();
-                this.Stages = (simObject as Simulation).RunSimulation(this.Gravity, this.Atmosphere);
-
-                this.LastStage = this.Stages.Last();
-                if (this.Stages.Length != _numberOfStages || this.Stages.Where(s => s.deltaV > 0d).Count() != stagesWithDeltaV)
-                {
-                    _numberOfStages = this.Stages.Length;
-                    _stagingChanged = true;
-                }
-            }
-            catch { /* Something went wrong! */ }
-
-            _timer.Stop();
-            _millisecondsBetweenSimulations = 10 * _timer.ElapsedMilliseconds;
-
-            _timer.Reset();
-            _timer.Start();
-
-            _simRunning = false;
-        }
-
-        #endregion
-    }
+// Project:	KerbalEngineer

+// Author:	CYBUTEK

+// License:	Attribution-NonCommercial-ShareAlike 3.0 Unported

+

+#region Using Directives

+

+using System;

+using System.Collections.Generic;

+using System.Diagnostics;

+using System.Linq;

+using System.Threading;

+

+using UnityEngine;

+

+#endregion

+

+namespace KerbalEngineer.Simulation

+{

+    public class SimulationManager

+    {

+        private static bool bRequested;

+        private static bool bRunning;

+        private static readonly Stopwatch timer = new Stopwatch();

+        private static long delayBetweenSims;

+        private static bool stagingChanged;

+        private static int numberOfStages;

+

+        private static Stopwatch _func = new Stopwatch();

+

+        public static Stage[] Stages { get; private set; }

+        public static Stage LastStage { get; private set; }

+        public static String failMessage { get; private set; }

+

+        public static double Gravity { get; set; }

+        public static double Atmosphere { get; set; }

+

+        public static bool StagingChanged

+        {

+            get

+            {

+                if (stagingChanged)

+                {

+                    stagingChanged = false;

+                    return true;

+                }

+                return false;

+            }

+        }

+

+        public static void RequestSimulation()

+        {

+            bRequested = true;

+            if (!timer.IsRunning)

+            {

+                timer.Start();

+            }

+        }

+

+        public static void TryStartSimulation()

+        {

+            if (bRequested && !bRunning && (HighLogic.LoadedSceneIsEditor || FlightGlobals.ActiveVessel != null) && timer.ElapsedMilliseconds > delayBetweenSims)

+            {

+                bRequested = false;

+                timer.Reset();

+                StartSimulation();

+            }

+        }

+

+        public static bool ResultsReady()

+        {

+            return !bRunning;

+        }

+

+        private static void ClearResults()

+        {

+            failMessage = "";

+            Stages = null;

+            LastStage = null;

+        }

+

+        private static void StartSimulation()

+        {

+            try

+            {

+                bRunning = true;

+                ClearResults();

+                timer.Start();

+

+                List<Part> parts = HighLogic.LoadedSceneIsEditor ? EditorLogic.SortedShipList : FlightGlobals.ActiveVessel.Parts;

+

+                // Create the Simulation object in this thread

+                Simulation sim = new Simulation();

+

+                // This call doesn't ever fail at the moment but we'll check and return a sensible error for display

+                if (sim.PrepareSimulation(parts, Gravity, Atmosphere))

+                {

+                    ThreadPool.QueueUserWorkItem(RunSimulation, sim);

+                }

+                else

+                {

+                    failMessage = "PrepareSimulation failed";

+                    bRunning = false;

+                }

+            }

+            catch (Exception e)

+            {

+                MonoBehaviour.print("Exception in StartSimulation: " + e);

+                failMessage = e.ToString();

+                bRunning = false;

+            }

+        }

+

+        private static void RunSimulation(object simObject)

+        {

+            try

+            {

+                Stages = (simObject as Simulation).RunSimulation();

+                if (Stages != null)

+                {

+#if LOG

+                    foreach (Stage stage in Stages)

+                        stage.Dump();

+#endif

+                    LastStage = Stages.Last();

+

+                    if (numberOfStages != Stages.Length)

+                    {

+                        numberOfStages = Stages.Length;

+                        stagingChanged = true;

+                    }

+                }

+            }

+            catch (Exception e)

+            {

+                MonoBehaviour.print("Exception in RunSimulation: " + e);

+                Stages = null;

+                LastStage = null;

+                failMessage = e.ToString();

+            }

+

+            timer.Stop();

+            MonoBehaviour.print("RunSimulation took " + timer.ElapsedMilliseconds + "ms");

+            delayBetweenSims = 10 * timer.ElapsedMilliseconds;

+

+            timer.Reset();

+            timer.Start();

+

+            bRunning = false;

+        }

+    }

 }
-

--- a/KerbalEngineer/Simulation/Stage.cs
+++ b/KerbalEngineer/Simulation/Stage.cs
@@ -1,134 +1,49 @@
-// Project:	KerbalEngineer

-// Author:	CYBUTEK

-// License:	Attribution-NonCommercial-ShareAlike 3.0 Unported

+// Kerbal Engineer Redux
+// Author:  CYBUTEK
+// License: Attribution-NonCommercial-ShareAlike 3.0 Unported

 

-#region Using Directives

-

-using KerbalEngineer.Extensions;

-

-#endregion

-

-namespace KerbalEngineer.Simulation

-{

-    public class Stage

-    {

-        #region Fields

-

-        public double actualThrust = 0;

-        public double actualThrustToWeight = 0;

-        public int cost = 0;

-        public double deltaV = 0;

-        public double inverseTotalDeltaV = 0;

-        public double isp = 0;

-        public double mass = 0;

-        public int number = 0;

-        public int partCount = 0;

-        public double thrust = 0;

-        public double thrustToWeight = 0;

-        public double time = 0;

-        public int totalCost = 0;

-        public double totalDeltaV = 0;

-        public double totalMass = 0;

-        public double totalTime = 0;

-

-        #endregion

-

-        #region Properties

-

-        public string Number

-        {

-            get { return "S" + this.number; }

-        }

-

-        public string Parts

-        {

-            get { return this.partCount.ToString(); }

-        }

-

-        public string Cost

-        {

-            get { return this.cost + " / " + this.totalCost; }

-        }

-

-        public string Mass

-        {

-            get

-            {

-                if (HighLogic.LoadedSceneIsFlight)

-                {

-                    return this.totalMass.ToMass();

-                }

-                return this.mass.ToMass(false) + " / " + this.totalMass.ToMass();

-            }

-        }

-

-        public string Isp

-        {

-            get { return this.isp.ToString("#,0.00") + "s"; }

-        }

-

-        public string Thrust

-        {

-            get { return this.thrust.ToForce(); }

-        }

-

-        public string ActualThrust

-        {

-            get { return this.actualThrust.ToForce(); }

-        }

-

-        public string TWR

-        {

-            get

-            {

-                if (HighLogic.LoadedSceneIsFlight)

-                {

-                    return this.actualThrustToWeight.ToString("0.00") + " / " + this.thrustToWeight.ToString("0.00");

-                }

-                return this.thrustToWeight.ToString("0.00");

-            }

-        }

-

-        public string DeltaV

-        {

-            get

-            {

-                if (HighLogic.LoadedSceneIsFlight)

-                {

-                    return this.deltaV.ToSpeed();

-                }

-                return this.deltaV.ToString("#,0.") + " / " + this.inverseTotalDeltaV.ToString("#,0.") + "m/s";

-            }

-        }

-

-        public string TotalDeltaV

-        {

-            get

-            {

-                if (HighLogic.LoadedSceneIsFlight)

-                {

-                    return this.totalDeltaV.ToSpeed();

-                }

-                return this.inverseTotalDeltaV.ToString("#,0.") + "m/s";

-            }

-        }

-

-        public string Time

-        {

-            get { return this.time.ToTime(); }

-        }

-

-        #endregion

-

-        #region Initialisation

-

-        public Stage() { }

-

-        public Stage(int stageNumber)

-        {

-            this.number = stageNumber;

-        }

-

-        #endregion

-    }

+namespace KerbalEngineer.Simulation
+{
+    public class Stage
+    {
+        public int Number = 0;
+        public int Cost = 0;
+        public int TotalCost = 0;
+        public double Time = 0f;
+        public double TotalTime = 0f;
+        public double Mass = 0f;
+        public double TotalMass = 0f;
+        public double Isp = 0f;
+        public double Thrust = 0f;
+        public double ActualThrust = 0f;
+        public double ThrustToWeight = 0f;
+        public double ActualThrustToWeight = 0f;
+        public double DeltaV = 0f;
+        public double TotalDeltaV = 0f;
+        public double InverseTotalDeltaV = 0f;
+#if LOG
+        public void Dump()
+        {
+            StringBuilder str = new StringBuilder("", 512);
+            str.AppendFormat("number        : {0:d}\n", number);
+            str.AppendFormat("cost          : {0:d}\n", cost);
+            str.AppendFormat("totalCost     : {0:d}\n", totalCost);
+            str.AppendFormat("time          : {0:g6}\n", time);
+            str.AppendFormat("totalTime     : {0:g6}\n", totalTime);
+            str.AppendFormat("mass          : {0:g6}\n", mass);
+            str.AppendFormat("totalMass     : {0:g6}\n", totalMass);
+            str.AppendFormat("isp           : {0:g6}\n", isp);
+            str.AppendFormat("thrust        : {0:g6}\n", thrust);
+            str.AppendFormat("actualThrust  : {0:g6}\n", actualThrust);
+            str.AppendFormat("thrustToWeight: {0:g6}\n", thrustToWeight);
+            str.AppendFormat("actualTWR     : {0:g6}\n", actualThrustToWeight);
+            str.AppendFormat("deltaV        : {0:g6}\n", deltaV);
+            str.AppendFormat("totalDeltaV   : {0:g6}\n", totalDeltaV);
+            str.AppendFormat("invTotDeltaV  : {0:g6}\n", inverseTotalDeltaV);
+            
+            MonoBehaviour.print(str);
+        }
+#endif
+    }
 }
+

 Binary files a/Output/KerbalEngineer/KerbalEngineer.dll and b/Output/KerbalEngineer/KerbalEngineer.dll differ