1.3.49
 
Loading...
Searching...
No Matches
EnergyBalanceModel.cpp
Go to the documentation of this file.
1
16#include "EnergyBalanceModel.h"
17#include "../include/EnergyBalanceModel.h"
18
19#include "global.h"
20
21using namespace helios;
22
24
25 // All default values set here
26
27 temperature_default = 300; // Kelvin
28
29 wind_speed_default = 1.f; // m/s
30
31 air_temperature_default = 300; // Kelvin
32
33 air_humidity_default = 0.5; // %
34
35 pressure_default = 101000; // Pa
36
37 gS_default = 0.; // mol/m^2-s
38
39 Qother_default = 0; // W/m^2
40
41 heatcapacity_default = 0; // J/m^2-oC
42
43 surface_humidity_default = 1; //(unitless)
44
45 air_energy_balance_enabled = false;
46
47 message_flag = true; // print messages to screen by default
48
49 // Copy pointer to the context
50 context = __context;
51}
52
54 run(context->getAllUUIDs());
55}
56
58 run(context->getAllUUIDs(), dt);
59}
60
61void EnergyBalanceModel::run(const std::vector<uint> &UUIDs) {
62 run(UUIDs, 0.f);
63}
64
65
66void EnergyBalanceModel::run(const std::vector<uint> &UUIDs, float dt) {
67
68 if (message_flag) {
69 std::cout << "Running energy balance model..." << std::flush;
70 }
71
72 // Check that some primitives exist in the context
73 if (UUIDs.empty()) {
74 std::cerr << "WARNING (EnergyBalanceModel::run): No primitives have been added to the context. There is nothing to simulate. Exiting..." << std::endl;
75 return;
76 }
77
78 evaluateSurfaceEnergyBalance(UUIDs, dt);
79
80 if (message_flag) {
81 std::cout << "done." << std::endl;
82 }
83}
84
85void EnergyBalanceModel::evaluateAirEnergyBalance(float dt_sec, float time_advance_sec) {
86 evaluateAirEnergyBalance(context->getAllUUIDs(), dt_sec, time_advance_sec);
87}
88
89void EnergyBalanceModel::evaluateAirEnergyBalance(const std::vector<uint> &UUIDs, float dt_sec, float time_advance_sec) {
90
91 if (dt_sec <= 0) {
92 std::cerr << "ERROR (EnergyBalanceModel::evaluateAirEnergyBalance): dt_sec must be greater than zero to run the air energy balance. Skipping..." << std::endl;
93 return;
94 }
95
96 float air_temperature_reference = air_temperature_default; // Default air temperature in Kelvin
97 if (context->doesGlobalDataExist("air_temperature_reference") && context->getGlobalDataType("air_temperature_reference") == helios::HELIOS_TYPE_FLOAT) {
98 context->getGlobalData("air_temperature_reference", air_temperature_reference);
99 }
100
101 float air_humidity_reference = air_humidity_default; // Default air relative humidity
102 if (context->doesGlobalDataExist("air_humidity_reference") && context->getGlobalDataType("air_humidity_reference") == helios::HELIOS_TYPE_FLOAT) {
103 context->getGlobalData("air_humidity_reference", air_humidity_reference);
104 }
105
106 float wind_speed_reference = wind_speed_default; // Default wind speed in m/s
107 if (context->doesGlobalDataExist("wind_speed_reference") && context->getGlobalDataType("wind_speed_reference") == helios::HELIOS_TYPE_FLOAT) {
108 context->getGlobalData("wind_speed_reference", wind_speed_reference);
109 }
110
111 // Variables to be set externally via global data
112
113 float Patm;
114 if (context->doesGlobalDataExist("air_pressure") && context->getGlobalDataType("air_pressure") == helios::HELIOS_TYPE_FLOAT) {
115 context->getGlobalData("air_pressure", Patm);
116 } else {
117 Patm = pressure_default;
118 }
119
120 float air_temperature_average;
121 if (context->doesGlobalDataExist("air_temperature_average") && context->getGlobalDataType("air_temperature_average") == helios::HELIOS_TYPE_FLOAT) {
122 context->getGlobalData("air_temperature_average", air_temperature_average);
123 } else {
124 air_temperature_average = air_temperature_reference;
125 }
126
127 float air_moisture_average;
128 if (context->doesGlobalDataExist("air_moisture_average") && context->getGlobalDataType("air_moisture_average") == helios::HELIOS_TYPE_FLOAT) {
129 context->getGlobalData("air_moisture_average", air_moisture_average);
130 std::cerr << "Reading air_moisture_average from global data: " << air_moisture_average << std::endl;
131 } else {
132 air_moisture_average = air_humidity_reference * esat_Pa(air_temperature_reference) / Patm;
133 }
134
135 float air_temperature_ABL = 0; // initialize to 0 as a flag so that if it's not available from global data, we'll initialize it below
136 if (context->doesGlobalDataExist("air_temperature_ABL") && context->getGlobalDataType("air_temperature_ABL") == helios::HELIOS_TYPE_FLOAT) {
137 context->getGlobalData("air_temperature_ABL", air_temperature_ABL);
138 }
139
140 float air_moisture_ABL;
141 if (context->doesGlobalDataExist("air_moisture_ABL") && context->getGlobalDataType("air_moisture_ABL") == helios::HELIOS_TYPE_FLOAT) {
142 context->getGlobalData("air_moisture_ABL", air_moisture_ABL);
143 std::cerr << "Reading air_moisture_ABL from global data: " << air_moisture_ABL << std::endl;
144 } else {
145 air_moisture_ABL = air_humidity_reference * esat_Pa(air_temperature_reference) / Patm;
146 }
147
148 // Get dimensions of canopy volume
149 if (canopy_dimensions == make_vec3(0, 0, 0)) {
150 vec2 xbounds, ybounds, zbounds;
151 context->getDomainBoundingBox(UUIDs, xbounds, ybounds, zbounds);
152 canopy_dimensions = make_vec3(xbounds.y - xbounds.x, ybounds.y - ybounds.x, zbounds.y - zbounds.x);
153 }
154 assert(canopy_dimensions.x > 0 && canopy_dimensions.y > 0 && canopy_dimensions.z > 0);
155 if (canopy_height_m == 0) {
156 canopy_height_m = canopy_dimensions.z; // Set the canopy height if not already set
157 }
158 if (reference_height_m == 0) {
159 reference_height_m = canopy_height_m; // Set the reference height if not already set
160 }
161
162 std::cout << "Read in initial values. Ta = " << air_temperature_average << "; e = " << air_moisture_average << "; " << air_moisture_average / esat_Pa(air_temperature_reference) * Patm << std::endl;
163
164 std::cout << "Canopy dimensions: " << canopy_dimensions.x << " x " << canopy_dimensions.y << " x " << canopy_height_m << std::endl;
165
166 float displacement_height = 0.67f * canopy_height_m;
167 float zo_m = 0.1f * canopy_height_m; // Roughness length for momentum transfer (m)
168 float zo_h = 0.01f * canopy_height_m; // Roughness length for heat transfer (m)
169
170 float abl_height_m = 1000.f;
171
172 float time = 0;
173 float air_temperature_old = 0;
174 float air_moisture_old = 0;
175 float air_temperature_ABL_old = 0;
176 float air_moisture_ABL_old = 0;
177 while (time < time_advance_sec) {
178 float dt_actual = dt_sec;
179 if (time + dt_actual > time_advance_sec) {
180 dt_actual = time_advance_sec - time; // Adjust the time step to not exceed the total time
181 }
182
183 std::cout << "Air moisture average: " << air_moisture_average << std::endl;
184
185 // Update the surface energy balance
186 this->run(UUIDs);
187
188 // Calculate temperature source term
189 float sensible_source_flux_W_m3;
190 context->calculatePrimitiveDataAreaWeightedSum(UUIDs, "sensible_flux", sensible_source_flux_W_m3); // units: Watts
191 sensible_source_flux_W_m3 /= canopy_dimensions.x * canopy_dimensions.y * canopy_height_m; // Convert to W/m^3
192
193 // Calculate moisture source term
194 float moisture_source_flux_W_m3;
195 context->calculatePrimitiveDataAreaWeightedSum(UUIDs, "latent_flux", moisture_source_flux_W_m3); // units: Watts
196 moisture_source_flux_W_m3 /= canopy_dimensions.x * canopy_dimensions.y * canopy_height_m; // Convert to W/m^3
197 float moisture_source_flux_mol_s_m3 = moisture_source_flux_W_m3 / lambda_mol; // Convert to mol/s/m^3
198
199 float rho_air_mol_m3 = Patm / (R * air_temperature_average);
200 ; // Molar density of air (mol/m^3).
201
202 // --- canopy / ABL interface & implicit‐Euler update ---
203
204 // 1) canopy‐scale source fluxes per unit ground area
205 float H_s = sensible_source_flux_W_m3 * canopy_height_m; // W m⁻²
206 float E_s = moisture_source_flux_mol_s_m3 * canopy_height_m; // mol m⁻² s⁻¹
207
208 // 2) neutral friction velocity for Obukhov length
209 float u_star_neutral = von_Karman_constant * wind_speed_reference / std::log((reference_height_m - displacement_height) / zo_m);
210
211 // 3) Obukhov length L [m]
212 float rho_air_kg_m3 = rho_air_mol_m3 * 0.02896f;
213 float cp_air_kg = cp_air_mol / 0.02896f;
214 float L = 1e6f;
215 if (std::fabs(H_s) > 1e-6f) {
216 L = -rho_air_kg_m3 * cp_air_kg * std::pow(u_star_neutral, 3) * air_temperature_reference / (von_Karman_constant * 9.81f * H_s);
217 }
218
219 // 4) stability functions (Businger–Dyer)
220 auto psi_unstable_u = [&](float zeta) {
221 float x = std::pow(1.f - 16.f * zeta, 0.25f);
222 return 2.f * std::log((1.f + x) / 2.f) + std::log((1.f + x * x) / 2.f) - 2.f * std::atan(x) + 0.5f * M_PI;
223 };
224 auto psi_unstable_h = [&](float zeta) { return 2.f * std::log((1.f + std::sqrt(1.f - 16.f * zeta)) / 2.f); };
225 auto psi_stable = [&](float zeta) { return -5.f * zeta; };
226
227 float zeta_r = (reference_height_m - displacement_height) / L;
228 float psi_m_r = (zeta_r < 0.f) ? psi_unstable_u(zeta_r) : psi_stable(zeta_r);
229 float psi_h_r = (zeta_r < 0.f) ? psi_unstable_h(zeta_r) : psi_stable(zeta_r);
230
231 // 5) stability‐corrected friction velocity
232 float u_star = von_Karman_constant * wind_speed_reference / (std::log((reference_height_m - displacement_height) / zo_m) - psi_m_r);
233
234 // 6) Monin–Obukhov scales θ_* and q_*
235 float theta_star = H_s / (rho_air_mol_m3 * cp_air_mol * u_star);
236 float q_star = E_s / (rho_air_mol_m3 * u_star);
237
238 // 7) corrected log‐law lengths
239 float ln_m_corr = std::log((reference_height_m - displacement_height) / zo_m) - psi_m_r;
240 float ln_h_corr = std::log((reference_height_m - displacement_height) / zo_h) - psi_h_r;
241
242 // 8) aerodynamic conductance ga [mol m⁻² s⁻¹]
243 float ga = std::pow(von_Karman_constant, 2) * wind_speed_reference / (ln_m_corr * ln_h_corr) * rho_air_mol_m3;
244
245 // 9) interface values at canopy top (Monin–Obukhov log-law)
246 float air_moisture_reference = esat_Pa(air_temperature_reference) / Patm * air_humidity_reference;
247 float T_int = air_temperature_reference + theta_star / von_Karman_constant * ln_h_corr;
248 float x_int = air_moisture_reference + q_star / von_Karman_constant * ln_h_corr;
249
250 // Cap relative humidity to 100%
251 float x_sat = esat_Pa(T_int) / Patm;
252 if (x_int > x_sat) {
253 // std::cerr << "WARNING (EnergyBalanceModel::evaluateAirEnergyBalance): Air moisture exceeds saturation. Capping to saturation value." << std::endl;
254 x_int = 0.99f * x_sat;
255 }
256
257 // 10) entrainment conductance g_e (mol m⁻² s⁻¹)
258 float H_canopy = sensible_source_flux_W_m3 * canopy_height_m;
259 float g_e;
260 if (air_temperature_ABL == 0.f) {
261 // first‐step equilibrium initialization
262 float w_star0 = H_canopy > 0.f ? std::pow(9.81f * H_canopy * canopy_height_m / (rho_air_mol_m3 * cp_air_mol * air_temperature_reference), 1.f / 3.f) : 0.f;
263 const float A_e = 0.02f;
264 float w_e0 = A_e * w_star0;
265 g_e = rho_air_mol_m3 * w_e0;
266 // initialize ABL state
267 air_temperature_ABL = (ga * air_temperature_average + g_e * air_temperature_reference) / (ga + g_e);
268 air_moisture_ABL = (ga * air_moisture_average + g_e * air_moisture_reference) / (ga + g_e);
269 } else {
270 // ongoing entrainment
271 float w_star = 0.f;
272 if (H_canopy > 0.f) {
273 w_star = std::pow(9.81f * H_canopy * canopy_height_m / (rho_air_mol_m3 * cp_air_mol * air_temperature_ABL), 1.f / 3.f);
274 }
275 float u_star_shear = von_Karman_constant * wind_speed_reference / std::log((reference_height_m - displacement_height) / zo_m);
276 float w_e = std::fmax(0.01f * w_star, 0.005f * u_star_shear);
277 g_e = rho_air_mol_m3 * w_e;
278 }
279
280 // if ( g_e>0.05 ) {
281 // std::cerr << "Capping g_e value of " << g_e << std::endl;
282 // g_e = 0.05f;
283 // }
284 g_e = 2.0;
285
286 // 11) canopy→ABL fluxes
287 float sensible_upper_flux_W_m2 = cp_air_mol * ga * (air_temperature_average - T_int);
288
289 // 12) update canopy‐air temperature (implicit Euler)
290 // numerator: T^n + (Δt / (ρ c_p h_c)) * [H_s + c_p g_a T_int]
291 // denominator: 1 + Δt·g_a/(ρ h_c)
292 float numerator_temp = air_temperature_average + dt_actual / (rho_air_mol_m3 * cp_air_mol * canopy_height_m) * (H_s + cp_air_mol * ga * T_int);
293 float denominator_temp = 1.f + dt_actual * ga / (rho_air_mol_m3 * canopy_height_m);
294 air_temperature_average = numerator_temp / denominator_temp;
295
296 // 13) update canopy‐air moisture (implicit Euler)
297
298 // --- canopy implicit‐Euler moisture update ---
299 // update: ρh (x_new - x_old)/dt = E_s - E_top
300 float E_top = ga * (air_moisture_average - x_int);
301 float f = std::pow(1.f - (1.f - 0.622f) * air_moisture_average, 2) / 0.622f;
302 float numer_can_x = air_moisture_average + dt_actual * f / (rho_air_mol_m3 * canopy_height_m) * (E_s + ga * x_int);
303 float denom_can_x = 1.f + dt_actual * f * ga / (rho_air_mol_m3 * canopy_height_m);
304 air_moisture_average = numer_can_x / denom_can_x;
305
306 // 14) update ABL‐air temperature (implicit Euler)
307 float denom_abl_T = 1.f + dt_actual * (ga + g_e) / (rho_air_mol_m3 * cp_air_mol * abl_height_m);
308 float num_abl_T = air_temperature_ABL + dt_actual / (rho_air_mol_m3 * cp_air_mol * abl_height_m) * (sensible_upper_flux_W_m2 + cp_air_mol * g_e * (air_temperature_reference - air_temperature_ABL));
309 air_temperature_ABL = num_abl_T / denom_abl_T;
310
311 // 15) update ABL‐air moisture (implicit Euler)
312 // source into ABL storage
313 // ρ_ab·h_ab · (x_ab_new - x_ab_old)/dt = E_top - E_e
314 float E_e = g_e * (air_moisture_ABL - air_moisture_reference);
315 float numer_abl_x = air_moisture_ABL + dt_actual / (rho_air_mol_m3 * abl_height_m) * (E_top - E_e);
316 float denom_abl_x = 1.f + dt_actual * (ga + g_e) / (rho_air_mol_m3 * abl_height_m);
317 air_moisture_ABL = numer_abl_x / denom_abl_x;
318
319 // moisture budget check (mol m⁻² s⁻¹)
320 float canopy_storage = (air_moisture_average - air_moisture_old) * rho_air_mol_m3 * canopy_height_m / dt_actual;
321 float flux_in = E_s; // from latent source
322 float flux_out = E_top; // to ABL
323 std::cerr << std::fixed << "CANOPY MOISTURE BUDGET: in=" << flux_in << " out=" << flux_out << " storage=" << canopy_storage << " residual=" << (flux_in - flux_out - canopy_storage) << std::endl;
324
325 // Cap relative humidity to 100%
326 float esat = esat_Pa(air_temperature_average) / Patm;
327 if (air_moisture_average > esat) {
328 std::cerr << "WARNING (EnergyBalanceModel::evaluateAirEnergyBalance): Air moisture exceeds saturation. Capping to saturation value." << std::endl;
329 air_moisture_average = 0.99f * esat;
330 }
331 esat = esat_Pa(air_temperature_ABL) / Patm;
332 if (air_moisture_ABL > esat) {
333 air_moisture_ABL = 0.99f * esat;
334 }
335
336 // Check that timestep is not too large based on aerodynamic resistance
337 if (dt_actual > 0.5f * canopy_height_m * rho_air_mol_m3 / ga) {
338 std::cerr << "WARNING (EnergyBalanceModel::evaluateAirEnergyBalance): Time step is too large. The air energy balance may not converge properly." << std::endl;
339 }
340
341 float heat_from_Htop = dt_actual / (rho_air_mol_m3 * cp_air_mol * abl_height_m) * sensible_upper_flux_W_m2;
342 float heat_from_entrainment = dt_actual * g_e * (air_temperature_reference - air_temperature_ABL) / (rho_air_mol_m3 * abl_height_m);
343
344 std::cerr << "ABL ENERGY UPDATE:\n"
345 << " T_ABL_n = " << air_temperature_ABL << "\n"
346 << " T_ref = " << air_temperature_reference << "\n"
347 << " g_e = " << g_e << " mol/m²/s\n"
348 << " Heat from H_top = " << heat_from_Htop << " K\n"
349 << " Heat from H_e = " << heat_from_entrainment << " K\n"
350 << " Numerator = " << num_abl_T << " K\n"
351 << " Denominator = " << denom_abl_T << "\n"
352 << " T_ABL_new = " << (num_abl_T / denom_abl_T) << "\n";
353
354
355#ifdef HELIOS_DEBUG
356
357 // -- check energy consistency -- //
358 H_s = sensible_source_flux_W_m3 * canopy_height_m; // W m⁻², canopy source
359 float H_top = cp_air_mol * ga * (air_temperature_average - T_int); // W m⁻², canopy→ABL
360 float H_e = cp_air_mol * g_e * (air_temperature_ABL - air_temperature_reference); // W m⁻², ABL→free atm
361
362 E_s = moisture_source_flux_mol_s_m3 * canopy_height_m; // mol m⁻² s⁻¹, canopy source
363 E_top = ga * (air_moisture_average - x_int); // mol m⁻² s⁻¹, canopy→ABL
364 E_e = g_e * (air_moisture_ABL - air_moisture_reference); // mol m⁻² s⁻¹, ABL→free atm
365
366 // Print a concise table of key values
367 std::cout << std::fixed << std::setprecision(5) << "step=" << time << " T_c=" << air_temperature_average << " T_int=" << T_int << " T_b=" << air_temperature_ABL << " H_s=" << H_s << " H_top=" << H_top << " H_e=" << H_e
368 << " E_s=" << E_s << " E_top=" << E_top << " E_e=" << E_e << " psi_m=" << psi_m_r << " psi_h=" << psi_h_r << " u*=" << u_star << " L=" << L << std::endl;
369
370 // temperature balance
371
372 const float tol = 1e-5f;
373 float S_can = rho_air_mol_m3 * cp_air_mol * canopy_height_m * (air_temperature_average - air_temperature_old) / dt_actual;
374 if (fabs(H_s - H_top - S_can) > tol)
375 std::cerr << "Canopy budget error: " << (H_s - H_top - S_can) << "\n";
376
377 float S_abl = rho_air_mol_m3 * cp_air_mol * abl_height_m * (air_temperature_ABL - air_temperature_ABL_old) / dt_actual;
378 if (fabs(H_top - H_e - S_abl) > tol)
379 std::cerr << "ABL budget error: " << (H_top - H_e - S_abl) << "\n";
380
381 // moisture balance per unit ground area
382
383 S_can = rho_air_mol_m3 * canopy_height_m * (air_moisture_average - air_moisture_old) / dt_actual;
384
385 const float tol_m = 1e-6f;
386 if (std::fabs(E_s - E_top - S_can) > tol_m) {
387 std::cerr << "CANOPY MOISTURE BUDGET ERROR: " << "E_s - E_up - S_can = " << E_s - E_top - S_can << " mol/m2/s\n";
388 }
389
390 S_abl = rho_air_mol_m3 * abl_height_m * (air_moisture_ABL - air_moisture_ABL_old) / dt_actual;
391
392 if (std::fabs(E_top - E_e - S_abl) > tol_m) {
393 std::cerr << "ABL MOISTURE BUDGET ERROR: " << "E_up - E_e - S_abl = " << E_top - E_e - S_abl << " mol/m2/s\n";
394 }
395
396
397#endif
398
399 // Set primitive data
400 context->setPrimitiveData(UUIDs, "air_temperature", air_temperature_average);
401 float air_humidity = air_moisture_average * Patm / esat_Pa(air_temperature_average);
402 context->setPrimitiveData(UUIDs, "air_humidity", air_humidity);
403
404 // Set global data
405 context->setGlobalData("air_temperature_average", air_temperature_average);
406 context->setGlobalData("air_humidity_average", air_humidity);
407 context->setGlobalData("air_moisture_average", air_moisture_average);
408
409 context->setGlobalData("air_temperature_ABL", air_temperature_ABL);
410 float air_humidity_ABL = air_moisture_ABL * Patm / esat_Pa(air_temperature_ABL);
411 context->setGlobalData("air_humidity_ABL", air_humidity_ABL);
412
413 context->setGlobalData("air_temperature_interface", T_int);
414 float air_humidity_interface = x_int * Patm / esat_Pa(T_int);
415 context->setGlobalData("air_humidity_interface", air_humidity_interface);
416
417 context->setGlobalData("aerodynamic_resistance", rho_air_mol_m3 / ga);
418
419 std::cout << "Computed air temperature: " << air_temperature_average << " " << air_temperature_old << std::endl;
420 std::cout << "Computed air humidity: " << air_humidity << std::endl;
421 std::cout << "Computed air moisture: " << air_moisture_average << " " << air_moisture_old << std::endl;
422
423 if (time > 0 && std::abs(air_temperature_average - air_temperature_old) / air_temperature_old < 0.003f && std::abs(air_moisture_average - air_moisture_old) / air_moisture_old < 0.003f) {
424 // If the air temperature and humidity have not changed significantly, we can stop iterating
425 std::cout << "Converged" << std::endl;
426 break;
427 }
428 air_temperature_old = air_temperature_average;
429 air_moisture_old = air_moisture_average;
430 air_temperature_ABL_old = air_temperature_ABL;
431 air_moisture_ABL_old = air_moisture_ABL;
432
433 time += dt_actual;
434 }
435}
436
438 message_flag = true;
439}
440
442 message_flag = false;
443}
444
446 if (std::find(radiation_bands.begin(), radiation_bands.end(), band) == radiation_bands.end()) { // only add band if it doesn't exist
447 radiation_bands.emplace_back(band);
448 }
449}
450
451void EnergyBalanceModel::addRadiationBand(const std::vector<std::string> &bands) {
452 for (auto &band: bands) {
453 addRadiationBand(band.c_str());
454 }
455}
456
460
461void EnergyBalanceModel::enableAirEnergyBalance(float canopy_height_m, float reference_height_m) {
462
463 if (canopy_height_m < 0) {
464 helios_runtime_error("ERROR (EnergyBalanceModel::enableAirEnergyBalance): Canopy height must be greater than or equal to zero.");
465 } else if (canopy_height_m != 0 && reference_height_m < canopy_height_m) {
466 helios_runtime_error("ERROR (EnergyBalanceModel::enableAirEnergyBalance): Reference height must be greater than or equal to canopy height.");
467 }
468
469 air_energy_balance_enabled = true;
470 this->canopy_height_m = canopy_height_m;
471 this->reference_height_m = reference_height_m;
472}
473
475
476 if (strcmp(label, "boundarylayer_conductance_out") == 0 || strcmp(label, "vapor_pressure_deficit") == 0 || strcmp(label, "storage_flux") == 0 || strcmp(label, "net_radiation_flux") == 0) {
477 output_prim_data.emplace_back(label);
478 } else {
479 std::cerr << "WARNING (EnergyBalanceModel::optionalOutputPrimitiveData): unknown output primitive data '" << label << "' will be ignored." << std::endl;
480 }
481}
482
486
487void EnergyBalanceModel::printDefaultValueReport(const std::vector<uint> &UUIDs) const {
488
489 size_t assumed_default_TL = 0;
490 size_t assumed_default_U = 0;
491 size_t assumed_default_L = 0;
492 size_t assumed_default_p = 0;
493 size_t assumed_default_Ta = 0;
494 size_t assumed_default_rh = 0;
495 size_t assumed_default_gH = 0;
496 size_t assumed_default_gs = 0;
497 size_t assumed_default_Qother = 0;
498 size_t assumed_default_heatcapacity = 0;
499 size_t assumed_default_fs = 0;
500 size_t twosided_0 = 0;
501 size_t twosided_1 = 0;
502 size_t Ne_1 = 0;
503 size_t Ne_2 = 0;
504
505 size_t Nprimitives = UUIDs.size();
506
507 for (uint UUID: UUIDs) {
508
509 // surface temperature (K)
510 if (!context->doesPrimitiveDataExist(UUID, "temperature") || context->getPrimitiveDataType("temperature") != HELIOS_TYPE_FLOAT) {
511 assumed_default_TL++;
512 }
513
514 // air pressure (Pa)
515 if (!context->doesPrimitiveDataExist(UUID, "air_pressure") || context->getPrimitiveDataType("air_pressure") != HELIOS_TYPE_FLOAT) {
516 assumed_default_p++;
517 }
518
519 // air temperature (K)
520 if (!context->doesPrimitiveDataExist(UUID, "air_temperature") || context->getPrimitiveDataType("air_temperature") != HELIOS_TYPE_FLOAT) {
521 assumed_default_Ta++;
522 }
523
524 // air humidity
525 if (!context->doesPrimitiveDataExist(UUID, "air_humidity") || context->getPrimitiveDataType("air_humidity") != HELIOS_TYPE_FLOAT) {
526 assumed_default_rh++;
527 }
528
529 // boundary-layer conductance to heat
530 if (!context->doesPrimitiveDataExist(UUID, "boundarylayer_conductance") || context->getPrimitiveDataType("boundarylayer_conductance") != HELIOS_TYPE_FLOAT) {
531 assumed_default_gH++;
532 }
533
534 // wind speed
535 if (!context->doesPrimitiveDataExist(UUID, "wind_speed") || context->getPrimitiveDataType("wind_speed") != HELIOS_TYPE_FLOAT) {
536 assumed_default_U++;
537 }
538
539 // object length
540 if (!context->doesPrimitiveDataExist(UUID, "object_length") || context->getPrimitiveDataType("object_length") != HELIOS_TYPE_FLOAT) {
541 assumed_default_L++;
542 }
543
544 // moisture conductance
545 if (!context->doesPrimitiveDataExist(UUID, "moisture_conductance") || context->getPrimitiveDataType("moisture_conductance") != HELIOS_TYPE_FLOAT) {
546 assumed_default_gs++;
547 }
548
549 // Heat capacity
550 if (!context->doesPrimitiveDataExist(UUID, "heat_capacity") || context->getPrimitiveDataType("heat_capacity") != HELIOS_TYPE_FLOAT) {
551 assumed_default_heatcapacity++;
552 }
553
554 //"Other" heat fluxes
555 if (!context->doesPrimitiveDataExist(UUID, "other_surface_flux") || context->getPrimitiveDataType("other_surface_flux") != HELIOS_TYPE_FLOAT) {
556 assumed_default_Qother++;
557 }
558
559 // two-sided flag
560 if (context->doesPrimitiveDataExist(UUID, "twosided_flag") && context->getPrimitiveDataType("twosided_flag") == HELIOS_TYPE_UINT) {
561 uint twosided;
562 context->getPrimitiveData(UUID, "twosided_flag", twosided);
563 if (twosided == 0) {
564 twosided_0++;
565 } else if (twosided == 1) {
566 twosided_1++;
567 }
568 } else {
569 twosided_1++;
570 }
571
572 // number of evaporating faces
573 if (context->doesPrimitiveDataExist(UUID, "evaporating_faces") && context->getPrimitiveDataType("evaporating_faces") == HELIOS_TYPE_UINT) {
574 uint Ne;
575 context->getPrimitiveData(UUID, "evaporating_faces", Ne);
576 if (Ne == 1) {
577 Ne_1++;
578 } else if (Ne == 2) {
579 Ne_2++;
580 }
581 } else {
582 Ne_1++;
583 }
584
585 // Surface humidity
586 if (!context->doesPrimitiveDataExist(UUID, "surface_humidity") || context->getPrimitiveDataType("surface_humidity") != HELIOS_TYPE_FLOAT) {
587 assumed_default_fs++;
588 }
589 }
590
591 std::cout << "--- Energy Balance Model Default Value Report ---" << std::endl;
592
593 std::cout << "surface temperature (initial guess): " << assumed_default_TL << " of " << Nprimitives << " used default value of " << temperature_default
594 << " because "
595 "temperature"
596 " primitive data did not exist"
597 << std::endl;
598 std::cout << "air pressure: " << assumed_default_p << " of " << Nprimitives << " used default value of " << pressure_default
599 << " because "
600 "air_pressure"
601 " primitive data did not exist"
602 << std::endl;
603 std::cout << "air temperature: " << assumed_default_Ta << " of " << Nprimitives << " used default value of " << air_temperature_default
604 << " because "
605 "air_temperature"
606 " primitive data did not exist"
607 << std::endl;
608 std::cout << "air humidity: " << assumed_default_rh << " of " << Nprimitives << " used default value of " << air_humidity_default
609 << " because "
610 "air_humidity"
611 " primitive data did not exist"
612 << std::endl;
613 std::cout << "boundary-layer conductance: " << assumed_default_gH << " of " << Nprimitives
614 << " calculated boundary-layer conductance from Polhausen equation because "
615 "boundarylayer_conductance"
616 " primitive data did not exist"
617 << std::endl;
618 if (assumed_default_gH > 0) {
619 std::cout << " - wind speed: " << assumed_default_U << " of " << assumed_default_gH << " using Polhausen equation used default value of " << wind_speed_default
620 << " because "
621 "wind_speed"
622 " primitive data did not exist"
623 << std::endl;
624 std::cout << " - object length: " << assumed_default_L << " of " << assumed_default_gH
625 << " using Polhausen equation used the primitive/object length/area to calculate object length because "
626 "object_length"
627 " primitive data did not exist"
628 << std::endl;
629 }
630 std::cout << "moisture conductance: " << assumed_default_gs << " of " << Nprimitives << " used default value of " << gS_default
631 << " because "
632 "moisture_conductance"
633 " primitive data did not exist"
634 << std::endl;
635 std::cout << "surface humidity: " << assumed_default_fs << " of " << Nprimitives << " used default value of " << surface_humidity_default
636 << " because "
637 "surface_humidity"
638 " primitive data did not exist"
639 << std::endl;
640 std::cout << "two-sided flag: " << twosided_0 << " of " << Nprimitives << " used two-sided flag=0; " << twosided_1 << " of " << Nprimitives << " used two-sided flag=1 (default)" << std::endl;
641 std::cout << "evaporating faces: " << Ne_1 << " of " << Nprimitives << " used Ne = 1 (default); " << Ne_2 << " of " << Nprimitives << " used Ne = 2" << std::endl;
642
643 std::cout << "------------------------------------------------------" << std::endl;
644}