ERA5生成驱动LBM模型的数据

参考致谢:

ERA5基本态驱动LBM-专业气象研究-气象家园_气象人自己的家园

0.安装nc库

复制代码
cd ~/lbm
# 新建存放库的总文件夹
mkdir -p libs/src libs/netcdf_install

sudo apt update
sudo apt install -y build-essential gfortran libz-dev libhdf5-dev wget make
# 验证gfortran版本,确认是9.4.0
gfortran --version

# 复制主模型目录下的intp.f到util
cp /home/accept7/lbm/ln_solver/solver/custom/intp.f ~/lbm/ln_solver/solver/util/

1.准备ERA5资料

下载ERA5月尺度的资料,主要资料为23层的u,v,t,z,q,和单层的slp

2.原始的ERA5数据从0.25°插值到2.5度,每个变量对应输出一个nc

复制代码
import xarray as xr
import numpy as np
from pathlib import Path

# ======================
# 配置(和你原来完全一样)
# ======================
data_dir = Path("I:/LBM/Data2")
out_dir = Path("I:/LBM/Data2")

lon_target = np.arange(0, 360, 2.5)
lat_target = np.arange(-90, 90.1, 2.5)

var_config = [
    # ("t", "t"),
    # ("z", "z"),
    ("q", "q"),
    # ("u", "u"),
    # ("v", "v"),
    # ("slp", "slp")
]

# ======================
# 逐变量处理
# ======================
for var_orig, out_suffix in var_config:
    in_file = data_dir / f"ERA5_{var_orig}.nc"
    out_file = out_dir / f"ERA5_{out_suffix}_res2.5.nc"

    print(f"\n处理变量: {var_orig}")
    print(f"输入: {in_file.name}")

    ds = xr.open_dataset(in_file)
    ds = ds.rename({"valid_time": "time"})

    nt = len(ds.time)
    result = []

    # ======================
    # 逐时间步插值(绝不爆内存)
    # ======================
    for tidx in range(nt):
        print(f"  时间步 {tidx + 1}/{nt}")

        # 只取当前时间片
        ds_t = ds.isel(time=tidx)

        # 插值到 2.5°
        if var_orig == "slp":
            di = ds_t.interp(
                longitude=lon_target,
                latitude=lat_target,
                method="linear"
            )
        else:
            di = ds_t.interp(
                longitude=lon_target,
                latitude=lat_target,
                pressure_level=ds.pressure_level,
                method="linear"
            )

        # ======================
        # 填补 NaN:0°经线 + 两极
        # ======================
        di = di.fillna(0.0)

        result.append(di)

    # 合并时间
    ds_out = xr.concat(result, dim="time")

    # 位势高度单位转换
    if var_orig == "z":
        ds_out["z"] = ds_out["z"] / 9.80665
        ds_out["z"].attrs["units"] = "m"
        ds_out["z"].attrs["long_name"] = "Geopotential Height"

    # 保存(经典格式,不压缩、不报错)
    ds_out.to_netcdf(out_file, format="NETCDF4_CLASSIC")
    print(f"✅ 已保存: {out_file.name}")

3.因为层数不同,所以将平均态时段的所有数据转换为slp_for_fortran.nc的和upper_for_fortran.nc两个文件

复制代码
import xarray as xr
import numpy as np
import xarray as xr

data_dir = "I:/LBM/Data2/"

# 读取 2.5° 数据
ds_air  = xr.open_dataset(data_dir + "ERA5_t_res2.5.nc")
ds_hgt  = xr.open_dataset(data_dir + "ERA5_z_res2.5.nc")
ds_shum = xr.open_dataset(data_dir + "ERA5_q_res2.5.nc")
ds_uwnd = xr.open_dataset(data_dir + "ERA5_u_res2.5.nc")
ds_vwnd = xr.open_dataset(data_dir + "ERA5_v_res2.5.nc")
ds_slp  = xr.open_dataset(data_dir + "ERA5_slp_res2.5.nc")

def select_period(ds,start_time,end_time):
    return ds.sel(time=slice(start_time, end_time))

# start_time='2000-01-01'
# end_time='2025-12-31'
# nc_name="era520002025.nc"

start_time='1970-01-01'
end_time='2000-12-31'
nc_name="era519702000.nc"

ds_air  = select_period(ds_air,start_time, end_time)
ds_hgt  = select_period(ds_hgt,start_time, end_time)
ds_shum = select_period(ds_shum,start_time, end_time)
ds_uwnd = select_period(ds_uwnd,start_time, end_time)
ds_vwnd = select_period(ds_vwnd,start_time, end_time)
ds_slp  = select_period(ds_slp,start_time, end_time)



