// 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() { // Update the active engines UpdateActiveEngines(); // Empty the draining resources set drainingResources.Clear(); // Reset the resource drains of all draining parts foreach (PartSim partSim in drainingParts) { partSim.resourceDrains.Reset(); } // Empty the draining parts set drainingParts.Clear(); // Loop through all the active engine modules for (int i = 0; i < activeEngines.Count; ++i) { EngineSim engine = activeEngines[i]; // Set the resource drains for this engine if (engine.SetResourceDrains(log, allParts, allFuelLines, drainingParts)) { // If it is active then add the consumed resource types to the set for (int j = 0; j < engine.ResourceConsumptionsForMass.Types.Count; ++j) { drainingResources.Add(engine.ResourceConsumptionsForMass.Types[j]); } } } //foreach (RCSSim r in allRCS) { // r.SetResourceDrains(log, allParts); //} // Update the active engines again to remove any engines that have no fuel supply UpdateActiveEngines(); if (log != null) { log.AppendLine("Active engines = ", activeEngines.Count); int i = 0; for (int j = 0; j < activeEngines.Count; j++) { EngineSim engine = activeEngines[j]; log.Append("Engine " + (i++) + ":"); engine.DumpEngineToLog(log); } log.Flush(); } }
public void Dump(LogMsg log) { log.buf.AppendFormat("number : {0:d}\n", this.number); log.buf.AppendFormat("cost : {0:g6}\n", this.cost); log.buf.AppendFormat("totalCost : {0:g6}\n", this.totalCost); log.buf.AppendFormat("time : {0:g6}\n", this.time); log.buf.AppendFormat("totalTime : {0:g6}\n", this.totalTime); log.buf.AppendFormat("mass : {0:g6}\n", this.mass); log.buf.AppendFormat("fullMass : {0:g6}\n", this.stageFullMass); log.buf.AppendFormat("resourceMass : {0:g6}\n", this.resourceMass); log.buf.AppendFormat("endMass : {0:g6}\n", this.endMass); log.buf.AppendFormat("totalMass : {0:g6}\n", this.totalMass); log.buf.AppendFormat("isp : {0:g6}\n", this.isp); log.buf.AppendFormat("thrust : {0:g6}\n", this.thrust); log.buf.AppendFormat("actualThrust : {0:g6}\n", this.actualThrust); log.buf.AppendFormat("simpleThrust : {0:g6}\n", this.simpleThrust); log.buf.AppendFormat("thrustToWeight: {0:g6}\n", this.thrustToWeight); log.buf.AppendFormat("maxTWR : {0:g6}\n", this.maxThrustToWeight); log.buf.AppendFormat("actualTWR : {0:g6}\n", this.actualThrustToWeight); log.buf.AppendFormat("ThrustTorque : {0:g6}\n", this.maxThrustTorque); log.buf.AppendFormat("ThrustOffset : {0:g6}\n", this.thrustOffsetAngle); log.buf.AppendFormat("deltaV : {0:g6}\n", this.deltaV); log.buf.AppendFormat("totalDeltaV : {0:g6}\n", this.totalDeltaV); log.buf.AppendFormat("invTotDeltaV : {0:g6}\n", this.inverseTotalDeltaV); log.buf.AppendFormat("stageDeltaV : {0:g6}\n", this.stageStartDeltaV); log.Flush(); }
public void Dump(LogMsg log) { log.buf.AppendFormat("number : {0:d}\n", this.number); log.buf.AppendFormat("cost : {0:g6}\n", this.cost); log.buf.AppendFormat("totalCost : {0:g6}\n", this.totalCost); log.buf.AppendFormat("time : {0:g6}\n", this.time); log.buf.AppendFormat("totalTime : {0:g6}\n", this.totalTime); log.buf.AppendFormat("mass : {0:g6}\n", this.mass); log.buf.AppendFormat("totalMass : {0:g6}\n", this.totalMass); log.buf.AppendFormat("isp : {0:g6}\n", this.isp); log.buf.AppendFormat("thrust : {0:g6}\n", this.thrust); log.buf.AppendFormat("actualThrust : {0:g6}\n", this.actualThrust); log.buf.AppendFormat("thrustToWeight: {0:g6}\n", this.thrustToWeight); log.buf.AppendFormat("maxTWR : {0:g6}\n", this.maxThrustToWeight); log.buf.AppendFormat("actualTWR : {0:g6}\n", this.actualThrustToWeight); log.buf.AppendFormat("ThrustTorque : {0:g6}\n", this.maxThrustTorque); log.buf.AppendFormat("ThrustOffset : {0:g6}\n", this.thrustOffsetAngle); log.buf.AppendFormat("deltaV : {0:g6}\n", this.deltaV); log.buf.AppendFormat("totalDeltaV : {0:g6}\n", this.totalDeltaV); log.buf.AppendFormat("invTotDeltaV : {0:g6}\n", this.inverseTotalDeltaV); log.buf.AppendFormat("RCSdeltaVStart : {0:g6}\n", this.RCSdeltaVStart); log.buf.AppendFormat("RCSIsp : {0:g6}\n", this.RCSIsp); log.buf.AppendFormat("RCSThrust : {0:g6}\n", this.RCSThrust); log.buf.AppendFormat("RCSTWRStart : {0:g6}\n", this.RCSTWRStart); log.buf.AppendFormat("RCSdeltaVEnd : {0:g6}\n", this.RCSdeltaVEnd); log.buf.AppendFormat("RCSTWREnd : {0:g6}\n", this.RCSTWREnd); log.Flush(); }
private static void CheckForMods() { hasCheckedForMods = true; foreach (var assembly in AssemblyLoader.loadedAssemblies) { log.AppendLine("Assembly: ", assembly.assembly); var name = assembly.assembly.ToString().Split(',')[0]; if (name == "RealFuels") { log.AppendLine("Found RealFuels mod"); var RF_ModuleEngineConfigs_Type = assembly.assembly.GetType("RealFuels.ModuleEngineConfigs"); if (RF_ModuleEngineConfigs_Type != null) { RF_ModuleEngineConfigs_localCorrectThrust = RF_ModuleEngineConfigs_Type.GetField("localCorrectThrust"); } var RF_ModuleHybridEngine_Type = assembly.assembly.GetType("RealFuels.ModuleHybridEngine"); if (RF_ModuleHybridEngine_Type != null) { RF_ModuleHybridEngine_localCorrectThrust = RF_ModuleHybridEngine_Type.GetField("localCorrectThrust"); } var RF_ModuleHybridEngines_Type = assembly.assembly.GetType("RealFuels.ModuleHybridEngines"); if (RF_ModuleHybridEngines_Type != null) { RF_ModuleHybridEngines_localCorrectThrust = RF_ModuleHybridEngines_Type.GetField("localCorrectThrust"); } hasInstalledRealFuels = true; break; } else if (name == "KerbalIspDifficultyScaler") { log.AppendLine("Found KIDS mod"); var KIDS_Utils_Type = assembly.assembly.GetType("KerbalIspDifficultyScaler.KerbalIspDifficultyScalerUtils"); if (KIDS_Utils_Type != null) { KIDS_Utils_GetIspMultiplier = KIDS_Utils_Type.GetMethod("GetIspMultiplier"); } KIDSparameters = new object[6]; hasInstalledKIDS = true; } } log.Flush(); }
public void Dump(LogMsg log) { log.buf.AppendFormat("number : {0:d}\n", this.number); log.buf.AppendFormat("cost : {0:g6}\n", this.cost); log.buf.AppendFormat("totalCost : {0:g6}\n", this.totalCost); log.buf.AppendFormat("time : {0:g6}\n", this.time); log.buf.AppendFormat("totalTime : {0:g6}\n", this.totalTime); log.buf.AppendFormat("mass : {0:g6}\n", this.mass); log.buf.AppendFormat("totalMass : {0:g6}\n", this.totalMass); log.buf.AppendFormat("isp : {0:g6}\n", this.isp); log.buf.AppendFormat("thrust : {0:g6}\n", this.thrust); log.buf.AppendFormat("actualThrust : {0:g6}\n", this.actualThrust); log.buf.AppendFormat("thrustToWeight: {0:g6}\n", this.thrustToWeight); log.buf.AppendFormat("maxTWR : {0:g6}\n", this.maxThrustToWeight); log.buf.AppendFormat("actualTWR : {0:g6}\n", this.actualThrustToWeight); log.buf.AppendFormat("ThrustTorque : {0:g6}\n", this.maxThrustTorque); log.buf.AppendFormat("ThrustOffset : {0:g6}\n", this.thrustOffsetAngle); log.buf.AppendFormat("deltaV : {0:g6}\n", this.deltaV); log.buf.AppendFormat("totalDeltaV : {0:g6}\n", this.totalDeltaV); log.buf.AppendFormat("invTotDeltaV : {0:g6}\n", this.inverseTotalDeltaV); log.Flush(); }
public void CreateEngineSims(List<EngineSim> allEngines, double atmosphere, double mach, bool vectoredThrust, bool fullThrust, LogMsg log) { if (log != null) { log.buf.AppendLine("CreateEngineSims for " + this.name); for (int i = 0; i < this.part.Modules.Count; i++) { PartModule partMod = this.part.Modules[i]; log.buf.AppendLine("Module: " + partMod.moduleName); } } if (hasMultiModeEngine) { // A multi-mode engine has multiple ModuleEngines but only one is active at any point // The mode of the engine is the engineID of the ModuleEngines that is active string mode = part.GetModule<MultiModeEngine>().mode; List<ModuleEngines> engines = part.GetModules<ModuleEngines>(); for (int i = 0; i < engines.Count; ++i) { ModuleEngines engine = engines[i]; if (engine.engineID == mode) { if (log != null) log.buf.AppendLine("Module: " + engine.moduleName); EngineSim engineSim = EngineSim.New( this, engine, atmosphere, (float)mach, vectoredThrust, fullThrust, log); 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); EngineSim engineSim = EngineSim.New( this, engine, atmosphere, (float)mach, vectoredThrust, fullThrust, log); allEngines.Add(engineSim); } } if (log != null) { log.Flush(); } }
// This function runs the simulation and returns a newly created array of Stage objects public Stage[] RunSimulation(LogMsg _log) { log = _log; if (log != null) { log.AppendLine("RunSimulation started"); } _timer.Reset(); _timer.Start(); // Start with the last stage to simulate // (this is in a member variable so it can be accessed by AllowedToStage and ActivateStage) currentStage = lastStage; // Work out which engines would be active if just doing the staging and if this is different to the // currently active engines then generate an extra stage // Loop through all the engines bool anyActive = false; for (int i = 0; i < allEngines.Count; ++i) { EngineSim engine = allEngines[i]; if (log != null) { log.AppendLine("Testing engine mod of ", engine.partSim.name, ":", engine.partSim.partId); } bool bActive = engine.isActive; bool bStage = (engine.partSim.inverseStage >= currentStage); if (log != null) { log.AppendLine("bActive = ", bActive, " bStage = ", bStage); } if (HighLogic.LoadedSceneIsFlight) { if (bActive) { anyActive = true; } if (bActive != bStage) { // If the active state is different to the state due to staging if (log != null) { log.AppendLine("Need to do current active engines first"); } doingCurrent = true; } } else { if (bStage) { if (log != null) { log.AppendLine("Marking as active"); } engine.isActive = true; } } } // If we need to do current because of difference in engine activation and there actually are active engines // then we do the extra stage otherwise activate the next stage and don't treat it as current if (doingCurrent && anyActive) { currentStage++; } else { ActivateStage(); doingCurrent = false; } // Create a list of lists of PartSims that prevent decoupling BuildDontStageLists(log); if (log != null) { log.Flush(); } // Create the array of stages that will be returned Stage[] stages = new Stage[currentStage + 1]; int startStage = currentStage; // Loop through the stages while (currentStage >= 0) { if (log != null) { log.AppendLine("Simulating stage ", currentStage); log.Flush(); _timer.Reset(); _timer.Start(); } // Update active engines and resource drains UpdateResourceDrains(); // Update the masses of the parts to correctly handle "no physics" parts stageStartMass = UpdatePartMasses(); if (log != null) { allParts[0].DumpPartToLog(log, "", allParts); } // Create the Stage object for this stage Stage stage = new Stage(); stageTime = 0d; vecStageDeltaV = Vector3.zero; stageStartCom = ShipCom; stepStartMass = stageStartMass; stepEndMass = 0; CalculateThrustAndISP(); // Store various things in the Stage object stage.thrust = totalStageThrust; stage.thrustToWeight = totalStageThrust / (stageStartMass * gravity); stage.maxThrustToWeight = stage.thrustToWeight; stage.actualThrust = totalStageActualThrust; stage.actualThrustToWeight = totalStageActualThrust / (stageStartMass * gravity); CalculateRCS(gravity, false); stage.RCSIsp = RCSIsp; stage.RCSThrust = RCSThrust; stage.RCSdeltaVStart = RCSDeltaV; stage.RCSTWRStart = RCSTWR; stage.RCSBurnTime = RCSBurnTime; if (log != null) { log.AppendLine("stage.thrust = ", stage.thrust); log.AppendLine("StageMass = ", stageStartMass); log.AppendLine("Initial maxTWR = ", stage.maxThrustToWeight); } // calculate torque and associates stage.maxThrustTorque = totalStageThrustForce.TorqueAt(stageStartCom).magnitude; // torque divided by thrust. imagine that all engines are at the end of a lever that tries to turn the ship. // this numerical value, in meters, would represent the length of that lever. double torqueLeverArmLength = (stage.thrust <= 0) ? 0 : stage.maxThrustTorque / stage.thrust; // how far away are the engines from the CoM, actually? double thrustDistance = (stageStartCom - totalStageThrustForce.GetAverageForceApplicationPoint()).magnitude; // the combination of the above two values gives an approximation of the offset angle. double sinThrustOffsetAngle = 0; if (thrustDistance > 1e-7) { sinThrustOffsetAngle = torqueLeverArmLength / thrustDistance; if (sinThrustOffsetAngle > 1) { sinThrustOffsetAngle = 1; } } stage.thrustOffsetAngle = Math.Asin(sinThrustOffsetAngle) * 180 / Math.PI; // Calculate the total cost of the vessel at this point stage.totalCost = 0d; for (int i = 0; i < allParts.Count; ++i) { if (currentStage > allParts[i].decoupledInStage) { stage.totalCost += allParts[i].GetCost(currentStage); } } // The total mass is simply the mass at the start of the stage stage.totalMass = stageStartMass; // If we have done a previous stage if (currentStage < startStage) { // Calculate what the previous stage's mass and cost were by subtraction Stage prev = stages[currentStage + 1]; prev.cost = prev.totalCost - stage.totalCost; prev.mass = prev.totalMass - stage.totalMass; } // The above code will never run for the last stage so set those directly if (currentStage == 0) { stage.cost = stage.totalCost; stage.mass = stage.totalMass; } dontStageParts = dontStagePartsLists[currentStage]; if (log != null) { log.AppendLine("Stage setup took ", _timer.ElapsedMilliseconds, "ms"); if (dontStageParts.Count > 0) { log.AppendLine("Parts preventing staging:"); for (int i = 0; i < dontStageParts.Count; i++) { PartSim partSim = dontStageParts[i]; partSim.DumpPartToLog(log, ""); } } else { log.AppendLine("No parts preventing staging"); } log.Flush(); } // Now we will loop until we are allowed to stage int loopCounter = 0; while (!AllowedToStage()) { loopCounter++; //if (log != null) log.AppendLine("loop = ", loopCounter); // Calculate how long each draining tank will take to drain and run for the minimum time double resourceDrainTime = double.MaxValue; PartSim partMinDrain = null; foreach (PartSim partSim in drainingParts) { double time = partSim.TimeToDrainResource(log); if (time < resourceDrainTime) { resourceDrainTime = time; partMinDrain = partSim; } } if (log != null) { log.Append("Drain time = ", resourceDrainTime, " (", partMinDrain.name) .AppendLine(":", partMinDrain.partId, ")"); } foreach (PartSim partSim in drainingParts) { partSim.DrainResources(resourceDrainTime, log); } // Get the mass after draining stepEndMass = ShipMass; stageTime += resourceDrainTime; double stepEndTWR = totalStageThrust / (stepEndMass * gravity); /*if (log != null) * { * log.AppendLine("After drain mass = ", stepEndMass); * log.AppendLine("currentThrust = ", totalStageThrust); * log.AppendLine("currentTWR = ", stepEndTWR); * }*/ if (stepEndTWR > stage.maxThrustToWeight) { stage.maxThrustToWeight = stepEndTWR; } //if (log != null) log.AppendLine("newMaxTWR = ", stage.maxThrustToWeight); // 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) { vecStageDeltaV += vecThrust * (float)((currentisp * Units.GRAVITY * Math.Log(stepStartMass / stepEndMass)) / simpleTotalThrust); } // Update the active engines and resource drains for the next step UpdateResourceDrains(); // Recalculate the current thrust and isp for the next step CalculateThrustAndISP(); // Check if we actually changed anything if (stepStartMass == stepEndMass) { //MonoBehaviour.print("No change in mass"); break; } // Check to stop rampant looping if (loopCounter == 1000) { if (log != null) { log.AppendLine("exceeded loop count"); log.AppendLine("stageStartMass = " + stageStartMass); log.AppendLine("stepStartMass = " + stepStartMass); log.AppendLine("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 // Store the magnitude of the deltaV vector stage.deltaV = vecStageDeltaV.magnitude; stage.resourceMass = stageStartMass - stepEndMass; if (HighLogic.LoadedSceneIsEditor) //this is only needed in the VAB. { CalculateRCS(gravity, true); } stage.RCSdeltaVEnd = RCSDeltaV; stage.RCSTWREnd = RCSTWR; // Recalculate effective stage isp from the stage deltaV (flip the standard deltaV calculation around) // Note: If the mass doesn't change then this is a divide by zero if (stageStartMass != stepStartMass) { stage.isp = stage.deltaV / (Units.GRAVITY * Math.Log(stageStartMass / stepStartMass)); } else { stage.isp = 0; } // 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 = doingCurrent ? -1 : currentStage; // Set the stage number to -1 if doing current engines stage.totalPartCount = allParts.Count; stage.maxMach = maxMach; stages[currentStage] = stage; // Now activate the next stage currentStage--; doingCurrent = false; if (log != null) { // Log how long the stage took _timer.Stop(); log.AppendLine("Simulating stage took ", _timer.ElapsedMilliseconds, "ms"); stage.Dump(log); _timer.Reset(); _timer.Start(); } // Activate the next stage ActivateStage(); if (log != null) { // Log how long it took to activate _timer.Stop(); log.AppendLine("ActivateStage took ", _timer.ElapsedMilliseconds, "ms"); } } // 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].totalDeltaV += stages[j].deltaV; stages[i].totalResourceMass += stages[j].resourceMass; stages[i].totalTime += stages[j].time; stages[i].partCount = i > 0 ? stages[i].totalPartCount - stages[i - 1].totalPartCount : stages[i].totalPartCount; } // 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; } } FreePooledObject(); _timer.Stop(); if (log != null) { log.AppendLine("RunSimulation: ", _timer.ElapsedMilliseconds, "ms"); log.Flush(); } log = null; return(stages); }
// 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(LogMsg _log, List <Part> parts, double theGravity, double theAtmosphere = 0, double theMach = 0, bool dumpTree = false, bool vectoredThrust = false, bool fullThrust = false) { log = _log; if (log != null) { log.AppendLine("PrepareSimulation started"); } _timer.Reset(); _timer.Start(); // Store the parameters in members for ease of access in other functions partList = parts; gravity = theGravity; atmosphere = theAtmosphere; mach = theMach; lastStage = StageManager.LastStage; maxMach = 1.0f; if (log != null) { log.AppendLine("lastStage = ", lastStage); } // Clear the lists for our simulation parts allParts.Clear(); allFuelLines.Clear(); drainingParts.Clear(); allEngines.Clear(); activeEngines.Clear(); drainingResources.Clear(); // A dictionary for fast lookup of Part->PartSim during the preparation phase partSimLookup.Clear(); if (partList.Count > 0 && partList[0].vessel != null) { vesselName = partList[0].vessel.vesselName; vesselType = partList[0].vessel.vesselType; } // First we create a PartSim for each Part (giving each a unique id) int partId = 1; for (int i = 0; i < partList.Count; ++i) { Part part = partList[i]; // If the part is already in the lookup dictionary then log it and skip to the next part if (partSimLookup.ContainsKey(part)) { if (log != null) { log.AppendLine("Part ", part.name, " appears in vessel list more than once"); } continue; } // Create the PartSim PartSim partSim = PartSim.New(part, partId, atmosphere, log); // Add it to the Part lookup dictionary and the necessary lists partSimLookup.Add(part, partSim); allParts.Add(partSim); if (partSim.isFuelLine) { allFuelLines.Add(partSim); } if (partSim.isEngine) { partSim.CreateEngineSims(allEngines, atmosphere, mach, vectoredThrust, fullThrust, log); } if (partSim.isRCS) { partSim.CreateRCSSims(allRCS, atmosphere, mach, vectoredThrust, fullThrust, log); } partId++; } for (int i = 0; i < allEngines.Count; ++i) { maxMach = Mathf.Max(maxMach, allEngines[i].maxMach); } UpdateActiveEngines(); // Now that all the PartSims have been created we can do any set up that needs access to other parts // First we set up all the parent links for (int i = 0; i < allParts.Count; i++) { PartSim partSim = allParts[i]; partSim.SetupParent(partSimLookup, log); } // Then, in the VAB/SPH, we add the parent of each fuel line to the fuelTargets list of their targets if (HighLogic.LoadedSceneIsEditor) { for (int i = 0; i < allFuelLines.Count; ++i) { PartSim partSim = allFuelLines[i]; CModuleFuelLine fuelLine = partSim.part.GetModule <CModuleFuelLine>(); if (fuelLine.target != null) { PartSim targetSim; if (partSimLookup.TryGetValue(fuelLine.target, out targetSim)) { if (log != null) { log.AppendLine("Fuel line target is ", targetSim.name, ":", targetSim.partId); } targetSim.fuelTargets.Add(partSim.parent); } else { if (log != null) { log.AppendLine("No PartSim for fuel line target (", partSim.part.partInfo.name, ")"); } } } else { if (log != null) { log.AppendLine("Fuel line target is null"); } } } } if (log != null) { log.AppendLine("SetupAttachNodes and count stages"); } for (int i = 0; i < allParts.Count; ++i) { PartSim partSim = allParts[i]; partSim.SetupAttachNodes(partSimLookup, log); if (partSim.decoupledInStage >= lastStage) { lastStage = partSim.decoupledInStage + 1; } } // And finally release the Part references from all the PartSims if (log != null) { log.AppendLine("ReleaseParts"); } for (int i = 0; i < allParts.Count; ++i) { allParts[i].ReleasePart(); } // And dereference the core's part list partList = null; _timer.Stop(); if (log != null) { log.AppendLine("PrepareSimulation: ", _timer.ElapsedMilliseconds, "ms"); log.Flush(); } Dump(); log = null; return(true); }
public void CreateEngineSims(List<EngineSim> allEngines, double atmosphere, double velocity, bool vectoredThrust, 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; foreach (ModuleEnginesFX engine in part.GetModules<ModuleEnginesFX>()) { if (engine.engineID == mode) { if (log != null) log.buf.AppendLine("Module: " + engine.moduleName); Vector3 thrustvec = CalculateThrustVector(vectoredThrust ? engine.thrustTransforms : null, log); EngineSim engineSim = new EngineSim(this, atmosphere, velocity, engine.maxThrust, engine.minThrust, engine.thrustPercentage, engine.requestedThrust, thrustvec, engine.realIsp, engine.atmosphereCurve, engine.useVelocityCurve ? engine.velocityCurve : null, engine.throttleLocked, engine.propellants, engine.isOperational, correctThrust); allEngines.Add(engineSim); } } } else { if (hasModuleEnginesFX) { foreach (ModuleEnginesFX engine in part.GetModules<ModuleEnginesFX>()) { if (log != null) log.buf.AppendLine("Module: " + engine.moduleName); Vector3 thrustvec = CalculateThrustVector(vectoredThrust ? engine.thrustTransforms : null, log); EngineSim engineSim = new EngineSim(this, atmosphere, velocity, engine.maxThrust, engine.minThrust, engine.thrustPercentage, engine.requestedThrust, thrustvec, engine.realIsp, engine.atmosphereCurve, engine.useVelocityCurve ? engine.velocityCurve : null, engine.throttleLocked, engine.propellants, engine.isOperational, correctThrust); allEngines.Add(engineSim); } } if (hasModuleEngines) { foreach (ModuleEngines engine in part.GetModules<ModuleEngines>()) { if (log != null) log.buf.AppendLine("Module: " + engine.moduleName); Vector3 thrustvec = CalculateThrustVector(vectoredThrust ? engine.thrustTransforms : null, log); EngineSim engineSim = new EngineSim(this, atmosphere, velocity, engine.maxThrust, engine.minThrust, engine.thrustPercentage, engine.requestedThrust, thrustvec, engine.realIsp, engine.atmosphereCurve, engine.useVelocityCurve ? engine.velocityCurve : null, engine.throttleLocked, engine.propellants, engine.isOperational, correctThrust); allEngines.Add(engineSim); } } } if (log != null) log.Flush(); }
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 " + this.name); for (int i = 0; i < this.part.Modules.Count; i++) { PartModule partMod = this.part.Modules[i]; 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 = this.CalculateThrustVector(vectoredThrust ? engine.thrustTransforms : null, log); EngineSim engineSim = EngineSim.New( 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, fullThrust ? 0.0f : engine.currentThrottle, engine.g, engine.throttleLocked, engine.propellants, engine.isOperational, engine.resultingThrust, engine.thrustTransforms, log); 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 = this.CalculateThrustVector(vectoredThrust ? engine.thrustTransforms : null, log); EngineSim engineSim = EngineSim.New( 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, fullThrust ? 0.0f : engine.currentThrottle, engine.g, engine.throttleLocked, engine.propellants, engine.isOperational, engine.resultingThrust, engine.thrustTransforms, log); allEngines.Add(engineSim); } } } if (log != null) { log.Flush(); } }
public void CreateEngineSims(List <EngineSim> allEngines, double atmosphere, double velocity, bool vectoredThrust, 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; foreach (ModuleEnginesFX engine in part.GetModules <ModuleEnginesFX>()) { if (engine.engineID == mode) { if (log != null) { log.buf.AppendLine("Module: " + engine.moduleName); } Vector3 thrustvec = CalculateThrustVector(vectoredThrust ? engine.thrustTransforms : null, log); EngineSim engineSim = new EngineSim(this, atmosphere, velocity, engine.maxThrust, engine.minThrust, engine.thrustPercentage, engine.requestedThrust, thrustvec, engine.realIsp, engine.atmosphereCurve, engine.useVelocityCurve ? engine.velocityCurve : null, engine.throttleLocked, engine.propellants, engine.isOperational, correctThrust); allEngines.Add(engineSim); } } } else { if (hasModuleEnginesFX) { foreach (ModuleEnginesFX engine in part.GetModules <ModuleEnginesFX>()) { if (log != null) { log.buf.AppendLine("Module: " + engine.moduleName); } Vector3 thrustvec = CalculateThrustVector(vectoredThrust ? engine.thrustTransforms : null, log); EngineSim engineSim = new EngineSim(this, atmosphere, velocity, engine.maxThrust, engine.minThrust, engine.thrustPercentage, engine.requestedThrust, thrustvec, engine.realIsp, engine.atmosphereCurve, engine.useVelocityCurve ? engine.velocityCurve : null, engine.throttleLocked, engine.propellants, engine.isOperational, correctThrust); allEngines.Add(engineSim); } } if (hasModuleEngines) { foreach (ModuleEngines engine in part.GetModules <ModuleEngines>()) { if (log != null) { log.buf.AppendLine("Module: " + engine.moduleName); } Vector3 thrustvec = CalculateThrustVector(vectoredThrust ? engine.thrustTransforms : null, log); EngineSim engineSim = new EngineSim(this, atmosphere, velocity, engine.maxThrust, engine.minThrust, engine.thrustPercentage, engine.requestedThrust, thrustvec, engine.realIsp, engine.atmosphereCurve, engine.useVelocityCurve ? engine.velocityCurve : null, engine.throttleLocked, engine.propellants, engine.isOperational, correctThrust); allEngines.Add(engineSim); } } } if (log != null) { log.Flush(); } }
// This function runs the simulation and returns a newly created array of Stage objects public Stage[] RunSimulation(LogMsg _log) { log = _log; if (log != null) log.AppendLine("RunSimulation started"); _timer.Reset(); _timer.Start(); // Start with the last stage to simulate // (this is in a member variable so it can be accessed by AllowedToStage and ActivateStage) currentStage = lastStage; // Work out which engines would be active if just doing the staging and if this is different to the // currently active engines then generate an extra stage // Loop through all the engines bool anyActive = false; for (int i = 0; i < allEngines.Count; ++i) { EngineSim engine = allEngines[i]; if (log != null) log.AppendLine("Testing engine mod of ", engine.partSim.name, ":", engine.partSim.partId); bool bActive = engine.isActive; bool bStage = (engine.partSim.inverseStage >= currentStage); if (log != null) log.AppendLine("bActive = ", bActive, " bStage = ", bStage); if (HighLogic.LoadedSceneIsFlight) { if (bActive) { anyActive = true; } if (bActive != bStage) { // If the active state is different to the state due to staging if (log != null) log.AppendLine("Need to do current active engines first"); doingCurrent = true; } } else { if (bStage) { if (log != null) log.AppendLine("Marking as active"); engine.isActive = true; } } } // If we need to do current because of difference in engine activation and there actually are active engines // then we do the extra stage otherwise activate the next stage and don't treat it as current if (doingCurrent && anyActive) { currentStage++; } else { ActivateStage(); doingCurrent = false; } // Create a list of lists of PartSims that prevent decoupling BuildDontStageLists(log); if (log != null) log.Flush(); // Create the array of stages that will be returned Stage[] stages = new Stage[currentStage + 1]; int startStage = currentStage; // Loop through the stages while (currentStage >= 0) { if (log != null) { log.AppendLine("Simulating stage ", currentStage); log.Flush(); _timer.Reset(); _timer.Start(); } // Update active engines and resource drains UpdateResourceDrains(); // Update the masses of the parts to correctly handle "no physics" parts stageStartMass = UpdatePartMasses(); if (log != null) allParts[0].DumpPartToLog(log, "", allParts); // Create the Stage object for this stage Stage stage = new Stage(); stageTime = 0d; vecStageDeltaV = Vector3.zero; stageStartCom = ShipCom; stepStartMass = stageStartMass; stepEndMass = 0; CalculateThrustAndISP(); // Store various things in the Stage object stage.thrust = totalStageThrust; stage.thrustToWeight = totalStageThrust / (stageStartMass * gravity); stage.maxThrustToWeight = stage.thrustToWeight; stage.actualThrust = totalStageActualThrust; stage.actualThrustToWeight = totalStageActualThrust / (stageStartMass * gravity); if (log != null) { log.AppendLine("stage.thrust = ", stage.thrust); log.AppendLine("StageMass = ", stageStartMass); log.AppendLine("Initial maxTWR = ", stage.maxThrustToWeight); } // calculate torque and associates stage.maxThrustTorque = totalStageThrustForce.TorqueAt(stageStartCom).magnitude; // torque divided by thrust. imagine that all engines are at the end of a lever that tries to turn the ship. // this numerical value, in meters, would represent the length of that lever. double torqueLeverArmLength = (stage.thrust <= 0) ? 0 : stage.maxThrustTorque / stage.thrust; // how far away are the engines from the CoM, actually? double thrustDistance = (stageStartCom - totalStageThrustForce.GetAverageForceApplicationPoint()).magnitude; // the combination of the above two values gives an approximation of the offset angle. double sinThrustOffsetAngle = 0; if (thrustDistance > 1e-7) { sinThrustOffsetAngle = torqueLeverArmLength / thrustDistance; if (sinThrustOffsetAngle > 1) { sinThrustOffsetAngle = 1; } } stage.thrustOffsetAngle = Math.Asin(sinThrustOffsetAngle) * 180 / Math.PI; // Calculate the total cost of the vessel at this point stage.totalCost = 0d; for (int i = 0; i < allParts.Count; ++i) { if (currentStage > allParts[i].decoupledInStage) stage.totalCost += allParts[i].GetCost(currentStage); } // The total mass is simply the mass at the start of the stage stage.totalMass = stageStartMass; // If we have done a previous stage if (currentStage < startStage) { // Calculate what the previous stage's mass and cost were by subtraction Stage prev = stages[currentStage + 1]; prev.cost = prev.totalCost - stage.totalCost; prev.mass = prev.totalMass - stage.totalMass; } // The above code will never run for the last stage so set those directly if (currentStage == 0) { stage.cost = stage.totalCost; stage.mass = stage.totalMass; } dontStageParts = dontStagePartsLists[currentStage]; if (log != null) { log.AppendLine("Stage setup took ", _timer.ElapsedMilliseconds, "ms"); if (dontStageParts.Count > 0) { log.AppendLine("Parts preventing staging:"); for (int i = 0; i < dontStageParts.Count; i++) { PartSim partSim = dontStageParts[i]; partSim.DumpPartToLog(log, ""); } } else { log.AppendLine("No parts preventing staging"); } log.Flush(); } // Now we will loop until we are allowed to stage int loopCounter = 0; while (!AllowedToStage()) { loopCounter++; //if (log != null) log.AppendLine("loop = ", loopCounter); // Calculate how long each draining tank will take to drain and run for the minimum time double resourceDrainTime = double.MaxValue; PartSim partMinDrain = null; foreach (PartSim partSim in drainingParts) { double time = partSim.TimeToDrainResource(log); if (time < resourceDrainTime) { resourceDrainTime = time; partMinDrain = partSim; } } if (log != null) log.Append("Drain time = ", resourceDrainTime, " (", partMinDrain.name) .AppendLine(":", partMinDrain.partId, ")"); foreach (PartSim partSim in drainingParts) { partSim.DrainResources(resourceDrainTime, log); } // Get the mass after draining stepEndMass = ShipMass; stageTime += resourceDrainTime; double stepEndTWR = totalStageThrust / (stepEndMass * gravity); /*if (log != null) { log.AppendLine("After drain mass = ", stepEndMass); log.AppendLine("currentThrust = ", totalStageThrust); log.AppendLine("currentTWR = ", stepEndTWR); }*/ if (stepEndTWR > stage.maxThrustToWeight) { stage.maxThrustToWeight = stepEndTWR; } //if (log != null) log.AppendLine("newMaxTWR = ", stage.maxThrustToWeight); // 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) { vecStageDeltaV += vecThrust * (float)((currentisp * Units.GRAVITY * Math.Log(stepStartMass / stepEndMass)) / simpleTotalThrust); } // Update the active engines and resource drains for the next step UpdateResourceDrains(); // Recalculate the current thrust and isp for the next step CalculateThrustAndISP(); // Check if we actually changed anything if (stepStartMass == stepEndMass) { //MonoBehaviour.print("No change in mass"); break; } // Check to stop rampant looping if (loopCounter == 1000) { if (log != null) { log.AppendLine("exceeded loop count"); log.AppendLine("stageStartMass = " + stageStartMass); log.AppendLine("stepStartMass = " + stepStartMass); log.AppendLine("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 // Store the magnitude of the deltaV vector stage.deltaV = vecStageDeltaV.magnitude; stage.resourceMass = stageStartMass - stepEndMass; // Recalculate effective stage isp from the stage deltaV (flip the standard deltaV calculation around) // Note: If the mass doesn't change then this is a divide by zero if (stageStartMass != stepStartMass) { stage.isp = stage.deltaV / (Units.GRAVITY * Math.Log(stageStartMass / stepStartMass)); } else { stage.isp = 0; } // 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 = doingCurrent ? -1 : currentStage; // Set the stage number to -1 if doing current engines stage.totalPartCount = allParts.Count; stage.maxMach = maxMach; stages[currentStage] = stage; // Now activate the next stage currentStage--; doingCurrent = false; if (log != null) { // Log how long the stage took _timer.Stop(); log.AppendLine("Simulating stage took ", _timer.ElapsedMilliseconds, "ms"); stage.Dump(log); _timer.Reset(); _timer.Start(); } // Activate the next stage ActivateStage(); if (log != null) { // Log how long it took to activate _timer.Stop(); log.AppendLine("ActivateStage took ", _timer.ElapsedMilliseconds, "ms"); } } // 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].totalDeltaV += stages[j].deltaV; stages[i].totalTime += stages[j].time; stages[i].partCount = i > 0 ? stages[i].totalPartCount - stages[i - 1].totalPartCount : stages[i].totalPartCount; } // 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; } } FreePooledObject(); _timer.Stop(); if (log != null) { log.AppendLine("RunSimulation: ", _timer.ElapsedMilliseconds, "ms"); log.Flush(); } log = null; return stages; }
// 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(LogMsg _log, List<Part> parts, double theGravity, double theAtmosphere = 0, double theMach = 0, bool dumpTree = false, bool vectoredThrust = false, bool fullThrust = false) { log = _log; if (log != null) log.AppendLine("PrepareSimulation started"); _timer.Reset(); _timer.Start(); // Store the parameters in members for ease of access in other functions partList = parts; gravity = theGravity; atmosphere = theAtmosphere; mach = theMach; lastStage = StageManager.LastStage; maxMach = 1.0f; if (log != null) log.AppendLine("lastStage = ", lastStage); // Clear the lists for our simulation parts allParts.Clear(); allFuelLines.Clear(); drainingParts.Clear(); allEngines.Clear(); activeEngines.Clear(); drainingResources.Clear(); // A dictionary for fast lookup of Part->PartSim during the preparation phase partSimLookup.Clear(); if (partList.Count > 0 && partList[0].vessel != null) { vesselName = partList[0].vessel.vesselName; vesselType = partList[0].vessel.vesselType; } // First we create a PartSim for each Part (giving each a unique id) int partId = 1; for (int i = 0; i < partList.Count; ++i) { Part part = partList[i]; // If the part is already in the lookup dictionary then log it and skip to the next part if (partSimLookup.ContainsKey(part)) { if (log != null) log.AppendLine("Part ", part.name, " appears in vessel list more than once"); continue; } // Create the PartSim PartSim partSim = PartSim.New(part, partId, atmosphere, log); // Add it to the Part lookup dictionary and the necessary lists partSimLookup.Add(part, partSim); allParts.Add(partSim); if (partSim.isFuelLine) { allFuelLines.Add(partSim); } if (partSim.isEngine) { partSim.CreateEngineSims(allEngines, atmosphere, mach, vectoredThrust, fullThrust, log); } partId++; } for (int i = 0; i < allEngines.Count; ++i) { maxMach = Mathf.Max(maxMach, allEngines[i].maxMach); } UpdateActiveEngines(); // Now that all the PartSims have been created we can do any set up that needs access to other parts // First we set up all the parent links for (int i = 0; i < allParts.Count; i++) { PartSim partSim = allParts[i]; partSim.SetupParent(partSimLookup, log); } // Then, in the VAB/SPH, we add the parent of each fuel line to the fuelTargets list of their targets if (HighLogic.LoadedSceneIsEditor) { for (int i = 0; i < allFuelLines.Count; ++i) { PartSim partSim = allFuelLines[i]; CModuleFuelLine fuelLine = partSim.part.GetModule<CModuleFuelLine>(); if (fuelLine.target != null) { PartSim targetSim; if (partSimLookup.TryGetValue(fuelLine.target, out targetSim)) { if (log != null) log.AppendLine("Fuel line target is ", targetSim.name, ":", targetSim.partId); targetSim.fuelTargets.Add(partSim.parent); } else { if (log != null) log.AppendLine("No PartSim for fuel line target (", partSim.part.partInfo.name, ")"); } } else { if (log != null) log.AppendLine("Fuel line target is null"); } } } if (log != null) log.AppendLine("SetupAttachNodes and count stages"); for (int i = 0; i < allParts.Count; ++i) { PartSim partSim = allParts[i]; partSim.SetupAttachNodes(partSimLookup, log); if (partSim.decoupledInStage >= lastStage) { lastStage = partSim.decoupledInStage + 1; } } // And finally release the Part references from all the PartSims if (log != null) log.AppendLine("ReleaseParts"); for (int i = 0; i < allParts.Count; ++i) { allParts[i].ReleasePart(); } // And dereference the core's part list partList = null; _timer.Stop(); if (log != null) { log.AppendLine("PrepareSimulation: ", _timer.ElapsedMilliseconds, "ms"); log.Flush(); } Dump(); log = null; return true; }
public void CreateEngineSims(List <EngineSim> allEngines, double atmosphere, double mach, bool vectoredThrust, bool fullThrust, LogMsg log) { if (log != null) { log.buf.AppendLine("CreateEngineSims for " + this.name); for (int i = 0; i < this.part.Modules.Count; i++) { PartModule partMod = this.part.Modules[i]; log.buf.AppendLine("Module: " + partMod.moduleName); } } if (hasMultiModeEngine) { // A multi-mode engine has multiple ModuleEngines but only one is active at any point // The mode of the engine is the engineID of the ModuleEngines that is active string mode = part.GetModule <MultiModeEngine>().mode; List <ModuleEngines> engines = part.GetModules <ModuleEngines>(); for (int i = 0; i < engines.Count; ++i) { ModuleEngines engine = engines[i]; if (engine.engineID == mode) { if (log != null) { log.buf.AppendLine("Module: " + engine.moduleName); } EngineSim engineSim = EngineSim.New( this, engine, atmosphere, (float)mach, vectoredThrust, fullThrust, log); 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); } EngineSim engineSim = EngineSim.New( this, engine, atmosphere, (float)mach, vectoredThrust, fullThrust, log); allEngines.Add(engineSim); } } if (log != null) { log.Flush(); } }
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); for (int i = 0; i < this.part.Modules.Count; i++) { PartModule partMod = this.part.Modules[i]; 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> enginesFx = this.part.GetModules <ModuleEnginesFX>(); for (int i = 0; i < enginesFx.Count; i++) { ModuleEnginesFX engine = enginesFx[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.New( this, atmosphere, 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.g, engine.throttleLocked || fullThrust, engine.propellants, engine.isOperational, correctThrust, engine.thrustTransforms); allEngines.Add(engineSim); } } } else { if (this.hasModuleEngines) { List <ModuleEngines> engines = this.part.GetModules <ModuleEngines>(); // only place that still allocate some memory 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.New( this, atmosphere, 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.g, engine.throttleLocked || fullThrust, engine.propellants, engine.isOperational, correctThrust, engine.thrustTransforms); allEngines.Add(engineSim); } } } if (log != null) { log.Flush(); } }