CLM独立运行完整实战:从环境配置到结果可视化指南
2026/9/24 20:57:23 网站建设 项目流程

1. 先说清楚:CLM独立运行到底解决什么问题,以及你学它值不值

先说个我在超级计算机上花了整整一个学期才想明白的道理:CESM全耦合模式不适合用来学陆面过程,CLM独立运行才是入门和科研的正确姿势。

CESM(Community Earth System Model)的完整版把大气、海洋、陆面、海冰四个模块通过耦合器拼在一起,跑一次全球十年模拟,动辄要几千核并行、算个几天几夜。更麻烦的是,四个模块互相牵制,只要你改了一个参数,整个系统就要重新磨合平衡。做陆面过程研究的人——比如我当年研究土壤湿度对降水的影响,实际关心的只是CLM(Community Land Model)这一个模块,却要为大气模式的动力核心、海洋模式的网格分辨率买单,纯属浪费计算资源和时间。

CLM独立运行(也叫CLM off-line mode)的思路很简单:把陆面模块从CESM里单独拆出来,给它提供一个已经算好的大气强迫场(通常来自观测数据或再分析资料),让它自己闭门修炼。这时候你的输入是气温、降水、辐射、风速这些气象要素,输出是土壤温湿度、径流、蒸散发、碳通量、植被状态,完全由陆面过程本身决定。这不只省了计算开销,更重要的是把问题简化到了纯粹可控的陆面过程模拟层面。

我梳理了一下,下面这几类人最应该提前掌握这套独立运行的完整技能:

  • 研究陆面过程机理本身的(土壤水热、蒸散发、碳循环、冻土演变)
  • 给某个流域、某块农田做水循环或能量平衡模拟的
  • 需要为其他模型提供陆面下边界条件的
  • 想在CLM里加入新的植被参数化方案、陆面模块二次开发的
  • 以及像我当年一样,毕业论文只需要陆面结果,但不想把时间耗在跑通整个CESM上的人

这篇文章要讲的就是一套完整的、可以直接复现的CLM独立运行流程,从环境配置、文件下载、编译,一路走到结果处理和可视化出图。硬核但不玄乎,你在自己机器上就能跟着一步步做下去。

2. 环境准备与编译配置:文件锁死版本,省掉99%的无头绪报错

CLM2CESM这套系统最折磨人的不是代码本身,而是版本的互相兼容问题。我见过太多新手一上来就在官网把最新的CESM3代码拖下来,然后被一堆接口报错碾碎信心。这里我给出一套经过了大量实践验证、稳定可用的版本组合和配套环境,按这个来踩坑最少。

2.1 推荐版本组合与硬件要求

根据实际经验,我推荐使用下面这套组合:

组件推荐版本说明
CESM代码包cesm2_1_1 或2.1.3这两个版本对CLM5.0支持完善,社区资料多,报错好查
netCDF库netcdf-c 4.7.4 + netcdf-fortran 4.5.3前后端分离,编译器要求清晰
MPI库mpich 3.3.2(串行可不装)跑大区域或全球网格时强烈建议并行
Fortran编译器gfortran 9.x 或 Intel ifort 19+多数Linux发行版自带gfortran,最省事
Python3.8以上后面做可视化用,先配好环境

为什么强调这个版本组合?因为这些版本之间的接口约定是匹配好的,CLM5.0的代码里硬编码了很多对netCDF库接口的期望,比如老版本netcdf-c在处理时间单位字符串时会跟新版本有细微差异,直接导致读取数据边界错位。我在实验室的一台CentOS 7服务器上一开始装的是netcdf-c 4.9.2,CLM编译倒是能过,但运行时读初始条件文件一直报内存越界,后来退回到4.7.4才正常。这个坑值得你留意。

操作系统的要求其实很宽松:Ubuntu 20.04/22.04、CentOS 7/8、openSUSE都行,macOS也能跑(但需要brew装gcc和netcdf),Windows用户建议直接上WSL2或装虚拟机。

2.2 环境变量与依赖库编译

以Ubuntu系统为例,先把基础依赖装齐:

sudo apt update sudo apt install -y build-essential gfortran m4 perl \ libxml2-dev libcurl4-openssl-dev \ libhdf5-dev libopenmpi-dev openmpi-bin

然后编译安装netCDF。这里有个关键点:必须先编译netcdf-c,再编译netcdf-fortran,前者是后者的基础,顺序反了必然失败。

# 编译 netcdf-c wget https://github.com/Unidata/netcdf-c/archive/refs/tags/v4.7.4.tar.gz tar -xzf v4.7.4.tar.gz cd netcdf-c-4.7.4 ./configure --prefix=/usr/local/netcdf --disable-dap make -j4 && sudo make install # 编译 netcdf-fortran wget https://github.com/Unidata/netcdf-fortran/archive/refs/tags/v4.5.3.tar.gz tar -xzf v4.5.3.tar.gz cd netcdf-fortran-4.5.3 export LD_LIBRARY_PATH=/usr/local/netcdf/lib:$LD_LIBRARY_PATH export CPPFLAGS="-I/usr/local/netcdf/include" export LDFLAGS="-L/usr/local/netcdf/lib" ./configure --prefix=/usr/local/netcdf make -j4 && sudo make install

最后把环境变量写进~/.bashrc,确保每次登录自动生效:

export NETCDF=/usr/local/netcdf export PATH=$NETCDF/bin:$PATH export LD_LIBRARY_PATH=$NETCDF/lib:$LD_LIBRARY_PATH export LIBRARY_PATH=$NETCDF/lib:$LIBRARY_PATH export CPATH=$NETCDF/include:$CPATH

注意:串行和并行的netCDF建议分开安装。如果你要用MPI并行跑CLM,配套的netcdf也要用--enable-parallel重新编译一份,否则并行I/O会挂。我第一次没注意这个,一跑mpirun -np 8就报NetCDF: Unknown file format,排查了半天才发现是串行版库文件顶替了并行版。

2.3 下载CESM源代码与CLM部分

代码获取最稳妥的方式是通过Git和SVN组合拉取:

# 创建代码目录 mkdir -p ~/cesm_code cd ~/cesm_code # 用git克隆管理脚本 git clone https://github.com/ESCOMP/cesm.git cesm2_1_1 cd cesm2_1_1 git checkout cesm2_1_1

第一次checkout之后代码会指引你运行脚本来拉取所有子模块。这一步对网络要求比较苛刻,因为要同时从GitHub和SVN服务器上拉一堆组件,经常断线。建议用脚本里的重试机制多跑几次:

./manage_externals/checkout_externals

如果网络不稳定,也可以单独把CLM部分拉下来:

cd components # 如果clm目录为空,直接从svn拉取 svn export https://svn-ccsm-models.cgd.ucar.edu/clm2/trunk/ ./clm

CLM的源代码体积不小,下载完成后确认目录大小在2GB以上才算正常。跑一次du -sh确认一下,小于这个数基本说明有子模块没拉全。

2.4 创建Machine文件与配置编译

这是CLM独立运行里最容易被人忽略的一步create_newcase脚本在创建Case时需要知道当前机器上的编译器、MPI命令、作业调度系统是什么,这些信息都写在config/cesm/machines/config_machines.xml里。如果你的机器不在默认列表中,脚本会直接报错说找不到匹配项。

解决办法是自己定义一个machine。在config_machines.xml里追加一段:

<machine MACH="myworkstation"> <DESC>Local workstation</DESC> <OS>LINUX</OS> <COMPILERS>gnu</COMPILERS> <MPILIBS>mpich</MPILIBS> <PROJECT>none</PROJECT> <SAVE_TIMING>FALSE</SAVE_TIMING> <CIME_OUTPUT_ROOT>/home/yourname/cesm_runs</CIME_OUTPUT_ROOT> <DIN_LOC_ROOT>/home/yourname/cesm_inputdata</DIN_LOC_ROOT> <DIN_LOC_ROOT_CLMFORC>/home/yourname/cesm_inputdata/atm_forcing.datm7</DIN_LOC_ROOT_CLMFORC> <RUNDIR>/home/yourname/cesm_runs/run</RUNDIR> <EXEROOT>/home/yourname/cesm_runs/bld</EXEROOT> </machine>

里面的路径按你自己的目录结构改就行。DIN_LOC_ROOT是输入数据根目录,DIN_LOC_ROOT_CLMFORC专门指大气强迫数据目录,这两个路径后面下载数据时要用到。

接下来是编译。以CLM5.0独立运行的典型配置为例:

cd ~/cesm_code/cesm2_1_1/cime/scripts ./create_newcase --case /home/yourname/cesm_runs/clm_single_site \ --compset I2000Clm50SpGs \ --res 1x1_brazil --machine myworkstation --run-unsupported

参数解释一下:

  • I2000Clm50SpGs:表示2000年初始条件,CLM5.0,Sp是卫星植被数据驱动,Gs是地表数据使用网格尺度。这是陆面独立模拟最常用的compset。
  • res 1x1_brazil:单点网格,巴西某个点。如果你要跑某个站点(比如通量观测站),CESM里预置了一批1x1_xxx的单点grid,你可以用query_defaults查看可用选项。
  • --run-unsupported:因为我们是自定义的workstation机器,必须加上这个参数否则脚本直接拒绝执行。

创建完成后进入Case目录,开始配置编译:

cd /home/yourname/cesm_runs/clm_single_site ./xmlchange STOP_N=5,STOP_OPTION=nyears # 模拟5年 ./xmlchange RUN_STARTDATE=2000-01-01 ./xmlchange DATM_CLMNCEP_YR_ALIGN=2000 # 强迫数据与模拟年份对齐 ./xmlchange DATM_CLMNCEP_YR_START=2000 ./xmlchange DATM_CLMNCEP_YR_END=2004 ./case.setup ./case.build

编译时间取决于机器配置,一般20到60分钟。看到case.build毫无报错地跑完,你的环境配置这一大关就算过了。

3. 独立运行的三块硬骨头:domain文件、大气强迫数据、namelist参数

环境编译过了,你只是有了一个能跑的空壳。CLM能不能真正转起来并输出有意义的结果,取决于三样东西:domain文件(模拟区域定义)、大气强迫数据(外源气象驱动)、以及运行控制参数(namelist)。这三块的坑我挨个说说。

3.1 domain文件:定义你要模拟的地球表面

domain文件说白了就是告诉CLM:你的模拟区域有多少个网格点?每个网格点的经度纬度是多少?哪些网格点是陆地、哪些是海洋/湖泊?陆地占每个网格的面积比例是多少?

对新手而言,最稳的路径是利用CESM自带的工具从地表数据生成domain文件,而不是自己去构造。在components/clm/tools/mkmapdata/下有个mkmapdata.sh脚本,它会读取CLM的地表数据集并生成对应分辨率的domain文件。

但单点模拟时还有一个更省事的做法:CESM在输入数据仓库里直接预置了和各个站点网格对应的domain和surfdata文件。比如运行结果里会提示你下载:

# 查看case里设置的输入数据路径 ./xmlquery DIN_LOC_ROOT # 在该目录下按如下结构放置文件 # 以单点为例(比如1x1_brazil): # ${DIN_LOC_ROOT}/share/domains/domain.lnd.1x1_brazil_navy.090715.nc # ${DIN_LOC_ROOT}/lnd/clm2/surfdata_map/surfdata_1x1_brazil_hist_16pfts_Irrig_CMIP6_simyr2000_c190214.nc

这些文件的下载地址在${DIN_LOC_ROOT}/lnd/clm2/surfdata_map/下面有个README文件里有完整链接列表。用wget批量拉下来就行。

一个重要的提醒:domain文件里的网格坐标必须和surfdata以及大气强迫数据完全对齐。很多时候模拟出来的结果出现奇怪的斑块状异常,就是因为domain文件里陆地mask和强迫数据的格点错位了半度。我会在第四部分再详细教你验证这个对齐关系。

3.2 大气强迫数据:CLM模拟的"外部燃料"

独立运行的CLM自己不会算大气,它需要外部输入的辐射、降水、气温、风速、气压、比湿。CESM官方推荐使用GSWP3或CRUNCEP这两套再分析数据集。

下载强迫数据的时候,系统一般会自动通过DATM_CLMNCEP相关XML变量定位到输入数据服务器。手动下载也行,但文件体积很大——全球范围逐小时的气象数据动辄几个GB,建议只下你模拟区域所在的那部分数据。站点模拟通常用.nc的单点多文件,也可以在inputdata/atm_forcing.datm7/下看到按数据集和年份组织好的目录。

强迫数据的时间覆盖很关键。你在DATM_CLMNCEP_YR_STARTDATM_CLMNCEP_YR_END里指定的年份,必须严格落在你下载的forcing文件覆盖范围内。比如你想跑2000到2004年,那么至少要有2000到2004年的逐小时forcing文件。我遇到过缺2001年4月数据的情况,CLM直接在某一个时间步上算出NaN,接着整个碳循环模块就崩了。

3.3 namelist参数:控制模拟走向的旋钮

在Case目录跑过一次./case.setup之后,会生成namelist相关的模板文件。CLM的关键运行参数在user_nl_clm里维护,这是你需要手动编辑的最核心文件。

一份典型的user_nl_clm长这样:

! 输出控制:设置历史文件输出频率 hist_fincl1 = 'FSDS','FSR','FSA','FIRA','FIRE','FCTR','FCEV','FCS','FGEV','FGR','FGR12','FGR34','FHD','FG' hist_nhtfrq = -24 hist_mfilt = 30 ! 初始条件 finidat = '' ! 输出网格(单点模拟时通常省略,使用默认网格) ! hist_doy2date = .true. ! 时间步长(秒) dtime = 1800 ! 土壤层设置:默认10层,可修改 ! nlevgrnd = 10

我重点解释几个参数的作用:

  • hist_fincl1:你要输出哪些变量的列表。CLM的变量名和ECMWF、NCEP那套不太一样,比如向下短波辐射是FSDS,感热通量是FSH,潜热通量是FCEV+FCTR。选变量时建议先在CLM的文档里查一下变量的含义,不要凭缩写猜,我做第一个实验时把FGR(土壤热通量)当成地表径流,白白浪费了很多分析时间。
  • hist_nhtfrq:输出频率,单位是小时,负数表示小时数,正数表示时间步数。-24就是每天输出一次;要做日尺度的蒸散发分析,这个设置就够用了。
  • finidat:热启动初始文件,空字符串表示从冷启动开始。后续做续跑时这个参数非常重要,要填上之前的restart文件路径。
  • dtime:积分步长,单点模拟1800秒(30分钟)完全够;如果跑全球0.9°分辨率,建议用900秒以防数值不稳定。

配置完成后,提交运行命令:

cd /home/yourname/cesm_runs/clm_single_site nohup ./case.submit > run.log 2>&1 &

单点5年模拟在现代工作站上通常几十分钟就能跑完,多核并行更快。

4. 从提交到产出:实测运行流程和三个最容易翻车的运行细节

编译过了、文件配齐了,很多人觉得万事大吉,结果一跑起来全是幺蛾子。这一节我把实际运行中容易翻车的细节摊开来讲清楚。

4.1 冷启动预热期:前几个月的结果不可直接使用

CLM冷启动时,土壤湿度、土壤温度的初始值仓促给出,和实际气候状态差距巨大,模型需要一个"spin-up"阶段来自己调整植被、土壤水、碳库到平衡态。

我在跑一个亚马逊单点站点时,前6个月的模拟结果里土壤含水量一直在剧烈波动,直接看蒸散发数值会发现它比观测值低了很多。这不是模型bug,而是土壤水库还在蓄水阶段。所以分析结果时必须舍弃前期的预热期。一般建议冷启动后至少舍弃1到2年,再做研究分析;如果你关心的是碳循环,那需要的时间更长,甚至要用finidat做多次循环加速平衡。

怎么判断spin-up是否完成?CLM在运行时会在run/目录下生成.log文件,里面每步都会打印土壤水和碳库的统计。你找到水循环或碳循环相关的诊断量,看它逐年变化是否趋于稳定,如果连续几年波动小于1%,基本可以认为进入平衡态了。

4.2 单点网格的边界效应:坑了我一周的怪相

单点模拟运行时,CLM只计算那一个格点,看起来简单,但实际有隐藏问题:单点格点在半球日射计算里没有邻域信息,处理长波辐射时的天空视角因子会和真实环境有偏差。更常见的是,一些站点位于海岸线附近,1x1_xxx网格里同时包含了一定的海洋面积比例,导致CLM打出奇怪的低植被覆盖度。

我的处理办法是:模拟之前用ncdump -h查看land fraction变量,确定你关心的坐标点上landfrac的数值是否接近1。如果不接近,换一个内陆的单点grid或者改用区域网格,别在海岸边缘硬跑,否则分析结果总差一口气。

4.3 缺少独立的实时观测:验证数据的必须有观有测

CLM独立运行的本质是"强迫-响应"实验,所以它特别依赖高质量的强迫数据,这一点常常被忽略。GSWP3数据集在2003年之前和之后的连续性有跳变,CNRDAP在某些高海拔区域也存在系统性偏差。如果你观测站点附近的气象站数据是能和GSWP3对比的,我强烈建议你做一个简单的一致性检验:

ncdump -v FSDS path/to/forcing/file.nc | head -100

检查辐射通量的日变化曲线是否符合常识(白天高、夜间为0,最大值不超过太阳常数1361 W/m²)。如果连这个基本物理界限都超了,你的模拟结果再漂亮也没用,问题根源在forcing上。

另外一个很实际的小技巧:跑完以后立刻去看run/目录下的.lnd.log.*文件,搜ERRORWARNING。有些警告可以忽略(比如某个格点短暂出现负降水,被代码钳制为0),但只要是ERROR,你就要老老实实回溯数据来源。这个习惯能帮你省下一整周排查问题的时间。

5. 结果处理的三个常用操作:finidat续跑、变量筛选、时间尺度聚合

CLM跑完后,原来的run/目录下会生成一大票历史文件,比如*.h0.*.nc(月平均)、*.h1.*.nc(高频输出)。直接在原始文件上做分析会非常吃力,尤其是跨几十年的模拟,文件数量大到不聚合没法用。所以结果处理这一步值得专门讲。

5.1 用finidat实现平滑续跑

续跑的正确姿势不是重新从冷启动跑一遍,而是利用CLM的restart机制。在Case目录下执行:

./xmlchange RUN_TYPE=branch ./xmlchange RUN_REFCASE=clm_single_site ./xmlchange RUN_REFDATE=2004-01-01

这样CLM会读取2004年1月1日的状态文件(.r*.nc),从这个时间点接着往下跑。这套机制做两件事:一是可以无缝延长模拟年限,二是可以用来做敏感性实验——比如你在某个时间点改变了某个参数,从该时间点的状态文件做branch run,结果的变化就纯粹来自参数改动。

如果你是要做超长spin-up(比如500年以上碳循环平衡),可以循环提交多次案例,每次用上一次的restart文件作为本次的finidat输入:

finidat = '/path/to/run/clm_single_site.clm2.r.2009-01-01-00000.nc'

我可以告诉你一个我测试过的平衡收敛指标:对碳库量(比如TOTVEGC总植被碳)连续跑两段各50年,末态差异小于0.5%,基本可以视为进入准平衡。

5.2 基于xarray的变量筛选与时空裁剪

CLM输出文件是老牌netCDF格式,推荐用xarray来处理,代码简洁、内存管理也灵活:

import xarray as xr ds = xr.open_dataset('clm_single_site.clm2.h0.2000-01.nc') # 查看所有变量 print(ds.data_vars) # 选取温度和降水相关变量 sub = ds[['TSA', 'RAIN', 'SNOW', 'FSDS']] # 裁剪到感兴趣的时间段 sub = sub.sel(time=slice('2000-02-01', '2000-08-01'))

这里有个小坑:CLM的time维度经常带着time_bounds,如果后续要用resample做时间聚合,先检查时间索引是否连续。某些版本的输出时间轴存在重复或缺失,例如闰年处理不当导致2月29日前后时间坐标对齐错乱。

5.3 把逐小时数据聚合到月/季尺度

CLM最高频输出可以到逐半小时,但大多数分析根本不需要这么高的时间分辨率。建议在数据产出的第一时间就做聚合,减少后续处理的负担。用xarray可以一行搞定:

# 月均 monthly = sub.resample(time='1MS').mean() # 季节均(DJF、MAM、JJA、SON) seasonal = sub.resample(time='QS-DEC').mean() # 多年年均 annual = sub.resample(time='1AS').mean()

如果你的time坐标不稳定(比如不是标准日历),可以用groupby配合time.dt.month做多年同月合成:

clim_month = sub.groupby('time.month').mean('time')

这是研究气候态最常用的一招,多年逐月平均之后的曲线会比原始逐日数据平滑得多,也更容易在图上看出气候信号。

6. 用Python把CLM结果画出专业级图件:空间图、时间序列、过程归因

数据处理的终点是理解和呈现。CLM模拟的结果怎么画出来?这里我不推荐去看那些大而全的可视化平台软件,单发一个图很多时候用不上那些重型工具。我自己最常用的组合是Python + xarray + matplotlib + cartopy,画出来的图从投稿标准到日常探索都能覆盖。

6.1 空间分布图:一张图看区域差异

以模拟的蒸散发为例,如果跑的是区域网格,先做多年月均再画空间填色图:

import xarray as xr import matplotlib.pyplot as plt import cartopy.crs as ccrs import cartopy.feature as cfeature ds = xr.open_dataset('clm_regional.clm2.h0.2000-2010.nc') et = ds['FCEV'] + ds['FCTR'] # 总蒸散发 W/m2 et_annual = et.mean('time') # 多年年均 fig = plt.figure(figsize=(12, 6)) ax = plt.axes(projection=ccrs.PlateCarree()) im = ax.pcolormesh(et_annual.lon, et_annual.lat, et_annual, cmap='YlGnBu', shading='auto') ax.coastlines() ax.add_feature(cfeature.BORDERS, linestyle=':') plt.colorbar(im, ax=ax, label='ET (W/m²)', shrink=0.8) ax.set_title('Annual Mean Evapotranspiration (2000-2010)') plt.savefig('et_spatial.png', dpi=300, bbox_inches='tight')

注意事项:CLM的网格通常是不规则网格(比如FV网格是经纬度规则网格,但SE网格就不规则),这种网格画图时要避免直接用pcolormesh,而要先用xr.interp把数据插值到规则的经纬度网格上再画。别小看这一步,不正规则网格直接画出来的图会有明显的网格边界锯齿,编辑看到直接打回。

插值的做法:

et_regrid = et_annual.interp(lat=np.linspace(-60, 80, 280), lon=np.linspace(-180, 180, 720))

6.2 时间序列图:站点模拟的标配

单点模拟最常用的是时间序列。画多年逐月蒸散发曲线时,加一个阴影包络(标准差范围)会更直观:

import numpy as np et = (ds['FCEV'] + ds['FCTR']).squeeze() et_monthly = et.resample(time='1MS').mean() # 多年逐月统计 clim = et_monthly.groupby('time.month').mean('time') std = et_monthly.groupby('time.month').std('time') fig, ax = plt.subplots(figsize=(10, 5)) months = np.arange(1, 13) ax.plot(months, clim, 'o-', color='#1a9850', lw=2) ax.fill_between(months, clim - std, clim + std, color='#1a9850', alpha=0.3) ax.set_xlabel('Month') ax.set_ylabel('ET (W/m²)') ax.set_xticks(months) ax.set_xticklabels(['Jan','Feb','Mar','Apr','May','Jun', 'Jul','Aug','Sep','Oct','Nov','Dec'])

这里fill_between的包络带能让你一眼看出季节性波动的幅度,比如季风区的ET是单峰还是双峰。

6.3 过程归因:把"土壤湿度-蒸散发"耦合画成散点密度图

我做的研究里最能体现CLM价值的一步,是把模拟输出的土壤湿度与蒸散发关系画出来,用来诊断陆气耦合强度:

soil = ds['H2OSOI'].isel(levgrnd=0, time=slice(None, None, 3)) # 表层土壤湿度 et = (ds['FCEV'] + ds['FCTR']).isel(time=slice(None, None, 3)) fig, ax = plt.subplots(figsize=(6, 6)) hb = ax.hexbin(soil.values.flatten(), et.values.flatten(), gridsize=50, cmap='viridis', mincnt=1) plt.colorbar(hb, ax=ax, label='Count') ax.set_xlabel('Surface Soil Moisture (m³/m³)') ax.set_ylabel('ET (W/m²)')

用hexbin而不是普通的scatter,是因为几万个点叠在一起会互相遮挡,根本看不出密度分布。hexbin可以把点密度用颜色显示出来,非常适合这类"变量间关系"的分析画图。

6.4 快速探索工具推荐:别什么图都从头写代码

在做正式图件之前,建议先用一些轻量工具快速浏览数据形态,比如直接用ncview

ncview clm_single_site.clm2.h0.2000-01.nc

这个工具可以在终端里快速翻页查看所有变量的空间分布,对排查异常值特别有用。我通常先用ncview浏览一遍全部变量,找到感兴趣的变量之后再写Python脚本做深度分析。如果数据量很大,还可以考虑用holoviewshvplot做交互式探索,但正式出图还是matplotlib和cartopy最稳。

6.5 出图前一定要检查的三个细节

出图工作中有几个细节,直接影响图片是否被期刊接收:

第一是色板选择。CLM输出的变量种类繁多,辐射通量和降水用YlGnBu这类渐变色比较合适,而温度场用RdBu_r更能体现冷暖对比。不要在一篇论文里把十张图的色板风格搞得完全不统一,视觉上像拼凑出来的。

第二是单位标注。CLM输出的通量单位多为W/m²,储量单位多为gC/m²或kg/m²,变量名的缩写又不直观,画图时必须明确标注。我审稿时经常看到变量符号对但单位错的论文,这种硬伤很影响可信度。

第三是坐标轴范围和刻度。如果你对比多个实验组,一定要统一coord范围,否则图之间的视觉差异会被夸大或缩小。这算是科研绘图的基本伦理问题,我这里提一句。

7. 根据我个人跑CLM的体会,最后给你几条实在的建议

跑了快三年CLM之后,回头看自己掉进去又爬出来的那些坑,有几条经验值得特别拎出来说。

  • 别一上来就追求全球高分辨率。先把单点跑到通,把强迫数据、domain、namelist这套逻辑吃透了,再扩展到区域或全球。全球0.25°模拟一跑就是一周起步,光调试就够折腾的你怀疑人生。
  • 每次改动前先保存一份原来的配置和输出。CLM对参数变化极其敏感,有时候只是改了地表反照率方案,蒸散发结果会变20%以上。没有对照实验,你根本讲不清楚结果变化是物理改进还是数值噪音。
  • 养成盯log文件的好习惯。CLM运行中的很多问题在log里其实已经给了明确提示,只是被埋在大量输出里。我每次跑完必做的第一件事是grep -i "error\|warning" *.log*,根据这两个词筛出需要关注的内容,能过滤掉90%的无效信息。
  • 可视化工具不在多,顺手最好。我分析时xarray+matplotlib足够应付95%的需求,剩下的5%用ncview浏览异常值。不需要为了好看把环境里装上一堆大屏可视化平台,那些东西在科研出图上用武之地有限。

CESM和CLM的学习曲线确实陡,但只要你把独立运行的这条链路完整走通一次——环境配置、数据准备、运行、结果处理、出图——后面的研究就只是在换变量、换区域、换场景了。希望这篇东西能帮你少走我当初走过的那些弯路。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询