@@ -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