FAO-56基準蒸発散量(ET₀)の計算

NAROの日別入力と月別集計(2008〜2018年)

Read this page in English (EN)

対象範囲とPETとの違い

この章では、FAO-56 Penman–Monteith式を使って、1日ごとの基準蒸発散量(ET₀)を計算し、 月や季節ごとに集計する方法を説明します。 入力には農研機構メッシュ農業気象データを使います。 FAO-56は、この式を説明したFAO(国連食糧農業機関)の灌漑排水技術資料第56号を指します。

実データの利用には申請が必要なため、ページ内で実行する例には架空データまたはFAOの公開計算例を使います。 NAROの実データや、それを使った地図は掲載していません。

項目 従来のThornthwaite法によるPET この章のET₀
データ JMAメッシュ平年値2020(国土交通省から提供) 農研機構メッシュ農業気象データ
期間 1991〜2020年の月別平年値 2008〜2018年の日別値
入力 月平均気温と緯度・日長 日平均・最高・最低気温、日射量、相対湿度、風速、標高、緯度、日付
物理量 気温に基づく潜在蒸発散量 十分に給水された仮想的な草地面からの基準蒸発散量
AIの分母 Thornthwaite法のPET FAO-56法のET₀

いずれも森林の実蒸発散量の測定値ではありません。 両者は計算方法だけでなく、入力データや対象期間も異なります。 値に差があっても、計算方法だけが原因とはいえません。 降水量はAIに必要ですが、ET₀式そのものの入力には含まれません。

警告NAROデータを利用する方へ

利用にあたっては、農研機構の利用案内・利用規約を確認し、 データの扱いに注意してください。利用する際は、各自で利用申請を行ってください。 このサイトで提供するのは計算コードと架空の入力例であり、NAROの実データは配布していません。

今回作成したET₀・降水量の表や、それらを使った地図を外部へ共有できるかは未確認のため、ここでは公開していません。 コードや文書のライセンスがあっても、気象データを自由に再配布できるわけではありません。 元のファイルは変更せずに保管し、実データはGitの管理対象に含めないでください。

source("R/et0_support.R")
source("R/pipeline_p0_et0.R")

日別計算式と仮定

ET₀は、十分に水が供給された草地を基準にした蒸発散量です。 ここでは、気温に加えて日射量・湿度・風速を使うFAO-56の式6で計算します。

ET0=0.408Δ(Rn−G)+γ900T+273u2(es−ea)Δ+γ(1+0.34u2). ET_0 = \frac{0.408\Delta(R_n-G) + \gamma\frac{900}{T+273}u_2(e_s-e_a)} {\Delta+\gamma(1+0.34u_2)}.

式に出てくる記号と単位は次のとおりです。

記号 意味 単位
ET0ET_0 1日あたりの基準蒸発散量 mm/日
TT 日平均気温 ℃
RnR_n 地表が受け取る放射と放出する放射の差(純放射) MJ/m²/日
GG 地中へ出入りする熱(地中熱流量) MJ/m²/日
u2u_2 地上2 mの風速 m/s
ese_s, eae_a 飽和水蒸気圧と実際の水蒸気圧 kPa
Δ\Delta 飽和水蒸気圧が気温とともに変わる割合 kPa/℃
γ\gamma 気圧などに応じて決まる乾湿計定数 kPa/℃

今回の計算では、次の仮定と欠測の扱いを用います。

  • 日別の地中熱流量Gは0、地表が日射を反射する割合(アルベド)は0.23とします。
  • 飽和水蒸気圧は日最高・最低気温から求めた値の平均です。実水蒸気圧には、 最高・最低相対湿度の組ではなく日平均相対湿度を用います。
  • 元の風速は地上10 mの値と仮定し、2 mへ換算します。NAROの元データが表す高度は未確認です。
  • 相対湿度と風速は、同じ年の前後の値が揃っている場合に限り、連続2日までの欠測を直線で補います。 3日以上続く欠測や、年初・年末で前後の値が揃わない欠測は残します。他の入力変数は補いません。
  • 必要な値に欠測が残る日は、ET₀も欠測になります。その日を含む月合計や、その月を含む多年平均も欠測とします。 欠測を除いて計算するna.rm = TRUEは使いません。
  • 計算された負のET₀は0に切り上げず、そのまま保持します。

e∘(T)=0.6108exp⁡[17.27T/(T+237.3)]e^\circ(T)=0.6108\exp[17.27T/(T+237.3)]として、日平均相対湿度による近似は ea=(RHmean/100)[e∘(Tmax)+e∘(Tmin)]/2e_a=(RH_{mean}/100)[e^\circ(T_{max})+e^\circ(T_{min})]/2です。 風速換算はu2=uz4.87/ln⁡(67.8z−5.42)u_2=u_z4.87/\ln(67.8z-5.42)、z = 10 mとします。 FAO-56の式19・47に対応します。 最高・最低相対湿度が得られる場合は、それらを使う方法が望まれます。 コードには最低気温から水蒸気圧を推定する関数もありますが、今回の湿度の欠測を補うためには使いません。

