// 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.Reset(); 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 < allEngines.Count; ++i) { EngineSim engine = 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 BuildDontStageLists(log); if (log != null) { log.Flush(); } // Create the array of stages that will be returned Stage[] stages = new Stage[this.currentStage + 1]; int startStage = currentStage; // Loop through the stages while (this.currentStage >= 0) { if (log != null) { log.buf.AppendLine("Simulating stage " + this.currentStage); log.Flush(); this._timer.Reset(); this._timer.Start(); } // Update active engines and resource drains this.UpdateResourceDrains(); // Update the masses of the parts to correctly handle "no physics" parts this.stageStartMass = this.UpdatePartMasses(); if (log != null) this.allParts[0].DumpPartToBuffer(log.buf, "", this.allParts); // Create the Stage object for this stage Stage stage = new Stage(); this.stageTime = 0d; this.vecStageDeltaV = Vector3.zero; this.stageStartCom = this.ShipCom; this.stepStartMass = this.stageStartMass; this.stepEndMass = 0; this.CalculateThrustAndISP(); // Store various things in the Stage object stage.thrust = this.totalStageThrust; if (log != null) log.buf.AppendLine("stage.thrust = " + stage.thrust); stage.thrustToWeight = this.totalStageThrust / (this.stageStartMass * this.gravity); stage.maxThrustToWeight = stage.thrustToWeight; if (log != null) log.buf.AppendLine("StageMass = " + stageStartMass); if (log != null) log.buf.AppendLine("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 total cost of the vessel at this point stage.totalCost = 0d; for (int i = 0; i < allParts.Count; ++i) { if (this.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 = this.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; } 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 * Units.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 / (Units.GRAVITY * Math.Log(this.stageStartMass / this.stepStartMass)); stage.resourceMass = this.stageStartMass - this.stepEndMass; } else { stage.isp = 0; stage.resourceMass = 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; stage.maxMach = maxMach; 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].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; }
// 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 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); }