Exemplo n.º 1
0
        public static void UpdateThermodynamicsPre(ModularFI.ModularFlightIntegrator fi)
        {
            if (lastVessel != fi.Vessel || parts.Count != fi.partThermalDataList.Count)
            {
                parts.Clear();
                lastVessel = fi.Vessel;

                for (int i = fi.partThermalDataList.Count; i-- > 0;)
                {
                    var ptd  = fi.partThermalDataList[i];
                    var part = ptd.part;
                    if (part.GetComponent <ModuleNonReentryRated>())
                    {
                        parts.Add(part);
                    }
                }
            }

            for (int i = fi.partThermalDataList.Count; i-- > 0;)
            {
                PartThermalData ptd  = fi.partThermalDataList[i];
                var             part = ptd.part;
                if (parts.Contains(part))
                {
                    ptd.convectionTempMultiplier  = Math.Max(ptd.convectionTempMultiplier, 0.75d);
                    ptd.convectionCoeffMultiplier = Math.Max(ptd.convectionCoeffMultiplier, 0.75d);
                    ptd.convectionAreaMultiplier  = Math.Max(ptd.convectionAreaMultiplier, 0.75d);

                    ptd.postShockExtTemp = UtilMath.LerpUnclamped(part.vessel.atmosphericTemperature, part.vessel.externalTemperature, ptd.convectionTempMultiplier);
                    ptd.finalCoeff       = part.vessel.convectiveCoefficient * ptd.convectionArea * 0.001d * part.heatConvectiveConstant * ptd.convectionCoeffMultiplier;
                }
            }
        }
Exemplo n.º 2
0
        public override void CalculatePerformance(double airRatio, double commandedThrottle, double flowMult, double ispMult)
        {
            // set base bits
            base.CalculatePerformance(airRatio, commandedThrottle, flowMult, ispMult);

            // Calculate Isp (before the shutdown check, so it displays even then)
            Isp = atmosphereCurve.Evaluate((float)(p0 * 0.001d * PhysicsGlobals.KpaToAtmospheres)) * ispMult;

            // if we're not combusting, don't combust and start cooling off
            bool shutdown = !running;

            statusString = "Nominal";
            if (ffFraction <= 0d)
            {
                shutdown     = true;
                statusString = "No propellants";
            }
            if (shutdown || commandedThrottle <= 0d)
            {
                double declinePow = Math.Pow(tempDeclineRate, TimeWarp.fixedDeltaTime);
                chamberTemp = Math.Max(t0, chamberTemp * declinePow);
                return;
            }

            // get current flow, and thus thrust.
            double fuelFlowMult = FlowMult();

            if (fuelFlowMult < 0.05d)
            {
                fuelFlow = 0d;
            }
            else
            {
                fuelFlow = flowMult * UtilMath.LerpUnclamped(minFlow, maxFlow, commandedThrottle) * fuelFlowMult;
            }

            double exhaustVelocity = Isp * 9.80665d;

            thrust = fuelFlow * exhaustVelocity;

            // Calculate chamber temperature as ratio
            double desiredTempRatio = Math.Max(tempMin, commandedThrottle);
            double machTemp         = MachTemp() * 0.05d;

            desiredTempRatio = desiredTempRatio * (1d + machTemp) + machTemp;

            // set based on desired
            double desiredTemp = desiredTempRatio * chamberNominalTemp;

            if (Math.Abs(desiredTemp - chamberTemp) < 1d)
            {
                chamberTemp = desiredTemp;
            }
            else
            {
                double lerpVal = Math.Min(1d, tempLerpRate * TimeWarp.fixedDeltaTime);
                chamberTemp = UtilMath.LerpUnclamped(chamberTemp, desiredTemp, lerpVal);
            }
        }
        public virtual float CalculateShockTemperature(CelestialBody body, float machNumber, float speed)
        {
            double convectiveMachLerp = Math.Pow(
                UtilMath.Clamp01(
                    (machNumber - PhysicsGlobals.NewtonianMachTempLerpStartMach) /
                    (PhysicsGlobals.NewtonianMachTempLerpEndMach - PhysicsGlobals.NewtonianMachTempLerpStartMach)),
                PhysicsGlobals.NewtonianMachTempLerpExponent);

            double num = speed * PhysicsGlobals.NewtonianTemperatureFactor;

            if (convectiveMachLerp > 0.0)
            {
                double b = PhysicsGlobals.MachTemperatureScalar * Math.Pow(speed, PhysicsGlobals.MachTemperatureVelocityExponent);
                num = UtilMath.LerpUnclamped(num, b, convectiveMachLerp);
            }
            return((float)(num * (double)HighLogic.CurrentGame.Parameters.Difficulty.ReentryHeatScale * body.shockTemperatureMultiplier));
        }
Exemplo n.º 4
0
        public static void UpdateThermodynamicsPre(ModularFI.ModularFlightIntegrator fi)
        {
            foreach (Part part in _registeredNonReentryParts)
            {
                if (part.vessel != fi.Vessel)
                {
                    continue;
                }

                PartThermalData ptd = part.ptd;
                ptd.convectionTempMultiplier  = Math.Max(ptd.convectionTempMultiplier, 0.75d);
                ptd.convectionCoeffMultiplier = Math.Max(ptd.convectionCoeffMultiplier, 0.75d);
                ptd.convectionAreaMultiplier  = Math.Max(ptd.convectionAreaMultiplier, 0.75d);

                ptd.postShockExtTemp = UtilMath.LerpUnclamped(part.vessel.atmosphericTemperature, part.vessel.externalTemperature, ptd.convectionTempMultiplier);
                ptd.finalCoeff       = part.vessel.convectiveCoefficient * ptd.convectionArea * 0.001d * part.heatConvectiveConstant * ptd.convectionCoeffMultiplier;
            }
        }
        protected virtual float CalculateConvectiveCoefficient(CelestialBody body, float speed, double atmDensity, double convectiveMachLerp)
        {
            double num = 0.0;

            if (convectiveMachLerp == 0.0)
            {
                num = CalculateConvectiveCoefficientNewtonian(speed, atmDensity);
            }
            else if (convectiveMachLerp == 1.0)
            {
                num = CalculateConvectiveCoefficientMach(speed, atmDensity);
            }
            else
            {
                num = UtilMath.LerpUnclamped(CalculateConvectiveCoefficientNewtonian(speed, atmDensity),
                                             CalculateConvectiveCoefficientMach(speed, atmDensity), convectiveMachLerp);
            }
            return((float)(num * body.convectionMultiplier));
        }
Exemplo n.º 6
0
        public override void OnVertexBuildHeight(PQS.VertexBuildData data)
        {
            Vector3 reference = moveCorner ? Vector3.one : Vector3.up;
            Vector3 vector    = data.directionFromCenter;

            Quaternion rotation = Quaternion.FromToRotation(reference, position) * Quaternion.AngleAxis(angle, reference);

            vector = Quaternion.Inverse(rotation) * vector;

            double x   = Math.Abs(vector.x);
            double y   = Math.Abs(vector.y);
            double z   = Math.Abs(vector.z);
            double max = Math.Max(x, Math.Max(y, z));

            x /= max;
            y /= max;
            z /= max;

            data.vertHeight += sphere.radius * deformity * ((UtilMath.LerpUnclamped(1, Math.Pow(x * x + y * y + z * z, 0.5), power) * radius) - 1);
        }
Exemplo n.º 7
0
            public void Update()
            {
                Altitude    = pos.magnitude - Body.Radius;
                Atmosphere  = Body.atmosphere && Altitude < Body.atmosphereDepth;
                rel_pos     = Body.BodyFrame.WorldToLocal(TrajectoryCalculator.BodyRotationAtdT(Body, Path.UT0 - UT) * pos);
                srf_vel     = vel + Vector3d.Cross(Body.zUpAngularVelocity, pos);
                HorSrfSpeed = Vector3d.Exclude(rel_pos, srf_vel).magnitude;
                SrfSpeed    = srf_vel.magnitude;

                if (Atmosphere)
                {
                    Pressure = Body.GetPressure(Altitude);
                    AtmosphereTemperature = Body.GetTemperature(Altitude);
                    Density = Body.GetDensity(Pressure, AtmosphereTemperature);
                    Mach1   = Body.GetSpeedOfSound(Pressure, Density);

                    var Rho_v = Density * SrfSpeed;
                    DynamicPressure = Rho_v * SrfSpeed / 2;
                    Mach            = SrfSpeed / Mach1;
                    SpecificDrag    = AtmoSim.Cd * DynamicPressure *
                                      PhysicsGlobals.DragCurveValue(PhysicsGlobals.SurfaceCurves, VSL.OnPlanetParams.DragCurveK, (float)Mach) *
                                      PhysicsGlobals.DragCurvePseudoReynolds.Evaluate((float)(Rho_v));

                    var convectiveMachLerp = Math.Pow(UtilMath.Clamp01((Mach - PhysicsGlobals.NewtonianMachTempLerpStartMach) /
                                                                       (PhysicsGlobals.NewtonianMachTempLerpEndMach - PhysicsGlobals.NewtonianMachTempLerpStartMach)),
                                                      PhysicsGlobals.NewtonianMachTempLerpExponent);
                    ShockTemperature = SrfSpeed * PhysicsGlobals.NewtonianTemperatureFactor;
                    if (convectiveMachLerp > 0.0)
                    {
                        double b = PhysicsGlobals.MachTemperatureScalar * Math.Pow(SrfSpeed, PhysicsGlobals.MachTemperatureVelocityExponent);
                        ShockTemperature = UtilMath.LerpUnclamped(ShockTemperature, b, convectiveMachLerp);
                    }
                    ShockTemperature *= (double)HighLogic.CurrentGame.Parameters.Difficulty.ReentryHeatScale * Body.shockTemperatureMultiplier;
                    ShockTemperature  = Math.Max(AtmosphereTemperature, ShockTemperature);
                    //calculate convective coefficient for speed > Mach1; lower speed is not a concern
                    ConvectiveCoefficient  = 1E-10 * PhysicsGlobals.MachConvectionFactor;
                    ConvectiveCoefficient *= Density > 1 ? Density : Math.Pow(Density, PhysicsGlobals.MachConvectionDensityExponent);
                    ConvectiveCoefficient *= Math.Pow(SrfSpeed, PhysicsGlobals.MachConvectionVelocityExponent) * Body.convectionMultiplier;
                }
            }
