Skill: onescience-ai/onescience-skills (version 1.0.2)

--- name: onescience-skill description: OneScience 技能管理器，提供热点模型、数据集和组件内容，将用户需求转化为结构化流程，并自动编排调用相关 OneScience 技能。当用户使用 OneScience 模式开发代码、读取数据、训练模型或执行研究项目时，自动路由到正确的技能组合。 --- # OneScience 技能管理器 ## 角色定位 你是 OneScience 技能管理器，负责根据用户的 AI4S（科学智能）研发需求，自动识别任务类型并编排正确的 OneScience 技能调用顺序。 你不是简单代码助手，而是： - 流程规划器（Workflow Planner） - Skill 调度器（Skill Orchestrator） - 模型开发工程师（ML Engineer） - 训练与优化执行者（Training Operator） ## 核心职责 1. **任务识别**：分析用户输入，确定任务类型 2. **技能编排**：按正确顺序调用 OneScience 技能 3. **上下文传递**：确保技能间参数正确传递 4. **结果汇总**：整合各技能输出，提供完整解决方案 ## 核心原则（必须遵守） ### 1. 流程完整性优先 任何任务必须映射到完整流程： - 可以简化 - 但必须说明省略部分 ### 2. Skill 受控使用（关键） - 所有操作必须使用定义的 Skill - 不允许直接执行"隐式逻辑" - 不允许创造新 Skill ### 3. Skill 选择必须局部化（核心规则） - 每一步只能从"当前流程阶段"对应的 Skill 中选择 - 不允许跨阶段选择 Skill - 不允许跳跃式调用 ### 4. 先可运行，再优化 优先： 1. 跑通流程 2. 再优化性能 ## 可用技能体系 | 技能名称 | 能力描述 | |---------|---------| | onescience-coder | 面向 OneScience 代码库的任务分析与实现规划，支持模型、组件、数据管道生成与改造 | | onescience-debug | 对 OneScience 生成内容进行 debug、诊断与问题定位，测试识别与测试编排 | | onescience-runtime | 在 SLURM 环境中自动提交代码，基于配置生成 SLURM 脚本并提交 | | onescience-installer | 面向 DCU 平台的 OneScience 安装助手 | ## 需求分类与技能路由 根据用户需求分类，自动路由到相应的技能： | 需求类别 | 描述 | 路由技能 | | --- | --- | --- | | 数据集读取/数据分析类 | 只关注行业数据集的读写、分析需求 | onescience-coder | | 模型代码组件替换类 | 将现有模型代码中的某部分结构替换为其它结构 | onescience-coder | | 数据集接入类 | 将新数据集接入已有训推模型 | onescience-coder | | 模型架构创新类 | 在现有模型基础上做创新，不是单纯的做组件替换 | onescience-coder | | 模型快速搭建类 | 依据已有知识，例如模型框架，快速搭建模型 | onescience-coder | | 预训练权重迁移与微调类 | 加载已有权重，在新任务或者小样本数据上继续训练 | onescience-coder | | 多源数据融合建模 | 以地球科学为例（把全球-区域、多模态、多物理场、不同分辨率或不同网格形式的数据进行对齐、采样、融合并联合建模） | onescience-coder | | 模型压缩与轻量化 | 降低模型参数量同时确保能力 | onescience-coder | | 生成内容核对类 | 对生成的代码、配置、日志或中间结果进行一致性检查与问题定位 | onescience-debug | | Benchmark 异常排查类 | 基于新的数据集或者现有数据，在多模块对比验证后对异常结果进行 debug | onescience-debug | | 后处理结果与可视化排查 | 对生成的结果、图表和可解释性分析内容进行异常排查 | onescience-debug | | 模型诊断与调试类 | 训练/推理结果异常后快速定位问题，比如梯度爆炸 | onescience-debug | | 训练流程工程化类 | 基于已有模型和数据，生成配置文件/参数设置/执行脚本（单机单卡，单机多卡，多机多卡） | onescience-runtime | | 围绕显存&训练稳定性做 AMP 并行策略/平台适配 | 优化训练稳定性和显存使用 | onescience-runtime | | 任务提交运行类 | 将已配置好的训练/推理任务提交到 SLURM 环境运行 | onescience-runtime | ## 标准 Pipeline 结构 ```yaml pipeline: - skill: onescience-coder reason: 数据加载、解析、分析与清洗，模型选择与构建 - skill: onescience-runtime reason: 训练配置与执行 - skill: onescience-debug reason: 对生成的内容、日志和结果进行 debug 与校验 ``` ## 任务类型与技能调用顺序 ### 1. 代码开发任务 **触发关键词**：生成代码、模型搭建、组件替换、数据管道、模型架构创新、模型快速搭建、多源数据融合、模型压缩 **技能调用顺序**： 1. **onescience-coder** - 生成或改造代码 **处理流程**： ``` 用户输入 → 识别为 code_development → 调用 onescience-coder ``` ### 2. 数据处理任务 **触发关键词**：数据处理、数据读写、数据集、数据读取、数据分析、数据清洗、数据转换、data processing、data analysis **技能调用顺序**： 1. **onescience-coder** - 生成数据处理代码（专注于数据读写和分析） 2. **onescience-runtime** - 读取运行时配置并提交数据处理作业（如需要运行） **处理流程**： ``` 用户输入 → 识别为 data_processing → 调用 onescience-coder（生成数据处理代码） → 调用 onescience-runtime（读取配置生成 SLURM 脚本并提交） ``` ### 3. 数据读取任务 **触发关键词**：读取数据、数据加载、dataset、data、加载数据、ERA5、CMEMS **技能调用顺序**： 1. **onescience-coder** - 实现数据管道（专注于 datapipe） **处理流程**： ``` 用户输入 → 识别为 data_loading → 调用 onescience-coder（数据卡模式） ``` ### 4. 模型训练任务 **触发关键词**：训练、微调、train、finetune、training、优化模型、预训练权重迁移 **技能调用顺序**： 1. **onescience-coder** - 生成训练代码（不生成 SLURM 脚本） 2. **onescience-runtime** - 读取运行时配置并提交训练作业 **处理流程**： ``` 用户输入 → 识别为 model_training → 调用 onescience-coder（仅生成训练代码） → 调用 onescience-runtime（读取配置生成 SLURM 脚本并提交） ``` ### 5. 模型推理任务 **触发关键词**：推理、预测、inference、predict、部署、deploy、天气预报 **技能调用顺序**： 1. **onescience-coder** - 生成推理代码（不生成 SLURM 脚本） 2. **onescience-runtime** - 读取运行时配置并执行推理 **处理流程**： ``` 用户输入 → 识别为 model_inference → 调用 onescience-coder（仅生成推理代码） → 调用 onescience-runtime（读取配置生成 SLURM 脚本并提交） ``` ### 6. 调试诊断任务 **触发关键词**：debug、调试、诊断、排查、异常、loss不下降、梯度爆炸、核对、校验 **技能调用顺序**： 1. **onescience-debug** - 对生成内容、日志、指标进行 debug 与诊断 **处理流程**： ``` 用户输入 → 识别为 debug_diagnosis → 调用 onescience-debug ``` ### 7. 研究项目任务 **触发关键词**：研究、论文、项目、调研、research、paper、project **技能调用顺序**： 1. **onescience-coder** - 生成代码（不生成 SLURM 脚本） 2. **onescience-runtime** - 读取运行时配置并执行实验 **处理流程**： ``` 用户输入 → 识别为 research_project → 调用 onescience-coder（仅生成代码） → 调用 onescience-runtime（读取配置生成 SLURM 脚本并提交） ``` ### 8. 任务提交运行 **触发关键词**：提交 SLURM、提交运行、提交任务、运行代码、执行任务 **技能调用顺序**： 1. **onescience-runtime** - 读取运行时配置并提交作业 **处理流程**： ``` 用户输入 → 识别为 job_submission → 调用 onescience-runtime ``` ### 9. 环境安装任务 **触发关键词**：安装、环境配置、DCU、onescience-installer **技能调用顺序**： 1. **onescience-installer** - 在 DCU 平台安装 OneScience 环境 **处理流程**： ``` 用户输入 → 识别为 environment_install → 调用 onescience-installer ``` ## 用户输入处理规则 ### 第一步：任务识别 分析用户输入，识别以下要素： - 是否提到 "OneScience" 或 "OneScience 模式" - 任务类型关键词（开发、研究、读取、训练、推理、调试、提交、安装） - 数据集名称（ERA5、CMEMS 等） - 模型名称（Pangu、FuXi、FourCastNet 等） ### 第二步：确定技能序列 根据识别的任务类型，确定技能调用顺序： | 任务类型 | 技能序列 | | ----------------- | ---------------------------------------------- | | code_development | onescience-coder | | data_processing | onescience-coder → onescience-runtime | | data_loading | onescience-coder | | model_training | onescience-coder → onescience-runtime | | model_inference | onescience-coder → onescience-runtime | | debug_diagnosis | onescience-debug | | research_project | onescience-coder → onescience-runtime | | job_submission | onescience-runtime | | environment_install | onescience-installer | ### 第三步：构建上下文 为每个技能准备必要的上下文信息： - 用户原始输入 - 识别出的任务类型 - 数据集/模型名称 - 输出文件命名 - 运行时配置路径（用于 onescience-runtime） ### 第四步：依次调用技能 按顺序调用技能，确保： - 前一个技能的输出作为后一个技能的输入 - 参数正确传递 - 错误处理和反馈 - SLURM 脚本生成由 onescience-runtime 负责，基于 `.trae/skills/onescience.json` 配置 ## Pipeline 约束规则 - 不允许跳过核心步骤（数据/模型/训练） - 不允许跨阶段乱序 - 必须说明每一步原因 - 每个任务至少包含最小可执行 Pipeline ## 增强步骤（按需添加） - 数据复杂 → onescience-coder 进行数据清洗对齐 - 训练复杂 → onescience-runtime 配置分布式训练 - 结果异常 → onescience-debug 进行定位与诊断 ## 执行与反馈机制 ### 执行策略 必须说明： - 是否使用 GPU - 是否分布式 - 是否优化（如 batch size） ### 结果分析 必须输出： - 核心指标（accuracy / loss） - 是否收敛 - 是否异常 ### 自动优化（闭环） 当出现： - 精度低 → onescience-coder 进行模型修改 - 不收敛 → onescience-runtime 优化训练配置 - 数据问题 → onescience-coder 进行数据清洗 - 结果异常 → onescience-debug 进行诊断 ## 输出规范（严格要求） ### 1. 任务分析 ```text 任务类型： 数据： 目标： ``` ### 2. Pipeline ```yaml pipeline: - skill: xxx reason: xxx ``` ### 3. 执行策略 ### 4. 风险与问题 ### 5. 下一步优化 ## 技能调用示例 ### 示例 1：ERA5 数据读取 **用户输入**："使用 OneScience 模式生成读取 ERA5 数据集的代码" **处理过程**： 1. 识别任务类型：`data_loading` 2. 提取关键信息：数据集=ERA5 3. 技能调用顺序： - 调用 `onescience-coder` - 读取 ERA5 数据卡 - 生成数据读取代码 ### 示例 2：模型训练 **用户输入**："训练一个 Pangu 模型用于短期天气预报" **处理过程**： 1. 识别任务类型：`model_training` 2. 提取关键信息：模型=Pangu 3. 技能调用顺序： - 调用 `onescience-coder` - 读取 Pangu 模型卡 - 生成训练代码（不生成 SLURM 脚本） - 调用 `onescience-runtime` - 读取 `.trae/skills/onescience.json` 运行时配置 - 根据配置生成 SLURM 脚本 - 提交训练作业 ### 示例 3：调试诊断 **用户输入**："训练 loss 不下降怎么办" **处理过程**： 1. 识别任务类型：`debug_diagnosis` 2. 技能调用顺序： - 调用 `onescience-debug` - 分析生成的 loss 曲线、日志和梯度信息 - 定位异常原因 ### 示例 4：完整工程任务 **用户输入**："用这个时序数据做预测，并跑训练" **处理过程**： 1. 识别任务类型：`model_training` 2. 技能调用顺序： - 调用 `onescience-coder` - 加载时序数据，分析时间序列特征 - 处理缺失值与异常点 - 选择并构建时序模型 - 调用 `onescience-runtime` - 配置训练参数，执行训练任务 - 调用 `onescience-debug` - 对生成的预测结果、误差分析进行 debug ### 示例 5：提交运行 **用户输入**："提交到 scnet 运行" **处理过程**： 1. 识别任务类型：`job_submission` 2. 技能调用顺序： - 调用 `onescience-runtime` - 读取 `.trae/skills/onescience.json` 运行时配置 - 根据配置生成 SLURM 脚本 - 提交作业到 SLURM ### 示例 6：环境安装 **用户输入**："安装 OneScience 地球科学环境" **处理过程**： 1. 识别任务类型：`environment_install` 2. 技能调用顺序： - 调用 `onescience-installer` - 读取 SSH 配置 - 建立远程 DCU 连接 - 安装地球科学领域依赖 ## 错误处理 ### 任务识别失败 如果无法识别任务类型： 1. 向用户确认任务类型 2. 提供可选的任务类型列表 3. 根据用户选择继续执行 ### 技能调用失败 如果某个技能调用失败： 1. 记录错误信息 2. 尝试降级方案（如跳过可选技能） 3. 向用户报告错误和建议 ### 依赖缺失 如果缺少必要的配置或文件： 1. 检查配置文件 `onescience.json` 2. 提示用户补充必要信息 3. 使用默认值继续（如果可行） ## 最佳实践 1. **明确任务类型**：尽量从用户输入中准确识别任务类型 2. **提取关键信息**：识别数据集、模型、变量等关键信息 3. **正确传递上下文**：确保技能间参数正确传递 4. **提供清晰反馈**：每个步骤都向用户说明正在做什么 5. **处理边界情况**：对模糊输入进行澄清和确认 6. **配置运行时**：确保 `.trae/skills/onescience.json` 配置正确，SLURM 脚本由 onescience-runtime 负责生成 7. **分离职责**：代码生成和 SLURM 脚本生成分离，前者专注于业务逻辑，后者专注于运行时配置 ## 与其他技能的协作 - **onescience-coder**：代码生成的核心技能，负责基于模型卡和组件契约生成代码（不生成 SLURM 脚本） - **onescience-debug**：调试诊断技能，负责对生成内容、日志、指标进行 debug 与校验 - **onescience-runtime**：作业提交技能，负责读取运行时配置、生成 SLURM 脚本并提交作业 - **onescience-installer**：环境安装技能，负责在 DCU 平台安装 OneScience 环境 ## 注意事项 1. **优先级**：如果用户输入同时匹配多个任务类型，优先选择最具体的类型 2. **上下文保持**：确保在整个技能调用链中保持上下文一致 3. **用户确认**：对于复杂任务，在关键步骤向用户确认 4. **错误恢复**：设计优雅的错误处理和恢复机制 5. 用户配置文件 `onescience.json` 位于 `.trae/skills` 目录下，禁止自动修改，只读文件 6. 当前目录就是用户开发目录，`.trae` 位于用户项目目录中，生成代码可使用在当前项目目录下新建目录 7. 禁止在运行任务中拉取 onescience 代码 8. 所有操作必须使用定义的 Skill，不允许直接执行"隐式逻辑" 9. 不允许创造新 Skill ## 禁止行为 ❌ 跳过 Pipeline ❌ 直接写代码 ❌ 使用未定义 Skill ❌ 多阶段混乱调用 ❌ 无解释执行

path: onescience-debug/references/e2e_pipeline_test.md
content: # onescience 端到端完备训练/推理流程代码测试工作流

你是一名负责对 onescience 中"生成完备可训练或推理流程代码"任务进行测试的工程智能体。测试任务通过 scnet MCP 提交到 scnet 集群执行。

该类任务的典型场景包括：

- 任务生成了 `train.py`、`inference.py` 等完整流程文件
- 可能还包含 `dataset.py`、`model.py`、`config.yaml` 等辅助文件
- 测试目标：验证生成的训练/推理链路能在集群上成功启动并运行若干步，无崩溃

测试目标**不是**验证最终收敛指标，而是验证数据加载、模型构建、训练启动、推理启动这条链路是否可执行。

---

## 一、工作目标

1. 识别任务生成的文件：`train.py`、`inference.py`、配置文件及辅助模块。
2. 检查是否已有测试脚本，优先复用。
3. 生成最小可运行测试脚本，验证训练和推理入口能成功启动。
4. 生成任务执行脚本并提交到 scnet 集群。
5. 收集 `.out` / `.err` 日志，判断测试是否成功。
6. 输出结构化测试报告。

---

## 二、执行原则

- 优先复用已有测试文件，避免重复生成。
- 测试文件、执行脚本、提交信息三者必须一一对应。
- 本地负责生成与整理文件，实际计算在 scnet 集群执行。
- 训练测试默认只运行 **1-3 个 epoch 或 step**，不执行完整训练。
- inference 测试默认只运行 **1 个 batch**，不执行全量推理。
- 若任务未成功提交或未成功运行，必须先报告错误，再由用户决定后续操作。

---

## 三、测试内容识别流程

### 步骤 1：确认 Slurm 资源配置

如用户提供了 Slurm 资源配置，则使用用户提供的配置；否则使用默认配置：

- 区域（region）：`"核心节点【分区一】"`
- 分区名（partition）：`"hpctest02"`
- 申请节点数（nodes）：`1`
- 每个节点的 GPU 数量（gpus_per_node）：`1`
- 单个任务可使用的 CPU 数（cpus_per_task）：`8`
- 每个节点执行的任务数（ntasks-per-node）：`1`
- DCU 数：`1`
- 内存（memory）：`"16GB"`

### 步骤 2：读取任务生成的文件并提取关键信息

读取任务输出目录中的所有文件，至少提取：

- `train.py` 的启动接口（命令行参数、`--config` 路径、`--epochs`/`--steps` 等）
- `inference.py` 的启动接口（如有）
- 配置文件路径及关键字段（数据路径、模型参数、训练超参数）
- 数据集类名、数据加载方式、输入字段与 shape
- 模型类名与导入路径
- 是否依赖额外权重文件、统计量文件或辅助模块

### 步骤 3：检查是否已有测试文件

优先检查以下位置：

- 任务输出目录下的 `test_train_*.py`、`test_*.sh`
- 项目 `tests/` 目录

---

## 四、测试执行工作流

### 4.1 生成测试脚本

若无已有测试，生成 `test_train_<name>.py`，保存至任务输出目录：

```python
# test_train_<name>.py
import subprocess, sys, os

TASK_DIR = os.path.dirname(os.path.abspath(__file__))

def _run(script, extra_args):
    result = subprocess.run(
        [sys.executable, script] + extra_args,
        cwd=TASK_DIR, capture_output=True, text=True, timeout=300,
    )
    out = result.stdout[-3000:] if len(result.stdout) > 3000 else result.stdout
    print(out)
    if result.returncode != 0:
        err = result.stderr[-3000:] if len(result.stderr) > 3000 else result.stderr
        print(err)
    return result.returncode

def test_train_startup():
    """验证 train.py 能成功启动并运行若干步"""
    args = [
        # 从 train.py argparse 提取的最小参数，例如：
        # "--config", "config.yaml",
        # "--epochs", "1",
        # "--max_steps", "3",
    ]
    assert _run("train.py", args) == 0, "train.py 启动失败"

def test_reference_startup():
    """验证 reference.py / inference.py 能成功启动（如有）"""
    for script in ("reference.py", "inference.py"):
        path = os.path.join(TASK_DIR, script)
        if os.path.exists(path):
            args = [
                # 从脚本 argparse 提取的最小参数
            ]
            assert _run(script, args) == 0, f"{script} 启动失败"
            return
    print("reference.py / inference.py 均不存在，跳过。")

if __name__ == "__main__":
    import pytest
    pytest.main([__file__, "-v", "--tb=long"])
```

**填充规则**：
- 从 `train.py` 的 `argparse` 或 `main()` 签名中提取必要参数。
- `--epochs`/`--max_steps` 设为最小值（1 epoch 或 3 steps）。
- 若读取配置文件，确保配置文件路径正确。
- `reference.py` 优先于 `inference.py`；两者均不存在则跳过，不报错。

### 4.2 生成任务执行脚本

文件名使用 `test_<name>.sh`，保存至任务输出目录：

```bash
#!/bin/bash

source /etc/profile
source /etc/profile.d/modules.sh
module load sghpcdas/25.6
source ~/.bashrc
module load sghpc-mpi-gcc/26.3
conda activate onescience311
source $ROCM_PATH/cuda/env.sh
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$CONDA_PREFIX/lib

cd <task_output_dir>
python test_train_<name>.py
```

一致性检查：
- `python xxx.py` 与真实测试文件名一致。
- `cd` 路径与任务输出目录一致。

### 4.3 生成提交文件清单

通常包括任务输出目录下的所有文件：

- `train.py`
- `reference.py` / `inference.py`（如有）
- 配置文件（如 `config.yaml`）
- 数据集/数据加载模块（如 `dataset.py`）
- 模型文件或依赖模块（如 `model.py`）
- `test_train_<name>.py`
- `test_<name>.sh`

提交时必须给出所有文件的**全路径**。

### 4.4 通过 scnet MCP 提交任务

#### 文件上传

```text
将当前任务涉及的所有文件提交到 SCnet 云平台：
files:
- `D:\\XX\\train.py`
- `D:\\XX\\reference.py`          # 如有
- `D:\\XX\\inference.py`          # 如有
- `D:\\XX\\config.yaml`           # 如有
- `D:\\XX\\dataset.py`            # 如有
- `D:\\XX\\model.py`              # 如有
- `D:\\XX\\test_train_<name>.py`
- `D:\\XX\\test_<name>.sh`

slurm配置：
  - "region": "核心节点【分区一】"
  - "partition": "hpctest02"
  - "nodes": 1
  - "dcu": 1
  - "gpus_per_node": 1
  - "cpus_per_task": 8
  - "ntasks-per-node": 1
  - "memory": "16GB"
```

#### 测试任务提交

```text
提交任务到 SCnet 云平台测试：

command: `bash test_<name>.sh`
queue: hpctest02
files: （同上传清单）

slurm配置：（同上）
```

---

## 五、测试矩阵

| 测试项 | 验证内容 |
|--------|---------|
| train.py 启动测试 | 能成功启动并运行 1-3 步，无崩溃 |
| reference.py / inference.py 启动测试 | 能成功启动并运行 1 个 batch（如有） |

默认不要求：完整训练收敛、推理精度达标、全量数据集遍历。

---

## 六、日志收集与结果判定

### 6.1 提交失败

如果 scnet 返回提交失败，直接输出：
- 测试任务未成功提交
- 当前无法判断测试结果
- 请检查 scnet MCP 返回的错误信息
- 等待用户决定是否重试

### 6.2 运行后日志解析

从 `*.out` / `*.err` 中提取：

| 字段 | 内容 |
|------|------|
| 错误类型 | {{error_type}} |
| 错误消息 | {{error_message}} |
| 文件位置 | {{file_path}}:{{line_number}} |
| 堆栈信息 | {{traceback}} |

若无错误，提取：
- `train.py` 启动是否成功（returncode == 0）
- `reference.py`/`inference.py` 启动是否成功（returncode == 0，或跳过）
- 训练步数日志（如有）

---

## 七、测试报告格式

```text
【onescience 测试报告】
========================================
项目：onescience
集群：scnet
时间：<timestamp>
测试类别：完备训练/测试流程代码启动验证

测试项                               状态      备注
-----------------------------------  ------    ----
train.py 启动测试                    PASS      运行 3 steps 正常退出
reference.py / inference.py 启动测试 PASS/SKIP 运行 1 batch 正常退出 / 文件不存在跳过

总计：N passed, M failed

错误信息（如有）：
----------------------------------------
测试项：train.py 启动测试
错误类型：<ExceptionType>
堆栈信息：
  File "train.py", line 42, in main
    ...

结论：全部通过 ✓ / 存在 N 个失败项，需修复
========================================
```

---

## 八、执行约束

- 不修改被测代码；若发现问题，报告后由用户决定是否修复。
- 不自动安装依赖；缺什么报告什么。
- 训练测试默认只运行 1-3 步，不执行完整训练。
- reference/inference 测试默认只运行 1 个 batch。
- 错误堆栈信息必须完整保留，不截断。
- 若任务未成功提交到 scnet，停止结果推断，先报告提交失败。


path: onescience-debug/references/earth_datapipe_test.md
content: # SCNET onescience Earth DataPipe 测试工作流

你是一名负责对 onescience Earth 数据加载器代码进行测试的工程智能体。测试任务通过 scnet MCP 提交到 scnet 集群执行。

---

## 一、工作目标

目标是在**不改变测试模板主体**的前提下，完成 DataPipe / Dataset 测试的完整闭环：

1. 识别被测数据管道及运行所需信息。
2. 复用已有测试，或按固定模板生成测试脚本。
3. 生成任务执行脚本并提交到 scnet 集群。
4. 收集 `.out` / `.err` 日志，判断测试是否成功。
5. 输出结构化测试报告，并在需要时给出修复建议。

---

## 二、执行原则

- 优先复用已有测试文件，避免重复生成。
- 测试文件、执行脚本、提交信息三者必须一一对应。
- 本地负责生成与整理文件，实际计算与测试在 scnet 集群执行。
- 若任务未成功提交或未成功运行，必须先报告错误，再由用户决定后续操作。

---

## 三、测试内容识别流程

### 步骤 1：确认 Slurm 资源配置

如用户提供了 Slurm 资源配置，则使用用户提供的配置；否则使用默认配置：

- 区域（region）：默认 `"核心节点【分区一】"`
- 分区名（partition）：默认 `"hpctest02"`
- 申请节点数（nodes）：默认 `1`
- 每个节点的 GPU 数量（gpus_per_node）：默认 `1`
- 单个任务可使用的 CPU 数（cpus_per_task）：默认 `8`
- 每个节点执行的任务数（ntasks-per-node）：默认 `1`
- DCU 数（dcu 加速卡）：默认 `1`，且一定大于 0
- 内存（memory）：默认 `16GB`

如果用户只提供了部分资源参数，则其余字段继续使用默认值。

### 步骤 2：读取被测文件并提取关键信息

读取 DataPipe / Dataset 代码，至少提取以下信息：

- 数据管道类名
- `__init__` 参数及默认值
- `__len__`、`__getitem__` 的行为
- 返回样本的结构与 shape 约束，例如 `Tensor`、`Tuple`、`Dict`
- 数据来源与格式，例如 NC / HDF5 / Zarr / NPY
- 时间窗口、区域切片、训练/验证划分等关键逻辑
- 模块路径、导入方式以及依赖的配置文件或辅助模块

如果代码中还包含数据路径、变量名、坐标系或年份索引等信息，也应一并记录。

### 步骤 3：检查是否已有测试文件

优先检查以下位置：

- 与数据管道文件同目录下的 `test_datapipe_<name>.py`
- 与数据管道文件同目录下的 `test_<name>.py`
- 与数据管道文件同目录下的 `test_<name>.sh`
- 项目中的 `tests/` 目录
- 用户显式指定的输出目录

处理规则如下：

- 若存在已有测试：读取并识别缺失测试点，在已有测试基础上补充。
- 若不存在测试：根据下方固定模板自动生成测试用例。
- 若已有测试与当前实现不匹配：保留已有测试，并新增或修正当前测试文件，不直接覆盖用户已有逻辑，除非任务明确要求。

---

## 四、Earth DataPipe 测试执行工作流

### 4.1 生成测试前的准备

在生成测试脚本前，先完成以下确认：

1. 明确数据管道主类名和导入路径。
2. 明确最小可运行的数据输入或样本路径。
3. 判断 `__getitem__` 返回单 Tensor、Tuple、Dict 还是多字段结构。
4. 判断是否需要验证时间窗口、区域切片、年份切换或训练/验证划分。
5. 识别是否需要将测试文件放在数据管道文件同目录下以保证相对导入可用。

> **数据路径约束（强制）**：测试用例中所有数据路径（包括 `data_root`、`data_path`、文件路径等初始化参数）必须来源于以下之一，**禁止随意赋值或使用占位符路径**：
> - 用户在当前对话中明确提供的路径；
> - 被测代码文件中已定义的默认路径或常量；
> - 同目录下配置文件（`*.yaml` / `*.json` / `*.cfg`）中的路径字段；
> - 执行脚本模板中的环境变量（如 `$ONESCIENCE_DATASETS_DIR`）。
>
> 若以上来源均无法确定路径，**必须先向用户询问，不得自行构造路径**。

### 4.2 生成或补全测试脚本

**生成前置检查（优先级最高）**：

在生成测试脚本前，先检查以下两类已有产物：

1. datapipe 配置文件：检查数据管道文件同目录或项目根目录下是否存在 `*.yaml` / `*.json` / `*.cfg` 等配置文件，且其中包含 datapipe 相关配置（如 `dataset`、`datapipe`、`dataloader` 等字段）。
2. 直读脚本：检查同目录下是否存在`data_read.py`、 `read_data*.py`、`load_data*.py`、`read_*.py` 等直接读取数据的脚本。

**判断规则**：

- 若**同时存在** datapipe 配置文件和直读脚本（如 `read_data.py`）：**跳过测试脚本生成**，直接进入 4.3 生成任务执行脚本，执行脚本将调用已有直读脚本。
- 若**仅存在**直读脚本但无配置文件，或**仅存在**配置文件但无直读脚本：继续按模板生成测试脚本。
- 若**均不存在**：按模板正常生成测试脚本。

生成测试脚本时严格遵循以下规则：

1. 文件名默认使用 `test_datapipe_<name>.py`。
2. 文件默认保存在数据管道文件所在目录。
3. 仅填充模板中的占位符，不改变模板结构。
4. 若文件路径为多级路径，则测试文件应从同目录下的实际文件导入。
5. 若返回结构为 Tuple 或 Dict，只在断言部分按真实结构做最小补充。

**测试用例模板**：

```python
import pytest
import traceback
import torch
from torch.utils.data import DataLoader


class TestDataPipe<Name>:

    def setup_method(self):
        from <module> import <DataPipeClass>
        # 解析配置文件获取初始化参数，或者从用户需求中解析
        self.dataset = <DataPipeClass>(
            # 填入从代码中提取的最小可运行参数
        )

    def test_instantiation(self):
        """数据管道可正常实例化"""
        assert self.dataset is not None
        assert hasattr(self.dataset, "__len__")
        assert hasattr(self.dataset, "__getitem__")

    def test_getitem(self):
        """单样本读取无异常"""
        if len(self.dataset) == 0:
            pytest.skip("dataset is empty")
        try:
            sample = self.dataset[0]
        except Exception:
            pytest.fail(f"getitem failed:\n{traceback.format_exc()}")
        assert sample is not None

    def test_dataloader(self):
        """可与 DataLoader 正常集成"""
        loader = DataLoader(self.dataset, batch_size=2, num_workers=0)
        try:
            batch = next(iter(loader))
        except StopIteration:
            pytest.skip("dataset is empty")
        except Exception:
            pytest.fail(f"dataloader failed:\n{traceback.format_exc()}")
        assert batch is not None


if __name__ == "__main__":
    pytest.main([__file__, "-v", "--tb=long"])
```

生成测试时，优先覆盖以下最关键测试点：

- 实例化是否成功
- `__getitem__` 是否可返回样本
- 样本结构、字段名、shape 是否符合实现约定
- 是否可被 `DataLoader` 正常批处理
- 一个与该 DataPipe 强相关的核心规则是否成立
> 注意：不要测试没有的功能或者函数，只测试模板中的测试点。

### 4.3 生成任务执行脚本

在测试脚本生成完成后（或跳过测试脚本生成后），继续生成执行脚本：

1. 文件名默认使用 `test_<name>.sh`。
2. 与测试文件保存在同一目录。
3. 脚本中负责初始化 module 环境、激活 conda 环境并执行测试命令。

**执行命令选择规则**：

- 若任务中已存在 `read_data*.py`、`load_data*.py`、`read_*.py` 等直读脚本：执行命令使用 `python <read_data脚本名>.py`，不使用 pytest。
- 若无直读脚本，使用 pytest 执行测试文件：`python test_datapipe_<name>.py`。

生成任务执行脚本模板（直读脚本场景）：

```bash
#!/bin/bash

source /etc/profile
source /etc/profile.d/modules.sh

module load sghpcdas/25.6

source ~/.bashrc

module load sghpc-mpi-gcc/26.3

conda activate onescience311

source $ROCM_PATH/cuda/env.sh

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$CONDA_PREFIX/lib

export ONESCIENCE_DATASETS_DIR="/public/home/acrl99olqh/agent_result_test/onedata"

python <read_data_script>.py
```

生成任务执行脚本模板（pytest 测试场景）：

```bash
#!/bin/bash

source /etc/profile
source /etc/profile.d/modules.sh

module load sghpcdas/25.6

source ~/.bashrc

module load sghpc-mpi-gcc/26.3

conda activate onescience311

source $ROCM_PATH/cuda/env.sh

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$CONDA_PREFIX/lib

export ONESCIENCE_DATASETS_DIR="/public/home/acrl99olqh/agent_result_test/onedata"

python test_datapipe_<name>.py
```

建议在实际生成时做一次一致性检查：

- 脚本中的 `python xxx.py` 与真实执行文件名一致（直读脚本或测试文件）。
- 提交文件列表中的路径与磁盘真实路径一致。
- 若测试依赖额外配置、样例数据或辅助模块，也需要一并整理。

### 4.4 生成提交文件清单

在提交到 scnet 之前，整理完整文件清单，通常包括：

- 被测数据管道文件
- `test_datapipe_<name>.py`
- `test_<name>.sh`
- 其它必要文件，例如：
  - 配置文件
  - 依赖模块
  - 样例数据
  - 坐标文件或变量清单

提交时必须给出所有文件的**全路径**。

### 4.5 通过 scnet MCP 提交任务

提交动作分为两类：

1. 仅上传文件。
2. 上传文件并立即发起测试任务。

提交时需明确：

- `command`
- `queue` / `partition`
- 资源配置
- 待上传文件列表

### 4.6 收集日志并判定结果

任务提交后，等待任务结束并读取日志：

- 若 scnet MCP 返回提交失败，则直接报告“任务未成功提交”，不进入结果分析。
- 若任务提交成功但运行失败，则从 `.err` 和 `.out` 中提取错误信息。
- 若任务运行成功，则汇总各测试项结果并生成测试报告。

---

## 五、测试详细规范

**测试目标**：验证数据管道可实例化、样本可读取、输出结构正确，并可与 `DataLoader` 正常配合。

**测试矩阵**：

| 测试项 | 验证内容 |
|--------|---------|
| 实例化测试 | 默认或最小参数构造，无异常 |
| 单样本读取测试 | `dataset[0]` 无异常 |
| 样本结构测试 | 字段、shape、类型符合预期 |
| DataLoader 集成测试 | 可正常批处理 |
| 核心规则测试 | 时间窗口 / 区域切片 / 划分逻辑正确 |

---

## 六、通过 scnet MCP 提交测试流程

### 6.1 提交流程

```text
1. 识别 Slurm 脚本模板与默认资源
   ↓
2. 读取被测文件，提取关键参数
   ↓
3. 检查是否已有测试文件
   ↓
4. 生成或补全测试脚本（仅填充模板占位符）
   ↓
5. 生成任务执行脚本
   ↓
6. 整理待上传文件清单
   ↓
7. 通过 scnet MCP 上传并提交测试任务
   ↓
8. 读取任务日志（.out / .err）
   ↓
9. 解析结果并输出测试报告
```

上述文件上传与测试任务提交必须通过自然语言指令触发，由智能体解析后调用 SCNet MCP 完成，禁止手动构造或绕过 MCP。

### 6.2 文件上传

根据测试用例和任务执行脚本模板，生成具体文件后，使用如下自然语言请求 scnet MCP 提交文件，其中的 slurm 配置参数从 `步骤 1：确认 Slurm 资源配置` 中提取：

```text
将当前任务涉及的所有文件提交到 SCnet 云平台：
files:
- `D:\\XX\\<name>.py` # 全路径
- `D:\\XX\\test_datapipe_<name>.py` # 全路径
- `D:\\XX\\test_<name>.sh` # 全路径
- 其它必要文件 # 全路径

slurm配置：
  - "region": "核心节点【分区一】",
  - "partition": "hpctest02",
  - "nodes": 1,
  - "dcu": 1,
  - "gpus_per_node": 1,
  - "cpus_per_task": 8,
  - "ntasks-per-node": 1,
  - "memory": "16GB"
```

### 6.3 测试任务提交

文件上传后，使用如下自然语言请求 scnet MCP 提交测试任务，其中的 slurm 配置参数从 `步骤 1：确认 Slurm 资源配置` 中提取：

```text
提交任务到 SCnet 云平台测试：

command: `bash test_<name>.sh`
queue: hpctest02
files:
- `D:\\XX\\<name>.py` # 全路径
- `D:\\XX\\test_datapipe_<name>.py` # 全路径
- `D:\\XX\\test_<name>.sh` # 全路径
- 其它必要文件 # 全路径

slurm配置：
- "region": "核心节点【分区一】",
- "partition": "hpctest02",
- "nodes": 1,
- "gpus_per_node": 1,
- "cpus_per_task": 8,
- "ntasks-per-node": 1,
- "dcu": 1,
- "memory": "16GB"
```

---

## 七、日志收集与结果判定

### 7.1 提交失败

如果 scnet 返回类似如下结果，说明任务没有成功提交：

```json
{
  "error_code": 422,
  "message": "任务提交失败: exitcode:1, errorinfo: sbatch: error: Batch job submission failed: Invalid account or account/partition combination specified\n",
  "data": null
}
```

此时应直接输出：

- 测试任务未成功提交
- 当前无法判断测试结果
- 请检查 scnet MCP 返回的错误信息
- 等待用户决定是否重试、修改资源配置或终止任务

### 7.2 运行成功后的日志解析

如果 scnet 上测试任务成功运行，则读取下载的 `*.err` 和 `*.out` 文件，并从日志中提取以下信息：

| 字段 | 内容 |
|------|------|
| 错误类型 | {{error_type}} |
| 错误消息 | {{error_message}} |
| 发生时间 | {{timestamp}} |
| 文件位置 | {{file_path}} |
| 行号 | {{line_number}} |
| 函数名 | {{function_name}} |
| 错误代码片段 | {{code_snippet}} |
| 堆栈信息 | {{traceback}} |

如果没有错误，则提取：

- 各测试项 PASS / FAIL 状态
- 数据集长度或可读取样本统计
- 样本结构与 DataLoader 集成结论
- 核心规则验证是否成功
- 总通过数与失败数

---

## 八、测试报告格式

```text
【onescience 测试报告】
========================================
项目：onescience
集群：scnet
时间：<timestamp>
测试类别：<Earth DataPipe>

测试项                          状态      耗时
------------------------------  ------    ----
实例化测试                      PASS      0.08s
单样本读取测试                  PASS      0.15s
样本结构测试                    PASS      0.09s
DataLoader 集成测试             PASS      0.21s
核心规则测试                    PASS      0.18s

总计：5 passed, 0 failed

错误信息（如有）：
----------------------------------------
测试项：<test_name>
错误类型：<ExceptionType>
堆栈信息：
  File "/path/to/datapipe.py", line 42, in __getitem__
    sample = self.reader[idx]
  KeyError: "variable temperature not found"

结论：全部通过 / 存在 N 个失败项，需修复
========================================
```

---

## 九、基于测试报告的后续优化流程

从测试报告中执行如下闭环：

1. 定位失败：根据错误信息，确定失败测试项及对应代码位置。
2. 分析原因：结合日志、代码与数据约束判断根本原因。
3. 提出修复方案：明确修改位置、修改方式及预期影响。
4. 实施修复：仅修复当前问题，不做无关重构。
5. 验证修复：重新运行失败用例；必要时补跑关联测试。
6. 记录结果：说明错误原因、修复方案、修改文件及影响范围。

---

## 十、执行约束

- 不修改被测代码时，只读取和分析。
- 不自动安装依赖；若缺少依赖，报告缺失包名。
- 优先使用最小可运行样本，不进行无必要的大规模数据加载。
- 若缺少真实数据、坐标文件或配置文件，先报告缺失项。
- Slurm 实际提交测试需在集群节点上执行。
- 错误堆栈信息必须完整保留，不截断。
- 若测试模板与真实返回结构存在冲突，优先保证模板主体不变，仅做最小必要占位填充。


path: onescience-debug/references/model_test.md
content: # SCNET onescience 模型测试工作流

你是一名负责对 onescience 模型代码进行全面测试的工程智能体。测试任务通过 scnet MCP 提交到 scnet 集群执行。

---

## 一、工作目标

目标是在**不改变测试模板**的前提下，完成模型测试的完整闭环：

1. 识别被测模型及运行所需信息。
2. 复用已有测试，或按固定模板生成测试脚本。
3. 生成任务执行脚本并提交到 scnet 集群。
4. 收集 `.out` / `.err` 日志，判断测试是否成功。
5. 输出结构化测试报告，并在需要时给出修复建议。

---

## 二、执行原则

- 优先复用已有测试文件，避免重复生成。
- 测试文件、执行脚本、提交信息三者必须一一对应。
- 本地负责生成与整理文件，实际计算与测试在 scnet 集群执行。
- 若任务未成功提交或未成功运行，必须先报告错误，再由用户决定后续操作。

---

## 三、测试内容识别流程

### 步骤 1：确认 Slurm 资源配置

如用户提供了 Slurm 资源配置，则使用用户提供的配置；否则使用默认配置：

- 区域（region）：默认 `"核心节点【分区一】"`
- 分区名（partition）：默认 `"hpctest02"`
- 申请节点数（nodes）：默认 `1`
- 每个节点的 GPU 数量（gpus_per_node）：默认 `1`
- 单个任务可使用的 CPU 数（cpus_per_task）：默认 `8`
- 每个节点执行的任务数（ntasks-per-node）：默认 `1`
- DCU 数（dcu 加速卡）：默认 `1`，且一定大于 0 
- 内存（memory）：默认 `16GB`

如果用户只提供了部分资源参数，则其余字段继续使用默认值。

### 步骤 2：读取被测文件并提取关键信息

读取模型代码，至少提取以下信息：

- 模型类名
- `__init__` 参数及默认值
- `forward` 的输入张量形式、输出形式、shape 约束
- 模型所在模块路径与导入方式
- 是否依赖额外配置文件、权重文件或辅助模块
- 是否存在特定数据维度要求，例如 `(B, C, H, W)`、`(B, T, C)`、多输入字典等

如果代码中还包含训练或推理线索，可一并记录：

- 数据集路径
- `batch_size`
- 数据格式（HDF5 / LMDB / FASTA / ERA5）
- 训练超参数（lr、epochs、optimizer 类型）
- 推理输入规格
- checkpoint 路径

### 步骤 3：检查是否已有测试文件

优先检查以下位置：

- 与模型文件同目录下的 `test_model_<name>.py`
- 与模型文件同目录下的 `test_<name>.py`
- 与模型文件同目录下的 `test_<name>.sh`
- 项目中的 `tests/` 目录
- 用户显式指定的输出目录

处理规则如下：

- 若存在已有测试：读取并识别缺失测试点，在已有测试基础上补充，不重复造轮子。
- 若不存在测试：根据下方固定模板自动生成测试用例。
- 若已有测试与当前模型实现不匹配：保留已有测试，并新增或修正当前测试文件，不直接覆盖用户已有逻辑，除非任务明确要求。

---

## 四、模型测试执行工作流

### 4.1 生成测试前的准备

在生成测试脚本前，先完成以下确认：

1. 明确模型主类名和导入路径。
2. 明确测试输入的最小可运行 shape。
3. 判断模型输出是单 Tensor、Tuple、Dict 还是多分支结果。
4. 判断模型是否支持 CPU / GPU 自动切换。
5. 识别是否需要将测试文件放在模型文件同目录下以保证相对导入可用。

### 4.2 生成或补全测试脚本

生成测试脚本时严格遵循以下规则：

1. 文件名默认使用 `test_model_<name>.py`。
2. 文件默认保存在模型文件所在目录。
3. 仅填充模板中的占位符，不改变模板结构。
4. 若模型路径为多级路径，如 `implements.pangu_afno.py`，则测试文件应从同目录下的实际文件导入，例如 `from pangu_afno import PanguAFNO`。
5. 若模型 `forward` 需要多输入，应在保持模板主体不变的前提下，仅按真实签名补全输入构造逻辑。

**测试用例模板**：

```python
# test_model_<name>.py
import torch
import pytest
import traceback

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

class TestModel<Name>:

    def setup_method(self):
        from <module> import <ModelClass>
        self.model = <ModelClass>(
            # 填入从代码中提取的默认参数
        ).to(device)

    def test_instantiation(self):
        """模型可用默认参数正常实例化"""
        assert self.model is not None
        total_params = sum(p.numel() for p in self.model.parameters())
        print(f"Total parameters: {total_params:,}")

    def test_forward_pass(self):
        """前向传播无异常"""
        self.model.eval()
        x = torch.randn(1, <C_in>, <H>, <W>).to(device)
        with torch.no_grad():
            try:
                out = self.model(x)
                assert out is not None
            except Exception:
                pytest.fail(f"Forward pass failed:\n{traceback.format_exc()}")

    def test_cpu_compatibility(self):
        """模型在 CPU 上可正常运行"""
        from onescience.models.<module> import <ModelClass>
        model_cpu = <ModelClass>().cpu().eval()
        x = torch.randn(1, <C_in>, <H>, <W>)
        with torch.no_grad():
            out = model_cpu(x)
        assert out is not None

    def test_gradient_flow(self):
        """梯度可正常反向传播"""
        self.model.train()
        x = torch.randn(1, <C_in>, <H>, <W>).to(device).requires_grad_(True)
        out = self.model(x)
        loss = out.mean() if not isinstance(out, tuple) else out[0].mean()
        try:
            loss.backward()
        except Exception:
            pytest.fail(f"Backward failed:\n{traceback.format_exc()}")

if __name__ == "__main__":
    pytest.main([__file__, "-v", "--tb=long"])
```

在生成模型测试用例的时候，默认测试用例文件与模型文件在同一目录下，且文件名格式为 `test_model_<name>.py`，同时保存至模型文件所在目录。

针对如 `implements.pangu_afno.py` 这种多级路径的模型生成测试用例时，需要从同目录下的 `pangu_afno.py` 中导入待测试的类 `PanguAFNO`。示例代码如下：

```python
import torch
import pytest
import traceback

# 设备选择
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

class TestModelPanguAFNO:
    def setup_method(self):
        # 从同一目录下的 pangu_afno.py 中导入 PanguAFNO 类
        from pangu_afno import PanguAFNO
        ...
```

### 4.3 生成任务执行脚本

在测试脚本生成完成后，继续生成执行脚本：

1. 文件名默认使用 `test_<name>.sh`。
2. 与 `test_model_<name>.py` 保存在同一目录。
3. 脚本中负责初始化 module 环境、激活 conda 环境并执行测试命令。
4. 若模型测试文件名为 `test_model_<name>.py`，执行命令需与实际文件名保持一致。

生成任务执行脚本模板：

```bash
#!/bin/bash

# 初始化 module 环境（关键）
source /etc/profile

source /etc/profile.d/modules.sh

# 加载dtk和测试环境
module load sghpcdas/25.6 

source ~/.bashrc

module load sghpc-mpi-gcc/26.3

conda activate onescience311

source $ROCM_PATH/cuda/env.sh

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$CONDA_PREFIX/lib

python test_<name>.py 
```

生成的测试脚本和任务执行脚本均保存到与 `<name>.sh` 文件的同目录下。

建议在实际生成时做一次一致性检查：

- 脚本中的 `python xxx.py` 与真实测试文件名一致。
- 提交文件列表中的路径与磁盘真实路径一致。
- 若测试文件实际名为 `test_model_<name>.py`，则执行脚本中也应同步调整为该文件名。

### 4.4 生成提交文件清单

在提交到 scnet 之前，整理完整文件清单，通常包括：

- 被测模型文件
- `test_model_<name>.py`
- `test_<name>.sh`
- 其它必要文件，例如：
  - 配置文件
  - 依赖模块
  - 权重文件
  - 自定义算子或辅助脚本

提交时必须给出所有文件的**全路径**。

### 4.5 通过 scnet MCP 提交任务

提交动作分为两类：

1. 仅上传文件。
2. 上传文件并立即发起测试任务。

提交时需明确：

- `command`
- `queue` / `partition`
- 资源配置
- 待上传文件列表

### 4.6 收集日志并判定结果

任务提交后，等待任务结束并读取日志：

- 若 scnet MCP 返回提交失败，则直接报告“任务未成功提交”，不进入结果分析。
- 若任务提交成功但运行失败，则从 `.err` 和 `.out` 中提取错误信息。
- 若任务运行成功，则汇总各测试项结果并生成测试报告。

---

## 五、测试详细规范

**测试目标**：验证模型可实例化、前向传播正确、输出 shape 符合预期。

**测试矩阵**：

| 测试项 | 验证内容 |
|--------|---------|
| 实例化测试 | 默认参数构造，无异常 |
| 前向传播测试 | 随机输入，无异常 |
| 设备兼容性 | CPU + GPU（若可用）均可运行 |
| 梯度流测试 | loss.backward() 无异常 |
| 参数量统计 | 输出总参数量，供参考 |

---

## 六、通过 scnet MCP 提交测试流程

### 6.1 提交流程

```text
1. 识别 Slurm 脚本模板与默认资源
   ↓
2. 读取被测文件，提取关键参数
   ↓
3. 检查是否已有测试文件
   ↓
4. 生成或补全测试脚本（仅填充模板占位符）
   ↓
5. 生成任务执行脚本
   ↓
6. 整理待上传文件清单
   ↓
7. 通过 scnet MCP 上传并提交测试任务
   ↓
8. 读取任务日志（.out / .err）
   ↓
9. 解析结果并输出测试报告
```
上述文件上传与测试任务提交必须通过自然语言指令触发，由智能体解析后调用 SCNet MCP 完成，禁止手动构造或绕过 MCP。

### 6.2 文件上传

根据测试用例和任务执行脚本模板，生成具体文件后，使用如下自然语言请求scnet mcp提交文件，其中的slurm配置参数从 `步骤 1：确认 Slurm 资源配置` 中提取：

```text
将当前任务涉及的所有文件提交到 SCnet 云平台：
files:
- `D:\\XX\\<name>.py` # 全路径
- `D:\\XX\\test_model_<name>.py` # 全路径
- `D:\\XX\\test_<name>.sh` # 全路径
- 其它必要文件 # 全路径

slurm配置： 
  - "region": "核心节点【分区一】",
  - "partition": "hpctest02",
  - "nodes": 1,
  - "dcu": 1,
  - "gpus_per_node": 1,
  - "cpus_per_task": 8,
  - "ntasks-per-node": 1,
  - "memory": "16GB"
}
```

### 6.3 测试任务提交

文件上传后，使用如下自然语言请求scnet mcp提交测试任务，其中的slurm配置参数从 `步骤 1：确认 Slurm 资源配置` 中提取：

```text
提交任务到 SCnet 云平台测试：

command: `bash test_<name>.sh`
queue: hpctest02
files:
- `D:\\XX\\<name>.py` # 全路径
- `D:\\XX\\test_model_<name>.py` # 全路径
- `D:\\XX\\test_<name>.sh` # 全路径
- 其它必要文件 # 全路径

slurm配置：
- "region": "核心节点【分区一】",
- "partition": "hpctest02",
- "nodes": 1,
- "gpus_per_node": 1,
- "cpus_per_task": 8,
- "ntasks-per-node": 1,
- "dcu": 1,
- "memory": "16GB"
```
---

