@@ -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;
@@ -2080,7 +2091,7 @@ public override void Initialize()
20802091
20812092 // Set up Injectors
20822093 // To calculate the maximum combined water delivery rate from the injectors at maximum boiler pressure, the formula is used:
2083- // WaterFlowRateUKGpM = K * A * P^0.5, k is calculated using known flow rates from manuafacturers manuals.
2094+ // WaterFlowRateUKGpM = K * A * P^0.5, k is calculated using known flow rates from manuafacturers manuals, and rearranging the formula .
20842095 // If user does not enter any values then the D&M 11mm injector is assumed to be operating at 200 psi .
20852096 // http://users.fini.net/~bersano/english-anglais/english-anglais/NZ-accessories/D&M_Type_J_Exhaust_steam_injector.pdf
20862097 //
@@ -2126,14 +2137,62 @@ public override void Initialize()
21262137 Trace.TraceInformation("RefInjector2FlowRateUKGpH not specified in ENG file. Defaulting to {0} UKG/h", RefInjector2FlowRateUKGpH);
21272138 }
21282139
2129- // Calculate K values for injector flow rate calculations
2140+ if (MaxRefInjector1FlowRateUKGpH == 0)
2141+ {
2142+ MaxRefInjector1FlowRateUKGpH = 2600;
2143+ if (Simulator.Settings.VerboseConfigurationMessages)
2144+ Trace.TraceInformation("MaxInjector1FlowRateUKGpH not specified in ENG file. Defaulting to {0} UKG/h", MaxRefInjector1FlowRateUKGpH);
2145+ }
2146+
2147+ if (MaxRefInjector2FlowRateUKGpH == 0)
2148+ {
2149+ MaxRefInjector2FlowRateUKGpH = 2600;
2150+ if (Simulator.Settings.VerboseConfigurationMessages)
2151+ Trace.TraceInformation("MaxInjector2FlowRateUKGpH not specified in ENG file. Defaulting to {0} UKG/h", MaxRefInjector2FlowRateUKGpH);
2152+ }
2153+
2154+ if(MaxRefInjector1PressurePSI == 0)
2155+ {
2156+ MaxRefInjector1PressurePSI = 300.0f; // Default to D&M 11mm injector operating pressure
2157+ if (Simulator.Settings.VerboseConfigurationMessages)
2158+ Trace.TraceInformation("MaxRefInjector1SteamPressurePSI not specified in ENG file. Defaulting to {0} psi", MaxRefInjector1PressurePSI);
2159+ }
2160+
2161+ if (MaxRefInjector2PressurePSI == 0)
2162+ {
2163+ MaxRefInjector2PressurePSI = 300.0f; // Default to D&M 11mm injector operating pressure
2164+ if (Simulator.Settings.VerboseConfigurationMessages)
2165+ Trace.TraceInformation("MaxRefInjector2SteamPressurePSI not specified in ENG file. Defaulting to {0} psi", MaxRefInjector2PressurePSI);
2166+ }
2167+
2168+ // Calculate K values for injector delivery flow rate calculations - up to to optimum (maximum delivery rate)
21302169
21312170 var RefInjectorNozzle1AreaMM2 = (float)(Math.PI * Math.Pow((RefInjector1NozzleSizeMM) / 2.0f, 2));
21322171 RefKInjector1Factor = RefInjector1FlowRateUKGpH / (float)(RefInjectorNozzle1AreaMM2 * Math.Sqrt(RefInjector1PressurePSI));
21332172
21342173 var RefInjectorNozzle2AreaMM2 = (float)(Math.PI * Math.Pow((RefInjector2NozzleSizeMM) / 2.0f, 2));
21352174 RefKInjector2Factor = RefInjector2FlowRateUKGpH / (float)(RefInjectorNozzle2AreaMM2 * Math.Sqrt(RefInjector2PressurePSI));
21362175
2176+ // calculate relevant factors for delivery flow rates above optimum delivery rate
2177+ // Calculation based upon a quadratic equation to fit the known data points, with the user setting a known point above the optimum delivery rate.
2178+ // Effective delivery @ P pressure - Qeff(P)= Qbase(P)* η(P) at current Boiler Pressure
2179+ // Delivery @ P - Qbase(P) = Qopt √(P/Popt)
2180+ // Efficiency factor is calculated by η(P)=1-β(P-Popt)-α(P-Popt)^2
2181+ // Following section calculates the constants required to model the efficiency curve above optimum delivery rate
2182+ // β=1/(2 Popt )
2183+ // η(Pmax)= (Qeff(Popt))/(Qbase(Pmax))
2184+ // α= [1 - β(Pmax - Popt) - η(Pmax)] / (Pmax - Popt)^2
2185+
2186+ UpperBetaInjector1Factor = 1 / (2 * RefInjector1PressurePSI);
2187+ var DeliveryBaseInjector1Pmax = RefInjector1FlowRateUKGpH * Math.Sqrt(MaxRefInjector1PressurePSI / RefInjector1PressurePSI);
2188+ var EfficiencyBaseInjector1atMax = MaxRefInjector1FlowRateUKGpH / DeliveryBaseInjector1Pmax;
2189+ UpperAlphaInjector1Factor = (float)(1.0 - UpperBetaInjector1Factor * (MaxRefInjector1PressurePSI - RefInjector1PressurePSI) - EfficiencyBaseInjector1atMax) / (float)Math.Pow((MaxRefInjector1PressurePSI - RefInjector1PressurePSI), 2);
2190+
2191+ UpperBetaInjector2Factor = 1 / (2 * RefInjector2PressurePSI);
2192+ var DeliveryBaseInjector2Pmax = RefInjector2FlowRateUKGpH * Math.Sqrt(MaxRefInjector2PressurePSI / RefInjector2PressurePSI);
2193+ var EfficiencyBaseInjector2atMax = MaxRefInjector2FlowRateUKGpH / DeliveryBaseInjector2Pmax;
2194+ UpperAlphaInjector2Factor = (float)(1.0 - UpperBetaInjector2Factor * (MaxRefInjector2PressurePSI - RefInjector2PressurePSI) - EfficiencyBaseInjector2atMax) / (float)Math.Pow((MaxRefInjector2PressurePSI - RefInjector2PressurePSI), 2);
2195+
21372196 // Calculate Injector sizing, a single injector should be able to cope with 75% of the maximum water delivery rate required at full steam usage.
21382197
21392198 if (ActualInjector1NozzleSizeMM == 0)
@@ -7409,25 +7468,45 @@ private void UpdateWaterInjection(float elapsedClockSeconds)
74097468
74107469 // Calculate the flow rates for injector #1 based upon boiler pressure and injector nozzle size
74117470 var ActualInjector1NozzleAreaMM2 = (float)(Math.PI * Math.Pow((ActualInjector1NozzleSizeMM) / 2.0f, 2));
7412-
7471+
74137472 MaxInjectorFlowRateLBpS = ActualInjectorTemperatureCorrectionFactor * pS.FrompH(RefKInjector1Factor * ActualInjector1NozzleAreaMM2 * (float)Math.Sqrt(MaxBoilerPressurePSI)) * WaterLBpUKG;
74147473
7415- ActualInjector1FlowRateLBpS = ActualInjectorTemperatureCorrectionFactor * pS.FrompH(RefKInjector1Factor * ActualInjector1NozzleAreaMM2 * (float)Math.Sqrt(BoilerPressurePSI)) * WaterLBpUKG;
7474+ // Injector #1 - calculate flow rate depending upon whether pressure is above or below optimum pressure
7475+ if (BoilerPressurePSI <= RefInjector1PressurePSI)
7476+ {
7477+ ActualInjector1FlowRateLBpS = ActualInjectorTemperatureCorrectionFactor * pS.FrompH(RefKInjector1Factor * ActualInjector1NozzleAreaMM2 * (float)Math.Sqrt(BoilerPressurePSI)) * WaterLBpUKG;
7478+ }
7479+ else
7480+ {
7481+ float InjectorDeliveryEfficincyatP = (float)(1 - UpperBetaInjector1Factor * (BoilerPressurePSI - RefInjector1PressurePSI) - UpperAlphaInjector1Factor * Math.Pow(BoilerPressurePSI - RefInjector1PressurePSI, 2));
7482+ float DeliveryBaseFlowRateatP = (float)(RefInjector1FlowRateUKGpH * Math.Sqrt(BoilerPressurePSI / RefInjector1PressurePSI));
7483+ ActualInjector1FlowRateLBpS = pS.FrompH(DeliveryBaseFlowRateatP * InjectorDeliveryEfficincyatP) * WaterLBpUKG;
7484+ }
74167485
74177486 // Calculate the flow rates for injector #2 based upon boiler pressure and injector nozzle size
74187487 var ActualInjector2NozzleAreaMM2 = (float)(Math.PI * Math.Pow((ActualInjector1NozzleSizeMM) / 2.0f, 2));
74197488
74207489 MaxInjectorFlowRateLBpS = pS.FrompH(RefKInjector2Factor * ActualInjector1NozzleAreaMM2 * (float)Math.Sqrt(MaxBoilerPressurePSI)) * WaterLBpUKG;
74217490
7422- ActualInjector2FlowRateLBpS = pS.FrompH(RefKInjector2Factor * ActualInjector1NozzleAreaMM2 * (float)Math.Sqrt(BoilerPressurePSI)) * WaterLBpUKG;
7491+ // Injector #2 - calculate flow rate depending upon whether pressure is above or below optimum pressure
7492+ if (BoilerPressurePSI <= RefInjector2PressurePSI)
7493+ {
7494+ ActualInjector2FlowRateLBpS = pS.FrompH(RefKInjector2Factor * ActualInjector1NozzleAreaMM2 * (float)Math.Sqrt(BoilerPressurePSI)) * WaterLBpUKG;
7495+ }
7496+ else
7497+ {
7498+ float InjectorDeliveryEfficincyatP = (float)(1 - UpperBetaInjector2Factor * (BoilerPressurePSI - RefInjector2PressurePSI) - UpperAlphaInjector2Factor * Math.Pow(BoilerPressurePSI - RefInjector2PressurePSI, 2));
7499+ float DeliveryBaseFlowRateatP = (float)(RefInjector2FlowRateUKGpH * Math.Sqrt(BoilerPressurePSI / RefInjector2PressurePSI));
7500+ ActualInjector2FlowRateLBpS = pS.FrompH(DeliveryBaseFlowRateatP * InjectorDeliveryEfficincyatP) * WaterLBpUKG;
7501+ }
74237502
74247503 // Calculate the temperature correction factor as the feedwater temperature will affect the flow rate and the hence the steam used
74257504 // Actual Delivery Capacity = Base Delivery * Temperature Correction Factor
74267505 // Actual Steam used = Base Steam used * Temperature Correction Factor
74277506 // Temperature Correction Factor = (Tsat - Tfeedwater) / (Tsat - Tref), Assume ref = 20C
74287507
74297508 var ReferenceTemperature = 20.0f; // Reference temperature for injector performance
7430- ActualInjectorTemperatureCorrectionFactor = (float)Math.Sqrt(( C.FromF(PressureToTemperaturePSItoF[BoilerPressurePSI]) - TenderWaterTemperatureC) / (C.FromF(PressureToTemperaturePSItoF[BoilerPressurePSI]) - ReferenceTemperature));
7509+ ActualInjectorTemperatureCorrectionFactor = (float)Math.Sqrt((C.FromF(PressureToTemperaturePSItoF[BoilerPressurePSI]) - TenderWaterTemperatureC) / (C.FromF(PressureToTemperaturePSItoF[BoilerPressurePSI]) - ReferenceTemperature));
74317510
74327511 // Calculate enthalpy of injector feed water based on boiler water temperature
74337512 // hfg kJ/kg) = 2500 - 2.4 * Tsat (C) - 4% accurate, to be explored later for better accuracy
@@ -7645,25 +7724,25 @@ private void UpdateFiring(float absSpeedMpS)
76457724 StopInjector1Sound();
76467725 StopInjector2Sound();
76477726 }
7648- else if (CurrentWaterGaugeFraction <= 0.55 && CurrentWaterGaugeFraction > 0.525 && !InjectorLockedOut)
7727+ else if (CurrentWaterGaugeFraction <= 0.55 && CurrentWaterGaugeFraction > 0.50 && !InjectorLockedOut)
76497728 {
76507729 Injector1IsOn = true;
7651- Injector1Fraction = 0.25f ;
7730+ Injector1Fraction = 0.5f ;
76527731 Injector2IsOn = false;
76537732 Injector2Fraction = 0.0f;
76547733 InjectorLockedOut = true;
76557734 PlayInjector1SoundIfStarting();
76567735 }
7657- else if (CurrentWaterGaugeFraction <= 0.525 && CurrentWaterGaugeFraction > 0.5 && !InjectorLockedOut)
7736+ else if (CurrentWaterGaugeFraction <= 0.50 && CurrentWaterGaugeFraction > 0.45 && !InjectorLockedOut)
76587737 {
76597738 Injector1IsOn = true;
7660- Injector1Fraction = 0.5f ;
7739+ Injector1Fraction = 0.75f ;
76617740 Injector2IsOn = false;
76627741 Injector2Fraction = 0.0f;
76637742 InjectorLockedOut = true;
76647743 PlayInjector1SoundIfStarting();
76657744 }
7666- else if (CurrentWaterGaugeFraction <= 0.5 && CurrentWaterGaugeFraction > 0.475 && !InjectorLockedOut)
7745+ else if (CurrentWaterGaugeFraction <= 0.45 && CurrentWaterGaugeFraction > 0.40 && !InjectorLockedOut)
76677746 {
76687747 Injector1IsOn = true;
76697748 Injector1Fraction = 1.0f;
@@ -7674,26 +7753,26 @@ private void UpdateFiring(float absSpeedMpS)
76747753 }
76757754 else if (BoilerPressurePSI > (MaxBoilerPressurePSI - 100.0)) // If boiler pressure is not too low then turn on injector 2 as well
76767755 {
7677- if (CurrentWaterGaugeFraction <= 0.475 && CurrentWaterGaugeFraction > 0.45 && !InjectorLockedOut)
7756+ if (CurrentWaterGaugeFraction <= 0.40 && CurrentWaterGaugeFraction > 0.35 && !InjectorLockedOut)
76787757 {
76797758
76807759 Injector1IsOn = true;
76817760 Injector1Fraction = 1.0f;
76827761 Injector2IsOn = true;
7683- Injector2Fraction = 0.25f ;
7762+ Injector2Fraction = 0.5f ;
76847763 InjectorLockedOut = true;
76857764 PlayInjector2SoundIfStarting();
76867765 }
7687- else if (CurrentWaterGaugeFraction <= 0.45 && CurrentWaterGaugeFraction > 0.425 && !InjectorLockedOut)
7766+ else if (CurrentWaterGaugeFraction <= 0.35 && CurrentWaterGaugeFraction > 0.3 && !InjectorLockedOut)
76887767 {
76897768 Injector1IsOn = true;
76907769 Injector1Fraction = 1.0f;
76917770 Injector2IsOn = true;
7692- Injector2Fraction = 0.5f ;
7771+ Injector2Fraction = 0.75f ;
76937772 InjectorLockedOut = true;
76947773 PlayInjector2SoundIfStarting();
76957774 }
7696- else if (CurrentWaterGaugeFraction <= 0.425 && !InjectorLockedOut)
7775+ else if (CurrentWaterGaugeFraction <= 0.3 && !InjectorLockedOut)
76977776 {
76987777 Injector1IsOn = true;
76997778 Injector1Fraction = 1.0f;
0 commit comments