# 变量名严格匹配 Fortran
air  = ds_air['t'].rename('air')
hgt  = ds_hgt['z'].rename('hgt')
shum = ds_shum['q'].rename('shum')
uwnd = ds_uwnd['u'].rename('uwnd')
vwnd = ds_vwnd['v'].rename('vwnd')
slp  = ds_slp['msl'].rename('slp')



# 月平均
air_clim  = air.groupby('time.month').mean(dim='time')
hgt_clim  = hgt.groupby('time.month').mean(dim='time')
shum_clim = shum.groupby('time.month').mean(dim='time')
uwnd_clim = uwnd.groupby('time.month').mean(dim='time')
vwnd_clim = vwnd.groupby('time.month').mean(dim='time')
slp_clim  = slp.groupby('time.month').mean(dim='time')

# 高层场:4维 (lon, lat, pressure_level(23), month)
air_out  = air_clim.transpose("longitude", "latitude", "pressure_level", "month")
hgt_out  = hgt_clim.transpose("longitude", "latitude", "pressure_level", "month")
shum_out = shum_clim.transpose("longitude", "latitude", "pressure_level", "month")
uwnd_out = uwnd_clim.transpose("longitude", "latitude", "pressure_level", "month")
vwnd_out = vwnd_clim.transpose("longitude", "latitude", "pressure_level", "month")

# ===================== SLP 关键正确写法 =====================
# 变成 4 维,第三维长度=1,名字随便叫一个,不和 pressure_level 冲突
slp_out = slp_clim.expand_dims(dim='lev', axis=2)
slp_out = slp_out.transpose("longitude", "latitude", "lev", "month")
slp_out = slp_out / 100.0
slp_out.attrs['units'] = 'hPa'
# ==============================================================

# 合成输出
ds_out = xr.Dataset({
    'air'         : air_out,
    'hgt'         : hgt_out,
    'shum'        : shum_out,
    'uwnd'        : uwnd_out,
    'vwnd'        : vwnd_out,
    'slp'         : slp_out,    # 4 维:(144,73,1,12)
})

# 保存
ds_out.to_netcdf(data_dir + nc_name, format="NETCDF4_CLASSIC")

print("✅ 生成完成!")
print("✅ pressure_level 保持 23 层不变")
print("✅ slp 是 4 维,第三维长度=1,完全匹配 PRES(nx,ny,1,nt)")



#=================================分成两个文件
ds = xr.open_dataset(data_dir + 'era520002025.nc')

# 👇 核心:把 (month, pressure_level, latitude, longitude) 转成 (longitude, latitude, pressure_level, month)
ds['hgt'] = ds['hgt'].transpose("longitude", "latitude", "pressure_level", "month")
ds['air'] = ds['air'].transpose("longitude", "latitude", "pressure_level", "month")
ds['uwnd'] = ds['uwnd'].transpose("longitude", "latitude", "pressure_level", "month")
ds['vwnd'] = ds['vwnd'].transpose("longitude", "latitude", "pressure_level", "month")
ds['shum'] = ds['shum'].transpose("longitude", "latitude", "pressure_level", "month")

# 清空 coordinates 属性(保险)
for var in ds.data_vars:
    if "coordinates" in ds[var].attrs:
        del ds[var].attrs["coordinates"]

# 保存
ds[["hgt","air","uwnd","vwnd","shum"]].to_netcdf(
    "I:\\LBM\\Data2\\upper_for_fortran_0025.nc",
    format="NETCDF3_CLASSIC"
)

# ======================
# 3. 拆分并清理 SLP(关键!这里要删 number)
# ======================
ds_slp = ds[["slp"]].squeeze(drop=True)  # 去掉 lev=1 维


if "number" in ds_slp:
    ds_slp = ds_slp.drop_vars("number")
# 再确认清理 coordinates
if "coordinates" in ds_slp["slp"].attrs:
    del ds_slp["slp"].attrs["coordinates"]

ds_slp.to_netcdf(
    "I:\\LBM\\Data2\\slp_for_fortran_0025.nc",
    format="NETCDF3_CLASSIC"
)

4.用create_lbm_era5_bs_T42.f90将nc文件转为模型需要的grd格式

