From da11acd8c376d5e6dc3a13fa240f09bd8084af84 Mon Sep 17 00:00:00 2001 From: Kevin Zheng Date: Sat, 30 Jul 2022 20:00:34 -0700 Subject: [PATCH] Make passive gates great (#9816) --- .../Components/GasPassiveGateComponent.cs | 17 +----- .../EntitySystems/GasPassiveGateSystem.cs | 60 +++++++++++++++---- .../atmos/gas-passive-gate-component.ftl | 1 + 3 files changed, 52 insertions(+), 26 deletions(-) create mode 100644 Resources/Locale/en-US/atmos/gas-passive-gate-component.ftl diff --git a/Content.Server/Atmos/Piping/Binary/Components/GasPassiveGateComponent.cs b/Content.Server/Atmos/Piping/Binary/Components/GasPassiveGateComponent.cs index b219ae46f8..f770a25652 100644 --- a/Content.Server/Atmos/Piping/Binary/Components/GasPassiveGateComponent.cs +++ b/Content.Server/Atmos/Piping/Binary/Components/GasPassiveGateComponent.cs @@ -5,17 +5,6 @@ namespace Content.Server.Atmos.Piping.Binary.Components [RegisterComponent] public sealed class GasPassiveGateComponent : Component { - [DataField("enabled")] - [ViewVariables(VVAccess.ReadWrite)] - public bool Enabled { get; set; } = true; - - /// - /// This is the minimum difference needed to overcome the friction in the mechanism. - /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("frictionDifference")] - public float FrictionPressureDifference { get; set; } = 10f; - [ViewVariables(VVAccess.ReadWrite)] [DataField("inlet")] public string InletName { get; set; } = "inlet"; @@ -24,8 +13,8 @@ namespace Content.Server.Atmos.Piping.Binary.Components [DataField("outlet")] public string OutletName { get; set; } = "outlet"; - [ViewVariables(VVAccess.ReadWrite)] - [DataField("targetPressure")] - public float TargetPressure { get; set; } = Atmospherics.OneAtmosphere; + [ViewVariables(VVAccess.ReadOnly)] + [DataField("flowRate")] + public float FlowRate { get; set; } = 0; } } diff --git a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasPassiveGateSystem.cs b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasPassiveGateSystem.cs index 57a7a39929..834794981c 100644 --- a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasPassiveGateSystem.cs +++ b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasPassiveGateSystem.cs @@ -4,6 +4,7 @@ using Content.Server.Atmos.Piping.Components; using Content.Server.NodeContainer; using Content.Server.NodeContainer.Nodes; using Content.Shared.Atmos; +using Content.Shared.Examine; using JetBrains.Annotations; namespace Content.Server.Atmos.Piping.Binary.EntitySystems @@ -12,19 +13,18 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems public sealed class GasPassiveGateSystem : EntitySystem { [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; + [Dependency] private readonly ExamineSystemShared _examineSystem = default!; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnPassiveGateUpdated); + SubscribeLocalEvent(OnExamined); } private void OnPassiveGateUpdated(EntityUid uid, GasPassiveGateComponent gate, AtmosDeviceUpdateEvent args) { - if (!gate.Enabled) - return; - if (!EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer)) return; @@ -32,23 +32,59 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems || !nodeContainer.TryGetNode(gate.OutletName, out PipeNode? outlet)) return; - var outputStartingPressure = outlet.Air.Pressure; - var inputStartingPressure = inlet.Air.Pressure; + var n1 = inlet.Air.TotalMoles; + var n2 = outlet.Air.TotalMoles; + var P1 = inlet.Air.Pressure; + var P2 = outlet.Air.Pressure; + var V1 = inlet.Air.Volume; + var V2 = outlet.Air.Volume; + var T1 = inlet.Air.Temperature; + var T2 = outlet.Air.Temperature; + var pressureDelta = P1 - P2; - if (outputStartingPressure >= MathF.Min(gate.TargetPressure, inputStartingPressure - gate.FrictionPressureDifference)) - return; // No need to pump gas, target reached or input pressure too low. + float dt = 1/_atmosphereSystem.AtmosTickRate; + float dV = 0; + var denom = (T1*V2 + T2*V1); - if (inlet.Air.TotalMoles > 0 && inlet.Air.Temperature > 0) + if (pressureDelta > 0 && P1 > 0 && denom > 0) { - // We calculate the necessary moles to transfer using our good ol' friend PV=nRT. - var pressureDelta = MathF.Min(gate.TargetPressure - outputStartingPressure, (inputStartingPressure - outputStartingPressure)/2); - // We can't have a pressure delta that would cause outlet pressure > inlet pressure. + // Calculate the number of moles to transfer to equalize the final pressure of + // both sides of the valve. You can derive this equation yourself by solving + // the equations: + // + // P_inlet,final = P_outlet,final (pressure equilibrium) + // n_inlet,initial + n_outlet,initial = n_inlet,final + n_outlet,final (mass conservation) + // + // These simplifying assumptions allow an easy closed-form solution: + // + // T_inlet,initial = T_inlet,final + // T_outlet,initial = T_outlet,final + // + // If you don't want to push through the math, just know that this behaves like a + // pump that can equalize pressure instantly, i.e. much faster than pressure or + // volume pumps. + var transferMoles = n1 - (n1+n2)*T2*V1 / denom; - var transferMoles = pressureDelta * outlet.Air.Volume / (inlet.Air.Temperature * Atmospherics.R); + // Get the volume transfered to update our flow meter. + dV = n1*Atmospherics.R*T1/P1; // Actually transfer the gas. _atmosphereSystem.Merge(outlet.Air, inlet.Air.Remove(transferMoles)); } + + // Update transfer rate with an exponential moving average. + var tau = 1; // Time constant (averaging time) in seconds + var a = dt/tau; + gate.FlowRate = a*dV/tau + (1-a)*gate.FlowRate; // in L/sec + } + + private void OnExamined(EntityUid uid, GasPassiveGateComponent gate, ExaminedEvent args) + { + if (!EntityManager.GetComponent(gate.Owner).Anchored || !args.IsInDetailsRange) // Not anchored? Out of range? No status. + return; + + var str = Loc.GetString("gas-passive-gate-examined", ("flowRate", $"{gate.FlowRate:0.#}")); + args.PushMarkup(str); } } } diff --git a/Resources/Locale/en-US/atmos/gas-passive-gate-component.ftl b/Resources/Locale/en-US/atmos/gas-passive-gate-component.ftl new file mode 100644 index 0000000000..3b19d18f27 --- /dev/null +++ b/Resources/Locale/en-US/atmos/gas-passive-gate-component.ftl @@ -0,0 +1 @@ +gas-passive-gate-examined = The flow rate meter indicates [color=lightblue]{$flowRate} liters/sec[/color].