GPVデータファイルからWBGTを求める

この記事は、SDGsの目標13「気候変動に具体的な対策を」をテーマに、気象データの利用方法の普及と気象関連ソリューション開発を促進するためのナレッジを提供することを目的として作成しています。

今回は、PythonのMetPyを使って気象庁メソモデル(MSM)から湿球黒球温度(WBGT)を求める方法について解説していきます。

GPVデータから物理量を抽出する方法や風速、露点温度の求め方については、以下で詳しく解説していますので参考にしてください。

GPVデータファイルからWBGTを求める

今回の解説では、PythonのモジュールのPygribとMetPyを利用します。また、作成したコードは再利用できるようにすることを前提としクラスで作成していきます。

なぜWBGTを求めるのか

WBGT(Wet Bulb Globe Temperature)は、湿球黒球温度という暑さ指数であり、熱中症を予防することを目的として1954年にアメリカ海兵隊で訓練時の熱中症リスクを事前に判断するための指標提案された指標です。

WBGTは、気温、湿度と輻射熱が人体に与える影響が考慮されており、湿球温度(Tw)、黒球温度(Tg)および乾球温度(Td)を使って算出されます。

湿球温度(wet-bulb temperature)は、空気中の湿度を考慮に入れた温度の指標で、湿った布に包まれた温度計を通風環境に暴露して測定することで得られます。湿度が低いほど蒸発が盛んとなり、湿球温度は低くなります。

黒球温度(black globe temperature)は、黒色に塗装された直径約15cm程度の薄い銅板の球の中心に温度計を入れて観測される温度です。直射日光と通風環境に暴露した状態で球体の中の温度を観測しており、日なたにおける体感温度と相関関係があるとされています。

乾球温度(NDB:Natural Dry Bulb temperature)は、乾球温度計を使用して測定される温度のことであり、乾燥した状態の空気の温度を示します。一般に気温として扱われる温度のことです。

近年、温暖化により地球全体の平均温度が上昇していると言われており、SDGsの目標13「気候変動に具体的な対策を」で温暖化の原因となる二酸化炭素などの温室効果ガスの削減が叫ばれています。

日本における近年の気候も、春と秋が短く夏が長くなったように感じられます。真夏日や酷暑日の出現日数が増加しており、熱中症リスクが増大しているため、WBGTを活用した熱中症対策の呼びかけがとても重要です。

WBGTは、通常は専用の測器を用いた観測によって得られるものですが、数日後ぐらいまでのWBGT予報についても需要があると考えられるため、数値予報結果を利用したWBGTの算出についても研究がなされています。

GPVからWBGTを求める方法

環境省によると、WBGTは以下の計算式で計算されます。

WBGT(屋外) =0.7 × 湿球温度(Tw) + 0.2 × 黒球温度(Tg) + 0.1 × 乾球温度(Ta)

ここで、GPVから直接得られる物理量は、乾球温度(Ta)だけであり、湿球温度(Tw)と黒球温度(Tg)はいくつかの物理量を組み合わせて算出する必要があります。

湿球温度(Tw)は、地上気圧、気温と露点温度を使ってMetpyで算出することができます。

一方、黒球温度(Tg)はMetpyでは求めることはできないため、以下の論文に示される黒球温度推定式で算出します。

Maki.Okada Masumi.Okada and Hiroyuki.Kusaka, Parameter adjustment and application to an extension area of Okada and Kusaka’s formula for the black globe temperature

ここで、Tgは黒球温度(℃)、Taは気温(℃)、So は全天日射量(W/m2)、U は風速(m/s)を表す.完全な物理式であれば、a〜d は気象要素から成る関数で表現されるはずである.但し、Okada and Kusaka(23)の式は、気象官署から得られない気象要素を全て経験定数 a〜d として押し込めることで黒球温度を推定する.

https://www.heat-island.jp/web_journal/download/13A003.pdf

黒球温度推定式の原型は以下のとおりで、定数a~dが不定のため、このままでは黒球温度を算出できません。

そこで彼らは、環境省の黒球温度観測データと気象官署の気温、全天日射量、風速の観測データを用いて、非線形関数の最小値を探索する滑降シンプレックス法により定数 a~d の値を推定し、以下の式を導出しています。

