HydPy-Evap-AET-MORSIM (actual evapotranspiration based on MORECS/LARSIM)

evap_aet_morsim is a submodel that supplies its main model with estimates of evapotranspiration from soils and evaporation from interception storages and water areas. It implements the MORECS equations (Thompson et al., 1981) for calculating actual evapotranspiration with some modifications following the LARSIM model (LEG, 2020).

The following list summarises its main components:

evap_aet_morsim requires additional data about the catchment’s current state, which it usually queries from its main model, if possible:

  • The current air temperature (required).

  • The amount of intercepted water (required).

  • The soil water content (required).

  • The snow cover degree (required).

  • The current snow albedo (optional).

  • The snow cover degree within the canopy of tree-like vegetation (optional).

The last two data types are optional. Hence, evap_aet_morsim works in combination with models as hland_96, which cannot estimate the current snow albedo and snow interception. Then, evap_aet_morsim relies on land type-specific albedo values (for snow-free conditions) and assumes zero snow interception.

The above data is usually supplied by the respective main model but can be supplied by sub-submodels, too, of which we make use in the following examples to simplify the necessary configuration. However, evap_aet_morsim also requires radiation-related data from another model (potential sunshine duration, actual sunshine duration, and global radiation), for which it generally requires a “real” submodel that complies with the RadiationModel_V1 or the RadiationModel_V4 interface.

Integration tests

Note

When new to HydPy, consider reading section Integration Tests first.

We prepare a simulation period of three days for the first examples:

>>> from hydpy import pub, Timegrid
>>> pub.timegrids = "2000-08-01", "2000-08-04", "1d"

According to the intended usage as a submodel, evap_aet_morsim requires no connections to any nodes. Hence, assigning a model instance to a blank Element instance is sufficient:

>>> from hydpy import Element
>>> from hydpy.models.evap_aet_morsim import *
>>> parameterstep("1h")
>>> element = Element("element")
>>> element.model = model

We will apply the following parameter set on all integration tests, which makes a few of them a little unrealistic but simplifies comparisons:

>>> nmbhru(1)
>>> hrutype(0)
>>> measuringheightwindspeed(10.0)
>>> albedo(0.2)
>>> leafareaindex(5.0)
>>> cropheight(10.0)
>>> surfaceresistance(40.0)
>>> emissivity(0.95)
>>> averagesoilheatflux(3.0)
>>> maxsoilwater(200.0)
>>> soilmoisturelimit(100.0)
>>> soilmoisturelimit(0.5)

We add submodels of type meteo_temp_io, meteo_psun_sun_glob_io, dummy_interceptedwater, dummy_soilwater, dummy_snowcover, dummy_snowalbedo, and dummy_snowycanopy that supply additional information on the catchment’s state instead of complicating the comparisons by introducing complex main or submodels:

>>> with model.add_tempmodel_v2("meteo_temp_io"):
...     hruarea(1.0)
...     temperatureaddend(0.0)
>>> with model.add_radiationmodel_v4("meteo_psun_sun_glob_io"):
...     pass
>>> with model.add_intercmodel_v1("dummy_interceptedwater"):
...     pass
>>> with model.add_soilwatermodel_v1("dummy_soilwater"):
...     pass
>>> with model.add_snowcovermodel_v1("dummy_snowcover"):
...     pass
>>> with model.add_snowalbedomodel_v1("dummy_snowalbedo"):
...     pass
>>> with model.add_snowycanopymodel_v1("dummy_snowycanopy"):
...     pass

Now, we can initialise an IntegrationTest object:

>>> from hydpy import IntegrationTest
>>> test = IntegrationTest(element)
>>> test.dateformat = "%d/%m"

The following time-constant meteorological input data will also apply to all integration tests except those with an hourly simulation step:

>>> inputs.windspeed.series = 2.0
>>> inputs.relativehumidity.series = 80.0
>>> inputs.atmosphericpressure.series = 1000.0
>>> model.tempmodel.sequences.inputs.temperature.series = 15.0
>>> model.radiationmodel.sequences.inputs.sunshineduration.series = 6.0
>>> model.radiationmodel.sequences.inputs.possiblesunshineduration.series = 16.0
>>> model.radiationmodel.sequences.inputs.globalradiation.series = 190.0

