Calculate FAO-56 Reference Evapotranspiration (ET₀)
Daily NARO inputs and monthly summaries, 2008–2018
Scope and relation to PET
This chapter uses the FAO-56 Penman–Monteith equation to calculate daily reference evapotranspiration (ET₀) and combine it into monthly and seasonal totals. FAO-56 refers to Irrigation and Drainage Paper 56, the technical guide published by the Food and Agriculture Organization of the United Nations (FAO).
All examples run on this page use synthetic data or a published FAO example. The page does not include actual NARO data or maps made from them.
| Property | Existing Thornthwaite PET | This ET0 workflow |
|---|---|---|
| Source | JMA Mesh Climatology 2020, distributed by MLIT | NARO Agro-Meteorological Grid Square Data |
| Period | 1991–2020 monthly climate normals | Daily inputs, 2008–2018 |
| Inputs | Monthly mean temperature and latitude/day length | Daily mean, maximum and minimum temperature, solar radiation, RH, wind, elevation, latitude and date |
| Quantity | Temperature-based potential evapotranspiration | Reference evapotranspiration from a hypothetical well-watered grass surface |
| AI denominator | Thornthwaite PET | FAO-56 ET0 |
Neither quantity is measured forest evapotranspiration. Comparing these products mixes method, input and period differences; it does not isolate a method effect. The existing PET and AI chapters retain their original definitions and inputs. Precipitation is needed for AI, but is not an input to the ET0 equation.
Before using the data, read NARO’s usage terms and application guide. Each user should apply for access and handle the data according to the terms. This site provides calculation code and synthetic examples, not NARO data.
Permission to share the ET₀ and precipitation tables prepared for this project, or maps made from them, has not been confirmed, so they are not published here. The code and documentation licenses do not grant permission to redistribute weather data. Keep original files unchanged and actual data outside Git.
Daily equation and assumptions
The implementation uses FAO-56 equation 6:
ET0 is in mm/day; T is daily mean temperature (°C); Rn and G are net radiation and soil heat flux (MJ/m²/day); u2 is wind speed at 2 m (m/s); es and ea are saturation and actual vapour pressure (kPa); Delta and gamma are the slope of the saturation vapour-pressure curve and psychrometric constant (kPa/°C).
- Daily soil heat flux G is zero; reference albedo is 0.23.
- Saturation pressure is averaged from daily maximum and minimum temperature. Actual pressure uses mean relative humidity, rather than paired RH extremes.
- Source wind is assumed to be at 10 m and converted to 2 m. The source measurement height remains unconfirmed.
- Interior gaps of at most two days in RH and wind are linearly interpolated separately within each calendar year. Longer gaps and gaps at year edges remain missing. Other input variables are not interpolated.
- Missing inputs propagate to daily ET0, monthly totals and multi-year means. Sums and means do not use
na.rm = TRUE. - Negative computed ET0 is retained, without clipping to zero.
With , mean-RH vapour pressure is . Wind conversion is with z = 10 m. These are FAO-56 equations 19 and 47. Mean RH is less preferable than paired humidity extremes when available. The available Tmin vapour-pressure fallback is not used to fill RH here.
Net radiation uses absorbed shortwave and FAO-estimated longwave radiation; the solar/clear-sky radiation ratio is capped at 1. Pressure and gamma depend on elevation. Solar geometry retains the FAO denominator 365 even in leap years; monthly sums include every actual date, including 29 February. Polar day/night is outside the implementation’s scope.
Monthly mean weather cannot exactly reproduce daily ET0 because the equation is nonlinear. Changing wind or humidity assumptions requires authorized daily inputs and recalculation, rather than the monthly tables.
Relation to Zomer et al. (2022)
The global database described by Zomer et al. also uses FAO Penman–Monteith reference evapotranspiration (Zomer et al. 2022). However, it uses WorldClim monthly climate data for 1970–2000, while this chapter uses daily NARO data for 2008–2018. The basic method is shared, but the inputs, time steps and resulting values are not identical.
Published FAO check
The Uccle example (6 July, FAO-56 example 18) uses the published actual vapour pressure, rather than the NARO mean-RH approximation. The expected result is about 3.9 mm/day. This checks implementation, not the accuracy of every cell.
example_et0 <- pipeline_p0_et0_fao56(
mean_temperature = matrix(16.9),
maximum_temperature = matrix(21.5),
minimum_temperature = matrix(12.3),
solar_radiation = matrix(22.07),
actual_vapour_pressure = matrix(1.409),
wind_2m = matrix(pipeline_p0_wind_at_2m(10 * 1000 / 3600, 10)),
elevation = 100,
latitude = 50.80,
day_of_year = 187
)
stopifnot(abs(as.numeric(example_et0) - 3.9) < 0.08)
data.frame(et0_mm_day = as.numeric(example_et0)) et0_mm_day
1 3.879455
Synthetic daily CSV
The CSV below contains three invented days for one cell. They do not constitute a July total. Matrices have one row per cell and one column per day. Latitude and elevation have one entry per row; day of year has one per column.
daily <- read.csv("data/examples/et0_daily_synthetic.csv")
as_row <- function(x) matrix(x, nrow = 1)
daily$et0_mm_day <- as.numeric(pipeline_p0_et0_fao56(
mean_temperature = as_row(daily$mean_temperature_c),
maximum_temperature = as_row(daily$maximum_temperature_c),
minimum_temperature = as_row(daily$minimum_temperature_c),
solar_radiation = as_row(daily$solar_radiation_mj_m2_day),
actual_vapour_pressure = as_row(pipeline_p0_actual_vapour_pressure_rh(
daily$relative_humidity_percent,
daily$maximum_temperature_c,
daily$minimum_temperature_c
)),
wind_2m = as_row(pipeline_p0_wind_at_2m(daily$wind_10m_m_s, 10)),
elevation = daily$elevation_m[1],
latitude = daily$latitude_degrees[1],
day_of_year = as.integer(format(as.Date(daily$date), "%j"))
))
daily[c("date", "et0_mm_day")] date et0_mm_day
1 2008-07-05 3.513454
2 2008-07-06 3.784751
3 2008-07-07 4.002401
For gaps, call pipeline_p0_fill_short_gaps() separately on each year’s RH and wind before the equation, as in the local bundle’s export_yearly.R. The low-level pipeline_p0_monthly_sums() does not check calendar completeness: supply every date exactly once, in matching order, for a complete month (28/29/30/31 days) before reporting a monthly total.
Local monthly products
Year-specific files contain 12 monthly totals per mesh/year, covering January 2008 through December 2018 (132 months per variable per cell). The means table contains 12 mean monthly totals: July is the mean of the eleven July totals. These are 11-year averages, not standard 30-year climate normals.
The prepared bundle covers 377,449 meshes, 153 primary-mesh blocks and 4,151,939 mesh-year rows. Its verification.csv records agreement of yearly averages with the supplied means within 1e-8 mm (maximum difference 4.77484718430787e-12 mm). This verifies aggregation, not meteorological accuracy. SHA256SUMS records bundle integrity.
Set PROJECT_DATA_DIR in the ignored .Renviron and restart R after editing. The following example is deliberately not executed during rendering:
bundle <- et0_data_dir() # PROJECT_DATA_DIR/naro-et0/2008-2018
normal <- read_monthly_csv(file.path(
bundle,
"data",
"monthly_normals_2008_2018.csv.gz"
))
yearly_files <- list.files(
file.path(bundle, "data", "yearly"),
pattern = "^monthly_[0-9]{4}_2008_2018[.]csv[.]gz$",
full.names = TRUE
)
# One block, not all Japan:
one_block <- read_monthly_csv(yearly_files[1])
period_summary(one_block, 7:9, "Jul-Sep")
winter_summary(one_block)Base R reads gzip CSVs directly. read_monthly_csv() keeps mesh3_code as character. et0_data_dir() fails if the environment variable is unset. Monthly columns are et0_fao56_Jan through et0_fao56_Dec and precipitation_Jan through precipitation_Dec, in mm/month. See the bundle’s data-dictionary.csv and STATUS.md. Annual totals in the means table sum its 12 months. et0_filled_days is the maximum of separate RH and wind filled-day counts per cell/year, summed across 11 years in the means table. It is not their union or a count of missing days in all input variables.
Month windows and AI
For July–September, add the three monthly ET₀ totals and the three precipitation totals:
Here, is the month number, and are the monthly totals in mm, and denotes July–September. AI is dimensionless. Do not multiply the monthly totals by days again. AI is sum(P)/sum(ET0) over identical months, rather than the mean of monthly AI. Missing months propagate and non-positive or missing ET0 denominators give NA. P−ET0 is a climatic water balance, not a soil-water balance model.
The synthetic CSV below has two invented identifiers and three years of illustrative monthly totals, independent of the daily example above.
yearly <- read_monthly_csv("data/examples/et0_monthly_synthetic.csv")
jas <- period_summary(yearly, 7:9, "Jul-Sep")
jas mesh3_code year period et0_mm precipitation_mm aridity_index
1 00000001 2008 Jul-Sep 54 168 3.1111111
2 00000002 2008 Jul-Sep 69 198 2.8695652
3 00000001 2009 Jul-Sep 108 252 2.3333333
4 00000002 2009 Jul-Sep 123 282 2.2926829
5 00000001 2010 Jul-Sep 162 84 0.5185185
6 00000002 2010 Jul-Sep 177 114 0.6440678
water_balance_mm
1 114
2 129
3 144
4 159
5 -78
6 -63
columns <- c(
paste0("et0_fao56_", month.abb),
paste0("precipitation_", month.abb)
)
normal <- yearly[yearly$year == 2008, "mesh3_code", drop = FALSE]
for (column in columns) {
normal[[column]] <- vapply(
normal$mesh3_code,
function(key) {
mean(yearly[yearly$mesh3_code == key, column]) # no na.rm
},
numeric(1)
)
}
climate_jas <- period_summary(normal, 7:9, "Jul-Sep")
data.frame(
mesh3_code = normal$mesh3_code,
ratio_of_climatological_sums = climate_jas$aridity_index,
mean_yearly_ai = vapply(
normal$mesh3_code,
function(key) {
mean(jas$aridity_index[jas$mesh3_code == key])
},
numeric(1)
)
) mesh3_code ratio_of_climatological_sums mean_yearly_ai
00000001 00000001 1.555556 1.987654
00000002 00000002 1.609756 1.935439
A ratio of climatological sums (the original production definition) generally differs from the mean yearly AI. Averaging monthly AI also changes the weighting:
ratio_of_sums mean_monthly_ai
1 1.666667 2.25
Winter crosses years
Winter Y combines December Y−1 with January-February Y. period_summary(data, c(12, 1, 2)) only selects months within the same row. Use winter_summary() on year-specific rows for a chronological winter. It aligns by mesh and year, preserves input rows, and labels winter by the year of January-February. Missing prior December yields NA.
winter_summary(yearly) mesh3_code year period et0_mm precipitation_mm aridity_index
1 00000001 2008 Dec(previous year)-Feb NA NA NA
2 00000002 2008 Dec(previous year)-Feb NA NA NA
3 00000001 2009 Dec(previous year)-Feb 68 193 2.838235
4 00000002 2009 Dec(previous year)-Feb 83 223 2.686747
5 00000001 2010 Dec(previous year)-Feb 113 139 1.230088
6 00000002 2010 Dec(previous year)-Feb 128 169 1.320312
water_balance_mm
1 NA
2 NA
3 125
4 140
5 26
6 41
For the complete real export, winters ending in 2009–2018 are available. Winter 2008 lacks December 2007; winter 2019 lacks January-February 2019 and has no anchor row. Summing December, January and February means describes an average seasonal window, not a specific winter or its variability.
Reproduction and verification
R/pipeline_p0_et0.R is unchanged from the standalone bundle, originating at japanese-trees-sdms commit 1731a9e9fcaba87d718168e5eed0aeaed68b2acd. The gap helper is extracted unchanged from its R/pipeline_p0_naro.R. R/et0_support.R adds CSV reading, paths and seasonal summaries. No new package dependency is required. The export environment was R 4.5.3 and terra 1.8.86, as recorded in the bundle’s build-environment.csv. Optional regeneration from authorized NetCDFs uses terra and the local bundle scripts. Raw inputs remain immutable. This integration does not retrain an SDM.
Download the calculation functions, support functions, synthetic daily CSV and synthetic monthly CSV.
From the project root:
Rscript --vanilla tests/test_et0.R
Rscript tests/verify_et0_local.R
quarto render notebooks/calculate_ET0.qmd --profile en
quarto render notebooks/calculate_ET0.qmd --profile jaThe first script uses base R and synthetic inputs. The second reads all local yearly tables via PROJECT_DATA_DIR, validates keys and coverage, and compares their means with the supplied table. It prints aggregate statistics only. Rendering this chapter never reads NARO tables.
References
- Allen et al. (1998), Crop evapotranspiration, FAO Paper 56: equation and surface, inputs, worked examples.
- NARO Agro-Meteorological Grid Square Data System, obtained using agrmesh (Murakami and Nagasaki, 2023).
- NARO usage terms and application guide: apply for access yourself and check the conditions before using the data.
For further reading:
- Global ET₀ and AI: Zomer et al. (2022) describe the global database introduced above (Zomer et al. 2022).
- How NARO temperature and precipitation data are made: Ohno et al. (2016) explain how observations, numerical forecasts and climate normals are combined to produce daily 1 km grid data (大野 et al. 2016).
- Snow modelling with NARO data: Kominami et al. (2015) describe a snow water equivalent model using grid weather data (小南 et al. 2015). This is background reading on NARO data applications; snow water equivalent is not an input to the ET₀ calculation in this chapter.