Skip to content

Commit

Permalink
Merge pull request #6 from Open-ET/era5
Browse files Browse the repository at this point in the history
ERA5-Land
  • Loading branch information
cgmorton authored Nov 15, 2022
2 parents b99f198 + 30e3a01 commit 9f0509d
Show file tree
Hide file tree
Showing 7 changed files with 286 additions and 1 deletion.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ __pycache__/
*$py.class

# Distribution / packaging
build/
dist/
.eggs/
*.egg-info/
Expand Down
36 changes: 36 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,42 @@ For the daily function, the RTMA collection must be filtered to a single 24 hour
print('ETr: {:.2f} mm'.format(float(etr['etr'])))
ERA5-Land
---------

Helper functions for computing daily/hourly ETo/ETr for `ERA5-Land <https://cds.climate.copernicus.eu/cdsapp#!/dataset/reanalysis-era5-land>`__ images are available.

For the daily function, the ERA5-Land collection must be filtered to a single 24 hour period.

.. code-block:: console
import ee
import openet.refetgee
source_coll = ee.ImageCollection('ECMWF/ERA5_LAND/HOURLY')\
.filterDate('2015-07-01', '2015-07-02')
etr = openet.refetgee.Daily.era5_land(source_coll).etr\
.reduceRegion(reducer=ee.Reducer.first(),
geometry=ee.Geometry.Point(-118.77388, 39.4575),
scale=1000)\
.getInfo()
print('ETr: {:.2f} mm'.format(float(etr['etr'])))
.. code-block:: console
import ee
import openet.refetgee
source_img = ee.Image('ECMWF/ERA5_LAND/HOURLY/20150701T20')
etr = openet.refetgee.Hourly.era5_land(source_img).etr\
.reduceRegion(reducer=ee.Reducer.first(),
geometry=ee.Geometry.Point(-118.77388, 39.4575),
scale=1000)\
.getInfo()
print('ETr: {:.2f} mm'.format(float(etr['etr'])))
Input Parameters
================

Expand Down
2 changes: 1 addition & 1 deletion openet/refetgee/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from .daily import Daily
from .hourly import Hourly

__version__ = '0.5.2'
__version__ = '0.6.0'
77 changes: 77 additions & 0 deletions openet/refetgee/daily.py
Original file line number Diff line number Diff line change
Expand Up @@ -770,6 +770,83 @@ def rtma(cls, input_coll, rs=None, zw=None, elev=None, lat=None,
rso_type=rso_type,
)

@classmethod
def era5_land(cls, input_coll, zw=None, elev=None, lat=None,
method='asce', rso_type=None):
"""Initialize daily RefET from a hourly ERA5-Land image collection
Parameters
----------
input_coll : ee.ImageCollection
Collection of ERA5-Land hourly images for a single day from the
collection ECMWF/ERA5_LAND/HOURLY.
zw : ee.Number, optional
Wind speed height [m] (the default is 10).
elev : ee.Image or ee.Number, optional
Elevation image [m]. The OpenET ERA5-Land elevation image
(projects/openet/assets/meteorology/era5land/elevation)
will be used if not set.
lat : ee.Image or ee.Number
Latitude image [degrees]. The OpenET ERA5-Land latitude image
(projects/openet/assets/meteorology/era5land/latitude)
will be used if not set.
method : {'asce' (default), 'refet'}, optional
Specifies which calculation method to use.
* 'asce' -- Calculations will follow ASCE-EWRI 2005.
* 'refet' -- Calculations will follow RefET software.
rso_type : {None (default), 'full' , 'simple'}, optional
Specifies which clear sky solar radiation (Rso) model to use.
* None -- Rso type will be determined from "method" parameter
* 'full' -- Full clear sky solar formulation
* 'simple' -- Simplified clear sky solar formulation
Notes
-----
Temperatures are converted from K to C.
Solar radiation is converted from J m-2 to MJ m-2 day-1.
Actual vapor pressure is computed from dew point temperature.
"""
input_coll = ee.ImageCollection(input_coll)
start_date = ee.Date(ee.Image(input_coll.first()).get('system:time_start'))

if zw is None:
zw = ee.Number(10)
if elev is None:
elev = ee.Image('projects/openet/assets/meteorology/era5land/elevation')\
.rename(['elevation'])
if lat is None:
lat = ee.Image('projects/openet/assets/meteorology/era5land/latitude')\
# lat = ee.Image('projects/openet/assets/meteorology/era5land/elevation')\
# .multiply(0).add(ee.Image.pixelLonLat().select('latitude'))\
# .rename(['latitude'])

