Account for module mass in the editor as KSP 1.0.1 now makes use of it in flight.
Account for module mass in the editor as KSP 1.0.1 now makes use of it in flight.

--- a/KerbalEngineer/VesselSimulator/PartSim.cs
+++ b/KerbalEngineer/VesselSimulator/PartSim.cs
@@ -19,26 +19,22 @@
 
 #region Using Directives
 
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-
-using KerbalEngineer.Extensions;
-
-using UnityEngine;
-
 #endregion
 
 namespace KerbalEngineer.VesselSimulator
 {
+    using System;
+    using System.Collections.Generic;
+    using System.Linq;
+    using System.Text;
     using CompoundParts;
+    using Extensions;
+    using UnityEngine;
 
     public class PartSim : Pool<PartSim>
     {
-        private readonly List<AttachNodeSim> attachNodes = new List<AttachNodeSim>();
+        public double baseMass;
         public Vector3d centerOfMass;
-        public double baseMass;
         public double cost;
         public int decoupledInStage;
         public bool fuelCrossFeed;
@@ -70,6 +66,636 @@
         public double startMass = 0d;
         public String vesselName;
         public VesselType vesselType;
+        private readonly List<AttachNodeSim> attachNodes = new List<AttachNodeSim>();
+
+        public ResourceContainer ResourceDrains
+        {
+            get
+            {
+                return resourceDrains;
+            }
+        }
+
+        public ResourceContainer Resources
+        {
+            get
+            {
+                return resources;
+            }
+        }
+
+        public void CreateEngineSims(List<EngineSim> allEngines, double atmosphere, double mach, bool vectoredThrust, bool fullThrust, LogMsg log)
+        {
+            bool correctThrust = SimManager.DoesEngineUseCorrectedThrust(part);
+            if (log != null)
+            {
+                log.buf.AppendLine("CreateEngineSims for " + name);
+
+                foreach (PartModule partMod in part.Modules)
+                {
+                    log.buf.AppendLine("Module: " + partMod.moduleName);
+                }
+
+                log.buf.AppendLine("correctThrust = " + correctThrust);
+            }
+
+            if (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 = part.GetModule<MultiModeEngine>().mode;
+
+                List<ModuleEnginesFX> engines = part.GetModules<ModuleEnginesFX>();
+
+                for (int i = 0; i < engines.Count; ++i)
+                {
+                    ModuleEnginesFX engine = engines[i];
+
+                    if (engine.engineID == mode)
+                    {
+                        if (log != null)
+                        {
+                            log.buf.AppendLine("Module: " + engine.moduleName);
+                        }
+
+                        Vector3 thrustvec = CalculateThrustVector(vectoredThrust ? engine.thrustTransforms : null, log);
+
+                        EngineSim engineSim = EngineSim.GetPoolObject().Initialise(this,
+                            atmosphere,
+                            (float)mach,
+                            engine.maxFuelFlow,
+                            engine.minFuelFlow,
+                            engine.thrustPercentage,
+                            thrustvec,
+                            engine.atmosphereCurve,
+                            engine.atmChangeFlow,
+                            engine.useAtmCurve ? engine.atmCurve : null,
+                            engine.useVelCurve ? engine.velCurve : null,
+                            engine.currentThrottle,
+                            engine.throttleLocked || fullThrust,
+                            engine.propellants,
+                            engine.isOperational,
+                            engine.resultingThrust,
+                            engine.thrustTransforms);
+                        allEngines.Add(engineSim);
+                    }
+                }
+            }
+            else
+            {
+                if (hasModuleEngines)
+                {
+                    List<ModuleEngines> engines = part.GetModules<ModuleEngines>();
+                    for (int i = 0; i < engines.Count; ++i)
+                    {
+                        ModuleEngines engine = engines[i];
+                        if (log != null)
+                        {
+                            log.buf.AppendLine("Module: " + engine.moduleName);
+                        }
+
+                        Vector3 thrustvec = CalculateThrustVector(vectoredThrust ? engine.thrustTransforms : null, log);
+
+                        EngineSim engineSim = EngineSim.GetPoolObject().Initialise(this,
+                            atmosphere,
+                            (float)mach,
+                            engine.maxFuelFlow,
+                            engine.minFuelFlow,
+                            engine.thrustPercentage,
+                            thrustvec,
+                            engine.atmosphereCurve,
+                            engine.atmChangeFlow,
+                            engine.useAtmCurve ? engine.atmCurve : null,
+                            engine.useVelCurve ? engine.velCurve : null,
+                            engine.currentThrottle,
+                            engine.throttleLocked || fullThrust,
+                            engine.propellants,
+                            engine.isOperational,
+                            engine.resultingThrust,
+                            engine.thrustTransforms);
+                        allEngines.Add(engineSim);
+                    }
+                }
+            }
+
+            if (log != null)
+            {
+                log.Flush();
+            }
+        }
+
+        public int DecouplerCount()
+        {
+            int count = 0;
+            PartSim partSim = this;
+            while (partSim != null)
+            {
+                if (partSim.isDecoupler)
+                {
+                    count++;
+                }
+
+                partSim = partSim.parent;
+            }
+            return count;
+        }
+
+        public void DrainResources(double time)
+        {
+            //MonoBehaviour.print("DrainResources(" + name + ":" + partId + ", " + time + ")");
+            for (int i = 0; i < resourceDrains.Types.Count; ++i)
+            {
+                int type = resourceDrains.Types[i];
+
+                //MonoBehaviour.print("draining " + (time * resourceDrains[type]) + " " + ResourceContainer.GetResourceName(type));
+                resources.Add(type, -time * resourceDrains[type]);
+                //MonoBehaviour.print(ResourceContainer.GetResourceName(type) + " left = " + resources[type]);
+            }
+        }
+
+        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(", vesselName = '{0}'", vesselName);
+            buffer.AppendFormat(", vesselType = {0}", SimManager.GetVesselTypeString(vesselType));
+            buffer.AppendFormat(", initialVesselName = '{0}'", initialVesselName);
+
+            buffer.AppendFormat(", fuelCF = {0}", fuelCrossFeed);
+            buffer.AppendFormat(", noCFNKey = '{0}'", noCrossFeedNodeKey);
+
+            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);
+                    }
+                }
+            }
+        }
+
+        public bool EmptyOf(HashSet<int> types)
+        {
+            foreach (int type in types)
+            {
+                if (resources.HasType(type) && resourceFlowStates[type] != 0 && resources[type] > SimManager.RESOURCE_MIN)
+                {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+
+        public double GetMass()
+        {
+            double mass = baseMass;
+
+            for (int i = 0; i < resources.Types.Count; ++i)
+            {
+                mass += resources.GetResourceMass(resources.Types[i]);
+            }
+
+            return mass;
+        }
+
+        public HashSet<PartSim> GetSourceSet(int type, List<PartSim> allParts, HashSet<PartSim> visited, LogMsg log, String indent)
+        {
+            if (log != null)
+            {
+                log.buf.AppendLine(indent + "GetSourceSet(" + ResourceContainer.GetResourceName(type) + ") for " + name + ":" + partId);
+                indent += "  ";
+            }
+
+            HashSet<PartSim> allSources = new HashSet<PartSim>();
+            HashSet<PartSim> partSources = null;
+
+            // 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))
+            {
+                if (log != null)
+                {
+                    log.buf.AppendLine(indent + "Returning empty set, already visited (" + name + ":" + partId + ")");
+                }
+
+                return allSources;
+            }
+
+            //if (log != null)
+            //    log.buf.AppendLine(indent + "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");
+            for (int i = 0; i < fuelTargets.Count; ++i)
+            {
+                PartSim partSim = fuelTargets[i];
+
+                if (visited.Contains(partSim))
+                {
+                    //if (log != null)
+                    //    log.buf.AppendLine(indent + "Fuel target already visited, skipping (" + partSim.name + ":" + partSim.partId + ")");
+                }
+                else
+                {
+                    //if (log != null)
+                    //    log.buf.AppendLine(indent + "Adding fuel target as source (" + partSim.name + ":" + partSim.partId + ")");
+
+                    partSources = partSim.GetSourceSet(type, allParts, visited, log, indent);
+                    if (partSources.Count > 0)
+                    {
+                        allSources.UnionWith(partSources);
+                        partSources.Clear();
+                    }
+                }
+            }
+
+            if (allSources.Count > 0)
+            {
+                if (log != null)
+                {
+                    log.buf.AppendLine(indent + "Returning " + allSources.Count + " fuel target sources (" + name + ":" + partId + ")");
+                }
+
+                return allSources;
+            }
+
+            // Rule 3: This rule has been removed and merged with rules 4 and 7 to fix issue with fuel tanks with disabled crossfeed
+
+            // 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]
+            if (fuelCrossFeed)
+            {
+                //MonoBehaviour.print("foreach attach node");
+                for (int i = 0; i < attachNodes.Count; ++i)
+                {
+                    AttachNodeSim attachSim = attachNodes[i];
+
+                    if (attachSim.attachedPartSim != null)
+                    {
+                        if (attachSim.nodeType == AttachNode.NodeType.Stack)
+                        {
+                            if (!(noCrossFeedNodeKey != null && noCrossFeedNodeKey.Length > 0 && attachSim.id.Contains(noCrossFeedNodeKey)))
+                            {
+                                if (visited.Contains(attachSim.attachedPartSim))
+                                {
+                                    //if (log != null)
+                                    //    log.buf.AppendLine(indent + "Attached part already visited, skipping (" + attachSim.attachedPartSim.name + ":" + attachSim.attachedPartSim.partId + ")");
+                                }
+                                else
+                                {
+                                    //if (log != null)
+                                    //    log.buf.AppendLine(indent + "Adding attached part as source (" + attachSim.attachedPartSim.name + ":" + attachSim.attachedPartSim.partId + ")");
+
+                                    partSources = attachSim.attachedPartSim.GetSourceSet(type, allParts, visited, log, indent);
+                                    if (partSources.Count > 0)
+                                    {
+                                        allSources.UnionWith(partSources);
+                                        partSources.Clear();
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+
+                if (allSources.Count > 0)
+                {
+                    if (log != null)
+                    {
+                        log.buf.AppendLine(indent + "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]
+            if (resources.HasType(type) && resourceFlowStates[type] != 0)
+            {
+                if (resources[type] > SimManager.RESOURCE_MIN)
+                {
+                    allSources.Add(this);
+
+                    if (log != null)
+                    {
+                        log.buf.AppendLine(indent + "Returning enabled tank 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 (parent != null && parentAttach == AttachModes.SRF_ATTACH)
+            {
+                if (fuelCrossFeed)
+                {
+                    if (visited.Contains(parent))
+                    {
+                        //if (log != null)
+                        //    log.buf.AppendLine(indent + "Parent part already visited, skipping (" + parent.name + ":" + parent.partId + ")");
+                    }
+                    else
+                    {
+                        allSources = parent.GetSourceSet(type, allParts, visited, log, indent);
+                        if (allSources.Count > 0)
+                        {
+                            if (log != null)
+                            {
+                                log.buf.AppendLine(indent + "Returning " + allSources.Count + " parent sources (" + name + ":" + partId + ")");
+                            }
+
+                            return allSources;
+                        }
+                    }
+                }
+            }
+
+            // Rule 8: If all preceding rules failed, part returns empty list.
+            //if (log != null)
+            //    log.buf.AppendLine(indent + "Returning empty set, no sources found (" + name + ":" + partId + ")");
+
+            return allSources;
+        }
+
+        public double GetStartMass()
+        {
+            return startMass;
+        }
+
+        public PartSim Initialise(Part thePart, int id, double atmosphere, LogMsg log)
+        {
+            Reset(this);
+
+            part = thePart;
+            centerOfMass = thePart.transform.TransformPoint(thePart.CoMOffset);
+            partId = id;
+            name = part.partInfo.name;
+
+            if (log != null)
+            {
+                log.buf.AppendLine("Create PartSim for " + name);
+            }
+
+            parent = null;
+            parentAttach = part.attachMode;
+            fuelCrossFeed = part.fuelCrossFeed;
+            noCrossFeedNodeKey = part.NoCrossFeedNodeKey;
+            decoupledInStage = DecoupledInStage(part);
+            isFuelLine = part.HasModule<CModuleFuelLine>();
+            isFuelTank = part is FuelTank;
+            isSepratron = IsSepratron();
+            inverseStage = part.inverseStage;
+            //MonoBehaviour.print("inverseStage = " + inverseStage);
+
+            cost = part.GetCostWet();
+
+            // Work out if the part should have no physical significance
+            isNoPhysics = part.HasModule<LaunchClamp>();
+
+            if (isNoPhysics == false)
+            {
+                if (part.vessel != null)
+                {
+                    baseMass = part.mass;
+                }
+                else
+                {
+                    baseMass = part.mass + part.GetModuleMass(part.mass);
+                }
+            }
+
+            if (SimManager.logOutput)
+            {
+                MonoBehaviour.print((isNoPhysics ? "Ignoring" : "Using") + " part.mass of " + part.mass);
+            }
+
+            for (int i = 0; i < part.Resources.Count; ++i)
+            {
+                PartResource resource = part.Resources[i];
+
+                // 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))
+                {
+                    if (SimManager.logOutput)
+                    {
+                        MonoBehaviour.print(resource.resourceName + " = " + resource.amount);
+                    }
+
+                    resources.Add(resource.info.id, resource.amount);
+                    resourceFlowStates.Add(resource.info.id, resource.flowState ? 1 : 0);
+                }
+                else
+                {
+                    MonoBehaviour.print(resource.resourceName + " is NaN. Skipping.");
+                }
+            }
+
+            startMass = GetMass();
+
+            hasVessel = (part.vessel != null);
+            isLanded = hasVessel && part.vessel.Landed;
+            if (hasVessel)
+            {
+                vesselName = part.vessel.vesselName;
+                vesselType = part.vesselType;
+            }
+            initialVesselName = part.initialVesselName;
+
+            hasMultiModeEngine = part.HasModule<MultiModeEngine>();
+            hasModuleEnginesFX = part.HasModule<ModuleEnginesFX>();
+            hasModuleEngines = part.HasModule<ModuleEngines>();
+
+            isEngine = hasMultiModeEngine || hasModuleEnginesFX || hasModuleEngines;
+
+            if (SimManager.logOutput)
+            {
+                MonoBehaviour.print("Created " + name + ". Decoupled in stage " + decoupledInStage);
+            }
+
+            return this;
+        }
+
+        public void ReleasePart()
+        {
+            part = null;
+        }
+
+        public void RemoveAttachedParts(HashSet<PartSim> partSims)
+        {
+            // Loop through the attached parts
+            for (int i = 0; i < attachNodes.Count; ++i)
+            {
+                AttachNodeSim attachSim = attachNodes[i];
+
+                // 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 SetupAttachNodes(Dictionary<Part, PartSim> partSimLookup, LogMsg log)
+        {
+            if (log != null)
+            {
+                log.buf.AppendLine("SetupAttachNodes for " + name + ":" + partId + "");
+            }
+
+            attachNodes.Clear();
+
+            for (int i = 0; i < part.attachNodes.Count; ++i)
+            {
+                AttachNode attachNode = part.attachNodes[i];
+
+                if (log != null)
+                {
+                    log.buf.AppendLine("AttachNode " + attachNode.id + " = " + (attachNode.attachedPart != null ? attachNode.attachedPart.partInfo.name : "null"));
+                }
+
+                if (attachNode.attachedPart != null && attachNode.id != "Strut")
+                {
+                    PartSim attachedSim;
+                    if (partSimLookup.TryGetValue(attachNode.attachedPart, out attachedSim))
+                    {
+                        if (log != null)
+                        {
+                            log.buf.AppendLine("Adding attached node " + attachedSim.name + ":" + attachedSim.partId + "");
+                        }
+
+                        attachNodes.Add(AttachNodeSim.GetPoolObject().Initialise(attachedSim, attachNode.id, attachNode.nodeType));
+                    }
+                    else
+                    {
+                        if (log != null)
+                        {
+                            log.buf.AppendLine("No PartSim for attached part (" + attachNode.attachedPart.partInfo.name + ")");
+                        }
+                    }
+                }
+            }
+
+            for (int i = 0; i < part.fuelLookupTargets.Count; ++i)
+            {
+                Part p = part.fuelLookupTargets[i];
+
+                if (p != null)
+                {
+                    PartSim targetSim;
+                    if (partSimLookup.TryGetValue(p, out targetSim))
+                    {
+                        if (log != null)
+                        {
+                            log.buf.AppendLine("Fuel target: " + targetSim.name + ":" + targetSim.partId);
+                        }
+
+                        fuelTargets.Add(targetSim);
+                    }
+                    else
+                    {
+                        if (log != null)
+                        {
+                            log.buf.AppendLine("No PartSim for fuel target (" + p.name + ")");
+                        }
+                    }
+                }
+            }
+        }
+
+        public void SetupParent(Dictionary<Part, PartSim> partSimLookup, LogMsg log)
+        {
+            if (part.parent != null)
+            {
+                parent = null;
+                if (partSimLookup.TryGetValue(part.parent, out parent))
+                {
+                    if (log != null)
+                    {
+                        log.buf.AppendLine("Parent part is " + parent.name + ":" + parent.partId);
+                    }
+                }
+                else
+                {
+                    if (log != null)
+                    {
+                        log.buf.AppendLine("No PartSim for parent part (" + part.parent.partInfo.name + ")");
+                    }
+                }
+            }
+        }
+
+        public double TimeToDrainResource()
+        {
+            //MonoBehaviour.print("TimeToDrainResource(" + name + ":" + partId + ")");
+            double time = double.MaxValue;
+
+            for (int i = 0; i < resourceDrains.Types.Count; ++i)
+            {
+                int type = resourceDrains.Types[i];
+
+                if (resourceDrains[type] > 0)
+                {
+                    time = Math.Min(time, resources[type] / resourceDrains[type]);
+                    //MonoBehaviour.print("type = " + ResourceContainer.GetResourceName(type) + "  amount = " + resources[type] + "  rate = " + resourceDrains[type] + "  time = " + time);
+                }
+            }
+
+            //if (time < double.MaxValue)
+            //    MonoBehaviour.print("TimeToDrainResource(" + name + ":" + partId + ") = " + time);
+            return time;
+        }
 
         private static void Reset(PartSim partSim)
         {
@@ -108,203 +734,6 @@
             partSim.vesselType = VesselType.Base;
         }
 
-        public PartSim Initialise(Part thePart, int id, double atmosphere, LogMsg log)
-        {
-            Reset(this);
-
-            this.part = thePart;
-            this.centerOfMass = thePart.transform.TransformPoint(thePart.CoMOffset);
-            this.partId = id;
-            this.name = this.part.partInfo.name;
-
-            if (log != null)
-            {
-                log.buf.AppendLine("Create PartSim for " + this.name);
-            }
-
-            this.parent = null;
-            this.parentAttach = part.attachMode;
-            this.fuelCrossFeed = this.part.fuelCrossFeed;
-            this.noCrossFeedNodeKey = this.part.NoCrossFeedNodeKey;
-            this.decoupledInStage = this.DecoupledInStage(this.part);
-            this.isFuelLine = this.part.HasModule<CModuleFuelLine>();
-            this.isFuelTank = this.part is FuelTank;
-            this.isSepratron = this.IsSepratron();
-            this.inverseStage = this.part.inverseStage;
-            //MonoBehaviour.print("inverseStage = " + inverseStage);
-
-            this.cost = this.part.GetCostWet();
-
-            // Work out if the part should have no physical significance
-            this.isNoPhysics = this.part.HasModule<LaunchClamp>();
-
-            if (isNoPhysics == false)
-            {
-                baseMass = part.mass;
-            }
-
-            if (SimManager.logOutput)
-            {
-                MonoBehaviour.print((this.isNoPhysics ? "Ignoring" : "Using") + " part.mass of " + this.part.mass);
-            }
-
-            for (int i = 0; i < part.Resources.Count; ++i)
-            {
-                PartResource resource = part.Resources[i];
-
-                // 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))
-                {
-                    if (SimManager.logOutput)
-                    {
-                        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;
-            if (this.hasVessel)
-            {
-                this.vesselName = this.part.vessel.vesselName;
-                this.vesselType = this.part.vesselType;
-            }
-            this.initialVesselName = this.part.initialVesselName;
-
-            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;
-
-            if (SimManager.logOutput)
-            {
-                MonoBehaviour.print("Created " + this.name + ". Decoupled in stage " + this.decoupledInStage);
-            }
-
-            return this;
-        }
-
-        public ResourceContainer Resources
-        {
-            get { return this.resources; }
-        }
-
-        public ResourceContainer ResourceDrains
-        {
-            get { return this.resourceDrains; }
-        }
-
-        public void CreateEngineSims(List<EngineSim> allEngines, double atmosphere, double mach, bool vectoredThrust, bool fullThrust, LogMsg log)
-        {
-            bool correctThrust = SimManager.DoesEngineUseCorrectedThrust(this.part);
-            if (log != null)
-            {
-                log.buf.AppendLine("CreateEngineSims for " + this.name);
-
-                foreach (PartModule partMod in this.part.Modules)
-                {
-                    log.buf.AppendLine("Module: " + partMod.moduleName);
-                }
-
-                log.buf.AppendLine("correctThrust = " + correctThrust);
-            }
-
-            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;
-
-                List<ModuleEnginesFX> engines = part.GetModules<ModuleEnginesFX>();
-
-                for (int i = 0; i < engines.Count; ++i)
-                {
-                    ModuleEnginesFX engine = engines[i];
-
-                    if (engine.engineID == mode)
-                    {
-                        if (log != null)
-                        {
-                            log.buf.AppendLine("Module: " + engine.moduleName);
-                        }
-
-                        Vector3 thrustvec = this.CalculateThrustVector(vectoredThrust ? engine.thrustTransforms : null, log);
-
-                        EngineSim engineSim = EngineSim.GetPoolObject().Initialise(this,
-                            atmosphere,
-                            (float)mach,
-                            engine.maxFuelFlow,
-                            engine.minFuelFlow,
-                            engine.thrustPercentage,
-                            thrustvec,
-                            engine.atmosphereCurve,
-                            engine.atmChangeFlow,
-                            engine.useAtmCurve ? engine.atmCurve : null,
-                            engine.useVelCurve ? engine.velCurve : null,
-                            engine.currentThrottle,
-                            engine.throttleLocked || fullThrust,
-                            engine.propellants,
-                            engine.isOperational,
-                            engine.resultingThrust,
-                            engine.thrustTransforms);
-                        allEngines.Add(engineSim);
-                    }
-                }
-            }
-            else
-            {
-                if (this.hasModuleEngines)
-                {
-                    List<ModuleEngines> engines = part.GetModules<ModuleEngines>();
-                    for (int i = 0; i < engines.Count; ++i)
-                    {
-                        ModuleEngines engine = engines[i];
-                        if (log != null)
-                        {
-                            log.buf.AppendLine("Module: " + engine.moduleName);
-                        }
-
-                        Vector3 thrustvec = this.CalculateThrustVector(vectoredThrust ? engine.thrustTransforms : null, log);
-
-                        EngineSim engineSim = EngineSim.GetPoolObject().Initialise(this,
-                            atmosphere,
-                            (float)mach,
-                            engine.maxFuelFlow,
-                            engine.minFuelFlow,
-                            engine.thrustPercentage,
-                            thrustvec,
-                            engine.atmosphereCurve,
-                            engine.atmChangeFlow,
-                            engine.useAtmCurve ? engine.atmCurve : null,
-                            engine.useVelCurve ? engine.velCurve : null,
-                            engine.currentThrottle,
-                            engine.throttleLocked || fullThrust,
-                            engine.propellants,
-                            engine.isOperational,
-                            engine.resultingThrust,
-                            engine.thrustTransforms);
-                        allEngines.Add(engineSim);
-                    }
-                }
-            }
-
-            if (log != null)
-            {
-                log.Flush();
-            }
-        }
-
         private Vector3 CalculateThrustVector(List<Transform> thrustTransforms, LogMsg log)
         {
             if (thrustTransforms == null)
@@ -340,98 +769,9 @@
             return thrustvec;
         }
 
-        public void SetupParent(Dictionary<Part, PartSim> partSimLookup, LogMsg log)
-        {
-            if (this.part.parent != null)
-            {
-                this.parent = null;
-                if (partSimLookup.TryGetValue(this.part.parent, out this.parent))
-                {
-                    if (log != null)
-                    {
-                        log.buf.AppendLine("Parent part is " + this.parent.name + ":" + this.parent.partId);
-                    }
-                }
-                else
-                {
-                    if (log != null)
-                    {
-                        log.buf.AppendLine("No PartSim for parent part (" + this.part.parent.partInfo.name + ")");
-                    }
-                }
-            }
-        }
-
-        public void SetupAttachNodes(Dictionary<Part, PartSim> partSimLookup, LogMsg log)
-        {
-            if (log != null)
-            {
-                log.buf.AppendLine("SetupAttachNodes for " + this.name + ":" + this.partId + "");
-            }
-
-            attachNodes.Clear();
-
-            for (int i = 0; i < part.attachNodes.Count; ++i)
-            {
-                AttachNode attachNode = part.attachNodes[i];
-
-                if (log != null)
-                {
-                    log.buf.AppendLine("AttachNode " + attachNode.id + " = " + (attachNode.attachedPart != null ? attachNode.attachedPart.partInfo.name : "null"));
-                }
-
-                if (attachNode.attachedPart != null && attachNode.id != "Strut")
-                {
-                    PartSim attachedSim;
-                    if (partSimLookup.TryGetValue(attachNode.attachedPart, out attachedSim))
-                    {
-                        if (log != null)
-                        {
-                            log.buf.AppendLine("Adding attached node " + attachedSim.name + ":" + attachedSim.partId + "");
-                        }
-
-                        attachNodes.Add(AttachNodeSim.GetPoolObject().Initialise(attachedSim, attachNode.id, attachNode.nodeType));
-                    }
-                    else
-                    {
-                        if (log != null)
-                        {
-                            log.buf.AppendLine("No PartSim for attached part (" + attachNode.attachedPart.partInfo.name + ")");
-                        }
-                    }
-                }
-            }
-
-            for (int i = 0; i < part.fuelLookupTargets.Count; ++i)
-            {
-                Part p = part.fuelLookupTargets[i];
-
-                if (p != null)
-                {
-                    PartSim targetSim;
-                    if (partSimLookup.TryGetValue(p, out targetSim))
-                    {
-                        if (log != null)
-                        {
-                            log.buf.AppendLine("Fuel target: " + targetSim.name + ":" + targetSim.partId);
-                        }
-
-                        this.fuelTargets.Add(targetSim);
-                    }
-                    else
-                    {
-                        if (log != null)
-                        {
-                            log.buf.AppendLine("No PartSim for fuel target (" + p.name + ")");
-                        }
-                    }
-                }
-            }
-        }
-
         private int DecoupledInStage(Part thePart, int stage = -1)
         {
-            if (this.IsDecoupler(thePart))
+            if (IsDecoupler(thePart))
             {
                 if (thePart.inverseStage > stage)
                 {
@@ -441,10 +781,16 @@
 
             if (thePart.parent != null)
             {
-                stage = this.DecoupledInStage(thePart.parent, stage);
+                stage = DecoupledInStage(thePart.parent, stage);
             }
 
             return stage;
+        }
+
+        private bool IsActiveDecoupler(Part thePart)
+        {
+            return thePart.FindModulesImplementing<ModuleDecouple>().Any(mod => !mod.isDecoupled) ||
+                   thePart.FindModulesImplementing<ModuleAnchoredDecoupler>().Any(mod => !mod.isDecoupled);
         }
 
         private bool IsDecoupler(Part thePart)
@@ -453,25 +799,19 @@
                    thePart.HasModule<ModuleAnchoredDecoupler>();
         }
 
-        private bool IsActiveDecoupler(Part thePart)
-        {
-            return thePart.FindModulesImplementing<ModuleDecouple>().Any(mod => !mod.isDecoupled) ||
-                   thePart.FindModulesImplementing<ModuleAnchoredDecoupler>().Any(mod => !mod.isDecoupled);
-        }
-
         private bool IsSepratron()
         {
-            if (!this.part.ActivatesEvenIfDisconnected)
+            if (!part.ActivatesEvenIfDisconnected)
             {
                 return false;
             }
 
-            if (this.part is SolidRocket)
+            if (part is SolidRocket)
             {
                 return true;
             }
 
-            var modList = this.part.Modules.OfType<ModuleEngines>();
+            IEnumerable<ModuleEngines> modList = part.Modules.OfType<ModuleEngines>();
             if (modList.Count() == 0)
             {
                 return false;
@@ -483,339 +823,6 @@
             }
 
             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, HashSet<PartSim> visited, LogMsg log, String indent)
-        {
-            if (log != null)
-            {
-                log.buf.AppendLine(indent + "GetSourceSet(" + ResourceContainer.GetResourceName(type) + ") for " + this.name + ":" + this.partId);
-                indent += "  ";
-            }
-
-            HashSet<PartSim> allSources = new HashSet<PartSim>();
-            HashSet<PartSim> partSources = null;
-
-            // 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))
-            {
-                if (log != null)
-                {
-                    log.buf.AppendLine(indent + "Returning empty set, already visited (" + this.name + ":" + this.partId + ")");
-                }
-
-                return allSources;
-            }
-
-            //if (log != null)
-            //    log.buf.AppendLine(indent + "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");
-            for (int i = 0; i < fuelTargets.Count; ++i)
-            {
-                PartSim partSim = fuelTargets[i];
-
-                if (visited.Contains(partSim))
-                {
-                    //if (log != null)
-                    //    log.buf.AppendLine(indent + "Fuel target already visited, skipping (" + partSim.name + ":" + partSim.partId + ")");
-                }
-                else
-                {
-                    //if (log != null)
-                    //    log.buf.AppendLine(indent + "Adding fuel target as source (" + partSim.name + ":" + partSim.partId + ")");
-
-                    partSources = partSim.GetSourceSet(type, allParts, visited, log, indent);
-                    if (partSources.Count > 0)
-                    {
-                        allSources.UnionWith(partSources);
-                        partSources.Clear();
-                    }
-                }
-            }
-
-            if (allSources.Count > 0)
-            {
-                if (log != null)
-                {
-                    log.buf.AppendLine(indent + "Returning " + allSources.Count + " fuel target sources (" + this.name + ":" + this.partId + ")");
-                }
-
-                return allSources;
-            }
-
-            // Rule 3: This rule has been removed and merged with rules 4 and 7 to fix issue with fuel tanks with disabled crossfeed
-
-            // 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]
-            if (this.fuelCrossFeed)
-            {
-                //MonoBehaviour.print("foreach attach node");
-                for (int i = 0; i < attachNodes.Count; ++i)
-                {
-                    AttachNodeSim attachSim = attachNodes[i];
-
-                    if (attachSim.attachedPartSim != null)
-                    {
-                        if (attachSim.nodeType == AttachNode.NodeType.Stack)
-                        {
-                            if (!(this.noCrossFeedNodeKey != null && this.noCrossFeedNodeKey.Length > 0 && attachSim.id.Contains(this.noCrossFeedNodeKey)))
-                            {
-                                if (visited.Contains(attachSim.attachedPartSim))
-                                {
-                                    //if (log != null)
-                                    //    log.buf.AppendLine(indent + "Attached part already visited, skipping (" + attachSim.attachedPartSim.name + ":" + attachSim.attachedPartSim.partId + ")");
-                                }
-                                else
-                                {
-                                    //if (log != null)
-                                    //    log.buf.AppendLine(indent + "Adding attached part as source (" + attachSim.attachedPartSim.name + ":" + attachSim.attachedPartSim.partId + ")");
-
-                                    partSources = attachSim.attachedPartSim.GetSourceSet(type, allParts, visited, log, indent);
-                                    if (partSources.Count > 0)
-                                    {
-                                        allSources.UnionWith(partSources);
-                                        partSources.Clear();
-                                    }
-                                }
-                            }
-                        }
-                    }
-                }
-
-                if (allSources.Count > 0)
-                {
-                    if (log != null)
-                    {
-                        log.buf.AppendLine(indent + "Returning " + allSources.Count + " attached sources (" + this.name + ":" + this.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]
-            if (this.resources.HasType(type) && this.resourceFlowStates[type] != 0)
-            {
-                if (this.resources[type] > SimManager.RESOURCE_MIN)
-                {
-                    allSources.Add(this);
-
-                    if (log != null)
-                    {
-                        log.buf.AppendLine(indent + "Returning enabled tank as only source (" + this.name + ":" + this.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 && this.parentAttach == AttachModes.SRF_ATTACH)
-            {
-                if (this.fuelCrossFeed)
-                {
-                    if (visited.Contains(this.parent))
-                    {
-                        //if (log != null)
-                        //    log.buf.AppendLine(indent + "Parent part already visited, skipping (" + parent.name + ":" + parent.partId + ")");
-                    }
-                    else
-                    {
-                        allSources = this.parent.GetSourceSet(type, allParts, visited, log, indent);
-                        if (allSources.Count > 0)
-                        {
-                            if (log != null)
-                            {
-                                log.buf.AppendLine(indent + "Returning " + allSources.Count + " parent sources (" + this.name + ":" + this.partId + ")");
-                            }
-
-                            return allSources;
-                        }
-                    }
-                }
-            }
-
-            // Rule 8: If all preceding rules failed, part returns empty list.
-            //if (log != null)
-            //    log.buf.AppendLine(indent + "Returning empty set, no sources found (" + name + ":" + partId + ")");
-
-            return allSources;
-        }
-
-        public void RemoveAttachedParts(HashSet<PartSim> partSims)
-        {
-            // Loop through the attached parts
-            for (int i = 0; i < attachNodes.Count; ++i)
-            {
-                AttachNodeSim attachSim = attachNodes[i];
-
-                // 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 + ")");
-            for (int i = 0; i < resourceDrains.Types.Count; ++i)
-            {
-                int type = resourceDrains.Types[i];
-
-                //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;
-
-            for (int i = 0; i < resourceDrains.Types.Count; ++i)
-            {
-                int type = resourceDrains.Types[i];
-
-                if (this.resourceDrains[type] > 0)
-                {
-                    time = Math.Min(time, this.resources[type] / this.resourceDrains[type]);
-                    //MonoBehaviour.print("type = " + ResourceContainer.GetResourceName(type) + "  amount = " + resources[type] + "  rate = " + resourceDrains[type] + "  time = " + time);
-                }
-            }
-
-            //if (time < double.MaxValue)
-            //    MonoBehaviour.print("TimeToDrainResource(" + name + ":" + partId + ") = " + time);
-            return time;
-        }
-
-        public bool EmptyOf(HashSet<int> types)
-        {
-            foreach (int type in types)
-            {
-                if (this.resources.HasType(type) && this.resourceFlowStates[type] != 0 && (double)this.resources[type] > SimManager.RESOURCE_MIN)
-                {
-                    return false;
-                }
-            }
-
-            return true;
-        }
-
-        public int DecouplerCount()
-        {
-            int count = 0;
-            PartSim partSim = this;
-            while (partSim != null)
-            {
-                if (partSim.isDecoupler)
-                {
-                    count++;
-                }
-
-                partSim = partSim.parent;
-            }
-            return count;
-        }
-
-        public double GetStartMass()
-        {
-            return this.startMass;
-        }
-
-        public double GetMass()
-        {
-            double mass = this.baseMass;
-
-            for (int i = 0; i < resources.Types.Count; ++i)
-            {
-                mass += this.resources.GetResourceMass(resources.Types[i]);
-            }
-
-            return mass;
-        }
-
-        public String DumpPartAndParentsToBuffer(StringBuilder buffer, String prefix)
-        {
-            if (this.parent != null)
-            {
-                prefix = this.parent.DumpPartAndParentsToBuffer(buffer, prefix) + " ";
-            }
-
-            this.DumpPartToBuffer(buffer, prefix);
-
-            return prefix;
-        }
-
-        public void DumpPartToBuffer(StringBuilder buffer, String prefix, List<PartSim> allParts = null)
-        {
-            buffer.Append(prefix);
-            buffer.Append(this.name);
-            buffer.AppendFormat(":[id = {0:d}, decouple = {1:d}, invstage = {2:d}", this.partId, this.decoupledInStage, this.inverseStage);
-
-            buffer.AppendFormat(", vesselName = '{0}'", this.vesselName);
-            buffer.AppendFormat(", vesselType = {0}", SimManager.GetVesselTypeString(this.vesselType));
-            buffer.AppendFormat(", initialVesselName = '{0}'", this.initialVesselName);
-
-            buffer.AppendFormat(", fuelCF = {0}", this.fuelCrossFeed);
-            buffer.AppendFormat(", noCFNKey = '{0}'", this.noCrossFeedNodeKey);
-
-            buffer.AppendFormat(", isSep = {0}", this.isSepratron);
-
-            foreach (int type in this.resources.Types)
-            {
-                buffer.AppendFormat(", {0} = {1:g6}", ResourceContainer.GetResourceName(type), this.resources[type]);
-            }
-
-            if (this.attachNodes.Count > 0)
-            {
-                buffer.Append(", attached = <");
-                this.attachNodes[0].DumpToBuffer(buffer);
-                for (int i = 1; i < this.attachNodes.Count; i++)
-                {
-                    buffer.Append(", ");
-                    this.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);
-                    }
-                }
-            }
         }
     }
 }

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