Пример #1
0
 public void SetupParent(Dictionary <Part, PartSim> partSimLookup, LogMsg log)
 {
     if (part.parent != null)
     {
         parent = null;
         if (partSimLookup.TryGetValue(part.parent, out parent))
         {
             if (log != null)
             {
                 log.AppendLine("Parent part is ", parent.name, ":", parent.partId);
             }
             if (part.attachMode == AttachModes.SRF_ATTACH && part.attachRules.srfAttach && part.fuelCrossFeed && part.parent.fuelCrossFeed)
             {
                 if (log != null)
                 {
                     log.Append("Added (", name, ":", partId)
                     .AppendLine(", ", parent.name, ":", parent.partId, ") to surface mounted fuel targets.");
                 }
                 parent.surfaceMountFuelTargets.Add(this);
                 surfaceMountFuelTargets.Add(parent);
             }
         }
         else
         {
             if (log != null)
             {
                 log.AppendLine("No PartSim for parent part (", part.parent.partInfo.name, ")");
             }
         }
     }
 }
Пример #2
0
        public void DumpSourcePartSets(LogMsg log, String msg)
        {
            if (log == null)
            {
                return;
            }

            log.AppendLine("DumpSourcePartSets ", msg);
            foreach (int type in sourcePartSets.Keys)
            {
                log.AppendLine("SourcePartSet for ", ResourceContainer.GetResourceName(type));
                HashSet <PartSim> sourcePartSet = sourcePartSets[type];
                if (sourcePartSet.Count > 0)
                {
                    foreach (PartSim partSim in sourcePartSet)
                    {
                        log.AppendLine("Part ", partSim.name, ":", partSim.partId);
                    }
                }
                else
                {
                    log.AppendLine("No parts");
                }
            }
        }
Пример #3
0
        private void BuildDontStageLists(LogMsg log)
        {
            if (log != null)
            {
                log.AppendLine("Creating list with capacity of ", (currentStage + 1));
            }

            dontStagePartsLists.Clear();
            for (int i = 0; i <= currentStage; i++)
            {
                if (i < dontStagePartsLists.Count)
                {
                    dontStagePartsLists[i].Clear();
                }
                else
                {
                    dontStagePartsLists.Add(new List <PartSim>());
                }
            }

            for (int i = 0; i < allParts.Count; ++i)
            {
                PartSim partSim = allParts[i];

                if (partSim.isEngine || !partSim.resources.Empty)
                {
                    if (log != null)
                    {
                        log.AppendLine(partSim.name, ":", partSim.partId, " is engine or tank, decoupled = ", partSim.decoupledInStage);
                    }

                    if (partSim.decoupledInStage < -1 || partSim.decoupledInStage > currentStage - 1)
                    {
                        if (log != null)
                        {
                            log.AppendLine("decoupledInStage out of range");
                        }
                    }
                    else
                    {
                        dontStagePartsLists[partSim.decoupledInStage + 1].Add(partSim);
                    }
                }
            }

            for (int i = 1; i <= lastStage; i++)
            {
                if (dontStagePartsLists[i].Count == 0)
                {
                    dontStagePartsLists[i] = dontStagePartsLists[i - 1];
                }
            }
        }
Пример #4
0
        private static void CheckForMods()
        {
            hasCheckedForMods = true;

            foreach (var assembly in AssemblyLoader.loadedAssemblies)
            {
                log.AppendLine("Assembly: ", assembly.assembly);

                var name = assembly.assembly.ToString().Split(',')[0];

                if (name == "RealFuels")
                {
                    log.AppendLine("Found RealFuels mod");

                    var RF_ModuleEngineConfigs_Type = assembly.assembly.GetType("RealFuels.ModuleEngineConfigs");
                    if (RF_ModuleEngineConfigs_Type != null)
                    {
                        RF_ModuleEngineConfigs_localCorrectThrust = RF_ModuleEngineConfigs_Type.GetField("localCorrectThrust");
                    }

                    var RF_ModuleHybridEngine_Type = assembly.assembly.GetType("RealFuels.ModuleHybridEngine");
                    if (RF_ModuleHybridEngine_Type != null)
                    {
                        RF_ModuleHybridEngine_localCorrectThrust = RF_ModuleHybridEngine_Type.GetField("localCorrectThrust");
                    }

                    var RF_ModuleHybridEngines_Type = assembly.assembly.GetType("RealFuels.ModuleHybridEngines");
                    if (RF_ModuleHybridEngines_Type != null)
                    {
                        RF_ModuleHybridEngines_localCorrectThrust = RF_ModuleHybridEngines_Type.GetField("localCorrectThrust");
                    }

                    hasInstalledRealFuels = true;
                    break;
                }
                else if (name == "KerbalIspDifficultyScaler")
                {
                    log.AppendLine("Found KIDS mod");

                    var KIDS_Utils_Type = assembly.assembly.GetType("KerbalIspDifficultyScaler.KerbalIspDifficultyScalerUtils");
                    if (KIDS_Utils_Type != null)
                    {
                        KIDS_Utils_GetIspMultiplier = KIDS_Utils_Type.GetMethod("GetIspMultiplier");
                    }

                    KIDSparameters   = new object[6];
                    hasInstalledKIDS = true;
                }
            }
            log.Flush();
        }
Пример #5
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");
            }
        }
Пример #6
0
        private void CalculateThrustAndISP()
        {
            // Reset all the values
            vecThrust       = Vector3.zero;
            vecActualThrust = Vector3.zero;
            totalVectoredExhaustVelocity       = Vector3.zero;
            totalActualVectoredExhaustVelocity = Vector3.zero;
            totalExhaustVelocity       = 0;
            totalActualExhaustVelocity = 0;
            simpleTotalThrust          = 0d;
            simpleActualTotalThrust    = 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;
                simpleActualTotalThrust += engine.actualThrust;
                vecThrust       += ((float)engine.thrust * engine.thrustVec);
                vecActualThrust += ((float)engine.actualThrust * engine.thrustVec);

                totalVectoredExhaustVelocity += engine.thrustVec * (float)((engine.isp * BasicDeltaV.GRAVITY) / engine.thrust);
                totalExhaustVelocity         += totalVectoredExhaustVelocity.magnitude;

                totalActualVectoredExhaustVelocity += engine.thrustVec * (float)((engine.isp * BasicDeltaV.GRAVITY) / engine.actualThrust);
                totalActualExhaustVelocity         += totalActualVectoredExhaustVelocity.magnitude;

                totalStageFlowRate    += engine.ResourceConsumptions.Mass;
                totalStageIspFlowRate += engine.ResourceConsumptions.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;
            }
        }
Пример #7
0
        // This is a new function for STAGE_STACK_FLOW(_BALANCE)
        public void GetSourceSet(int type, bool includeSurfaceMountedParts, List <PartSim> allParts, HashSet <PartSim> visited, HashSet <PartSim> allSources, LogMsg log, String indent)
        {
            // Initial version of support for new flow mode

            // Call a modified version of the old GetSourceSet code that adds all potential sources rather than stopping the recursive scan
            // when certain conditions are met
            int priMax = int.MinValue;

            GetSourceSet_Internal(type, includeSurfaceMountedParts, allParts, visited, allSources, ref priMax, log, indent);
            if (log != null)
            {
                log.AppendLine(allSources.Count, " parts with priority of ", priMax);
            }
        }
Пример #8
0
        public bool SetResourceDrains(LogMsg log, List <PartSim> allParts, List <PartSim> allFuelLines, HashSet <PartSim> drainingParts)
        {
            //DumpSourcePartSets(log, "before clear");
            foreach (HashSet <PartSim> sourcePartSet in sourcePartSets.Values)
            {
                sourcePartSet.Clear();
            }
            //DumpSourcePartSets(log, "after clear");

            for (int index = 0; index < this.resourceConsumptionsForMass.Types.Count; index++)
            {
                int type = this.resourceConsumptionsForMass.Types[index];

                HashSet <PartSim> sourcePartSet;
                if (!sourcePartSets.TryGetValue(type, out sourcePartSet))
                {
                    sourcePartSet = new HashSet <PartSim>();
                    sourcePartSets.Add(type, sourcePartSet);
                }

                switch ((ResourceFlowMode)this.resourceFlowModes[type])
                {
                case ResourceFlowMode.NO_FLOW:
                    if (partSim.resources[type] > SimManager.RESOURCE_MIN && partSim.resourceFlowStates[type] != 0)
                    {
                        sourcePartSet.Add(partSim);
                    }
                    break;

                case ResourceFlowMode.ALL_VESSEL:
                case ResourceFlowMode.ALL_VESSEL_BALANCE:
                    for (int i = 0; i < allParts.Count; i++)
                    {
                        PartSim aPartSim = allParts[i];
                        if (aPartSim.resources[type] > SimManager.RESOURCE_MIN && aPartSim.resourceFlowStates[type] != 0)
                        {
                            sourcePartSet.Add(aPartSim);
                        }
                    }
                    break;

                case ResourceFlowMode.STAGE_PRIORITY_FLOW:
                case ResourceFlowMode.STAGE_PRIORITY_FLOW_BALANCE:

                    if (log != null)
                    {
                        log.Append("Find ", ResourceContainer.GetResourceName(type), " sources for ", partSim.name)
                        .AppendLine(":", partSim.partId);
                    }
                    foreach (HashSet <PartSim> stagePartSet in stagePartSets.Values)
                    {
                        stagePartSet.Clear();
                    }
                    var maxStage = -1;

                    for (int i = 0; i < allParts.Count; i++)
                    {
                        var aPartSim = allParts[i];
                        //if (log != null) log.Append(aPartSim.name, ":" + aPartSim.partId, " contains ", aPartSim.resources[type])
                        //                  .AppendLine((aPartSim.resourceFlowStates[type] == 0) ? " (disabled)" : "");
                        if (aPartSim.resources[type] <= SimManager.RESOURCE_MIN || aPartSim.resourceFlowStates[type] == 0)
                        {
                            continue;
                        }

                        int stage = aPartSim.inverseStage;
                        if (stage > maxStage)
                        {
                            maxStage = stage;
                        }

                        HashSet <PartSim> tempPartSet;
                        if (!stagePartSets.TryGetValue(stage, out tempPartSet))
                        {
                            tempPartSet = new HashSet <PartSim>();
                            stagePartSets.Add(stage, tempPartSet);
                        }
                        tempPartSet.Add(aPartSim);
                    }

                    for (int j = maxStage; j >= -1; j--)
                    {
                        //if (log != null) log.AppendLine("Testing stage ", j);
                        HashSet <PartSim> stagePartSet;
                        if (stagePartSets.TryGetValue(j, out stagePartSet) && stagePartSet.Count > 0)
                        {
                            //if (log != null) log.AppendLine("Not empty");
                            // We have to copy the contents of the set here rather than copying the set reference or
                            // bad things (tm) happen
                            foreach (PartSim aPartSim in stagePartSet)
                            {
                                sourcePartSet.Add(aPartSim);
                            }
                            break;
                        }
                    }
                    break;

                case ResourceFlowMode.STACK_PRIORITY_SEARCH:
                case ResourceFlowMode.STAGE_STACK_FLOW:
                case ResourceFlowMode.STAGE_STACK_FLOW_BALANCE:
                    visited.Clear();

                    if (log != null)
                    {
                        log.Append("Find ", ResourceContainer.GetResourceName(type), " sources for ", partSim.name)
                        .AppendLine(":", partSim.partId);
                    }

                    partSim.GetSourceSet(type, allParts, visited, sourcePartSet, log, "");
                    break;

                default:
                    if (log != null)
                    {
                        log.Append("SetResourceDrains(", partSim.name, ":", partSim.partId)
                        .AppendLine(") Unexpected flow type for ", ResourceContainer.GetResourceName(type), ")");
                    }
                    break;
                }

                if (log != null && sourcePartSet.Count > 0)
                {
                    log.AppendLine("Source parts for ", ResourceContainer.GetResourceName(type), ":");
                    foreach (PartSim partSim in sourcePartSet)
                    {
                        log.AppendLine(partSim.name, ":", partSim.partId);
                    }
                }

                //DumpSourcePartSets(log, "after " + ResourceContainer.GetResourceName(type));
            }

            // If we don't have sources for all the needed resources then return false without setting up any drains
            for (int i = 0; i < this.resourceConsumptionsForMass.Types.Count; i++)
            {
                int type = this.resourceConsumptionsForMass.Types[i];
                HashSet <PartSim> sourcePartSet;
                if (!sourcePartSets.TryGetValue(type, out sourcePartSet) || sourcePartSet.Count == 0)
                {
                    if (log != null)
                    {
                        log.AppendLine("No source of ", ResourceContainer.GetResourceName(type));
                    }
                    isActive = false;
                    return(false);
                }
            }

            // Now we set the drains on the members of the sets and update the draining parts set
            for (int i = 0; i < this.resourceConsumptionsForMass.Types.Count; i++)
            {
                int type = this.resourceConsumptionsForMass.Types[i];
                HashSet <PartSim> sourcePartSet = sourcePartSets[type];
                ResourceFlowMode  mode          = (ResourceFlowMode)resourceFlowModes[type];
                double            consumption   = resourceConsumptionsForMass[type];
                double            amount        = 0d;
                double            total         = 0d;
                if (mode == ResourceFlowMode.ALL_VESSEL_BALANCE ||
                    mode == ResourceFlowMode.STAGE_PRIORITY_FLOW_BALANCE ||
                    mode == ResourceFlowMode.STAGE_STACK_FLOW_BALANCE ||
                    mode == ResourceFlowMode.STACK_PRIORITY_SEARCH)
                {
                    foreach (PartSim partSim in sourcePartSet)
                    {
                        total += partSim.resources[type];
                    }
                }
                else
                {
                    amount = consumption / sourcePartSet.Count;
                }

                // Loop through the members of the set
                foreach (PartSim partSim in sourcePartSet)
                {
                    if (total != 0d)
                    {
                        amount = consumption * partSim.resources[type] / total;
                    }

                    if (log != null)
                    {
                        log.Append("Adding drain of ", amount, " ", ResourceContainer.GetResourceName(type))
                        .AppendLine(" to ", partSim.name, ":", partSim.partId);
                    }

                    partSim.resourceDrains.Add(type, amount);
                    drainingParts.Add(partSim);
                }
            }
            return(true);
        }
