Skip to content

Commit 45c4272

Browse files
committed
Adjustments to injector water delivery
1 parent e4d7f70 commit 45c4272

File tree

1 file changed

+95
-16
lines changed

1 file changed

+95
-16
lines changed

Source/Orts.Simulation/Simulation/RollingStocks/MSTSSteamLocomotive.cs

Lines changed: 95 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,17 @@ public class MSTSSteamLocomotive : MSTSLocomotive
153153
float RefKInjector1Factor;
154154
float RefKInjector2Factor;
155155

156+
float MaxRefInjector1PressurePSI;
157+
float MaxRefInjector2PressurePSI;
158+
float MaxRefInjector1FlowRateUKGpH;
159+
float MaxRefInjector2FlowRateUKGpH;
160+
float UpperWaterDeliveryInjector1EfficiencyFactor;
161+
float UpperWaterDeliveryInjector2EfficiencyFactor;
162+
float UpperAlphaInjector1Factor;
163+
float UpperAlphaInjector2Factor;
164+
float UpperBetaInjector1Factor;
165+
float UpperBetaInjector2Factor;
166+
156167
float ActualInjector1NozzleSizeMM;
157168
float ActualInjector2NozzleSizeMM;
158169
float ActualInjectorTemperatureCorrectionFactor;
@@ -2052,7 +2063,7 @@ public override void Initialize()
20522063

20532064
// Set up Injectors
20542065
// To calculate the maximum combined water delivery rate from the injectors at maximum boiler pressure, the formula is used:
2055-
// WaterFlowRateUKGpM = K * A * P^0.5, k is calculated using known flow rates from manuafacturers manuals.
2066+
// WaterFlowRateUKGpM = K * A * P^0.5, k is calculated using known flow rates from manuafacturers manuals, and rearranging the formula.
20562067
// If user does not enter any values then the D&M 11mm injector is assumed to be operating at 200 psi .
20572068
// http://users.fini.net/~bersano/english-anglais/english-anglais/NZ-accessories/D&M_Type_J_Exhaust_steam_injector.pdf
20582069
//
@@ -2098,14 +2109,62 @@ public override void Initialize()
20982109
Trace.TraceInformation("RefInjector2FlowRateUKGpH not specified in ENG file. Defaulting to {0} UKG/h", RefInjector2FlowRateUKGpH);
20992110
}
21002111

2101-
// Calculate K values for injector flow rate calculations
2112+
if (MaxRefInjector1FlowRateUKGpH == 0)
2113+
{
2114+
MaxRefInjector1FlowRateUKGpH = 2600;
2115+
if (Simulator.Settings.VerboseConfigurationMessages)
2116+
Trace.TraceInformation("MaxInjector1FlowRateUKGpH not specified in ENG file. Defaulting to {0} UKG/h", MaxRefInjector1FlowRateUKGpH);
2117+
}
2118+
2119+
if (MaxRefInjector2FlowRateUKGpH == 0)
2120+
{
2121+
MaxRefInjector2FlowRateUKGpH = 2600;
2122+
if (Simulator.Settings.VerboseConfigurationMessages)
2123+
Trace.TraceInformation("MaxInjector2FlowRateUKGpH not specified in ENG file. Defaulting to {0} UKG/h", MaxRefInjector2FlowRateUKGpH);
2124+
}
2125+
2126+
if(MaxRefInjector1PressurePSI == 0)
2127+
{
2128+
MaxRefInjector1PressurePSI = 300.0f; // Default to D&M 11mm injector operating pressure
2129+
if (Simulator.Settings.VerboseConfigurationMessages)
2130+
Trace.TraceInformation("MaxRefInjector1SteamPressurePSI not specified in ENG file. Defaulting to {0} psi", MaxRefInjector1PressurePSI);
2131+
}
2132+
2133+
if (MaxRefInjector2PressurePSI == 0)
2134+
{
2135+
MaxRefInjector2PressurePSI = 300.0f; // Default to D&M 11mm injector operating pressure
2136+
if (Simulator.Settings.VerboseConfigurationMessages)
2137+
Trace.TraceInformation("MaxRefInjector2SteamPressurePSI not specified in ENG file. Defaulting to {0} psi", MaxRefInjector2PressurePSI);
2138+
}
2139+
2140+
// Calculate K values for injector delivery flow rate calculations - up to to optimum (maximum delivery rate)
21022141

