The BioCro C++ Library
sunML.cpp
Go to the documentation of this file.
1#include "sunML.h"
2
35 double R, // dimensionless
36 double T, // dimensionless
37 double I_0 // Light units such as `micromol / m^2 / s` or `J / m^2 / s`
38)
39{
40 return I_0 * (1 - R - T); // same units as `I_0`
41}
42
80 double R, // dimensionless
81 double T, // dimensionless
82 double I_0 // Light units such as `micromol / m^2 / s` or `J / m^2 / s`
83)
84{
85 return I_0 * (1 - R - T) / (1 - T); // same units as `I_0`
86}
87
102 double ppfd, // micromol / m^2 / s
103 double par_energy_content, // J / micromol
104 double par_energy_fraction // dimensionless
105)
106{
107 return ppfd * par_energy_content *
108 (1 - par_energy_fraction) / par_energy_fraction; // J / m^2 / s
109}
110
151 double incident_nir, // J / m^2 / s
152 double incident_ppfd, // micromol / m^2 / s
153 double par_energy_content, // J / micromol
154 double leaf_reflectance_par, // dimensionless
155 double leaf_transmittance_par, // dimensionless
156 double leaf_reflectance_nir, // dimensionless
157 double leaf_transmittance_nir // dimensionless
158)
159{
160 double const absorbed_par = thin_layer_absorption(
161 leaf_reflectance_par,
162 leaf_transmittance_par,
163 incident_ppfd * par_energy_content); // J / m^2 / s
164
165 double const absorbed_nir = thin_layer_absorption(
166 leaf_reflectance_nir,
167 leaf_transmittance_nir,
168 incident_nir); // J / m^2 / s
169
170 return absorbed_par + absorbed_nir; // J / m^2 / s
171}
172
217 double Q_o, // Light units such as `micromol / m^2 / s` or `J / m^2 / s`
218 double k, // dimensionless
219 double alpha, // dimensionless
220 double ell // dimensionless from m^2 leaf / m^2 ground
221)
222{
223 return Q_o * exp(-k * sqrt(alpha) * ell); // same units as `Q_ob`
224}
225
278 double Q_ob, // Light units such as `micromol / m^2 / s` or `J / m^2 / s`
279 double k_direct, // dimensionless
280 double alpha_direct, // dimensionless
281 double ell // dimensionless from m^2 leaf / m^2 ground
282)
283{
284 return total_radiation(Q_ob, k_direct, alpha_direct, ell) -
285 Q_ob * exp(-k_direct * ell); // same units as `Q_ob`
286}
287
336 double Q_ob, // Light units such as `micromol / m^2 / s` or `J / m^2 / s`
337 double Q_od, // same units as `Q_ob`
338 double k_direct, // dimensionless
339 double k_diffuse, // dimensionless
340 double alpha, // dimensionless
341 double ell // dimensionless from m^2 leaf / m^2 ground
342)
343{
344 return total_radiation(Q_od, k_diffuse, alpha, ell) +
345 downscattered_radiation(Q_ob, k_direct, alpha, ell); // same units as `Q_ob`
346}
347
409 double ambient_ppfd_beam, // micromol / (m^2 beam) / s
410 double ambient_ppfd_diffuse, // micromol / m^2 / s
411 double chil, // dimensionless from m^2 / m^2
412 double cosine_zenith_angle, // dimensionless
413 double heightf, // m^-1 from m^2 leaf / m^2 ground / m height
414 double k_diffuse, // dimensionless
415 double lai, // dimensionless from m^2 / m^2
416 double leaf_reflectance_nir, // dimensionless
417 double leaf_reflectance_par, // dimensionless
418 double leaf_transmittance_nir, // dimensionless
419 double leaf_transmittance_par, // dimensionless
420 double par_energy_content, // J / micromol
421 double par_energy_fraction, // dimensionless
422 int nlayers // dimensionless
423)
424{
425 if (nlayers < 1 || nlayers > MAXLAY) {
426 throw std::out_of_range("nlayers must be at least 1 but no more than MAXLAY.");
427 }
428
429 if (cosine_zenith_angle > 1 || cosine_zenith_angle < -1) {
430 throw std::out_of_range("cosine_zenith_angle must be between -1 and 1.");
431 }
432
433 if (k_diffuse > 1 || k_diffuse < 0) {
434 throw std::out_of_range("k_diffuse must be between 0 and 1.");
435 }
436
437 if (chil < 0) {
438 throw std::out_of_range("chil must be non-negative.");
439 }
440
441 if (heightf <= 0) {
442 throw std::out_of_range("heightf must greater than zero.");
443 }
444
445 // Calculate absorptivity from leaf reflectance and transmission
446 double const absorptivity_nir = 1.0 - leaf_reflectance_nir - leaf_transmittance_nir; // dimensionless
447 double const absorptivity_par = 1.0 - leaf_reflectance_par - leaf_transmittance_par; // dimensionless
448
449 if (absorptivity_par > 1 || absorptivity_par < 0) {
450 throw std::out_of_range("absorptivity_par must be between 0 and 1.");
451 }
452
453 if (absorptivity_nir > 1 || absorptivity_nir < 0) {
454 throw std::out_of_range("absorptivity_nir must be between 0 and 1.");
455 }
456
457 // Calculate the leaf shape factor for an ellipsoidal leaf angle
458 // distribution using the equation from page 251 of Campbell & Norman
459 // (1998). We will use this value as `k_direct`, the canopy extinction
460 // coefficient for direct photosynthetically active radiation throughout the
461 // canopy. This quantity represents the ratio of horizontal area to total
462 // area for leaves in the canopy and is therefore dimensionless from
463 // (m^2 ground) / (m^2 leaf).
464 double zenith_angle = acos(cosine_zenith_angle); // radians
465 double k0 = sqrt(pow(chil, 2) + pow(tan(zenith_angle), 2));
466 double k1 = chil + 1.744 * pow((chil + 1.182), -0.733);
467 double k_direct = k0 / k1; // dimensionless
468
469 double lai_per_layer = lai / nlayers;
470
471 // Calculate the fraction of direct radiation that passes through the canopy
472 // using Equation 15.1. Note that this is equivalent to the fraction of
473 // ground area below the canopy that is exposed to direct sunlight. Note
474 // that if the sun is at or below the horizon, no part of the soil is
475 // sunlit; this corresponds to the case where cosine_zenith_angle is close
476 // to or below zero.
477 double canopy_direct_transmission_fraction =
478 cosine_zenith_angle <= 1E-10 ? 0.0 : exp(-k_direct * lai); // dimensionless
479
480 // Calculate the ambient direct PPFD through a surface parallel to the ground
481 const double ambient_ppfd_beam_ground = ambient_ppfd_beam * cosine_zenith_angle; // micromol / (m^2 ground) / s
482
483 // Calculate the ambient direct PPFD through a unit area of leaf surface
484 double ambient_ppfd_beam_leaf = ambient_ppfd_beam_ground * k_direct; // micromol / (m^2 leaf) / s
485
486 // Calculate related NIR energy fluxes
487 const double ambient_nir_beam = nir_from_ppfd(
488 ambient_ppfd_beam, par_energy_content, par_energy_fraction); // J / (m^2 beam) / s
489
490 const double ambient_nir_beam_ground = nir_from_ppfd(
491 ambient_ppfd_beam_ground, par_energy_content, par_energy_fraction); // J / (m^2 ground) / s
492
493 const double ambient_nir_diffuse = nir_from_ppfd(
494 ambient_ppfd_diffuse, par_energy_content, par_energy_fraction); // J / (m^2 ground) / s
495
496 double ambient_nir_beam_leaf = nir_from_ppfd(
497 ambient_ppfd_beam_leaf, par_energy_content, par_energy_fraction); // J / (m^2 leaf) / s
498
499 // Start to fill in the light profile values
500 Light_profile light_profile;
501 light_profile.canopy_direct_transmission_fraction = canopy_direct_transmission_fraction;
502
503 // Fill in the layer-dependent light profile values
504 for (int i = 0; i < nlayers; ++i) {
505 // Get the cumulative LAI for this layer, which represents the total
506 // leaf area above this layer
507 const double cumulative_lai = lai_per_layer * (i + 0.5);
508
509 // Calculate the PPFD incident on shaded leaves
510 double shaded_ppfd = shaded_radiation(
511 ambient_ppfd_beam_ground, ambient_ppfd_diffuse,
512 k_direct, k_diffuse,
513 absorptivity_par, cumulative_lai); // micromol / m^2 / s
514
515 // Calculate the NIR incident on shaded leaves
516 double shaded_nir = shaded_radiation(
517 ambient_nir_beam_ground, ambient_nir_diffuse,
518 k_direct, k_diffuse,
519 absorptivity_nir, cumulative_lai); // J / m^2 / s
520
521 // Calculate the fraction of sunlit and shaded leaves in this canopy
522 // layer using Equation 15.22.
523 double sunlit_fraction = exp(-k_direct * cumulative_lai); // dimensionless
524 double shaded_fraction = 1 - sunlit_fraction; // dimensionless
525
526 // For values of cosine_zenith_angle close to or less than 0, in place
527 // of the calculations above, we want to use the limits of the above
528 // expressions as cosine_zenith_angle approaches 0 from the right:
529 if (cosine_zenith_angle <= 1E-10) {
530 ambient_ppfd_beam_leaf = ambient_ppfd_beam / k1;
531 ambient_nir_beam_leaf = ambient_nir_beam / k1;
532 shaded_ppfd = ambient_ppfd_diffuse * exp(-k_diffuse * cumulative_lai);
533 shaded_nir = ambient_nir_diffuse * exp(-k_diffuse * cumulative_lai);
534 sunlit_fraction = 0;
535 shaded_fraction = 1;
536 }
537
538 // Store values of incident PPFD
539 light_profile.height[i] = (lai - cumulative_lai) / heightf; // m
540 light_profile.shaded_fraction[i] = shaded_fraction; // dimensionless from m^2 / m^2
541 light_profile.shaded_incident_ppfd[i] = shaded_ppfd; // micromol / (m^2 leaf) / s
542 light_profile.shaded_incident_nir[i] = shaded_nir; // J / (m^2 leaf) / s
543 light_profile.sunlit_fraction[i] = sunlit_fraction; // dimensionless from m^2 / m^2
544 light_profile.sunlit_incident_ppfd[i] = ambient_ppfd_beam_leaf + shaded_ppfd; // micromol / (m^2 leaf) / s
545 light_profile.sunlit_incident_nir[i] = ambient_nir_beam_leaf + shaded_nir; // J / (m^2 leaf) / s
546
547 // Store values of absorbed PPFD
548 light_profile.sunlit_absorbed_ppfd[i] =
550 leaf_reflectance_par,
551 leaf_transmittance_par,
552 ambient_ppfd_beam_leaf + shaded_ppfd); // micromol / m^2 / s
553
554 light_profile.shaded_absorbed_ppfd[i] =
556 leaf_reflectance_par,
557 leaf_transmittance_par,
558 shaded_ppfd); // micromol / m^2 / s
559
560 // Store values of absorbed solar energy (including PAR and NIR)
561 light_profile.sunlit_absorbed_shortwave[i] =
563 ambient_nir_beam_leaf + shaded_nir,
564 ambient_ppfd_beam_leaf + shaded_ppfd,
565 par_energy_content,
566 leaf_reflectance_par,
567 leaf_transmittance_par,
568 leaf_reflectance_nir,
569 leaf_transmittance_nir); // J / (m^2 leaf) / s
570
571 light_profile.shaded_absorbed_shortwave[i] =
573 shaded_nir,
574 shaded_ppfd,
575 par_energy_content,
576 leaf_reflectance_par,
577 leaf_transmittance_par,
578 leaf_reflectance_nir,
579 leaf_transmittance_nir); // J / (m^2 leaf) / s
580 }
581 return light_profile;
582}
#define MAXLAY
Definition: AuxBioCro.h:13
double shaded_fraction[MAXLAY]
Definition: sunML.h:11
double sunlit_incident_nir[MAXLAY]
Definition: sunML.h:17
double shaded_incident_nir[MAXLAY]
Definition: sunML.h:12
double sunlit_absorbed_ppfd[MAXLAY]
Definition: sunML.h:14
double sunlit_fraction[MAXLAY]
Definition: sunML.h:16
double shaded_incident_ppfd[MAXLAY]
Definition: sunML.h:13
double height[MAXLAY]
Definition: sunML.h:8
double sunlit_incident_ppfd[MAXLAY]
Definition: sunML.h:18
double shaded_absorbed_shortwave[MAXLAY]
Definition: sunML.h:10
double sunlit_absorbed_shortwave[MAXLAY]
Definition: sunML.h:15
double canopy_direct_transmission_fraction
Definition: sunML.h:7
double shaded_absorbed_ppfd[MAXLAY]
Definition: sunML.h:9
double shaded_radiation(double Q_ob, double Q_od, double k_direct, double k_diffuse, double alpha, double ell)
Computes the radiation incident on shaded leaves using Equation 15.19 from Campbell & Norman (1998).
Definition: sunML.cpp:335
Light_profile sunML(double ambient_ppfd_beam, double ambient_ppfd_diffuse, double chil, double cosine_zenith_angle, double heightf, double k_diffuse, double lai, double leaf_reflectance_nir, double leaf_reflectance_par, double leaf_transmittance_nir, double leaf_transmittance_par, double par_energy_content, double par_energy_fraction, int nlayers)
Computes an n-layered light profile from the direct light, diffuse light, leaf area index,...
Definition: sunML.cpp:408
double absorbed_shortwave(double incident_nir, double incident_ppfd, double par_energy_content, double leaf_reflectance_par, double leaf_transmittance_par, double leaf_reflectance_nir, double leaf_transmittance_nir)
Computes total shortwave radiation absorbed by a leaf.
Definition: sunML.cpp:150
double thin_layer_absorption(double R, double T, double I_0)
Computes absorbed light from incident light for a thin layer of material.
Definition: sunML.cpp:34
double downscattered_radiation(double Q_ob, double k_direct, double alpha_direct, double ell)
Computes downscattered radiation using Equation 15.20 from Campbell & Norman (1998).
Definition: sunML.cpp:277
double total_radiation(double Q_o, double k, double alpha, double ell)
Computes total radiation (direct and downscattered) using Equation 15.15 from Campbell & Norman (1998...
Definition: sunML.cpp:216
double nir_from_ppfd(double ppfd, double par_energy_content, double par_energy_fraction)
Computes energy flux in the near-infrared band (in J / m^2 / s) from the photosynthetically active ph...
Definition: sunML.cpp:101
double thick_layer_absorption(double R, double T, double I_0)
Computes absorbed light from incident light for a thick layer of material.
Definition: sunML.cpp:79