純放射は、地表が吸収する日射と、FAO式で推定した長波放射から求めます。 日射量を晴天時の日射量で割った比は、1を上限とします。気圧と乾湿計定数は標高から計算します。 太陽位置を求める式では、うるう年も分母を365とします。 ただし月合計には、2月29日を含めて実際にあるすべての日を入れます。白夜・極夜は対象としていません。

この式では、入力値とET₀が単純な比例関係になりません。 そのため、月平均の気象値を式に入れた結果は、日別に計算してから合計した結果とは一致しません。 風速や湿度の仮定を変えたい場合は、利用許可を得た日別データから計算し直す必要があります。

Zomerら(2022)のデータベースとの関係

Zomerらの全球データベースも、FAO Penman–Monteith法によるET₀を基礎にしています (Zomer ほか 2022年)。 ただし、同じ式を基礎にしていても、入力データや時間の単位まで同じとは限りません。 ZomerらはWorldClimの1970〜2000年の月別気候値を使い、この章ではNAROの2008〜2018年の日別値を使います。 したがって、基本となる計算方法は共通ですが、計算手順や得られる値がすべて同じとはいえません。

FAOの公開計算例との照合

Uccleの例(7月6日、FAO-56の例18)は、NARO用の日平均相対湿度による近似ではなく、 公開されている実水蒸気圧を使います。期待値は約3.9 mm/日です。 この例で確認できるのは、コードが公開計算例に近い値を返すことです。 各メッシュのET₀が、現地の値をどれほど正確に表すかを確かめるものではありません。

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

架空の日別CSVによる例

次のCSVは1地点・3日分の架空入力です。7月全体の合計を意味しません。 計算関数へ渡す行列では、各行を地点、各列を日とします。 緯度と標高は地点ごとに、年初から数えた日番号(通日)は日ごとに指定します。

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

欠測がある場合は、ローカルのexport_yearly.Rと同様に、年ごとの相対湿度と風速に それぞれpipeline_p0_fill_short_gaps()を使ってから計算します。 月合計を求めるpipeline_p0_monthly_sums()自体は、日付の抜けや重複を調べません。 計算前に、その月の全日付が1回ずつ含まれ、すべての入力で日付の順序が揃っていることを確認してください。

ローカルの月別データ

年別ファイルには、メッシュと年ごとに、12か月分の月合計が入っています。 対象は2008年1月〜2018年12月で、1メッシュ・1変数あたり132か月分です。 平均表には、11年分の月合計を月ごとに平均した値が入っています。 例えば7月の値は11個の7月合計の平均であり、標準的な30年平年値ではありません。

手元で用意したデータは377,449メッシュ分で、153個の一次メッシュのブロックに分かれています。 年別表は1メッシュ・1年を1行とし、合計4,151,939行あります。 verification.csvには、年別表から再計算した平均と既存平均表との差が1e-8 mm未満 (最大4.77484718430787e-12 mm)であることが記録されています。 この比較は、集計し直しても同じ値になることを確かめたものです。気象値そのものの正確さを保証するものではありません。 SHA256SUMSは、ファイルの内容が変わっていないかを調べるための一覧です。

Git管理外の.RenvironにPROJECT_DATA_DIRを設定し、変更後はRを再起動します。 以下は、各自で利用申請を行い、同じ構成のデータを用意した場合の読込例です。 ページのレンダリング時には実行しません。

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でgzip圧縮CSVを直接読めます。read_monthly_csv()はmesh3_codeを文字列として保持します。 環境変数が未設定の場合、et0_data_dir()は停止します。 月別列はet0_fao56_Jan〜et0_fao56_Decとprecipitation_Jan〜precipitation_Decで、 単位はmm/月です。ローカル一式のdata-dictionary.csvとSTATUS.mdも参照してください。 平均表の年合計は、12か月の値を足したものです。 et0_filled_daysは、各メッシュ・年について、湿度を補った日数と風速を補った日数のうち大きい方です。 平均表では、この日数を11年分足しています。湿度か風速のどちらかを補った日の総数や、 すべての入力変数の欠測日数を表す値ではありません。

集計する月の選び方と乾燥指数AI

例えば7〜9月を集計する場合は、その3か月のET₀と降水量をそれぞれ足します。

ET0,7:9=∑m=79ET0,m=ET0,7+ET0,8+ET0,9,P7:9=∑m=79Pm=P7+P8+P9,AI7:9=P7:9ET0,7:9=P7+P8+P9ET0,7+ET0,8+ET0,9. \begin{aligned} ET_{0,7:9} &= \sum_{m=7}^{9} ET_{0,m} = ET_{0,7} + ET_{0,8} + ET_{0,9}, \\ P_{7:9} &= \sum_{m=7}^{9} P_m = P_7 + P_8 + P_9, \\ AI_{7:9} &= \frac{P_{7:9}}{ET_{0,7:9}} = \frac{P_7 + P_8 + P_9}{ET_{0,7} + ET_{0,8} + ET_{0,9}}. \end{aligned}