//		ptd.finalCoeff = this.convectiveCoefficient * ptd.convectionArea * 0.001 * part.heatConvectiveConstant * ptd.convectionCoeffMultiplier;
//		ptd.finalCoeff = Math.Min(ptd.finalCoeff, part.skinThermalMass * part.skinExposedAreaFrac);

        public AtmosphericConditions(Orbit orb, double UT) : this(UT)
        {
            var Body = orb.referenceBody;
            if (!Body.atmosphere)
            {
                return;
            }
            var pos = orb.getRelativePositionAtUT(UT);
            Altitude = pos.magnitude - Body.Radius;
            Pressure = Body.GetPressure(Altitude);
            if (Pressure > 0)
            {
                Speed                  = orb.getOrbitalSpeedAtRelativePos(pos);
                Atmosphere             = true;
                AtmosphericTemperature = Body.GetTemperature(Altitude);
                Density                = Body.GetDensity(Pressure, AtmosphericTemperature);
                DynamicPressure        = 0.0005 * Density * Speed * Speed;

                var soundV             = Body.GetSpeedOfSound(Pressure, Density);
                var mach               = soundV > 0? Speed / soundV : 0;
                var convectiveMachLerp = Math.Pow(UtilMath.Clamp01((mach - PhysicsGlobals.NewtonianMachTempLerpStartMach) /
                                                                   (PhysicsGlobals.NewtonianMachTempLerpEndMach - PhysicsGlobals.NewtonianMachTempLerpStartMach)),
                                                  PhysicsGlobals.NewtonianMachTempLerpExponent);
                ShockTemperature = Speed * PhysicsGlobals.NewtonianTemperatureFactor;
                if (convectiveMachLerp > 0.0)
                {
                    double b = PhysicsGlobals.MachTemperatureScalar * Math.Pow(Speed, PhysicsGlobals.MachTemperatureVelocityExponent);
                    ShockTemperature = UtilMath.LerpUnclamped(ShockTemperature, b, convectiveMachLerp);
                }
                ShockTemperature *= (double)HighLogic.CurrentGame.Parameters.Difficulty.ReentryHeatScale * Body.shockTemperatureMultiplier;
                ShockTemperature  = Math.Max(AtmosphericTemperature, ShockTemperature);
                //calculate convective coefficient for speed > Mach1; lower speed is not a consern
                ConvectiveCoefficient  = 1E-10 * PhysicsGlobals.MachConvectionFactor;
                ConvectiveCoefficient *= Density > 1? Density : Math.Pow(Density, PhysicsGlobals.MachConvectionDensityExponent);
                ConvectiveCoefficient *= Math.Pow(Speed, PhysicsGlobals.MachConvectionVelocityExponent) * Body.convectionMultiplier;
            }
        }
Exemplo n.º 9
0
        public static void UpdateThermodynamicsPre(ModularFI.ModularFlightIntegrator fi)
        {
            if (fi.CurrentMainBody != body)
            {
                body = fi.CurrentMainBody;
                RealHeatUtils.baseTempCurve.CalculateNewAtmTempCurve(body, RealHeatUtils.debugging);
            }

            if (fi.staticPressurekPa > 0d)
            {
                float spd = (float)fi.spd;

                // set shock temperature
                fi.Vessel.externalTemperature = fi.externalTemperature = fi.atmosphericTemperature + (double)RealHeatUtils.baseTempCurve.EvaluateTempDiffCurve(spd);

                // get gamma
                double Cp    = (double)RealHeatUtils.baseTempCurve.EvaluateVelCpCurve(spd);
                double R     = (double)RealHeatUtils.baseTempCurve.specificGasConstant;
                double Cv    = Cp - R;
                double gamma = Cp / Cv;

                // change density lerp
                double shockDensity = GetShockDensity(fi.density, fi.mach, gamma);
                fi.DensityThermalLerp = CalculateDensityThermalLerp(shockDensity);
                double lerpVal = fi.dynamicPressurekPa * 10d;
                if (lerpVal < 1d)
                {
                    fi.convectiveCoefficient *= UtilMath.LerpUnclamped(0.1d, 1d, lerpVal);
                }

                // reset background temps
                fi.backgroundRadiationTemp        = CalculateBackgroundRadiationTemperature(fi.atmosphericTemperature, fi.DensityThermalLerp);
                fi.backgroundRadiationTempExposed = CalculateBackgroundRadiationTemperature(fi.externalTemperature, fi.DensityThermalLerp);
                //print("At rho " + fi.density + "/" + shockDensity + ", gamma " + gamma + ", DTL " + fi.DensityThermalLerp + ", BT = " + fi.backgroundRadiationTempExposed.ToString("N2") + "/" + fi.backgroundRadiationTemp.ToString("N2"));
            }
        }
Exemplo n.º 10
0
            public override void PostCalculateTracking(Boolean trackingLOS, Vector3 trackingDirection)
            {
                // Maximum values
                Double         maxEnergy   = 0;
                Double         maxFlowRate = 0;
                KopernicusStar maxStar     = null;

                // Override layer mask
                planetLayerMask = ModularFlightIntegrator.SunLayerMask;

                // Efficiency modifier
                _efficMult = (temperatureEfficCurve.Evaluate((Single)part.skinTemperature) * timeEfficCurve.Evaluate((Single)((Planetarium.GetUniversalTime() - launchUT) * 1.15740740740741E-05)) * efficiencyMult);
                _flowRate  = 0;
                sunAOA     = 0;

                // Go through all stars
                foreach (KopernicusStar star in KopernicusStar.Stars)
                {
                    // Calculate stuff
                    Vector3       trackDir = (star.sun.transform.position - panelRotationTransform.position).normalized;
                    CelestialBody old      = trackingBody;
                    trackingTransformLocal  = star.sun.transform;
                    trackingTransformScaled = star.sun.scaledBody.transform;
                    trackingLOS             = CalculateTrackingLOS(trackDir, ref blockingObject);
                    trackingTransformLocal  = old.transform;
                    trackingTransformScaled = old.scaledBody.transform;

                    // Calculate sun AOA
                    Single _sunAOA = 0f;
                    if (!trackingLOS)
                    {
                        _sunAOA = 0f;
                        status  = "Blocked by " + blockingObject;
                    }
                    else
                    {
                        status = "Direct Sunlight";
                        if (panelType == PanelType.FLAT)
                        {
                            _sunAOA = Mathf.Clamp(Vector3.Dot(trackingDotTransform.forward, trackDir), 0f, 1f);
                        }
                        else if (panelType != PanelType.CYLINDRICAL)
                        {
                            _sunAOA = 0.25f;
                        }
                        else
                        {
                            Vector3 direction;
                            if (alignType == PanelAlignType.PIVOT)
                            {
                                direction = trackingDotTransform.forward;
                            }
                            else if (alignType != PanelAlignType.X)
                            {
                                direction = alignType != PanelAlignType.Y ? part.partTransform.forward : part.partTransform.up;
                            }
                            else
                            {
                                direction = part.partTransform.right;
                            }
                            _sunAOA = (1f - Mathf.Abs(Vector3.Dot(direction, trackDir))) * 0.318309873f;
                        }
                    }

                    // Calculate distance multiplier
                    Double __distMult = 1;
                    if (!useCurve)
                    {
                        if (!KopernicusStar.SolarFlux.ContainsKey(star.name))
                        {
                            continue;
                        }
                        __distMult = (Single)(KopernicusStar.SolarFlux[star.name] / stockLuminosity);
                    }
                    else
                    {
                        __distMult = powerCurve.Evaluate((star.sun.transform.position - panelRotationTransform.position).magnitude);
                    }

                    // Calculate flow rate
                    Double __flowRate = _sunAOA * _efficMult * __distMult;
                    if (part.submergedPortion > 0)
                    {
                        Double altitudeAtPos = -FlightGlobals.getAltitudeAtPos((Vector3d)secondaryTransform.position, vessel.mainBody);
                        altitudeAtPos = (altitudeAtPos * 3 + part.maxDepth) * 0.25;
                        if (altitudeAtPos < 0.5)
                        {
                            altitudeAtPos = 0.5;
                        }
                        Double num = 1 / (1 + altitudeAtPos * part.vessel.mainBody.oceanDensity);
                        if (part.submergedPortion >= 1)
                        {
                            __flowRate = __flowRate * num;
                        }
                        else
                        {
                            __flowRate = __flowRate * UtilMath.LerpUnclamped(1, num, part.submergedPortion);
                        }
                        status += ", Underwater";
                    }
                    sunAOA += _sunAOA;
                    Double energy = __distMult * _efficMult;
                    if (energy > maxEnergy)
                    {
                        maxFlowRate = __flowRate;
                        maxEnergy   = energy;
                        maxStar     = star;
                    }

                    // Apply the flow rate
                    _flowRate += __flowRate;
                }

                // Sun AOA
                sunAOA   /= relativeSunAOA ? KopernicusStar.Stars.Count : 1;
                _distMult = _flowRate != 0 ? _flowRate / _efficMult / sunAOA : 0;

                // We got the best star to use
                if (maxStar != null && maxStar.sun != trackingBody)
                {
                    if (!manualTracking)
                    {
                        trackingBody = maxStar.sun;
                        GetTrackingBodyTransforms();
                    }
                }

                // Use the flow rate
                flowRate = (Single)(resHandler.UpdateModuleResourceOutputs(_flowRate) * flowMult);
            }
