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