21032142
var RefInjectorNozzle1AreaMM2 = (float)(Math.PI * Math.Pow((RefInjector1NozzleSizeMM) / 2.0f, 2));
21042143
RefKInjector1Factor = RefInjector1FlowRateUKGpH / (float)(RefInjectorNozzle1AreaMM2 * Math.Sqrt(RefInjector1PressurePSI));
21052144

21062145
var RefInjectorNozzle2AreaMM2 = (float)(Math.PI * Math.Pow((RefInjector2NozzleSizeMM) / 2.0f, 2));
21072146
RefKInjector2Factor = RefInjector2FlowRateUKGpH / (float)(RefInjectorNozzle2AreaMM2 * Math.Sqrt(RefInjector2PressurePSI));
21082147

2148+
// calculate relevant factors for delivery flow rates above optimum delivery rate
2149+
// Calculation based upon a quadratic equation to fit the known data points, with the user setting a known point above the optimum delivery rate.
2150+
// Effective delivery @ P pressure - Qeff(P)= Qbase(P)* η(P) at current Boiler Pressure
2151+
// Delivery @ P - Qbase(P) = Qopt √(P/Popt)
2152+
// Efficiency factor is calculated by η(P)=1-β(P-Popt)-α(P-Popt)^2
2153+
// Following section calculates the constants required to model the efficiency curve above optimum delivery rate
2154+
// β=1/(2 Popt )
2155+
// η(Pmax)= (Qeff(Popt))/(Qbase(Pmax))
2156+
// α= [1 - β(Pmax - Popt) - η(Pmax)] / (Pmax - Popt)^2
2157+
2158+
UpperBetaInjector1Factor = 1 / (2 * RefInjector1PressurePSI);
2159+
var DeliveryBaseInjector1Pmax = RefInjector1FlowRateUKGpH * Math.Sqrt(MaxRefInjector1PressurePSI / RefInjector1PressurePSI);
2160+
var EfficiencyBaseInjector1atMax = MaxRefInjector1FlowRateUKGpH / DeliveryBaseInjector1Pmax;
2161+
UpperAlphaInjector1Factor = (float)(1.0 - UpperBetaInjector1Factor * (MaxRefInjector1PressurePSI - RefInjector1PressurePSI) - EfficiencyBaseInjector1atMax) / (float)Math.Pow((MaxRefInjector1PressurePSI - RefInjector1PressurePSI), 2);
2162+
2163+
UpperBetaInjector2Factor = 1 / (2 * RefInjector2PressurePSI);
2164+
var DeliveryBaseInjector2Pmax = RefInjector2FlowRateUKGpH * Math.Sqrt(MaxRefInjector2PressurePSI / RefInjector2PressurePSI);
2165+
var EfficiencyBaseInjector2atMax = MaxRefInjector2FlowRateUKGpH / DeliveryBaseInjector2Pmax;
2166+
UpperAlphaInjector2Factor = (float)(1.0 - UpperBetaInjector2Factor * (MaxRefInjector2PressurePSI - RefInjector2PressurePSI) - EfficiencyBaseInjector2atMax) / (float)Math.Pow((MaxRefInjector2PressurePSI - RefInjector2PressurePSI), 2);
2167+
21092168
// Calculate Injector sizing, a single injector should be able to cope with 75% of the maximum water delivery rate required at full steam usage.
21102169

21112170
if (ActualInjector1NozzleSizeMM == 0)
@@ -7458,25 +7517,45 @@ private void UpdateWaterInjection(float elapsedClockSeconds)
74587517

74597518
// Calculate the flow rates for injector #1 based upon boiler pressure and injector nozzle size
74607519
var ActualInjector1NozzleAreaMM2 = (float)(Math.PI * Math.Pow((ActualInjector1NozzleSizeMM) / 2.0f, 2));
7461-
7520+
74627521
MaxInjectorFlowRateLBpS = ActualInjectorTemperatureCorrectionFactor * pS.FrompH(RefKInjector1Factor * ActualInjector1NozzleAreaMM2 * (float)Math.Sqrt(MaxBoilerPressurePSI)) * WaterLBpUKG;
74637522