复制代码
cp /mnt/hgfs/Data/upper_for_fortran_0025.nc ~/lbm/ln_solver/solver/util/
cp /mnt/hgfs/Data/slp_for_fortran_0025.nc ~/lbm/ln_solver/solver/util/

cp /mnt/hgfs/Data/upper_for_fortran_7000.nc ~/lbm/ln_solver/solver/util/
cp /mnt/hgfs/Data/slp_for_fortran_7000.nc ~/lbm/ln_solver/solver/util/

58行  filepath = "/home/accept7/lbm/ln_solver/solver/util/upper_for_fortran_final.nc"
90行  filepath = "/home/accept7/lbm/ln_solver/solver/util/slp_for_fortran.nc"
104行 filepath = "/home/accept7/lbm/ln_solver/solver/util/upper_for_fortran_final.nc"
147行  为输出的grd的名称
OPEN(UNIT=1,FILE='era5.clim.y70-00.t42.grd',FORM='unformatted',CONVERT='big_endian')

编译运行create_lbm_era5_bs_T42.f90

复制代码
gfortran \
-I/usr/include \
-L/usr/lib/x86_64-linux-gnu \
create_lbm_era5_bs_T42.f90 \
-o create_lbm_era5_bs_T42 \
-lnetcdff -lnetcdf

./create_lbm_era5_bs_T42

检查输出的grd数据是否合理

复制代码
DSET ^era5.clim.y70-00.t42.grd
OPTIONS SEQUENTIAL YREV BIG_ENDIAN
undef -9.9e8
title ERA5 T42 L23 Climatology for LBM ecmsbs

XDEF 128 LINEAR 0.0 2.8125
YDEF 64 levels
-87.8638 -85.0965 -82.3129 -79.5256 -76.7369 -73.9475 -71.1577
-68.3678 -65.5776 -62.7873 -59.9970 -57.2066 -54.4162 -51.6257 -48.8352 -46.0447 -43.2542 -40.4636 -37.6731 -34.8825 -32.0919 -29.3014 -26.5108 -23.7202 -20.9296 -18.1390 -15.3484 -12.5578 -9.76715 -6.97653 -4.18592 -1.39531 1.39531 4.18592 6.97653 9.76715 12.5578 15.3484 18.1390 20.9296 23.7202 26.5108 29.3014 32.0919 34.8825 37.6731 40.4636 43.2542 46.0447 48.8352 51.6257 54.4162 57.2066 59.9970 62.7873 65.5776 68.3678 71.1577 73.9475 76.7369 79.5256 82.3129 85.0965 87.8638

ZDEF 23 levels
1000 925 850 775 700 600 500 400 300 250 200 150 100 70 50
30 20 10 7 5 3 2 1

TDEF 12 LINEAR 00Z01jan1970 1mon

VARS 6
u     23  99  Zonal Wind (m/s)
v     23  99  Meridional Wind (m/s)
t     23  99  Temperature (K)
z     23  99  Geopotential Height (gpm)
q     23  99  Specific Humidity (kg/kg)
slp    0  99  Sea Level Pressure (hPa)
ENDVARS

grads -l

open era5.clim.y00-25.t42.ctl
set t 1 last
set z 1 last
d u
d v
d t
d z
d q
d slp

检查数值范围是否合适

相关推荐
_upupup8 小时前
Linux的调试工具——gdb/cgdb
linux·服务器
JeJe同学8 小时前
Docker镜像导入、容器启动
linux·运维·docker
Jason_zhao_MR8 小时前
Linux与实时控制如何兼得
linux·嵌入式硬件·机器人·工业控制
闲云野鹤在人间9 小时前
MySQL|从理论、安装、备份到主从复制、MHA高可用详解
linux·运维·数据库·mysql·云计算
一个风轻云淡10 小时前
GCC 和 GDB命令简单解读
java·linux·前端
H.莓飛10 小时前
【数据结构】二叉树_OJ题
linux·开发语言·数据结构·算法
资料库0110 小时前
Linux 8个常见运维故障排查
java·linux·运维
零基础12311 小时前
Ubuntu 常用命令汇总
linux·运维·开发语言
Android系统攻城狮12 小时前
Linux Gstreamer深度解析之gst_audio_sink_get_type调用流程与实战(六十五)
linux·运维·服务器·音视频·gstreamer音视频·音视频进阶·gstreamer音视频进阶
xiaoqiMikko12 小时前
Linux 实战(一):CentOS 7、Rocky 9、Ubuntu 24.04 三台真机实测 —— 认清系统、换源、装包的版本坑
linux·centos