Physics Simulation & Visualization Tool 0.1
A C++ physics simulation engine with real-time 3D visualization
Loading...
Searching...
No Matches
ThermalUtils.cpp
Go to the documentation of this file.
2
3#include <algorithm>
4#include <cmath>
5#include "physics/Constants.h"
6
7namespace Physics::Thermal {
8
9double fourthPower(double value) {
10 const double square = value * value;
11 return square * square;
12}
13
14double clampTemperature(double tempK) {
15 if (!std::isfinite(tempK)) return kMinTemperatureK;
16 return std::clamp(tempK, kMinTemperatureK, kMaxTemperatureK);
17}
18
19double linearTemperatureFactor(double coeffPerK, double tempK, double referenceTempK) {
20 if (!std::isfinite(coeffPerK) || !std::isfinite(tempK) || !std::isfinite(referenceTempK)) return 1.0;
21 return std::max(0.0, 1.0 + coeffPerK * (tempK - referenceTempK));
22}
23
24double effectiveSpecificHeat(const ThermalProperties& props, double tempK) {
25 return static_cast<double>(std::max(props.specificHeat, 0.0f))
27}
28
29double effectiveConductivity(const ThermalProperties& props, double tempK) {
30 return static_cast<double>(std::max(props.conductivity, 0.0f))
32}
33
34double effectiveEmissivity(const ThermalProperties& props, double tempK) {
35 const double value = static_cast<double>(std::clamp(props.emissivity, 0.0f, 1.0f))
37 return std::clamp(value, 0.0, 1.0);
38}
39
40double effectiveAbsorptivity(const ThermalProperties& props, double tempK) {
41 const double value = static_cast<double>(std::clamp(props.absorptivity, 0.0f, 1.0f))
43 return std::clamp(value, 0.0, 1.0);
44}
45
46double effectiveDensity(const ThermalProperties& props, double tempK) {
47 const double density = static_cast<double>(std::max(props.density, 0.0f));
48 if (density <= 0.0) return 0.0;
49
50 const double expansion = 1.0 + 3.0 * static_cast<double>(props.linearExpansionCoeff) * (tempK - props.referenceTempK);
51 return expansion > 0.0 ? density / expansion : density;
52}
53
54double activeThermalMass(double massKg, const ThermalProperties& props) {
55 if (massKg <= 0.0) return 0.0;
56 const double activeMassFraction = std::clamp(static_cast<double>(props.thermalMassFraction), 0.0, 1.0);
57 return massKg * activeMassFraction;
58}
59
60double heatCapacity(double massKg, const ThermalProperties& props) {
61 const double specificHeat = effectiveSpecificHeat(props, props.tempK);
62 if (specificHeat <= 0.0) return 0.0;
63 return activeThermalMass(massKg, props) * specificHeat;
64}
65
66double thermalDiffusivity(const ThermalProperties& props, double tempK) {
67 const double density = effectiveDensity(props, tempK);
68 const double specificHeat = effectiveSpecificHeat(props, tempK);
69 if (density <= 0.0 || specificHeat <= 0.0) return 0.0;
70 return effectiveConductivity(props, tempK) / (density * specificHeat);
71}
72
73double biotNumber(const ThermalProperties& props, double characteristicLengthM) {
74 const double conductivity = effectiveConductivity(props, props.tempK);
75 if (characteristicLengthM <= 0.0 || conductivity <= 0.0) return 0.0;
76 return static_cast<double>(props.heatTransferCoeff) * characteristicLengthM / conductivity;
77}
78
79double fourierNumber(const ThermalProperties& props, double characteristicLengthM, double elapsedSeconds) {
80 if (characteristicLengthM <= 0.0 || elapsedSeconds <= 0.0) return 0.0;
81 return thermalDiffusivity(props, props.tempK) * elapsedSeconds / (characteristicLengthM * characteristicLengthM);
82}
83
84double carnotEfficiency(double hotTempK, double coldTempK) {
85 hotTempK = clampTemperature(hotTempK);
86 coldTempK = clampTemperature(coldTempK);
87 if (hotTempK <= 0.0 || coldTempK >= hotTempK) return 0.0;
88 return 1.0 - coldTempK / hotTempK;
89}
90
91double specificEntropyChange(double specificHeatJPerKgK, double fromTempK, double toTempK) {
92 fromTempK = clampTemperature(fromTempK);
93 toTempK = clampTemperature(toTempK);
94 if (specificHeatJPerKgK <= 0.0 || fromTempK <= 0.0 || toTempK <= 0.0) return 0.0;
95 return specificHeatJPerKgK * std::log(toTempK / fromTempK);
96}
97
98double convectionHeatRate(const ThermalProperties& props, double areaM2, double ambientTempK) {
99 if (areaM2 <= 0.0 || props.heatTransferCoeff <= 0.0f) return 0.0;
100 return static_cast<double>(props.heatTransferCoeff) * areaM2 * (ambientTempK - props.tempK);
101}
102
103double ambientRadiationHeatRate(const ThermalProperties& props, double areaM2, double ambientTempK) {
104 const double tObj = clampTemperature(props.tempK);
105 const double tAmb = clampTemperature(ambientTempK);
106 const double emissivity = effectiveEmissivity(props, tObj);
107 if (areaM2 <= 0.0 || emissivity <= 0.0) return 0.0;
108 return emissivity * Constants::STEFAN_BOLTZMANN * areaM2 * (fourthPower(tAmb) - fourthPower(tObj));
109}
110
111double externalHeatFluxRate(const ThermalProperties& props, double areaM2) {
112 if (areaM2 <= 0.0 || !std::isfinite(props.externalHeatFlux)) return 0.0;
113 return props.externalHeatFlux * areaM2;
114}
115
116double conductiveHeatRate(double conductivityA, double conductivityB, double areaM2, double distanceM, double tempAK, double tempBK) {
117 if (areaM2 <= 0.0 || distanceM <= 0.0 || conductivityA <= 0.0 || conductivityB <= 0.0) return 0.0;
118 const double kEff = 2.0 * conductivityA * conductivityB / (conductivityA + conductivityB);
119 return kEff * areaM2 * (tempBK - tempAK) / std::max(distanceM, kMinConductionDistance);
120}
121
122void applyThermalEnergy(ThermalProperties& props, double massKg, double energyJ) {
123 const double capacity = heatCapacity(massKg, props);
124 if (capacity <= 0.0 || energyJ == 0.0 || !std::isfinite(energyJ)) return;
125
126 const double activeMass = activeThermalMass(massKg, props);
127 const double fusionEnergy = activeMass * static_cast<double>(std::max(props.latentHeatFusion, 0.0f));
128 const double vaporizationEnergy = activeMass * static_cast<double>(std::max(props.latentHeatVaporization, 0.0f));
129 const double meltingPoint = static_cast<double>(std::max(props.meltingPoint, 0.0f));
130 const double boilingPoint = static_cast<double>(std::max(props.boilingPoint, 0.0f));
131
132 auto applySensibleHeatToward = [&](double targetTemp, double& energy) {
133 const double initialTemp = props.tempK;
134 const double specificHeat = effectiveSpecificHeat(props, initialTemp);
135 const double required = (targetTemp - props.tempK) * capacity;
136 if ((energy > 0.0 && required <= 0.0) || (energy < 0.0 && required >= 0.0)) return false;
137 if (std::abs(energy) < std::abs(required)) {
138 props.tempK = clampTemperature(props.tempK + energy / capacity);
139 props.entropyJPerK += activeMass * specificEntropyChange(specificHeat, initialTemp, props.tempK);
140 energy = 0.0;
141 return true;
142 }
143 props.tempK = clampTemperature(targetTemp);
144 props.entropyJPerK += activeMass * specificEntropyChange(specificHeat, initialTemp, props.tempK);
145 energy -= required;
146 return false;
147 };
148
149 auto applyLatentHeat = [&](double latentCapacity, float& progress, double& energy) {
150 if (latentCapacity <= 0.0 || energy == 0.0) return;
151 const double phaseTemp = std::max(props.tempK, 1.0e-9);
152
153 if (energy > 0.0) {
154 const double required = (1.0 - static_cast<double>(progress)) * latentCapacity;
155 if (energy < required) {
156 progress = static_cast<float>(std::clamp(static_cast<double>(progress) + energy / latentCapacity, 0.0, 1.0));
157 props.entropyJPerK += energy / phaseTemp;
158 energy = 0.0;
159 } else {
160 progress = 1.0f;
161 props.entropyJPerK += required / phaseTemp;
162 energy -= required;
163 }
164 } else {
165 const double released = static_cast<double>(progress) * latentCapacity;
166 const double cooling = -energy;
167 if (cooling < released) {
168 progress = static_cast<float>(std::clamp(static_cast<double>(progress) - cooling / latentCapacity, 0.0, 1.0));
169 props.entropyJPerK += energy / phaseTemp;
170 energy = 0.0;
171 } else {
172 progress = 0.0f;
173 props.entropyJPerK -= released / phaseTemp;
174 energy += released;
175 }
176 }
177 };
178
179 if (energyJ > 0.0) {
180 if (meltingPoint > 0.0 && props.tempK < meltingPoint && applySensibleHeatToward(meltingPoint, energyJ)) return;
181 if (meltingPoint > 0.0 && props.tempK <= meltingPoint && props.fusionProgress < 1.0f) {
182 props.tempK = meltingPoint;
183 applyLatentHeat(fusionEnergy, props.fusionProgress, energyJ);
184 if (energyJ == 0.0) return;
185 }
186
187 if (boilingPoint > 0.0 && props.tempK < boilingPoint && applySensibleHeatToward(boilingPoint, energyJ)) return;
188 if (boilingPoint > 0.0 && props.tempK <= boilingPoint && props.vaporizationProgress < 1.0f) {
189 props.tempK = boilingPoint;
190 applyLatentHeat(vaporizationEnergy, props.vaporizationProgress, energyJ);
191 if (energyJ == 0.0) return;
192 }
193
194 const double initialTemp = props.tempK;
195 props.tempK = clampTemperature(props.tempK + energyJ / capacity);
196 props.entropyJPerK += activeMass * specificEntropyChange(effectiveSpecificHeat(props, initialTemp), initialTemp, props.tempK);
197 return;
198 }
199
200 if (boilingPoint > 0.0 && props.tempK > boilingPoint && applySensibleHeatToward(boilingPoint, energyJ)) return;
201 if (boilingPoint > 0.0 && props.tempK >= boilingPoint && props.vaporizationProgress > 0.0f) {
202 props.tempK = boilingPoint;
203 applyLatentHeat(vaporizationEnergy, props.vaporizationProgress, energyJ);
204 if (energyJ == 0.0) return;
205 }
206
207 if (meltingPoint > 0.0 && props.tempK > meltingPoint && applySensibleHeatToward(meltingPoint, energyJ)) return;
208 if (meltingPoint > 0.0 && props.tempK >= meltingPoint && props.fusionProgress > 0.0f) {
209 props.tempK = meltingPoint;
210 applyLatentHeat(fusionEnergy, props.fusionProgress, energyJ);
211 if (energyJ == 0.0) return;
212 }
213
214 const double initialTemp = props.tempK;
215 props.tempK = clampTemperature(props.tempK + energyJ / capacity);
216 props.entropyJPerK += activeMass * specificEntropyChange(effectiveSpecificHeat(props, initialTemp), initialTemp, props.tempK);
217}
218
219void applyConductiveExchange(ThermalProperties& a, double massA, ThermalProperties& b, double massB, double areaM2, double distanceM, double dt) {
220 if (dt <= 0.0) return;
221 const double capA = heatCapacity(massA, a);
222 const double capB = heatCapacity(massB, b);
223 if (capA <= 0.0 || capB <= 0.0) return;
224
225 const double rateToA = conductiveHeatRate(effectiveConductivity(a, a.tempK), effectiveConductivity(b, b.tempK), areaM2, distanceM, a.tempK, b.tempK);
226 if (!std::isfinite(rateToA) || rateToA == 0.0) return;
227
228 const double equilibriumEnergy = (b.tempK - a.tempK) * capA * capB / (capA + capB);
229 double energyToA = rateToA * dt;
230 if (std::abs(energyToA) > std::abs(equilibriumEnergy)) {
231 energyToA = equilibriumEnergy;
232 }
233
234 applyThermalEnergy(a, massA, energyToA);
235 applyThermalEnergy(b, massB, -energyToA);
236}
237
238}
constexpr double STEFAN_BOLTZMANN
Definition Constants.h:6
double specificEntropyChange(double specificHeatJPerKgK, double fromTempK, double toTempK)
double conductiveHeatRate(double conductivityA, double conductivityB, double areaM2, double distanceM, double tempAK, double tempBK)
double effectiveConductivity(const ThermalProperties &props, double tempK)
double effectiveAbsorptivity(const ThermalProperties &props, double tempK)
double convectionHeatRate(const ThermalProperties &props, double areaM2, double ambientTempK)
void applyConductiveExchange(ThermalProperties &a, double massA, ThermalProperties &b, double massB, double areaM2, double distanceM, double dt)
constexpr double kMaxTemperatureK
double heatCapacity(double massKg, const ThermalProperties &props)
constexpr double kMinTemperatureK
Definition ThermalUtils.h:9
double activeThermalMass(double massKg, const ThermalProperties &props)
double effectiveSpecificHeat(const ThermalProperties &props, double tempK)
void applyThermalEnergy(ThermalProperties &props, double massKg, double energyJ)
double effectiveDensity(const ThermalProperties &props, double tempK)
constexpr double kMinConductionDistance
double externalHeatFluxRate(const ThermalProperties &props, double areaM2)
double thermalDiffusivity(const ThermalProperties &props, double tempK)
double ambientRadiationHeatRate(const ThermalProperties &props, double areaM2, double ambientTempK)
double biotNumber(const ThermalProperties &props, double characteristicLengthM)
double clampTemperature(double tempK)
double effectiveEmissivity(const ThermalProperties &props, double tempK)
double carnotEfficiency(double hotTempK, double coldTempK)
double fourthPower(double value)
double linearTemperatureFactor(double coeffPerK, double tempK, double referenceTempK)
double fourierNumber(const ThermalProperties &props, double characteristicLengthM, double elapsedSeconds)