ここで、mmは月の番号、ET0,mET_{0,m}とPmP_mはそれぞれmm月のET₀と降水量の月合計(mm)です。 添字の7:97:9は7〜9月を表します。AIには単位がありません。

もとの値はすでに月合計なので、日数をもう一度掛ける必要はありません。単位は、選んだ期間の合計mmです。 乾燥指数AIは、同じ期間の降水量の合計をET₀の合計で割った値(sum(P) / sum(ET0))とします。 月ごとのAIを平均する方法とは区別してください。

選んだ月に欠測があれば集計結果も欠測とし、ET₀の合計が0以下、または欠測の場合はAIをNAとします。 降水量からET₀を引いたP−ET₀も計算できますが、これは気候上の水の過不足を表す目安です。 土壌の保水量や流出を計算しているわけではないので、土壌水分そのものとは異なります。

次の架空CSVは、実在メッシュを表さない2個の識別子と3年分の月合計を持ちます。 上の日別CSVとは独立した説明用の数値です。

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

この章のAIは、11年平均の月別値を足してから比を求めます。 各年のAIを先に求めてから平均した値とは、一般に一致しません。 月別AIの平均も、合計同士の比とは異なります。次の例で違いを確認できます。

p <- c(50, 200)
et0 <- c(100, 50)
data.frame(ratio_of_sums = sum(p) / sum(et0), mean_monthly_ai = mean(p / et0))
  ratio_of_sums mean_monthly_ai
1      1.666667            2.25

年をまたぐ冬季

例えば2009年の冬季は、2008年12月と2009年1〜2月を合わせた期間とします。 period_summary(data, c(12, 1, 2))では、同じ行にある同じ年の12月・1月・2月を選んでしまいます。 連続した冬の3か月を集計するには、年別表にwinter_summary()を使ってください。 この関数は同じメッシュの前年12月を探し、1〜2月が属する年を冬季の年として表示します。 入力行は削除せず、前年12月がない場合は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

対象期間のうち、冬の3か月が揃うのは2009〜2018年の冬季です。 2008年の冬季に必要な2007年12月と、2019年の冬季に必要な2019年1〜2月は含まれていません。 一方、11年平均表の12月・1月・2月を足すと、平均的な冬の値を求められます。 ただし、それは特定の年の冬を表す値ではなく、年ごとのばらつきも分かりません。

再現と検証

計算を担うのはR/pipeline_p0_et0.R、CSVの読込や季節集計を担うのはR/et0_support.Rです。 このページの例はbase Rで動き、新しいパッケージの追加は不要です。

計算コードは、japanese-trees-sdmsのコミット1731a9e9fcaba87d718168e5eed0aeaed68b2acdを元に、 変更せず取り込んでいます。欠測を補う関数も、同じ版のR/pipeline_p0_naro.Rから取り出しています。 手元の実データを出力した環境はR 4.5.3・terra 1.8.86で、build-environment.csvに記録しています。 利用許可を得たNetCDFから計算し直す場合は、terraと手元の処理スクリプトを使います。元のデータは変更しません。

計算関数、補助関数、 架空の日別CSV、 架空の月別CSVをダウンロードできます。

プロジェクトのルートで実行します。

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 ja

最初のスクリプトは、架空データとFAOの公開計算例を使って関数の動作を確かめます。 2番目は実データを用意した人向けです。PROJECT_DATA_DIRで指定した場所の全ブロックを読み、 メッシュと年の対応、対象期間の抜け、11年平均が合うかを確認します。表示するのは集計結果だけです。 この章のレンダリングでは、NAROの実データは読み込みません。

参考文献

関連する手法やNAROデータについて詳しく知りたい方は、次の文献も参考にしてください。

  • 全球のET₀・AIデータについて:Zomerら(2022)は、上で紹介した全球データベースの 作成方法を説明しています (Zomer ほか 2022年)。
  • 気温・降水量データの作り方について:大野ら(2016)は、実況値・数値予報・平年値を 組み合わせて、約1 kmのメッシュごとに日別の気温と降水量を求める方法を説明しています (大野 ほか 2016年)。
  • 積雪に関するモデルについて:小南ら(2015)は、メッシュ農業気象データを使って 積雪水量を推定する方法を説明しています (小南 ほか 2015年)。 NAROデータの活用方法を知るための参考文献です。この章のET₀計算では、積雪水量は入力に使いません。
Zomer, Robert J., Jianchu Xu, と Antonio Trabucco. 2022年. 「Version 3 of the Global Aridity Index and Potential Evapotranspiration Database」. Scientific Data 9 (1): 409. https://doi.org/10.1038/s41597-022-01493-1.
大野宏之, 佐々木華織, 大原源二, と 中園江. 2016年. 「実況値と数値予報,平年値を組み合わせたメッシュ気温・降水量データの作成」. 生物と気象 16: 71–79. https://doi.org/10.2480/cib.J-16-028.
小南靖弘, 広田知良, 井上聡, と 大野宏之. 2015年. 「メッシュ農業気象データのための積雪水量推定モデル」. 雪氷 77 (3): 233–46. https://doi.org/10.5331/seppyo.77.3_233.