16#include <cuda_runtime.h>
21#define CUDA_CHECK_ERROR(ans) \
23 gpuAssert((ans), __FILE__, __LINE__); \
25inline void gpuAssert(cudaError_t code,
const char *file,
int line,
bool abort =
true) {
26 if (code != cudaSuccess) {
27 fprintf(stderr,
"GPUassert: %s %s %d\n", cudaGetErrorString(code), file, line);
33__device__
float evaluateEnergyBalance(
float T,
float R,
float Qother,
float eps,
float Ta,
float ea,
float pressure,
float gH,
float gS,
uint Nsides,
float stomatal_sidedness,
float heatcapacity,
float surfacehumidity,
float dt,
float Tprev) {
36 float Rout = float(Nsides) * eps * 5.67e-8F * T * T * T * T;
42 float es = 611.0f * expf(17.502f * (T - 273.f) / (T - 273.f + 240.97f));
43 float gM = 1.08f * gH * gS * (stomatal_sidedness / (1.08f * gH + gS * stomatal_sidedness) + (1.f - stomatal_sidedness) / (1.08f * gH + gS * (1.f - stomatal_sidedness)));
44 if (gH == 0 && gS == 0) {
48 float QL = gM *
lambda_mol * (es - ea * surfacehumidity) / pressure;
53 storage = heatcapacity * (T - Tprev) / dt;
57 return R - Rout - QH - QL - Qother - storage;
60__global__
void solveEnergyBalance(
uint Nprimitives,
float *To,
float *
R,
float *Qother,
float *eps,
float *Ta,
float *ea,
float *pressure,
float *gH,
float *gS,
uint *Nsides,
float *stomatal_sidedness,
float *TL,
float *heatcapacity,
61 float *surfacehumidity,
float dt) {
63 uint p = blockIdx.x * blockDim.x + threadIdx.x;
65 if (p >= Nprimitives) {
71 float err_max = 0.0001;
74 float T_old_old = To[p];
76 float T_old = T_old_old;
79 float resid_old = evaluateEnergyBalance(T_old,
R[p], Qother[p], eps[p], Ta[p], ea[p], pressure[p], gH[p], gS[p], Nsides[p], stomatal_sidedness[p], heatcapacity[p], surfacehumidity[p], dt, To[p]);
80 float resid_old_old = evaluateEnergyBalance(T_old_old,
R[p], Qother[p], eps[p], Ta[p], ea[p], pressure[p], gH[p], gS[p], Nsides[p], stomatal_sidedness[p], heatcapacity[p], surfacehumidity[p], dt, To[p]);
85 while (err > err_max && iter < max_iter) {
87 if (resid_old == resid_old_old) {
92 T = fabs((T_old_old * resid_old - T_old * resid_old_old) / (resid_old - resid_old_old));
94 resid = evaluateEnergyBalance(T,
R[p], Qother[p], eps[p], Ta[p], ea[p], pressure[p], gH[p], gS[p], Nsides[p], stomatal_sidedness[p], heatcapacity[p], surfacehumidity[p], dt, To[p]);
96 resid_old_old = resid_old;
101 err = fabs(T_old - T_old_old) / fabs(T_old_old);
110 printf(
"WARNING (EnergyBalanceModel::solveEnergyBalance): Energy balance did not converge.\n");
116void EnergyBalanceModel::evaluateSurfaceEnergyBalance(
const std::vector<uint> &UUIDs,
float dt) {
122 if (radiation_bands.empty()) {
127 const uint Nprimitives = UUIDs.size();
129 std::vector<float> Rn;
130 Rn.resize(Nprimitives, 0);
132 std::vector<float> emissivity;
133 emissivity.resize(Nprimitives);
134 for (
size_t u = 0; u < Nprimitives; u++) {
135 emissivity.at(u) = 1.f;
138 for (
int b = 0; b < radiation_bands.size(); b++) {
139 for (
size_t u = 0; u < Nprimitives; u++) {
140 size_t p = UUIDs.at(u);
143 sprintf(str,
"radiation_flux_%s", radiation_bands.at(b).c_str());
145 helios::helios_runtime_error(
"ERROR (EnergyBalanceModel::run): No radiation was found in the context for band " + std::string(radiation_bands.at(b)) +
". Did you run the radiation model for this band?");
147 helios::helios_runtime_error(
"ERROR (EnergyBalanceModel::run): Radiation primitive data for band " + std::string(radiation_bands.at(b)) +
" does not have the correct type of ''float'");
153 sprintf(str,
"emissivity_%s", radiation_bands.at(b).c_str());
164 float *To = (
float *) malloc(Nprimitives *
sizeof(
float));
166 CUDA_CHECK_ERROR(cudaMalloc((
void **) &d_To, Nprimitives *
sizeof(
float)));
168 float *
R = (
float *) malloc(Nprimitives *
sizeof(
float));
170 CUDA_CHECK_ERROR(cudaMalloc((
void **) &d_R, Nprimitives *
sizeof(
float)));
172 float *Qother = (
float *) malloc(Nprimitives *
sizeof(
float));
174 CUDA_CHECK_ERROR(cudaMalloc((
void **) &d_Qother, Nprimitives *
sizeof(
float)));
176 float *eps = (
float *) malloc(Nprimitives *
sizeof(
float));
178 CUDA_CHECK_ERROR(cudaMalloc((
void **) &d_eps, Nprimitives *
sizeof(
float)));
180 float *Ta = (
float *) malloc(Nprimitives *
sizeof(
float));
182 CUDA_CHECK_ERROR(cudaMalloc((
void **) &d_Ta, Nprimitives *
sizeof(
float)));
184 float *ea = (
float *) malloc(Nprimitives *
sizeof(
float));
186 CUDA_CHECK_ERROR(cudaMalloc((
void **) &d_ea, Nprimitives *
sizeof(
float)));
188 float *pressure = (
float *) malloc(Nprimitives *
sizeof(
float));
190 CUDA_CHECK_ERROR(cudaMalloc((
void **) &d_pressure, Nprimitives *
sizeof(
float)));
192 float *gH = (
float *) malloc(Nprimitives *
sizeof(
float));
194 CUDA_CHECK_ERROR(cudaMalloc((
void **) &d_gH, Nprimitives *
sizeof(
float)));
196 float *gS = (
float *) malloc(Nprimitives *
sizeof(
float));
198 CUDA_CHECK_ERROR(cudaMalloc((
void **) &d_gS, Nprimitives *
sizeof(
float)));
200 uint *Nsides = (
uint *) malloc(Nprimitives *
sizeof(
uint));
202 CUDA_CHECK_ERROR(cudaMalloc((
void **) &d_Nsides, Nprimitives *
sizeof(
uint)));
204 float *stomatal_sidedness = (
float *) malloc(Nprimitives *
sizeof(
float));
205 float *d_stomatal_sidedness;
206 CUDA_CHECK_ERROR(cudaMalloc((
void **) &d_stomatal_sidedness, Nprimitives *
sizeof(
float)));
208 float *heatcapacity = (
float *) malloc(Nprimitives *
sizeof(
float));
209 float *d_heatcapacity;
210 CUDA_CHECK_ERROR(cudaMalloc((
void **) &d_heatcapacity, Nprimitives *
sizeof(
float)));
212 float *surfacehumidity = (
float *) malloc(Nprimitives *
sizeof(
float));
213 float *d_surfacehumidity;
214 CUDA_CHECK_ERROR(cudaMalloc((
void **) &d_surfacehumidity, Nprimitives *
sizeof(
float)));
216 bool calculated_blconductance_used =
false;
217 bool primitive_length_used =
false;
219 for (
uint u = 0; u < Nprimitives; u++) {
220 size_t p = UUIDs.at(u);
226 To[u] = temperature_default;
235 if (message_flag && Ta[u] < 250.f) {
236 std::cout <<
"WARNING (EnergyBalanceModel::run): Value of " << Ta[u] <<
" given in 'air_temperature' primitive data is very small. Values should be given in units of Kelvin. Assuming default value of " << air_temperature_default
238 Ta[u] = air_temperature_default;
241 Ta[u] = air_temperature_default;
250 std::cout <<
"WARNING (EnergyBalanceModel::run): Value of " << hr <<
" given in 'air_humidity' primitive data is large than 1. Values should be given as fractional values between 0 and 1. Assuming default value of "
251 << air_humidity_default << std::endl;
253 hr = air_humidity_default;
254 }
else if (hr < 0.f) {
256 std::cout <<
"WARNING (EnergyBalanceModel::run): Value of " << hr <<
" given in 'air_humidity' primitive data is less than 0. Values should be given as fractional values between 0 and 1. Assuming default value of "
257 << air_humidity_default << std::endl;
259 hr = air_humidity_default;
262 hr = air_humidity_default;
266 float esat = esat_Pa(Ta[u]);
272 if (pressure[u] < 10000.f) {
274 std::cout <<
"WARNING (EnergyBalanceModel::run): Value of " << pressure[u] <<
" given in 'air_pressure' primitive data is very small. Values should be given in units of Pascals. Assuming default value of " << pressure_default
277 pressure[u] = pressure_default;
280 pressure[u] = pressure_default;
294 stomatal_sidedness[u] = 0.f;
302 stomatal_sidedness[u] = 0.f;
303 }
else if (flag == 2) {
304 stomatal_sidedness[u] = 0.5f;
318 U = wind_speed_default;
327 primitive_length_used =
true;
334 primitive_length_used =
true;
337 gH[u] = 0.135f * sqrt(U / L) * float(Nsides[u]);
339 calculated_blconductance_used =
true;
353 Qother[u] = Qother_default;
360 heatcapacity[u] = heatcapacity_default;
367 surfacehumidity[u] = surface_humidity_default;
371 eps[u] = emissivity.at(u);
378 if (calculated_blconductance_used) {
379 auto it = find(output_prim_data.begin(), output_prim_data.end(),
"boundarylayer_conductance_out");
380 if (it == output_prim_data.end()) {
381 output_prim_data.emplace_back(
"boundarylayer_conductance_out");
386 if (message_flag && primitive_length_used) {
388 <<
"WARNING (EnergyBalanceModel::run): The length of a primitive that is not a member of a compound object was used to calculate the boundary-layer conductance. This often results in incorrect values because the length should be that of the object (e.g., leaf, stem) not the primitive. Make sure this is what you intended."
393 CUDA_CHECK_ERROR(cudaMemcpy(d_To, To, Nprimitives *
sizeof(
float), cudaMemcpyHostToDevice));
394 CUDA_CHECK_ERROR(cudaMemcpy(d_R,
R, Nprimitives *
sizeof(
float), cudaMemcpyHostToDevice));
395 CUDA_CHECK_ERROR(cudaMemcpy(d_Qother, Qother, Nprimitives *
sizeof(
float), cudaMemcpyHostToDevice));
396 CUDA_CHECK_ERROR(cudaMemcpy(d_eps, eps, Nprimitives *
sizeof(
float), cudaMemcpyHostToDevice));
397 CUDA_CHECK_ERROR(cudaMemcpy(d_Ta, Ta, Nprimitives *
sizeof(
float), cudaMemcpyHostToDevice));
398 CUDA_CHECK_ERROR(cudaMemcpy(d_ea, ea, Nprimitives *
sizeof(
float), cudaMemcpyHostToDevice));
399 CUDA_CHECK_ERROR(cudaMemcpy(d_pressure, pressure, Nprimitives *
sizeof(
float), cudaMemcpyHostToDevice));
400 CUDA_CHECK_ERROR(cudaMemcpy(d_gH, gH, Nprimitives *
sizeof(
float), cudaMemcpyHostToDevice));
401 CUDA_CHECK_ERROR(cudaMemcpy(d_gS, gS, Nprimitives *
sizeof(
float), cudaMemcpyHostToDevice));
402 CUDA_CHECK_ERROR(cudaMemcpy(d_Nsides, Nsides, Nprimitives *
sizeof(
uint), cudaMemcpyHostToDevice));
403 CUDA_CHECK_ERROR(cudaMemcpy(d_stomatal_sidedness, stomatal_sidedness, Nprimitives *
sizeof(
float), cudaMemcpyHostToDevice));
404 CUDA_CHECK_ERROR(cudaMemcpy(d_heatcapacity, heatcapacity, Nprimitives *
sizeof(
float), cudaMemcpyHostToDevice));
405 CUDA_CHECK_ERROR(cudaMemcpy(d_surfacehumidity, surfacehumidity, Nprimitives *
sizeof(
float), cudaMemcpyHostToDevice));
407 float *T = (
float *) malloc(Nprimitives *
sizeof(
float));
409 CUDA_CHECK_ERROR(cudaMalloc((
void **) &d_T, Nprimitives *
sizeof(
float)));
412 dim3 dimBlock(64, 1);
413 dim3 dimGrid(ceil(Nprimitives / 64.f));
415 solveEnergyBalance<<<dimGrid, dimBlock>>>(Nprimitives, d_To, d_R, d_Qother, d_eps, d_Ta, d_ea, d_pressure, d_gH, d_gS, d_Nsides, d_stomatal_sidedness, d_T, d_heatcapacity, d_surfacehumidity, dt);
417 CUDA_CHECK_ERROR(cudaPeekAtLastError());
418 CUDA_CHECK_ERROR(cudaDeviceSynchronize());
420 CUDA_CHECK_ERROR(cudaMemcpy(T, d_T, Nprimitives *
sizeof(
float), cudaMemcpyDeviceToHost));
422 for (
uint u = 0; u < Nprimitives; u++) {
423 size_t UUID = UUIDs.at(u);
426 T[u] = temperature_default;
431 float QH =
cp_air_mol * gH[u] * (T[u] - Ta[u]);
434 float es = esat_Pa(T[u]);
435 float gM = 1.08f * gH[u] * gS[u] * (stomatal_sidedness[u] / (1.08f * gH[u] + gS[u] * stomatal_sidedness[u]) + (1.f - stomatal_sidedness[u]) / (1.08f * gH[u] + gS[u] * (1.f - stomatal_sidedness[u])));
436 if (gH[u] == 0 && gS[u] == 0) {
439 float QL =
lambda_mol * gM * (es - ea[u]) / pressure[u];
442 for (
const auto &data_label: output_prim_data) {
443 if (data_label ==
"boundarylayer_conductance_out") {
445 }
else if (data_label ==
"vapor_pressure_deficit") {
446 float vpd = (es - ea[u]) / pressure[u];
448 }
else if (data_label ==
"net_radiation_flux") {
449 float Rnet =
R[u] - float(Nsides[u]) * eps[u] * 5.67e-8F * std::pow(T[u], 4);
451 }
else if (data_label ==
"storage_flux") {
454 storage = heatcapacity[u] * (T[u] - To[u]) / dt;
471 free(stomatal_sidedness);
473 free(surfacehumidity);
477 CUDA_CHECK_ERROR(cudaFree(d_To));
478 CUDA_CHECK_ERROR(cudaFree(d_R));
479 CUDA_CHECK_ERROR(cudaFree(d_Qother));
480 CUDA_CHECK_ERROR(cudaFree(d_eps));
481 CUDA_CHECK_ERROR(cudaFree(d_Ta));
482 CUDA_CHECK_ERROR(cudaFree(d_ea));
483 CUDA_CHECK_ERROR(cudaFree(d_pressure));
484 CUDA_CHECK_ERROR(cudaFree(d_gH));
485 CUDA_CHECK_ERROR(cudaFree(d_gS));
486 CUDA_CHECK_ERROR(cudaFree(d_Nsides));
487 CUDA_CHECK_ERROR(cudaFree(d_stomatal_sidedness));
488 CUDA_CHECK_ERROR(cudaFree(d_heatcapacity));
489 CUDA_CHECK_ERROR(cudaFree(d_surfacehumidity));
490 CUDA_CHECK_ERROR(cudaFree(d_T));