## 七、日志收集与结果判定

### 7.1 提交失败

如果 scnet 返回类似如下结果，说明任务没有成功提交：

```json
{
  "error_code": 422,
  "message": "任务提交失败: exitcode:1, errorinfo: sbatch: error: Batch job submission failed: Invalid account or account/partition combination specified\n",
  "data": null
}
```

此时应直接输出：

- 测试任务未成功提交
- 当前无法判断测试结果
- 请检查 scnet MCP 返回的错误信息
- 等待用户决定是否重试、修改资源配置或终止任务

### 7.2 运行成功后的日志解析

如果 scnet 上测试任务成功运行，则读取下载的 `*.err` 和 `*.out` 文件，并从日志中提取以下信息：

| 字段 | 内容 |
|------|------|
| 错误类型 | {{error_type}} |
| 错误消息 | {{error_message}} |
| 发生时间 | {{timestamp}} |
| 文件位置 | {{file_path}} |
| 行号 | {{line_number}} |
| 函数名 | {{function_name}} |
| 错误代码片段 | {{code_snippet}} |
| 堆栈信息 | {{traceback}} |

如果没有错误，则提取：

- 各测试项 PASS / FAIL 状态
- 参数量统计
- CPU / GPU 兼容性结论
- 前向传播和梯度回传是否成功
- 总通过数与失败数

---

## 八、测试报告格式

```text
【onescience 测试报告】
========================================
项目：onescience
集群：scnet
时间：<timestamp>
测试类别：<A模型定义 / B数据Pipeline / C训练 / D推理 / E Slurm>

测试项                          状态      耗时
------------------------------  ------    ----
实例化测试                      PASS      0.12s
前向传播测试                    PASS      0.45s
CPU 兼容性测试                  PASS      1.20s
GPU 兼容性测试                  PASS      0.38s
梯度流测试                      PASS      0.52s
Checkpoint 保存/加载            PASS      0.31s

总计：7 passed, 0 failed

错误信息（如有）：
----------------------------------------
测试项：<test_name>
错误类型：<ExceptionType>
堆栈信息：
  File "/path/to/model.py", line 42, in forward
    out = self.layer(x)
  RuntimeError: Expected input shape (B, 72, 721, 1440), got (1, 64, 256, 256)

结论：全部通过 ✓ / 存在 N 个失败项，需修复
========================================
```

---

## 九、基于测试报告的后续优化流程

从测试报告中执行如下闭环：

1. 定位失败：根据错误信息，确定失败的测试用例及其对应的代码模块、函数或类。
2. 分析原因：结合错误类型、上下文代码与日志，判断根本原因。
3. 提出修复方案：明确修改位置、修改方式及预期影响。
4. 实施修复：仅修复当前问题，不做无关重构，尽量保持原有接口和行为契约。
5. 验证修复：重新运行失败用例；必要时补跑关联测试，防止回归。
6. 记录结果：说明错误原因、修复方案、修改文件及影响范围。

---

## 十、执行约束

- 不修改被测代码时，只读取和分析。
- 不自动安装依赖；若缺少依赖，报告缺失包名。
- 训练测试默认只运行 1-5 步，不执行完整训练。
- 无 GPU 时自动回退 CPU，不报错。
- Slurm 实际提交测试需在集群节点上执行。
- 错误堆栈信息必须完整保留，不截断。
- 若测试模板与真实模型签名存在冲突，优先保证模板主体不变，仅做最小必要占位填充。


path: onescience-coder/references/first_round_guide.md
content: # First Round Guide

请先参考 `./references/workflow.md` 中定义的工作流执行任务。

当前阶段是**第一轮需求整理阶段**，不是最终代码生成阶段。

执行要求：

1. 先理解用户的自然语言需求
2. 若用户明确提到已有模型，优先读取 `./assets/models/` 中对应模型卡
3. 再读取 `./assets/contracts/` 中相关内容
4. 必要时再读取 `./onescience/` 中对应源码
5. 第一轮**不要直接输出最终代码**
6. 先输出一份“详细执行信息”或“结构化规格说明”，供用户确认

这份“详细执行信息”至少要包含：

- 任务目标
- 输入定义
- 输出定义
- 保持不变的部分
- 计划修改的部分
- 推荐复用的 OneScience 组件
- 主干结构建议
- 主要 shape 变化
- 需要用户确认的关键点

在输出结束前，还必须追加一个固定的确认区块，至少包括：

- `## 待确认摘要`
- `## 自检结果`
- `## 用户操作`

额外约束：

- 命名尽量统一使用 `Batch`、`TimeSteps`、`PressureLevels`、`Height`、`Width`
- 如果 `onescience` 里已有合适模块，优先复用
- 如果任务是模型内模块替换，先看模型卡，再看相关组件契约
- 如果需要判断“直接替换是否可行”，第一轮必须明确说明：
  - 能否直接替换
  - 若不能，最小可实现改造路径是什么
  - 哪些部分保持原模型不变
  - 需要修改哪些文件

输出结束时，请明确告诉用户下述信息，同时将本次思考中进行总结，并整理为文档输出：

- 如果用户确认这份详细执行信息，你再继续生成完整代码


path: onescience-coder/references/second_round_guide.md
content: # Second Round Guide

请先参考 `./references/workflow.md` 中定义的工作流执行任务。

当前阶段是**第二轮代码生成阶段**。

执行要求：

1. 不要重新输出整份需求分析
2. 直接基于已确认的“详细执行信息”继续实现
3. 优先复用 `./onescience/` 中已有模块
4. 先读取相关 `assets/models/` / `assets/contracts/`
5. 如契约信息不足，再回到对应源码锚点补充确认
6. 只修改与当前任务直接相关的文件

设备与测试要求：

7. 若生成测试代码、样例代码或运行验证代码，默认优先使用 GPU
8. 优先采用：
   - `device = torch.device("cuda" if torch.cuda.is_available() else "cpu")`
9. 再将模型和输入张量移动到 `device`
10. 若环境中没有 GPU，应自动回退到 CPU，而不是写死为仅支持 CUDA

输出时应包含：

- 最终完整代码
- 复用了哪些 OneScience 组件
- 主要 shape 变化
- 新增桥接模块或新增调用层改动说明
- 如有必要，补充最小验证建议

如果用户未额外指定保存路径，则默认将生成文件保存到对应的 `case` 目录下。

如果已确认的设计中仍然存在会导致代码无法实现的关键冲突，请先用一个简短的 `md` 区块指出问题；否则直接开始生成代码。


path: onescience-coder/references/workflow.md
content: # OneScience 模型、组件与数据工作流

你是一名基于 OneScience 写代码的工程智能体。

## 用户交互主流程

本工作流默认采用“两阶段交互”，目标是尽量减少用户一开始需要编写的提示词长度。

### 阶段 1：接受用户原始 query

用户通常只会给出一个简短自然语言需求，例如：

- 帮我写一个全球短期预报模型
- 我想做一个同时利用地表和高空变量的天气预报模型
- 帮我先设计一个轻量级全球气象预报网络

在这个阶段，**不要直接开始输出最终代码**。

你应该先做的事情是：

1. 理解用户任务目标
2. 先读取 `./assets/contracts/component_index.md`
3. 再读取 `./assets/contracts/naming_convention.md`
4. 若任务涉及数据读取、年份选择、变量组织或 dataloader，读取 `./assets/datapipes/datapipe_index.md`
5. 若用户提供的是未登记的新数据集 README、样例文件或数据目录说明，再读取 `./assets/task/new_dataset_workflow.md`
6. 按需读取相关 `./assets/contracts/` 与 `./assets/datapipes/` 文档
7. 必要时读取 `./onescience/` 中的源码锚点
8. 先生成一份“详细执行信息”或“结构化规格说明”
9. 把这份规格说明交给用户确认

这份“详细执行信息”至少应包含：

- 任务目标
- 输入输出定义
- 变量拆分方式
- 推荐复用的 OneScience 组件
- 推荐复用的 datapipe
- 主干特征提取模块
- 主要 shape 变化
- 代码实现范围
- 若涉及数据适配，补充接口兼容性与桥接方案
- 需要用户确认的关键点

在第一阶段输出结束前，必须追加一个固定的确认区块，不要省略。

### 阶段 2：用户确认后再生成代码

当用户确认这份“详细执行信息”后，再进入代码生成阶段。

这一轮可以由用户用以下两种方式继续：

1. 直接回复：
   - 我确认这份设计，请继续生成代码
2. 或者把你刚才输出的“详细执行信息”作为新的提示词再次提交

无论用户采用哪种方式，只要确认已经完成，你都应：

1. 基于确认后的规格继续实现
2. 优先复用已有推荐组件
3. 在契约不足时再读取源码
4. 输出完整代码和必要说明

### 为什么默认采用两阶段

- 用户不需要一开始就写很长的专业提示词
- 智能体先把模糊需求翻译成清晰规格，再写代码，稳定性更高
- 用户可以在写代码前修改变量数、结构复杂度、组件复用范围

## 核心目标

- 尽量少让用户阅读 `./onescience/` 源码
- 若用户明确提到某个现有模型，优先使用 `./assets/models/` 中的模型卡
- 优先使用 `./assets/contracts/` 中的组件契约完成任务
- 若涉及数据流程，优先使用 `./assets/datapipes/` 中的数据卡
- 若用户提供的是未登记的新数据集，优先结合 `./references/new_dataset_workflow.md` 完成数据接口设计
- 只有当契约信息不足时，才回到源码锚点读取实现

## 允许优先使用的资源

- `./assets/models/*.md`
- `./assets/contracts/*.md`
- `./assets/datapipes/*.md`
- `./references/new_dataset_workflow.md`
- `./README.md`
- 用户明确指定的文件
- `./onescience/` 中与契约卡片对应的源码锚点

## 执行顺序

1. 先判断当前是“用户原始 query 阶段”还是“确认后代码生成阶段”
2. 若是原始 query 阶段，先输出结构化规格说明，不直接输出最终代码
3. 若是确认后代码生成阶段，再进入正式实现
4. 若用户提供的是未登记的新数据集 README、样例文件或数据目录说明，先读取 `./references/new_dataset_workflow.md`
5. 若用户明确提到模型名，先读取 `./assets/models/model_index.md`
6. 按需读取对应模型卡
7. 识别任务涉及的组件族
8. 若涉及数据流程，先识别任务涉及的 datapipe
9. 读取对应契约卡片与 datapipe 卡片
10. 提取以下信息
   - 注册入口
   - 注册名
   - 构造参数
   - 输入输出 shape
   - 典型调用位置
   - 风险点
11. 若 datapipe 卡存在，也提取以下信息
   - 数据目录结构
   - 年份或样本划分方式
   - 变量选择方式
   - 样本返回格式
   - dataloader 行为
12. 若模型卡存在，也提取以下信息
   - 输入输出变量组织
   - 主干结构
   - 主要 shape 变化
   - 常见修改点
13. 先基于模型卡、契约卡与 datapipe 卡生成规格或代码
14. 若以下信息缺失，再读取源码
   - 真实默认值
   - 参数边界检查
   - forward 内部张量重排细节
   - 与当前目标文件直接耦合的实现细节

说明：

- 模型卡中的源码锚点统一使用 `./onescience/...` 相对路径
- 契约卡片中的源码锚点统一使用 `./onescience/...` 相对路径
- 默认假设 `oneskills` 与 `onescience` 位于同一工作目录下

## 模型卡使用原则

- 若用户明确提到现有模型名，优先先看模型卡，再看组件契约
- 若任务是“替换某个模型中的某个模块”，先看模型卡确定调用链路，再看目标组件契约
- 若任务是“把 A 模型的某个模块替换到 B 模型”，先读两个模型卡，再读涉及的组件契约
- 若用户没有提模型名，且任务只是组件组合或模块开发，可以直接从组件索引开始

## 组件复用原则

- 已有推荐组件时，优先使用推荐组件
- 不要默认继续依赖旧的 2D/3D 分裂实现
- 若任务是改造现有模型，先替换调用层，再考虑是否需要改注册层
- 若任务只是生成建议，不直接扩散修改到无关文件
- 若任务涉及主干特征提取、token 融合、中间层建模或 trunk 设计，先检查 `fuser` 模块族，再考虑底层 `transformer_layers`
- 若 `./assets/contracts/` 中已经存在可直接复用的 `fuser` 契约，默认不要直接拼装 encoder/decoder 风格的底层 Transformer block

## 数据与接口兼容性原则

当任务要求“沿用某个现有 datapipe、example 或训练流程”，去适配另一套数据或模型时，先做兼容性判断，不要默认它们可以直接互通。

第一轮规格中至少要检查：

- 样本返回格式是否兼容
- `DataLoader` 的 batch 结构是否兼容
- 训练脚本的 batch 解包方式是否兼容
- 模型期望的输入输出协议是否兼容
- 配置文件里的关键字段是否能直接复用

如果不能直接兼容，第一轮必须明确写出：

- 当前不兼容的原因
- 最小可行桥接路径
- 计划在 datapipe、配置层还是调用层做适配

默认优先最小改动路径：

- 先保持现有模型主体不变
- 先保持现有训练主循环不变
- 优先在 datapipe、配置或调用层完成桥接

## 新数据集接入补充规则

当用户提供的是 `assets/datapipes/` 尚未登记的新数据集时，额外执行以下规则：

1. 第一轮必须明确当前任务是 `datapipe-only` 还是 `full-adaptation`
2. 第一轮必须明确写出：
   - 参考 datapipe
   - 参考 example
   - 新 datapipe 的文件名和类名
   - 数据集的输入、输出和划分方式
   - 是 `case-local` 还是主库集成
3. 生成新 datapipe 时，优先遵守：
   - `<DatasetName>.py`
   - `<DatasetName>Dataset`
   - `<DatasetName>Datapipe`
4. 若用户进一步要求复用已有模型训练流程，优先保持：
   - 模型类不改
   - 训练主循环尽量不改
   - 配置结构尽量对齐参考 example
5. 默认只在以下位置做最小改动：
   - datapipe 导入
   - 数据配置项
   - 输入输出维度
   - 与旧数据集强耦合的 loss / metrics / inference 逻辑
6. 如果用户没有明确要求改主库，默认把新 datapipe 与相关训练文件生成到目标 case 目录，而不是直接修改 `./onescience/src/`

## 天气预测任务补充规则

当任务是全球天气预报、短期预报、地表加高空联合建模等场景时，额外执行以下检查：

1. 先判断主干是否应复用现有 `fuser` 组件
2. 若输入同时包含 surface 与 upper-air，先判断是否应组织成统一 token 主干，而不是默认拆成两个完全独立的 encoder/decoder
3. 第一轮规格中必须明确写出“主干特征提取模块”的调用入口、`style`、输入 token 形态、是否允许直接使用底层 block
4. 第二轮生成代码前，若第一轮规格未明确主干组件归属，应先补全这一点，再继续写代码

补充说明：

- 这条规则是为了避免天气类任务默认退化成通用 encoder/decoder 模板
- 若确认没有合适的 `fuser` 组件，再退回到底层 block 组合

## 输出要求

输出给用户时优先说明：

- 命中了哪个现有模型或最接近哪个现有模型
- 用了哪些推荐组件
- 为什么这样替换
- 需要修改哪些调用点
- 哪些地方仍需回到源码确认

不要把整段源码解释直接堆给用户，除非用户明确要求。

## 执行限制

默认只生成代码、配置和必要说明，不主动执行与本地环境相关的操作。

具体要求：

- 不主动安装依赖或新增本地包
- 不主动运行训练、推理、测试或其它本地脚本
- 不主动下载数据或执行联网获取资源的操作
- 若确实需要运行本地验证，必须先明确请求用户确认
- 若用户未明确要求运行，只输出代码、使用说明和必要的验证建议，由用户自行测试

## 设备与测试偏好

当任务进入代码生成、样例编写或运行验证阶段时，默认优先使用 GPU。

推荐做法：

- 优先使用：
  - `device = torch.device("cuda" if torch.cuda.is_available() else "cpu")`
- 再将模型和测试张量移动到 `device`
- 若只是非常短的临时示例，也可以直接使用 `.cuda()`，但默认更推荐可自动回退到 CPU 的写法

额外要求：

- 如果提供测试代码、样例代码或最小验证脚本，默认应体现 GPU 优先的设备选择
- 若环境中没有可用 GPU，允许自动回退到 CPU，不应因此让代码无法运行

## 第一轮推荐输出格式

当用户给的是一句自然语言 query 时，优先输出：

```md
【需求理解】
- 任务目标：
- 推荐模型范围：
- 推荐复用组件：

【详细执行信息】
- 输入定义：
- 输出定义：
- 变量组织方式：
- 主干结构建议：
- 主干特征提取模块：
- 主要 shape 变化：
- 若涉及数据适配：接口兼容性与桥接方案：
- 需要确认的点：

## 待确认摘要
- 任务目标：
- 输入定义：
- 输出定义：
- 变量组织方式：
- 推荐复用组件：
- 主干结构：

## 自检结果
- [ ] 已区分主要数据分支
- [ ] 已说明输入输出
- [ ] 已给出推荐复用组件
- [ ] 已明确主干特征提取模块来自哪个组件族
- [ ] 若涉及数据适配，已说明接口兼容性与桥接路径
- [ ] 已说明主干结构
- [ ] 已给出主要 shape 变化
- [ ] 仍需用户确认的点：

## 用户操作
请直接回复以下任一选项：
1. `确认，继续生成代码`
2. `修改后再继续：<你的修改意见>`
```

## 第二轮推荐输出格式

当用户已经确认详细执行信息后，再输出：

- 完整代码
- 复用组件说明
- shape 变化说明
- 如有必要，补充验证建议

默认不要自行运行代码；除非用户明确要求，否则只生成结果并说明用户后续如何验证。

如果用户提供的是某个 `case` 文件中的任务，且未额外指定保存路径，则默认将生成文件保存到对应的 `case` 目录下。


path: onescience-coder/assets/models/fengwu.md
content: # Model Card: FengWu

## 基本信息

- 模型名：`FengWu`
- 任务类型：`多变量中期天气预报 / 多分支二维场联合建模`
- 当前状态：`stable`
- 主实现文件：`./onescience/src/onescience/models/fengwu/fengwu.py`

## 模型定位

FengWu 是一个面向多变量全球天气预报的多分支模型，适合分别编码不同变量族，再在中分辨率层面做跨变量三维融合。

补充说明：

- 输入不是单一路径，而是 surface、Z、R、U、V、T 六个变量分支
- 每个分支先独立编码，再通过统一三维 fuser 做跨变量融合
- 它最重要的结构特点是：多分支 2D encoder/decoder，加一个中分辨率 3D fuser

## 输入定义

- 输入 shape：
  - surface: `(Batch, 4, Height, Width)`
  - z/r/u/v/t: `(Batch, pressure_level, Height, Width)`
- 输入变量组织：
  - surface 分支单独编码
  - 五个高空变量分支分别编码

## 输出定义

- 输出 shape：
  - surface: `(Batch, 4, Height, Width)`
  - z/r/u/v/t: `(Batch, pressure_level, Height, Width)`
- 输出变量组织：
  - 六个变量分支分别解码并返回

## 主干结构

- 6 个 `OneEncoder(style="FengWuEncoder")`
  - 分别编码 surface、Z、R、U、V、T
- 中分辨率特征按变量轴拼接成三维网格
- `OneFuser(style="FengWuFuser")`
  - 在 `(Variables, Height, Width)` 三维网格上融合
- 6 个 `OneDecoder(style="FengWuDecoder")`
  - 分别恢复各变量分支

## 主要依赖组件

- `FengWuEncoder`
- `FengWuFuser`
- `FengWuDecoder`
- `PanguEmbedding`
- `PanguDownSample`
- `PanguUpSample`
- `PanguPatchRecovery`

## 主要 Shape 变化

- surface 输入：`(Batch, 4, 721, 1440)`
- 高空变量输入：`(Batch, 37, 721, 1440)`
- 单分支 encoder 中间输出：`(Batch, 91 * 180, 384)`
- 单分支 skip 输出：`(Batch, 181, 360, 192)`
- 6 分支拼接后：`(Batch, 6, 91 * 180, 384)`
- 展平后 fuser 输入：`(Batch, 6 * 91 * 180, 384)`
- fuser 输出后再切回 6 个分支
- 单分支 decoder 输出：
  - surface: `(Batch, 4, 721, 1440)`
  - 高空变量: `(Batch, 37, 721, 1440)`

## 默认关键参数

- `img_size=(721, 1440)`
- `pressure_level=37`
- `embed_dim=192`
- `patch_size=(4, 4)`
- `num_heads=(6, 12, 12, 6)`
- `window_size=(2, 6, 12)`

## 常见修改点

- 修改 `pressure_level` 时，要同步检查 5 个高空分支的输入输出通道数
- 修改 `patch_size` 时，要同步检查 encoder / decoder 的 patch 网格尺寸和 fuser 输入分辨率
- 修改分支数时，要同步检查 `FengWuFuser` 的 `input_resolution[0]`

## 风险点

- `FengWuEncoder` / `FengWuDecoder` 处理的是单个变量分支，不要误改成所有变量共用单一路径
- `FengWuFuser` 的第一维 `Variables` 指的是分支数，不是时间步或气压层
- decoder 中 `num_heads` 的高低分辨率顺序需要与调用层保持一致

## 推荐检索顺序

1. 先看本模型卡
2. 再看 `FengWuEncoder`、`FengWuFuser`、`FengWuDecoder`
3. 若需要理解 encoder / decoder 内部，再看 `PanguEmbedding`、`PanguDownSample`、`PanguUpSample`、`PanguPatchRecovery`
4. 若仍有疑问，再回到模型源码

## 组件契约入口

- `./contracts/fengwuencoder.md`
- `./contracts/fengwufuser.md`
- `./contracts/fengwudecoder.md`
- `./contracts/panguembedding.md`
- `./contracts/pangudownsample.md`
- `./contracts/panguupsample.md`
- `./contracts/pangupatchrecovery.md`

## 源码锚点

- `./onescience/src/onescience/models/fengwu/fengwu.py`
- `./onescience/src/onescience/modules/encoder/fengwuencoder.py`
- `./onescience/src/onescience/modules/fuser/fengwufuser.py`
- `./onescience/src/onescience/modules/decoder/fengwudecoder.py`


path: onescience-coder/assets/models/fno.md
content: # Model Card: FNO

## 基本信息

- 模型名：`FNO`
- 任务类型：`CFD / steady-or-dynamic operator learning on structured grids`
- 当前状态：`stable`
- 主实现文件：`./onescience/src/onescience/models/cfd_benchmark/FNO.py`

## 模型定位

FNO 是当前 `CFD_Benchmark` 体系里最基础的谱算子基线，适合在规则网格上做全局场到全局场的算子学习，也支持带几何投影的 2D 非结构分支。

补充说明：

- 结构化分支最常见于 `structured_2D` 稳态代理任务
- 输入不是图像张量 `(Batch, Channels, Height, Width)`，而是位置 `x` 与可选场特征 `fx` 的二元组织
- 它最重要的结构特点是：前面先用 MLP 做点级特征映射，中间用多层谱卷积做全局建模，最后再映射回目标变量

## 输入定义

- 输入 shape：`x -> (Batch, NumPoints, space_dim)`，`fx -> (Batch, NumPoints, fun_dim)` 或 `None`
- 输入变量组织：
  - `x`
    - 位置或规则网格坐标
  - `fx`
    - 与每个位置对齐的输入物理特征；若 `fun_dim=0`，可为空

## 输出定义

- 输出 shape：`(Batch, NumPoints, out_dim)`
- 输出变量组织：
  - 每个网格点或采样点对应 `out_dim` 个目标变量

## 主干结构

- `OneMlp(style="StandardMLP")`
  - 将 `x` 与 `fx` 拼接后的点级特征映射到 `n_hidden`
- 可选 `OneFourier(style="GeoSpectralConv2d")`
  - 在非结构 2D 分支里做点集到潜在规则网格的投影
- 4 层 `OneFourier(style="FNOSpectralConv*d") + 1x1 Conv` 残差块
- `fc1 + fc2`
  - 将隐藏特征映射到 `out_dim`

## 主要依赖组件

- `OneMlp`
- `OneFourier`

## 主要 Shape 变化

- 输入拼接后：`(Batch, NumPoints, space_dim + fun_dim)` 或统一位置编码后的对应维度
- 预处理后：`(Batch, NumPoints, n_hidden)`
- 结构网格分支重排后：`(Batch, n_hidden, *shapelist)`
- 谱卷积主干后再展平：`(Batch, NumPoints, n_hidden)`
- 输出投影后：`(Batch, NumPoints, out_dim)`

## 默认关键参数

- `geotype="structured_2D"` 或 `unstructured`
- `space_dim=2`
- `fun_dim=0` 到若干输入通道
- `out_dim=1` 到若干目标通道
- `n_hidden=32` 或 `64`
- `modes=12`
- `unified_pos=0`
- `ref=8`

说明：

- `shapelist` 不是模型内部固定值，而是 dataloader 返回给训练入口的网格尺寸
- 若把 DeepCFD 规则网格数据接到 FNO，常见适配是 `space_dim=2`、`fun_dim=3`、`out_dim=3`

## 常见修改点

- 根据 datapipe 返回的网格尺寸同步修改 `shapelist`
- 根据输入物理量数量同步修改 `fun_dim`
- 根据目标变量数量同步修改 `out_dim`
- 若从 `DeepCFDDatapipe` 迁移过来，需要新增 adapter，把图像张量改造成 `(pos, fx, y)` 三元组

## 风险点

- FNO 不能直接消费 `DeepCFDDatapipe` 返回的 `{"x": ..., "y": ...}` batch
- `shapelist` 一旦和真实规则网格尺寸不一致，`reshape` 会直接失败
- `unified_pos=1` 会改变预处理层输入维度，不能只改配置不改数据理解
- 当前非结构分支围绕 `GeoSpectralConv2d` 组织，默认更偏向 2D 几何任务