黒球温度推定式

黒球温度推定式は、日射量(W/m2)、地上付近の風速(m/s)と地上付近の気温(℃)を使って求められることが理解できます。

WBGTを求めるクラスを作る

ここからは、ある地点のWBGTを気象庁MSMのGRIB2から求めるPythonのコードについて紹介していきます。

はじめにクラスコードの全体を以下に示します。

import pygrib
import metpy.calc as mpcalc
from metpy.units import units
from datetime import timedelta
import numpy as np

class MetPhysModule :
        def __init__(self, reflat, reflon, grib2) :

                # 抽出する地点の緯度経度と抽出範囲を設定する
                self.req_lat1 = reflat - 0.025
                self.req_lat2 = reflat + 0.025
                self.req_lon1 = reflon - 0.03125
                self.req_lon2 = reflon + 0.03125

                # GRIB2ファイル名を設定する
                self.grib2dat = grib2

                # UTCから日本時間に変換するための時刻差を設定する
                self.diff_time = timedelta(hours=9)

        def extractValidTime(self, physquantity) :
                validtime = physquantity.validDate + self.diff_time
                return validtime

        def extractPointData(self, physquantity) :
                pointdata = physquantity.data(
                        lat1 = self.req_lat1, lat2 = self.req_lat2,
                        lon1 = self.req_lon1, lon2 = self.req_lon2
                        )[0][0][0]
                return pointdata

    def calcWBGT(self) :
                # GPVファイル名を指定してオープンする
                gpv_file = pygrib.open(self.grib2dat)

                # 日射量、気温、相対湿度、風速ベクトルを取り出す。
                shortwave = gpv_file.select(name="Mean surface downward short-wave radiation flux")
                temp = gpv_file.select(name="Temperature")
                rhum = gpv_file.select(name="Relative humidity")
                pres = gpv_file.select(name="Surface pressure")
                u_comp = gpv_file.select(name="10 metre U wind component")
                v_comp = gpv_file.select(name="10 metre V wind component")

                # 予想時刻と抽出した要素データを格納するリストを初期化する
                valid_dates = []
                solar_list  = []
                temp_list   = []
                rh_list     = []
                dew_list    = []
                wspd_list   = []
                wetb_list   = []

                for sw, tk, rh, psfc, uwind, vwind in zip(shortwave, temp, rhum, pres, u_comp,v_comp) :
                        # 予想時刻をリストに抽出する
                        vt = self.extractValidTime(uwind)

                        # 日射量を抽出する
                        sw_pt = self.extractPointData(sw)

                        # 風速ベクトルを抽出してする
                        u_pt = self.extractPointData(uwind)
                        v_pt = self.extractPointData(vwind)
                        # 風速をMetpyで算出する
                        wspd = mpcalc.wind_speed(
                                u_pt * units('m/s'), v_pt * units('m/s')
                                )

                        # 湿球温度をMetpyで算出する
                        psfc_pt = self.extractPointData(psfc)
                        tc_pt   = self.extractPointData(tk) - 273.15
                        rh_pt   = self.extractPointData(rh)
                        dew_pt  = mpcalc.dewpoint_from_relative_humidity(
                                        tc_pt * units.degC,
                                        rh_pt * units.percent
                                        )
                        wetb_pt = mpcalc.wet_bulb_temperature(
                                        psfc_pt * units.Pa,
                                        tc_pt * units.degC,
                                        dew_pt.magnitude * units.degC
                                        )
                        # リスト変数に追加する
                        valid_dates.append(vt)
                        solar_list.append(sw_pt)
                        temp_list.append(tc_pt)
                        rh_list.append(rh_pt)
                        dew_list.append(dew_pt.magnitude)
                        wspd_list.append(wspd.magnitude)
                        wetb_list.append(wetb_pt.magnitude)

                # 黒球温度を計算する
                So = np.array(solar_list)
                U  = np.array(wspd_list)
                Ta = np.array(temp_list)
                Tg = ((So - 38.5) / ((So * 0.0217) + (U * 4.35) + 23.5)) + Ta

                # WBGTを計算する
                Tw = np.array(wetb_list)
                WBGT_out = (0.7 * Tw) + (0.2 * Tg) + (0.1 * Ta)

                return (valid_dates, WBGT_out.tolist(),
                        temp_list, dew_list, rh_list,
                        Tg.tolist(), Tw.tolist(),
                        solar_list, wspd_list
                        )

