Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

more veegee-like HE pipes (radiatiors) #23

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 15 additions & 23 deletions Content.Server/Atmos/Components/HeatExchangerComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,26 @@ namespace Content.Server.Atmos.Components;
[RegisterComponent]
public sealed partial class HeatExchangerComponent : Component
{
[ViewVariables(VVAccess.ReadWrite)]
[DataField("inlet")]
public string InletName { get; set; } = "inlet";

[ViewVariables(VVAccess.ReadWrite)]
[DataField("outlet")]
public string OutletName { get; set; } = "outlet";
[DataField("pipe")]
public string PipeName { get; set; } = "pipe";

/// <summary>
/// Pipe conductivity (mols/kPa/sec).

/// <summary>
/// Thermal convection multiplier. 0.0-1.0
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField("conductivity")]
public float G { get; set; } = 1f;

/// <summary>
/// Thermal convection coefficient (J/degK/sec).
[ViewVariables(VVAccess.ReadWrite)]
[DataField("convection efficiency")]
public float convection_coeff { get; set; } = .5f;


/// <summary>
/// the simulated surface area for thermal radiation. >0.0
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField("convectionCoefficient")]
public float K { get; set; } = 8000f;
[ViewVariables(VVAccess.ReadWrite)]
[DataField("radiation area")]
public float surface_area { get; set; } = 3f;

/// <summary>
/// Thermal radiation coefficient. Number of "effective" tiles this
/// radiator radiates compared to superconductivity tile losses.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField("radiationCoefficient")]
public float alpha { get; set; } = 140f;
}

160 changes: 74 additions & 86 deletions Content.Server/Atmos/EntitySystems/HeatExchangerSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,93 +47,81 @@ private void OnAtmosUpdate(EntityUid uid, HeatExchangerComponent comp, ref Atmos
{
return;
}

if (!_nodeContainer.TryGetNodes(uid, comp.InletName, comp.OutletName, out PipeNode? inlet, out PipeNode? outlet))
return;


if(!_nodeContainer.TryGetNode(uid,comp.PipeName, out PipeNode? pipe ) ) 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);


//first priority is to figure out convection.
var environment = _atmosphereSystem.GetContainingMixture(uid, true, true);
if(environment!=null){
float envT=environment.Temperature;
float pipeT=pipe.Air.Temperature;

// (1- 1/( (5*moles+volume)/volume ))^.75
// math formula, where x is moles and v is volume: \left(1-\frac{1}{\frac{5x+v}{v}}\right)^{.75}
//this is to simulate more moles making more heat transfer avalible.
//this has no basis in reality.
float env_convection_coef= 1f-(1f/((500f*environment.TotalMoles+environment.Volume)/environment.Volume));
env_convection_coef=MathF.Pow(env_convection_coef,.75f);

if(envT>pipeT){ // env -> pipe
float EnergyToConvect= comp.convection_coeff*env_convection_coef*(envT-pipeT);
float heatcap_env= _atmosphereSystem.GetHeatCapacity(environment, true);
float heatcap_pipe= _atmosphereSystem.GetHeatCapacity(pipe.Air, true);

if(heatcap_env>Atmospherics.MinimumHeatCapacity && heatcap_pipe>Atmospherics.MinimumHeatCapacity){
pipe.Air.Temperature+= EnergyToConvect*dt/heatcap_pipe; //divide by heat capacity to get temp change
environment.Temperature-=EnergyToConvect*dt/heatcap_env;
}
}else{ // pipe->env
float EnergyToConvect= comp.convection_coeff*env_convection_coef*(pipeT-envT);
float heatcap_env= _atmosphereSystem.GetHeatCapacity(environment, true);
float heatcap_pipe= _atmosphereSystem.GetHeatCapacity(pipe.Air, true);

if(heatcap_env>Atmospherics.MinimumHeatCapacity && heatcap_pipe>Atmospherics.MinimumHeatCapacity){
pipe.Air.Temperature-= EnergyToConvect*dt/heatcap_pipe;
environment.Temperature+=EnergyToConvect*dt/heatcap_env;
}
}
}


//next up, simulate heat loss via radiation. we are assuming a perfect black body for this and also spherical cows.
//this uses the Stefan–Boltzmann law
if(environment!=null){
float pipetemp=pipe.Air.Temperature;
float envtemp=environment.Temperature;
if (pipe.Air.Temperature<environment.Temperature){ // environment -> pipe
float energy_radiated = Atmospherics.StefanBoltzmann*comp.surface_area*( MathF.Pow(envtemp ,4f)-MathF.Pow(pipetemp ,4f) );
float heatcap_env= _atmosphereSystem.GetHeatCapacity(environment, true);
float heatcap_pipe= _atmosphereSystem.GetHeatCapacity(pipe.Air, true);

if(heatcap_env>Atmospherics.MinimumHeatCapacity) environment.Temperature-=energy_radiated*dt/heatcap_env;
if(heatcap_pipe>Atmospherics.MinimumHeatCapacity) pipe.Air.Temperature+=energy_radiated*dt/heatcap_pipe;
}else{ // pipe -> environment
float energy_radiated = Atmospherics.StefanBoltzmann*comp.surface_area*( MathF.Pow(pipetemp ,4f)-MathF.Pow(envtemp ,4f) );
float heatcap_env= _atmosphereSystem.GetHeatCapacity(environment, true);
float heatcap_pipe= _atmosphereSystem.GetHeatCapacity(pipe.Air, true);

if(heatcap_env>Atmospherics.MinimumHeatCapacity) environment.Temperature+=energy_radiated*dt/heatcap_env;
if(heatcap_pipe>Atmospherics.MinimumHeatCapacity) pipe.Air.Temperature-=energy_radiated*dt/heatcap_pipe;
}
}else{ //assume space cooling.
float pipetemp=pipe.Air.Temperature;
if (pipe.Air.Temperature<Atmospherics.TCMB){ // space -> pipe
float energy_radiated = Atmospherics.StefanBoltzmann*comp.surface_area*( MathF.Pow(Atmospherics.TCMB ,4f)-MathF.Pow(pipetemp ,4f) );
float heatcap_pipe= _atmosphereSystem.GetHeatCapacity(pipe.Air, true);

if(heatcap_pipe>Atmospherics.MinimumHeatCapacity) pipe.Air.Temperature+=energy_radiated*dt/heatcap_pipe;
}else{ // pipe -> space
float energy_radiated = Atmospherics.StefanBoltzmann*comp.surface_area*( MathF.Pow(pipetemp ,4f)- MathF.Pow(Atmospherics.TCMB ,4f) );
float heatcap_pipe= _atmosphereSystem.GetHeatCapacity(pipe.Air, true);

if(heatcap_pipe>Atmospherics.MinimumHeatCapacity) pipe.Air.Temperature-=energy_radiated*dt/heatcap_pipe;
}
}

}
}
5 changes: 5 additions & 0 deletions Content.Shared/Atmos/Atmospherics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ public static class Atmospherics
/// </summary>
public const float R = 8.314462618f;

/// <summary>
/// Relates energy radiated to temperature. W*m^-2*K^-4
/// </summary>
public const float StefanBoltzmann = 5.670374419e-8f;

/// <summary>
/// 1 ATM in kPA.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -458,14 +458,10 @@
- type: HeatExchanger
- type: NodeContainer
nodes:
inlet:
!type:PipeNode
nodeGroupID: Pipe
pipeDirection: North
outlet:
pipe:
!type:PipeNode
nodeGroupID: Pipe
pipeDirection: South
pipeDirection: Longitudinal
- type: Construction
graph: GasBinary
node: radiator
Expand Down