OpenSWMM Engine  6.0.0-alpha.4
Data-oriented, plugin-extensible SWMM Engine (6.0.0-alpha.4)
Loading...
Searching...
No Matches
ThermalCheckpoint.hpp
Go to the documentation of this file.
1// SPDX-License-Identifier: Apache-2.0
2
11
12#pragma once
13
14#include "GroundHeat.hpp"
15#include "MeshHeat.hpp"
20#include <cmath>
21#include <stdexcept>
22
25
40
51
66
77inline void heatConfig(Archive& a, HeatConfigData& h) {
80 radiative(a, h.radiative);
81 sediment(a, h.sediment);
82 auto rows = h.overrides.rows;
83 a.sequence(rows, [&](auto& r) { a(r.attr, r.scope, r.name, r.value); });
84 auto& s = h.solar;
85 a(s.latitude_deg, s.longitude_deg, s.timezone_hours, s.has_latitude, s.has_longitude,
86 s.has_timezone, s.elevation_m, s.has_elevation, s.aod380, s.aod500, s.precip_water_cm,
87 s.ozone_cm, s.ground_albedo);
88 auto& c = h.cloud;
89 a(c.configured, c.use_timeseries, c.ts_index, c.fraction, c.sw_atten_k, c.sw_atten_n,
90 c.lw_cloud_k);
91 for (double& value : h.global_temp)
92 a(value);
93}
94
118inline void groundHeat(Archive& a, GroundHeatState& h, bool model) {
119 bool active = h.active();
120 a(active);
121 if (!active)
122 return;
123 if (!h.config)
124 throw std::runtime_error("Active thermal checkpoint has no groundwater configuration.");
125 if (model) {
127 auto o = h.config->options;
128 a(o.thermal_model, o.thermal_layers, o.lateral_conduction, o.thermal_max_step,
129 o.water_conductivity, o.air_conductivity, o.surface_resistance, o.soil_et_heat,
130 o.transport_pollutants, o.transport_msx, o.transport_age, o.transport_temperature,
131 o.dispersion, o.conduction, o.surface_thermal_bc, o.surface_thermal_arg,
132 o.deep_thermal_bc, o.deep_thermal_arg, o.deep_depth, o.thermal_mixing, o.c_diff,
133 o.authored);
134 std::vector<std::string> authored(o.thermal_authored.begin(), o.thermal_authored.end());
135 a(authored);
136 auto params = h.config->params;
137 a.sequence(params, [&](auto& p) {
138 a(p.scope, p.tag, p.cell, p.rho_s, p.c_s, p.lambda_s, p.a_s, p.alpha_L, p.alpha_T,
139 p.D_m, p.D_v, p.geo_flux);
140 });
141 a.sequence(
142 h.cells,
143 [&](auto& c) {
144 material(a, c.column.material);
145 a(c.column.area, c.column.depth_edges, c.column.initial_energy, c.initial_j,
146 c.geo_flux, c.surface_owned);
147 auto& p = c.porewater;
148 a(p.n_species, p.age_row, p.temp_row, p.retention, p.initial_mass);
149 if (p.sigma.active()) {
150 std::string profile = "SIGMA porous overlap v1";
151 a(profile, p.sigma.layers, p.sigma.species);
152 }
153 },
154 true);
155 std::vector<unsigned char> snow_contact(h.cells.size());
156 for (std::size_t i = 0; i < snow_contact.size(); ++i)
157 snow_contact[i] = h.snowContactEnabled(int(i));
158 a(snow_contact);
159 return;
160 }
161 std::string runtime_profile = "ground heat runtime v3";
162 a(runtime_profile);
163 a(h.top_now, h.deep_now);
164 // Ground columns may be initialized lazily on the first accepted thermal
165 // step. Their model identity is frozen separately, so restore the
166 // accepted cell payload into a fresh target whose runtime vector is still
167 // empty.
168 a.sequence(h.cells, [&](auto& c) {
169 auto& col = c.column;
170 auto& p = c.porewater;
171 a.fixed(col.theta, col.capacity, col.energy, col.conductivity, c.saturated_water,
172 c.reserved_out_water, p.mass, p.contact_mass, p.reserved_out, p.generated_age);
173 if (p.sigma.active()) {
174 auto& m = p.sigma;
175 std::string profile = "SIGMA porous overlap receipts v1";
176 a(profile);
177 if (a.reading() && profile != "SIGMA porous overlap receipts v1")
178 throw std::runtime_error("Unsupported SIGMA porous overlap checkpoint.");
179 a.sequence(m.overlaps, [&](auto& o) { a(o.physical, o.reactor, o.water, o.bulk); });
180 a(m.capacity, m.mass, m.reserved, m.pending);
181 }
182 a(c.advective_j, c.source_j, c.surface_j, c.deep_j, c.latent_j, c.lateral_j,
183 c.water_contact_j, c.channel_contact_j, c.lid_contact_j, c.vertical_absolute_j, c.failed,
184 c.error);
185 });
186 if (a.reading()) {
187 const auto bad = [](const char* reason) {
188 throw std::runtime_error(std::string("Invalid thermal ground checkpoint state: ") +
189 reason + ".");
190 };
191 for (const auto& c : h.cells) {
192 const auto& col = c.column;
193 if (col.theta.empty() && col.depth_edges.empty() && col.capacity.empty() &&
194 col.energy.empty() && col.conductivity.empty() && c.saturated_water.empty() &&
195 c.reserved_out_water.empty() && c.porewater.mass.empty() &&
196 c.porewater.n_species == 0)
197 continue;
198 for (double value : {c.initial_j, c.advective_j, c.source_j, c.surface_j, c.deep_j,
199 c.latent_j, c.lateral_j, c.water_contact_j, c.channel_contact_j,
200 c.lid_contact_j, c.vertical_absolute_j})
201 if (!std::isfinite(value))
202 bad("nonfinite thermal ledger value");
203 if (col.theta.size() + 1 != col.depth_edges.size() ||
204 col.capacity.size() != col.theta.size() || col.energy.size() != col.theta.size() ||
205 col.conductivity.size() != col.theta.size() ||
206 c.saturated_water.size() != col.theta.size() ||
207 c.reserved_out_water.size() != col.theta.size() ||
208 c.porewater.mass.size() != c.porewater.n_species * col.theta.size() ||
209 c.porewater.reserved_out.size() != c.porewater.mass.size()) {
210 throw std::runtime_error(
211 "Invalid thermal ground checkpoint layer shapes (layers=" +
212 std::to_string(col.theta.size()) +
213 ", edges=" + std::to_string(col.depth_edges.size()) +
214 ", capacity=" + std::to_string(col.capacity.size()) +
215 ", energy=" + std::to_string(col.energy.size()) +
216 ", conductivity=" + std::to_string(col.conductivity.size()) +
217 ", saturation=" + std::to_string(c.saturated_water.size()) +
218 ", porewater=" + std::to_string(c.porewater.mass.size()) +
219 ", porewater species=" + std::to_string(c.porewater.n_species) + ").");
220 }
221 for (std::size_t j = 0; j < col.theta.size(); ++j)
222 if (!std::isfinite(col.theta[j]) || col.theta[j] < 0 ||
223 col.theta[j] > col.material.porosity || !(col.capacity[j] > 0) ||
224 !std::isfinite(col.capacity[j]) || !std::isfinite(col.energy[j]) ||
225 !(col.conductivity[j] > 0) || !std::isfinite(col.conductivity[j]) ||
226 !std::isfinite(c.saturated_water[j]) || c.saturated_water[j] < 0 ||
227 !std::isfinite(c.reserved_out_water[j]) || c.reserved_out_water[j] < 0 ||
228 !(col.capacity[j] - col.material.water_heat_capacity * c.reserved_out_water[j] >
229 0))
230 bad("invalid layer thermodynamic value or outgoing heat-capacity reservation");
231 const auto& p = c.porewater;
232 if (p.mass.size() != p.reserved_out.size())
233 bad("invalid porewater reservation shape");
234 if (p.sigma.active()) {
235 const auto& m = p.sigma;
236 const auto ne = m.overlaps.size();
237 if (m.species != p.n_species || m.layers < 1 ||
238 m.capacity.size() != std::size_t(m.species) * ne ||
239 m.mass.size() != m.capacity.size() || m.reserved.size() != m.mass.size() ||
240 m.pending.size() != std::size_t(m.species) * (m.layers + 1))
241 bad("SIGMA mapping shape");
242 for (const auto& o : m.overlaps)
243 if (o.physical < 0 || std::size_t(o.physical) >= col.theta.size() ||
244 o.reactor < 0 || o.reactor > m.layers || !std::isfinite(o.water) ||
245 o.water < 0 || !std::isfinite(o.bulk) || o.bulk <= 0)
246 bad("SIGMA mapping geometry");
247 for (std::size_t k = 0; k < m.mass.size(); ++k)
248 if (!std::isfinite(m.capacity[k]) || m.capacity[k] < 0 ||
249 !std::isfinite(m.mass[k]) || m.mass[k] < 0 ||
250 !std::isfinite(m.reserved[k]) || m.reserved[k] < 0 ||
251 m.reserved[k] > m.mass[k])
252 bad("SIGMA mapping inventory or reservation");
253 for (double delta : m.pending)
254 if (!std::isfinite(delta))
255 bad("SIGMA mapping receipt");
256 }
257 for (std::size_t k = 0; k < p.mass.size(); ++k)
258 if (!std::isfinite(p.mass[k]) || p.mass[k] < 0 ||
259 !std::isfinite(p.reserved_out[k]) || p.reserved_out[k] < 0 ||
260 p.reserved_out[k] > p.mass[k])
261 bad("invalid porewater inventory or outgoing reservation");
262 }
263 }
264}
265
278inline std::string groundHeatModelIdentity(const GroundHeatState& state) {
279 Archive a;
280 auto copy = state;
281 groundHeat(a, copy, true);
282 return a.bytes();
283}
284
295inline std::string meshHeatModelIdentity(const MeshHeatState& state) {
296 Archive a;
297 auto h = state;
298 a(h.enabled, h.surface, h.radiative, h.bed, h.surface_inherits, h.radiative_inherits,
299 h.ground_column, h.bed_species, h.bed_age_row, h.bed_temp_row);
300 if (!h.enabled)
301 return a.bytes();
302 a.fixed(h.aquifer_owned, h.hyporheic_eligible, h.initial_bed_energy, h.bed_pore_volume,
303 h.bed_initial_mass);
304 auto ground = groundHeatModelIdentity(h.standalone_ground);
305 a(ground);
306 if (h.context) {
307 auto config = h.context->heat_config;
308 heatConfig(a, config);
309 }
310 return a.bytes();
311}
312
330inline void meshHeat(Archive& a, MeshHeatState& h) {
331 if (!h.enabled)
332 return;
333 a(h.ground_now);
337 groundHeat(a, h.standalone_ground, false);
338 if (!h.standalone_ground.cells.empty() &&
339 h.standalone_ground.cells.size() != h.reported_temperature.size())
340 throw std::runtime_error("Thermal ground checkpoint cell shape differs from the mesh.");
341 if (a.reading()) {
342 const auto bad = []() {
343 throw std::runtime_error("Invalid thermal mesh checkpoint state.");
344 };
345 for (const auto* row :
349 for (double v : *row)
350 if (!std::isfinite(v))
351 bad();
352 for (double v : h.bed_capacity)
353 if (!(v > 0) || !std::isfinite(v))
354 bad();
355 for (double v : h.reported_temperature)
356 if (!std::isfinite(v))
357 bad();
358 for (double v : h.evaporated_m3)
359 if (v < 0)
360 bad();
361 for (int v : h.failures)
362 if (v < 0)
363 bad();
364 for (double v : h.bed_carried_mass)
365 if (v < 0)
366 bad();
367 const auto n = h.reported_temperature.size();
368 for (const auto* row : {&h.atmospheric_j, &h.radiative_j, &h.sensible_j, &h.ground_j,
370 if (row->size() != n)
371 bad();
372 if (h.failures.size() != n ||
373 (!h.channel_footprint.empty() && h.channel_footprint.size() != n) ||
374 h.bed_energy.size() != h.bed_capacity.size() ||
375 (!h.bed_energy.empty() && h.bed_energy.size() != n) ||
376 (!h.bed_carried_mass.empty() &&
377 h.bed_carried_mass.size() != n * std::size_t(h.bed_species)) ||
378 (!h.bed_exchange_mass.empty() &&
379 h.bed_exchange_mass.size() != n * std::size_t(h.bed_species)) ||
380 (!h.bed_generated_age.empty() && h.bed_generated_age.size() != n))
381 bad();
382 }
383}
384} // namespace openswmm::twoD::checkpoint
Versioned archives of completed source-boundary state.
Thermal state and exchange bookkeeping for fixed ground layers.
U4 (2026-09-07) — authoring state for subsurface (groundwater) transport: [GW_TRANSPORT_OPTIONS],...
Heat-transport data (heat plan §1, §3; phase H1).
Mesh thermal state, including bed energy retained through surface drying.
The central, reentrant simulation context for the new engine.
Definition CompletedSourceArchive.hpp:25
Definition MeshCheckpointState.hpp:18
detail::CompletedSourceArchive Archive
Definition ThermalCheckpoint.hpp:24
void meshHeat(Archive &a, MeshHeatState &h)
Archive accepted mesh heat runtime state against a separately prepared model.
Definition ThermalCheckpoint.hpp:330
void sediment(Archive &a, SedimentConfig &s)
Archive the explicit sediment and ground-exchange configuration fields.
Definition ThermalCheckpoint.hpp:60
void material(Archive &a, ThermalMaterial &m)
Archive a ground-column material's explicitly stored thermal properties.
Definition ThermalCheckpoint.hpp:36
void groundHeat(Archive &a, GroundHeatState &h, bool model)
Archive model-identity fields or runtime receipts of a prepared ground-heat state.
Definition ThermalCheckpoint.hpp:118
std::string groundHeatModelIdentity(const GroundHeatState &state)
Encode an owned byte identity of the ground thermal model and initial state.
Definition ThermalCheckpoint.hpp:278
void radiative(Archive &a, RadiativeConfig &r)
Archive the explicit radiative forcing configuration.
Definition ThermalCheckpoint.hpp:47
std::string meshHeatModelIdentity(const MeshHeatState &state)
Encode mesh thermal ownership, options and initial inventories as a model identity.
Definition ThermalCheckpoint.hpp:295
void heatConfig(Archive &a, HeatConfigData &h)
Archive heat configuration fields used to construct model identity.
Definition ThermalCheckpoint.hpp:77
Parsed model.heat state (heat component, phase H1).
Definition HeatData.hpp:324
SolarConfig solar
Definition HeatData.hpp:379
bool radiative_exchange
Definition HeatData.hpp:329
double global_temp[static_cast< int >(HeatSource::COUNT_)]
GLOBAL inlet temperature per source, °C.
Definition HeatData.hpp:389
bool sediment_exchange
Definition HeatData.hpp:364
RadiativeConfig radiative
Parameters for the module above.
Definition HeatData.hpp:332
HeatOverrideData overrides
Definition HeatData.hpp:375
DryTempPolicy dry_temp_policy
[HEAT_FLUXES] DRY_ELEMENT_TEMPERATURE HOLD|AIR|DEFAULT (D-H5c).
Definition HeatData.hpp:347
bool surface_exchange
[HEAT_FLUXES] SURFACE_EXCHANGE ON — latent + sensible exchange at the free surface (plan §2....
Definition HeatData.hpp:344
bool configured
Definition HeatData.hpp:325
bool layer_conduction
Definition HeatData.hpp:352
CloudConfig cloud
Definition HeatData.hpp:383
SedimentConfig sediment
Definition HeatData.hpp:371
std::vector< HeatOverrideRow > rows
as parsed; the serializer's input
Definition HeatData.hpp:288
[RADIATIVE_FLUXES] parameters (heat plan §2.2, phase H3).
Definition HeatData.hpp:237
double sky_view
fsky, 1 = open sky
Definition HeatData.hpp:241
double atm_emiss_coeff
Brunt Aa.
Definition HeatData.hpp:244
double shade_factor
fs, 0 = unshaded
Definition HeatData.hpp:240
double albedo
Rs, reference default.
Definition HeatData.hpp:239
int sw_ts_index
Index into ctx.tables under TIMESERIES; -1 otherwise.
Definition HeatData.hpp:270
double lw_reflection
RL.
Definition HeatData.hpp:245
double shortwave_wm2
Incoming solar Jin, W/m² (0 = night)
Definition HeatData.hpp:238
double emiss_landcover
εlc
Definition HeatData.hpp:243
ShortwaveMode sw_mode
Definition HeatData.hpp:268
double landcover_temp
Land-cover radiating temperature, °C. NaN ⇒ use air temperature.
Definition HeatData.hpp:264
double emiss_water
εw
Definition HeatData.hpp:242
[SEDIMENT_EXCHANGE] — the bed zone's material and geometry.
Definition BedZoneData.hpp:88
double bed_thickness
Definition BedZoneData.hpp:109
double initial_temp
Definition BedZoneData.hpp:148
double ground_depth
Definition BedZoneData.hpp:116
double sed_density
Bed bulk density rho_sed, kg/m³ (htsmodel.cpp:50).
Definition BedZoneData.hpp:140
double ground_temp
Deep-ground temperature T_gr, °C. Constant spelling.
Definition BedZoneData.hpp:119
double mesh_porosity
Pore fraction of the new mesh bed; legacy 1D capacity is unchanged.
Definition BedZoneData.hpp:93
bool ground_source_configured
Definition BedZoneData.hpp:91
double hyporheic_velocity
Definition BedZoneData.hpp:137
bool has_ground_temp
Definition BedZoneData.hpp:127
double sed_specific_heat
Bed specific heat c_sed, J/kg/K (htsmodel.cpp:51).
Definition BedZoneData.hpp:143
bool mesh_ground
Explicit routed-channel finite mesh contact; default prescribed ground.
Definition BedZoneData.hpp:89
int ground_ts_index
Definition BedZoneData.hpp:131
double thermal_diffusivity
Definition BedZoneData.hpp:97
bool has_initial_temp
Definition BedZoneData.hpp:149
double solute_diffusivity
Definition BedZoneData.hpp:104
Definition GroundHeat.hpp:45
double water_capacity
Definition GroundHeat.hpp:55
bool active() const noexcept
Test whether the owner has allocated any thermal cell slots.
Definition GroundHeat.hpp:63
double density
Definition GroundHeat.hpp:55
bool snowContactEnabled(int cell) const noexcept
Test reciprocal UEB snow/soil thermal contact availability.
Definition GroundHeat.cpp:505
const GwTransportData * config
Definition GroundHeat.hpp:48
std::vector< GroundThermalCell > cells
Definition GroundHeat.hpp:49
int deep_series
Definition GroundHeat.hpp:56
int top_series
Definition GroundHeat.hpp:56
GwTransportOptions options
Definition GwTransportData.hpp:188
std::vector< GwParamsRow > params
Definition GwTransportData.hpp:189
Definition MeshHeat.hpp:27
std::vector< int > failures
Definition MeshHeat.hpp:40
std::vector< double > water_j
Definition MeshHeat.hpp:38
std::vector< double > water_bed_j
Definition MeshHeat.hpp:38
std::vector< double > radiative_j
Definition MeshHeat.hpp:38
std::vector< double > evaporated_m3
Definition MeshHeat.hpp:39
std::vector< double > ground_j
Definition MeshHeat.hpp:38
std::vector< double > sensible_j
Definition MeshHeat.hpp:38
std::vector< double > channel_footprint
Definition MeshHeat.hpp:35
std::vector< double > latent_j
Definition MeshHeat.hpp:39
bool enabled
Definition MeshHeat.hpp:29
std::vector< double > bed_generated_age
Definition MeshHeat.hpp:50
std::vector< double > reported_temperature
Definition MeshHeat.hpp:37
double ground_now
Definition MeshHeat.hpp:36
GroundHeatState standalone_ground
Definition MeshHeat.hpp:32
std::vector< double > bed_carried_mass
Definition MeshHeat.hpp:49
std::vector< double > atmospheric_j
Definition MeshHeat.hpp:38
std::vector< double > bed_energy
Definition MeshHeat.hpp:37
std::vector< double > bed_exchange_mass
Definition MeshHeat.hpp:49
int bed_species
Definition MeshHeat.hpp:48
std::vector< double > bed_capacity
Definition MeshHeat.hpp:37
Definition PorousHeat.hpp:20
double grain_conductivity
Definition PorousHeat.hpp:22
double grain_density
Definition PorousHeat.hpp:21
double water_conductivity
Definition PorousHeat.hpp:22
bool geometric
Definition PorousHeat.hpp:24
double porosity
Definition PorousHeat.hpp:21
double air_conductivity
Definition PorousHeat.hpp:22
double grain_specific_heat
Definition PorousHeat.hpp:21
double water_heat_capacity
Definition PorousHeat.hpp:23