露点温度、湿球温度および風速の物理量計算を行うため、metpy.calcをインポートしエイリアス(mpcalc)を設定しています。

MetPyでは、計算対象の物理量に対して単位を設定しなければならないため、metpy.unitsモジュールもインポートし気象物理量の単位を設定できるようします。

今回は、黒球温度を自力で計算するため、Numpyもインポートしています。

import pygrib
import metpy.calc as mpcalc
from metpy.units import units
from datetime import timedelta
import numpy as np

次に、コンストラクタ変数の初期化を行います。

続いて予報時刻を抽出するメソッド(extractValidTime)とポイントのデータを抽出するメソッド(extractPointData)を作成します。これらのメソッドは、WBGTを計算するメソッド内で使用します。

        def __init__(self, reflat, reflon, grib2) :

                # 抽出する地点の緯度経度と抽出範囲を設定する
                self.req_lat1 = reflat - 0.025
                self.req_lat2 = reflat + 0.025
                self.req_lon1 = reflon - 0.03125
                self.req_lon2 = reflon + 0.03125

                # GRIB2ファイル名を設定する
                self.grib2dat = grib2

                # UTCから日本時間に変換するための時刻差を設定する
                self.diff_time = timedelta(hours=9)

        def extractValidTime(self, physquantity) :
                validtime = physquantity.validDate + self.diff_time
                return validtime

        def extractPointData(self, physquantity) :
                pointdata = physquantity.data(
                        lat1 = self.req_lat1, lat2 = self.req_lat2,
                        lon1 = self.req_lon1, lon2 = self.req_lon2
                        )[0][0][0]
                return pointdata

ここからがWBGTを算出する中心的なメソッドです。

PygribでGPVをオープンして、各GRIBメッセージを日射量(shortwave)、地上気温(temp)、地上相対湿度(rhum)、地上気圧(pres)および地上の成分風速(u_comp、v_comp)のオブジェクト変数に格納します。

予想時刻と抽出した物理量データを格納するリスト変数を初期化しておきます。

    def calcWBGT(self) :
                # GPVファイル名を指定してオープンする
                gpv_file = pygrib.open(self.grib2dat)

                # 日射量、気温、相対湿度、風速ベクトルを取り出す。
                shortwave = gpv_file.select(name="Mean surface downward short-wave radiation flux")
                temp = gpv_file.select(name="Temperature")
                rhum = gpv_file.select(name="Relative humidity")
                pres = gpv_file.select(name="Surface pressure")
                u_comp = gpv_file.select(name="10 metre U wind component")
                v_comp = gpv_file.select(name="10 metre V wind component")

                # 予想時刻と抽出した要素データを格納するリストを初期化する
                valid_dates = []
                solar_list  = []
                temp_list   = []
                rh_list     = []
                dew_list    = []
                wspd_list   = []
                wetb_list   = []

以下の部分は、各物理量のGRIBメッセージを格納したオブジェクト変数をループさせて、予報時刻、特定地点の日射量を抽出しています。

スカラー風速を求めるため、抽出した特定地点の成分風速をMetpyに与えて計算させます。