Пример #9
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);
                if (log != null)
                {
                    log.AppendLine("stage.thrust = ", stage.thrust);
                    log.AppendLine("StageMass = ", stageStartMass);
                    log.AppendLine("Initial maxTWR = ", stage.maxThrustToWeight);
                }

                // calculate torque and associates
                stage.maxThrustTorque = totalStageThrustForce.TorqueAt(stageStartCom).magnitude;

                // torque divided by thrust. imagine that all engines are at the end of a lever that tries to turn the ship.
                // this numerical value, in meters, would represent the length of that lever.
                double torqueLeverArmLength = (stage.thrust <= 0) ? 0 : stage.maxThrustTorque / stage.thrust;

                // how far away are the engines from the CoM, actually?
                double thrustDistance = (stageStartCom - totalStageThrustForce.GetAverageForceApplicationPoint()).magnitude;

                // the combination of the above two values gives an approximation of the offset angle.
                double sinThrustOffsetAngle = 0;
                if (thrustDistance > 1e-7) {
                    sinThrustOffsetAngle = torqueLeverArmLength / thrustDistance;
                    if (sinThrustOffsetAngle > 1) {
                        sinThrustOffsetAngle = 1;
                    }
                }

                stage.thrustOffsetAngle = Math.Asin(sinThrustOffsetAngle) * 180 / Math.PI;

                // Calculate the total cost of the vessel at this point
                stage.totalCost = 0d;
                for (int i = 0; i < allParts.Count; ++i)
                {
                    if (currentStage > allParts[i].decoupledInStage)
                        stage.totalCost += allParts[i].GetCost(currentStage);
                }

                // The total mass is simply the mass at the start of the stage
                stage.totalMass = stageStartMass;

                // If we have done a previous stage
                if (currentStage < startStage)
                {
                    // Calculate what the previous stage's mass and cost were by subtraction
                    Stage prev = stages[currentStage + 1];
                    prev.cost = prev.totalCost - stage.totalCost;
                    prev.mass = prev.totalMass - stage.totalMass;
                }

                // The above code will never run for the last stage so set those directly
                if (currentStage == 0)
                {
                    stage.cost = stage.totalCost;
                    stage.mass = stage.totalMass;
                }

                dontStageParts = dontStagePartsLists[currentStage];

                if (log != null)
                {
                    log.AppendLine("Stage setup took ", _timer.ElapsedMilliseconds, "ms");

                    if (dontStageParts.Count > 0)
                    {
                        log.AppendLine("Parts preventing staging:");
                        for (int i = 0; i < dontStageParts.Count; i++)
                        {
                            PartSim partSim = dontStageParts[i];
                            partSim.DumpPartToLog(log, "");
                        }
                    }
                    else
                    {
                        log.AppendLine("No parts preventing staging");
                    }

                    log.Flush();
                }

                // Now we will loop until we are allowed to stage
                int loopCounter = 0;
                while (!AllowedToStage())
                {
                    loopCounter++;
                    //if (log != null) log.AppendLine("loop = ", loopCounter);
                    // Calculate how long each draining tank will take to drain and run for the minimum time
                    double resourceDrainTime = double.MaxValue;
                    PartSim partMinDrain = null;
                    foreach (PartSim partSim in drainingParts)
                    {
                        double time = partSim.TimeToDrainResource(log);
                        if (time < resourceDrainTime)
                        {
                            resourceDrainTime = time;
                            partMinDrain = partSim;
                        }
                    }

                    if (log != null) log.Append("Drain time = ", resourceDrainTime, " (", partMinDrain.name)
                                        .AppendLine(":", partMinDrain.partId, ")");
                    foreach (PartSim partSim in drainingParts)
                    {
                        partSim.DrainResources(resourceDrainTime, log);
                    }

                    // Get the mass after draining
                    stepEndMass = ShipMass;
                    stageTime += resourceDrainTime;

                    double stepEndTWR = totalStageThrust / (stepEndMass * gravity);
                    /*if (log != null)
                    {
                        log.AppendLine("After drain mass = ", stepEndMass);
                        log.AppendLine("currentThrust = ", totalStageThrust);
                        log.AppendLine("currentTWR = ", stepEndTWR);
                    }*/
                    if (stepEndTWR > stage.maxThrustToWeight)
                    {
                        stage.maxThrustToWeight = stepEndTWR;
                    }

                    //if (log != null) log.AppendLine("newMaxTWR = ", stage.maxThrustToWeight);

                    // If we have drained anything and the masses make sense then add this step's deltaV to the stage total
                    if (resourceDrainTime > 0d && stepStartMass > stepEndMass && stepStartMass > 0d && stepEndMass > 0d)
                    {
                        vecStageDeltaV += vecThrust * (float)((currentisp * Units.GRAVITY * Math.Log(stepStartMass / stepEndMass)) / simpleTotalThrust);
                    }

                    // Update the active engines and resource drains for the next step
                    UpdateResourceDrains();

                    // Recalculate the current thrust and isp for the next step
                    CalculateThrustAndISP();

                    // Check if we actually changed anything
                    if (stepStartMass == stepEndMass)
                    {
                        //MonoBehaviour.print("No change in mass");
                        break;
                    }

                    // Check to stop rampant looping
                    if (loopCounter == 1000)
                    {
                        if (log != null)
                        {
                            log.AppendLine("exceeded loop count");
                            log.AppendLine("stageStartMass = " + stageStartMass);
                            log.AppendLine("stepStartMass = " + stepStartMass);
                            log.AppendLine("StepEndMass   = " + stepEndMass);
                        }
                        break;
                    }

                    // The next step starts at the mass this one ended at
                    stepStartMass = stepEndMass;
                }

                // Store more values in the Stage object and stick it in the array

                // Store the magnitude of the deltaV vector
                stage.deltaV = vecStageDeltaV.magnitude;
                stage.resourceMass = stageStartMass - stepEndMass;

                // Recalculate effective stage isp from the stage deltaV (flip the standard deltaV calculation around)
                // Note: If the mass doesn't change then this is a divide by zero
                if (stageStartMass != stepStartMass)
                {
                    stage.isp = stage.deltaV / (Units.GRAVITY * Math.Log(stageStartMass / stepStartMass));
                }
                else
                {
                    stage.isp = 0;
                }

                // Zero stage time if more than a day (this should be moved into the window code)
                stage.time = (stageTime < SECONDS_PER_DAY) ? stageTime : 0d;
                stage.number = doingCurrent ? -1 : currentStage; // Set the stage number to -1 if doing current engines
                stage.totalPartCount = allParts.Count;
                stage.maxMach = maxMach;
                stages[currentStage] = stage;

                // Now activate the next stage
                currentStage--;
                doingCurrent = false;

                if (log != null)
                {
                    // Log how long the stage took
                    _timer.Stop();
                    log.AppendLine("Simulating stage took ", _timer.ElapsedMilliseconds, "ms");
                    stage.Dump(log);
                    _timer.Reset();
                    _timer.Start();
                }

                // Activate the next stage
                ActivateStage();

                if (log != null)
                {
                    // Log how long it took to activate
                    _timer.Stop();
                    log.AppendLine("ActivateStage took ", _timer.ElapsedMilliseconds, "ms");
                }
            }

            // Now we add up the various total fields in the stages
            for (int i = 0; i < stages.Length; i++)
            {
                // For each stage we total up the cost, mass, deltaV and time for this stage and all the stages above
                for (int j = i; j >= 0; j--)
                {
                    stages[i].totalDeltaV += stages[j].deltaV;
                    stages[i].totalTime += stages[j].time;
                    stages[i].partCount = i > 0 ? stages[i].totalPartCount - stages[i - 1].totalPartCount : stages[i].totalPartCount;
                }
                // We also total up the deltaV for stage and all stages below
                for (int j = i; j < stages.Length; j++)
                {
                    stages[i].inverseTotalDeltaV += stages[j].deltaV;
                }

                // Zero the total time if the value will be huge (24 hours?) to avoid the display going weird
                // (this should be moved into the window code)
                if (stages[i].totalTime > SECONDS_PER_DAY)
                {
                    stages[i].totalTime = 0d;
                }
            }

            FreePooledObject();

            _timer.Stop();

            if (log != null)
            {
                log.AppendLine("RunSimulation: ", _timer.ElapsedMilliseconds, "ms");
                log.Flush();
            }
            log = null;

            return stages;
        }
Пример #10
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;
            float currentThrottle = engineMod.currentThrottle;
            float IspG = engineMod.g;
            bool throttleLocked = engineMod.throttleLocked || fullThrust;
            List<Propellant> propellants = engineMod.propellants;
            bool active = engineMod.isOperational;
            float resultingThrust = 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.resourceConsumptions.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, 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.AppendLine("throttleLocked is true, using thrust for flowRate");
                    flowRate = GetFlowRate(engineSim.thrust, engineSim.isp);
                }
                else
                {
                    if (currentThrottle > 0.0f && engineSim.partSim.isLanded == false)
                    {
                        // TODO: This bit doesn't work for RF engines
                        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, 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);
                    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.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());
            }

            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;
        }
