//--------------------------------------------------------- //Initialization Functions /// <summary> /// Sets the freestream properties /// </summary> /// <param name="altitude">altitude in m</param> /// <param name="pressure">pressure in kPa</param> /// <param name="temperature">temperature in K</param> /// <param name="velocity">velocity in m/s</param> /// <param name="hasOxygen">does the atmosphere contain oxygen</param> /// <param name="isUnderwater">Is the engine's thrust transform underwater</param> virtual public void SetFreestreamAndInlet(EngineThermodynamics ambientTherm, EngineThermodynamics inletTherm, double altitude, double inMach, Vector3 inVel, bool hasOxygen, bool isUnderwater) { th0.CopyFrom(ambientTherm); th1.CopyFrom(inletTherm); alt = altitude; p0 = ambientTherm.P; t0 = ambientTherm.T; rho = ambientTherm.Rho; oxygen = hasOxygen; underwater = isUnderwater; velocity = inVel; vel = velocity.magnitude; mach = inMach; Q = 0.5d * rho * vel * vel; P1 = inletTherm.P; T1 = inletTherm.T; Rho1 = inletTherm.Rho; gamma_c = inletTherm.Gamma; inv_gamma_c = 1d / gamma_c; inv_gamma_cm1 = 1d / (gamma_c - 1d); Cp_c = inletTherm.Cp; Cv_c = inletTherm.Cv; R_c = inletTherm.R; eair0 = ambientTherm.SpeedOfSound(0d); M0 = vel / eair0; }
/// <summary> /// Performs an adiabatic expansion or compression /// </summary> /// <param name="tempRatio">Total temperature ratio for this process</param> /// <param name="efficiency">Adiabatic efficiency for this process</param> /// <param name="work">Work done in the process, per unit reference mass. Positive if the process does work, negative if the process requires work.</param> /// <returns>Resulting thermodynamic state</returns> public EngineThermodynamics AdiabaticProcessWithTempRatio(double tempRatio, out double work, double efficiency = 1d) { EngineThermodynamics result = this.AdiabaticProcessWithTempRatio(tempRatio, efficiency: efficiency); work = Cp * (T - result.T) * MassRatio; return(result); }
/// <summary> /// Changes the reference frame of the gas, adjusting total conditions to account for the change /// </summary> /// <param name="t">Input thermodynamics</param> /// <param name="mach">Mach to change reference by. Positive = speed up, negative = slow down</param> public void FromChangeReferenceFrameMach(EngineThermodynamics t, double mach, bool forward = true) { CopyFrom(t); _T += 0.5 * (Gamma - 1) * mach * mach * Math.Sign(mach); double pressureExponent = t.Cp / t.R; P *= Math.Pow(T / t.T, pressureExponent); Recalculate(); }
virtual public void UpdateFlightCondition(EngineThermodynamics ambientTherm, double altitude, Vector3d vel, double mach, bool oxygen, bool underwater) { // In flight, these are the same and this will just return this.ambientTherm.CopyFrom(ambientTherm); engineSolver.SetEngineState(EngineIgnited, lastPropellantFraction); engineSolver.SetFreestreamAndInlet(ambientTherm, inletTherm, altitude, mach, vel, oxygen, underwater); engineSolver.CalculatePerformance(areaRatio, currentThrottle, flowMult, ispMult); }
/// <summary> /// Takes input conditions and performs an adiabatic expansion or compression, storing the result in self /// Positive work corresponds to work being done on the gas (i.e. compression), and negative work corresponds to the gas doing work (i.e. expansion) /// </summary> /// <param name="t">Input thermodynamics</param> /// <param name="work">Work to do in this process, per unit reference mass (controlled by MassRatio). Positive = compression, negative = expansion</param> /// <param name="efficiency">Adiabatic efficiency for this process</param> public void FromAdiabaticProcessWithWork(EngineThermodynamics t, double work, double efficiency = 1d) { CopyFrom(t); _T += work / Cp / MassRatio; double pressureExponent = t.Cp / t.R * efficiency; P *= Math.Pow(T / t.T, pressureExponent); Recalculate(); }
private void Start() { vessel = gameObject.GetComponent <Vessel>(); this.enabled = true; updatePartsList(); AmbientTherm = new EngineThermodynamics(); InletTherm = new EngineThermodynamics(); }
/// <summary> /// Performs an adiabatic expansion or compression /// </summary> /// <param name="tempRatio">Total temperature ratio for this process</param> /// <param name="efficiency">Adiabatic efficiency for this process</param> /// <returns>Resulting thermodynamic state</returns> public EngineThermodynamics AdiabaticProcessWithTempRatio(double tempRatio, double efficiency = 1d) { double pressureExponent = Cp / R * efficiency; EngineThermodynamics result = this; result.T *= tempRatio; result.P *= Math.Pow(tempRatio, pressureExponent); return(result); }
protected override void OnStart() { base.OnStart(); this.enabled = true; updatePartsList(); AmbientTherm = new EngineThermodynamics(); InletTherm = new EngineThermodynamics(); }
protected override void OnStart() { base.OnStart(); this.enabled = true; UpdatePartsList(); AmbientTherm = new EngineThermodynamics(); InletTherm = new EngineThermodynamics(); }
/// <summary> /// Takes input conditions and performs an adiabatic expansion or compression /// Positive work corresponds to work being done on the gas (i.e. compression), and negative work corresponds to the gas doing work (i.e. expansion) /// </summary> /// <param name="work">Work to do in this process, per unit reference mass (controlled by MassRatio). Positive = compression, negative = expansion</param> /// <param name="efficiency">Adiabatic efficiency for this process</param> /// <returns>Resulting thermodynamic state</returns> public EngineThermodynamics AdiabaticProcessWithWork(double work, double efficiency = 1d) { EngineThermodynamics result = this; result.T += work / Cp / MassRatio; double pressureExponent = Cp / R * efficiency; result.P *= Math.Pow(result.T / T, pressureExponent); return(result); }
/// <summary> /// Takes input conditions and performs an adiabatic expansion or compression /// </summary> /// <param name="pressureRatio">Total pressure ratio for this process</param> /// <param name="work">Work done in the process, per unit reference mass. Positive if the process does work, negative if the process requires work.</param> /// <param name="efficiency">Adiabatic efficiency for this process</param> /// <returns>Resulting thermodynamic state</returns> public EngineThermodynamics AdiabaticProcessWithPressureRatio(double pressureRatio, out double work, double efficiency = 1d) { //_T *= Math.Pow(pressureRatio, (Gamma - 1d) / Gamma / efficiency); // equivalent: tempExponent = (Gamma - 1) / Gamma EngineThermodynamics result = AdiabaticProcessWithPressureRatio(pressureRatio, efficiency: efficiency); work = Cp * (T - result.T) * MassRatio; return(result); }
//ferram4: separate out so function can be called separately for editor sims virtual public void UpdateInletEffects(EngineThermodynamics inletTherm, double areaRatio = 1d, double TPR = 1d) { if (engineSolver == null) { Debug.Log("*ERROR* EngineSolver on this part is null!"); return; } this.inletTherm = inletTherm; this.areaRatio = areaRatio; }
/// <summary> /// Changes the reference frame of the gas, adjusting total conditions to account for the change /// </summary> /// <param name="mach">Mach to change reference by. Positive = speed up, negative = slow down</param> public EngineThermodynamics ChangeReferenceFrameMach(double mach) { EngineThermodynamics result = this; double machFactor = 0.5 * (Gamma - 1) * mach * mach + 1d; result.T *= (mach > 0d) ? machFactor : 1d / machFactor; double pressureExponent = Cp / R; result.P *= Math.Pow(result.T / T, pressureExponent); return(result); }
/// <summary> /// Takes input conditions and performs an adiabatic expansion or compression /// </summary> /// <param name="pressureRatio">Total pressure ratio for this process</param> /// <param name="efficiency">Adiabatic efficiency for this process</param> /// <returns>Resulting thermodynamic state</returns> public EngineThermodynamics AdiabaticProcessWithPressureRatio(double pressureRatio, double efficiency = 1d) { //_T *= Math.Pow(pressureRatio, (Gamma - 1d) / Gamma / efficiency); // equivalent: tempExponent = (Gamma - 1) / Gamma EngineThermodynamics result = this; double tempExponent = R / Cp / efficiency; result.T *= Math.Pow(pressureRatio, tempExponent); result.P *= pressureRatio; return(result); }
//ferram4: separate out so function can be called separately for editor sims virtual public void UpdateInletEffects(EngineThermodynamics inletTherm, double areaRatio = 1d, double TPR = 1d) { if (engineSolver == null) { Debug.Log("*ERROR* EngineSolver on this part is null!"); return; } // CopyFrom avoids GC associated with allocating a new one every frame // Could probably just assign since this *shouldn't* be changed, but just to be sure this.inletTherm.CopyFrom(inletTherm); this.areaRatio = areaRatio; }
/// <summary> /// Sets the freestream properties to static conditions : sea level and not moving /// </summary> /// <param name="usePlanetarium">Whether to use Planetarium.fetch.Home to get static conditions or use standard Earth conditions</param> /// <param name="overallTPR">Total pressure recovery of inlet</param> protected void SetStaticConditions(bool usePlanetarium = true, double overallTPR = 1d) { EngineThermodynamics ambientTherm = new EngineThermodynamics(); ambientTherm.FromStandardConditions(usePlanetarium); EngineThermodynamics inletTherm = new EngineThermodynamics(); inletTherm.CopyFrom(ambientTherm); inletTherm.P *= overallTPR; SetFreestreamAndInlet(ambientTherm, inletTherm, 0d, 0d, Vector3.zero, true, false); }
/// <summary> /// Takes input conditions and performs an adiabatic expansion or compression, storing the result in self /// </summary> /// <param name="t">Input thermodynamics</param> /// <param name="pressureRatio">Total pressure ratio for this process</param> /// <param name="efficiency">Adiabatic efficiency for this process</param> /// <returns>Work done in the process, per unit reference mass. Positive if the process does work, negative if the process requires work.</returns> public double FromAdiabaticProcessWithPressureRatio(EngineThermodynamics t, double pressureRatio, double efficiency = 1d) { CopyFrom(t); double oldT = t.T; double oldCp = t.Cp; //_T *= Math.Pow(pressureRatio, (Gamma - 1d) / Gamma / efficiency); // equivalent: tempExponent = (Gamma - 1) / Gamma double tempExponent = R / Cp / efficiency; _T *= Math.Pow(pressureRatio, tempExponent); P *= pressureRatio; Rho *= Math.Pow(pressureRatio, 1d - tempExponent); Recalculate(); return(oldCp * (oldT - T) * MassRatio); }
// This odd system of copying from another Thermodynamics and then modifying is so that new ones don't have to be allocated every frame, which is bad for GC /// <summary> /// Takes input conditions and performs an adiabatic expansion or compression, storing the result in self /// </summary> /// <param name="t">Input thermodynamics</param> /// <param name="tempRatio">Total temperature ratio for this process</param> /// <param name="efficiency">Adiabatic efficiency for this process</param> /// <returns>Work done in the process, per unit reference mass. Positive if the process does work, negative if the process requires work.</returns> public double FromAdiabaticProcessWithTempRatio(EngineThermodynamics t, double tempRatio, double efficiency = 1d) { CopyFrom(t); double oldT = t.T; double oldCp = t.Cp; _T *= tempRatio; //double pressureExponent = Gamma / (Gamma - 1d) * efficiency; double pressureExponent = Cp / R * efficiency; // One less step P *= Math.Pow(tempRatio, pressureExponent); Rho *= Math.Pow(tempRatio, pressureExponent - 1d); Recalculate(); return(oldCp * (oldT - T) * MassRatio); }
/// <summary> /// Copies all values from another instance /// </summary> /// <param name="t">Thermodynamics to copy from</param> public void CopyFrom(EngineThermodynamics t) { if (this == t) // lol { return; } P = t.P; _T = t.T; Rho = t.Rho; _Far = t.Far; _FF = t.FF; Cp = t.Cp; Cv = t.Cv; Gamma = t.Gamma; R = t.R; MassRatio = t.MassRatio; }
/// <summary> /// Add fuel to a gas (and burn it) /// </summary> /// <param name="t">Input thermodynamics</param> /// <param name="maxTemp">Maximum allowed temperature</param> /// <param name="heatOfFuel">Heat of combustion of fuel, measured in joules/kg/K</param> /// <param name="throttle">Throttle for adding fuel. 0 corresponds to no fuel added, 1 corresponds to max fuel added</param> /// <param name="maxFar">Maximum fuel-air ratio (usually defined by stoichiometry). This will be handled automatically in the future</param> public void FromAddFuelToTemperature(EngineThermodynamics t, double maxTemp, double heatOfFuel, double throttle = 1d, double maxFar = 0d) { System.Diagnostics.Debug.Assert(throttle >= 0d && throttle <= 1d); CopyFrom(t); // Max fuel-air ratio - don't want to inject more fuel than can be burnt in air if (maxFar > 0d) { maxTemp = Math.Min(maxTemp, (maxFar - Far) * heatOfFuel / Cp + T); } double delta = (maxTemp - T) * throttle; _T += delta; double addedFuel = delta * Cp / heatOfFuel; // Order is important here - want old Far in this line not new Far MassRatio *= (addedFuel / (1d + Far) + 1d); Far += addedFuel; Rho = P / R / T; }
virtual public void Start() { CreateEngine(); if (ambientTherm == null) { ambientTherm = new EngineThermodynamics(); } if (inletTherm == null) { inletTherm = new EngineThermodynamics(); } Need_Area = RequiredIntakeArea(); Fields["Need_Area"].guiActiveEditor = Need_Area > 0f; currentThrottle = 0f; flameout = false; SetUnflameout(); Fields["fuelFlowGui"].guiUnits = " kg/sec"; }
/// <summary> /// Add fuel to a gas (and burn it) /// </summary> /// <param name="maxTemp">Maximum allowed temperature</param> /// <param name="heatOfFuel">Heat of combustion of fuel, measured in joules/kg/K</param> /// <param name="throttle">Throttle for adding fuel. 0 corresponds to no fuel added, 1 corresponds to max fuel added</param> /// <param name="maxFar">Maximum fuel-air ratio (usually defined by stoichiometry). This will be handled automatically in the future</param> public EngineThermodynamics AddFuelToTemperature(double maxTemp, double heatOfFuel, double throttle = 1d, double maxFar = 0d) { EngineThermodynamics result = this; System.Diagnostics.Debug.Assert(throttle >= 0d && throttle <= 1d); // Max fuel-air ratio - don't want to inject more fuel than can be burnt in air if (maxFar > 0d) { maxTemp = Math.Min(maxTemp, (maxFar - Far) * heatOfFuel / Cp + T); } double delta = (maxTemp - T) * throttle; result.T += delta; double addedFuel = delta * Cp / heatOfFuel; result.Far += addedFuel; // Order is important here - want old Far in this line not new Far result.MassRatio *= (addedFuel / (1d + Far) + 1d); return(result); }
public virtual void UpdateFlightCondition(EngineThermodynamics ambientTherm, double altitude, Vector3d vel, double mach, bool oxygen) { // In flight, these are the same and this will just return this.ambientTherm.CopyFrom(ambientTherm); engineSolver.SetEngineState(EngineIgnited, lastPropellantFraction); engineSolver.SetFreestreamAndInlet(ambientTherm, inletTherm, altitude, mach, vel, oxygen); engineSolver.CalculatePerformance(areaRatio, currentThrottle, flowMult, ispMult); }
public virtual void Start() { CreateEngine(); if (ambientTherm == null) ambientTherm = new EngineThermodynamics(); if (inletTherm == null) inletTherm = new EngineThermodynamics(); Need_Area = RequiredIntakeArea(); Fields["Need_Area"].guiActiveEditor = Need_Area > 0f; currentThrottle = 0f; flameout = false; SetUnflameout(); Fields["fuelFlowGui"].guiUnits = " kg/sec"; HideEventsActions(); }
public override void OnStart(PartModule.StartState state) { base.OnStart(state); flameout = false; SetUnflameout(); // set initial params engineTemp = 288.15d; currentThrottle = 0f; if (ambientTherm == null) ambientTherm = new EngineThermodynamics(); if (inletTherm == null) inletTherm = new EngineThermodynamics(); // Get emissives emissiveAnims = new List<ModuleAnimateEmissive>(); int mCount = part.Modules.Count; for (int i = 0; i < mCount; ++i) if (part.Modules[i] is ModuleAnimateEmissive) emissiveAnims.Add(part.Modules[i] as ModuleAnimateEmissive); FitEngineIfNecessary(); HideEventsActions(); // Set up ours Events["vShutdown"].active = false; Events["vActivate"].active = false; if (state != StartState.PreLaunch) { if (EngineIgnited) { if (allowShutdown) Events["vShutdown"].active = true; else Events["vShutdown"].active = false; Events["vActivate"].active = false; } else { Events["vShutdown"].active = false; if (!allowRestart && engineShutdown) Events["vActivate"].active = false; else Events["vActivate"].active = true; } } }
/// <summary> /// Changes the reference frame of the gas, adjusting total conditions to account for the change /// </summary> /// <param name="t">Input thermodynamics</param> /// <param name="speed">Speed to change reference by. Positive = speed up, negative = slow down</param> public void FromChangeReferenceFrame(EngineThermodynamics t, double speed, bool forward = true) { FromAdiabaticProcessWithWork(t, speed * speed / 2d * Math.Sign(speed)); }
// This odd system of copying from another Thermodynamics and then modifying is so that new ones don't have to be allocated every frame, which is bad for GC /// <summary> /// Takes input conditions and performs an adiabatic expansion or compression, storing the result in self /// </summary> /// <param name="t">Input thermodynamics</param> /// <param name="tempRatio">Total temperature ratio for this process</param> /// <param name="efficiency">Adiabatic efficiency for this process</param> /// <returns>Work done in the process, per unit reference mass. Positive if the process does work, negative if the process requires work.</returns> public double FromAdiabaticProcessWithTempRatio(EngineThermodynamics t, double tempRatio, double efficiency = 1d) { CopyFrom(t); double oldT = t.T; double oldCp = t.Cp; _T *= tempRatio; //double pressureExponent = Gamma / (Gamma - 1d) * efficiency; double pressureExponent = Cp / R * efficiency; // One less step P *= Math.Pow(tempRatio, pressureExponent); Rho *= Math.Pow(tempRatio, pressureExponent - 1d); Recalculate(); return oldCp * (oldT - T) * MassRatio; }
public override void UpdateFlightCondition(EngineThermodynamics ambientTherm, double altitude, Vector3d vel, double mach, bool oxygen) { throttledUp = false; if (!(engineSolver is SolverDEV)) { base.UpdateFlightCondition(ambientTherm, altitude, vel, mach, oxygen); return; } SolverDEV devSolver = (engineSolver as SolverDEV); // handle ignition if (HighLogic.LoadedSceneIsFlight && vessel != null) { if (vessel.ctrlState.mainThrottle > 0f || throttleLocked) throttledUp = true; else ignited = false; IgnitionUpdate(); // Ullage bool pressureOK = ullageSet.PressureOK(); propellantStatus = "Nominal"; if (ullage && RFSettings.Instance.simulateUllage) { propellantStatus = ullageSet.GetUllageState(); if (EngineIgnited && ignited && throttledUp && devSolver.GetRunning()) { curveTime += TimeWarp.fixedDeltaTime * devSolver.overPressureRatio * devSolver.overTempRatio; if (curveTime > maxBurnTime) { devSolver.SetDamageFrac(UnityEngine.Mathf.Pow(curveTime / maxBurnTime, 0.05f));/*MAGIC*/ } double state = ullageSet.GetUllageStability(); double testValue = Math.Pow(state, RFSettings.Instance.stabilityPower); if (((devSolver.failed & SolverDEV.isFailed.IGNITION) != SolverDEV.isFailed.NONE) && (UnityEngine.Random.value > devSolver.Stability) && (UnityEngine.Random.value > devSolver.Stability))/*MAGIC*/ testValue *= Mathf.Pow(devSolver.Stability, 2); if (UnityEngine.Random.value > testValue) { ScreenMessages.PostScreenMessage(ullageFail); FlightLogger.eventLog.Add("[" + FormatTime(vessel.missionTime) + "] " + ullageFail.message); reignitable = false; ullageOK = false; ignited = false; Flameout("Vapor in feed line"); } } } if (!pressureOK) { propellantStatus = "Feed pressure too low"; // override ullage status indicator vFlameout("Lack of pressure", false, ignited); ignited = false; reignitable = false; } devSolver.SetEngineStatus(pressureOK, (ullageOK || !RFSettings.Instance.simulateUllage), ignited); } // Set part temp devSolver.SetPartTemp(part.temperature); // do heat heatProduction = (float)(scaleRecip * devSolver.GetHeat() / PhysicsGlobals.InternalHeatProductionFactor * part.thermalMassReciprocal); // run base method code base.UpdateFlightCondition(ambientTherm, altitude, vel, mach, oxygen); Fields["statusDEV"].guiName = ""; Fields["statusDEV"].guiActive = true; statusDEV = devSolver.statusString; }
/// <summary> /// Changes the reference frame of the gas, adjusting total conditions to account for the change /// </summary> /// <param name="t">Input thermodynamics</param> /// <param name="speed">Speed to change reference by. Positive = speed up, negative = slow down</param> public void FromChangeReferenceFrame(EngineThermodynamics t, double speed, bool forward=true) { FromAdiabaticProcessWithWork(t, speed * speed / 2d * Math.Sign(speed)); }
protected string GetStaticThrustInfo(bool primaryField)//TODO WIP { string output = ""; if (engineSolver == null || !(engineSolver is SolverDEV)) CreateEngine(); SolverDEV devSolver = (engineSolver as SolverDEV); devSolver.SetEngineStatus(true, true, true); // get stats double pressure = 101.325d, temperature = 288.15d, density = 1.225d; if (Planetarium.fetch != null) { CelestialBody home = Planetarium.fetch.Home; if (home != null) { pressure = home.GetPressure(0d); temperature = home.GetTemperature(0d); density = home.GetDensity(pressure, temperature); } } currentThrottle = 1f; lastPropellantFraction = 1d; bool oldE = EngineIgnited; EngineIgnited = true; devSolver.UpdateThrustRatio(1d); ambientTherm = new EngineThermodynamics(); ambientTherm.FromAmbientConditions(pressure, temperature, density); inletTherm = new EngineThermodynamics(); inletTherm.CopyFrom(ambientTherm); UpdateFlightCondition(ambientTherm, 0d, Vector3d.zero, 0d, true); double thrust_atm = (devSolver.GetThrust() * 0.001d); double Isp_atm = devSolver.GetIsp(); double Cstar_atm = devSolver.Cstar; double Ct_atm = devSolver.Ct; ambientTherm = new EngineThermodynamics(); ambientTherm.FromAmbientConditions(0d, 4d, 0d); inletTherm = new EngineThermodynamics(); inletTherm.CopyFrom(ambientTherm); UpdateFlightCondition(ambientTherm, 0d, Vector3d.zero, 0d, true); double thrust_vac = (devSolver.GetThrust() * 0.001d); double Isp_vac = devSolver.GetIsp(); double Cstar_vac = devSolver.Cstar; double Ct_vac = devSolver.Ct; double P_vac = devSolver.GetEnginePressure(); double T_vac = devSolver.GetEngineTemp(); ambientTherm = new EngineThermodynamics(); ambientTherm.FromAmbientConditions(pressure * 2, temperature, density * 2); inletTherm = new EngineThermodynamics(); inletTherm.CopyFrom(ambientTherm); UpdateFlightCondition(ambientTherm, 0d, Vector3d.zero, 0d, true); double thrust_2atm = (devSolver.GetThrust() * 0.001d); double Isp_2atm = devSolver.GetIsp(); double Cstar_2atm = devSolver.Cstar; double Ct_2atm = devSolver.Ct; FloatCurve tC = new FloatCurve(); tC.Add(0, (float)Isp_vac); tC.Add(1, (float)Isp_atm); tC.Add(2, (float)Isp_2atm); atmosphereCurve = tC; maxThrust = (float)Math.Max(thrust_vac, thrust_atm); output += "<b>Max. Thrust(<color=#00FF99>ASL</color>/<color=#99CCFF>Vac.</color>):</b> <color=#00FF99>" + thrust_atm.ToString("N2") + "</color><b>/</b><color=#99CCFF>" + thrust_vac.ToString("N1") + "</color>kN "; output += ThrottleString()+"\n"; output += "<b>Isp(<color=#00FF99>ASL</color>/<color=#99CCFF>Vac.</color>):</b> <color=#00FF99>" + Isp_atm.ToString("N2") + "</color><b>/</b><color=#99CCFF>" + Isp_vac.ToString("N2") + "</color>s\n"; output += "<b><color=#0099ff>Ignitions Available: </color></b>" + ignitions + "\n"; output += "<b><color=#0099ff>Max. Burn time: </color></b>" + maxBurnTime + " Sec.\n"; if (!primaryField) { output += "<b>C*(<color=#00FF99>ASL</color>/<color=#99CCFF>Vac.</color>):</b> <color=#00FF99>" + Cstar_atm.ToString("N2") + "</color><b>/</b><color=#99CCFF>" + Cstar_vac.ToString("N2") + "</color>m/s\n"; output += "<b>Ct(<color=#00FF99>ASL</color>/<color=#99CCFF>Vac.</color>):</b> <color=#00FF99>" + Ct_atm.ToString("N2") + "</color><b>/</b><color=#99CCFF>" + Ct_vac.ToString("N2") + "</color>\n"; output += $"<b>Chamber Pressure:\n</b>{P_vac.ToString("N1")}<b>/</b>{nominalPcns.ToString("N1")}kPa\n<b>Chamber Temperature:\n</b>{T_vac.ToString("N1")}<b>/</b>{nominalTcns.ToString("N1")}K\n"; output += $"<b>Nozzle Exit Pressure: </b>{nominalPe} kPa\n<b>Nozzle Throat Area:</b>{At} m^2\n"; } output += "\n"; EngineIgnited = oldE; return output; }
virtual public void UpdateFlightCondition() { ambientTherm = EngineThermodynamics.VesselAmbientConditions(vessel, useExtTemp); }
private void Start() { vessel = gameObject.GetComponent<Vessel>(); this.enabled = true; updatePartsList(); AmbientTherm = new EngineThermodynamics(); InletTherm = new EngineThermodynamics(); }
/// <summary> /// Add fuel to a gas (and burn it) /// </summary> /// <param name="t">Input thermodynamics</param> /// <param name="maxTemp">Maximum allowed temperature</param> /// <param name="heatOfFuel">Heat of combustion of fuel, measured in joules/kg/K</param> /// <param name="throttle">Throttle for adding fuel. 0 corresponds to no fuel added, 1 corresponds to max fuel added</param> /// <param name="maxFar">Maximum fuel-air ratio (usually defined by stoichiometry). This will be handled automatically in the future</param> public void FromAddFuelToTemperature(EngineThermodynamics t, double maxTemp, double heatOfFuel, double throttle = 1d, double maxFar = 0d) { System.Diagnostics.Debug.Assert(throttle >= 0d && throttle <= 1d); CopyFrom(t); // Max fuel-air ratio - don't want to inject more fuel than can be burnt in air if (maxFar > 0d) maxTemp = Math.Min(maxTemp, (maxFar - Far) * heatOfFuel / Cp + T); double delta = (maxTemp - T) * throttle; _T += delta; double addedFuel = delta * Cp / heatOfFuel; // Order is important here - want old Far in this line not new Far MassRatio *= (addedFuel / (1d + Far) + 1d); Far += addedFuel; Rho = P / R / T; }
private void FixedUpdate() { if (!HighLogic.LoadedSceneIsFlight || !vessel) { return; } if (vessel.altitude > vessel.mainBody.atmosphereDepth) { return; } int newCount = vessel.Parts.Count; if (partsCount != newCount) { partsCount = newCount; UpdatePartsList(); } InletArea = 0d; EngineArea = 0d; OverallTPR = 0d; AreaRatio = 0d; for (int j = engineList.Count - 1; j >= 0; --j) { ModuleEnginesSolver e = engineList[j]; if ((object)e != null && e.EngineIgnited) { EngineArea += e.Need_Area; } } for (int j = inletList.Count - 1; j >= 0; --j) { AJEInlet i = inletList[j]; if ((object)i != null) { double area = i.UsableArea(); InletArea += area; OverallTPR += area * i.overallTPR; } } if (InletArea > 0d) { if (EngineArea > 0d) { AreaRatio = Math.Min(1d, InletArea / EngineArea); OverallTPR /= InletArea; OverallTPR *= AreaRatio; } else { AreaRatio = 1d; } } AmbientTherm = EngineThermodynamics.VesselAmbientConditions(vessel); Mach = vessel.mach; // Transform from static frame to vessel frame, increasing total pressure and temperature if (vessel.srfSpeed < 0.01d) { InletTherm = AmbientTherm; } else { InletTherm = AmbientTherm.ChangeReferenceFrame(vessel.srfSpeed); } InletTherm.P *= OverallTPR; // TPR accounts for loss of total pressure by inlet // Push parameters to each engine for (int i = engineList.Count - 1; i >= 0; --i) { engineList[i].UpdateInletEffects(InletTherm, AreaRatio, OverallTPR); } }
//ferram4: separate out so function can be called separately for editor sims public virtual void UpdateInletEffects(EngineThermodynamics inletTherm, double areaRatio = 1d, double TPR = 1d) { if (engineSolver == null) { Debug.Log("*ERROR* EngineSolver on this part is null!"); return; } // CopyFrom avoids GC associated with allocating a new one every frame // Could probably just assign since this *shouldn't* be changed, but just to be sure this.inletTherm.CopyFrom(inletTherm); this.areaRatio = areaRatio; }
public override void UpdateFlightCondition(EngineThermodynamics ambientTherm, double altitude, Vector3d vel, double mach, bool oxygen) { throttledUp = false; // handle ignition if (HighLogic.LoadedSceneIsFlight) { if (vessel.ctrlState.mainThrottle > 0f || throttleLocked) throttledUp = true; else ignited = false; IgnitionUpdate(); // Ullage bool pressureOK = ullageSet.PressureOK(); propellantStatus = "Nominal"; if (ullage && RFSettings.Instance.simulateUllage) { propellantStatus = ullageSet.GetUllageState(); if (EngineIgnited && ignited && throttledUp && rfSolver.GetRunning()) { double state = ullageSet.GetUllageStability(); double testValue = Math.Pow(state, RFSettings.Instance.stabilityPower); if (UnityEngine.Random.value > testValue) { ScreenMessages.PostScreenMessage(ullageFail); FlightLogger.eventLog.Add("[" + FormatTime(vessel.missionTime) + "] " + ullageFail.message); reignitable = false; ullageOK = false; ignited = false; Flameout("Vapor in feed line"); } } } if (!pressureOK) { propellantStatus = "Feed pressure too low"; // override ullage status indicator vFlameout("Lack of pressure", false, ignited); ignited = false; reignitable = false; } rfSolver.SetEngineStatus(pressureOK, (ullageOK || !RFSettings.Instance.simulateUllage), ignited); // do thrust curve if (ignited && useThrustCurve) { thrustCurveRatio = (float)((propellants[curveProp].totalResourceAvailable / propellants[curveProp].totalResourceCapacity)); if (thrustCurveUseTime) { thrustCurveDisplay = thrustCurve.Evaluate(curveTime); if (EngineIgnited) { curveTime += TimeWarp.fixedDeltaTime; } } else { thrustCurveDisplay = thrustCurve.Evaluate(thrustCurveRatio); } rfSolver.UpdateThrustRatio(thrustCurveDisplay); thrustCurveDisplay *= 100f; } } // Set part temp rfSolver.SetPartTemp(part.temperature); // do heat heatProduction = (float)(scaleRecip * extHeatkW / PhysicsGlobals.InternalHeatProductionFactor * part.thermalMassReciprocal); // run base method code base.UpdateFlightCondition(ambientTherm, altitude, vel, mach, oxygen); }
protected string GetThrustInfo() { string output = ""; if (engineSolver == null || !(engineSolver is SolverRF)) CreateEngine(); rfSolver.SetEngineStatus(true, true, true); // get stats double pressure = 101.325d, temperature = 288.15d, density = 1.225d; if (Planetarium.fetch != null) { CelestialBody home = Planetarium.fetch.Home; if (home != null) { pressure = home.GetPressure(0d); temperature = home.GetTemperature(0d); density = home.GetDensity(pressure, temperature); } } ambientTherm = new EngineThermodynamics(); ambientTherm.FromAmbientConditions(pressure, temperature, density); inletTherm = new EngineThermodynamics(); inletTherm.CopyFrom(ambientTherm); currentThrottle = 1f; lastPropellantFraction = 1d; bool oldE = EngineIgnited; EngineIgnited = true; rfSolver.UpdateThrustRatio(1d); UpdateFlightCondition(ambientTherm, 0d, Vector3d.zero, 0d, true); double thrustASL = (engineSolver.GetThrust() * 0.001d); if (atmChangeFlow) // If it's a jet { output += "<b>Static Thrust: </b>" + (thrustASL).ToString("0.0##") + " kN" + ThrottleString(); if (useVelCurve) // if thrust changes with mach { float vMin, vMax, tMin, tMax; velCurve.FindMinMaxValue(out vMin, out vMax, out tMin, out tMax); // get the max mult, and thus report maximum thrust possible. output += "\n<b>Max. Thrust: </b>" + (thrustASL* vMax).ToString("0.0##") + " kN Mach " + tMax.ToString("0.#"); } } else { // get stats again double spaceHeight = 131000d; pressure = 0d; density = 0d; if (Planetarium.fetch != null) { CelestialBody home = Planetarium.fetch.Home; if (home != null) { temperature = home.GetTemperature(home.atmosphereDepth + 1d); spaceHeight = home.atmosphereDepth + 1000d; } } else temperature = PhysicsGlobals.SpaceTemperature; ambientTherm.FromAmbientConditions(pressure, temperature, density); UpdateFlightCondition(ambientTherm, spaceHeight, Vector3d.zero, 0d, true); double thrustVac = (engineSolver.GetThrust() * 0.001d); if (thrustASL != thrustVac) { output += (throttleLocked ? "<b>" : "<b>Max. ") + "Thrust (Vac.): </b>" + (thrustVac).ToString("0.0##") + " kN" + ThrottleString() + "\n" + (throttleLocked ? "<b>" : "<b>Max. ") + "Thrust (ASL): </b>" + (thrustASL).ToString("0.0##") + " kN"; } else { output += (throttleLocked ? "<b>" : "<b>Max. ") + "Thrust: </b>" + (thrustVac).ToString("0.0##") + " kN" + ThrottleString(); } } output += "\n"; EngineIgnited = oldE; return output; }
/// <summary> /// Sets the freestream properties to static conditions : sea level and not moving /// </summary> /// <param name="usePlanetarium">Whether to use Planetarium.fetch.Home to get static conditions or use standard Earth conditions</param> /// <param name="overallTPR">Total pressure recovery of inlet</param> protected void SetStaticConditions(bool usePlanetarium = true, double overallTPR = 1d) { EngineThermodynamics ambientTherm = new EngineThermodynamics(); ambientTherm = EngineThermodynamics.StandardConditions(usePlanetarium); EngineThermodynamics inletTherm = new EngineThermodynamics(); inletTherm = ambientTherm; inletTherm.P *= overallTPR; SetFreestreamAndInlet(ambientTherm, inletTherm, 0d, 0d, Vector3.zero, true, false); }
/// <summary> /// Takes input conditions and performs an adiabatic expansion or compression, storing the result in self /// </summary> /// <param name="t">Input thermodynamics</param> /// <param name="pressureRatio">Total pressure ratio for this process</param> /// <param name="efficiency">Adiabatic efficiency for this process</param> /// <returns>Work done in the process, per unit reference mass. Positive if the process does work, negative if the process requires work.</returns> public double FromAdiabaticProcessWithPressureRatio(EngineThermodynamics t, double pressureRatio, double efficiency = 1d) { CopyFrom(t); double oldT = t.T; double oldCp = t.Cp; //_T *= Math.Pow(pressureRatio, (Gamma - 1d) / Gamma / efficiency); // equivalent: tempExponent = (Gamma - 1) / Gamma double tempExponent = R / Cp / efficiency; _T *= Math.Pow(pressureRatio, tempExponent); P *= pressureRatio; Rho *= Math.Pow(pressureRatio, 1d - tempExponent); Recalculate(); return oldCp * (oldT - T) * MassRatio; }
//--------------------------------------------------------- //Initialization Functions /// <summary> /// Sets the freestream properties /// </summary> /// <param name="altitude">altitude in m</param> /// <param name="pressure">pressure in kPa</param> /// <param name="temperature">temperature in K</param> /// <param name="velocity">velocity in m/s</param> /// <param name="hasOxygen">does the atmosphere contain oxygen</param> /// <param name="isUnderwater">Is the engine's thrust transform underwater</param> public virtual void SetFreestreamAndInlet(EngineThermodynamics ambientTherm, EngineThermodynamics inletTherm, double altitude, double inMach, Vector3 inVel, bool hasOxygen, bool isUnderwater) { th0 = ambientTherm; th1 = inletTherm; alt = altitude; p0 = ambientTherm.P; t0 = ambientTherm.T; rho = ambientTherm.Rho; oxygen = hasOxygen; underwater = isUnderwater; velocity = inVel; vel = velocity.magnitude; mach = inMach; Q = 0.5d * rho * vel * vel; P1 = inletTherm.P; T1 = inletTherm.T; Rho1 = inletTherm.Rho; gamma_c = inletTherm.Gamma; inv_gamma_c = 1d / gamma_c; inv_gamma_cm1 = 1d / (gamma_c - 1d); Cp_c = inletTherm.Cp; Cv_c = inletTherm.Cv; R_c = inletTherm.R; eair0 = ambientTherm.SpeedOfSound(0d); M0 = vel / eair0; }
public virtual void UpdateFlightCondition() { ambientTherm = EngineThermodynamics.VesselAmbientConditions(vessel, useExtTemp); }
public override void OnStart(PartModule.StartState state) { base.OnStart(state); flameout = false; SetUnflameout(); // set initial params engineTemp = 288.15d; currentThrottle = 0f; if (ambientTherm == null) ambientTherm = new EngineThermodynamics(); if (inletTherm == null) inletTherm = new EngineThermodynamics(); // Get emissives emissiveAnims = new List<ModuleAnimateHeat>(); int mCount = part.Modules.Count; for (int i = 0; i < mCount; ++i) if (part.Modules[i] is ModuleAnimateHeat) emissiveAnims.Add(part.Modules[i] as ModuleAnimateHeat); FitEngineIfNecessary(); }
//ferram4: separate out so function can be called separately for editor sims public virtual void UpdateInletEffects(EngineThermodynamics inletTherm, double areaRatio = 1d, double TPR = 1d) { if (engineSolver == null) { Debug.Log("*ERROR* EngineSolver on this part is null!"); return; } this.inletTherm = inletTherm; this.areaRatio = areaRatio; }
public override void OnStart(PartModule.StartState state) { base.OnStart(state); flameout = false; SetUnflameout(); // set initial params engineTemp = 288.15d; currentThrottle = 0f; if (ambientTherm == null) { ambientTherm = new EngineThermodynamics(); } if (inletTherm == null) { inletTherm = new EngineThermodynamics(); } // Get emissives emissiveAnims = new List <ModuleAnimateEmissive>(); int mCount = part.Modules.Count; for (int i = 0; i < mCount; ++i) { if (part.Modules[i] is ModuleAnimateEmissive) { emissiveAnims.Add(part.Modules[i] as ModuleAnimateEmissive); } } FitEngineIfNecessary(); HideEventsActions(); // Set up ours Events["vShutdown"].active = false; Events["vActivate"].active = false; if (state != StartState.PreLaunch) { if (EngineIgnited) { if (allowShutdown) { Events["vShutdown"].active = true; } else { Events["vShutdown"].active = false; } Events["vActivate"].active = false; } else { Events["vShutdown"].active = false; if (!allowRestart && engineShutdown) { Events["vActivate"].active = false; } else { Events["vActivate"].active = true; } } } }
private void FixedUpdate() { if (!HighLogic.LoadedSceneIsFlight || !vessel) return; if (vessel.altitude > vessel.mainBody.atmosphereDepth) return; int newCount = vessel.Parts.Count; if (partsCount != newCount) { partsCount = newCount; updatePartsList(); } InletArea = 0d; EngineArea = 0d; OverallTPR = 0d; AreaRatio = 0d; for (int j = engineList.Count - 1; j >= 0; --j) { ModuleEnginesSolver e = engineList[j]; if ((object)e != null && e.EngineIgnited) { EngineArea += e.Need_Area; } } for (int j = inletList.Count -1; j >= 0; --j) { AJEInlet i = inletList[j]; if ((object)i != null) { double area = i.UsableArea(); InletArea += area; OverallTPR += area * i.overallTPR; } } if (InletArea > 0d) { if (EngineArea > 0d) { AreaRatio = Math.Min(1d, InletArea / EngineArea); OverallTPR /= InletArea; OverallTPR *= AreaRatio; } else { AreaRatio = 1d; } } AmbientTherm = EngineThermodynamics.VesselAmbientConditions(vessel); Mach = vessel.mach; // Transform from static frame to vessel frame, increasing total pressure and temperature if (vessel.srfSpeed < 0.01d) InletTherm = AmbientTherm; else InletTherm = AmbientTherm.ChangeReferenceFrame(vessel.srfSpeed); InletTherm.P *= OverallTPR; // TPR accounts for loss of total pressure by inlet // Push parameters to each engine for (int i = engineList.Count - 1; i >= 0 ; --i) { engineList[i].UpdateInletEffects(InletTherm, AreaRatio, OverallTPR); } }
/// <summary> /// Copies all values from another instance /// </summary> /// <param name="t">Thermodynamics to copy from</param> public void CopyFrom(EngineThermodynamics t) { if (this == t) // lol return; P = t.P; _T = t.T; Rho = t.Rho; _Far = t.Far; _FF = t.FF; Cp = t.Cp; Cv = t.Cv; Gamma = t.Gamma; R = t.R; MassRatio = t.MassRatio; }