runops 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- runops/__init__.py +5 -0
- runops/_data/README.md +476 -0
- runops/adapters/__init__.py +29 -0
- runops/adapters/_utils/__init__.py +36 -0
- runops/adapters/_utils/toml_utils.py +81 -0
- runops/adapters/base.py +335 -0
- runops/adapters/contrib/__init__.py +5 -0
- runops/adapters/contrib/beach.py +837 -0
- runops/adapters/contrib/emses.py +1010 -0
- runops/adapters/generic.py +439 -0
- runops/adapters/registry.py +244 -0
- runops/cli/__init__.py +3 -0
- runops/cli/analyze.py +222 -0
- runops/cli/clone.py +104 -0
- runops/cli/config.py +217 -0
- runops/cli/context.py +56 -0
- runops/cli/create.py +263 -0
- runops/cli/dashboard.py +179 -0
- runops/cli/extend.py +204 -0
- runops/cli/history.py +105 -0
- runops/cli/init.py +1432 -0
- runops/cli/jobs.py +145 -0
- runops/cli/knowledge.py +1017 -0
- runops/cli/list.py +102 -0
- runops/cli/log.py +163 -0
- runops/cli/main.py +96 -0
- runops/cli/manage.py +231 -0
- runops/cli/new.py +343 -0
- runops/cli/notes.py +257 -0
- runops/cli/run_lookup.py +148 -0
- runops/cli/setup.py +174 -0
- runops/cli/status.py +187 -0
- runops/cli/submit.py +297 -0
- runops/cli/update.py +113 -0
- runops/cli/update_harness.py +245 -0
- runops/cli/update_refs.py +370 -0
- runops/core/__init__.py +3 -0
- runops/core/actions.py +1186 -0
- runops/core/analysis.py +1090 -0
- runops/core/campaign.py +156 -0
- runops/core/case.py +307 -0
- runops/core/context.py +426 -0
- runops/core/discovery.py +192 -0
- runops/core/environment.py +266 -0
- runops/core/exceptions.py +93 -0
- runops/core/knowledge.py +595 -0
- runops/core/knowledge_source.py +1204 -0
- runops/core/manifest.py +219 -0
- runops/core/project.py +171 -0
- runops/core/provenance.py +147 -0
- runops/core/retry.py +193 -0
- runops/core/run.py +170 -0
- runops/core/run_creation.py +456 -0
- runops/core/site.py +337 -0
- runops/core/state.py +197 -0
- runops/core/survey.py +380 -0
- runops/core/validation.py +40 -0
- runops/harness/__init__.py +27 -0
- runops/harness/builder.py +327 -0
- runops/harness/claude.py +189 -0
- runops/jobgen/__init__.py +3 -0
- runops/jobgen/generator.py +295 -0
- runops/launchers/__init__.py +17 -0
- runops/launchers/base.py +313 -0
- runops/launchers/mpiexec.py +131 -0
- runops/launchers/mpirun.py +132 -0
- runops/launchers/srun.py +126 -0
- runops/sites/__init__.py +0 -0
- runops/sites/camphor.md +98 -0
- runops/sites/camphor.toml +27 -0
- runops/slurm/__init__.py +3 -0
- runops/slurm/query.py +384 -0
- runops/slurm/submit.py +203 -0
- runops/templates/__init__.py +29 -0
- runops/templates/adapters/beach/agent_guide.md +50 -0
- runops/templates/adapters/beach/beach.toml +19 -0
- runops/templates/adapters/beach/case.toml +16 -0
- runops/templates/adapters/beach/summarize.py +272 -0
- runops/templates/adapters/emses/agent_guide.md +39 -0
- runops/templates/adapters/emses/case.toml +18 -0
- runops/templates/adapters/emses/plasma.toml +118 -0
- runops/templates/adapters/emses/summarize.py +413 -0
- runops/templates/adapters/generic/case.toml.j2 +13 -0
- runops/templates/adapters/generic/summarize.py +21 -0
- runops/templates/agent.md +156 -0
- runops/templates/rules/cookbook.md +22 -0
- runops/templates/scaffold/campaign.toml.j2 +10 -0
- runops/templates/scaffold/cases_claude.md +22 -0
- runops/templates/scaffold/facts.toml +2 -0
- runops/templates/scaffold/gitignore.txt +30 -0
- runops/templates/scaffold/notes/README.md +69 -0
- runops/templates/scaffold/rules/plan-before-act.md +17 -0
- runops/templates/scaffold/rules/runops-workflow.md +84 -0
- runops/templates/scaffold/rules/upstream-feedback.md +85 -0
- runops/templates/scaffold/runs_claude.md +24 -0
- runops/templates/scaffold/vscode_settings.json +9 -0
- runops/templates/skills/analyze/SKILL.md +40 -0
- runops/templates/skills/check-status/SKILL.md +29 -0
- runops/templates/skills/cleanup/SKILL.md +43 -0
- runops/templates/skills/create-run/SKILL.md +135 -0
- runops/templates/skills/debug-failed/SKILL.md +38 -0
- runops/templates/skills/learn/SKILL.md +54 -0
- runops/templates/skills/new-case/SKILL.md +108 -0
- runops/templates/skills/note/SKILL.md +107 -0
- runops/templates/skills/run-all/SKILL.md +47 -0
- runops/templates/skills/runops-reference/SKILL.md +203 -0
- runops/templates/skills/setup-campaign/SKILL.md +111 -0
- runops/templates/skills/setup-env/SKILL.md +32 -0
- runops/templates/skills/survey-design/SKILL.md +73 -0
- runops/templates/survey.toml.j2 +22 -0
- runops-0.2.0.dist-info/METADATA +491 -0
- runops-0.2.0.dist-info/RECORD +115 -0
- runops-0.2.0.dist-info/WHEEL +4 -0
- runops-0.2.0.dist-info/entry_points.txt +2 -0
- runops-0.2.0.dist-info/licenses/LICENSE +201 -0
runops/__init__.py
ADDED
runops/_data/README.md
ADDED
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
# runops
|
|
2
|
+
|
|
3
|
+
HPC 環境における Slurm ベースのシミュレーション実行管理 CLI ツール。
|
|
4
|
+
|
|
5
|
+
run ディレクトリを日常運用の主単位とし、パラメータサーベイ展開・job 投入・状態追跡・provenance 記録・解析補助を一貫して管理します。
|
|
6
|
+
|
|
7
|
+
## 特徴
|
|
8
|
+
|
|
9
|
+
- **run 中心の管理**: すべての操作は run ディレクトリ (`runs/.../Rxxxx/`) を基点に行う
|
|
10
|
+
- **パラメータサーベイ**: survey.toml による parameter sweep の自動展開(直積生成)
|
|
11
|
+
- **Slurm 連携**: sbatch による job 投入、squeue/sacct による状態同期
|
|
12
|
+
- **Simulator Adapter**: シミュレータ固有処理を Adapter パターンで抽象化。core はシミュレータに依存しない
|
|
13
|
+
- **Launcher Profile**: srun / mpirun / mpiexec の起動方式を Profile で切替可能
|
|
14
|
+
- **Provenance 記録**: git commit、executable hash、パラメータ snapshot を manifest.toml に自動記録
|
|
15
|
+
- **多重ネスト対応**: `runs/` 以下を自由に階層化して分類・整理できる
|
|
16
|
+
- **Agent/AI 対応**: TOML/JSON ベースの構造化データで AI エージェントとの連携が容易
|
|
17
|
+
- **知識層**: シミュレータ知識・実行環境・研究意図の 3 層で AI エージェントにコンテキストを提供
|
|
18
|
+
- **外部知識ソース**: 共有知識リポジトリを project に接続し、profile ベースで必要な知識だけを投影
|
|
19
|
+
- **Lab notebook**: `notes/YYYY-MM-DD.md` に append-only な時系列ノートを残し、curated knowledge (`.runops/insights/`, `facts.toml`) と二層で管理
|
|
20
|
+
- **パラメータバリデーション**: 物理的制約 (CFL 条件, Debye 長等) を run 生成前にチェック
|
|
21
|
+
- **Research Campaign**: campaign.toml で研究仮説・変数・観測量を構造化し、実験設計を明示
|
|
22
|
+
|
|
23
|
+
## インストール
|
|
24
|
+
|
|
25
|
+
runops はプロジェクトごとにブートストラップインストールされます。
|
|
26
|
+
事前のグローバルインストールは不要で、[uv](https://docs.astral.sh/uv/) だけあれば始められます。
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
# プロジェクトディレクトリを作成して初期化 (uv だけあれば OK)
|
|
30
|
+
uvx --from runops runops init
|
|
31
|
+
|
|
32
|
+
# activate して利用開始
|
|
33
|
+
source .venv/bin/activate
|
|
34
|
+
runops doctor
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
`runops init` が以下を自動的に行います:
|
|
38
|
+
|
|
39
|
+
1. `.venv/` を作成 (`uv venv`)
|
|
40
|
+
2. `tools/runops/` に runops リポジトリを clone
|
|
41
|
+
3. runops を `.venv` に editable install (`uv pip install -e`)
|
|
42
|
+
4. シミュレータ固有パッケージをインストール
|
|
43
|
+
|
|
44
|
+
runops の更新は `cd tools/runops && git pull` で行えます。
|
|
45
|
+
|
|
46
|
+
### 既存プロジェクトのセットアップ
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
uvx --from runops runops setup https://github.com/user/my-project.git
|
|
50
|
+
source my-project/.venv/bin/activate
|
|
51
|
+
runops doctor
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### 開発者向け (runops 自体の開発)
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
git clone https://github.com/Nkzono99/runops.git
|
|
58
|
+
cd runops
|
|
59
|
+
uv sync --dev
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## クイックスタート
|
|
63
|
+
|
|
64
|
+
### 1. プロジェクトの初期化
|
|
65
|
+
|
|
66
|
+
上記のインストール手順を実行すると、以下のファイル・ディレクトリが生成されます:
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
my-simulation-project/
|
|
70
|
+
runops.toml # プロジェクト設定
|
|
71
|
+
simulators.toml # シミュレータ定義
|
|
72
|
+
launchers.toml # Launcher Profile 定義
|
|
73
|
+
SITE.md # site profile 由来の companion doc (生成物)
|
|
74
|
+
campaign.toml # 研究意図 (仮説・変数・観測量)
|
|
75
|
+
.gitignore # 大容量出力の除外設定
|
|
76
|
+
CLAUDE.md # 現在の標準 Agent ハーネス (Claude Code) 向け指示
|
|
77
|
+
AGENTS.md # CLAUDE.md と同内容の補助ミラー
|
|
78
|
+
.claude/
|
|
79
|
+
settings.json # Claude Code の team-shared permission / hook 設定
|
|
80
|
+
hooks/ # submit 承認・保護パス監視 hook
|
|
81
|
+
rules/ # project 固有の運用ルール
|
|
82
|
+
skills/ # 定型作業用 SKILL
|
|
83
|
+
cases/ # Case 定義の格納場所
|
|
84
|
+
runs/ # run の格納場所
|
|
85
|
+
refs/ # シミュレータリファレンス / 外部知識ソース
|
|
86
|
+
MPIEMSES3D/ # Adapter が参照する simulator docs
|
|
87
|
+
beach/ # Adapter が参照する simulator docs
|
|
88
|
+
knowledge/ # 外部知識ソースのマウントポイント
|
|
89
|
+
shared-lab-kb/ # git/path で接続した共有知識リポジトリ
|
|
90
|
+
tools/
|
|
91
|
+
runops/ # runops 本体 (editable install, Git 管理外)
|
|
92
|
+
.venv/ # Python 仮想環境 (Git 管理外)
|
|
93
|
+
.runops/ # 知識層 (ナレッジ・環境・知見)
|
|
94
|
+
knowledge/ # 自動生成ナレッジ (gitignore 対象)
|
|
95
|
+
enabled/ # 有効な profile の imports.md
|
|
96
|
+
candidates/ # 外部 source 由来の candidate fact transport
|
|
97
|
+
insights/ # 実験知見 (人間向け curated Markdown)
|
|
98
|
+
facts.toml # 構造化された知識 (AI 向け machine-readable claims)
|
|
99
|
+
environment.toml # 実行環境記述 (自動検出)
|
|
100
|
+
notes/ # Lab notebook (append-only, 時系列)
|
|
101
|
+
YYYY-MM-DD.md # 日次の作業ログ (`runops notes append` で追記)
|
|
102
|
+
reports/ # 長文レポート (改稿可)
|
|
103
|
+
README.md # 二層 (curated vs lab notebook) の運用規約
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
`runops init` が生成する Claude ハーネスは、`.claude/settings.json` と
|
|
107
|
+
`.claude/hooks/` により次のようなガードを入れます。
|
|
108
|
+
|
|
109
|
+
- `manifest.toml`、`runs/**/input/**`、`submit/job.sh`、`work/**`、`SITE.md` などの生成物は直接編集しない
|
|
110
|
+
- `.runops/facts.toml` や `.runops/insights/` は `runops knowledge save` / `add-fact` 経由で更新する
|
|
111
|
+
- `runops runs submit` は `--dry-run` を除き実行前に確認を挟む
|
|
112
|
+
|
|
113
|
+
runops 自体の開発では、Claude ハーネスの定義は
|
|
114
|
+
`src/runops/harness/claude.py` にまとまっています。
|
|
115
|
+
|
|
116
|
+
### 2. 研究意図の定義 (campaign.toml)
|
|
117
|
+
|
|
118
|
+
プロジェクトルートに `campaign.toml` を作成し、何を調べたいかを記述:
|
|
119
|
+
|
|
120
|
+
```toml
|
|
121
|
+
[campaign]
|
|
122
|
+
name = "parameter-sensitivity"
|
|
123
|
+
description = "格子サイズと時間刻みの感度解析"
|
|
124
|
+
hypothesis = "nx=512 以上で解が収束する"
|
|
125
|
+
simulator = "my_solver"
|
|
126
|
+
|
|
127
|
+
[variables]
|
|
128
|
+
"nx" = { role = "independent", range = [128, 1024], unit = "cells" }
|
|
129
|
+
"dt" = { role = "independent", range = [1.0e-9, 1.0e-7], unit = "s" }
|
|
130
|
+
|
|
131
|
+
[observables]
|
|
132
|
+
max_field = { source = "work/output.h5", description = "最大電場強度" }
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
AI エージェントはこのファイルを読んで survey 設計や結果解釈に活用します。
|
|
136
|
+
|
|
137
|
+
### 3. シミュレータと Launcher の設定
|
|
138
|
+
|
|
139
|
+
`simulators.toml` にシミュレータを定義 (シミュレータ指定で init した場合は自動生成):
|
|
140
|
+
|
|
141
|
+
```toml
|
|
142
|
+
[simulators.my_solver]
|
|
143
|
+
adapter = "generic"
|
|
144
|
+
resolver_mode = "local_executable"
|
|
145
|
+
executable = "/path/to/solver"
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
`launchers.toml` に Launcher Profile を定義:
|
|
149
|
+
|
|
150
|
+
```toml
|
|
151
|
+
[launchers.slurm_srun]
|
|
152
|
+
kind = "srun"
|
|
153
|
+
command = "srun"
|
|
154
|
+
use_slurm_ntasks = true
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### 4. Case の定義
|
|
158
|
+
|
|
159
|
+
`runops case new` で case を生成し、`cases/<simulator>/<case>/case.toml` を編集します:
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
runops case new my_case -s my_solver
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
生成された `cases/my_solver/my_case/case.toml` の例:
|
|
166
|
+
|
|
167
|
+
```toml
|
|
168
|
+
[case]
|
|
169
|
+
name = "my_case"
|
|
170
|
+
simulator = "my_solver"
|
|
171
|
+
launcher = "slurm_srun"
|
|
172
|
+
description = "基本ケース"
|
|
173
|
+
|
|
174
|
+
[classification]
|
|
175
|
+
model = "cavity"
|
|
176
|
+
submodel = "rectangular"
|
|
177
|
+
tags = ["baseline"]
|
|
178
|
+
|
|
179
|
+
[job]
|
|
180
|
+
partition = "compute"
|
|
181
|
+
nodes = 1
|
|
182
|
+
ntasks = 32
|
|
183
|
+
walltime = "12:00:00"
|
|
184
|
+
|
|
185
|
+
[params]
|
|
186
|
+
nx = 256
|
|
187
|
+
ny = 256
|
|
188
|
+
dt = 1.0e-8
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### 5. 単一 run の作成
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
runops runs create my_case --dest runs/cavity/test
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### 6. パラメータサーベイの実行
|
|
198
|
+
|
|
199
|
+
`runs/cavity/scan/survey.toml` を作成:
|
|
200
|
+
|
|
201
|
+
```toml
|
|
202
|
+
[survey]
|
|
203
|
+
id = "S20260327-scan"
|
|
204
|
+
name = "parameter scan"
|
|
205
|
+
base_case = "my_case"
|
|
206
|
+
simulator = "my_solver"
|
|
207
|
+
launcher = "slurm_srun"
|
|
208
|
+
|
|
209
|
+
[axes]
|
|
210
|
+
nx = [128, 256, 512]
|
|
211
|
+
dt = [1.0e-8, 1.0e-9]
|
|
212
|
+
|
|
213
|
+
[naming]
|
|
214
|
+
display_name = "nx{nx}_dt{dt}"
|
|
215
|
+
|
|
216
|
+
[job]
|
|
217
|
+
partition = "compute"
|
|
218
|
+
nodes = 1
|
|
219
|
+
ntasks = 32
|
|
220
|
+
walltime = "12:00:00"
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
runops runs sweep runs/cavity/scan
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### 7. Job の投入
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
# cwd の run を投入
|
|
231
|
+
cd runs/cavity/test/R20260327-0001
|
|
232
|
+
runops runs submit
|
|
233
|
+
|
|
234
|
+
# survey 内の全 run を一括投入
|
|
235
|
+
cd runs/cavity/scan
|
|
236
|
+
runops runs submit --all
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### 8. 状態の確認
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
# 単一 run の状態確認
|
|
243
|
+
runops runs status R20260327-0001
|
|
244
|
+
|
|
245
|
+
# Slurm 状態を manifest に同期
|
|
246
|
+
runops runs sync R20260327-0001
|
|
247
|
+
|
|
248
|
+
# run の一覧表示
|
|
249
|
+
runops runs list
|
|
250
|
+
runops runs list runs/cavity/scan
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
## コマンドリファレンス
|
|
254
|
+
|
|
255
|
+
### プロジェクト管理
|
|
256
|
+
|
|
257
|
+
| コマンド | 説明 |
|
|
258
|
+
|---------|------|
|
|
259
|
+
| `runops init [SIMS...] [-y]` | プロジェクトの初期化 (対話型がデフォルト) |
|
|
260
|
+
| `runops setup [URL]` | 既存 runops project のセットアップ |
|
|
261
|
+
| `runops doctor [PATH]` | 環境検査 (設定・sbatch・run_id 一意性・環境検出) |
|
|
262
|
+
| `runops context [DIR]` | Agent 向け project context の要約を表示 |
|
|
263
|
+
| `runops config show` | 設定表示 |
|
|
264
|
+
| `runops config add-simulator` | シミュレータ追加 (対話型) |
|
|
265
|
+
| `runops config add-launcher` | ランチャー追加 (対話型) |
|
|
266
|
+
| `runops update` | シミュレータパッケージのアップグレード |
|
|
267
|
+
| `runops update-refs [SIMS...]` | refs/ リポジトリ更新 + ナレッジインデックス再生成 |
|
|
268
|
+
|
|
269
|
+
### Run 作成・投入
|
|
270
|
+
|
|
271
|
+
| コマンド | 説明 |
|
|
272
|
+
|---------|------|
|
|
273
|
+
| `runops case new CASE [--minimal] [--survey]` | 新規 case のスキャフォールド生成 (`--minimal` で小さな bundled テンプレートを使用、EMSES では `emu generate -u` を best-effort で自動実行し `[meta.physical]` を埋める) |
|
|
274
|
+
| `runops runs create CASE` | case から単一 run を生成 |
|
|
275
|
+
| `runops runs sweep [DIR] [--dry-run]` | survey.toml からパラメータ直積で全 run 一括生成 (`--dry-run` で件数・パラメータ組合せ・概算 core-hour のみ表示) |
|
|
276
|
+
| `runops runs submit [RUN]` | run を sbatch で投入 (`-qn` でキュー上書き、`--afterok` で依存ジョブ指定) |
|
|
277
|
+
| `runops runs submit --all [DIR]` | created な run を一括投入 |
|
|
278
|
+
| `runops runs clone` | run 複製・派生 |
|
|
279
|
+
| `runops runs extend` | スナップショットから継続 run 生成 |
|
|
280
|
+
|
|
281
|
+
### 状態管理・モニタリング
|
|
282
|
+
|
|
283
|
+
| コマンド | 説明 |
|
|
284
|
+
|---------|------|
|
|
285
|
+
| `runops runs status [RUNS...]` | run の状態確認 (run_id / run dir / survey dir を複数渡してまとめて表示可) |
|
|
286
|
+
| `runops runs sync [RUNS...]` | Slurm 状態を manifest.toml に反映 (bulk 対応: survey 配下の created run + terminal state な run は silent skip) |
|
|
287
|
+
| `runops runs log [RUN]` | 最新 job の stdout/stderr 表示 + 進捗% |
|
|
288
|
+
| `runops runs jobs [PATH] [--watch SECS]` | プロジェクト内の実行中ジョブ一覧 (`--watch` で自動更新) |
|
|
289
|
+
| `runops runs dashboard [TARGETS...] [--watch SECS] [--all]` | 複数 run の進捗 (state, step/N, %, last Slurm state) を 1 つの表で表示 |
|
|
290
|
+
| `runops runs history [PATH]` | 投入履歴表示 |
|
|
291
|
+
| `runops runs list [PATHS...]` | run の一覧表示 (複数 PATH 指定可、状態・タグでフィルタ可能) |
|
|
292
|
+
|
|
293
|
+
### 解析・整理
|
|
294
|
+
|
|
295
|
+
| コマンド | 説明 |
|
|
296
|
+
|---------|------|
|
|
297
|
+
| `runops analyze summarize [RUN]` | Adapter による run 解析 summary 生成 |
|
|
298
|
+
| `runops analyze collect [DIR]` | survey 内の全 run から集計データ生成 |
|
|
299
|
+
| `runops analyze plot [DIR]` | survey 集計結果の可視化 (`--recipe` / `--list-recipes` 対応) |
|
|
300
|
+
| `runops runs cancel [RUN]` | submitted/running な run を `scancel` + `sync` で安全に停止 |
|
|
301
|
+
| `runops runs archive [RUN]` | run のアーカイブ (completed のみ) |
|
|
302
|
+
| `runops runs purge-work [RUN]` | work/ 内の不要ファイル削除 (archived のみ) |
|
|
303
|
+
| `runops runs delete [RUN]` | created / cancelled / failed の run ディレクトリをハード削除 (completed/archived は archive → purge-work を使う) |
|
|
304
|
+
|
|
305
|
+
### Lab notebook (実験ノート)
|
|
306
|
+
|
|
307
|
+
| コマンド | 説明 |
|
|
308
|
+
|---------|------|
|
|
309
|
+
| `runops notes append TITLE [BODY]` | 今日の `notes/YYYY-MM-DD.md` に timestamped エントリを追記 (`-` または省略で stdin から本文を読む) |
|
|
310
|
+
| `runops notes list [-n N]` | 最近の lab notebook 日付一覧 (新しい順) |
|
|
311
|
+
| `runops notes show [DATE\|today\|latest]` | 指定日 (省略時は today) の lab notebook を表示 |
|
|
312
|
+
|
|
313
|
+
`notes/` は curated knowledge (`.runops/insights/`, `facts.toml`) と
|
|
314
|
+
**二層構造** で運用する append-only な実験ノートです。準備フェーズの意思決定、観察、
|
|
315
|
+
仮説、TODO をその場で残し、価値が出てきたら `notes/reports/` の long-form
|
|
316
|
+
レポートを経て `runops knowledge save` / `add-fact` で curated 層に昇格させます。
|
|
317
|
+
|
|
318
|
+
### 知識管理
|
|
319
|
+
|
|
320
|
+
| コマンド | 説明 |
|
|
321
|
+
|---------|------|
|
|
322
|
+
| `runops knowledge save NAME` | 知見を .runops/insights/ に保存 |
|
|
323
|
+
| `runops knowledge list` | 知見一覧表示 |
|
|
324
|
+
| `runops knowledge show NAME` | 知見の詳細表示 |
|
|
325
|
+
| `runops knowledge add-fact CLAIM` | 構造化された知識を facts.toml に追加 |
|
|
326
|
+
| `runops knowledge facts` | local facts と imported candidate facts の一覧表示 |
|
|
327
|
+
| `runops knowledge promote-fact FACT_ID` | candidate fact を local facts.toml に昇格 |
|
|
328
|
+
| `runops knowledge source list` | 外部知識ソース一覧表示 |
|
|
329
|
+
| `runops knowledge source attach TYPE NAME URL` | 外部知識ソースを接続 (git / path) |
|
|
330
|
+
| `runops knowledge source detach NAME` | 外部知識ソースを切断 |
|
|
331
|
+
| `runops knowledge source sync [NAME]` | 知識ソース同期 + insight / fact transport |
|
|
332
|
+
| `runops knowledge source render` | 有効な profile から imports.md を生成 |
|
|
333
|
+
| `runops knowledge source status` | 知識統合の状態表示 |
|
|
334
|
+
| `runops knowledge profile enable SOURCE PROFILE...` | source の profile を有効化して imports.md を更新 |
|
|
335
|
+
| `runops knowledge profile disable SOURCE PROFILE...` | source の profile を無効化して imports.md を更新 |
|
|
336
|
+
|
|
337
|
+
知識管理は三層構造:
|
|
338
|
+
- **source knowledge** — 外部共有知識リポジトリ (`refs/knowledge/` にマウント)
|
|
339
|
+
- **local knowledge** — プロジェクト固有の知見 (insights, facts)
|
|
340
|
+
- **derived knowledge** — source と local から生成される派生物 (imports.md, candidate fact transport 等)
|
|
341
|
+
|
|
342
|
+
profile source は repo ルートの `entrypoints.toml` で import 対象を明示できる。`imports.md` はこの manifest を優先し、未指定 profile は `profiles/<name>.md` にフォールバックする。
|
|
343
|
+
|
|
344
|
+
全コマンドは引数省略時にカレントディレクトリをデフォルトとする。
|
|
345
|
+
|
|
346
|
+
## プロジェクト構成
|
|
347
|
+
|
|
348
|
+
```
|
|
349
|
+
runops/
|
|
350
|
+
pyproject.toml
|
|
351
|
+
SPEC.md # 詳細仕様書
|
|
352
|
+
CLAUDE.md # 開発ガイド
|
|
353
|
+
AGENTS.md # Agent 運用ガイド
|
|
354
|
+
src/
|
|
355
|
+
runops/
|
|
356
|
+
cli/ # CLI エントリポイント (typer)
|
|
357
|
+
main.py # コマンド登録
|
|
358
|
+
init.py # init / setup / doctor
|
|
359
|
+
new.py # case new (`--minimal`, EMSES `emu generate -u`)
|
|
360
|
+
create.py # runs create / runs sweep (`--dry-run`)
|
|
361
|
+
submit.py # runs submit (`-qn`, `--afterok`)
|
|
362
|
+
status.py # runs status / runs sync (bulk-friendly)
|
|
363
|
+
log.py # runs log
|
|
364
|
+
jobs.py # runs jobs (`--watch`)
|
|
365
|
+
dashboard.py # runs dashboard (multi-run 進捗ビュー)
|
|
366
|
+
history.py # runs history
|
|
367
|
+
list.py # runs list (複数 PATH 対応)
|
|
368
|
+
clone.py # runs clone
|
|
369
|
+
extend.py # runs extend
|
|
370
|
+
analyze.py # analyze summarize / collect / plot
|
|
371
|
+
notes.py # notes append / list / show (lab notebook)
|
|
372
|
+
manage.py # runs archive / purge-work / cancel / delete
|
|
373
|
+
update.py # update (パッケージ更新)
|
|
374
|
+
update_refs.py # update-refs (refs/ 更新 + ナレッジ)
|
|
375
|
+
knowledge.py # knowledge / knowledge source
|
|
376
|
+
config.py # config (設定管理)
|
|
377
|
+
core/ # ドメインロジック
|
|
378
|
+
project.py # Project 読込・検証
|
|
379
|
+
case.py # Case 読込・展開
|
|
380
|
+
survey.py # Survey 展開・parameter 直積
|
|
381
|
+
run.py # Run 生成・run_id 採番
|
|
382
|
+
manifest.py # manifest.toml 読書き
|
|
383
|
+
state.py # 状態遷移管理
|
|
384
|
+
provenance.py # コード provenance 取得
|
|
385
|
+
discovery.py # runs/ 再帰探索・run_id 一意性検証
|
|
386
|
+
exceptions.py # ドメイン例外
|
|
387
|
+
validation.py # パラメータバリデーション
|
|
388
|
+
campaign.py # campaign.toml 読込
|
|
389
|
+
environment.py # 実行環境検出・記述
|
|
390
|
+
knowledge.py # 知識層 (insights, facts)
|
|
391
|
+
knowledge_source.py # 外部知識ソース管理
|
|
392
|
+
adapters/ # Simulator Adapter
|
|
393
|
+
base.py # SimulatorAdapter 抽象基底クラス
|
|
394
|
+
registry.py # Adapter 登録・lookup
|
|
395
|
+
generic.py # 汎用 Adapter 実装
|
|
396
|
+
launchers/ # Launcher Profile
|
|
397
|
+
base.py # Launcher 抽象基底クラス
|
|
398
|
+
srun.py # srun Launcher
|
|
399
|
+
mpirun.py # mpirun Launcher
|
|
400
|
+
mpiexec.py # mpiexec Launcher
|
|
401
|
+
jobgen/ # job.sh 生成
|
|
402
|
+
generator.py # Slurm batch script 生成
|
|
403
|
+
slurm/ # Slurm 連携
|
|
404
|
+
submit.py # sbatch 投入
|
|
405
|
+
query.py # squeue / sacct 問合せ
|
|
406
|
+
tests/
|
|
407
|
+
docs/
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
## 状態遷移
|
|
411
|
+
|
|
412
|
+
run は以下の状態遷移に従います:
|
|
413
|
+
|
|
414
|
+
```
|
|
415
|
+
created --> submitted --> running --> completed
|
|
416
|
+
| |
|
|
417
|
+
v v
|
|
418
|
+
failed failed
|
|
419
|
+
|
|
|
420
|
+
v
|
|
421
|
+
cancelled
|
|
422
|
+
|
|
423
|
+
completed --> archived --> purged
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
`runops runs cancel` は `submitted` / `running` の run に対して `scancel` と `sync`
|
|
427
|
+
を組み合わせて発行し、最終状態を `cancelled` に遷移させます。
|
|
428
|
+
`runops runs delete` はライフサイクル外の操作で、`created` / `cancelled` / `failed`
|
|
429
|
+
の run ディレクトリを直接削除します (`completed` / `archived` の run は
|
|
430
|
+
`archive` → `purge-work` 経路を使ってください)。
|
|
431
|
+
|
|
432
|
+
> **Note**: `runops runs sync` は Slurm の観測結果を manifest に反映するため、
|
|
433
|
+
> ポーリング間隔によっては `submitted → completed` のように途中状態を飛び越す遷移が発生します。
|
|
434
|
+
> 詳細は [SPEC.md](SPEC.md) を参照してください。
|
|
435
|
+
|
|
436
|
+
## 技術スタック
|
|
437
|
+
|
|
438
|
+
- Python 3.10+
|
|
439
|
+
- CLI: [typer](https://typer.tiangolo.com/) (click ベース)
|
|
440
|
+
- 設定ファイル: TOML (tomli / tomli-w)
|
|
441
|
+
- パッケージ管理: [uv](https://docs.astral.sh/uv/)
|
|
442
|
+
- テスト: pytest
|
|
443
|
+
- Lint/Format: ruff
|
|
444
|
+
- 型チェック: mypy (strict)
|
|
445
|
+
|
|
446
|
+
## 開発
|
|
447
|
+
|
|
448
|
+
```bash
|
|
449
|
+
# テスト
|
|
450
|
+
uv run pytest
|
|
451
|
+
|
|
452
|
+
# Lint
|
|
453
|
+
uv run ruff check src/ tests/
|
|
454
|
+
uv run ruff format --check src/ tests/
|
|
455
|
+
|
|
456
|
+
# 型チェック
|
|
457
|
+
uv run mypy src/
|
|
458
|
+
|
|
459
|
+
# CLI 実行 (開発中)
|
|
460
|
+
uv run runops --help
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
## ドキュメント
|
|
464
|
+
|
|
465
|
+
- [AI エージェントではじめる](docs/get-started-with-agent.md) -- `runops init` 済み project を Agent と進める最短導線
|
|
466
|
+
- [AI Agent 運用概念図](docs/project-flow.md) -- `runops init` 後の project を Agent とどう回すかの全体像
|
|
467
|
+
- [src 構成ガイド](docs/src-structure.md) -- `src/runops/` の層構造と adapter / launcher / site 解決の流れ
|
|
468
|
+
- [アーキテクチャ](docs/architecture.md) -- システム設計とモジュール構成
|
|
469
|
+
- [拡張ガイド](docs/extending.md) -- Adapter / Launcher の追加方法
|
|
470
|
+
- [知識層](docs/knowledge-layer.md) -- AI エージェント向け知識管理アーキテクチャ
|
|
471
|
+
- [TOML リファレンス](docs/toml-reference.md) -- 全設定ファイルのフィールド定義
|
|
472
|
+
- [SPEC.md](SPEC.md) -- 完全な仕様書
|
|
473
|
+
|
|
474
|
+
## ライセンス
|
|
475
|
+
|
|
476
|
+
Apache-2.0
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Simulator adapters for abstracting simulator-specific behavior.
|
|
2
|
+
|
|
3
|
+
Importing this package automatically registers the built-in adapters
|
|
4
|
+
(e.g. ``GenericAdapter``) in the global registry.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from runops.adapters.base import SimulatorAdapter
|
|
10
|
+
from runops.adapters.contrib.beach import BeachAdapter
|
|
11
|
+
from runops.adapters.contrib.emses import EmseAdapter
|
|
12
|
+
from runops.adapters.generic import GenericAdapter
|
|
13
|
+
from runops.adapters.registry import get, get_global_registry, list_adapters, register
|
|
14
|
+
|
|
15
|
+
# Register built-in adapters on import
|
|
16
|
+
register(GenericAdapter)
|
|
17
|
+
register(EmseAdapter)
|
|
18
|
+
register(BeachAdapter)
|
|
19
|
+
|
|
20
|
+
__all__ = [
|
|
21
|
+
"BeachAdapter",
|
|
22
|
+
"EmseAdapter",
|
|
23
|
+
"GenericAdapter",
|
|
24
|
+
"SimulatorAdapter",
|
|
25
|
+
"get",
|
|
26
|
+
"get_global_registry",
|
|
27
|
+
"list_adapters",
|
|
28
|
+
"register",
|
|
29
|
+
]
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""Shared utilities for simulator adapters."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from runops.adapters._utils.toml_utils import apply_dotted_overrides
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def find_venv(start: Path) -> Path | None:
|
|
11
|
+
"""Find the nearest ``.venv`` directory by searching upward.
|
|
12
|
+
|
|
13
|
+
Starts from *start* and walks parent directories until a ``.venv``
|
|
14
|
+
directory containing ``bin/activate`` (or ``Scripts/activate``) is found.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
start: Starting directory for the search.
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
Path to the venv directory, or ``None`` if not found.
|
|
21
|
+
"""
|
|
22
|
+
current = start.resolve()
|
|
23
|
+
for directory in [current, *current.parents]:
|
|
24
|
+
venv = directory / ".venv"
|
|
25
|
+
if venv.is_dir() and (
|
|
26
|
+
(venv / "bin" / "activate").exists()
|
|
27
|
+
or (venv / "Scripts" / "activate").exists()
|
|
28
|
+
):
|
|
29
|
+
return venv
|
|
30
|
+
return None
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
__all__ = [
|
|
34
|
+
"apply_dotted_overrides",
|
|
35
|
+
"find_venv",
|
|
36
|
+
]
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""Shared TOML configuration utilities for simulator adapters.
|
|
2
|
+
|
|
3
|
+
Provides deep-merge and dot-notation override for TOML-based configs.
|
|
4
|
+
Supports both dict navigation and list-index access for array-of-tables
|
|
5
|
+
(e.g. ``species.0.wp`` targets ``config["species"][0]["wp"]``).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import copy
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def deep_merge(base: dict[str, Any], override: dict[str, Any]) -> dict[str, Any]:
|
|
15
|
+
"""Deep-merge *override* into *base*.
|
|
16
|
+
|
|
17
|
+
Dicts are recursively merged; all other values (including lists)
|
|
18
|
+
are replaced.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
base: Base configuration dictionary.
|
|
22
|
+
override: Overrides to apply.
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
New merged dictionary (neither input is mutated).
|
|
26
|
+
"""
|
|
27
|
+
result = copy.deepcopy(base)
|
|
28
|
+
for key, value in override.items():
|
|
29
|
+
if key in result and isinstance(result[key], dict) and isinstance(value, dict):
|
|
30
|
+
result[key] = deep_merge(result[key], value)
|
|
31
|
+
else:
|
|
32
|
+
result[key] = copy.deepcopy(value)
|
|
33
|
+
return result
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def apply_dotted_overrides(
|
|
37
|
+
config: dict[str, Any],
|
|
38
|
+
overrides: dict[str, Any],
|
|
39
|
+
) -> dict[str, Any]:
|
|
40
|
+
"""Apply flat dot-notation overrides to a nested config dict.
|
|
41
|
+
|
|
42
|
+
Supports both dict keys and numeric list indices::
|
|
43
|
+
|
|
44
|
+
{"sim.dt": 1e-8} -> config["sim"]["dt"] = 1e-8
|
|
45
|
+
{"species.0.wp": 2.1} -> config["species"][0]["wp"] = 2.1
|
|
46
|
+
{"tmgrid.nx": 512} -> config["tmgrid"]["nx"] = 512
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
config: Base config dictionary.
|
|
50
|
+
overrides: Flat dot-notation key-value pairs.
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
New dict with overrides applied (input not mutated).
|
|
54
|
+
"""
|
|
55
|
+
result = copy.deepcopy(config)
|
|
56
|
+
for key, value in overrides.items():
|
|
57
|
+
parts = key.split(".")
|
|
58
|
+
target: Any = result
|
|
59
|
+
for part in parts[:-1]:
|
|
60
|
+
if isinstance(target, list):
|
|
61
|
+
target = target[int(part)]
|
|
62
|
+
elif part.isdigit() and isinstance(target.get(part), type(None)):
|
|
63
|
+
# Numeric key not present as string; skip if target is a list-like
|
|
64
|
+
idx = int(part)
|
|
65
|
+
# Check if the parent is storing a list
|
|
66
|
+
target = (
|
|
67
|
+
target[idx]
|
|
68
|
+
if isinstance(target, list)
|
|
69
|
+
else target.setdefault(part, {})
|
|
70
|
+
)
|
|
71
|
+
else:
|
|
72
|
+
if part not in target:
|
|
73
|
+
target[part] = {}
|
|
74
|
+
target = target[part]
|
|
75
|
+
# Set the final value
|
|
76
|
+
leaf = parts[-1]
|
|
77
|
+
if isinstance(target, list):
|
|
78
|
+
target[int(leaf)] = value
|
|
79
|
+
else:
|
|
80
|
+
target[leaf] = value
|
|
81
|
+
return result
|