142 lines
5.3 KiB
C#
142 lines
5.3 KiB
C#
using Content.Server.Atmos.EntitySystems;
|
|
using Content.Server.Atmos.Piping.Components;
|
|
using Content.Server.Atmos.Piping.Unary.Components;
|
|
using Content.Server.Atmos;
|
|
using Content.Server.Atmos.Components;
|
|
using Content.Server.NodeContainer.EntitySystems;
|
|
using Content.Server.NodeContainer.Nodes;
|
|
using Content.Server.NodeContainer;
|
|
using Content.Shared.Atmos.Piping;
|
|
using Content.Shared.Atmos;
|
|
using Content.Shared.CCVar;
|
|
using Content.Shared.Interaction;
|
|
using JetBrains.Annotations;
|
|
using Robust.Shared.Configuration;
|
|
|
|
namespace Content.Server.Atmos.EntitySystems;
|
|
|
|
public sealed class HeatExchangerSystem : EntitySystem
|
|
{
|
|
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
|
|
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
|
[Dependency] private readonly NodeContainerSystem _nodeContainer = default!;
|
|
|
|
float tileLoss;
|
|
|
|
public override void Initialize()
|
|
{
|
|
base.Initialize();
|
|
SubscribeLocalEvent<HeatExchangerComponent, AtmosDeviceUpdateEvent>(OnAtmosUpdate);
|
|
|
|
// Getting CVars is expensive, don't do it every tick
|
|
_cfg.OnValueChanged(CCVars.SuperconductionTileLoss, CacheTileLoss, true);
|
|
}
|
|
|
|
public override void Shutdown()
|
|
{
|
|
base.Shutdown();
|
|
_cfg.UnsubValueChanged(CCVars.SuperconductionTileLoss, CacheTileLoss);
|
|
}
|
|
|
|
private void CacheTileLoss(float val)
|
|
{
|
|
tileLoss = val;
|
|
}
|
|
|
|
private void OnAtmosUpdate(EntityUid uid, HeatExchangerComponent comp, ref AtmosDeviceUpdateEvent args)
|
|
{
|
|
if (!TryComp(uid, out NodeContainerComponent? nodeContainer)
|
|
|| !TryComp(uid, out AtmosDeviceComponent? device)
|
|
|| !_nodeContainer.TryGetNode(nodeContainer, comp.InletName, out PipeNode? inlet)
|
|
|| !_nodeContainer.TryGetNode(nodeContainer, comp.OutletName, out PipeNode? outlet))
|
|
{
|
|
return;
|
|
}
|
|
|
|
var dt = args.dt;
|
|
|
|
// Let n = moles(inlet) - moles(outlet), really a Δn
|
|
var P = inlet.Air.Pressure - outlet.Air.Pressure; // really a ΔP
|
|
// Such that positive P causes flow from the inlet to the outlet.
|
|
|
|
// We want moles transferred to be proportional to the pressure difference, i.e.
|
|
// dn/dt = G*P
|
|
|
|
// To solve this we need to write dn in terms of P. Since PV=nRT, dP/dn=RT/V.
|
|
// This assumes that the temperature change from transferring dn moles is negligible.
|
|
// Since we have P=Pi-Po, then dP/dn = dPi/dn-dPo/dn = R(Ti/Vi - To/Vo):
|
|
float dPdn = Atmospherics.R * (outlet.Air.Temperature / outlet.Air.Volume + inlet.Air.Temperature / inlet.Air.Volume);
|
|
|
|
// Multiplying both sides of the differential equation by dP/dn:
|
|
// dn/dt * dP/dn = dP/dt = G*P * (dP/dn)
|
|
// Which is a first-order linear differential equation with constant (heh...) coefficients:
|
|
// dP/dt + kP = 0, where k = -G*(dP/dn).
|
|
// This differential equation has a closed-form solution, namely:
|
|
float Pfinal = P * MathF.Exp(-comp.G * dPdn * dt);
|
|
|
|
// Finally, back out n, the moles transferred in this tick:
|
|
float n = (P - Pfinal) / dPdn;
|
|
|
|
GasMixture xfer;
|
|
if (n > 0)
|
|
xfer = inlet.Air.Remove(n);
|
|
else
|
|
xfer = outlet.Air.Remove(-n);
|
|
|
|
float CXfer = _atmosphereSystem.GetHeatCapacity(xfer, true);
|
|
if (CXfer < Atmospherics.MinimumHeatCapacity)
|
|
return;
|
|
|
|
var radTemp = Atmospherics.TCMB;
|
|
|
|
var environment = _atmosphereSystem.GetContainingMixture(uid, true, true);
|
|
bool hasEnv = false;
|
|
float CEnv = 0f;
|
|
if (environment != null)
|
|
{
|
|
CEnv = _atmosphereSystem.GetHeatCapacity(environment, true);
|
|
hasEnv = CEnv >= Atmospherics.MinimumHeatCapacity && environment.TotalMoles > 0f;
|
|
if (hasEnv)
|
|
radTemp = environment.Temperature;
|
|
}
|
|
|
|
// How ΔT' scales in respect to heat transferred
|
|
float TdivQ = 1f / CXfer;
|
|
// Since it's ΔT, also account for the environment's temperature change
|
|
if (hasEnv)
|
|
TdivQ += 1f / CEnv;
|
|
|
|
// Radiation
|
|
float dTR = xfer.Temperature - radTemp;
|
|
float dTRA = MathF.Abs(dTR);
|
|
float a0 = tileLoss / MathF.Pow(Atmospherics.T20C, 4);
|
|
// ΔT' = -kΔT^4, k = -ΔT'/ΔT^4
|
|
float kR = comp.alpha * a0 * TdivQ;
|
|
// Based on the fact that ((3t)^(-1/3))' = -(3t)^(-4/3) = -((3t)^(-1/3))^4, and ΔT' = -kΔT^4.
|
|
float dT2R = dTR * MathF.Pow((1f + 3f * kR * dt * dTRA * dTRA * dTRA), -1f/3f);
|
|
float dER = (dTR - dT2R) / TdivQ;
|
|
_atmosphereSystem.AddHeat(xfer, -dER);
|
|
if (hasEnv && environment != null)
|
|
{
|
|
_atmosphereSystem.AddHeat(environment, dER);
|
|
|
|
// Convection
|
|
|
|
// Positive dT is from pipe to surroundings
|
|
float dT = xfer.Temperature - environment.Temperature;
|
|
// ΔT' = -kΔT, k = -ΔT' / ΔT
|
|
float k = comp.K * TdivQ;
|
|
float dT2 = dT * MathF.Exp(-k * dt);
|
|
float dE = (dT - dT2) / TdivQ;
|
|
_atmosphereSystem.AddHeat(xfer, -dE);
|
|
_atmosphereSystem.AddHeat(environment, dE);
|
|
}
|
|
|
|
if (n > 0)
|
|
_atmosphereSystem.Merge(outlet.Air, xfer);
|
|
else
|
|
_atmosphereSystem.Merge(inlet.Air, xfer);
|
|
|
|
}
|
|
}
|