TANGERANG SELATAN WEATHER

Sabtu, 25 Juli 2026

Menghitung Indeks Panas dari Suhu dan Kelembaban

NWS 2024 heat index chart mapping air temperature and relative humidity to color-coded danger categories Sumber: National Weather Service / NOAA (https://www.weather.gov/safety/heat-index) — karya US Government, domain publik

Pengenalan Indeks Panas

Ketika termometer menunjukkan 32°C, kita cenderung mengasumsikan beban panas setara dengan 32°C. Asumsi itu keliru di iklim tropis lembab seperti Indonesia. Tubuh manusia mendinginkan diri terutama lewat penguapan keringat, dan proses ini bergantung pada seberapa banyak uap air yang masih bisa ditampung udara. Di udara jenuh — kelembaban relatif 80–90% yang umum di pesisir Indonesia — keringat hampir tidak menguap, sehingga panas tubuh tertahan dan suhu inti tubuh naik lebih cepat dari yang seharusnya.

BMKG menyebut besaran ini sebagai indeks panas (heat index) atau suhu terasa: ukuran seberapa panas yang sesungguhnya dirasakan tubuh ketika kelembaban dimasukkan ke dalam perhitungan. Pada kondisi 32°C dengan RH 80% — sangat lazim di kota-kota pesisir Indonesia — indeks panas mencapai sekitar 44–47°C. Selisih lebih dari 12°C dari suhu aktual itu bukan rekayasa, melainkan cerminan dari seberapa besar kelembaban menekan kapasitas termoregulasi tubuh.

Relevansi angka ini semakin nyata melihat data terkini. Pada 14 Oktober 2025, BMKG mencatat suhu permukaan 37,6°C di Majalengka (Jawa Barat) dan Boven Digoel (Papua), dengan sebaran 35–37°C di Nusa Tenggara, Jawa, Kalimantan, Sulawesi selatan, dan Papua. Rata-rata suhu udara Indonesia pada Juni 2026 berdasarkan 103 stasiun pengamatan BMKG adalah 27,3°C — lebih tinggi 0,7°C dari normal klimatologis 1991–2020 sebesar 26,6°C.

Tutorial ini memandu langkah lengkap: mengunduh data ERA5, menurunkan RH dari formula Magnus, mengimplementasikan algoritma dua tahap NWS, dan memvisualisasikan seri waktu indeks panas untuk satu lokasi Indonesia.

Mempersiapkan Data ERA5

ERA5 menyediakan dua variabel surface yang kita butuhkan: t2m (suhu udara 2 m, dalam Kelvin) dan d2m (dewpoint temperature 2 m, juga dalam Kelvin). Variabel d2m adalah suhu di mana udara akan mencapai kejenuhan jika didinginkan tanpa perubahan tekanan atau kandungan uap air — semakin kecil selisih t2m - d2m, semakin lembab udara. Kita pakai dataset 6-hourly tahun 2024, bounding box Indonesia ([6°N, 95°E, −11°S, 141°E]).

Snippet berikut mengunduh kedua file satu kali dari CDS. Snippet ini ditandai run=false karena membutuhkan kredensial CDS yang dikonfigurasi di sistem pembaca — daftarkan akun dan buat file ~/.cdsapirc di cds.climate.copernicus.eu terlebih dahulu.

import os, cdsapi

OUT_T2M = "era5_t2m_indonesia_2024_6h.nc"
if not os.path.exists(OUT_T2M):
    c = cdsapi.Client(quiet=True)
    c.retrieve(
        "reanalysis-era5-single-levels",
        {
            "product_type": "reanalysis",
            "variable": ["2m_temperature"],
            "year": "2024",
            "month": [f"{m:02d}" for m in range(1, 13)],
            "day":   [f"{d:02d}" for d in range(1, 32)],
            "time":  ["00:00", "06:00", "12:00", "18:00"],
            "area":  [6, 95, -11, 141],
            "format": "netcdf",
        },
        OUT_T2M,
    )

OUT_D2M = "era5_d2m_indonesia_2024_6h.nc"
if not os.path.exists(OUT_D2M):
    c = cdsapi.Client(quiet=True)
    c.retrieve(
        "reanalysis-era5-single-levels",
        {
            "product_type": "reanalysis",
            "variable": ["2m_dewpoint_temperature"],
            "year": "2024",
            "month": [f"{m:02d}" for m in range(1, 13)],
            "day":   [f"{d:02d}" for d in range(1, 32)],
            "time":  ["00:00", "06:00", "12:00", "18:00"],
            "area":  [6, 95, -11, 141],
            "format": "netcdf",
        },
        OUT_D2M,
    )

print("Kedua file ERA5 siap di working directory.")

Setelah kedua file tersedia secara lokal, snippet berikut memuat dataset, mendeteksi nama dimensi waktu (ERA5 terbaru mungkin memakai valid_time sebagai ganti time), dan mengonversi Kelvin ke °C.

import os
import xarray as xr
import numpy as np

ds_t2m = xr.open_dataset("era5_t2m_indonesia_2024_6h.nc")
ds_d2m = xr.open_dataset("era5_d2m_indonesia_2024_6h.nc")

time_dim = "valid_time" if "valid_time" in ds_t2m.dims else "time"

t2m_c = ds_t2m["t2m"] - 273.15   # Kelvin → °C
d2m_c = ds_d2m["d2m"] - 273.15   # Kelvin → °C

n_times = ds_t2m.dims[time_dim]
print(f"Dimensi waktu: '{time_dim}' ({n_times} timestep)")
print(f"Shape t2m: {t2m_c.shape},  shape d2m: {d2m_c.shape}")
print(f"T2m — min: {float(t2m_c.min()):.2f}°C, "
      f"mean: {float(t2m_c.mean()):.2f}°C, "
      f"max: {float(t2m_c.max()):.2f}°C")
Dimensi waktu: 'valid_time' (1464 timestep)
Shape t2m: (1464, 69, 185),  shape d2m: (1464, 69, 185)
T2m — min: 6.86°C, mean: 27.31°C, max: 39.26°C

Output di atas mengonfirmasi dataset berhasil dimuat. Rentang suhu yang tercetak — sekitar 7–39°C untuk wilayah Indonesia — konsisten dengan variasi antara puncak pegunungan Papua yang sangat sejuk dan pesisir pantai yang panas, dengan rata-rata domain di kisaran 27°C.

Menurunkan Kelembaban Relatif dari Formula Magnus

ERA5 tidak menyertakan RH sebagai variabel keluaran langsung, tapi kita bisa menurunkannya dari pasangan t2m dan d2m menggunakan formula August-Roche-Magnus. Lawrence (2005) dalam BAMS merekomendasikan koefisien Alduchov & Eskridge (1996): \(a = 17{,}625\) dan \(b = 243{,}04\,°\text{C}\), yang akurat dalam rentang \(-40\) hingga \(+50\,°\text{C}\) — lebih dari cukup untuk kondisi tropis Indonesia.

Formulanya dalam bentuk display:

$$RH = 100 \cdot \frac{\exp\!\left(\dfrac{17{,}625\,T_d}{243{,}04 + T_d}\right)}{\exp\!\left(\dfrac{17{,}625\,T}{243{,}04 + T}\right)}$$

di mana \(T\) adalah suhu dry-bulb dan \(T_d\) adalah dewpoint, keduanya dalam °C. Secara intuitif: pembilang mengukur tekanan uap aktual di udara (lewat dewpoint), sedangkan penyebut mengukur tekanan uap jenuh pada suhu aktual. Rasio keduanya adalah fraksi kejenuhan — yaitu RH itu sendiri. Ketika \(T_d = T\), udara tepat jenuh dan \(RH = 100\%\); semakin besar selisih \(T - T_d\), semakin rendah RH.

import numpy as np

a, b = 17.625, 243.04
t_vals = t2m_c.values   # numpy array, °C
d_vals = d2m_c.values   # numpy array, °C

rh = 100.0 * np.exp(a * d_vals / (b + d_vals)) / np.exp(a * t_vals / (b + t_vals))
rh = np.clip(rh, 0.0, 100.0)

print(f"RH — min: {rh.min():.1f}%, mean: {rh.mean():.1f}%, max: {rh.max():.1f}%")
RH — min: 18.8%, mean: 82.0%, max: 100.0%

Nilai mean RH yang tinggi — biasanya di atas 75–85% untuk wilayah Indonesia — mencerminkan iklim tropis lembab dan menjadi faktor penguat utama indeks panas. Di atas 80%, kapasitas penguapan keringat menyusut drastis.

Implementasi Algoritma Indeks Panas Dua Tahap

NWS menggunakan algoritma dua tahap yang menggabungkan model fisik Steadman (1979) dan regresi polinomial Rothfusz (1990). Alasan dua tahap ini penting: regresi Rothfusz hanya valid di atas 80°F (26,7°C) dan RH ≥ 40% — di bawah batas itu, formula polinomialnya menyimpang dan menghasilkan nilai yang tidak bermakna. Perkiraan Steadman yang sederhana bertindak sebagai penjaga gerbang.

Tahap pertama menghitung perkiraan Steadman:

$$HI_{\text{simple}} = 0{,}5 \cdot \bigl(T + 61{,}0 + 1{,}2\,(T - 68{,}0) + 0{,}094\,RH\bigr)$$

di mana \(T\) dalam °F dan \(RH\) dalam %. Jika rata-rata \((HI_{\text{simple}} + T)/2 < 80\,°\text{F}\), nilai ini sudah cukup sebagai indeks panas akhir.

Jika rata-rata itu \(\geq 80\,°\text{F}\), kita terapkan regresi polinomial 9-koefisien Rothfusz (1990) — koefisien lengkapnya ada di kode di bawah. Setelah regresi Rothfusz, ada dua koreksi opsional: penyesuaian ke bawah untuk RH sangat rendah (\(< 13\%\) dan \(80 < T < 112\,°\text{F}\)) dan penyesuaian ke atas untuk RH sangat tinggi (\(> 85\%\) dan \(80 < T < 87\,°\text{F}\)). Kedua penyesuaian ini jarang aktif di kondisi tropis lembab, tapi penting untuk kebenaran implementasi di wilayah yang lebih luas.

Seluruh kalkulasi berlangsung dalam °F, sehingga pipeline konversinya adalah: ERA5 Kelvin → °C (untuk Magnus) → °F (untuk Rothfusz) → °F HI → °C HI untuk ditampilkan.

import numpy as np

def heat_index_c(T_c, RH):
    """Menghitung heat index (°C) dari suhu °C dan RH %.
    Algoritma dua tahap NWS: Steadman sederhana lalu Rothfusz (1990).
    Vectorized — bekerja pada numpy arrays maupun skalar.
    """
    T_f = T_c * 9.0 / 5.0 + 32.0

    # Tahap 1 — perkiraan Steadman sederhana
    HI_simple = 0.5 * (T_f + 61.0 + 1.2 * (T_f - 68.0) + 0.094 * RH)

    # Gate: gunakan Rothfusz jika rata-rata ≥ 80°F
    use_r = (HI_simple + T_f) / 2.0 >= 80.0

    # Tahap 2 — regresi Rothfusz (9 koefisien, NWS SR 90-23, 1990)
    HI_r = (-42.379
             + 2.04901523  * T_f
             + 10.14333127 * RH
             - 0.22475541  * T_f * RH
             - 0.00683783  * T_f**2
             - 0.05481717  * RH**2
             + 0.00122874  * T_f**2 * RH
             + 0.00085282  * T_f * RH**2
             - 0.00000199  * T_f**2 * RH**2)

    # Penyesuaian RH rendah (<13%) dan RH tinggi (>85%)
    adj_low = np.where(
        (RH < 13) & (T_f > 80) & (T_f < 112),
        ((13.0 - RH) / 4.0) * np.sqrt((17.0 - np.abs(T_f - 95.0)) / 17.0),
        0.0,
    )
    adj_high = np.where(
        (RH > 85) & (T_f > 80) & (T_f < 87),
        ((RH - 85.0) / 10.0) * ((87.0 - T_f) / 5.0),
        0.0,
    )
    HI_r = HI_r - adj_low + adj_high

    HI_f = np.where(use_r, HI_r, HI_simple)
    return (HI_f - 32.0) * 5.0 / 9.0


# Contoh skalar: T=32°C, RH=80%
hi_ex = heat_index_c(np.array([32.0]), np.array([80.0]))
print(f"Contoh: T=32°C, RH=80% → HI = {hi_ex[0]:.1f}°C")

# Terapkan ke seluruh domain ERA5 Indonesia 2024
hi_c = heat_index_c(t_vals, rh)
print(f"Domain HI: min={np.nanmin(hi_c):.1f}°C, "
      f"mean={np.nanmean(hi_c):.1f}°C, "
      f"max={np.nanmax(hi_c):.1f}°C")
Contoh: T=32°C, RH=80% → HI = 44.4°C
Domain HI: min=6.1°C, mean=30.8°C, max=49.5°C

Contoh skalar mengonfirmasi bahwa 32°C + 80% RH menghasilkan HI sekitar 43–47°C, sesuai dengan angka yang dikutip BMKG. Nilai domain mean yang biasanya berkisar 30–40°C mencerminkan variasi diurnal (siang vs. malam) dan spasial (pesisir lembab vs. pegunungan yang lebih sejuk dan kering).

Penerapan dan Klasifikasi di Indonesia

NWS mendefinisikan empat kategori risiko indeks panas yang BMKG gunakan sebagai acuan dalam peringatan cuaca panas:

Kategori Indeks Panas Risiko Kesehatan
Waspada 27–32°C Kelelahan mungkin terjadi dengan paparan lama atau aktivitas fisik
Sangat Waspada 32–41°C Kram otot, kelelahan panas, dan sengatan matahari mungkin terjadi
Berbahaya 41–54°C Kelelahan panas kemungkinan besar; heatstroke mungkin terjadi
Sangat Berbahaya ≥54°C Heatstroke sangat mungkin terjadi; kondisi darurat yang mengancam jiwa

Kita terapkan klasifikasi ini ke seri waktu indeks panas di Surabaya — kota pesisir besar dengan iklim panas dan lembab sepanjang tahun yang representatif untuk kondisi tropis dataran rendah Indonesia. Snippet berikut memilih grid cell terdekat, menghitung HI, melakukan resampling ke maksimum harian agar tren lebih terlihat, lalu mencetak distribusi kategori dan menyimpan plot.

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np

# Pilih grid cell terdekat ke Surabaya (lat -7.25, lon 112.75)
t_sub = t2m_c.sel(latitude=-7.25, longitude=112.75, method="nearest")
d_sub = d2m_c.sel(latitude=-7.25, longitude=112.75, method="nearest")

# RH dan HI untuk grid cell ini
a, b = 17.625, 243.04
rh_sub = np.clip(
    100.0 * np.exp(a * d_sub.values / (b + d_sub.values))
            / np.exp(a * t_sub.values / (b + t_sub.values)),
    0.0, 100.0,
)
hi_sub = heat_index_c(t_sub.values, rh_sub)

# Resample ke maksimum harian
hi_da = t_sub.copy(data=hi_sub)
hi_daily = hi_da.resample(**{time_dim: "1D"}).max()
times  = hi_daily[time_dim].values
hi_vals = hi_daily.values

# Distribusi kategori
batas = [
    ("Normal (< 27°C)",       -np.inf, 27),
    ("Waspada (27–32°C)",       27,    32),
    ("Sangat Waspada (32–41°C)", 32,   41),
    ("Berbahaya (41–54°C)",      41,   54),
    ("Sangat Berbahaya (≥54°C)", 54,   np.inf),
]
n_total = len(hi_vals)
print("Distribusi kategori — Surabaya, ERA5 2024:")
for nama, lo, hi_thr in batas:
    n = int(np.sum((hi_vals >= lo) & (hi_vals < hi_thr)))
    print(f"  {nama}: {n} hari ({100 * n / n_total:.1f}%)")

# Plot deret waktu
fig, ax = plt.subplots(figsize=(12, 5))
ax.plot(times, hi_vals, color="#C0392B", linewidth=1.2, label="HI Maks. Harian")

warna  = ["#F9CA24", "#F0932B", "#EB2F06", "#6F1E51"]
label  = ["Waspada 27°C", "Sangat Waspada 32°C", "Berbahaya 41°C", "Sangat Berbahaya 54°C"]
ambang = [27, 32, 41, 54]
for val, col, lbl in zip(ambang, warna, label):
    ax.axhline(val, color=col, linestyle="--", linewidth=1.3, label=lbl)

ax.set_title("Indeks Panas Maksimum Harian — Surabaya (ERA5, 2024)", fontsize=13)
ax.set_xlabel("Waktu (2024)")
ax.set_ylabel("Indeks Panas (°C)")
ax.legend(fontsize=9, loc="upper left", ncol=2)
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig("heat_index_timeseries.png", dpi=150, bbox_inches="tight")
print("Plot indeks panas tersimpan.")

Time series of daily maximum heat index for an Indonesian city through 2024 with horizontal lines marking the NWS danger-category thresholds

Plot di atas menampilkan fluktuasi indeks panas maksimum harian Surabaya sepanjang tahun 2024. Distribusi kategori yang dicetak sebelum plot menunjukkan berapa hari dalam setahun yang masuk ke tiap level risiko — angka yang berguna untuk komunikasi publik dan perencanaan respons kesehatan. Musim kemarau (Juni–September) biasanya menunjukkan indeks panas lebih tinggi karena radiasi matahari lebih kuat meski kelembaban kadang sedikit turun, sementara musim hujan (Desember–Maret) membawa RH yang sangat tinggi yang juga mendorong HI ke atas meski suhu aktualnya lebih moderat.

Kesimpulan dan Pembacaan Lanjutan

Algoritma NWS yang kita implementasikan di atas dirancang untuk kondisi teduh dengan angin ringan. Ini bukan detail kecil: paparan langsung sinar matahari dapat menambah hingga ~15°F (~8°C) di atas nilai indeks panas yang dihitung. Artinya, pekerja lapangan, petani, atau atlet yang berkegiatan di bawah terik matahari tropis menghadapi beban panas yang jauh melebihi angka indeks panas standar. Ketika mengomunikasikan risiko, selalu sertakan konteks ini.

Sebagai bacaan lanjutan, Sherwood & Huber (2010) di PNAS mengajukan bahwa suhu wet-bulb 35°C adalah batas absolut kelangsungan hidup manusia — di atas ambang itu, tubuh tidak mampu membuang panas metabolik bahkan saat istirahat total. Meski wilayah Indonesia saat ini masih berada di bawah ambang tersebut, proyeksi iklim menunjukkan sejumlah kawasan tropis berpotensi mendekatinya pada akhir abad ini. Wet-bulb temperature adalah konsep yang berbeda dari indeks panas NWS dan layak menjadi tutorial berikutnya.

Pantau peringatan cuaca panas ekstrem yang diterbitkan BMKG secara rutin — terutama selama periode transisi musim saat kelembaban tinggi bertemu puncak suhu siang hari. Keterampilan menghitung indeks panas dari reanalysis ERA5 yang kita pelajari di sini bisa diperluas ke data observasi stasiun BMKG atau ke proyeksi model iklim untuk analisis risiko panas masa depan.

Eksplorasi artikel meteorologi lainnya di meteo.my.id untuk memperdalam analisis data cuaca, dan kunjungi https://meteo.my.id untuk topik terkait.

Referensi

Tidak ada komentar:

Posting Komentar