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].