The splash module
The splash submodule implements the SPLASH model (Davis et al., 2017) for calculating robust indices of radiation, evapotranspiration and plant-available soil moisture.
It is based on the (SPLASH v1.0 implementation)[https://doi.org/10.5281/zenodo.376293] published with the original description but has been extensively refactored and restructured. The main changes are:
The implementation now provides a programmatic API to the main model components, allowing the functionality to be imported and used within Python workflows. The original code was implemented as command line scripts that required editing for particular use cases.
The code has been updated to accept numpy arrays and to calculate estimates simultaneously across arrays. The original code predominantly expected scalar inputs and required users to script iteratation over sites.
Solar fluxes and the majority of evaporative fluxes do not vary given the initial data and so are now calculated once when a model instance is created. The original implementation recalculated many of these values when iterating over daily calculations, which reduces the memory footprint but at a substantial runtime cost. Only actual evapotranspiration (AET), soil moisture and runoff need to be calculated on a daily basis, because they depend on the soil moisture from the previous day.
The hard-coded constants in the original code can now be modified by users. In particular, the maximum soil moisture capacity (
kWm) was fixed globally in SPLASH v1.0 at 150mm: this can now be set by the user and can vary between sites.
The DailySolarFluxes submodule
The solar submodule provides the DailySolarFluxes class, used to calculate daily
solar radiation fluxes for observations.
- class DailySolarFluxes(latitude: dataclasses.InitVar[numpy.ndarray[tuple[typing.Any, ...], numpy.dtype[numpy.floating]] | xarray.core.dataarray.DataArray], elevation: dataclasses.InitVar[numpy.ndarray[tuple[typing.Any, ...], numpy.dtype[numpy.floating]] | xarray.core.dataarray.DataArray], dates: ~pyrealm.core.calendar.Calendar, sunshine_fraction: dataclasses.InitVar[numpy.ndarray[tuple[typing.Any, ...], numpy.dtype[numpy.floating]] | xarray.core.dataarray.DataArray], temperature: dataclasses.InitVar[numpy.ndarray[tuple[typing.Any, ...], numpy.dtype[numpy.floating]] | xarray.core.dataarray.DataArray], core_const: ~pyrealm.constants.core_const.CoreConst = <factory>)
Calculate daily solar fluxes.
This dataclass takes arrays describing the latitude, elevation, sunshine fraction and mean daily temperature for observations and then calculates key radiation fluxes given a Calendar object providing the Julian day of the observations and the year and number of days in the year.
The first dimension for the array inputs should correspond to time. If xarray inputs are used,
datesshould also be initialised using xarray inputs to ensure this. Alternatively,latitudecan include time as the first dimension.- Parameters:
latitude – The Latitude of observations (degrees)
elevation – Elevation of observations (metres)
dates – Dates of observations
sunshine_fraction – Daily sunshine fraction of observations (unitless)
temperature – Daily temperature of observations (°C)
- crossover_hour_angle: ndarray[tuple[Any, ...], dtype[floating]]
Net radiation cross-over hour angle, (\(h_n\), degrees)
- daily_ppfd: ndarray[tuple[Any, ...], dtype[floating]]
Daily photosynthetic photon flux density (PPFD, µmol m-2 s-1)
- daily_solar_radiation: ndarray[tuple[Any, ...], dtype[floating]]
Daily extraterrestrial solar radiation (\(R_d\), J m-2)
- daytime_net_radiation: ndarray[tuple[Any, ...], dtype[floating]]
Daytime net radiation (\(R_{d}\), J m-2)
- lambda_: ndarray[tuple[Any, ...], dtype[floating]]
True heliocentric longitude, (\(\lambda\), degrees)
- net_longwave_radiation: ndarray[tuple[Any, ...], dtype[floating]]
Net longwave radiation (\(R_{nl}\), W m-2)
The DailyEvapFluxes submodule
The evap submodule provides functions and classes to calculate evaporative
fluxes.
- class DailyEvapFluxes(solar: ~pyrealm.splash.solar.DailySolarFluxes, pa: dataclasses.InitVar[numpy.ndarray[tuple[typing.Any, ...], numpy.dtype[numpy.floating]] | xarray.core.dataarray.DataArray], tc: dataclasses.InitVar[numpy.ndarray[tuple[typing.Any, ...], numpy.dtype[numpy.floating]] | xarray.core.dataarray.DataArray], kWm: ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.floating]] | ~xarray.core.dataarray.DataArray = <factory>, core_const: ~pyrealm.constants.core_const.CoreConst = <factory>)
Calculate daily evaporative fluxes.
This class calculates daily evaporative fluxes given temperature and atmospheric pressure for daily observations and the calculated solar fluxes for those observations. The class attributes provide components of those fluxes that depend only on the initial data and are not dependent on the previous model state when iterating over time.
Two remaining components, the intersection hour angle (hi, degrees) and the estimated daily AET (aet_d, mm), depend on the soil moisture from the previous day and so must be calculated on a daily basis during either the spin process of the SPLASH model to estimate initial equilibrium soil moisture, or the daily calculation of soil moisture along a time series. These quantities are estimated using the
estimate_aet()method, given an estimate of soil moisture from the preceding day.- Parameters:
solar – A
DailySolarFluxesinstance from which to calculate evaporative fluxes. See the class definition for the flux variables and units provided.kWm – The maximum soil water capacity (mm).
tc – The air temperature of the observations (°C).
pa – The atmospheric pressure of the observations (Pa).
core_const – An instance of CoreConst.
- estimate_aet(wn: ndarray[tuple[Any, ...], dtype[floating]] | DataArray, day_idx: int | None = None, only_aet: bool = True) ndarray[tuple[Any, ...], dtype[floating]] | tuple[ndarray[tuple[Any, ...], dtype[floating]], ndarray[tuple[Any, ...], dtype[floating]], ndarray[tuple[Any, ...], dtype[floating]]]
Estimate actual evapotranspiration.
This method estimates the daily actual evapotranspiration (AET, mm/day), given estimates of the soil moisture (
wn) for observations. Optionally, the method can also return the the intersection hour angle (hi, degrees) and evaporative supply rate (sw, mm/h).By default,
wnis expected to provide estimates for all observations across all days in the model, butday_idxcan be set to indicate thatwnis providing the soil moisture for one specific day across the observations.- Parameters:
wn – The soil moisture (mm).
day_idx – An integer giving the index of the provided
wnvalues along the time axis.only_aet – Should the function only return AET or AET,
hiandsw.
- Returns:
An array of AET values or a tuple of arrays containing AET,
hiandsw.
The SplashModel submodule
The splash submodule provides the main SplashModel class for calculating
predictions under the SPLASH model.
- class SplashModel(lat: ndarray[tuple[Any, ...], dtype[floating]] | DataArray, elv: ndarray[tuple[Any, ...], dtype[floating]] | DataArray, sf: ndarray[tuple[Any, ...], dtype[floating]] | DataArray, tc: ndarray[tuple[Any, ...], dtype[floating]] | DataArray, pn: ndarray[tuple[Any, ...], dtype[floating]] | DataArray, dates: Calendar, kWm: ndarray[tuple[Any, ...], dtype[floating]] | DataArray = np.array([150.0]), core_const: CoreConst = CoreConst(), bounds_checker: BoundsChecker = BoundsChecker())
Fitting the SPLASH model.
The SplashModel class calculates the predictions of the SPLASH v1.0 model (Davis et al., 2017). The input variables of latitude, elevation, temperature, precipitation and sunshine fraction of observations are initially used to calculate solar and evaporative fluxes, which are stored in the
solarandevapattributes as instances ofDailySolarFluxesandDailyEvapFluxes.The inputs to a SplashModel are expected to be arrays with time varying along the first dimension. Other dimensions represent observations at sites on a particular date. The
datesargument is expected to be a Calendar object with the same length as the first dimension.If xarray inputs are used,
datesshould also be initialised using xarray inputs to ensure that the time dimension is set correctly. Alternatively,latcan include time as the first dimension.The main use of the SplashModel object is then to calculate the expected actual evapotranspiration (AET), soil moisture and runoff across the time series:
The
calculate_soil_moisture()returns these calculations, given an initial estimate of soil moisture in observed sites. This method simply iterates over the days, applying theestimate_daily_water_balance()method to calculate the daily water balance, given the soil moisture of the preceding day.The
estimate_initial_soil_moisture()method can be used to estimate an initial soil moisture for a time series from the first year of data in a time series.
- Parameters:
lat – The latitude of observations
elv – The elevation of observations (m), also used to calculate atmospheric pressure.
sf – The sunshine fraction (0-1, unitless)
tc – Air temperature (°C)
pn – Precipitation (mm/day)
dates – The dates of the time series
kWm – The maximum soil moisture capacity, defaulting to 150 (mm)
- calculate_soil_moisture(wn_init: ndarray[tuple[Any, ...], dtype[floating]] | DataArray) tuple[ndarray[tuple[Any, ...], dtype[floating]], ndarray[tuple[Any, ...], dtype[floating]], ndarray[tuple[Any, ...], dtype[floating]]]
Calculate the soil moisture, AET and runoff from a SplashModel.
This function takes an initial array of soil moisture values for the first observations in a SplashModel time series and then iteratively applies the daily water balance calculations along the time axis using the
estimate_daily_water_balance()method. This produces the expected actual evapotranspiration (AET), soil moisture, runoff and for all sites across the time series.- Parameters:
wn_init – The initial state of the soil moisture for observations
- Returns:
A tuple of numpy arrays containing predicted AET, soil moisture and runoff.
- estimate_daily_water_balance(previous_wn: ndarray[tuple[Any, ...], dtype[floating]] | DataArray, day_idx: int | None = None) tuple[ndarray[tuple[Any, ...], dtype[floating]], ndarray[tuple[Any, ...], dtype[floating]], ndarray[tuple[Any, ...], dtype[floating]]]
Estimate the daily water balance.
This function estimates the daily water balance within observations. The function first calculates the expected actual evapotranspiration (mm d-1, \(\textrm{AET}_{[t]}\)), given the soil moisture from the preceding day (mm, \(W_{n[t-1]}\)). Those are then used, along with the precipitation (mm d-1, \(P_{[t]}\)) and condensation (mm d-1, \(C_{[t]}\)) for the current day, to calculate the current soil moisture (mm, \(W_{n[t]}\)) as:
\[W_{n[t]} = W_{n[t-1]} + P_{[t]} + C_{[t]} - \textrm{AET}_{[t]}.\]When the resulting soil moisture exceeds the maximum capacity of the soil (
kWm), the excess is allocated to run off, leaving the soil saturated. Note that the soil moisture is not altered by subsurface flow: there is not vertical or horizontal transfer of water from the soil, only losses through evapotranspiration. Negative soil moisture values are replaced by zero.By default,
previous_wnis expected to provide estimates for all observations across all days in the model, butday_idxcan be set to provide an estimate for only one particular day, for use in iterating over time series.- Parameters:
day_idx – Optionally, the index of the date for which to calculate water balance.
previous_wn – Soil moisture estimates for the preceding day (mm)
- Returns:
A tuple of numpy arrays containing estimated AET, daily soil moisture and runoff.
- estimate_initial_soil_moisture(wn_init: ndarray[tuple[Any, ...], dtype[floating]] | DataArray | None = None, max_iter: int = 10, max_diff: float = 1.0, return_convergence: bool = False, verbose: bool = False) ndarray[tuple[Any, ...], dtype[floating]]
Estimate initial soil moisture.
This method uses the first year of data provided to a SplashModel instance to estimate initial values for the soil moisture data. The process assumes that the soil moisture cycle is stationary over the first year of data and iteratively updates an initial guess at start of year soil moisture until those values are within a given tolerance of the end of year values. The method cannot be run when less than one year of data is provided to the model.
The user can provide an array of initial values across sites, defaulting to an initial guess of zero soil moisture in all sites . The user can also control the maximum number of update iterations and the accepted tolerance for convergence. The method will normally fail if the estimates do not converge, but the
return_convergenceoption can be used to return the estimated soil moisture at each iteration regardless of the success of convergence.- Parameters:
wn_init – An optional estimate of the start of year soil moisture.
max_iter – The maximum number of iterations used to achieve convergence.
max_diff – The maximum acceptable difference between year start and year end soil moisture,
return_convergence – Optionally return an array of soil moistures at the end of each iteration even when convergence fails.
verbose – Optionally turn on detailed logging of the iteration process.
- Returns:
An array of the estimated starting soil moisture. If
return_convergenceis set to True, the returned array will have an additional dimension for each iteration of the equilibration loop.- Raises:
ValueError – The input data are of the wrong shape, contain invalid values or do not include at least a full year of data.
RuntimeError – The estimation fails to converge within the set number of iterations.
- evap: DailyEvapFluxes
Estimated evaporative fluxes for observations
- pa: ndarray[tuple[Any, ...], dtype[floating]]
The atmospheric pressure at sites, derived from elevation
- solar: DailySolarFluxes
Estimated solar fluxes for observations