Agentic CFD 워크플로우
주어진 문제 설명을 신뢰할 수 있는 시뮬레이션 보고서로 변환합니다. 독자는 엔지니어이며, 주로 요약을 읽고 모든 주장을 확인할 수 있어야 합니다. 네 가지 규칙이 있습니다:
- 모든 숫자는 코드가 계산합니다. 코드를 작성하고, 결과값, 참고값 또는 수식 출력을 보고서에 수동으로 입력하지 않습니다. 이론은 기호적으로(SymPy) 풀고 자체 검증합니다.
- 모든 선택은 문서화됩니다. 각 OpenFOAM 설정은 이유/효과/대안을 포함한 결정에 속합니다. 그렇지 않으면 검사기가 보고서 생성을 거부합니다.
- 기억에서 가져오지 않습니다. 참고 데이터, 상관식, 상수는 문제의
reference/파일이나 인용된 출처에서 가져옵니다. 근사값을 사용해야 한다면 보고서에 그렇게 명시합니다. - 로그 통찰은 실시간으로, 실험 노트처럼 기록합니다. 여러분의 임무 중 일부는 이후의 에이전트를 위해 이 워크플로우를 개선하는 것입니다. 무언가 실패하거나, 놀라움을 주거나, 두 번째 시도가 필요하거나, 이 지침이 불분명한 순간, 문제를 해결하기 전에 통찰을 엽니다:
insights.log(case, stage, problem); 진단, 수정, 교훈 및 지침을insights.resolve(...)로 해결한 후 채웁니다. 끝날 때까지 모아두지 마십시오: 긴 세션의 기억은 손실되고 왜곡됩니다. 5-15개의 항목을 기록할 것으로 예상하며, 기록이 하나도 없는 실행은 면밀히 검토되지 않은 것입니다.
폴더 구조
cfdlib/ 공유 도구 (수정하지 마십시오; 필요한 변경사항은 insights, stage "tooling"으로 기록)
foam.py render_case, run (타임아웃, PID 파일), stop, running; 필드 및 postProcessing .dat 파일 읽기
monitor.py parse_log, assess (상태 점검), preflight (짧은 테스트 실행 + 실행 시간 예측)
verification.py 오차 노름, 관측 차수, Richardson 외삽, GCI, level_sequence
(모든 레벨 시퀀스의 수렴, 참조 자체의 것 포함)
signals.py 주기 신호: cycles, refined peaks, periodicity check, 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/ {{ placeholders }}가 포함된 OpenFOAM 케이스 (0/, constant/, system/)
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는 시간에 대해 1차 정확도였습니다).
단계
0. 접수 (Intake)
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 heredoc이나 인라인 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에서 1차 정확도로 밝혀져 전체 재실행 비용이 발생했습니다.
- 정지 상태에서 시작하는 사전 테스트는 발달된 비정상 유동의 비용을 과소평가합니다 (사례 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 대역 + 참조 자체의 불확실성)에 대해 판단하고, 0에 대해서는 판단하지 않습니다. 차이가 세분화에 따라 줄어들지 않으면, 이산화 오차가 아닙니다; 어느 한쪽을 비난하기 전에 강건성을 테스트하고 (다른 기법, 더 엄격한 반복), 두 번째 독립적인 참조를 권장합니다.
- 참조가 여러 격자 레벨을 제공하는 경우, 여전히 수렴 중입니다: 자체의 관측 차수와 Richardson 값을 계산하고 (
verification.level_sequence) 자신의 결과를 가장 미세한 레벨 및 외삽값 모두와 비교합니다. 합격 규칙을 보기 전에 결정합니다 (사례 4는 가장 미세한 레벨 규칙을 유지하고 외삽을 맥락으로 보고했습니다). - 주기적 출력 (와류 유격, 진동):
cfdlib.signals를 사용합니다 (상향 교차 사이의 주기, 포물선 정제된 피크, 주기성 확인, FFT 교차 확인) 자체 작성 대신. - 거친 격자 일치는 운 (오차 상쇄)일 수 있습니다. 차이점을 정직하게, 어디서 왜 발생했는지 보고합니다.
- 설명은 주장입니다: 쓰기 전에 데이터 (부호, 크기, 스케일링)에 대해 확인합니다. 사례 3에서 오류에 대한 첫 번째 설명은 부호가 반대이고 크기가 1/3이었습니다.
6. 보고서
analyze.py에서results.json을 구축하고report.render(results, svgs, out, case_dir=<finest case>)를 호출합니다. 계약은references/report-contract.md에 있습니다;render는 계약을 위반하는 보고서 작성을 거부합니다.- 수학: 텍스트의 모든 공식, 기호 및 과학적 숫자는
\( \)안에 LaTeX로 표시합니다 (m(),tex_num()사용):\(0.009\,U\)로 쓰고, 절대0.009 U,6.3e-06또는u*H/nu로 쓰지 마십시오. 검사기는 e-표기법 및 ASCII 수학을 거부하고, 일반 텍스트의 숫자-기호 곱에 대해 경고합니다. - 캡션 및 발견 사항도 결과입니다: 데이터에서 생성합니다 ("줄어든다"는 캡션은 계산되어야 함). 그림을
{fig:<키>}로 참조합니다; render()가 번호를 삽입합니다. - 그림 (
cfdlib.plots):plots.figure()는 한 패널,plots.wide(n)은 2-3 패널,plots.sketch()는 문제 스케치용 (필수).to_svg()는 텍스트가 텍스트, 선, 화살표 또는 도형과 겹치거나, 범례가 데이터를 덮거나, 열보다 넓은 그림을 거부합니다. 레이아웃을 수정하십시오 (레이블을 선에서 멀리, 지시선이 있는annotate, 범례는 외부); 정말 의도된 겹침에 대해서만allow=를 사용하고, 필요하다면 통찰을 기록합니다. - 평결: 정의된 모든 검사가 통과하는 경우에만 PASS; caveat와 함께 결과를 사용할 수 있으면 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).map(x => x.tagName + (x.id ? '#' + x.id : '') + (x.className ? '.' + x.className : '') + ' (' + x.clientWidth + ' vs ' + x.scrollWidth + ')');