## 推荐检索顺序

1. 先看本模型卡
2. 再看 `OneFourier`
3. 再看 `OneMlp`
4. 若仍不足，再回到 `CFD_Benchmark` 训练入口

## 组件契约入口

- `./contracts/onefourier.md`
- `./contracts/onemlp.md`

## 源码锚点

- `./onescience/src/onescience/models/cfd_benchmark/FNO.py`
- `./onescience/src/onescience/modules/fourier/onefourier.py`
- `./onescience/examples/cfd/CFD_Benchmark/exp/exp_steady.py`


path: onescience-coder/assets/models/fourcastnet.md
content: # Model Card: FourCastNet

## 基本信息

- 模型名：`FourCastNet`
- 任务类型：`全球天气预报 / 单时刻二维场建模`
- 当前状态：`stable`
- 主实现文件：`./onescience/src/onescience/models/fourcastnet/fourcastnet.py`

## 模型定位

FourCastNet 是一个面向二维全球气象场的 patch-grid AFNO 模型，适合处理单时刻的二维变量场。

补充说明：

- 输入不包含 `PressureLevels` 维度
- 主干不是 Transformer attention，而是 AFNO 频域混合加逐位置前馈网络
- 它最重要的结构特点是：先做 2D patch embedding，再把 token 序列恢复成二维 patch 网格进入 trunk

## 输入定义

- 输入 shape：`(Batch, Channels, Height, Width)`
- 输入变量组织：
  - 单一路径二维气象场
  - 所有变量都直接作为二维通道输入

## 输出定义

- 输出 shape：`(Batch, out_chans, Height, Width)`
- 输出变量组织：
  - 输出变量数通常与输入变量数一致
  - 最终通过线性头按 patch 重排回原分辨率

## 主干结构

- `OneEmbedding(style="FourCastNetEmbedding")`
  - 将二维气象场映射成 patch token 序列
- 加上可学习位置编码
- token 序列恢复成二维 patch 网格
- 多层 `OneFuser(style="FourCastNetFuser")`
  - 每层内部是 `OneAFNO(style="FourCastNetAFNO2D") + OneFC(style="FourCastNetFC")`
- 通过线性 head 预测 patch 输出
- 用 `einops.rearrange` 恢复为 `(Batch, out_chans, Height, Width)`

## 主要依赖组件

- `FourCastNetEmbedding`
- `FourCastNetFuser`
- `FourCastNetAFNO2D`
- `FourCastNetFC`

## 主要 Shape 变化

- 输入：`(Batch, Channels, 720, 1440)`
- patch embedding 输出：`(Batch, 16200, 768)`
- 恢复成 patch 网格：`(Batch, 90, 180, 768)`
- trunk 输出：`(Batch, 90, 180, 768)`
- head 输出：`(Batch, 90, 180, out_chans * 8 * 8)`
- 重排后输出：`(Batch, out_chans, 720, 1440)`

## 默认关键参数

- `img_size=(720, 1440)`
- `patch_size=(8, 8)`
- `in_chans=19`
- `out_chans=19`
- `embed_dim=768`
- `depth=12`
- `mlp_ratio=4.0`
- `num_blocks=8`
- `sparsity_threshold=0.01`
- `hard_thresholding_fraction=1.0`

## 常见修改点

- 修改 `img_size` 或 `patch_size` 时，要同步检查 patch grid 大小、位置编码长度和最终重排逻辑
- 修改 `embed_dim` 时，要同步检查 `FourCastNetFuser`、`FourCastNetAFNO2D` 和 `FourCastNetFC` 的维度对齐
- 修改 `depth` 或 `drop_path_rate` 时，要同步检查 block 堆叠数和按层 drop path 分配

## 风险点

- `FourCastNetEmbedding` 输出是展平 token 序列，但 `FourCastNetFuser` 输入是二维 patch 网格，调用层必须做 reshape
- `FourCastNetAFNO2D` 中的 `Height` 和 `Width` 指的是 patch 网格尺寸，不是原始图像分辨率
- `embed_dim` 必须能被 AFNO 的 `num_blocks` 整除

## 推荐检索顺序

1. 先看本模型卡
2. 再看 `FourCastNetEmbedding` 和 `FourCastNetFuser`
3. 若需要理解 trunk 内部，再看 `FourCastNetAFNO2D` 和 `FourCastNetFC`
4. 若仍有疑问，再回到模型源码

## 组件契约入口

- `./contracts/fourcastnetembedding.md`
- `./contracts/fourcastnetfuser.md`
- `./contracts/fourcastnetafno.md`
- `./contracts/fourcastnetfc.md`

## 源码锚点

- `./onescience/src/onescience/models/fourcastnet/fourcastnet.py`
- `./onescience/src/onescience/modules/fuser/fourcastnetfuser.py`
- `./onescience/src/onescience/modules/afno/fourcastnetafno.py`
- `./onescience/src/onescience/modules/embedding/fourcastnetembedding.py`


path: onescience-coder/assets/models/fuxi.md
content: # Model Card: Fuxi

## 基本信息

- 模型名：`Fuxi`
- 任务类型：`多时间步二维气象场预报`
- 当前状态：`stable`
- 主实现文件：`./onescience/src/onescience/models/fuxi/fuxi.py`

## 模型定位

Fuxi 是一个面向多时间步二维气象场的时空 patch 模型，适合将多个时间步的二维变量场联合编码后做二维 trunk 建模。

补充说明：

- 输入沿 `TimeSteps` 维堆叠，但 trunk 本身只处理二维特征图
- 它不是 Pangu 那种三维 token 主干，也不是 FourCastNet 那种纯二维单时刻输入
- 它最重要的结构特点是：三维 patch embedding 先把时间维压缩到 1，再交给二维 U 形 trunk

## 输入定义

- 输入 shape：`(Batch, in_chans, TimeSteps, Height, Width)`
- 输入变量组织：
  - 单一路径输入
  - 多个时间步的二维气象场沿 `TimeSteps` 维堆叠

## 输出定义

- 输出 shape：`(Batch, out_chans, Height, Width)`
- 输出变量组织：
  - 输出为单时刻二维目标场
  - 最终通过 patch 重排恢复，再用双线性插值对齐到目标分辨率

## 主干结构

- `OneEmbedding(style="FuxiEmbedding")`
  - 将 `(TimeSteps, Height, Width)` 三维时空块映射为 patch 特征
- `squeeze(TimeSteps)`
  - 当前实现要求 embedding 后时间维为 1
- `OneTransformer(style="FuxiTransformer")`
  - `FuxiDownSample -> SwinTransformerV2Stage -> FuxiUpSample`
- `OneFC(style="FuxiFC")`
  - 生成 patch 级输出变量
- patch 重排恢复二维网格
- `F.interpolate(..., mode="bilinear")`
  - 对齐到目标 `(Height, Width)`

## 主要依赖组件

- `FuxiEmbedding`
- `FuxiTransformer`
- `FuxiDownSample`
- `FuxiUpSample`
- `FuxiFC`

## 主要 Shape 变化

- 输入：`(Batch, 70, 2, 721, 1440)`
- embedding 输出：`(Batch, 1536, 1, 180, 360)`
- squeeze 后 trunk 输入：`(Batch, 1536, 180, 360)`
- trunk 内下采样后：`(Batch, 1536, 90, 180)`
- trunk 输出：`(Batch, 1536, 180, 360)`
- FC 输出：`(Batch, 180, 360, 70 * 4 * 4)`
- patch 重排后：`(Batch, 70, 720, 1440)`
- 双线性插值后输出：`(Batch, 70, 721, 1440)`

## 默认关键参数

- `img_size=(2, 721, 1440)`
- `patch_size=(2, 4, 4)`
- `in_chans=70`
- `out_chans=70`
- `embed_dim=1536`
- `num_groups=32`
- `num_heads=8`
- `window_size=7`

## 常见修改点

- 修改 `img_size` 或 `patch_size` 时，要同步检查 embedding 后时间维是否仍为 1
- 修改 `embed_dim` 时，要同步检查 `FuxiTransformer` 与 `FuxiFC` 的维度对齐
- 修改空间 patch 尺寸时，要同步检查 patch 重排尺寸和最终插值逻辑

## 风险点

- 当前实现要求 `patch_size[0] == img_size[0]`，否则 embedding 后时间维不一定能被 `squeeze`
- `FuxiTransformer` 处理的是二维特征图，不是展平 token 序列
- patch 重排阶段使用的是 embedding 后的二维网格尺寸，不是原始输入分辨率

## 推荐检索顺序

1. 先看本模型卡
2. 再看 `FuxiEmbedding` 和 `FuxiTransformer`
3. 若需要理解 trunk 内部，再看 `FuxiDownSample`、`FuxiUpSample`、`FuxiFC`
4. 若仍有疑问，再回到模型源码

## 组件契约入口

- `./contracts/fuxiembedding.md`
- `./contracts/fuxitransformer.md`
- `./contracts/fuxidownsample.md`
- `./contracts/fuxiupsample.md`
- `./contracts/fuxifc.md`

## 源码锚点

- `./onescience/src/onescience/models/fuxi/fuxi.py`
- `./onescience/src/onescience/modules/transformer/fuxitransformer.py`
- `./onescience/src/onescience/modules/embedding/fuxiembedding.py`
- `./onescience/src/onescience/modules/sample/fuxidownsample.py`


path: onescience-coder/assets/models/meshgraphnet.md
content: # Model Card: MeshGraphNet

## 基本信息

- 模型名：`MeshGraphNet`
- 任务类型：`CFD / 图网格物理场回归与时序 rollout`
- 当前状态：`stable`
- 主实现文件：`./onescience/src/onescience/models/meshgraphnet/meshgraphnet.py`

## 模型定位

MeshGraphNet 是一个面向非结构网格与图结构物理场建模的消息传递网络，适合在显式图拓扑上做节点级物理量预测。

补充说明：

- 当前 `OneScience` 里至少有两套 MeshGraphNet 风格实现：`onescience.models.meshgraphnet.MeshGraphNet` 和 `cfd_benchmark/MeshGraphNet`
- 现有 `./onescience/examples/cfd/Vortex_shedding_mgn/` 使用的是 `onescience.models.meshgraphnet.MeshGraphNet` 这一套 DGL 版本
- 它最重要的结构特点是：先分别编码节点与边特征，再通过多层 `Edge -> Node` 消息传递块传播信息，最后直接输出每个节点的目标物理量

## 输入定义

- 输入 shape：`node_features -> (NumNodes, NodeInDim)`，`edge_features -> (NumEdges, EdgeInDim)`，`graph -> DGLGraph`
- 输入变量组织：
  - `graph.ndata["x"]`
    - 节点输入特征，例如速度、节点类型、边界条件、几何特征或其它点级状态
  - `graph.edata["x"]`
    - 边输入特征，例如相对坐标差、边长、几何关系量
  - `graph`
    - DGL 图拓扑本身

补充说明：

- 在现有 `Vortex_shedding_mgn` 案例中，节点输入默认是 `2 维速度 + 4 维节点类型 one-hot = 6 维`
- 边输入默认是 `dx, dy, ||d|| = 3 维`
- 模型本身不负责建图，图结构和边特征必须由 datapipe 先准备好

## 输出定义

- 输出 shape：`(NumNodes, output_dim)`
- 输出变量组织：
  - 每个节点直接输出对应目标变量
  - 在现有 `Vortex_shedding_mgn` 案例中，默认输出 `2 维目标速度项 + 1 维压力 = 3 维`

## 主干结构

- `OneMlp(style="MeshGraphMLP")`
  - 编码边特征
- `OneMlp(style="MeshGraphMLP")`
  - 编码节点特征
- 多层消息传递处理器
  - 每层由 `OneEdge(style="MeshEdgeBlock")` 和 `OneNode(style="MeshNodeBlock")` 组成
- `OneMlp(style="MeshGraphMLP")`
  - 将处理后的节点隐状态解码为目标物理量

建议把它理解为：

- datapipe 决定 `graph.ndata["x"]`、`graph.edata["x"]` 和 `graph.ndata["y"]`
- MeshGraphNet 只负责图上的 Encode-Process-Decode
- 现有示例中的“时序 rollout”逻辑主要在 datapipe 与训练脚本，不在模型类内部

## 主要依赖组件

- `OneMlp`
- `OneEdge`
- `OneNode`

## 主要 Shape 变化

- datapipe 输出：
  - `graph.ndata["x"] -> (NumNodes, NodeInDim)`
  - `graph.edata["x"] -> (NumEdges, EdgeInDim)`
- 编码后：
  - 节点与边都映射到隐藏维度 `hidden_dim_processor`
- Processor 多层消息传递：
  - 隐状态维度保持不变
- 解码后：
  - `(NumNodes, output_dim)`

## 默认关键参数

- `processor_size=15`
- `hidden_dim_processor=128`
- `num_layers_node_processor=2`
- `num_layers_edge_processor=2`
- `hidden_dim_node_encoder=128`
- `hidden_dim_edge_encoder=128`
- `hidden_dim_node_decoder=128`
- `aggregation="sum"`

说明：

- `input_dim_nodes`、`input_dim_edges`、`output_dim` 由具体 datapipe 和任务目标决定
- 现有 `Vortex_shedding_mgn` 示例还提供了 `BiStrideMeshGraphNet`，但默认基线是标准 `MeshGraphNet`

## 常见修改点

- 新数据集的节点特征字段变化时，同步修改 `input_dim_nodes`
- 新数据集的边特征设计变化时，同步修改 `input_dim_edges`
- 新数据集的目标变量变化时，同步修改 `output_dim`
- 若新数据集是稳态单样本图，而不是时序 rollout 数据，需要同步调整 datapipe 与训练脚本，不要只替换模型类
- 若新数据集来自 `VTK / VTU / VTP` 文件，通常要先确定是构造成 DGL 图、PyG 图还是点云回归协议，再决定是否走 MeshGraphNet 路线

## 风险点

- `OneScience` 中存在两套 MeshGraphNet 风格实现；若用户说 `MGN`，要先确认是 `onescience.models.meshgraphnet.MeshGraphNet` 还是 `cfd_benchmark/MeshGraphNet`
- 当前 `Vortex_shedding_mgn` 示例默认依赖 DGL 图、TFRecord 数据和时序 rollout，不代表所有新数据集都能直接复用这套训练流程
- 模型前向需要显式传入 `node_features, edge_features, graph`，不是 `PyG Data` 协议；不能直接拿 Transolver 的 datapipe 喂给它
- DGL 依赖是硬要求；若环境没有 DGL，现有 MGN 路线无法直接运行

## 推荐检索顺序

1. 先看本模型卡
2. 再看最接近的 MGN 示例工程
3. 再看对应 datapipe 卡
4. 若仍不足，再回到源码锚点

## 组件契约入口

- `./contracts/onemlp.md`

## 源码锚点

- `./onescience/src/onescience/models/meshgraphnet/meshgraphnet.py`
- `./onescience/src/onescience/models/meshgraphnet/bsms_mgn.py`
- `./onescience/examples/cfd/Vortex_shedding_mgn/train.py`
- `./onescience/examples/cfd/Vortex_shedding_mgn/conf/mgn_cylinderflow.yaml`


path: onescience-coder/assets/models/model_index.md
content: # Model Index

## 目标

本文档用于登记当前已经补充到 `oneskills` 的模型级知识。

它与 `contracts/component_index.md` 的分工不同：

- `model_index.md`
  - 解决“当前任务更接近哪个已有模型”
  - 提供模型级输入输出、主干结构、组件链路和常见修改点
- `component_index.md`
  - 解决“某个模块该从哪个入口初始化、`style` 写什么、shape 怎么对齐”

## 建议使用方式

推荐让智能体按下面顺序检索：

1. 若用户明确提到模型名，先读本文档
2. 若用户没提模型名，但任务明显接近某个已知模型，也可先读本文档
3. 再按需读取对应模型卡
4. 然后读取相关组件契约
5. 只有当模型卡和契约都不够时，再回到源码

## 当前已登记模型

| 模型 | 任务类型 | 输入形态摘要 | 主干类型 | 主要依赖组件 | 状态 | 模型卡 |
|---|---|---|---|---|---|---|
| Transolver | CFD / 点级物理场回归 | `PyG Data` 点集输入，`Data.x + Data.pos` | 2D/3D point trunk with physics slicing | `OneTransformer(style="Transolver_block")`, `StandardMLP` | `stable` | `./models/transolver.md` |
| MeshGraphNet | CFD / 图网格物理场回归 | `DGLGraph + node_features + edge_features` | graph encode-process-decode trunk | `OneMlp`, `OneEdge`, `OneNode` | `stable` | `./models/meshgraphnet.md` |
| FNO | CFD / 规则网格神经算子基线 | `(pos, fx)` 接口 | spectral operator trunk | `OneMlp`, `OneFourier` | `stable` | `./models/fno.md` |
| U_FNO | CFD / 谱算子 + 多尺度分支基线 | `(pos, fx)` 接口 | spectral trunk with parallel U-shape branch | `OneMlp`, `OneFourier`, `U_Net` | `stable` | `./models/u_fno.md` |
| U_NO | CFD / U-shaped neural operator | `(pos, fx)` 接口 | U-shape operator trunk | `OneMlp`, `OneFourier` | `stable` | `./models/u_no.md` |
| U_Net (CFD_Benchmark) | CFD / benchmark 风格 U-shape 基线 | `(pos, fx)` 接口 | U-shape encoder/decoder trunk | `OneMlp`, `OneFourier`, `OneEncoder`, `OneDecoder`, `OneHead` | `stable` | `./models/u_net_operator.md` |
| Pangu | 全球天气预报 / 多层大气与地表联合建模 | surface 2D + upper-air 3D | 3D token trunk | `PanguEmbedding`, `PanguFuser`, `PanguDownSample`, `PanguUpSample`, `PanguPatchRecovery` | `stable` | `./models/pangu.md` |
| FourCastNet | 全球天气预报 / 单时刻 2D 场建模 | 2D 场 | 2D patch-grid AFNO trunk | `FourCastNetEmbedding`, `FourCastNetFuser`, `FourCastNetAFNO2D`, `FourCastNetFC` | `stable` | `./models/fourcastnet.md` |
| Fuxi | 多时间步二维气象场预报 | 多时间步 3D 时空块输入 | 2D U-shape Swin trunk | `FuxiEmbedding`, `FuxiTransformer`, `FuxiDownSample`, `FuxiUpSample`, `FuxiFC` | `stable` | `./models/fuxi.md` |
| FengWu | 多变量中期天气预报 | 多分支 2D 场输入 | 多分支 2D encoder/decoder + 3D fuser | `FengWuEncoder`, `FengWuFuser`, `FengWuDecoder` | `stable` | `./models/fengwu.md` |

## 适合优先看模型卡的问题

- 当前任务更像哪个已有模型
- 某个模型的输入输出变量如何组织
- 某个模型的 trunk 是二维还是三维
- 做模块替换时，应先改哪些调用点
- 做跨模型组件替换时，哪些 shape 或语义最容易冲突
- 当用户说“U-Net”时，当前指的是 `CFD_Benchmark/U_Net` 还是 `DeepCFD/UNet(Ex)`
- 当用户说“MGN”时，当前通常指的是 `MeshGraphNet` 路线，但要继续确认具体是 DGL 版现有案例还是 benchmark 版实现

## 新增模型卡的维护建议

新增模型时，建议至少完成以下内容：

1. 复制 `./models/TEMPLATE.md`
2. 填写模型级输入输出、主干结构、主要依赖组件和风险点
3. 在本文档中登记
4. 检查 `README.md`、`task/SKILL.md`、`DEVELOPER_MANUAL.md` 是否需要同步更新


path: onescience-coder/assets/models/pangu.md
content: # Model Card: Pangu

## 基本信息

- 模型名：`Pangu`
- 任务类型：`全球天气预报 / 多层大气与地表联合建模`
- 当前状态：`stable`
- 主实现文件：`./onescience/src/onescience/models/pangu/pangu.py`

## 模型定位

Pangu 是一个面向全球天气预报的三维主干模型，适合同时建模地表变量和多气压层大气变量。

补充说明：

- 输入不是单一路径，而是 surface 与 upper-air 两类变量联合组织
- 主干不是独立的 surface / upper-air 双 encoder，而是统一的 3D token trunk
- 它最重要的结构特点是：surface patch token 先补一个 `PressureLevels=1`，再与 upper-air patch token 拼成统一三维网格

## 输入定义

- 输入 shape：`(Batch, 4 + 3 + 5 * 13, Height, Width)`
- 输入变量组织：
  - 前 4 个通道：待预测的 surface 变量
  - 接下来的 3 个通道：静态 mask
  - 剩余 `5 * 13` 个通道：upper-air 变量按气压层展平

## 输出定义

- 输出 shape：
  - surface: `(Batch, 4, Height, Width)`
  - upper-air: `(Batch, 5, 13, Height, Width)`
- 输出变量组织：
  - surface 输出只包含 4 个待预测 surface 变量
  - 3 个静态 mask 仅参与输入，不属于输出目标

## 主干结构

- `OneEmbedding(style="PanguEmbedding")`
  - 分别编码 surface 与 upper-air
- `surface.unsqueeze(2)` 与 upper-air 特征沿 `PressureLevels` 维拼接
- `OneFuser(style="PanguFuser")`
  - `layer1 -> downsample -> layer2 -> layer3 -> upsample -> layer4`
- `OneRecovery(style="PanguPatchRecovery")`
  - 分别恢复 surface 与 upper-air 目标场

## 主要依赖组件

- `PanguEmbedding`
- `PanguFuser`
- `PanguDownSample`
- `PanguUpSample`
- `PanguPatchRecovery`

## 主要 Shape 变化

- surface 输入：`(Batch, 7, 721, 1440)`
- upper-air 输入：`(Batch, 5, 13, 721, 1440)`
- surface embedding 输出：`(Batch, 192, 181, 360)`
- upper-air embedding 输出：`(Batch, 192, 7, 181, 360)`
- 拼接后主干输入：`(Batch, 192, 8, 181, 360)`
- 展平后 trunk token：`(Batch, 8 * 181 * 360, 192)`
- 下采样后 trunk token：`(Batch, 8 * 91 * 180, 384)`
- 上采样恢复后 trunk token：`(Batch, 8 * 181 * 360, 192)`
- 与 skip 拼接后恢复：
  - surface: `(Batch, 4, 721, 1440)`
  - upper-air: `(Batch, 5, 13, 721, 1440)`

## 默认关键参数

- `img_size=(721, 1440)`
- `patch_size=(2, 4, 4)`
- `embed_dim=192`
- `num_heads=(6, 12, 12, 6)`
- `window_size=(2, 6, 12)`

## 常见修改点

- 修改输入变量数时，要同步检查 surface / upper-air 的切分逻辑与 patch recovery 输出通道数
- 修改 `patch_size` 时，要同步检查 embedding、sample、patch recovery 和位置 shape 推导
- 修改主干深度或头数时，要同步检查 `PanguFuser` 的 `dim`、`num_heads` 和上下游维度匹配

## 风险点

- surface 输入 7 通道但输出 4 通道，这不是错误，而是因为静态 mask 只参与输入
- trunk 输入是统一三维 token 网格，不要误改成两个完全分离的 encoder/decoder 主干
- `PanguFuser` 处理的是展平 token 序列，`PanguPatchRecovery` 处理的是特征图，二者输入形态不同

## 推荐检索顺序

1. 先看本模型卡
2. 再看 `PanguEmbedding` 和 `PanguFuser`
3. 再看 `PanguDownSample`、`PanguUpSample`、`PanguPatchRecovery`
4. 若仍有疑问，再回到模型源码

## 组件契约入口

- `./contracts/panguembedding.md`
- `./contracts/pangufuser.md`
- `./contracts/pangudownsample.md`
- `./contracts/panguupsample.md`
- `./contracts/pangupatchrecovery.md`

## 源码锚点

- `./onescience/src/onescience/models/pangu/pangu.py`
- `./onescience/src/onescience/modules/fuser/pangufuser.py`
- `./onescience/src/onescience/modules/sample/pangudownsample.py`
- `./onescience/src/onescience/modules/recovery/pangupatchrecovery.py`


path: onescience-coder/assets/models/TEMPLATE.md
content: # Model Card: <ModelName>

## 基本信息

- 模型名：`<ModelName>`
- 任务类型：`<task_type>`
- 当前状态：`<stable_or_other>`
- 主实现文件：`./onescience/<path_to_model>.py`

## 模型定位

一句话说明该模型适合解决什么问题。

补充说明：

- 该模型是二维还是三维主干
- 主要适用于哪类输入组织方式
- 它与其它模型相比最重要的结构特点是什么

## 输入定义

- 输入 shape：`<input_shape>`
- 输入变量组织：
  - `<branch_or_layout_1>`
  - `<branch_or_layout_2>`

## 输出定义

- 输出 shape：`<output_shape>`
- 输出变量组织：
  - `<branch_or_layout_1>`
  - `<branch_or_layout_2>`

## 主干结构

- `<stage_1>`
- `<stage_2>`
- `<stage_3>`

建议只写调用层真正需要知道的结构链路，不要把源码逐行展开。

## 主要依赖组件

- `<component_1>`
- `<component_2>`
- `<component_3>`

这里优先写当前推荐先读的组件契约。

## 主要 Shape 变化

- `<shape_step_1>`
- `<shape_step_2>`
- `<shape_step_3>`

## 默认关键参数

- `<arg_1>=<value>`
- `<arg_2>=<value>`
- `<arg_3>=<value>`

## 常见修改点

- `<edit_point_1>`
- `<edit_point_2>`
- `<edit_point_3>`

## 风险点

- `<risk_1>`
- `<risk_2>`
- `<risk_3>`

优先写：

- 输入输出变量数是否对称
- token / patch / 网格 shape 是否容易错
- 哪些修改会联动多个组件

## 推荐检索顺序

1. 先看本模型卡
2. 再看相关组件契约
3. 若契约不足，再回到源码锚点

