gcmfaces:全球海洋模式立方球网格后处理实战指南
2026/9/14 1:34:02 网站建设 项目流程

简介:gcmfaces是一套面向Matlab与Octave的开源工具箱,专为处理全球气候模型(GCM)的海洋环流数据而设计,主要服务于海洋科学和气候研究领域的科研人员、研究生及工程师。其核心在于对face分块网格结构的支持,能高效读取NetCDF输出、完成切片与重采样,并提供流线图、等值面等二维/三维可视化,以及涡度、散度、温盐梯度等物理量计算功能,借助并行方式显著提升大数据集处理效率。该工具箱支持用户自定义扩展函数,便于根据实际研究流程定制分析模块。资源包内含317个文件,以296个m函数脚本为主体,辅以rst文档、PDF使用指南、yaml配置、bin数据、bib参考文献及readme说明,整体压缩后仅3MB,部署轻便。已有118人学习下载,适合需要快速上手gcmfaces、开展GCM输出数据分析或进行模型诊断的研究者,也适合希望扩展自定义处理函数、深入海洋环流模拟流程的进阶用户。

1. gcmfaces 是给谁用的:绕不开的全球环流后处理工具箱

全球海洋模式的输出常常不是一张“平面地图”。MITgcm 这类全球环流模式采用立方球网格把地球切成六块,输出数据按面存放,普通 lat/lon 后处理工具一开文件就抓瞎。gcmfaces 正是 ECCO 项目在长期全球模拟中打磨出来的 Matlab/Octave 工具箱,它把六个面的网格结构、掩膜和插值统一抽象成一套接口,让读数据、体积积分、输运诊断和画图都围绕同一套网格对象进行。它主要面向需要处理全球海洋模型输出、又不想自己从零搭一套后处理链路的海洋科学与气候工程人员,下文按“网格结构—环境搭建—核心操作—诊断案例—排错”逐层展开。

2. gcmfaces 的网格骨架:立方球、faces 与掩膜变量

2.1 为什么全球模式要切六个面而不是用经纬网格

传统经纬网格沿经线把全球网格化,在极点区域网格收敛成奇点:经度方向的格距趋近于零,模式不得不做滤波或干脆放弃极区计算。立方球网格先把地球表面投影到内切立方体的六个平面上,再在每个面上独立生成准均匀的二维网格。这种做法的直接收益是两个方向的分辨率不随位置剧烈变化,模式时间步长可以统一;代价是数据存储、边界对接和诊断工具全部要重新适配。

下表列出的两类网格差异,是理解 gcmfaces 存在必要性的关键:

特性经纬网格立方球网格
极点处理需要特殊汇合与滤波极点落在面内,无奇点
空间分辨率高纬过度加密六面上近似均匀
数据布局单个二维场6 个面单独存储
常用后处理直接切片、绘图需要 faces 感知的工具

因此,做全球环流诊断时最先遇到的往往不是物理问题而是数据结构问题:一个温度场在 gcmfaces 里不是一个普通 2D/3D 数组,而是包含 6 个面的 cell 结构。这个差异决定了后面所有脚本怎么写。

2.2 mygrid:gcmfaces 的全局网格对象

gcmfaces 在初始化时把一套叫 mygrid 的全局变量装进工作区,里面保存每个面的中心坐标、角点坐标、湿度掩膜和分层几何。习惯上所有 gcmfaces 脚本开头都有这两行:

global mygrid gcmfaces_global;

gcmfaces_global 负责读取网格文件并组装 mygrid,核心字段包括:

  • XC、YC:每个网格面中心点的经度和纬度
  • XG、YG:网格角点坐标,用于计算格距和面积
  • hFacC:垂直方向上每一层格子的水体填充比例
  • maskC、maskW、maskS:干湿掩膜,分别对应格心、东西界面、南北界面

下面这段代码可以快速确认初始化结果:

global mygrid gcmfaces_global; fprintf('面数: %d\n', mygrid.nFaces); for f = 1:mygrid.nFaces fprintf('面 %d: %d x %d\n', f, size(mygrid.XC{f}, 1), size(mygrid.XC{f}, 2)); end

这段代码在 Matlab 和 Octave 下都能直接运行。nFaces 一般是 6,但部分网格会把一个面继续细分,此时面数多于 6。脚本如果写死 6,换一套网格就会出错,通过 mygrid.nFaces 循环遍历更稳妥。

2.3 掩膜不是“可有可无”的附属数组

maskC 中 1 表示海洋,0 表示陆地。gcmfaces 的很多内部算子会自动应用掩膜,但自己写循环时很容易把陆地点也统计进去。比如算全球平均温度,正确做法是按水体体积加权:

global mygrid % gcmfaces_vol 返回每个网格格子的实际体积 vol = gcmfaces_vol(mygrid.maskC); total_vol = 0; s = 0; for f = 1:mygrid.nFaces total_vol = total_vol + sum(sum(vol{f}, 1), 2); s = s + sum(sum(THETA{f} .* vol{f}, 1), 2); end mean_theta = s / total_vol;

这里的 THETA 是三维 faces 结构,.乘是按面逐点相乘。如果直接把 THETA 在全部网格点上做算术平均,结果会被高纬密集格子主导,物理意义完全不对。这是新手写 gcmfaces 脚本最常见的错误来源。

注意:不同版本里体积计算函数名可能不同。如果当前版本没有 gcmfaces_vol,用各面面积乘以层厚自己构造体积场也是一样的。

3. 在 Matlab 与 Octave 里把 gcmfaces 装起来并跑通

3.1 下载、解压与首次运行前的路径设置

gcmfaces 以源码包形式发布,常见做法是把压缩包解压到专门目录,比如~/tools/gcmfaces,然后在脚本或 startup.m 里把整个目录加入搜索路径:

addpath(genpath('/home/user/tools/gcmfaces'));

genpath 会把所有子目录加进来,避免手动逐层添加。Windows 用户注意路径中不要出现中文目录,部分内部函数对非 ASCII 路径处理并不可靠。安装 gcmfaces 不需要安装器,路径设置不生效的典型特征是调用 gcmfaces_global 时报Undefined function。这也和 matlab 安装教程里常被忽略的一步类似:工具箱本身没问题,是搜索路径没配对。

3.2 初始化网格与冒烟测试

路径设好后的第一步不是去读海量数据,而是先确认网格能正常加载。最小冒烟测试可以这样写:

global mygrid gcmfaces_global; assert(logical(exist('mygrid', 'var'))); assert(mygrid.nFaces == 6); % 把掩膜传入体积函数,验证非零总体积 vol = gcmfaces_vol(mygrid.maskC); total_vol = 0; for f = 1:mygrid.nFaces total_vol = total_vol + sum(sum(vol{f}, 1), 2); end fprintf('网格总体积: %g 10^6 km^3\n', total_vol / 1e15);

如果输出结果在 1330 附近(单位是 (10^6) km³),说明网格装载正常。如果拿到 0 或 NaN,多半是 maskC 或 hFacC 读取失败,后面所有计算都不可信。内存有限时可以只跑工具箱自带 test 脚本,它通常只加载小网格,跑通后再加载真实网格。

3.3 Octave 兼容性:能跑的部分与需要绕开的部分

Octave 在纯计算路径上与 gcmfaces 兼容得相当好,但有四个边界需要提前知道。

  1. 绘图:gcmfaces 自带的 face 拼接绘图函数在 Octave 下可能遇到句柄属性不识别的问题,稳定做法是先把数据插值到经纬度网格,再用 pcolor 自己画。
  2. MEX 文件:如果网格生成或插值依赖编译好的 MEX 文件,Octave 需要自行编译,Windows 上通常要装 MinGW-w64。
  3. 字符串处理:老版本 Octave 对某些新式字符串函数支持滞后,遇到convertCharsToStrings报错时把相关行改为 char 处理。
  4. 路径缓存:多次 addpath 后 Octave 不刷新函数缓存,改动 gcmfaces 源码后要执行rehashclear functions

检查环境是否就绪:

disp(version); which gcmfaces_global;

如果 which 返回路径,说明搜索路径已生效;如果返回 not found,说明 addpath 没执行或拼错了目录。

3.4 卸载与残留路径的清理

很多用户是在旧版 Matlab 里装了 gcmfaces,之后升级或换电脑,于是出现启动时找不到 gcmfaces_global 的报错。这和常见的“win工具箱怎么卸载”情况类似:问题不在工具箱本身,而是 pathdef.m 中残留旧路径。清理方式:

% 查看当前路径 path % 删除包含 gcmfaces 的项 rmpath(genpath('/old/path/to/gcmfaces')); savepath

如果 savepath 因权限失败,Windows 上可以找到matlabroot/toolbox/local/pathdef.m手动删除相关行。Octave 没有 pathdef.m,检查~/.octaverc是否有旧 addpath 残留即可。

4. 上手 gcmfaces:读数据、插值与诊断绘图

4.1 读取 MITgcm 原生输出

读取模型原生输出的常用入口是 gcmfaces_load,它面向 MITgcm 二进制输出(state 系列、pickup 系列)直接把数据装配成 faces 结构:

fld = gcmfaces_load({'THETA', 'V'}, ... 'dir', '/data/ecco/run/', ... 'nDims', 3, ... 'tiles', mygrid.tiles);

参数含义:

  • 第一个参数是要加载的变量名列表,THETA 是位温,V 是经向速度
  • dir指向数据目录,目录下应有按面拆分的文件或能被内部逻辑识别的命名格式
  • nDims表示变量维数,3 代表三维场,2 代表二维场
  • tiles取自 mygrid.tiles,告诉装配逻辑每个文件对应哪个面

如果模型输出是 NetCDF 格式,需要先用标准工具把数据拆成六面结构,再逐一塞进 faces cell。社区里更常见的做法是保持二进制并配合 gcmfaces_load,因为它在面边界上的处理最完善。加载后记得确认 fld 的 faces 数与 mygrid.nFaces 一致,否则后续逐面运算会越界。

4.2 插值到经纬度网格:全球图的入口

诊断输出和画图都倾向于地理网格。gcmfaces_interp 是 faces 到普通网格插值的核心函数:

lon = -180:1:180; lat = -90:1:90; [XT, YT] = meshgrid(lon, lat); THETA_ll = gcmfaces_interp(THETA, XT, YT); V_ll = gcmfaces_interp(V, XT, YT);

这段代码在指定经纬度集合上做空间插值,返回普通 2D/3D 数组。使用要点有三个:输出网格越细,插值越慢,1/4 度数据插到 0.5 度网格就可能吃掉几 GB 内存,先上 2 度粗网格验证流程再加密;矢量场插值要注意面边界上方向的一致性,某些版本提供vectorInterp选项,标量场不需要;插值后近海岸会有空洞,绘图时把陆地掩膜叠加在地理网格上即可。

4.3 面平均与全局积分

有了 faces 对象,体积积分的写法是统一的。例如计算 100 米以浅的平均温度:

global mygrid % 构造深度掩膜:只保留前 10 层,其余层设 0 depth_fac = mygrid.hFacC; for f = 1:mygrid.nFaces tmp = depth_fac{f}; depth_fac{f} = zeros(size(tmp)); depth_fac{f}(:, :, 1:min(10, size(tmp, 3))) = 1; end vol_100m = gcmfaces_vol(depth_fac); num = 0; den = 0; for f = 1:mygrid.nFaces num = num + sum(sum(sum(THETA{f} .* vol_100m{f}, 1), 2), 3); den = den + sum(sum(sum(vol_100m{f}, 1), 2), 3); end mean_100m = num / den;

这段代码的关键在于深度掩膜也要按面拆开处理,而不是直接在整个三维数组上切层。层数 10 是示意值,实际应依据网格 param 文件中的分层厚度换算。

4.4 画一张不拼接错位的全球图

快速预览时可以直接调 gcmfaces_plot 类接口,不过它在不同 Octave 版本上表现不稳定。稳妥路径是先插值到经纬度,再用 Matlab 自带绘图:

pcolor(lon, lat, squeeze(THETA_ll(:, :, 1))); shading interp; hold on; landmask = ~isnan(squeeze(THETA_ll(:, :, 1))); contour(lon, lat, landmask, [0.5 0.5], 'k'); colorbar; xlabel('Longitude'); ylabel('Latitude');

展示时注意首尾经度拼接:插值网格的 lon 从 -180 到 180 时,180°E 和 -180°W 交界处会有白缝,绘图前把 lon 扩展一位并复制首列到末列即可。colormap 用 matlab 图像处理相关工具箱自带的 parula 或 jet 都够用,不需要额外装包。

5. 用 gcmfaces 计算一条全球经向翻转环流(MOC)曲线

5.1 MOC 的思路与数据准备

MOC(Meridional Overturning Circulation)描述全球海洋在经向剖面上的翻转结构。大西洋 MOC 在 26°N 附近实测约 17 Sv,这是模型诊断里最常拿来对标的量级。从 gcmfaces 计算一条全球 MOC 曲线,可以同时验证插值路径和质量守恒:理论上一整圈经向输运积分应为 0,如果明显偏了,说明 faces 拼接时出了问题。

数据准备建议用时间平均场,而不是单一时次的快照。先在 faces 结构上做时间平均,再插值到经纬度网格,避免“先插值再平均”累积插值误差。

% 已有 V 随时间变化的 faces 结构,先做时间平均 V_mean = V; % 如果 V 是 time×face 结构,先 squeeze 掉时间维 V_ll = gcmfaces_interp(V_mean, XT, YT); fprintf('插值后尺寸: %d x %d x %d\n', size(V_ll));

这里的 XT、YT 沿用 4.2 节定义的 1 度网格。检查 V_ll 的第三维深度层数与模型输出一致,不一致时要回到 gcmfaces_load 的 nDims 设置排查。

5.2 在纬向做累积

核心循环按层对经度加权求和,再按纬度从南向北累积:

nz = size(V_ll, 3); dx = 111e3 * cosd(lat); % 经度方向实际长度,随纬度变化 psi = zeros(length(lat), nz); for k = 1:nz v_k = squeeze(V_ll(:, :, k)); % nlat x nlon % 经度方向积分,dx 随纬度变化 trans = sum(v_k .* repmat(dx(:), 1, length(lon)), 2); % 从南到北累积 psi(:, k) = cumsum(trans); end dz = 10; % 示意:均匀层厚 10m,需按实际网格替换 psi = psi * dz * 1e-6; % 转成 Sv

这里必须用 repmat 把 ddx 扩展到经度维,因为 dx 是每个月度对应的实际距离。实际模型的层厚不均匀,要把 dz 替换为每层实际厚度的列向量后再乘。这段代码的效率不是最优,但胜在逻辑完全透明,逐面排查错误时很容易定位。

5.3 验证:量级与转向特征

画成等值线图后重点看两个特征:

figure; contourf(lat, 1:nz, psi'); set(gca, 'YDir', 'reverse'); colorbar; xlabel('Latitude'); ylabel('Depth level'); title('Global MOC streamfunction (Sv)');

一个合理的全球 MOC 曲线在大西洋深水区应有约 15 到 20 Sv 的向北输运峰,40°S 附近有明显的循环反转。如果最大值只有 1 Sv 或图像完全是高频噪声,优先检查两处:V 单位是否已是 m/s;插值网格经度步长是否过细,把经度步长加到 2 度再试。如果全纬度积分不为零,则说明质量守恒被破坏,回头检查 5.1 步骤里的面边界处理。

6. gcmfaces 排错技巧与 Octave 环境边界

6.1 报错信息的含义

报错现象常见原因排查动作
Undefined function gcmfaces_global路径未加入或缓存未刷新addpath 后执行 rehash
Index exceeds array bounds用第 1 个面的尺寸去索引其他面用 mygrid.nFaces 循环遍历
NaN 出现在插值结果目标点落在陆地掩膜内对插值结果再做最近邻填充
Out of memory高分辨率数据一次装载多个变量逐个变量读、先转 single、分块插值

6.2 降低内存占用的小技巧

内存是 gcmfaces 应用中最常见的瓶颈。一个 1/4 度的全球三维场在 double 精度下接近 2 GB,几个变量同时装载就会吃光普通工作站。我的习惯是数据尽早转 single,gcmfaces 的大多数运算能保持 single 精度,混用时注意显式转换;细网格插值按经纬度范围分块,每块调用一次 gcmfaces_interp 再拼接;时间序列循环处理时用 clear 显式删除不再需要的 faces 对象,不要等到 out of memory 发生。

6.3 工具箱卸载与长期维护

多台机器同步环境时,把 gcmfaces 放在统一路径并维护一个 startup.m 能节省大量“换机器就报错”的时间。Windows 用户卸载旧版 Matlab 后常遇到残留路径问题,原因和 win工具箱卸载残留一模一样:pathdef.m 里记录的绝对路径已不存在,启动时仍然逐一扫描。验证命令只有一行:

which gcmfaces_global

返回 not found 说明路径没生效,返回一个已不存在的路径说明有残留,用 rmpath 清掉即可。Octave 用户还需要注意以包形式安装时执行一次pkg rebuild,让函数缓存与改动后的源码保持一致。

本文还有配套的精品资源,点击获取

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

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

立即咨询