From: ArtisticRoomba <145879011+ArtisticRoomba@users.noreply.github.com>
Date: Sat, 27 Dec 2025 02:05:10 +0000 (-0800)
Subject: Atmospherics/Temperature HeatContainers (#39997)
X-Git-Url: https://git.smokeofanarchy.ru/gitweb.cgi?a=commitdiff_plain;h=8313a4e3105d69a496261cddcffcc6894a053bbe;p=space-station-14.git
Atmospherics/Temperature HeatContainers (#39997)
* Initial HeatContainer logic
* comment fixes
* Comment changes + ChangeHeatCapacity
* highly intelligent specimen
* n-body full heat exchange methods
* extract to partials
* highly intelligent specimen
* fixes + ChangeHeatCapacityKeepTemperature
* Divide and merge methods
* even divide
* different merge signature
* forgot one little thing
* address review
* missing docs
* addr review
* oops
* review
---------
Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>
---
diff --git a/Content.Shared/Temperature/HeatContainer/HeatContainer.cs b/Content.Shared/Temperature/HeatContainer/HeatContainer.cs
new file mode 100644
index 0000000000..c88d11b15a
--- /dev/null
+++ b/Content.Shared/Temperature/HeatContainer/HeatContainer.cs
@@ -0,0 +1,66 @@
+using Content.Shared.Atmos;
+using Content.Shared.Atmos.EntitySystems;
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.Temperature.HeatContainer;
+
+///
+/// A general-purpose container for heat energy.
+/// Any object that contains, stores, or transfers heat should use a
+/// instead of implementing its own system.
+/// This allows for consistent heat transfer mechanics across different objects and systems.
+///
+[Serializable, NetSerializable, DataDefinition]
+[Access(typeof(HeatContainerHelpers), typeof(SharedAtmosphereSystem))]
+public partial struct HeatContainer : IRobustCloneable
+{
+ ///
+ /// The heat capacity of this container in Joules per Kelvin.
+ /// This determines how much energy is required to change the temperature of the container.
+ /// Higher values mean the container can absorb or release more heat energy
+ /// without a significant change in temperature.
+ ///
+ [DataField]
+ public float HeatCapacity = 4000f; // about 1kg of water
+
+ ///
+ /// The current temperature of the container in Kelvin.
+ ///
+ [DataField]
+ public float Temperature = Atmospherics.T20C; // room temperature
+
+ ///
+ /// The current temperature of the container in Celsius.
+ /// Ideal if you just need to read the temperature for UI.
+ /// Do not perform computations in Celsius/set this value, use Kelvin instead.
+ ///
+ [ViewVariables]
+ public float TemperatureC => TemperatureHelpers.KelvinToCelsius(Temperature);
+
+ ///
+ /// The current thermal energy of the container in Joules.
+ ///
+ [ViewVariables]
+ public float InternalEnergy => Temperature * HeatCapacity;
+
+ public HeatContainer(float heatCapacity, float temperature)
+ {
+ HeatCapacity = heatCapacity;
+ Temperature = temperature;
+ }
+
+ ///
+ /// Copy constructor for implementing ICloneable.
+ ///
+ /// The HeatContainer to copy.
+ private HeatContainer(HeatContainer c)
+ {
+ HeatCapacity = c.HeatCapacity;
+ Temperature = c.Temperature;
+ }
+
+ public HeatContainer Clone()
+ {
+ return new HeatContainer(this);
+ }
+}
diff --git a/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.Conduct.cs b/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.Conduct.cs
new file mode 100644
index 0000000000..deb705b8e2
--- /dev/null
+++ b/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.Conduct.cs
@@ -0,0 +1,155 @@
+using JetBrains.Annotations;
+
+namespace Content.Shared.Temperature.HeatContainer;
+
+public static partial class HeatContainerHelpers
+{
+ ///
+ /// Conducts heat between a and some body with a different temperature,
+ /// given some constant thermal conductance g and a small time delta.
+ ///
+ /// The to conduct heat to.
+ /// The temperature of the second object that we are conducting heat with, in kelvin.
+ ///
+ /// The amount of time that the heat is allowed to conduct, in seconds.
+ /// This value should be small such that deltaTime << C / g where C is the heat capacity of the container.
+ /// If you need to simulate a larger time step split it into several smaller ones.
+ ///
+ /// The thermal conductance in watt per kelvin. This describes how well heat flows between the bodies.
+ /// The amount of heat in joules that was added to the heat container.
+ /// A positive value indicates heat transfer from a hot body to a cold heat container c.
+ ///
+ /// This performs a single step using the Euler method for solving the Fourier heat equation
+ /// \frac{dQ}{dt} = g \Delta T.
+ /// If we need more precision in the future consider using a higher order integration scheme.
+ /// If we need support for larger time steps in the future consider adding a method to split the time delta into several
+ /// integration steps with adaptive step size.
+ ///
+ [PublicAPI]
+ public static float ConductHeat(this HeatContainer c, float temp, float deltaTime, float g)
+ {
+ var dQ = c.ConductHeatQuery(temp, deltaTime, g);
+ c.AddHeat(dQ);
+ return dQ;
+ }
+
+ ///
+ /// Conducts heat between two s,
+ /// given some constant thermal conductance g and a small time delta.
+ ///
+ /// The first to conduct heat to.
+ /// The second to conduct heat to.
+ ///
+ /// The amount of time that the heat is allowed to conduct, in seconds.
+ /// This value should be small such that deltaTime << C / g where C is the heat capacity of the containers.
+ /// If you need to simulate a larger time step split it into several smaller ones.
+ ///
+ /// The thermal conductance in watt per kelvin. This describes how well heat flows between the bodies.
+ /// The amount of heat in joules that is exchanged between the bodies.
+ /// A positive value indicates heat transfer from a hot cB to a cold cA.
+ ///
+ /// This performs a single step using the Euler method for solving the Fourier heat equation
+ /// \frac{dQ}{dt} = g \Delta T.
+ /// If we need more precision in the future consider using a higher order integration scheme.
+ /// If we need support for larger time steps in the future consider adding a method to split the time delta into several
+ /// integration steps with adaptive step size.
+ ///
+ [PublicAPI]
+ public static float ConductHeat(this HeatContainer cA, HeatContainer cB, float deltaTime, float g)
+ {
+ var dQ = ConductHeatQuery(cA, cB.Temperature, deltaTime, g);
+ cA.AddHeat(dQ);
+ cB.AddHeat(-dQ);
+ return dQ;
+ }
+
+ ///
+ /// Calculates the amount of heat that would be conducted between a and some body with a different temperature,
+ /// given some constant thermal conductance g and a small time delta.
+ ///
+ /// The to conduct heat to.
+ /// The temperature of the second object that we are conducting heat with, in kelvin.
+ ///
+ /// The amount of time that the heat is allowed to conduct, in seconds.
+ /// This value should be small such that deltaTime << C / g where C is the heat capacity of the container.
+ /// If you need to simulate a larger time step split it into several smaller ones.
+ ///
+ /// The thermal conductance in watt per kelvin. This describes how well heat flows between the bodies.
+ /// The amount of heat in joules that would be exchanged between the bodies.
+ /// A positive value indicates heat transfer from a hot body to a cold heat container c.
+ ///
+ /// This performs a single step using the Euler method for solving the Fourier heat equation
+ /// \frac{dQ}{dt} = g \Delta T.
+ /// If we need more precision in the future consider using a higher order integration scheme.
+ /// If we need support for larger time steps in the future consider adding a method to split the time delta into several
+ /// integration steps with adaptive step size.
+ ///
+ [PublicAPI]
+ public static float ConductHeatQuery(this HeatContainer c, float temp, float deltaTime, float g)
+ {
+ var dQ = g * (temp - c.Temperature) * deltaTime;
+ var dQMax = Math.Abs(ConductHeatToTempQuery(c, temp));
+
+ // Clamp the transferred heat amount in case we are overshooting the equilibrium temperature because our time step was too large.
+ return Math.Clamp(dQ, -dQMax, dQMax);
+ }
+
+ ///
+ /// Calculates the amount of heat that would be conducted between two s,
+ /// given some conductivity constant k and a time delta. Does not modify the containers.
+ ///
+ /// The first to conduct heat to.
+ /// The second to conduct heat to.
+ ///
+ /// The amount of time that the heat is allowed to conduct, in seconds.
+ /// This value should be small such that deltaTime << C / g where C is the heat capacity of the container.
+ /// If you need to simulate a larger time step split it into several smaller ones.
+ ///
+ /// The thermal conductance in watt per kelvin. This describes how well heat flows between the bodies.
+ /// The amount of heat in joules that would be exchanged between the bodies.
+ /// A positive value indicates heat transfer from a hot c2 to a cold c1.
+ ///
+ /// This performs a single step using the Euler method for solving the Fourier heat equation
+ /// \frac{dQ}{dt} = g \Delta T.
+ /// If we need more precision in the future consider using a higher order integration scheme.
+ /// If we need support for larger time steps in the future consider adding a method to split the time delta into several
+ /// integration steps with adaptive step size.
+ ///
+ [PublicAPI]
+ public static float ConductHeatQuery(this HeatContainer c1, HeatContainer c2, float deltaTime, float g)
+ {
+ return ConductHeatQuery(c1, c2.Temperature, deltaTime, g);
+ }
+
+ ///
+ /// Changes the temperature of a to a target temperature by
+ /// adding or removing the necessary amount of heat.
+ ///
+ /// The to change the temperature of.
+ /// The desired temperature to reach.
+ /// The amount of heat in joules that was transferred to or from the
+ /// to reach the target temperature.
+ /// A positive value indicates heat must be added to the container to reach the target temperature.
+ [PublicAPI]
+ public static float ConductHeatToTemp(this HeatContainer c, float targetTemp)
+ {
+ var dQ = ConductHeatToTempQuery(c, targetTemp);
+ c.Temperature = targetTemp;
+ return dQ;
+ }
+
+ ///
+ /// Determines the amount of heat that must be transferred to or from a
+ /// to reach a target temperature. Does not modify the heat container.
+ ///
+ /// The to query.
+ /// The desired temperature to reach.
+ /// The amount of heat in joules that must be transferred to or from the
+ /// to reach the target temperature.
+ /// A positive value indicates heat must be added to the container to reach the target temperature.
+ [PublicAPI]
+ public static float ConductHeatToTempQuery(this HeatContainer c, float targetTemp)
+ {
+ return (targetTemp - c.Temperature) * c.HeatCapacity;
+ }
+}
diff --git a/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.Divide.cs b/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.Divide.cs
new file mode 100644
index 0000000000..f1e7206683
--- /dev/null
+++ b/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.Divide.cs
@@ -0,0 +1,55 @@
+using JetBrains.Annotations;
+
+namespace Content.Shared.Temperature.HeatContainer;
+
+public static partial class HeatContainerHelpers
+{
+ ///
+ /// Splits a into two.
+ ///
+ /// The to split. This will be modified to contain the remaining heat capacity.
+ /// The fraction of the heat capacity to move to the new container. Clamped between 0 and 1.
+ /// A new containing the specified fraction of the original container's heat capacity and the same temperature.
+ [PublicAPI]
+ public static HeatContainer Split(this ref HeatContainer c, float fraction = 0.5f)
+ {
+ fraction = Math.Clamp(fraction, 0f, 1f);
+ var newHeatCapacity = c.HeatCapacity * fraction;
+
+ var newContainer = new HeatContainer
+ {
+ HeatCapacity = newHeatCapacity,
+ Temperature = c.Temperature,
+ };
+
+ c.HeatCapacity -= newHeatCapacity;
+
+ return newContainer;
+ }
+
+ ///
+ /// Divides a source into a specified number of equal parts.
+ ///
+ /// The input to split.
+ /// The number of s
+ /// to split the source into.
+ /// Thrown when attempting to divide the source container by zero.
+ /// An array of s equally split from the source .
+ [PublicAPI]
+ public static HeatContainer[] Divide(this HeatContainer c, uint num)
+ {
+ if (num == 0)
+ throw new ArgumentException("Cannot divide by zero.", nameof(num));
+
+ var fraction = 1f / num;
+ var cFrac = c.Split(fraction);
+ var containers = new HeatContainer[num];
+
+ for (var i = 0; i < num; i++)
+ {
+ containers[i] = cFrac;
+ }
+
+ return containers;
+ }
+}
diff --git a/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.Exchange.cs b/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.Exchange.cs
new file mode 100644
index 0000000000..6b7eb125dc
--- /dev/null
+++ b/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.Exchange.cs
@@ -0,0 +1,214 @@
+using JetBrains.Annotations;
+
+namespace Content.Shared.Temperature.HeatContainer;
+
+public static partial class HeatContainerHelpers
+{
+ #region 2-Body Exchange
+
+ ///
+ /// Determines the amount of heat energy that must be transferred between two heat containers
+ /// to bring them into thermal equilibrium.
+ /// Does not modify the containers.
+ ///
+ /// The first to exchange heat.
+ /// The second to exchange heat with.
+ /// The amount of heat in joules that is needed
+ /// to bring the containers to thermal equilibrium.
+ /// A positive value indicates heat transfer from a hot cA to a cold cB.
+ [PublicAPI]
+ public static float EquilibriumHeatQuery(this HeatContainer cA, HeatContainer cB)
+ {
+ /*
+ The solution is derived from the following facts:
+ 1. Let Q be the amount of heat energy transferred from cA to cB.
+ 2. T_A > T_B, so heat will flow from cA to cB.
+ 3. The energy lost by T_A is equal to Q = C_A * (T_A_initial - T_A_final)
+ 4. The energy gained by T_B is equal to Q = C_B * (T_B_final - T_B_initial)
+ 5. Energy is conserved. So T_A_final and T_B_final can be expressed as:
+ T_A_final = T_A_initial - Q / C_A
+ T_B_final = T_B_initial + Q / C_B
+ 6. At thermal equilibrium, T_A_final = T_B_final.
+ 7. Solve for Q.
+ */
+ return (cA.Temperature - cB.Temperature) *
+ (cA.HeatCapacity * cB.HeatCapacity / (cA.HeatCapacity + cB.HeatCapacity));
+ }
+
+ ///
+ /// Determines the resulting temperature if two heat containers are brought into thermal equilibrium.
+ /// Does not modify the containers.
+ ///
+ /// The first to exchange heat.
+ /// The second to exchange heat with.
+ /// The resulting equilibrium temperature both containers will be at.
+ [PublicAPI]
+ public static float EquilibriumTemperatureQuery(this HeatContainer cA, HeatContainer cB)
+ {
+ // Insert the above solution for Q into T_A_final = T_A_initial - Q / C_A and rearrange the result.
+ return (cA.HeatCapacity * cA.Temperature - cB.HeatCapacity * cB.Temperature) / (cA.HeatCapacity + cB.HeatCapacity);
+ }
+
+ ///
+ /// Brings two s into thermal equilibrium by exchanging heat.
+ ///
+ /// The first to exchange heat.
+ /// The second to exchange heat with.
+ [PublicAPI]
+ public static void Equilibrate(this HeatContainer cA, HeatContainer cB)
+ {
+ var tFinal = EquilibriumTemperatureQuery(cA, cB);
+ cA.Temperature = tFinal;
+ cB.Temperature = tFinal;
+ }
+
+ ///
+ /// Brings two s into thermal equilibrium by exchanging heat.
+ ///
+ /// The first to exchange heat.
+ /// The second to exchange heat with.
+ /// The amount of heat in joules that was transferred from container A to B.
+ [PublicAPI]
+ public static void Equilibrate(this HeatContainer cA, HeatContainer cB, out float dQ)
+ {
+ var tInitialA = cA.Temperature;
+ var tFinal = EquilibriumTemperatureQuery(cA, cB);
+ cA.Temperature = tFinal;
+ cB.Temperature = tFinal;
+ dQ = (tInitialA - tFinal) / cA.HeatCapacity;
+ }
+
+ #endregion
+
+ #region N-Body Exchange
+
+ ///
+ /// Brings an array of s into thermal equilibrium by exchanging heat.
+ ///
+ /// The array of s to bring into thermal equilibrium.
+ [PublicAPI]
+ public static void Equilibrate(this HeatContainer[] cN)
+ {
+ var tF = cN.EquilibriumTemperatureQuery();
+ for (var i = 0; i < cN.Length; i++)
+ {
+ cN[i].Temperature = tF;
+ }
+ }
+
+ ///
+ /// Brings a into thermal equilibrium
+ /// with an array of other s by exchanging heat.
+ ///
+ /// The first to bring into thermal equilibrium.
+ /// The array of s to bring into thermal equilibrium.
+ [PublicAPI]
+ public static void Equilibrate(this HeatContainer cA, HeatContainer[] cN)
+ {
+ var tF = cA.EquilibriumTemperatureQuery(cN);
+
+ cA.Temperature = tF;
+ for (var i = 0; i < cN.Length; i++)
+ {
+ cN[i].Temperature = tF;
+ }
+ }
+
+ ///
+ /// Determines the final temperature of an array of s
+ /// when they are brought into thermal equilibrium. Does not modify the containers.
+ ///
+ /// The array of s to bring into thermal equilibrium.
+ /// The temperature of all s involved after reaching thermal equilibrium.
+ [PublicAPI]
+ public static float EquilibriumTemperatureQuery(this HeatContainer[] cN)
+ {
+ /*
+ The solution is derived via the following:
+
+ 1. In thermal equilibrium all bodies have the same temperature T_f.
+
+ 2. Heat exchange for each body is defined by the equation \Delta Q_n = C_n \Delta T_n = C_n (T_f - T_n)
+ where C_n is the heat capacity and \Delta T_n the change in temperature of the n-th body.
+
+ 3. Heat energy must be conserved, so the sum of all heat changes must equal zero.
+ Therefore, \sum_{n=1}^{N} Q_n = 0.
+
+ 4. Substitute and expand.
+ \sum_{n=1}^{N} C_n (T_f - T_n) = 0.
+
+ 5. Unroll and expand.
+ C_1(T_f - T_1) + C_2(T_f - T_2) + ... + C_n(T_f - T_n) = 0
+ C_1 T_f - C_1 T_1 + C_2 T_f - C_2 T_2 + ... + C_n T_f - C_n T_n = 0
+
+ 6. Group like terms.
+ T_f(C_1 + C_2 + ... + C_n) - (C_1 T_1 + C_2 T_2 + ... + C_n T_n) = 0
+
+ 7. Solve.
+ T_f(C_1 + C_2 + ... + C_n) = (C_1 T_1 + C_2 T_2 + ... + C_n T_n)
+ T_f = \frac{C_1 T_1 + C_2 T_2 + ... + C_n T_n}{C_1 + C_2 + ... + C_n}
+
+ 8. Summation.
+ T_f = \frac{\sum(C_n T_n)}{\sum(C_n)}
+ */
+
+ var numerator = 0f;
+ var denominator = 0f;
+
+ foreach (var c in cN)
+ {
+ numerator += c.HeatCapacity * c.Temperature;
+ denominator += c.HeatCapacity;
+ }
+
+ return numerator / denominator;
+ }
+
+ ///
+ /// Determines the final temperature of an array of s
+ /// when they are brought into thermal equilibrium. Does not modify the containers.
+ ///
+ /// The array of s to bring into thermal equilibrium.
+ /// The amount of heat in joules that was added to each container
+ /// to reach thermal equilibrium.
+ /// The temperature of all s involved after reaching thermal equilibrium.
+ [PublicAPI]
+ public static float EquilibriumTemperatureQuery(this HeatContainer[] cN, out float[] dQ)
+ {
+ /*
+ For finding the total heat exchanged during the equalization between a group of bodies
+ take the difference of the internal energy before and after the exchange.
+
+ dQ = C * (T_f - T_i) for each container
+ */
+
+ var tF = cN.EquilibriumTemperatureQuery();
+ dQ = new float[cN.Length];
+
+ for (var i = 0; i < cN.Length; i++)
+ {
+ dQ[i] = cN[i].HeatCapacity * (tF - cN[i].Temperature);
+ }
+
+ return tF;
+ }
+
+ ///
+ /// Determines the final temperature of a when it is brought into thermal equilibrium
+ /// with an array of other s. Does not modify the containers.
+ ///
+ /// The first to bring into thermal equilibrium.
+ /// The array of s to bring into thermal equilibrium.
+ /// The temperature of all s involved after reaching thermal equilibrium.
+ [PublicAPI]
+ public static float EquilibriumTemperatureQuery(this HeatContainer cA, HeatContainer[] cN)
+ {
+ var cAll = new HeatContainer[cN.Length + 1];
+ cAll[0] = cA;
+ cN.CopyTo(cAll, 1);
+
+ return cAll.EquilibriumTemperatureQuery();
+ }
+
+ #endregion
+}
diff --git a/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.Merge.cs b/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.Merge.cs
new file mode 100644
index 0000000000..d9f2cdd3e7
--- /dev/null
+++ b/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.Merge.cs
@@ -0,0 +1,66 @@
+using JetBrains.Annotations;
+
+namespace Content.Shared.Temperature.HeatContainer;
+
+public static partial class HeatContainerHelpers
+{
+ ///
+ /// Merges two heat containers into one, conserving total internal energy.
+ ///
+ /// The first to merge. This will be modified to contain the merged result.
+ /// The second to merge.
+ [PublicAPI]
+ public static void Merge(this ref HeatContainer cA, HeatContainer cB)
+ {
+ var merged = new HeatContainer
+ {
+ HeatCapacity = cA.HeatCapacity + cB.HeatCapacity,
+ Temperature = (cA.InternalEnergy + cB.InternalEnergy) / (cA.HeatCapacity + cB.HeatCapacity)
+ };
+
+ cA = merged;
+ }
+
+
+ ///
+ /// Merges an array of s into a single heat container, conserving total internal energy.
+ ///
+ /// The first to merge.
+ /// This will be modified to contain the merged result.
+ /// The array of s to merge.
+ [PublicAPI]
+ public static void Merge(this ref HeatContainer cA, HeatContainer[] cN)
+ {
+ var cAcN = new HeatContainer[cN.Length + 1];
+ cAcN[0] = cA;
+ cN.CopyTo(cAcN, 1);
+
+ cA = cAcN.Merge();
+ }
+
+ ///
+ /// Merges an array of s into a single heat container, conserving total internal energy.
+ ///
+ /// The array of s to merge.
+ /// A new representing the merged result.
+ [PublicAPI]
+ public static HeatContainer Merge(this HeatContainer[] cN)
+ {
+ var totalHeatCapacity = 0f;
+ var totalEnergy = 0f;
+
+ foreach (var c in cN)
+ {
+ totalHeatCapacity += c.HeatCapacity;
+ totalEnergy += c.InternalEnergy;
+ }
+
+ var result = new HeatContainer
+ {
+ HeatCapacity = totalHeatCapacity,
+ Temperature = totalEnergy / totalHeatCapacity,
+ };
+
+ return result;
+ }
+}
diff --git a/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.cs b/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.cs
new file mode 100644
index 0000000000..39078a810e
--- /dev/null
+++ b/Content.Shared/Temperature/HeatContainer/HeatContainerHelpers.cs
@@ -0,0 +1,52 @@
+using JetBrains.Annotations;
+
+namespace Content.Shared.Temperature.HeatContainer;
+
+///
+/// Class containing helper methods for working with s.
+/// Use these classes instead of implementing your own heat transfer logic.
+///
+public static partial class HeatContainerHelpers
+{
+ ///
+ /// Adds or removes heat energy from the container.
+ /// Positive values add heat, negative values remove heat.
+ /// The temperature can never become lower than 0K even if more heat is removed.
+ ///
+ /// The to add or remove energy.
+ /// The energy in joules to add or remove.
+ [PublicAPI]
+ public static void AddHeat(this HeatContainer c, float dQ)
+ {
+ c.Temperature = c.AddHeatQuery(dQ);
+ }
+
+ ///
+ /// Calculates the resulting temperature of the container after adding or removing heat energy.
+ /// Positive values add heat, negative values remove heat. This method doesn't change the container's state.
+ /// The temperature can never become lower than 0K even if more heat is removed.
+ ///
+ /// The to query.
+ /// The energy in joules to add or remove.
+ /// The resulting temperature in kelvin after the heat change.
+ [PublicAPI]
+ public static float AddHeatQuery(this HeatContainer c, float dQ)
+ {
+ // Don't allow the temperature to go below the absolute minimum.
+ return Math.Max(0f, c.Temperature + dQ / c.HeatCapacity);
+ }
+
+ ///
+ /// Sets the heat capacity of a without altering its thermal energy.
+ /// Adjusts the temperature accordingly to maintain the same internal energy.
+ ///
+ /// The to modify.
+ /// The new heat capacity to set.
+ [PublicAPI]
+ public static void SetHeatCapacity(this HeatContainer c, float newHeatCapacity)
+ {
+ var currentEnergy = c.InternalEnergy;
+ c.HeatCapacity = newHeatCapacity;
+ c.Temperature = currentEnergy / c.HeatCapacity;
+ }
+}