湿球温度を求めるため、はじめに抽出した特定地点の地上気温と相対湿度からMetpyで露点温度を計算します。次に求められた露点温度と抽出した特定地点の地上気圧および地上気温からMetpyで湿球温度を計算させています。

                for sw, tk, rh, psfc, uwind, vwind in zip(shortwave, temp, rhum, pres, u_comp,v_comp) :
                        # 予想時刻をリストに抽出する
                        vt = self.extractValidTime(uwind)

                        # 日射量を抽出する
                        sw_pt = self.extractPointData(sw)

                        # 風速ベクトルを抽出してする
                        u_pt = self.extractPointData(uwind)
                        v_pt = self.extractPointData(vwind)
                        # 風速をMetpyで算出する
                        wspd = mpcalc.wind_speed(
                                u_pt * units('m/s'), v_pt * units('m/s')
                                )

                        # 湿球温度をMetpyで算出する
                        psfc_pt = self.extractPointData(psfc)
                        tc_pt   = self.extractPointData(tk) - 273.15
                        rh_pt   = self.extractPointData(rh)
                        dew_pt  = mpcalc.dewpoint_from_relative_humidity(
                                        tc_pt * units.degC,
                                        rh_pt * units.percent
                                        )
                        wetb_pt = mpcalc.wet_bulb_temperature(
                                        psfc_pt * units.Pa,
                                        tc_pt * units.degC,
                                        dew_pt.magnitude * units.degC
                                        )

                        # リスト変数に追加する
                        valid_dates.append(vt)
                        solar_list.append(sw_pt)
                        temp_list.append(tc_pt)
                        rh_list.append(rh_pt)
                        dew_list.append(dew_pt.magnitude)
                        wspd_list.append(wspd.magnitude)
                        wetb_list.append(wetb_pt.magnitude)

最後に、黒球温度とWBGTを自力で計算します。

リスト変数に格納された特定地点の日射量などの物理量は、このままでは計算できないため、Numpyアレイに代入してから計算します。

黒球温度は、日射量(SO)、風速(U)および地上気温(Ta)を黒球温度推定式に代入して求めます。

屋外におけるWBGT(WBGT_out)は、湿球温度(Tw)黒球温度(Tg)および気温(Ta)のそれぞれに係数を掛けてたものの総和により求まります。

このクラスからは、予想時間、WBGT、地上気温、露点温度、相対湿度、黒球温度、湿球温度、日射量、地上風速が戻り値に設定されています。

なお、戻り値はすべてリスト変数で返したいので、WBGT、黒球温度、湿球温度はtolist()関数でNumpyアレイからリスト変数に変換しています。

                # 黒球温度を計算する
                So = np.array(solar_list)
                U  = np.array(wspd_list)
                Ta = np.array(temp_list)
                Tg = ((So - 38.5) / ((So * 0.0217) + (U * 4.35) + 23.5)) + Ta

                # WBGTを計算する
                Tw = np.array(wetb_list)
                WBGT_out = (0.7 * Twb) + (0.2 * Tg) + (0.1 * Ta)

                return (valid_dates, WBGT_out.tolist(),
                        temp_list, dew_list, rh_list,
                        Tg.tolist(), Tw.tolist(),
                        solar_list, wspd_list
                        )

WBGTを計算してみる

作成したクラスをインポートして、WBGTを計算してみます。Pythonのメインスクリプトは以下のとおりです。

from MetPhysModule import MetPhysModule
import pandas as pd

# 東京駅の緯度経度を設定する
land_lat = 35.681236
land_lon = 139.767125

# 気象庁MSMのGRIB2ファイル名を指定する
msm_gpv  ='Z__C_RJTD_20230720000000_MSM_GPV_Rjp_Lsurf_FH00-15_grib2.bin'

# MetPhysModuleクラスをインスタンス化する
calcphys = MetPhysModule(land_lat, land_lon, msm_gpv)

# WBGTを計算する
vt, wbgt, tempc, dewp, rhum, tg, tw, sw, wspd = calcphys.calcWBGT()


# 予報時刻、戻り値のリスト変数をPandasのデータフレームに入力する
df = pd.DataFrame({
        "ValidTime":vt,
        "WBGT":wbgt,
        "Temp C":tempc,
        "Dewp C":dewp,
        "Rhum %":rhum,
        "Globe C":tg,
        "WetBulb C":tw,
        "Solar W/m**2":sw,
        "Wind m/s":wspd
        })

pd.set_option('display.max_rows', None)
pd.set_option('display.max_columns',None)
print(df)

コードを実行すると以下のような結果が出力されます。

(MetPy) [reafnex@devsrv01 MetPy]$ python3.12 main.py
             ValidTime       WBGT     Temp C     Dewp C     Rhum %    Globe C  \
