GasPassiveGateSystem.cs 3.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. using Content.Server.Atmos.EntitySystems;
  2. using Content.Server.Atmos.Piping.Binary.Components;
  3. using Content.Server.Atmos.Piping.Components;
  4. using Content.Server.NodeContainer;
  5. using Content.Server.NodeContainer.EntitySystems;
  6. using Content.Server.NodeContainer.Nodes;
  7. using Content.Shared.Atmos;
  8. using Content.Shared.Examine;
  9. using JetBrains.Annotations;
  10. namespace Content.Server.Atmos.Piping.Binary.EntitySystems
  11. {
  12. [UsedImplicitly]
  13. public sealed class GasPassiveGateSystem : EntitySystem
  14. {
  15. [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
  16. [Dependency] private readonly NodeContainerSystem _nodeContainer = default!;
  17. public override void Initialize()
  18. {
  19. base.Initialize();
  20. SubscribeLocalEvent<GasPassiveGateComponent, AtmosDeviceUpdateEvent>(OnPassiveGateUpdated);
  21. SubscribeLocalEvent<GasPassiveGateComponent, ExaminedEvent>(OnExamined);
  22. }
  23. private void OnPassiveGateUpdated(EntityUid uid, GasPassiveGateComponent gate, ref AtmosDeviceUpdateEvent args)
  24. {
  25. if (!_nodeContainer.TryGetNodes(uid, gate.InletName, gate.OutletName, out PipeNode? inlet, out PipeNode? outlet))
  26. return;
  27. var n1 = inlet.Air.TotalMoles;
  28. var n2 = outlet.Air.TotalMoles;
  29. var P1 = inlet.Air.Pressure;
  30. var P2 = outlet.Air.Pressure;
  31. var V1 = inlet.Air.Volume;
  32. var V2 = outlet.Air.Volume;
  33. var T1 = inlet.Air.Temperature;
  34. var T2 = outlet.Air.Temperature;
  35. var pressureDelta = P1 - P2;
  36. float dt = args.dt;
  37. float dV = 0;
  38. var denom = (T1*V2 + T2*V1);
  39. if (pressureDelta > 0 && P1 > 0 && denom > 0)
  40. {
  41. // Calculate the number of moles to transfer to equalize the final pressure of
  42. // both sides of the valve. You can derive this equation yourself by solving
  43. // the equations:
  44. //
  45. // P_inlet,final = P_outlet,final (pressure equilibrium)
  46. // n_inlet,initial + n_outlet,initial = n_inlet,final + n_outlet,final (mass conservation)
  47. //
  48. // These simplifying assumptions allow an easy closed-form solution:
  49. //
  50. // T_inlet,initial = T_inlet,final
  51. // T_outlet,initial = T_outlet,final
  52. //
  53. // If you don't want to push through the math, just know that this behaves like a
  54. // pump that can equalize pressure instantly, i.e. much faster than pressure or
  55. // volume pumps.
  56. var transferMoles = n1 - (n1+n2)*T2*V1 / denom;
  57. // Get the volume transfered to update our flow meter.
  58. // When you remove x from one side and add x to the other the total difference is 2x.
  59. // Also account for atmos speedup so that measured flow rate matches the setting on the volume pump.
  60. dV = 2*transferMoles*Atmospherics.R*T1/P1 / _atmosphereSystem.Speedup;
  61. // Actually transfer the gas.
  62. _atmosphereSystem.Merge(outlet.Air, inlet.Air.Remove(transferMoles));
  63. }
  64. // Update transfer rate with an exponential moving average.
  65. var tau = 1; // Time constant (averaging time) in seconds
  66. var a = dt/tau;
  67. gate.FlowRate = a*dV/tau + (1-a)*gate.FlowRate; // in L/sec
  68. }
  69. private void OnExamined(Entity<GasPassiveGateComponent> gate, ref ExaminedEvent args)
  70. {
  71. if (!Comp<TransformComponent>(gate).Anchored || !args.IsInDetailsRange) // Not anchored? Out of range? No status.
  72. return;
  73. var str = Loc.GetString("gas-passive-gate-examined", ("flowRate", $"{gate.Comp.FlowRate:0.#}"));
  74. args.PushMarkup(str);
  75. }
  76. }
  77. }