Exemplo n.º 11
0
        public override void CalculatePerformance(double airRatio, double commandedThrottle, double flowMult, double ispMult)
        {
            // set base bits
            base.CalculatePerformance(airRatio, commandedThrottle, flowMult, ispMult);
            M0 = mach;

            // Calculate Isp (before the shutdown check, so it displays even then)
            Isp = atmosphereCurve.Evaluate((float)(p0 * 0.001d * PhysicsGlobals.KpaToAtmospheres)) * ispMult;

            // if we're not combusting, don't combust and start cooling off
            combusting   = running && ignited;
            statusString = "Nominal";

            // ullage check first, overwrite if bad pressure or no propellants
            if (!ullage)
            {
                combusting   = false;
                statusString = "Vapor in feed line";
            }

            // check fuel flow fraction
            if (ffFraction <= 0d)
            {
                combusting   = false;
                statusString = "No propellants";
            }
            // check pressure
            if (!pressure)
            {
                combusting   = false;
                statusString = "Lack of pressure";
            }

            // check flow mult
            double fuelFlowMult = FlowMult();

            if (fuelFlowMult < flowMultMin)
            {
                combusting   = false;
                statusString = "Airflow outside specs";
            }

            if (!combusting || commandedThrottle <= 0d)
            {
                combusting = false; // for throttle FX
                double declinePow = Math.Pow(tempDeclineRate, TimeWarp.fixedDeltaTime);
                chamberTemp = Math.Max(Math.Max(t0, partTemperature), chamberTemp * declinePow);
                fxPower     = 0f;
            }
            else
            {
                // get current flow, and thus thrust.
                fuelFlow = scale * flowMult * UtilMath.LerpUnclamped(minFlow, maxFlow, commandedThrottle) * thrustRatio;

                if (varyThrust > 0d && fuelFlow > 0d && HighLogic.LoadedSceneIsFlight)
                {
                    fuelFlow *= (1d + (Mathf.PerlinNoise(Time.time, 0f) * 2d - 1d) * varyThrust);
                }

                fxPower = (float)(fuelFlow / maxFlow * ispMult); // FX is proportional to fuel flow and Isp mult.
                if (float.IsNaN(fxPower))
                {
                    fxPower = 0f;
                }

                // apply fuel flow multiplier
                double ffMult = fuelFlow * fuelFlowMult;
                if (multFlow) // do we apply the flow multiplier to fuel flow or thrust?
                {
                    fuelFlow = ffMult;
                }
                else
                {
                    Isp *= fuelFlowMult;
                }

                double exhaustVelocity = Isp * 9.80665d;
                SFC = 3600d / Isp;

                thrust = ffMult * exhaustVelocity; // either way, thrust is base * mult * EV

                // Calculate chamber temperature as ratio
                double desiredTempRatio = Math.Max(tempMin, fxPower);
                double machTemp         = MachTemp() * 0.05d;
                desiredTempRatio = desiredTempRatio * (1d + machTemp) + machTemp;

                // set temp based on desired
                double desiredTemp = desiredTempRatio * chamberNominalTemp;
                if (Math.Abs(desiredTemp - chamberTemp) < 1d)
                {
                    chamberTemp = desiredTemp;
                }
                else
                {
                    double lerpVal = UtilMath.Clamp01(tempLerpRate * TimeWarp.fixedDeltaTime);
                    chamberTemp = UtilMath.LerpUnclamped(chamberTemp, desiredTemp, lerpVal);
                }
            }
            fxThrottle = combusting ? (float)throttle : 0f;
        }
        public void LatePostCalculateTracking(Boolean trackingLos, Vector3 trackingDirection, int panelId)
        {
            ModuleDeployableSolarPanel SP = SPs[panelId];

            // Maximum values
            Double         maxEnergy = 0;
            KopernicusStar maxStar   = null;

            // Override layer mask
            typeof(ModuleDeployableSolarPanel).GetFields(BindingFlags.Instance | BindingFlags.NonPublic).FirstOrDefault(f => f.Name == "planetLayerMask").SetValue(SP, ModularFlightIntegrator.SunLayerMask);

            // Efficiency modifier
            SP._efficMult = SP.temperatureEfficCurve.Evaluate((Single)part.skinTemperature) *
                            SP.timeEfficCurve.Evaluate(
                (Single)((Planetarium.GetUniversalTime() - SP.launchUT) * 1.15740740740741E-05)) *
                            SP.efficiencyMult;
            SP._flowRate = 0;
            SP.sunAOA    = 0;

            // Go through all stars
            Int32 stars = KopernicusStar.Stars.Count;

            for (Int32 i = 0; i < stars; i++)
            {
                KopernicusStar star = KopernicusStar.Stars[i];

                // Calculate stuff
                Transform     sunTransform = star.sun.transform;
                Vector3       trackDir     = (sunTransform.position - SP.panelRotationTransform.position).normalized;
                CelestialBody old          = SP.trackingBody;
                SP.trackingTransformLocal  = sunTransform;
                SP.trackingTransformScaled = star.sun.scaledBody.transform;



                SP.trackingTransformLocal  = old.transform;
                SP.trackingTransformScaled = old.scaledBody.transform;

                // Calculate sun AOA
                Single sunAoa;
                if (!trackingLos)
                {
                    FieldInfo blockingObject     = typeof(ModuleDeployableSolarPanel).GetFields(BindingFlags.Instance | BindingFlags.NonPublic).FirstOrDefault(f => f.Name == "blockingObject");
                    string    blockingObjectName = (string)blockingObject.GetValue(SP);
                    SP.status = Localizer.Format("#Kopernicus_UI_PanelBlocked", blockingObjectName);//"Blocked by " + blockingObjectName
                }
                else
                {
                    SP.status = SP_status_DirectSunlight;//"Direct Sunlight"
                }
                if (SP.panelType == ModuleDeployableSolarPanel.PanelType.FLAT)
                {
                    sunAoa = Mathf.Clamp(Vector3.Dot(SP.trackingDotTransform.forward, trackDir), 0f, 1f);
                }
                else if (SP.panelType != ModuleDeployableSolarPanel.PanelType.CYLINDRICAL)
                {
                    sunAoa = 0.25f;
                }
                else
                {
                    Vector3 direction;
                    if (SP.alignType == ModuleDeployableSolarPanel.PanelAlignType.PIVOT)
                    {
                        direction = SP.trackingDotTransform.forward;
                    }
                    else if (SP.alignType != ModuleDeployableSolarPanel.PanelAlignType.X)
                    {
                        direction = SP.alignType != ModuleDeployableSolarPanel.PanelAlignType.Y
                            ? part.partTransform.forward
                            : part.partTransform.up;
                    }
                    else
                    {
                        direction = part.partTransform.right;
                    }

                    sunAoa = (1f - Mathf.Abs(Vector3.Dot(direction, trackDir))) * 0.318309873f;
                }

                // Calculate distance multiplier
                Double distMult;
                if (!SP.useCurve)
                {
                    if (!KopernicusStar.SolarFlux.ContainsKey(star.name))
                    {
                        continue;
                    }

                    distMult = (Single)(KopernicusStar.SolarFlux[star.name] / StockLuminosity);
                }
                else
                {
                    distMult =
                        SP.powerCurve.Evaluate((star.sun.transform.position - SP.panelRotationTransform.position).magnitude);
                }

                // Calculate flow rate
                Double panelFlowRate = sunAoa * SP._efficMult * distMult;
                if (part.submergedPortion > 0)
                {
                    Double altitudeAtPos =
                        -FlightGlobals.getAltitudeAtPos
                        (
                            (Vector3d)(((Transform)typeof(ModuleDeployableSolarPanel).GetFields(BindingFlags.Instance | BindingFlags.NonPublic).FirstOrDefault(f => f.Name == "secondaryTransform").GetValue(SP)).position),
                            vessel.mainBody
                        );
                    altitudeAtPos = (altitudeAtPos * 3 + part.maxDepth) * 0.25;
                    if (altitudeAtPos < 0.5)
                    {
                        altitudeAtPos = 0.5;
                    }

                    Double num = 1 / (1 + altitudeAtPos * part.vessel.mainBody.oceanDensity);
                    if (part.submergedPortion >= 1)
                    {
                        panelFlowRate *= num;
                    }
                    else
                    {
                        panelFlowRate *= UtilMath.LerpUnclamped(1, num, part.submergedPortion);
                    }

                    SP.status += ", " + SP_status_Underwater;//Underwater
                }

                SP.sunAOA += sunAoa;
                Double energy = distMult * SP._efficMult;
                if (energy > maxEnergy)
                {
                    maxEnergy = energy;
                    maxStar   = star;
                }

                // Apply the flow rate
                SP._flowRate += panelFlowRate;
            }

            // Sun AOA
            SP.sunAOA   /= _relativeSunAoa ? stars : 1;
            SP._distMult = Math.Abs(SP._flowRate) > 0.01 ? SP._flowRate / SP._efficMult / SP.sunAOA : 0;

            // We got the best star to use
            if (maxStar != null && maxStar.sun != SP.trackingBody)
            {
                if (!_manualTracking)
                {
                    SP.trackingBody = maxStar.sun;
                    SP.GetTrackingBodyTransforms();
                }
            }

            // Use the flow rate
            SP.flowRate = (Single)(SP.resHandler.UpdateModuleResourceOutputs(SP._flowRate) * SP.flowMult);
        }
