This article was last updated on <span id="expire-date"></span> days ago, the information described in the article may be outdated.
老高让我学 WRF
,我就装 WRF
。装 WRf
的过程出现了很多问题,就开贴记录一下。
WRF 版本 V4.4.1
WRF 安装
clone 源码
根据 官网 的描述,先执行以下代码将源代码 clone 下来。
1 | git clone --recurse-submodules https://github.com/wrf-model/WRF |
安装依赖
1 | sudo pacman -S netcdf hdf5 netcdf-fortran |
gcc、gcc-fortran什么的都默认你已经装好了嗷,没有的话快装。
生成 configure.wrf
首先需要在环境变量中设置以下 NETCDF
、NETCDF_INC
和 NETCDF_LIB
1 | export NETCDF=/usr |
然后执行下面的命令运行 configure
1 | bash ./configure |
选择编译器的那里我选择的 34
,因为用到了 GCC
的编译器。
在输出一长串日志以后,configure.wrf
就生成成功了。
但是需要注意的是,需要修改几个地方,否则后面编译的时候会报错。
修改 CPP 编译器路径
定位到 configure.wrf
的第 162 行,这里的内容应该是
1 | CPP = /lib/cpp -P -nostdinc |
将其修改为
1 | CPP = cpp -P -nostdinc |
正常人谁特么会写死编译器路径?
去除调用 time 命令的设置
之所以要去掉,是因为编译的时候会出现 make
找不到 time
命令的错误。翻了一下 Github 的 issue,开发者说 time
命令无关紧要,可以去掉。那你特么调这个命令干啥。定位到 configure.wrf
的第 139 行,内容应该如下
1 | FC = time $(DM_FC) |
将其修改为
1 | FC = $(DM_FC) |
开始编译
做完上面的修改以后执行下面命令开始编译,然后等待编译程序报错🐶。(该报错的时候我才发现,留一份完整的日志是多么的重要)
1 | ./compile em_real > compile.log 2>&1 |
上面的命令会将所有的输出 (包括 make
报的错误) 全部重定向进文件,先看会文献然后等待程序运行完成。
解决报错
dfft1f_ 符号未定义
从后往前查找日志,定位到第一个出现问题的地方,出现如下错误
1 | /usr/bin/ld: libwrflib.a(module_polarfft.o): in function `__module_polarfft_MOD_polar_filter_fft_2d_ncar': |
看样子是 module_polarfft
这个代码出了问题。对吗?不对。通过谷歌发现,在 external/fftpack/fftpack5
下存在一个名叫 dfftf1.F
的文件,使用 nm
看一眼这个目录下编译出来的静态链接库 libfftpack.a
1 | nm -n libfftpack.a |
WRF,你tm在逗我吗?
在日志里向上搜寻编译出 libfftpack.a
的命令,结果发现只调用了 ar
来打包,没有编译过程。原来这些 .o 文件在 clone 下来的时候就出问题了。。。
于是我只好现场查询 Makefile 的用法,对 Makefile 进行了修改并自己手动编译。
首先现在 Makefile 开头 .SUFFIXES
的下一行加入以下内容
1 | CPP = cpp -P -nostdinc |
然后在最后添加以下部分
1 | build: |
最后在终端中运行以下命令重新把文件全部编译一遍
注意我使用的是 zsh,若你使用其他类型的 shell,请注意修改 make 那一行的字符串切片
1 | ls *.F | while read line |
在输出一长串编译日志以后,.o
文件都已经重新编译好了。然后在直接运行 make
命令打包出 libfftpack.a
。
1 | make |
最后再重新去运行一下下面的命令编译出 wrf.exe
,可以发现 dfft1f_
符号未定义的错误已经消失了,但是还有其他符号未定义问题需要解决。
1 | mpif90 -o wrf.exe -O2 -ftree-vectorize -funroll-loops -w -ffree-form -ffree-line-length-none -fconvert=big-endian -frecord-marker=4 -fallow-argument-mismatch -fallow-invalid-boz wrf.o ../main/module_wrf_top.o libwrflib.a /home/syize/Apps/WRF/external/fftpack/fftpack5/libfftpack.a /home/syize/Apps/WRF/external/io_grib1/libio_grib1.a /home/syize/Apps/WRF/external/io_grib_share/libio_grib_share.a /home/syize/Apps/WRF/external/io_int/libwrfio_int.a -L/home/syize/Apps/WRF/external/esmf_time_f90 -lesmf_time /home/syize/Apps/WRF/external/RSL_LITE/librsl_lite.a /home/syize/Apps/WRF/frame/module_internal_header_util.o /home/syize/Apps/WRF/frame/pack_utils.o -L/home/syize/Apps/WRF/external/io_netcdf -lwrfio_nf -L/usr/lib -lnetcdff -lnetcdf |
上面的 /home/syize/Apps/
需要替换成你自己的 WRF 路径,若你觉得麻烦,直接在 compile.log 里面找就好了
get_dims_ 符号未定义
使用 grep
命令在 WRF 目录递归搜索字符串 get_dims_,将问题定位到 external/io_grib_share/io_grib_share.o
这个文件,再用 nm
命令看一下 libio_grib_share.a
,第一行就发现了猫腻
好吧,看来又要我手动修了。首先执行以下命令清理 .o
文件
1 | make clean |
然后执行以下命令重新进行编译打包
1 | make -j 2 CC="gcc" CFLAGS="-w -O3 -c -DRPC_TYPES=2 -DDM_PARALLEL -DLANDREAD_STUB=1 -DMAX_HISTORY=25 -DNMM_CORE=0" RM="rm -f" RANLIB="ranlib" CPP="cpp -P -nostdinc" FC="gfortran -I. -w -ffree-form -ffree-line-length-none -fconvert=big-endian -frecord-marker=4 -fallow-argument-mismatch -fallow-invalid-boz " TRADFLAG="-traditional-cpp -DUSE_NETCDF4_FEATURES -DWRFIO_NCD_LARGE_FILE_SUPPORT" AR="ar" ARFLAGS="ru" archive |
编译好以后看一眼 libio_grib_share.a
,发现修好了
再执行命令编译 wrf.exe
,终于修好了。但是,你先别急,还有其他错误呢🐶。
module_wps_io_arw.mod 文件找不到
还是那句话,你要是觉得敲命令麻烦,就自己去日志文件里面复制就好了
还是使用递归搜索,定位带文件 dyn_em/module_wps_io_arw.o
,检查一遍发现由 F 文件生成的 f90 文件的大小竟然是0。。。
分别使用下面的命令重新生成 module_wps_io_arw.o
。
1 | sed -e "s/^\!.*'.*//" -e "s/^ *\!.*'.*//" module_wps_io_arw.F > module_wps_io_arw.G |
注意修改命令中的路径为你自己的 WRF 路径
然后在执行命令重新编译 real_em.o
文件
1 | mpif90 -o real_em.o -c -O2 -ftree-vectorize -funroll-loops -w -ffree-form -ffree-line-length-none -fconvert=big-endian -frecord-marker=4 -fallow-argument-mismatch -fallow-invalid-boz -I../dyn_em -I/home/syize/Apps/WRF/external/esmf_time_f90 -I/home/syize/Apps/WRF/main -I/home/syize/Apps/WRF/external/io_netcdf -I/home/syize/Apps/WRF/external/io_int -I/home/syize/Apps/WRF/frame -I/home/syize/Apps/WRF/share -I/home/syize/Apps/WRF/phys -I/home/syize/Apps/WRF/wrftladj -I/home/syize/Apps/WRF/chem -I/home/syize/Apps/WRF/inc -I/usr/include real_em.f90 |
到此为止,基本所有额外的修复工作均已完成,剩下的工作就是复制编译失败的命令重新执行以下就可以了,为了保持博客简洁我就不再赘述了。
贴一张完成的图片,根据日志需要编译出的4个可执行文件均已成功编译出。
WPS 安装
clone 源码
执行以下命令 clone 最新的 WPS 源码
1 | git clone https://github.com/wrf-model/WPS |
然后 configure
,compile
,记得用以下命令将日志输出到文件,方便后续查找报错修复
1 | ./compile em_real > compile.log 2>&1 |
解决报错
compile
命令执行完成以后看了一眼日志,果然报错了,开始修复。
nf_strerror_ 符号未定义
这里其实是解决所有以 nf
开头的符号未定义的解决方案,经查询这些符号均是在 libnetcdff.so
动态链接库中的,但是查看最后连接时候的命令可以看到只链接了 libnetcdf.so
这个库,解决方法也很简单,复制编译日志中的链接指令,在最后加上 -lnetcdff
即可。
jpc_decode 符号未定义
出现这个错误是因为新版本的 jasper
中已经弃用了该函数,官方也推荐使用 jas_image_decode
来代替。解决这个错误的方法只好是下载一个旧版本的 jasper
源码自己编译一个动态链接库。这是 2.0.14 版本的下载地址
解压压缩包,并进入文件夹
1 | tar -zxvf jasper-2.0.14.tar.gz |
可以发现 CMakeLists.txt
都给你写好了,直接 cmake
开始编译
1 | cmake .. |
编译到最后会报错,但是没关系,libjasper.so.4.0.0
已经给你编译好了,直接复制该链接库到 WPS 的路径下的 ungrib/src
文件夹中,然后复制日志中的指令开始编译就好了。
最后我们还需要复制该链接库到系统的 library 路径中,并命名为 libjasper.so.4
,这样你运行 ungrid.exe
时才不会报下面的错误
1 | ./ungrib.exe: error while loading shared libraries: libjasper.so.4: cannot open shared object file: No such file or directory |
__netcdf_MOD_nf90_get_var_4d_fourbytereal 符号未定义
这个也是由于没有链接 libnetcdff.so
造成的,解决方法和 nf_strerror_
一样。
Author: Syize
Permalink: https://blog.syize.cn/2022/10/25/install-wrf-on-arch/
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Syizeのblog!
Comments