예제 #1
0
        //---------------------------------------------------------
        //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;
        }
예제 #2
0
        /// <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);
        }
예제 #3
0
        /// <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();
        }
예제 #4
0
        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);
        }
예제 #5
0
        /// <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();
        }
예제 #6
0
        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);
        }
예제 #8
0
        protected override void OnStart()
        {
            base.OnStart();

            this.enabled = true;
            updatePartsList();

            AmbientTherm = new EngineThermodynamics();
            InletTherm = new EngineThermodynamics();
        }
예제 #9
0
        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);
        }
예제 #12
0
        //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);
        }
예제 #15
0
        //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;
        }
예제 #16
0
        /// <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);
        }
예제 #17
0
        /// <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);
        }
예제 #18
0
        // 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);
        }
예제 #19
0
 /// <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;
 }
예제 #20
0
        /// <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;
        }
예제 #21
0
        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);
        }
예제 #23
0
        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);
        }
예제 #24
0
        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();
        }
예제 #25
0
        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;
                }
            }
        }
예제 #26
0
 /// <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;
        }
예제 #31
0
 virtual public void UpdateFlightCondition()
 {
     ambientTherm = EngineThermodynamics.VesselAmbientConditions(vessel, useExtTemp);
 }
예제 #32
0
        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;
        }
예제 #34
0
        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);
            }
        }
예제 #35
0
        //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;
        }
예제 #36
0
        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);
        }
 /// <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();
 }
예제 #38
0
        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>
 /// 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();
 }
예제 #40
0
        /// <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;
        }
예제 #42
0
        //---------------------------------------------------------
        //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;
        }
예제 #43
0
 public virtual void UpdateFlightCondition()
 {
     ambientTherm = EngineThermodynamics.VesselAmbientConditions(vessel, useExtTemp);
 }
예제 #44
0
        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();
        }
예제 #45
0
        //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;
        }
예제 #46
0
        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;
                    }
                }
            }
        }
예제 #47
0
        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;
 }