Exemplo n.º 13
0
        public void GetThermalStats(Vessel vessel)
        {
            shockTemp = vessel.externalTemperature;
            double densityThermalLerp = 1d - vessel.atmDensity;

            if (densityThermalLerp < 0.5d)
            {
                densityThermalLerp = 0.25d / vessel.atmDensity;
            }
            backgroundRadTemp = UtilMath.Lerp(
                vessel.externalTemperature,
                PhysicsGlobals.SpaceTemperature,
                densityThermalLerp);
            // sun
            inSun = vessel.directSunlight;
            Vector3 sunVector = (FlightGlobals.Bodies[0].scaledBody.transform.position - ScaledSpace.LocalToScaledSpace(vessel.transform.position)).normalized;

            solarFlux = vessel.solarFlux;
            if (FlightGlobals.Bodies[0] != vessel.mainBody)
            {
                double sunDistanceSqr = ((FlightGlobals.Bodies[0].scaledBody.transform.position - vessel.mainBody.scaledBody.transform.position) * ScaledSpace.ScaleFactor).sqrMagnitude;
                bodySunFlux = PhysicsGlobals.SolarLuminosity / (4d * Math.PI * sunDistanceSqr);
                sunDot      = Vector3.Dot(sunVector, vessel.upAxis);
                Vector3 mainBodyUp     = vessel.mainBody.bodyTransform.up;
                float   sunAxialDot    = Vector3.Dot(sunVector, mainBodyUp);
                double  bodyPolarAngle = Math.Acos(Vector3.Dot(mainBodyUp, vessel.upAxis));
                double  sunPolarAngle  = Math.Acos(sunAxialDot);
                double  sunBodyMaxDot  = (1d + Math.Cos(sunPolarAngle - bodyPolarAngle)) * 0.5d;
                double  sunBodyMinDot  = (1d + Math.Cos(sunPolarAngle + bodyPolarAngle)) * 0.5d;

                double fracBodyPolar = bodyPolarAngle;
                double fracSunPolar  = sunPolarAngle;

                if (bodyPolarAngle > Math.PI * 0.5d)
                {
                    fracBodyPolar = Math.PI - fracBodyPolar;
                    fracSunPolar  = Math.PI - fracSunPolar;
                }

                double bodyDayFraction = (Math.PI * 0.5d + Math.Asin((Math.PI * 0.5d - fracSunPolar) / fracBodyPolar)) * (1d / Math.PI);
                // correct sunDot based on the fact peak temp is ~3pm and min temp is ~3am, or so we will assume
                double sunDotCorrected = (1d + Vector3.Dot(sunVector, Quaternion.AngleAxis(-45f * Mathf.Sign((float)vessel.mainBody.rotationPeriod), mainBodyUp) * vessel.upAxis)) * 0.5d;

                // get normalized dot
                double sunDotNormalized = (sunDotCorrected - sunBodyMinDot) / (sunBodyMaxDot - sunBodyMinDot);
                if (double.IsNaN(sunDotNormalized))
                {
                    if (sunDotCorrected > 0.5d)
                    {
                        sunDotNormalized = 1d;
                    }
                    else
                    {
                        sunDotNormalized = 0d;
                    }
                }

                // get latitude-based changes, if body has an atmosphere
                if (vessel.mainBody.atmosphere)
                {
                    float latitude = (float)(Math.PI * 0.5d - fracBodyPolar);
                    latitude    *= Mathf.Rad2Deg;
                    diurnalRange = vessel.mainBody.latitudeTemperatureSunMultCurve.Evaluate(latitude);
                    latTempMod   = vessel.mainBody.latitudeTemperatureBiasCurve.Evaluate(latitude);
                    axialTempMod = vessel.mainBody.axialTemperatureSunMultCurve.Evaluate(sunAxialDot);
                    atmosphereTemperatureOffset = latTempMod + diurnalRange * sunDotNormalized + axialTempMod;
                    altTempMult = vessel.mainBody.atmosphereTemperatureSunMultCurve.Evaluate((float)vessel.altitude);

                    // calculate air mass etc
                    double depthFactor = 1.0;

                    if (vessel.atmDensity > 0)
                    {
                        // get final temp mod
                        finalAtmoMod = atmosphereTemperatureOffset * altTempMult;

                        // get solar air stuff
                        double rcosz = vessel.mainBody.radiusAtmoFactor * sunDot;
                        depthFactor = vessel.mainBody.GetSolarPowerFactor(vessel.atmDensity);
                        // alternative 1: absolute value

                        /*if(rcosz < 0)
                         * {
                         *  rcosz = -rcosz;
                         * }
                         * solarAMD = Math.Sqrt(rcosz * rcosz + 2 * radiusFactor + 1) - rcosz;*/

                        // alternative 2: clamp
                        if (rcosz < 0)
                        {
                            solarAMMult = 1 / Math.Sqrt(2 * vessel.mainBody.radiusAtmoFactor + 1);
                        }
                        else
                        {
                            solarAMMult = 1 / (Math.Sqrt(rcosz * rcosz + 2 * vessel.mainBody.radiusAtmoFactor + 1) - rcosz);
                        }

                        sunFinalMult = depthFactor * solarAMMult;


                        // get hypersonic convective shock temp
                        double machLerp = (vessel.mach - PhysicsGlobals.NewtonianMachTempLerpStartMach) / (PhysicsGlobals.NewtonianMachTempLerpEndMach - PhysicsGlobals.NewtonianMachTempLerpStartMach);
                        if (machLerp > 0)
                        {
                            machLerp = Math.Pow(machLerp, PhysicsGlobals.NewtonianMachTempLerpExponent);
                            machLerp = Math.Min(1d, machLerp);
                            double machExtTemp = Math.Pow(0.5d * vessel.convectiveMachFlux
                                                          / (PhysicsGlobals.StefanBoltzmanConstant * PhysicsGlobals.RadiationFactor), 0.25d);
                            shockTemp = Math.Max(shockTemp, UtilMath.LerpUnclamped(shockTemp, machExtTemp, machLerp));
                        }
                    }
                }

                // now body properties
                // Now calculate albedo and emissive fluxes, and body temperature under vessel
                double nightTempScalar = 0d;
                double bodyMinTemp     = 0d;
                double bodyMaxTemp     = 0d;
                if (vessel.mainBody.atmosphere)
                {
                    double baseTemp = vessel.mainBody.GetTemperature(0d);
                    bodyTemperature = baseTemp + atmosphereTemperatureOffset;
                    bodyMinTemp     = baseTemp + (vessel.mainBody.latitudeTemperatureBiasCurve.Evaluate(90f) // no lat sun mult since night
                                                  + vessel.mainBody.axialTemperatureSunMultCurve.Evaluate(-(float)vessel.mainBody.orbit.inclination));
                    bodyMaxTemp = baseTemp + (vessel.mainBody.latitudeTemperatureBiasCurve.Evaluate(0f)
                                              + (vessel.mainBody.latitudeTemperatureSunMultCurve.Evaluate(0f))
                                              + vessel.mainBody.axialTemperatureSunMultCurve.Evaluate((float)vessel.mainBody.orbit.inclination));

                    nightTempScalar = 1d - Math.Sqrt(bodyMaxTemp) * 0.0016d;
                    nightTempScalar = UtilMath.Clamp01(nightTempScalar);
                }
                else
                {
                    double spaceTemp4 = PhysicsGlobals.SpaceTemperature;
                    spaceTemp4 *= spaceTemp4;
                    spaceTemp4 *= spaceTemp4;

                    // now calculate two temperatures: the effective temp, and the maximum possible surface temperature
                    double sbERecip      = 1d / (PhysicsGlobals.StefanBoltzmanConstant * vessel.mainBody.emissivity);
                    double tmp           = bodySunFlux * (1d - vessel.mainBody.albedo) * sbERecip;
                    double effectiveTemp = Math.Pow(0.25d * tmp + spaceTemp4, 0.25d);
                    double fullSunTemp   = Math.Pow(tmp + spaceTemp4, 0.25d);

                    // now use some magic numbers to calculate minimum and maximum temperatures
                    double tempOffset = fullSunTemp - effectiveTemp;
                    bodyMaxTemp = effectiveTemp + Math.Sqrt(tempOffset) * 2d;
                    bodyMinTemp = effectiveTemp - Math.Pow(tempOffset, 1.1d) * 1.22d;

                    double lerpVal = 2d / Math.Sqrt(Math.Sqrt(vessel.mainBody.solarDayLength));
                    bodyMaxTemp = UtilMath.Lerp(bodyMaxTemp, effectiveTemp, lerpVal);
                    bodyMinTemp = UtilMath.Lerp(bodyMinTemp, effectiveTemp, lerpVal);

                    double latitudeLerpVal = Math.Max(0d, sunBodyMaxDot * 2d - 1d);
                    latitudeLerpVal = Math.Sqrt(latitudeLerpVal);
                    nightTempScalar = 1d - Math.Sqrt(bodyMaxTemp) * 0.0016d;
                    nightTempScalar = UtilMath.Clamp01(nightTempScalar);
                    double tempDiff  = (bodyMaxTemp - bodyMinTemp) * latitudeLerpVal;
                    double dayTemp   = bodyMinTemp + tempDiff;
                    double nightTemp = bodyMinTemp + tempDiff * nightTempScalar;
                    bodyTemperature = Math.Max(PhysicsGlobals.SpaceTemperature,
                                               nightTemp + (dayTemp - nightTemp) * sunDotNormalized + vessel.mainBody.coreTemperatureOffset);
                }

                // Quite a lerp. We need to get the lerp between front and back faces, then lerp between that and the top facing
                effectiveFaceTemp = UtilMath.LerpUnclamped(
                    UtilMath.LerpUnclamped(                              // horizontal component
                        UtilMath.LerpUnclamped(bodyMinTemp, bodyMaxTemp, // front face
                                               UtilMath.LerpUnclamped(bodyEmissiveScalarS0Front, bodyEmissiveScalarS1, nightTempScalar)),
                        UtilMath.LerpUnclamped(bodyMinTemp, bodyMaxTemp, // back face
                                               UtilMath.LerpUnclamped(bodyEmissiveScalarS0Back, bodyEmissiveScalarS1, nightTempScalar)),
                        sunDotNormalized),
                    UtilMath.LerpUnclamped(bodyMinTemp, bodyMaxTemp, UtilMath.LerpUnclamped(bodyEmissiveScalarS0Top, bodyEmissiveScalarS1Top, nightTempScalar)),
                    sunBodyMaxDot);

                double temp4 = UtilMath.Lerp(bodyTemperature, effectiveFaceTemp, 0.2d + vessel.altitude / vessel.mainBody.Radius * 0.5d);
                temp4 *= temp4;
                temp4 *= temp4;
                double bodyFluxScalar = (4d * Math.PI * vessel.mainBody.Radius * vessel.mainBody.Radius)
                                        / (4d * Math.PI * (vessel.mainBody.Radius + vessel.altitude) * (vessel.mainBody.Radius + vessel.altitude));
                bodyEmissiveFlux = PhysicsGlobals.StefanBoltzmanConstant * vessel.mainBody.emissivity * temp4 * bodyFluxScalar;
                bodyAlbedoFlux   = bodySunFlux * 0.5d * (sunDot + 1f) * vessel.mainBody.albedo * bodyFluxScalar;

                // density lerps
                bodyEmissiveFlux = UtilMath.Lerp(0d, bodyEmissiveFlux, densityThermalLerp);
                bodyAlbedoFlux   = UtilMath.Lerp(0d, bodyAlbedoFlux, densityThermalLerp);
            }
        }
        public void UpdateThermodynamicsPre(ModularFI.ModularFlightIntegrator fi)
        {
            wx_enabled = Util.getWindBool();
            use_climo  = Util.useCLIM();
            use_point  = Util.useWX();

            //Get reference to Kerbin (i.e. celestial body)
            kerbin = Util.getbody();

            //Get vessel position
            Vessel v       = FlightGlobals.ActiveVessel;
            double vheight = v.altitude;
            double vlat    = v.latitude;
            double vlng    = v.longitude;

            //Check to see if root part is in list. If not do not perform thermo update.
            //This avoids updating thermodynamics of parts that have been decoupled and are no longer part of the active vessel.

            bool hasPart = false;

            for (int i = 0; i < fi.PartThermalDataCount; i++)
            {
                PartThermalData pttd = fi.partThermalDataList[i];
                Part            part = pttd.part;
                if (part == v.rootPart)
                {
                    hasPart = true;
                }
            }

            if (!hasPart)
            {
                return;
            }

            //Start out with Stock Thermodynamics before performing adjustments due to wind/weather.
            fi.BaseFIUpdateThermodynamics();

            // Initialize External Temp and part drag/lift sum for GUI. //
            // This ensures GUI updates are smooth and don't bounce back and forth between stock aero and KWP aero updates //
            v.externalTemperature = fi.externalTemperature;

            if ((fi.CurrentMainBody != kerbin) || (v.altitude >= 70000))
            {
                return;
            }
            //Check if in atmosphere
            if (wx_enabled)
            {
                //Define gamma (ratio of cp/cv)
                double gamma = 1.4;

                if (use_climo)
                {
                    climate_api.wx_aero ptd = _clim_api.getPTD(vlat, vlng, vheight); //Retrieve pressure,temperature,and density from climate API
                    //Adjust atmospheric constants
                    CalculateConstantsAtmosphere_CLIMO(fi, ptd, v);
                }
                else
                {
                    weather_api.wx_aero ptd = _wx_api.getPTD(vheight); //Retrieve pressure,temperature,and density from climate API
                    //Adjust atmospheric constants
                    CalculateConstantsAtmosphere_WX(fi, ptd, v);
                }
                // change density lerp
                double shockDensity = GetShockDensity(fi.density, fi.mach, gamma);
                fi.DensityThermalLerp = CalculateDensityThermalLerp(shockDensity);
                double lerpVal = fi.dynamicPressurekPa;
                if (lerpVal < 1d)
                {
                    fi.convectiveCoefficient *= UtilMath.LerpUnclamped(0.1d, 1d, lerpVal);
                }

                // reset background temps
                fi.backgroundRadiationTemp        = CalculateBackgroundRadiationTemperature(fi.atmosphericTemperature, fi.DensityThermalLerp);
                fi.backgroundRadiationTempExposed = CalculateBackgroundRadiationTemperature(fi.externalTemperature, fi.DensityThermalLerp);
            }
            else if (!wx_enabled)
            {
                fi.BaseFIUpdateThermodynamics();
            }
        }
