@@ -155,6 +155,10 @@ public class MSTSSteamLocomotive : MSTSLocomotive
155155
156156 float ActualInjector1NozzleSizeMM;
157157 float ActualInjector2NozzleSizeMM;
158+ float ActualInjectorTemperatureCorrectionFactor;
159+ float ActualInjector1SteamUsedLBpS;
160+ float ActualInjector2SteamUsedLBpS;
161+ float InjectorHFGkJPkg; // Enthalpy of steam used by injector - kJ/kg
158162
159163 // Feedwater Pump
160164 float WaterMotionPump1FlowRateLBpS;
@@ -235,6 +239,7 @@ public class MSTSSteamLocomotive : MSTSLocomotive
235239 public bool AuxTenderMoveFlag = false; // Flag to indicate whether train has moved
236240 bool SteamIsAuxTenderCoupled = false;
237241 float TenderWaterPercent; // Percentage of water in tender
242+ float TenderWaterTemperatureC;
238243 public float WaterConsumptionLbpS;
239244 public float CurrentAuxTenderWaterMassKG;
240245 public float CurrentAuxTenderWaterVolumeUKG;
@@ -4086,6 +4091,15 @@ private void UpdateTender(float elapsedClockSeconds)
40864091 CoalIsExhausted = false;
40874092 }
40884093
4094+ // Calculate water temperature in tender based upon ambient temperature and water level
4095+ // Twt = Tat + 3 + 0.3 * (Tat - 10) - where wt = water temperature, at = ambient temperature, has to be capped at 50 deg C
4096+ // Check for more accurate solution later
4097+
4098+ TenderWaterTemperatureC = CarOutsideTempC + 3.0f + 0.3f * (CarOutsideTempC - 10.0f);
4099+ TenderWaterTemperatureC = MathHelper.Clamp(TenderWaterTemperatureC, 0, 50);
4100+
4101+
4102+
40894103 #region Auxiliary Water Tender Operation
40904104
40914105 // If aux tender is coupled then assume that both tender and aux tender will equalise at same % water level
@@ -7390,12 +7404,15 @@ private void UpdateWaterInjection(float elapsedClockSeconds)
73907404 {
73917405 #region Calculate Injector flow rates
73927406
7407+ ActualInjector1SteamUsedLBpS = 0;
7408+ ActualInjector2SteamUsedLBpS = 0;
7409+
73937410 // Calculate the flow rates for injector #1 based upon boiler pressure and injector nozzle size
73947411 var ActualInjector1NozzleAreaMM2 = (float)(Math.PI * Math.Pow((ActualInjector1NozzleSizeMM) / 2.0f, 2));
73957412
7396- MaxInjectorFlowRateLBpS = pS.FrompH(RefKInjector1Factor * ActualInjector1NozzleAreaMM2 * (float)Math.Sqrt(MaxBoilerPressurePSI)) * WaterLBpUKG;
7413+ MaxInjectorFlowRateLBpS = ActualInjectorTemperatureCorrectionFactor * pS.FrompH(RefKInjector1Factor * ActualInjector1NozzleAreaMM2 * (float)Math.Sqrt(MaxBoilerPressurePSI)) * WaterLBpUKG;
73977414
7398- ActualInjector1FlowRateLBpS = pS.FrompH(RefKInjector1Factor * ActualInjector1NozzleAreaMM2 * (float)Math.Sqrt(BoilerPressurePSI)) * WaterLBpUKG;
7415+ ActualInjector1FlowRateLBpS = ActualInjectorTemperatureCorrectionFactor * pS.FrompH(RefKInjector1Factor * ActualInjector1NozzleAreaMM2 * (float)Math.Sqrt(BoilerPressurePSI)) * WaterLBpUKG;
73997416
74007417 // Calculate the flow rates for injector #2 based upon boiler pressure and injector nozzle size
74017418 var ActualInjector2NozzleAreaMM2 = (float)(Math.PI * Math.Pow((ActualInjector1NozzleSizeMM) / 2.0f, 2));
@@ -7404,6 +7421,19 @@ private void UpdateWaterInjection(float elapsedClockSeconds)
74047421
74057422 ActualInjector2FlowRateLBpS = pS.FrompH(RefKInjector2Factor * ActualInjector1NozzleAreaMM2 * (float)Math.Sqrt(BoilerPressurePSI)) * WaterLBpUKG;
74067423
7424+ // Calculate the temperature correction factor as the feedwater temperature will affect the flow rate and the hence the steam used
7425+ // Actual Delivery Capacity = Base Delivery * Temperature Correction Factor
7426+ // Actual Steam used = Base Steam used * Temperature Correction Factor
7427+ // Temperature Correction Factor = (Tsat - Tfeedwater) / (Tsat - Tref), Assume ref = 20C
7428+
7429+ var ReferenceTemperature = 20.0f; // Reference temperature for injector performance
7430+ ActualInjectorTemperatureCorrectionFactor = ( C.FromF(PressureToTemperaturePSItoF[BoilerPressurePSI]) - TenderWaterTemperatureC) / (C.FromF(PressureToTemperaturePSItoF[BoilerPressurePSI]) - ReferenceTemperature);
7431+
7432+ // Calculate enthalpy of injector feed water based on boiler water temperature
7433+ // hfg kJ/kg) = 2500 - 2.4 * Tsat (C) - 4% accurate, to be explored later for better accuracy
7434+
7435+ InjectorHFGkJPkg = 2500.0f - (2.4f * C.FromF(PressureToTemperaturePSItoF[BoilerPressurePSI]));
7436+
74077437 #endregion
74087438
74097439 if (WaterIsExhausted)
@@ -7421,6 +7451,7 @@ private void UpdateWaterInjection(float elapsedClockSeconds)
74217451 else
74227452 {
74237453 // Injectors to fill boiler
7454+ // // Injector #1
74247455 if (Injector1IsOn)
74257456 {
74267457 // Calculate Injector 1 delivery water temp
@@ -7441,6 +7472,12 @@ private void UpdateWaterInjection(float elapsedClockSeconds)
74417472 MaxInject1SteamUsedLbpS = InjWaterFedSteamPressureFtoPSI[BoilerPressurePSI]; // Maximum amount of steam used at actual boiler pressure
74427473 ActInject1SteamUsedLbpS = (Injector1Fraction * ActualInjector1FlowRateLBpS) / MaxInject1SteamUsedLbpS; // Lbs of steam injected into boiler to inject water.
74437474
7475+ // Actual steam used by the injector is calculated by following formula
7476+ // Steam Mass (kg/hr) = Water Mass (kg/h) * (Cp*(Tsat-Ttenderwater))/(hfg+(Cp*(Tsat-Ttenderwater))
7477+ // where Cp = specific heat of water (4.18kJ/kgK), hfg = latent heat of vaporisation at boiler pressure
7478+
7479+ ActualInjector1SteamUsedLBpS = (Injector1Fraction * ActualInjector1FlowRateLBpS) * (4.18f * (C.FromF(PressureToTemperaturePSItoF[BoilerPressurePSI]) - TenderWaterTemperatureC)) / (InjectorHFGkJPkg + (4.18f * (C.FromF(PressureToTemperaturePSItoF[BoilerPressurePSI]) - TenderWaterTemperatureC)));
7480+
74447481 // Calculate heat loss for steam injection
74457482 Inject1SteamHeatLossBTU = ActInject1SteamUsedLbpS * (BoilerSteamHeatBTUpLB - WaterHeatPSItoBTUpLB[Injector1WaterTempPressurePSI]); // Calculate heat loss for injection steam, ie steam heat to water delivery temperature
74467483
@@ -7454,6 +7491,8 @@ private void UpdateWaterInjection(float elapsedClockSeconds)
74547491 BoilerWaterInputLB += (elapsedClockSeconds * Injector1Fraction * ActualInjector1FlowRateLBpS); // Keep track of water flow into boilers from Injector 1
74557492 BoilerHeatOutBTUpS += (Inject1WaterHeatLossBTU + Inject1SteamHeatLossBTU); // Total loss of boiler heat due to water injection - inject steam and water Heat
74567493 }
7494+
7495+ // Injector #2
74577496 if (Injector2IsOn)
74587497 {
74597498 // Calculate Injector 2 delivery water temp
@@ -7473,6 +7512,12 @@ private void UpdateWaterInjection(float elapsedClockSeconds)
74737512 MaxInject2SteamUsedLbpS = InjWaterFedSteamPressureFtoPSI[BoilerPressurePSI]; // Maximum amount of steam used at boiler pressure
74747513 ActInject2SteamUsedLbpS = (Injector2Fraction * ActualInjector2FlowRateLBpS) / MaxInject2SteamUsedLbpS; // Lbs of steam injected into boiler to inject water.
74757514
7515+ // Actual steam used by the injector is calculated by following formula
7516+ // Steam Mass (kg/hr) = Water Mass (kg/h) * (Cp*(Tsat-Ttenderwater))/(hfg+(Cp*(Tsat-Ttenderwater))
7517+ // where Cp = specific heat of water (4.18kJ/kgK), hfg = latent heat of vaporisation at boiler pressure
7518+
7519+ ActualInjector2SteamUsedLBpS = (Injector1Fraction * ActualInjector2FlowRateLBpS) * (4.18f * (C.FromF(PressureToTemperaturePSItoF[BoilerPressurePSI]) - TenderWaterTemperatureC)) / (InjectorHFGkJPkg + (4.18f * (C.FromF(PressureToTemperaturePSItoF[BoilerPressurePSI]) - TenderWaterTemperatureC)));
7520+
74767521 // Calculate heat loss for steam injection
74777522 Inject2SteamHeatLossBTU = ActInject2SteamUsedLbpS * (BoilerSteamHeatBTUpLB - WaterHeatPSItoBTUpLB[Injector2WaterTempPressurePSI]); // Calculate heat loss for injection steam, ie steam heat to water delivery temperature
74787523
@@ -8528,7 +8573,7 @@ public override string GetDebugStatus()
85288573 }
85298574 else
85308575 {
8531- status.AppendFormat("{0}\t{1}\t{2}/{13 }\t\t{3}\t({4:N0} {14 })\t{5}/{13 }\t\t{6}\t{7}\t{8}\t({9 :N0} {14 })\t{10 }/{13}\t\t{11 }\t{12 }\n",
8576+ status.AppendFormat("{0}\t{1}\t{2}/{17 }\t\t{3}\t({4:N0} {18 })\t{5}/{17 }\t\t{6}\t{7}\t{8}\t{9}/{17}\t\t{10}\t({11 :N0} {18 })\t{12 }/{17}\t\t{ 13}\t{14} \t{15 }\t{16:N0}/{17 }\n",
85328577 Simulator.Catalog.GetString("Injector:"),
85338578 Simulator.Catalog.GetString("Max"),
85348579 FormatStrings.FormatFuelVolume(pS.TopH(L.FromGUK(MaxInjectorFlowRateLBpS / WaterLBpUKG)), IsMetric, IsUK),
@@ -8537,11 +8582,15 @@ public override string GetDebugStatus()
85378582 FormatStrings.FormatFuelVolume(Injector1Fraction * pS.TopH(L.FromGUK(ActualInjector1FlowRateLBpS / WaterLBpUKG)), IsMetric, IsUK),
85388583 Simulator.Catalog.GetString("Temp1"),
85398584 FormatStrings.FormatTemperature(C.FromF(Injector1WaterDelTempF), IsMetric, false),
8585+ Simulator.Catalog.GetString("Steam1"),
8586+ FormatStrings.FormatMass(pS.TopH(ActualInjector1SteamUsedLBpS), IsMetric),
85408587 Simulator.Catalog.GetString("Inj2"),
85418588 ActualInjector2NozzleSizeMM,
85428589 FormatStrings.FormatFuelVolume(Injector2Fraction * pS.TopH(L.FromGUK(ActualInjector2FlowRateLBpS / WaterLBpUKG)), IsMetric, IsUK),
85438590 Simulator.Catalog.GetString("Temp2"),
85448591 FormatStrings.FormatTemperature(C.FromF(Injector2WaterDelTempF), IsMetric, false),
8592+ Simulator.Catalog.GetString("Steam2"),
8593+ FormatStrings.FormatMass(pS.TopH(ActualInjector2SteamUsedLBpS), IsMetric),
85458594 FormatStrings.h,
85468595 FormatStrings.mm);
85478596 }
@@ -8602,14 +8651,16 @@ public override string GetDebugStatus()
86028651 }
86038652 else // default to coal
86048653 {
8605- status.AppendFormat("{0}\t{1}\t{2}\t{3:N0}%\t{4}\t{5}\t\t{6:N0}%\t{7}\t{8:N0}\t{9}\t\t{10 :N0}\n",
8654+ status.AppendFormat("{0}\t{1}\t{2}\t{3:N0}%\t{4}\t{5}\t\t{6:N0}%\t{7}\t{8:N0}\t{9}\t{10:N0} \t{11}\t\t{12 :N0}\n",
86068655 Simulator.Catalog.GetString("Tender:"),
86078656 Simulator.Catalog.GetString("Coal"),
86088657 FormatStrings.FormatMass(TenderFuelMassKG, IsMetric),
86098658 TenderFuelMassKG / MaxTenderFuelMassKG * 100,
86108659 Simulator.Catalog.GetString("Water"),
86118660 FormatStrings.FormatFuelVolume(L.FromGUK(CombinedTenderWaterVolumeUKG), IsMetric, IsUK),
86128661 CombinedTenderWaterVolumeUKG / MaxTotalCombinedWaterVolumeUKG * 100,
8662+ Simulator.Catalog.GetString("Tempw"),
8663+ FormatStrings.FormatTemperature(TenderWaterTemperatureC, IsMetric, false),
86138664 Simulator.Catalog.GetString("Steam"),
86148665 FormatStrings.FormatMass(Kg.FromLb(CumulativeCylinderSteamConsumptionLbs), IsMetric),
86158666 Simulator.Catalog.GetString("TotSteam"),
0 commit comments