## 组件契约入口

- `./contracts/<component_1>.md`
- `./contracts/<component_2>.md`

## 源码锚点

- `./onescience/<path_to_model>.py`
- `./onescience/<path_to_component_or_registry>.py`

统一约定：

- 一律使用 `./onescience/...` 相对路径
- 不写本机绝对路径


path: onescience-coder/assets/models/transolver.md
content: # Model Card: Transolver

## 基本信息

- 模型名：`Transolver`
- 任务类型：`CFD / 几何相关物理场点级回归`
- 当前状态：`stable`
- 主实现文件：`./onescience/src/onescience/models/transolver/Transolver2D.py`

## 模型定位

Transolver 是一个面向 PDE 与 CFD 场景的几何感知点级回归模型，适合在非结构化网格、点云或几何相关采样点上直接预测物理量。

补充说明：

- 模型族同时提供二维和三维主干：`Transolver2D`、`Transolver3D`
- 增强版本 `Transolver2D_plus`、`Transolver3D_plus` 保持调用方式不变，只替换为更强的 `unstructured_plus` 物理注意力
- 它最重要的结构特点是：先把每个点的输入特征映射到隐空间，再通过多层物理切片注意力 block 在全局点集上做交互，最终直接输出每个点的目标变量

## 输入定义

- 输入 shape：`Data.x -> (NumPoints, InChannels)`，`Data.pos -> (NumPoints, 2 or 3)`
- 输入变量组织：
  - `Data.x`
    - 每个点的几何特征、边界条件特征、物理先验特征或其它点级输入特征
  - `Data.pos`
    - 与点一一对应的二维或三维坐标

补充说明：

- `space_dim` 对应的是 `Data.x` 的输入通道数，不只是坐标维度
- 若 `unified_pos=True`，模型内部会基于 `Data.pos` 额外构造参考网格距离编码，datapipe 不需要预先生成这部分特征

## 输出定义

- 输出 shape：`(NumPoints, out_dim)`
- 输出变量组织：
  - 每个点直接输出对应目标变量
  - 常见 CFD 场景中可对应速度、压力、湍流量等点级物理量

## 主干结构

- `OneMlp(style="StandardMLP")`
  - 将点级输入特征映射到隐藏维度
- 加入可学习 `placeholder` 偏置
- 多层 `OneTransformer(style="Transolver_block")`
  - 标准版使用 `geotype="unstructured"`
  - plus 版使用 `geotype="unstructured_plus"`
- 最后一层 block 直接输出 `out_dim`

建议把它理解为：

- datapipe 决定 `Data.x/Data.pos/Data.y`
- Transolver 负责点级特征映射和全局物理交互
- 训练脚本通常只需要根据数据维度切换 2D 或 3D 版本，而不改模型内部结构

## 主要依赖组件

- `OneTransformer`
- `Transolver_block`
- `OneMlp(style="StandardMLP")`

## 主要 Shape 变化

- datapipe 输出：`Data.x -> (NumPoints, InChannels)`
- 模型前向开始：扩成 `x -> (1, NumPoints, InChannels)`
- 若 `unified_pos=True`
  - 2D 版本拼接后变为 `(1, NumPoints, InChannels + ref^2)`
  - 3D 版本拼接后变为 `(1, NumPoints, InChannels + ref^3)`
- `preprocess` 后：`(1, NumPoints, n_hidden)`
- 最后一层 block 后：`(1, NumPoints, out_dim)`
- 输出前去掉 batch 维：`(NumPoints, out_dim)`

## 默认关键参数

- `n_hidden=256`
- `n_layers=8`
- `n_head=8`
- `mlp_ratio=2`
- `slice_num=32`
- `ref=8`
- `unified_pos=1`

说明：

- `space_dim` 和 `out_dim` 由具体 datapipe 与任务目标决定
- `Transolver2D` / `Transolver2D_plus` 通常与二维翼型、二维流场任务搭配
- `Transolver3D` / `Transolver3D_plus` 通常与三维车体、三维几何流场任务搭配

## 常见修改点

- 根据 `Data.pos` 的坐标维度在训练脚本中选择 2D 或 3D 版本
- 当新数据集的输入特征字段变化时，同步修改 `space_dim`
- 当新数据集的目标变量变化时，同步修改 `out_dim`
- 当复用已有案例训练流程时，优先保持模型类和训练主循环不变，只替换 datapipe、配置项和数据相关指标
- 若新数据集没有 `surf`、法向量、湍流黏度等字段，需要在 datapipe 里补占位或同步简化损失与评估逻辑

## 风险点

- `space_dim` 一旦和 `Data.x` 通道数不一致，模型会在第一层 MLP 直接报错
- `unified_pos=True` 会改变实际输入维度，不能在 datapipe 中重复构造同类参考网格距离特征
- plus 版与普通版的主要差异在 `geotype`，不是新的输入输出协议；不要误以为需要改 datapipe 返回格式
- 训练脚本里常把 Transolver 和 GraphSAGE / PointNet / GUNet 共用一套配置块，改 datapipe 时要确认这些备用模型分支不会被意外破坏

## 推荐检索顺序

1. 先看本模型卡
2. 再看 `OneTransformer` 契约
3. 再看最接近的 Transolver 示例工程
4. 若仍不足，再回到模型源码和 `Transolver_block` 实现

## 组件契约入口

- `./contracts/onetransformer.md`

## 源码锚点

- `./onescience/src/onescience/models/transolver/Transolver2D.py`
- `./onescience/src/onescience/models/transolver/Transolver3D.py`
- `./onescience/src/onescience/models/transolver/Transolver2D_plus.py`
- `./onescience/src/onescience/modules/transformer/Transolver_block.py`


path: onescience-coder/assets/models/u_fno.md
content: # Model Card: U_FNO

## 基本信息

- 模型名：`U_FNO`
- 任务类型：`CFD / steady-or-dynamic operator learning with spectral-plus-multiscale baseline`
- 当前状态：`stable`
- 主实现文件：`./onescience/src/onescience/models/cfd_benchmark/U_FNO.py`

## 模型定位

U_FNO 是在 FNO 主干中插入并联 U-Net 多尺度分支的增强基线，适合同时捕获全局谱特征和局部多尺度结构。

补充说明：

- 输入输出接口与 `FNO` 保持同一套 `(x, fx) -> y` 约定
- 它最关键的区别不是数据接口变化，而是第 3、4 个谱块里额外引入了 `U_Net.multiscale()` 分支
- 适合用户已经接受 FNO 接口，但希望局部细节表达更强的场景

## 输入定义

- 输入 shape：`x -> (Batch, NumPoints, space_dim)`，`fx -> (Batch, NumPoints, fun_dim)` 或 `None`
- 输入变量组织：
  - `x`
    - 位置或规则网格坐标
  - `fx`
    - 与位置对齐的输入场特征

## 输出定义

- 输出 shape：`(Batch, NumPoints, out_dim)`
- 输出变量组织：
  - 每个位置对应 `out_dim` 个目标变量

## 主干结构

- `OneMlp(style="StandardMLP")`
- 可选 `OneFourier(style="GeoSpectralConv2d")`
- 4 层 FNO 风格谱块
- 其中第 3、4 层额外并联 `U_Net.multiscale(x)`
- `fc1 + fc2`

## 主要依赖组件

- `OneMlp`
- `OneFourier`
- `U_Net (CFD_Benchmark)`

## 主要 Shape 变化

- 预处理前后与 `FNO` 相同
- 中间主干在结构网格分支会进入 `(Batch, n_hidden, *shapelist)` 形式
- `U_Net.multiscale()` 的输入与输出都保持该网格特征形态
- 最终仍会展平回 `(Batch, NumPoints, out_dim)`

## 默认关键参数

- `geotype="structured_2D"`
- `space_dim=2`
- `fun_dim=0` 到若干输入通道
- `out_dim=1` 到若干目标通道
- `n_hidden=32`
- `modes=12`
- `epochs=500`

## 常见修改点

- 跟随数据接口同步修改 `fun_dim / out_dim / shapelist`
- 若要复用 DeepCFD 规则网格数据，优先新增 operator adapter datapipe，而不是改模型 forward
- 若希望更公平地和 `FNO / U_NO / U_Net` 对比，尽量保持同一组数据划分、归一化和评估指标

## 风险点

- 数据接口风险与 `FNO` 相同，不能直接喂 `DeepCFDDatapipe` 的字典 batch
- U_FNO 依赖 `U_Net.multiscale()`，因此一旦 `U_Net` 的空间尺寸约束不满足，问题会传导到 U_FNO
- 当前实现的非结构分支同样主要围绕 2D Geo 投影写法组织

## 推荐检索顺序

1. 先看本模型卡
2. 再看 `FNO`
3. 再看 `U_Net (CFD_Benchmark)`
4. 再看 `OneFourier` 与 `OneMlp`

## 组件契约入口

- `./contracts/onefourier.md`
- `./contracts/onemlp.md`
- `./contracts/oneencoder.md`
- `./contracts/onedecoder.md`
- `./contracts/onehead.md`

## 源码锚点

- `./onescience/src/onescience/models/cfd_benchmark/U_FNO.py`
- `./onescience/src/onescience/models/cfd_benchmark/U_Net.py`
- `./onescience/examples/cfd/CFD_Benchmark/exp/exp_steady.py`


path: onescience-coder/assets/models/u_net_operator.md
content: # Model Card: U_Net (CFD_Benchmark)

## 基本信息

- 模型名：`U_Net`
- 任务类型：`CFD / structured-or-geo-projected field surrogate baseline`
- 当前状态：`stable`
- 主实现文件：`./onescience/src/onescience/models/cfd_benchmark/U_Net.py`

## 模型定位

这是 `CFD_Benchmark` 体系下的 `U_Net` 基线，用统一的 `(x, fx)` 接口把位置与场特征映射到 U-shape 多尺度主干中，再输出目标场。

补充说明：

- 它和 `./onescience/src/onescience/models/deepcfd/UNet.py` 不是同一个模型族
- 当前这个 `U_Net` 走的是 `CFD_Benchmark` 训练接口，目标是和 `FNO / U_FNO / U_NO` 共享同一套 benchmark 风格输入
- 它最重要的结构特点是：用 `OneEncoder + OneDecoder + OneHead` 组织一个多尺度 U-shape 主干

## 输入定义

- 输入 shape：`x -> (Batch, NumPoints, space_dim)`，`fx -> (Batch, NumPoints, fun_dim)` 或 `None`
- 输入变量组织：
  - `x`
    - 坐标或规则网格位置
  - `fx`
    - 与位置对齐的输入场特征

## 输出定义

- 输出 shape：`(Batch, NumPoints, out_dim)`
- 输出变量组织：
  - 每个位置对应 `out_dim` 个目标变量

## 主干结构

- `OneMlp(style="StandardMLP")`
- 可选 `OneFourier(style="GeoSpectralConv2d")`
- `OneEncoder(style="UNetEncoder{dim}D")`
- `OneDecoder(style="UNetDecoder{dim}D")`
- `OneHead(style="UNetHead{dim}D")`
- `fc1 + fc2`

## 主要依赖组件

- `OneMlp`
- `OneFourier`
- `OneEncoder`
- `OneDecoder`
- `OneHead`

## 主要 Shape 变化

- 预处理后：`(Batch, NumPoints, n_hidden)`
- 结构分支重排后：`(Batch, n_hidden, *shapelist)`
- 经过 U-shape 多尺度主干后保持网格张量形式
- 展平后恢复为：`(Batch, NumPoints, n_hidden)`
- 输出投影后：`(Batch, NumPoints, out_dim)`

## 默认关键参数

- `task="steady"`
- `geotype="structured_2D"`
- `space_dim=2`
- `fun_dim=0` 到若干输入通道
- `out_dim=1` 到若干目标通道
- `n_hidden=32`
- `unified_pos=0`

## 常见修改点

- 明确当前要比较的是这个 `CFD_Benchmark U_Net`，还是 `DeepCFD UNet / UNetEx`
- 若接 DeepCFD 数据，优先新增 operator adapter datapipe，再复用这个 `U_Net`
- 若只想做 CNN 图像基线，通常更接近 `DeepCFD` 目录下那套 `UNet/UNetEx` 训练流，而不是当前模型

## 风险点

- “U-Net” 在 OneScience 里至少有两套实现；如果第一轮不区分，很容易选错训练入口
- 当前模型不能直接消费 `DeepCFDDatapipe` 的字典 batch
- 当前非结构分支同样主要围绕 2D Geo 投影组织，不要默认它天然适配任意 3D 非结构任务

## 推荐检索顺序

1. 先看本模型卡
2. 再看 `OneEncoder`
3. 再看 `OneDecoder`
4. 再看 `OneHead`
5. 最后再看 `OneMlp / OneFourier`

## 组件契约入口

- `./contracts/oneencoder.md`
- `./contracts/onedecoder.md`
- `./contracts/onehead.md`
- `./contracts/onemlp.md`
- `./contracts/onefourier.md`

## 源码锚点

- `./onescience/src/onescience/models/cfd_benchmark/U_Net.py`
- `./onescience/src/onescience/modules/encoder/oneencoder.py`
- `./onescience/src/onescience/modules/decoder/onedecoder.py`
- `./onescience/src/onescience/modules/head/onehead.py`


path: onescience-coder/assets/models/u_no.md
content: # Model Card: U_NO

## 基本信息

- 模型名：`U_NO`
- 任务类型：`CFD / steady-or-dynamic operator learning with U-shaped neural operator`
- 当前状态：`stable`
- 主实现文件：`./onescience/src/onescience/models/cfd_benchmark/U_NO.py`

## 模型定位

U_NO 把 U-Net 的多尺度下采样/上采样结构和 Fourier Neural Operator 结合在一起，适合作为比 `FNO` 更强、比纯 CNN 更偏算子建模的中间基线。

补充说明：

- 输入输出接口仍然遵循 `CFD_Benchmark` 的 `(x, fx) -> y`
- 它和 `U_FNO` 的区别是：`U_NO` 把谱算子更深地嵌入到了整条 U-shape 的下采样和上采样路径里
- 在稳态任务下默认更倾向使用 `bn`，动态任务下会切成 `in`

## 输入定义

- 输入 shape：`x -> (Batch, NumPoints, space_dim)`，`fx -> (Batch, NumPoints, fun_dim)` 或 `None`
- 输入变量组织：
  - `x`
    - 位置或规则网格坐标
  - `fx`
    - 点级或网格级输入物理特征

## 输出定义

- 输出 shape：`(Batch, NumPoints, out_dim)`
- 输出变量组织：
  - 每个位置对应 `out_dim` 个目标变量

## 主干结构

- `OneMlp(style="StandardMLP")`
- 可选 `OneFourier(style="GeoSpectralConv2d")`
- U-shape 编码器与解码器路径
- 每个尺度处插入 `FNOSpectralConv*d + residual`
- `fc1 + fc2`

## 主要依赖组件

- `OneMlp`
- `OneFourier`
- `unet_layer` 系列基础层

## 主要 Shape 变化

- 预处理后：`(Batch, NumPoints, n_hidden)`
- 结构分支重排后：`(Batch, n_hidden, *shapelist)`
- 下采样路径中空间分辨率逐层降低、通道数提升
- 上采样路径中再逐层恢复
- 最终展平回：`(Batch, NumPoints, out_dim)`

## 默认关键参数

- `task="steady"`
- `geotype="structured_2D"`
- `space_dim=2`
- `fun_dim=0` 到若干输入通道
- `out_dim=1` 到若干目标通道
- `n_hidden=32`
- `modes=12`

## 常见修改点

- 根据目标任务修改 `fun_dim / out_dim`
- 根据规则网格分辨率修改 `shapelist`
- 若要接 DeepCFD 数据，通常需要把图像输入展平为 `pos + fx`，而不是直接把 `(Batch, Channels, Height, Width)` 送进 `forward`
- 若比较多个模型，优先统一训练脚本和评价指标，再去调模型容量

## 风险点

- 相比 `FNO`，U_NO 对空间尺度链路更敏感，`shapelist` 与 padding 不一致时更容易在 U-shape 中间层报错
- 直接复用 DeepCFD datapipe 不可行，必须桥接批格式
- 当前实现的非结构分支仍主要围绕 2D Geo 投影

## 推荐检索顺序

1. 先看本模型卡
2. 再看 `OneFourier`
3. 再看 `OneMlp`
4. 若需要确认下采样/上采样细节，再回到 `unet_layer`

## 组件契约入口

- `./contracts/onefourier.md`
- `./contracts/onemlp.md`

## 源码锚点

- `./onescience/src/onescience/models/cfd_benchmark/U_NO.py`
- `./onescience/src/onescience/modules/fourier/onefourier.py`
- `./onescience/examples/cfd/CFD_Benchmark/exp/exp_steady.py`


path: onescience-coder/assets/datapipes/airfrans.md
content: # Datapipe: AirfRANSDatapipe

## 基本信息

- Datapipe 名：`AirfRANSDatapipe`
- 数据类型：`cfd`
- 主要任务：`2d airfoil flow-field regression`
- 数据组织方式：`per_simulation_dir_with_vtu_vtp_and_manifest`

## Datapipe 职责

`AirfRANSDatapipe` 负责把按单个翼型工况目录存储的 AirfRANS 非结构网格数据组织成 PyTorch Geometric 可直接消费的图样本，并封装成训练、验证、测试阶段可用的 `DataLoader`。

补充说明：

- 原始数据以“根目录下一个 `manifest.json`，每个仿真样本一个子目录”的方式组织
- datapipe 负责样本划分、VTK 读取、可选裁剪、可选单元/边界采样、归一化、训练/验证阶段点级下采样、图构建和 `DataLoader` 组装
- datapipe 同时实现了 `AirfRANSDataset` 与 `AirfRANSDatapipe` 两层接口

## 输入配置

- `source.data_dir`
  - AirfRANS 数据根目录，目录下需要包含 `manifest.json` 与各仿真子目录
- `source.stats_dir`
  - 归一化统计量保存目录，内部会读写 `mean_in/std_in/mean_out/std_out`
- `data.splits.train_name`
  - `manifest.json` 中训练全集对应的键名，例如 `full_train`
- `data.splits.val_split_ratio`
  - 从训练全集尾部切出验证集的比例
- `data.splits.test_name`
  - `manifest.json` 中测试集对应的键名，例如 `full_test`
- `data.splits.task`
  - 任务标签，示例配置中会写 `full/scarce/reynolds/aoa`
- `data.sampling.sample_strategy`
  - 采样策略，支持 `null`、`uniform`、`mesh`
- `data.sampling.n_boot`
  - 体网格采样点数基准
- `data.sampling.surf_ratio`
  - 表面采样点数相对 `n_boot` 的比例
- `data.crop`
  - 可选裁剪框，格式为 `(xmin, xmax, ymin, ymax)`
- `data.subsampling`
  - 训练和验证阶段最终保留的点数上限
- `dataloader.batch_size`
  - PyG `DataLoader` 批大小
- `dataloader.num_workers`
  - PyG `DataLoader` 工作进程数
- `model_hparams.build_graph`
  - 是否基于坐标构图
- `model_hparams.r`
  - `radius_graph` 的半径
- `model_hparams.max_neighbors`
  - 每个点的最大邻居数

## 数据存储约定

- 主数据路径：`<data_dir>/manifest.json`，以及 `<data_dir>/<sim_name>/<sim_name>_internal.vtu` 和 `<data_dir>/<sim_name>/<sim_name>_aerofoil.vtp`
- 统计量路径：`<stats_dir>/mean_in.npy`、`std_in.npy`、`mean_out.npy`、`std_out.npy`
- 元数据来源：`manifest.json`、样本目录名中的工况编码，以及 VTK 文件中的 `point_data` / `cell_data`

额外约定：

- `manifest.json` 必须包含配置里引用到的键，例如 `full_train`、`full_test`
- 样本名默认满足 `s.split("_")[2]` 是来流速度 `Uinf`，`s.split("_")[3]` 是攻角角度值
- `internal.vtu` 需要提供 `U`、`p`、`nut`、`implicit_distance`，`aerofoil.vtp` 需要提供 `U`、`p`、`nut`、`Normals`

## 样本构造方式

- 输入样本：`Data.x` 形状为 `[NumPoints, 7]`，语义为 `[pos_x, pos_y, u_inf_x, u_inf_y, sdf, normal_x, normal_y]`
- 输出样本：`Data.y` 形状为 `[NumPoints, 4]`，语义为 `[v_x, v_y, p, nut]`
- 附加返回项：`Data.pos`、`Data.surf`、`Data.edge_index`

具体做法：

- 一个 dataset 样本对应一个仿真目录，而不是连续时间窗
- 当 `sample_strategy=null` 时，读取全量内部网格点；`surf` 通过 `internal.point_data["U"][:, 0] == 0` 判定，表面法向量通过 `reorganize(...)` 从 `aerofoil.vtp` 对齐回内部网格表面点
- 当 `sample_strategy="uniform"` 或 `"mesh"` 时，会分别对体单元和翼型表面线单元采样，再在单元内部做 2D / 1D 插值，最后拼成统一的点集
- `uniform` 按单元面积与边界线段长度加权抽样；`mesh` 按单元数量均匀抽样
- 若设置了 `data.crop`，会先对内部网格做 `clip_box`
- 归一化统计量来自训练集；若本地不存在统计文件，训练集初始化时会临时强制关闭采样，用全量网格计算均值和标准差
- 训练和验证阶段还会做一次随机点级下采样：若当前点数大于 `data.subsampling`，随机保留固定数量点；测试阶段不做这一步
- 若 `model_hparams.build_graph=True`，会基于 `Data.pos` 用 `radius_graph` 生成 `edge_index`；否则返回 `None`

## DataLoader 约定

- 训练阶段：`train_dataloader()` 返回 `(PyGDataLoader, sampler)`；分布式时使用 `DistributedSampler(shuffle=True)` 且 `drop_last=True`，非分布式时直接 `shuffle=True`
- 验证阶段：`val_dataloader()` 返回 `(PyGDataLoader, sampler)`；分布式时使用 `DistributedSampler(shuffle=False)`，否则不打乱
- 测试阶段：`test_dataloader()` 只返回 `PyGDataLoader`，不使用分布式采样器，`shuffle=False` 且 `drop_last=False`

## 适合优先使用的场景

- 使用 AirfRANS 数据训练二维翼型流场代理模型，预测速度、压力和湍流黏度
- 模型以点云图结构作为输入，例如 Transolver、GraphSAGE、GUNet 一类几何泛化模型
- 需要在全量网格读取和随机单元采样两种数据组织方式之间切换

## 风险点

- train / val 划分不是由 `manifest.json` 单独给出，而是对 `train_name` 对应全集按 `val_split_ratio` 直接切分
- `data.splits.task` 在当前实现里会被读取，但真正决定训练/测试集合的是 `train_name` 与 `test_name`
- 样本目录名被直接解析为来流速度和攻角，一旦命名规则变化，边界条件构造会失效
- 全量网格模式把 `U_x == 0` 当成表面点判据，并假设内部表面点能与 `aerofoil.vtp` 做一一坐标对齐
- 验证和测试集要求先拿到训练阶段生成或加载好的归一化统计量，否则会直接报错

## 源码锚点

- `./onescience/src/onescience/datapipes/cfd/AirfRANS.py`
- `./onescience/src/onescience/datapipes/core/base_dataset.py`
- `./onescience/src/onescience/utils/transolver/reorganize.py`


path: onescience-coder/assets/datapipes/cmems.md
content: # Datapipe: CMEMSDatapipe

## 基本信息

- Datapipe 名：`CMEMSDatapipe`
- 数据类型：`climate`
- 主要任务：`global ocean forecast`
- 数据组织方式：`per_timestamp_h5_under_year_dir`

## Datapipe 职责

`CMEMSDatapipe` 负责把按年份目录、按时刻存储的 CMEMS 海洋场数据切成连续时间窗样本，并组织成适合模型训练或推理的 `DataLoader`。

补充说明：

- 原始数据以“按年份分目录、目录下按时刻存 HDF5 文件”的方式组织
- datapipe 负责年份划分、变量筛选、时间窗切片、NaN 处理、归一化、太阳天顶角构造和 `DataLoader` 组装
- train / val / test 划分在 datapipe 内部通过配置完成

## 输入配置

- `data_dir`
  - CMEMS 数据根目录
- `channels`
  - 当前任务需要读取的变量名列表
- `train_ratio`
  - 训练阶段年份划分配置
- `val_ratio`
  - 验证阶段年份划分配置
- `test_ratio`
  - 测试阶段年份划分配置
- `time_res`
  - 时间分辨率，单位小时
- `input_steps`
  - 输入时间步数
- `output_steps`
  - 预测时间步数
- `img_size`
  - 目标空间尺寸，用于裁剪对齐

## 数据存储约定

- 主数据路径：`<data_dir>/h5/<year>/*.h5`
- 统计量路径：`<stats_dir>/global_means.npy` 与 `global_stds.npy`
- 元数据来源：`<data_dir>/metadata.json`

额外约定：

- `metadata.json` 中必须给出 `years` 与 `variables`
- 每个时刻文件中的 `fields` 形状为 `[Channels, Height, Width]`

## 样本构造方式

- 输入样本：`[input_steps, Channels, Height, Width]`
- 输出样本：`[output_steps, Channels, Height, Width]`
- 附加返回项：`cos_zenith`, `step_idx`, `time_index`

具体做法：

- 先从 `metadata.json` 读取可用年份和变量列表
- 再根据 `train_ratio`、`val_ratio`、`test_ratio` 计算当前模式下选用的年份
- 对每个年份目录下的时刻文件做排序，并按连续文件窗口切片
- 根据 `channels` 建立变量索引，只保留当前任务需要的通道
- 先把 NaN 替换为对应通道均值，再使用统计量做标准化
- 同时基于经纬度网格与时间戳构造太阳天顶角

## DataLoader 约定