Exemplo n.º 15
0
        public override void GetAtmoThermalStats(bool getBodyFlux, CelestialBody sunBody, Vector3d sunVector, double sunDot, Vector3d upAxis, double altitude, out double atmosphereTemperatureOffset, out double bodyEmissiveFlux, out double bodyAlbedoFlux)
        {
            atmosphereTemperatureOffset = 0.0;
            bodyEmissiveFlux            = 0.0;
            bodyAlbedoFlux = 0.0;
            if (sunBody == this)
            {
                return;
            }
            Vector3d a    = sunBody.scaledBody.transform.position;
            Vector3  up   = bodyTransform.up;
            double   num  = (double)Vector3.Dot(sunVector, up);
            double   num2 = (double)Vector3.Dot(up, upAxis);
            double   num3 = Math.Acos(num2);

            if (double.IsNaN(num3))
            {
                double num4;
                if (!(num2 < 0.0))
                {
                    num4 = 0.0;
                }
                else
                {
                    num4 = 3.1415926535897931;
                }
                num3 = num4;
            }
            double num5 = Math.Acos(num);

            if (double.IsNaN(num5))
            {
                double num6;
                if (!(num < 0.0))
                {
                    num6 = 0.0;
                }
                else
                {
                    num6 = 3.1415926535897931;
                }
                num5 = num6;
            }
            double num7 = (1.0 + Math.Cos(num5 - num3)) * 0.5;
            double num8 = (1.0 + Math.Cos(num5 + num3)) * 0.5;

            if (num2 < 0.0)
            {
                num = 0.0 - num;
            }
            double num9  = num3;
            double num10 = num5;

            if (num3 > 1.5707963267948966)
            {
                num9  = 3.1415926535897931 - num9;
                num10 = 3.1415926535897931 - num10;
            }
            double sqrMagnitude = ((a - scaledBody.transform.position) * (double)ScaledSpace.ScaleFactor).sqrMagnitude;
            double num11        = PhysicsGlobals.SolarLuminosity / (12.566370614359172 * sqrMagnitude);
            double num12        = (1.0 + (double)Vector3.Dot(sunVector, Quaternion.AngleAxis(this.maxTempAngleOffset() * Mathf.Sign((float)rotationPeriod), up) * (Vector3)upAxis)) * 0.5;
            double num13        = num7 - num8;
            double num14;

            if (num13 > 0.001)
            {
                num14 = (num12 - num8) / num13;
                if (double.IsNaN(num14))
                {
                    if (num12 > 0.5)
                    {
                        num14 = 1.0;
                    }
                    else
                    {
                        num14 = 0.0;
                    }
                }
            }
            else
            {
                num14 = num8 + num13 * 0.5;
            }
            if (atmosphere)
            {
                float num15 = (float)(1.5707963267948966 - num9);
                num15 *= 57.29578f;
                CelestialBody bodyReferencing = GetBodyReferencing(this, sunBody);
                float         time            = ((float)bodyReferencing.orbit.trueAnomaly * 57.29578f + 360f) % 360f;
                double        num16           = (double)latitudeTemperatureBiasCurve.Evaluate(num15) + (double)latitudeTemperatureSunMultCurve.Evaluate(num15) * num14 + (double)(axialTemperatureSunBiasCurve.Evaluate(time) * axialTemperatureSunMultCurve.Evaluate(num15));
                double        num17;
                if (bodyReferencing.orbit.eccentricity != 0.0)
                {
                    num17 = (double)eccentricityTemperatureBiasCurve.Evaluate((float)((bodyReferencing.orbit.radius - bodyReferencing.orbit.PeR) / (bodyReferencing.orbit.ApR - bodyReferencing.orbit.PeR)));
                }
                else
                {
                    num17 = 0.0;
                }
                atmosphereTemperatureOffset = num16 + num17;
            }
            else
            {
                atmosphereTemperatureOffset = 0.0;
            }
            if (!getBodyFlux)
            {
                return;
            }
            double num18 = 0.0;
            double num19 = 0.0;
            double num20 = 0.0;
            double a2;

            if (atmosphere)
            {
                double temperature = GetTemperature(0.0);
                a2    = temperature + atmosphereTemperatureOffset;
                num19 = temperature + (double)(latitudeTemperatureBiasCurve.Evaluate(90f) + axialTemperatureSunMultCurve.Evaluate(0f - (float)orbit.inclination));
                num20 = temperature + (double)(latitudeTemperatureBiasCurve.Evaluate(0f) + latitudeTemperatureSunMultCurve.Evaluate(0f) + axialTemperatureSunMultCurve.Evaluate((float)orbit.inclination));
                num18 = 1.0 - Math.Sqrt(num20) * 0.0016;
                num18 = UtilMath.Clamp01(num18);
            }
            else
            {
                double spaceTemperature = PhysicsGlobals.SpaceTemperature;
                spaceTemperature *= spaceTemperature;
                spaceTemperature *= spaceTemperature;
                double num21 = 1.0 / (PhysicsGlobals.StefanBoltzmanConstant * emissivity);
                double num22 = num11 * (1.0 - albedo) * num21;
                double num23 = Math.Sqrt(Math.Sqrt(0.25 * num22 + spaceTemperature));
                double num24 = Math.Sqrt(Math.Sqrt(num22 + spaceTemperature)) - num23;
                num20 = num23 + Math.Sqrt(num24) * 2.0;
                num19 = num23 - Math.Pow(num24, 1.1) * 1.22;
                double t = 2.0 / Math.Sqrt(Math.Sqrt(solarDayLength));
                num20 = UtilMath.Lerp(num20, num23, t);
                num19 = UtilMath.Lerp(num19, num23, t);
                double d = Math.Max(0.0, num7 * 2.0 - 1.0);
                d     = Math.Sqrt(d);
                num18 = 1.0 - Math.Sqrt(num20) * 0.0016;
                num18 = UtilMath.Clamp01(num18);
                double num25 = (num20 - num19) * d;
                double num26 = num19 + num25;
                double num27 = num19 + num25 * num18;
                a2 = Math.Max(PhysicsGlobals.SpaceTemperature, num27 + (num26 - num27) * num14 + coreTemperatureOffset);
            }
            double b     = UtilMath.LerpUnclamped(UtilMath.LerpUnclamped(UtilMath.LerpUnclamped(num19, num20, UtilMath.LerpUnclamped(0.782048841, 0.87513007, num18)), UtilMath.LerpUnclamped(num19, num20, UtilMath.LerpUnclamped(0.093081228, 0.87513007, num18)), num14), UtilMath.LerpUnclamped(num19, num20, UtilMath.LerpUnclamped(0.398806364, 0.797612728, num18)), num7);
            double num28 = UtilMath.Lerp(a2, b, 0.2 + altitude / Radius * 0.5);

            num28 *= num28;
            num28 *= num28;
            double num29 = Radius * Radius / ((Radius + altitude) * (Radius + altitude));

            if (num29 > 1.0)
            {
                num29 = 1.0;
            }
            bodyEmissiveFlux = PhysicsGlobals.StefanBoltzmanConstant * emissivity * num28 * num29;
            bodyAlbedoFlux   = num11 * 0.5 * (sunDot + 1.0) * albedo * num29;
        }
Exemplo n.º 16
0
        public void GetThermalStatsPlus(Vessel vessel)
        {
            FlightIntegrator component = vessel.GetComponent <FlightIntegrator>();

            if (component == null)
            {
                return;
            }
            shockTemp         = vessel.externalTemperature;
            backgroundRadTemp = component.backgroundRadiationTemp;
            double  t   = component.CalculateDensityThermalLerp();
            Vector3 lhs = (Planetarium.fetch.Sun.scaledBody.transform.position - ScaledSpace.LocalToScaledSpace(vessel.transform.position)).normalized;

            solarFlux = vessel.solarFlux;
            if (Planetarium.fetch.Sun == vessel.mainBody)
            {
                return;
            }
            Vector3 vector = (Planetarium.fetch.Sun.scaledBody.transform.position - vessel.mainBody.scaledBody.transform.position) * ScaledSpace.ScaleFactor;
            double  num    = vector.sqrMagnitude;

            bodySunFlux = PhysicsGlobals.SolarLuminosity / (12.566370614359172 * num);
            sunDot      = Vector3.Dot(lhs, vessel.upAxis);
            Vector3 up   = vessel.mainBody.bodyTransform.up;
            float   num2 = Vector3.Dot(lhs, up);
            double  num3 = Vector3.Dot(up, vessel.upAxis);
            double  num4 = Math.Acos(num3);

            if (double.IsNaN(num4))
            {
                double num5;
                if (!(num3 < 0.0))
                {
                    num5 = 0.0;
                }
                else
                {
                    num5 = 3.1415926535897931;
                }
                num4 = num5;
            }
            double num6 = Math.Acos(num2);

            if (double.IsNaN(num6))
            {
                double num7;
                if (!(num2 < 0.0))
                {
                    num7 = 0.0;
                }
                else
                {
                    num7 = 3.1415926535897931;
                }
                num6 = num7;
            }
            double num8 = (1.0 + Math.Cos(num6 - num4)) * 0.5;
            double num9 = (1.0 + Math.Cos(num6 + num4)) * 0.5;

            if (num3 < 0.0)
            {
                num2 = 0f - num2;
            }
            double num10 = num4;
            double num11 = num6;

            if (num4 > 1.5707963267948966)
            {
                num10 = 3.1415926535897931 - num10;
                num11 = 3.1415926535897931 - num11;
            }
            double num12 = (1.0 + Vector3.Dot(lhs, Quaternion.AngleAxis(-(vessel.mainBody.maxTempAngleOffset()) * Mathf.Sign((float)vessel.mainBody.rotationPeriod), up) * vessel.upAxis)) * 0.5;
            double num13 = (num12 - num9) / (num8 - num9);

            if (double.IsNaN(num13))
            {
                if (num12 > 0.5)
                {
                    num13 = 1.0;
                }
                else
                {
                    num13 = 0.0;
                }
            }
            latitude  = 1.5707963267948966 - num10;
            latitude *= 57.295779513082323;
            if (vessel.mainBody.atmosphere)
            {
                float         time            = (float)latitude;
                CelestialBody bodyReferencing = CelestialBody.GetBodyReferencing(vessel.mainBody, FlightIntegrator.sunBody);
                diurnalRange = vessel.mainBody.latitudeTemperatureSunMultCurve.Evaluate(time);
                latTempMod   = vessel.mainBody.latitudeTemperatureBiasCurve.Evaluate(time);
                float time2 = ((float)bodyReferencing.orbit.trueAnomaly * 57.29578f + 360f) % 360f;
                axialTempMod = (vessel.mainBody.axialTemperatureSunBiasCurve.Evaluate(time2) * vessel.mainBody.axialTemperatureSunMultCurve.Evaluate(time));
                atmosphereTemperatureOffset = latTempMod + diurnalRange * num13 + axialTempMod + vessel.mainBody.eccentricityTemperatureBiasCurve.Evaluate((float)((bodyReferencing.orbit.radius - bodyReferencing.orbit.PeR) / (bodyReferencing.orbit.ApR - bodyReferencing.orbit.PeR)));
                altTempMult = vessel.mainBody.atmosphereTemperatureSunMultCurve.Evaluate((float)vessel.altitude);
                if (vessel.atmDensity > 0.0)
                {
                    finalAtmoMod = atmosphereTemperatureOffset * altTempMult;
                    double num14 = vessel.mainBody.radiusAtmoFactor * sunDot;
                    if (num14 < 0.0)
                    {
                        solarAMMult = Math.Sqrt(2.0 * vessel.mainBody.radiusAtmoFactor + 1.0);
                    }
                    else
                    {
                        solarAMMult = Math.Sqrt(num14 * num14 + 2.0 * vessel.mainBody.radiusAtmoFactor + 1.0) - num14;
                    }
                    sunFinalMult = vessel.mainBody.GetSolarPowerFactor(vessel.atmDensity * solarAMMult);
                    double num15 = (vessel.mach - PhysicsGlobals.NewtonianMachTempLerpStartMach) / (PhysicsGlobals.NewtonianMachTempLerpEndMach - PhysicsGlobals.NewtonianMachTempLerpStartMach);
                    if (num15 > 0.0)
                    {
                        num15 = Math.Pow(num15, PhysicsGlobals.NewtonianMachTempLerpExponent);
                        num15 = Math.Min(1.0, num15);
                        double b = Math.Pow(0.5 * vessel.convectiveMachFlux / (PhysicsGlobals.StefanBoltzmanConstant * PhysicsGlobals.RadiationFactor), 0.25);
                        shockTemp = Math.Max(shockTemp, UtilMath.LerpUnclamped(shockTemp, b, num15));
                    }
                }
            }
            double num16 = 0.0;
            double num17 = 0.0;
            double num18 = 0.0;

            if (vessel.mainBody.atmosphere)
            {
                double temperature = vessel.mainBody.GetTemperature(0.0);
                bodyTemperature = temperature + atmosphereTemperatureOffset;
                num17           = temperature + (vessel.mainBody.latitudeTemperatureBiasCurve.Evaluate(90f) + vessel.mainBody.axialTemperatureSunMultCurve.Evaluate(0f - (float)vessel.mainBody.orbit.inclination));
                num18           = temperature + (vessel.mainBody.latitudeTemperatureBiasCurve.Evaluate(0f) + vessel.mainBody.latitudeTemperatureSunMultCurve.Evaluate(0f) + vessel.mainBody.axialTemperatureSunMultCurve.Evaluate((float)vessel.mainBody.orbit.inclination));
                num16           = 1.0 - Math.Sqrt(num18) * 0.0016;
                num16           = UtilMath.Clamp01(num16);
            }
            else
            {
                double spaceTemperature = PhysicsGlobals.SpaceTemperature;
                spaceTemperature *= spaceTemperature;
                spaceTemperature *= spaceTemperature;
                double num19 = 1.0 / (PhysicsGlobals.StefanBoltzmanConstant * vessel.mainBody.emissivity);
                double num20 = bodySunFlux * (1.0 - vessel.mainBody.albedo) * num19;
                double num21 = Math.Pow(0.25 * num20 + spaceTemperature, 0.25);
                double num22 = Math.Pow(num20 + spaceTemperature, 0.25) - num21;
                num18 = num21 + Math.Sqrt(num22) * 2.0;
                num17 = num21 - Math.Pow(num22, 1.1) * 1.22;
                double t2 = 2.0 / Math.Sqrt(Math.Sqrt(vessel.mainBody.solarDayLength));
                num18 = UtilMath.Lerp(num18, num21, t2);
                num17 = UtilMath.Lerp(num17, num21, t2);
                double d = Math.Max(0.0, num8 * 2.0 - 1.0);
                d     = Math.Sqrt(d);
                num16 = 1.0 - Math.Sqrt(num18) * 0.0016;
                num16 = UtilMath.Clamp01(num16);
                double num23 = (num18 - num17) * d;
                double num24 = num17 + num23;
                double num25 = num17 + num23 * num16;
                bodyTemperature = Math.Max(PhysicsGlobals.SpaceTemperature, num25 + (num24 - num25) * num13 + vessel.mainBody.coreTemperatureOffset);
            }
            effectiveFaceTemp = UtilMath.LerpUnclamped(UtilMath.LerpUnclamped(UtilMath.LerpUnclamped(num17, num18, UtilMath.LerpUnclamped(0.782048841, 0.87513007, num16)), UtilMath.LerpUnclamped(num17, num18, UtilMath.LerpUnclamped(0.093081228, 0.87513007, num16)), num13), UtilMath.LerpUnclamped(num17, num18, UtilMath.LerpUnclamped(0.398806364, 0.797612728, num16)), num8);
            double num26 = UtilMath.Lerp(bodyTemperature, effectiveFaceTemp, 0.2 + vessel.altitude / vessel.mainBody.Radius * 0.5);

            num26 *= num26;
            num26 *= num26;
            double num27 = 12.566370614359172 * vessel.mainBody.Radius * vessel.mainBody.Radius / (12.566370614359172 * (vessel.mainBody.Radius + vessel.altitude) * (vessel.mainBody.Radius + vessel.altitude));

            bodyEmissiveFlux = PhysicsGlobals.StefanBoltzmanConstant * vessel.mainBody.emissivity * num26 * num27;
            bodyAlbedoFlux   = bodySunFlux * 0.5 * (sunDot + 1.0) * vessel.mainBody.albedo * num27;
            bodyEmissiveFlux = UtilMath.Lerp(0.0, bodyEmissiveFlux, t);
            bodyAlbedoFlux   = UtilMath.Lerp(0.0, bodyAlbedoFlux, t);
            int num28 = vessel.Parts.Count;

            while (num28-- > 0)
            {
                convectiveTotal += vessel.Parts[num28].thermalConvectionFlux * dTime;
            }
        }
