20using namespace helios;
24 constexpr const char *LABEL_EMP_Tref =
"photo_emp_Tref";
25 constexpr const char *LABEL_EMP_Ci_ref =
"photo_emp_Ci_ref";
26 constexpr const char *LABEL_EMP_Asat =
"photo_emp_Asat";
27 constexpr const char *LABEL_EMP_theta =
"photo_emp_theta";
28 constexpr const char *LABEL_EMP_Tmin =
"photo_emp_Tmin";
29 constexpr const char *LABEL_EMP_Topt =
"photo_emp_Topt";
30 constexpr const char *LABEL_EMP_q =
"photo_emp_q";
31 constexpr const char *LABEL_EMP_R =
"photo_emp_R";
32 constexpr const char *LABEL_EMP_ER =
"photo_emp_ER";
33 constexpr const char *LABEL_EMP_kC =
"photo_emp_kC";
36 constexpr const char *LABEL_FQ_Vcmax =
"photo_fq_Vcmax";
37 constexpr const char *LABEL_FQ_Jmax =
"photo_fq_Jmax";
38 constexpr const char *LABEL_FQ_Rd =
"photo_fq_Rd";
39 constexpr const char *LABEL_FQ_alpha =
"photo_fq_alpha";
40 constexpr const char *LABEL_FQ_O =
"photo_fq_O";
41 constexpr const char *LABEL_FQ_TPU_flag =
"photo_fq_TPU_flag";
42 constexpr const char *LABEL_FQ_TPU =
"photo_fq_TPU";
43 constexpr const char *LABEL_FQ_TPU_dHa =
"photo_fq_TPU_dHa";
44 constexpr const char *LABEL_FQ_TPU_dHd =
"photo_fq_TPU_dHd";
45 constexpr const char *LABEL_FQ_TPU_Topt =
"photo_fq_TPU_Topt";
46 constexpr const char *LABEL_FQ_theta =
"photo_fq_theta";
47 constexpr const char *LABEL_FQ_theta_dHa =
"photo_fq_theta_dHa";
48 constexpr const char *LABEL_FQ_theta_dHd =
"photo_fq_theta_dHd";
49 constexpr const char *LABEL_FQ_theta_Topt =
"photo_fq_theta_Topt";
50 constexpr const char *LABEL_FQ_c_Rd =
"photo_fq_c_Rd";
51 constexpr const char *LABEL_FQ_c_Vcmax =
"photo_fq_c_Vcmax";
52 constexpr const char *LABEL_FQ_c_Jmax =
"photo_fq_c_Jmax";
53 constexpr const char *LABEL_FQ_c_Gamma =
"photo_fq_c_Gamma";
54 constexpr const char *LABEL_FQ_c_Kc =
"photo_fq_c_Kc";
55 constexpr const char *LABEL_FQ_c_Ko =
"photo_fq_c_Ko";
56 constexpr const char *LABEL_FQ_dH_Rd =
"photo_fq_dH_Rd";
57 constexpr const char *LABEL_FQ_dH_Vcmax =
"photo_fq_dH_Vcmax";
58 constexpr const char *LABEL_FQ_dH_Jmax =
"photo_fq_dH_Jmax";
59 constexpr const char *LABEL_FQ_dH_Gamma =
"photo_fq_dH_Gamma";
60 constexpr const char *LABEL_FQ_dH_Kc =
"photo_fq_dH_Kc";
61 constexpr const char *LABEL_FQ_dH_Ko =
"photo_fq_dH_Ko";
62 constexpr const char *LABEL_FQ_gm =
"photo_fq_gm";
63 constexpr const char *LABEL_FQ_gm_dHa =
"photo_fq_gm_dHa";
64 constexpr const char *LABEL_FQ_gm_dHd =
"photo_fq_gm_dHd";
65 constexpr const char *LABEL_FQ_gm_Topt =
"photo_fq_gm_Topt";
69 constexpr const char *LABEL_C4_Vpmax =
"photo_c4_Vpmax";
70 constexpr const char *LABEL_C4_Vpmax_dHa =
"photo_c4_Vpmax_dHa";
71 constexpr const char *LABEL_C4_Vpmax_dHd =
"photo_c4_Vpmax_dHd";
72 constexpr const char *LABEL_C4_Vpmax_Topt =
"photo_c4_Vpmax_Topt";
73 constexpr const char *LABEL_C4_Vcmax =
"photo_c4_Vcmax";
74 constexpr const char *LABEL_C4_Vcmax_dHa =
"photo_c4_Vcmax_dHa";
75 constexpr const char *LABEL_C4_Vcmax_dHd =
"photo_c4_Vcmax_dHd";
76 constexpr const char *LABEL_C4_Vcmax_Topt =
"photo_c4_Vcmax_Topt";
77 constexpr const char *LABEL_C4_Jmax =
"photo_c4_Jmax";
78 constexpr const char *LABEL_C4_Jmax_dHa =
"photo_c4_Jmax_dHa";
79 constexpr const char *LABEL_C4_Jmax_dHd =
"photo_c4_Jmax_dHd";
80 constexpr const char *LABEL_C4_Jmax_Topt =
"photo_c4_Jmax_Topt";
81 constexpr const char *LABEL_C4_Rd =
"photo_c4_Rd";
82 constexpr const char *LABEL_C4_Rd_dHa =
"photo_c4_Rd_dHa";
83 constexpr const char *LABEL_C4_Rd_dHd =
"photo_c4_Rd_dHd";
84 constexpr const char *LABEL_C4_Rd_Topt =
"photo_c4_Rd_Topt";
85 constexpr const char *LABEL_C4_gm =
"photo_c4_gm";
86 constexpr const char *LABEL_C4_gm_dHa =
"photo_c4_gm_dHa";
87 constexpr const char *LABEL_C4_gm_dHd =
"photo_c4_gm_dHd";
88 constexpr const char *LABEL_C4_gm_Topt =
"photo_c4_gm_Topt";
90 constexpr const char *LABEL_C4_Kc_25 =
"photo_c4_Kc_25";
91 constexpr const char *LABEL_C4_Ko_25 =
"photo_c4_Ko_25";
92 constexpr const char *LABEL_C4_Kp_25 =
"photo_c4_Kp_25";
93 constexpr const char *LABEL_C4_gamma_star_25 =
"photo_c4_gamma_star_25";
94 constexpr const char *LABEL_C4_Om_25 =
"photo_c4_Om_25";
95 constexpr const char *LABEL_C4_dH_Kc =
"photo_c4_dH_Kc";
96 constexpr const char *LABEL_C4_dH_Ko =
"photo_c4_dH_Ko";
97 constexpr const char *LABEL_C4_dH_Kp =
"photo_c4_dH_Kp";
98 constexpr const char *LABEL_C4_dH_gamma_star =
"photo_c4_dH_gamma_star";
99 constexpr const char *LABEL_C4_dH_Om =
"photo_c4_dH_Om";
101 constexpr const char *LABEL_C4_alpha =
"photo_c4_alpha";
102 constexpr const char *LABEL_C4_x =
"photo_c4_x";
103 constexpr const char *LABEL_C4_Vpr =
"photo_c4_Vpr";
104 constexpr const char *LABEL_C4_Rm_frac =
"photo_c4_Rm_frac";
105 constexpr const char *LABEL_C4_fcyc =
"photo_c4_fcyc";
106 constexpr const char *LABEL_C4_gbs =
"photo_c4_gbs";
107 constexpr const char *LABEL_C4_ao =
"photo_c4_ao";
108 constexpr const char *LABEL_C4_absorptance =
"photo_c4_absorptance";
109 constexpr const char *LABEL_C4_f_spectral =
"photo_c4_f_spectral";
110 constexpr const char *LABEL_C4_theta_etr =
"photo_c4_theta_etr";
111 constexpr const char *LABEL_C4_h_protons =
"photo_c4_h_protons";
112 constexpr const char *LABEL_C4_H_J =
"photo_c4_H_J";
113 constexpr const char *LABEL_C4_H_Jcyc =
"photo_c4_H_Jcyc";
144 empiricalmodelcoeffs = modelcoefficients;
145 empiricalmodel_coefficients.clear();
150 for (
uint UUID: UUIDs) {
151 empiricalmodel_coefficients[UUID] = modelcoefficients;
157 farquharmodelcoeffs = modelcoefficients;
158 farquharmodel_coefficients.clear();
163 for (
uint UUID: UUIDs) {
164 farquharmodel_coefficients[UUID] = modelcoefficients;
170 if (modelcoefficients.size() != UUIDs.size()) {
171 std::cerr <<
"WARNING (PhotosynthesisModel::setModelCoefficients): number of model coefficients (" + std::to_string(modelcoefficients.size()) +
") does not match number of UUIDs (" + std::to_string(UUIDs.size()) +
")" << std::endl;
174 for (
uint i = 0; i < UUIDs.size(); i++) {
175 farquharmodel_coefficients[UUIDs.at(i)] = modelcoefficients.at(i);
181 c4modelcoeffs = modelcoefficients;
182 c4model_coefficients.clear();
187 for (
uint UUID: UUIDs) {
188 c4model_coefficients[UUID] = modelcoefficients;
194 return getCoefficientsForPrimitive_C4(UUID);
205 write_resp(LABEL_C4_Vpmax, LABEL_C4_Vpmax_dHa, LABEL_C4_Vpmax_dHd, LABEL_C4_Vpmax_Topt, coeffs.getVpmaxTempResponse());
206 write_resp(LABEL_C4_Vcmax, LABEL_C4_Vcmax_dHa, LABEL_C4_Vcmax_dHd, LABEL_C4_Vcmax_Topt, coeffs.getVcmaxTempResponse());
207 write_resp(LABEL_C4_Jmax, LABEL_C4_Jmax_dHa, LABEL_C4_Jmax_dHd, LABEL_C4_Jmax_Topt, coeffs.getJmaxTempResponse());
208 write_resp(LABEL_C4_Rd, LABEL_C4_Rd_dHa, LABEL_C4_Rd_dHd, LABEL_C4_Rd_Topt, coeffs.getRdTempResponse());
209 write_resp(LABEL_C4_gm, LABEL_C4_gm_dHa, LABEL_C4_gm_dHd, LABEL_C4_gm_Topt, coeffs.getMesophyllConductance_gmTempResponse());
238 material_coefficient_cache_c4.erase(matID);
247 if (material_coefficient_cache_c4.find(materialID) != material_coefficient_cache_c4.end()) {
248 return material_coefficient_cache_c4.at(materialID);
252 bool found_in_material =
false;
258 auto load_resp = [&](
const char *base,
const char *dHa_lbl,
const char *dHd_lbl,
const char *Topt_lbl,
auto setter_constant,
auto setter_arrhenius,
auto setter_peaked) {
259 float v25 = 0.f, dHa = 0.f, dHd = 0.f, Topt = 0.f;
265 setter_peaked(v25, dHa, Topt - 273.15f, dHd);
266 }
else if (dHa != 0.f) {
267 setter_arrhenius(v25, dHa);
269 setter_constant(v25);
273 load_resp(LABEL_C4_Vpmax, LABEL_C4_Vpmax_dHa, LABEL_C4_Vpmax_dHd, LABEL_C4_Vpmax_Topt, [&](
float v) { coeffs.
setVpmax(v); }, [&](
float v,
float a) { coeffs.
setVpmax(v, a); }, [&](
float v,
float a,
float t,
float d) { coeffs.
setVpmax(v, a, t, d); });
274 load_resp(LABEL_C4_Vcmax, LABEL_C4_Vcmax_dHa, LABEL_C4_Vcmax_dHd, LABEL_C4_Vcmax_Topt, [&](
float v) { coeffs.
setVcmax(v); }, [&](
float v,
float a) { coeffs.
setVcmax(v, a); }, [&](
float v,
float a,
float t,
float d) { coeffs.
setVcmax(v, a, t, d); });
275 load_resp(LABEL_C4_Jmax, LABEL_C4_Jmax_dHa, LABEL_C4_Jmax_dHd, LABEL_C4_Jmax_Topt, [&](
float v) { coeffs.
setJmax(v); }, [&](
float v,
float a) { coeffs.
setJmax(v, a); }, [&](
float v,
float a,
float t,
float d) { coeffs.
setJmax(v, a, t, d); });
276 load_resp(LABEL_C4_Rd, LABEL_C4_Rd_dHa, LABEL_C4_Rd_dHd, LABEL_C4_Rd_Topt, [&](
float v) { coeffs.
setRd(v); }, [&](
float v,
float a) { coeffs.
setRd(v, a); }, [&](
float v,
float a,
float t,
float d) { coeffs.
setRd(v, a, t, d); });
277 load_resp(LABEL_C4_gm, LABEL_C4_gm_dHa, LABEL_C4_gm_dHd, LABEL_C4_gm_Topt, [&](
float v) { coeffs.
setMesophyllConductance_gm(v); }, [&](
float v,
float a) { coeffs.
setMesophyllConductance_gm(v, a); }, [&](
float v,
float a,
float t,
float d) { coeffs.
setMesophyllConductance_gm(v, a, t, d); });
306 material_coefficient_cache_c4[materialID] = coeffs;
307 found_in_material =
true;
309 }
catch (
const std::exception &) {
313 if (found_in_material) {
316 if (c4model_coefficients.find(UUID) != c4model_coefficients.end()) {
317 return c4model_coefficients.at(UUID);
319 return c4modelcoeffs;
325 farquharmodelcoeffs = fmc;
326 farquharmodel_coefficients.clear();
333 for (
uint UUID: UUIDs) {
334 farquharmodel_coefficients[UUID] = fmc;
343 context->
setMaterialData(material_label, LABEL_EMP_Ci_ref, coeffs.Ci_ref);
345 context->
setMaterialData(material_label, LABEL_EMP_theta, coeffs.theta);
355 material_coefficient_cache_empirical.erase(matID);
368 context->
setMaterialData(material_label, LABEL_FQ_Vcmax, vcmax_resp.value_at_25C);
369 context->
setMaterialData(material_label,
"photo_fq_Vcmax_dHa", vcmax_resp.dHa);
370 context->
setMaterialData(material_label,
"photo_fq_Vcmax_dHd", vcmax_resp.dHd);
371 context->
setMaterialData(material_label,
"photo_fq_Vcmax_Topt", vcmax_resp.Topt);
374 context->
setMaterialData(material_label, LABEL_FQ_Jmax, jmax_resp.value_at_25C);
375 context->
setMaterialData(material_label,
"photo_fq_Jmax_dHa", jmax_resp.dHa);
376 context->
setMaterialData(material_label,
"photo_fq_Jmax_dHd", jmax_resp.dHd);
377 context->
setMaterialData(material_label,
"photo_fq_Jmax_Topt", jmax_resp.Topt);
380 context->
setMaterialData(material_label, LABEL_FQ_Rd, rd_resp.value_at_25C);
381 context->
setMaterialData(material_label,
"photo_fq_Rd_dHa", rd_resp.dHa);
382 context->
setMaterialData(material_label,
"photo_fq_Rd_dHd", rd_resp.dHd);
383 context->
setMaterialData(material_label,
"photo_fq_Rd_Topt", rd_resp.Topt);
386 context->
setMaterialData(material_label, LABEL_FQ_alpha, alpha_resp.value_at_25C);
387 context->
setMaterialData(material_label,
"photo_fq_alpha_dHa", alpha_resp.dHa);
392 context->
setMaterialData(material_label, LABEL_FQ_TPU, tpu_resp.value_at_25C);
393 context->
setMaterialData(material_label, LABEL_FQ_TPU_dHa, tpu_resp.dHa);
394 context->
setMaterialData(material_label, LABEL_FQ_TPU_dHd, tpu_resp.dHd);
395 context->
setMaterialData(material_label, LABEL_FQ_TPU_Topt, tpu_resp.Topt);
400 context->
setMaterialData(material_label, LABEL_FQ_theta, theta_resp.value_at_25C);
401 context->
setMaterialData(material_label, LABEL_FQ_theta_dHa, theta_resp.dHa);
402 context->
setMaterialData(material_label, LABEL_FQ_theta_dHd, theta_resp.dHd);
403 context->
setMaterialData(material_label, LABEL_FQ_theta_Topt, theta_resp.Topt);
407 context->
setMaterialData(material_label, LABEL_FQ_gm, gm_resp.value_at_25C);
408 context->
setMaterialData(material_label, LABEL_FQ_gm_dHa, gm_resp.dHa);
409 context->
setMaterialData(material_label, LABEL_FQ_gm_dHd, gm_resp.dHd);
410 context->
setMaterialData(material_label, LABEL_FQ_gm_Topt, gm_resp.Topt);
414 context->
setMaterialData(material_label, LABEL_FQ_TPU_flag, coeffs.TPU_flag);
418 material_coefficient_cache_farquhar.erase(matID);
429 std::string s = std::move(species);
431 bool defaultSpecies =
false;
432 if (s ==
"Almond" || s ==
"almond") {
433 fmc.
setVcmax(72.6, 27.3, 315.3 - 273.15, 478.4);
434 fmc.
setJmax(144.2, 64.1, 314.9 - 273.15, 508.4);
435 fmc.
setTPU(6.4, 37.1, 311.3 - 273.15, 477.9);
436 fmc.
setRd(0.2, 46.39f);
438 }
else if (s ==
"Apple" || s ==
"apple") {
441 fmc.
setRd(3.00, 46.39f);
443 }
else if (s ==
"Cherry" || s ==
"cherry") {
446 fmc.
setRd(2.12, 46.39f);
448 }
else if (s ==
"Prune" || s ==
"prune") {
451 fmc.
setRd(1.65, 46.39f);
453 }
else if (s ==
"Pear" || s ==
"pear") {
456 fmc.
setRd(1.510, 46.39f);
458 }
else if (s ==
"PistachioFemale" || s ==
"pistachiofemale" || s ==
"pistachio_female" || s ==
"Pistachio_Female" || s ==
"Pistachio_female" || s ==
"pistachio" || s ==
"Pistachio") {
460 fmc.
setVcmax(101.8, 56.5, 316.6 - 273.15, 483.1);
461 fmc.
setJmax(223.0, 27.7, 314.6 - 273.15, 458.5);
462 fmc.
setTPU(9.8, 39.9, 315.4 - 273.15, 494.3);
463 fmc.
setRd(1.5, 46.39f);
466 }
else if (s ==
"PistachioMale" || s ==
"pistachiomale" || s ==
"pistachio_male" || s ==
"Pistachio_Male" || s ==
"Pistachio_male") {
469 fmc.
setRd(2.050, 46.39f);
471 }
else if (s ==
"Walnut" || s ==
"walnut") {
472 fmc.
setVcmax(81.6, 85.3, 316.5 - 273.15, 500.6);
473 fmc.
setJmax(201.9, 41.4, 308.6 - 273.15, 308.2);
474 fmc.
setTPU(10.2, 21.9, 310.4 - 273.15, 434.9);
475 fmc.
setRd(0.9, 46.39f);
477 }
else if (s ==
"Grape" || s ==
"grape") {
479 fmc.
setVcmax(74.5, 76.1, 318.8 - 273.15, 499.8);
480 fmc.
setJmax(180.2, 23.0, 313.8 - 273.15, 502.3);
481 fmc.
setTPU(7.7, 24.0, 314.6 - 273.15, 496.4);
482 fmc.
setRd(1.3, 46.39f);
485 }
else if (s ==
"Elderberry" || s ==
"elderberry" || s ==
"blue_elderberry") {
486 fmc.
setVcmax(37.7, 66.0, 319.4 - 273.15, 496.0);
487 fmc.
setJmax(149.7, 24.5, 314.8 - 273.15, 492.9);
488 fmc.
setTPU(7.3, 33.6, 314.5 - 273.15, 497.5);
489 fmc.
setRd(1.3, 46.39f);
492 }
else if (s ==
"Toyon" || s ==
"toyon") {
493 fmc.
setVcmax(52.8, 42.1, 315.1 - 273.15, 483.0);
494 fmc.
setJmax(142.4, 9.0, 313.0 - 273.15, 486.2);
495 fmc.
setTPU(6.6, 14.0, 314.8 - 273.15, 493.8);
496 fmc.
setRd(0.8, 46.39f);
499 }
else if (s ==
"Big_Leaf_Maple" || s ==
"big_leaf_maple" || s ==
"Maple" || s ==
"maple") {
500 fmc.
setVcmax(96.4, 48.9, 307.1 - 273.15, 505.0);
501 fmc.
setJmax(168.0, 8.5, 304.7 - 273.15, 476.7);
502 fmc.
setTPU(2.7, 32.1, 308.3 - 273.15, 471.6);
503 fmc.
setRd(0.1, 46.39f);
505 }
else if (s ==
"Western_Redbud" || s ==
"western_redbud" || s ==
"Redbud" || s ==
"redbud") {
506 fmc.
setVcmax(68.5, 66.6, 315.1 - 273.15, 496.0);
507 fmc.
setJmax(132.4, 41.2, 313.1 - 273.15, 474.0);
508 fmc.
setTPU(6.6, 34.3, 312.8 - 273.15, 463.2);
509 fmc.
setRd(0.8, 46.39f);
512 }
else if (s ==
"Baylaurel" || s ==
"baylaurel" || s ==
"Bay_Laurel" || s ==
"bay_laurel" || s ==
"bay" || s ==
"Bay") {
513 fmc.
setVcmax(97.5, 49.1, 308.6 - 273.15, 505.8);
514 fmc.
setJmax(193.0, 34.0, 308.5 - 273.15, 456.7);
515 fmc.
setTPU(3.3, 0.1, 309.4 - 273.15, 477.5);
516 fmc.
setRd(0.1, 46.39f);
518 }
else if (s ==
"Olive" || s ==
"olive") {
519 fmc.
setVcmax(75.9, 55.4, 315.2 - 273.15, 497.0);
520 fmc.
setJmax(170.4, 32.2, 312.5 - 273.15, 493.4);
521 fmc.
setTPU(8.3, 37.2, 311.7 - 273.15, 498.9);
522 fmc.
setRd(1.9, 46.39f);
524 }
else if (s ==
"EasternRedbudSunlit" || s ==
"easternredbudsunlit" || s ==
"easternredbud_sunlit" || s ==
"EasternRedbud_Sunlit" || s ==
"EasternRedbud_sunlit" || s ==
"sunlitEasternRedbud" || s ==
"SunlitEasternRedbud" ||
525 s ==
"eastern_redbud_sunlit" || s ==
"Eastern_Redbud_Sunlit") {
526 fmc.
setVcmax(104.35, 54.9927, 313.8828 - 273.15, 365.1581);
527 fmc.
setJmax(211.3090, 46.5415, 310.9710 - 273.15, 200.3197);
528 fmc.
setTPU(9.9965, 48.4469, 310.3164 - 273.15, 167.9181);
529 fmc.
setRd(1.4136, 46.39f);
531 }
else if (s ==
"EasternRedbudShaded" || s ==
"easternredbudshaded" || s ==
"easternredbud_shaded" || s ==
"EasternRedbud_Shaded" || s ==
"EasternRedbud_shaded" || s ==
"shadedEasternRedbud" || s ==
"ShadedEasternRedbud" ||
532 s ==
"eastern_redbud_shaded" || s ==
"Eastern_Redbud_Shaded") {
536 fmc.
setRd(1.13, 46.39f);
539 defaultSpecies =
true;
541 std::cerr <<
"WARNING (PhotosynthesisModel::getModelCoefficients): unknown species " << s <<
". Setting default (Almond)." << std::endl;
544 if (!defaultSpecies) {
546 std::cerr <<
"Setting Photosynthesis Model Coefficients to " << s << std::endl;
581 std::string s = species;
582 std::transform(s.begin(), s.end(), s.begin(), [](
unsigned char c) { return std::tolower(c); });
586 if (s ==
"setariaviridis_vc2021" || s ==
"setariaviridis" || s ==
"setaria_viridis_vc2021") {
593 c4.
setJmax(247.69f, 77.9f, 43.f, 260.f);
594 c4.
setRd(0.4f, 66.4f);
604 }
else if (s ==
"genericc4_vc2000" || s ==
"genericc4" || s ==
"generic_c4" || s ==
"generic_vc2000") {
611 c4.
setRd(1.0f, 51.2f);
615 }
else if (s ==
"maize_massad2007" || s ==
"maize" || s ==
"zea_mays_massad2007") {
631 c4.
setVcmax(60.f, 67.3f, 32.3f, 144.6f);
632 c4.
setVpmax(120.f, 70.4f, 43.1f, 117.9f);
633 c4.
setJmax(400.f, 77.9f, 31.5f, 191.9f);
634 c4.
setRd(0.6f, 66.4f);
649 helios_runtime_error(
"ERROR (PhotosynthesisModel::getC4CoefficientsFromLibrary): unknown C4 species '" + species +
650 "'. Available keys (case-insensitive): SetariaViridis_vC2021, GenericC4_vC2000, Maize_Massad2007.");
654 std::cerr <<
"Setting C4 Photosynthesis Model Coefficients to " << species << std::endl;
665 if (material_coefficient_cache_empirical.find(materialID) != material_coefficient_cache_empirical.end()) {
666 return material_coefficient_cache_empirical.at(materialID);
689 material_coefficient_cache_empirical[materialID] = coeffs;
694 }
catch (
const std::exception &) {
699 if (!found && empiricalmodel_coefficients.find(UUID) != empiricalmodel_coefficients.end()) {
700 return empiricalmodel_coefficients.at(UUID);
704 return empiricalmodelcoeffs;
711 if (material_coefficient_cache_farquhar.find(materialID) != material_coefficient_cache_farquhar.end()) {
712 return material_coefficient_cache_farquhar.at(materialID);
724 float vcmax_25C, vcmax_dHa, vcmax_dHd, vcmax_Topt;
731 float jmax_25C, jmax_dHa, jmax_dHd, jmax_Topt;
738 float rd_25C, rd_dHa, rd_dHd, rd_Topt;
745 float alpha_25C, alpha_dHa;
750 if (vcmax_Topt < 1000.f) {
751 coeffs.
setVcmax(vcmax_25C, vcmax_dHa, vcmax_Topt - 273.15f, vcmax_dHd);
752 }
else if (vcmax_dHa > 0.f) {
753 coeffs.
setVcmax(vcmax_25C, vcmax_dHa);
758 if (jmax_Topt < 1000.f) {
759 coeffs.
setJmax(jmax_25C, jmax_dHa, jmax_Topt - 273.15f, jmax_dHd);
760 }
else if (jmax_dHa > 0.f) {
761 coeffs.
setJmax(jmax_25C, jmax_dHa);
766 if (rd_Topt < 1000.f) {
767 coeffs.
setRd(rd_25C, rd_dHa, rd_Topt - 273.15f, rd_dHd);
768 }
else if (rd_dHa > 0.f) {
769 coeffs.
setRd(rd_25C, rd_dHa);
771 coeffs.
setRd(rd_25C);
774 if (alpha_dHa > 0.f) {
783 float gm_25C, gm_dHa = 0.f, gm_dHd = 0.f, gm_Topt = 10000.f;
794 if (gm_Topt < 1000.f) {
796 }
else if (gm_dHa > 0.f) {
806 float tpu_25C, tpu_dHa = 0.f, tpu_dHd = 0.f, tpu_Topt = 10000.f;
817 if (tpu_Topt < 1000.f) {
818 coeffs.
setTPU(tpu_25C, tpu_dHa, tpu_Topt - 273.15f, tpu_dHd);
819 }
else if (tpu_dHa > 0.f) {
820 coeffs.
setTPU(tpu_25C, tpu_dHa);
828 float theta_25C, theta_dHa = 0.f, theta_dHd = 0.f, theta_Topt = 10000.f;
839 if (theta_Topt < 1000.f) {
841 }
else if (theta_dHa > 0.f) {
853 material_coefficient_cache_farquhar[materialID] = coeffs;
858 }
catch (
const std::exception &) {
863 if (!found && farquharmodel_coefficients.find(UUID) != farquharmodel_coefficients.end()) {
864 return farquharmodel_coefficients.at(UUID);
868 return farquharmodelcoeffs;
873 helios_runtime_error(
"ERROR (PhotosynthesisModel::setCm): Cm must be non-negative. Received Cm = " + std::to_string(Cm) +
" ubar.");
875 if (!std::isfinite(Cm)) {
878 for (
uint UUID: UUIDs) {
880 helios_runtime_error(
"ERROR (PhotosynthesisModel::setCm): Primitive with UUID " + std::to_string(UUID) +
" does not exist.");
882 manual_Cm[UUID] = Cm;
889 helios_runtime_error(
"ERROR (PhotosynthesisModel::setCi): Ci must be non-negative. Received Ci = " + std::to_string(Ci) +
" umol/mol.");
892 if (!std::isfinite(Ci)) {
897 if (Ci > 2000.f && message_flag) {
898 std::cout <<
"WARNING (PhotosynthesisModel::setCi): Ci = " << Ci <<
" umol/mol is unusually high. Verify this is intentional." << std::endl;
902 for (
uint UUID: UUIDs) {
904 helios_runtime_error(
"ERROR (PhotosynthesisModel::setCi): Primitive with UUID " + std::to_string(UUID) +
" does not exist.");
906 manual_Ci[UUID] = Ci;
919 for (
uint UUID: lUUIDs) {
924 i_PAR = i_PAR * 4.57f;
927 warnings.
addWarning(
"negative_par_clipped",
"PAR flux value provided was negative. Clipping to zero.");
930 i_PAR = i_PAR_default;
937 warnings.
addWarning(
"low_temperature",
"Primitive temperature value was very low (" + std::to_string(TL) +
"K). Using default. Are you using absolute temperature units?");
949 warnings.
addWarning(
"negative_co2_clipped",
"CO2 concentration value provided was negative. Clipping to zero.");
960 warnings.
addWarning(
"negative_moisture_conductance_clipped",
"Moisture conductance value provided was negative. Clipping to zero.");
968 uint Nsides = (twosided_flag == 0) ? 1 : 2;
970 float stomatal_sidedness = 0.f;
985 warnings.
addWarning(
"negative_boundarylayer_conductance_clipped",
"Boundary-layer conductance value provided was negative. Clipping to zero.");
989 if (gH == 0 && gM == 0) {
992 gM = 1.08f * gH * gM * (stomatal_sidedness / (1.08f * gH + gM * stomatal_sidedness) + (1.f - stomatal_sidedness) / (1.08f * gH + gM * (1.f - stomatal_sidedness)));
995 float A, Ci, Gamma, J_over_Jmax = 0.f;
996 int limitation_state, TPU_flag = 0;
997 float Cm_c4 = 0.f, Vp_c4 = 0.f;
999 if (model ==
"farquhar") {
1002 if (manual_Ci.find(UUID) != manual_Ci.end()) {
1004 Ci = manual_Ci.at(UUID);
1009 std::vector<float> variables{CO2, i_PAR, TL, gM, 0.f, 0.f, 0.f, float(TPU_flag), 0.f};
1013 evaluateCi_Farquhar(Ci, variables, &coeffs);
1017 limitation_state = (int) variables[5];
1018 Gamma = variables[6];
1019 J_over_Jmax = variables[8];
1022 previous_Ci[UUID] = Ci;
1028 if (previous_Ci.find(UUID) != previous_Ci.end()) {
1029 Ci = previous_Ci.at(UUID);
1035 A = evaluateFarquharModel(coeffs, i_PAR, TL, CO2, gM, Ci, Gamma, limitation_state, TPU_flag, J_over_Jmax, warnings);
1038 previous_Ci[UUID] = Ci;
1041 }
else if (model ==
"c4") {
1045 if (manual_Cm.find(UUID) != manual_Cm.end()) {
1048 const float Cm_in = manual_Cm.at(UUID);
1049 std::vector<float> variables{CO2, i_PAR, TL, gM, 0.f, 0.f, 0.f, 0.f};
1050 evaluateCm_C4(Cm_in, variables, c4coeffs);
1052 limitation_state =
static_cast<int>(variables[5]);
1053 Cm_c4 = variables[6];
1054 Vp_c4 = variables[7];
1055 const float gm_eval = respondToTemperature(&c4coeffs.gmTempResponse, TL);
1056 Ci = (std::isfinite(gm_eval) && gm_eval > 0.f) ? (Cm_in + A / gm_eval) : Cm_in;
1057 previous_Ci[UUID] = Ci;
1058 }
else if (manual_Ci.find(UUID) != manual_Ci.end()) {
1060 Ci = manual_Ci.at(UUID);
1061 std::vector<float> variables{CO2, i_PAR, TL, gM, 0.f, 0.f, 0.f, 0.f};
1062 evaluateCi_C4(Ci, variables, &c4coeffs);
1064 limitation_state =
static_cast<int>(variables[5]);
1065 Cm_c4 = variables[6];
1066 Vp_c4 = variables[7];
1067 previous_Ci[UUID] = Ci;
1069 if (previous_Ci.find(UUID) != previous_Ci.end()) {
1070 Ci = previous_Ci.at(UUID);
1075 A = evaluateC4Model(c4coeffs, i_PAR, TL, CO2, gM, Ci, Cm_c4, Vp_c4, limitation_state, warnings);
1076 previous_Ci[UUID] = Ci;
1083 A = evaluateEmpiricalModel(coeffs, i_PAR, TL, CO2, gM);
1087 warnings.
addWarning(
"convergence_failure",
"Solution did not converge for primitive " + std::to_string(UUID) +
".");
1092 for (
const auto &data: output_prim_data) {
1093 if (data ==
"Ci" && (model ==
"farquhar" || model ==
"c4")) {
1095 }
else if (data ==
"limitation_state" && (model ==
"farquhar" || model ==
"c4")) {
1097 }
else if (data ==
"Gamma_CO2" && model ==
"farquhar") {
1099 }
else if (data ==
"electron_transport_ratio" && model ==
"farquhar") {
1101 }
else if (data ==
"Cm" && model ==
"c4") {
1103 }
else if (data ==
"Vp" && model ==
"c4") {
1109 warnings.
report(std::cerr);
1121 const float Tmin = params.Tmin;
1122 const float Topt = params.Topt;
1123 const float Tref = params.Tref;
1124 const float q = params.q;
1130 const float denom_ref = (1.f + q) * Topt - Tmin - q * Tref;
1131 if (Tref <= Tmin || std::fabs(denom_ref) < 1.0e-6f) {
1132 helios_runtime_error(
"ERROR (PhotosynthesisModel): Invalid empirical model temperature response coefficients. Requires Tref > Tmin and (1+q)*Topt - Tmin - q*Tref != 0. Received Tmin = " + std::to_string(Tmin) +
1133 " K, Topt = " + std::to_string(Topt) +
" K, Tref = " + std::to_string(Tref) +
" K, q = " + std::to_string(q) +
".");
1141 const float numer = (1.f + q) * Topt - Tmin - q * TL;
1146 const float f_T = std::pow((TL - Tmin) / (Tref - Tmin), q) * numer / denom_ref;
1148 return std::max(f_T, 0.f);
1152float PhotosynthesisModel::evaluateCi_Empirical(
const EmpiricalModelCoefficients ¶ms,
float Ci,
float CO2,
float fL,
float fT,
float Rd,
float gM)
const {
1157 float fC = params.kC * Ci / params.Ci_ref;
1162 float A = params.Asat * fL * fT * fC - Rd;
1166 float resid = 0.75f * gM * (CO2 - Ci) - A;
1172float PhotosynthesisModel::evaluateEmpiricalModel(
const EmpiricalModelCoefficients ¶ms,
float i_PAR,
float TL,
float CO2,
float gM) {
1179 float fL = i_PAR / (params.theta + i_PAR);
1181 assert(fL >= 0 && fL <= 1);
1185 float fT = evaluateEmpiricalTemperatureResponse(params, TL);
1189 float Rd = params.R * sqrt(TL - 273.f) * exp(-params.ER / TL);
1192 float Ci_old_old = 0.95f * Ci;
1194 float resid_old = evaluateCi_Empirical(params, Ci_old, CO2, fL, fT, Rd, gM);
1195 float resid_old_old = evaluateCi_Empirical(params, Ci_old_old, CO2, fL, fT, Rd, gM);
1197 float err = 10000, err_max = 0.01;
1198 int iter = 0, max_iter = 100;
1200 while (err > err_max && iter < max_iter) {
1202 if (resid_old == resid_old_old) {
1206 Ci = fabs((Ci_old_old * resid_old - Ci_old * resid_old_old) / (resid_old - resid_old_old));
1208 resid = evaluateCi_Empirical(params, Ci, CO2, fL, fT, Rd, gM);
1210 resid_old_old = resid_old;
1215 Ci_old_old = Ci_old;
1222 if (err > err_max) {
1225 float fC = params.kC * Ci / params.Ci_ref;
1226 A = params.Asat * fL * fT * fC - Rd;
1232float PhotosynthesisModel::evaluateCi_Farquhar(
float Ci, std::vector<float> &variables,
const void *parameters) {
1236 float Vcmax, Jmax, TPU, Rd, alpha, theta;
1238 float CO2 = variables[0];
1239 float Q = variables[1];
1240 float TL = variables[2];
1241 float gM = variables[3];
1242 int TPUflag = modelcoeffs.TPU_flag;
1244 float R = 0.0083144598;
1246 float c_Gamma = 19.02;
1247 float dH_Gamma = 37.83;
1249 float dH_Kc = 79.43;
1251 float dH_Ko = 36.38;
1253 float invDiffRT = (1.f / 298.15f - 1.f / TL) /
R;
1255 if (modelcoeffs.Vcmax > 0) {
1256 Vcmax = modelcoeffs.Vcmax * expf(modelcoeffs.dH_Vcmax * (invDiffRT));
1258 Vcmax = respondToTemperature(&modelcoeffs.VcmaxTempResponse, TL);
1261 if (modelcoeffs.Jmax > 0) {
1262 Jmax = modelcoeffs.Jmax * expf(modelcoeffs.dH_Jmax * (invDiffRT));
1264 Jmax = respondToTemperature(&modelcoeffs.JmaxTempResponse, TL);
1267 if (modelcoeffs.Rd > 0) {
1268 Rd = modelcoeffs.Rd * expf(modelcoeffs.dH_Rd * (invDiffRT));
1270 Rd = respondToTemperature(&modelcoeffs.RdTempResponse, TL);
1273 if (modelcoeffs.alpha > 0) {
1274 alpha = modelcoeffs.alpha;
1276 alpha = respondToTemperature(&modelcoeffs.alphaTempResponse, TL);
1280 TPU = respondToTemperature(&modelcoeffs.TPUTempResponse, TL);
1281 theta = respondToTemperature(&modelcoeffs.thetaTempResponse, TL);
1287 const float gm = respondToTemperature(&modelcoeffs.gmTempResponse, TL);
1288 const bool gm_infinite = (!std::isfinite(gm) || gm > 1.0e6f);
1291 float Gamma_star = exp(c_Gamma - dH_Gamma / (
R * TL));
1292 float Kc = exp(c_Kc - dH_Kc / (
R * TL));
1293 float Ko = exp(c_Ko - dH_Ko / (
R * TL));
1294 float Kco = Kc * (1.f + O / Ko);
1297 double a = std::max(theta, 0.0001f);
1298 double ia = 1.000f / a;
1299 double b = -(alpha * Q + Jmax);
1300 double c = alpha * Q * Jmax;
1301 double J = (-b - sqrt(pow(b, 2.000f) - 4.f * a * c)) * 0.5f * ia;
1305 float J_over_Jmax = (Jmax > 0.f) ?
static_cast<float>(J / Jmax) : 0.f;
1306 variables[8] = J_over_Jmax;
1309 float limitation_state;
1313 float Wc = Vcmax * Ci / (Ci + Kco);
1314 float Wj = J * Ci / (4.f * Ci + 8.f * Gamma_star);
1316 float smooth_factor = 0.99f;
1317 float s =
helios::clamp(0.5f + 0.5f * (Wc - Wj) / smooth_factor, 0.0f, 1.0f);
1318 float smooth_min = Wc * (1.f - s) + Wj * s - smooth_factor * s * (1.f - s);
1320 A = (1.f - Gamma_star / Ci) * smooth_min - Rd;
1326 float A_p = 3.f * TPU - Rd;
1327 smooth_factor = 0.99f;
1328 s =
helios::clamp(0.5f + 0.5f * (A - A_p) / smooth_factor, 0.0f, 1.0f);
1329 A = A * (1.f - s) + A_p * s - smooth_factor * s * (1.f - s);
1333 limitation_state = 0;
1335 limitation_state = 1;
1343 auto solve_quadratic_min_root = [](
float aq,
float bq,
float cq) ->
float {
1347 float disc = bq * bq - 4.f * aq * cq;
1351 float sqrtD = std::sqrt(disc);
1352 float q = -0.5f * (bq - sqrtD);
1354 return (-bq + sqrtD) * 0.5f / aq;
1359 const float inv_gm = 1.f / gm;
1361 const float a_c = inv_gm;
1362 const float b_c = -((Ci + Kco) + (Vcmax - Rd) * inv_gm);
1363 const float c_c = Vcmax * (Ci - Gamma_star) - Rd * (Ci + Kco);
1364 const float A_c_net = solve_quadratic_min_root(a_c, b_c, c_c);
1367 const float J4 = 0.25f *
static_cast<float>(J);
1368 const float a_j = inv_gm;
1369 const float b_j = -((Ci + 2.f * Gamma_star) + (J4 - Rd) * inv_gm);
1370 const float c_j = J4 * (Ci - Gamma_star) - Rd * (Ci + 2.f * Gamma_star);
1371 const float A_j_net = solve_quadratic_min_root(a_j, b_j, c_j);
1375 float smooth_factor = 0.99f;
1376 float s =
helios::clamp(0.5f + 0.5f * (A_c_net - A_j_net) / smooth_factor, 0.0f, 1.0f);
1377 A = A_c_net * (1.f - s) + A_j_net * s - smooth_factor * s * (1.f - s);
1382 float A_p = 3.f * TPU - Rd;
1383 smooth_factor = 0.99f;
1384 s =
helios::clamp(0.5f + 0.5f * (A - A_p) / smooth_factor, 0.0f, 1.0f);
1385 A = A * (1.f - s) + A_p * s - smooth_factor * s * (1.f - s);
1389 if (A_c_net < A_j_net) {
1390 limitation_state = 0;
1392 limitation_state = 1;
1398 float resid = 0.75f * gM * (CO2 - Ci) - A;
1401 variables[5] = limitation_state;
1403 float Gamma = (Gamma_star + Kco * Rd / Vcmax) / (1.f - Rd / Vcmax);
1404 variables[6] = Gamma;
1415 void computeC4RatesFromCm(
float Cm,
const C4ModelCoefficients &p,
float Vpmax,
float Vcmax,
float Jmax,
float Rd,
float Kc,
float Ko,
float Kp,
float gamma_star,
float Om,
float I_incident,
float &Ac_out,
float &Aj_out,
float &Vp_out) {
1417 const float Rm = p.
Rm_frac * Rd;
1420 const float Vp_MM = Cm * Vpmax / (Cm + Kp);
1421 const float Vp = std::min(Vp_MM, p.
Vpr);
1425 const float rho = (1.f - p.
fcyc) / (2.f - p.
fcyc);
1427 const float theta = std::max(p.
theta_etr, 1.0e-4f);
1428 const float sum_IJ = I2 + Jmax;
1429 const float radicand_J = std::max(sum_IJ * sum_IJ - 4.f * theta * I2 * Jmax, 0.f);
1430 const float J = (sum_IJ - std::sqrt(radicand_J)) / (2.f * theta);
1436 const float denom_z = std::max(p.
h_protons * (1.f - p.
fcyc), 1.0e-6f);
1440 const float ao = std::max(p.
ao, 1.0e-6f);
1441 const float gbs = p.
gbs;
1444 const float term_PEP = Vp - Rm + gbs * Cm;
1445 const float term_Rub = Vcmax - Rd;
1446 const float Kco = 1.f + Om / Ko;
1447 const float a_c = 1.f - alpha * Kc / (ao * Ko);
1448 const float b_c = -(term_PEP + term_Rub + gbs * Kc * Kco + (alpha / ao) * (gamma_star * Vcmax + Rd * Kc / Ko));
1449 const float c_c = term_Rub * term_PEP - gbs * (Vcmax * gamma_star * Om + Rd * Kc * Kco);
1450 const float disc_c = std::max(b_c * b_c - 4.f * a_c * c_c, 0.f);
1452 Ac_out = (-b_c - std::sqrt(disc_c)) / (2.f * a_c);
1457 const float term_mesJ = Jm * z * 0.5f - Rm + gbs * Cm;
1458 const float term_bunJ = Jb * z / 3.f - Rd;
1459 const float a_j = 1.f - 7.f * gamma_star * alpha / (3.f * ao);
1460 const float b_j = -(term_mesJ + term_bunJ + gbs * 7.f * gamma_star * Om / 3.f + (alpha * gamma_star / ao) * (Jb * z / 3.f + 7.f * Rd / 3.f));
1461 const float c_j = term_mesJ * term_bunJ - gbs * gamma_star * Om * (Jb * z / 3.f + 7.f * Rd / 3.f);
1462 const float disc_j = std::max(b_j * b_j - 4.f * a_j * c_j, 0.f);
1463 Aj_out = (-b_j - std::sqrt(disc_j)) / (2.f * a_j);
1468float PhotosynthesisModel::evaluateCi_C4(
float Ci, std::vector<float> &variables,
const void *parameters) {
1472 const float CO2 = variables[0];
1473 const float I_incident = variables[1];
1474 const float TL = variables[2];
1475 const float gM = variables[3];
1477 const float R = 0.0083144598f;
1478 const float invDiffRT = (1.f / 298.15f - 1.f / TL) /
R;
1481 const float Vpmax = respondToTemperature(&p.VpmaxTempResponse, TL);
1482 const float Vcmax = respondToTemperature(&p.VcmaxTempResponse, TL);
1483 const float Jmax = respondToTemperature(&p.JmaxTempResponse, TL);
1484 const float Rd = respondToTemperature(&p.RdTempResponse, TL);
1485 const float gm = respondToTemperature(&p.gmTempResponse, TL);
1488 const float Kc = p.
Kc_25 * std::exp(p.
dH_Kc * invDiffRT);
1489 const float Ko = p.
Ko_25 * std::exp(p.
dH_Ko * invDiffRT);
1490 const float Kp = p.
Kp_25 * std::exp(p.
dH_Kp * invDiffRT);
1492 const float Om = p.
Om_25 * std::exp(p.
dH_Om * invDiffRT);
1496 float Cm = std::max(Ci, 0.1f);
1497 if (!std::isfinite(gm) || gm <= 0.f) {
1499 helios::helios_runtime_error(
"ERROR (PhotosynthesisModel::evaluateCi_C4): Mesophyll conductance g_m must be positive and finite (got " + std::to_string(gm) +
"). Check C4ModelCoefficients::setMesophyllConductance_gm().");
1502 float Ac = 0.f, Aj = 0.f, Vp = 0.f, A = 0.f;
1503 for (
int iter = 0; iter < 50; ++iter) {
1504 computeC4RatesFromCm(Cm, p, Vpmax, Vcmax, Jmax, Rd, Kc, Ko, Kp, gamma_star, Om, I_incident, Ac, Aj, Vp);
1505 A = std::min(Ac, Aj);
1506 const float Cm_target = Ci - A / gm;
1507 if (std::fabs(Cm_target - Cm) < 1.0e-5f) {
1511 Cm = 0.5f * Cm + 0.5f * Cm_target;
1514 computeC4RatesFromCm(Cm, p, Vpmax, Vcmax, Jmax, Rd, Kc, Ko, Kp, gamma_star, Om, I_incident, Ac, Aj, Vp);
1515 A = std::min(Ac, Aj);
1518 const int limitation = (Ac < Aj) ? 1 : 2;
1521 variables[5] =
static_cast<float>(limitation);
1526 return 0.75f * gM * (CO2 - Ci) - A;
1530float PhotosynthesisModel::evaluateCm_C4(
float Cm, std::vector<float> &variables,
const C4ModelCoefficients ¶ms) {
1532 const float I_incident = variables[1];
1533 const float TL = variables[2];
1535 const float R = 0.0083144598f;
1536 const float invDiffRT = (1.f / 298.15f - 1.f / TL) /
R;
1538 const float Vpmax = respondToTemperature(¶ms.VpmaxTempResponse, TL);
1539 const float Vcmax = respondToTemperature(¶ms.VcmaxTempResponse, TL);
1540 const float Jmax = respondToTemperature(¶ms.JmaxTempResponse, TL);
1541 const float Rd = respondToTemperature(¶ms.RdTempResponse, TL);
1542 const float Kc = params.Kc_25 * std::exp(params.dH_Kc * invDiffRT);
1543 const float Ko = params.Ko_25 * std::exp(params.dH_Ko * invDiffRT);
1544 const float Kp = params.Kp_25 * std::exp(params.dH_Kp * invDiffRT);
1545 const float gamma_star = params.gamma_star_25 * std::exp(params.dH_gamma_star * invDiffRT);
1546 const float Om = params.Om_25 * std::exp(params.dH_Om * invDiffRT);
1548 float Ac = 0.f, Aj = 0.f, Vp = 0.f;
1549 computeC4RatesFromCm(Cm, params, Vpmax, Vcmax, Jmax, Rd, Kc, Ko, Kp, gamma_star, Om, I_incident, Ac, Aj, Vp);
1550 const float A = std::min(Ac, Aj);
1551 const int limitation = (Ac < Aj) ? 1 : 2;
1554 variables[5] =
static_cast<float>(limitation);
1561float PhotosynthesisModel::evaluateC4Model(
const C4ModelCoefficients ¶ms,
float i_PAR,
float TL,
float CO2,
float gM,
float &Ci,
float &Cm,
float &Vp,
int &limitation_state,
helios::WarningAggregator &warnings) {
1565 std::vector<float> variables{CO2, i_PAR, TL, gM, A,
static_cast<float>(limitation_state), Cm, Vp};
1567 std::vector<float> initial_guesses;
1568 if (Ci > 0 && std::isfinite(Ci)) {
1569 initial_guesses.push_back(Ci);
1571 initial_guesses.insert(initial_guesses.end(), {CO2 * 0.6f, CO2 * 0.3f, 100.f, 50.f, CO2 * 0.9f});
1573 bool overall_converged =
false;
1574 for (
float guess: initial_guesses) {
1575 std::vector<float> vars_attempt = variables;
1576 bool attempt_converged =
false;
1577 float Ci_attempt =
fzero(evaluateCi_C4, vars_attempt, ¶ms, guess, attempt_converged, 0.001f, 200);
1578 if (attempt_converged && Ci_attempt > 0 && std::isfinite(Ci_attempt)) {
1580 variables = vars_attempt;
1581 overall_converged =
true;
1586 if (!overall_converged) {
1587 warnings.
addWarning(
"photosynthesis_c4_ci_convergence_failure",
"C4 photosynthesis model failed to converge for Ci after trying multiple initial guesses.");
1588 bool final_converged =
false;
1589 Ci =
fzero(evaluateCi_C4, variables, ¶ms, 100.f, final_converged, 0.01f, 500);
1593 limitation_state =
static_cast<int>(variables[5]);
1602 float T = T_in_Kelvin;
1603 float R = 0.0083144598f;
1604 float v25 = params->value_at_25C;
1605 float dHa = params->dHa;
1606 float dHd = params->dHd;
1607 float Topt = params->Topt;
1611 float logterm = logf(dHd / dHa - 1.f);
1612 float t1 = 1.f + expf(dHd /
R * (1.f / Topt - 1.f / 298.15f) - logterm);
1613 float t2 = 1.f + expf(dHd /
R * (1.f / Topt - 1.f / T) - logterm);
1614 return v25 * exp(dHa /
R * (1.f / 298.15f - 1.f / T)) * t1 / t2;
1619float PhotosynthesisModel::evaluateFarquharModel(
const FarquharModelCoefficients ¶ms,
float i_PAR,
float TL,
float CO2,
float gM,
float &Ci,
float &Gamma,
int &limitation_state,
int &TPU_flag,
float &J_over_Jmax,
1623 std::vector<float> variables{CO2, i_PAR, TL, gM, A, float(limitation_state), Gamma, float(TPU_flag), 0.f};
1627 std::vector<float> initial_guesses;
1630 if (Ci > 0 && std::isfinite(Ci)) {
1631 initial_guesses.push_back(Ci);
1635 initial_guesses.insert(initial_guesses.end(), {CO2 * 0.7f, 100.0f, CO2 * 0.3f, CO2 * 0.9f, 50.0f});
1637 bool overall_converged =
false;
1638 for (
float guess: initial_guesses) {
1640 std::vector<float> vars_attempt = variables;
1641 bool attempt_converged =
false;
1642 float Ci_attempt =
fzero(evaluateCi_Farquhar, vars_attempt, ¶ms, guess, attempt_converged, 0.001f, 200);
1645 if (attempt_converged && Ci_attempt > 0 && std::isfinite(Ci_attempt)) {
1647 variables = vars_attempt;
1648 overall_converged =
true;
1654 if (!overall_converged) {
1655 warnings.
addWarning(
"photosynthesis_ci_convergence_failure",
"Photosynthesis model failed to converge for Ci calculation after trying multiple initial guesses.");
1657 bool final_converged =
false;
1658 Ci =
fzero(evaluateCi_Farquhar, variables, ¶ms, 100.0f, final_converged, 0.01f, 500);
1662 limitation_state = (int) variables[5];
1663 Gamma = variables[6];
1664 J_over_Jmax = variables[8];
1670 return getCoefficientsForPrimitive_Empirical(UUID);
1674 return getCoefficientsForPrimitive_Farquhar(UUID);
1678 message_flag =
false;
1682 message_flag =
true;
1687 if (strcmp(label,
"Ci") == 0 || strcmp(label,
"limitation_state") == 0 || strcmp(label,
"Gamma_CO2") == 0 || strcmp(label,
"electron_transport_ratio") == 0 || strcmp(label,
"Cm") == 0 || strcmp(label,
"Vp") == 0) {
1688 output_prim_data.emplace_back(label);
1691 static bool unknown_output_warning_shown =
false;
1692 if (!unknown_output_warning_shown) {
1693 std::cerr <<
"WARNING (PhotosynthesisModel::optionalOutputPrimitiveData): unknown output primitive data " << label << std::endl;
1694 unknown_output_warning_shown =
true;
1706 size_t assumed_default_i = 0;
1707 size_t assumed_default_TL = 0;
1708 size_t assumed_default_CO2 = 0;
1709 size_t assumed_default_gM = 0;
1710 size_t assumed_default_gH = 0;
1712 size_t Nprimitives = UUIDs.size();
1714 for (
uint UUID: UUIDs) {
1717 assumed_default_i++;
1722 assumed_default_TL++;
1728 assumed_default_gH++;
1733 assumed_default_gM++;
1738 assumed_default_CO2++;
1742 std::cout <<
"--- Photosynthesis Model Default Value Report ---" << std::endl;
1744 std::cout <<
"PAR flux: " << assumed_default_i <<
" of " << Nprimitives <<
" used default value of " << i_PAR_default
1746 "radiation_flux_PAR"
1747 " primitive data did not exist"
1749 std::cout <<
"surface temperature: " << assumed_default_TL <<
" of " << Nprimitives <<
" used default value of " << TL_default
1752 " primitive data did not exist"
1754 std::cout <<
"boundary-layer conductance: " << assumed_default_gH <<
" of " << Nprimitives <<
" used default value of " << gH_default
1756 "boundarylayer_conductance"
1757 " primitive data did not exist"
1759 std::cout <<
"moisture conductance: " << assumed_default_gM <<
" of " << Nprimitives <<
" used default value of " << gM_default
1761 "moisture_conductance"
1762 " primitive data did not exist"
1764 std::cout <<
"air CO2: " << assumed_default_CO2 <<
" of " << Nprimitives <<
" used default value of " << CO2_default
1767 " primitive data did not exist"
1770 std::cout <<
"--------------------------------------------------" << std::endl;