Agentic CFD 工作流程
你將問題陳述轉化為值得信賴的模擬報告。讀者是一位工程師,他們主要會閱讀摘要,並且必須能夠檢查每一項聲明。四條規則:
- 程式碼計算每一個數字。 你撰寫程式碼;你絕不手動將結果、參考值或公式輸出鍵入報告中。理論推導以符號方式(使用 SymPy)完成並進行自我檢查。
- 每個選擇都有文件記錄。 每個 OpenFOAM 設定都屬於一個決策,該決策包含原因/效果/替代方案。否則檢查器會拒絕呈現報告。
- 不從記憶中提取。 參考數據、相關性和常數必須來自問題的
reference/檔案或引用的來源。如果你必須使用近似值,請在報告中說明。 - 像實驗室筆記本一樣即時記錄洞見。 你的部分工作是在你之後為代理人改進此工作流程。一旦出現故障、讓你驚訝、需要第二次嘗試、或在本指南中不清楚的地方,請在開始修復之前開啟一個洞見:
insights.log(case, stage, problem);解決後用insights.resolve(...)填寫診斷、修復、教訓和指南。不要把它們留到最後:長時間會話的記憶會丟失和扭曲。預計記錄 5-15 條;一個沒有條目的運行表示沒有仔細檢查。
檔案夾佈局
cfdlib/ 共享工具(請勿修改;將需要的更改記錄為洞見,階段為“tooling”)
foam.py render_case, run (timeout, PID file), stop, running; 讀取欄位和 postProcessing .dat 檔案
monitor.py parse_log, assess(健康檢查), preflight(短測試運行 + 運行時間預測)
verification.py 誤差範數、觀察到的階數、Richardson 外推法、GCI、level_sequence
(任何層級序列的收斂性,包括參考資料自身的)
signals.py 週期性訊號:循環、精煉峰值、週期性檢查、FFT 頻率
plots.py 圖形樣式和尺寸(figure, wide, sketch);to_svg 檢查文字重疊
report.py m(), tex_num(), tex_fix()(表格列的固定小數位數), tex_pct(), LETTERS, load_yaml,
environment_info(), render()
perf.py 每次運行的性能數據(summarize, machine)用於性能附錄
contract.py 報告合約檢查器(也提供 --decisions 模式)
insights.py 即時洞見日誌(記錄時檢查排版;拒絕覆蓋已填寫的條目)
foamdict.py OpenFOAM 字典解析器(由檢查器使用)
cases/<name>/ 每個問題一個資料夾
problem.yaml 由使用者給定:description(簡要概述), setup(事實列表:幾何形狀、
邊界條件、定義), given data(已知數據), requested outputs(要求的輸出)[+ reference/]。
不要更改物理設定。將 description 和 setup 原封不動地傳遞給報告。
reference/ 使用者提供的參考數據及引用
numerics.yaml 你的數值計劃(網格、運行長度、公差)
decisions.yaml 每個選擇,在設定案例時撰寫
insights.yaml 即時日誌(cfdlib.insights)
template/ OpenFOAM 案例(0/, constant/, system/)包含 {{ placeholders }}
theory.py / analytical.py, sketch.py, run.py, analyze.py
results/<name>/ 原始數據, results.json, report.html
已處理的範例(在開始前閱讀所有檔案;複製結構,而非數值設定):
cases/plane_poiseuille/:線性、穩態、精確解(驗證)。報告:results/plane_poiseuille/report.html。cases/lid_driven_cavity_re100/:非線性、穩態、無精確解、已發表的標竿測試(標竿比較、Richardson 外推法、殘差終止的 SIMPLEC、分開的預飛和生產指令)。報告:results/lid_driven_cavity_re100/report.html。其 insights.yaml 包含來自建構它的代理人的 15 個教訓。cases/stokes_first_problem/:線性、暫態、精確解(合併和分開的網格與時間步階研究、每步動量平衡、並行運行)。報告:results/stokes_first_problem/report.html。cases/dfg_cylinder_re100/:非線性、暫態且週期性(渦街)、標竿測試,包含多個參考層級、以 Python 生成的曲線體 O 型網格、MPI 運行和性能附錄。報告:results/dfg_cylinder_re100/report.html。其 insights.yaml 包含 16 個教訓(最重要的一個:PISO 在時間上是一階的)。
階段
0. 攝入
- 讀取
problem.yaml和任何reference/檔案。列出要求的輸出(它們將被賦予字母 a, b, c...)。 - 透過程式碼計算主導的無量綱數(Re, Ma, ...)。決定哪些物理適用。
- 如果數據缺失或矛盾,選擇一個有文件記錄的假設,並在報告的限制中標記它。絕不默默地憑空創造輸入。
- 如果參考資料沒有給出不確定性,請估計一個(其網格、其列印的數字、第二個來源)並記錄如何估計;在階段 5 中你會需要它。
1. 理論:無需模擬即可知道的內容
- 存在精確解:使用 SymPy 推導,自我檢查(滿足方程式和邊界條件),評估要求的量。請參閱
cases/plane_poiseuille/analytical.py。 - 沒有精確解(常見情況):在
theory.intro中說明,並給出理論能提供的內容:極限情況、縮放比例、守恆定律、對稱性、量的符號、預期的流動結構。請參閱cases/lid_driven_cavity_re100/theory.py。 - 每個推導的量都需要:為什麼重要、此問題中每個符號的意義和數值,以及代入數字的公式(便於手動檢查)。
2. 設定與決策
- 找到最接近的 OpenFOAM 範例(
$FOAM_TUTORIALS)並從其開始;進行最小的修改。 - 閱讀
references/openfoam-primer.md以了解每個檔案控制的內容和已知陷阱。 - 模板可能包含 Jinja 迴圈:從 Python 生成整個
blockMeshDict是可以的,並且是對曲線體進行網格劃分的方法(範例:cases/dfg_cylinder_re100/run.py中的mesh_spec())。 - 僅使用編輯/寫入工具編輯包含 LaTeX 的檔案,切勿透過 Bash heredocs 或內置 Python:Bash 工具會吃掉反斜線(
\times變成 TAB)。案例 3 和 4 都因此浪費了時間。 - 逐步撰寫
decisions.yaml:每個選擇一條記錄,帶有kind(physics / accuracy / solver / output)和涵蓋案例檔案中每個設定的settings模式。一旦一個案例被渲染,立即檢查覆蓋率:python -m cfdlib.contract --decisions cases/<name>/decisions.yaml <case dir>。 - 規劃網格研究:至少 3 個網格,細化比為 2(觀察到的階數和 GCI 所需)。暫態問題還需要時間步階研究(或者用數字論證時間誤差可忽略不計)。
- 在查看結果之前,為你的問題定義一個物理收斂/穩定性檢查(一個平衡量、一個守恆量、一個必須停止變化的監測值)及其公差。僅靠正規化殘差是不夠的。暫態問題:每步平衡(守恆量的變化 = 邊界通量)是一個強有力的檢查;它必須使用求解器自身的時間格式。
- 通過/失敗公差必須具有區分力:根據粗略的預飛誤差和預期的階數(最細網格應達到的目標)來設定其大小,而不是根據整數。比達到的精度鬆弛 100 倍的公差不證明任何事(案例 3)。使觀察到的精度階數成為一條獨立的規則。
3. 預飛測試(強制性,在任何長時間運行之前)
- 在最粗略和最細網格上使用
cfdlib.monitor.preflight()進行短時間運行(完整長度的百分之幾)。它在一個獨立的<case>_preflight副本中工作;將預飛和生產案例也渲染到單獨的目錄中,這樣重新運行其中一個就不會刪除另一個。 - 它會報告崩潰、NaN、Courant 數、殘差/Courant/連續性趨勢並預測運行時間。暫態運行:預測是可靠的。穩態運行:傳遞
residual_targets;結果是一個下限(SIMPLE 在後期和更細的網格上會變慢)。 - 列印預飛寫入的每個
postProcessing檔案的標題和前幾行,並檢查每個檔案是否非平凡(案例 4:一個壓力積分在每一步都寫入 0,悄然無聲)。 - 暫態非線性問題:一個短時間步階研究(3 個時間步,比為 2,在粗略網格上)應屬於預飛階段,在網格研究之前。它顯示了在時間上的觀察階數:PISO(1 個外部校正式)在案例 4 中被證明是一階的,並導致了完全的重新運行。
- 從靜止開始的預飛會低估已發展非穩態流動的成本(案例 4:兩次)。從一個已發展的流場(從粗略運行使用
mapFields)開始,或者預算兩倍的預測值。 - 如果預飛失敗或發出警告,請修復設定並記錄一個洞見。沒有預飛失敗的生產運行。如果預測時間過長(此處每個運行約 >15 分鐘),則放寬計劃並說明。
4. 生產運行
- 從粗略到精細運行網格。對於穩態運行,在啟動最細網格之前,根據較粗略網格外推其迭代次數(它通常每次細化增長 3-4 倍)。
- 停止必須是物理的:將階段 2 的物理檢查作為停止規則(例如,對探測值使用
runTimeControl函數物件,請參閱入門指南),或檢查每個網格的殘差目標是否確實滿足它,首先在粗略網格上檢查。 - 獨立運行(不同網格)可以同時運行,最多 6 個(8 個核心)。在再現性說明中提到這一點,因為它會增加每個運行的壁鐘時間。
- 長時間運行在背景中進行;
python -m cfdlib.foam ps列出求解器進程,python -m cfdlib.foam stop <case dir>終止一個(僅停止 Python 包裝器會留下孤兒求解器)。記錄每個運行:健康狀況(monitor.assess)、物理檢查、壁鐘時間。 - 將一系列長時間運行為一個背景腳本,該腳本寫入完成標記和每個運行的記錄。在任何中斷(API 暫停也一樣)之後,在重新運行任何東西之前先讀取這些檔案。
- 當所有硬體執行緒都在運行求解器時,你自己的工作會變慢:空出 1-2 個執行緒,或預算 1.5 倍的預測時間。
5. 分析
- 所有比較透過程式碼完成。對於精確解:每個網格的誤差和觀察到的階數。無精確解:從 3 個網格計算觀察到的階數和 Richardson 外推法,最細網格上的 GCI(
verification.py),然後將外推值與參考進行比較。 - 標竿測試也是一種模擬:根據(你的 GCI 區間 + 參考資料自身的不確定性)來判斷一致性,而不是根據零。如果差異不隨細化而縮小,那不是你的離散化誤差;在歸咎任何一方之前,先測試穩健性(另一種格式、更嚴格的迭代),並建議第二個獨立的參考資料。
- 當參考資料給出多個網格層級時,它仍在收斂:計算其自身的觀察階數和 Richardson 值(
verification.level_sequence),並將你的結果與其最細層級及其外推值進行比較。在查看之前決定通過規則(案例 4 保留了最細層級規則,並將外推法作為背景報告)。 - 週期性輸出(脫落、振盪):使用
cfdlib.signals(向上穿越之間的循環、拋物線精煉峰值、週期性檢查、FFT 交叉檢查),而不是自己撰寫。 - 粗略網格的匹配可能是運氣(誤差抵消)。誠實地報告差異,說明在哪裡以及為什麼。
- 解釋就是一個聲明:在撰寫之前,根據數據(符號、大小、縮放比例)進行檢查。在案例 3 中,錯誤的第一個解釋符號錯誤且大小只有三分之一。
6. 報告
- 在
analyze.py中構建results.json並呼叫report.render(results, svgs, out, case_dir=<最細案例>)。合約在references/report-contract.md中;render拒絕撰寫違反合約的報告。 - 數學:文字中的每個公式、符號和科學數字都作為
\( \)內的 LaTeX(m(),tex_num()):撰寫\(0.009\,U\),絕不寫0.009 U、6.3e-06或u*H/nu。檢查器拒絕 e 符號和 ASCII 數學,並警告純文字中的數字乘以符號。 - 圖說和發現也是結果:從數據生成它們(說“縮小”的圖說必須是計算得出的)。使用
{fig:<key>}引用圖形;render() 會插入編號。 - 圖形(
cfdlib.plots):plots.figure()用於單面板,plots.wide(n)用於 2-3 個面板,plots.sketch()用於問題草圖(必需)。to_svg()拒絕文字與文字、線條、箭頭或形狀重疊,或圖例覆蓋數據,或寬度超過列寬的圖形。修正佈局(標籤遠離線條、使用帶引導線的annotate、圖例在外);僅對真正有意圖的重疊使用allow=,並在需要時記錄一個洞見。 - 裁決:僅當你定義的每個檢查都通過時為 PASS;如果結果可用但有注意事項則為 WARN;否則為 FAIL。絕不軟化一個失敗的檢查。
7. 視覺審查(強制性)
合約檢查結構;只有查看才能捕捉到讀者會看到的內容。透過本地伺服器在內建瀏覽器中打開報告(file:// 無法使用):
http://localhost:8765/<name>/report.html(如果無法載入,請使用預覽工具從 .claude/launch.json 啟動 reports 伺服器)。重新渲染後添加 ?v=<n> 以避免快取副本。
- 從上到下截取每個部分的螢幕截圖。檢查:圖形可讀(文字不微小,沒有重疊,引導線不穿過曲線),表格沒有被切斷,數學渲染正確(沒有原始
\(),表格列對齊,摘要回答了每個要求。 - 在頁面中執行(等待 MathJax,打開每個 ⓘ 面板,然後列出內容被切斷的每個元素;應該為空;在 MathJax 完成之前立即執行,會將未排版的 TeX 報告為溢出):
await MathJax.startup.promise; document.querySelectorAll('details').forEach(d => d.open = true); [...document.querySelectorAll('section *')].filter(x => !x.tagName.startsWith('MJX') && x.scrollWidth > x.clientWidth) - 檢查:表格在窄螢幕上沒有換行崩潰,下拉選單打開,專有名詞大寫一致,文字內參考文獻(
[1])沒有變成原始符號,{fig:...}全部被替換。
如果報告的某個部分看起來不對,請記錄一個洞見(stage=visual_review),無論多麼微小。這是確保你為下一個代理人改進模板的唯一方法。
洞見:即時實驗室筆記本
在 cases/<name>/insights.yaml 中運作。所有函數和格式在 cfdlib/insights.py 中有記錄。
記錄任何值得注意的事情,特別是:
- 工作流程的文件缺失、錯誤或令人困惑(guideline: "doc")
- 一個設定花費了你兩次或更多嘗試(guideline: "setup")
- 一個你認為對未來代理人有用的開銷或模式(guideline: "pattern")
- 一個檢查失敗,因為它太嚴格或太寬鬆(guideline: "check")
- 任何花費 >2 分鐘除錯的問題(guideline: "debug")
不要記錄:
- 你已經修復的簡單打字錯誤(改進指南;僅在不確定修復是否完整時記錄)
- 關於結果的個人意見(使用分析中的事實)
from cfdlib.insights import log, resolve
# 遇到問題時立即記錄
log(case="lid_driven_cavity_re100", stage="setup", problem="解算器在 1000 次迭代後不收斂")
# ... 診斷並修復後 ...
resolve(diagnosis="初始殘差目標 1e-6 太嚴格;鬆弛因子需要調整",
fix="將殘差目標更改為 1e-4 並將 momentumU 鬆弛從 0.7 調整為 0.5",
lesson="在穩態 SIMPLEC 中,過於嚴格的殘差目標會導致收斂失敗;使用預飛測試來估算可達到的殘差水平",
guideline="穩態模擬的殘差目標應根據預飛測試的粗略網格誤差來選擇,而不是使用任意小的數字。")
工具使用常見陷阱
- Bash heredocs 破壞 LaTeX:
cat << 'EOF' > file中字串內的\times等會遺失。始終使用編輯/寫入工具處理含有反斜線的文字。 - 短運行與長運行: 從靜止開始的預飛(案例 4 中的暫態運行)低估了已發展週期狀態的成本。從
mapFields開始。 - 同時運行耗盡記憶體: 並行 MPI 運行(每個運行 2-4 個核心)乘以 6 個運行可能超過 8 個核心。腳本中進行分時或緩衝。
- 停止 Python 包裝器使求解器孤立: 始終使用
cfdlib.foam.stop(case),而不僅僅是KeyboardInterrupt。 - 靜默的 postProcessing: 檢查
postProcessing檔案是否非平凡,特別是在functionObjectProperties錯誤時。 - 模板中的
{{與 Jinja: OpenFOAM 本身不使用{{}};代理人的模板引擎對 dict 條目進行 Jinja 渲染。引用在檔案中的文字{{必須在模板中轉義。 for和if對子字典的影響: Jinja 的{% for %}作用於 key, value;訪問value["key"]而不是.key。整數索引是loop.index0。- 在 Python 字串中組合
\和{會破壞 LaTeX 渲染: 使用r"\\(...\\)"原始字串,或在 f-string 中雙重轉義。
開始新問題
- 將
template資料夾複製到cases/<name>/(包含 OpenFOAM 案例骨架)。 - 撰寫
problem.yaml和reference/。 - 執行階段 0-7。
- 每次
render()後執行視覺審查。 - 在提交訊息中包含
insights.yaml摘要。
有關參考格式和報告合約,請參閱 references/ 中的文件。