Exemplo n.º 17
0
        public override void CalculatePerformance(double airRatio, double commandedThrottle, double flowMult, double ispMult)
        {
            mixtureRatioVariance = 0d;

            // set base bits
            base.CalculatePerformance(airRatio, commandedThrottle, flowMult, ispMult);
            M0 = mach;

            // Calculate Isp (before the shutdown check, so it displays even then)
            Isp = atmosphereCurve.Evaluate((float)(p0 * 0.001d * PhysicsGlobals.KpaToAtmospheres)) * ispMult;

            // if we're not combusting, don't combust and start cooling off
            combusting   = running;
            statusString = "Nominal";

            // ullage check first, overwrite if bad pressure or no propellants
            if (!ullage)
            {
                combusting   = false;
                statusString = "Vapor in feed line";
            }

            // check fuel flow fraction
            if (ffFraction <= 0d)
            {
                combusting   = false;
                statusString = "Flameout";
            }
            // check pressure
            if (!pressure)
            {
                combusting   = false;
                statusString = "Lack of pressure";
            }

            if (disableUnderwater && underwater)
            {
                combusting   = false;
                statusString = "Underwater";
            }

            // check flow mult
            double fuelFlowMult = FlowMult();

            if (fuelFlowMult < flowMultMin)
            {
                combusting   = false;
                statusString = "Airflow outside specs";
            }

            // FIXME handle engine spinning down, non-instant shutoff.
            if (commandedThrottle <= 0d)
            {
                combusting = false;
            }

            if (!wasCombusting && combusting)
            {
                // Reset run-to-run variances
                double vFlow, vIsp, vMR;
                GetVariances(false, out vFlow, out vMR, out vIsp);
                runVaryFlow = baseVaryFlow + VarianceRun * varyFlow * vFlow;
                runVaryIsp  = baseVaryIsp + VarianceRun * varyIsp * vIsp;
                runVaryMR   = baseVaryMR + VarianceRun * varyMR * vMR;
            }

            if (!combusting)
            {
                double declinePow = Math.Pow(tempDeclineRate, TimeWarp.fixedDeltaTime);
                // removed t0 from next calculation; under some circumstances t0 can spike during staging/decoupling resulting in engine part destruction even on an unfired engine.
                chamberTemp = Math.Max(partTemperature, chamberTemp * declinePow);
                fxPower     = 0f;
            }
            else
            {
                if (HighLogic.LoadedSceneIsFlight)
                {
                    mixtureRatioVariance = runVaryMR;
                }

                // get current flow, and thus thrust.
                fuelFlow = scale * flowMult * maxFlow * commandedThrottle * thrustRatio;
                double perlin = 0d;
                if (HighLogic.LoadedSceneIsFlight && (varyFlow > 0 || varyIsp > 0))
                {
                    perlin = Mathf.PerlinNoise(Time.time, timeOffset) * 2d - 1d;
                }

                if (HighLogic.LoadedSceneIsFlight && varyFlow > 0d && fuelFlow > 0d)
                {
                    fuelFlow *= (1d + runVaryFlow) * (1d + perlin * varyFlow * VarianceDuring);
                }

                // FIXME fuel flow is actually wrong, since mixture ratio varies now. Either need to fix MR for constant flow,
                // or fix fuel flow here in light of MR. But it's mostly just a visual bug, since the variation will be fine in most cases.

                // apply fuel flow multiplier
                double ffMult = fuelFlow * fuelFlowMult;
                fuelFlow = ffMult;

                double ispOtherMult = 1d;
                if (atmCurveIsp != null)
                {
                    ispOtherMult *= atmCurveIsp.Evaluate((float)(rho * (1d / 1.225d)));
                }
                if (velCurveIsp != null)
                {
                    ispOtherMult *= velCurveIsp.Evaluate((float)mach);
                }

                if (HighLogic.LoadedSceneIsFlight && varyIsp > 0d && fuelFlow > 0d)
                {
                    ispOtherMult *= (1d + runVaryIsp) * (1d + perlin * varyIsp * VarianceDuring);
                }

                Isp *= ispOtherMult;

                fxPower = (float)(fuelFlow * maxFlowRecip * ispMult * ispOtherMult); // FX is proportional to fuel flow and Isp mult.

                double exhaustVelocity = Isp * 9.80665d;
                SFC = 3600d / Isp;

                thrust = ffMult * exhaustVelocity; // either way, thrust is base * mult * EV

                // Calculate chamber temperature as ratio
                double desiredTempRatio = Math.Max(tempMin, fxPower);
                double machTemp         = MachTemp() * 0.05d;
                desiredTempRatio = desiredTempRatio * (1d + machTemp) + machTemp;

                // set temp based on desired
                double desiredTemp = desiredTempRatio * chamberNominalTemp;
                if (Math.Abs(desiredTemp - chamberTemp) < 1d)
                {
                    chamberTemp = desiredTemp;
                }
                else
                {
                    double lerpVal = UtilMath.Clamp01(tempLerpRate * TimeWarp.fixedDeltaTime);
                    chamberTemp = UtilMath.LerpUnclamped(chamberTemp, desiredTemp, lerpVal);
                }
            }
            fxThrottle = combusting ? (float)throttle : 0f;
        }