The data supplied by the “water content submodels” varies only for the amount of intercepted water:

>>> model.intercmodel.sequences.inputs.interceptedwater.series = [0.0], [3.0], [6.0]
>>> model.soilwatermodel.sequences.inputs.soilwater.series = 50.0

We set the snow-related data so that evap_aet_morsim assumes snow-free conditions for now:

>>> model.snowcovermodel.sequences.inputs.snowcover.series = 0.0
>>> model.snowycanopymodel.sequences.inputs.snowycanopy.series = 0.0
>>> model.snowalbedomodel.sequences.inputs.snowalbedo.series[:] = nan

non-tree vegetation

Both interception evaporation and soil evapotranspiration are relevant for hydrological response units with vegetation:

>>> interception(True)
>>> soil(True)
>>> tree(False)
>>> conifer(False)

The following test results demonstrate the general functioning of evap_aet_morsim. Due to the simulation step of one day, the “daily” averages or sums (e.g. DailyAirTemperature) are equal to their original counterparts (AirTemperature). The last two columns show how the Wigmosta et al. (1994) approach reduces soil evapotranspiration to prevent too high total evaporation estimates:

>>> test()
Click to see the table

deciduous trees

evap_aet_morsim does not distinguish between non-tree vegetation and deciduous trees for snow-free conditions. Hence, without adjusting other model parameters, activating the Tree flag while keeping the Conifer flag disabled does not change simulation results:

>>> tree(True)
>>> test()
Click to see the table

conifers

However, evap_aet_morsim applies additional equations for coniferous trees as explained in the documentation on method Calc_LanduseSurfaceResistance_V1. Hence, also enabling the Conifer flag causes some differences from the previous results:

>>> conifer(True)
>>> test()
Click to see the table

bare soil

If we simulate bare soil by disabling interception evaporation, soil water evapotranspiration becomes identical for all three days:

>>> interception(False)
>>> tree(False)
>>> conifer(False)
>>> test()
Click to see the table

sealed surface

For simulating sealed surfaces, one can disable the Soil flag:

>>> interception(True)
>>> soil(False)
>>> test()
Click to see the table

water area

Set the Interception and the Soil flag to False and the Water flag to True to disable the Penman-Monteith-based interception evaporation and soil evapotranspiration estimation and enable the Penman-based open water evaporation estimation:

>>> interception(False)
>>> water(True)
>>> test()
Click to see the table

snow on non-tree vegetation

Next, we show how evap_aet_morsim uses external information on the occurrence of snow to modify its outputs. Therefore, we set the degree of the snow cover (on the ground) to one and the snow albedo to 0.8:

>>> water(False)
>>> interception(True)
>>> soil(True)
>>> model.snowcovermodel.sequences.inputs.snowcover.series = 1.0
>>> model.snowalbedomodel.sequences.inputs.snowalbedo.series = 0.8

Now evap_aet_morsim uses the given snow albedo instead of the land type-specific albedo value for snow-free conditions for calculating the net shortwave radiation. However, this difference is irrelevant for non-tree vegetation, as any appearance of snow (on the ground) suppresses interception evaporation and soil evapotranspiration completely:

>>> tree(False)
>>> conifer(False)
>>> test()
Click to see the table

snow under tree canopies

In contrast, snow on the ground does not suppress interception evaporation and soil evapotranspiration for tree-like vegetation:

>>> tree(True)
>>> test()
Click to see the table

snow in tree canopies

However, snow interception in the canopy suppresses interception evaporation but not soil evapotranspiration:

>>> model.snowycanopymodel.sequences.inputs.snowycanopy.series = 1.0
>>> test()
Click to see the table

hourly simulation, land

For sub-daily step sizes, the averaging and summing mechanisms of evap_aet_morsim come into play. To demonstrate this, we prepare a simulation period of 24 hours:

>>> pub.timegrids = "2000-08-03", "2000-08-04", "1h"

We need to restore the values of all time-dependent parameters:

>>> for parameter in model.parameters.fixed:
...     parameter.restore()

This time, we prefer to let meteo_glob_morsim calculate the possible sunshine duration, the actual sunshine duration, and the global radiation on the fly instead of inserting pre-calculated values via meteo_psun_sun_glob_io. The following configuration agrees with the acre (summer) example of lland_knauf:

>>> with model.add_radiationmodel_v1("meteo_glob_morsim"):
...     latitude(54.1)
...     longitude(9.7)
...     angstromconstant(0.25)
...     angstromfactor(0.5)
...     angstromalternative(0.15)
>>> test = IntegrationTest(element)

The following meteorological input data also stems from the acre (summer) example of lland_knauf, too:

>>> inputs.atmosphericpressure.series = (
...     1015.0, 1015.0, 1015.0, 1015.0, 1015.0, 1015.0, 1015.0, 1015.0, 1016.0, 1016.0,
...     1016.0, 1016.0, 1016.0, 1016.0, 1016.0, 1016.0, 1016.0, 1016.0, 1016.0, 1016.0,
...     1016.0, 1016.0, 1017.0, 1017.0)
>>> inputs.windspeed.series = (
...     0.8, 0.8, 0.8, 0.8, 0.8, 0.6, 0.9, 0.9, 0.9, 1.3, 1.5, 1.2, 1.3, 1.5, 1.9, 1.9,
...     2.3, 2.4, 2.5, 2.5, 2.2, 1.7, 1.7, 2.3)
>>> inputs.relativehumidity.series = (
...     95.1, 94.9, 95.9, 96.7, 97.2, 97.5, 97.7, 97.4, 96.8, 86.1, 76.8, 71.8, 67.5,
...     66.1, 63.4, 62.4, 61.1, 62.1, 67.0, 74.5, 81.2, 86.9, 90.1, 90.9)
>>> model.radiationmodel.sequences.inputs.sunshineduration.series = (
...     0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.2, 0.5, 0.7, 0.8, 0.5, 0.4, 0.5,
...     0.5, 0.3, 0.1, 0.0, 0.0, 0.0, 0.0, 0.0)

evap_aet_morsim calculates “daily” averages and sums based on the last 24 hours. Hence, we must provide the corresponding logged data for the 24 hours preceding the simulation period. We take them from the acre (summer) example, too:

>>> test.inits = (
...     (logs.loggedsunshineduration,
...      [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.2, 0.1, 0.2, 0.1, 0.2, 0.2, 0.3, 0.0,
...       0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]),
...     (logs.loggedpossiblesunshineduration,
...      [0.0, 0.0, 0.0, 0.0, 0.5, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0,
...       1.0, 1.0, 1.0, 1.0, 1.0, 0.2, 0.0, 0.0, 0.0]),
...     (logs.loggedglobalradiation,
...      [0.0, 0.0, 0.0, 0.0, 0.0, 27.8, 55.6, 138.9, 222.2, 305.6, 333.3, 388.9,
...       527.8, 444.4, 250.0, 222.2, 166.7, 111.1, 55.6, 27.8, 0.0, 0.0, 0.0, 0.0]),
...     (logs.loggedairtemperature,
...      [[13.2], [13.2], [13.1], [12.6], [12.7], [13.0], [13.5], [14.8], [16.2],
...       [17.7], [18.8], [19.4], [20.4], [21.0], [21.5], [21.2], [20.4], [20.7],
...       [20.2], [19.7], [19.0], [18.0], [17.5], [17.1]]),
...     (logs.loggedrelativehumidity,
...      [95.1, 94.5, 94.8, 96.4, 96.6, 97.1, 97.1, 96.7, 92.2, 88.5, 81.1, 76.5, 75.1,
...       70.8, 68.9, 69.2, 75.0, 74.0, 77.4, 81.4, 85.3, 90.1, 92.3, 93.8]),
...     (logs.loggedwindspeed2m,
...      [0.8, 1.0, 1.2, 1.3, 0.9, 1.1, 1.3, 1.3, 1.9, 2.2, 1.8, 2.3, 2.4, 2.5, 2.4,
...       2.5, 2.1, 2.2, 1.7, 1.7, 1.3, 1.3, 0.7, 0.8]),
...     (model.radiationmodel.sequences.logs.loggedunadjustedglobalradiation,
...      [0.0, 0.0, 0.0, 0.0, 0.0, 27.777778, 55.555556, 138.888889, 222.222222,
...       305.555556, 333.333333, 388.888889, 527.777778, 444.444444, 250.0,
...       222.222222, 166.666667, 111.111111, 55.555556, 27.777778, 0.0, 0.0, 0.0,
...       0.0]))