def wind_magnitude(input_img):
"""Compute hourly wind magnitude from vectors"""
return ee.Image(input_img.select(['u_component_of_wind_10m'])).pow(2)\
.add(ee.Image(input_img.select(['v_component_of_wind_10m'])).pow(2))\
.sqrt().rename(['wind_10m'])

wind_img = ee.Image(ee.ImageCollection(input_coll.map(wind_magnitude)).mean())

return cls(
tmax=input_coll.select(['temperature_2m']).max().subtract(273.15),
tmin=input_coll.select(['temperature_2m']).min().subtract(273.15),
ea=calcs._sat_vapor_pressure(
input_coll.select(['dewpoint_temperature_2m']).mean().subtract(273.15)
),
# TODO: Check that solar does not need additional conversion
rs=input_coll.select(['surface_solar_radiation_downwards_hourly'])
.sum().divide(1000000),
uz=wind_img,
zw=zw,
elev=elev,
lat=lat,
doy=ee.Number(start_date.getRelative('day', 'year')).add(1).double(),
method=method,
rso_type=rso_type,
)

# @classmethod
# def prism(cls, input_img, lat=None):
# """Initialize daily RefET from a PRISM image
Expand Down
73 changes: 73 additions & 0 deletions openet/refetgee/hourly.py
Original file line number Diff line number Diff line change
Expand Up @@ -400,3 +400,76 @@ def rtma(cls, input_img, rs=None, zw=None, elev=None, lat=None, lon=None,
time=ee.Number(start_date.get('hour')),
method=method,
)

@classmethod
def era5_land(cls, input_img, zw=None, elev=None, lat=None, lon=None,
method='asce'):
"""Initialize hourly RefET from an ERA5-Land image
Parameters
----------
input_img : ee.Image
ERA5-Land hourly image from the collection ECMWF/ERA5_LAND/HOURLY.
zw : ee.Number, optional
Wind speed height [m] (the default is 10).
elev : ee.Image or ee.Number, optional
Elevation image [m]. The OpenET ERA5-Land elevation image
(projects/openet/assets/meteorology/era5land/elevation)
will be used if not set.
lat : ee.Image or ee.Number
Latitude image [degrees]. The OpenET ERA5-Land latitude image
(projects/openet/assets/meteorology/era5land/latitude)
will be used if not set.
lon : ee.Image or ee.Number
Longitude image [degrees]. The OpenET ERA5-Land longitude image
(projects/openet/assets/meteorology/era5land/longitude)
will be used if not set.
method : {'asce' (default), 'refet'}, optional
Specifies which calculation method to use.
* 'asce' -- Calculations will follow ASCE-EWRI 2005.
* 'refet' -- Calculations will follow RefET software.
Notes
-----
Temperatures are converted from K to C.
Solar radiation is converted from J m-2 to MJ m-2 day-1.
Actual vapor pressure is computed from dew point temperature.
"""
start_date = ee.Date(input_img.get('system:time_start'))

if zw is None:
zw = ee.Number(10)
if elev is None:
elev = ee.Image('projects/openet/assets/meteorology/era5land/elevation')\
.rename(['elevation'])
if lat is None:
lat = ee.Image('projects/openet/assets/meteorology/era5land/latitude')
# lat = ee.Image('projects/openet/assets/meteorology/era5land/elevation')\
# .multiply(0).add(ee.Image.pixelLonLat().select('latitude'))\
# .rename(['latitude'])
if lon is None:
lon = ee.Image('projects/openet/assets/meteorology/era5land/longitude')
# lon = ee.Image('projects/openet/assets/meteorology/era5land/elevation')\
# .multiply(0).add(ee.Image.pixelLonLat().select('longitude'))\
# .rename(['longitude'])

return cls(
tmean=input_img.select(['temperature_2m']).subtract(273.15),
ea=calcs._sat_vapor_pressure(
input_img.select(['dewpoint_temperature_2m']).subtract(273.15)
),
rs=input_img.select(['surface_solar_radiation_downwards_hourly'])
.divide(1000000),
uz=input_img.select(['u_component_of_wind_10m']).pow(2)
.add(input_img.select(['v_component_of_wind_10m']).pow(2))
.sqrt().rename(['wind_10m']),
zw=zw,
elev=elev,
lat=lat,
lon=lon,
doy=ee.Number(start_date.getRelative('day', 'year')).add(1).double(),
# time=ee.Number(start_date.getRelative('hour', 'day')),
time=ee.Number(start_date.get('hour')),
method=method,
)
Loading

0 comments on commit 9f0509d

Please sign in to comment.