initial commit FESurrogateModelTutorial
@@ -0,0 +1,42 @@
|
||||
---
|
||||
name: harness-review
|
||||
description: "Use when reviewing this Harness repository: local changes, generated phase files, step outputs, implementation diffs, missing tests, build readiness, or compliance with AGENTS.md, docs/ARCHITECTURE.md, docs/ADR.md, and Harness acceptance criteria."
|
||||
---
|
||||
|
||||
# Harness Review
|
||||
|
||||
## Overview
|
||||
|
||||
Use this skill to review Harness work against the repository's persistent rules, architecture docs, and executable verification requirements. Prioritize bugs, regressions, missing tests, and rule violations.
|
||||
|
||||
## Review Process
|
||||
|
||||
1. Read `/AGENTS.md`, `/docs/ARCHITECTURE.md`, and `/docs/ADR.md`.
|
||||
2. Inspect the changed files with `git status --short` and `git diff`.
|
||||
3. Check architecture, stack choices, tests, critical rules, and build readiness.
|
||||
4. Run relevant verification commands when feasible. If a command cannot be run, report that as residual risk.
|
||||
5. Lead with actionable findings. Keep summaries secondary.
|
||||
|
||||
## Checklist
|
||||
|
||||
| Item | Question |
|
||||
| --- | --- |
|
||||
| Architecture | Does the change follow `docs/ARCHITECTURE.md` directory and module boundaries? |
|
||||
| Stack | Does the change stay within choices documented in `docs/ADR.md`? |
|
||||
| Tests | Are new or changed behaviors covered by tests? |
|
||||
| Critical Rules | Does the change violate any `AGENTS.md` CRITICAL rule? |
|
||||
| Build | Do relevant build/test/lint commands pass? |
|
||||
|
||||
## Output Format
|
||||
|
||||
If there are findings, list them first in severity order with file and line references when possible. Then include this table:
|
||||
|
||||
| 항목 | 결과 | 비고 |
|
||||
| --- | --- | --- |
|
||||
| 아키텍처 준수 | PASS/FAIL | {상세} |
|
||||
| 기술 스택 준수 | PASS/FAIL | {상세} |
|
||||
| 테스트 존재 | PASS/FAIL | {상세} |
|
||||
| CRITICAL 규칙 | PASS/FAIL | {상세} |
|
||||
| 빌드 가능 | PASS/FAIL | {상세} |
|
||||
|
||||
When there are no findings, say that clearly, then mention any commands not run or remaining risk.
|
||||
@@ -0,0 +1,4 @@
|
||||
interface:
|
||||
display_name: "Harness Review"
|
||||
short_description: "Review Harness changes safely"
|
||||
default_prompt: "Use $harness-review to review Harness repository changes."
|
||||
@@ -0,0 +1,124 @@
|
||||
---
|
||||
name: harness-workflow
|
||||
description: "Use when planning or running this Harness framework: reading AGENTS.md and docs/*.md, discussing implementation scope, creating or updating phases/index.json, phases/{task}/index.json, phases/{task}/stepN.md, or invoking scripts/execute.py for staged Codex execution."
|
||||
---
|
||||
|
||||
# Harness Workflow
|
||||
|
||||
## Overview
|
||||
|
||||
Use this skill to turn a user-approved task into small, self-contained Harness steps that another Codex session can execute reliably. Keep the workflow grounded in repository docs and executable acceptance criteria.
|
||||
|
||||
## Workflow
|
||||
|
||||
1. Read `AGENTS.md` and relevant files under `docs/`, especially `docs/PRD.md`, `docs/ARCHITECTURE.md`, and `docs/ADR.md`.
|
||||
2. Discuss unresolved product or technical decisions with the user before writing phase files.
|
||||
3. When the user asks for an implementation plan, draft steps and get approval before creating files.
|
||||
4. Create or update `phases/index.json`, `phases/{task-name}/index.json`, and one `phases/{task-name}/stepN.md` per step.
|
||||
5. Run the phase with `python scripts/execute.py {task-name}` when asked to execute it. Use `--push` only when the user asks to push.
|
||||
|
||||
## Step Design Rules
|
||||
|
||||
- Scope each step to one layer or module. Split steps when multiple modules would otherwise change together.
|
||||
- Make every step self-contained. Do not rely on prior conversation; include all required context and file paths.
|
||||
- Force context gathering. Each step must tell Codex which docs and previous outputs to read before editing.
|
||||
- Specify interfaces and signatures, not full implementations, unless exact code is required for a constraint.
|
||||
- Put core invariants directly in the step: idempotency, security, data integrity, API contracts, or other non-negotiables.
|
||||
- Use executable acceptance criteria such as `npm run build && npm test`, not abstract statements.
|
||||
- Write cautions concretely: "Do not do X. Reason: Y."
|
||||
- Name steps with kebab-case slugs such as `project-setup`, `api-layer`, or `auth-flow`.
|
||||
|
||||
## Phase Files
|
||||
|
||||
Create or update `phases/index.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"phases": [
|
||||
{
|
||||
"dir": "0-mvp",
|
||||
"status": "pending"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Create `phases/{task-name}/index.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"project": "<project-name>",
|
||||
"phase": "<task-name>",
|
||||
"steps": [
|
||||
{ "step": 0, "name": "project-setup", "status": "pending" },
|
||||
{ "step": 1, "name": "core-types", "status": "pending" },
|
||||
{ "step": 2, "name": "api-layer", "status": "pending" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Rules:
|
||||
|
||||
- `project` comes from `AGENTS.md`.
|
||||
- `phase` matches the task directory name.
|
||||
- `steps[].step` starts at `0`.
|
||||
- Initial status is always `pending`.
|
||||
- Do not add timestamps when creating files. `scripts/execute.py` records `created_at`, `started_at`, `completed_at`, `failed_at`, and `blocked_at`.
|
||||
|
||||
## Step Template
|
||||
|
||||
```markdown
|
||||
# Step {N}: {name}
|
||||
|
||||
## 읽어야 할 파일
|
||||
|
||||
먼저 아래 파일들을 읽고 프로젝트의 아키텍처와 설계 의도를 파악하라:
|
||||
|
||||
- `/AGENTS.md`
|
||||
- `/docs/ARCHITECTURE.md`
|
||||
- `/docs/ADR.md`
|
||||
- {previously created or modified files}
|
||||
|
||||
이전 step에서 만들어진 코드를 꼼꼼히 읽고, 설계 의도를 이해한 뒤 작업하라.
|
||||
|
||||
## 작업
|
||||
|
||||
{Concrete instructions with file paths, interfaces, signatures, and rules.}
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
npm test
|
||||
```
|
||||
|
||||
## 검증 절차
|
||||
|
||||
1. 위 AC 커맨드를 실행한다.
|
||||
2. 아키텍처 체크리스트를 확인한다:
|
||||
- ARCHITECTURE.md 디렉토리 구조를 따르는가?
|
||||
- ADR 기술 스택을 벗어나지 않았는가?
|
||||
- AGENTS.md CRITICAL 규칙을 위반하지 않았는가?
|
||||
3. 결과에 따라 `phases/{task-name}/index.json`의 해당 step을 업데이트한다:
|
||||
- 성공: `"status": "completed"`, `"summary": "산출물 한 줄 요약"`
|
||||
- 3회 수정 시도 후 실패: `"status": "error"`, `"error_message": "구체적 에러 내용"`
|
||||
- 사용자 개입 필요: `"status": "blocked"`, `"blocked_reason": "구체적 사유"` 후 중단
|
||||
|
||||
## 금지사항
|
||||
|
||||
- {Do not do X. Reason: Y.}
|
||||
- 기존 테스트를 깨뜨리지 마라.
|
||||
```
|
||||
|
||||
## Execution And Recovery
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
python scripts/execute.py {task-name}
|
||||
python scripts/execute.py {task-name} --push
|
||||
```
|
||||
|
||||
`scripts/execute.py` creates or checks out `feat-{task-name}`, injects `AGENTS.md` and `docs/*.md` into each prompt, carries completed step summaries forward, retries failed steps up to three times, separates code and metadata commits, and records timestamps.
|
||||
|
||||
If a step is `error`, set it back to `pending` and remove `error_message` after fixing the cause. If a step is `blocked`, resolve `blocked_reason`, set it back to `pending`, remove `blocked_reason`, and rerun.
|
||||
@@ -0,0 +1,4 @@
|
||||
interface:
|
||||
display_name: "Harness Workflow"
|
||||
short_description: "Plan staged Harness workflow steps"
|
||||
default_prompt: "Use $harness-workflow to plan Harness phases and step files."
|
||||
@@ -0,0 +1,4 @@
|
||||
#:schema https://developers.openai.com/codex/config-schema.json
|
||||
|
||||
[features]
|
||||
codex_hooks = true
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"hooks": {
|
||||
"PreToolUse": [
|
||||
{
|
||||
"matcher": "^Bash$",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "python -c \"import pathlib, runpy, subprocess; root = pathlib.Path(subprocess.check_output(['git', 'rev-parse', '--show-toplevel'], text=True).strip()); runpy.run_path(str(root / '.codex' / 'hooks' / 'pre_commit_checks.py'), run_name='__main__')\"",
|
||||
"timeout": 600,
|
||||
"statusMessage": "Running pre-commit checks"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"matcher": "^(apply_patch|Edit|Write)$",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "python -c \"import pathlib, runpy, subprocess; root = pathlib.Path(subprocess.check_output(['git', 'rev-parse', '--show-toplevel'], text=True).strip()); runpy.run_path(str(root / '.codex' / 'hooks' / 'tdd-guard.py'), run_name='__main__')\"",
|
||||
"timeout": 30,
|
||||
"statusMessage": "Checking TDD guard"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
import json
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
CHECKS = ("lint", "build", "test")
|
||||
|
||||
|
||||
def _repo_root(cwd: Path) -> Path:
|
||||
try:
|
||||
root = subprocess.check_output(
|
||||
["git", "rev-parse", "--show-toplevel"],
|
||||
cwd=cwd,
|
||||
text=True,
|
||||
stderr=subprocess.DEVNULL,
|
||||
).strip()
|
||||
except (subprocess.CalledProcessError, FileNotFoundError):
|
||||
return cwd
|
||||
return Path(root)
|
||||
|
||||
|
||||
def _load_scripts(root: Path) -> dict[str, str]:
|
||||
package_json = root / "package.json"
|
||||
if not package_json.exists():
|
||||
return {}
|
||||
|
||||
try:
|
||||
package = json.loads(package_json.read_text(encoding="utf-8"))
|
||||
except json.JSONDecodeError as exc:
|
||||
_deny(f"Invalid package.json: {exc}")
|
||||
raise SystemExit(0) from exc
|
||||
|
||||
scripts = package.get("scripts", {})
|
||||
if not isinstance(scripts, dict):
|
||||
return {}
|
||||
return {str(name): str(command) for name, command in scripts.items()}
|
||||
|
||||
|
||||
def _is_git_commit(command: str) -> bool:
|
||||
return re.search(r"\bgit(?:\s+(?:-[A-Za-z]\s+\S+|--[A-Za-z0-9-]+(?:=\S+)?))*\s+commit\b", command) is not None
|
||||
|
||||
|
||||
def _deny(reason: str) -> None:
|
||||
print(
|
||||
json.dumps(
|
||||
{
|
||||
"hookSpecificOutput": {
|
||||
"hookEventName": "PreToolUse",
|
||||
"permissionDecision": "deny",
|
||||
"permissionDecisionReason": reason,
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def _tail(text: str, limit: int = 1200) -> str:
|
||||
text = text.strip()
|
||||
if len(text) <= limit:
|
||||
return text
|
||||
return text[-limit:]
|
||||
|
||||
|
||||
def _run_checks(root: Path, scripts: dict[str, str]) -> str | None:
|
||||
npm = shutil.which("npm") or shutil.which("npm.cmd")
|
||||
if npm is None:
|
||||
return "npm was not found, so pre-commit checks could not run."
|
||||
|
||||
for check in CHECKS:
|
||||
if check not in scripts:
|
||||
continue
|
||||
result = subprocess.run(
|
||||
[npm, "run", check],
|
||||
cwd=root,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
|
||||
if result.returncode != 0:
|
||||
details = _tail(result.stdout + "\n" + result.stderr)
|
||||
if details:
|
||||
return f"npm run {check} failed:\n{details}"
|
||||
return f"npm run {check} failed with exit code {result.returncode}."
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def main() -> int:
|
||||
try:
|
||||
payload = json.load(sys.stdin)
|
||||
except json.JSONDecodeError:
|
||||
return 0
|
||||
|
||||
command = payload.get("tool_input", {}).get("command", "")
|
||||
if not isinstance(command, str) or not _is_git_commit(command):
|
||||
return 0
|
||||
|
||||
cwd = Path(payload.get("cwd") or Path.cwd())
|
||||
root = _repo_root(cwd)
|
||||
failure = _run_checks(root, _load_scripts(root))
|
||||
if failure:
|
||||
_deny(f"PRE-COMMIT CHECKS: {failure}")
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,189 @@
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
SOURCE_SUFFIXES = {".ts", ".tsx", ".js", ".jsx"}
|
||||
TEST_SUFFIXES = ("ts", "tsx", "js", "jsx")
|
||||
CONFIG_SUFFIXES = {".json", ".css", ".scss", ".md", ".yml", ".yaml"}
|
||||
NEXT_SPECIAL_FILES = {
|
||||
"layout.ts",
|
||||
"layout.tsx",
|
||||
"page.ts",
|
||||
"page.tsx",
|
||||
"loading.tsx",
|
||||
"error.tsx",
|
||||
"not-found.tsx",
|
||||
"globals.css",
|
||||
}
|
||||
|
||||
|
||||
def _repo_root(cwd: Path) -> Path:
|
||||
try:
|
||||
root = subprocess.check_output(
|
||||
["git", "rev-parse", "--show-toplevel"],
|
||||
cwd=cwd,
|
||||
text=True,
|
||||
stderr=subprocess.DEVNULL,
|
||||
).strip()
|
||||
except (subprocess.CalledProcessError, FileNotFoundError):
|
||||
return cwd
|
||||
return Path(root)
|
||||
|
||||
|
||||
def _extract_patch_paths(command: str) -> list[str]:
|
||||
prefixes = (
|
||||
"*** Add File: ",
|
||||
"*** Update File: ",
|
||||
"*** Delete File: ",
|
||||
"*** Move to: ",
|
||||
)
|
||||
paths: list[str] = []
|
||||
for raw_line in command.splitlines():
|
||||
line = raw_line.strip()
|
||||
for prefix in prefixes:
|
||||
if line.startswith(prefix):
|
||||
paths.append(line[len(prefix) :].strip())
|
||||
break
|
||||
return paths
|
||||
|
||||
|
||||
def _touched_paths(payload: dict) -> list[str]:
|
||||
tool_input = payload.get("tool_input", {})
|
||||
if not isinstance(tool_input, dict):
|
||||
return []
|
||||
|
||||
file_path = tool_input.get("file_path")
|
||||
if isinstance(file_path, str) and file_path:
|
||||
return [file_path]
|
||||
|
||||
command = tool_input.get("command")
|
||||
if isinstance(command, str):
|
||||
return _extract_patch_paths(command)
|
||||
|
||||
return []
|
||||
|
||||
|
||||
def _normalize(path_text: str) -> str:
|
||||
return path_text.replace("\\", "/").lower()
|
||||
|
||||
|
||||
def _is_test_path(path_text: str) -> bool:
|
||||
normalized = _normalize(path_text)
|
||||
name = normalized.rsplit("/", 1)[-1]
|
||||
return (
|
||||
"__tests__/" in normalized
|
||||
or ".test." in name
|
||||
or ".spec." in name
|
||||
or "test" in name
|
||||
or "spec" in name
|
||||
)
|
||||
|
||||
|
||||
def _is_exempt(path_text: str) -> bool:
|
||||
normalized = _normalize(path_text)
|
||||
path = Path(path_text)
|
||||
name = path.name.lower()
|
||||
|
||||
if _is_test_path(path_text):
|
||||
return True
|
||||
if name in NEXT_SPECIAL_FILES:
|
||||
return True
|
||||
if path.suffix.lower() in CONFIG_SUFFIXES:
|
||||
return True
|
||||
if ".env" in name or ".config." in name:
|
||||
return True
|
||||
if any(token in name for token in ("tailwind", "postcss", "next.config", "tsconfig")):
|
||||
return True
|
||||
if "/types/" in normalized or name in {"types.ts", "types.d.ts"}:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def _resolve_path(path_text: str, cwd: Path) -> Path:
|
||||
path = Path(path_text)
|
||||
if path.is_absolute():
|
||||
return path
|
||||
return (cwd / path).resolve()
|
||||
|
||||
|
||||
def _base_name(path: Path) -> str:
|
||||
for suffix in (".tsx", ".ts", ".jsx", ".js"):
|
||||
if path.name.endswith(suffix):
|
||||
return path.name[: -len(suffix)]
|
||||
return path.stem
|
||||
|
||||
|
||||
def _has_existing_test(path: Path, root: Path) -> bool:
|
||||
directory = path.parent
|
||||
parent = directory.parent
|
||||
base = _base_name(path)
|
||||
|
||||
for ext in TEST_SUFFIXES:
|
||||
if (directory / f"{base}.test.{ext}").exists():
|
||||
return True
|
||||
if (directory / f"{base}.spec.{ext}").exists():
|
||||
return True
|
||||
|
||||
for ext in TEST_SUFFIXES:
|
||||
if (parent / "__tests__" / f"{base}.test.{ext}").exists():
|
||||
return True
|
||||
if (directory / "__tests__" / f"{base}.test.{ext}").exists():
|
||||
return True
|
||||
|
||||
for ext in TEST_SUFFIXES:
|
||||
if (root / "src" / "__tests__" / f"{base}.test.{ext}").exists():
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def _guarded_paths(paths: list[str], cwd: Path, root: Path) -> list[str]:
|
||||
missing_tests: list[str] = []
|
||||
for path_text in paths:
|
||||
if _is_exempt(path_text):
|
||||
continue
|
||||
|
||||
path = _resolve_path(path_text, cwd)
|
||||
if path.suffix.lower() not in SOURCE_SUFFIXES:
|
||||
continue
|
||||
if not _has_existing_test(path, root):
|
||||
missing_tests.append(_base_name(path))
|
||||
|
||||
return missing_tests
|
||||
|
||||
|
||||
def main() -> int:
|
||||
try:
|
||||
payload = json.load(sys.stdin)
|
||||
except json.JSONDecodeError:
|
||||
return 0
|
||||
|
||||
cwd = Path(payload.get("cwd") or Path.cwd())
|
||||
root = _repo_root(cwd)
|
||||
missing_tests = _guarded_paths(_touched_paths(payload), cwd, root)
|
||||
if not missing_tests:
|
||||
return 0
|
||||
|
||||
names = ", ".join(sorted(set(missing_tests)))
|
||||
print(
|
||||
json.dumps(
|
||||
{
|
||||
"hookSpecificOutput": {
|
||||
"hookEventName": "PreToolUse",
|
||||
"permissionDecision": "deny",
|
||||
"permissionDecisionReason": (
|
||||
"TDD GUARD: missing test file for "
|
||||
f"{names}. Write or add the test first."
|
||||
),
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,19 @@
|
||||
node_modules/
|
||||
.next/
|
||||
out/
|
||||
next-env.d.ts
|
||||
tsconfig.tsbuildinfo
|
||||
|
||||
# Python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
.pytest_cache/
|
||||
.ruff_cache/
|
||||
.venv/
|
||||
*.egg-info/
|
||||
.ipynb_checkpoints/
|
||||
*.nbconvert.ipynb
|
||||
|
||||
# phase execution outputs
|
||||
phases/**/phase*-output.json
|
||||
phases/**/step*-output.json
|
||||
@@ -0,0 +1,168 @@
|
||||
# FEMSurrogateTutorial Agent Instructions
|
||||
|
||||
## Project Purpose
|
||||
This repository is a Korean tutorial project for studying surrogate models in finite element method (FEM) structural analysis. The tutorial combines:
|
||||
- Theory documents on surrogate modeling methods.
|
||||
- NumPy/SciPy implementation of a 2D Euler-Bernoulli beam/frame element.
|
||||
- Jupyter notebooks that build, validate, and compare surrogate models from generated FEM data.
|
||||
|
||||
The primary audience is graduate students and researchers who already know basic FEM and Python.
|
||||
|
||||
## Tech Stack
|
||||
- Python `>=3.12,<3.15`
|
||||
- Environment and dependency management: `uv`, `pyproject.toml`, `uv.lock`
|
||||
- Numerical computing: NumPy, SciPy
|
||||
- Machine learning: scikit-learn
|
||||
- Data and plotting: pandas, matplotlib
|
||||
- Notebook workflow: JupyterLab, ipykernel, nbconvert
|
||||
- Testing and quality: pytest, ruff
|
||||
- Model/result persistence: joblib, CSV, JSON
|
||||
|
||||
## Architecture Rules
|
||||
- CRITICAL: At the start of every new task or session, read `AGENTS.md`, `PROGRESS.md`, and `WORKNOTES.md` before planning or editing. Use them to understand current scope, completed work, open tasks, and known pitfalls.
|
||||
- CRITICAL: Keep `PROGRESS.md` current while working. Update it whenever the task state materially changes, including completed milestones, in-progress work, blockers, verification status, and next actions.
|
||||
- CRITICAL: Record meaningful trial-and-error, mistakes, false starts, debugging discoveries, and corrective decisions in `WORKNOTES.md`. Do not log routine command output; log lessons future agents need.
|
||||
- CRITICAL: Core calculation logic must live under `src/femsurrogate/`; notebooks should explain and orchestrate, not hide reusable implementation in cells.
|
||||
- CRITICAL: FEM data for the tutorial must be generated with the in-repository 2D beam/frame implementation using NumPy/SciPy. Do not introduce Abaqus, ANSYS, OpenSeesPy, CalculiX, or other external solvers for v1.
|
||||
- CRITICAL: Keep the structural model linear static and Euler-Bernoulli based for v1. Do not add Timoshenko shear deformation, geometric nonlinearity, material nonlinearity, dynamics, contact, or shell/solid elements unless explicitly requested.
|
||||
- CRITICAL: Use `BeamExamples/CantileverBeam.txt` and `BeamExamples/CantileverBeam_Displacements.txt` as the canonical solver verification fixture. The solver must reproduce the listed `Ux`, `Uy`, and `Rz` values within documented floating-point tolerance before it is used to generate surrogate training data.
|
||||
- CRITICAL: All random sampling, train/test splits, and model training examples must use fixed seeds for reproducibility.
|
||||
- CRITICAL: Keep the project document and Python notebook based. Do not add browser app or design-system work.
|
||||
- Keep public helper interfaces small and stable:
|
||||
- `run_beam2d_case(params)`
|
||||
- `generate_lhs_samples(bounds, n, seed)`
|
||||
- `build_dataset(samples)`
|
||||
- `make_model(model_name, random_state)`
|
||||
- `evaluate_model(...)`
|
||||
|
||||
## Expected Repository Shape
|
||||
```text
|
||||
docs/
|
||||
PRD.md
|
||||
ARCHITECTURE.md
|
||||
ADR.md
|
||||
theory/
|
||||
00_surrogate_modeling_for_fem.md
|
||||
01_doe_sampling_validation.md
|
||||
02_response_surface_methodology.md
|
||||
03_gaussian_process_kriging.md
|
||||
04_random_forest.md
|
||||
05_gradient_boosting.md
|
||||
06_mlp_neural_network.md
|
||||
|
||||
PROGRESS.md
|
||||
WORKNOTES.md
|
||||
|
||||
BeamExamples/
|
||||
CantileverBeam.txt
|
||||
CantileverBeam_Displacements.txt
|
||||
|
||||
notebooks/
|
||||
00_beam2d_fea_dataset.ipynb
|
||||
01_response_surface_surrogate.ipynb
|
||||
02_gaussian_process_kriging_surrogate.ipynb
|
||||
03_random_forest_surrogate.ipynb
|
||||
04_gradient_boosting_surrogate.ipynb
|
||||
05_mlp_surrogate.ipynb
|
||||
06_compare_surrogate_models.ipynb
|
||||
|
||||
src/femsurrogate/
|
||||
fea/
|
||||
io.py
|
||||
data/
|
||||
surrogates/
|
||||
plotting/
|
||||
|
||||
tests/
|
||||
```
|
||||
|
||||
## Development Process
|
||||
- Start each session by reading `PROGRESS.md` and `WORKNOTES.md` after `AGENTS.md`.
|
||||
- Before making changes, update `PROGRESS.md` with the current task, intended scope, and success criteria when they are not already recorded.
|
||||
- During longer tasks, update `PROGRESS.md` after each meaningful milestone instead of waiting until the end.
|
||||
- When you encounter an implementation mistake, incorrect assumption, failed approach, non-obvious parser/detail issue, or verification problem, add a concise note to `WORKNOTES.md` with the date and lesson learned.
|
||||
- At the end of a task, update `PROGRESS.md` with completed work, verification commands/results, remaining risks, and recommended next step.
|
||||
- Use TDD for code changes: write a failing pytest first, implement the smallest passing change, then refactor only if needed.
|
||||
- For documentation changes, verify that links, filenames, and referenced commands match the repository.
|
||||
- Prefer simple educational implementations over production abstractions.
|
||||
- Keep every change traceable to the tutorial goal. Do not add speculative tooling, dashboards, APIs, or deployment layers.
|
||||
- Commit messages should follow Conventional Commits, for example `docs: define tutorial architecture` or `feat: add beam2d solver`.
|
||||
|
||||
## Commands
|
||||
Use these commands once the Python project files exist:
|
||||
|
||||
```powershell
|
||||
uv sync
|
||||
uv run pytest
|
||||
uv run ruff check .
|
||||
uv run jupyter nbconvert --to notebook --execute notebooks/00_beam2d_fea_dataset.ipynb
|
||||
```
|
||||
|
||||
For full notebook verification:
|
||||
|
||||
```powershell
|
||||
uv run jupyter nbconvert --to notebook --execute notebooks/00_beam2d_fea_dataset.ipynb
|
||||
uv run jupyter nbconvert --to notebook --execute notebooks/01_response_surface_surrogate.ipynb
|
||||
uv run jupyter nbconvert --to notebook --execute notebooks/02_gaussian_process_kriging_surrogate.ipynb
|
||||
uv run jupyter nbconvert --to notebook --execute notebooks/03_random_forest_surrogate.ipynb
|
||||
uv run jupyter nbconvert --to notebook --execute notebooks/04_gradient_boosting_surrogate.ipynb
|
||||
uv run jupyter nbconvert --to notebook --execute notebooks/05_mlp_surrogate.ipynb
|
||||
uv run jupyter nbconvert --to notebook --execute notebooks/06_compare_surrogate_models.ipynb
|
||||
```
|
||||
|
||||
## Documentation Standards
|
||||
- Write tutorial content in Korean.
|
||||
- Keep technical terms such as surrogate model, response surface, Gaussian process, Kriging, Random Forest, Gradient Boosting, MLP, DOE, LHS, RMSE, MAE, and R2 in English when that is clearer.
|
||||
- Theory documents must include citations and links to primary or official sources where possible.
|
||||
- Avoid long quotations. Summarize sources in your own words.
|
||||
- When adding equations, define symbols and units near the equation.
|
||||
- Each model-specific theory document should explain:
|
||||
- Core idea and assumptions.
|
||||
- Mathematical form.
|
||||
- FEM structural-analysis use case.
|
||||
- Strengths, weaknesses, and failure modes.
|
||||
- Hyperparameters used in the matching notebook.
|
||||
- References.
|
||||
|
||||
## Progress And Work Notes
|
||||
`PROGRESS.md` and `WORKNOTES.md` are mandatory handoff files for future agents.
|
||||
|
||||
`PROGRESS.md` should answer:
|
||||
- What is the current project/task state?
|
||||
- What has been completed?
|
||||
- What is in progress?
|
||||
- What is blocked or intentionally deferred?
|
||||
- What verification has been run, with command names and results?
|
||||
- What should the next agent do first?
|
||||
|
||||
`WORKNOTES.md` should answer:
|
||||
- What assumptions turned out to be wrong?
|
||||
- What approaches were tried and rejected?
|
||||
- What parsing, numerical, notebook, or tooling pitfalls were discovered?
|
||||
- What should future agents avoid repeating?
|
||||
|
||||
Keep both files concise and factual. Do not use them as a full chat transcript.
|
||||
|
||||
## Data And Result Conventions
|
||||
- Treat files under `BeamExamples/` as checked-in verification fixtures, not generated data.
|
||||
- `BeamExamples/CantileverBeam.txt` defines a six-node, five-element cantilever beam with fixed node 1 and a downward nodal load at node 6.
|
||||
- `BeamExamples/CantileverBeam_Displacements.txt` defines expected nodal `Ux`, `Uy`, and `Rz` values for that example.
|
||||
- The BeamExamples fixture uses clockwise-positive `Rz`; keep element stiffness, solver output, and regression comparisons in that convention.
|
||||
- For the 2D in-plane beam/frame solver, use `Area`, `ElasticModulus`, and `Izz` from the example input. Preserve `J`, `Iyy`, and `Poisson'sRatio` when parsing metadata, but do not use them in the v1 Euler-Bernoulli in-plane solve.
|
||||
- Store generated reference data under `data/reference/`.
|
||||
- Store derived or regenerated data under `data/processed/`.
|
||||
- Store metrics under `reports/results/`.
|
||||
- Store predictions under `reports/predictions/`.
|
||||
- Store generated figures under `reports/figures/`.
|
||||
- Use SI units in data columns and encode units in column names where practical, for example `tip_uy_m`, `max_abs_bending_stress_pa`, `mass_kg`, `compliance_j`.
|
||||
- Include metadata JSON for generated datasets with seed, sample count, bounds, and code version notes.
|
||||
|
||||
## Review Checklist
|
||||
- Does the change keep notebooks thin and reusable code in `src/femsurrogate/`?
|
||||
- Does the FEM implementation remain a linear static 2D Euler-Bernoulli beam/frame model?
|
||||
- Does the solver pass the `BeamExamples/CantileverBeam.txt` displacement regression check?
|
||||
- Are random seeds fixed?
|
||||
- Are tests added or updated for new code behavior?
|
||||
- Do docs and notebooks refer to the same paths and artifact names?
|
||||
- Are citations present for theory claims?
|
||||
- Has browser-app and design-system work been avoided?
|
||||
@@ -0,0 +1,29 @@
|
||||
Area, 1.0
|
||||
J, 0.140625
|
||||
Iyy, 0.0833333
|
||||
Izz, 0.0833333
|
||||
|
||||
ElasticModulus, 2.05e+011
|
||||
Poisson'sRatio, 0.3
|
||||
|
||||
# Node, NodeID, X, Y
|
||||
Node, 1, 0.0, 0.0,
|
||||
Node, 2, 1.0, 0.0,
|
||||
Node, 3, 2.0, 0.0,
|
||||
Node, 4, 3.0, 0.0,
|
||||
Node, 5, 4.0, 0.0,
|
||||
Node, 6, 5.0, 0.0,
|
||||
|
||||
# Beam, BeamID, NodeID1, NodeID2
|
||||
Beam, 1, 1, 2,
|
||||
Beam, 2, 2, 3,
|
||||
Beam, 3, 3, 4,
|
||||
Beam, 4, 4, 5,
|
||||
Beam, 5, 5, 6,
|
||||
|
||||
# Fix, NodeID
|
||||
Fix, 1,
|
||||
|
||||
# NodalLoad, NodeID, Fx, Fy, Mz
|
||||
NodeLoad, 6, 0.0, -100000.0, 0.0,
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
# NodeID, Ux, Uy, Rz
|
||||
1 0.000000, 0.000000, 0.000000
|
||||
2 0.000000, -0.000014, 0.000026
|
||||
3 0.000000, -0.000051, 0.000047
|
||||
4 0.000000, -0.000105, 0.000061
|
||||
5 0.000000, -0.000172, 0.000070
|
||||
6 0.000000, -0.000244, 0.000073
|
||||
@@ -0,0 +1,91 @@
|
||||
# Progress
|
||||
|
||||
## Current State
|
||||
- Project direction is defined as a Korean FEM surrogate tutorial using NumPy/SciPy 2D Euler-Bernoulli beam/frame analysis and scikit-learn surrogate models.
|
||||
- Documentation files have been populated for PRD, architecture, ADR, and model-specific theory.
|
||||
- `BeamExamples/CantileverBeam.txt` and `BeamExamples/CantileverBeam_Displacements.txt` are designated as canonical solver verification fixtures.
|
||||
- Implementation work is active on branch `codex/harness-implementation`.
|
||||
|
||||
## Completed
|
||||
- Replaced template project instructions in `AGENTS.md`.
|
||||
- Removed UI-oriented documentation from the planned scope.
|
||||
- Added theory document structure under `docs/theory/`.
|
||||
- Updated PRD and architecture to require BeamExamples-based solver regression verification.
|
||||
- Added `PROGRESS.md` and `WORKNOTES.md` as mandatory handoff files and documented their workflow in `AGENTS.md`.
|
||||
- Created Harness phase files under `phases/`.
|
||||
- Completed phase `0-project-foundation` step 0: `pyproject.toml`, package skeleton, artifact directories, and lint configuration.
|
||||
- Completed phase `0-project-foundation` step 1 with TDD: package import smoke tests and `femsurrogate.__version__`.
|
||||
- Completed phase `1-beam2d-solver` step 0 with TDD: BeamExamples input and displacement parser.
|
||||
- Completed phase `1-beam2d-solver` step 1 with TDD: frame local stiffness and transformation matrix.
|
||||
- Completed phase `1-beam2d-solver` step 2 with TDD: sparse global assembly, constrained DOF handling, load vector assembly, and linear static solve.
|
||||
- Completed phase `1-beam2d-solver` step 3 with TDD: BeamExamples displacement regression and analytical cantilever tip check.
|
||||
- Completed phase `2-dataset-and-surrogates` step 0 with TDD: LHS sampling, dataset schema, Beam2D case runner, and dataset builder.
|
||||
- Completed phase `2-dataset-and-surrogates` step 1 with TDD: common split/evaluate utilities, metrics, timings, and prediction table.
|
||||
- Completed phase `2-dataset-and-surrogates` step 2 with TDD: scikit-learn model builders and registry for RSM, GPR, Random Forest, Gradient Boosting, and MLP.
|
||||
- Completed phase `2-dataset-and-surrogates` step 3 with TDD: plotting diagnostics and metric comparison helpers.
|
||||
- Completed phase `3-notebooks-and-final-verification` step 0: dataset notebook, reference CSV, and metadata JSON.
|
||||
- Completed phase `3-notebooks-and-final-verification` step 1: five surrogate model notebooks with metrics, predictions, and diagnostic figures.
|
||||
- Completed phase `3-notebooks-and-final-verification` step 2: comparison notebook and model comparison outputs.
|
||||
- Completed phase `3-notebooks-and-final-verification` step 3: full pytest, ruff, and all notebook execution checks.
|
||||
|
||||
## In Progress
|
||||
- None.
|
||||
|
||||
## Blockers
|
||||
- None recorded.
|
||||
|
||||
## Verification
|
||||
- Documentation checks were run manually with PowerShell/`rg` to confirm required BeamExamples references and absence of stale template terms.
|
||||
- Verified that `AGENTS.md` references `PROGRESS.md` and `WORKNOTES.md`, both files exist, and stale template terms are absent.
|
||||
- `uv sync` passed.
|
||||
- `uv run python -c "import femsurrogate; print(femsurrogate.__name__)"` printed `femsurrogate`.
|
||||
- `uv run ruff check .` passed after excluding existing local harness infrastructure from ruff scope.
|
||||
- RED: `uv run pytest tests/test_project_structure.py -q` failed because `femsurrogate.__version__` was missing.
|
||||
- GREEN: `uv run pytest tests/test_project_structure.py -q` passed with 2 tests.
|
||||
- `uv run ruff check .` passed.
|
||||
- RED: `uv run pytest tests/test_beamexamples_io.py -q` failed because `femsurrogate.fea.io` did not exist.
|
||||
- GREEN: `uv run pytest tests/test_beamexamples_io.py tests/test_project_structure.py -q` passed with 4 tests.
|
||||
- `uv run ruff check .` passed.
|
||||
- RED: `uv run pytest tests/test_frame_element.py -q` failed because `femsurrogate.fea.element` did not exist.
|
||||
- GREEN: `uv run pytest tests/test_frame_element.py tests/test_beamexamples_io.py tests/test_project_structure.py -q` passed with 8 tests.
|
||||
- `uv run ruff check .` passed.
|
||||
- RED: `uv run pytest tests/test_beam_solver.py -q` failed because `femsurrogate.fea.assembly` did not exist.
|
||||
- GREEN: `uv run pytest tests/test_beam_solver.py tests/test_frame_element.py tests/test_beamexamples_io.py -q` passed with 9 tests.
|
||||
- `uv run ruff check .` passed after solver import formatting was corrected.
|
||||
- RED: `uv run pytest tests/test_cantilever_fixture_regression.py -q` failed because `femsurrogate.fea.benchmark` did not exist.
|
||||
- Regression discovery: fixture comparison then exposed an `Rz` sign convention mismatch; the element convention was aligned to BeamExamples clockwise-positive `Rz`.
|
||||
- GREEN: `uv run pytest tests/test_cantilever_fixture_regression.py tests/test_beam_solver.py tests/test_frame_element.py tests/test_beamexamples_io.py -q` passed with 11 tests.
|
||||
- `uv run ruff check .` passed.
|
||||
- GREEN: `uv run pytest -q` passed with 13 tests after completing phase 1.
|
||||
- RED: `uv run pytest tests/test_sampling.py tests/test_dataset_builder.py -q` failed because `femsurrogate.data.bounds` did not exist.
|
||||
- GREEN: `uv run pytest tests/test_sampling.py tests/test_dataset_builder.py -q` passed with 4 tests.
|
||||
- `uv run ruff check .` passed.
|
||||
- GREEN: `uv run pytest -q` passed with 17 tests after completing dataset builder work.
|
||||
- RED: `uv run pytest tests/test_surrogate_common.py -q` failed because `femsurrogate.surrogates.common` did not exist.
|
||||
- GREEN: `uv run pytest tests/test_surrogate_common.py -q` passed with 2 tests.
|
||||
- `uv run ruff check .` passed.
|
||||
- GREEN: `uv run pytest -q` passed with 19 tests after completing surrogate common utilities.
|
||||
- RED: `uv run pytest tests/test_surrogate_models.py -q` failed because `femsurrogate.surrogates.registry` did not exist.
|
||||
- GREEN: `uv run pytest tests/test_surrogate_models.py -q` passed with 2 tests.
|
||||
- `uv run ruff check .` passed.
|
||||
- GREEN: `uv run pytest -q` passed with 21 tests after completing surrogate model builders.
|
||||
- RED: `uv run pytest tests/test_plotting_and_results.py -q` failed because `femsurrogate.plotting.comparison` did not exist.
|
||||
- GREEN: `uv run pytest tests/test_plotting_and_results.py -q` passed with 2 tests.
|
||||
- `uv run ruff check .` passed.
|
||||
- Initial `uv run jupyter nbconvert --to notebook --execute notebooks/00_beam2d_fea_dataset.ipynb` failed because nbconvert executed with `notebooks/` as the working directory; notebooks now locate the repository root by checking for `pyproject.toml`.
|
||||
- `uv run jupyter nbconvert --to notebook --execute notebooks/00_beam2d_fea_dataset.ipynb` passed.
|
||||
- `uv run jupyter nbconvert --to notebook --execute` passed for notebooks 01 through 05.
|
||||
- `uv run jupyter nbconvert --to notebook --execute notebooks/06_compare_surrogate_models.ipynb` exited 0 and wrote comparison output; Windows/Jupyter emitted a transient zmq connection-reset message after execution.
|
||||
- `uv run ruff check .` passed after notebook cell style fixes.
|
||||
- FINAL: `uv run pytest` passed with 23 tests.
|
||||
- FINAL: `uv run ruff check .` passed.
|
||||
- FINAL: `uv run jupyter nbconvert --to notebook --execute notebooks/00_beam2d_fea_dataset.ipynb` passed.
|
||||
- FINAL: `uv run jupyter nbconvert --to notebook --execute notebooks/01_response_surface_surrogate.ipynb` passed.
|
||||
- FINAL: `uv run jupyter nbconvert --to notebook --execute notebooks/02_gaussian_process_kriging_surrogate.ipynb` passed.
|
||||
- FINAL: `uv run jupyter nbconvert --to notebook --execute notebooks/03_random_forest_surrogate.ipynb` passed.
|
||||
- FINAL: `uv run jupyter nbconvert --to notebook --execute notebooks/04_gradient_boosting_surrogate.ipynb` passed.
|
||||
- FINAL: `uv run jupyter nbconvert --to notebook --execute notebooks/05_mlp_surrogate.ipynb` passed.
|
||||
- FINAL: `uv run jupyter nbconvert --to notebook --execute notebooks/06_compare_surrogate_models.ipynb` passed and `reports/results/model_comparison.csv` exists.
|
||||
|
||||
## Next Actions
|
||||
- Review generated tutorial outputs and continue with documentation polish or additional explanatory notebook text if desired.
|
||||
@@ -0,0 +1,16 @@
|
||||
# Work Notes
|
||||
|
||||
## 2026-05-21
|
||||
- Initial repository documents were generic templates for a web project. They were replaced with FEM surrogate tutorial-specific instructions before implementation work.
|
||||
- The previous design-guide document was removed from the active scope because the project is notebook based.
|
||||
- BeamExamples fixture files use a simple comma/space text format rather than JSON/CSV. Future parser work should handle comments, trailing commas, and labels such as `Poisson'sRatio`.
|
||||
- For the v1 in-plane Euler-Bernoulli frame solver, use `Area`, `ElasticModulus`, and `Izz` from `BeamExamples/CantileverBeam.txt`; preserve `J`, `Iyy`, and `Poisson'sRatio` as metadata but do not use them in the in-plane solve.
|
||||
- Avoid writing removed/stale file names into handoff notes when the project uses simple `rg` checks for stale terms; describe the lesson without preserving obsolete filenames unless the exact name matters.
|
||||
- `scripts/execute.py` expects `git rev-parse --abbrev-ref HEAD` to succeed. On an unborn repository with no initial commit, it returns `HEAD` plus a fatal stderr and nonzero exit code, so create an initial commit before running Harness execution.
|
||||
- `ruff check .` should exclude existing local agent/harness infrastructure (`.agents`, `.codex`, `scripts/execute.py`) so linting focuses on tutorial package code and future project scripts.
|
||||
- Use `Izz` as the code parameter name for the in-plane bending inertia instead of bare `I`; ruff flags `I` as an ambiguous variable name and the fixture also uses `Izz`.
|
||||
- The BeamExamples displacement fixture reports downward cantilever rotations as positive `Rz`, so this project treats `Rz` as clockwise-positive. A textbook counterclockwise-positive Euler-Bernoulli matrix reproduces `Uy` but flips every `Rz`; keep the element stiffness convention aligned instead of flipping only solver output.
|
||||
- `jupyter nbconvert --execute notebooks/*.ipynb` can run cells with `Path.cwd()` equal to `notebooks/` on Windows. Notebook code should discover the repository root by walking/checking for `pyproject.toml`, not by assuming the shell working directory.
|
||||
- `ruff check .` lints notebook code cells too. Generated notebooks must keep imports sorted and line lengths under the configured limit.
|
||||
- nbconvert writes `*.nbconvert.ipynb` output files by default; keep them ignored because they are verification artifacts, not source notebooks.
|
||||
- On Windows, nbconvert may emit a zmq Proactor event loop warning, and comparison execution once printed a libzmq connection-reset assertion after finishing with exit code 0. Treat this as an environment warning only when the command exit code is 0 and expected output files exist.
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,301 @@
|
||||
L_m,b_m,h_m,E_pa,P_n,A_m2,I_m4,tip_uy_m,max_abs_bending_stress_pa,mass_kg,compliance_j
|
||||
1.8314998429486489,0.02288848310526338,0.0815229398597667,172594545239.26154,1204.4650264605164,0.0018659364316716727,1.0334161957807233e-06,-0.013829015619417438,87011299.20020096,26.827078910236825,16.656565663964518
|
||||
1.485780501096186,0.0703451958583708,0.15227975734470864,134603011411.74176,430.61187485571196,0.01071214935567871,2.070044708657494e-05,-0.00016896401045085464,2353277.332637697,124.93983570435536,0.07275790932338262
|
||||
1.912481924636748,0.047325586440789394,0.13412538162234836,137384268072.70396,1491.016058175157,0.006347562341872312,9.51585181099103e-06,-0.0026593065863062455,20096154.678896487,95.29584621803525,3.965068823793571
|
||||
1.9005563087063533,0.03892541267357975,0.07695136609138858,204751900490.29495,1095.028618079069,0.002995363680903012,1.4780903506148138e-06,-0.008279755549199318,54174082.80724987,44.68893012378931,9.066569277072231
|
||||
1.5947212929865011,0.028377670567845643,0.060651928533480566,143550315733.9896,285.4873970880711,0.0017211604472276289,5.27629829547462e-07,-0.005095497266108835,26167185.46189781,21.54645402864454,1.4547002513707934
|
||||
1.07237473124558,0.060835540739851784,0.1272283305923341,194807902395.3725,841.5626396886561,0.0077400042890132725,1.0440651813465087e-05,-0.00017008684513586912,5498690.043104498,65.15645240127142,0.14313873436885768
|
||||
1.719404317170917,0.05385550434598768,0.12141457596271288,180228444125.88403,780.2860073349921,0.006538843223426135,8.03269604288166e-06,-0.0009132337131101088,10139378.371577015,88.25688485115214,0.7125834877663964
|
||||
1.8443936254162967,0.06781937032695569,0.0906637700929121,206850241685.69434,664.8332582220359,0.0061487597991691754,4.2118590646042665e-06,-0.0015959616691765433,13197640.606252536,89.02475701575838,1.0610483965161202
|
||||
1.1133087091552474,0.07276333760631655,0.08972554699620917,109961391102.16035,527.9000060948273,0.00652873026799659,4.380056467488244e-06,-0.0005041471272986309,6019683.850035296,57.057664296625695,0.2661392715736369
|
||||
1.7653262594338854,0.05980665036951069,0.10875237157317413,126953548532.28677,896.458597440227,0.006504115063531939,6.410389856402397e-06,-0.0020200152084294265,13423924.977437384,90.13279816085453,1.8108600005565714
|
||||
2.4184005785926237,0.046583251237609244,0.1568083429212308,150170594507.19855,1012.1064382265317,0.007304642434452879,1.4967733662081683e-05,-0.00212299367486471,12821461.97409473,138.67457919566064,2.148695566644777
|
||||
1.0122542173163664,0.0257631638203873,0.05996802707039356,122101793297.94275,196.1163789614812,0.0015449661053999694,4.629959923088148e-07,-0.0011993946892279361,12856299.886894634,12.276602878045393,0.23522094339701388
|
||||
2.99885756490169,0.034208042406339394,0.06827291867439919,212556641451.73248,222.15212458178422,0.0023354828972184083,9.071777376802585e-07,-0.010356897291806043,25068733.85015985,54.979677349072226,2.30080673745004
|
||||
2.5090373124694736,0.0644222822855815,0.06190015136572917,213676707662.4631,1509.0204900058407,0.003987749024803228,1.2732978139795645e-06,-0.029201695277680767,92030965.56296098,78.54247710356118,44.06595651692707
|
||||
1.8803275265391401,0.03546362707534823,0.141638741145562,188165807683.9365,370.8129344289211,0.005023023495407992,8.39746263160384e-06,-0.0005200502893437317,5880203.563159812,74.14269535721769,0.19284137384215863
|
||||
2.2767602373665152,0.05078719396396178,0.1319282799922206,113377538465.96469,502.63221905928657,0.006700267145296765,9.718218816358546e-06,-0.001794598357182278,7767635.770409364,119.75097925673768,0.9020229545906786
|
||||
1.8387426022120184,0.03598261817712693,0.042756728124461955,139276750527.0553,693.7486407804699,0.0015384990226057391,2.3438235152907022e-07,-0.04403927624425049,116351676.43422394,22.20689401616478,30.552188035404416
|
||||
1.3011131084669427,0.05481935324204158,0.08484687238436737,115209362593.83258,1734.3755729455568,0.004651250668721058,2.7903596005967974e-06,-0.003961130049810408,34308668.80130053,47.50665524433302,6.870087199651787
|
||||
2.6198549887683336,0.07887793897606994,0.13647070121249977,207909603351.69885,1561.5862492494184,0.01076452764226103,1.670677321550229e-05,-0.0026946933734432015,16709370.142657414,221.38178634570068,4.2079961179124314
|
||||
1.9163836401280836,0.0692936197063689,0.09353918410176623,173905530654.12637,1950.663751507745,0.006481668650791817,4.725989305697665e-06,-0.005568040616383383,36994376.79622444,97.50770554040177,10.861374997301908
|
||||
2.460080176958872,0.03952084747455992,0.0688781284182868,107735300872.5662,974.7000408118613,0.002722122007552264,1.0761901586120548e-06,-0.041720592641427086,76733069.55438615,52.56861136183609,40.66506335029402
|
||||
2.7755844200326263,0.05125709604641546,0.05692408518502546,175283604103.8452,1997.850111329783,0.0029177633016831852,7.878815513196515e-07,-0.10311046485275611,200319153.39364558,63.57321213773421,205.99925368534448
|
||||
1.4102930025734708,0.022265414249600112,0.08705755680218691,186941727856.41827,117.79790590776206,0.0019383725657587836,1.2242467456018725e-06,-0.00048124824409759455,5906829.594940002,21.45933513707948,0.05669003537648415
|
||||
2.7187095888004675,0.04325268945427481,0.08898072117551989,134150937902.88864,326.1913240502886,0.0038486555004221762,2.53933287372463e-06,-0.0064139601803737635,15537513.364638543,82.13750641195135,2.092178163641946
|
||||
1.5015451743314063,0.0280474634849621,0.11212059965520282,163640968179.4723,1011.4454299974409,0.0031446984247413554,3.294341222963827e-06,-0.002117267779164323,25844473.813744865,37.06696794352397,2.1415008193165854
|
||||
1.5613872984144939,0.039382290755115124,0.11412904353427888,149887803869.72943,1234.6779455652597,0.004494663176070163,4.878746594076833e-06,-0.002142346946215878,22548738.685472872,55.09059345107331,2.6451085262418284
|
||||
2.2823683011586327,0.021672540627220464,0.05066818292659669,184397309151.04816,1023.8895143480386,0.001098108252984105,2.3492788514504826e-07,-0.09366986044671798,252005249.36306617,19.674356622635106,95.90758792183861
|
||||
2.6010016269455036,0.06835079352389664,0.1545043674209379,186248249676.83844,589.1393859193188,0.01056049611612879,2.100799452968336e-05,-0.0008831650596319473,5634882.319220892,215.62276049831084,0.520307320896964
|
||||
2.6988235184899976,0.050858874149295734,0.14880865969476118,102787761774.47253,1136.1743909684308,0.007568240895741235,1.396593804751516e-05,-0.005186037815692381,16336069.803628998,160.33897020574037,5.892243356783542
|
||||
1.601688330241449,0.03963062111434279,0.1144439338431775,113729873278.4416,1702.5081209908894,0.004535484180973879,4.950259498266031e-06,-0.004141898486083398,31521147.53931131,57.02579186458539,7.051615808876856
|
||||
1.9382531257107587,0.03620970064654632,0.15745742612342728,141534717182.76892,1897.3497040557022,0.005701486264504984,1.1779670221306714e-05,-0.0027622405372338702,24578642.735462748,86.74975005098388,5.240936265851348
|
||||
1.5591287409274512,0.049392939146356715,0.06940672689731209,100163372270.41394,1158.927649116863,0.0034282022379867358,1.376221431308448e-06,-0.01062144848682015,45563969.61218766,41.9583178158119,12.309490325046339
|
||||
1.0169440865973296,0.05593646414679583,0.07417492434149953,100476764002.75151,160.82272485226002,0.004149082996019581,1.902326686036506e-06,-0.0002949623926723835,3188500.632999652,33.122175528188606,0.04743665571851501
|
||||
1.2608420992915117,0.021894964398804215,0.040383667280905984,106701680904.53453,680.973354795578,0.0008841989574085911,1.2016562860306466e-07,-0.035484561670924356,144273416.82930407,8.751456866755749,24.16404100449994
|
||||
1.1681065315811268,0.03651047386470185,0.06101704401387274,172176203613.8894,1116.1525285212006,0.0022277611907698634,6.911776980931584e-07,-0.004982953168829409,57548956.32287725,20.427759822268623,5.5617357788916735
|
||||
2.613091893660169,0.0428529520046492,0.08746146285403349,188757426541.5191,1856.724475458743,0.0037479818699403056,2.3891846138442986e-06,-0.02448705173017026,88805359.56713,76.8814951791214,45.46570827923148
|
||||
1.957399449788186,0.02352144812631788,0.13224525759397504,144858300254.10114,1813.4632198003533,0.0031105999664482295,4.533390505297857e-06,-0.0069033416455300395,51774414.247427806,47.79619030326978,12.518956167884774
|
||||
1.0754425740145133,0.05632837492058661,0.10197971033879796,127852674408.22131,1402.1610993072868,0.0057443513582566335,4.978371458644394e-06,-0.0009133583838071284,15444776.195958031,48.49510208452314,1.28067559550053
|
||||
2.748190193991543,0.029189783685797673,0.13914471791153568,194550648608.6056,1991.810225336548,0.004061604216859064,6.553145407482807e-06,-0.010808976488024223,58114120.24030992,87.62217791307721,21.529429894268976
|
||||
2.737376665709003,0.059793119208386175,0.15846534399138273,197171418728.4136,1360.1770486512287,0.009475137203674669,1.982772363055239e-05,-0.0023788171016291743,14878578.766606314,203.60560296298277,3.23561242457504
|
||||
2.127996843275138,0.07648227551987294,0.065583255277641,210719969283.56403,1765.8392275775352,0.0050159565996347,1.7978707338303775e-06,-0.014971960976924529,68537246.1786959,83.79042750871774,26.438076006813407
|
||||
2.339542531006592,0.05110218729810469,0.11375155805330195,217519353948.91022,1324.3401998970633,0.005812953425091065,6.268019011143268e-06,-0.0041461301601332,28114318.46768957,106.75726638477374,5.490886845070045
|
||||
2.4807541533989843,0.07280280445677859,0.1434690996526244,188989500502.69434,1030.2910044573773,0.010444952807600093,1.791603828409672e-05,-0.0015484963132998005,10233637.532757176,203.40417646715596,1.5954018220281971
|
||||
1.611259544606639,0.020965647782533448,0.0793032577437884,132195515076.3166,301.11512726835883,0.0016626441698637357,8.713650254441947e-07,-0.003644945025297345,22077962.1869384,21.02976761062358,1.0975480851785815
|
||||
1.1380693108621813,0.054688929246585666,0.1302823502190609,172923054616.7614,1611.8077500652946,0.007125002233209116,1.0078013308574489e-05,-0.0004544334545482174,11856666.740908664,63.65365909430245,0.7324593639297616
|
||||
1.1970560162717296,0.07686991602122166,0.07875653775878153,161018911947.34885,236.11945674620944,0.006054008443639709,3.129212149325244e-06,-0.00026794277217479195,3556868.926398274,56.88884975564729,0.06326650180498523
|
||||
2.2366756486113366,0.032494419533240844,0.04880067870206303,133425120710.35454,1317.3621792082665,0.0015857497272517274,3.1470607277950625e-07,-0.11701734408260855,228454093.01701942,27.842441227927658,154.15422340582876
|
||||
1.5799282239183674,0.07445087941133813,0.07008585625845357,190051464709.8472,885.9142139521327,0.005217953632738504,2.135894201675166e-06,-0.0028690064822059374,22964113.163278602,64.71533889136836,2.5416936225070463
|
||||
2.875760889847622,0.027160509123923204,0.11772326594936802,198436241667.45178,1085.3627463884377,0.0031974238389158483,3.6926960907836247e-06,-0.011742144368100255,49752673.59576146,72.18095743013201,12.744486059850818
|
||||
2.43133244952272,0.07060028264520024,0.10016988958032483,196552139300.14716,467.0925737092751,0.007072022516909432,5.913393489384802e-06,-0.001925307900863989,9618729.896012884,134.97633695857036,0.8992970225973624
|
||||
2.454532752038515,0.060416097529133087,0.09619491473650338,212976693312.94727,472.14750470019595,0.005811721350527229,4.481545039166519e-06,-0.0024383895768227836,12437723.079946205,111.98072414463617,1.151279554183844
|
||||
2.2659447659220726,0.028952339083094296,0.04094356756849021,124497478994.26439,1125.413412036063,0.0011854120515145112,1.6559966563004738e-07,-0.21169898568446774,315251844.6210834,21.08571413368349,238.2488778037305
|
||||
2.8870356750940136,0.07720109864779011,0.07803697159449194,118508819724.02277,1241.3343766667094,0.006024539942241167,3.057338015981658e-06,-0.027480830284222508,45737022.472930476,136.53553465334076,34.11289933114898
|
||||
1.1291014037948819,0.03573057699007314,0.052549547438675834,183667874579.9599,1353.1837966588705,0.0018776256505511077,4.320815517243893e-07,-0.008181547419697236,92910114.81121816,16.642233599032494,11.07113739993049
|
||||
2.2414313057299413,0.06872856088386628,0.12359238421025875,146548487465.68164,812.8953468340021,0.008494326702976963,1.0812624845355039e-05,-0.0019256493507311863,10413354.435320249,149.45968087623032,1.5653513968432986
|
||||
1.4065306561811344,0.020273390641434193,0.1040998919792323,135341893943.94872,397.70883693499695,0.0021104577758260788,1.90588187213665e-06,-0.0014300899817837975,15277023.517729312,23.30212494816057,0.5687594233676251
|
||||
1.826249320085565,0.04697611579152199,0.11924746491239926,144621923926.2337,1653.5705281560415,0.005601782719570324,6.638092864183832e-06,-0.0034970690839348415,27124305.438361306,80.30747728062661,5.7826503721203
|
||||
2.2246456016622407,0.05005609518099281,0.15198244049392126,197936724240.38977,142.72425932125824,0.007607647507203299,1.4643873337184473e-05,-0.00018070772012295034,1647654.2683857938,132.85590859228773,0.025791375508181322
|
||||
1.042833035705912,0.058779886893946726,0.14493211466677136,126379582748.83847,1972.5755796896055,0.008519093307413338,1.4912188568033188e-05,-0.00039567484010969616,9996355.198026434,69.73933669156939,0.7804985270979758
|
||||
2.899760811553612,0.0318220486590499,0.13490142046062267,194336063708.38275,1659.3238440922435,0.004292839566072884,6.510231879122651e-06,-0.010659738945541567,49852093.38463715,97.71843236028197,17.68795900413583
|
||||
1.780938884510343,0.03761604152904589,0.11829859744482052,216411005745.66534,307.6507833259193,0.004449924954312251,5.189561130770031e-06,-0.0005157910996826229,6244907.491086512,62.21159841665765,0.15868353584989625
|
||||
2.4755986030522696,0.07137351869202158,0.05303918024990824,138485060383.08636,688.5633491549236,0.0037855929229763273,8.874548585343163e-07,-0.028334407686754395,50938325.87525222,73.56712713198132,19.510034653112616
|
||||
2.9282979381162413,0.07048741960062826,0.06795280784918846,205214703573.28278,479.0341318296734,0.004789818079906613,1.8431156484791915e-06,-0.010600587323396578,25858691.500124387,110.10421309763807,5.078043145347921
|
||||
2.102098815519008,0.026402623716520364,0.12228369270545006,151914192477.86105,253.96104698542044,0.003228610325168604,4.023198635307189e-06,-0.0012865758997511547,8113108.99264617,53.276834831428495,0.3267401625270126
|
||||
1.438691137842491,0.053003422601885564,0.06601178728785918,110575643852.93709,942.6032728618675,0.0034988506583241777,1.2705364910413264e-06,-0.006659839671566904,35229044.54530091,39.51505866290807,6.2775866711542685
|
||||
1.414403183805858,0.04882944216761645,0.14845767749078817,149309506519.0759,610.60676382933,0.007249105577375095,1.3313998479414166e-05,-0.00028971038864845423,4815030.022969181,80.48729036581547,0.1768991228603701
|
||||
2.0353686403612703,0.02332481098639935,0.15539943358306796,179856019927.0757,1945.7396345704922,0.0036246624157185796,7.29432621084417e-06,-0.004168526314270902,42185389.05134458,57.91356507087033,8.110866867426946
|
||||
2.885249561289233,0.07502641998074575,0.05528161931795067,147988357964.81046,606.4353783401605,0.004147581988164274,1.0562707308145347e-06,-0.031060656337359775,45787130.56863121,93.93945152733245,18.836280877440476
|
||||
2.351414219740206,0.02986481013474012,0.09828716313145244,214871839997.58304,505.5376364361669,0.0029353274656030563,2.3630282440532682e-06,-0.0043149050563687525,24721824.882794213,54.18204032637211,2.181346903643125
|
||||
1.4311801416075802,0.02434680593601473,0.1293718902803398,191379545728.0371,1304.2210211934942,0.0031497923062308235,4.393195391237984e-06,-0.001515782214629715,27483681.44445964,35.387173561097285,1.976915027871303
|
||||
1.8605504351019462,0.042054043274588296,0.14538206538300838,155548354819.96948,404.43763353660887,0.006113903668966059,1.0768594273778364e-05,-0.0005183584838648528,5079428.273869284,89.29552513200956,0.20964367853792554
|
||||
2.838279252077004,0.06353785559677937,0.15836925718719325,157717901217.78394,355.8809369288714,0.010062442994129098,2.1031194818127544e-05,-0.0008177186490437612,3803091.596639364,224.19618192723718,0.2910104789659047
|
||||
2.0111673108402313,0.06485092449088713,0.12682787158977285,111271833012.24095,1986.3541833732334,0.008224904723808288,1.1025011173054024e-05,-0.004390517963427739,22977911.960835844,129.85202719509425,8.721123723830017
|
||||
1.8574788390230883,0.0685463759352212,0.0947230269330848,105468411318.24774,613.2931684395832,0.0064929202138773135,4.854784488659686e-06,-0.0025587398737420674,11113417.203661704,94.67462592082764,1.5692576843799715
|
||||
2.6392631901607957,0.050586520941824586,0.07477415415586579,205756877676.9475,1792.692131769722,0.0037825643151129244,1.7624146452234193e-06,-0.030294940628359013,100369516.79162282,78.36798467615259,54.30950169689008
|
||||
2.111956910778975,0.04150066578872504,0.15400899877518737,152541561771.2149,316.0008881453181,0.006391475986625215,1.263316331472268e-05,-0.0005148988350637654,4067966.312636845,105.96339675824329,0.1627084891851395
|
||||
1.0014264214742683,0.061764448541219166,0.11708875672061811,125718315392.91345,1376.2922971645733,0.007231922489225947,8.262337020810443e-06,-0.00044355257253967935,9765894.263652556,56.85157033130386,0.6104579889738913
|
||||
1.6856494847041383,0.0663499842356854,0.1189675311235549,108626234296.58911,263.5662911187316,0.00789349381460628,9.309898043819937e-06,-0.0004160933527119218,2838642.271951645,104.44946068168507,0.10966818173343947
|
||||
1.1458396446677404,0.026891163534892886,0.08406813850038895,178256591425.2339,1547.380576946341,0.002260690060487984,1.331443190666247e-06,-0.003269483729673584,55975731.78183147,20.33454812056554,5.059135619938985
|
||||
1.6712647923371784,0.04365954073872256,0.12457052002277555,128972751590.80008,1905.650002103744,0.005438691693778223,7.03305071686388e-06,-0.003269007846047917,28205248.893309005,71.35252746187737,6.229584808698369
|
||||
2.1384126500867273,0.05709307593456131,0.055745934746160644,215490403396.62256,970.0232332545547,0.0031827068855056493,8.242174439925595e-07,-0.0178018726648786,70147961.63245386,53.42663422404477,17.268230080371417
|
||||
1.2557271906680043,0.044839093908410316,0.12255691330320925,160243710423.25885,1111.453854069051,0.0054953409447275016,6.878425296936459e-06,-0.0006655582310943875,12433847.260623487,54.17009501334135,0.739737261057237
|
||||
2.588034071579322,0.021174945313894144,0.15511863778583707,207517772888.27136,1141.6966984823966,0.0032846286722808538,6.586170934589154e-06,-0.004826709522544038,34795391.17509136,66.73073769334123,5.510638326422073
|
||||
2.807867229176492,0.0666381271839097,0.10374811733425016,113085413208.90451,1067.323026027182,0.0069135802380109485,6.201292416114119e-06,-0.011230925047518122,25069199.434559733,152.38746078475626,11.987024906801516
|
||||
1.227061247834137,0.029723238153739916,0.1029526971865092,177496282147.6928,1883.8957752126732,0.0030600875370444826,2.7028880727597e-06,-0.0024183380949151356,44025346.65861299,29.476081428746827,4.5558969200464885
|
||||
2.9710431244066564,0.060186340327272714,0.13692333426772887,166872332657.3057,729.6871195986578,0.008240914394982451,1.2875054890388292e-05,-0.0029689908975776454,11527735.869587757,192.20027960848614,2.1664344161680655
|
||||
2.210021847705903,0.051760835238663316,0.13811991091366066,176150450348.11322,1826.625937519196,0.007149201951980845,1.1365509212876897e-05,-0.003282807450351345,24529211.211943835,124.02915618418395,5.996461236693027
|
||||
2.4487914057713125,0.053673046310513686,0.14252070565784486,213317360550.77258,191.55717833418424,0.0076495204349805975,1.2948184857742409e-05,-0.00033946692094041287,2581602.071297701,147.04682721070228,0.0650273255131391
|
||||
2.045876965049992,0.04954726517142617,0.0833183854162591,117833768369.22206,1959.1727057772932,0.004128198135874477,2.3881465726731752e-06,-0.019872809059518277,69920110.49397166,66.29941596578111,38.93426509653193
|
||||
1.3947201725217278,0.07977939782851484,0.1589662855852927,142043082185.1145,332.6222866889177,0.01268223453903037,2.6706968102347878e-05,-7.929515677140202e-05,1380666.7640673867,138.852121502264,0.026375336368659956
|
||||
2.727914991379722,0.07249126009796847,0.07971050709530414,120733477883.6434,265.75679548591074,0.005778315102386654,3.0595046598209796e-06,-0.004868293923976997,9443862.782197664,123.73760628282379,1.2937821927196567
|
||||
1.5080205001189793,0.07929796780301963,0.059519692129124196,104269368673.55035,1047.7877266045866,0.004719790630100931,1.3933583994093103e-06,-0.008244307928712086,33748027.387947045,55.87269705772418,8.638284662053405
|
||||
2.7117035361833945,0.06135319406939463,0.12096633619886818,108056962302.81097,583.7158701538976,0.007421671100672796,9.050019442820886e-06,-0.00396738732995687,10578612.956230627,157.98416837946354,2.315826947543323
|
||||
1.0939706323345735,0.06893894354764854,0.1361355202715921,157283226091.914,536.4549517916284,0.009385038946833051,1.4494316622265394e-05,-0.00010269489179134353,2756021.731196738,80.59561238053969,0.055091183225171696
|
||||
1.1622913272795432,0.05786893010930312,0.0460773074504991,157178725492.1706,1425.8497974086044,0.0026664444844778047,4.717647482754067e-07,-0.010064177004606168,80932021.23996015,24.32860459700026,14.35000474310204
|
||||
2.7716878936706366,0.04312541237999208,0.12480506453875065,156706246149.03607,1682.697094517661,0.005382269875345148,6.9863227093708594e-06,-0.010908944065388938,41658520.38609861,117.10608219360417,18.356448483085646
|
||||
2.5389745767420147,0.05546185075884183,0.1291954309889122,169475506176.02783,1536.9320110250876,0.007165417712231298,9.966773228725662e-06,-0.0049641733799332816,25291558.42784295,142.81358521427373,7.629596975898065
|
||||
2.691255638827559,0.06297069741791503,0.0765125118485241,182849657583.16687,1188.249760943137,0.00481804623229805,2.3504695887419873e-06,-0.017963979523115015,52048775.4075278,101.7877636128114,21.345694373928826
|
||||
2.471636471146515,0.05778734751839677,0.08634307603146052,201290879335.22626,314.4498614731415,0.004989537340437364,3.099802786616029e-06,-0.002536446909736171,10824291.000963096,96.80873134846313,0.7975853794005168
|
||||
1.1749551739564756,0.07225278999907067,0.06712687228516774,217670736453.52356,165.16932008621438,0.004850103806514662,1.821220843370476e-06,-0.00022527311363690155,3576469.151747768,44.734438309269635,0.03720820701311154
|
||||
2.3809623473625416,0.04852277304787561,0.06134584352093114,140821987726.1037,1039.775234610213,0.0029766704425966324,9.335117613737227e-07,-0.03558652100852212,81344341.81955363,55.635620917987154,37.00198323059736
|
||||
2.0875962313292424,0.03274970697390237,0.04426037959548125,190994540317.8186,1457.9021947833785,0.0014495144623056986,2.3663096529928056e-07,-0.09782596018411872,284635101.82364684,23.754107290817885,142.6206820592181
|
||||
1.7941800678946425,0.045497545343850115,0.10729594440554388,103020088720.08171,995.1496827382779,0.0048817020958024536,4.683350275692161e-06,-0.003970879214337942,20452721.60720234,68.75542289185287,3.9516191903404247
|
||||
2.0574912934882548,0.034019059204692954,0.14686011359844534,189703355518.88113,498.1916558730003,0.004996042899313445,8.979509875556454e-06,-0.0008491053312872404,8382154.9957609605,80.69262092276516,0.4230171910045828
|
||||
2.5323886742521076,0.071607908391047,0.07490476862260115,137178636631.16434,622.871006540016,0.005363773809579794,2.507888032320121e-06,-0.009801066218384816,23555905.66826902,106.6278063660451,6.104799980610698
|
||||
2.3301750347380024,0.05527413751107069,0.09549247389991775,123981563453.13733,1556.5889191219576,0.005278264133616382,4.010958444788195e-06,-0.01320118883679208,43177099.64580963,96.54934259061136,20.548824262587036
|
||||
1.677122085801654,0.07462903940988234,0.11497922397710428,206518586292.924,1232.4381175880685,0.008580789037505004,9.453327962571316e-06,-0.0009926451687196605,12569976.11805454,112.96959184597424,1.2233737431697491
|
||||
2.0323791598945036,0.04277015701138904,0.09154170388539487,140161625063.5206,554.0170695932885,0.003915253048268421,2.7341137178513925e-06,-0.004045483107103573,18849520.242546406,62.46463780296122,2.2412666960866736
|
||||
2.8062624068569506,0.07985373881967696,0.11337690514066068,200031319360.05948,205.79991536540234,0.009053569771285607,9.698125558017938e-06,-0.0007814891818280886,3375827.68091437,199.44253610156997,0.16083040747919816
|
||||
2.2717225155456804,0.04716473377826938,0.0949863969839479,127466196746.35666,1090.6494644510033,0.0044800081263049124,3.3683746050520937e-06,-0.009926916544691682,34934256.698401086,79.89208234328272,10.826786213117787
|
||||
1.7378500598700533,0.0738777679130561,0.14607640565505262,219006087605.34467,1760.7324236745358,0.010791798794557413,1.9189901333882387e-05,-0.0007329596853289192,11646166.685165578,147.2230462226333,1.290545883204913
|
||||
1.5137154358328395,0.05407141725293492,0.10531225799488872,198347582797.3896,491.23308203901007,0.005694383043890359,5.262877731885171e-06,-0.0005440626066401296,7439735.481965973,67.66445276199077,0.26726155108200844
|
||||
1.4492631580499715,0.025535568028259188,0.10835483609802105,181044649435.6347,962.4982454017294,0.002766902288371891,2.7071303983189564e-06,-0.0019926250153884852,27916201.656973228,31.47826095542309,1.917898081055011
|
||||
1.2689404943203408,0.06687857997010405,0.058791498763259065,123206849125.83661,272.9127958972913,0.003931891951600894,1.132529158521066e-06,-0.0013321163869399325,8988770.643501224,39.16629479592707,0.3635516076203749
|
||||
1.4258128606535507,0.0758595259575483,0.06400342688102204,202271809950.72742,1362.0811610946794,0.004855269622852936,1.6574428459527883e-06,-0.003925503713462117,37497316.15409862,54.34324108110321,5.346854655913956
|
||||
1.0313069010984408,0.03073462810610016,0.143667461943138,203435921628.17838,653.2340954951508,0.004415566013769644,7.594898511996559e-06,-0.0001545830051510399,6371815.872576612,35.74735906271248,0.10097888954876179
|
||||
2.904521170293873,0.03156970614678105,0.14293141312972282,121103625103.24872,1964.0599277206998,0.004512302711649512,7.681965562248249e-06,-0.017243587282486036,53070675.69776106,102.8827182091696,33.8674387916851
|
||||
1.4573945036854046,0.06471900711662265,0.14999607121470343,145928435034.80405,104.33495908973752,0.00970759680040983,1.820079053666575e-05,-4.0533098182527294e-05,626565.6961965796,111.06021603415373,0.0042290191406542995
|
||||
2.3995696313896944,0.03826366264328562,0.08648192895824242,106970536155.77838,1569.0855127466498,0.0033091153543987816,2.0624403488417235e-06,-0.032755170141048756,78939422.57360174,62.332553782768194,51.39566293587124
|
||||
1.5831564779291536,0.06158952125377512,0.14651519020975837,134859695898.89427,1524.898180279426,0.009023800421424817,1.6142602099109667e-05,-0.0009264786171953522,10955792.648574464,112.14579152784042,1.4127855574289916
|
||||
2.763846605279553,0.07855458480595,0.14787570706278394,135672389234.38428,649.6311811736562,0.011616314771203281,2.11680471453804e-05,-0.0015918976721288796,6271433.8012254285,252.029840348054,1.0341463650526777
|
||||
1.3259034621897743,0.023928018494800956,0.1309319860687389,139710905166.88168,1446.3098788607458,0.0031329429842138056,4.475718424778278e-06,-0.0017971434688577397,28049573.410042908,32.60874260445615,2.599226352739018
|
||||
1.4630438878749488,0.0753596491752525,0.12043197556765828,203664306156.47577,1650.7582276289302,0.009075711428261307,1.0969404555335327e-05,-0.0007713230369176848,13257742.691050088,104.23358844589103,1.2732678493516012
|
||||
1.054406551847298,0.027783601365745487,0.05215405511524665,147170484989.8246,1287.711424834247,0.0014490274769291323,3.284517181024652e-07,-0.010409486893534211,107798617.8929205,11.993732914024568,13.404415199466358
|
||||
2.7894324144038096,0.07145804127893124,0.08227478525214643,148956404054.46942,849.1079141955836,0.0058791950007630826,3.3164246451966493e-06,-0.012435402609648991,29379564.91368695,128.7369942799756,10.558998772061372
|
||||
2.2539066242101127,0.0380942901797728,0.11154464927677643,115056077385.51292,826.4707556456384,0.004249214237550506,4.405800892019677e-06,-0.006222710058892296,23580732.457847882,75.1820571239686,5.142887884536431
|
||||
2.402994457545718,0.024450172665569057,0.07333284694924996,119617979556.28087,1486.8203416185795,0.0017930007699669106,8.035193158826713e-07,-0.0715488673316161,163035992.62936315,33.822281663954676,106.38031136841587
|
||||
2.742828039600342,0.023609133476818346,0.09170812089936714,159918493491.65118,395.0861585155586,0.002165149267221353,1.5174772399310487e-06,-0.011198127962772995,32745076.440472394,46.61826214243065,4.424225359377641
|
||||
1.5697906829164001,0.07549512638840895,0.07604579502760658,181453987934.30493,1258.2507780822061,0.0057410869069162,2.7667077354952207e-06,-0.0032317770495882414,27145064.58942552,70.74659217987953,4.066385987232621
|
||||
1.0361433009419025,0.06182983510372808,0.04805833918607037,111760346401.94025,706.8580808633781,0.0029714391872337647,5.719039774627004e-07,-0.0041007149991116746,30772848.329627056,24.168868942866858,2.8986235344397473
|
||||
1.1891332822946319,0.0527875598961165,0.08766354684184721,139024803572.45477,1499.9087014354996,0.004627544729620024,2.963517222398649e-06,-0.0020404944982919558,26380130.898834437,43.196724508391526,3.0605554532193686
|
||||
2.544032769922248,0.06594414947234263,0.05742754543697271,120109802061.00427,156.39458008854078,0.0037870106401254764,1.0407724496932926e-06,-0.006866482356673922,10976878.837448228,75.62909147290901,1.073880624857392
|
||||
2.4113491041746977,0.06806925253289528,0.15637898691304603,165932504055.44736,1719.6287239408757,0.010644600751022457,2.1692266004892448e-05,-0.0022328385307194484,14946457.070863962,201.4926106094112,3.8396532733471047
|
||||
1.5923443831004058,0.04005190618973452,0.05773069148673035,151035260497.287,1282.6360973889136,0.0023122242396950293,6.421880539337018e-07,-0.017797239006710864,91802603.88603026,28.90257965229384,22.82738118386537
|
||||
2.14854194002951,0.05439898896951202,0.11195997362774329,147207832368.7976,1631.9826419816247,0.006090509370402464,6.362062696420984e-06,-0.005760987394660233,30852774.00784508,102.7228613248515,9.401831428760444
|
||||
2.9217862660688345,0.032843445417127595,0.048757074670193176,195326934051.26315,761.0501315585215,0.001601350320629304,3.1723448179120516e-07,-0.10211624123203614,170879107.52690297,36.72860648574005,77.71557882390282
|
||||
2.650969177806023,0.033149857480434794,0.07285282237491812,112263045235.50276,562.2891172867971,0.002415060678775967,1.0681680006457648e-06,-0.029118974501895014,50832465.96639513,50.25767366243609,16.3732824689673
|
||||
2.645330657566194,0.05809176556486251,0.062398521644742064,192519600897.1284,1836.6219985253722,0.0036248402909803547,1.176132446739775e-06,-0.05005036379772565,128880680.4817552,75.27287403151523,91.92359918510083
|
||||
1.7116485574723677,0.04607224077507914,0.05080289837571628,133938373623.35886,414.7005979668885,0.0023406033660378772,5.034119949422421e-07,-0.01028084050401299,35816586.66371313,31.44937944448555,4.263470704616394
|
||||
2.3179594419089993,0.0698117402640165,0.12983737616156665,109203043249.62506,1619.773916426538,0.009064173181152699,1.273345944268063e-05,-0.004835812948398948,19141834.59741644,164.93152859555715,7.832923678534328
|
||||
2.2170807507995125,0.06206830874195027,0.09726977055680387,219837925483.5011,1265.2639654958202,0.0060373701501783665,4.760168652730884e-06,-0.00439217324060257,28660793.27513042,105.07489659148435,5.557258531549435
|
||||
1.5294680246631351,0.04242086202547611,0.1352559504127669,140463337062.4261,856.1338645748704,0.0057376740105846225,8.747166327492526e-06,-0.0008310195877148452,10123742.215554321,68.88837314076949,0.711464011167726
|
||||
2.2316117653477456,0.07494208675424054,0.09992226275683394,208295443337.3362,956.7772193039224,0.007488382884202668,6.230620737223157e-06,-0.0027310799449712883,17121050.84331536,131.18263228035056,2.6130350754463385
|
||||
1.2445404504507112,0.020747849981456956,0.10212715532645167,196038222403.36148,1775.016121825097,0.002118918897746172,1.8416858803310756e-06,-0.0031590156914121205,61250127.86547549,20.701080193835935,5.607303781354969
|
||||
2.595558640536703,0.07828353095980646,0.04130940703342244,116560683003.23747,124.8123838731956,0.0032338462444321723,4.5987102109595957e-07,-0.013571905660408055,14550283.171452533,65.89005486093926,1.6939418991776465
|
||||
1.316696947387901,0.04519978117597574,0.15359480367917966,136273168756.41151,1435.5960470836774,0.006942451516065875,1.364849156707118e-05,-0.0005873182212224315,10636039.812315397,71.75767204095068,0.8431517167671394
|
||||
1.626844127395485,0.05282349110856836,0.047938376313797666,167988424833.00262,793.6237756270751,0.0025322723949710952,4.849487175130852e-07,-0.013981574273287148,63814316.719322376,32.338957926586744,11.096109763976525
|
||||
1.0617097056098412,0.05157370872018972,0.12594127202734143,132787328876.609,1712.9943483542802,0.0064952584793882846,8.585218314452442e-06,-0.000599437708845018,13339773.44982657,54.13421989888761,1.0268334074419545
|
||||
1.690444257651822,0.021496426584195448,0.0818724117236866,121600668008.59973,1672.1450161760738,0.0017599642878892516,9.83100182416688e-07,-0.022522754868352028,117702206.53730504,23.35470396445711,37.66131230367025
|
||||
2.197356544114635,0.0249116415865122,0.15997594369687082,183594002331.03647,1251.4457206043853,0.003985263371840501,8.499338833351692e-06,-0.0028362775317182136,25879274.133217223,68.74279971855255,3.549447379515127
|
||||
1.4693122091591027,0.059152260883236454,0.1331021331716896,191936918720.63373,1417.963699048954,0.007873292105487064,1.1623720270335684e-05,-0.0006720203919551751,11928588.864011742,90.81134510241475,0.952900520813088
|
||||
2.8602167128648457,0.07186473302707089,0.09702997350637749,143986230123.234,698.2192085353996,0.006973033141659579,5.470818525639466e-06,-0.006913438243374578,17709827.89240488,156.56342955941122,4.827095378547361
|
||||
2.2941834394689877,0.037284593794104204,0.11294452893614798,144254417101.51257,1604.7796995369506,0.004211090882650726,4.476553355336487e-06,-0.010002434203830587,46444575.73239496,75.83896747584768,16.051703356261367
|
||||
2.0844917675866554,0.05949897487368812,0.14722733877245786,202799760420.5213,1572.01388989613,0.008759875730342438,1.5823174704596453e-05,-0.0014790219398015017,15244788.603835296,143.3401274331005,2.3250430328290785
|
||||
2.304222089828586,0.03906286972043847,0.12026734480875395,107303479831.53624,230.63138968017876,0.004697987621887408,5.662732780013312e-06,-0.0015478550699345377,5643316.537703733,84.97787382033972,0.3569839658025127
|
||||
2.288668294374408,0.07636508084636587,0.13870445205023224,115956690685.08931,1784.523748927207,0.010592176694566862,1.6981841100838293e-05,-0.0036213379155577586,16679415.125940835,190.29953490874354,6.4623635132033685
|
||||
1.619280685381442,0.06031216365010704,0.12285413804816706,208559810329.4909,1179.1318813706505,0.007409598879053893,9.319508963432962e-06,-0.000858586154413993,12584943.811277138,94.18602975751622,1.0123863075729633
|
||||
1.9764133743797743,0.04862186406220104,0.04718223796736001,209818554532.6043,914.5179335817894,0.002294088360599399,4.255846630641964e-07,-0.026355694473561814,100191893.13257454,35.592425305496334,24.102755248074736
|
||||
2.5811156078482758,0.04791637475969256,0.07180650089040813,162818322054.708,1394.271398804625,0.0034407072068469934,1.4784069635400225e-06,-0.0332009996887741,87396601.42829902,69.71477512798624,46.29120427777899
|
||||
2.4240966074345485,0.0550835490300282,0.1164652314828088,137726245762.51544,291.74655046003613,0.006415318288676882,7.251528374280895e-06,-0.0013870368085180226,5679268.500204659,122.07810769867636,0.40466320424623076
|
||||
1.8119409846568342,0.03615284614821878,0.043978129880797225,168655136048.6181,1384.97239798188,0.0015899345634668451,2.562545111832703e-07,-0.06354493181927395,215337868.01679626,22.61481064797419,88.00797660133492
|
||||
1.5203382549442104,0.0661720352183438,0.13349617672348985,153446858345.07272,1758.8207431663848,0.008833713707661018,1.3118969722462246e-05,-0.0010234420659766342,13605073.890493095,105.42732813140951,1.8000511350687638
|
||||
1.119274932276804,0.03118191689650051,0.15397494615710128,130820606053.8404,1267.9718041324911,0.004801233975213873,9.48575157053905e-06,-0.00047758510126295325,11518467.262977421,42.185121534752895,0.6055644424751853
|
||||
2.8264732580246443,0.03711519596359855,0.04504328364823122,210362584278.08685,1195.6522622459536,0.001671790299448056,2.826575829108088e-07,-0.1513523398706324,269270748.2849036,37.0933740091573,180.96476756254006
|
||||
1.9700378017525204,0.02010190573151893,0.06320283381208486,176721792183.19147,378.1281448349977,0.001270497407255387,4.229272215427543e-07,-0.012893941091676782,55661445.40198045,19.64798416667518,4.875562024607487
|
||||
2.3540189868785317,0.05728838900570228,0.10253006432107029,166687539871.52856,860.9030418047646,0.005873782209605151,5.145635904998451e-06,-0.004364361156086619,20190467.95615102,108.5419095426689,3.7572917948095292
|
||||
2.681783428693869,0.06279757642523184,0.0903787048143618,131752029800.08102,579.2534526212464,0.005675563622793355,3.863313725592832e-06,-0.007316463209146168,18170566.797023073,119.48196490602393,4.2380865748742425
|
||||
1.1228746478704057,0.03003018546318814,0.10893177537958146,164989127617.54022,1929.469855839357,0.003271241417483183,3.2347484534134203e-06,-0.0017061402656593592,36479875.439676814,28.8345733298308,3.291946212423486
|
||||
2.3452421725929256,0.06721105485541519,0.08017800091707836,171857774746.8822,798.8693930488431,0.005388848017835283,2.8868561032027542e-06,-0.006923461670399869,26017380.52327449,99.20950602000063,5.530941622429273
|
||||
2.1921406572154005,0.04161487558830651,0.11868647536109089,125111735826.98828,1697.0981515337937,0.004939122906166404,5.7979044364939595e-06,-0.008215270238589898,38078125.56060209,84.99392924869139,13.942119936261504
|
||||
1.6538092114275944,0.023076624935595744,0.15312730231001884,170280287585.6021,1465.4676397641606,0.0035336613228078885,6.90476560049627e-06,-0.0018793076552264289,26874187.02850412,45.87541291894207,2.7540645538953936
|
||||
2.572389637442625,0.06310861419853078,0.15585331463983307,117112596843.1493,989.0783852809141,0.009835686705167455,1.99092787415357e-05,-0.0024069139974230015,9958592.990530154,198.61456567641395,2.3806266100811726
|
||||
1.9501819587052847,0.06965944772581867,0.05461591488166573,102058969811.37593,756.6883347626233,0.0038045144676971555,9.45706599929415e-07,-0.019382669354814672,42611318.31850184,58.24303949080942,14.666639797349244
|
||||
2.169380236533605,0.07309913637665019,0.14042989389535024,185356904469.80307,1328.859299956813,0.010265303965214724,1.686979103822627e-05,-0.0014462597918054655,11998709.765359618,174.81437822155294,1.921875774494297
|
||||
1.9210801752994098,0.03752749610171206,0.09940157815533544,105927512798.03633,675.2751286959088,0.003730292336728378,3.0714834610985046e-06,-0.004905000029512263,20991397.246766213,56.25459665085755,3.31222452618233
|
||||
1.8672525327673226,0.06439535012713746,0.14110818184384483,161447928371.44077,871.9607816125334,0.009086710775638168,1.5077517842054512e-05,-0.0007773606289221339,7618901.494090041,133.19239212612575,0.6778279815897544
|
||||
2.5139736529173735,0.0523306246636633,0.08575743461063907,158554436079.10913,1902.691856398136,0.004487740122728002,2.7503629902673907e-06,-0.023107925436530653,74572886.04475248,88.56417437297526,43.967261546342215
|
||||
2.8287887582563256,0.0655810217625155,0.1502194765651921,185857541823.98007,132.24380229654287,0.00985154676177555,1.8525744315176733e-05,-0.00028980092717855843,1516688.6192816324,218.7633661395054,0.0383243765191561
|
||||
1.1491168951070976,0.043562541919502956,0.13726800646592155,214735079097.06833,1303.003086355074,0.005979743285878311,9.38944552879265e-06,-0.0003268691068341117,10944830.456825476,53.940678699917086,0.42591145503897393
|
||||
1.816452532974277,0.031336992307416156,0.053829698863246477,169898635912.34576,1175.1395559848966,0.001686860859188083,4.073257968145525e-07,-0.03392411627878146,141046897.41507798,24.05320604151176,39.865570941027244
|
||||
1.7201861883903216,0.05216015809158496,0.08834191964205884,124165131519.08154,1368.5047738109579,0.004607928494643884,2.9968026860878492e-06,-0.006240129876809475,34697681.819949664,62.22298538557642,8.539647525614152
|
||||
1.4466566045237335,0.027334226317785183,0.07884479362491571,159208365270.32013,739.752259166604,0.0021551614329225126,1.1164636867654345e-06,-0.004200015783817907,37787675.500095166,24.47456138790466,3.1069711646146914
|
||||
1.8527855752852664,0.03493335622852884,0.14129445290853324,116134563701.037,335.50969196930976,0.004935889456568885,8.211725114663548e-06,-0.000745870451627361,5347994.409986015,71.78938657271767,0.25024676547450586
|
||||
2.7971497624395587,0.024165381669526993,0.09574930693548259,118806388105.07796,1437.322937855852,0.0023138185466886245,1.7677438297101707e-06,-0.049925352234107614,108882075.16564503,50.805961435856894,71.75885394661577
|
||||
1.2963080909812095,0.04750360625822915,0.09387910852407244,145324504696.18393,752.151047413974,0.0044595962072011015,3.2753084462676986e-06,-0.001147406876847494,13973334.399871632,45.38093357034563,0.8630232842308393
|
||||
1.9936770076287835,0.03342559856298535,0.07239437329396463,180532790604.77344,953.707162368382,0.0024198252599429686,1.0568476488097286e-06,-0.013203519451001034,65122681.703268915,37.871147368336985,12.592291068889933
|
||||
1.3869643241945444,0.058219887610593324,0.13360766982317238,104935850950.06586,547.9444957150688,0.007778623521018357,1.157135682245887e-05,-0.0004013312796635827,4387518.563440152,84.69108552269515,0.21990726564994506
|
||||
2.5564017033384347,0.03856683579322107,0.1511509527738792,164293231268.08487,627.6901227004914,0.005829413975619112,1.1098529223897001e-05,-0.0019170181887193482,10926721.058913376,116.98324196138995,1.2032933820963216
|
||||
1.3502967241920518,0.07867742122415634,0.049844588613273774,148097496367.31027,983.0855480332484,0.003921643694071328,8.11938095415363e-07,-0.006709452211138715,40746060.66989111,41.56875367339051,6.595965503990194
|
||||
2.8669274755305993,0.04082263006107567,0.08536535809262197,174577218859.83533,1923.2652766415831,0.0034848384334463588,2.116239102299512e-06,-0.04088987199949751,111209742.10429634,78.42761556316417,78.64207098295249
|
||||
2.321092882149906,0.0624418907370763,0.11083112427626327,156235495722.25974,790.1308036373971,0.006920504952325756,7.084023859237722e-06,-0.002975744043477069,14346410.090740222,126.09560806795292,2.351227032491734
|
||||
1.6420849752535778,0.05664594997241158,0.1006076067015132,203193406480.75067,1199.838353027825,0.005699013456057977,4.807065859132985e-06,-0.0018130036740156116,20617647.849358328,73.46237530419205,2.175311342264287
|
||||
2.6727701007027393,0.06108791293093188,0.0710279279220952,103940414216.20375,454.08890509393973,0.004338947876569457,1.8241539064487798e-06,-0.015242581589106612,23628718.435804565,91.0365297010695,6.921487184602466
|
||||
1.7744739384736667,0.06564742067871134,0.1216052643240628,123074084192.20244,511.90463757274773,0.007983071943827637,9.837699424928616e-06,-0.0007874382793362332,5614195.354110834,111.2011619392673,0.40309330699452256
|
||||
2.960533583025227,0.07947322771835542,0.15159347388603736,178840793298.61615,1346.2324845207952,0.012047622670761614,2.307178104636529e-05,-0.0028220264349158876,13093611.348032258,279.9890233723858,3.7991036588601776
|
||||
1.2157155733362761,0.06378382198624324,0.06471442790537063,109114310589.13678,149.76433221439063,0.0041277335494577325,1.4405642793286307e-06,-0.0005706485592192375,4089581.3660352468,39.39247717546836,0.08546280040057326
|
||||
2.389810404232901,0.05241290466780049,0.13599722167844264,162288211966.3734,1937.3023089560381,0.007128009414917944,1.0986189627661704e-05,-0.004943475859068718,28655882.859444786,133.72153983074168,9.577007196042262
|
||||
2.0263533456094995,0.051833773855205115,0.12841653159329114,215618442447.67346,1634.7370694283527,0.006656313457876456,9.147330929693026e-06,-0.002298750802032596,23251963.77338491,105.88113790162892,3.757853149460842
|
||||
2.1801583211376006,0.0544373241338348,0.08478722391231747,182444307143.85284,1227.1349606572255,0.004615589590522854,2.765074079163017e-06,-0.008402298097986923,41017955.65846277,78.99232101396575,10.310753745903463
|
||||
2.14563415133425,0.04819637311475541,0.08252572251278449,205188393280.99457,596.2179361203249,0.003977440513790931,2.2573615199825473e-06,-0.0042383507360532644,23384006.977611676,66.9929377801286,2.5269807284037373
|
||||
1.7926478098633127,0.04021839785799433,0.0836200901763734,167578973472.24176,659.5016413182734,0.0033630660556347486,1.959636024754766e-06,-0.003856422518740346,25224123.077278662,47.32602504261536,2.543316980726008
|
||||
2.3756280674227153,0.0483290729257396,0.09665149195898387,200871859581.95782,130.53595113399155,0.004671077003267266,3.636243060911639e-06,-0.0007986789177711991,4121300.169104623,87.10942182732785,0.1042563121819305
|
||||
1.6958661415634104,0.06526374561339524,0.062458407865225524,168178476222.96375,1870.3868278064733,0.004076269642333763,1.3251452289488016e-06,-0.013644291790094198,74751591.11529726,54.265540211985474,25.520103638940196
|
||||
2.4433997055693606,0.04408042722693588,0.09428494243490745,131315942784.47629,1341.246356802283,0.004156120543597776,3.078871543997218e-06,-0.016131019880204368,50179213.1978636,79.71725014341997,21.635671645829305
|
||||
1.1028063868576052,0.06406947408643922,0.046615887590654936,190473321972.5782,1310.082184245514,0.00298665540200583,5.408437141121633e-07,-0.005685506510251672,62262992.35227599,25.855565823497166,7.448480787492602
|
||||
2.0518223806710534,0.05934782620461518,0.04929105853191075,161709968680.2527,1056.1085577582949,0.0029253171751933535,5.922812776066097e-07,-0.03174985874842365,90169252.42114542,47.11751531739171,33.53129753182728
|
||||
1.0223759978198008,0.03397969986648991,0.12554263886747186,142541733129.8655,523.4450425052518,0.004265901189163824,5.602889429367168e-06,-0.00023346817289116728,5995576.4022467835,34.23663663124551,0.12220775768264053
|
||||
1.5385843620956283,0.0569991138377577,0.14001589228789396,146082075158.87045,877.2234445545054,0.00798078178361289,1.3038236515113336e-05,-0.0005591613347830274,7247029.445275793,96.39098248908093,0.49050943216006226
|
||||
2.5662646359499606,0.028666054976278867,0.04563183452125549,129906583615.10869,175.8268955552439,0.0013080846770547698,2.2698150440767e-07,-0.033592841401824065,45355944.012306854,26.351597863296394,5.9065250165623935
|
||||
2.5475806232002713,0.026145266709118133,0.060181120434581035,141646259842.15485,456.35486046361933,0.0015734514446156804,4.7488957685110224e-07,-0.03739103507369861,73666197.535083,31.46668113301765,17.063580593648027
|
||||
1.8023601030541823,0.04529786577896972,0.04061234069047676,199529001178.22357,1584.56457706168,0.0018396523575670068,2.528544241506192e-07,-0.061296658313751695,229355403.64641982,26.028370700231466,97.12851345622427
|
||||
2.0785411776654135,0.030505920460044238,0.11257009285054553,184566302940.21112,1074.432761558658,0.0034340542986785365,3.6263687238748333e-06,-0.004805178356806669,34662425.026948035,56.03191263921675,5.1628410516856835
|
||||
1.383765571277426,0.0456377171355702,0.06848166717680429,101575719368.99918,425.77229281039746,0.0031253469555872567,1.221421724284707e-06,-0.003031015502007379,16516522.223900557,33.94926799619116,1.2905224198335397
|
||||
1.8869495499350393,0.02206603407158994,0.13149700401976216,176890774227.61542,917.97242336677,0.0029016173710120713,4.181100558450406e-06,-0.002779671352508547,27238624.543780982,42.98036389967212,2.5516616476254583
|
||||
2.9070650191280567,0.07764107771594184,0.05129676553297728,201713576323.0376,1684.3832381599352,0.0039827361593223354,8.733337723868289e-07,-0.07830121620547195,143805463.9610624,90.88787280808175,131.88925610403405
|
||||
1.7054965108590037,0.0670474566809185,0.11559254638985875,154576172017.00507,1878.5585276090378,0.007750186246711116,8.629597801067986e-06,-0.002328750055407863,21457782.839533187,103.76063747784498,4.37469327525646
|
||||
1.184295505330851,0.027483044815957383,0.045438835799037305,104640068317.19223,716.4567217412143,0.001248797560649871,2.1486475721748903e-07,-0.017643540894781685,89718510.65179947,11.609720904444318,12.640833469382338
|
||||
2.6278699871233693,0.02582792346346185,0.09088122248710997,197355116711.1208,738.0035598900417,0.0023472732586629244,1.615588397802971e-06,-0.01400134349726241,54547615.502999134,48.421382241937906,10.333041344222945
|
||||
2.7024519568205987,0.03539519932942396,0.04210185934832423,133110101253.87187,248.86983309758128,0.0014902037037733077,2.2012377114750704e-07,-0.055878789497748604,64318302.56899091,31.613550735289312,13.906545015999573
|
||||
1.3297143707186265,0.020598701447028227,0.08283703529892925,204128818615.94937,1890.6208917107308,0.0017063353588795823,9.757357983043522e-07,-0.007439122734166268,106714916.20655371,17.81116838654492,14.064560857215
|
||||
2.493970388921495,0.029519715390018526,0.12384489925720596,211627243968.80576,772.5088228459828,0.0036558661785782366,4.67267195611426e-06,-0.004039421782807421,25531573.26245612,71.57333266258458,3.1204889664149817
|
||||
1.2376104313828369,0.07799389912769801,0.15785801418451584,152877308824.47632,1841.6809879495952,0.01231196203480585,2.5566971772605712e-05,-0.00029773007909170217,7036484.147785957,119.61368926376164,0.5483238262039172
|
||||
1.4771859313584725,0.040513737564428005,0.10497583568425264,189411673010.8775,632.8111067480272,0.004252963457518328,3.905611909384729e-06,-0.0009190994374579616,12562599.497313634,49.31697962031793,0.5816163322292619
|
||||
2.819157854789256,0.05351171996944851,0.14819831316475116,155105351085.70502,184.8837000512838,0.007930346634016799,1.4514345119172876e-05,-0.0006133541746896295,2660932.375508624,175.50165718524644,0.11339918925852019
|
||||
1.3369512392761456,0.053296070850150104,0.11593498395800672,103501600362.37758,1152.4278222710052,0.006178879119036942,6.920818587591752e-06,-0.0012815473411189504,12904950.645225698,64.84775174994141,1.4768908114629091
|
||||
2.8501593284628433,0.02277771271481304,0.0532787527568028,138339274506.69586,1866.5618870210176,0.0012135681240980074,2.8707211811440863e-07,-0.36273715876780366,493678907.641683,27.152070700541252,677.071355562274
|
||||
2.0951792560264026,0.07406533974700003,0.07146009099466157,110929048849.82607,1740.7797644535306,0.005292715917871146,2.25229082633491e-06,-0.02136074396510656,57859425.41469475,87.05013050344027,37.18435084813037
|
||||
1.0836687756578331,0.04222388045854136,0.041856923487206044,193639384841.11835,726.7213463140549,0.0017673617336861002,2.5803511413136545e-07,-0.006169697030512495,63873832.58763539,15.034592599791754,4.483650532363868
|
||||
2.7546212839409696,0.04461943357315988,0.12642371509089032,192860071445.43384,767.1257728362566,0.005640954557570071,7.513260584185761e-06,-0.0036885887754046563,17778641.534867678,121.97874386530671,2.8296115150074383
|
||||
1.5467854062971749,0.049870594991660216,0.04470165073126904,218740964751.01474,640.2836832422215,0.00222929791907777,3.712222395568458e-07,-0.009726963089415511,59629625.15415055,27.068727077017517,6.228015753652102
|
||||
2.173910077761913,0.06642213976014666,0.13974731270509982,211435093105.7926,417.4666239245434,0.00928231553560306,1.5106435890405407e-05,-0.0004475959511004225,4197732.824075782,158.40451640933568,0.1868563705881884
|
||||
2.9881608632106027,0.06226482992396756,0.06380558300294158,187433213647.60504,446.12442249273533,0.003972843773877753,1.3478377128018105e-06,-0.015705888459199183,31553755.344870836,93.1912458038986,7.006780418595552
|
||||
1.928995177822485,0.021303088247859245,0.11962160800408958,130279639299.75061,485.0840592149928,0.002548309671661946,3.0387168120254663e-06,-0.0029317193820578113,18417791.69843448,38.58806498563921,1.4221303383278734
|
||||
1.6519785274161694,0.0302512897817867,0.10102083746353827,110336460642.15823,1386.2454826876135,0.00305601062810827,2.5989357504399295e-06,-0.007264696769490036,44507168.68080702,39.63044190694513,10.070653079800861
|
||||
1.7318884807611248,0.07204950073348351,0.15261615406608325,154159461726.3519,708.8711369355949,0.010995917704325698,2.134279264448713e-05,-0.00037306489081996894,4389413.364616604,149.49307017902404,0.264454933306305
|
||||
1.660836317721548,0.029259975287818117,0.06563276847501871,216087586514.02155,1599.6674590351797,0.0019204131836501354,6.893739688650444e-07,-0.01639850496561314,126471418.98158444,25.037511889433635,26.232154770318147
|
||||
1.3605777007928852,0.05032300096904793,0.10614835262365957,106246645726.0461,1625.703309806559,0.005341703651943262,5.01562503462157e-06,-0.0025612371482731057,23405764.992079355,57.05225255366169,4.1638117091471
|
||||
2.9558431518635926,0.07374489336359635,0.12823646225845173,206271115408.20898,901.4139582943543,0.009456784234574371,1.295941182037301e-05,-0.0029028424886419295,13182625.226640377,219.4292517095832,2.616662737991756
|
||||
2.249765552000813,0.03329480317374009,0.12404159838985881,148666125744.9088,1060.8610296626157,0.004129940603746465,5.295381665986321e-06,-0.005114930511678461,27953477.905458976,72.93747510162815,5.426230449271942
|
||||
1.7725347730110497,0.04188773776384526,0.08091372269812258,192280991525.7248,1538.9560461677734,0.0033892927978754527,1.8491502826542483e-06,-0.008034903989242845,59681668.88542053,47.15996882017868,12.365364074622839
|
||||
2.940011731978143,0.07566834988588603,0.059190285389112834,162741747361.5662,1126.6770492461765,0.004478831224668837,1.3076283241468332e-06,-0.04484789766891998,74969502.74749662,103.36735831669986,50.52909701051324
|
||||
2.624776066505209,0.043805932405658055,0.08922955060496712,174865528976.846,881.0331896034272,0.003908783662388435,2.5934495239728607e-06,-0.011710239589151218,39781892.249888845,80.53850217854237,10.317109736250226
|
||||
2.7240323279965777,0.05656983266985362,0.10595797521122305,174184264326.22763,211.23376340607712,0.005994024927735386,5.607956031341586e-06,-0.001457019403494844,5435939.931264098,128.17415377205305,0.3077716919558935
|
||||
1.4930080857952328,0.05746940956810506,0.08857761921629925,181986546190.57208,1726.7551338255507,0.0050905034773091554,3.328338577228582e-06,-0.0031624902764392274,34305158.0410692,59.66127839127186,5.460846320514821
|
||||
1.4978591215046884,0.030966805920008115,0.10651328394486723,101910433057.51341,381.2722785189818,0.0032983761918234196,3.1183617195054307e-06,-0.0013439457646466806,9753342.762155218,38.78294749085136,0.5124092638927752
|
||||
2.4904040188857453,0.04111838637386729,0.11614233792704334,125431018377.34157,927.0671449994952,0.0047755855252484295,5.368173072711737e-06,-0.007088721321710404,24975549.821073905,93.36112846919856,6.571720637415113
|
||||
1.6202029806615617,0.07332331449794896,0.0674029628912004,218117586637.73328,1645.1576020070886,0.00494220864616507,1.8711034749810364e-06,-0.005714865914334549,48009603.80757185,62.85794225960871,9.401855103418674
|
||||
1.5406629516324877,0.04120566430531498,0.07240269404633617,119564303692.352,1748.06148661587,0.002983401105673756,1.303286368322009e-06,-0.013674673808985646,74808202.8299052,36.081862093962,23.90417062752255
|
||||
1.960630680308092,0.03506576023661653,0.08072497564950115,118234799547.0885,215.9390093583068,0.0028306826412321153,1.5371837366101303e-06,-0.002984874888866303,11116780.867309257,43.566897376029544,0.6445509265602756
|
||||
2.4397064110239333,0.02535178025825994,0.1106155953071023,160769926256.55527,1799.7399897197276,0.0028043022653622667,2.8594091243021e-06,-0.018950475123467307,84929271.86628518,53.707142589738105,34.105927903893004
|
||||
2.0647253922351423,0.046344053185831235,0.11735671690144217,163550742253.26984,815.8856469688461,0.005438785929794975,6.242184805680148e-06,-0.002344799933806798,15835541.745614268,88.15235538560438,1.9130886110064667
|
||||
1.0523732677245807,0.032248275964980415,0.05625056779683694,158803880645.26825,278.6476441083222,0.0018139838334992379,4.783061746560689e-07,-0.0014252020732370275,17243130.329282057,14.985556541504364,0.3971292000857942
|
||||
1.7401156422876118,0.04582209130992622,0.05172123384202792,173440774897.3228,1694.7117816808484,0.002369975099771449,5.283239400985305e-07,-0.03248309717520949,144348591.41050282,32.37364133211387,55.04948748831141
|
||||
1.206060508769472,0.0735780027774754,0.06659308000994577,166028251316.1757,1498.1950736613505,0.00489978582593243,1.810731492584172e-06,-0.002914193282266561,33226340.64116436,46.38908976077129,4.366030019188764
|
||||
1.7583912519298883,0.025094062051144405,0.07049933995135561,150585897268.83624,1576.7306984844304,0.0017691148113040415,7.327315204746838e-07,-0.025897249241152977,133377698.35273038,24.41974866167448,40.83298788482852
|
||||
1.9922434745333981,0.07701808187849038,0.07756755245216204,219344889732.8167,387.5516471728011,0.0059741041058747136,2.995378556826978e-06,-0.0015547348521333635,9997015.741973927,93.42967888072982,0.6025400528612463
|
||||
2.9755934765842085,0.03475764166615617,0.05659374519233602,170948948382.8667,905.0359449688766,0.001967065115940964,5.25018202556666e-07,-0.08855734201116047,145145476.28413817,45.94751109703079,80.14757771100261
|
||||
1.3094892725297502,0.07093992867841134,0.09238199660591137,195849893830.10547,1852.1065781860923,0.006553572250392591,4.660918768946262e-06,-0.0015186429556610496,24035531.69588962,67.36738558530635,2.8126886080958
|
||||
1.8981167520140687,0.038721206016067364,0.13766089031344603,121475057997.73196,1454.1325339922068,0.005330395694182196,8.417814504873018e-06,-0.003241641893466258,22568782.972723186,79.42404989162846,4.713776940841385
|
||||
1.3574827743366096,0.03447660181414077,0.1421915841094498,114059411442.89397,1911.9193096375288,0.004902282626663407,8.25971162199173e-06,-0.0016922139691061726,22339986.12159546,52.23989913190785,3.2353765635724563
|
||||
2.3614452255990823,0.056194595351063276,0.15040608205209757,170741500984.13037,1146.7202174889098,0.008452008919256444,1.593343809830657e-05,-0.0018502109703872754,12780894.536866957,156.67780545642705,2.1216743263628635
|
||||
1.2835769902090703,0.03370516955796104,0.06515841870363565,117335088497.90169,1786.1736193459988,0.0021961755505346594,7.770104836812091e-07,-0.013810627192923274,96130069.42665954,22.128839164539308,24.668177958622035
|
||||
2.844162452744898,0.07675297437317298,0.09281253990843882,128746469431.01382,1804.5579421198604,0.0071236384971014996,5.113684640101157e-06,-0.02102052230264616,46576617.9910677,159.04716335201582,37.932750468747784
|
||||
1.7485215564374665,0.028478863066171697,0.1012479627143423,153814855172.39337,1078.618158304916,0.002883426865870612,2.4632034319057656e-06,-0.005072953519384799,38760978.732904285,39.57761214637765,5.471779782245274
|
||||
1.6367506690640443,0.031673975209392455,0.10710018393981813,126460361692.25813,804.6497488754809,0.0033922885710311714,3.2425895336681313e-06,-0.002868050609130363,21749919.389263835,43.585795118104606,2.3077762023989163
|
||||
2.8593012729562552,0.039935660012452856,0.06986382492509467,187784073528.28076,1274.6650253987286,0.0027900579593781107,1.134845387465724e-06,-0.04660785489340355,112186772.41173568,62.624287757741776,59.409402541480496
|
||||
1.2894122392511047,0.07003745071138104,0.07587294613660116,182345905342.1501,1666.9298081007958,0.005313947725369472,2.549235324139676e-06,-0.0025625094730750667,31985719.588014014,53.787173501280925,4.271523424209492
|
||||
2.5007519169881003,0.02472886936083557,0.10324005577111053,198895061932.1864,1528.157161891395,0.0025530098519691707,2.2676065649571843e-06,-0.017663098512832365,86993972.04948102,50.11788760900107,26.991990493578026
|
||||
2.575713827835502,0.06074189800573748,0.13939833856790468,178616507363.1539,571.5920948948395,0.008467319663460928,1.3711340152901193e-05,-0.0013294018832493678,7483961.0802418385,171.20372988375613,0.759875607403651
|
||||
2.3127451009760245,0.03796653861409344,0.12617502498012312,193413776489.3423,1101.938323212827,0.004790428958042049,6.355357079950439e-06,-0.003696510805289008,25298134.89069752,86.97047266864062,4.073326918518267
|
||||
1.0867103616976994,0.058812886692577795,0.07546259379684746,112590649803.91116,1594.1365388368088,0.004438172978502014,2.106136119624612e-06,-0.0028757883752170105,31035205.190956067,37.8606172175473,4.584399326895575
|
||||
2.1545894431208374,0.04435398512393907,0.13450475818736907,114782616266.9025,1729.1938392131376,0.00596582204374159,8.99423736084342e-06,-0.0055843874647924854,27858101.40911099,100.9030929806182,9.656488399898238
|
||||
2.9161579355363765,0.047608153993703106,0.06288634706975409,185042648477.68683,564.93738666509,0.0029939028953983125,9.866638140493067e-07,-0.025578268905592966,52501115.328370355,68.53594544013282,14.450120390942622
|
||||
2.000970817284346,0.03692262640850094,0.10948735191872908,155690935171.92496,1166.5925641960014,0.004042560591351303,4.038342930675903e-06,-0.004955114455666087,31643952.152809307,63.49900929762199,5.780599678720175
|
||||
2.5257575434441066,0.049619483679336954,0.15926963098288882,128319429680.0518,347.56102305797657,0.007902876855169471,1.670590149549538e-05,-0.0008708080825570729,4184617.698189847,156.69189403005493,0.30265894806069116
|
||||
1.8742592357417815,0.06944088769281627,0.0986166055119026,165387780700.4831,1036.5394879150635,0.006848024627998794,5.549887332477966e-06,-0.002478369121151686,17260421.557063296,100.75454123406925,2.5689274597030747
|
||||
1.2481383635438636,0.07808945345119186,0.04746855694708957,209349288623.42667,1479.3639341378,0.003706793668115001,6.96032029338957e-07,-0.006580232727711145,62962877.45763502,36.318742355889135,9.734558975609067
|
||||
1.275326027497872,0.0321316650017885,0.1304934908462004,175661232869.35196,1412.3338300614255,0.004192973132784065,5.950021456310129e-06,-0.0009342977827756744,19751446.500165913,41.97715098538465,1.3195403659654659
|
||||
2.9333622363830667,0.07107695027394666,0.05409851593936968,158363799031.54465,1518.188155176256,0.0038451575273168895,9.377856727866733e-07,-0.08600860998612192,128452646.89814313,88.54203308606732,130.57725292410456
|
||||
1.3703702447732584,0.027947534902596813,0.07398694652962075,179263668998.52936,109.23131917603185,0.00206775277047314,9.432516503091359e-07,-0.0005541404200699495,5870601.936981746,22.243656931100304,0.060529489093001015
|
||||
1.1567270365111966,0.06386407424978399,0.10457363801910008,171509667256.70508,1405.6795964765386,0.0066784985830218415,6.086141230807372e-06,-0.0006947476967621571,13969067.724642783,60.64281901312218,0.9765926620376335
|
||||
2.950898911344147,0.0774297823386892,0.05496187934167775,211191670593.61926,178.12924338747985,0.004255686354351407,1.0713010128343616e-06,-0.006743551272841987,13483716.681648755,98.58108680611,1.20122368597602
|
||||
1.2245838548143226,0.046656130713799045,0.14458165377891735,214231300322.82773,1931.3916143840818,0.006745620537526405,1.175078924724582e-05,-0.00046963960449313805,14550403.131653015,64.84553730752617,0.9070579939007035
|
||||
2.654042778904257,0.06908777372321699,0.15664819627399507,177931027615.85968,1216.535491985191,0.010822475138327854,2.2130750799585856e-05,-0.0019252159470979626,11426992.90055299,225.47799912738665,2.342093529380555
|
||||
1.341647441716586,0.06518563156775864,0.1276817747379519,199710785525.73175,1296.2575257759631,0.008323017125985684,1.1307259608286994e-05,-0.00046208979063604963,9819090.191774791,87.65745388036989,0.5989873686962185
|
||||
2.119507188432695,0.0445034505665908,0.10768973330179513,169075513542.73682,441.7098327760789,0.004792564722525787,4.63164583863495e-06,-0.0017902118817806182,10883809.28827098,79.73932173631543,0.7907541909350664
|
||||
1.9833354161451644,0.026723614080772236,0.09760623526288784,164792672870.41846,1005.0305193964633,0.002608391363042477,2.070840412147525e-06,-0.007658821840670438,46976034.399680264,40.61052251049103,7.6973496924939875
|
||||
2.9830555649770574,0.07432930167586123,0.043372498172153065,186610332176.64044,933.5025178522313,0.003223847501073705,5.053847335008139e-07,-0.08758313029815197,119492090.63789581,75.49279239541507,81.75907265470491
|
||||
2.2059194362242263,0.06750011366567416,0.05817678090920038,209145047096.00708,1830.4582337522565,0.003926939324074048,1.1075728938136135e-06,-0.028274023080775923,106046623.13896349,68.00071747180812,51.75441834950763
|
||||
2.372138401379674,0.04908727021159006,0.13277350189106182,216988375018.40353,834.7645356514713,0.006517488764265615,9.574627017102846e-06,-0.0017877404859739238,13729779.534080055,121.36402521945894,1.492342356639358
|
||||
2.12015487262597,0.03674782348618326,0.14563549833092412,129541030934.79411,1470.121780419921,0.005351787585987137,9.45915003841848e-06,-0.003811319465878837,23994186.74255478,89.07095544236498,5.603103758926898
|
||||
2.016354373847353,0.06773755385580861,0.0776701506922517,136685689093.0688,542.9227248488031,0.005261186015505169,2.644908830182127e-06,-0.004103775526658433,16073791.377157208,83.27606115680874,2.2280329911012284
|
||||
2.6656097913052337,0.040681731393672725,0.04307193176899242,143020562662.46283,1208.9931467032854,0.0017522407588327486,2.7089515186277925e-07,-0.19701196119820402,256202601.8744024,36.66570246922919,238.18611090720225
|
||||
2.7834542390347785,0.026360463582715998,0.14929065592923285,152346166625.45764,240.32891356571506,0.003935370898862327,7.30919714808926e-06,-0.001551442980384774,6831609.964977957,85.98830976330774,0.3728566059350277
|
||||
1.2116712982771045,0.07911760326114853,0.11039028427685474,122653054422.75237,942.102232455435,0.008733814715301595,8.86919966074582e-06,-0.0005135330986130388,7103939.887531149,83.07272402775914,0.4838006786431009
|
||||
1.3743417983180422,0.07603784896307911,0.05026209108819763,130676803187.83884,1976.5708030753901,0.003821821290732896,8.045818570399575e-07,-0.016266969392967665,84849141.58795275,41.23203665261483,32.15281675666089
|
||||
2.0690065573514795,0.02248557684543056,0.14406425226894867,151244101150.64072,341.9052137577071,0.003239367815072939,5.602623980454012e-06,-0.0011912424166293971,9094992.56856866,52.61284502081509,0.4072919930949216
|
||||
2.6751069756974353,0.06320983032140148,0.09257444072869406,100880770512.19016,362.8125614439693,0.00585161469055939,4.1790413614801725e-06,-0.00549160714740934,10749986.04889828,122.88150793080075,1.992424055595592
|
||||
2.161070148505975,0.05567257776565458,0.09899735765114717,200734643086.90546,831.3373741828162,0.005511438092427811,4.5012267733521605e-06,-0.0030953475561482768,19756448.814332534,93.49824404454299,2.573278109511506
|
||||
1.9431887660878542,0.058471811981890304,0.10969641005462585,212336367484.17975,1817.640943138909,0.006414147863802424,6.431948395661203e-06,-0.0032551040880256102,30119166.95645154,97.84161557279313,5.916610464574188
|
||||
|
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"dataset_name": "beam2d_lhs_300",
|
||||
"sample_count": 300,
|
||||
"random_seed": 20260521,
|
||||
"unit_system": "SI",
|
||||
"fea_model": "2D Euler-Bernoulli beam/frame, linear static",
|
||||
"target_columns": [
|
||||
"tip_uy_m",
|
||||
"max_abs_bending_stress_pa",
|
||||
"mass_kg",
|
||||
"compliance_j"
|
||||
],
|
||||
"parameter_bounds": {
|
||||
"L_m": {
|
||||
"lower": 1.0,
|
||||
"upper": 3.0
|
||||
},
|
||||
"b_m": {
|
||||
"lower": 0.02,
|
||||
"upper": 0.08
|
||||
},
|
||||
"h_m": {
|
||||
"lower": 0.04,
|
||||
"upper": 0.16
|
||||
},
|
||||
"E_pa": {
|
||||
"lower": 100000000000.0,
|
||||
"upper": 220000000000.0
|
||||
},
|
||||
"P_n": {
|
||||
"lower": 100.0,
|
||||
"upper": 2000.0
|
||||
}
|
||||
},
|
||||
"notes": "Generated by notebooks/00_beam2d_fea_dataset.ipynb using src/femsurrogate."
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
# Architecture Decision Records
|
||||
|
||||
## 철학
|
||||
이 프로젝트는 연구용 개념 전달과 재현성을 우선한다. 구현은 작고 명시적이어야 하며, notebook 교육 흐름과 테스트 가능한 Python 모듈 사이의 경계를 분명히 둔다.
|
||||
|
||||
---
|
||||
|
||||
## ADR-001: Python tutorial project로 구성
|
||||
**결정**: Python `>=3.12,<3.15` 기반의 문서 + notebook + src package 구조를 사용한다.
|
||||
|
||||
**이유**: 대상 사용자는 FEM/CAE 연구자이며 Python scientific stack에 익숙하다. NumPy, SciPy, scikit-learn, pandas, matplotlib로 FEM 데이터 생성과 surrogate 모델링을 모두 재현할 수 있다.
|
||||
|
||||
**트레이드오프**: 브라우저 대시보드 기반 상호작용은 제공하지 않는다. 시각적 결과는 notebook과 저장된 figure로 제한한다.
|
||||
|
||||
---
|
||||
|
||||
## ADR-002: `uv`와 `pyproject.toml` 사용
|
||||
**결정**: 의존성 관리는 `uv`, 프로젝트 설정은 `pyproject.toml`, lock은 `uv.lock`으로 관리한다.
|
||||
|
||||
**이유**: 튜토리얼 재현성을 높이고, pytest/ruff/jupyter 실행 명령을 하나의 환경에서 고정하기 쉽다.
|
||||
|
||||
**트레이드오프**: conda 기반 연구실 환경보다 초기 학습 비용이 있을 수 있다. 대신 repository-local workflow가 단순해진다.
|
||||
|
||||
---
|
||||
|
||||
## ADR-003: 자체 2D Euler-Bernoulli beam/frame solver 사용
|
||||
**결정**: FEM 데이터는 NumPy/SciPy로 구현한 2-node 2D Euler-Bernoulli beam/frame element에서 생성한다.
|
||||
|
||||
**이유**: 외부 CAE solver 없이 해석 데이터 생성 과정을 완전히 설명할 수 있다. 요소 강성, 조립, 경계조건, 응답 계산이 surrogate 학습 데이터와 직접 연결된다.
|
||||
|
||||
**트레이드오프**: 산업용 고충실도 해석과 차이가 있다. v1은 선형 정적, slender beam 가정에 제한된다.
|
||||
|
||||
---
|
||||
|
||||
## ADR-004: Notebook은 orchestration layer로 제한
|
||||
**결정**: 핵심 계산과 학습 helper는 `src/femsurrogate/`에 두고 notebook은 설명, 호출, 시각화, 해석을 담당한다.
|
||||
|
||||
**이유**: notebook hidden state 문제를 줄이고, pytest로 핵심 로직을 검증할 수 있다. 모델별 notebook도 같은 helper를 사용하므로 비교 조건이 일관된다.
|
||||
|
||||
**트레이드오프**: 독자가 처음 볼 파일 수는 늘어난다. 대신 코드 재사용성과 검증 가능성이 높아진다.
|
||||
|
||||
---
|
||||
|
||||
## ADR-005: scikit-learn 중심 surrogate 모델
|
||||
**결정**: Response Surface, Gaussian Process/Kriging, Random Forest, Gradient Boosting, MLP를 모두 scikit-learn API로 구현한다.
|
||||
|
||||
**이유**: 공통 `fit/predict` 인터페이스를 사용하면 모델별 비교가 단순하다. 연구자에게 익숙하고 설치 부담이 낮다.
|
||||
|
||||
**트레이드오프**: PyTorch 기반 deep learning, GPU 학습, 복잡한 Bayesian optimization framework는 제외한다.
|
||||
|
||||
---
|
||||
|
||||
## ADR-006: 기준 데이터셋은 CSV + metadata JSON
|
||||
**결정**: 기준 FEM 데이터는 CSV로 저장하고, 생성 조건은 JSON metadata로 별도 저장한다.
|
||||
|
||||
**이유**: CSV는 notebook, pandas, spreadsheet, 다른 ML 도구에서 쉽게 읽을 수 있다. Metadata JSON은 seed, bounds, 단위, target 정보를 명확히 남긴다.
|
||||
|
||||
**트레이드오프**: 대용량/버전관리형 데이터셋에는 Parquet, DVC, database가 더 적합할 수 있다. v1에서는 단순성과 가독성을 우선한다.
|
||||
|
||||
---
|
||||
|
||||
## ADR-007: 모델별 이론 문서와 모델별 notebook 분리
|
||||
**결정**: 각 surrogate 모델은 별도 이론 문서와 별도 실습 notebook으로 구성한다. 최종 비교는 별도 notebook에서 수행한다.
|
||||
|
||||
**이유**: 모델별 가정, 장점, 실패 모드, hyperparameter가 다르므로 독립적으로 학습하는 편이 좋다. 최종 비교 notebook은 같은 입력 데이터와 metric을 기준으로 모델 선택을 돕는다.
|
||||
|
||||
**트레이드오프**: notebook 수가 늘어난다. 대신 각 notebook의 목표가 분명하고 실행 실패 범위가 작아진다.
|
||||
|
||||
---
|
||||
|
||||
## ADR-008: Notebook 중심 범위 고정
|
||||
**결정**: 브라우저 앱과 디자인 시스템 계획을 프로젝트 범위에서 제외한다.
|
||||
|
||||
**이유**: 튜토리얼은 notebook 기반이다. 브라우저 앱 설계 지침은 현재 목표에 직접 기여하지 않으며 미래 agent에게 잘못된 구현 방향을 줄 수 있다.
|
||||
|
||||
**트레이드오프**: 웹 기반 인터랙티브 학습 환경은 제공하지 않는다.
|
||||
@@ -0,0 +1,226 @@
|
||||
# 아키텍처
|
||||
|
||||
## 개요
|
||||
이 프로젝트는 문서와 Jupyter notebook이 중심인 교육용 Python 프로젝트다. 재사용 가능한 계산 로직은 `src/femsurrogate/`에 모듈화하고, notebook은 설명, 시각화, 실행 순서, 결과 해석을 담당한다.
|
||||
|
||||
## 기술 스택
|
||||
- Python `>=3.12,<3.15`
|
||||
- `uv` + `pyproject.toml` + `uv.lock`
|
||||
- NumPy, SciPy
|
||||
- pandas, matplotlib
|
||||
- scikit-learn
|
||||
- JupyterLab, ipykernel, nbconvert
|
||||
- pytest, ruff
|
||||
- joblib, CSV, JSON
|
||||
|
||||
## 디렉토리 구조
|
||||
```text
|
||||
docs/
|
||||
PRD.md
|
||||
ARCHITECTURE.md
|
||||
ADR.md
|
||||
theory/
|
||||
00_surrogate_modeling_for_fem.md
|
||||
01_doe_sampling_validation.md
|
||||
02_response_surface_methodology.md
|
||||
03_gaussian_process_kriging.md
|
||||
04_random_forest.md
|
||||
05_gradient_boosting.md
|
||||
06_mlp_neural_network.md
|
||||
|
||||
BeamExamples/
|
||||
CantileverBeam.txt
|
||||
CantileverBeam_Displacements.txt
|
||||
|
||||
notebooks/
|
||||
00_beam2d_fea_dataset.ipynb
|
||||
01_response_surface_surrogate.ipynb
|
||||
02_gaussian_process_kriging_surrogate.ipynb
|
||||
03_random_forest_surrogate.ipynb
|
||||
04_gradient_boosting_surrogate.ipynb
|
||||
05_mlp_surrogate.ipynb
|
||||
06_compare_surrogate_models.ipynb
|
||||
|
||||
src/femsurrogate/
|
||||
fea/
|
||||
element.py
|
||||
model.py
|
||||
io.py
|
||||
assembly.py
|
||||
solver.py
|
||||
responses.py
|
||||
benchmark.py
|
||||
data/
|
||||
bounds.py
|
||||
sampling.py
|
||||
dataset.py
|
||||
schema.py
|
||||
surrogates/
|
||||
common.py
|
||||
rsm.py
|
||||
gpr.py
|
||||
random_forest.py
|
||||
boosting.py
|
||||
mlp.py
|
||||
registry.py
|
||||
plotting/
|
||||
diagnostics.py
|
||||
comparison.py
|
||||
|
||||
tests/
|
||||
data/
|
||||
reference/
|
||||
processed/
|
||||
reports/
|
||||
results/
|
||||
predictions/
|
||||
figures/
|
||||
```
|
||||
|
||||
## 핵심 모듈 책임
|
||||
### `femsurrogate.fea`
|
||||
2D Euler-Bernoulli beam/frame 요소 기반 선형 정적 해석을 담당한다.
|
||||
|
||||
- `element.py`: local 6x6 stiffness matrix와 좌표 변환 행렬.
|
||||
- `model.py`: node, element, support, load, material, section, parameter dataclass.
|
||||
- `io.py`: `BeamExamples/*.txt`의 간단한 텍스트 fixture parser.
|
||||
- `assembly.py`: global sparse stiffness matrix 조립.
|
||||
- `solver.py`: 경계조건 적용과 `K u = f` 풀이.
|
||||
- `responses.py`: tip displacement, bending stress, mass, compliance 계산.
|
||||
- `benchmark.py`: cantilever analytical solution 등 검증 helper.
|
||||
|
||||
### `femsurrogate.data`
|
||||
입력 공간 정의, 샘플링, batch 해석, dataset schema를 담당한다.
|
||||
|
||||
- `bounds.py`: 설계변수 범위와 단위.
|
||||
- `sampling.py`: Latin Hypercube Sampling.
|
||||
- `dataset.py`: 샘플별 FEM 실행과 CSV/metadata 저장.
|
||||
- `schema.py`: 컬럼명, target명, 단위, split seed.
|
||||
|
||||
### `femsurrogate.surrogates`
|
||||
scikit-learn 기반 모델 생성, 학습, 평가를 담당한다.
|
||||
|
||||
- `common.py`: train/test split, scaling, metric, timing, JSON 저장.
|
||||
- `rsm.py`: `PolynomialFeatures` + `Ridge`.
|
||||
- `gpr.py`: `GaussianProcessRegressor`.
|
||||
- `random_forest.py`: `RandomForestRegressor`.
|
||||
- `boosting.py`: `GradientBoostingRegressor`.
|
||||
- `mlp.py`: `MLPRegressor`.
|
||||
- `registry.py`: notebook에서 모델명을 통해 builder를 가져오는 작은 registry.
|
||||
|
||||
### `femsurrogate.plotting`
|
||||
모델 진단과 최종 비교 그림을 담당한다.
|
||||
|
||||
- `diagnostics.py`: parity plot, residual plot, error histogram.
|
||||
- `comparison.py`: 모델별 metric table, bar plot, prediction-time comparison.
|
||||
|
||||
## 주요 인터페이스
|
||||
```python
|
||||
def run_beam2d_case(params: BeamParameters) -> AnalysisResult:
|
||||
...
|
||||
|
||||
def generate_lhs_samples(bounds: ParameterBounds, n: int, seed: int) -> pd.DataFrame:
|
||||
...
|
||||
|
||||
def build_dataset(samples: pd.DataFrame) -> pd.DataFrame:
|
||||
...
|
||||
|
||||
def make_model(model_name: str, random_state: int):
|
||||
...
|
||||
|
||||
def evaluate_model(model, X_train, X_test, y_train, y_test) -> MetricsReport:
|
||||
...
|
||||
```
|
||||
|
||||
## 데이터 흐름
|
||||
```text
|
||||
Parameter bounds
|
||||
-> Latin Hypercube samples
|
||||
-> Beam2D FEM batch analysis
|
||||
-> data/reference/beam2d_lhs_300.csv
|
||||
-> model-specific notebooks
|
||||
-> reports/results/<model>_metrics.json
|
||||
-> reports/predictions/<model>_predictions.csv
|
||||
-> notebooks/06_compare_surrogate_models.ipynb
|
||||
```
|
||||
|
||||
## FEM 해석 설계
|
||||
- 요소: 2-node 2D Euler-Bernoulli frame element.
|
||||
- 노드 DOF: `[ux, uy, rz]`.
|
||||
- Local stiffness matrix: axial `EA/L` 항과 bending `EI` 항을 포함한 6x6 matrix.
|
||||
- Global assembly: sparse matrix 기반.
|
||||
- Solver: constrained DOF 제거 후 `scipy.sparse.linalg.spsolve`.
|
||||
- 기준 검증: cantilever tip displacement `P L^3 / (3 E I)`.
|
||||
|
||||
## Solver Verification Fixture
|
||||
`BeamExamples/`의 cantilever 예제는 solver 구현의 canonical regression fixture다.
|
||||
|
||||
```text
|
||||
BeamExamples/CantileverBeam.txt
|
||||
BeamExamples/CantileverBeam_Displacements.txt
|
||||
```
|
||||
|
||||
입력 파일은 다음 항목을 포함한다.
|
||||
|
||||
- Section/material metadata: `Area`, `J`, `Iyy`, `Izz`, `ElasticModulus`, `Poisson'sRatio`.
|
||||
- Geometry: `Node, NodeID, X, Y`.
|
||||
- Connectivity: `Beam, BeamID, NodeID1, NodeID2`.
|
||||
- Boundary condition: `Fix, NodeID`.
|
||||
- Load: `NodeLoad, NodeID, Fx, Fy, Mz`.
|
||||
|
||||
2D in-plane Euler-Bernoulli frame solver는 `Area`, `Izz`, `ElasticModulus`, node coordinates, beam connectivity, fixed nodes, nodal loads를 사용한다. `J`, `Iyy`, `Poisson'sRatio`는 fixture metadata로 보존하되 v1 해석에는 사용하지 않는다.
|
||||
|
||||
기준 변위 파일은 다음 형식을 가진다.
|
||||
|
||||
```text
|
||||
# NodeID, Ux, Uy, Rz
|
||||
1 0.000000, 0.000000, 0.000000
|
||||
...
|
||||
```
|
||||
|
||||
검증 test는 solver 결과의 모든 node별 `[Ux, Uy, Rz]`를 기준 파일과 비교한다. 기준 파일 값은 소수점 6자리로 반올림되어 있으므로 기본 허용오차는 `atol=5e-7`, `rtol=1e-6`로 둔다. Tip displacement는 별도로 해석해 `P L^3 / (3 E I)`와 비교해 부호와 크기를 확인한다.
|
||||
|
||||
## Dataset Schema
|
||||
기본 dataset은 SI 단위 컬럼을 사용한다.
|
||||
|
||||
```text
|
||||
L_m
|
||||
b_m
|
||||
h_m
|
||||
E_pa
|
||||
P_n
|
||||
A_m2
|
||||
I_m4
|
||||
tip_uy_m
|
||||
max_abs_bending_stress_pa
|
||||
mass_kg
|
||||
compliance_j
|
||||
```
|
||||
|
||||
Metadata JSON에는 다음을 포함한다.
|
||||
|
||||
```text
|
||||
dataset_name
|
||||
created_by
|
||||
sample_count
|
||||
random_seed
|
||||
parameter_bounds
|
||||
target_columns
|
||||
unit_system
|
||||
fea_model
|
||||
notes
|
||||
```
|
||||
|
||||
## Notebook 책임
|
||||
- Notebook은 한 번에 위에서 아래로 실행 가능해야 한다.
|
||||
- Notebook 셀에 핵심 FEM 또는 ML helper 구현을 길게 두지 않는다.
|
||||
- 각 모델별 notebook은 같은 dataset, target, split seed를 사용한다.
|
||||
- 각 모델별 notebook은 metric JSON과 prediction CSV를 저장한다.
|
||||
- 최종 비교 notebook은 이전 notebook 결과물을 읽어 비교만 수행한다.
|
||||
|
||||
## 검증 전략
|
||||
- Unit tests: FEM element, solver, dataset generation, model factory.
|
||||
- Regression tests: `BeamExamples/CantileverBeam.txt`를 해석하고 `BeamExamples/CantileverBeam_Displacements.txt`와 비교.
|
||||
- Integration tests: 작은 sample count로 dataset 생성과 모든 surrogate smoke test.
|
||||
- Notebook tests: `nbconvert --execute`로 순차 실행.
|
||||
- Documentation checks: 문서의 경로와 notebook/file 이름이 실제 구조와 일치하는지 검토.
|
||||
@@ -0,0 +1,60 @@
|
||||
# PRD: FEM Surrogate Tutorial
|
||||
|
||||
## 목표
|
||||
FEM 기반 구조해석 데이터로 surrogate model을 구축하고 검증하는 전체 절차를 학습할 수 있는 한국어 튜토리얼을 만든다. 독자는 2D beam 요소로 해석 데이터를 직접 생성하고, 여러 surrogate 모델을 같은 데이터셋에서 학습, 평가, 비교한다.
|
||||
|
||||
## 사용자
|
||||
- FEM과 선형 정적 구조해석의 기본 개념을 아는 대학원생.
|
||||
- CAE 해석 결과를 기계학습 모델로 근사하고 싶은 연구자.
|
||||
- surrogate 기반 설계 탐색, 민감도 분석, 최적화의 입문 파이프라인을 재현하고 싶은 엔지니어.
|
||||
|
||||
## 사용자 문제
|
||||
고정밀 구조해석은 반복 설계, 불확실성 분석, 최적화에서 계산 비용이 커진다. Surrogate model은 해석 데이터에서 입력 변수와 응답의 관계를 학습하여 빠른 근사 예측을 제공하지만, 모델별 가정, 데이터 설계, 검증 방법, 외삽 위험을 함께 이해해야 실무적으로 사용할 수 있다.
|
||||
|
||||
## 학습 성과
|
||||
튜토리얼을 완료한 독자는 다음을 할 수 있어야 한다.
|
||||
|
||||
1. 2D Euler-Bernoulli beam/frame 요소를 이용해 선형 정적 FEM 해석 데이터를 생성한다.
|
||||
2. DOE와 Latin Hypercube Sampling으로 입력 공간을 정의하고 해석 케이스를 만든다.
|
||||
3. Response Surface, Gaussian Process/Kriging, Random Forest, Gradient Boosting, MLP를 같은 데이터셋에 적용한다.
|
||||
4. RMSE, MAE, R2, residual, parity plot, uncertainty 또는 feature importance로 모델을 진단한다.
|
||||
5. 최종 비교표를 통해 구조해석 surrogate 문제에 맞는 모델을 선택하는 기준을 설명한다.
|
||||
|
||||
## 핵심 산출물
|
||||
- `docs/theory/`: surrogate 모델별 이론 문서와 출처.
|
||||
- `notebooks/`: 데이터 생성, 모델별 학습, 최종 비교 notebook.
|
||||
- `src/femsurrogate/`: notebook에서 재사용하는 FEM, 데이터, 모델, plotting helper.
|
||||
- `tests/`: FEM 공식 검증, dataset schema, surrogate pipeline smoke test.
|
||||
- `BeamExamples/`: 2D beam solver 검증용 cantilever 예제와 기준 변위 파일.
|
||||
- `data/reference/`: 재현 가능한 기준 해석 데이터.
|
||||
- `reports/`: 모델별 metric, 예측값, 그림.
|
||||
|
||||
## MVP 범위
|
||||
- 선형 정적 2D Euler-Bernoulli beam/frame 요소.
|
||||
- 기준 구조: fixed-free cantilever beam 또는 간단한 planar frame.
|
||||
- Solver 검증 기준 fixture: `BeamExamples/CantileverBeam.txt`와 `BeamExamples/CantileverBeam_Displacements.txt`.
|
||||
- 입력 변수: `L`, `b`, `h`, `E`, `P`.
|
||||
- 응답 변수: `tip_uy_m`, `max_abs_bending_stress_pa`, `mass_kg`, `compliance_j`.
|
||||
- Surrogate 모델:
|
||||
- Response Surface Methodology: polynomial features + Ridge regression.
|
||||
- Gaussian Process Regression/Kriging.
|
||||
- Random Forest Regressor.
|
||||
- Gradient Boosting Regressor.
|
||||
- Multi-layer Perceptron Regressor.
|
||||
|
||||
## MVP 제외 사항
|
||||
- 상용 CAE 연동.
|
||||
- 3D beam, shell, solid element.
|
||||
- Timoshenko beam, geometric nonlinearity, material nonlinearity, contact, buckling, modal, transient analysis.
|
||||
- PyTorch/TensorFlow 기반 deep learning framework.
|
||||
- Bayesian optimization, active learning, MLflow, DVC, browser dashboard.
|
||||
- 배포용 Python package publishing.
|
||||
|
||||
## 성공 기준
|
||||
- 이론 문서가 모델별로 분리되어 있고 각 문서에 출처가 있다.
|
||||
- FEM solver가 `BeamExamples/CantileverBeam.txt`를 해석했을 때 `BeamExamples/CantileverBeam_Displacements.txt`의 모든 노드 `Ux`, `Uy`, `Rz` 기준값과 허용오차 내에서 일치한다.
|
||||
- `00_beam2d_fea_dataset.ipynb`가 데이터셋을 생성하고, 생성 데이터가 동일 seed에서 재현된다.
|
||||
- 모든 모델별 notebook이 같은 dataset과 split을 사용한다.
|
||||
- 최종 비교 notebook이 모든 모델의 metric JSON을 읽어 하나의 비교표와 그림을 만든다.
|
||||
- FEM solver는 cantilever beam 해석해와 BeamExamples regression fixture를 모두 비교하는 pytest를 통과한다.
|
||||
- 전체 notebook은 `nbconvert --execute`로 순서대로 실행 가능하다.
|
||||
@@ -0,0 +1,98 @@
|
||||
# FEM 구조해석을 위한 Surrogate Modeling 개요
|
||||
|
||||
## 목적
|
||||
Surrogate model은 계산 비용이 큰 해석 모델을 반복 호출하기 어려울 때, 입력 변수와 출력 응답 사이의 관계를 데이터 기반으로 근사하는 모델이다. FEM 구조해석에서는 설계변수 변화에 따른 변위, 응력, 질량, compliance 같은 응답을 빠르게 예측하기 위해 사용한다.
|
||||
|
||||
이 튜토리얼의 surrogate는 고충실도 산업용 solver를 대체하는 범용 도구가 아니라, 제한된 입력 범위 안에서 반복 설계 탐색과 모델 비교를 학습하기 위한 교육용 근사 모델이다.
|
||||
|
||||
## 문제 정의
|
||||
FEM 해석 모델을 다음 함수로 본다.
|
||||
|
||||
```text
|
||||
y = f(x)
|
||||
```
|
||||
|
||||
- `x`: 설계변수 벡터. 예: beam 길이 `L`, 폭 `b`, 높이 `h`, 탄성계수 `E`, 하중 `P`.
|
||||
- `f`: FEM solver. 이 튜토리얼에서는 2D Euler-Bernoulli beam/frame solver.
|
||||
- `y`: 응답. 예: tip displacement, maximum bending stress, mass, compliance.
|
||||
|
||||
Surrogate model은 해석 데이터 `{x_i, y_i}`를 이용해 `f`를 근사하는 `\hat{f}`를 만든다.
|
||||
|
||||
```text
|
||||
\hat{y} = \hat{f}(x)
|
||||
```
|
||||
|
||||
좋은 surrogate는 빠른 예측뿐 아니라, 어떤 영역에서 믿을 수 있고 어떤 영역에서 위험한지도 설명할 수 있어야 한다.
|
||||
|
||||
## 왜 FEM에서 필요한가
|
||||
구조해석에서는 다음 작업이 반복 평가를 요구한다.
|
||||
|
||||
- 설계변수 sweep.
|
||||
- 민감도 분석.
|
||||
- 최적화.
|
||||
- 불확실성 전파.
|
||||
- 신뢰성 분석.
|
||||
- inverse problem 또는 model calibration.
|
||||
|
||||
Queipo et al.은 surrogate-based analysis and optimization이 비싼 high-fidelity model의 계산 부담을 줄이고 민감도/최적화 연구를 가능하게 한다고 정리한다. Forrester와 Keane도 긴 계산 시간이 필요한 aerospace design evaluation에서 surrogate 기반 방법이 효율적 최적화를 가능하게 하는 배경을 설명한다.
|
||||
|
||||
## 전체 workflow
|
||||
```text
|
||||
1. 해석 문제 정의
|
||||
2. 입력 변수와 범위 설정
|
||||
3. DOE로 샘플 생성
|
||||
4. FEM batch 해석
|
||||
5. 데이터 정리와 train/test split
|
||||
6. surrogate 모델 학습
|
||||
7. 검증과 진단
|
||||
8. 모델 비교
|
||||
9. 설계 탐색 또는 최적화에 활용
|
||||
```
|
||||
|
||||
이 workflow에서 가장 중요한 판단은 데이터의 범위다. Surrogate는 학습한 입력 영역 안에서만 신뢰할 수 있다. 학습 범위를 벗어난 외삽은 모델별로 다르게 실패하며, 예측값이 그럴듯해 보여도 구조적으로 틀릴 수 있다.
|
||||
|
||||
## 이 튜토리얼의 FEM 모델
|
||||
기준 해석 모델은 2D Euler-Bernoulli beam/frame element이다.
|
||||
|
||||
- 노드당 자유도: `ux`, `uy`, `rz`.
|
||||
- 요소: 축방향 변형과 굽힘 변형을 포함한 2-node frame element.
|
||||
- 해석: 선형 정적 해석.
|
||||
- solver: sparse global stiffness matrix를 조립하고 `scipy.sparse.linalg.spsolve`로 선형 시스템을 푼다.
|
||||
- 검증: cantilever beam tip displacement 해석해 `P L^3 / (3 E I)`와 비교한다.
|
||||
|
||||
이 선택은 학습 목적에 맞다. Beam 요소는 수식이 충분히 작아 직접 구현할 수 있고, 단면 형상과 하중 변화가 변위/응력에 비선형적인 영향을 주므로 surrogate 비교에도 적합하다.
|
||||
|
||||
## 모델 비교 관점
|
||||
이 튜토리얼은 다음 질문에 답하도록 설계한다.
|
||||
|
||||
| 모델 | 핵심 질문 |
|
||||
| --- | --- |
|
||||
| Response Surface | 낮은 차수 다항식으로 구조 응답을 충분히 설명할 수 있는가? |
|
||||
| Gaussian Process/Kriging | 적은 데이터에서 예측 불확실성을 함께 얻을 수 있는가? |
|
||||
| Random Forest | 비선형성과 변수 상호작용을 튼튼하게 포착하는가? |
|
||||
| Gradient Boosting | 작은 tree를 순차적으로 더해 높은 정확도를 얻는가? |
|
||||
| MLP | scaling과 충분한 데이터가 있을 때 매끄러운 비선형 근사가 가능한가? |
|
||||
|
||||
## 검증 원칙
|
||||
Surrogate 검증은 단순히 test R2 하나로 끝내지 않는다.
|
||||
|
||||
- Train/test split은 고정 seed로 재현한다.
|
||||
- K-fold cross validation으로 표본 의존성을 확인한다.
|
||||
- RMSE, MAE, R2를 함께 본다.
|
||||
- Parity plot으로 편향과 outlier를 본다.
|
||||
- Residual plot으로 입력 영역별 오류 구조를 본다.
|
||||
- 모델별 uncertainty 또는 feature importance를 해석한다.
|
||||
- 해석 데이터의 물리 단위와 target 정의를 metadata에 남긴다.
|
||||
|
||||
## 실패 모드
|
||||
- 학습 영역 밖에서 외삽한다.
|
||||
- 입력 변수 범위가 물리적으로 의미 없는 조합을 포함한다.
|
||||
- 응력처럼 국소적이고 비선형적인 응답을 너무 단순한 모델로 근사한다.
|
||||
- train/test split이 샘플링 편향을 숨긴다.
|
||||
- feature scaling을 하지 않아 MLP나 GPR 학습이 불안정하다.
|
||||
- 예측 정확도만 보고 물리 제약을 확인하지 않는다.
|
||||
|
||||
## References
|
||||
- Queipo, N. V. et al. (2005), "Surrogate-based analysis and optimization", Progress in Aerospace Sciences. https://doi.org/10.1016/j.paerosci.2005.02.001
|
||||
- Forrester, A. I. J. and Keane, A. J. (2009), "Recent advances in surrogate-based optimization", Progress in Aerospace Sciences. https://doi.org/10.1016/j.paerosci.2008.11.001
|
||||
- SciPy `spsolve` documentation. https://docs.scipy.org/doc/scipy/reference/generated/scipy.sparse.linalg.spsolve.html
|
||||
@@ -0,0 +1,114 @@
|
||||
# DOE, Sampling, Validation
|
||||
|
||||
## 목적
|
||||
Surrogate model의 품질은 모델 종류만큼이나 데이터 설계에 좌우된다. FEM surrogate에서는 입력 변수 범위, 샘플링 방법, train/test split, 검증 지표가 모두 모델 해석에 영향을 준다.
|
||||
|
||||
## 입력 공간 정의
|
||||
이 튜토리얼의 기본 입력 변수는 다음과 같다.
|
||||
|
||||
| 변수 | 의미 | 단위 | 예시 범위 |
|
||||
| --- | --- | --- | --- |
|
||||
| `L_m` | beam length | m | 1.0-3.0 |
|
||||
| `b_m` | rectangular section width | m | 0.02-0.08 |
|
||||
| `h_m` | rectangular section height | m | 0.04-0.16 |
|
||||
| `E_pa` | Young's modulus | Pa | 100e9-220e9 |
|
||||
| `P_n` | tip point load magnitude | N | 100-2000 |
|
||||
|
||||
파생 변수는 FEM 해석 직전에 계산한다.
|
||||
|
||||
```text
|
||||
A = b h
|
||||
I = b h^3 / 12
|
||||
```
|
||||
|
||||
`h`는 bending stiffness에 세제곱으로 들어가므로, tip displacement와 bending stress에 큰 영향을 준다. 이런 구조적 비선형성 때문에 단순 선형 회귀보다 다양한 surrogate 비교가 의미를 가진다.
|
||||
|
||||
## Latin Hypercube Sampling
|
||||
Latin Hypercube Sampling(LHS)은 각 입력 변수의 marginal distribution을 층화하여, 적은 샘플에서도 각 변수 범위를 비교적 고르게 덮도록 설계한다. McKay, Beckman, Conover의 1979년 논문은 computer code output 분석에서 sampling plan을 비교한 고전적 출처다.
|
||||
|
||||
SciPy의 `scipy.stats.qmc.LatinHypercube`는 `[0, 1)^d` 단위 hypercube에 샘플을 만들고, 이후 사용자가 물리 범위로 scaling한다.
|
||||
|
||||
```text
|
||||
u ~ LHS([0,1)^d)
|
||||
x_j = lower_j + u_j (upper_j - lower_j)
|
||||
```
|
||||
|
||||
이 튜토리얼은 다음을 기본값으로 사용한다.
|
||||
|
||||
- `n_samples = 300`
|
||||
- `seed = 20260521`
|
||||
- `target = tip_uy_m`
|
||||
- 동일 dataset과 동일 split을 모든 model notebook에서 사용
|
||||
|
||||
## Train/Test Split
|
||||
단일 test set만으로 모델을 판단하면 샘플 배치에 민감할 수 있다. 따라서 다음 두 가지를 함께 사용한다.
|
||||
|
||||
- Hold-out test set: 최종 성능 비교용.
|
||||
- K-fold cross validation: 학습 데이터 안에서 모델 안정성 확인용.
|
||||
|
||||
추천 기본값:
|
||||
|
||||
```text
|
||||
test_size = 0.2
|
||||
cv_folds = 5
|
||||
random_state = 20260521
|
||||
```
|
||||
|
||||
## 평가 지표
|
||||
### RMSE
|
||||
큰 오차에 민감하다. 구조해석 surrogate에서 위험한 outlier를 확인하는 데 유용하다.
|
||||
|
||||
```text
|
||||
RMSE = sqrt(mean((y - y_hat)^2))
|
||||
```
|
||||
|
||||
### MAE
|
||||
평균적인 절대 오차를 직관적으로 보여준다.
|
||||
|
||||
```text
|
||||
MAE = mean(abs(y - y_hat))
|
||||
```
|
||||
|
||||
### R2
|
||||
분산 설명력을 나타낸다. 다만 target scale이나 test set 분포에 따라 해석이 왜곡될 수 있으므로 RMSE/MAE와 함께 본다.
|
||||
|
||||
```text
|
||||
R2 = 1 - sum((y - y_hat)^2) / sum((y - mean(y))^2)
|
||||
```
|
||||
|
||||
## Plot 기반 진단
|
||||
- Parity plot: 예측값과 실제값이 `y=x` 선 주변에 있는지 확인.
|
||||
- Residual plot: 예측값 또는 주요 입력 변수에 따른 오차 패턴 확인.
|
||||
- Error histogram: 오차 분포와 outlier 확인.
|
||||
- Model comparison bar plot: RMSE, MAE, R2, 학습 시간, 예측 시간 비교.
|
||||
|
||||
## Dataset Metadata
|
||||
CSV만으로는 dataset의 생성 조건을 알기 어렵다. 따라서 metadata JSON을 함께 저장한다.
|
||||
|
||||
```json
|
||||
{
|
||||
"dataset_name": "beam2d_lhs_300",
|
||||
"sample_count": 300,
|
||||
"random_seed": 20260521,
|
||||
"unit_system": "SI",
|
||||
"fea_model": "2D Euler-Bernoulli beam/frame, linear static",
|
||||
"target_columns": [
|
||||
"tip_uy_m",
|
||||
"max_abs_bending_stress_pa",
|
||||
"mass_kg",
|
||||
"compliance_j"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 주의점
|
||||
- DOE 범위는 surrogate의 유효 영역이다. 범위 밖 예측은 별도 경고를 둔다.
|
||||
- 물리적으로 불가능한 조합을 허용하지 않는다.
|
||||
- FEM solver 실패 케이스는 조용히 버리지 말고 metadata에 기록한다.
|
||||
- 단위가 섞이면 모델 비교가 무의미해진다.
|
||||
- 동일 데이터셋을 쓰지 않으면 모델별 비교가 공정하지 않다.
|
||||
|
||||
## References
|
||||
- McKay, M. D., Beckman, R. J., and Conover, W. J. (1979), "Comparison of Three Methods for Selecting Values of Input Variables in the Analysis of Output from a Computer Code", Technometrics. https://doi.org/10.1080/00401706.1979.10489755
|
||||
- OSTI bibliographic record for McKay et al. https://www.osti.gov/biblio/5236110
|
||||
- SciPy `LatinHypercube` documentation. https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.qmc.LatinHypercube.html
|
||||
@@ -0,0 +1,77 @@
|
||||
# Response Surface Methodology
|
||||
|
||||
## 핵심 아이디어
|
||||
Response Surface Methodology(RSM)는 입력 변수와 응답 사이의 관계를 낮은 차수 다항식으로 근사한다. 구조해석 surrogate에서는 물리 응답이 충분히 매끄럽고 입력 범위가 좁을 때 강력한 기준선이 된다.
|
||||
|
||||
기본 2차 response surface는 다음 형태다.
|
||||
|
||||
```text
|
||||
y = beta_0 + sum beta_i x_i + sum beta_ii x_i^2 + sum beta_ij x_i x_j + epsilon
|
||||
```
|
||||
|
||||
- `x_i`: scaling된 설계변수.
|
||||
- `beta`: 회귀 계수.
|
||||
- `x_i x_j`: 변수 상호작용.
|
||||
- `epsilon`: 모델이 설명하지 못한 잔차.
|
||||
|
||||
## FEM surrogate에서의 의미
|
||||
Beam 문제에서는 `I = b h^3 / 12` 때문에 응답이 입력 변수에 대해 비선형이다. 낮은 차수 다항식은 이 관계를 완벽히 재현하지 못할 수 있지만, 다음 장점이 있다.
|
||||
|
||||
- 빠르다.
|
||||
- 계수 해석이 가능하다.
|
||||
- 적은 데이터에서도 안정적이다.
|
||||
- 복잡한 모델의 baseline으로 적합하다.
|
||||
|
||||
RSM은 특히 설계변수 범위가 좁고 응답이 단조롭고 매끄러울 때 유용하다.
|
||||
|
||||
## 구현 선택
|
||||
이 튜토리얼에서는 scikit-learn의 `PolynomialFeatures`와 `Ridge`를 조합한다.
|
||||
|
||||
```text
|
||||
StandardScaler
|
||||
-> PolynomialFeatures(degree=2, include_bias=False)
|
||||
-> Ridge(alpha=...)
|
||||
```
|
||||
|
||||
`PolynomialFeatures`는 입력 변수의 다항 조합을 만들고, `Ridge`는 L2 regularization으로 계수 크기를 제어한다. 일반 선형회귀 대신 Ridge를 쓰는 이유는 다항 feature가 늘어나면 feature 간 상관성이 커지고 계수가 불안정해질 수 있기 때문이다.
|
||||
|
||||
## Notebook 실습 포인트
|
||||
`notebooks/01_response_surface_surrogate.ipynb`는 다음을 보여준다.
|
||||
|
||||
1. 동일 FEM dataset 로드.
|
||||
2. target `tip_uy_m` 선택.
|
||||
3. degree 1, 2, 3 비교.
|
||||
4. Ridge `alpha` 변화에 따른 train/test error 비교.
|
||||
5. parity plot과 residual plot.
|
||||
6. 다항 feature 이름과 계수 크기 해석.
|
||||
|
||||
## 장점
|
||||
- 학습과 예측이 매우 빠르다.
|
||||
- 작은 데이터에서 baseline으로 좋다.
|
||||
- 변수 간 interaction을 명시적으로 볼 수 있다.
|
||||
- extrapolation이 tree model보다 연속적이다.
|
||||
|
||||
## 한계와 실패 모드
|
||||
- 실제 응답이 강하게 비선형이면 낮은 차수 다항식으로 부족하다.
|
||||
- 높은 차수는 과적합과 수치 불안정을 만든다.
|
||||
- 입력 scaling 없이 다항 feature를 만들면 큰 단위의 변수가 학습을 지배할 수 있다.
|
||||
- 설계공간 경계 밖에서는 다항식이 물리적으로 말이 안 되는 큰 값을 낼 수 있다.
|
||||
|
||||
## 구조해석 해석 기준
|
||||
RSM이 좋은 선택인 경우:
|
||||
|
||||
- 설계변수 수가 적다.
|
||||
- 입력 범위가 좁다.
|
||||
- 응답이 매끄럽고 단조적이다.
|
||||
- 설명 가능한 surrogate가 중요하다.
|
||||
|
||||
RSM을 피해야 하는 경우:
|
||||
|
||||
- 응답에 threshold, discontinuity, contact-like behavior가 있다.
|
||||
- 입력 범위가 넓고 상호작용이 복잡하다.
|
||||
- 국소 응력 peak처럼 고차 비선형성이 크다.
|
||||
|
||||
## References
|
||||
- Box, G. E. P. and Wilson, K. B. (1951), "On the Experimental Attainment of Optimum Conditions", Journal of the Royal Statistical Society Series B. https://doi.org/10.1111/j.2517-6161.1951.tb00067.x
|
||||
- scikit-learn `PolynomialFeatures` documentation. https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.PolynomialFeatures.html
|
||||
- scikit-learn `Ridge` documentation. https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.Ridge.html
|
||||
@@ -0,0 +1,86 @@
|
||||
# Gaussian Process Regression and Kriging
|
||||
|
||||
## 핵심 아이디어
|
||||
Gaussian Process Regression(GPR)은 함수값을 확률변수로 보고, 입력 위치 사이의 상관관계를 kernel로 정의한다. Engineering design 분야에서는 Kriging이라는 이름으로 널리 사용된다.
|
||||
|
||||
GPR은 예측 평균뿐 아니라 예측 표준편차를 제공한다.
|
||||
|
||||
```text
|
||||
y(x) ~ GP(m(x), k(x, x'))
|
||||
```
|
||||
|
||||
- `m(x)`: mean function.
|
||||
- `k(x, x')`: covariance function 또는 kernel.
|
||||
- 예측 결과: `mean`, `standard deviation`.
|
||||
|
||||
## FEM surrogate에서의 의미
|
||||
FEM 해석 데이터는 deterministic한 computer experiment인 경우가 많다. 같은 입력을 넣으면 같은 출력이 나온다. 이런 상황에서 GPR/Kriging은 적은 해석점으로 매끄러운 응답면을 만들고, 학습점에서 먼 영역의 불확실성을 크게 줄 수 있어 surrogate 신뢰도 설명에 유리하다.
|
||||
|
||||
Sacks et al.의 computer experiment 연구는 계산 모델 입력과 출력 사이의 응답을 stochastic process로 모델링하는 관점을 제시했다. Rasmussen과 Williams의 GPML은 Gaussian process의 수학적 기반과 kernel 관점을 체계적으로 설명한다.
|
||||
|
||||
## 구현 선택
|
||||
이 튜토리얼에서는 다음 kernel을 기본으로 사용한다.
|
||||
|
||||
```text
|
||||
ConstantKernel * RBF + WhiteKernel
|
||||
```
|
||||
|
||||
- `ConstantKernel`: 응답 scale.
|
||||
- `RBF`: 입력 공간에서 가까운 점은 비슷한 응답을 가진다는 smoothness 가정.
|
||||
- `WhiteKernel`: 수치 noise 또는 모델 불일치 허용.
|
||||
|
||||
Notebook pipeline:
|
||||
|
||||
```text
|
||||
StandardScaler
|
||||
-> GaussianProcessRegressor(kernel=..., normalize_y=True)
|
||||
```
|
||||
|
||||
## Hyperparameter 의미
|
||||
- `length_scale`: 각 입력 방향에서 함수가 얼마나 빨리 변하는지 나타낸다.
|
||||
- `noise_level`: 해석 데이터의 noise 또는 모델 불일치 허용량이다.
|
||||
- `alpha`: numerical stability를 위한 diagonal jitter로 사용할 수 있다.
|
||||
- `n_restarts_optimizer`: kernel hyperparameter 최적화의 local optimum 위험을 줄인다.
|
||||
|
||||
## Notebook 실습 포인트
|
||||
`notebooks/02_gaussian_process_kriging_surrogate.ipynb`는 다음을 보여준다.
|
||||
|
||||
1. 동일 dataset과 split 로드.
|
||||
2. RBF kernel 기반 GPR 학습.
|
||||
3. 예측 평균과 표준편차 계산.
|
||||
4. parity plot에 uncertainty band 또는 error coloring 추가.
|
||||
5. 입력 공간에서 학습점과 먼 샘플의 uncertainty 확인.
|
||||
6. 학습 sample 수 증가에 따른 RMSE와 예측 표준편차 변화.
|
||||
|
||||
## 장점
|
||||
- 작은 데이터에서 강력하다.
|
||||
- 예측 uncertainty를 제공한다.
|
||||
- Smooth response surface에 적합하다.
|
||||
- Bayesian optimization과 active learning으로 확장하기 좋다.
|
||||
|
||||
## 한계와 실패 모드
|
||||
- 기본 exact GPR은 학습 데이터 수가 커지면 계산 비용이 급격히 증가한다.
|
||||
- Kernel 선택이 성능을 크게 좌우한다.
|
||||
- 입력 scaling이 중요하다.
|
||||
- 고차원 입력에서는 length scale 학습이 불안정할 수 있다.
|
||||
- 예측 표준편차는 모델 가정 하의 불확실성이지, 모든 물리 오류를 보장하지 않는다.
|
||||
|
||||
## 구조해석 해석 기준
|
||||
GPR/Kriging이 좋은 선택인 경우:
|
||||
|
||||
- FEM 해석이 비싸고 sample 수가 작다.
|
||||
- 응답이 매끄럽다.
|
||||
- uncertainty 기반 추가 샘플링을 설명하고 싶다.
|
||||
- 설계공간 탐색에서 신뢰도 경고가 필요하다.
|
||||
|
||||
주의할 경우:
|
||||
|
||||
- sample 수가 수천 개 이상이다.
|
||||
- 응답이 불연속적이다.
|
||||
- 입력 차원이 많고 상호작용이 복잡하다.
|
||||
|
||||
## References
|
||||
- Sacks, J., Welch, W. J., Mitchell, T. J., and Wynn, H. P. (1989), "Design and Analysis of Computer Experiments", Statistical Science. https://doi.org/10.1214/ss/1177012413
|
||||
- Sacks, J., Schiller, S. B., and Welch, W. J. (1989), "Designs for Computer Experiments", Technometrics. https://doi.org/10.1080/00401706.1989.10488474
|
||||
- Rasmussen, C. E. and Williams, C. K. I. (2006), Gaussian Processes for Machine Learning. https://gaussianprocess.org/gpml/chapters/RW.pdf
|
||||
- scikit-learn Gaussian Process documentation. https://scikit-learn.org/stable/modules/gaussian_process.html
|
||||
@@ -0,0 +1,76 @@
|
||||
# Random Forest Surrogate
|
||||
|
||||
## 핵심 아이디어
|
||||
Random Forest는 여러 decision tree를 bootstrap sample로 학습하고 평균하여 예측하는 ensemble 모델이다. 각 tree는 입력 공간을 여러 영역으로 나누고, 회귀 문제에서는 leaf의 평균값을 예측한다.
|
||||
|
||||
```text
|
||||
\hat{f}(x) = (1 / T) sum_{t=1}^{T} h_t(x)
|
||||
```
|
||||
|
||||
- `T`: tree 개수.
|
||||
- `h_t`: 개별 regression tree.
|
||||
|
||||
Breiman의 2001년 Random Forest 논문은 bagging과 random feature selection을 결합한 ensemble 방법을 제시했다.
|
||||
|
||||
## FEM surrogate에서의 의미
|
||||
Random Forest는 비선형성과 변수 상호작용을 명시적 feature engineering 없이 포착할 수 있다. Beam surrogate에서는 `h`, `b`, `L`, `P`, `E`의 비선형 영향과 interaction을 빠르게 잡는 비교 모델로 좋다.
|
||||
|
||||
## 구현 선택
|
||||
이 튜토리얼에서는 scikit-learn의 `RandomForestRegressor`를 사용한다.
|
||||
|
||||
기본 설정 예:
|
||||
|
||||
```text
|
||||
RandomForestRegressor(
|
||||
n_estimators=300,
|
||||
max_depth=None,
|
||||
min_samples_leaf=2,
|
||||
random_state=20260521,
|
||||
n_jobs=-1
|
||||
)
|
||||
```
|
||||
|
||||
Tree model은 feature scaling이 필수는 아니지만, 다른 모델과 공통 전처리 구조를 설명하기 위해 입력 column 관리는 동일하게 유지한다.
|
||||
|
||||
## Notebook 실습 포인트
|
||||
`notebooks/03_random_forest_surrogate.ipynb`는 다음을 보여준다.
|
||||
|
||||
1. 동일 dataset과 split 로드.
|
||||
2. `n_estimators`, `max_depth`, `min_samples_leaf` 영향 비교.
|
||||
3. test metric 계산.
|
||||
4. parity plot과 residual plot.
|
||||
5. permutation importance로 입력 변수 영향 확인.
|
||||
6. feature importance와 beam 물리 관계 비교.
|
||||
|
||||
## 장점
|
||||
- scaling에 덜 민감하다.
|
||||
- 비선형성과 interaction을 잘 포착한다.
|
||||
- outlier에 비교적 튼튼하다.
|
||||
- feature importance를 계산하기 쉽다.
|
||||
- 학습이 안정적이고 baseline으로 좋다.
|
||||
|
||||
## 한계와 실패 모드
|
||||
- 예측이 piecewise constant라 매끄러운 응답면이 필요한 경우 부자연스러울 수 있다.
|
||||
- 학습 데이터 범위 밖으로 외삽하지 못한다.
|
||||
- 물리적으로 단조여야 하는 관계를 자동으로 보장하지 않는다.
|
||||
- uncertainty는 GPR처럼 원래 제공되는 개념이 아니다.
|
||||
- feature importance는 correlated feature에서 오해될 수 있다.
|
||||
|
||||
## 구조해석 해석 기준
|
||||
Random Forest가 좋은 선택인 경우:
|
||||
|
||||
- 빠르고 안정적인 비선형 baseline이 필요하다.
|
||||
- 변수 중요도 분석을 보고 싶다.
|
||||
- 데이터가 중간 규모 이상이다.
|
||||
- target이 매끄러운 global response다.
|
||||
|
||||
주의할 경우:
|
||||
|
||||
- 설계 최적화에서 매끄러운 gradient-like response가 필요하다.
|
||||
- 외삽이 필요하다.
|
||||
- 응답면의 국소 smoothness가 중요하다.
|
||||
|
||||
## References
|
||||
- Breiman, L. (2001), "Random Forests", Machine Learning, 45, 5-32. https://doi.org/10.1023/A:1010933404324
|
||||
- CiNii bibliographic record for Breiman (2001). https://cir.nii.ac.jp/crid/1360574092892023168
|
||||
- scikit-learn `RandomForestRegressor` documentation. https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.RandomForestRegressor.html
|
||||
@@ -0,0 +1,77 @@
|
||||
# Gradient Boosting Surrogate
|
||||
|
||||
## 핵심 아이디어
|
||||
Gradient Boosting은 약한 예측기, 보통 shallow decision tree를 순차적으로 더해 손실함수를 줄이는 additive model이다. 각 단계의 tree는 이전 모델의 오차를 보정하는 방향으로 학습된다.
|
||||
|
||||
```text
|
||||
F_M(x) = F_0(x) + sum_{m=1}^{M} nu h_m(x)
|
||||
```
|
||||
|
||||
- `F_M`: 최종 모델.
|
||||
- `h_m`: m번째 weak learner.
|
||||
- `nu`: learning rate.
|
||||
|
||||
Friedman의 2001년 논문은 gradient boosting machine을 함수공간에서의 greedy approximation으로 설명한다.
|
||||
|
||||
## FEM surrogate에서의 의미
|
||||
Gradient Boosting은 Random Forest보다 bias를 더 적극적으로 줄이는 경우가 많다. Beam surrogate에서 낮은 차수 RSM이 놓치는 비선형성을 tree ensemble이 순차적으로 보정할 수 있다.
|
||||
|
||||
## 구현 선택
|
||||
이 튜토리얼에서는 scikit-learn의 `GradientBoostingRegressor`를 사용한다.
|
||||
|
||||
기본 설정 예:
|
||||
|
||||
```text
|
||||
GradientBoostingRegressor(
|
||||
loss="squared_error",
|
||||
learning_rate=0.05,
|
||||
n_estimators=300,
|
||||
max_depth=3,
|
||||
subsample=0.9,
|
||||
random_state=20260521
|
||||
)
|
||||
```
|
||||
|
||||
`learning_rate`와 `n_estimators`는 함께 조정해야 한다. 작은 learning rate는 더 많은 tree를 요구하지만 과적합을 완화할 수 있다.
|
||||
|
||||
## Notebook 실습 포인트
|
||||
`notebooks/04_gradient_boosting_surrogate.ipynb`는 다음을 보여준다.
|
||||
|
||||
1. 동일 dataset과 split 로드.
|
||||
2. `learning_rate`와 `n_estimators` tradeoff.
|
||||
3. staged prediction으로 train/test error curve 확인.
|
||||
4. parity plot과 residual plot.
|
||||
5. permutation importance 또는 built-in feature importance 비교.
|
||||
6. Random Forest와 오차 패턴 비교.
|
||||
|
||||
## 장점
|
||||
- tabular regression에서 높은 성능을 내기 쉽다.
|
||||
- 비선형성과 interaction을 잘 포착한다.
|
||||
- model size와 성능의 tradeoff를 조절하기 쉽다.
|
||||
- staged prediction으로 과적합 진행을 관찰할 수 있다.
|
||||
|
||||
## 한계와 실패 모드
|
||||
- hyperparameter에 Random Forest보다 민감하다.
|
||||
- noise가 큰 데이터에서는 오차를 과도하게 따라갈 수 있다.
|
||||
- tree 기반 모델이므로 외삽은 약하다.
|
||||
- 학습 순서가 sequential하므로 Random Forest보다 병렬화 이점이 작다.
|
||||
- 너무 복잡한 설정은 교육용 notebook을 흐리게 만들 수 있다.
|
||||
|
||||
## 구조해석 해석 기준
|
||||
Gradient Boosting이 좋은 선택인 경우:
|
||||
|
||||
- 정확도가 중요한 tabular surrogate가 필요하다.
|
||||
- 입력 변수 수가 적거나 중간 규모다.
|
||||
- 해석 응답이 비선형이지만 불연속은 아니다.
|
||||
- 최종 비교에서 강한 classical ML baseline이 필요하다.
|
||||
|
||||
주의할 경우:
|
||||
|
||||
- uncertainty가 핵심 요구사항이다.
|
||||
- 모델 해석성이 RSM 수준으로 필요하다.
|
||||
- 데이터 수가 매우 적다.
|
||||
|
||||
## References
|
||||
- Friedman, J. H. (2001), "Greedy Function Approximation: A Gradient Boosting Machine", The Annals of Statistics. https://doi.org/10.1214/aos/1013203451
|
||||
- CiNii bibliographic record for Friedman (2001). https://cir.nii.ac.jp/crid/1360292617909870720
|
||||
- scikit-learn `GradientBoostingRegressor` documentation. https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.GradientBoostingRegressor.html
|
||||
@@ -0,0 +1,90 @@
|
||||
# MLP Neural Network Surrogate
|
||||
|
||||
## 핵심 아이디어
|
||||
Multi-layer Perceptron(MLP)은 affine transform과 nonlinear activation을 여러 층으로 쌓아 입력과 출력의 비선형 관계를 학습한다.
|
||||
|
||||
단일 hidden layer MLP는 다음처럼 표현할 수 있다.
|
||||
|
||||
```text
|
||||
\hat{y} = W_2 phi(W_1 x + b_1) + b_2
|
||||
```
|
||||
|
||||
- `x`: scaling된 입력.
|
||||
- `phi`: activation function.
|
||||
- `W`, `b`: 학습되는 weight와 bias.
|
||||
|
||||
Cybenko의 universal approximation theorem은 충분한 hidden unit을 가진 신경망이 넓은 함수군을 근사할 수 있음을 보인 고전적 결과다. 다만 "근사 가능하다"는 말이 적은 데이터에서 항상 잘 학습된다는 뜻은 아니다.
|
||||
|
||||
## FEM surrogate에서의 의미
|
||||
MLP는 beam 응답처럼 매끄러운 비선형 함수를 학습할 수 있다. 그러나 데이터 수, scaling, regularization, optimization 설정에 민감하다. 이 튜토리얼에서는 deep learning framework를 도입하지 않고 scikit-learn의 `MLPRegressor`로 작은 neural surrogate의 특성을 보여준다.
|
||||
|
||||
## 구현 선택
|
||||
기본 pipeline:
|
||||
|
||||
```text
|
||||
StandardScaler
|
||||
-> TransformedTargetRegressor(
|
||||
regressor=MLPRegressor(...),
|
||||
transformer=StandardScaler()
|
||||
)
|
||||
```
|
||||
|
||||
추천 기본 설정:
|
||||
|
||||
```text
|
||||
MLPRegressor(
|
||||
hidden_layer_sizes=(64, 32),
|
||||
activation="relu",
|
||||
solver="adam",
|
||||
alpha=1e-4,
|
||||
learning_rate_init=1e-3,
|
||||
early_stopping=True,
|
||||
max_iter=2000,
|
||||
random_state=20260521
|
||||
)
|
||||
```
|
||||
|
||||
입력뿐 아니라 target scaling도 중요하다. FEM 응답은 단위와 scale이 크게 다를 수 있기 때문이다.
|
||||
|
||||
## Notebook 실습 포인트
|
||||
`notebooks/05_mlp_surrogate.ipynb`는 다음을 보여준다.
|
||||
|
||||
1. 동일 dataset과 split 로드.
|
||||
2. 입력 scaling과 target scaling의 효과.
|
||||
3. hidden layer 크기 비교.
|
||||
4. train/test learning curve.
|
||||
5. parity plot과 residual plot.
|
||||
6. RSM, GPR, tree ensemble 대비 MLP의 장단점 정리.
|
||||
|
||||
## 장점
|
||||
- 매끄러운 비선형 함수 근사에 적합하다.
|
||||
- 다중 출력 surrogate로 확장하기 쉽다.
|
||||
- 충분한 데이터와 tuning이 있으면 복잡한 관계를 학습할 수 있다.
|
||||
- 동일 pipeline 구조로 다른 neural framework로 확장 가능하다.
|
||||
|
||||
## 한계와 실패 모드
|
||||
- 작은 데이터에서는 과적합하거나 불안정할 수 있다.
|
||||
- scaling 없이 학습하면 convergence가 나빠질 수 있다.
|
||||
- hyperparameter 선택이 성능에 큰 영향을 준다.
|
||||
- GPR처럼 기본 uncertainty를 제공하지 않는다.
|
||||
- 모델 해석성이 낮다.
|
||||
|
||||
## 구조해석 해석 기준
|
||||
MLP가 좋은 선택인 경우:
|
||||
|
||||
- 데이터 수가 충분하다.
|
||||
- 응답이 매끄럽지만 단순 다항식으로 부족하다.
|
||||
- 다중 출력 또는 복잡한 feature representation 확장을 고려한다.
|
||||
- 최종 정확도가 중요하고 해석성 요구가 낮다.
|
||||
|
||||
주의할 경우:
|
||||
|
||||
- sample 수가 매우 작다.
|
||||
- 예측 불확실성이 중요하다.
|
||||
- 실험자가 scaling과 validation을 엄격히 관리하기 어렵다.
|
||||
|
||||
## References
|
||||
- Cybenko, G. (1989), "Approximation by Superpositions of a Sigmoidal Function", Mathematics of Control, Signals, and Systems. https://doi.org/10.1007/BF02551274
|
||||
- CiNii bibliographic record for Cybenko (1989). https://cir.nii.ac.jp/crid/1361981471344652032
|
||||
- Glorot, X. and Bengio, Y. (2010), "Understanding the difficulty of training deep feedforward neural networks", AISTATS. https://proceedings.mlr.press/v9/glorot10a.html
|
||||
- scikit-learn `MLPRegressor` documentation. https://scikit-learn.org/stable/modules/generated/sklearn.neural_network.MLPRegressor.html
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "7e3ade70",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# 00 Beam2D FEM Dataset\n",
|
||||
"\n",
|
||||
"BeamExamples ?? ??? ?? ??? ? LHS sample? ???, repository solver? FEM surrogate ??? dataset? ????."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "71929240",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import json\n",
|
||||
"from pathlib import Path\n",
|
||||
"\n",
|
||||
"import numpy as np\n",
|
||||
"\n",
|
||||
"from femsurrogate.data.bounds import DEFAULT_PARAMETER_BOUNDS\n",
|
||||
"from femsurrogate.data.dataset import build_dataset\n",
|
||||
"from femsurrogate.data.sampling import generate_lhs_samples\n",
|
||||
"from femsurrogate.data.schema import DEFAULT_RANDOM_SEED, TARGET_COLUMNS\n",
|
||||
"from femsurrogate.fea.io import read_beam_example, read_expected_displacements\n",
|
||||
"from femsurrogate.fea.solver import solve_linear_static\n",
|
||||
"\n",
|
||||
"ROOT = Path.cwd().resolve()\n",
|
||||
"if not (ROOT / \"pyproject.toml\").exists():\n",
|
||||
" ROOT = ROOT.parent\n",
|
||||
"assert (ROOT / \"pyproject.toml\").exists(), ROOT\n",
|
||||
"REFERENCE_DIR = ROOT / \"data\" / \"reference\"\n",
|
||||
"REFERENCE_DIR.mkdir(parents=True, exist_ok=True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "df256ebc",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## BeamExamples ?? ??\n",
|
||||
"\n",
|
||||
"Fixture? ?? node? ?? `Ux`, `Uy`, `Rz`? ???? ???? ?? ????? ????."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "0a8c947c",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"model = read_beam_example(ROOT / \"BeamExamples\" / \"CantileverBeam.txt\")\n",
|
||||
"expected = read_expected_displacements(ROOT / \"BeamExamples\" / \"CantileverBeam_Displacements.txt\")\n",
|
||||
"actual = solve_linear_static(model)\n",
|
||||
"\n",
|
||||
"max_abs_error = 0.0\n",
|
||||
"for node_id, expected_displacement in expected.items():\n",
|
||||
" actual_displacement = actual[node_id]\n",
|
||||
" actual_values = np.array(\n",
|
||||
" [actual_displacement.ux, actual_displacement.uy, actual_displacement.rz]\n",
|
||||
" )\n",
|
||||
" expected_values = np.array(\n",
|
||||
" [expected_displacement.ux, expected_displacement.uy, expected_displacement.rz]\n",
|
||||
" )\n",
|
||||
" error = float(np.max(np.abs(actual_values - expected_values)))\n",
|
||||
" max_abs_error = max(max_abs_error, error)\n",
|
||||
"\n",
|
||||
"assert max_abs_error <= 5e-7\n",
|
||||
"max_abs_error"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "6620d0ee",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## LHS sampling? FEM batch ??"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "9923fd04",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"N_SAMPLES = 300\n",
|
||||
"\n",
|
||||
"samples = generate_lhs_samples(DEFAULT_PARAMETER_BOUNDS, n=N_SAMPLES, seed=DEFAULT_RANDOM_SEED)\n",
|
||||
"dataset = build_dataset(samples)\n",
|
||||
"\n",
|
||||
"assert len(dataset) == N_SAMPLES\n",
|
||||
"assert set(TARGET_COLUMNS).issubset(dataset.columns)\n",
|
||||
"dataset.head()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "c0efee15",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Dataset ??\n",
|
||||
"\n",
|
||||
"?? notebook? ?? CSV? metadata? ??? `data/reference/`? ????."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "d80890bd",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"dataset_path = REFERENCE_DIR / \"beam2d_lhs_300.csv\"\n",
|
||||
"metadata_path = REFERENCE_DIR / \"beam2d_lhs_300_metadata.json\"\n",
|
||||
"\n",
|
||||
"dataset.to_csv(dataset_path, index=False)\n",
|
||||
"metadata = {\n",
|
||||
" \"dataset_name\": \"beam2d_lhs_300\",\n",
|
||||
" \"sample_count\": N_SAMPLES,\n",
|
||||
" \"random_seed\": DEFAULT_RANDOM_SEED,\n",
|
||||
" \"unit_system\": \"SI\",\n",
|
||||
" \"fea_model\": \"2D Euler-Bernoulli beam/frame, linear static\",\n",
|
||||
" \"target_columns\": list(TARGET_COLUMNS),\n",
|
||||
" \"parameter_bounds\": {\n",
|
||||
" name: {\"lower\": bound.lower, \"upper\": bound.upper}\n",
|
||||
" for name, bound in DEFAULT_PARAMETER_BOUNDS.items()\n",
|
||||
" },\n",
|
||||
" \"notes\": \"Generated by notebooks/00_beam2d_fea_dataset.ipynb using src/femsurrogate.\",\n",
|
||||
"}\n",
|
||||
"metadata_path.write_text(json.dumps(metadata, indent=2), encoding=\"utf-8\")\n",
|
||||
"\n",
|
||||
"{\"dataset_path\": str(dataset_path), \"metadata_path\": str(metadata_path)}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "d56bcddb",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## ?? sanity check"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "e560e50c",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"dataset.describe().T.loc[[\"L_m\", \"b_m\", \"h_m\", \"E_pa\", \"P_n\", *TARGET_COLUMNS]]"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python",
|
||||
"pygments_lexer": "ipython3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "260fc3ce",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Response Surface Surrogate\n",
|
||||
"\n",
|
||||
"Response Surface? polynomial feature? Ridge regression?? ?? baseline? ???.\n",
|
||||
"\n",
|
||||
"?? ?? notebook? ?? dataset, target, train/test split seed? ????."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "56528a9c",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import json\n",
|
||||
"import warnings\n",
|
||||
"from pathlib import Path\n",
|
||||
"\n",
|
||||
"import pandas as pd\n",
|
||||
"from sklearn.exceptions import ConvergenceWarning\n",
|
||||
"\n",
|
||||
"from femsurrogate.data.schema import DEFAULT_RANDOM_SEED, PARAMETER_COLUMNS\n",
|
||||
"from femsurrogate.plotting.diagnostics import plot_parity, plot_residuals\n",
|
||||
"from femsurrogate.surrogates.common import evaluate_model, metrics_to_dict, split_dataset\n",
|
||||
"from femsurrogate.surrogates.registry import make_model\n",
|
||||
"\n",
|
||||
"ROOT = Path.cwd().resolve()\n",
|
||||
"if not (ROOT / \"pyproject.toml\").exists():\n",
|
||||
" ROOT = ROOT.parent\n",
|
||||
"assert (ROOT / \"pyproject.toml\").exists(), ROOT\n",
|
||||
"DATASET_PATH = ROOT / \"data\" / \"reference\" / \"beam2d_lhs_300.csv\"\n",
|
||||
"RESULTS_DIR = ROOT / \"reports\" / \"results\"\n",
|
||||
"PREDICTIONS_DIR = ROOT / \"reports\" / \"predictions\"\n",
|
||||
"FIGURES_DIR = ROOT / \"reports\" / \"figures\"\n",
|
||||
"for directory in [RESULTS_DIR, PREDICTIONS_DIR, FIGURES_DIR]:\n",
|
||||
" directory.mkdir(parents=True, exist_ok=True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "7cd36ad9",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Dataset? split"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "50689a07",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"dataset = pd.read_csv(DATASET_PATH)\n",
|
||||
"target_column = \"tip_uy_m\"\n",
|
||||
"split = split_dataset(\n",
|
||||
" dataset,\n",
|
||||
" feature_columns=list(PARAMETER_COLUMNS),\n",
|
||||
" target_column=target_column,\n",
|
||||
" test_size=0.2,\n",
|
||||
" random_state=DEFAULT_RANDOM_SEED,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"len(split.X_train), len(split.X_test)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "0473847f",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## ?? ??? ??"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "539ffb4d",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"MODEL_NAME = \"rsm\"\n",
|
||||
"model = make_model(MODEL_NAME, random_state=DEFAULT_RANDOM_SEED, **{})\n",
|
||||
"\n",
|
||||
"with warnings.catch_warnings():\n",
|
||||
" warnings.filterwarnings(\"ignore\", category=ConvergenceWarning)\n",
|
||||
" result = evaluate_model(\n",
|
||||
" model,\n",
|
||||
" split.X_train,\n",
|
||||
" split.X_test,\n",
|
||||
" split.y_train,\n",
|
||||
" split.y_test,\n",
|
||||
" model_name=MODEL_NAME,\n",
|
||||
" target_column=target_column,\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
"metrics = metrics_to_dict(result.metrics)\n",
|
||||
"metrics"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "78c34610",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## ?? ??"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "e15bf80d",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"metrics_path = RESULTS_DIR / f\"{MODEL_NAME}_metrics.json\"\n",
|
||||
"predictions_path = PREDICTIONS_DIR / f\"{MODEL_NAME}_predictions.csv\"\n",
|
||||
"\n",
|
||||
"metrics_path.write_text(json.dumps(metrics, indent=2), encoding=\"utf-8\")\n",
|
||||
"result.predictions.to_csv(predictions_path, index=False)\n",
|
||||
"\n",
|
||||
"{\"metrics_path\": str(metrics_path), \"predictions_path\": str(predictions_path)}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "2142e40b",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## ?? plot"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "14b89510",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"parity_fig = plot_parity(result.predictions, title=f\"{MODEL_NAME} parity\")\n",
|
||||
"residual_fig = plot_residuals(result.predictions, title=f\"{MODEL_NAME} residuals\")\n",
|
||||
"parity_fig.savefig(FIGURES_DIR / f\"{MODEL_NAME}_parity.png\", dpi=150, bbox_inches=\"tight\")\n",
|
||||
"residual_fig.savefig(FIGURES_DIR / f\"{MODEL_NAME}_residuals.png\", dpi=150, bbox_inches=\"tight\")\n",
|
||||
"parity_fig"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python",
|
||||
"pygments_lexer": "ipython3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "144f56e0",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Gaussian Process / Kriging Surrogate\n",
|
||||
"\n",
|
||||
"GPR? smooth response? ?? ???? ??? ???? ????.\n",
|
||||
"\n",
|
||||
"?? ?? notebook? ?? dataset, target, train/test split seed? ????."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "7a5a2974",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import json\n",
|
||||
"import warnings\n",
|
||||
"from pathlib import Path\n",
|
||||
"\n",
|
||||
"import pandas as pd\n",
|
||||
"from sklearn.exceptions import ConvergenceWarning\n",
|
||||
"\n",
|
||||
"from femsurrogate.data.schema import DEFAULT_RANDOM_SEED, PARAMETER_COLUMNS\n",
|
||||
"from femsurrogate.plotting.diagnostics import plot_parity, plot_residuals\n",
|
||||
"from femsurrogate.surrogates.common import evaluate_model, metrics_to_dict, split_dataset\n",
|
||||
"from femsurrogate.surrogates.registry import make_model\n",
|
||||
"\n",
|
||||
"ROOT = Path.cwd().resolve()\n",
|
||||
"if not (ROOT / \"pyproject.toml\").exists():\n",
|
||||
" ROOT = ROOT.parent\n",
|
||||
"assert (ROOT / \"pyproject.toml\").exists(), ROOT\n",
|
||||
"DATASET_PATH = ROOT / \"data\" / \"reference\" / \"beam2d_lhs_300.csv\"\n",
|
||||
"RESULTS_DIR = ROOT / \"reports\" / \"results\"\n",
|
||||
"PREDICTIONS_DIR = ROOT / \"reports\" / \"predictions\"\n",
|
||||
"FIGURES_DIR = ROOT / \"reports\" / \"figures\"\n",
|
||||
"for directory in [RESULTS_DIR, PREDICTIONS_DIR, FIGURES_DIR]:\n",
|
||||
" directory.mkdir(parents=True, exist_ok=True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "a2c2b3fe",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Dataset? split"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "ef13216f",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"dataset = pd.read_csv(DATASET_PATH)\n",
|
||||
"target_column = \"tip_uy_m\"\n",
|
||||
"split = split_dataset(\n",
|
||||
" dataset,\n",
|
||||
" feature_columns=list(PARAMETER_COLUMNS),\n",
|
||||
" target_column=target_column,\n",
|
||||
" test_size=0.2,\n",
|
||||
" random_state=DEFAULT_RANDOM_SEED,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"len(split.X_train), len(split.X_test)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "6c3be3f4",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## ?? ??? ??"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "d5e09c8a",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"MODEL_NAME = \"gpr\"\n",
|
||||
"model = make_model(MODEL_NAME, random_state=DEFAULT_RANDOM_SEED, **{})\n",
|
||||
"\n",
|
||||
"with warnings.catch_warnings():\n",
|
||||
" warnings.filterwarnings(\"ignore\", category=ConvergenceWarning)\n",
|
||||
" result = evaluate_model(\n",
|
||||
" model,\n",
|
||||
" split.X_train,\n",
|
||||
" split.X_test,\n",
|
||||
" split.y_train,\n",
|
||||
" split.y_test,\n",
|
||||
" model_name=MODEL_NAME,\n",
|
||||
" target_column=target_column,\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
"metrics = metrics_to_dict(result.metrics)\n",
|
||||
"metrics"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "4b9e74af",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## ?? ??"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "5af40658",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"metrics_path = RESULTS_DIR / f\"{MODEL_NAME}_metrics.json\"\n",
|
||||
"predictions_path = PREDICTIONS_DIR / f\"{MODEL_NAME}_predictions.csv\"\n",
|
||||
"\n",
|
||||
"metrics_path.write_text(json.dumps(metrics, indent=2), encoding=\"utf-8\")\n",
|
||||
"result.predictions.to_csv(predictions_path, index=False)\n",
|
||||
"\n",
|
||||
"{\"metrics_path\": str(metrics_path), \"predictions_path\": str(predictions_path)}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "37a881b4",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## ?? plot"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "d156a54d",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"parity_fig = plot_parity(result.predictions, title=f\"{MODEL_NAME} parity\")\n",
|
||||
"residual_fig = plot_residuals(result.predictions, title=f\"{MODEL_NAME} residuals\")\n",
|
||||
"parity_fig.savefig(FIGURES_DIR / f\"{MODEL_NAME}_parity.png\", dpi=150, bbox_inches=\"tight\")\n",
|
||||
"residual_fig.savefig(FIGURES_DIR / f\"{MODEL_NAME}_residuals.png\", dpi=150, bbox_inches=\"tight\")\n",
|
||||
"parity_fig"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python",
|
||||
"pygments_lexer": "ipython3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "1638813f",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Random Forest Surrogate\n",
|
||||
"\n",
|
||||
"Random Forest? feature scaling ??? ????? interaction? ??? ???? tree ensemble??.\n",
|
||||
"\n",
|
||||
"?? ?? notebook? ?? dataset, target, train/test split seed? ????."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "b8bdcff3",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import json\n",
|
||||
"import warnings\n",
|
||||
"from pathlib import Path\n",
|
||||
"\n",
|
||||
"import pandas as pd\n",
|
||||
"from sklearn.exceptions import ConvergenceWarning\n",
|
||||
"\n",
|
||||
"from femsurrogate.data.schema import DEFAULT_RANDOM_SEED, PARAMETER_COLUMNS\n",
|
||||
"from femsurrogate.plotting.diagnostics import plot_parity, plot_residuals\n",
|
||||
"from femsurrogate.surrogates.common import evaluate_model, metrics_to_dict, split_dataset\n",
|
||||
"from femsurrogate.surrogates.registry import make_model\n",
|
||||
"\n",
|
||||
"ROOT = Path.cwd().resolve()\n",
|
||||
"if not (ROOT / \"pyproject.toml\").exists():\n",
|
||||
" ROOT = ROOT.parent\n",
|
||||
"assert (ROOT / \"pyproject.toml\").exists(), ROOT\n",
|
||||
"DATASET_PATH = ROOT / \"data\" / \"reference\" / \"beam2d_lhs_300.csv\"\n",
|
||||
"RESULTS_DIR = ROOT / \"reports\" / \"results\"\n",
|
||||
"PREDICTIONS_DIR = ROOT / \"reports\" / \"predictions\"\n",
|
||||
"FIGURES_DIR = ROOT / \"reports\" / \"figures\"\n",
|
||||
"for directory in [RESULTS_DIR, PREDICTIONS_DIR, FIGURES_DIR]:\n",
|
||||
" directory.mkdir(parents=True, exist_ok=True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "6957e06b",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Dataset? split"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "209f40be",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"dataset = pd.read_csv(DATASET_PATH)\n",
|
||||
"target_column = \"tip_uy_m\"\n",
|
||||
"split = split_dataset(\n",
|
||||
" dataset,\n",
|
||||
" feature_columns=list(PARAMETER_COLUMNS),\n",
|
||||
" target_column=target_column,\n",
|
||||
" test_size=0.2,\n",
|
||||
" random_state=DEFAULT_RANDOM_SEED,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"len(split.X_train), len(split.X_test)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "fdc2fbed",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## ?? ??? ??"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "63ed6060",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"MODEL_NAME = \"random_forest\"\n",
|
||||
"model = make_model(MODEL_NAME, random_state=DEFAULT_RANDOM_SEED, **{})\n",
|
||||
"\n",
|
||||
"with warnings.catch_warnings():\n",
|
||||
" warnings.filterwarnings(\"ignore\", category=ConvergenceWarning)\n",
|
||||
" result = evaluate_model(\n",
|
||||
" model,\n",
|
||||
" split.X_train,\n",
|
||||
" split.X_test,\n",
|
||||
" split.y_train,\n",
|
||||
" split.y_test,\n",
|
||||
" model_name=MODEL_NAME,\n",
|
||||
" target_column=target_column,\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
"metrics = metrics_to_dict(result.metrics)\n",
|
||||
"metrics"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "25dafcbf",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## ?? ??"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "8357de5d",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"metrics_path = RESULTS_DIR / f\"{MODEL_NAME}_metrics.json\"\n",
|
||||
"predictions_path = PREDICTIONS_DIR / f\"{MODEL_NAME}_predictions.csv\"\n",
|
||||
"\n",
|
||||
"metrics_path.write_text(json.dumps(metrics, indent=2), encoding=\"utf-8\")\n",
|
||||
"result.predictions.to_csv(predictions_path, index=False)\n",
|
||||
"\n",
|
||||
"{\"metrics_path\": str(metrics_path), \"predictions_path\": str(predictions_path)}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "069f56dd",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## ?? plot"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "00159cc2",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"parity_fig = plot_parity(result.predictions, title=f\"{MODEL_NAME} parity\")\n",
|
||||
"residual_fig = plot_residuals(result.predictions, title=f\"{MODEL_NAME} residuals\")\n",
|
||||
"parity_fig.savefig(FIGURES_DIR / f\"{MODEL_NAME}_parity.png\", dpi=150, bbox_inches=\"tight\")\n",
|
||||
"residual_fig.savefig(FIGURES_DIR / f\"{MODEL_NAME}_residuals.png\", dpi=150, bbox_inches=\"tight\")\n",
|
||||
"parity_fig"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python",
|
||||
"pygments_lexer": "ipython3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "ad3ce4ad",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Gradient Boosting Surrogate\n",
|
||||
"\n",
|
||||
"Gradient Boosting? shallow tree? ????? ?? residual pattern? ??? ensemble??.\n",
|
||||
"\n",
|
||||
"?? ?? notebook? ?? dataset, target, train/test split seed? ????."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "0fec75b1",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import json\n",
|
||||
"import warnings\n",
|
||||
"from pathlib import Path\n",
|
||||
"\n",
|
||||
"import pandas as pd\n",
|
||||
"from sklearn.exceptions import ConvergenceWarning\n",
|
||||
"\n",
|
||||
"from femsurrogate.data.schema import DEFAULT_RANDOM_SEED, PARAMETER_COLUMNS\n",
|
||||
"from femsurrogate.plotting.diagnostics import plot_parity, plot_residuals\n",
|
||||
"from femsurrogate.surrogates.common import evaluate_model, metrics_to_dict, split_dataset\n",
|
||||
"from femsurrogate.surrogates.registry import make_model\n",
|
||||
"\n",
|
||||
"ROOT = Path.cwd().resolve()\n",
|
||||
"if not (ROOT / \"pyproject.toml\").exists():\n",
|
||||
" ROOT = ROOT.parent\n",
|
||||
"assert (ROOT / \"pyproject.toml\").exists(), ROOT\n",
|
||||
"DATASET_PATH = ROOT / \"data\" / \"reference\" / \"beam2d_lhs_300.csv\"\n",
|
||||
"RESULTS_DIR = ROOT / \"reports\" / \"results\"\n",
|
||||
"PREDICTIONS_DIR = ROOT / \"reports\" / \"predictions\"\n",
|
||||
"FIGURES_DIR = ROOT / \"reports\" / \"figures\"\n",
|
||||
"for directory in [RESULTS_DIR, PREDICTIONS_DIR, FIGURES_DIR]:\n",
|
||||
" directory.mkdir(parents=True, exist_ok=True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "1d93d9fb",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Dataset? split"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "c905b0aa",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"dataset = pd.read_csv(DATASET_PATH)\n",
|
||||
"target_column = \"tip_uy_m\"\n",
|
||||
"split = split_dataset(\n",
|
||||
" dataset,\n",
|
||||
" feature_columns=list(PARAMETER_COLUMNS),\n",
|
||||
" target_column=target_column,\n",
|
||||
" test_size=0.2,\n",
|
||||
" random_state=DEFAULT_RANDOM_SEED,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"len(split.X_train), len(split.X_test)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "c5e7af1a",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## ?? ??? ??"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "f5168f3e",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"MODEL_NAME = \"gradient_boosting\"\n",
|
||||
"model = make_model(MODEL_NAME, random_state=DEFAULT_RANDOM_SEED, **{})\n",
|
||||
"\n",
|
||||
"with warnings.catch_warnings():\n",
|
||||
" warnings.filterwarnings(\"ignore\", category=ConvergenceWarning)\n",
|
||||
" result = evaluate_model(\n",
|
||||
" model,\n",
|
||||
" split.X_train,\n",
|
||||
" split.X_test,\n",
|
||||
" split.y_train,\n",
|
||||
" split.y_test,\n",
|
||||
" model_name=MODEL_NAME,\n",
|
||||
" target_column=target_column,\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
"metrics = metrics_to_dict(result.metrics)\n",
|
||||
"metrics"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "f02aead6",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## ?? ??"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "1daaf0f6",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"metrics_path = RESULTS_DIR / f\"{MODEL_NAME}_metrics.json\"\n",
|
||||
"predictions_path = PREDICTIONS_DIR / f\"{MODEL_NAME}_predictions.csv\"\n",
|
||||
"\n",
|
||||
"metrics_path.write_text(json.dumps(metrics, indent=2), encoding=\"utf-8\")\n",
|
||||
"result.predictions.to_csv(predictions_path, index=False)\n",
|
||||
"\n",
|
||||
"{\"metrics_path\": str(metrics_path), \"predictions_path\": str(predictions_path)}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "14f71882",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## ?? plot"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "807c1025",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"parity_fig = plot_parity(result.predictions, title=f\"{MODEL_NAME} parity\")\n",
|
||||
"residual_fig = plot_residuals(result.predictions, title=f\"{MODEL_NAME} residuals\")\n",
|
||||
"parity_fig.savefig(FIGURES_DIR / f\"{MODEL_NAME}_parity.png\", dpi=150, bbox_inches=\"tight\")\n",
|
||||
"residual_fig.savefig(FIGURES_DIR / f\"{MODEL_NAME}_residuals.png\", dpi=150, bbox_inches=\"tight\")\n",
|
||||
"parity_fig"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python",
|
||||
"pygments_lexer": "ipython3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "69c9cc92",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# MLP Neural Network Surrogate\n",
|
||||
"\n",
|
||||
"MLP? scaled input? target? ??? ???? ??? ??? ????.\n",
|
||||
"\n",
|
||||
"?? ?? notebook? ?? dataset, target, train/test split seed? ????."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "eed089f5",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import json\n",
|
||||
"import warnings\n",
|
||||
"from pathlib import Path\n",
|
||||
"\n",
|
||||
"import pandas as pd\n",
|
||||
"from sklearn.exceptions import ConvergenceWarning\n",
|
||||
"\n",
|
||||
"from femsurrogate.data.schema import DEFAULT_RANDOM_SEED, PARAMETER_COLUMNS\n",
|
||||
"from femsurrogate.plotting.diagnostics import plot_parity, plot_residuals\n",
|
||||
"from femsurrogate.surrogates.common import evaluate_model, metrics_to_dict, split_dataset\n",
|
||||
"from femsurrogate.surrogates.registry import make_model\n",
|
||||
"\n",
|
||||
"ROOT = Path.cwd().resolve()\n",
|
||||
"if not (ROOT / \"pyproject.toml\").exists():\n",
|
||||
" ROOT = ROOT.parent\n",
|
||||
"assert (ROOT / \"pyproject.toml\").exists(), ROOT\n",
|
||||
"DATASET_PATH = ROOT / \"data\" / \"reference\" / \"beam2d_lhs_300.csv\"\n",
|
||||
"RESULTS_DIR = ROOT / \"reports\" / \"results\"\n",
|
||||
"PREDICTIONS_DIR = ROOT / \"reports\" / \"predictions\"\n",
|
||||
"FIGURES_DIR = ROOT / \"reports\" / \"figures\"\n",
|
||||
"for directory in [RESULTS_DIR, PREDICTIONS_DIR, FIGURES_DIR]:\n",
|
||||
" directory.mkdir(parents=True, exist_ok=True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "b2547495",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Dataset? split"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "039279d4",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"dataset = pd.read_csv(DATASET_PATH)\n",
|
||||
"target_column = \"tip_uy_m\"\n",
|
||||
"split = split_dataset(\n",
|
||||
" dataset,\n",
|
||||
" feature_columns=list(PARAMETER_COLUMNS),\n",
|
||||
" target_column=target_column,\n",
|
||||
" test_size=0.2,\n",
|
||||
" random_state=DEFAULT_RANDOM_SEED,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"len(split.X_train), len(split.X_test)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "71b3323f",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## ?? ??? ??"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "605253ef",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"MODEL_NAME = \"mlp\"\n",
|
||||
"model = make_model(MODEL_NAME, random_state=DEFAULT_RANDOM_SEED, **{'max_iter': 500})\n",
|
||||
"\n",
|
||||
"with warnings.catch_warnings():\n",
|
||||
" warnings.filterwarnings(\"ignore\", category=ConvergenceWarning)\n",
|
||||
" result = evaluate_model(\n",
|
||||
" model,\n",
|
||||
" split.X_train,\n",
|
||||
" split.X_test,\n",
|
||||
" split.y_train,\n",
|
||||
" split.y_test,\n",
|
||||
" model_name=MODEL_NAME,\n",
|
||||
" target_column=target_column,\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
"metrics = metrics_to_dict(result.metrics)\n",
|
||||
"metrics"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "f70ad9fd",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## ?? ??"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "fb08797b",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"metrics_path = RESULTS_DIR / f\"{MODEL_NAME}_metrics.json\"\n",
|
||||
"predictions_path = PREDICTIONS_DIR / f\"{MODEL_NAME}_predictions.csv\"\n",
|
||||
"\n",
|
||||
"metrics_path.write_text(json.dumps(metrics, indent=2), encoding=\"utf-8\")\n",
|
||||
"result.predictions.to_csv(predictions_path, index=False)\n",
|
||||
"\n",
|
||||
"{\"metrics_path\": str(metrics_path), \"predictions_path\": str(predictions_path)}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "39a34166",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## ?? plot"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "694a1081",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"parity_fig = plot_parity(result.predictions, title=f\"{MODEL_NAME} parity\")\n",
|
||||
"residual_fig = plot_residuals(result.predictions, title=f\"{MODEL_NAME} residuals\")\n",
|
||||
"parity_fig.savefig(FIGURES_DIR / f\"{MODEL_NAME}_parity.png\", dpi=150, bbox_inches=\"tight\")\n",
|
||||
"residual_fig.savefig(FIGURES_DIR / f\"{MODEL_NAME}_residuals.png\", dpi=150, bbox_inches=\"tight\")\n",
|
||||
"parity_fig"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python",
|
||||
"pygments_lexer": "ipython3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "741ede83",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# 06 Compare Surrogate Models\n",
|
||||
"\n",
|
||||
"? notebook? ?? model notebook?? ??? metrics JSON? ?? ??? ????. ??? ?? ???? ???."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "10f43a71",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import json\n",
|
||||
"from pathlib import Path\n",
|
||||
"\n",
|
||||
"import pandas as pd\n",
|
||||
"\n",
|
||||
"from femsurrogate.plotting.comparison import plot_metric_comparison\n",
|
||||
"\n",
|
||||
"ROOT = Path.cwd().resolve()\n",
|
||||
"if not (ROOT / \"pyproject.toml\").exists():\n",
|
||||
" ROOT = ROOT.parent\n",
|
||||
"assert (ROOT / \"pyproject.toml\").exists(), ROOT\n",
|
||||
"RESULTS_DIR = ROOT / \"reports\" / \"results\"\n",
|
||||
"FIGURES_DIR = ROOT / \"reports\" / \"figures\"\n",
|
||||
"FIGURES_DIR.mkdir(parents=True, exist_ok=True)\n",
|
||||
"MODEL_NAMES = [\"rsm\", \"gpr\", \"random_forest\", \"gradient_boosting\", \"mlp\"]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "9798e3bb",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Metrics ??"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "55d2447d",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"records = []\n",
|
||||
"for model_name in MODEL_NAMES:\n",
|
||||
" metrics_path = RESULTS_DIR / f\"{model_name}_metrics.json\"\n",
|
||||
" assert metrics_path.exists(), metrics_path\n",
|
||||
" records.append(json.loads(metrics_path.read_text(encoding=\"utf-8\")))\n",
|
||||
"\n",
|
||||
"comparison = pd.DataFrame(records).sort_values(\"rmse\").reset_index(drop=True)\n",
|
||||
"comparison_path = RESULTS_DIR / \"model_comparison.csv\"\n",
|
||||
"comparison.to_csv(comparison_path, index=False)\n",
|
||||
"comparison"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "731bb2f7",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Metric ?? plot"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "64657b84",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"figures = {}\n",
|
||||
"for metric in [\"rmse\", \"mae\", \"r2\", \"fit_time_s\", \"predict_time_s\"]:\n",
|
||||
" figure = plot_metric_comparison(comparison, metric=metric, title=f\"Surrogate {metric}\")\n",
|
||||
" figure.savefig(FIGURES_DIR / f\"comparison_{metric}.png\", dpi=150, bbox_inches=\"tight\")\n",
|
||||
" figures[metric] = figure\n",
|
||||
"\n",
|
||||
"figures[\"rmse\"]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "63cb5b8d",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## ?? ???\n",
|
||||
"\n",
|
||||
"- `rmse`? `mae`? ?? ??? ?? ????.\n",
|
||||
"- `r2`? ??? residual plot?? ?? ?? ??? bias? ??? ????? ????.\n",
|
||||
"- `fit_time_s`, `predict_time_s`? ?? ??? ??? loop?? ????.\n",
|
||||
"- GPR? ?? dataset?? ???? sample ?? ??? ?? ??? ??? ?? ? ??."
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python",
|
||||
"pygments_lexer": "ipython3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"project": "FEMSurrogateTutorial",
|
||||
"phase": "0-project-foundation",
|
||||
"steps": [
|
||||
{
|
||||
"step": 0,
|
||||
"name": "python-project-skeleton",
|
||||
"status": "completed",
|
||||
"summary": "Created uv/pyproject foundation, src package skeleton, artifact directories, and ruff configuration."
|
||||
},
|
||||
{
|
||||
"step": 1,
|
||||
"name": "import-smoke-test",
|
||||
"status": "completed",
|
||||
"summary": "Added package import smoke tests and exposed femsurrogate.__version__."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
# Step 0: python-project-skeleton
|
||||
|
||||
## 읽어야 할 파일
|
||||
|
||||
- `/AGENTS.md`
|
||||
- `/PROGRESS.md`
|
||||
- `/WORKNOTES.md`
|
||||
- `/docs/PRD.md`
|
||||
- `/docs/ARCHITECTURE.md`
|
||||
- `/docs/ADR.md`
|
||||
|
||||
## 작업
|
||||
|
||||
Python tutorial project의 최소 기반을 만든다.
|
||||
|
||||
- `/pyproject.toml` 생성: Python `>=3.12,<3.15`, setuptools src layout, pytest `pythonpath = ["src"]`, ruff target `py312`.
|
||||
- runtime dependencies: `numpy`, `scipy`, `pandas`, `scikit-learn`, `matplotlib`, `joblib`.
|
||||
- dev dependencies: `pytest`, `ruff`, `jupyterlab`, `ipykernel`, `nbconvert`.
|
||||
- Python package directories: `/src/femsurrogate/`, `/src/femsurrogate/fea/`, `/src/femsurrogate/data/`, `/src/femsurrogate/surrogates/`, `/src/femsurrogate/plotting/`.
|
||||
- artifact directories: `/tests/`, `/data/reference/`, `/data/processed/`, `/reports/results/`, `/reports/predictions/`, `/reports/figures/`, `/notebooks/`.
|
||||
- 빈 artifact directory에는 `.gitkeep`을 둔다.
|
||||
- Python cache, virtualenv, notebook checkpoint를 `.gitignore`에 추가한다.
|
||||
- `/PROGRESS.md`에 phase 0 시작 상태를 기록한다.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
```powershell
|
||||
uv sync
|
||||
uv run python -c "import femsurrogate; print(femsurrogate.__name__)"
|
||||
uv run ruff check .
|
||||
```
|
||||
|
||||
## 검증 절차
|
||||
|
||||
AC 커맨드를 실행하고 성공하면 `phases/0-project-foundation/index.json`의 step 0을 `completed`로 갱신한다.
|
||||
|
||||
## 금지사항
|
||||
|
||||
- Beam solver, parser, dataset, surrogate 모델 구현을 이 step에서 만들지 마라.
|
||||
- Notebook 내용을 만들지 마라.
|
||||
@@ -0,0 +1,36 @@
|
||||
# Step 1: import-smoke-test
|
||||
|
||||
## 읽어야 할 파일
|
||||
|
||||
- `/AGENTS.md`
|
||||
- `/PROGRESS.md`
|
||||
- `/WORKNOTES.md`
|
||||
- `/docs/ARCHITECTURE.md`
|
||||
- `/pyproject.toml`
|
||||
- `/src/femsurrogate/__init__.py`
|
||||
|
||||
## 작업
|
||||
|
||||
TDD로 package import smoke test를 추가한다.
|
||||
|
||||
- 먼저 `/tests/test_project_structure.py`를 작성한다.
|
||||
- 테스트는 `femsurrogate.__version__`이 문자열인지 확인한다.
|
||||
- 테스트는 `femsurrogate.fea`, `femsurrogate.data`, `femsurrogate.surrogates`, `femsurrogate.plotting` import 가능성을 확인한다.
|
||||
- 테스트 실패를 먼저 확인한 뒤 최소 구현으로 통과시킨다.
|
||||
- `/PROGRESS.md`에 검증 결과를 기록한다.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
```powershell
|
||||
uv run pytest tests/test_project_structure.py -q
|
||||
uv run ruff check .
|
||||
```
|
||||
|
||||
## 검증 절차
|
||||
|
||||
테스트가 먼저 실패한 기록을 확인하고, 구현 후 AC가 통과하면 `phases/0-project-foundation/index.json`의 step 1을 `completed`로 갱신한다.
|
||||
|
||||
## 금지사항
|
||||
|
||||
- solver API를 만들지 마라.
|
||||
- 테스트를 구현 후에만 작성하지 마라.
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"project": "FEMSurrogateTutorial",
|
||||
"phase": "1-beam2d-solver",
|
||||
"steps": [
|
||||
{
|
||||
"step": 0,
|
||||
"name": "beamexamples-parser",
|
||||
"status": "completed",
|
||||
"summary": "Added BeamExamples parser dataclasses and tests for cantilever input and displacement fixtures."
|
||||
},
|
||||
{
|
||||
"step": 1,
|
||||
"name": "frame-element",
|
||||
"status": "completed",
|
||||
"summary": "Added 2D Euler-Bernoulli frame local stiffness and transformation matrix tests and implementation."
|
||||
},
|
||||
{
|
||||
"step": 2,
|
||||
"name": "assembly-and-solver",
|
||||
"status": "completed",
|
||||
"summary": "Added sparse global stiffness assembly, load vector assembly, constrained DOF handling, and linear static solve."
|
||||
},
|
||||
{
|
||||
"step": 3,
|
||||
"name": "fixture-regression",
|
||||
"status": "completed",
|
||||
"summary": "Added BeamExamples displacement regression, analytical cantilever tip check, and clockwise-positive Rz convention alignment."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
# Step 0: beamexamples-parser
|
||||
|
||||
## 읽어야 할 파일
|
||||
|
||||
- `/AGENTS.md`
|
||||
- `/PROGRESS.md`
|
||||
- `/WORKNOTES.md`
|
||||
- `/docs/ARCHITECTURE.md`
|
||||
- `/BeamExamples/CantileverBeam.txt`
|
||||
- `/BeamExamples/CantileverBeam_Displacements.txt`
|
||||
|
||||
## 작업
|
||||
|
||||
TDD로 `BeamExamples` parser를 구현한다.
|
||||
|
||||
- `/tests/test_beamexamples_io.py`를 먼저 작성한다.
|
||||
- `read_beam_example()`은 metadata, 6 nodes, 5 beams, fixed node 1, node 6 load를 읽는다.
|
||||
- `read_expected_displacements()`는 node별 `Ux`, `Uy`, `Rz` 기준값을 읽는다.
|
||||
- parser는 comment, blank line, trailing comma, `Poisson'sRatio` label을 처리한다.
|
||||
- 구현 파일: `/src/femsurrogate/fea/model.py`, `/src/femsurrogate/fea/io.py`.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
```powershell
|
||||
uv run pytest tests/test_beamexamples_io.py -q
|
||||
uv run ruff check .
|
||||
```
|
||||
|
||||
## 금지사항
|
||||
|
||||
- solver 구현을 이 step에 넣지 마라.
|
||||
- fixture 파일을 수정하지 마라.
|
||||
@@ -0,0 +1,28 @@
|
||||
# Step 1: frame-element
|
||||
|
||||
## 읽어야 할 파일
|
||||
|
||||
- `/AGENTS.md`
|
||||
- `/docs/ARCHITECTURE.md`
|
||||
- `/src/femsurrogate/fea/model.py`
|
||||
- `/src/femsurrogate/fea/io.py`
|
||||
|
||||
## 작업
|
||||
|
||||
TDD로 2-node 2D Euler-Bernoulli frame element 행렬을 구현한다.
|
||||
|
||||
- `/tests/test_frame_element.py`를 먼저 작성한다.
|
||||
- `local_frame_stiffness(E, A, I, L)`의 shape, symmetry, axial/bending 계수를 검증한다.
|
||||
- `transformation_matrix(x1, y1, x2, y2)`의 shape와 orthogonality를 검증한다.
|
||||
- 구현 파일: `/src/femsurrogate/fea/element.py`.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
```powershell
|
||||
uv run pytest tests/test_frame_element.py tests/test_beamexamples_io.py -q
|
||||
uv run ruff check .
|
||||
```
|
||||
|
||||
## 금지사항
|
||||
|
||||
- Timoshenko beam, 3D beam DOF를 추가하지 마라.
|
||||
@@ -0,0 +1,31 @@
|
||||
# Step 2: assembly-and-solver
|
||||
|
||||
## 읽어야 할 파일
|
||||
|
||||
- `/AGENTS.md`
|
||||
- `/docs/ARCHITECTURE.md`
|
||||
- `/src/femsurrogate/fea/model.py`
|
||||
- `/src/femsurrogate/fea/io.py`
|
||||
- `/src/femsurrogate/fea/element.py`
|
||||
|
||||
## 작업
|
||||
|
||||
TDD로 global assembly와 constrained linear static solver를 구현한다.
|
||||
|
||||
- `/tests/test_beam_solver.py`를 먼저 작성한다.
|
||||
- BeamExamples model의 global stiffness shape `(18, 18)`을 검증한다.
|
||||
- fixed node 1의 3 DOF가 constrained 처리되는지 검증한다.
|
||||
- `solve_linear_static(model)`이 모든 node displacement를 finite 값으로 반환하는지 검증한다.
|
||||
- 구현 파일: `/src/femsurrogate/fea/assembly.py`, `/src/femsurrogate/fea/solver.py`.
|
||||
- solver는 `scipy.sparse`와 `scipy.sparse.linalg.spsolve`를 사용한다.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
```powershell
|
||||
uv run pytest tests/test_beam_solver.py tests/test_frame_element.py tests/test_beamexamples_io.py -q
|
||||
uv run ruff check .
|
||||
```
|
||||
|
||||
## 금지사항
|
||||
|
||||
- expected displacement 값을 solver에 hard-code하지 마라.
|
||||
@@ -0,0 +1,30 @@
|
||||
# Step 3: fixture-regression
|
||||
|
||||
## 읽어야 할 파일
|
||||
|
||||
- `/AGENTS.md`
|
||||
- `/docs/PRD.md`
|
||||
- `/docs/ARCHITECTURE.md`
|
||||
- `/BeamExamples/CantileverBeam.txt`
|
||||
- `/BeamExamples/CantileverBeam_Displacements.txt`
|
||||
- `/src/femsurrogate/fea/`
|
||||
|
||||
## 작업
|
||||
|
||||
TDD로 BeamExamples displacement regression을 완성한다.
|
||||
|
||||
- `/tests/test_cantilever_fixture_regression.py`를 먼저 작성한다.
|
||||
- solver 결과의 모든 node별 `Ux`, `Uy`, `Rz`를 기준 displacement file과 `atol=5e-7`, `rtol=1e-6`으로 비교한다.
|
||||
- tip displacement 부호와 해석해 `P L^3 / (3 E I)` 대비 크기를 검증한다.
|
||||
- 필요한 helper는 `/src/femsurrogate/fea/benchmark.py`, `/src/femsurrogate/fea/responses.py`에 둔다.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
```powershell
|
||||
uv run pytest tests/test_cantilever_fixture_regression.py tests/test_beam_solver.py tests/test_frame_element.py tests/test_beamexamples_io.py -q
|
||||
uv run ruff check .
|
||||
```
|
||||
|
||||
## 금지사항
|
||||
|
||||
- 허용오차를 문서 기준보다 느슨하게 키우지 마라.
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"project": "FEMSurrogateTutorial",
|
||||
"phase": "2-dataset-and-surrogates",
|
||||
"steps": [
|
||||
{
|
||||
"step": 0,
|
||||
"name": "sampling-and-dataset",
|
||||
"status": "completed",
|
||||
"summary": "Added reproducible LHS sampling, BeamParameters/AnalysisResult schema, and Beam2D dataset builder using the in-repository solver."
|
||||
},
|
||||
{
|
||||
"step": 1,
|
||||
"name": "surrogate-common",
|
||||
"status": "completed",
|
||||
"summary": "Added reproducible dataset split helper, evaluation result dataclasses, regression metrics, fit/predict timing, and prediction table generation."
|
||||
},
|
||||
{
|
||||
"step": 2,
|
||||
"name": "surrogate-models",
|
||||
"status": "completed",
|
||||
"summary": "Added scikit-learn builders and registry for RSM, GPR, Random Forest, Gradient Boosting, and MLP models."
|
||||
},
|
||||
{
|
||||
"step": 3,
|
||||
"name": "plotting-and-results",
|
||||
"status": "completed",
|
||||
"summary": "Added parity and residual diagnostic plots, metrics table creation, and metric comparison plot helpers returning matplotlib figures."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
# Step 0: sampling-and-dataset
|
||||
|
||||
## 읽어야 할 파일
|
||||
|
||||
- `/AGENTS.md`
|
||||
- `/docs/ARCHITECTURE.md`
|
||||
- `/docs/theory/01_doe_sampling_validation.md`
|
||||
- `/src/femsurrogate/fea/`
|
||||
|
||||
## 작업
|
||||
|
||||
TDD로 LHS sampling과 Beam2D dataset builder를 구현한다.
|
||||
|
||||
- 테스트: `/tests/test_sampling.py`, `/tests/test_dataset_builder.py`.
|
||||
- 구현: `/src/femsurrogate/data/bounds.py`, `schema.py`, `sampling.py`, `dataset.py`.
|
||||
- 기본 seed는 `20260521`.
|
||||
- dataset schema는 문서의 SI 단위 컬럼을 따른다.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
```powershell
|
||||
uv run pytest tests/test_sampling.py tests/test_dataset_builder.py -q
|
||||
uv run ruff check .
|
||||
```
|
||||
|
||||
## 금지사항
|
||||
|
||||
- surrogate 학습을 이 step에 넣지 마라.
|
||||
@@ -0,0 +1,26 @@
|
||||
# Step 1: surrogate-common
|
||||
|
||||
## 읽어야 할 파일
|
||||
|
||||
- `/AGENTS.md`
|
||||
- `/docs/ARCHITECTURE.md`
|
||||
- `/src/femsurrogate/data/`
|
||||
|
||||
## 작업
|
||||
|
||||
TDD로 surrogate 공통 split/evaluate/result helper를 구현한다.
|
||||
|
||||
- 테스트: `/tests/test_surrogate_common.py`.
|
||||
- 구현: `/src/femsurrogate/surrogates/common.py`.
|
||||
- metrics는 `rmse`, `mae`, `r2`, `fit_time_s`, `predict_time_s`를 포함한다.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
```powershell
|
||||
uv run pytest tests/test_surrogate_common.py -q
|
||||
uv run ruff check .
|
||||
```
|
||||
|
||||
## 금지사항
|
||||
|
||||
- 개별 모델 builder를 이 step에 섞지 마라.
|
||||
@@ -0,0 +1,30 @@
|
||||
# Step 2: surrogate-models
|
||||
|
||||
## 읽어야 할 파일
|
||||
|
||||
- `/AGENTS.md`
|
||||
- `/docs/theory/02_response_surface_methodology.md`
|
||||
- `/docs/theory/03_gaussian_process_kriging.md`
|
||||
- `/docs/theory/04_random_forest.md`
|
||||
- `/docs/theory/05_gradient_boosting.md`
|
||||
- `/docs/theory/06_mlp_neural_network.md`
|
||||
- `/src/femsurrogate/surrogates/common.py`
|
||||
|
||||
## 작업
|
||||
|
||||
TDD로 모델별 scikit-learn pipeline builder와 registry를 구현한다.
|
||||
|
||||
- 테스트: `/tests/test_surrogate_models.py`.
|
||||
- 구현: `rsm.py`, `gpr.py`, `random_forest.py`, `boosting.py`, `mlp.py`, `registry.py`.
|
||||
- 모델명: `rsm`, `gpr`, `random_forest`, `gradient_boosting`, `mlp`.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
```powershell
|
||||
uv run pytest tests/test_surrogate_models.py -q
|
||||
uv run ruff check .
|
||||
```
|
||||
|
||||
## 금지사항
|
||||
|
||||
- PyTorch/TensorFlow, AutoML, MLflow를 추가하지 마라.
|
||||
@@ -0,0 +1,26 @@
|
||||
# Step 3: plotting-and-results
|
||||
|
||||
## 읽어야 할 파일
|
||||
|
||||
- `/AGENTS.md`
|
||||
- `/docs/ARCHITECTURE.md`
|
||||
- `/src/femsurrogate/surrogates/`
|
||||
|
||||
## 작업
|
||||
|
||||
TDD로 diagnostics plotting과 result comparison helper를 구현한다.
|
||||
|
||||
- 테스트: `/tests/test_plotting_and_results.py`.
|
||||
- 구현: `/src/femsurrogate/plotting/diagnostics.py`, `/src/femsurrogate/plotting/comparison.py`.
|
||||
- plotting 함수는 matplotlib Figure를 반환한다.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
```powershell
|
||||
uv run pytest tests/test_plotting_and_results.py -q
|
||||
uv run ruff check .
|
||||
```
|
||||
|
||||
## 금지사항
|
||||
|
||||
- browser dashboard를 만들지 마라.
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"project": "FEMSurrogateTutorial",
|
||||
"phase": "3-notebooks-and-final-verification",
|
||||
"steps": [
|
||||
{
|
||||
"step": 0,
|
||||
"name": "dataset-notebook",
|
||||
"status": "completed",
|
||||
"summary": "Added and executed dataset notebook that validates BeamExamples, generates LHS samples, builds FEM dataset, and writes reference CSV/metadata."
|
||||
},
|
||||
{
|
||||
"step": 1,
|
||||
"name": "model-notebooks",
|
||||
"status": "completed",
|
||||
"summary": "Added and executed five model notebooks sharing the same dataset, target, and split seed while saving metrics, predictions, and diagnostic figures."
|
||||
},
|
||||
{
|
||||
"step": 2,
|
||||
"name": "comparison-notebook",
|
||||
"status": "completed",
|
||||
"summary": "Added and executed comparison notebook that reads saved metrics and writes model comparison CSV and figures without retraining."
|
||||
},
|
||||
{
|
||||
"step": 3,
|
||||
"name": "final-verification",
|
||||
"status": "completed",
|
||||
"summary": "Ran full pytest, ruff, all seven nbconvert executions, and verified comparison outputs exist."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
# Step 0: dataset-notebook
|
||||
|
||||
## 읽어야 할 파일
|
||||
|
||||
- `/AGENTS.md`
|
||||
- `/docs/PRD.md`
|
||||
- `/docs/ARCHITECTURE.md`
|
||||
- `/src/femsurrogate/`
|
||||
|
||||
## 작업
|
||||
|
||||
`notebooks/00_beam2d_fea_dataset.ipynb`를 만든다. Notebook은 BeamExamples regression 검증, LHS sampling, FEM batch run, dataset 저장을 설명하고 실행한다.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
```powershell
|
||||
uv run jupyter nbconvert --to notebook --execute notebooks/00_beam2d_fea_dataset.ipynb
|
||||
uv run ruff check .
|
||||
```
|
||||
|
||||
## 금지사항
|
||||
|
||||
- 핵심 solver 구현을 notebook cell에 넣지 마라.
|
||||
@@ -0,0 +1,39 @@
|
||||
# Step 1: model-notebooks
|
||||
|
||||
## 읽어야 할 파일
|
||||
|
||||
- `/AGENTS.md`
|
||||
- `/docs/theory/02_response_surface_methodology.md`
|
||||
- `/docs/theory/03_gaussian_process_kriging.md`
|
||||
- `/docs/theory/04_random_forest.md`
|
||||
- `/docs/theory/05_gradient_boosting.md`
|
||||
- `/docs/theory/06_mlp_neural_network.md`
|
||||
- `/src/femsurrogate/surrogates/`
|
||||
- `/src/femsurrogate/plotting/`
|
||||
|
||||
## 작업
|
||||
|
||||
모델별 notebook 5개를 만든다.
|
||||
|
||||
- `notebooks/01_response_surface_surrogate.ipynb`
|
||||
- `notebooks/02_gaussian_process_kriging_surrogate.ipynb`
|
||||
- `notebooks/03_random_forest_surrogate.ipynb`
|
||||
- `notebooks/04_gradient_boosting_surrogate.ipynb`
|
||||
- `notebooks/05_mlp_surrogate.ipynb`
|
||||
|
||||
각 notebook은 같은 dataset, target, split seed를 쓰고 metric JSON과 prediction CSV를 저장한다.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
```powershell
|
||||
uv run jupyter nbconvert --to notebook --execute notebooks/01_response_surface_surrogate.ipynb
|
||||
uv run jupyter nbconvert --to notebook --execute notebooks/02_gaussian_process_kriging_surrogate.ipynb
|
||||
uv run jupyter nbconvert --to notebook --execute notebooks/03_random_forest_surrogate.ipynb
|
||||
uv run jupyter nbconvert --to notebook --execute notebooks/04_gradient_boosting_surrogate.ipynb
|
||||
uv run jupyter nbconvert --to notebook --execute notebooks/05_mlp_surrogate.ipynb
|
||||
uv run ruff check .
|
||||
```
|
||||
|
||||
## 금지사항
|
||||
|
||||
- 모델별로 다른 split을 쓰지 마라.
|
||||
@@ -0,0 +1,24 @@
|
||||
# Step 2: comparison-notebook
|
||||
|
||||
## 읽어야 할 파일
|
||||
|
||||
- `/AGENTS.md`
|
||||
- `/docs/PRD.md`
|
||||
- `/reports/results/`
|
||||
- `/reports/predictions/`
|
||||
- `/src/femsurrogate/plotting/comparison.py`
|
||||
|
||||
## 작업
|
||||
|
||||
`notebooks/06_compare_surrogate_models.ipynb`를 만든다. 이전 notebook 결과물을 읽어 RMSE, MAE, R2, 학습 시간, 예측 시간을 비교하고 모델 선택 가이드를 정리한다.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
```powershell
|
||||
uv run jupyter nbconvert --to notebook --execute notebooks/06_compare_surrogate_models.ipynb
|
||||
uv run ruff check .
|
||||
```
|
||||
|
||||
## 금지사항
|
||||
|
||||
- 비교 notebook에서 모델을 다시 학습하지 마라.
|
||||
@@ -0,0 +1,37 @@
|
||||
# Step 3: final-verification
|
||||
|
||||
## 읽어야 할 파일
|
||||
|
||||
- `/AGENTS.md`
|
||||
- `/PROGRESS.md`
|
||||
- `/WORKNOTES.md`
|
||||
- `/docs/PRD.md`
|
||||
- `/docs/ARCHITECTURE.md`
|
||||
|
||||
## 작업
|
||||
|
||||
전체 검증과 handoff 문서 정리를 수행한다.
|
||||
|
||||
- `uv run pytest`
|
||||
- `uv run ruff check .`
|
||||
- 모든 notebook 순차 `nbconvert --execute`
|
||||
- `/PROGRESS.md` 최종 업데이트
|
||||
- `/WORKNOTES.md`에 남은 주의점 기록
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
```powershell
|
||||
uv run pytest
|
||||
uv run ruff check .
|
||||
uv run jupyter nbconvert --to notebook --execute notebooks/00_beam2d_fea_dataset.ipynb
|
||||
uv run jupyter nbconvert --to notebook --execute notebooks/01_response_surface_surrogate.ipynb
|
||||
uv run jupyter nbconvert --to notebook --execute notebooks/02_gaussian_process_kriging_surrogate.ipynb
|
||||
uv run jupyter nbconvert --to notebook --execute notebooks/03_random_forest_surrogate.ipynb
|
||||
uv run jupyter nbconvert --to notebook --execute notebooks/04_gradient_boosting_surrogate.ipynb
|
||||
uv run jupyter nbconvert --to notebook --execute notebooks/05_mlp_surrogate.ipynb
|
||||
uv run jupyter nbconvert --to notebook --execute notebooks/06_compare_surrogate_models.ipynb
|
||||
```
|
||||
|
||||
## 금지사항
|
||||
|
||||
- 실패한 검증을 성공으로 기록하지 마라.
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"phases": [
|
||||
{
|
||||
"dir": "0-project-foundation",
|
||||
"status": "completed"
|
||||
},
|
||||
{
|
||||
"dir": "1-beam2d-solver",
|
||||
"status": "completed"
|
||||
},
|
||||
{
|
||||
"dir": "2-dataset-and-surrogates",
|
||||
"status": "completed"
|
||||
},
|
||||
{
|
||||
"dir": "3-notebooks-and-final-verification",
|
||||
"status": "completed"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
[project]
|
||||
name = "femsurrogate-tutorial"
|
||||
version = "0.1.0"
|
||||
description = "Korean tutorial for FEM structural-analysis surrogate models."
|
||||
readme = "docs/PRD.md"
|
||||
requires-python = ">=3.12,<3.15"
|
||||
dependencies = [
|
||||
"joblib",
|
||||
"matplotlib",
|
||||
"numpy",
|
||||
"pandas",
|
||||
"scikit-learn",
|
||||
"scipy",
|
||||
]
|
||||
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"ipykernel",
|
||||
"jupyterlab",
|
||||
"nbconvert",
|
||||
"pytest",
|
||||
"ruff",
|
||||
]
|
||||
|
||||
[build-system]
|
||||
requires = ["setuptools>=69", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[tool.setuptools.packages.find]
|
||||
where = ["src"]
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
pythonpath = ["src"]
|
||||
testpaths = ["tests"]
|
||||
|
||||
[tool.ruff]
|
||||
target-version = "py312"
|
||||
line-length = 100
|
||||
extend-exclude = [
|
||||
".agents",
|
||||
".codex",
|
||||
"scripts/execute.py",
|
||||
]
|
||||
|
||||
[tool.ruff.lint]
|
||||
select = ["E", "F", "I", "UP", "B"]
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 37 KiB |
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
y_true,y_pred,residual
|
||||
-0.0098010662183848,-0.015250421684545492,0.005449355466160691
|
||||
-0.0038113194658788,-0.0017486039076851003,-0.0020627155581936997
|
||||
-0.0027310799449712,-0.00530647071310256,0.0025753907681313605
|
||||
-0.0100024342038305,-0.010316738327277896,0.00031430412344739603
|
||||
-0.0499253522341076,-0.04382392281863989,-0.006101429415467707
|
||||
-0.001425202073237,0.005078412534373628,-0.006503614607610628
|
||||
-0.0082152702385898,-0.009571524248486946,0.0013562540098971468
|
||||
-0.030294940628359,-0.02508246148372859,-0.005212479144630411
|
||||
-0.0003394669209404,-0.0023962303315858673,0.0020567634106454674
|
||||
-0.0104094868935342,-0.017755890098581213,0.007346403205047012
|
||||
-0.0007458704516273,-0.001982076369748365,0.0012362059181210648
|
||||
-0.0019926250153884,-0.003373505311388497,0.0013808802960000974
|
||||
-0.0067094522111387,-0.005789165245259511,-0.0009202869658791885
|
||||
-0.0007713230369176,0.0012402266579821346,-0.0020115496948997346
|
||||
-0.0009264786171953,0.0010793023104413834,-0.002005780927636683
|
||||
-0.0048682939239769,-0.010842246768661199,0.0059739528446842985
|
||||
-0.0015514429803847,-0.005987417572614714,0.004435974592230015
|
||||
-0.0082797555491993,-0.006796396801820442,-0.0014833587473788582
|
||||
-0.0067435512728419,0.00680790397400651,-0.01355145524684841
|
||||
-0.0002679427721747,0.0006763820975785838,-0.0009443248697532838
|
||||
-0.0117102395891512,0.0006489142032870647,-0.012359153792438266
|
||||
-0.0038564225187403,-0.0035722131493459413,-0.00028420936939435853
|
||||
-0.0043643611560866,-0.002491745084681229,-0.0018726160714053709
|
||||
-0.00690334164553,0.00027024498753304947,-0.007173586633063049
|
||||
-0.0032694837296735,-0.003198392657812337,-7.109107186116298e-05
|
||||
-0.0065802327277111,-0.006997526861566011,0.000417294133854911
|
||||
-0.0057609873946602,-0.008005191210867332,0.0022442038162071324
|
||||
-0.0978259601841187,-0.10476466176020587,0.0069387015760871695
|
||||
-0.0012815473411189,0.005795482786457884,-0.007077030127576784
|
||||
-0.2116989856844677,-0.1421513721299082,-0.06954761355455952
|
||||
-0.0005873182212224,0.003556659925605549,-0.004143978146827949
|
||||
-0.0102808405040129,-0.013683666669211234,0.0034028261651983336
|
||||
-0.0274808302842225,-0.023811924704933395,-0.0036689055792891064
|
||||
-0.0055843874647924,-0.004446019039931307,-0.0011383684248610932
|
||||
-0.0936698604467179,-0.08476905261398741,-0.008900807832730492
|
||||
-0.0042383507360532,-0.0028395908107003084,-0.0013987599253528918
|
||||
-0.0064139601803737,-0.002776365124053777,-0.003637595056319923
|
||||
-0.0500503637977256,-0.04468561411923253,-0.005364749678493069
|
||||
-0.0032551040880256,-0.007385900526260484,0.004130796438234884
|
||||
-0.0006720203919551,-0.0017365014801694594,0.0010644810882143594
|
||||
-0.0140013434972624,-0.018657807128895445,0.004656463631633044
|
||||
-0.0136746738089856,-0.01026732343038411,-0.0034073503786014904
|
||||
-0.0029028424886419,-0.006199045083251903,0.003296202594610003
|
||||
-0.0002977300790917,-0.003466576745293704,0.003168846666202004
|
||||
-0.0004013312796635,0.006938137232452899,-0.0073394685121164
|
||||
-0.001387036808518,-0.002671626354811536,0.0012845895462935358
|
||||
-0.0032416418934662,-2.721371817649039e-05,-0.0032144281752897095
|
||||
-0.0012865758997511,-0.004382274888399403,0.003095698988648303
|
||||
-0.000781489181828,0.014354677949252189,-0.015136167131080189
|
||||
-0.0282740230807759,-0.024446790443019183,-0.0038272326377567153
|
||||
-0.0014462597918054,-0.0025761975119785365,0.0011299377201731365
|
||||
-0.0009190994374579,-0.0015752616401020422,0.0006561622026441422
|
||||
-0.0024183380949151,0.001816942808085446,-0.004235280903000546
|
||||
-0.0080349039892428,-0.008306703776443457,0.00027179978720065674
|
||||
-0.0317498587484236,-0.026193372689921272,-0.005556486058502327
|
||||
-0.0028680506091303,-0.006249707444856041,0.003381656835725741
|
||||
-0.0327551701410487,-0.032500451863128575,-0.0002547182779201254
|
||||
-0.0002898009271785,-0.004854942238588591,0.00456514131141009
|
||||
-0.0049641733799332,-0.008914425892218376,0.003950252512285176
|
||||
-0.0005541404200699,0.0025102185642642497,-0.00306435898433415
|
||||
|
@@ -0,0 +1,61 @@
|
||||
y_true,y_pred,residual
|
||||
-0.0098010662183848,-0.011534131293699368,0.0017330650753145677
|
||||
-0.0038113194658788,-0.004566781630798601,0.0007554621649198006
|
||||
-0.0027310799449712,-0.002569043760932535,-0.0001620361840386649
|
||||
-0.0100024342038305,-0.008113001885148158,-0.0018894323186823426
|
||||
-0.0499253522341076,-0.05179401032427151,0.0018686580901639063
|
||||
-0.001425202073237,-0.0027429307062308422,0.0013177286329938422
|
||||
-0.0082152702385898,-0.005244392438185645,-0.002970877800404154
|
||||
-0.030294940628359,-0.026941379068354337,-0.003353561560004662
|
||||
-0.0003394669209404,0.0011920263930887208,-0.0015314933140291207
|
||||
-0.0104094868935342,-0.02009314147825499,0.009683654584720789
|
||||
-0.0007458704516273,-0.0006783343667398965,-6.753608488740351e-05
|
||||
-0.0019926250153884,-0.0035238050937769078,0.001531180078388508
|
||||
-0.0067094522111387,0.0045098371679258165,-0.011219289379064516
|
||||
-0.0007713230369176,0.0009525966890204713,-0.0017239197259380713
|
||||
-0.0009264786171953,-0.0010291487366641794,0.00010267011946887947
|
||||
-0.0048682939239769,-0.008554456850656682,0.003686162926679782
|
||||
-0.0015514429803847,-0.003701977800637113,0.002150534820252413
|
||||
-0.0082797555491993,-0.010581044499564738,0.0023012889503654377
|
||||
-0.0067435512728419,-0.03214833568118499,0.02540478440834309
|
||||
-0.0002679427721747,0.0012861204508787876,-0.0015540632230534876
|
||||
-0.0117102395891512,-0.011566111843574601,-0.00014412774557659956
|
||||
-0.0038564225187403,-0.007016644417818661,0.0031602218990783608
|
||||
-0.0043643611560866,-0.0037020847290705405,-0.0006622764270160595
|
||||
-0.00690334164553,-0.006957646097004912,5.430445147491282e-05
|
||||
-0.0032694837296735,-0.008033853112083723,0.0047643693824102225
|
||||
-0.0065802327277111,-0.0029191448483979277,-0.003661087879313172
|
||||
-0.0057609873946602,-0.005458264133573717,-0.0003027232610864827
|
||||
-0.0978259601841187,-0.07722849901007466,-0.02059746117404404
|
||||
-0.0012815473411189,-0.0006360341549186051,-0.0006455131862002948
|
||||
-0.2116989856844677,-0.08524519557269143,-0.12645379011177627
|
||||
-0.0005873182212224,0.00015120038122322824,-0.0007385186024456282
|
||||
-0.0102808405040129,-0.013413515680865662,0.0031326751768527613
|
||||
-0.0274808302842225,-0.021333224751441966,-0.006147605532780535
|
||||
-0.0055843874647924,-0.004638268193139127,-0.0009461192716532733
|
||||
-0.0936698604467179,-0.10583589880087134,0.012166038354153433
|
||||
-0.0042383507360532,-0.006994619539973823,0.002756268803920623
|
||||
-0.0064139601803737,-0.00721593555420163,0.0008019753738279295
|
||||
-0.0500503637977256,-0.029980393421298147,-0.020069970376427455
|
||||
-0.0032551040880256,-0.004159641884839451,0.0009045377968138514
|
||||
-0.0006720203919551,0.000695861307391559,-0.001367881699346659
|
||||
-0.0140013434972624,-0.012330661500693965,-0.0016706819965684357
|
||||
-0.0136746738089856,-0.01605569638030974,0.002381022571324139
|
||||
-0.0029028424886419,-0.0023341567334053885,-0.0005686857552365115
|
||||
-0.0002977300790917,0.0018160654458535613,-0.002113795524945261
|
||||
-0.0004013312796635,0.000462792310109772,-0.000864123589773272
|
||||
-0.001387036808518,-0.0002553521012893653,-0.0011316847072286348
|
||||
-0.0032416418934662,-0.0034662863630390488,0.0002246444695728489
|
||||
-0.0012865758997511,-0.001419939312063704,0.00013336341231260382
|
||||
-0.000781489181828,0.0004714048460805055,-0.0012528940279085054
|
||||
-0.0282740230807759,-0.01656459879962216,-0.011709424281153739
|
||||
-0.0014462597918054,-0.001017996892076924,-0.00042826289972847597
|
||||
-0.0009190994374579,-0.0030369363287297622,0.0021178368912718624
|
||||
-0.0024183380949151,-0.0027257756235102245,0.0003074375285951245
|
||||
-0.0080349039892428,-0.013922103226463197,0.005887199237220397
|
||||
-0.0317498587484236,-0.04130074037606117,0.009550881627637571
|
||||
-0.0028680506091303,-0.004243174200819291,0.0013751235916889906
|
||||
-0.0327551701410487,-0.023406512064579634,-0.009348658076469066
|
||||
-0.0002898009271785,-0.0003650281642084131,7.522723702991309e-05
|
||||
-0.0049641733799332,-0.005214160126186096,0.00024998674625289607
|
||||
-0.0005541404200699,0.0014239990341411483,-0.0019781394542110484
|
||||
|
@@ -0,0 +1,61 @@
|
||||
y_true,y_pred,residual
|
||||
-0.0098010662183848,-0.012802577507486139,0.0030015112891013385
|
||||
-0.0038113194658788,-0.0046515848170360155,0.0008402653511572155
|
||||
-0.0027310799449712,-0.010083004760998512,0.007351924816027313
|
||||
-0.0100024342038305,-0.020845283706657287,0.010842849502826787
|
||||
-0.0499253522341076,-0.040221134519255825,-0.009704217714851776
|
||||
-0.001425202073237,-0.009762081791064406,0.008336879717827405
|
||||
-0.0082152702385898,-0.016983972468944918,0.008768702230355118
|
||||
-0.030294940628359,-0.038763408487603644,0.008468467859244645
|
||||
-0.0003394669209404,0.0035130197140065145,-0.0038524866349469145
|
||||
-0.0104094868935342,-0.022486601392994278,0.012077114499460077
|
||||
-0.0007458704516273,0.008186185488004654,-0.008932055939631955
|
||||
-0.0019926250153884,-0.0024680978973060427,0.00047547288191764287
|
||||
-0.0067094522111387,-0.00822713371747703,0.0015176815063383305
|
||||
-0.0007713230369176,0.00270054572699867,-0.00347186876391627
|
||||
-0.0009264786171953,0.010416977768376428,-0.011343456385571727
|
||||
-0.0048682939239769,-0.011506092562164864,0.006637798638187964
|
||||
-0.0015514429803847,-0.009492646984972703,0.007941204004588002
|
||||
-0.0082797555491993,-0.021706863748004304,0.013427108198805004
|
||||
-0.0067435512728419,-0.024564514477627017,0.017820963204785118
|
||||
-0.0002679427721747,0.003440036938729933,-0.003707979710904633
|
||||
-0.0117102395891512,-0.01948773259389725,0.007777493004746051
|
||||
-0.0038564225187403,-0.009001834137222772,0.005145411618482472
|
||||
-0.0043643611560866,-0.005807071720011332,0.0014427105639247323
|
||||
-0.00690334164553,-0.012051489346343392,0.005148147700813392
|
||||
-0.0032694837296735,-0.014340233862364537,0.011070750132691036
|
||||
-0.0065802327277111,-0.023390501917222473,0.016810269189511375
|
||||
-0.0057609873946602,-0.009722949790975566,0.003961962396315366
|
||||
-0.0978259601841187,-0.04217063129833486,-0.05565532888578384
|
||||
-0.0012815473411189,0.0006427388868981282,-0.001924286228017028
|
||||
-0.2116989856844677,-0.03964152265257154,-0.17205746303189617
|
||||
-0.0005873182212224,0.013045438142832844,-0.013632756364055244
|
||||
-0.0102808405040129,-0.014460966326431212,0.004180125822418312
|
||||
-0.0274808302842225,-0.020779704750440366,-0.006701125533782135
|
||||
-0.0055843874647924,-0.011569739698444227,0.005985352233651826
|
||||
-0.0936698604467179,-0.04673036973556968,-0.046939490711148224
|
||||
-0.0042383507360532,-0.015102657835677205,0.010864307099624004
|
||||
-0.0064139601803737,-0.021855516256323176,0.015441556075949476
|
||||
-0.0500503637977256,-0.04096821507960851,-0.009082148718117092
|
||||
-0.0032551040880256,-0.009473373379514092,0.006218269291488492
|
||||
-0.0006720203919551,0.008151921529756209,-0.008823941921711308
|
||||
-0.0140013434972624,-0.027026365760490362,0.013025022263227961
|
||||
-0.0136746738089856,-0.018268174014002526,0.004593500205016926
|
||||
-0.0029028424886419,-0.005384453894577489,0.0024816114059355893
|
||||
-0.0002977300790917,0.01502494424731751,-0.01532267432640921
|
||||
-0.0004013312796635,0.008176262367795053,-0.008577593647458553
|
||||
-0.001387036808518,-0.0039136142664811455,0.002526577457963145
|
||||
-0.0032416418934662,-0.0005496866955882595,-0.0026919551978779404
|
||||
-0.0012865758997511,-0.0015485890387889768,0.00026201313903787666
|
||||
-0.000781489181828,0.00021154768682392702,-0.000993036868651927
|
||||
-0.0282740230807759,-0.0361710784496896,0.007897055368913702
|
||||
-0.0014462597918054,0.0010447393076983297,-0.0024909990995037297
|
||||
-0.0009190994374579,0.0005076838393030369,-0.001426783276760937
|
||||
-0.0024183380949151,-0.005814615168676113,0.0033962770737610134
|
||||
-0.0080349039892428,-0.017259202204240334,0.009224298214997534
|
||||
-0.0317498587484236,-0.01979978011856444,-0.01195007862985916
|
||||
-0.0028680506091303,-0.0025464659000482004,-0.00032158470908209965
|
||||
-0.0327551701410487,-0.02888314308530364,-0.0038720270557450615
|
||||
-0.0002898009271785,0.004474162373067482,-0.004763963300245982
|
||||
-0.0049641733799332,-0.009564500114358745,0.004600326734425545
|
||||
-0.0005541404200699,-0.008223453061769786,0.007669312641699886
|
||||
|
@@ -0,0 +1,61 @@
|
||||
y_true,y_pred,residual
|
||||
-0.0098010662183848,-0.013052973102582651,0.003251906884197851
|
||||
-0.0038113194658788,-0.0030832217758694816,-0.0007280976900093184
|
||||
-0.0027310799449712,-0.003443617119201804,0.0007125371742306044
|
||||
-0.0100024342038305,-0.006389981087977729,-0.0036124531158527717
|
||||
-0.0499253522341076,-0.015352738964269329,-0.03457261326983827
|
||||
-0.001425202073237,-0.018459481777023584,0.017034279703786584
|
||||
-0.0082152702385898,-0.004456616612166581,-0.003758653626423218
|
||||
-0.030294940628359,-0.0233835822329406,-0.006911358395418399
|
||||
-0.0003394669209404,-0.001335100166637542,0.000995633245697142
|
||||
-0.0104094868935342,-0.029524370191124354,0.019114883297590153
|
||||
-0.0007458704516273,-0.0010697310437712045,0.00032386059214390447
|
||||
-0.0019926250153884,-0.0020536291218114233,6.1004106423023525e-05
|
||||
-0.0067094522111387,-0.014557394629973457,0.007847942418834757
|
||||
-0.0007713230369176,-0.0012499540750173254,0.0004786310380997254
|
||||
-0.0009264786171953,-0.0012898747386864487,0.0003633961214911487
|
||||
-0.0048682939239769,-0.010281716791039721,0.005413422867062821
|
||||
-0.0015514429803847,-0.006064928713181127,0.004513485732796427
|
||||
-0.0082797555491993,-0.01020851915611972,0.0019287636069204202
|
||||
-0.0067435512728419,-0.05887513385526359,0.05213158258242169
|
||||
-0.0002679427721747,-0.0014368826831020398,0.0011689399109273398
|
||||
-0.0117102395891512,-0.00897904191064286,-0.002731197678508341
|
||||
-0.0038564225187403,-0.007064039332542149,0.0032076168138018488
|
||||
-0.0043643611560866,-0.0038693413391170145,-0.0004950198169695855
|
||||
-0.00690334164553,-0.0038950530678037608,-0.003008288577726239
|
||||
-0.0032694837296735,-0.006531726228139853,0.003262242498466353
|
||||
-0.0065802327277111,-0.01746332239669454,0.010883089668983442
|
||||
-0.0057609873946602,-0.0042079257092469515,-0.0015530616854132484
|
||||
-0.0978259601841187,-0.06427351865985945,-0.03355244152425925
|
||||
-0.0012815473411189,-0.00131837298277374,3.6825641654840034e-05
|
||||
-0.2116989856844677,-0.07299885186088179,-0.13870013382358592
|
||||
-0.0005873182212224,-0.0008764204060906751,0.00028910218486827516
|
||||
-0.0102808405040129,-0.025656683515613517,0.015375843011600616
|
||||
-0.0274808302842225,-0.0233500781934777,-0.004130752090744802
|
||||
-0.0055843874647924,-0.00360254423169254,-0.0019818432330998602
|
||||
-0.0936698604467179,-0.06835084136803436,-0.025319019078683544
|
||||
-0.0042383507360532,-0.008322436707289365,0.004084085971236165
|
||||
-0.0064139601803737,-0.007978798272136524,0.0015648380917628237
|
||||
-0.0500503637977256,-0.029134230073410976,-0.020916133724314626
|
||||
-0.0032551040880256,-0.0038575984824836713,0.0006024943944580715
|
||||
-0.0006720203919551,-0.0008321139785327232,0.0001600935865776232
|
||||
-0.0140013434972624,-0.009220030597946776,-0.004781312899315625
|
||||
-0.0136746738089856,-0.012918969755213044,-0.000755704053772557
|
||||
-0.0029028424886419,-0.007061520705634288,0.004158678216992388
|
||||
-0.0002977300790917,-0.0008244792974268024,0.0005267492183351024
|
||||
-0.0004013312796635,-0.0006335738388530976,0.00023224255918959758
|
||||
-0.001387036808518,-0.0022687878733974104,0.0008817510648794103
|
||||
-0.0032416418934662,-0.0031708494223524506,-7.079247111374922e-05
|
||||
-0.0012865758997511,-0.0020774063656214905,0.0007908304658703904
|
||||
-0.000781489181828,-0.004022832924982734,0.003241343743154734
|
||||
-0.0282740230807759,-0.023201264759851178,-0.005072758320924721
|
||||
-0.0014462597918054,-0.0021273945521054927,0.0006811347603000928
|
||||
-0.0009190994374579,-0.0011891624177935518,0.0002700629803356518
|
||||
-0.0024183380949151,-0.002935912586886879,0.0005175744919717791
|
||||
-0.0080349039892428,-0.012493932904864844,0.004459028915622044
|
||||
-0.0317498587484236,-0.02917403551090198,-0.0025758232375216183
|
||||
-0.0028680506091303,-0.002477127846164984,-0.00039092276296531607
|
||||
-0.0327551701410487,-0.020574378594002435,-0.012180791547046266
|
||||
-0.0002898009271785,-0.0030515507112431066,0.0027617497840646066
|
||||
-0.0049641733799332,-0.005639149529924011,0.0006749761499908113
|
||||
-0.0005541404200699,-0.0033415931535627887,0.0027874527334928885
|
||||
|
@@ -0,0 +1,61 @@
|
||||
y_true,y_pred,residual
|
||||
-0.0098010662183848,-0.008063708243651395,-0.0017373579747334052
|
||||
-0.0038113194658788,-0.0036842415434730005,-0.0001270779224057995
|
||||
-0.0027310799449712,0.0015855198478710482,-0.0043165997928422475
|
||||
-0.0100024342038305,-0.020768772136692537,0.010766337932862037
|
||||
-0.0499253522341076,-0.08634072145899263,0.03641536922488503
|
||||
-0.001425202073237,0.009057330651376155,-0.010482532724613155
|
||||
-0.0082152702385898,-0.01623359805579585,0.00801832781720605
|
||||
-0.030294940628359,-0.05494634739863705,0.024651406770278047
|
||||
-0.0003394669209404,0.0003057217392075026,-0.0006451886601479026
|
||||
-0.0104094868935342,-0.025186470928988097,0.014776984035453897
|
||||
-0.0007458704516273,0.005123479401716775,-0.0058693498533440745
|
||||
-0.0019926250153884,0.006483277853635958,-0.008475902869024358
|
||||
-0.0067094522111387,-0.004951368629097145,-0.0017580835820415544
|
||||
-0.0007713230369176,0.002431470422207911,-0.003202793459125511
|
||||
-0.0009264786171953,0.002098379150773981,-0.0030248577679692808
|
||||
-0.0048682939239769,0.0023269502345097445,-0.007195244158486645
|
||||
-0.0015514429803847,0.0054304376400158855,-0.006981880620400585
|
||||
-0.0082797555491993,-0.011235438686456989,0.0029556831372576885
|
||||
-0.0067435512728419,-0.01513640141753062,0.00839285014468872
|
||||
-0.0002679427721747,0.005023227034898421,-0.005291169807073121
|
||||
-0.0117102395891512,-0.021353175562886392,0.009642935973735191
|
||||
-0.0038564225187403,0.0024523028133941953,-0.006308725332134495
|
||||
-0.0043643611560866,0.0018118523029535282,-0.006176213459040129
|
||||
-0.00690334164553,-0.011090572616019383,0.004187230970489383
|
||||
-0.0032694837296735,-0.0010327942129321746,-0.0022366895167413255
|
||||
-0.0065802327277111,-0.010449659009701765,0.0038694262819906657
|
||||
-0.0057609873946602,-0.004202124085317911,-0.0015588633093422885
|
||||
-0.0978259601841187,-0.07849009051035474,-0.019335869673763953
|
||||
-0.0012815473411189,0.0045897836777313485,-0.005871331018850248
|
||||
-0.2116989856844677,-0.11236685970490985,-0.09933212597955786
|
||||
-0.0005873182212224,-0.0038346423165307834,0.0032473240953083834
|
||||
-0.0102808405040129,-0.011514380677840113,0.001233540173827213
|
||||
-0.0274808302842225,-0.03997989882186454,0.01249906853764204
|
||||
-0.0055843874647924,-0.007827865894016085,0.0022434784292236845
|
||||
-0.0936698604467179,-0.07675546207236617,-0.01691439837435174
|
||||
-0.0042383507360532,0.0005673146824279073,-0.004805665418481108
|
||||
-0.0064139601803737,-0.011035615882888063,0.004621655702514363
|
||||
-0.0500503637977256,-0.07016141645387532,0.020111052656149715
|
||||
-0.0032551040880256,0.00548188417318066,-0.008736988261206259
|
||||
-0.0006720203919551,0.0009801902763272793,-0.0016522106682823794
|
||||
-0.0140013434972624,-0.025223749239703955,0.011222405742441554
|
||||
-0.0136746738089856,-0.033649129185114264,0.019974455376128665
|
||||
-0.0029028424886419,0.0016175132552921486,-0.004520355743934048
|
||||
-0.0002977300790917,-0.012343066428881081,0.012045336349789382
|
||||
-0.0004013312796635,-0.0019906728761898054,0.0015893415965263053
|
||||
-0.001387036808518,0.014394621344358304,-0.015781658152876302
|
||||
-0.0032416418934662,-0.0017551803377404158,-0.001486461555725784
|
||||
-0.0012865758997511,0.011086934940341958,-0.012373510840093058
|
||||
-0.000781489181828,0.009985914013305923,-0.010767403195133923
|
||||
-0.0282740230807759,-0.03956474679869234,0.01129072371791644
|
||||
-0.0014462597918054,0.0017128674505823053,-0.0031591272423877052
|
||||
-0.0009190994374579,0.008880594747871942,-0.009799694185329841
|
||||
-0.0024183380949151,0.006026571466815343,-0.008444909561730444
|
||||
-0.0080349039892428,-0.012179467818523977,0.004144563829281177
|
||||
-0.0317498587484236,-0.03552887414112901,0.0037790153927054135
|
||||
-0.0028680506091303,0.0019507691371565064,-0.004818819746286807
|
||||
-0.0327551701410487,-0.06483928482330545,0.03208411468225675
|
||||
-0.0002898009271785,0.0050500589361993,-0.0053398598633778
|
||||
-0.0049641733799332,-0.001537216194153125,-0.003426957185780075
|
||||
-0.0005541404200699,0.015848580403955245,-0.016402720824025146
|
||||
|
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"model_name": "gpr",
|
||||
"target_column": "tip_uy_m",
|
||||
"rmse": 0.0102275703149458,
|
||||
"mae": 0.004887524852501009,
|
||||
"r2": 0.8984149766904297,
|
||||
"fit_time_s": 0.15534250007476658,
|
||||
"predict_time_s": 0.0016494999872520566
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"model_name": "gradient_boosting",
|
||||
"target_column": "tip_uy_m",
|
||||
"rmse": 0.017503204254034725,
|
||||
"mae": 0.0056308989929006875,
|
||||
"r2": 0.7024774700809826,
|
||||
"fit_time_s": 0.4196564999874681,
|
||||
"predict_time_s": 0.0021611000411212444
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"model_name": "mlp",
|
||||
"target_column": "tip_uy_m",
|
||||
"rmse": 0.02540606802811113,
|
||||
"mae": 0.011091159933185777,
|
||||
"r2": 0.3731557450209999,
|
||||
"fit_time_s": 0.114232899970375,
|
||||
"predict_time_s": 0.0020921999821439385
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
model_name,target_column,rmse,mae,r2,fit_time_s,predict_time_s
|
||||
gpr,tip_uy_m,0.0102275703149458,0.004887524852501009,0.8984149766904297,0.15534250007476658,0.0016494999872520566
|
||||
rsm,tip_uy_m,0.01692421409745107,0.00986868711451997,0.7218354651088679,0.008010599995031953,0.0015782000264152884
|
||||
gradient_boosting,tip_uy_m,0.017503204254034725,0.0056308989929006875,0.7024774700809826,0.4196564999874681,0.0021611000411212444
|
||||
random_forest,tip_uy_m,0.021182094831369028,0.008076487480895672,0.5642648925820651,0.6153512999881059,0.08771130000241101
|
||||
mlp,tip_uy_m,0.02540606802811113,0.011091159933185777,0.3731557450209999,0.114232899970375,0.0020921999821439385
|
||||
|
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"model_name": "random_forest",
|
||||
"target_column": "tip_uy_m",
|
||||
"rmse": 0.021182094831369028,
|
||||
"mae": 0.008076487480895672,
|
||||
"r2": 0.5642648925820651,
|
||||
"fit_time_s": 0.6153512999881059,
|
||||
"predict_time_s": 0.08771130000241101
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"model_name": "rsm",
|
||||
"target_column": "tip_uy_m",
|
||||
"rmse": 0.01692421409745107,
|
||||
"mae": 0.00986868711451997,
|
||||
"r2": 0.7218354651088679,
|
||||
"fit_time_s": 0.008010599995031953,
|
||||
"predict_time_s": 0.0015782000264152884
|
||||
}
|
||||
@@ -0,0 +1,417 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Harness Step Executor — phase 내 step을 순차 실행하고 자가 교정한다.
|
||||
|
||||
Usage:
|
||||
python scripts/execute.py <phase-dir> [--push]
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import contextlib
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
import types
|
||||
from datetime import datetime, timezone, timedelta
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def progress_indicator(label: str):
|
||||
"""터미널 진행 표시기. with 문으로 사용하며 .elapsed 로 경과 시간을 읽는다."""
|
||||
frames = "◐◓◑◒"
|
||||
stop = threading.Event()
|
||||
t0 = time.monotonic()
|
||||
|
||||
def _animate():
|
||||
idx = 0
|
||||
while not stop.wait(0.12):
|
||||
sec = int(time.monotonic() - t0)
|
||||
sys.stderr.write(f"\r{frames[idx % len(frames)]} {label} [{sec}s]")
|
||||
sys.stderr.flush()
|
||||
idx += 1
|
||||
sys.stderr.write("\r" + " " * (len(label) + 20) + "\r")
|
||||
sys.stderr.flush()
|
||||
|
||||
th = threading.Thread(target=_animate, daemon=True)
|
||||
th.start()
|
||||
info = types.SimpleNamespace(elapsed=0.0)
|
||||
try:
|
||||
yield info
|
||||
finally:
|
||||
stop.set()
|
||||
th.join()
|
||||
info.elapsed = time.monotonic() - t0
|
||||
|
||||
|
||||
class StepExecutor:
|
||||
"""Phase 디렉토리 안의 step들을 순차 실행하는 하네스."""
|
||||
|
||||
MAX_RETRIES = 3
|
||||
FEAT_MSG = "feat({phase}): step {num} — {name}"
|
||||
CHORE_MSG = "chore({phase}): step {num} output"
|
||||
TZ = timezone(timedelta(hours=9))
|
||||
|
||||
def __init__(self, phase_dir_name: str, *, auto_push: bool = False):
|
||||
self._root = str(ROOT)
|
||||
self._phases_dir = ROOT / "phases"
|
||||
self._phase_dir = self._phases_dir / phase_dir_name
|
||||
self._phase_dir_name = phase_dir_name
|
||||
self._top_index_file = self._phases_dir / "index.json"
|
||||
self._auto_push = auto_push
|
||||
|
||||
if not self._phase_dir.is_dir():
|
||||
print(f"ERROR: {self._phase_dir} not found")
|
||||
sys.exit(1)
|
||||
|
||||
self._index_file = self._phase_dir / "index.json"
|
||||
if not self._index_file.exists():
|
||||
print(f"ERROR: {self._index_file} not found")
|
||||
sys.exit(1)
|
||||
|
||||
idx = self._read_json(self._index_file)
|
||||
self._project = idx.get("project", "project")
|
||||
self._phase_name = idx.get("phase", phase_dir_name)
|
||||
self._total = len(idx["steps"])
|
||||
|
||||
def run(self):
|
||||
self._print_header()
|
||||
self._check_blockers()
|
||||
self._checkout_branch()
|
||||
guardrails = self._load_guardrails()
|
||||
self._ensure_created_at()
|
||||
self._execute_all_steps(guardrails)
|
||||
self._finalize()
|
||||
|
||||
# --- timestamps ---
|
||||
|
||||
def _stamp(self) -> str:
|
||||
return datetime.now(self.TZ).strftime("%Y-%m-%dT%H:%M:%S%z")
|
||||
|
||||
# --- JSON I/O ---
|
||||
|
||||
@staticmethod
|
||||
def _read_json(p: Path) -> dict:
|
||||
return json.loads(p.read_text(encoding="utf-8"))
|
||||
|
||||
@staticmethod
|
||||
def _write_json(p: Path, data: dict):
|
||||
p.write_text(json.dumps(data, indent=2, ensure_ascii=False), encoding="utf-8")
|
||||
|
||||
# --- git ---
|
||||
|
||||
def _run_git(self, *args) -> subprocess.CompletedProcess:
|
||||
cmd = ["git"] + list(args)
|
||||
return subprocess.run(cmd, cwd=self._root, capture_output=True, text=True)
|
||||
|
||||
def _checkout_branch(self):
|
||||
branch = f"feat-{self._phase_name}"
|
||||
|
||||
r = self._run_git("rev-parse", "--abbrev-ref", "HEAD")
|
||||
if r.returncode != 0:
|
||||
print(f" ERROR: git을 사용할 수 없거나 git repo가 아닙니다.")
|
||||
print(f" {r.stderr.strip()}")
|
||||
sys.exit(1)
|
||||
|
||||
if r.stdout.strip() == branch:
|
||||
return
|
||||
|
||||
r = self._run_git("rev-parse", "--verify", branch)
|
||||
r = self._run_git("checkout", branch) if r.returncode == 0 else self._run_git("checkout", "-b", branch)
|
||||
|
||||
if r.returncode != 0:
|
||||
print(f" ERROR: 브랜치 '{branch}' checkout 실패.")
|
||||
print(f" {r.stderr.strip()}")
|
||||
print(f" Hint: 변경사항을 stash하거나 commit한 후 다시 시도하세요.")
|
||||
sys.exit(1)
|
||||
|
||||
print(f" Branch: {branch}")
|
||||
|
||||
def _commit_step(self, step_num: int, step_name: str):
|
||||
output_rel = f"phases/{self._phase_dir_name}/step{step_num}-output.json"
|
||||
index_rel = f"phases/{self._phase_dir_name}/index.json"
|
||||
|
||||
self._run_git("add", "-A")
|
||||
self._run_git("reset", "HEAD", "--", output_rel)
|
||||
self._run_git("reset", "HEAD", "--", index_rel)
|
||||
|
||||
if self._run_git("diff", "--cached", "--quiet").returncode != 0:
|
||||
msg = self.FEAT_MSG.format(phase=self._phase_name, num=step_num, name=step_name)
|
||||
r = self._run_git("commit", "-m", msg)
|
||||
if r.returncode == 0:
|
||||
print(f" Commit: {msg}")
|
||||
else:
|
||||
print(f" WARN: 코드 커밋 실패: {r.stderr.strip()}")
|
||||
|
||||
self._run_git("add", "-A")
|
||||
if self._run_git("diff", "--cached", "--quiet").returncode != 0:
|
||||
msg = self.CHORE_MSG.format(phase=self._phase_name, num=step_num)
|
||||
r = self._run_git("commit", "-m", msg)
|
||||
if r.returncode != 0:
|
||||
print(f" WARN: housekeeping 커밋 실패: {r.stderr.strip()}")
|
||||
|
||||
# --- top-level index ---
|
||||
|
||||
def _update_top_index(self, status: str):
|
||||
if not self._top_index_file.exists():
|
||||
return
|
||||
top = self._read_json(self._top_index_file)
|
||||
ts = self._stamp()
|
||||
for phase in top.get("phases", []):
|
||||
if phase.get("dir") == self._phase_dir_name:
|
||||
phase["status"] = status
|
||||
ts_key = {"completed": "completed_at", "error": "failed_at", "blocked": "blocked_at"}.get(status)
|
||||
if ts_key:
|
||||
phase[ts_key] = ts
|
||||
break
|
||||
self._write_json(self._top_index_file, top)
|
||||
|
||||
# --- guardrails & context ---
|
||||
|
||||
def _load_guardrails(self) -> str:
|
||||
sections = []
|
||||
agents_md = ROOT / "AGENTS.md"
|
||||
if agents_md.exists():
|
||||
sections.append(f"## 프로젝트 규칙 (AGENTS.md)\n\n{agents_md.read_text(encoding='utf-8')}")
|
||||
docs_dir = ROOT / "docs"
|
||||
if docs_dir.is_dir():
|
||||
for doc in sorted(docs_dir.glob("*.md")):
|
||||
sections.append(f"## {doc.stem}\n\n{doc.read_text(encoding='utf-8')}")
|
||||
return "\n\n---\n\n".join(sections) if sections else ""
|
||||
|
||||
@staticmethod
|
||||
def _build_step_context(index: dict) -> str:
|
||||
lines = [
|
||||
f"- Step {s['step']} ({s['name']}): {s['summary']}"
|
||||
for s in index["steps"]
|
||||
if s["status"] == "completed" and s.get("summary")
|
||||
]
|
||||
if not lines:
|
||||
return ""
|
||||
return "## 이전 Step 산출물\n\n" + "\n".join(lines) + "\n\n"
|
||||
|
||||
def _build_preamble(self, guardrails: str, step_context: str,
|
||||
prev_error: Optional[str] = None) -> str:
|
||||
commit_example = self.FEAT_MSG.format(
|
||||
phase=self._phase_name, num="N", name="<step-name>"
|
||||
)
|
||||
retry_section = ""
|
||||
if prev_error:
|
||||
retry_section = (
|
||||
f"\n## ⚠ 이전 시도 실패 — 아래 에러를 반드시 참고하여 수정하라\n\n"
|
||||
f"{prev_error}\n\n---\n\n"
|
||||
)
|
||||
return (
|
||||
f"당신은 {self._project} 프로젝트의 개발자입니다. 아래 step을 수행하세요.\n\n"
|
||||
f"{guardrails}\n\n---\n\n"
|
||||
f"{step_context}{retry_section}"
|
||||
f"## 작업 규칙\n\n"
|
||||
f"1. 이전 step에서 작성된 코드를 확인하고 일관성을 유지하라.\n"
|
||||
f"2. 이 step에 명시된 작업만 수행하라. 추가 기능이나 파일을 만들지 마라.\n"
|
||||
f"3. 기존 테스트를 깨뜨리지 마라.\n"
|
||||
f"4. AC(Acceptance Criteria) 검증을 직접 실행하라.\n"
|
||||
f"5. /phases/{self._phase_dir_name}/index.json의 해당 step status를 업데이트하라:\n"
|
||||
f" - AC 통과 → \"completed\" + \"summary\" 필드에 이 step의 산출물을 한 줄로 요약\n"
|
||||
f" - {self.MAX_RETRIES}회 수정 시도 후에도 실패 → \"error\" + \"error_message\" 기록\n"
|
||||
f" - 사용자 개입이 필요한 경우 (API 키, 인증, 수동 설정 등) → \"blocked\" + \"blocked_reason\" 기록 후 즉시 중단\n"
|
||||
f"6. 모든 변경사항을 커밋하라:\n"
|
||||
f" {commit_example}\n\n---\n\n"
|
||||
)
|
||||
|
||||
# --- Codex 호출 ---
|
||||
|
||||
def _invoke_codex(self, step: dict, preamble: str) -> dict:
|
||||
step_num, step_name = step["step"], step["name"]
|
||||
step_file = self._phase_dir / f"step{step_num}.md"
|
||||
|
||||
if not step_file.exists():
|
||||
print(f" ERROR: {step_file} not found")
|
||||
sys.exit(1)
|
||||
|
||||
prompt = preamble + step_file.read_text(encoding="utf-8")
|
||||
result = subprocess.run(
|
||||
["codex", "exec", "--dangerously-bypass-approvals-and-sandbox", "--json", prompt],
|
||||
cwd=self._root, capture_output=True, text=True, timeout=1800,
|
||||
)
|
||||
|
||||
if result.returncode != 0:
|
||||
print(f"\n WARN: Codex가 비정상 종료됨 (code {result.returncode})")
|
||||
if result.stderr:
|
||||
print(f" stderr: {result.stderr[:500]}")
|
||||
|
||||
output = {
|
||||
"step": step_num, "name": step_name,
|
||||
"exitCode": result.returncode,
|
||||
"stdout": result.stdout, "stderr": result.stderr,
|
||||
}
|
||||
out_path = self._phase_dir / f"step{step_num}-output.json"
|
||||
with open(out_path, "w", encoding="utf-8") as f:
|
||||
json.dump(output, f, indent=2, ensure_ascii=False)
|
||||
|
||||
return output
|
||||
|
||||
# --- 헤더 & 검증 ---
|
||||
|
||||
def _print_header(self):
|
||||
print(f"\n{'='*60}")
|
||||
print(f" Harness Step Executor")
|
||||
print(f" Phase: {self._phase_name} | Steps: {self._total}")
|
||||
if self._auto_push:
|
||||
print(f" Auto-push: enabled")
|
||||
print(f"{'='*60}")
|
||||
|
||||
def _check_blockers(self):
|
||||
index = self._read_json(self._index_file)
|
||||
for s in reversed(index["steps"]):
|
||||
if s["status"] == "error":
|
||||
print(f"\n ✗ Step {s['step']} ({s['name']}) failed.")
|
||||
print(f" Error: {s.get('error_message', 'unknown')}")
|
||||
print(f" Fix and reset status to 'pending' to retry.")
|
||||
sys.exit(1)
|
||||
if s["status"] == "blocked":
|
||||
print(f"\n ⏸ Step {s['step']} ({s['name']}) blocked.")
|
||||
print(f" Reason: {s.get('blocked_reason', 'unknown')}")
|
||||
print(f" Resolve and reset status to 'pending' to retry.")
|
||||
sys.exit(2)
|
||||
if s["status"] != "pending":
|
||||
break
|
||||
|
||||
def _ensure_created_at(self):
|
||||
index = self._read_json(self._index_file)
|
||||
if "created_at" not in index:
|
||||
index["created_at"] = self._stamp()
|
||||
self._write_json(self._index_file, index)
|
||||
|
||||
# --- 실행 루프 ---
|
||||
|
||||
def _execute_single_step(self, step: dict, guardrails: str) -> bool:
|
||||
"""단일 step 실행 (재시도 포함). 완료되면 True, 실패/차단이면 False."""
|
||||
step_num, step_name = step["step"], step["name"]
|
||||
done = sum(1 for s in self._read_json(self._index_file)["steps"] if s["status"] == "completed")
|
||||
prev_error = None
|
||||
|
||||
for attempt in range(1, self.MAX_RETRIES + 1):
|
||||
index = self._read_json(self._index_file)
|
||||
step_context = self._build_step_context(index)
|
||||
preamble = self._build_preamble(guardrails, step_context, prev_error)
|
||||
|
||||
tag = f"Step {step_num}/{self._total - 1} ({done} done): {step_name}"
|
||||
if attempt > 1:
|
||||
tag += f" [retry {attempt}/{self.MAX_RETRIES}]"
|
||||
|
||||
with progress_indicator(tag) as pi:
|
||||
self._invoke_codex(step, preamble)
|
||||
elapsed = int(pi.elapsed)
|
||||
|
||||
index = self._read_json(self._index_file)
|
||||
status = next((s.get("status", "pending") for s in index["steps"] if s["step"] == step_num), "pending")
|
||||
ts = self._stamp()
|
||||
|
||||
if status == "completed":
|
||||
for s in index["steps"]:
|
||||
if s["step"] == step_num:
|
||||
s["completed_at"] = ts
|
||||
self._write_json(self._index_file, index)
|
||||
self._commit_step(step_num, step_name)
|
||||
print(f" ✓ Step {step_num}: {step_name} [{elapsed}s]")
|
||||
return True
|
||||
|
||||
if status == "blocked":
|
||||
for s in index["steps"]:
|
||||
if s["step"] == step_num:
|
||||
s["blocked_at"] = ts
|
||||
self._write_json(self._index_file, index)
|
||||
reason = next((s.get("blocked_reason", "") for s in index["steps"] if s["step"] == step_num), "")
|
||||
print(f" ⏸ Step {step_num}: {step_name} blocked [{elapsed}s]")
|
||||
print(f" Reason: {reason}")
|
||||
self._update_top_index("blocked")
|
||||
sys.exit(2)
|
||||
|
||||
err_msg = next(
|
||||
(s.get("error_message", "Step did not update status") for s in index["steps"] if s["step"] == step_num),
|
||||
"Step did not update status",
|
||||
)
|
||||
|
||||
if attempt < self.MAX_RETRIES:
|
||||
for s in index["steps"]:
|
||||
if s["step"] == step_num:
|
||||
s["status"] = "pending"
|
||||
s.pop("error_message", None)
|
||||
self._write_json(self._index_file, index)
|
||||
prev_error = err_msg
|
||||
print(f" ↻ Step {step_num}: retry {attempt}/{self.MAX_RETRIES} — {err_msg}")
|
||||
else:
|
||||
for s in index["steps"]:
|
||||
if s["step"] == step_num:
|
||||
s["status"] = "error"
|
||||
s["error_message"] = f"[{self.MAX_RETRIES}회 시도 후 실패] {err_msg}"
|
||||
s["failed_at"] = ts
|
||||
self._write_json(self._index_file, index)
|
||||
self._commit_step(step_num, step_name)
|
||||
print(f" ✗ Step {step_num}: {step_name} failed after {self.MAX_RETRIES} attempts [{elapsed}s]")
|
||||
print(f" Error: {err_msg}")
|
||||
self._update_top_index("error")
|
||||
sys.exit(1)
|
||||
|
||||
return False # unreachable
|
||||
|
||||
def _execute_all_steps(self, guardrails: str):
|
||||
while True:
|
||||
index = self._read_json(self._index_file)
|
||||
pending = next((s for s in index["steps"] if s["status"] == "pending"), None)
|
||||
if pending is None:
|
||||
print("\n All steps completed!")
|
||||
return
|
||||
|
||||
step_num = pending["step"]
|
||||
for s in index["steps"]:
|
||||
if s["step"] == step_num and "started_at" not in s:
|
||||
s["started_at"] = self._stamp()
|
||||
self._write_json(self._index_file, index)
|
||||
break
|
||||
|
||||
self._execute_single_step(pending, guardrails)
|
||||
|
||||
def _finalize(self):
|
||||
index = self._read_json(self._index_file)
|
||||
index["completed_at"] = self._stamp()
|
||||
self._write_json(self._index_file, index)
|
||||
self._update_top_index("completed")
|
||||
|
||||
self._run_git("add", "-A")
|
||||
if self._run_git("diff", "--cached", "--quiet").returncode != 0:
|
||||
msg = f"chore({self._phase_name}): mark phase completed"
|
||||
r = self._run_git("commit", "-m", msg)
|
||||
if r.returncode == 0:
|
||||
print(f" ✓ {msg}")
|
||||
|
||||
if self._auto_push:
|
||||
branch = f"feat-{self._phase_name}"
|
||||
r = self._run_git("push", "-u", "origin", branch)
|
||||
if r.returncode != 0:
|
||||
print(f"\n ERROR: git push 실패: {r.stderr.strip()}")
|
||||
sys.exit(1)
|
||||
print(f" ✓ Pushed to origin/{branch}")
|
||||
|
||||
print(f"\n{'='*60}")
|
||||
print(f" Phase '{self._phase_name}' completed!")
|
||||
print(f"{'='*60}")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Harness Step Executor")
|
||||
parser.add_argument("phase_dir", help="Phase directory name (e.g. 0-mvp)")
|
||||
parser.add_argument("--push", action="store_true", help="Push branch after completion")
|
||||
args = parser.parse_args()
|
||||
|
||||
StepExecutor(args.phase_dir, auto_push=args.push).run()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,3 @@
|
||||
"""FEM surrogate tutorial package."""
|
||||
|
||||
__version__ = "0.1.0"
|
||||
@@ -0,0 +1 @@
|
||||
"""Dataset generation helpers for FEM surrogate tutorials."""
|
||||
@@ -0,0 +1,16 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ParameterBound:
|
||||
lower: float
|
||||
upper: float
|
||||
|
||||
|
||||
DEFAULT_PARAMETER_BOUNDS = {
|
||||
"L_m": ParameterBound(lower=1.0, upper=3.0),
|
||||
"b_m": ParameterBound(lower=0.02, upper=0.08),
|
||||
"h_m": ParameterBound(lower=0.04, upper=0.16),
|
||||
"E_pa": ParameterBound(lower=100e9, upper=220e9),
|
||||
"P_n": ParameterBound(lower=100.0, upper=2000.0),
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
from collections.abc import Mapping
|
||||
from dataclasses import asdict
|
||||
|
||||
import pandas as pd
|
||||
|
||||
from femsurrogate.data.schema import (
|
||||
DENSITY_KG_M3,
|
||||
DERIVED_COLUMNS,
|
||||
PARAMETER_COLUMNS,
|
||||
TARGET_COLUMNS,
|
||||
AnalysisResult,
|
||||
BeamParameters,
|
||||
)
|
||||
from femsurrogate.fea.model import Beam, BeamModel, NodalLoad, Node
|
||||
from femsurrogate.fea.solver import solve_linear_static
|
||||
|
||||
N_CANTILEVER_NODES = 6
|
||||
|
||||
|
||||
def _section_area(params: BeamParameters) -> float:
|
||||
return params.b_m * params.h_m
|
||||
|
||||
|
||||
def _section_izz(params: BeamParameters) -> float:
|
||||
return params.b_m * params.h_m**3 / 12.0
|
||||
|
||||
|
||||
def _coerce_parameters(params: BeamParameters | Mapping[str, float]) -> BeamParameters:
|
||||
if isinstance(params, BeamParameters):
|
||||
return params
|
||||
return BeamParameters(**{column: float(params[column]) for column in PARAMETER_COLUMNS})
|
||||
|
||||
|
||||
def _make_cantilever_model(params: BeamParameters) -> BeamModel:
|
||||
area = _section_area(params)
|
||||
izz = _section_izz(params)
|
||||
iyy = params.h_m * params.b_m**3 / 12.0
|
||||
nodes = {
|
||||
node_id: Node(
|
||||
id=node_id,
|
||||
x=params.L_m * (node_id - 1) / (N_CANTILEVER_NODES - 1),
|
||||
y=0.0,
|
||||
)
|
||||
for node_id in range(1, N_CANTILEVER_NODES + 1)
|
||||
}
|
||||
beams = tuple(
|
||||
Beam(id=beam_id, node_i=beam_id, node_j=beam_id + 1)
|
||||
for beam_id in range(1, N_CANTILEVER_NODES)
|
||||
)
|
||||
|
||||
return BeamModel(
|
||||
metadata={
|
||||
"Area": area,
|
||||
"J": iyy + izz,
|
||||
"Iyy": iyy,
|
||||
"Izz": izz,
|
||||
"ElasticModulus": params.E_pa,
|
||||
"Poisson'sRatio": 0.3,
|
||||
},
|
||||
nodes=nodes,
|
||||
beams=beams,
|
||||
fixed_nodes=(1,),
|
||||
nodal_loads={
|
||||
N_CANTILEVER_NODES: NodalLoad(
|
||||
node_id=N_CANTILEVER_NODES,
|
||||
fx=0.0,
|
||||
fy=-params.P_n,
|
||||
mz=0.0,
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def run_beam2d_case(params: BeamParameters | Mapping[str, float]) -> AnalysisResult:
|
||||
beam_params = _coerce_parameters(params)
|
||||
area = _section_area(beam_params)
|
||||
izz = _section_izz(beam_params)
|
||||
model = _make_cantilever_model(beam_params)
|
||||
solution = solve_linear_static(model)
|
||||
tip_uy = solution[N_CANTILEVER_NODES].uy
|
||||
max_moment = abs(beam_params.P_n) * beam_params.L_m
|
||||
max_abs_bending_stress = max_moment * (beam_params.h_m / 2.0) / izz
|
||||
mass = DENSITY_KG_M3 * area * beam_params.L_m
|
||||
compliance = -beam_params.P_n * tip_uy
|
||||
|
||||
return AnalysisResult(
|
||||
tip_uy_m=tip_uy,
|
||||
max_abs_bending_stress_pa=max_abs_bending_stress,
|
||||
mass_kg=mass,
|
||||
compliance_j=compliance,
|
||||
)
|
||||
|
||||
|
||||
def build_dataset(samples: pd.DataFrame) -> pd.DataFrame:
|
||||
rows: list[dict[str, float]] = []
|
||||
|
||||
for sample in samples.to_dict(orient="records"):
|
||||
params = _coerce_parameters(sample)
|
||||
result = run_beam2d_case(params)
|
||||
row = dict(sample)
|
||||
row["A_m2"] = _section_area(params)
|
||||
row["I_m4"] = _section_izz(params)
|
||||
row.update(asdict(result))
|
||||
rows.append(row)
|
||||
|
||||
return pd.DataFrame(rows, columns=[*samples.columns, *DERIVED_COLUMNS, *TARGET_COLUMNS])
|
||||
@@ -0,0 +1,18 @@
|
||||
import pandas as pd
|
||||
from scipy.stats import qmc
|
||||
|
||||
from femsurrogate.data.bounds import ParameterBound
|
||||
|
||||
|
||||
def generate_lhs_samples(bounds: dict[str, ParameterBound], n: int, seed: int) -> pd.DataFrame:
|
||||
if n <= 0:
|
||||
raise ValueError("n must be positive")
|
||||
|
||||
columns = list(bounds)
|
||||
sampler = qmc.LatinHypercube(d=len(columns), seed=seed)
|
||||
unit_samples = sampler.random(n)
|
||||
lower = [bounds[column].lower for column in columns]
|
||||
upper = [bounds[column].upper for column in columns]
|
||||
samples = qmc.scale(unit_samples, lower, upper)
|
||||
|
||||
return pd.DataFrame(samples, columns=columns)
|
||||
@@ -0,0 +1,24 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
DEFAULT_RANDOM_SEED = 20260521
|
||||
DENSITY_KG_M3 = 7850.0
|
||||
PARAMETER_COLUMNS = ("L_m", "b_m", "h_m", "E_pa", "P_n")
|
||||
DERIVED_COLUMNS = ("A_m2", "I_m4")
|
||||
TARGET_COLUMNS = ("tip_uy_m", "max_abs_bending_stress_pa", "mass_kg", "compliance_j")
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class BeamParameters:
|
||||
L_m: float
|
||||
b_m: float
|
||||
h_m: float
|
||||
E_pa: float
|
||||
P_n: float
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class AnalysisResult:
|
||||
tip_uy_m: float
|
||||
max_abs_bending_stress_pa: float
|
||||
mass_kg: float
|
||||
compliance_j: float
|
||||
@@ -0,0 +1 @@
|
||||
"""Finite element helpers for 2D beam/frame examples."""
|
||||
@@ -0,0 +1,60 @@
|
||||
import math
|
||||
|
||||
import numpy as np
|
||||
from scipy.sparse import csr_matrix, lil_matrix
|
||||
|
||||
from femsurrogate.fea.element import local_frame_stiffness, transformation_matrix
|
||||
from femsurrogate.fea.model import BeamModel
|
||||
|
||||
|
||||
def node_ids(model: BeamModel) -> tuple[int, ...]:
|
||||
return tuple(sorted(model.nodes))
|
||||
|
||||
|
||||
def dof_index(model: BeamModel, node_id: int, local_dof: int) -> int:
|
||||
node_position = node_ids(model).index(node_id)
|
||||
return node_position * 3 + local_dof
|
||||
|
||||
|
||||
def node_dofs(model: BeamModel, node_id: int) -> tuple[int, int, int]:
|
||||
base = dof_index(model, node_id, 0)
|
||||
return (base, base + 1, base + 2)
|
||||
|
||||
|
||||
def constrained_dofs(model: BeamModel) -> tuple[int, ...]:
|
||||
return tuple(dof for node_id in sorted(model.fixed_nodes) for dof in node_dofs(model, node_id))
|
||||
|
||||
|
||||
def assemble_global_stiffness(model: BeamModel) -> csr_matrix:
|
||||
size = 3 * len(model.nodes)
|
||||
stiffness = lil_matrix((size, size), dtype=float)
|
||||
E = model.metadata["ElasticModulus"]
|
||||
A = model.metadata["Area"]
|
||||
Izz = model.metadata["Izz"]
|
||||
|
||||
for beam in model.beams:
|
||||
node_i = model.nodes[beam.node_i]
|
||||
node_j = model.nodes[beam.node_j]
|
||||
length = math.hypot(node_j.x - node_i.x, node_j.y - node_i.y)
|
||||
local_stiffness = local_frame_stiffness(E=E, A=A, Izz=Izz, L=length)
|
||||
transform = transformation_matrix(node_i.x, node_i.y, node_j.x, node_j.y)
|
||||
element_stiffness = transform.T @ local_stiffness @ transform
|
||||
element_dofs = (*node_dofs(model, beam.node_i), *node_dofs(model, beam.node_j))
|
||||
|
||||
for row_local, row_global in enumerate(element_dofs):
|
||||
for col_local, col_global in enumerate(element_dofs):
|
||||
stiffness[row_global, col_global] += element_stiffness[row_local, col_local]
|
||||
|
||||
return stiffness.tocsr()
|
||||
|
||||
|
||||
def assemble_load_vector(model: BeamModel) -> np.ndarray:
|
||||
loads = np.zeros(3 * len(model.nodes), dtype=float)
|
||||
|
||||
for node_id, load in model.nodal_loads.items():
|
||||
ux_dof, uy_dof, rz_dof = node_dofs(model, node_id)
|
||||
loads[ux_dof] += load.fx
|
||||
loads[uy_dof] += load.fy
|
||||
loads[rz_dof] += load.mz
|
||||
|
||||
return loads
|
||||
@@ -0,0 +1,2 @@
|
||||
def cantilever_tip_displacement(*, force_y: float, length: float, E: float, Izz: float) -> float:
|
||||
return force_y * length**3 / (3.0 * E * Izz)
|
||||
@@ -0,0 +1,46 @@
|
||||
import math
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
||||
def local_frame_stiffness(*, E: float, A: float, Izz: float, L: float) -> np.ndarray:
|
||||
"""Return local frame stiffness with Rz positive clockwise."""
|
||||
axial = E * A / L
|
||||
b12 = 12.0 * E * Izz / L**3
|
||||
b6 = 6.0 * E * Izz / L**2
|
||||
b4 = 4.0 * E * Izz / L
|
||||
b2 = 2.0 * E * Izz / L
|
||||
|
||||
return np.array(
|
||||
[
|
||||
[axial, 0.0, 0.0, -axial, 0.0, 0.0],
|
||||
[0.0, b12, -b6, 0.0, -b12, -b6],
|
||||
[0.0, -b6, b4, 0.0, b6, b2],
|
||||
[-axial, 0.0, 0.0, axial, 0.0, 0.0],
|
||||
[0.0, -b12, b6, 0.0, b12, b6],
|
||||
[0.0, -b6, b2, 0.0, b6, b4],
|
||||
],
|
||||
dtype=float,
|
||||
)
|
||||
|
||||
|
||||
def transformation_matrix(x1: float, y1: float, x2: float, y2: float) -> np.ndarray:
|
||||
dx = x2 - x1
|
||||
dy = y2 - y1
|
||||
length = math.hypot(dx, dy)
|
||||
c = dx / length
|
||||
s = dy / length
|
||||
|
||||
block = np.array(
|
||||
[
|
||||
[c, s, 0.0],
|
||||
[-s, c, 0.0],
|
||||
[0.0, 0.0, 1.0],
|
||||
],
|
||||
dtype=float,
|
||||
)
|
||||
|
||||
matrix = np.zeros((6, 6), dtype=float)
|
||||
matrix[:3, :3] = block
|
||||
matrix[3:, 3:] = block
|
||||
return matrix
|
||||
@@ -0,0 +1,70 @@
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
from femsurrogate.fea.model import Beam, BeamModel, Displacement, NodalLoad, Node
|
||||
|
||||
|
||||
def _tokens(line: str) -> list[str]:
|
||||
return [token for token in re.split(r"[,\s]+", line.strip()) if token]
|
||||
|
||||
|
||||
def read_beam_example(path: str | Path) -> BeamModel:
|
||||
metadata: dict[str, float] = {}
|
||||
nodes: dict[int, Node] = {}
|
||||
beams: list[Beam] = []
|
||||
fixed_nodes: list[int] = []
|
||||
nodal_loads: dict[int, NodalLoad] = {}
|
||||
|
||||
for raw_line in Path(path).read_text(encoding="utf-8").splitlines():
|
||||
line = raw_line.strip()
|
||||
if not line or line.startswith("#"):
|
||||
continue
|
||||
|
||||
parts = _tokens(line)
|
||||
keyword = parts[0]
|
||||
|
||||
if keyword == "Node":
|
||||
node_id = int(parts[1])
|
||||
nodes[node_id] = Node(id=node_id, x=float(parts[2]), y=float(parts[3]))
|
||||
elif keyword == "Beam":
|
||||
beams.append(Beam(id=int(parts[1]), node_i=int(parts[2]), node_j=int(parts[3])))
|
||||
elif keyword == "Fix":
|
||||
fixed_nodes.append(int(parts[1]))
|
||||
elif keyword == "NodeLoad":
|
||||
node_id = int(parts[1])
|
||||
nodal_loads[node_id] = NodalLoad(
|
||||
node_id=node_id,
|
||||
fx=float(parts[2]),
|
||||
fy=float(parts[3]),
|
||||
mz=float(parts[4]),
|
||||
)
|
||||
else:
|
||||
metadata[keyword] = float(parts[1])
|
||||
|
||||
return BeamModel(
|
||||
metadata=metadata,
|
||||
nodes=nodes,
|
||||
beams=tuple(beams),
|
||||
fixed_nodes=tuple(fixed_nodes),
|
||||
nodal_loads=nodal_loads,
|
||||
)
|
||||
|
||||
|
||||
def read_expected_displacements(path: str | Path) -> dict[int, Displacement]:
|
||||
displacements: dict[int, Displacement] = {}
|
||||
|
||||
for raw_line in Path(path).read_text(encoding="utf-8").splitlines():
|
||||
line = raw_line.strip()
|
||||
if not line or line.startswith("#"):
|
||||
continue
|
||||
|
||||
parts = _tokens(line)
|
||||
node_id = int(parts[0])
|
||||
displacements[node_id] = Displacement(
|
||||
node_id=node_id,
|
||||
ux=float(parts[1]),
|
||||
uy=float(parts[2]),
|
||||
rz=float(parts[3]),
|
||||
)
|
||||
|
||||
return displacements
|
||||
@@ -0,0 +1,40 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Node:
|
||||
id: int
|
||||
x: float
|
||||
y: float
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Beam:
|
||||
id: int
|
||||
node_i: int
|
||||
node_j: int
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class NodalLoad:
|
||||
node_id: int
|
||||
fx: float
|
||||
fy: float
|
||||
mz: float
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Displacement:
|
||||
node_id: int
|
||||
ux: float
|
||||
uy: float
|
||||
rz: float
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class BeamModel:
|
||||
metadata: dict[str, float]
|
||||
nodes: dict[int, Node]
|
||||
beams: tuple[Beam, ...]
|
||||
fixed_nodes: tuple[int, ...]
|
||||
nodal_loads: dict[int, NodalLoad]
|
||||
@@ -0,0 +1,33 @@
|
||||
import numpy as np
|
||||
from scipy.sparse.linalg import spsolve
|
||||
|
||||
from femsurrogate.fea.assembly import (
|
||||
assemble_global_stiffness,
|
||||
assemble_load_vector,
|
||||
constrained_dofs,
|
||||
node_ids,
|
||||
)
|
||||
from femsurrogate.fea.model import BeamModel, Displacement
|
||||
|
||||
|
||||
def solve_linear_static(model: BeamModel) -> dict[int, Displacement]:
|
||||
stiffness = assemble_global_stiffness(model)
|
||||
loads = assemble_load_vector(model)
|
||||
fixed = np.array(constrained_dofs(model), dtype=int)
|
||||
all_dofs = np.arange(loads.size)
|
||||
free = np.setdiff1d(all_dofs, fixed, assume_unique=True)
|
||||
|
||||
displacements = np.zeros_like(loads)
|
||||
displacements[free] = spsolve(stiffness[free][:, free], loads[free])
|
||||
|
||||
result: dict[int, Displacement] = {}
|
||||
for position, node_id in enumerate(node_ids(model)):
|
||||
base = position * 3
|
||||
result[node_id] = Displacement(
|
||||
node_id=node_id,
|
||||
ux=float(displacements[base]),
|
||||
uy=float(displacements[base + 1]),
|
||||
rz=float(displacements[base + 2]),
|
||||
)
|
||||
|
||||
return result
|
||||
@@ -0,0 +1 @@
|
||||
"""Plotting helpers for diagnostics and model comparison."""
|
||||