The considered day is warm and snow-free:

>>> model.tempmodel.sequences.inputs.temperature.series = (
...     16.9, 16.6, 16.4, 16.3, 16.0, 15.9, 16.0, 16.6, 17.4, 19.0, 20.3, 21.4, 21.3,
...     21.8, 22.9, 22.7, 22.5, 21.9, 21.4, 20.7, 19.4, 17.8, 17.0, 16.4)
>>> model.snowcovermodel.sequences.inputs.snowcover.series = 0.0
>>> model.intercmodel.sequences.inputs.interceptedwater.series = 0.1
>>> model.snowycanopymodel.sequences.inputs.snowycanopy.series = 0.0
>>> model.snowalbedomodel.sequences.inputs.snowalbedo.series[:] = nan

For land areas, there is a clear diurnal evaporation pattern with a maximum around noon and a minimum with possible condensation at night because the Penman-Monteith equation uses only a few “daily” input values (see Return_Evaporation_PenmanMonteith_V1):

>>> test("evap_aet_morsim_hourly_simulation_land",
...      axis1=(fluxes.potentialinterceptionevaporation,
...             fluxes.interceptionevaporation, fluxes.soilevapotranspiration))
Click to see the table
Click to see the graph

hourly simulation, water

In contrast, the Penman equation applied for water areas uses only aggregated input, so its evaporation estimates show a more pronounced delay and no diurnal pattern:

>>> interception(False)
>>> soil(False)
>>> water(True)
>>> test("evap_aet_morsim_hourly_simulation_water", axis1=fluxes.waterevaporation)
Click to see the table
Click to see the graph
class hydpy.models.evap_aet_morsim.Model[source]

Bases: Main_TempModel_V1, Main_TempModel_V2B, Main_RadiationModel_V1, Main_RadiationModel_V4, Main_IntercModel_V1, Main_SoilWaterModel_V1, Main_SnowCoverModel_V1, Main_SnowyCanopyModel_V1, Main_SnowAlbedoModel_V1, Sub_ETModel, AETModel_V1

HydPy-Evap-AET-MORSIM (actual evapotranspiration based on MORECS/LARSIM).

The following “run methods” are called in the given sequence during each simulation step:
The following interface methods are available to main models using the defined model as a submodel:
The following “additional methods” might be called by one or more of the other methods or are meant to be directly called by the user:
Users can hook submodels into the defined main model if they satisfy one of the following interfaces:
  • TempModel_V1 Pure getter interface for using main models as sub-submodels.

  • TempModel_V2 Simple interface for determining the temperature in one step.

  • IntercModel_V1 Pure getter interface for using main models as sub-submodels or simple dummy models as submodels for querying the amount of intercepted water.

  • RadiationModel_V1 Simple interface for determining all data in one step.

  • RadiationModel_V4 Pure getter interface for possible sunshine duration, actual sunshine duration, and global radiation.

  • SoilWaterModel_V1 Pure getter interface for using main models as sub-submodels or simple dummy models as submodels for querying the soil water content.

  • SnowCoverModel_V1 Pure getter interface for using main models as sub-submodels or simple dummy models as submodels for querying the snow cover degree.

DOCNAME: DocName = ('Evap-AET-MORSIM ', 'actual evapotranspiration based on MORECS/LARSIM')
tempmodel: SubmodelProperty

Required submodel that complies with one of the following interfaces: TempModel_V1 or TempModel_V2.

radiationmodel: modeltools.SubmodelProperty

Required submodel that complies with one of the following interfaces: RadiationModel_V1 or RadiationModel_V4.

intercmodel: modeltools.SubmodelProperty

Required submodel that complies with the following interface: IntercModel_V1.

soilwatermodel: modeltools.SubmodelProperty

Required submodel that complies with the following interface: SoilWaterModel_V1.

