sokort项目停止维护

目录

cemc-oper/sokort 项目封装 NWP 业务系统绘图脚本并提供新的调用接口。

在近期工作中,笔者发现 sokort 项目存在一系列问题,计划停止 sokort 项目后续开发,仅进行简单维护。 笔者将择机推动 NWP 业务系统绘图脚本切换到 Python,并在业务更新时逐步使用绘图工具库原生接口代替 sokort 接口。

本文简要介绍 sokort 项目的使用方法和应用情况,分析项目存在的问题,并尝试提供一种解决方案。

声明:本文仅代表笔者个人观点,文章中引用的相关材料仅用于探讨和交流,不能作为最终事实依据。 关于 NWP 业务系统的相关信息,请以官方发布的信息及经过同行评议的论文为准。 如有任何疑问,建议查阅权威资料或咨询相关领域的专家。

简介

sokort 项目最早是为了解决业务系统 NCL 绘图脚本不容易使用的问题,将使用 NCL 绘图工具需要的生成绘图参数配置文件、设置环境变量、调用具体的绘图脚本等步骤隐藏到工具库内部,对外提供所有图片一致的命令行和 Python API 接口,应用在气象超算的实时业务系统和 CMADaaS 加工流水线上部署的绘图算法。

命令行接口支持生成图片文件

python3 -m sokort draw \
  --config ${CONFIG_FILE_PATH} \
  --system grapes_gfs_gmf \
  --plot-type cn.cape \
  --start-time 2025071000 \
  --forecast-time 000h \
  --data-dir ${DATA_DIR} \
  --work-dir .

Python 接口 draw_plot 用于生成图片文件

from sokort import draw_plot  

draw_plot(
    system="cma_gfs_gmf",
    plot_type="cn.t_2m",
    start_time = "2025-07-09 00:00:00",
    forecast_time = "144h",
    verbose=2,
)

Python 接口 show_plot 支持在 Jupyter Notebook 中显示图片

from sokort import show_plot  

show_plot(
    system="cma_gfs_gmf",
    plot_type="cn.t_2m",
    start_time = "2025-07-09 00:00:00",
    forecast_time = "144h",
    verbose=2,
)

应用情况

sokort 项目已应用在 NWP 业务系统中,包括:

  • 全球预报系统
  • 区域预报系统
  • 区域台风预报系统

一个典型的 ecFlow 任务脚本如下所示:

#==================================
# 1. config sokort, share by tasks
#==================================
SOKORT_PROJECT_BASE_DIR=${COMPONENT_PROJECT_BASE}/lib/python/sokort/current
SOKORT_CONFIG_PATH=${COMPONENT_PROJECT_BASE}/config/sokort/config.yaml
export PYTHONPATH=${SOKORT_PROJECT_BASE_DIR}/src:${PYTHONPATH}

SYSTEM_NAME=grapes_gfs_gmf

#================================
# 2. set plot type
#================================
plot_type=cn.k
SOKORT_PLOT_TYPE=$(echo "${plot_type}" | awk '{print tolower($0)}')

#==================================
# 3. run plot CLI, shared by tasks
#==================================
start_time=${START_TIME}
forecast_hour=000

cd ${run_dir}/${forecast_hour}
rm -rf *

python3 -m sokort draw \
  --config ${SOKORT_CONFIG_PATH} \
  --system ${SYSTEM_NAME} \
  --plot-type ${SOKORT_PLOT_TYPE} \
  --start-time ${start_time} \
  --forecast-time ${forecast_hour}h \
  --data-dir ${DATA_DIR} \
  --work-dir "."

包括三个部分:

  • sokort 工具库设置
  • 绘图任务参数设置
  • 调用命令行接口

对于不同的绘图任务,只需要修改图片种类 plot_task 变量,因此业务系统的绘图任务使用同一个模板,通过 ecFlow 变量机制为不同类型的绘图任务设置不同的 PLOT_TASK 值。

但上述系统的部分绘图脚本未集成到 sokort 项目中,在 Shell 脚本中直接调用绘图脚本,尤其是需要执行计算的绘图脚本。 主要是因为 sokort 项目的定位存在一些问题。

存在问题

不好用

sokort 项目的核心目标是拓宽业务绘图脚本的应用范围,但 sokort 的实现方式导致该项目目前只应用在批处理任务中。

使用 sokort 绘图需要编写配置文件(通用配置文件,单个系统配置文件)和环境加载文件(加载 NCL 环境,加载 Python 环境)。

配置文件示例如下所示:

通用配置文件

ncl:
  ncl_lib: "${COMPONENT_PROJECT_BASE}/script/ncllib/"
  geodiag_root: "/g1/u/op_post/OPER/UTIL/GEODIAG"
  load_env_script: "./load_env.sh"

python:
  load_env_script: "./load_python_env.sh"

general:
  run_base_dir: "${JOBDIR}/graphics"
  
config:
  systems_dir: "./systems"

单个系统配置文件

system:
  ncl_dir: "${COMPONENT_PROJECT_BASE}/script/"
  script_dir: "${COMPONENT_PROJECT_BASE}/script"

components:
  gams:
    ncl_dir: "${COMPONENT_PROJECT_BASE}/script/GAMS-A/"
  typhoon:
    ncl_dir: "${COMPONENT_PROJECT_BASE}/script/typhoon/"
    python_script_dir: "${COMPONENT_PROJECT_BASE}/script/typhoon/"

sokort 默认从固定目录或环境变量中获取通用配置文件路径,否则需要手动指定文件路径。 尽管在超算平台上可以使用同一套配置文件,但配置文件没有内置在项目中,需要用户手动拷贝、编写或在调用时指定配置文件路径,极大增加了项目的上手成本。

更致命的问题是 sokort 作为开源项目却没有包含绘图脚本,导致 sokort 项目仅能在内部有限场景中使用,失去了开源的意义,也没法进行推广,也就是非常难用。 封装一个项目提供额外接口是一种开发工具库的思路,但封装闭源项目提供开源接口则是一项没有意义的工作。

sokort 项目初衷是为了扩展绘图脚本的应用范围,最终却只用在业务系统中。

难维护

暂停 sokort 项目后续开发工作的直接原因是维护成本过高。

下面两图分别展示 sokort 调用 NCL 绘图脚本和 Python 绘图脚本的流程。

NCL脚本run_ncl.shNclPlotterSystemNclPlotterAeaFcPlotterNCL脚本run_ncl.shNclPlotterSystemNclPlotterAeaFcPlotterrun_plot()run_plot()_check_validity()_prepare_environment()准备运行环境_generate_environ()生成环境变量_run_process()执行返回结果完成_do_postprocess()图像后处理返回结果get_image_list()返回图像列表

图 NCL 脚本调用示意图,使用 Trae + DeepSeek 生成

subprocessBasePlotterPythonPlotterWmcGlobalPythonPlotterUsersubprocessBasePlotterPythonPlotterWmcGlobalPythonPlotterUsercreate_plotter()super().__init__()super().__init__()run_plot()_check_validity()_prepare_environment()_generate_environ()_run_process(envs)Popen(run_python.sh)执行结果_do_postprocess()

图 Python 脚本调用示意图,使用 Trae + DeepSeek 生成

sokort 调用脚本分为以下几个步骤:

  • 检查有效性 _check_validity()
  • 准备运行环境 _prepare_environment()
  • 生成环境变量 _generate_environ()
  • 运行脚本 _run_process()
  • 执行后处理 _do_postprocess()

对接每类绘图脚本都需要实现上述函数,针对每种图片也需要创建 BasePlotter 的子类,用于保存每种图片的名称和特定属性信息。

上述实现方式导致新增绘图任务成本过高,尤其最近新增的 Python 绘图脚本直接内置命令行接口,再封装一层只会增加维护成本。 再加上 sokort 项目缺少自动测试用例,对接新绘图脚本的开发成本过高,集成成本远大于收益。 省局区域 NCL 绘图脚本和 WMC 网站多要素绘图产品都没有集成到 sokort 项目中,而直接在 ecFlow 任务脚本中使用原生绘图接口 (命令行运行 ncl 脚本、Python 命令行)。

难维护也暴露了 sokort 项目的根本问题,即通用绘图工具本身应该提供命令行接口,封装绘图工具的项目没有长期维护价值。

方向错

sokort 封装一套更新不活跃的 NCL 绘图工具并提供额外的 API 接口,在本就维护困难的 NCL 脚本之上又增加了一套需要额外维护的脚本,无论从开发、应用还是调试来看,sokort 项目都带来了更高的维护成本,而没有达到预期的效果。

现在来看,sokort 项目完全走错了方向。 早在 sokort 项目开始之前,NCAR 已与 2019 年停止开发 NCL,全面转向 Python,并在后续开发了 geocat 系列工具集。 单位内部的 NCL 绘图工具也逐渐成为遗留软件,缺乏活跃更新。 sokort 项目从诞生之初就注定只是特定阶段的特定项目,没有长期维护价值。

sokort 项目实际上是尝试通过封装遗留代码来缓解技术债务,但从实践上看,从 2020 年至今 5 年左右时间,sokort 项目就已经因为维护成本飙升反而成为了新的技术债务。 封装方式对遗留软件进行“现代化”改造无法解决隐藏的技术债务,从长期维护成本看远不如对遗留软件进行重构。

因此笔者认为对绘图工具进行重构才是最合适的发展方向,也是单位目前正在进行的一项工作。 集合预报业务系统的绘图脚本已全面切换成 Python,确定性模式业务系统最近两年新增的绘图脚本也都使用 Python 实现。 笔者正在开发的 cedarkit 工具套件中也包含绘图工具库,不过暂未应用在业务系统中。

解决方案

cedarkit 工具套件的绘图工具提供一套绘制业务产品的工具集,提供 Python API 和命令行接口,支持绘制单张图片和批量绘图。

绘图工具库分为三个层次的工具包:

基础绘图工具 (cedarkit-maps)

  • 基础绘图功能函数

图片产品包 (cedar-graph)

  • 预定义业务图片
  • 包含数据处理操作
  • 不包含数据源信息,适用于任意数据

业务绘图包 (cemc-plots-kit)

  • 将数据源与图片产品包对接
  • 提供命令行接口

图 cedarkit 绘图工具项目之间调用关系链

cedar-graph 可以使用 CMA-HPC2023-SC1 上的业务系统数据快速绘图:

from cedar_graph.quickplot import quick_plot

quick_plot(
    plot_type="cn.bli_wind.default",
    system_name="CMA-MESO",
    start_time="2024073000",
    forecast_time="24h",
    wind_level=700,
)

cedar-graph 绘图使用两个关键对象,任意图片的 plot 函数都需要使用这两个对象。 只要填充了合适的数据,cedar-graph 可以用于任意模式的输出结果。

  • PlotData: 图片使用的数据
  • PlotMetadata: 图片使用的元信息,包括时间、业务系统名称、绘图配置等等

cedar-graph 提供 Python API 用于生成上述两个对象,支持控制绘图的各个步骤。

from pathlib import Path

import pytest
import pandas as pd

from cedar_graph.plots.cn.bli_wind.default import PlotMetadata, plot, load_data
from cedar_graph.data import LocalDataSource, DataLoader

system_name = "cma_gfs"
start_time = "2025071300"
wind_level = 700
forecast_time = pd.to_timedelta("24h")

output_image_path = Path(f"{plot_name}.{system_name}.CN.png")

metadata = PlotMetadata(
    start_time=start_time,
    forecast_time=forecast_time,
    system_name=system_name,
    wind_level=wind_level,
    sample_step=default_sample_step,
)

data_source = LocalDataSource(system_name=system_name)
data_loader = DataLoader(data_source=data_source)

plot_data = load_data(
    data_loader=data_loader,
    start_time=start_time,
    forecast_time=forecast_time,
    wind_level=wind_level,
)

panel = plot(
    plot_data=plot_data,
    plot_metadata=metadata,
)

output_dir.mkdir(exist_ok=True, parents=True)
panel.save(output_image_path)

cemc-plots-kit 将 cedar-graph 与实际的 NWP 系统数据相关联,支持 NWP 模式试验的数据保存方式,即同一时次 GRIB2 数据保存到同一个文件夹中。

cemc-plots-kit 提供 Python API:

from cemc_plots_kit.draw import draw_plot

draw_plot(
    system_name="cma_gfs,
    plot_type="height_500_mslp",
    start_time="2025071300",
    forecast_time="24h",
    data_dir=data_dir,
    work_dir=work_dir,
    data_file_name_template="gmf.gra.{start_time_label}{forecast_hour_label}.grb2",
)

cemc-plots-kit 也提供命令行 API:

python -m cemc_plots_kit \
	draw \
    --system-name cma_gfs
    --plot-type height_500_mslp \
    --start-time 2025071300 \
    --forecast-time 24h \
    --data-dir . \
    --work-dir .

cemc-plots-kit 还通过 YAML 支持批量绘图:

YAML 任务文件

runtime:
  base_work_dir: .

source:
  data_dir: .

system_name: cma_gfs

time:
  start_time: 2025071300
  forecast_time: 48h
  forecast_interval: 6h

plots:
  height_500_mslp: on
  rain_1h_wind_10m: off
  rain_24h: on

命令行调用:

python -m cemc_esmi_plots task \
  --task-file ${task_file_path}

注意:cedarkit 绘图相关项目还在开发中,未来可能会修改 API。

总结

笔者在写这篇博文时使用 ChatGPT 查阅学术文献,发现遗留软件现代化改造也是学术界的一个关注点,有不少研究文献。 笔者开发 sokort 项目的五年工作经验也证明了对遗留软件进行封装虽然能带来短期收益,但封装的维护成本会随时间逐渐增长,直到突破一个临界值而成为新的遗留软件。 因为封装只是临时掩盖遗留软件本身的问题,不会解决技术债务。 笔者认为一个合适的解决方案是开发新的软件代替遗留软件,也是目前正在开展的一项工作。

除了绘图脚本外,NWP 业务系统还有更多缺乏活跃更新的软件,有成为遗留软件的潜质,比如检索 CMADaaS 观测资料的 Fortran 程序。 笔者后续将继续关注业务系统遗留软件的改造问题,推动工具软件在单位内部的可持续发展。

参考

sokort 项目:

cemc-oper/sokort

使用Jupyter Notebook显示NCL绘图

cedarkit 工具套件项目:

cemc-oper/cedarkit-maps

cemc-oper/cedar-graph

cemc-oper/cemc-plots-kit