Mengapa Kita Membaca Model GFS
Prakiraan cuaca berbasis NWP (Numerical Weather Prediction) bukan lagi eksklusif milik badan meteorologi besar. Siapa pun yang tahu cara download file GRIB2 dan membukanya dengan Python sudah bisa mengekstrak output model global — langsung dari server publik NOAA tanpa biaya.
GFS (Global Forecast System) adalah model global operasional utama yang dikelola oleh NCEP/NOAA. Outputnya tersedia gratis di NOMADS (NOAA Operational Model Archive and Distribution System) beberapa menit setelah setiap siklus model selesai. Untuk keperluan prakiraan lokal di Indonesia — estimasi suhu udara, pola angin, atau risiko hujan ekstrem — GFS menjadi titik awal yang paling mudah dijangkau.
Dalam tutorial ini kita akan tempuh tiga langkah utama. Pertama, kita lihat cara mengakses dan mendownload subset GRIB2 dari NOMADS menggunakan Grib Filter. Kedua, kita parse file tersebut dengan cfgrib dan xarray, dan kita simulasikan hasilnya lewat dataset sintetis yang strukturnya identik dengan output cfgrib asli. Ketiga, kita visualisasikan lapangan suhu dan angin dengan matplotlib dan cartopy.
Sebagai konteks tambahan: pada 17 Desember 2025, NOAA merilis AIGFS v1.0 — model berbasis AI (GraphCast) yang beroperasi bersama GFS tradisional. AIGFS menawarkan delivery lebih cepat dan skill lebih baik untuk trek siklon tropis. Namun format GRIB2 GFS tradisional tidak berubah, sehingga seluruh workflow di artikel ini tetap berlaku.
Memahami Model GFS dan Siklus Jalannya
GFS menggunakan dynamical core FV3 (Finite-Volume Cubed-Sphere) dengan resolusi native sekitar 13 km (C768). Data yang didistribusikan ke publik tersedia dalam tiga resolusi: 0,25°, 0,50°, dan 1,00°. Pada ekuator, resolusi 0,25° setara sekitar 28 km — cukup halus untuk menangkap kontras daratan-laut di kepulauan Indonesia.
Model ini memiliki 127 lapisan vertikal dan dijalankan empat kali sehari pada siklus 00, 06, 12, dan 18 UTC. Setiap siklus menghasilkan prakiraan hingga 384 jam ke depan (16 hari): output per jam untuk 120 jam pertama, kemudian setiap 3 jam setelahnya. Untuk keperluan operasional jangka pendek, siklus 00 dan 12 UTC yang paling sering dipakai karena kelengkapan datanya.
Bagan berikut menggambarkan alur satu siklus GFS dari penerimaan data observasi hingga tersedianya output di NOMADS.
Alur satu siklus GFS: dari observasi sinoptik hingga file GRIB2 tersedia di NOMADS untuk diakses publik.
Untuk Indonesia, resolusi 0,25° menjadi pilihan terbaik. Kepulauan dengan lebar 50–200 km masih bisa direpresentasikan dengan 2–8 grid point, cukup untuk melihat gradien suhu dan angin antara pesisir barat Sumatra, perairan Jawa, dan Kalimantan.
Mengakses GFS dari NOMADS dan Subsetting untuk Indonesia
Data GFS real-time tersedia di NOMADS dalam format GRIB2. Struktur path-nya:
/pub/data/nccf/com/gfs/prod/gfs.YYYYMMDD/CC/atmos/gfs.tCCz.pgrb2.0p25.fFFF
di mana YYYYMMDD adalah tanggal run, CC adalah siklus (00/06/12/18), dan FFF adalah jam prakiraan (000–384).
Mengunduh file lengkap satu siklus GFS 0,25° membutuhkan sekitar 1–2 GB per jam prakiraan karena mencakup ratusan variabel di semua level. Untuk keperluan Indonesia, kita cukup ambil beberapa variabel dan subset bbox menggunakan Grib Filter yang disediakan NOMADS. Grib Filter memungkinkan kita memilih variabel tertentu (misalnya suhu 2 m, angin 10 m, MSLP) dan memotong domain spasial sebelum file didownload.
Bagan berikut memperinci komponen URL Grib Filter yang akan kita gunakan.
Anatomi URL Grib Filter NOMADS: setiap parameter memilih dimensi yang berbeda (waktu, variabel, domain spasial).
Snippet berikut menunjukkan perintah curl ke Grib Filter untuk mengambil T2m, U10, V10, dan PRMSL untuk domain Indonesia. Snippet ini tidak dijalankan di sandbox karena membutuhkan koneksi internet ke server NOMADS.
# Ganti YYYYMMDD, CC, dan FFF sesuai run yang diinginkan
DATE="20260701"
CYCLE="00"
FHOUR="024"
BASE="https://nomads.ncep.noaa.gov/cgi-bin/filter_gfs_0p25.pl"
curl -o "gfs_indonesia_f${FHOUR}.grib2" \
"${BASE}?dir=%2Fgfs.${DATE}%2F${CYCLE}%2Fatmos\
&file=gfs.t${CYCLE}z.pgrb2.0p25.f${FHOUR}\
&var_TMP=on&lev_2_m_above_ground=on\
&var_UGRD=on&lev_10_m_above_ground=on\
&var_VGRD=on&lev_10_m_above_ground=on\
&var_PRMSL=on&lev_mean_sea_level=on\
&leftlon=95&rightlon=141&toplat=6&bottomlat=-11"
Setelah file didownload, ukurannya jauh lebih kecil — biasanya 1–5 MB untuk satu jam prakiraan dengan empat variabel di atas untuk domain Indonesia.
Membaca GRIB2 dengan cfgrib dan xarray
Library cfgrib memungkinkan xarray membuka file GRIB2 langsung dengan xr.open_dataset(..., engine='cfgrib'). Karena satu file GRIB2 bisa memuat variabel dari berbagai typeOfLevel (heightAboveGround, isobaricInhPa, meanSea), cfgrib memerlukan parameter filter_by_keys untuk menentukan subset yang ingin dibaca:
ds_t2m = xr.open_dataset(
"gfs_indonesia_f024.grib2",
engine="cfgrib",
backend_kwargs={
"filter_by_keys": {
"typeOfLevel": "heightAboveGround",
"level": 2,
}
},
)
Di sandbox ini cfgrib tidak tersedia, tetapi kita bisa membuat xarray Dataset sintetis yang strukturnya identik dengan output cfgrib asli — sehingga semua operasi subsetting dan plotting di langkah berikutnya bisa kita demonstrasikan dengan benar.
Snippet berikut membuat Dataset sintetis yang merepresentasikan field suhu 2 m GFS untuk domain Indonesia.
import numpy as np
import xarray as xr
import pandas as pd
# Koordinat yang identik dengan output cfgrib GFS 0.25deg Indonesia
lats = np.linspace(6, -11, 69) # 6N sampai 11S, step 0.25°
lons = np.linspace(95, 141, 185) # 95E sampai 141E, step 0.25°
valid_time = pd.Timestamp("2026-07-02T00:00:00")
# Suhu 2 m sintetis: gradien realistis tropis ~296–305 K
# Lebih hangat di laut terbuka, sedikit lebih dingin di dataran tinggi
rng = np.random.default_rng(seed=42)
lon2d, lat2d = np.meshgrid(lons, lats)
t2m_values = (
300.5
- 0.04 * lat2d # sedikit lebih hangat di ekuator
+ 0.005 * (lon2d - 118) # gradien timur-barat kecil
+ rng.normal(0, 0.6, (69, 185))
).astype(np.float32)
ds = xr.Dataset(
{
"t2m": xr.DataArray(
t2m_values,
dims=["latitude", "longitude"],
attrs={
"long_name": "2 metre temperature",
"units": "K",
"GRIB_shortName": "2t",
"GRIB_typeOfLevel": "heightAboveGround",
"GRIB_level": 2,
},
)
},
coords={
"latitude": ("latitude", lats, {"units": "degrees_north"}),
"longitude": ("longitude", lons, {"units": "degrees_east"}),
"valid_time": valid_time,
"step": pd.Timedelta("24h"),
},
)
print("=== Dataset sintetis GFS T2m ===")
print(f"dims : {dict(ds.dims)}")
print(f"data_vars: {list(ds.data_vars)}")
print(f"coords : {list(ds.coords)}")
print(f"t2m attrs:")
for k, v in ds['t2m'].attrs.items():
print(f" {k}: {v}")
print(f"\nRange suhu : {float(ds['t2m'].min()):.2f} K – {float(ds['t2m'].max()):.2f} K")
=== Dataset sintetis GFS T2m ===
dims : {'latitude': 69, 'longitude': 185}
data_vars: ['t2m']
coords : ['latitude', 'longitude', 'valid_time', 'step']
t2m attrs:
long_name: 2 metre temperature
units: K
GRIB_shortName: 2t
GRIB_typeOfLevel: heightAboveGround
GRIB_level: 2
Range suhu : 297.91 K – 303.40 K
Output menunjukkan struktur Dataset yang akan kita temui saat membuka file GRIB2 asli dengan cfgrib: koordinat latitude, longitude, valid_time, dan step sudah terpasang otomatis, beserta atribut GRIB seperti GRIB_shortName dan GRIB_typeOfLevel. Ini penting karena kita perlu typeOfLevel untuk filter saat file memuat banyak level sekaligus.
Sekarang kita demonstrasikan subsetting spasial menggunakan Dataset di atas. Misalnya kita ingin fokus ke wilayah Kalimantan dan Laut Jawa.
# Subset Kalimantan + Laut Jawa: lat -4 sampai 6, lon 108 sampai 117
subset = ds["t2m"].sel(
latitude=slice(6, -4), # xarray slice: nilai turun karena lats menurun
longitude=slice(108, 117),
)
print("=== Subset Kalimantan ===")
print(f"Shape : {subset.shape} (lat × lon)")
print(f"Lat range: {float(subset.latitude.min()):.2f}° – {float(subset.latitude.max()):.2f}°")
print(f"Lon range: {float(subset.longitude.min()):.2f}° – {float(subset.longitude.max()):.2f}°")
print(f"T2m min : {float(subset.min()):.2f} K ({float(subset.min()) - 273.15:.1f} °C)")
print(f"T2m max : {float(subset.max()):.2f} K ({float(subset.max()) - 273.15:.1f} °C)")
print(f"T2m mean : {float(subset.mean()):.2f} K ({float(subset.mean()) - 273.15:.1f} °C)")
# Alternatif: isel untuk subset berdasarkan indeks
print("\n=== Contoh isel (5 baris pertama, 5 kolom pertama) ===")
print(ds["t2m"].isel(latitude=slice(0, 5), longitude=slice(0, 5)).values.round(2))
=== Subset Kalimantan ===
Shape : (41, 37) (lat × lon)
Lat range: -4.00° – 6.00°
Lon range: 108.00° – 117.00°
T2m min : 297.91 K (24.8 °C)
T2m max : 302.53 K (29.4 °C)
T2m mean : 300.42 K (27.3 °C)
=== Contoh isel (5 baris pertama, 5 kolom pertama) ===
[[300.33 299.52 300.6 300.71 298.98]
[301.08 300.27 299.42 299.34 301.15]
[300.17 300.96 300.68 300.67 300.5 ]
[299.99 299.72 299.56 299.43 299.65]
[300.79 300.9 299.58 299.07 300.25]]
Metode .sel() bekerja dengan nilai koordinat, sedangkan .isel() bekerja dengan indeks integer — keduanya perlu kita kuasai karena output GFS sering membutuhkan keduanya: .sel() untuk memotong domain geografis, .isel() untuk mengambil satu step waktu dari Dataset multi-step.
Memvisualisasikan Lapangan Angin dan Suhu GFS
Setelah data ter-parse dan ter-subset, langkah berikutnya adalah visualisasi. Dalam workflow nyata, kita tinggal memasukkan variabel t2m, u10, dan v10 dari Dataset cfgrib ke plotting routine di bawah ini. Di sini kita generate field sintetis yang tampilannya identik dengan output GFS sesungguhnya, sehingga output berikut mensimulasikan tampilan data GFS setelah diunduh dan diproses menggunakan cfgrib.
Snippet ini menghasilkan peta suhu 2 m (pcolormesh dengan colormap RdYlBu_r) dan vektor angin 10 m (quiver setiap 5 grid point) di atas peta kartografis Indonesia menggunakan cartopy.
import numpy as np
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import cartopy.feature as cfeature
# --- Buat field sintetis GFS ---
lats = np.linspace(6, -11, 69)
lons = np.linspace(95, 141, 185)
lon2d, lat2d = np.meshgrid(lons, lats)
rng = np.random.default_rng(seed=7)
# Suhu 2 m: gradien realistis 296–305 K
t2m = (
300.5
- 0.04 * lat2d
+ 0.005 * (lon2d - 118)
+ rng.normal(0, 0.5, lon2d.shape)
).astype(np.float32)
# Angin 10 m: angin timuran lemah (~2–5 m/s) khas musim kemarau
u10 = -3.5 + rng.normal(0, 0.8, lon2d.shape) # komponen zonal (negatif = timuran)
v10 = 0.5 + rng.normal(0, 0.6, lon2d.shape) # komponen meridional kecil
# --- Plot ---
proj = ccrs.Mercator()
fig, ax = plt.subplots(
figsize=(12, 7),
subplot_kw={"projection": proj},
)
# pcolormesh suhu
pcm = ax.pcolormesh(
lons, lats, t2m,
transform=ccrs.PlateCarree(),
cmap="RdYlBu_r",
vmin=296, vmax=306,
shading="auto",
)
# Quiver angin setiap 5 grid point
step = 5
ax.quiver(
lons[::step], lats[::step],
u10[::step, ::step], v10[::step, ::step],
transform=ccrs.PlateCarree(),
scale=80, width=0.002,
color="black", alpha=0.65,
)
# Fitur kartografis
ax.add_feature(cfeature.COASTLINE, linewidth=0.7, color="white")
ax.add_feature(cfeature.BORDERS, linewidth=0.4, color="white", linestyle="--")
ax.add_feature(cfeature.LAND, facecolor="none", edgecolor="none")
# Extent Indonesia
ax.set_extent([95, 141, -11, 6], crs=ccrs.PlateCarree())
# Gridlines
gl = ax.gridlines(
draw_labels=True, linewidth=0.4,
color="gray", alpha=0.6, linestyle="--",
)
gl.top_labels = False
gl.right_labels = False
# Colorbar dan judul
cbar = plt.colorbar(pcm, ax=ax, orientation="vertical", pad=0.03, shrink=0.85)
cbar.set_label("Suhu 2 m [K]", fontsize=11)
ax.set_title(
"GFS F+24 — Prakiraan Suhu dan Angin Permukaan Indonesia",
fontsize=13, fontweight="bold", pad=10,
)
plt.tight_layout()
plt.savefig("gfs_indonesia_forecast.png", dpi=120, bbox_inches="tight")
print("Peta disimpan: gfs_indonesia_forecast.png")
Peta yang dihasilkan menunjukkan gradien suhu permukaan yang khas musim kemarau Juli: wilayah perairan Laut Banda dan Samudra Hindia selatan Jawa cenderung sedikit lebih hangat, sementara area Sulawesi utara dan Filipina selatan sedikit lebih dingin relatif terhadap rata-rata domain. Vektor angin memperlihatkan pola timuran yang dominan di selatan ekuator — konsisten dengan posisi ITCZ yang bergeser ke utara ekuator selama Juli. Begitulah tampilan visual yang akan kita peroleh saat menggunakan field suhu dan angin dari file GRIB2 GFS asli.
Langkah Selanjutnya: Integrasi dan Automasi
Begitu workflow dasar di atas berjalan, ada beberapa arah pengembangan yang natural.
Automasi download harian. Kita bisa jadwalkan curl ke Grib Filter via cron atau systemd timer. Dengan satu siklus per hari (misalnya 00 UTC) dan F+24 sebagai target, pipeline download + parsing + plotting bisa selesai dalam hitungan menit. Output peta bisa disimpan lokal atau dikirim via email/webhook.
Konversi dan inspeksi dengan wgrib2 atau ecCodes. Tool command-line seperti wgrib2 dan grib_ls (bagian dari ecCodes) berguna untuk inspeksi cepat isi file GRIB2 tanpa Python — terutama saat perlu debug filter_by_keys yang tepat untuk variabel yang kurang umum.
Membandingkan dengan AIGFS dan GraphCast. NOAA menerbitkan output AIGFS di jalur terpisah dari NOMADS. Jalankan kedua model untuk kasus yang sama — misalnya prediksi trek TC — lalu bandingkan hasilnya secara visual. Latihan ini sangat berguna untuk memahami trade-off antara model fisika berbasis persamaan dan model berbasis machine learning.
Menambahkan variabel pressure-level. MSLP dan suhu 2 m adalah titik awal, tetapi untuk analisis sinoptik yang lebih dalam kita butuh geopotential 500 hPa, angin 850 hPa, dan specific humidity. Semua tersedia di file pgrb2.0p25 yang sama — tinggal sesuaikan filter_by_keys ke typeOfLevel: isobaricInhPa.
Eksplorasi artikel meteorologi lainnya di meteo.my.id — termasuk tutorial ERA5 dengan xarray, analisis CAPE/CIN, dan pemetaan lanjutan dengan cartopy. Kunjungi meteo.my.id.
Referensi
- NCEP/EMC — Global Forecast System — Dokumentasi resmi GFS: FV3 dynamical core ~13 km (C768), 127 lapisan vertikal, siklus 00/06/12/18 UTC, prakiraan hingga 384 jam.
- NOMADS — NOAA Operational Model Archive and Distribution System — Server distribusi real-time GFS GRIB2 resolusi 0,25°/0,50°/1,00° beserta Grib Filter untuk subsetting variabel dan bbox spasial.
- NOMADS Grib Filter Migration Guide — Referensi parameter Grib Filter: nama variabel, level type, dan format URL untuk download subset Indonesia.
- cfgrib — GitHub ecmwf/cfgrib — Library Python untuk membaca GRIB2 sebagai xarray Dataset;
filter_by_keysuntuk memilih typeOfLevel dan level tertentu; lazy loading otomatis. - NOAA EPIC — AIGFS v1.0 Operational — Pengumuman 17 Desember 2025: AIGFS berbasis GraphCast resmi operasional bersama GFS tradisional, dengan peningkatan skill untuk track siklon tropis.
Tidak ada komentar:
Posting Komentar