Пример #11
0
        public void SetupAttachNodes(Dictionary<Part, PartSim> partSimLookup, LogMsg log)
        {
            if (log != null) log.AppendLine("SetupAttachNodes for ", name, ":", partId);

            attachNodes.Clear();

            for (int i = 0; i < part.attachNodes.Count; ++i)
            {
                AttachNode attachNode = part.attachNodes[i];

                if (log != null) log.AppendLine("AttachNode ", attachNode.id, " = ", (attachNode.attachedPart != null ? attachNode.attachedPart.partInfo.name : "null"));

                if (attachNode.attachedPart != null && attachNode.id != "Strut")
                {
                    PartSim attachedSim;
                    if (partSimLookup.TryGetValue(attachNode.attachedPart, out attachedSim))
                    {
                        if (log != null) log.AppendLine("Adding attached node ", attachedSim.name, ":", attachedSim.partId);

                        attachNodes.Add(AttachNodeSim.New(attachedSim, attachNode.id, attachNode.nodeType));
                    }
                    else
                    {
                        if (log != null) log.AppendLine("No PartSim for attached part (", attachNode.attachedPart.partInfo.name, ")");
                    }
                }
            }

            for (int i = 0; i < part.fuelLookupTargets.Count; ++i)
            {
                Part p = part.fuelLookupTargets[i];

                if (p != null)
                {
                    PartSim targetSim;
                    if (partSimLookup.TryGetValue(p, out targetSim))
                    {
                        if (log != null) log.AppendLine("Fuel target: ", targetSim.name, ":", targetSim.partId);

                        fuelTargets.Add(targetSim);
                    }
                    else
                    {
                        if (log != null) log.AppendLine("No PartSim for fuel target (", p.name, ")");
                    }
                }
            }
        }
Пример #12
0
        public void GetSourceSet_Internal(int type, bool includeSurfaceMountedParts, List<PartSim> allParts, HashSet<PartSim> visited, HashSet<PartSim> allSources, ref int priMax, LogMsg log, String indent)
        {
            if (log != null)
            {
                log.Append(indent, "GetSourceSet_Internal(", ResourceContainer.GetResourceName(type), ") for ")
                    .AppendLine(name, ":", partId);
                indent += "  ";
            }

            // Rule 1: Each part can be only visited once, If it is visited for second time in particular search it returns as is.
            if (visited.Contains(this))
            {
                if (log != null) log.Append(indent, "Nothing added, already visited (", name, ":")
                                    .AppendLine(partId + ")");
                return;
            }

            if (log != null) log.AppendLine(indent, "Adding this to visited");

            visited.Add(this);

            // Rule 2: Part performs scan on start of every fuel pipe ending in it. This scan is done in order in which pipes were installed.
            // Then it makes an union of fuel tank sets each pipe scan returned. If the resulting list is not empty, it is returned as result.
            //MonoBehaviour.print("for each fuel line");

            int lastCount = allSources.Count;

            for (int i = 0; i < this.fuelTargets.Count; i++)
            {
                PartSim partSim = this.fuelTargets[i];
                if (partSim != null)
                {
                    if (visited.Contains(partSim))
                    {
                        if (log != null) log.Append(indent, "Fuel target already visited, skipping (", partSim.name, ":")
                                            .AppendLine(partSim.partId, ")");
                    }
                    else
                    {
                        if (log != null) log.Append(indent, "Adding fuel target as source (", partSim.name, ":")
                                            .AppendLine(partSim.partId, ")");

                        partSim.GetSourceSet_Internal(type, includeSurfaceMountedParts, allParts, visited, allSources, ref priMax, log, indent);
                    }
                }
            }

            if (fuelCrossFeed)
            {
                if (includeSurfaceMountedParts)
                {
                    // check surface mounted fuel targets
                    for (int i = 0; i < surfaceMountFuelTargets.Count; i++)
                    {
                        PartSim partSim = this.surfaceMountFuelTargets[i];
                        if (partSim != null)
                        {
                            if (visited.Contains(partSim))
                            {
                                if (log != null) log.Append(indent, "Surface part already visited, skipping (", partSim.name, ":")
                                                    .AppendLine(partSim.partId, ")");
                            }
                            else
                            {
                                if (log != null) log.Append(indent, "Adding surface part as source (", partSim.name, ":")
                                                    .AppendLine(partSim.partId, ")");

                                partSim.GetSourceSet_Internal(type, includeSurfaceMountedParts, allParts, visited, allSources, ref priMax, log, indent);
                            }
                        }
                    }
                }

                lastCount = allSources.Count;
                //MonoBehaviour.print("for each attach node");
                for (int i = 0; i < this.attachNodes.Count; i++)
                {
                    AttachNodeSim attachSim = this.attachNodes[i];
                    if (attachSim.attachedPartSim != null)
                    {
                        if (attachSim.nodeType == AttachNode.NodeType.Stack)
                        {
                            if ((string.IsNullOrEmpty(noCrossFeedNodeKey) == false && attachSim.id.Contains(noCrossFeedNodeKey)) == false)
                            {
                                if (visited.Contains(attachSim.attachedPartSim))
                                {
                                    if (log != null) log.Append(indent, "Attached part already visited, skipping (", attachSim.attachedPartSim.name, ":")
                                                        .AppendLine(attachSim.attachedPartSim.partId, ")");
                                }
                                else
                                {
                                    if (log != null) log.Append(indent, "Adding attached part as source  (", attachSim.attachedPartSim.name, ":")
                                                        .AppendLine(attachSim.attachedPartSim.partId, ")");

                                    attachSim.attachedPartSim.GetSourceSet_Internal(type, includeSurfaceMountedParts, allParts, visited, allSources, ref priMax, log, indent);
                                }
                            }
                        }
                    }
                }
            }

            // If the part is fuel container for searched type of fuel (i.e. it has capability to contain that type of fuel and the fuel
            // type was not disabled) and it contains fuel, it adds itself.
            if (resources.HasType(type) && resourceFlowStates[type] > 0.0)
            {
                if (resources[type] > resRequestRemainingThreshold)
                {
                    // Get the priority of this tank
                    int pri = GetResourcePriority();
                    if (pri > priMax)
                    {
                        // This tank is higher priority than the previously added ones so we clear the sources
                        // and set the priMax to this priority
                        allSources.Clear();
                        priMax = pri;
                    }
                    // If this is the correct priority then add this to the sources
                    if (pri == priMax)
                    {
                        if (log != null) log.Append(indent, "Adding enabled tank as source (", name, ":")
                                            .AppendLine(partId, ")");

                        allSources.Add(this);
                    }
                }
            }
            else
            {
                if (log != null) log.Append(indent, "Not fuel tank or disabled. HasType = ", resources.HasType(type))
                                    .AppendLine("  FlowState = " + resourceFlowStates[type]);
            }
        }
Пример #13
0
        public void DumpPartToLog(LogMsg log, String prefix, List<PartSim> allParts = null)
        {
            if (log == null)
                return;

            log.Append(prefix);
            log.Append(name);
            log.Append(":[id = ", partId, ", decouple = ", decoupledInStage);
            log.Append(", invstage = ", inverseStage);

            //log.Append(", vesselName = '", vesselName, "'");
            //log.Append(", vesselType = ", SimManager.GetVesselTypeString(vesselType));
            //log.Append(", initialVesselName = '", initialVesselName, "'");

            log.Append(", isNoPhys = ", isNoPhysics);
            log.buf.AppendFormat(", baseMass = {0}", baseMass);
            log.buf.AppendFormat(", baseMassForCoM = {0}", baseMassForCoM);

            log.Append(", fuelCF = {0}", fuelCrossFeed);
            log.Append(", noCFNKey = '{0}'", noCrossFeedNodeKey);

            log.Append(", isSep = {0}", isSepratron);

            for (int i = 0; i < resources.Types.Count; i++)
            {
                int type = resources.Types[i];
                log.buf.AppendFormat(", {0} = {1:g6}", ResourceContainer.GetResourceName(type), resources[type]);
            }

            if (attachNodes.Count > 0)
            {
                log.Append(", attached = <");
                attachNodes[0].DumpToLog(log);
                for (int i = 1; i < attachNodes.Count; i++)
                {
                    log.Append(", ");
                    attachNodes[i].DumpToLog(log);
                }
                log.Append(">");
            }

            if (surfaceMountFuelTargets.Count > 0)
            {
                log.Append(", surface = <");
                log.Append(surfaceMountFuelTargets[0].name, ":", surfaceMountFuelTargets[0].partId);
                for (int i = 1; i < surfaceMountFuelTargets.Count; i++)
                {
                    log.Append(", ", surfaceMountFuelTargets[i].name, ":", surfaceMountFuelTargets[i].partId);
                }
                log.Append(">");
            }

            // Add more info here

            log.AppendLine("]");

            if (allParts != null)
            {
                String newPrefix = prefix + " ";
                for (int i = 0; i < allParts.Count; i++)
                {
                    PartSim partSim = allParts[i];
                    if (partSim.parent == this)
                        partSim.DumpPartToLog(log, newPrefix, allParts);
                }
            }
        }
Пример #14
0
        public static PartSim New(Part p, int id, double atmosphere, LogMsg log)
        {
            PartSim partSim = pool.Borrow();

            partSim.part = p;
            partSim.centerOfMass = p.transform.TransformPoint(p.CoMOffset);
            partSim.partId = id;
            partSim.name = p.partInfo.name;

            if (log != null) log.AppendLine("Create PartSim for ", partSim.name);

            partSim.parent = null;
            partSim.parentAttach = p.attachMode;
            partSim.fuelCrossFeed = p.fuelCrossFeed;
            partSim.noCrossFeedNodeKey = p.NoCrossFeedNodeKey;
            partSim.decoupledInStage = partSim.DecoupledInStage(p);
            partSim.isFuelLine = p.HasModule<CModuleFuelLine>();
            partSim.isSepratron = partSim.IsSepratron();
            partSim.inverseStage = p.inverseStage;
            if (log != null) log.AppendLine("inverseStage = ", partSim.inverseStage);
            partSim.resPriorityOffset = p.resourcePriorityOffset;
            partSim.resPriorityUseParentInverseStage = p.resourcePriorityUseParentInverseStage;
            partSim.resRequestRemainingThreshold = p.resourceRequestRemainingThreshold;

            partSim.baseCost = p.GetCostDry();

            if (log != null) log.AppendLine("Parent part = ", (p.parent == null ? "null" : p.parent.partInfo.name))
                                .AppendLine("physicalSignificance = ", p.physicalSignificance)
                                .AppendLine("PhysicsSignificance = ", p.PhysicsSignificance);

            // Work out if the part should have no physical significance
            // The root part is never "no physics"
            partSim.isNoPhysics = p.physicalSignificance == Part.PhysicalSignificance.NONE ||
                                    p.PhysicsSignificance == 1;

            if (p.HasModule<LaunchClamp>())
            {
                partSim.realMass = 0d;
                if (log != null) log.AppendLine("Ignoring mass of launch clamp");
            }
            else
            {
                partSim.realMass = p.mass;
                if (log != null) log.AppendLine("Using part.mass of ", p.mass);
            }

            partSim.postStageMassAdjust = 0f;
            if (log != null) log.AppendLine("Calculating postStageMassAdjust, prefabMass = ", p.prefabMass);
            int count = p.Modules.Count;
            for (int i = 0; i < count; i++)
            {
                if (log != null) log.AppendLine("Module: ", p.Modules[i].moduleName);
                IPartMassModifier partMassModifier = p.Modules[i] as IPartMassModifier;
                if (partMassModifier != null)
                {
                    if (log != null) log.AppendLine("ChangeWhen = ", partMassModifier.GetModuleMassChangeWhen());
                    if (partMassModifier.GetModuleMassChangeWhen() == ModifierChangeWhen.STAGED)
                    {
                        float preStage = partMassModifier.GetModuleMass(p.prefabMass, ModifierStagingSituation.UNSTAGED);
                        float postStage = partMassModifier.GetModuleMass(p.prefabMass, ModifierStagingSituation.STAGED);
                        if (log != null) log.AppendLine("preStage = ", preStage, "   postStage = ", postStage);
                        partSim.postStageMassAdjust += (postStage - preStage);
                    }
                }
            }
            if (log != null) log.AppendLine("postStageMassAdjust = ", partSim.postStageMassAdjust);

            for (int i = 0; i < p.Resources.Count; i++)
            {
                PartResource resource = p.Resources[i];

                // Make sure it isn't NaN as this messes up the part mass and hence most of the values
                // This can happen if a resource capacity is 0 and tweakable
                if (!Double.IsNaN(resource.amount))
                {
                    if (log != null) log.AppendLine(resource.resourceName, " = ", resource.amount);

                    partSim.resources.Add(resource.info.id, resource.amount);
                    partSim.resourceFlowStates.Add(resource.info.id, resource.flowState ? 1 : 0);
                }
                else
                {
                    if (log != null) log.AppendLine(resource.resourceName, " is NaN. Skipping.");
                }
            }

            partSim.hasVessel = (p.vessel != null);
            partSim.isLanded = partSim.hasVessel && p.vessel.Landed;
            if (partSim.hasVessel)
            {
                partSim.vesselName = p.vessel.vesselName;
                partSim.vesselType = p.vesselType;
            }
            partSim.initialVesselName = p.initialVesselName;

            partSim.hasMultiModeEngine = p.HasModule<MultiModeEngine>();
            partSim.hasModuleEngines = p.HasModule<ModuleEngines>();

            partSim.isEngine = partSim.hasMultiModeEngine || partSim.hasModuleEngines;

            if (log != null) log.AppendLine("Created ", partSim.name, ". Decoupled in stage ", partSim.decoupledInStage);

            return partSim;
        }