Exemplo n.º 18
0
        public void Update(Vector3d localAcceleration, Vector3d rotation, double deltaTime, double ventingAcc, double fuelRatio)
        {
            double utTimeDelta = deltaTime;

            if (Planetarium.fetch)
            {
                double newUT = Planetarium.GetUniversalTime();
                utTimeDelta = newUT - UT;
                UT          = newUT;
            }

            double fuelRatioFactor      = (0.5d + fuelRatio) * (1d / 1.4d);
            double fuelRatioFactorRecip = 1.0d / fuelRatioFactor;

            double accSqrMag    = localAcceleration.sqrMagnitude;
            double accThreshSqr = RFSettings.Instance.naturalDiffusionAccThresh;

            accThreshSqr *= accThreshSqr; // square it to compare to sqrMag.

            //if (ventingAcc != 0.0f) Debug.Log("BoilOffAcc: " + ventingAcc.ToString("F8"));
            //else Debug.Log("BoilOffAcc: No boiloff.");

            Vector3d localAccelerationAmount = localAcceleration * deltaTime;
            Vector3d rotationAmount          = rotation * deltaTime;

            //Debug.Log("Ullage: dt: " + deltaTime.ToString("F2") + " localAcc: " + localAcceleration.ToString() + " rotateRate: " + rotation.ToString());

            // Natural diffusion.
            //Debug.Log("Ullage: LocalAcc: " + localAcceleration.ToString());
            if (ventingAcc <= RFSettings.Instance.ventingAccThreshold && accSqrMag < accThreshSqr)
            {
                double ventingConst = Math.Min(1d, (1d - ventingAcc / RFSettings.Instance.ventingAccThreshold) * fuelRatioFactorRecip * utTimeDelta);
                ullageHeightMin = UtilMath.LerpUnclamped(ullageHeightMin, 0.05d, RFSettings.Instance.naturalDiffusionRateY * ventingConst);
                ullageHeightMax = UtilMath.LerpUnclamped(ullageHeightMax, 0.95d, RFSettings.Instance.naturalDiffusionRateY * ventingConst);
                ullageRadialMin = UtilMath.LerpUnclamped(ullageRadialMin, 0.00d, RFSettings.Instance.naturalDiffusionRateX * ventingConst);
                ullageRadialMax = UtilMath.LerpUnclamped(ullageRadialMax, 0.95d, RFSettings.Instance.naturalDiffusionRateX * ventingConst);
            }

            // Translate forward/backward.
            double radialFac = Math.Abs(localAccelerationAmount.y) * RFSettings.Instance.translateAxialCoefficientX * fuelRatioFactor;
            double heightFac = localAccelerationAmount.y * RFSettings.Instance.translateAxialCoefficientY * fuelRatioFactor;

            ullageHeightMin = UtilMath.Clamp(ullageHeightMin + heightFac, 0.0d, 0.9d);
            ullageHeightMax = UtilMath.Clamp(ullageHeightMax + heightFac, 0.1d, 1.0d);
            ullageRadialMin = UtilMath.Clamp(ullageRadialMin - radialFac, 0.0d, 0.9d);
            ullageRadialMax = UtilMath.Clamp(ullageRadialMax + radialFac, 0.1d, 1.0d);

            // Translate up/down/left/right.
            Vector3d sideAcc    = new Vector3d(localAccelerationAmount.x, 0.0d, localAccelerationAmount.z);
            double   sideFactor = sideAcc.magnitude * fuelRatioFactor;
            double   sideY      = sideFactor * RFSettings.Instance.translateSidewayCoefficientY;
            double   sideX      = sideFactor * RFSettings.Instance.translateSidewayCoefficientX;

            ullageHeightMin = UtilMath.Clamp(ullageHeightMin - sideY, 0.0d, 0.9d);
            ullageHeightMax = UtilMath.Clamp(ullageHeightMax + sideY, 0.1d, 1.0d);
            ullageRadialMin = UtilMath.Clamp(ullageRadialMin + sideX, 0.0d, 0.9d);
            ullageRadialMax = UtilMath.Clamp(ullageRadialMax + sideX, 0.1d, 1.0d);

            // Rotate yaw/pitch.
            Vector3d rotateYawPitch = new Vector3d(rotation.x, 0.0d, rotation.z);
            double   mag            = rotateYawPitch.magnitude;
            double   ypY            = mag * RFSettings.Instance.rotateYawPitchCoefficientY;
            double   ypX            = mag * RFSettings.Instance.rotateYawPitchCoefficientX;

            if (ullageHeightMin < 0.45d)
            {
                ullageHeightMin = UtilMath.Clamp(ullageHeightMin + ypY, 0.0d, 0.45d);
            }
            else
            {
                ullageHeightMin = UtilMath.Clamp(ullageHeightMin - ypY, 0.45d, 0.9d);
            }

            if (ullageHeightMax < 0.55d)
            {
                ullageHeightMax = UtilMath.Clamp(ullageHeightMax + ypY, 0.1d, 0.55d);
            }
            else
            {
                ullageHeightMax = UtilMath.Clamp(ullageHeightMax - ypY, 0.55d, 1.0d);
            }

            ullageRadialMin = UtilMath.Clamp(ullageRadialMin - ypX, 0.0d, 0.9d);
            ullageRadialMax = UtilMath.Clamp(ullageRadialMax + ypX, 0.1d, 1.0d);

            // Rotate roll.
            double absRot  = Math.Abs(rotationAmount.y) * fuelRatioFactor;
            double absRotX = absRot * RFSettings.Instance.rotateRollCoefficientX;
            double absRotY = absRot * RFSettings.Instance.rotateRollCoefficientY;

            ullageHeightMin = UtilMath.Clamp(ullageHeightMin - absRotY, 0.0d, 0.9d);
            ullageHeightMax = UtilMath.Clamp(ullageHeightMax + absRotY, 0.1d, 1.0d);
            ullageRadialMin = UtilMath.Clamp(ullageRadialMin - absRotX, 0.0d, 0.9d);
            ullageRadialMax = UtilMath.Clamp(ullageRadialMax - absRotX, 0.1d, 1.0d);

            //Debug.Log("Ullage: Height: (" + ullageHeightMin.ToString("F2") + " - " + ullageHeightMax.ToString("F2") + ") Radius: (" + ullageRadialMin.ToString("F2") + " - " + ullageRadialMax.ToString("F2") + ")");

            double bLevel = UtilMath.Clamp((ullageHeightMax - ullageHeightMin) * (ullageRadialMax - ullageRadialMin) * 10d * UtilMath.Clamp(8.2d - 8d * fuelRatio, 0.0d, 8.2d) - 1.0d, 0.0d, 15.0d);
            //Debug.Log("Ullage: bLevel: " + bLevel.ToString("F3"));

            double pVertical = UtilMath.Clamp01(1.0d - (ullageHeightMin - 0.1d) * 5d);
            //Debug.Log("Ullage: pVertical: " + pVertical.ToString("F3"));

            double pHorizontal = UtilMath.Clamp01(1.0d - (ullageRadialMin - 0.1d) * 5d);

            //Debug.Log("Ullage: pHorizontal: " + pHorizontal.ToString("F3"));

            propellantStability = Math.Max(0.0d, 1.0d - (pVertical * pHorizontal * (0.75d + Math.Sqrt(bLevel))));

#if DEBUG
            if (propellantStability < 0.5d)
            {
                MonoBehaviour.print("*US* for part " + name + ", low stability of " + propellantStability
                                    + "\npV/H = " + pVertical + "/" + pHorizontal + ", blevel " + bLevel
                                    + "\nUllage Height Min/Max " + ullageHeightMin + "/" + ullageHeightMax + ", Radial Min/Max " + ullageRadialMin + "/" + ullageRadialMax
                                    + "\nInputs: Time = " + deltaTime + ", UT delta = " + utTimeDelta + ", Acc " + localAcceleration + ", Rot " + rotation + ", FR " + fuelRatio);
            }
#endif

            SetStateString();
        }
Exemplo n.º 19
0
        public override void CalculatePerformance(double airRatio, double commandedThrottle, double flowMult, double ispMult)
        {
            // set base bits
            base.CalculatePerformance(airRatio, commandedThrottle, flowMult, ispMult);
            M0 = mach;

            // Calculate Isp (before the shutdown check, so it displays even then)
            Isp = atmosphereCurve.Evaluate((float)(p0 * 0.001d * PhysicsGlobals.KpaToAtmospheres)) * ispMult;

            // if we're not combusting, don't combust and start cooling off
            combusting   = running;
            statusString = "Nominal";

            // ullage check first, overwrite if bad pressure or no propellants
            if (!ullage)
            {
                combusting   = false;
                statusString = "Vapor in feed line";
            }

            // check fuel flow fraction
            if (ffFraction <= 0d)
            {
                combusting   = false;
                statusString = "No propellants";
            }
            // check pressure
            if (!pressure)
            {
                combusting   = false;
                statusString = "Lack of pressure";
            }

            if (disableUnderwater && underwater)
            {
                combusting   = false;
                statusString = "Underwater";
            }

            // check flow mult
            double fuelFlowMult = FlowMult();

            if (fuelFlowMult < flowMultMin)
            {
                combusting   = false;
                statusString = "Airflow outside specs";
            }

            if (commandedThrottle <= 0d)
            {
                combusting = false;
            }

            if (!combusting)
            {
                double declinePow = Math.Pow(tempDeclineRate, TimeWarp.fixedDeltaTime);
                // removed t0 from next calculation; under some circumstances t0 can spike during staging/decoupling resulting in engine part destruction even on an unfired engine.
                chamberTemp = Math.Max(partTemperature, chamberTemp * declinePow);
                fxPower     = 0f;
            }
            else
            {
                // get current flow, and thus thrust.
                fuelFlow = scale * flowMult * maxFlow * commandedThrottle * thrustRatio;

                if (varyThrust > 0d && fuelFlow > 0d && HighLogic.LoadedSceneIsFlight)
                {
                    fuelFlow *= (1d + (Mathf.PerlinNoise(Time.time, 0f) * 2d - 1d) * varyThrust);
                }

                fxPower = (float)(fuelFlow * maxFlowRecip * ispMult); // FX is proportional to fuel flow and Isp mult.

                // apply fuel flow multiplier
                double ffMult = fuelFlow * fuelFlowMult;
                fuelFlow = ffMult;

                if (atmCurveIsp != null)
                {
                    Isp *= atmCurveIsp.Evaluate((float)(rho * (1d / 1.225d)));
                }
                if (velCurveIsp != null)
                {
                    Isp *= velCurveIsp.Evaluate((float)mach);
                }

                double exhaustVelocity = Isp * 9.80665d;
                SFC = 3600d / Isp;

                thrust = ffMult * exhaustVelocity; // either way, thrust is base * mult * EV

                // Calculate chamber temperature as ratio
                double desiredTempRatio = Math.Max(tempMin, fxPower);
                double machTemp         = MachTemp() * 0.05d;
                desiredTempRatio = desiredTempRatio * (1d + machTemp) + machTemp;

                // set temp based on desired
                double desiredTemp = desiredTempRatio * chamberNominalTemp;
                if (Math.Abs(desiredTemp - chamberTemp) < 1d)
                {
                    chamberTemp = desiredTemp;
                }
                else
                {
                    double lerpVal = UtilMath.Clamp01(tempLerpRate * TimeWarp.fixedDeltaTime);
                    chamberTemp = UtilMath.LerpUnclamped(chamberTemp, desiredTemp, lerpVal);
                }
            }
            fxThrottle = combusting ? (float)throttle : 0f;
        }