7464-
ActualInjector1FlowRateLBpS = ActualInjectorTemperatureCorrectionFactor * pS.FrompH(RefKInjector1Factor * ActualInjector1NozzleAreaMM2 * (float)Math.Sqrt(BoilerPressurePSI)) * WaterLBpUKG;
7523+
// Injector #1 - calculate flow rate depending upon whether pressure is above or below optimum pressure
7524+
if (BoilerPressurePSI <= RefInjector1PressurePSI)
7525+
{
7526+
ActualInjector1FlowRateLBpS = ActualInjectorTemperatureCorrectionFactor * pS.FrompH(RefKInjector1Factor * ActualInjector1NozzleAreaMM2 * (float)Math.Sqrt(BoilerPressurePSI)) * WaterLBpUKG;
7527+
}
7528+
else
7529+
{
7530+
float InjectorDeliveryEfficincyatP = (float)(1 - UpperBetaInjector1Factor * (BoilerPressurePSI - RefInjector1PressurePSI) - UpperAlphaInjector1Factor * Math.Pow(BoilerPressurePSI - RefInjector1PressurePSI, 2));
7531+
float DeliveryBaseFlowRateatP = (float)(RefInjector1FlowRateUKGpH * Math.Sqrt(BoilerPressurePSI / RefInjector1PressurePSI));
7532+
ActualInjector1FlowRateLBpS = pS.FrompH(DeliveryBaseFlowRateatP * InjectorDeliveryEfficincyatP) * WaterLBpUKG;
7533+
}
74657534

74667535
// Calculate the flow rates for injector #2 based upon boiler pressure and injector nozzle size
74677536
var ActualInjector2NozzleAreaMM2 = (float)(Math.PI * Math.Pow((ActualInjector1NozzleSizeMM) / 2.0f, 2));
74687537

74697538
MaxInjectorFlowRateLBpS = pS.FrompH(RefKInjector2Factor * ActualInjector1NozzleAreaMM2 * (float)Math.Sqrt(MaxBoilerPressurePSI)) * WaterLBpUKG;
74707539

7471-
ActualInjector2FlowRateLBpS = pS.FrompH(RefKInjector2Factor * ActualInjector1NozzleAreaMM2 * (float)Math.Sqrt(BoilerPressurePSI)) * WaterLBpUKG;
7540+
// Injector #2 - calculate flow rate depending upon whether pressure is above or below optimum pressure
7541+
if (BoilerPressurePSI <= RefInjector2PressurePSI)
7542+
{
7543+
ActualInjector2FlowRateLBpS = pS.FrompH(RefKInjector2Factor * ActualInjector1NozzleAreaMM2 * (float)Math.Sqrt(BoilerPressurePSI)) * WaterLBpUKG;
7544+
}
7545+
else
7546+
{
7547+
float InjectorDeliveryEfficincyatP = (float)(1 - UpperBetaInjector2Factor * (BoilerPressurePSI - RefInjector2PressurePSI) - UpperAlphaInjector2Factor * Math.Pow(BoilerPressurePSI - RefInjector2PressurePSI, 2));
7548+
float DeliveryBaseFlowRateatP = (float)(RefInjector2FlowRateUKGpH * Math.Sqrt(BoilerPressurePSI / RefInjector2PressurePSI));
7549+
ActualInjector2FlowRateLBpS = pS.FrompH(DeliveryBaseFlowRateatP * InjectorDeliveryEfficincyatP) * WaterLBpUKG;
7550+
}
74727551

74737552
// Calculate the temperature correction factor as the feedwater temperature will affect the flow rate and the hence the steam used
74747553
// Actual Delivery Capacity = Base Delivery * Temperature Correction Factor
74757554
// Actual Steam used = Base Steam used * Temperature Correction Factor
74767555
// Temperature Correction Factor = (Tsat - Tfeedwater) / (Tsat - Tref), Assume ref = 20C
74777556

74787557
var ReferenceTemperature = 20.0f; // Reference temperature for injector performance
7479-
ActualInjectorTemperatureCorrectionFactor = (float)Math.Sqrt(( C.FromF(PressureToTemperaturePSItoF[BoilerPressurePSI]) - TenderWaterTemperatureC) / (C.FromF(PressureToTemperaturePSItoF[BoilerPressurePSI]) - ReferenceTemperature));
7558+
ActualInjectorTemperatureCorrectionFactor = (float)Math.Sqrt((C.FromF(PressureToTemperaturePSItoF[BoilerPressurePSI]) - TenderWaterTemperatureC) / (C.FromF(PressureToTemperaturePSItoF[BoilerPressurePSI]) - ReferenceTemperature));
74807559

