作者: 夏子涵
邮箱: xiazh@mail.ustc.edu.cn
google scholar:https://scholar.google.com/citations?user=pxi8qSsAAAAJ&hl=zh-CN
随着人工智能技术在气象和环境科学领域的深入应用,将预训练好的 AI 模型集成到现有的大气数值模式中,已成为提升模拟效率与精度的前沿方向。本文档旨在提供一个清晰、简单、可行的技术指南(已在本人两篇论文中应用并进行介绍,放在文末啦,恳请引用,哈哈),详细阐述如何在大气数值模式中(本教程以WRF-Chem (Weather Research and Forecasting with Chemistry) 为例)耦合一个基于 PyTorch 训练的 AI 方案。
由于 WRF-Chem 主要使用 Fortran 语言编写,而主流 AI 框架(如 PyTorch)则主要提供 Python 和 C++ 接口,我们无法直接在 Fortran 中调用 AI 模型。前人有过多种尝试,如将AI方案用fortran代码重写,或者使用第三方库CFFI,又或者是使用后面开发的FotranToKeras 等其他方法,但大多存在重写代码量大、计算性能不高、数据传输困难等问题。幸运的是,Fortran 具备与 C++ 良好的互操作性。同时,PyTorch 提供了其 C++ 原生库 LibTorch,专门用于在非 Python 环境中部署模型。
因此,就目前而言,我认为最佳实践路径是:
Fortran (WRF-Chem) → C++ Wrapper → LibTorch → PyTorch AI 模型
本教程将以 AIPC (光化学 AI 方案) 为例,一步步展示完整的耦合流程。此方法同样适用于 AIMACI (气溶胶化学 AI 方案) 等其他 AI 模型的集成。
此步骤的核心是创建一个 C++ 函数,该函数作为 Fortran 和 LibTorch 之间的桥梁。它负责接收来自 Fortran 的输入数据(通常是多维数组),将其转换为 LibTorch 所需的张量 (Tensor) 格式,调用预训练的 AI 模型进行推理计算,最后将计算结果传回给 Fortran。
#include <torch/script.h> // LibTorch 核心头文件
#include <torch/torch.h>
#include <iostream>
#include <memory>
#include <unistd.h>
// 使用 extern "C" 确保 C++ 编译器以 C 语言标准编译函数名,以便 Fortran 能正确识别和链接
extern"C"void saprc99_DNN(double *c_cin, double *c_cout, int batch_size);
void saprc99_DNN(double *c_cin, double *c_cout, int batch_size) {
// 1. 定义并加载序列化的 PyTorch 模型
// 模型文件 "AIPC_MHSA.pt" 需要与最终的可执行文件位于同一目录
torch::jit::script::Module module;
try {
module = torch::jit::load("AIPC_MHSA.pt");
} catch (const c10::Error& e) {
std::cerr << "error loading the model\n";
return;
}
// 2. 将 Fortran 传入的 double* 数组转换为 LibTorch 张量
// a. 使用 torch::from_blob 将原始数据指针封装为张量,不发生内存拷贝
auto cinput = torch::from_blob(c_cin, {batch_size, 91}, torch::kDouble).unsqueeze(1); // 形状: [batch_size, channel=1, 91 features]
// b. 根据模型要求,将数据类型转换为 Float
auto cinput_ = cinput.to(torch::kFloat);
// 3. 准备模型输入
std::vector<torch::jit::IValue> inputs;
inputs.push_back(cinput_);
// 4. 执行模型前向推理
at::Tensor output = module.forward(inputs).toTensor();
// 5. 将输出张量转换回 Fortran 需要的 double 类型
output = output.to(torch::kDouble);
auto res = output.accessor<double, 2>(); // 使用 accessor 获取数据访问器
// 6. 将结果从输出张量内存中拷贝回 Fortran 的数组指针
std::memcpy(c_cout, res.data(), 74 * batch_size * sizeof(double));
}
为了让 WRF 的编译系统能够识别和链接我们编写的 C++ 代码,需要将其打包成一个静态库(.a 文件)(也可以是动态库.so文件)。我们推荐使用 CMake 来管理这一过程,因为它能方便地找到并链接像 LibTorch 这样的外部依赖。
以下是一个示例 CMakeLists.txt 文件:
# 指定最低 CMake 版本要求
cmake_minimum_required(VERSION 3.0 FATAL_ERROR)
# 定义项目名称和支持的语言
project(Demo LANGUAGES C CXX Fortran)
# 查找已安装的 LibTorch 包,这是关键依赖
find_package(Torch REQUIRED)
# 从源文件 saprc99DNN.cpp 创建一个名为 saprc99DNN 的静态库
add_library(saprc99DNN STATIC saprc99DNN.cpp)
# 将 LibTorch 的库链接到我们的静态库上
target_link_libraries(saprc99DNN "${TORCH_LIBRARIES}")
# 设置 C++ 标准为 C++14 或更高,以兼容 LibTorch
set_property(TARGET saprc99DNN PROPERTY CXX_STANDARD 14)
编译完成后,将生成的静态库文件 libsaprc99DNN.a 移动到 WRF-Chem 项目的 external/ 文件夹(可自由指定)下。
为了让 Fortran 代码能够安全、正确地调用 C++ 函数,我们需要定义一个接口块 (Interface Block)。这个接口精确地描述了外部 C++ 函数的名称、参数类型和传递方式。
创建一个新的 Fortran 模块文件 module_saprc99_DNN.f90:
MODULE module_saprc99_DNN
! 定义 Fortran 与 C 语言之间数据类型的对应关系
USE, INTRINSIC :: iso_c_binding
IMPLICITNONE
! 接口定义
INTERFACE
! 定义一个与 C++ 函数匹配的 Fortran 子程序
! bind(c, name="saprc99_DNN") 是关键,它告诉 Fortran 编译器:
! 1. 使用 C 语言的调用约定 (bind(c))
! 2. 这个子程序在链接时对应的外部符号名为 "saprc99_DNN"
SUBROUTINE saprc99_DNN(c_cin, c_cout, batch_size) BIND(C, name='saprc99_DNN')
USEiso_c_binding
IMPLICITNONE
! 参数类型必须与 C 语言类型精确对应
REAL(c_double), INTENT(IN) :: c_cin(91, *)
REAL(c_double), INTENT(OUT) :: c_cout(74, *)
INTEGER(c_int), VALUE :: batch_size
ENDSUBROUTINE saprc99_DNN
ENDINTERFACE
ENDMODULE module_saprc99_DNN
现在,我们可以在 WRF-Chem 的核心逻辑(如 chem_driver.F)中调用 AI 方案了。
修改 chem_driver.F:
在 chem_driver.F 的适当位置,通常是化学计算部分,添加对 AI 方案的调用。首先需要 USE 我们刚刚创建的模块,然后像调用普通 Fortran 子程序一样调用 saprc99_DNN。
! 在程序的声明部分添加 USE 语句
USE module_saprc99_DNN
! ... 省略其他代码 ...
! 在需要进行化学计算的地方
CALL saprc99_DNN(input_array, output_array, grid_cell_count)
! ... 省略其他代码 ...
修改 Makefile:
将 module_saprc99_DNN.f90 添加到 WRF 的编译列表中,确保它能被正确编译并链接到最终的可执行文件中。
为了让 WRF 在编译时能够找到我们的静态库 libsaprc99DNN.a 以及它所依赖的 LibTorch,我们需要修改 configure.wrf 文件。
在 configure.wrf 文件中找到 LIB_EXTERNAL 变量的定义部分,添加以下内容:
# 在 LIB_EXTERNAL 变量中添加 AI 静态库和 LibTorch 的路径
# L$ 指示链接器搜索库的路径
# l 指示链接具体的库名
LIB_EXTERNAL = \
-L$(WRF_SRC_ROOT_DIR)/external/io_netcdf -lwrfio_nf ... \
# 添加我们自己的静态库
$(WRF_SRC_ROOT_DIR)/external/libsaprc99DNN.a \
# 添加 LibTorch 及其依赖的路径和库
/opt/rh/devtoolset-11/root/lib/gcc/x86_64-redhat-linux/11/libstdc++.a \
-L/home/zhxia/env/libtorch_cpu/lib -ltorch_cpu -lc10 ...
注意: 上述路径(如 /home/zhxia/env/libtorch_cpu/lib)需要根据您系统中 LibTorch 的实际安装位置进行修改。
最后,在运行 WRF-Chem 之前,请确保已将 AI 模型文件(例如 AIPC_MHSA.pt)链接或复制到 WRF 的运行目录 (wrf/run/ 或 wrf/test/em_real/) 下。
$ ln -s /path/to/your/model/AIPC_MHSA.pt wrf/run/AIPC_MHSA.pt
这样,当 C++ 代码执行 torch::jit::load() 时,才能正确找到并加载模型参数,从而完成计算。
至此,整个 AI 方案的耦合工作就完成了。通过以上步骤,您可以成功地将一个外部的、基于 PyTorch 的 AI 模型集成到 WRF-Chem 中,利用 AI 的强大能力来加速或改进模式中的物理或化学过程。
其他数值模式的耦合思路都是一样的,可能只是具体调用代码不同,可参考本教程自行修改。
【1】Xia, Z., Zhao, C., Du, Q., Yang, Z., Zhang, M., & Qiao, L. (2025). Advancing sophisticated photochemistry simulation in atmospheric numerical models with Artificial Intelligence PhotoChemistry (AIPC) scheme using the feature‐mapping subspace self‐attention algorithm. Journal of Advances in Modeling Earth Systems, 17(8), e2024MS004646.
【2】Xia, Z., Zhao, C., Yang, Z., Du, Q., Feng, J., Jin, C., ... & An, H. (2025). Toward a learnable Artificial Intelligence Model for Aerosol Chemistry and Interactions (AIMACI) based on the Multi-Head Self-Attention algorithm. Atmospheric Chemistry and Physics, 25(12), 6197-6218.
【1】https://zenodo.org/records/15614698
【2】https://zenodo.org/records/15702248
END
声明:欢迎转载、转发。气象学家公众号转载信息旨在传播交流,其内容由作者负责,不代表本号观点。文中部分图片来源于网络,如涉及内容、版权和其他问题,请联系小编处理。