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

检查数值范围是否合适

相关推荐
zbyyd4 分钟前
Linux 进程间通信学习笔记
linux·笔记·学习
w200512243 小时前
Pod管理
linux·服务器·云原生·容器·k8s·podman
chen<>5 小时前
C++ 六种 memory_order:从原子性到线程间可见性.md
java·linux·开发语言·c++
一直C5 小时前
Linux系统编程|进程间通信IPC全套详解(1)(管道、信号)
linux·c语言·开发语言·网络·青少年编程·visual studio code
库玛西5 小时前
Linux网络编程:HTTP/HTTPS协议核心技术全解析
linux·运维·服务器·网络·c++·http·https
sxd20015 小时前
Vmware和multicast
linux·windows·vmware·无线桥接·multicast
暴力求解5 小时前
Linux网络---NAT代理服务、内网穿透
linux·网络·智能路由器
一直C6 小时前
Linux系统编程|信号进阶 + SystemV IPC(消息队列、共享内存)
linux·c语言·开发语言·算法·青少年编程·vim
_F_y6 小时前
Linux网络常用指令
linux·运维·网络
老当益壮梁奶奶7 小时前
Linux软件编程学习笔记(九):消息队列、共享内存与信号灯详解
linux·c语言·笔记·学习