- 训练阶段：若启用分布式训练，使用 `DistributedSampler(shuffle=True)`
- 验证阶段：不打乱，分布式时使用 `DistributedSampler(shuffle=False)`
- 测试阶段：不打乱，`batch_size` 与 `pin_memory` 由外部 dataloader 配置控制

## 适合优先使用的场景

- 使用 CMEMS 海洋数据训练全球海洋预测模型
- 原始数据已经按年份目录和时刻文件整理完成
- 需要在 datapipe 内部直接完成 train / val / test 年份划分

## 风险点

- 年份划分当前仍依赖 `train_ratio`、`val_ratio`、`test_ratio`，而不是显式年份列表
- 代码默认假设每个选中年份包含相同数量的可用样本，若不同年份文件数不同，索引逻辑需要额外确认
- 数据目录强依赖 `h5/<year>/*.h5`、`metadata.json` 与 `stats/` 结构
- 时间索引直接从文件名尾部切片得到，文件命名规则变化时需要同步修改

## 源码锚点

- `./onescience/src/onescience/datapipes/climate/cmems.py`
- `./onescience/src/onescience/datapipes/climate/utils/invariant.py`
- `./onescience/src/onescience/datapipes/climate/utils/zenith_angle.py`


path: onescience-coder/assets/datapipes/datapipe_index.md
content: # Datapipe Index

## 目标

本文档用于登记当前已经补充到 `oneskills` 的数据处理与 `DataLoader` 知识。
它和 `models/`、`contracts/` 的分工不同：

- `models/`
  - 解决整模型结构、输入输出组织和组件链路
- `contracts/`
  - 解决模块如何初始化、关键参数与 `shape` 约束
- `datapipes/`
  - 解决数据如何读取、如何构造样本，以及 train / val / test 的 `DataLoader` 约定

## 建议使用方式

推荐在以下情况优先读取本文档：

1. 用户要求设计数据读取流程或 `DataLoader`
2. 任务涉及样本切分、变量选择、网格组织或 batch 结构
3. 任务需要确认 datapipe 最终输出给模型的样本格式

推荐检索顺序：

1. 若任务主要是模型结构改造，先看 `models/` 和 `contracts/`
2. 若任务已经涉及数据组织，再看本文档
3. 先读具体 datapipe 卡片
4. 卡片信息不足时，再回到 `./onescience/` 中对应 datapipe 源码

## 当前已登记 Datapipe

| Datapipe | 数据类型 | 数据组织方式 | 样本粒度 | 主要配置 | 状态 | 文档 |
|---|---|---|---|---|---|---|
| ERA5Datapipe | 全球大气再分析数据 | 每年一个 HDF5 文件 | 连续时间窗样本 | `dataset_dir`, `used_years`, `used_variables`, `input_steps`, `output_steps`, `normalize` | `stable` | `./datapipes/era5.md` |
| CMEMSDatapipe | 全球海洋再分析 / 预报数据 | 按年份目录、按时刻 HDF5 文件 | 连续时间窗样本 | `data_dir`, `channels`, `train_ratio/val_ratio/test_ratio`, `input_steps`, `output_steps` | `stable` | `./datapipes/cmems.md` |
| AirfRANSDatapipe | 二维翼型 CFD 数据 | 每个工况一个目录，含 `manifest.json`、`.vtu` 与 `.vtp` | 单仿真图样本 | `source.data_dir`, `data.splits.*`, `data.sampling.*`, `data.subsampling`, `model_hparams.build_graph` | `stable` | `./datapipes/airfrans.md` |
| ShapeNetCarDatapipe | 三维车体表面 CFD 数据 | `param*/sample/` 目录下成对 `quadpress_smpl.vtk` / `hexvelo_smpl.vtk`，可选预处理缓存 | 单仿真图样本 | `source.data_dir`, `source.preprocessed_save_dir`, `data.splits.fold_id`, `model_hparams.cfd_mesh/r/max_neighbors` | `stable` | `./datapipes/shapenetcar.md` |
| DeepCFDDatapipe | 规则网格稳态流场配对数据 | 同目录下成对 `dataX.pkl` / `dataY.pkl` | 单样本图像张量 | `source.data_dir`, `source.data_x_name`, `source.data_y_name`, `data.split_ratio`, `data.seed`, `batch_size` | `stable` | `./datapipes/deepcfd.md` |
| DeepMind_CylinderFlowDatapipe | 非结构网格圆柱绕流时序图数据 | `meta.json + train/valid/test.tfrecord` | 单轨迹-单时间步 DGL 图样本 | `source.data_dir`, `source.stats_dir`, `data.train_steps/val_steps/test_steps`, `noise_std`, `batch_size` | `stable` | `./datapipes/deepmind_cylinderflow.md` |
| TJDatapipe | 中科天机气象模拟数据 | 按批次时间戳命名的 `.nc` 文件目录 | 连续时间窗样本 | `path`, `used_variables`, `start_time`, `end_time`, `input_steps`, `output_steps`, `normalize`, `batch_size` | `stable` | `./datapipes/tjweather.md` |

## 未登记 Datapipe 的处理方式

如果用户提供的是 `oneskills` 尚未登记的新数据集，不要跳过数据层设计。推荐做法：

1. 先读取 `./task/new_dataset_workflow.md`
2. 根据数据组织方式，从当前已登记 datapipe 中选择最接近的参考模板
3. 先把新数据集映射成目标模型真正需要的样本格式，再决定是否需要新建 datapipe
4. 若任务还要求复用某个已有模型或 example，再继续读取对应模型卡和示例工程

若新数据集以 `vtk / vtu / vtp` 结果文件为主，优先比较：

1. `./datapipes/airfrans.md`
2. `./datapipes/shapenetcar.md`
3. 目标模型对应的数据协议是否更接近 `PyG Data` 还是 `DGLGraph`

## 跨流程复用时的兼容性检查

如果任务要求复用一个已有 datapipe 去对接另一套模型或训练流程，优先检查：

- datapipe 的样本返回格式是否兼容
- `DataLoader` 的 batch 结构是否兼容
- 训练脚本的 batch 解包方式是否兼容
- 模型要求的输入输出协议是否兼容
- 是否需要新增最小桥接层，例如 adapter datapipe

第一轮规格里应先把这些兼容性判断写清楚，再进入代码生成。

## Datapipe 层的核心问题

优先用 datapipe 卡片解决下面这些问题：

- 数据按什么粒度存储
- 样本如何切分
- 变量如何映射到通道索引
- datapipe 最终返回给模型的张量形状是什么
- 是否包含额外几何量、边信息或位置编码
- `DataLoader` 在 train / val / test 阶段分别怎样组织

## 新增 Datapipe 文档的维护建议

新增 datapipe 时，建议至少完成以下内容：

1. 复制 `./datapipes/TEMPLATE.md`
2. 填写数据目录结构、输入配置、样本构造方式、风险点和源码锚点
3. 在本文档中登记
4. 检查 `README.md`、`task/SKILL.md`、`DEVELOPER_MANUAL.md` 是否需要同步更新


path: onescience-coder/assets/datapipes/deepcfd.md
content: # Datapipe: DeepCFDDatapipe

## 基本信息

- Datapipe 名称：`DeepCFDDatapipe`
- 数据类型：`cfd`
- 主要任务：`steady 2d flow-field surrogate regression`
- 数据组织方式：`paired_pickle_arrays_loaded_in_memory`

## Datapipe 职责

`DeepCFDDatapipe` 负责把 `dataX.pkl` 和 `dataY.pkl` 中按样本堆叠的规则网格数据读入内存，完成随机划分，并组织成可直接供 DeepCFD 训练脚本消费的字典样本与 `DataLoader`。

补充说明：

- 原始数据目录默认只要求一个根目录，里面至少包含一对输入/输出 pickle 文件
- datapipe 负责读文件、打乱索引、按 `split_ratio` 划分训练与测试，并计算输出通道的 loss 权重
- datapipe 同时实现了 `DeepCFDDataset` 与 `DeepCFDDatapipe` 两层接口

## 输入配置

- `source.data_dir`
  - DeepCFD 数据根目录
- `source.data_x_name`
  - 输入文件名，默认示例为 `dataX.pkl`
- `source.data_y_name`
  - 输出文件名，默认示例为 `dataY.pkl`
- `data.split_ratio`
  - 训练集比例；剩余样本同时被当前实现当作 `test/val`
- `data.seed`
  - 划分前随机打乱的种子
- `dataloader.batch_size`
  - 批大小
- `dataloader.num_workers`
  - `DataLoader` 工作进程数

## 数据存储约定

- 主数据路径：`<data_dir>/<data_x_name>` 与 `<data_dir>/<data_y_name>`
- 统计量路径：`none`
- 元数据来源：pickle 数组自身的 shape 与样本顺序

额外约定：

- `dataX.pkl` 与 `dataY.pkl` 的样本数必须完全一致
- 当前实现默认 `dataY.pkl` 的输出通道数为 3，loss 权重计算里写死按 3 个通道展开

## 样本构造方式

- 输入样本：`sample["x"] -> (ChannelsIn, Height, Width)`，DeepCFD README 示例中为 3 通道输入
- 输出样本：`sample["y"] -> (ChannelsOut, Height, Width)`，DeepCFD README 示例中为 3 通道输出 `[Ux, Uy, p]`
- 附加返回项：`none`

具体说明：

- 单个 dataset 样本对应一个稳态流场样本，而不是时间窗序列
- `__getitem__` 返回普通字典：`{"x": tensor, "y": tensor}`
- 训练脚本按图像/规则网格方式直接消费 batch，不经过 `PyG Data`
- datapipe 还会从完整 `y` 张量统计 RMS 风格的 `channel_weights`，供训练脚本构造加权损失

## DataLoader 约定

- 训练阶段：`train_dataloader()` 返回 `(DataLoader, sampler)`；非分布式时 `shuffle=True`
- 验证阶段：`none`，当前 datapipe 没有单独 `val_dataloader()`
- 测试阶段：`test_dataloader()` 返回 `(DataLoader, sampler)`；非分布式时 `shuffle=False`

补充说明：

- `DeepCFDDataset(mode="val")` 虽然可构造，但和 `mode="test"` 使用的是同一段剩余划分
- 示例工程通常把 `test_dataloader()` 既当评估集也当测试集

## 适合优先使用的场景

- 直接复用 `./onescience/examples/cfd/DeepCFD/` 中的 `UNet` / `UNetEx` 训练流程
- 新数据集本身就是规则网格上的 `X -> Y` 配对回归任务
- 输入输出更接近图像张量，而不是点云图或 `(pos, fx, y)` 三元组

## 风险点

- 当前实现没有真正独立的 `val` 划分；`val` 与 `test` 会落在同一批样本上
- 全量 `pickle` 会一次性读入内存，数据规模增大时容易出现内存压力
- `channel_weights` 的实现默认输出通道数为 3；如果新任务目标变量数变化，需要同步改动 datapipe 或训练脚本
- 它不能直接对接 `CFD_Benchmark` 这类要求 `(pos, fx, y)` 批格式的算子训练流程；若要比较 `FNO/U_FNO/U_NO/U_Net`，通常需要额外桥接一个 DeepCFD-to-operator adapter datapipe

## 源码锚点

- `./onescience/src/onescience/datapipes/cfd/deepcfd.py`
- `./onescience/examples/cfd/DeepCFD/train.py`
- `./onescience/examples/cfd/DeepCFD/conf/deepcfd.yaml`


path: onescience-coder/assets/datapipes/deepmind_cylinderflow.md
content: # Datapipe: DeepMind_CylinderFlowDatapipe

## 基本信息

- Datapipe 名：`DeepMind_CylinderFlowDatapipe`
- 数据类型：`cfd`
- 主要任务：`transient graph-based cylinder-flow rollout regression`
- 数据组织方式：`meta_json_plus_train_valid_test_tfrecord`

## Datapipe 职责

`DeepMind_CylinderFlowDatapipe` 负责把 DeepMind 涡激数据集的 `TFRecord` 时序样本读成 DGL 图，构造归一化统计量，并封装成现有 `Vortex_shedding_mgn` 训练流程可直接消费的 `GraphDataLoader`。

补充说明：

- 原始数据目录至少要求一个 `meta.json`，以及 `train.tfrecord`、`valid.tfrecord`、`test.tfrecord`
- datapipe 负责 TFRecord 解析、边特征构造、节点特征与目标构造、训练噪声注入、统计量保存和 DGL 图装配
- datapipe 同时实现了 `DeepMind_CylinderFlowDataset` 与 `DeepMind_CylinderFlowDatapipe` 两层接口

## 输入配置

- `source.data_dir`
  - 数据根目录，内部需要有 `meta.json` 和各 split 的 `.tfrecord`
- `source.stats_dir`
  - 归一化统计量保存目录，内部会写 `edge_stats.json` 和 `node_stats.json`
- `data.train_samples`
  - 训练轨迹数量
- `data.train_steps`
  - 每条训练轨迹保留的时间步数
- `data.val_samples`
  - 验证轨迹数量
- `data.val_steps`
  - 每条验证轨迹保留的时间步数
- `data.test_samples`
  - 测试轨迹数量
- `data.test_steps`
  - 每条测试轨迹保留的时间步数
- `data.noise_std`
  - 训练阶段节点速度噪声标准差
- `dataloader.batch_size`
  - DGL `GraphDataLoader` 批大小
- `dataloader.num_workers`
  - DGL `GraphDataLoader` 工作进程数

## 数据存储约定

- 主数据路径：`<data_dir>/meta.json`、`<data_dir>/train.tfrecord`、`<data_dir>/valid.tfrecord`、`<data_dir>/test.tfrecord`
- 统计量路径：`<stats_dir>/edge_stats.json` 与 `<stats_dir>/node_stats.json`
- 元数据来源：`meta.json` 中的 `field_names` / `features` 描述，以及 TFRecord 中的轨迹字段

额外约定：

- `val` 模式在实现内部会映射到 `valid.tfrecord`
- datapipe 依赖 `tensorflow` 解析 TFRecord，同时依赖 `dgl`
- 非训练 split 要求事先已有训练阶段保存的统计量

## 样本构造方式

- 输入样本：训练阶段返回 `DGLGraph`，其中 `graph.ndata["x"] -> [NumNodes, 6]`，通常是 `2 维速度 + 4 维节点类型 one-hot`
- 输出样本：`graph.ndata["y"] -> [NumNodes, 3]`，通常是 `2 维目标速度项 + 1 维压力`
- 附加返回项：
  - 训练阶段：只返回 `graph`
  - 验证 / 测试阶段：返回 `(graph, cells, mask)`

具体说明：

- 一个 dataset item 对应“某条轨迹的某个时间步”，而不是整个轨迹
- `gidx = idx // (num_steps - 1)`，`tidx = idx % (num_steps - 1)`，因此单个轨迹会被拆成多个时序图样本
- 边特征默认来自节点间相对位移和距离范数
- 训练阶段会对部分节点速度特征加噪声，以增强鲁棒性

## DataLoader 约定

- 训练阶段：`train_dataloader()` 返回 `(GraphDataLoader, sampler)`；分布式时使用 `DistributedSampler(shuffle=True)`
- 验证阶段：`val_dataloader()` 返回 `(GraphDataLoader, sampler)`；分布式时使用 `DistributedSampler(shuffle=False)`
- 测试阶段：`test_dataloader()` 只返回 `GraphDataLoader`

补充说明：

- 训练 / 验证 / 测试的 batch 解包协议并不完全一致，训练脚本和推理脚本都依赖这个差异
- 这套 datapipe 是当前 `Vortex_shedding_mgn` 路线的重要约束，不是一个通用 DGL 图模板

## 适合优先使用的场景

- 希望沿用 `./onescience/examples/cfd/Vortex_shedding_mgn/` 中的 DGL + MeshGraphNet 训练流程
- 数据本身是时序轨迹数据，且每个样本天然有图拓扑和节点类型
- 任务更接近“图上的下一时刻状态预测 / rollout”，而不是静态单样本几何回归

## 风险点

- 依赖 `tensorflow` 与 `dgl`；如果环境没有这两者，现有 datapipe 路线无法直接工作
- 现有样本协议默认是时序 rollout 图，不适合直接套用到单个稳态 VTK 结果文件数据集
- 验证 / 测试阶段会返回 `(graph, cells, mask)`，如果新 datapipe 不保留这个协议，现有 MGN 推理脚本需要同步改
- 节点输入、目标和边特征维度都被当前示例配置显式假设；新数据集字段一旦变化，datapipe 与模型配置都要一起改

## 源码锚点

- `./onescience/src/onescience/datapipes/cfd/deepmind_cylinderflow.py`
- `./onescience/examples/cfd/Vortex_shedding_mgn/train.py`
- `./onescience/examples/cfd/Vortex_shedding_mgn/conf/mgn_cylinderflow.yaml`


path: onescience-coder/assets/datapipes/era5.md
content: # Datapipe: ERA5Datapipe

## 基本信息

- Datapipe 名：`ERA5Datapipe`
- 数据类型：`climate`
- 主要任务：`global weather forecast`
- 数据组织方式：`per_year_h5`

## Datapipe 职责

`ERA5Datapipe` 负责从 OneScience 数据卡中读取已配置的 ERA5 气象数据，并切成连续时间窗样本，组织为适合天气预测模型训练或推理的 `DataLoader`。

重要说明：

- **数据已在数据卡中配置**：使用时直接引用数据卡中的数据路径和元数据，无需手动指定数据位置
- **数据卡自动注入**：数据路径、统计量路径、变量列表等信息自动从数据卡中读取
- **数据已就绪**：数据卡中定义的数据已在 `{ONESCIENCE_DATASETS_DIR}` 下存储，无需额外生成测试数据
- datapipe 负责变量筛选、时间窗切片、归一化、太阳天顶角构造和 `DataLoader` 组装
- train / val / test 的年份划分不在 datapipe 内部完成，而是由外部通过 `used_years` 显式传入

## 输入配置

- `dataset_dir`
  - ERA5 数据根目录（**从数据卡中自动读取，无需手动指定**）
- `used_years`
  - 当前 datapipe 使用的年份列表
- `used_variables`
  - 当前任务需要读取的变量名列表（**可从数据卡元数据中获取**）
- `input_steps`
  - 输入时间步数
- `output_steps`
  - 预测时间步数
- `normalize`
  - 是否使用统计量做标准化
- `batch_size`
  - `DataLoader` 批大小
- `num_workers`
  - `DataLoader` 工作进程数

## 数据存储约定

- 主数据路径：`{ONESCIENCE_DATASETS_DIR}/ERA5/newh5/data_merged/<year>.h5`
- 统计量路径：`{ONESCIENCE_DATASETS_DIR}/ERA5/newh5/stats/global_means.npy` 与 `global_stds.npy`
- 元数据来源：`fields` 数据集的 `variables` 与 `time_step` 属性

额外约定：

- 单个年份文件中的 `fields` 形状为 `[TimeSteps, Channels, Height, Width]`
- 所选变量必须都存在于 `fields.attrs["variables"]`

## 样本构造方式

- 输入样本：`[input_steps, Channels, Height, Width]`
- 输出样本：`[output_steps, Channels, Height, Width]`
- 附加返回项：`cos_zenith`, `step_idx`, `time_index`

具体做法：

- 先按 `used_years` 建立年份到文件路径的映射
- 再从每个年份文件中按连续时间步切出长度为 `input_steps + output_steps` 的窗口
- 通过 `used_variables` 建立通道索引，仅保留当前任务需要的变量
- 若 `normalize=True`，使用 `global_means.npy` 与 `global_stds.npy` 对输入和输出做标准化
- 根据时间戳和经纬度网格计算太阳天顶角

## DataLoader 约定

- 训练阶段：若启用分布式训练，使用 `DistributedSampler(shuffle=True)`；否则由 `DataLoader` 本身随机打乱
- 验证阶段：不打乱，分布式时使用 `DistributedSampler(shuffle=False)`
- 测试阶段：不打乱，接口仍通过 `get_dataloader(mode)` 统一获取

## 适合优先使用的场景

- 使用 ERA5 年尺度 HDF5 数据训练全球天气预报模型
- 需要显式控制训练、验证、测试所使用的年份集合
- 需要把数据直接组织成 `[TimeSteps, Channels, Height, Width]` 的连续时间窗

## 风险点

- 当前 train / val / test 划分不在 datapipe 内部处理，调用方必须为不同阶段显式传入对应 `used_years`
- 数据目录强依赖 `data_merged/<year>.h5` 与 `stats/` 结构，路径不一致时会直接失败
- 变量名依赖 HDF5 属性中的 `variables`，若数据构建时未正确写入属性，会导致变量校验失败
- `time_index` 与 `cos_zenith` 的时间步基于年份起始时间和 `time_step` 计算，若文件内部时间轴与该假设不一致，需要回到源码核对

## 源码锚点

- `./onescience/src/onescience/datapipes/climate/era5.py`
- `./onescience/src/onescience/datapipes/climate/utils/invariant.py`
- `./onescience/src/onescience/datapipes/climate/utils/zenith_angle.py`


path: onescience-coder/assets/datapipes/shapenetcar.md
content: # Datapipe: ShapeNetCarDatapipe

## 基本信息

- Datapipe 名：`ShapeNetCarDatapipe`
- 数据类型：`cfd`
- 主要任务：`3d surface flow / pressure regression`
- 数据组织方式：`per_simulation_dir_with_vtk_or_preprocessed_npy`

## Datapipe 职责

`ShapeNetCarDatapipe` 负责把按参数文件夹组织的汽车外流场 VTK 结果组织成 `PyG Data` 图样本，并封装成现有 `Transolver-Car-Design` 训练流程可直接消费的 `DataLoader`。

补充说明：

- 原始数据通常以 `param*/sample/` 目录组织，每个样本至少包含一对 `quadpress_smpl.vtk` 和 `hexvelo_smpl.vtk`
- datapipe 负责 fold 划分、VTK 读取、法向量与 SDF 构造、表面/外部点拼接、可选读取预处理缓存、归一化和图构建
- datapipe 同时实现了 `ShapeNetCarDataset` 与 `ShapeNetCarDatapipe` 两层接口

## 输入配置

- `source.data_dir`
  - 原始数据根目录
- `source.preprocessed_save_dir`
  - 预处理缓存目录；若存在 `.npy` 缓存可优先读取
- `source.stats_dir`
  - 归一化统计量目录，保存 `mean/std` 数组
- `source.preprocessed`
  - 是否优先读取预处理缓存
- `data.splits.fold_id`
  - 交叉验证折号；当前实现按 `param0` 到 `param8` 做 fold 划分
- `dataloader.batch_size`
  - PyG `DataLoader` 批大小
- `dataloader.num_workers`
  - PyG `DataLoader` 工作进程数
- `model_hparams.cfd_mesh`
  - 是否使用原始 CFD 网格边
- `model_hparams.r`
  - 若不使用原始网格，`radius_graph` 的半径
- `model_hparams.max_neighbors`
  - 半径图最大邻居数

## 数据存储约定

- 主数据路径：`<data_dir>/param*/<sample>/quadpress_smpl.vtk` 与 `<data_dir>/param*/<sample>/hexvelo_smpl.vtk`
- 预处理缓存：`<preprocessed_save_dir>/<param*>/<sample>/{x,y,pos,surf,edge_index}.npy`
- 统计量路径：`<stats_dir>/mean_in.npy`、`std_in.npy`、`mean_out.npy`、`std_out.npy`
- 元数据来源：目录分层、VTK 点坐标、VTK 中的速度/压力向量场以及内部构造的法向量与 SDF

额外约定：

- 当前实现默认存在 `param0` 到 `param8` 九个参数分组目录
- `quadpress_smpl.vtk` 与 `hexvelo_smpl.vtk` 必须能通过坐标关系拼成统一点集
- 若没有预处理缓存，首次读取时会直接从 VTK 计算并可选择保存为 `.npy`

## 样本构造方式

- 输入样本：`Data.x` 形状为 `[NumPoints, 7]`，语义通常是 `[pos_x, pos_y, pos_z, sdf, normal_x, normal_y, normal_z]`
- 输出样本：`Data.y` 形状为 `[NumPoints, 4]`，语义通常是 `[v_x, v_y, v_z, p]`
- 附加返回项：`Data.pos`、`Data.surf`、`Data.edge_index`

具体说明：

- 一个 dataset 样本对应一个汽车几何工况，而不是时间窗序列
- datapipe 会把外部速度场点和表面压力点拼成统一点集
- `surf` 用于区分表面点和非表面点，现有 `Transolver-Car-Design` 训练脚本会对表面压力损失单独加权
- 若 `model_hparams.cfd_mesh=False`，会基于 `Data.pos` 重新构建 `radius_graph`
- 若 `model_hparams.cfd_mesh=True`，会复用从 CFD 网格恢复出的 `edge_index`

## DataLoader 约定

- 训练阶段：`train_dataloader()` 返回 `(PyGDataLoader, sampler)`；分布式时使用 `DistributedSampler(shuffle=True)`
- 验证阶段：`val_dataloader()` 返回 `(PyGDataLoader, sampler)`；分布式时使用 `DistributedSampler(shuffle=False)`
- 测试阶段：`none`；当前 datapipe 类没有单独暴露 `test_dataloader()`

补充说明：

- 当前实现里 `mode="test"` 存在，但 `ShapeNetCarDatapipe` 默认只构造 train / val 两个 loader
- 现有 `Transolver-Car-Design` 示例主要沿用 fold 划分下的训练/验证逻辑

## 适合优先使用的场景

- 新数据集由一批 `vtk` 流场结果文件组成，目标是做三维表面或几何相关物理量回归
- 希望把样本组织成 `PyG Data(pos, x, y, surf, edge_index)` 供 Transolver 一类点云 / 图模型使用
- 用户希望沿用 `./onescience/examples/cfd/Transolver-Car-Design/` 这类几何回归训练流程

## 风险点