Пример #15
0
        public void DumpPartToLog(LogMsg log, String prefix, List <PartSim> allParts = null)
        {
            if (log == null)
            {
                return;
            }

            log.Append(prefix);
            log.Append(name);
            log.Append(":[id = ", partId, ", decouple = ", decoupledInStage);
            log.Append(", invstage = ", inverseStage);

            //log.Append(", vesselName = '", vesselName, "'");
            //log.Append(", vesselType = ", SimManager.GetVesselTypeString(vesselType));
            //log.Append(", initialVesselName = '", initialVesselName, "'");

            log.Append(", isNoPhys = ", isNoPhysics);
            log.buf.AppendFormat(", baseMass = {0}", baseMass);
            log.buf.AppendFormat(", baseMassForCoM = {0}", baseMassForCoM);

            log.Append(", fuelCF = {0}", fuelCrossFeed);
            log.Append(", noCFNKey = '{0}'", noCrossFeedNodeKey);

            log.Append(", isSep = {0}", isSepratron);

            for (int i = 0; i < resources.Types.Count; i++)
            {
                int type = resources.Types[i];
                log.buf.AppendFormat(", {0} = {1:g6}", ResourceContainer.GetResourceName(type), resources[type]);
            }

            if (attachNodes.Count > 0)
            {
                log.Append(", attached = <");
                attachNodes[0].DumpToLog(log);
                for (int i = 1; i < attachNodes.Count; i++)
                {
                    log.Append(", ");
                    attachNodes[i].DumpToLog(log);
                }
                log.Append(">");
            }

            if (surfaceMountFuelTargets.Count > 0)
            {
                log.Append(", surface = <");
                log.Append(surfaceMountFuelTargets[0].name, ":", surfaceMountFuelTargets[0].partId);
                for (int i = 1; i < surfaceMountFuelTargets.Count; i++)
                {
                    log.Append(", ", surfaceMountFuelTargets[i].name, ":", surfaceMountFuelTargets[i].partId);
                }
                log.Append(">");
            }

            // Add more info here

            log.AppendLine("]");

            if (allParts != null)
            {
                String newPrefix = prefix + " ";
                for (int i = 0; i < allParts.Count; i++)
                {
                    PartSim partSim = allParts[i];
                    if (partSim.parent == this)
                    {
                        partSim.DumpPartToLog(log, newPrefix, allParts);
                    }
                }
            }
        }
Пример #16
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);
                    }
                }
            }
        }
Пример #17
0
        public static PartSim New(Part p, int id, double atmosphere, LogMsg log)
        {
            PartSim partSim = pool.Borrow();

            partSim.part         = p;
            partSim.centerOfMass = p.transform.TransformPoint(p.CoMOffset);
            partSim.partId       = id;
            partSim.name         = p.partInfo.name;

            if (log != null)
            {
                log.AppendLine("Create PartSim for ", partSim.name);
            }

            partSim.parent             = null;
            partSim.parentAttach       = p.attachMode;
            partSim.fuelCrossFeed      = p.fuelCrossFeed;
            partSim.noCrossFeedNodeKey = p.NoCrossFeedNodeKey;
            partSim.decoupledInStage   = partSim.DecoupledInStage(p);
            partSim.isFuelLine         = p.HasModule <CModuleFuelLine>();
            partSim.isSepratron        = partSim.IsSepratron();
            partSim.inverseStage       = p.inverseStage;
            if (log != null)
            {
                log.AppendLine("inverseStage = ", partSim.inverseStage);
            }
            partSim.resPriorityOffset = p.resourcePriorityOffset;
            partSim.resPriorityUseParentInverseStage = p.resourcePriorityUseParentInverseStage;
            partSim.resRequestRemainingThreshold     = p.resourceRequestRemainingThreshold;

            partSim.baseCost = p.GetCostDry();

            if (log != null)
            {
                log.AppendLine("Parent part = ", (p.parent == null ? "null" : p.parent.partInfo.name))
                .AppendLine("physicalSignificance = ", p.physicalSignificance)
                .AppendLine("PhysicsSignificance = ", p.PhysicsSignificance);
            }

            // Work out if the part should have no physical significance
            // The root part is never "no physics"
            partSim.isNoPhysics = p.physicalSignificance == Part.PhysicalSignificance.NONE ||
                                  p.PhysicsSignificance == 1;

            if (p.HasModule <LaunchClamp>())
            {
                partSim.realMass = 0d;
                if (log != null)
                {
                    log.AppendLine("Ignoring mass of launch clamp");
                }
            }
            else
            {
                partSim.realMass = p.mass;
                if (log != null)
                {
                    log.AppendLine("Using part.mass of ", p.mass);
                }
            }

            partSim.postStageMassAdjust = 0f;
            if (log != null)
            {
                log.AppendLine("Calculating postStageMassAdjust, prefabMass = ", p.prefabMass);
            }
            int count = p.Modules.Count;

            for (int i = 0; i < count; i++)
            {
                if (log != null)
                {
                    log.AppendLine("Module: ", p.Modules[i].moduleName);
                }
                IPartMassModifier partMassModifier = p.Modules[i] as IPartMassModifier;
                if (partMassModifier != null)
                {
                    if (log != null)
                    {
                        log.AppendLine("ChangeWhen = ", partMassModifier.GetModuleMassChangeWhen());
                    }
                    if (partMassModifier.GetModuleMassChangeWhen() == ModifierChangeWhen.STAGED)
                    {
                        float preStage  = partMassModifier.GetModuleMass(p.prefabMass, ModifierStagingSituation.UNSTAGED);
                        float postStage = partMassModifier.GetModuleMass(p.prefabMass, ModifierStagingSituation.STAGED);
                        if (log != null)
                        {
                            log.AppendLine("preStage = ", preStage, "   postStage = ", postStage);
                        }
                        partSim.postStageMassAdjust += (postStage - preStage);
                    }
                }
            }
            if (log != null)
            {
                log.AppendLine("postStageMassAdjust = ", partSim.postStageMassAdjust);
            }

            for (int i = 0; i < p.Resources.Count; i++)
            {
                PartResource resource = p.Resources[i];

                // Make sure it isn't NaN as this messes up the part mass and hence most of the values
                // This can happen if a resource capacity is 0 and tweakable
                if (!Double.IsNaN(resource.amount))
                {
                    if (log != null)
                    {
                        log.AppendLine(resource.resourceName, " = ", resource.amount);
                    }

                    partSim.resources.Add(resource.info.id, resource.amount);
                    partSim.resourceFlowStates.Add(resource.info.id, resource.flowState ? 1 : 0);
                }
                else
                {
                    if (log != null)
                    {
                        log.AppendLine(resource.resourceName, " is NaN. Skipping.");
                    }
                }
            }

            partSim.hasVessel = (p.vessel != null);
            partSim.isLanded  = partSim.hasVessel && p.vessel.Landed;
            if (partSim.hasVessel)
            {
                partSim.vesselName = p.vessel.vesselName;
                partSim.vesselType = p.vesselType;
            }
            partSim.initialVesselName = p.initialVesselName;

            partSim.hasMultiModeEngine = p.HasModule <MultiModeEngine>();
            partSim.hasModuleEngines   = p.HasModule <ModuleEngines>();

            partSim.isEngine = partSim.hasMultiModeEngine || partSim.hasModuleEngines;

            if (log != null)
            {
                log.AppendLine("Created ", partSim.name, ". Decoupled in stage ", partSim.decoupledInStage);
            }

            return(partSim);
        }
Пример #18
0
        public bool SetResourceDrains(LogMsg log, List<PartSim> allParts, List<PartSim> allFuelLines, HashSet<PartSim> drainingParts)
        {
            //DumpSourcePartSets(log, "before clear");
            foreach (HashSet<PartSim> sourcePartSet in sourcePartSets.Values)
            {
                sourcePartSet.Clear();
            }
            //DumpSourcePartSets(log, "after clear");

            for (int index = 0; index < this.resourceConsumptions.Types.Count; index++)
            {
                int type = this.resourceConsumptions.Types[index];

                HashSet<PartSim> sourcePartSet;
                if (!sourcePartSets.TryGetValue(type, out sourcePartSet))
                {
                    sourcePartSet = new HashSet<PartSim>();
                    sourcePartSets.Add(type, sourcePartSet);
                }

                switch ((ResourceFlowMode)this.resourceFlowModes[type])
                {
                    case ResourceFlowMode.NO_FLOW:
                        if (partSim.resources[type] > SimManager.RESOURCE_MIN && partSim.resourceFlowStates[type] != 0)
                        {
                            sourcePartSet.Add(partSim);
                        }
                        break;

                    case ResourceFlowMode.ALL_VESSEL:
                    case ResourceFlowMode.ALL_VESSEL_BALANCE:
                        for (int i = 0; i < allParts.Count; i++)
                        {
                            PartSim aPartSim = allParts[i];
                            if (aPartSim.resources[type] > SimManager.RESOURCE_MIN && aPartSim.resourceFlowStates[type] != 0)
                            {
                                sourcePartSet.Add(aPartSim);
                            }
                        }
                        break;

                    case ResourceFlowMode.STAGE_PRIORITY_FLOW:
                    case ResourceFlowMode.STAGE_PRIORITY_FLOW_BALANCE:

                        if (log != null) log.Append("Find ", ResourceContainer.GetResourceName(type), " sources for ", partSim.name)
                                            .AppendLine(":" , partSim.partId);
                        foreach (HashSet<PartSim> stagePartSet in stagePartSets.Values)
                        {
                            stagePartSet.Clear();
                        }
                        var maxStage = -1;

                        for (int i = 0; i < allParts.Count; i++)
                        {
                            var aPartSim = allParts[i];
                            //if (log != null) log.Append(aPartSim.name, ":" + aPartSim.partId, " contains ", aPartSim.resources[type])
                            //                  .AppendLine((aPartSim.resourceFlowStates[type] == 0) ? " (disabled)" : "");
                            if (aPartSim.resources[type] <= SimManager.RESOURCE_MIN || aPartSim.resourceFlowStates[type] == 0)
                            {
                                continue;
                            }

                            int stage = aPartSim.inverseStage;
                            if (stage > maxStage)
                            {
                                maxStage = stage;
                            }

                            HashSet<PartSim> tempPartSet;
                            if (!stagePartSets.TryGetValue(stage, out tempPartSet))
                            {
                                tempPartSet = new HashSet<PartSim>();
                                stagePartSets.Add(stage, tempPartSet);
                            }
                            tempPartSet.Add(aPartSim);
                        }

                        for (int j = maxStage; j >= -1; j--)
                        {
                            //if (log != null) log.AppendLine("Testing stage ", j);
                            HashSet<PartSim> stagePartSet;
                            if (stagePartSets.TryGetValue(j, out stagePartSet) && stagePartSet.Count > 0)
                            {
                                //if (log != null) log.AppendLine("Not empty");
                                // We have to copy the contents of the set here rather than copying the set reference or
                                // bad things (tm) happen
                                foreach (PartSim aPartSim in stagePartSet)
                                {
                                    sourcePartSet.Add(aPartSim);
                                }
                                break;
                            }
                        }
                        break;

                    case ResourceFlowMode.STACK_PRIORITY_SEARCH:
                    case ResourceFlowMode.STAGE_STACK_FLOW:
                    case ResourceFlowMode.STAGE_STACK_FLOW_BALANCE:
                        visited.Clear();

                        if (log != null) log.Append("Find ", ResourceContainer.GetResourceName(type), " sources for ", partSim.name)
                                            .AppendLine(":", partSim.partId);

                        partSim.GetSourceSet(type, true, allParts, visited, sourcePartSet, log, "");
                        break;

                    default:
                        if (log != null) log.Append("SetResourceDrains(", partSim.name, ":", partSim.partId)
                                            .AppendLine(") Unexpected flow type for ", ResourceContainer.GetResourceName(type), ")");
                        break;
                }

                if (log != null && sourcePartSet.Count > 0)
                {
                    log.AppendLine("Source parts for ", ResourceContainer.GetResourceName(type), ":");
                    foreach (PartSim partSim in sourcePartSet)
                    {
                        log.AppendLine(partSim.name, ":", partSim.partId);
                    }
                }

                //DumpSourcePartSets(log, "after " + ResourceContainer.GetResourceName(type));
            }

            // If we don't have sources for all the needed resources then return false without setting up any drains
            for (int i = 0; i < this.resourceConsumptions.Types.Count; i++)
            {
                int type = this.resourceConsumptions.Types[i];
                HashSet<PartSim> sourcePartSet;
                if (!sourcePartSets.TryGetValue(type, out sourcePartSet) || sourcePartSet.Count == 0)
                {
                    if (log != null) log.AppendLine("No source of ", ResourceContainer.GetResourceName(type));
                    isActive = false;
                    return false;
                }
            }

            // Now we set the drains on the members of the sets and update the draining parts set
            for (int i = 0; i < this.resourceConsumptions.Types.Count; i++)
            {
                int type = this.resourceConsumptions.Types[i];
                HashSet<PartSim> sourcePartSet = sourcePartSets[type];
                ResourceFlowMode mode = (ResourceFlowMode)resourceFlowModes[type];
                double consumption = resourceConsumptions[type];
                double amount = 0d;
                double total = 0d;
                if (mode == ResourceFlowMode.ALL_VESSEL_BALANCE ||
                    mode == ResourceFlowMode.STAGE_PRIORITY_FLOW_BALANCE ||
                    mode == ResourceFlowMode.STAGE_STACK_FLOW_BALANCE ||
                    mode == ResourceFlowMode.STACK_PRIORITY_SEARCH)
                {
                    foreach (PartSim partSim in sourcePartSet)
                        total += partSim.resources[type];
                }
                else
                    amount = consumption / sourcePartSet.Count;

                // Loop through the members of the set
                foreach (PartSim partSim in sourcePartSet)
                {
                    if (total != 0d)
                        amount = consumption * partSim.resources[type] / total;

                    if (log != null) log.Append("Adding drain of ", amount, " ", ResourceContainer.GetResourceName(type))
                                        .AppendLine(" to ", partSim.name, ":", partSim.partId);

                    partSim.resourceDrains.Add(type, amount);
                    drainingParts.Add(partSim);
                }
            }
            return true;
        }
Пример #19
0
        private void BuildDontStageLists(LogMsg log)
        {
            if (log != null) log.AppendLine("Creating list with capacity of ", (currentStage + 1));

            dontStagePartsLists.Clear();
            for (int i = 0; i <= currentStage; i++)
            {
                if (i < dontStagePartsLists.Count)
                {
                    dontStagePartsLists[i].Clear();
                }
                else
                {
                    dontStagePartsLists.Add(new List<PartSim>());
                }
            }

            for (int i = 0; i < allParts.Count; ++i)
            {
                PartSim partSim = allParts[i];

                if (partSim.isEngine || !partSim.Resources.Empty)
                {
                    if (log != null) log.AppendLine(partSim.name, ":", partSim.partId, " is engine or tank, decoupled = ", partSim.decoupledInStage);

                    if (partSim.decoupledInStage < -1 || partSim.decoupledInStage > currentStage - 1)
                    {
                        if (log != null) log.AppendLine("decoupledInStage out of range");
                    }
                    else
                    {
                        dontStagePartsLists[partSim.decoupledInStage + 1].Add(partSim);
                    }
                }
            }

            for (int i = 1; i <= lastStage; i++)
            {
                if (dontStagePartsLists[i].Count == 0)
                {
                    dontStagePartsLists[i] = dontStagePartsLists[i - 1];
                }
            }
        }
Пример #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);
        }
Пример #21
0
        public static RCSSim New(PartSim theEngine,
                                 ModuleRCS engineMod,
                                 double atmosphere,
                                 float machNumber,
                                 bool vectoredThrust,
                                 bool fullThrust,
                                 LogMsg log)
        {
            double maxFuelFlow = engineMod.maxFuelFlow;
            // double minFuelFlow = engineMod.minFuelFlow;
            float            thrustPercentage = engineMod.thrustPercentage;
            List <Transform> thrustTransforms = engineMod.thrusterTransforms;
            //   List<float> thrustTransformMultipliers = engineMod.th
            Vector3    vecThrust       = CalculateThrustVector(vectoredThrust ? thrustTransforms : null, log);
            FloatCurve atmosphereCurve = engineMod.atmosphereCurve;
            //   bool atmChangeFlow = engineMod.at
            //   FloatCurve atmCurve = engineMod.useAtmCurve ? engineMod.atmCurve : null;
            //   FloatCurve velCurve = engineMod.useVelCurve ? engineMod.velCurve : null;
            //    float currentThrottle = engineMod.currentThrottle;
            double IspG = engineMod.G;
            //    bool throttleLocked = engineMod.throttleLocked || fullThrust;
            List <Propellant> propellants = engineMod.propellants;
            bool active = engineMod.moduleIsEnabled;
            //    float resultingThrust = engineMod.resultingThrust;
            bool isFlamedOut = engineMod.flameout;

            RCSSim 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.resourceConsumptions.Reset();
            engineSim.resourceFlowModes.Reset();
            engineSim.appliedForces.Clear();

            double flowRate = 0.0;

            if (engineSim.partSim.hasVessel)
            {
                if (log != null)
                {
                    log.AppendLine("hasVessel is true");
                }

                engineSim.isp          = atmosphereCurve.Evaluate((float)atmosphere);
                engineSim.thrust       = GetThrust(maxFuelFlow, engineSim.isp);
                engineSim.actualThrust = engineSim.isActive ? engineSim.thrust : 0.0;

                if (log != null)
                {
                    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 (true)
                {
                    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)
                    //              {
                    //// TODO: This bit doesn't work for RF engines
                    //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");
                }
                engineSim.isp          = atmosphereCurve.Evaluate((float)atmosphere);
                engineSim.thrust       = GetThrust(maxFuelFlow, engineSim.isp);
                engineSim.actualThrust = 0d;
                if (log != null)
                {
                    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.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());
            }

            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, position);
                engineSim.appliedForces.Add(appliedForce);
            }

            return(engineSim);
        }
Пример #22
0
        public void GetSourceSet_Internal(int type, bool includeSurfaceMountedParts, List <PartSim> allParts, HashSet <PartSim> visited, HashSet <PartSim> allSources, ref int priMax, LogMsg log, String indent)
        {
            if (log != null)
            {
                log.Append(indent, "GetSourceSet_Internal(", ResourceContainer.GetResourceName(type), ") for ")
                .AppendLine(name, ":", partId);
                indent += "  ";
            }

            // Rule 1: Each part can be only visited once, If it is visited for second time in particular search it returns as is.
            if (visited.Contains(this))
            {
                if (log != null)
                {
                    log.Append(indent, "Nothing added, already visited (", name, ":")
                    .AppendLine(partId + ")");
                }
                return;
            }

            if (log != null)
            {
                log.AppendLine(indent, "Adding this to visited");
            }

            visited.Add(this);

            // Rule 2: Part performs scan on start of every fuel pipe ending in it. This scan is done in order in which pipes were installed.
            // Then it makes an union of fuel tank sets each pipe scan returned. If the resulting list is not empty, it is returned as result.
            //MonoBehaviour.print("for each fuel line");

            int lastCount = allSources.Count;

            for (int i = 0; i < this.fuelTargets.Count; i++)
            {
                PartSim partSim = this.fuelTargets[i];
                if (partSim != null)
                {
                    if (visited.Contains(partSim))
                    {
                        if (log != null)
                        {
                            log.Append(indent, "Fuel target already visited, skipping (", partSim.name, ":")
                            .AppendLine(partSim.partId, ")");
                        }
                    }
                    else
                    {
                        if (log != null)
                        {
                            log.Append(indent, "Adding fuel target as source (", partSim.name, ":")
                            .AppendLine(partSim.partId, ")");
                        }

                        partSim.GetSourceSet_Internal(type, includeSurfaceMountedParts, allParts, visited, allSources, ref priMax, log, indent);
                    }
                }
            }

            if (fuelCrossFeed)
            {
                if (includeSurfaceMountedParts)
                {
                    // check surface mounted fuel targets
                    for (int i = 0; i < surfaceMountFuelTargets.Count; i++)
                    {
                        PartSim partSim = this.surfaceMountFuelTargets[i];
                        if (partSim != null)
                        {
                            if (visited.Contains(partSim))
                            {
                                if (log != null)
                                {
                                    log.Append(indent, "Surface part already visited, skipping (", partSim.name, ":")
                                    .AppendLine(partSim.partId, ")");
                                }
                            }
                            else
                            {
                                if (log != null)
                                {
                                    log.Append(indent, "Adding surface part as source (", partSim.name, ":")
                                    .AppendLine(partSim.partId, ")");
                                }

                                partSim.GetSourceSet_Internal(type, includeSurfaceMountedParts, allParts, visited, allSources, ref priMax, log, indent);
                            }
                        }
                    }
                }

                lastCount = allSources.Count;
                //MonoBehaviour.print("for each attach node");
                for (int i = 0; i < this.attachNodes.Count; i++)
                {
                    AttachNodeSim attachSim = this.attachNodes[i];
                    if (attachSim.attachedPartSim != null)
                    {
                        if (attachSim.nodeType == AttachNode.NodeType.Stack)
                        {
                            if ((string.IsNullOrEmpty(noCrossFeedNodeKey) == false && attachSim.id.Contains(noCrossFeedNodeKey)) == false)
                            {
                                if (visited.Contains(attachSim.attachedPartSim))
                                {
                                    if (log != null)
                                    {
                                        log.Append(indent, "Attached part already visited, skipping (", attachSim.attachedPartSim.name, ":")
                                        .AppendLine(attachSim.attachedPartSim.partId, ")");
                                    }
                                }
                                else
                                {
                                    if (log != null)
                                    {
                                        log.Append(indent, "Adding attached part as source  (", attachSim.attachedPartSim.name, ":")
                                        .AppendLine(attachSim.attachedPartSim.partId, ")");
                                    }

                                    attachSim.attachedPartSim.GetSourceSet_Internal(type, includeSurfaceMountedParts, allParts, visited, allSources, ref priMax, log, indent);
                                }
                            }
                        }
                    }
                }
            }

            // If the part is fuel container for searched type of fuel (i.e. it has capability to contain that type of fuel and the fuel
            // type was not disabled) and it contains fuel, it adds itself.
            if (resources.HasType(type) && resourceFlowStates[type] > 0.0)
            {
                if (resources[type] > resRequestRemainingThreshold)
                {
                    // Get the priority of this tank
                    int pri = GetResourcePriority();
                    if (pri > priMax)
                    {
                        // This tank is higher priority than the previously added ones so we clear the sources
                        // and set the priMax to this priority
                        allSources.Clear();
                        priMax = pri;
                    }
                    // If this is the correct priority then add this to the sources
                    if (pri == priMax)
                    {
                        if (log != null)
                        {
                            log.Append(indent, "Adding enabled tank as source (", name, ":")
                            .AppendLine(partId, ")");
                        }

                        allSources.Add(this);
                    }
                }
            }
            else
            {
                if (log != null)
                {
                    log.Append(indent, "Not fuel tank or disabled. HasType = ", resources.HasType(type))
                    .AppendLine("  FlowState = " + resourceFlowStates[type]);
                }
            }
        }
Пример #23
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);
                    }
                }
            }
        }
Пример #24
0
        // This is the old recursive function for STACK_PRIORITY_SEARCH
        public void GetSourceSet_Old(int type, bool includeSurfaceMountedParts, List <PartSim> allParts, HashSet <PartSim> visited, HashSet <PartSim> allSources, LogMsg log, String indent)
        {
            if (log != null)
            {
                log.Append(indent, "GetSourceSet_Old(", ResourceContainer.GetResourceName(type), ") for ")
                .AppendLine(name, ":", partId);
                indent += "  ";
            }

            // Rule 1: Each part can be only visited once, If it is visited for second time in particular search it returns as is.
            if (visited.Contains(this))
            {
                if (log != null)
                {
                    log.Append(indent, "Returning empty set, already visited (", name, ":")
                    .AppendLine(partId + ")");
                }
                return;
            }

            if (log != null)
            {
                log.AppendLine(indent, "Adding this to visited");
            }

            visited.Add(this);

            // Rule 2: Part performs scan on start of every fuel pipe ending in it. This scan is done in order in which pipes were installed.
            // Then it makes an union of fuel tank sets each pipe scan returned. If the resulting list is not empty, it is returned as result.
            //MonoBehaviour.print("for each fuel line");

            int lastCount = allSources.Count;

            for (int i = 0; i < this.fuelTargets.Count; i++)
            {
                PartSim partSim = this.fuelTargets[i];
                if (partSim != null)
                {
                    if (visited.Contains(partSim))
                    {
                        if (log != null)
                        {
                            log.Append(indent, "Fuel target already visited, skipping (", partSim.name, ":")
                            .AppendLine(partSim.partId, ")");
                        }
                    }
                    else
                    {
                        if (log != null)
                        {
                            log.Append(indent, "Adding fuel target as source (", partSim.name, ":")
                            .AppendLine(partSim.partId, ")");
                        }

                        partSim.GetSourceSet_Old(type, includeSurfaceMountedParts, allParts, visited, allSources, log, indent);
                    }
                }
            }

            // check surface mounted fuel targets
            if (includeSurfaceMountedParts)
            {
                for (int i = 0; i < surfaceMountFuelTargets.Count; i++)
                {
                    PartSim partSim = this.surfaceMountFuelTargets[i];
                    if (partSim != null)
                    {
                        if (visited.Contains(partSim))
                        {
                            if (log != null)
                            {
                                log.Append(indent, "Fuel target already visited, skipping (", partSim.name, ":")
                                .AppendLine(partSim.partId, ")");
                            }
                        }
                        else
                        {
                            if (log != null)
                            {
                                log.Append(indent, "Adding fuel target as source (", partSim.name, ":")
                                .AppendLine(partSim.partId, ")");
                            }

                            partSim.GetSourceSet_Old(type, true, allParts, visited, allSources, log, indent);
                        }
                    }
                }
            }

            if (allSources.Count > lastCount)
            {
                if (log != null)
                {
                    log.Append(indent, "Returning ", (allSources.Count - lastCount), " fuel target sources (")
                    .AppendLine(this.name, ":", this.partId, ")");
                }
                return;
            }


            // Rule 3: This rule has been removed and merged with rules 4 and 7 to fix issue with fuel tanks with disabled crossfeed

            // Rule 4: Part performs scan on each of its axially mounted neighbors.
            //  Couplers (bicoupler, tricoupler, ...) are an exception, they only scan one attach point on the single attachment side,
            //  skip the points on the side where multiple points are. [Experiment]
            //  Again, the part creates union of scan lists from each of its neighbor and if it is not empty, returns this list.
            //  The order in which mount points of a part are scanned appears to be fixed and defined by the part specification file. [Experiment]
            if (fuelCrossFeed)
            {
                lastCount = allSources.Count;
                //MonoBehaviour.print("for each attach node");
                for (int i = 0; i < this.attachNodes.Count; i++)
                {
                    AttachNodeSim attachSim = this.attachNodes[i];
                    if (attachSim.attachedPartSim != null)
                    {
                        if (attachSim.nodeType == AttachNode.NodeType.Stack)
                        {
                            if ((string.IsNullOrEmpty(noCrossFeedNodeKey) == false && attachSim.id.Contains(noCrossFeedNodeKey)) == false)
                            {
                                if (visited.Contains(attachSim.attachedPartSim))
                                {
                                    if (log != null)
                                    {
                                        log.Append(indent, "Attached part already visited, skipping (", attachSim.attachedPartSim.name, ":")
                                        .AppendLine(attachSim.attachedPartSim.partId, ")");
                                    }
                                }
                                else
                                {
                                    if (log != null)
                                    {
                                        log.Append(indent, "Adding attached part as source  (", attachSim.attachedPartSim.name, ":")
                                        .AppendLine(attachSim.attachedPartSim.partId, ")");
                                    }

                                    attachSim.attachedPartSim.GetSourceSet_Old(type, includeSurfaceMountedParts, allParts, visited, allSources, log, indent);
                                }
                            }
                        }
                    }
                }

                if (allSources.Count > lastCount)
                {
                    if (log != null)
                    {
                        log.Append(indent, "Returning " + (allSources.Count - lastCount) + " attached sources (")
                        .AppendLine(this.name, ":", this.partId, ")");
                    }
                    return;
                }
            }

            // Rule 5: If the part is fuel container for searched type of fuel (i.e. it has capability to contain that type of fuel and the fuel
            // type was not disabled [Experiment]) and it contains fuel, it returns itself.
            // Rule 6: If the part is fuel container for searched type of fuel (i.e. it has capability to contain that type of fuel and the fuel
            // type was not disabled) but it does not contain the requested fuel, it returns empty list. [Experiment]
            if (resources.HasType(type) && resourceFlowStates[type] > 0.0)
            {
                if (resources[type] > SimManager.RESOURCE_MIN)
                {
                    allSources.Add(this);

                    if (log != null)
                    {
                        log.Append(indent, "Returning enabled tank as only source (", name, ":")
                        .AppendLine(partId, ")");
                    }

                    return;
                }
            }
            else
            {
                if (log != null)
                {
                    log.Append(indent, "Not fuel tank or disabled. HasType = ", resources.HasType(type))
                    .AppendLine("  FlowState = " + resourceFlowStates[type]);
                }
            }

            // Rule 7: If the part is radially attached to another part and it is child of that part in the ship's tree structure, it scans its
            // parent and returns whatever the parent scan returned. [Experiment] [Experiment]
            if (parent != null && parentAttach == AttachModes.SRF_ATTACH)
            {
                if (fuelCrossFeed)
                {
                    if (visited.Contains(parent))
                    {
                        if (log != null)
                        {
                            log.Append(indent, "Parent part already visited, skipping (", parent.name, ":")
                            .AppendLine(parent.partId, ")");
                        }
                    }
                    else
                    {
                        lastCount = allSources.Count;
                        this.parent.GetSourceSet_Old(type, includeSurfaceMountedParts, allParts, visited, allSources, log, indent);
                        if (allSources.Count > lastCount)
                        {
                            if (log != null)
                            {
                                log.Append(indent, "Returning ", (allSources.Count - lastCount), " parent sources (")
                                .AppendLine(this.name, ":", this.partId, ")");
                            }
                            return;
                        }
                    }
                }
            }

            // Rule 8: If all preceding rules failed, part returns empty list.
            if (log != null)
            {
                log.Append(indent, "Returning empty set, no sources found (", name, ":")
                .AppendLine(partId, ")");
            }

            return;
        }
Пример #25
0
        // This is a new function for STAGE_STACK_FLOW(_BALANCE)
        public void GetSourceSet(int type, bool includeSurfaceMountedParts, List<PartSim> allParts, HashSet<PartSim> visited, HashSet<PartSim> allSources, LogMsg log, String indent)
        {
            // Initial version of support for new flow mode

            // Call a modified version of the old GetSourceSet code that adds all potential sources rather than stopping the recursive scan
            // when certain conditions are met
            int priMax = int.MinValue;
            GetSourceSet_Internal(type, includeSurfaceMountedParts, allParts, visited, allSources, ref priMax, log, indent);
            if (log != null) log.AppendLine(allSources.Count, " parts with priority of ", priMax);
        }
Пример #26
0
        public void SetupAttachNodes(Dictionary <Part, PartSim> partSimLookup, LogMsg log)
        {
            if (log != null)
            {
                log.AppendLine("SetupAttachNodes for ", name, ":", partId);
            }

            attachNodes.Clear();

            for (int i = 0; i < part.attachNodes.Count; ++i)
            {
                AttachNode attachNode = part.attachNodes[i];

                if (log != null)
                {
                    log.AppendLine("AttachNode ", attachNode.id, " = ", (attachNode.attachedPart != null ? attachNode.attachedPart.partInfo.name : "null"));
                }

                if (attachNode.attachedPart != null && attachNode.id != "Strut")
                {
                    PartSim attachedSim;
                    if (partSimLookup.TryGetValue(attachNode.attachedPart, out attachedSim))
                    {
                        if (log != null)
                        {
                            log.AppendLine("Adding attached node ", attachedSim.name, ":", attachedSim.partId);
                        }

                        attachNodes.Add(AttachNodeSim.New(attachedSim, attachNode.id, attachNode.nodeType));
                    }
                    else
                    {
                        if (log != null)
                        {
                            log.AppendLine("No PartSim for attached part (", attachNode.attachedPart.partInfo.name, ")");
                        }
                    }
                }
            }

            for (int i = 0; i < part.fuelLookupTargets.Count; ++i)
            {
                Part p = part.fuelLookupTargets[i];

                if (p != null)
                {
                    PartSim targetSim;
                    if (partSimLookup.TryGetValue(p, out targetSim))
                    {
                        if (log != null)
                        {
                            log.AppendLine("Fuel target: ", targetSim.name, ":", targetSim.partId);
                        }

                        fuelTargets.Add(targetSim);
                    }
                    else
                    {
                        if (log != null)
                        {
                            log.AppendLine("No PartSim for fuel target (", p.name, ")");
                        }
                    }
                }
            }
        }