snowcovermodel: modeltools.SubmodelProperty

Required submodel that complies with the following interface: SnowCoverModel_V1.

snowycanopymodel: modeltools.SubmodelProperty

Optional submodel that complies with the following interface: SnowyCanopyModel_V1.

snowalbedomodel: modeltools.SubmodelProperty

Optional submodel that complies with the following interface: SnowAlbedoModel_V1.

REUSABLE_METHODS: ClassVar[tuple[type[ReusableMethod], ...]] = ()
preparemethod2arguments: dict[str, tuple[tuple[Any, ...], dict[str, Any]]]
cymodel: CyModelProtocol | None
parameters: parametertools.Parameters
sequences: sequencetools.Sequences
masks: masktools.Masks
class hydpy.models.evap_aet_morsim.ControlParameters(master: Parameters, cls_fastaccess: type[FastAccessParameter] | None = None, cymodel: CyModelProtocol | None = None)

Bases: SubParameters

Control parameters of model evap_aet_morsim.

The following classes are selected:
  • NmbHRU() The number of separately modelled hydrological response units [-].

  • HRUType() Hydrological response unit type [-].

  • Water() A flag that indicates whether the individual zones are water areas or not.

  • Interception() A flag that indicates whether interception evaporation is relevant for the individual zones.

  • Soil() A flag that indicates whether soil evapotranspiration is relevant for the individual zones.

  • Tree() A flag that indicates whether the individual zones contain tree-like vegetation.

  • Conifer() A flag that indicates whether the individual zones contain conifer-like vegetation.

  • MeasuringHeightWindSpeed() The height above ground of the wind speed measurements [m].

  • Albedo() Earth surface albedo [-].

  • LeafAreaIndex() Leaf area index [-].

  • CropHeight() Crop height [m].

  • Emissivity() The emissivity of the land surface [-].

  • AverageSoilHeatFlux() Monthly averages of the soil heat flux [W/m²].

  • SurfaceResistance() Surface resistance of water areas, sealed areas, and vegetation with sufficient water supply [s/m].

  • MaxSoilWater() Maximum soil water content [mm].

  • SoilMoistureLimit() Relative soil moisture limit for potential evapotranspiration [-].

class hydpy.models.evap_aet_morsim.DerivedParameters(master: Parameters, cls_fastaccess: type[FastAccessParameter] | None = None, cymodel: CyModelProtocol | None = None)

Bases: SubParameters

Derived parameters of model evap_aet_morsim.

The following classes are selected:
  • MOY() References the “global” month of the year index array [-].

  • Hours() The length of the actual simulation step size in hours [h].

  • Days() The length of the actual simulation step size in days [d].

  • NmbLogEntries() The number of log entries required for a memory duration of 24 hours [-].

class hydpy.models.evap_aet_morsim.FactorSequences(master: Sequences, cls_fastaccess: type[TypeFastAccess_co] | None = None, cymodel: CyModelProtocol | None = None)

Bases: FactorSequences

Factor sequences of model evap_aet_morsim.

The following classes are selected:
class hydpy.models.evap_aet_morsim.FixedParameters(master: Parameters, cls_fastaccess: type[FastAccessParameter] | None = None, cymodel: CyModelProtocol | None = None)

Bases: SubParameters

Fixed parameters of model evap_aet_morsim.

The following classes are selected:
class hydpy.models.evap_aet_morsim.FluxSequences(master: Sequences, cls_fastaccess: type[TypeFastAccess_co] | None = None, cymodel: CyModelProtocol | None = None)

Bases: FluxSequences

Flux sequences of model evap_aet_morsim.

The following classes are selected:
class hydpy.models.evap_aet_morsim.InputSequences(master: Sequences, cls_fastaccess: type[TypeFastAccess_co] | None = None, cymodel: CyModelProtocol | None = None)

Bases: InputSequences

Input sequences of model evap_aet_morsim.

The following classes are selected:
class hydpy.models.evap_aet_morsim.LogSequences(master: Sequences, cls_fastaccess: type[TypeFastAccess_co] | None = None, cymodel: CyModelProtocol | None = None)

Bases: LogSequences

Log sequences of model evap_aet_morsim.

The following classes are selected: