Chapter 8 开发者指南

本章面向希望将自有水文模型接入 GHDC 平台的研究者或工程师。阅读本章前,建议先了解 GHDC 的整体使用流程(第 5 章)。


8.1 平台架构概述

GHDC 的系统分为两个独立层,通过文件系统进行解耦通信,没有 RPC 或 HTTP 调用:

┌─────────────────────────────┐
│   Web 层(Flask + 前端)     │
│  接受用户提交、发送邮件       │
│  写任务文件 → 读状态信号      │
└──────────────┬──────────────┘
               │ 文件系统(/data/GHDC/tasks-v2/)
┌──────────────▼──────────────┐
│   R 层(GO.R 调度器)         │
│  轮询任务文件 → 调用模型       │
│  写状态信号 → 写结果 ZIP      │
└─────────────────────────────┘

关键原则:Web 层只负责接收用户输入、写入任务文件、轮询状态信号;R 层只负责读取任务文件、执行建模、写回结果。两层之间没有共享内存、没有数据库交互、没有网络调用,完全通过约定好的文件格式通信。

这一设计使得替换或扩展 R 层中的建模逻辑非常简单,无需改动任何 Web 代码。


8.2 任务文件协议

用户点击邮件确认链接后,Web 层在任务队列目录 /data/GHDC/tasks-v2/ 中写入以下文件:

/data/GHDC/tasks-v2/
├── task_{task_id}.txt          ← 触发文件(GO.R 扫描此文件)
└── {task_id}/
    ├── input.json              ← 建模参数(JSON 格式)
    └── input.zip               ← 流域边界 Shapefile ZIP

{task_id} 的格式为 YYYYMMDD_HHMMSS_{email_hash}_{project_name},例如 20260618_211224_6d1bf451_lcg2017

8.2.1 触发文件(task_{task_id}.txt

触发文件共 4 行:

20260618_211224_6d1bf451_lcg2017
user@example.com
lcg2017
2026-06-18T21:12:24.000000

依次为:任务 ID、邮箱、项目名称、任务创建时间(ISO 8601)。

GO.R 通过扫描 tasks-v2/task_*.txt 文件的出现来检测新任务。

8.2.2 参数文件(input.json

{
  "task_id": "20260618_211224_6d1bf451_lcg2017",
  "email": "user@example.com",
  "project_name": "lcg2017",
  "parameters": {
    "start_year": 2017,
    "end_year": 2018,
    "dem_source": "aster30",
    "forcing": "GLDAS",
    "min_cells": 400,
    "max_cell_area": 0.1,
    "aquifer_depth": 10,
    "aquifer_thickness": 10,
    "soil_data": "HWSD",
    "land_use": "MODIS",
    "dataonly": "no"
  },
  "created_at": "2026-06-18T21:12:24.000000"
}

parameters 子对象中各字段的含义:

字段 类型 说明
start_year int 模拟起始年份
end_year int 模拟结束年份
dem_source str 高程数据:aster30(ASTER 30m)或 merit90(MERIT 90m)
forcing str 气象驱动:GLDASNLDASCMFD
min_cells int 网格最小单元数
max_cell_area float 网格单元最大面积(km²)
aquifer_depth int 含水层厚度(m),与 aquifer_thickness 互为别名
aquifer_thickness int 同上
soil_data str 土壤数据:HWSD
land_use str 土地利用:MODIS
dataonly str "yes" 表示仅准备数据不建模(超大流域时由系统自动设置)

注意locale(语言)字段不会出现在 input.json 中,仅在 Web 数据库和邮件发送中使用,R 后端无需感知。


8.3 R 后端结构

?? 展示了 Web 层与 R 层之间通过文件系统交互的架构:

R 后端位于仓库 R_Backend/ 目录,核心文件如下:

文件 作用
GO.R 主调度入口:持续轮询任务队列,逐一处理新任务
getReady.R 初始化:加载依赖包、读取服务配置
Run_GHDC.sh 生产环境启动脚本(通过 systemd 管理)
functions/Configure.R 任务初始化:读取 input.json、计算建模参数
functions/task_intake_v2.R v2 任务解析:flatten.v2.task.json() 展平 JSON
script/service_ghdc.cfg.txt 服务配置:数据目录路径等

8.3.1 GO.R 调度机制

GO.R 是一个轮询循环,逻辑如下(简化):

repeat {
  tasks <- list.files(task_dir, pattern = "^task_.*\\.txt$")
  for (task_file in tasks) {
    task_id <- read_task_id(task_file)
    if (!is_processed(task_id)) {
      Configure(task_id)      # 初始化任务
      ModelDeploy(task_id)    # 执行建模
    }
  }
  Sys.sleep(check_interval)
}

检测到 task_*.txt 后,GO.R 调用 Configure() 读取并展平 input.json,然后调用 ModelDeploy() 执行完整的建模流程。

8.3.2 input.json 展平与字段映射

functions/task_intake_v2.R 中的 flatten.v2.task.json()input.json 的嵌套结构展平,并对部分字段进行重命名,以兼容现有 R 函数:

input.json 字段 R 内部变量名 说明
min_cells minimum_cell_number 最小网格单元数
max_cell_area maxim_cell_area 最大单元面积(km²)
forcing meteorological_data 气象驱动名称
soil_data soil(小写) 土壤数据集名称
land_use landuse;MODIS → mglc 土地利用数据集
task_id keyid 任务唯一标识
(默认) model = "shud" 默认建模模式
dataonly dataonly 仅数据模式标志

8.4 状态信号文件

R 后端通过在任务子目录下写入特定文件来向 Web 层报告处理状态,Web 层每 5 秒扫描一次这些文件:

信号文件 路径 含义
task_log.txt {task_id}/task_log.txt 任务已开始处理(Web 显示”处理中”)
completed.txt {task_id}/completed.txt 任务成功完成
error.log {task_id}/error.log 任务失败,文件内含错误信息

判断逻辑(优先级从高到低):

  1. completed.txt 存在 → 已完成
  2. error.log 存在 → 失败
  3. task_log.txt 存在 → 处理中
  4. 均不存在 → 排队等待

结果 ZIP 文件写入 /data/GHDC/downloads/{task_id}_result.zip,Web 层在检测到 completed.txt 后从此路径提供下载链接。


8.5 接入新模型的步骤

如果你希望将模型 X 集成到 GHDC 平台,以下是完整的接入流程。

8.5.1 第一步:实现 ModelDeploy() 函数

ModelDeploy() 是建模执行的唯一入口,接收由 Configure() 构建的 task_config 列表:

ModelDeploy <- function(task_config) {
  task_id    <- task_config$keyid
  task_dir   <- file.path(task_config$target_dir, task_id)
  log_file   <- file.path(task_dir, "task_log.txt")
  done_file  <- file.path(task_dir, "completed.txt")
  error_file <- file.path(task_dir, "error.log")

  # 1. 写入 task_log.txt,通知 Web 层任务已开始
  writeLines(c(task_id, as.character(Sys.time())), log_file)

  tryCatch({
    # 2. 读取建模参数
    start_year   <- task_config$start_year
    end_year     <- task_config$end_year
    dem_source   <- task_config$dem_source       # "aster30" 或 "merit90"
    forcing      <- task_config$meteorological_data
    min_cells    <- task_config$minimum_cell_number
    max_cell_area <- task_config$maxim_cell_area
    aquifer_depth <- task_config$aquifer_depth
    boundary_zip <- file.path(task_dir, "input.zip")  # 边界 Shapefile ZIP

    # 3. 调用模型 X 的建模逻辑
    run_model_x(boundary_zip, start_year, end_year, forcing, ...)

    # 4. 打包结果为 ZIP,写入下载目录
    result_zip <- file.path(task_config$download_dir,
                            paste0(task_id, "_result.zip"))
    zip(result_zip, files = <你的结果目录>)

    # 5. 写入 completed.txt,通知 Web 层任务成功
    writeLines(c(task_id, as.character(Sys.time())), done_file)

  }, error = function(e) {
    # 失败时写入 error.log
    writeLines(c(task_id, conditionMessage(e)), error_file)
  })
}

8.5.2 第二步:注册新的数据选项(可选)

如果你的模型需要新的气象驱动或数据集,需要在 Web 端的配置文件 dev/config/data_sources.yaml 中注册,并将 enabled 设为 true

forcing:
  - value: "MyForcing"
    label: "My Forcing Dataset"
    year_min: 1980
    year_max: 2023
    coverage: "global"
    enabled: true

修改后需要重新部署 Web 端(sudo ./dev/scripts/remot-deploy-v2.sh)使更改生效。

8.5.3 第三步:配置路径

R 后端的路径配置在 R_Backend/script/service_ghdc.cfg.txt 中定义:

TARGET.DIR=/data/GHDC/tasks-v2
DIR.DOWNLOAD_DIR=/data/GHDC/downloads
DIR.PROCESSING=/data/GHDC/rProcessing
DIR.RLOG=/data/GHDC/rProcessing/rlog
R 配置键 说明
TARGET.DIR 任务队列目录(Web 层写入,R 层读取)
DIR.DOWNLOAD_DIR 结果 ZIP 输出目录(R 层写入,Web 层读取)
DIR.PROCESSING 建模工作区(中间文件,处理完成后可清理)

确保这些路径与 Web 端 dev/config/prod.yaml 中的 paths 配置保持一致:

R 配置键 Flask prod.yaml
TARGET.DIR paths.r_task_dir
DIR.DOWNLOAD_DIR paths.download_dir

8.6 本地开发与测试

8.6.1 准备测试环境

  1. 克隆仓库并进入 R 后端目录:
git clone https://github.com/SHUD-System/GHDC.git
cd GHDC/R_Backend
Rscript getReady.R   # 安装/检查 R 依赖
  1. 将服务配置复制为本地开发版本并修改路径:
cp script/service_ghdc.cfg.txt script/service_local.cfg.txt
# 编辑 service_local.cfg.txt,将路径改为本地测试目录

8.6.2 构造测试任务

使用仓库内的示例边界文件(Misc/ExampleData/ 目录下的任一 .zip)手动构造一个测试任务:

TASK_ID="20260101_120000_test0001_mytest"
QUEUE_DIR="/your/local/tasks-v2"

# 创建任务子目录并复制边界文件
mkdir -p "${QUEUE_DIR}/${TASK_ID}"
cp Misc/ExampleData/example_boundary.zip "${QUEUE_DIR}/${TASK_ID}/input.zip"

# 写入 input.json
cat > "${QUEUE_DIR}/${TASK_ID}/input.json" << 'EOF'
{
  "task_id": "20260101_120000_test0001_mytest",
  "email": "test@example.com",
  "project_name": "mytest",
  "parameters": {
    "start_year": 2000,
    "end_year": 2024,
    "dem_source": "aster30",
    "forcing": "GLDAS",
    "min_cells": 100,
    "max_cell_area": 1.0,
    "aquifer_depth": 10,
    "aquifer_thickness": 10,
    "soil_data": "HWSD",
    "land_use": "MODIS",
    "dataonly": "no"
  },
  "created_at": "2026-01-01T12:00:00.000000"
}
EOF

# 写入触发文件
echo -e "${TASK_ID}\ntest@example.com\nmytest\n2026-01-01T12:00:00" \
  > "${QUEUE_DIR}/task_${TASK_ID}.txt"

构造完成后,启动 GO.R 即可触发处理:

Rscript GO.R

8.6.3 验证任务结果

任务完成后,检查以下路径:

# 状态信号
ls "${QUEUE_DIR}/${TASK_ID}/"
# 应该看到 task_log.txt 和 completed.txt(成功)或 error.log(失败)

# 结果 ZIP
ls /your/local/downloads/
# 应该看到 ${TASK_ID}_result.zip

如果出现 error.log,查看其内容定位错误:

cat "${QUEUE_DIR}/${TASK_ID}/error.log"

8.7 相关资源

资源 链接
SHUD 模型 https://github.com/SHUD-System/SHUD
rSHUD 工具包 https://github.com/SHUD-System/rSHUD
AutoSHUD 自动化脚本 https://github.com/SHUD-System/AutoSHUD
GHDC 仓库 https://github.com/SHUD-System/GHDC

如有接入需求或技术问题,请联系: