diff --git a/docs/source/api/core_api.md b/docs/source/api/core_api.md index 528f10d3..a5460a49 100644 --- a/docs/source/api/core_api.md +++ b/docs/source/api/core_api.md @@ -69,3 +69,11 @@ language_info: :autosummary: :members: ``` + +## The {mod}`~pyrealm.core.solar` submodule + +```{eval-rst} +.. automodule:: pyrealm.core.solar + :autosummary: + :members: +``` diff --git a/docs/source/conf.py b/docs/source/conf.py index 2fe69fc6..0b60ec4d 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -107,6 +107,8 @@ class MyReferenceStyle(AuthorYearReferenceStyle): ("py:class", "numpy.dtype"), ("py:class", "numpy.dtype[+ScalarType]"), ("py:class", "numpy.typing.NDArray"), + ("py:class", "numpy.NDArray"), + ("py:class", "NDArray"), ("py:class", "dataclasses.InitVar"), ( "py:class", diff --git a/docs/source/refs.bib b/docs/source/refs.bib index 7a4abd0d..adac77ea 100644 --- a/docs/source/refs.bib +++ b/docs/source/refs.bib @@ -1,3 +1,16 @@ +@article{allen:1996a, + title = {Assessing Integrity of Weather Data for Reference Evapotranspiration Estimation}, + author = {Allen, Richard G.}, + year = {1996}, + month = mar, + journal = {Journal of irrigation and drainage engineering}, + volume = {122}, + number = {2}, + pages = {97--106}, + doi = {10.1061/(ASCE)0733-9437(1996)122:2(97)}, + abstract = {Procedures and guidelines are recommended for assessing integrity, quality, and reasonableness of measured weather data and equipment calibration for automated and electronic agricultural weather stations. The procedures include calculation of hourly and 24-h clear sky envelopes for solar radiation, validation of net radiation measurements using calculation equations, and evaluation of expected trends and relationships between air vapor content and air temperature. The procedures for creating clear sky solar radiation envelopes include equations to account for the effects of atmospheric water vapor content and sun angle. Procedures for adjusting air temperature and air vapor content data are introduced to compensate for the aridity of the weather station environment. All of the guidelines are simple and straightforward, and can serve as preliminary ``filters'' by which to scrutinize weather measurements and as near real-time data flagging procedures for agricultural weather networks.} +} + @techreport{allen:1998a, title = {Crop Evapotranspiration - {{Guidelines}} for Computing Crop Water Requirements}, author = {Allen, Richard G. and Pereira, Luis S and Raes, Dirk and Smith, Martin}, @@ -5,10 +18,10 @@ @techreport{allen:1998a number = {56}, address = {Rome}, institution = {FAO}, - file = {/Users/dorme/Zotero/storage/UW9UBHX2/Allen et al. - 1998 - Crop evapotranspiration - Guidelines for computing.pdf} + file = {/Users/je484/Zotero/storage/UW9UBHX2/Allen et al. - 1998 - Crop evapotranspiration - Guidelines for computing.pdf} } -@article{Argles:2020cy, +@article{argles:2020a, title = {Robust {{Ecosystem Demography}} ({{RED}} Version 1.0): A Parsimonious Approach to Modelling Vegetation Dynamics in {{Earth}} System Models}, author = {Argles, Arthur P K and Moore, Jonathan R and Huntingford, Chris and Wiltshire, Andrew J and Harper, Anna B and Jones, Chris D and Cox, Peter M}, year = {2020}, @@ -17,14 +30,8 @@ @article{Argles:2020cy number = {9}, pages = {4067--4089}, doi = {10.5194/gmd-13-4067-2020}, - date-added = {2020-12-01T16:58:12GMT}, - date-modified = {2021-07-22T14:23:35GMT}, langid = {english}, - local-url = {file://localhost/Users/dorme/References/Library.papers3/Articles/2020/Argles/Geoscientific\%20Model\%20Development\%202020\%20Argles.pdf}, - rating = {0}, - read = {Yes}, - uri = {papers3://publication/doi/10.5194/gmd-13-4067-2020}, - file = {/Users/dorme/Zotero/storage/WKQGYNRT/Geoscientific Model Development 2020 Argles.pdf} + file = {/Users/je484/Zotero/storage/WKQGYNRT/Geoscientific Model Development 2020 Argles.pdf} } @article{Atkin:2015hk, @@ -35,18 +42,13 @@ @article{Atkin:2015hk journal = {New Phytologist}, volume = {206}, number = {2}, + eprint = {25581061}, + eprinttype = {pmid}, pages = {614--636}, doi = {10.1111/nph.13253}, abstract = {Leaf dark respiration (Rdark ) is an important yet poorly quantified component of the global carbon cycle. Given this, we analyzed a new global database of Rdark and associated leaf traits. Data for 899 species were compiled from 100 sites (from the Arctic to the tropics). Several woody and nonwoody plant functional types (PFTs) were represented. Mixed-effects models were used to disentangle sources of variation in Rdark . Area-based Rdark at the prevailing average daily growth temperature (T) of each site increased only twofold from the Arctic to the tropics, despite a 20{$^\circ$}C increase in growing T (8-28{$^\circ$}C). By contrast, Rdark at a standard T (25{$^\circ$}C, Rdark (25) ) was threefold higher in the Arctic than in the tropics, and twofold higher at arid than at mesic sites. Species and PFTs at cold sites exhibited higher Rdark (25) at a given photosynthetic capacity (Vcmax (25) ) or leaf nitrogen concentration ([N]) than species at warmer sites. Rdark (25) values at any given Vcmax (25) or [N] were higher in herbs than in woody plants. The results highlight variation in Rdark among species and across global gradients in T and aridity. In addition to their ecological significance, the results provide a framework for improving representation of Rdark in terrestrial biosphere models (TBMs) and associated land-surface components of Earth system models (ESMs).}, - affiliation = {ARC Centre of Excellence in Plant Energy Biology, Research School of Biology, The Australian National University, Building 134, Canberra, ACT, 0200, Australia; Division of Plant Sciences, Research School of Biology, The Australian National University, Building 46, Canberra, ACT, 0200, Australia.}, - date-added = {2021-01-28T11:56:12GMT}, - date-modified = {2021-01-28T15:58:45GMT}, langid = {english}, - local-url = {file://localhost/Users/dorme/References/Library.papers3/Articles/2015/Atkin/New\%20Phytol.\%202015\%20Atkin.pdf}, - pmid = {25581061}, - rating = {0}, - uri = {papers3://publication/doi/10.1111/nph.13253}, - file = {/Users/dorme/Zotero/storage/JCS7TM7H/New Phytol. 2015 Atkin.pdf} + file = {/Users/je484/Zotero/storage/JCS7TM7H/New Phytol. 2015 Atkin.pdf} } @article{badeck:2005a, @@ -56,11 +58,10 @@ @article{badeck:2005a journal = {Rapid Communications in Mass Spectrometry}, volume = {19}, number = {11}, - eprint = {https://analyticalsciencejournals.onlinelibrary.wiley.com/doi/pdf/10.1002/rcm.1912}, pages = {1381--1391}, doi = {10.1002/rcm.1912}, abstract = {Abstract Discrimination against 13C during photosynthesis is a well-characterised phenomenon. It leaves behind distinct signatures in organic matter of plants and in the atmosphere. The former is depleted in 13C, the latter is enriched during periods of preponderant photosynthetic activity of terrestrial ecosystems. The intra-annual cycle and latitudinal gradient in atmospheric 13C resulting from photosynthetic and respiratory activities of terrestrial plants have been exploited for the reconstruction of sources and sinks through deconvolution by inverse modelling. Here, we compile evidence for widespread post-photosynthetic fractionation that further modifies the isotopic signatures of individual plant organs and consequently leads to consistent differences in {$\delta$}13C between plant organs. Leaves were on average 0.96‰ and 1.91‰ more depleted than roots and woody stems, respectively. This phenomenon is relevant if the isotopic signature of CO2-exchange fluxes at the ecosystem level is used for the reconstruction of individual sources and sinks. It may also modify the parameterisation of inverse modelling approaches if it leads to different isotopic signatures of organic matter with different residence times within the ecosystems and to a respiratory contribution to the average difference between the isotopic composition of plant organic matter and the atmosphere. We discuss the main hypotheses that can explain the observed inter-organ differences in {$\delta$}13C. Copyright {\copyright} 2005 John Wiley \& Sons, Ltd.}, - file = {/Users/dorme/Zotero/storage/2NXLVXHK/Badeck et al. - 2005 - Post-photosynthetic fractionation of stable carbon.pdf} + file = {/Users/je484/Zotero/storage/2NXLVXHK/Badeck et al. - 2005 - Post-photosynthetic fractionation of stable carbon.pdf} } @article{BerberanSantos:2009bk, @@ -73,13 +74,8 @@ @article{BerberanSantos:2009bk number = {3}, pages = {990--1004}, doi = {10.1007/s10910-009-9620-7}, - date-added = {2020-11-30T16:18:45GMT}, - date-modified = {2020-11-30T16:19:02GMT}, langid = {english}, - local-url = {file://localhost/Users/dorme/References/Library.papers3/Articles/2009/Berberan-Santos/J\%20Math\%20Chem\%202009\%20Berberan-Santos.pdf}, - rating = {0}, - uri = {papers3://publication/doi/10.1007/s10910-009-9620-7}, - file = {/Users/dorme/Zotero/storage/EN33QA2H/J Math Chem 2009 Berberan-Santos.pdf} + file = {/Users/je484/Zotero/storage/EN33QA2H/J Math Chem 2009 Berberan-Santos.pdf} } @article{berger:1978a, @@ -91,14 +87,12 @@ @article{berger:1978a volume = {35}, number = {12}, pages = {2362--2367}, - publisher = {American Meteorological Society}, issn = {0022-4928, 1520-0469}, doi = {10.1175/1520-0469(1978)035<2362:LTVODI>2.0.CO;2}, urldate = {2023-07-13}, abstract = {Abstract The first part of this note provides all trigonometrical formulas which allow the direct spectral analysis and the computation of those long-term variations of the earth's orbital elements which are of primary interest for the computation of the insolation. The elements are the eccentricity, the longitude of the perihelion, the processional parameter and the obliquity. This new formulary is much more simple to use than the ones previously designed and still provides excellent accuracy, mainly because it takes into account the influence of the most important higher order terms in the series expansions. The second part is devoted to the computation of the daily insolation both for calendar and solar dates.}, - chapter = {Journal of the Atmospheric Sciences}, langid = {english}, - file = {/Users/dorme/Zotero/storage/DPM5WN32/Berger - 1978 - Long-Term Variations of Daily Insolation and Quate.pdf} + file = {/Users/je484/Zotero/storage/DPM5WN32/Berger - 1978 - Long-Term Variations of Daily Insolation and Quate.pdf} } @article{berger:1993a, @@ -114,7 +108,7 @@ @article{berger:1993a urldate = {2024-08-02}, abstract = {Solar irradiance received on a horizontal surface depends on the solar output, the semimajor axis of the elliptical orbit of the Earth around the sun (a), the distance from the Earth to the sun (r), and the zenith distance (z). The spectrum of the distance, r, for a given value of the true longitude, {$\lambda$}, displays mainly the precessional periods and, with much less power, half precession periods, eccentricity periods, and some combination tones. The zenith distance or its equivalent, the elevation angle (E), is only a function of obliquity ({$\epsilon$}) for a given latitude, {$\phi$}, true longitude, and hour angle, H. Therefore the insolation at a given constant value of z is only a function of precession and eccentricity. On the other hand, the value of the hour angle, H, corresponding to this fixed value of z varies with {$\varepsilon$}, except for the equinoxes, where H corresponding to a constant z also remains constant through time. Three kinds of insolation have been computed both analytically and numerically: the instantaneous insolation (irradiance) at noon, the daily irradiation, and the irradiations received during particular time intervals of the day defined by two constant values of the zenith distance (diurnal irradiations). Mean irradiances (irradiations divided by the length of the time interval over which they are calculated) are also computed for different time intervals, like the interval between sunrise and sunset, in particular. Examples of these insolations are given in this paper for the equinoxes and the solstices. At the equinoxes, for each latitude, all insolations are only a function of precession (this invalidates the results obtained by Cerveny [1991]). At the solstices, both precession and obliquity are present, although precession dominates for most of the latitudes. Because the lengths of the astronomical seasons are secularly variable (in terms of precession only), a particular calendar day does not always correspond to the same position relative to the sun through geological time. Similarly, a given longitude of the Sun on its orbit does not correspond to the same calendar day. For example, 103 kyr ago, assuming arbitrarily that the spring equinox is always on March 21, autumn began on September 13, and 114 kyr ago, it began on September 27, the length of the summer season being 85 and 98 calendar days, respectively, at these remote times in the past.}, langid = {english}, - file = {/Users/dorme/Zotero/storage/ALTXDVSM/Berger et al. - 1993 - Insolation and Earth's orbital periods.pdf;/Users/dorme/Zotero/storage/HWQ4XSZN/93JD00222.html} + file = {/Users/je484/Zotero/storage/ALTXDVSM/Berger et al. - 1993 - Insolation and Earth's orbital periods.pdf;/Users/je484/Zotero/storage/HWQ4XSZN/93JD00222.html} } @article{Bernacchi:2001kg, @@ -126,14 +120,8 @@ @article{Bernacchi:2001kg number = {2}, pages = {253--259}, doi = {10.1111/j.1365-3040.2001.00668.x}, - date-added = {2020-11-30T12:24:10GMT}, - date-modified = {2020-11-30T14:42:53GMT}, langid = {english}, - local-url = {file://localhost/Users/dorme/References/Library.papers3/Articles/2001/Bernacchi/Plant\%20Cell\%20\&\%20Environment\%202001\%20Bernacchi.pdf}, - rating = {0}, - read = {Yes}, - uri = {papers3://publication/doi/10.1111/j.1365-3040.2001.00668.x}, - file = {/Users/dorme/Zotero/storage/FI9562Z3/Plant Cell & Environment 2001 Bernacchi.pdf} + file = {/Users/je484/Zotero/storage/FI9562Z3/Plant Cell & Environment 2001 Bernacchi.pdf} } @article{Bernacchi:2003dc, @@ -145,16 +133,10 @@ @article{Bernacchi:2003dc volume = {26}, number = {9}, pages = {1419--1430}, - publisher = {John Wiley \& Sons, Ltd}, doi = {10.1046/j.0016-8025.2003.01050.x}, abstract = {The leaf model of C3 photosynthesis of Farquhar, von Caemmerer \& Berry (Planta 149, 78--90, 1980) provides the basis for scaling carbon exchange from leaf to canopy and Earth-System models, and is wid...}, - date-added = {2020-11-30T14:15:56GMT}, - date-modified = {2020-11-30T14:42:52GMT}, langid = {english}, - local-url = {file://localhost/Users/dorme/References/Library.papers3/Articles/2003/Bernacchi/Plant\%20Cell\%20\&\%20Environment\%202003\%20Bernacchi.pdf}, - rating = {0}, - uri = {papers3://publication/doi/10.1046/j.0016-8025.2003.01050.x}, - file = {/Users/dorme/Zotero/storage/7J2I98DM/Plant Cell & Environment 2003 Bernacchi.pdf} + file = {/Users/je484/Zotero/storage/7J2I98DM/Plant Cell & Environment 2003 Bernacchi.pdf} } @article{boyd:2015a, @@ -169,7 +151,7 @@ @article{boyd:2015a doi = {10.1104/pp.15.00586}, urldate = {2022-05-20}, langid = {english}, - file = {/Users/dorme/Zotero/storage/P866H95S/Boyd et al. - 2015 - Temperature response of C4 photosynthesis Biochem.pdf} + file = {/Users/je484/Zotero/storage/P866H95S/Boyd et al. - 2015 - Temperature response of C4 photosynthesis Biochem.pdf} } @article{cai:2020a, @@ -187,7 +169,7 @@ @article{cai:2020a urldate = {2022-05-16}, abstract = {Abstract Gross primary production (GPP) by terrestrial ecosystems is the largest flux in the global carbon cycle, and its continuing increase in response to environmental changes is key to land ecosystems' capacity to offset anthropogenic CO 2 emissions. However, the CO 2 - and climate-sensitivities of GPP vary among models. We applied the `P model'---a parameter-sparse and extensively tested light use efficiency (LUE) model, driven by CO 2 , climate and remotely sensed greenness data---at 29 sites with multi-year eddy-covariance flux measurements. Observed (both positive and negative) GPP trends at these sites were predicted, albeit with some bias. Increasing LUE (due to rising atmospheric CO 2 concentration) and green vegetation cover were the primary controls of modelled GPP trends across sites. Global GPP simulated by the same model increased by 0.46 {\textpm} 0.09 Pg C yr --2 during 1982--2016. This increase falls in the mid-range rate of simulated increase by the TRENDY v8 ensemble of state-of-the-art ecosystem models. The modelled LUE increase during 1900--2013 was 15\%, similar to a published estimate based on deuterium isotopomers. Rising CO 2 was the largest contributor to the modelled GPP increase. Greening, which may in part be caused by rising CO 2 , ranked second but dominated the modelled GPP change over large areas, including semi-arid vegetation on all continents. Warming caused a small net reduction in modelled global GPP, but dominated the modelled GPP increase in high northern latitudes. These findings strengthen the evidence that rising LUE due to rising CO 2 level and increased green vegetation cover (fAPAR) are the main causes of increasing GPP, and thereby, the terrestrial carbon sink.}, langid = {english}, - file = {/Users/dorme/Zotero/storage/9YJ8TV4Y/Cai and Prentice - 2020 - Recent trends in gross primary production and thei.pdf} + file = {/Users/je484/Zotero/storage/9YJ8TV4Y/Cai and Prentice - 2020 - Recent trends in gross primary production and thei.pdf} } @article{chen:2008a, @@ -203,7 +185,30 @@ @article{chen:2008a doi = {10.1063/1.434179}, urldate = {2023-07-04}, abstract = {The equation of state of water valid over the range 0--100\,{$^\circ$}C and 0--1000 bar has been determined from the high pressure sound velocities of Wilson, which were reanalyzed by Chen and Millero. The equation of state has a maximum error of {\textpm}0.01 bar-1 in isothermal compressibility and is in the form of a secant bulk modulus: K=V0P/(V0-V) =K0+AP+BP2, where K, K0, and V, V0 are the secant bulk moduli and specific volumes at applied pressures P and 0 (1 atm), respectively; A and B are temperature dependent parameters. The good agreement (to within 20{\texttimes}10-6 cm3\,g-1) of specific volumes calculated using the above equation with those obtained from other modifications of the Wilson sound velocity data demonstrates the reliability of the sound velocity method for determining equations of state.}, - file = {/Users/dorme/Zotero/storage/HHYLGDG4/Chen et al. - 2008 - The equation of state of pure water determined fro.pdf;/Users/dorme/Zotero/storage/M6MBWTPL/The-equation-of-state-of-pure-water-determined.html} + file = {/Users/je484/Zotero/storage/HHYLGDG4/Chen et al. - 2008 - The equation of state of pure water determined fro.pdf;/Users/je484/Zotero/storage/M6MBWTPL/The-equation-of-state-of-pure-water-determined.html} +} + +@article{colinprentice:1993a, + title = {A Simulation Model for the Transient Effects of Climate Change on Forest Landscapes}, + author = {Prentice, Iain Colin and Sykes, Martin T. and Cramer, Wolfgang}, + year = {1993}, + month = jan, + journal = {Ecological Modelling}, + volume = {65}, + number = {1}, + pages = {51--70}, + issn = {0304-3800}, + doi = {10.1016/0304-3800(93)90126-D}, + urldate = {2024-10-15}, + abstract = {Forests are likely to show complex transient responses to rapid changes in climate. The model described here simulates the dynamics of forest landscapes in a changing environment with simple phenomenological equations for tree growth processes and local environmental feedbacks. Tree establishment and growth rates are modified by species-specific functions describing the effects of winter and summer temperature limitations, accumulated annual foliage net assimilation and sapwood respiration as functions of temperature, CO2 fertilization, and growing-season drought. These functions provide external conditions for the simulation of patch-scale forest dynamics by a forest succession model, FORSKA, in which all of the trees on each 0.1 ha patch interact by competition for light and nutrients. The landscape is simulated as an array of such patches. The probability of disturbance on a patch is a power function of time since disturbance. Forest structure, composition and biomass simulated for the landscape average in boreal and temperate deciduous forests approach reasonable equilibrium values in 200--400 years. A climatic warning scenario is applied to central Sweden, where temperature and precipitation increases are shown to interact with each other and with soil water capacity in determining the transient and equilibrium responses of the forest landscape to climate change.}, + file = {/Users/je484/Zotero/storage/6XY4AXAB/Colin Prentice et al. - 1993 - A simulation model for the transient effects of cl.pdf;/Users/je484/Zotero/storage/VQAXKGCP/030438009390126D.html} +} + +@misc{CrownmdcroJupyterLab, + title = {Crown.Md\#cro{\dots} - {{JupyterLab}}}, + urldate = {2024-09-30}, + howpublished = {http://localhost:8888/lab/tree/source/users/demography/crown.md\#crown-radius-values}, + file = {/Users/je484/Zotero/storage/LHHVRPS9/crown.html} } @article{davis:2017a, @@ -222,7 +227,7 @@ @article{davis:2017a urldate = {2023-07-05}, abstract = {Bioclimatic indices for use in studies of ecosystem function, species distribution, and vegetation dynamics under changing climate scenarios depend on estimates of surface fluxes and other quantities, such as radiation, evapotranspiration and soil moisture, for which direct observations are sparse. These quantities can be derived indirectly from meteorological variables, such as near-surface air temperature, precipitation and cloudiness. Here we present a consolidated set of simple process-led algorithms for simulating habitats (SPLASH) allowing robust approximations of key quantities at ecologically relevant timescales. We specify equations, derivations, simplifications, and assumptions for the estimation of daily and monthly quantities of top-of-the-atmosphere solar radiation, net surface radiation, photosynthetic photon flux density, evapotranspiration (potential, equilibrium, and actual), condensation, soil moisture, and runoff, based on analysis of their relationship to fundamental climatic drivers. The climatic drivers include a minimum of three meteorological inputs: precipitation, air temperature, and fraction of bright sunshine hours. Indices, such as the moisture index, the climatic water deficit, and the Priestley--Taylor coefficient, are also defined. The SPLASH code is transcribed in C++, FORTRAN, Python, and R. A total of 1 year of results are presented at the local and global scales to exemplify the spatiotemporal patterns of daily and monthly model outputs along with comparisons to other model results.}, langid = {english}, - file = {/Users/dorme/Zotero/storage/CDUHE8TA/Davis et al. - 2017 - Simple process-led algorithms for simulating habit.pdf} + file = {/Users/je484/Zotero/storage/CDUHE8TA/Davis et al. - 2017 - Simple process-led algorithms for simulating habit.pdf} } @article{DeKauwe:2015im, @@ -244,7 +249,7 @@ @article{DeKauwe:2015im rating = {0}, read = {Yes}, uri = {papers3://publication/doi/10.5194/gmd-8-431-2015}, - file = {/Users/dorme/Zotero/storage/8WDYDVKQ/Geoscientific Model Development 2015 De Kauwe.pdf;/Users/dorme/Zotero/storage/WXI7ASHC/Geoscientific Model Development 2015 De Kauwe.pdf} + file = {/Users/je484/Zotero/storage/8WDYDVKQ/Geoscientific Model Development 2015 De Kauwe.pdf;/Users/je484/Zotero/storage/WXI7ASHC/Geoscientific Model Development 2015 De Kauwe.pdf} } @article{depury:1997a, @@ -257,7 +262,7 @@ @article{depury:1997a abstract = {In big-leaf models of canopy photosynthesis, the Rubisco activity per unit ground area is taken as the sum of activities per unit leaf area within the canopy, and electron transport capacity is similarly summed. Such models overestimate rates of photosynthesis and require empirical curvature factors in the response to irradiance. We show that, with any distribution of leaf nitrogen within the canopy (including optimal), the required curvature factors are not constant but vary with canopy leaf area index and leaf nitrogen content. We further show that the underlying reason is the difference between the time-averaged and instantaneous distributions of absorbed irradiance, caused by penetration of sunflecks and the range of leaf angles in canopies. These errors are avoided in models that treat the canopy in terms of a number of layers -- the multi-layer models. We present an alternative to the multi-layer model: by separately integrating the sunlit and shaded leaf fractions of the canopy, a single layered sun/shade model is obtained, which is as accurate and simpler. The model is a scaled version of a leaf model as distinct from an integrative approach.} } -@article{Diaz:2016by, +@article{diaz:2016a, title = {The Global Spectrum of Plant Form and Function}, author = {D{\'i}az, Sandra and Kattge, Jens and Cornelissen, Johannes H C and Wright, Ian J. and Lavorel, Sandra and Dray, St{\'e}phane and Reu, Bj{\"o}rn and Kleyer, Michael and Wirth, Christian and Prentice, I. Colin and Garnier, Eric and B{\"o}nisch, Gerhard and Westoby, Mark and Poorter, Hendrik and Reich, Peter B and Moles, Angela T and Dickie, John and Gillison, Andrew N and Zanne, Amy E and Chave, J{\'e}r{\^o}me and Wright, S Joseph and Sheremet'ev, Serge N and Jactel, Herv{\'e} and Baraloto, Christopher and Cerabolini, Bruno and Pierce, Simon and Shipley, Bill and Kirkup, Donald and Casanoves, Fernando and Joswig, Julia S and G{\"u}nther, Angela and Falczuk, Valeria and R{\"u}ger, Nadja and Mahecha, Miguel D and Gorn{\'e}, Lucas D}, year = {2016}, @@ -274,10 +279,10 @@ @article{Diaz:2016by local-url = {file://localhost/Users/dorme/References/Library.papers3/Articles/2016/D\%C3\%ADaz/Nature\%202016\%20D\%C3\%ADaz.pdf}, rating = {0}, uri = {papers3://publication/doi/10.1038/nature16489}, - file = {/Users/dorme/Zotero/storage/RGG46LIG/Nature 2016 Díaz.pdf} + file = {/Users/je484/Zotero/storage/RGG46LIG/Nature 2016 Díaz.pdf} } -@book{duffie:2013a, +@book{Duffie:2013a, title = {Solar {{Engineering}} of {{Thermal Processes}}}, author = {Duffie, John A. and Beckman, William A.}, year = {2013}, @@ -309,7 +314,7 @@ @article{Farquhar:1980ft pmid = {24306196}, rating = {0}, uri = {papers3://publication/doi/10.1007/BF00386231}, - file = {/Users/dorme/Zotero/storage/PGDMWNFK/Planta 1980 Farquhar.pdf;/Users/dorme/Zotero/storage/YNCRDMLJ/Planta 1980 Farquhar.pdf} + file = {/Users/je484/Zotero/storage/PGDMWNFK/Planta 1980 Farquhar.pdf;/Users/je484/Zotero/storage/YNCRDMLJ/Planta 1980 Farquhar.pdf} } @article{farquhar:1982a, @@ -325,7 +330,7 @@ @article{farquhar:1982a urldate = {2022-05-20}, abstract = {Theory is developed to explain the carbon isotopic composition of plants. It is shown how diffusion of gaseous COz can significantly affect carbon isotopic discrimination. The effects on discrimination by diffusion and carboxylation are integrated, yielding a simple relationship between discrimination and the ratio of the intercellular and atmospheric partial pressures of COZ. The effects of dark respiration and photorespiration are also considered, and it is suggested that they have relatively little effect on discrimination other than cia their effects on intercellular p(COz). It is also suggested that various environmental factors such as light, temperature, salinity and drought will also have effects via changes in intercellular p(C0,). A simple method is suggested for assessing water use efficiencies in the field.}, langid = {english}, - file = {/Users/dorme/Zotero/storage/8ACJPME5/Farquhar et al. - 1982 - On the Relationship Between Carbon Isotope Discrim.pdf} + file = {/Users/je484/Zotero/storage/8ACJPME5/Farquhar et al. - 1982 - On the Relationship Between Carbon Isotope Discrim.pdf} } @book{Fisher:1975tm, @@ -339,7 +344,7 @@ @book{Fisher:1975tm rating = {0}, read = {Yes}, uri = {papers3://publication/uuid/5D2C2C96-7975-4AB9-8314-A988EFA489FF}, - file = {/Users/dorme/Zotero/storage/3K2X7BU7/1975 Fisher.pdf} + file = {/Users/je484/Zotero/storage/3K2X7BU7/1975 Fisher.pdf} } @article{frank:2015a, @@ -354,7 +359,7 @@ @article{frank:2015a issn = {1758-6798}, doi = {10.1038/nclimate2614}, abstract = {Considering the combined effects of CO2 fertilization and climate change drivers on plant physiology leads to a modest increase in simulated European forest transpiration in spite of the effects of CO2-induced stomatal closure.}, - file = {/Users/dorme/Zotero/storage/CNDFZFBW/Frank et al. - 2015 - Water-use efficiency and transpiration across Euro.pdf} + file = {/Users/je484/Zotero/storage/CNDFZFBW/Frank et al. - 2015 - Water-use efficiency and transpiration across Euro.pdf} } @article{graven:2020a, @@ -370,10 +375,10 @@ @article{graven:2020a urldate = {2022-05-23}, abstract = {In this ``Grand Challenges'' paper, we review how the carbon isotopic composition of atmospheric CO2 has changed since the Industrial Revolution due to human activities and their influence on the natural carbon cycle, and we provide new estimates of possible future changes for a range of scenarios. Emissions of CO2 from fossil fuel combustion and land use change reduce the ratio of 13C/12C in atmospheric CO2 ({$\delta$}13CO2). This is because 12C is preferentially assimilated during photosynthesis and {$\delta$}13C in plant-derived carbon in terrestrial ecosystems and fossil fuels is lower than atmospheric {$\delta$}13CO2. Emissions of CO2 from fossil fuel combustion also reduce the ratio of 14C/C in atmospheric CO2 ({$\Delta$}14CO2) because 14C is absent in million-year-old fossil fuels, which have been stored for much longer than the radioactive decay time of 14C. Atmospheric {$\Delta$}14CO2 rapidly increased in the 1950s to 1960s because of 14C produced during nuclear bomb testing. The resulting trends in {$\delta$}13C and {$\Delta$}14C in atmospheric CO2 are influenced not only by these human emissions but also by natural carbon exchanges that mix carbon between the atmosphere and ocean and terrestrial ecosystems. This mixing caused {$\Delta$}14CO2 to return toward preindustrial levels in the first few decades after the spike from nuclear testing. More recently, as the bomb 14C excess is now mostly well mixed with the decadally overturning carbon reservoirs, fossil fuel emissions have become the main factor driving further decreases in atmospheric {$\Delta$}14CO2. For {$\delta$}13CO2, in addition to exchanges between reservoirs, the extent to which 12C is preferentially assimilated during photosynthesis appears to have increased, slowing down the recent {$\delta$}13CO2 trend slightly. A new compilation of ice core and flask {$\delta$}13CO2 observations indicates that the decline in {$\delta$}13CO2 since the preindustrial period is less than some prior estimates, which may have incorporated artifacts owing to offsets from different laboratories' measurements. Atmospheric observations of {$\delta$}13CO2 have been used to investigate carbon fluxes and the functioning of plants, and they are used for comparison with {$\delta$}13C in other materials such as tree rings. Atmospheric observations of {$\Delta$}14CO2 have been used to quantify the rate of air-sea gas exchange and ocean circulation, and the rate of net primary production and the turnover time of carbon in plant material and soils. Atmospheric observations of {$\Delta$}14CO2 are also used for comparison with {$\Delta$}14C in other materials in many fields such as archaeology, forensics, and physiology. Another major application is the assessment of regional emissions of CO2 from fossil fuel combustion using {$\Delta$}14CO2 observations and models. In the future, {$\delta$}13CO2 and {$\Delta$}14CO2 will continue to change. The sign and magnitude of the changes are mainly determined by global fossil fuel emissions. We present here simulations of future {$\delta$}13CO2 and {$\Delta$}14CO2 for six scenarios based on the shared socioeconomic pathways (SSPs) from the 6th Coupled Model Intercomparison Project (CMIP6). Applications using atmospheric {$\delta$}13CO2 and {$\Delta$}14CO2 observations in carbon cycle science and many other fields will be affected by these future changes. We recommend an increased effort toward making coordinated measurements of {$\delta$}13C and {$\Delta$}14C across the Earth System and for further development of isotopic modeling and model-data analysis tools.}, langid = {english}, - file = {/Users/dorme/Zotero/storage/BFT23X8N/Graven et al. - 2020 - Changes to Carbon Isotopes in Atmospheric CO .pdf} + file = {/Users/je484/Zotero/storage/BFT23X8N/Graven et al. - 2020 - Changes to Carbon Isotopes in Atmospheric CO .pdf} } -@book{HEllenbergBerlinGeobotanicalInstituteETH:fe, +@book{hellenberg-berlingeobotanicalinstituteetha, title = {A Key to {{Raunkiaer}} Plant Life Forms with Revised Subdivision.}, author = {{H Ellenberg - Berlin Geobotanical Institute ETH} and {Stiftung} and {1967}}, doi = {10.5169/seals-377651}, @@ -399,7 +404,7 @@ @article{henderson-sellers:1984a abstract = {Existing formulae and approximations for the latent heat of vaporization of water, Lv, are reviewed. Using an analytical approximation to the saturated vapour pressure as a function of temperature, a new, temperature-dependent function for Lv is derived.}, copyright = {Copyright {\copyright} 1984 Royal Meteorological Society}, langid = {english}, - file = {/Users/dorme/Zotero/storage/RHK394FU/qj.html} + file = {/Users/je484/Zotero/storage/RHK394FU/qj.html} } @article{hengl:2017a, @@ -419,7 +424,7 @@ @article{hengl:2017a abstract = {This paper describes the technical development and accuracy assessment of the most recent and improved version of the SoilGrids system at 250m resolution (June 2016 update). SoilGrids provides global predictions for standard numeric soil properties (organic carbon, bulk density, Cation Exchange Capacity (CEC), pH, soil texture fractions and coarse fragments) at seven standard depths (0, 5, 15, 30, 60, 100 and 200 cm), in addition to predictions of depth to bedrock and distribution of soil classes based on the World Reference Base (WRB) and USDA classification systems (ca. 280 raster layers in total). Predictions were based on ca. 150,000 soil profiles used for training and a stack of 158 remote sensing-based soil covariates (primarily derived from MODIS land products, SRTM DEM derivatives, climatic images and global landform and lithology maps), which were used to fit an ensemble of machine learning methods---random forest and gradient boosting and/or multinomial logistic regression---as implemented in the R packages ranger, xgboost, nnet and caret. The results of 10--fold cross-validation show that the ensemble models explain between 56\% (coarse fragments) and 83\% (pH) of variation with an overall average of 61\%. Improvements in the relative accuracy considering the amount of variation explained, in comparison to the previous version of SoilGrids at 1 km spatial resolution, range from 60 to 230\%. Improvements can be attributed to: (1) the use of machine learning instead of linear regression, (2) to considerable investments in preparing finer resolution covariate layers and (3) to insertion of additional soil profiles. Further development of SoilGrids could include refinement of methods to incorporate input uncertainties and derivation of posterior probability distributions (per pixel), and further automation of spatial modeling so that soil maps can be generated for potentially hundreds of soil variables. Another area of future research is the development of methods for multiscale merging of SoilGrids predictions with local and/or national gridded soil products (e.g. up to 50 m spatial resolution) so that increasingly more accurate, complete and consistent global soil information can be produced. SoilGrids are available under the Open Data Base License.}, langid = {english}, keywords = {Agricultural soil science,Forecasting,Glaciers,Machine learning,Remote sensing,Shannon index,Soil pH,Trees}, - file = {/Users/dorme/Zotero/storage/2YL6J66K/Hengl et al. - 2017 - SoilGrids250m Global gridded soil information bas.pdf} + file = {/Users/je484/Zotero/storage/2YL6J66K/Hengl et al. - 2017 - SoilGrids250m Global gridded soil information bas.pdf} } @article{Heskel:2016fg, @@ -438,7 +443,7 @@ @article{Heskel:2016fg local-url = {file://localhost/Users/dorme/References/Library.papers3/Articles/2016/Heskel/PNAS\%202016\%20Heskel.pdf}, rating = {0}, uri = {papers3://publication/doi/10.1073/pnas.1520282113}, - file = {/Users/dorme/Zotero/storage/22X4KLAX/PNAS 2016 Heskel.pdf;/Users/dorme/Zotero/storage/LC3HMVT7/PNAS 2016 Heskel.pdf} + file = {/Users/je484/Zotero/storage/22X4KLAX/PNAS 2016 Heskel.pdf;/Users/je484/Zotero/storage/LC3HMVT7/PNAS 2016 Heskel.pdf} } @article{Huber:2009fy, @@ -457,13 +462,14 @@ @article{Huber:2009fy local-url = {file://localhost/Users/dorme/References/Library.papers3/Articles/2009/Huber/Journal\%20of\%20Physical\%20and\%20Chemical\%20Reference\%20Data\%202009\%20Huber.pdf}, rating = {0}, uri = {papers3://publication/doi/10.1063/1.3088050}, - file = {/Users/dorme/Zotero/storage/HREIVFC4/Journal of Physical and Chemical Reference Data 2009 Huber.pdf} + file = {/Users/je484/Zotero/storage/HREIVFC4/Journal of Physical and Chemical Reference Data 2009 Huber.pdf} } @book{iqbal:1983a, title = {An Introduction to Solar Radiation}, author = {Iqbal, Mohammed}, year = {1983}, + publisher = {Academic Press}, langid = {english} } @@ -477,7 +483,7 @@ @techreport{joshi:2022a doi = {10.5194/egusphere-egu22-9994}, urldate = {2024-09-05}, langid = {english}, - file = {/Users/dorme/Zotero/storage/47RVAZGK/EGU22-9994.html} + file = {/Users/je484/Zotero/storage/47RVAZGK/EGU22-9994.html} } @article{Kattge:2007db, @@ -498,7 +504,7 @@ @article{Kattge:2007db rating = {0}, read = {Yes}, uri = {papers3://publication/doi/10.1111/j.1365-3040.2007.01690.x}, - file = {/Users/dorme/Zotero/storage/WCL92RHJ/Plant Cell & Environment 2007 Kattge.pdf} + file = {/Users/je484/Zotero/storage/WCL92RHJ/Plant Cell & Environment 2007 Kattge.pdf} } @article{kennedy:2019a, @@ -515,10 +521,10 @@ @article{kennedy:2019a urldate = {2022-07-21}, abstract = {Version 5 of the Community Land Model (CLM5) introduces the plant hydraulic stress (PHS) configuration of vegetation water use, which is described and compared with the corresponding parameterization from CLM4.5. PHS updates vegetation water stress and root water uptake to better reflect plant hydraulic theory, advancing the physical basis of the model. The new configuration introduces prognostic vegetation water potential, modeled at the root, stem, and leaf levels. Leaf water potential replaces soil potential as the basis for stomatal conductance water stress, and root water potential is used to implement hydraulic root water uptake, replacing a transpiration partitioning function. Point simulations of a tropical forest site (Caxiuan{\~a}, Brazil) under ambient conditions and partial precipitation exclusion highlight the differences between PHS and the previous CLM implementation. Model description and simulation results are contextualized with a list of benefits and limitations of the new model formulation, including hypotheses that were not testable in previous versions of the model. Key results include reductions in transpiration and soil moisture biases relative to a control model under both ambient and exclusion conditions, correcting excessive dry season soil moisture stress in the control model. PHS implements hydraulic gradient root water uptake, which allows hydraulic redistribution and compensatory root water uptake and results in PHS utilizing a larger portion of the soil column to buffer shortfalls in precipitation. The new model structure, which bases water stress on leaf water potential, could have significant implications for vegetation-climate feedbacks, including increased sensitivity of photosynthesis to atmospheric vapor pressure deficit.}, langid = {english}, - file = {/Users/dorme/Zotero/storage/X6XIHAEF/Kennedy et al. - 2019 - Implementing Plant Hydraulics in the Community Lan.pdf} + file = {/Users/je484/Zotero/storage/X6XIHAEF/Kennedy et al. - 2019 - Implementing Plant Hydraulics in the Community Lan.pdf} } -@phdthesis{Lancelot:2020tm, +@phdthesis{lancelot:2020a, title = {A Novel Computational Model to Predict Forest Dynamics}, author = {Lancelot, Maxime}, year = {2020}, @@ -529,7 +535,7 @@ @phdthesis{Lancelot:2020tm local-url = {file://localhost/Users/dorme/References/Library.papers3/Books/2020/Lancelot/2020\%20Lancelot.pdf}, rating = {0}, uri = {papers3://publication/uuid/D73D9C6F-36C9-461D-A90C-784809C52BCB}, - file = {/Users/dorme/Zotero/storage/86NBGKCA/2020 Lancelot.pdf} + file = {/Users/je484/Zotero/storage/86NBGKCA/2020 Lancelot.pdf} } @article{lavergne:2020a, @@ -547,7 +553,7 @@ @article{lavergne:2020a urldate = {2022-05-19}, abstract = {Atmospheric aridity and drought both influence physiological function in plant leaves, but their relative contributions to changes in the ratio of leaf internal to ambient partial pressure of CO2 ({$\chi$}) -- an index of adjustments in both stomatal conductance and photosynthetic rate to environmental conditions -- are difficult to disentangle. Many stomatal models predicting {$\chi$} include the influence of only one of these drivers. In particular, the least-cost optimality hypothesis considers the effect of atmospheric demand for water on {$\chi$} but does not predict how soils with reduced water further influence {$\chi$}, potentially leading to an overestimation of {$\chi$} under dry conditions. Here, we use a large network of stable carbon isotope measurements in C3 woody plants to examine the acclimated response of {$\chi$} to soil water stress. We estimate the ratio of cost factors for carboxylation and transpiration ({$\beta$}) expected from the theory to explain the variance in the data, and investigate the responses of {$\beta$} (and thus {$\chi$}) to soil water content and suction across seed plant groups, leaf phenological types and regions. Overall, {$\beta$} decreases linearly with soil drying, implying that the cost of water transport along the soil--plant--atmosphere continuum increases as water available in the soil decreases. However, despite contrasting hydraulic strategies, the stomatal responses of angiosperms and gymnosperms to soil water tend to converge, consistent with the optimality theory. The prediction of {$\beta$} as a simple, empirical function of soil water significantly improves {$\chi$} predictions by up to 6.3 {\textpm} 2.3\% (mean {\textpm} SD of adjusted-R2) over 1980--2018 and results in a reduction of around 2\% of mean {$\chi$} values across the globe. Our results highlight the importance of soil water status on stomatal functions and plant water-use efficiency, and suggest the implementation of trait-based hydraulic functions into the model to account for soil water stress.}, langid = {english}, - file = {/Users/dorme/Zotero/storage/ECFRUWX5/Lavergne et al. - 2020 - Impacts of soil water stress on the acclimated sto.pdf} + file = {/Users/je484/Zotero/storage/ECFRUWX5/Lavergne et al. - 2020 - Impacts of soil water stress on the acclimated sto.pdf} } @article{lavergne:2022a, @@ -555,7 +561,7 @@ @article{lavergne:2022a author = {Lavergne, Ali{\'e}nor and Harrison, Sandy P. and Atsawawaranunt, Kamolphat and Dong, Ning and Prentice, Iain Colin}, year = {2022}, journal = {Global Ecology and Biogeography (submitted)}, - file = {/Users/dorme/Zotero/storage/I797PRT2/Lavergneetal_GEB.docx} + file = {/Users/je484/Zotero/storage/I797PRT2/Lavergneetal_GEB.docx} } @article{Li:2014bc, @@ -574,7 +580,7 @@ @article{Li:2014bc rating = {0}, read = {Yes}, uri = {papers3://publication/doi/10.5194/bg-11-6711-2014}, - file = {/Users/dorme/Zotero/storage/HAR4D2NE/Biogeosciences 2014 Li.pdf} + file = {/Users/je484/Zotero/storage/HAR4D2NE/Biogeosciences 2014 Li.pdf} } @article{Lin:2015wh, @@ -591,10 +597,10 @@ @article{Lin:2015wh local-url = {file://localhost/Users/dorme/References/Library.papers3/Articles/2015/Lin/2015\%20Lin-2.pdf}, rating = {0}, uri = {papers3://publication/uuid/61D6E8CB-F257-4021-AC24-2DF4D72C298E}, - file = {/Users/dorme/Zotero/storage/J7WANJNI/2015 Lin-2.pdf} + file = {/Users/je484/Zotero/storage/J7WANJNI/2015 Lin-2.pdf} } -@article{linacre:1968a, +@article{Linacre:1968a, title = {Estimating the Net-Radiation Flux}, author = {Linacre, E. T.}, year = {1968}, @@ -607,7 +613,7 @@ @article{linacre:1968a doi = {10.1016/0002-1571(68)90022-8}, urldate = {2024-08-02}, abstract = {A major influence controlling the water loss from irrigated crops is the net-radiation intensity Qn, but measurements of this are not normally available, and so attempts are often made to deduce it from other climatic data, such as the solar-radiation. Here it is shown that the relationship between net and solar-radiation intensities depends on the degree of cloudiness and the ambient temperature. By making appropriate assumptions, a series of expressions for Qn is derived, with decreasing accuracy but increasing simplicity of estimation. It appears that clouds lower the net-radiation intensity when it exceeds a critical value in the region of 0.02 cal./cm2 min, but increase it when the intensity is lower.}, - file = {/Users/dorme/Zotero/storage/UY4NRV4W/0002157168900228.html} + file = {/Users/je484/Zotero/storage/UY4NRV4W/0002157168900228.html} } @article{long:1993a, @@ -624,8 +630,8 @@ @article{long:1993a urldate = {2024-07-19}, abstract = {The maximum quantum yields ({$\phi$}a,c) for CO2 uptake in low-oxygen atmospheres were determined for 11 species of C3 vascular plants of diverse taxa, habitat and life form using an Ulbricht-sphere leaf chamber. Comparisons were also made between tissues of varied age within species. The species examined were Psilotum nudum (L.) P. Beauv., Davallia bullata Wall. ex Hook., Cycas revoluta Thunb., Araucaria heterophylla (Salisb.) Franco, Picea abies (L.) Karst., Nerium oleander L., Ruellia humilis Nutt., Pilea microphylla (L.) Karst., Beaucarnea stricta Lem., Oplismenus hirtellus (L.) P. Beauv. and Poa annua L. Quantum yields were calculated from the initial slopes of the response of CO2 uptake to the quantity of photons absorbed in conditions of diffuse lighting. Regression analysis of variance of the initial slopes of the response of CO2 uptake to photon absorption failed to show any statistically significant differences between age classes within species or between the mature photosynthetic organs of different species. The constancy of {$\phi$}a,c was apparent despite marked variation in the light-saturated rates of CO2 uptake within and between species. The mean {$\phi$}a,c was 0.093{\textpm}0.003 for 11 species. By contrast, surface absorptance varied markedly between species from 0.90 to 0.60, producing proportional variation in the quantum yield calculated on an incidentlight basis. The ratio of variable to maximum fluorescence emission at 695 nm for the same tissues also failed to show any statistically significant variation between species, with a mean of 0.838{\textpm}0.008. Mean values of {$\phi$}a,c reported here for C3 species, in the absence of photorespiration, are higher than reported in previous surveys of vascular plants, but consistent with recent estimates of the quantum yields of O2 evolution.}, langid = {english}, - keywords = {C3 plant,Photosynthesis,Quantum yield (O2 CO2 uptake)}, - file = {/Users/dorme/Zotero/storage/88PTVDYD/Long et al. - 1993 - Quantum yields for uptake of carbon dioxide in C3 .pdf} + keywords = {C3 plant,CO2 uptake),Photosynthesis,Quantum yield (O2,Quantum yield (O2 CO2 uptake)}, + file = {/Users/je484/Zotero/storage/88PTVDYD/Long et al. - 1993 - Quantum yields for uptake of carbon dioxide in C3 .pdf} } @article{mengoli:2022a, @@ -642,7 +648,7 @@ @article{mengoli:2022a urldate = {2022-05-26}, abstract = {Vegetation regulates land-atmosphere, water, and energy exchanges and is an essential component of land-surface models (LSMs). However, LSMs have been handicapped by assumptions that equate acclimated photosynthetic responses to the environment with the fast responses observable in the laboratory. The effects of acclimation can be taken into account by including PFT-specific values of photosynthetic parameters, but at the cost of increasing parameter requirements. Here, we develop an alternative approach for including acclimation in LSMs by adopting the P model, an existing light-use efficiency model for gross primary production (GPP) that implicitly predicts the acclimation of photosynthetic parameters on a weekly to monthly timescale via optimality principles. We demonstrate that it is possible to explicitly separate the fast and slow photosynthetic responses to environmental conditions, allowing the simulation of GPP at the sub-daily timesteps required for coupling in an LSM. The resulting model reproduces the diurnal cycles of GPP recorded by eddy-covariance flux towers in a temperate grassland and boreal, temperate and tropical forests. The best performance is achieved when biochemical capacities are adjusted to match recent midday conditions. Comparison between this model and the operational LSM in the European Centre for Medium-range Weather Forecasts climate model shows that the new model has better predictive power in most of the sites and years analyzed, particularly in summer and autumn. Our analyses suggest a simple and parameter-sparse method to include both instantaneous and acclimated responses within an LSM framework, with potential applications in weather, climate, and carbon-cycle modeling.}, langid = {english}, - file = {/Users/dorme/Zotero/storage/KAFPVD6H/2021ms002767-sup-0002-supporting information si-s02.pdf;/Users/dorme/Zotero/storage/VU8B6HP8/Mengoli et al. - 2022 - Ecosystem Photosynthesis in Land‐Surface Models A.pdf;/Users/dorme/Zotero/storage/ZQBUWXEK/2021ms002767-sup-0001-supporting information si-s01.pdf} + file = {/Users/je484/Zotero/storage/KAFPVD6H/2021ms002767-sup-0002-supporting information si-s02.pdf;/Users/je484/Zotero/storage/VU8B6HP8/Mengoli et al. - 2022 - Ecosystem Photosynthesis in Land‐Surface Models A.pdf;/Users/je484/Zotero/storage/ZQBUWXEK/2021ms002767-sup-0001-supporting information si-s01.pdf} } @article{mengoli:2023a, @@ -657,10 +663,10 @@ @article{mengoli:2023a urldate = {2023-07-03}, abstract = {{$<$}p{$><$}strong class="journal-contentHeaderColor"{$>$}Abstract.{$<$}/strong{$>$} The coupling between carbon uptake and water loss through stomata implies that gross primary production (GPP) can be limited by soil water availability through reduced leaf area and/or reduced stomatal conductance. Vegetation and land-surface models typically assume that GPP is highest under well-watered conditions and apply a stress function to reduce GPP with declining soil moisture below a critical threshold, which may be universal or prescribed by vegetation type. It is unclear how well current schemes represent the water conservation strategies of plants in different climates. Here eddy-covariance flux data are used to investigate empirically how soil moisture influences the light-use efficiency (LUE) of GPP. Well-watered GPP is estimated using the P model, a first-principles LUE model driven by atmospheric data and remotely sensed green vegetation cover. Breakpoint regression is used to relate the daily value of the ratio \β(\θ) (flux-derived GPP/modelled well-watered GPP) to soil moisture, which is estimated using a generic water-balance model. Maximum LUE, even during wetter periods, is shown to decline with increasing climatic aridity index (AI). The critical soil-moisture threshold also declines with AI. Moreover, for any AI, there is a value of soil moisture at which \β(\θ) is maximized, and this value declines with increasing AI. Thus, ecosystems adapted to seasonally dry conditions use water more conservatively (relative to well-watered ecosystems) when soil moisture is high, but maintain higher GPP when soil moisture is low. An empirical non-linear function of AI expressing these relationships is derived by non-linear regression, and used to generate a \β(\θ) function that provides a multiplier for well-watered GPP as simulated by the P model. Substantially improved GPP simulation is shown during both unstressed and water-stressed conditions, compared to the reference model version that ignores soil-moisture stress, and to an earlier formulation in which maximum LUE was not reduced. This scheme may provide a step towards better-founded representations of carbon-water cycle coupling in vegetation and land-surface models.{$<$}/p{$>$}}, langid = {english}, - file = {/Users/dorme/Zotero/storage/GMFKIMVQ/Mengoli et al. - 2023 - A global function of climatic aridity accounts for.pdf} + file = {/Users/je484/Zotero/storage/GMFKIMVQ/Mengoli et al. - 2023 - A global function of climatic aridity accounts for.pdf} } -@article{Moore:2018dv, +@article{moore:2018a, title = {Equilibrium Forest Demography Explains the Distribution of Tree Sizes across {{North America}}}, author = {Moore, Jonathan R and Zhu, Kai and Huntingford, Chris and Cox, Peter M}, year = {2018}, @@ -677,7 +683,7 @@ @article{Moore:2018dv local-url = {file://localhost/Users/dorme/References/Library.papers3/Articles/2018/Moore/Environ\%20Res\%20Lett\%202018\%20Moore.pdf}, rating = {0}, uri = {papers3://publication/doi/10.1088/1748-9326/aad6d1}, - file = {/Users/dorme/Zotero/storage/9Z93DUPX/Environ Res Lett 2018 Moore.pdf} + file = {/Users/je484/Zotero/storage/9Z93DUPX/Environ Res Lett 2018 Moore.pdf} } @article{murphy:2021a, @@ -695,10 +701,10 @@ @article{murphy:2021a copyright = {{\copyright} 2020 The Authors New Phytologist {\copyright} 2020 New Phytologist Trust}, langid = {english}, keywords = {Arrhenius,carbon balance,gas exchange,modelling,photosynthesis,temperature}, - file = {/Users/dorme/Zotero/storage/NZTU5A6H/Murphy and Stinziano - 2021 - A derivation error that affects carbon balance mod.pdf;/Users/dorme/Zotero/storage/5Y2669L6/nph.html} + file = {/Users/je484/Zotero/storage/NZTU5A6H/Murphy and Stinziano - 2021 - A derivation error that affects carbon balance mod.pdf;/Users/je484/Zotero/storage/5Y2669L6/nph.html} } -@article{Peng:2021ky, +@article{peng:2021a, title = {Global Climate and Nutrient Controls of Photosynthetic Capacity}, author = {Peng, Yunke and Bloomfield, Keith J and Cernusak, Lucas A and Domingues, Tomas F and Prentice, I. Colin}, year = {2021}, @@ -713,7 +719,7 @@ @article{Peng:2021ky local-url = {file://localhost/Users/dorme/References/Library.papers3/Articles/2021/Peng/Communications\%20Biology\%202021\%20Peng.pdf}, rating = {0}, uri = {papers3://publication/doi/10.1038/s42003-021-01985-7}, - file = {/Users/dorme/Zotero/storage/ZPIDVNUB/Communications Biology 2021 Peng.pdf} + file = {/Users/je484/Zotero/storage/ZPIDVNUB/Communications Biology 2021 Peng.pdf} } @article{Prentice:2014bc, @@ -732,7 +738,7 @@ @article{Prentice:2014bc rating = {0}, read = {Yes}, uri = {papers3://publication/doi/10.1111/ele.12211}, - file = {/Users/dorme/Zotero/storage/MDRBDVTF/Ecol Lett 2014 Prentice.pdf} + file = {/Users/je484/Zotero/storage/MDRBDVTF/Ecol Lett 2014 Prentice.pdf} } @article{purves:2008a, @@ -749,7 +755,7 @@ @article{purves:2008a urldate = {2022-06-20}, abstract = {The perfect-plasticity approximation (PPA) is an analytically tractable model of forest dynamics, defined in terms of parameters for individual trees, including allometry, growth, and mortality. We estimated these parameters for the eight most common species on each of four soil types in the US Lake states (Michigan, Wisconsin, and Minnesota) by using short-term ({$\leq$}15-year) inventory data from individual trees. We implemented 100-year PPA simulations given these parameters and compared these predictions to chronosequences of stand development. Predictions for the timing and magnitude of basal area dynamics and ecological succession on each soil were accurate, and predictions for the diameter distribution of 100-year-old stands were correct in form and slope. For a given species, the PPA provides analytical metrics for early-successional performance ( H 20 , height of a 20-year-old open-grown tree) and late-successional performance ( {\^Z} *, equilibrium canopy height in monoculture). These metrics predicted which species were early or late successional on each soil type. Decomposing {\^Z} * showed that ( i ) succession is driven both by superior understory performance and superior canopy performance of late-successional species, and ( ii ) performance differences primarily reflect differences in mortality rather than growth. The predicted late-successional dominants matched chronosequences on xeromesic ( Quercus rubra ) and mesic (codominance by Acer rubrum and Acer saccharum ) soil. On hydromesic and hydric soils, the literature reports that the current dominant species in old stands ( Thuja occidentalis ) is now failing to regenerate. Consistent with this, the PPA predicted that, on these soils, stands are now succeeding to dominance by other late-successional species (e.g., Fraxinus nigra , A. rubrum ).}, langid = {english}, - file = {/Users/dorme/Zotero/storage/KI43FP4H/Purves et al. - 2008 - Predicting and understanding forest dynamics using.pdf} + file = {/Users/je484/Zotero/storage/KI43FP4H/Purves et al. - 2008 - Predicting and understanding forest dynamics using.pdf} } @article{sandoval:in_prep, @@ -778,7 +784,14 @@ @article{Smith:2019dv rating = {0}, read = {Yes}, uri = {papers3://publication/doi/10.1111/ele.13210}, - file = {/Users/dorme/Zotero/storage/EIWDBL6T/Ecol Lett 2019 Smith.pdf} + file = {/Users/je484/Zotero/storage/EIWDBL6T/Ecol Lett 2019 Smith.pdf} +} + +@misc{stine_geyer:2001, + title = {Power from the Sun}, + author = {Stine, William and Geyer, Michael}, + journal = {Power from the sun.net}, + howpublished = {http://www.powerfromthesun.net/book.html} } @article{Stocker:2018be, @@ -798,7 +811,7 @@ @article{Stocker:2018be local-url = {file://localhost/Users/dorme/References/Library.papers3/Articles/2018/Stocker/New\%20Phytol.\%202018\%20Stocker.pdf}, rating = {0}, uri = {papers3://publication/doi/10.1111/nph.15123}, - file = {/Users/dorme/Zotero/storage/E6PPD38Z/New Phytol. 2018 Stocker.pdf} + file = {/Users/je484/Zotero/storage/E6PPD38Z/New Phytol. 2018 Stocker.pdf} } @article{Stocker:2020dh, @@ -817,10 +830,10 @@ @article{Stocker:2020dh rating = {0}, read = {Yes}, uri = {papers3://publication/doi/10.5194/gmd-13-1545-2020}, - file = {/Users/dorme/Zotero/storage/J4YM5X2R/Geoscientific Model Development 2020 Stocker.pdf} + file = {/Users/je484/Zotero/storage/J4YM5X2R/Geoscientific Model Development 2020 Stocker.pdf} } -@article{Togashi:2018es, +@article{togashi:2018a, title = {Functional Trait Variation Related to Gap Dynamics in Tropical Moist forests{{{\textsubscript{A}}}} Vegetation Modelling Perspective}, author = {Togashi, Henrique F{\"u}rstenau and Atkin, Owen K and Bloomfield, Keith J and Bradford, Matt and Cao, Kunfang and Dong, Ning and Evans, Bradley J and Fan, Zexin and Harrison, Sandy P and Hua, Zhu and Liddell, Michael J and Lloyd, Jon and Ni, Jian and Wang, Han and Weerasinghe, Lasantha K and Prentice, Iain Colin}, year = {2018}, @@ -837,7 +850,7 @@ @article{Togashi:2018es rating = {0}, read = {Yes}, uri = {papers3://publication/doi/10.1016/j.ppees.2018.10.004}, - file = {/Users/dorme/Zotero/storage/L4HJJSBU/Perspectives in Plant Ecology Evolution and Systematics 2018 Togashi.pdf} + file = {/Users/je484/Zotero/storage/L4HJJSBU/Perspectives in Plant Ecology Evolution and Systematics 2018 Togashi.pdf} } @article{tsilingiris:2008a, @@ -855,7 +868,7 @@ @article{tsilingiris:2008a abstract = {The aim of the present investigation is evaluation of the thermophysical and transport properties of moist air as a function of mixture temperature with relative humidity as a parameter, ranging between dry air and saturation conditions. Based on a literature review of the most widely available analytical procedures and methods, a number of developed correlations are presented, which are employed with recent gas mixture component properties as input parameters, to derive the temperature and humidity dependence of mixture density, viscosity, specific heat capacity, thermal conductivity, thermal diffusivity and Prandtl number under conditions corresponding to the total barometric pressure of 101.3kPa. The derived results at an accuracy level suitable for engineering calculations were plotted and compared with adequate accuracy with existing results from previous analytical calculations and measured data from earlier experimental investigations. The saturated mixture properties were also appropriately fitted, and the fitting expressions suitable for computer calculations are also presented.}, langid = {english}, keywords = {Density,Prandtl number,Specific heat capacity,Thermal conductivity,Thermal diffusivity,Thermophysical properties,Viscosity}, - file = {/Users/dorme/Zotero/storage/CUISZDZT/Tsilingiris - 2008 - Thermophysical and transport properties of humid a.pdf;/Users/dorme/Zotero/storage/RFS82MFU/S0196890407003329.html} + file = {/Users/je484/Zotero/storage/CUISZDZT/Tsilingiris - 2008 - Thermophysical and transport properties of humid a.pdf;/Users/je484/Zotero/storage/RFS82MFU/S0196890407003329.html} } @article{voncaemmerer:2014a, @@ -872,10 +885,10 @@ @article{voncaemmerer:2014a urldate = {2022-05-20}, abstract = {Photosynthetic carbon isotope discrimination is a non-destructive tool for investigating C4 metabolism. Tuneable diode laser absorption spectroscopy provides new opportunities for making rapid, concurrent measurements of carbon isotope discrimination and CO2 assimilation over a range of environmental conditions, and this has facilitated the use of carbon isotope discrimination as a probe of C4 metabolism. In spite of the significant progress made in recent years, understanding how photosynthetic carbon isotope discrimination measured concurrently with gas exchange relates to carbon isotope composition of leaf and plant dry matter remains a challenge that requires resolution if this technique is to be successfully applied as a screening tool in crop breeding and phylogenetic research. In this review, we update our understanding of the factors and assumptions that underlie variations in photosynthetic carbon isotope discrimination in C4 leaves. Closing the main gaps in our understanding of carbon isotope discrimination during C4 photosynthesis may help advance research aimed at developing higher productivity and efficiency in key C4 food, feed, and biofuel crops.}, langid = {english}, - file = {/Users/dorme/Zotero/storage/DBGBCVUB/Caemmerer et al. - 2014 - Carbon isotope discrimination as a tool to explore.pdf} + file = {/Users/je484/Zotero/storage/DBGBCVUB/Caemmerer et al. - 2014 - Carbon isotope discrimination as a tool to explore.pdf} } -@article{Walker:2014ce, +@article{walker:2014a, title = {The Relationship of Leaf Photosynthetic Traits - {{Vcmaxand Jmax-}} to Leaf Nitrogen, Leaf Phosphorus, and Specific Leaf Area: A Meta-Analysis and Modeling Study}, author = {Walker, Anthony P and Beckerman, Andrew P and Gu, Lianhong and Kattge, Jens and Cernusak, Lucas A and Domingues, Tomas F and Scales, Joanna C and Wohlfahrt, Georg and Wullschleger, Stan D and Woodward, F Ian}, year = {2014}, @@ -891,7 +904,7 @@ @article{Walker:2014ce local-url = {file://localhost/Users/dorme/References/Library.papers3/Articles/2014/Walker/Ecology\%20and\%20Evolution\%202014\%20Walker.pdf}, rating = {0}, uri = {papers3://publication/doi/10.1002/ece3.1173}, - file = {/Users/dorme/Zotero/storage/N8LKZ4EP/Ecology and Evolution 2014 Walker.pdf} + file = {/Users/je484/Zotero/storage/N8LKZ4EP/Ecology and Evolution 2014 Walker.pdf} } @article{Wang:2017go, @@ -910,10 +923,10 @@ @article{Wang:2017go rating = {0}, read = {Yes}, uri = {papers3://publication/doi/10.1038/s41477-017-0006-8}, - file = {/Users/dorme/Zotero/storage/D4RYI3NP/Nature Plants 2017 Wang.pdf} + file = {/Users/je484/Zotero/storage/D4RYI3NP/Nature Plants 2017 Wang.pdf} } -@article{Wang:2020ik, +@article{Wang:2020a, title = {Acclimation of Leaf Respiration Consistent with Optimal Photosynthetic Capacity}, author = {Wang, Han and Atkin, Owen K and Keenan, Trevor F and Smith, Nicholas G and Wright, Ian J. and Bloomfield, Keith J and Kattge, Jens and Reich, Peter B and Prentice, I. Colin}, year = {2020}, @@ -930,20 +943,13 @@ @article{Wang:2020ik rating = {0}, read = {Yes}, uri = {papers3://publication/doi/10.1111/gcb.14980}, - file = {/Users/dorme/Zotero/storage/2XYW4BF4/gcb15314-sup-0004-Supinfo.pdf;/Users/dorme/Zotero/storage/RAGU56IZ/Global Change Biol 2020 Wang.pdf} + file = {/Users/je484/Zotero/storage/2XYW4BF4/gcb15314-sup-0004-Supinfo.pdf;/Users/je484/Zotero/storage/RAGU56IZ/Global Change Biol 2020 Wang.pdf} } -@book{woolf:1968a, +@book{Woolf:1968, title = {On the {{Computation}} of {{Solar Elevation Angles}} and the {{Determination}} of {{Sunrise}} and {{Sunset Times}}}, author = {Woolf, Harold M.}, year = {1968}, publisher = {{National Aeronautics and Space Administration}}, langid = {english} } - -@misc{zotero-2443a, - title = {Crown.Md\#cro{\dots} - {{JupyterLab}}}, - urldate = {2024-09-30}, - howpublished = {http://localhost:8888/lab/tree/source/users/demography/crown.md\#crown-radius-values}, - file = {/Users/dorme/Zotero/storage/LHHVRPS9/crown.html} -} diff --git a/pyrealm/constants/core_const.py b/pyrealm/constants/core_const.py index 935d5cfb..b9f38463 100644 --- a/pyrealm/constants/core_const.py +++ b/pyrealm/constants/core_const.py @@ -98,6 +98,7 @@ class CoreConst(ConstantsClass): k_d = 0.50 # angular coefficient of transmittivity (Linacre, 1968) k_fFEC = 2.04 # from flux to energy conversion, umol/J (Meek et al., 1984) k_Gsc = 1360.8 # solar constant, W/m^2 (Kopp & Lean, 2011) + k_secs_d = 86400 # seconds in one solar day # Paleoclimate variables: ke = 0.0167 # eccentricity for 2000 CE (Berger, 1978) diff --git a/pyrealm/core/calendar.py b/pyrealm/core/calendar.py index e2d4a587..1a4fecd8 100644 --- a/pyrealm/core/calendar.py +++ b/pyrealm/core/calendar.py @@ -118,3 +118,90 @@ def __repr__(self) -> str: """Representation of a Calendar instance.""" return f"Calendar({self.dates[0]!s}, {self.dates[-1]!s})" + + +@dataclass +class LocationDateTime: + """A data class representing an observation location and date and time information. + + This class encapsulates the latitude and longitude of a location along with a + corresponding time array. It automatically calculates the latitude and longitude in + radians, the Julian days from the date-time information, and a decimal + representation of the local time. + + Example: + >>> import numpy as np + >>> ldt = LocationDateTime( + ... latitude=-35.058333, + ... longitude=147.34167, + ... year_date_time=np.array([np.datetime64("2024-08-12T10:30")]), + ... ) + >>> print(ldt.latitude_rad) + -0.6118833411105811 + >>> print(ldt.decimal_time) + [10.5] + >>> print(ldt.local_standard_meridian) + 150 + """ + + latitude: float + """The latitude of the location in degrees.""" + latitude_rad: float = field(init=False) + """The latitude of the location in radians, calculated automatically.""" + longitude: float + """The longitude of the location in degrees.""" + longitude_rad: float = field(init=False) + """The longitude of the location in radians, calculated automatically.""" + year_date_time: np.ndarray + """An array of np.datetime64 values corresponding to observations at the + location (local time).""" + julian_days: np.ndarray = field(init=False) + """An array of Julian day of the year numbers calculated from the + ``year_date_time``.""" + decimal_time: np.ndarray = field(init=False) + """An array of decimal hour values calculated from local ``year_date_time``.""" + local_standard_meridian: int = field(init=False) + """An int describing time offset from local meridian to Greenwich meridian + in hours.""" + + def __post_init__(self) -> None: + """Initialise calculated attributes. + + Initializes calculated attributes like ``latitude_rad``, ``longitude_rad``, + ``julian_days``, and ``local_time`` after the object is instantiated. + """ + + self.julian_days = Calendar(self.year_date_time).julian_day + self.decimal_time = self.decimal_hour() + self.latitude_rad = self.latitude * np.pi / 180 + self.longitude_rad = self.longitude * np.pi / 180 + self.local_standard_meridian = self.get_local_standard_meridian() + + def decimal_hour(self) -> np.ndarray: + """Convert ``year_date_time`` to a decimal representation of hours. + + This method extracts the hours and minutes from the `year_date_time` attribute + and converts them into a decimal representation of hours. + + Returns: + An array of decimal hour values. + """ + + # Extract hours + hours = self.year_date_time.astype("datetime64[h]").astype(int) % 24 + + # Extract minutes + minutes = self.year_date_time.astype("datetime64[m]").astype(int) % 60 + + # Convert to decimal hours + return hours + minutes / 60 + + def get_local_standard_meridian(self) -> int: + """Calculates local meridian from longitude. + + Returns: + An integer in degrees format representing local meridian offset from + Greenwich. + """ + + return 30 * round(self.longitude / 30) diff --git a/pyrealm/core/solar.py b/pyrealm/core/solar.py index 0f7ea2ec..5d566530 100644 --- a/pyrealm/core/solar.py +++ b/pyrealm/core/solar.py @@ -1,26 +1,604 @@ -"""The ``solar`` submodule provides functions and classes to calculate daily solar -radiation fluxes and other radiative values. +"""The :mod:`~pyrealm.core.solar` submodule provides functions to calculate +photosynthetic photon flux density (ppfd), daily solar radiation fluxes and other +radiative values. """ # noqa: D205 import numpy as np from numpy.typing import NDArray from pyrealm.constants import CoreConst +from pyrealm.core.calendar import LocationDateTime +from pyrealm.core.utilities import check_input_shapes -# from pyrealm.splash.const import ( -# kA, -# kalb_sw, -# kalb_vis, -# kb, -# kc, -# kd, -# ke, -# keps, -# kfFEC, -# kGsc, -# komega, -# pir, -# ) + +def calc_distance_factor(nu: NDArray, k_e: float) -> NDArray: + r"""Calculates distance factor. + + This function calculates the distance factor :math:`dr` using the method of + :cite:t:`berger:1993a`. + + .. math:: + + dr = \left( 1.0 \mathbin{/} + \left(\frac{1.0 - k_{e}^2} + {1.0 + k_{e} \cos\left(\nu \cdot \pi \mathbin{/} + 180)\right)} + \right) + \right)^2 + + Args: + nu: Heliocentric true anomaly (degrees) + k_e: Solar eccentricity + + Returns: + An array of distance factors + """ + + dr = (1.0 / ((1.0 - k_e**2) / (1.0 + k_e * np.cos(np.deg2rad(nu))))) ** 2 + + return dr + + +def calc_declination_angle_delta( + lambda_: NDArray, k_eps: float, k_pir: float +) -> NDArray: + r"""Calculates declination angle delta. + + This function calculates the solar declination angle delta using the method of + :cite:t:`Woolf:1968`. + + .. math:: + + \delta = \frac{\arcsin(\sin(\deg2rad(\lambda_)) + \cdot \sin(\deg2rad(k_{eps})))}{k_{pir}} + + Args: + lambda_: heliocentric longitude + k_eps: Solar obliquity + k_pir: conversion factor from radians to degrees + + Returns: + An array of solar declination angle delta + """ + + delta = np.arcsin(np.sin(np.deg2rad(lambda_)) * np.sin(np.deg2rad(k_eps))) / k_pir + + return delta + + +def calc_lat_delta_intermediates( + delta: NDArray, latitude: NDArray +) -> tuple[NDArray, NDArray]: + r"""Calculates intermediate values for use in solar radiation calcs. + + This function calculates :math:`ru` and :math:`rv` which are dimensionless + intermediate values calculated from the solar declination angle delta and the + observation latitude. + + .. math:: + + ru = \sin(\deg2rad(\delta)) \cdot \sin(\deg2rad(\text{latitude})) + + .. math:: + + rv = \cos(\deg2rad(\delta)) \cdot \cos(\deg2rad(\text{latitude})) + + Args: + delta: solar declination delta + latitude: observation latitude (degrees) + + Returns: + A Tuple of :math:`ru` and :math:`rv`, calculation intermediates, unitless + + """ + ru = np.sin(np.deg2rad(delta)) * np.sin(np.deg2rad(latitude)) + rv = np.cos(np.deg2rad(delta)) * np.cos(np.deg2rad(latitude)) + + return ru, rv + + +def calc_sunset_hour_angle(delta: NDArray, latitude: NDArray, k_pir: float) -> NDArray: + r"""Calculates sunset hour angle. + + This function calculates the sunset hour angle :math:`hs` using eq3.22 + :cite:t:`stine_geyer:2001`. + + .. math:: + + hs = \frac{\arccos(-1.0 \cdot \text{clip}(\frac{ru}{rv}, -1.0, + 1.0))}{k_{pir}} + + Args: + delta: solar declination delta + latitude: site latitude(s) + k_pir: constant rad to degrees conversion, degrees/rad + + Returns: + An array of local hour angle, degrees + """ + + ru, rv = calc_lat_delta_intermediates(delta=delta, latitude=latitude) + + return _calc_sunset_hour_angle_from_ru_rv(ru, rv, k_pir) + + +def _calc_sunset_hour_angle_from_ru_rv( + ru: NDArray, rv: NDArray, k_pir: float +) -> NDArray: + """Calculate sunset hour angle from intermediates. + + This function calculates the sunset hour angle using Eq3.22, + :cite:t:'stine_geyer:2001'. + + Args: + ru: dimensionless parameter + rv: dimensionless parameter + k_pir: constant rad to degrees conversion, degrees/rad + + Returns: + An array of local hour angle, degrees + """ + + return np.arccos(-1.0 * np.clip(ru / rv, -1.0, 1.0)) / k_pir + + +def calc_daily_solar_radiation( + dr: NDArray, hs: NDArray, delta: NDArray, latitude: NDArray, const: CoreConst +) -> NDArray: + r"""Calculate daily extraterrestrial solar radiation (J/m^2). + + This function calculates the daily extraterrestrial solar radition :math:`J/m^2` + using Eq. 1.10.3 :cite:t:`Duffie:2013a`. + + .. math:: + + ra\_d = \left( \frac{secs_{d}}{\pi} \right) \cdot rad\_const \cdot dr + \cdot \left(ru \cdot k_{pir} \cdot hs + rv \cdot + \sin(\deg2rad(hs))\right) + + Args: + rad_const: planetary radiation constant, W/m^2 + dr: dimensionless distance factor + hs: local hour angle, degrees + delta: solar declination delta + latitude: site latitude(s) + const: CoreConst object containing core constants: + + Returns: + NDArray: An array of daily solar radiation, J/m^2 + """ + + ru, rv = calc_lat_delta_intermediates(delta=delta, latitude=latitude) + + return _calc_daily_solar_radiation(dr=dr, ru=ru, rv=rv, hs=hs, const=const) + + +def _calc_daily_solar_radiation( + dr: NDArray, ru: NDArray, rv: NDArray, hs: NDArray, const: CoreConst +) -> NDArray: + """Calculate daily extraterrestrial solar radiation (J/m^2). + + This function calculates the daily extraterrestrial solar radition (J/m^2) using + Eq. 1.10.3, :cite:t:`Duffie:2013a`. + + Args: + dr: dimensionless distance factor + ru: dimensionless variable substitute + rv: dimensionless variable substitute + hs: local hour angle, degrees + const: CoreConst object + + Returns: + An array of daily solar radiation, J/m^2 + """ + k_pir = const.k_pir + k_secs_d = const.k_secs_d + k_Gsc = const.k_Gsc + + ra_d = ( + (k_secs_d / np.pi) + * k_Gsc + * dr + * (ru * k_pir * hs + rv * np.sin(np.deg2rad(hs))) + ) + + return ra_d + + +def calc_transmissivity(sf: NDArray, elv: NDArray, k_c: float, k_d: float) -> NDArray: + r"""Calculate atmospheric transmissivity, :math:`tau`. + + This function calculates atmospheric transmissivity using the method of Eq.11, + :cite:t:`Linacre:1968a` and Eq 2, :cite:t:`allen:1996a`. + + .. math:: + + \tau = (k_{c} + k_{d} \cdot sf) \cdot (1.0 + (2.67 \times 10^{-5}) \cdot elv) + + Args: + sf: Daily sunshine fraction of observations, unitless + elv: Elevation of observations, metres + k_c: dimensionless cloudy transmissivity + k_d: dimensionless angular coefficient of transmissivity + + Returns: + An array of bulk transmissivity, unitless + """ + + tau = (k_c + k_d * sf) * (1.0 + (2.67e-5) * elv) + + return tau + + +def calc_ppfd_from_tau_ra_d( + tau: NDArray, ra_d: NDArray, k_fFEC: float, k_alb_vis: float +) -> NDArray: + r"""Calculate photosynthetic photon flux density, :math:`PPFD`,(mol/m^2). + + This function calculates the :math:`PPFD` in :math:`mol/m^2` from secondary + calculated variables, and constants. + + .. math:: + + ppfd = (1.0 \times 10^{-6}) \cdot k_{fFEC} \cdot (1.0 - k_{alb_vis}) + \cdot \tau \ + \cdot ra_{d} + + Args: + tau: bulk transmissivity, unitless + ra_d: daily solar radiation, J/m^2 + k_fFEC: from flux to energy conversion, umol/J + k_alb_vis: visible light albedo + + Returns: + An array of photosynthetic photon flux density, mol/m^2 + """ + + ppfd = (1.0e-6) * k_fFEC * (1.0 - k_alb_vis) * tau * ra_d + + return ppfd + + +def calc_ppfd( + sf: NDArray, + elv: NDArray, + latitude: NDArray, + julian_day: NDArray, + n_days: NDArray, + const: CoreConst, +) -> NDArray: + r"""Calculates Photosynthetic Photon Flux Density, :math:`PPFD`, (:math:`mol/m^2`). + + This function calculates :math:`PPFD` (:math:`mol/m^2`) from the observation + location and time using the following calculations: + + - :func:`calc_heliocentric_longitudes` + - :func:`calc_distance_factor` + - :func:`calc_declination_angle_delta` + - :func:`calc_transmissivity` + - :func:`calc_sunset_hour_angle` + - :func:`calc_daily_solar_radiation` + - :func:`calc_ppfd_from_tau_ra_d` + + Args: + sf: Daily sunshine fraction of observations, unitless. + elv: Elevation of observations, metres. + latitude: The Latitude of observations (degrees). + julian_day: Julian day of the year. + n_days: Days in the calendar year. + const: CoreConst object. + + Returns: + An array of photosynthetic photon flux density, :math:`PPFD` (:math:`mol/m^2`). + + Example: + >>> # Calculate ppfd + >>> import numpy as np + >>> from pyrealm.constants import CoreConst + >>> # Create dataclass of constants + >>> const = CoreConst() + >>> # Define variable values + >>> sf = np.array([1.0]) + >>> elv = np.array([142]) + >>> latitude = np.array([37.7]) + >>> julian_day = np.array([172]) + >>> n_days = np.array([366]) + >>> # Evaluate function + >>> calc_ppfd(sf=sf, elv=elv, latitude=latitude, julian_day=julian_day, \ + ... n_days=n_days, const=const) + array([62.04230021]) + + """ + + # Validate the inputs + _ = check_input_shapes(latitude, elv, sf, julian_day, n_days) + + # Calculate intermediate values + + nu, lambda_ = calc_heliocentric_longitudes(julian_day=julian_day, n_days=n_days) + + dr = calc_distance_factor(nu=nu, k_e=const.k_e) + + delta = calc_declination_angle_delta( + lambda_=lambda_, k_eps=const.k_eps, k_pir=const.k_pir + ) + + tau = calc_transmissivity(sf=sf, elv=elv, k_c=const.k_c, k_d=const.k_d) + + hs = calc_sunset_hour_angle(delta=delta, latitude=latitude, k_pir=const.k_pir) + + ra_d = calc_daily_solar_radiation( + dr=dr, hs=hs, delta=delta, latitude=latitude, const=const + ) + + # Calculate ppfd + + ppfd = calc_ppfd_from_tau_ra_d( + tau=tau, ra_d=ra_d, k_fFEC=const.k_fFEC, k_alb_vis=const.k_alb_vis + ) + + return ppfd + + +def calc_net_longwave_radiation( + sf: NDArray, tc: NDArray, k_b: float, k_A: float +) -> NDArray: + r"""Calculates net longwave radiation, :math:`rnl`, :math:`W/m^2`. + + This function calculates net longwave radiation in :math:`W/m^2` using the methods + of Eq. 11, :cite:t:`colinprentice:1993a`, Eq. 5 and 6 :cite:t:`Linacre:1968a` . + + .. math:: + + rnl = (k_{b} + (1.0 - k_{b}) \cdot sf) \cdot (k_{A} - tc) + + Args: + sf: sunshine fraction of observations, unitless + tc: temperature of observations, °C + k_b: calculation constant for Rnl + k_A: calculation constant for Rnl + + Returns: + An array of net longwave radiation, :math:`W/m^2`. + """ + + rnl = (k_b + (1.0 - k_b) * sf) * (k_A - tc) + + return rnl + + +def calc_rw(tau: NDArray, dr: NDArray, k_alb_sw: float, k_Gsc: float) -> NDArray: + r"""Calculates variable substitute :math:`rw`, :math:`W/m^2`. + + .. math:: + + rw = (1.0 - k_{alb_sw}) \cdot \tau \cdot k_{Gsc} \cdot dr + + Args: + tau : bulk transmissivity, unitless + dr : distance ration, unitless + k_alb_sw : shortwave albedo + k_Gsc : solar constant, W/m^2 + + Returns: + An array of intermediate variable rw, :math:`W/m^2`. + """ + + rw = (1.0 - k_alb_sw) * tau * k_Gsc * dr + + return rw + + +def calc_net_rad_crossover_hour_angle( + rnl: NDArray, + tau: NDArray, + dr: NDArray, + delta: NDArray, + latitude: NDArray, + const: CoreConst, +) -> NDArray: + r"""Calculates the net radiation crossover hour angle, :math:`hn` degrees. + + This function calculates the net radiation crossover hour angle :math:`hn` in + degrees. + + .. math:: + + hn = \frac{\arccos(\text{clip}((rnl - rw \cdot ru) / (rw \cdot rv), -1.0, 1.0))} + {k_{pir}} + + Args: + rnl: net longwave radiation, :math:`W/m^2` + tau: bulk transmissivity, unitless + dr: distance ration, unitless + delta: solar declination delta + latitude: site latitude(s) + const: CoreConst object + + Returns: + _calc_net_rad_crossover_hour_angle + """ + + ru, rv = calc_lat_delta_intermediates(delta=delta, latitude=latitude) + rw = calc_rw(tau=tau, dr=dr, k_alb_sw=const.k_alb_sw, k_Gsc=const.k_Gsc) + + return _calc_net_rad_crossover_hour_angle( + rnl=rnl, rw=rw, ru=ru, rv=rv, k_pir=const.k_pir + ) + + +def _calc_net_rad_crossover_hour_angle( + rnl: NDArray, rw: NDArray, ru: NDArray, rv: NDArray, k_pir: float +) -> NDArray: + """Calculates the net radiation crossover hour angle, degrees. + + This function calculates the net radiation crossover hour angle in degrees. + + Args: + rnl: net longwave radiation, W/m^2 + rw: intermediate variable, W/m^2 + ru: intermediate variable, W/m^2 + rv: intermediate variable, W/m^2 + k_pir: conversion factor from radians to degrees + + Returns: + An array of net radiation crossover hour angle, degrees + """ + + hn = np.arccos(np.clip((rnl - rw * ru) / (rw * rv), -1.0, 1.0)) / k_pir + + return hn + + +def calc_daytime_net_radiation( + hn: NDArray, + rnl: NDArray, + delta: NDArray, + latitude: NDArray, + tau: NDArray, + dr: NDArray, + const: CoreConst, +) -> NDArray: + r"""Calculates daily net radiation, :math:`rn_{d}`, :math:`J/m^2`. + + .. math:: + + rn_{d} = \left( \frac{secs_{d}}{\pi} \right) \cdot \ + \left( hn \cdot k_{pir} \cdot (rw \cdot ru - rnl) + rw \cdot rv \cdot \ + sin(\deg2rad(hn)) \right) + + Args: + hn: crossover hour angle, degrees + rnl: net longwave radiation, W/m^2 + delta: solar declination delta + latitude: site latitude(s) + tau: bulk transmissivity, unitless + dr: distance ration, unitless + const: CoreConst object + + Result: + _calc_daytime_net_radiation + """ + ru, rv = calc_lat_delta_intermediates(delta=delta, latitude=latitude) + rw = calc_rw(tau=tau, dr=dr, k_alb_sw=const.k_alb_sw, k_Gsc=const.k_Gsc) + + return _calc_daytime_net_radiation( + hn=hn, rw=rw, ru=ru, rv=rv, rnl=rnl, k_pir=const.k_pir, k_secs_d=const.k_secs_d + ) + + +def _calc_daytime_net_radiation( + hn: NDArray, + rw: NDArray, + ru: NDArray, + rv: NDArray, + rnl: NDArray, + k_pir: float, + k_secs_d: int, +) -> NDArray: + """Calculates daily net radiation, :math:`J/m^2`. + + Args: + hn: crossover hour angle, degrees + rw: dimensionless variable substitute + ru: dimensionless variable substitute + rv: dimensionless variable substitute + rnl: net longwave radiation, W/m^2 + k_pir: conversion factor from radians to degrees + k_secs_d: seconds in one solar day + + Result: + An array of daily net radiation, J/m^2 + """ + + rn_d = (k_secs_d / np.pi) * ( + hn * k_pir * (rw * ru - rnl) + rw * rv * np.sin(np.deg2rad(hn)) + ) + + return rn_d + + +def calc_nighttime_net_radiation( + rnl: NDArray, + hn: NDArray, + hs: NDArray, + delta: NDArray, + latitude: NDArray, + tau: NDArray, + dr: NDArray, + const: CoreConst, +) -> NDArray: + r"""Calculates nightime net radiation, :math:`rnn_{d}` :math:`J/m^2`. + + .. math:: + + rnn_{d} = \left( + rw \cdot rv \cdot (\sin(\deg2rad(hs)) - \sin(\deg2rad(hn))) \ + + rw \cdot ru \cdot k_{pir} \cdot (hs - hn) + - rnl \cdot (\pi - k_{pir} \cdot hn) + \right) \cdot \left( \frac{secs_{d}}{\pi} \right) + + Args: + rnl: net longwave radiation, rnl, :math:`W/m^2` + hs: sunset hour angle, degrees + hn: crossover hour angle, degrees + delta: solar declination delta + latitude: site latitude(s) + tau: bulk transmissivity, unitless + dr: distance ration, unitless + const: CoreConst object + + Returns: + _calc_nighttime_net_radiation + """ + ru, rv = calc_lat_delta_intermediates(delta=delta, latitude=latitude) + rw = calc_rw(tau=tau, dr=dr, k_alb_sw=const.k_alb_sw, k_Gsc=const.k_Gsc) + + return _calc_nighttime_net_radiation( + rw=rw, + rv=rv, + ru=ru, + hs=hs, + hn=hn, + rnl=rnl, + k_pir=const.k_pir, + k_secs_d=const.k_secs_d, + ) + + +def _calc_nighttime_net_radiation( + rw: NDArray, + rv: NDArray, + ru: NDArray, + hs: NDArray, + hn: NDArray, + rnl: NDArray, + k_pir: float, + k_secs_d: int, +) -> NDArray: + """Calculates nightime net radiation, :math:`J/m^2`. + + Args: + rw: dimensionless variable substitute + rv: dimensionless variable substitute + ru: dimensionless variable substitute + hs: sunset hour angle, degrees + hn: crossover hour angle, degrees + rnl: net longwave radiation, rnl, :math:`W/m^2` + k_pir: conversion factor from radians to degrees + k_secs_d: seconds in one solar day + + Returns: + An array of nighttime net radiation, :math:`J/m^2` + """ + + rnn_d = ( + (rw * rv * (np.sin(np.deg2rad(hs)) - np.sin(np.deg2rad(hn)))) + + (rw * ru * k_pir * (hs - hn)) + - (rnl * (np.pi - k_pir * hn)) + ) * (k_secs_d / np.pi) + + return rnn_d def calc_heliocentric_longitudes( @@ -31,8 +609,8 @@ def calc_heliocentric_longitudes( """Calculate heliocentric longitude and anomaly. This function calculates the heliocentric true anomaly (``nu``, degrees) and true - longitude (``lambda_``, degrees), given the Julian day in the year and the number of - days in the year, following :cite:t:`berger:1978a`. + longitude (``lambda_``, degrees), given the Julian day in the year and the + number of days in the year, following :cite:t:`berger:1978a`. Args: julian_day: day of year @@ -40,7 +618,7 @@ def calc_heliocentric_longitudes( core_const: An instance of CoreConst. Returns: - A tuple of arrays containing ``nu`` and ``lambda_``. + A tuple of NDArrays containing ``nu`` and ``lambda_``. """ # Variable substitutes: @@ -89,3 +667,258 @@ def calc_heliocentric_longitudes( nu = (lambda_ - core_const.k_omega) % 360 return (nu, lambda_) + + +def calc_solar_elevation(site_obs_data: LocationDateTime) -> NDArray: + r"""Calculate the solar elevation angle for a specific location and times. + + This function calculates the solar elevation angle, which is the angle between the + sun and the observer's local horizon, using the methods outlined in + :cite:t:`depury:1997a`. + + NB: This implementation does not correct for the effect of local observation + altitude on perceived solar elevation. + + This approach uses the following calculations: + + - :func:`day_angle` + - :func:`equation_of_time` + - :func:`solar_noon` + - :func:`local_hour_angle` + - :func:`solar_declination` + - :func:`elevation_from_lat_dec_hn` + + Args: + site_obs_data: A :class:`~pyrealm.core.calendar.LocationDateTime` instance + containing the location and time-specific information for the observation + site. + + Returns: + An array of solar elevation angles in radians, representing the angular height + of the sun above the horizon at the specified location and time. + + Example: + >>> # Calculate solar elevation at Wagga Wagga, Australia on 25th October 1995. + >>> # Worked example taken from dePury and Farquhar (1997) + >>> import numpy as np + >>> from pyrealm.core.calendar import LocationDateTime + >>> from pyrealm.core.solar import calc_solar_elevation + >>> # Create instance of LocationDateTime dataclass + >>> latitude = -35.058333 + >>> longitude = 147.34167 + >>> year_date_time = np.array([np.datetime64("1995-10-25T10:30")]) + >>> ldt = LocationDateTime(latitude = latitude, longitude = longitude,\ + ... year_date_time = year_date_time) + >>> # Run solar elevation calculation + >>> calc_solar_elevation(ldt) + array([1.0615713]) + + """ + + G_d = day_angle(site_obs_data.julian_days) + + E_t = equation_of_time(G_d) + + t0 = solar_noon(site_obs_data.longitude, site_obs_data.local_standard_meridian, E_t) + + hour_angle = local_hour_angle(site_obs_data.decimal_time, t0) + + declination = solar_declination(site_obs_data.julian_days) + + elevation = elevation_from_lat_dec_hn( + site_obs_data.latitude_rad, declination, hour_angle + ) + return elevation + + +def elevation_from_lat_dec_hn( + latitude: NDArray | float, declination: NDArray, hour_angle: NDArray +) -> NDArray: + r"""Calculate the elevation angle of the sun above the horizon. + + The elevation angle (or solar altitude angle) is the angle between the horizon and + the sun, which indicates how high the sun is in the sky at a given time. This + function calculates the elevation angle based on the observer's latitude, the + solar declination, and the hour angle. + + The calculation is based on the following trigonometric relationship based on Eqn + A13, :cite:t:`depury:1997a`: + + .. math:: + \sin(\alpha) = \sin(\phi) \cdot \sin(\delta) + + \cos(\phi) \cdot \cos(\delta) \cdot \cos(h) + + where, + + - :math:`\alpha` is the elevation angle, + - :math:`\phi` is the latitude of the observer, + - :math:`\delta` is the solar declination, and + - :math:`h` is the hour angle. + + The elevation angle is then given by: + + .. math:: + \alpha = \arcsin(\sin(\alpha)) + + Args: + latitude: Array of latitudes in radians, or a single latitude value (as a \ + float). + declination: Array of solar declination angles in radians. + hour_angle: Array of hour angles in radians. + + Returns: + An array of elevation angles in radians (as a floating-point number array), + representing the angular height of the sun above the horizon. + + """ + + sin_alpha = np.sin(latitude) * np.sin(declination) + np.cos(latitude) * np.cos( + declination + ) * np.cos(hour_angle) + + elevation = np.arcsin(sin_alpha) + + return elevation + + +def solar_declination(td: NDArray) -> NDArray: + r"""Calculates solar declination angle. + + Use method described in eqn A14 of :cite:t:`depury:1997a` to calculate solar + declination angle, from day of the year. + + .. math:: + + \text{declination} = -23.4 \cdot \left(\frac{1}{k\_pir}\right) \cdot \cos\left + (\frac{2 \cdot \pi \cdot (td + 10)}{365}\right) + + Args: + td: Julian day of the year + + Returns: + Array of solar declination angles (radians) + """ + + declination = -23.4 * (np.pi / 180) * np.cos((2 * np.pi * (td + 10)) / 365) + + return declination + + +def local_hour_angle(t: NDArray, t0: NDArray) -> NDArray: + r"""Calculate the local hour angle :math:`h` for a given time and solar noon. + + The local hour angle is a measure of time, expressed in angular terms, that + indicates the position of the sun relative to solar noon. This function + calculates the local hour angle by determining the difference between the + current time (``t``) and the solar noon time (:math:`t_{0}`), and then + converting this difference into an angle. + + Equation implemented from A15 :cite:t:`depury:1997a`. + + .. math:: + h = \pi \cdot \frac{t - t_{0}}{12} + + Args: + t: Array of current time values in hours (as a floating-point number). + t0: Array of solar noon time values in hours (as a floating-point number). + + Returns: + The local hour angle in radians (as a floating-point number array), which + represents the angular distance of the sun from the local meridian at the + given time. + + """ + + h = np.pi * (t - t0) / 12 + + return h + + +def solar_noon(L_e: float, L_s: float, E_t: NDArray) -> NDArray: + r"""Calculate the solar noon time for a given location. + + The solar noon is the time of day when the sun is at its highest point in the sky + for a given location. This function calculates the solar noon by adjusting the + standard noon time (12:00 PM) based on the difference between the local + longitude (:math:`L_{e}`) and the local standard meridian (:math:`L_{s}`) and + the equation of time (:math:`E_{t}`). Based on EqA16, :cite:t:`depury:1997a`. + + .. math:: t_{0} = 12 + \frac{4 \cdot -(L_{e} - L_{s}) - E_{t}}{60} + + Args: + L_e: Local longitude of the observer in degrees (positive for east,negative \ + for west). + L_s: Longitude of the standard meridian for the observer's time zone in degrees. + E_t: Equation of time in minutes, accounting for the irregularity of the \ + Earth's orbit and axial tilt. + + Returns: + The solar noon time in hours (as a floating-point number), which can be + interpreted as a time of day. + + """ + + t0 = 12 + (4 * -(L_e - L_s) - E_t) / 60 + + return t0 + + +def equation_of_time(day_angle: NDArray) -> NDArray: + r"""Calculates equation of time in minutes. + + Based on eqn 1.4.1 :cite:t:`iqbal:1983a` rather than eqn A17 + :cite:t:`depury:1997a` due to incorrect reported implementation in the latter. + + .. math:: + + E_t = \left( 0.000075 + + 0.001868 \cdot \cos(\Gamma) + - 0.032077 \cdot \sin(\Gamma) + - 0.014615 \cdot \cos(2\Gamma) + - 0.04089 \cdot \sin(2\Gamma) \right) + \times 229.18 + + Where gamma is the day angle. + + Args: + day_angle: day angle in radians + + Returns: + An array of Equation of time values + + """ + E_t = ( + 0.000075 + + 0.001868 * np.cos(day_angle) + - 0.032077 * np.sin(day_angle) + - 0.014615 * np.cos(2 * day_angle) + - 0.04089 * np.sin(2 * day_angle) + ) * 229.18 + + return E_t + + +def day_angle(t_d: NDArray) -> NDArray: + r"""Calculates solar day angle (gamma), radians. + + The day angle (``gamma``) for a given day of the year ``N``, (where N=1 for + January 1st and N=365 for December 31st) can be calculated using the following + formula: + + Based on Eqn A18, :cite:t:`depury:1997a`. + + .. math:: + + \gamma = \frac{2\pi (N - 1)}{365} + + Args: + t_d: Julian day of the year + + Returns: + An array of solar day angles + + """ + + day_angle = 2 * np.pi * (t_d - 1) / 365 + + return day_angle diff --git a/pyrealm/splash/solar.py b/pyrealm/splash/solar.py index fc1e1831..fe3bfc54 100644 --- a/pyrealm/splash/solar.py +++ b/pyrealm/splash/solar.py @@ -9,7 +9,21 @@ from pyrealm.constants import CoreConst from pyrealm.core.calendar import Calendar -from pyrealm.core.solar import calc_heliocentric_longitudes +from pyrealm.core.solar import ( + calc_daily_solar_radiation, + calc_daytime_net_radiation, + calc_declination_angle_delta, + calc_distance_factor, + calc_heliocentric_longitudes, + calc_lat_delta_intermediates, + calc_net_longwave_radiation, + calc_net_rad_crossover_hour_angle, + calc_nighttime_net_radiation, + calc_ppfd_from_tau_ra_d, + calc_rw, + calc_sunset_hour_angle, + calc_transmissivity, +) from pyrealm.core.utilities import check_input_shapes @@ -90,20 +104,11 @@ def __post_init__( ) # Calculate distance factor (dr), Berger et al. (1993) - dr = ( - 1.0 - / ( - (1.0 - self.core_const.k_e**2) - / (1.0 + self.core_const.k_e * np.cos(np.deg2rad(nu))) - ) - ) ** 2 + dr = calc_distance_factor(nu, self.core_const.k_e) # Calculate declination angle (delta), Woolf (1968) - delta = ( - np.arcsin( - np.sin(np.deg2rad(lambda_)) * np.sin(np.deg2rad(self.core_const.k_eps)) - ) - / self.core_const.k_pir + delta = calc_declination_angle_delta( + lambda_, self.core_const.k_eps, self.core_const.k_pir ) # The nu, lambda_, dr and delta attributes are all one dimensional arrays @@ -117,78 +122,56 @@ def __post_init__( self.dr = np.expand_dims(dr, axis=expand_dims) self.delta = np.expand_dims(delta, axis=expand_dims) - # Calculate variable substitutes (u and v), unitless - self.ru = np.sin(np.deg2rad(self.delta)) * np.sin(np.deg2rad(lat)) - self.rv = np.cos(np.deg2rad(self.delta)) * np.cos(np.deg2rad(lat)) + self.ru, self.rv = calc_lat_delta_intermediates(self.delta, lat) # Calculate the sunset hour angle (hs), Eq. 3.22, Stine & Geyer (2001) - self.hs = ( - np.arccos(-1.0 * np.clip(self.ru / self.rv, -1.0, 1.0)) - / self.core_const.k_pir - ) + self.hs = calc_sunset_hour_angle(self.delta, lat, self.core_const.k_pir) # Calculate daily extraterrestrial solar radiation (ra_d), J/m^2 # Eq. 1.10.3, Duffy & Beckman (1993) - self.ra_d = ( - (86400.0 / np.pi) - * self.core_const.k_Gsc - * self.dr - * ( - self.ru * self.core_const.k_pir * self.hs - + self.rv * np.sin(np.deg2rad(self.hs)) - ) + self.ra_d = calc_daily_solar_radiation( + self.dr, self.hs, self.delta, lat, self.core_const ) # Calculate transmittivity (tau), unitless # Eq. 11, Linacre (1968); Eq. 2, Allen (1996) - self.tau = (self.core_const.k_c + self.core_const.k_d * sf) * ( - 1.0 + (2.67e-5) * elv + self.tau = calc_transmissivity( + sf, elv, self.core_const.k_c, self.core_const.k_d + ) + + self.rw = calc_rw( + self.tau, self.dr, self.core_const.k_alb_sw, self.core_const.k_Gsc ) # Calculate daily PPFD (ppfd_d), mol/m^2 - self.ppfd_d = ( - (1.0e-6) - * self.core_const.k_fFEC - * (1.0 - self.core_const.k_alb_vis) - * self.tau - * self.ra_d + self.ppfd_d = calc_ppfd_from_tau_ra_d( + self.tau, self.ra_d, self.core_const.k_fFEC, self.core_const.k_alb_vis ) # Estimate net longwave radiation (rnl), W/m^2 # Eq. 11, Prentice et al. (1993); Eq. 5 and 6, Linacre (1968) - self.rnl = (self.core_const.k_b + (1.0 - self.core_const.k_b) * sf) * ( - self.core_const.k_A - tc - ) - - # Calculate variable substitute (rw), W/m^2 - self.rw = ( - (1.0 - self.core_const.k_alb_sw) - * self.tau - * self.core_const.k_Gsc - * self.dr + self.rnl = calc_net_longwave_radiation( + sf, tc, self.core_const.k_b, self.core_const.k_A ) # Calculate net radiation cross-over hour angle (hn), degrees - self.hn = ( - np.arccos( - np.clip((self.rnl - self.rw * self.ru) / (self.rw * self.rv), -1.0, 1.0) - ) - / self.core_const.k_pir + self.hn = calc_net_rad_crossover_hour_angle( + self.rnl, self.tau, self.dr, self.delta, lat, self.core_const ) # Calculate daytime net radiation (rn_d), J/m^2 - self.rn_d = (86400.0 / np.pi) * ( - self.hn * self.core_const.k_pir * (self.rw * self.ru - self.rnl) - + self.rw * self.rv * np.sin(np.deg2rad(self.hn)) + self.rn_d = calc_daytime_net_radiation( + self.hn, self.rnl, self.delta, lat, self.tau, self.dr, self.core_const ) # Calculate nighttime net radiation (rnn_d), J/m^2 - self.rnn_d = ( - ( - self.rw - * self.rv - * (np.sin(np.deg2rad(self.hs)) - np.sin(np.deg2rad(self.hn))) - ) - + (self.rw * self.ru * self.core_const.k_pir * (self.hs - self.hn)) - - (self.rnl * (np.pi - self.core_const.k_pir * self.hn)) - ) * (86400.0 / np.pi) + self.rnn_d = calc_nighttime_net_radiation( + self.rnl, + self.hn, + self.hs, + self.delta, + lat, + self.tau, + self.dr, + self.core_const, + ) diff --git a/tests/regression/splash/test_splash.py b/tests/regression/splash/test_splash.py index a6225069..0c9f1db1 100644 --- a/tests/regression/splash/test_splash.py +++ b/tests/regression/splash/test_splash.py @@ -39,9 +39,9 @@ def test_estimate_daily_water_balance_scalar(splash_core_constants): # Expected values are the output of __main__ in original splash.py evap_expected = { - "cond": 0.885192, - "eet_d": 6.405468, - "pet_d": 8.070889, + "cond": np.array([0.885192]), + "eet_d": np.array([6.405468]), + "pet_d": np.array([8.070889]), } for ky, val in evap_expected.items(): diff --git a/tests/unit/core/test_solar.py b/tests/unit/core/test_solar.py index c465aea0..134c23f8 100644 --- a/tests/unit/core/test_solar.py +++ b/tests/unit/core/test_solar.py @@ -3,6 +3,326 @@ import numpy as np import pytest +from pyrealm.constants import CoreConst + +# @pytest.fixture(scope="module") +# def CoreConst_fixture(): +# """Sets up core constants dataclass.""" +# const = CoreConst() +# return CoreConst() + + +@pytest.mark.parametrize( + argnames="nu, expected", + argvalues=[ + (np.array([166.097934]), np.array([0.968381])), + ], +) +def test_calc_distance_factor(nu, expected): + """Tests calc_distance_factor. + + This tests aims to confirm the correct implementation of the maths. + """ + from pyrealm.core.solar import calc_distance_factor + + Const = CoreConst() + result = calc_distance_factor(nu, Const.k_e) + + assert np.allclose(result, expected) + + +@pytest.mark.parametrize( + argnames="lambda_, expected", + argvalues=[ + ( + np.array([89.097934]), + np.array([23.436921]), + ) + ], +) +def test_calc_declination_angle_delta(lambda_, expected): + """Tests calc_declination_angle_delta. + + This tests aims to confirm the correct implementation of the maths. + """ + + from pyrealm.core.solar import calc_declination_angle_delta + + Const = CoreConst() + result = calc_declination_angle_delta(lambda_, Const.k_eps, Const.k_pir) + + assert np.allclose(result, expected) + + +@pytest.mark.parametrize( + argnames="delta,latitude, expected", + argvalues=[ + ( + np.array([23.436921]), + np.array([37.7]), + (np.array([0.243228277]), np.array([0.725946417])), + ), + ], +) +def test_calc_lat_delta_intermediates(delta, latitude, expected): + """Tests calc_lat_delta_intermediates. + + This tests aims to confirm the correct implementation of the maths. + """ + + from pyrealm.core.solar import calc_lat_delta_intermediates + + result = calc_lat_delta_intermediates(delta, latitude) + + assert np.allclose(result, expected) + + +@pytest.mark.parametrize( + argnames="delta, latitude, expected", + argvalues=[ + ( + np.array([23.436921]), + np.array([37.7]), + np.array([109.575573]), + ) + ], +) +def test_calc_sunset_hour_angle(delta, latitude, expected): + """Tests calc_sunset_hour_angle. + + This tests aims to confirm the correct implementation of the maths. + """ + + from pyrealm.core.solar import calc_sunset_hour_angle + + Const = CoreConst() + result = calc_sunset_hour_angle(delta, latitude, Const.k_pir) + + assert np.allclose(result, expected) + + +@pytest.mark.parametrize( + argnames="dr, hs, delta, latitude, expected", + argvalues=[ + ( + np.array([0.968381]), + np.array([109.575573]), + np.array([23.436921]), + np.array([37.7]), + np.array([41646763]), + ) + ], +) +def test_calc_daily_solar_radiation(dr, hs, delta, latitude, expected): + """Tests calc_daily_solar_radiation. + + This test is intended to verify the implemented maths + """ + + from pyrealm.core.solar import calc_daily_solar_radiation + + Const = CoreConst() + + result = calc_daily_solar_radiation(dr, hs, delta, latitude, Const) + + assert np.allclose(result, expected) + + +@pytest.mark.parametrize( + argnames="sf, elv, expected", + argvalues=[(np.array([1.0]), np.array([142]), np.array([0.752844]))], +) +def test_calc_transmissivity(sf, elv, expected): + """Tests calc_transmissivity. + + This test is intended to verify the implemented maths + """ + + from pyrealm.core.solar import calc_transmissivity + + Const = CoreConst() + + result = calc_transmissivity(sf, elv, Const.k_c, Const.k_d) + + assert np.allclose(result, expected) + + +@pytest.mark.parametrize( + argnames="tau, ra_d, expected", + argvalues=[(np.array([0.752844]), np.array([41646763]), np.array([62.042300]))], +) +def test_calc_ppfd_from_tau_ra_d(tau, ra_d, expected): + """Tests calc_ppfd_from_tau_ra_d. + + This test is intended to verify the implemented maths. + """ + + from pyrealm.core.solar import calc_ppfd_from_tau_ra_d + + Const = CoreConst() + + result = calc_ppfd_from_tau_ra_d(tau, ra_d, Const.k_fFEC, Const.k_alb_vis) + + assert np.allclose(result, expected) + + +@pytest.mark.parametrize( + argnames="sf, elv, latitude, julian_day, n_days, expected", + argvalues=[ + ( + np.array([1.0]), + np.array([142]), + np.array([37.7]), + np.array([172]), + np.array([366]), + np.array([62.042300]), + ) + ], +) +def test_calc_ppfd( + sf, + elv, + latitude, + julian_day, + n_days, + expected, +): + """Tests calc_ppfd. + + This test is intended to verify the implemented maths. + """ + + from pyrealm.core.solar import calc_ppfd + + Const = CoreConst() + + result = calc_ppfd(sf, elv, latitude, julian_day, n_days, Const) + + assert np.allclose(result, expected) + + +@pytest.mark.parametrize( + argnames="sf, tc, expected", + argvalues=[(np.array([1.0]), np.array([23.0]), np.array([84.000000]))], +) +def test_calc_net_longwave_radiation(sf, tc, expected): + """Tests calc_net_longwave_radiation. + + This test is intended to verify the implemented maths. + """ + from pyrealm.core.solar import calc_net_longwave_radiation + + Const = CoreConst() + + result = calc_net_longwave_radiation(sf, tc, Const.k_b, Const.k_A) + + assert np.allclose(result, expected) + + +@pytest.mark.parametrize( + argnames="tau, dr, expected", + argvalues=[ + ( + np.array([0.752844]), + np.array([0.968381]), + np.array([823.4242375]), + ) + ], +) +def test_calc_rw(tau, dr, expected): + """Test calc_rw. + + This test is intended to verify the implemented maths. + """ + from pyrealm.core.solar import calc_rw + + Const = CoreConst() + + result = calc_rw(tau, dr, Const.k_alb_sw, Const.k_Gsc) + + assert np.allclose(result, expected) + + +@pytest.mark.parametrize( + argnames="rnl, tau, dr, delta, latitude, expected", + argvalues=[ + ( + np.array([84.000000]), + np.array([0.752844]), + np.array([0.968381]), + np.array([23.436921]), + np.array([37.7]), + np.array([101.217016]), + ) + ], +) +def test_calc_net_rad_crossover_hour_angle(rnl, tau, dr, delta, latitude, expected): + """Tests calc_net_rad_crossover_hour_angle. + + This test is intended to verify the implemented maths. + """ + + from pyrealm.core.solar import calc_net_rad_crossover_hour_angle + + Const = CoreConst() + + result = calc_net_rad_crossover_hour_angle(rnl, tau, dr, delta, latitude, Const) + + assert np.allclose(result, expected) + + +@pytest.mark.parametrize( + argnames="hn,rnl,delta,latitude,tau,dr,expected", + argvalues=[ + ( + np.array([101.217016]), + np.array([84.000000]), + np.array([23.436921]), + np.array([37.7]), + np.array([0.752844]), + np.array([0.968381]), + np.array([21774953]), + ) + ], +) +def test_daytime_net_radiation(hn, rnl, delta, latitude, tau, dr, expected): + """Tests calculation of net daytime radiation.""" + + from pyrealm.core.solar import calc_daytime_net_radiation + + Const = CoreConst() + + result = calc_daytime_net_radiation(hn, rnl, delta, latitude, tau, dr, Const) + + assert np.allclose(result, expected) + + +@pytest.mark.parametrize( + argnames="rnl, hn, hs, delta, latitude, tau, dr, expected", + argvalues=[ + ( + np.array([84.000000]), + np.array([101.217016]), + np.array([109.575573]), + np.array([23.436921]), + np.array([37.7]), + np.array([0.752844]), + np.array([0.968381]), + np.array([-3009150]), + ) + ], +) +def test_nightime_net_radiation(rnl, hn, hs, delta, latitude, tau, dr, expected): + """Tests calculation of net nighttime radiation.""" + + from pyrealm.core.solar import calc_nighttime_net_radiation + + Const = CoreConst() + + result = calc_nighttime_net_radiation(rnl, hn, hs, delta, latitude, tau, dr, Const) + + assert np.allclose(result, expected) + @pytest.mark.parametrize( argnames="day,n_day,expected", @@ -24,3 +344,142 @@ def test_calc_heliocentric_longitudes(day, n_day, expected): result = calc_heliocentric_longitudes(day, n_day) assert np.allclose(result, expected) + + +@pytest.mark.parametrize( + argnames="latitude, longitude, year_date_time, expected", + argvalues=[ + ( + -35.058333, + 147.34167, + np.array([np.datetime64("1995-10-25T10:30")]), + np.array([1.0615713]), + ) + ], +) +def test_calc_solar_elevation(latitude, longitude, year_date_time, expected): + """Tests calc_solar_elevation. + + This test is intended to verify the implemented maths. + """ + + from pyrealm.core.calendar import LocationDateTime + from pyrealm.core.solar import calc_solar_elevation + + site_obs_data = LocationDateTime( + latitude=latitude, + longitude=longitude, + year_date_time=year_date_time, + ) + + result = calc_solar_elevation(site_obs_data) + + assert np.allclose(result, expected) + + +@pytest.mark.parametrize( + argnames="td, expected", + argvalues=[(np.array([298]), -0.22708144)], +) +def test_calc_declination(td, expected): + """Tests calc_declination. + + This test is intended to verify the implemented maths. + """ + + # This test is intended to verify the implemented maths. + + from pyrealm.core.solar import solar_declination + + # Const = CoreConst() + + result = solar_declination(td) + + assert np.allclose(result, expected) + + +@pytest.mark.parametrize( + argnames="julian_day, expected", + argvalues=[(np.array([298]), 5.11261928)], +) +def test_calc_day_angle(julian_day, expected): + """Tests calc_day_angle. + + This test is intended to verify the implemented maths. + """ + + from pyrealm.core.solar import day_angle + + result = day_angle(julian_day) + + assert np.allclose(result, expected) + + +@pytest.mark.parametrize( + argnames="day_angle, expected", + argvalues=[(np.array([5.11]), 15.99711625)], +) +def test_equation_of_time(day_angle, expected): + """Tests equation_of_time. + + This test is intended to verify the implemented maths. + """ + + from pyrealm.core.solar import equation_of_time + + result = equation_of_time(day_angle) + + assert np.allclose(result, expected) + + +@pytest.mark.parametrize( + argnames="longitude, standard_meridian, E_t, expected", + argvalues=[(147.34167, 150, 16.01, np.array([11.910388666666668]))], +) +def test_solar_noon(longitude, standard_meridian, E_t, expected): + """Tests solar_noon. + + This test is intended to verify the implemented maths. + """ + + from pyrealm.core.solar import solar_noon + + result = solar_noon(longitude, standard_meridian, E_t) + + assert np.allclose(result, expected) + + +@pytest.mark.parametrize( + argnames="t, t0, expected", + argvalues=[(np.array([10.5]), np.array([11.91]), np.array([-0.36913714]))], +) +def test_local_hour_angle(t, t0, expected): + """Tests local_hour_angle. + + This test is intended to verify the implemented maths. + """ + + from pyrealm.core.solar import local_hour_angle + + result = local_hour_angle(t, t0) + + assert np.allclose(result, expected) + + +@pytest.mark.parametrize( + argnames="latitude, declination, hour_angle, expected", + argvalues=[ + (np.array([-0.61]), np.array([-0.23]), np.array([-0.37]), np.array([1.0647289])) + ], +) +def test_elevation_from_lat_dec_hn(latitude, declination, hour_angle, expected): + """Tests elevation_from_lat_dec_hn. + + This test is intended to verify the implemented maths. + """ + + from pyrealm.core.solar import elevation_from_lat_dec_hn + + result = elevation_from_lat_dec_hn(latitude, declination, hour_angle) + + assert np.allclose(result, expected)