74817560
// Calculate enthalpy of injector feed water based on boiler water temperature
74827561
// hfg kJ/kg) = 2500 - 2.4 * Tsat (C) - 4% accurate, to be explored later for better accuracy
@@ -7694,25 +7773,25 @@ private void UpdateFiring(float absSpeedMpS)
76947773
StopInjector1Sound();
76957774
StopInjector2Sound();
76967775
}
7697-
else if (CurrentWaterGaugeFraction <= 0.55 && CurrentWaterGaugeFraction > 0.525 && !InjectorLockedOut)
7776+
else if (CurrentWaterGaugeFraction <= 0.55 && CurrentWaterGaugeFraction > 0.50 && !InjectorLockedOut)
76987777
{
76997778
Injector1IsOn = true;
7700-
Injector1Fraction = 0.25f;
7779+
Injector1Fraction = 0.5f;
77017780
Injector2IsOn = false;
77027781
Injector2Fraction = 0.0f;
77037782
InjectorLockedOut = true;
77047783
PlayInjector1SoundIfStarting();
77057784
}
7706-
else if (CurrentWaterGaugeFraction <= 0.525 && CurrentWaterGaugeFraction > 0.5 && !InjectorLockedOut)
7785+
else if (CurrentWaterGaugeFraction <= 0.50 && CurrentWaterGaugeFraction > 0.45 && !InjectorLockedOut)
77077786
{
77087787
Injector1IsOn = true;
7709-
Injector1Fraction = 0.5f;
7788+
Injector1Fraction = 0.75f;
77107789
Injector2IsOn = false;
77117790
Injector2Fraction = 0.0f;
77127791
InjectorLockedOut = true;
77137792
PlayInjector1SoundIfStarting();
77147793
}
7715-
else if (CurrentWaterGaugeFraction <= 0.5 && CurrentWaterGaugeFraction > 0.475 && !InjectorLockedOut)
7794+
else if (CurrentWaterGaugeFraction <= 0.45 && CurrentWaterGaugeFraction > 0.40 && !InjectorLockedOut)
77167795
{
77177796
Injector1IsOn = true;
77187797
Injector1Fraction = 1.0f;
@@ -7723,26 +7802,26 @@ private void UpdateFiring(float absSpeedMpS)
77237802
}
77247803
else if (BoilerPressurePSI > (MaxBoilerPressurePSI - 100.0)) // If boiler pressure is not too low then turn on injector 2 as well
77257804
{
7726-
if (CurrentWaterGaugeFraction <= 0.475 && CurrentWaterGaugeFraction > 0.45 && !InjectorLockedOut)
7805+
if (CurrentWaterGaugeFraction <= 0.40 && CurrentWaterGaugeFraction > 0.35 && !InjectorLockedOut)
77277806
{
77287807

77297808
Injector1IsOn = true;
77307809
Injector1Fraction = 1.0f;
77317810
Injector2IsOn = true;
7732-
Injector2Fraction = 0.25f;
7811+
Injector2Fraction = 0.5f;
77337812
InjectorLockedOut = true;
77347813
PlayInjector2SoundIfStarting();
77357814
}
7736-
else if (CurrentWaterGaugeFraction <= 0.45 && CurrentWaterGaugeFraction > 0.425 && !InjectorLockedOut)
7815+
else if (CurrentWaterGaugeFraction <= 0.35 && CurrentWaterGaugeFraction > 0.3 && !InjectorLockedOut)
77377816
{
77387817
Injector1IsOn = true;
77397818
Injector1Fraction = 1.0f;
77407819
Injector2IsOn = true;
7741-
Injector2Fraction = 0.5f;
7820+
Injector2Fraction = 0.75f;
77427821
InjectorLockedOut = true;
77437822
PlayInjector2SoundIfStarting();
77447823
}
7745-
else if (CurrentWaterGaugeFraction <= 0.425 && !InjectorLockedOut)
7824+
else if (CurrentWaterGaugeFraction <= 0.3 && !InjectorLockedOut)
77467825
{
77477826
Injector1IsOn = true;
77487827
Injector1Fraction = 1.0f;

0 commit comments

Comments
 (0)