0  2023-07-20 09:00:00  28.204719  27.333521  23.901170  81.552700  40.728725
1  2023-07-20 10:00:00  29.551712  29.249536  24.068106  73.676754  44.488780
2  2023-07-20 11:00:00  30.218298  29.818384  24.615339  73.675938  45.704081
3  2023-07-20 12:00:00  30.596670  31.011743  24.213162  67.163734  47.045105
4  2023-07-20 13:00:00  30.655998  31.878809  23.856524  62.573605  47.087742
5  2023-07-20 14:00:00  29.846093  31.465326  24.391543  66.153625  42.265909
6  2023-07-20 15:00:00  28.795842  30.401056  24.341985  70.092070  38.516961
7  2023-07-20 16:00:00  27.473617  29.820123  23.533158  69.029978  34.662994
8  2023-07-20 17:00:00  25.833955  28.832086  22.308251  67.857021  30.757492
9  2023-07-20 18:00:00  24.255273  27.586572  21.292224  68.571854  26.998699
10 2023-07-20 19:00:00  23.761430  26.878900  21.272135  71.392384  25.572846
11 2023-07-20 20:00:00  23.653113  26.396204  21.556524  74.745361  25.034782
12 2023-07-20 21:00:00  23.521129  25.999078  21.721967  77.300816  24.536084
13 2023-07-20 22:00:00  23.371278  25.621515  21.801221  79.435814  24.129072
14 2023-07-20 23:00:00  23.254207  25.258295  21.911953  81.721951  23.789356

    WetBulb C  Solar W/m**2  Wind m/s
0   24.750889    581.136551  1.011287
1   25.327147    654.402016  0.624138
2   25.850919    713.884396  0.810105
3   25.837820    730.350897  0.874044
4   25.786527    672.188646  0.822773
5   26.066255    480.616901  1.610380
6   25.789062    344.372106  1.543703
7   25.084295    205.157766  1.485307
8   23.998926    102.255630  1.699741
9   22.995537     19.375000  1.979792
10  22.798530      0.000000  1.374277
11  22.866480      0.000000  1.098681
12  22.877149      0.000000  0.647333
13  22.833303      0.000000  0.527962
14  22.815008      0.000000  0.622852

2023年の関東甲信地方の梅雨明けは7月22日で、7月20日は日本の南海上に梅雨前線が停滞しており、東京は曇りで最高気温が32.6℃でした。

WBGTの計算結果から、この日の日中は厳重警戒(WBGT=28~31)の範囲であり、熱中症の危険が高い状態であったことが考えられます。

まとめ

いかがでしたでしょうか。

今回は、PythonのPygribやMetPyモジュールを利用して、暑さ指標であるWBGT(湿球黒球温度)を計算するコードを紹介しました。

また、WBGTを求めるうえで重要となる黒球温度については、一般的に観測によって得られるものですが、数日間の期間における暑さ指数の予想を可能とするため、気象庁MSMの数値予報結果とM.Okadaらの研究によって考案された黒球温度推定式を用いて予測するためのコードをPythonで作成しました。

近年における数値予報の予測精度は非常に高精度化しているため、求められたWBGTも高い精度を有している可能性が考えられます。

今回紹介したWBGTの計算方法には風速が考慮されているため、人体影響と相関関係にある黒球温度の推定は非常によく実現できていると考えられます。

しかしながら、降水、散乱による日射量変化や地面等から受ける赤外放射の影響など細かなところは考慮されていないため、一定の誤差を持っていることを十分に理解して利用する必要があります。

観測と予報の両側面から、効率的かつ効果的なWBGTの運用を目指し、地球温暖化により増加し続けている熱中症リスクから人類を守っていことが、SDGsの目標13「気候変動に具体的な対策を」に対する具体的なアクションとなると信じています。

参考になれば幸いです。

気象データ利用のお悩みについてご相談ください

気象データの利用に関するお悩みは、こちらからお問い合わせください。
気象システムの設計製造及び運用保守にかかわる現役システムエンジニアが、気象予報士の視点で気象データを活用したビジネスアイディアの創出と課題解決のお手伝いをさせていただきます。
気象や波浪などの数値予報モデルを活用した気象予報システム開発や運用についても是非ご相談ください。