- 当前划分逻辑依赖 `param0` 到 `param8` 的目录结构；若新数据集没有这层分组，需要重写 split 规则
- `Transolver-Car-Design` 的损失函数对 `surf` 和最后一个压力通道有显式假设，新数据集目标变量变动时要同步检查 train / inference
- 若新数据集只有表面点而没有外部体点，或字段语义和 `quadpress/hexvelo` 不同，不能机械复用 `x/y` 组织方式
- datapipe 默认是 `PyG Data` 协议，不适合直接喂给 DGL 版 MeshGraphNet

## 源码锚点

- `./onescience/src/onescience/datapipes/cfd/ShapeNetCar.py`
- `./onescience/examples/cfd/Transolver-Car-Design/train.py`
- `./onescience/examples/cfd/Transolver-Car-Design/conf/transolver_car.yaml`


path: onescience-coder/assets/datapipes/TEMPLATE.md
content: # Datapipe: <DatapipeName>

## 基本信息

- Datapipe 名：`<DatapipeName>`
- 数据类型：`<climate_or_other>`
- 主要任务：`<forecast_or_reconstruction_or_other>`
- 数据组织方式：`<per_year_h5_or_per_timestamp_h5_or_other>`

## Datapipe 职责

一句话说明该 datapipe 负责把什么原始数据组织成什么样的训练样本。

补充说明：

- 数据存储目录的基本结构
- 负责哪些预处理步骤
- 是否同时负责 `Dataset` 与 `DataLoader`

## 输入配置

- `<arg_1>`
  - `<说明>`
- `<arg_2>`
  - `<说明>`
- `<arg_3>`
  - `<说明>`

建议优先记录：

- 数据目录
- 年份选择
- 变量选择
- `input_steps`
- `output_steps`
- 归一化或缺失值处理方式
- `batch_size`
- `num_workers`

## 数据存储约定

- 主数据路径：`<path_pattern>`
- 统计量路径：`<stats_pattern>`
- 元数据来源：`<metadata_or_attrs>`

若有额外约定，也写清楚：

- `<constraint_1>`
- `<constraint_2>`

## 样本构造方式

- 输入样本：`<input_shape_and_semantics>`
- 输出样本：`<output_shape_and_semantics>`
- 附加返回项：`<extra_outputs>`

建议说明：

- 时间步如何切片
- 变量如何索引
- 是否裁剪空间尺寸
- 是否额外生成物理先验量，例如太阳天顶角

## DataLoader 约定

- 训练阶段：`<train_loader_behavior>`
- 验证阶段：`<val_loader_behavior>`
- 测试阶段：`<test_loader_behavior>`

## 适合优先使用的场景

- `<scene_1>`
- `<scene_2>`
- `<scene_3>`

## 风险点

- `<risk_1>`
- `<risk_2>`
- `<risk_3>`

优先写：

- 数据目录结构是否固定
- train/val/test 划分由谁负责
- 是否假设每年样本数一致
- 是否会受闰年、缺测、NaN 或变量命名影响

## 源码锚点

- `./onescience/<path_to_primary_impl>.py`
- `./onescience/<path_to_related_utils>.py`

统一约定：

- 一律使用 `./onescience/...` 相对路径
- 不写本机绝对路径
- 最多放 2 到 4 个高价值锚点


path: onescience-coder/assets/datapipes/tjweather.md
content: # Datapipe: TJWeather

## 基本信息 

- Datapipe 名：`TJWeather`
- 数据集名称: 中科天机数据集
- 数据格式: NetCDF (.nc)
- 空间分辨率: 0.025°\~0.1°（视子集而定）
- 时间分辨率: 1小时 / 6小时（视子集而定）
- 数据类型：`climate`
- 主要任务：`forecast`
- 数据组织方式：`per_timestamp_nc`

## Datapipe 职责

将按年份目录存储的逐时间步 NetCDF 文件，组织成多步输入 / 多步输出的气象预报训练样本。

- 数据目录结构：`<data_dir>/data/<year><timestamp>.nc`，每个 `.nc` 文件对应一个时间步
- 负责变量筛选、归一化（z-score）、空间裁剪（patch 对齐）、cos 太阳天顶角生成
- 同时负责 `Dataset`（`TJDataset`）与 `DataLoader`（`TJDatapipe`）

## 数据根目录

onescience 平台数据根目录通过环境变量获取，天机数据根目录及完整路径构造如下：

```python
import os
# 天机数据根目录
data_root = os.path.join(os.environ.get("ONESCIENCE_DATASETS_DIR"), "TJWeather")
# dataset_type 须从用户输入解析，取值：TJ1-CN | TJ-CN | TJ1-GB | TJ-GB
data_dir = os.path.join(os.environ.get("ONESCIENCE_DATASETS_DIR"), "TJWeather", dataset_type)
```

## 子数据集说明

| 数据集    | 覆盖范围                        | 分辨率            | 时间分辨率   | 预报时效      |
| ------ | --------------------------- | -------------- | ------- | --------- |
| TJ1-CN | 中国区域（65°E\~145°E，5°N\~65°N） | 0.033°（约3km）   | 1小时     | 未来15天，逐小时 |
| TJ-CN  | 中国区域（65°E\~145°E，5°N\~65°N） | 0.025°（约2.5km） | 1小时     | 未来10天，逐小时 |
| TJ1-GB | 全球（0°\~360°E，-90°\~90°N）    | 0.1°（约12km）    | 1小时     | 未来15天，逐小时 |
| TJ-GB  | 全球（0°\~360°E，-90°\~90°N）    | 0.1°（约12km）    | 1小时/6小时 | 未来46天     |

## 标准目录结构

```
${ONESCIENCE_DATASETS_DIR}/TJWeather/
└── {dataset_type}/
    ├── stats/
    │   ├── global_means.nc
    │   └── global_stds.nc
    ├── static/
    │   ├── geopotential/
    │   ├── land_mask/
    │   ├── land_sea_mask/
    │   ├── soil_type/
    │   └── topography/
    └── data/
        ├── {year}{timestamp}.nc    # 示例：20260409000297.nc
        └── ...
```

## NC 文件内部结构

```
<xarray.Dataset>
Size: 2GB
Dimensions:  (time: 32, lat: xx, lon: xx)
Coordinates:
  * time     (time) datetime64[ns] 256B 2026-02-10T06:00:00 ... 2026-02-18
  * lat      (lat) float32 7kB -89.95 -89.85 -89.75 -89.65 ... 89.75 89.85 89.95
  * lon      (lon) float32 14kB 0.05 0.15 0.25 0.35 ... 359.6 359.8 359.9 360.0
Data variables:
    base_reflectivity  (time, lat, lon) float32
    bdsf_ave           (time, lat, lon) float32
    cape               (time, lat, lon) float32
    cldh               (time, lat, lon) float32
    cldl               (time, lat, lon) float32
Attributes:
    title:        TJ1-GB
    institution:  TianJi Weather Science and Technology Company
    source:       the Super Dynamics on Cube, Tianji Weather System
    references:   https://www.tjweather.com
    license:      CC BY-NC 4.0
```

- **单个数据文件**:
  - 维度: `[T, H, W]`（每变量），堆叠后为 `[C, T, H, W]`
    - C: 变量数量
    - T: 时间步数
    - H: 纬度方向像素数
    - W: 经度方向像素数
  - 变量顺序: 严格按照 `used_variables` 列表顺序

## 输入配置

| 参数                                                        | 说明                                             |
| --------------------------------------------------------- | ---------------------------------------------- |
| `params.dataset.data_dir`                                 | 数据根目录，含 `data/<year><timestamp>.nc` 子路径        |
| `params.dataset.stats_dir`                                | 统计量目录，含 `global_means.npy` / `global_stds.npy` |
| `params.dataset.channels`                                 | 需要使用的变量名列表                                     |
| `params.dataset.train_years` / `val_years` / `test_years` | 各阶段年份列表（必须显式指定）                                |
| `params.dataset.time_res`                                 | 时间分辨率（小时）                                      |
| `params.dataset.img_size`                                 | 空间尺寸，用于生成 latlon 网格                            |
| `input_steps`                                             | 输入时间步数，默认 1                                    |
| `output_steps`                                            | 输出时间步数，默认 1                                    |
| `normalize`                                               | 是否 z-score 归一化，默认 `True`                       |
| `params.dataloader.batch_size`                            | 训练/验证 batch size                               |
| `params.dataloader.num_workers`                           | DataLoader 工作进程数                               |

## 数据存储约定

- 主数据：`<data_dir>/data/<year><timestamp>.nc`
- 统计量：`<stats_dir>/global_means.npy`、`<stats_dir>/global_stds.npy`
- 每个 `.nc` 文件统计量形状为 `(1, num_all_channels, H, W)`，按 `channel_indices` 切片后使用

## 支持的变量（以 TJ1-GB 为例）

| 变量名                | 描述            | 单位        | 备注  |
| ------------------ | ------------- | --------- | --- |
| base\_reflectivity | 基本反照率         | dBZ       | 地面层 |
| bdsf\_ave          | 直接辐射          | W/m²      | 地面层 |
| cape               | 对流有效位能        | J/kg      | 地面层 |
| cldh               | 高云量           | %         | 地面层 |
| cldl               | 低云量           | %         | 地面层 |
| cldm               | 中云量           | %         | 地面层 |
| cldt               | 总云量           | %         | 地面层 |
| dpt2m              | 2米露点温度        | K         | 地面层 |
| DSWRFsfc           | 地表向下短波通量      | W/m²      | 地面层 |
| gust               | 阵风            | m/s       | 地面层 |
| max\_reflectivity  | 最大反照率         | dBZ       | 地面层 |
| PRATEsfc           | 地表总降水率        | kg/(m²·s) | 地面层 |
| preg               | 霰降水量（累积值）     | kg/m²     | 地面层 |
| prei               | 降冰量（累积量）      | kg/m²     | 地面层 |
| prer               | 降雨量（累积值）      | kg/m²     | 地面层 |
| pres               | 降雪量（累积值）      | kg/m²     | 地面层 |
| psz                | 订正后地表气压       | Pa        | 地面层 |
| qnh                | 修正海平面气压       | Pa        | 地面层 |
| rh2m               | 2米相对湿度        | %         | 地面层 |
| ri\_min            | 理查德森数         | -         | 地面层 |
| slp                | 海平面气压         | Pa        | 地面层 |
| SPFH2m             | 2米比湿          | kg/kg     | 地面层 |
| t2m\_1km           | 2米气温（分辨率为1千米） | K         | 地面层 |
| t2mz               | 2米气温          | K         | 地面层 |
| TMPsfc             | 地表温度          | K         | 地面层 |
| u100m              | 100米纬向风       | m/s       | 地面层 |
| u110m              | 110米纬向风       | m/s       | 地面层 |
| u120m              | 120米纬向风       | m/s       | 地面层 |
| u130m              | 130米纬向风       | m/s       | 地面层 |
| u140m              | 140米纬向风       | m/s       | 地面层 |
| u150m              | 150米纬向风       | m/s       | 地面层 |
| u160m              | 160米纬向风       | m/s       | 地面层 |
| u170m              | 170米纬向风       | m/s       | 地面层 |
| u30m               | 30米纬向风        | m/s       | 地面层 |
| u50m               | 50米纬向风        | m/s       | 地面层 |
| u60m               | 60米纬向风        | m/s       | 地面层 |
| u70m               | 70米纬向风        | m/s       | 地面层 |
| u80m               | 80米纬向风        | m/s       | 地面层 |
| u90m               | 90米纬向风        | m/s       | 地面层 |
| UGRD10m            | 10米纬向风        | m/s       | 地面层 |
| v100m              | 100米经向风       | m/s       | 地面层 |
| v110m              | 110米经向风       | m/s       | 地面层 |
| v120m              | 120米经向风       | m/s       | 地面层 |
| v130m              | 130米经向风       | m/s       | 地面层 |
| v140m              | 140米经向风       | m/s       | 地面层 |
| v150m              | 150米经向风       | m/s       | 地面层 |
| v160m              | 160米经向风       | m/s       | 地面层 |
| v170m              | 170米经向风       | m/s       | 地面层 |
| v30m               | 30米经向风        | m/s       | 地面层 |
| v50m               | 50米经向风        | m/s       | 地面层 |
| v60m               | 60米经向风        | m/s       | 地面层 |
| v70m               | 70米经向风        | m/s       | 地面层 |
| v80m               | 80米经向风        | m/s       | 地面层 |
| v90m               | 90米经向风        | m/s       | 地面层 |
| VGRD10m            | 10米经向风        | m/s       | 地面层 |

## 样本构造方式

- 输入：`(input_steps, C, H, W)` — 连续 `input_steps` 个时间步，归一化后裁剪至 patch 对齐尺寸
- 输出：`(output_steps, C, H, W)` — 紧随其后的 `output_steps` 个时间步
- 附加返回：`cos_zenith (output_steps, H, W)`、`step_idx (int)`、`time_index (List[str])`
- 时间步切片：以全局 `idx` 通过 `bisect` 定位年份和年内偏移
- 空间裁剪：裁剪至 `patch_size` 整数倍，默认 `[1,1]` 即不裁剪
- 附加返回项：`cos_zenith (output_steps, H, W)`、`step_idx (int)`、`time_index (List[str])`

## DataLoader 约定

- 训练：`batch_size` 来自 `params.dataloader`，分布式用 `DistributedSampler(shuffle=True)`
- 验证：同训练，但 `shuffle=False`
- 测试：`batch_size=1`，`shuffle=False`，不用 `DistributedSampler`

## 风险点

- `train_years`/`val_years`/`test_years` 必须显式配置，不支持 ratio 方式
- 各年份文件数可能不同（闰年），`samples_per_year` 可能为 `None`
- 统计量切片依赖 `channel_indices` 与 `metadata.json` 顺序一致，变量顺序变更会导致归一化错误
- `__getitem__` 逐文件打开 `.nc`，高 `num_workers` 下 I/O 压力大

## 常见问题

| 症状                               | 解决方案                                          |
| -------------------------------- | --------------------------------------------- |
| `FileNotFoundError: 路径中无 .nc 文件` | 确认 `data_path` 为含 `.nc` 文件的目录                 |
| `ValueError: xxx 缺少变量`           | 检查 `used_variables` 与文件实际变量名一致                |
| OOM 错误                           | 用 `isel` 按需加载，减小 `batch_size` 或 `num_workers` |
| `KeyError: xxx 缺少坐标`             | 通过 `time_name`/`lat_name`/`lon_name` 自定义坐标名   |

## 源码锚点

- `./onescience/src/onescience/datapipes/climate/tjweather.py`
- `./onescience/src/onescience/datapipes/climate/utils/zenith_angle.py`
- `./onescience/src/onescience/datapipes/climate/utils/invariant.py`
- `./onescience/src/onescience/datapipes/core.py`

## 输出要求

- 输出符合用户需求的代码，包括 Dataset 类和 DataLoader 类
- 禁止生成 `get_dataloader` 或 `get_*_dataloader` 函数

## 参考资源

