// This function activates the next stage // currentStage must be updated before calling this function private void ActivateStage() { // Build a set of all the parts that will be decoupled decoupledParts.Clear(); for (int i = 0; i < allParts.Count; ++i) { PartSim partSim = allParts[i]; if (partSim.decoupledInStage >= this.currentStage) { decoupledParts.Add(partSim); } } foreach (PartSim partSim in decoupledParts) { // Remove it from the all parts list this.allParts.Remove(partSim); partSim.Release(); if (partSim.isEngine) { // If it is an engine then loop through all the engine modules and remove all the ones from this engine part for (int i = this.allEngines.Count - 1; i >= 0; i--) { EngineSim engine = this.allEngines[i]; if (engine.partSim == partSim) { this.allEngines.RemoveAt(i); engine.Release(); } } } // If it is a fuel line then remove it from the list of all fuel lines if (partSim.isFuelLine) { this.allFuelLines.Remove(partSim); } } // Loop through all the (remaining) parts for (int i = 0; i < allParts.Count; ++i) { // Ask the part to remove all the parts that are decoupled allParts[i].RemoveAttachedParts(decoupledParts); } // Now we loop through all the engines and activate those that are ignited in this stage for (int i = 0; i < allEngines.Count; ++i) { EngineSim engine = allEngines[i]; if (engine.partSim.inverseStage == this.currentStage) { engine.isActive = true; } } }
// 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(); } }
// This function simply rebuilds the active engines by testing the isActive flag of all the engines private void UpdateActiveEngines() { this.activeEngines.Clear(); for (int i = 0; i < this.allEngines.Count; i++) { EngineSim engine = this.allEngines[i]; if (engine.isActive) { this.activeEngines.Add(engine); } } }
// This function simply rebuilds the active engines by testing the isActive flag of all the engines private void UpdateActiveEngines() { activeEngines.Clear(); for (int i = 0; i < allEngines.Count; ++i) { EngineSim engine = allEngines[i]; if (engine.isActive && engine.isFlamedOut == false) { activeEngines.Add(engine); } } }
private void CalculateThrustAndISP() { // Reset all the values vecThrust = Vector3.zero; vecActualThrust = Vector3.zero; simpleTotalThrust = 0d; totalStageThrust = 0d; totalStageActualThrust = 0d; totalStageFlowRate = 0d; totalStageIspFlowRate = 0d; totalStageThrustForce.Reset(); // Loop through all the active engines totalling the thrust, actual thrust and mass flow rates // The thrust is totalled as vectors for (int i = 0; i < activeEngines.Count; ++i) { EngineSim engine = activeEngines[i]; simpleTotalThrust += engine.thrust; vecThrust += ((float)engine.thrust * engine.thrustVec); vecActualThrust += ((float)engine.actualThrust * engine.thrustVec); totalStageFlowRate += engine.ResourceConsumptionsForIsp.Mass; totalStageIspFlowRate += engine.ResourceConsumptionsForIsp.Mass * engine.isp; for (int j = 0; j < engine.appliedForces.Count; ++j) { totalStageThrustForce.AddForce(engine.appliedForces[j]); } } if (log != null) { log.AppendLine("vecThrust = ", vecThrust.ToString(), " magnitude = ", vecThrust.magnitude); } totalStageThrust = vecThrust.magnitude; totalStageActualThrust = vecActualThrust.magnitude; // Calculate the effective isp at this point if (totalStageFlowRate > 0d && totalStageIspFlowRate > 0d) { currentisp = totalStageIspFlowRate / totalStageFlowRate; } else { currentisp = 0; } }
// 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 this.UpdateActiveEngines(); // Empty the draining resources set this.drainingResources.Clear(); // Reset the resource drains of all draining parts foreach (PartSim partSim in this.drainingParts) { partSim.ResourceDrains.Reset(); } // Empty the draining parts set this.drainingParts.Clear(); // Loop through all the 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(this.allParts, this.allFuelLines, this.drainingParts)) { // If it is active then add the consumed resource types to the set for (int j = 0; j < engine.ResourceConsumptions.Types.Count; ++j) { drainingResources.Add(engine.ResourceConsumptions.Types[j]); } } } // Update the active engines again to remove any engines that have no fuel supply this.UpdateActiveEngines(); if (SimManager.logOutput) { StringBuilder buffer = new StringBuilder(1024); buffer.AppendFormat("Active engines = {0:d}\n", this.activeEngines.Count); int i = 0; for (int j = 0; j < this.activeEngines.Count; j++) { EngineSim engine = this.activeEngines[j]; engine.DumpEngineToBuffer(buffer, "Engine " + (i++) + ":"); } MonoBehaviour.print(buffer); } }
private static void Reset(EngineSim engineSim) { engineSim.resourceConsumptions.Reset(); engineSim.resourceFlowModes.Reset(); engineSim.actualThrust = 0; engineSim.isActive = false; engineSim.isp = 0; for (int i = 0; i < engineSim.appliedForces.Count; i++) { engineSim.appliedForces[i].Release(); } engineSim.appliedForces.Clear(); engineSim.thrust = 0; engineSim.maxMach = 0f; }
private static void Reset(EngineSim engineSim) { engineSim.resourceConsumptions.Reset(); engineSim.actualThrust = 0; engineSim.isActive = false; engineSim.isp = 0; engineSim.partSim = null; for (int i = 0; i < engineSim.appliedForces.Count; i++) { engineSim.appliedForces[i].Release(); } engineSim.appliedForces.Clear(); engineSim.thrust = 0; engineSim.thrustVec = Vector3.zero; }
private void CalculateThrustAndISP() { // Reset all the values this.vecThrust = Vector3.zero; this.vecActualThrust = Vector3.zero; this.simpleTotalThrust = 0d; this.totalStageThrust = 0d; this.totalStageActualThrust = 0d; this.totalStageFlowRate = 0d; this.totalStageIspFlowRate = 0d; this.totalStageThrustForce.Reset(); // Loop through all the active engines totalling the thrust, actual thrust and mass flow rates // The thrust is totalled as vectors for (int i = 0; i < activeEngines.Count; ++i) { EngineSim engine = activeEngines[i]; this.simpleTotalThrust += engine.thrust; this.vecThrust += ((float)engine.thrust * engine.thrustVec); this.vecActualThrust += ((float)engine.actualThrust * engine.thrustVec); this.totalStageFlowRate += engine.ResourceConsumptions.Mass; this.totalStageIspFlowRate += engine.ResourceConsumptions.Mass * engine.isp; for (int j = 0; j < engine.appliedForces.Count; ++j) { this.totalStageThrustForce.AddForce(engine.appliedForces[j]); } } //MonoBehaviour.print("vecThrust = " + vecThrust.ToString() + " magnitude = " + vecThrust.magnitude); this.totalStageThrust = this.vecThrust.magnitude; this.totalStageActualThrust = this.vecActualThrust.magnitude; // Calculate the effective isp at this point if (this.totalStageFlowRate > 0d && this.totalStageIspFlowRate > 0d) { this.currentisp = this.totalStageIspFlowRate / this.totalStageFlowRate; } else { this.currentisp = 0; } }
public void CreateEngineSims(List <EngineSim> allEngines, double atmosphere, double mach, bool vectoredThrust, bool fullThrust, LogMsg log) { if (log != null) { log.AppendLine("CreateEngineSims for ", this.name); } List <ModuleEngines> cacheModuleEngines = part.FindModulesImplementing <ModuleEngines>(); try { if (cacheModuleEngines.Count > 0) { //find first active engine, assuming that two are never active at the same time foreach (ModuleEngines engine in cacheModuleEngines) { if (engine.isEnabled) { if (log != null) { log.AppendLine("Module: ", engine.moduleName); } EngineSim engineSim = EngineSim.New( this, engine, atmosphere, (float)mach, vectoredThrust, fullThrust, log); allEngines.Add(engineSim); } } } } catch { Debug.Log("[KER] Error Catch in CreateEngineSims"); } }
public void CreateEngineSims(List <EngineSim> allEngines, double atmosphere, double mach, bool vectoredThrust, bool fullThrust, LogMsg log) { if (log != null) { log.AppendLine("CreateEngineSims for ", this.name); } var partMods = this.part.Modules; var numMods = partMods.Count; 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 (are?) active string mode = part.GetModule <MultiModeEngine>().mode; for (int i = 0; i < numMods; i++) { //log.AppendLine("Module: ", partMods[i].moduleName); var engine = partMods[i] as ModuleEngines; if (engine != null && engine.engineID == mode) { if (log != null) { log.AppendLine("Module: ", engine.moduleName); } EngineSim engineSim = EngineSim.New( this, engine, atmosphere, (float)mach, vectoredThrust, fullThrust, log); allEngines.Add(engineSim); } } } else if (hasModuleEngines) { for (int i = 0; i < numMods; i++) { //log.AppendLine("Module: ", partMods[i].moduleName); var engine = partMods[i] as ModuleEngines; if (engine != null) { if (log != null) { log.AppendLine("Module: ", engine.moduleName); } EngineSim engineSim = EngineSim.New( this, engine, atmosphere, (float)mach, vectoredThrust, fullThrust, log); allEngines.Add(engineSim); } } } }
// This function works out if it is time to stage private bool AllowedToStage() { if (log != null) { log.AppendLine("AllowedToStage") .AppendLine("currentStage = ", currentStage); } if (activeEngines.Count > 0) { for (int i = 0; i < dontStageParts.Count; ++i) { PartSim partSim = dontStageParts[i]; if (log != null) { partSim.DumpPartToLog(log, "Testing: "); } //if (log != null) log.AppendLine("isSepratron = ", partSim.isSepratron ? "true" : "false"); if (!partSim.isSepratron && !partSim.EmptyOf(drainingResources)) { if (log != null) { partSim.DumpPartToLog(log, "Decoupled part not empty => false: "); } return(false); } if (partSim.isEngine) { for (int j = 0; j < activeEngines.Count; ++j) { EngineSim engine = activeEngines[j]; if (engine.dontDecoupleActive && engine.partSim == partSim) { if (log != null) { partSim.DumpPartToLog(log, "Decoupled part is active engine => false: "); } return(false); } } } } } if (currentStage == 0 && doingCurrent) { if (log != null) { log.AppendLine("Current stage == 0 && doingCurrent => false"); } return(false); } if (log != null) { log.AppendLine("Returning true"); } return(true); }
// 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); }
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(); } }
public static EngineSim New(PartSim theEngine, double atmosphere, double machNumber, float maxFuelFlow, float minFuelFlow, float thrustPercentage, Vector3 vecThrust, FloatCurve atmosphereCurve, bool atmChangeFlow, FloatCurve atmCurve, FloatCurve velCurve, float currentThrottle, float IspG, bool throttleLocked, List <Propellant> propellants, bool active, bool correctThrust, List <Transform> thrustTransforms) { EngineSim engineSim = pool.Borrow(); StringBuilder buffer = null; //MonoBehaviour.print("Create EngineSim for " + theEngine.name); //MonoBehaviour.print("maxThrust = " + maxThrust); //MonoBehaviour.print("minThrust = " + minThrust); //MonoBehaviour.print("thrustPercentage = " + thrustPercentage); //MonoBehaviour.print("requestedThrust = " + requestedThrust); //MonoBehaviour.print("velocity = " + velocity); engineSim.partSim = theEngine; engineSim.isActive = active; //engineSim.thrust = (maxThrust - minThrust) * (thrustPercentage / 100f) + minThrust; //MonoBehaviour.print("thrust = " + thrust); engineSim.thrustVec = vecThrust; double flowRate = 0d; if (engineSim.partSim.hasVessel) { //MonoBehaviour.print("hasVessel is true"); //engineSim.actualThrust = engineSim.isActive ? resultingThrust : 0.0; engineSim.isp = atmosphereCurve.Evaluate((float)atmosphere); //if (engineSim.isp == 0d) //{ // MonoBehaviour.print("Isp at " + engineSim.partSim.part.staticPressureAtm + " is zero. Flow rate will be NaN"); //} //if (correctThrust && realIsp == 0) //{ // float ispsl = atmosphereCurve.Evaluate(0); // if (ispsl != 0) // { // engineSim.thrust = engineSim.thrust * engineSim.isp / ispsl; // } // else // { // MonoBehaviour.print("Isp at sea level is zero. Unable to correct thrust."); // } // //MonoBehaviour.print("corrected thrust = " + thrust); //} //if (velocityCurve != null) //{ // engineSim.thrust *= velocityCurve.Evaluate((float)velocity); // //MonoBehaviour.print("thrust at velocity = " + thrust); //} float multiplier = 1; if (atmChangeFlow) { multiplier = (float)(engineSim.partSim.part.atmDensity / 1.225); if (atmCurve != null) { multiplier = atmCurve.Evaluate(multiplier); } } if (velCurve != null) { multiplier *= velCurve.Evaluate((float)machNumber); } if (throttleLocked) { //MonoBehaviour.print("throttleLocked is true"); //flowRate = engineSim.thrust / (engineSim.isp * 9.82); flowRate = Mathf.Lerp(minFuelFlow, maxFuelFlow, (thrustPercentage / 100f)) * multiplier; } else { if (engineSim.partSim.isLanded) { //MonoBehaviour.print("partSim.isLanded is true, mainThrottle = " + FlightInputHandler.state.mainThrottle); flowRate = Mathf.Lerp(minFuelFlow, maxFuelFlow, FlightInputHandler.state.mainThrottle * (thrustPercentage / 100f)) * multiplier; } else { if (currentThrottle > 0) { //MonoBehaviour.print("requestedThrust > 0"); //flowRate = requestedThrust / (engineSim.isp * 9.82) * multiplier; flowRate = Mathf.Lerp(minFuelFlow, maxFuelFlow, currentThrottle * (thrustPercentage / 100f)) * multiplier; } else { //MonoBehaviour.print("requestedThrust <= 0"); flowRate = Mathf.Lerp(minFuelFlow, maxFuelFlow, (thrustPercentage / 100f)) * multiplier; } } } } else { //MonoBehaviour.print("hasVessel is false"); engineSim.isp = atmosphereCurve.Evaluate((float)atmosphere); if (engineSim.isp == 0d) { MonoBehaviour.print("Isp at " + atmosphere + " is zero. Flow rate will be NaN"); } //if (correctThrust) //{ // float ispsl = atmosphereCurve.Evaluate(0); // if (ispsl != 0) // { // engineSim.thrust = engineSim.thrust * engineSim.isp / ispsl; // } // else // { // MonoBehaviour.print("Isp at sea level is zero. Unable to correct thrust."); // } // //MonoBehaviour.print("corrected thrust = " + thrust); //} float multiplier = 1; if (atmChangeFlow) { //multiplier = (float)(engineSim.partSim.part.atmDensity / 1.225); multiplier = (float)atmosphere; // technically wrong but about the same for my Editor need if (atmCurve != null) { multiplier = atmCurve.Evaluate(multiplier); } } if (velCurve != null) { multiplier *= velCurve.Evaluate((float)machNumber); } flowRate = Mathf.Lerp(minFuelFlow, maxFuelFlow, (thrustPercentage / 100f)) * multiplier; } if (SimManager.logOutput) { buffer = new StringBuilder(1024); buffer.AppendFormat("flowRate = {0:g6}\n", flowRate); } engineSim.thrust = flowRate * (engineSim.isp * IspG); // TODO : look into the diff between the 2 in the old code (jet real thrust vs ideal thrust ?) engineSim.actualThrust = engineSim.thrust; float flowMass = 0f; for (int i = 0; i < propellants.Count; i++) { Propellant propellant = propellants[i]; flowMass += propellant.ratio * ResourceContainer.GetResourceDensity(propellant.id); } if (SimManager.logOutput) { buffer.AppendFormat("flowMass = {0:g6}\n", flowMass); } for (int i = 0; i < propellants.Count; i++) { Propellant propellant = propellants[i]; if (propellant.name == "ElectricCharge" || propellant.name == "IntakeAir") { continue; } double consumptionRate = propellant.ratio * flowRate / flowMass; if (SimManager.logOutput) { buffer.AppendFormat( "Add consumption({0}, {1}:{2:d}) = {3:g6}\n", ResourceContainer.GetResourceName(propellant.id), theEngine.name, theEngine.partId, consumptionRate); } engineSim.resourceConsumptions.Add(propellant.id, consumptionRate); } if (SimManager.logOutput) { MonoBehaviour.print(buffer); } double thrustPerThrustTransform = engineSim.thrust / thrustTransforms.Count; for (int i = 0; i < thrustTransforms.Count; i++) { Transform thrustTransform = thrustTransforms[i]; Vector3d direction = thrustTransform.forward.normalized; Vector3d position = thrustTransform.position; AppliedForce appliedForce = AppliedForce.New(direction * thrustPerThrustTransform, position); engineSim.appliedForces.Add(appliedForce); } return(engineSim); }
// This function works out if it is time to stage private bool AllowedToStage() { StringBuilder buffer = null; if (SimManager.logOutput) { buffer = new StringBuilder(1024); buffer.AppendLine("AllowedToStage"); buffer.AppendFormat("currentStage = {0:d}\n", this.currentStage); } if (this.activeEngines.Count > 0) { for (int i = 0; i < dontStageParts.Count; ++i) { PartSim partSim = dontStageParts[i]; if (SimManager.logOutput) { partSim.DumpPartToBuffer(buffer, "Testing: "); } //buffer.AppendFormat("isSepratron = {0}\n", partSim.isSepratron ? "true" : "false"); if (!partSim.isSepratron && !partSim.EmptyOf(this.drainingResources)) { if (SimManager.logOutput) { partSim.DumpPartToBuffer(buffer, "Decoupled part not empty => false: "); MonoBehaviour.print(buffer); } return(false); } if (partSim.isEngine) { for (int j = 0; j < activeEngines.Count; ++j) { EngineSim engine = activeEngines[j]; if (engine.partSim == partSim) { if (SimManager.logOutput) { partSim.DumpPartToBuffer(buffer, "Decoupled part is active engine => false: "); MonoBehaviour.print(buffer); } return(false); } } } } } if (this.currentStage == 0 && this.doingCurrent) { if (SimManager.logOutput) { buffer.AppendLine("Current stage == 0 && doingCurrent => false"); MonoBehaviour.print(buffer); } return(false); } if (SimManager.logOutput) { buffer.AppendLine("Returning true"); MonoBehaviour.print(buffer); } 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 static EngineSim New(PartSim theEngine, double atmosphere, float machNumber, float maxFuelFlow, float minFuelFlow, float thrustPercentage, Vector3 vecThrust, FloatCurve atmosphereCurve, bool atmChangeFlow, FloatCurve atmCurve, FloatCurve velCurve, float currentThrottle, float IspG, bool throttleLocked, List <Propellant> propellants, bool active, float resultingThrust, List <Transform> thrustTransforms, LogMsg log) { EngineSim engineSim = pool.Borrow(); engineSim.isp = 0.0; engineSim.maxMach = 0.0f; engineSim.actualThrust = 0.0; engineSim.partSim = theEngine; engineSim.isActive = active; engineSim.thrustVec = vecThrust; engineSim.resourceConsumptions.Reset(); engineSim.resourceFlowModes.Reset(); engineSim.appliedForces.Clear(); double flowRate = 0.0; if (engineSim.partSim.hasVessel) { if (log != null) { log.buf.AppendLine("hasVessel is true"); } float flowModifier = GetFlowModifier(atmChangeFlow, atmCurve, engineSim.partSim.part.atmDensity, velCurve, machNumber, ref engineSim.maxMach); engineSim.isp = atmosphereCurve.Evaluate((float)atmosphere); engineSim.thrust = GetThrust(Mathf.Lerp(minFuelFlow, maxFuelFlow, GetThrustPercent(thrustPercentage)) * flowModifier, engineSim.isp); engineSim.actualThrust = engineSim.isActive ? resultingThrust : 0.0; if (log != null) { log.buf.AppendFormat("flowMod = {0:g6}\n", flowModifier); log.buf.AppendFormat("isp = {0:g6}\n", engineSim.isp); log.buf.AppendFormat("thrust = {0:g6}\n", engineSim.thrust); log.buf.AppendFormat("actual = {0:g6}\n", engineSim.actualThrust); } if (throttleLocked) { if (log != null) { log.buf.AppendLine("throttleLocked is true, using thrust for flowRate"); } flowRate = GetFlowRate(engineSim.thrust, engineSim.isp); } else { if (currentThrottle > 0.0f && engineSim.partSim.isLanded == false) { if (log != null) { log.buf.AppendLine("throttled up and not landed, using actualThrust for flowRate"); } flowRate = GetFlowRate(engineSim.actualThrust, engineSim.isp); } else { if (log != null) { log.buf.AppendLine("throttled down or landed, using thrust for flowRate"); } flowRate = GetFlowRate(engineSim.thrust, engineSim.isp); } } } else { if (log != null) { log.buf.AppendLine("hasVessel is false"); } float flowModifier = GetFlowModifier(atmChangeFlow, atmCurve, SimManager.Body.GetDensity(FlightGlobals.getStaticPressure(0, SimManager.Body), FlightGlobals.getExternalTemperature(0, SimManager.Body)), velCurve, machNumber, ref engineSim.maxMach); engineSim.isp = atmosphereCurve.Evaluate((float)atmosphere); engineSim.thrust = GetThrust(Mathf.Lerp(minFuelFlow, maxFuelFlow, GetThrustPercent(thrustPercentage)) * flowModifier, engineSim.isp); engineSim.actualThrust = 0d; if (log != null) { log.buf.AppendFormat("flowMod = {0:g6}\n", flowModifier); log.buf.AppendFormat("isp = {0:g6}\n", engineSim.isp); log.buf.AppendFormat("thrust = {0:g6}\n", engineSim.thrust); log.buf.AppendFormat("actual = {0:g6}\n", engineSim.actualThrust); } if (log != null) { log.buf.AppendLine("no vessel, using thrust for flowRate"); } flowRate = GetFlowRate(engineSim.thrust, engineSim.isp); } if (log != null) { log.buf.AppendFormat("flowRate = {0:g6}\n", flowRate); } float flowMass = 0f; for (int i = 0; i < propellants.Count; ++i) { Propellant propellant = propellants[i]; if (!propellant.ignoreForIsp) { flowMass += propellant.ratio * ResourceContainer.GetResourceDensity(propellant.id); } } if (log != null) { log.buf.AppendFormat("flowMass = {0:g6}\n", flowMass); } for (int i = 0; i < propellants.Count; ++i) { Propellant propellant = propellants[i]; if (propellant.name == "ElectricCharge" || propellant.name == "IntakeAir") { continue; } double consumptionRate = propellant.ratio * flowRate / flowMass; if (log != null) { log.buf.AppendFormat( "Add consumption({0}, {1}:{2:d}) = {3:g6}\n", ResourceContainer.GetResourceName(propellant.id), theEngine.name, theEngine.partId, consumptionRate); } engineSim.resourceConsumptions.Add(propellant.id, consumptionRate); engineSim.resourceFlowModes.Add(propellant.id, (double)propellant.GetFlowMode()); } double thrustPerThrustTransform = engineSim.thrust / thrustTransforms.Count; for (int i = 0; i < thrustTransforms.Count; i++) { Transform thrustTransform = thrustTransforms[i]; Vector3d direction = thrustTransform.forward.normalized; Vector3d position = thrustTransform.position; AppliedForce appliedForce = AppliedForce.New(direction * thrustPerThrustTransform, position); engineSim.appliedForces.Add(appliedForce); } return(engineSim); }
public static EngineSim New(PartSim theEngine, ModuleEngines engineMod, double atmosphere, float machNumber, bool vectoredThrust, bool fullThrust, LogMsg log) { float maxFuelFlow = engineMod.maxFuelFlow; float minFuelFlow = engineMod.minFuelFlow; float thrustPercentage = engineMod.thrustPercentage; List <Transform> thrustTransforms = engineMod.thrustTransforms; List <float> thrustTransformMultipliers = engineMod.thrustTransformMultipliers; Vector3 vecThrust = CalculateThrustVector(vectoredThrust ? thrustTransforms : null, vectoredThrust ? thrustTransformMultipliers : null, log); FloatCurve atmosphereCurve = engineMod.atmosphereCurve; bool atmChangeFlow = engineMod.atmChangeFlow; FloatCurve atmCurve = engineMod.useAtmCurve ? engineMod.atmCurve : null; FloatCurve velCurve = engineMod.useVelCurve ? engineMod.velCurve : null; FloatCurve thrustCurve = engineMod.useThrustCurve ? engineMod.thrustCurve : null; float currentThrottle = engineMod.currentThrottle; float IspG = engineMod.g; bool throttleLocked = engineMod.throttleLocked || fullThrust; List <Propellant> propellants = engineMod.propellants; float thrustCurveRatio = engineMod.thrustCurveRatio; foreach (Propellant p in propellants) { if (p.ignoreForThrustCurve) { continue; } double ratio = p.totalResourceAvailable / p.totalResourceCapacity; if (ratio < thrustCurveRatio) { thrustCurveRatio = (float)ratio; } } bool active = engineMod.isOperational; //I do not know if this matters. RF and stock always have finalThrust. But stock uses resultingThrust in the mass flow calculations, so keep it. float resultingThrust = SimManager.hasInstalledRealFuels ? engineMod.finalThrust : engineMod.resultingThrust; bool isFlamedOut = engineMod.flameout; EngineSim engineSim = pool.Borrow(); engineSim.isp = 0.0; engineSim.maxMach = 0.0f; engineSim.actualThrust = 0.0; engineSim.partSim = theEngine; engineSim.isActive = active; engineSim.thrustVec = vecThrust; engineSim.isFlamedOut = isFlamedOut; engineSim.resourceConsumptionsForMass.Reset(); engineSim.resourceConsumptionsForIsp.Reset(); engineSim.resourceFlowModes.Reset(); engineSim.appliedForces.Clear(); double flowRate = 0.0; if (engineSim.partSim.hasVessel) { if (log != null) { log.AppendLine("hasVessel is true"); } float flowModifier = GetFlowModifier(atmChangeFlow, atmCurve, engineSim.partSim.part.atmDensity, velCurve, machNumber, thrustCurve, thrustCurveRatio, ref engineSim.maxMach, engineMod.flowMultCap, engineMod.flowMultCapSharpness); engineSim.isp = atmosphereCurve.Evaluate((float)atmosphere); engineSim.thrust = GetThrust(Mathf.Lerp(minFuelFlow, maxFuelFlow, GetThrustPercent(thrustPercentage)) * flowModifier, engineSim.isp); engineSim.actualThrust = engineSim.isActive ? resultingThrust : 0.0; if (log != null) { log.buf.AppendFormat("flowMod = {0:g6}\n", flowModifier); log.buf.AppendFormat("isp = {0:g6}\n", engineSim.isp); log.buf.AppendFormat("thrust = {0:g6}\n", engineSim.thrust); log.buf.AppendFormat("actual = {0:g6}\n", engineSim.actualThrust); log.buf.AppendFormat("final = {0:g6}\n", engineMod.finalThrust); log.buf.AppendFormat("resulting = {0:g6}\n", engineMod.resultingThrust); } if (throttleLocked) { if (log != null) { log.AppendLine("throttleLocked is true, using thrust for flowRate"); } flowRate = GetFlowRate(engineSim.thrust, engineSim.isp); } else { if (currentThrottle > 0.0f && engineSim.partSim.isLanded == false) { if (log != null) { log.AppendLine("throttled up and not landed, using actualThrust for flowRate"); } flowRate = GetFlowRate(engineSim.actualThrust, engineSim.isp); } else { if (log != null) { log.AppendLine("throttled down or landed, using thrust for flowRate"); } flowRate = GetFlowRate(engineSim.thrust, engineSim.isp); } } } else { if (log != null) { log.buf.AppendLine("hasVessel is false"); } float flowModifier = GetFlowModifier(atmChangeFlow, atmCurve, CelestialBodies.SelectedBody.GetDensity(BuildAdvanced.Altitude), velCurve, machNumber, thrustCurve, thrustCurveRatio, ref engineSim.maxMach, engineMod.flowMultCap, engineMod.flowMultCapSharpness); engineSim.isp = atmosphereCurve.Evaluate((float)atmosphere); engineSim.thrust = GetThrust(Mathf.Lerp(minFuelFlow, maxFuelFlow, GetThrustPercent(thrustPercentage)) * flowModifier, engineSim.isp); engineSim.actualThrust = 0d; if (log != null) { log.buf.AppendFormat("flowMod = {0:g6}\n", flowModifier); log.buf.AppendFormat("isp = {0:g6}\n", engineSim.isp); log.buf.AppendFormat("thrust = {0:g6}\n", engineSim.thrust); log.buf.AppendFormat("actual = {0:g6}\n", engineSim.actualThrust); log.AppendLine("no vessel, using thrust for flowRate"); } flowRate = GetFlowRate(engineSim.thrust, engineSim.isp); } if (log != null) { log.buf.AppendFormat("flowRate = {0:g6}\n", flowRate); } float flowMass = 0f; for (int i = 0; i < propellants.Count; ++i) { Propellant propellant = propellants[i]; if (propellant.name == "ElectricCharge" || propellant.name == "IntakeAir") { continue; } flowMass += propellant.ratio * ResourceContainer.GetResourceDensity(propellant.id); } if (log != null) { log.buf.AppendFormat("flowMass = {0:g6}\n", flowMass); } for (int i = 0; i < propellants.Count; ++i) { Propellant propellant = propellants[i]; if (propellant.name == "ElectricCharge" || propellant.name == "IntakeAir") { continue; } double consumptionRate = propellant.ratio * flowRate / flowMass; if (log != null) { log.buf.AppendFormat( "Add consumption({0}, {1}:{2:d}) = {3:g6}\n", ResourceContainer.GetResourceName(propellant.id), theEngine.name, theEngine.partId, consumptionRate); } // Add all for mass engineSim.resourceConsumptionsForMass.Add(propellant.id, consumptionRate); if (!propellant.ignoreForIsp) { engineSim.resourceConsumptionsForIsp.Add(propellant.id, consumptionRate); } engineSim.resourceFlowModes.Add(propellant.id, (double)propellant.GetFlowMode()); } for (int i = 0; i < thrustTransforms.Count; i++) { Transform thrustTransform = thrustTransforms[i]; Vector3d direction = thrustTransform.forward.normalized; Vector3d position = thrustTransform.position; AppliedForce appliedForce = AppliedForce.New(direction * engineSim.thrust * thrustTransformMultipliers[i], position); engineSim.appliedForces.Add(appliedForce); } return(engineSim); }
// This function runs the simulation and returns a newly created array of Stage objects public Stage[] RunSimulation() { if (SimManager.logOutput) { MonoBehaviour.print("RunSimulation started"); } this._timer.Start(); LogMsg log = null; if (SimManager.logOutput) { log = new LogMsg(); } // Start with the last stage to simulate // (this is in a member variable so it can be accessed by AllowedToStage and ActivateStage) this.currentStage = this.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 < this.allEngines.Count; i++) { EngineSim engine = this.allEngines[i]; if (log != null) { log.buf.AppendLine("Testing engine mod of " + engine.partSim.name + ":" + engine.partSim.partId); } bool bActive = engine.isActive; bool bStage = (engine.partSim.inverseStage >= this.currentStage); if (log != null) { log.buf.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.buf.AppendLine("Need to do current active engines first"); } this.doingCurrent = true; } } else { if (bStage) { if (log != null) { log.buf.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 (this.doingCurrent && anyActive) { this.currentStage++; } else { this.ActivateStage(); this.doingCurrent = false; } // Create a list of lists of PartSims that prevent decoupling this.BuildDontStageLists(log); if (log != null) { log.Flush(); } // Create the array of stages that will be returned Stage[] stages = new Stage[this.currentStage + 1]; // Loop through the stages while (this.currentStage >= 0) { if (log != null) { log.buf.AppendLine("Simulating stage " + this.currentStage); log.buf.AppendLine("ShipMass = " + this.ShipMass); log.Flush(); this._timer.Reset(); this._timer.Start(); } // Update active engines and resource drains this.UpdateResourceDrains(); // Create the Stage object for this stage Stage stage = new Stage(); this.stageTime = 0d; this.vecStageDeltaV = Vector3.zero; this.stageStartMass = this.ShipMass; this.stageStartCom = this.ShipCom; this.stepStartMass = this.stageStartMass; this.stepEndMass = 0; this.CalculateThrustAndISP(); // Store various things in the Stage object stage.thrust = this.totalStageThrust; //MonoBehaviour.print("stage.thrust = " + stage.thrust); stage.thrustToWeight = this.totalStageThrust / (this.stageStartMass * this.gravity); stage.maxThrustToWeight = stage.thrustToWeight; //MonoBehaviour.print("StageMass = " + stageStartMass); //MonoBehaviour.print("Initial maxTWR = " + stage.maxThrustToWeight); stage.actualThrust = this.totalStageActualThrust; stage.actualThrustToWeight = this.totalStageActualThrust / (this.stageStartMass * this.gravity); // calculate torque and associates stage.maxThrustTorque = this.totalStageThrustForce.TorqueAt(this.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 = (this.stageStartCom - this.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 cost and mass of this stage and add all engines and tanks that are decoupled // in the next stage to the dontStageParts list for (int i = 0; i < this.allParts.Count; i++) { PartSim partSim = this.allParts[i]; if (partSim.decoupledInStage == this.currentStage - 1) { stage.cost += partSim.cost; stage.mass += partSim.GetStartMass(); } } this.dontStageParts = dontStagePartsLists[this.currentStage]; if (log != null) { log.buf.AppendLine("Stage setup took " + this._timer.ElapsedMilliseconds + "ms"); if (this.dontStageParts.Count > 0) { log.buf.AppendLine("Parts preventing staging:"); for (int i = 0; i < this.dontStageParts.Count; i++) { PartSim partSim = this.dontStageParts[i]; partSim.DumpPartToBuffer(log.buf, ""); } } else { log.buf.AppendLine("No parts preventing staging"); } log.Flush(); } // Now we will loop until we are allowed to stage int loopCounter = 0; while (!this.AllowedToStage()) { loopCounter++; //MonoBehaviour.print("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 this.drainingParts) { double time = partSim.TimeToDrainResource(); if (time < resourceDrainTime) { resourceDrainTime = time; partMinDrain = partSim; } } if (log != null) { MonoBehaviour.print("Drain time = " + resourceDrainTime + " (" + partMinDrain.name + ":" + partMinDrain.partId + ")"); } foreach (PartSim partSim in this.drainingParts) { partSim.DrainResources(resourceDrainTime); } // Get the mass after draining this.stepEndMass = this.ShipMass; this.stageTime += resourceDrainTime; double stepEndTWR = this.totalStageThrust / (this.stepEndMass * this.gravity); //MonoBehaviour.print("After drain mass = " + stepEndMass); //MonoBehaviour.print("currentThrust = " + totalStageThrust); //MonoBehaviour.print("currentTWR = " + stepEndTWR); if (stepEndTWR > stage.maxThrustToWeight) { stage.maxThrustToWeight = stepEndTWR; } //MonoBehaviour.print("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 && this.stepStartMass > this.stepEndMass && this.stepStartMass > 0d && this.stepEndMass > 0d) { this.vecStageDeltaV += this.vecThrust * (float)((this.currentisp * STD_GRAVITY * Math.Log(this.stepStartMass / this.stepEndMass)) / this.simpleTotalThrust); } // Update the active engines and resource drains for the next step this.UpdateResourceDrains(); // Recalculate the current thrust and isp for the next step this.CalculateThrustAndISP(); // Check if we actually changed anything if (this.stepStartMass == this.stepEndMass) { //MonoBehaviour.print("No change in mass"); break; } // Check to stop rampant looping if (loopCounter == 1000) { MonoBehaviour.print("exceeded loop count"); MonoBehaviour.print("stageStartMass = " + this.stageStartMass); MonoBehaviour.print("stepStartMass = " + this.stepStartMass); MonoBehaviour.print("StepEndMass = " + this.stepEndMass); Logger.Log("exceeded loop count"); Logger.Log("stageStartMass = " + this.stageStartMass); Logger.Log("stepStartMass = " + this.stepStartMass); Logger.Log("StepEndMass = " + this.stepEndMass); break; } // The next step starts at the mass this one ended at this.stepStartMass = this.stepEndMass; } // Store more values in the Stage object and stick it in the array // Store the magnitude of the deltaV vector stage.deltaV = this.vecStageDeltaV.magnitude; stage.resourceMass = this.stageStartMass - this.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 (this.stageStartMass != this.stepStartMass) { stage.isp = stage.deltaV / (STD_GRAVITY * Math.Log(this.stageStartMass / this.stepStartMass)); } else { stage.isp = 0; } // Zero stage time if more than a day (this should be moved into the window code) stage.time = (this.stageTime < SECONDS_PER_DAY) ? this.stageTime : 0d; stage.number = this.doingCurrent ? -1 : this.currentStage; // Set the stage number to -1 if doing current engines stage.totalPartCount = this.allParts.Count; stages[this.currentStage] = stage; // Now activate the next stage this.currentStage--; this.doingCurrent = false; if (log != null) { // Log how long the stage took this._timer.Stop(); MonoBehaviour.print("Simulating stage took " + this._timer.ElapsedMilliseconds + "ms"); stage.Dump(); this._timer.Reset(); this._timer.Start(); } // Activate the next stage this.ActivateStage(); if (log != null) { // Log how long it took to activate this._timer.Stop(); MonoBehaviour.print("ActivateStage took " + this._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].totalCost += stages[j].cost; stages[i].totalMass += stages[j].mass; 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; } } if (log != null) { this._timer.Stop(); MonoBehaviour.print("RunSimulation: " + this._timer.ElapsedMilliseconds + "ms"); } FreePooledObject(); return(stages); }