// Static methods // Generate list of PersistentPropellant from propellant list public static List <PersistentPropellant> MakeList(List <Propellant> plist) { // Sum of ratios of propellants with mass var ratioMassSum = 0.0; // Create list of PersistentPropellant and calculate ratioSum & ratioMassSum var pplist = new List <PersistentPropellant>(); foreach (var p in plist) { var pp = new PersistentPropellant(p); pplist.Add(pp); if (pp.density > 0.0) { ratioMassSum += pp.ratio; } } // Normalize ratios to ratioMassSum if (ratioMassSum > 0) { foreach (var pp in pplist) { pp.normalizedRatio = pp.ratio / ratioMassSum; } } return(pplist); }
private double RequestResource(PersistentPropellant propellant, double demand, bool simulate = false) { if (propellant.density > 0 && !vessel.packed) { return(demand); } if (!useKerbalismInFlight) { return(part.RequestResource(propellant.definition.id, demand, propellant.propellant.GetFlowMode(), simulate)); } _availablePartResources.TryGetValue(propellant.definition.name, out var currentAmount); var available = Math.Min(currentAmount, demand); if (simulate) { _availablePartResources[propellant.definition.name] = Math.Max(0, currentAmount - demand); } else { var demandPerSecond = demand / TimeWarp.fixedDeltaTime; _kerbalismResourceChangeRequest.TryGetValue(propellant.definition.name, out var currentDemand); _kerbalismResourceChangeRequest[propellant.definition.name] = currentDemand - demandPerSecond; } return(available); }
public double UpdateBuffer(PersistentPropellant propellant, double baseSize) { var requiredBufferSize = useDynamicBuffer ? Math.Max(baseSize / TimeWarp.fixedDeltaTime * 10 * buffersizeMult, baseSize * buffersizeMult) : Math.Max(0, propellant.maxamount - baseSize); if (previousfixedDeltaTime == TimeWarp.fixedDeltaTime) { return(requiredBufferSize); } var amountRatio = propellant.maxamount > 0 ? Math.Min(1, propellant.amount / propellant.maxamount) : 0; dynamicBufferSize = useDynamicBuffer ? requiredBufferSize : 0; var partresource = part.Resources[propellant.definition.name]; if (partresource == null) { var node = new ConfigNode("RESOURCE"); node.AddValue("name", propellant.definition.name); node.AddValue("maxAmount", 0); node.AddValue("amount", 0); this.part.AddResource(node); partresource = part.Resources[propellant.definition.name]; } partresource.maxAmount = dynamicBufferSize; partresource.amount = dynamicBufferSize * amountRatio; previousfixedDeltaTime = TimeWarp.fixedDeltaTime; return(requiredBufferSize); }
private PersistentPropellant LoadPropellantAvailability(PersistentPropellant currentPersistentPropellant) { var activePropellant = currentPersistentPropellant; var activePropellants = _persistentEngines.SelectMany(pe => pe.moduleEngines.SelectMany(pl => pl.propellants.Where(pp => pp.missionTime == vessel.missionTime && pp.definition.id == currentPersistentPropellant.definition.id))).ToList(); if (activePropellants.Any()) { return(activePropellants.First()); } // store mission time to prevent other engines doing unnecessary work currentPersistentPropellant.missionTime = vessel.missionTime; // determine amount and maxAmount at start of PersistentEngine testing part.GetConnectedResourceTotals(currentPersistentPropellant.definition.id, currentPersistentPropellant.propellant.GetFlowMode(), out currentPersistentPropellant.amount, out currentPersistentPropellant.maxAmount, true); // calculate total demand on operational engines currentPersistentPropellant.totalEnginesDemand = _persistentEngines .Where(e => e.currentEngine.engine.getIgnitionState) .Sum(m => m.currentEngine.propellants .Where(l => l.definition.id == currentPersistentPropellant.definition.id) .Sum(l => l.normalizedDemand)); return(activePropellant); }
// Initialization public override void OnLoad(ConfigNode node) { // Run base OnLoad method base.OnLoad(node); Debug.Log("[PersistentThrust]: OnLoad called for " + part.partInfo.title + " " + part.persistentId); // Populate moduleEngine and moduleEngineFx fields FindModuleEngines(); // Initialize PersistentPropellant list if (isMultiMode) { moduleEngines[0].propellants = PersistentPropellant.MakeList(moduleEngines[0].engine.propellants); moduleEngines[1].propellants = PersistentPropellant.MakeList(moduleEngines[1].engine.propellants); moduleEngines[0].averageDensity = moduleEngines[0].propellants.AverageDensity(); moduleEngines[1].averageDensity = moduleEngines[1].propellants.AverageDensity(); } else { foreach (var engine in moduleEngines) { engine.propellants = PersistentPropellant.MakeList(engine.engine.propellants); engine.averageDensity = engine.propellants.AverageDensity(); } } }
// Initialization public override void OnLoad(ConfigNode node) { // Run base OnLoad method base.OnLoad(node); // Populate engine and engineFX fields FindModuleEngines(); if (IsPersistentEngine) { // Initialize PersistentPropellant list pplist = PersistentPropellant.MakeList(engine.propellants); // Initialize density of propellant used in deltaV and mass calculations densityAverage = pplist.AverageDensity(); } }
// Static methods // Generate list of PersistentPropellant from propellant list public static List<PersistentPropellant> MakeList(List<Propellant> plist) { // Sum of ratios of propellants with mass var ratioMassSum = 0.0; // Create list of PersistentPropellant and calculate ratioSum & ratioMassSum var pplist = new List<PersistentPropellant>(); foreach (var p in plist) { var pp = new PersistentPropellant(p); pplist.Add(pp); if (pp.density > 0.0) { ratioMassSum += pp.ratio; } } // Normalize ratios to ratioMassSum foreach (var pp in pplist) { pp.normalizedRatio = pp.ratio / ratioMassSum; } return pplist; }
private PersistentPropellant LoadPropellantAvailability(PersistentPropellant pp) { var activePropellant = pp; var firstProcessedEngine = persistentEngines.FirstOrDefault(m => m.pplist.Any(l => l.missionTime == vessel.missionTime && l.definition.id == pp.definition.id)); if (firstProcessedEngine == null) { // store mission time to prevent other engines doing unnesisary work pp.missionTime = vessel.missionTime; // determine amount and maxamount at start of PersistenEngine testing part.GetConnectedResourceTotals(pp.definition.id, pp.propellant.GetFlowMode(), out pp.amount, out pp.maxamount, true); // calculate total demand pp.totalEnginesDemand = persistentEngines.Sum(m => m.pplist.Where(l => l.definition.id == pp.definition.id).Sum(l => l.normalizedDemand)); } else { activePropellant = firstProcessedEngine.pplist.First(m => m.definition.id == pp.definition.id); } return(activePropellant); }
// Apply demanded resources & return results // Updated depleted boolean flag if resource request failed public virtual double[] ApplyDemands(double[] demands, ref float finalPropellantReqMetFactor) { double overallPropellantReqMet = 1; var demandsOut = new double[currentEngine.propellants.Count]; // first do a simulation run to determine the propellant availability so we don't over consume for (var i = 0; i < currentEngine.propellants.Count; i++) { currentEngine.autoMaximizePersistentIsp = false; PersistentPropellant persistentPropellant = currentEngine.propellants[i]; // Request resources if: // - resource has mass & request mass flag true // - resource massless & request massless flag true if ((!(persistentPropellant.density > 0) || !requestPropMass) && (persistentPropellant.density != 0 || !requestPropMassless)) { continue; } persistentPropellant.demandIn = demands[i]; var storageModifier = 1.0; // Process massless propellants like ElectricCharge separately if (persistentPropellant.density == 0) { // find initial resource amount for propellant var availablePropellant = LoadPropellantAvailability(persistentPropellant); _availablePartResources.TryGetValue(persistentPropellant.definition.name, out var kerbalismAmount); var currentPropellantAmount = useKerbalismInFlight ? kerbalismAmount : availablePropellant.amount; // update power buffer bufferSize = UpdateBuffer(availablePropellant, persistentPropellant.demandIn); var bufferedTotalEnginesDemand = Math.Min(availablePropellant.maxAmount, availablePropellant.totalEnginesDemand * bufferSizeMult); if (bufferedTotalEnginesDemand > currentPropellantAmount && availablePropellant.totalEnginesDemand > 0) { storageModifier = Math.Min(1, (persistentPropellant.demandIn / availablePropellant.totalEnginesDemand) + currentPropellantAmount / bufferedTotalEnginesDemand * (persistentPropellant.demandIn / availablePropellant.totalEnginesDemand)); } if (!MaximizePersistentPower && currentPropellantAmount < bufferSize) { storageModifier *= currentPropellantAmount / bufferSize; } } persistentPropellant.demandOut = IsInfinite(persistentPropellant.propellant) ? persistentPropellant.demandIn : RequestResource(persistentPropellant, persistentPropellant.demandIn * storageModifier, true); var propellantFoundRatio = persistentPropellant.demandOut >= persistentPropellant.demandIn ? 1 : persistentPropellant.demandIn > 0 ? persistentPropellant.demandOut / persistentPropellant.demandIn : 1; if (propellantFoundRatio < overallPropellantReqMet) { overallPropellantReqMet = propellantFoundRatio; } if (persistentPropellant.propellant.resourceDef.density > 0) { // reset stabilize Queue when out of mass propellant if (propellantFoundRatio < 1) { currentEngine.propellantReqMetFactorQueue.Clear(); } } else if (propellantFoundRatio == 0) { // reset stabilize Queue when out power for too long if (currentEngine.missingPowerCountdown <= 0) { currentEngine.propellantReqMetFactorQueue.Clear(); } currentEngine.missingPowerCountdown--; } else { currentEngine.missingPowerCountdown = missingPowerCountdownSize; } } // attempt to stabilize thrust output with First In Last Out Queue currentEngine.propellantReqMetFactorQueue.Enqueue((float)overallPropellantReqMet); if (currentEngine.propellantReqMetFactorQueue.Count() > propellantReqMetFactorQueueSize) { currentEngine.propellantReqMetFactorQueue.Dequeue(); } var averagePropellantReqMetFactor = currentEngine.propellantReqMetFactorQueue.Average(); if (averagePropellantReqMetFactor < minimumPropellantReqMetFactor) { currentEngine.autoMaximizePersistentIsp = true; } finalPropellantReqMetFactor = !vessel.packed || MaximizePersistentIsp || currentEngine.autoMaximizePersistentIsp ? averagePropellantReqMetFactor : Mathf.Pow(averagePropellantReqMetFactor, fudgeExponent); // secondly we can consume the resource based on propellant availability for (var i = 0; i < currentEngine.propellants.Count; i++) { var pp = currentEngine.propellants[i]; // Request resources if: // - resource has mass & request mass flag true // - resource massless & request massless flag true if ((pp.density > 0 && requestPropMass) || (pp.density == 0 && requestPropMassless)) { var demandIn = pp.density > 0 ? MaximizePersistentIsp || currentEngine.autoMaximizePersistentIsp ? averagePropellantReqMetFactor * demands[i] : demands[i] : overallPropellantReqMet * demands[i]; var demandOut = IsInfinite(pp.propellant) ? demandIn : RequestResource(pp, demandIn, false); demandsOut[i] = demandOut; } // Otherwise demand is 0 else { demandsOut[i] = 0; } } // Return demand outputs return(demandsOut); }