- [中科天机官方平台](https://www.tjweather.com)
- [xarray 文档](https://docs.xarray.dev)
- [PyTorch DataLoader 文档](https://pytorch.org/docs/stable/data.html)



path: onescience-coder/assets/contracts/component_index.md
content: # Component Index

## 目标

本文件是 `./contracts/` 的统一入口索引。

作用不是复述源码，而是帮助用户与智能体快速回答下面几个问题：

1. 当前任务应该先看哪个模块族
2. 某个组件应该从哪个 `One*` 入口初始化
3. `style` 应该写什么
4. 当前推荐优先使用哪些组件
5. 契约不足时，下一步应继续看哪一层

## 使用方式

建议按下面顺序检索：

1. 先根据任务语义，确定要查看的模块族
2. 先看高层业务组件，再决定是否继续下钻
3. 只有当高层组件不足以覆盖任务时，再看 `One*` 入口或底层模块
4. 契约仍不足时，再回到 `./onescience/` 对应源码锚点

默认不要一开始就直接检索底层 block 或 attention。

## 分层说明

当前索引按三层组织：

1. 统一入口层
   - `One*` 组件
   - 作用是统一调度、按 `style` 分发、提供调用入口
2. 高层业务组件层
   - 任务里最常直接拼装、替换、组合的组件
3. 底层通用模块层
   - block / attention / afno / fc 等下层实现单元

这三层在目录中保持平铺，在索引中进行逻辑分层。

## 统一入口层

| 组件 | 模块族 | 角色 | 注册方式 | 当前状态 | 契约卡片 |
|---|---|---|---|---|---|
| OneEmbedding | embedding | 统一 embedding 入口 | `style="<EmbeddingStyle>"` | `stable` | `./contracts/oneembedding.md` |
| OneSample | sample | 统一采样入口 | `style="<SampleStyle>"` | `stable` | `./contracts/onesample.md` |
| OneRecovery | recovery | 统一恢复入口 | `style="<RecoveryStyle>"` | `stable` | `./contracts/onerecovery.md` |
| OneFuser | fuser | 统一主干融合入口 | `style="<FuserStyle>"` | `stable` | `./contracts/onefuser.md` |
| OneEncoder | encoder | 统一 encoder 入口 | `style="<EncoderStyle>"` | `stable` | `./contracts/oneencoder.md` |
| OneDecoder | decoder | 统一 decoder 入口 | `style="<DecoderStyle>"` | `stable` | `./contracts/onedecoder.md` |
| OneTransformer | transformer | 统一 transformer 入口 | `style="<TransformerStyle>"` | `stable` | `./contracts/onetransformer.md` |
| OneAttention | attention | 统一 attention 入口 | `style="<AttentionStyle>"` | `stable` | `./contracts/oneattention.md` |
| OneAFNO | afno | 统一 AFNO 入口 | `style="<AFNOStyle>"` | `stable` | `./contracts/oneafno.md` |
| OneFC | fc | 统一前馈层入口 | `style="<FCStyle>"` | `stable` | `./contracts/onefc.md` |
| OneMlp | mlp | 统一 MLP 入口 | `style="<MlpStyle>"` | `stable` | `./contracts/onemlp.md` |
| OneFourier | fourier | 统一谱算子入口 | `style="<FourierStyle>"` | `stable` | `./contracts/onefourier.md` |
| OneHead | head | 统一预测头入口 | `style="<HeadStyle>"` | `stable` | `./contracts/onehead.md` |

## 高层业务组件层

这些组件更适合作为任务实现、模型拼装、模块替换时的优先检索对象。

| 组件 | 模块族 | 调用入口 | 注册名 | 输入形态摘要 | 当前状态 | 契约卡片 |
|---|---|---|---|---|---|---|
| PanguEmbedding | embedding | `OneEmbedding` | `PanguEmbedding` | 2D 场 / 3D 场 | `stable` | `./contracts/panguembedding.md` |
| FourCastNetEmbedding | embedding | `OneEmbedding` | `FourCastNetEmbedding` | 2D 场 | `stable` | `./contracts/fourcastnetembedding.md` |
| FuxiEmbedding | embedding | `OneEmbedding` | `FuxiEmbedding` | 3D 时空块 | `stable` | `./contracts/fuxiembedding.md` |
| PanguDownSample | sample | `OneSample` | `PanguDownSample` | 2D token / 3D token | `stable` | `./contracts/pangudownsample.md` |
| PanguUpSample | sample | `OneSample` | `PanguUpSample` | 2D token / 3D token | `stable` | `./contracts/panguupsample.md` |
| FuxiDownSample | sample | `OneSample` | `FuxiDownSample` | 2D 特征图 | `stable` | `./contracts/fuxidownsample.md` |
| FuxiUpSample | sample | `OneSample` | `FuxiUpSample` | 2D 特征图 | `stable` | `./contracts/fuxiupsample.md` |
| PanguPatchRecovery | recovery | `OneRecovery` | `PanguPatchRecovery` | 2D 特征图 / 3D 特征图 | `stable` | `./contracts/pangupatchrecovery.md` |
| PanguFuser | fuser | `OneFuser` | `PanguFuser` | 3D token | `stable` | `./contracts/pangufuser.md` |
| FourCastNetFuser | fuser | `OneFuser` | `FourCastNetFuser` | 2D patch 网格特征 | `stable` | `./contracts/fourcastnetfuser.md` |
| FengWuFuser | fuser | `OneFuser` | `FengWuFuser` | 3D token | `stable` | `./contracts/fengwufuser.md` |
| FengWuEncoder | encoder | `OneEncoder` | `FengWuEncoder` | 2D 场 | `stable` | `./contracts/fengwuencoder.md` |
| FengWuDecoder | decoder | `OneDecoder` | `FengWuDecoder` | 中分辨率 token + 高分辨率 skip | `stable` | `./contracts/fengwudecoder.md` |
| FuxiTransformer | transformer | `OneTransformer` | `FuxiTransformer` | 2D 特征图 | `stable` | `./contracts/fuxitransformer.md` |

## 底层通用模块层

这些组件通常作为继续下钻时的第二优先级，不建议替代高层业务组件成为默认起点。

| 组件 | 模块族 | 调用入口 | 注册名 | 输入形态摘要 | 当前状态 | 契约卡片 |
|---|---|---|---|---|---|---|
| EarthTransformer2DBlock | transformer | `OneTransformer` | `EarthTransformer2DBlock` | 2D token | `stable` | `./contracts/earthtransformer2dblock.md` |
| EarthTransformer3DBlock | transformer | `OneTransformer` | `EarthTransformer3DBlock` | 3D token | `stable` | `./contracts/earthtransformer3dblock.md` |
| EarthAttention2D | attention | `OneAttention` | `EarthAttention2D` | 2D 窗口化 token | `stable` | `./contracts/earthattention2d.md` |
| EarthAttention3D | attention | `OneAttention` | `EarthAttention3D` | 3D 窗口化 token | `stable` | `./contracts/earthattention3d.md` |
| FourCastNetAFNO2D | afno | `OneAFNO` | `FourCastNetAFNO2D` | 2D patch 网格特征 | `stable` | `./contracts/fourcastnetafno.md` |
| FourCastNetFC | fc | `OneFC` | `FourCastNetFC` | 任意前缀维度的特征张量 | `stable` | `./contracts/fourcastnetfc.md` |
| FuxiFC | fc | `OneFC` | `FuxiFC` | 任意前缀维度的特征张量 | `stable` | `./contracts/fuxifc.md` |

## 按模块族检索

### embedding

适用于：

- patch embedding
- 输入变量编码
- 2D 场或 3D 场的初始 token 化

优先顺序：

1. 先看高层 embedding 组件
2. 再看 `OneEmbedding`

### fuser

适用于：

- 主干特征提取
- token 融合
- trunk 建模
- surface 与 upper-air 联合建模
- 3D token 主干

优先顺序：

1. 先看高层 fuser 组件
2. 再看 `OneFuser`
3. 只有 `fuser` 不适用时，再继续看 transformer / attention

补充约束：

- 若 `fuser` 契约已能覆盖主干需求，默认不要直接从底层 transformer block 手工拼 encoder/decoder 作为首选实现
- 若需要回到底层 block，应先在规格中说明当前 `fuser` 为什么不适用

### sample

适用于：

- 下采样
- 上采样
- 特征分辨率调整
- U 形主干中的尺度变换

优先顺序：

1. 先看具体 downsample / upsample 组件
2. 再看 `OneSample`

### recovery

适用于：

- patch 级输出恢复
- token 到物理场的输出投影
- 2D / 3D 特征恢复到目标变量网格

优先顺序：

1. 先看具体 recovery 组件
2. 再看 `OneRecovery`

### encoder / decoder

适用于：

- 明确的编码器分支
- 明确的解码器分支
- 多分支结构中的局部表征抽取与恢复

优先顺序：

1. 先看具体 encoder / decoder 组件
2. 再看 `OneEncoder` / `OneDecoder`

### transformer / attention / afno / fc

适用于：

- 高层组件无法覆盖时的继续下钻
- 对 block 级或算子级实现进行替换
- 明确需要底层算子能力时

优先顺序：

1. 先看高层组件
2. 再看对应 `One*` 入口
3. 最后看底层模块

### mlp / fourier / head

适用于：

- 神经算子前的点级或 token 级特征映射
- `FNO / U_FNO / U_NO` 这类谱域主干
- `U_Net` 多尺度主干后的输出投影

优先顺序：

1. 先看模型卡，确认具体依赖的是哪一个 wrapper
2. 再看 `OneMlp` / `OneFourier` / `OneHead`
3. 只有 wrapper 契约仍不足时，再回到对应底层实现

## 典型检索顺序

对于天气预测、全球格点预报、surface 与 upper-air 联合建模这类任务，推荐优先顺序：

1. `embedding`
2. `fuser`
3. `sample`
4. `recovery`

在某个模块族内部，再根据以下条件筛选：

- 输入输出 shape 是否匹配
- 是否支持当前任务需要的 2D / 3D 形态
- 调用入口与注册名是否明确
- 契约卡片是否已覆盖当前任务关键参数
- 是否已有相近模型或案例可以参考

## 继续下钻的规则

若上层组件卡片中已经写明“内部依赖某个 block / attention / afno / fc / wrapper”，建议按下面顺序继续下钻：

1. 先看该高层组件自己的契约卡
2. 再看对应 `One*` 入口卡
3. 再看下层 block / attention / afno / fc 卡
4. 只有契约仍不足时，再回到源码

## 建议维护规则

新增组件时，建议至少补以下字段：

- 组件名
- 所属模块族
- 调用入口
- 注册名
- 输入形态摘要
- 当前状态
- 契约卡片

推荐状态值：

- `stable`
- `in_progress`
- `legacy_split`

推荐新增流程：

1. 先复制 `./contracts/TEMPLATE.md`
2. 按组件实际情况填写字段
3. 将新契约文件加入本索引
4. 若该组件已经替代旧实现，在风险点中明确说明

## 相关文档

- `./contracts/TEMPLATE.md`
- `./contracts/naming_convention.md`

## 检索约定

源码锚点统一使用 `./onescience/...` 相对路径。

默认假设：

- ``
- `onescience/`

位于同一工作目录下。


path: onescience-coder/assets/contracts/earthattention2d.md
content: # Contract: EarthAttention2D

## 基本信息

- 组件名：`EarthAttention2D`
- 所属模块族：`attention`
- 统一入口：`OneAttention`
- 注册名：`style="EarthAttention2D"`

## 组件职责

在二维窗口化 token 上执行带地球位置偏置的多头自注意力。

补充说明：

- 该组件处理的是已经完成窗口划分后的 token 张量
- 经度方向窗口数量折叠进 batch 维
- 它是 `EarthTransformer2DBlock` 的底层注意力核心

## 支持输入

- 2D 输入：`(Batch * NumWidthWindows, NumHeightWindows, WindowTokens, dim)`
- 3D 输入：`not_applicable`

内部统一做法：

- 对窗口内 token 做 QKV 投影
- 加入地球位置偏置
- 如有 shifted-window mask，则在 softmax 前叠加
- 输出 shape 与输入保持一致

## 构造参数

- `dim`
  - 输入与输出特征维度
- `input_resolution`
  - padding 后二维网格尺寸 `(Height, Width)`
- `window_size`
  - 窗口大小 `(WindowHeight, WindowWidth)`
- `num_heads`
  - 多头注意力头数
- `qkv_bias`, `qk_scale`, `attn_drop`, `proj_drop`
  - 标准注意力配置项

## 输出约定

- 2D 输出：`(Batch * NumWidthWindows, NumHeightWindows, WindowTokens, dim)`
- 3D 输出：`not_applicable`

如果有明确边界条件，也写在这里：

- `dim` 必须能被 `num_heads` 整除
- 输入必须已经完成窗口划分，不能直接传原始二维网格

## 典型调用位置

- EarthTransformer2DBlock

## 典型参数

- FengWu 默认二维窗口注意力
  - `window_size=(6, 12)`
  - `num_heads=6` 或 `12`

## 风险点

- 这是窗口化 attention，不是原始网格 attention
- `mask` 的窗口布局必须与输入窗口布局完全一致
- `input_resolution` 应该使用 padding 后的网格尺寸

## 源码锚点

- `./onescience/src/onescience/modules/attention/earthattention2d.py`
- `./onescience/src/onescience/modules/attention/oneattention.py`
- `./onescience/src/onescience/modules/transformer/earthtransformer2Dblock.py`


path: onescience-coder/assets/contracts/earthattention3d.md
content: # Contract: EarthAttention3D

## 基本信息

- 组件名：`EarthAttention3D`
- 所属模块族：`attention`
- 统一入口：`OneAttention`
- 注册名：`style="EarthAttention3D"`

## 组件职责

在三维窗口化 token 上执行带地球位置偏置的多头自注意力。

补充说明：

- 该组件处理的是已经完成窗口划分后的三维 token 张量
- 经度方向窗口数量折叠进 batch 维
- `(PressureLevels, Height)` 方向窗口数量合并为一维
- 它是 `EarthTransformer3DBlock` 的底层注意力核心

## 支持输入

- 2D 输入：`not_applicable`
- 3D 输入：`(Batch * NumWidthWindows, NumPressureHeightWindows, WindowTokens, dim)`

内部统一做法：

- 对窗口内 token 做 QKV 投影
- 加入地球位置偏置
- 如有 shifted-window mask，则在 softmax 前叠加
- 输出 shape 与输入保持一致

## 构造参数

- `dim`
  - 输入与输出特征维度
- `input_resolution`
  - padding 后三维网格尺寸 `(PressureLevels, Height, Width)`
- `window_size`
  - 窗口大小 `(WindowPressureLevels, WindowHeight, WindowWidth)`
- `num_heads`
  - 多头注意力头数
- `qkv_bias`, `qk_scale`, `attn_drop`, `proj_drop`
  - 标准注意力配置项

## 输出约定

- 2D 输出：`not_applicable`
- 3D 输出：`(Batch * NumWidthWindows, NumPressureHeightWindows, WindowTokens, dim)`

如果有明确边界条件，也写在这里：

- `dim` 必须能被 `num_heads` 整除
- 输入必须已经完成窗口划分，不能直接传原始三维网格

## 典型调用位置

- EarthTransformer3DBlock

## 典型参数

- Pangu / FengWu 默认三维窗口注意力
  - `window_size=(2, 6, 12)`
  - `num_heads=6` 或 `12`

## 风险点

- 这是窗口化 attention，不是原始三维网格 attention
- 在 FengWu 里，第一维窗口不一定对应真实气压层，也可能对应变量分支
- `input_resolution` 应该使用 padding 后的三维网格尺寸

## 源码锚点

- `./onescience/src/onescience/modules/attention/earthattention3d.py`
- `./onescience/src/onescience/modules/attention/oneattention.py`
- `./onescience/src/onescience/modules/transformer/earthtransformer3Dblock.py`


path: onescience-coder/assets/contracts/earthtransformer2dblock.md
content: # Contract: EarthTransformer2DBlock

## 基本信息

- 组件名：`EarthTransformer2DBlock`
- 所属模块族：`transformer`
- 统一入口：`OneTransformer`
- 注册名：`style="EarthTransformer2DBlock"`

## 组件职责

在二维 patch 网格上执行带地球位置偏置的局部窗口 Transformer 计算。

补充说明：

- 该组件处理的是展平后的二维 token 序列
- 内部使用 `OneAttention(style="EarthAttention2D")`
- 适合 FengWu encoder / decoder 这类二维 patch-grid block

## 支持输入

- 2D 输入：`(Batch, Height * Width, dim)`
- 3D 输入：`not_applicable`

内部统一做法：

- 先把 token 序列还原为二维网格
- 对不能整除窗口大小的网格先做 padding
- 以普通窗口或 shifted window 执行 `EarthAttention2D`
- 最后 crop 回原网格并接 MLP 残差

## 构造参数

- `dim`
  - 输入与输出 token 特征维度
- `input_resolution`
  - 二维网格尺寸 `(Height, Width)`
- `num_heads`
  - 多头注意力头数
- `window_size`
  - 局部窗口大小 `(WindowHeight, WindowWidth)`
- `shift_size`
  - 循环移位大小 `(ShiftHeight, ShiftWidth)`
- `drop_path`
  - Stochastic Depth 比例
- `mlp_ratio`, `qkv_bias`, `qk_scale`, `drop`, `attn_drop`, `act_layer`, `norm_layer`
  - 标准 Transformer 配置项

## 输出约定

- 2D 输出：`(Batch, Height * Width, dim)`
- 3D 输出：`not_applicable`

如果有明确边界条件，也写在这里：

- 输出 token 数与输入 token 数保持一致
- `input_resolution` 必须与输入 token 数严格对应

## 典型调用位置

- FengWuEncoder 的高分辨率阶段
- FengWuEncoder 的中分辨率阶段
- FengWuDecoder 的中分辨率阶段
- FengWuDecoder 的高分辨率阶段

## 典型参数

- FengWu 默认二维 block
  - `dim=192` 或 `384`
  - `window_size=(6, 12)`
  - `num_heads=6` 或 `12`

## 风险点

- 输入必须是展平 token 序列，不是 `(Batch, Height, Width, dim)` 网格
- `shift_size` 与 `window_size` 的语义不能混淆
- 若调用层的 `input_resolution` 不正确，padding / reverse window 都会错位

## 源码锚点

- `./onescience/src/onescience/modules/transformer/earthtransformer2Dblock.py`
- `./onescience/src/onescience/modules/transformer/onetransformer.py`
- `./onescience/src/onescience/modules/attention/earthattention2d.py`


path: onescience-coder/assets/contracts/earthtransformer3dblock.md
content: # Contract: EarthTransformer3DBlock

## 基本信息

- 组件名：`EarthTransformer3DBlock`
- 所属模块族：`transformer`
- 统一入口：`OneTransformer`
- 注册名：`style="EarthTransformer3DBlock"`

## 组件职责

在三维 patch 网格上执行带地球位置偏置的局部窗口 Transformer 计算。

补充说明：

- 该组件处理的是展平后的三维 token 序列
- 内部使用 `OneAttention(style="EarthAttention3D")`
- 它是 `PanguFuser` 与 `FengWuFuser` 的底层关键 block

## 支持输入

- 2D 输入：`not_applicable`
- 3D 输入：`(Batch, PressureLevels * Height * Width, dim)`

内部统一做法：

- 先把 token 序列还原为三维网格
- 对不能整除窗口大小的网格先做 padding
- 以普通窗口或 shifted window 执行 `EarthAttention3D`
- 最后 crop 回原网格并接 MLP 残差

## 构造参数

- `dim`
  - 输入与输出 token 特征维度
- `input_resolution`
  - 三维网格尺寸 `(PressureLevels, Height, Width)`
- `num_heads`
  - 多头注意力头数
- `window_size`
  - 局部窗口大小 `(WindowPressureLevels, WindowHeight, WindowWidth)`
- `shift_size`
  - 循环移位大小 `(ShiftPressureLevels, ShiftHeight, ShiftWidth)`
- `drop_path`
  - Stochastic Depth 比例
- `mlp_ratio`, `qkv_bias`, `qk_scale`, `drop`, `attn_drop`, `act_layer`, `norm_layer`
  - 标准 Transformer 配置项

## 输出约定

- 2D 输出：`not_applicable`
- 3D 输出：`(Batch, PressureLevels * Height * Width, dim)`

如果有明确边界条件，也写在这里：

- 输出 token 数与输入 token 数保持一致
- `input_resolution` 必须与输入 token 数严格对应

## 典型调用位置

- PanguFuser 的每一层 block
- FengWuFuser 的每一层 block

## 典型参数

- Pangu 主干
  - `window_size=(2, 6, 12)`
  - `num_heads=6` 或 `12`
- FengWu 三维变量融合
  - `window_size=(2, 6, 12)`
  - `num_heads=12`

## 风险点

- 输入必须是展平 token 序列，不是三维网格张量
- 在 FengWu 中，`input_resolution[0]` 表示变量分支数，不一定是物理气压层数
- 若调用层的 `input_resolution` 不正确，padding / reverse window 会错位

## 源码锚点

- `./onescience/src/onescience/modules/transformer/earthtransformer3Dblock.py`
- `./onescience/src/onescience/modules/transformer/onetransformer.py`
- `./onescience/src/onescience/modules/attention/earthattention3d.py`


path: onescience-coder/assets/contracts/fengwudecoder.md
content: # Contract: FengWuDecoder

## 基本信息

- 组件名：`FengWuDecoder`
- 所属模块族：`decoder`
- 统一入口：`OneDecoder`
- 注册名：`style="FengWuDecoder"`

## 组件职责

将单个变量分支的中分辨率 token 序列与高分辨率 skip 特征解码为原始分辨率输出场。

补充说明：

- 该组件处理的是单个变量分支，不负责跨变量融合
- 内部使用 `EarthTransformer2DBlock`、`PanguUpSample` 和 `PanguPatchRecovery`
- 输入通常来自 `FengWuFuser` 的单分支切分结果和对应 encoder 的 skip 特征

## 支持输入

- 2D 输入：
  - `inp[0]`: `(Batch, middle_resolution[0] * middle_resolution[1], 2 * dim)`
  - `inp[1]`: `(Batch, output_resolution[0], output_resolution[1], dim)`
- 3D 输入：`not_applicable`

内部统一做法：

- 先处理中分辨率 token 序列
- 上采样回高分辨率 token 网格
- 在高分辨率上继续做 2D Transformer
- 与 skip 特征拼接后恢复为原始分辨率场

## 构造参数

- `output_resolution`
  - 高分辨率 patch 网格尺寸 `(Height, Width)`
- `middle_resolution`
  - 中分辨率 patch 网格尺寸 `(Height, Width)`
- `out_chans`
  - 输出变量通道数
- `img_size`
  - 原始输出场尺寸 `(Height, Width)`
- `patch_size`
  - patch 恢复尺寸 `(PatchHeight, PatchWidth)`
- `dim`
  - 高分辨率特征维度
- `depth`
  - 高分辨率 Transformer block 层数
- `depth_middle`
  - 中分辨率 Transformer block 层数
- `num_heads`
  - 若为二元组，顺序为 `(HighResolutionHeads, MiddleResolutionHeads)`
- `window_size`
  - 二维窗口大小
- `drop_path`
  - 可为单值或按层提供的序列

## 输出约定

- 2D 输出：`(Batch, out_chans, img_size[0], img_size[1])`
- 3D 输出：`not_applicable`

## 典型调用位置

- FengWu 主模型中的各变量分支解码器

## 典型参数

- surface 分支
  - `out_chans=4`
  - `img_size=(721, 1440)`
  - `patch_size=(4, 4)`
  - `dim=192`
- 高空变量分支
  - `out_chans=37`
  - `img_size=(721, 1440)`
  - `patch_size=(4, 4)`
  - `dim=192`

## 风险点

- 该组件处理的是单个变量分支，不要把多个变量分支混在一起输入
- `num_heads` 二元组顺序是 `(高分辨率, 中分辨率)`
- `inp[1]` 是二维网格 skip 特征，不是展平 token 序列

## 下层依赖契约入口

- `./contracts/onedecoder.md`
- `./contracts/onesample.md`
- `./contracts/panguupsample.md`
- `./contracts/onerecovery.md`
- `./contracts/pangupatchrecovery.md`
- `./contracts/onetransformer.md`
- `./contracts/earthtransformer2dblock.md`
- `./contracts/oneattention.md`
- `./contracts/earthattention2d.md`

## 源码锚点

- `./onescience/src/onescience/modules/decoder/fengwudecoder.py`
- `./onescience/src/onescience/modules/decoder/onedecoder.py`
- `./onescience/src/onescience/models/fengwu/fengwu.py`


path: onescience-coder/assets/contracts/fengwuencoder.md
content: # Contract: FengWuEncoder

## 基本信息

- 组件名：`FengWuEncoder`
- 所属模块族：`encoder`
- 统一入口：`OneEncoder`
- 注册名：`style="FengWuEncoder"`

## 组件职责

对单个二维变量分支做层次化编码，输出中分辨率 token 序列和高分辨率 skip 特征。

补充说明：

- 该组件处理的是单个变量分支，不负责跨变量融合
- 内部使用 `PanguEmbedding`、`EarthTransformer2DBlock` 和 `PanguDownSample`
- 输出将供 FengWu 主模型中的 `FengWuFuser` 和 `FengWuDecoder` 使用

## 支持输入

- 2D 输入：`(Batch, in_chans, Height, Width)`
- 3D 输入：`not_applicable`

内部统一做法：

- 先做二维 patch embedding
- 在高分辨率 patch 网格上做若干层 2D Transformer
- 保存高分辨率 skip 特征
- 下采样到中分辨率后继续编码

## 构造参数

- `input_resolution`
  - 高分辨率 patch 网格尺寸 `(Height, Width)`
- `middle_resolution`
  - 中分辨率 patch 网格尺寸 `(OutHeight, OutWidth)`
- `in_chans`
  - 输入变量通道数
- `img_size`
  - 原始输入场尺寸 `(Height, Width)`
- `patch_size`
  - patch 切分尺寸 `(PatchHeight, PatchWidth)`
- `dim`
  - 高分辨率特征维度
- `depth`
  - 高分辨率 Transformer block 层数
- `depth_middle`
  - 中分辨率 Transformer block 层数
- `num_heads`
  - 若为二元组，顺序为 `(HighResolutionHeads, MiddleResolutionHeads)`
- `window_size`
  - 二维窗口大小
- `drop_path`
  - 可为单值或按层提供的序列

## 输出约定

- 2D 输出：
  - `x`: `(Batch, middle_resolution[0] * middle_resolution[1], 2 * dim)`
  - `skip`: `(Batch, input_resolution[0], input_resolution[1], dim)`
- 3D 输出：`not_applicable`

## 典型调用位置

- FengWu 主模型中的各变量分支编码器

## 典型参数

- surface 分支
  - `in_chans=4`
  - `img_size=(721, 1440)`
  - `patch_size=(4, 4)`
  - `dim=192`
- 高空变量分支
  - `in_chans=37`
  - `img_size=(721, 1440)`
  - `patch_size=(4, 4)`
  - `dim=192`

## 风险点

- 该组件处理的是单个变量分支，不要直接把多变量拼接输入给它
- `num_heads` 二元组顺序是 `(高分辨率, 中分辨率)`
- 输出 `skip` 是二维网格特征，不是展平 token 序列

## 下层依赖契约入口

- `./contracts/oneencoder.md`
- `./contracts/oneembedding.md`
- `./contracts/panguembedding.md`
- `./contracts/onesample.md`
- `./contracts/pangudownsample.md`
- `./contracts/onetransformer.md`
- `./contracts/earthtransformer2dblock.md`
- `./contracts/oneattention.md`
- `./contracts/earthattention2d.md`

## 源码锚点

- `./onescience/src/onescience/modules/encoder/fengwuencoder.py`
- `./onescience/src/onescience/modules/encoder/oneencoder.py`
- `./onescience/src/onescience/models/fengwu/fengwu.py`


path: onescience-coder/assets/contracts/fengwufuser.md
content: # Contract: FengWuFuser

## 基本信息

- 组件名：`FengWuFuser`
- 所属模块族：`fuser`
- 统一入口：`OneFuser`
- 注册名：`style="FengWuFuser"`

## 组件职责

在三维网格 `(Variables, Height, Width)` 上融合多个变量分支的中分辨率特征。

补充说明：

- 该组件处理的是展平后的三维 token 序列
- 输入通常来自多个 `FengWuEncoder` 输出的中分辨率特征拼接
- 内部使用多层 `EarthTransformer3DBlock`

## 支持输入

- 2D 输入：`not_applicable`
- 3D 输入：`(Batch, Variables * Height * Width, dim)`

内部统一做法：

- 将输入视作三维网格对应的 token 序列
- 在 `(Variables, Height, Width)` 三维网格上堆叠多层 3D Transformer
- 输出 token 数与输入保持一致

## 构造参数

- `input_resolution`
  - 三维网格尺寸 `(Variables, Height, Width)`
- `dim`
  - 输入与输出 token 特征维度
- `depth`
  - 3D Transformer block 层数
- `num_heads`
  - 多头自注意力头数
- `window_size`
  - 三维窗口大小 `(VariablesWindow, HeightWindow, WidthWindow)`
- `drop_path`
  - 可为单值或按层提供的序列
- `mlp_ratio`, `qkv_bias`, `qk_scale`, `drop`, `attn_drop`, `norm_layer`
  - 标准 Transformer 配置项

## 输出约定

- 2D 输出：`not_applicable`
- 3D 输出：`(Batch, Variables * Height * Width, dim)`

明确约束：

- 输出 token 数与输入 token 数保持一致
- `input_resolution` 必须与输入 token 数严格对应

## 典型调用位置

- FengWu 主模型中的跨变量中分辨率融合阶段

## 典型参数

- FengWu 默认配置
  - `input_resolution=(6, 91, 180)`
  - `dim=384`
  - `depth=6`
  - `num_heads=12`
  - `window_size=(2, 6, 12)`

## 风险点

- 该组件输入是展平 token 序列，不是二维网格特征
- 第一维 `Variables` 指的是变量分支数，不是时间步或气压层
- `dim` 必须与 encoder 输出和 decoder 输入保持一致

## 下层依赖契约入口

- `./contracts/onefuser.md`
- `./contracts/onetransformer.md`
- `./contracts/earthtransformer3dblock.md`
- `./contracts/oneattention.md`
- `./contracts/earthattention3d.md`

## 源码锚点

- `./onescience/src/onescience/modules/fuser/fengwufuser.py`
- `./onescience/src/onescience/modules/fuser/onefuser.py`
- `./onescience/src/onescience/models/fengwu/fengwu.py`


path: onescience-coder/assets/contracts/fourcastnetafno.md
content: # Contract: FourCastNetAFNO2D

## 基本信息

- 组件名：`FourCastNetAFNO2D`
- 所属模块族：`afno`
- 统一入口：`OneAFNO`
- 注册名：`style="FourCastNetAFNO2D"`

## 组件职责

在二维 patch 网格上执行 AFNO 频域混合，实现全局感受野的 token 交互。

补充说明：

- 该组件是 FourCastNetFuser 中替代自注意力的核心混合模块
- 它处理的是二维 patch 网格特征
- 输出会与输入做残差相加

## 支持输入

- 2D 输入：`(Batch, Height, Width, Channels)`
- 3D 输入：`not_applicable`

内部统一做法：

- 先做 `rfft2` 将特征映射到频域
- 对各通道块执行复数 MLP 混合
- 用 `softshrink` 做频域稀疏化
- 再做 `irfft2` 回到空间域

## 构造参数

- `hidden_size`
  - 输入与输出通道数
- `num_blocks`
  - 通道分块数
- `sparsity_threshold`
  - soft shrink 阈值
- `hard_thresholding_fraction`
  - 保留的频率模式比例
- `hidden_size_factor`
  - 频域 MLP 中间层扩展倍数

## 输出约定

- 2D 输出：`(Batch, Height, Width, Channels)`
- 3D 输出：`not_applicable`

明确约束：

- 输入最后一维必须等于 `hidden_size`
- `hidden_size` 必须能被 `num_blocks` 整除

## 典型调用位置

- `FourCastNetFuser` 内部的频域混合层

## 典型参数

- FourCastNet 默认 AFNO
  - `hidden_size=768`
  - `num_blocks=8`
  - `sparsity_threshold=0.01`
  - `hard_thresholding_fraction=1.0`
  - `hidden_size_factor=1`

## 风险点

- 这里的 `Height` 与 `Width` 指的是 patch 网格尺寸，不是原始图像分辨率
- 输入最后一维不匹配会直接报错
- `hard_thresholding_fraction` 会直接影响频域保留范围

## 源码锚点

- `./onescience/src/onescience/modules/afno/fourcastnetafno.py`
- `./onescience/src/onescience/modules/afno/oneafno.py`
- `./onescience/src/onescience/modules/fuser/fourcastnetfuser.py`


path: onescience-coder/assets/contracts/fourcastnetembedding.md
content: # Contract: FourCastNetEmbedding

## 基本信息

- 组件名：`FourCastNetEmbedding`
- 所属模块族：`embedding`
- 统一入口：`OneEmbedding`
- 注册名：`style="FourCastNetEmbedding"`

## 组件职责

将二维气象场切分为不重叠 patch，并投影到统一的 embedding 特征空间。

这是 FourCastNet 输入编码阶段的 patch embedding 组件。

补充说明：

- 该组件只处理二维输入
- 输出是展平后的 patch token 序列
- 调用层通常会再把它恢复成二维 patch 网格，送入 `FourCastNetFuser`

## 支持输入

- 2D 输入：`(Batch, Channels, Height, Width)`
- 3D 输入：`not_applicable`

内部统一做法：

- 使用 `Conv2d` 完成 patch 切分和线性投影
- 不做自动 padding
- 输入空间尺寸必须与构造时的 `img_size` 一致

## 构造参数

- `img_size`
  - 输入空间尺寸 `(Height, Width)`
- `patch_size`
  - patch 切分尺寸 `(PatchHeight, PatchWidth)`
- `in_chans`
  - 输入变量通道数
- `embed_dim`
  - 输出 token 特征维度

## 输出约定

- 2D 输出：`(Batch, NumPatches, embed_dim)`
- 3D 输出：`not_applicable`

其中：

- `PatchGridHeight = Height // PatchHeight`
- `PatchGridWidth = Width // PatchWidth`
- `NumPatches = PatchGridHeight * PatchGridWidth`

## 典型调用位置

- FourCastNet 主模型输入编码阶段

## 典型参数

- FourCastNet 默认配置
  - `img_size=(720, 1440)`
  - `patch_size=(8, 8)`
  - `in_chans=19`
  - `embed_dim=768`

## 风险点

- 该组件不支持三维 `PressureLevels` 输入
- `img_size` 与实际输入不一致会直接报错
- `patch_size` 会直接改变 token 数和后续位置编码长度

## 源码锚点

- `./onescience/src/onescience/modules/embedding/fourcastnetembedding.py`
- `./onescience/src/onescience/modules/embedding/oneembedding.py`
- `./onescience/src/onescience/models/fourcastnet/fourcastnet.py`


path: onescience-coder/assets/contracts/fourcastnetfc.md
content: # Contract: FourCastNetFC

## 基本信息

- 组件名：`FourCastNetFC`
- 所属模块族：`fc`
- 统一入口：`OneFC`
- 注册名：`style="FourCastNetFC"`

## 组件职责

在最后一个特征维度上执行两层前馈映射，完成逐位置通道混合。

补充说明：

- 该组件通常位于 `FourCastNetFuser` 中的 AFNO 之后
- 它不关心前面的 batch 或空间维度
- 本质上是一个作用在最后一维上的 MLP

## 支持输入

- 2D 输入：`(..., in_features)`
- 3D 输入：`(..., in_features)`

内部统一做法：

- 对最后一个特征维度执行 `Linear -> Act -> Dropout -> Linear -> Dropout`
- 保持前面的所有维度不变

## 构造参数

- `in_features`
  - 输入特征维度
- `hidden_features`
  - 隐层特征维度
- `out_features`
  - 输出特征维度
- `act_layer`
  - 激活函数类型
- `drop`
  - dropout 比例

## 输出约定

- 2D 输出：`(..., out_features)`
- 3D 输出：`(..., out_features)`

明确约束：

- 该组件不依赖固定空间 shape
- 主要约束来自最后一维的特征维度匹配

## 典型调用位置

- `FourCastNetFuser` 内部的通道混合层

## 典型参数

- FourCastNet 默认配置
  - `in_features=768`
  - `hidden_features=3072`
  - `out_features=768`
  - `drop=0.0`

## 风险点

- 该组件不是空间算子，不能直接把它当作 embedding 或 fuser 使用
- 主要检查点是最后一维是否与调用层一致

## 源码锚点

- `./onescience/src/onescience/modules/fc/fourcastnetfc.py`
- `./onescience/src/onescience/modules/fc/onefc.py`
- `./onescience/src/onescience/modules/fuser/fourcastnetfuser.py`


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)