Пример #27
0
        // This is the old recursive function for STACK_PRIORITY_SEARCH
        public void GetSourceSet_Old(int type, bool includeSurfaceMountedParts, List<PartSim> allParts, HashSet<PartSim> visited, HashSet<PartSim> allSources, LogMsg log, String indent)
        {
            if (log != null)
            {
                log.Append(indent, "GetSourceSet_Old(", ResourceContainer.GetResourceName(type), ") for ")
                    .AppendLine(name, ":", partId);
                indent += "  ";
            }

            // Rule 1: Each part can be only visited once, If it is visited for second time in particular search it returns as is.
            if (visited.Contains(this))
            {
                if (log != null) log.Append(indent, "Returning empty set, already visited (", name, ":")
                                    .AppendLine(partId + ")");
                return;
            }

            if (log != null) log.AppendLine(indent, "Adding this to visited");

            visited.Add(this);

            // Rule 2: Part performs scan on start of every fuel pipe ending in it. This scan is done in order in which pipes were installed.
            // Then it makes an union of fuel tank sets each pipe scan returned. If the resulting list is not empty, it is returned as result.
            //MonoBehaviour.print("for each fuel line");

            int lastCount = allSources.Count;

            for (int i = 0; i < this.fuelTargets.Count; i++)
            {
                PartSim partSim = this.fuelTargets[i];
                if (partSim != null)
                {
                    if (visited.Contains(partSim))
                    {
                        if (log != null) log.Append(indent, "Fuel target already visited, skipping (", partSim.name, ":")
                                            .AppendLine(partSim.partId, ")");
                    }
                    else
                    {
                        if (log != null) log.Append(indent, "Adding fuel target as source (", partSim.name, ":")
                                            .AppendLine(partSim.partId, ")");

                        partSim.GetSourceSet_Old(type, includeSurfaceMountedParts, allParts, visited, allSources, log, indent);
                    }
                }
            }

            // check surface mounted fuel targets
            if (includeSurfaceMountedParts)
            {
                for (int i = 0; i < surfaceMountFuelTargets.Count; i++)
                {
                    PartSim partSim = this.surfaceMountFuelTargets[i];
                    if (partSim != null)
                    {
                        if (visited.Contains(partSim))
                        {
                            if (log != null) log.Append(indent, "Fuel target already visited, skipping (", partSim.name, ":")
                                                .AppendLine(partSim.partId, ")");
                        }
                        else
                        {
                            if (log != null) log.Append(indent, "Adding fuel target as source (", partSim.name, ":")
                                                .AppendLine(partSim.partId, ")");

                            partSim.GetSourceSet_Old(type, true, allParts, visited, allSources, log, indent);
                        }
                    }
                }
            }

            if (allSources.Count > lastCount)
            {
                if (log != null) log.Append(indent, "Returning ", (allSources.Count - lastCount), " fuel target sources (")
                                    .AppendLine(this.name, ":", this.partId, ")");
                return;
            }

            // Rule 3: This rule has been removed and merged with rules 4 and 7 to fix issue with fuel tanks with disabled crossfeed

            // Rule 4: Part performs scan on each of its axially mounted neighbors.
            //  Couplers (bicoupler, tricoupler, ...) are an exception, they only scan one attach point on the single attachment side,
            //  skip the points on the side where multiple points are. [Experiment]
            //  Again, the part creates union of scan lists from each of its neighbor and if it is not empty, returns this list.
            //  The order in which mount points of a part are scanned appears to be fixed and defined by the part specification file. [Experiment]
            if (fuelCrossFeed)
            {
                lastCount = allSources.Count;
                //MonoBehaviour.print("for each attach node");
                for (int i = 0; i < this.attachNodes.Count; i++)
                {
                    AttachNodeSim attachSim = this.attachNodes[i];
                    if (attachSim.attachedPartSim != null)
                    {
                        if (attachSim.nodeType == AttachNode.NodeType.Stack)
                        {
                            if ((string.IsNullOrEmpty(noCrossFeedNodeKey) == false && attachSim.id.Contains(noCrossFeedNodeKey)) == false)
                            {
                                if (visited.Contains(attachSim.attachedPartSim))
                                {
                                    if (log != null) log.Append(indent, "Attached part already visited, skipping (", attachSim.attachedPartSim.name, ":")
                                                        .AppendLine(attachSim.attachedPartSim.partId, ")");
                                }
                                else
                                {
                                    if (log != null) log.Append(indent, "Adding attached part as source  (", attachSim.attachedPartSim.name, ":")
                                                        .AppendLine(attachSim.attachedPartSim.partId, ")");

                                    attachSim.attachedPartSim.GetSourceSet_Old(type, includeSurfaceMountedParts, allParts, visited, allSources, log, indent);
                                }
                            }
                        }
                    }
                }

                if (allSources.Count > lastCount)
                {
                    if (log != null) log.Append(indent, "Returning " + (allSources.Count - lastCount) + " attached sources (")
                                        .AppendLine(this.name, ":", this.partId, ")");
                    return;
                }
            }

            // Rule 5: If the part is fuel container for searched type of fuel (i.e. it has capability to contain that type of fuel and the fuel
            // type was not disabled [Experiment]) and it contains fuel, it returns itself.
            // Rule 6: If the part is fuel container for searched type of fuel (i.e. it has capability to contain that type of fuel and the fuel
            // type was not disabled) but it does not contain the requested fuel, it returns empty list. [Experiment]
            if (resources.HasType(type) && resourceFlowStates[type] > 0.0)
            {
                if (resources[type] > SimManager.RESOURCE_MIN)
                {
                    allSources.Add(this);

                    if (log != null) log.Append(indent, "Returning enabled tank as only source (", name, ":")
                                        .AppendLine(partId, ")");

                    return;
                }
            }
            else
            {
                if (log != null) log.Append(indent, "Not fuel tank or disabled. HasType = ", resources.HasType(type))
                                    .AppendLine("  FlowState = " + resourceFlowStates[type]);
            }

            // Rule 7: If the part is radially attached to another part and it is child of that part in the ship's tree structure, it scans its
            // parent and returns whatever the parent scan returned. [Experiment] [Experiment]
            if (parent != null && parentAttach == AttachModes.SRF_ATTACH)
            {
                if (fuelCrossFeed)
                {
                    if (visited.Contains(parent))
                    {
                        if (log != null) log.Append(indent, "Parent part already visited, skipping (", parent.name, ":")
                                            .AppendLine(parent.partId, ")");
                    }
                    else
                    {
                        lastCount = allSources.Count;
                        this.parent.GetSourceSet_Old(type, includeSurfaceMountedParts, allParts, visited, allSources, log, indent);
                        if (allSources.Count > lastCount)
                        {
                            if (log != null) log.Append(indent, "Returning ", (allSources.Count - lastCount), " parent sources (")
                                                .AppendLine(this.name, ":", this.partId, ")");
                            return;
                        }
                    }
                }
            }

            // Rule 8: If all preceding rules failed, part returns empty list.
            if (log != null) log.Append(indent, "Returning empty set, no sources found (", name, ":")
                                .AppendLine(partId, ")");

            return;
        }
Пример #28
0
        public bool SetPossibleResourceDrains(LogMsg log, List <PartSim> allParts, HashSet <PartSim> drainingParts)
        {
            //DumpSourcePartSets(log, "before clear");
            foreach (HashSet <PartSim> sourcePartSet in fullSourcePartSets.Values)
            {
                sourcePartSet.Clear();
            }
            //DumpSourcePartSets(log, "after clear");

            for (int index = 0; index < this.resourceConsumptions.Types.Count; index++)
            {
                int type = this.resourceConsumptions.Types[index];

                if (!fullSourcePartSets.TryGetValue(type, out HashSet <PartSim> sourcePartSet))
                {
                    sourcePartSet = new HashSet <PartSim>();
                    fullSourcePartSets.Add(type, sourcePartSet);
                }

                switch ((ResourceFlowMode)this.resourceFlowModes[type])
                {
                case ResourceFlowMode.NO_FLOW:
                    if (partSim.maxResources[type] > SimManager.RESOURCE_MIN)
                    {
                        sourcePartSet.Add(partSim);
                    }
                    break;

                case ResourceFlowMode.ALL_VESSEL:
                case ResourceFlowMode.ALL_VESSEL_BALANCE:
                    for (int i = 0; i < allParts.Count; i++)
                    {
                        PartSim aPartSim = allParts[i];
                        if (aPartSim.maxResources[type] > SimManager.RESOURCE_MIN)
                        {
                            sourcePartSet.Add(aPartSim);
                        }
                    }
                    break;

                case ResourceFlowMode.STAGE_PRIORITY_FLOW:
                case ResourceFlowMode.STAGE_PRIORITY_FLOW_BALANCE:
                    //All vessel ordered by stage
                    if (log != null)
                    {
                        log.Append("Find ", ResourceContainer.GetResourceName(type), " sources for ", partSim.name)
                        .AppendLine(":", partSim.partId);
                    }
                    foreach (HashSet <PartSim> stagePartSet in stagePartSets.Values)
                    {
                        stagePartSet.Clear();
                    }
                    var maxStage = -1;

                    for (int i = 0; i < allParts.Count; i++)
                    {
                        var aPartSim = allParts[i];
                        //if (log != null) log.Append(aPartSim.name, ":" + aPartSim.partId, " contains ", aPartSim.resources[type])
                        //                  .AppendLine((aPartSim.resourceFlowStates[type] == 0) ? " (disabled)" : "");
                        if (aPartSim.maxResources[type] > SimManager.RESOURCE_MIN)
                        {
                            int stage = aPartSim.inverseStage;
                            if (stage > maxStage)
                            {
                                maxStage = stage;
                            }

                            if (!stagePartSets.TryGetValue(stage, out HashSet <PartSim> tempPartSet))
                            {
                                tempPartSet = new HashSet <PartSim>();
                                stagePartSets.Add(stage, tempPartSet);
                            }
                            tempPartSet.Add(aPartSim);
                        }
                    }

                    //Add resource containers in order by stage
                    for (int j = maxStage; j >= -1; j--)
                    {
                        //if (log != null) log.AppendLine("Testing stage ", j);

                        if (stagePartSets.TryGetValue(j, out HashSet <PartSim> stagePartSet) && stagePartSet.Count > 0)
                        {
                            //if (log != null) log.AppendLine("Not empty");
                            // We have to copy the contents of the set here rather than copying the set reference or
                            // bad things (tm) happen
                            foreach (PartSim aPartSim in stagePartSet)
                            {
                                sourcePartSet.Add(aPartSim);
                            }
                            break;
                        }
                    }
                    break;

                case ResourceFlowMode.STACK_PRIORITY_SEARCH:
                case ResourceFlowMode.STAGE_STACK_FLOW:
                case ResourceFlowMode.STAGE_STACK_FLOW_BALANCE:
                    visited.Clear();
                    //Resource containers limited to current stage
                    if (log != null)
                    {
                        log.Append("Find ", ResourceContainer.GetResourceName(type), " sources for ", partSim.name)
                        .AppendLine(":", partSim.partId);
                    }

                    partSim.GetSourceSet(type, true, allParts, visited, sourcePartSet, true, log, "");
                    break;

                default:
                    if (log != null)
                    {
                        log.Append("SetResourceDrains(", partSim.name, ":", partSim.partId)
                        .AppendLine(") Unexpected flow type for ", ResourceContainer.GetResourceName(type), ")");
                    }
                    break;
                }
            }

            // If we don't have sources for all the needed resources then return false without setting up any drains
            for (int i = 0; i < this.resourceConsumptions.Types.Count; i++)
            {
                int type = this.resourceConsumptions.Types[i];

                if (!fullSourcePartSets.TryGetValue(type, out HashSet <PartSim> sourcePartSet) || sourcePartSet.Count == 0)
                {
                    if (log != null)
                    {
                        log.AppendLine("No source of ", ResourceContainer.GetResourceName(type));
                    }

                    return(false);
                }
            }

            // Now we set the drains on the members of the sets and update the draining parts set
            for (int i = 0; i < this.resourceConsumptions.Types.Count; i++)
            {
                HashSet <PartSim> sourcePartSet = fullSourcePartSets[resourceConsumptions.Types[i]];

                // Loop through the members of the set
                foreach (PartSim partSim in sourcePartSet)
                {
                    drainingParts.Add(partSim);
                }
            }

            return(true);
        }
Пример #29
0
 public void SetupParent(Dictionary<Part, PartSim> partSimLookup, LogMsg log)
 {
     if (part.parent != null)
     {
         parent = null;
         if (partSimLookup.TryGetValue(part.parent, out parent))
         {
             if (log != null) log.AppendLine("Parent part is ", parent.name, ":", parent.partId);
             if (part.attachMode == AttachModes.SRF_ATTACH && part.attachRules.srfAttach && part.fuelCrossFeed && part.parent.fuelCrossFeed)
             {
                 if (log != null) log.Append("Added (", name, ":", partId)
                                     .AppendLine(", ", parent.name, ":", parent.partId, ") to surface mounted fuel targets.");
                 parent.surfaceMountFuelTargets.Add(this);
                 surfaceMountFuelTargets.Add(parent);
             }
         }
         else
         {
             if (log != null) log.AppendLine("No PartSim for parent part (", part.parent.partInfo.name, ")");
         }
     }
 }
Пример #30
0
        public void DumpSourcePartSets(LogMsg log, String msg)
        {
            if (log == null)
                return;

            log.AppendLine("DumpSourcePartSets ", msg);
            foreach (int type in sourcePartSets.Keys)
            {
                log.AppendLine("SourcePartSet for ", ResourceContainer.GetResourceName(type));
                HashSet<PartSim> sourcePartSet = sourcePartSets[type];
                if (sourcePartSet.Count > 0)
                {
                    foreach (PartSim partSim in sourcePartSet)
                    {
                        log.AppendLine("Part ", partSim.name, ":", partSim.partId);
                    }
                }
                else
                {
                    log.AppendLine("No parts");
                }
            }
        }
Пример #31
0
        // This function prepares the simulation by creating all the necessary data structures it will
        // need during the simulation.  All required data is copied from the core game data structures
        // so that the simulation itself can be run in a background thread without having issues with
        // the core game changing the data while the simulation is running.
        public bool PrepareSimulation(LogMsg _log, List <Part> parts, double theGravity, double theAtmosphere = 0, double theMach = 0, bool dumpTree = false, bool vectoredThrust = false, bool fullThrust = false)
        {
            log = _log;
            if (log != null)
            {
                log.AppendLine("PrepareSimulation started");
            }

            _timer.Reset();
            _timer.Start();

            // Store the parameters in members for ease of access in other functions
            partList   = parts;
            gravity    = theGravity;
            atmosphere = theAtmosphere;
            mach       = theMach;
            lastStage  = StageManager.LastStage;
            maxMach    = 1.0f;
            if (log != null)
            {
                log.AppendLine("lastStage = ", lastStage);
            }

            // Clear the lists for our simulation parts
            allParts.Clear();
            allFuelLines.Clear();
            drainingParts.Clear();
            allEngines.Clear();
            activeEngines.Clear();
            drainingResources.Clear();

            // A dictionary for fast lookup of Part->PartSim during the preparation phase
            partSimLookup.Clear();

            if (partList.Count > 0 && partList[0].vessel != null)
            {
                vesselName = partList[0].vessel.vesselName;
                vesselType = partList[0].vessel.vesselType;
            }
            // First we create a PartSim for each Part (giving each a unique id)
            int partId = 1;

            for (int i = 0; i < partList.Count; ++i)
            {
                Part part = partList[i];

                // If the part is already in the lookup dictionary then log it and skip to the next part
                if (partSimLookup.ContainsKey(part))
                {
                    if (log != null)
                    {
                        log.AppendLine("Part ", part.name, " appears in vessel list more than once");
                    }
                    continue;
                }

                // Create the PartSim
                PartSim partSim = PartSim.New(part, partId, atmosphere, log);

                // Add it to the Part lookup dictionary and the necessary lists
                partSimLookup.Add(part, partSim);
                allParts.Add(partSim);

                if (partSim.isFuelLine)
                {
                    allFuelLines.Add(partSim);
                }

                if (partSim.isEngine)
                {
                    partSim.CreateEngineSims(allEngines, atmosphere, mach, vectoredThrust, fullThrust, log);
                }

                if (partSim.isRCS)
                {
                    partSim.CreateRCSSims(allRCS, atmosphere, mach, vectoredThrust, fullThrust, log);
                }

                partId++;
            }

            for (int i = 0; i < allEngines.Count; ++i)
            {
                maxMach = Mathf.Max(maxMach, allEngines[i].maxMach);
            }

            UpdateActiveEngines();

            // Now that all the PartSims have been created we can do any set up that needs access to other parts
            // First we set up all the parent links
            for (int i = 0; i < allParts.Count; i++)
            {
                PartSim partSim = allParts[i];
                partSim.SetupParent(partSimLookup, log);
            }

            // Then, in the VAB/SPH, we add the parent of each fuel line to the fuelTargets list of their targets
            if (HighLogic.LoadedSceneIsEditor)
            {
                for (int i = 0; i < allFuelLines.Count; ++i)
                {
                    PartSim partSim = allFuelLines[i];

                    CModuleFuelLine fuelLine = partSim.part.GetModule <CModuleFuelLine>();
                    if (fuelLine.target != null)
                    {
                        PartSim targetSim;
                        if (partSimLookup.TryGetValue(fuelLine.target, out targetSim))
                        {
                            if (log != null)
                            {
                                log.AppendLine("Fuel line target is ", targetSim.name, ":", targetSim.partId);
                            }

                            targetSim.fuelTargets.Add(partSim.parent);
                        }
                        else
                        {
                            if (log != null)
                            {
                                log.AppendLine("No PartSim for fuel line target (", partSim.part.partInfo.name, ")");
                            }
                        }
                    }
                    else
                    {
                        if (log != null)
                        {
                            log.AppendLine("Fuel line target is null");
                        }
                    }
                }
            }

            if (log != null)
            {
                log.AppendLine("SetupAttachNodes and count stages");
            }
            for (int i = 0; i < allParts.Count; ++i)
            {
                PartSim partSim = allParts[i];

                partSim.SetupAttachNodes(partSimLookup, log);
                if (partSim.decoupledInStage >= lastStage)
                {
                    lastStage = partSim.decoupledInStage + 1;
                }
            }

            // And finally release the Part references from all the PartSims
            if (log != null)
            {
                log.AppendLine("ReleaseParts");
            }
            for (int i = 0; i < allParts.Count; ++i)
            {
                allParts[i].ReleasePart();
            }

            // And dereference the core's part list
            partList = null;

            _timer.Stop();
            if (log != null)
            {
                log.AppendLine("PrepareSimulation: ", _timer.ElapsedMilliseconds, "ms");
                log.Flush();
            }

            Dump();
            log = null;

            return(true);
        }
Пример #32
0
        // This function prepares the simulation by creating all the necessary data structures it will
        // need during the simulation.  All required data is copied from the core game data structures
        // so that the simulation itself can be run in a background thread without having issues with
        // the core game changing the data while the simulation is running.
        public bool PrepareSimulation(LogMsg _log, List<Part> parts, double theGravity, double theAtmosphere = 0, double theMach = 0, bool dumpTree = false, bool vectoredThrust = false, bool fullThrust = false)
        {
            log = _log;
            if (log != null) log.AppendLine("PrepareSimulation started");

            _timer.Reset();
            _timer.Start();

            // Store the parameters in members for ease of access in other functions
            partList = parts;
            gravity = theGravity;
            atmosphere = theAtmosphere;
            mach = theMach;
            lastStage = StageManager.LastStage;
            maxMach = 1.0f;
            if (log != null) log.AppendLine("lastStage = ", lastStage);

            // Clear the lists for our simulation parts
            allParts.Clear();
            allFuelLines.Clear();
            drainingParts.Clear();
            allEngines.Clear();
            activeEngines.Clear();
            drainingResources.Clear();

            // A dictionary for fast lookup of Part->PartSim during the preparation phase
            partSimLookup.Clear();

            if (partList.Count > 0 && partList[0].vessel != null)
            {
                vesselName = partList[0].vessel.vesselName;
                vesselType = partList[0].vessel.vesselType;
            }
            // First we create a PartSim for each Part (giving each a unique id)
            int partId = 1;
            for (int i = 0; i < partList.Count; ++i)
            {
                Part part = partList[i];

                // If the part is already in the lookup dictionary then log it and skip to the next part
                if (partSimLookup.ContainsKey(part))
                {
                    if (log != null) log.AppendLine("Part ", part.name, " appears in vessel list more than once");
                    continue;
                }

                // Create the PartSim
                PartSim partSim = PartSim.New(part, partId, atmosphere, log);

                // Add it to the Part lookup dictionary and the necessary lists
                partSimLookup.Add(part, partSim);
                allParts.Add(partSim);
                if (partSim.isFuelLine)
                {
                    allFuelLines.Add(partSim);
                }
                if (partSim.isEngine)
                {
                    partSim.CreateEngineSims(allEngines, atmosphere, mach, vectoredThrust, fullThrust, log);
                }

                partId++;
            }

            for (int i = 0; i < allEngines.Count; ++i)
            {
                maxMach = Mathf.Max(maxMach, allEngines[i].maxMach);
            }

            UpdateActiveEngines();

            // Now that all the PartSims have been created we can do any set up that needs access to other parts
            // First we set up all the parent links
            for (int i = 0; i < allParts.Count; i++)
            {
                PartSim partSim = allParts[i];
                partSim.SetupParent(partSimLookup, log);
            }

            // Then, in the VAB/SPH, we add the parent of each fuel line to the fuelTargets list of their targets
            if (HighLogic.LoadedSceneIsEditor)
            {
                for (int i = 0; i < allFuelLines.Count; ++i)
                {
                    PartSim partSim = allFuelLines[i];

                    CModuleFuelLine fuelLine = partSim.part.GetModule<CModuleFuelLine>();
                    if (fuelLine.target != null)
                    {
                        PartSim targetSim;
                        if (partSimLookup.TryGetValue(fuelLine.target, out targetSim))
                        {
                            if (log != null) log.AppendLine("Fuel line target is ", targetSim.name, ":", targetSim.partId);

                            targetSim.fuelTargets.Add(partSim.parent);
                        }
                        else
                        {
                            if (log != null) log.AppendLine("No PartSim for fuel line target (", partSim.part.partInfo.name, ")");
                        }
                    }
                    else
                    {
                        if (log != null) log.AppendLine("Fuel line target is null");
                    }
                }
            }

            if (log != null) log.AppendLine("SetupAttachNodes and count stages");
            for (int i = 0; i < allParts.Count; ++i)
            {
                PartSim partSim = allParts[i];

                partSim.SetupAttachNodes(partSimLookup, log);
                if (partSim.decoupledInStage >= lastStage)
                {
                    lastStage = partSim.decoupledInStage + 1;
                }
            }

            // And finally release the Part references from all the PartSims
            if (log != null) log.AppendLine("ReleaseParts");
            for (int i = 0; i < allParts.Count; ++i)
            {
                allParts[i].ReleasePart();
            }

            // And dereference the core's part list
            partList = null;

            _timer.Stop();
            if (log != null)
            {
                log.AppendLine("PrepareSimulation: ", _timer.ElapsedMilliseconds, "ms");
                log.Flush();
            }

            Dump();
            log = null;

            return true;
        }
Пример #33
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;
            float             currentThrottle = engineMod.currentThrottle;
            float             IspG            = engineMod.g;
            bool              throttleLocked  = engineMod.throttleLocked || fullThrust;
            List <Propellant> propellants     = engineMod.propellants;
            bool              active          = engineMod.isOperational;
            float             resultingThrust = 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.resourceConsumptions.Reset();
            engineSim.maxResourceConsumptions.Reset();
            engineSim.resourceFlowModes.Reset();
            engineSim.appliedForces.Clear();

            double flowRate    = 0.0;
            double maxFlowRate = 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, ref engineSim.maxMach);
                engineSim.isp          = atmosphereCurve.Evaluate((float)atmosphere);
                engineSim.fullThrust   = GetThrust(maxFuelFlow * flowModifier, engineSim.isp);
                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.AppendLine("throttleLocked is true, using thrust for flowRate");
                    }
                    flowRate = GetFlowRate(engineSim.thrust, engineSim.isp);
                }
                else
                {
                    if (currentThrottle > 0.0f && engineSim.partSim.isLanded == false)
                    {
                        // TODO: This bit doesn't work for RF engines
                        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);
                    }
                }

                maxFlowRate = GetFlowRate(engineSim.fullThrust, engineSim.isp);
            }
            else
            {
                if (log != null)
                {
                    log.buf.AppendLine("hasVessel is false");
                }
                float altitude     = BasicDeltaV.Instance.AtmosphereDepth;
                float flowModifier = GetFlowModifier(atmChangeFlow, atmCurve, BasicDeltaV.Instance.CurrentCelestialBody.GetDensity(BasicDeltaV.Instance.CurrentCelestialBody.GetPressure(altitude), BasicDeltaV.Instance.CurrentCelestialBody.GetTemperature(altitude)), velCurve, machNumber, ref engineSim.maxMach);
                engineSim.isp          = atmosphereCurve.Evaluate((float)atmosphere);
                engineSim.fullThrust   = GetThrust(maxFuelFlow * flowModifier, engineSim.isp);
                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);
                maxFlowRate = GetFlowRate(engineSim.fullThrust, 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 maxConsumptionRate = propellant.ratio * maxFlowRate / flowMass;

                engineSim.maxResourceConsumptions.Add(propellant.id, maxConsumptionRate);
            }

            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);
        }
Пример #34
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);
        }