Beispiel #1
0
        // 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;
                }
            }
        }
Beispiel #2
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
            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();
            }
        }
Beispiel #3
0
 // 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);
         }
     }
 }
Beispiel #4
0
 // 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);
         }
     }
 }
Beispiel #5
0
        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;
            }
        }
Beispiel #6
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);
            }
        }
Beispiel #7
0
 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;
 }
Beispiel #8
0
 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;
 }
Beispiel #9
0
 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;
 }
Beispiel #10
0
        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;
            }
        }
Beispiel #11
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");
            }
        }
Beispiel #12
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);
            }
            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);
                    }
                }
            }
        }
Beispiel #13
0
        // 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);
        }
Beispiel #14
0
        // 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);
        }
Beispiel #15
0
        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();
            }
        }
Beispiel #16
0
        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);
        }
Beispiel #17
0
        // 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);
        }
Beispiel #18
0
        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();
            }
        }
Beispiel #19
0
        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);
        }
Beispiel #20
0
        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);
        }
Beispiel #21
0
        // 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);
        }