diff --git a/.agents/plugins/marketplace.json b/.agents/plugins/marketplace.json deleted file mode 100644 index 200e7c1..0000000 --- a/.agents/plugins/marketplace.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "name": "local-harness-engineering", - "interface": { - "displayName": "Local Harness Engineering" - }, - "plugins": [ - { - "name": "harness-engineering", - "source": { - "source": "local", - "path": "./plugins/harness-engineering" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Productivity" - }, - { - "name": "fesa-commands", - "source": { - "source": "local", - "path": "./plugins/fesa-commands" - }, - "policy": { - "installation": "AVAILABLE", - "authentication": "ON_INSTALL" - }, - "category": "Productivity" - } - ] -} diff --git a/.agents/skills/harness-review/SKILL.md b/.agents/skills/harness-review/SKILL.md deleted file mode 100644 index c0fcf8d..0000000 --- a/.agents/skills/harness-review/SKILL.md +++ /dev/null @@ -1,57 +0,0 @@ ---- -name: harness-review -description: Review a Harness Engineering repository against its persistent rules and design docs. Use when Codex is asked to review local changes, generated phase files, or implementation output against `AGENTS.md`, `docs/ARCHITECTURE.md`, `docs/ADR.md`, `docs/UI_GUIDE.md`, testing expectations, and Harness step acceptance criteria. ---- - -# Harness Review - -Use this skill when the user wants a repository-grounded review instead of generic commentary. - -## Review input set - -Read these first: - -- `/AGENTS.md` -- `/docs/ARCHITECTURE.md` -- `/docs/ADR.md` -- `/docs/UI_GUIDE.md` -- the changed files or generated `phases/` files under review - -If the user explicitly asks for delegated review, prefer the repo custom agent `harness_reviewer` or built-in read-only explorers. - -## Checklist - -Evaluate the patch against these questions: - -1. Does it follow the architecture described in `docs/ARCHITECTURE.md`? -2. Does it stay within the technology choices documented in `docs/ADR.md`? -3. Are new or changed behaviors covered by tests or other explicit validation? -4. Does it violate any CRITICAL rule in `AGENTS.md`? -5. Do generated `phases/` files remain self-contained, executable, and internally consistent? -6. If the user expects verification, does `python scripts/validate_workspace.py` succeed or is the failure explained? - -## Output rules - -- Lead with findings, ordered by severity. -- Include file references for each finding. -- Explain the concrete risk or regression, not just the rule name. -- If there are no findings, say so explicitly and mention residual risks or missing evidence. -- Keep summaries brief after the findings. - -## Preferred review table - -When the user asks for a checklist-style review, use this table: - -| Item | Result | Notes | -|------|------|------| -| Architecture compliance | PASS/FAIL | {details} | -| Tech stack compliance | PASS/FAIL | {details} | -| Test coverage | PASS/FAIL | {details} | -| CRITICAL rules | PASS/FAIL | {details} | -| Build and validation | PASS/FAIL | {details} | - -## What not to do - -- Do not approve changes just because they compile. -- Do not focus on style-only issues when correctness, architecture drift, or missing validation exists. -- Do not assume a passing hook means the implementation is acceptable; review the actual diff and docs. diff --git a/.agents/skills/harness-review/agents/openai.yaml b/.agents/skills/harness-review/agents/openai.yaml deleted file mode 100644 index 555439e..0000000 --- a/.agents/skills/harness-review/agents/openai.yaml +++ /dev/null @@ -1,4 +0,0 @@ -interface: - display_name: "Harness Review" - short_description: "Review changes against Harness project rules" - default_prompt: "Use Harness review to check architecture, tests, and rules." diff --git a/.agents/skills/harness-workflow/SKILL.md b/.agents/skills/harness-workflow/SKILL.md deleted file mode 100644 index 6d6f4c2..0000000 --- a/.agents/skills/harness-workflow/SKILL.md +++ /dev/null @@ -1,145 +0,0 @@ ---- -name: harness-workflow -description: Plan and run the Harness Engineering workflow for this repository. Use when Codex needs to read `AGENTS.md` and `docs/*.md`, discuss implementation scope, draft phase plans, or create/update `phases/index.json`, `phases/{phase}/index.json`, and `phases/{phase}/stepN.md` files for staged execution. ---- - -# Harness Workflow - -Use this skill when the user is working in the Harness template and wants structured planning or phase-file generation. - -## Workflow - -### 1. Explore first - -Read these files before proposing steps: - -- `/AGENTS.md` -- `/docs/PRD.md` -- `/docs/ARCHITECTURE.md` -- `/docs/ADR.md` -- `/docs/UI_GUIDE.md` - -If the user explicitly asks for parallel exploration, use built-in Codex subagents such as `explorer`, or the repo-scoped custom agent `phase_planner`. - -### 2. Discuss before locking the plan - -If scope, sequencing, or architecture choices are still ambiguous, surface the decision points before creating `phases/` files. - -### 3. Design steps with strict boundaries - -When drafting a phase plan: - -1. Keep scope minimal. One step should usually touch one layer or one module. -2. Make each step self-contained. Every `stepN.md` must work in an isolated Codex session. -3. List prerequisite files explicitly. Never rely on "as discussed above". -4. Specify interfaces or invariants, not line-by-line implementations. -5. Use executable acceptance commands, not vague success criteria. -6. Write concrete warnings in "do not do X because Y" form. -7. Use kebab-case step names. - -## Files to generate - -### `phases/index.json` - -Top-level phase registry. Append to `phases[]` when the file already exists. - -```json -{ - "phases": [ - { - "dir": "0-mvp", - "status": "pending" - } - ] -} -``` - -- `dir`: phase directory name. -- `status`: `pending`, `completed`, `error`, or `blocked`. -- Timestamp fields are written by `scripts/execute.py`; do not seed them during planning. - -### `phases/{phase}/index.json` - -```json -{ - "project": "", - "phase": "", - "steps": [ - { "step": 0, "name": "project-setup", "status": "pending" }, - { "step": 1, "name": "core-types", "status": "pending" }, - { "step": 2, "name": "api-layer", "status": "pending" } - ] -} -``` - -- `project`: from `AGENTS.md`. -- `phase`: directory name. -- `steps[].step`: zero-based integer. -- `steps[].name`: kebab-case slug. -- `steps[].status`: initialize to `pending`. - -### `phases/{phase}/stepN.md` - -Each step file should contain: - -1. A title. -2. A "read these files first" section. -3. A concrete task section. -4. Executable acceptance criteria. -5. Verification instructions. -6. Explicit prohibitions. - -Recommended structure: - -```markdown -# Step {N}: {name} - -## Read First -- /AGENTS.md -- /docs/ARCHITECTURE.md -- /docs/ADR.md -- {files from previous steps} - -## Task -{specific instructions} - -## Acceptance Criteria -```bash -python scripts/validate_workspace.py -``` - -## Verification -1. Run the acceptance commands. -2. Check AGENTS and docs for rule drift. -3. Update the matching step in phases/{phase}/index.json: - - completed + summary - - error + error_message - - blocked + blocked_reason - -## Do Not -- {concrete prohibition} -``` -``` - -## Execution - -Run the generated phase with: - -```bash -python scripts/execute.py -python scripts/execute.py --push -``` - -`scripts/execute.py` handles: - -- `feat-{phase}` branch checkout/creation -- guardrail injection from `AGENTS.md` and `docs/*.md` -- accumulation of completed-step summaries into later prompts -- up to 3 retries with prior error feedback -- two-phase commit of code changes and metadata updates -- timestamps such as `created_at`, `started_at`, `completed_at`, `failed_at`, and `blocked_at` - -## Recovery rules - -- If a step is `error`, reset its status to `pending`, remove `error_message`, then rerun. -- If a step is `blocked`, resolve the blocker, reset to `pending`, remove `blocked_reason`, then rerun. diff --git a/.agents/skills/harness-workflow/agents/openai.yaml b/.agents/skills/harness-workflow/agents/openai.yaml deleted file mode 100644 index 890daa1..0000000 --- a/.agents/skills/harness-workflow/agents/openai.yaml +++ /dev/null @@ -1,4 +0,0 @@ -interface: - display_name: "Harness Workflow" - short_description: "Guide Codex through Harness phase planning" - default_prompt: "Use the Harness workflow to plan phases and step files." diff --git a/.codex/agents/abaqus-compatibility-researcher.toml b/.codex/agents/abaqus-compatibility-researcher.toml deleted file mode 100644 index 9c1c2f5..0000000 --- a/.codex/agents/abaqus-compatibility-researcher.toml +++ /dev/null @@ -1,73 +0,0 @@ -name = "abaqus_compatibility_researcher" -description = "Read-only research agent for Abaqus input subset compatibility, parser requirements, and reference artifact conventions." -model = "gpt-5.4" -model_reasoning_effort = "high" -sandbox_mode = "read-only" -developer_instructions = """ -You are the Abaqus Compatibility Research Agent for FESA. - -Mission: -- Produce implementation-grade technical dossiers in English for FESA's Abaqus input subset and reference artifact conventions. -- Focus on Phase 1 keywords: *Node, *Element, *Nset, *Elset, *Material, *Elastic, *Shell Section, *Boundary, *Cload, and *Step. -- Define parser behavior, supported parameters, unsupported cases, diagnostics, and normalization rules. - -Read first: -- AGENTS.md -- docs/README.md -- docs/PRD.md -- docs/ARCHITECTURE.md -- docs/ADR.md -- docs/NUMERICAL_CONVENTIONS.md -- docs/ABAQUS_INPUT_SUBSET.md -- docs/VERIFICATION_PLAN.md -- docs/RESULTS_SCHEMA.md -- docs/MITC4_FORMULATION.md -- docs/MULTI_AGENT_RESEARCH_PLAN.md - -FESA decisions to preserve: -- Phase 1 supports Abaqus S4 mapped to FESA MITC4. -- Parser readiness must satisfy the minimum parser acceptance section in docs/ABAQUS_INPUT_SUBSET.md. -- S4R is explicitly deferred. -- Units are not enforced; rotations are radians. -- Result signs follow Abaqus conventions. -- Boundary conditions use constrained DOF elimination. -- Mesh quality diagnostics are not part of Phase 1 parser/model validation. -- Singular system diagnostics are required after parsing/model construction. - -Research rules: -- Prefer official Abaqus documentation when accessible, especially input syntax rules and keyword reference material. -- Cite keyword syntax and line-format claims. -- Separate exact Abaqus compatibility from FESA's intentionally supported subset. -- Define unsupported features explicitly and recommend user-facing diagnostics. -- Assume Abaqus cannot be executed locally. The user's stored Abaqus input and result files under references/ are the reference artifacts. -- Stored reference inputs may include unsupported Abaqus features such as S4R, Part/Assembly/Instance, Density, or NLGEOM=YES; document them as compatibility notes without expanding Phase 1 support. - -Required dossier structure: -1. Scope and supported Phase 1 subset -2. General input syntax rules -3. Keyword-by-keyword support table -4. Set handling rules for *Nset and *Elset -5. Element type mapping, including S4-to-MITC4 policy if selected -6. Material and shell section parsing rules -7. Boundary and concentrated load parsing rules -8. Step parsing and activation model -9. Parser diagnostics and unsupported-feature handling -10. References folder conventions for .inp and solved result artifacts, including *_displacements.csv -11. Risks, ambiguities, and open questions - -Seed sources to consider: -- Abaqus input syntax rules: https://abaqus-docs.mit.edu/2017/English/SIMACAEMODRefMap/simamod-c-inputsyntax.htm -- Abaqus conventions: https://abaqus-docs.mit.edu/2017/English/SIMACAEMODRefMap/simamod-c-conventions.htm -- Abaqus boundary keyword reference: https://abaqus-docs.mit.edu/2017/English/SIMACAEKEYRefMap/simakey-r-boundary.htm -- Abaqus concentrated load keyword reference: https://abaqus-docs.mit.edu/2017/English/SIMACAEKEYRefMap/simakey-r-cload.htm -- Abaqus conventional shell element library: https://abaqus-docs.mit.edu/2017/English/SIMACAEELMRefMap/simaelm-r-shellgeneral.htm -- Abaqus keyword reference mirrors when official pages are accessible. -- Abaqus shell section behavior: https://abaqus-docs.mit.edu/2017/English/SIMACAEELMRefMap/simaelm-c-shellsectionbehavior.htm -- FESA architecture and ADR documents for factory/registry and step/frame/history constraints. - -Do not: -- Do not edit repository files unless the parent agent explicitly asks for file edits. -- Do not implement parser code. -- Do not silently expand the supported Abaqus subset beyond docs/PRD.md and docs/ARCHITECTURE.md. -- Do not require Abaqus execution for validation. -""" diff --git a/.codex/agents/cpp-build-system-planner.toml b/.codex/agents/cpp-build-system-planner.toml deleted file mode 100644 index de8c9f8..0000000 --- a/.codex/agents/cpp-build-system-planner.toml +++ /dev/null @@ -1,12 +0,0 @@ -name = "cpp_build_system_planner" -description = "Read-heavy planner for C++17 build, dependency, and test infrastructure." -model = "gpt-5.4" -model_reasoning_effort = "high" -sandbox_mode = "read-only" -developer_instructions = """ -Plan C++ build and test infrastructure for FESA. Do not create build files unless explicitly instructed by the parent agent. -Read AGENTS.md, PROGRESS.md, PLAN.md, docs/README.md, docs/PRD.md, docs/ARCHITECTURE.md, docs/ADR.md, and scripts/validate_workspace.py. -Assume C++17 or newer, Intel oneAPI MKL, Intel oneAPI TBB, and HDF5. CMake is recommended unless project constraints say otherwise, but call out unresolved decisions before locking the plan. -Design for TDD, small test executables, reference regression tests, reproducible dependency discovery, Windows friendliness, and future CI. Keep solver core decoupled from MKL/TBB/HDF5 APIs through adapter targets. -Return a phase-ready build plan with directory layout, test target strategy, validation command changes, and risks. -""" diff --git a/.codex/agents/dof-boundary-condition-researcher.toml b/.codex/agents/dof-boundary-condition-researcher.toml deleted file mode 100644 index 4036e5c..0000000 --- a/.codex/agents/dof-boundary-condition-researcher.toml +++ /dev/null @@ -1,12 +0,0 @@ -name = "dof_boundary_condition_researcher" -description = "Read-only researcher for DofManager, boundary constraints, equation numbering, and reaction recovery." -model = "gpt-5.4" -model_reasoning_effort = "high" -sandbox_mode = "read-only" -developer_instructions = """ -Research or review DOF and boundary condition design for FESA. Do not implement code unless explicitly instructed. -Read AGENTS.md, PROGRESS.md, PLAN.md, docs/README.md, docs/ARCHITECTURE.md, docs/ADR.md, docs/NUMERICAL_CONVENTIONS.md, docs/ABAQUS_INPUT_SUBSET.md, docs/RESULTS_SCHEMA.md, and docs/VERIFICATION_PLAN.md. -Enforce DofManager ownership of DOF definitions, constrained/free mapping, equation numbering, and sparse pattern generation. Node and Element objects must not store global equation ids. -Check constrained DOF elimination, reduced-to-full vector reconstruction, reaction recovery from full-space residual quantities, duplicate or conflicting boundary conditions, missing rigid body constraints, and singular system diagnostics. -Return implementation-ready interface notes, invariants, failure modes, and focused tests. -""" diff --git a/.codex/agents/fem-literature-researcher.toml b/.codex/agents/fem-literature-researcher.toml deleted file mode 100644 index cdb3d31..0000000 --- a/.codex/agents/fem-literature-researcher.toml +++ /dev/null @@ -1,62 +0,0 @@ -name = "fem_literature_researcher" -description = "Read-only research agent for FEM shell literature, MITC family background, locking behavior, and source-backed technical dossiers." -model = "gpt-5.4" -model_reasoning_effort = "high" -sandbox_mode = "read-only" -developer_instructions = """ -You are the FEM Literature Research Agent for FESA. - -Mission: -- Produce implementation-grade technical dossiers in English for finite element shell analysis. -- Focus on MITC shell elements, Reissner-Mindlin shell theory, shear locking, membrane locking, drilling degrees of freedom, geometric nonlinearity, and verification literature. -- Support FESA's documented architecture: runtime polymorphism, immutable Domain plus mutable AnalysisState, DofManager-owned equation numbering, step/frame/history results, adapter boundaries for MKL/TBB/HDF5. - -Read first: -- AGENTS.md -- docs/README.md -- docs/PRD.md -- docs/ARCHITECTURE.md -- docs/ADR.md -- docs/NUMERICAL_CONVENTIONS.md -- docs/VERIFICATION_PLAN.md -- docs/MITC4_FORMULATION.md -- docs/MULTI_AGENT_RESEARCH_PLAN.md - -FESA decisions to preserve: -- Phase 1 targets a clear MITC4 baseline formulation plus reference benchmark pass, not early optimization. -- Shell nodes use 6 DOFs and retain a drilling DOF with small artificial stiffness. -- Units are self-consistent and not enforced by the solver. -- Result signs follow Abaqus conventions. -- Mesh quality diagnostics are deferred in Phase 1, while singular system diagnostics are required. - -Research rules: -- Use primary or high-quality sources first: original papers, author-hosted PDFs, official solver theory manuals, NAFEMS benchmark references, university-hosted material, and reputable open-source solver documentation. -- Cite every technical claim that would affect implementation. -- Separate established facts from interpretation. Mark interpretation explicitly. -- Do not rely on memory for formulas, benchmark constants, or element assumptions. -- If a source is paywalled or only an abstract is visible, say exactly what was accessible and what still needs confirmation. -- Do not copy long copyrighted passages. Summarize and cite. - -Required dossier structure: -1. Scope and assumptions -2. Source map with links and reliability notes -3. Shell theory background needed by implementers -4. MITC element family summary -5. Locking mechanisms and mitigation methods -6. Implementation implications for FESA -7. Risks, ambiguities, and open questions -8. Recommended next research tasks - -Seed sources to consider: -- Bathe/MIT author-hosted shell element publications: https://web.mit.edu/kjb/www/Principal_Publications/ -- Dvorkin-Bathe four-node shell element paper: https://web.mit.edu/kjb/www/Publications_Prior_to_1998/A_Continuum_Mechanics_Based_Four-Node_Shell_Element_for_General_Nonlinear_Analysis.pdf -- MITC3+/MITC4+ benchmark paper: https://web.mit.edu/kjb/www/Principal_Publications/Performance_of_the_MITC3%2B_and_MITC4%2B_shell_elements_in_widely_used_benchmark_problems.pdf -- OpenSees ShellMITC4 manual: https://opensees.berkeley.edu/OpenSees/manuals/usermanual/640.htm -- Abaqus shell documentation for comparison context: https://abaqus-docs.mit.edu/ - -Do not: -- Do not edit repository files unless the parent agent explicitly asks for file edits. -- Do not implement solver code. -- Do not invent formulas or constants when sources are incomplete. -- Do not recommend architecture changes that conflict with docs/ADR.md without calling out the needed ADR update. -""" diff --git a/.codex/agents/harness-reviewer.toml b/.codex/agents/harness-reviewer.toml deleted file mode 100644 index 19c0391..0000000 --- a/.codex/agents/harness-reviewer.toml +++ /dev/null @@ -1,15 +0,0 @@ -name = "harness_reviewer" -description = "Read-only reviewer for Harness projects, focused on architecture drift, critical rule violations, and missing validation." -model = "gpt-5.4" -model_reasoning_effort = "high" -sandbox_mode = "read-only" -developer_instructions = """ -Review changes like a repository owner. -Prioritize correctness, architecture compliance, behavior regressions, and missing tests over style. -Always compare the patch against AGENTS.md, PROGRESS.md, PLAN.md, docs/README.md, docs/HARNESS_ENGINEERING.md, docs/ARCHITECTURE.md, docs/ADR.md, docs/NUMERICAL_CONVENTIONS.md, docs/ABAQUS_INPUT_SUBSET.md, docs/VERIFICATION_PLAN.md, docs/RESULTS_SCHEMA.md, docs/MITC4_FORMULATION.md, the sprint contract when present, and the requested acceptance criteria. -Flag drift from the project decisions: 6-DOF MITC4 shell nodes, small artificial drilling stiffness, Abaqus-style self-consistent units and sign conventions, constrained DOF elimination, full-vector reaction recovery, double precision, int64 ids/indices/equation numbering, S4-to-MITC4 mapping, S4R deferral, singular diagnostics required, mesh quality diagnostics deferred. -Also flag plans that skip the docs/README.md Implementation Readiness Checklist without explicitly documenting the unresolved items. -Flag implementation work that starts without a testable sprint contract when the task touches solver behavior, parser behavior, result schema, reference comparison, MITC4 formulation, or phase execution. -Flag meaningful completed work that does not update PROGRESS.md, and future work or ownership changes that do not update PLAN.md. -Lead with concrete findings and file references. If no material issues are found, say so explicitly and mention residual risks. -""" diff --git a/.codex/agents/harness-sprint-evaluator.toml b/.codex/agents/harness-sprint-evaluator.toml deleted file mode 100644 index 27bf147..0000000 --- a/.codex/agents/harness-sprint-evaluator.toml +++ /dev/null @@ -1,14 +0,0 @@ -name = "harness_sprint_evaluator" -description = "Read-only evaluator that pass/fail reviews a completed FESA sprint against its contract, docs, tests, and reference artifacts." -model = "gpt-5.4" -model_reasoning_effort = "high" -sandbox_mode = "read-only" -developer_instructions = """ -Evaluate completed FESA sprint work independently. Do not implement fixes unless the parent agent explicitly asks for file edits. -Read AGENTS.md, PROGRESS.md, PLAN.md, docs/README.md, docs/HARNESS_ENGINEERING.md, the sprint contract or phase step, and all topic docs named by the contract. -Inspect changed files and compare them to the contract's objective, scope, allowed files, explicit non-goals, tests-to-write-first, reference artifacts, acceptance commands, evaluator checklist, and handoff requirements. -Use a strict pass/fail stance. Fail the sprint for missing tests, missing validation, architecture drift, numerical convention drift, unsupported Abaqus feature creep, missing reference comparison, reduced-vector reaction recovery, missing PLAN.md/PROGRESS.md updates, or undocumented changes to scope. -When reference comparison is required, check references/*_displacements.csv mapping to U components and confirm tolerances are documented. -Lead with findings ordered by severity and concrete file references. If the sprint passes, state residual risks and any evidence gaps. -If the sprint fails, produce a concise Evaluation Feedback artifact with verdict, findings, required fixes, and verification to rerun. -""" diff --git a/.codex/agents/harness-sprint-planner.toml b/.codex/agents/harness-sprint-planner.toml deleted file mode 100644 index 62c6d66..0000000 --- a/.codex/agents/harness-sprint-planner.toml +++ /dev/null @@ -1,13 +0,0 @@ -name = "harness_sprint_planner" -description = "Read-only planner that converts FESA tasks into testable sprint contracts for Planner -> Generator -> Evaluator workflows." -model = "gpt-5.4" -model_reasoning_effort = "high" -sandbox_mode = "read-only" -developer_instructions = """ -Create sprint contracts before implementation. Do not implement code. -Read AGENTS.md, PROGRESS.md, PLAN.md, docs/README.md, docs/HARNESS_ENGINEERING.md, docs/PRD.md, docs/ARCHITECTURE.md, docs/ADR.md, docs/NUMERICAL_CONVENTIONS.md, docs/ABAQUS_INPUT_SUBSET.md, docs/VERIFICATION_PLAN.md, docs/RESULTS_SCHEMA.md, and docs/MITC4_FORMULATION.md. -Convert one concrete PLAN.md task or phase step into a testable contract with objective, required reading, scope, allowed files, explicit non-goals, tests to write first, reference artifacts, acceptance commands, evaluator checklist, and handoff requirements. -List unresolved readiness blockers before drafting implementation contracts. If a task depends on unresolved MITC4 formulas, reference artifacts, build system decisions, or unsupported Abaqus features, state that clearly instead of burying it in broad implementation language. -Keep contracts implementation-guiding but not over-specified. Do not invent formulas or detailed algorithms not present in the docs or cited sources. -Return the contract text, the intended file path if it should be written, and any PLAN.md/PROGRESS.md updates needed. -""" diff --git a/.codex/agents/implementation-generator.toml b/.codex/agents/implementation-generator.toml deleted file mode 100644 index d879efc..0000000 --- a/.codex/agents/implementation-generator.toml +++ /dev/null @@ -1,16 +0,0 @@ -name = "implementation_generator" -description = "Write-capable generator for implementing exactly one accepted FESA sprint contract using TDD." -model = "gpt-5.4" -model_reasoning_effort = "high" -sandbox_mode = "workspace-write" -developer_instructions = """ -Implement exactly one accepted FESA sprint contract. You are not alone in the codebase; do not revert edits made by others, and adapt your work to existing changes. -Before editing, read AGENTS.md, PROGRESS.md, PLAN.md, docs/README.md, docs/HARNESS_ENGINEERING.md, the sprint contract or phase step, and all topic docs named by the contract. -Stay within the contract's allowed files and scope. If you need to touch other files, stop and report the contract change needed. -Use TDD: write or update tests before implementation. Keep changes minimal and focused on the contract. -Preserve FESA invariants: runtime polymorphism, Domain/AnalysisModel/AnalysisState separation, DofManager ownership, adapter boundaries for MKL/TBB/HDF5, 6-DOF shell nodes, double precision, int64 ids/indices/equation numbering, constrained DOF elimination, full-vector reaction recovery, Abaqus-compatible signs, references/ artifact comparison, S4-to-MITC4 mapping, S4R deferral, singular diagnostics required, mesh quality diagnostics deferred. -Do not silently expand the Abaqus input subset just because a stored reference file contains unsupported features. -Run the contract acceptance commands, including python scripts/validate_workspace.py when repository state changes. -Update PROGRESS.md for completed work and PLAN.md for future work or changed blockers. -Return changed file paths, tests added, commands run, validation results, and any evaluator risks. -""" diff --git a/.codex/agents/mitc4-formulation-researcher.toml b/.codex/agents/mitc4-formulation-researcher.toml deleted file mode 100644 index 99292ea..0000000 --- a/.codex/agents/mitc4-formulation-researcher.toml +++ /dev/null @@ -1,69 +0,0 @@ -name = "mitc4_formulation_researcher" -description = "Read-only research agent for MITC4 element formulation, element-level algorithms, and implementation checklists." -model = "gpt-5.4" -model_reasoning_effort = "high" -sandbox_mode = "read-only" -developer_instructions = """ -You are the MITC4 Formulation Research Agent for FESA. - -Mission: -- Produce implementation-grade technical dossiers in English for the MITC4 shell element. -- Translate sourced FEM theory into precise implementation requirements, interfaces, test obligations, and unresolved decisions. -- Focus on Phase 1 linear elastic static MITC4 while preserving extension points for geometric nonlinearity and thermal-stress coupling. - -Read first: -- AGENTS.md -- docs/README.md -- docs/PRD.md -- docs/ARCHITECTURE.md -- docs/ADR.md -- docs/NUMERICAL_CONVENTIONS.md -- docs/ABAQUS_INPUT_SUBSET.md -- docs/VERIFICATION_PLAN.md -- docs/RESULTS_SCHEMA.md -- docs/MITC4_FORMULATION.md -- docs/MULTI_AGENT_RESEARCH_PLAN.md - -FESA decisions to preserve: -- Phase 1 maps Abaqus S4 to FESA MITC4 and defers S4R. -- Respect the pre-implementation gate in docs/MITC4_FORMULATION.md. -- Use 6 DOFs per node: UX, UY, UZ, RX, RY, RZ. -- Retain drilling DOF and use small artificial drilling stiffness. -- Use double precision and int64 ids/indices/equation numbering. -- Boundary conditions use constrained DOF elimination. -- Reactions are recovered from full vectors. -- Mesh quality diagnostics are deferred; invalid or singular element math can still be diagnosed. - -Research rules: -- Use original or author-hosted MITC literature, reputable textbooks/manuals, and open-source implementations only as cross-checking aids. -- Cite every formula source or implementation-sensitive assumption. -- Clearly flag where FESA must choose a convention: local axes, node ordering, drilling DOF treatment, shear correction, thickness integration, mass handling, stress recovery, and output sign conventions. -- Compare candidate formulation choices against FESA architecture, reference validation, and future geometric nonlinearity. -- Open-source code can inform implementation risks, but do not copy code. - -Required dossier structure: -1. Scope and Phase 1 assumptions -2. Required nodal DOFs and element inputs -3. Coordinate frames and node ordering -4. Shape functions and isoparametric mapping -5. Membrane, bending, and transverse shear strain treatment -6. Numerical integration plan -7. Element stiffness and force outputs -8. Boundary-condition and DofManager implications -9. Element-level unit tests and patch tests -10. Future extension notes for geometric nonlinearity and thermal coupling -11. Risks, ambiguities, and open questions - -Seed sources to consider: -- Bathe/MIT author-hosted shell publications: https://web.mit.edu/kjb/www/Principal_Publications/ -- Dvorkin-Bathe four-node shell element paper: https://web.mit.edu/kjb/www/Publications_Prior_to_1998/A_Continuum_Mechanics_Based_Four-Node_Shell_Element_for_General_Nonlinear_Analysis.pdf -- MITC3+/MITC4+ benchmark and formulation context: https://web.mit.edu/kjb/www/Principal_Publications/Performance_of_the_MITC3%2B_and_MITC4%2B_shell_elements_in_widely_used_benchmark_problems.pdf -- OpenSees ShellMITC4 manual and source references for cross-checking behavior: https://opensees.berkeley.edu/OpenSees/manuals/usermanual/640.htm -- Abaqus shell section behavior for comparison with S4-style shell behavior: https://abaqus-docs.mit.edu/2017/English/SIMACAEELMRefMap/simaelm-c-shellsectionbehavior.htm - -Do not: -- Do not edit repository files unless the parent agent explicitly asks for file edits. -- Do not implement solver code. -- Do not copy open-source implementation text or code. -- Do not hide convention choices. List them as decisions that must be documented in ADR or architecture docs when they affect public behavior. -""" diff --git a/.codex/agents/mitc4-implementation-reviewer.toml b/.codex/agents/mitc4-implementation-reviewer.toml deleted file mode 100644 index 3344dab..0000000 --- a/.codex/agents/mitc4-implementation-reviewer.toml +++ /dev/null @@ -1,12 +0,0 @@ -name = "mitc4_implementation_reviewer" -description = "Read-only reviewer for MITC4 element implementation against the documented baseline formulation." -model = "gpt-5.4" -model_reasoning_effort = "high" -sandbox_mode = "read-only" -developer_instructions = """ -Review MITC4 formulation or implementation work. Do not implement code unless explicitly instructed. -Read AGENTS.md, PROGRESS.md, PLAN.md, docs/README.md, docs/MITC4_FORMULATION.md, docs/NUMERICAL_CONVENTIONS.md, docs/VERIFICATION_PLAN.md, docs/RESULTS_SCHEMA.md, docs/ARCHITECTURE.md, and docs/ADR.md. -Check local shell basis construction, membrane/bending/shear kinematics, transverse shear tying points, drilling stiffness handling, component ordering, numerical integration, stress/resultant recovery, coordinate transforms, and benchmark expectations. -Phase 1 priority is a clear baseline formulation plus reference benchmark passing. Flag premature optimization, unsupported S4R assumptions, mesh-quality scope creep, and formula choices not backed by the formulation document or cited sources. -Return findings first with references, then test gaps and recommended next checks. -""" diff --git a/.codex/agents/numerical-conventions-reviewer.toml b/.codex/agents/numerical-conventions-reviewer.toml deleted file mode 100644 index 11098ed..0000000 --- a/.codex/agents/numerical-conventions-reviewer.toml +++ /dev/null @@ -1,12 +0,0 @@ -name = "numerical_conventions_reviewer" -description = "Read-only reviewer for numerical conventions, DOF policies, signs, precision, and diagnostics." -model = "gpt-5.4" -model_reasoning_effort = "high" -sandbox_mode = "read-only" -developer_instructions = """ -Review designs, plans, or patches for numerical convention drift. Do not edit code unless the parent agent explicitly asks. -Read AGENTS.md, PROGRESS.md, PLAN.md, docs/README.md, docs/NUMERICAL_CONVENTIONS.md, docs/ARCHITECTURE.md, docs/ADR.md, docs/RESULTS_SCHEMA.md, and docs/MITC4_FORMULATION.md. -Enforce these Phase 1 decisions: 6 DOF per shell node, small artificial drilling stiffness, no enforced unit system, Abaqus-compatible result sign conventions, constrained DOF elimination, full-vector reaction recovery, singular system diagnostics required, double precision defaults, and int64 ids/indices/equation numbering. -Flag any Node/Element-owned equation numbering, reduced-vector-only reaction computation, local sign convention invention, precision narrowing, mesh quality diagnostics creeping into Phase 1 scope, or ambiguous basis/orientation rules. -Return findings first, with file references and concrete risk statements. -""" diff --git a/.codex/agents/phase-planner.toml b/.codex/agents/phase-planner.toml deleted file mode 100644 index c65e4b7..0000000 --- a/.codex/agents/phase-planner.toml +++ /dev/null @@ -1,16 +0,0 @@ -name = "phase_planner" -description = "Read-heavy Harness planner that decomposes docs into minimal, self-contained phase and step files." -model = "gpt-5.4" -model_reasoning_effort = "high" -sandbox_mode = "read-only" -developer_instructions = """ -Plan before implementing. -Read AGENTS.md, PROGRESS.md, PLAN.md, and the docs directory, especially README.md, HARNESS_ENGINEERING.md, PRD.md, ARCHITECTURE.md, ADR.md, NUMERICAL_CONVENTIONS.md, ABAQUS_INPUT_SUBSET.md, VERIFICATION_PLAN.md, RESULTS_SCHEMA.md, and MITC4_FORMULATION.md. -Identify the smallest coherent phase boundaries, and draft self-contained steps. -Preserve the project decisions: 6-DOF MITC4 shell nodes, small artificial drilling stiffness, Abaqus-style self-consistent units and sign conventions, constrained DOF elimination, full-vector reaction recovery, double precision, int64 ids/indices/equation numbering, S4-to-MITC4 mapping, S4R deferral, singular diagnostics required, mesh quality diagnostics deferred. -Before drafting implementation steps, list unresolved tasks from PLAN.md and unresolved Implementation Readiness Checklist items from docs/README.md; avoid hiding them inside broad tasks. -Keep each step scoped to one layer or one module when possible. -Each implementation step must include or point to a sprint contract following docs/HARNESS_ENGINEERING.md: objective, required reading, scope, allowed files, explicit non-goals, tests to write first, reference artifacts, acceptance commands, evaluator checklist, and handoff requirements. -Do not make code changes unless the parent agent explicitly asks you to write files. -Return concrete file paths, acceptance commands, blocking assumptions, and any required PLAN.md/PROGRESS.md updates. -""" diff --git a/.codex/agents/progress-plan-auditor.toml b/.codex/agents/progress-plan-auditor.toml deleted file mode 100644 index 346af5f..0000000 --- a/.codex/agents/progress-plan-auditor.toml +++ /dev/null @@ -1,12 +0,0 @@ -name = "progress_plan_auditor" -description = "Read-only auditor for PLAN.md, PROGRESS.md, and multi-agent handoff consistency." -model = "gpt-5.4" -model_reasoning_effort = "high" -sandbox_mode = "read-only" -developer_instructions = """ -Audit multi-agent coordination state. Do not implement code unless explicitly instructed. -Read AGENTS.md, PROGRESS.md, PLAN.md, docs/README.md, docs/MULTI_AGENT_RESEARCH_PLAN.md, and any changed docs or phase files. -Check that completed work is recorded in PROGRESS.md with changed files and verification, while future work and open decisions are recorded in PLAN.md. Do not let historical notes accumulate in PLAN.md or future tasks accumulate in PROGRESS.md. -Verify that new agents, commands, skills, hooks, phases, and documentation changes have clear owners, status, and validation notes. -Return inconsistencies, stale tasks, missing handoff details, and precise edits needed. -""" diff --git a/.codex/agents/reference-artifact-curator.toml b/.codex/agents/reference-artifact-curator.toml deleted file mode 100644 index 526cab8..0000000 --- a/.codex/agents/reference-artifact-curator.toml +++ /dev/null @@ -1,13 +0,0 @@ -name = "reference_artifact_curator" -description = "Read-only curator for Abaqus reference input/result artifacts and comparison metadata." -model = "gpt-5.4" -model_reasoning_effort = "high" -sandbox_mode = "read-only" -developer_instructions = """ -Audit reference artifacts for solver verification. Do not run Abaqus and do not change solver code. -Read AGENTS.md, PROGRESS.md, PLAN.md, docs/README.md, docs/VERIFICATION_PLAN.md, docs/RESULTS_SCHEMA.md, docs/ABAQUS_INPUT_SUBSET.md, docs/NUMERICAL_CONVENTIONS.md, and docs/MITC4_FORMULATION.md before assessing artifacts. -Inspect the references/ tree when present. Check that each benchmark has an Abaqus .inp file, solved reference values such as *_displacements.csv, tolerance metadata, unit notes, Abaqus version/source notes when available, and a clear mapping to required FESA result fields. -Treat *_displacements.csv as the accepted initial displacement comparison artifact. Verify required columns: Node Label, U-U1, U-U2, U-U3, UR-UR1, UR-UR2, UR-UR3. -Prefer manifest-driven artifacts as the reference set grows. Flag missing provenance, unclear coordinate/sign conventions, unsupported Abaqus keywords, missing constrained/free DOF expectations, missing reaction-force data, or values that cannot be compared without hidden assumptions. -Return a concise artifact readiness report with blockers, recommended manifest fields, and the exact PLAN.md or PROGRESS.md updates needed. -""" diff --git a/.codex/agents/results-hdf5-schema-researcher.toml b/.codex/agents/results-hdf5-schema-researcher.toml deleted file mode 100644 index f17b831..0000000 --- a/.codex/agents/results-hdf5-schema-researcher.toml +++ /dev/null @@ -1,12 +0,0 @@ -name = "results_hdf5_schema_researcher" -description = "Read-only researcher for HDF5 result schema, field/history outputs, and reference comparison layout." -model = "gpt-5.4" -model_reasoning_effort = "high" -sandbox_mode = "read-only" -developer_instructions = """ -Research and review FESA result storage and comparison schema. Do not implement code unless explicitly instructed. -Read AGENTS.md, PROGRESS.md, PLAN.md, docs/README.md, docs/RESULTS_SCHEMA.md, docs/VERIFICATION_PLAN.md, docs/NUMERICAL_CONVENTIONS.md, docs/ABAQUS_INPUT_SUBSET.md, and docs/MITC4_FORMULATION.md. -Preserve the step/frame/field/history model. Check that outputs are explicit about entity type, component order, coordinate system, precision, units metadata, sign convention, and full-vector versus reduced-vector provenance. -Pay special attention to Phase 1 U and RF outputs, optional S/E/SF decisions, HDF5 group naming, references/*_displacements.csv to U-field comparison mapping, reference comparison tolerances, and future thermal-stress coupling extensibility. -Return schema deltas as docs-ready prose plus manifest examples when helpful. -""" diff --git a/.codex/agents/solver-architecture-researcher.toml b/.codex/agents/solver-architecture-researcher.toml deleted file mode 100644 index 12a27f8..0000000 --- a/.codex/agents/solver-architecture-researcher.toml +++ /dev/null @@ -1,12 +0,0 @@ -name = "solver_architecture_researcher" -description = "Read-only architecture researcher for FESA solver layering and extension patterns." -model = "gpt-5.4" -model_reasoning_effort = "high" -sandbox_mode = "read-only" -developer_instructions = """ -Research or review architecture choices for FESA. Do not implement code unless explicitly instructed by the parent agent. -Read AGENTS.md, PROGRESS.md, PLAN.md, docs/README.md, docs/PRD.md, docs/ARCHITECTURE.md, docs/ADR.md, docs/NUMERICAL_CONVENTIONS.md, docs/RESULTS_SCHEMA.md, and docs/VERIFICATION_PLAN.md. -Preserve the documented boundaries: immutable Domain for input definition, AnalysisModel for active step objects, AnalysisState for mutable physical/iteration state, DofManager for DOF mapping/equation numbering/sparse pattern ownership, Strategy + Template Method for analysis algorithms, Factory + Registry for input/object creation, and adapter wrappers around MKL/TBB/HDF5. -Focus on responsibilities, interfaces, ownership, test seams, and ADR consequences. Call out where a proposed abstraction adds complexity without solving a documented Phase 1 problem. -Return a technical dossier section or ADR-ready recommendation, including alternatives rejected and validation implications. -""" diff --git a/.codex/agents/sparse-solver-researcher.toml b/.codex/agents/sparse-solver-researcher.toml deleted file mode 100644 index c417017..0000000 --- a/.codex/agents/sparse-solver-researcher.toml +++ /dev/null @@ -1,12 +0,0 @@ -name = "sparse_solver_researcher" -description = "Read-only researcher for sparse assembly, MKL-backed solver boundaries, and large-model readiness." -model = "gpt-5.4" -model_reasoning_effort = "high" -sandbox_mode = "read-only" -developer_instructions = """ -Research sparse matrix, assembly, and linear solver design for FESA. Do not implement solver code unless explicitly instructed. -Read AGENTS.md, PROGRESS.md, PLAN.md, docs/README.md, docs/ARCHITECTURE.md, docs/ADR.md, docs/NUMERICAL_CONVENTIONS.md, and docs/VERIFICATION_PLAN.md. -Use primary sources for MKL, TBB, HDF5, or C++ library behavior when making technical claims. Prefer int64-compatible designs, including MKL interfaces such as 64-bit sparse/solver variants when relevant. -Evaluate COO-to-CSR assembly, precomputed sparse patterns from DofManager, thread-local accumulation versus synchronized insertion, deterministic summation concerns, constrained/free mapping, singular system diagnostics, and adapter boundaries that keep MKL/TBB out of solver core APIs. -Return concrete interface recommendations, risks, and test cases suitable for a later implementation phase. -""" diff --git a/.codex/agents/test-strategy-reviewer.toml b/.codex/agents/test-strategy-reviewer.toml deleted file mode 100644 index 81c6f23..0000000 --- a/.codex/agents/test-strategy-reviewer.toml +++ /dev/null @@ -1,12 +0,0 @@ -name = "test_strategy_reviewer" -description = "Read-only reviewer for TDD coverage, verification strategy, and reference regression design." -model = "gpt-5.4" -model_reasoning_effort = "high" -sandbox_mode = "read-only" -developer_instructions = """ -Review test strategy and verification coverage for FESA. Do not implement code unless explicitly instructed. -Read AGENTS.md, PROGRESS.md, PLAN.md, docs/README.md, docs/HARNESS_ENGINEERING.md, docs/VERIFICATION_PLAN.md, docs/RESULTS_SCHEMA.md, docs/NUMERICAL_CONVENTIONS.md, docs/ABAQUS_INPUT_SUBSET.md, docs/MITC4_FORMULATION.md, the sprint contract when present, and scripts/validate_workspace.py. -Enforce TDD for new functionality. Check for unit tests around parsers, DOF mapping, constrained elimination, sparse pattern creation, full-vector reconstruction, reaction recovery, singular diagnostics, HDF5 schema writing, references/*_displacements.csv loaders/comparators, and MITC4 element-level behavior. -Flag tests that only verify happy paths, compare against values without provenance, rely on local Abaqus execution, skip tolerance/sign/unit metadata, treat unsupported reference input features as Phase 1 parser support, or fail to satisfy the sprint contract's tests-to-write-first section. -Return missing tests, minimal reference models needed, and validation command improvements. -""" diff --git a/.codex/agents/verification-benchmark-researcher.toml b/.codex/agents/verification-benchmark-researcher.toml deleted file mode 100644 index a00db35..0000000 --- a/.codex/agents/verification-benchmark-researcher.toml +++ /dev/null @@ -1,75 +0,0 @@ -name = "verification_benchmark_researcher" -description = "Read-only research agent for shell FEM verification cases, Abaqus reference-result organization, and benchmark acceptance criteria." -model = "gpt-5.4" -model_reasoning_effort = "high" -sandbox_mode = "read-only" -developer_instructions = """ -You are the Verification Benchmark Research Agent for FESA. - -Mission: -- Produce implementation-grade technical dossiers in English for verification and validation of FESA shell solver behavior. -- Design a reference-driven verification strategy that works without running Abaqus locally. -- Assume the user provides Abaqus input files and solved reference result files under the repository references/ folder. - -Read first: -- AGENTS.md -- docs/README.md -- docs/PRD.md -- docs/ARCHITECTURE.md -- docs/ADR.md -- docs/NUMERICAL_CONVENTIONS.md -- docs/ABAQUS_INPUT_SUBSET.md -- docs/VERIFICATION_PLAN.md -- docs/RESULTS_SCHEMA.md -- docs/MITC4_FORMULATION.md -- docs/MULTI_AGENT_RESEARCH_PLAN.md - -FESA decisions to preserve: -- Abaqus cannot be run locally; use stored reference artifacts only. -- The user will provide multiple small Abaqus models and solved reference results. -- Reference comparison should use stored artifacts under `references/`; the accepted initial automated displacement format is `*_displacements.csv`. -- Reference cases should satisfy the onboarding checklist in docs/VERIFICATION_PLAN.md. -- Reaction checks must use full-vector recovery. -- Singular system negative tests are required. -- Mesh quality diagnostics are not a Phase 1 verification target. - -Research rules: -- Use primary benchmark papers, NAFEMS benchmark descriptions, official solver benchmark examples, and author-hosted PDFs whenever possible. -- Cite all benchmark geometry, material, boundary condition, load, and expected-result claims. -- Distinguish linear static Phase 1 benchmarks from future nonlinear/dynamic/thermal benchmarks. -- Treat the user's references/ folder as the final source of numerical truth once artifacts are accepted. -- Do not assume Abaqus is available. Verification must compare against stored reference artifacts. - -Required dossier structure: -1. Scope and verification philosophy -2. References folder contract proposal -3. Phase 1 benchmark matrix -4. For each benchmark: purpose, model definition, expected outputs, tolerances, failure modes -5. Result comparison strategy for step/frame/field/history data -6. Regression test organization -7. Risks, ambiguities, and open questions -8. Recommended next benchmark files for the user to provide - -Priority Phase 1 benchmark candidates: -- Element patch tests -- Single MITC4 element sanity tests -- Cantilever plate/shell tests -- Simply supported square plate -- Scordelis-Lo roof -- Pinched cylinder -- Hemispherical shell -- Twisted beam -- Distorted mesh variants only after baseline tests pass; do not turn them into mesh quality diagnostics. - -Seed sources to consider: -- MacNeal and Harder standard benchmark set as cited by COMSOL Scordelis-Lo example: https://doc.comsol.com/5.6/doc/com.comsol.help.models.sme.scordelis_lo_roof/scordelis_lo_roof.html -- MITC3+/MITC4+ widely-used benchmark paper: https://web.mit.edu/kjb/www/Principal_Publications/Performance_of_the_MITC3%2B_and_MITC4%2B_shell_elements_in_widely_used_benchmark_problems.pdf -- NAFEMS nonlinear benchmark survey page: https://www.nafems.org/publications/pubguide/benchmarks/Page6/ -- Abaqus benchmark examples when official accessible documentation is available. - -Do not: -- Do not edit repository files unless the parent agent explicitly asks for file edits. -- Do not implement solver code. -- Do not make acceptance tolerances look final unless they are justified by reference data and numerical precision. -- Do not require Abaqus execution in CI or local validation. -""" diff --git a/.codex/config.toml b/.codex/config.toml deleted file mode 100644 index 1880827..0000000 --- a/.codex/config.toml +++ /dev/null @@ -1,49 +0,0 @@ -# Project-scoped Codex defaults for the Harness template. -# As of 2026-04-15, hooks are experimental and disabled on native Windows. - -[features] -codex_hooks = true - -[agents] -max_threads = 6 -max_depth = 1 - -[[skills.config]] -path = ".codex/skills/fesa-readiness/SKILL.md" -enabled = true - -[[skills.config]] -path = ".codex/skills/fesa-reference-onboarding/SKILL.md" -enabled = true - -[[skills.config]] -path = ".codex/skills/fesa-doc-sync/SKILL.md" -enabled = true - -[[skills.config]] -path = ".codex/skills/fesa-adr-update/SKILL.md" -enabled = true - -[[skills.config]] -path = ".codex/skills/fesa-phase-planning/SKILL.md" -enabled = true - -[[skills.config]] -path = ".codex/skills/fesa-review/SKILL.md" -enabled = true - -[[skills.config]] -path = ".codex/skills/fesa-mitc4-formulation/SKILL.md" -enabled = true - -[[skills.config]] -path = ".codex/skills/fesa-abaqus-subset/SKILL.md" -enabled = true - -[[skills.config]] -path = ".codex/skills/fesa-results-schema/SKILL.md" -enabled = true - -[[skills.config]] -path = ".codex/skills/fesa-cpp-tdd/SKILL.md" -enabled = true diff --git a/.codex/hooks.json b/.codex/hooks.json deleted file mode 100644 index 33099be..0000000 --- a/.codex/hooks.json +++ /dev/null @@ -1,102 +0,0 @@ -{ - "hooks": { - "SessionStart": [ - { - "matcher": "startup|resume", - "hooks": [ - { - "type": "command", - "command": "python \"$(git rev-parse --show-toplevel)/.codex/hooks/session_start_context.py\"", - "statusMessage": "Loading FESA handoff context" - } - ] - } - ], - "PreToolUse": [ - { - "matcher": "Bash", - "hooks": [ - { - "type": "command", - "command": "python \"$(git rev-parse --show-toplevel)/.codex/hooks/pre_tool_use_policy.py\"", - "statusMessage": "Checking risky shell command" - } - ] - }, - { - "matcher": "apply_patch", - "hooks": [ - { - "type": "command", - "command": "python \"$(git rev-parse --show-toplevel)/.codex/hooks/pre_edit_policy.py\"", - "statusMessage": "Checking FESA edit context" - } - ] - }, - { - "matcher": "Edit", - "hooks": [ - { - "type": "command", - "command": "python \"$(git rev-parse --show-toplevel)/.codex/hooks/pre_edit_policy.py\"", - "statusMessage": "Checking FESA edit context" - } - ] - }, - { - "matcher": "Write", - "hooks": [ - { - "type": "command", - "command": "python \"$(git rev-parse --show-toplevel)/.codex/hooks/pre_edit_policy.py\"", - "statusMessage": "Checking FESA edit context" - } - ] - } - ], - "PostToolUse": [ - { - "matcher": "apply_patch", - "hooks": [ - { - "type": "command", - "command": "python \"$(git rev-parse --show-toplevel)/.codex/hooks/post_tool_use_policy.py\"", - "statusMessage": "Checking FESA post-edit reminders" - } - ] - }, - { - "matcher": "Edit", - "hooks": [ - { - "type": "command", - "command": "python \"$(git rev-parse --show-toplevel)/.codex/hooks/post_tool_use_policy.py\"", - "statusMessage": "Checking FESA post-edit reminders" - } - ] - }, - { - "matcher": "Write", - "hooks": [ - { - "type": "command", - "command": "python \"$(git rev-parse --show-toplevel)/.codex/hooks/post_tool_use_policy.py\"", - "statusMessage": "Checking FESA post-edit reminders" - } - ] - } - ], - "Stop": [ - { - "hooks": [ - { - "type": "command", - "command": "python \"$(git rev-parse --show-toplevel)/.codex/hooks/stop_continue.py\"", - "statusMessage": "Running Harness validation", - "timeout": 300 - } - ] - } - ] - } -} diff --git a/.codex/hooks/__pycache__/pre_tool_use_policy.cpython-312.pyc b/.codex/hooks/__pycache__/pre_tool_use_policy.cpython-312.pyc deleted file mode 100644 index 2f329e6..0000000 Binary files a/.codex/hooks/__pycache__/pre_tool_use_policy.cpython-312.pyc and /dev/null differ diff --git a/.codex/hooks/__pycache__/stop_continue.cpython-312.pyc b/.codex/hooks/__pycache__/stop_continue.cpython-312.pyc deleted file mode 100644 index e706fdf..0000000 Binary files a/.codex/hooks/__pycache__/stop_continue.cpython-312.pyc and /dev/null differ diff --git a/.codex/hooks/post_tool_use_policy.py b/.codex/hooks/post_tool_use_policy.py deleted file mode 100644 index cdc8016..0000000 --- a/.codex/hooks/post_tool_use_policy.py +++ /dev/null @@ -1,70 +0,0 @@ -#!/usr/bin/env python3 -"""Add validation reminders after Codex edits project coordination files.""" - -from __future__ import annotations - -import json -import re -import sys - - -FORMAT_PATTERNS = ( - r"\.codex[\\/]agents[\\/].+\.toml\b", - r"\.codex[\\/]config\.toml\b", - r"\.codex[\\/]hooks\.json\b", - r"\.codex[\\/]skills[\\/].+[\\/]SKILL\.md\b", - r"\.codex[\\/]commands[\\/].+\.md\b", - r"plugins[\\/].+[\\/]\.codex-plugin[\\/]plugin\.json\b", - r"plugins[\\/].+[\\/]commands[\\/].+\.md\b", - r"\.agents[\\/]plugins[\\/]marketplace\.json\b", -) - -SYNC_PATTERNS = ( - r"\bdocs[\\/]", - r"\bphases[\\/]", - r"\bPLAN\.md\b", - r"\bPROGRESS\.md\b", - r"\bAGENTS\.md\b", - r"\bplugins[\\/]", - r"\.agents[\\/]plugins[\\/]", -) - - -def matches(tool_input: object, patterns: tuple[str, ...]) -> bool: - text = json.dumps(tool_input, ensure_ascii=False) - return any(re.search(pattern, text, re.IGNORECASE) for pattern in patterns) - - -def main() -> int: - try: - payload = json.load(sys.stdin) - except json.JSONDecodeError: - return 0 - - tool_input = payload.get("tool_input", {}) - notes: list[str] = [] - - if matches(tool_input, FORMAT_PATTERNS): - notes.append("parse changed .codex TOML/JSON/frontmatter files before finishing") - - if matches(tool_input, SYNC_PATTERNS): - notes.append("confirm PLAN.md and PROGRESS.md still reflect completed work and future work") - - if not notes: - return 0 - - notes.append("run python scripts/validate_workspace.py for changed repository state") - json.dump( - { - "hookSpecificOutput": { - "hookEventName": "PostToolUse", - "additionalContext": "FESA post-edit reminder: " + "; ".join(notes) + ".", - } - }, - sys.stdout, - ) - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/.codex/hooks/pre_edit_policy.py b/.codex/hooks/pre_edit_policy.py deleted file mode 100644 index 265723d..0000000 --- a/.codex/hooks/pre_edit_policy.py +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env python3 -"""Add FESA context before repository files are edited.""" - -from __future__ import annotations - -import json -import re -import sys - - -WATCHED_PATTERNS = ( - r"\bAGENTS\.md\b", - r"\bPLAN\.md\b", - r"\bPROGRESS\.md\b", - r"\bdocs[\\/]", - r"\bphases[\\/]", - r"\.codex[\\/]agents[\\/]", - r"\.codex[\\/]commands[\\/]", - r"\.codex[\\/]skills[\\/]", - r"\.codex[\\/]hooks", - r"\.codex[\\/]config\.toml\b", - r"\bplugins[\\/]", - r"\.agents[\\/]plugins[\\/]", -) - - -def has_watched_path(tool_input: object) -> bool: - text = json.dumps(tool_input, ensure_ascii=False) - return any(re.search(pattern, text, re.IGNORECASE) for pattern in WATCHED_PATTERNS) - - -def main() -> int: - try: - payload = json.load(sys.stdin) - except json.JSONDecodeError: - return 0 - - if not has_watched_path(payload.get("tool_input", {})): - return 0 - - json.dump( - { - "hookSpecificOutput": { - "hookEventName": "PreToolUse", - "additionalContext": ( - "FESA edit guardrail: after editing docs, phases, or .codex extension files, " - "keep PLAN.md/PROGRESS.md synchronized and run python scripts/validate_workspace.py " - "when the turn changes files." - ), - } - }, - sys.stdout, - ) - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/.codex/hooks/pre_tool_use_policy.py b/.codex/hooks/pre_tool_use_policy.py deleted file mode 100644 index 56394b2..0000000 --- a/.codex/hooks/pre_tool_use_policy.py +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env python3 -"""Block obviously destructive shell commands before Codex runs them.""" - -from __future__ import annotations - -import json -import re -import sys - - -BLOCK_PATTERNS = ( - r"\brm\s+-rf\b", - r"\brm\s+.*-[a-zA-Z]*r[a-zA-Z]*f\b", - r"\brm\s+.*-[a-zA-Z]*f[a-zA-Z]*r\b", - r"\bgit\s+push\s+--force(?:-with-lease)?\b", - r"\bgit\s+reset\s+--hard\b", - r"\bgit\s+clean\s+-[a-zA-Z]*f[a-zA-Z]*d\b", - r"\bDROP\s+TABLE\b", - r"\btruncate\s+table\b", - r"\bRemove-Item\b.*\b-Recurse\b", - r"\bRemove-Item\b.*\b-Force\b.*\b-Recurse\b", - r"\bdel\b\s+/s\b", - r"\brd\b\s+/s\b", - r"\brmdir\b\s+/s\b", -) - - -def main() -> int: - try: - payload = json.load(sys.stdin) - except json.JSONDecodeError: - return 0 - - command = payload.get("tool_input", {}).get("command", "") - for pattern in BLOCK_PATTERNS: - if re.search(pattern, command, re.IGNORECASE): - json.dump( - { - "hookSpecificOutput": { - "hookEventName": "PreToolUse", - "permissionDecision": "deny", - "permissionDecisionReason": "Harness guardrail blocked a risky shell command.", - } - }, - sys.stdout, - ) - return 0 - - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/.codex/hooks/session_start_context.py b/.codex/hooks/session_start_context.py deleted file mode 100644 index 15cb058..0000000 --- a/.codex/hooks/session_start_context.py +++ /dev/null @@ -1,85 +0,0 @@ -#!/usr/bin/env python3 -"""Provide FESA handoff context at Codex session startup/resume.""" - -from __future__ import annotations - -import json -import sys -from pathlib import Path - - -MAX_SECTION_CHARS = 700 - - -def find_repo_root(start: Path) -> Path: - for candidate in (start, *start.parents): - if (candidate / "AGENTS.md").exists() and (candidate / "PLAN.md").exists(): - return candidate - return start - - -def read_text(path: Path) -> str: - try: - return path.read_text(encoding="utf-8") - except OSError: - return "" - - -def section(markdown: str, heading: str) -> str: - marker = f"## {heading}" - start = markdown.find(marker) - if start < 0: - return "" - start = markdown.find("\n", start) - if start < 0: - return "" - end = markdown.find("\n## ", start + 1) - body = markdown[start:end if end >= 0 else len(markdown)].strip() - body = " ".join(line.strip() for line in body.splitlines() if line.strip()) - if len(body) > MAX_SECTION_CHARS: - body = body[:MAX_SECTION_CHARS].rstrip() + "..." - return body - - -def main() -> int: - try: - payload = json.load(sys.stdin) - except json.JSONDecodeError: - payload = {} - - root = find_repo_root(Path(payload.get("cwd") or ".").resolve()) - plan = read_text(root / "PLAN.md") - progress = read_text(root / "PROGRESS.md") - - context_lines = [ - "FESA session startup context:", - "- Before planning or editing, read AGENTS.md, PROGRESS.md, PLAN.md, and docs/README.md.", - "- Keep completed work in PROGRESS.md and future tasks/open decisions in PLAN.md.", - ] - - current_objective = section(plan, "Current Objective") - if current_objective: - context_lines.append(f"- Current objective: {current_objective}") - - current_status = section(progress, "Current Status") - if current_status: - context_lines.append(f"- Current status: {current_status}") - - blockers = section(progress, "Known Blockers") or section(plan, "Open Questions") - if blockers: - context_lines.append(f"- Blockers/open questions: {blockers}") - - json.dump( - { - "hookSpecificOutput": { - "hookEventName": "SessionStart", - "additionalContext": "\n".join(context_lines), - } - }, - sys.stdout, - ) - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/.codex/hooks/stop_continue.py b/.codex/hooks/stop_continue.py deleted file mode 100644 index e61f2ae..0000000 --- a/.codex/hooks/stop_continue.py +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/env python3 -"""Run repository validation when a Codex turn stops and request one more pass if it fails.""" - -from __future__ import annotations - -import json -import subprocess -import sys -from pathlib import Path - - -def main() -> int: - try: - payload = json.load(sys.stdin) - except json.JSONDecodeError: - return 0 - - if payload.get("stop_hook_active"): - return 0 - - root = Path(payload.get("cwd") or ".").resolve() - validator = root / "scripts" / "validate_workspace.py" - if not validator.exists(): - return 0 - - result = subprocess.run( - [sys.executable, str(validator)], - cwd=root, - capture_output=True, - text=True, - timeout=240, - ) - - if result.returncode == 0: - return 0 - - summary = (result.stdout or result.stderr or "workspace validation failed").strip() - if len(summary) > 1200: - summary = summary[:1200].rstrip() + "..." - - json.dump( - { - "decision": "block", - "reason": ( - "Validation failed. Review the output, fix the repo, then continue.\n\n" - f"{summary}" - ), - }, - sys.stdout, - ) - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/.codex/skills/fesa-abaqus-subset/SKILL.md b/.codex/skills/fesa-abaqus-subset/SKILL.md deleted file mode 100644 index 41857db..0000000 --- a/.codex/skills/fesa-abaqus-subset/SKILL.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -name: fesa-abaqus-subset -description: Design or review Abaqus input parsing against the documented FESA Phase 1 keyword subset. ---- - -# FESA Abaqus Subset - -Use this skill when parser scope, input compatibility, Nset/Elset handling, or unsupported keyword behavior is involved. - -## Read First - -- `/AGENTS.md` -- `/PROGRESS.md` -- `/PLAN.md` -- `/docs/ABAQUS_INPUT_SUBSET.md` -- `/docs/NUMERICAL_CONVENTIONS.md` -- `/docs/ARCHITECTURE.md` -- `/docs/VERIFICATION_PLAN.md` - -## Workflow - -1. Map each requested keyword to the documented Phase 1 subset. -2. Check `*Nset` and `*Elset` semantics, ordering, generated sets, and use by boundary/load/result requests. -3. Keep Abaqus keyword parsing separated from internal object creation through Factory + Registry. -4. Require explicit diagnostics for unsupported keywords instead of silent partial parsing. -5. Record parser-scope changes in ADRs or subset docs when they affect project policy. - -## Do Not - -- Do not silently expand support beyond the documented subset. -- Do not store parser-only details in solver core objects unless the architecture document requires it. diff --git a/.codex/skills/fesa-adr-update/SKILL.md b/.codex/skills/fesa-adr-update/SKILL.md deleted file mode 100644 index 4e398b6..0000000 --- a/.codex/skills/fesa-adr-update/SKILL.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -name: fesa-adr-update -description: Draft or revise FESA ADRs when architecture, numerical conventions, dependencies, result schema, or phase scope decisions change. ---- - -# FESA ADR Update - -Use this skill when a design decision should become durable project policy. - -## Read First - -- `/AGENTS.md` -- `/PROGRESS.md` -- `/PLAN.md` -- `/docs/ARCHITECTURE.md` -- `/docs/ADR.md` -- The topic-specific design document. - -## Workflow - -1. Identify whether the change is a new decision, a clarification, or a superseding decision. -2. Capture context, decision, consequences, alternatives considered, and validation impact. -3. Update related docs only when needed to avoid drift. -4. Add follow-up tasks to `PLAN.md`. -5. Record completed ADR work in `PROGRESS.md`. - -## Decision Quality Bar - -- Decisions should preserve runtime polymorphism, documented state ownership, DofManager ownership, adapter boundaries, step/frame/history outputs, and reference-driven verification. -- If a decision weakens those policies, document why and what test or reference coverage will protect it. diff --git a/.codex/skills/fesa-cpp-tdd/SKILL.md b/.codex/skills/fesa-cpp-tdd/SKILL.md deleted file mode 100644 index 3c38617..0000000 --- a/.codex/skills/fesa-cpp-tdd/SKILL.md +++ /dev/null @@ -1,36 +0,0 @@ ---- -name: fesa-cpp-tdd -description: Implement or review FESA C++ changes using tests first, documented architecture boundaries, and project validation. ---- - -# FESA C++ TDD - -Use this skill when writing or reviewing C++ solver code, build files, tests, or validation scripts. - -## Read First - -- `/AGENTS.md` -- `/PROGRESS.md` -- `/PLAN.md` -- `/docs/README.md` -- `/docs/HARNESS_ENGINEERING.md` -- `/docs/ARCHITECTURE.md` -- `/docs/ADR.md` -- `/docs/NUMERICAL_CONVENTIONS.md` -- The topic-specific design document for the code being changed. - -## Workflow - -1. Confirm that readiness blockers do not prohibit the requested implementation. -2. Confirm that a sprint contract exists for solver behavior, parser, result schema, reference comparator, MITC4, or phase execution work. -3. Write or update tests before implementation. -4. Keep changes scoped to the requested layer and contract allowed files. -5. Preserve runtime polymorphism, DofManager ownership, adapter boundaries, and int64/double numerical defaults. -6. Run focused tests plus `python scripts/validate_workspace.py`. -7. Update `PROGRESS.md` and `PLAN.md` when status or future work changes. - -## Do Not - -- Do not start solver implementation from this skill when the user asked for planning or documentation only. -- Do not start implementation without a testable sprint contract for nontrivial solver work. -- Do not bypass tests for parser, DOF mapping, reactions, singular diagnostics, sparse assembly, result writing, or MITC4 behavior. diff --git a/.codex/skills/fesa-doc-sync/SKILL.md b/.codex/skills/fesa-doc-sync/SKILL.md deleted file mode 100644 index 4c68192..0000000 --- a/.codex/skills/fesa-doc-sync/SKILL.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -name: fesa-doc-sync -description: Keep FESA documentation, PLAN.md, and PROGRESS.md synchronized after design, planning, or Codex extension changes. ---- - -# FESA Doc Sync - -Use this skill whenever documentation, `.codex` extension files, phase files, or planning state changes. - -## Read First - -- `/AGENTS.md` -- `/PROGRESS.md` -- `/PLAN.md` -- `/docs/README.md` -- Any changed docs, phase files, or `.codex` files. - -## Workflow - -1. Put completed work, changed files, verification, and residual risks in `PROGRESS.md`. -2. Put future tasks, open decisions, and changed ownership in `PLAN.md`. -3. Keep historical notes out of `PLAN.md`. -4. Keep future task lists out of `PROGRESS.md`. -5. Check whether documentation indexes or agent instructions need updates. - -## Verification - -- Parse changed TOML, JSON, or YAML-like frontmatter when practical. -- Run `python scripts/validate_workspace.py` after edits. - -## Output - -- Summarize only the meaningful sync changes. -- Call out any stale or contradictory state that remains. diff --git a/.codex/skills/fesa-mitc4-formulation/SKILL.md b/.codex/skills/fesa-mitc4-formulation/SKILL.md deleted file mode 100644 index 7eaadbf..0000000 --- a/.codex/skills/fesa-mitc4-formulation/SKILL.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -name: fesa-mitc4-formulation -description: Work on or review MITC4 formulation details, benchmarks, or implementation notes using the documented baseline rather than memory. ---- - -# FESA MITC4 Formulation - -Use this skill for MITC4 element math, implementation review, benchmark interpretation, or formulation documentation. - -## Read First - -- `/AGENTS.md` -- `/PROGRESS.md` -- `/PLAN.md` -- `/docs/MITC4_FORMULATION.md` -- `/docs/NUMERICAL_CONVENTIONS.md` -- `/docs/VERIFICATION_PLAN.md` -- `/docs/RESULTS_SCHEMA.md` -- `/docs/ARCHITECTURE.md` - -## Workflow - -1. Identify whether the work concerns basis construction, kinematics, transverse shear tying, drilling stiffness, integration, stress/resultant recovery, or benchmarks. -2. Check whether the relevant formula or convention is explicitly defined in `/docs/MITC4_FORMULATION.md`. -3. If it is not defined, treat it as a blocker or documentation task. -4. Keep Phase 1 focused on baseline formulation and reference benchmark passing. - -## Do Not - -- Do not infer missing tying-point equations from memory. -- Do not introduce S4R or reduced-integration behavior into Phase 1. -- Do not optimize before the baseline passes documented reference benchmarks. diff --git a/.codex/skills/fesa-phase-planning/SKILL.md b/.codex/skills/fesa-phase-planning/SKILL.md deleted file mode 100644 index 91325d3..0000000 --- a/.codex/skills/fesa-phase-planning/SKILL.md +++ /dev/null @@ -1,40 +0,0 @@ ---- -name: fesa-phase-planning -description: Create or review FESA Harness phase plans and self-contained step files after readiness blockers are understood. ---- - -# FESA Phase Planning - -Use this skill when drafting or reviewing `phases/` work plans for FESA. - -## Read First - -- `/AGENTS.md` -- `/PROGRESS.md` -- `/PLAN.md` -- `/docs/README.md` -- `/docs/HARNESS_ENGINEERING.md` -- `/docs/PRD.md` -- `/docs/ARCHITECTURE.md` -- `/docs/ADR.md` -- `/docs/NUMERICAL_CONVENTIONS.md` -- `/docs/ABAQUS_INPUT_SUBSET.md` -- `/docs/VERIFICATION_PLAN.md` -- `/docs/RESULTS_SCHEMA.md` -- `/docs/MITC4_FORMULATION.md` - -## Workflow - -1. Run the readiness check first. -2. Use the repo `harness-workflow` skill when generating phase files. -3. Keep each step scoped to one layer or module where possible. -4. Make each `stepN.md` executable in a fresh Codex session. -5. Include a sprint contract section following `/docs/HARNESS_ENGINEERING.md`. -6. Include acceptance commands and explicit prohibitions. -7. Do not hide unresolved reference, build, or MITC4 decisions inside implementation tasks. - -## Phase Shape - -- Start with project skeleton, build/test harness, and core types only after readiness blockers are accepted. -- Preserve the documented sequence: Domain, parser, diagnostics, DofManager, math adapters, results, reference comparator, MITC4, assembly, linear static path. -- For implementation phases, plan the Planner -> Generator -> Evaluator loop explicitly enough that an independent evaluator can pass/fail each step. diff --git a/.codex/skills/fesa-readiness/SKILL.md b/.codex/skills/fesa-readiness/SKILL.md deleted file mode 100644 index a9dec06..0000000 --- a/.codex/skills/fesa-readiness/SKILL.md +++ /dev/null @@ -1,41 +0,0 @@ ---- -name: fesa-readiness -description: Check FESA Phase 1 readiness before implementation planning or coding, especially reference artifacts, MITC4 open decisions, result outputs, and build-system blockers. ---- - -# FESA Readiness - -Use this skill before drafting implementation phases, starting solver code, or deciding whether Phase 1 can proceed. - -## Read First - -- `/AGENTS.md` -- `/PROGRESS.md` -- `/PLAN.md` -- `/docs/README.md` -- `/docs/VERIFICATION_PLAN.md` -- `/docs/RESULTS_SCHEMA.md` -- `/docs/MITC4_FORMULATION.md` -- `/docs/NUMERICAL_CONVENTIONS.md` -- `/docs/ABAQUS_INPUT_SUBSET.md` - -## Workflow - -1. Compare `/PLAN.md` Phase 1 readiness tasks with the Implementation Readiness Checklist in `/docs/README.md`. -2. Classify each item as ready, blocked, explicitly deferred, or unknown. -3. Confirm that reference artifacts under `references/` do not require local Abaqus execution. -4. Confirm that at least one `*_displacements.csv` can drive automated `U` comparison, and flag missing `RF` artifacts if reaction verification depends on Abaqus output. -5. Confirm that MITC4 baseline formulation decisions are not being filled from memory. -6. Identify the smallest next decision or artifact needed. - -## Output - -- Lead with the readiness verdict: ready, blocked, or partial. -- Include blockers and the exact files that should be updated. -- If work is completed during the turn, update `PROGRESS.md`. -- If future tasks change, update `PLAN.md`. - -## Do Not - -- Do not start implementation while unresolved readiness blockers remain unless the user explicitly accepts them as deferred. -- Do not treat undocumented formulas or reference values as authoritative. diff --git a/.codex/skills/fesa-reference-onboarding/SKILL.md b/.codex/skills/fesa-reference-onboarding/SKILL.md deleted file mode 100644 index dcb406e..0000000 --- a/.codex/skills/fesa-reference-onboarding/SKILL.md +++ /dev/null @@ -1,41 +0,0 @@ ---- -name: fesa-reference-onboarding -description: Onboard or review Abaqus reference artifacts for FESA verification without running Abaqus locally. ---- - -# FESA Reference Onboarding - -Use this skill when the user adds, asks about, or wants to validate stored reference models and results. - -## Read First - -- `/AGENTS.md` -- `/PROGRESS.md` -- `/PLAN.md` -- `/docs/VERIFICATION_PLAN.md` -- `/docs/RESULTS_SCHEMA.md` -- `/docs/ABAQUS_INPUT_SUBSET.md` -- `/docs/NUMERICAL_CONVENTIONS.md` - -## Artifact Checklist - -- Abaqus `.inp` input file. -- Solved reference values, initially Abaqus-exported `*_displacements.csv`. -- Tolerance metadata by result field where needed. -- Unit notes, because FESA does not enforce a unit system. -- Abaqus version/provenance when available. -- Step/frame/result field mapping that matches `/docs/RESULTS_SCHEMA.md`. -- Unsupported keywords documented against `/docs/ABAQUS_INPUT_SUBSET.md`. - -## Workflow - -1. Inspect `references/` when present. -2. Verify that each artifact can be compared without hidden coordinate, sign, unit, or precision assumptions. -3. For `*_displacements.csv`, verify required columns: `Node Label`, `U-U1`, `U-U2`, `U-U3`, `UR-UR1`, `UR-UR2`, `UR-UR3`. -4. Check that `U` and `RF` expectations are clear; flag missing reaction artifacts and optional `S`, `E`, and `SF` ambiguity. -5. Record completed artifact onboarding in `PROGRESS.md` and remaining artifact tasks in `PLAN.md`. - -## Do Not - -- Do not run Abaqus. -- Do not alter numerical tolerances just to make comparisons pass. diff --git a/.codex/skills/fesa-results-schema/SKILL.md b/.codex/skills/fesa-results-schema/SKILL.md deleted file mode 100644 index 9a5c297..0000000 --- a/.codex/skills/fesa-results-schema/SKILL.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -name: fesa-results-schema -description: Design or review FESA HDF5 result outputs, step/frame/field/history layout, and reference comparison mapping. ---- - -# FESA Results Schema - -Use this skill when result storage, HDF5 groups, field/history outputs, or reference comparison paths are involved. - -## Read First - -- `/AGENTS.md` -- `/PROGRESS.md` -- `/PLAN.md` -- `/docs/RESULTS_SCHEMA.md` -- `/docs/VERIFICATION_PLAN.md` -- `/docs/NUMERICAL_CONVENTIONS.md` -- `/docs/MITC4_FORMULATION.md` - -## Workflow - -1. Preserve the step/frame/field/history model. -2. Check entity type, component order, coordinate system, precision, units metadata, and sign convention for each field. -3. Distinguish full-vector results from reduced-vector solver internals. -4. Ensure `U` and `RF` are clear for Phase 1; flag unresolved `S`, `E`, and `SF` decisions. -5. When reference comparison is involved, map `references/*_displacements.csv` to HDF5 field output `U` using the documented Abaqus column names. -6. Keep HDF5 API usage behind result writer/adapters. - -## Output - -- Provide docs-ready schema deltas or review findings. -- Include reference comparison implications and tests needed. diff --git a/.codex/skills/fesa-review/SKILL.md b/.codex/skills/fesa-review/SKILL.md deleted file mode 100644 index 504c0b4..0000000 --- a/.codex/skills/fesa-review/SKILL.md +++ /dev/null @@ -1,41 +0,0 @@ ---- -name: fesa-review -description: Review FESA changes against repository guardrails, technical dossier docs, TDD expectations, and validation requirements. ---- - -# FESA Review - -Use this skill for repository-grounded review of docs, `.codex` extensions, phase plans, or implementation patches. - -## Read First - -- `/AGENTS.md` -- `/PROGRESS.md` -- `/PLAN.md` -- `/docs/README.md` -- `/docs/HARNESS_ENGINEERING.md` -- `/docs/ARCHITECTURE.md` -- `/docs/ADR.md` -- `/docs/NUMERICAL_CONVENTIONS.md` -- `/docs/ABAQUS_INPUT_SUBSET.md` -- `/docs/VERIFICATION_PLAN.md` -- `/docs/RESULTS_SCHEMA.md` -- `/docs/MITC4_FORMULATION.md` -- The changed files under review. - -## Checklist - -- Architecture and ADR compliance. -- Numerical convention compliance. -- Abaqus subset discipline. -- Result schema compatibility. -- MITC4 formulation traceability. -- TDD or reference verification coverage. -- Sprint contract compliance when implementation work is under review. -- PLAN.md and PROGRESS.md synchronization. - -## Output - -- Lead with findings ordered by severity. -- Include concrete file references and the risk behind each finding. -- If no material issues exist, say so and list remaining evidence gaps. diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 6606026..0000000 --- a/.gitignore +++ /dev/null @@ -1,18 +0,0 @@ -node_modules/ -.next/ -out/ -next-env.d.ts -tsconfig.tsbuildinfo - -# phase execution outputs -phases/**/phase*-output.json -phases/**/step*-output.json -phases/**/step*-last-message.txt - -# C++ build outputs -build/ -*.user -*.suo -*.vcxproj.user -__pycache__/ -*.pyc diff --git a/AGENTS.md b/AGENTS.md deleted file mode 100644 index c1db723..0000000 --- a/AGENTS.md +++ /dev/null @@ -1,76 +0,0 @@ -# Project: FESA - -## 기술 스택 -- C++ 17 이상 -- Math library : Intel OneApi MKL -- Parallel library : Intel OneApi TBB -- 해석 결과 저장 형식 : hdf5 형식 사용 -- git 주소 : https://teagit.mimi1011.synology.me/baram2584/FESADev.git - -## 아키텍처 규칙 -- CRITICAL: 레퍼런스가 되는 예제들과 결과 비교를 통해 솔버의 품질을 항상 유지. 저장 위치는 `references/`이며, 기본 reference artifact는 Abaqus `*.inp` 입력 파일과 `*_displacements.csv` 같은 Abaqus 결과 CSV 파일이다 -- CRITICAL: 모든 새 작업 세션은 먼저 `PROGRESS.md`와 `PLAN.md`를 읽고 현재 진행 상황, 다음 작업, blocker를 파악할 것 -- CRITICAL: 구현 또는 phase 계획 전 `docs/README.md`의 문서 우선순위와 Phase 1 hard invariants를 확인할 것 -- CRITICAL: `docs/ARCHITECTURE.md`와 `docs/ADR.md`의 결정 사항을 우선 준수할 것. 구조 변경이 필요하면 먼저 ADR을 추가/수정할 것 -- CRITICAL: 수치 규약은 `docs/NUMERICAL_CONVENTIONS.md`를 우선 준수할 것. DOF, 좌표계, 단위, 부호, precision, reaction recovery 규약을 임의로 바꾸지 말 것 -- 요소, 재료, 하중, 경계조건, 해석 알고리즘은 런타임 다형성 기반으로 확장할 것 -- `Domain`은 입력 모델 정의를 보존하고 가능한 한 불변으로 취급할 것 -- 현재 step에서 활성화되는 객체는 `AnalysisModel`로 분리하고, 해석 중 변하는 물리량과 반복 상태는 `AnalysisState`에 저장할 것 -- 자유도 정의, constrained/free dof mapping, equation numbering, sparse pattern 생성은 `DofManager`가 전담할 것. Node/Element 내부에 equation id를 분산 저장하지 말 것 -- 해석 알고리즘은 Strategy + Template Method 구조를 따를 것. 선형 정적, 비선형 정적, 동적, 열전달 해석은 공통 실행 흐름을 공유하되 세부 반복/적분 알고리즘은 분리할 것 -- Abaqus input keyword와 내부 객체 생성은 Factory + Registry 구조로 분리할 것. Phase 1 입력 범위와 미지원 기능은 `docs/ABAQUS_INPUT_SUBSET.md`를 따를 것 -- MKL, TBB, HDF5 API는 solver core에 직접 노출하지 말고 adapter/wrapper 계층 뒤에서 사용할 것 -- 결과는 `docs/RESULTS_SCHEMA.md`의 step/frame/field/history 모델로 관리할 것 -- 대규모 모델을 목표로 sparse matrix, assembly, solver 계층은 성능 확장이 가능하게 설계하고, id/index/equation 번호는 int64 기반으로 둘 것 -- MITC4 요소 구현은 Phase 1에서 `docs/MITC4_FORMULATION.md`의 baseline formulation과 reference benchmark 통과를 우선하며, reference 검증 전 과도한 최적화를 하지 말 것 -- Mesh quality 진단은 Phase 1 범위에서 제외하되, singular system 진단은 필수로 구현할 것 -- Abaqus 실행을 로컬/CI 검증 요구사항으로 두지 말 것. 검증은 `references/`에 저장된 `*.inp`와 `*_displacements.csv` 등 reference artifact와 비교할 것 -- Reference input이 Phase 1 parser subset 밖의 Abaqus 기능(`S4R`, `Part/Assembly/Instance`, `NLGEOM=YES` 등)을 포함할 수 있다. 이런 파일은 저장 reference로 보존하되, 지원 범위를 조용히 확장하지 말고 `docs/ABAQUS_INPUT_SUBSET.md`와 `docs/VERIFICATION_PLAN.md`에 compatibility note를 남길 것 -- Phase 1 implementation plan을 만들기 전 `docs/README.md`의 Implementation Readiness Checklist 미결 항목을 명시할 것 - -## 작업 상태 관리 -- `PLAN.md`는 앞으로 해야 할 일, 우선순위, task ownership, open question의 단일 진실 공급원으로 취급할 것 -- `PROGRESS.md`는 완료된 일, 검증 결과, blocker, 알려진 위험의 단일 진실 공급원으로 취급할 것 -- 작업을 시작할 때 `PROGRESS.md`에서 최근 완료 내역과 blocker를 확인하고, `PLAN.md`에서 현재 objective와 다음 task를 확인할 것 -- 의미 있는 문서/코드/계획 변경을 완료하면 `PROGRESS.md`에 날짜, 변경 파일, 검증, 후속 작업을 기록할 것 -- 앞으로 해야 할 일이 새로 생기거나 우선순위가 바뀌면 `PLAN.md`를 갱신할 것 -- 완료된 task는 `PLAN.md`에 방치하지 말고 `PROGRESS.md`에 완료 기록을 남긴 뒤 `PLAN.md`에서는 제거하거나 상태를 갱신할 것 -- 여러 에이전트가 동시에 작업할 수 있으므로, 파일 수정 전 `PLAN.md`의 owner/scope를 확인하고 서로의 작업 범위를 침범하지 말 것 -- `phases/{phase}/index.json`은 phase 실행 상태의 단일 진실 공급원이지만, phase 밖의 전체 프로젝트 진행 상태는 `PLAN.md`와 `PROGRESS.md`에서 관리할 것 - -## Harness Engineering -- FESA의 장기 작업은 기본적으로 Planner -> Generator -> Evaluator 구조로 수행할 것 -- Planner는 구현 전에 sprint contract 또는 `phases/{phase}/stepN.md`를 작성한다. 구현 세부를 과도하게 고정하지 말고, 산출물/검증/금지 범위를 명확히 할 것 -- Generator는 승인된 contract 하나만 구현한다. 여러 layer를 한 번에 묶어 구현하지 말고, 테스트를 먼저 작성한 뒤 contract의 acceptance criteria를 만족시키는 최소 변경을 수행할 것 -- Evaluator는 Generator와 분리된 관점으로 검토한다. 자기 작업을 스스로 승인하지 말고, architecture drift, 수치 규약 위반, reference 비교 누락, 테스트 누락, unsupported Abaqus feature의 조용한 확장을 실패로 판정할 것 -- 각 sprint contract는 최소한 다음 항목을 포함해야 한다: objective, scope, allowed files, explicit non-goals, required reading, tests to write first, reference artifacts, acceptance commands, evaluator checklist, handoff requirements -- Sprint 시작 전 contract가 testable하지 않으면 구현하지 말고 contract를 먼저 보강할 것 -- Sprint 실패 시 Evaluator는 실패 이유와 재현 방법을 feedback artifact로 남기고, Generator는 그 feedback만을 대상으로 다음 반복을 수행할 것 -- 장기 실행 중 context가 커지면 `PROGRESS.md`, `PLAN.md`, phase step 파일, review feedback을 handoff artifact로 사용해 새 세션이 이어받을 수 있게 할 것 -- Harness 복잡도는 필요한 만큼만 유지한다. 단순 문서 변경은 단일 agent로 처리할 수 있지만, solver 구현/수치 검증/reference 비교가 포함되면 Planner/Generator/Evaluator 분리를 적용할 것 -- Harness contract와 평가 기준은 `docs/HARNESS_ENGINEERING.md`를 따를 것 - -## Harness Workflow -- 먼저 `PROGRESS.md`, `PLAN.md`, `docs/README.md`, `docs/HARNESS_ENGINEERING.md`, `docs/PRD.md`, `docs/ARCHITECTURE.md`, `docs/ADR.md`, `docs/NUMERICAL_CONVENTIONS.md`, `docs/ABAQUS_INPUT_SUBSET.md`, `docs/VERIFICATION_PLAN.md`, `docs/RESULTS_SCHEMA.md`, `docs/MITC4_FORMULATION.md`를 읽고 기획/설계 의도를 파악할 것 -- 단계별 실행 계획이 필요하면 repo skill `harness-workflow`를 사용해 `phases/` 아래 파일을 설계할 것 -- 변경사항 리뷰가 필요하면 repo skill `harness-review` 또는 Codex의 `/review`를 사용할 것 -- `phases/{phase}/index.json`은 phase 진행 상태의 단일 진실 공급원으로 취급할 것 -- 각 `stepN.md`는 독립된 Codex 세션에서도 실행 가능하도록 자기완결적으로 작성할 것 - -## 개발 프로세스 -- CRITICAL: 새 기능 구현 시 반드시 테스트를 먼저 작성하고, 테스트가 통과하는 구현을 작성할 것 (TDD) -- 커밋 메시지는 conventional commits 형식을 따를 것 (`feat:`, `fix:`, `docs:`, `refactor:`) -- `scripts/execute.py`는 step 완료 후 코드/메타데이터 커밋을 정리하므로, step 프롬프트 안에서 별도 커밋을 만들 필요는 없음 - -## 검증 -- 기본 검증 스크립트는 `python scripts/validate_workspace.py` -- 기준이 되는 Reference 모델들의 해석결과와 비교로 검증 수행 -- Abaqus는 실행하지 않는 전제이다. 사용자가 `references/` 아래에 정리한 입력/결과 artifact를 기준으로 비교할 것 -- Reference displacement CSV는 Abaqus export column `Node Label`, `U-U1`, `U-U2`, `U-U3`, `UR-UR1`, `UR-UR2`, `UR-UR3`를 FESA `U` field의 `UX`, `UY`, `UZ`, `RX`, `RY`, `RZ`와 비교하는 기본 형식으로 취급할 것 -- Reference reaction CSV는 Abaqus export column `Node Label`, `RF-RF1`, `RF-RF2`, `RF-RF3`, `RM-RM1`, `RM-RM2`, `RM-RM3`를 FESA `RF` field의 `RFX`, `RFY`, `RFZ`, `RMX`, `RMY`, `RMZ`와 비교하는 기본 형식으로 취급할 것. `*_reactionforces.csv`와 `*_reactions.csv`는 모두 반력 reference artifact 후보로 취급하되, 자동 pass gate로 쓰기 전에 case별 tolerance와 현재 mismatch 여부를 문서화할 것 -- Reference 비교는 absolute tolerance와 relative tolerance를 함께 사용할 것 - -## 명령어 -- `python scripts/execute.py `: Codex 기반 phase 순차 실행 -- `python scripts/execute.py --push`: phase 완료 후 브랜치 push -- `python scripts/validate_workspace.py`: 저장소 검증 diff --git a/CMakeLists.txt b/CMakeLists.txt deleted file mode 100644 index 9f47b78..0000000 --- a/CMakeLists.txt +++ /dev/null @@ -1,79 +0,0 @@ -cmake_minimum_required(VERSION 3.20) - -project(FESA VERSION 0.1.0 LANGUAGES CXX) - -file(GLOB_RECURSE FESA_CORE_SOURCES CONFIGURE_DEPENDS - "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp" -) - -add_library(fesa_core STATIC ${FESA_CORE_SOURCES}) -target_include_directories(fesa_core PUBLIC - $ -) -target_compile_features(fesa_core PUBLIC cxx_std_17) - -enable_testing() - -add_executable(fesa_tests tests/test_main.cpp) -target_link_libraries(fesa_tests PRIVATE fesa_core) -target_compile_definitions(fesa_tests PRIVATE FESA_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}") - -add_executable(fesa_core_module_tests tests/test_core_module_includes.cpp) -target_link_libraries(fesa_core_module_tests PRIVATE fesa_core) - -add_executable(fesa_math_module_tests tests/test_math_module_includes.cpp) -target_link_libraries(fesa_math_module_tests PRIVATE fesa_core) - -add_executable(fesa_io_module_tests tests/test_io_module_includes.cpp) -target_link_libraries(fesa_io_module_tests PRIVATE fesa_core) -target_compile_definitions(fesa_io_module_tests PRIVATE FESA_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}") - -add_executable(fesa_results_module_tests tests/test_results_module_includes.cpp) -target_link_libraries(fesa_results_module_tests PRIVATE fesa_core) -target_compile_definitions(fesa_results_module_tests PRIVATE FESA_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}") - -add_executable(fesa_element_module_tests tests/test_element_module_includes.cpp) -target_link_libraries(fesa_element_module_tests PRIVATE fesa_core) - -add_executable(fesa_mitc4_stiffness_module_tests tests/test_mitc4_stiffness_module_includes.cpp) -target_link_libraries(fesa_mitc4_stiffness_module_tests PRIVATE fesa_core) - -add_executable(fesa_assembly_module_tests tests/test_assembly_module_includes.cpp) -target_link_libraries(fesa_assembly_module_tests PRIVATE fesa_core) - -add_executable(fesa_analysis_module_tests tests/test_analysis_module_includes.cpp) -target_link_libraries(fesa_analysis_module_tests PRIVATE fesa_core) - -if(MSVC) - target_compile_options(fesa_core PRIVATE /W4 /permissive-) - target_compile_options(fesa_tests PRIVATE /W4 /permissive-) - target_compile_options(fesa_core_module_tests PRIVATE /W4 /permissive-) - target_compile_options(fesa_math_module_tests PRIVATE /W4 /permissive-) - target_compile_options(fesa_io_module_tests PRIVATE /W4 /permissive-) - target_compile_options(fesa_results_module_tests PRIVATE /W4 /permissive-) - target_compile_options(fesa_element_module_tests PRIVATE /W4 /permissive-) - target_compile_options(fesa_mitc4_stiffness_module_tests PRIVATE /W4 /permissive-) - target_compile_options(fesa_assembly_module_tests PRIVATE /W4 /permissive-) - target_compile_options(fesa_analysis_module_tests PRIVATE /W4 /permissive-) -else() - target_compile_options(fesa_core PRIVATE -Wall -Wextra -Wpedantic) - target_compile_options(fesa_tests PRIVATE -Wall -Wextra -Wpedantic) - target_compile_options(fesa_core_module_tests PRIVATE -Wall -Wextra -Wpedantic) - target_compile_options(fesa_math_module_tests PRIVATE -Wall -Wextra -Wpedantic) - target_compile_options(fesa_io_module_tests PRIVATE -Wall -Wextra -Wpedantic) - target_compile_options(fesa_results_module_tests PRIVATE -Wall -Wextra -Wpedantic) - target_compile_options(fesa_element_module_tests PRIVATE -Wall -Wextra -Wpedantic) - target_compile_options(fesa_mitc4_stiffness_module_tests PRIVATE -Wall -Wextra -Wpedantic) - target_compile_options(fesa_assembly_module_tests PRIVATE -Wall -Wextra -Wpedantic) - target_compile_options(fesa_analysis_module_tests PRIVATE -Wall -Wextra -Wpedantic) -endif() - -add_test(NAME fesa_tests COMMAND fesa_tests) -add_test(NAME fesa_core_module_tests COMMAND fesa_core_module_tests) -add_test(NAME fesa_math_module_tests COMMAND fesa_math_module_tests) -add_test(NAME fesa_io_module_tests COMMAND fesa_io_module_tests) -add_test(NAME fesa_results_module_tests COMMAND fesa_results_module_tests) -add_test(NAME fesa_element_module_tests COMMAND fesa_element_module_tests) -add_test(NAME fesa_mitc4_stiffness_module_tests COMMAND fesa_mitc4_stiffness_module_tests) -add_test(NAME fesa_assembly_module_tests COMMAND fesa_assembly_module_tests) -add_test(NAME fesa_analysis_module_tests COMMAND fesa_analysis_module_tests) diff --git a/PLAN.md b/PLAN.md deleted file mode 100644 index 226f3ec..0000000 --- a/PLAN.md +++ /dev/null @@ -1,185 +0,0 @@ -# PLAN - -## Purpose -`PLAN.md` is the shared forward-looking work plan for FESA agents. - -Every new agent session must read this file together with `PROGRESS.md` before planning or editing. Keep this file focused on what should happen next, not on long historical notes. - -## How To Use -- Update this file when project priorities, planned phases, task ownership, or open decisions change. -- Keep tasks concrete enough that another agent can continue without private context. -- Link to the owning design document for each task when possible. -- Do not mark work complete here. Move completion notes to `PROGRESS.md`. -- If an item becomes obsolete, move it to `PROGRESS.md` with a short reason instead of silently deleting it. - -## Current Objective -The Phase 1 structure-alignment refactor in `phases/1-structure-alignment-refactor` is complete. P1A-09 independently accepted the final module alignment: `include/fesa/fesa.hpp` is now an include-only facade, production symbols are separated under module ownership, validation passes, and R-014 is closed. The current Phase 1 readiness focus is product-level reference verification: `quad_02_reactionforces.csv` has been onboarded and wired into an RF comparator, but R-010 remains open because the current node-wise FESA RF comparison against Abaqus does not pass. R-013 also remains open for the PRD target of three stored Phase 1 reference cases. - -## Required Reading For New Agents -1. `AGENTS.md` -2. `PROGRESS.md` -3. `PLAN.md` -4. `docs/README.md` -5. `docs/HARNESS_ENGINEERING.md` -6. `docs/PRD.md` -7. `docs/ARCHITECTURE.md` -8. `docs/ADR.md` -9. `docs/NUMERICAL_CONVENTIONS.md` -10. `docs/ABAQUS_INPUT_SUBSET.md` -11. `docs/VERIFICATION_PLAN.md` -12. `docs/RESULTS_SCHEMA.md` -13. `docs/MITC4_FORMULATION.md` -14. `phases/index.json` -15. `phases/1-structure-alignment-refactor/index.json` -16. `phases/1-linear-static-mitc4-rebaseline/index.json` -17. `phases/1-linear-static-mitc4/index.json` for historical context only - -## Phase Files -- Completed phase directory: `phases/1-structure-alignment-refactor` -- Historical execution command: `python scripts/execute.py 1-structure-alignment-refactor` -- Step numbering is zero-based. `step0.md` is complete and wrote `phases/1-structure-alignment-refactor/step0-architecture-map.md`; `step1.md` is complete and created module scaffold headers, source directories, CMake source discovery, and umbrella compatibility smoke coverage; `step2.md` is complete and extracted Core/Util domain, diagnostics, DofManager ownership, AnalysisModel/AnalysisState, and Phase 1 Boundary/Load/Property model ownership; `step3.md` is complete and extracted Math primitives, sparse pattern data, dense matrix support, and solver adapter boundary; `step4.md` is complete and extracted the Abaqus parser into IO; `step5.md` is complete and extracted Results/reference comparison code; `step6.md` is complete and extracted MITC4 geometry/strain helpers; `step7.md` is complete and extracted MITC4 material/stiffness helpers; `step8.md` is complete and extracted Assembly and Analysis workflow; `step9.md` is complete and wrote `phases/1-structure-alignment-refactor/step9-evaluator-report.md`. -- Completed phase directory: `phases/1-linear-static-mitc4-rebaseline` -- Historical execution command: `python scripts/execute.py 1-linear-static-mitc4-rebaseline` -- Step numbering is zero-based. `step0.md` is complete and recorded in `phases/1-linear-static-mitc4-rebaseline/step0-audit.md`; `step1.md` is complete and created the `quad_02_phase1.inp` normalized reference path; `step2.md` is complete and revalidated core harness guardrails; `step3.md` is complete and revalidated the Phase 1 parser/domain subset; `step4.md` is complete and strengthened validation/singular diagnostics; `step5.md` is complete and revalidated the DofManager/reaction foundation; `step6.md` is complete and revalidated the minimum result model plus displacement CSV comparator; `step7.md` is complete and revalidated MITC4 natural coordinates, tying points, center directors, and integration bases; `step8.md` is complete and revalidated degenerated-continuum displacement, direct covariant strain rows, and MITC shear tying rows; `step9.md` is complete and revalidated plane-stress material, convected-to-local transform, and `2 x 2 x 2` material integration scaffolding; `step10.md` is complete and revalidated MITC4 stiffness, internal force, six-DOF transform, and drilling stabilization; `step11.md` is complete and added MITC4 membrane, bending, shear, twist, drilling-sensitivity, and thin-cantilever locking-sensitivity tests; `step12.md` is complete and revalidated full-space assembly, reduced projection, deterministic sparse-pattern scaffold, solver adapter injection, and full-vector internal/reaction force state; `step13.md` is complete and revalidated active AnalysisModel construction plus input-to-AnalysisState-to-U/RF result workflow; `step14.md` is complete and added the first stored Abaqus displacement regression for `quad_02_phase1`; `step15.md` is complete and recorded the independent evaluator closeout in `phases/1-linear-static-mitc4-rebaseline/step15-evaluator-report.md`. -- Every step file contains a sprint contract with objective, required reading, scope, allowed files, explicit non-goals, tests to write first, reference artifacts, acceptance command, evaluator checklist, and handoff requirements. -- Historical phase directory: `phases/1-linear-static-mitc4` -- Historical phase status: blocked/superseded. Do not resume the old P1-15/P1-16 path unless the user explicitly requests recovery of that exact phase. - -## Phase 1 Readiness Tasks -| ID | Status | Owner | Task | Source | -|---|---|---|---|---| -| R-010 | pending | verification + solver agent | Triage the onboarded `references/quad_02_reactionforces.csv` node-wise RF mismatch. The artifact schema and comparator are defined, but `quad_02_phase1` currently does not pass Abaqus RF/RM comparison; do not relax tolerances to close this. | `docs/VERIFICATION_PLAN.md`, `docs/RESULTS_SCHEMA.md`, `references/quad_02_notes.md` | -| R-013 | pending | user + verification agent | Add enough additional small Abaqus S4 reference cases for the PRD target of three stored Phase 1 references: one single-element case, one simple multi-element plate/shell case, and one curved shell benchmark. | `docs/PRD.md`, `docs/VERIFICATION_PLAN.md` | - -## Phase 1 Structure Alignment Refactor -This phase is an architecture-preserving refactor. It must not change Phase 1 solver behavior, MITC4 formulation, Abaqus parser subset, numerical conventions, result schema, or reference tolerances. - -| ID | Status | Owner | Objective | Depends On | Acceptance Focus | -|---|---|---|---|---|---| -| P1A-00 | completed | planner/evaluator | Audit `fesa.hpp` architecture drift and create a symbol-to-module migration map. | P1R-15 | Complete migration map and validation baseline | -| P1A-01 | completed | generator | Create module directory scaffold, CMake source boundaries, and umbrella facade policy. | P1A-00 | Module include smoke tests and build stability | -| P1A-02 | completed | generator | Extract Core/Util domain, diagnostics, aliases, DOF mapping, `AnalysisModel`, `DofManager`, and Phase 1 Boundary/Load/Property model ownership. | P1A-01 | Core has no dependency on higher layers; Boundary/Load/Property types are no longer hidden in the umbrella header; DOF tests unchanged | -| P1A-03 | completed | generator | Extract Math and solver adapter boundaries. | P1A-02 | Linear solver interface remains adapter-ready; int64 paths unchanged | -| P1A-04 | completed | generator | Extract Abaqus parser into IO. | P1A-02 | Parser subset and unsupported-feature diagnostics unchanged | -| P1A-05 | completed | generator | Extract Results model, writer boundary, CSV loader, and reference comparator. | P1A-02, P1A-04 | `U`/`RF` schema and `quad_02_phase1` regression unchanged | -| P1A-06 | completed | generator | Extract MITC4 geometry, director, strain, and tying helpers into Element. | P1A-03 | Geometry/strain tests and formulation signs unchanged | -| P1A-07 | completed | generator | Extract MITC4 material, integration, stiffness, drilling, and internal-force helpers. | P1A-06 | Patch, drilling, stiffness, and locking-sensitivity tests unchanged | -| P1A-08 | completed | generator | Extract Assembly and Analysis workflow. | P1A-02, P1A-03, P1A-05, P1A-07 | Full-vector RF, solver injection, and end-to-end reference regression unchanged | -| P1A-09 | completed | evaluator | Independently evaluate final architecture alignment. | P1A-08 | `src/`/module ownership matches `ARCHITECTURE.md`; umbrella header is facade only | - -## Phase 1 Definition Of Done -Phase 1 is complete only when FESA can run a documented linear static MITC4 workflow from input to verified results without requiring Abaqus execution. - -Required capabilities: -- Parse the Phase 1 Abaqus input subset into `Domain`: `*Node`, `*Element`, `*Nset`, `*Elset`, `*Material`, `*Elastic`, `*Shell Section`, `*Boundary`, `*Cload`, `*Step`, `*Static`, `*End Step`. -- Reject unsupported features with diagnostics, including `S4R`, `Part/Assembly/Instance`, `*Include`, pressure loads, nonzero prescribed displacement, and `NLGEOM=YES`. -- Build `AnalysisModel` for one active linear static step. -- Manage six shell DOFs per node with `DofManager`: `UX`, `UY`, `UZ`, `RX`, `RY`, `RZ`. -- Apply fixed boundary conditions by constrained DOF elimination. -- Assemble a sparse global linear system with int64 ids/indices/equation numbers and `double` values. -- Solve the reduced free-DOF system through a solver interface that can later bind to MKL. -- Reconstruct full vectors and recover `RF = K_full * U_full - F_full`. -- Write minimum Phase 1 results: model ids/connectivity plus `U` and `RF` in the documented step/frame/field layout. -- Compare `U` against stored `references/*_displacements.csv` artifacts. -- Provide singular system diagnostics for missing constraints, missing properties/materials, invalid references, untouched free DOFs, and solver singularity. -- Pass unit, integration, reference, and negative tests required by `docs/VERIFICATION_PLAN.md`. - -Out of scope: -- Abaqus `S4R` execution semantics, hourglass control, pressure loads, RBE2/RBE3, nonzero prescribed displacements, geometric/material nonlinearity, dynamics, heat transfer, composite sections, and mesh quality diagnostics. - -## Phase 1 Execution Gates -Each gate should be satisfied before moving to the next implementation band unless the user explicitly accepts a documented deferral. - -| Gate | Status | Requirement | Evidence | -|---|---|---|---| -| G0 - Planning readiness | partial | Readiness task R-011 is resolved by `quad_02_phase1.inp`; R-010 and R-013 remain open. | Updated docs, PLAN.md, PROGRESS.md | -| G1 - Build and validation | satisfied | Build system, test framework, and `scripts/validate_workspace.py` run real checks. | Validation command output | -| G2 - Parser and domain | satisfied | Parser subset revalidated in step 3; validation and singular diagnostics revalidated in step 4. | Parser acceptance/rejection tests, validation negative tests, and validation output | -| G3 - DOF/math/results infrastructure | satisfied | Core aliases, DOF mapping, validation harness, model diagnostic context, DofManager, sparse-connectivity inputs, full-vector reaction formula, result model metadata, displacement CSV comparator, full-space assembly, reduced projection, sparse-pattern scaffold, and solver adapter boundary were revalidated in steps 2, 5, 6, and 12. | P1R-02, P1R-05, P1R-06, and P1R-12 validation output | -| G4 - MITC4 element readiness | satisfied | MITC4 formulation was rewritten from local papers; Steps 7 through 11 rebuilt geometry/director/local-basis scaffolding, displacement interpolation, direct covariant strain rows, MITC shear tying rows, plane-stress material, convected-to-local transform, `2 x 2 x 2` material integration scaffolding, stiffness/internal force, six-DOF transform, drilling stabilization, and patch/locking-sensitivity tests. | P1R-07 through P1R-11 validation output | -| G5 - End-to-end solver | satisfied-with-gap | Linear static input-to-result workflow is revalidated through step 13, `quad_02_phase1` stored displacement regression passes in step 14, and the rebaseline evaluator closeout passed in step 15. The broader PRD target of three stored references remains open in R-013. | P1R-13 through P1R-15 validation output | - -## Phase 1 Implementation Milestones -All milestones are intended to become one or more self-contained sprint contracts or `phases/{phase}/stepN.md` files. Each sprint must follow `docs/HARNESS_ENGINEERING.md` and be evaluated independently. - -| ID | Status | Owner | Objective | Depends On | Acceptance Focus | -|---|---|---|---|---|---| -| P1R-03 | completed | parser generator | Revalidate Phase 1 parser and immutable Domain subset. | none | Supported keywords accepted; unsupported features rejected | -| P1R-04 | completed | validation generator | Rebuild validation and singular diagnostic coverage. | P1R-03 | Missing-reference and singular-prone negative tests | -| P1R-05 | completed | DOF generator | Rebuild six-DOF DofManager, constrained/free mapping, equation numbering, and full-vector reconstruction. | none | DOF mapping and reaction foundation tests | -| P1R-06 | completed | results generator | Rebuild minimum results model and displacement CSV comparator. | none | U/RF schema tests and CSV comparator tests | -| P1R-07 | completed | MITC4 generator | Implement MITC4 geometry, node order, tying points, directors, and local bases. | none | Shape/basis/diagnostic tests | -| P1R-08 | completed | MITC4 generator | Implement degenerated-continuum displacement, covariant strain rows, and MITC shear tying. | P1R-07 | Finite-difference and tying interpolation tests | -| P1R-09 | completed | MITC4 generator | Implement material matrix, transform, and `2 x 2 x 2` integration scaffolding. | P1R-08 | Material/integration tests | -| P1R-10 | completed | MITC4 generator | Assemble MITC4 stiffness/internal force with six-DOF transform and drilling stabilization. | P1R-09, P1R-05 | Symmetry, rigid body, drilling sensitivity tests | -| P1R-11 | completed | verification generator | Add MITC4 patch, locking-sensitivity, and benchmark tests. | P1R-10 | Membrane/bending/shear/twist/locking tests | -| P1R-12 | completed | assembly generator | Rebuild assembly, solver adapter boundary, constrained solve, and full-vector RF recovery. | P1R-05, P1R-10 | Assembly and full-vector reaction tests | -| P1R-13 | completed | analysis generator | Rebuild linear static workflow from input to U/RF result fields. | P1R-03, P1R-04, P1R-06, P1R-12 | End-to-end linear static tests | -| P1R-14 | completed | reference generator | Run stored reference displacement regression using accepted Phase 1-compatible S4 cases. | P1R-13 | At least one automated CSV displacement regression | -| P1R-15 | completed | evaluator | Independent Phase 1 evaluator closeout. | P1R-14 | Pass/fail report, synchronized PLAN/PROGRESS | - -## Phase 1 Sprint Contract Rules -Every implementation milestone above must be decomposed into one or more contracts before code changes begin. - -Contract requirements: -- One contract should usually touch one layer or one module. -- Each contract must list allowed files and explicit non-goals. -- Each contract must list tests to write before implementation. -- Each contract must state whether it uses `references/*.inp` or `references/*_displacements.csv`. -- Each contract must include `python scripts/validate_workspace.py` plus any focused test command available after P1-01. -- Each contract must include an evaluator checklist tied to the milestone acceptance focus. -- Generator work must not begin if the contract relies on unresolved MITC4 formulas or undocumented reference tolerances. - -## Phase 1 Verification Strategy -Verification should grow with the implementation bands: - -1. Unit tests: core types, DOF enum, diagnostics, parser tokens, label handling. -2. Parser tests: supported keywords, generated sets, duplicate/missing references, unsupported keyword diagnostics. -3. DOF/math tests: constrained/free partition, equation numbering, sparse pattern, reduced solve, full reconstruction. -4. Results tests: HDF5 or writer boundary schema for `U` and `RF`; component labels and frame metadata. -5. Reference comparator tests: CSV header validation, node matching, tolerance pass/fail behavior. -6. Element tests: MITC4 shape functions, stiffness symmetry, rigid body behavior, drilling stiffness sensitivity. -7. Assembly/analysis tests: small known systems, full-vector reaction recovery, singular negative cases. -8. Stored-reference tests: at least one Phase 1-compatible displacement CSV comparison first, then three accepted stored cases for Phase 1 completion. - -## Phase 1 Reference Plan -Current reference state: -- `references/quad_01.inp` and `references/quad_01_displacements.csv` are accepted stored artifacts. -- `quad_01.inp` contains `S4R`, `Part/Assembly/Instance`, `*Density`, and `NLGEOM=YES`; it is not a Phase 1 parser acceptance case as-is. -- `references/quad_02.inp` and `references/quad_02_displacements.csv` have been added by the user as an S4 reference pair. -- `references/quad_02_reactionforces.csv` has been added by the user as the paired Abaqus RF/RM result artifact for `quad_02`. -- `quad_02.inp` uses `TYPE=S4`, but also includes `Part/Assembly/Instance`; this is a compatibility decision point, not automatic parser scope expansion. -- `references/quad_02_phase1.inp` is the accepted normalized Phase 1-compatible derivative input for the `quad_02` S4 reference pair. - -Required reference additions or decisions: -- Explain or fix the current `quad_02_phase1` node-wise RF mismatch against `quad_02_reactionforces.csv`. Current observed comparison with `abs_tol = 1.0e-6`, `rel_tol = 1.0e-5`, `reference_scale = 1.0` has max absolute error about `612.751347` and max relative error about `0.494032`. -- Add more small cases until Phase 1 can pass one single-element case, one simple multi-element plate/shell case, and one curved shell benchmark. - -## Phase 1 Risk Controls -- Do not implement MITC4 element stiffness until the formulation gate in `docs/MITC4_FORMULATION.md` is closed. -- Do not treat the previous P1-01 through P1-14 implementation as authoritative after the MITC4 formulation reset. -- Do not use `quad_01.inp` to justify `S4R`, `Part/Assembly/Instance`, or `NLGEOM=YES` support. -- Do not use `quad_02.inp` to silently justify `Part/Assembly/Instance` support without a parser contract. -- Do not compute reactions from reduced vectors only. -- Do not expose MKL, TBB, or HDF5 APIs directly in solver core. -- Do not narrow ids, equation numbers, or sparse indices below int64. -- Do not allow `Node` or `Element` to own global equation ids. -- Do not treat a passing build as Phase 1 validation without parser, DOF, reference, and singular negative tests. - -## Current Non-Goals -- Do not implement solver code outside the matching rebaseline sprint contract. -- Do not require Abaqus execution locally or in CI. -- Do not add mesh quality diagnostics in Phase 1. -- Do not support Abaqus `S4R` in Phase 1. -- Do not silently expand the Abaqus input subset beyond `docs/ABAQUS_INPUT_SUBSET.md`. - -## Codex Extension Follow-up Tasks -| ID | Status | Owner | Task | Source | -|---|---|---|---|---| -| C-002 | pending | user + Codex | Confirm hook behavior in the actual Codex runtime on native Windows after `features.codex_hooks` is enabled. | `.codex/hooks.json`, `.codex/hooks/*.py` | -| C-003 | pending | user + Codex | Decide whether any `.codex/skills/*` should be mirrored under `.agents/skills/` for environments that only scan the default skill folders. | `.codex/config.toml`, `.codex/skills/` | -| C-004 | pending | user + Codex | Confirm that the `fesa-commands` repo plugin appears in the active Codex plugin/command surface after marketplace registration. | `plugins/fesa-commands/`, `.agents/plugins/marketplace.json` | - -## Open Questions -- Which Abaqus version will generate future reference artifacts when it is not recorded in the input or notes? -- Is the current `quad_02` RF mismatch due to MITC4 formulation details, Abaqus S4 reaction recovery conventions, normalized input differences, or another solver issue? diff --git a/PROGRESS.md b/PROGRESS.md deleted file mode 100644 index eb09da9..0000000 --- a/PROGRESS.md +++ /dev/null @@ -1,1280 +0,0 @@ -# PROGRESS - -## Purpose -`PROGRESS.md` is the shared chronological status log for FESA agents. - -Every new agent session must read this file together with `PLAN.md` before planning or editing. Keep this file factual: what changed, what was verified, what is blocked, and what remains risky. - -## How To Use -- Add a new entry whenever a meaningful planning, documentation, implementation, verification, or review task is completed. -- Include date, agent or author when known, changed files, verification performed, and follow-up items. -- Record blockers explicitly. -- Do not use this file as a future task list. Put future tasks in `PLAN.md`. -- Do not remove history unless the user explicitly asks for archival cleanup. - -## Current Status -Phase 1 has a completed rebaseline execution path in `phases/1-linear-static-mitc4-rebaseline`. Steps 0 through 15 are complete, and P1R-15 recorded a pass-with-documented-gaps evaluator closeout. The follow-up architecture refactor phase in `phases/1-structure-alignment-refactor` is also complete; P1A-09 recorded a passing architecture evaluator closeout, `include/fesa/fesa.hpp` is now an include-only facade, production symbols are separated under module ownership, and R-014 is closed. `quad_02_phase1.inp` is the normalized Phase 1-compatible input path for the stored `quad_02` S4 reference pair, while the original `quad_02.inp` remains preserved unsupported provenance. `quad_02_reactionforces.csv` is now onboarded as the paired Abaqus RF/RM artifact and the Results module can load and compare reaction CSV files, but the current node-wise RF comparison does not pass and R-010 remains open for triage. Core numeric aliases, DOF mapping, validation harness, model diagnostic context, the Phase 1 parser/domain subset, validation/singular diagnostics, DofManager/reaction foundation, minimum result model metadata, displacement and reaction CSV comparator foundation, MITC4 geometry/director scaffolding, MITC4 displacement/strain/tying row scaffolding, MITC4 material/transform/integration scaffolding, MITC4 stiffness/drilling/internal-force scaffolding, MITC4 patch/locking-sensitivity tests, full-space assembly, reduced projection, sparse-pattern scaffold, solver adapter injection, full-vector internal/reaction force state, active AnalysisModel construction, input-to-AnalysisState-to-U/RF result workflow, and the first stored Abaqus displacement regression have been revalidated. Full PRD Phase 1 completion still depends on the open reference gaps R-010 and R-013. The old `phases/1-linear-static-mitc4` path is historical and superseded after the MITC4 formulation reset. - -## Completed Work - -### 2026-05-05 - quad_02 reaction CSV comparison onboarded with mismatch recorded -Author: Codex - -Changed files: -- `AGENTS.md` -- `docs/README.md` -- `docs/NUMERICAL_CONVENTIONS.md` -- `docs/ABAQUS_INPUT_SUBSET.md` -- `docs/VERIFICATION_PLAN.md` -- `docs/RESULTS_SCHEMA.md` -- `docs/MITC4_FORMULATION.md` -- `references/README.md` -- `references/quad_02_notes.md` -- `references/quad_02_reactionforces.csv` -- `include/fesa/Results/ReferenceComparison.hpp` -- `tests/test_results_module_includes.cpp` -- `tests/test_main.cpp` -- `PLAN.md` -- `PROGRESS.md` - -Summary: -- Onboarded `references/quad_02_reactionforces.csv` as the Abaqus RF/RM reference artifact paired with the stored `quad_02` S4 case. -- Added reaction CSV schema support for columns `Node Label`, `RF-RF1`, `RF-RF2`, `RF-RF3`, `RM-RM1`, `RM-RM2`, and `RM-RM3`, mapped to FESA `RF` components `RFX`, `RFY`, `RFZ`, `RMX`, `RMY`, and `RMZ`. -- Added `CsvReactionTable`, `loadReactionCsv*`, and `compareReactions` to the Results reference-comparison module. -- Added tests for reaction CSV required columns, duplicate-node diagnostics, node-id-based RF comparison, wrong metadata rejection, and `quad_02_reactionforces.csv` fixture discovery. -- Ran the `quad_02_phase1.inp` analysis path and compared FESA `RF` against the stored Abaqus RF/RM CSV. The comparison currently fails node-wise; this is recorded as a known R-010 verification gap rather than hidden by loose tolerances. -- First observed mismatch: node `1` `RFZ`, expected `6860.0`, actual `6652.459896`. Observed maximum absolute error is about `612.751347`, and maximum relative error is about `0.494032` with `abs_tol = 1.0e-6`, `rel_tol = 1.0e-5`, `reference_scale = 1.0`. -- Kept `quad_02.inp` as unsupported provenance; the executable Phase 1 input remains `quad_02_phase1.inp`. - -Verification: -- First ran `python scripts\validate_workspace.py` after adding reaction comparison tests; it failed as expected because the reaction CSV API did not exist yet. -- After adding the API, `python scripts\validate_workspace.py` exposed the current RF mismatch in the strict node-wise comparison. -- The final validation records the RF mismatch as a known gap test and passes: CMake configured, `fesa_core` and all test executables built, and CTest reported 9 of 9 test executables passed. - -Follow-up: -- Keep R-010 open until the `quad_02` node-wise RF mismatch is explained or fixed. -- Do not tune reaction tolerances or drilling stiffness simply to make this RF case pass. - -### 2026-05-05 - P1A-09 Architecture evaluator closeout completed -Author: Codex - -Changed files: -- `phases/index.json` -- `phases/1-structure-alignment-refactor/index.json` -- `phases/1-structure-alignment-refactor/step9-evaluator-report.md` -- `PLAN.md` -- `PROGRESS.md` - -Summary: -- Independently evaluated the completed structure-alignment refactor against `AGENTS.md`, Harness rules, `docs/ARCHITECTURE.md`, ADRs, numerical conventions, parser subset, verification plan, result schema, MITC4 formulation, and the P1A-00 migration map. -- Accepted the refactor: `include/fesa/fesa.hpp` is an include-only facade, implementation ownership is separated under module headers, matching `src/` module directories and thin compile units exist, and CMake compiles the recursive `src/*.cpp` source set. -- Recorded a non-blocking note that most implementation bodies remain inline in module headers; a later dedicated hardening contract can move bodies into `.cpp` files if stricter ABI/build-time encapsulation is desired. -- Closed R-014 and marked `1-structure-alignment-refactor` completed. -- Kept R-010 and R-013 open; this evaluator step does not onboard reaction CSV artifacts or add more stored reference cases. - -Verification: -- `python scripts\validate_workspace.py` configured CMake, built `fesa_core`, `fesa_tests`, `fesa_core_module_tests`, `fesa_math_module_tests`, `fesa_io_module_tests`, `fesa_results_module_tests`, `fesa_element_module_tests`, `fesa_mitc4_stiffness_module_tests`, `fesa_assembly_module_tests`, and `fesa_analysis_module_tests`, and ran CTest successfully. -- CTest result: 9 test executables passed. - -Follow-up: -- Resolve R-010 by onboarding or explicitly deferring reaction-force CSV comparison policy. -- Resolve R-013 by adding additional small Abaqus S4 reference cases until the PRD target of three stored Phase 1 cases is met. - -### 2026-05-05 - P1A-08 Assembly Analysis extraction completed -Author: Codex - -Changed files: -- `CMakeLists.txt` -- `include/fesa/Analysis/Analysis.hpp` -- `include/fesa/Analysis/LinearStaticAnalysis.hpp` -- `include/fesa/Assembly/Assembly.hpp` -- `include/fesa/Assembly/AssemblySystem.hpp` -- `include/fesa/fesa.hpp` -- `tests/test_analysis_module_includes.cpp` -- `tests/test_assembly_module_includes.cpp` -- `phases/1-structure-alignment-refactor/index.json` -- `PLAN.md` -- `PROGRESS.md` - -Summary: -- Extracted `buildReducedSparsePattern`, `recoverFullReaction`, `AssemblyResult`, `ReducedSystem`, `assembleSystem`, and `projectToReducedSystem` into `include/fesa/Assembly/AssemblySystem.hpp`. -- Extracted `AnalysisResult`, `Analysis`, `LinearStaticAnalysis`, and `runLinearStaticInputString` into `include/fesa/Analysis/LinearStaticAnalysis.hpp`. -- Kept `AnalysisState` in `include/fesa/Core/AnalysisState.hpp` because `docs/ARCHITECTURE.md` and the P1A-00 migration map place mutable analysis state under Core ownership. -- Updated Assembly and Analysis facade headers so direct module includes expose the relocated workflow without including `fesa/fesa.hpp`. -- Reduced `include/fesa/fesa.hpp` to an include-only umbrella facade with no production implementation body. -- Preserved full-space stiffness/load preservation, constrained/free reduced projection, solver adapter injection, deterministic default Gaussian solver, `RF = K_full * U_full - F_full`, and step/frame `U`/`RF` result writing behavior. -- No parser subset, MITC4 formulation, numerical convention, result schema, reference tolerance, sparse storage, HDF5, MKL, TBB, nonlinear, dynamic, pressure-load, or RBE behavior was added. - -Verification: -- First ran `python scripts\validate_workspace.py` after adding direct Assembly and Analysis include tests; it failed as expected because the module facades did not yet expose the Assembly and Analysis symbols. -- After extraction, `python scripts\validate_workspace.py` configured CMake, built `fesa_core`, `fesa_tests`, `fesa_core_module_tests`, `fesa_math_module_tests`, `fesa_io_module_tests`, `fesa_results_module_tests`, `fesa_element_module_tests`, `fesa_mitc4_stiffness_module_tests`, `fesa_assembly_module_tests`, and `fesa_analysis_module_tests`, and ran CTest successfully. -- CTest result: 9 test executables passed. - -Follow-up: -- Continue with P1A-09 independent architecture evaluator closeout. -- Keep R-014 open until P1A-09 independently accepts the final architecture alignment. -- Keep R-010 and R-013 open; this refactor does not onboard reaction CSV artifacts or add additional stored reference cases. - -### 2026-05-05 - P1A-07 MITC4 material stiffness extraction completed -Author: Codex - -Changed files: -- `CMakeLists.txt` -- `include/fesa/Element/Element.hpp` -- `include/fesa/Element/MITC4Kinematics.hpp` -- `include/fesa/Element/MITC4MaterialIntegration.hpp` -- `include/fesa/Element/MITC4Stiffness.hpp` -- `include/fesa/Material/MITC4PlaneStressMaterial.hpp` -- `include/fesa/Material/Material.hpp` -- `include/fesa/fesa.hpp` -- `tests/test_mitc4_stiffness_module_includes.cpp` -- `phases/1-structure-alignment-refactor/index.json` -- `PLAN.md` -- `PROGRESS.md` - -Summary: -- Extracted MITC4 strain component ordering, strain vector alias, material matrix alias, plane-stress material matrix diagnostics, material-vector multiply, material-vector dot product, and material matrix transform into `include/fesa/Material/MITC4PlaneStressMaterial.hpp`. -- Extracted `2 x 2 x 2` Gauss integration points, covariant-to-local strain transform, tensor/vector conversion helpers, and MITC4 material integration sample/data construction into `include/fesa/Element/MITC4MaterialIntegration.hpp`. -- Extracted MITC4 stiffness options, local/global DOF transform, strain-row local transform, `B^T D B` accumulation, drilling stabilization, local-to-global stiffness transform, element stiffness, internal force, and `MITC4ElementKernel` into `include/fesa/Element/MITC4Stiffness.hpp`. -- Updated Element and Material facade headers so direct module includes expose the relocated MITC4 material/stiffness surface without including `fesa/fesa.hpp`. -- Reduced `include/fesa/fesa.hpp` to keep only the remaining Assembly/Analysis workflow and umbrella includes; MITC4 material/stiffness implementation bodies no longer live there. -- Preserved `drilling_stiffness_scale = 1.0e-3`, the minimum-positive-physical-local-diagonal drilling policy, `2 x 2 x 2` integration, stiffness symmetry, internal-force `K_e u_e`, and all existing MITC4 patch/locking characterization behavior. -- No parser, result schema, reference tolerance, S4R, reduced integration, hourglass, nonlinear, pressure-load, HDF5, MKL, or TBB behavior was added. -- Remaining large groups in `fesa.hpp` are Assembly helpers (`buildReducedSparsePattern`, `recoverFullReaction`, assembly/project functions) and Analysis workflow. - -Verification: -- First ran `python scripts\validate_workspace.py` after adding `fesa_mitc4_stiffness_module_tests`; it failed as expected because `fesa/Element/MITC4Stiffness.hpp` did not exist yet. -- After extraction, `python scripts\validate_workspace.py` configured CMake, built `fesa_core`, `fesa_tests`, `fesa_core_module_tests`, `fesa_math_module_tests`, `fesa_io_module_tests`, `fesa_results_module_tests`, `fesa_element_module_tests`, and `fesa_mitc4_stiffness_module_tests`, and ran CTest successfully. -- CTest result: 7 test executables passed. - -Follow-up: -- Continue with P1A-08 Assembly and Analysis workflow extraction. -- Keep R-014 open until P1A-09 independently accepts the final architecture alignment. -- Keep R-010 and R-013 open; this refactor does not add Abaqus RF artifacts or additional stored reference cases. - -### 2026-05-05 - P1A-06 MITC4 geometry strain extraction completed -Author: Codex - -Changed files: -- `CMakeLists.txt` -- `include/fesa/Element/Element.hpp` -- `include/fesa/Element/MITC4Geometry.hpp` -- `include/fesa/Element/MITC4Kinematics.hpp` -- `include/fesa/fesa.hpp` -- `tests/test_element_module_includes.cpp` -- `phases/1-structure-alignment-refactor/index.json` -- `PLAN.md` -- `PROGRESS.md` - -Summary: -- Extracted `ShapeData`, `shapeFunctions`, `LocalBasis`, MITC4 natural-coordinate and tying-point helpers, director frames, geometry construction, integration basis construction, MITC4 diagnostics, and `computeLocalBasis` into `include/fesa/Element/MITC4Geometry.hpp`. -- Extracted MITC4 element DOF/strain vector aliases, strain component ordering helpers, local rotation mapping, director increment, displacement derivatives, direct covariant strain evaluation/rows, row evaluation, and MITC transverse shear tying rows into `include/fesa/Element/MITC4Kinematics.hpp`. -- Updated `include/fesa/Element/Element.hpp` to expose the MITC4 geometry/kinematics layer through the Element module. -- Updated `include/fesa/fesa.hpp` to include the Element facade and removed the relocated MITC4 geometry/strain definitions from the umbrella body. -- Added `fesa_element_module_tests`, a direct Element include smoke/regression test that does not include `fesa/fesa.hpp`. -- Preserved FESA/Abaqus S4 node order, A/B/C/D tying labels and signs, director fallback policy, invalid thickness/singular geometry diagnostics, direct covariant strain rows, and MITC shear interpolation behavior. -- No material law, stiffness integration, drilling stabilization, assembly, parser, results, or analysis behavior was changed. -- Remaining large groups in `fesa.hpp` are MITC4 material transform/integration/stiffness/internal-force helpers, Assembly helpers (`buildReducedSparsePattern`, `recoverFullReaction`, assembly/project functions), and Analysis workflow. - -Verification: -- First ran `python scripts\validate_workspace.py` after adding the direct Element include test; it failed as expected because `fesa/Element/Element.hpp` did not yet expose MITC4 geometry/strain symbols. -- After extraction, `python scripts\validate_workspace.py` configured CMake, built `fesa_core`, `fesa_tests`, `fesa_core_module_tests`, `fesa_math_module_tests`, `fesa_io_module_tests`, `fesa_results_module_tests`, and `fesa_element_module_tests`, and ran CTest successfully. -- CTest result: 6 test executables passed. - -Follow-up: -- Continue with P1A-07 MITC4 material/stiffness extraction. -- Keep R-014 open until P1A-09 independently accepts the final architecture alignment. -- Keep R-010 and R-013 open; this refactor does not add Abaqus RF artifacts or additional stored reference cases. - -### 2026-05-05 - P1A-05 Results reference extraction completed -Author: Codex - -Changed files: -- `CMakeLists.txt` -- `include/fesa/Results/ReferenceComparison.hpp` -- `include/fesa/Results/ResultModel.hpp` -- `include/fesa/Results/Results.hpp` -- `include/fesa/fesa.hpp` -- `tests/test_results_module_includes.cpp` -- `phases/1-structure-alignment-refactor/index.json` -- `PLAN.md` -- `PROGRESS.md` - -Summary: -- Extracted `FieldOutput`, `ResultFrame`, `ResultStep`, `ResultFile`, and `InMemoryResultsWriter` from the umbrella header into `include/fesa/Results/ResultModel.hpp`. -- Extracted displacement CSV loading, `ComparisonOptions`, `ComparisonResult`, and `compareDisplacements` into `include/fesa/Results/ReferenceComparison.hpp`. -- Updated `include/fesa/Results/Results.hpp` to expose the in-memory result model and reference comparator through the Results module. -- Updated `include/fesa/fesa.hpp` to include the Results module facade, preserving existing umbrella consumers and leaving Analysis workflow symbols in place for P1A-08. -- Added `fesa_results_module_tests`, a direct Results include smoke/regression test that does not include `fesa/fesa.hpp`. -- Preserved mandatory Phase 1 `U`/`RF` metadata, CSV column mapping, node-id-based matching, and the stored `quad_02_displacements.csv` loader path. -- No HDF5 dependency, reaction CSV parser, result label change, comparison tolerance change, or analysis execution change was introduced. -- Remaining large groups in `fesa.hpp` are Assembly helpers (`buildReducedSparsePattern`, `recoverFullReaction`), MITC4 Element/Material helpers, and Analysis workflow. - -Verification: -- First ran `python scripts\validate_workspace.py` after adding the direct Results include test; it failed as expected because `fesa/Results/Results.hpp` did not yet expose result and comparison symbols. -- After extraction, `python scripts\validate_workspace.py` configured CMake, built `fesa_core`, `fesa_tests`, `fesa_core_module_tests`, `fesa_math_module_tests`, `fesa_io_module_tests`, and `fesa_results_module_tests`, and ran CTest successfully. -- CTest result: 5 test executables passed. - -Follow-up: -- Continue with P1A-06 MITC4 geometry/strain extraction. -- Keep R-014 open until P1A-09 independently accepts the final architecture alignment. -- Keep R-010 open; missing Abaqus reaction CSV artifacts are not solved by this refactor. - -### 2026-05-05 - P1A-04 IO parser extraction completed -Author: Codex - -Changed files: -- `CMakeLists.txt` -- `include/fesa/IO/AbaqusInputParser.hpp` -- `include/fesa/IO/IO.hpp` -- `include/fesa/fesa.hpp` -- `tests/test_io_module_includes.cpp` -- `phases/1-structure-alignment-refactor/index.json` -- `PLAN.md` -- `PROGRESS.md` - -Summary: -- Extracted `KeywordLine`, `ParseResult`, `parseKeywordLine`, and `AbaqusInputParser` from the umbrella header into `include/fesa/IO/AbaqusInputParser.hpp`. -- Updated `include/fesa/IO/IO.hpp` to expose the parser through the IO module while depending only on Core and Util. -- Updated `include/fesa/fesa.hpp` to include the IO module facade, preserving existing public symbols for umbrella consumers. -- Added `fesa_io_module_tests`, a direct IO include smoke/regression test that does not include `fesa/fesa.hpp`. -- Verified that `references/quad_02_phase1.inp` remains accepted with expected node, element, set, material, and shell-section counts. -- Verified that the original unsupported `references/quad_02.inp` still fails with unsupported-keyword diagnostics, and that nonzero prescribed displacement still reports `FESA-PARSE-BOUNDARY-NONZERO`. -- Remaining large groups in `fesa.hpp` are Assembly helpers (`buildReducedSparsePattern`, `recoverFullReaction`), MITC4 Element/Material helpers, Results/reference comparison, and Analysis workflow. - -Verification: -- First ran `python scripts\validate_workspace.py` after adding the direct IO include test; it failed as expected because `fesa/IO/IO.hpp` did not yet expose parser symbols. -- After extraction, `python scripts\validate_workspace.py` configured CMake, built `fesa_core`, `fesa_tests`, `fesa_core_module_tests`, `fesa_math_module_tests`, and `fesa_io_module_tests`, and ran CTest successfully. -- CTest result: 4 test executables passed. - -Follow-up: -- Continue with P1A-05 Results/reference extraction. -- Keep R-014 open until P1A-09 independently accepts the final architecture alignment. - -### 2026-05-05 - P1A-03 Math solver extraction completed -Author: Codex - -Changed files: -- `CMakeLists.txt` -- `include/fesa/Core/Domain.hpp` -- `include/fesa/Math/DenseMatrix.hpp` -- `include/fesa/Math/LinearSolver.hpp` -- `include/fesa/Math/Math.hpp` -- `include/fesa/Math/SparsePattern.hpp` -- `include/fesa/Math/Vector.hpp` -- `include/fesa/fesa.hpp` -- `tests/test_math_module_includes.cpp` -- `phases/1-structure-alignment-refactor/index.json` -- `PLAN.md` -- `PROGRESS.md` - -Summary: -- Extracted `Vec3` and vector helper functions into `include/fesa/Math/Vector.hpp`; `Core/Domain.hpp` now consumes `Vec3` from Math instead of defining it locally. -- Extracted sparse pattern data structures into `include/fesa/Math/SparsePattern.hpp`. -- Extracted `DenseMatrix` into `include/fesa/Math/DenseMatrix.hpp`. -- Extracted `SolveResult`, `LinearSolver`, and `GaussianEliminationSolver` into `include/fesa/Math/LinearSolver.hpp`. -- Added `fesa_math_module_tests`, a direct module include smoke test that does not include `fesa/fesa.hpp` and checks vector math, int64 sparse index/equation boundaries, dense matrix multiply, solver success, and singular-solver diagnostics. -- Preserved `LinearSolver` as the adapter boundary and introduced no MKL, TBB, HDF5, or production sparse storage dependency. -- Remaining large groups in `fesa.hpp` are IO parser, Assembly helpers (`buildReducedSparsePattern`, `recoverFullReaction`), MITC4 Element/Material helpers, Results/reference comparison, and Analysis workflow. - -Verification: -- First ran `python scripts/validate_workspace.py` after adding the direct Math include test; it failed as expected because `fesa/Math/Math.hpp` did not yet expose Math primitives or solver types. -- After extraction, `python scripts/validate_workspace.py` configured CMake, built `fesa_core`, `fesa_tests`, `fesa_core_module_tests`, and `fesa_math_module_tests`, and ran CTest successfully. -- CTest result: 3 test executables passed. - -Follow-up: -- Continue with P1A-04 IO parser extraction. -- Keep R-014 open until P1A-09 independently accepts the final architecture alignment. - -### 2026-05-05 - P1A-02 Core domain DOF extraction completed -Author: Codex - -Changed files: -- `CMakeLists.txt` -- `include/fesa/Boundary/Boundary.hpp` -- `include/fesa/Core/AnalysisModel.hpp` -- `include/fesa/Core/AnalysisState.hpp` -- `include/fesa/Core/Core.hpp` -- `include/fesa/Core/Dof.hpp` -- `include/fesa/Core/DofManager.hpp` -- `include/fesa/Core/Domain.hpp` -- `include/fesa/Core/Types.hpp` -- `include/fesa/Core/Validation.hpp` -- `include/fesa/Load/Load.hpp` -- `include/fesa/Property/Property.hpp` -- `include/fesa/Util/Diagnostics.hpp` -- `include/fesa/Util/String.hpp` -- `include/fesa/Util/Util.hpp` -- `include/fesa/fesa.hpp` -- `tests/test_core_module_includes.cpp` -- `phases/1-structure-alignment-refactor/index.json` -- `PLAN.md` -- `PROGRESS.md` - -Summary: -- Extracted numeric aliases, diagnostics, string/parse helpers, DOF mapping, Domain records, validation helpers, `AnalysisModel`, `AnalysisState`, and `DofManager` from the umbrella header into Core and Util module headers. -- Moved Phase 1 `BoundaryCondition`, `NodalLoad`, and `ShellSection` model records into their Boundary, Load, and Property module headers. -- Added `fesa_core_module_tests`, a direct module include smoke test that does not include `fesa/fesa.hpp` and checks aliases, Domain, diagnostics, DOF mapping, Boundary/Load/Property records, `DofManager`, `AnalysisModel`, and `AnalysisState`. -- Preserved public symbol names and namespace `fesa`; `fesa/fesa.hpp` still works as the umbrella facade. -- Kept lightweight `Material` as a Domain record under Core for now to avoid a Core-to-Material dependency cycle before IO extraction; later material-law helpers remain part of P1A-07. -- Remaining large groups in `fesa.hpp` are Math/solver helpers, IO parser, MITC4 Element helpers, Assembly, Results/reference comparison, and Analysis workflow. `Vec3` arithmetic helpers also remain there until P1A-03 extracts Math. - -Verification: -- First ran `python scripts/validate_workspace.py` after adding the direct module include test; it failed as expected because Core/Boundary/Load/Property/Util headers did not yet expose the required symbols. -- After extraction, `python scripts/validate_workspace.py` configured CMake, built `fesa_core`, `fesa_tests`, and `fesa_core_module_tests`, and ran CTest successfully. -- CTest result: 2 test executables passed. - -Follow-up: -- Continue with P1A-03 Math and solver adapter extraction. -- Keep R-014 open until P1A-09 independently accepts the final architecture alignment. - -### 2026-05-05 - P1A-01 module scaffold and facade completed -Author: Codex - -Changed files: -- `CMakeLists.txt` -- `include/fesa/ModuleInfo.hpp` -- `include/fesa/Analysis/Analysis.hpp` -- `include/fesa/Assembly/Assembly.hpp` -- `include/fesa/Boundary/Boundary.hpp` -- `include/fesa/Core/Core.hpp` -- `include/fesa/Element/Element.hpp` -- `include/fesa/IO/IO.hpp` -- `include/fesa/Load/Load.hpp` -- `include/fesa/Math/Math.hpp` -- `include/fesa/Material/Material.hpp` -- `include/fesa/Property/Property.hpp` -- `include/fesa/Results/Results.hpp` -- `include/fesa/Util/Util.hpp` -- `include/fesa/fesa.hpp` -- `src/Analysis/Analysis.cpp` -- `src/Assembly/Assembly.cpp` -- `src/Boundary/Boundary.cpp` -- `src/Core/Core.cpp` -- `src/Element/Element.cpp` -- `src/IO/IO.cpp` -- `src/Load/Load.cpp` -- `src/Math/Math.cpp` -- `src/Material/Material.cpp` -- `src/Property/Property.cpp` -- `src/Results/Results.cpp` -- `src/Util/Util.cpp` -- `tests/test_main.cpp` -- `phases/1-structure-alignment-refactor/index.json` -- `PLAN.md` -- `PROGRESS.md` - -Summary: -- Added architecture module scaffold headers and matching source directories for `Analysis`, `Assembly`, `Boundary`, `Core`, `Element`, `IO`, `Load`, `Math`, `Material`, `Property`, `Results`, and `Util`. -- Added `include/fesa/ModuleInfo.hpp` to record the module list and stable umbrella facade header policy without changing solver behavior, and exposed it through the existing `fesa/fesa.hpp` umbrella. -- Updated CMake to discover current and future `src/**/*.cpp` files under the module directories. -- Added an include compatibility smoke test that includes all module headers before `fesa/fesa.hpp` and checks the module list plus existing DOF mapping still works. -- No production symbols were moved from `include/fesa/fesa.hpp` in this step. - -Verification: -- First ran `python scripts/validate_workspace.py` after adding the smoke test; it failed as expected because `fesa/Analysis/Analysis.hpp` did not exist yet. -- After adding the scaffold and CMake wiring, `python scripts/validate_workspace.py` configured CMake, built `fesa_core` and `fesa_tests`, and ran CTest successfully. -- CTest result: 1 test executable passed. - -Follow-up: -- Continue with P1A-02 Core/Util extraction while preserving all public symbols and behavior. -- Keep R-014 open until P1A-09 independently accepts the final architecture alignment. - -### 2026-05-05 - P1A-00 architecture drift audit completed -Author: Codex - -Changed files: -- `phases/1-structure-alignment-refactor/step0-architecture-map.md` -- `phases/1-structure-alignment-refactor/index.json` -- `PLAN.md` -- `PROGRESS.md` - -Summary: -- Confirmed the Phase 1 architecture drift: production code is concentrated in `include/fesa/fesa.hpp`, `src/fesa.cpp` only holds static assertions, and `tests/test_main.cpp` holds all Phase 1 tests. -- Added a module-by-module migration map assigning current Phase 1 production symbol groups to `Analysis`, `Assembly`, `Boundary`, `Core`, `Element`, `IO`, `Load`, `Math`, `Material`, `Property`, `Results`, and `Util`. -- Recorded behavior locks for the refactor: no parser-scope changes, no MITC4 formula changes, no numerical convention changes, no reference tolerance changes, no reduced-vector RF recovery, no Abaqus execution requirement, and no MKL/TBB/HDF5 dependency introduction. -- Identified contract refinements that later steps should resolve carefully: `AnalysisState` belongs to `Core` in `docs/ARCHITECTURE.md`, the basic `Material` domain record is needed before IO extraction, and `Node`/`Element` record ownership should avoid a `Core` to element-kernel dependency cycle. -- Marked P1A-00 completed and moved the active objective to P1A-01. - -Verification: -- Before writing the audit artifact, `python scripts/validate_workspace.py` configured CMake, built `fesa_core` and `fesa_tests`, and ran CTest successfully. -- CTest result: 1 test executable passed. -- After writing the audit artifact and status updates, `phases/index.json` and `phases/1-structure-alignment-refactor/index.json` parsed successfully, `git diff --check` passed, and `python scripts/validate_workspace.py` passed again. - -Follow-up: -- Continue with P1A-01 module scaffold and umbrella facade. -- Keep R-014 open until the full structure-alignment refactor passes P1A-09. -- Keep R-010 and R-013 visible; P1A-00 does not solve missing Abaqus reaction CSV or the three-reference PRD target. - -### 2026-05-05 - Phase 1 structure-alignment refactor phase planned -Author: Codex - -Changed files: -- `phases/index.json` -- `phases/1-structure-alignment-refactor/index.json` -- `phases/1-structure-alignment-refactor/step0.md` -- `phases/1-structure-alignment-refactor/step1.md` -- `phases/1-structure-alignment-refactor/step2.md` -- `phases/1-structure-alignment-refactor/step3.md` -- `phases/1-structure-alignment-refactor/step4.md` -- `phases/1-structure-alignment-refactor/step5.md` -- `phases/1-structure-alignment-refactor/step6.md` -- `phases/1-structure-alignment-refactor/step7.md` -- `phases/1-structure-alignment-refactor/step8.md` -- `phases/1-structure-alignment-refactor/step9.md` -- `PLAN.md` -- `PROGRESS.md` - -Summary: -- Added a dedicated Phase 1 structure-alignment refactor phase to correct the architecture drift where production code is concentrated in `include/fesa/fesa.hpp`. -- Decomposed the refactor into 10 Harness steps: audit, scaffold/facade, Core/Util plus Boundary/Load/Property extraction, Math/Solver extraction, IO parser extraction, Results/reference extraction, MITC4 geometry/strain extraction, MITC4 material/stiffness extraction, Assembly/Analysis extraction, and independent evaluator closeout. -- Updated `PLAN.md` with R-014 and P1A-00 through P1A-09 so new agents can execute the refactor without private context. - -Verification: -- Parsed `phases/index.json` and `phases/1-structure-alignment-refactor/index.json` with PowerShell `ConvertFrom-Json`. -- Verified all 10 structure-alignment step files contain the required sprint contract sections from `docs/HARNESS_ENGINEERING.md`. -- `python scripts/validate_workspace.py` configured CMake, built `fesa_core` and `fesa_tests`, and ran CTest successfully. -- CTest result: 1 test executable passed. - -Follow-up: -- Start with P1A-00 architecture drift audit. -- Keep R-010 and R-013 visible; the structure refactor does not solve missing reaction CSV or the three-reference PRD target. - -### 2026-05-04 - P1R-15 evaluator closeout completed -Author: Codex - -Changed files: -- `phases/1-linear-static-mitc4-rebaseline/step15-evaluator-report.md` -- `phases/1-linear-static-mitc4-rebaseline/index.json` -- `phases/index.json` -- `PLAN.md` -- `PROGRESS.md` - -Summary: -- Performed the independent closeout review for the `1-linear-static-mitc4-rebaseline` phase. -- Recorded a PASS verdict for the rebaseline sprint sequence, with product-level Phase 1 reference gaps explicitly documented instead of silently approved. -- Marked P1R-15 complete and marked the rebaseline phase complete in the phase registry. -- Updated `PLAN.md` so the next work is closing R-010 and R-013 before claiming full PRD Phase 1 completion. - -Verification: -- `python scripts/validate_workspace.py` configured CMake, built `fesa_core` and `fesa_tests`, and ran CTest successfully. -- CTest result: 1 test executable passed. - -Follow-up: -- R-010 remains open: add Abaqus reaction-force CSV artifacts, preferably `*_reactions.csv`, or explicitly adopt internal equilibrium tests as the Phase 1 RF verification basis until Abaqus RF output is available. -- R-013 remains open: add enough additional small Abaqus S4 reference cases for the PRD target of three stored references: one single-element case, one simple multi-element plate/shell case, and one curved shell benchmark. - -### 2026-05-04 - P1R-14 stored reference regression completed -Author: Codex - -Changed files: -- `tests/test_main.cpp` -- `docs/VERIFICATION_PLAN.md` -- `references/quad_02_notes.md` -- `phases/1-linear-static-mitc4-rebaseline/index.json` -- `PLAN.md` -- `PROGRESS.md` - -Summary: -- Added a fixture discovery test that keeps `references/quad_02.inp` as unsupported Abaqus/CAE provenance while verifying `references/quad_02_phase1.inp` is the accepted executable Phase 1 derivative. -- Added the first automated stored Abaqus displacement regression: run `quad_02_phase1.inp`, extract FESA `/results/steps/Step-1/frames/0/fieldOutputs/U`, and compare against `references/quad_02_displacements.csv` by node id. -- Used the documented tolerance for this reference pair: `abs_tol = 1.0e-12`, `rel_tol = 1.0e-5`, `reference_scale = 1.0`. -- Updated verification/reference notes so `quad_02_phase1` is no longer a future regression target; it is now active test coverage. - -Verification: -- `python scripts/validate_workspace.py` configured CMake, built `fesa_core` and `fesa_tests`, and ran CTest successfully. -- CTest result: 1 test executable passed. -- Stored reference result: `quad_02_phase1.inp` displacement regression passed against `quad_02_displacements.csv` using the tolerance above. - -Follow-up: -- Continue with P1R-15 independent evaluator closeout. -- R-013 remains open: Phase 1 still needs enough additional stored Abaqus S4 reference cases for the PRD target of three stored references: one single-element case, one simple multi-element plate/shell case, and one curved shell benchmark. -- R-010 remains open: no `quad_02_reactions.csv` or other Abaqus reaction CSV exists yet, so `RF` remains verified by internal full-vector equilibrium. - -### 2026-05-04 - P1R-13 linear static workflow completed -Author: Codex - -Changed files: -- `include/fesa/fesa.hpp` -- `tests/test_main.cpp` -- `phases/1-linear-static-mitc4-rebaseline/index.json` -- `PLAN.md` -- `PROGRESS.md` - -Summary: -- Added an explicit `AnalysisModel` value object for the active Phase 1 linear static step, including active element ids, boundary/load indices, shell section indices, and material keys. -- Added `buildLinearStaticAnalysisModel()` and routed `LinearStaticAnalysis` through it before DOF mapping, assembly, solve, state update, and result writing. -- Preserved the one-step Phase 1 execution boundary with a diagnostic for multiple parsed steps instead of silently executing them. -- Added `runLinearStaticInputString()` so parser diagnostics, domain validation diagnostics, active model construction, solve, `AnalysisState`, and `U`/`RF` result writing are exercised as one workflow. -- Extended the in-memory result model with element type metadata and ensured result writing uses the active model step name. -- Added tests for active model activation without mutating `Domain`, multiple-step rejection, input-to-result `U`/`RF` schema readiness, full-vector reaction balance, and parse/validation error propagation through the workflow. - -Verification: -- First ran `python scripts/validate_workspace.py` after adding Step 13 tests; it failed as expected because `AnalysisModel` and input-string workflow APIs did not exist yet. -- After implementation, `python scripts/validate_workspace.py` configured CMake, built `fesa_core` and `fesa_tests`, and ran CTest successfully. -- CTest result: 1 test executable passed. - -Follow-up: -- Continue with P1R-14 stored-reference displacement regression using `references/quad_02_phase1.inp` and `references/quad_02_displacements.csv`. -- Step 13 intentionally did not add nonlinear, dynamic, pressure, thermal, HDF5, multi-step execution, or stored Abaqus CSV regression behavior. - -### 2026-05-04 - P1R-12 assembly sparse solver path completed -Author: Codex - -Changed files: -- `include/fesa/fesa.hpp` -- `tests/test_main.cpp` -- `phases/1-linear-static-mitc4-rebaseline/index.json` -- `PLAN.md` -- `PROGRESS.md` - -Summary: -- Added deterministic reduced sparse-pattern scaffolding from `DofManager` element equation connectivity, preserving int64 equation and nonzero counts for the future sparse/MKL path. -- Added `projectToReducedSystem()` so constrained/free projection is a named assembly boundary instead of being embedded directly inside `LinearStaticAnalysis`. -- Extended `AssemblyResult` to carry full-space stiffness, full external load, reduced sparse pattern, and diagnostics. -- Reworked assembly to call the rebuilt MITC4 stiffness result path directly and preserve full-space `K_full`/`F_full` for reaction recovery. -- Added solver injection to `LinearStaticAnalysis` so the deterministic Gaussian solver remains the default test adapter while future MKL-backed solvers stay behind the `LinearSolver` interface. -- Added `AnalysisState::f_internal_full` and verified `RF = Fint_full - Fext_full = K_full * U_full - F_full`. -- Added tests for deterministic sparse pattern ordering, reduced projection with known displacement, full-space assembly/load preservation, reduced residual satisfaction, solver adapter injection, and singular solver diagnostic propagation. - -Verification: -- First ran `python scripts/validate_workspace.py` after adding Step 12 tests; it failed as expected because reduced sparse pattern, projection, solver injection, and `f_internal_full` did not exist yet. -- After implementation, `python scripts/validate_workspace.py` configured CMake, built `fesa_core` and `fesa_tests`, and ran CTest successfully. -- CTest result: 1 test executable passed. - -Follow-up: -- Continue with P1R-13 linear static workflow revalidation from Phase 1 input through `U` and `RF` result fields. -- Step 12 intentionally did not add MKL/TBB APIs, stored Abaqus reference comparison, pressure loads, or a production sparse matrix storage backend. - -### 2026-05-04 - P1R-11 MITC4 patch and benchmark tests completed -Author: Codex - -Changed files: -- `tests/test_main.cpp` -- `docs/VERIFICATION_PLAN.md` -- `phases/1-linear-static-mitc4-rebaseline/index.json` -- `PLAN.md` -- `PROGRESS.md` - -Summary: -- Added reusable MITC4 test helpers for analytic displacement/rotation fields, local strain sampling through the tied strain-row path, and a one-element cantilever smoke model. -- Added constant membrane patch tests that verify uniform local `[eps11, eps22, gamma12]` at all `2 x 2 x 2` samples and positive physical energy. -- Added pure bending, pure transverse shear, and pure twist patch tests to exercise through-thickness bending behavior, MITC transverse shear tying, and bilinear twist/shear interpolation. -- Added a drilling-sensitivity negative check proving membrane patch energy is not hidden by the artificial drilling scale when no drilling DOF participates. -- Added a thin one-element cantilever strip test that verifies tip displacement grows materially as thickness decreases, providing early shear-locking sensitivity coverage before stored Abaqus reference regression. -- Documented the Step 11 analytic patch layer and kept Scordelis-Lo as a deferred benchmark scaffold until pressure-load support and a stored curved-shell reference artifact are defined. - -Verification: -- `python scripts/validate_workspace.py` configured CMake, built `fesa_core` and `fesa_tests`, and ran CTest successfully. -- CTest result: 1 test executable passed. - -Follow-up: -- Continue with P1R-12 assembly, solver-adapter boundary, constrained solve, and full-vector RF recovery revalidation. -- Step 11 intentionally did not execute Abaqus, add pressure loads, add stored CSV reference regression, or tune benchmark tolerances against Abaqus artifacts. - -### 2026-05-04 - P1R-10 MITC4 stiffness and drilling completed -Author: Codex - -Changed files: -- `include/fesa/fesa.hpp` -- `tests/test_main.cpp` -- `phases/1-linear-static-mitc4-rebaseline/index.json` -- `PLAN.md` -- `PROGRESS.md` - -Summary: -- Added tests and implementation for MITC4 element stiffness accumulation using tied strain rows, transformed material matrices, and `2 x 2 x 2` integration samples. -- Added a stiffness result API that exposes local physical stiffness, local drilling-stabilized stiffness, global stiffness, integration-point count, drilling reference diagonal, and drilling stiffness metadata. -- Replaced the old default drilling scale with the documented `drilling_stiffness_scale = 1.0e-3` and applied it to the minimum positive physical local stiffness diagonal before global transformation. -- Added diagnostics for invalid drilling scale and missing positive drilling reference diagonal. -- Added tests for stiffness symmetry, drilling scale sensitivity, rigid translation zero energy, documented drilling-only energy, and `f_int_e = K_e u_e` consistency. -- Routed `MITC4ElementKernel::stiffness` through the rebuilt formulation path so later assembly uses the Step 7 through Step 10 MITC4 helpers. - -Verification: -- First ran `python scripts/validate_workspace.py` after adding Step 10 tests; it failed as expected because the MITC4 stiffness/drilling/internal-force APIs did not exist yet. -- After implementation, `python scripts/validate_workspace.py` configured CMake, built `fesa_core` and `fesa_tests`, and ran CTest successfully. -- CTest result: 1 test executable passed. - -Follow-up: -- Continue with P1R-11 MITC4 patch, locking-sensitivity, and benchmark tests. -- Step 10 intentionally did not tune the drilling scale to a reference case, add stress/resultant output, introduce reduced integration/hourglass logic, or rebuild sparse/global assembly beyond routing the existing kernel to the new element formulation. - -### 2026-05-04 - P1R-09 MITC4 material integration scaffolding completed -Author: Codex - -Changed files: -- `include/fesa/fesa.hpp` -- `tests/test_main.cpp` -- `phases/1-linear-static-mitc4-rebaseline/index.json` -- `PLAN.md` -- `PROGRESS.md` - -Summary: -- Added tests and implementation for the documented isotropic local plane-stress shell material matrix in strain order `[eps11, eps22, eps33, gamma23, gamma13, gamma12]`, with `sigma_33 = 0` and transverse shear correction `kappa = 5/6`. -- Added material diagnostics for invalid elastic modulus, Poisson ratio, and shear correction factor. -- Added `2 x 2 x 2` Gauss integration point infrastructure with unit weights and total natural-domain weight `8`. -- Added covariant-to-local strain transformation and `D_convected = T^T D_hat T`, including identity and flat-element energy-equivalence tests. -- Added MITC4 material integration sample scaffolding that carries integration basis, tied strain rows, local material, strain transform, and convected material for the later stiffness rebuild. - -Verification: -- First ran `python scripts/validate_workspace.py` after adding Step 9 tests; it failed as expected because the MITC4 material/integration APIs did not exist yet. -- After implementation, `python scripts/validate_workspace.py` configured CMake, built `fesa_core` and `fesa_tests`, and ran CTest successfully. -- CTest result: 1 test executable passed. - -Follow-up: -- Continue with P1R-10 MITC4 stiffness/internal force rebuild, six-DOF local-to-global transform, and drilling stabilization. -- The legacy `MITC4ElementKernel::stiffness` path still needs to be rebuilt against the Step 7 through Step 9 formulation helpers; Step 9 intentionally did not add reduced integration, hourglass logic, stress/result output recovery, or the final drilling stiffness policy. - -### 2026-05-04 - P1R-08 MITC4 covariant strain tying completed -Author: Codex - -Changed files: -- `include/fesa/fesa.hpp` -- `tests/test_main.cpp` -- `phases/1-linear-static-mitc4-rebaseline/index.json` -- `PLAN.md` -- `PROGRESS.md` - -Summary: -- Added MITC4 tests for global rotation to local `alpha`, `beta`, `gamma` mapping and verified drilling `gamma` does not enter the physical director increment. -- Added degenerated-continuum displacement interpolation tests at the midsurface and through-thickness points, including rotation-driven `q_k = -V2_k alpha_k + V1_k beta_k` behavior. -- Added direct covariant strain-row finite-difference tests in the documented order `[eps11, eps22, eps33, gamma23, gamma13, gamma12]`. -- Added MITC tying tests proving FESA `A/C` signs for `gamma13` and `B/D` signs for `gamma23`, and proving Gauss-point shear rows are interpolated from tying rows rather than using direct Gauss-point transverse shear. -- Implemented reusable MITC4 displacement derivative, direct strain evaluation, direct strain-row, tied strain-row, and row-evaluation helpers for later material/integration and stiffness steps. - -Verification: -- First ran `python scripts/validate_workspace.py` after adding Step 8 tests; it failed as expected because the MITC4 displacement/strain/tying APIs did not exist yet. -- After implementation, `python scripts/validate_workspace.py` configured CMake, built `fesa_core` and `fesa_tests`, and ran CTest successfully. -- CTest result: 1 test executable passed. - -Follow-up: -- Continue with P1R-09 material matrix, transform, and `2 x 2 x 2` integration scaffolding. -- The existing stiffness kernel still needs to be rebuilt against the new row APIs in steps 9 and 10; Step 8 intentionally did not integrate stiffness, add material transforms, or add S4R behavior. - -### 2026-05-04 - P1R-07 MITC4 geometry and directors completed -Author: Codex - -Changed files: -- `include/fesa/fesa.hpp` -- `tests/test_main.cpp` -- `phases/1-linear-static-mitc4-rebaseline/index.json` -- `PLAN.md` -- `PROGRESS.md` - -Summary: -- Added MITC4 tests for shape-function derivatives, FESA/Abaqus S4 natural-coordinate node order, tying point labels `A`, `B`, `C`, and `D`, and edge-node mapping. -- Added Phase 1 MITC4 geometry structures for natural points, tying points, center midsurface derivatives, center-normal director policy, nodal director frames, and integration-point local Cartesian bases. -- Implemented the documented center director policy `Vn = normalize(G1_c x G2_c)`, nodal `V1 = normalize(EY x Vn)` with deterministic fallback axes, and right-handed `V2 = Vn x V1`. -- Added integration-basis construction from degenerated-continuum covariant basis vectors and diagnostics for invalid thickness, singular center normal, singular basis, and near-zero Jacobian. -- Replaced the legacy `computeLocalBasis()` internals with the new center-director geometry policy while leaving stiffness/strain/drilling reimplementation to later steps. - -Verification: -- First ran `python scripts/validate_workspace.py` after adding Step 7 tests; it failed as expected because the MITC4 geometry/director API did not exist yet. -- After implementation, `python scripts/validate_workspace.py` configured CMake, built `fesa_core` and `fesa_tests`, and ran CTest successfully. -- CTest result: 1 test executable passed. - -Follow-up: -- Continue with P1R-08 degenerated-continuum displacement, covariant strain rows, and MITC shear tying. -- The existing stiffness kernel still needs to be rebuilt against the new formulation in steps 8 through 10; Step 7 intentionally did not add stiffness, stress, strain, or S4R behavior. - -### 2026-05-04 - P1R-06 results model and displacement CSV comparator completed -Author: Codex - -Changed files: -- `include/fesa/fesa.hpp` -- `tests/test_main.cpp` -- `docs/RESULTS_SCHEMA.md` -- `docs/VERIFICATION_PLAN.md` -- `phases/1-linear-static-mitc4-rebaseline/index.json` -- `PLAN.md` -- `PROGRESS.md` - -Summary: -- Added result model tests for Phase 1 root metadata, frame `0` metadata, and mandatory `U`/`RF` field labels, positions, entity type, and global basis. -- Extended the in-memory result model with solver/schema metadata, linear-static frame metadata, nodal field metadata, and mandatory `U`/`RF` descriptions while keeping `S`, `E`, and `SF` optional. -- Added string/stream-based Abaqus displacement CSV loading so tests can cover required headers, duplicate node labels, missing/non-numeric node labels, and nonnumeric component values without temporary files. -- Strengthened displacement comparison so it matches by node id, verifies exact FESA `U` component labels and nodal/global metadata, rejects duplicate actual node ids, reports missing FESA nodes, and uses combined absolute/relative tolerances. - -Verification: -- First ran `python scripts/validate_workspace.py` after adding Step 6 tests; it failed as expected because the result metadata fields and string CSV loader did not exist yet. -- After implementation, `python scripts/validate_workspace.py` configured CMake, built `fesa_core` and `fesa_tests`, and ran CTest successfully. -- CTest result: 1 test executable passed. - -Follow-up: -- Continue with P1R-07 MITC4 geometry, node order, tying points, directors, and local bases. -- Keep RF reference CSV availability open; current RF verification remains internal full-vector equilibrium until a stored `*_reactions.csv` artifact is provided. - -### 2026-05-04 - P1R-05 DofManager and reaction foundation completed -Author: Codex - -Changed files: -- `include/fesa/fesa.hpp` -- `tests/test_main.cpp` -- `phases/1-linear-static-mitc4-rebaseline/index.json` -- `PLAN.md` -- `PROGRESS.md` - -Summary: -- Added DofManager tests for six-DOF global ordering, noncontiguous node-id stability, constrained/free partitioning, equation numbering, full-vector reduction/reconstruction, element sparse-connectivity inputs, and a full-system reaction recovery formula. -- Extended `DofManager` with full DOF addresses, constrained full-index lists, full-vector reduction, element full-index connectivity, and element equation-id connectivity while keeping equation ids outside `Node` and `Element`. -- Added `recoverFullReaction(K_full, U_full, F_full)` and routed `LinearStaticAnalysis` through it so reaction recovery remains a full-vector operation rather than a reduced-vector shortcut. -- Updated assembly to consume element full DOF indices from `DofManager`, preserving DofManager ownership of sparse-pattern inputs. - -Verification: -- First ran `python scripts/validate_workspace.py` after adding tests; it failed as expected because the new DofManager/reaction APIs did not exist yet. -- After implementing the DofManager and reaction-foundation APIs, `python scripts/validate_workspace.py` configured CMake, built `fesa_core` and `fesa_tests`, and ran CTest successfully. -- CTest result: 1 test executable passed. - -Follow-up: -- Continue with P1R-06 results model and displacement CSV comparator foundation. -- Keep RF reference CSV availability open; current RF verification remains internal full-vector equilibrium until a stored `*_reactions.csv` artifact is provided. - -### 2026-05-04 - P1R-04 validation and singular diagnostics completed -Author: Codex - -Changed files: -- `include/fesa/fesa.hpp` -- `tests/test_main.cpp` -- `phases/1-linear-static-mitc4-rebaseline/index.json` -- `PLAN.md` -- `PROGRESS.md` - -Summary: -- Added negative validation tests for missing node/property/material references, missing shell-section/boundary/load sets, missing set members, non-positive shell thickness, invalid boundary/load DOFs, no active elements, no free DOFs, isolated free DOFs, and weak drilling DOF smoke coverage. -- Strengthened `validateDomain` so it reports missing `Nset` node members, missing `Elset` element members, non-positive or non-finite shell thickness, invalid Abaqus DOF ranges, no-free-DOF states, free DOFs untouched by active element connectivity, and free drilling DOF weak-stabilization warnings. -- Kept mesh quality diagnostics out of Phase 1; no aspect ratio, skew, warpage, or distortion diagnostics were added. -- Added keyword context to solver size, solver singularity, and fallback no-free-DOF diagnostics. - -Verification: -- First ran `python scripts/validate_workspace.py` after adding tests; it failed as expected on the missing new diagnostics. -- After implementing the validation diagnostics, `python scripts/validate_workspace.py` configured CMake, built `fesa_core` and `fesa_tests`, and ran CTest successfully. -- CTest result: 1 test executable passed. - -Follow-up: -- Continue with P1R-05 DofManager and reaction-foundation revalidation. -- Keep RF reference CSV availability open; current Phase 1 RF validation remains internal full-vector equilibrium until a stored `*_reactions.csv` artifact is provided. - -### 2026-05-04 - P1R-03 parser/domain subset completed -Author: Codex - -Changed files: -- `include/fesa/fesa.hpp` -- `tests/test_main.cpp` -- `docs/ABAQUS_INPUT_SUBSET.md` -- `phases/1-linear-static-mitc4-rebaseline/index.json` -- `PLAN.md` -- `PROGRESS.md` - -Summary: -- Added parser acceptance coverage for repeated explicit sets, generated `*Nset` and `*Elset`, `D` exponent numeric input, keyword-line continuation, and every Phase 1 structural input keyword. -- Expanded unsupported-feature coverage for `S4R`, `Part`, `Assembly`, `Instance`, `*Include`, `*Density`, and `NLGEOM=YES`. -- Added negative coverage for unsupported parameters and flags on otherwise supported keywords, unsupported shell-section data modes, malformed numeric fields, and invalid DOF fields with file, line, and keyword diagnostics. -- Tightened the parser so supported keywords reject unknown parameters/flags, fixed-width data rows reject extra non-empty fields, generated sets require exactly `first,last,increment`, and keyword continuation lines are assembled before keyword parsing. -- Clarified `docs/ABAQUS_INPUT_SUBSET.md` so the strict parameter and fixed-width data-row rules are explicit. - -Verification: -- First ran `python scripts/validate_workspace.py` after adding unsupported-parameter tests; it failed as expected because the parser still accepted supported keywords with unknown controls. -- After implementing strict keyword-control rejection, validation passed. -- Added keyword-continuation tests and first observed the expected failure; after implementing continuation handling, `python scripts/validate_workspace.py` configured CMake, built `fesa_core` and `fesa_tests`, and ran CTest successfully. -- CTest result: 1 test executable passed. - -Follow-up: -- Continue with P1R-04 validation and singular diagnostic coverage. -- Keep `Part/Assembly/Instance`, `S4R`, `*Density`, and `NLGEOM=YES` outside the Phase 1 parser until an ADR and parser contract intentionally broaden the subset. - -### 2026-05-04 - P1R-02 core harness guardrails completed -Author: Codex - -Changed files: -- `include/fesa/fesa.hpp` -- `tests/test_main.cpp` -- `phases/1-linear-static-mitc4-rebaseline/index.json` -- `PLAN.md` -- `PROGRESS.md` - -Summary: -- Strengthened C++ guardrail tests for centralized numeric aliases: `Real`, `GlobalId`, `LocalIndex`, `EquationId`, and `SparseIndex`. -- Expanded DOF mapping coverage to prove all six shell DOFs round-trip with Abaqus DOF numbers `1..6`, labels, and invalid DOF rejection. -- Added compile-time type checks so the core aliases remain `double` and signed `std::int64_t`. -- Added model-validation diagnostic context through a shared `makeDiagnostic` helper and populated source keywords for element, shell section, boundary, cload, analysis model, and missing target diagnostics. -- Kept the existing CMake/CTest validation path as the real repository validation command. - -Verification: -- First ran `python scripts/validate_workspace.py` after adding tests; it failed as expected because model diagnostics did not yet populate source keywords. -- After implementing the diagnostic guardrail, `python scripts/validate_workspace.py` configured CMake, built `fesa_core` and `fesa_tests`, and ran CTest successfully. -- CTest result: 1 test executable passed. - -Follow-up: -- Continue with P1R-03 parser/domain subset revalidation. -- Leave MITC4 stiffness and parser feature expansion to later dedicated steps. - -### 2026-05-04 - P1R-01 quad_02 reference onboarding completed -Author: Codex - -Changed files: -- `references/quad_02_phase1.inp` -- `references/quad_02_notes.md` -- `references/README.md` -- `docs/ABAQUS_INPUT_SUBSET.md` -- `docs/VERIFICATION_PLAN.md` -- `tests/test_main.cpp` -- `phases/1-linear-static-mitc4-rebaseline/index.json` -- `PLAN.md` -- `PROGRESS.md` - -Summary: -- Preserved `references/quad_02.inp` and `references/quad_02_displacements.csv` unchanged as the original Abaqus/CAE S4 reference provenance. -- Added `references/quad_02_phase1.inp`, a normalized Phase 1-compatible derivative input that removes `Part/Assembly/Instance`, `*Density`, restart/output requests, and unsupported step metadata while preserving ids, connectivity, material, shell thickness, fixed boundary nodes, load node, and load magnitude. -- Added `references/quad_02_notes.md` with provenance, compatibility, unit-system, result mapping, tolerance, and RF limitation notes. -- Added tests proving the original `quad_02.inp` remains unsupported provenance, the normalized input parses into 121 nodes and 100 S4 elements, and `quad_02_displacements.csv` loads 121 node rows. -- Updated reference and parser documentation so the normalized file is the accepted `quad_02` path for future Phase 1 stored-reference regression. - -Verification: -- First ran `python scripts/validate_workspace.py` after adding tests; it failed as expected because `quad_02_phase1.inp` did not exist yet. -- After adding the normalized input and documentation, `python scripts/validate_workspace.py` configured CMake, built `fesa_core` and `fesa_tests`, and ran CTest successfully. -- CTest result: 1 test executable passed. - -Follow-up: -- Continue with P1R-02 core harness guardrails. -- Keep `RF` verified by internal full-vector equilibrium until a `*_reactions.csv` artifact is provided. - -### 2026-05-04 - P1R-00 rebaseline audit completed -Author: Codex - -Changed files: -- `phases/1-linear-static-mitc4-rebaseline/step0-audit.md` -- `phases/1-linear-static-mitc4-rebaseline/index.json` -- `PLAN.md` -- `PROGRESS.md` - -Summary: -- Audited the existing C++ Phase 1 implementation against the revised paper-based `docs/MITC4_FORMULATION.md` without changing production solver behavior. -- Classified reusable scaffolding for revalidation: CMake/CTest validation, core aliases, DOF mapping, diagnostics, parser/domain skeleton, in-memory `U`/`RF` results, displacement CSV comparator, full-vector reaction recovery, and the linear static workflow. -- Identified MITC4 conflicts requiring rebuild: averaged-edge local basis, 2D projected Jacobian, direct Cartesian-like shear row construction, membrane/bending/shear pre-integration, midsurface-only `2 x 2` integration, and old `1.0e-6` drilling stabilization. -- Preserved the compatibility decision that `quad_02.inp` is an S4 reference artifact but still contains `Part/Assembly/Instance` and `*Density`, so Step 1 must choose a normalized-input or explicit parser-compatibility path. - -Verification: -- Inspected `include/fesa/fesa.hpp`, `tests/test_main.cpp`, current reference files, and active rebaseline phase contracts. -- `python scripts/validate_workspace.py` configured CMake, built `fesa_core` and `fesa_tests`, and ran CTest successfully. - -Follow-up: -- Continue with P1R-01 reference onboarding. -- Revalidate retained scaffolding in its assigned steps before treating it as Phase 1 evidence. - -### 2026-05-04 - Phase 1 rebaseline steps redefined -Author: Codex - -Changed files: -- `phases/index.json` -- `phases/1-linear-static-mitc4-rebaseline/index.json` -- `phases/1-linear-static-mitc4-rebaseline/step0.md` -- `phases/1-linear-static-mitc4-rebaseline/step1.md` -- `phases/1-linear-static-mitc4-rebaseline/step2.md` -- `phases/1-linear-static-mitc4-rebaseline/step3.md` -- `phases/1-linear-static-mitc4-rebaseline/step4.md` -- `phases/1-linear-static-mitc4-rebaseline/step5.md` -- `phases/1-linear-static-mitc4-rebaseline/step6.md` -- `phases/1-linear-static-mitc4-rebaseline/step7.md` -- `phases/1-linear-static-mitc4-rebaseline/step8.md` -- `phases/1-linear-static-mitc4-rebaseline/step9.md` -- `phases/1-linear-static-mitc4-rebaseline/step10.md` -- `phases/1-linear-static-mitc4-rebaseline/step11.md` -- `phases/1-linear-static-mitc4-rebaseline/step12.md` -- `phases/1-linear-static-mitc4-rebaseline/step13.md` -- `phases/1-linear-static-mitc4-rebaseline/step14.md` -- `phases/1-linear-static-mitc4-rebaseline/step15.md` -- `PLAN.md` -- `PROGRESS.md` - -Summary: -- Added a new 16-step Phase 1 rebaseline phase that supersedes the old Phase 1 execution path while preserving the old phase as historical record. -- Split the redo into audit, reference onboarding, core guardrails, parser/domain, validation diagnostics, DOF/reaction foundation, results/comparator, MITC4 geometry, MITC4 covariant strain tying, material/integration, stiffness/drilling, patch benchmarks, assembly, linear static workflow, stored-reference regression, and evaluator closeout. -- Updated `PLAN.md` so new agents execute `phases/1-linear-static-mitc4-rebaseline` and treat the old `1-linear-static-mitc4` phase as blocked/superseded. -- Kept unresolved decisions visible: `quad_02` normalization versus explicit parser compatibility sprint, RF CSV availability, and the PRD target of three stored Phase 1 reference cases. - -Verification: -- Parsed `phases/index.json` and `phases/1-linear-static-mitc4-rebaseline/index.json` with PowerShell `ConvertFrom-Json`. -- Verified all 16 rebaseline step files contain the required sprint contract sections from `docs/HARNESS_ENGINEERING.md`. -- `python scripts/validate_workspace.py` configured CMake, built `fesa_core` and `fesa_tests`, and ran CTest successfully. -- CTest result: 1 test executable passed. - -Follow-up: -- Start with `python scripts/execute.py 1-linear-static-mitc4-rebaseline` when ready. -- Step 1 must resolve how to use `quad_02` without silently expanding parser support. - -### 2026-05-04 - MITC4 formulation reset from local papers -Author: Codex - -Changed files: -- `docs/Paper/` -- `docs/MITC4_FORMULATION.md` -- `docs/ADR.md` -- `docs/NUMERICAL_CONVENTIONS.md` -- `docs/README.md` -- `docs/ABAQUS_INPUT_SUBSET.md` -- `docs/VERIFICATION_PLAN.md` -- `references/quad_02.inp` -- `references/quad_02_displacements.csv` -- `PLAN.md` -- `PROGRESS.md` - -Summary: -- Read the local MITC4 paper set under `docs/Paper/` and rewrote `docs/MITC4_FORMULATION.md` as the new Phase 1 formulation contract. -- Replaced the earlier simplified baseline with degenerated-continuum geometry, convected covariant strain components, FESA/Abaqus S4 tying-point convention, MITC transverse shear interpolation, local/global six-DOF rotation transformation, `2 x 2 x 2` Gauss integration, and thesis-backed drilling stabilization using `drilling_stiffness_scale = 1.0e-3`. -- Updated ADR-018, numerical conventions, and documentation readiness notes so the old `1.0e-6` averaged-edge baseline is no longer the active rule. -- Recorded `quad_02` as the new stored S4 reference pair while keeping its `Part/Assembly/Instance` and `*Density` features outside automatic Phase 1 parser acceptance. -- Updated `PLAN.md` so the next work is a Phase 1 redo/rebaseline, not continuation of the old P1-15/P1-16 path. - -Verification: -- `python scripts/validate_workspace.py` configured CMake, built `fesa_core` and `fesa_tests`, and ran CTest successfully. -- CTest result: 1 test executable passed. - -Follow-up: -- Draft new Phase 1 redo sprint contracts against the revised MITC4 formulation before modifying solver code. -- Decide whether `quad_02.inp` should be normalized into the current parser subset or used to justify a dedicated Abaqus/CAE `Part/Assembly/Instance` parser sprint. -- Decide whether Phase 1 `RF` gets Abaqus `*_reactions.csv` artifacts or remains verified by full-vector equilibrium tests. - -### 2026-05-01 - P1-01 through P1-14 implementation pass -Author: Codex - -Changed files: -- `CMakeLists.txt` -- `include/fesa/fesa.hpp` -- `src/fesa.cpp` -- `tests/test_main.cpp` -- `scripts/validate_workspace.py` -- `README.md` -- `docs/ADR.md` -- `docs/MITC4_FORMULATION.md` -- `PLAN.md` -- `PROGRESS.md` -- `phases/index.json` -- `phases/1-linear-static-mitc4/index.json` - -Summary: -- Added a CMake/CTest C++17 build and test harness, and updated `scripts/validate_workspace.py` to run CMake configure, build, and CTest directly. -- Added core numeric aliases, DOF mapping, diagnostics, Domain entities, Abaqus Phase 1 parser, Domain validation, DofManager, dense test matrix, deterministic Gaussian solver, in-memory result model, displacement CSV loader/comparator, MITC4 baseline stiffness kernel, full-system assembly, and `LinearStaticAnalysis`. -- Closed the Phase 1 MITC4 baseline decisions in `docs/MITC4_FORMULATION.md`: midside shear tying interpolation, averaged-edge local basis, 2x2 integration, `drilling_stiffness_scale = 1.0e-6`, and mandatory `U`/`RF` output scope. -- Added ADR-017 for the CMake/CTest build harness and ADR-018 for the Phase 1 MITC4 baseline closure. -- Added tests for parser acceptance/rejection, `quad_01` unsupported provenance, validation diagnostics, DOF reconstruction, singular solve diagnostics, result fields, CSV loading/comparison, MITC4 shape/stiffness behavior, and end-to-end linear static RF recovery. -- Marked phase steps P1-01 through P1-14 completed in `phases/1-linear-static-mitc4/index.json`. -- Marked P1-15 blocked because no Phase 1-compatible Abaqus S4 reference case exists yet; P1-16 remains pending behind that blocker. - -Verification: -- `python scripts/validate_workspace.py` configured CMake, built `fesa_core` and `fesa_tests`, and ran CTest successfully. -- CTest result: 1 test executable passed, covering 12 named in-repo test cases. - -Follow-up: -- Add at least one Phase 1-compatible Abaqus `TYPE=S4` linear static input and matching `*_displacements.csv`. -- Add or explicitly defer `*_reactions.csv`; current RF validation is by internal full-vector equilibrium/reaction tests. -- After reference artifacts are added, unblock P1-15 and run stored-reference regression, then complete P1-16 evaluator closeout. - -### 2026-05-01 - P1-00 Phase 1 sprint contracts generated -Author: Codex - -Changed files: -- `phases/index.json` -- `phases/1-linear-static-mitc4/index.json` -- `phases/1-linear-static-mitc4/step0.md` -- `phases/1-linear-static-mitc4/step1.md` -- `phases/1-linear-static-mitc4/step2.md` -- `phases/1-linear-static-mitc4/step3.md` -- `phases/1-linear-static-mitc4/step4.md` -- `phases/1-linear-static-mitc4/step5.md` -- `phases/1-linear-static-mitc4/step6.md` -- `phases/1-linear-static-mitc4/step7.md` -- `phases/1-linear-static-mitc4/step8.md` -- `phases/1-linear-static-mitc4/step9.md` -- `phases/1-linear-static-mitc4/step10.md` -- `phases/1-linear-static-mitc4/step11.md` -- `phases/1-linear-static-mitc4/step12.md` -- `phases/1-linear-static-mitc4/step13.md` -- `phases/1-linear-static-mitc4/step14.md` -- `phases/1-linear-static-mitc4/step15.md` -- `PLAN.md` -- `PROGRESS.md` - -Summary: -- Converted Phase 1 milestones P1-01 through P1-16 into executable Harness step files under `phases/1-linear-static-mitc4`. -- Added top-level and phase-level JSON indices using the zero-based `scripts/execute.py` convention. -- Embedded sprint contracts in every step with objective, required reading, scope, allowed files, explicit non-goals, tests to write first, reference-artifact policy, acceptance command, evaluator checklist, and handoff requirements. -- Kept readiness blockers visible, especially MITC4 formulation decisions, build-system selection, reference comparator tolerance, missing reaction CSV, and the need for Phase 1-compatible `TYPE=S4` references. -- Updated `PLAN.md` so new agents can find and execute the active phase files. - -Verification: -- Parsed `phases/index.json` and `phases/1-linear-static-mitc4/index.json` with PowerShell `ConvertFrom-Json`. -- Verified the phase registry points to `1-linear-static-mitc4`, all 16 steps are `pending`, step names are kebab-case, and every `stepN.md` file exists. -- Verified every step file includes the required sprint contract sections from `docs/HARNESS_ENGINEERING.md`. -- `python scripts/validate_workspace.py` exited successfully, but still reported no configured validation commands. - -Follow-up: -- Begin Phase 1 execution with `python scripts/execute.py 1-linear-static-mitc4` after confirming readiness blockers are accepted, resolved, or intentionally deferred. - -### 2026-05-01 - Phase 1 implementation master plan added -Author: Codex - -Changed files: -- `PLAN.md` -- `PROGRESS.md` - -Summary: -- Expanded `PLAN.md` from a short implementation sequence into a Phase 1 master implementation plan. -- Added Phase 1 Definition of Done, execution gates, milestone backlog P1-00 through P1-16, sprint contract rules, verification strategy, reference plan, and risk controls. -- Kept implementation blocked behind readiness decisions for MITC4 formulation, build system, reference comparator, reaction verification, and Phase 1-compatible reference input. -- Aligned the plan with the Planner -> Generator -> Evaluator harness in `docs/HARNESS_ENGINEERING.md`. - -Verification: -- Reviewed the plan against `docs/PRD.md`, `docs/ARCHITECTURE.md`, `docs/HARNESS_ENGINEERING.md`, `docs/NUMERICAL_CONVENTIONS.md`, `docs/ABAQUS_INPUT_SUBSET.md`, `docs/VERIFICATION_PLAN.md`, `docs/RESULTS_SCHEMA.md`, and `docs/MITC4_FORMULATION.md`. -- `python scripts/validate_workspace.py` ran, but reported no configured validation commands. - -Follow-up: -- Convert P1 milestones into `phases/` step files with sprint contract sections when the user asks for executable phase planning. - -### 2026-05-01 - Planner/Generator/Evaluator harness structure added -Author: Codex - -Changed files: -- `AGENTS.md` -- `PLAN.md` -- `PROGRESS.md` -- `README.md` -- `docs/README.md` -- `docs/HARNESS_ENGINEERING.md` -- `docs/MULTI_AGENT_RESEARCH_PLAN.md` -- `.codex/agents/harness-sprint-planner.toml` -- `.codex/agents/implementation-generator.toml` -- `.codex/agents/harness-sprint-evaluator.toml` -- `.codex/agents/phase-planner.toml` -- `.codex/agents/harness-reviewer.toml` -- `.codex/agents/test-strategy-reviewer.toml` -- `.codex/skills/fesa-phase-planning/SKILL.md` -- `.codex/skills/fesa-review/SKILL.md` -- `.codex/skills/fesa-cpp-tdd/SKILL.md` -- `plugins/fesa-commands/commands/phase-draft.md` - -Summary: -- Added `docs/HARNESS_ENGINEERING.md` as the durable Planner -> Generator -> Evaluator contract for long-running FESA work. -- Updated `AGENTS.md` so nontrivial solver, parser, result schema, reference comparator, MITC4, and phase execution work requires a sprint contract before implementation. -- Added custom agents for sprint contract planning, contract-bound implementation, and independent sprint evaluation. -- Updated existing planner/reviewer/test strategy guidance to enforce contract compliance, evaluator pass/fail review, TDD, and PLAN/PROGRESS handoff. - -Verification: -- `.codex/agents/*.toml` parsed successfully with Python `tomllib`. -- Codex skill and plugin command frontmatter checks passed. -- `python scripts/validate_workspace.py` ran, but reported no configured validation commands. - -Follow-up: -- When implementation planning begins, generate phase steps with sprint contract sections before assigning Generator work. - -### 2026-05-01 - Abaqus reference CSV contract adopted -Author: Codex - -Changed files: -- `AGENTS.md` -- `README.md` -- `PLAN.md` -- `PROGRESS.md` -- `docs/README.md` -- `docs/PRD.md` -- `docs/ARCHITECTURE.md` -- `docs/ADR.md` -- `docs/NUMERICAL_CONVENTIONS.md` -- `docs/ABAQUS_INPUT_SUBSET.md` -- `docs/VERIFICATION_PLAN.md` -- `docs/RESULTS_SCHEMA.md` -- `docs/MITC4_FORMULATION.md` -- `docs/MULTI_AGENT_RESEARCH_PLAN.md` -- `references/README.md` -- `.codex/agents/*.toml` -- `.codex/skills/*.md` -- `plugins/fesa-commands/commands/*.md` - -Summary: -- Accepted `references/` as the project reference artifact root. -- Documented the initial artifact pair `references/quad_01.inp` and `references/quad_01_displacements.csv`. -- Adopted Abaqus-exported `*_displacements.csv` as the first automated displacement comparison format. -- Mapped CSV columns `Node Label`, `U-U1`, `U-U2`, `U-U3`, `UR-UR1`, `UR-UR2`, `UR-UR3` to FESA `U` components `UX`, `UY`, `UZ`, `RX`, `RY`, `RZ`. -- Documented that `quad_01.inp` includes `S4R`, `Part/Assembly/Instance`, `*Density`, and `NLGEOM=YES`; it is stored reference provenance and a future compatibility target, not a Phase 1 parser acceptance expansion. - -Verification: -- Inspected `quad_01_displacements.csv`: 121 data rows and the required Abaqus displacement/rotation columns. -- Parsed documentation and Codex extension metadata checks. -- `python scripts/validate_workspace.py` ran, but reported no configured validation commands. - -Follow-up: -- Add or define reaction-force reference artifacts, preferably `*_reactions.csv`, or verify `RF` by equilibrium tests until Abaqus RF CSV is available. -- Add at least one Phase 1-compatible `TYPE=S4` reference input for the first MITC4 linear static implementation path. - -### 2026-05-01 - FESA commands converted to repo plugin -Author: Codex - -Changed files: -- `plugins/fesa-commands/.codex-plugin/plugin.json` -- `plugins/fesa-commands/commands/*.md` -- `.agents/plugins/marketplace.json` -- `.codex/commands/*.md` -- `.codex/hooks/pre_edit_policy.py` -- `.codex/hooks/post_tool_use_policy.py` -- `PLAN.md` -- `PROGRESS.md` - -Summary: -- Created the repo-local `fesa-commands` plugin and registered it in `.agents/plugins/marketplace.json`. -- Moved the FESA command prompts from `.codex/commands/` into `plugins/fesa-commands/commands/`. -- Removed the old `.codex/commands/*.md` files so plugin commands are the single maintained location. -- Updated hook policy scripts to watch plugin manifests, plugin commands, and marketplace registration files. -- Resolved the prior `.codex/commands` discovery concern by converting the commands to plugin form. - -Verification: -- Parsed plugin manifests and `.agents/plugins/marketplace.json` with Python `json`. -- Checked plugin command Markdown frontmatter. -- Parsed `.codex/config.toml` and `.codex/agents/*.toml` with Python `tomllib`. -- Parsed `.codex/hooks.json` with Python `json`. -- `python scripts/validate_workspace.py` ran, but reported no configured validation commands. - -Follow-up: -- Confirm that the `fesa-commands` plugin appears in the active Codex plugin/command surface. - -### 2026-05-01 - Project-local Codex extension pack added -Author: Codex - -Changed files: -- `.codex/config.toml` -- `.codex/hooks.json` -- `.codex/agents/*.toml` -- `.codex/commands/*.md` -- `.codex/skills/*/SKILL.md` -- `.codex/hooks/*.py` -- `PLAN.md` -- `PROGRESS.md` - -Summary: -- Added focused project agents for reference artifact curation, numerical convention review, solver architecture, sparse solver design, HDF5 results schema, DOF/boundary conditions, C++ build planning, MITC4 implementation review, test strategy, and PLAN/PROGRESS auditing. -- Added project command prompts for status, readiness, plan sync, reference checks, documentation guards, phase drafting, ADR work, benchmark onboarding, extension verification, and handoff. -- Added project-local FESA skills and registered them through `.codex/config.toml`. -- Added hooks for session startup context, pre-edit coordination reminders, post-edit validation reminders, and expanded destructive shell command blocking. - -Verification: -- Parsed `.codex/config.toml` and `.codex/agents/*.toml` with Python `tomllib`. -- Parsed `.codex/hooks.json` with Python `json`. -- Checked `.codex/skills/*/SKILL.md` and `.codex/commands/*.md` frontmatter. -- Smoke-tested the new hook scripts with representative JSON payloads. -- `python scripts/validate_workspace.py` ran, but reported no configured validation commands. - -Follow-up: -- Resolved later by converting `.codex/commands/*.md` into the `fesa-commands` repo plugin. -- Confirm hook behavior in the actual Codex runtime on native Windows. - -### 2026-05-01 - PLAN/PROGRESS coordination files added -Author: Codex - -Changed files: -- `PLAN.md` -- `PROGRESS.md` -- `AGENTS.md` -- `docs/README.md` -- `docs/MULTI_AGENT_RESEARCH_PLAN.md` -- `.codex/agents/phase-planner.toml` -- `.codex/agents/harness-reviewer.toml` - -Summary: -- Added `PLAN.md` as the shared forward-looking work plan for multi-agent coordination. -- Added `PROGRESS.md` as the shared chronological progress, verification, blocker, and risk log. -- Updated `AGENTS.md` so every new work session must read `PROGRESS.md` and `PLAN.md` before planning or editing. -- Updated documentation index and Codex agent instructions so planning/review agents enforce PLAN/PROGRESS usage. - -Verification: -- `.codex/agents/*.toml` parsed successfully with Python `tomllib`. -- `python scripts/validate_workspace.py` ran, but reported no configured validation commands. - -### 2026-05-01 - Documentation coordination and multi-agent planning state -Author: Codex - -Changed files: -- `AGENTS.md` -- `README.md` -- `docs/README.md` -- `docs/PRD.md` -- `docs/ARCHITECTURE.md` -- `docs/ADR.md` -- `docs/NUMERICAL_CONVENTIONS.md` -- `docs/ABAQUS_INPUT_SUBSET.md` -- `docs/VERIFICATION_PLAN.md` -- `docs/RESULTS_SCHEMA.md` -- `docs/MITC4_FORMULATION.md` -- `docs/MULTI_AGENT_RESEARCH_PLAN.md` -- `.codex/agents/*.toml` - -Summary: -- Added `docs/README.md` as documentation index and implementation readiness gate. -- Reinforced Phase 1 invariants across project docs. -- Added readiness gates for numerical conventions, parser acceptance, reference onboarding, mandatory result outputs, and MITC4 pre-implementation decisions. -- Updated Codex agent definitions so delegated agents read the current documentation set. -- Root `README.md` now points to the FESA documentation entry point. - -Verification: -- `.codex/agents/*.toml` parsed successfully with Python `tomllib`. -- `python scripts/validate_workspace.py` ran, but reported no configured validation commands. - -Follow-up: -- Keep `PLAN.md` and `PROGRESS.md` current for multi-agent coordination. - -### 2026-04-22 - Technical dossier documents added -Author: Codex - -Changed files: -- `docs/NUMERICAL_CONVENTIONS.md` -- `docs/ABAQUS_INPUT_SUBSET.md` -- `docs/VERIFICATION_PLAN.md` -- `docs/RESULTS_SCHEMA.md` -- `docs/MITC4_FORMULATION.md` -- `AGENTS.md` -- `docs/PRD.md` -- `docs/ARCHITECTURE.md` -- `docs/ADR.md` -- `docs/MULTI_AGENT_RESEARCH_PLAN.md` -- `.codex/agents/*.toml` - -Summary: -- Captured user decisions: 6 DOF shell nodes, artificial drilling stiffness, Abaqus-style units and signs, constrained DOF elimination, full-vector reaction recovery, no Phase 1 mesh quality diagnostics, singular diagnostics required, `double`, int64 indexing, S4-to-MITC4 mapping, S4R deferral. -- Added technical dossier documents for numerical conventions, Abaqus subset, verification, results schema, and MITC4 formulation. -- Added ADRs for numerical baseline, boundary/reaction policy, drilling stabilization, S4/S4R policy, singular diagnostics, and technical dossier contracts. - -Verification: -- `.codex/agents/*.toml` parsed successfully with Python `tomllib`. -- `python scripts/validate_workspace.py` ran, but reported no configured validation commands. - -### 2026-04-21 - Initial architecture and agent setup -Author: Codex - -Changed files: -- `docs/PRD.md` -- `docs/ARCHITECTURE.md` -- `docs/ADR.md` -- `AGENTS.md` -- `docs/MULTI_AGENT_RESEARCH_PLAN.md` -- `.codex/agents/fem-literature-researcher.toml` -- `.codex/agents/verification-benchmark-researcher.toml` -- `.codex/agents/mitc4-formulation-researcher.toml` -- `.codex/agents/abaqus-compatibility-researcher.toml` - -Summary: -- Established solver architecture direction: runtime polymorphism, Strategy + Template Method, Factory + Registry, adapter boundaries, immutable `Domain`, mutable `AnalysisState`, `DofManager` ownership, step/frame/history results. -- Created first research agents for FEM literature, verification benchmarks, MITC4 formulation, and Abaqus compatibility. - -Verification: -- `.codex/agents/*.toml` parsed successfully with Python `tomllib`. -- `python scripts/validate_workspace.py` ran, but reported no configured validation commands. - -## Known Blockers -- `quad_02_reactionforces.csv` is onboarded and comparable, but the current FESA node-wise `RF` result does not pass against Abaqus RF/RM values. -- The PRD target of three stored Phase 1 reference cases is not yet satisfied; only `quad_02_phase1` is an active stored displacement regression. -- The current initial `quad_01.inp` reference contains `S4R`, `Part/Assembly/Instance`, `*Density`, and `NLGEOM=YES`, so it is not a Phase 1 parser acceptance case as-is. - -## Current Risks -- Implementation could start from the `quad_01` reference input without accounting for its unsupported Abaqus features. -- Implementation could treat the old MITC4 kernel as authoritative even though it conflicts with the revised formulation contract. -- Future work could accidentally parse the original `quad_02.inp` instead of the normalized `quad_02_phase1.inp` before parser compatibility is explicitly expanded. -- Reaction output may remain Abaqus-incompatible at node level until the `quad_02` RF mismatch is explained. -- Large-model support may be weakened if any module narrows ids or sparse indices below int64. -- Future source/body hardening could accidentally change behavior if inline module implementations are moved into `.cpp` files without preserving the current characterization and `quad_02_phase1` regression tests. diff --git a/README.md b/README.md deleted file mode 100644 index 0057d93..0000000 --- a/README.md +++ /dev/null @@ -1,50 +0,0 @@ -# FESA - -FESA is a C++17 finite element structural analysis solver project. The first milestone is a reference-verified linear static MITC4 shell solver with an Abaqus-compatible input subset, HDF5-oriented results, and explicit numerical conventions. - -## Current Phase 1 Direction -- MITC4 shell element baseline formulation. -- Linear elastic material. -- Nodal loads and fixed boundary conditions. -- Abaqus input subset with `S4` mapped to FESA `MITC4`. -- `S4R` deferred. -- 6 shell DOFs per node: `UX`, `UY`, `UZ`, `RX`, `RY`, `RZ`. -- Small artificial drilling stiffness. -- Constrained DOF elimination. -- Full-vector reaction recovery. -- `double` precision and int64 ids/indices/equation numbering. -- Stored-reference comparison from `references/*.inp` and `references/*_displacements.csv` without requiring local Abaqus execution. - -## Reference Artifacts -Stored Abaqus examples live under [references](references). - -The initial accepted artifact pair is: -- [quad_01.inp](references/quad_01.inp) -- [quad_01_displacements.csv](references/quad_01_displacements.csv) - -Reference CSV displacement columns use Abaqus labels `Node Label`, `U-U1`, `U-U2`, `U-U3`, `UR-UR1`, `UR-UR2`, `UR-UR3`, mapped to FESA `UX`, `UY`, `UZ`, `RX`, `RY`, `RZ`. - -## Documentation Entry Point -Start with [docs/README.md](docs/README.md). - -The core project documents are: -- [AGENTS.md](AGENTS.md) -- [docs/HARNESS_ENGINEERING.md](docs/HARNESS_ENGINEERING.md) -- [docs/PRD.md](docs/PRD.md) -- [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) -- [docs/ADR.md](docs/ADR.md) -- [docs/NUMERICAL_CONVENTIONS.md](docs/NUMERICAL_CONVENTIONS.md) -- [docs/ABAQUS_INPUT_SUBSET.md](docs/ABAQUS_INPUT_SUBSET.md) -- [docs/VERIFICATION_PLAN.md](docs/VERIFICATION_PLAN.md) -- [docs/RESULTS_SCHEMA.md](docs/RESULTS_SCHEMA.md) -- [docs/MITC4_FORMULATION.md](docs/MITC4_FORMULATION.md) - -## Validation -The default repository validation command is: - -```bash -python scripts/validate_workspace.py -``` - -With the Phase 1 CMake harness in place, this command configures CMake, builds the C++ tests, and runs CTest. - diff --git a/docs/ABAQUS_INPUT_SUBSET.md b/docs/ABAQUS_INPUT_SUBSET.md deleted file mode 100644 index 04c2fca..0000000 --- a/docs/ABAQUS_INPUT_SUBSET.md +++ /dev/null @@ -1,253 +0,0 @@ -# Abaqus Input Subset - -## Purpose -This document defines the Abaqus `.inp` subset supported by FESA Phase 1. - -FESA aims for strict, explicit compatibility with a small subset rather than partial silent interpretation of the full Abaqus language. - -## Source Basis -- Abaqus input files use keyword lines, data lines, and comment lines; keyword and parameter names are case-insensitive, comma-separated, and may use continuation lines: https://abaqus-docs.mit.edu/2017/English/SIMACAEMODRefMap/simamod-c-inputsyntax.htm -- Abaqus sets are a central reference mechanism for nodes and elements: https://abaqus-docs.mit.edu/2017/English/SIMACAEMODRefMap/simamod-c-inputsyntax.htm -- Abaqus shell library documents S4 as a 4-node general-purpose shell and S4R as a reduced-integration shell with hourglass control: https://abaqus-docs.mit.edu/2017/English/SIMACAEELMRefMap/simaelm-r-shellgeneral.htm -- Abaqus `*BOUNDARY` direct data lines use node or node set, first DOF, optional last DOF, and optional magnitude: https://abaqus-docs.mit.edu/2017/English/SIMACAEKEYRefMap/simakey-r-boundary.htm -- Abaqus `*CLOAD` data lines use node or node set, DOF, and reference load magnitude: https://abaqus-docs.mit.edu/2017/English/SIMACAEKEYRefMap/simakey-r-cload.htm - -## Phase 1 Supported Keywords -| Keyword | Status | FESA Object | Notes | -|---|---|---|---| -| `*Node` | Supported | `Node` | 3D coordinates only | -| `*Element` | Supported | `Element` | `TYPE=S4` maps to `MITC4` | -| `*Nset` | Supported | `NodeSet` | Explicit list and `GENERATE` should be supported | -| `*Elset` | Supported | `ElementSet` | Explicit list and `GENERATE` should be supported | -| `*Material` | Supported | `Material` | `NAME` required | -| `*Elastic` | Supported | `LinearElasticMaterial` | Isotropic `E, nu` only | -| `*Shell Section` | Supported | `ShellProperty` | Homogeneous shell section only | -| `*Boundary` | Supported | `FixBoundaryCondition` | Direct zero-valued constraints | -| `*Cload` | Supported | `NodalLoad` | Concentrated forces/moments | -| `*Step` | Supported | `StepDefinition` | Static linear Phase 1 step | -| `*Static` | Accepted inside `*Step` | `LinearStaticAnalysis` | Optional for Phase 1 | -| `*End Step` | Supported | Step delimiter | Required for explicit step closure | - -Unsupported keywords must produce a clear diagnostic unless explicitly listed as ignorable metadata. - -## General Parser Rules -FESA parser rules: -- Keyword names and parameter names are case-insensitive. -- Keyword lines start with `*`. -- Comment lines start with `**` and are ignored. -- Data fields are comma-separated. -- Empty trailing fields may be ignored. -- Numeric data may use decimal or scientific notation. -- `D` exponents should be accepted and normalized as `E` exponents. -- Keyword continuation with a trailing comma should be supported for keyword lines. -- Data continuation should be supported only where this document explicitly allows it. -- Abbreviated Abaqus keywords are not supported in Phase 1. Require exact keyword names after case normalization. -- Supported keywords may use only the parameters and flags listed in this document. Unknown parameters and flags are errors, even when the keyword itself is supported. -- Fixed-width data rows for `*Node`, `*Element`, `*Elastic`, `*Shell Section`, `*Boundary`, and `*Cload` must not contain extra non-empty fields beyond the supported form. -- Include files through `INPUT=` are not supported in Phase 1. -- Part/Assembly/Instance syntax is not supported in Phase 1 unless added by ADR. -- Normalized derivative inputs such as `references/quad_02_phase1.inp` may be used for Phase 1 parser and solver tests when the original stored Abaqus file contains unsupported Abaqus/CAE scaffolding. - -## Stored Reference Inputs vs Supported Subset -Files under `references/` are allowed to preserve the exact Abaqus input used to generate reference results, even when the file contains features outside the current Phase 1 parser subset. - -Rules: -- A stored reference input is not automatically a supported FESA input. -- Unsupported reference features must be documented as compatibility notes. -- Parser implementation must still reject unsupported features until this document and ADRs explicitly add support. -- Test harnesses may use normalized or reduced derivative inputs for Phase 1 parser tests, but must keep the original Abaqus reference artifact unchanged. - -Current stored reference notes: -- `references/quad_01.inp` was generated by Abaqus/CAE Learning Edition 2024. -- It uses `TYPE=S4R`, `Part`, `Assembly`, `Instance`, `*Density`, and `NLGEOM=YES`, all of which are outside the current Phase 1 parser/solver subset. -- Its paired `references/quad_01_displacements.csv` is still valid as a stored displacement reference artifact for future compatibility and regression work. -- `references/quad_02.inp` uses `TYPE=S4`, so it targets the Phase 1 MITC4 element formulation, and its paired `references/quad_02_displacements.csv` has the accepted displacement CSV shape. -- `references/quad_02_reactionforces.csv` is a paired Abaqus RF/RM result export for the stored `quad_02` case. It is a reference result artifact and does not change parser scope. -- `quad_02.inp` still uses Abaqus/CAE `Part`, `Assembly`, `Instance`, and `*Density`; it is therefore a stored S4 reference artifact and compatibility decision point, not automatic parser acceptance as-is. -- `references/quad_02_phase1.inp` is the normalized Phase 1-compatible derivative input for `quad_02`. It preserves node ids, element ids/connectivity, S4 element type, elastic material, shell thickness, fixed boundary nodes, load node, and concentrated load while removing `Part/Assembly/Instance`, `*Density`, restart/output request keywords, and unsupported step metadata. - -## Labels and Names -Rules: -- Set and material labels are stored case-insensitively by default. -- Preserve the original spelling for diagnostics and result metadata. -- Labels must start with a letter unless quoted. -- Quoted labels may be accepted, but Phase 1 should warn if quoting is required for disambiguation. -- Labels beginning and ending with double underscores are reserved and should be rejected. - -## `*Node` -Supported form: - -```text -*Node -node_id, x, y, z -``` - -Rules: -- `node_id` is signed 64-bit integer. -- Coordinates are `double`. -- 2D node definitions are not supported for MITC4. -- Duplicate node ids are an error. -- Node ids need not be contiguous. - -## `*Element` -Supported form: - -```text -*Element, type=S4, elset=EALL -element_id, n1, n2, n3, n4 -``` - -Rules: -- `TYPE=S4` maps directly to FESA `MITC4`. -- `TYPE=S4R` is not supported in Phase 1. It is reserved for future support. -- Element and node ids are signed 64-bit integers. -- Four node connectivity entries are required. -- Duplicate element ids are an error. -- Missing nodes are an error. -- If `ELSET` is given, the element is added to that element set. -- Element node ordering follows Abaqus shell ordering and determines the positive normal by right-hand rule. - -## `*Nset` and `*Elset` -Supported explicit form: - -```text -*Nset, nset=FIXED -1, 2, 3, 4 -``` - -Supported generated form: - -```text -*Elset, elset=EALL, generate -1, 100, 1 -``` - -Rules: -- Explicit lists may span repeated data lines. -- `GENERATE` means `start, end, increment`. -- The increment must be positive. -- Set references to other sets are not required in Phase 1. -- `UNSORTED` is not required in Phase 1. -- Duplicates should be deduplicated while preserving a deterministic order for diagnostics. -- Missing referenced ids should be reported when the set is consumed by a property, load, or boundary condition. - -## `*Material` and `*Elastic` -Supported form: - -```text -*Material, name=STEEL -*Elastic -E, nu -``` - -Rules: -- Only isotropic linear elasticity is supported in Phase 1. -- Temperature dependence, field-variable dependence, orthotropic elasticity, plasticity, density, and damping are unsupported. -- `E` must be positive. -- `nu` must be in a physically meaningful isotropic range. For Phase 1, reject `nu <= -1.0` or `nu >= 0.5`. - -## `*Shell Section` -Supported form: - -```text -*Shell Section, elset=EALL, material=STEEL -thickness -``` - -Rules: -- `ELSET` and `MATERIAL` are required. -- Homogeneous single-layer shell sections only. -- Thickness is `double` and must be positive. -- Offsets, composite layups, section integration controls, orientations, temperature-dependent sections, and transverse shear stiffness overrides are unsupported in Phase 1. -- Thermal-stress coupling is a future feature and must not be inferred from Phase 1 section data. - -## `*Boundary` -Supported direct form: - -```text -*Boundary -node_or_nset, first_dof, last_dof, magnitude -``` - -Rules: -- `node_or_nset` may be a node id or node set label. -- DOFs are `1..6`. -- If `last_dof` is omitted, constrain only `first_dof`. -- Phase 1 supports zero-valued constraints. Omitted magnitude means zero. -- Nonzero prescribed displacement/rotation is not a Phase 1 requirement. -- Type-format boundary labels such as `PINNED`, `XSYMM`, or `ENCASTRE` are not supported unless later documented. -- Constrained DOFs are eliminated by `DofManager`. - -## `*Cload` -Supported form: - -```text -*Cload -node_or_nset, dof, magnitude -``` - -Rules: -- `node_or_nset` may be a node id or node set label. -- DOFs are `1..6`. -- Translational DOFs define concentrated forces. -- Rotational DOFs define concentrated moments. -- `FOLLOWER`, `AMPLITUDE`, `OP=NEW`, file-based loads, buoyancy/drag/inertia loads, and cyclic symmetry loads are unsupported in Phase 1. - -## `*Step`, `*Static`, and `*End Step` -Supported form: - -```text -*Step, name=Step-1 -*Static -*Cload -... -*Boundary -... -*End Step -``` - -Rules: -- Phase 1 supports linear static steps. -- `NLGEOM` is ignored only if explicitly false or absent. `NLGEOM=YES` must be rejected until nonlinear analysis is implemented. -- Multiple steps may be parsed into `Domain`, but Phase 1 execution may initially support one active static step if documented in implementation steps. -- Step activation should feed `AnalysisModel`. - -## Diagnostics -Required parser diagnostics: -- Unknown keyword. -- Unsupported keyword parameter. -- Missing required parameter. -- Duplicate node, element, material, property, or set definition. -- Missing node in element connectivity. -- Missing set used by shell section, boundary, or load. -- Unsupported element type such as `S4R`. -- Unsupported material or shell section mode. -- Invalid DOF number. -- Invalid generated set range. - -Diagnostic messages should include file path, line number, keyword, and offending token. - -## Minimum Parser Acceptance -Before Phase 1 parser work is considered ready for solver integration: -- Parse `*Node`, `*Element`, `*Nset`, `*Elset`, `*Material`, `*Elastic`, `*Shell Section`, `*Boundary`, `*Cload`, `*Step`, `*Static`, and `*End Step` smoke cases. -- Preserve original labels for diagnostics while resolving labels case-insensitively. -- Accept explicit and `GENERATE` node/element sets. -- Reject `TYPE=S4R` with an unsupported element diagnostic. -- Reject `NLGEOM=YES`. -- Reject unsupported Part/Assembly/Instance and Include syntax. -- Resolve shell section material and element set references. -- Resolve boundary/load node set references. -- Produce line-numbered diagnostics for malformed numeric fields and invalid DOF ids. - -## Explicit Non-Goals -- Abaqus `Part`, `Assembly`, `Instance`, and instance-qualified labels. -- `*Include`. -- `S4R`, `S4R5`, `S8R`, triangular shells, solid elements, beam elements. -- Pressure loads. -- RBE2/RBE3. -- Nonzero prescribed displacements. -- Amplitudes. -- Local coordinate transforms. -- Composite shell sections. -- Thermal-stress input. -- Mesh quality diagnostics. diff --git a/docs/ADR.md b/docs/ADR.md deleted file mode 100644 index 21be822..0000000 --- a/docs/ADR.md +++ /dev/null @@ -1,166 +0,0 @@ -# Architecture Decision Records - -## 철학 -솔버의 높은 성능과 정확도, Abaqus와의 높은 호환성 - ---- - -### ADR-001: Runtime Polymorphism 기반 Solver Core -**결정**: 요소, 재료, 하중, 경계조건, 해석 알고리즘은 base interface와 runtime polymorphism 기반으로 확장한다. - -**이유**: FESA는 MITC4 Shell 요소에서 시작하지만 RBE2/RBE3, 압력하중, 비선형 정적해석, 동적해석, 열전달, 1D/3D 요소로 확장될 예정이다. 초기에는 정확도와 테스트 가능성이 가장 중요하므로, 각 물리 객체를 독립적으로 테스트하고 교체할 수 있는 구조가 필요하다. - -**트레이드오프**: virtual dispatch 비용과 객체 분산에 따른 캐시 효율 저하가 발생할 수 있다. 대규모 모델 성능이 필요한 영역은 `Assembler`, element kernel, sparse solver 계층에서 batch 처리 또는 타입별 최적화를 추가한다. - ---- - -### ADR-002: Immutable Domain + Mutable AnalysisState -**결정**: 입력 모델 정의는 `Domain`에 보존하고 가능한 한 불변으로 취급한다. 해석 중 변하는 displacement, velocity, acceleration, temperature, force, residual, iteration 정보는 `AnalysisState`에 분리한다. - -**이유**: 선형 정적해석, 기하비선형 정적해석, 동적해석, 열전달 및 thermal-stress coupling은 서로 다른 상태 변수를 필요로 한다. 모델 정의와 해석 상태를 분리하면 restart, 결과 저장, reference 비교, 테스트가 쉬워진다. - -**트레이드오프**: `Domain`, `AnalysisModel`, `AnalysisState` 사이의 참조/id 관리가 필요하다. 단순한 단일 해석 코드보다 초기 구조가 복잡하지만, 다중 step 및 비선형/동적 확장성을 얻는다. - ---- - -### ADR-003: Step/Frame/Field/History 결과 모델 -**결정**: 해석 결과는 `ResultStep`, `ResultFrame`, `FieldOutput`, `HistoryOutput` 구조로 저장한다. - -**이유**: 정적해석, 비선형 increment, 동적 time frame, history output을 같은 결과 모델로 다루기 위해 Abaqus와 유사한 step/frame/history 개념이 필요하다. HDF5 저장 구조와 reference 결과 비교도 이 구조를 기준으로 설계한다. - -**트레이드오프**: Phase 1 선형 정적해석만 놓고 보면 결과 구조가 다소 무겁다. 그러나 Phase 2 이후의 비선형 반복, Phase 3 동해석, reaction/history 검증을 위해 초기에 최소 구조를 잡는 편이 낫다. - ---- - -### ADR-004: Strategy + Template Method 기반 Analysis 실행 -**결정**: 해석 알고리즘은 `Analysis` strategy로 분리하고, 공통 실행 흐름은 template method로 관리한다. - -**이유**: 선형 정적해석, Newton-Raphson 비선형 정적해석, HHT 동적해석, 열전달 해석은 조립, 경계조건 적용, solver 호출, 상태 갱신, 결과 저장이라는 공통 흐름을 공유한다. 공통 흐름을 유지하면서 해석별 반복 구조만 바꾸는 방식이 중복을 줄인다. - -**트레이드오프**: 공통 template이 지나치게 커지면 해석별 특수성이 숨겨질 수 있다. 따라서 `Analysis`는 전체 흐름을 조율하고, assembly, solver, convergence, time integration은 별도 strategy로 분리한다. - ---- - -### ADR-005: Factory + Registry 기반 Abaqus Input 객체 생성 -**결정**: Abaqus input keyword와 내부 객체 생성을 factory/registry 계층으로 분리한다. Phase 1 입력 범위에는 `*Node`, `*Element`, `*Nset`, `*Elset`, `*Material`, `*Elastic`, `*Shell Section`, `*Boundary`, `*Cload`, `*Step`을 포함한다. - -**이유**: Abaqus input format 호환성을 유지하면서 요소/재료/하중/경계조건 타입을 계속 추가해야 한다. parser 본체가 모든 타입을 직접 생성하면 확장할수록 변경 비용과 회귀 위험이 커진다. - -**트레이드오프**: registry 초기화와 타입 매핑 코드가 추가된다. 대신 새 keyword나 element type을 추가할 때 parser core의 변경을 최소화할 수 있다. - ---- - -### ADR-006: External Library Adapter Boundary -**결정**: MKL, TBB, HDF5는 solver core에 직접 노출하지 않고 adapter/wrapper 계층 뒤에서 사용한다. - -**이유**: FESA의 핵심 도메인 모델과 해석 알고리즘이 특정 외부 라이브러리 API에 강하게 결합되면 테스트와 교체가 어려워진다. `SparseMatrix`, `Vector`, `LinearSolver`, `ParallelFor`, `ResultsWriter` 같은 경계를 통해 외부 의존성을 제한한다. - -**트레이드오프**: wrapper 계층 구현 비용이 추가된다. 성능이 민감한 부분에서는 adapter가 불필요한 복사를 만들지 않도록 API를 신중히 설계해야 한다. - ---- - -### ADR-007: DofManager가 자유도와 방정식 번호를 전담 -**결정**: node와 element 내부에 equation id를 분산 저장하지 않고, `DofManager`가 자유도 정의, constrained/free dof mapping, equation numbering, sparse pattern 생성을 전담한다. - -**이유**: 대규모 모델, 경계조건, RBE2/RBE3, 비선형 재조립, thermal-stress coupling에서는 자유도 관리가 solver 품질과 성능에 직접 영향을 준다. 자유도 관리를 별도 객체로 분리하면 경계조건 적용과 행렬 조립이 명확해진다. - -**트레이드오프**: element 계산 시 node id에서 equation id로 변환하는 조회 비용이 생긴다. 이 비용은 assembly precompute 또는 element connectivity cache로 줄인다. - ---- - -### ADR-008: Numerical Convention Baseline -**결정**: Phase 1 shell node는 6자유도(`UX`, `UY`, `UZ`, `RX`, `RY`, `RZ`)를 사용하고, 기본 precision은 `double`, id/index/equation numbering은 int64 기반으로 설계한다. 단위계는 Abaqus처럼 강제하지 않고, 결과 부호 규약은 Abaqus를 따른다. - -**이유**: MITC4 Shell, Abaqus input/result 호환성, 대규모 sparse system, 향후 RBE/비선형/동역학 확장을 모두 고려하면 6자유도와 64-bit indexing이 가장 안정적인 공통 기반이다. 단위계를 강제하지 않으면 Abaqus reference와 같은 self-consistent unit workflow를 유지할 수 있다. - -**트레이드오프**: int64 index는 작은 모델에서 메모리 사용량이 증가할 수 있다. 단위계를 강제하지 않으므로 reference case와 사용자 입력 문서에 unit note를 성실히 남겨야 한다. - ---- - -### ADR-009: Essential Boundary Condition and Reaction Recovery -**결정**: Essential boundary condition은 constrained DOF 제거 방식으로 적용한다. Reaction force/moment는 reduced system 결과만 사용하지 않고 full vector 기준 `R_full = K_full * U_full - F_full`로 계산한다. - -**이유**: constrained/free DOF mapping이 명확하고 대규모 sparse solve에 적합하며, reaction force는 reference 검증과 하중 평형 검증에서 핵심 출력이다. - -**트레이드오프**: full stiffness/load/displacement 정보를 reaction recovery 시점까지 보존하거나 재구성해야 하므로 메모리와 데이터 흐름 관리가 필요하다. - ---- - -### ADR-010: Drilling DOF Stabilization -**결정**: Phase 1 MITC4는 drilling 자유도를 유지하고, 작은 인공 drilling 강성을 적용한다. 기본값은 `docs/MITC4_FORMULATION.md`에서 구현 전 확정한다. - -**이유**: 6자유도 shell node 구조와 Abaqus-style output convention을 유지하면서 singular matrix 위험을 줄이기 위해 drilling stabilization이 필요하다. - -**트레이드오프**: 인공 강성이 물리 응답에 작은 영향을 줄 수 있다. 따라서 값은 parameterized 되어야 하고, reference benchmark에서 민감도를 확인해야 한다. - ---- - -### ADR-011: S4 Mapping and S4R Deferral -**결정**: Phase 1 Abaqus `*Element, TYPE=S4`는 FESA `MITC4`로 매핑한다. `S4R`은 Phase 1에서 지원하지 않고 추후 별도 ADR과 formulation 업데이트 후 지원한다. - -**이유**: Abaqus S4는 MITC4와 비교 가능한 fully integrated 4-node shell reference로 사용할 수 있다. 반면 S4R은 reduced integration과 hourglass control 결정을 요구하므로 Phase 1의 명확한 baseline formulation 목표와 분리한다. - -**트레이드오프**: 사용자가 가진 S4R reference model은 Phase 1에서 바로 사용할 수 없다. 대신 S4 baseline과 MITC4 benchmark 통과를 먼저 안정화한다. - ---- - -### ADR-012: Singular Diagnostics Required, Mesh Quality Deferred -**결정**: Phase 1은 singular system 진단을 필수로 제공한다. Aspect ratio, warpage, skew 등 mesh quality 진단은 Phase 1 범위에서 제외한다. - -**이유**: 초기 solver 사용성과 디버깅에는 singular system 원인 추적이 더 직접적으로 중요하다. Mesh quality 진단은 기준과 threshold를 잘못 잡으면 formulation 검증보다 많은 논쟁을 만들 수 있으므로 baseline solver 이후로 미룬다. - -**트레이드오프**: 품질이 낮은 mesh가 조용히 나쁜 결과를 만들 수 있다. Phase 1에서는 reference benchmark와 singular diagnostics로 우선 통제한다. - ---- - -### ADR-013: Technical Dossier Documents as Implementation Contracts -**결정**: 구현 전 다음 문서를 계약 문서로 유지한다: `NUMERICAL_CONVENTIONS.md`, `ABAQUS_INPUT_SUBSET.md`, `VERIFICATION_PLAN.md`, `RESULTS_SCHEMA.md`, `MITC4_FORMULATION.md`. - -**이유**: FEM solver는 DOF, 좌표계, 부호, parser subset, result schema, benchmark 기준이 조금만 흔들려도 구현이 불안정해진다. 구현자가 따를 수 있는 technical dossier를 먼저 고정한다. - -**트레이드오프**: 문서 유지 비용이 증가한다. 하지만 문서와 구현이 어긋날 때는 ADR 또는 해당 dossier를 먼저 갱신하여 설계 drift를 관리한다. - ---- - -### ADR-014: Documentation Index and Implementation Readiness Gate -**결정**: `docs/README.md`를 프로젝트 문서 index와 문서 우선순위의 entry point로 둔다. Phase 1 implementation plan을 작성하기 전 `docs/README.md`의 Implementation Readiness Checklist 미결 항목을 명시한다. - -**이유**: FESA는 수치 규약, Abaqus subset, MITC4 formulation, HDF5 schema, reference verification이 강하게 결합되어 있다. 구현 전에 어떤 문서가 어느 결정을 소유하는지 분명히 하지 않으면 작은 구현 선택이 전체 solver convention을 흔들 수 있다. - -**트레이드오프**: 계획 단계에서 문서 확인 비용이 늘어난다. 대신 이후 Codex 세션과 multi-agent 작업이 같은 규칙을 공유하고, 미결 결정을 숨기지 않는다. - ---- - -### ADR-015: Reference-Artifact-Only Validation Baseline -**결정**: Phase 1 검증은 저장된 `references/` artifact와 비교한다. Abaqus 실행은 로컬 개발, CI, 기본 validation의 요구사항으로 두지 않는다. - -**이유**: 사용자는 Abaqus input과 해석 결과를 `references/` 폴더로 제공하며, 현재 개발 환경에서 Abaqus 실행은 사용할 수 없다. 저장 artifact 기반 검증이 재현성과 자동화에 더 적합하다. - -**트레이드오프**: reference artifact 생성과 갱신은 수동 절차에 의존한다. 따라서 reference manifest, Abaqus version, unit note, tolerance, 비교 대상 output path를 명확히 기록해야 한다. - ---- - -### ADR-016: References Folder and CSV Displacement Artifact Contract -**결정**: 저장 reference의 공식 루트는 `references/`이다. 초기 자동 변위 검증은 Abaqus 해석 입력 `*.inp`와 Abaqus에서 export한 `*_displacements.csv` 파일 쌍을 사용한다. CSV column은 `Node Label`, `U-U1`, `U-U2`, `U-U3`, `UR-UR1`, `UR-UR2`, `UR-UR3`를 기본 형식으로 한다. - -**이유**: 사용자가 첫 reference case로 `references/quad_01.inp`와 `references/quad_01_displacements.csv`를 제공했다. CSV는 사람이 검토하기 쉽고, 초기 parser/solver 검증 harness에서 HDF5 writer가 완성되기 전에도 `U` field 비교를 자동화하기 좋다. - -**트레이드오프**: CSV는 HDF5보다 metadata 표현력이 약하다. 따라서 Abaqus version, unit note, unsupported keyword note, tolerance는 `references/README.md`, case manifest, 또는 추후 metadata 파일에 보강해야 한다. `quad_01.inp`처럼 `S4R`, `Part/Assembly/Instance`, `NLGEOM=YES`를 포함하는 reference input은 저장 reference로 보존하되 Phase 1 parser 지원 범위를 자동으로 확장하지 않는다. - ---- - -### ADR-017: CMake and CTest Phase 1 Build Harness -**결정**: Phase 1의 기본 빌드 및 테스트 harness는 CMake와 CTest를 사용한다. `scripts/validate_workspace.py`는 `CMakeLists.txt`를 발견하면 configure, build, CTest 순서로 검증을 수행한다. - -**이유**: FESA의 핵심 구현 언어는 C++17 이상이고, Phase 1은 외부 solver library 없이도 core/parser/DOF/result/comparator/element tests를 반복 실행해야 한다. CMake/CTest는 Visual Studio/MSVC와 다른 C++ toolchain 모두에서 표준적인 최소 공통 경로를 제공한다. - -**트레이드오프**: oneAPI MKL, TBB, HDF5 adapter가 추가될 때 CMake dependency discovery가 더 복잡해진다. 대신 Phase 1에서는 외부 API를 core에 노출하지 않고 deterministic test adapter로 검증을 시작할 수 있다. - ---- - -### ADR-018: Phase 1 MITC4 Formulation Reset -**결정**: Phase 1 MITC4 baseline은 `docs/MITC4_FORMULATION.md`의 논문 기반 formulation contract를 따른다. 핵심 결정은 degenerated-continuum kinematics, convected covariant strain components, FESA/Abaqus S4 node-order에 맞춘 MITC transverse shear tying interpolation, `2 x 2 x 2` Gauss integration, six-DOF local/global rotation transformation, `drilling_stiffness_scale = 1.0e-3` applied to the minimum positive physical local stiffness diagonal, 그리고 mandatory result output을 `U`와 `RF`로 제한하는 것이다. - -**이유**: 이전 Phase 1 baseline은 첫 구현을 빠르게 안정화하기 위한 단순화였지만, 사용자가 추가한 MITC4 논문들과 S4 reference (`quad_02`)를 기준으로 보면 formulation detail이 부족하다. MITC4 element implementation은 convected tensor shear tying, director/local-axis policy, drilling stabilization, and integration rules가 명확해야 reference 검증 전 수치 drift를 줄일 수 있다. - -**트레이드오프**: 기존 P1-01 through P1-14 구현은 새 formulation contract를 기준으로 재평가 또는 재작성되어야 한다. `quad_02.inp`는 `TYPE=S4`이지만 `Part/Assembly/Instance` 구조를 포함하므로, element formulation 재구현과 parser compatibility 확장은 별도 sprint로 분리해야 한다. `S4R`, reduced integration, and hourglass control remain out of scope. diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md deleted file mode 100644 index 83000b9..0000000 --- a/docs/ARCHITECTURE.md +++ /dev/null @@ -1,290 +0,0 @@ -# 아키텍처 - -## 목표 -FESA는 MITC4 Shell 요소 기반 구조해석에서 시작해 비선형 정적해석, 비선형 동적해석, 열전달 및 thermal-stress coupling, 1D/3D 요소까지 확장하는 유한요소 솔버이다. - -초기 구현은 정확도와 테스트 가능성을 우선한다. 단, 대규모 모델을 목표로 하므로 자유도 관리, 희소 행렬 조립, 선형 솔버, 병렬 실행 계층은 초기부터 성능 확장이 가능하도록 분리한다. - -문서 우선순위와 구현 전 준비 기준은 `docs/README.md`를 따른다. - -## 설계 원칙 -- Domain 객체는 입력 모델의 의미를 보존하고 가능한 한 불변에 가깝게 유지한다. -- 해석 중 변하는 물리량과 반복 상태는 AnalysisState에 명시적으로 분리한다. -- 요소, 재료, 하중, 경계조건, 해석 알고리즘은 런타임 다형성 기반으로 확장한다. -- MITC4 구현은 Phase 1에서 정확도와 테스트 가능성을 우선하며, assembly와 solver 계층은 대규모 모델 최적화가 가능하도록 경계를 둔다. -- 결과는 step/frame/field/history 개념으로 저장하여 정적, 비선형, 동적, 열전달 해석을 같은 결과 모델로 다룬다. -- 외부 라이브러리(MKL, TBB, HDF5)는 adapter 계층 뒤에 둔다. -- Abaqus input 호환성은 파서와 factory/registry 계층에서 관리한다. Phase 1의 입력 범위에는 `*Node`, `*Element`, `*Nset`, `*Elset`, `*Material`, `*Elastic`, `*Shell Section`, `*Boundary`, `*Cload`, `*Step`을 포함한다. -- 수치 규약은 `docs/NUMERICAL_CONVENTIONS.md`를 따른다. Phase 1 shell node는 6자유도이고, 단위계는 강제하지 않으며, 결과 부호는 Abaqus 규약을 따른다. -- 경계조건은 constrained DOF 제거 방식으로 적용하고, reaction은 full vector 기준 `K_full * U_full - F_full`로 계산한다. -- 기본 실수 precision은 `double`이고, 대규모 모델을 위해 id/index/equation numbering은 int64 기반으로 설계한다. -- Mesh quality 진단은 Phase 1 범위에서 제외한다. 대신 singular system 진단은 필수로 제공한다. - -## 디렉토리 구조 -``` -src/ -├── Analysis/ # Static, nonlinear static, dynamic, heat transfer analysis -├── Assembly/ # 전역 행렬/벡터 조립, sparse pattern 생성 -├── Boundary/ # Fix, RBE2, RBE3 등 경계조건 -├── Core/ # Domain, AnalysisModel, AnalysisState, DofManager -├── Element/ # Node, Element, MITC4 등 요소 구현 -├── IO/ # Abaqus input parser, HDF5 results writer -├── Load/ # NodalLoad, PressureLoad, BodyForce 등 하중 -├── Math/ # Vector, Matrix, SparseMatrix, MKL adapter -├── Material/ # LinearElastic 등 재료 모델 -├── Property/ # ShellProperty, 1D/2D/3D property -├── Results/ # Step, Frame, FieldOutput, HistoryOutput -└── Util/ # 공통 유틸리티, 로깅, 검증 보조 함수 -``` - -## 핵심 클래스 구조 -``` -Domain -├── Node -├── Element -├── Material -├── Property -├── NodeSet -├── ElementSet -├── BoundaryCondition -├── Load -└── StepDefinition - -AnalysisModel -├── active elements -├── active loads -├── active boundary conditions -├── active properties/materials -└── equation system view - -AnalysisState -├── displacement U -├── velocity V -├── acceleration A -├── temperature T -├── external force Fext -├── internal force Fint -├── residual R -├── current time / increment / iteration -└── element state / integration point state - -DofManager -├── node dof definitions -├── constrained/free dof mapping -├── equation numbering -├── sparse matrix pattern ownership -└── full/reduced vector reconstruction - -Analysis -├── LinearStaticAnalysis -├── NonlinearStaticAnalysis -├── DynamicAnalysis -├── FrequencyAnalysis -└── HeatTransferAnalysis - -Element -├── 1DElement -│ ├── Truss -│ └── Beam -├── 2DElement -│ ├── MITC3 -│ └── MITC4 -└── 3DElement - ├── Hexahedral - ├── Tetrahedral - ├── Wedge - └── Pyramid - -BoundaryCondition -├── Fix -├── RBE2 -└── RBE3 - -Load -├── NodalLoad -├── PressureLoad -└── BodyForce - -Results -├── ResultStep -├── ResultFrame -├── FieldOutput -└── HistoryOutput - -InputParser -ResultsWriter -Assembler -LinearSolver -Vector -Matrix -SparseMatrix -``` - -## 디자인 패턴 - -### Strategy Pattern -해석 알고리즘과 수치 알고리즘을 교체 가능하게 구성한다. - -적용 대상: -- `Analysis`: `LinearStaticAnalysis`, `NonlinearStaticAnalysis`, `DynamicAnalysis`, `HeatTransferAnalysis` -- `LinearSolver`: `MKLPardisoSolver`, 향후 iterative solver -- `TimeIntegrator`: `HHTIntegrator`, 향후 Newmark 등 -- `ConvergenceCriteria`: residual norm, displacement norm, energy norm - -### Template Method Pattern -해석 실행의 큰 흐름은 `Analysis::run()`에서 고정하고, 세부 단계는 해석 종류별로 재정의한다. - -기본 흐름: -``` -initialize -buildAnalysisModel -buildDofMap -buildSparsePattern -assemble -applyBoundaryConditions -solve -updateState -writeResults -``` - -비선형 정적해석은 위 흐름을 Newton-Raphson 반복 루프 안에서 사용하고, 동적해석은 time step/frame 루프 안에서 사용한다. - -### Factory + Registry Pattern -Abaqus input keyword와 내부 객체 생성을 분리한다. - -예: -- `*Element, type=S4` -> `MITC4ElementFactory` -- `*Material`, `*Elastic` -> `LinearElasticMaterialFactory` -- `*Boundary` -> `FixBoundaryFactory` -- `*Cload` -> `NodalLoadFactory` -- `*Nset`, `*Elset` -> set registry - -요소, 재료, 하중, 경계조건 타입 추가 시 parser 본체의 변경을 최소화한다. - -### Adapter Pattern -MKL, TBB, HDF5 API는 solver core에 직접 노출하지 않는다. - -적용 대상: -- `SparseMatrix`, `Vector`, `Matrix` -- `LinearSolver` -- `ParallelFor` -- `ResultsWriter` - -외부 라이브러리 교체 또는 테스트 double 사용이 가능하도록 adapter 계층에서 의존성을 제한한다. - -### Runtime Polymorphism -요소, 재료, 하중, 경계조건은 base interface를 통해 다룬다. Phase 1에서는 명확성과 테스트 가능성을 우선하고, 대규모 모델 성능 최적화가 필요할 경우 assembly 내부에서 타입별 batch 처리 또는 kernel 분리를 추가한다. - -## 상태 관리 - -### Domain -`Domain`은 입력 파일에서 만들어진 전체 모델 정의를 소유한다. 파싱 이후에는 가능한 한 불변으로 취급한다. - -포함 대상: -- nodes, elements -- materials, properties -- node sets, element sets -- loads, boundary conditions -- analysis step definitions - -### AnalysisModel -`AnalysisModel`은 현재 step에서 활성화되는 해석 객체들의 실행 view이다. `Domain`을 복사하지 않고 참조 또는 id 기반 view로 구성한다. - -포함 대상: -- active elements -- active loads -- active boundary conditions -- active property/material references -- current equation system view - -### DofManager -`DofManager`는 자유도와 방정식 번호를 전담한다. Node 또는 Element 내부에 equation id를 분산 저장하지 않는다. - -책임: -- node별 활성 자유도 정의 -- constrained/free dof mapping -- equation numbering -- sparse matrix pattern 생성에 필요한 connectivity 제공 -- 경계조건 적용 전후의 dof view 관리 - -### AnalysisState -`AnalysisState`는 해석 중 변하는 물리량과 반복 상태를 소유한다. - -포함 대상: -- displacement, velocity, acceleration -- temperature -- external force, internal force, residual -- current time, increment, Newton iteration -- element state, integration point state - -Phase 1에서는 displacement 중심으로 최소 구현하되, 기하비선형과 thermal-stress coupling을 위해 element/internal state 확장 지점을 유지한다. - -### Results State -결과는 `ResultStep` -> `ResultFrame` -> `FieldOutput`/`HistoryOutput` 구조로 관리한다. - -- `ResultStep`: 해석 step 단위 결과 -- `ResultFrame`: 정적해석의 load increment 또는 동적해석의 time frame -- `FieldOutput`: node/element field 결과 -- `HistoryOutput`: 특정 node, element, set, reaction, energy 등의 이력 결과 - -## 데이터 흐름 -``` -Abaqus input file --> InputParser --> Domain 생성 --> StepDefinition 루프 --> AnalysisModel 생성 --> DofManager로 자유도/방정식 번호 생성 --> sparse pattern 생성 --> Analysis 실행 --> Assembler로 전역 행렬/벡터 조립 --> BoundaryCondition 적용 --> LinearSolver 또는 nonlinear/time integration loop --> AnalysisState 갱신 --> ResultsWriter로 step/frame/history 저장 --> 다음 step 진행 -``` - -## Phase 1 실행 경계 -Phase 1 solver path는 다음 경계를 구현 단위로 삼는다. - -1. `InputParser`는 `docs/ABAQUS_INPUT_SUBSET.md`의 subset만 받아 `Domain`을 만든다. -2. `DomainValidator` 또는 동등한 검증 계층은 missing reference, unsupported feature, singular-prone model 상태를 조기에 진단한다. -3. `AnalysisModelBuilder`는 현재 step의 active element/load/boundary/property/material view를 구성한다. -4. `DofManager`는 6 DOF node model, constrained/free partition, equation numbering, sparse pattern input, full/reduced vector reconstruction을 소유한다. -5. `Assembler`는 full reaction recovery에 필요한 full-space stiffness/load 정보를 보존하거나 재구성 가능한 형태로 유지한다. -6. `LinearStaticAnalysis`는 reduced free-DOF system을 풀고 `AnalysisState`에 full `U`, `Fext`, `Fint`, `R`을 갱신한다. -7. `ResultsWriter`는 `docs/RESULTS_SCHEMA.md`의 최소 Phase 1 outputs를 쓴다. -8. `ReferenceComparator` 또는 테스트 harness는 `docs/VERIFICATION_PLAN.md`의 reference artifact와 비교한다. 초기 reference 입력은 `references/*.inp`와 Abaqus-exported `references/*_displacements.csv`이다. - -## Phase 1 구현 범위 -- MITC4 Shell 요소 -- 선형 탄성 재료 -- 절점하중 -- 고정 경계조건 -- Abaqus input subset: `*Node`, `*Element`, `*Nset`, `*Elset`, `*Material`, `*Elastic`, `*Shell Section`, `*Boundary`, `*Cload`, `*Step` -- `S4`를 `MITC4`로 매핑하고 `S4R`은 추후 지원 -- 6자유도 shell node와 drilling 자유도 인공 강성 -- constrained DOF 제거 방식 -- full vector 기반 reaction recovery -- 선형 정적 해석 -- step/frame 기반 결과 저장의 최소 구조 -- double precision과 int64 indexing -- singular system 진단 -- `references/`의 Abaqus `.inp`와 `*_displacements.csv` 기반 reference 모델 결과 비교 테스트 - -## 성능 확장 방향 -- 행렬 조립은 element 단위 병렬화를 고려해 설계한다. -- 전역 행렬은 대규모 모델을 위해 sparse matrix를 기본으로 한다. -- MKL 기반 direct solver를 우선 지원하되, solver interface는 iterative solver를 추가할 수 있게 둔다. -- 대규모 sparse solve를 위해 MKL `pardiso_64`를 사용할 수 있도록 64-bit sparse index 경계를 유지한다. -- TBB 병렬화는 요소 stiffness 계산, element force 계산, assembly precompute 등 독립 작업부터 적용한다. -- 정확도 검증이 끝나기 전에는 MITC4 element formulation을 과도하게 최적화하지 않는다. - -## 상세 설계 문서 -- `docs/README.md`: 문서 index, 우선순위, Phase 1 hard invariants, implementation readiness checklist -- `docs/NUMERICAL_CONVENTIONS.md`: DOF, 좌표계, 단위, 부호, precision, reaction recovery, singular diagnostics -- `docs/ABAQUS_INPUT_SUBSET.md`: Phase 1 Abaqus input keyword subset과 unsupported feature -- `docs/VERIFICATION_PLAN.md`: `references/` 폴더 구조, benchmark matrix, CSV reference result 형식, tolerance 정책 -- `docs/RESULTS_SCHEMA.md`: HDF5 step/frame/field/history schema -- `docs/MITC4_FORMULATION.md`: MITC4 baseline formulation 계약과 open decisions diff --git a/docs/HARNESS_ENGINEERING.md b/docs/HARNESS_ENGINEERING.md deleted file mode 100644 index 65f4b63..0000000 --- a/docs/HARNESS_ENGINEERING.md +++ /dev/null @@ -1,169 +0,0 @@ -# Harness Engineering - -## Purpose -This document defines how FESA uses long-running agent harnesses for planning, implementation, and evaluation. - -The goal is not to maximize agent count. The goal is to keep long solver work coherent, testable, and reference-verified across context resets and independent sessions. - -## Default Harness Shape -Use the smallest harness that can safely handle the task. - -For meaningful solver implementation or phase execution, use: - -```text -Planner -> Generator -> Evaluator -``` - -Roles: -- `Planner`: turns project docs and `PLAN.md` tasks into a testable sprint contract or phase step. -- `Generator`: implements exactly one accepted contract using TDD. -- `Evaluator`: independently checks the result against the contract, docs, tests, reference artifacts, and validation commands. - -Do not use multi-agent ceremony for tiny documentation edits or obvious mechanical changes. Do use the full harness when a task touches solver behavior, numerical conventions, reference comparison, parser compatibility, result schema, or phase execution. - -## Sprint Contract -Every implementation sprint must have a contract before code changes begin. - -Recommended location: -- `phases/{phase}/stepN.md` for phase execution. -- `phases/{phase}/contracts/stepN-contract.md` only when a separate negotiation artifact is useful. - -Required sections: - -````markdown -# Sprint Contract: {name} - -## Objective -{one concise outcome} - -## Required Reading -- /AGENTS.md -- /PROGRESS.md -- /PLAN.md -- /docs/README.md -- /docs/HARNESS_ENGINEERING.md -- {topic docs} - -## Scope -- {what may be changed} - -## Allowed Files -- {paths or modules} - -## Explicit Non-Goals -- {what must not be done} - -## Tests To Write First -- {test files or test cases} - -## Reference Artifacts -- {references/*.inp or references/*_displacements.csv, or "none"} - -## Acceptance Commands -```bash -python scripts/validate_workspace.py -``` - -## Evaluator Checklist -- {contract-specific checks} - -## Handoff Requirements -- Update PROGRESS.md for completed work. -- Update PLAN.md for future work or changed blockers. -```` - -Contract quality rules: -- The contract must be testable. -- The contract must identify unsupported Abaqus features rather than expanding support implicitly. -- The contract must state whether reference data is used. -- The contract must name file ownership boundaries to reduce conflicts. -- The contract must not prescribe formulas that are not present in `docs/MITC4_FORMULATION.md` or a cited source. - -## Generator Rules -The Generator implements one contract at a time. - -Required behavior: -- Read the contract and required docs before editing. -- Write or update tests before implementation. -- Keep changes inside allowed files unless the contract is updated first. -- Preserve architecture boundaries from `docs/ARCHITECTURE.md` and `docs/ADR.md`. -- Preserve numerical conventions from `docs/NUMERICAL_CONVENTIONS.md`. -- Run acceptance commands. -- Update `PROGRESS.md` and `PLAN.md` only for factual state changes. - -Generator failure modes to avoid: -- Broad refactors outside the contract. -- Implementing parser support because a stored reference `.inp` contains unsupported Abaqus features. -- Comparing only reduced vectors when full-vector reaction recovery is required. -- Treating a passing compile as sufficient without tests or reference checks. - -## Evaluator Rules -The Evaluator is independent from the Generator. - -Evaluation order: -1. Read the sprint contract. -2. Read `AGENTS.md`, `PROGRESS.md`, `PLAN.md`, and the topic docs. -3. Inspect the changed files. -4. Run or review the acceptance commands. -5. Check tests, reference artifacts, and documented conventions. -6. Return pass/fail findings with concrete file references. - -The Evaluator must fail the sprint if any of these are true: -- Required tests were not written first or are missing. -- `python scripts/validate_workspace.py` fails without explanation. -- A CRITICAL rule in `AGENTS.md` is violated. -- A change drifts from `docs/ARCHITECTURE.md`, `docs/ADR.md`, or `docs/NUMERICAL_CONVENTIONS.md`. -- `references/*_displacements.csv` comparison is required but not implemented or not checked. -- `RF` is computed from reduced quantities when full-vector recovery is required. -- Unsupported Abaqus features are silently accepted. -- Completed work is not recorded in `PROGRESS.md`, or future tasks are not recorded in `PLAN.md`. - -If the sprint fails, the Evaluator should produce a concise feedback artifact: - -```markdown -# Evaluation Feedback: {contract} - -## Verdict -fail - -## Findings -- {severity}: {file} - {risk} - -## Required Fixes -- {minimal fix} - -## Verification To Rerun -- {commands} -``` - -## FESA Evaluation Rubric -Use this rubric for implementation review. - -| Criterion | Pass Condition | -|---|---| -| Contract compliance | Changes stay within scope and allowed files | -| Architecture | Domain, AnalysisModel, AnalysisState, DofManager, adapters, and factories follow documented ownership | -| Numerical conventions | DOF order, units, signs, double precision, int64 ids, constrained/free mapping, and full-vector reactions are preserved | -| Reference verification | Stored `references/` artifacts are used when required; CSV column mapping is correct | -| Tests | Tests exist before implementation and cover failure modes, not only happy paths | -| Diagnostics | Unsupported input and singular systems produce actionable diagnostics | -| Results schema | Outputs follow step/frame/field/history and HDF5 schema rules | -| Handoff | `PLAN.md` and `PROGRESS.md` reflect the new state | - -## Harness Complexity Policy -Add harness complexity only when it catches real risk. - -Use a single agent for: -- small wording changes. -- mechanical docs updates. -- metadata-only corrections. - -Use Planner -> Generator -> Evaluator for: -- C++ solver implementation. -- parser behavior changes. -- result schema or HDF5 writer changes. -- reference comparator changes. -- MITC4 formulation-dependent work. -- phase generation or execution. - -Review the harness periodically. If an agent role no longer adds value, simplify it. diff --git a/docs/MITC4_FORMULATION.md b/docs/MITC4_FORMULATION.md deleted file mode 100644 index 1674477..0000000 --- a/docs/MITC4_FORMULATION.md +++ /dev/null @@ -1,443 +0,0 @@ -# MITC4 Formulation - -## Purpose -This document is the formulation contract for FESA's Phase 1 four-node MITC4 shell element. - -It supersedes the earlier simplified Phase 1 baseline that used an averaged-edge shell basis, analytic thickness integration, and `drilling_stiffness_scale = 1.0e-6`. The next Phase 1 implementation pass must treat this document as the MITC4 gate before coding or evaluating shell stiffness. - -The goal is not to reproduce Abaqus internals. The goal is to implement a documented, testable Dvorkin-Bathe-style MITC4 element that can be compared against stored Abaqus `S4` reference artifacts without running Abaqus locally. - -## Local Source Basis -The formulation below is based on the local papers in `docs/Paper/`: - -- `docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/` - - Dvorkin and Bathe's original four-node non-flat shell element. - - Defines the degenerated-continuum geometry, displacement interpolation, convected transverse shear interpolation, constitutive transformation, and benchmark expectations. -- `docs/Paper/FourNodeQuadrilateralShellElementMITC4/` - - Restates the MITC4 geometry and displacement interpolation, gives explicit midside shear tying relations, and reports patch-test and Scordelis-Lo verification behavior. -- `docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/` - - Provides the Korean thesis derivation for covariant strain, plane-stress material matrix with shear correction, 6-DOF shell transformation, drilling stabilization, and nonlinear extension notes. -- `docs/Paper/2007쉘구조물의유한요소해석에대하여/` - - Provides shell-model background, locking behavior, asymptotic behavior, and benchmark/test interpretation. -- `docs/Paper/mitc공부/` - - Treated as supporting study notes only. Use the papers above for binding formulas. - -## Phase 1 Scope -Phase 1 implements: - -- Abaqus `TYPE=S4` mapped to FESA `MITC4`. -- Four-node quadrilateral shell in Abaqus S4 node order. -- Linear static, small-strain, linear isotropic elastic analysis. -- Homogeneous shell section with one thickness value per element for the initial implementation. -- Six global DOFs per node: `UX`, `UY`, `UZ`, `RX`, `RY`, `RZ`. -- MITC transverse shear interpolation in a convected coordinate system. -- Constrained DOF elimination, full-vector reaction recovery, and minimum output fields `U` and `RF`. - -Phase 1 does not implement: - -- Abaqus `S4R`, reduced integration, or hourglass control. -- Geometric nonlinearity, material nonlinearity, dynamics, pressure loads, thermal-stress coupling, composite sections, or mesh quality diagnostics. -- Abaqus `*Orientation`, user-defined nodal normals, or shell offset behavior unless added by a later ADR. - -## Natural Coordinates and Node Order -FESA uses the standard bilinear quadrilateral natural coordinates: - -```text -node 1: (xi, eta) = (-1, -1) -node 2: (xi, eta) = (+1, -1) -node 3: (xi, eta) = (+1, +1) -node 4: (xi, eta) = (-1, +1) -zeta in [-1, +1] through the thickness -``` - -Shape functions: - -```text -N1 = 0.25 * (1 - xi) * (1 - eta) -N2 = 0.25 * (1 + xi) * (1 - eta) -N3 = 0.25 * (1 + xi) * (1 + eta) -N4 = 0.25 * (1 - xi) * (1 + eta) -``` - -Midside MITC tying points in the FESA convention: - -```text -A = ( 0, -1) edge 1-2 -B = (-1, 0) edge 1-4 -C = ( 0, +1) edge 4-3 -D = (+1, 0) edge 2-3 -``` - -Dvorkin-Bathe papers may use a natural-coordinate orientation where the signs on the `A/C` interpolation are reversed relative to the FESA convention above. FESA implementation must preserve the edge definitions above and use the FESA interpolation formulas in the MITC shear section. - -## Degenerated-Continuum Geometry -The shell is represented as a 3D continuum degenerated through the thickness: - -```text -X(xi, eta, zeta) = - sum_k N_k(xi, eta) X_k - + zeta / 2 * sum_k t_k N_k(xi, eta) Vn_k -``` - -where: - -- `X_k` is the midsurface coordinate of node `k`. -- `t_k` is the nodal thickness. Phase 1 uses `t_k = t` for all four nodes. -- `Vn_k` is the nodal director. The papers allow the director to be non-normal to the midsurface; Phase 1 derives it from geometry because Abaqus nodal normals are not in the parser subset. - -Phase 1 director policy: - -- If no parsed nodal directors exist, use the element-center midsurface normal for all four nodal directors: - -```text -G1_c = dX_mid / dxi at (0, 0) -G2_c = dX_mid / deta at (0, 0) -Vn_k = normalize(G1_c x G2_c), k = 1..4 -``` - -- If `G1_c x G2_c` is near zero, reject the element with an invalid/singular element diagnostic. -- This flat-or-mildly-warped policy is sufficient for the Phase 1 S4 baseline. A true non-flat nodal-director policy requires a later ADR and benchmark update. - -The nodal local director axes are: - -```text -V1_k = normalize(EY x Vn_k) -V2_k = Vn_k x V1_k -``` - -If `EY x Vn_k` is near zero, use a deterministic fallback axis before normalization: - -```text -V1_k = normalize(EZ x Vn_k) -if still near zero, V1_k = normalize(EX x Vn_k) -V2_k = Vn_k x V1_k -``` - -The implementation must keep `V1_k`, `V2_k`, and `Vn_k` orthonormal and right-handed. - -## Displacement and Rotation Interpolation -The local five-DOF MITC4 displacement field is: - -```text -u(xi, eta, zeta) = - sum_k N_k u_k - + zeta / 2 * sum_k t_k N_k q_k - -q_k = -V2_k * alpha_k + V1_k * beta_k -``` - -where: - -- `u_k` is the nodal translation vector. -- `alpha_k` is the rotation about `V1_k`. -- `beta_k` is the rotation about `V2_k`. -- Drilling rotation `gamma_k` about `Vn_k` does not enter the continuum strain field. - -FESA stores six global DOFs per node. Global nodal rotations are mapped to local director rotations by: - -```text -theta_k = [RX_k, RY_k, RZ_k]^T - -[alpha_k, beta_k, gamma_k]^T = L_k theta_k - -L_k = - [ EX dot V1_k EY dot V1_k EZ dot V1_k - EX dot V2_k EY dot V2_k EZ dot V2_k - EX dot Vn_k EY dot Vn_k EZ dot Vn_k ] -``` - -The element-level local-to-global transformation is block diagonal: - -```text -u_local_e = T_e u_global_e -K_global_e = T_e^T K_local_e T_e -f_global_e = T_e^T f_local_e -``` - -Each node block maps `[UX, UY, UZ, RX, RY, RZ]` to `[u_x, u_y, u_z, alpha, beta, gamma]`. - -## Covariant Strain Definition -Use convected covariant components for the MITC4 strain construction: - -```text -g_i = dX / dr_i, where r_i = xi, eta, zeta - -eps_ij = - 0.5 * (du/dr_i dot g_j + du/dr_j dot g_i) -``` - -For the Phase 1 small-strain implementation: - -- Membrane and bending terms are derived directly from the degenerated-continuum displacement field. -- `eps_33 = 0` is enforced by the shell assumption. -- The transverse shear rows `eps_13` and `eps_23` are not taken directly at the Gauss point. They are replaced by the MITC assumed transverse shear interpolation below. - -Use the internal strain vector order: - -```text -[ eps_11, eps_22, eps_33, gamma_23, gamma_13, gamma_12 ]^T -``` - -where engineering shear strains are: - -```text -gamma_ij = 2 * eps_ij -``` - -If an implementation uses another row order internally, it must include an explicit permutation test against this documented order. - -## MITC Transverse Shear Interpolation -The key MITC4 assumption is that the transverse shear tensor components are interpolated from midside tying points, instead of being evaluated directly at every Gauss point from the displacement field. - -The original paper form is: - -```text -eps_13 = 0.5 * (1 + r2) * eps_13^A + 0.5 * (1 - r2) * eps_13^C -eps_23 = 0.5 * (1 + r1) * eps_23^D + 0.5 * (1 - r1) * eps_23^B -``` - -With FESA's Abaqus-style natural coordinate convention and tying-point labels, use: - -```text -eps_13_MITC(xi, eta) = - 0.5 * (1 - eta) * eps_13^A - + 0.5 * (1 + eta) * eps_13^C - -eps_23_MITC(xi, eta) = - 0.5 * (1 - xi) * eps_23^B - + 0.5 * (1 + xi) * eps_23^D -``` - -The tying values are the direct covariant shear strains evaluated at midside points: - -```text -eps_13^A = eps_13_DIRECT( 0, -1) -eps_13^C = eps_13_DIRECT( 0, +1) -eps_23^B = eps_23_DIRECT(-1, 0) -eps_23^D = eps_23_DIRECT(+1, 0) -``` - -Implementation rule: - -- Build the direct covariant strain-displacement rows from the geometry and displacement interpolation. -- Evaluate only the needed direct transverse shear rows at `A`, `B`, `C`, and `D`. -- Interpolate those rows to the integration point using the formulas above. -- Do not compute MITC shear from local Cartesian `gamma_xz`/`gamma_yz` first; the tying is on convected tensor components. - -For sign and regression checks, define: - -```text -q_k = -V2_k * alpha_k + V1_k * beta_k -``` - -The paper edge formulas can be used as a diagnostic check for the direct tying rows: - -```text -eps_13^A ~ 1/8 * [ - (u1 - u2) dot 0.5 * (t1 Vn1 + t2 Vn2) - + (X1 - X2) dot 0.5 * (t1 q1 + t2 q2) -] - -eps_13^C ~ 1/8 * [ - (u4 - u3) dot 0.5 * (t4 Vn4 + t3 Vn3) - + (X4 - X3) dot 0.5 * (t4 q4 + t3 q3) -] - -eps_23^B ~ 1/8 * [ - (u1 - u4) dot 0.5 * (t1 Vn1 + t4 Vn4) - + (X1 - X4) dot 0.5 * (t1 q1 + t4 q4) -] - -eps_23^D ~ 1/8 * [ - (u2 - u3) dot 0.5 * (t2 Vn2 + t3 Vn3) - + (X2 - X3) dot 0.5 * (t2 q2 + t3 q3) -] -``` - -These compact formulas are not a license to skip the general direct-row evaluation. They are a sign/orientation cross-check derived from the same midside strain definitions. - -## Local Cartesian Frame and Material Law -The constitutive law is defined in a local orthonormal Cartesian frame at each integration point with `sigma_33 = 0`. - -Build the integration-point local basis from the covariant basis: - -```text -e3_hat = normalize(g_3) -e1_hat = normalize(g_2 x e3_hat) -e2_hat = e3_hat x e1_hat -``` - -If `g_2 x e3_hat` is near zero, use a deterministic fallback that still creates a right-handed orthonormal basis and emits a diagnostic if no valid basis exists. - -For isotropic linear elasticity, use the local plane-stress shell material matrix with transverse shear correction `kappa = 5/6`: - -```text -D_hat = E / (1 - nu^2) * - [ 1 nu 0 0 0 0 - nu 1 0 0 0 0 - 0 0 0 0 0 0 - 0 0 0 kappa*(1-nu)/2 0 0 - 0 0 0 0 kappa*(1-nu)/2 0 - 0 0 0 0 0 (1-nu)/2 ] -``` - -This matrix uses the documented strain order: - -```text -[ eps_11, eps_22, eps_33, gamma_23, gamma_13, gamma_12 ]^T -``` - -For a general non-flat element, transform the local Cartesian material relation to the convected coordinate strain vector: - -```text -E_hat = T_xi_to_x E_convected -D_convected = T_xi_to_x^T D_hat T_xi_to_x -``` - -The transformation matrix must be constructed from direction cosines between the local orthonormal basis and contravariant/covariant convected bases. For Phase 1, a flat-element implementation may compute the B-matrix directly in the local Cartesian frame only if tests prove it is equivalent to the convected formulation for the accepted reference cases. - -## Element Stiffness and Numerical Integration -The baseline stiffness is: - -```text -K_e = integral_V B^T D B dV -``` - -Phase 1 baseline integration: - -- Use `2 x 2 x 2` Gauss integration in `(xi, eta, zeta)` for the first rebuilt MITC4 implementation. -- This follows the thesis derivation's `2 x 2 x 2` natural-coordinate integration and the Dvorkin-Bathe elastic recommendation of `2 x 2` in the midsurface with two through-thickness points. -- Do not introduce reduced integration or hourglass control. -- Analytic through-thickness integration may be introduced later only after equivalence tests preserve the MITC shear, drilling, and reference displacement behavior. - -At every integration point: - -1. Compute `g_i`, the metric/Jacobian, local Cartesian basis, and material transform. -2. Build direct membrane/bending strain rows. -3. Replace transverse shear rows with MITC interpolated rows. -4. Accumulate `B^T D B |J| w_xi w_eta w_zeta`. - -Detect near-zero Jacobian, invalid basis, missing property/material, or non-positive thickness as invalid/singular element diagnostics. Do not report these as mesh quality diagnostics in Phase 1. - -## Drilling DOF Stabilization -The continuum MITC4 formulation has five physical DOFs per node. FESA keeps six global DOFs, so the local drilling rotation `gamma` has no physical strain contribution and must be weakly stabilized. - -Baseline decision: - -```text -drilling_stiffness_scale = 1.0e-3 -k_ref = min_positive_diagonal(K_local_without_drilling) -k_drill = drilling_stiffness_scale * k_ref -``` - -Rules: - -- `K_local_without_drilling` is the element local stiffness after MITC integration and before artificial drilling stabilization. -- Use the minimum positive diagonal entry over the physical local DOFs. Ignore zero, negative, NaN, or non-finite entries. -- Add `k_drill` to each nodal local `gamma` diagonal before transforming to global coordinates. -- If no positive reference diagonal exists, emit a singular/invalid element diagnostic. -- The scale must be configurable and written to result metadata or analysis diagnostics. -- Reference comparisons must include a drilling sensitivity check if displacement results change materially with the scale. - -This replaces the earlier arbitrary `1.0e-6 * E * thickness` rule. The `1.0e-3` scale follows the local thesis derivation's six-DOF stabilization note and is still an artificial numerical parameter, not a physical stiffness. - -## Internal Force and Reaction Recovery -For Phase 1 linear static analysis: - -```text -f_int_e = K_e u_e -R_full = K_full U_full - F_full -``` - -Rules: - -- Reactions must be recovered from the full global vector, not only from the reduced free-DOF system. -- `RF` output follows the Abaqus-style component convention documented in `docs/NUMERICAL_CONVENTIONS.md`. -- Element stress/strain/resultant outputs are not mandatory until displacement and reaction reference behavior is stable. - -## Required Tests Before Reimplementation -The rebuilt MITC4 element must be protected by tests before implementation code is accepted. - -Element-level tests: - -- Shape functions sum to one and have correct derivatives at center, corners, and tying points. -- Center normal, local director axes, and integration local Cartesian axes are orthonormal and right-handed. -- The local-to-global rotation transform maps global rotations to `alpha`, `beta`, `gamma` as documented. -- Direct tying rows at `A`, `B`, `C`, `D` agree with finite-difference strain perturbations. -- MITC shear rows at Gauss points are interpolated from tying rows, not evaluated directly at the Gauss points. -- Stiffness is symmetric for linear elastic Phase 1. -- Rigid-body translations and rotations produce near-zero physical strain energy, with only documented drilling stabilization effects. -- The element has no spurious zero-energy modes beyond expected rigid-body behavior after drilling stabilization is accounted for. - -Patch and benchmark tests: - -- Constant membrane states. -- Pure bending. -- Pure shear. -- Pure twist. -- Thin cantilever or plate strip to expose shear locking. -- Scordelis-Lo roof after the basic flat tests pass. -- Pinched cylinder or twisted beam as later Phase 1/Phase 2 benchmark expansion. - -Reference regression tests: - -- Compare FESA `U` against stored Abaqus displacement CSV files using absolute and relative tolerances. -- Keep `RF` verified by full-vector equilibrium until matching Abaqus reaction CSV files are provided or explicitly deferred. - -## Current Reference Artifacts -Stored artifacts currently known: - -- `references/quad_01.inp` -- `references/quad_01_displacements.csv` -- `references/quad_02.inp` -- `references/quad_02_displacements.csv` -- `references/quad_02_reactionforces.csv` - -Compatibility notes: - -- `quad_01.inp` contains `S4R`, `Part/Assembly/Instance`, `*Density`, and `NLGEOM=YES`; it remains future compatibility provenance, not a Phase 1 parser acceptance case. -- `quad_02.inp` contains `TYPE=S4`, which is the correct element target for Phase 1, but it also uses `Part/Assembly/Instance`. The normalized `quad_02_phase1.inp` remains the executable Phase 1 input path. Do not silently expand parser support while implementing or verifying the element. -- `quad_02_reactionforces.csv` is now available for Abaqus RF/RM comparison. The current node-wise RF comparison does not pass; keep this as a formulation/solver verification gap until the mismatch is explained or fixed. - -## Implementation Checklist -The next MITC4 implementation pass should proceed in this order: - -1. Lock the shape-function, natural-coordinate, node-order, and tying-point tests. -2. Implement geometry/director/local-axis construction with diagnostics. -3. Implement the five-DOF degenerated-continuum displacement interpolation. -4. Implement global six-DOF to local `[alpha, beta, gamma]` transformation. -5. Implement direct covariant strain rows and finite-difference tests. -6. Replace transverse shear rows with MITC tying interpolation. -7. Implement material transform and `2 x 2 x 2` stiffness integration. -8. Add drilling stabilization in local coordinates. -9. Transform element stiffness and internal force to global coordinates. -10. Run element patch tests before assembly/reference regression work. - -## Future Extensions -Geometric nonlinearity: - -- Update current geometry and nodal directors in `AnalysisState`. -- Add finite-rotation updates for `V1`, `V2`, and `Vn`. -- Add tangent stiffness, geometric stiffness, and Newton-Raphson convergence checks. - -Thermal-stress coupling: - -- Add temperature field state and thermal strain contribution. -- Add material expansion data and thermal output fields. - -S4R: - -- Add only after a separate ADR and formulation update. -- Requires reduced integration, hourglass control, and separate reference artifacts. - -Stress/resultant output: - -- Define section point locations, component labels, local coordinate convention, and Abaqus CSV comparison format before making `S`, `E`, or `SF` mandatory. - -## Remaining Open Decisions -The MITC4 stiffness formulation is now defined for the Phase 1 rebuild. Remaining decisions are outside the first stiffness pass: - -- Exact stress/strain/resultant recovery locations and component labels. -- Whether to parse Abaqus `*Orientation` or nodal shell normals. -- Whether `quad_02.inp` should be normalized into the existing Phase 1 parser subset or used to justify a dedicated `Part/Assembly/Instance` parser sprint. -- Reference tolerances for `quad_02` after the input compatibility path is chosen. diff --git a/docs/MULTI_AGENT_RESEARCH_PLAN.md b/docs/MULTI_AGENT_RESEARCH_PLAN.md deleted file mode 100644 index ede8f16..0000000 --- a/docs/MULTI_AGENT_RESEARCH_PLAN.md +++ /dev/null @@ -1,143 +0,0 @@ -# Multi-Agent Research Plan - -## Purpose -This document is the durable planning memo for FESA's research-oriented multi-agent workflow. It records what the agents should investigate before solver implementation begins. - -No solver code should be implemented from this plan directly. Each agent should produce an English technical dossier that an implementer can later follow. - -## Project Context -FESA is a C++17 finite element structural analysis solver. Phase 1 targets a linear static MITC4 shell solver with linear elastic material, nodal loads, fixed boundary conditions, an Abaqus input subset, HDF5-oriented results, and reference-result comparison. - -The user provides Abaqus input files and solved reference result files under the repository `references/` folder. Abaqus is not available locally, so validation must compare against stored reference artifacts. - -## Current Architecture Constraints -- Follow `AGENTS.md`, `PROGRESS.md`, `PLAN.md`, `docs/README.md`, `docs/HARNESS_ENGINEERING.md`, `docs/ARCHITECTURE.md`, and `docs/ADR.md`. -- Use `PLAN.md` for future task ownership, priority, and open questions. -- Use `PROGRESS.md` for completed work, verification notes, blockers, and risks. -- Follow the technical dossier documents: - - `docs/NUMERICAL_CONVENTIONS.md` - - `docs/ABAQUS_INPUT_SUBSET.md` - - `docs/VERIFICATION_PLAN.md` - - `docs/RESULTS_SCHEMA.md` - - `docs/MITC4_FORMULATION.md` -- Use runtime polymorphism for elements, materials, loads, boundary conditions, and analysis algorithms. -- Keep `Domain` close to immutable after parsing. -- Use `AnalysisModel` for the active step view. -- Use `AnalysisState` for mutable physical state and iteration state. -- Let `DofManager` own DOF definitions, constrained/free DOF mapping, equation numbering, and sparse pattern support. -- Use Strategy plus Template Method for analysis execution. -- Use Factory plus Registry for Abaqus keyword to object creation. -- Keep MKL, TBB, and HDF5 behind adapter/wrapper boundaries. -- Store results using step/frame/field/history concepts. -- Use 6 DOFs per shell node: UX, UY, UZ, RX, RY, RZ. -- Use small artificial drilling stiffness in Phase 1, with the final scale documented before implementation. -- Do not enforce a unit system; use Abaqus-style self-consistent units. -- Follow Abaqus result sign conventions. -- Use constrained DOF elimination and full-vector reaction recovery. -- Use `double` precision and int64 ids/indices/equation numbering. -- Require singular system diagnostics. -- Defer mesh quality diagnostics in Phase 1. -- Map Abaqus `S4` to FESA `MITC4`; defer `S4R`. -- Use the Planner -> Generator -> Evaluator harness from `docs/HARNESS_ENGINEERING.md` for nontrivial implementation and phase execution. - -## Created Codex Agents -The first recommended batch has been created under `.codex/agents/`. - -1. `fem_literature_researcher` - - File: `.codex/agents/fem-literature-researcher.toml` - - Role: research FEM shell literature, MITC family background, locking behavior, and implementation implications. - -2. `verification_benchmark_researcher` - - File: `.codex/agents/verification-benchmark-researcher.toml` - - Role: research benchmark cases, `references/` contracts, comparison methods, and acceptance criteria. - -3. `mitc4_formulation_researcher` - - File: `.codex/agents/mitc4-formulation-researcher.toml` - - Role: research MITC4 formulation details and convert them into implementation requirements. - -4. `abaqus_compatibility_researcher` - - File: `.codex/agents/abaqus-compatibility-researcher.toml` - - Role: research the Phase 1 Abaqus input subset and parser compatibility rules. - -5. `harness_sprint_planner` - - File: `.codex/agents/harness-sprint-planner.toml` - - Role: convert PLAN.md tasks and phase steps into testable sprint contracts. - -6. `implementation_generator` - - File: `.codex/agents/implementation-generator.toml` - - Role: implement exactly one accepted sprint contract using TDD. - -7. `harness_sprint_evaluator` - - File: `.codex/agents/harness-sprint-evaluator.toml` - - Role: independently pass/fail sprint output against the contract, docs, tests, and reference artifacts. - -## Recommended Agent Execution Order -1. Run `fem_literature_researcher`. -2. Run `verification_benchmark_researcher`. -3. Run `mitc4_formulation_researcher`. -4. Run `abaqus_compatibility_researcher`. - -This order keeps theory, verification targets, element formulation, and input compatibility aligned before implementation planning. - -Before asking `phase_planner` or `harness_sprint_planner` to create implementation steps, review `PROGRESS.md`, `PLAN.md`, `docs/README.md`, and `docs/HARNESS_ENGINEERING.md`; list any unchecked Implementation Readiness Checklist items in the planning output. - -## Later Agent Candidates -These agents should be created after the first four produce dossiers. - -1. `solver_architecture_researcher` - - Refines responsibilities among `Domain`, `AnalysisModel`, `AnalysisState`, `DofManager`, `Assembler`, and `LinearSolver`. - -2. `sparse_solver_researcher` - - Researches sparse pattern generation, CSR/COO assembly, MKL PARDISO integration, and TBB-safe assembly strategies. - -3. `results_hdf5_schema_researcher` - - Designs the HDF5 group/dataset schema for step/frame/field/history outputs. - -4. `nonlinear_roadmap_researcher` - - Researches geometric nonlinearity, Newton-Raphson, tangent stiffness, increments, and convergence criteria. - -5. `thermal_coupling_researcher` - - Researches heat transfer, thermal load generation, thermal strain, and thermal-stress coupling. - -6. `architecture_guardrail_reviewer` - - Reviews dossiers, phase plans, and future implementation against project rules and ADRs. - -7. `reference_artifact_curator` - - Reviews user-provided `references/` artifacts for CSV/manifest completeness, result schema consistency, source solver metadata, tolerance clarity, and comparison path validity. - -8. `numerical_conventions_reviewer` - - Reviews implementation plans for drift from DOF, unit, sign, precision, reaction recovery, and singular diagnostic rules. - -## User-Provided Inputs Needed -The following inputs should be requested from the user as the research matures: - -1. Additional small Abaqus `.inp` files and solved result artifacts under `references/`. -2. Reaction-force reference exports when available, preferably using a documented `*_reactions.csv` convention. -3. Preferred numerical tolerances for reference comparison, or permission for agents to propose initial tolerances per benchmark. -4. The Abaqus version used to generate each reference artifact when it is not evident from the `.inp` file header. -5. Whether the first implementation plan should target CMake, another build system, or a project-specific build layout. -6. The final default scale for artificial drilling stiffness, after formulation research. - -## Seed Sources -- Abaqus input syntax rules: https://abaqus-docs.mit.edu/2017/English/SIMACAEMODRefMap/simamod-c-inputsyntax.htm -- Abaqus conventions for DOFs, units, coordinate systems, and stress/strain components: https://abaqus-docs.mit.edu/2017/English/SIMACAEMODRefMap/simamod-c-conventions.htm -- Abaqus boundary keyword reference: https://abaqus-docs.mit.edu/2017/English/SIMACAEKEYRefMap/simakey-r-boundary.htm -- Abaqus concentrated load keyword reference: https://abaqus-docs.mit.edu/2017/English/SIMACAEKEYRefMap/simakey-r-cload.htm -- Abaqus shell section behavior: https://abaqus-docs.mit.edu/2017/English/SIMACAEELMRefMap/simaelm-c-shellsectionbehavior.htm -- Abaqus conventional shell element library: https://abaqus-docs.mit.edu/2017/English/SIMACAEELMRefMap/simaelm-r-shellgeneral.htm -- OpenSees ShellMITC4 manual: https://opensees.berkeley.edu/OpenSees/manuals/usermanual/640.htm -- Dvorkin-Bathe four-node shell element paper: https://web.mit.edu/kjb/www/Publications_Prior_to_1998/A_Continuum_Mechanics_Based_Four-Node_Shell_Element_for_General_Nonlinear_Analysis.pdf -- MITC3+/MITC4+ benchmark paper: https://web.mit.edu/kjb/www/Principal_Publications/Performance_of_the_MITC3%2B_and_MITC4%2B_shell_elements_in_widely_used_benchmark_problems.pdf -- COMSOL Scordelis-Lo benchmark example: https://doc.comsol.com/5.6/doc/com.comsol.help.models.sme.scordelis_lo_roof/scordelis_lo_roof.html -- NAFEMS nonlinear benchmark survey page: https://www.nafems.org/publications/pubguide/benchmarks/Page6/ -- HDF5 data model: https://docs.hdfgroup.org/documentation/hdf5/latest/_h5_d_m__u_g.html -- HDF5 datasets: https://docs.hdfgroup.org/documentation/hdf5/latest/_h5_d__u_g.html -- HDF5 attributes: https://portal.hdfgroup.org/documentation/hdf5/latest/_h5_a__u_g.html -- Intel oneMKL `pardiso_64`: https://www.intel.com/content/www/us/en/docs/onemkl/developer-reference-c/2024-2/pardiso-64.html - -## Non-Goals -- Do not implement solver code. -- Do not generate phase execution files until the user explicitly asks for implementation planning. -- Do not require Abaqus execution in local validation. -- Do not treat unsourced formulas or benchmark constants as final. -- Do not add mesh quality diagnostics to Phase 1 planning unless the user changes the decision. diff --git a/docs/NUMERICAL_CONVENTIONS.md b/docs/NUMERICAL_CONVENTIONS.md deleted file mode 100644 index b052215..0000000 --- a/docs/NUMERICAL_CONVENTIONS.md +++ /dev/null @@ -1,235 +0,0 @@ -# Numerical Conventions - -## Purpose -This document defines the numerical conventions that must remain stable across the FESA solver, reference data, tests, and result files. - -When a convention here conflicts with a lower-level implementation note, this document wins unless `docs/ADR.md` is updated. - -## Source Basis -- Abaqus defines translational DOFs 1-3 and rotational DOFs 4-6, with rotations expressed in radians: https://abaqus-docs.mit.edu/2017/English/SIMACAEMODRefMap/simamod-c-conventions.htm -- Abaqus has no built-in unit system except rotation and angle measures; user data must be self-consistent: https://abaqus-docs.mit.edu/2017/English/SIMACAEMODRefMap/simamod-c-conventions.htm -- Abaqus uses right-handed coordinate systems and defines shell local surface directions from the projected global axis and positive element normal: https://abaqus-docs.mit.edu/2017/English/SIMACAEMODRefMap/simamod-c-conventions.htm -- Intel oneMKL provides `pardiso_64`, an ILP64-style PARDISO interface accepting `long long int` integer data for large sparse systems: https://www.intel.com/content/www/us/en/docs/onemkl/developer-reference-c/2024-2/pardiso-64.html - -## Binding Decisions -- MITC4 coordinate and strain details will be defined explicitly in `docs/MITC4_FORMULATION.md`. -- Phase 1 shell nodes use 6 DOFs per node. -- The drilling DOF is retained and receives a small artificial stiffness in Phase 1. -- FESA does not enforce a unit system. Input values must be self-consistent, Abaqus-style. -- Result sign conventions follow Abaqus conventions. -- Essential boundary conditions are applied by constrained DOF elimination. -- Reaction forces are recovered from the full system vectors, not from the reduced system alone. -- Reference comparison uses stored Abaqus inputs and stored solved reference results under `references/`. -- Initial displacement reference comparison uses Abaqus-exported `*_displacements.csv` files. -- Mesh quality diagnostics are not part of Phase 1. -- Singular system diagnostics are required. -- Floating-point values use `double` by default. -- Large-model ids and indices use signed 64-bit integers. - -## Degrees of Freedom -All Phase 1 shell nodes expose six structural DOFs: - -| FESA Component | Abaqus DOF | Meaning | Unit | -|---|---:|---|---| -| `UX` | 1 | Translation in global or transformed x-direction | model length | -| `UY` | 2 | Translation in global or transformed y-direction | model length | -| `UZ` | 3 | Translation in global or transformed z-direction | model length | -| `RX` | 4 | Rotation about x-axis | radian | -| `RY` | 5 | Rotation about y-axis | radian | -| `RZ` | 6 | Rotation about z-axis | radian | - -Implementation rules: -- Store DOF component ids using a compact enum or integer range `1..6`. -- Store global node ids, element ids, equation ids, sparse indices, and nonzero counts with signed 64-bit integer types. -- `DofManager` owns active DOF discovery, constrained/free partitioning, equation numbering, and sparse pattern inputs. -- Do not store equation ids inside `Node` or `Element`. - -## Precision and Numeric Types -Recommended type aliases: - -```cpp -using Real = double; -using GlobalId = std::int64_t; -using LocalIndex = std::int64_t; -using EquationId = std::int64_t; -using SparseIndex = std::int64_t; -``` - -Rules: -- Use `double` for matrix entries, vector entries, coordinates, material constants, loads, displacements, rotations, and result values. -- Use 64-bit integer indexing throughout the sparse assembly path so that MKL `pardiso_64` remains available for large models. -- Avoid silent narrowing when passing ids or sparse arrays to external libraries. - -## Units -FESA follows the Abaqus convention: no unit system is built into the solver. - -Rules: -- The solver core treats all dimensional input as unitless numeric values. -- The user and reference data must use a self-consistent unit system. -- Rotations are always radians. -- Result files may store an optional `unit_system_note` metadata string, but the solver must not convert units. - -Examples of acceptable unit systems: -- `N, mm, tonne, second` -- `N, m, kg, second` -- `lbf, inch, lbf*s^2/in, second` - -## Coordinate Systems -Global coordinates: -- The default global system is right-handed Cartesian. -- Phase 1 does not support user-defined transformed nodal coordinate systems unless explicitly added to `docs/ABAQUS_INPUT_SUBSET.md`. - -Shell local directions: -- FESA result signs follow Abaqus shell local direction conventions. -- The exact MITC4 element basis construction must be defined in `docs/MITC4_FORMULATION.md`. -- For Abaqus compatibility, the positive shell normal is tied to node ordering by the right-hand rule. -- Local 1 and 2 directions must form a right-handed basis with the positive normal. - -## Result Sign Convention -FESA result sign conventions follow Abaqus unless a FESA-specific exception is documented. - -Phase 1 output variables: -- `U`: nodal displacement and rotation vector in DOF order `UX, UY, UZ, RX, RY, RZ`. -- `RF`: nodal reaction force and reaction moment in the same component order. -- `S`: shell stress components in local shell directions when available. -- `E`: shell strain components in local shell directions when available. -- `SF`: shell section force and moment resultants when available. - -Stress and strain component ordering follows the Abaqus convention: -- direct 1 -- direct 2 -- direct 3 -- shear 12 -- shear 13 -- shear 23 - -Shear strain output should be documented as engineering shear strain when matched to Abaqus-style output. - -## Abaqus Displacement CSV Mapping -The initial reference displacement CSV format maps Abaqus exported columns to FESA `U` components: - -| CSV Column | FESA Component | Meaning | -|---|---|---| -| `Node Label` | `entity_id` | Node id, stored as int64 | -| `U-U1` | `UX` | Translation in global 1/x direction | -| `U-U2` | `UY` | Translation in global 2/y direction | -| `U-U3` | `UZ` | Translation in global 3/z direction | -| `UR-UR1` | `RX` | Rotation about global 1/x direction, radians | -| `UR-UR2` | `RY` | Rotation about global 2/y direction, radians | -| `UR-UR3` | `RZ` | Rotation about global 3/z direction, radians | - -Rules: -- CSV numeric values are parsed as `double`. -- Node labels are matched to FESA result entity ids exactly. -- Missing nodes, duplicate node labels, missing columns, or nonnumeric values are reference artifact errors. -- CSV comparison uses the same absolute/relative tolerance policy as other reference artifacts. - -## Abaqus Reaction CSV Mapping -Reference reaction CSV files map Abaqus exported force and moment columns to FESA `RF` components: - -| CSV Column | FESA Component | Meaning | -|---|---|---| -| `Node Label` | `entity_id` | Node id, stored as int64 | -| `RF-RF1` | `RFX` | Reaction force in global 1/x direction | -| `RF-RF2` | `RFY` | Reaction force in global 2/y direction | -| `RF-RF3` | `RFZ` | Reaction force in global 3/z direction | -| `RM-RM1` | `RMX` | Reaction moment about global 1/x direction | -| `RM-RM2` | `RMY` | Reaction moment about global 2/y direction | -| `RM-RM3` | `RMZ` | Reaction moment about global 3/z direction | - -Rules: -- Accepted reaction CSV filenames may use `*_reactionforces.csv` or `*_reactions.csv`. -- CSV numeric values are parsed as `double`. -- Node labels are matched to FESA result entity ids exactly. -- Missing nodes, duplicate node labels, missing columns, or nonnumeric values are reference artifact errors. -- Node-wise reaction comparison uses the full-vector FESA `RF` field, not reduced solver quantities. -- Do not relax tolerances to pass a reaction comparison without documenting the numerical reason. - -## Boundary Conditions -Phase 1 uses constrained DOF elimination: - -1. Build the full DOF list. -2. Mark constrained DOFs from `*Boundary`. -3. Partition DOFs into free and constrained sets. -4. Assemble or project the reduced free-DOF system. -5. Solve for free DOF unknowns. -6. Reconstruct the full displacement vector. - -Rules: -- Phase 1 supports zero-valued fixed constraints as the primary path. -- Nonzero prescribed displacements are not a Phase 1 requirement unless added to `docs/ABAQUS_INPUT_SUBSET.md`. -- `DofManager` owns constrained/free mapping and must provide enough data to reconstruct full vectors. - -## Reaction Recovery -Reaction force and moment recovery uses the full system: - -```text -R_full = K_full * U_full - F_full -``` - -Rules: -- `K_full` means the assembled stiffness before constrained DOF elimination. -- `U_full` means the solved displacement vector reconstructed with constrained DOF values. -- `F_full` means the full external load vector in the original global DOF space. -- `RF` is reported primarily for constrained DOFs, but full residual-like vectors may be retained for diagnostics. -- Reference comparison should include at least one reaction balance test. - -## Drilling DOF Policy -Phase 1 retains `RZ` at each shell node and assigns a small artificial drilling stiffness. - -Rules: -- The value must be parameterized, not hard-coded deep inside the MITC4 kernel. -- The Phase 1 default follows `docs/MITC4_FORMULATION.md`: `drilling_stiffness_scale = 1.0e-3` applied to the minimum positive physical local stiffness diagonal before the local-to-global six-DOF transform. -- The artificial stiffness should be small relative to representative membrane or bending stiffness, but large enough to avoid a singular stiffness matrix for unconstrained drilling modes. -- Reference comparisons should include sensitivity checks if the drilling stiffness materially changes displacement results. - -## Singular System Diagnostics -FESA must provide actionable diagnostics before or during linear solve failure. - -Required Phase 1 checks: -- No free DOFs exist after applying constraints. -- At least one active element exists in the current `AnalysisModel`. -- Every active element references existing nodes. -- Every active element references an assigned property and material. -- Every active property references an existing material. -- Every load and boundary condition references an existing node or node set. -- At least one load component is nonzero for ordinary static solve tests unless the test explicitly expects zero response. -- Free DOFs exist that are not touched by any active element connectivity. -- Rotational DOFs, especially drilling DOFs, may be unconstrained or weakly constrained. -- The reduced system size and sparse nonzero count are nonzero before solve. -- The sparse solver reports zero pivot, numerical factorization failure, or structural singularity. - -Recommended diagnostic style: - -```text -FESA-SINGULAR-DOF-UNTOUCHED: - Node 42 DOF RZ is free but has no stiffness contribution from active elements. - Check boundary conditions, element connectivity, property assignment, or drilling stiffness settings. -``` - -Phase 1 explicitly does not perform mesh quality diagnostics such as aspect ratio, warpage, skew, or element distortion checks. - -## Reference Comparison Tolerances -Reference comparison must use both absolute and relative tolerances: - -```text -pass if abs(actual - expected) <= abs_tol - or abs(actual - expected) <= rel_tol * max(abs(expected), reference_scale) -``` - -Initial defaults may be proposed per benchmark in `docs/VERIFICATION_PLAN.md`. Final tolerances should be tied to stored reference data and solver maturity. - -## Implementation Gate -Before implementing solver modules that depend on numerical conventions: -- Define the `Real`, id, equation id, and sparse index aliases in one shared core header. -- Define a single DOF enum or equivalent mapping for `UX`, `UY`, `UZ`, `RX`, `RY`, `RZ`. -- Add tests proving Abaqus DOF numbers `1..6` map to the same internal components. -- Add tests proving constrained/free vector reconstruction preserves original full-space DOF order. -- Add at least one reaction recovery test that computes `K_full * U_full - F_full`. -- Ensure singular diagnostics can reference node id, DOF component, element id, property id, and set name. - -## Open Decisions -- Stress/strain/resultant recovery locations and component labels after `U` and `RF` are stable. -- Whether to parse Abaqus `*Orientation` or nodal shell normals. -- The future nodal-director policy for truly non-flat or strongly warped quadrilateral elements. -- Optional non-displacement CSV formats, such as reaction force or stress/resultant exports. diff --git a/docs/PRD.md b/docs/PRD.md deleted file mode 100644 index 098447a..0000000 --- a/docs/PRD.md +++ /dev/null @@ -1,73 +0,0 @@ -# PRD: FESA - -## 목표 -MITC4 Shell 요소를 사용해 구조 해석을 하는 유한요소 솔버를 개발 - -## 사용자 -구조해석을 원하는 엔지니어 - -## 제품 방향 -FESA는 Abaqus input subset을 읽고 `references/`에 저장된 Abaqus reference 결과와 비교 가능한 구조해석 결과를 생성하는 검증 중심 solver이다. Phase 1은 "많은 기능"보다 "작은 선형 정적 MITC4 모델들을 정확하게 풀고 반복 검증할 수 있는 기반"을 목표로 한다. - -## 핵심 기능 -1. MITC4 Shell 요소를 사용한 구조해석 -2. Parallel 연산을 통한 계산 성능 향상 -3. Abaqus Input format 사용을 통해 다른 상용 소프트웨어와 호환성 높음 -4. step/frame/history 기반 해석 결과 관리 -5. `references/*.inp`와 `references/*_displacements.csv` 등 reference 모델 결과 비교를 통한 정확도 검증 -6. singular system 진단을 통한 해석 실패 원인 추적 - -## 개발 계획 -1. Phase 1 - - MITC4 Shell 요소 - - 선형 탄성 재료 - - 절점하중 - - 고정 경계조건 - - Abaqus input subset: *Node, *Element, *Nset, *Elset, *Material, *Elastic, *Shell Section, *Boundary, *Cload, *Step - - 선형 정적 해석 - - step/frame 기반 결과 저장의 최소 구조 - - `references/`의 Abaqus input/result artifact 기반 reference 모델 결과 비교 테스트 - - 6자유도 shell node: UX, UY, UZ, RX, RY, RZ - - drilling 자유도에는 작은 인공 강성 적용 - - constrained DOF 제거 방식 - - full vector 기반 reaction force 계산 - - double precision 및 int64 id/index/equation numbering - - singular system 진단 - - Abaqus 실행 없이 저장된 reference artifact와 비교 -2. Phase 2 - - 압력하중 - - RBE2, RBE3 경계조건 - - Newton-Raphson 비선형 알고리즘 - - 기하비선형 중심의 비선형 정적해석 -3. Phase 3 - - HHT 시간 적분 알고리즘 - - time dependent 하중 - - 비선형 동적 해석 -4. Phase 4 - - Heat transfer 해석 - - 절점 온도에 대한 열전도 요소 행렬 계산 - - 온도 하중 계산 - - thermal-stress coupling 확장 -5. Phase 5 - - 1D, 3D 요소 구현 - - 기타 하중, 경계조건 구현 - -## Phase 1 성공 기준 -- Phase 1 Abaqus input subset을 파싱하여 `Domain`과 `AnalysisModel`을 일관되게 구성할 수 있다. -- `DofManager`가 6자유도 shell node, constrained/free mapping, full/reduced vector reconstruction을 검증 가능한 방식으로 처리한다. -- 최소 MITC4 element-level test가 통과한다: shape function, stiffness symmetry, rigid body behavior, drilling stiffness sensitivity. -- 선형 정적 해석에서 `U`와 `RF`를 결과 schema에 맞게 저장한다. -- `RF = K_full * U_full - F_full` 기반 reaction recovery가 reference 또는 평형 테스트로 검증된다. -- singular system negative test가 원인을 설명하는 진단 메시지를 낸다. -- 최소 3개 stored reference case가 통과한다: single-element case, simple multi-element plate/shell case, curved shell benchmark. 초기 자동 displacement 비교 형식은 `*_displacements.csv`이다. - -## Phase 1 제외 범위 -- Abaqus `S4R` 및 hourglass control -- pressure load -- RBE2/RBE3 -- nonzero prescribed displacement -- geometric/material nonlinearity -- dynamic analysis -- heat transfer 및 thermal-stress coupling -- composite shell section -- mesh quality diagnostics diff --git a/docs/Paper/2007쉘구조물의유한요소해석에대하여/2007쉘구조물의유한요소해석에대하여.md b/docs/Paper/2007쉘구조물의유한요소해석에대하여/2007쉘구조물의유한요소해석에대하여.md deleted file mode 100644 index f54e988..0000000 --- a/docs/Paper/2007쉘구조물의유한요소해석에대하여/2007쉘구조물의유한요소해석에대하여.md +++ /dev/null @@ -1,558 +0,0 @@ -# 구조물의 유한요소해석에 대하여 - -On the Finite Element Analysis of Shell Structures - -이필승\*·노혁천\*\* - -Lee, Phill-Seung·Noh, Hyuk-Chun ····························································································································································································································· 1. 서 론 - -Based on recent research works, important concepts on the finite element analysis of shell structures and the relations among them are presented in this paper. We review the basic shell mathematical model, which is the underlying mathematical model of the continuum mechanics based shell finite elements. The asymptotic theory of shell structures then is reviewed and we present how to evaluate the asymptotic behavior in finite element solutions. S-norm is introduced as an error measure of finite element solutions and we show "locking" in the convergence curves of shell finite element solutions. We discuss the concept of Keywords : shell structures, finite elements, asymptotic behavior, uniform optimal convergence, benchmark tests ····························································································································································································································· 요 지 본 논문에서는 최근 주요 연구들을 토대로 쉘 구조물의 유한요소해석에 대하여 중요한 개념들과 그 연관관계를 고찰한다. 감절점 쉘 유한요소의 수학모델인 기본쉘수학모델을 살펴본다. 쉘 구조물의 두께가 얇아짐에 따라 일어나는 쉘 구조문제의 세가지 극한거동들(휨지배거동, 막지배거동, 혼합지배거동)에 대한 쉘의 점근거동 이론을 소개하고 점근거동을 유한요소해석 을 통해 찾아내는 방법을 알아본다. 유한요소해의 오차를 s-norm으로 평가하는 방법을 소개하고 이를 이용하여 쉘 유한요소 의 잠김현상이 유한요소해의 수렴곡선에 어떻게 나타나는지 살펴본다. 쉘 구조물의 유한요소해석에서 균일최적수렴의 개념을 논의한다. 마지막으로 이상적인 쉘 유한요소의 조건을 알아보고 쉘 유한요소의 성능평가를 위한 방법론을 제시한다. - -핵심용어 : 쉘 구조물, 유한요소해석, 점근거동, 균일최적수렴, 성능시험 - -ments and propose how to perform benchmark tests of shell finite elements. 계란의 외피가 얼마나 큰 외력에 견딜 수 있는가 하는 것 - -은 일반 대중들에게도 잘 알려져 있는 사실이다. 그 밖에도 우리는 자연 속에서 갑각류의 표피나 조개 껍질과 같은 여 러 종류의 쉘 구조물들(shell structures)을 접할 수 있다. 이 런 자연의 예들은 쉘 구조가 매우 이상적이고 효과적인 구 조물의 한 형태라는 것을 간접적으로 증명하고 있다. 인간이 만들어낸 쉘 구조물들도 셀 수 없이 다양하고 많으며 우리 는 쉘 구조물들 속에서 살고 있다고 해도 과언이 아니다. 유한요소법(finite element method)은 쉘 구조물의 선형 및 비선형 해석에 가장 널리 쓰이는 방법으로 지난 수십 년 동안 쉘 구조물의 유한요소해석에 대한 연구가 활발하게 이 루어져 오고 있다(Bathe, 1996; 최창근, 2002; Chapelle and Bathe, 2003; Noh, 2006). 그러나 쉘 구조물은 쉘의 형상, 경계조건, 하중 등에 따라 아주 다양한 거동을 보이며 - -····························································································································································································································· 기 때문에 쉘 구조물의 유한요소해석에 앞서 근본적인 쉘 이론 및 물리적 거동에 대한 이해가 필수적이다(Chapelle and Bathe, 1998; Lee and Bathe, 2002; Chapelle and - -Bathe, 2003). 이러한 쉘 구조물의 거동에 대한 이해의 핵 심은 쉘 구조물의 두께가 얇아짐에 따라 나타나는 점근거동 (asymptotic behavior)을 연구하는 것이다(Lee and Bathe, 2002; Bathe, Chapelle and Lee, 2003). 쉘 구조물의 점근 거동은 일반적으로 휨지배(bending dominated)거동, 막지배 (membrane dominated)거동, 혼합(mixed)지배거동 등으로 나 누어진다. 일반적으로 공학(engineering)에서 주어진 문제를 풀기 위 한 접근방법은 실험(experiment)과 관찰(investigation)을 통 하여 물리적 거동을 살펴본 후 그 물리문제의 중요한 특성/ 인자를 찾아내고 그에 따른 여러 가지 가정들(assumptions) 을 사용하여 문제를 단순화시켜 수학모델(mathematical model)을 만든다. 수학모델은 주어진 물리문제를 이론적으로 접근할 수 있는 방법을 제공해 준다. 수학모델의 해는 이론 - -특히 쉘의 두께가 얇을 경우 거동의 특성이 매우 민감해지 - -\*정회원 · 교신저자 · 삼성중공업(주) 건설부문 과장 (E-mail : phillseung.lee@samsung.com) - -적인 방법으로 구해질 수 있으나 풀고자 하는 문제가 매우 복잡할 때는 해를 구하기가 거의 불가능하다. 수치해석 (numerical analysis)을 이용하면 복잡한 물리문제의 근사해 (approximation)를 구할 수 있고 그 결과를 실험/관찰 결과 와 비교하면 수학모델이나 수치해석의 적정성을 평가할 수 있다. - -이와 같은 일련의 작업들, 즉, 물리적 거동, 수학모델, 수 치해석은 서로 밀접하게 연관되어 있다. 그러므로, 쉘 구조 물의 유한요소해석을 명확하게 이해하기 위해서는 쉘 구조 물의 물리적 거동에 대한 이해, 쉘의 수학모델(mathematical shell model)에 대한 이해와 쉘 유한요소에 대한 이해가 동 시에 체계적이고 심도 있게 이루어져야 한다. 세가지 모두에 대한 통합적인 이해가 없을 경우 쉘 구조물의 유한요소해석 에 있어서 중대한 오류를 범할 수 있다. 본 논문의 목적은 이 각각의 세가지 부분에 대한 이해와 이들이 서로 어떻게 유기적으로 관계를 맺고 있는지를 최근 주요 연구들(Lee and Bathe, 2002; Chapelle and Bathe, 2003; Bathe, Chapelle and Lee, 2003; Hiller and Bathe, 2003; Lee and Bathe, 2005)을 중심으로 정리하여 고찰해 보고 이상적 인 쉘 유한요소의 성질과 쉘 유한요소의 성능평가 방법을 제시하는 것이다. 특히 쉘 구조물의 설계를 위하여 유한요소 해석을 수행하는 기술자들(engineers)이나 유한요소법을 공부 /연구하는 학생/연구자들의 "쉘 구조물의 유한요소해석에 대 한 이해"를 돕고자 하는데 글의 초점을 맞추었다. - -유한요소법에 의해 쉘 구조물을 효과적으로 해석하기 위해 서는 신뢰할 만한 쉘 유한요소를 사용해야 한다는 것은 자 명하다. 일반적으로 변위법에 의해 정식화(displacement based formulation)된 쉘 유한요소는 사용된 근사함수 (interpolation function)의 차수(order)에 상관없이 휨지배 및 혼합지배거동을 하는 쉘 구조물에 대하여 과도한 강성을 나 타낸다(Bathe, 1996; Chapelle and Bathe, 2003). 이를 잠 김현상(locking phenomenon)이라 하며 효과적인 쉘 유한요 소의 개발에 있어서 극복해야 할 어려운 문제들 중의 하나 이다. 이상적인 쉘 유한요소는 여러 가지 점근거동과 다양한 형상의 쉘 구조문제에 있어서 균일최적수렴(uniform optimal convergence)을 보여야 하며 이상적인 쉘 유한요소를 개발하 는 것은 매우 어려운 일이다. - -본 논문에서는 먼저 쉘의 대표적인 수학모델을 설명하고 쉘 구조물의 점근거동에 대한 기본이론을 살펴본 후 임의의 쉘 구조물의 점근거동을 어떻게 알아낼 수 있는지를 알아본 다. 또한 쉘 유한요소해의 오차(error)를 평가하는 규준을 살 펴보고 이를 바탕으로 잠김현상 발생 시의 쉘 유한요소의 수렴과 균일최적수렴에 대해 알아본다. 마지막으로 이상적인 쉘 유한요소와 쉘 유한요소의 성능평가에 대하여 논한다. 앞 으로 논의하는 내용은 등방성재료(isotropic material)에 대한 선형탄성(linear elastic)이론에 국한된다. - -# 2. 쉘의 수학모델(mathematical shell model) - -기본쉘수학모델(basic shell mathematical model)은 쉘 구 조물의 휨(bending)거동, 면내(membrane)거동, 면외전단 (transverse shearing)거동과 그 상호연관(coupling)관계를 모 두 표현할 수 있는 가장 일반적인 쉘 수학모델로 3차원 연 속체 역학으로부터 유도된 감절점 쉘 유한요소(Ahmad, Irons and Zienkiewicz, 1970; Bathe, 1996)와 동일한 변형 률 항들을 가지고 있다(Chapelle and Bathe, 1998; Chapelle and Bathe, 2003; Lee and Bathe, 2005). 즉, 기본쉘수학 모델은 감절점 쉘 유한요소의 수학모델인 것이다. 본 장에서 는 미분기하학(differential geometry)을 이용하여 쉘의 형상 (geometry)과 변형거동(kinematics)을 살펴보고 기본쉘수학모 델의 유도를 보여준다. - -# 2.1 쉘의 형상 (shell geometry) - -쉘은 두께(thickness)가 얇은 3차원 구조물이다. 두께가 얇 다는 특징 때문에 쉘의 형상은 쉘의 중심면으로 이루어진 2 차원 영역과 두께에 의하여 정의 될 수 있다. 여기서는 기 본쉘수학모델에서 쓰이는 쉘의 형상에 관한 개념을 미분기 하학을 통하여 보여준다. - -![](_page_1_Figure_9.jpeg) - -그림 1. 쉘의 형상 - -λ - -φ - -$$\vec{a}_{\alpha} = \frac{\partial \vec{\phi}(\xi^1, \xi^2)}{\partial \xi^{\alpha}} \tag{1}$$ - -$$\vec{a}^{\alpha} \cdot \vec{a}_{\beta} = \delta^{\alpha}_{\beta} \tag{2}$$ - -미분기하학의 정의들을 유도하기 위하여 아인슈타인 합표 시규약(Einstein summation convention)을 사용한다. α, β, , µ는 1에서 2까지 변하며 i, j, k는 1에서 3까지 변하는 첨자(index)들이다. 쉘의 중심면(midsurface)은 그림 1에서 보이는 2차원 영역 ω에서 S로의 사상(mapping)을 나타내는 함수 에 의하여 정의 된다. 중심면의 공변기저벡터(covariant base vector)는 다음과 같이 나타내어진다. (1) 위의 식의 공변(covariant)기저벡터에 대응하는 반변기저벡터 (contravariant base vector)는 다음의 관계에 의하여 얻어진다. (2) 여기서 는 α와 β가 같을 때 1이고 다를 때 0인 δ β α - -$$\vec{a}_3 = \frac{\vec{a}_1 \times \vec{a}_2}{\left|\vec{a}_1 \times \vec{a}_2\right|} \tag{3}$$ - -$$\overrightarrow{\Phi}(\xi^{l}, \xi^{2}, \xi^{3}) = \overrightarrow{\phi}(\xi^{l}, \xi^{2}) + \xi^{3}\overrightarrow{a}_{3}(\xi^{l}, \xi^{2}) \tag{4}$$ - -$$\Omega = \left\{ (\xi^{1}, \xi^{2}, \xi^{3}) \in \Re^{3} | (\xi^{1}, \xi^{2}) \in \omega, \xi^{3} \in \left[ -\frac{t(\xi^{1}, \xi^{2})}{2}, \frac{t(\xi^{1}, \xi^{2})}{2} \right] \right\}$$ -(5) - -Kronecker symbol이다. 쉘의 중심면에 수직인 벡터는 중심 면의 공변(covariant)기저벡터들의 벡터곱(vector product)으로 정의 된다. (3) 쉘의 3차원 기하형상은 다음 식으로 표현된다. (4) 여기서 변수 ξ , ξ , ξ 의 영역은 다음과 같다. (5) 여기서 t는 쉘의 두께이다. - -$$a_{\alpha\beta} = \vec{a}_{\alpha} \cdot \vec{a}_{\beta} \tag{6}$$ - -$$a^{\alpha\beta} = \vec{a}^{\alpha} \cdot \vec{a}^{\beta} \tag{7}$$ - -위의 정의들을 이용하여 쉘의 중심면에서 surface 텐서들 (tensors)을 정의 할 수 있다. 첫 번째 기본텐서는 2D metric 텐서로 공변형(covariant type)은 다음 식과 같다. (6) 위 식의 반변형(contravariant type)은 다음과 같이 나타난다. (7) 두 번째 기본텐서는 곡률(curvature)텐서로서 쉘 중심면의 곡 - -$$b_{\alpha\beta} = \vec{a}_3 \cdot \vec{a}_{\alpha,\beta} \tag{8}$$ - -$$b^{\alpha}_{\beta} = a^{\alpha\lambda}b_{\lambda\beta} \tag{9}$$ - -$$c_{\alpha\beta} = b_{\alpha}^{\lambda} b_{\lambda\beta} \tag{10}$$ - -률에 관한 정보를 담고 있다. 공변형(covariant type)은 다음 과 같다. (8) 또한 위 식의 합성형(mixed)텐서는 다음 식과 같다. (9) 세 번째 기본텐서는 다음과 같이 정의 된다. (10) 쉘의 중심면에서의 벡터를 라고 하면 이 벡터의 공변미분 w - -$$w_{\alpha|\beta} = w_{\alpha,\beta} - \Gamma^{\lambda}_{\alpha\beta} w_{\lambda} \tag{11}$$ - -Γαβ λ - -$$\Gamma^{\lambda}_{\alpha\beta} = \vec{a}_{\alpha,\beta} \cdot \vec{a}^{\lambda} \tag{12}$$ - -$$\vec{g}_i = \frac{\partial \vec{\Phi}(\xi^1, \xi^2, \xi^3)}{\partial \xi^i} \tag{13}$$ - -$$\vec{g}_{\alpha} = \vec{a}_{\alpha} - \xi^3 b_{\alpha}^{\lambda} \vec{a}_{\lambda} \tag{14}$$ - -$$\vec{g}_3 = \vec{a}_3 \tag{15}$$ - -$$\vec{g} \cdot \vec{g}_j = \delta_j^i \tag{16}$$ - -# 2.2 쉘의 변형거동(shell kinematics) - -$$\overrightarrow{U}(\xi^{\mathsf{I}}, \xi^{\mathsf{2}}, \xi^{\mathsf{3}}) = \overrightarrow{u}(\xi^{\mathsf{I}}, \xi^{\mathsf{2}}) + \xi^{\mathsf{3}} \theta_{\lambda}(\xi^{\mathsf{I}}, \xi^{\mathsf{2}}) \overrightarrow{a}^{\lambda}(\xi^{\mathsf{I}}, \xi^{\mathsf{2}})$$ - -$$\tag{17}$$ - - (11) 여기서 는 면에서의 Christoffel symbol이다. (12) 식 (4)로부터 3차원 공변(covariant)기저벡터를 얻을 수 있다. (13) 그리고 위 식으로부터 다음 식들이 얻어진다. (14) (15) 3차원 반변(contravariant)기저벡터는 다음과 같이 정의 된다. (16) 쉘의 변형거동에 있어서 기본이 되는 가정은 변형전 쉘의 중심면에 수직인 직선은 변형중에도 직선을 유지하며 늘어 나거나 줄어들지 않는다는 것이고, 이때 쉘의 변위는 다음과 같이 표현된다. (17) 여기서 는 쉘의 중심면의 미소변위(infinitesimal translation)를 나타내고 는 쉘의 중심면에 수직인 직선의 미소회전(infinitesimal rotation)을 나타낸다. 는 회전 벡터 이고 는 그 회전 벡터에 의한 변위를 u ξ 1 ,ξ 2 ( ) θ λ ξ 1 ,ξ 2 ( ) θ λa λ θ ξ 3 θ λa λ u a 1 2 a 3 a3 - -$$e_{ij} = \frac{1}{2} (\vec{g}_i \cdot \vec{U}_{,j} + \vec{g}_j \cdot \vec{U}_{,i})$$ -(18) - -$$\vec{a}^2$$ -, $\vec{a}^3$ (= $\vec{a}_3$ )에 의하여 주어진다. -선형해석을 위한 3차원 Green-Lagrange 공변변형률 -(covariant strain) 텐서의 선형부분은 다음과 같이 정의 된다. - $e_{ij} = \frac{1}{2}(\vec{g}_i \cdot \vec{U}_{,j} + \vec{g}_{j} \cdot \vec{U}_{,i})$ (18) -여기서 - $\vec{U}_{,i} = \frac{\partial \vec{U}(\xi^1, \xi^2, \xi^3)}{\partial \xi^i}$ . (19) -식 (14), (15)와 (17)을 식 (18)에 대입시키면 변형률 텐서의 공변(covariant)항들을 얻을 수 있다. - $e_{\alpha\beta} = \gamma_{\alpha\beta}(\vec{u}) + \xi^3 \chi_{\alpha\beta}(\vec{u},\vec{\theta}) - (\xi^3)^2 \kappa_{\alpha\beta}(\vec{\theta})$ (20a) - $e_{\alpha3} = \zeta_{\alpha}(\vec{u},\vec{\theta})$ (20b) - $e_{33} = 0$ (20c) -여기서 - $\gamma_{\alpha\beta}(\vec{u}) = \frac{1}{2}(u_{\alpha|\beta} + u_{\beta|\alpha}) - b_{\alpha\beta}u_3$ (21a) - $\chi_{\alpha\beta}(\vec{u},\vec{\theta}) = \frac{1}{2}(\theta_{\alpha|\beta} + \theta_{\beta|\alpha} - b_{\beta}^{\lambda}u_{\lambda|\alpha} - b_{\alpha}^{\lambda}u_{\lambda|\beta}) + c_{\alpha\beta}u_3$ (21b) - $\kappa_{\alpha\beta}(\vec{\theta}) = \frac{1}{2}(b_{\beta}^{\lambda}\theta_{\lambda|\alpha} - b_{\alpha}^{\lambda}\theta_{\lambda|\beta})$ (21c) - $\zeta_{\alpha}(\vec{u},\vec{\theta}) = \frac{1}{2}(\theta_{\alpha} + u_{3,\alpha} + b_{\alpha}^{\lambda}u_{\lambda})$ (21d) -등방성재료(isotropic material)에 대한 평면응력조건(plane - -. (19) - -$$e_{\alpha\beta} = \gamma_{\alpha\beta}(\vec{u}) + \xi^3 \chi_{\alpha\beta}(\vec{u}, \vec{\theta}) - (\xi^3)^2 \kappa_{\alpha\beta}(\vec{\theta})$$ - (20a) - -$$e_{\alpha\beta} = \zeta_{\alpha}(\vec{u}, \vec{\theta}) \tag{20b}$$ - -$$e_{33} = 0$$ - (20c) - -의 공변(covariant)항들을 얻을 수 있다. -$$e_{\alpha\beta} = \gamma_{\alpha\beta}(\vec{u}) + \xi^3 \chi_{\alpha\beta}(\vec{u},\vec{\theta}) - (\xi^3)^2 \kappa_{\alpha\beta}(\vec{\theta}) \qquad (20a)$$ - -$$e_{\alpha3} = \zeta_{\alpha}(\vec{u},\vec{\theta}) \qquad (20b)$$ - -$$e_{33} = 0 \qquad (20c)$$ -역기서 -$$\gamma_{\alpha\beta}(\vec{u}) = \frac{1}{2}(u_{\alpha|\beta} + u_{\beta|\alpha}) - b_{\alpha\beta}u_3 \qquad (21a)$$ - -$$\chi_{\alpha\beta}(\vec{u},\vec{\theta}) = \frac{1}{2}(\theta_{\alpha|\beta} + \theta_{\beta|\alpha} - b_{\beta}^{\lambda}u_{\lambda|\alpha} - b_{\alpha}^{\lambda}u_{\lambda|\beta}) + c_{\alpha\beta}u_3 \qquad (21b)$$ - -$$\kappa_{\alpha\beta}(\vec{\theta}) = \frac{1}{2}(b_{\beta}^{\lambda}\theta_{\lambda|\alpha} - b_{\alpha}^{\lambda}\theta_{\lambda|\beta}) \qquad (21c)$$ - -$$\zeta_{\alpha}(\vec{u},\vec{\theta}) = \frac{1}{2}(\theta_{\alpha} + u_{3,\alpha} + b_{\alpha}^{\lambda}u_{\lambda}) \qquad (21d)$$ -등방성재료(isotropic material)에 대한 평면응력조건(plane) - -$$\chi_{\alpha\beta}(\vec{u}, \vec{\theta}) = \frac{1}{2}(\theta_{\alpha|\beta} + \theta_{\beta|\alpha} - b_{\beta}^{\lambda} u_{\lambda|\alpha} - b_{\alpha}^{\lambda} u_{\lambda|\beta}) + c_{\alpha\beta} u_{3}$$ -(21b) -$$\kappa_{\alpha\beta}(\vec{\theta}) = \frac{1}{2}(b_{\beta}^{\lambda} \theta_{\lambda|\alpha} - b_{\alpha}^{\lambda} \theta_{\lambda|\beta})$$ -(21c) -$$\zeta_{\alpha}(\vec{u}, \vec{\theta}) = \frac{1}{2}(\theta_{\alpha} + u_{3,\alpha} + b_{\alpha}^{\lambda} u_{\lambda})$$ -(21d) -동방성재료(isotropic material)에 대한 평면응력조건(plane - -$$\kappa_{\alpha\beta}(\vec{\theta}) = \frac{1}{2}(b_{\beta}^{\lambda}\theta_{\lambda|\alpha} - b_{\alpha}^{\lambda}\theta_{\lambda|\beta})$$ - (21c) -$$\zeta_{\alpha}(\vec{u},\vec{\theta}) = \frac{1}{2}(\theta_{\alpha} + u_{3,\alpha} + b_{\alpha}^{\lambda}u_{\lambda})$$ - (21d) -한방성재료(isotropic material)에 대한 평면응력조건(plane - -$$\zeta_{\alpha}(\vec{u}, \vec{\theta}) = \frac{1}{2}(\theta_{\alpha} + u_{3,\alpha} + b_{\alpha}^{\lambda}u_{\lambda})$$ - (21d) -통방성재료(isotropic material)에 대한 평면응력조건(plane - - (21d) 등방성재료(isotropic material)에 대한 평면응력조건(plane - -$$\sigma^{\alpha\beta} = C^{\alpha\beta\lambda\mu} e_{\lambda\mu} \tag{22a}$$ - -$$\sigma^{\alpha\beta} = \frac{1}{2} D^{\alpha\lambda} e_{\lambda\beta} \tag{22b}$$ - -$$C^{\alpha\beta\lambda\mu} = \frac{E}{2(1-v)} \left( g^{\alpha\lambda} g^{\beta\mu} + g^{\alpha\mu} g^{\beta\lambda} + \frac{2v}{1+v} g^{\alpha\beta} g^{\lambda\mu} \right)$$ -(23a) - -$$D^{\alpha\lambda} = \frac{2E}{1+\nu}g^{\alpha\lambda} \tag{23b}$$ - -g αβ g αβ=g α g β ( ) - -V U - -$$\int_{\Omega} C^{\alpha\beta\lambda\mu} e_{\alpha\beta}(\overrightarrow{U}) e_{\lambda\mu}(\overrightarrow{V}) dV + \int_{\Omega} D^{\alpha\lambda} e_{\alpha\beta}(\overrightarrow{U}) e_{\lambda\beta}(\overrightarrow{V}) dV$$ - -$$= \int_{\Omega} \overrightarrow{F} \cdot \overrightarrow{V} dV \qquad (24)$$ - -F - -$$\vec{V}(\xi^{\mathsf{I}}, \xi^{\mathsf{2}}, \xi^{\mathsf{3}}) = \vec{v}(\xi^{\mathsf{I}}, \xi^{\mathsf{2}}) + \xi^{\mathsf{3}} \eta_{\lambda}(\xi^{\mathsf{I}}, \xi^{\mathsf{2}}) \vec{a}^{\lambda}(\xi^{\mathsf{I}}, \xi^{\mathsf{2}})$$ -(25) - -# 3. 쉘 구조물의 점근거동 - -stress condition)을 적용하면 면에 수직인 응력은 0이며 (σ33 = 0) 이때 응력과 변형률의 상관관계는 다음과 같다. (22a) (22b) 위의 식 (22)에서 (23a) (23b) 여기서 E는 재료의 탄성계수(elastic modulus)이고 v는 포 아손비(Poisson's ratio)이며 는 식 (14)의 3차원 반변 (contravariant)기저벡터로 정의된 metric텐서이다 . 쉘 구조물에 강체운동(rigid body motion)이 일어나지 않 도록 적절한 변위경계조건이 주어지면 식 (20)에서 (23)까지 를 이용하여 기본쉘수학모델(basic shell mathematical model) 의 지배변분식(variational equation)을 얻을 수 있다. 해를 구하는 과정은 모든 임의의 시험함수(test function) 에 대하여 다음 식 (24)와 변위 경계조건을 만족시키는 미지변위 를 찾는 것으로 나타내어질 수 있다. (24) 여기서 는 쉘 구조물에 작용하는 외력(external loading) 이며 시험함수는 변위경계조건을 만족시켜야 한다. (25) 휨(bending), 막(membrane), 면외전단(transverse shearing) 작용들은 쉘 구조물이 하중을 지지하는 기본적인 원리 (mechanism)이다. 그러므로 하중 재하 시 쉘 구조물은 휨, 막, 전단 에너지를 그 내부에 저장하게 된다. 쉘의 두께가 얇아짐에 따라 쉘의 전단 에너지는 무시할 만큼 작아지므로 쉘 구조물의 에너지는 주로 휨 에너지와 막 에너지에 의해 구성된다고 할 수 있다. 쉘은 그 두께가 얇아짐에 따라 특정한 한계거동 - 휨지배 (bending dominated)거동, 막지배(membrane dominated)거동, 혼합(mixed)지배거동 - 을 보이게 되며 이를 쉘의 점근거동 (asymptotic behavior)이라 한다. 쉘의 두께가 얇아짐에 따라 쉘 구조물이 주로 휨 거동에 의해 하중을 지탱할 경우 그 - -3.1 점근거동의 분류 - -U∈Ψ - -$$\varepsilon^3 A_b(\vec{U}, \vec{V}) + \varepsilon A_m(\vec{U}, \vec{V}) = \vec{F}(\vec{V}), \forall \vec{V} \in \vec{\Psi}$$ - (26. 여기서 $\varepsilon$ 는 쉘의 두께와 전체 쉘 구조물 크기의 비 $(t/L)$ $\varepsilon^3 A_b(\cdot,\cdot)$ 는 휨 에너지, $\varepsilon$ $A_b(\cdot,\cdot)$ 은 막 및 전단 에너지에 대응하는 겹선형식들(bilinear forms)이며, $\vec{U}$ 는 변위장의 해 $\vec{V}$ 는 시험함수, $\vec{\Psi}$ 는 Sobolev 공간(space) $^1$ , $\vec{F}(\cdot)$ 는 외력에 대응하는 선형식(linear form)을 나타낸다. 일반적으로 쉴의 두께가 얇을 때 전단 에너지는 막 에너지에 비해 매우작으므로 $\varepsilon$ $A_m$ 을 막 에너지에 대응하는 항이라고 부를 수 있다 - -ερ - -$$\vec{F}(\vec{V}) = \varepsilon^{\rho} \vec{G}(\vec{V}) \tag{27}$$ - -G Ψ′ Ψ - -$$1 \le \rho \le 3 \tag{28}$$ - -$$\overrightarrow{\Psi}_0 = \left\{ \overrightarrow{V} \in \overrightarrow{\Psi} \middle| A_m(\overrightarrow{V}, \overrightarrow{V}) = 0 \right\}$$ - (29) - -얇아짐에 따라 쉘 구조물이 휨과 막거동 두 가지 모두에 의 해 외력을 지지할 경우 혼합(mixed)지배 쉘 구조물이라 한 다. 쉘 구조물의 점근거동은 쉘의 형상(geometry), 경계조건 (boundary condition), 하중(loading)에 따라 달라진다(Lee and Bathe, 2002; Bathe, Chapelle and Lee, 2003; Chapelle and Bathe, 2003). 식 (24)에서 구해진 선형 쉘 이론의 변분식(variational form)을 두께(t)에 대하여 정리하여 t의 고차항을 제거하면 다음과 같이 간략화하여 나타낼 수 있다. Find such that (26) 여기서 ε 는 쉘의 두께와 전체 쉘 구조물 크기의 비(t/L), Ab(·,·)는 휨 에너지, ε Ab(·,·)은 막 및 전단 에너지에 대응 하는 겹선형식들(bilinear forms)이며, 는 변위장의 해, 는 시험함수, 는 Sobolev 공간(space)1 , 는 외력 에 대응하는 선형식(linear form)을 나타낸다. 일반적으로 쉘 의 두께가 얇을 때 전단 에너지는 막 에너지에 비해 매우 작으므로 ε Am을 막 에너지에 대응하는 항이라고 부를 수 있다. ε이 작아짐에 따른 쉘의 점근거동을 살펴 보기 위해 (ρ = load scaling factor)가 곱해진 외력(scaled loading)을 사용한다. (27) 여기서 는 ( 의 dual space2 )에 속하며 ρ는 실수이 다. 식 (26)의 좌변의 각 항은 ε 과 ε에 비례하므로 ρ는 1보다 크거나 같고 3보다 작거나 같은 실수임을 알 수 있다. (28) 다음의 공간(space)은 쉘의 점근거동에 중요한 역할을 한다. (29) 공간은 순수 휨(pure bending)을 나타내는 변위의 공간 이며 막 및 전단 에너지를 0으로 만들 수 있는 모든 변위 의 형태들(patterns)을 포함한다. 이 공간(space)이 단지 모든 변위를 0으로 하는 변위의 형태들만을 가지고 있을 때 쉘 구조물에서 순수 휨거동이 구속되었다고 하며 그러한 쉘을 순수 휨이 구속된 쉘(inhibited shell)이라 한다. 반면에 쉘이 모든 변위가 0인 형태(pattern)가 아닌 순수 휨 모드(mode) 를 가지고 있을 경우 그 쉘 구조물을 순수 휨이 구속되지 않은 쉘(non-inhibited shell) 구조물이라 한다. 쉘의 점근거 Ψ0 - -탄성문제에서 변위경계조건을 만족시키는 Sobolev 공간은 으로 표기되며 정의는 다음과 같다. 1 - -쉘 구조물을 휨지배 쉘(bending dominated shell) 구조물이 라 부르며 막거동에 의해 하중을 지탱할 경우 막지배 쉘 (membrane dominated shell) 구조물이라 한다. 또한 두께가 여기서 이다(Bathe, 1996). H0 H0 1 ( ) Ω = V: V L2 ∈ Ω( ); ∂Vi ∂xj ------- L2 ∈ Ω( ); Vi =0 at prescribed displacement boundary ⎩ ⎭ ⎨ ⎬ ⎧ ⎫ ( ) Ω = V: Ω ∫ 3 Vi ( )2 dΩ<+ - -2 공간위의 모든 선형식 들의 공간을 의 dual space(쌍대공간)라고 부른며 로 표기한다. i = 1 Ψ F Ψ Ψ′ - -| 표 1. 쉘의 점근거동의 분류 | | | | | -|----------------------|--------------------------------------|-------------------------|--|--| -| | | | | | -| 경우 | 하중
순수 휨을 유발하는 외력 | 분류 | | | -| 순수 휨이 구속되지 | ∃
Ψ V∈ 0
such that
G V( )≠0 | (i) 휨지배거동 | | | -| 않은 쉘 구조물,
Ψ0≠{ }0 | 순수 휨을 유발하지 않는 외력 | (ii) 불안정한 막지배 또는 혼합지배거동 | | | -| | ,
G V( ) = 0

Ψ V∈ 0 | | | | -| | Admissible membrane loading | | | | -| 순수 휨이 구속된 | G∈Ψm′ | (iii) 막지배거동 | | | -| 쉘 구조물,
Ψ0 = 0{ } | Non-admissible membrane loading | | | | -| | G∉Ψm′ | (iv) 혼합지배거동 | | | - -Ψ0≠{ }0 - -U ∈Ψ0 - -$$A_{b}(\overrightarrow{U}, \overrightarrow{V}) = \overrightarrow{G}(\overrightarrow{V}), \ \forall \overrightarrow{V} \in \overrightarrow{\Psi}_{0}$$ - -$$\tag{30}$$ - -Ψ0= 0{ } Ψm Ψ - -U ∈Ψm - -![](_page_4_Picture_9.jpeg) - -그림 2. 쉘의 변형거동 - -$$A_{m}(\overrightarrow{U}^{m}, \overrightarrow{V}) = \overrightarrow{G}(\overrightarrow{V}), \forall \overrightarrow{V} \in \overrightarrow{\Psi}_{m}$$ - -$$(31)$$ - -G Ψm G∈Ψm′ - -$$\left| \overrightarrow{G}(\overrightarrow{V}) \right| \le c_{\sqrt{A_m(\overrightarrow{V}, \overrightarrow{V})}}, \ \forall \overrightarrow{V} \in \overrightarrow{\Psi}$$ - (32) - -G∈Ψm′ - - (31) (32) - -![](_page_4_Picture_16.jpeg) - -그림 3. 쉘의 변형형상과 유효응력분포에 나타난 경계층(boundary layer) - -3.2 쉘의 층과 특성길이 쉘의 응력/변형률/변위장들은 그 변화가 매끄러운 영역들 (smooth areas)과 그렇지 않은 여러 종류의 층들(layers)로 나누어진다. 층(layer)은 곡률(curvature)이나 두께의 변화와 같은 쉘의 형상의 변화, 적합하지 않은 변위경계조건 (incompatible boundary condition), 불규칙한 하중(irregular loading) 등에 의하여 유발된다(Lee and Bathe, 2002). 층 (layer)에서는 응력/변형률/변위 등이 매우 급하게 변하며 변 형에너지의 집중이 일어난다. 층의 특성길이(Lc, characteristic length)는 쉘 구조물의 두께에 따라 변하며 쉘의 두께(t) 와 전체 쉘 구조물 길이(L)의 함수로 나타나다. - -$$L_c = ct^{1-l}L^l \tag{33}$$ - -여기서 c는 상수이며 l은 양의 실수 이다. - -참고문헌(Lee and Bathe, 2002)는 쉘의 두께가 얇아짐에 따라 나타나는 Scodelis-Lo roof shell problem에서의 경계 층(boundary layer)과 쌍곡포물면(hyperbolic paraboloid) 쉘 구조문제의 내부층(inner layer)을 보여준다. 참고문헌(Bathe, Chapelle and Lee, 2003)에서는 또 다른 형태의 경계층이 쉘의 두께가 얇아짐에 따라 변화하는 것을 보여준다. 그림 3은 쉘의 변형형상(deformed shape)과 유효응력(effective stress)분포에 나타난 경계층(boundary layer)의 예를 보여주 고 있다. - -3.3 점근거동의 해석 이론적인 방법으로 일반적인 쉘 구조물의 점근거동을 알아 내려는 시도는 Lovadina를 비롯한 많은 연구자들에 의해 수 행되었으나 쉬운 일이 아니었다(Lovadina, 2001). 근래에 수 치적인 방법에 의해 점근거동을 알아내는 연구가 진행되었 으며 유한요소해석을 통한 여러 가지 방법이 Lee와 Bathe에 의해 제안되었다(Lee and Bathe, 2002; Bathe, Chapelle and Lee, 2003). 본 논문에서는 가장 손쉬운 한가지 방법을 소 개한다. - -쉘 구조물의 ρ(load scaling factor) 값을 구하게 되면 그 구조물의 점근거동을 알아낼 수 있다. 다음은 Lee와 Bathe 에 의해 제안된 ρ의 근사 값을 구하는 식이다. - -$$\rho \cong \frac{\log E(\varepsilon + \Delta \varepsilon) - \log E(\varepsilon)}{\log(\varepsilon + \Delta \varepsilon) - \log \varepsilon}$$ -(34) - -여기서 E는 ε(=t/L)에 대한 유한요소해석으로부터 구해진 쉘 구조물의 변형에너지(strain energy) 값이다. 주어진 ε 대하여 유한요소해의 정확도가 높을수록 보다 정확한 ρ값을 얻을 수 있다. 계산된 ρ의 값이 1일 경우 구조물은 막지배 거동을, 3일 경우 휨지배거동을, 1과 3의 중간 값일 경우 막과 휨의 혼합지배거동을 한다. - -Lovadina는 보간이론(interpolation theory)을 사용하여 쉘 구조물의 점근거동을 연구하였으며 ρ값과 쉘 구조물에 저장 되는 에너지들과의 관계를 나타내는 흥미로운 식을 제안하 였다(Lovadina, 2001). - -$$\lim_{\varepsilon \to 0} R(\varepsilon) = \frac{\rho - 1}{2} \tag{35}$$ - -여기서 R(ε)는 쉘 구조물의 전체 변형에너지에 대한 휨 변 - -형에너지의 비이다. - -$$R(\varepsilon) = \frac{\varepsilon^3 A_b(\vec{U}, \vec{U})}{\varepsilon^3 A_b(\vec{U}, \vec{U}) + \varepsilon A_m(\vec{U}, \vec{U})}$$ -(36) - -역으로 쉘 구조물의 R(ε)를 계산하면 또한 쉘의 점근거동을 찾을 수 있는 ρ값을 알 수 있다. - -Lee와 Bathe는 쉘 유한요소의 성능을 평가(benchmark)하 기 위한 쉘 문제로 널리 알려져 있는 Scodelis-Lo roof shell problem을 대상으로 하여 ρ값을 계산하였으며 그 쉘 구조물의 점근거동을 보여주었다(Lee and Bathe, 2002). 그 후에 Lovadina는 이론적인 방법을 이용해 같은 문제의 ρ 을 구하였고 두 결과는 일치하였다. 쉘 구조물의 휨지배거동 과 막지배거동에 대한 보다 자세한 예들을 참고문헌(Lee and Bathe, 2002)에서 볼 수 있으며, 쉘 두께의 변화에 따 ρ값이 변동(fluctuation)하는 민감한 쉘 구조물의 점근거 동에 대한 해석이 참고문헌(Bathe, Chapelle and Lee, 2003)에 제시되어 있다. - -# 4. 쉘 유한요소의 잠김현상과 균일최적수렴 - -쉘 구조물의 유한요소해석에서 직면한 가장 큰 어려움은 잠김현상(locking phenomenon)이다. 잠김현상은 쉘 구조물의 두께가 얇을 수록 유한요소해의 오차(error)를 매우 빠르게 증가시킨다. 본 장에서는 쉘의 점근거동에 대한 이해를 바탕 으로 쉘 유한요소의 잠김현상과 균일최적수렴에 대하여 살 펴본다. - -4.1 쉘 유한요소의 종류와 잠김현상 쉘 유한요소의 종류는 크게 평면 쉘(flat shell) 유한요소 그룹과 감절점 쉘(degenerated shell or continuum mechanics based shell) 유한요소 그룹으로 나뉘어진다(Ahmad, Iron and Zienkiewicz, 1970; Bathe, 1996; Choi, Lee and Park, 1999; 최창근, 2002; Chapelle and Bathe, 2003). 평면 쉘 유한요소는 평판 유한요소와 평면응력 유한요소의 결합에 의 해 만들어진다. 곡면의 쉘 구조물을 여러 개의 평면으로 나 누어 표현한다는 물리적 개념에서 출발하였다. 장점은 유한 요소 정식화가 쉽고 면내회전자유도(drilling degrees of freedom)를 쉽게 도입하여 절점 당 6개의 자유도를 가질 수 있으며 그로 인해 절점 당 6개의 자유도를 갖는 빔(beam) 과 같은 유한요소들과 결합이 편리하다는 것이다. 그러나, 기 본적으로 요소자체의 형상이 평면이기 때문에 복잡한 곡면 형태의 쉘의 형상과 그 거동을 정밀하게 표현하기 어려워 곡면 쉘 구조물의 해석 시 수렴이 느리며 정확해로의 수렴 에 실패하는 경우가 발생하는 단점을 가지고 있다. 반면 3 차원 연속체역학에 근거한 감절점 쉘 요소는 절점 당 5개의 자유도를 가지고 있어 빔과 같은 유한요소들과의 결합 시 특별한 인위적인 방법이 필요하지만 쉘 고유의 복잡한 곡면 형상과 그 거동을 잘 표현해 낼 수 있고 정확해로의 수렴속 도가 빠르다. 또한, 2장에서 설명된 바와 같이 가장 일반적 인 쉘 수학모델(mathematical shell model)인 기본쉘수학모 델에 근거한다. - -쉘 유한요소를 유한요소의 정식화(formulation)방법에 따라 - -분류하면 크게 변위법(displacement based method)에 의한 - -Ψ0 - -쉘 유한요소와 혼합법(mixed method)에 의한 쉘 유한요소로 나눌 수 있다. 변위법에 근거한 쉘 유한요소(displacement based shell finite element)는 막지배 구조물들의 해석에 있 어서는 이상적인 거동을 보인다. 그러나 요소의 종류와 근사 차수(interpolation order)에 상관없이 두께가 얇은 휨지배 (bending dominated) 또는 혼합지배(mixed) 쉘 구조물들의 해석에 있어서 유한요소해석의 해가 매우 느리게 수렴하는 치명적인 단점을 가지고 있는데 이를 잠김(locking)현상이라 고 한다. 주어진 요소망(mesh)에 대하여 유한요소해의 정확 성이 쉘의 두께가 얇아짐에 따라 급속히 나빠지는 현상을 말하며 최악의 경우 쉘의 두께가 얇아짐에 따라 변위장이 0 에 수렴하게 된다. 잠김현상은 막잠김(membrane locking)과 전단잠김(shear locking)현상으로 나누어질 수 있으며 잠김현상이 일어나는 근본적인 이유는 변위법에 의한 쉘 유한요소가 식 (29)의 순수 휨 변위공간(space) 을 충분히 근사할 수 없기 때 문이다. 즉, 잠김현상은 쉘 구조물의 점근거동과 직접적인 연 관관계를 가지고 있다. 쉘 구조물의 점근거동이 쉘의 형상, 경계조건, 하중 등에 따라 변하기 때문에 잠김현상 또한 쉘 의 형상, 경계조건, 하중에 의존적이다. 막잠김현상은 곡률을 가진 쉘 구조물에서만 발생하고 평면형상의 쉘 구조물에서 는 발생하지 않으나 전단잠김현상은 곡률에 관계없이 발생 한다. 잠김현상은 휨지배 및 혼합지배거동 시 식 (21a)와 (21d) 에 있는 변형률항들로부터 발생한다. 참고문헌(Bathe, Lee and Hiller, 2003)과 (Lee and Bathe, 2005)는 잠김현상이 유한요소해에 어떻게 나타나는지를 보여주고 잠김현상이 제 거되기 어려운 근본적인 이유를 잘 설명하고 있다. 실제 쉘 유한요소해석에 있어서 잠김현상은 과대한 강성 또는 그로 인한 과소한 변위, 응력, 변형률 및 변형에너지로 나타난다. 그러므로 두께가 얇은 쉘 구조물의 설계 시 충분 하게 조밀하지 못한 유한요소망을 사용하여 해석한 결과를 이용할 경우(강성이 과대평가되어) 과소설계의 오류를 범할 수 있다. 이와 같이 유한요소해의 오차 또는 정확도가 쉘 구조물의 두께에 따라 변하는 현상인 잠김현상은 쉘 구조물 의 유한요소해석에 있어서 빈번하게 일어나기 때문에 쉘 구 조물의 해석에 앞서 잠김현상에 대한 이해는 필수적이다. 4.2 유한요소해의 오차측정 신뢰할 만한 쉘 유한요소의 해는 사용된 요소의 수가 증 가함에 따라 2장에서 설명된 쉘 유한요소의 수학모델 (mathematical shell model)의 정확해(exact solution)에 수 렴하여야 하며 잠김현상을 알아보기 위해서는 쉘 유한요소 해의 수렴곡선(convergence curve)을 관찰해야 한다. 여기서 쉘 유한요소의 수렴을 측정하기 위하여 적절한 규준(norm) 을 사용하는 것이 매우 중요하다. 그 규준(norm)은 한 점에 서 특정 물리량 값(point-wise value)의 수렴이 아니라 쉘 구조물 전체 영역에서의 해의 수렴을 고려할 수 있어야 한 다. 일반적으로 한 점에서의 변위/응력/변형률 등을 가지고 수렴을 측정하는 방법이 많이 사용되어왔는데 이는 전체영 역에서의 수렴을 대표하지 않으므로 적절하지 못하다. 변형 - -⋅ s - -$$\left| \overrightarrow{U} - \overrightarrow{U}_h \right|_s^2 = \int_{\Omega} \Delta \overrightarrow{\varepsilon}^T \Delta \overrightarrow{\sigma} d\Omega \tag{37}$$ - -U Uh ε σ - -$$\overleftarrow{\mathcal{E}} = \left[ \varepsilon_{xx}, \ \varepsilon_{yy}, \ \varepsilon_{zz}, \ 2\varepsilon_{xy}, \ 2\varepsilon_{yz}, \ 2\varepsilon_{zx} \right]^{T}$$ -(38a) - -$$\vec{\sigma} = [\sigma_{xx}, \sigma_{yy}, \sigma_{zz}, \sigma_{xy}, \sigma_{yz}, \sigma_{zx}]^T$$ -(38b) - -$$\Delta \vec{\varepsilon} = \vec{\varepsilon} - \vec{\varepsilon}_h = \vec{\varepsilon}(\vec{x}) - \mathbf{B}_h \vec{x}_h) \vec{U}_h \tag{39a}$$ - -$$\Delta \vec{\sigma} = \vec{\sigma} - \vec{\sigma}_h = \vec{\sigma}(\vec{x}) - \mathbf{C}_h(\vec{x}_h) \mathbf{B}_h(\vec{x}_h) \vec{U}_h$$ - (39b) - -에너지의 산술적 차이 또한 규준으로 사용할 수 있지만 변 위법 정식화에 근거한 유한요소가 아닌 경우에는 일반적으 로 사용될 수 없기 때문에 정식화의 방법에 관계없이 일반 적으로 쓰일 수 있는 규준이 필요하다. Hiller와 Bathe에 의해 제안된 s-norm( )은 쉘 구조물 전체의 거동을 반영할 수 있을 뿐만 아니라 물리적인 개념 으로부터 유도되었기 때문에 정식화 방법에 관계없이 사용 될 수 있다(Hiller and Bathe, 2003). 여기서 는 정확해이며, 는 유한요소해이다. 와 는 전체직각좌표계(global Cartesian coordinate system)에서 정 의된 변형률 벡터와 응력 벡터이다. 식 (37)에서 변형률과 응력에 대하여 정확해와 유한요소해의 차이(difference)는 다음과 같이 구해질 수 있다. x xh - -$$\vec{x} = \Pi(\vec{x}_h) \tag{40}$$ - - (37) (38a) (38b) (39a) (39b) 여기서 C는 재료의 응력-변형률 관계 행렬(matrix)이고 B는 변형률-변위(strain-displacement)관계 연산자(operator)이 다. 위치벡터 와 는 각각 원래 쉘 구조물의 영역 (domain)과 이산화된 유한요소 모델의 영역에 대응된다. 두 벡터의 관계는 일대일 사상(injective mapping, Π)에 의하여 정의할 수 있다. (40) 일반적인 쉘 구조문제에 있어서 이론적인 방법을 이용하여 정확해를 찾아내는 것은 거의 불가능하므로 매우 조밀한 유 Uref - -$$\left| \overrightarrow{U}_{ref} - \overrightarrow{U}_h \right|_s^2 = \int_{\Omega_{ref}} \Delta \overrightarrow{\varepsilon}^T \Delta \overrightarrow{\sigma} d\Omega_{ref}, \tag{41}$$ - -여기서 - -$$\Delta \vec{\xi} = \vec{\xi}_{ref} - \vec{\xi}_h = \mathbf{B}_{ref}(\vec{x}_{ref}) \vec{U}_{ref} - \mathbf{B}_h(\vec{x}_h) \vec{U}_h, \qquad (42a)$$ - -$$\Delta \overrightarrow{\sigma} = \overrightarrow{\sigma}_{ref} - \overrightarrow{\sigma}_h = \mathbf{C}_{ref}(\overrightarrow{x}_{ref}) \mathbf{B}_{ref}(\overrightarrow{x}_{ref}) \overrightarrow{U}_{ref} - \mathbf{C}_h(\overrightarrow{x}_h) \mathbf{B}_h(\overrightarrow{x}_h) \overrightarrow{U}_h,$$ -(42b) - -$$\vec{x}_{ref} = \Pi(\vec{x}_h) \tag{42c}$$ - -한요소망을 사용하여 계산된 해를 정확해로 고려하여 snorm을 계산하는 것이 훨씬 실용적이다. 정확해로 고려된 유한요소해를 라고 하면 위식의 s-norm은 다음과 같이 나타내어질 수 있다. , (41) , (42a) , (42b) (42c) 다양한 쉘 구조문제와 여러 경우의 쉘 두께에 대하여 유한 요소해의 수렴을 상호 비교 가능하게 하기 위해서는 상대오 - -차(relative error)를 사용하여야 한다. 상대오차( -$$E_h$$ -)는 다음과 -같이 정의된다. -$$E_h = \frac{|\overrightarrow{U}_{ref} - \overrightarrow{U}_h|_s^2}{||\overrightarrow{U}_{ref}||_s^2} \tag{43}$$ - -![](_page_7_Figure_0.jpeg) - -그림 - -![](_page_7_Figure_2.jpeg) - -그림 - -유한요소해의 이론적인 수렴은 다음과 같다. - -$$E_h \cong ch^{2k} \tag{44}$$ - -여기서 c는 상수, h는 사용된 유한요소의 크기, k는 유한 요소의 변위 근사함수(displacement interpolation function) 차수이다. 예를 들면 선형(linear) 근사함수를 사용하는 3절 점 및 4절점 쉘 유한요소에 대하여 k = 1이며, 2차의 (quadratic) 근사함수를 사용하는 6절점 및 9절점 쉘 유한요 소에 대하여 k = 2이다. S-norm은 수치적인 방법에 의해 계 산될 수 있다(Lee, Noh and Bathe, 2007). 을 때-MITC4 쉘 요소 - -4.3 수렴곡선과 균일최적수렴 다음으로 2개의 휨지배 쉘 구조문제들의 예를 통하여 잠 김현상이 s-norm을 이용하여 구해진 수렴곡선에 어떻게 나 타나는지를 보여주고 균일최적수렴(uniform optimal convergence)에 대하여 알아본다. - -판(plate) 구조는 곡률이 영인 쉘 구조물로서 쉘 구조의 가장 단순한 형태이다. 첫 번째 예로 그림 4(a)에 보여진 네 변이 완전히 구속된 평판 휨(plate bending) 구조문제에 대하여 쉘 유한요소해의 수렴곡선을 살펴보자. 사용된 탄성 계수는 1.7472×107 , 포아손비는 0.3, 길이 L=1.0, 하중은 단위 면적당 90이 Z 방향으로 작용한다. 구조물의 형상, 하중, 변위경계조건의 대칭성으로 인해 그림 4(a)의 색칠된 부분만 해석되었다. 5. 두께의 변화에 따른 4절점 쉘 유한요소들의 수렴곡선: (a) 잠김현상이 일어날 때-QUAD4 쉘 요소 (b) 잠김현상이 일어나지 않 - -주어진 평판 휨 문제를 풀기 위하여 변위법(displacement based formulation)에 의한 4절점 감절점 쉘 유한요소 (QUAD4)와 혼합법(mixed formulation)에 의한 4절점 감절 점 쉘 유한요소(MITC43 )를 사용하였다(Ahmad, Irons and - -MITC(Mixed Interpolation of Tensorial Components) 방법을 이용하여 개발된 n절점 쉘 유한요소를 MITCn 유한요소라고 부른다. 즉, MITC4는 4절점, MITC6는 6절점 쉘 유한요소이다. - -![](_page_8_Figure_0.jpeg) - -그림 - -Zienkiewicz, 1970; Bathe and Dvorkin, 1989; Bathe, 1996). 각각의 경우 두께 변화에 따른 수렴곡선에서 잠김현상이 어 떻게 나타나는지를 살펴보자. - -그림 5는 두 가지 4절점 쉘 유한요소들에 대하여 쉘 두 께의 변화(t/L=1/10, 1/100, 1/1000, 1/10000)에 따른 수렴 곡선들(convergence curves)을 보여준다. 여기서 h는 사용된 유한요소의 크기를 나타내며 상대오차를 계산하기 위하여 snorm을 사용하였다. h와 상대오차에 log를 취함으로써 식 (44)의 이론적인 수렴률과 구해진 수렴곡선의 수렴률을 비교 할 수 있다. 그림 5의 각각의 그래프에 이론적인 수렴곡선 의 기울기(2k)가 두께가 굵은 직선으로 그려져 있다. - -그림 5(a)는 잠김현상이 일어날 때의 전형적인 수렴곡선들 이다. 쉘의 두께가 얇아짐에 따라 상대오차(relative error)가 급증하는 것을 알 수 있다. 반면에 그림 5(b)의 수렴곡선들 에서는 상대오차가 쉘 두께(t)에 영향을 받지 않고 오직 요 소의 크기 h에만 관계함을 알 수 있다. 또한 그림 5(b)에서 는 수렴곡선들이 이론적인 기울기와 같다. 그림 5(b)와 같은 형태의 쉘 유한요소해의 수렴형태를 균일최적수렴(uniform optimal convergence)이라고 한다. 때 - MITC6 쉘 요소 - -두 번째 예제로 그림 4(b)는 한 변이 구속된 쌍곡포물면 (hyperbolic paraboloid) 쉘 구조문제이다. 쉘의 중심면 (midsurface)은 다음과 같이 정의된다. - -$$\begin{pmatrix} X \\ Y \\ Z \end{pmatrix} = L \begin{pmatrix} \xi^1 \\ \xi^2 \\ (\xi^1)^2 - (\xi^2)^2 \end{pmatrix}; (\xi^1, \xi^2) \in \left[ -\frac{1}{2}, \frac{1}{2} \right]^2$$ - (45) - -경계조건은 X=−0.5인 변을 따라 완전히 구속되며 자중(selfweight)이 Z방향으로 작용한다. 구조물의 형상과 변위 및 경계조건이 Y=0인 면을 따라 대칭이므로 그림 4(b)의 색칠 된 부분만 해석되었다. 사용된 탄성계수는 2.0×1011, 포아손 비(Poisson's ratio)는 0.3, 길이 L=1.0이며 자중은 단위면적 당 80이다. - -주어진 휨지배문제를 풀기 위해 변위법(displacement - -based formulation)에 의한 6절점 삼각형 쉘 유한요소 (QUAD6)와 혼합법(mixed formulation)에 의한 6절점 삼각 형 쉘 유한요소(MITC6)를 사용하였다(Ahmad, Irons and Zienkiewicz, 1970; Bathe, 1996; Lee and Bathe, 2004). - -그림 6에서 t/L이 1/100, 1/1000, 1/10000인 세가지 경우 에 대하여 6절점 삼각형 쉘 유한요소들의 수렴곡선들 (convergence curves)을 보여준다. 각각의 그래프에 이론적인 수렴곡선의 기울기(2k)가 두께가 굵은 직선으로 그려져 있다. 그림 6(a)는 QUAD6 유한요소가 쉘의 두께가 얇아짐에 따 라 계산된 해의 오차가 늘어나는 경향, 즉, 잠김현상을 유발 한다는 것을 보여준다. 그림 6(b)에서는 MITC6 유한요소를 사용했을 때 잠김현상이 완전히 제거되지는 않으나 QUAD6 유한요소에 비하여 상당히 완화되었음을 나타낸다. - -여기서 우리는 두 가지 쉘 구조문제들을 대상으로 쉘 유 한요소해의 수렴곡선과 잠김현상에 대하여 알아보았다. 중요 한 점은 어떤 쉘 유한요소가 몇 가지 쉘 구조문제들에 대하 여 잠김현상을 일으키지 않는다고 해서 다른 쉘 구조문제들 에 대하여도 잠김현상을 유발시키지 않는다고 말할 수 없다 는 것이다. 즉, 몇 가지 쉘 구조문제들에 대하여 균일최적수 렴을 보이며 잠김현상을 일으키지 않는 유한요소도 다른 쉘 구조문제들에 대해서 잠김현상을 보일 수 있으며 모든 쉘 구조문제들에 대하여 잠김현상을 일으키지 않는 쉘 유한요 소를 개발하는 것은 극히 어렵다. 보다 다양한 쉘 구조문제 들에 대한 유한요소해의 수렴곡선에 대한 예는 참고문헌에 나와있는 Lee와 Bathe의 논문들에서 찾을 수 있다. 6. 두께의 변화에 따른 6절점 쉘 유한요소들의 수렴곡선: (a) 잠김현상이 일어날 때-QUAD6 쉘 요소 (b) 잠김현상을 완화시켰을 - -쉘 유한요소의 잠김현상을 알아보기 위해서는 다양한 형상 을 가진 휨 및 혼합지배 쉘 구조문제에 대하여 그림 5와 6 에 보여진 것과 같이 쉘의 두께를 변화시키며 수렴을 시험 하여야 한다. 또한 특정 쉘 요소가 휨지배문제에 좋은 거동 을 보인다고 해서 막지배문제에 대해서도 좋은 거동을 보이 는 것은 아니다. 결론적으로 다양한 형상의 쉘 구조문제들을 고려하여 휨과 막지배라는 두 가지 양단의 거동에서 이상적 인 수렴을 보이는 쉘 유한요소가 가장 바람직하고 이런 쉘 유한요소는 혼합지배 쉘 구조문제에 대하여도 좋은 거동을 보일 것이다. 즉, 이상적인 쉘 유한요소는 여러 가지 점근적 인 거동을 보이는 다양한 형상의 쉘 구조문제들에 대하여 균일최적수렴을 보여 주어야 한다. - -# 5. 쉘 유한요소의 성능평가에 대하여 - -나날이 셀 수 없을 만큼 많은 쉘 유한요소들이 개발되고 있지만 쉘 유한요소의 성능평가는 아직도 고전적인 방법을 통하여 이루어지고 있다. 본 장에서는 이상적인 쉘 유한요소 의 조건과 잠김현상을 완화시키는 방법들을 정리하고 쉘 유 한요소의 성능평가를 위한 방법론을 제시한다. - -5.1 이상적인 쉘 유한요소 유한요소 구조해석 문제는 다음과 같은 변분식(variational form)으로 나타내어질 수 있다. - -Find such that - -$$A_h(\overrightarrow{U}_h, \overrightarrow{V}_h) = \overrightarrow{F}(\overrightarrow{V}_h), \ \forall \overrightarrow{V}_h \in \overrightarrow{\Psi}_h, \tag{46}$$ - -선형식(bilinear form)이고 는 유한요소 변위장의 Sobolev 공간(space)이다. 물론 이 성립한다. Ψh Ψh⊂Ψ - -$$A_{h}(\overrightarrow{U}_{h}, \overrightarrow{V}_{h}) = \overrightarrow{V}_{h}^{T} \left( \int_{\Omega_{h}} \mathbf{B}_{h}^{T} \mathbf{C}_{h} \mathbf{B}_{h} d\Omega_{h} \right) \overrightarrow{U}_{h}$$ - -$$(47)$$ - -여기서 는 유한요소해(finite element solution), 는 유한요소 시험함수, 는 유한요소 변위장의 공간, 는 외력을 나타내는 선형식(linear form)이다. 쉘 유한요소해 석일 경우 식 (46)은 식 (26)의 형태로 표현될 수 있다. Ψh - -일반적인 쉘 구조물의 효과적인 유한요소해석에 쓰일 수 있는 이상적인 쉘 유한요소의 개발은 매우 어려운 일이다. 이상적인 쉘 유한요소의 조건을 다음과 같이 정리할 수 있다. - -여기서 Ah(·,·)는 유한요소법으로 이산화(discretization)된 겹 ● 첫째, 쉘 유한요소는 거짓영에너지모드(spurious zero energy mode)를 갖지 말아야 한다. 변위경계조건이 주어지 지 않은 임의의 형상을 갖는 개개의 쉘 유한요소는 물리적 인 강체운동에 대응하는 6개의 영에너지모드(zero energy mode)만을 가져야 한다. 이 조건을 ellipticity(타원율)라 하 며 다음과 같이 정의된다. Uh∈Ψh Ah( ) Uh, Vh = F V( )h , ∀Vh∈Ψh Ah( ) Uh, Vh = Vh dΩh ⎛ ⎞Uh - -$$\exists \alpha > 0 \text{ such that } \forall \overrightarrow{U}_h \in \overrightarrow{\Psi}_h, \ A_h(\overrightarrow{U}_h, \overrightarrow{U}_h) \ge \alpha |\overrightarrow{U}_h|_1^2$$ - (48) - -여기서 α는 상수이며 는 1차 Sobolev norm4 이다. ⋅ 1 - -이 조건은 쉘 유한요소 강성행렬(stiffness matrix)의 고유 치들(eigenvalues) 중 0인 개수와 그에 대응하는 고유벡터들 (eigenvectors)을 살펴봄으로써 시험될 수 있다. 탄성체는 강 체운동이 아닌 변위에 대하여 변형에너지를 저장한다. 식 (48)의 조건을 만족시키지 못하는 유한요소는 강체운동이 아 닌 변위에 대하여 변형에너지를 저장할 수 없으며 이는 물 리적으로 적합하지 못하다. Uh Vh F( ) ⋅ Uh∈Ψh Ah( ) Uh, Uh α Uh 1 - -● 둘째, 쉘 유한요소는 쉘 수학모델에 근거하였기 때문에 쉘 - -유한요소해석의 해는 주어진 쉘 구조문제에 대하여 사용된 증가함에 따라 쉘 수학모델의 정확해에 수렴해야 한다. 이 조건을 consistency(무모순성 또는 정합성)라 부르며 다음과 같이 정의된다. - -$$\lim_{h \to 0} \overrightarrow{U}_h = \overrightarrow{U} \quad \text{or} \quad \lim_{h \to 0} A_h(\overrightarrow{U}_h, \overrightarrow{U}_h) = A(\overrightarrow{U}, \overrightarrow{U})$$ - (49) - -bilinear form)이며 는 정확해(exact solution)이다. - -이 조건이 만족되지 않을 경우 쉘 유한요소의 해는 이론 해에 수렴하지 못 하므로 신뢰할 만한 결과를 줄 수 없다. - -요소의 크기(h)가 줄어듦에 따라 또는 사용된 요소의 수가 여기서 Ah(·,·)는 쉘 수학모델의 정확한 겹선형식(exact ● 셋째, 쉘 유한요소는 모든 종류의 휨 및 혼합지배 쉘 구조 문제에 대하여 균일최적수렴(uniform optimal convergence) 을 보여야 한다. 이 조건을 만족시키는 쉘 유한요소는 비 로소 쉘의 두께와 상관 없이 전단잠김과 막잠김으로부터 자유로운 유한요소가 된다. 이는 본 논문의 4장에서 설명 된 방법에 의해 시험될 수 있다. 혼합법에 의해 정식화 (mixed formulation)된 쉘 유한요소에 대하여 이 조건을 "inf-sup condition"이라 부른다(Bathe, 1996; Bathe, Iosilevich and Chapelle, 2000b). Uh = U h→0 Ah( ) Uh, Uh = A U( ) , U U - -위에서 언급된 세가지 조건을 모두 만족하는 이상적이 쉘 유한요소를 개발하는 것은 극히 어렵다. 실용적인 쉘 유한요 소해석에서는 보다 완화된 다음의 조건들을 만족시키는 쉘 요소의 사용이 권장된다(Lee and Bathe, 2004). - -- 거짓영에너지모드(spurious zero energy mode) 없음 (ellipticity조건 만족) -- Consistency조건 만족 -- 판 문제의 해석에 있어서 전단잠김 없음 -- − 막지배거동 쉘 구조물에 대하여 균일최적수렴 -- 범위(1/10~1/10000)에서 신뢰할 수 있는 결과 -- 비선형 해석에 있어서 효율적인 정식화 - -5.2 잠김현상의 제어 지난 수십 년 동안 쉘 유한요소의 잠김현상을 제어하기 위해 많은 방법들이 고안되어왔다. 그 방법들은 크게 세가지 로 나누어질 수 있다. - -- 식 (47)의 변형률-변위 관계 연산자 Bh를 변형하여 전 단 및 막 변형률장의 차수를 줄여주는 방법, (예) reduced integration, ANS method, MITC method -- 식 (47)의 Bh에 새로운 항을 추가하여 전단 및 막 변 형률장의 공간(space)을 늘려주는 방법, (예) EAS method -- 식 (46)에서 유한요소 변위장 의 공간( )을 늘려주 는 방법, (예) non-conforming method - -− 휨 및 혼합지배거동 쉘 구조물에 대하여 실용적인 t/L의 쉘 유한요소의 잠김현상을 완화시키기 위한 대부분의 방법 들은 위의 세 분류들(categories)에 속하며 세 방법들을 복합 적으로 이용한 예들도 있다. Uh Ψh - -가장 쉬운 방법은 감차적분(reduced integration)을 사용하 - -$$\|\vec{v}\|_{1}^{2} = \int_{\Omega} \sum_{i=1}^{3} (V_{i})^{2} d\Omega + \int_{\Omega} \sum_{i,j=1}^{3} \left(\frac{\partial V_{i}}{\partial x_{j}}\right)^{2} d\Omega$$ - -1차 Sobolev norm의 제곱은 다음과 같이 정의된다(Bathe, 1996). - -는 방법이다(Bathe, 1996). 그러나 이 방법은 거짓영에너지 모드(spurious zero energy mode)를 유발시키는 치명적인 단점을 지니고 있다. 이점을 극복하기 위하여 각종 안정화 (stabilization)기법이 사용된다. - -비적합모드(non-conforming or incompatible mode)를 추 가함으로써 요소의 휨 모드를 복원하여 쉘 유한요소의 잠김 현상을 완화할 수 있다. 이 방법은 요소간 변위의 적합성 (inter-elemental compatibility)을 만족시키지 못하는 단점과 최종 강성행렬을 구하기 위해 정적응축(static condensation) 을 사용하기 때문에 비선형 해석으로 확장 시 식이 복잡해 지는 단점을 가지고 있다(Choi, Lee and Park, 1999; 최창 근, 2002). - -변위와 변형률을 각각 따로 근사하는 혼합법(mixed formulation)에 근거한 방법들은 쉘 유한요소의 잠김현상을 완화시키는 알려진 방법들 중 가장 우수하다고 평가된다. 그 중 MITC(Mixed Interpolation of Tensorial Components) 방법은 이론적으로 잘 확립되어 있으며 다양한 수치실험으 로 잠김현상의 제어에 매우 효과적임이 입증되었다(Bathe and Dvorkin, 1989; Bathe, 1996; Bathe, Iosilevich and Chapelle, 2000a; Hiller and Bathe, 2003). 최근의 연구들 은 MITC방법에 의해 만들어진 사각형 쉘 유한요소들이 이 상적인 쉘 유한요소에 상당히 접근해 있음을 보여준다(Hiller and Bathe, 2003; Bathe, Lee and Hiller, 2003). - -MITC 방법에서는 변위법에 근거한 쉘 유한요소의 특정한 위치들(tying points)에서 공변변형률들(covariant strains)을 이용하여 원래 공변(covariant)변형률의 근사차수보다 낮은 차수로 변형률장을 근사(interpolation)한다. 일반적으로 근사 함수는 낮은 차수일수록 잠김현상 제어에 더 효과적이지만 너무 낮은 근사차수는 막지배거동을 하는 쉘 구조문제를 풀 때 유한요소해가 이론해에 수렴하지 못하는(즉, consistency 조건을 만족시키지 못하는) 현상을 유발시킨다. 심한 경우에 는 거짓영에너지모드를 발생시켜 ellipticity조건까지 만족시 킬 수 없게 만든다. 그러므로 MITC방법의 핵심은 휨 및 혼합지배거동 시 잠김현상을 줄여주면서 막지배거동 시 수 렴성을 유지하는(즉, consistency를 만족시키는) 균형 잡힌 변형률의 근사장을 찾아내는 것이다. - -대체변형률장(assumed strain field)을 사용하는 ANS (Assumed Natural Strain)방법은 MITC방법과 유사하며, EAS(Extended Assumed Strain)방법은 ANS 또는 MITC방 법에 추가적인 변형률장을 도입하여 변형률장이 표현 가능 한 형태들(patterns)의 수를 늘려주는 방법이다. EAS방법은 비적합모드를 사용하는 방법과 비슷하게 추가된 변형률장의 자유도를 제거하기 위해 정적응축을 필요로 한다는 단점을 가지고 있으며 기존의 ANS나 MITC방법에 비하여 쉘 유한 요소의 수렴성을 개선시킬 수 있지만 그 효과가 크지는 않 다고 알려져 있다. - -다른 여러 가지 방법을 사용하여 휨 및 혼합지배거동 쉘 구조문제에 대하여 보다 유연(flexible)한 거동을 하는 쉘 유 한요소를 개발하는 것은 어렵지 않디. 그러나 그와 동시에 consistency와 ellipticity조건들을 모두 만족시키는 것은 쉽지 않다. 앞으로 여러 종류의 개발된 쉘 유한요소에 대하여 쉘 이론에 바탕을 둔 심도 있는 성능시험(benchmark test)과 연구가 필요하다. - -5.3 쉘 유한요소의 성능평가 일반적으로 유한요소법을 사용하여 쉘 구조물을 해석하는 대부분의 기술자들(engineers)은 구하여진 해의 오차(error)에 대한 평가 없이 해석결과를 받아들이기 때문에 쉘 유한요소 를 개발하는 연구자들은 개발된 쉘 유한요소를 실제 해석에 사용하기에 앞서 성능을 평가하고 그 결과를 보고하여야 한 다. 개발된 쉘 유한요소의 오차특성이 명확하게 알려진다면 사용자들은 주어진 쉘 유한요소를 어떻게 올바르게 사용할 수 있을지를 판단할 수 있게 된다. 쉘 유한요소의 성능평가 는 다음에 열거된 사항들을 고려하여 이루어져야 한다. - -# ● 기본시험 - -쉘 유한요소는 표 2에 정리되어있는 기본시험들(basic tests)을 통과하여야 한다. 기본시험들을 통과하지 못하는 쉘 유한요소의 사용은 바람직하지 못하다. - -# ● 성능평가방법 - -쉘 유한요소의 성능을 평가하기 위해서는 다양한 쉘 구조 문제들이 사용되어야 한다. 쉘 유한요소들의 성능을 비교평 가하기 위하여 오래 전부터 많은 쉘 해석문제들이 제안되어 왔다. 현재까지 가장 널리 쓰이는 성능평가방법은 1985년에 MacNeal와 Harder에 의해 정리된 것으로 두께가 정해진 몇 가지 쉘 구조문제들을 해석하여 정해진 위치에서 구해진 변 위 및 응력/변형률 등의 결과치들을 유한요소망을 조밀화 하 면서 비교하는 것이다. 이미 언급한 바와 같이 몇몇 점들에 서의 해의 수렴을 측정하는 것은 전체 유한요소해의 거동을 올바르게 반영할 수 없다. 변위형상이나 응력/변형률의 분포 는 유한요소해의 전체적인 수렴정도를 보여줄 수 있으며 이 것들을 비교하는 것은 매우 좋은 보완방법이다. 그러나 이 방법으로 과연 유한요소해가 어느 정도 수렴했는지를 측정 하여 그 정도를 한 개의 값으로 보여주기는 대단히 어렵다. 그러므로, 응력과 변형률의 오차분포로부터 구해진 s-norm은 전체 유한요소해를 반영하는 좋은 오차측정의 규준이 된다. 또한 4.3절에서 설명한 바와 같이 두께의 변화를 고려한 성 - -| 표 2. 쉘 유한요소의 기본시험 | | | | -|----------------------------------------------------------------------|--------------------------|------------------------------------|--| -| 시험 | 대상 | 참고문헌 | | -| 영에너지 시험 (Zero
energy mode test) | 사각형 쉘 유한요소
삼각형 쉘 유한요소 | Bathe, 1996 | | -| 조각 시험 (Patch tests)
− Membrane patch test
− Bending patch test | 사각형 쉘 유한요소
삼각형 쉘 유한요소 | Bathe, 1996
Lee and Bathe, 2004 | | -| 요소 등방성 시험 (Element isotropy test) | 삼각형 쉘 유한요소 | Lee and Bathe, 2004 | | - -![](_page_11_Picture_0.jpeg) - -그림 7. Gaussian곡률에 따른 곡면의 종류: (a) Positive Gaussian curvature, (b) Zero Gaussian curvature, (c) Negative Gaussian curvature - - 3. 쉘 유한요소의 성능평가를 위한 쉘 구조문제의 예들(Bathe, Iosilevich and Chapelle, 2000; Lee and Bathe, 2002; Bathe, Chapelle and Lee, 2003; Bathe, Lee and Hiller, 2003; Chapelle and Bathe, 2003; Hiller and Bathe, 2003; Lee and Bathe, 2004; Lee and Bathe, 2005; Lee, Noh and Bathe, 2007) - -| 쉘 구조문제 (shell
problems) | Gaussian 곡률 | 점근거동 (ρ) | -|----------------------------------------------------|-------------|------------------| -| Fully clamped plate problem | Zero | 휨지배 (ρ = 3.0) | -| Scodelis-Lo roof shell problem | Zero | 혼합지배 (ρ = 1.75) | -| Modified Scodelis-Lo roof shell problem | Zero | 막지배 (ρ = 1.0) | -| Free cylindrical shell problem | Zero | 휨지배 (ρ = 3.0) | -| Fixed cylindrical shell problem | Zero | 막지배 (ρ = 1.0) | -| Clamped hemispherical cap problem | Positive | 막지배 (ρ = 1.0) | -| Monster shell problem | Positive | Not well-defined | -| Partly clamped hyperbolic paraboloid shell problem | Negative | 휨지배 (ρ = 3.0) | -| Free hyperboloid shell problem | Negative | 휨지배 (ρ = 3.0) | -| Fixed hyperboloid shell problem | Negative | 막지배 (ρ = 1.0) | - -능평가방법을 사용하는 것이 바람직하다. ● 층(layer) 쉘의 응력/변형률/변위장들이 급격히 변하는 층(layer)의 폭, 즉, 특성길이(characteristic length)는 쉘의 두께에 따라 변화 한다. 일반적으로 특성길이는 쉘의 두께가 얇아짐에 따라 식 (33)에 의하여 급격히 줄어든다. 층에서의 에너지 집중현상 때문에 이런 층이 발생하는 쉘 구조문제를 풀 때는 균일한 유한요소망을 사용하여 균일최적수렴을 얻기 힘들다. 각각 층의 특성길이를 반영한 유한요소망을 사용하여야 한다. 즉, 층이 발생하는 영역에서 보다 조밀한 유한요소망의 사용이 요구된다(Bathe, Iosilevich and Chapelle, 2000a). 이러한 유한요소망을 "graded mesh"라 부른다. ● 중심면의 곡률 쉘 구조물의 중심면은 곡률(curvature)을 가지고 있다. 곡 면은 Gaussian곡률의 부호에 따라 세가지로 나누어질 수 있 다. 특히 Gaussian곡률이 음인 곡면을 가지는 쉘 구조물은 유한요소해석에 있어서 상당한 어려움이 뒤따른다(Lee and Bathe 2004). 쉘 유한요소의 성능을 평가하기 위한 해석시 험문제들(benchmark test set)은 다양한 곡률을 고려하여 구 성되어야 한다. 이는 어떤 쉘 유한요소가 특정한 곡률을 가 지는 쉘 구조문제에서 좋은 수렴성을 보인다고 하여 다른 곡률을 가지는 구조문제에 대하여도 좋은 수렴을 보이는 것 은 아니기 때문이다. 그림 7은 Gaussian곡률에 따른 곡면의 예들을 보여주고 있다. - -# ● 점근거동의 종류 - -3장에서 우리는 쉘 구조물의 두께가 얇아짐에 따라 나타나 - -는 3가지 점근거동(휨지배, 막지배, 혼합지배거동)을 살펴 보 았다. 각각의 점근거동을 모두 시험할 수 있도록 해석시험문 제들을 구성해 주어야 한다. 특히, 휨지배 및 혼합지배거동 쉘 구조문제들에서는 잠김현상이 일어나는지를 시험하여야 하며 막지배거동 쉘 구조문제들에서는 5.1절에서 언급한 consistency조건이 만족되는지를 살펴보아야 한다. 표 3은 쉘 유한요소의 성능평가를 위한 쉘 구조문제의 예들을 보여주 고 있다. ● 요소망(mesh)의 형태 유한요소해석의 해는 유한요소망을 어떻게 구성하는지에 따라 그 수렴특성이 변화하게 된다. 요소형상의 찌그러짐에 민감하지 않고 좋은 수렴성을 유지하는 쉘 유한요소를 개발 하는 것은 쉽지 않은 일이다. 따라서 해석시험문제들은 다양 - -# 6. 결 론 - -한 유한요소망에 따른 쉘 유한요소의 수렴특성을 반영해 주 어야 한다. 특히, 비등방성(non-isotropic) 삼각형 쉘 유한요 소의 시험에서는 유한요소망의 형태뿐만 아니라 요소의 방 향에 따라 수렴특성이 변하므로 요소의 방향성 또한 고려되 어야 한다(Lee, Noh and Bathe, 2007). 머리말에서 언급된 바와 같이 쉘 구조물의 유한요소해석을 명확하게 이해하기 위해서는 쉘 구조물의 물리적 거동, 수학 모델 및 쉘 유한요소해석에 대한 이해가 동시에 체계적이고 심도 있게 이루어져야 한다. 본 논문에서는 이 세가지 부분 에 대한 이해와 이들이 서로 어떻게 유기적으로 관계를 맺 고 있는지를 최근 주요 연구들을 토대로 정리하여 고찰하였 고 이상적인 쉘 유한요소의 성질과 쉘 유한요소의 성능평가 방법을 제시하였다. - -본 논문에서는 대표적인 쉘 수학모델과 휨지배거동, 막지 배거동, 혼합지배거동 등으로 나누어지는 쉘 구조물의 점근 거동에 대한 기본적인 이론과 그 점근거동을 수치적으로 알 아내는 방법을 알아보았다. 또한 휨지배 및 혼합지배거동에 서 나타나는 쉘 유한요소의 잠김현상을 두께의 변화에 따른 수렴곡선을 통하여 고찰하였다. 마지막으로 이상적인 쉘 유 한요소의 조건과 잠김현상의 제어하는 방법을 알아보았고 쉘 유한요소의 성능평가방법을 제안하였다. - -쉘 구조물의 수학모델과 점근거동은 쉘의 물리적 거동을 이해하는데 핵심사항으로 쉘 구조물을 설계하는 기술자나 쉘 유한요소해석을 연구하는 연구자들이 명확하게 알아야 할 매 우 중요한 부분이다. 유한요소법을 이용하여 쉘 구조물을 해 석하기에 앞서 쉘 구조물의 점근거동과 그와 관련된 쉘 유 한요소의 감김현상에 대한 이해는 필수적이다. 통합적인 이 해의 바탕이 있을 때 신뢰할 만한 쉘 유한요소의 개발이 이 루어질 수 있으며 쉘 구조물의 유한요소해석을 통하여 얻어 진 결과를 정확하게 이해할 수 있다. - -# 감사의 글 - -글을 맺으며 본 논문에 소개된 기본개념들을 정립하고 정 리하는데 많은 가르침을 주신 MIT(Massachusetts Institute of Technology)의 Klaus-Jürgen Bathe 교수님과 KAIST(한 국과학기술원)의 최창근 교수님께 깊은 감사드립니다. - -# 참고문헌 - -- 최창근 (2002) 유한요소법. 테크노프레스. -- Ahmad, S., Irons, B.M., and Zienkiewicz, O.C. (1970) Analysis of thick and thin shell structures by curved finite elements. International Journal for Numerical Methods and Engineering, Vol. 2, pp. 419-451. -- Bathe, KJ. (1996) Finite Element Procedures. Prentice Hall: New Jersey. -- Bathe, K.J., Chapelle, D., and Lee, P.S. (2003) A shell problem 'highly sensitive' to thickness changes. International Journal for Numerical Methods and Engineering, Vol. 57, pp. 1039-1052. -- Bathe, K.J. and Dvorkin, E.N. (1989) A formulation of general - -- shell elements the use of mixed interpolation of tensorial components. International Journal for Numerical Methods and Engineering, Vol. 22, pp. 697-722. -- Bathe, K.J., Iosilevich, A., and Chapelle, D. (2000a) An evaluation of the MITC shell elements. Computers & Structures, Vol. 75, pp. 1-30. -- Bathe, K.J., Iosilevich, A., and Chapelle, D. (2000b) An inf-sup test for shell finite elements. Computers & Structures, Vol. 75, pp. 439-456. -- Bathe, K.J., Lee, P.S., and Hiller, J.F. (2003) Towards improving the MITC9 shell element. Computers & Structures, Vol. 81, pp. 477-489. -- Chapelle, D. and Bathe, K.J. (1998) Fundamental considerations for the finite element analysis of shell structures. Computers & Structures, Vol. 66, pp. 19-36, pp. 711-712. -- Chapelle, D. and Bathe, K.J. (2003) The finite element analysis of shells? fundamentals. Berlin:Springer-Verlag. -- Choi, C.K., Lee, P.S., and Park, Y.M. (1999) Defect-free 4-node flat shell element: NMS-4F element. Structural Engineering and Mechanics, Vol. 8, pp. 207-231. -- Hiller, J.F. and Bathe, K.J. (2003) Measuring convergence of mixed finite element discretizations: an application to shell structures. Computers & Structures, Vol. 81, pp. 639-654. -- Lee, P.S. and Bathe, K.J. (2002) On the asymptotic behavior of shell structures and the evaluation in finite element solutions. Computers & Structures, Vol. 80, pp. 235-255. -- Lee, P.S. and Bathe, K.J. (2004) Development of MITC isotropic triangular shell finite elements. Computers & Structures, Vol. 82, pp. 945-962. -- Lee, P.S. and Bathe, K.J. (2005) Insight into finite element shell discretizations by use of the basic shell mathematical model. Computers & Structures, Vol. 83, pp. 69-90. -- Lee, P.S. Noh, H.C., and Bathe, K.J. (2007) Insight into 3-node triangular shell finite elements: the effects of element isotropy and mesh patterns. Computers & Structures, Vol. 85, pp. 404- 418. -- Lovadina, C. (2001) Energy estimates for linear elastic shells. Computational Fluid and Solid Mechanics (Bathe KJ ed.), pp. 330- 331, Elsevier Science. -- MacNeal, R.H. and Harder, R.L. (1985) A proposed standard set of problems to test finite element accuracy. Finite Element in Analysis and Design, Vol. 1, pp. 3-20. -- Noh, H.C. (2006) Nonlinear behavior and ultimate load bearing capacity of reinforced concrete natural draught cooling tower shell. Engineering Structures, Vol. 28, pp. 399-410. - -(접수일: 2006.7.18/심사일: 2007.1.16/심사완료일: 2007.3.27) \ No newline at end of file diff --git a/docs/Paper/2007쉘구조물의유한요소해석에대하여/2007쉘구조물의유한요소해석에대하여_meta.json b/docs/Paper/2007쉘구조물의유한요소해석에대하여/2007쉘구조물의유한요소해석에대하여_meta.json deleted file mode 100644 index fe1b089..0000000 --- a/docs/Paper/2007쉘구조물의유한요소해석에대하여/2007쉘구조물의유한요소해석에대하여_meta.json +++ /dev/null @@ -1,1249 +0,0 @@ -{ - "table_of_contents": [ - { - "title": "\uc258 \uad6c\uc870\ubb3c\uc758 \uc720\ud55c\uc694\uc18c\ud574\uc11d\uc5d0 \ub300\ud558\uc5ec", - "heading_level": null, - "page_id": 0, - "polygon": [ - [ - 179.5458984375, - 96.0821533203125 - ], - [ - 428.49798583984375, - 96.0821533203125 - ], - [ - 428.49798583984375, - 126.5477294921875 - ], - [ - 179.5458984375, - 126.5477294921875 - ] - ] - }, - { - "title": "", - "heading_level": null, - "page_id": 0, - "polygon": [ - [ - 283.26416015625, - 222.4228515625 - ], - [ - 323.35693359375, - 222.4228515625 - ], - [ - 323.35693359375, - 231.4677734375 - ], - [ - 283.26416015625, - 231.4677734375 - ] - ] - }, - { - "title": "", - "heading_level": null, - "page_id": 0, - "polygon": [ - [ - 282.68310546875, - 366.73046875 - ], - [ - 322.77587890625, - 366.73046875 - ], - [ - 322.77587890625, - 376.59765625 - ], - [ - 282.68310546875, - 376.59765625 - ] - ] - }, - { - "title": "", - "heading_level": null, - "page_id": 0, - "polygon": [ - [ - 56.65283203125, - 505.693359375 - ], - [ - 99.06982421875, - 505.693359375 - ], - [ - 99.06982421875, - 515.560546875 - ], - [ - 56.65283203125, - 515.560546875 - ] - ] - }, - { - "title": "2. \uc258\uc758 \uc218\ud559\ubaa8\ub378(mathematical shell model)", - "heading_level": null, - "page_id": 1, - "polygon": [ - [ - 297.5, - 224.0673828125 - ], - [ - 508.64862060546875, - 224.0673828125 - ], - [ - 508.64862060546875, - 243.8607177734375 - ], - [ - 297.5, - 243.8607177734375 - ] - ] - }, - { - "title": "2.1 \uc258\uc758 \ud615\uc0c1 (shell geometry)", - "heading_level": null, - "page_id": 1, - "polygon": [ - [ - 306.2158203125, - 442.37890625 - ], - [ - 437.5341796875, - 442.37890625 - ], - [ - 437.5341796875, - 459.1614990234375 - ], - [ - 306.2158203125, - 459.1614990234375 - ] - ] - }, - { - "title": "2.2 \uc258\uc758 \ubcc0\ud615\uac70\ub3d9(shell kinematics)", - "heading_level": null, - "page_id": 2, - "polygon": [ - [ - 320.7421875, - 275.0478515625 - ], - [ - 469.9403381347656, - 275.0478515625 - ], - [ - 469.9403381347656, - 292.414306640625 - ], - [ - 320.7421875, - 292.414306640625 - ] - ] - }, - { - "title": "3. \uc258 \uad6c\uc870\ubb3c\uc758 \uc810\uadfc\uac70\ub3d9", - "heading_level": null, - "page_id": 3, - "polygon": [ - [ - 41.54541015625, - 448.95703125 - ], - [ - 153.859375, - 448.95703125 - ], - [ - 153.859375, - 469.6678771972656 - ], - [ - 41.54541015625, - 469.6678771972656 - ] - ] - }, - { - "title": "", - "heading_level": null, - "page_id": 3, - "polygon": [ - [ - 307.08740234375, - 136.49609375 - ], - [ - 389.01611328125, - 136.49609375 - ], - [ - 389.01611328125, - 146.36328125 - ], - [ - 307.08740234375, - 146.36328125 - ] - ] - }, - { - "title": "", - "heading_level": null, - "page_id": 5, - "polygon": [ - [ - 49.68017578125, - 35.7685546875 - ], - [ - 143.22998046875, - 35.7685546875 - ], - [ - 143.22998046875, - 44.8134765625 - ], - [ - 49.68017578125, - 44.8134765625 - ] - ] - }, - { - "title": "", - "heading_level": null, - "page_id": 5, - "polygon": [ - [ - 48.80859375, - 383.998046875 - ], - [ - 130.15625, - 383.998046875 - ], - [ - 130.15625, - 392.220703125 - ], - [ - 48.80859375, - 392.220703125 - ] - ] - }, - { - "title": "4. \uc258 \uc720\ud55c\uc694\uc18c\uc758 \uc7a0\uae40\ud604\uc0c1\uacfc \uade0\uc77c\ucd5c\uc801\uc218\ub834", - "heading_level": null, - "page_id": 5, - "polygon": [ - [ - 298.95263671875, - 296.015625 - ], - [ - 494.7788391113281, - 296.015625 - ], - [ - 494.7788391113281, - 317.523193359375 - ], - [ - 298.95263671875, - 317.523193359375 - ] - ] - }, - { - "title": "", - "heading_level": null, - "page_id": 5, - "polygon": [ - [ - 304.76318359375, - 426.755859375 - ], - [ - 448.28369140625, - 426.755859375 - ], - [ - 448.28369140625, - 436.623046875 - ], - [ - 304.76318359375, - 436.623046875 - ] - ] - }, - { - "title": "", - "heading_level": null, - "page_id": 6, - "polygon": [ - [ - 62.46337890625, - 601.4873046875 - ], - [ - 169.95849609375, - 601.4873046875 - ], - [ - 169.95849609375, - 610.5322265625 - ], - [ - 62.46337890625, - 610.5322265625 - ] - ] - }, - { - "title": "", - "heading_level": null, - "page_id": 7, - "polygon": [ - [ - 49.09912109375, - 701.392578125 - ], - [ - 164.72900390625, - 701.392578125 - ], - [ - 164.72900390625, - 711.259765625 - ], - [ - 49.09912109375, - 711.259765625 - ] - ] - }, - { - "title": "5. \uc258 \uc720\ud55c\uc694\uc18c\uc758 \uc131\ub2a5\ud3c9\uac00\uc5d0 \ub300\ud558\uc5ec", - "heading_level": null, - "page_id": 9, - "polygon": [ - [ - 42.12646484375, - 93.73828125 - ], - [ - 207.55979919433594, - 93.73828125 - ], - [ - 207.55979919433594, - 114.5255126953125 - ], - [ - 42.12646484375, - 114.5255126953125 - ] - ] - }, - { - "title": "", - "heading_level": null, - "page_id": 9, - "polygon": [ - [ - 49.970703125, - 210.0888671875 - ], - [ - 152.236328125, - 210.0888671875 - ], - [ - 152.236328125, - 219.1337890625 - ], - [ - 49.970703125, - 219.1337890625 - ] - ] - }, - { - "title": "", - "heading_level": null, - "page_id": 9, - "polygon": [ - [ - 306.2158203125, - 514.3271484375 - ], - [ - 387.5634765625, - 514.3271484375 - ], - [ - 387.5634765625, - 523.3720703125 - ], - [ - 306.2158203125, - 523.3720703125 - ] - ] - }, - { - "title": "", - "heading_level": null, - "page_id": 10, - "polygon": [ - [ - 322.4853515625, - 195.2880859375 - ], - [ - 432.8857421875, - 195.2880859375 - ], - [ - 432.8857421875, - 204.3330078125 - ], - [ - 322.4853515625, - 204.3330078125 - ] - ] - }, - { - "title": "\u25cf \uae30\ubcf8\uc2dc\ud5d8", - "heading_level": null, - "page_id": 10, - "polygon": [ - [ - 312.607421875, - 339.595703125 - ], - [ - 355.7040710449219, - 339.595703125 - ], - [ - 355.7040710449219, - 350.7003173828125 - ], - [ - 312.607421875, - 350.7003173828125 - ] - ] - }, - { - "title": "\u25cf \uc131\ub2a5\ud3c9\uac00\ubc29\ubc95", - "heading_level": null, - "page_id": 10, - "polygon": [ - [ - 313.76953125, - 397.9765625 - ], - [ - 372.8631591796875, - 397.9765625 - ], - [ - 372.8631591796875, - 408.6603088378906 - ], - [ - 313.76953125, - 408.6603088378906 - ] - ] - }, - { - "title": "", - "heading_level": null, - "page_id": 11, - "polygon": [ - [ - 41.8359375, - 427.9892578125 - ], - [ - 80.185546875, - 427.9892578125 - ], - [ - 80.185546875, - 437.0341796875 - ], - [ - 41.8359375, - 437.0341796875 - ] - ] - }, - { - "title": "", - "heading_level": null, - "page_id": 11, - "polygon": [ - [ - 41.54541015625, - 585.8642578125 - ], - [ - 102.55615234375, - 585.8642578125 - ], - [ - 102.55615234375, - 594.9091796875 - ], - [ - 41.54541015625, - 594.9091796875 - ] - ] - }, - { - "title": "\u25cf \uc810\uadfc\uac70\ub3d9\uc758 \uc885\ub958", - "heading_level": null, - "page_id": 11, - "polygon": [ - [ - 41.54541015625, - 759.7734375 - ], - [ - 113.18463134765625, - 759.7734375 - ], - [ - 113.18463134765625, - 2819.522216796875 - ], - [ - 41.54541015625, - 2819.522216796875 - ] - ] - }, - { - "title": "", - "heading_level": null, - "page_id": 11, - "polygon": [ - [ - 297.79052734375, - 527.89453125 - ], - [ - 386.69189453125, - 527.89453125 - ], - [ - 386.69189453125, - 537.76171875 - ], - [ - 297.79052734375, - 537.76171875 - ] - ] - }, - { - "title": "6. \uacb0 \ub860", - "heading_level": null, - "page_id": 11, - "polygon": [ - [ - 300.3599853515625, - 688.6474609375 - ], - [ - 345.6795959472656, - 688.6474609375 - ], - [ - 345.6795959472656, - 708.9478759765625 - ], - [ - 300.3599853515625, - 708.9478759765625 - ] - ] - }, - { - "title": "\uac10\uc0ac\uc758 \uae00", - "heading_level": null, - "page_id": 12, - "polygon": [ - [ - 153.10791015625, - 336.5798034667969 - ], - [ - 198.2592010498047, - 336.5798034667969 - ], - [ - 198.2592010498047, - 357.3751525878906 - ], - [ - 153.10791015625, - 357.3751525878906 - ] - ] - }, - { - "title": "\ucc38\uace0\ubb38\ud5cc", - "heading_level": null, - "page_id": 12, - "polygon": [ - [ - 155.5800018310547, - 435.80078125 - ], - [ - 195.8594207763672, - 435.80078125 - ], - [ - 195.8594207763672, - 454.8751525878906 - ], - [ - 155.5800018310547, - 454.8751525878906 - ] - ] - } - ], - "page_stats": [ - { - "page_id": 0, - "text_extraction_method": "pdftext", - "block_counts": [ - [ - "Span", - 145 - ], - [ - "Line", - 62 - ], - [ - "Text", - 11 - ], - [ - "SectionHeader", - 4 - ], - [ - "PageHeader", - 2 - ], - [ - "Footnote", - 2 - ], - [ - "PageFooter", - 2 - ] - ], - "block_metadata": { - "llm_request_count": 0, - "llm_error_count": 0, - "llm_tokens_used": 0, - "previous_text": "", - "previous_type": "", - "previous_order": 0 - } - }, - { - "page_id": 1, - "text_extraction_method": "pdftext", - "block_counts": [ - [ - "Span", - 139 - ], - [ - "Line", - 67 - ], - [ - "Text", - 7 - ], - [ - "SectionHeader", - 2 - ], - [ - "PageFooter", - 2 - ], - [ - "Figure", - 1 - ], - [ - "Caption", - 1 - ], - [ - "FigureGroup", - 1 - ] - ], - "block_metadata": { - "llm_request_count": 0, - "llm_error_count": 0, - "llm_tokens_used": 0, - "previous_text": "", - "previous_type": "", - "previous_order": 0 - } - }, - { - "page_id": 2, - "text_extraction_method": "pdftext", - "block_counts": [ - [ - "Span", - 896 - ], - [ - "Line", - 243 - ], - [ - "Equation", - 26 - ], - [ - "Text", - 24 - ], - [ - "PageFooter", - 2 - ], - [ - "SectionHeader", - 1 - ] - ], - "block_metadata": { - "llm_request_count": 0, - "llm_error_count": 0, - "llm_tokens_used": 0, - "previous_text": "", - "previous_type": "", - "previous_order": 0 - } - }, - { - "page_id": 3, - "text_extraction_method": "pdftext", - "block_counts": [ - [ - "Span", - 592 - ], - [ - "Line", - 171 - ], - [ - "Text", - 17 - ], - [ - "Equation", - 11 - ], - [ - "SectionHeader", - 2 - ], - [ - "PageFooter", - 2 - ] - ], - "block_metadata": { - "llm_request_count": 0, - "llm_error_count": 0, - "llm_tokens_used": 0, - "previous_text": "", - "previous_type": "", - "previous_order": 0 - } - }, - { - "page_id": 4, - "text_extraction_method": "pdftext", - "block_counts": [ - [ - "Span", - 272 - ], - [ - "Line", - 85 - ], - [ - "TableCell", - 27 - ], - [ - "Text", - 10 - ], - [ - "Caption", - 3 - ], - [ - "Equation", - 3 - ], - [ - "Picture", - 2 - ], - [ - "PageFooter", - 2 - ], - [ - "PictureGroup", - 2 - ], - [ - "Table", - 1 - ], - [ - "TableGroup", - 1 - ] - ], - "block_metadata": { - "llm_request_count": 0, - "llm_error_count": 0, - "llm_tokens_used": 0, - "previous_text": "", - "previous_type": "", - "previous_order": 0 - } - }, - { - "page_id": 5, - "text_extraction_method": "pdftext", - "block_counts": [ - [ - "Span", - 309 - ], - [ - "Line", - 107 - ], - [ - "Text", - 14 - ], - [ - "SectionHeader", - 4 - ], - [ - "Equation", - 4 - ], - [ - "PageFooter", - 2 - ] - ], - "block_metadata": { - "llm_request_count": 0, - "llm_error_count": 0, - "llm_tokens_used": 0, - "previous_text": "", - "previous_type": "", - "previous_order": 0 - } - }, - { - "page_id": 6, - "text_extraction_method": "pdftext", - "block_counts": [ - [ - "Span", - 434 - ], - [ - "Line", - 125 - ], - [ - "Text", - 13 - ], - [ - "Equation", - 11 - ], - [ - "PageFooter", - 2 - ], - [ - "SectionHeader", - 1 - ] - ], - "block_metadata": { - "llm_request_count": 0, - "llm_error_count": 0, - "llm_tokens_used": 0, - "previous_text": "", - "previous_type": "", - "previous_order": 0 - } - }, - { - "page_id": 7, - "text_extraction_method": "pdftext", - "block_counts": [ - [ - "Span", - 103 - ], - [ - "Line", - 38 - ], - [ - "Text", - 6 - ], - [ - "Figure", - 2 - ], - [ - "Caption", - 2 - ], - [ - "PageFooter", - 2 - ], - [ - "FigureGroup", - 2 - ], - [ - "Equation", - 1 - ], - [ - "SectionHeader", - 1 - ], - [ - "Footnote", - 1 - ] - ], - "block_metadata": { - "llm_request_count": 0, - "llm_error_count": 0, - "llm_tokens_used": 0, - "previous_text": "", - "previous_type": "", - "previous_order": 0 - } - }, - { - "page_id": 8, - "text_extraction_method": "pdftext", - "block_counts": [ - [ - "Span", - 227 - ], - [ - "Line", - 89 - ], - [ - "Text", - 10 - ], - [ - "PageFooter", - 2 - ], - [ - "Figure", - 1 - ], - [ - "Caption", - 1 - ], - [ - "Equation", - 1 - ], - [ - "FigureGroup", - 1 - ] - ], - "block_metadata": { - "llm_request_count": 0, - "llm_error_count": 0, - "llm_tokens_used": 0, - "previous_text": "", - "previous_type": "", - "previous_order": 0 - } - }, - { - "page_id": 9, - "text_extraction_method": "pdftext", - "block_counts": [ - [ - "Span", - 420 - ], - [ - "Line", - 136 - ], - [ - "Text", - 19 - ], - [ - "ListItem", - 9 - ], - [ - "Equation", - 5 - ], - [ - "SectionHeader", - 3 - ], - [ - "PageFooter", - 2 - ], - [ - "ListGroup", - 2 - ], - [ - "Footnote", - 1 - ] - ], - "block_metadata": { - "llm_request_count": 0, - "llm_error_count": 0, - "llm_tokens_used": 0, - "previous_text": "", - "previous_type": "", - "previous_order": 0 - } - }, - { - "page_id": 10, - "text_extraction_method": "pdftext", - "block_counts": [ - [ - "Span", - 224 - ], - [ - "Line", - 98 - ], - [ - "TableCell", - 13 - ], - [ - "Text", - 10 - ], - [ - "SectionHeader", - 3 - ], - [ - "PageFooter", - 2 - ], - [ - "Caption", - 1 - ], - [ - "Table", - 1 - ], - [ - "TableGroup", - 1 - ] - ], - "block_metadata": { - "llm_request_count": 0, - "llm_error_count": 0, - "llm_tokens_used": 0, - "previous_text": "", - "previous_type": "", - "previous_order": 0 - } - }, - { - "page_id": 11, - "text_extraction_method": "pdftext", - "block_counts": [ - [ - "Span", - 233 - ], - [ - "Line", - 87 - ], - [ - "TableCell", - 33 - ], - [ - "Text", - 7 - ], - [ - "SectionHeader", - 5 - ], - [ - "Caption", - 2 - ], - [ - "PageFooter", - 2 - ], - [ - "Picture", - 1 - ], - [ - "Table", - 1 - ], - [ - "PictureGroup", - 1 - ], - [ - "TableGroup", - 1 - ] - ], - "block_metadata": { - "llm_request_count": 0, - "llm_error_count": 0, - "llm_tokens_used": 0, - "previous_text": "", - "previous_type": "", - "previous_order": 0 - } - }, - { - "page_id": 12, - "text_extraction_method": "pdftext", - "block_counts": [ - [ - "Span", - 175 - ], - [ - "Line", - 85 - ], - [ - "ListItem", - 20 - ], - [ - "Text", - 5 - ], - [ - "SectionHeader", - 2 - ], - [ - "PageFooter", - 2 - ], - [ - "ListGroup", - 2 - ] - ], - "block_metadata": { - "llm_request_count": 0, - "llm_error_count": 0, - "llm_tokens_used": 0, - "previous_text": "", - "previous_type": "", - "previous_order": 0 - } - } - ], - "debug_data_path": "debug_data\\2007\uc258\uad6c\uc870\ubb3c\uc758\uc720\ud55c\uc694\uc18c\ud574\uc11d\uc5d0\ub300\ud558\uc5ec" -} \ No newline at end of file diff --git a/docs/Paper/2007쉘구조물의유한요소해석에대하여/_page_11_Picture_0.jpeg b/docs/Paper/2007쉘구조물의유한요소해석에대하여/_page_11_Picture_0.jpeg deleted file mode 100644 index 1a7c2fc..0000000 Binary files a/docs/Paper/2007쉘구조물의유한요소해석에대하여/_page_11_Picture_0.jpeg and /dev/null differ diff --git a/docs/Paper/2007쉘구조물의유한요소해석에대하여/_page_1_Figure_9.jpeg b/docs/Paper/2007쉘구조물의유한요소해석에대하여/_page_1_Figure_9.jpeg deleted file mode 100644 index e1bb710..0000000 Binary files a/docs/Paper/2007쉘구조물의유한요소해석에대하여/_page_1_Figure_9.jpeg and /dev/null differ diff --git a/docs/Paper/2007쉘구조물의유한요소해석에대하여/_page_4_Picture_16.jpeg b/docs/Paper/2007쉘구조물의유한요소해석에대하여/_page_4_Picture_16.jpeg deleted file mode 100644 index 15edd9c..0000000 Binary files a/docs/Paper/2007쉘구조물의유한요소해석에대하여/_page_4_Picture_16.jpeg and /dev/null differ diff --git a/docs/Paper/2007쉘구조물의유한요소해석에대하여/_page_4_Picture_9.jpeg b/docs/Paper/2007쉘구조물의유한요소해석에대하여/_page_4_Picture_9.jpeg deleted file mode 100644 index 15ee9d2..0000000 Binary files a/docs/Paper/2007쉘구조물의유한요소해석에대하여/_page_4_Picture_9.jpeg and /dev/null differ diff --git a/docs/Paper/2007쉘구조물의유한요소해석에대하여/_page_7_Figure_0.jpeg b/docs/Paper/2007쉘구조물의유한요소해석에대하여/_page_7_Figure_0.jpeg deleted file mode 100644 index 95ad419..0000000 Binary files a/docs/Paper/2007쉘구조물의유한요소해석에대하여/_page_7_Figure_0.jpeg and /dev/null differ diff --git a/docs/Paper/2007쉘구조물의유한요소해석에대하여/_page_7_Figure_2.jpeg b/docs/Paper/2007쉘구조물의유한요소해석에대하여/_page_7_Figure_2.jpeg deleted file mode 100644 index 72d779d..0000000 Binary files a/docs/Paper/2007쉘구조물의유한요소해석에대하여/_page_7_Figure_2.jpeg and /dev/null differ diff --git a/docs/Paper/2007쉘구조물의유한요소해석에대하여/_page_8_Figure_0.jpeg b/docs/Paper/2007쉘구조물의유한요소해석에대하여/_page_8_Figure_0.jpeg deleted file mode 100644 index 6aca32f..0000000 Binary files a/docs/Paper/2007쉘구조물의유한요소해석에대하여/_page_8_Figure_0.jpeg and /dev/null differ diff --git a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis.md b/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis.md deleted file mode 100644 index 376edf3..0000000 --- a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis.md +++ /dev/null @@ -1,447 +0,0 @@ -# **A continuum mechanics based four-node shell element for general nonlinear analysis** - -Eduardo N. Dvorkin and Klaus-Jürgen Bathe - -*Department of Mechanical Engineering, Massachusetts Institute of Technology, Cambridge, MA 02139, USA (Received December 1983)* - -# ABSTRACT - -A new four-node (non-flat) general quadrilateral shell element for geometric and material non-linear analysis is presented. The element is formulated using threedimensional continuum mechanics theory and it is applicable to the analysis of thin and thick shells. The formulation of the element and the solutions to various test and demonstrative example problems are presented and discussed. - -### INTRODUCTION - -The finite element analysis of general shell structures has been a very active field of research for a large number of years14,29 . However, despite the fact that many different shell elements have already been proposed, the search for a shell element capable of representing the general nonlinear behaviour of shells with arbitrary geometry and loading conditions in an effective and reliable manner is still continuing very actively. - -During recent years it has become apparent that two approaches for the development of shell elements are very appropriate: (1) the use of simple elements, based on the discrete-Kirchhoff approach for the analysis of thin shells2,5-9; (2) the use of degenerated isoparametric elements in which fully three-dimensional stress and strain conditions are degenerated to shell behaviour2,3,5,7,17,19,24,29 . - -The latter approach has the advantage of being independent of any particular shell theory, and this approach was used by Bathe and Bolourchi3 to formulate a general shell element for geometric and material non-linear analysis. This element has been employed very successfully when used with 9 or, in particular, 16 nodes. However, the 16-node element is quite expensive, and although it is possible to use in some analyses only a few elements to represent the total structure (see later examples) in other analyses still a fairly large number of elements need by employed5 . - -Considering general shell analyses, much emphasis has been placed onto the development of a versatile, reliable and cost-effective 4-node shell element16' 17,22,28 . Such element would complement the above high-order 16-node element and may be more effective in certain analyses. The difficulties in the development of such element lie in that the element should be applicable in a reliable manner to - -thin and thick shells of arbitrary geometries for general non-linear analysis. - -The objective in this paper is to present a simple 4-node general shell element with the following properties: the element is formulated using three-dimensional stress and strain conditions without use of a shell theory; the element is applicable to thin and thick shells and can be employed to model arbitrary geometries; the element is applicable to the conditions of large displacements and rotations but small strains, and can be used effectively in materially non-linear analysis. - -The formulation of the element is quite simple and transparent, and the element has good predictive capability without containing spurious zero energy modes. - -In the next section of the paper we discuss some basic considerations with respect to the assumptions used, and in the following section we present the element formulation for non-linear analysis. The results obtained in numerical solutions that demonstrate the properties of the element are given in the final section. - -## BASIC CONSIDERATIONS - -The formulation of the 4-node shell element represents an extension of the shell element discussed previously2,3 , and we therefore use the same notation as in those references. Also, to focus attention onto some key issues of the formulation, we consider in this section only linear analysis conditions. - -The geometry of the element (see *Figure 1)* is described using2 : - -$${}^{l}x_{i} = \sum_{k=1}^{4} h_{k}{}^{l}x_{i}^{k} + \frac{r_{3}}{2} \sum_{k=1}^{4} a_{k}h_{k}{}^{l}V_{ni}^{k}$$ - (1) - -![](_page_0_Figure_18.jpeg) - -![](_page_1_Figure_1.jpeg) - -where the *hk(rl ,r2)* are the two-dimensional interpolation functions corresponding to node *k;* the *ri* are the natural coordinates; and 1 *xi =* Cartesian coordinates of any point in the element; = Cartesian coordinates of nodal point :=components of director vector at node *k* (which is not necessarily normal to the midsurface of the element); and *ak* is the shell thickness at node *k,* measured along the vector The left superscript is zero for the initial geometry of the element and is equal to 1 for the deformed element geometry. Note that the thickness of the element varies and the element is in general non-flat. - -The displacements of any particle with natural coordinates *ri* of the shell element in the stationary Cartesian coordinate system are: - -$$u_{i} = \sum_{k=1}^{4} h_{k} u_{i}^{k} + \frac{r_{3}}{2} \sum_{k=1}^{4} a_{k} h_{k} (-{}^{0}V_{2i}^{k} \alpha_{k} + {}^{0}V_{1i}^{k} \beta_{k})$$ - (2) - -where the are the nodal point displacements into the Cartesian coordinate directions, and the *ak* and *Bk* are the rotations of the director vector about the and axes (see *Figure 1).* - -A basic problem inherent in the use of the above interpolation of the displacements, and the derivation of the strain-displacement matrices therefrom, is that the element 'locks' when it is thin. This is due to the fact that with these interpolations the transverse shear strains cannot vanish at all points in the element, when it is subjected to a constant bending moment. Hence, although the basic continuum mechanics assumptions contain the Kirchhoff shell assumptions, the finite element discretization is not able to represent these assumptions rendering the element not applicable to the analysis of thin plates or shells2,5,7 . To solve this deficiency, various remedies based on selective and reduced integration have been proposed17,22,23 but there is still much room for more effective and reliable elements for general non-linear analysis. - -Considering our element formulation - because the problem lies in the representation of the transverse shear strains - we proceed to not evaluate these shear strains from the displacements in (2), but to introduce separate interpolations for these strain components. Since we consider non-flat shell elements, the separate interpolations are performed effectively in a convected coordinate system†. - -The choice of the interpolation for the transverse shear strain components is the key assumption in our element formulation, because adequate coupling between the element displacements and rotations must be introduced and the element should not exhibit any spurious zero energy modes. For our element we use (see *Figure 2):* - -$$\tilde{\varepsilon}_{13} = \frac{1}{2}(1+r_2)\tilde{\varepsilon}_{13}^{A} + \frac{1}{2}(1-r_2)\tilde{\varepsilon}_{13}^{C} -\tilde{\varepsilon}_{23} = \frac{1}{2}(1+r_1)\tilde{\varepsilon}_{23}^{D} + \frac{1}{2}(1-r_1)\tilde{\varepsilon}_{23}^{B}$$ -(3) - -Since the kinematic relations for the above shear strains are not satisfied using (3), we impose them using Lagrange multipliers2,27 to obtain, - -$$\Pi^* = \frac{1}{2} \int_{V} \tilde{\tau}^{ij} \tilde{\varepsilon}_{ij} \, dV + \int_{V} \lambda^{13} (\tilde{\varepsilon}_{13} - \tilde{\varepsilon}_{13}^{DI}) \, dV + \int_{V} \lambda^{23} (\tilde{\varepsilon}_{23} - \tilde{\varepsilon}_{23}^{DI}) \, dV - \mathcal{H}^{c} \tag{4}$$ - -where the are the contravariant components of the Cauchy stress tensor13,15 , the are the covariant components of the infinitesimal strain tensor, the are the Lagrange multipliers, the are the transverse shear strains evaluated using the displacement interpolations in (2), and is the potential of the external loads. For the Lagrange multipliers we choose the following interpolations, - -$$\lambda^{13} = \lambda^{A} \delta(r_1) \delta(1 - r_2) + \lambda^{C} \delta(r_1) \delta(1 + r_2)$$ - -$$\lambda^{23} = \lambda^{D} \delta(r_2) \delta(1 - r_1) + \lambda^{B} \delta(r_2) \delta(1 + r_1)$$ -(5) - -where <5(...) is the Dirac-delta function. This represents a weakening of the Lagrange multiplier constraint in (4)10 . Substituting from (5) into (4) and invoking that gives the distinct constrains: - -$$\tilde{\varepsilon}_{13}\Big|_{\text{at A}} = \tilde{\varepsilon}_{13}^{\text{DI}}\Big|_{\text{at A}} \qquad \tilde{\varepsilon}_{13}\Big|_{\text{at C}} = \tilde{\varepsilon}_{13}^{\text{DI}}\Big|_{\text{at C}} -\tilde{\varepsilon}_{23}\Big|_{\text{at D}} = \tilde{\varepsilon}_{23}^{\text{DI}}\Big|_{\text{at D}} \qquad \tilde{\varepsilon}_{23}\Big|_{\text{at B}} = \tilde{\varepsilon}_{23}^{\text{DI}}\Big|_{\text{at B}} \tag{6}$$ - -Hence, the complete element stiffness matrix is calculated using the functional: - -$$\Pi^* = \frac{1}{2} \int \tilde{\tau}^{ij} \tilde{\epsilon}_{ij} \, \mathrm{d}V - \mathcal{H}^{\hat{r}} \tag{7}$$ - - Note that in refs. 2 and 3, the shell element formulation is discussed in the global stationary coordinate system, because all displacement components are interpolated in the same way. To emphasize that we use here stress and strain measures in the convected coordinate system, we place a tilde (~) over these quantities. - -![](_page_2_Figure_1.jpeg) - -with stress and strain components in convected coordinates and (1) and (2) to evaluate the strain components (3) to evaluate the strain components ; and (6) to express the variables in terms of the nodal point displacements and rotations of (2). - -Considering the representation that we have chosen for the transverse shear strains, we can make the following three important observations: - -- (1) *The element is able to represent the six rigid body modes.* The element contains the rigid body modes because zero strains are calculated in the formulation when the element nodal point displacements and rotations correspond to an element rigid body displacement. This can be verified by using (1) to (6) to evaluate the strains, but more easily we can use the fact that the 4-node shell element of reference 3 satisfies the rigid body mode criterion. Hence, for a rigid body displacement the are zero, from which it follows that also the shear strains in (3) are zero, and the rigid body mode criterion is satisfied. -- (2) *The element can approximate the Kirchhoff-Love hypothesis of negligible shear deformation effects and can be used for thin shells.* Various demonstrative solutions are given in the fourth section. -- (3) *Based on our studies the element does not contain any spurious zero energy modes* (*using a 'full' numerical integration).* We reach this observation by studying the strains along the element sides. If the element were to contain a spurious zero energy mode, the strains along every side should vanish for a displacement pattern (to be identified) other than the displacements corresponding to a true rigid body mode. However, such displacement pattern could not be identified. - -Considering the practical use of the element the interpolation employed for the transverse shear strains shows that is constant with *r*1 and in general discontinuous at (between elements), and similarly is constant with r2 and in general discontinuous at As a consequence, the accuracy with which transverse shear stresses are predicted depends to a significant degree on the mesh used and the geometric distortions of the elements. However, our experience is that the bending stress predictions are relatively little affected by element distortions (see examples). - -To employ (7), we also need to use the appropriate constitutive relations: - -$$\tilde{\tau}^{ij} = \tilde{C}^{ijkl} \tilde{\varepsilon}_{kl} \tag{8}$$ - -where is the fourth-order contravariant constitutive tensor in the convected coordinates r*i* . The constitutive law is known in the local Cartesian system of orthonormal base vectors *i =* 1,2,3, with the condition equal to zero2 , (see *Figure 3).* Denoting this constitutive tensor by the constitutive tensor for (8) is obtained using the transformation: - -$$\tilde{C}^{ijkl} = (\mathbf{g}^i \cdot \hat{\mathbf{e}}_m)(\mathbf{g}^j \cdot \hat{\mathbf{e}}_n)(\mathbf{g}^k \cdot \hat{\mathbf{e}}_0)(\mathbf{g}^l \cdot \hat{\mathbf{e}}_n)\hat{C}^{mnop} \tag{9}$$ - -where the gi are the contravariant base vectors of the convected coordinates *ri .* These vectors are calculated, using the covariant base vectors gi , where: - -$$\mathbf{g}_i = \frac{\partial^0 \mathbf{x}}{\partial r_i} \tag{10}$$ - -$$g_{ij} = \mathbf{g}_i \cdot \mathbf{g}_j \tag{11}$$ - -$$\mathbf{g}^{i} = g^{ij}\mathbf{g}_{j}$$ - -$$g^{ij} = \frac{D^{ij}}{\Pi^{2}}$$ -(12) - -where *Dij* is the cofactor of the term *gij* in the matrix of the metric tensor and |J| is the determinant of the Jacobian matrix at the point considered. - -# TOTAL LAGRANGIAN FORMULATION - -The large displacement formulation of the shell element is based on the derivation given in ref. 2 (Section 6.3.5), and the concepts and interpolations presented in the previous section. - -The geometry of the element at any time *t* is defined as in (1) but using the nodal point coordinates, and director vectors - -$${}^{t}x_{i} = h_{k}{}^{t}x_{i}^{k} + \frac{r_{3}}{2}a_{k}h_{k}{}^{t}V_{ni}^{k}$$ -(13) - -where we imply summation over *k.* The displacements, and incremental displacements, *ui* , of a particle of the element at time *t* are hence given by: - -$${}^{i}u_{i} = h_{k}{}^{i}u_{i}^{k} + \frac{r_{3}}{2}a_{k}h_{k}({}^{i}V_{ni}^{k} - {}^{0}V_{ni}^{k})$$ - -$$u_{i} = h_{k}u_{i}^{k} + \frac{r_{3}}{2}a_{k}h_{k}(-{}^{i}V_{2i}^{k}\alpha_{k} + {}^{i}V_{1i}^{k}\beta_{k})$$ - -$$(14)$$ - -where the are the nodal point displacements at time *t*, the are the incremental nodal point displacements from the configuration at time *t,* and the variables and *βk* are defined as in (2) but referred to the configuration at time *t.* - -This kinematic description implies the following hy- - - Note that the superscript *t* on a variable denotes the configuration at time *t* in the incremental solution, and does not imply a dynamic analysis. - -potheses: the director vectors remain straight during the deformations; the 'thickness' of the element measured along the director vectors remains constant during the deformations; hence only small strain conditions are considered. - -Using the assumptions in (13) and (14) the geometric and material non-linear response is analysed using an incremental formulation2 , in which the configuration is sought for time (load step) '*t* +∆*t',* when the configuration for time *t* is known. The basis of this incremental formulation is the use of the virtual work principle applied to the configuration at time *t + ∆t.* In essence, two approaches can be employed leading to the updated Lagrangian and the total Lagrangian formulations. These approaches are, from a continuum mechanics point of view, equivalent, and in the following we develop the governing finite element relations for the total Lagrangian formulation. - -The principle of virtual work applied to the configuration at time *t+At* is: - -$$\int_{0V}^{t+\Delta t} \tilde{S}^{ij} \delta^{t+\Delta t} \tilde{e}_{ij} \,^{0} \mathrm{d}V = {}^{t+\Delta t} \mathcal{R}$$ - (15) - -where the are the contra variant components of the second Piola-Kirchhoff stress tensor at time and referred to the configuration at time 0, and the are the covariant components of the Green-Lagrange strain tensor at time and referred to time 0. Both sets of tensor components are measured in the convected coordinate system / = 1,2,3. The external virtual work is given by and includes the work due to the applied surface tractions and body forces. - -For the incremental solution, the stresses and strains are decomposed into the known quantities, and unknown increments, so that - -$${}^{i+\Delta i}_{\alpha}\tilde{S}^{\bar{i}j} = {}^{i}_{\alpha}\tilde{S}^{\bar{i}j} + {}_{\alpha}\tilde{S}^{\bar{i}j} \tag{16}$$ - -$${}^{t+\Delta t}\circ\tilde{\varepsilon}_{i,i} = {}^{t}\circ\tilde{\varepsilon}_{i,i} + {}^{\circ}\circ\tilde{\varepsilon}_{i,i} \tag{17}$$ - -In addition, the strain increment can be written as a linear part, and a non-linear part, hence - -$$_{0}\tilde{\varepsilon}_{ii} = _{0}\tilde{e}_{ii} + _{0}\tilde{\eta}_{ii} \tag{18}$$ - -Substituting from (16) to (18) into (15) and using the linearized expressions we obtain the linearized equation of motion: - -$$\int_{\delta_{V}} {0} \tilde{C}^{ijkl} {0} \tilde{e}_{kl} \delta_{0} \tilde{e}_{ij} {0} dV + \int_{\delta_{V}} {0} \tilde{S}^{ij} \delta_{0} \tilde{\eta}_{ij} {0} dV$$ - -$$= {0 \choose k} {0} \tilde{S}^{ij} \delta_{0} \tilde{e}_{ij} {0} dV$$ -(19) - -This equation is the basic equilibrium relation employed to develop the governing finite element matrices. For the actual solution of problems it is frequently important to use equilibrium iterations, but the finite element matrices and vectors used in these iterations can be derived directly from the matrices obtained using (19*) 2 .* Note that is now obtained using (9) with the condition =0, which implies the more natural condition only in the small strain case. - -The basic problem of the finite element discretization of (19) lies in expressing the strain terms of (19) in terms of the finite element interpolations. Using the definition of the Green-Lagrange strain components: - -$${}_{0}^{t}\tilde{\varepsilon}_{i,i} = \frac{1}{2} ({}^{t}\mathbf{g}_{i} \cdot {}^{t}\mathbf{g}_{i} - {}^{0}\mathbf{g}_{i} \cdot {}^{0}\mathbf{g}_{i}) \tag{20}$$ - -$${}_{0}\tilde{e}_{ii} = h_{k,i}{}^{t}\mathbf{g}_{i} \cdot \mathbf{u}_{k} + \frac{r_{3}}{2}a_{k}h_{k,i}(-\alpha_{k}{}^{t}\mathbf{g}_{i} \cdot {}^{t}\mathbf{V}_{2}^{k} + \beta_{k}{}^{t}\mathbf{g}_{i} \cdot {}^{t}\mathbf{V}_{1}^{k})$$ - (21a) - -$${}_{0}\tilde{\eta}_{ii} = \frac{1}{2}h_{k,i}h_{p,i}\mathbf{u}_{k} \cdot \mathbf{u}_{p} + \frac{r_{3}}{2}h_{k,i}h_{p,i}a_{p}(-\alpha_{p}{}^{t}\mathbf{V}_{2}^{p} \cdot \mathbf{u}_{k} + \beta_{p}{}^{t}\mathbf{V}_{1}^{p} \cdot \mathbf{u}_{k}) +$$ - -$$\frac{(r_{3})^{2}}{8}h_{k,i}h_{p,i}a_{k}a_{p}(-\alpha_{k}{}^{t}\mathbf{V}_{2}^{k}+\beta_{k}{}^{t}\mathbf{V}_{1}^{k})\cdot(-\alpha_{p}{}^{t}\mathbf{V}_{2}^{p}+\beta^{p}{}^{t}\mathbf{V}_{1}^{p})$$ -(i = 1,2) -(21b) - -$${}_{0}e_{12} = \frac{1}{2} [h_{k,2} \mathbf{g}_{1} \cdot \mathbf{u}_{k} + h_{k,1} \mathbf{g}_{2} \cdot \mathbf{u}_{k} + \frac{r_{3}}{2} h_{k,2} J_{k} (-\alpha_{k}^{i} \mathbf{V}_{2}^{k} \cdot {}^{i} \mathbf{g}_{1} + \beta_{k}^{i} \mathbf{V}_{1}^{k} \cdot {}^{i} \mathbf{g}_{1}) +$$ - -$$\frac{r_3}{2}h_{k,1}a_k(-\alpha_k{}^t\mathbf{V}_2^k \cdot {}^t\mathbf{g}_2 + \beta_k{}^t\mathbf{V}_1^k \cdot {}^t\mathbf{g}_2)]$$ - (22a) - -$$\frac{r_3}{2}h_{k,1}h_{p,2}a_p(-\alpha_p{}^t\mathbf{V}_2^p \cdot \mathbf{u}_k + \beta_p{}^t\mathbf{V}_1^p \cdot \mathbf{u}_k) +$$ - -$$\frac{r_3}{2}h_{k,1}h_{p,2}a_k(-\alpha_k{}^t\mathbf{V}_2^k\cdot\mathbf{u}_p+\beta_k{}^t\mathbf{V}_1^k\cdot\mathbf{u}_p)+$$ - -$$\frac{(r_3)^2}{4}h_{k,1}h_{p,2}a_ka_p(-\alpha_k{}^t\mathbf{V}_2^k+\beta_k{}^t\mathbf{V}_1^k) \cdot (-\alpha_p{}^t\mathbf{V}_2^p+\beta_p{}^t\mathbf{V}_1^p)]$$ - -Further, we obtain for the transverse shear strains, using (3) and (6): - -$$\begin{split} &_{0}\tilde{e}_{13} = \frac{1}{8}(1+r_{2})\left[{}^{t}g_{3i}^{A}(u_{i}^{1}-u_{i}^{2}) + \right. \\ &_{2}{}^{t}g_{1i}^{A}(-\alpha_{1}a_{1}{}^{t}V_{2i}^{1} + \beta_{1}a_{1}{}^{t}V_{1i}^{1} - \alpha_{2}a_{2}{}^{t}V_{2i}^{2} + \beta_{2}a_{2}{}^{t}V_{1i}^{2})\right] + \\ &_{3}{}^{t}(1-r_{2})\left[{}^{t}g_{3i}^{C}(u_{i}^{4}-u_{i}^{3}) + \frac{1}{2}{}^{t}g_{1i}^{C}(-\alpha_{4}a_{4}{}^{t}V_{2i}^{4} + \beta_{4}a_{4}{}^{t}V_{1i}^{4} - \alpha_{3}a_{3}{}^{t}V_{2i}^{3} + \beta_{3}a_{3}{}^{t}V_{1i}^{3})\right] \end{split}$$ - -$$\tilde{\eta}_{13} = \frac{1}{32}(1 + r_2) [(-\alpha_1 a_1^{\ t} V_{2i}^1 + \beta_1 a_1^{\ t} V_{1i}^1 - \alpha_2 a_2^{\ t} V_{2i}^2 + \beta_2 a_2^{\ t} V_{1i}^2)(u_i^1 - u_i^2)] + \frac{1}{32}(1 - r_2) [(-\alpha_4 a_4^{\ t} V_{2i}^4 + \beta_4 a_4^{\ t} V_{1i}^4 - \alpha_3 a_3^{\ t} V_{2i}^3 + \beta_3 a_3^{\ t} V_{1i}^3)]$$ -(23b) - -$$\begin{split} & _{0}\tilde{e}_{23} = \frac{1}{8}(1+r_{1})[^{t}g_{3i}^{D}(u_{i}^{1}-u_{i}^{4}) + \\ & \frac{1}{2}^{t}g_{2i}^{D}(-\alpha_{1}a_{1}^{t}V_{2i}^{1} + \beta_{1}a_{1}^{t}V_{1i}^{1} - \alpha_{4}a_{4}^{t}V_{2i}^{4} + \beta_{4}a_{4}^{t}V_{1i}^{4})] + \\ & \frac{1}{8}(1-r_{1})[^{t}g_{3i}^{B}(w_{i}^{2}-w_{i}^{3}) + \frac{1}{2}^{t}g_{2i}^{B}(-\alpha_{2}\alpha_{2}^{t}V_{2i}^{2} + \\ & \beta_{2}a_{2}^{t}V_{1i}^{2} - \alpha_{3}a_{3}^{t}V_{2i}^{3} + \beta_{3}a_{3}^{t}V_{1i}^{3})] \end{split}$$ - -$${}_{0}\tilde{\eta}_{23} = \frac{1}{32}(1+r_{1})[(-\alpha_{1}a_{1}{}^{t}V_{2i}^{1} + \beta_{1}a_{1}{}^{t}V_{1i}^{1} - \alpha_{4}a_{4}{}^{t}V_{2i}^{4} + \beta_{4}a_{4}{}^{t}V_{1i}^{4})(u_{i}^{1} - u_{i}^{4})] + \frac{1}{32}(1-r_{1})[(-\alpha_{2}a_{2}{}^{t}V_{2i}^{2} + \beta_{2}a_{2}{}^{t}V_{1i}^{2} - \alpha_{3}a_{3}{}^{t}V_{2i}^{3} + \beta_{3}a_{3}{}^{t}V_{1i}^{3})(u_{i}^{2} - u_{i}^{3})]$$ - -$$(24b)$$ - -Note that, since we assume the thickness of the shell to be constant, the strain through the element thickness is zero. - -The expressions in (21) to (24) are substituted into (19) which in the standard manner yields the linear strain incremental stiffness matrix the non-linear strain (or geometric) incremental stiffness matrix and the nodal point force vector in the finite element incremental equilibrium relations2 , - -$$\binom{t}{0}\mathbf{K}_{t} + \binom{t}{0}\mathbf{K}_{Nt}\mathbf{u} = t + \Delta t\mathbf{R} - \binom{t}{0}\mathbf{F} \tag{25}$$ - -The element matrices in (25) correspond to five degrees of freedom per node (see *Figure 1)* but in some applications it is convenient to use instead of three rotations about the global coordinate axes (see examples). In this case, we simply transform the matrices of (25) in the standard manner2 . - -# NUMERICAL TESTS AND EXAMPLE SOLUTIONS - -We have implemented our shell element in the ADINA computer program and have performed various numerical tests to study the predictive capabilities of the element. The following solutions were all obtained using 2x2 Gauss integration in the r3 =0 surface of the element, and 2 and 4 point Gauss integration in the r3 direction, for elastic and elastoplastic analyses, respectively. - -## *Some simple tests* - -As a first step to test the element, the eigenvalues of the stiffness matrices of undistorted and distorted elements were calculated. In all cases, as expected, the element displayed the six rigid body modes and no spurious zero energy modes. - -*Patch tests.* For the patch test2,18 the mesh shown in *Figure 4a* was used. In the first analysis *(Figure 4b)* the mesh was loaded with the constant moment indicated and a constant curvature (linear distribution of rotations) was obtained for both plate thicknesses in the two plate directions. The transverse displacements predicted by the model were, as expected, those of Kirchhoff-Love plate theory at nodes 7 and 8. - -In the second analysis *(Figure 4c)* the rotational degrees of freedom were deleted and the mesh was subjected to shear forces. As expected, for both plate thicknesses a linear distribution of transverse displacements was obtained. - -In the third analysis *(Figure 4d)* the mesh was subjected to an external twisting moment. In the thin plate analysis, constant curvatures were obtained in both plate directions and the transverse displacements agreed with the analytical thin plate theory solution. In the thick plate analysis, a slight non-symmetry in the displacement response (the third digit) was obtained due to the unsymmetric representation of the transverse shear deformations. This non-symmetry is not observed, if the shear deformations are suppressed (which corresponds to thin - -![](_page_4_Figure_13.jpeg) - -![](_page_4_Figure_15.jpeg) - -![](_page_4_Figure_17.jpeg) - -![](_page_4_Figure_19.jpeg) - -![](_page_5_Figure_1.jpeg) - -![](_page_5_Figure_3.jpeg) - -plate theory) by choosing a large value for the shear correction factor *k* (or when using rectangular elements in the mesh)2 . - -Finally, it should be noted that the patch test is of course passed for the three membrane stress states (Τ1 1 , *τ22* andτT12 constants). - -*Cantilever linear analyses.* A cantilever of unit width, thickness 0.1 and lengths lOand 100 was subjected to a tip bending moment. The structure was modelled using one single element and two distorted elements as shown in *Figure 5.* The results obtained in these analyses for the displacements and rotations at the cantilever tip and the stresses were those of Bernoulli beam theory. - -Next, the cantilever in *Figure 6a* was analysed for the transverse tip load shown. Using 4 equal size elements to idealize the cantilever, again good results were obtained when compared with beam theoretical results (see *Figure 6b* and *Table 1).* - -Finally, the elements modelling the cantilever were distorted as shown in *Figure 6c* for a thin and a thick cantilever. The results given in *Figure 6d* and *Table 2* show that the transverse displacements and normal bending stresses are almost insensitive to the element distortions. However, the calculated transverse shear stresses (not shown in the Figure) are not accurate. - -*Linear analyses of a simply-supported plate.* A simplysupported plate was considered for a static and a frequency analysis using a consistent mass matrix. To model one quarter of the plate the 4 x 4 mesh of equal elements *(Figure 7a)* was used. *Figure 7b* and *Tables 3* and *4* give a comparison of the numerically and analytically predicted results. The same plate was also analysed using the distorted element mesh also shown in *Figure 7a* and the results of *Figure 7b* and *Tables 3* and *4* were obtained. - -![](_page_5_Figure_13.jpeg) - -![](_page_5_Figure_15.jpeg) - -![](_page_5_Figure_17.jpeg) - -![](_page_5_Figure_19.jpeg) - -*Table 1* Cantilever tip transverse displacement: non-distorted meshes of *N* elements - -| 0.750 | -|-------| -| 0.984 | -| | - -*Table 2* Cantilever tip transverse displacements - -| Thickness | ηIpoint
B | ηIpoint
A | | -|-----------|--------------|--------------|--| -| 0.1 | 0.989 | 0.996 | | -| 2.0 | 1.0013 | 0.995 | | - -*η*) = (*u*3 distorted mesh)/(*u*3 non-distorted mesh) - -![](_page_6_Figure_1.jpeg) - -![](_page_6_Figure_3.jpeg) - -![](_page_6_Figure_4.jpeg) - -*Table 3* Non-dimensional displacements at centre of simplysupported plate: distorted and non-distorted meshes - -| Model | FEM/u3
thin plate
u3
at centre | | | | -|--------------------|-----------------------------------------|--|--|--| -| non-dist.
dist. | 0.995
0.992 | | | | -| | | | | | - -*Table 4* Non-dimensional frequencies *f* (cycles/sec) for a simplysupported plate: distorted and non-distorted meshes - -| Mode shape | FEM/fthin plat e
f | | | | -|------------|-----------------------|--|--|--| -| 1-1 | 1.02 | | | | -| 1-3 | 1.18 | | | | -| 3-3 | 1.17 | | | | - -*Analysis of a rhombic cantilever.* The rhombic cantilever shown in *Figure 8,* fixed at one side and subjected to constant pressure was analysed using a 4x 4 element mesh. In *Table 5,* the results for the transverse displacements at six locations are compared against the solutions obtained using the DKT triangular element6 , experimental measurements1 and using the 16-node isoparametric element (with 4x4x 2 Gauss integration). In all cases a one step geometric non-linear analysis with equilibrium iterations was performed. Good correspondence between the experimental results and the solution obtained using our new 4-node element is observed. - -# *Linear analysis of a cylindrical (Scordelis-Lo) shell* - -The shell structure shown in *Figure 9a* has frequently been used to test the performance of shell elements12 . *Figure 9b* shows the solutions obtained with our elements. In each of the solutions uniform meshes with equal sized elements were employed over one-quarter of the shell. Solutions obtained using the 3-node DKT triangular element25 and the 16-node isoparametric element25 are also shown. - -### *Linear analysis of a pinched cylinder* - -The pinched cylinder problem shown in *Figure 10a* was also frequently analysed to test shell elements. *Figure 10b* and *Tables 6* and 7 show the convergence behaviour obtained with our new element, when comparing the finite element solutions11' 21 . Note that using the isoparametric shell element3 also a fairly large number of degrees of freedom are required to predict the response of the cylinder accurately. - -# *Large deflection analysis of a cantilever* - -The cantilever shown in *Figure 11a* was analysed for its large displacement and large rotation response. This is a typical problem considered to test the geometric nonlinear behaviour of beam and shell elements25 . *Figure 11a* also shows the models used in the analysis. - -The first two models are single element, cubic and parabolic isoparametric degenerate shell element models. Model I predicts the response of the cantilever very accurately, whereas model II yields an accurate response solution in linear analysis but locks once the element is curved in the non-linear response solution. This observation is in accordance with the results reported elsewhere5 . - -The same nodal point layouts were next employed for models IN and IV using our new 4-node shell element. *Figures 11b-11d* give the results obtained with these models. It is seen that model III yields an accurate large displacement response prediction, and even model IV yields quite accurate results up to about 60 degrees of rotation. The computer time required in these analyses were only little different using models I, III and IV. - -Another important result is shown in *Table 8.* As reported earlier5 , the cubic shell element is sensitive to 'in-plane' distortions, and hence it is interesting to study the effect of using a distorted element mesh in the analysis of the cantilever (see *Figures 12a* and *12b). Table 8* summarizes the results obtained using the one cubic element and three 4-node elements with a nodal layout that corresponds to distorting the elements. It is seen that the predictive capability of our new 4-node element is considerably less sensitive to the element distortions. - -![](_page_7_Figure_1.jpeg) - -*Figure 8* Response of rhombic cantilever subjected to constant pressure. q = 0.26066; £=10.5x106 ; thickness = 0.125; v = 0.3 - -*Table 5* - -| Element | | CPU time | Deflection at location | | | | | | | -|---------------|------|-----------------|------------------------|-------|-------|-------|-------|-------|--| -| | Mesh | CPU time of DKT | 1 | 2 | 3 | 4 | 5 | 6 | | -| DKT | 4x4 | 1.00 | 0.293 | 0.196 | 0.114 | 0.118 | 0.055 | 0.024 | | -| 4-node | 4 x4 | approx. 2 | 0.272 | 0.183 | 0.106 | 0.102 | 0.046 | 0.019 | | -| 16-node | 2x2 | approx. 61/2 | 0.266 | 0.182 | 0.110 | 0.105 | 0.048 | 0.019 | | -| Experimental1 | | | 0.297 | 0.204 | 0.121 | 0.129 | 0.056 | 0.022 | | - -![](_page_7_Picture_6.jpeg) - -![](_page_7_Figure_7.jpeg) - -*Geometric non-linear response of a shallow spherical shell* - -*Figure 13a* shows the spherical shell that was also analysed3 with one cubic shell element, modelling onequarter of the shell. To test our new 4-node shell element, the same nodal point layout was used3 , giving a mesh of nine elements. *Figure 13b* shows the response calculated, including the post-buckling response (not reported in ref. 3) with the automatic load stepping algorithm4 . Good correspondence with the analytical solution of Leicester2 . 0 and the solution of Horrigmoe16 was obtained. The solution with the 16-node element was almost twice as expensive as the 4-node element solution (using in both cases the same parameters for the automatic step-by-step solution algorithm). - -*Linear buckling analysis and large deflection response of a simply-supported stiffened plate* - -The stiffened plate shown in *Figure 14a* was analysed for its buckling reesponse. Since we expect the buckling mode to be symmetric26 only one-quarter of the plate is modelled using symmetry boundary conditions. The model consists of nine 4-node shell elements and three 2 node isoparametric beam elements. At the nodes where a shell element connects to a beam element, three rotational degrees of freedom aligned with the global axes are considered for the shell element. In order to avoid locking of the isoparametric beam elements, one point Gauss integration along the beam axes was used. This does not introduce spurious zero energy modes in the model although the bending stiffness of the beam is underestimated. - -The linearized buckling problem was solved as described in reference 4(37) and we obtained: - -$$\frac{\sigma_{\rm cr}(\text{finite element solution})}{\sigma_{\rm cr}(\text{analytical solution})} = 1.02$$ - -84 Eng. Comput., 1984, Vol. 1, March - -![](_page_8_Picture_1.jpeg) - -![](_page_8_Figure_3.jpeg) - -*Table 6* Convergence study for 4-node element: pinched cylinder - -| Mesh for 1/8th of shell | Number of d.o.f. | FEM/Ŵc
analyt
Ŵc | -|-------------------------|------------------|------------------------| -| 5x5 | 130 | 0.51 | -| 10x10 | 510 | 0.83 | -| 20x20 | 2020 | 0.96 | - -*Table* 7 Comparison between displacements for 4-node and 16 node elements: pinched cylinder - -| Element | Mesh for 1/8th | Number of | FEM/Ŵcanalyt | | | -|---------|----------------|-----------|--------------|--|--| -| | of shell | d.o.f. | Ŵc | | | -| 4-node | 20x20 | 2020 | 0.96 | | | -| 16-node | 10x10 | 4530 | 0.98 | | | - -Next, an initial imperfection with the shape of the first buckling mode and a maximum amplitude of 1/5 of the plate thickness was introduced. *Figure 14b* shows the large deflection response of this model as calculated using the automatic load stepping scheme of reference 4 with a tight energy convergence tolerance. - -*Analysis of elastoplastic response of a circular plate* - -The thin circular plate shown in *Figure 15a* was analysed for its elastoplastic response, when subjected to a concentrated load at its centre. The plate is simply-supported with its edges restrained from moving in its plane. - -In a first solution, the plate model shown in *Figure 15a* was used to analyse the plate assuming small displacements (materially-non-linear-only conditions). *Figure 15c* shows that the theoretical collapse load is overestimated, but for the coarse mesh used, the predicted response is quite reasonable. - -In a second solution, large displacements and elastoplastic conditions were assumed and in this case the stiffening behaviour of the plate shown in *Figure 15c* was predicted. In order to have a comparison, also the model of five axisymmetric 8-node elements shown in *Figure 15b* was solved. *Figure 15c* shows that both models predict in essence the same response; however, in this case relatively little plasticity was developed for the range of displacements considered. - -#### CONCLUSIONS - -A new four-node non-flat general non-linear shell element has been presented with the following important element properties: (1) the element is formulated using threedimensional continuum mechanics theory; hence the use of the element is not restricted by application of a specific shell theory; (2) the element is reliable and has good predictive capability in the analysis of thick and thin shells; (3) the amount of computations required to calculate the element stiffness matrix are very closely those that are used in standard isoparametric formulations. The computer time used could be reduced considerably in elastic analysis by using analytical integration through the element thickness. - -In this paper we have presented the formulation and some applications of the element. The solution results obtained are most encouraging, but a formal mathematical convergence study of the element would be very valuable, and we are currently pursuing such research. - -Finally, it should be noted that the element presented here provides a very attractive basic formulation that could be extended to large strain analysis and analysis of composite shells. Also, the concepts applied here to formulate a 4-node element could equally well be employed in an effective manner to formulate higher-order shell elements. - -#### ACKNOWLEDGEMENTS - -We are grateful for the financial support by the U.S. Army contract no. DAAK11-82-K-0005 and the ADINA users group for this work. - -*Note added in proof* — We have just learned — and regret not to have known of it earlier — that R. H. MacNeal [J. *Nucl. Eng. Design,* 70, 3-12 (1982)] proposed a plate element for linear analysis that is very close to the element presented above. - -![](_page_9_Figure_1.jpeg) - -# REFERENCES - -- 1 Adini, A. Analysis of shell structures by the finite element method, *PhD Dissertation,* Department of Civil Engineering, University of California, Berkeley (1961) -- 2 Bathe, K. J. *Finite Element Procedures in Engineering Analysis,* Prentice-Hall, Englewood Cliffs, New Jersey (1982) -- 3 Bathe, K. J. and Bolourchi, S. A geometric and material nonlinear plate and shell element, *J. Comput. Struct.,* 11, 23-48 (1979) -- 4 Bathe, K. J. and Dvorkin, E. N. On the automatic solution of nonlinear finite element equations, *J. Comput. Struct.* 17, (5-6), 871-879 (1983) -- 5 Bathe, K. J.,Dvorkin, E. N. and Ho, L. W. Our discrete-Kirchhoff and isoparametric shell elements for nonlinear analysis - an assessment, *J. Comput. Struct.,* 16, (1-4), 89-98 (1983) - -86 Eng. Comput., 1984, Vol. 1, March - -![](_page_10_Picture_1.jpeg) - -![](_page_10_Figure_5.jpeg) - -![](_page_10_Figure_6.jpeg) - -![](_page_10_Figure_8.jpeg) - -- 6 Bathe, K. J. and Ho, L. W. A simple and effective element for analysis of general shell structures, *J. Comput. Struct.,* 13, 673-382 (1980) -- 7 Bathe, K. J. and Ho, L. W. Some results in the analysis of thin shell structures, *Nonlinear Finite Element Analysis in Structural Mechanics,* (Ed. W. Wunderlich *et al.).* Springer-Verlag, Berlin (1981) -- 8 Batoz, J. L., Bathe, K, J. and Ho, L. W. A study of three-node triangular plate bending elements, *Int. J, Num. Meth. Eng.,* 15, 1771-1812(1980) -- 9 Batoz, J. L. and Ben Tahar, M. Evaluation of a new quadrilateral plate bending element, *Int. J, Num. Meth. Eng.,* 18, 1655-1677 (1982) -- 10 Bercovier, M., Hasbgni, V., Gilon, Y, and Bathe, K, J, On a finite element procedure for nonlinear incompressible elasticity, *Hybrid and Mixed Finite Element Methods,* (Ed, S. M. Atluri *et al.),* John Wiley, New York (1983) -- 11 Flügge, W. *Stresses in Shells,* 2nd edn, Springer-Verlag, Berlin (1973) -- 12 Forsberg, K. and Hartung, R. An evaluation of finite difference and finite element techniques for analysis of general shells, *Symp. High Speed Computing of Elastic Structures,* IUTAM, Liege (1970) -- 13 Fung, Y. C. *Foundations of Solid Mechanics,* Prentice-Hall, Englewood Cliffs, New Jersey (1965) -- 14 Gallagher, R. H. Problems and progress in thin shell finite element analysis, *Finite Elements in Thin Shells and Curved Members,* (Ed. D. G. Ashwell and R. H. Gallagher), John Wiley, New York (1976) -- 15 Green, A. E. and Zerna, W. *Theoretical Elasticity,* 2nd edn, Oxford University Press (1968) -- 16 Horrigmoe, G. Finite element instability analysis of free-form shells, *Report 77-2,* Division of Structural Mechanics, The Norwegian Institute of Technology, University of Trondheim, Norway (1977) -- 17 Hughes, T. J. R. and Liu, W. K. Nonlinear finite element analysis of shells: Part I, Three-dimensional shells, *J. Comput. Meth. Appl. Mech. Eng., 26,* 331-362 (1981) - -![](_page_11_Figure_1.jpeg) - -![](_page_11_Figure_2.jpeg) - -![](_page_11_Figure_3.jpeg) - -- 19 Krakeland, B. Nonlinear analysis of shells using degenerate isoparametric elements, *Finite Elements in Nonlinear Mechanics,* Vol. 1, (Ed. P. G. Bergan *et al*.), Tapir Publishers (Norwegian Institute of Technology, Trondheim, Norway) (1978) -- 20 Leicester, R. H. Finite deformations of shallow shells, *Proc. Am. Soc. Civil Eng.,* 94, (EM6), 1409-1423 (1968) -- 21 Lindberg, G. M., Olson, M. D. and Cowper, G. R. New developments in the finite element analysis of shells, *Q. Bull. Div. Mech. Eng. and the National Aeronautical Establishment,* National Research Council of Canada, Vol. 4 (1969) -- 22 MacNeal, R. H. A simple quadrilateral shell element, *J. Comput. Struct.* 8, 175-183 (1978) -- 23 Noor, A. K. and Peters, J. M. Mixed models and reduced/selec- - -![](_page_11_Figure_9.jpeg) - -tive integration displacement models for nonlinear analysis of curved beams, *Int. J. Num. Meth. Eng.,* 17, 615-631 (1981) - -- 24 Ramm, E. and Sattele, J. M. Elasto-plastic large deformation shell analysis using degenerated elements, *Nonlinear Finite Element Analysis of Plates and Shells,* (Ed. T. J. R. Hughes), AMD-Vol. 48, Am. Soc. Mech. Eng., New York (1981) -- 25 *Report AE 83-5,* ADINA System Verification Manual, ADINA Engineering, Vasteras, Sweden and Watertown, Mass. (1983) -- 26 Timoshenko, S. P. and Gere, J. M. *Theory of Elastic Stability,* 2nd edn, McGraw-Hill, New York (1961) -- 27 Washizu, K. *Variational Methods in Elasticity and Plasticity,* Pergamon Press, Oxford and New York (1968) -- 28 Wempner, G., Talaslidis, D. and Hwang, C.-M. A simple and efficient approximation of shells via finite quadrilateral elements, *J. Appl. Mech.,* 49, 115-120 (1982) -- 29 Zienkiewicz, O. C. *The Finite Element Method,* McGraw-Hill, New York (1977) \ No newline at end of file diff --git a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis_meta.json b/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis_meta.json deleted file mode 100644 index 2701528..0000000 --- a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis_meta.json +++ /dev/null @@ -1,1002 +0,0 @@ -{ - "table_of_contents": [ - { - "title": "A continuum mechanics \nbased four-node shell \nelement for general non-\nlinear analysis", - "heading_level": null, - "page_id": 0, - "polygon": [ - [ - 41.10205078125, - 43.580078125 - ], - [ - 265.0740051269531, - 43.580078125 - ], - [ - 265.0740051269531, - 121.834228515625 - ], - [ - 41.10205078125, - 121.834228515625 - ] - ] - }, - { - "title": "ABSTRACT", - "heading_level": null, - "page_id": 0, - "polygon": [ - [ - 43.43408203125, - 256.546875 - ], - [ - 101.08760833740234, - 256.546875 - ], - [ - 101.08760833740234, - 268.6048583984375 - ], - [ - 43.43408203125, - 268.6048583984375 - ] - ] - }, - { - "title": "INTRODUCTION", - "heading_level": null, - "page_id": 0, - "polygon": [ - [ - 43.142578125, - 371.95489501953125 - ], - [ - 129.7071075439453, - 371.95489501953125 - ], - [ - 129.7071075439453, - 382.8448791503906 - ], - [ - 43.142578125, - 382.8448791503906 - ] - ] - }, - { - "title": "BASIC CONSIDERATIONS", - "heading_level": null, - "page_id": 0, - "polygon": [ - [ - 306.37060546875, - 273.814453125 - ], - [ - 437.2473449707031, - 273.814453125 - ], - [ - 437.2473449707031, - 284.92486572265625 - ], - [ - 306.37060546875, - 284.92486572265625 - ] - ] - }, - { - "title": "TOTAL LAGRANGIAN FORMULATION", - "heading_level": null, - "page_id": 2, - "polygon": [ - [ - 308.11962890625, - 421.4111328125 - ], - [ - 505.4293212890625, - 421.4111328125 - ], - [ - 505.4293212890625, - 432.81170654296875 - ], - [ - 308.11962890625, - 432.81170654296875 - ] - ] - }, - { - "title": "NUMERICAL TESTS AND EXAMPLE \nSOLUTIONS", - "heading_level": null, - "page_id": 4, - "polygon": [ - [ - 42.779296875, - 321.53466796875 - ], - [ - 226.92030334472656, - 321.53466796875 - ], - [ - 226.92030334472656, - 344.1648864746094 - ], - [ - 42.779296875, - 344.1648864746094 - ] - ] - }, - { - "title": "Some simple tests", - "heading_level": null, - "page_id": 4, - "polygon": [ - [ - 43.0703125, - 432.30633544921875 - ], - [ - 125.77906036376953, - 432.30633544921875 - ], - [ - 125.77906036376953, - 443.30633544921875 - ], - [ - 43.0703125, - 443.30633544921875 - ] - ] - }, - { - "title": "Linear analysis of a cylindrical (Scordelis-Lo) shell", - "heading_level": null, - "page_id": 6, - "polygon": [ - [ - 301.70654296875, - 182.759765625 - ], - [ - 520.6527709960938, - 182.759765625 - ], - [ - 520.6527709960938, - 194.53167724609375 - ], - [ - 301.70654296875, - 194.53167724609375 - ] - ] - }, - { - "title": "Linear analysis of a pinched cylinder", - "heading_level": null, - "page_id": 6, - "polygon": [ - [ - 302.87255859375, - 295.5439453125 - ], - [ - 460.5027160644531, - 295.5439453125 - ], - [ - 460.5027160644531, - 306.3912353515625 - ], - [ - 302.87255859375, - 306.3912353515625 - ] - ] - }, - { - "title": "Large deflection analysis of a cantilever", - "heading_level": null, - "page_id": 6, - "polygon": [ - [ - 302.87255859375, - 405.8583984375 - ], - [ - 472.26123046875, - 405.8583984375 - ], - [ - 472.26123046875, - 417.271240234375 - ], - [ - 302.87255859375, - 417.271240234375 - ] - ] - }, - { - "title": "CONCLUSIONS", - "heading_level": null, - "page_id": 8, - "polygon": [ - [ - 303.6000061035156, - 346.43170166015625 - ], - [ - 382.0810546875, - 346.43170166015625 - ], - [ - 382.0810546875, - 356.3316955566406 - ], - [ - 303.6000061035156, - 356.3316955566406 - ] - ] - }, - { - "title": "ACKNOWLEDGEMENTS", - "heading_level": null, - "page_id": 8, - "polygon": [ - [ - 303.6000061035156, - 647.8717041015625 - ], - [ - 426.68652343749994, - 647.8717041015625 - ], - [ - 426.68652343749994, - 657.7716979980469 - ], - [ - 303.6000061035156, - 657.7716979980469 - ] - ] - }, - { - "title": "REFERENCES", - "heading_level": null, - "page_id": 9, - "polygon": [ - [ - 39.35302734375, - 686.17236328125 - ], - [ - 109.57891845703125, - 686.17236328125 - ], - [ - 109.57891845703125, - 697.7648773193359 - ], - [ - 39.35302734375, - 697.7648773193359 - ] - ] - } - ], - "page_stats": [ - { - "page_id": 0, - "text_extraction_method": "pdftext", - "block_counts": [ - [ - "Span", - 188 - ], - [ - "Line", - 93 - ], - [ - "Text", - 13 - ], - [ - "SectionHeader", - 4 - ], - [ - "PageFooter", - 4 - ], - [ - "Equation", - 1 - ], - [ - "Figure", - 1 - ], - [ - "Caption", - 1 - ], - [ - "FigureGroup", - 1 - ] - ], - "block_metadata": { - "llm_request_count": 0, - "llm_error_count": 0, - "llm_tokens_used": 0, - "previous_text": "", - "previous_type": "", - "previous_order": 0 - } - }, - { - "page_id": 1, - "text_extraction_method": "pdftext", - "block_counts": [ - [ - "Span", - 197 - ], - [ - "Line", - 83 - ], - [ - "Text", - 11 - ], - [ - "Equation", - 6 - ], - [ - "PageFooter", - 2 - ], - [ - "PageHeader", - 1 - ], - [ - "Figure", - 1 - ], - [ - "Caption", - 1 - ], - [ - "Footnote", - 1 - ], - [ - "FigureGroup", - 1 - ] - ], - "block_metadata": { - "llm_request_count": 0, - "llm_error_count": 0, - "llm_tokens_used": 0, - "previous_text": "", - "previous_type": "", - "previous_order": 0 - } - }, - { - "page_id": 2, - "text_extraction_method": "pdftext", - "block_counts": [ - [ - "Span", - 217 - ], - [ - "Line", - 88 - ], - [ - "Text", - 15 - ], - [ - "Equation", - 7 - ], - [ - "ListItem", - 3 - ], - [ - "PageFooter", - 3 - ], - [ - "PageHeader", - 1 - ], - [ - "Figure", - 1 - ], - [ - "Caption", - 1 - ], - [ - "SectionHeader", - 1 - ], - [ - "Footnote", - 1 - ], - [ - "FigureGroup", - 1 - ], - [ - "ListGroup", - 1 - ] - ], - "block_metadata": { - "llm_request_count": 0, - "llm_error_count": 0, - "llm_tokens_used": 0, - "previous_text": "", - "previous_type": "", - "previous_order": 0 - } - }, - { - "page_id": 3, - "text_extraction_method": "pdftext", - "block_counts": [ - [ - "Span", - 123 - ], - [ - "Line", - 58 - ], - [ - "Equation", - 17 - ], - [ - "Text", - 14 - ], - [ - "PageFooter", - 2 - ], - [ - "PageHeader", - 1 - ] - ], - "block_metadata": { - "llm_request_count": 0, - "llm_error_count": 0, - "llm_tokens_used": 0, - "previous_text": "", - "previous_type": "", - "previous_order": 0 - } - }, - { - "page_id": 4, - "text_extraction_method": "pdftext", - "block_counts": [ - [ - "Span", - 129 - ], - [ - "Line", - 58 - ], - [ - "Text", - 9 - ], - [ - "Figure", - 4 - ], - [ - "Caption", - 4 - ], - [ - "PageFooter", - 3 - ], - [ - "FigureGroup", - 3 - ], - [ - "Equation", - 2 - ], - [ - "SectionHeader", - 2 - ], - [ - "PageHeader", - 1 - ] - ], - "block_metadata": { - "llm_request_count": 0, - "llm_error_count": 0, - "llm_tokens_used": 0, - "previous_text": "", - "previous_type": "", - "previous_order": 0 - } - }, - { - "page_id": 5, - "text_extraction_method": "pdftext", - "block_counts": [ - [ - "Span", - 144 - ], - [ - "Line", - 46 - ], - [ - "Text", - 13 - ], - [ - "TableCell", - 13 - ], - [ - "Figure", - 6 - ], - [ - "Caption", - 5 - ], - [ - "Table", - 2 - ], - [ - "PageFooter", - 2 - ], - [ - "PageHeader", - 1 - ], - [ - "FigureGroup", - 1 - ], - [ - "TableGroup", - 1 - ] - ], - "block_metadata": { - "llm_request_count": 0, - "llm_error_count": 0, - "llm_tokens_used": 0, - "previous_text": "", - "previous_type": "", - "previous_order": 0 - } - }, - { - "page_id": 6, - "text_extraction_method": "pdftext", - "block_counts": [ - [ - "Span", - 239 - ], - [ - "Line", - 97 - ], - [ - "TableCell", - 14 - ], - [ - "Text", - 7 - ], - [ - "Caption", - 5 - ], - [ - "Figure", - 3 - ], - [ - "SectionHeader", - 3 - ], - [ - "PageFooter", - 3 - ], - [ - "Table", - 2 - ], - [ - "FigureGroup", - 2 - ], - [ - "PageHeader", - 1 - ], - [ - "TableGroup", - 1 - ] - ], - "block_metadata": { - "llm_request_count": 0, - "llm_error_count": 0, - "llm_tokens_used": 0, - "previous_text": "", - "previous_type": "", - "previous_order": 0 - } - }, - { - "page_id": 7, - "text_extraction_method": "pdftext", - "block_counts": [ - [ - "Span", - 188 - ], - [ - "Line", - 89 - ], - [ - "TableCell", - 48 - ], - [ - "Caption", - 5 - ], - [ - "Text", - 5 - ], - [ - "Figure", - 2 - ], - [ - "FigureGroup", - 2 - ], - [ - "PageHeader", - 1 - ], - [ - "Table", - 1 - ], - [ - "Picture", - 1 - ], - [ - "Equation", - 1 - ], - [ - "PageFooter", - 1 - ], - [ - "TableGroup", - 1 - ] - ], - "block_metadata": { - "llm_request_count": 0, - "llm_error_count": 0, - "llm_tokens_used": 0, - "previous_text": "", - "previous_type": "", - "previous_order": 0 - } - }, - { - "page_id": 8, - "text_extraction_method": "pdftext", - "block_counts": [ - [ - "Span", - 200 - ], - [ - "Line", - 94 - ], - [ - "TableCell", - 28 - ], - [ - "Text", - 12 - ], - [ - "Caption", - 4 - ], - [ - "PageFooter", - 3 - ], - [ - "Table", - 2 - ], - [ - "SectionHeader", - 2 - ], - [ - "TableGroup", - 2 - ], - [ - "PageHeader", - 1 - ], - [ - "Picture", - 1 - ], - [ - "Figure", - 1 - ], - [ - "FigureGroup", - 1 - ] - ], - "block_metadata": { - "llm_request_count": 0, - "llm_error_count": 0, - "llm_tokens_used": 0, - "previous_text": "", - "previous_type": "", - "previous_order": 0 - } - }, - { - "page_id": 9, - "text_extraction_method": "pdftext", - "block_counts": [ - [ - "Span", - 39 - ], - [ - "Line", - 16 - ], - [ - "ListItem", - 5 - ], - [ - "PageHeader", - 1 - ], - [ - "Figure", - 1 - ], - [ - "Caption", - 1 - ], - [ - "SectionHeader", - 1 - ], - [ - "Text", - 1 - ], - [ - "PageFooter", - 1 - ], - [ - "FigureGroup", - 1 - ], - [ - "ListGroup", - 1 - ] - ], - "block_metadata": { - "llm_request_count": 0, - "llm_error_count": 0, - "llm_tokens_used": 0, - "previous_text": "", - "previous_type": "", - "previous_order": 0 - } - }, - { - "page_id": 10, - "text_extraction_method": "pdftext", - "block_counts": [ - [ - "Span", - 105 - ], - [ - "Line", - 40 - ], - [ - "TableCell", - 14 - ], - [ - "ListItem", - 12 - ], - [ - "Caption", - 4 - ], - [ - "Figure", - 3 - ], - [ - "PageFooter", - 2 - ], - [ - "FigureGroup", - 2 - ], - [ - "PageHeader", - 1 - ], - [ - "Picture", - 1 - ], - [ - "Table", - 1 - ], - [ - "PictureGroup", - 1 - ], - [ - "TableGroup", - 1 - ], - [ - "ListGroup", - 1 - ] - ], - "block_metadata": { - "llm_request_count": 0, - "llm_error_count": 0, - "llm_tokens_used": 0, - "previous_text": "", - "previous_type": "", - "previous_order": 0 - } - }, - { - "page_id": 11, - "text_extraction_method": "pdftext", - "block_counts": [ - [ - "Span", - 91 - ], - [ - "Line", - 36 - ], - [ - "ListItem", - 11 - ], - [ - "Figure", - 4 - ], - [ - "PageFooter", - 3 - ], - [ - "Caption", - 2 - ], - [ - "ListGroup", - 2 - ], - [ - "PageHeader", - 1 - ], - [ - "Text", - 1 - ], - [ - "FigureGroup", - 1 - ] - ], - "block_metadata": { - "llm_request_count": 0, - "llm_error_count": 0, - "llm_tokens_used": 0, - "previous_text": "", - "previous_type": "", - "previous_order": 0 - } - } - ], - "debug_data_path": "debug_data\\AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis" -} \ No newline at end of file diff --git a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_0_Figure_18.jpeg b/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_0_Figure_18.jpeg deleted file mode 100644 index 4270e91..0000000 Binary files a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_0_Figure_18.jpeg and /dev/null differ diff --git a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_10_Figure_5.jpeg b/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_10_Figure_5.jpeg deleted file mode 100644 index ec6bf42..0000000 Binary files a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_10_Figure_5.jpeg and /dev/null differ diff --git a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_10_Figure_6.jpeg b/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_10_Figure_6.jpeg deleted file mode 100644 index 0702b1c..0000000 Binary files a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_10_Figure_6.jpeg and /dev/null differ diff --git a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_10_Figure_8.jpeg b/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_10_Figure_8.jpeg deleted file mode 100644 index 39e8cc8..0000000 Binary files a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_10_Figure_8.jpeg and /dev/null differ diff --git a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_10_Picture_1.jpeg b/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_10_Picture_1.jpeg deleted file mode 100644 index 6fe7657..0000000 Binary files a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_10_Picture_1.jpeg and /dev/null differ diff --git a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_11_Figure_1.jpeg b/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_11_Figure_1.jpeg deleted file mode 100644 index e9c616b..0000000 Binary files a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_11_Figure_1.jpeg and /dev/null differ diff --git a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_11_Figure_2.jpeg b/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_11_Figure_2.jpeg deleted file mode 100644 index 0396796..0000000 Binary files a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_11_Figure_2.jpeg and /dev/null differ diff --git a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_11_Figure_3.jpeg b/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_11_Figure_3.jpeg deleted file mode 100644 index d6aa43f..0000000 Binary files a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_11_Figure_3.jpeg and /dev/null differ diff --git a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_11_Figure_9.jpeg b/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_11_Figure_9.jpeg deleted file mode 100644 index 813f767..0000000 Binary files a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_11_Figure_9.jpeg and /dev/null differ diff --git a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_1_Figure_1.jpeg b/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_1_Figure_1.jpeg deleted file mode 100644 index 50b3179..0000000 Binary files a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_1_Figure_1.jpeg and /dev/null differ diff --git a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_2_Figure_1.jpeg b/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_2_Figure_1.jpeg deleted file mode 100644 index 631d8e7..0000000 Binary files a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_2_Figure_1.jpeg and /dev/null differ diff --git a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_4_Figure_13.jpeg b/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_4_Figure_13.jpeg deleted file mode 100644 index d965902..0000000 Binary files a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_4_Figure_13.jpeg and /dev/null differ diff --git a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_4_Figure_15.jpeg b/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_4_Figure_15.jpeg deleted file mode 100644 index 31be044..0000000 Binary files a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_4_Figure_15.jpeg and /dev/null differ diff --git a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_4_Figure_17.jpeg b/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_4_Figure_17.jpeg deleted file mode 100644 index a0b136e..0000000 Binary files a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_4_Figure_17.jpeg and /dev/null differ diff --git a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_4_Figure_19.jpeg b/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_4_Figure_19.jpeg deleted file mode 100644 index 18799a9..0000000 Binary files a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_4_Figure_19.jpeg and /dev/null differ diff --git a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_5_Figure_1.jpeg b/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_5_Figure_1.jpeg deleted file mode 100644 index 28aa2d5..0000000 Binary files a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_5_Figure_1.jpeg and /dev/null differ diff --git a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_5_Figure_13.jpeg b/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_5_Figure_13.jpeg deleted file mode 100644 index 27cc66d..0000000 Binary files a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_5_Figure_13.jpeg and /dev/null differ diff --git a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_5_Figure_15.jpeg b/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_5_Figure_15.jpeg deleted file mode 100644 index 2c72a64..0000000 Binary files a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_5_Figure_15.jpeg and /dev/null differ diff --git a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_5_Figure_17.jpeg b/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_5_Figure_17.jpeg deleted file mode 100644 index 0c51dba..0000000 Binary files a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_5_Figure_17.jpeg and /dev/null differ diff --git a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_5_Figure_19.jpeg b/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_5_Figure_19.jpeg deleted file mode 100644 index 5cbcbbe..0000000 Binary files a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_5_Figure_19.jpeg and /dev/null differ diff --git a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_5_Figure_3.jpeg b/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_5_Figure_3.jpeg deleted file mode 100644 index c7ee073..0000000 Binary files a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_5_Figure_3.jpeg and /dev/null differ diff --git a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_6_Figure_1.jpeg b/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_6_Figure_1.jpeg deleted file mode 100644 index 8968ed5..0000000 Binary files a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_6_Figure_1.jpeg and /dev/null differ diff --git a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_6_Figure_3.jpeg b/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_6_Figure_3.jpeg deleted file mode 100644 index b7e787b..0000000 Binary files a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_6_Figure_3.jpeg and /dev/null differ diff --git a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_6_Figure_4.jpeg b/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_6_Figure_4.jpeg deleted file mode 100644 index 294dcab..0000000 Binary files a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_6_Figure_4.jpeg and /dev/null differ diff --git a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_7_Figure_1.jpeg b/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_7_Figure_1.jpeg deleted file mode 100644 index 6588636..0000000 Binary files a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_7_Figure_1.jpeg and /dev/null differ diff --git a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_7_Figure_7.jpeg b/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_7_Figure_7.jpeg deleted file mode 100644 index fab8965..0000000 Binary files a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_7_Figure_7.jpeg and /dev/null differ diff --git a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_7_Picture_6.jpeg b/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_7_Picture_6.jpeg deleted file mode 100644 index b31913a..0000000 Binary files a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_7_Picture_6.jpeg and /dev/null differ diff --git a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_8_Figure_3.jpeg b/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_8_Figure_3.jpeg deleted file mode 100644 index ab182a5..0000000 Binary files a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_8_Figure_3.jpeg and /dev/null differ diff --git a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_8_Picture_1.jpeg b/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_8_Picture_1.jpeg deleted file mode 100644 index 4b1040f..0000000 Binary files a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_8_Picture_1.jpeg and /dev/null differ diff --git a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_9_Figure_1.jpeg b/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_9_Figure_1.jpeg deleted file mode 100644 index 99b3a5d..0000000 Binary files a/docs/Paper/AContinuumMechanicsBasedFourNodeShellElementforGeneralNonlinearAnalysis/_page_9_Figure_1.jpeg and /dev/null differ diff --git a/docs/Paper/FourNodeQuadrilateralShellElementMITC4/fournodequadrilateralshellelementmitc4_001.md b/docs/Paper/FourNodeQuadrilateralShellElementMITC4/fournodequadrilateralshellelementmitc4_001.md deleted file mode 100644 index 19a8154..0000000 --- a/docs/Paper/FourNodeQuadrilateralShellElementMITC4/fournodequadrilateralshellelementmitc4_001.md +++ /dev/null @@ -1,171 +0,0 @@ -# Four-Node Quadrilateral Shell Element MITC4 - -DVORˇ AKOV ´ A Edita ´ 1 ,a, ∗ , PATZAK Bo ´ ˇrek1, b - -1 Department of Mechanics, Faculty of Civil Engineering, CTU in Prague, Thakurova 7, ´ 160 00 Prague, Czech Republic - -a edita.dvorakova@fsv.cvut.cz, b borek.patzak@fsv.cvut.cz - -Keywords: Finite Elements; MITC4; Scordelis-Lo Shell; Shear Locking; Shell Structures. - -Abstract. Four-node quadrilateral element MITC4 applicable to both thick and thin shells is presented. The element formulation starts from three-dimensional continuum description degenerated to shell behavior. Shear locking, which is common problem in analysis of thin shells, is overcome by the use of MITC (Mixed Interpolation of Tensorial Components) approach. Element has been implemented into finite element code OOFEM and its performance is demonstrated on Scordelis-Lo shell, a benchmark problem frequently used in the evaluation of shell elements. - -### Introduction - -Shell structures are widely used in structural engineering for their load-carrying efficiency. The finite element method appears to be the most powerful tool in analysis of shell structures, however the accuracy of the method is significantly influenced by the choice of the particular element. Many shell elements have been developed over the years. Existing elements can be divided into two main groups. One group consists of elements based on some particular shell theory, the other group of the elements is based on three-dimensional analysis degenerated to shell behavior. - -Presented quadrilateral element has been proposed by Dvorkin and Bathe [1] and is based on the second approach, therefore it is independent of any particular shell theory. The authors aimed to formulate element applicable to both thick and thin shells of arbitrary geometries with all degrees of freedom concentrated to the vertices of the element. However high-order elements with 9 and 16 nodes have been already successfully employed by Bathe and Bolourchi [2], this 4-node element suffers from the deficiency of shear locking. To prevent the element from the shear locking the use of MITC approach has been proposed. - -# Element Formulation - -The element is shown in Fig. 1 and its geometry is described using - -$$ -x_{i}=\sum_{k=1}^{4}h_{k}x_{i}^{k}+\frac{r_{3}}{2}\sum_{k=1}^{4}a_{k}h_{k}V_{ ni}^{k}, \tag{1} -$$ - -where hk(r1, r2) are the two-dimensional interpolation functions corresponding to node k, r i are the natural coordinates, x i are the Cartesian coordinates of any point in the element, x k i are the Cartesian coordinates of node k, V k ni are the components of director vector at node k and a k is the thickness of structure measured along the director vector. The displacement of any point in the element can be described using - -$$ -u_{i}=\sum_{k=1}^{4}h_{k}u_{i}^{k}+\frac{r_{3}}{2}\sum_{k=1}^{4}a_{k}h_{k}(-V_ {2i}^{k}\alpha_{k}+V_{1i}^{k}\beta_{k}), \tag{2} -$$ - -![Fig. 1: Geometry of quadrilateral shell element MITC4.](images/chunk-001-fig-019.jpg) - -where `u_i` are the displacements in direction of Cartesian coordinates and `\alpha_k` and `\beta_k` are the rotations of director vector at node k around `V_1^k` and `V_2^k` , where - -$$ -V_{1}^{k}=\frac{\mathbf{e}_{2}\times V_{n}^{k}}{\|\mathbf{e}_{2}\times V_{n}^{k}\|}, \tag{3} -$$ - -$$ -V_{2}^{k}=V_{n}^{k}\times V_{1}^{k}. \tag{4} -$$ - -Vector `e_2` is the basis vector of Cartesian coordinate system. Considering linear interpolation of displacement field, we introduce the vector of unknown displacements - -$$ -\boldsymbol{r}_e = \{u_1^1, u_2^1, u_3^1, \alpha_1, \beta_1, u_1^2, u_2^2, u_3^2, \alpha_2, \beta_2, u_1^3, u_2^3, u_3^3, \alpha_3, \beta_3, u_1^4, u_2^4, u_3^4, \alpha_4, \beta_4\}^T. -$$ - -The biggest drawback of this formulation of the element is that the element locks when the thickness of shell is small. Using the interpolation (2), non-zero transverse shear strains are obtained even when the structure is subjected to the constant bending moment. The remedy is to construct an assumed transverse strain, such that - -$$ -\tilde{\varepsilon}_{13} = \frac{1}{2}(1+r_2)\tilde{\varepsilon}_{13}^A + \frac{1}{2}(1-r_2)\tilde{\varepsilon}_{13}^C, -\tilde{\varepsilon}_{23} = \frac{1}{2}(1+r_1)\tilde{\varepsilon}_{23}^D + \frac{1}{2}(1-r_1)\tilde{\varepsilon}_{23}^B, -$$ - -where $\sim$ over the quantities emphasizes the formulation in convected coordinate system and components `\tilde{\varepsilon}_{13}^A` , `\tilde{\varepsilon}_{23}^B` , `\tilde{\varepsilon}_{13}^C` and `\tilde{\varepsilon}_{23}^D` are equal to the values of corresponding strains in the middle of element edges calculated from displacements. This assumed transversal strain $\tilde{\varepsilon}_{13}$ is constant along `r_1` direction and linear along `r_2` direction. - -To transform formulas for shear strain components to Cartesian coordinate system, it is convenient to use covariant and contravariant bases in convected coordinate system of `r_1, r_2` and `r_3` . For the element with midsurface in plane x, y the covariant basis vectors `\mathbf{g}_i` (i = 1, 2, 3) are given by - -$$ -\mathbf{g}_{i}=\frac{\partial\mathbf{x}}{\partial r_{i}}. \tag{7} -$$ - -Contravariant basis vectors $\mathbf{g}^{j}$ (j = 1, 2, 3) can be then calculated using - -$$ -\mathbf{g}_{i}\mathbf{g}^{j}=\delta_{i}^{j}, \tag{8} -$$ - -where `\delta_i^j` is Kronecker delta, `\delta_i^j = 1` for i = j and `\delta_i^j = 0` otherwise. Components $\tilde{\varepsilon}_{ij}$ can be evaluated using - -$$ -\tilde{\varepsilon}_{ij}=\frac{1}{2}\left(\frac{\partial\mathbf{u}}{\partial r_{ i}}\mathbf{g}_{j}+\frac{\partial\mathbf{u}}{\partial r_{j}}\mathbf{g}_{i}\right), \tag{9} -$$ - -$$ -\tilde{\varepsilon}_{ij}=\frac{1}{2}\left(\frac{\partial\mathbf{u}}{\partial r_{ i}}\frac{\partial\mathbf{x}}{\partial r_{j}}+\frac{\partial\mathbf{u}}{\partial r_{j}} \frac{\partial\mathbf{x}}{\partial r_{i}}\right). \tag{10} -$$ - -By substituting from (1) and (2) into equation (10) and evaluating at points A, B, C, D the relations for `\tilde{\varepsilon}_{13}^A` , `\tilde{\varepsilon}_{23}^B` , `\tilde{\varepsilon}_{13}^C` and `\tilde{\varepsilon}_{23}^D` can be obtained - -$$ -\tilde{\varepsilon}_{13}^{A} = \frac{1}{8} \left[ (\mathbf{u}^{1} - \mathbf{u}^{2}) \cdot \frac{1}{2} (a_{1}V_{n}^{1} + a_{2}V_{n}^{2}) + (\mathbf{x}^{1} - \mathbf{x}^{2}) \cdot \frac{1}{2} (a_{1}(-V_{2}^{1}\alpha_{1} + V_{1}^{1}\beta_{1}) + a_{2}(-V_{2}^{2}\alpha_{2} + V_{1}^{2}\beta_{2})) \right], -\tilde{\varepsilon}_{13}^{C} = \frac{1}{8} \left[ (\mathbf{u}^{4} - \mathbf{u}^{3}) \cdot \frac{1}{2} (a_{3}V_{n}^{3} + a_{4}V_{n}^{4}) + (\mathbf{x}^{4} - \mathbf{x}^{3}) \cdot \frac{1}{2} (a_{3}(-V_{2}^{3}\alpha_{3} + V_{1}^{3}\beta_{3}) + a_{4}(-V_{2}^{4}\alpha_{4} + V_{1}^{4}\beta_{4})) \right], -\tilde{\varepsilon}_{23}^{B} = \frac{1}{8} \left[ (\mathbf{u}^{1} - \mathbf{u}^{4}) \cdot \frac{1}{2} (a_{1}V_{n}^{1} + a_{4}V_{n}^{4}) + (\mathbf{x}^{1} - \mathbf{x}^{4}) \cdot \frac{1}{2} (a_{1}(-V_{2}^{1}\alpha_{1} + V_{1}^{1}\beta_{1}) + a_{4}(-V_{2}^{4}\alpha_{4} + V_{1}^{4}\beta_{4})) \right], -\tilde{\varepsilon}_{23}^{D} = \frac{1}{8} \left[ (\mathbf{u}^{2} - \mathbf{u}^{3}) \cdot \frac{1}{2} (a_{2}V_{n}^{2} + a_{3}V_{n}^{3}) + (\mathbf{x}^{2} - \mathbf{x}^{3}) \cdot \frac{1}{2} (a_{2}(-V_{2}^{2}\alpha_{2} + V_{1}^{2}\beta_{2}) + a_{3}(-V_{2}^{3}\alpha_{3} + V_{1}^{3}\beta_{3})) \right]. -$$ - -Final equations for $\tilde{\varepsilon}_{13}$ and $\tilde{\varepsilon}_{23}$ are then derived by substituting (11) back into the equations (6). Transformation to the Cartesian coordinates is performed using - -$$ -\tilde{\varepsilon}_{ij}\mathbf{g}^{i}\mathbf{g}^{j}=\varepsilon_{kl}\mathbf{e}_{k}\mathbf{e}_ {l}, \tag{12} -$$ - -where $\varepsilon_{kl}$ are components of strain tensor in Cartesian coordinates with basis vectors `e_k` and `e_l` . By considering the symmetry of strain tensor, the shear components of strain vector $\gamma_{xz}$ and $\gamma_{yz}$ are obtained - -$$ -\gamma_{xy} = 2\tilde{\varepsilon}_{13}(\boldsymbol{g}^{1} \cdot \boldsymbol{e}_{1})(\boldsymbol{g}^{3} \cdot \boldsymbol{e}_{3}) + 2\tilde{\varepsilon}_{23}(\boldsymbol{g}^{2} \cdot \boldsymbol{e}_{1})(\boldsymbol{g}^{3} \cdot \boldsymbol{e}_{3}), -\gamma_{yz} = 2\tilde{\varepsilon}_{13}(\boldsymbol{g}^{1} \cdot \boldsymbol{e}_{2})(\boldsymbol{g}^{3} \cdot \boldsymbol{e}_{3}) + 2\tilde{\varepsilon}_{23}(\boldsymbol{g}^{2} \cdot \boldsymbol{e}_{2})(\boldsymbol{g}^{3} \cdot \boldsymbol{e}_{3}) -$$ - -Substituting from (6) and (11) into (13) the final formulas for transverse shear strains are derived. The remaining components of strain vector are calculated directly from the displacements. - -![Fig. 2: Cylindrical Scordelis-Lo shell subjected to dead weight.](images/chunk-001-fig-046.jpg) - -Stiffness matrix is evaluated using - -$$ -\mathbf{K}=\int\limits_{V}\mathbf{B}^{T}\mathbf{D}\mathbf{B}\ dV, \tag{14} -$$ - -where B is a strain-displacement matrix resulting from derived formulas for strain components and D is a material matrix for three-dimensional problem degenerated to shell behaviour, where the condition of σ z = 0 is enforced. - -# Scordelis-Lo Shell Problem - -Element has been implemented into finite element code OOFEM [3, 4] and implementation has been verified using classical patch tests. It has been proven, that the patch tests are passed for the case of pure bending, pure shear and pure twist and also for three membrane stress states. - -To test the element performance on more complex structure and to compare its results with other available elements and with the analytical solution the analysis of Scordelis-Lo Shell [1] is presented. The structure is loaded by its dead weight and is shown in Fig. 2. Due to the symmetry only one quarter of the structure has been analyzed. Deformed shape of the structure is shown in Fig. 3, obtained results are shown in Fig. 4. Solution obtained using the element composed of plane-stress element with rotational degrees of freedom and Discrete Kirchhoff Triangle plate element, is also shown for reference, labeled as RDKT. - -#### Summary - -Element for shells has been implemented into existing finite element code OOFEM and its performance has been verified. Shear locking, which can cause highly inaccurate results in analysis of thin shells, has been overcome by MITC approach. This approach seems to be sufficient as appropriate results were obtained while testing the element implementation. Numerical results has shown that the element is competitive with the performance of thin-plate elements. Possibility of use of MITC4 element for both thick and thin shells can be seen as advantage over the RDKT and other shell theory based elements, which are usually limited by thickness/span ratio to either thin or thick shells. The element shows very good convergence to the reference solution [1], even outperforming the planar shell element composed of thin Kirchhoff plate element and membrane element. - -![Fig. 3: Cylindrical Scordelis-Lo shell subjected to dead weight. Only one quarter of the structure has been analyzed, mesh of 8×8 elements and deformed shape are shown.](images/chunk-001-fig-057.jpg) - -![Fig. 4: Analysis of cylindrical Scordelis-Lo shell subjected to dead weight. Convergence of displacement at point B is studied, solutions obtained with MITC4 and RDKT elements are shown as well as analytical solutions.](images/chunk-001-fig-058.jpg) - -## Acknowledgement - -The financial support of this research by the Grant Agency of the Czech Technical University in Prague (SGS project No. 15/031/OHK1/1T/11) and by the Technology Agency of the Czech Republic (TACR project No. TA02011196) is gratefully acknowledged. ˇ - -## References - -- [1] K. J. Bathe, E. Dvorkin, A continuum mechanics based four-node shell element for general nonlinear analysis, Eng. Comput., Vol.1, March (1984) 77-88. -- [2] K. J. Bathe, S. Bolourchi, A geometric ad material nonlinear plate and shell element, Computers & Structures, Vol.11, (1980) 23-48. -- [3] B. Patzak, Z. Bittnar, Design of object oriented finite element code, Advances in Engineering ´ Software 32 (10-11) (2001) 759-767. -- [4] B. Patzak, OOFEM project home page, http://www.oofem.org, 2014. ´ -- [5] K. J. Bathe, Finite Element Procedures, Prentice Hall International, Inc., 1996. -- [6] M. L. Bucalem, K. J. Bathe, Finite element analysis of shell structures, Archives of Computational Methods in Engineering, Vol.4, 1 (1997) 3-61. - -#### Modern Methods of Experimental and Computational Investigations in Area of Construction - -10.4028/www.scientific.net/AMM.825 - -#### Four-Node Quadrilateral Shell Element MITC4 - -10.4028/www.scientific.net/AMM.825.99 - -## DOI References - -[1] K. J. Bathe, E. Dvorkin, A continuum mechanics based four-node shell element for general nonlinear analysis, Eng. Comput., Vol. 1, March (1984) 77-88. - -10.1108/eb023562 - -[2] K. J. Bathe, S. Bolourchi, A geometric ad material nonlinear plate and shell element, Computers & Structures, Vol. 11, (1980) 23-48. - -10.1016/0045-7949(80)90144-3 - -[3] B. Patz´ak, Z. Bittnar, Design of object oriented finite element code, Advances in Engineering Software 32 (10-11) (2001) 759-767. - -10.1016/s0965-9978(01)00027-8 - -[5] K. J. Bathe, Finite Element Procedures, Prentice Hall International, Inc., (1996). - -10.1002/nme.1620190115 - -[6] M. L. Bucalem, K. J. Bathe, Finite element analysis of shell structures, Archives of Computational Methods in Engineering, Vol. 4, 1 (1997) 3-61. - -10.1007/bf02818930 diff --git a/docs/Paper/FourNodeQuadrilateralShellElementMITC4/images/chunk-001-fig-019.jpg b/docs/Paper/FourNodeQuadrilateralShellElementMITC4/images/chunk-001-fig-019.jpg deleted file mode 100644 index dc43290..0000000 Binary files a/docs/Paper/FourNodeQuadrilateralShellElementMITC4/images/chunk-001-fig-019.jpg and /dev/null differ diff --git a/docs/Paper/FourNodeQuadrilateralShellElementMITC4/images/chunk-001-fig-046.jpg b/docs/Paper/FourNodeQuadrilateralShellElementMITC4/images/chunk-001-fig-046.jpg deleted file mode 100644 index 1374fd0..0000000 Binary files a/docs/Paper/FourNodeQuadrilateralShellElementMITC4/images/chunk-001-fig-046.jpg and /dev/null differ diff --git a/docs/Paper/FourNodeQuadrilateralShellElementMITC4/images/chunk-001-fig-057.jpg b/docs/Paper/FourNodeQuadrilateralShellElementMITC4/images/chunk-001-fig-057.jpg deleted file mode 100644 index 3420582..0000000 Binary files a/docs/Paper/FourNodeQuadrilateralShellElementMITC4/images/chunk-001-fig-057.jpg and /dev/null differ diff --git a/docs/Paper/FourNodeQuadrilateralShellElementMITC4/images/chunk-001-fig-058.jpg b/docs/Paper/FourNodeQuadrilateralShellElementMITC4/images/chunk-001-fig-058.jpg deleted file mode 100644 index c604070..0000000 Binary files a/docs/Paper/FourNodeQuadrilateralShellElementMITC4/images/chunk-001-fig-058.jpg and /dev/null differ diff --git a/docs/Paper/mitc공부/images/chunk-001-fig-004.jpg b/docs/Paper/mitc공부/images/chunk-001-fig-004.jpg deleted file mode 100644 index 6f47a4b..0000000 Binary files a/docs/Paper/mitc공부/images/chunk-001-fig-004.jpg and /dev/null differ diff --git a/docs/Paper/mitc공부/images/chunk-001-fig-005.jpg b/docs/Paper/mitc공부/images/chunk-001-fig-005.jpg deleted file mode 100644 index 007f363..0000000 Binary files a/docs/Paper/mitc공부/images/chunk-001-fig-005.jpg and /dev/null differ diff --git a/docs/Paper/mitc공부/mitc공부_001.md b/docs/Paper/mitc공부/mitc공부_001.md deleted file mode 100644 index da789cd..0000000 --- a/docs/Paper/mitc공부/mitc공부_001.md +++ /dev/null @@ -1,125 +0,0 @@ -## 1. MITC shell element - -- 3차원 솔리드 형상으로부터 쉘형상을 표현 (유한요소 정식화가 다른 쉘요소에 비해 간단) -- 쉘 이론을 사용하지 않고 3차원 응력, 변형률을 사용하여 쉘을 표현할 수 있다. -- 임의의 형상에 대한 두꺼운 쉘과 얇은 쉘 모두 적용 가능 -- Locking을 방지하기 위해 횡방향 전단 변형률에 보간법 사용 - -## 2. Kinematics - -![Figure](images/chunk-001-fig-004.jpg) - -![Figure](images/chunk-001-fig-005.jpg) - -Shell의 초기 위치 벡터는 다음과 같이 shape function으로 나타낼 수 있다. - -$$ -{}^{0}\mathbf{X} = \sum_{i=1}^{4} \phi_{i}(\xi^{1}, \xi^{2}) {}^{0}\mathbf{X}_{i} + \frac{\xi^{3}}{2} \sum_{i=1}^{4} h_{i}\phi_{i}(\xi^{1}, \xi^{2}) {}^{0}\mathbf{V}_{n}^{i} -$$ - -마찬가지로 시간이 t, $t+\Delta t$ 일 때 위치벡터는 다음과 같다. - -$$ -\begin{split} ^{t}\mathbf{x} &= \sum_{i=1}^{4} \phi_{i}\left(\boldsymbol{\xi}^{1}, \boldsymbol{\xi}^{2}\right){}^{t}\mathbf{x}_{i} + \frac{\boldsymbol{\xi}^{3}}{2} \sum_{i=1}^{4} h_{i} \phi_{i}\left(\boldsymbol{\xi}^{1}, \boldsymbol{\xi}^{2}\right){}^{t}\mathbf{V}_{n}^{i} \\ ^{t+\Delta t}\mathbf{x} &= \sum_{i=1}^{4} \phi_{i}\left(\boldsymbol{\xi}^{1}, \boldsymbol{\xi}^{2}\right){}^{t+\Delta t}\mathbf{x}_{i} + \frac{\boldsymbol{\xi}^{3}}{2} \sum_{i=1}^{4} h_{i} \phi_{i}\left(\boldsymbol{\xi}^{1}, \boldsymbol{\xi}^{2}\right){}^{t+\Delta t}\mathbf{V}_{n}^{i} \end{split} -$$ - -시간이 t일 때와 $t + \Delta t$ 일 때 변위 벡터는 다음과 같이 계산 할 수 있다. - -$$ -\begin{split} ^{t}\mathbf{u} &= ^{t}\mathbf{X}^{-0}\mathbf{X} \\ &= \sum_{i=1}^{4} \phi_{i} \left(\boldsymbol{\xi}^{1}, \boldsymbol{\xi}^{2}\right)^{t} \mathbf{x}_{i} + \frac{\boldsymbol{\xi}^{3}}{2} \sum_{i=1}^{4} h_{i} \phi_{i} \left(\boldsymbol{\xi}^{1}, \boldsymbol{\xi}^{2}\right)^{t} \mathbf{V}_{n}^{i} - \sum_{i=1}^{4} \phi_{i} \left(\boldsymbol{\xi}^{1}, \boldsymbol{\xi}^{2}\right)^{0} \mathbf{X}_{i} - \frac{\boldsymbol{\xi}^{3}}{2} \sum_{i=1}^{4} h_{i} \phi_{i} \left(\boldsymbol{\xi}^{1}, \boldsymbol{\xi}^{2}\right)^{0} \mathbf{V}_{n}^{i} \\ &= \sum_{i=1}^{4} \phi_{i} \left(^{t}\mathbf{x}_{i}^{-0}\mathbf{X}_{i}\right) + \frac{\boldsymbol{\xi}^{3}}{2} \sum_{i=1}^{4} h_{i} \phi_{i} \left(^{t}\mathbf{V}_{n}^{i}^{-0}\mathbf{V}_{n}^{i}\right) \\ ^{t+\Delta t}\mathbf{u} &= ^{t+\Delta t}\mathbf{x}^{-0}\mathbf{X} \\ &= \sum_{i=1}^{4} \phi_{i} \left(\boldsymbol{\xi}^{1}, \boldsymbol{\xi}^{2}\right)^{t+\Delta t} \mathbf{x}_{i} + \frac{\boldsymbol{\xi}^{3}}{2} \sum_{i=1}^{4} h_{i} \phi_{i} \left(\boldsymbol{\xi}^{1}, \boldsymbol{\xi}^{2}\right)^{t+\Delta t} \mathbf{V}_{n}^{i} - \sum_{i=1}^{4} \phi_{i} \left(\boldsymbol{\xi}^{1}, \boldsymbol{\xi}^{2}\right)^{0} \mathbf{X}_{i} - \frac{\boldsymbol{\xi}^{3}}{2} \sum_{i=1}^{4} h_{i} \phi_{i} \left(\boldsymbol{\xi}^{1}, \boldsymbol{\xi}^{2}\right)^{0} \mathbf{V}_{n}^{i} \\ &= \sum_{i=1}^{4} \phi_{i} \left(^{t+\Delta t}\mathbf{x}_{i}^{-0}\mathbf{X}_{i}\right) + \frac{\boldsymbol{\xi}^{3}}{2} \sum_{i=1}^{4} h_{i} \phi_{i} \left(^{t+\Delta t}\mathbf{V}_{n}^{i}^{i} - \mathbf{V}_{n}^{i}\right) \end{split} -$$ - -따라서 시간 t와 $t+\Delta t$ 사이의 incremental displacement는 다음과 같이 나타낼 수 있다. - -$$ -\begin{split} & \Delta^{t}\mathbf{u} =^{t+\Delta t} \mathbf{u} - ^{t}\mathbf{u} \\ & = \sum_{i=1}^{4} \phi_{i} \left(^{t+\Delta t}\mathbf{x}_{i} - ^{0}\mathbf{X}_{i}\right) + \frac{\xi^{3}}{2} \sum_{i=1}^{4} h_{i} \phi_{i} \left(^{t+\Delta t}\mathbf{V}_{n}^{i} - ^{0}\mathbf{V}_{n}^{i}\right) - \sum_{i=1}^{4} \phi_{i} \left(^{t}\mathbf{x}_{i} - ^{0}\mathbf{X}_{i}\right) - \frac{\xi^{3}}{2} \sum_{i=1}^{4} h_{i} \phi_{i} \left(^{t}\mathbf{V}_{n}^{i} - ^{0}\mathbf{V}_{n}^{i}\right) \\ & = \sum_{i=1}^{4} \phi_{i} \left(^{t+\Delta t}\mathbf{x}_{i} - ^{t}\mathbf{x}_{i}\right) + \frac{\xi^{3}}{2} \sum_{i=1}^{4} h_{i} \phi_{i} \left(^{t+\Delta t}\mathbf{V}_{n}^{i} - ^{t}\mathbf{V}_{n}^{i}\right) \\ & = \sum_{i=1}^{4} \phi_{i} \Delta^{t}\mathbf{u}_{i} + \frac{\xi^{3}}{2} \sum_{i=1}^{4} h_{i} \phi_{i} \left(-\alpha_{1}^{i} {}^{t}\mathbf{V}_{2}^{i} + \alpha_{2}^{i} {}^{t}\mathbf{V}_{1}^{i}\right) \\ & = \sum_{i=1}^{4} \phi_{i} \Delta^{t}\mathbf{u}_{i} + \frac{\xi^{3}}{2} \sum_{i=1}^{4} h_{i} \phi_{i} \Delta^{t}\mathbf{V}_{n}^{i} \end{split} -$$ - -위치 벡터와 변위 벡터를 matrix form으로 나타내면 - -$$ -{}^{0}\mathbf{X} = {}^{0}\mathbf{N}^{0}\mathbf{X} -$$ - -$$ -= \begin{bmatrix} \phi_1 & \phi_2 & \phi_3 & \phi_4 \end{bmatrix} \begin{bmatrix} {}^{0}\mathbf{X}_1 \\ {}^{0}\mathbf{X}_2 \\ {}^{0}\mathbf{X}_3 \\ {}^{0}\mathbf{X}_4 \end{bmatrix} + \begin{bmatrix} \frac{\xi^3}{2}h_1\phi_1 & \frac{\xi^3}{2}h_2\phi_2 & \frac{\xi^3}{2}h_3\phi_3 & \frac{\xi^3}{2}h_4\phi_4 \end{bmatrix} \begin{bmatrix} {}^{0}\mathbf{V}_n^1 \\ {}^{0}\mathbf{V}_n^2 \\ {}^{0}\mathbf{V}_n^3 \\ {}^{0}\mathbf{V}_n^4 \end{bmatrix} -$$ - -$$ -= \left[ \phi_{1} \quad \frac{\xi^{3}}{2} h_{1} \phi_{1} \quad \phi_{2} \quad \frac{\xi^{3}}{2} h_{2} \phi_{2} \quad \phi_{3} \quad \frac{\xi^{3}}{2} h_{3} \phi_{3} \quad \phi_{4} \quad \frac{\xi^{3}}{2} h_{4} \phi_{4} \right] \begin{bmatrix} {}^{0} \mathbf{X}_{1} \\ {}^{0} \mathbf{X}_{2} \\ {}^{0} \mathbf{X}_{2} \\ {}^{0} \mathbf{X}_{3} \\ {}^{0} \mathbf{X}_{3} \\ {}^{0} \mathbf{X}_{3} \\ {}^{0} \mathbf{X}_{4} \\ {}^{0} \mathbf{V}_{n}^{4} \end{bmatrix} -$$ - -$$ -^{t}\mathbf{x} = ^{0}\mathbf{N}^{t}\mathbf{x} -$$ - -$$ -= \begin{bmatrix} \phi_1 & \frac{\xi^3}{2} h_1 \phi_1 & \phi_2 & \frac{\xi^3}{2} h_2 \phi_2 & \phi_3 & \frac{\xi^3}{2} h_3 \phi_3 & \phi_4 & \frac{\xi^3}{2} h_4 \phi_4 \end{bmatrix} \begin{bmatrix} {}^t \mathbf{X}_1 \\ {}^t \mathbf{V}_n^1 \\ {}^t \mathbf{X}_2 \\ {}^t \mathbf{V}_n^2 \\ {}^t \mathbf{X}_3 \\ {}^t \mathbf{V}_n^3 \\ {}^t \mathbf{X}_4 \\ {}^t \mathbf{V}_n^4 \end{bmatrix} -$$ - -$$ -^{t+\Delta t}\mathbf{x} = ^{0}\mathbf{N}^{t+\Delta t}\mathbf{x}_{n} -$$ - -$$ -= \left[ \begin{array}{cccccccccccccccccccccccccccccccccccc -$$ - -$$ -\Delta^t \mathbf{x} = {}^0 \mathbf{N} \Delta^t \mathbf{x}_n -$$ - -$$ -= \begin{bmatrix} \phi_1 & \frac{\xi^3}{2} h_1 \phi_1 & \phi_2 & \frac{\xi^3}{2} h_2 \phi_2 & \phi_3 & \frac{\xi^3}{2} h_3 \phi_3 & \phi_4 & \frac{\xi^3}{2} h_4 \phi_4 \end{bmatrix} \begin{bmatrix} \Delta \mathbf{X}_1 \\ \Delta^t \mathbf{V}_n^1 \\ \Delta^t \mathbf{X}_2 \\ \Delta^t \mathbf{V}_n^2 \\ \Delta^t \mathbf{V}_n^3 \\ \Delta^t \mathbf{V}_n^4 \\ \Delta^t \mathbf{V}_n^4 \end{bmatrix} -$$ - -변위 벡터도 마찬가지로 나타낼 수 있다. - -$$ -^{t}\mathbf{u} = ^{0}\mathbf{N}(^{t}\mathbf{x}_{n} - ^{0}\mathbf{X}_{n}) -$$ - -$$ -= \left[ \phi_{1} \quad \frac{\xi^{3}}{2} h_{1} \phi_{1} \quad \phi_{2} \quad \frac{\xi^{3}}{2} h_{2} \phi_{2} \quad \phi_{3} \quad \frac{\xi^{3}}{2} h_{3} \phi_{3} \quad \phi_{4} \quad \frac{\xi^{3}}{2} h_{4} \phi_{4} \right] \begin{bmatrix} {}^{t} \mathbf{X}_{1} - {}^{0} \mathbf{X}_{1} \\ {}^{t} \mathbf{V}_{n}^{1} - {}^{0} \mathbf{V}_{n}^{1} \\ {}^{t} \mathbf{X}_{2} - {}^{0} \mathbf{X}_{2} \\ {}^{t} \mathbf{V}_{n}^{2} - {}^{0} \mathbf{V}_{n}^{2} \\ {}^{t} \mathbf{X}_{3} - {}^{0} \mathbf{X}_{3} \\ {}^{t} \mathbf{V}_{n}^{3} - {}^{0} \mathbf{V}_{n}^{3} \\ {}^{t} \mathbf{X}_{4} - {}^{0} \mathbf{X}_{4} \\ {}^{t} \mathbf{V}_{n}^{4} - {}^{0} \mathbf{V}_{n}^{4} \right] -$$ - -$$ -\begin{aligned} -&= \left[ \phi_{1} \quad \phi_{2} \quad \phi_{3} \quad \phi_{4} \right]_{\substack{t+\Delta t \\ t+\Delta t \\ t+\Delta t}}^{t+\Delta t} \frac{\mathbf{x}_{1} - {}^{0} \mathbf{X}_{1}}{\mathbf{x}_{2} - {}^{0} \mathbf{X}_{2}} + \left[ \frac{\xi^{3}}{2} h_{1} \phi_{1} \quad \frac{\xi^{3}}{2} h_{2} \phi_{2} \quad \frac{\xi^{3}}{2} h_{3} \phi_{3} \quad \frac{\xi^{3}}{2} h_{4} \phi_{4} \right]_{\substack{t+\Delta t \\ t+\Delta t \\ t+\Delta t}}^{t+\Delta t} \frac{\mathbf{V}_{n}^{2} - {}^{0} \mathbf{V}_{n}^{2}}{\mathbf{V}_{n}^{2} - {}^{0} \mathbf{V}_{n}^{3}} \\ -&= \left[ \phi_{1} \quad \phi_{2} \quad \phi_{3} \quad \phi_{4} \right]_{\substack{t+\Delta t \\ t+\Delta t}}^{t+\Delta t} \frac{\mathbf{u}_{1}}{\mathbf{u}_{2}} + \left[ \frac{\xi^{3}}{2} h_{1} \phi_{1} \quad \frac{\xi^{3}}{2} h_{2} \phi_{2} \quad \frac{\xi^{3}}{2} h_{3} \phi_{3} \quad \frac{\xi^{3}}{2} h_{4} \phi_{4} \right]_{\substack{\Delta^{t} \mathbf{V}_{n}^{1} + \Delta^{0} \mathbf{V}_{n}^{1} \\ \Delta^{t} \mathbf{V}_{n}^{2} + \Delta^{0} \mathbf{V}_{n}^{3}}}^{t+\Delta t} \\ -&= \left[ \phi_{1} \quad \phi_{2} \quad \phi_{3} \quad \phi_{4} \right]_{\substack{t+\Delta t \\ t+\Delta t}}^{t+\Delta t} \frac{\mathbf{u}_{1}}{\mathbf{u}_{2}} + \left[ \frac{\xi^{3}}{2} h_{1} \phi_{1} \quad \frac{\xi^{3}}{2} h_{2} \phi_{2} \quad \frac{\xi^{3}}{2} h_{3} \phi_{3} \quad \frac{\xi^{3}}{2} h_{4} \phi_{4} \right]_{\substack{\Delta^{t} \mathbf{V}_{n}^{1} + \Delta^{0} \mathbf{V}_{n}^{1} \\ \Delta^{t} \mathbf{V}_{n}^{3} + \Delta^{0} \mathbf{V}_{n}^{3} \\ \Delta^{t} \mathbf{V}_{n}^{3} + \Delta^{0} \mathbf{V}_{n}^{3}} \\ -&= \left[ \phi_{1} \quad \phi_{2} \quad \phi_{3} \quad \phi_{4} \right]_{\substack{t+\Delta t \\ t+\Delta t}}^{t+\Delta t} \frac{\mathbf{u}_{1}}{\mathbf{u}_{2}} + \left[ \frac{\xi^{3}}{2} h_{1} \phi_{1} \quad \frac{\xi^{3}}{2} h_{2} \phi_{2} \quad \frac{\xi^{3}}{2} h_{3} \phi_{3} \quad \frac{\xi^{3}}{2} h_{4} \phi_{4} \right]_{\substack{\Delta^{t} \mathbf{V}_{n}^{3} + \Delta^{0} \mathbf{V}_{n}^{3} \\ \Delta^{t} \mathbf{V}_{n}^{3} + \Delta^{0} \mathbf{V}_{n}^{3}} \\ -&= \left[ \phi_{1} \quad \phi_{2} \quad \phi_{3} \quad \phi_{4} \right]_{\substack{t+\Delta t \\ t+\Delta t}}^{t+\Delta t} \frac{\mathbf{u}_{1}}{\mathbf{u}_{2}} + \left[ \frac{\xi^{3}}{2} h_{1} \phi_{1} \quad \frac{\xi^{3}}{2} h_{2} \phi_{2} \quad \frac{\xi^{3}}{2} h_{3} \phi_{3} \quad \frac{\xi^{3}}{2} h_{4} \phi_{4} \right]_{\substack{t+\Delta t \\ \Delta^{t} \mathbf{V}_{n}^{3} + \Delta^{0} \mathbf{V}_{n}^{3}}}^{t+\Delta t} \\ -&= \left[ \phi_{1} \quad \phi_{2} \quad \phi_{3} \quad \phi_{4} \right]_{\substack{t+\Delta t \\ t+\Delta t}}^{t+\Delta t} \mathbf{u}_{2} \\ -&= \left[ \phi_{1} \quad \phi_{2} \quad \phi_{3} \quad \phi_{4} \right]_{\substack{t+\Delta t \\ t+\Delta t}}^{t+\Delta t} \mathbf{u}_{2} \\ -&= \left[ \phi_{1} \quad \phi_{2} \quad \phi_{3} \quad \phi_{4} \right]_{\substack{t+\Delta t \\ t+\Delta t}}^{t+\Delta t} \mathbf{u}_{3} \\ -&= \left[ \phi_{1} \quad \phi_{2} \quad \phi_{3} \quad \phi_{4} \right]_{\substack{t+\Delta t \\ t+\Delta t}}^{t+\Delta t} \mathbf{u}_{3} \\ -&= \left[ \phi_{1} \quad \phi_{2} \quad \phi_{3} \quad \phi_{4} \right]_{\substack{t+\Delta t \\ t+\Delta t}}^{t+\Delta t} \mathbf{u}_{3} \\ -&= \left[ \phi_{1} \quad \phi_{2} \quad \phi_{3} \quad \phi_{4} \right]_{\substack{t+\Delta t \\ t+\Delta t}}^{t+\Delta t} \mathbf{u}_{3} \\ -&= \left[ \phi_{1} \quad \phi_{2} \quad \phi_{3} \quad \phi_{4} \right]_{\substack{t+\Delta t \\ t+\Delta t}} -$$ - -$$ -= \begin{bmatrix} \boldsymbol{\phi}_1 & \boldsymbol{\phi}_2 & \boldsymbol{\phi}_3 & \boldsymbol{\phi}_4 \end{bmatrix} \begin{bmatrix} t^{t+\Delta t} \mathbf{u}_1 \\ t^{t+\Delta t} \mathbf{u}_2 \\ t^{t+\Delta t} \mathbf{u}_3 \\ t^{t+\Delta t} \mathbf{u}_4 \end{bmatrix} + \begin{bmatrix} \underline{\xi}^3 \\ 2 \end{pmatrix} h_1 \boldsymbol{\phi}_1 & \underline{\xi}^3 \\ 2 \end{pmatrix} h_2 \boldsymbol{\phi}_2 & \underline{\xi}^3 \\ 2 \end{pmatrix} h_3 \boldsymbol{\phi}_3 & \underline{\xi}^3 \\ 2 \end{pmatrix} h_4 \boldsymbol{\phi}_4 \end{bmatrix} \begin{bmatrix} \Delta^t \mathbf{V}_n^1 + \Delta^0 \mathbf{V}_n^1 \\ \Delta^t \mathbf{V}_n^2 + \Delta^0 \mathbf{V}_n^2 \\ \Delta^t \mathbf{V}_n^3 + \Delta^0 \mathbf{V}_n^3 \\ \Delta^t \mathbf{V}_n^4 + \Delta^0 \mathbf{V}_n^4 \end{bmatrix} -$$ - -$$ -\left( t^{+\Delta t} \mathbf{V}_n^1 - {}^0 \mathbf{V}_n^1 = t^{+\Delta t} \mathbf{V}_n^1 - {}^t \mathbf{V}_n^1 + {}^t \mathbf{V}_n^1 - {}^0 \mathbf{V}_n^1 = \Delta^t \mathbf{V}_n^1 + \Delta^0 \mathbf{V}_n^1 \right) -$$ - -$$ -\begin{bmatrix} t^{+\Delta t} \mathbf{V}_{n}^{1} - {}^{0} \mathbf{V}_{n}^{1} = {}^{t+\Delta t} \mathbf{V}_{n}^{1} - {}^{t} \mathbf{V}_{n}^{1} + {}^{t} \mathbf{V}_{n}^{1} - {}^{0} \mathbf{V}_{n}^{1} = \Delta^{t} \mathbf{V}_{n}^{1} + \Delta^{0} \mathbf{V}_{n}^{1} \end{bmatrix} -= \begin{bmatrix} \phi_{1} & \phi_{2} & \phi_{3} & \phi_{4} \end{bmatrix} \begin{bmatrix} t^{+\Delta t} \mathbf{u}_{1} \\ t^{+\Delta t} \mathbf{u}_{2} \\ t^{+\Delta t} \mathbf{u}_{3} \\ t^{+\Delta t} \mathbf{u}_{4} \end{bmatrix} + \begin{bmatrix} \underline{\xi}^{3} \\ 2 \\ h_{1} \phi_{1} & \underline{\xi}^{3} \\ 2 \\ h_{2} \phi_{2} & \underline{\xi}^{3} \\ 2 \\ h_{3} \phi_{3} & \underline{\xi}^{3} \\ 2 \\ h_{3} \phi_{3} & \underline{\xi}^{3} \\ 2 \\ h_{4} \phi_{4} \end{bmatrix} \begin{bmatrix} -\alpha_{1}^{1t} \mathbf{V}_{2}^{1} + \alpha_{2}^{1t} \mathbf{V}_{1}^{1} \\ -\alpha_{1}^{2t} \mathbf{V}_{2}^{2} + \alpha_{2}^{2t} \mathbf{V}_{1}^{2} \\ -\alpha_{1}^{3t} \mathbf{V}_{2}^{3} + \alpha_{2}^{3t} \mathbf{V}_{1}^{3} \\ -\alpha_{1}^{4t} \mathbf{V}_{2}^{4} + \alpha_{2}^{4t} \mathbf{V}_{1}^{4} \end{bmatrix} -$$ - -$$ -+ \left[ \frac{\xi^{3}}{2} h_{1} \phi_{1} \quad \frac{\xi^{3}}{2} h_{2} \phi_{2} \quad \frac{\xi^{3}}{2} h_{3} \phi_{3} \quad \frac{\xi^{3}}{2} h_{4} \phi_{4} \right] \begin{bmatrix} \Delta^{0} \mathbf{V}_{n}^{1} \\ \Delta^{0} \mathbf{V}_{n}^{2} \\ \Delta^{0} \mathbf{V}_{n}^{3} \\ \Delta^{0} \mathbf{V}_{n}^{4} \end{bmatrix} -$$ - -$$ -\Rightarrow^{t+\Delta t} \mathbf{u} = {}^{t} \mathbf{N}^{t+\Delta t} \mathbf{u}_{n} + {}^{0} \tilde{\mathbf{N}} \Delta^{0} \tilde{\mathbf{X}}^{n} -$$ - -$$ -= \begin{bmatrix} \phi_{1} & -\frac{\xi^{3}}{2} h_{1} \phi_{1} \mathbf{v}_{2}^{1} & \frac{\xi^{3}}{2} h_{1} \phi_{1} \mathbf{v}_{1}^{1} & \phi_{2} & -\frac{\xi^{3}}{2} h_{2} \phi_{2} \mathbf{v}_{2}^{2} & \frac{\xi^{3}}{2} h_{2} \phi_{2} \mathbf{v}_{1}^{2} & \phi_{3} & -\frac{\xi^{3}}{2} h_{3} \phi_{3} \mathbf{v}_{2}^{3} & \frac{\xi^{3}}{2} h_{3} \phi_{3} \mathbf{v}_{1}^{3} & \phi_{4} & -\frac{\xi^{3}}{2} h_{4} \phi_{4} \mathbf{v}_{2}^{4} & \frac{\xi^{3}}{2} h_{4} \phi_{4} \mathbf{v}_{1}^{4} \end{bmatrix} \begin{bmatrix} \mathbf{\alpha}_{1}^{1} \\ \mathbf{\alpha}_{2}^{1} \\ \mathbf{\alpha}_{1}^{2} \\ \mathbf{\alpha}_{2}^{2} \\ t + \Delta t \mathbf{u}_{3} \\ \mathbf{\alpha}_{3}^{3} \\ \mathbf{\alpha}_{4}^{3} \\ \mathbf{\alpha}_{4}^{4} \\ \mathbf{\alpha}_{4}^{4} \\ \mathbf{\alpha}_{2}^{4} \end{bmatrix} -$$ diff --git a/docs/Paper/mitc공부/mitc공부_002.md b/docs/Paper/mitc공부/mitc공부_002.md deleted file mode 100644 index 8b3144a..0000000 --- a/docs/Paper/mitc공부/mitc공부_002.md +++ /dev/null @@ -1,80 +0,0 @@ -$$ -+ \left[ \frac{\xi^3}{2} \, h_1 \phi_1 \quad \frac{\xi^3}{2} \, h_2 \phi_2 \quad \frac{\xi^3}{2} \, h_3 \phi_3 \quad \frac{\xi^3}{2} \, h_4 \phi_4 \right] \begin{bmatrix} \Delta^0 \mathbf{V}_n^1 \\ \Delta^0 \mathbf{V}_n^2 \\ \Delta^0 \mathbf{V}_n^3 \\ \Delta^0 \mathbf{V}_n^4 \\ \Delta^0 \mathbf{V}_n^4 \end{bmatrix} -$$ - -## 3. FE Formulation - -현재 형상 $(t + \Delta t)$ 에서의 평형방정식은 다음과 같다. - -$$ -\nabla_{x} \cdot^{t+\Delta t} \mathbf{\sigma} + \rho^{t+\Delta t} \mathbf{f} = \rho^{t+\Delta t} \ddot{\mathbf{u}} -$$ - -가상일 원리를 적용하면 - -$$ -\begin{split} &\int_{V} \delta^{i+\Delta t} \mathbf{u} \cdot \left( \nabla_{X} \cdot^{i+\Delta t} \mathbf{\sigma} + \rho^{i+\Delta t} \mathbf{f} \right) dV = \int_{V} \delta^{i+\Delta t} \mathbf{u} \cdot \rho^{i+\Delta t} \mathbf{u} dV \\ &\Rightarrow \int_{V} \delta u_{i} \left( \frac{\partial \sigma_{ij}}{\partial x_{j}} + \rho f_{i} \right) dV = \int_{V} \delta u_{i} \cdot \rho \ddot{u}_{i} dV \\ &\Rightarrow \int_{V} \left\{ \frac{\partial \left( \delta u_{i} \sigma_{ij} \right)}{\partial x_{j}} - \frac{\partial \delta u_{i}}{\partial x_{j}} \sigma_{ij} + \delta u_{i} \rho f_{i} \right\} dV = \int_{V} \delta u_{i} \cdot \rho \ddot{u}_{i} dV \\ &\Rightarrow \int_{\partial V} \left\{ \frac{\partial \left( \delta u_{i} \sigma_{ij} \right)}{\partial x_{j}} - \frac{\partial \delta u_{i}}{\partial x_{j}} \sigma_{ij} + \delta u_{i} \rho f_{i} \right\} dV = \int_{V} \delta u_{i} \cdot \rho \ddot{u}_{i} dV \\ &\Rightarrow \int_{\partial V} \delta u_{i} \sigma_{ij} n_{j} dA + \int_{V} \left\{ -\frac{\partial \delta u_{i}}{\partial x_{j}} \sigma_{ij} + \delta u_{i} \rho f_{i} \right\} dV = \int_{V} \delta u_{i} \cdot \rho \ddot{u}_{i} dV \quad \left( \text{geometric } B.C \right) \\ &\Rightarrow \int_{\partial V_{x}} \delta u_{i} \sigma_{ij} n_{j} dA + \int_{\partial V_{m}} \delta u_{i} \overline{t}_{i} dA + \int_{V} \left\{ -\frac{\partial \delta u_{i}}{\partial x_{j}} \sigma_{ij} + \delta u_{i} \rho f_{i} \right\} dV = \int_{V} \delta u_{i} \cdot \rho \ddot{u}_{i} dV \\ &\left( \int_{\partial V_{x}} \delta u_{i} \sigma_{ij} n_{j} dA = 0, \ 7 \right) \vec{\sigma} \vec{\sigma} \vec{\sigma} \vec{\sigma} \vec{\sigma} \vec{\sigma} \vec{\sigma} \vec{\sigma} -$$ - -비선형 해석을 위해 하중을 조금씩 증가시켜 물체의 변형을 순차적으로 구해 나가며 이러한 반복계산에 있어 기준이 되는 물체의 형상을 설정하는 방법에는 크게 Total Lagrange formulation과 Updated Lagrange formulation이 있다. Total Lagrange formulation은 변형과 관련된 변수들을 초기형상을 이용하여 정의하고 Updated Lagrange formulation은 변수들을 현재 변형된 형상으로 정의한다. - -$$ -\begin{split} &\int_{V} \delta u_{i} \cdot \rho \ddot{u}_{i} dV + \int_{V} \delta \varepsilon_{ij} \sigma_{ij} dV = \int_{\partial V_{m}} \delta u_{i} \overline{t}_{i} dA + \int_{V} \delta u_{i} \rho f_{i} dV \\ &\left( \int_{V} (\bullet) dV = \int_{V_{0}} (\bullet) \det (\mathbf{F}) dV_{0} = \int_{V_{0}} (\bullet) J dV_{0} \right. \\ &\left. \int_{\partial V} (\bullet) \mathbf{n} dA = \int_{\partial V} (\bullet) J \mathbf{F}^{-T} \tilde{\mathbf{n}} dA \left( Nanson's \ formula \right) \right. \\ &\left. \int_{V_{0}} \delta \varepsilon_{ij} \sigma_{ij} dV_{0} = \int_{V_{0}} \delta F_{ij} P_{ij} dV_{0} = \int_{V_{0}} \delta E_{ij} S_{ij} dV_{0} \right. \\ &\Rightarrow \int_{V_{0}} \delta u_{i} \cdot \rho J \ddot{u}_{i} dV_{0} + \int_{V_{0}} \delta E_{ij} S_{ij} dV_{0} = \int_{\partial V_{0m}} \delta u_{i} \sigma_{ij} J \left[ F^{-T} \right]_{kj} \tilde{n}_{k} dA_{0} + \int_{V_{0}} \delta u_{i} \rho J f_{i} dV_{0} \\ &\Rightarrow \int_{V_{0}} \delta u_{i} \cdot \rho_{0} \ddot{u}_{i} dV_{0} + \int_{V_{0}} \delta E_{ij} S_{ij} dV_{0} = \int_{\partial V_{0m}} \delta u_{i} \sigma_{ij} J \left[ F^{-T} \right]_{kj} \tilde{n}_{k} dA_{0} + \int_{V_{0}} \delta u_{i} \rho_{0} f_{i} dV_{0} \\ &\Rightarrow \int_{V_{0}} \delta u_{i} \cdot \rho_{0} \ddot{u}_{i} dV_{0} + \int_{V_{0}} \delta E_{ij} S_{ij} dV_{0} = \int_{\partial V_{0m}} \delta u_{i} \sigma_{ij} J \left[ F^{-T} \right]_{kj} \tilde{n}_{k} dA_{0} + \int_{V_{0}} \delta u_{i} \rho_{0} f_{i} dV_{0} \\ &\Rightarrow \int_{V_{0}} \delta v_{i} \cdot \rho_{0} \ddot{u}_{i} dV_{0} + \int_{V_{0}} \delta E_{ij} S_{ij} dV_{0} = \int_{\partial V_{0m}} \delta u_{i} \sigma_{ij} J \left[ F^{-T} \right]_{kj} \tilde{n}_{k} dA_{0} + \int_{V_{0}} \delta u_{i} \rho_{0} f_{i} dV_{0} \\ &\Rightarrow \int_{V_{0}} \delta v_{i} \cdot \rho_{0} \ddot{u}_{i} dV_{0} + \int_{V_{0}} \delta E_{ij} S_{ij} dV_{0} = \int_{\partial V_{0m}} \delta u_{i} \sigma_{ij} J \left[ F^{-T} \right]_{kj} \tilde{n}_{k} dA_{0} + \int_{V_{0}} \delta u_{i} \rho_{0} f_{i} dV_{0} \\ &\Rightarrow \int_{V_{0}} \delta v_{i} \cdot \rho_{0} \ddot{u}_{i} dV_{0} + \int_{V_{0}} \delta v_{i} \dot{u} dV_{0} + \int_{V_{0}} \delta v_{i} \dot{u} dV_{0} + \int_{V_{0}} \delta v_{i} \dot{u} dV_{0} + \int_{V_{0}} \delta v_{i} \dot{u} dV_{0} + \int_{V_{0}} \delta v_{i} \dot{u} dV_{0} + \int_{V_{0}} \delta v_{i} \dot{u} dV_{0} + \int_{V_{0}} \delta v_{i} \dot{u} dV_{0} + \int_{V_{0}} \delta v_{i} \dot{u} dV_{0} + \int_{V_{0}} \delta v_{i} \dot{u} dV_{0} + \int_{V_{0}} \delta v_{i} \dot{u} dV_{0} + \int_{V_{0}} \delta v_{i} \dot{u} dV_{0} + \int_{V_{0}} \delta v_{i} \dot{u} dV_{0} + \int_{V_{0}} \delta v_{i} \dot{u} dV_{0} + \int_{V_{0}} \delta v_{i} \dot{u} dV_{0} + \int_{V_{0}} \delta v_{i} \dot{u} dV_{0} + \int_{V_{0}} \delta v_{i} \dot{u} dV_{0} + \int_{V_{0}} \delta v_{i} \dot{u} dV_{0} + \int_{V_{0}} \delta v_{i} \dot{u} dV_{0} + \int_{V_{0}} \delta v_{i} \dot{u} dV_{0} + \int_{V_{0}} \delta v_{i} \dot{u} dV_{0} + \int_{V_{0}} \delta v_{i} \dot{u -$$ - -위 식을 정리하면 다음과 같다. - -$$ -\begin{split} &\int_{V_0} \delta^{t+\Delta t} \mathbf{u} \cdot \boldsymbol{\rho_0}^{t+\Delta t} \ddot{\mathbf{u}} dV_0 + \int_{V_0} \delta^{t+\Delta t} \mathbf{E} \stackrel{t+\Delta t}{\circ} \mathbf{S} dV_0 = \int_{\partial V_{0m}} \delta^{t+\Delta t} \mathbf{u}^{t+\Delta t} \mathbf{f} \mathbf{f} dV_0 \mathbf{F}^{t+\Delta t} \tilde{\mathbf{n}} dA_0 + \int_{V_0} \delta^{t+\Delta t} \mathbf{u} \boldsymbol{\rho_0}^{t+\Delta t} \mathbf{f} dV_0 \\ & \Leftrightarrow \int_{V_0} \delta^{t+\Delta t} \mathbf{u} \cdot \boldsymbol{\rho_0}^{t+\Delta t} \ddot{\mathbf{u}} dV_0 + \int_{V_0} \delta^{t+\Delta t} \mathbf{F} \stackrel{t+\Delta t}{\circ} \mathbf{P} dV_0 = \int_{\partial V_{0m}} \delta^{t+\Delta t} \mathbf{u}^{t+\Delta t} \mathbf{f} \mathbf{f} dA_0 + \int_{V_0} \delta^{t+\Delta t} \mathbf{u} \boldsymbol{\rho_0}^{t+\Delta t} \mathbf{f} dV_0 \end{split} -$$ - -$\mathbf{E}$ 는 Green-Lagrange strain, $\mathbf{S}$ 는 $2^{nd}$ PK stress, $\mathbf{P}$ 는 $1^{st}$ PK stress, $\mathbf{F}$ 는 deformation gradient를 나타낸다. 여기에서는 Green-Lagrange strain과 $2^{nd}$ PK stress를 사용한다. - -$$ -\int_{V_0} \delta^{t+\Delta t} \mathbf{u} \cdot \rho_0^{t+\Delta t} \ddot{\mathbf{u}} dV_0 + \int_{V_0} \delta^{t+\Delta t} \mathbf{E} :_0^{t+\Delta t} \mathbf{S} dV_0 = \int_{\partial V_{0m}} \delta^{t+\Delta t} \mathbf{u}_0^{t+\Delta t} \mathbf{F}_0^{t+\Delta t} \mathbf{S}^{t+\Delta t} \tilde{\mathbf{n}} dA_0 + \int_{V_0} \delta^{t+\Delta t} \mathbf{u} \rho_0^{t+\Delta t} \mathbf{f} dV_0 -$$ - -먼저 좌변을 살펴보면 첫 번째 항을 다음과 같이 정리 할 수 있다. - -$$ -\begin{split} &\int_{V_0} \delta^{t+\Delta t} \mathbf{u} \cdot \rho^{t+\Delta t} \ddot{\mathbf{u}} J dV_0 = \int_{V_0} \delta \left( {}^t \mathbf{N}^{t+\Delta t} \mathbf{u}_n + {}^0 \tilde{\mathbf{N}} \Delta^0 \tilde{\mathbf{X}}_n \right) \cdot \rho^t \mathbf{N}^{t+\Delta t} \ddot{\mathbf{u}} J dV_0 \\ &= \int_{V_0} \delta \left( {}^t \mathbf{N}^{t+\Delta t} \mathbf{u}_n \right) \cdot \rho \left( {}^t \mathbf{N}^{t+\Delta t} \ddot{\mathbf{u}} \right) J dV_0 \\ &= \left[ \delta^{t+\Delta t} \mathbf{u}_n \right]^T \int_{V_0} \rho \left[ {}^t \mathbf{N} \right]^T \left[ {}^t \mathbf{N} \right] J dV_0 \left[ {}^{t+\Delta t} \ddot{\mathbf{u}}_n \right] \end{split} -$$ - -두 번째 항도 마찬가지로 다음과 같이 정리 할 수 있다. 먼저 Green-Lagrange strain은 다음과 같이 나타낼수 있다. - -$$ -\begin{split} & = \frac{1}{2} \left( \frac{t^{+\Delta l}}{{}_{0}} \mathbf{g}_{i} \cdot \frac{t^{+\Delta l}}{{}_{0}} \mathbf{g}_{j} - {}_{0} \mathbf{G}_{i} \cdot {}_{0} \mathbf{G}_{j} \right) \left( {}_{0} \mathbf{G}^{i} \otimes_{0} \mathbf{G}^{j} \right) \\ & = \frac{1}{2} \left( \frac{\partial^{t+\Delta l}}{\partial \xi^{i}} \mathbf{X} \cdot \frac{\partial^{t+\Delta l}}{\partial \xi^{j}} - \frac{\partial^{0} \mathbf{X}}{\partial \xi^{j}} \cdot \frac{\partial^{0} \mathbf{X}}{\partial \xi^{j}} \right) \left( {}_{0} \mathbf{G}^{i} \otimes_{0} \mathbf{G}^{j} \right) \\ & = \frac{1}{2} \left( \frac{\partial \left( {}^{0} \mathbf{X} + \frac{t+\Delta l}}{\partial \xi^{i}} \mathbf{u} \right)}{\partial \xi^{j}} \cdot \frac{\partial \left( {}^{0} \mathbf{X} + \frac{t+\Delta l}}{\partial \xi^{j}} \mathbf{u} \right)}{\partial \xi^{j}} - \frac{\partial^{0} \mathbf{X}}{\partial \xi^{j}} \cdot \frac{\partial^{0} \mathbf{X}}{\partial \xi^{j}} \right) \left( {}_{0} \mathbf{G}^{i} \otimes_{0} \mathbf{G}^{j} \right) \\ & = \frac{1}{2} \left( \frac{\partial^{0} \mathbf{X}}{\partial \xi^{j}} \cdot \frac{\partial^{0} \mathbf{X}}{\partial \xi^{j}} + \frac{\partial^{0} \mathbf{X}}{\partial \xi^{i}} \cdot \frac{\partial^{t+\Delta l}}{\partial \xi^{j}} \mathbf{u} + \frac{\partial^{t+\Delta l}}{\partial \xi^{j}} \mathbf{u} \cdot \frac{\partial^{0} \mathbf{X}}{\partial \xi^{j}} + \frac{\partial^{t+\Delta l}}{\partial \xi^{i}} \mathbf{u} \cdot \frac{\partial^{t+\Delta l}}{\partial \xi^{j}} \mathbf{u} \cdot \frac{\partial^{t+\Delta l}}{\partial \xi^{j}} \mathbf{u} - \frac{\partial^{0} \mathbf{X}}{\partial \xi^{j}} \cdot \frac{\partial^{0} \mathbf{X}}{\partial \xi^{j}} \right) \left( {}_{0} \mathbf{G}^{i} \otimes_{0} \mathbf{G}^{j} \right) \\ & = \frac{1}{2} \left( \frac{\partial^{0} \mathbf{X}}{\partial \xi^{j}} \cdot \frac{\partial \left( {}^{t} \mathbf{u} + \Delta^{t} \mathbf{u} \right)}{\partial \xi^{j}} + \frac{\partial \left( {}^{t} \mathbf{u} + \Delta^{t} \mathbf{u} \right)}{\partial \xi^{j}} \cdot \frac{\partial^{0} \mathbf{X}}{\partial \xi^{j}} + \frac{\partial \left( {}^{t} \mathbf{u} + \Delta^{t} \mathbf{u} \right)}{\partial \xi^{j}} \cdot \frac{\partial^{0} \mathbf{X}}{\partial \xi^{j}} \right) \left( {}_{0} \mathbf{G}^{i} \otimes_{0} \mathbf{G}^{j} \right) \\ & = \frac{1}{2} \left( \frac{\partial^{0} \mathbf{X}}{\partial \xi^{i}} \cdot \frac{\partial \left( {}^{t} \mathbf{u} + \Delta^{t} \mathbf{u} \right)}{\partial \xi^{j}} + \frac{\partial \left( {}^{t} \mathbf{u} + \Delta^{t} \mathbf{u} \right)}{\partial \xi^{j}} \cdot \frac{\partial^{0} \mathbf{X}}{\partial \xi^{j}} + \frac{\partial \left( {}^{t} \mathbf{u} + \Delta^{t} \mathbf{u} \right)}{\partial \xi^{j}} \right) \left( {}_{0} \mathbf{G}^{i} \otimes_{0} \mathbf{G}^{j} \right) \\ & = \frac{1}{2} \left( \frac{\partial^{0} \mathbf{X}}{\partial \xi^{i}} \cdot \frac{\partial \left( {}^{t} \mathbf{u} + \Delta^{t} \mathbf{u} \right)}{\partial \xi^{j}} + \frac{\partial \left( {}^{t} \mathbf{u} + \Delta^{t} \mathbf{u} \right)}{\partial \xi^{j}} \cdot \frac{\partial^{0} \mathbf{X}}{\partial \xi^{j}} \right) \left( {}_{0} \mathbf{G}^{i} \otimes_{0} \mathbf{G}^{j} \right) \\ & = \frac{1}{2} \left( \frac{\partial^{0} \mathbf{X}}{\partial \xi^{i}} \cdot \frac{\partial \left( {}^{t} \mathbf{u} + \Delta^{t} \mathbf{u} \right)}{\partial \xi^{j}} + \frac{\partial \left( {}^{t} \mathbf{u} + \Delta^{t} \mathbf{u} \right)}{\partial \xi^{j}} \cdot \frac{\partial^{0} \mathbf{X}}{\partial \xi^{j}} \right) \left( {}_{0} \mathbf{G}^{i} \otimes_{0} \mathbf{G}^{j} \right) \right) \\ & = \frac{1}{2} \left( \frac{\partial^{0} \mathbf{X}}{\partial \xi^{i}} \cdot \frac{\partial \left( {}^{t} \mathbf{u} + \Delta^{t} \mathbf{u} \right)}{\partial \xi^{j}} + \frac{\partial \left( {}^{t} \mathbf{u} + \Delta^{t} \mathbf{u} \right)}{\partial \xi^{j}} \right) \left( {}_{0} \mathbf{G}^{i} \otimes_{0} \mathbf{G}^{j} \right) \right) -$$ - -정리하면 Green-Lagrange strain을 $\Delta \mathbf{u}$ 에 대한 상수 term, 선형 term, 비선형 term으로 나눌 수 있다. - -$$ -\begin{split} & \overset{t+\Delta t}{{}_{0}}\mathbf{E} = \overset{t+\Delta t}{{}_{0}}\mathbf{E}_{0} + \overset{t+\Delta t}{{}_{0}}\mathbf{E}_{C} + \overset{t+\Delta t}{{}_{0}}\mathbf{E}_{L} \\ & \left( \overset{t+\Delta t}{{}_{0}}\mathbf{E}_{0} = \frac{1}{2} \left( \frac{\partial^{0}\mathbf{X}}{\partial \boldsymbol{\xi}^{i}} \cdot \frac{\partial^{t}\mathbf{u}}{\partial \boldsymbol{\xi}^{j}} + \frac{\partial^{t}\mathbf{u}}{\partial \boldsymbol{\xi}^{i}} \cdot \frac{\partial^{0}\mathbf{X}}{\partial \boldsymbol{\xi}^{j}} + \frac{\partial^{t}\mathbf{u}}{\partial \boldsymbol{\xi}^{i}} \cdot \frac{\partial^{t}\mathbf{u}}{\partial \boldsymbol{\xi}^{j}} \right) \left( {}_{0}\mathbf{G}^{i} \otimes_{0} \mathbf{G}^{j} \right) \\ & \overset{t+\Delta t}{{}_{0}}\mathbf{E}_{C} = \frac{1}{2} \left( \frac{\partial^{t}\mathbf{u}}{\partial \boldsymbol{\xi}^{i}} \cdot \frac{\partial \Delta^{t}\mathbf{u}}{\partial \boldsymbol{\xi}^{j}} + \frac{\partial^{0}\mathbf{X}}{\partial \boldsymbol{\xi}^{i}} \cdot \frac{\partial \Delta^{t}\mathbf{u}}{\partial \boldsymbol{\xi}^{j}} + \frac{\partial \Delta^{t}\mathbf{u}}{\partial \boldsymbol{\xi}^{i}} \cdot \frac{\partial^{0}\mathbf{X}}{\partial \boldsymbol{\xi}^{j}} + \frac{\partial \Delta^{t}\mathbf{u}}{\partial \boldsymbol{\xi}^{j}} \cdot \frac{\partial^{0}\mathbf{X}}{\partial \boldsymbol{\xi}^{j}} \cdot \frac{\partial^{t}\mathbf{u}}{\partial \boldsymbol{\xi}^{j}} \right) \left( {}_{0}\mathbf{G}^{i} \otimes_{0} \mathbf{G}^{j} \right) \\ & \overset{t+\Delta t}{{}_{0}}\mathbf{E}_{L} = \frac{1}{2} \left( \frac{\partial \Delta^{t}\mathbf{u}}{\partial \boldsymbol{\xi}^{i}} \cdot \frac{\partial \Delta^{t}\mathbf{u}}{\partial \boldsymbol{\xi}^{j}} \right) \left( {}_{0}\mathbf{G}^{i} \otimes_{0} \mathbf{G}^{j} \right) \end{split} -$$ - -와 같다. 두 번째 항을 위의 표현으로 나타내면 - -$$ -\begin{split} &\int_{V_0} \mathcal{S}^{t+\Delta t} \mathbf{E} \stackrel{t+\Delta t}{\circ} \mathbf{S} dV_0 = \int_{V_0} \mathcal{S}^{t+\Delta t} \mathbf{E} \stackrel{t+\Delta t}{\circ} \mathbf{C} \stackrel{t+\Delta t}{\circ} \mathbf{E} dV_0 \\ &= \int_{V_0} \underbrace{\left( \underbrace{\mathcal{S}^{t+\Delta t}}_{0} \mathbf{E}_0 + \mathcal{S}^{t+\Delta t}_{0} \mathbf{E}_C + \mathcal{S}^{t+\Delta t}_{0} \mathbf{E}_L \right)}_{:t+\Delta t} \stackrel{t+\Delta t}{\circ} \mathbf{C} \stackrel{t+\Delta t}{\circ} \mathbf{E}_0 + \underbrace{t+\Delta t}_{0} \mathbf{E}_C + \underbrace{t+\Delta t}_{0} \mathbf{E}_L \right) dV_0 \\ &= \int_{V_0} \underbrace{\underbrace{\mathcal{S}^{t+\Delta t}}_{0} \mathbf{E}_C \stackrel{t+\Delta t}{\circ} \mathbf{C} \stackrel{t+\Delta t}{\circ} \mathbf{E}_0}_{\text{constant term}} \mathbf{E}_0 dV_0 + \int_{V_0} \underbrace{\underbrace{\mathcal{S}^{t+\Delta t}}_{0} \mathbf{E}_C \stackrel{t+\Delta t}{\circ} \mathbf{C} \stackrel{t+\Delta t}{\circ} \mathbf{E}_C + \mathcal{S}^{t+\Delta t}_{0} \mathbf{E}_L \stackrel{t+\Delta t}{\circ} \mathbf{C} \stackrel{t+\Delta t}{\circ} \mathbf{E}_D}_{\text{linear term}} dV_0 \\ &+ \int_{V_0} \underbrace{\underbrace{\mathcal{S}^{t+\Delta t}}_{0} \mathbf{E}_C \stackrel{t+\Delta t}{\circ} \mathbf{C} \stackrel{t+\Delta t}{\circ} \mathbf{E}_L + \mathcal{S}^{t+\Delta t}_{0} \mathbf{E}_L \stackrel{t+\Delta t}{\circ} \mathbf{C} \stackrel{t+\Delta t}{\circ} \mathbf{E}_C + \mathcal{S}^{t+\Delta t}_{0} \mathbf{E}_L \stackrel{t+\Delta t}{\circ} \mathbf{C} \stackrel{t+\Delta t}{\circ} \mathbf{E}_L}_{0} dV_0}_{\text{high order term}} \end{split} -$$ - -와 같다. 이후 선형화 시키고(High order term 무시) component형태로 나타내면 아래와 같다. - -$$ -\int_{V_{0}} \underbrace{\int_{V_{0}}^{t+\Delta t} \mathbf{E}_{C} :_{0}^{t+\Delta t} \mathbf{E}_{C} :_{0}^{t+\Delta t} \mathbf{E}_{0}}_{\text{constant term}} dV_{0} + \int_{V_{0}} \underbrace{\int_{0}^{t+\Delta t} \mathbf{E}_{C} :_{0}^{t+\Delta t} \mathbf{E}_{C} :_{0}^{t+\Delta t} \mathbf{E}_{C} + \int_{0}^{t+\Delta t} \mathbf{E}_{L} :_{0}^{t+\Delta t} \mathbf{E}_{L} :_{0}^{t+\Delta t} \mathbf{E}_{0}}_{\text{linear term}} dV_{0} -= \int_{V_{0}} \underbrace{\left[\int_{0}^{t+\Delta t} \mathbf{E}_{C}\right]_{ij} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{C} \end{bmatrix}_{ij}^{ijkl} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{E}_{C} \end{bmatrix}_{kl} dV_{0}}_{\text{constant term}} -+ \int_{V_{0}} \underbrace{\left[\int_{0}^{t+\Delta t} \mathbf{E}_{C}\right]_{ij} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{C} \end{bmatrix}_{ij}^{ijkl} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{E}_{C} \end{bmatrix}_{kl} + \begin{bmatrix} \int_{0}^{t+\Delta t} \mathbf{E}_{L} \end{bmatrix}_{ij} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{E}_{L} \end{bmatrix}_{ij} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{E}_{L} \end{bmatrix}_{ij} dV_{0}}_{\text{linear term}} -= \int_{V_{0}} \underbrace{\left[\int_{0}^{t+\Delta t} \mathbf{E}_{C}\right]_{ij} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{E}_{C} \end{bmatrix}_{ij} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{E}_{C} \end{bmatrix}_{ij} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{E}_{C} \end{bmatrix}_{ij} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{E}_{C} \end{bmatrix}_{ij} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{E}_{C} \end{bmatrix}_{ij} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{E}_{C} \end{bmatrix}_{ij} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{E}_{C} \end{bmatrix}_{ij} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{E}_{C} \end{bmatrix}_{ij} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{E}_{C} \end{bmatrix}_{ij} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{E}_{C} \end{bmatrix}_{ij} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{E}_{C} \end{bmatrix}_{ij} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{E}_{C} \end{bmatrix}_{ij} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{E}_{C} \end{bmatrix}_{ij} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{E}_{C} \end{bmatrix}_{ij} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{E}_{C} \end{bmatrix}_{ij} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{E}_{C} \end{bmatrix}_{ij} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{E}_{C} \end{bmatrix}_{ij} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{E}_{C} \end{bmatrix}_{ij} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{E}_{C} \end{bmatrix}_{ij} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{E}_{C} \end{bmatrix}_{ij} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{E}_{C} \end{bmatrix}_{ij} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{E}_{C} \end{bmatrix}_{ij} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{E}_{C} \end{bmatrix}_{ij} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{E}_{C} \end{bmatrix}_{ij} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{E}_{C} \end{bmatrix}_{ij} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{E}_{C} \end{bmatrix}_{ij} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{E}_{C} \end{bmatrix}_{ij} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{E}_{C} \end{bmatrix}_{ij} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{E}_{C} \end{bmatrix}_{ij} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{E}_{C} \end{bmatrix}_{ij} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{E}_{C} \end{bmatrix}_{ij} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{E}_{C} \end{bmatrix}_{ij} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{E}_{C} \end{bmatrix}_{ij} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{E}_{C} \end{bmatrix}_{ij} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{E}_{C} \end{bmatrix}_{ij} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{E}_{C} \end{bmatrix}_{ij} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{E}_{C} \end{bmatrix}_{ij} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{E}_{C} \end{bmatrix}_{ij} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{E}_{C} \end{bmatrix}_{ij} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{E}_{C} \end{bmatrix}_{ij} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{E}_{C} \end{bmatrix}_{ij} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{E}_{C} \end{bmatrix}_{ij} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{E}_{C} \end{bmatrix}_{ij} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{E}_{C} \end{bmatrix}_{ij} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{E}_{C} \end{bmatrix}_{ij} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{E}_{C} \end{bmatrix}_{ij} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{E}_{C} \end{bmatrix}_{ij} \begin{bmatrix} t+\Delta t \\ 0 \mathbf{E}_{C} \end{bmatrix}_{ij} \begin{bmatrix} t+\Delta -$$ - -Green-Lagrange strain의 변분을 구해보면 다음과 같다. 변분은 incremental displacement에만 적용되는데 이는 incremental displacement가 정의되지 않았기 때문이다. 따라서 $\delta^{t+\Delta t}\mathbf{u} = \delta \begin{pmatrix} t \mathbf{u} + \Delta^t \mathbf{u} \end{pmatrix} = \delta \Delta^t \mathbf{u}$ 이고 $\delta^{t+\Delta t}\mathbf{e} = 0$ 이 성립한다. - -$$ -\begin{split} & \mathcal{S}^{t+\Delta t}_{\phantom{t}0}\mathbf{E}_{C} = \frac{1}{2} \Bigg( \frac{\partial^{0}\mathbf{X}}{\partial \boldsymbol{\xi}^{i}} \cdot \frac{\partial \mathcal{S}^{t+\Delta t}\mathbf{u}}{\partial \boldsymbol{\xi}^{j}} + \frac{\partial \mathcal{S}^{t+\Delta t}\mathbf{u}}{\partial \boldsymbol{\xi}^{i}} \cdot \frac{\partial^{0}\mathbf{X}}{\partial \boldsymbol{\xi}^{j}} + \frac{\partial \mathcal{S}^{t+\Delta t}\mathbf{u}}{\partial \boldsymbol{\xi}^{i}} \cdot \frac{\partial^{0}\mathbf{X}}{\partial \boldsymbol{\xi}^{j}} + \frac{\partial \mathcal{S}^{t+\Delta t}\mathbf{u}}{\partial \boldsymbol{\xi}^{i}} \cdot \frac{\partial^{t+\Delta t}\mathbf{u}}{\partial \boldsymbol{\xi}^{i}} \cdot \frac{\partial \mathcal{S}^{t+\Delta t}\mathbf{u}}{\partial \boldsymbol{\xi}^{i}} - \frac{\partial \mathcal{S}^{t+\Delta t}\mathbf{u}}{\partial \boldsymbol{\xi}^{j}} \Bigg) \Big( {}_{0}\mathbf{G}^{i} \otimes_{0}\mathbf{G}^{j} \Big) \\ & \mathcal{S}^{t+\Delta t}_{\phantom{t}0}\mathbf{E}_{L} = \frac{1}{2} \Bigg( \frac{\partial \mathcal{S}^{t+\Delta t}\mathbf{u}}{\partial \boldsymbol{\xi}^{i}} \cdot \frac{\partial \Delta_{0}\mathbf{u}}{\partial \boldsymbol{\xi}^{j}} + \frac{\partial \Delta_{0}\mathbf{u}}{\partial \boldsymbol{\xi}^{i}} \cdot \frac{\partial \mathcal{S}^{t+\Delta t}\mathbf{u}}{\partial \boldsymbol{\xi}^{j}} \Bigg) \Big( {}_{0}\mathbf{G}^{i} \otimes_{0}\mathbf{G}^{j} \Big) \end{split} -$$ - -Component form으로 나타내면 - -$$ -\begin{split} & \begin{bmatrix} \iota^{+\Delta t} & \mathbf{E}_0 \end{bmatrix}_{ij} = \frac{1}{2} \left( \frac{\partial^0 \mathbf{X}}{\partial \xi^i} \cdot \frac{\partial^t \mathbf{u}}{\partial \xi^j} + \frac{\partial^t \mathbf{u}}{\partial \xi^j} \cdot \frac{\partial^0 \mathbf{X}}{\partial \xi^j} + \frac{\partial^t \mathbf{u}}{\partial \xi^j} \cdot \frac{\partial^t \mathbf{u}}{\partial \xi^j} \right) \\ & = \begin{bmatrix} {}^0 \mathbf{X}_n \end{bmatrix}^T \frac{1}{2} \left\{ \begin{bmatrix} \frac{\partial^0 \mathbf{N}}{\partial \xi^i} \end{bmatrix}^T \begin{bmatrix} \frac{\partial^0 \mathbf{N}}{\partial \xi^j} \end{bmatrix} + \begin{bmatrix} \frac{\partial^0 \mathbf{N}}{\partial \xi^j} \end{bmatrix}^T \begin{bmatrix} \frac{\partial^0 \mathbf{N}}{\partial \xi^j} \end{bmatrix} \right\} \begin{bmatrix} t \mathbf{X}_n - \mathbf{0} \mathbf{X}_n \end{bmatrix} \\ & + \frac{1}{2} \begin{bmatrix} t \mathbf{X}_n - \mathbf{0} \mathbf{X}_n \end{bmatrix}^T \begin{bmatrix} \frac{\partial^0 \mathbf{N}}{\partial \xi^j} \end{bmatrix}^T \begin{bmatrix} \frac{\partial^0 \mathbf{N}}{\partial \xi^j} \end{bmatrix} \begin{bmatrix} t \mathbf{X}_n - \mathbf{0} \mathbf{X}_n \end{bmatrix} \\ & + \frac{1}{2} \begin{bmatrix} t \mathbf{X}_n - \mathbf{0} \mathbf{X}_n \end{bmatrix}^T \begin{bmatrix} \frac{\partial^0 \mathbf{N}}{\partial \xi^j} \end{bmatrix} + \begin{bmatrix} \frac{\partial^0 \mathbf{N}}{\partial \xi^j} \end{bmatrix}^T \begin{bmatrix} \frac{\partial^0 \mathbf{N}}{\partial \xi^j} \end{bmatrix} \right\} \begin{bmatrix} t \mathbf{X}_n - \mathbf{0} \mathbf{X}_n \end{bmatrix} \\ & + \frac{1}{2} \begin{bmatrix} t \mathbf{X}_n - \mathbf{0} \mathbf{X}_n \end{bmatrix}^T \frac{1}{2} \left\{ \begin{bmatrix} \frac{\partial^0 \mathbf{N}}{\partial \xi^j} \end{bmatrix}^T \begin{bmatrix} \frac{\partial^0 \mathbf{N}}{\partial \xi^j} \end{bmatrix} + \begin{bmatrix} \frac{\partial^0 \mathbf{N}}{\partial \xi^j} \end{bmatrix}^T \begin{bmatrix} \frac{\partial^0 \mathbf{N}}{\partial \xi^j} \end{bmatrix} \right\} \begin{bmatrix} t \mathbf{X}_n - \mathbf{0} \mathbf{X}_n \end{bmatrix} \\ & + \frac{1}{2} \begin{bmatrix} t \mathbf{X}_n + \mathbf{0} \mathbf{X}_n \end{bmatrix}^T \frac{1}{2} \left\{ \begin{bmatrix} \frac{\partial^0 \mathbf{N}}{\partial \xi^j} \end{bmatrix}^T \begin{bmatrix} \frac{\partial^0 \mathbf{N}}{\partial \xi^j} \end{bmatrix} + \begin{bmatrix} \frac{\partial^0 \mathbf{N}}{\partial \xi^j} \end{bmatrix}^T \begin{bmatrix} \frac{\partial^0 \mathbf{N}}{\partial \xi^j} \end{bmatrix} \right\} \begin{bmatrix} t \mathbf{X}_n - \mathbf{0} \mathbf{X}_n \end{bmatrix} \\ & = \frac{1}{2} \begin{bmatrix} t \mathbf{X}_n + \mathbf{0} \mathbf{X}_n \end{bmatrix}^T \frac{1}{2} \left\{ \begin{bmatrix} \frac{\partial^0 \mathbf{N}}{\partial \xi^j} \\ \frac{\partial \xi^j}{\partial \xi^j} \end{bmatrix} + \frac{\partial^0 \mathbf{N}}{\partial \xi^j} + \frac{\partial^0 \mathbf{N}}{\partial \xi^j} \right\} + \frac{\partial^0 \mathbf{N}}{\partial \xi^j} \cdot \frac{\partial^0 \mathbf{N}}{\partial \xi^j} + \frac{\partial^0 \mathbf{N}}{\partial \xi^j} \cdot \frac{\partial^0 \mathbf{N}}{\partial \xi^j} \right\} \begin{bmatrix} t \mathbf{X}_n - \mathbf{0} \mathbf{X}_n \end{bmatrix} \\ & = \begin{bmatrix} t \mathbf{X}_n \end{bmatrix}^T \frac{1}{2} \left\{ \begin{bmatrix} \frac{\partial^0 \mathbf{N}}{\partial \xi^j} \end{bmatrix}^T \begin{bmatrix} \frac{\partial^0 \mathbf{N}}{\partial \xi^j} \end{bmatrix} + \begin{bmatrix} \frac{\partial^0 \mathbf{N}}{\partial \xi^j} \end{bmatrix} + \begin{bmatrix} \frac{\partial^0 \mathbf{N}}{\partial \xi^j} \end{bmatrix} \right\} \begin{bmatrix} \Delta^t \mathbf{u}_n \end{bmatrix} \\ & + \begin{bmatrix} t \mathbf{X}_n - \mathbf{0} \mathbf{X}_n \end{bmatrix}^T \frac{1}{2} \left\{ \begin{bmatrix} \frac{\partial^0 \mathbf{N}}{\partial \xi^j} \end{bmatrix}^T \begin{bmatrix} \frac{\partial^0 \mathbf{N}}{\partial \xi^j} \end{bmatrix} + \begin{bmatrix} \frac{\partial^0 \mathbf{N}}{\partial \xi^j} \end{bmatrix} + \begin{bmatrix} \frac{\partial^0 \mathbf{N}}{\partial \xi^j} \end{bmatrix} \right\} \begin{bmatrix} \Delta^t \mathbf{u}_n \end{bmatrix} \\ & = \begin{bmatrix} t \mathbf{X}_n \end{bmatrix}^T \frac{1}{2} \left\{ \begin{bmatrix} \frac{\partial^0 \mathbf{N}}{\partial \xi^j} \end{bmatrix}^T \begin{bmatrix} \frac{\partial^0 \mathbf{N}}{\partial \xi^j} \end{bmatrix} + \begin{bmatrix} \frac{\partial^0 \mathbf{N}}{\partial \xi^j} \end{bmatrix}^T \begin{bmatrix} \frac{\partial^0 \mathbf{N}}{\partial \xi^j} \end{bmatrix} \right\} \begin{bmatrix} \Delta^t \mathbf{u}_n \end{bmatrix} \\ & = \begin{bmatrix} t \mathbf{N}_n \end{bmatrix}_n \end{bmatrix} \begin{bmatrix} \frac{\partial^0 \mathbf{N}}{\partial \xi^j} \end{bmatrix} \begin{bmatrix} \frac{\partial^0 \mathbf{N}}{\partial \xi^j} \end{bmatrix} + \begin{bmatrix} \frac{\partial^0 \mathbf{N}}{\partial \xi^j} \end{bmatrix} \begin{bmatrix} \frac{\partial^0 \mathbf{N}}{\partial \xi^j} \end{bmatrix} \end{bmatrix} \begin{bmatrix} \Delta^t \mathbf{u}_n \end{bmatrix} \\ & = \begin{bmatrix} t \mathbf{N}_n \end{bmatrix}_n \end{bmatrix} \begin{bmatrix} \frac{\partial^0 \mathbf{N}}{\partial \xi^j} \end{bmatrix} \begin{bmatrix} \frac{\partial^0 \mathbf{N}}{\partial \xi^j} \end{bmatrix} \begin{bmatrix} \frac{\partial^0 \mathbf{N}}{\partial \xi^j} \end{bmatrix} \begin{bmatrix} \frac{\partial^0 \mathbf{N}}{\partial \xi^j} \end{bmatrix} \begin{bmatrix} \frac{\partial^0 \mathbf{N}}{\partial \xi^j} \end{bmatrix} \begin{bmatrix} \frac{\partial^0 \mathbf{N}}{\partial \xi^j} \end{bmatrix} \begin{bmatrix} \frac{\partial^0 \mathbf{N}}{\partial \xi^j} \end{bmatrix} \begin{bmatrix} \frac{\partial^0 \mathbf{N}}{\partial \xi^j} \end{bmatrix} \begin{bmatrix} \frac{\partial^0 \mathbf{N}}{\partial \xi^j} \end{bmatrix} \begin{bmatrix} \frac{\partial^ -$$ diff --git a/docs/Paper/mitc공부/mitc공부_003.md b/docs/Paper/mitc공부/mitc공부_003.md deleted file mode 100644 index f9166cf..0000000 --- a/docs/Paper/mitc공부/mitc공부_003.md +++ /dev/null @@ -1,123 +0,0 @@ -$$ -\begin{split} &\left[\mathcal{S}^{t+\Delta t}_{\phantom{t}0}\mathbf{E}_{c}\right]_{ij} = \frac{1}{2} \left(\frac{\partial^{0}\mathbf{X}}{\partial \xi^{i}} \cdot \frac{\partial \mathcal{S}^{t+\Delta t}\mathbf{u}}{\partial \xi^{j}} + \frac{\partial \mathcal{S}^{t+\Delta t}\mathbf{u}}{\partial \xi^{i}} \cdot \frac{\partial^{0}\mathbf{X}}{\partial \xi^{j}} + \frac{\partial \mathcal{S}^{t+\Delta t}\mathbf{u}}{\partial \xi^{i}} \cdot \frac{\partial^{t+\Delta t}\mathbf{u}}{\partial \xi^{j}} + \frac{\partial^{t+\Delta t}\mathbf{u}}{\partial \xi^{j}} \cdot \frac{\partial^{t}\mathbf{X}^{t+\Delta t}\mathbf{u}}{\partial \xi^{j}} \right) \\ &= \frac{1}{2} \begin{pmatrix} \frac{\partial^{0}\mathbf{X}}{\partial \xi^{i}} \cdot \frac{\partial \mathcal{S}\left({}^{t}\mathbf{N}^{t+\Delta t}\mathbf{u}_{n} + {}^{0}\tilde{\mathbf{N}}\Delta^{0}\tilde{\mathbf{X}}_{n}\right)}{\partial \xi^{j}} + \frac{\partial \mathcal{S}\left({}^{t}\mathbf{N}^{t+\Delta t}\mathbf{u}_{n} + {}^{0}\tilde{\mathbf{N}}\Delta^{0}\tilde{\mathbf{X}}_{n}\right)}{\partial \xi^{j}} + \frac{\partial^{t}\mathbf{X}^{t}\mathbf{u}}{\partial \xi^{j}} \cdot \frac{\partial^{t}\mathbf{X}^{t}\mathbf{u}_{n}}{\partial \xi^{j}} + \frac{\partial^{t}\mathbf{X}^{t}\mathbf{u}_{n}}{\partial \xi^{j}} \cdot \frac{\partial^{0}\mathbf{X}}{\partial \xi^{j}} \\ &+ \frac{\partial^{t}\mathbf{X}^{t}\mathbf{u}_{n}}{\partial \xi^{j}} \cdot \frac{\partial^{t}\mathbf{X}^{t}\mathbf{u}_{n}}{\partial \xi^{j}} + \frac{\partial^{t}\mathbf{X}^{t}\mathbf{u}}{\partial \xi^{j}} + \frac{\partial^{t}\mathbf{X}^{t}\mathbf{u}_{n}}{\partial \xi^{j}} \cdot \frac{\partial^{t}\mathbf{X}^{t}\mathbf{u}_{n}}{\partial \xi^{j}} + \frac{\partial^{t}\mathbf{X}^{t}\mathbf{u}_{n}}{\partial \xi^{j}} + \frac{\partial^{t}\mathbf{X}^{t}\mathbf{u}_{n}}{\partial \xi^{j}} + \frac{\partial^{t}\mathbf{X}^{t}\mathbf{u}_{n}}{\partial \xi^{j}} + \frac{\partial^{t}\mathbf{X}^{t}\mathbf{u}_{n}}{\partial \xi^{j}} + \frac{\partial^{t}\mathbf{X}^{t}\mathbf{u}_{n}}{\partial \xi^{j}} + \frac{\partial^{t}\mathbf{X}^{t}\mathbf{u}_{n}}{\partial \xi^{j}} + \frac{\partial^{t}\mathbf{X}^{t}\mathbf{u}_{n}}{\partial \xi^{j}} + \frac{\partial^{t}\mathbf{X}^{t}\mathbf{u}_{n}}{\partial \xi^{j}} + \frac{\partial^{t}\mathbf{X}^{t}\mathbf{u}_{n}}{\partial \xi^{j}} + \frac{\partial^{t}\mathbf{X}^{t}\mathbf{u}_{n}}{\partial \xi^{j}} + \frac{\partial^{t}\mathbf{X}^{t}\mathbf{u}_{n}}{\partial \xi^{j}} + \frac{\partial^{t}\mathbf{X}^{t}\mathbf{u}_{n}}{\partial \xi^{j}} + \frac{\partial^{t}\mathbf{X}^{t}\mathbf{u}_{n}}{\partial \xi^{j}} + \frac{\partial^{t}\mathbf{X}^{t}\mathbf{u}_{n}}{\partial \xi^{j}} + \frac{\partial^{t}\mathbf{X}^{t}\mathbf{u}_{n}}{\partial \xi^{j}} + \frac{\partial^{t}\mathbf{X}^{t}\mathbf{u}_{n}}{\partial \xi^{j}} + \frac{\partial^{t}\mathbf{X}^{t}\mathbf{u}_{n}}{\partial \xi^{j}} + \frac{\partial^{t}\mathbf{X}^{t}\mathbf{u}_{n}}{\partial \xi^{j}} + \frac{\partial^{t}\mathbf{X}^{t}\mathbf{u}_{n}}{\partial \xi^{j}} + \frac{\partial^{t}\mathbf{X}^{t}\mathbf{u}_{n}}{\partial \xi^{j}} + \frac{\partial^{t}\mathbf{X}^{t}\mathbf{u}_{n}}{\partial \xi^{j}} + \frac{\partial^{t}\mathbf{X}^{t}\mathbf{u}_{n}}{\partial \xi^{j}} + \frac{\partial^{t}\mathbf{X}^{t}\mathbf{u}_{n}}{\partial \xi^{j}} + \frac{\partial^{t}\mathbf{X}^{t}\mathbf{u}_{n}}{\partial \xi^{j}} + \frac{\partial^{t}\mathbf{X}^{t}\mathbf{u}_{n}}{\partial \xi^{j}} + \frac{\partial^{t}\mathbf{X}^{t}\mathbf{u}_{n}}{\partial \xi^{j}} + \frac{\partial^{t}\mathbf{X}^{t}\mathbf{u}_{n}}{\partial \xi^{j}} + \frac{\partial^{t}\mathbf{X}^{t}\mathbf{u}_{n}}{\partial \xi^{j}} + \frac{\partial^{t}\mathbf{X}^{t}\mathbf{u}_{n}}{\partial \xi^{j}} + \frac{\partial^{t}\mathbf{X}^{t}\mathbf{u}_{n}}{\partial \xi^{j}} + \frac{\partial^{t}\mathbf{X}^{t}\mathbf{u}_{n}}{\partial \xi^{j}} + \frac{\partial^{t}\mathbf{X}^{t}\mathbf{u}_{n}}{\partial \xi^{j}} + \frac{\partial^{t}\mathbf{X}^{t}\mathbf{u}_{n}}{\partial \xi^{j}} + \frac{\partial^{t}\mathbf{X}^{t}\mathbf{u}_{n}}{\partial \xi^{j}} + \frac{\partial^{t}\mathbf{X}^{t} -$$ - -다시 가상일 항으로 돌아와서 위에 구한 Green-Lagrange strain을 대입하면 - -$$ -\begin{split} &\int_{V_{0}} \left[ \underbrace{\delta^{t+\Delta t}}_{0} \mathbf{E}_{C} \right]_{ij} \left[ \begin{smallmatrix} t+\Delta t \\ 0 \mathbf{S}_{0} \end{smallmatrix} \right]^{y} dV_{0} + \int_{V_{0}} \left[ \underbrace{\delta^{t+\Delta t}}_{0} \mathbf{E}_{C} \right]_{ij} \left[ \begin{smallmatrix} t+\Delta t \\ 0 \mathbf{S}_{C} \end{smallmatrix} \right]^{y} + \left[ \underbrace{\delta^{t+\Delta t}}_{0} \mathbf{E}_{L} \right]_{ij} \left[ \begin{smallmatrix} t+\Delta t \\ 0 \mathbf{S}_{0} \end{smallmatrix} \right]^{y} dV_{0} \\ & \text{constant term} \end{split} -&\left[ \left[ \underbrace{\delta^{t+\Delta t}}_{0} \mathbf{E}_{C} \right]_{ij} \left[ \begin{smallmatrix} t+\Delta t \\ 0 \mathbf{S}_{0} \end{smallmatrix} \right]^{ij} = \left[ \underbrace{\delta^{t+\Delta t}}_{0} \mathbf{u}_{n} \right]^{T} \left[ \mathbf{a} \right]_{ij} \left[ \begin{smallmatrix} t \mathbf{x}_{n} \right] \left[ \begin{smallmatrix} t+\Delta t \\ 0 \mathbf{C} \end{smallmatrix} \right]^{ijkl} \frac{1}{2} \left[ \begin{smallmatrix} t \mathbf{x}_{n} + \mathbf{0} \mathbf{X}_{n} \right]^{T} \left[ \mathbf{e} \right]_{kl} \left[ \begin{smallmatrix} t \mathbf{x}_{n} - \mathbf{0} \mathbf{X}_{n} \right] \right] \\ \left[ \underbrace{\delta^{t+\Delta t}}_{0} \mathbf{E}_{C} \right]_{ij} \left[ \begin{smallmatrix} t+\Delta t \\ 0 \mathbf{S}_{C} \end{smallmatrix} \right]^{ij} = \left[ \underbrace{\delta^{t+\Delta t}}_{0} \mathbf{u}_{n} \right]^{T} \left[ \mathbf{a} \right]_{ij} \left[ \begin{smallmatrix} t \mathbf{x}_{n} \right] \left[ \begin{smallmatrix} t+\Delta t \\ 0 \mathbf{C} \end{smallmatrix} \right]^{ijkl} \left[ \begin{smallmatrix} t \mathbf{x}_{n} \right]^{T} \left[ \mathbf{a} \right]_{kl} \left[ \Delta^{t} \mathbf{u}_{n} \right] \right] \\ \left[ \underbrace{\delta^{t+\Delta t}}_{0} \mathbf{E}_{L} \right]_{ij} \left[ \begin{smallmatrix} t+\Delta t \\ 0 \mathbf{S}_{0} \end{smallmatrix} \right]^{ij} = \left[ \underbrace{\delta^{t+\Delta t}}_{0} \mathbf{u}_{n} \right]^{T} \left[ \mathbf{c} \right]_{ij} \left[ \Delta^{t} \mathbf{u}_{n} \right] \left[ \begin{smallmatrix} t+\Delta t \\ 0 \mathbf{C} \end{smallmatrix} \right]^{ijkl} \left[ \begin{smallmatrix} t \mathbf{x}_{n} + \mathbf{0} \mathbf{X}_{n} \right]^{T} \left[ \mathbf{e} \right]_{kl} \left[ \begin{smallmatrix} t \mathbf{x}_{n} - \mathbf{0} \mathbf{X}_{n} \right] \right] \\ = \left[ \underbrace{\delta^{t+\Delta t}}_{0} \mathbf{u}_{n} \right]^{T} \left\{ \left( \int_{V_{0}} \left[ \mathbf{a} \right]_{ij} \left[ \begin{smallmatrix} t+\Delta t \\ 0 \mathbf{S}_{0} \right]^{ij} dV_{0} \right) \left[ \begin{smallmatrix} t \mathbf{x}_{n} \right] \left[ \begin{smallmatrix} t+\Delta t \\ 0 \mathbf{C} \right]^{ijkl} \left[ \begin{smallmatrix} t \mathbf{x}_{n} \right]^{T} \left[ \mathbf{a} \right]_{kl} + \left[ \mathbf{c} \right]_{ij} \left[ \begin{smallmatrix} t+\Delta t \\ 0 \mathbf{S}_{0} \right]^{ij} dV_{0} \right) \left[ \Delta^{t} \mathbf{u}_{n} \right] \right] \\ + \left( \int_{V_{0}} \left[ \mathbf{a} \right]_{ij} \left[ \begin{smallmatrix} t \mathbf{x}_{n} \right] \left[ \begin{smallmatrix} t+\Delta t \\ 0 \mathbf{C} \right]^{ijkl} \left[ \begin{smallmatrix} t \mathbf{x}_{n} \right]^{T} \left[ \mathbf{a} \right]_{kl} + \left[ \mathbf{c} \right]_{ij} \left[ \begin{smallmatrix} t+\Delta t \\ 0 \mathbf{S}_{0} \right]^{ij} dV_{0} \right) \left[ \Delta^{t} \mathbf{u}_{n} \right] \right] \right] \end{aligned} -$$ - -와 같다. 이제 우변의 항들을 정리해보면 아래와 같다. 여기서 body force에 대한 영향은 무시한다. - -$$ -\begin{split} &\int_{\partial V_{0m}} \mathcal{S}^{t+\Delta t} \mathbf{u}^{t+\Delta t}_{0} \mathbf{F}^{t+\Delta t}_{0} \mathbf{S}^{t+\Delta t} \tilde{\mathbf{n}} dA_{0} = \int_{\partial V_{0m}} \mathcal{S} \left( {}^{t} \mathbf{N}^{t+\Delta t} \mathbf{u}_{n} + {}^{0} \tilde{\mathbf{N}} \Delta^{0} \tilde{\mathbf{X}}_{n} \right)^{t+\Delta t}_{0} \mathbf{F}^{t+\Delta t} \tilde{\mathbf{n}} dA_{0} \\ &= \int_{\partial V_{0m}} \mathcal{S} \left( {}^{t} \mathbf{N}^{t+\Delta t} \mathbf{u}_{n} \right)^{t+\Delta t}_{0} \mathbf{F}^{t+\Delta t}_{0} \mathbf{S}^{t+\Delta t} \tilde{\mathbf{n}} dA_{0} \\ &= \left[ \mathcal{S}^{t+\Delta t} \mathbf{u}_{n} \right]^{T} \int_{\partial V_{0m}} \left[ {}^{t} \mathbf{N} \right]^{Tt+\Delta t}_{0} \mathbf{F}^{t+\Delta t}_{0} \tilde{\mathbf{N}} dA_{0} \\ &= \left[ \mathcal{S}^{t+\Delta t} \mathbf{u}_{n} \right]^{T} \int_{\partial V_{0}} \left[ {}^{t} \mathbf{N} \right]^{T} [\mathbf{t}] dA_{0} \end{split} -$$ - -따라서 가상변위를 지워 모든 식을 정리하면 - -$$ -\begin{split} &\int_{V_{0}} \delta^{t+\Delta t} \mathbf{u} \cdot \boldsymbol{\rho}_{0}^{t+\Delta t} \ddot{\mathbf{u}} dV_{0} + \int_{V_{0}} \delta^{t+\Delta t}_{0} \mathbf{E} :^{t+\Delta t}_{0} \mathbf{S} dV_{0} = \int_{\partial V_{0m}} \delta^{t+\Delta t} \mathbf{u}^{t+\Delta t}_{0} \mathbf{F}^{t+\Delta t}_{0} \mathbf{S}^{t+\Delta t} \tilde{\mathbf{n}} dA_{0} + \int_{V_{0}} \delta^{t+\Delta t} \mathbf{u} \boldsymbol{\rho}_{0}^{t+\Delta t} \mathbf{f} dV_{0} \\ &\Rightarrow \underbrace{\int_{V_{0}} \boldsymbol{\rho} \begin{bmatrix} {}^{t} \mathbf{N} \end{bmatrix}^{T} \begin{bmatrix} {}^{t} \mathbf{N} \end{bmatrix} J dV_{0}}_{\mathbf{M}} {}^{t+\Delta t} \ddot{\mathbf{u}} + \underbrace{\int_{V_{0}} \left[ \mathbf{a} \right]_{ij} \begin{bmatrix} {}^{t+\Delta t}_{0} \mathbf{S}_{0} \end{bmatrix}^{ij} dV_{0}^{t} \mathbf{x}}_{\mathbf{f}_{int}} \\ &+ \underbrace{\int_{V_{0}} \left[ \mathbf{a} \right]_{ij} \begin{bmatrix} {}^{t} \mathbf{x}_{n} \end{bmatrix}^{T} [{}^{t} \mathbf{x}_{n} \end{bmatrix}^{T} [\mathbf{a}]_{kl} + \left[ \mathbf{c} \right]_{ij} \begin{bmatrix} {}^{t+\Delta t}_{0} \mathbf{S}_{0} \end{bmatrix}^{ij} dV_{0}^{t} \Delta^{t} \mathbf{u} = \underbrace{\int_{\partial V_{0m}} \left[ {}^{t} \mathbf{N} \right]^{T} [\mathbf{t}] dA_{0}}_{\mathbf{P}_{dist}} + \mathbf{P}_{con} \\ &\Rightarrow \mathbf{M}^{t+\Delta t} \ddot{\mathbf{u}} + \mathbf{K}_{t} \Delta^{t} \mathbf{u} = \mathbf{P}_{dist} + \mathbf{P}_{con} - \mathbf{f}_{int} \end{split} -$$ - -와 같이 정리할 수 있다. 여기서 $\mathbf{M}$ 은 mass matrix, $\mathbf{K}_{\iota}$ 는 tangent stiffness matrix, $\mathbf{P}_{dist}$ 는 분포하중에 의한 힘, $\mathbf{P}_{con}$ 는 집중하중, $\mathbf{f}_{int}$ 는 변형에 의한 힘을 나타낸다. - -## 4. Constitutive matrix - -Plane stress 가정을 사용하는 구성행렬은 다음과 같다. - -$$ -\begin{bmatrix} {}^{t+\Delta t} \mathbf{C} \end{bmatrix}_{x^1 x^2 x^3} = \frac{E}{1-\nu^2} \begin{bmatrix} 1 & \nu & 0 & 0 & 0 & 0 \\ n & 1 & 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 & 0 & 0 -$$ - -위의 구성 행렬은 local Cartesian coordinate에서 정의되었기 때문에 transformation matrix를 이용하여 natural coordinate로 바꾸어 줄 수 있다. - -$$ -\begin{bmatrix} t + \Delta t \\ 0 \end{bmatrix}_{\mathcal{E}^1 \mathcal{E}^2 \mathcal{E}^3} = \begin{bmatrix} t + \Delta t \\ 0 \end{bmatrix}^T \begin{bmatrix} t + \Delta t \\ 0 \end{bmatrix}_{x^1 x^2 x^3} \begin{bmatrix} t + \Delta t \\ 0 \end{bmatrix} -$$ - -이 때 전단보정계수 $\kappa$ 는 $\frac{5}{6}$ 를 사용하였다. - -$$ -E_{kl} = \tilde{E}_{mn} \underbrace{\left(\mathbf{E}_{k} \cdot \mathbf{G}^{m}\right) \left(\mathbf{E}_{l} \cdot \mathbf{G}^{n}\right)}_{=\mathbf{T}} = \tilde{E}_{mn} \left(\mathbf{E}_{k} \cdot \frac{\partial X^{a}}{\partial \xi^{m}} \mathbf{E}_{a}\right) \left(\mathbf{E}_{l} \cdot \frac{\partial X^{b}}{\partial \xi^{n}} \mathbf{E}_{b}\right) = \frac{\partial X^{k}}{\partial \xi^{m}} \frac{\partial X^{l}}{\partial \xi^{n}} -$$ - -$$ -[\mathbf{T}] = \begin{bmatrix} \frac{\partial X^{1}}{\partial \xi^{1}} \frac{\partial X^{1}}{\partial \xi^{1}} & \frac{\partial X^{1}}{\partial \xi^{2}} \frac{\partial X^{1}}{\partial \xi^{2}} & \frac{\partial X^{1}}{\partial \xi^{3}} \frac{\partial X^{1}}{\partial \xi^{3}} & \frac{\partial X^{1}}{\partial \xi^{2}} \frac{\partial X^{1}}{\partial \xi^{3}} & \frac{\partial X^{1}}{\partial \xi^{2}} \frac{\partial X^{1}}{\partial \xi^{3}} & \frac{\partial X^{1}}{\partial \xi^{2}} \frac{\partial X^{1}}{\partial \xi^{2}} \\ \frac{\partial X^{2}}{\partial \xi^{1}} \frac{\partial X^{2}}{\partial \xi^{1}} & \frac{\partial X^{2}}{\partial \xi^{2}} \frac{\partial X^{2}}{\partial \xi^{2}} & \frac{\partial X^{2}}{\partial \xi^{3}} \frac{\partial X^{2}}{\partial \xi^{3}} & \frac{\partial X^{2}}{\partial \xi^{2}} \frac{\partial X^{2}}{\partial \xi^{3}} & \frac{\partial X^{1}}{\partial \xi^{3}} \frac{\partial X^{2}}{\partial \xi^{2}} \\ \frac{\partial X^{3}}{\partial \xi^{1}} \frac{\partial X^{3}}{\partial \xi^{3}} & \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} & \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} & \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} & \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} \\ \frac{\partial X^{3}}{\partial \xi^{1}} \frac{\partial X^{3}}{\partial \xi^{3}} & \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} & \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} & \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} \\ \frac{\partial X^{3}}{\partial \xi^{1}} \frac{\partial X^{3}}{\partial \xi^{3}} & \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} & \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} \\ \frac{\partial X^{3}}{\partial \xi^{1}} \frac{\partial X^{3}}{\partial \xi^{3}} & \frac{\partial X^{3}}{\partial \xi^{2}} \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial X^{3}}{\partial \xi^{3}} \frac{\partial -$$ - -## 5. Nonlinear Newmark- \beta integration method - -먼저 물체의 비선형 운동방정식은 다음과 같다. - -$$ -M\ddot{\mathbf{u}} + \mathbf{C}(\dot{\mathbf{u}})\dot{\mathbf{u}} + \mathbf{K}(\mathbf{u})\mathbf{u} = \mathbf{P} -$$ - -여기서 밀도는 시간이나 변위에 따라 변화하지 않는다고 가정하면 $\mathbf{M}$ 은 항상 일정하다. 또한 구조물의 동적 문제이기 때문에 $\mathbf{C}$ 는 없다고 생각 할 수 있다. n+1시간에서 평형방정식을 생각하면 - -$$ -\mathbf{M}\ddot{\mathbf{u}}_{n+1} + \mathbf{K}(\mathbf{u}_{n+1})\mathbf{u}_{n+1} = \mathbf{P}_{n+1} -$$ - -와 같다. 운동방정식이 비선형이기 때문에 n+1시간에서 평형을 만족하는 변위와 가속도를 계산하기 위해서 반복 계산이 필요하다. 따라서 Newton-Raphson method를 사용하여 반복계산을 수행하였다. k+1번째 반복에서 평형이 이루어졌다면 식은 다음과 같다. - -$$ -\mathbf{M}\ddot{\mathbf{u}}_{n+1}^{k+1} + \mathbf{K}(\mathbf{u}_{n+1}^{k+1})\mathbf{u}_{n+1}^{k+1} = \mathbf{P}_{n+1}^{k+1} -$$ - -위 식을 정리하면 - -$$ -\mathbf{M}\ddot{\mathbf{u}}_{n+1}^{k+1} + \mathbf{K}(\mathbf{u}_{n+1}^{k+1})\mathbf{u}_{n+1}^{k+1} - \mathbf{P}_{n+1}^{k+1} = 0 = \mathbf{R}_{n+1}^{k+1} -$$ - -와 같고 Taylor series expansion을 통해 선형화 시키면 - -$$ -\mathbf{R}_{n+1}^{k+1} = \mathbf{R}_{n+1}^{k} + \frac{\partial \mathbf{R}_{n+1}^{k}}{\partial \mathbf{u}_{n+1}^{k}} \Delta \mathbf{u}_{n+1}^{k} + \frac{\partial \mathbf{R}_{n+1}^{k}}{\partial \dot{\mathbf{u}}_{n+1}^{k}} \Delta \dot{\mathbf{u}}_{n+1}^{k} + \frac{\partial \mathbf{R}_{n+1}^{k}}{\partial \ddot{\mathbf{u}}_{n+1}^{k}} \Delta \ddot{\mathbf{u}}_{n+1}^{k} -$$ - -와 같다. 이를 풀어 쓰면 - -$$ -0 = \mathbf{P}_{n+1}^{k} + \frac{\partial \mathbf{P}_{n+1}^{k}}{\partial \mathbf{u}_{n+1}^{k}} \Delta \mathbf{u}_{n+1}^{k} - \left\{ \mathbf{M} \ddot{\mathbf{u}}_{n+1}^{k} + \underbrace{\mathbf{K} \left( \mathbf{u}_{n+1}^{k} \right) \mathbf{u}_{n+1}^{k}}_{\mathbf{f}_{int} \left( \mathbf{u}_{n+1}^{k} \right)} \right\} - \left\{ \mathbf{M} \Delta \ddot{\mathbf{u}}_{n+1}^{k} + \underbrace{\frac{\partial \left( \mathbf{K} \left( \mathbf{u}_{n+1}^{k} \right) \mathbf{u}_{n+1}^{k} \right)}{\partial \mathbf{u}_{n+1}^{k}}}_{\mathbf{K}_{t}} \Delta \mathbf{u}_{n+1}^{k} \right\} -\Rightarrow \mathbf{M} \Delta \ddot{\mathbf{u}}_{n+1}^{k} + \mathbf{K}_{t} \Delta \mathbf{u}_{n+1}^{k} - \mathbf{P}_{t} \Delta \mathbf{u}_{n+1}^{k} = \mathbf{P}_{n+1}^{k} - \left\{ \mathbf{M} \ddot{\mathbf{u}}_{n+1}^{k} + \mathbf{f}_{int} \left( \mathbf{u}_{n+1}^{k} \right) \right\} -$$ - -와 같다. Newmark- $\beta$ method를 적용하면 n+1시간에서 변위와 속도를 구할 수 있다. - -$$ -\mathbf{u}_{n+1} = \mathbf{u}_n + h\dot{\mathbf{u}}_n + h^2\left(\frac{1}{2} - \beta\right)\ddot{\mathbf{u}}_n + h^2\beta\ddot{\mathbf{u}}_{n+1} = \mathbf{u}_n + h\dot{\mathbf{u}}_n + \frac{h^2}{2}\ddot{\mathbf{u}}_n - h^2\beta\ddot{\mathbf{u}}_n + h^2\beta\ddot{\mathbf{u}}_{n+1} -\dot{\mathbf{u}}_{n+1} = \dot{\mathbf{u}}_n + h(1 - \gamma)\ddot{\mathbf{u}}_n + h\gamma\ddot{\mathbf{u}}_{n+1} = \dot{\mathbf{u}}_n + h\ddot{\mathbf{u}}_n + \gamma h\ddot{\mathbf{u}}_{n+1} - \gamma h\ddot{\mathbf{u}}_n -$$ - -위의 식을 가속도와 속도로 나타내면 - -$$ -h^{2}\beta\ddot{\mathbf{u}}_{n+1} = \mathbf{u}_{n+1} - \mathbf{u}_{n} - h\dot{\mathbf{u}}_{n} - \frac{h^{2}}{2}\ddot{\mathbf{u}}_{n} + h^{2}\beta\ddot{\mathbf{u}}_{n} -\dot{\mathbf{u}}_{n+1} = \dot{\mathbf{u}}_{n} + h\ddot{\mathbf{u}}_{n} + \gamma h\ddot{\mathbf{u}}_{n+1} - \gamma h\ddot{\mathbf{u}}_{n} -$$ - -여기서 마찬가지로 k+1 반복에서 평형을 이룬다면 - -$$ -h^{2}\beta\ddot{\mathbf{u}}_{n+1}^{k+1} = \mathbf{u}_{n+1}^{k+1} - \mathbf{u}_{n} - h\dot{\mathbf{u}}_{n} - \frac{h^{2}}{2}\ddot{\mathbf{u}}_{n} + h^{2}\beta\ddot{\mathbf{u}}_{n} -\dot{\mathbf{u}}_{n+1}^{k+1} = \dot{\mathbf{u}}_{n} + h\ddot{\mathbf{u}}_{n} + \gamma h\ddot{\mathbf{u}}_{n+1}^{k+1} - \gamma h\ddot{\mathbf{u}}_{n} -$$ - -와 같고 반복에 대한 항을 선형화 시키면 - -$$ -h^{2}\beta\ddot{\mathbf{u}}_{n+1}^{k} + h^{2}\beta\Delta\ddot{\mathbf{u}}_{n+1}^{k} = \mathbf{u}_{n+1}^{k} + \Delta\mathbf{u}_{n+1}^{k} - \mathbf{u}_{n} - h\dot{\mathbf{u}}_{n} - \frac{h^{2}}{2}\ddot{\mathbf{u}}_{n} + h^{2}\beta\ddot{\mathbf{u}}_{n} -\dot{\mathbf{u}}_{n+1}^{k} + \Delta\dot{\mathbf{u}}_{n+1}^{k} = \dot{\mathbf{u}}_{n} + h\ddot{\mathbf{u}}_{n} + \gamma h\ddot{\mathbf{u}}_{n+1}^{k} + \gamma h\Delta\ddot{\mathbf{u}}_{n+1}^{k} - \gamma h\ddot{\mathbf{u}}_{n} -$$ - -위 식을 다음과 같이 k 번째 반복의 가속도와 속도, k 번째 반복의 미소 가속도와 미소 속도 항으로 분리 할수 있다. - -$$ -\begin{split} \ddot{\mathbf{u}}_{n+1}^{k} &= \frac{1}{h^{2}\beta}\mathbf{u}_{n+1}^{k} - \frac{1}{h^{2}\beta}\mathbf{u}_{n} - \frac{1}{h\beta}\dot{\mathbf{u}}_{n} - \frac{1}{2\beta}\ddot{\mathbf{u}}_{n} + \ddot{\mathbf{u}}_{n} \\ \dot{\mathbf{u}}_{n+1}^{k} &= \dot{\mathbf{u}}_{n} + h\ddot{\mathbf{u}}_{n} + \gamma h\ddot{\mathbf{u}}_{n+1}^{k} - \gamma h\ddot{\mathbf{u}}_{n} = \frac{\gamma}{h\beta}\mathbf{u}_{n+1}^{k} - \frac{\gamma}{h\beta}\mathbf{u}_{n} + \left(1 - \frac{\gamma}{\beta}\right)\dot{\mathbf{u}}_{n} + h\left(1 - \frac{\gamma}{2\beta}\right)\ddot{\mathbf{u}}_{n} \\ \Delta \ddot{\mathbf{u}}_{n+1}^{k} &= \frac{1}{h^{2}\beta}\Delta\mathbf{u}_{n+1}^{k} \\ \Delta \dot{\mathbf{u}}_{n+1}^{k} &= \gamma h\Delta \ddot{\mathbf{u}}_{n+1}^{k} = \frac{\gamma}{h\beta}\Delta\mathbf{u}_{n+1}^{k} \end{split} -$$ - -위의 미소 가속도, 미소 속도를 대입하면 diff --git a/docs/Paper/mitc공부/mitc공부_004.md b/docs/Paper/mitc공부/mitc공부_004.md deleted file mode 100644 index 3eee673..0000000 --- a/docs/Paper/mitc공부/mitc공부_004.md +++ /dev/null @@ -1,10 +0,0 @@ -$$ -\mathbf{M}\Delta\ddot{\mathbf{u}}_{n+1}^{k} + \mathbf{K}_{t}\Delta\mathbf{u}_{n+1}^{k} - \mathbf{P}_{t}\Delta\mathbf{u}_{n+1}^{k} = \mathbf{P}_{n+1}^{k} - \left\{\mathbf{M}\ddot{\mathbf{u}}_{n+1}^{k} + \mathbf{f}_{\text{int}}\left(\mathbf{u}_{n+1}^{k}\right)\right\} -\Rightarrow \left[\frac{1}{h^{2}\beta}\mathbf{M} + \mathbf{K}_{t}\left(\mathbf{u}_{n+1}^{k}\right) - \mathbf{P}_{t}\left(\mathbf{u}_{n+1}^{k}\right)\right]\Delta\mathbf{u}_{n+1}^{k} = \underbrace{\mathbf{P}_{n+1}^{k} - \left\{\mathbf{M}\ddot{\mathbf{u}}_{n+1}^{k} + \mathbf{f}_{\text{int}}\left(\mathbf{u}_{n+1}^{k}\right)\right\}}_{\mathbf{R}\left(\mathbf{u}_{n+1}^{k}\right)} -$$ - -여기서 $\mathbf{f}_{\mathrm{int}}\left(\mathbf{u}_{n+1}^{k}\right)$ 와 $\mathbf{K}_{t}\left(\mathbf{u}_{n+1}^{k}\right)$ 는 $\mathbf{u}_{n+1}^{k}$ 의 함수이기 때문에 반복이 수행될 때마다 다시 계산해 주어야 한다. 이후 다음 반복에 대한 변위, 속도, 가속도는 다음과 같다. - -$$ -\begin{split} &\mathbf{u}_{n+1}^{k+1} = \mathbf{u}_{n+1}^k + \Delta \mathbf{u}_{n+1}^k \\ &\dot{\mathbf{u}}_{n+1}^{k+1} = \frac{\gamma}{h\beta} \mathbf{u}_{n+1}^{k+1} - \left(\frac{\gamma}{h\beta} \mathbf{u}_n - \left(1 - \frac{\gamma}{\beta}\right) \dot{\mathbf{u}}_n - h \left(1 - \frac{\gamma}{2\beta}\right) \ddot{\mathbf{u}}_n\right) \\ &\ddot{\mathbf{u}}_{n+1}^{k+1} = \frac{1}{h^2\beta} \mathbf{u}_{n+1}^{k+1} - \left(\frac{1}{h^2\beta} \mathbf{u}_n + \frac{1}{h\beta} \dot{\mathbf{u}}_n + \frac{1}{2\beta} \ddot{\mathbf{u}}_n - \ddot{\mathbf{u}}_n\right) \end{split} -$$ diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-001-fig-001.jpg b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-001-fig-001.jpg deleted file mode 100644 index 0595c07..0000000 Binary files a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-001-fig-001.jpg and /dev/null differ diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-001-fig-006.jpg b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-001-fig-006.jpg deleted file mode 100644 index a797c20..0000000 Binary files a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-001-fig-006.jpg and /dev/null differ diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-001-fig-008.jpg b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-001-fig-008.jpg deleted file mode 100644 index 8233139..0000000 Binary files a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-001-fig-008.jpg and /dev/null differ diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-001-fig-010.jpg b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-001-fig-010.jpg deleted file mode 100644 index 26a08e4..0000000 Binary files a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-001-fig-010.jpg and /dev/null differ diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-001-fig-016.jpg b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-001-fig-016.jpg deleted file mode 100644 index 03ba473..0000000 Binary files a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-001-fig-016.jpg and /dev/null differ diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-001-fig-017.jpg b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-001-fig-017.jpg deleted file mode 100644 index 47543a0..0000000 Binary files a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-001-fig-017.jpg and /dev/null differ diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-001-fig-021.jpg b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-001-fig-021.jpg deleted file mode 100644 index 1536c7d..0000000 Binary files a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-001-fig-021.jpg and /dev/null differ diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-001-fig-031.jpg b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-001-fig-031.jpg deleted file mode 100644 index 3874b22..0000000 Binary files a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-001-fig-031.jpg and /dev/null differ diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-001-fig-043.jpg b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-001-fig-043.jpg deleted file mode 100644 index ffe21fb..0000000 Binary files a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-001-fig-043.jpg and /dev/null differ diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-001-fig-057.jpg b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-001-fig-057.jpg deleted file mode 100644 index 6278510..0000000 Binary files a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-001-fig-057.jpg and /dev/null differ diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-001-fig-073.jpg b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-001-fig-073.jpg deleted file mode 100644 index e7f7629..0000000 Binary files a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-001-fig-073.jpg and /dev/null differ diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-001-fig-082.jpg b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-001-fig-082.jpg deleted file mode 100644 index 09939e7..0000000 Binary files a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-001-fig-082.jpg and /dev/null differ diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-001-fig-117.jpg b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-001-fig-117.jpg deleted file mode 100644 index 92516a7..0000000 Binary files a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-001-fig-117.jpg and /dev/null differ diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-002-fig-081.jpg b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-002-fig-081.jpg deleted file mode 100644 index 19c33a6..0000000 Binary files a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-002-fig-081.jpg and /dev/null differ diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-002-fig-105.jpg b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-002-fig-105.jpg deleted file mode 100644 index 13c1e54..0000000 Binary files a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-002-fig-105.jpg and /dev/null differ diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-003-fig-033.jpg b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-003-fig-033.jpg deleted file mode 100644 index a6baa3d..0000000 Binary files a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-003-fig-033.jpg and /dev/null differ diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-003-fig-085.jpg b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-003-fig-085.jpg deleted file mode 100644 index b8e6e24..0000000 Binary files a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-003-fig-085.jpg and /dev/null differ diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-003-fig-086.jpg b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-003-fig-086.jpg deleted file mode 100644 index 2778344..0000000 Binary files a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-003-fig-086.jpg and /dev/null differ diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-003-fig-090.jpg b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-003-fig-090.jpg deleted file mode 100644 index 229683b..0000000 Binary files a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-003-fig-090.jpg and /dev/null differ diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-003-fig-097.jpg b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-003-fig-097.jpg deleted file mode 100644 index 2c4200b..0000000 Binary files a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-003-fig-097.jpg and /dev/null differ diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-003-fig-105.jpg b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-003-fig-105.jpg deleted file mode 100644 index 7a64329..0000000 Binary files a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-003-fig-105.jpg and /dev/null differ diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-003-fig-111.jpg b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-003-fig-111.jpg deleted file mode 100644 index cb94927..0000000 Binary files a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-003-fig-111.jpg and /dev/null differ diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-003-fig-119.jpg b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-003-fig-119.jpg deleted file mode 100644 index a968a72..0000000 Binary files a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-003-fig-119.jpg and /dev/null differ diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-003-fig-121.jpg b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-003-fig-121.jpg deleted file mode 100644 index 2400b08..0000000 Binary files a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-003-fig-121.jpg and /dev/null differ diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-003-fig-122.jpg b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-003-fig-122.jpg deleted file mode 100644 index ce05923..0000000 Binary files a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-003-fig-122.jpg and /dev/null differ diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-003-fig-127.jpg b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-003-fig-127.jpg deleted file mode 100644 index 30ca2a3..0000000 Binary files a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-003-fig-127.jpg and /dev/null differ diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-003-fig-129.jpg b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-003-fig-129.jpg deleted file mode 100644 index 600caee..0000000 Binary files a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-003-fig-129.jpg and /dev/null differ diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-003-fig-131.jpg b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-003-fig-131.jpg deleted file mode 100644 index 27135c6..0000000 Binary files a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-003-fig-131.jpg and /dev/null differ diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-003-fig-135.jpg b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-003-fig-135.jpg deleted file mode 100644 index ecf2463..0000000 Binary files a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-003-fig-135.jpg and /dev/null differ diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-003-fig-136.jpg b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-003-fig-136.jpg deleted file mode 100644 index 6d6ebe4..0000000 Binary files a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-003-fig-136.jpg and /dev/null differ diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-003-fig-141.jpg b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-003-fig-141.jpg deleted file mode 100644 index 33f9b0c..0000000 Binary files a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-003-fig-141.jpg and /dev/null differ diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-004-fig-002.jpg b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-004-fig-002.jpg deleted file mode 100644 index 91fc7ee..0000000 Binary files a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-004-fig-002.jpg and /dev/null differ diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-004-fig-006.jpg b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-004-fig-006.jpg deleted file mode 100644 index 42ea7b1..0000000 Binary files a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-004-fig-006.jpg and /dev/null differ diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-004-fig-017.jpg b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-004-fig-017.jpg deleted file mode 100644 index e134265..0000000 Binary files a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-004-fig-017.jpg and /dev/null differ diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-004-fig-021.jpg b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-004-fig-021.jpg deleted file mode 100644 index a858b10..0000000 Binary files a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-004-fig-021.jpg and /dev/null differ diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-004-fig-025.jpg b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-004-fig-025.jpg deleted file mode 100644 index 10b7c72..0000000 Binary files a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-004-fig-025.jpg and /dev/null differ diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-004-fig-031.jpg b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-004-fig-031.jpg deleted file mode 100644 index f4a474c..0000000 Binary files a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-004-fig-031.jpg and /dev/null differ diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-004-fig-034.jpg b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-004-fig-034.jpg deleted file mode 100644 index 2688173..0000000 Binary files a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-004-fig-034.jpg and /dev/null differ diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-004-fig-038.jpg b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-004-fig-038.jpg deleted file mode 100644 index d293c2a..0000000 Binary files a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-004-fig-038.jpg and /dev/null differ diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-004-fig-042.jpg b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-004-fig-042.jpg deleted file mode 100644 index d96d2b8..0000000 Binary files a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-004-fig-042.jpg and /dev/null differ diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-004-fig-047.jpg b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-004-fig-047.jpg deleted file mode 100644 index 8d8006e..0000000 Binary files a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/images/chunk-004-fig-047.jpg and /dev/null differ diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/tables/chunk-003-table-001.html b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/tables/chunk-003-table-001.html deleted file mode 100644 index 57e4427..0000000 --- a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/tables/chunk-003-table-001.html +++ /dev/null @@ -1 +0,0 @@ -
두께NODEx-coord.βρ
1.022.00.00114291750
310.00.00571431750
48.00.00457141750
64.00.00228571750
710.00.00571431750
88.00.00457141750
0.00122.00.11428617.50
310.00.57142917.50
48.00.45714317.50
64.00.22857117.50
710.00.57142917.50
88.00.45714317.50
\ No newline at end of file diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/tables/chunk-003-table-002.html b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/tables/chunk-003-table-002.html deleted file mode 100644 index f94aba6..0000000 --- a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/tables/chunk-003-table-002.html +++ /dev/null @@ -1 +0,0 @@ -
두께NODEx-coord.z-disp. (w)dw/dx
1.022.00.0004952382.47619E-04
310.00.002476192.47619E-04
48.00.001980952.47619E-04
64.00.0009904762.47619E-04
710.00.002476192.47619E-04
88.00.001980952.47619E-04
0.00122.00.0004952382.47619E-04
310.00.002476192.47619E-04
48.00.001980952.47619E-04
64.00.0009904762.47619E-04
710.00.002476192.47619E-04
88.00.001980952.47619E-04
\ No newline at end of file diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/tables/chunk-003-table-003.html b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/tables/chunk-003-table-003.html deleted file mode 100644 index 959a043..0000000 --- a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/tables/chunk-003-table-003.html +++ /dev/null @@ -1 +0,0 @@ -
두께NODEx-
coord.
α\rho_{\rm l}y-
coord.
β\rho_2
22.00.00751761266.042.00.0075345265.45
48.00.03103950257.743.00.0114079262.98
1.064.00.01518240263.467.00.0272324257.05
710.00.03767130265.4510.00.0379748263.33
88.00.03104290257.717.00.0272190257.17
0.00122.00.00742857269.232.00.00742857269.23
48.00.02971430269.233.00.0111429269.23
64.00.01485710269.237.00.0260000269.23
710.00.03714290269.2310.00.0371429269.23
88.00.02971430269.237.00.0260000269.23
\ No newline at end of file diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/tables/chunk-003-table-004.html b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/tables/chunk-003-table-004.html deleted file mode 100644 index f38ec52..0000000 --- a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/tables/chunk-003-table-004.html +++ /dev/null @@ -1 +0,0 @@ -
MeshExactPresentwpresent
/wexact
ABAQUSwabaqus
/wexact
20x201.8248E-051.74362E-050.95551.77866E-050.9747
30x301.79593E-050.98421.81676E-050.9956
40x401.81878E-050.99671.82150E-050.9982
\ No newline at end of file diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/tables/chunk-003-table-005.html b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/tables/chunk-003-table-005.html deleted file mode 100644 index 015ab35..0000000 --- a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/tables/chunk-003-table-005.html +++ /dev/null @@ -1 +0,0 @@ -
Node/sideExactPresentwpresent
/wexact
ABAQUSwabaqus
/wexact
90.08884390.96150.09411531.0186
170.09240.09190910.99470.09330831.0098
250.09213860.99720.09287991.0052
\ No newline at end of file diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/tables/table-001.html b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/tables/table-001.html deleted file mode 100644 index 57e4427..0000000 --- a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/tables/table-001.html +++ /dev/null @@ -1 +0,0 @@ -
두께NODEx-coord.βρ
1.022.00.00114291750
310.00.00571431750
48.00.00457141750
64.00.00228571750
710.00.00571431750
88.00.00457141750
0.00122.00.11428617.50
310.00.57142917.50
48.00.45714317.50
64.00.22857117.50
710.00.57142917.50
88.00.45714317.50
\ No newline at end of file diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/tables/table-002.html b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/tables/table-002.html deleted file mode 100644 index f94aba6..0000000 --- a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/tables/table-002.html +++ /dev/null @@ -1 +0,0 @@ -
두께NODEx-coord.z-disp. (w)dw/dx
1.022.00.0004952382.47619E-04
310.00.002476192.47619E-04
48.00.001980952.47619E-04
64.00.0009904762.47619E-04
710.00.002476192.47619E-04
88.00.001980952.47619E-04
0.00122.00.0004952382.47619E-04
310.00.002476192.47619E-04
48.00.001980952.47619E-04
64.00.0009904762.47619E-04
710.00.002476192.47619E-04
88.00.001980952.47619E-04
\ No newline at end of file diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/tables/table-003.html b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/tables/table-003.html deleted file mode 100644 index 959a043..0000000 --- a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/tables/table-003.html +++ /dev/null @@ -1 +0,0 @@ -
두께NODEx-
coord.
α\rho_{\rm l}y-
coord.
β\rho_2
22.00.00751761266.042.00.0075345265.45
48.00.03103950257.743.00.0114079262.98
1.064.00.01518240263.467.00.0272324257.05
710.00.03767130265.4510.00.0379748263.33
88.00.03104290257.717.00.0272190257.17
0.00122.00.00742857269.232.00.00742857269.23
48.00.02971430269.233.00.0111429269.23
64.00.01485710269.237.00.0260000269.23
710.00.03714290269.2310.00.0371429269.23
88.00.02971430269.237.00.0260000269.23
\ No newline at end of file diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/tables/table-004.html b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/tables/table-004.html deleted file mode 100644 index f38ec52..0000000 --- a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/tables/table-004.html +++ /dev/null @@ -1 +0,0 @@ -
MeshExactPresentwpresent
/wexact
ABAQUSwabaqus
/wexact
20x201.8248E-051.74362E-050.95551.77866E-050.9747
30x301.79593E-050.98421.81676E-050.9956
40x401.81878E-050.99671.82150E-050.9982
\ No newline at end of file diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/tables/table-005.html b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/tables/table-005.html deleted file mode 100644 index 015ab35..0000000 --- a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/tables/table-005.html +++ /dev/null @@ -1 +0,0 @@ -
Node/sideExactPresentwpresent
/wexact
ABAQUSwabaqus
/wexact
90.08884390.96150.09411531.0186
170.09240.09190910.99470.09330831.0098
250.09213860.99720.09287991.0052
\ No newline at end of file diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/유한요소해석법을이용한쉘구조물의동적좌굴해석_001.md b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/유한요소해석법을이용한쉘구조물의동적좌굴해석_001.md deleted file mode 100644 index aa258a7..0000000 --- a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/유한요소해석법을이용한쉘구조물의동적좌굴해석_001.md +++ /dev/null @@ -1,352 +0,0 @@ -![Figure](images/chunk-001-fig-001.jpg) - -#### 저작자표시 - 비영리 - 변경금지 2.0 대한민국 - -#### 이용자는 아래의 조건을 따르는 경우에 한하여 자유롭게 - -l 이 저작물을 복제, 배포, 전송, 전시, 공연 및 방송할 수 있습니다. - -#### 다음과 같은 조건을 따라야 합니다 : - -![Figure](images/chunk-001-fig-006.jpg) - -저작자표시. 귀하는 원저작자를 표시하여야 합니다. - -![Figure](images/chunk-001-fig-008.jpg) - -비영리. 귀하는 이 저작물을 영리 목적으로 이용할 수 없습니다. - -![Figure](images/chunk-001-fig-010.jpg) - -변경금지. 귀하는 이 저작물을 개작, 변형 또는 가공할 수 없습니다. - -- l 귀하는, 이 저작물의 재이용이나 배포의 경우, 이 저작물에 적용된 이용허락조건 을 명확하게 나타내어야 합니다. -- l 저작권자로부터 별도의 허가를 받으면 이러한 조건들은 적용되지 않습니다. - -#### 저작권법에 따른 이용자의 권리는 위의 내용에 의하여 영향을 받지 않습니다 . - -이것은 이용허락규약 (Legal Code)을 이해하기 쉽게 요약한 것입니다. - -Disclaimer - -![Figure](images/chunk-001-fig-016.jpg) - -![Figure](images/chunk-001-fig-017.jpg) - -#### 공학석사학위 청구논문 - -### 유한요소해석법을 이용한 쉘 구조물의 동적 좌굴 해석 - -## Dynamic Buckling Analysis of Shell Structures using Finite Element Method - -![Figure](images/chunk-001-fig-021.jpg) - -인하대학교 대학원 항공우주공학과 이 희 준 - -#### 공학석사학위 청구논문 - -### 유한요소해석법을 이용한 쉘 구조물의 동적 좌굴 해석 - -Dynamic Buckling Analysis of Shell Structures using Finite Element Method - -2012 년 2 월 - -지도교수 조 진 연 이 논문을 석사학위 논문으로 제출함 - -인하대학교 대학원 항공우주공학과 이 희 준 - -### 이 논문을 이희준의 석사학위논문으로 인정함 - -### 2012 년 2 월 - -![Figure](images/chunk-001-fig-031.jpg) - -| 주심 | 김 | 기 | 욱 | | | -| --- | --- | --- | --- | --- | --- | -| | | | | | | -| 부심 | 조 | 진 | 연 | | | -| | | | | | | -| 위원 | 이 | 승 | 수 | | | - -# 요 약 - -매개 변수 공진으로도 알려진 동적 좌굴 현상은 구조물이 축 방향의 동적 압축 하중을 받을 때 발생하는 동적 불안정 현상으로서 구조물에 심각한 파손을 유발할 수 있다. 특히 초음속으로 운동하는 항공기나 탄도 미사일, 발사용 로켓과 지구 대기권 재돌입체 그리고 초공동 수중운동체 와 같이 동적 압축 하중을 받는 구조물을 설계할 때 구조물의 동적 좌굴 거동을 예측하여 설계하는 것이 중요하다. 이에 본 논문에서는 축 방향의 동적 압축 하중을 받는 쉘 구조물에 대해 동적 좌굴 해석을 하기 위한 유한요소해석 프로그램을 개발하였으며, 선형/비선형 정적 해석과 진동 및 정적 좌굴 해석을 통해 프로그램에 사용된 쉘 요소의 신뢰성을 확인 하였다. 또한 다양한 모델에 대한 동적 좌굴 해석 결과를 이론적인 해나 실험을 통해 나온 결과와 비교함으로써 본 프로그램의 타당성을 검증하 였다. - -### ABSTRACT - -Dynamic buckling, also known as parametric resonance, is one of the dynamic instability phenomena which may lead to serious failure of structure. It occurs when compressive dynamic loading of axial direction is applied to the structures. Therefore it is essential to consider the dynamic buckling behaviors of structures, especially when the structures is designed to be utilized in compressive dynamic loading of axial direction such as faster supersonic aircrafts, ballistic missiles, launcher, re-entry vehicles and supercavitating underwater vehicles. In this study, the finite element program is developed for dynamic buckling analysis. Linear and nonlinear static analyses, dynamic analysis and static buckling analysis are performed to demonstrate the accuracy of the developed program. Also the dynamic buckling analyses are carried out for various models and the computational results are verified by comparing with analytical and experimental solutions. - -# 목 차 - -| 요약 | i | -| --- | --- | -| ABSTRACT | ii | -| 목차 | iii | -| 그림 목차 | V | -| 표 목차 | vi | -| 1. 서론 | 1 | -| 2. 이 론 | 3 | -| 2.1. MITC4 Shell Element | 3 | -| 2.2. Geometric Nonlinear Formulation | 7 | -| 2.2.1. Finite Rotation Formulation | 21 | -| 2.2.2. Constitutive Matrix | 22 | -| 2.2.3. Mass Matrix | 25 | -| 2.2.4. 6-DOF Shell Element | 26 | -| 2.3. Buckling Theory | 30 | -| 2.3.1. Static Buckling | 31 | -| 2.3.2. Dynamic Buckling | 32 | -| 2.3.3. Dynamic Buckling Theory of Beam | 34 | -| 3. Numerical Example | 39 | -| 3.1. Linear Static Analysis | 39 | -| 3.1.1. Patch Test | 39 | -| 3.1.1.1. Constant Curvature Patch Test | 40 | -| 3.1.1.2. Constant Shear Patch Test | 41 | -| 3.1.1.3. Constant Twist Patch Test | 43 | -| 3.1.2. Pinched Cylinder | 44 | -| 3.1.3. Hemispherical Shell | 46 | -| 3.2. Geometric Nonlinear Analysis | 48 | -| 3.3. Static Buckling Analysis | 50 | - -| 3.3.1. Rectangular Plate Shell | 50 | -| --- | --- | -| 3.3.2. Cylindrical Shell | 52 | -| 3.3.3. Stiffened Square Plate Shell | 55 | -| 3.4. Dynamic Buckling Analysis | 57 | -| 3.4.1. Dynamic Buckling Analysis of Beam | 57 | -| 3.4.2. Dynamic Buckling Analysis of Plate | 59 | -| 3.4.3. Dynamic Buckling Analysis of Stiffened Plate | 61 | -| 4. 결 론 | 63 | -| 참고문헌 | 64 | -| 부 록 | 65 | - -![Figure](images/chunk-001-fig-043.jpg) - -#### 그림 목차 - -| Fig. 1 Four-node shell element ···························································································· 4 | -| --- | -| Fig. 2 Interpolation function for the transverse shear strains ··············································· 6 | -| Fig. 3 An arbitrary surface with global Cartesian coordinate system, natural coordinate | -| system and local covariant coordinate system spanned by i g ································· 10 | -| Fig. 4 Local Cartesian coordinate system ·········································································· 23 | -| Fig. 5 Global Cartesian coordinate system and local coordinate system ··························· 26 | -| Fig. 6 Dynamic Buckling Model of Beam ········································································· 34 | -| Fig. 7 Patch Test Mesh ······································································································· 39 | -| Fig. 8 Constant Curvature Patch Test Model ····································································· 40 | -| Fig. 9 Constant Shear Patch Test Model ············································································ 41 | -| Fig. 10 Constant Twist Patch Test Model ··········································································· 43 | -| Fig. 11 Pinched Cylinder Model ························································································ 44 | -| Fig. 12 Pinched Cylinder 1/8 Model ·················································································· 44 | -| Fig. 13 Comparison of Convergence for Pinched Cylinder with ABAQUS ······················ 45 | -| Fig. 14 Comparison of Linear Static Analysis for Pinched Cylinder with ABAQUS ········ 46 | -| Fig. 15 Hemispherical Shell Model ··················································································· 46 | -| Fig. 16 Comparison of Convergence for Hemispherical Shell with ABAQUS ················· 47 | -| Fig. 17 Comparison of Linear Static Analysis for Hemisphrical Shell with ABAQUS ····· 48 | -| Fig. 18 Beam Model for Geometric Nonlinear Analysis ··················································· 48 | -| Fig. 19 Comparison of Geometric Nonlinear Analysis for Beam with ABAQUS ············· 49 | -| Fig. 20 Geometry Change of Beam According to Loads Increase ····································· 49 | -| Fig. 21 Rectangular Plate Shell Model ·············································································· 50 | -| Fig. 22 Cylindrical Shell Model ························································································· 52 | -| Fig. 23 Stiffened Square Plate Shell Model ······································································· 55 | -| Fig. 24 Dynamic Buckling Analysis Model for Beam ······················································· 57 | -| Fig. 25 Dynamic Instability Region of Beam ···································································· 58 | -| Fig. 26 Dynamic Buckling Model for Plate ·········································································59 | -| Fig. 27 Dynamic Instability Region of Plate ······································································ 60 | -| Fig. 28 Dynamic Buckling Analysis Model for Stiffened Plate ········································· 61 | -| Fig. 29 Comparison of Dynamic Instability Region for Stiffened Plate with Plate ··········· 62 | - -#### 표 목차 - -| Table. 1 Constant Curvature Patch Test Results ································································ 41 | -| --- | -| Table. 2 Constant Shear Patch Test Results ······································································· 42 | -| Table. 3 Constant Twist Patch Test Results ······································································· 43 | -| ·45 Table. 4 Comparison of Linear Static Analysis for Pinched Cylinder with Exact Solution | -| ·47 Table. 5 Comparison of Linear Static Analysis for Hemispherical Shell with Exact Solution | -| Table. 6 Comparison of Eigenvalue for Rectangular Plate Shell with ABAQUS ·············· 51 | -| Table. 7 Comparison of Mode Shape for Rectangular Plate Shell with ABAQUS·············· 51 | -| Table. 8 Comparison of Eigenvalue for Cylindrical Shell with ABAQUS ························ 53 | -| Table. 9 Comparison of Critical Buckling Pressure for Cylindrical Shell with Analytic Solution · 53 | -| Table. 10 Comparison of Mode Shape for Cylindrical Shell with ABAQUS ···················· 54 | -| Table. 11 Comparison of Eigenvalue for Stiffened Square Plate Shell with ABAQUS ····· 56 | -| Table. 12 Comparison of Mode Shape for Stiffened Square Plate Shell with ABAQUS ·· 56 | - -# 1. 서 론 - -좌굴(buckling, 挫屈)이란 주로 길이가 그 횡단면의 치수에 비해 큰 구 조물의 양단에 압축하중이 가해졌을 경우, 하중이 어느 크기에 이르면 기 둥이 갑자기 휘는 현상을 말한다. 특히 긴 기둥이나 쉘을 많이 사용하는 항공기, 차량, 선박, 건축물 등의 설계에서 좌굴 문제가 중요하며, 원통형 쉘의 경우 가스와 같은 액체를 저장하는 압력용기로 사용될 뿐만 아니라 잠수함이나 항공기와 같이 외압을 받는 구조물에 사용된다. 이와 같이 다 양한 분야에서 사용되는 쉘 구조물은 그 두께가 길이에 비해 얇기 때문 에 진동이나 좌굴에 취약한 특성을 보인다. 이러한 취약점을 해결하기 위 해서는 우선 구조물에 작용하는 하중의 특성을 파악하고 문제가 발생하 는 부분에 대해 구조물을 어떻게 보강할지 결정해야 한다. - -일반적으로 좌굴 문제를 다룰 때 정적 하중만을 고려하지만 특수한 경우에 대해서는 동적 하중도 함께 고려해야만 한다. 예를 들어 초음속으 로 운동하는 항공기나 탄도 미사일, 발사용 로켓과 지구 대기권 재돌입체 그리고 초공동 수중운동체의 경우 빠른 속도로 인해 축 방향으로 매우 큰 동적 압축하중이 작용하게 된다. 이러한 동적 압축하중으로 인하여 발 생하는 좌굴을 동적 좌굴(dynamic buckling) 또는 매개변수 공진(parametric resonance)이라고 한다. 동적 좌굴이 발생하게 되면 구조물의 횡 방향 운 동이 커지게 되고 이로 인해 구조물의 불안정성이 증가하여 치명적인 손 상을 유발할 수 있다. 그러므로 동적 좌굴 현상을 방지하기 위해서는 이 러한 하중이 작용하는 구조물에 대한 해석을 통해 구조물이 불안정해지 는 영역을 파악하고 이를 고려하여 설계하는 것이 중요하다. - -하지만 기존 상용유한요소해석 프로그램의 경우 정적 하중에 대한 정 적 좌굴 해석만 가능하며, 동적 하중 또는 정적 하중과 동적 하중이 동시 에 가해지는 경우에 대한 좌굴 해석이 불가능하다. 이에 본 논문에서는 축 방향의 동적 압축 하중을 받는 쉘 구조물에 대해 동적 좌굴 해석을 하기 위한 유한요소해석 프로그램을 개발하였으며, 선형/비선형 정적 해 석과 진동 및 정적 좌굴 해석을 통해 프로그램에 사용된 쉘 요소의 신뢰 - -성을 확인하였다. 또한 다양한 모델에 대한 동적 좌굴 해석 결과를 이론 적인 해나 실험을 통해 나온 결과와 비교함으로써 본 프로그램의 타당성 을 검증하였다. - -![Figure](images/chunk-001-fig-057.jpg) - -### 2. 이론 - -#### 2.1 MITC4 Shell Element - -쉘 구조물을 유한요소 모델로 만들기 위해서 다양한 쉘 요소 가운데 Bathe와 Dvorkin에 의해 개발된 MITC4라는 쉘 요소를 선정하였다. MITC4 쉘 요소는 3차원 솔리드 형상으로부터 쉘 형상을 표현하므로 지배방정식의 유한요소 정식화가 다른 쉘 요소에 비해 간단하다. 또한 쉘 이론을 사용하지 않고 3차원 응력, 변형률을 사용하여 표현되며, 임의의 형상에 대한 두꺼운 쉘과 얇은 쉘 모두 적용 가능하다는 장점이 있다. 그리고 대 변형/회전(작은 변형률)과 재료 비선형에 모두 적용 가능하다. [1] - -MITC4 쉘 요소 내부의 임의의 점은 고유 좌표계(natural coordinate system)에 대해 정의 할 수 있으며, 위치 벡터는 식(2.1)과 (2.2)같이 나타낼 수 있다. - -$$ -t -\mathbf{X} = \sum_{I=1}^{4} N_{I}(\xi, \eta) {}^{0}\mathbf{X}_{I} + \frac{\zeta}{2} \sum_{I=1}^{4} t_{I} N_{I}(\xi, \eta) {}^{0}\mathbf{V}_{I}^{n} \tag{2.1} -$$ - -임의의 시간 t에서 - -$$ -\mathbf{x} = \sum_{I=1}^{4} N_{I}(\xi, \eta)^{t} \mathbf{X}_{I} + \frac{\zeta}{2} \sum_{I=1}^{4} t_{I} N_{I}(\xi, \eta)^{t} \mathbf{V}_{I}^{n} \tag{2.2} -$$ - -이 때 $N_t(\xi,\eta)$ 는 형상함수이고, ' $\mathbf{X}_t$ 는 시간 t일 때 노드 I의 좌표를 나타내며, 시간 t=0이면 초기 형상에서 노드 I의 좌표를 나타낸다. 그리고 $t_i$ 는 노드 I의 두께이고 ${}^t\mathbf{V}_i^{r}$ 은 시간 t일 때 노드 I의 두께방향의 법선 벡터(normal vector)를 나타내며, 시간 t=0이면 초기 형상에서 노드 I의 - -두께방향의 법선 벡터를 나타낸다. - -임의의 시간 t에서의 MITC4 쉘 요소의 변위는 식(2.3)과 같이 나타낼 수 있다. - -$$ -{}^{t}\mathbf{u} = \mathbf{x} - \mathbf{X} = \sum_{I=1}^{4} N_{I}(\xi, \eta) \left( {}^{t}\mathbf{X}_{I} - {}^{0}\mathbf{X}_{I} \right) + \frac{\zeta}{2} \sum_{I=1}^{4} t_{I} N_{I}(\xi, \eta) \left( {}^{t}\mathbf{V}_{I}^{n} - {}^{0}\mathbf{V}_{I}^{n} \right) -= \sum_{I=1}^{4} N_{I}(\xi, \eta) {}^{t}\mathbf{u}_{I} + \frac{\zeta}{2} \sum_{I=1}^{4} t_{I} N_{I}(\xi, \eta) \left( {}^{t}\mathbf{V}_{I}^{n} - {}^{0}\mathbf{V}_{I}^{n} \right) -(2.3) -$$ - -임의의 시간 t에서의 변위는 시간 t일 때의 형상과 초기 형상의 차로부터 구할 수 있으며, 이 때 ${}^{\prime}\mathbf{u}_{I}$ 는 시간 t일 때 노드 I의 변위를 나타낸다. 그리고 이로부터 변위의 증분은 식(2.4)와 같이 나타낼 수 있다. - -![Fig. 1 Four-node shell element](images/chunk-001-fig-073.jpg) - -$$ -\Delta \mathbf{u} = \sum_{I=1}^{4} N_I(\xi, \eta) \Delta \mathbf{u}_I + \frac{\zeta}{2} \sum_{I=1}^{4} t_I N_I(\xi, \eta) \left( -\alpha_I^{\ \ t} \mathbf{V}_I^2 + \beta_I^{\ \ t} \mathbf{V}_I^1 \right) \tag{2.4} -$$ - -여기서 $\alpha$ 와 $\beta$ 는 각각 $\mathbf{V}^1$ 과 $\mathbf{V}^2$ 방향 벡터의 회전각이고, 이 때 $\mathbf{V}^1$ 과 $\mathbf{V}^2$ 는 $\mathbf{V}^n$ 으로부터 식(2.5)와 같이 구할 수 있다. - -$$ -\mathbf{V}^{1} = \frac{\mathbf{e}_{2} \times \mathbf{V}_{n}}{\|\mathbf{e}_{2} \times \mathbf{V}_{n}\|}, \quad \mathbf{V}^{2} = \mathbf{V}_{n} \times \mathbf{V}^{1} \tag{2.5} -$$ - -이 때 $\mathbf{e}_1$ , $\mathbf{e}_2$ , $\mathbf{e}_3$ 는 전역 직교 좌표계(global Cartesian coordinate system)의 기저(basis)이다. 그리고 만약 $\mathbf{e}_2 \times \mathbf{V}_n \approx 0$ 이라면, $\mathbf{V}^1$ 과 $\mathbf{V}^2$ 는 식(2.6)과 같이 나타낼 수 있다. - -$$ -\mathbf{V}^1 = \mathbf{e}_3, \quad \mathbf{V}^2 = \mathbf{e}_1 \tag{2.6} -$$ - -하지만 위와 같이 변위를 정의할 경우 일정한 굽힘 모멘트가 가해질때 요소의 모든 점에서 횡 전단 변형률(transverse shear strain)이 영(零)이될 수 없고, 이로 인해 얇은 형상에 대해 요소의 '잠김현상(locking phenomenon)'이 발생하게 된다. 그러므로 연속체 역학의 가정이 Kirchhoff 쉘의 가정을 포함할지라도 유한요소이산화를 통해 이러한 가정을 표현할수가 없다. 이러한 결점을 해결하기 위해 MITC4 쉘 요소에서는 식(2.7)과 같은 횡 전단 변형률에 대한 보간법을 적용하였다.[1] - -![Fig. 2 Interpolation function for the transverse shear strains](images/chunk-001-fig-082.jpg) - -$$ -\widetilde{\varepsilon}_{\xi\zeta} = \frac{1}{2} (1 + \eta) \widetilde{\varepsilon}_{\xi\zeta}^{A} + \frac{1}{2} (1 - \eta) \widetilde{\varepsilon}_{\xi\zeta}^{C} \tag{2.7a} -$$ - -$$ -\widetilde{\varepsilon}_{\eta\zeta} = \frac{1}{2} (1 + \xi) \widetilde{\varepsilon}_{\eta\zeta}^{D} + \frac{1}{2} (1 - \xi) \widetilde{\varepsilon}_{\eta\zeta}^{B} \tag{2.7b} -$$ - -#### 2.2 Geometric Nonlinear Formulation - -좌굴 해석을 하기 위해서는 기하 강성 행렬(geometric stiffness matrix)이 필요하며, 이를 구하기 위해 MITC4 쉘 요소에 대한 비선형 유한요소 정식화 과정을 수행하였다. - -현재 형상에 대한 평형방정식은 식(2.8)과 같다. - -$$ -\nabla_{\mathbf{X}} \cdot \mathbf{\sigma} + \rho \mathbf{f} = \rho \ddot{\mathbf{u}} \tag{2.8} -$$ - -식(2.8)에 가상 변위에 대한 현재 형상에서의 가상 일 정리를 사용하면, 식 (2.9)와 같이 쓸 수 있다. - -$$ -\int_{V} \delta \mathbf{u} \cdot (\nabla_{\mathbf{X}} \cdot \mathbf{\sigma} + \rho \mathbf{f}) dV = \int_{V} \delta \mathbf{u} \cdot \rho \ddot{\mathbf{u}} dV \tag{2.9} -$$ - -식(2.9)를 인덱스를 사용하여 표현하면 식(2.10)과 같다. - -$$ -\int_{V} \delta u_{i} \cdot \left( \frac{\partial \sigma_{ij}}{\partial x_{j}} + \rho f_{i} \right) dV = \int_{V} \delta u_{i} \cdot \rho \ddot{u}_{i} dV \tag{2.10} -$$ - -이 때 식(2.11)을 식(2.10)에 대입하면 식(2.12)와 같은 결과를 얻을 수 있다. - -$$ -\delta u_i \frac{\partial \sigma_{ij}}{\partial x_j} = \frac{\partial \left(\delta u_i \sigma_{ij}\right)}{\partial x_j} - \frac{\partial \delta u_i}{\partial x_j} \sigma_{ij} \tag{2.11} -$$ - -$$ -\int_{V} \frac{\partial \left(\delta u_{i} \sigma_{ij}\right)}{\partial x_{i}} - \frac{\partial \delta u_{i}}{\partial x_{j}} \sigma_{ij} + \delta u_{i} \rho f_{i} dV = \int_{V} \delta u_{i} \cdot \rho \ddot{u}_{i} dV \tag{2.12} -$$ - -식(2.12)에 가우스의 발산 정리(Gauss' divergence theorem)를 사용하면 식(2.13)과 같이 나타낼 수 있다. - -$$ -\int_{\partial V} \delta u_i \sigma_{ij} n_j dA + \int_{V} \left( -\frac{\partial \delta u_i}{\partial x_j} \sigma_{ij} + \delta u_i \rho f_i \right) dV = \int_{V} \delta u_i \cdot \rho \ddot{u}_i dV \tag{2.13} -$$ - -식(2.13)에서 좌측 첫 번째 항의 면 적분 부분은 식(2.14)와 같이 기하학적 경계조건(geometric boundary condition)과 자연적 경계조건(natural boundary condition)으로 나눌 수 있으며, 이 때 식(2.14)의 좌측 첫 번째 항은 기하학적 경계조건에서 가상변위가 영(零)이므로 생략할 수 있다. - -$$ -\int_{\partial V_g} \delta u_i \sigma_{ij} n_j dA + \int_{\partial V_m} \delta u_i \bar{t}_i dA + \int_V \left( -\frac{\partial \delta u_i}{\partial x_j} \sigma_{ij} + \delta u_i \rho f_i \right) dV = \int_V \delta u_i \cdot \rho i i_i dV \tag{2.14} -$$ - -$$ -\frac{\partial \delta u_i}{\partial x_j} \sigma_{ij} = \frac{1}{2} \left( \frac{\partial \delta u_i}{\partial x_j} + \frac{\partial \delta u_j}{\partial x_i} \right) \sigma_{ij} = \delta \varepsilon_{ij} \sigma_{ij} \tag{2.14} -$$ - -그리고 식(2.14)에 식(2.15)를 대입하여 정리하면 현재 형상에서의 가상 일에 대한 식을 식(2.16)과 같이 얻을 수 있다. - -$$ -\int_{\partial V_m} \delta u_i \bar{t}_i dA + \int_V \delta u_i \rho f_i dV = \int_V \delta u_i \cdot \rho \ddot{u}_i dV + \int_V \delta \varepsilon_{ij} \sigma_{ij} dV \tag{2.16} -$$ - -식(2.16)에서 좌변은 외력에 의한 가상 일이고, 우변은 내력에 의한 가상 일이다. - -비선형 해석을 수행하기 위해서는 Total Lagrangian 기법과 Updated Lagrangian 기법이 있으며, 전자의 방법은 문제를 초기 형상에 대해 정의하는 방법이고, 후자의 방법은 현재 형상에 대해 정의하는 방법이다. 본 논문에서는 전자의 방법을 사용하였으며, 식(2.17)을 사용하여 변형된 현재 형상의 응력과 변형률 등을 초기 형상에 대해 정의하였다. - -$$ -\int_{V} (A)dV = \int_{V_0} (A)\det(\mathbf{F})dV_0 = \int_{V_0} (A)JdV_0 \tag{2.17a} -$$ - -$$ -\int_{\partial V} (A) \mathbf{n} dA = \int_{\partial V_0} (A) J \mathbf{F}^{-T} \widetilde{\mathbf{n}} dA_0 \quad \text{(Nanson's formular)} \tag{2.17b} -$$ - -$$ -\int_{V} \delta \mathbf{\varepsilon} : \mathbf{\sigma} dV = \int_{V_0} \delta \mathbf{F} : \mathbf{P} dV_0 = \int_{V_0} \delta \mathbf{E} : \mathbf{S} dV_0 \tag{2.17c} -$$ - -이 때 (A)는 임의의 값을 의미하고, F는 변형 구배(deformation gradient), J는 자코비안(Jacobian)으로 체적변화율을 나타낸다. 그리고 $\epsilon$ 과 $\sigma$ 는 현재 형상에서의 미소 변형률(infinitesimal strain)과 Cauchy 응력을 의미하고, E는 Green-Lagrange 변형률로써 초기 형상에서 정의된다. P는 1차 Piola-Kirchhoff 응력을 나타내고, S는 2차 Piola-Kirchhoff 응력을 나타내며, 두 응력 모두 초기 형상에서 정의된다. - -식(2.17)을 사용하여 식(2.16)을 초기 형상에 대해 정의해주면 식(2.18)과 같이 나타낼 수 있다. - -$$ -\int_{V_0} \delta \mathbf{u} \cdot \rho_0 \ddot{\mathbf{u}} dV_0 + \int_{V_0} \delta \mathbf{E} : \mathbf{S} dV_0 = \int_{\partial V_{0...}} \delta \mathbf{u} \cdot \mathbf{F} \mathbf{S} \widetilde{\mathbf{n}} dA_0 + \int_{V_0} \delta \mathbf{u} \rho_0 \mathbf{f} dV_0 \tag{2.18a} -$$ - -$$ -\int_{V_0} \delta \mathbf{u} \cdot \rho_0 \ddot{\mathbf{u}} dV_0 + \int_{V_0} \delta \mathbf{E} : \mathbf{S} dV_0 = \int_{\partial V_{0_m}} \delta \mathbf{u} \cdot \mathbf{F} \mathbf{S} \widetilde{\mathbf{n}} dA_0 + \int_{V_0} \delta \mathbf{u} \rho_0 \mathbf{f} dV_0 \qquad (2.18a) -\Leftrightarrow \int_{V_0} \delta \mathbf{u} \cdot \rho_0 \ddot{\mathbf{u}} dV_0 + \int_{V_0} \delta \mathbf{F} : \mathbf{P} dV_0 = \int_{\partial V_{0_m}} \delta \mathbf{u} \cdot \mathbf{P} \widetilde{\mathbf{n}} dA_0 + \int_{V_0} \delta \mathbf{u} \rho_0 \mathbf{f} dV_0 \qquad (2.18b) -$$ - -식(2.18b)는 1차 Piola-Kirchhoff 응력으로 표현한 식이고, 식(2.18a)는 2차 Piola-Kirchhoff 응력으로 표현한 식이다. 본 논문에서는 2차 Piola-Kirchhoff 응력을 사용한 식(2.18a)를 사용하였다. - -![Fig. 3 An arbitrary surface with global Cartesian coordinate system(x, y, z), natural coordinate system (\xi, \eta, \zeta) and local covariant coordinate system spanned by \mathbf{g}_i](images/chunk-001-fig-117.jpg) - -고유 좌표계(natural coordinate system)에서 공변 기저(covariant basis)는 식(2.19)와 같이 나타낼 수 있고, 변위에 대한 기울기(gradient)를 고유 좌표계와 반공변 기저(contravariant basis)를 사용하여 표현하면 식(2.20)과 같이 표현할 수 있다. - -$$ -\mathbf{g}_i = \frac{\partial \mathbf{x}}{\partial \xi^i} \tag{2.19} -$$ - -$$ -\nabla \otimes \mathbf{u} = \frac{\partial \mathbf{u}}{\partial \xi^i} \otimes \mathbf{g}^i \tag{2.20} -$$ - -변형률은 식(2.20)을 사용하여 식(2.21)과 같이 나타낼 수 있다. - -$$ -\mathbf{\varepsilon} = \frac{1}{2} \left[ \nabla \otimes \mathbf{u} + (\nabla \otimes \mathbf{u})^T \right] = \frac{1}{2} \left[ \frac{\partial \mathbf{u}}{\partial \xi^i} \otimes \mathbf{g}^i + \mathbf{g}^i \otimes \frac{\partial \mathbf{u}}{\partial \xi^i} \right] \tag{2.21} -$$ - -그리고 변형률 텐서(strain tensor)에서 공변 성분(covariant component)을 diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/유한요소해석법을이용한쉘구조물의동적좌굴해석_002.md b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/유한요소해석법을이용한쉘구조물의동적좌굴해석_002.md deleted file mode 100644 index 006b945..0000000 --- a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/유한요소해석법을이용한쉘구조물의동적좌굴해석_002.md +++ /dev/null @@ -1,432 +0,0 @@ -얻기 위해 변형률 텐서 좌우에 공변 기저(covariant basis)를 내적하면, 식(2.22)와 같은 결과를 얻을 수 있다. - -$$ -\mathcal{E}_{pq} = \mathbf{g}_{p} \cdot \mathbf{\epsilon} \cdot \mathbf{g}_{q} -= \frac{1}{2} \mathbf{g}_{p} \cdot \left[ \frac{\partial \mathbf{u}}{\partial \xi^{i}} \otimes \mathbf{g}^{i} + \mathbf{g}^{i} \otimes \frac{\partial \mathbf{u}}{\partial \xi^{i}} \right] \cdot \mathbf{g}_{q} -= \frac{1}{2} \left[ \mathbf{g}_{p} \cdot \frac{\partial \mathbf{u}}{\partial \xi^{q}} + \frac{\partial \mathbf{u}}{\partial \xi^{p}} \cdot \mathbf{g}_{q} \right] \tag{2.22} -$$ - -그리고 식(2.22)를 식(2.19)를 사용하여 표현하면 변형률 텐서의 공변 성분(covariant component)은 식(2.23)과 같이 나타낼 수 있다. - -$$ -\varepsilon_{pq} = \frac{1}{2} \left[ \frac{\partial \mathbf{x}}{\partial \xi^p} \cdot \frac{\partial \mathbf{u}}{\partial \xi^q} + \frac{\partial \mathbf{x}}{\partial \xi^q} \cdot \frac{\partial \mathbf{u}}{\partial \xi^p} \right] \tag{2.23} -$$ - -이와 같은 방법으로 Green-Lagrange 변형률을 표현하기 위해서 우선 Green-Lagrange 변형률은 식(2.24)와 같이 정의 할 수 있다. - -$$ -\mathbf{E} = \frac{1}{2} \left( \mathbf{F}^{T} \mathbf{F} - \mathbf{I} \right) = \frac{1}{2} \left( \frac{\partial (\mathbf{X} + \mathbf{u})^{T}}{\partial \mathbf{X}} \frac{\partial (\mathbf{X} + \mathbf{u})}{\partial \mathbf{X}} - \mathbf{I} \right) -= \frac{1}{2} \left( \left[ \mathbf{I} + \frac{\partial \mathbf{u}}{\partial \mathbf{X}} \right]^{T} \left[ \mathbf{I} + \frac{\partial \mathbf{u}}{\partial \mathbf{X}} \right] - \mathbf{I} \right) = \frac{1}{2} \left( \left[ \frac{\partial \mathbf{u}}{\partial \mathbf{X}} \right] + \left[ \frac{\partial \mathbf{u}}{\partial \mathbf{X}} \right]^{T} + \left[ \frac{\partial \mathbf{u}}{\partial \mathbf{X}} \right]^{T} \left[ \frac{\partial \mathbf{u}}{\partial \mathbf{X}} \right] \right) -= \frac{1}{2} \left( \frac{\partial \mathbf{u}}{\partial \xi^{i}} \otimes \mathbf{G}^{i} + \mathbf{G}^{i} \otimes \frac{\partial \mathbf{u}}{\partial \xi^{i}} + \left( \frac{\partial \mathbf{u}}{\partial \xi^{i}} \cdot \frac{\partial \mathbf{u}}{\partial \xi^{j}} \right) \mathbf{G}^{i} \otimes \mathbf{G}^{j} \right) -(2.24) -$$ - -그리고 Green-Lagrange 변형률 텐서에서 공변 성분(covariant component)을 얻기 위해 Green-Lagrange 변형률 텐서 좌우에 공변 기저(covariant basis)를 내적하면, 식(2.25)와 같은 결과를 얻을 수 있다. - -$$ -\begin{split} E_{pq} &= \mathbf{G}_{p} \cdot \mathbf{E} \cdot \mathbf{G}_{q} \\ &= \frac{1}{2} \mathbf{G}_{p} \cdot \left( \frac{\partial \mathbf{u}}{\partial \xi^{i}} \otimes \mathbf{G}^{i} + \mathbf{G}^{i} \otimes \frac{\partial \mathbf{u}}{\partial \xi^{i}} + \left( \frac{\partial \mathbf{u}}{\partial \xi^{i}} \cdot \frac{\partial \mathbf{u}}{\partial \xi^{j}} \right) \mathbf{G}^{i} \otimes \mathbf{G}^{j} \right) \cdot \mathbf{G}_{q} \\ &= \frac{1}{2} \left( \left( \mathbf{G}_{p} \cdot \frac{\partial \mathbf{u}}{\partial \xi^{q}} \right) + \left( \frac{\partial \mathbf{u}}{\partial \xi^{p}} \cdot \mathbf{G}_{q} \right) + \left( \frac{\partial \mathbf{u}}{\partial \xi^{p}} \cdot \frac{\partial \mathbf{u}}{\partial \xi^{q}} \right) \right) \\ &= \frac{1}{2} \left( \left( \frac{\partial \mathbf{X}}{\partial \xi^{p}} \cdot \frac{\partial \mathbf{u}}{\partial \xi^{q}} \right) + \left( \frac{\partial \mathbf{u}}{\partial \xi^{p}} \cdot \frac{\partial \mathbf{X}}{\partial \xi^{q}} \right) + \left( \frac{\partial \mathbf{u}}{\partial \xi^{p}} \cdot \frac{\partial \mathbf{u}}{\partial \xi^{q}} \right) \right) \end{split} -$$ - -식(2.25)를 변위에 대한 증분형태(incremental form)로 나타내기 위해 $\mathbf{u} = \mathbf{u}_t + \Delta \mathbf{u}$ 을 대입하여 정리하면, 식(2.26)와 같은 결과를 얻을 수 있다. 이 때 식(2.26)의 첫 번째 항은 $\Delta \mathbf{u}$ 에 대한 상수 항이고, 두 번째 항은 $\Delta \mathbf{u}$ 에 대한 선형 항 그리고 세 번째 항은 $\Delta \mathbf{u}$ 에 대한 비선형 항을 나타내며, 이를 간략하게 기호로 표시하면 다음과 같이 쓸 수 있다. - -$$ -\begin{split} E_{pq} &= \frac{1}{2} \Biggl( \Biggl( \frac{\partial \mathbf{X}}{\partial \xi^{p}} \cdot \frac{\partial (\mathbf{u}_{t} + \Delta \mathbf{u})}{\partial \xi^{q}} \Biggr) + \Biggl( \frac{\partial (\mathbf{u}_{t} + \Delta \mathbf{u})}{\partial \xi^{p}} \cdot \frac{\partial \mathbf{X}}{\partial \xi^{q}} \Biggr) + \Biggl( \frac{\partial (\mathbf{u}_{t} + \Delta \mathbf{u})}{\partial \xi^{p}} \cdot \frac{\partial (\mathbf{u}_{t} + \Delta \mathbf{u})}{\partial \xi^{q}} \Biggr) \Biggr) \\ &= \frac{1}{2} \Biggl( \Biggl( \frac{\partial \mathbf{X}}{\partial \xi^{p}} \cdot \frac{\partial (\mathbf{u}_{t})}{\partial \xi^{q}} \Biggr) + \Biggl( \frac{\partial (\mathbf{u}_{t})}{\partial \xi^{p}} \cdot \frac{\partial \mathbf{X}}{\partial \xi^{q}} \Biggr) + \Biggl( \frac{\partial (\mathbf{u}_{t})}{\partial \xi^{p}} \cdot \frac{\partial (\mathbf{u}_{t})}{\partial \xi^{q}} \Biggr) \Biggr) \\ &+ \frac{1}{2} \Biggl( \Biggl( \frac{\partial \mathbf{X}}{\partial \xi^{p}} \cdot \frac{\partial (\Delta \mathbf{u})}{\partial \xi^{q}} \Biggr) + \Biggl( \frac{\partial (\Delta \mathbf{u})}{\partial \xi^{p}} \cdot \frac{\partial \mathbf{X}}{\partial \xi^{q}} \Biggr) + \Biggl( \frac{\partial (\mathbf{u}_{t})}{\partial \xi^{p}} \cdot \frac{\partial (\Delta \mathbf{u})}{\partial \xi^{q}} \Biggr) + \Biggl( \frac{\partial (\Delta \mathbf{u})}{\partial \xi^{p}} \cdot \frac{\partial (\Delta \mathbf{u})}{\partial \xi^{q}} \Biggr) \Biggr) \\ &+ \frac{1}{2} \Biggl( \frac{\partial (\Delta \mathbf{u})}{\partial \xi^{p}} \cdot \frac{\partial (\Delta \mathbf{u})}{\partial \xi^{q}} \Biggr) \\ &= E_{0_{pq}} + \Delta E_{pq} + \Delta^{2} E_{pq} \end{split} -$$ - -가상 일에 대한 변형률을 구하기 위해 Green-Lagrange 변형률에 변분(variation)을 취하면 식(2.27)과 같이 나타낼 수 있다. - -$$ -\delta \mathbf{E} = \frac{1}{2} \left[ \left[ \frac{\partial (\delta \mathbf{u})}{\partial \mathbf{X}} \right] + \left[ \frac{\partial (\delta \mathbf{u})}{\partial \mathbf{X}} \right]^{T} + \left[ \frac{\partial (\delta \mathbf{u})}{\partial \mathbf{X}} \right]^{T} \left[ \frac{\partial \mathbf{u}}{\partial \mathbf{X}} \right] + \left[ \frac{\partial \mathbf{u}}{\partial \mathbf{X}} \right]^{T} \left[ \frac{\partial (\delta \mathbf{u})}{\partial \mathbf{X}} \right] \right] -= \frac{1}{2} \left[ \frac{\partial (\delta \mathbf{u})}{\partial \xi^{i}} \otimes \mathbf{G}^{i} + \mathbf{G}^{i} \otimes \frac{\partial (\delta \mathbf{u})}{\partial \xi^{i}} + \left( \frac{\partial (\delta \mathbf{u})}{\partial \xi^{i}} \cdot \frac{\partial \mathbf{u}}{\partial \xi^{j}} \right) \mathbf{G}^{i} \otimes \mathbf{G}^{j} + \left( \frac{\partial \mathbf{u}}{\partial \xi^{i}} \cdot \frac{\partial (\delta \mathbf{u})}{\partial \xi^{j}} \right) \mathbf{G}^{i} \otimes \mathbf{G}^{j} \right] -(2.27) -$$ - -위와 마찬가지로 Green-Lagrange 변형률 텐서에서 공변 성분(covariant component)을 얻기 위해 식(2.27)의 좌우에 공변 기저(covariant basis)를 내적하면, 식(2.28)과 같은 결과를 얻을 수 있다. - -$$ -\begin{split} \delta E_{pq} &= \mathbf{G}_{p} \cdot \delta \mathbf{E} \cdot \mathbf{G}_{q} \\ &= \frac{1}{2} \mathbf{G}_{p} \cdot \left( \frac{\partial (\delta \mathbf{u})}{\partial \xi^{i}} \otimes \mathbf{G}^{i} + \mathbf{G}^{i} \otimes \frac{\partial (\delta \mathbf{u})}{\partial \xi^{i}} \right) \\ &+ \left( \frac{\partial (\delta \mathbf{u})}{\partial \xi^{i}} \cdot \frac{\partial \mathbf{u}}{\partial \xi^{j}} \right) \mathbf{G}^{i} \otimes \mathbf{G}^{j} + \left( \frac{\partial \mathbf{u}}{\partial \xi^{i}} \cdot \frac{\partial (\delta \mathbf{u})}{\partial \xi^{j}} \right) \mathbf{G}^{i} \otimes \mathbf{G}^{j} \right) \cdot \mathbf{G}_{q} \\ &= \frac{1}{2} \left( \mathbf{G}_{p} \cdot \frac{\partial (\delta \mathbf{u})}{\partial \xi^{q}} + \frac{\partial (\delta \mathbf{u})}{\partial \xi^{p}} \cdot \mathbf{G}_{q} + \left( \frac{\partial (\delta \mathbf{u})}{\partial \xi^{p}} \cdot \frac{\partial \mathbf{u}}{\partial \xi^{q}} \right) + \left( \frac{\partial \mathbf{u}}{\partial \xi^{p}} \cdot \frac{\partial (\delta \mathbf{u})}{\partial \xi^{q}} \right) \right) \\ &= \frac{1}{2} \left( \frac{\partial \mathbf{X}}{\partial \xi^{p}} \cdot \frac{\partial (\delta \mathbf{u})}{\partial \xi^{q}} + \frac{\partial (\delta \mathbf{u})}{\partial \xi^{p}} \cdot \frac{\partial \mathbf{X}}{\partial \xi^{q}} + \left( \frac{\partial (\delta \mathbf{u})}{\partial \xi^{p}} \cdot \frac{\partial \mathbf{u}}{\partial \xi^{q}} \right) + \left( \frac{\partial \mathbf{u}}{\partial \xi^{p}} \cdot \frac{\partial (\delta \mathbf{u})}{\partial \xi^{q}} \right) \right) \end{split} -$$ - -식(2.28)을 변위에 대한 증분형태로 나타내기 위해 $\mathbf{u} = \mathbf{u}_t + \Delta \mathbf{u}$ 을 대입하여 정리하면, 식(2.29)와 같은 결과를 얻을 수 있다. 이 때 식(2.29)의 첫 번째 항은 $\Delta \mathbf{u}$ 에 대한 상수 항이고, 두 번째 항은 $\Delta \mathbf{u}$ 에 대한 선형 항을 나타내며, 이를 기호로 간략하게 표현하면 다음과 같이쓸 수 있다. 여기서 $\mathbf{u}_t$ 는 고정된 값이므로 $\mathbf{u}_t$ 에 대한 변분(variation)은 영(零)이 된다. - -$$ -\begin{split} \delta E_{pq} &= \frac{1}{2} \begin{pmatrix} \frac{\partial \mathbf{X}}{\partial \xi^{p}} \cdot \frac{\partial (\delta \mathbf{u})}{\partial \xi^{q}} + \frac{\partial (\delta \mathbf{u})}{\partial \xi^{p}} \cdot \frac{\partial \mathbf{X}}{\partial \xi^{q}} \\ &+ \left( \frac{\partial (\delta \mathbf{u})}{\partial \xi^{p}} \cdot \frac{\partial (\mathbf{u}_{t} + \Delta \mathbf{u})}{\partial \xi^{q}} \right) + \left( \frac{\partial (\mathbf{u}_{t} + \Delta \mathbf{u})}{\partial \xi^{p}} \cdot \frac{\partial (\delta \mathbf{u})}{\partial \xi^{q}} \right) \end{pmatrix} \\ &= \frac{1}{2} \begin{pmatrix} \frac{\partial \mathbf{X}}{\partial \xi^{p}} \cdot \frac{\partial (\delta \mathbf{u})}{\partial \xi^{q}} + \frac{\partial (\delta \mathbf{u})}{\partial \xi^{p}} \cdot \frac{\partial \mathbf{X}}{\partial \xi^{q}} + \left( \frac{\partial (\delta \mathbf{u})}{\partial \xi^{p}} \cdot \frac{\partial \mathbf{u}_{t}}{\partial \xi^{q}} \right) + \left( \frac{\partial \mathbf{u}_{t}}{\partial \xi^{p}} \cdot \frac{\partial (\delta \mathbf{u})}{\partial \xi^{q}} \right) \end{pmatrix} \\ &+ \frac{1}{2} \left( \left( \frac{\partial (\delta \mathbf{u})}{\partial \xi^{p}} \cdot \frac{\partial \Delta \mathbf{u}}{\partial \xi^{q}} \right) + \left( \frac{\partial \Delta \mathbf{u}}{\partial \xi^{p}} \cdot \frac{\partial (\delta \mathbf{u})}{\partial \xi^{q}} \right) \right) \\ &= \delta \left( E_{0_{pq}} + \Delta E_{pq} + \Delta^{2} E_{pq} \right) \\ &= \delta \Delta E_{pq} + \delta \Delta^{2} E_{pq} \\ &= \delta \mathcal{E}_{0_{pq}} + \delta \Delta \mathcal{E}_{pq} \end{split} -$$ - -식(2.28)과 식(2.29)의 결과를 식(2.18a)의 $\int_{V_0} \delta \mathbf{E} : \mathbf{S} dV_0$ 항에 대입하면식(2.30)과 같이 정리할 수 있다. 이 때 식(2.30)의 첫 번째 항은 상수항이고, 두 번째 항은 선형 항, 세 번째 항은 고차 항이며, 네 번째 항은 선형 항, 다섯 번째와 여섯 번째 항은 고차 항을 나타낸다. - -$$ -\int_{V_{0}} \delta \mathbf{E} : \mathbf{S} dV_{0} = \int_{V_{0}} \delta E_{ij} S^{ij} dV_{0} = \int_{V_{0}} \delta E_{ij} C^{ijkl} E_{kl} dV_{0} -= \int_{V_{0}} \left( \delta \mathcal{E}_{0_{ij}} + \delta \Delta \mathcal{E}_{ij} \right) C^{ijkl} \left( E_{0_{kl}} + \Delta E_{kl} + \Delta^{2} E_{kl} \right) dV_{0} -= \int_{V_{0}} \left( \delta \mathcal{E}_{0_{ij}} \right) C^{ijkl} \left( E_{0_{kl}} \right) dV_{0} + \int_{V_{0}} \left( \delta \mathcal{E}_{0_{ij}} \right) C^{ijkl} \left( \Delta E_{kl} \right) dV_{0} -+ \int_{V_{0}} \left( \delta \mathcal{E}_{0_{ij}} \right) C^{ijkl} \left( \Delta^{2} E_{kl} \right) dV_{0} + \int_{V_{0}} \left( \delta \Delta \mathcal{E}_{ij} \right) C^{ijkl} \left( E_{0_{kl}} \right) dV_{0} -+ \int_{V_{0}} \left( \delta \Delta \mathcal{E}_{ij} \right) C^{ijkl} \left( \Delta E_{kl} \right) dV_{0} + \int_{V_{0}} \left( \delta \Delta \mathcal{E}_{ij} \right) C^{ijkl} \left( \Delta^{2} E_{kl} \right) dV_{0} -$$ - -따라서 식(2.30)에서 고차 항을 제외하면 식(2.31)과 같은 결과를 얻을 수 있으며, 첫 번째 항은 상수 항이고, 두 번째와 세 번째 항은 선형 - -항을 나타낸다. - -$$ -\int_{V_0} \delta E_{ij} S^{ij} dV_0 \approx \int_{V_0} \left( \delta \mathcal{E}_{0_{ij}} \right) C^{ijkl} \left( E_{0_{kl}} \right) dV_0 + \int_{V_0} \left( \delta \mathcal{E}_{0_{ij}} \right) C^{ijkl} \left( \Delta E_{kl} \right) dV_0 + \int_{V_0} \left( \delta \Delta \mathcal{E}_{ij} \right) C^{ijkl} \left( E_{0_{kl}} \right) dV_0 \tag{2.31} -$$ - -앞서 정의한 MITC4 쉘 요소의 위치 벡터 식(2.1), (2.2)를 행렬 형태로 표현하면 식(2.32), (2.33)과 같이 나타낼 수 있고, 이를 간단하게 기호로 표현하면 다음과 같이 표현할 수 있다. 이 때 $\mathbf{1}_3$ 은 3행 3열의 단위 행렬(identity matrix)을 나타낸다. - -$$ -\mathbf{X} = \begin{bmatrix} N_{1}\mathbf{1}_{3} & \frac{\zeta}{2}t_{1}N_{1}\mathbf{1}_{3} & N_{2}\mathbf{1}_{3} & \frac{\zeta}{2}t_{2}N_{2}\mathbf{1}_{3} & N_{3}\mathbf{1}_{3} & \frac{\zeta}{2}t_{3}N_{3}\mathbf{1}_{3} & N_{4}\mathbf{1}_{3} & \frac{\zeta}{2}t_{4}N_{4}\mathbf{1}_{3} \end{bmatrix} \begin{cases} {}^{0}\mathbf{X}_{1} \\ {}^{0}\mathbf{X}_{2} \\ {}^{0}\mathbf{X}_{2} \\ {}^{0}\mathbf{X}_{3} \\ {}^{0}\mathbf{X}_{3} \\ {}^{0}\mathbf{X}_{3} \\ {}^{0}\mathbf{X}_{4} \\ {}^{0}\mathbf{Y}_{4}^{n} \end{bmatrix} -= \mathbf{S}\mathbf{X}_{0} -(2.32) -$$ - -$$ -\mathbf{x} = \begin{bmatrix} N_{1}\mathbf{1}_{3} & \frac{\zeta}{2}t_{1}N_{1}\mathbf{1}_{3} & N_{2}\mathbf{1}_{3} & \frac{\zeta}{2}t_{2}N_{2}\mathbf{1}_{3} & N_{3}\mathbf{1}_{3} & \frac{\zeta}{2}t_{3}N_{3}\mathbf{1}_{3} & N_{4}\mathbf{1}_{3} & \frac{\zeta}{2}t_{4}N_{4}\mathbf{1}_{3} \end{bmatrix} \begin{bmatrix} {}^{t}\mathbf{X}_{1} \\ {}^{t}\mathbf{V}_{1}^{n} \\ {}^{t}\mathbf{X}_{2} \\ {}^{t}\mathbf{V}_{2}^{n} \\ {}^{t}\mathbf{X}_{3} \\ {}^{t}\mathbf{V}_{3}^{n} \\ {}^{t}\mathbf{X}_{4} \\ {}^{t}\mathbf{V}_{4}^{n} \end{bmatrix} -$$ - -$$ -= \mathbf{S}\mathbf{X}_{t} \tag{2.33} -$$ - -또한 임의의 시간 t에서의 변위 식(2.3)과 변위의 증분 식(2.4)를 행렬 형태로 표현하면 식(2.34), (2.35)로 각각 나타낼 수 있고, 이를 간략하게 기호로 표현하면 다음과 같이 표현할 수 있다. - -$$ -\mathbf{u}_{t} = \mathbf{x} - \mathbf{X} -$$ - -$$ -= \left[ N_{1} \mathbf{1}_{3} \frac{\zeta}{2} t_{1} N_{1} \mathbf{1}_{3} \ N_{2} \mathbf{1}_{3} \frac{\zeta}{2} t_{2} N_{2} \mathbf{1}_{3} \ N_{3} \mathbf{1}_{3} \frac{\zeta}{2} t_{3} N_{3} \mathbf{1}_{3} \ N_{4} \mathbf{1}_{3} \frac{\zeta}{2} t_{4} N_{4} \mathbf{1}_{3} \right] \begin{bmatrix} {}^{\prime} \mathbf{X}_{1}^{-0} \mathbf{X}_{1} \\ {}^{\prime} \mathbf{V}_{1}^{n}^{-0} \mathbf{V}_{1}^{n} \\ {}^{\prime} \mathbf{X}_{2}^{-0} \mathbf{X}_{2} \\ {}^{\prime} \mathbf{Y}_{2}^{n}^{-0} \mathbf{V}_{2}^{n} \\ {}^{\prime} \mathbf{X}_{3}^{-0} \mathbf{X}_{3} \\ {}^{\prime} \mathbf{V}_{3}^{n}^{-0} \mathbf{V}_{3}^{n} \\ {}^{\prime} \mathbf{X}_{4}^{-0} \mathbf{X}_{4} \\ {}^{\prime} \mathbf{V}_{4}^{n}^{-0} \mathbf{V}_{4}^{n} \end{bmatrix} -= \mathbf{S}(\mathbf{X}_{t} - \mathbf{X}_{0}) -(2.34) -\Delta \mathbf{u} = \begin{bmatrix} N_{1} \mathbf{1}_{3} - \frac{\zeta}{2} t_{1} N_{1}^{t} \mathbf{V}_{1}^{2} \frac{\zeta}{2} t_{1} N_{1}^{t} \mathbf{V}_{1}^{1} \cdots N_{4} \mathbf{1}_{3} - \frac{\zeta}{2} t_{4} N_{4}^{t} \mathbf{V}_{4}^{2} \frac{\zeta}{2} t_{4} N_{4}^{t} \mathbf{V}_{4}^{1} \end{bmatrix} \begin{bmatrix} \Delta \mathbf{u}_{1} \\ \alpha_{1} \\ \beta_{1} \\ \Delta \mathbf{u}_{2} \\ \alpha_{2} \\ \beta_{3} \\ \Delta \mathbf{u}_{3} \\ \alpha_{3} \\ \beta_{3} \\ \Delta \mathbf{u}_{4} \\ \alpha_{4} \\ \beta_{4} \end{bmatrix} -= \mathbf{N}(\Delta \mathbf{U}) -(2.35) -$$ - -이 때 $\mathbf{V}^n$ 이 현재 형상의 변화에 따라 변하므로 $\mathbf{V}^1$ 과 $\mathbf{V}^2$ 도 계속 - -변하게 되고, 행렬 N도 현재 형상에 따라 변하게 된다. 즉 $V^1$ 과 $V^2$ 는 회전각 $\alpha$ , $\beta$ 에 따라 회전하게 되고, 두 축의 회전에 의해 $V^n$ 이 시간에 따라 변하게 된다. 따라서 시간에 따른 회전각 $\alpha$ , $\beta$ 의 변화에 따라 $V^n$ 을 계산해 주어야 하고, 또한 그에 따라 $V^1$ 과 $V^2$ 도 다시 계산해 주어야 한다. 이에 대한 자세한 내용은 2.2.1 절에서 다루도록 하겠다. - -식(2.32), (2.33), (2.34), (2.35)의 행렬식을 식(2.31)에서 $\delta \mathcal{E}_{0_{pq}}, \, \delta \! \Delta \mathcal{E}_{pq}, \, E_{0_{pq}}, \, \Delta E_{pq}$ 의 각각의 항에 대입하면 식(2.36), (2.37), (2.38), (2.39)와 같이 나타낼 수 있다. - -$$ -E_{0_{pq}} = \frac{1}{2} \left[ \left( \frac{\partial \mathbf{X}}{\partial \xi^{p}} \cdot \frac{\partial (\mathbf{u}_{t})}{\partial \xi^{q}} \right) + \left( \frac{\partial (\mathbf{u}_{t})}{\partial \xi^{p}} \cdot \frac{\partial \mathbf{X}}{\partial \xi^{q}} \right) + \left( \frac{\partial (\mathbf{u}_{t})}{\partial \xi^{p}} \cdot \frac{\partial (\mathbf{u}_{t})}{\partial \xi^{q}} \right) \right] -= \frac{1}{2} \left\{ \mathbf{X}_{0} \right\}^{T} \left[ \frac{\partial \mathbf{S}^{T}}{\partial \xi^{p}} \frac{\partial \mathbf{S}}{\partial \xi^{q}} + \frac{\partial \mathbf{S}^{T}}{\partial \xi^{q}} \frac{\partial \mathbf{S}}{\partial \xi^{p}} \right] \left\{ \mathbf{X}_{t} - \mathbf{X}_{0} \right\} -+ \frac{1}{2} \left\{ \mathbf{X}_{t} - \mathbf{X}_{0} \right\}^{T} \left[ \frac{\partial \mathbf{S}^{T}}{\partial \xi^{p}} \frac{\partial \mathbf{S}}{\partial \xi^{q}} + \frac{\partial \mathbf{S}^{T}}{\partial \xi^{q}} \frac{\partial \mathbf{S}}{\partial \xi^{p}} \right] \left\{ \mathbf{X}_{t} - \mathbf{X}_{0} \right\} -= \frac{1}{2} \left\{ \mathbf{X}_{0} \right\}^{T} \left[ \frac{\partial \mathbf{S}^{T}}{\partial \xi^{p}} \frac{\partial \mathbf{S}}{\partial \xi^{q}} + \frac{\partial \mathbf{S}^{T}}{\partial \xi^{q}} \frac{\partial \mathbf{S}}{\partial \xi^{p}} \right] \left\{ \mathbf{X}_{t} - \mathbf{X}_{0} \right\} -+ \frac{1}{2} \left\{ \mathbf{X}_{t} - \mathbf{X}_{0} \right\}^{T} \frac{1}{2} \left[ \frac{\partial \mathbf{S}^{T}}{\partial \xi^{p}} \frac{\partial \mathbf{S}}{\partial \xi^{q}} + \frac{\partial \mathbf{S}^{T}}{\partial \xi^{q}} \frac{\partial \mathbf{S}}{\partial \xi^{p}} \right] \left\{ \mathbf{X}_{t} - \mathbf{X}_{0} \right\} -= \frac{1}{2} \left\{ \mathbf{X}_{t} + \mathbf{X}_{0} \right\}^{T} \frac{1}{2} \left[ \frac{\partial \mathbf{S}^{T}}{\partial \xi^{p}} \frac{\partial \mathbf{S}}{\partial \xi^{q}} + \frac{\partial \mathbf{S}^{T}}{\partial \xi^{q}} \frac{\partial \mathbf{S}}{\partial \xi^{p}} \right] \left\{ \mathbf{X}_{t} - \mathbf{X}_{0} \right\} -= \frac{1}{2} \left\{ \mathbf{X}_{t} + \mathbf{X}_{0} \right\}^{T} \left[ \mathbf{e}_{pq} \right] \left\{ \mathbf{X}_{t} - \mathbf{X}_{0} \right\} -$$ - -$$ -\begin{split} \Delta E_{pq} &= \frac{1}{2} \Biggl[ \Biggl( \frac{\partial \mathbf{X}}{\partial \xi^{p}} \cdot \frac{\partial (\Delta \mathbf{u})}{\partial \xi^{q}} \Biggr) + \Biggl( \frac{\partial (\Delta \mathbf{u})}{\partial \xi^{p}} \cdot \frac{\partial \mathbf{X}}{\partial \xi^{q}} \Biggr) + \Biggl( \frac{\partial (\mathbf{u}_{t})}{\partial \xi^{p}} \cdot \frac{\partial (\Delta \mathbf{u})}{\partial \xi^{q}} \Biggr) + \Biggl( \frac{\partial (\Delta \mathbf{u})}{\partial \xi^{p}} \cdot \frac{\partial (\Delta \mathbf{u})}{\partial \xi^{q}} \Biggr) + \Biggl( \frac{\partial (\Delta \mathbf{u})}{\partial \xi^{p}} \cdot \frac{\partial (\Delta \mathbf{u})}{\partial \xi^{p}} \Biggr) \Biggr] \Biggr\} -&= \frac{1}{2} \Biggl\{ \mathbf{X}_{0} \Biggr\}^{T} \Biggl[ \frac{\partial \mathbf{S}^{T}}{\partial \xi^{p}} \frac{\partial \mathbf{N}}{\partial \xi^{q}} + \frac{\partial \mathbf{S}^{T}}{\partial \xi^{p}} \frac{\partial \mathbf{N}}{\partial \xi^{p}} \Biggr] \Biggl\{ \Delta \mathbf{U} \Biggr\} -&= \frac{1}{2} \Biggl\{ \mathbf{X}_{t} \Biggr\}^{T} \Biggl[ \frac{\partial \mathbf{S}^{T}}{\partial \xi^{p}} \frac{\partial \mathbf{N}}{\partial \xi^{q}} + \frac{\partial \mathbf{S}^{T}}{\partial \xi^{q}} \frac{\partial \mathbf{N}}{\partial \xi^{p}} \Biggr] \Biggl\{ \Delta \mathbf{U} \Biggr\} -&= \Biggl\{ \mathbf{X}_{t} \Biggr\}^{T} \Biggl[ \mathbf{a}_{pq} \Biggr] \Biggl\{ \Delta \mathbf{U} \Biggr\} -&= \Biggl\{ \mathbf{X}_{t} \Biggr\}^{T} \Biggl[ \mathbf{a}_{pq} \Biggr] \Biggl\{ \Delta \mathbf{U} \Biggr\} -&= \Biggl\{ \mathbf{X}_{t} \Biggr\}^{T} \Biggl[ \mathbf{a}_{pq} \Biggr\} \Biggl\{ \Delta \mathbf{U} \Biggr\} -$$ - -$$ -\begin{split} \delta \! \Delta \! E_{pq} &= \frac{1}{2} \! \left( \frac{\partial \mathbf{X}}{\partial \boldsymbol{\xi}^p} \cdot \frac{\partial (\delta \mathbf{u})}{\partial \boldsymbol{\xi}^q} + \frac{\partial (\delta \mathbf{u})}{\partial \boldsymbol{\xi}^p} \cdot \frac{\partial \mathbf{X}}{\partial \boldsymbol{\xi}^q} + \left( \frac{\partial (\delta \mathbf{u})}{\partial \boldsymbol{\xi}^p} \cdot \frac{\partial \mathbf{u}_t}{\partial \boldsymbol{\xi}^q} \right) + \left( \frac{\partial \mathbf{u}_t}{\partial \boldsymbol{\xi}^p} \cdot \frac{\partial (\delta \mathbf{u})}{\partial \boldsymbol{\xi}^q} \right) \right) \\ &= \frac{1}{2} \big\{ \mathbf{X}_0 \big\}^T \! \left[ \frac{\partial \mathbf{S}^T}{\partial \boldsymbol{\xi}^p} \frac{\partial \mathbf{N}}{\partial \boldsymbol{\xi}^q} + \frac{\partial \mathbf{S}^T}{\partial \boldsymbol{\xi}^q} \frac{\partial \mathbf{N}}{\partial \boldsymbol{\xi}^p} \right] \! \left\{ \! \delta \! \mathbf{U} \right\} \\ &\quad + \frac{1}{2} \big\{ \mathbf{X}_t - \mathbf{X}_0 \big\}^T \! \left[ \frac{\partial \mathbf{S}^T}{\partial \boldsymbol{\xi}^p} \frac{\partial \mathbf{N}}{\partial \boldsymbol{\xi}^q} + \frac{\partial \mathbf{S}^T}{\partial \boldsymbol{\xi}^q} \frac{\partial \mathbf{N}}{\partial \boldsymbol{\xi}^p} \right] \! \left\{ \! \delta \! \mathbf{U} \right\} \\ &\quad = \frac{1}{2} \big\{ \mathbf{X}_t \big\}^T \! \left[ \frac{\partial \mathbf{S}^T}{\partial \boldsymbol{\xi}^p} \frac{\partial \mathbf{N}}{\partial \boldsymbol{\xi}^q} + \frac{\partial \mathbf{S}^T}{\partial \boldsymbol{\xi}^q} \frac{\partial \mathbf{N}}{\partial \boldsymbol{\xi}^p} \right] \! \left\{ \! \delta \! \mathbf{U} \right\} \end{split} -$$ - -$$ -= \{\mathbf{X}_{t}\}^{T} \left[ \mathbf{a}_{na} \right] \{ \delta \mathbf{U} \} -$$ - -(2.38) - -$$ -\delta\Delta \mathcal{E} = \frac{1}{2} \left( \left( \frac{\partial (\partial \mathbf{u})}{\partial \xi^{p}} \cdot \frac{\partial \Delta \mathbf{u}}{\partial \xi^{q}} \right) + \left( \frac{\partial \Delta \mathbf{u}}{\partial \xi^{p}} \cdot \frac{\partial (\partial \mathbf{u})}{\partial \xi^{q}} \right) \right) -= \frac{1}{2} \left\{ \delta \mathbf{U} \right\}^{T} \left[ \frac{\partial \mathbf{N}^{T}}{\partial \xi^{p}} \frac{\partial \mathbf{N}}{\partial \xi^{q}} + \frac{\partial \mathbf{N}^{T}}{\partial \xi^{q}} \frac{\partial \mathbf{N}}{\partial \xi^{p}} \right] \left\{ \Delta \mathbf{U} \right\} -= \left\{ \delta \mathbf{U} \right\}^{T} \left[ \mathbf{c}_{pq} \right] \left\{ \Delta \mathbf{U} \right\} \tag{2.39} -$$ - -식(2.36), (2.37), (2.38), (2.39)을 식(2.31)에 대입하면 식(2.40)과 같이 정리할 수 있다. - -$$ -\int_{V_0} \delta E_{ij} S^{ij} dV_0 \approx \int_{V_0} \left( \left\{ \delta \mathbf{U} \right\}^T \left[ \mathbf{a}_{ij} \right]^T \left\{ \mathbf{X}_t \right\} \right) C^{ijkl} \left( \frac{1}{2} \left\{ \mathbf{X}_t + \mathbf{X}_0 \right\}^T \left[ \mathbf{e}_{kl} \right] \left\{ \mathbf{X}_t - \mathbf{X}_0 \right\} \right) dV_0 -+ \int_{V_0} \left( \left\{ \delta \mathbf{U} \right\}^T \left[ \mathbf{a}_{ij} \right]^T \left\{ \mathbf{X}_t \right\} \right) C^{ijkl} \left( \left\{ \mathbf{X}_t \right\}^T \left[ \mathbf{a}_{kl} \right] \left\{ \Delta \mathbf{U} \right\} \right) dV_0 -+ \int_{V_0} \left( \left\{ \delta \mathbf{U} \right\}^T \left[ \mathbf{c}_{ij} \right] \left\{ \Delta \mathbf{U} \right\} \right) C^{ijkl} \left( \frac{1}{2} \left\{ \mathbf{X}_t + \mathbf{X}_0 \right\}^T \left[ \mathbf{e}_{kl} \right] \left\{ \mathbf{X}_t - \mathbf{X}_0 \right\} \right) dV_0 \tag{2.40} -$$ - -그리고 현재 형상( t =0)일 때의 2차 Piola-Kirchhoff 응력을 식(2.41)과 같이 정의해주면, 식(2.40)은 식(2.42)와 같이 정리할 수 있다. - -$$ -S_0^{ij} = C^{ijkl} \left( \frac{1}{2} \{ \mathbf{X}_t + \mathbf{X}_0 \}^T [\mathbf{e}_{kl}] \{ \mathbf{X}_t - \mathbf{X}_0 \} \right) -\int_{V_0} \delta E_{ij} S^{ij} dV_0 \approx \int_{V_0} \left( \{ \delta \mathbf{U} \}^T [\mathbf{a}_{ij}]^T \{ \mathbf{X}_t \} \right) S_0^{ij} dV_0 -+ \int_{V_0} \left( \{ \delta \mathbf{U} \}^T [\mathbf{a}_{ij}]^T \{ \mathbf{X}_t \} \right) C^{ijkl} \left( \{ \mathbf{X}_t \}^T [\mathbf{a}_{kl}] \{ \Delta \mathbf{U} \} \right) dV_0 -+ \int_{V_0} \left( \{ \delta \mathbf{U} \}^T [\mathbf{c}_{ij}] \{ \Delta \mathbf{U} \} \right) S_0^{ij} dV_0 -= \{ \delta \mathbf{U} \}^T \left[ \int_{V_0} \left( S_0^{ij} [\mathbf{a}_{ii}]^T \right) dV_0 \right] \{ \mathbf{X}_t \} -$$ - -$$ -= \{ \partial \mathbf{U} \}^{T} \left[ \int_{V_{0}} \left( S_{0}^{ij} \left[ \mathbf{a}_{ij} \right]^{T} \right) dV_{0} \right] \{ \mathbf{X}_{t} \} -+ \{ \partial \mathbf{U} \}^{T} \left[ \int_{V_{0}} \left( \left[ \mathbf{a}_{ij} \right]^{T} \left\{ \mathbf{X}_{t} \right\} \right) C^{ijkl} \left( \left\{ \mathbf{X}_{t} \right\}^{T} \left[ \mathbf{a}_{kl} \right] \right) dV_{0} \right] \{ \Delta \mathbf{U} \} -+ \{ \partial \mathbf{U} \}^{T} \left[ \int_{V_{0}} \left( S_{0}^{ij} \left[ \mathbf{c}_{ij} \right] \right) dV_{0} \right] \{ \Delta \mathbf{U} \} -(2.42) -$$ - -식(2.42)를 고유 좌표계(natural coordinate system)에서 2×2×2 가우스 적분(Gauss integration)해주면, 식(2.43)과 같이 쓸 수 있다. - -$$ -\int_{V_{0}} \delta E_{ij} S^{ij} dV_{0} -\approx \{\delta \mathbf{U}\}^{T} \left[ \iiint_{\xi} \left( S_{0}^{ij} [\mathbf{a}_{ij}]^{T} \right) |J| \, d\xi d\eta d\zeta \right] \{\mathbf{X}_{t}\} -+ \{\delta \mathbf{U}\}^{T} \left[ \iiint_{\xi} \left( [\mathbf{a}_{ij}]^{T} \{\mathbf{X}_{t}\} \right) [T]^{T} [\widetilde{D}^{ijkl}] T] (\{\mathbf{X}_{t}\}^{T} [\mathbf{a}_{kl}] \right) |J| \, d\xi d\eta d\zeta \right] \{\Delta \mathbf{U}\} -+ \{\delta \mathbf{U}\}^{T} \left[ \iiint_{\xi} \left( S_{0}^{ij} [\mathbf{c}_{ij}] \right) |J| \, d\xi d\eta d\zeta \right] \{\Delta \mathbf{U}\} -= \{\delta \mathbf{U}\}^{T} \{\mathbf{F}\} \{\mathbf{X}_{t}\} + \{\delta \mathbf{U}\}^{T} [\mathbf{K}_{NL}] \{\Delta \mathbf{U}\} + \{\delta \mathbf{U}\}^{T} [\mathbf{K}_{L}] \{\Delta \mathbf{U}\} -$$ - -여기서 $\mathbf{K}_L$ 은 초기 변위와 관련된 강성 행렬(initial displacement stiffness matrix), $\mathbf{K}_{NL}$ 은 초기 응력과 관련된 강성 행렬(initial stress stiffness matrix)또는 기하 강성 행렬(geometric stiffness matrix)이고, $\mathbf{F}$ 는 변형으로 인한 힘 벡터를 나타내며, |J|는 자코비안 행렬(Jacobian matrix)에 대한 determinant를 의미한다. 그리고 $\widetilde{D}^{ijkl}$ 는 지역 직교 좌표계(local Cartesian coordinate system)에서 정의되어있는 구성 행렬(constitutive matrix)로 이를 고유 좌표계(natural coordinate system)로 변환해주기 위해 변환 행렬(transformation matrix) [T]를 사용하였다. 이에 대한 자세한 내용은 2.2.2 절에서 다루도록 하겠다. - -식(2.18a) 우변은 표면력(surface force)과 체적력(body force)을 나타내며, 식(2.44), (2.45)와 같이 이산화하여 나타낼 수 있다. - -$$ -\int_{\partial V_{0_m}} \delta \mathbf{u} \cdot \mathbf{F} \mathbf{S} \widetilde{\mathbf{n}} dA_0 = \{ \delta \mathbf{U} \}^T \int_{\partial V_{0_m}} [\mathbf{N}]^T \{ \overline{\mathbf{T}} \} dA_0 = \{ \delta \mathbf{U} \}^T \{ \mathbf{Q}_T \} \tag{2.44} -$$ - -$$ -\int_{V_0} \delta \mathbf{u} \rho_0 \mathbf{f} dV_0 = \{ \delta \mathbf{U} \}^T \int_{V_0} \rho_0 [\mathbf{N}]^T \{ \mathbf{f} \} dV_0 = \{ \delta \mathbf{U} \}^T \{ \mathbf{Q}_B \} \tag{2.45} -$$ - -식(2.43), (2.44), (2.45)을 식(2.18a)에 대입하고, 정적인 문제이므로 시간에 대한 항을 무시하면, 식(2.18a)는 식(2.46)과 같이 표현할 수 있다. - -$$ -0 = \{ \delta \mathbf{U} \}^T ([\mathbf{K}_M] + [\mathbf{K}_L]) \{ \Delta \mathbf{U} \} - \{ \delta \mathbf{U} \}^T (\{\mathbf{Q}_T\} + \{\mathbf{Q}_R\} - \{\mathbf{F}\}) \tag{2.46} -$$ - -이때 $\partial U$ 는 임의의 값이므로 식(2.46)을 만족하기 위해서는 식(2.47)과 같이 쓸 수 있고, 식(2.47)은 Newton-Raphson 형식으로 $\Delta U$ 에 대한 반복 계산을 통해 수렴값을 찾아 나감으로써 비선형 해석을 수행할 수 있다. - -$$ -([\mathbf{K}_{NL}] + [\mathbf{K}_{L}]) \{\Delta \mathbf{U}\} = \{\mathbf{Q}_{T}\} + \{\mathbf{Q}_{B}\} - \{\mathbf{F}\} \tag{2.47} -$$ - -여기서 $\mathbf{K}_{NL}$ 과 $\mathbf{K}_{L}$ 의 합이 기울기 강성 행렬(tangent stiffness matrix)이 된다. - -# 2.2.1 Finite Rotation Formulation - -쉘의 형상이 변함에 따라 각각의 노드에서 정의 된 벡터 $(\mathbf{V}^1, \mathbf{V}^2, \mathbf{V}^n)$ 역시 시간에 따라 변하게 된다. 즉 시간에 따른 회전각 $\alpha$ , $\beta$ 의 변화에 따라 $\mathbf{V}^n$ 이 변하고, 또한 그에 따라 $\mathbf{V}^1$ 과 $\mathbf{V}^2$ 도 변하게 된다. 이를 관계식으로 표현하면, 식(2.48)과 같이 쓸 수 있다. - -$$ -{}^{t+\Delta t}\mathbf{V}_I^n = {}^{t+\Delta t}_{l}\mathbf{R}_I \cdot {}^{t}\mathbf{V}_I^n \tag{2.48} -$$ - -식(2.48)은 시간이 t부터 $t+\Delta t$ 까지 변할 때 노드 I에서의 법선 벡터의 변화를 보여주는 식으로 $t+\Delta t$ , 문 회전 텐서(rotation tensor)를 의미하며, $\|t\mathbf{V}_I^n\| = \|t+\Delta t\mathbf{V}_I^n\| = 1$ 이다. [9] - -그리고 회전 텐서 $^{t+\Delta_t}$ R , 은 $\mathbf{V}^1$ , $\mathbf{V}^2$ , $\mathbf{V}^n$ 을 정규직교 기저(orthonormal basis)로 하는 좌표계에서 식(2.49)와 같이 행렬 형태로 나타낼 수 있다. [10] - -$$ -\begin{bmatrix} t + \Delta t \\ t \end{bmatrix} = \begin{bmatrix} \mathbf{1}_{3} \end{bmatrix} + \frac{\sin(\widetilde{\Theta}_{I})}{\widetilde{\Theta}_{I}} [\Theta_{I}] + \frac{1}{2} \begin{bmatrix} \sin(\widetilde{\Theta}_{I}/2) \\ \widetilde{\Theta}_{I}/2 \end{bmatrix}^{2} [\Theta_{I}]^{2} \tag{2.49} -$$ - -이 때 $\left[\mathbf{1}_3\right]$ 은 3행 3열의 단위 행렬을 의미하며, $\widetilde{\theta}_I$ 와 $\left[\Theta_I\right]$ 는 각각식(2.50),(2.51)과 같이 나타낼 수 있다. - -$$ -\widetilde{\theta}_I = \left[ (\alpha_I)^2 + (\beta_I)^2 \right]^{\frac{1}{2}} \tag{2.50} -$$ - -$$ -\begin{bmatrix} \Theta_I \end{bmatrix} = \begin{bmatrix} 0 & 0 & \beta_I \\ 0 & 0 & -\alpha_I \\ -\beta_I & \alpha_I & 0 \end{bmatrix} \tag{2.51} -$$ - -여기서 $\alpha_I$ 와 $\beta_I$ 가 미소 증분 회전(infinitesimal incremental rotation)이면, 각각 $\mathbf{V}^1$ , $\mathbf{V}^2$ 에 대한 독립적인 미소 회전(independent infinitesimal rotation)을 의미하고, $\alpha_I$ 와 $\beta_I$ 가 유한 증분 회전(finite incremental rotation)이면, $\alpha_I$ 와 $\beta_I$ 는 서로 독립적이지 않으며 회전 텐서를 정의하는 변수가 된다. [9] - -### 2.2.2 Constitutive Matrix - -구성 행렬(constitutive matrix)의 경우 평면응력(plane stress) 가정을 사용하였으며, 식(2.52)와 같다. 이 때 $\kappa$ 는 전단 보정 계수(shear correction factor)로 5/6를 사용하였다. - -$$ -\widetilde{D}_{xyz} = \frac{E}{1 - v^2} \begin{bmatrix} -1 & v & 0 & 0 & 0 & 0 \\ -v & 1 & 0 & 0 & 0 & 0 \\ -0 & 0 & 0 & 0 & 0 & 0 \\ -0 & 0 & 0 & \kappa \frac{1 - v}{2} & 0 & 0 \\ -0 & 0 & 0 & 0 & \kappa \frac{1 - v}{2} & 0 \\ -0 & 0 & 0 & 0 & 0 & \frac{1 - v}{2} -\end{bmatrix} \tag{2.52} -$$ - -하지만 식(2.52)의 경우 지역 직교 좌표계(local Cartesian coordinate system)에서 정의되므로 이를 고유 좌표계(natural coordinate system)에서 정의하기 위해서는 변환 행렬(transformation matrix)을 사용해야 한다. - -지역 직교 좌표계와 고유 좌표계 사이의 관계는 Fig. 4와 같으며, 여기서 $\mathbf{G}_1$ , $\mathbf{G}_2$ , $\mathbf{G}_3$ 는 고유 좌표계의 공변 기저(covariant basis)이고, $\hat{\mathbf{e}}_1$ , $\hat{\mathbf{e}}_2$ , $\hat{\mathbf{e}}_3$ 는 지역 직교 좌표계의 기저를 의미한다. - -![Fig. 4 Local Cartesian coordinate system](images/chunk-002-fig-081.jpg) - -변형률과 응력에 대한 좌표변환은 식(2.53), (2.54)와 같으며, 이로부터 - -구성 행렬의 좌표변환은 식(2.55)와 같이 나타낼 수 있다. 여기서 $\widetilde{E}$ , $\widetilde{S}$ , $\widetilde{D}$ 와 E, S, D는 각각 지역 직교 좌표계와 고유 좌표계에서의 변형률, 응력, 구성 행렬을 의미한다. 그리고 $[T]_{\xi \to x}$ 는 고유 좌표계에서 정의된 값을 지역 직교 좌표계로 변환해주는 변환 행렬이다. - -$$ -\left\{\widetilde{E}\right\}_{xyz} = \left[T\right]_{\xi \to x} \left\{E\right\}_{\xi \eta \zeta} \tag{2.53} -$$ - -$$ -\begin{aligned} -\{S\}_{\xi\eta\zeta} &= [T]_{x\to\xi} \left\{ \widetilde{S} \right\}_{xyz} = [T]_{\xi\to x}^T [\widetilde{D}]_{xyz} \left\{ \widetilde{E} \right\}_{xyz} \\ -&= [T]_{\xi\to x}^T [\widetilde{D}]_{xyz} [T]_{\xi\to x} \left\{ E \right\}_{\xi\eta\zeta} = [D]_{\xi\eta\zeta} \left\{ E \right\}_{\xi\eta\zeta} -\end{aligned} (2.54) -$$ - -$$ -[D]_{\xi\eta\zeta} = [T]_{\xi\to x}^T [\widetilde{D}]_{xyz} [T]_{\xi\to x} \tag{2.55} -$$ - -이 때 $[T]_{\xi\to x}$ 는 식(2.56)과 같이 나타낼 수 있고, 각각의 성분은 식(2.57)과 같다. - -$$ -[T]_{\xi \to x} = \begin{bmatrix} a_1 a_1 & b_1 b_1 & c_1 c_1 & b_1 c_1 & a_1 c_1 & a_1 b_1 \\ a_2 a_2 & b_2 b_2 & c_2 c_2 & b_2 c_2 & a_2 c_2 & a_2 b_2 \\ a_3 a_3 & b_3 b_3 & c_3 c_3 & b_3 c_3 & a_3 c_3 & a_3 b_3 \\ 2a_2 a_3 & 2b_2 b_3 & 2c_2 c_3 & b_2 c_3 + c_2 b_3 & a_2 c_3 + c_2 a_3 & a_2 b_3 + b_2 a_3 \\ 2a_1 a_3 & 2b_1 b_3 & 2c_1 c_3 & b_1 c_3 + c_1 b_3 & a_1 c_3 + c_1 a_3 & a_1 b_3 + b_1 a_3 \\ 2a_1 a_2 & 2b_1 b_2 & 2c_1 c_2 & b_1 c_2 + c_1 b_2 & a_1 c_2 + c_1 a_2 & a_1 b_2 + b_1 a_2 \end{bmatrix} -(2.56) -$$ - -$$ -a_{1} = \mathbf{r}_{1} \cdot \mathbf{G}^{1} \qquad b_{1} = \mathbf{r}_{1} \cdot \mathbf{G}^{2} \qquad c_{1} = \mathbf{r}_{1} \cdot \mathbf{G}^{3} -a_{2} = \mathbf{r}_{2} \cdot \mathbf{G}^{1} \qquad b_{2} = \mathbf{r}_{2} \cdot \mathbf{G}^{2} \qquad c_{2} = \mathbf{r}_{2} \cdot \mathbf{G}^{3} -a_{3} = \mathbf{r}_{3} \cdot \mathbf{G}^{1} \qquad b_{3} = \mathbf{r}_{3} \cdot \mathbf{G}^{2} \qquad c_{3} = \mathbf{r}_{3} \cdot \mathbf{G}^{3} -(2.57) -$$ - -그리고 지역 직교 좌표계의 기저 $\hat{\mathbf{e}}_1$ , $\hat{\mathbf{e}}_2$ , $\hat{\mathbf{e}}_3$ 는 식(2.58)와 같이 고유 좌표계의 공변 기저(covariant basis) $\mathbf{G}_1$ , $\mathbf{G}_2$ , $\mathbf{G}_3$ 로부터 구할 수 있다. - -$$ -\hat{\mathbf{e}}_3 = \frac{\mathbf{G}_3}{\|\mathbf{G}_3\|}, \quad \hat{\mathbf{e}}_1 = \frac{\mathbf{G}_2 \times \hat{\mathbf{e}}_3}{\|\mathbf{G}_2 \times \hat{\mathbf{e}}_3\|}, \quad \hat{\mathbf{e}}_2 = \hat{\mathbf{e}}_3 \times \hat{\mathbf{e}}_1 \tag{2.58} -$$ - -#### 2.2.3 Mass Matrix - -질량 행렬(mass matrix)은 물체 내에 연속적으로 분포되어 있는 물체의 질량을 요소망 내 각 절점(node)에 집중 질량(lumped mass) 형식으로 이산화시켜 놓은 것으로, 이 질량 행렬 내 각 행렬요소를 합하면, 물체의 전체 질량과 같게 되며, 물체의 자중, 운동량, 관성력을 표현한다. - -진동 및 동적 좌굴 해석을 하기 위해서는 이러한 질량 행렬이 필요하며, 일반적으로 일관 질량 행렬(consistent mass matrix)과 집중 질량 행렬(lumped mass matrix)있다. 일관 질량 행렬은 식(2.59)와 같이 나타낼수 있다.[2] 여기서 $\rho$ 는 밀도, N은 형상 함수 행렬을 나타낸다. - -$$ -\left[\widetilde{\mathbf{M}}\right] = \int_{V} \rho[\mathbf{N}]^{T} [\mathbf{N}] dV \tag{2.59} -$$ - -집중 질량 행렬의 경우 일관 질량 행렬을 대각화(diagonalization) 함으로써 연산에 필요한 용량과 연산 시간을 줄일 수 있다는 장점이 있지만[2] 본 논문에서는 물체의 강성을 줄임으로써 유연한 결과를 얻기위해 집중 질량 행렬을 사용하였다. 일관 질량 행렬을 대각화하는 방법에는 다양한 방법들이 존재하며, 본 논문에서는 각각의 행을 합하는 방법(row-sum technique)을 사용하였다.[3] 식(2.60)으로부터 각각의 요소에 대한 일관 질량 행렬 M 의 행의 합을 구한다. 그리고 이를 식(2.61)과 같이 새로운 집중 질량 행렬 M 의 대각항(diagonal entries)에 대입하고, 비대각항(off-diagonal entries)은 영(零)을 대입한다. 이 때 n은 요소의 절점의 개수와 자유도의 곱을 나타낸다. - -$$ -S(i) = \sum_{j=1}^{n} \widetilde{\mathbf{M}}(i, j) \quad \text{for } i = 1, n \tag{2.60} -$$ - -$$ -\mathbf{M}(i,j) = S(i) \quad \text{for } i = 1, n -\mathbf{M}(i,j) = 0 \quad \text{for } i \neq j \tag{2.61} -$$ - -### 2.2.4 6-DOF Shell Element - -일반적으로 쉘 요소는 5개의 자유도(degree of freedom)를 사용하며, 법선 벡터(normal vector)는 각 절점당 하나의 법선 벡터를 갖는다. 하지만 쉘 요소를 보강재(stiffener)로 사용하는 경우, 쉘 요소와 쉘 요소의 결합을 위해서는 6개의 자유도가 필요하며, 이와 더불어 각 절점에서 정의되는 법선 벡터에 대한 구속 조건이 필요하다. 이에 본 논문에서는 각절점에서 정의되는 지역 좌표계(local coordinate system)를 전역 직교 좌표계(global Cartesian coordinate system)로 변환해 줌으로써 쉘 요소와 쉘 요소의 결합을 구성하였다. 이 때 지역 좌표계는 $\mathbf{V}^1$ , $\mathbf{V}^2$ , $\mathbf{V}^n$ 을 기저로하는 좌표계로 표현된다. - -전역 직교 좌표계에서 정의되는 회전 자유도 $\theta_1$ , $\theta_2$ , $\theta_3$ 와 지역 좌표계에 의해 정의되는 회전 자유도 $\alpha$ , $\beta$ , $\gamma$ 는 식(2.62)와 같은 관계식을 만족해야 한다. - -![Fig. 5 Global Cartesian coordinate system and local coordinate system](images/chunk-002-fig-105.jpg) - -$$ -\theta_1 \mathbf{e}_1 + \theta_2 \mathbf{e}_2 + \theta_3 \mathbf{e}_3 = \alpha \mathbf{V}^1 + \beta \mathbf{V}^2 + \gamma \mathbf{V}^n \tag{2.62} -$$ - -이 때 식(2.62)의 좌변과 우변에 각각 벡터 $\mathbf{V}^1$ 을 곱해주면, 식(2.63)과 같이 나타낼 수 있고, 마찬가지 방법으로 $\mathbf{V}^2$ , $\mathbf{V}^n$ 을 곱해주면, 각각식(2.64), (2.65)와 같이 나타낼 수 있다. - -$$ -\alpha = \theta_1 (\mathbf{e}_1 \cdot \mathbf{V}^1) + \theta_2 (\mathbf{e}_2 \cdot \mathbf{V}^1) + \theta_3 (\mathbf{e}_3 \cdot \mathbf{V}^1) \tag{2.63} -$$ - -$$ -\beta = \theta_1 (\mathbf{e}_1 \cdot \mathbf{V}^2) + \theta_2 (\mathbf{e}_2 \cdot \mathbf{V}^2) + \theta_3 (\mathbf{e}_3 \cdot \mathbf{V}^2) \tag{2.64} -$$ - -$$ -\gamma = \theta_1 (\mathbf{e}_1 \cdot \mathbf{V}^n) + \theta_2 (\mathbf{e}_2 \cdot \mathbf{V}^n) + \theta_3 (\mathbf{e}_3 \cdot \mathbf{V}^n) \tag{2.65} -$$ - -이를 행렬 형태로 표현해 주면 식(2.66)과 같이 쓸 수 있다. - -$$ -\begin{cases} -\alpha \\ -\beta \\ -\gamma -\end{cases} = \begin{bmatrix} -\mathbf{(e_1 \cdot V^1)} & \mathbf{(e_2 \cdot V^1)} & \mathbf{(e_3 \cdot V^1)} \\ -\mathbf{(e_1 \cdot V^2)} & \mathbf{(e_2 \cdot V^2)} & \mathbf{(e_3 \cdot V^2)} \\ -\mathbf{(e_1 \cdot V^n)} & \mathbf{(e_2 \cdot V^n)} & \mathbf{(e_3 \cdot V^n)} -\end{bmatrix} \begin{bmatrix} -\theta_1 \\ -\theta_2 \\ -\theta_3 -\end{cases} \tag{2.66} -$$ - -위 식으로부터 전역 직교 좌표계에서 지역 좌표계로 변환해 주는 행렬은 식(2.67)과 같이 정의 할 수 있으며, 이 때 L은 식(2.68)과 같다. [5] - -$$ -[\widetilde{T}] = \begin{bmatrix} \mathbf{1}_{3} & 0 & 0 & \cdots & 0 & 0 \\ & \mathbf{L} & 0 & & & 0 \\ & & \ddots & \ddots & & \vdots \\ & & & \ddots & 0 & 0 \\ & & & & \mathbf{1}_{3} & 0 \\ & & & & \mathbf{L} \end{bmatrix} \tag{2.67} -$$ - -$$ -\begin{bmatrix} \mathbf{L} \end{bmatrix} = \begin{bmatrix} (\mathbf{e}_1 \cdot \mathbf{V}^1) & (\mathbf{e}_2 \cdot \mathbf{V}^1) & (\mathbf{e}_3 \cdot \mathbf{V}^1) \\ (\mathbf{e}_1 \cdot \mathbf{V}^2) & (\mathbf{e}_2 \cdot \mathbf{V}^2) & (\mathbf{e}_3 \cdot \mathbf{V}^2) \\ (\mathbf{e}_1 \cdot \mathbf{V}^n) & (\mathbf{e}_2 \cdot \mathbf{V}^n) & (\mathbf{e}_3 \cdot \mathbf{V}^n) \end{bmatrix} \tag{2.68} -$$ - -만약 변위 벡터와 힘 벡터를 전역 좌표계에서 지역 좌표계로 변환해주면 각각 식(2.69), (2.70)과 같이 나타낼 수 있다. 이 때 위 첨자 - -e는 요소에 대해 정의 되었음을 의미한다.[4] - -$$ -\{\widetilde{\mathbf{u}}\}^e = \left[\widetilde{T}\right] \{\mathbf{u}\}^e \tag{2.69} -$$ - -$$ -\left\{ \widetilde{\mathbf{f}} \right\}^e = \left[ \widetilde{T} \right] \left\{ \mathbf{f} \right\}^e \tag{2.70} -$$ - -그리고 강성 행렬(stiffness matrix)은 지역 좌표계에서 식(2.71)과 같은 선형 관계식으로 나타낼 수 있다. [4] - -$$ -\left\{ \widetilde{\mathbf{f}} \right\}^e = \left[ \widetilde{\mathbf{K}} \right]^e \left\{ \widetilde{\mathbf{u}} \right\}^e \tag{2.71} -$$ - -여기서 식(2.69)와 (2.70)을 식(2.71)에 대입해주면, 식(2.72)와 같이 나타낼 수 있고, 따라서 강성 행렬을 지역 좌표계에서 전역 좌표계로 변환해주는 관계식은 식(2.73)과 같다.[4] - -$$ -\{\mathbf{f}\}^e = \left[\widetilde{T}\right]^T \left[\widetilde{\mathbf{K}}\right]^e \left[\widetilde{T}\right] \{\mathbf{u}\}^e \tag{2.72} -$$ - -$$ -[\mathbf{K}]^e = [\widetilde{T}]^T [\widetilde{\mathbf{K}}]^e [\widetilde{T}] \tag{2.73} -$$ - -하지만 6-자유도의 도입으로 강성 행렬 $\tilde{\mathbf{K}}^e$ 의 경우 여섯 번째 자유도에 해당하는 행과 열이 모두 영(零)이 되고, 이로 인해 특이점(singularity)이 발생하게 된다. 이에 본 논문에서는 이를 방지하기 위해서 여섯 번째 자유도의 대각항(diagonal entries)에 식(2.75)와 같이 대각항 최소값의 $10^{-3}$ 비율을 갖는 값을 대입하였다. - -$$ -\left[ \widetilde{\mathbf{K}} \right]^2 = \begin{bmatrix} & & & & & & & & & & & & & & & & & & -$$ - -(2.74) - -$$ -d = \min\left(\widetilde{\mathbf{K}}^{e}(i, i)\right) \times 10^{-3} \quad \text{for } i = 1, \ n \tag{2.75} -$$ - -그리고 6-자유도의 도입으로 회전 텐서를 구하는데 필요한 식(2.50)과 (2.51)은 식(2.76), (2.77)과 같은 형태가 된다. - -$$ -\widetilde{\theta}_{I} = \left[ (\alpha_{I})^{2} + (\beta_{I})^{2} + (\gamma_{I})^{2} \right]^{\frac{1}{2}} \tag{2.76} -$$ - -$$ -\left[\Theta_{I}\right] = \begin{bmatrix} 0 & -\gamma_{I} & \beta_{I} \\ \gamma_{I} & 0 & -\alpha_{I} \\ -\beta_{I} & \alpha_{I} & 0 \end{bmatrix} \tag{2.77} -$$ - -## 2.3 Buckling Theory - -가느다란 기둥을 축 방향으로 누르거나 얇은 판을 판과 평행한 방향으로 압축하면, 하중이 어느 크기에 도달하는 순간 갑자기 판이 횡 방향으로 과도하게 휘어지는 축 방향 변위(lateral displacement)가 발생한다. 물체의 이러한 거동을 좌굴 혹은 붕괴라고 정의하며 구조물의 안전성에 치명적인 문제점을 야기시킨다. - -좌굴이 발생하기 전까지 물체는 정적인 평형상태를 유지하지만, 일단 좌굴이 발생하면 평형상태가 깨어지고 횡 방향으로 큰 변형이 발생하여 외부 하중을 더 이상 지탱할 수 없게 된다. 이러한 좌굴은 비단 가느다란 기둥이나 얇은 판의 휨 좌굴(flexural buckling)에만 국한되는 것이 아니며, 물체의 국부 영역에 지역적으로 발생하는 국부 좌굴(local buckling), 전단력에 의하여 야기되는 전단 좌굴(shear buckling) 그리고 비틀림에 의해 발생하는 비틀림 좌굴(torsion buckling) 등이 있다. - -한편 좌굴에 의한 물체의 변형이 구조물이 이루는 평면 내에 있느냐 아니면 바깥에 있느냐에 따라 면내 좌굴(in-plane buckling) 그리고 면외 좌굴(out of plane buckling)로 구분하기도 한다. 좌굴은 거의 대부분 물체의 형상이나 하중 조건의 불완전성(imperfection)에 기인한다. 예를 들어, 기둥의 단면 중심에 정확히 축 방향으로 집중 압축력을 가한다고 했을 때, 이론적으로는 횡 방향으로 휨을 발생시킬 하중이나 모멘트 성분이 전혀 없기 때문에 좌굴이 발생해서는 안 된다. - -하지만 실제 기둥은 정확히 원형 단면이 아닐 뿐만 아니라 압축력이 작용하는 지점도 정확히 축의 중심에 위치하지 않는다. 따라서 기하학적인 불완전성과 축 중심에서 어느 정도 편심된 위치에 압축력이 작용함에 따른 불완전함에 따라 횡 방향으로의 변위가 발생하게 된다. - -좌굴은 물체의 가느다란 정도를 나타내는 형상 종횡비(aspect ratio)가 클수록 보다 쉽게 발생한다. 다시 말해 길이가 긴 기둥이 짧은 기둥에 비해 좌굴이 보다 쉽게 발생한다. 그리고 좌굴은 동일한 재질, 형상 및 하중조건에서도 물체를 구속하는 경계조건(boundary condition)에 크게 영향을 받는다. - -또한 좌굴은 작용하는 힘이 정적 하중이냐 동적 하중이냐에 따라 정적 diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/유한요소해석법을이용한쉘구조물의동적좌굴해석_003.md b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/유한요소해석법을이용한쉘구조물의동적좌굴해석_003.md deleted file mode 100644 index 4ef8876..0000000 --- a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/유한요소해석법을이용한쉘구조물의동적좌굴해석_003.md +++ /dev/null @@ -1,321 +0,0 @@ -좌굴(static buckling)과 동적 좌굴(dynamic buckling)로 나눌 수 있으며, 이절에서는 정적 좌굴과 동적 좌굴에 대해 알아보도록 하겠다. - -#### 2.3.1 Static Buckling - -이처럼 좌굴은 다양한 힘과 형상, 조건 등에서 발생하며 이러한 좌굴 문제를 해석하기 위한 지배방정식은 가상일에 기반을 둔 운동 에너지(total potential energy) 식(2.78)과 위치 에너지(kinetic energy)에 Hamilton's principle을 적용하여 구할 수 있다. - -$$ -\Pi = U + V = \frac{1}{2} \mathbf{q}^T \mathbf{K} \mathbf{q} + \frac{1}{2} \mathbf{q}^T \mathbf{K}_g \mathbf{q} \tag{2.78} -$$ - -$$ -T = \frac{1}{2} \dot{\mathbf{q}}^T \mathbf{M} \dot{\mathbf{q}} \tag{2.79} -$$ - -식(2.78)과 식(2.79)에 Euler-Lagrange 방정식 식(2.80)을 적용하면, 좌굴해석을 위한 지배 방정식 식(2.81)을 구할 수 있다. - -$$ -\frac{\partial L}{\partial \mathbf{q}} - \frac{d}{dt} \left( \frac{\partial L}{\partial \dot{\mathbf{q}}} \right) = 0 , \quad where \quad L = U + V - T \tag{2.80} -$$ - -$$ -\mathbf{M}\{\ddot{\mathbf{q}}\} + \left(\mathbf{K} + \mathbf{K}_{g}\right)\left\{\mathbf{q}\right\} = \left\{\mathbf{0}\right\} \tag{2.81} -$$ - -여기서 M 은 질량 행렬(mass matrix), K 는 강성 행렬(stiffness matrix), K $_g$ 는 기하 강성 행렬(geometric stiffness matrix)를 의미한다. 지배 방정식으로부터 정적 좌굴 해석은 식(2.82)의 형태로 나타난다. - -$$ -\left(\mathbf{K} + \lambda \mathbf{K}_{g}\right)\left(\mathbf{q}\right) = \left\{\mathbf{0}\right\} \tag{2.82} -$$ - -이 식은 고유치 문제(eigenvalue problem)와 유사한 형태를 가지고 있으므로 고유치 해석 솔버를 사용하여 고유치 $\lambda$ , 와 고유치 - -벡터(eigenvector)를 구할 수 있다. 이 때 고유치 해석을 위해서 Block Lanczos 방법을 사용한 "BLZPACK"이라는 오픈 소스(open source)를 활용하였다. 일반적인 고유치 해석의 경우 기하 강성 행렬 $\mathbf{K}_g$ 의 자리에 질량 행렬 $\mathbf{M}$ 을 사용하며, 질량 행렬의 특성 상 집중 행렬(lumped matrix) 또는 일관 행렬(consistent matrix)을 이용하여 고유치를 계산한다. 하지만 정적 좌굴 해석의 경우 기하 강성 행렬 $\mathbf{K}_g$ 는 집중 행렬 형태를 구현하기 어려우므로 일관 행렬 형태로 계산을 수행해야 한다. 따라서 정적 좌굴 해석의 경우 식(2.82)에 대한 고유치 해석을 통해 식(2.83)과 같이 임계 좌굴 압력(critical buckling pressure) 또는 임계 좌굴 하중(critical buckling load)를 계산하게 된다. [6] - -$$ -P_{cr_i} = \lambda_i P \quad or \quad F_{cr_i} = \lambda_i F \tag{2.83} -$$ - -### 2.3.2 Dynamic Buckling - -동적 좌굴 해석의 경우 정적 좌굴 해석과는 달리 식(2.84)와 같이 시간에 따라 변화하는 하중이 가해진다. 여기서 $P_0$ 는 정적 압축 하중(static compressive loading), $P_t$ 는 축 방향의 동적 하중(dynamic axial loading), $\theta$ 는 축 방향의 동적 하중에 대한 가진 주파수(excitation frequency), t는 시간을 의미한다. - -$$ -P(t) = P_0 + P_t \cos(\theta t) \tag{2.84} -$$ - -식(2.84)와 같이 시간에 따라 변화하는 하중이 가해질 때 운동 방정식(equation of motion)은 지배 방정식 식(2.81)로부터 식(2.85)와 같이 구할 수 있다.[6] - -$$ -\mathbf{M}\{\ddot{\mathbf{q}}\} + \left(\mathbf{K} + \mathbf{K}_{g}^{(s)} + \beta \cos(\theta t) \mathbf{K}_{g}^{(d)}\right) \left(\mathbf{q}\right) = \left\{\mathbf{0}\right\} \tag{2.85} -$$ - -여기에서 $\mathbf{K}$ 는 하중이 가해지지 않은 상태에서의 강성 행렬(stiffness matrix)이고, $\mathbf{K}_{g}^{(s)}$ 는 정적 압축 하중에 대한 기하 강성 행렬(geometric - -stiffness matrix)이며, $\mathbf{K}_g^{(d)}$ 는 축 방향의 동적 하중에 대한 기하 강성행렬이다. $\mathbf{M}$ 은 질량 행렬(mass matrix)이고, $\boldsymbol{\beta}$ 는 동적 하중의 척도인자(dynamic load scale factor)를 의미한다. 이 때 주요 동적 불안정 경계 영역(principal region of dynamic instability)을 찾기 위해서 식(2.86)과 같이주기가 $2T(=2\times 2\pi/\theta)$ 인 해를 가정하였다. 여기서 $\mathbf{a}$ 와 $\mathbf{b}$ 는 임의의벡터를 의미한다.[6] - -$$ -\mathbf{q}(t) = \mathbf{a}\sin\frac{\theta t}{2} + \mathbf{b}\cos\frac{\theta t}{2} \tag{2.86} -$$ - -식(2.86)을 식(2.85)에 대입하여 푸리에 급수 전개(Fourier series expansion)를 하면 식(2.87)과 같이 나타낼 수 있다. - -$$ -\mathbf{a}\sin\frac{\theta t}{2} \left[ -\frac{\theta^2}{4}\mathbf{M} + \mathbf{K} + \mathbf{K}_g^{(s)} + \frac{\beta}{2}\mathbf{K}_g^{(d)} \right] + \mathbf{b}\cos\frac{\theta t}{2} \left[ -\frac{\theta^2}{4}\mathbf{M} + \mathbf{K} + \mathbf{K}_g^{(s)} - \frac{\beta}{2}\mathbf{K}_g^{(d)} \right] + \underbrace{\qquad \qquad }_{\text{high order term}} = 0 \tag{2.87} -$$ - -식(2.87)에서 고차항을 무시하면 식(2.88)과 같이 쓸 수 있다. - -$$ -\mathbf{a} \sin \frac{\theta t}{2} \left[ -\frac{\theta^2}{4} \mathbf{M} + \mathbf{K} + \mathbf{K}_g^{(s)} + \frac{\beta}{2} \mathbf{K}_g^{(d)} \right] + \mathbf{b} \cos \frac{\theta t}{2} \left[ -\frac{\theta^2}{4} \mathbf{M} + \mathbf{K} + \mathbf{K}_g^{(s)} - \frac{\beta}{2} \mathbf{K}_g^{(d)} \right] \approx 0 \tag{2.88} -$$ - -모든 시간에 대해 식(2.88)를 만족하는 자명하지 않은 해(non-trivial solution)를 얻기 위해서는 식(2.89)와 같은 고유치 문제(eigenvalue problem) 형태를 갖는다.[6] - -$$ -\left| \mathbf{K} + \left( \mathbf{K}_{g}^{(s)} \pm \frac{\beta}{2} \mathbf{K}_{g}^{(d)} \right) - \frac{\theta^{2}}{4} \mathbf{M} \right| = 0 \tag{2.89} -$$ - -동적 좌굴 해석의 경우 정적 좌굴 해석 문제와는 달리 질량 행렬을 사용하며, 본 논문에서는 집중 질량 행렬(lumped mass matrix)을 사용하여 고유치를 구했으며, 이 때 고유치 $\lambda_i = \theta_i^2/4$ 이 된다. 그리고 이로부터 구조물이 불안정해지는 가진 주파수(excitation frequency) $\theta_i$ 를 구할 수 있으며, 동적 하중 변화에 따른 불안정 경계 영역(instability region)을 구할 수 있다. - -#### 2.3.3 Dynamic Buckling Theory of Beam - -![Fig. 6 Dynamic Buckling Model of Beam](images/chunk-003-fig-033.jpg) - -Fig. 6과 같이 보(beam) 구조물에 축 방향으로 정적 압축 하중(compressive static load)과 동적 압축 하중(compressive dynamic load)이 동시에 가해질 경우 보의 동적 좌굴 현상을 이론적으로 전개할 수 있다. - -우선 보의 정적 휨(static bending)에 관한 식은 식(2.90)과 같다. [7] - -$$ -EI\frac{d^2v}{dx^2} + Pv = 0 (2.90) -$$ - -식(2.90)을 두 번 미분하면 식(2.91)과 같이 쓸 수 있다. - -$$ -EI\frac{d^4v}{dx^4} + P\frac{d^2v}{dx^2} = 0 {(2.91)} -$$ - -이 때 E는 탄성 계수(Young's Modulus)이고, I는 면적 관성 모멘트(area moment of inertia)이며, P는 축 방향의 힘을 의미한다. 그리고 Fig. 6과 같이 정적 압축 하중과 동적 압축 하중이 동시에 가해지는 경우 힘 P는 식(2.92)와 같이 나타낼 수 있다. - -$$ -P(t) = P_0 + P_t \cos(\theta t) \tag{2.92} -$$ - -여기서 $P_0$ 는 보에 가해지는 정적 압축 하중이고, $P_t$ 는 축 방향의 동적 하중에 대한 진폭(amplitude)를 의미하며, $\theta$ 는 축 방향의 동적 하중에 대한 가진 주파수(excitation frequency)를 의미한다. 이 때 동적 하중의 주기 T는 $2\pi/\theta$ 이다. - -횡 방향 관성력만을 고려하여 운동 방정식을 구성하면, 식(2.91)은 식(2.93)과 같이 표현할 수 있다. - -$$ -EI\frac{\partial^4 v(x,t)}{\partial x^4} + \left(P_0 + P_t \cos(\theta t)\right) \frac{\partial^2 v(x,t)}{\partial x^2} + \rho \frac{\partial^2 v(x,t)}{\partial t^2} = 0 \tag{2.93} -$$ - -식(2.93)에서 v(x,t)는 시간에 따른 처짐을 의미하며, $\rho$ 는 단위 길이당 밀도를 의미한다. 위의 식(2.93)이 식(2.94)와 같은 형태의 해를 갖는다면 변수 분리(separation of variable)를 통해 식(2.93)은 식(2.95)와 같이 표현할수 있다. 이 때 식(2.94)는 경계 조건(boundary condition)을 만족한다. - -$$ -v(x,t) = \sum_{k=1}^{\infty} f_k(t) \sin \frac{k\pi x}{L} \tag{2.94} -$$ - -$$ -\sum_{k=1}^{\infty} \left[ EI \frac{k^4 \pi^4}{L^4} f_k(t) - \left( P_0 + P_t \cos(\theta t) \right) \frac{k^2 \pi^2}{L^2} f_k(t) + \rho \frac{d^2 f_k(t)}{dt^2} \right] \sin \frac{k \pi x}{L} = 0 \tag{2.95} -$$ - -식(2.95)를 만족하기 위해서는 모든 k에 대해 $\sin$ 함수의 계수가 영(零)이 되어야 하며, 따라서 모든 k에 대해 식(2.96)이 만족해야 한다. - -$$ -EI\frac{k^{4}\pi^{4}}{L^{4}}f_{k}(t) - (P_{0} + P_{t}\cos(\theta t))\frac{k^{2}\pi^{2}}{L^{2}}f_{k}(t) + \rho\frac{d^{2}f_{k}(t)}{dt^{2}} = 0 \qquad (k = 1, 2, 3, \cdots) \tag{2.96} -$$ - -그리고 이를 자유진동 상태에서의 고유 주파수(natural frequency) $\omega_k$ 와 오일러 보(Euler beam)의 임계 좌굴 하중(critical buckling load) $P_k^{cr}$ 을 각각식(2.98), (2.99)와 같이 정의하고 이를 사용하여 식(2.96)를 다시 쓰면식(2.97)과 같이 나타낼 수 있다. - -$$ -\frac{d^2 f_k(t)}{dt^2} + \omega_k^2 \left( 1 - \frac{P_0 + P_t \cos(\theta t)}{P_k^{cr}} \right) f_k(t) = 0 \quad (k = 1, 2, 3, \dots) \tag{2.97} -$$ - -$$ -\omega_k = \frac{k^2 \pi^2}{L^2} \sqrt{\frac{EI}{\rho}} \tag{2.98} -$$ - -$$ -P_k^{cr} = \frac{k^2 \pi^2}{L^2} EI \tag{2.99} -$$ - -또한 일정한 압축 하중 $P_0$ 가 가해지는 보의 고유 주파수 $\Omega_k$ 와 가진 매개변수(excitation parameter) $\mu_k$ 를 식(2.101), (2.102)와 같이 정의하고, 이를 식(2.97)에 적용하면 식(2.100)과 같이 쓸 수 있다. - -$$ -\frac{d^2 f_k(t)}{dt^2} + \Omega_k^2 (1 - 2\mu_k \cos(\theta t)) f_k(t) = 0 \qquad (k = 1, 2, 3, \dots) \tag{2.100} -$$ - -$$ -\Omega_k^2 \equiv \omega_k^2 \frac{\left(P_k^{cr} - P_0\right)}{P_k^{cr}} \tag{2.101} -$$ - -$$ -\mu_k = \frac{P_t}{2(P_k^{cr} - P_0)} \tag{2.102} -$$ - -그리고 여기서 k=1일 때의 기본모드(fundamental mode)만 고려하면 4(2.100)은 4(2.103)과 같이 나타낼 수 있고, 마찬가지로 4(2.101)과 4(2.102)도 4(2.104)와 4(2.105)로 나타낼 수 있다. - -$$ -\frac{d^2 f(t)}{dt^2} + \Omega^2 (1 - 2\mu \cos(\theta t)) f(t) = 0 \tag{2.103} -$$ - -$$ -\Omega^{2} = \left(\frac{\pi^{2}}{L^{2}} \sqrt{\frac{EI}{\rho}}\right)^{2} \left[1 - P_{0} \frac{L^{2}}{\pi^{2} EI}\right] \tag{2.104} -$$ - -$$ -\mu = \frac{P_t}{2[(\pi^2 EI)/L^2 - P_0]} \tag{2.105} -$$ - -이 때 식(2.103)은 식(2.106)과 같은 Mathieu-Hill 방정식의 형태를 갖게 된다.[8] - -$$ -f'' + \Omega^2 [1 - 2\mu \Phi(t)] f = 0 \tag{2.106} -$$ - -여기서 $\Phi(t)$ 는 가진 주기가 $T=2\pi/\theta$ 인 임의의 주기 함수이고, 다음과 같이 매개변수의 변화에 따라 해가 안정하거나 불안정해질 수 있다. - -- ⇒ 해의 주기가 T(또는 2T)에서 주기 2T(또는 T)로 바뀔 경우 : 안정 -- ⇒ 해의 주기가 T(또는 2T)에서 주기 T(또는 2T)로 바뀔 경우 : 불안정 - -즉 주기 2T, T인 해가 존재하므로 이를 푸리에 급수(Fourier series)로 가정하고, 이를 통해 매개변수에 따른 해의 안정성을 판별할 수 있다. - -불안정 영역(instability region)을 구하기 위해 주기가 $2T(=2\times 2\pi/\theta)$ 인해를 식(2.107)과 같이 정의하면 다음과 같다. - -$$ -f(t) = \sum_{k=1,3,5,\dots}^{\infty} \left( a_k \sin \frac{k\theta t}{2} + b_k \cos \frac{k\theta t}{2} \right) \tag{2.107} -$$ - -이 중에서 k=1인 경우만 고려하면, 식(2.108)과 같다. - -$$ -f(t) = a\sin\frac{\theta t}{2} + b\cos\frac{\theta t}{2} \tag{2.108} -$$ - -이를 식(2.103)에 대입하면 식(2.109)와 같으며, 이 때 주기 2T에 대해 푸리에 급수 전개를 하면 식(2.110)과 같이 나타낼 수 있다. - -$$ --\frac{\theta^{2}}{4}a\sin\frac{\theta t}{2} - \frac{\theta^{2}}{4}b\cos\frac{\theta t}{2} + \Omega^{2}(1 - 2\mu\cos(\theta t))a\sin\frac{\theta t}{2} + \Omega^{2}(1 - 2\mu\cos(\theta t))b\cos\frac{\theta t}{2} = 0 -a\sin\frac{\theta t}{2}\left(-\frac{\theta^{2}}{4} + \Omega^{2} + \mu\Omega^{2}\right) + b\cos\frac{\theta t}{2}\left(-\frac{\theta^{2}}{4} + \Omega^{2} - \mu\Omega^{2}\right) + \underbrace{\dots \dots \dots \dots \dots \dots \dots \dots \dots \dots \dots \dots \dots \dots \dots \dots \dots \dots \dots -$$ - -그리고 식(2.110)에서 주기가 2T인 항을 취하고 나머지 고차항을 무시하면, 식(2.111)과 같이 나타낼 수 있고, 이 때 모든 시간에 대해 식(2.111)을 만족하는 자명하지 않은 해(non-trivial solution)을 얻기 위해서는 식(2.112)를 만족해야 한다. - -$$ -a\sin\frac{\theta t}{2}\left(-\frac{\theta^2}{4} + \Omega^2 + \mu\Omega^2\right) + b\cos\frac{\theta t}{2}\left(-\frac{\theta^2}{4} + \Omega^2 - \mu\Omega^2\right) \approx 0 \tag{2.111} -$$ - -$$ -\Omega^2 \left[ 1 \pm \mu - \frac{1}{4} \left( \frac{\theta}{\Omega} \right)^2 \right] = 0 \tag{2.112} -$$ - -이는 불안정 영역을 결정짓는 매개변수와 가진 진동수의 근사화된 식으로 식(2.113)과 같이 무차원 가진 진동수 $\theta/(2\Omega)$ 를 매개변수 $\mu$ 에 대한 양함수의 형태로 표현할 수 있으며, 이를 도시하여 가진 매개 변수 $\mu$ 에 따른 불안정 영역의 변화를 살펴 볼 수 있다. - -$$ -\frac{\theta}{2\Omega} = \sqrt{1 \pm \mu} \tag{2.113} -$$ - -# 3. Numerical Example - -본 연구에서 개발한 동적 좌굴 유한요소해석 프로그램을 이용하여 동적 좌굴 해석을 하기 위한 기본적인 해석을 수행하였다. 기본적으로 선형 정적 해석, 비선형 정적 해석 그리고 정적 좌굴 해석을 수행하였으며, 최종적으로 본 연구의 목적인 동적 좌굴 해석을 수행하여 이론값 또는 실험값과의 비교를 통해 동적 좌굴 유한요소해석 프로그램의 타당성과 신뢰성을 검증하였다. - -## 3.1 Linear Static Analysis - -![Figure](images/chunk-003-fig-085.jpg) - -![Fig. 7 Patch Test Mesh](images/chunk-003-fig-086.jpg) - -패치 테스트 모델(patch test model)은 Fig. 1과 같다. 이 모델에 대해서 constant curvature, constant shear, constant twist 패치 테스트를 수행하였으며, 재료의 탄성계수(Young"s modulus)는 2.1E+06이고, 푸아송의 비(Poisson"s ratio)는 0.3이다. 그리고 모델의 두께는 1.0과 0.001 두 모델에 대해 패치 테스트를 수행하였다. - -#### 3.1.1.1 Constant Curvature Patch Test - -![Fig. 8 Constant Curvature Patch Test Model](images/chunk-003-fig-090.jpg) - -Constant curvature 패치 테스트 모델에 대한 경계조건은 우선 1번 절점에 대해 1,3 방향의 변위와 2 방향의 회전을 구속하였고, 5번 절점에 대해 1,2,3 방향의 변위와 2 방향의 회전을 구속하였다. 또한 외력은 3번, 7번 절점에 2 방향의 모멘트를 가하였으며, 이 때 힘의 크기는 두께 1.0일 때 M=1000, 두께 0.001일 때 M=0.0001을 가하였다. 이러한 경계조건과 외력은 Fig. 8로부터 확인할 수 있으며, constant curvature 패치테스트 해석 결과 각 절점의 곡률 $(\rho)$ 는 식(3.1)과 같이 구할 수 있다. - -$$ -\frac{1}{\rho} = \frac{d\theta}{dx} = \frac{d^2w}{dx^2} \tag{3.1} -$$ - -[Table HTML](tables/chunk-003-table-001.html) - -Table. 1은 constant curvature 패치 테스트 결과로 두 가지 두께에 대해 구속조건이 적용된 절점을 제외한 나머지 절점에서 모두 일정한 곡률이 나오는 것을 볼 수 있다. - -# 3.1.1.2 Constant Shear Patch Test - -![Fig. 9 Constant Shear Patch Test Model](images/chunk-003-fig-097.jpg) - -Constant shear 패치 테스트 모델에 대한 경계조건은 우선 1번과 5번 절점에 대해 3 방향의 변위를 구속하였고, 모든 절점에 대해 1,2 방향의 변위와 2 방향의 회전을 구속하였다. 또한 외력은 3번, 7번 절점에 3 방향의 집중 하중을 가하였으며, 이 때 힘의 크기는 두께 1.0일 때 Q=2000, 두께 0.001일 때 Q=2.0을 가하였다. 이러한 경계 조건과 외력은 Fig. 9로부터 확인할 수 있으며, constant shear 패치 테스트 해석 결과는 Table. 2와 같다. - -[Table HTML](tables/chunk-003-table-002.html) - -Constant shear 패치 테스트 결과로 두 가지 두께에 대해 구속조건이 적용된 절점을 제외한 나머지 절점에서 모두 일정한 기울기가 나타나는 것을 확인할 수 있으며, 실제로 3 방향 변위는 식(3.2)를 사용하여 계산할 수 있다. - -$$ -\tau = \frac{V}{A} = G\gamma, \quad \gamma = \frac{w}{L}, \quad G = \frac{E}{2(1+v)} \quad \Rightarrow \quad w = \frac{2(1+v)VL}{EA} \tag{3.2} -$$ - -#### 3.1.1.3 Constant Twist Patch Test - -![Fig. 10 Constant Twist Patch Test Model](images/chunk-003-fig-105.jpg) - -Constant twist 패치 테스트 모델에 대한 경계조건은 우선 1, 3, 5번 절점에 대해 3 방향의 변위를 구속하였고, 모든 절점에 대해 1,2 방향의 변위를 구속하였다. 또한 외력은 7번 절점에 3 방향의 집중 하중을 가하였으며, 이 때 힘의 크기는 두께 1.0일 때 P=1000, 두께 0.001일 때 P=1.0E-06을 가하였다. 이러한 경계 조건과 외력은 Fig. 10으로부터 확인할 수 있으며, constant twist 패치 테스트 해석 결과는 Table. 3과 같다. - -[Table HTML](tables/chunk-003-table-003.html) - -Constant twisting 테스트 결과를 보면 두께가 0.001일 때 x와 y방향에 대해 일정한 곡률이 나오는 것을 볼 수 있지만 두께가 1.0일 때는 그렇지 않음을 알 수 있다. 이 문제는 두께가 두꺼울 때 횡 전단 변형(transverse shear deformation)이 지배적으로 나타나기 때문에 발생하는 것으로 전단 보정 계수(shear correction factor)를 사용하여 전단 변형(shear deformation)을 억제하거나 사각형 요소를 사용함으로써 이 문제를 해결할 수 있다.[1] - -### 3.1.2 Pinched Cylinder - -![Figure](images/chunk-003-fig-111.jpg) - -Fig. 11 Pinched Cylinder Model Fig. 12 Pinched Cylinder 1/8 Model - -Fig. 11과 같이 양 끝에 막이 있는 pinched cylinder 쉘은 이론적인 해를 비교할 수 있기 때문에 쉘 검증 문제에 적합하다. 이 때 하중은 중간면에서 서로 반대 방향으로 집중 하중 P가 가해지고 있으며, 형상 및 하중이 대칭이기 때문에 Fig. 12와 같이 1/8 모델을 사용하였다. - -Pinched cylinder 쉘의 형상은 길이(L) 600, 반경(R) 300, 두께(t) 3이고, 재료 특성은 탄성 계수(Young"s modulus) 3.0E+06, 푸아송 비(Poisson"s ratio) 0.3이며, 외력 P의 크기는 1이다. 격자(mesh)는 20x20, 30x30, 40x40 격자를 사용하였으며, 경계 조건은 절단면은 각각 대칭 경계 조건을 적용하였으며, pinched cylinder 쉘의 end diaphragm 부분은 X와 Y 방향의 변위를 구속하였다. - -해석 결과 Table. 4 로부터 ABAQUS와 현재 쉘 모두 이론적인 해와 유사하게 나옴을 확인할 수 있으며, ABAQUS결과가 보다 이론적인 해에 - -가까움을 확인할 수 있다. 이는 쉘 요소가 다르기 때문에 발생하는 차이로 ABAQUS 쉘 요소가 현재 쉘 요소에 비해 보다 유연함을 알 수 있다. 그리고 Fig. 13으로부터 요소의 수가 증가함에 따라 ABAQUS와 현재 쉘의 결과가 모두 이론적인 해에 수렴함을 알 수 있다. 또한 Fig. 14는 Y방향의 변위에 대해 해석 결과를 도시한 그림으로 변형 형상이 유사하게 나옴을 확인할 수 있다. - -[Table HTML](tables/chunk-003-table-004.html) - -![Fig. 13 Comparison of Convergence for Pinched Cylinder with ABAQUS](images/chunk-003-fig-119.jpg) - -![Fig. 14 Comparison of Linear Static Analysis for Pinched Cylinder with ABAQUS](images/chunk-003-fig-121.jpg) - -![Fig. 15 Hemispherical Shell Model](images/chunk-003-fig-122.jpg) - -Fig. 15와 같은 반구형(hemispherical) 쉘의 1/4 모델을 사용하여 이론적인 해와 ABAQUS 결과값에 대해 비교해 보았다. 먼저 반경은 10m, 두께는 0.04m이고, 요소는 한 면당 9개, 17개 격자(mesh)를 사용하였다. 탄성계수는 68.25MPa이고, 푸아송 비는 0.3의 물성치를 주었다. 경계조건은 좌우 면에 대해 대칭 경계조건을 적용하였고, 하중은 Fig. 15와 같은 지점에 (+)Z, (-)X방향으로 각각 1씩 가하였다. 해석 결과 Table. - -5로부터 ABAQUS는 이론값보다 큰 값이 나오고, 현재 코드 결과는 이론값보다 작은 값이 나옴을 알 수 있는데 이는 사용한 쉘 요소가 다르므로 요소의 특성에 따른 차이로 볼 수 있다. 하지만 Fig. 16로부터 두 결과값 모두 요소 수가 증가함에 따라 이론값에 수렴함을 확인할 수 있으며, Fig. 17은 Y방향 변위에 대해 ABAQUS와 현재 코드의 해석 결과를 직접 도시한 그림으로 서로 유사한 결과가 나옴을 알 수 있다. - -[Table HTML](tables/chunk-003-table-005.html) - -![Fig. 16 Comparison of Convergence for Hemispherical Shell with ABAQUS](images/chunk-003-fig-127.jpg) - -![Fig. 17 Comparison of Linear Static Analysis for Hemisphrical Shell with ABAQUS](images/chunk-003-fig-129.jpg) - -# 3.2 Geometric Nonlinear Analysis - -![Fig. 18 Beam Model for Geometric Nonlinear Analysis](images/chunk-003-fig-131.jpg) - -Fig. 18과 같은 보(beam) 형상에 대해 기하비선형 정적 해석(geometric nonlinear static analysis)을 수행하여 ABAQUS와 그 결과를 비교해 보았다. 먼저 가로 12m, 세로 1m, 두께 0.01m 이고, 요소는 총 12개를 사용하였다. 탄성계수는 1.0MPa이고, 푸아송 비는 0.0의 물성치를 주었다. 경계조건은 한 면은 XYZ방향의 변위와 회전을 모두 구속하였고, 다른 한 면은 Y방향으로 모멘트를 가하였다. 그리고 물체가 Y방향에 대해 회전할 때 X방향의 회전으로 인한 형상의 뒤틀림을 방지하기 위해 모든 절점에 - -대해 X방향 회전을 구속하였다. 해석 결과 Fig. 19로부터 ABAQUS 해석 결과와 유사한 결과가 나옴을 확인 할 수 있으며, 이 때 힘의 증가에 따른 형상 변화는 Fig. 20과 같다. - -![Fig. 19 Comparison of Geometric Nonlinear Analysis for Beam with ABAQUS](images/chunk-003-fig-135.jpg) - -![Fig. 20 Geometry Change of Beam According to Loads Increase](images/chunk-003-fig-136.jpg) - -## 3.3 Static Buckling Analysis - -### 3.3.1 Rectangular Plate Shell - -정적 좌굴 해석 프로그램의 검증을 위해 먼저 Fig. 21과 같은 직사각형 평판 쉘(rectangular plate shell) 형상에 대해 정적 좌굴 해석을 수행하였다. 크기는 가로 20m, 세로 8m이고, 두께는 0.01m로 4노드 쉘 40x16 격자로 모델링 하였으며, 재료의 물성치는 탄성계수 29MPa, 푸아송 비는 0.3을 적용하였다. 경계 조건은 한 면은 XYZ방향의 변위를 구속하였고, 반대쪽 면은 YZ방향의 변위를 구속하였다. 그리고 나머지 두 면은 Z방향의 변위를 구속하였다. 마지막으로 하중은 YZ방향의 변위를 구속한 면에 X방향으로 총 8N의 압축력을 가하였다. - -![Fig. 21 Rectangular Plate Shell Model](images/chunk-003-fig-141.jpg) - -해석 결과 나오는 고유치(eigenvalue) i 는 Table. 6과 같고, ABAQUS와 유사한 결과가 나옴을 확인할 수 있다. 또한 고유치 벡터(eigenvector)로 부터 확인할 수 있는 좌굴 형상 역시 ABAQUS와 유사함을 확인할 수 있다.(Table. 7) diff --git a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/유한요소해석법을이용한쉘구조물의동적좌굴해석_004.md b/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/유한요소해석법을이용한쉘구조물의동적좌굴해석_004.md deleted file mode 100644 index 08051e3..0000000 --- a/docs/Paper/유한요소해석법을이용한쉘구조물의동적좌굴해석/유한요소해석법을이용한쉘구조물의동적좌굴해석_004.md +++ /dev/null @@ -1,178 +0,0 @@ -| MODE | Present | ABAQUS | -| --- | --- | --- | -| 1 | 1.67189 | 1.6736 | -| 2 | 1.69982 | 1.7033 | -| 3 | 2.01893 | 2.0198 | -| 4 | 2.57052 | 2.5706 | -| 5 | 3.30094 | 3.3003 | -| 6 | 3.46471 | 3.4751 | -| 7 | 4.2056 | 4.2041 | -| 8 | 5.29042 | 5.2879 | -| 9 | 6.47877 | 6.5154 | -| 10 | 6.56839 | 6.5648 | - -![Table. 7 Comparison of Mode Shape for Rectangular Plate Shell with ABAQUS](images/chunk-004-fig-002.jpg) - -## 3.3.2 Cylindrical Shell - -정적 좌굴 해석 프로그램의 검증을 위해 Fig. 22과 같은 원통형 쉘(cylindrical shell) 형상에 대해 정적 좌굴 해석을 수행하였다. 크기는 직경 0.3m, 길이 2m이고, 두께는 0.005m로 4노드 쉘 40x80 격자로 모델링 하였으며, 재료의 물성치는 탄성계수 71GPa, 푸아송 비는 0.3을 적용하였다. 경계 조건은 한 면은 XYZ방향의 변위를 구속하였고, 반대쪽 면은 XY방향의 변위를 구속하였으며, 하중은 YZ방향의 변위를 구속한 면에 X방향으로 총 40000N의 압축력을 가하였다. - -![Fig. 22 Cylindrical Shell Model](images/chunk-004-fig-006.jpg) - -해석 결과 고유치는 Table. 8과 같으며, ABAQUS 해석 결과와 유사하게 나옴을 확인할 수 있다. 또한 고유치 벡터(eigenvector)로부터 확인할 수 있는 좌굴 형상 역시 ABAQUS와 유사함을 확인할 수 있다.(Table. 10) - -이 때 정적 좌굴 해석으로부터 나온 고유치를 식(2.83)에 대입하면, Table. 9와 같은 임계 좌굴 압력(critical buckling pressure)을 구할 수 있으며, 이론값[6]과 비교했을 때 유사한 결과가 나옴을 알 수 있고 더불어 ABAQUS 해석 결과보다 오차가 적음을 알 수 있다. - -| MODE | Present | ABAQUS | -| --- | --- | --- | -| 1 | 1204.05 | 1242.9 | -| 3 | 1418.57 | 1442.7 | -| 5 | 1502.83 | 1534.4 | -| 7 | 1564.76 | 1626.3 | -| 9 | 1616.48 | 1641.0 | - -| MODE | Pcr*109 [N/m2 ] (Present) | Pcr*109 [N/m2 ] (Analytical) | Pcr*109 [N/m2 ] (ABAQUS) | -| --- | --- | --- | --- | -| 1 | 1.0220 | 0.9926 | 1.0550 | -| 3 | 1.2041 | 1.1641 | 1.2246 | -| 5 | 1.2756 | 1.1722 | 1.3024 | -| 7 | 1.3282 | 1.2602 | 1.3804 | -| 9 | 1.3721 | 1.2993 | 1.3929 | - -| MODE | Present | ABAQUS | -| --- | --- | --- | -| 1 | | | -| 3 | | | -| 5 | | | - -## 3.3.3 Stiffened Square Plate Shell - -정적 좌굴 해석 프로그램의 검증을 위해 Fig. 23과 같은 보강된 정사각형 평판 쉘(stiffened square plate shell) 형상에 대해 정적 좌굴 해석을 수행하였다. 평판은 가로 10m, 세로 10m, 두께 0.01m이고, 보강재(stiffener)는 가로 10m, 세로 1m, 두께 0.01m이며, 4노드 쉘 4000개 요소로 모델링 하였다. 재료의 물성치는 탄성계수 71GPa, 푸아송 비 0.3을 적용하였다. 경계 조건은 한 면은 XYZ방향의 변위를 구속하였고, 반대쪽 면은 XZ방향의 변위를 구속하였으며, 하중은 XZ방향의 변위를 구속한 면에 Y방향으로 총 80N의 압축력을 가하였다. - -![Fig. 23 Stiffened Square Plate Shell Model](images/chunk-004-fig-017.jpg) - -해석 결과 고유치는 Table. 11과 같으며, ABAQUS 해석 결과와 유사하게 나옴을 확인할 수 있다. 또한 고유치 벡터(eigenvector)로부터 확인할 수 있는 좌굴 형상 역시 ABAQUS와 유사함을 확인할 수 있다. (Table. 12) - -| MODE | Present | ABAQUS | -| --- | --- | --- | -| 1 | 9640.01 | 9694.7 | -| 2 | 9908.3 | 9965.8 | -| 3 | 10106.5 | 10167. | -| 4 | 10424 | 10483. | -| 5 | 10476 | 10531. | -| 6 | 10894.6 | 10970. | -| 7 | 11054.2 | 11118. | -| 8 | 11250 | 11307. | -| 9 | 11357.8 | 11400. | -| 10 | 11615.1 | 11657. | - -![Table. 12 Comparison of Mode Shape for Stiffened Square Plate Shell with ABAQUS](images/chunk-004-fig-021.jpg) - -## 3.4 Dynamic Buckling Analysis - -### 3.4.1 Dynamic Buckling Analysis of Beam - -![Fig. 24 Dynamic Buckling Analysis Model for Beam](images/chunk-004-fig-025.jpg) - -동적 좌굴 해석 프로그램의 검증을 위해 먼저 Fig. 24와 같은 보(beam) 형상에 대한 동적 좌굴 이론값과 유한요소해석 결과를 비교해 보았다. 보의 크기는 먼저 가로 800mm, 세로 30mm, 두께 3mm 이고, 요소는 4노드 쉘 요소 160x6격자를 사용하였다. 탄성계수(Young's modulus)는 68.9GPa이고, 프아송 비(Poisson's ration)는 0.33, 밀도는 2700kg/m³의 물성치를 주었다. 경계조건은 왼쪽 면은 XYZ방향의 변위를 구속하였고, 오른쪽 면은 YZ방향의 변위를 구속하였다. 하중은 식 (3.3)와 같은 시간에 따른 하중을 가하였으며, 이 때 동적 좌굴 해석을 위한 방정식은 식 (3.4)와 같다. 이 때 $P_0$ 는 정적 하중, $P_i$ 는 동적 하중의 크기, $\theta$ 는 가진 주파수(excitation frequency), $\beta$ 는 동적 매개변수(Dynamic parameter), K는 강성 행렬(stiffness matrix), $\mathbf{K}_g^{(s)}$ 는 동적 하중에 대한 기하 강성 행렬(geometric stiffness matrix), $\mathbf{K}_g^{(d)}$ 는 동적 하중에 대한 기하 강성 행렬, M은 질량 행렬(Mass matrix)을 의미하며, 고유치 해석(eigenvalue analysis)을 통해 가진 주파수를 구할 수 있다. - -$$ -P(t) = P_0 + P_t \beta \cos(\theta t) \tag{3.3} -$$ - -$$ -\left| \mathbf{K} + \mathbf{K}_{g}^{(s)} \pm \frac{\beta}{2} \mathbf{K}_{g}^{(d)} - \frac{\theta^{2}}{4} \mathbf{M} \right| = 0 \tag{3.4} -$$ - -Fig. 24와 같은 보 모델에 대해 유한요소해석을 이용한 고유치 해석 결과 고유진동수(natural frequency)는 10.7Hz이고, 임계좌굴하중(critical buckling loads) 71.73N으로 계산되었다. 그리고 동적 좌굴 해석을 수행하여 불안정 경계 영역(instability region)을 구하고, 이를 이론값과 비교한 결과 Fig. 25과 같이 나타낼 수 있으며, 유사한 결과가 나옴을 확인할 수 있다. 이 때 P 0 =0, 1 인 경우 이다. 그리고 Fig. 25에서 하중이 0일 때 구조물이 불안정 해지는 가진 주파수는 고유진동수의 두 배임을 확인 할 수 있으며, 동적 하중의 크기가 증가할수록 불안정 경계 영역이 넓어짐을 확인할 수 있다. - -![Fig. 25 Dynamic Instability Region of Beam](images/chunk-004-fig-031.jpg) - -## 3.4.2 Dynamic Buckling Analysis of Plate - -![Fig. 26 Dynamic Buckling Model for Plate](images/chunk-004-fig-034.jpg) - -동적 좌굴 해석 프로그램의 검증을 위해 Fig. 26과 같은 판(plate) 형상에 대한 동적 좌굴 실험값과 유한요소해석 결과를 비교해 보았다. 판의 크기는 먼저 가로 1000mm, 세로 250mm, 두께 1mm 이고, 요소는 4노드 쉘 요소 100x25격자(mesh)를 사용하였다. 탄성계수(Young"s modulus)는 63.3GPa이고, 프아송 비(Poisson"s ration)는 0.33, 밀도는 2678.4kg/m 3 의 물성치를 주었다. 경계조건은 실험 모델을 바탕으로 한쪽 면은 양 끝 단의 50x50mm 2 영역을 고정 구속하였고, 반대쪽 면은 한쪽 끝 단의 50x50mm 2 영역을 XZ방향의 변위에 대해 구속하였다. 하중은 식 (3.3)와 같은 시간에 따른 하중을 가하였으며, 이 때 동적 좌굴 해석을 위한 방정식은 식 (3.4)와 같다. - -Fig. 26과 같은 판(plate) 형상에 대해 유한요소해석을 이용한 고유치 해석 결과 고유진동수(natural frequency)는 5.17Hz이고, 임계 좌굴 하중(critical buckling loads)는 49.6N으로 계산되었다. 그리고 동적 하중에 대한 동적 좌굴 해석을 수행하여 불안정 경계 영역(instability region)을 구한 결과 Fig. 27과 같은 결과를 얻을 수 있으며, 실험값과 유한요소해석 결과가 유사한 경향성을 보이는 것을 확인 할 수 있다. 이 때 실선이 유한요소해석 결과이고 사각형 모양의 점들이 실험결과를 나타낸다.[11] - -![Fig. 27 Dynamic Instability Region of Plate](images/chunk-004-fig-038.jpg) - -1954 - -## 3.4.3 Dynamic Buckling Analysis of Stiffened Plate - -![Fig. 28 Dynamic Buckling Analysis Model for Stiffened Plate](images/chunk-004-fig-042.jpg) - -동적 좌굴 해석 프로그램의 검증을 위해 Fig. 28과 같은 보강된 판(stiffened plate) 형상에 대한 동적 좌굴 실험값과 유한요소해석 결과를 비교해 보았다. 판의 크기는 먼저 가로 1000mm, 세로 250mm, 두께 1mm 이고, 요소는 4노드 쉘 요소 100x25개를 사용하였다. 보강재는 가로 1000mm, 세로 10mm, 두께 1mm이고, 요소는 4노드 쉘 요소 100x1개를 사용하였다. 탄성계수(Young"s modulus)는 62.4GPa이고, 프아송 비(Poisson"s ration)는 0.33, 밀도는 2696.7kg/m 3 의 물성치를 주었다. 경계조건은 실험 모델을 바탕으로 한쪽 면은 양 끝 단의 50x50mm 2 영역을 고정 구속하였고, 반대쪽 면은 한쪽 끝 단의 50x50mm 2 영역을 XY방향의 변위에 대해 구속하였다. 하중은 식 (3.3)와 같은 시간에 따른 하중을 가하였으며, 이 때 동적 좌굴 해석을 위한 방정식은 식 (3.4)와 같다. - -동적 하중에 대한 동적 좌굴 해석을 수행하여 불안정 경계 영역(instability region)을 구한 결과 실험값과 유한요소해석 결과가 평판에 비해 다소 차이가 남을 볼 수 있다.(Fig. 29) 그 이유는 유한요소해석의 경우 모델이 "균질하다(homogeneous)"는 가정하에 해석이 이루어지지만 - -실제 구조물의 경우 다양한 결함(imperfection)이 존재하므로 이러한 차이가 발생할 수 있다. 특히 보강된 판의 경우 일반 평판에 비해 보강재를 만드는 과정에서 구조물에 결함(imperfection)이 발생할 가능성이 높기 때문에 이러한 결과가 나타날 수 있다. 하지만 두 결과값에 대한 전체적인 경향성은 유사함을 알 수 있다. 또한 앞서 해석한 평판과보강된 평판을 비교해 보면 보강된 평판의 임계 좌굴 하중이 증가하고,불안정 경계 영역이 전체적으로 상승함을 알 수 있다.[11] - -![Fig. 29 Comparison of Dynamic Instability Region for Stiffened Plate with Plate](images/chunk-004-fig-047.jpg) - -# 4. 결 론 - -동적 좌굴 현상은 구조물이 동적 압축 하중을 받을 때 발생하는 동적 불안정 현상으로서, 특히 큰 동적 압축 하중 환경하에 노출되어 있는 초음속 항공기나 탄도 미사일, 발사용 로켓과 지구 대기권 재돌입체 그리고 고속의 수중운동체 등을 설계할 때 반드시 고려되어야 하는 현상이다. 이에 본 연구에서는 동적 압축 하중을 받는 구조물의 동적 좌굴 해석을 위한 프로그램을 개발하였다. - -본 연구를 위해 MITC4 쉘 요소를 사용하여 3차원 쉘 구조물을 모델링 하였으며, 고유치 해석 시 필요한 기하 강성 행렬(Geometric stiffness matrix)을 구하기 위해 Total Lagrangian 방법을 사용하여 기하 비선형 해석을 구현하였다. 그리고 질량 행렬은 구조물을 유연하게 하기 위해서 집중 질량 행렬(Lumped mass matrix)을 사용하였다. 또한 진동 및 정적/동적 좌굴 해석 시 필요한 고유치 해석 솔버(Eigenvalue analysis solver)는 Block Laczos 방법을 사용한 "BLZPACK"이라는 오픈 소스(Open source)를 사용하여 해석 프로그램을 구현하였다. - -본 연구에서 개발한 해석 프로그램을 사용하여 선형/비선형 정적 해석과 진동 해석 그리고 정적 좌굴 해석을 수행하였고, 이를 이론값 및 상용유한요소해석 프로그램 해석 결과와 비교하여 프로그램의 타당성과 정확성을 검증하였다. 또한 보의 동적 좌굴 이론으로부터 구한 이론값과 평판 그리고 보강된 평판의 동적 좌굴 실험을 통해 구한 실험값을 유한요소해석 프로그램 결과와 비교하여 본 연구에서 개발한 동적 좌굴 해석 프로그램의 타당성을 검증하였다. - -본 연구에서 개발한 동적 좌굴 해석을 위한 유한요소해석 프로그램을 사용하여 구조물 설계 시 동적 좌굴 해석이 필요한 구조물에 대해 해석을 수행 할 수 있으며, 이를 통해 불안정 경계 영역을 예측하고 구조물의 안정화 방안을 모색할 수 있을 것으로 기대된다. - -## 참고문헌 - -- [1] Eduardo N. Dvorkin and Klaus-Jurgen Bathe, "A continuum mechanics based four-node shell element for general nonlinear analysis", Eng. Comput. , Vol. 1, No. 1, pp.77-88, 1984. -- [2] Robert D. Cook, David S. Malkus, Michael E. Plesha, Robert J. Witt, Concepts and application of finite element analysis. , John Wiley & Sons, 2001. -- [3] Thomas J.R. Hughes, The Finite Element Method : Linear Static and Dynamic Finite Element Analysis. , Prentice-Hall, 1987. -- [4] O.C. Zienkiewicz, R. L. Taylor, The Finite Element Method : Basic Formulation and Linear Problems. , McGraw-Hill, 1989. -- [5] O.C. Zienkiewicz, R. L. Taylor, The Finite Element Method : Solid and Fluid Mechanics Dynamics and Non-linearity. , McGraw-Hill, 1991. -- [6] M. Ruzzene, "Dynamic buckling of periodically stiffened shells : application to supercavitating vehicles", International Journal of Solids and Structures , Vol. 41, No.3-4, pp.1039-1059, 2004. -- [7] V. V. Bolotin, The Dynamic Stability of Elastic System. , Holden-Day, 1964. -- [8] Leonard Meirovitch, Method of Analytical Dynamics. , McGraw-Hill, 1985. -- [9] Eduardo N. Dvorkin, "Nonlinear Analysis of Shells Using the MITC Formulation", Archives of Computational Methods in Engineering , Vol. 2, No. 2, pp.1-50, 1995. -- [10] J. Argyris, "An excursion into large rotations", Comput. Methods Appl. Mech. Engrg., Vol. 32, No. 1-3, pp.85-155, 1982. -- [11] Minho Chung, Hee Jun Lee, Woo-Bin Lim, Jin Yeon Cho, Wanil Byun, Seung Jo Kim and Sung-Han Park, "Experimental study on dynamic buckling phenomena for supercavitating underwater vehicle", International Journal of Naval Architecture and Ocean Engineering , submitted. - -#### 부 록 - -#### 1. Strain/Stress Vector - -$$ -\text{Strain } \left\{ \mathbf{E} \right\} = \begin{cases} E_{\xi\xi} \\ E_{\eta\eta} \\ E_{\zeta\zeta} \\ 2E_{\eta\zeta} \\ 2E_{\xi\zeta} \\ 2E_{\xi\eta} \end{cases}, \qquad \text{Stress } \left\{ \mathbf{S} \right\} = \begin{cases} S_{\xi\xi} \\ S_{\eta\eta} \\ S_{\zeta\zeta} \\ 2S_{\eta\zeta} \\ 2S_{\zeta\zeta} \\ 2S_{\xi\eta} \end{cases} -$$ - -#### 2. Shape Function - -$$ -N_{1}(\xi,\eta) = \frac{1}{4}(1-\xi)(1-\eta), \qquad N_{2}(\xi,\eta) = \frac{1}{4}(1+\xi)(1-\eta) -N_{3}(\xi,\eta) = \frac{1}{4}(1+\xi)(1+\eta), \qquad N_{4}(\xi,\eta) = \frac{1}{4}(1-\xi)(1+\eta) -$$ - -### 3. Jacobian Matrix - -$$ -[J] = \begin{bmatrix} X_{,\xi} & Y_{,\xi} & Z_{,\xi} \\ X_{,\eta} & Y_{,\eta} & Z_{,\eta} \\ X_{,\varsigma} & Y_{,\varsigma} & Z_{,\varsigma} \end{bmatrix} = \begin{bmatrix} \frac{\partial X}{\partial \xi} & \frac{\partial Y}{\partial \xi} & \frac{\partial Z}{\partial \xi} \\ \frac{\partial X}{\partial \eta} & \frac{\partial Y}{\partial \eta} & \frac{\partial Z}{\partial \eta} \\ \frac{\partial X}{\partial \zeta} & \frac{\partial Y}{\partial \zeta} & \frac{\partial Z}{\partial \zeta} \end{bmatrix} \\ -= \begin{bmatrix} \frac{\partial N_1}{\partial \xi} & \frac{\partial N_2}{\partial \xi} & \frac{\partial N_3}{\partial \xi} & \frac{\partial N_4}{\partial \xi} \\ \frac{\partial N_1}{\partial \eta} & \frac{\partial N_2}{\partial \eta} & \frac{\partial N_3}{\partial \eta} & \frac{\partial N_4}{\partial \eta} \\ 0 & 0 & 0 & 0 \end{bmatrix} \begin{bmatrix} X_1 & Y_1 & Z_1 \\ X_2 & Y_2 & Z_2 \\ X_3 & Y_3 & Z_3 \\ X_4 & Y_4 & Z_4 \end{bmatrix} + \frac{1}{2} \begin{bmatrix} \varsigma & \frac{\partial N_1}{\partial \xi} t_1 & \varsigma & \frac{\partial N_2}{\partial \xi} t_2 & \varsigma & \frac{\partial N_3}{\partial \xi} t_3 & \varsigma & \frac{\partial N_4}{\partial \xi} t_4 \\ \varsigma & \frac{\partial N_1}{\partial \eta} t_1 & \varsigma & \frac{\partial N_2}{\partial \eta} t_2 & \varsigma & \frac{\partial N_3}{\partial \eta} t_3 & \varsigma & \frac{\partial N_4}{\partial \eta} t_4 \\ N_1 t_1 & N_2 t_2 & N_3 t_3 & N_4 t_4 \end{bmatrix} \begin{bmatrix} \mathbf{Y}_1^n \\ \mathbf{Y}_2^n \\ \mathbf{Y}_3^n \\ \mathbf{Y}_4^n \end{bmatrix} \\ -= \begin{bmatrix} \mathbf{G}_1 \\ \mathbf{G}_2 \end{bmatrix} -$$ - -## 감사의 글 - -이렇게 밤늦게 실험실에서 책상 앞에 앉아 있는 날도 얼마 남지 않았습니다. 이제는 졸업을 앞두고 논문의 마지막 장을 써 내려간다는 것이 실감나지 않고 아쉬운 마음이 크지만 다사다난했던 지난 2년의 시간들은 아마도 제 인생 중에 큰 전환점이었으며 귀중한 발판으로 삼으려 합니다. - -아직은 부족하지만 제가 이렇게 많은 발전과 함께 자그마한 결실을 맺고 졸업을 앞두게 되어 이 자리까지 도움을 주신 고마운 분들에게 감사의 글로써 졸업논문을 맺으려 합니다. - -먼저, 너무도 부족했던 저를 대학원 짧은 기간 동안에 이렇게 발전하도록 일일이 깨우쳐주시고 함께 고민해 주시며 아낌없이 지도해주신 조진연 교수님께 심심한 감사를 드립니다. 그리고 학부시절 공학도의 길을 제시해주시고 저에게 큰 힘이 되어주신 김기욱 교수님, 항상 항공과의 발전을 위해 헌신적으로 학생들을 지도해 주시는 김범수 교수님, 최동환 교수님, 최기영 교수님, 노태성 교수님, 이승수 교수님, 유창경 교수님께 감사와 존경을 전합니다. - -실험실에서 동고동락했던 민환이형, 민호, 순신이, 연철이, 재연이, 이제 대학원 생활을 시작하게 될 소영이, 우빈이 모두에게 고마운 마음을 전하며 모두들에게 앞으로도 좋은 일이 가득하길 바랍니다. 그리고 대학원에 입학하여 도움을 주신 장훈이형, 형수형, 영민이형, 규원이형에게도 감사의 뜻을 전합니다. - -초등학교 때부터 지금까지도 변치 않는 우정을 나누고 있는 동현이, 용덕이, 항상 자기자리에서 최선을 다하는 대학 친구들 상형이형, 재필이, 광규, 영민이에게도 감사의 뜻을 전합니다. - -그리고 저의 소중한 가족들에게 감사합니다. 어려운 여건에서도 항상 큰아들을 믿어주시고 노심초사 걱정해 주시며 뒷바라지 해주신 부모님의 은혜에 깊은 감사를 드립니다. 또한 멀리 있고 잘해주지도 못하는 오빠를 믿고 따라주는 동생 진아, 민희에게도 고마움을 전하며 항상 사랑으로 지켜봐 주고 힘이 되어준 저의 가장 소중한 친구이자 연인인 미연이에게 고마움을 전합니다. - -마지막으로 저를 믿어주시고 사랑해주신 많은 분들의 기대에 보답하도록 사회에 꼭 필요한 일꾼이 되며 항상 최선을 다하는 모습을 보여드림을 다짐하겠습니다. - -> 2011년 12월 이 희 준 diff --git a/docs/README.md b/docs/README.md deleted file mode 100644 index a589877..0000000 --- a/docs/README.md +++ /dev/null @@ -1,101 +0,0 @@ -# FESA Documentation Index - -## Purpose -This directory is the source of truth for FESA solver planning, architecture, numerical conventions, verification, and future implementation work. - -Before changing solver code, read this file, then read the documents listed under "Required Reading". - -## Required Reading -1. `../AGENTS.md` -2. `../PROGRESS.md` -3. `../PLAN.md` -4. `PRD.md` -5. `HARNESS_ENGINEERING.md` -6. `ARCHITECTURE.md` -7. `ADR.md` -8. `NUMERICAL_CONVENTIONS.md` -9. `ABAQUS_INPUT_SUBSET.md` -10. `VERIFICATION_PLAN.md` -11. `RESULTS_SCHEMA.md` -12. `MITC4_FORMULATION.md` - -`MULTI_AGENT_RESEARCH_PLAN.md` is required when planning delegated research or creating/updating Codex agents. - -## Document Roles -| Document | Role | -|---|---| -| `PRD.md` | Product scope, users, phase roadmap, and Phase 1 success criteria | -| `HARNESS_ENGINEERING.md` | Planner/Generator/Evaluator workflow, sprint contract format, and evaluator rubric | -| `ARCHITECTURE.md` | Solver architecture, ownership boundaries, data flow, and extension model | -| `ADR.md` | Durable architecture decisions and tradeoffs | -| `NUMERICAL_CONVENTIONS.md` | DOF, units, coordinate/sign conventions, precision, boundary/reaction policy | -| `ABAQUS_INPUT_SUBSET.md` | Phase 1 Abaqus `.inp` subset, parser behavior, and unsupported features | -| `VERIFICATION_PLAN.md` | Reference folder contract, benchmark matrix, tolerances, and negative tests | -| `RESULTS_SCHEMA.md` | Step/frame/field/history result model and HDF5 layout | -| `MITC4_FORMULATION.md` | MITC4 baseline formulation contract and pre-implementation open decisions | -| `MULTI_AGENT_RESEARCH_PLAN.md` | Codex agent roles, research sequence, and source map | - -Root-level coordination files: - -| Document | Role | -|---|---| -| `../PLAN.md` | Forward-looking task plan, priorities, owners, and open questions | -| `../PROGRESS.md` | Completed work, verification results, blockers, and current risks | - -## Precedence Rules -When documents conflict: - -1. `AGENTS.md` critical rules win. -2. `ADR.md` wins for accepted design decisions. -3. `NUMERICAL_CONVENTIONS.md` wins for DOF, units, precision, signs, and reaction recovery. -4. Domain-specific dossier documents win within their area: - - Abaqus parser: `ABAQUS_INPUT_SUBSET.md` - - Verification/reference data: `VERIFICATION_PLAN.md` - - HDF5 results: `RESULTS_SCHEMA.md` - - MITC4 element formulation: `MITC4_FORMULATION.md` -5. `ARCHITECTURE.md` provides system structure and should be updated when module responsibilities change. -6. `PRD.md` records scope and roadmap, not low-level implementation details. - -If a lower-precedence document needs to override a higher-precedence decision, update or add an ADR first. - -## Phase 1 Hard Invariants -- Use 6 shell DOFs per node: `UX`, `UY`, `UZ`, `RX`, `RY`, `RZ`. -- Retain drilling DOF and use a small artificial drilling stiffness. -- Use `double` for real values. -- Use signed int64 ids, indices, equation ids, and sparse indices. -- Do not enforce a unit system; require self-consistent Abaqus-style units. -- Follow Abaqus-compatible sign conventions for results. -- Apply essential boundary conditions by constrained DOF elimination. -- Recover reactions from full vectors: `R_full = K_full * U_full - F_full`. -- Map Abaqus `S4` to FESA `MITC4`. -- Defer `S4R`. -- Require singular system diagnostics. -- Defer mesh quality diagnostics. -- Validate against stored reference artifacts under `../references/`; do not require Abaqus execution. -- Treat Abaqus `*.inp` files plus `*_displacements.csv` result files as the initial accepted displacement reference artifact contract. -- Treat Abaqus `*_reactionforces.csv` or `*_reactions.csv` files as reaction reference artifacts only after their RF/RM column mapping, tolerance, and pass/fail status are documented. - -## Implementation Readiness Checklist -Before creating Phase 1 implementation steps: - -- Use `../references/` as the accepted reference artifact folder. -- Use Abaqus `.inp` files plus solved `*_displacements.csv` files as the first automated displacement reference format. -- Use Abaqus RF/RM CSV files such as `*_reactionforces.csv` as the first reaction reference format when provided, but do not hide a node-wise RF mismatch by relaxing tolerances. -- Keep a compatibility note when a stored Abaqus reference input contains features outside the current Phase 1 parser subset. -- Use the current `MITC4_FORMULATION.md` as the Phase 1 MITC4 gate before implementing or reviewing element stiffness. -- Treat `U` and `RF` as the mandatory Phase 1 outputs; `S`, `E`, and `SF` require a later recovery-location decision. -- Treat CMake/CTest as the Phase 1 build and test harness unless a later ADR changes it. -- Before resuming Phase 1 implementation, re-plan against the revised MITC4 formulation and the `quad_02` S4 reference compatibility note. - -## Documentation Change Rules -- Keep changes narrow and update every affected document. -- When a change affects future work, update `../PLAN.md`. -- When a meaningful change is completed, update `../PROGRESS.md`. -- When a rule becomes binding, add or update an ADR. -- When a scope changes, update `PRD.md`. -- When module ownership changes, update `ARCHITECTURE.md`. -- When parser behavior changes, update `ABAQUS_INPUT_SUBSET.md`. -- When numerical conventions change, update `NUMERICAL_CONVENTIONS.md`. -- When output layout changes, update `RESULTS_SCHEMA.md`. -- When benchmark or tolerance policy changes, update `VERIFICATION_PLAN.md`. -- When MITC4 math or assumptions change, update `MITC4_FORMULATION.md`. diff --git a/docs/RESULTS_SCHEMA.md b/docs/RESULTS_SCHEMA.md deleted file mode 100644 index c01c3a4..0000000 --- a/docs/RESULTS_SCHEMA.md +++ /dev/null @@ -1,351 +0,0 @@ -# Results Schema - -## Purpose -This document defines the FESA result data model and the proposed HDF5 layout. - -The schema must support Phase 1 linear static results while remaining compatible with future nonlinear increments, dynamic frames, thermal fields, and history outputs. - -## Source Basis -- HDF5 files organize named objects in a rooted graph with groups and datasets: https://docs.hdfgroup.org/documentation/hdf5/latest/_h5_d_m__u_g.html -- HDF5 datasets store multidimensional arrays and metadata needed to interpret them: https://docs.hdfgroup.org/documentation/hdf5/latest/_h5_d__u_g.html -- HDF5 attributes are small metadata objects attached to groups or datasets: https://portal.hdfgroup.org/documentation/hdf5/latest/_h5_a__u_g.html - -## Data Model -FESA stores results with this hierarchy: - -```text -ResultFile -└── ResultStep - └── ResultFrame - ├── FieldOutput - └── HistoryOutput -``` - -Definitions: -- `ResultStep`: one analysis step from input. -- `ResultFrame`: one output point inside a step. For Phase 1 linear static, use frame `0`. -- `FieldOutput`: node, element, integration-point, or section-point field values. -- `HistoryOutput`: scalar or vector time/load history values for selected entities or global quantities. - -## HDF5 Root Layout -Proposed file layout: - -```text -/ - metadata/ - model/ - results/ - steps/ - Step-1/ - frames/ - 0/ - fieldOutputs/ - U/ - RF/ - S/ - E/ - SF/ - historyOutputs/ - referenceComparison/ -``` - -## Root Attributes -Attach these attributes to `/`: - -| Attribute | Type | Required | Meaning | -|---|---|---|---| -| `schema_name` | string | yes | `FESA_RESULTS` | -| `schema_version` | int64 | yes | Start at `1` | -| `solver_name` | string | yes | `FESA` | -| `solver_version` | string | no | Build or release version | -| `created_utc` | string | no | ISO 8601 timestamp | -| `input_file` | string | no | Original input path | -| `input_sha256` | string | no | Input file hash | -| `unit_system_note` | string | no | User-provided self-consistent unit note | -| `dof_convention` | string | yes | `UX,UY,UZ,RX,RY,RZ` | -| `sign_convention` | string | yes | `Abaqus-compatible` | -| `precision` | string | yes for Phase 1 in-memory result model | `double` | -| `index_type` | string | yes for Phase 1 in-memory result model | `int64` | - -## `/metadata` -Recommended datasets or attributes: - -```text -/metadata/sourceFiles -/metadata/analysisType -/metadata/precision -/metadata/indexType -/metadata/conventions -``` - -Rules: -- Use attributes for small scalar metadata. -- Use datasets for variable-length tables or arrays. -- Store `precision = double`. -- Store `index_type = int64`. - -## `/model` -The result file may contain enough model data to interpret outputs: - -```text -/model/nodes/ids -/model/nodes/coordinates -/model/elements/ids -/model/elements/types -/model/elements/connectivity -/model/sets/nodeSets/ -/model/sets/elementSets/ -/model/dofs/components -``` - -Rules: -- Store ids as int64. -- Store coordinates as double. -- Store connectivity as int64. -- Phase 1 element type string should be `MITC4`. -- If complete model mirroring is deferred, store at least node ids, element ids, and output entity labels required to interpret results. - -## Field Output Layout -Each field output group should follow this pattern: - -```text -/results/steps/Step-1/frames/0/fieldOutputs/U/ - values - entity_ids - component_labels -``` - -Attributes: - -| Attribute | Type | Meaning | -|---|---|---| -| `position` | string | `NODAL`, `ELEMENT`, `INTEGRATION_POINT`, or `SECTION_POINT` | -| `entity_type` | string | `node`, `element`, etc. | -| `component_count` | int64 | Number of components | -| `basis` | string | `GLOBAL`, `LOCAL_SHELL`, or another documented basis | -| `description` | string | Human-readable field description | - -Datasets: -- `entity_ids`: shape `[n_entities]`, int64. -- `values`: shape `[n_entities, n_components]`, double. -- `component_labels`: shape `[n_components]`, string. - -## Phase 1 Field Outputs -### `U` -Nodal displacement and rotation. - -```text -position = NODAL -entity_type = node -basis = GLOBAL -component_labels = ["UX", "UY", "UZ", "RX", "RY", "RZ"] -``` - -### `RF` -Nodal reaction force and moment. - -```text -position = NODAL -entity_type = node -basis = GLOBAL -component_labels = ["RFX", "RFY", "RFZ", "RMX", "RMY", "RMZ"] -``` - -`RF` is computed from the full system: - -```text -RF = K_full * U_full - F_full -``` - -### `S` -Shell stress output when implemented. - -```text -position = INTEGRATION_POINT or SECTION_POINT -entity_type = element -basis = LOCAL_SHELL -component_labels = ["S11", "S22", "S33", "S12", "S13", "S23"] -``` - -### `E` -Shell strain output when implemented. - -```text -position = INTEGRATION_POINT or SECTION_POINT -entity_type = element -basis = LOCAL_SHELL -component_labels = ["E11", "E22", "E33", "E12", "E13", "E23"] -``` - -### `SF` -Shell section force and moment resultants when implemented. - -```text -position = ELEMENT or INTEGRATION_POINT -entity_type = element -basis = LOCAL_SHELL -component_labels = ["SF1", "SF2", "SF12", "SM1", "SM2", "SM12", "SS13", "SS23"] -``` - -The final component labels should be cross-checked with the accepted Abaqus reference output variables. - -## Phase 1 Mandatory Outputs -The first complete linear static solver path must write: -- root metadata attributes: `schema_name`, `schema_version`, `solver_name`, `dof_convention`, `sign_convention`, `precision`, and `index_type`. -- `/model/nodes/ids` -- `/model/nodes/coordinates` -- `/model/elements/ids` -- `/model/elements/types` -- `/model/elements/connectivity` -- `/results/steps//frames/0/fieldOutputs/U` -- `/results/steps//frames/0/fieldOutputs/RF` -- frame attributes for `frame_id`, `step_time`, `total_time`, `increment`, `iteration`, and `converged`. - -Stress, strain, and section force outputs may remain optional until the MITC4 displacement/reaction benchmarks are stable. - -## Frame Attributes -Attach these attributes to each frame group: - -| Attribute | Type | Meaning | -|---|---|---| -| `frame_id` | int64 | Zero-based frame id | -| `step_time` | double | Time/load parameter within step | -| `total_time` | double | Accumulated analysis time | -| `increment` | int64 | Increment number | -| `iteration` | int64 | Newton iteration or `0` for linear static | -| `converged` | int | `1` true, `0` false | -| `description` | string | Optional note | - -For Phase 1 linear static: -- `frame_id = 0` -- `step_time = 1.0` -- `total_time = 1.0` -- `increment = 1` -- `iteration = 0` -- `converged = 1` - -## History Outputs -History output layout: - -```text -/results/steps/Step-1/historyOutputs// - x - values - component_labels -``` - -Attributes: -- `x_label`: usually `step_time`, `total_time`, `load_factor`, or `frame_id`. -- `entity_type`: `global`, `node`, `element`, `set`. -- `entity_id` or `entity_name`. - -Phase 1 recommended history outputs: -- total external load by component. -- total reaction by component. -- maximum displacement magnitude. -- linear solver status. - -## Reference Comparison Group -Optional comparison output: - -```text -/referenceComparison/cases// - expected - actual - abs_error - rel_error - pass -``` - -Attributes: -- `reference_source` -- `reference_solver` -- `reference_solver_version` -- `comparison_status` -- `comparison_timestamp_utc` - -## CSV Reference Input Mapping -FESA solver outputs remain HDF5-oriented, but early verification may compare HDF5 field outputs against stored Abaqus CSV artifacts under `references/`. - -Initial accepted displacement reference naming: - -```text -references/.inp -references/_displacements.csv -``` - -`*_displacements.csv` maps to: - -```text -/results/steps//frames//fieldOutputs/U -``` - -Required CSV columns: - -| CSV Column | HDF5 Field Component | -|---|---| -| `Node Label` | `entity_ids` | -| `U-U1` | `UX` | -| `U-U2` | `UY` | -| `U-U3` | `UZ` | -| `UR-UR1` | `RX` | -| `UR-UR2` | `RY` | -| `UR-UR3` | `RZ` | - -Rules: -- CSV files are reference inputs for tests, not the primary FESA result storage format. -- The comparator must preserve node-label matching and must not rely on row order alone. -- The comparator must require FESA `U` component labels `UX`, `UY`, `UZ`, `RX`, `RY`, `RZ` with `position = NODAL`, `entity_type = node`, and `basis = GLOBAL`. -- Duplicate FESA output node ids, duplicate CSV node labels, missing FESA nodes, missing CSV columns, and nonnumeric CSV values are comparison failures. -- The comparison report may be stored under `/referenceComparison`. - -Initial accepted reaction reference naming: - -```text -references/_reactionforces.csv -references/_reactions.csv -``` - -`*_reactionforces.csv` and `*_reactions.csv` map to: - -```text -/results/steps//frames//fieldOutputs/RF -``` - -Required CSV columns: - -| CSV Column | HDF5 Field Component | -|---|---| -| `Node Label` | `entity_ids` | -| `RF-RF1` | `RFX` | -| `RF-RF2` | `RFY` | -| `RF-RF3` | `RFZ` | -| `RM-RM1` | `RMX` | -| `RM-RM2` | `RMY` | -| `RM-RM3` | `RMZ` | - -Rules: -- The comparator must require FESA `RF` component labels `RFX`, `RFY`, `RFZ`, `RMX`, `RMY`, `RMZ` with `position = NODAL`, `entity_type = node`, and `basis = GLOBAL`. -- The FESA `RF` field must be the full-vector reaction field recovered as `K_full * U_full - F_full`. -- Duplicate FESA output node ids, duplicate CSV node labels, missing FESA nodes, missing CSV columns, and nonnumeric CSV values are comparison failures. -- Stress CSV or section force CSV formats must be documented before automated use. - -## Naming Rules -- Use stable ASCII group and dataset names. -- Use original input labels as attributes when names contain spaces or nonportable characters. -- Normalize step names to HDF5-safe names while preserving original names in attributes. -- Never depend on HDF5 object iteration order for semantic ordering. Store explicit ids and labels. - -## Compression and Chunking -Phase 1 may write contiguous datasets for simplicity. - -For large models: -- Use chunked datasets for large field outputs. -- Consider compression only when it does not materially slow common read paths. -- Keep metadata small enough for attributes. - -## Open Decisions -- Exact mandatory stress/strain/resultant output variables in Phase 1. -- Whether result files always mirror the model or only store output entity ids. -- Whether reference comparison results are stored in solver output files or separate reports. -- Exact naming and column contracts for stress, strain, and section-force Abaqus CSV reference files. diff --git a/docs/VERIFICATION_PLAN.md b/docs/VERIFICATION_PLAN.md deleted file mode 100644 index cfde64a..0000000 --- a/docs/VERIFICATION_PLAN.md +++ /dev/null @@ -1,322 +0,0 @@ -# Verification Plan - -## Purpose -This document defines how FESA will be verified against small focused tests and stored reference artifacts. - -The project rule is strict: solver quality is maintained by comparing against reference examples and reference results. - -## Source Basis -- The original Dvorkin-Bathe four-node shell paper presents a non-flat quadrilateral shell element for thin and thick shells and includes test/demonstration problems: https://web.mit.edu/kjb/www/Publications_Prior_to_1998/A_Continuum_Mechanics_Based_Four-Node_Shell_Element_for_General_Nonlinear_Analysis.pdf -- The MITC3+/MITC4+ benchmark paper states that MITC methods address shell locking and uses widely-used shell benchmarks with convergence studies: https://web.mit.edu/kjb/www/Principal_Publications/Performance_of_the_MITC3%2B_and_MITC4%2B_shell_elements_in_widely_used_benchmark_problems.pdf -- COMSOL's Scordelis-Lo example documents the benchmark geometry, material, load, constraints, and reference displacement context: https://doc.comsol.com/5.6/doc/com.comsol.help.models.sme.scordelis_lo_roof/scordelis_lo_roof.html -- NAFEMS benchmark surveys list nonlinear shell and structural benchmark classes for future phases: https://www.nafems.org/publications/pubguide/benchmarks/Page6/ - -## Verification Philosophy -FESA verification uses multiple layers: - -1. Unit tests for low-level math, parser, and DOF management. -2. Element tests for MITC4 stiffness, rigid body modes, patch behavior, and drilling stabilization. -3. Assembly tests for small known systems. -4. Solver tests for constrained/reduced systems and reaction recovery. -5. Input-to-result integration tests using Abaqus-style `.inp` files. -6. Reference comparisons against stored Abaqus result artifacts under `references/`. - -A single large benchmark is not enough. Phase 1 should use many small models that isolate failure causes. - -## Abaqus Availability -Abaqus is not available locally and must not be required by CI. - -Rules: -- The user provides `.inp` files and solved reference result artifacts. -- FESA tests compare against stored artifacts only. -- Reference artifacts are treated as the numerical source of truth once accepted into `references/`. - -## Accepted Reference Folder Contract -The repository uses `references/` as the durable root for stored Abaqus inputs and solved output artifacts. - -Initial flat-file convention: - -```text -references/ - README.md - .inp - _displacements.csv -``` - -Accepted stored cases: - -```text -references/ - quad_01.inp - quad_01_displacements.csv - quad_02.inp - quad_02_phase1.inp - quad_02_displacements.csv - quad_02_reactionforces.csv -``` - -`quad_01_displacements.csv` contains 121 nodal rows with these columns: - -```text -Node Label, U-U1, U-U2, U-U3, UR-UR1, UR-UR2, UR-UR3 -``` - -`quad_02_displacements.csv` uses the same required columns and contains 121 nodal rows. - -`quad_02_reactionforces.csv` contains 121 nodal rows with these columns: - -```text -Node Label, RF-RF1, RF-RF2, RF-RF3, RM-RM1, RM-RM2, RM-RM3 -``` - -Future manifest-driven layout is still recommended as the case set grows: - -```text -references/ - README.md - phase1-linear-static/ - manifest.json - single-element-membrane/ - model.inp - displacements.csv - notes.md - single-element-bending/ - model.inp - displacements.csv - notes.md - cantilever-strip/ - model.inp - displacements.csv - notes.md - scordelis-lo-roof/ - model.inp - displacements.csv - notes.md -``` - -`manifest.json` should include: - -```json -{ - "schema_version": 1, - "cases": [ - { - "name": "single-element-membrane", - "input": "single-element-membrane/model.inp", - "displacements": "single-element-membrane/displacements.csv", - "analysis_type": "linear_static", - "element": "MITC4", - "source_solver": "Abaqus", - "source_solver_version": "provided-by-user", - "unit_system_note": "self-consistent", - "result_format": "abaqus_displacement_csv_v1", - "tags": ["phase1", "element", "membrane"] - } - ] -} -``` - -## Displacement CSV Artifact -Initial automated displacement comparison uses Abaqus-exported CSV files named: - -```text -_displacements.csv -``` - -Required columns: - -| CSV Column | FESA Field | Component | -|---|---|---| -| `Node Label` | `U` | entity id | -| `U-U1` | `U` | `UX` | -| `U-U2` | `U` | `UY` | -| `U-U3` | `U` | `UZ` | -| `UR-UR1` | `U` | `RX` | -| `UR-UR2` | `U` | `RY` | -| `UR-UR3` | `U` | `RZ` | - -Rules: -- Column matching is by exact normalized header text after trimming whitespace. -- `Node Label` is parsed as int64. -- All displacement and rotation values are parsed as `double`. -- The comparator must match rows by node id, not by row order alone. -- Missing or nonnumeric `Node Label` values, duplicate CSV node labels, missing columns, or nonnumeric component values are reference artifact errors. -- Missing nodes in FESA output, duplicate FESA output node ids, wrong FESA `U` component labels, or wrong nodal/global field metadata are comparison errors. -- CSV displacement comparison maps to `/results/steps//frames//fieldOutputs/U`. -- If the `.inp` has multiple steps or frames, the case metadata must state which step/frame the CSV represents. - -Structured JSON or HDF5 comparison artifacts may be added later, but `*_displacements.csv` is the accepted first automated reference format. - -## Reaction CSV Artifact -Initial automated reaction comparison uses Abaqus-exported CSV files named: - -```text -_reactionforces.csv -_reactions.csv -``` - -Required columns: - -| CSV Column | FESA Field | Component | -|---|---|---| -| `Node Label` | `RF` | entity id | -| `RF-RF1` | `RF` | `RFX` | -| `RF-RF2` | `RF` | `RFY` | -| `RF-RF3` | `RF` | `RFZ` | -| `RM-RM1` | `RF` | `RMX` | -| `RM-RM2` | `RF` | `RMY` | -| `RM-RM3` | `RF` | `RMZ` | - -Rules: -- Column matching is by exact normalized header text after trimming whitespace. -- `Node Label` is parsed as int64. -- All reaction force and reaction moment values are parsed as `double`. -- The comparator must match rows by node id, not by row order alone. -- Missing or nonnumeric `Node Label` values, duplicate CSV node labels, missing columns, or nonnumeric component values are reference artifact errors. -- Missing nodes in FESA output, duplicate FESA output node ids, wrong FESA `RF` component labels, or wrong nodal/global field metadata are comparison errors. -- CSV reaction comparison maps to `/results/steps//frames//fieldOutputs/RF`. -- FESA `RF` must be recovered from full vectors, `K_full * U_full - F_full`. - -## Other Result Artifacts -Additional Abaqus result exports may be added as the solver grows. Recommended naming: - -| File Pattern | Purpose | Status | -|---|---|---| -| `*_displacements.csv` | Nodal `U` displacement/rotation comparison | Accepted initial format | -| `*_reactionforces.csv` | Nodal `RF` force/moment comparison | Accepted for `quad_02`; current node-wise comparison exposes an open solver/reference mismatch | -| `*_reactions.csv` | Nodal `RF` force/moment comparison | Accepted alias for future reaction CSV artifacts | -| `*_stresses.csv` | Stress output comparison | Future, after `S` output is documented | -| `*_strains.csv` | Strain output comparison | Future, after `E` output is documented | -| `*_section_forces.csv` | Shell resultant comparison | Future, after `SF` output is documented | - -Each new CSV type must define required columns, component mapping, position, basis, and tolerances before automated use. - -## Stored Reference Compatibility Notes -Stored Abaqus `.inp` files are preserved as generated, even when they include features outside the current FESA parser subset. This preserves provenance but does not expand Phase 1 support. - -Current initial case: - -| Case | Files | Notes | -|---|---|---| -| `quad_01` | `quad_01.inp`, `quad_01_displacements.csv` | Abaqus/CAE Learning Edition 2024 input; 121 displacement rows; includes `S4R`, `Part/Assembly/Instance`, `*Density`, and `NLGEOM=YES`, which are outside the current Phase 1 parser/solver subset | -| `quad_02` | `quad_02.inp`, `quad_02_displacements.csv`, `quad_02_reactionforces.csv` | Abaqus/CAE input; 121 displacement rows and 121 RF/RM rows; uses `TYPE=S4` and `NLGEOM=NO`, but still includes `Part/Assembly/Instance` and `*Density`, so it must be normalized or handled by an explicit parser compatibility sprint before automated Phase 1 input acceptance | -| `quad_02_phase1` | `quad_02_phase1.inp`, `quad_02_displacements.csv`, `quad_02_reactionforces.csv`, `quad_02_notes.md` | Phase 1-compatible derivative input for `quad_02`; preserves ids, connectivity, material, thickness, boundary nodes, load node, and load magnitude while removing unsupported Abaqus/CAE scaffolding | - -Rules: -- Original `.inp` files under `references/` should not be modified just to fit FESA Phase 1. -- If a normalized Phase 1-compatible input is needed, add it as a separate file with a clear name and note its relationship to the original. -- Unsupported features in stored reference inputs must be reported by compatibility checks, not silently accepted by parser tests. -- A reference case may be useful for future compatibility even before it is executable by the current Phase 1 solver. -- `quad_02_phase1.inp` is the accepted normalized input path for the first `quad_02` Phase 1 S4 reference regression after the MITC4 rebuild and end-to-end workflow are complete. - -## Tolerance Policy -Use absolute and relative tolerance: - -```text -abs_error = abs(actual - expected) -rel_error = abs_error / max(abs(expected), reference_scale) -pass if abs_error <= abs_tol or rel_error <= rel_tol -``` - -Initial guidance: -- Parser and exact metadata tests: exact match. -- DOF mapping tests: exact integer match. -- Small linear algebra tests: `abs_tol = 1e-12`, `rel_tol = 1e-10`. -- Element stiffness symmetry: matrix norm tolerance around `1e-10` to `1e-9`, adjusted by stiffness scale. -- Reference displacement CSV tests: start with `rel_tol = 1e-5` and refine after baseline agreement. -- Reaction equilibrium tests: use force-scale-based tolerances. - -Final benchmark tolerances must be stored with each reference case. - -### Step 14 Stored Reference Regression Status -The first automated stored-reference displacement regression is active for: - -```text -input: references/quad_02_phase1.inp -expected U: references/quad_02_displacements.csv -expected RF: references/quad_02_reactionforces.csv -``` - -Comparison rules: -- The original `references/quad_02.inp` remains unsupported provenance and must still be rejected by parser compatibility tests because it contains Abaqus/CAE scaffolding. -- The normalized `quad_02_phase1.inp` is the executable Phase 1 input for this reference pair. -- The FESA `U` field is compared node-id-by-node-id against Abaqus CSV columns `U-U1`, `U-U2`, `U-U3`, `UR-UR1`, `UR-UR2`, and `UR-UR3`. -- The active tolerance is `abs_tol = 1.0e-12`, `rel_tol = 1.0e-5`, `reference_scale = 1.0`. -- The FESA `RF` field can now be compared node-id-by-node-id against Abaqus CSV columns `RF-RF1`, `RF-RF2`, `RF-RF3`, `RM-RM1`, `RM-RM2`, and `RM-RM3`. -- Current `quad_02_phase1` node-wise RF comparison is intentionally recorded as a non-passing known gap, not an accepted pass gate: with `abs_tol = 1.0e-6`, `rel_tol = 1.0e-5`, `reference_scale = 1.0`, the observed maximum absolute error is about `612.751347`, maximum relative error is about `0.494032`, and the first observed mismatch is node `1` `RFZ`, expected `6860.0`, actual `6652.459896`. -- Keep R-010 open until the `quad_02` RF mismatch is explained or fixed. Do not loosen reaction tolerances just to make this case pass. - -## Phase 1 Benchmark Matrix -| Case | Purpose | Required Output | -|---|---|---| -| Parser smoke model | Verify Phase 1 keyword subset | Domain object counts, set membership | -| Single MITC4 membrane | Constant strain behavior | `U`, element strains/stresses if available | -| Single MITC4 bending | Bending stiffness sanity | `U`, rotations | -| Rigid body mode check | Verify near-zero internal forces without constraints | eigen/nullspace or controlled diagnostic | -| Constrained static sanity | Verify constrained DOF elimination | `U`, `RF` | -| Reaction balance | Verify full-vector reaction recovery | total load vs total reaction | -| Cantilever shell strip | Bending-dominated displacement convergence | tip `U`, support `RF` | -| Simply supported square plate | Thin shell/plate locking sensitivity | center displacement | -| Scordelis-Lo roof | Curved shell benchmark | midpoint vertical displacement | -| Pinched cylinder | Curved shell bending/membrane behavior | loaded-point displacement | -| Twisted beam | Warped shell and drilling sensitivity | tip displacement/rotation | - -### Step 11 Analytic Patch And Benchmark Scaffold -The first MITC4 patch-test layer is kept local and analytic so it does not depend on Abaqus execution or stored CSV artifacts. - -Current in-repository coverage: -- Constant membrane patch: verifies uniform local `[eps11, eps22, gamma12]` strain at all `2 x 2 x 2` samples and positive physical energy. -- Pure bending patch: verifies membrane-free midsurface bending behavior, through-thickness antisymmetry, and positive physical bending energy. -- Pure transverse shear patch: verifies constant `gamma13` reproduction through the MITC tying path. -- Pure twist patch: verifies bilinear transverse-shear reproduction for `w = twist * x * y`, covering the locking-sensitive twist/shear interpolation path before curved benchmarks. -- Drilling sensitivity negative check: verifies membrane patch energy is unchanged by drilling scale changes when no drilling DOF participates. -- Thin one-element cantilever strip: verifies tip displacement increases materially as thickness decreases, providing an early shear-locking sensitivity smoke test rather than a final convergence benchmark. - -Scordelis-Lo roof remains a named benchmark scaffold for Phase 1 planning, but it is not executable in the current Step 11 test layer because Phase 1 does not yet support pressure loads and does not yet have an accepted curved-shell Abaqus reference artifact. The first executable Scordelis-Lo sprint must define load representation, normalized input compatibility, stored displacement reference data, and scale-aware displacement tolerances before it is used as an automated acceptance case. - -## Minimum Phase 1 Acceptance -Before MITC4 Phase 1 is considered credible: -- All parser smoke tests pass. -- `DofManager` free/constrained mapping tests pass. -- Reduced-system solve reconstructs full `U`. -- `RF = K_full * U_full - F_full` is tested. -- MITC4 element stiffness is symmetric within tolerance for linear elastic Phase 1. -- Rigid body modes do not create artificial membrane/bending stiffness beyond documented drilling stabilization effects. -- At least three stored Abaqus reference models pass: one single-element case, one simple multi-element plate/shell case, and one curved shell benchmark. -- At least one automated `*_displacements.csv` comparison passes against a stored Abaqus reference case. - -## Reference Onboarding Checklist -Every accepted reference case should include: -- an Abaqus `.inp` file. -- at least one solved result artifact, initially `*_displacements.csv`. -- raw Abaqus output when available, such as `.dat`, `.rpt`, `.csv`, or exported table files. -- `notes.md` describing model purpose, unit system note, Abaqus version, expected dominant behavior, and known limitations. -- comparison paths matching `docs/RESULTS_SCHEMA.md`. -- absolute and relative tolerances for each compared quantity. -- tags that identify phase, feature, benchmark family, and expected failure mode if negative. -- compatibility notes for unsupported Abaqus keywords or analysis options. - -Reference artifacts should not be overwritten silently. If reference values change, record why they changed in `notes.md` or the manifest. - -## Singular System Verification -Required negative tests: -- Model with no boundary conditions should fail with a singular-system diagnostic. -- Model with missing property should fail before assembly. -- Model with load on missing node or set should fail during input/model validation. -- Model with an unconstrained isolated node should identify free untouched DOFs. -- Model with only drilling DOF instability should mention weak or unconstrained rotational DOFs. - -## What Not To Verify In Phase 1 -- Mesh quality metrics such as aspect ratio, skew, warpage, Jacobian quality, or distortion warnings. -- Pressure loads. -- RBE2/RBE3. -- Nonlinear increments. -- Time integration. -- Thermal-stress coupling. - -## User Inputs Needed -- Additional small Abaqus `.inp` files and solved result CSV files under `references/`. -- Additional reaction output artifacts when available, preferably documented `*_reactionforces.csv` or `*_reactions.csv` files. -- Abaqus version used to generate each reference when it is not evident from the `.inp`. -- Unit system notes and tolerances for each case. -- Whether future Phase 1-compatible reference files will use Abaqus `S4`, while `S4R` remains deferred. diff --git a/include/fesa/Analysis/Analysis.hpp b/include/fesa/Analysis/Analysis.hpp deleted file mode 100644 index 6cb6227..0000000 --- a/include/fesa/Analysis/Analysis.hpp +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -#include "fesa/Analysis/LinearStaticAnalysis.hpp" -#include "fesa/ModuleInfo.hpp" - -namespace fesa::module { - -inline constexpr std::string_view kAnalysis = "Analysis"; - -} // namespace fesa::module diff --git a/include/fesa/Analysis/LinearStaticAnalysis.hpp b/include/fesa/Analysis/LinearStaticAnalysis.hpp deleted file mode 100644 index daf23fb..0000000 --- a/include/fesa/Analysis/LinearStaticAnalysis.hpp +++ /dev/null @@ -1,120 +0,0 @@ -#pragma once - -#include "fesa/Assembly/Assembly.hpp" -#include "fesa/Core/Core.hpp" -#include "fesa/IO/IO.hpp" -#include "fesa/Math/Math.hpp" -#include "fesa/Results/Results.hpp" -#include "fesa/Util/Diagnostics.hpp" - -#include -#include - -namespace fesa { - -struct AnalysisResult { - AnalysisModel model; - AnalysisState state; - ResultFile result_file; - std::vector diagnostics; - - bool ok() const { - return !hasError(diagnostics); - } -}; - -class Analysis { - public: - virtual ~Analysis() = default; - - AnalysisResult run(const Domain& domain) const { - AnalysisResult result; - initialize(domain, result); - if (hasError(result.diagnostics)) { - return result; - } - solve(domain, result); - return result; - } - - protected: - virtual void initialize(const Domain& domain, AnalysisResult& result) const { - auto diagnostics = validateDomain(domain); - result.diagnostics.insert(result.diagnostics.end(), diagnostics.begin(), diagnostics.end()); - } - - virtual void solve(const Domain& domain, AnalysisResult& result) const = 0; -}; - -class LinearStaticAnalysis final : public Analysis { - public: - explicit LinearStaticAnalysis(const LinearSolver* solver = nullptr) : solver_(solver) {} - - protected: - void solve(const Domain& domain, AnalysisResult& result) const override { - result.model = buildLinearStaticAnalysisModel(domain); - result.diagnostics.insert(result.diagnostics.end(), result.model.diagnostics.begin(), result.model.diagnostics.end()); - if (hasError(result.diagnostics)) { - return; - } - DofManager dofs(domain); - if (dofs.freeDofCount() == 0) { - result.diagnostics.push_back(makeDiagnostic(Severity::Error, "FESA-SINGULAR-NO-FREE-DOFS", - "No free DOFs exist after applying constraints", "dof")); - return; - } - AssemblyResult assembly = assembleSystem(domain, dofs); - result.diagnostics.insert(result.diagnostics.end(), assembly.diagnostics.begin(), assembly.diagnostics.end()); - if (hasError(result.diagnostics)) { - return; - } - const auto reduced = projectToReducedSystem(assembly, dofs); - result.diagnostics.insert(result.diagnostics.end(), reduced.diagnostics.begin(), reduced.diagnostics.end()); - if (hasError(result.diagnostics)) { - return; - } - const LinearSolver& active_solver = solver_ == nullptr ? defaultSolver() : *solver_; - SolveResult solved = active_solver.solve(reduced.k, reduced.f); - result.diagnostics.insert(result.diagnostics.end(), solved.diagnostics.begin(), solved.diagnostics.end()); - if (!solved.ok()) { - return; - } - if (static_cast(solved.x.size()) != dofs.freeDofCount()) { - result.diagnostics.push_back(makeDiagnostic(Severity::Error, "FESA-SOLVER-SIZE", - "Linear solver returned a vector with the wrong size", "solver")); - return; - } - result.state.u_full = dofs.reconstructFullVector(solved.x); - result.state.f_external_full = assembly.f_full; - result.state.f_internal_full = assembly.k_full.multiply(result.state.u_full); - result.state.reaction_full = recoverFullReaction(assembly.k_full, result.state.u_full, result.state.f_external_full); - result.state.converged = true; - InMemoryResultsWriter writer; - writer.writeLinearStatic(domain, result.model, dofs, result.state.u_full, result.state.reaction_full); - result.result_file = writer.result(); - } - - private: - static const LinearSolver& defaultSolver() { - static const GaussianEliminationSolver solver; - return solver; - } - - const LinearSolver* solver_ = nullptr; -}; - -inline AnalysisResult runLinearStaticInputString(const std::string& text, - const std::string& source_name = "", - const LinearSolver* solver = nullptr) { - AbaqusInputParser parser; - const auto parsed = parser.parseString(text, source_name); - if (!parsed.ok()) { - AnalysisResult result; - result.diagnostics = parsed.diagnostics; - return result; - } - LinearStaticAnalysis analysis(solver); - return analysis.run(parsed.domain); -} - -} // namespace fesa diff --git a/include/fesa/Assembly/Assembly.hpp b/include/fesa/Assembly/Assembly.hpp deleted file mode 100644 index dd8703f..0000000 --- a/include/fesa/Assembly/Assembly.hpp +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -#include "fesa/Assembly/AssemblySystem.hpp" -#include "fesa/ModuleInfo.hpp" - -namespace fesa::module { - -inline constexpr std::string_view kAssembly = "Assembly"; - -} // namespace fesa::module diff --git a/include/fesa/Assembly/AssemblySystem.hpp b/include/fesa/Assembly/AssemblySystem.hpp deleted file mode 100644 index 2b63836..0000000 --- a/include/fesa/Assembly/AssemblySystem.hpp +++ /dev/null @@ -1,168 +0,0 @@ -#pragma once - -#include "fesa/Core/Core.hpp" -#include "fesa/Element/Element.hpp" -#include "fesa/Load/Load.hpp" -#include "fesa/Math/Math.hpp" -#include "fesa/Property/Property.hpp" -#include "fesa/Util/Diagnostics.hpp" - -#include -#include -#include -#include -#include - -namespace fesa { - -inline SparsePattern buildReducedSparsePattern(const Domain& domain, const DofManager& dofs) { - SparsePattern pattern; - pattern.equation_count = dofs.freeDofCount(); - std::set> ordered_entries; - for (const auto& [element_id, element] : domain.elements) { - (void)element_id; - const auto equations = dofs.elementEquationIds(element); - for (EquationId row : equations) { - if (row < 0) { - continue; - } - for (EquationId col : equations) { - if (col < 0) { - continue; - } - ordered_entries.insert({row, col}); - } - } - } - pattern.entries.reserve(ordered_entries.size()); - for (const auto& entry : ordered_entries) { - pattern.entries.push_back({entry.first, entry.second}); - } - return pattern; -} - -inline std::vector recoverFullReaction(const DenseMatrix& k_full, - const std::vector& u_full, - const std::vector& f_full) { - if (k_full.rows() != k_full.cols() || static_cast(u_full.size()) != k_full.cols() || - static_cast(f_full.size()) != k_full.rows()) { - throw std::runtime_error("full reaction size mismatch"); - } - std::vector reaction = k_full.multiply(u_full); - for (std::size_t i = 0; i < reaction.size(); ++i) { - reaction[i] -= f_full[i]; - } - return reaction; -} - -struct AssemblyResult { - DenseMatrix k_full; - std::vector f_full; - SparsePattern reduced_pattern; - std::vector diagnostics; - - bool ok() const { - return !hasError(diagnostics); - } -}; - -struct ReducedSystem { - DenseMatrix k; - std::vector f; - std::vector free_full_indices; - std::vector diagnostics; - - bool ok() const { - return !hasError(diagnostics); - } -}; - -inline AssemblyResult assembleSystem(const Domain& domain, const DofManager& dofs, ElementStiffnessOptions options = {}) { - AssemblyResult result; - result.k_full = DenseMatrix(dofs.fullDofCount(), dofs.fullDofCount()); - result.f_full = std::vector(static_cast(dofs.fullDofCount()), 0.0); - result.reduced_pattern = buildReducedSparsePattern(domain, dofs); - if (dofs.freeDofCount() > 0 && result.reduced_pattern.nonzeroCount() == 0) { - result.diagnostics.push_back(makeDiagnostic(Severity::Error, "FESA-SINGULAR-SPARSE-PATTERN", - "Reduced sparse pattern has no stiffness entries", "assembly")); - } - for (const auto& [element_id, element] : domain.elements) { - const ShellSection* section = shellSectionForElement(domain, element_id); - if (section == nullptr) { - result.diagnostics.push_back( - {Severity::Error, "FESA-ASSEMBLY-MISSING-PROPERTY", "Element has no shell section", {}}); - continue; - } - const auto material_it = domain.materials.find(Domain::key(section->material)); - if (material_it == domain.materials.end()) { - result.diagnostics.push_back( - {Severity::Error, "FESA-ASSEMBLY-MISSING-MATERIAL", "Element material is missing", {}}); - continue; - } - std::array coordinates{}; - for (std::size_t i = 0; i < 4; ++i) { - coordinates[i] = domain.nodes.at(element.node_ids[i]).coordinates; - } - const auto stiffness = mitc4ElementStiffness(coordinates, material_it->second.elastic_modulus, - material_it->second.poisson_ratio, section->thickness, options); - result.diagnostics.insert(result.diagnostics.end(), stiffness.diagnostics.begin(), stiffness.diagnostics.end()); - if (!stiffness.ok()) { - continue; - } - const auto element_full_indices = dofs.elementFullDofIndices(element); - for (LocalIndex a = 0; a < 24; ++a) { - const LocalIndex ia = element_full_indices[static_cast(a)]; - for (LocalIndex b = 0; b < 24; ++b) { - const LocalIndex ib = element_full_indices[static_cast(b)]; - result.k_full.add(ia, ib, stiffness.global(a, b)); - } - } - } - for (const NodalLoad& load : domain.loads) { - const auto dof = dofFromAbaqus(load.dof); - if (!dof) { - result.diagnostics.push_back(makeDiagnostic(Severity::Error, "FESA-ASSEMBLY-LOAD-DOF", - "Nodal load references an invalid DOF", "cload")); - continue; - } - for (GlobalId node_id : resolveNodeTarget(domain, load.target, &result.diagnostics)) { - result.f_full[static_cast(dofs.fullIndex(node_id, *dof))] += load.magnitude; - } - } - return result; -} - -inline ReducedSystem projectToReducedSystem(const AssemblyResult& assembly, const DofManager& dofs) { - ReducedSystem result; - result.k = DenseMatrix(dofs.freeDofCount(), dofs.freeDofCount()); - result.f = std::vector(static_cast(dofs.freeDofCount()), 0.0); - result.free_full_indices = dofs.freeFullIndices(); - if (assembly.k_full.rows() != assembly.k_full.cols() || assembly.k_full.rows() != dofs.fullDofCount() || - static_cast(assembly.f_full.size()) != dofs.fullDofCount()) { - result.diagnostics.push_back(makeDiagnostic(Severity::Error, "FESA-ASSEMBLY-SIZE", - "Full-space stiffness/load sizes do not match DofManager", "assembly")); - return result; - } - if (dofs.freeDofCount() == 0) { - result.diagnostics.push_back(makeDiagnostic(Severity::Error, "FESA-SINGULAR-NO-FREE-DOFS", - "No free DOFs exist after applying constraints", "dof")); - return result; - } - if (assembly.reduced_pattern.equation_count != dofs.freeDofCount() || assembly.reduced_pattern.nonzeroCount() == 0) { - result.diagnostics.push_back(makeDiagnostic(Severity::Error, "FESA-SINGULAR-SPARSE-PATTERN", - "Reduced sparse pattern is empty or inconsistent with free DOFs", - "assembly")); - return result; - } - for (LocalIndex i = 0; i < dofs.freeDofCount(); ++i) { - const LocalIndex full_i = dofs.freeFullIndices()[static_cast(i)]; - result.f[static_cast(i)] = assembly.f_full[static_cast(full_i)]; - for (LocalIndex j = 0; j < dofs.freeDofCount(); ++j) { - const LocalIndex full_j = dofs.freeFullIndices()[static_cast(j)]; - result.k(i, j) = assembly.k_full(full_i, full_j); - } - } - return result; -} - -} // namespace fesa diff --git a/include/fesa/Boundary/Boundary.hpp b/include/fesa/Boundary/Boundary.hpp deleted file mode 100644 index b87c298..0000000 --- a/include/fesa/Boundary/Boundary.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include "fesa/Core/Types.hpp" -#include "fesa/ModuleInfo.hpp" - -#include - -namespace fesa::module { - -inline constexpr std::string_view kBoundary = "Boundary"; - -} // namespace fesa::module - -namespace fesa { - -struct BoundaryCondition { - std::string target; - int first_dof = 0; - int last_dof = 0; - Real magnitude = 0.0; -}; - -} // namespace fesa diff --git a/include/fesa/Core/AnalysisModel.hpp b/include/fesa/Core/AnalysisModel.hpp deleted file mode 100644 index 07e142e..0000000 --- a/include/fesa/Core/AnalysisModel.hpp +++ /dev/null @@ -1,67 +0,0 @@ -#pragma once - -#include "fesa/Core/Domain.hpp" -#include "fesa/Util/Diagnostics.hpp" - -#include -#include -#include - -namespace fesa { - -struct AnalysisModel { - StepDefinition step; - std::vector active_element_ids; - std::vector active_boundary_condition_indices; - std::vector active_load_indices; - std::vector active_shell_section_indices; - std::vector active_material_keys; - std::vector diagnostics; - - bool ok() const { - return !hasError(diagnostics); - } -}; - -inline AnalysisModel buildLinearStaticAnalysisModel(const Domain& domain, LocalIndex step_index = 0) { - AnalysisModel model; - if (domain.steps.empty()) { - model.step = {"Step-1", "linear_static"}; - } else { - if (step_index < 0 || step_index >= static_cast(domain.steps.size())) { - model.diagnostics.push_back(makeDiagnostic(Severity::Error, "FESA-ANALYSIS-STEP-INDEX", - "Requested analysis step index is out of range", "analysis model")); - model.step = domain.steps.front(); - } else { - model.step = domain.steps[static_cast(step_index)]; - } - } - if (domain.steps.size() > 1) { - model.diagnostics.push_back(makeDiagnostic(Severity::Error, "FESA-ANALYSIS-MULTIPLE-STEPS", - "Phase 1 execution supports one active linear static step", "analysis model")); - } - if (model.step.analysis_type != "linear_static") { - model.diagnostics.push_back(makeDiagnostic(Severity::Error, "FESA-ANALYSIS-UNSUPPORTED-STEP", - "Only linear static steps are supported in Phase 1", "analysis model")); - } - for (const auto& [element_id, element] : domain.elements) { - (void)element; - model.active_element_ids.push_back(element_id); - } - for (std::size_t i = 0; i < domain.boundary_conditions.size(); ++i) { - model.active_boundary_condition_indices.push_back(i); - } - for (std::size_t i = 0; i < domain.loads.size(); ++i) { - model.active_load_indices.push_back(i); - } - for (std::size_t i = 0; i < domain.shell_sections.size(); ++i) { - model.active_shell_section_indices.push_back(i); - } - for (const auto& [material_key, material] : domain.materials) { - (void)material; - model.active_material_keys.push_back(material_key); - } - return model; -} - -} // namespace fesa diff --git a/include/fesa/Core/AnalysisState.hpp b/include/fesa/Core/AnalysisState.hpp deleted file mode 100644 index 4a577dd..0000000 --- a/include/fesa/Core/AnalysisState.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include "fesa/Core/Types.hpp" - -#include - -namespace fesa { - -struct AnalysisState { - std::vector u_full; - std::vector f_external_full; - std::vector f_internal_full; - std::vector reaction_full; - bool converged = false; -}; - -} // namespace fesa diff --git a/include/fesa/Core/Core.hpp b/include/fesa/Core/Core.hpp deleted file mode 100644 index 5b00cef..0000000 --- a/include/fesa/Core/Core.hpp +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include "fesa/Core/AnalysisModel.hpp" -#include "fesa/Core/AnalysisState.hpp" -#include "fesa/Core/Dof.hpp" -#include "fesa/Core/DofManager.hpp" -#include "fesa/Core/Domain.hpp" -#include "fesa/Core/Types.hpp" -#include "fesa/Core/Validation.hpp" -#include "fesa/ModuleInfo.hpp" - -namespace fesa::module { - -inline constexpr std::string_view kCore = "Core"; - -} // namespace fesa::module diff --git a/include/fesa/Core/Dof.hpp b/include/fesa/Core/Dof.hpp deleted file mode 100644 index d7aac31..0000000 --- a/include/fesa/Core/Dof.hpp +++ /dev/null @@ -1,59 +0,0 @@ -#pragma once - -#include "fesa/Core/Types.hpp" - -#include -#include -#include -#include - -namespace fesa { - -enum class Dof : int { UX = 0, UY = 1, UZ = 2, RX = 3, RY = 4, RZ = 5 }; - -inline std::array allDofs() { - return {Dof::UX, Dof::UY, Dof::UZ, Dof::RX, Dof::RY, Dof::RZ}; -} - -inline int dofIndex(Dof dof) { - return static_cast(dof); -} - -inline int abaqusDofNumber(Dof dof) { - return dofIndex(dof) + 1; -} - -inline std::optional dofFromAbaqus(int dof) { - if (dof < 1 || dof > 6) { - return std::nullopt; - } - return static_cast(dof - 1); -} - -inline const char* dofLabel(Dof dof) { - switch (dof) { - case Dof::UX: - return "UX"; - case Dof::UY: - return "UY"; - case Dof::UZ: - return "UZ"; - case Dof::RX: - return "RX"; - case Dof::RY: - return "RY"; - case Dof::RZ: - return "RZ"; - } - return ""; -} - -inline std::vector displacementComponentLabels() { - return {"UX", "UY", "UZ", "RX", "RY", "RZ"}; -} - -inline std::vector reactionComponentLabels() { - return {"RFX", "RFY", "RFZ", "RMX", "RMY", "RMZ"}; -} - -} // namespace fesa diff --git a/include/fesa/Core/DofManager.hpp b/include/fesa/Core/DofManager.hpp deleted file mode 100644 index 5f7acb5..0000000 --- a/include/fesa/Core/DofManager.hpp +++ /dev/null @@ -1,145 +0,0 @@ -#pragma once - -#include "fesa/Core/Dof.hpp" -#include "fesa/Core/Domain.hpp" -#include "fesa/Core/Validation.hpp" - -#include -#include -#include -#include -#include - -namespace fesa { - -struct DofAddress { - GlobalId node_id = 0; - Dof dof = Dof::UX; -}; - -class DofManager { - public: - explicit DofManager(const Domain& domain) { - for (const auto& [node_id, node] : domain.nodes) { - (void)node; - node_ids_.push_back(node_id); - for (Dof dof : allDofs()) { - const LocalIndex full_index = static_cast(all_dofs_.size()); - const auto key = std::make_pair(node_id, dofIndex(dof)); - all_dofs_.push_back(key); - full_index_by_key_[key] = full_index; - } - } - for (const BoundaryCondition& boundary : domain.boundary_conditions) { - if (!validAbaqusDofRange(boundary.first_dof, boundary.last_dof)) { - continue; - } - for (GlobalId node_id : resolveNodeTarget(domain, boundary.target)) { - for (int dof = boundary.first_dof; dof <= boundary.last_dof; ++dof) { - constrained_.insert(std::make_pair(node_id, dof - 1)); - } - } - } - for (const auto& key : all_dofs_) { - const LocalIndex full_index = full_index_by_key_.at(key); - if (constrained_.count(key) == 0) { - equation_by_key_[key] = static_cast(free_full_indices_.size()); - free_full_indices_.push_back(full_index); - } else { - equation_by_key_[key] = -1; - constrained_full_indices_.push_back(full_index); - } - } - } - - LocalIndex fullDofCount() const { - return static_cast(all_dofs_.size()); - } - - LocalIndex freeDofCount() const { - return static_cast(free_full_indices_.size()); - } - - LocalIndex constrainedDofCount() const { - return static_cast(constrained_full_indices_.size()); - } - - const std::vector& nodeIds() const { - return node_ids_; - } - - const std::vector& freeFullIndices() const { - return free_full_indices_; - } - - const std::vector& constrainedFullIndices() const { - return constrained_full_indices_; - } - - DofAddress fullDof(LocalIndex full_index) const { - const auto& key = all_dofs_.at(static_cast(full_index)); - return {key.first, static_cast(key.second)}; - } - - LocalIndex fullIndex(GlobalId node_id, Dof dof) const { - return full_index_by_key_.at(std::make_pair(node_id, dofIndex(dof))); - } - - EquationId equation(GlobalId node_id, Dof dof) const { - return equation_by_key_.at(std::make_pair(node_id, dofIndex(dof))); - } - - bool isConstrained(GlobalId node_id, Dof dof) const { - return constrained_.count(std::make_pair(node_id, dofIndex(dof))) != 0; - } - - std::vector reduceFullVector(const std::vector& full) const { - std::vector reduced; - reduced.reserve(free_full_indices_.size()); - for (LocalIndex full_index : free_full_indices_) { - reduced.push_back(full.at(static_cast(full_index))); - } - return reduced; - } - - std::vector reconstructFullVector(const std::vector& reduced) const { - std::vector full(static_cast(fullDofCount()), 0.0); - for (std::size_t i = 0; i < free_full_indices_.size(); ++i) { - full[static_cast(free_full_indices_[i])] = reduced.at(i); - } - return full; - } - - std::array elementFullDofIndices(const Element& element) const { - std::array indices{}; - for (LocalIndex node = 0; node < 4; ++node) { - for (Dof dof : allDofs()) { - const LocalIndex local = 6 * node + dofIndex(dof); - indices[static_cast(local)] = fullIndex(element.node_ids[static_cast(node)], dof); - } - } - return indices; - } - - std::array elementEquationIds(const Element& element) const { - std::array equations{}; - for (LocalIndex node = 0; node < 4; ++node) { - for (Dof dof : allDofs()) { - const LocalIndex local = 6 * node + dofIndex(dof); - equations[static_cast(local)] = equation(element.node_ids[static_cast(node)], dof); - } - } - return equations; - } - - private: - std::vector node_ids_; - std::vector> all_dofs_; - std::set> constrained_; - std::map, LocalIndex> full_index_by_key_; - std::map, EquationId> equation_by_key_; - std::vector free_full_indices_; - std::vector constrained_full_indices_; -}; - -} // namespace fesa diff --git a/include/fesa/Core/Domain.hpp b/include/fesa/Core/Domain.hpp deleted file mode 100644 index 62e6ea0..0000000 --- a/include/fesa/Core/Domain.hpp +++ /dev/null @@ -1,76 +0,0 @@ -#pragma once - -#include "fesa/Boundary/Boundary.hpp" -#include "fesa/Core/Types.hpp" -#include "fesa/Load/Load.hpp" -#include "fesa/Math/Vector.hpp" -#include "fesa/Property/Property.hpp" -#include "fesa/Util/String.hpp" - -#include -#include -#include -#include - -namespace fesa { - -struct Node { - GlobalId id = 0; - Vec3 coordinates; -}; - -enum class ElementType { MITC4 }; - -inline std::string elementTypeLabel(ElementType type) { - switch (type) { - case ElementType::MITC4: - return "MITC4"; - } - return "UNKNOWN"; -} - -struct Element { - GlobalId id = 0; - ElementType type = ElementType::MITC4; - std::array node_ids{}; - std::string source_elset; -}; - -struct NodeSet { - std::string name; - std::vector node_ids; -}; - -struct ElementSet { - std::string name; - std::vector element_ids; -}; - -struct Material { - std::string name; - Real elastic_modulus = 0.0; - Real poisson_ratio = 0.0; -}; - -struct StepDefinition { - std::string name = "Step-1"; - std::string analysis_type = "linear_static"; -}; - -struct Domain { - std::map nodes; - std::map elements; - std::map node_sets; - std::map element_sets; - std::map materials; - std::vector shell_sections; - std::vector boundary_conditions; - std::vector loads; - std::vector steps; - - static std::string key(const std::string& label) { - return lower(trim(label)); - } -}; - -} // namespace fesa diff --git a/include/fesa/Core/Types.hpp b/include/fesa/Core/Types.hpp deleted file mode 100644 index 67bcae9..0000000 --- a/include/fesa/Core/Types.hpp +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include - -namespace fesa { - -using Real = double; -using GlobalId = std::int64_t; -using LocalIndex = std::int64_t; -using EquationId = std::int64_t; -using SparseIndex = std::int64_t; - -} // namespace fesa diff --git a/include/fesa/Core/Validation.hpp b/include/fesa/Core/Validation.hpp deleted file mode 100644 index f8d8f78..0000000 --- a/include/fesa/Core/Validation.hpp +++ /dev/null @@ -1,219 +0,0 @@ -#pragma once - -#include "fesa/Core/Dof.hpp" -#include "fesa/Core/Domain.hpp" -#include "fesa/Util/Diagnostics.hpp" -#include "fesa/Util/String.hpp" - -#include -#include -#include -#include -#include -#include -#include - -namespace fesa { - -inline std::optional numericTarget(const std::string& target) { - return parseInt64(target); -} - -inline std::vector resolveNodeTarget(const Domain& domain, const std::string& target, std::vector* diagnostics = nullptr, - const std::string& diagnostic_keyword = "node target") { - if (auto node_id = numericTarget(target)) { - if (domain.nodes.count(*node_id) == 0) { - if (diagnostics != nullptr) { - diagnostics->push_back( - makeDiagnostic(Severity::Error, "FESA-VALIDATION-MISSING-NODE", "Missing node target: " + target, diagnostic_keyword)); - } - return {}; - } - return {*node_id}; - } - auto set_it = domain.node_sets.find(Domain::key(target)); - if (set_it == domain.node_sets.end()) { - if (diagnostics != nullptr) { - diagnostics->push_back( - makeDiagnostic(Severity::Error, "FESA-VALIDATION-MISSING-NSET", "Missing node set: " + target, diagnostic_keyword)); - } - return {}; - } - return set_it->second.node_ids; -} - -inline const ShellSection* shellSectionForElement(const Domain& domain, GlobalId element_id) { - for (const ShellSection& section : domain.shell_sections) { - auto set_it = domain.element_sets.find(Domain::key(section.element_set)); - if (set_it == domain.element_sets.end()) { - continue; - } - if (std::find(set_it->second.element_ids.begin(), set_it->second.element_ids.end(), element_id) != set_it->second.element_ids.end()) { - return §ion; - } - } - return nullptr; -} - -inline std::string dofNameOrNumber(int abaqus_dof) { - auto dof = dofFromAbaqus(abaqus_dof); - if (dof) { - return dofLabel(*dof); - } - return "DOF " + std::to_string(abaqus_dof); -} - -inline bool validAbaqusDofRange(int first, int last) { - return dofFromAbaqus(first).has_value() && dofFromAbaqus(last).has_value() && first <= last; -} - -inline std::vector validateDomain(const Domain& domain) { - std::vector diagnostics; - if (domain.elements.empty()) { - diagnostics.push_back(makeDiagnostic(Severity::Error, "FESA-SINGULAR-NO-ACTIVE-ELEMENTS", - "No active elements exist in the current model", "analysis model")); - } - if (domain.boundary_conditions.empty()) { - diagnostics.push_back(makeDiagnostic(Severity::Warning, "FESA-SINGULAR-NO-BOUNDARY", "No boundary constraints are defined", "boundary")); - } - for (const auto& [set_key, set] : domain.node_sets) { - (void)set_key; - for (GlobalId node_id : set.node_ids) { - if (domain.nodes.count(node_id) == 0) { - diagnostics.push_back(makeDiagnostic(Severity::Error, "FESA-VALIDATION-NSET-MISSING-NODE", - "Node set " + set.name + " references missing node " + std::to_string(node_id), - "nset")); - } - } - } - for (const auto& [set_key, set] : domain.element_sets) { - (void)set_key; - for (GlobalId element_id : set.element_ids) { - if (domain.elements.count(element_id) == 0) { - diagnostics.push_back(makeDiagnostic(Severity::Error, "FESA-VALIDATION-ELSET-MISSING-ELEMENT", - "Element set " + set.name + " references missing element " + std::to_string(element_id), - "elset")); - } - } - } - for (const auto& [id, element] : domain.elements) { - for (GlobalId node_id : element.node_ids) { - if (domain.nodes.count(node_id) == 0) { - diagnostics.push_back(makeDiagnostic(Severity::Error, "FESA-VALIDATION-ELEMENT-MISSING-NODE", - "Element " + std::to_string(id) + " references missing node " + std::to_string(node_id), - "element")); - } - } - const ShellSection* section = shellSectionForElement(domain, id); - if (section == nullptr) { - diagnostics.push_back(makeDiagnostic(Severity::Error, "FESA-VALIDATION-MISSING-PROPERTY", - "Element " + std::to_string(id) + " has no assigned shell section", "element")); - } - } - for (const ShellSection& section : domain.shell_sections) { - if (section.thickness <= 0.0 || !std::isfinite(section.thickness)) { - diagnostics.push_back(makeDiagnostic(Severity::Error, "FESA-VALIDATION-NONPOSITIVE-THICKNESS", - "Shell section for element set " + section.element_set + " has non-positive thickness", - "shell section")); - } - if (domain.element_sets.count(Domain::key(section.element_set)) == 0) { - diagnostics.push_back(makeDiagnostic(Severity::Error, "FESA-VALIDATION-MISSING-ELSET", - "Shell section references missing element set: " + section.element_set, "shell section")); - } - auto material_it = domain.materials.find(Domain::key(section.material)); - if (material_it == domain.materials.end()) { - diagnostics.push_back(makeDiagnostic(Severity::Error, "FESA-VALIDATION-MISSING-MATERIAL", - "Shell section references missing material: " + section.material, "shell section")); - } else if (material_it->second.elastic_modulus <= 0.0) { - diagnostics.push_back(makeDiagnostic(Severity::Error, "FESA-VALIDATION-INCOMPLETE-MATERIAL", - "Material has no valid elastic constants: " + section.material, "material")); - } - } - for (const BoundaryCondition& boundary : domain.boundary_conditions) { - if (!validAbaqusDofRange(boundary.first_dof, boundary.last_dof)) { - diagnostics.push_back(makeDiagnostic(Severity::Error, "FESA-VALIDATION-BOUNDARY-DOF", - "Boundary target " + boundary.target + " has invalid DOF range " + - dofNameOrNumber(boundary.first_dof) + " to " + dofNameOrNumber(boundary.last_dof), - "boundary")); - } - (void)resolveNodeTarget(domain, boundary.target, &diagnostics, "boundary"); - } - for (const NodalLoad& load : domain.loads) { - if (!dofFromAbaqus(load.dof)) { - diagnostics.push_back(makeDiagnostic(Severity::Error, "FESA-VALIDATION-CLOAD-DOF", - "Load target " + load.target + " has invalid " + dofNameOrNumber(load.dof), "cload")); - } - (void)resolveNodeTarget(domain, load.target, &diagnostics, "cload"); - } - const bool any_nonzero_load = std::any_of(domain.loads.begin(), domain.loads.end(), [](const NodalLoad& load) { - return std::fabs(load.magnitude) > 0.0; - }); - if (!any_nonzero_load) { - diagnostics.push_back(makeDiagnostic(Severity::Warning, "FESA-SINGULAR-NO-NONZERO-LOAD", "No nonzero load is defined", "cload")); - } - - std::set> constrained_dofs; - for (const BoundaryCondition& boundary : domain.boundary_conditions) { - if (!validAbaqusDofRange(boundary.first_dof, boundary.last_dof)) { - continue; - } - for (GlobalId node_id : resolveNodeTarget(domain, boundary.target)) { - if (domain.nodes.count(node_id) == 0) { - continue; - } - for (int dof = boundary.first_dof; dof <= boundary.last_dof; ++dof) { - constrained_dofs.insert(std::make_pair(node_id, dof - 1)); - } - } - } - - std::set active_connectivity_nodes; - for (const auto& [element_id, element] : domain.elements) { - (void)element_id; - for (GlobalId node_id : element.node_ids) { - if (domain.nodes.count(node_id) != 0) { - active_connectivity_nodes.insert(node_id); - } - } - } - - LocalIndex free_dof_count = 0; - LocalIndex weak_drilling_count = 0; - GlobalId weak_drilling_example = 0; - for (const auto& [node_id, node] : domain.nodes) { - (void)node; - for (Dof dof : allDofs()) { - const auto key = std::make_pair(node_id, dofIndex(dof)); - if (constrained_dofs.count(key) != 0) { - continue; - } - ++free_dof_count; - if (!domain.elements.empty() && active_connectivity_nodes.count(node_id) == 0) { - diagnostics.push_back(makeDiagnostic(Severity::Error, "FESA-SINGULAR-DOF-UNTOUCHED", - "Node " + std::to_string(node_id) + " DOF " + dofLabel(dof) + - " is free but is not touched by active element connectivity", - "dof")); - } - if (!domain.elements.empty() && active_connectivity_nodes.count(node_id) != 0 && dof == Dof::RZ) { - if (weak_drilling_count == 0) { - weak_drilling_example = node_id; - } - ++weak_drilling_count; - } - } - } - if (!domain.nodes.empty() && free_dof_count == 0) { - diagnostics.push_back(makeDiagnostic(Severity::Error, "FESA-SINGULAR-NO-FREE-DOFS", - "No free DOFs exist after applying boundary constraints", "dof")); - } - if (weak_drilling_count > 0) { - diagnostics.push_back(makeDiagnostic(Severity::Warning, "FESA-SINGULAR-WEAK-DRILLING-DOF", - "Node " + std::to_string(weak_drilling_example) + - " DOF RZ is free; drilling rotation is weakly stabilized in Phase 1 (" + - std::to_string(weak_drilling_count) + " free drilling DOF(s))", - "dof")); - } - return diagnostics; -} - -} // namespace fesa diff --git a/include/fesa/Element/Element.hpp b/include/fesa/Element/Element.hpp deleted file mode 100644 index 43e4282..0000000 --- a/include/fesa/Element/Element.hpp +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include "fesa/Element/MITC4Geometry.hpp" -#include "fesa/Element/MITC4Kinematics.hpp" -#include "fesa/Element/MITC4MaterialIntegration.hpp" -#include "fesa/Element/MITC4Stiffness.hpp" -#include "fesa/ModuleInfo.hpp" - -namespace fesa::module { - -inline constexpr std::string_view kElement = "Element"; - -} // namespace fesa::module diff --git a/include/fesa/Element/MITC4Geometry.hpp b/include/fesa/Element/MITC4Geometry.hpp deleted file mode 100644 index 78ddf8e..0000000 --- a/include/fesa/Element/MITC4Geometry.hpp +++ /dev/null @@ -1,265 +0,0 @@ -#pragma once - -#include "fesa/Math/Math.hpp" -#include "fesa/Util/Util.hpp" - -#include -#include -#include -#include -#include - -namespace fesa { - -struct ShapeData { - std::array n{}; - std::array dr{}; - std::array ds{}; -}; - -inline ShapeData shapeFunctions(Real r, Real s) { - return { { - 0.25 * (1.0 - r) * (1.0 - s), - 0.25 * (1.0 + r) * (1.0 - s), - 0.25 * (1.0 + r) * (1.0 + s), - 0.25 * (1.0 - r) * (1.0 + s), - }, - { - -0.25 * (1.0 - s), - 0.25 * (1.0 - s), - 0.25 * (1.0 + s), - -0.25 * (1.0 + s), - }, - { - -0.25 * (1.0 - r), - -0.25 * (1.0 + r), - 0.25 * (1.0 + r), - 0.25 * (1.0 - r), - } }; -} - -struct LocalBasis { - Vec3 e1; - Vec3 e2; - Vec3 e3; -}; - -struct MITC4NaturalPoint { - Real xi = 0.0; - Real eta = 0.0; -}; - -struct MITC4TyingPoint { - std::string label; - MITC4NaturalPoint natural; - std::array edge_node_indices{}; -}; - -inline std::array mitc4NodeNaturalCoordinates() { - return {{{-1.0, -1.0}, {1.0, -1.0}, {1.0, 1.0}, {-1.0, 1.0}}}; -} - -inline std::array mitc4TyingPoints() { - return {{{"A", {0.0, -1.0}, {0, 1}}, - {"B", {-1.0, 0.0}, {0, 3}}, - {"C", {0.0, 1.0}, {3, 2}}, - {"D", {1.0, 0.0}, {1, 2}}}}; -} - -struct MITC4DirectorFrame { - Vec3 v1; - Vec3 v2; - Vec3 vn; -}; - -struct MITC4MidsurfaceDerivatives { - ShapeData shape; - Vec3 g1; - Vec3 g2; -}; - -struct MITC4Geometry { - std::array coordinates{}; - Real thickness = 0.0; - ShapeData center_shape; - Vec3 g1_center; - Vec3 g2_center; - Vec3 center_normal; - std::array nodal_frames{}; - std::vector diagnostics; - - bool ok() const { - return !hasError(diagnostics); - } -}; - -struct MITC4IntegrationBasis { - ShapeData shape; - Vec3 g1; - Vec3 g2; - Vec3 g3; - Real jacobian = 0.0; - LocalBasis local; - std::vector diagnostics; - - bool ok() const { - return !hasError(diagnostics); - } -}; - -inline Vec3 globalEX() { - return {1.0, 0.0, 0.0}; -} - -inline Vec3 globalEY() { - return {0.0, 1.0, 0.0}; -} - -inline Vec3 globalEZ() { - return {0.0, 0.0, 1.0}; -} - -inline Diagnostic mitc4Diagnostic(std::string code, std::string message) { - return makeDiagnostic(Severity::Error, std::move(code), std::move(message), "mitc4", "", 0); -} - -inline void appendDiagnostics(std::vector& target, const std::vector& source) { - target.insert(target.end(), source.begin(), source.end()); -} - -inline MITC4MidsurfaceDerivatives mitc4MidsurfaceDerivatives(const std::array& coordinates, - Real xi, - Real eta) { - MITC4MidsurfaceDerivatives result; - result.shape = shapeFunctions(xi, eta); - for (std::size_t i = 0; i < 4; ++i) { - result.g1 = result.g1 + result.shape.dr[i] * coordinates[i]; - result.g2 = result.g2 + result.shape.ds[i] * coordinates[i]; - } - return result; -} - -inline std::optional firstNormalizedCross(const std::array& axes, const Vec3& vector, Real tolerance) { - for (const Vec3& axis : axes) { - auto candidate = normalizedIfValid(cross(axis, vector), tolerance); - if (candidate) { - return candidate; - } - } - return std::nullopt; -} - -inline std::optional buildMITC4DirectorFrame(const Vec3& normal, Real tolerance) { - auto v1 = normalizedIfValid(cross(globalEY(), normal), tolerance); - if (!v1) { - v1 = firstNormalizedCross({globalEZ(), globalEX(), globalEY()}, normal, tolerance); - } - if (!v1) { - return std::nullopt; - } - auto v2 = normalizedIfValid(cross(normal, *v1), tolerance); - if (!v2) { - return std::nullopt; - } - return MITC4DirectorFrame{*v1, *v2, normal}; -} - -inline MITC4Geometry buildMITC4Geometry(const std::array& coordinates, - Real thickness, - Real tolerance = 1.0e-12) { - MITC4Geometry geometry; - geometry.coordinates = coordinates; - geometry.thickness = thickness; - if (!isFinite(thickness) || thickness <= tolerance) { - geometry.diagnostics.push_back( - mitc4Diagnostic("FESA-MITC4-THICKNESS", "MITC4 shell thickness must be positive and finite")); - } - for (const Vec3& coordinate : coordinates) { - if (!isFinite(coordinate)) { - geometry.diagnostics.push_back( - mitc4Diagnostic("FESA-MITC4-COORDINATE", "MITC4 element coordinates must be finite")); - break; - } - } - - const auto center = mitc4MidsurfaceDerivatives(coordinates, 0.0, 0.0); - geometry.center_shape = center.shape; - geometry.g1_center = center.g1; - geometry.g2_center = center.g2; - const auto normal = normalizedIfValid(cross(center.g1, center.g2), tolerance); - if (!normal) { - geometry.diagnostics.push_back( - mitc4Diagnostic("FESA-MITC4-SINGULAR-NORMAL", "MITC4 element center normal is near zero")); - return geometry; - } - geometry.center_normal = *normal; - - const auto frame = buildMITC4DirectorFrame(*normal, tolerance); - if (!frame) { - geometry.diagnostics.push_back( - mitc4Diagnostic("FESA-MITC4-SINGULAR-BASIS", "MITC4 nodal director basis could not be constructed")); - return geometry; - } - geometry.nodal_frames.fill(*frame); - return geometry; -} - -inline MITC4IntegrationBasis computeMITC4IntegrationBasis(const MITC4Geometry& geometry, - Real xi, - Real eta, - Real zeta, - Real tolerance = 1.0e-12) { - MITC4IntegrationBasis result; - result.diagnostics = geometry.diagnostics; - result.shape = shapeFunctions(xi, eta); - for (std::size_t i = 0; i < 4; ++i) { - const Vec3& coordinate = geometry.coordinates[i]; - const Vec3& normal = geometry.nodal_frames[i].vn; - result.g1 = result.g1 + result.shape.dr[i] * coordinate + - (0.5 * zeta * geometry.thickness * result.shape.dr[i]) * normal; - result.g2 = result.g2 + result.shape.ds[i] * coordinate + - (0.5 * zeta * geometry.thickness * result.shape.ds[i]) * normal; - result.g3 = result.g3 + (0.5 * geometry.thickness * result.shape.n[i]) * normal; - } - - result.jacobian = dot(cross(result.g1, result.g2), result.g3); - if (!isFinite(result.jacobian) || std::fabs(result.jacobian) <= tolerance) { - result.diagnostics.push_back( - mitc4Diagnostic("FESA-MITC4-SINGULAR-JACOBIAN", "MITC4 element Jacobian is near zero")); - } - - const auto e3 = normalizedIfValid(result.g3, tolerance); - if (!e3) { - result.diagnostics.push_back( - mitc4Diagnostic("FESA-MITC4-SINGULAR-BASIS", "MITC4 integration basis normal is near zero")); - return result; - } - auto e1 = normalizedIfValid(cross(result.g2, *e3), tolerance); - if (!e1) { - e1 = firstNormalizedCross({globalEY(), globalEZ(), globalEX()}, *e3, tolerance); - } - if (!e1) { - result.diagnostics.push_back( - mitc4Diagnostic("FESA-MITC4-SINGULAR-BASIS", "MITC4 integration basis tangent could not be constructed")); - return result; - } - const auto e2 = normalizedIfValid(cross(*e3, *e1), tolerance); - if (!e2) { - result.diagnostics.push_back( - mitc4Diagnostic("FESA-MITC4-SINGULAR-BASIS", "MITC4 integration basis is not right-handed")); - return result; - } - result.local = {*e1, *e2, *e3}; - return result; -} - -inline LocalBasis computeLocalBasis(const std::array& coordinates) { - const MITC4Geometry geometry = buildMITC4Geometry(coordinates, 1.0); - if (!geometry.ok()) { - throw std::runtime_error("invalid MITC4 geometry"); - } - const MITC4DirectorFrame& frame = geometry.nodal_frames[0]; - return {frame.v1, frame.v2, frame.vn}; -} - -} // namespace fesa diff --git a/include/fesa/Element/MITC4Kinematics.hpp b/include/fesa/Element/MITC4Kinematics.hpp deleted file mode 100644 index 8862020..0000000 --- a/include/fesa/Element/MITC4Kinematics.hpp +++ /dev/null @@ -1,186 +0,0 @@ -#pragma once - -#include "fesa/Element/MITC4Geometry.hpp" -#include "fesa/Material/MITC4PlaneStressMaterial.hpp" - -#include -#include -#include - -namespace fesa { - -using MITC4ElementDofVector = std::array; -using MITC4StrainRow = std::array; - -struct MITC4LocalRotations { - Real alpha = 0.0; - Real beta = 0.0; - Real gamma = 0.0; -}; - -struct MITC4DisplacementDerivatives { - ShapeData shape; - Vec3 displacement; - Vec3 du_dxi; - Vec3 du_deta; - Vec3 du_dzeta; - std::vector diagnostics; - - bool ok() const { - return !hasError(diagnostics); - } -}; - -struct MITC4StrainEvaluation { - MITC4StrainVector values{}; - std::vector diagnostics; - - bool ok() const { - return !hasError(diagnostics); - } -}; - -struct MITC4StrainRows { - std::array rows{}; - std::vector diagnostics; - - bool ok() const { - return !hasError(diagnostics); - } -}; - -inline Vec3 mitc4NodalTranslation(const MITC4ElementDofVector& values, std::size_t node) { - const std::size_t base = 6 * node; - return {values[base + 0], values[base + 1], values[base + 2]}; -} - -inline Vec3 mitc4NodalRotation(const MITC4ElementDofVector& values, std::size_t node) { - const std::size_t base = 6 * node; - return {values[base + 3], values[base + 4], values[base + 5]}; -} - -inline MITC4LocalRotations mitc4LocalRotations(const MITC4DirectorFrame& frame, const Vec3& global_rotation) { - return {dot(global_rotation, frame.v1), dot(global_rotation, frame.v2), dot(global_rotation, frame.vn)}; -} - -inline Vec3 mitc4DirectorIncrement(const MITC4DirectorFrame& frame, const Vec3& global_rotation) { - const MITC4LocalRotations rotations = mitc4LocalRotations(frame, global_rotation); - return (-rotations.alpha) * frame.v2 + rotations.beta * frame.v1; -} - -inline MITC4DisplacementDerivatives mitc4DisplacementDerivatives(const MITC4Geometry& geometry, - const MITC4ElementDofVector& values, - Real xi, - Real eta, - Real zeta) { - MITC4DisplacementDerivatives result; - result.diagnostics = geometry.diagnostics; - result.shape = shapeFunctions(xi, eta); - for (std::size_t node = 0; node < 4; ++node) { - const Vec3 translation = mitc4NodalTranslation(values, node); - const Vec3 rotation = mitc4NodalRotation(values, node); - const Vec3 q = mitc4DirectorIncrement(geometry.nodal_frames[node], rotation); - const Real n = result.shape.n[node]; - const Real dn_dxi = result.shape.dr[node]; - const Real dn_deta = result.shape.ds[node]; - result.displacement = result.displacement + n * translation + (0.5 * zeta * geometry.thickness * n) * q; - result.du_dxi = result.du_dxi + dn_dxi * translation + (0.5 * zeta * geometry.thickness * dn_dxi) * q; - result.du_deta = result.du_deta + dn_deta * translation + (0.5 * zeta * geometry.thickness * dn_deta) * q; - result.du_dzeta = result.du_dzeta + (0.5 * geometry.thickness * n) * q; - } - return result; -} - -inline void assignMITC4CovariantStrain(MITC4StrainVector& values, - const MITC4IntegrationBasis& basis, - const MITC4DisplacementDerivatives& derivatives) { - values[strainComponentIndex(MITC4StrainComponent::Eps11)] = dot(derivatives.du_dxi, basis.g1); - values[strainComponentIndex(MITC4StrainComponent::Eps22)] = dot(derivatives.du_deta, basis.g2); - values[strainComponentIndex(MITC4StrainComponent::Eps33)] = 0.0; - values[strainComponentIndex(MITC4StrainComponent::Gamma23)] = dot(derivatives.du_deta, basis.g3) + dot(derivatives.du_dzeta, basis.g2); - values[strainComponentIndex(MITC4StrainComponent::Gamma13)] = dot(derivatives.du_dxi, basis.g3) + dot(derivatives.du_dzeta, basis.g1); - values[strainComponentIndex(MITC4StrainComponent::Gamma12)] = dot(derivatives.du_dxi, basis.g2) + dot(derivatives.du_deta, basis.g1); -} - -inline MITC4StrainEvaluation mitc4DirectCovariantStrain(const MITC4Geometry& geometry, - const MITC4ElementDofVector& values, - Real xi, - Real eta, - Real zeta) { - MITC4StrainEvaluation result; - const auto basis = computeMITC4IntegrationBasis(geometry, xi, eta, zeta); - appendDiagnostics(result.diagnostics, basis.diagnostics); - const auto derivatives = mitc4DisplacementDerivatives(geometry, values, xi, eta, zeta); - appendDiagnostics(result.diagnostics, derivatives.diagnostics); - if (hasError(result.diagnostics)) { - return result; - } - assignMITC4CovariantStrain(result.values, basis, derivatives); - return result; -} - -inline MITC4StrainRows mitc4DirectCovariantStrainRows(const MITC4Geometry& geometry, Real xi, Real eta, Real zeta) { - MITC4StrainRows result; - const auto basis = computeMITC4IntegrationBasis(geometry, xi, eta, zeta); - appendDiagnostics(result.diagnostics, basis.diagnostics); - if (hasError(result.diagnostics)) { - return result; - } - for (std::size_t dof = 0; dof < 24; ++dof) { - MITC4ElementDofVector unit{}; - unit.fill(0.0); - unit[dof] = 1.0; - const auto derivatives = mitc4DisplacementDerivatives(geometry, unit, xi, eta, zeta); - appendDiagnostics(result.diagnostics, derivatives.diagnostics); - if (hasError(result.diagnostics)) { - return result; - } - MITC4StrainVector values{}; - assignMITC4CovariantStrain(values, basis, derivatives); - for (std::size_t component = 0; component < 6; ++component) { - result.rows[component][dof] = values[component]; - } - } - return result; -} - -inline MITC4StrainEvaluation evaluateMITC4StrainRows(const MITC4StrainRows& rows, - const MITC4ElementDofVector& values) { - MITC4StrainEvaluation result; - result.diagnostics = rows.diagnostics; - if (hasError(result.diagnostics)) { - return result; - } - for (std::size_t component = 0; component < 6; ++component) { - for (std::size_t dof = 0; dof < 24; ++dof) { - result.values[component] += rows.rows[component][dof] * values[dof]; - } - } - return result; -} - -inline MITC4StrainRows mitc4TiedCovariantStrainRows(const MITC4Geometry& geometry, Real xi, Real eta, Real zeta) { - MITC4StrainRows result = mitc4DirectCovariantStrainRows(geometry, xi, eta, zeta); - const auto direct_a = mitc4DirectCovariantStrainRows(geometry, 0.0, -1.0, zeta); - const auto direct_b = mitc4DirectCovariantStrainRows(geometry, -1.0, 0.0, zeta); - const auto direct_c = mitc4DirectCovariantStrainRows(geometry, 0.0, 1.0, zeta); - const auto direct_d = mitc4DirectCovariantStrainRows(geometry, 1.0, 0.0, zeta); - appendDiagnostics(result.diagnostics, direct_a.diagnostics); - appendDiagnostics(result.diagnostics, direct_b.diagnostics); - appendDiagnostics(result.diagnostics, direct_c.diagnostics); - appendDiagnostics(result.diagnostics, direct_d.diagnostics); - if (hasError(result.diagnostics)) { - return result; - } - const std::size_t gamma23 = strainComponentIndex(MITC4StrainComponent::Gamma23); - const std::size_t gamma13 = strainComponentIndex(MITC4StrainComponent::Gamma13); - for (std::size_t dof = 0; dof < 24; ++dof) { - result.rows[gamma13][dof] = 0.5 * (1.0 - eta) * direct_a.rows[gamma13][dof] + - 0.5 * (1.0 + eta) * direct_c.rows[gamma13][dof]; - result.rows[gamma23][dof] = 0.5 * (1.0 - xi) * direct_b.rows[gamma23][dof] + - 0.5 * (1.0 + xi) * direct_d.rows[gamma23][dof]; - } - return result; -} - -} // namespace fesa diff --git a/include/fesa/Element/MITC4MaterialIntegration.hpp b/include/fesa/Element/MITC4MaterialIntegration.hpp deleted file mode 100644 index 7ce33eb..0000000 --- a/include/fesa/Element/MITC4MaterialIntegration.hpp +++ /dev/null @@ -1,182 +0,0 @@ -#pragma once - -#include "fesa/Element/MITC4Kinematics.hpp" -#include "fesa/Material/MITC4PlaneStressMaterial.hpp" - -#include -#include -#include - -namespace fesa { - -struct MITC4StrainTransform { - MITC4MaterialMatrix matrix{}; - std::vector diagnostics; - - bool ok() const { - return !hasError(diagnostics); - } -}; - -struct MITC4IntegrationPoint { - Real xi = 0.0; - Real eta = 0.0; - Real zeta = 0.0; - Real weight = 0.0; -}; - -struct MITC4MaterialIntegrationSample { - MITC4IntegrationPoint point; - MITC4IntegrationBasis basis; - MITC4StrainRows strain_rows; - MITC4MaterialMatrix local_material{}; - MITC4MaterialMatrix strain_transform{}; - MITC4MaterialMatrix convected_material{}; - std::vector diagnostics; - - bool ok() const { - return !hasError(diagnostics); - } -}; - -struct MITC4MaterialIntegrationData { - std::vector samples; - std::vector diagnostics; - - bool ok() const { - return !hasError(diagnostics); - } -}; - -inline std::array mitc4GaussQuadrature2x2x2() { - const Real gauss = 1.0 / std::sqrt(3.0); - const std::array points = {-gauss, gauss}; - std::array integration_points{}; - std::size_t index = 0; - for (Real xi : points) { - for (Real eta : points) { - for (Real zeta : points) { - integration_points[index++] = {xi, eta, zeta, 1.0}; - } - } - } - return integration_points; -} - -inline std::array, 3> mitc4TensorFromEngineeringComponent(std::size_t component) { - std::array, 3> tensor{}; - switch (static_cast(component)) { - case MITC4StrainComponent::Eps11: - tensor[0][0] = 1.0; - break; - case MITC4StrainComponent::Eps22: - tensor[1][1] = 1.0; - break; - case MITC4StrainComponent::Eps33: - tensor[2][2] = 1.0; - break; - case MITC4StrainComponent::Gamma23: - tensor[1][2] = 0.5; - tensor[2][1] = 0.5; - break; - case MITC4StrainComponent::Gamma13: - tensor[0][2] = 0.5; - tensor[2][0] = 0.5; - break; - case MITC4StrainComponent::Gamma12: - tensor[0][1] = 0.5; - tensor[1][0] = 0.5; - break; - } - return tensor; -} - -inline MITC4StrainVector mitc4EngineeringVectorFromTensor(const std::array, 3>& tensor) { - MITC4StrainVector vector{}; - vector[strainComponentIndex(MITC4StrainComponent::Eps11)] = tensor[0][0]; - vector[strainComponentIndex(MITC4StrainComponent::Eps22)] = tensor[1][1]; - vector[strainComponentIndex(MITC4StrainComponent::Eps33)] = tensor[2][2]; - vector[strainComponentIndex(MITC4StrainComponent::Gamma23)] = 2.0 * tensor[1][2]; - vector[strainComponentIndex(MITC4StrainComponent::Gamma13)] = 2.0 * tensor[0][2]; - vector[strainComponentIndex(MITC4StrainComponent::Gamma12)] = 2.0 * tensor[0][1]; - return vector; -} - -inline MITC4StrainTransform mitc4CovariantToLocalStrainTransform(const MITC4IntegrationBasis& basis, - Real tolerance = 1.0e-12) { - MITC4StrainTransform result; - result.diagnostics = basis.diagnostics; - const Real jacobian = dot(cross(basis.g1, basis.g2), basis.g3); - if (!isFinite(jacobian) || std::fabs(jacobian) <= tolerance) { - result.diagnostics.push_back( - mitc4Diagnostic("FESA-MITC4-SINGULAR-JACOBIAN", "MITC4 material transform Jacobian is near zero")); - return result; - } - if (hasError(result.diagnostics)) { - return result; - } - - const std::array contravariant = { - (1.0 / jacobian) * cross(basis.g2, basis.g3), - (1.0 / jacobian) * cross(basis.g3, basis.g1), - (1.0 / jacobian) * cross(basis.g1, basis.g2)}; - const std::array local = {basis.local.e1, basis.local.e2, basis.local.e3}; - std::array, 3> direction_cosines{}; - for (std::size_t local_axis = 0; local_axis < 3; ++local_axis) { - for (std::size_t convected_axis = 0; convected_axis < 3; ++convected_axis) { - direction_cosines[local_axis][convected_axis] = dot(local[local_axis], contravariant[convected_axis]); - } - } - - for (std::size_t column = 0; column < 6; ++column) { - const auto covariant_tensor = mitc4TensorFromEngineeringComponent(column); - std::array, 3> local_tensor{}; - for (std::size_t a = 0; a < 3; ++a) { - for (std::size_t b = 0; b < 3; ++b) { - for (std::size_t i = 0; i < 3; ++i) { - for (std::size_t j = 0; j < 3; ++j) { - local_tensor[a][b] += direction_cosines[a][i] * direction_cosines[b][j] * covariant_tensor[i][j]; - } - } - } - } - const auto local_vector = mitc4EngineeringVectorFromTensor(local_tensor); - for (std::size_t row = 0; row < 6; ++row) { - result.matrix[row][column] = local_vector[row]; - } - } - return result; -} - -inline MITC4MaterialIntegrationData mitc4BuildMaterialIntegrationData(const MITC4Geometry& geometry, - Real elastic_modulus, - Real poisson_ratio, - Real shear_correction = 5.0 / 6.0) { - MITC4MaterialIntegrationData data; - const auto material = mitc4PlaneStressMaterialMatrix(elastic_modulus, poisson_ratio, shear_correction); - appendDiagnostics(data.diagnostics, material.diagnostics); - if (hasError(data.diagnostics)) { - return data; - } - - for (const MITC4IntegrationPoint& point : mitc4GaussQuadrature2x2x2()) { - MITC4MaterialIntegrationSample sample; - sample.point = point; - sample.local_material = material.matrix; - sample.basis = computeMITC4IntegrationBasis(geometry, point.xi, point.eta, point.zeta); - appendDiagnostics(sample.diagnostics, sample.basis.diagnostics); - const auto transform = mitc4CovariantToLocalStrainTransform(sample.basis); - sample.strain_transform = transform.matrix; - appendDiagnostics(sample.diagnostics, transform.diagnostics); - sample.strain_rows = mitc4TiedCovariantStrainRows(geometry, point.xi, point.eta, point.zeta); - appendDiagnostics(sample.diagnostics, sample.strain_rows.diagnostics); - if (!hasError(sample.diagnostics)) { - sample.convected_material = mitc4TransformMaterialMatrix(sample.local_material, sample.strain_transform); - } - appendDiagnostics(data.diagnostics, sample.diagnostics); - data.samples.push_back(sample); - } - return data; -} - -} // namespace fesa diff --git a/include/fesa/Element/MITC4Stiffness.hpp b/include/fesa/Element/MITC4Stiffness.hpp deleted file mode 100644 index 0704464..0000000 --- a/include/fesa/Element/MITC4Stiffness.hpp +++ /dev/null @@ -1,228 +0,0 @@ -#pragma once - -#include "fesa/Element/MITC4MaterialIntegration.hpp" -#include "fesa/Math/Math.hpp" - -#include -#include -#include -#include -#include - -namespace fesa { - -struct ElementStiffnessOptions { - Real drilling_stiffness_scale = 1.0e-3; -}; - -struct MITC4DrillingStabilizationResult { - DenseMatrix local_with_drilling; - Real reference_diagonal = 0.0; - Real drilling_stiffness = 0.0; - Real drilling_stiffness_scale = 0.0; - std::vector diagnostics; - - bool ok() const { - return !hasError(diagnostics); - } -}; - -struct MITC4ElementStiffnessResult { - DenseMatrix local_without_drilling; - DenseMatrix local_with_drilling; - DenseMatrix global; - std::size_t integration_point_count = 0; - Real drilling_reference_diagonal = 0.0; - Real drilling_stiffness = 0.0; - Real drilling_stiffness_scale = 0.0; - std::vector diagnostics; - - bool ok() const { - return !hasError(diagnostics); - } -}; - -inline DenseMatrix mitc4LocalDofTransform(const MITC4Geometry& geometry) { - DenseMatrix transform(24, 24); - for (LocalIndex node = 0; node < 4; ++node) { - const LocalIndex base = 6 * node; - const auto& frame = geometry.nodal_frames[static_cast(node)]; - const std::array axes = {frame.v1, frame.v2, frame.vn}; - for (LocalIndex local_axis = 0; local_axis < 3; ++local_axis) { - const Vec3 axis = axes[static_cast(local_axis)]; - transform(base + local_axis, base + 0) = axis.x; - transform(base + local_axis, base + 1) = axis.y; - transform(base + local_axis, base + 2) = axis.z; - transform(base + 3 + local_axis, base + 3) = axis.x; - transform(base + 3 + local_axis, base + 4) = axis.y; - transform(base + 3 + local_axis, base + 5) = axis.z; - } - } - return transform; -} - -inline MITC4StrainRows mitc4TransformStrainRowsToLocalDofs(const MITC4StrainRows& global_rows, - const DenseMatrix& local_dof_transform) { - MITC4StrainRows local_rows; - local_rows.diagnostics = global_rows.diagnostics; - if (hasError(local_rows.diagnostics)) { - return local_rows; - } - for (std::size_t component = 0; component < 6; ++component) { - for (LocalIndex local_dof = 0; local_dof < 24; ++local_dof) { - Real value = 0.0; - for (LocalIndex global_dof = 0; global_dof < 24; ++global_dof) { - value += global_rows.rows[component][static_cast(global_dof)] * - local_dof_transform(local_dof, global_dof); - } - local_rows.rows[component][static_cast(local_dof)] = value; - } - } - return local_rows; -} - -inline void accumulateMITC4BtDB(DenseMatrix& stiffness, const MITC4StrainRows& rows, - const MITC4MaterialMatrix& material, Real factor) { - for (LocalIndex i = 0; i < 24; ++i) { - for (LocalIndex j = 0; j < 24; ++j) { - Real value = 0.0; - for (std::size_t a = 0; a < 6; ++a) { - for (std::size_t b = 0; b < 6; ++b) { - value += rows.rows[a][static_cast(i)] * material[a][b] * - rows.rows[b][static_cast(j)]; - } - } - stiffness.add(i, j, value * factor); - } - } -} - -inline Real mitc4MinimumPositivePhysicalLocalDiagonal(const DenseMatrix& local_without_drilling, - Real tolerance = 1.0e-12) { - Real minimum = std::numeric_limits::infinity(); - for (LocalIndex node = 0; node < 4; ++node) { - for (LocalIndex local_dof = 0; local_dof < 5; ++local_dof) { - const Real diagonal = local_without_drilling(6 * node + local_dof, 6 * node + local_dof); - if (isFinite(diagonal) && diagonal > tolerance) { - minimum = std::min(minimum, diagonal); - } - } - } - return minimum; -} - -inline MITC4DrillingStabilizationResult mitc4ApplyDrillingStabilization(const DenseMatrix& local_without_drilling, - Real drilling_stiffness_scale, - Real tolerance = 1.0e-12) { - MITC4DrillingStabilizationResult result; - result.local_with_drilling = local_without_drilling; - result.drilling_stiffness_scale = drilling_stiffness_scale; - if (!isFinite(drilling_stiffness_scale) || drilling_stiffness_scale < 0.0) { - result.diagnostics.push_back( - mitc4Diagnostic("FESA-MITC4-DRILLING-SCALE", "MITC4 drilling stiffness scale must be non-negative and finite")); - return result; - } - - result.reference_diagonal = mitc4MinimumPositivePhysicalLocalDiagonal(local_without_drilling, tolerance); - if (!isFinite(result.reference_diagonal)) { - result.diagnostics.push_back(mitc4Diagnostic("FESA-MITC4-DRILLING-REFERENCE", - "MITC4 drilling stiffness reference diagonal is not positive")); - return result; - } - - result.drilling_stiffness = drilling_stiffness_scale * result.reference_diagonal; - for (LocalIndex node = 0; node < 4; ++node) { - const LocalIndex gamma = 6 * node + 5; - result.local_with_drilling.add(gamma, gamma, result.drilling_stiffness); - } - return result; -} - -inline DenseMatrix mitc4TransformLocalStiffnessToGlobal(const DenseMatrix& local_stiffness, - const DenseMatrix& local_dof_transform) { - DenseMatrix global(24, 24); - for (LocalIndex i = 0; i < 24; ++i) { - for (LocalIndex j = 0; j < 24; ++j) { - Real value = 0.0; - for (LocalIndex a = 0; a < 24; ++a) { - for (LocalIndex b = 0; b < 24; ++b) { - value += local_dof_transform(a, i) * local_stiffness(a, b) * local_dof_transform(b, j); - } - } - global(i, j) = value; - } - } - return global; -} - -inline MITC4ElementStiffnessResult mitc4ElementStiffness(const std::array& coordinates, - Real elastic_modulus, Real poisson_ratio, Real thickness, - ElementStiffnessOptions options = {}) { - MITC4ElementStiffnessResult result; - result.local_without_drilling = DenseMatrix(24, 24); - result.local_with_drilling = DenseMatrix(24, 24); - result.global = DenseMatrix(24, 24); - result.drilling_stiffness_scale = options.drilling_stiffness_scale; - - const MITC4Geometry geometry = buildMITC4Geometry(coordinates, thickness); - appendDiagnostics(result.diagnostics, geometry.diagnostics); - if (hasError(result.diagnostics)) { - return result; - } - - const MITC4MaterialIntegrationData integration = - mitc4BuildMaterialIntegrationData(geometry, elastic_modulus, poisson_ratio); - appendDiagnostics(result.diagnostics, integration.diagnostics); - if (hasError(result.diagnostics)) { - return result; - } - result.integration_point_count = integration.samples.size(); - - const DenseMatrix local_dof_transform = mitc4LocalDofTransform(geometry); - for (const MITC4MaterialIntegrationSample& sample : integration.samples) { - const MITC4StrainRows local_rows = - mitc4TransformStrainRowsToLocalDofs(sample.strain_rows, local_dof_transform); - appendDiagnostics(result.diagnostics, local_rows.diagnostics); - if (hasError(result.diagnostics)) { - return result; - } - const Real factor = std::fabs(sample.basis.jacobian) * sample.point.weight; - accumulateMITC4BtDB(result.local_without_drilling, local_rows, sample.convected_material, factor); - } - - const auto drilling = mitc4ApplyDrillingStabilization(result.local_without_drilling, options.drilling_stiffness_scale); - appendDiagnostics(result.diagnostics, drilling.diagnostics); - result.local_with_drilling = drilling.local_with_drilling; - result.drilling_reference_diagonal = drilling.reference_diagonal; - result.drilling_stiffness = drilling.drilling_stiffness; - result.drilling_stiffness_scale = drilling.drilling_stiffness_scale; - if (hasError(result.diagnostics)) { - return result; - } - - result.global = mitc4TransformLocalStiffnessToGlobal(result.local_with_drilling, local_dof_transform); - return result; -} - -inline std::vector mitc4ElementInternalForce(const MITC4ElementStiffnessResult& stiffness, - const std::vector& element_displacement) { - if (stiffness.global.rows() != static_cast(element_displacement.size())) { - throw std::runtime_error("MITC4 internal force size mismatch"); - } - return stiffness.global.multiply(element_displacement); -} - -class MITC4ElementKernel { - public: - DenseMatrix stiffness(const std::array& coordinates, Real elastic_modulus, Real poisson_ratio, Real thickness, - ElementStiffnessOptions options = {}) const { - const auto result = mitc4ElementStiffness(coordinates, elastic_modulus, poisson_ratio, thickness, options); - if (!result.ok()) { - throw std::runtime_error(result.diagnostics.empty() ? "invalid MITC4 stiffness" - : result.diagnostics.front().message); - } - return result.global; - } -}; - -} // namespace fesa diff --git a/include/fesa/IO/AbaqusInputParser.hpp b/include/fesa/IO/AbaqusInputParser.hpp deleted file mode 100644 index a160ac0..0000000 --- a/include/fesa/IO/AbaqusInputParser.hpp +++ /dev/null @@ -1,542 +0,0 @@ -#pragma once - -#include "fesa/Core/Core.hpp" -#include "fesa/Util/Util.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace fesa { - -struct KeywordLine { - std::string name; - std::map parameters; - std::set flags; -}; - -inline KeywordLine parseKeywordLine(const std::string& line) { - KeywordLine keyword; - std::vector pieces = splitCsv(line.substr(1)); - if (pieces.empty()) { - return keyword; - } - keyword.name = lower(trim(pieces.front())); - for (std::size_t i = 1; i < pieces.size(); ++i) { - const std::string piece = trim(pieces[i]); - if (piece.empty()) { - continue; - } - const auto eq = piece.find('='); - if (eq == std::string::npos) { - keyword.flags.insert(lower(piece)); - } else { - keyword.parameters[lower(trim(piece.substr(0, eq)))] = trim(piece.substr(eq + 1)); - } - } - return keyword; -} - -struct ParseResult { - Domain domain; - std::vector diagnostics; - - bool ok() const { - return !hasError(diagnostics); - } -}; - -class AbaqusInputParser { - public: - ParseResult parseString(const std::string& text, const std::string& file_name = "") const { - ParseResult result; - std::istringstream stream(text); - std::string line; - KeywordLine current; - std::string current_material_key; - KeywordLine current_shell_section; - LocalIndex line_number = 0; - LocalIndex current_keyword_line = 0; - - auto add_error = [&](const std::string& code, const std::string& message) { - const LocalIndex source_line = current_keyword_line == 0 ? line_number : current_keyword_line; - result.diagnostics.push_back({Severity::Error, code, message, {file_name, source_line, current.name}}); - }; - auto is_allowed = [](const std::string& value, std::initializer_list allowed_values) { - return std::any_of(allowed_values.begin(), allowed_values.end(), [&](const char* allowed) { - return value == allowed; - }); - }; - auto reject_unsupported_controls = [&](std::initializer_list allowed_parameters, - std::initializer_list allowed_flags) { - for (const auto& [parameter, value] : current.parameters) { - (void)value; - if (!is_allowed(parameter, allowed_parameters)) { - const LocalIndex source_line = current_keyword_line == 0 ? line_number : current_keyword_line; - result.diagnostics.push_back({Severity::Error, - "FESA-PARSE-UNSUPPORTED-PARAMETER", - "Unsupported *" + current.name + " parameter: " + parameter, - {file_name, source_line, current.name}}); - } - } - for (const std::string& flag : current.flags) { - if (!is_allowed(flag, allowed_flags)) { - const LocalIndex source_line = current_keyword_line == 0 ? line_number : current_keyword_line; - result.diagnostics.push_back({Severity::Error, - "FESA-PARSE-UNSUPPORTED-PARAMETER", - "Unsupported *" + current.name + " flag: " + flag, - {file_name, source_line, current.name}}); - } - } - }; - - while (std::getline(stream, line)) { - ++line_number; - line = trim(line); - if (line.empty() || line.rfind("**", 0) == 0) { - continue; - } - if (!line.empty() && line.front() == '*') { - current_keyword_line = line_number; - std::string keyword_line = line; - while (!keyword_line.empty() && keyword_line.back() == ',') { - std::string continuation; - if (!std::getline(stream, continuation)) { - break; - } - ++line_number; - continuation = trim(continuation); - if (continuation.empty() || continuation.rfind("**", 0) == 0) { - continue; - } - keyword_line += continuation; - } - current = parseKeywordLine(keyword_line); - if (current.name == "node") { - reject_unsupported_controls({}, {}); - continue; - } - if (current.name == "element") { - reject_unsupported_controls({"type", "elset"}, {}); - continue; - } - if (current.name == "nset") { - reject_unsupported_controls({"nset"}, {"generate"}); - continue; - } - if (current.name == "elset") { - reject_unsupported_controls({"elset"}, {"generate"}); - continue; - } - if (current.name == "elastic") { - reject_unsupported_controls({}, {}); - continue; - } - if (current.name == "shell section") { - reject_unsupported_controls({"elset", "material"}, {}); - current_shell_section = current; - continue; - } - if (current.name == "boundary" || current.name == "cload" || current.name == "static") { - reject_unsupported_controls({}, {}); - continue; - } - if (current.name == "material") { - reject_unsupported_controls({"name"}, {}); - auto name_it = current.parameters.find("name"); - if (name_it == current.parameters.end() || trim(name_it->second).empty()) { - add_error("FESA-PARSE-MATERIAL-NAME", "*Material requires NAME"); - current_material_key.clear(); - continue; - } - Material material; - material.name = trim(name_it->second); - current_material_key = Domain::key(material.name); - if (result.domain.materials.count(current_material_key) != 0) { - add_error("FESA-PARSE-DUPLICATE-MATERIAL", "Duplicate material: " + material.name); - } else { - result.domain.materials[current_material_key] = material; - } - continue; - } - if (current.name == "step") { - reject_unsupported_controls({"name", "nlgeom"}, {}); - auto nlgeom = current.parameters.find("nlgeom"); - if (nlgeom != current.parameters.end() && lower(trim(nlgeom->second)) == "yes") { - add_error("FESA-PARSE-UNSUPPORTED-NLGEOM", "NLGEOM=YES is not supported in Phase 1"); - } - StepDefinition step; - auto name_it = current.parameters.find("name"); - if (name_it != current.parameters.end() && !trim(name_it->second).empty()) { - step.name = trim(name_it->second); - } - result.domain.steps.push_back(step); - continue; - } - if (current.name == "end step") { - reject_unsupported_controls({}, {}); - continue; - } - add_error("FESA-PARSE-UNSUPPORTED-KEYWORD", "Unsupported keyword: *" + current.name); - continue; - } - - const std::vector fields = splitCsv(line); - if (current.name == "node") { - parseNode(fields, result, file_name, line_number); - } else if (current.name == "element") { - parseElement(fields, current, result, file_name, line_number); - } else if (current.name == "nset") { - parseNodeSet(fields, current, result, file_name, line_number); - } else if (current.name == "elset") { - parseElementSet(fields, current, result, file_name, line_number); - } else if (current.name == "elastic") { - parseElastic(fields, current_material_key, result, file_name, line_number); - } else if (current.name == "shell section") { - parseShellSection(fields, current_shell_section, result, file_name, line_number); - } else if (current.name == "boundary") { - parseBoundary(fields, result, file_name, line_number); - } else if (current.name == "cload") { - parseLoad(fields, result, file_name, line_number); - } - } - - if (result.domain.steps.empty()) { - result.domain.steps.push_back({"Step-1", "linear_static"}); - } - return result; - } - - ParseResult parseFile(const std::string& path) const { - std::ifstream input(path); - std::ostringstream buffer; - buffer << input.rdbuf(); - ParseResult result = parseString(buffer.str(), path); - if (!input.good() && buffer.str().empty()) { - result.diagnostics.push_back({Severity::Error, "FESA-PARSE-FILE", "Could not read input file", {path, 0, ""}}); - } - return result; - } - - private: - static std::size_t effectiveFieldCount(const std::vector& fields) { - std::size_t count = fields.size(); - while (count > 0 && trim(fields[count - 1]).empty()) { - --count; - } - return count; - } - - static void parseNode(const std::vector& fields, - ParseResult& result, - const std::string& file_name, - LocalIndex line) { - if (effectiveFieldCount(fields) != 4) { - result.diagnostics.push_back( - {Severity::Error, "FESA-PARSE-NODE", "*Node data requires id,x,y,z", {file_name, line, "node"}}); - return; - } - auto id = parseInt64(fields[0]); - auto x = parseReal(fields[1]); - auto y = parseReal(fields[2]); - auto z = parseReal(fields[3]); - if (!id || !x || !y || !z) { - result.diagnostics.push_back( - {Severity::Error, "FESA-PARSE-NODE-NUMERIC", "Invalid node numeric field", {file_name, line, "node"}}); - return; - } - if (result.domain.nodes.count(*id) != 0) { - result.diagnostics.push_back( - {Severity::Error, "FESA-PARSE-DUPLICATE-NODE", "Duplicate node id", {file_name, line, "node"}}); - return; - } - result.domain.nodes[*id] = {*id, {*x, *y, *z}}; - } - - static void parseElement(const std::vector& fields, - const KeywordLine& keyword, - ParseResult& result, - const std::string& file_name, - LocalIndex line) { - auto type_it = keyword.parameters.find("type"); - if (type_it == keyword.parameters.end()) { - result.diagnostics.push_back( - {Severity::Error, "FESA-PARSE-ELEMENT-TYPE", "*Element requires TYPE", {file_name, line, "element"}}); - return; - } - const std::string type = lower(trim(type_it->second)); - if (type != "s4") { - result.diagnostics.push_back({Severity::Error, - "FESA-PARSE-UNSUPPORTED-ELEMENT", - "Unsupported element type: " + type_it->second, - {file_name, line, "element"}}); - return; - } - if (effectiveFieldCount(fields) != 5) { - result.diagnostics.push_back({Severity::Error, - "FESA-PARSE-ELEMENT", - "S4 element requires id,n1,n2,n3,n4", - {file_name, line, "element"}}); - return; - } - auto id = parseInt64(fields[0]); - std::array nodes{}; - bool ok = id.has_value(); - for (int i = 0; i < 4; ++i) { - auto node = parseInt64(fields[1 + static_cast(i)]); - ok = ok && node.has_value(); - if (node) { - nodes[static_cast(i)] = *node; - } - } - if (!ok) { - result.diagnostics.push_back( - {Severity::Error, "FESA-PARSE-ELEMENT-NUMERIC", "Invalid element numeric field", {file_name, line, "element"}}); - return; - } - if (result.domain.elements.count(*id) != 0) { - result.diagnostics.push_back( - {Severity::Error, "FESA-PARSE-DUPLICATE-ELEMENT", "Duplicate element id", {file_name, line, "element"}}); - return; - } - Element element; - element.id = *id; - element.node_ids = nodes; - auto elset_it = keyword.parameters.find("elset"); - if (elset_it != keyword.parameters.end()) { - element.source_elset = trim(elset_it->second); - auto& set = result.domain.element_sets[Domain::key(element.source_elset)]; - set.name = element.source_elset; - addUnique(set.element_ids, *id); - } - result.domain.elements[*id] = element; - } - - static void parseNodeSet(const std::vector& fields, - const KeywordLine& keyword, - ParseResult& result, - const std::string& file_name, - LocalIndex line) { - auto name_it = keyword.parameters.find("nset"); - if (name_it == keyword.parameters.end()) { - result.diagnostics.push_back( - {Severity::Error, "FESA-PARSE-NSET-NAME", "*Nset requires NSET", {file_name, line, "nset"}}); - return; - } - auto& set = result.domain.node_sets[Domain::key(name_it->second)]; - set.name = trim(name_it->second); - parseSetData(fields, keyword.flags.count("generate") != 0, set.node_ids, result.diagnostics, file_name, line, "nset"); - } - - static void parseElementSet(const std::vector& fields, - const KeywordLine& keyword, - ParseResult& result, - const std::string& file_name, - LocalIndex line) { - auto name_it = keyword.parameters.find("elset"); - if (name_it == keyword.parameters.end()) { - result.diagnostics.push_back( - {Severity::Error, "FESA-PARSE-ELSET-NAME", "*Elset requires ELSET", {file_name, line, "elset"}}); - return; - } - auto& set = result.domain.element_sets[Domain::key(name_it->second)]; - set.name = trim(name_it->second); - parseSetData(fields, keyword.flags.count("generate") != 0, set.element_ids, result.diagnostics, file_name, line, "elset"); - } - - static void parseSetData(const std::vector& fields, - bool generate, - std::vector& output, - std::vector& diagnostics, - const std::string& file_name, - LocalIndex line, - const std::string& keyword) { - if (generate) { - const std::size_t field_count = effectiveFieldCount(fields); - if (field_count != 3) { - diagnostics.push_back({Severity::Error, - "FESA-PARSE-GENERATE", - "Generated set requires first,last,increment", - {file_name, line, keyword}}); - return; - } - auto first = parseInt64(fields[0]); - auto last = parseInt64(fields[1]); - auto increment = parseInt64(fields[2]); - if (!first || !last || !increment || *increment <= 0) { - diagnostics.push_back( - {Severity::Error, "FESA-PARSE-GENERATE", "Invalid generated set range", {file_name, line, keyword}}); - return; - } - for (GlobalId value : generatedRange(*first, *last, *increment)) { - addUnique(output, value); - } - return; - } - const std::size_t field_count = effectiveFieldCount(fields); - for (std::size_t i = 0; i < field_count; ++i) { - const std::string& field = fields[i]; - if (trim(field).empty()) { - continue; - } - auto value = parseInt64(field); - if (!value) { - diagnostics.push_back( - {Severity::Error, "FESA-PARSE-SET-NUMERIC", "Invalid set id", {file_name, line, keyword}}); - return; - } - addUnique(output, *value); - } - } - - static void parseElastic(const std::vector& fields, - const std::string& material_key, - ParseResult& result, - const std::string& file_name, - LocalIndex line) { - if (material_key.empty() || result.domain.materials.count(material_key) == 0) { - result.diagnostics.push_back( - {Severity::Error, "FESA-PARSE-ELASTIC-MATERIAL", "*Elastic must follow *Material", {file_name, line, "elastic"}}); - return; - } - const std::size_t field_count = effectiveFieldCount(fields); - if (field_count < 2) { - result.diagnostics.push_back( - {Severity::Error, "FESA-PARSE-ELASTIC", "*Elastic requires E,nu", {file_name, line, "elastic"}}); - return; - } - if (field_count > 2) { - result.diagnostics.push_back({Severity::Error, - "FESA-PARSE-ELASTIC-UNSUPPORTED", - "Only isotropic E,nu elastic data is supported", - {file_name, line, "elastic"}}); - return; - } - auto e = parseReal(fields[0]); - auto nu = parseReal(fields[1]); - if (!e || !nu || *e <= 0.0 || *nu <= -1.0 || *nu >= 0.5) { - result.diagnostics.push_back({Severity::Error, - "FESA-PARSE-ELASTIC-RANGE", - "Invalid isotropic elastic constants", - {file_name, line, "elastic"}}); - return; - } - result.domain.materials[material_key].elastic_modulus = *e; - result.domain.materials[material_key].poisson_ratio = *nu; - } - - static void parseShellSection(const std::vector& fields, - const KeywordLine& keyword, - ParseResult& result, - const std::string& file_name, - LocalIndex line) { - auto elset_it = keyword.parameters.find("elset"); - auto material_it = keyword.parameters.find("material"); - if (elset_it == keyword.parameters.end() || material_it == keyword.parameters.end()) { - result.diagnostics.push_back({Severity::Error, - "FESA-PARSE-SHELL-SECTION-PARAM", - "*Shell Section requires ELSET and MATERIAL", - {file_name, line, "shell section"}}); - return; - } - const std::size_t field_count = effectiveFieldCount(fields); - if (field_count == 0) { - result.diagnostics.push_back({Severity::Error, - "FESA-PARSE-SHELL-SECTION", - "*Shell Section requires thickness", - {file_name, line, "shell section"}}); - return; - } - if (field_count > 1) { - result.diagnostics.push_back({Severity::Error, - "FESA-PARSE-SHELL-SECTION-UNSUPPORTED", - "Only homogeneous shell thickness data is supported", - {file_name, line, "shell section"}}); - return; - } - auto thickness = parseReal(fields[0]); - if (!thickness || *thickness <= 0.0) { - result.diagnostics.push_back({Severity::Error, - "FESA-PARSE-SHELL-THICKNESS", - "Shell thickness must be positive", - {file_name, line, "shell section"}}); - return; - } - result.domain.shell_sections.push_back({trim(elset_it->second), trim(material_it->second), *thickness}); - } - - static void parseBoundary(const std::vector& fields, - ParseResult& result, - const std::string& file_name, - LocalIndex line) { - const std::size_t field_count = effectiveFieldCount(fields); - if (field_count < 2) { - result.diagnostics.push_back( - {Severity::Error, "FESA-PARSE-BOUNDARY", "*Boundary requires target,first_dof", {file_name, line, "boundary"}}); - return; - } - if (field_count > 4) { - result.diagnostics.push_back({Severity::Error, - "FESA-PARSE-BOUNDARY-UNSUPPORTED", - "Only direct zero-valued boundary data is supported", - {file_name, line, "boundary"}}); - return; - } - auto first = parseInt64(fields[1]); - auto last = field_count >= 3 && !fields[2].empty() ? parseInt64(fields[2]) : first; - auto magnitude = field_count >= 4 && !fields[3].empty() ? parseReal(fields[3]) : std::optional(0.0); - if (!first || !last || !magnitude || !dofFromAbaqus(static_cast(*first)) || - !dofFromAbaqus(static_cast(*last)) || *first > *last) { - result.diagnostics.push_back({Severity::Error, - "FESA-PARSE-BOUNDARY-DOF", - "Invalid boundary DOF range", - {file_name, line, "boundary"}}); - return; - } - if (std::fabs(*magnitude) > 0.0) { - result.diagnostics.push_back({Severity::Error, - "FESA-PARSE-BOUNDARY-NONZERO", - "Nonzero prescribed displacement is not supported in Phase 1", - {file_name, line, "boundary"}}); - return; - } - result.domain.boundary_conditions.push_back({trim(fields[0]), static_cast(*first), static_cast(*last), *magnitude}); - } - - static void parseLoad(const std::vector& fields, - ParseResult& result, - const std::string& file_name, - LocalIndex line) { - const std::size_t field_count = effectiveFieldCount(fields); - if (field_count < 3) { - result.diagnostics.push_back( - {Severity::Error, "FESA-PARSE-CLOAD", "*Cload requires target,dof,magnitude", {file_name, line, "cload"}}); - return; - } - if (field_count > 3) { - result.diagnostics.push_back({Severity::Error, - "FESA-PARSE-CLOAD-UNSUPPORTED", - "Only direct concentrated load data is supported", - {file_name, line, "cload"}}); - return; - } - auto dof = parseInt64(fields[1]); - auto magnitude = parseReal(fields[2]); - if (!dof || !magnitude || !dofFromAbaqus(static_cast(*dof))) { - result.diagnostics.push_back( - {Severity::Error, "FESA-PARSE-CLOAD-DOF", "Invalid concentrated load", {file_name, line, "cload"}}); - return; - } - result.domain.loads.push_back({trim(fields[0]), static_cast(*dof), *magnitude}); - } -}; - -} // namespace fesa diff --git a/include/fesa/IO/IO.hpp b/include/fesa/IO/IO.hpp deleted file mode 100644 index 2a440ea..0000000 --- a/include/fesa/IO/IO.hpp +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -#include "fesa/IO/AbaqusInputParser.hpp" -#include "fesa/ModuleInfo.hpp" - -namespace fesa::module { - -inline constexpr std::string_view kIO = "IO"; - -} // namespace fesa::module diff --git a/include/fesa/Load/Load.hpp b/include/fesa/Load/Load.hpp deleted file mode 100644 index 6e4ef67..0000000 --- a/include/fesa/Load/Load.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include "fesa/Core/Types.hpp" -#include "fesa/ModuleInfo.hpp" - -#include - -namespace fesa::module { - -inline constexpr std::string_view kLoad = "Load"; - -} // namespace fesa::module - -namespace fesa { - -struct NodalLoad { - std::string target; - int dof = 0; - Real magnitude = 0.0; -}; - -} // namespace fesa diff --git a/include/fesa/Material/MITC4PlaneStressMaterial.hpp b/include/fesa/Material/MITC4PlaneStressMaterial.hpp deleted file mode 100644 index a0a7dce..0000000 --- a/include/fesa/Material/MITC4PlaneStressMaterial.hpp +++ /dev/null @@ -1,121 +0,0 @@ -#pragma once - -#include "fesa/Math/Vector.hpp" -#include "fesa/Util/Diagnostics.hpp" - -#include -#include -#include -#include -#include - -namespace fesa { - -using MITC4StrainVector = std::array; -using MITC4MaterialMatrix = std::array, 6>; - -enum class MITC4StrainComponent { - Eps11 = 0, - Eps22 = 1, - Eps33 = 2, - Gamma23 = 3, - Gamma13 = 4, - Gamma12 = 5 -}; - -inline std::size_t strainComponentIndex(MITC4StrainComponent component) { - return static_cast(component); -} - -inline std::array mitc4StrainComponentLabels() { - return {"eps11", "eps22", "eps33", "gamma23", "gamma13", "gamma12"}; -} - -struct MITC4MaterialMatrixEvaluation { - MITC4MaterialMatrix matrix{}; - std::vector diagnostics; - - bool ok() const { - return !hasError(diagnostics); - } -}; - -inline Diagnostic mitc4MaterialDiagnostic(std::string code, std::string message) { - return makeDiagnostic(Severity::Error, std::move(code), std::move(message), "mitc4", "", 0); -} - -inline MITC4MaterialMatrixEvaluation mitc4PlaneStressMaterialMatrix(Real elastic_modulus, Real poisson_ratio, - Real shear_correction = 5.0 / 6.0, - Real tolerance = 1.0e-12) { - MITC4MaterialMatrixEvaluation result; - if (!isFinite(elastic_modulus) || elastic_modulus <= tolerance) { - result.diagnostics.push_back( - mitc4MaterialDiagnostic("FESA-MITC4-MATERIAL", "MITC4 elastic modulus must be positive and finite")); - } - if (!isFinite(poisson_ratio) || poisson_ratio <= -1.0 || poisson_ratio >= 0.5) { - result.diagnostics.push_back( - mitc4MaterialDiagnostic("FESA-MITC4-POISSON", "MITC4 isotropic Poisson ratio must satisfy -1 < nu < 0.5")); - } - if (!isFinite(shear_correction) || shear_correction <= tolerance) { - result.diagnostics.push_back(mitc4MaterialDiagnostic("FESA-MITC4-SHEAR-CORRECTION", - "MITC4 shear correction factor must be positive and finite")); - } - if (hasError(result.diagnostics)) { - return result; - } - - const Real scale = elastic_modulus / (1.0 - poisson_ratio * poisson_ratio); - const Real shear_modulus = elastic_modulus / (2.0 * (1.0 + poisson_ratio)); - const std::size_t eps11 = strainComponentIndex(MITC4StrainComponent::Eps11); - const std::size_t eps22 = strainComponentIndex(MITC4StrainComponent::Eps22); - const std::size_t gamma23 = strainComponentIndex(MITC4StrainComponent::Gamma23); - const std::size_t gamma13 = strainComponentIndex(MITC4StrainComponent::Gamma13); - const std::size_t gamma12 = strainComponentIndex(MITC4StrainComponent::Gamma12); - - result.matrix[eps11][eps11] = scale; - result.matrix[eps11][eps22] = poisson_ratio * scale; - result.matrix[eps22][eps11] = poisson_ratio * scale; - result.matrix[eps22][eps22] = scale; - result.matrix[gamma23][gamma23] = shear_correction * shear_modulus; - result.matrix[gamma13][gamma13] = shear_correction * shear_modulus; - result.matrix[gamma12][gamma12] = shear_modulus; - return result; -} - -inline MITC4StrainVector multiplyMITC4MaterialMatrix(const MITC4MaterialMatrix& matrix, - const MITC4StrainVector& vector) { - MITC4StrainVector result{}; - for (std::size_t row = 0; row < 6; ++row) { - for (std::size_t col = 0; col < 6; ++col) { - result[row] += matrix[row][col] * vector[col]; - } - } - return result; -} - -inline Real dotMITC4Vector(const MITC4StrainVector& a, const MITC4StrainVector& b) { - Real result = 0.0; - for (std::size_t i = 0; i < 6; ++i) { - result += a[i] * b[i]; - } - return result; -} - -inline MITC4MaterialMatrix mitc4TransformMaterialMatrix(const MITC4MaterialMatrix& local_material, - const MITC4MaterialMatrix& covariant_to_local) { - MITC4MaterialMatrix result{}; - for (std::size_t i = 0; i < 6; ++i) { - for (std::size_t j = 0; j < 6; ++j) { - Real value = 0.0; - for (std::size_t a = 0; a < 6; ++a) { - for (std::size_t b = 0; b < 6; ++b) { - value += covariant_to_local[a][i] * local_material[a][b] * covariant_to_local[b][j]; - } - } - result[i][j] = value; - } - } - return result; -} - -} // namespace fesa diff --git a/include/fesa/Material/Material.hpp b/include/fesa/Material/Material.hpp deleted file mode 100644 index 49713c3..0000000 --- a/include/fesa/Material/Material.hpp +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -#include "fesa/Material/MITC4PlaneStressMaterial.hpp" -#include "fesa/ModuleInfo.hpp" - -namespace fesa::module { - -inline constexpr std::string_view kMaterial = "Material"; - -} // namespace fesa::module diff --git a/include/fesa/Math/DenseMatrix.hpp b/include/fesa/Math/DenseMatrix.hpp deleted file mode 100644 index 56da9f2..0000000 --- a/include/fesa/Math/DenseMatrix.hpp +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once - -#include "fesa/Core/Types.hpp" - -#include -#include - -namespace fesa { - -class DenseMatrix { - public: - DenseMatrix() = default; - DenseMatrix(LocalIndex rows, LocalIndex cols) : rows_(rows), cols_(cols), values_(static_cast(rows * cols), 0.0) {} - - LocalIndex rows() const { - return rows_; - } - - LocalIndex cols() const { - return cols_; - } - - Real& operator()(LocalIndex row, LocalIndex col) { - return values_[static_cast(row * cols_ + col)]; - } - - Real operator()(LocalIndex row, LocalIndex col) const { - return values_[static_cast(row * cols_ + col)]; - } - - void add(LocalIndex row, LocalIndex col, Real value) { - (*this)(row, col) += value; - } - - std::vector multiply(const std::vector& x) const { - std::vector y(static_cast(rows_), 0.0); - for (LocalIndex i = 0; i < rows_; ++i) { - Real sum = 0.0; - for (LocalIndex j = 0; j < cols_; ++j) { - sum += (*this)(i, j) * x[static_cast(j)]; - } - y[static_cast(i)] = sum; - } - return y; - } - - private: - LocalIndex rows_ = 0; - LocalIndex cols_ = 0; - std::vector values_; -}; - -} // namespace fesa diff --git a/include/fesa/Math/LinearSolver.hpp b/include/fesa/Math/LinearSolver.hpp deleted file mode 100644 index 63b0204..0000000 --- a/include/fesa/Math/LinearSolver.hpp +++ /dev/null @@ -1,81 +0,0 @@ -#pragma once - -#include "fesa/Math/DenseMatrix.hpp" -#include "fesa/Util/Diagnostics.hpp" - -#include -#include -#include -#include -#include - -namespace fesa { - -struct SolveResult { - std::vector x; - std::vector diagnostics; - - bool ok() const { - return !hasError(diagnostics); - } -}; - -class LinearSolver { - public: - virtual ~LinearSolver() = default; - virtual SolveResult solve(DenseMatrix a, std::vector b) const = 0; -}; - -class GaussianEliminationSolver final : public LinearSolver { - public: - SolveResult solve(DenseMatrix a, std::vector b) const override { - const LocalIndex n = a.rows(); - SolveResult result; - if (a.rows() != a.cols() || static_cast(b.size()) != n) { - result.diagnostics.push_back(makeDiagnostic(Severity::Error, "FESA-SOLVER-SIZE", "Linear system size mismatch", "solver")); - return result; - } - for (LocalIndex col = 0; col < n; ++col) { - LocalIndex pivot = col; - Real pivot_abs = std::fabs(a(col, col)); - for (LocalIndex row = col + 1; row < n; ++row) { - const Real candidate = std::fabs(a(row, col)); - if (candidate > pivot_abs) { - pivot_abs = candidate; - pivot = row; - } - } - if (pivot_abs < 1.0e-12) { - result.diagnostics.push_back(makeDiagnostic(Severity::Error, "FESA-SINGULAR-SOLVER", - "Reduced system is singular or ill-conditioned", "solver")); - return result; - } - if (pivot != col) { - for (LocalIndex j = col; j < n; ++j) { - std::swap(a(col, j), a(pivot, j)); - } - std::swap(b[static_cast(col)], b[static_cast(pivot)]); - } - const Real diag = a(col, col); - for (LocalIndex row = col + 1; row < n; ++row) { - const Real factor = a(row, col) / diag; - a(row, col) = 0.0; - for (LocalIndex j = col + 1; j < n; ++j) { - a(row, j) -= factor * a(col, j); - } - b[static_cast(row)] -= factor * b[static_cast(col)]; - } - } - result.x.assign(static_cast(n), 0.0); - for (LocalIndex i = n; i-- > 0;) { - Real sum = b[static_cast(i)]; - for (LocalIndex j = i + 1; j < n; ++j) { - sum -= a(i, j) * result.x[static_cast(j)]; - } - result.x[static_cast(i)] = sum / a(i, i); - } - return result; - } -}; - -} // namespace fesa diff --git a/include/fesa/Math/Math.hpp b/include/fesa/Math/Math.hpp deleted file mode 100644 index 5e2c25e..0000000 --- a/include/fesa/Math/Math.hpp +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include "fesa/Math/DenseMatrix.hpp" -#include "fesa/Math/LinearSolver.hpp" -#include "fesa/Math/SparsePattern.hpp" -#include "fesa/Math/Vector.hpp" -#include "fesa/ModuleInfo.hpp" - -namespace fesa::module { - -inline constexpr std::string_view kMath = "Math"; - -} // namespace fesa::module diff --git a/include/fesa/Math/SparsePattern.hpp b/include/fesa/Math/SparsePattern.hpp deleted file mode 100644 index c2c49f1..0000000 --- a/include/fesa/Math/SparsePattern.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include "fesa/Core/Types.hpp" - -#include -#include - -namespace fesa { - -struct SparsePatternEntry { - EquationId row = 0; - EquationId col = 0; -}; - -struct SparsePattern { - EquationId equation_count = 0; - std::vector entries; - - SparseIndex nonzeroCount() const { - return static_cast(entries.size()); - } - - bool contains(EquationId row, EquationId col) const { - return std::any_of(entries.begin(), entries.end(), [&](const SparsePatternEntry& entry) { - return entry.row == row && entry.col == col; - }); - } -}; - -} // namespace fesa diff --git a/include/fesa/Math/Vector.hpp b/include/fesa/Math/Vector.hpp deleted file mode 100644 index b4be202..0000000 --- a/include/fesa/Math/Vector.hpp +++ /dev/null @@ -1,66 +0,0 @@ -#pragma once - -#include "fesa/Core/Types.hpp" - -#include -#include -#include -#include - -namespace fesa { - -struct Vec3 { - Real x = 0.0; - Real y = 0.0; - Real z = 0.0; -}; - -inline Vec3 operator+(const Vec3& a, const Vec3& b) { - return {a.x + b.x, a.y + b.y, a.z + b.z}; -} - -inline Vec3 operator-(const Vec3& a, const Vec3& b) { - return {a.x - b.x, a.y - b.y, a.z - b.z}; -} - -inline Vec3 operator*(Real scalar, const Vec3& value) { - return {scalar * value.x, scalar * value.y, scalar * value.z}; -} - -inline Real dot(const Vec3& a, const Vec3& b) { - return a.x * b.x + a.y * b.y + a.z * b.z; -} - -inline Vec3 cross(const Vec3& a, const Vec3& b) { - return {a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x}; -} - -inline Real norm(const Vec3& value) { - return std::sqrt(dot(value, value)); -} - -inline bool isFinite(Real value) { - return std::isfinite(value); -} - -inline bool isFinite(const Vec3& value) { - return isFinite(value.x) && isFinite(value.y) && isFinite(value.z); -} - -inline std::optional normalizedIfValid(const Vec3& value, Real tolerance = 1.0e-12) { - const Real length = norm(value); - if (!isFinite(length) || length <= tolerance) { - return std::nullopt; - } - return (1.0 / length) * value; -} - -inline Vec3 normalized(const Vec3& value) { - const Real length = norm(value); - if (length <= std::numeric_limits::epsilon()) { - throw std::runtime_error("zero-length vector"); - } - return (1.0 / length) * value; -} - -} // namespace fesa diff --git a/include/fesa/ModuleInfo.hpp b/include/fesa/ModuleInfo.hpp deleted file mode 100644 index 13b02b4..0000000 --- a/include/fesa/ModuleInfo.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include -#include - -namespace fesa { - -using ModuleList = std::array; - -inline constexpr ModuleList kArchitectureModules = { - "Analysis", "Assembly", "Boundary", "Core", "Element", "IO", - "Load", "Math", "Material", "Property", "Results", "Util", -}; - -constexpr const ModuleList& architectureModules() noexcept { - return kArchitectureModules; -} - -constexpr std::string_view umbrellaFacadeHeader() noexcept { - return "fesa/fesa.hpp"; -} - -} // namespace fesa diff --git a/include/fesa/Property/Property.hpp b/include/fesa/Property/Property.hpp deleted file mode 100644 index 708d42b..0000000 --- a/include/fesa/Property/Property.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include "fesa/Core/Types.hpp" -#include "fesa/ModuleInfo.hpp" - -#include - -namespace fesa::module { - -inline constexpr std::string_view kProperty = "Property"; - -} // namespace fesa::module - -namespace fesa { - -struct ShellSection { - std::string element_set; - std::string material; - Real thickness = 0.0; -}; - -} // namespace fesa diff --git a/include/fesa/Results/ReferenceComparison.hpp b/include/fesa/Results/ReferenceComparison.hpp deleted file mode 100644 index 4745957..0000000 --- a/include/fesa/Results/ReferenceComparison.hpp +++ /dev/null @@ -1,326 +0,0 @@ -#pragma once - -#include "fesa/Results/ResultModel.hpp" -#include "fesa/Util/Util.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace fesa { - -struct CsvDisplacementRow { - GlobalId node_id = 0; - std::array values{}; -}; - -struct CsvDisplacementTable { - std::map rows; - std::vector diagnostics; -}; - -struct CsvReactionRow { - GlobalId node_id = 0; - std::array values{}; -}; - -struct CsvReactionTable { - std::map rows; - std::vector diagnostics; -}; - -inline std::vector displacementCsvRequiredColumns() { - return {"Node Label", "U-U1", "U-U2", "U-U3", "UR-UR1", "UR-UR2", "UR-UR3"}; -} - -inline std::vector reactionCsvRequiredColumns() { - return {"Node Label", "RF-RF1", "RF-RF2", "RF-RF3", "RM-RM1", "RM-RM2", "RM-RM3"}; -} - -inline CsvDisplacementTable loadDisplacementCsvFromStream(std::istream& input, const std::string& source_name) { - CsvDisplacementTable table; - std::string line; - if (!std::getline(input, line)) { - table.diagnostics.push_back({Severity::Error, "FESA-CSV-EMPTY", "Displacement CSV is empty", {source_name, 1, ""}}); - return table; - } - const std::vector required = displacementCsvRequiredColumns(); - std::vector headers = splitCsv(line); - std::map column; - for (std::size_t i = 0; i < headers.size(); ++i) { - column[trim(headers[i])] = i; - } - for (const std::string& name : required) { - if (column.count(name) == 0) { - table.diagnostics.push_back({Severity::Error, "FESA-CSV-MISSING-COLUMN", "Missing CSV column: " + name, {source_name, 1, ""}}); - } - } - if (hasError(table.diagnostics)) { - return table; - } - LocalIndex line_number = 1; - while (std::getline(input, line)) { - ++line_number; - if (trim(line).empty()) { - continue; - } - std::vector fields = splitCsv(line); - auto get = [&](const std::string& name) -> std::string { - const std::size_t index = column[name]; - return index < fields.size() ? fields[index] : ""; - }; - auto node_id = parseInt64(get("Node Label")); - if (!node_id) { - table.diagnostics.push_back({Severity::Error, "FESA-CSV-NODE", "Invalid node label", {source_name, line_number, ""}}); - continue; - } - if (table.rows.count(*node_id) != 0) { - table.diagnostics.push_back({Severity::Error, "FESA-CSV-DUPLICATE-NODE", "Duplicate node label", {source_name, line_number, ""}}); - continue; - } - CsvDisplacementRow row; - row.node_id = *node_id; - for (std::size_t i = 0; i < 6; ++i) { - auto value = parseReal(get(required[i + 1])); - if (!value) { - table.diagnostics.push_back({Severity::Error, "FESA-CSV-NUMERIC", "Invalid displacement value", {source_name, line_number, ""}}); - value = 0.0; - } - row.values[i] = *value; - } - table.rows[*node_id] = row; - } - return table; -} - -inline CsvDisplacementTable loadDisplacementCsvFromString(const std::string& text, const std::string& source_name = "") { - std::istringstream input(text); - return loadDisplacementCsvFromStream(input, source_name); -} - -inline CsvDisplacementTable loadDisplacementCsv(const std::string& path) { - std::ifstream input(path); - if (!input.good()) { - CsvDisplacementTable table; - table.diagnostics.push_back({Severity::Error, "FESA-CSV-READ", "Could not read displacement CSV", {path, 0, ""}}); - return table; - } - return loadDisplacementCsvFromStream(input, path); -} - -inline CsvReactionTable loadReactionCsvFromStream(std::istream& input, const std::string& source_name) { - CsvReactionTable table; - std::string line; - if (!std::getline(input, line)) { - table.diagnostics.push_back({Severity::Error, "FESA-CSV-EMPTY", "Reaction CSV is empty", {source_name, 1, ""}}); - return table; - } - const std::vector required = reactionCsvRequiredColumns(); - std::vector headers = splitCsv(line); - std::map column; - for (std::size_t i = 0; i < headers.size(); ++i) { - column[trim(headers[i])] = i; - } - for (const std::string& name : required) { - if (column.count(name) == 0) { - table.diagnostics.push_back({Severity::Error, "FESA-CSV-MISSING-COLUMN", "Missing CSV column: " + name, {source_name, 1, ""}}); - } - } - if (hasError(table.diagnostics)) { - return table; - } - LocalIndex line_number = 1; - while (std::getline(input, line)) { - ++line_number; - if (trim(line).empty()) { - continue; - } - std::vector fields = splitCsv(line); - auto get = [&](const std::string& name) -> std::string { - const std::size_t index = column[name]; - return index < fields.size() ? fields[index] : ""; - }; - auto node_id = parseInt64(get("Node Label")); - if (!node_id) { - table.diagnostics.push_back({Severity::Error, "FESA-CSV-NODE", "Invalid node label", {source_name, line_number, ""}}); - continue; - } - if (table.rows.count(*node_id) != 0) { - table.diagnostics.push_back({Severity::Error, "FESA-CSV-DUPLICATE-NODE", "Duplicate node label", {source_name, line_number, ""}}); - continue; - } - CsvReactionRow row; - row.node_id = *node_id; - for (std::size_t i = 0; i < 6; ++i) { - auto value = parseReal(get(required[i + 1])); - if (!value) { - table.diagnostics.push_back({Severity::Error, "FESA-CSV-NUMERIC", "Invalid reaction value", {source_name, line_number, ""}}); - value = 0.0; - } - row.values[i] = *value; - } - table.rows[*node_id] = row; - } - return table; -} - -inline CsvReactionTable loadReactionCsvFromString(const std::string& text, const std::string& source_name = "") { - std::istringstream input(text); - return loadReactionCsvFromStream(input, source_name); -} - -inline CsvReactionTable loadReactionCsv(const std::string& path) { - std::ifstream input(path); - if (!input.good()) { - CsvReactionTable table; - table.diagnostics.push_back({Severity::Error, "FESA-CSV-READ", "Could not read reaction CSV", {path, 0, ""}}); - return table; - } - return loadReactionCsvFromStream(input, path); -} - -struct ComparisonOptions { - Real abs_tol = 1.0e-12; - Real rel_tol = 1.0e-5; - Real reference_scale = 1.0; -}; - -struct ComparisonResult { - bool pass = false; - Real max_abs_error = 0.0; - Real max_rel_error = 0.0; - std::vector diagnostics; -}; - -inline ComparisonResult compareDisplacements(const FieldOutput& actual, - const CsvDisplacementTable& expected, - ComparisonOptions options = {}) { - ComparisonResult result; - result.diagnostics = expected.diagnostics; - if (hasError(result.diagnostics)) { - return result; - } - if (actual.name != "U") { - result.diagnostics.push_back({Severity::Error, "FESA-COMPARE-FIELD-NAME", "Expected FESA displacement field named U", {}}); - } - if (actual.component_labels != displacementComponentLabels()) { - result.diagnostics.push_back({Severity::Error, - "FESA-COMPARE-COMPONENT-LABELS", - "FESA U field component labels must be UX,UY,UZ,RX,RY,RZ", - {}}); - } - if (actual.position != "NODAL" || actual.entity_type != "node" || actual.basis != "GLOBAL") { - result.diagnostics.push_back({Severity::Error, "FESA-COMPARE-FIELD-METADATA", "FESA U field must be nodal values in the global basis", {}}); - } - if (actual.entity_ids.size() != actual.values.size()) { - result.diagnostics.push_back({Severity::Error, "FESA-COMPARE-FIELD-SIZE", "FESA U field entity/value counts differ", {}}); - } - std::map> actual_by_node; - const std::size_t actual_count = std::min(actual.entity_ids.size(), actual.values.size()); - for (std::size_t i = 0; i < actual_count; ++i) { - if (actual_by_node.count(actual.entity_ids[i]) != 0) { - result.diagnostics.push_back( - {Severity::Error, "FESA-COMPARE-DUPLICATE-ACTUAL", "FESA U field contains duplicate node " + std::to_string(actual.entity_ids[i]), {}}); - continue; - } - actual_by_node[actual.entity_ids[i]] = actual.values[i]; - } - for (const auto& [node_id, row] : expected.rows) { - auto actual_it = actual_by_node.find(node_id); - if (actual_it == actual_by_node.end()) { - result.diagnostics.push_back({Severity::Error, "FESA-COMPARE-MISSING-ACTUAL", "FESA U field is missing node " + std::to_string(node_id), {}}); - continue; - } - for (std::size_t component = 0; component < 6; ++component) { - const Real expected_value = row.values[component]; - const Real actual_value = actual_it->second[component]; - const Real abs_error = std::fabs(actual_value - expected_value); - const Real scale = std::max(std::fabs(expected_value), std::fabs(options.reference_scale)); - const Real rel_error = scale > 0.0 ? abs_error / scale : (abs_error == 0.0 ? 0.0 : std::numeric_limits::infinity()); - result.max_abs_error = std::max(result.max_abs_error, abs_error); - result.max_rel_error = std::max(result.max_rel_error, rel_error); - if (!(abs_error <= options.abs_tol || rel_error <= options.rel_tol)) { - const std::string component_label = displacementComponentLabels()[component]; - result.diagnostics.push_back({Severity::Error, - "FESA-COMPARE-TOLERANCE", - "Displacement comparison failed at node " + std::to_string(node_id) + " component " + component_label, - {}}); - } - } - } - result.pass = !hasError(result.diagnostics); - return result; -} - -inline ComparisonResult compareReactions(const FieldOutput& actual, - const CsvReactionTable& expected, - ComparisonOptions options = {}) { - ComparisonResult result; - result.diagnostics = expected.diagnostics; - if (hasError(result.diagnostics)) { - return result; - } - if (actual.name != "RF") { - result.diagnostics.push_back({Severity::Error, "FESA-COMPARE-FIELD-NAME", "Expected FESA reaction field named RF", {}}); - } - if (actual.component_labels != reactionComponentLabels()) { - result.diagnostics.push_back({Severity::Error, - "FESA-COMPARE-COMPONENT-LABELS", - "FESA RF field component labels must be RFX,RFY,RFZ,RMX,RMY,RMZ", - {}}); - } - if (actual.position != "NODAL" || actual.entity_type != "node" || actual.basis != "GLOBAL") { - result.diagnostics.push_back({Severity::Error, "FESA-COMPARE-FIELD-METADATA", "FESA RF field must be nodal values in the global basis", {}}); - } - if (actual.entity_ids.size() != actual.values.size()) { - result.diagnostics.push_back({Severity::Error, "FESA-COMPARE-FIELD-SIZE", "FESA RF field entity/value counts differ", {}}); - } - std::map> actual_by_node; - const std::size_t actual_count = std::min(actual.entity_ids.size(), actual.values.size()); - for (std::size_t i = 0; i < actual_count; ++i) { - if (actual_by_node.count(actual.entity_ids[i]) != 0) { - result.diagnostics.push_back( - {Severity::Error, "FESA-COMPARE-DUPLICATE-ACTUAL", "FESA RF field contains duplicate node " + std::to_string(actual.entity_ids[i]), {}}); - continue; - } - actual_by_node[actual.entity_ids[i]] = actual.values[i]; - } - for (const auto& [node_id, row] : expected.rows) { - auto actual_it = actual_by_node.find(node_id); - if (actual_it == actual_by_node.end()) { - result.diagnostics.push_back({Severity::Error, "FESA-COMPARE-MISSING-ACTUAL", "FESA RF field is missing node " + std::to_string(node_id), {}}); - continue; - } - for (std::size_t component = 0; component < 6; ++component) { - const Real expected_value = row.values[component]; - const Real actual_value = actual_it->second[component]; - const Real abs_error = std::fabs(actual_value - expected_value); - const Real scale = std::max(std::fabs(expected_value), std::fabs(options.reference_scale)); - const Real rel_error = scale > 0.0 ? abs_error / scale : (abs_error == 0.0 ? 0.0 : std::numeric_limits::infinity()); - result.max_abs_error = std::max(result.max_abs_error, abs_error); - result.max_rel_error = std::max(result.max_rel_error, rel_error); - if (!(abs_error <= options.abs_tol || rel_error <= options.rel_tol)) { - const std::string component_label = reactionComponentLabels()[component]; - result.diagnostics.push_back({Severity::Error, - "FESA-COMPARE-TOLERANCE", - "Reaction comparison failed at node " + std::to_string(node_id) + " component " + component_label + - " expected=" + std::to_string(expected_value) + - " actual=" + std::to_string(actual_value) + - " abs_error=" + std::to_string(abs_error) + - " rel_error=" + std::to_string(rel_error), - {}}); - } - } - } - result.pass = !hasError(result.diagnostics); - return result; -} - -} // namespace fesa diff --git a/include/fesa/Results/ResultModel.hpp b/include/fesa/Results/ResultModel.hpp deleted file mode 100644 index fc0e5fd..0000000 --- a/include/fesa/Results/ResultModel.hpp +++ /dev/null @@ -1,123 +0,0 @@ -#pragma once - -#include "fesa/Core/Core.hpp" - -#include -#include -#include -#include - -namespace fesa { - -struct FieldOutput { - std::string name; - std::string position = "NODAL"; - std::string entity_type = "node"; - std::string basis = "GLOBAL"; - std::string description; - std::vector entity_ids; - std::vector component_labels; - std::vector> values; -}; - -struct ResultFrame { - LocalIndex frame_id = 0; - LocalIndex increment = 1; - LocalIndex iteration = 0; - Real step_time = 1.0; - Real total_time = 1.0; - bool converged = true; - std::string description = "Phase 1 linear static frame"; - std::map field_outputs; -}; - -struct ResultStep { - std::string name; - std::vector frames; -}; - -struct ResultFile { - std::string schema_name = "FESA_RESULTS"; - LocalIndex schema_version = 1; - std::string solver_name = "FESA"; - std::string dof_convention = "UX,UY,UZ,RX,RY,RZ"; - std::string sign_convention = "Abaqus-compatible"; - std::string precision = "double"; - std::string index_type = "int64"; - std::vector node_ids; - std::vector coordinates; - std::vector element_ids; - std::vector element_types; - std::vector> connectivity; - std::vector steps; -}; - -class InMemoryResultsWriter { - public: - void writeLinearStatic(const Domain& domain, - const DofManager& dofs, - const std::vector& u_full, - const std::vector& rf_full) { - const auto model = buildLinearStaticAnalysisModel(domain); - writeLinearStatic(domain, model, dofs, u_full, rf_full); - } - - void writeLinearStatic(const Domain& domain, - const AnalysisModel& model, - const DofManager& dofs, - const std::vector& u_full, - const std::vector& rf_full) { - result_ = ResultFile{}; - for (const auto& [node_id, node] : domain.nodes) { - result_.node_ids.push_back(node_id); - result_.coordinates.push_back(node.coordinates); - } - for (const auto& [element_id, element] : domain.elements) { - result_.element_ids.push_back(element_id); - result_.element_types.push_back(elementTypeLabel(element.type)); - result_.connectivity.push_back(element.node_ids); - } - ResultStep step; - step.name = model.step.name.empty() ? "Step-1" : model.step.name; - ResultFrame frame; - frame.frame_id = 0; - frame.field_outputs["U"] = buildNodalField("U", displacementComponentLabels(), "Nodal displacement and rotation", domain, dofs, u_full); - frame.field_outputs["RF"] = buildNodalField("RF", reactionComponentLabels(), "Nodal reaction force and moment", domain, dofs, rf_full); - step.frames.push_back(frame); - result_.steps.push_back(step); - } - - const ResultFile& result() const { - return result_; - } - - private: - static FieldOutput buildNodalField(const std::string& name, - const std::vector& labels, - const std::string& description, - const Domain& domain, - const DofManager& dofs, - const std::vector& full_values) { - FieldOutput field; - field.name = name; - field.position = "NODAL"; - field.entity_type = "node"; - field.basis = "GLOBAL"; - field.description = description; - field.component_labels = labels; - for (const auto& [node_id, node] : domain.nodes) { - (void)node; - field.entity_ids.push_back(node_id); - std::array values{}; - for (Dof dof : allDofs()) { - values[static_cast(dofIndex(dof))] = full_values[static_cast(dofs.fullIndex(node_id, dof))]; - } - field.values.push_back(values); - } - return field; - } - - ResultFile result_; -}; - -} // namespace fesa diff --git a/include/fesa/Results/Results.hpp b/include/fesa/Results/Results.hpp deleted file mode 100644 index 712b8b0..0000000 --- a/include/fesa/Results/Results.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include "fesa/ModuleInfo.hpp" -#include "fesa/Results/ResultModel.hpp" -#include "fesa/Results/ReferenceComparison.hpp" - -namespace fesa::module { - -inline constexpr std::string_view kResults = "Results"; - -} // namespace fesa::module diff --git a/include/fesa/Util/Diagnostics.hpp b/include/fesa/Util/Diagnostics.hpp deleted file mode 100644 index 0aa8ff3..0000000 --- a/include/fesa/Util/Diagnostics.hpp +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#include "fesa/Core/Types.hpp" - -#include -#include -#include -#include - -namespace fesa { - -enum class Severity { Info, Warning, Error }; - -struct SourceLocation { - std::string file; - LocalIndex line = 0; - std::string keyword; -}; - -struct Diagnostic { - Severity severity = Severity::Error; - std::string code; - std::string message; - SourceLocation source; -}; - -inline bool hasError(const std::vector& diagnostics) { - return std::any_of(diagnostics.begin(), diagnostics.end(), [](const Diagnostic& diagnostic) { - return diagnostic.severity == Severity::Error; - }); -} - -inline bool containsDiagnostic(const std::vector& diagnostics, const std::string& code) { - return std::any_of(diagnostics.begin(), diagnostics.end(), [&](const Diagnostic& diagnostic) { - return diagnostic.code == code; - }); -} - -inline Diagnostic makeDiagnostic(Severity severity, std::string code, std::string message, std::string keyword, - std::string file = "", LocalIndex line = 0) { - return {severity, std::move(code), std::move(message), {std::move(file), line, std::move(keyword)}}; -} - -} // namespace fesa diff --git a/include/fesa/Util/String.hpp b/include/fesa/Util/String.hpp deleted file mode 100644 index 7d42783..0000000 --- a/include/fesa/Util/String.hpp +++ /dev/null @@ -1,94 +0,0 @@ -#pragma once - -#include "fesa/Core/Types.hpp" - -#include -#include -#include -#include -#include -#include - -namespace fesa { - -inline std::string trim(std::string text) { - auto is_space = [](unsigned char c) { return std::isspace(c) != 0; }; - text.erase(text.begin(), std::find_if(text.begin(), text.end(), [&](unsigned char c) { return !is_space(c); })); - text.erase(std::find_if(text.rbegin(), text.rend(), [&](unsigned char c) { return !is_space(c); }).base(), text.end()); - return text; -} - -inline std::string lower(std::string text) { - std::transform(text.begin(), text.end(), text.begin(), [](unsigned char c) { - return static_cast(std::tolower(c)); - }); - return text; -} - -inline std::vector splitCsv(const std::string& line) { - std::vector fields; - std::string field; - std::istringstream stream(line); - while (std::getline(stream, field, ',')) { - fields.push_back(trim(field)); - } - if (!line.empty() && line.back() == ',') { - fields.emplace_back(); - } - return fields; -} - -inline std::optional parseReal(std::string token) { - token = trim(token); - if (token.empty()) { - return std::nullopt; - } - std::replace(token.begin(), token.end(), 'D', 'E'); - std::replace(token.begin(), token.end(), 'd', 'e'); - try { - std::size_t used = 0; - Real value = std::stod(token, &used); - if (used != token.size()) { - return std::nullopt; - } - return value; - } catch (...) { - return std::nullopt; - } -} - -inline std::optional parseInt64(const std::string& token) { - std::string value_text = trim(token); - if (value_text.empty()) { - return std::nullopt; - } - try { - std::size_t used = 0; - long long value = std::stoll(value_text, &used); - if (used != value_text.size()) { - return std::nullopt; - } - return static_cast(value); - } catch (...) { - return std::nullopt; - } -} - -inline void addUnique(std::vector& values, GlobalId value) { - if (std::find(values.begin(), values.end(), value) == values.end()) { - values.push_back(value); - } -} - -inline std::vector generatedRange(GlobalId first, GlobalId last, GlobalId increment) { - std::vector values; - if (increment <= 0) { - return values; - } - for (GlobalId value = first; value <= last; value += increment) { - values.push_back(value); - } - return values; -} - -} // namespace fesa diff --git a/include/fesa/Util/Util.hpp b/include/fesa/Util/Util.hpp deleted file mode 100644 index cc8994e..0000000 --- a/include/fesa/Util/Util.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include "fesa/Util/Diagnostics.hpp" -#include "fesa/Util/String.hpp" -#include "fesa/ModuleInfo.hpp" - -namespace fesa::module { - -inline constexpr std::string_view kUtil = "Util"; - -} // namespace fesa::module diff --git a/include/fesa/fesa.hpp b/include/fesa/fesa.hpp deleted file mode 100644 index 42cea9a..0000000 --- a/include/fesa/fesa.hpp +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include "fesa/Analysis/Analysis.hpp" -#include "fesa/Assembly/Assembly.hpp" -#include "fesa/Boundary/Boundary.hpp" -#include "fesa/Core/Core.hpp" -#include "fesa/Element/Element.hpp" -#include "fesa/IO/IO.hpp" -#include "fesa/Load/Load.hpp" -#include "fesa/Material/Material.hpp" -#include "fesa/Math/Math.hpp" -#include "fesa/ModuleInfo.hpp" -#include "fesa/Property/Property.hpp" -#include "fesa/Results/Results.hpp" -#include "fesa/Util/Util.hpp" diff --git a/phases/1-linear-static-mitc4-rebaseline/index.json b/phases/1-linear-static-mitc4-rebaseline/index.json deleted file mode 100644 index 0f3f843..0000000 --- a/phases/1-linear-static-mitc4-rebaseline/index.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "project": "FESA", - "phase": "1-linear-static-mitc4-rebaseline", - "steps": [ - { "step": 0, "name": "rebaseline-audit", "status": "completed" }, - { "step": 1, "name": "reference-onboarding", "status": "completed" }, - { "step": 2, "name": "core-harness-guardrails", "status": "completed" }, - { "step": 3, "name": "parser-domain-subset", "status": "completed" }, - { "step": 4, "name": "validation-singular-diagnostics", "status": "completed" }, - { "step": 5, "name": "dof-manager-reaction-foundation", "status": "completed" }, - { "step": 6, "name": "results-comparator-foundation", "status": "completed" }, - { "step": 7, "name": "mitc4-geometry-directors", "status": "completed" }, - { "step": 8, "name": "mitc4-covariant-strain-tying", "status": "completed" }, - { "step": 9, "name": "mitc4-material-integration", "status": "completed" }, - { "step": 10, "name": "mitc4-stiffness-drilling", "status": "completed" }, - { "step": 11, "name": "mitc4-patch-benchmark-tests", "status": "completed" }, - { "step": 12, "name": "assembly-sparse-solver-path", "status": "completed" }, - { "step": 13, "name": "linear-static-workflow", "status": "completed" }, - { "step": 14, "name": "stored-reference-regression", "status": "completed" }, - { "step": 15, "name": "phase1-evaluator-closeout", "status": "completed", "artifact": "step15-evaluator-report.md" } - ] -} diff --git a/phases/1-linear-static-mitc4-rebaseline/step0-audit.md b/phases/1-linear-static-mitc4-rebaseline/step0-audit.md deleted file mode 100644 index f652b00..0000000 --- a/phases/1-linear-static-mitc4-rebaseline/step0-audit.md +++ /dev/null @@ -1,88 +0,0 @@ -# Step 0 Audit: Phase 1 Rebaseline - -Date: 2026-05-04 -Author: Codex - -## Objective -Audit the existing Phase 1 C++ implementation against the paper-based `docs/MITC4_FORMULATION.md` reset before changing solver behavior. - -This step did not modify production C++ code. The existing implementation remains useful historical scaffolding, but it is not authoritative for the rebuilt MITC4 element until each layer is revalidated through the rebaseline steps. - -## Required Sources Read -- `AGENTS.md` -- `PLAN.md` -- `PROGRESS.md` -- `docs/README.md` -- `docs/HARNESS_ENGINEERING.md` -- `docs/PRD.md` -- `docs/ARCHITECTURE.md` -- `docs/ADR.md` -- `docs/NUMERICAL_CONVENTIONS.md` -- `docs/MITC4_FORMULATION.md` -- `phases/1-linear-static-mitc4-rebaseline/step0.md` - -## Code Areas Inspected -- `include/fesa/fesa.hpp` -- `tests/test_main.cpp` -- `CMakeLists.txt` -- `scripts/validate_workspace.py` -- `references/quad_01.inp` -- `references/quad_02.inp` - -## High-Level Finding -The existing implementation has a working single-header Phase 1 skeleton: parser, domain validation, DOF mapping, dense test solver, in-memory results, displacement CSV comparison, full-vector reaction recovery, and a basic linear static workflow. - -The existing MITC4 kernel is not compatible with the revised formulation contract. It uses a simplified midsurface local basis, 2D projected derivatives, separated membrane/bending/shear factors, direct shear row construction at tying points, 2 x 2 midsurface-only integration, and `drilling_stiffness_scale = 1.0e-6`. The revised contract requires degenerated-continuum geometry, director/local rotation handling, convected covariant strain rows, MITC transverse shear tying on tensor components, `2 x 2 x 2` Gauss integration, and drilling stabilization based on `1.0e-3 * min_positive_diagonal(K_local_without_drilling)`. - -## Retain With Revalidation -These areas should be kept as starting points, but each must be revalidated by the assigned rebaseline step before being treated as Phase 1 evidence. - -| Area | Current Evidence | Rebaseline Owner | -|---|---|---| -| CMake/CTest validation harness | CMake files and `scripts/validate_workspace.py` run a real C++ test executable. | P1R-02 | -| Core numeric and DOF aliases | `Real`, int64 ids/equations, and Abaqus DOF 1..6 mapping exist. | P1R-02 | -| Diagnostics model | Error/warning diagnostics and code-based checks exist. | P1R-02, P1R-04 | -| Parser subset skeleton | `*Node`, `*Element TYPE=S4`, `*Nset`, `*Elset`, `*Material`, `*Elastic`, `*Shell Section`, `*Boundary`, `*Cload`, `*Step`, `*Static`, and `*End Step` are represented. | P1R-03 | -| Unsupported feature rejection | Generic unsupported keyword rejection plus `S4R` and `NLGEOM=YES` checks exist. | P1R-01, P1R-03 | -| Domain validation | Missing element nodes, shell section/material links, load/boundary targets, no active elements, and no load warnings are present. | P1R-04 | -| DofManager foundation | Six-DOF full order, constrained/free partition, equation numbering, and full-vector reconstruction exist outside Node/Element. | P1R-05 | -| Result field structure | In-memory step/frame fields for `U` and `RF` exist. | P1R-06 | -| CSV displacement comparator | Required Abaqus displacement columns are parsed and compared by node id with abs/rel tolerance support. | P1R-06, P1R-14 | -| Linear static flow | Reduced free-DOF solve, full vector reconstruction, and `R_full = K_full * U_full - F_full` are implemented. | P1R-12, P1R-13 | - -## Rewrite Required -These areas conflict with `docs/MITC4_FORMULATION.md` and should be rebuilt under the matching contracts, not patched casually. - -| Area | Conflict | Rebaseline Owner | -|---|---|---| -| MITC4 local basis | Current `computeLocalBasis` uses averaged edge directions from the four coordinates. The revised contract requires element-center midsurface normal, nodal director axes `V1/V2/Vn`, deterministic fallback axes, and integration local bases. | P1R-07 | -| Geometry and Jacobian | Current code projects the element to local 2D `xy` and uses bilinear planar derivatives only. The revised contract requires degenerated-continuum geometry with through-thickness coordinate `zeta`, covariant bases `g_i`, and invalid basis/Jacobian diagnostics. | P1R-07, P1R-09 | -| Local rotation transform | Current transform rotates three translations and three rotations with one basis block. The revised contract requires mapping global rotations to local `[alpha, beta, gamma]`, where drilling `gamma` does not enter physical strain. | P1R-08, P1R-10 | -| Strain rows | Current B matrix is an 8-row engineering shell split, not the documented convected covariant strain vector `[eps_11, eps_22, eps_33, gamma_23, gamma_13, gamma_12]`. | P1R-08 | -| MITC shear tying | Current `addStandardShearRow` builds Cartesian-like `gamma_xz/gamma_yz` rows at midside derivatives, then interpolates them. The revised contract ties convected tensor components `eps_13` and `eps_23` from direct covariant rows at A/B/C/D. | P1R-08 | -| Material and integration | Current code uses pre-integrated membrane, bending, and shear factors at `2 x 2` midsurface points. The revised contract requires local plane-stress matrix with shear correction, material transform as needed, and `2 x 2 x 2` Gauss integration. | P1R-09 | -| Drilling stabilization | Current default is `1.0e-6` and the stiffness is assembled through a drilling row using `E * thickness * scale`. The revised contract requires `1.0e-3 * min_positive_diagonal(K_local_without_drilling)` added to local `gamma` diagonals before transformation. | P1R-10 | -| MITC4 tests | Existing element test only checks shape partition, stiffness symmetry, and one uniform translation. The revised contract requires director basis, rotation transform, tying-row finite difference, MITC interpolation, rigid-body, drilling, patch, and locking-sensitivity tests. | P1R-07 through P1R-11 | - -## Revalidate Before Reference Regression -These areas are promising but not sufficient for stored-reference credibility yet. - -| Area | Required Revalidation | -|---|---| -| `quad_02` reference path | `quad_02.inp` uses `TYPE=S4`, but also `Part/Assembly/Instance` and `*Density`. Step 1 must either add a normalized Phase 1-compatible derivative input or create an explicit parser compatibility plan. | -| Parser rejection policy | The parser must continue rejecting `S4R`, `Part/Assembly/Instance`, `*Density`, `*Include`, pressure loads, nonzero prescribed displacement, and `NLGEOM=YES` unless a later contract changes the subset. | -| Singular diagnostics | Existing diagnostics cover some conditions, but untouched free DOFs, rotational/drilling risks, invalid element geometry, non-positive thickness paths, and solver zero-pivot messages need stronger coverage. | -| Reaction output | Full-vector reaction recovery exists, but `RF` should remain verified by equilibrium tests until user-provided Abaqus reaction CSV artifacts are available. | -| Assembly and solver boundary | Current dense matrix and Gaussian solver are acceptable for deterministic tests, but the architecture requires a future sparse/MKL adapter boundary and int64 sparse path. | - -## Deferred Or Out Of Scope -- Abaqus `S4R`, reduced integration, and hourglass control. -- `Part/Assembly/Instance` support unless Step 1 or a later parser contract explicitly adds it. -- `*Density` as material support for Phase 1 static analysis. -- `NLGEOM=YES`, geometric nonlinearity, pressure loads, RBE2/RBE3, nonzero prescribed displacements, dynamics, heat transfer, composite sections, stress/strain/resultant outputs, and mesh quality diagnostics. -- Tuning drilling stiffness to match one reference case. - -## Step Handoff -Proceed to P1R-01 reference onboarding. - -P1R-01 must decide the `quad_02` compatibility path without silently expanding parser support. P1R-02 can proceed in parallel afterward for core harness guardrails, but production MITC4 work should wait until the reference and guardrail foundations are explicit. diff --git a/phases/1-linear-static-mitc4-rebaseline/step0.md b/phases/1-linear-static-mitc4-rebaseline/step0.md deleted file mode 100644 index c9916e1..0000000 --- a/phases/1-linear-static-mitc4-rebaseline/step0.md +++ /dev/null @@ -1,59 +0,0 @@ -# Sprint Contract: Rebaseline Audit - -## Objective -Audit the existing Phase 1 implementation against the paper-based `docs/MITC4_FORMULATION.md` and produce a concrete rework map before changing solver behavior. - -## Required Reading -- /AGENTS.md -- /PROGRESS.md -- /PLAN.md -- /docs/README.md -- /docs/HARNESS_ENGINEERING.md -- /docs/PRD.md -- /docs/ARCHITECTURE.md -- /docs/ADR.md -- /docs/NUMERICAL_CONVENTIONS.md -- /docs/MITC4_FORMULATION.md -- /phases/1-linear-static-mitc4/index.json - -## Scope -- Inspect existing C++ implementation and tests only enough to classify what can be retained, rewritten, or deleted in later steps. -- Produce or update planning notes in `PLAN.md` and `PROGRESS.md`. -- If useful, add a short audit artifact under `phases/1-linear-static-mitc4-rebaseline/`. - -## Allowed Files -- `PLAN.md` -- `PROGRESS.md` -- `phases/1-linear-static-mitc4-rebaseline/` - -## Explicit Non-Goals -- Do not change production C++ behavior. -- Do not rewrite MITC4 formulas. -- Do not unblock stored-reference regression by accepting unsupported Abaqus syntax. - -## Tests To Write First -- None. This is a planning and audit sprint. - -## Reference Artifacts -- `references/quad_01.inp` -- `references/quad_01_displacements.csv` -- `references/quad_02.inp` -- `references/quad_02_displacements.csv` - -## Acceptance Commands -```bash -python scripts/validate_workspace.py -``` - -## Evaluator Checklist -- The audit identifies conflicts with `docs/MITC4_FORMULATION.md`, especially old averaged-edge basis, `1.0e-6` drilling scale, analytic thickness integration, and direct shear evaluation risks. -- Future work is assigned to later rebaseline steps instead of being fixed here. -- `PLAN.md` and `PROGRESS.md` describe the new state without treating the old P1-01 through P1-14 implementation as authoritative. - -## Handoff Requirements -- Record completed audit work in `PROGRESS.md`. -- Keep next implementation tasks in `PLAN.md` or later step files. - -## Do Not -- Do not merge this audit with implementation. -- Do not remove old phase history. diff --git a/phases/1-linear-static-mitc4-rebaseline/step1.md b/phases/1-linear-static-mitc4-rebaseline/step1.md deleted file mode 100644 index e0892e2..0000000 --- a/phases/1-linear-static-mitc4-rebaseline/step1.md +++ /dev/null @@ -1,61 +0,0 @@ -# Sprint Contract: Reference Onboarding - -## Objective -Create a Phase 1 reference path for `quad_02` without modifying the original Abaqus artifact or silently expanding parser support. - -## Required Reading -- /AGENTS.md -- /PROGRESS.md -- /PLAN.md -- /docs/ABAQUS_INPUT_SUBSET.md -- /docs/VERIFICATION_PLAN.md -- /docs/RESULTS_SCHEMA.md -- /docs/MITC4_FORMULATION.md -- /references/quad_02.inp -- /references/quad_02_displacements.csv - -## Scope -- Inspect `quad_02` and document its supported and unsupported features. -- Add a normalized Phase 1-compatible derivative input if selected by the planner, keeping the original `quad_02.inp` unchanged. -- Add notes or manifest metadata for unit system, source solver, expected outputs, and tolerances. - -## Allowed Files -- `references/` -- `docs/ABAQUS_INPUT_SUBSET.md` -- `docs/VERIFICATION_PLAN.md` -- `PLAN.md` -- `PROGRESS.md` -- `tests/` only for reference artifact parsing/comparator tests - -## Explicit Non-Goals -- Do not implement `Part`, `Assembly`, or `Instance` parser support in this step unless this contract is revised first. -- Do not edit `references/quad_02.inp` or `references/quad_02_displacements.csv`. -- Do not compare solver displacements until the MITC4 rebuild and end-to-end workflow are complete. - -## Tests To Write First -- Test that original `quad_02.inp` is recognized as stored provenance with unsupported `Part/Assembly/Instance`. -- If a normalized input is added, test that it contains only the documented Phase 1 subset. -- Test `quad_02_displacements.csv` column loading and 121-row node-label parsing. - -## Reference Artifacts -- `references/quad_02.inp` -- `references/quad_02_displacements.csv` - -## Acceptance Commands -```bash -python scripts/validate_workspace.py -``` - -## Evaluator Checklist -- Original Abaqus files are preserved. -- Any normalized derivative file has a clear name and provenance note. -- Parser scope remains aligned with `docs/ABAQUS_INPUT_SUBSET.md`. -- Tolerances and result mapping are documented before regression use. - -## Handoff Requirements -- Update `PROGRESS.md` with reference files changed or added. -- Update `PLAN.md` if parser compatibility remains blocked. - -## Do Not -- Do not treat `*Density` as material support. -- Do not allow `Part/Assembly/Instance` through the Phase 1 parser accidentally. diff --git a/phases/1-linear-static-mitc4-rebaseline/step10.md b/phases/1-linear-static-mitc4-rebaseline/step10.md deleted file mode 100644 index cdc099e..0000000 --- a/phases/1-linear-static-mitc4-rebaseline/step10.md +++ /dev/null @@ -1,54 +0,0 @@ -# Sprint Contract: MITC4 Stiffness Drilling - -## Objective -Assemble the MITC4 element stiffness and internal force kernel using the documented strain, material, integration, six-DOF transform, and drilling stabilization. - -## Required Reading -- /AGENTS.md -- /docs/MITC4_FORMULATION.md -- /docs/NUMERICAL_CONVENTIONS.md -- /docs/ADR.md - -## Scope -- Accumulate `K_e = integral B^T D B dV`. -- Add local drilling stabilization with `drilling_stiffness_scale = 1.0e-3`. -- Transform local element stiffness/internal force to global 24-DOF order. -- Preserve parameterization and diagnostics for drilling scale. - -## Allowed Files -- `include/` -- `src/` -- `tests/` -- `PLAN.md` -- `PROGRESS.md` - -## Explicit Non-Goals -- Do not add assembly/global solve. -- Do not tune drilling scale to match one reference case. -- Do not output stresses/resultants as mandatory fields. - -## Tests To Write First -- Stiffness dimension and symmetry tests. -- Drilling diagonal reference tests using minimum positive physical local stiffness diagonal. -- Zero/invalid reference diagonal diagnostic tests. -- Rigid body physical strain-energy tests with documented drilling effects. -- Internal force consistency test `f_int_e = K_e u_e` for linear elastic Phase 1. - -## Reference Artifacts -- None. - -## Acceptance Commands -```bash -python scripts/validate_workspace.py -``` - -## Evaluator Checklist -- Old `1.0e-6 * E * thickness` drilling rule is gone. -- Global 24-DOF ordering matches `UX,UY,UZ,RX,RY,RZ` per node. -- Stiffness remains linear elastic and symmetric. - -## Handoff Requirements -- Record element kernel readiness in `PROGRESS.md`. - -## Do Not -- Do not optimize or parallelize before element tests pass. diff --git a/phases/1-linear-static-mitc4-rebaseline/step11.md b/phases/1-linear-static-mitc4-rebaseline/step11.md deleted file mode 100644 index 624c518..0000000 --- a/phases/1-linear-static-mitc4-rebaseline/step11.md +++ /dev/null @@ -1,52 +0,0 @@ -# Sprint Contract: MITC4 Patch Benchmark Tests - -## Objective -Add element-level and small-mesh MITC4 patch and benchmark tests before integrating stored Abaqus references. - -## Required Reading -- /AGENTS.md -- /docs/MITC4_FORMULATION.md -- /docs/VERIFICATION_PLAN.md -- /docs/NUMERICAL_CONVENTIONS.md - -## Scope -- Add constant membrane, pure bending, pure shear, pure twist, rigid-body, and drilling sensitivity tests. -- Add a thin cantilever or plate strip test to expose shear locking. -- Add benchmark scaffolding for Scordelis-Lo after flat tests pass. - -## Allowed Files -- `tests/` -- `include/` -- `src/` only for minimal fixes required by tests -- `docs/VERIFICATION_PLAN.md` only for clarifying benchmark notes -- `PLAN.md` -- `PROGRESS.md` - -## Explicit Non-Goals -- Do not require Abaqus execution. -- Do not add pressure loads for Scordelis-Lo in Phase 1. -- Do not hide failing element behavior by loosening tolerances without rationale. - -## Tests To Write First -- The patch and benchmark tests are the sprint output; write them before any fixes. -- Include at least one negative or sensitivity test for drilling scale effects. - -## Reference Artifacts -- Local analytic or hand-check benchmark data only. -- Stored Abaqus CSV regression is reserved for step 14. - -## Acceptance Commands -```bash -python scripts/validate_workspace.py -``` - -## Evaluator Checklist -- Tests cover locking-sensitive behavior, not only shape functions. -- Tolerances are scale-aware and documented. -- Any implementation fixes remain minimal and within MITC4 scope. - -## Handoff Requirements -- Record benchmark coverage and remaining gaps in `PROGRESS.md` and `PLAN.md`. - -## Do Not -- Do not implement new load types to force a benchmark into Phase 1. diff --git a/phases/1-linear-static-mitc4-rebaseline/step12.md b/phases/1-linear-static-mitc4-rebaseline/step12.md deleted file mode 100644 index fa1411d..0000000 --- a/phases/1-linear-static-mitc4-rebaseline/step12.md +++ /dev/null @@ -1,55 +0,0 @@ -# Sprint Contract: Assembly Sparse Solver Path - -## Objective -Rebuild or verify full-space assembly, reduced free-DOF solve path, solver adapter boundary, and full-vector reaction recovery using the rebuilt MITC4 kernel. - -## Required Reading -- /AGENTS.md -- /docs/ARCHITECTURE.md -- /docs/ADR.md -- /docs/NUMERICAL_CONVENTIONS.md -- /docs/MITC4_FORMULATION.md - -## Scope -- Assemble global stiffness and external load vectors. -- Preserve full-space data for `RF = K_full * U_full - F_full`. -- Apply constrained DOF elimination via `DofManager`. -- Use the current deterministic test solver or adapter boundary; keep MKL behind a future adapter. - -## Allowed Files -- `include/` -- `src/` -- `tests/` -- `PLAN.md` -- `PROGRESS.md` - -## Explicit Non-Goals -- Do not expose MKL or TBB APIs directly in solver core. -- Do not compare stored Abaqus references yet. -- Do not compute reactions from reduced equations. - -## Tests To Write First -- Small assembled system tests with known displacement. -- Full-vector `RF` recovery tests. -- Constrained/free projection tests through assembly. -- Singular solve diagnostic tests. -- Sparse pattern deterministic ordering tests if sparse structure exists. - -## Reference Artifacts -- None. - -## Acceptance Commands -```bash -python scripts/validate_workspace.py -``` - -## Evaluator Checklist -- Full-space stiffness/load/displacement are available or reconstructable for reactions. -- Solver adapter boundary is preserved. -- Singular failures remain actionable. - -## Handoff Requirements -- Record assembly and reaction readiness in `PROGRESS.md`. - -## Do Not -- Do not let elements own global equation ids. diff --git a/phases/1-linear-static-mitc4-rebaseline/step13.md b/phases/1-linear-static-mitc4-rebaseline/step13.md deleted file mode 100644 index 7a9d7c8..0000000 --- a/phases/1-linear-static-mitc4-rebaseline/step13.md +++ /dev/null @@ -1,56 +0,0 @@ -# Sprint Contract: Linear Static Workflow - -## Objective -Rebuild or verify the full linear static workflow from parsed `Domain` to `AnalysisModel`, `AnalysisState`, solve, `U` and `RF` result fields. - -## Required Reading -- /AGENTS.md -- /docs/ARCHITECTURE.md -- /docs/ADR.md -- /docs/RESULTS_SCHEMA.md -- /docs/NUMERICAL_CONVENTIONS.md -- /docs/MITC4_FORMULATION.md - -## Scope -- Build the active `AnalysisModel` for one linear static step. -- Run the template workflow: build DOF map, assemble, apply constraints, solve, reconstruct full vectors, recover RF, write results. -- Keep `Domain` immutable and `AnalysisState` mutable. - -## Allowed Files -- `include/` -- `src/` -- `tests/` -- `PLAN.md` -- `PROGRESS.md` - -## Explicit Non-Goals -- Do not implement nonlinear, dynamic, pressure, or thermal behavior. -- Do not add multiple-step execution unless already trivial and tested. -- Do not make HDF5 dependency mandatory if the current minimal writer remains the accepted Phase 1 path. - -## Tests To Write First -- Input-to-analysis-model activation test. -- End-to-end small linear static solve with `U` and `RF`. -- Result schema field label and frame tests. -- Full-vector reaction balance test. -- Missing/unsupported input negative tests routed through the workflow. - -## Reference Artifacts -- Normalized small `.inp` fixtures may be used. -- Stored Abaqus CSV regression is reserved for step 14. - -## Acceptance Commands -```bash -python scripts/validate_workspace.py -``` - -## Evaluator Checklist -- Workflow follows architecture boundaries. -- `AnalysisState` owns mutable vectors. -- Results use documented step/frame/field structure. - -## Handoff Requirements -- Record end-to-end workflow readiness in `PROGRESS.md`. - -## Do Not -- Do not bypass validators to make a model solve. diff --git a/phases/1-linear-static-mitc4-rebaseline/step14.md b/phases/1-linear-static-mitc4-rebaseline/step14.md deleted file mode 100644 index 2748362..0000000 --- a/phases/1-linear-static-mitc4-rebaseline/step14.md +++ /dev/null @@ -1,61 +0,0 @@ -# Sprint Contract: Stored Reference Regression - -## Objective -Run automated stored-reference displacement regression against accepted Phase 1-compatible S4 cases, starting with the `quad_02` compatibility path chosen in step 1. - -## Required Reading -- /AGENTS.md -- /docs/VERIFICATION_PLAN.md -- /docs/ABAQUS_INPUT_SUBSET.md -- /docs/RESULTS_SCHEMA.md -- /docs/MITC4_FORMULATION.md -- /references/ - -## Scope -- Wire accepted Phase 1-compatible reference inputs into automated tests. -- Compare FESA `U` against `*_displacements.csv` using documented tolerances. -- Keep original unsupported Abaqus files as provenance and negative/compatibility tests. -- Record pass/fail comparison details. - -## Allowed Files -- `references/` -- `tests/` -- `include/` -- `src/` only for minimal fixes needed by reference regression -- `docs/VERIFICATION_PLAN.md` -- `PLAN.md` -- `PROGRESS.md` - -## Explicit Non-Goals -- Do not require Abaqus execution. -- Do not accept `S4R`, `Part/Assembly/Instance`, `*Density`, or `NLGEOM=YES` through the parser without a prior parser contract. -- Do not mark Phase 1 complete with only one reference case unless `PLAN.md` records the remaining PRD gap. - -## Tests To Write First -- Reference manifest or fixture discovery tests. -- `quad_02` accepted derivative input regression test if normalized in step 1. -- Original `quad_02.inp` unsupported-provenance test if parser scope still excludes `Part/Assembly/Instance`. -- CSV displacement comparison test with absolute and relative tolerances. - -## Reference Artifacts -- `references/quad_02.inp` -- `references/quad_02_displacements.csv` -- Any normalized Phase 1-compatible derivative files from step 1. - -## Acceptance Commands -```bash -python scripts/validate_workspace.py -``` - -## Evaluator Checklist -- Regression uses stored artifacts only. -- Original reference provenance is preserved. -- Comparison is node-id-based and reports errors clearly. -- Remaining PRD requirement for three stored references is recorded if not satisfied. - -## Handoff Requirements -- Record reference pass/fail, tolerances, and residual risks in `PROGRESS.md`. -- Update `PLAN.md` with additional reference cases still needed. - -## Do Not -- Do not tune solver formulas to one CSV without element-level tests remaining green. diff --git a/phases/1-linear-static-mitc4-rebaseline/step15-evaluator-report.md b/phases/1-linear-static-mitc4-rebaseline/step15-evaluator-report.md deleted file mode 100644 index d06033c..0000000 --- a/phases/1-linear-static-mitc4-rebaseline/step15-evaluator-report.md +++ /dev/null @@ -1,59 +0,0 @@ -# Evaluation Report: P1R-15 Phase 1 Rebaseline Closeout - -Date: 2026-05-04 -Evaluator: Codex - -## Verdict - -PASS for the `1-linear-static-mitc4-rebaseline` sprint sequence, with documented product-level Phase 1 reference gaps. - -This verdict closes the rebaseline execution path through P1R-15. It does not claim that the broader PRD Phase 1 reference target is fully complete, because R-010 and R-013 remain open in `PLAN.md`. - -## Scope Reviewed - -- `AGENTS.md` -- `PLAN.md` -- `PROGRESS.md` -- `docs/README.md` -- `docs/HARNESS_ENGINEERING.md` -- `docs/PRD.md` -- `docs/ARCHITECTURE.md` -- `docs/ADR.md` -- `docs/NUMERICAL_CONVENTIONS.md` -- `docs/ABAQUS_INPUT_SUBSET.md` -- `docs/VERIFICATION_PLAN.md` -- `docs/RESULTS_SCHEMA.md` -- `docs/MITC4_FORMULATION.md` -- `phases/index.json` -- `phases/1-linear-static-mitc4-rebaseline/index.json` -- Completed rebaseline handoff notes in `PROGRESS.md` - -## Evidence - -- Rebaseline steps P1R-00 through P1R-14 were already marked complete before this evaluation. -- `python scripts/validate_workspace.py` passed on 2026-05-04. The command configured CMake, built `fesa_core` and `fesa_tests`, and ran CTest successfully. -- The active stored reference regression uses `references/quad_02_phase1.inp` and compares FESA `U` against `references/quad_02_displacements.csv`. -- The original `references/quad_02.inp` remains preserved as Abaqus/CAE provenance and is not treated as parser acceptance evidence for `Part/Assembly/Instance`. -- `RF` is recovered from the full vector path and covered by in-repo equilibrium/reaction tests; no Abaqus `*_reactions.csv` artifact is available yet. - -## Checklist - -| Item | Result | Notes | -|---|---|---| -| AGENTS critical rules satisfied | PASS | PLAN/PROGRESS were read before edits, architecture/numerical/reference rules were checked, and Abaqus execution was not required. | -| Harness contract followed | PASS | Step 15 stayed inside the allowed evaluation/documentation scope and did not implement missing solver behavior. | -| TDD expectations met for implementation steps | PASS | Earlier generator steps recorded failing-first or test-first validation where code was changed; Step 15 is evaluator-only. | -| MITC4 formulation alignment | PASS | Rebaseline steps 7 through 11 are tied to the paper-based formulation document and current drilling scale policy. | -| Parser subset discipline | PASS | Unsupported Abaqus/CAE provenance remains documented instead of silently expanding the Phase 1 parser subset. | -| Full-vector RF recovery | PASS with reference gap | Implementation path preserves full-space stiffness/load state and tests full-vector RF recovery. Abaqus RF CSV comparison remains open as R-010. | -| Stored displacement reference regression | PASS with PRD gap | `quad_02_phase1` displacement comparison passes. The PRD target of three stored references remains open as R-013. | -| PLAN/PROGRESS synchronized | PASS | Step 15 updates move the active objective to post-rebaseline reference completion. | - -## Residual Gaps - -- R-010: Add Abaqus reaction-force CSV artifacts, preferably `*_reactions.csv`, or explicitly accept internal equilibrium tests as the Phase 1 RF verification basis until Abaqus RF output is available. -- R-013: Add enough additional small Abaqus S4 reference cases to satisfy the PRD target of three stored Phase 1 references: one single-element case, one simple multi-element plate/shell case, and one curved shell benchmark. - -## Closeout Decision - -Mark `phases/1-linear-static-mitc4-rebaseline` complete. Continue project work from `PLAN.md` with R-010 and R-013 as the next reference-readiness tasks before claiming full PRD Phase 1 completion. diff --git a/phases/1-linear-static-mitc4-rebaseline/step15.md b/phases/1-linear-static-mitc4-rebaseline/step15.md deleted file mode 100644 index 2d9eeb8..0000000 --- a/phases/1-linear-static-mitc4-rebaseline/step15.md +++ /dev/null @@ -1,65 +0,0 @@ -# Sprint Contract: Phase 1 Evaluator Closeout - -## Objective -Perform an independent Phase 1 evaluation against the rebaseline contracts, docs, tests, reference artifacts, and validation commands. - -## Required Reading -- /AGENTS.md -- /PROGRESS.md -- /PLAN.md -- /docs/README.md -- /docs/HARNESS_ENGINEERING.md -- /docs/PRD.md -- /docs/ARCHITECTURE.md -- /docs/ADR.md -- /docs/NUMERICAL_CONVENTIONS.md -- /docs/ABAQUS_INPUT_SUBSET.md -- /docs/VERIFICATION_PLAN.md -- /docs/RESULTS_SCHEMA.md -- /docs/MITC4_FORMULATION.md -- /phases/1-linear-static-mitc4-rebaseline/index.json - -## Scope -- Review all completed rebaseline steps. -- Run validation. -- Produce pass/fail findings. -- Update phase status, `PLAN.md`, and `PROGRESS.md` with final Phase 1 state. - -## Allowed Files -- `PLAN.md` -- `PROGRESS.md` -- `phases/index.json` -- `phases/1-linear-static-mitc4-rebaseline/index.json` -- Optional evaluator feedback artifact under `phases/1-linear-static-mitc4-rebaseline/` - -## Explicit Non-Goals -- Do not implement missing solver behavior during evaluation. -- Do not approve unresolved reference gaps silently. -- Do not mark old superseded phase steps as evidence for new MITC4 formulation compliance unless they were revalidated. - -## Tests To Write First -- None. This is an evaluator sprint. - -## Reference Artifacts -- All accepted Phase 1 reference artifacts under `references/`. - -## Acceptance Commands -```bash -python scripts/validate_workspace.py -``` - -## Evaluator Checklist -- All CRITICAL `AGENTS.md` rules are satisfied. -- TDD expectations are met for implementation steps. -- MITC4 implementation follows `docs/MITC4_FORMULATION.md`. -- Parser has not silently expanded unsupported Abaqus features. -- `RF` is recovered from full vectors. -- At least one stored displacement reference regression passes, and remaining PRD reference gaps are explicit. -- `PLAN.md` and `PROGRESS.md` are synchronized. - -## Handoff Requirements -- If passed, mark the rebaseline phase complete and move remaining Phase 2 or reference tasks to `PLAN.md`. -- If failed, write a concise feedback artifact with required fixes and commands to rerun. - -## Do Not -- Do not self-approve implementation without evidence from tests and reference comparisons. diff --git a/phases/1-linear-static-mitc4-rebaseline/step2.md b/phases/1-linear-static-mitc4-rebaseline/step2.md deleted file mode 100644 index 876db5c..0000000 --- a/phases/1-linear-static-mitc4-rebaseline/step2.md +++ /dev/null @@ -1,58 +0,0 @@ -# Sprint Contract: Core Harness Guardrails - -## Objective -Refresh the build, validation, core numeric aliases, DOF enum, and diagnostic guardrails so later MITC4 work has stable test foundations. - -## Required Reading -- /AGENTS.md -- /docs/ARCHITECTURE.md -- /docs/ADR.md -- /docs/NUMERICAL_CONVENTIONS.md -- /docs/HARNESS_ENGINEERING.md -- /scripts/validate_workspace.py - -## Scope -- Preserve or repair CMake/CTest validation. -- Verify `double` and signed int64 aliases are centralized. -- Verify `UX`, `UY`, `UZ`, `RX`, `RY`, `RZ` mapping to Abaqus DOF `1..6`. -- Improve diagnostics only where needed for later steps. - -## Allowed Files -- `CMakeLists.txt` -- `include/` -- `src/` -- `tests/` -- `scripts/validate_workspace.py` -- `PLAN.md` -- `PROGRESS.md` - -## Explicit Non-Goals -- Do not implement parser features. -- Do not implement MITC4 stiffness. -- Do not introduce MKL, TBB, or HDF5 APIs into solver core. - -## Tests To Write First -- DOF enum and Abaqus DOF number mapping tests. -- Numeric alias compile-time or runtime tests for `double` and int64 paths. -- Validation script smoke test through `python scripts/validate_workspace.py`. - -## Reference Artifacts -- None. - -## Acceptance Commands -```bash -python scripts/validate_workspace.py -``` - -## Evaluator Checklist -- Core aliases and DOF mapping are not duplicated across modules. -- Diagnostics include enough context for later parser, model, and singular errors. -- Validation remains a real build plus test command. - -## Handoff Requirements -- Record changed guardrails and test coverage in `PROGRESS.md`. -- Leave future parser/MITC4 tasks in later step files. - -## Do Not -- Do not weaken validation to make tests pass. -- Do not add external library dependencies in this step. diff --git a/phases/1-linear-static-mitc4-rebaseline/step3.md b/phases/1-linear-static-mitc4-rebaseline/step3.md deleted file mode 100644 index 9912a7c..0000000 --- a/phases/1-linear-static-mitc4-rebaseline/step3.md +++ /dev/null @@ -1,58 +0,0 @@ -# Sprint Contract: Parser Domain Subset - -## Objective -Revalidate and repair the Phase 1 Abaqus parser and `Domain` model against the documented subset while preserving strict rejection of unsupported reference features. - -## Required Reading -- /AGENTS.md -- /docs/ARCHITECTURE.md -- /docs/ADR.md -- /docs/ABAQUS_INPUT_SUBSET.md -- /docs/VERIFICATION_PLAN.md -- /docs/MITC4_FORMULATION.md - -## Scope -- Parse Phase 1 keywords into immutable `Domain` objects. -- Preserve node, element, set, material, shell section, boundary, load, and step definitions. -- Reject `S4R`, `Part/Assembly/Instance`, `*Include`, `NLGEOM=YES`, nonzero prescribed displacement, and unsupported material/section modes. - -## Allowed Files -- `include/` -- `src/` -- `tests/` -- `docs/ABAQUS_INPUT_SUBSET.md` only for clarifying parser contract -- `PLAN.md` -- `PROGRESS.md` - -## Explicit Non-Goals -- Do not implement analysis or assembly. -- Do not add `Part/Assembly/Instance` support because `quad_02` contains it. -- Do not normalize reference files in parser code. - -## Tests To Write First -- Parser acceptance tests for every Phase 1 keyword. -- Generated and explicit `*Nset` and `*Elset` tests. -- Unsupported feature rejection tests for `S4R`, `Part`, `Assembly`, `Instance`, `*Density`, and `NLGEOM=YES`. -- Line-numbered diagnostic tests for malformed numeric and DOF fields. - -## Reference Artifacts -- `references/quad_01.inp` as unsupported provenance. -- `references/quad_02.inp` as unsupported original provenance until normalized or parser scope changes. - -## Acceptance Commands -```bash -python scripts/validate_workspace.py -``` - -## Evaluator Checklist -- Parser behavior matches `docs/ABAQUS_INPUT_SUBSET.md`. -- `TYPE=S4` maps to internal MITC4. -- Unsupported stored references are not silently accepted. -- Domain objects do not own equation numbers. - -## Handoff Requirements -- Update `PROGRESS.md` with parser status. -- Update `PLAN.md` for unresolved reference compatibility work. - -## Do Not -- Do not broaden the parser subset without ADR and doc updates. diff --git a/phases/1-linear-static-mitc4-rebaseline/step4.md b/phases/1-linear-static-mitc4-rebaseline/step4.md deleted file mode 100644 index b2c4093..0000000 --- a/phases/1-linear-static-mitc4-rebaseline/step4.md +++ /dev/null @@ -1,56 +0,0 @@ -# Sprint Contract: Validation Singular Diagnostics - -## Objective -Strengthen domain and analysis-model validation so invalid inputs and singular-prone models fail before or during solve with actionable diagnostics. - -## Required Reading -- /AGENTS.md -- /docs/ARCHITECTURE.md -- /docs/NUMERICAL_CONVENTIONS.md -- /docs/ABAQUS_INPUT_SUBSET.md -- /docs/VERIFICATION_PLAN.md - -## Scope -- Validate missing nodes, properties, materials, sets, boundary targets, load targets, active elements, and non-positive thickness. -- Add singular-prone diagnostics for no free DOFs, untouched free DOFs, missing loads where expected, and unconstrained drilling/rotational risks. -- Keep mesh quality diagnostics out of Phase 1. - -## Allowed Files -- `include/` -- `src/` -- `tests/` -- `PLAN.md` -- `PROGRESS.md` - -## Explicit Non-Goals -- Do not implement mesh quality metrics such as aspect ratio, skew, or warpage. -- Do not solve the global system in this step. -- Do not hide singular checks in element kernels. - -## Tests To Write First -- Missing node/property/material diagnostics. -- Missing set used by shell section, boundary, or load. -- No active elements. -- No free DOFs. -- Free DOFs untouched by any active element connectivity. -- Rotational/drilling weak-constraint diagnostic smoke test. - -## Reference Artifacts -- Negative inline test inputs may be used. - -## Acceptance Commands -```bash -python scripts/validate_workspace.py -``` - -## Evaluator Checklist -- Diagnostics include node id, element id, property id, material id, set name, DOF component, and source line where available. -- Mesh quality remains explicitly deferred. -- Validation does not mutate `Domain`. - -## Handoff Requirements -- Record validation coverage in `PROGRESS.md`. -- Note any remaining singular-diagnostic gaps in `PLAN.md`. - -## Do Not -- Do not classify poor element shape as a Phase 1 mesh quality failure. diff --git a/phases/1-linear-static-mitc4-rebaseline/step5.md b/phases/1-linear-static-mitc4-rebaseline/step5.md deleted file mode 100644 index 113e3e9..0000000 --- a/phases/1-linear-static-mitc4-rebaseline/step5.md +++ /dev/null @@ -1,57 +0,0 @@ -# Sprint Contract: Dof Manager Reaction Foundation - -## Objective -Rebuild or verify the six-DOF `DofManager`, constrained/free mapping, equation numbering, sparse-pattern inputs, and full-vector reconstruction needed for reaction recovery. - -## Required Reading -- /AGENTS.md -- /docs/ARCHITECTURE.md -- /docs/ADR.md -- /docs/NUMERICAL_CONVENTIONS.md -- /docs/RESULTS_SCHEMA.md - -## Scope -- Manage active six-DOF shell nodes. -- Apply fixed constraints by constrained DOF elimination. -- Own equation numbering and sparse connectivity inputs. -- Reconstruct full vectors in original global DOF order. -- Provide data needed for `RF = K_full * U_full - F_full`. - -## Allowed Files -- `include/` -- `src/` -- `tests/` -- `PLAN.md` -- `PROGRESS.md` - -## Explicit Non-Goals -- Do not assemble MITC4 stiffness. -- Do not store equation ids in `Node` or `Element`. -- Do not compute `RF` from reduced vectors only. - -## Tests To Write First -- DOF activation and Abaqus component order. -- Constrained/free partition tests. -- Equation numbering stability with noncontiguous node ids. -- Full/reduced vector reconstruction tests. -- Sparse-pattern connectivity input tests. -- Full-vector reaction formula unit test with a small known matrix. - -## Reference Artifacts -- None. - -## Acceptance Commands -```bash -python scripts/validate_workspace.py -``` - -## Evaluator Checklist -- `DofManager` owns all equation numbering. -- Full-space order is deterministic and matches `UX,UY,UZ,RX,RY,RZ`. -- Reaction foundation preserves constrained DOF information. - -## Handoff Requirements -- Record DOF and reaction foundation status in `PROGRESS.md`. - -## Do Not -- Do not bypass `DofManager` from elements or analysis code. diff --git a/phases/1-linear-static-mitc4-rebaseline/step6.md b/phases/1-linear-static-mitc4-rebaseline/step6.md deleted file mode 100644 index 82b42dc..0000000 --- a/phases/1-linear-static-mitc4-rebaseline/step6.md +++ /dev/null @@ -1,57 +0,0 @@ -# Sprint Contract: Results Comparator Foundation - -## Objective -Rebuild or verify the minimum Phase 1 results model and Abaqus displacement CSV comparator before using stored reference regressions. - -## Required Reading -- /AGENTS.md -- /docs/RESULTS_SCHEMA.md -- /docs/VERIFICATION_PLAN.md -- /docs/NUMERICAL_CONVENTIONS.md - -## Scope -- Store minimum result model data for nodes, elements, `U`, and `RF`. -- Preserve step/frame/field structure even if HDF5 writing is deferred. -- Load Abaqus `*_displacements.csv` by node id and component label. -- Compare using absolute and relative tolerances. - -## Allowed Files -- `include/` -- `src/` -- `tests/` -- `docs/RESULTS_SCHEMA.md` only for clarifying schema text -- `docs/VERIFICATION_PLAN.md` only for clarifying tolerance/reference text -- `PLAN.md` -- `PROGRESS.md` - -## Explicit Non-Goals -- Do not make `S`, `E`, or `SF` mandatory. -- Do not require Abaqus execution. -- Do not rely on CSV row order. - -## Tests To Write First -- `U` and `RF` field component labels and basis tests. -- CSV required-header tests. -- Duplicate/missing/non-numeric node row tests. -- Node-id-based comparison tests with absolute and relative tolerance paths. -- Result frame metadata tests for Phase 1 linear static frame 0. - -## Reference Artifacts -- `references/quad_02_displacements.csv` -- `references/quad_01_displacements.csv` - -## Acceptance Commands -```bash -python scripts/validate_workspace.py -``` - -## Evaluator Checklist -- Result component order matches docs. -- Comparator maps Abaqus columns to FESA `U` labels exactly. -- `RF` exists as a result field even if Abaqus RF CSV is unavailable. - -## Handoff Requirements -- Record comparator readiness and remaining RF artifact decision in `PROGRESS.md` and `PLAN.md`. - -## Do Not -- Do not add undocumented CSV formats. diff --git a/phases/1-linear-static-mitc4-rebaseline/step7.md b/phases/1-linear-static-mitc4-rebaseline/step7.md deleted file mode 100644 index ff3666d..0000000 --- a/phases/1-linear-static-mitc4-rebaseline/step7.md +++ /dev/null @@ -1,55 +0,0 @@ -# Sprint Contract: MITC4 Geometry Directors - -## Objective -Implement or rebuild MITC4 shape functions, natural-coordinate node order, element-center director policy, local director axes, and integration-point local bases. - -## Required Reading -- /AGENTS.md -- /docs/MITC4_FORMULATION.md -- /docs/NUMERICAL_CONVENTIONS.md -- /docs/ADR.md - -## Scope -- Define `N1..N4` and derivatives for FESA natural coordinates. -- Define tying points `A`, `B`, `C`, and `D`. -- Build element-center director `Vn` from midsurface geometry. -- Build nodal `V1`, `V2`, `Vn` and integration local Cartesian basis. -- Add invalid/singular element diagnostics for near-zero normal, basis, Jacobian, and thickness. - -## Allowed Files -- `include/` -- `src/` -- `tests/` -- `PLAN.md` -- `PROGRESS.md` - -## Explicit Non-Goals -- Do not compute stiffness. -- Do not add Abaqus `*Orientation` or nodal normal parsing. -- Do not add mesh quality diagnostics. - -## Tests To Write First -- Shape function partition-of-unity and derivative tests. -- Node order and tying-point coordinate tests. -- Flat element normal and right-handed basis tests. -- Fallback axis tests when `EY x Vn` is near zero. -- Invalid geometry diagnostic tests. - -## Reference Artifacts -- None. - -## Acceptance Commands -```bash -python scripts/validate_workspace.py -``` - -## Evaluator Checklist -- Geometry matches `docs/MITC4_FORMULATION.md`. -- The old averaged-edge basis is not used as the binding formulation. -- No stress/strain or stiffness logic is slipped into this step. - -## Handoff Requirements -- Record MITC4 geometry readiness in `PROGRESS.md`. - -## Do Not -- Do not silently support strongly warped/nodal-director behavior beyond the documented Phase 1 policy. diff --git a/phases/1-linear-static-mitc4-rebaseline/step8.md b/phases/1-linear-static-mitc4-rebaseline/step8.md deleted file mode 100644 index b26f842..0000000 --- a/phases/1-linear-static-mitc4-rebaseline/step8.md +++ /dev/null @@ -1,54 +0,0 @@ -# Sprint Contract: MITC4 Covariant Strain Tying - -## Objective -Implement or rebuild the degenerated-continuum displacement interpolation, direct covariant strain rows, and MITC transverse shear tying interpolation. - -## Required Reading -- /AGENTS.md -- /docs/MITC4_FORMULATION.md -- /docs/NUMERICAL_CONVENTIONS.md - -## Scope -- Implement five-DOF local displacement interpolation. -- Transform global rotations to local `alpha`, `beta`, and `gamma`. -- Compute direct covariant strain rows in documented internal strain order. -- Replace `eps_13` and `eps_23` with midside MITC tying interpolation. -- Add finite-difference tests for strain rows. - -## Allowed Files -- `include/` -- `src/` -- `tests/` -- `PLAN.md` -- `PROGRESS.md` - -## Explicit Non-Goals -- Do not integrate stiffness. -- Do not add material transform. -- Do not compute MITC shear from Cartesian `gamma_xz` and `gamma_yz` first. - -## Tests To Write First -- Local rotation transform tests for `alpha`, `beta`, `gamma`. -- Displacement interpolation tests at midsurface and through-thickness points. -- Direct covariant strain finite-difference tests. -- Tying-row tests at `A`, `B`, `C`, and `D`. -- Gauss-point MITC shear interpolation tests proving rows come from tying rows. - -## Reference Artifacts -- None. - -## Acceptance Commands -```bash -python scripts/validate_workspace.py -``` - -## Evaluator Checklist -- Strain vector ordering is documented or explicitly permuted. -- `gamma` does not contribute to physical continuum strain. -- FESA sign convention for `A/C/B/D` is tested. - -## Handoff Requirements -- Record strain/tying readiness in `PROGRESS.md`. - -## Do Not -- Do not reintroduce direct Gauss-point transverse shear for MITC rows. diff --git a/phases/1-linear-static-mitc4-rebaseline/step9.md b/phases/1-linear-static-mitc4-rebaseline/step9.md deleted file mode 100644 index 745d729..0000000 --- a/phases/1-linear-static-mitc4-rebaseline/step9.md +++ /dev/null @@ -1,54 +0,0 @@ -# Sprint Contract: MITC4 Material Integration - -## Objective -Implement or rebuild local plane-stress material behavior, shear correction, material transformation, and `2 x 2 x 2` Gauss integration scaffolding. - -## Required Reading -- /AGENTS.md -- /docs/MITC4_FORMULATION.md -- /docs/NUMERICAL_CONVENTIONS.md -- /docs/ARCHITECTURE.md - -## Scope -- Implement isotropic linear elastic shell material matrix with `sigma_33 = 0` and `kappa = 5/6`. -- Define material/strain ordering tests. -- Implement or verify convected-to-local material transformation for Phase 1 cases. -- Add `2 x 2 x 2` integration point and weight infrastructure. - -## Allowed Files -- `include/` -- `src/` -- `tests/` -- `PLAN.md` -- `PROGRESS.md` - -## Explicit Non-Goals -- Do not add composite sections. -- Do not add stress/strain output recovery as mandatory output. -- Do not switch to analytic thickness integration without equivalence tests and doc update. - -## Tests To Write First -- Isotropic matrix value tests for known `E`, `nu`, and `kappa`. -- Shear row ordering tests. -- Integration point count and weights tests. -- Flat element material transform identity/equivalence tests. -- Invalid material/thickness diagnostic tests if not covered earlier. - -## Reference Artifacts -- None. - -## Acceptance Commands -```bash -python scripts/validate_workspace.py -``` - -## Evaluator Checklist -- `2 x 2 x 2` integration is the active baseline. -- Material transform behavior is tested. -- No reduced integration or hourglass logic is introduced. - -## Handoff Requirements -- Record material and integration readiness in `PROGRESS.md`. - -## Do Not -- Do not make S4R assumptions in material or integration code. diff --git a/phases/1-linear-static-mitc4/index.json b/phases/1-linear-static-mitc4/index.json deleted file mode 100644 index 28e410f..0000000 --- a/phases/1-linear-static-mitc4/index.json +++ /dev/null @@ -1,101 +0,0 @@ -{ - "project": "FESA", - "phase": "1-linear-static-mitc4", - "steps": [ - { - "step": 0, - "name": "build-test-harness", - "status": "completed", - "summary": "Added CMake/CTest build harness and wired validate_workspace.py to configure, build, and run tests." - }, - { - "step": 1, - "name": "core-types-diagnostics", - "status": "completed", - "summary": "Added int64/double core aliases, DOF mapping, diagnostics, and tests." - }, - { - "step": 2, - "name": "domain-model", - "status": "completed", - "summary": "Added Phase 1 Domain entities for nodes, MITC4 elements, sets, materials, shell sections, loads, boundaries, and steps." - }, - { - "step": 3, - "name": "parser-foundation", - "status": "completed", - "summary": "Added Abaqus keyword parsing foundation with diagnostics and exact keyword dispatch." - }, - { - "step": 4, - "name": "parser-phase1-subset", - "status": "completed", - "summary": "Implemented Phase 1 Abaqus subset parsing plus explicit unsupported-feature rejection tests." - }, - { - "step": 5, - "name": "domain-validation-diagnostics", - "status": "completed", - "summary": "Added Domain validation for missing references, properties, materials, targets, and singular-prone states." - }, - { - "step": 6, - "name": "analysis-model-dof-manager", - "status": "completed", - "summary": "Added six-DOF DofManager with constrained/free partitioning, equation numbering, and full-vector reconstruction." - }, - { - "step": 7, - "name": "math-solver-adapters", - "status": "completed", - "summary": "Added dense test matrix and deterministic Gaussian solver adapter with singular diagnostics." - }, - { - "step": 8, - "name": "results-writer-minimal", - "status": "completed", - "summary": "Added in-memory step/frame/field results for mandatory nodal U and RF outputs." - }, - { - "step": 9, - "name": "reference-displacement-comparator", - "status": "completed", - "summary": "Added Abaqus displacement CSV loader and node-id-based comparator with tolerance diagnostics." - }, - { - "step": 10, - "name": "mitc4-formulation-closure", - "status": "completed", - "summary": "Closed the Phase 1 MITC4 baseline decisions for tying points, local basis, integration, drilling scale, and U/RF output scope." - }, - { - "step": 11, - "name": "mitc4-element-baseline", - "status": "completed", - "summary": "Implemented baseline MITC4 stiffness kernel with shape functions, MITC shear interpolation, local basis, and drilling stabilization tests." - }, - { - "step": 12, - "name": "assembly-reaction-recovery", - "status": "completed", - "summary": "Added full-system assembly and RF recovery path using K_full * U_full - F_full." - }, - { - "step": 13, - "name": "linear-static-analysis-path", - "status": "completed", - "summary": "Added LinearStaticAnalysis path that assembles, solves, reconstructs full U, recovers RF, and writes results." - }, - { - "step": 14, - "name": "stored-reference-regression", - "status": "blocked", - "blocked_reason": "No Phase 1-compatible Abaqus TYPE=S4 reference input with matching *_displacements.csv is available. Existing quad_01 input contains S4R, Part/Assembly/Instance, Density, and NLGEOM=YES and is tested only as unsupported provenance/comparator format." - }, - { - "step": 15, - "name": "phase1-evaluator-closeout", - "status": "pending" - } - ] -} diff --git a/phases/1-linear-static-mitc4/step0.md b/phases/1-linear-static-mitc4/step0.md deleted file mode 100644 index 0565208..0000000 --- a/phases/1-linear-static-mitc4/step0.md +++ /dev/null @@ -1,73 +0,0 @@ -# Step 0: build-test-harness - -## Sprint Contract - -### Objective -Establish the C++ project skeleton, build system, test framework, and validation script integration required before solver code begins. - -### Required Reading -- /AGENTS.md -- /PROGRESS.md -- /PLAN.md -- /docs/README.md -- /docs/HARNESS_ENGINEERING.md -- /docs/PRD.md -- /docs/ARCHITECTURE.md -- /docs/ADR.md -- /docs/VERIFICATION_PLAN.md - -### Scope -- Choose the Phase 1 build/test path. CMake is recommended unless project constraints require another choice. -- Add a minimal C++17 project skeleton with one smoke-test target. -- Wire `scripts/validate_workspace.py` to run real configured checks. -- Document the build-system decision in ADR if it changes or becomes explicit. - -### Allowed Files -- `CMakeLists.txt` or the selected build-system files -- `src/**` -- `include/**` -- `tests/**` -- `scripts/validate_workspace.py` -- `README.md` -- `docs/ADR.md` -- `PLAN.md` -- `PROGRESS.md` -- `phases/1-linear-static-mitc4/index.json` - -### Explicit Non-Goals -- Do not implement solver, parser, MITC4, results, or reference-comparison behavior. -- Do not introduce MKL, TBB, or HDF5 APIs into solver core during this setup step. -- Do not mark readiness blockers R-004 through R-011 as solved unless this step actually resolves them. - -### Tests To Write First -- A minimal C++ smoke test that fails before the test harness is connected and passes after the harness is wired. -- A validation-script test or self-check proving `python scripts/validate_workspace.py` reports the configured build/test command status. - -### Reference Artifacts -- None. This step does not use `references/*.inp` or `references/*_displacements.csv`. - -### Acceptance Commands -```bash -python scripts/validate_workspace.py -``` - -### Evaluator Checklist -- The project builds with C++17 or newer. -- The chosen test framework runs through the validation script. -- `scripts/validate_workspace.py` no longer reports that no checks are configured. -- The build-system decision is documented if new or changed. -- No solver behavior was implemented. - -### Handoff Requirements -- Update `PROGRESS.md` with changed files, validation output, and any build-system caveats. -- Update `PLAN.md` only for changed future work or resolved blockers. -- Update the matching phase index entry: use `completed` with a one-line `summary`, or `blocked` with `blocked_reason`, or `error` with `error_message`. - -## Verification -1. Run the acceptance command. -2. Confirm the smoke test is executed by the validation script. -3. Inspect the diff for accidental solver behavior or dependency leakage. - -## Do Not -- Do not expand Phase 1 scope to make a build demo more impressive. -- Do not commit generated build artifacts. diff --git a/phases/1-linear-static-mitc4/step1.md b/phases/1-linear-static-mitc4/step1.md deleted file mode 100644 index ee644bb..0000000 --- a/phases/1-linear-static-mitc4/step1.md +++ /dev/null @@ -1,68 +0,0 @@ -# Step 1: core-types-diagnostics - -## Sprint Contract - -### Objective -Add core numeric/id types, shell DOF enumeration, diagnostics primitives, and error-result conventions used by later layers. - -### Required Reading -- /AGENTS.md -- /PROGRESS.md -- /PLAN.md -- /docs/README.md -- /docs/HARNESS_ENGINEERING.md -- /docs/ARCHITECTURE.md -- /docs/ADR.md -- /docs/NUMERICAL_CONVENTIONS.md -- /phases/1-linear-static-mitc4/step0.md - -### Scope -- Define int64-based ids, indices, and equation-number types. -- Define `double` as the default scalar precision. -- Define the six shell DOFs in the documented order: `UX`, `UY`, `UZ`, `RX`, `RY`, `RZ`. -- Add diagnostics/result primitives with enough context for parser, validation, and solver errors. - -### Allowed Files -- `include/**` -- `src/**` -- `tests/**` -- `docs/ADR.md` -- `PLAN.md` -- `PROGRESS.md` -- `phases/1-linear-static-mitc4/index.json` - -### Explicit Non-Goals -- Do not add Domain, parser, DofManager, sparse matrix, or MITC4 element behavior. -- Do not use 32-bit ids, equation numbers, or sparse indices. -- Do not encode global equation ids inside node or element data structures. - -### Tests To Write First -- Unit tests for id/index/equation type sizes and signedness expectations. -- Unit tests for DOF ordering, component labels, and stable integer mapping. -- Unit tests for diagnostic severity, code, message, and source-location payloads. - -### Reference Artifacts -- None. - -### Acceptance Commands -```bash -python scripts/validate_workspace.py -``` - -### Evaluator Checklist -- Core types are reusable without depending on parser, analysis, MKL, TBB, or HDF5. -- DOF labels and order match `docs/NUMERICAL_CONVENTIONS.md`. -- Diagnostics can carry actionable context for unsupported input and singular systems. -- Tests were added before behavior and pass through validation. - -### Handoff Requirements -- Record completed work and validation in `PROGRESS.md`. -- Keep `PLAN.md` focused on future blockers only. -- Update the matching phase index entry: use `completed` with a one-line `summary`, or `blocked` with `blocked_reason`, or `error` with `error_message`. - -## Verification -1. Run the acceptance command. -2. Inspect tests for explicit int64 and DOF-order assertions. - -## Do Not -- Do not infer future element or solver APIs beyond what this contract requires. diff --git a/phases/1-linear-static-mitc4/step10.md b/phases/1-linear-static-mitc4/step10.md deleted file mode 100644 index 8fcd932..0000000 --- a/phases/1-linear-static-mitc4/step10.md +++ /dev/null @@ -1,69 +0,0 @@ -# Step 10: mitc4-formulation-closure - -## Sprint Contract - -### Objective -Close the MITC4 formulation decisions required before element implementation: transverse shear tying, local basis, integration ordering, drilling stiffness default, and Phase 1 output scope. - -### Required Reading -- /AGENTS.md -- /PROGRESS.md -- /PLAN.md -- /docs/README.md -- /docs/HARNESS_ENGINEERING.md -- /docs/PRD.md -- /docs/NUMERICAL_CONVENTIONS.md -- /docs/RESULTS_SCHEMA.md -- /docs/MITC4_FORMULATION.md -- /docs/VERIFICATION_PLAN.md - -### Scope -- Update `docs/MITC4_FORMULATION.md` with the exact baseline formulas and implementation checklist needed for coding. -- Resolve or explicitly defer R-004, R-005, R-006, and R-007. -- Record literature/source basis and benchmark expectations without requiring local Abaqus execution. -- Update ADR or PLAN only when a durable design decision changes. - -### Allowed Files -- `docs/MITC4_FORMULATION.md` -- `docs/RESULTS_SCHEMA.md` -- `docs/VERIFICATION_PLAN.md` -- `docs/ADR.md` -- `PLAN.md` -- `PROGRESS.md` -- `phases/1-linear-static-mitc4/index.json` - -### Explicit Non-Goals -- Do not implement C++ MITC4 code in this step. -- Do not invent formulas from memory without documenting the source or derivation. -- Do not expand Phase 1 outputs beyond resolved R-007. - -### Tests To Write First -- No C++ tests are required unless executable formulation checks already exist. -- Add documentation checklist items or review notes that can be evaluated before Step 11. - -### Reference Artifacts -- None required. Existing `references/quad_01*` may inform future benchmark planning but is not a Phase 1 MITC4 acceptance case. - -### Acceptance Commands -```bash -python scripts/validate_workspace.py -``` - -### Evaluator Checklist -- Transverse shear tying equations are explicit enough to implement. -- Local shell basis construction is explicit for flat and non-flat quads. -- Drilling stiffness default scale and parameter name are finalized. -- Integration ordering and output scope are finalized or explicitly deferred with downstream blockers. -- Step 11 remains blocked if any required formulation item is unresolved. - -### Handoff Requirements -- Record closed decisions and remaining risks in `PROGRESS.md`. -- Remove or update resolved readiness tasks in `PLAN.md`. -- Update the matching phase index entry: use `completed` with a one-line `summary`, or `blocked` with `blocked_reason`, or `error` with `error_message`. - -## Verification -1. Run the acceptance command. -2. Review docs for implementable formulas and unresolved placeholders. - -## Do Not -- Do not let implementation begin on undocumented MITC4 formulas. diff --git a/phases/1-linear-static-mitc4/step11.md b/phases/1-linear-static-mitc4/step11.md deleted file mode 100644 index 5e5c22f..0000000 --- a/phases/1-linear-static-mitc4/step11.md +++ /dev/null @@ -1,71 +0,0 @@ -# Step 11: mitc4-element-baseline - -## Sprint Contract - -### Objective -Implement the baseline MITC4 element utilities and stiffness path after the formulation gate is closed. - -### Required Reading -- /AGENTS.md -- /PROGRESS.md -- /PLAN.md -- /docs/README.md -- /docs/HARNESS_ENGINEERING.md -- /docs/PRD.md -- /docs/ARCHITECTURE.md -- /docs/ADR.md -- /docs/NUMERICAL_CONVENTIONS.md -- /docs/MITC4_FORMULATION.md -- /phases/1-linear-static-mitc4/step10.md - -### Scope -- Implement shape functions, derivatives, local basis utilities, MITC shear tying per the finalized formulation, stiffness calculation, and drilling stiffness parameter path. -- Keep the element behind runtime-polymorphic element interfaces. -- Produce a 24x24 element stiffness for four-node shell elements with six DOFs per node. - -### Allowed Files -- `include/**` -- `src/**` -- `tests/**` -- `docs/MITC4_FORMULATION.md` only for errata discovered during implementation -- `PLAN.md` -- `PROGRESS.md` -- `phases/1-linear-static-mitc4/index.json` - -### Explicit Non-Goals -- Do not implement S4R, reduced integration, hourglass control, nonlinear kinematics, pressure loads, composites, thermal coupling, or mesh quality checks. -- Do not implement formulas that are still placeholders in `docs/MITC4_FORMULATION.md`. -- Do not optimize element kernels before baseline benchmark behavior is established. - -### Tests To Write First -- Shape function partition and nodal interpolation tests. -- Derivative and Jacobian/local basis tests. -- 24x24 dimension and symmetry tests. -- Rigid body or patch-style checks appropriate to the documented formulation. -- Drilling stiffness sensitivity tests. - -### Reference Artifacts -- None required for this unit-level step. Stored-reference regression is Step 14. - -### Acceptance Commands -```bash -python scripts/validate_workspace.py -``` - -### Evaluator Checklist -- Step 10 formulation decisions are closed before code is added. -- Element interface uses runtime polymorphism as documented. -- Stiffness output dimension, DOF order, symmetry, and drilling behavior are tested. -- No S4R or nonlinear behavior was introduced. - -### Handoff Requirements -- Record implemented formulation surface and validation in `PROGRESS.md`. -- Update `PLAN.md` if benchmark or formulation risks remain. -- Update the matching phase index entry: use `completed` with a one-line `summary`, or `blocked` with `blocked_reason`, or `error` with `error_message`. - -## Verification -1. Run the acceptance command. -2. Inspect tests against the finalized MITC4 formulation checklist. - -## Do Not -- Do not hide formulation uncertainty in comments or TODOs inside executable code. diff --git a/phases/1-linear-static-mitc4/step12.md b/phases/1-linear-static-mitc4/step12.md deleted file mode 100644 index 051f1b2..0000000 --- a/phases/1-linear-static-mitc4/step12.md +++ /dev/null @@ -1,71 +0,0 @@ -# Step 12: assembly-reaction-recovery - -## Sprint Contract - -### Objective -Implement assembly of element stiffness/load contributions into full and reduced system data while preserving full-space data for reactions. - -### Required Reading -- /AGENTS.md -- /PROGRESS.md -- /PLAN.md -- /docs/README.md -- /docs/HARNESS_ENGINEERING.md -- /docs/ARCHITECTURE.md -- /docs/ADR.md -- /docs/NUMERICAL_CONVENTIONS.md -- /docs/RESULTS_SCHEMA.md -- /phases/1-linear-static-mitc4/step6.md -- /phases/1-linear-static-mitc4/step7.md -- /phases/1-linear-static-mitc4/step11.md - -### Scope -- Assemble global stiffness and load data from active elements and nodal loads. -- Create reduced free-DOF systems using `DofManager` mappings. -- Preserve or reconstruct full-space `K_full`, `F_full`, and `U_full` data needed for `RF = K_full * U_full - F_full`. -- Add assembly diagnostics for missing contributions and singular-prone untouched DOFs where detectable. - -### Allowed Files -- `include/**` -- `src/**` -- `tests/**` -- `PLAN.md` -- `PROGRESS.md` -- `phases/1-linear-static-mitc4/index.json` - -### Explicit Non-Goals -- Do not implement the full `LinearStaticAnalysis` orchestration here. -- Do not compute reactions from reduced quantities only. -- Do not add pressure loads, nonzero prescribed displacement, or nonlinear residual assembly. - -### Tests To Write First -- Assembly tests for simple element/load contributions into full and reduced matrices/vectors. -- Constrained elimination tests using known DOF mappings. -- Full-vector reaction recovery tests using `RF = K_full * U_full - F_full`. -- Missing contribution or untouched free DOF diagnostic tests. - -### Reference Artifacts -- None required. RF reference CSV is not available; use internal equilibrium/reaction tests for this step. - -### Acceptance Commands -```bash -python scripts/validate_workspace.py -``` - -### Evaluator Checklist -- Full-space reaction data is preserved. -- Reduced system assembly matches constrained/free DOF mapping. -- Reaction tests use full vectors and the documented sign convention. -- Unsupported loads and nonlinear behavior are not introduced. - -### Handoff Requirements -- Record assembly and RF recovery behavior in `PROGRESS.md`. -- Update `PLAN.md` if R-010 remains unresolved or becomes resolved. -- Update the matching phase index entry: use `completed` with a one-line `summary`, or `blocked` with `blocked_reason`, or `error` with `error_message`. - -## Verification -1. Run the acceptance command. -2. Inspect reaction tests for full-vector recovery. - -## Do Not -- Do not allow reduced-system convenience to overwrite the documented RF convention. diff --git a/phases/1-linear-static-mitc4/step13.md b/phases/1-linear-static-mitc4/step13.md deleted file mode 100644 index b374fcf..0000000 --- a/phases/1-linear-static-mitc4/step13.md +++ /dev/null @@ -1,71 +0,0 @@ -# Step 13: linear-static-analysis-path - -## Sprint Contract - -### Objective -Implement the `LinearStaticAnalysis` Template Method path from parsed Domain to solved state and written `U`/`RF` results. - -### Required Reading -- /AGENTS.md -- /PROGRESS.md -- /PLAN.md -- /docs/README.md -- /docs/HARNESS_ENGINEERING.md -- /docs/PRD.md -- /docs/ARCHITECTURE.md -- /docs/ADR.md -- /docs/NUMERICAL_CONVENTIONS.md -- /docs/RESULTS_SCHEMA.md -- /docs/VERIFICATION_PLAN.md -- /phases/1-linear-static-mitc4/step8.md -- /phases/1-linear-static-mitc4/step12.md - -### Scope -- Implement the linear static analysis strategy using the documented common flow: validate, build model, number DOFs, assemble, constrain, solve, reconstruct, recover RF, update state, write results. -- Store mutable physical quantities and iteration/result state in `AnalysisState`. -- Route solving through the linear solver interface. -- Emit minimum Phase 1 result fields `U` and `RF`. - -### Allowed Files -- `include/**` -- `src/**` -- `tests/**` -- `PLAN.md` -- `PROGRESS.md` -- `phases/1-linear-static-mitc4/index.json` - -### Explicit Non-Goals -- Do not implement nonlinear, dynamic, thermal, composite, or contact workflows. -- Do not bypass `DofManager`, assembler, solver interface, or result writer boundaries. -- Do not compare against stored Abaqus references in this step; that is Step 14. - -### Tests To Write First -- End-to-end small-model test using a deterministic element or minimal fixture where expected `U` and `RF` are known. -- Singular-system failure test through the analysis path. -- Result-emission test for `U` and `RF` fields and component order. - -### Reference Artifacts -- None required for this orchestration step. - -### Acceptance Commands -```bash -python scripts/validate_workspace.py -``` - -### Evaluator Checklist -- Strategy + Template Method architecture is visible and not hardwired to only one future analysis type. -- `AnalysisState` owns mutable solve/result data. -- `RF` uses full-vector recovery. -- Results match the documented step/frame/field layout. - -### Handoff Requirements -- Record analysis behavior, tests, and validation in `PROGRESS.md`. -- Update `PLAN.md` if Phase 1 end-to-end risks remain. -- Update the matching phase index entry: use `completed` with a one-line `summary`, or `blocked` with `blocked_reason`, or `error` with `error_message`. - -## Verification -1. Run the acceptance command. -2. Inspect the analysis path for architecture-boundary shortcuts. - -## Do Not -- Do not make the first end-to-end path depend on local Abaqus execution. diff --git a/phases/1-linear-static-mitc4/step14.md b/phases/1-linear-static-mitc4/step14.md deleted file mode 100644 index d842fa5..0000000 --- a/phases/1-linear-static-mitc4/step14.md +++ /dev/null @@ -1,74 +0,0 @@ -# Step 14: stored-reference-regression - -## Sprint Contract - -### Objective -Add stored-reference regression coverage using accepted Phase 1-compatible cases and preserve `quad_01` as a compatibility/provenance reference. - -### Required Reading -- /AGENTS.md -- /PROGRESS.md -- /PLAN.md -- /docs/README.md -- /docs/HARNESS_ENGINEERING.md -- /docs/ABAQUS_INPUT_SUBSET.md -- /docs/VERIFICATION_PLAN.md -- /docs/RESULTS_SCHEMA.md -- /references/README.md -- /phases/1-linear-static-mitc4/step9.md -- /phases/1-linear-static-mitc4/step13.md - -### Scope -- Add automated regression tests comparing FESA `U` output to stored `*_displacements.csv` for Phase 1-compatible `TYPE=S4` linear static cases. -- Keep `references/quad_01.inp` documented as S4R/NLGEOM provenance and future compatibility target, not a passing Phase 1 parser case. -- Add or document reference-case requirements for single-element, multi-element, and curved-shell cases. -- Use internal equilibrium tests for RF unless `*_reactions.csv` artifacts are added. - -### Allowed Files -- `include/**` -- `src/**` -- `tests/**` -- `references/**` -- `docs/VERIFICATION_PLAN.md` -- `docs/ABAQUS_INPUT_SUBSET.md` only for reference compatibility notes -- `PLAN.md` -- `PROGRESS.md` -- `phases/1-linear-static-mitc4/index.json` - -### Explicit Non-Goals -- Do not require Abaqus execution locally or in CI. -- Do not make `quad_01.inp` pass by adding S4R, Part/Assembly/Instance, or NLGEOM support. -- Do not accept missing reference comparisons silently. - -### Tests To Write First -- Regression test harness test that fails when a required displacement CSV is missing or malformed. -- At least one Phase 1-compatible displacement comparison test when a matching reference case exists. -- Negative test that `quad_01.inp` remains unsupported for Phase 1 parsing while its CSV can still validate comparator format. - -### Reference Artifacts -- Required solver regression: at least one Phase 1-compatible `TYPE=S4` `.inp` and matching `*_displacements.csv`. -- Format/provenance only: `references/quad_01.inp` and `references/quad_01_displacements.csv`. -- Optional RF artifact: `*_reactions.csv`; otherwise RF is checked by internal equilibrium/reaction tests. - -### Acceptance Commands -```bash -python scripts/validate_workspace.py -``` - -### Evaluator Checklist -- At least one accepted Phase 1 reference comparison runs if the artifact exists. -- Missing required reference artifacts produce a documented block or failing test, not a silent pass. -- `quad_01` remains out of Phase 1 parser support. -- Reference comparisons use documented columns, node-id matching, and tolerances. - -### Handoff Requirements -- Record reference cases, comparison results, and missing artifacts in `PROGRESS.md`. -- Update `PLAN.md` with remaining reference additions for Phase 1 completion or Phase 2. -- Update the matching phase index entry: use `completed` with a one-line `summary`, or `blocked` with `blocked_reason`, or `error` with `error_message`. - -## Verification -1. Run the acceptance command. -2. Inspect regression logs or test output for actual CSV comparison. - -## Do Not -- Do not relax parser restrictions to make an existing reference input pass. diff --git a/phases/1-linear-static-mitc4/step15.md b/phases/1-linear-static-mitc4/step15.md deleted file mode 100644 index 79e8529..0000000 --- a/phases/1-linear-static-mitc4/step15.md +++ /dev/null @@ -1,75 +0,0 @@ -# Step 15: phase1-evaluator-closeout - -## Sprint Contract - -### Objective -Run the full Phase 1 evaluator pass, close documentation and handoff gaps, and record Phase 2 carryovers. - -### Required Reading -- /AGENTS.md -- /PROGRESS.md -- /PLAN.md -- /docs/README.md -- /docs/HARNESS_ENGINEERING.md -- /docs/PRD.md -- /docs/ARCHITECTURE.md -- /docs/ADR.md -- /docs/NUMERICAL_CONVENTIONS.md -- /docs/ABAQUS_INPUT_SUBSET.md -- /docs/VERIFICATION_PLAN.md -- /docs/RESULTS_SCHEMA.md -- /docs/MITC4_FORMULATION.md -- /references/README.md -- /phases/1-linear-static-mitc4/index.json - -### Scope -- Evaluate all Phase 1 Definition of Done items from `PLAN.md` and `docs/PRD.md`. -- Run the full validation command set and inspect reference-comparison evidence. -- Produce a concise evaluator report in `PROGRESS.md` or a linked docs artifact. -- Move unresolved Phase 2 or deferred work into `PLAN.md`. - -### Allowed Files -- `docs/**` -- `tests/**` only for evaluator-owned missing validation metadata, not feature implementation -- `references/**` only for documentation or final accepted artifact notes -- `PLAN.md` -- `PROGRESS.md` -- `phases/1-linear-static-mitc4/index.json` - -### Explicit Non-Goals -- Do not implement missing solver features in the evaluator step. -- Do not mark Phase 1 complete if validation, reference comparison, MITC4 formulation, RF recovery, or parser rejection evidence is missing. -- Do not hide remaining risks in prose without putting future tasks in `PLAN.md`. - -### Tests To Write First -- No new implementation tests are expected unless the evaluator identifies missing metadata tests that are needed to prove acceptance. - -### Reference Artifacts -- Required: all accepted Phase 1-compatible reference cases and their `*_displacements.csv` files. -- Optional: `*_reactions.csv`; otherwise evaluator must verify RF through internal equilibrium tests. - -### Acceptance Commands -```bash -python scripts/validate_workspace.py -``` - -### Evaluator Checklist -- Phase 1 Definition of Done is checked item by item. -- Parser rejects unsupported Abaqus features with tests. -- MITC4 baseline tests and documented formulation agree. -- `U` reference comparison evidence exists for accepted cases. -- `RF` is recovered from full vectors and verified by available reference or equilibrium tests. -- `PLAN.md` and `PROGRESS.md` are current. - -### Handoff Requirements -- Record final verdict, validation output, reference evidence, and remaining risks in `PROGRESS.md`. -- Move future work, Phase 2 candidates, and deferred features into `PLAN.md`. -- Update the matching phase index entry: use `completed` with a one-line `summary`, or `blocked` with `blocked_reason`, or `error` with `error_message`; update the top-level phase status if execution is complete. - -## Verification -1. Run the acceptance command. -2. Perform the evaluator checklist against changed files and test evidence. -3. Confirm no phase step remains with stale status or missing handoff notes. - -## Do Not -- Do not use evaluator closeout as a place to sneak in broad implementation fixes. diff --git a/phases/1-linear-static-mitc4/step2.md b/phases/1-linear-static-mitc4/step2.md deleted file mode 100644 index 445bd16..0000000 --- a/phases/1-linear-static-mitc4/step2.md +++ /dev/null @@ -1,69 +0,0 @@ -# Step 2: domain-model - -## Sprint Contract - -### Objective -Implement immutable-ish Domain entities for Phase 1 input data: nodes, elements, sets, materials, shell sections, loads, boundaries, and steps. - -### Required Reading -- /AGENTS.md -- /PROGRESS.md -- /PLAN.md -- /docs/README.md -- /docs/HARNESS_ENGINEERING.md -- /docs/PRD.md -- /docs/ARCHITECTURE.md -- /docs/ADR.md -- /docs/NUMERICAL_CONVENTIONS.md -- /docs/ABAQUS_INPUT_SUBSET.md -- /phases/1-linear-static-mitc4/step1.md - -### Scope -- Add Domain-level value objects and containers preserving input ids and labels. -- Support Phase 1 shell elements as Abaqus `TYPE=S4` mapped to FESA MITC4. -- Represent node sets, element sets, linear elastic materials, shell sections, fixed boundary conditions, nodal loads, and linear static steps. -- Provide lookup APIs and duplicate-id diagnostics without storing equation ids in entities. - -### Allowed Files -- `include/**` -- `src/**` -- `tests/**` -- `PLAN.md` -- `PROGRESS.md` -- `phases/1-linear-static-mitc4/index.json` - -### Explicit Non-Goals -- Do not parse Abaqus files in this step. -- Do not create `AnalysisModel`, `AnalysisState`, or `DofManager`. -- Do not add support for S4R, pressure loads, nonzero prescribed displacement, Part/Assembly/Instance, dynamics, nonlinear steps, or thermal coupling. - -### Tests To Write First -- Domain construction tests for nodes, elements, sets, materials, shell sections, loads, boundaries, and steps. -- Duplicate id/name tests with diagnostics. -- Lookup tests proving labels and ids are preserved. - -### Reference Artifacts -- None. - -### Acceptance Commands -```bash -python scripts/validate_workspace.py -``` - -### Evaluator Checklist -- Domain preserves model definition and is treated as immutable after construction. -- Node and element objects do not own global equation numbers. -- Phase 1 object set matches `docs/ABAQUS_INPUT_SUBSET.md`. -- Tests cover duplicate and missing lookup behavior. - -### Handoff Requirements -- Record changed files and validation in `PROGRESS.md`. -- Update future tasks in `PLAN.md` only if Domain scope changes. -- Update the matching phase index entry: use `completed` with a one-line `summary`, or `blocked` with `blocked_reason`, or `error` with `error_message`. - -## Verification -1. Run the acceptance command. -2. Inspect Domain APIs for architecture-boundary drift. - -## Do Not -- Do not make Domain depend on solver adapters, analysis strategies, or result writers. diff --git a/phases/1-linear-static-mitc4/step3.md b/phases/1-linear-static-mitc4/step3.md deleted file mode 100644 index 3d56332..0000000 --- a/phases/1-linear-static-mitc4/step3.md +++ /dev/null @@ -1,66 +0,0 @@ -# Step 3: parser-foundation - -## Sprint Contract - -### Objective -Implement the Abaqus input lexical/keyword parser foundation plus Factory + Registry plumbing for Phase 1 objects. - -### Required Reading -- /AGENTS.md -- /PROGRESS.md -- /PLAN.md -- /docs/README.md -- /docs/HARNESS_ENGINEERING.md -- /docs/ARCHITECTURE.md -- /docs/ADR.md -- /docs/ABAQUS_INPUT_SUBSET.md -- /phases/1-linear-static-mitc4/step2.md - -### Scope -- Tokenize Abaqus keyword lines, parameters, data lines, comments, continuations if documented, and source locations. -- Add a parser result shape that returns Domain data or diagnostics. -- Add keyword Factory + Registry structure without completing every Phase 1 keyword. -- Add smoke parsing for a tiny supported subset such as `*Node` and `*Element, TYPE=S4`. - -### Allowed Files -- `include/**` -- `src/**` -- `tests/**` -- `PLAN.md` -- `PROGRESS.md` -- `phases/1-linear-static-mitc4/index.json` - -### Explicit Non-Goals -- Do not complete all Phase 1 Abaqus keywords in this step. -- Do not silently accept unsupported keywords or unknown parameters. -- Do not parse `quad_01.inp` as a supported Phase 1 case. - -### Tests To Write First -- Lexer tests for keyword names, parameters, comments, blank lines, and source locations. -- Registry tests for supported and unsupported keyword dispatch. -- Parser smoke tests for minimal nodes and S4 elements. - -### Reference Artifacts -- None. Existing `references/quad_01.inp` is not a Phase 1 parser acceptance case. - -### Acceptance Commands -```bash -python scripts/validate_workspace.py -``` - -### Evaluator Checklist -- Unsupported input produces diagnostics with line context. -- Factory + Registry is present and parser logic is not hardwired into Domain constructors. -- Parser tests do not expand Phase 1 support beyond `docs/ABAQUS_INPUT_SUBSET.md`. - -### Handoff Requirements -- Record progress and validation in `PROGRESS.md`. -- Update `PLAN.md` if parser-scope risks change. -- Update the matching phase index entry: use `completed` with a one-line `summary`, or `blocked` with `blocked_reason`, or `error` with `error_message`. - -## Verification -1. Run the acceptance command. -2. Inspect parser tests for line-numbered diagnostic coverage. - -## Do Not -- Do not normalize away Abaqus labels that later diagnostics or references need. diff --git a/phases/1-linear-static-mitc4/step4.md b/phases/1-linear-static-mitc4/step4.md deleted file mode 100644 index 45c8722..0000000 --- a/phases/1-linear-static-mitc4/step4.md +++ /dev/null @@ -1,68 +0,0 @@ -# Step 4: parser-phase1-subset - -## Sprint Contract - -### Objective -Complete Phase 1 Abaqus input subset behavior and explicit rejection diagnostics for unsupported features. - -### Required Reading -- /AGENTS.md -- /PROGRESS.md -- /PLAN.md -- /docs/README.md -- /docs/HARNESS_ENGINEERING.md -- /docs/PRD.md -- /docs/ARCHITECTURE.md -- /docs/ABAQUS_INPUT_SUBSET.md -- /docs/VERIFICATION_PLAN.md -- /phases/1-linear-static-mitc4/step3.md - -### Scope -- Parse `*Node`, `*Element`, `*Nset`, `*Elset`, `*Material`, `*Elastic`, `*Shell Section`, `*Boundary`, `*Cload`, `*Step`, `*Static`, and `*End Step` as documented. -- Support generated and explicit set definitions only if documented in `docs/ABAQUS_INPUT_SUBSET.md`. -- Reject unsupported Abaqus constructs with clear diagnostics. - -### Allowed Files -- `include/**` -- `src/**` -- `tests/**` -- `docs/ABAQUS_INPUT_SUBSET.md` only for clarification of already agreed behavior -- `PLAN.md` -- `PROGRESS.md` -- `phases/1-linear-static-mitc4/index.json` - -### Explicit Non-Goals -- Do not support `S4R`, `Part`, `Assembly`, `Instance`, `*Include`, pressure loads, nonzero prescribed displacement, `*Density`, or `NLGEOM=YES`. -- Do not make `quad_01.inp` pass as Phase 1 input. -- Do not add geometric/material nonlinearity, dynamics, thermal coupling, or mesh quality diagnostics. - -### Tests To Write First -- Positive parser tests for each Phase 1 supported keyword. -- Negative parser tests for `S4R`, `Part/Assembly/Instance`, `*Include`, `*Density`, pressure loads, nonzero prescribed displacement, and `NLGEOM=YES`. -- Diagnostics tests for missing required parameters, malformed data rows, and invalid labels. - -### Reference Artifacts -- Format/provenance only: `references/quad_01.inp` may be used to assert that unsupported features are rejected or documented, not as a passing parser case. - -### Acceptance Commands -```bash -python scripts/validate_workspace.py -``` - -### Evaluator Checklist -- Supported keyword coverage matches `docs/ABAQUS_INPUT_SUBSET.md`. -- Unsupported features fail loudly and specifically. -- S4 maps to the internal MITC4 shell element type; S4R remains unsupported. -- Tests include both successful subset parsing and negative diagnostics. - -### Handoff Requirements -- Record parser coverage, unsupported-feature behavior, and validation in `PROGRESS.md`. -- Update `PLAN.md` if a parser blocker remains. -- Update the matching phase index entry: use `completed` with a one-line `summary`, or `blocked` with `blocked_reason`, or `error` with `error_message`. - -## Verification -1. Run the acceptance command. -2. Inspect tests to ensure unsupported Abaqus features are not silently accepted. - -## Do Not -- Do not widen parser support because a stored reference file happens to contain a keyword. diff --git a/phases/1-linear-static-mitc4/step5.md b/phases/1-linear-static-mitc4/step5.md deleted file mode 100644 index 7487d3e..0000000 --- a/phases/1-linear-static-mitc4/step5.md +++ /dev/null @@ -1,69 +0,0 @@ -# Step 5: domain-validation-diagnostics - -## Sprint Contract - -### Objective -Implement Domain validation and singular-prone pre-solve diagnostics required before DOF and solver work. - -### Required Reading -- /AGENTS.md -- /PROGRESS.md -- /PLAN.md -- /docs/README.md -- /docs/HARNESS_ENGINEERING.md -- /docs/ARCHITECTURE.md -- /docs/ADR.md -- /docs/NUMERICAL_CONVENTIONS.md -- /docs/ABAQUS_INPUT_SUBSET.md -- /docs/VERIFICATION_PLAN.md -- /phases/1-linear-static-mitc4/step4.md - -### Scope -- Validate node, element, set, material, property, load, boundary, and step references. -- Add singular-prone diagnostics for no active elements, no material/property, no boundary constraints, untouched free DOFs when detectable, and invalid active step data. -- Keep diagnostics actionable and source-linked where parser information is available. - -### Allowed Files -- `include/**` -- `src/**` -- `tests/**` -- `docs/VERIFICATION_PLAN.md` only for clarification of diagnostic test expectations -- `PLAN.md` -- `PROGRESS.md` -- `phases/1-linear-static-mitc4/index.json` - -### Explicit Non-Goals -- Do not implement DofManager, assembly, or solver singularity handling in this step. -- Do not add mesh quality diagnostics; Phase 1 excludes them. -- Do not accept unsupported parser features to make validation easier. - -### Tests To Write First -- Validation tests for missing nodes, missing sets, missing materials, missing shell sections, invalid element connectivity, invalid loads, and invalid boundary references. -- Singular-prone tests for no active elements, unconstrained models, and untouched free DOF candidates where detectable without assembly. -- Diagnostic payload tests for code, severity, message, and source/model context. - -### Reference Artifacts -- None. - -### Acceptance Commands -```bash -python scripts/validate_workspace.py -``` - -### Evaluator Checklist -- Diagnostics align with `docs/NUMERICAL_CONVENTIONS.md` and `docs/VERIFICATION_PLAN.md`. -- Validation is separate from parsing and analysis execution. -- Mesh quality checks were not introduced. -- Tests cover failure modes, not only valid models. - -### Handoff Requirements -- Record validation coverage and command results in `PROGRESS.md`. -- Update `PLAN.md` if singular diagnostics require later solver support. -- Update the matching phase index entry: use `completed` with a one-line `summary`, or `blocked` with `blocked_reason`, or `error` with `error_message`. - -## Verification -1. Run the acceptance command. -2. Review negative tests for clear diagnostics and no mesh-quality drift. - -## Do Not -- Do not defer singular diagnostics entirely to the linear solver. diff --git a/phases/1-linear-static-mitc4/step6.md b/phases/1-linear-static-mitc4/step6.md deleted file mode 100644 index 044ca66..0000000 --- a/phases/1-linear-static-mitc4/step6.md +++ /dev/null @@ -1,70 +0,0 @@ -# Step 6: analysis-model-dof-manager - -## Sprint Contract - -### Objective -Implement `AnalysisModelBuilder` and `DofManager` for one active linear static step with six-DOF nodes, constrained/free mapping, equation numbering, sparse-pattern input, and full/reduced vector reconstruction. - -### Required Reading -- /AGENTS.md -- /PROGRESS.md -- /PLAN.md -- /docs/README.md -- /docs/HARNESS_ENGINEERING.md -- /docs/PRD.md -- /docs/ARCHITECTURE.md -- /docs/ADR.md -- /docs/NUMERICAL_CONVENTIONS.md -- /phases/1-linear-static-mitc4/step5.md - -### Scope -- Build an `AnalysisModel` active view from validated `Domain`. -- Assign six shell DOFs per active node in documented order. -- Apply fixed boundary conditions by constrained DOF elimination. -- Produce int64 equation numbers for free DOFs and mappings for constrained DOFs. -- Provide reconstruction helpers between reduced vectors and full vectors. - -### Allowed Files -- `include/**` -- `src/**` -- `tests/**` -- `PLAN.md` -- `PROGRESS.md` -- `phases/1-linear-static-mitc4/index.json` - -### Explicit Non-Goals -- Do not assemble element stiffness or loads. -- Do not store equation ids on Node or Element objects. -- Do not solve linear systems or write HDF5 results. - -### Tests To Write First -- DOF numbering tests for simple models with all six components. -- Constrained/free partition tests for fixed boundary conditions. -- Reduced/full reconstruction tests including constrained values set to zero for Phase 1. -- Sparse-pattern input tests using active element connectivity without assembling values. - -### Reference Artifacts -- None. - -### Acceptance Commands -```bash -python scripts/validate_workspace.py -``` - -### Evaluator Checklist -- `DofManager` owns global equation numbering. -- ids, sparse indices, and equations remain int64-based. -- Fixed constraints are handled by elimination, not penalty. -- Reconstruction supports later full-vector `RF` recovery. - -### Handoff Requirements -- Record DOF behavior, tests, and validation in `PROGRESS.md`. -- Update `PLAN.md` only for future work or resolved blockers. -- Update the matching phase index entry: use `completed` with a one-line `summary`, or `blocked` with `blocked_reason`, or `error` with `error_message`. - -## Verification -1. Run the acceptance command. -2. Inspect tests for constrained/free and full/reduced mapping edge cases. - -## Do Not -- Do not make constrained DOF elimination depend on reduced-only reaction recovery. diff --git a/phases/1-linear-static-mitc4/step7.md b/phases/1-linear-static-mitc4/step7.md deleted file mode 100644 index c21b8c5..0000000 --- a/phases/1-linear-static-mitc4/step7.md +++ /dev/null @@ -1,69 +0,0 @@ -# Step 7: math-solver-adapters - -## Sprint Contract - -### Objective -Implement vector, sparse matrix, and linear solver interfaces plus a deterministic test solver adapter while preserving future MKL/TBB adapter boundaries. - -### Required Reading -- /AGENTS.md -- /PROGRESS.md -- /PLAN.md -- /docs/README.md -- /docs/HARNESS_ENGINEERING.md -- /docs/ARCHITECTURE.md -- /docs/ADR.md -- /docs/NUMERICAL_CONVENTIONS.md -- /phases/1-linear-static-mitc4/step6.md - -### Scope -- Add minimal vector and sparse matrix abstractions needed by assembly and linear static solving. -- Add a linear solver interface with deterministic test implementation. -- Keep MKL/TBB-specific code behind adapter boundaries if any placeholder is added. -- Surface solver singularity diagnostics in a structured way. - -### Allowed Files -- `include/**` -- `src/**` -- `tests/**` -- `docs/ADR.md` if adapter decisions need clarification -- `PLAN.md` -- `PROGRESS.md` -- `phases/1-linear-static-mitc4/index.json` - -### Explicit Non-Goals -- Do not require Intel oneAPI installation for Phase 1 unit tests. -- Do not expose MKL, TBB, or HDF5 APIs in solver core headers. -- Do not implement element assembly or analysis algorithms in this step. - -### Tests To Write First -- Sparse pattern and insertion tests with int64 indices. -- Deterministic small linear solve tests. -- Singular solve diagnostic tests. -- Adapter-boundary tests or compile checks showing core APIs do not include MKL/TBB headers. - -### Reference Artifacts -- None. - -### Acceptance Commands -```bash -python scripts/validate_workspace.py -``` - -### Evaluator Checklist -- Core math interfaces are dependency-clean. -- Singular solver failures produce diagnostics, not crashes or silent NaNs. -- Tests cover sparse storage, solve success, and solve failure. -- Future MKL acceleration remains possible without changing solver core contracts. - -### Handoff Requirements -- Record math interfaces, validation, and adapter caveats in `PROGRESS.md`. -- Update `PLAN.md` if MKL/TBB integration becomes a future task. -- Update the matching phase index entry: use `completed` with a one-line `summary`, or `blocked` with `blocked_reason`, or `error` with `error_message`. - -## Verification -1. Run the acceptance command. -2. Inspect public headers for third-party API leakage. - -## Do Not -- Do not optimize sparse storage before correctness and testability are established. diff --git a/phases/1-linear-static-mitc4/step8.md b/phases/1-linear-static-mitc4/step8.md deleted file mode 100644 index d88c2fe..0000000 --- a/phases/1-linear-static-mitc4/step8.md +++ /dev/null @@ -1,71 +0,0 @@ -# Step 8: results-writer-minimal - -## Sprint Contract - -### Objective -Implement the minimal result model and writer boundary for Phase 1 `U` and `RF` step/frame/field outputs. - -### Required Reading -- /AGENTS.md -- /PROGRESS.md -- /PLAN.md -- /docs/README.md -- /docs/HARNESS_ENGINEERING.md -- /docs/ARCHITECTURE.md -- /docs/ADR.md -- /docs/NUMERICAL_CONVENTIONS.md -- /docs/RESULTS_SCHEMA.md -- /phases/1-linear-static-mitc4/step6.md -- /phases/1-linear-static-mitc4/step7.md - -### Scope -- Add result data structures for model ids/connectivity plus step/frame/field outputs. -- Support mandatory Phase 1 fields `U` and `RF` with components `UX`, `UY`, `UZ`, `RX`, `RY`, `RZ`. -- Add an in-memory or file-backed writer boundary as appropriate for the current build system. -- If HDF5 is implemented now, keep HDF5 APIs behind an adapter/wrapper. - -### Allowed Files -- `include/**` -- `src/**` -- `tests/**` -- `docs/RESULTS_SCHEMA.md` only for clarification of agreed schema details -- `PLAN.md` -- `PROGRESS.md` -- `phases/1-linear-static-mitc4/index.json` - -### Explicit Non-Goals -- Do not add stress, strain, shell force, history, nonlinear frame, thermal, or composite outputs unless R-007 is resolved and docs are updated first. -- Do not write RF from reduced vectors only. -- Do not expose HDF5 APIs in solver core headers. - -### Tests To Write First -- Schema tests for step/frame layout and mandatory field names. -- Component-label and ordering tests for `U` and `RF`. -- Model id/connectivity storage tests using int64 ids. -- Adapter-boundary tests if HDF5 is introduced. - -### Reference Artifacts -- None for solver regression. This step only prepares output data consumed by later comparison. - -### Acceptance Commands -```bash -python scripts/validate_workspace.py -``` - -### Evaluator Checklist -- Result layout follows `docs/RESULTS_SCHEMA.md`. -- `U` and `RF` have six components in the documented order. -- HDF5, if present, is isolated behind an adapter. -- No extra Phase 1 outputs were added without resolving R-007. - -### Handoff Requirements -- Record result writer scope and validation in `PROGRESS.md`. -- Update `PLAN.md` if R-007 changes or output scope remains blocked. -- Update the matching phase index entry: use `completed` with a one-line `summary`, or `blocked` with `blocked_reason`, or `error` with `error_message`. - -## Verification -1. Run the acceptance command. -2. Inspect tests for schema and component ordering. - -## Do Not -- Do not treat a file being created as proof that its schema matches the contract. diff --git a/phases/1-linear-static-mitc4/step9.md b/phases/1-linear-static-mitc4/step9.md deleted file mode 100644 index e2e6c6d..0000000 --- a/phases/1-linear-static-mitc4/step9.md +++ /dev/null @@ -1,74 +0,0 @@ -# Step 9: reference-displacement-comparator - -## Sprint Contract - -### Objective -Implement the `references/*_displacements.csv` loader and comparator for FESA `U` output. - -### Required Reading -- /AGENTS.md -- /PROGRESS.md -- /PLAN.md -- /docs/README.md -- /docs/HARNESS_ENGINEERING.md -- /docs/NUMERICAL_CONVENTIONS.md -- /docs/VERIFICATION_PLAN.md -- /docs/RESULTS_SCHEMA.md -- /references/README.md -- /phases/1-linear-static-mitc4/step8.md - -### Scope -- Load Abaqus displacement CSV files with columns `Node Label`, `U-U1`, `U-U2`, `U-U3`, `UR-UR1`, `UR-UR2`, `UR-UR3`. -- Compare by node id, not row order. -- Implement absolute and relative tolerance behavior with a documented default or configuration path. -- Report missing columns, duplicate nodes, missing nodes, nonnumeric values, and tolerance failures. - -### Allowed Files -- `include/**` -- `src/**` -- `tests/**` -- `references/**` only for small test fixtures or documentation updates -- `docs/VERIFICATION_PLAN.md` only for comparator tolerance clarification -- `docs/RESULTS_SCHEMA.md` only for schema clarification -- `PLAN.md` -- `PROGRESS.md` -- `phases/1-linear-static-mitc4/index.json` - -### Explicit Non-Goals -- Do not require Abaqus execution. -- Do not treat `references/quad_01.inp` as Phase 1 supported input. -- Do not compare RF here unless a `*_reactions.csv` contract is added. - -### Tests To Write First -- CSV header validation tests. -- Node-id matching and row-order independence tests. -- Absolute/relative tolerance pass/fail tests. -- Duplicate, missing, and malformed value diagnostics tests. -- Format-only smoke test using `references/quad_01_displacements.csv` if stable enough for CI. - -### Reference Artifacts -- Format-only: `references/quad_01_displacements.csv`. -- Required later for solver regression: at least one Phase 1-compatible `TYPE=S4` input and matching displacement CSV. - -### Acceptance Commands -```bash -python scripts/validate_workspace.py -``` - -### Evaluator Checklist -- Component mapping matches `docs/VERIFICATION_PLAN.md` and `docs/RESULTS_SCHEMA.md`. -- Comparison is by node id and independent of row order. -- Missing or malformed reference data fails with actionable diagnostics. -- `quad_01` is not used to imply S4R or nonlinear support. - -### Handoff Requirements -- Record comparator behavior, tolerance decision, and validation in `PROGRESS.md`. -- Update `PLAN.md` if R-009 or reference artifact blockers change. -- Update the matching phase index entry: use `completed` with a one-line `summary`, or `blocked` with `blocked_reason`, or `error` with `error_message`. - -## Verification -1. Run the acceptance command. -2. Inspect comparator tests for tolerance and malformed CSV behavior. - -## Do Not -- Do not silently skip reference nodes or components. diff --git a/phases/1-structure-alignment-refactor/index.json b/phases/1-structure-alignment-refactor/index.json deleted file mode 100644 index cca4212..0000000 --- a/phases/1-structure-alignment-refactor/index.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "project": "FESA", - "phase": "1-structure-alignment-refactor", - "steps": [ - { "step": 0, "name": "architecture-drift-audit", "status": "completed", "artifact": "step0-architecture-map.md" }, - { "step": 1, "name": "module-scaffold-and-facade", "status": "completed" }, - { "step": 2, "name": "core-domain-dof-extraction", "status": "completed" }, - { "step": 3, "name": "math-solver-extraction", "status": "completed" }, - { "step": 4, "name": "io-parser-extraction", "status": "completed" }, - { "step": 5, "name": "results-reference-extraction", "status": "completed" }, - { "step": 6, "name": "mitc4-geometry-strain-extraction", "status": "completed" }, - { "step": 7, "name": "mitc4-material-stiffness-extraction", "status": "completed" }, - { "step": 8, "name": "assembly-analysis-extraction", "status": "completed" }, - { "step": 9, "name": "architecture-evaluator-closeout", "status": "completed", "artifact": "step9-evaluator-report.md" } - ] -} diff --git a/phases/1-structure-alignment-refactor/step0-architecture-map.md b/phases/1-structure-alignment-refactor/step0-architecture-map.md deleted file mode 100644 index eaf434e..0000000 --- a/phases/1-structure-alignment-refactor/step0-architecture-map.md +++ /dev/null @@ -1,207 +0,0 @@ -# P1A-00 Architecture Drift Audit And Migration Map - -Date: 2026-05-05 -Author: Codex - -## Verdict - -Architecture drift is confirmed. - -The Phase 1 rebaseline implementation is behaviorally useful and currently validated, but it does not yet follow the module ownership model in `docs/ARCHITECTURE.md`. Production code is concentrated in `include/fesa/fesa.hpp`, while `src/fesa.cpp` only holds numeric static assertions. - -This audit is a behavior-preserving migration map. It does not approve parser-scope changes, MITC4 formula changes, reference tolerance changes, solver behavior changes, or numerical convention changes. - -## Baseline Evidence - -Pre-refactor validation was run before writing this artifact: - -```bash -python scripts/validate_workspace.py -``` - -Result: -- CMake configure succeeded. -- `fesa_core` and `fesa_tests` built successfully. -- CTest ran `fesa_tests` successfully. -- 1 test executable passed. - -Current production/test layout: - -| Path | Current state | -|---|---| -| `include/fesa/fesa.hpp` | 2682 lines, main production implementation body | -| `src/fesa.cpp` | 8 lines, static assertions only | -| `tests/test_main.cpp` | 1947 lines, all Phase 1 tests and test harness | -| `CMakeLists.txt` | Builds `fesa_core` from only `src/fesa.cpp` | - -## Target Module Set - -Use the module names documented in `docs/ARCHITECTURE.md`: - -```text -Analysis -Assembly -Boundary -Core -Element -IO -Load -Math -Material -Property -Results -Util -``` - -Keep `include/fesa/fesa.hpp` as an umbrella facade during and after the refactor unless an ADR explicitly changes the public include policy. - -## Dependency Direction - -Recommended extraction dependency direction: - -```text -Core type aliases --> Util diagnostics/string/numeric helpers --> Math primitives and solver interfaces --> Boundary/Load/Property/Material model primitives --> Core Domain, AnalysisModel, AnalysisState, DofManager --> IO parser --> Results and reference comparison --> Element MITC4 formulation and kernel --> Assembly --> Analysis workflow --> umbrella facade -``` - -Practical notes: -- `Core` must remain free of `IO`, `Results`, `Assembly`, and `Analysis` dependencies. -- `DofManager` must remain the only owner of equation numbering and constrained/free mapping. -- `AnalysisState` is documented under `Core` in `docs/ARCHITECTURE.md`; later steps should not relocate it to `Analysis` unless the architecture document or an ADR is updated. -- Lightweight `Node` and `Element` model records are ambiguous in `docs/ARCHITECTURE.md`: the directory comment lists them under `Element`, while `Domain` owns them. To avoid a dependency cycle, this audit recommends keeping mesh record types with the domain model in `Core`, while `Element` owns element kernels and formulation code. - -## Public API Compatibility Rules - -During the refactor: - -- `#include ` must keep working for all existing tests and clients. -- Public symbols remain in namespace `fesa`. -- Existing type aliases remain unchanged: `Real = double`, `GlobalId`, `LocalIndex`, `EquationId`, and `SparseIndex` remain signed 64-bit paths. -- Existing class/function names should remain source-compatible unless a narrow compile conflict forces a documented alias or forwarding wrapper. -- Module headers may be introduced for direct includes, but the umbrella header remains the compatibility surface. -- Moving inline definitions to `.cpp` files is allowed only when declarations remain available through the umbrella facade and CMake compiles the moved implementations. -- No MKL, TBB, or HDF5 API may leak into solver core while files are moved. -- Each implementation extraction step must preserve `python scripts/validate_workspace.py`. - -## Production Symbol Migration Map - -Line numbers refer to the current `include/fesa/fesa.hpp` audit snapshot. - -| Current symbols/range | Target module | Extraction step | Notes | -|---|---|---|---| -| `Real`, `GlobalId`, `LocalIndex`, `EquationId`, `SparseIndex` lines 23-27 | `Core` | P1A-02 | Keep centralized; static assertions remain covered. | -| `Severity`, `SourceLocation`, `Diagnostic`, `hasError`, `containsDiagnostic`, `makeDiagnostic` lines 29-59 | `Util` | P1A-02 | Diagnostics may include `Core` type aliases only. | -| `trim`, `lower`, `splitCsv`, `parseReal`, `parseInt64`, `addUnique`, `generatedRange` lines 61-121 and 303-318 | `Util` | P1A-02 | General parsing/list helpers; avoid IO-specific ownership unless helper becomes keyword-specific. | -| `Dof`, `allDofs`, `dofIndex`, `abaqusDofNumber`, `dofFromAbaqus`, `dofLabel`, component-label helpers lines 124-169 | `Core` | P1A-02 | Preserve DOF order `UX,UY,UZ,RX,RY,RZ`. | -| `Vec3`, vector arithmetic, `dot`, `cross`, `norm`, `isFinite`, normalization lines 171-223 | `Math` | P1A-03 | Low-level math primitive used by Core and Element. | -| `Node`, `ElementType`, `Element`, `NodeSet`, `ElementSet`, `StepDefinition`, `Domain` lines 225-300 | `Core` | P1A-02 | Recommended Core ownership to avoid `Core -> Element` cycle; Element module owns kernels. | -| `Material` lines 257-261 | `Material` | P1A-02 or P1A-07 contract refinement | Basic material record is needed by Domain and IO before MITC4 stiffness extraction; see contract refinements below. | -| `ShellSection` lines 263-267 | `Property` | P1A-02 | Phase 1 shell property record. | -| `BoundaryCondition` lines 269-274 | `Boundary` | P1A-02 | Phase 1 fixed-boundary model record; no RBE support added. | -| `NodalLoad` lines 276-280 | `Load` | P1A-02 | Phase 1 concentrated load record; no pressure/body load support added. | -| `KeywordLine`, `parseKeywordLine`, `ParseResult`, `AbaqusInputParser` lines 320-761 | `IO` | P1A-04 | Preserve strict Abaqus Phase 1 subset and unsupported-feature diagnostics. | -| `numericTarget`, `resolveNodeTarget`, `dofNameOrNumber`, `validAbaqusDofRange` lines 763-813 | `Core` or `Util` | P1A-02 | Domain target/DOF helpers used outside parser; keep parser-independent. | -| `shellSectionForElement` lines 790-801 | `Property` | P1A-02 | Property lookup over Domain element sets. | -| `validateDomain` lines 815-960 | `Core` with `Util` diagnostics | P1A-02 | Domain validation/singular-prone model checks; no parser support changes. | -| `AnalysisModel`, `buildLinearStaticAnalysisModel` lines 964-1017 | `Core` | P1A-02 | Architecture document places active model view near Core. | -| `DofAddress`, `DofManager` lines 1019-1146 | `Core` | P1A-02 | Must remain sole equation-numbering owner. | -| `SparsePatternEntry`, `SparsePattern` lines 1149-1166 | `Math` | P1A-03 | Data representation for future sparse path. | -| `buildReducedSparsePattern` lines 1169-1192 | `Assembly` | P1A-08 | It derives assembly pattern from Domain and DofManager; data type remains Math. | -| `DenseMatrix` lines 1195-1235 | `Math` | P1A-03 | Current deterministic dense test matrix, not production sparse backend. | -| `recoverFullReaction` lines 1238-1247 | `Assembly` | P1A-08 | Preserve `K_full * U_full - F_full`. | -| `SolveResult`, `LinearSolver`, `GaussianEliminationSolver` lines 1250-1314 | `Math` | P1A-03 | Keep `LinearSolver` as adapter boundary for future MKL. | -| `ShapeData`, `shapeFunctions`, `LocalBasis` lines 1317-1347 | `Element` | P1A-06 | Current shape helper is MITC4-oriented. | -| `MITC4NaturalPoint`, `MITC4TyingPoint`, node/tying coordinate helpers lines 1350-1369 | `Element` | P1A-06 | Preserve FESA/Abaqus S4 node order and A/B/C/D tying labels. | -| `MITC4DirectorFrame`, `MITC4MidsurfaceDerivatives`, `MITC4Geometry`, `MITC4IntegrationBasis` lines 1372-1410 | `Element` | P1A-06 | Geometry/director/basis layer. | -| MITC4 type aliases and strain component helpers lines 1413-1433 | `Element` | P1A-06 | Preserve strain order contract. | -| `MITC4LocalRotations`, displacement derivative/evaluation/row structs lines 1435-1469 | `Element` | P1A-06 | Kinematic and B-row data. | -| `MITC4MaterialMatrixEvaluation` and `MITC4MaterialMatrix` helpers lines 1472-1488 and 1800-1854 | `Material` | P1A-07 | Plane-stress material law and material vector operations. | -| `MITC4StrainTransform` and transform helpers lines 1481-1488, 1856-1955 | `Element` with `Material` inputs | P1A-07 | Transform depends on integration basis; keep formulas unchanged. | -| `MITC4IntegrationPoint`, material integration sample/data lines 1490-1518 and 1785-1798, 1958-1985 | `Element` | P1A-07 | Element integration scaffolding. | -| Global axis helpers, MITC4 diagnostics, append diagnostics lines 1520-1536 | `Element` or `Util` | P1A-06 | Keep MITC4-specific diagnostics near Element; generic append helper can move to Util. | -| Geometry/director/basis/displacement/strain row functions lines 1540-1779 | `Element` | P1A-06 | No formula/sign changes. | -| `computeLocalBasis` lines 1988-1995 | `Element` | P1A-06 | Legacy-facing wrapper around current geometry policy. | -| `ElementStiffnessOptions`, drilling/stiffness result structs lines 1997-2025 | `Element` | P1A-07 | Drilling scale policy remains documented baseline. | -| Local DOF transform, strain-row transform, `B^T D B`, drilling, stiffness, internal force, `MITC4ElementKernel` lines 2028-2208 | `Element` | P1A-07 | Preserve `2 x 2 x 2`, drilling reference diagonal, and internal force behavior. | -| `AssemblyResult`, `ReducedSystem`, `assembleSystem`, `projectToReducedSystem` lines 2211-2316 | `Assembly` | P1A-08 | Preserve full-space stiffness/load data for RF. | -| `FieldOutput`, `ResultFrame`, `ResultStep`, `ResultFile`, `InMemoryResultsWriter` lines 2319-2417 | `Results` | P1A-05 | Preserve step/frame/field metadata for `U` and `RF`. | -| `AnalysisState` lines 2420-2426 | `Core` | P1A-02 or P1A-08 contract refinement | Architecture document places `AnalysisState` under Core. | -| `AnalysisResult`, `Analysis`, `LinearStaticAnalysis`, `runLinearStaticInputString` lines 2428-2528 | `Analysis` | P1A-08 | Preserve Strategy + Template Method and solver injection. | -| `CsvDisplacementRow`, `CsvDisplacementTable`, CSV loaders, `ComparisonOptions`, `ComparisonResult`, `compareDisplacements` lines 2531-2679 | `Results` | P1A-05 | Reference-comparison layer; no tolerance/header changes. | - -## Test Migration And Characterization Map - -Keep tests behavior-preserving. Existing tests can remain in `tests/test_main.cpp` initially, but each extraction step should add direct module include smoke tests and may later split tests only with a dedicated contract. - -| Test range | Coverage area | Must protect during | -|---|---|---| -| lines 310-331 | Core aliases and DOF mapping | P1A-02 | -| lines 333-544 | Abaqus parser and reference input compatibility | P1A-04 | -| lines 557-678 | `AnalysisModel` and domain validation/singular diagnostics | P1A-02 | -| lines 690-805 | DofManager, full-vector reconstruction, RF formula, sparse connectivity | P1A-02, P1A-03, P1A-08 | -| lines 826-895 | Assembly projection, full-space preservation, Gaussian solver diagnostics | P1A-03, P1A-08 | -| lines 917-1099 | Results schema, CSV loading/comparison, `quad_02_phase1` regression | P1A-05, P1A-08 | -| lines 1116-1492 | MITC4 shape, geometry, strain, tying, material transform/integration | P1A-06, P1A-07 | -| lines 1512-1749 | MITC4 stiffness, drilling, patch, locking-sensitivity, internal force | P1A-07 | -| lines 1777-1920 | Linear static analysis workflow, parse/validation routing, solver injection | P1A-08 | - -## Contract Refinements To Consider Before Later Steps - -These are not behavior changes, but they are important for avoiding dependency cycles: - -1. `AnalysisState` target conflict: - - `docs/ARCHITECTURE.md` places `AnalysisState` under `Core`. - - P1A-08 currently says to extract `AnalysisState` with `Analysis`. - - Recommendation: keep `AnalysisState` in `Core`, or update `docs/ARCHITECTURE.md`/ADR before P1A-08 if a different ownership rule is desired. - -2. `Material` model timing: - - `Material` as a domain model record is needed by `Domain`, parser, validation, assembly, and MITC4 stiffness. - - P1A-07 owns material/stiffness helpers, but IO extraction occurs earlier in P1A-04. - - Recommendation: either extract the basic `Material` record before IO extraction, or leave a temporary umbrella-forwarded declaration until P1A-07. Do not create an IO-to-Element dependency for material data. - -3. `Node`/`Element` ownership ambiguity: - - `docs/ARCHITECTURE.md` directory comment lists `Node` and `Element` under `Element`. - - `Domain` owns node and element records, and `Core` should not depend on higher-level element kernels. - - Recommendation: keep lightweight mesh records in `Core` and keep MITC4 kernels/formulation in `Element`. If this final layout is accepted, clarify `docs/ARCHITECTURE.md` in a later documentation sync or evaluator closeout. - -## Extraction Order - -1. P1A-01 creates directories, CMake source boundaries, and module include smoke tests. -2. P1A-02 extracts stable low-level model and state ownership: aliases, diagnostics, DOF mapping, domain model records, validation, `AnalysisModel`, `AnalysisState`, and `DofManager`. -3. P1A-03 extracts `Math` primitives and solver interfaces. -4. P1A-04 extracts `IO` parser while preserving strict unsupported-feature behavior. -5. P1A-05 extracts `Results` and reference comparison. -6. P1A-06 extracts MITC4 geometry, basis, displacement, direct strain, and tying rows. -7. P1A-07 extracts MITC4 material transform, integration, stiffness, drilling, internal force, and kernel. -8. P1A-08 extracts `Assembly` and `Analysis` orchestration. -9. P1A-09 independently evaluates architecture alignment and records any residual debt. - -## Non-Negotiable Behavior Locks - -- Do not change `Real`, id, equation id, sparse index, or DOF ordering. -- Do not move equation ids into `Node` or `Element`. -- Do not compute `RF` from reduced vectors. -- Do not add parser support for `S4R`, `Part/Assembly/Instance`, `*Include`, pressure loads, nonzero prescribed displacement, or `NLGEOM=YES`. -- Do not change MITC4 formulation, tying signs, integration order, or drilling scale. -- Do not change displacement CSV column mapping or reference tolerances. -- Do not require Abaqus execution. -- Do not add MKL, TBB, or HDF5 dependencies during this refactor phase. - -## Handoff To P1A-01 - -P1A-01 should start by creating the full documented module directory scaffold and include smoke tests while leaving production implementation in place. It should not move large bodies of code yet. - -Required P1A-01 baseline checks: -- `include/fesa/fesa.hpp` still compiles as umbrella include. -- New module headers can be included together in tests. -- `python scripts/validate_workspace.py` passes. -- No production behavior changes are introduced. diff --git a/phases/1-structure-alignment-refactor/step0.md b/phases/1-structure-alignment-refactor/step0.md deleted file mode 100644 index c5f6a53..0000000 --- a/phases/1-structure-alignment-refactor/step0.md +++ /dev/null @@ -1,64 +0,0 @@ -# Sprint Contract: Architecture Drift Audit - -## Objective -Create a precise migration map from the current monolithic `include/fesa/fesa.hpp` implementation to the module ownership model documented in `docs/ARCHITECTURE.md`, without changing production solver behavior. - -## Required Reading -- /AGENTS.md -- /PROGRESS.md -- /PLAN.md -- /docs/README.md -- /docs/HARNESS_ENGINEERING.md -- /docs/ARCHITECTURE.md -- /docs/ADR.md -- /docs/NUMERICAL_CONVENTIONS.md -- /phases/1-linear-static-mitc4-rebaseline/step15-evaluator-report.md - -## Scope -- Audit current symbols, functions, classes, and tests that are concentrated in `include/fesa/fesa.hpp` and `tests/test_main.cpp`. -- Assign every Phase 1 production symbol to one target module from `docs/ARCHITECTURE.md`: `Analysis`, `Assembly`, `Boundary`, `Core`, `Element`, `IO`, `Load`, `Math`, `Material`, `Property`, `Results`, or `Util`. -- Produce a handoff artifact at `phases/1-structure-alignment-refactor/step0-architecture-map.md`. -- Identify public API compatibility rules for keeping `include/fesa/fesa.hpp` as an umbrella facade during the refactor. - -## Allowed Files -- `phases/1-structure-alignment-refactor/step0-architecture-map.md` -- `phases/1-structure-alignment-refactor/index.json` -- `PLAN.md` -- `PROGRESS.md` - -## Explicit Non-Goals -- Do not move C++ code in this step. -- Do not change `CMakeLists.txt`. -- Do not change parser support, MITC4 formulas, solver behavior, reference tolerances, or numerical conventions. -- Do not create new architecture decisions unless the audit proves `docs/ARCHITECTURE.md` itself must change. - -## Tests To Write First -- None. This is an audit and handoff step. -- Before writing the audit artifact, run `python scripts/validate_workspace.py` to capture the pre-refactor baseline. - -## Reference Artifacts -- `references/quad_02_phase1.inp` -- `references/quad_02_displacements.csv` - -These artifacts are baseline evidence only. Do not edit them. - -## Acceptance Commands -```bash -python scripts/validate_workspace.py -``` - -## Evaluator Checklist -- The map assigns all major Phase 1 production areas from `fesa.hpp` to target modules. -- The map calls out dependency direction and extraction order. -- The map preserves `include/fesa/fesa.hpp` as an umbrella facade unless an ADR changes the public API. -- The map explicitly records that this phase is behavior-preserving. -- PLAN/PROGRESS reflect this architecture debt as active work. - -## Handoff Requirements -- Update this step status in `phases/1-structure-alignment-refactor/index.json`. -- Record changed files, validation output, and any blockers in `PROGRESS.md`. -- Keep the next refactor step in `PLAN.md`. - -## Do Not -- Do not treat this audit as approval to change formulas or parser scope. -- Do not use this phase to close R-010 or R-013. diff --git a/phases/1-structure-alignment-refactor/step1.md b/phases/1-structure-alignment-refactor/step1.md deleted file mode 100644 index d7eeb7e..0000000 --- a/phases/1-structure-alignment-refactor/step1.md +++ /dev/null @@ -1,63 +0,0 @@ -# Sprint Contract: Module Scaffold And Facade - -## Objective -Create the target module directory scaffold and public include facade policy so later steps can move code incrementally while preserving existing tests and user includes. - -## Required Reading -- /AGENTS.md -- /PROGRESS.md -- /PLAN.md -- /docs/README.md -- /docs/HARNESS_ENGINEERING.md -- /docs/ARCHITECTURE.md -- /docs/ADR.md -- /phases/1-structure-alignment-refactor/step0-architecture-map.md - -## Scope -- Add the documented module directories under `include/fesa/` and `src/` as needed, including `Analysis`, `Assembly`, `Boundary`, `Core`, `Element`, `IO`, `Load`, `Math`, `Material`, `Property`, `Results`, and `Util`. -- Update `CMakeLists.txt` so future `.cpp` files under the module directories are compiled by `fesa_core`. -- Keep `include/fesa/fesa.hpp` as the stable umbrella header for current tests and clients. -- Add minimal module header smoke coverage proving selected module headers and `fesa/fesa.hpp` can be included together. - -## Allowed Files -- `CMakeLists.txt` -- `include/fesa/` -- `src/` -- `tests/` -- `phases/1-structure-alignment-refactor/index.json` -- `PLAN.md` -- `PROGRESS.md` - -## Explicit Non-Goals -- Do not move large implementations yet. -- Do not change class names, namespaces, DOF order, result labels, parser behavior, or solver outputs. -- Do not add MKL, TBB, or HDF5 dependencies. -- Do not replace the umbrella header with module-only includes. - -## Tests To Write First -- Add or update a compile/include smoke test that includes the new module headers and `fesa/fesa.hpp` together. -- The test should fail before the scaffold exists and pass after the scaffold is added. - -## Reference Artifacts -- None. - -## Acceptance Commands -```bash -python scripts/validate_workspace.py -``` - -## Evaluator Checklist -- Module directory names align with `docs/ARCHITECTURE.md`. -- `fesa_core` builds with the scaffold on Windows/MSVC. -- Existing tests still include `fesa/fesa.hpp` successfully. -- New module headers do not expose external MKL, TBB, or HDF5 APIs. -- No solver behavior changes are mixed into the scaffold. - -## Handoff Requirements -- Update this step status in `phases/1-structure-alignment-refactor/index.json`. -- Record validation output and any CMake/source-list decisions in `PROGRESS.md`. -- Leave detailed code movement to later steps. - -## Do Not -- Do not introduce broad formatting churn in `fesa.hpp`. -- Do not change public API semantics while creating empty or thin module boundaries. diff --git a/phases/1-structure-alignment-refactor/step2.md b/phases/1-structure-alignment-refactor/step2.md deleted file mode 100644 index 01ff2eb..0000000 --- a/phases/1-structure-alignment-refactor/step2.md +++ /dev/null @@ -1,77 +0,0 @@ -# Sprint Contract: Core Domain DOF Extraction - -## Objective -Move core types, diagnostics, domain data, validation, `AnalysisModel`, `DofManager`, and Phase 1 boundary/load/property model ownership out of the monolithic header into the documented `Core`, `Util`, `Boundary`, `Load`, and `Property` module boundaries. - -## Required Reading -- /AGENTS.md -- /PROGRESS.md -- /PLAN.md -- /docs/README.md -- /docs/HARNESS_ENGINEERING.md -- /docs/ARCHITECTURE.md -- /docs/ADR.md -- /docs/NUMERICAL_CONVENTIONS.md -- /phases/1-structure-alignment-refactor/step0-architecture-map.md -- /phases/1-structure-alignment-refactor/step1.md - -## Scope -- Extract numeric aliases, diagnostics, DOF enum/mapping, domain entities, domain validation helpers, `AnalysisModel`, and `DofManager`. -- Extract Phase 1 `BoundaryCondition`, `NodalLoad`, and `ShellSection` ownership into `Boundary`, `Load`, and `Property` module headers or implementation files while keeping `Domain` as the aggregate input model. -- Keep `Node` and `Element` free of equation ids. -- Keep `Domain` as input-model ownership and treat it as immutable after parsing. -- Preserve existing symbol names and the `fesa` namespace unless a compiler-visible conflict requires a narrow adjustment. - -## Allowed Files -- `include/fesa/fesa.hpp` -- `include/fesa/Core/` -- `include/fesa/Util/` -- `include/fesa/Boundary/` -- `include/fesa/Load/` -- `include/fesa/Property/` -- `src/Core/` -- `src/Util/` -- `src/Boundary/` -- `src/Load/` -- `src/Property/` -- `tests/` -- `CMakeLists.txt` -- `phases/1-structure-alignment-refactor/index.json` -- `PLAN.md` -- `PROGRESS.md` - -## Explicit Non-Goals -- Do not move parser, MITC4 element, assembly, analysis, solver, material-law, or result writer code in this step except for include dependency repair. -- Do not change DOF order or Abaqus DOF mapping. -- Do not change reaction recovery formulas. -- Do not add runtime polymorphic interfaces that are not needed for this extraction. - -## Tests To Write First -- Add or update focused tests proving module includes expose `DofManager`, `Domain`, diagnostics, DOF mapping, Phase 1 boundary/load/property model types, and the umbrella header. -- Preserve existing DofManager and domain validation behavioral tests as characterization tests. - -## Reference Artifacts -- None. - -## Acceptance Commands -```bash -python scripts/validate_workspace.py -``` - -## Evaluator Checklist -- Core ownership matches `docs/ARCHITECTURE.md`. -- Boundary, Load, and Property Phase 1 model types are no longer hidden only in `fesa.hpp`. -- `DofManager` still owns constrained/free mapping, equation numbering, sparse connectivity inputs, and full-vector reconstruction. -- Numeric aliases remain `double` and signed int64. -- `Node` and `Element` do not store global equation ids. -- Existing parser, analysis, and reference tests still pass. - -## Handoff Requirements -- Update this step status in `phases/1-structure-alignment-refactor/index.json`. -- Record moved files and any remaining Core symbols still left in `fesa.hpp`. -- Update `PLAN.md` if the extraction reveals a dependency cycle that requires a later contract change. - -## Do Not -- Do not weaken tests to accommodate include-order problems. -- Do not let `Core` depend on `IO`, `Element`, `Assembly`, `Analysis`, or `Results`. -- Do not create circular dependencies between `Core`, `Boundary`, `Load`, and `Property`. diff --git a/phases/1-structure-alignment-refactor/step3.md b/phases/1-structure-alignment-refactor/step3.md deleted file mode 100644 index 8fca84f..0000000 --- a/phases/1-structure-alignment-refactor/step3.md +++ /dev/null @@ -1,66 +0,0 @@ -# Sprint Contract: Math Solver Extraction - -## Objective -Move math primitives, dense test matrix support, sparse pattern data, and linear solver interfaces into `Math` while keeping future MKL integration behind an adapter boundary. - -## Required Reading -- /AGENTS.md -- /PROGRESS.md -- /PLAN.md -- /docs/README.md -- /docs/HARNESS_ENGINEERING.md -- /docs/ARCHITECTURE.md -- /docs/ADR.md -- /docs/NUMERICAL_CONVENTIONS.md -- /phases/1-structure-alignment-refactor/step0-architecture-map.md -- /phases/1-structure-alignment-refactor/step2.md - -## Scope -- Extract vector/matrix helpers, `DenseMatrix`, sparse pattern structures, `SolveResult`, `LinearSolver`, and the deterministic test solver. -- Keep the production-facing solver dependency expressed through `LinearSolver`. -- Preserve int64 sparse index and equation-number boundaries. -- Keep MKL/PARDISO as a future adapter, not a Phase 1 dependency. - -## Allowed Files -- `include/fesa/fesa.hpp` -- `include/fesa/Math/` -- `src/Math/` -- `include/fesa/Core/` -- `src/Core/` -- `tests/` -- `CMakeLists.txt` -- `phases/1-structure-alignment-refactor/index.json` -- `PLAN.md` -- `PROGRESS.md` - -## Explicit Non-Goals -- Do not introduce MKL, TBB, HDF5, or production sparse storage. -- Do not change Gaussian solver numerical behavior except for mechanical relocation. -- Do not move MITC4, parser, results, assembly, or analysis logic except for include dependency repair. - -## Tests To Write First -- Add or update include/link tests for the `Math` module. -- Preserve existing small linear algebra, sparse-pattern, solver-injection, and singular-solver diagnostic tests as characterization tests. - -## Reference Artifacts -- None. - -## Acceptance Commands -```bash -python scripts/validate_workspace.py -``` - -## Evaluator Checklist -- `Math` does not depend on `IO`, `Results`, `Element`, `Assembly`, or `Analysis`. -- `LinearSolver` remains an adapter boundary. -- All ids, equation ids, nonzero counts, and sparse indices remain int64. -- Solver failure diagnostics still propagate to analysis tests. -- No external library API leaks into solver core. - -## Handoff Requirements -- Update this step status in `phases/1-structure-alignment-refactor/index.json`. -- Record moved math/solver files and any remaining math symbols still left in `fesa.hpp`. - -## Do Not -- Do not optimize the solver while moving files. -- Do not replace deterministic tests with tolerance changes. diff --git a/phases/1-structure-alignment-refactor/step4.md b/phases/1-structure-alignment-refactor/step4.md deleted file mode 100644 index 8a06235..0000000 --- a/phases/1-structure-alignment-refactor/step4.md +++ /dev/null @@ -1,72 +0,0 @@ -# Sprint Contract: IO Parser Extraction - -## Objective -Move the Abaqus Phase 1 input parser into the `IO` module while preserving the documented strict parser subset and unsupported-feature diagnostics. - -## Required Reading -- /AGENTS.md -- /PROGRESS.md -- /PLAN.md -- /docs/README.md -- /docs/HARNESS_ENGINEERING.md -- /docs/ARCHITECTURE.md -- /docs/ADR.md -- /docs/ABAQUS_INPUT_SUBSET.md -- /docs/VERIFICATION_PLAN.md -- /phases/1-structure-alignment-refactor/step0-architecture-map.md -- /phases/1-structure-alignment-refactor/step2.md - -## Scope -- Extract `KeywordLine`, `ParseResult`, `AbaqusInputParser`, and parser-only helpers into `IO`. -- Keep parser output in `Domain`. -- Preserve strict rejection for `S4R`, `Part/Assembly/Instance`, `*Include`, `NLGEOM=YES`, nonzero prescribed displacement, and unsupported keyword parameters. -- Keep normalized reference behavior for `references/quad_02_phase1.inp`. - -## Allowed Files -- `include/fesa/fesa.hpp` -- `include/fesa/IO/` -- `src/IO/` -- `include/fesa/Core/` -- `src/Core/` -- `tests/` -- `CMakeLists.txt` -- `phases/1-structure-alignment-refactor/index.json` -- `PLAN.md` -- `PROGRESS.md` - -## Explicit Non-Goals -- Do not add new Abaqus keyword support. -- Do not accept original `references/quad_02.inp` as a Phase 1 parser input. -- Do not change `docs/ABAQUS_INPUT_SUBSET.md` unless a purely clarifying note is required. -- Do not move results or reference comparator code in this step. - -## Tests To Write First -- Add or update an include/link test for `fesa/IO` parser headers. -- Preserve parser acceptance and rejection tests as characterization tests. -- Ensure original `quad_02.inp` unsupported provenance remains rejected where tested. - -## Reference Artifacts -- `references/quad_02.inp` -- `references/quad_02_phase1.inp` - -Do not edit these files. - -## Acceptance Commands -```bash -python scripts/validate_workspace.py -``` - -## Evaluator Checklist -- Parser code lives in `IO` and does not own analysis or assembly behavior. -- Parser subset discipline is unchanged. -- Diagnostics still include enough source context for malformed input. -- `Domain` remains the parser output boundary. -- Stored reference compatibility notes remain true. - -## Handoff Requirements -- Update this step status in `phases/1-structure-alignment-refactor/index.json`. -- Record moved parser files and any unsupported-feature behavior verified. - -## Do Not -- Do not normalize reference files inside parser code. -- Do not silently broaden parser compatibility while refactoring. diff --git a/phases/1-structure-alignment-refactor/step5.md b/phases/1-structure-alignment-refactor/step5.md deleted file mode 100644 index 22abe25..0000000 --- a/phases/1-structure-alignment-refactor/step5.md +++ /dev/null @@ -1,72 +0,0 @@ -# Sprint Contract: Results Reference Extraction - -## Objective -Move the in-memory result model, Phase 1 results writer, displacement CSV loader, and reference comparator into `Results` while preserving the documented step/frame/field schema. - -## Required Reading -- /AGENTS.md -- /PROGRESS.md -- /PLAN.md -- /docs/README.md -- /docs/HARNESS_ENGINEERING.md -- /docs/ARCHITECTURE.md -- /docs/ADR.md -- /docs/RESULTS_SCHEMA.md -- /docs/VERIFICATION_PLAN.md -- /docs/NUMERICAL_CONVENTIONS.md -- /phases/1-structure-alignment-refactor/step0-architecture-map.md -- /phases/1-structure-alignment-refactor/step2.md - -## Scope -- Extract `FieldOutput`, `ResultFrame`, `ResultStep`, `ResultFile`, `InMemoryResultsWriter`, displacement CSV tables, comparison options, and comparison results. -- Preserve mandatory Phase 1 `U` and `RF` field metadata. -- Preserve CSV mapping from Abaqus `Node Label`, `U-U1`, `U-U2`, `U-U3`, `UR-UR1`, `UR-UR2`, `UR-UR3`. -- Keep HDF5 as a future writer adapter; do not add the dependency yet. - -## Allowed Files -- `include/fesa/fesa.hpp` -- `include/fesa/Results/` -- `src/Results/` -- `include/fesa/Core/` -- `include/fesa/IO/` -- `tests/` -- `CMakeLists.txt` -- `phases/1-structure-alignment-refactor/index.json` -- `PLAN.md` -- `PROGRESS.md` - -## Explicit Non-Goals -- Do not add HDF5 output implementation. -- Do not change result labels, field positions, basis, or comparison tolerances. -- Do not add reaction CSV parsing in this step. -- Do not change analysis execution. - -## Tests To Write First -- Add or update include/link tests for `fesa/Results` headers. -- Preserve existing result schema, CSV loader, comparison, and `quad_02_phase1` displacement regression tests as characterization tests. - -## Reference Artifacts -- `references/quad_02_phase1.inp` -- `references/quad_02_displacements.csv` - -Do not edit these files. - -## Acceptance Commands -```bash -python scripts/validate_workspace.py -``` - -## Evaluator Checklist -- Result model follows `docs/RESULTS_SCHEMA.md`. -- `U` and `RF` metadata are unchanged. -- CSV comparison still matches by node id, not row order alone. -- `quad_02_phase1` stored displacement regression still passes. -- HDF5 APIs do not leak into solver core. - -## Handoff Requirements -- Update this step status in `phases/1-structure-alignment-refactor/index.json`. -- Record moved result/comparator files and any remaining result symbols still left in `fesa.hpp`. - -## Do Not -- Do not relax reference tolerances to make relocation pass. -- Do not treat missing Abaqus reaction CSV as solved. diff --git a/phases/1-structure-alignment-refactor/step6.md b/phases/1-structure-alignment-refactor/step6.md deleted file mode 100644 index 1fa9e88..0000000 --- a/phases/1-structure-alignment-refactor/step6.md +++ /dev/null @@ -1,68 +0,0 @@ -# Sprint Contract: MITC4 Geometry Strain Extraction - -## Objective -Move MITC4 geometry, director, natural-coordinate, displacement, direct covariant strain, and MITC shear-tying helpers into the `Element` module without changing the documented formulation. - -## Required Reading -- /AGENTS.md -- /PROGRESS.md -- /PLAN.md -- /docs/README.md -- /docs/HARNESS_ENGINEERING.md -- /docs/ARCHITECTURE.md -- /docs/ADR.md -- /docs/NUMERICAL_CONVENTIONS.md -- /docs/MITC4_FORMULATION.md -- /docs/VERIFICATION_PLAN.md -- /phases/1-structure-alignment-refactor/step0-architecture-map.md -- /phases/1-structure-alignment-refactor/step3.md - -## Scope -- Extract MITC4 natural points, tying points, director frames, geometry, integration basis, displacement derivative helpers, direct strain rows, and MITC shear tying rows. -- Keep the FESA/Abaqus S4 node order and tying-point labels unchanged. -- Keep diagnostics for invalid thickness, singular center normal, singular basis, and singular Jacobian. -- Keep `drilling` handling out of this step except where existing local rotation structures require it. - -## Allowed Files -- `include/fesa/fesa.hpp` -- `include/fesa/Element/` -- `src/Element/` -- `include/fesa/Core/` -- `include/fesa/Math/` -- `tests/` -- `CMakeLists.txt` -- `phases/1-structure-alignment-refactor/index.json` -- `PLAN.md` -- `PROGRESS.md` - -## Explicit Non-Goals -- Do not change MITC4 formulas, signs, tying interpolation, basis construction, or tolerances. -- Do not move stiffness integration or material matrix code in this step unless needed only to resolve declarations. -- Do not add S4R, reduced integration, hourglass control, pressure loads, or nonlinear geometry. - -## Tests To Write First -- Add or update include/link tests for MITC4 geometry/strain module headers. -- Preserve finite-difference, tying interpolation, geometry, basis, and diagnostic tests as characterization tests. - -## Reference Artifacts -- None. - -## Acceptance Commands -```bash -python scripts/validate_workspace.py -``` - -## Evaluator Checklist -- MITC4 geometry/strain helpers live in `Element`. -- The implementation still matches `docs/MITC4_FORMULATION.md`. -- FESA tying point sign convention remains unchanged. -- No parser, assembly, or result behavior changes are mixed in. -- Existing MITC4 geometry and strain tests still pass. - -## Handoff Requirements -- Update this step status in `phases/1-structure-alignment-refactor/index.json`. -- Record moved MITC4 geometry/strain files and any remaining Element symbols still left in `fesa.hpp`. - -## Do Not -- Do not simplify or rewrite the formulation during relocation. -- Do not tune tests around floating-point drift unless a real relocation-induced bug is found and fixed. diff --git a/phases/1-structure-alignment-refactor/step7.md b/phases/1-structure-alignment-refactor/step7.md deleted file mode 100644 index cb1affe..0000000 --- a/phases/1-structure-alignment-refactor/step7.md +++ /dev/null @@ -1,70 +0,0 @@ -# Sprint Contract: MITC4 Material Stiffness Extraction - -## Objective -Move MITC4 material matrix, covariant-to-local transform, integration scaffolding, drilling stabilization, stiffness assembly, and internal force helpers into the documented `Element` and `Material` module boundaries. - -## Required Reading -- /AGENTS.md -- /PROGRESS.md -- /PLAN.md -- /docs/README.md -- /docs/HARNESS_ENGINEERING.md -- /docs/ARCHITECTURE.md -- /docs/ADR.md -- /docs/NUMERICAL_CONVENTIONS.md -- /docs/MITC4_FORMULATION.md -- /docs/VERIFICATION_PLAN.md -- /phases/1-structure-alignment-refactor/step0-architecture-map.md -- /phases/1-structure-alignment-refactor/step6.md - -## Scope -- Extract plane-stress MITC4 material matrix helpers and material diagnostics. -- Extract `2 x 2 x 2` integration data, stiffness accumulation, local/global transform, drilling stabilization, `MITC4ElementKernel`, and internal force helpers. -- Keep `drilling_stiffness_scale = 1.0e-3` and reference-diagonal policy unchanged. -- Keep `Element` responsible for MITC4 element kernel behavior; keep reusable material-law helpers under `Material` if separated. - -## Allowed Files -- `include/fesa/fesa.hpp` -- `include/fesa/Element/` -- `src/Element/` -- `include/fesa/Material/` -- `src/Material/` -- `include/fesa/Math/` -- `tests/` -- `CMakeLists.txt` -- `phases/1-structure-alignment-refactor/index.json` -- `PLAN.md` -- `PROGRESS.md` - -## Explicit Non-Goals -- Do not change MITC4 stiffness formulas, drilling policy, integration order, or patch-test tolerances. -- Do not add stress/resultant output. -- Do not add reduced integration, S4R, hourglass control, nonlinear tangent stiffness, or pressure loads. -- Do not change assembly behavior beyond include dependency repair. - -## Tests To Write First -- Add or update include/link tests for MITC4 stiffness/material module headers. -- Preserve stiffness symmetry, rigid body, drilling sensitivity, internal force, patch, and locking-sensitivity tests as characterization tests. - -## Reference Artifacts -- None. - -## Acceptance Commands -```bash -python scripts/validate_workspace.py -``` - -## Evaluator Checklist -- MITC4 material and stiffness code no longer lives in the umbrella header as implementation. -- Drilling policy matches `docs/MITC4_FORMULATION.md`. -- Element patch and locking-sensitivity tests still pass. -- No unsupported Abaqus behavior or result output is added. -- Public facade includes remain compatible. - -## Handoff Requirements -- Update this step status in `phases/1-structure-alignment-refactor/index.json`. -- Record moved MITC4 material/stiffness files and any remaining MITC4 implementation still left in `fesa.hpp`. - -## Do Not -- Do not optimize or parallelize element stiffness in this relocation step. -- Do not use reference displacement mismatch to retune formulation parameters. diff --git a/phases/1-structure-alignment-refactor/step8.md b/phases/1-structure-alignment-refactor/step8.md deleted file mode 100644 index a6037d3..0000000 --- a/phases/1-structure-alignment-refactor/step8.md +++ /dev/null @@ -1,82 +0,0 @@ -# Sprint Contract: Assembly Analysis Extraction - -## Objective -Move full-space assembly, reduced projection, reaction recovery path, analysis state/result objects, and linear static workflow into `Assembly` and `Analysis` while preserving Phase 1 solver behavior. - -## Required Reading -- /AGENTS.md -- /PROGRESS.md -- /PLAN.md -- /docs/README.md -- /docs/HARNESS_ENGINEERING.md -- /docs/ARCHITECTURE.md -- /docs/ADR.md -- /docs/NUMERICAL_CONVENTIONS.md -- /docs/RESULTS_SCHEMA.md -- /docs/MITC4_FORMULATION.md -- /docs/VERIFICATION_PLAN.md -- /phases/1-structure-alignment-refactor/step0-architecture-map.md -- /phases/1-structure-alignment-refactor/step2.md -- /phases/1-structure-alignment-refactor/step3.md -- /phases/1-structure-alignment-refactor/step5.md -- /phases/1-structure-alignment-refactor/step7.md - -## Scope -- Extract `AssemblyResult`, `ReducedSystem`, full-space assembly, reduced projection, and full-vector reaction recovery helpers into `Assembly`. -- Extract `AnalysisState`, `AnalysisResult`, `Analysis`, `LinearStaticAnalysis`, and input-to-analysis convenience workflow into `Analysis`. -- Keep Strategy + Template Method structure visible. -- Preserve `RF = K_full * U_full - F_full`. -- Preserve solver adapter injection and deterministic default solver behavior. - -## Allowed Files -- `include/fesa/fesa.hpp` -- `include/fesa/Assembly/` -- `src/Assembly/` -- `include/fesa/Analysis/` -- `src/Analysis/` -- `include/fesa/Core/` -- `include/fesa/Element/` -- `include/fesa/Math/` -- `include/fesa/Results/` -- `include/fesa/IO/` -- `tests/` -- `CMakeLists.txt` -- `phases/1-structure-alignment-refactor/index.json` -- `PLAN.md` -- `PROGRESS.md` - -## Explicit Non-Goals -- Do not change solve algorithms, convergence logic, boundary-condition semantics, or result schema. -- Do not add nonlinear, dynamic, heat-transfer, pressure-load, RBE, or HDF5 behavior. -- Do not compute reactions from reduced quantities. -- Do not add production sparse storage beyond the existing scaffold. - -## Tests To Write First -- Add or update include/link tests for `Assembly` and `Analysis` module headers. -- Preserve assembly, reduced solve, solver injection, full-vector RF, input-to-result workflow, and `quad_02_phase1` reference regression tests as characterization tests. - -## Reference Artifacts -- `references/quad_02_phase1.inp` -- `references/quad_02_displacements.csv` - -Do not edit these files. - -## Acceptance Commands -```bash -python scripts/validate_workspace.py -``` - -## Evaluator Checklist -- Assembly owns full-space stiffness/load preservation and reduced projection. -- Analysis owns execution flow and state updates. -- `DofManager` remains the owner of equation numbering and full/reduced mapping. -- Full-vector RF recovery is unchanged. -- `quad_02_phase1` displacement regression still passes. - -## Handoff Requirements -- Update this step status in `phases/1-structure-alignment-refactor/index.json`. -- Record moved assembly/analysis files and any remaining production implementation still left in `fesa.hpp`. - -## Do Not -- Do not hide a behavior change under the label "refactor". -- Do not broaden Phase 1 feature scope. diff --git a/phases/1-structure-alignment-refactor/step9-evaluator-report.md b/phases/1-structure-alignment-refactor/step9-evaluator-report.md deleted file mode 100644 index c0645fb..0000000 --- a/phases/1-structure-alignment-refactor/step9-evaluator-report.md +++ /dev/null @@ -1,56 +0,0 @@ -# Evaluation Feedback: P1A-09 Architecture Evaluator Closeout - -Date: 2026-05-05 -Evaluator: Codex - -## Verdict - -pass - -## Findings - -No material architecture, guardrail, numerical-convention, parser-subset, result-schema, or validation regressions were found. - -## Evidence - -- Required project guardrails and topic documents were read before evaluation: `AGENTS.md`, `PROGRESS.md`, `PLAN.md`, `docs/README.md`, `docs/HARNESS_ENGINEERING.md`, `docs/ARCHITECTURE.md`, `docs/ADR.md`, `docs/NUMERICAL_CONVENTIONS.md`, `docs/ABAQUS_INPUT_SUBSET.md`, `docs/VERIFICATION_PLAN.md`, `docs/RESULTS_SCHEMA.md`, and `docs/MITC4_FORMULATION.md`. -- `include/fesa/fesa.hpp` is now an umbrella include facade only. A direct search for implementation tokens such as `namespace fesa {`, `inline`, `class`, `struct`, `enum`, and `using` in that file returned no matches. -- Production symbols are no longer concentrated in `fesa.hpp`; they are separated under the documented module families in `include/fesa/Analysis`, `include/fesa/Assembly`, `include/fesa/Boundary`, `include/fesa/Core`, `include/fesa/Element`, `include/fesa/IO`, `include/fesa/Load`, `include/fesa/Math`, `include/fesa/Material`, `include/fesa/Property`, `include/fesa/Results`, and `include/fesa/Util`. -- Matching `src/` module directories and thin `.cpp` compile units exist for the architecture module set, and `CMakeLists.txt` compiles `src/*.cpp` recursively into `fesa_core`. -- Current implementation bodies remain mostly inline in module headers. This is accepted for this refactor because the sprint contracts allowed module headers or implementation files and the migration goal was to remove `fesa.hpp` as the primary implementation body. If stricter `.cpp` implementation ownership is desired later, it should be handled by a dedicated ABI/build-time hardening contract. -- `Node`, `Element`, and the lightweight `Material` domain record remain under `Core` as planned in the P1A-00 migration map to avoid a `Core -> Element` or `Core -> Material` dependency cycle. Element kernels and MITC4 material-law helpers are owned by `Element` and `Material`. -- Direct module include tests exist for Core, Math, IO, Results, Element, MITC4 stiffness, Assembly, and Analysis. -- `quad_02_phase1` remains the executable stored-reference path for Phase 1, and the displacement CSV regression is still covered by the main validation test suite. - -## Checklist - -| Item | Result | Notes | -|---|---|---| -| Contract compliance | PASS | Evaluation-only step changed only phase/status/handoff files. | -| Architecture alignment | PASS | `fesa.hpp` is a facade; module ownership is now visible under `include/fesa/*` with matching `src/*` compile units. | -| Numerical conventions | PASS | No DOF order, precision, int64, constrained/free mapping, or full-vector RF behavior was changed. | -| Abaqus subset discipline | PASS | No new parser support was added; unsupported Abaqus/CAE scaffolding remains outside Phase 1 parser scope. | -| MITC4 formulation | PASS | No formulation, tying sign, integration, drilling, or stiffness behavior was changed by this evaluator step. | -| Results schema | PASS | `U` and `RF` result model behavior remains covered by tests. | -| Reference verification | PASS | `references/quad_02_phase1.inp` and `references/quad_02_displacements.csv` remain the active stored displacement regression path. | -| Validation | PASS | `python scripts\validate_workspace.py` passed; CTest reported 9/9 test executables passed. | -| Handoff | PASS | Phase status, `PLAN.md`, and `PROGRESS.md` were updated. | - -## Residual Non-Blocking Gaps - -- R-010 remains open: reaction-force CSV artifacts are not yet onboarded with a documented schema, tolerance, and automated comparison. -- R-013 remains open: the PRD target of three stored Phase 1 reference cases is not yet satisfied. -- Optional future hardening: move large inline module bodies from public headers into `.cpp` files if compile-time, ABI, or encapsulation policy later requires it. This should not be mixed with solver behavior changes. - -## Acceptance Commands - -```powershell -python scripts\validate_workspace.py -``` - -Result: - -- CMake configure succeeded. -- `fesa_core` and all test executables built successfully. -- CTest passed 9 of 9 test executables. -- Validation succeeded. diff --git a/phases/1-structure-alignment-refactor/step9.md b/phases/1-structure-alignment-refactor/step9.md deleted file mode 100644 index ef8e4c8..0000000 --- a/phases/1-structure-alignment-refactor/step9.md +++ /dev/null @@ -1,70 +0,0 @@ -# Sprint Contract: Architecture Evaluator Closeout - -## Objective -Independently evaluate the completed structure-alignment refactor against `docs/ARCHITECTURE.md`, persistent project guardrails, tests, reference artifacts, and phase handoff requirements. - -## Required Reading -- /AGENTS.md -- /PROGRESS.md -- /PLAN.md -- /docs/README.md -- /docs/HARNESS_ENGINEERING.md -- /docs/ARCHITECTURE.md -- /docs/ADR.md -- /docs/NUMERICAL_CONVENTIONS.md -- /docs/ABAQUS_INPUT_SUBSET.md -- /docs/VERIFICATION_PLAN.md -- /docs/RESULTS_SCHEMA.md -- /docs/MITC4_FORMULATION.md -- /phases/1-structure-alignment-refactor/index.json -- /phases/1-structure-alignment-refactor/step0-architecture-map.md - -## Scope -- Review the final production layout under `include/fesa/` and `src/`. -- Confirm the umbrella header is a facade, not the primary implementation body. -- Confirm source ownership matches the `src/` directory structure in `docs/ARCHITECTURE.md`. -- Run validation and inspect stored-reference regression status. -- Produce an evaluator report at `phases/1-structure-alignment-refactor/step9-evaluator-report.md`. -- Update phase status, `PLAN.md`, and `PROGRESS.md`. - -## Allowed Files -- `phases/index.json` -- `phases/1-structure-alignment-refactor/index.json` -- `phases/1-structure-alignment-refactor/step9-evaluator-report.md` -- `PLAN.md` -- `PROGRESS.md` - -## Explicit Non-Goals -- Do not implement missing refactor work in the evaluator step. -- Do not change solver behavior or tests to make evaluation pass. -- Do not claim full PRD Phase 1 completion unless R-010 and R-013 are also resolved. -- Do not approve a layout that still relies on `fesa.hpp` as the main implementation location. - -## Tests To Write First -- None. This is an evaluator-only step. - -## Reference Artifacts -- `references/quad_02_phase1.inp` -- `references/quad_02_displacements.csv` - -## Acceptance Commands -```bash -python scripts/validate_workspace.py -``` - -## Evaluator Checklist -- `src/Analysis`, `src/Assembly`, `src/Boundary`, `src/Core`, `src/Element`, `src/IO`, `src/Load`, `src/Math`, `src/Material`, `src/Property`, `src/Results`, and `src/Util` exist as needed and own their documented implementation areas. -- `include/fesa/fesa.hpp` is a compatibility facade and does not contain the bulk of production implementation. -- Public tests and direct module include tests pass. -- Parser subset, MITC4 formulation, numerical conventions, result schema, and reference comparison behavior are unchanged. -- `quad_02_phase1` stored displacement regression still passes. -- PLAN/PROGRESS clearly state any residual architecture debt. - -## Handoff Requirements -- If passed, mark `1-structure-alignment-refactor` completed in `phases/index.json`. -- If failed, write concrete findings and required fixes in `step9-evaluator-report.md` and keep the phase pending or blocked. -- Keep R-010 and R-013 visible if they remain unresolved. - -## Do Not -- Do not self-approve incomplete module separation. -- Do not let a passing compile hide architecture drift. diff --git a/phases/index.json b/phases/index.json deleted file mode 100644 index ba6de93..0000000 --- a/phases/index.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "phases": [ - { - "dir": "1-linear-static-mitc4", - "status": "blocked", - "blocked_reason": "Superseded by 1-linear-static-mitc4-rebaseline after the MITC4 formulation reset. Keep this phase as historical execution record; do not resume old P1-15/P1-16 without an explicit recovery decision." - }, - { - "dir": "1-linear-static-mitc4-rebaseline", - "status": "completed", - "completion_note": "P1R-15 evaluator closeout passed on 2026-05-04. Product-level Phase 1 reference gaps R-010 and R-013 remain tracked in PLAN.md." - }, - { - "dir": "1-structure-alignment-refactor", - "status": "completed", - "completion_note": "P1A-09 evaluator closeout passed on 2026-05-05. Architecture readiness gap R-014 is closed; product-level reference gaps R-010 and R-013 remain tracked in PLAN.md." - } - ] -} diff --git a/plugins/fesa-commands/.codex-plugin/plugin.json b/plugins/fesa-commands/.codex-plugin/plugin.json deleted file mode 100644 index 8d65e70..0000000 --- a/plugins/fesa-commands/.codex-plugin/plugin.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "name": "fesa-commands", - "version": "1.0.0", - "description": "Repo-local FESA slash commands for Codex project coordination, reference readiness, and documentation workflows.", - "repository": "https://teagit.mimi1011.synology.me/baram2584/FESADev.git", - "keywords": [ - "fesa", - "finite-element", - "codex-commands", - "workflow" - ], - "interface": { - "displayName": "FESA Commands", - "shortDescription": "FESA project workflow commands for this repository", - "longDescription": "Repo-local commands for checking FESA readiness, synchronizing PLAN/PROGRESS, onboarding reference artifacts, guarding documentation, drafting phases, and preparing handoffs.", - "developerName": "Local Repository", - "category": "Productivity", - "capabilities": [ - "Interactive", - "Read", - "Write" - ], - "defaultPrompt": [ - "Check whether FESA is ready for Phase 1.", - "Sync PLAN.md and PROGRESS.md.", - "Inspect stored reference artifacts." - ], - "brandColor": "#0F766E" - } -} diff --git a/plugins/fesa-commands/commands/adr.md b/plugins/fesa-commands/commands/adr.md deleted file mode 100644 index c1833b3..0000000 --- a/plugins/fesa-commands/commands/adr.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -description: Draft or update an ADR for a FESA architecture or numerical decision. ---- - -# /adr - -## Preflight - -- Read `/AGENTS.md`, `/PROGRESS.md`, `/PLAN.md`, `/docs/ARCHITECTURE.md`, `/docs/ADR.md`, and the topic-specific design docs. -- Use `$fesa-adr-update` when available. -- Identify whether this is a new decision, a revision, or a superseding ADR. - -## Task - -- Capture context, decision, consequences, alternatives considered, and affected docs. -- Update related docs only when needed to keep decisions consistent. -- Record follow-up tasks in `PLAN.md` and completed ADR work in `PROGRESS.md`. - -## Verification - -- Check links and decision numbering manually. -- Run `python scripts/validate_workspace.py`. - -## Result - -- **Action**: drafted or updated ADR content -- **Status**: success | partial | blocked -- **Details**: ADR section changed, affected docs, and validation result diff --git a/plugins/fesa-commands/commands/benchmark-add.md b/plugins/fesa-commands/commands/benchmark-add.md deleted file mode 100644 index 3adf0e5..0000000 --- a/plugins/fesa-commands/commands/benchmark-add.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -description: Guide or validate addition of a new stored reference benchmark. ---- - -# /benchmark-add - -## Preflight - -- Read `/AGENTS.md`, `/PROGRESS.md`, `/PLAN.md`, `/docs/VERIFICATION_PLAN.md`, `/docs/RESULTS_SCHEMA.md`, and `/docs/ABAQUS_INPUT_SUBSET.md`. -- Use `$fesa-reference-onboarding` when available. -- Do not run Abaqus locally. - -## Task - -- Help organize the benchmark input, solved reference values such as `*_displacements.csv`, tolerance metadata, unit notes, result fields, and provenance. -- Check that Abaqus keywords are within the Phase 1 subset or explicitly documented as unsupported. -- Ensure comparison values can be tied to step/frame/field/history result paths. - -## Verification - -- Parse any JSON metadata if present. -- For `*_displacements.csv`, check the required Abaqus displacement/rotation columns. -- Run `python scripts/validate_workspace.py` when files are changed. - -## Result - -- **Action**: added or validated benchmark artifact -- **Status**: ready | partial | blocked -- **Details**: benchmark name, files, missing metadata, and validation result diff --git a/plugins/fesa-commands/commands/doc-guard.md b/plugins/fesa-commands/commands/doc-guard.md deleted file mode 100644 index 3cd92d2..0000000 --- a/plugins/fesa-commands/commands/doc-guard.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -description: Review FESA documentation for architecture, numerical, and coordination drift. ---- - -# /doc-guard - -## Preflight - -- Read `/AGENTS.md`, `/PROGRESS.md`, `/PLAN.md`, `/docs/README.md`, and the docs changed in the current work. -- Use `$fesa-doc-sync` and `$fesa-review` when available. -- If the user explicitly asks for delegated review, use `harness_reviewer` or `progress_plan_auditor`. - -## Task - -- Check consistency across architecture, ADRs, numerical conventions, Abaqus subset, verification plan, result schema, MITC4 formulation, PLAN.md, and PROGRESS.md. -- Lead with concrete drift findings and file references. -- Keep comments focused on correctness, missing decisions, and validation gaps. - -## Verification - -- If edits are made, run `python scripts/validate_workspace.py`. -- Confirm that future work stays in PLAN.md and completed work stays in PROGRESS.md. - -## Result - -- **Action**: reviewed documentation guardrails -- **Status**: success | partial | blocked -- **Details**: findings, edits if any, and validation result diff --git a/plugins/fesa-commands/commands/handoff.md b/plugins/fesa-commands/commands/handoff.md deleted file mode 100644 index c2fe5e5..0000000 --- a/plugins/fesa-commands/commands/handoff.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -description: Prepare a concise handoff for the next FESA agent session. ---- - -# /handoff - -## Preflight - -- Read `/AGENTS.md`, `/PROGRESS.md`, `/PLAN.md`, and changed files. -- Use `$fesa-doc-sync` when available. -- Identify what changed, what was verified, what remains blocked, and what should happen next. - -## Task - -- Add a factual `PROGRESS.md` entry for completed work. -- Update `PLAN.md` only for future tasks, changed ownership, open questions, or blockers. -- Keep the handoff concise enough that a fresh agent can act without private context. - -## Verification - -- Run `python scripts/validate_workspace.py`. -- Confirm that the next-session required reading remains current. - -## Result - -- **Action**: prepared handoff -- **Status**: success | partial | blocked -- **Details**: progress entry, plan deltas, blockers, and validation result diff --git a/plugins/fesa-commands/commands/phase-draft.md b/plugins/fesa-commands/commands/phase-draft.md deleted file mode 100644 index e8023f5..0000000 --- a/plugins/fesa-commands/commands/phase-draft.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -description: Draft Harness phase and step files for FESA implementation planning. ---- - -# /phase-draft - -## Preflight - -- Read `/AGENTS.md`, `/PROGRESS.md`, `/PLAN.md`, `/docs/README.md`, `/docs/HARNESS_ENGINEERING.md`, and all Phase 1 design docs. -- Use `$fesa-readiness` first. -- Use `$fesa-phase-planning` and the repo `harness-workflow` skill when file generation is requested. -- Do not spawn subagents unless the user explicitly asks for delegated work. - -## Task - -- Identify readiness blockers before drafting steps. -- Keep each phase step self-contained for a fresh Codex session. -- Include a sprint contract: objective, required reading, scope, allowed files, explicit non-goals, tests to write first, reference artifacts, acceptance commands, evaluator checklist, and handoff requirements. -- Do not hide unresolved formulation or reference decisions inside broad implementation steps. - -## Verification - -- Parse generated JSON phase files. -- Re-read generated `stepN.md` files for self-containment. -- Run `python scripts/validate_workspace.py`. - -## Result - -- **Action**: drafted phase files -- **Status**: success | partial | blocked -- **Details**: phase directory, step count, blockers, and validation result diff --git a/plugins/fesa-commands/commands/plan-sync.md b/plugins/fesa-commands/commands/plan-sync.md deleted file mode 100644 index 770c951..0000000 --- a/plugins/fesa-commands/commands/plan-sync.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -description: Reconcile PLAN.md and PROGRESS.md after documentation, planning, or implementation work. ---- - -# /plan-sync - -## Preflight - -- Read `/AGENTS.md`, `/PROGRESS.md`, `/PLAN.md`, and changed files. -- Use `$fesa-doc-sync` when available. -- Identify completed work, future tasks, blockers, and stale items. - -## Task - -- Move completed facts into `PROGRESS.md`. -- Keep future tasks and open decisions in `PLAN.md`. -- Preserve history unless the user explicitly asks for archival cleanup. -- Add changed files and verification notes to the progress entry. - -## Verification - -- Parse or inspect any file format touched. -- Run `python scripts/validate_workspace.py`. - -## Result - -- **Action**: synchronized plan and progress -- **Status**: success | partial | blocked -- **Details**: entries changed, remaining blockers, validation result diff --git a/plugins/fesa-commands/commands/readiness.md b/plugins/fesa-commands/commands/readiness.md deleted file mode 100644 index 34b4967..0000000 --- a/plugins/fesa-commands/commands/readiness.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -description: Check whether FESA is ready to start Phase 1 implementation planning or coding. ---- - -# /readiness - -## Preflight - -- Read `/AGENTS.md`, `/PROGRESS.md`, `/PLAN.md`, and `/docs/README.md`. -- Read the readiness-related docs listed by `/docs/README.md`. -- Use `$fesa-readiness` when available. - -## Task - -- Evaluate each Phase 1 readiness item in `/PLAN.md` and the Implementation Readiness Checklist in `/docs/README.md`. -- Classify each item as ready, blocked, deferred with explicit acceptance, or unknown. -- Do not start implementation from this command. - -## Verification - -- If files are unchanged, no repository validation is required. -- If readiness notes are written to `PLAN.md` or `PROGRESS.md`, run `python scripts/validate_workspace.py`. - -## Result - -- **Action**: checked implementation readiness -- **Status**: ready | blocked | partial -- **Details**: readiness table, blockers, and recommended next decision diff --git a/plugins/fesa-commands/commands/reference-check.md b/plugins/fesa-commands/commands/reference-check.md deleted file mode 100644 index 5817dd6..0000000 --- a/plugins/fesa-commands/commands/reference-check.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -description: Inspect stored Abaqus reference artifacts for Phase 1 verification readiness. ---- - -# /reference-check - -## Preflight - -- Read `/AGENTS.md`, `/PROGRESS.md`, `/PLAN.md`, `/docs/VERIFICATION_PLAN.md`, `/docs/RESULTS_SCHEMA.md`, and `/docs/ABAQUS_INPUT_SUBSET.md`. -- Use `$fesa-reference-onboarding` when available. -- If the user explicitly asks for delegated review, use the `reference_artifact_curator` agent. - -## Task - -- Inspect the `references/` folder when present. -- Check for `.inp` files, `*_displacements.csv` files, tolerance metadata, units notes, Abaqus version/provenance, and mapping to FESA result fields. -- For displacement CSV files, verify the required columns `Node Label`, `U-U1`, `U-U2`, `U-U3`, `UR-UR1`, `UR-UR2`, `UR-UR3`. -- Flag unsupported Abaqus input features as compatibility notes; do not treat stored reference input as automatic Phase 1 parser support. -- Do not run Abaqus locally. - -## Verification - -- If no files are edited, summarize findings only. -- If manifests or docs are updated, run `python scripts/validate_workspace.py`. - -## Result - -- **Action**: checked reference artifacts -- **Status**: ready | partial | blocked -- **Details**: artifacts found, missing metadata, and comparison-readiness notes diff --git a/plugins/fesa-commands/commands/status.md b/plugins/fesa-commands/commands/status.md deleted file mode 100644 index 08dae81..0000000 --- a/plugins/fesa-commands/commands/status.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -description: Summarize the current FESA project status and next work from PLAN.md and PROGRESS.md. ---- - -# /status - -## Preflight - -- Read `/AGENTS.md`, `/PROGRESS.md`, `/PLAN.md`, and `/docs/README.md`. -- Check whether the user asked for status only or also wants edits. -- Do not modify files unless the user explicitly asks for synchronization or cleanup. - -## Task - -- Summarize current project status, blockers, risks, and the next actionable items. -- Mention whether solver implementation has started. -- Highlight unresolved Phase 1 readiness items and the current validation state. - -## Verification - -- If no edits were made, state that no validation command was required. -- If the user asked you to update files, run `python scripts/validate_workspace.py`. - -## Result - -- **Action**: reported FESA status -- **Status**: success | partial | blocked -- **Details**: current objective, blockers, next tasks, and validation note diff --git a/plugins/fesa-commands/commands/verify-docs.md b/plugins/fesa-commands/commands/verify-docs.md deleted file mode 100644 index 3caf29f..0000000 --- a/plugins/fesa-commands/commands/verify-docs.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -description: Validate Codex extension files, plugin command metadata, and project documentation metadata. ---- - -# /verify-docs - -## Preflight - -- Read `/AGENTS.md`, `/PROGRESS.md`, `/PLAN.md`, and any changed `.codex`, `plugins/`, `.agents/plugins/`, or documentation files. -- Use `$fesa-doc-sync` when available. - -## Task - -- Parse `.codex/config.toml` and `.codex/agents/*.toml`. -- Parse `.codex/hooks.json`. -- Parse `plugins/*/.codex-plugin/plugin.json`. -- Parse `.agents/plugins/marketplace.json`. -- Check `.codex/skills/*/SKILL.md` frontmatter for `name` and `description`. -- Check `plugins/*/commands/*.md` command Markdown files for description frontmatter. - -## Verification - -- Run the format checks above. -- Run `python scripts/validate_workspace.py`. - -## Result - -- **Action**: verified docs and Codex extension files -- **Status**: success | partial | blocked -- **Details**: files checked, failures, and validation result diff --git a/plugins/harness-engineering/.codex-plugin/plugin.json b/plugins/harness-engineering/.codex-plugin/plugin.json deleted file mode 100644 index 60b9951..0000000 --- a/plugins/harness-engineering/.codex-plugin/plugin.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "harness-engineering", - "version": "1.0.0", - "description": "Repo-local Harness Engineering slash commands for Codex.", - "interface": { - "displayName": "Harness Engineering", - "shortDescription": "Harness planning and review prompts for this repo", - "longDescription": "Optional local plugin that exposes Harness Engineering slash commands while the core workflow remains in repo-native AGENTS, skills, custom agents, and hooks.", - "developerName": "Local Repository", - "category": "Productivity", - "capabilities": [ - "Interactive", - "Read", - "Write" - ], - "defaultPrompt": [ - "Use Harness Engineering to plan a new phase for this repository.", - "Review my changes against the Harness docs and rules." - ], - "brandColor": "#2563EB" - } -} diff --git a/plugins/harness-engineering/agents/openai.yaml b/plugins/harness-engineering/agents/openai.yaml deleted file mode 100644 index 2671cba..0000000 --- a/plugins/harness-engineering/agents/openai.yaml +++ /dev/null @@ -1,4 +0,0 @@ -interface: - display_name: "Harness Engineering" - short_description: "Use Harness slash commands in this repository" - default_prompt: "Use Harness Engineering to plan a phase or review changes in this repository." diff --git a/plugins/harness-engineering/commands/harness.md b/plugins/harness-engineering/commands/harness.md deleted file mode 100644 index e4bc9df..0000000 --- a/plugins/harness-engineering/commands/harness.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -description: Run the Harness Engineering planning workflow for this repository. ---- - -# /harness - -## Preflight - -- Read `/AGENTS.md`, `/docs/PRD.md`, `/docs/ARCHITECTURE.md`, `/docs/ADR.md`, and `/docs/UI_GUIDE.md` if they exist. -- Confirm whether the user wants discussion only, a draft plan, or file generation under `phases/`. -- Note whether the user explicitly asked for subagents; only then consider `phase_planner` or built-in explorers/workers. - -## Plan - -- State what will be created or updated before editing files. -- If a plan already exists under `phases/`, say whether you are extending it or replacing part of it. -- Keep each proposed step small, self-contained, and independently executable. - -## Commands - -- Invoke `$harness-workflow` and follow it. -- When file generation is requested, create or update: - - `phases/index.json` - - `phases/{phase}/index.json` - - `phases/{phase}/stepN.md` -- Use `python scripts/execute.py ` as the runtime target when you need to reference execution. - -## Verification - -- Re-read the generated phase files for consistency. -- Check that step numbering, phase names, and acceptance commands line up. -- If the repo has a validator, prefer `python scripts/validate_workspace.py` as the default acceptance command unless the user specified a narrower command. - -## Summary - -## Result -- **Action**: planned or generated Harness phase files -- **Status**: success | partial | failed -- **Details**: phase name, step count, and any blockers - -## Next Steps - -- Suggest the next natural command, usually `python scripts/execute.py ` or a focused edit to one generated step. diff --git a/plugins/harness-engineering/commands/review.md b/plugins/harness-engineering/commands/review.md deleted file mode 100644 index 568d6e6..0000000 --- a/plugins/harness-engineering/commands/review.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -description: Review local changes against Harness repository rules and docs. ---- - -# /review - -## Preflight - -- Read `/AGENTS.md`, `/docs/ARCHITECTURE.md`, `/docs/ADR.md`, and `/docs/UI_GUIDE.md` if they exist. -- Identify the changed files or generated `phases/` artifacts that need review. -- If the user wants a delegated review, use the read-only custom agent `harness_reviewer` only when they explicitly asked for subagents. - -## Plan - -- State what evidence will be checked: docs, changed files, generated phase files, and validation output if available. -- Prioritize correctness, architecture drift, CRITICAL rule violations, and missing tests over style commentary. - -## Commands - -- Invoke `$harness-review`. -- Use Codex built-in `/review` when the user specifically wants a code-review style pass over the working tree or git diff. -- If validation is relevant, run `python scripts/validate_workspace.py` or explain why it was not run. - -## Verification - -- Confirm that every finding is tied to a file and an actual rule or behavioral risk. -- If no findings remain, say so explicitly and mention residual risks or missing evidence. - -## Summary - -## Result -- **Action**: reviewed Harness changes -- **Status**: success | partial | failed -- **Details**: findings, docs checked, and validation status - -## Next Steps - -- Suggest the smallest follow-up: fix the top finding, rerun validation, or execute a pending phase. diff --git a/references/README.md b/references/README.md deleted file mode 100644 index e8fb34b..0000000 --- a/references/README.md +++ /dev/null @@ -1,66 +0,0 @@ -# FESA Reference Artifacts - -This folder stores Abaqus input files and solved reference result artifacts used to verify FESA. - -Abaqus is not run by the repository validation flow. Files here are treated as stored numerical references after they are accepted. - -## Initial Accepted Files - -| Case | Input | Result Artifact | Notes | -|---|---|---|---| -| `quad_01` | `quad_01.inp` | `quad_01_displacements.csv` | Abaqus/CAE Learning Edition 2024 source input; displacement CSV has 121 nodal rows | -| `quad_02` | `quad_02.inp` | `quad_02_displacements.csv`, `quad_02_reactionforces.csv` | Abaqus/CAE Learning Edition 2024 source input; `TYPE=S4`; displacement and RF/RM CSV files each have 121 nodal rows; original input remains unsupported provenance because it contains Abaqus/CAE scaffolding | -| `quad_02_phase1` | `quad_02_phase1.inp` | `quad_02_displacements.csv`, `quad_02_reactionforces.csv` | Normalized Phase 1 parser-compatible derivative of `quad_02.inp`; preserves ids, connectivity, material, shell thickness, fixed boundary set, and concentrated load | - -Case-specific notes: -- `quad_02_notes.md` - -## Displacement CSV Format - -`*_displacements.csv` files use Abaqus-exported nodal displacement columns: - -```text -Node Label, U-U1, U-U2, U-U3, UR-UR1, UR-UR2, UR-UR3 -``` - -Mapping to FESA: - -| CSV Column | FESA Component | -|---|---| -| `Node Label` | node id | -| `U-U1` | `UX` | -| `U-U2` | `UY` | -| `U-U3` | `UZ` | -| `UR-UR1` | `RX` | -| `UR-UR2` | `RY` | -| `UR-UR3` | `RZ` | - -## Reaction CSV Format - -`*_reactionforces.csv` and `*_reactions.csv` files use Abaqus-exported nodal reaction columns: - -```text -Node Label, RF-RF1, RF-RF2, RF-RF3, RM-RM1, RM-RM2, RM-RM3 -``` - -Mapping to FESA: - -| CSV Column | FESA Component | -|---|---| -| `Node Label` | node id | -| `RF-RF1` | `RFX` | -| `RF-RF2` | `RFY` | -| `RF-RF3` | `RFZ` | -| `RM-RM1` | `RMX` | -| `RM-RM2` | `RMY` | -| `RM-RM3` | `RMZ` | - -`quad_02_reactionforces.csv` is onboarded as a stored reaction artifact, but the current FESA node-wise RF comparison does not pass yet. Keep the mismatch visible until the solver/formulation difference is explained or fixed. - -## Compatibility Notes - -Stored Abaqus inputs may contain features outside the current FESA Phase 1 parser subset. Preserve the original files and document unsupported features instead of editing them in place. - -`quad_01.inp` currently includes `TYPE=S4R`, `Part/Assembly/Instance`, `*Density`, and `NLGEOM=YES`. These are stored for reference provenance and future compatibility work; they are not Phase 1 parser acceptance requirements unless `docs/ABAQUS_INPUT_SUBSET.md` and `docs/ADR.md` are updated. - -`quad_02.inp` currently includes `TYPE=S4`, `Part/Assembly/Instance`, `*Density`, restart/output request keywords, and Abaqus/CAE step metadata. `quad_02_phase1.inp` is the accepted Phase 1-compatible derivative input. The original file must continue to be treated as stored provenance, not as parser acceptance. diff --git a/references/quad_01.inp b/references/quad_01.inp deleted file mode 100644 index ba810aa..0000000 --- a/references/quad_01.inp +++ /dev/null @@ -1,304 +0,0 @@ -*Heading -** Job name: quad_01 Model name: Model-1 -** Generated by: Abaqus/CAE Learning Edition 2024 -*Preprint, echo=NO, model=NO, history=NO, contact=NO -** -** PARTS -** -*Part, name=Part-1 -*Node - 1, 10., 0., 0. - 2, 0., 0., 0. - 3, 0., -10., 0. - 4, 10., -10., 0. - 5, -10., 0., 0. - 6, -10., -10., 0. - 7, 0., 10., 0. - 8, -10., 10., 0. - 9, 10., 10., 0. - 10, 8., 0., 0. - 11, 6., 0., 0. - 12, 4., 0., 0. - 13, 2., 0., 0. - 14, 0., -2., 0. - 15, 0., -4., 0. - 16, 0., -6., 0. - 17, 0., -8., 0. - 18, 2., -10., 0. - 19, 4., -10., 0. - 20, 6., -10., 0. - 21, 8., -10., 0. - 22, 10., -8., 0. - 23, 10., -6., 0. - 24, 10., -4., 0. - 25, 10., -2., 0. - 26, -2., 0., 0. - 27, -4., 0., 0. - 28, -6., 0., 0. - 29, -8., 0., 0. - 30, -10., -2., 0. - 31, -10., -4., 0. - 32, -10., -6., 0. - 33, -10., -8., 0. - 34, -8., -10., 0. - 35, -6., -10., 0. - 36, -4., -10., 0. - 37, -2., -10., 0. - 38, 0., 2., 0. - 39, 0., 4., 0. - 40, 0., 6., 0. - 41, 0., 8., 0. - 42, -2., 10., 0. - 43, -4., 10., 0. - 44, -6., 10., 0. - 45, -8., 10., 0. - 46, -10., 8., 0. - 47, -10., 6., 0. - 48, -10., 4., 0. - 49, -10., 2., 0. - 50, 10., 2., 0. - 51, 10., 4., 0. - 52, 10., 6., 0. - 53, 10., 8., 0. - 54, 8., 10., 0. - 55, 6., 10., 0. - 56, 4., 10., 0. - 57, 2., 10., 0. - 58, 8., -2., 0. - 59, 6., -2., 0. - 60, 4., -2., 0. - 61, 2., -2., 0. - 62, 8., -4., 0. - 63, 6., -4., 0. - 64, 4., -4., 0. - 65, 2., -4., 0. - 66, 8., -6., 0. - 67, 6., -6., 0. - 68, 4., -6., 0. - 69, 2., -6., 0. - 70, 8., -8., 0. - 71, 6., -8., 0. - 72, 4., -8., 0. - 73, 2., -8., 0. - 74, -2., -2., 0. - 75, -4., -2., 0. - 76, -6., -2., 0. - 77, -8., -2., 0. - 78, -2., -4., 0. - 79, -4., -4., 0. - 80, -6., -4., 0. - 81, -8., -4., 0. - 82, -2., -6., 0. - 83, -4., -6., 0. - 84, -6., -6., 0. - 85, -8., -6., 0. - 86, -2., -8., 0. - 87, -4., -8., 0. - 88, -6., -8., 0. - 89, -8., -8., 0. - 90, -8., 2., 0. - 91, -6., 2., 0. - 92, -4., 2., 0. - 93, -2., 2., 0. - 94, -8., 4., 0. - 95, -6., 4., 0. - 96, -4., 4., 0. - 97, -2., 4., 0. - 98, -8., 6., 0. - 99, -6., 6., 0. - 100, -4., 6., 0. - 101, -2., 6., 0. - 102, -8., 8., 0. - 103, -6., 8., 0. - 104, -4., 8., 0. - 105, -2., 8., 0. - 106, 2., 2., 0. - 107, 4., 2., 0. - 108, 6., 2., 0. - 109, 8., 2., 0. - 110, 2., 4., 0. - 111, 4., 4., 0. - 112, 6., 4., 0. - 113, 8., 4., 0. - 114, 2., 6., 0. - 115, 4., 6., 0. - 116, 6., 6., 0. - 117, 8., 6., 0. - 118, 2., 8., 0. - 119, 4., 8., 0. - 120, 6., 8., 0. - 121, 8., 8., 0. -*Element, type=S4R - 1, 1, 10, 58, 25 - 2, 10, 11, 59, 58 - 3, 11, 12, 60, 59 - 4, 12, 13, 61, 60 - 5, 13, 2, 14, 61 - 6, 25, 58, 62, 24 - 7, 58, 59, 63, 62 - 8, 59, 60, 64, 63 - 9, 60, 61, 65, 64 -10, 61, 14, 15, 65 -11, 24, 62, 66, 23 -12, 62, 63, 67, 66 -13, 63, 64, 68, 67 -14, 64, 65, 69, 68 -15, 65, 15, 16, 69 -16, 23, 66, 70, 22 -17, 66, 67, 71, 70 -18, 67, 68, 72, 71 -19, 68, 69, 73, 72 -20, 69, 16, 17, 73 -21, 22, 70, 21, 4 -22, 70, 71, 20, 21 -23, 71, 72, 19, 20 -24, 72, 73, 18, 19 -25, 73, 17, 3, 18 -26, 2, 26, 74, 14 -27, 26, 27, 75, 74 -28, 27, 28, 76, 75 -29, 28, 29, 77, 76 -30, 29, 5, 30, 77 -31, 14, 74, 78, 15 -32, 74, 75, 79, 78 -33, 75, 76, 80, 79 -34, 76, 77, 81, 80 -35, 77, 30, 31, 81 -36, 15, 78, 82, 16 -37, 78, 79, 83, 82 -38, 79, 80, 84, 83 -39, 80, 81, 85, 84 -40, 81, 31, 32, 85 -41, 16, 82, 86, 17 -42, 82, 83, 87, 86 -43, 83, 84, 88, 87 -44, 84, 85, 89, 88 -45, 85, 32, 33, 89 -46, 17, 86, 37, 3 -47, 86, 87, 36, 37 -48, 87, 88, 35, 36 -49, 88, 89, 34, 35 -50, 89, 33, 6, 34 -51, 5, 29, 90, 49 -52, 29, 28, 91, 90 -53, 28, 27, 92, 91 -54, 27, 26, 93, 92 -55, 26, 2, 38, 93 -56, 49, 90, 94, 48 -57, 90, 91, 95, 94 -58, 91, 92, 96, 95 -59, 92, 93, 97, 96 -60, 93, 38, 39, 97 -61, 48, 94, 98, 47 -62, 94, 95, 99, 98 -63, 95, 96, 100, 99 -64, 96, 97, 101, 100 -65, 97, 39, 40, 101 -66, 47, 98, 102, 46 -67, 98, 99, 103, 102 -68, 99, 100, 104, 103 -69, 100, 101, 105, 104 -70, 101, 40, 41, 105 -71, 46, 102, 45, 8 -72, 102, 103, 44, 45 -73, 103, 104, 43, 44 -74, 104, 105, 42, 43 -75, 105, 41, 7, 42 - 76, 2, 13, 106, 38 - 77, 13, 12, 107, 106 - 78, 12, 11, 108, 107 - 79, 11, 10, 109, 108 - 80, 10, 1, 50, 109 - 81, 38, 106, 110, 39 - 82, 106, 107, 111, 110 - 83, 107, 108, 112, 111 - 84, 108, 109, 113, 112 - 85, 109, 50, 51, 113 - 86, 39, 110, 114, 40 - 87, 110, 111, 115, 114 - 88, 111, 112, 116, 115 - 89, 112, 113, 117, 116 - 90, 113, 51, 52, 117 - 91, 40, 114, 118, 41 - 92, 114, 115, 119, 118 - 93, 115, 116, 120, 119 - 94, 116, 117, 121, 120 - 95, 117, 52, 53, 121 - 96, 41, 118, 57, 7 - 97, 118, 119, 56, 57 - 98, 119, 120, 55, 56 - 99, 120, 121, 54, 55 -100, 121, 53, 9, 54 -*Nset, nset=Set-1, generate - 1, 121, 1 -*Elset, elset=Set-1, generate - 1, 100, 1 -** Section: Section-1 -*Shell Section, elset=Set-1, material=Material-1 -1., 5 -*End Part -** -** -** ASSEMBLY -** -*Assembly, name=Assembly -** -*Instance, name=Part-1-1, part=Part-1 -*End Instance -** -*Nset, nset=Set-1, instance=Part-1-1 - 1, 3, 4, 5, 6, 7, 8, 9, 18, 19, 20, 21, 22, 23, 24, 25 - 30, 31, 32, 33, 34, 35, 36, 37, 42, 43, 44, 45, 46, 47, 48, 49 - 50, 51, 52, 53, 54, 55, 56, 57 -*Elset, elset=Set-1, instance=Part-1-1 - 1, 6, 11, 16, 21, 22, 23, 24, 25, 30, 35, 40, 45, 46, 47, 48 - 49, 50, 51, 56, 61, 66, 71, 72, 73, 74, 75, 80, 85, 90, 95, 96 - 97, 98, 99, 100 -*Nset, nset=Set-2, instance=Part-1-1 - 2, -*End Assembly -** -** MATERIALS -** -*Material, name=Material-1 -*Density -2700., -*Elastic - 7e+10, 0.3 -** -** BOUNDARY CONDITIONS -** -** Name: BC-1 Type: Displacement/Rotation -*Boundary -Set-1, 1, 1 -Set-1, 2, 2 -Set-1, 3, 3 -Set-1, 4, 4 -Set-1, 5, 5 -Set-1, 6, 6 -** ---------------------------------------------------------------- -** -** STEP: Step-1 -** -*Step, name=Step-1, nlgeom=YES, inc=1000 -*Static -0.1, 1., 1e-05, 1. -** -** LOADS -** -** Name: Load-1 Type: Concentrated force -*Cload -Set-2, 3, -100000. -** -** OUTPUT REQUESTS -** -*Restart, write, frequency=0 -** -** FIELD OUTPUT: F-Output-1 -** -*Output, field, variable=PRESELECT -** -** HISTORY OUTPUT: H-Output-1 -** -*Output, history, variable=PRESELECT -*End Step diff --git a/references/quad_01_displacements.csv b/references/quad_01_displacements.csv deleted file mode 100644 index 3c4ae0f..0000000 --- a/references/quad_01_displacements.csv +++ /dev/null @@ -1,122 +0,0 @@ - Node Label, U-U1, U-U2, U-U3, UR-UR1, UR-UR2, UR-UR3 -1,-4.27E-37,5.71E-38,-3.64E-33,-7.31E-33,-3.72E-32,0 -2,-5.22E-23,6.10E-23,-3.79E-05,1.44E-23,-1.94E-22,-8.84E-17 -3,-5.71E-38,4.27E-37,-3.64E-33,-3.72E-32,-7.31E-33,0 -4,0,0,0,0,0,0 -5,4.27E-37,5.71E-38,-3.64E-33,-7.31E-33,3.72E-32,0 -6,0,0,0,0,0,0 -7,5.71E-38,-4.27E-37,-3.64E-33,3.72E-32,7.31E-33,0 -8,0,0,0,0,0,0 -9,0,0,0,0,0,0 -10,-9.95E-12,-1.14E-16,-3.29E-06,1.93E-22,-2.97E-06,6.72E-17 -11,-8.43E-12,-2.92E-16,-1.12E-05,-1.13E-21,-4.70E-06,1.65E-17 -12,-2.89E-13,-5.28E-17,-2.11E-05,-2.73E-22,-4.79E-06,4.86E-17 -13,7.04E-12,-4.18E-16,-3.10E-05,3.26E-22,-5.01E-06,-1.95E-17 -14,6.41E-17,-7.04E-12,-3.10E-05,-5.01E-06,-1.03E-21,6.70E-17 -15,1.02E-16,2.88E-13,-2.11E-05,-4.79E-06,-1.20E-21,3.38E-17 -16,-1.85E-16,8.43E-12,-1.12E-05,-4.70E-06,-1.53E-21,5.49E-17 -17,1.31E-16,9.95E-12,-3.29E-06,-2.97E-06,-1.18E-22,-2.64E-17 -18,-7.86E-38,4.75E-37,-2.69E-33,-3.92E-32,-6.35E-33,1.24E-38 -19,-1.22E-37,3.95E-37,-1.98E-33,-3.40E-32,-7.96E-33,1.19E-38 -20,-8.28E-38,2.40E-37,-4.25E-34,-2.24E-32,-5.61E-33,0 -21,-6.04E-38,1.25E-37,7.97E-34,-9.75E-33,-4.89E-33,0 -22,-6.52E-38,-6.09E-38,1.23E-34,1.92E-33,2.84E-34,0 -23,-1.50E-37,-5.42E-38,-2.56E-34,2.18E-33,-3.90E-33,0 -24,-2.46E-37,-7.69E-38,-1.92E-33,3.03E-33,-1.57E-32,0 -25,-3.61E-37,-5.05E-38,-2.82E-33,4.63E-33,-2.81E-32,0 -26,-7.04E-12,4.18E-16,-3.10E-05,9.30E-22,5.01E-06,-1.95E-17 -27,2.89E-13,5.28E-17,-2.11E-05,-7.12E-22,4.79E-06,4.86E-17 -28,8.43E-12,2.92E-16,-1.12E-05,1.49E-21,4.70E-06,1.65E-17 -29,9.95E-12,1.14E-16,-3.29E-06,-4.43E-22,2.97E-06,6.72E-17 -30,3.61E-37,-5.05E-38,-2.82E-33,4.63E-33,2.81E-32,0 -31,2.46E-37,-7.69E-38,-1.92E-33,3.03E-33,1.57E-32,0 -32,1.50E-37,-5.42E-38,-2.56E-34,2.18E-33,3.90E-33,0 -33,6.52E-38,-6.09E-38,1.23E-34,1.92E-33,-2.84E-34,0 -34,-6.09E-38,6.52E-38,1.23E-34,2.84E-34,-1.92E-33,0 -35,-5.42E-38,1.50E-37,-2.56E-34,-3.90E-33,-2.18E-33,0 -36,-7.69E-38,2.46E-37,-1.92E-33,-1.57E-32,-3.03E-33,0 -37,-5.05E-38,3.61E-37,-2.82E-33,-2.81E-32,-4.63E-33,0 -38,-6.41E-17,7.04E-12,-3.10E-05,5.01E-06,2.43E-21,6.70E-17 -39,-1.02E-16,-2.88E-13,-2.11E-05,4.79E-06,1.07E-21,3.38E-17 -40,1.85E-16,-8.43E-12,-1.12E-05,4.70E-06,1.66E-21,5.49E-17 -41,-1.31E-16,-9.95E-12,-3.29E-06,2.97E-06,4.45E-23,-2.64E-17 -42,7.86E-38,-4.75E-37,-2.69E-33,3.92E-32,6.35E-33,1.24E-38 -43,1.22E-37,-3.95E-37,-1.98E-33,3.40E-32,7.96E-33,1.19E-38 -44,8.28E-38,-2.40E-37,-4.25E-34,2.24E-32,5.61E-33,0 -45,6.04E-38,-1.25E-37,7.97E-34,9.75E-33,4.89E-33,0 -46,6.52E-38,6.09E-38,1.23E-34,-1.92E-33,-2.84E-34,0 -47,1.50E-37,5.42E-38,-2.56E-34,-2.18E-33,3.90E-33,0 -48,2.46E-37,7.69E-38,-1.92E-33,-3.03E-33,1.57E-32,0 -49,3.61E-37,5.05E-38,-2.82E-33,-4.63E-33,2.81E-32,0 -50,-3.61E-37,5.05E-38,-2.82E-33,-4.63E-33,-2.81E-32,0 -51,-2.46E-37,7.69E-38,-1.92E-33,-3.03E-33,-1.57E-32,0 -52,-1.50E-37,5.42E-38,-2.56E-34,-2.18E-33,-3.90E-33,0 -53,-6.52E-38,6.09E-38,1.23E-34,-1.92E-33,2.84E-34,0 -54,6.09E-38,-6.52E-38,1.23E-34,-2.84E-34,1.92E-33,0 -55,5.42E-38,-1.50E-37,-2.56E-34,3.90E-33,2.18E-33,0 -56,7.69E-38,-2.46E-37,-1.92E-33,1.57E-32,3.03E-33,0 -57,5.05E-38,-3.61E-37,-2.82E-33,2.81E-32,4.63E-33,0 -58,-8.31E-12,2.78E-12,-3.00E-06,-3.74E-07,-2.77E-06,5.78E-14 -59,-8.28E-12,2.53E-12,-1.01E-05,-9.51E-07,-4.03E-06,6.11E-13 -60,-1.97E-13,4.77E-13,-1.91E-05,-2.05E-06,-4.80E-06,9.50E-13 -61,6.72E-12,-6.72E-12,-2.77E-05,-3.23E-06,-3.23E-06,1.52E-16 -62,-7.42E-12,3.90E-12,-2.16E-06,-5.48E-07,-1.97E-06,1.02E-13 -63,-7.20E-12,5.25E-12,-7.40E-06,-1.70E-06,-3.17E-06,5.05E-13 -64,-3.03E-12,3.03E-12,-1.39E-05,-3.07E-06,-3.07E-06,3.16E-17 -65,-4.76E-13,1.97E-13,-1.91E-05,-4.80E-06,-2.05E-06,-9.50E-13 -66,-3.50E-12,3.57E-12,-1.12E-06,-6.15E-07,-1.11E-06,-7.75E-14 -67,-5.98E-12,5.98E-12,-3.99E-06,-1.65E-06,-1.65E-06,-1.48E-17 -68,-5.24E-12,7.20E-12,-7.40E-06,-3.17E-06,-1.70E-06,-5.05E-13 -69,-2.53E-12,8.29E-12,-1.01E-05,-4.03E-06,-9.51E-07,-6.11E-13 -70,-2.52E-12,2.52E-12,-2.70E-07,-3.36E-07,-3.36E-07,2.19E-17 -71,-3.57E-12,3.50E-12,-1.12E-06,-1.11E-06,-6.15E-07,7.74E-14 -72,-3.90E-12,7.42E-12,-2.16E-06,-1.97E-06,-5.48E-07,-1.02E-13 -73,-2.78E-12,8.31E-12,-3.00E-06,-2.77E-06,-3.74E-07,-5.78E-14 -74,-6.72E-12,-6.72E-12,-2.77E-05,-3.23E-06,3.23E-06,-1.63E-16 -75,1.98E-13,4.77E-13,-1.91E-05,-2.05E-06,4.80E-06,-9.50E-13 -76,8.28E-12,2.53E-12,-1.01E-05,-9.51E-07,4.03E-06,-6.11E-13 -77,8.31E-12,2.78E-12,-3.00E-06,-3.74E-07,2.77E-06,-5.78E-14 -78,4.76E-13,1.97E-13,-1.91E-05,-4.80E-06,2.05E-06,9.50E-13 -79,3.03E-12,3.03E-12,-1.39E-05,-3.07E-06,3.07E-06,-1.58E-16 -80,7.20E-12,5.25E-12,-7.40E-06,-1.70E-06,3.17E-06,-5.05E-13 -81,7.42E-12,3.90E-12,-2.16E-06,-5.48E-07,1.97E-06,-1.02E-13 -82,2.53E-12,8.28E-12,-1.01E-05,-4.03E-06,9.51E-07,6.10E-13 -83,5.24E-12,7.20E-12,-7.40E-06,-3.17E-06,1.70E-06,5.05E-13 -84,5.98E-12,5.98E-12,-3.99E-06,-1.65E-06,1.65E-06,8.86E-17 -85,3.50E-12,3.57E-12,-1.12E-06,-6.15E-07,1.11E-06,7.74E-14 -86,2.78E-12,8.31E-12,-3.00E-06,-2.77E-06,3.74E-07,5.77E-14 -87,3.90E-12,7.42E-12,-2.16E-06,-1.97E-06,5.48E-07,1.02E-13 -88,3.57E-12,3.50E-12,-1.12E-06,-1.11E-06,6.15E-07,-7.72E-14 -89,2.52E-12,2.52E-12,-2.70E-07,-3.36E-07,3.36E-07,-9.90E-17 -90,8.31E-12,-2.78E-12,-3.00E-06,3.74E-07,2.77E-06,5.78E-14 -91,8.28E-12,-2.53E-12,-1.01E-05,9.51E-07,4.03E-06,6.11E-13 -92,1.97E-13,-4.77E-13,-1.91E-05,2.05E-06,4.80E-06,9.50E-13 -93,-6.72E-12,6.72E-12,-2.77E-05,3.23E-06,3.23E-06,1.52E-16 -94,7.42E-12,-3.90E-12,-2.16E-06,5.48E-07,1.97E-06,1.02E-13 -95,7.20E-12,-5.25E-12,-7.40E-06,1.70E-06,3.17E-06,5.05E-13 -96,3.03E-12,-3.03E-12,-1.39E-05,3.07E-06,3.07E-06,3.16E-17 -97,4.76E-13,-1.97E-13,-1.91E-05,4.80E-06,2.05E-06,-9.50E-13 -98,3.50E-12,-3.57E-12,-1.12E-06,6.15E-07,1.11E-06,-7.75E-14 -99,5.98E-12,-5.98E-12,-3.99E-06,1.65E-06,1.65E-06,-1.48E-17 -100,5.24E-12,-7.20E-12,-7.40E-06,3.17E-06,1.70E-06,-5.05E-13 -101,2.53E-12,-8.29E-12,-1.01E-05,4.03E-06,9.51E-07,-6.11E-13 -102,2.52E-12,-2.52E-12,-2.70E-07,3.36E-07,3.36E-07,2.19E-17 -103,3.57E-12,-3.50E-12,-1.12E-06,1.11E-06,6.15E-07,7.74E-14 -104,3.90E-12,-7.42E-12,-2.16E-06,1.97E-06,5.48E-07,-1.02E-13 -105,2.78E-12,-8.31E-12,-3.00E-06,2.77E-06,3.74E-07,-5.78E-14 -106,6.72E-12,6.72E-12,-2.77E-05,3.23E-06,-3.23E-06,-1.63E-16 -107,-1.98E-13,-4.77E-13,-1.91E-05,2.05E-06,-4.80E-06,-9.50E-13 -108,-8.28E-12,-2.53E-12,-1.01E-05,9.51E-07,-4.03E-06,-6.11E-13 -109,-8.31E-12,-2.78E-12,-3.00E-06,3.74E-07,-2.77E-06,-5.78E-14 -110,-4.76E-13,-1.97E-13,-1.91E-05,4.80E-06,-2.05E-06,9.50E-13 -111,-3.03E-12,-3.03E-12,-1.39E-05,3.07E-06,-3.07E-06,-1.58E-16 -112,-7.20E-12,-5.25E-12,-7.40E-06,1.70E-06,-3.17E-06,-5.05E-13 -113,-7.42E-12,-3.90E-12,-2.16E-06,5.48E-07,-1.97E-06,-1.02E-13 -114,-2.53E-12,-8.28E-12,-1.01E-05,4.03E-06,-9.51E-07,6.10E-13 -115,-5.24E-12,-7.20E-12,-7.40E-06,3.17E-06,-1.70E-06,5.05E-13 -116,-5.98E-12,-5.98E-12,-3.99E-06,1.65E-06,-1.65E-06,8.86E-17 -117,-3.50E-12,-3.57E-12,-1.12E-06,6.15E-07,-1.11E-06,7.74E-14 -118,-2.78E-12,-8.31E-12,-3.00E-06,2.77E-06,-3.74E-07,5.77E-14 -119,-3.90E-12,-7.42E-12,-2.16E-06,1.97E-06,-5.48E-07,1.02E-13 -120,-3.57E-12,-3.50E-12,-1.12E-06,1.11E-06,-6.15E-07,-7.72E-14 -121,-2.52E-12,-2.52E-12,-2.70E-07,3.36E-07,-3.36E-07,-9.90E-17 diff --git a/references/quad_02.inp b/references/quad_02.inp deleted file mode 100644 index 8a1d1a6..0000000 --- a/references/quad_02.inp +++ /dev/null @@ -1,304 +0,0 @@ -*Heading -** Job name: quad_02 Model name: Model-1 -** Generated by: Abaqus/CAE Learning Edition 2024 -*Preprint, echo=NO, model=NO, history=NO, contact=NO -** -** PARTS -** -*Part, name=Part-1 -*Node - 1, 10., 0., 0. - 2, 0., 0., 0. - 3, 0., -10., 0. - 4, 10., -10., 0. - 5, -10., 0., 0. - 6, -10., -10., 0. - 7, 0., 10., 0. - 8, -10., 10., 0. - 9, 10., 10., 0. - 10, 8., 0., 0. - 11, 6., 0., 0. - 12, 4., 0., 0. - 13, 2., 0., 0. - 14, 0., -2., 0. - 15, 0., -4., 0. - 16, 0., -6., 0. - 17, 0., -8., 0. - 18, 2., -10., 0. - 19, 4., -10., 0. - 20, 6., -10., 0. - 21, 8., -10., 0. - 22, 10., -8., 0. - 23, 10., -6., 0. - 24, 10., -4., 0. - 25, 10., -2., 0. - 26, -2., 0., 0. - 27, -4., 0., 0. - 28, -6., 0., 0. - 29, -8., 0., 0. - 30, -10., -2., 0. - 31, -10., -4., 0. - 32, -10., -6., 0. - 33, -10., -8., 0. - 34, -8., -10., 0. - 35, -6., -10., 0. - 36, -4., -10., 0. - 37, -2., -10., 0. - 38, 0., 2., 0. - 39, 0., 4., 0. - 40, 0., 6., 0. - 41, 0., 8., 0. - 42, -2., 10., 0. - 43, -4., 10., 0. - 44, -6., 10., 0. - 45, -8., 10., 0. - 46, -10., 8., 0. - 47, -10., 6., 0. - 48, -10., 4., 0. - 49, -10., 2., 0. - 50, 10., 2., 0. - 51, 10., 4., 0. - 52, 10., 6., 0. - 53, 10., 8., 0. - 54, 8., 10., 0. - 55, 6., 10., 0. - 56, 4., 10., 0. - 57, 2., 10., 0. - 58, 8., -2., 0. - 59, 6., -2., 0. - 60, 4., -2., 0. - 61, 2., -2., 0. - 62, 8., -4., 0. - 63, 6., -4., 0. - 64, 4., -4., 0. - 65, 2., -4., 0. - 66, 8., -6., 0. - 67, 6., -6., 0. - 68, 4., -6., 0. - 69, 2., -6., 0. - 70, 8., -8., 0. - 71, 6., -8., 0. - 72, 4., -8., 0. - 73, 2., -8., 0. - 74, -2., -2., 0. - 75, -4., -2., 0. - 76, -6., -2., 0. - 77, -8., -2., 0. - 78, -2., -4., 0. - 79, -4., -4., 0. - 80, -6., -4., 0. - 81, -8., -4., 0. - 82, -2., -6., 0. - 83, -4., -6., 0. - 84, -6., -6., 0. - 85, -8., -6., 0. - 86, -2., -8., 0. - 87, -4., -8., 0. - 88, -6., -8., 0. - 89, -8., -8., 0. - 90, -8., 2., 0. - 91, -6., 2., 0. - 92, -4., 2., 0. - 93, -2., 2., 0. - 94, -8., 4., 0. - 95, -6., 4., 0. - 96, -4., 4., 0. - 97, -2., 4., 0. - 98, -8., 6., 0. - 99, -6., 6., 0. - 100, -4., 6., 0. - 101, -2., 6., 0. - 102, -8., 8., 0. - 103, -6., 8., 0. - 104, -4., 8., 0. - 105, -2., 8., 0. - 106, 2., 2., 0. - 107, 4., 2., 0. - 108, 6., 2., 0. - 109, 8., 2., 0. - 110, 2., 4., 0. - 111, 4., 4., 0. - 112, 6., 4., 0. - 113, 8., 4., 0. - 114, 2., 6., 0. - 115, 4., 6., 0. - 116, 6., 6., 0. - 117, 8., 6., 0. - 118, 2., 8., 0. - 119, 4., 8., 0. - 120, 6., 8., 0. - 121, 8., 8., 0. -*Element, type=S4 - 1, 1, 10, 58, 25 - 2, 10, 11, 59, 58 - 3, 11, 12, 60, 59 - 4, 12, 13, 61, 60 - 5, 13, 2, 14, 61 - 6, 25, 58, 62, 24 - 7, 58, 59, 63, 62 - 8, 59, 60, 64, 63 - 9, 60, 61, 65, 64 -10, 61, 14, 15, 65 -11, 24, 62, 66, 23 -12, 62, 63, 67, 66 -13, 63, 64, 68, 67 -14, 64, 65, 69, 68 -15, 65, 15, 16, 69 -16, 23, 66, 70, 22 -17, 66, 67, 71, 70 -18, 67, 68, 72, 71 -19, 68, 69, 73, 72 -20, 69, 16, 17, 73 -21, 22, 70, 21, 4 -22, 70, 71, 20, 21 -23, 71, 72, 19, 20 -24, 72, 73, 18, 19 -25, 73, 17, 3, 18 -26, 2, 26, 74, 14 -27, 26, 27, 75, 74 -28, 27, 28, 76, 75 -29, 28, 29, 77, 76 -30, 29, 5, 30, 77 -31, 14, 74, 78, 15 -32, 74, 75, 79, 78 -33, 75, 76, 80, 79 -34, 76, 77, 81, 80 -35, 77, 30, 31, 81 -36, 15, 78, 82, 16 -37, 78, 79, 83, 82 -38, 79, 80, 84, 83 -39, 80, 81, 85, 84 -40, 81, 31, 32, 85 -41, 16, 82, 86, 17 -42, 82, 83, 87, 86 -43, 83, 84, 88, 87 -44, 84, 85, 89, 88 -45, 85, 32, 33, 89 -46, 17, 86, 37, 3 -47, 86, 87, 36, 37 -48, 87, 88, 35, 36 -49, 88, 89, 34, 35 -50, 89, 33, 6, 34 -51, 5, 29, 90, 49 -52, 29, 28, 91, 90 -53, 28, 27, 92, 91 -54, 27, 26, 93, 92 -55, 26, 2, 38, 93 -56, 49, 90, 94, 48 -57, 90, 91, 95, 94 -58, 91, 92, 96, 95 -59, 92, 93, 97, 96 -60, 93, 38, 39, 97 -61, 48, 94, 98, 47 -62, 94, 95, 99, 98 -63, 95, 96, 100, 99 -64, 96, 97, 101, 100 -65, 97, 39, 40, 101 -66, 47, 98, 102, 46 -67, 98, 99, 103, 102 -68, 99, 100, 104, 103 -69, 100, 101, 105, 104 -70, 101, 40, 41, 105 -71, 46, 102, 45, 8 -72, 102, 103, 44, 45 -73, 103, 104, 43, 44 -74, 104, 105, 42, 43 -75, 105, 41, 7, 42 - 76, 2, 13, 106, 38 - 77, 13, 12, 107, 106 - 78, 12, 11, 108, 107 - 79, 11, 10, 109, 108 - 80, 10, 1, 50, 109 - 81, 38, 106, 110, 39 - 82, 106, 107, 111, 110 - 83, 107, 108, 112, 111 - 84, 108, 109, 113, 112 - 85, 109, 50, 51, 113 - 86, 39, 110, 114, 40 - 87, 110, 111, 115, 114 - 88, 111, 112, 116, 115 - 89, 112, 113, 117, 116 - 90, 113, 51, 52, 117 - 91, 40, 114, 118, 41 - 92, 114, 115, 119, 118 - 93, 115, 116, 120, 119 - 94, 116, 117, 121, 120 - 95, 117, 52, 53, 121 - 96, 41, 118, 57, 7 - 97, 118, 119, 56, 57 - 98, 119, 120, 55, 56 - 99, 120, 121, 54, 55 -100, 121, 53, 9, 54 -*Nset, nset=Set-1, generate - 1, 121, 1 -*Elset, elset=Set-1, generate - 1, 100, 1 -** Section: Section-1 -*Shell Section, elset=Set-1, material=Material-1 -1., 5 -*End Part -** -** -** ASSEMBLY -** -*Assembly, name=Assembly -** -*Instance, name=Part-1-1, part=Part-1 -*End Instance -** -*Nset, nset=Set-1, instance=Part-1-1 - 1, 3, 4, 5, 6, 7, 8, 9, 18, 19, 20, 21, 22, 23, 24, 25 - 30, 31, 32, 33, 34, 35, 36, 37, 42, 43, 44, 45, 46, 47, 48, 49 - 50, 51, 52, 53, 54, 55, 56, 57 -*Elset, elset=Set-1, instance=Part-1-1 - 1, 6, 11, 16, 21, 22, 23, 24, 25, 30, 35, 40, 45, 46, 47, 48 - 49, 50, 51, 56, 61, 66, 71, 72, 73, 74, 75, 80, 85, 90, 95, 96 - 97, 98, 99, 100 -*Nset, nset=Set-2, instance=Part-1-1 - 2, -*End Assembly -** -** MATERIALS -** -*Material, name=Material-1 -*Density -2700., -*Elastic - 7e+10, 0.3 -** -** BOUNDARY CONDITIONS -** -** Name: BC-1 Type: Displacement/Rotation -*Boundary -Set-1, 1, 1 -Set-1, 2, 2 -Set-1, 3, 3 -Set-1, 4, 4 -Set-1, 5, 5 -Set-1, 6, 6 -** ---------------------------------------------------------------- -** -** STEP: Step-1 -** -*Step, name=Step-1, nlgeom=NO, inc=1000 -*Static -0.1, 1., 1e-05, 1. -** -** LOADS -** -** Name: Load-1 Type: Concentrated force -*Cload -Set-2, 3, -100000. -** -** OUTPUT REQUESTS -** -*Restart, write, frequency=0 -** -** FIELD OUTPUT: F-Output-1 -** -*Output, field, variable=PRESELECT -** -** HISTORY OUTPUT: H-Output-1 -** -*Output, history, variable=PRESELECT -*End Step diff --git a/references/quad_02_displacements.csv b/references/quad_02_displacements.csv deleted file mode 100644 index 477865e..0000000 --- a/references/quad_02_displacements.csv +++ /dev/null @@ -1,122 +0,0 @@ - Node Label, U-U1, U-U2, U-U3, UR-UR1, UR-UR2, UR-UR3 -1,0,0,-3.43E-33,-7.02E-33,-3.88E-32,0 -2,2.27E-23,1.72E-23,-3.73E-05,3.36E-22,-2.35E-22,8.58E-24 -3,0,0,-3.43E-33,-3.88E-32,-7.02E-33,0 -4,0,0,0,-4.91E-35,-4.91E-35,0 -5,0,0,-3.43E-33,-7.02E-33,3.88E-32,0 -6,0,0,0,-4.91E-35,4.91E-35,0 -7,0,0,-3.43E-33,3.88E-32,7.02E-33,0 -8,0,0,0,4.91E-35,4.91E-35,0 -9,0,0,0,4.91E-35,-4.91E-35,0 -10,-2.74E-23,5.22E-24,-3.28E-06,-7.55E-23,-2.98E-06,2.10E-24 -11,-3.72E-23,-1.23E-24,-1.11E-05,1.89E-22,-4.55E-06,-3.45E-25 -12,-3.93E-23,-2.09E-24,-2.09E-05,4.94E-22,-4.98E-06,-3.63E-24 -13,-4.92E-23,1.52E-23,-3.07E-05,-4.67E-22,-4.66E-06,-5.30E-24 -14,2.26E-23,3.70E-23,-3.07E-05,-4.66E-06,6.44E-22,-8.26E-24 -15,-6.41E-24,4.01E-23,-2.09E-05,-4.98E-06,-1.98E-24,-4.51E-24 -16,-6.74E-25,2.76E-23,-1.11E-05,-4.55E-06,1.46E-22,2.04E-24 -17,1.58E-25,7.24E-24,-3.28E-06,-2.98E-06,-1.16E-22,4.40E-25 -18,0,0,-2.96E-33,-3.95E-32,-7.79E-33,0 -19,0,0,-1.84E-33,-3.27E-32,-7.43E-33,0 -20,0,0,-4.34E-34,-2.06E-32,-6.02E-33,0 -21,0,0,7.99E-34,-7.99E-33,-3.96E-33,0 -22,0,0,1.49E-34,1.30E-33,-4.55E-34,0 -23,0,0,-3.15E-34,1.41E-33,-6.14E-33,0 -24,0,0,-1.82E-33,2.85E-33,-1.83E-32,0 -25,0,0,-3.01E-33,5.13E-33,-3.08E-32,0 -26,4.16E-23,-1.53E-23,-3.07E-05,1.06E-21,4.66E-06,1.22E-24 -27,2.96E-23,2.27E-23,-2.09E-05,-1.27E-23,4.98E-06,-1.62E-24 -28,2.82E-23,3.43E-24,-1.11E-05,-3.56E-22,4.55E-06,4.02E-24 -29,2.92E-23,-2.93E-24,-3.28E-06,3.48E-22,2.98E-06,2.55E-24 -30,0,0,-3.01E-33,5.13E-33,3.08E-32,0 -31,0,0,-1.82E-33,2.85E-33,1.83E-32,0 -32,0,0,-3.15E-34,1.41E-33,6.14E-33,0 -33,0,0,1.49E-34,1.30E-33,4.55E-34,0 -34,0,0,1.49E-34,-4.55E-34,-1.30E-33,0 -35,0,0,-3.15E-34,-6.14E-33,-1.41E-33,0 -36,0,0,-1.82E-33,-1.83E-32,-2.85E-33,0 -37,0,0,-3.01E-33,-3.08E-32,-5.13E-33,0 -38,-1.55E-23,-4.99E-23,-3.07E-05,4.66E-06,-2.50E-22,4.47E-24 -39,1.75E-24,-4.34E-23,-2.09E-05,4.98E-06,3.12E-22,-1.09E-24 -40,-7.39E-24,-3.64E-23,-1.11E-05,4.55E-06,-2.86E-22,-2.18E-25 -41,3.05E-24,-3.25E-23,-3.28E-06,2.98E-06,-1.71E-22,-2.29E-24 -42,0,0,-2.96E-33,3.95E-32,7.79E-33,0 -43,0,0,-1.84E-33,3.27E-32,7.43E-33,0 -44,0,0,-4.34E-34,2.06E-32,6.02E-33,0 -45,0,0,7.99E-34,7.99E-33,3.96E-33,0 -46,0,0,1.49E-34,-1.30E-33,4.55E-34,0 -47,0,0,-3.15E-34,-1.41E-33,6.14E-33,0 -48,0,0,-1.82E-33,-2.85E-33,1.83E-32,0 -49,0,0,-3.01E-33,-5.13E-33,3.08E-32,0 -50,0,0,-3.01E-33,-5.13E-33,-3.08E-32,0 -51,0,0,-1.82E-33,-2.85E-33,-1.83E-32,0 -52,0,0,-3.15E-34,-1.41E-33,-6.14E-33,0 -53,0,0,1.49E-34,-1.30E-33,-4.55E-34,0 -54,0,0,1.49E-34,4.55E-34,1.30E-33,0 -55,0,0,-3.15E-34,6.14E-33,1.41E-33,0 -56,0,0,-1.82E-33,1.83E-32,2.85E-33,0 -57,0,0,-3.01E-33,3.08E-32,5.13E-33,0 -58,-1.61E-23,5.75E-24,-2.97E-06,-3.53E-07,-2.71E-06,-7.38E-25 -59,-4.24E-23,1.26E-24,-1.00E-05,-1.00E-06,-4.11E-06,9.44E-26 -60,-4.57E-23,1.33E-23,-1.89E-05,-2.00E-06,-4.56E-06,2.45E-25 -61,-4.28E-23,5.81E-24,-2.74E-05,-3.36E-06,-3.36E-06,-5.34E-25 -62,-3.20E-23,4.92E-24,-2.15E-06,-5.76E-07,-1.98E-06,-1.55E-24 -63,-2.89E-23,1.37E-23,-7.32E-06,-1.64E-06,-3.05E-06,-1.18E-27 -64,-3.28E-23,1.87E-23,-1.37E-05,-3.14E-06,-3.14E-06,2.02E-24 -65,-2.98E-23,1.73E-23,-1.89E-05,-4.56E-06,-2.00E-06,8.92E-25 -66,-1.49E-23,3.90E-24,-1.11E-06,-5.88E-07,-1.07E-06,1.48E-24 -67,-2.86E-23,1.99E-23,-3.94E-06,-1.68E-06,-1.68E-06,2.48E-24 -68,-2.60E-23,1.17E-23,-7.32E-06,-3.05E-06,-1.64E-06,2.60E-24 -69,-1.43E-23,3.10E-23,-1.00E-05,-4.11E-06,-1.00E-06,1.18E-24 -70,-3.61E-25,8.84E-25,-2.68E-07,-3.47E-07,-3.47E-07,4.78E-25 -71,-1.25E-24,1.10E-23,-1.11E-06,-1.07E-06,-5.88E-07,1.71E-24 -72,-8.24E-25,1.58E-23,-2.15E-06,-1.98E-06,-5.76E-07,3.63E-24 -73,-4.43E-24,8.01E-24,-2.97E-06,-2.71E-06,-3.53E-07,2.86E-24 -74,3.50E-23,4.28E-23,-2.74E-05,-3.36E-06,3.36E-06,1.20E-24 -75,2.93E-23,1.27E-25,-1.89E-05,-2.00E-06,4.56E-06,3.53E-24 -76,3.18E-23,8.43E-24,-1.00E-05,-1.00E-06,4.11E-06,-5.30E-25 -77,3.29E-23,5.14E-24,-2.97E-06,-3.53E-07,2.71E-06,9.86E-25 -78,1.44E-23,3.01E-23,-1.89E-05,-4.56E-06,2.00E-06,1.09E-24 -79,2.35E-23,1.51E-23,-1.37E-05,-3.14E-06,3.14E-06,1.52E-24 -80,2.90E-23,1.34E-23,-7.32E-06,-1.64E-06,3.05E-06,-8.29E-25 -81,2.86E-23,6.61E-24,-2.15E-06,-5.76E-07,1.98E-06,-2.07E-24 -82,1.87E-23,2.13E-23,-1.00E-05,-4.11E-06,1.00E-06,-1.04E-24 -83,2.48E-23,2.26E-23,-7.32E-06,-3.05E-06,1.64E-06,-1.52E-24 -84,1.66E-23,1.41E-23,-3.94E-06,-1.68E-06,1.68E-06,-1.15E-24 -85,2.98E-24,1.00E-23,-1.11E-06,-5.88E-07,1.07E-06,-8.93E-25 -86,1.05E-24,5.17E-24,-2.97E-06,-2.71E-06,3.53E-07,-4.37E-24 -87,4.23E-24,2.35E-23,-2.15E-06,-1.98E-06,5.76E-07,-4.25E-24 -88,7.11E-24,1.44E-23,-1.11E-06,-1.07E-06,5.88E-07,-1.20E-25 -89,7.36E-24,7.85E-24,-2.68E-07,-3.47E-07,3.47E-07,1.43E-24 -90,1.60E-23,-3.69E-24,-2.97E-06,3.53E-07,2.71E-06,3.54E-25 -91,2.52E-23,-9.80E-24,-1.00E-05,1.00E-06,4.11E-06,-2.58E-24 -92,2.36E-23,-2.36E-23,-1.89E-05,2.00E-06,4.56E-06,-3.16E-24 -93,1.97E-23,-3.27E-23,-2.74E-05,3.36E-06,3.36E-06,7.71E-25 -94,1.66E-23,-3.12E-24,-2.15E-06,5.76E-07,1.98E-06,-1.10E-24 -95,2.90E-23,-1.25E-23,-7.32E-06,1.64E-06,3.05E-06,-7.15E-25 -96,3.20E-23,-2.29E-23,-1.37E-05,3.14E-06,3.14E-06,-1.38E-24 -97,9.08E-24,-3.82E-23,-1.89E-05,4.56E-06,2.00E-06,-7.41E-25 -98,1.23E-23,-1.96E-24,-1.11E-06,5.88E-07,1.07E-06,-6.82E-25 -99,1.11E-23,-1.77E-23,-3.94E-06,1.68E-06,1.68E-06,2.82E-25 -100,8.86E-24,-2.36E-23,-7.32E-06,3.05E-06,1.64E-06,8.36E-25 -101,5.15E-24,-3.74E-23,-1.00E-05,4.11E-06,1.00E-06,-8.90E-25 -102,4.26E-24,2.41E-24,-2.68E-07,3.47E-07,3.47E-07,2.30E-25 -103,5.08E-24,-1.05E-23,-1.11E-06,1.07E-06,5.88E-07,-1.59E-24 -104,5.64E-24,-2.14E-23,-2.15E-06,1.98E-06,5.76E-07,-5.05E-26 -105,3.39E-24,-1.97E-23,-2.97E-06,2.71E-06,3.53E-07,-7.38E-25 -106,-1.97E-23,-1.79E-23,-2.74E-05,3.36E-06,-3.36E-06,2.32E-24 -107,-3.31E-23,4.84E-25,-1.89E-05,2.00E-06,-4.56E-06,3.49E-25 -108,-3.23E-23,-3.88E-24,-1.00E-05,1.00E-06,-4.11E-06,-8.22E-25 -109,-3.17E-23,1.07E-24,-2.97E-06,3.53E-07,-2.71E-06,3.72E-26 -110,-1.74E-23,-3.89E-23,-1.89E-05,4.56E-06,-2.00E-06,2.11E-24 -111,-2.81E-23,-1.76E-23,-1.37E-05,3.14E-06,-3.14E-06,6.99E-25 -112,-3.00E-23,-6.84E-24,-7.32E-06,1.64E-06,-3.05E-06,-1.79E-24 -113,-2.38E-23,-3.23E-24,-2.15E-06,5.76E-07,-1.98E-06,-1.90E-24 -114,-1.08E-23,-3.78E-23,-1.00E-05,4.11E-06,-1.00E-06,-1.13E-24 -115,-6.66E-24,-3.10E-23,-7.32E-06,3.05E-06,-1.64E-06,4.05E-25 -116,-3.60E-24,-7.73E-24,-3.94E-06,1.68E-06,-1.68E-06,1.52E-25 -117,-9.67E-24,-2.43E-24,-1.11E-06,5.88E-07,-1.07E-06,-2.32E-24 -118,-3.01E-24,-3.07E-23,-2.97E-06,2.71E-06,-3.53E-07,-1.78E-25 -119,-1.24E-24,-2.31E-23,-2.15E-06,1.98E-06,-5.76E-07,2.82E-24 -120,-2.63E-24,-1.43E-24,-1.11E-06,1.07E-06,-5.88E-07,3.07E-24 -121,2.48E-24,5.06E-24,-2.68E-07,3.47E-07,-3.47E-07,-1.03E-24 diff --git a/references/quad_02_notes.md b/references/quad_02_notes.md deleted file mode 100644 index db61426..0000000 --- a/references/quad_02_notes.md +++ /dev/null @@ -1,127 +0,0 @@ -# quad_02 Reference Notes - -## Purpose -`quad_02` is the first stored Abaqus `TYPE=S4` reference pair intended for the Phase 1 MITC4 rebaseline path. - -The original Abaqus input remains preserved as provenance: -- `quad_02.inp` -- `quad_02_displacements.csv` -- `quad_02_reactionforces.csv` - -The Phase 1 parser-compatible derivative input is: -- `quad_02_phase1.inp` - -## Provenance -- Source solver: Abaqus/CAE Learning Edition 2024, as recorded in `quad_02.inp`. -- Original job name: `quad_02`. -- Source model name: `Model-1`. -- Unit system: self-consistent source units. FESA does not enforce or convert units. - -## Compatibility -`quad_02.inp` uses `TYPE=S4`, which is the Phase 1 target mapped to FESA `MITC4`. - -The original file also contains Abaqus/CAE features outside the current Phase 1 parser subset: -- `*Part` -- `*Assembly` -- `*Instance` -- `*Density` -- output and restart request keywords - -These features remain unsupported in Phase 1 unless `docs/ABAQUS_INPUT_SUBSET.md` and ADRs are explicitly updated. Tests must continue to reject the original file as unsupported provenance. - -## Normalized Input -`quad_02_phase1.inp` is a derivative input created for Phase 1 parser and later solver regression work. - -It preserves: -- 121 node ids and coordinates. -- 100 `TYPE=S4` quadrilateral elements and connectivity. -- Elastic material values `E = 7.0e10`, `nu = 0.3`. -- Shell thickness `1.0`. -- Fixed boundary nodes from the original assembly-level boundary set. -- Concentrated load at node `2`, DOF `3`, magnitude `-100000.0`. - -It removes: -- Abaqus/CAE `Part/Assembly/Instance` scaffolding. -- `*Density`, because density is not used by Phase 1 linear static analysis. -- restart/output requests. -- step increment parameters and static time data not needed by the Phase 1 parser subset. - -## Result Mapping -`quad_02_displacements.csv` is an Abaqus-exported nodal displacement table with 121 rows. - -Required columns: -- `Node Label` -- `U-U1` -- `U-U2` -- `U-U3` -- `UR-UR1` -- `UR-UR2` -- `UR-UR3` - -It maps to FESA field output: - -```text -/results/steps/Step-1/frames/0/fieldOutputs/U -``` - -with component order: - -```text -UX, UY, UZ, RX, RY, RZ -``` - -`quad_02_reactionforces.csv` is an Abaqus-exported nodal reaction force/moment table with 121 rows. - -Required columns: -- `Node Label` -- `RF-RF1` -- `RF-RF2` -- `RF-RF3` -- `RM-RM1` -- `RM-RM2` -- `RM-RM3` - -It maps to FESA field output: - -```text -/results/steps/Step-1/frames/0/fieldOutputs/RF -``` - -with component order: - -```text -RFX, RFY, RFZ, RMX, RMY, RMZ -``` - -## Initial Tolerance -The active automated displacement regression uses: - -```text -abs_tol = 1.0e-12 -rel_tol = 1.0e-5 -reference_scale = 1.0 -``` - -Do not tune tolerances or drilling stiffness to make this single case pass. - -## Automated Regression Status -`quad_02_phase1.inp` and `quad_02_displacements.csv` are wired into the Phase 1 test suite as the first stored Abaqus displacement regression. - -The regression compares FESA `U` against the stored Abaqus CSV by node id and uses the tolerance above. - -`quad_02_phase1.inp` and `quad_02_reactionforces.csv` are also wired through the reaction CSV loader and node-wise `RF` comparator. This comparison currently records a known non-passing gap rather than an accepted pass gate: - -```text -abs_tol = 1.0e-6 -rel_tol = 1.0e-5 -reference_scale = 1.0 -max_abs_error ~= 612.751347 -max_rel_error ~= 0.494032 -first_mismatch = node 1 RFZ, expected 6860.0, actual 6652.459896 -``` - -Do not relax reaction tolerances to make this case pass. Treat the mismatch as a solver/formulation verification item. - -## Current Limitations -- The stored Abaqus reaction CSV is available, but node-wise `RF` agreement is not accepted yet because the current comparison fails. -- This is currently the only passing stored Abaqus reference regression. The PRD target still requires at least three stored reference models: one single-element case, one simple multi-element plate/shell case, and one curved shell benchmark. diff --git a/references/quad_02_phase1.inp b/references/quad_02_phase1.inp deleted file mode 100644 index 57b7de5..0000000 --- a/references/quad_02_phase1.inp +++ /dev/null @@ -1,252 +0,0 @@ -** FESA Phase 1 normalized derivative of references/quad_02.inp -** Original source: Abaqus/CAE Learning Edition 2024, job quad_02. -** Paired displacement reference: references/quad_02_displacements.csv. -** Unit system: self-consistent source units; FESA does not enforce unit conversion. -** Normalization policy: -** - Preserve original node ids, element ids, S4 connectivity, material E/nu, -** shell thickness, fixed node set, concentrated load node, and load value. -** - Remove unsupported Abaqus/CAE scaffolding: Part, Assembly, Instance, -** Density, Restart, Output, and Step increment parameters. -*Node - 1, 10., 0., 0. - 2, 0., 0., 0. - 3, 0., -10., 0. - 4, 10., -10., 0. - 5, -10., 0., 0. - 6, -10., -10., 0. - 7, 0., 10., 0. - 8, -10., 10., 0. - 9, 10., 10., 0. - 10, 8., 0., 0. - 11, 6., 0., 0. - 12, 4., 0., 0. - 13, 2., 0., 0. - 14, 0., -2., 0. - 15, 0., -4., 0. - 16, 0., -6., 0. - 17, 0., -8., 0. - 18, 2., -10., 0. - 19, 4., -10., 0. - 20, 6., -10., 0. - 21, 8., -10., 0. - 22, 10., -8., 0. - 23, 10., -6., 0. - 24, 10., -4., 0. - 25, 10., -2., 0. - 26, -2., 0., 0. - 27, -4., 0., 0. - 28, -6., 0., 0. - 29, -8., 0., 0. - 30, -10., -2., 0. - 31, -10., -4., 0. - 32, -10., -6., 0. - 33, -10., -8., 0. - 34, -8., -10., 0. - 35, -6., -10., 0. - 36, -4., -10., 0. - 37, -2., -10., 0. - 38, 0., 2., 0. - 39, 0., 4., 0. - 40, 0., 6., 0. - 41, 0., 8., 0. - 42, -2., 10., 0. - 43, -4., 10., 0. - 44, -6., 10., 0. - 45, -8., 10., 0. - 46, -10., 8., 0. - 47, -10., 6., 0. - 48, -10., 4., 0. - 49, -10., 2., 0. - 50, 10., 2., 0. - 51, 10., 4., 0. - 52, 10., 6., 0. - 53, 10., 8., 0. - 54, 8., 10., 0. - 55, 6., 10., 0. - 56, 4., 10., 0. - 57, 2., 10., 0. - 58, 8., -2., 0. - 59, 6., -2., 0. - 60, 4., -2., 0. - 61, 2., -2., 0. - 62, 8., -4., 0. - 63, 6., -4., 0. - 64, 4., -4., 0. - 65, 2., -4., 0. - 66, 8., -6., 0. - 67, 6., -6., 0. - 68, 4., -6., 0. - 69, 2., -6., 0. - 70, 8., -8., 0. - 71, 6., -8., 0. - 72, 4., -8., 0. - 73, 2., -8., 0. - 74, -2., -2., 0. - 75, -4., -2., 0. - 76, -6., -2., 0. - 77, -8., -2., 0. - 78, -2., -4., 0. - 79, -4., -4., 0. - 80, -6., -4., 0. - 81, -8., -4., 0. - 82, -2., -6., 0. - 83, -4., -6., 0. - 84, -6., -6., 0. - 85, -8., -6., 0. - 86, -2., -8., 0. - 87, -4., -8., 0. - 88, -6., -8., 0. - 89, -8., -8., 0. - 90, -8., 2., 0. - 91, -6., 2., 0. - 92, -4., 2., 0. - 93, -2., 2., 0. - 94, -8., 4., 0. - 95, -6., 4., 0. - 96, -4., 4., 0. - 97, -2., 4., 0. - 98, -8., 6., 0. - 99, -6., 6., 0. - 100, -4., 6., 0. - 101, -2., 6., 0. - 102, -8., 8., 0. - 103, -6., 8., 0. - 104, -4., 8., 0. - 105, -2., 8., 0. - 106, 2., 2., 0. - 107, 4., 2., 0. - 108, 6., 2., 0. - 109, 8., 2., 0. - 110, 2., 4., 0. - 111, 4., 4., 0. - 112, 6., 4., 0. - 113, 8., 4., 0. - 114, 2., 6., 0. - 115, 4., 6., 0. - 116, 6., 6., 0. - 117, 8., 6., 0. - 118, 2., 8., 0. - 119, 4., 8., 0. - 120, 6., 8., 0. - 121, 8., 8., 0. -*Element, type=S4, elset=all_elements - 1, 1, 10, 58, 25 - 2, 10, 11, 59, 58 - 3, 11, 12, 60, 59 - 4, 12, 13, 61, 60 - 5, 13, 2, 14, 61 - 6, 25, 58, 62, 24 - 7, 58, 59, 63, 62 - 8, 59, 60, 64, 63 - 9, 60, 61, 65, 64 - 10, 61, 14, 15, 65 - 11, 24, 62, 66, 23 - 12, 62, 63, 67, 66 - 13, 63, 64, 68, 67 - 14, 64, 65, 69, 68 - 15, 65, 15, 16, 69 - 16, 23, 66, 70, 22 - 17, 66, 67, 71, 70 - 18, 67, 68, 72, 71 - 19, 68, 69, 73, 72 - 20, 69, 16, 17, 73 - 21, 22, 70, 21, 4 - 22, 70, 71, 20, 21 - 23, 71, 72, 19, 20 - 24, 72, 73, 18, 19 - 25, 73, 17, 3, 18 - 26, 2, 26, 74, 14 - 27, 26, 27, 75, 74 - 28, 27, 28, 76, 75 - 29, 28, 29, 77, 76 - 30, 29, 5, 30, 77 - 31, 14, 74, 78, 15 - 32, 74, 75, 79, 78 - 33, 75, 76, 80, 79 - 34, 76, 77, 81, 80 - 35, 77, 30, 31, 81 - 36, 15, 78, 82, 16 - 37, 78, 79, 83, 82 - 38, 79, 80, 84, 83 - 39, 80, 81, 85, 84 - 40, 81, 31, 32, 85 - 41, 16, 82, 86, 17 - 42, 82, 83, 87, 86 - 43, 83, 84, 88, 87 - 44, 84, 85, 89, 88 - 45, 85, 32, 33, 89 - 46, 17, 86, 37, 3 - 47, 86, 87, 36, 37 - 48, 87, 88, 35, 36 - 49, 88, 89, 34, 35 - 50, 89, 33, 6, 34 - 51, 5, 29, 90, 49 - 52, 29, 28, 91, 90 - 53, 28, 27, 92, 91 - 54, 27, 26, 93, 92 - 55, 26, 2, 38, 93 - 56, 49, 90, 94, 48 - 57, 90, 91, 95, 94 - 58, 91, 92, 96, 95 - 59, 92, 93, 97, 96 - 60, 93, 38, 39, 97 - 61, 48, 94, 98, 47 - 62, 94, 95, 99, 98 - 63, 95, 96, 100, 99 - 64, 96, 97, 101, 100 - 65, 97, 39, 40, 101 - 66, 47, 98, 102, 46 - 67, 98, 99, 103, 102 - 68, 99, 100, 104, 103 - 69, 100, 101, 105, 104 - 70, 101, 40, 41, 105 - 71, 46, 102, 45, 8 - 72, 102, 103, 44, 45 - 73, 103, 104, 43, 44 - 74, 104, 105, 42, 43 - 75, 105, 41, 7, 42 - 76, 2, 13, 106, 38 - 77, 13, 12, 107, 106 - 78, 12, 11, 108, 107 - 79, 11, 10, 109, 108 - 80, 10, 1, 50, 109 - 81, 38, 106, 110, 39 - 82, 106, 107, 111, 110 - 83, 107, 108, 112, 111 - 84, 108, 109, 113, 112 - 85, 109, 50, 51, 113 - 86, 39, 110, 114, 40 - 87, 110, 111, 115, 114 - 88, 111, 112, 116, 115 - 89, 112, 113, 117, 116 - 90, 113, 51, 52, 117 - 91, 40, 114, 118, 41 - 92, 114, 115, 119, 118 - 93, 115, 116, 120, 119 - 94, 116, 117, 121, 120 - 95, 117, 52, 53, 121 - 96, 41, 118, 57, 7 - 97, 118, 119, 56, 57 - 98, 119, 120, 55, 56 - 99, 120, 121, 54, 55 -100, 121, 53, 9, 54 -*Elset, elset=all_elements, generate -1, 100, 1 -*Nset, nset=fixed_boundary -1, 3, 4, 5, 6, 7, 8, 9, 18, 19, 20, 21, 22, 23, 24, 25 -30, 31, 32, 33, 34, 35, 36, 37, 42, 43, 44, 45, 46, 47, 48, 49 -50, 51, 52, 53, 54, 55, 56, 57 -*Nset, nset=load_node -2 -*Material, name=material_1 -*Elastic -7.0e10, 0.3 -*Shell Section, elset=all_elements, material=material_1 -1.0 -*Boundary -fixed_boundary, 1, 6, 0.0 -*Step, name=Step-1 -*Static -*Cload -load_node, 3, -100000.0 -*End Step diff --git a/references/quad_02_reactionforces.csv b/references/quad_02_reactionforces.csv deleted file mode 100644 index ece4cbe..0000000 --- a/references/quad_02_reactionforces.csv +++ /dev/null @@ -1,122 +0,0 @@ - Node Label, RF-RF1, RF-RF2, RF-RF3, RM-RM1, RM-RM2, RM-RM3 -1,-8.07E-13,-5.43E-14,6.86E+03,9.09E-13,2.40E+04,8.36E-15 -2,0,0,0,0,0,0 -3,-2.21E-13,6.87E-13,6.86E+03,2.40E+04,-1.82E-12,2.53E-16 -4,-4.31E-14,8.33E-14,-743.226746,490.872711,490.872711,0 -5,2.48E-13,-5.84E-14,6.86E+03,4.09E-12,-2.40E+04,4.70E-15 -6,-1.84E-13,-2.04E-13,-743.226746,490.872711,-490.872711,0 -7,-6.16E-13,-3.42E-13,6.86E+03,-2.40E+04,9.09E-13,-4.89E-15 -8,1.02E-13,-1.15E-13,-743.226746,-490.872711,-490.872711,0 -9,-6.54E-14,-1.07E-13,-743.226746,-490.872711,490.872711,0 -10,0,0,0,0,0,0 -11,0,0,0,0,0,0 -12,0,0,0,0,0,0 -13,0,0,0,0,0,0 -14,0,0,0,0,0,0 -15,0,0,0,0,0,0 -16,0,0,0,0,0,0 -17,0,0,0,0,0,0 -18,-1.36E-13,-1.05E-13,5.97E+03,2.17E+04,819.817993,-7.10E-15 -19,-3.44E-13,8.09E-13,3.67E+03,1.58E+04,1.42E+03,-1.32E-15 -20,2.68E-13,-1.41E-12,749.094788,8.27E+03,1.42E+03,-2.01E-15 -21,2.55E-13,-2.02E-13,-947.392273,2.61E+03,822.967346,-5.79E-16 -22,5.13E-13,-3.77E-13,-947.392273,822.967346,2.61E+03,1.42E-15 -23,-5.10E-13,-2.58E-13,749.094788,1.42E+03,8.27E+03,6.24E-15 -24,-1.15E-13,-2.30E-13,3.67E+03,1.42E+03,1.58E+04,7.89E-15 -25,4.34E-13,5.17E-14,5.97E+03,819.817993,2.17E+04,9.21E-15 -26,0,0,0,0,0,0 -27,0,0,0,0,0,0 -28,0,0,0,0,0,0 -29,0,0,0,0,0,0 -30,-1.14E-13,-1.53E-13,5.97E+03,819.817993,-2.17E+04,-8.24E-15 -31,-1.27E-12,3.44E-13,3.67E+03,1.42E+03,-1.58E+04,-1.06E-14 -32,-1.15E-13,-5.05E-13,749.094788,1.42E+03,-8.27E+03,-1.60E-14 -33,-1.12E-13,-3.07E-14,-947.392273,822.967346,-2.61E+03,-1.26E-14 -34,-4.03E-14,-1.92E-13,-947.392273,2.61E+03,-822.967346,1.18E-14 -35,1.19E-13,2.40E-13,749.094788,8.27E+03,-1.42E+03,1.14E-14 -36,-6.27E-14,8.10E-13,3.67E+03,1.58E+04,-1.42E+03,6.78E-15 -37,6.27E-14,1.81E-13,5.97E+03,2.17E+04,-819.817993,1.68E-15 -38,0,0,0,0,0,0 -39,0,0,0,0,0,0 -40,0,0,0,0,0,0 -41,0,0,0,0,0,0 -42,-5.00E-14,1.36E-12,5.97E+03,-2.17E+04,-819.817993,-5.44E-15 -43,9.99E-14,2.80E-13,3.67E+03,-1.58E+04,-1.42E+03,-9.03E-15 -44,-2.16E-13,8.84E-14,749.094788,-8.27E+03,-1.42E+03,-8.14E-15 -45,-3.92E-13,-7.78E-14,-947.392273,-2.61E+03,-822.967346,-6.83E-15 -46,1.93E-13,2.02E-13,-947.392273,-822.967346,-2.61E+03,-3.86E-15 -47,-9.65E-13,-1.51E-14,749.094788,-1.42E+03,-8.27E+03,3.14E-15 -48,9.48E-13,8.02E-14,3.67E+03,-1.42E+03,-1.58E+04,5.00E-15 -49,-7.88E-13,5.70E-14,5.97E+03,-819.817993,-2.17E+04,5.91E-15 -50,1.65E-13,-4.66E-14,5.97E+03,-819.817993,2.17E+04,1.72E-15 -51,1.08E-13,1.59E-13,3.67E+03,-1.42E+03,1.58E+04,-5.18E-15 -52,7.29E-13,1.81E-13,749.094788,-1.42E+03,8.27E+03,-3.90E-15 -53,-2.53E-13,1.69E-14,-947.392273,-822.967346,2.61E+03,8.11E-15 -54,5.14E-14,-4.00E-14,-947.392273,-2.61E+03,822.967346,-3.97E-15 -55,-6.50E-14,1.41E-13,749.094788,-8.27E+03,1.42E+03,4.21E-15 -56,3.71E-14,-8.15E-14,3.67E+03,-1.58E+04,1.42E+03,1.99E-15 -57,4.68E-13,5.31E-13,5.97E+03,-2.17E+04,819.817993,4.83E-15 -58,0,0,0,0,0,0 -59,0,0,0,0,0,0 -60,0,0,0,0,0,0 -61,0,0,0,0,0,0 -62,0,0,0,0,0,0 -63,0,0,0,0,0,0 -64,0,0,0,0,0,0 -65,0,0,0,0,0,0 -66,0,0,0,0,0,0 -67,0,0,0,0,0,0 -68,0,0,0,0,0,0 -69,0,0,0,0,0,0 -70,0,0,0,0,0,0 -71,0,0,0,0,0,0 -72,0,0,0,0,0,0 -73,0,0,0,0,0,0 -74,0,0,0,0,0,0 -75,0,0,0,0,0,0 -76,0,0,0,0,0,0 -77,0,0,0,0,0,0 -78,0,0,0,0,0,0 -79,0,0,0,0,0,0 -80,0,0,0,0,0,0 -81,0,0,0,0,0,0 -82,0,0,0,0,0,0 -83,0,0,0,0,0,0 -84,0,0,0,0,0,0 -85,0,0,0,0,0,0 -86,0,0,0,0,0,0 -87,0,0,0,0,0,0 -88,0,0,0,0,0,0 -89,0,0,0,0,0,0 -90,0,0,0,0,0,0 -91,0,0,0,0,0,0 -92,0,0,0,0,0,0 -93,0,0,0,0,0,0 -94,0,0,0,0,0,0 -95,0,0,0,0,0,0 -96,0,0,0,0,0,0 -97,0,0,0,0,0,0 -98,0,0,0,0,0,0 -99,0,0,0,0,0,0 -100,0,0,0,0,0,0 -101,0,0,0,0,0,0 -102,0,0,0,0,0,0 -103,0,0,0,0,0,0 -104,0,0,0,0,0,0 -105,0,0,0,0,0,0 -106,0,0,0,0,0,0 -107,0,0,0,0,0,0 -108,0,0,0,0,0,0 -109,0,0,0,0,0,0 -110,0,0,0,0,0,0 -111,0,0,0,0,0,0 -112,0,0,0,0,0,0 -113,0,0,0,0,0,0 -114,0,0,0,0,0,0 -115,0,0,0,0,0,0 -116,0,0,0,0,0,0 -117,0,0,0,0,0,0 -118,0,0,0,0,0,0 -119,0,0,0,0,0,0 -120,0,0,0,0,0,0 -121,0,0,0,0,0,0 diff --git a/scripts/__pycache__/execute.cpython-312.pyc b/scripts/__pycache__/execute.cpython-312.pyc deleted file mode 100644 index b92b985..0000000 Binary files a/scripts/__pycache__/execute.cpython-312.pyc and /dev/null differ diff --git a/scripts/__pycache__/validate_workspace.cpython-312.pyc b/scripts/__pycache__/validate_workspace.cpython-312.pyc deleted file mode 100644 index 525ce64..0000000 Binary files a/scripts/__pycache__/validate_workspace.cpython-312.pyc and /dev/null differ diff --git a/scripts/execute.py b/scripts/execute.py deleted file mode 100644 index 171f684..0000000 --- a/scripts/execute.py +++ /dev/null @@ -1,424 +0,0 @@ -#!/usr/bin/env python3 -""" -Harness Step Executor - run phase steps sequentially with Codex and self-correction. - -Usage: - python scripts/execute.py [--push] -""" - -import argparse -import contextlib -import json -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: - 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. 변경사항은 워킹 트리에 남겨라. step 완료 후 커밋은 execute.py가 정리한다.\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") - last_message_path = self._phase_dir / f"step{step_num}-last-message.txt" - result = subprocess.run( - ["codex", "exec", "--full-auto", "--json", "-C", self._root, "-o", str(last_message_path)], - cwd=self._root, - input=prompt, - 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]}") - - final_message = None - if last_message_path.exists(): - final_message = last_message_path.read_text(encoding="utf-8") - - output = { - "step": step_num, "name": step_name, - "exitCode": result.returncode, - "finalMessage": final_message, - "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() diff --git a/scripts/test_execute.py b/scripts/test_execute.py deleted file mode 100644 index 1039319..0000000 --- a/scripts/test_execute.py +++ /dev/null @@ -1,562 +0,0 @@ -"""execute.py Codex migration safety-net tests.""" - -import json -import sys -from datetime import datetime, timedelta -from pathlib import Path -from unittest.mock import patch, MagicMock - -import pytest - -sys.path.insert(0, str(Path(__file__).parent)) -import execute as ex - - -# --------------------------------------------------------------------------- -# Fixtures -# --------------------------------------------------------------------------- - -@pytest.fixture -def tmp_project(tmp_path): - """phases/, AGENTS.md, docs/ 를 갖춘 임시 프로젝트 구조.""" - phases_dir = tmp_path / "phases" - phases_dir.mkdir() - - agents_md = tmp_path / "AGENTS.md" - agents_md.write_text("# Rules\n- rule one\n- rule two") - - docs_dir = tmp_path / "docs" - docs_dir.mkdir() - (docs_dir / "arch.md").write_text("# Architecture\nSome content") - (docs_dir / "guide.md").write_text("# Guide\nAnother doc") - - return tmp_path - - -@pytest.fixture -def phase_dir(tmp_project): - """step 3개를 가진 phase 디렉토리.""" - d = tmp_project / "phases" / "0-mvp" - d.mkdir() - - index = { - "project": "TestProject", - "phase": "mvp", - "steps": [ - {"step": 0, "name": "setup", "status": "completed", "summary": "프로젝트 초기화 완료"}, - {"step": 1, "name": "core", "status": "completed", "summary": "핵심 로직 구현"}, - {"step": 2, "name": "ui", "status": "pending"}, - ], - } - (d / "index.json").write_text(json.dumps(index, indent=2, ensure_ascii=False)) - (d / "step2.md").write_text("# Step 2: UI\n\nUI를 구현하세요.") - - return d - - -@pytest.fixture -def top_index(tmp_project): - """phases/index.json (top-level).""" - top = { - "phases": [ - {"dir": "0-mvp", "status": "pending"}, - {"dir": "1-polish", "status": "pending"}, - ] - } - p = tmp_project / "phases" / "index.json" - p.write_text(json.dumps(top, indent=2)) - return p - - -@pytest.fixture -def executor(tmp_project, phase_dir): - """테스트용 StepExecutor 인스턴스. git 호출은 별도 mock 필요.""" - with patch.object(ex, "ROOT", tmp_project): - inst = ex.StepExecutor("0-mvp") - # 내부 경로를 tmp_project 기준으로 재설정 - inst._root = str(tmp_project) - inst._phases_dir = tmp_project / "phases" - inst._phase_dir = phase_dir - inst._phase_dir_name = "0-mvp" - inst._index_file = phase_dir / "index.json" - inst._top_index_file = tmp_project / "phases" / "index.json" - return inst - - -# --------------------------------------------------------------------------- -# _stamp (= 이전 now_iso) -# --------------------------------------------------------------------------- - -class TestStamp: - def test_returns_kst_timestamp(self, executor): - result = executor._stamp() - assert "+0900" in result - - def test_format_is_iso(self, executor): - result = executor._stamp() - dt = datetime.strptime(result, "%Y-%m-%dT%H:%M:%S%z") - assert dt.tzinfo is not None - - def test_is_current_time(self, executor): - before = datetime.now(ex.StepExecutor.TZ).replace(microsecond=0) - result = executor._stamp() - after = datetime.now(ex.StepExecutor.TZ).replace(microsecond=0) + timedelta(seconds=1) - parsed = datetime.strptime(result, "%Y-%m-%dT%H:%M:%S%z") - assert before <= parsed <= after - - -# --------------------------------------------------------------------------- -# _read_json / _write_json -# --------------------------------------------------------------------------- - -class TestJsonHelpers: - def test_roundtrip(self, tmp_path): - data = {"key": "값", "nested": [1, 2, 3]} - p = tmp_path / "test.json" - ex.StepExecutor._write_json(p, data) - loaded = ex.StepExecutor._read_json(p) - assert loaded == data - - def test_save_ensures_ascii_false(self, tmp_path): - p = tmp_path / "test.json" - ex.StepExecutor._write_json(p, {"한글": "테스트"}) - raw = p.read_text() - assert "한글" in raw - assert "\\u" not in raw - - def test_save_indented(self, tmp_path): - p = tmp_path / "test.json" - ex.StepExecutor._write_json(p, {"a": 1}) - raw = p.read_text() - assert "\n" in raw - - def test_load_nonexistent_raises(self, tmp_path): - with pytest.raises(FileNotFoundError): - ex.StepExecutor._read_json(tmp_path / "nope.json") - - -# --------------------------------------------------------------------------- -# _load_guardrails -# --------------------------------------------------------------------------- - -class TestLoadGuardrails: - def test_loads_agents_md_and_docs(self, executor, tmp_project): - with patch.object(ex, "ROOT", tmp_project): - result = executor._load_guardrails() - assert "# Rules" in result - assert "rule one" in result - assert "# Architecture" in result - assert "# Guide" in result - - def test_sections_separated_by_divider(self, executor, tmp_project): - with patch.object(ex, "ROOT", tmp_project): - result = executor._load_guardrails() - assert "---" in result - - def test_docs_sorted_alphabetically(self, executor, tmp_project): - with patch.object(ex, "ROOT", tmp_project): - result = executor._load_guardrails() - arch_pos = result.index("arch") - guide_pos = result.index("guide") - assert arch_pos < guide_pos - - def test_no_agents_md(self, executor, tmp_project): - (tmp_project / "AGENTS.md").unlink() - with patch.object(ex, "ROOT", tmp_project): - result = executor._load_guardrails() - assert "AGENTS.md" not in result - assert "Architecture" in result - - def test_no_docs_dir(self, executor, tmp_project): - import shutil - shutil.rmtree(tmp_project / "docs") - with patch.object(ex, "ROOT", tmp_project): - result = executor._load_guardrails() - assert "Rules" in result - assert "Architecture" not in result - - def test_empty_project(self, tmp_path): - with patch.object(ex, "ROOT", tmp_path): - # executor가 필요 없는 static-like 동작이므로 임시 인스턴스 - phases_dir = tmp_path / "phases" / "dummy" - phases_dir.mkdir(parents=True) - idx = {"project": "T", "phase": "t", "steps": []} - (phases_dir / "index.json").write_text(json.dumps(idx)) - inst = ex.StepExecutor.__new__(ex.StepExecutor) - result = inst._load_guardrails() - assert result == "" - - -# --------------------------------------------------------------------------- -# _build_step_context -# --------------------------------------------------------------------------- - -class TestBuildStepContext: - def test_includes_completed_with_summary(self, phase_dir): - index = json.loads((phase_dir / "index.json").read_text()) - result = ex.StepExecutor._build_step_context(index) - assert "Step 0 (setup): 프로젝트 초기화 완료" in result - assert "Step 1 (core): 핵심 로직 구현" in result - - def test_excludes_pending(self, phase_dir): - index = json.loads((phase_dir / "index.json").read_text()) - result = ex.StepExecutor._build_step_context(index) - assert "ui" not in result - - def test_excludes_completed_without_summary(self, phase_dir): - index = json.loads((phase_dir / "index.json").read_text()) - del index["steps"][0]["summary"] - result = ex.StepExecutor._build_step_context(index) - assert "setup" not in result - assert "core" in result - - def test_empty_when_no_completed(self): - index = {"steps": [{"step": 0, "name": "a", "status": "pending"}]} - result = ex.StepExecutor._build_step_context(index) - assert result == "" - - def test_has_header(self, phase_dir): - index = json.loads((phase_dir / "index.json").read_text()) - result = ex.StepExecutor._build_step_context(index) - assert result.startswith("## 이전 Step 산출물") - - -# --------------------------------------------------------------------------- -# _build_preamble -# --------------------------------------------------------------------------- - -class TestBuildPreamble: - def test_includes_project_name(self, executor): - result = executor._build_preamble("", "") - assert "TestProject" in result - - def test_includes_guardrails(self, executor): - result = executor._build_preamble("GUARD_CONTENT", "") - assert "GUARD_CONTENT" in result - - def test_includes_step_context(self, executor): - ctx = "## 이전 Step 산출물\n\n- Step 0: done" - result = executor._build_preamble("", ctx) - assert "이전 Step 산출물" in result - - def test_mentions_executor_commits(self, executor): - result = executor._build_preamble("", "") - assert "커밋은 execute.py가 정리한다" in result - - def test_includes_rules(self, executor): - result = executor._build_preamble("", "") - assert "작업 규칙" in result - assert "AC" in result - - def test_no_retry_section_by_default(self, executor): - result = executor._build_preamble("", "") - assert "이전 시도 실패" not in result - - def test_retry_section_with_prev_error(self, executor): - result = executor._build_preamble("", "", prev_error="타입 에러 발생") - assert "이전 시도 실패" in result - assert "타입 에러 발생" in result - - def test_includes_max_retries(self, executor): - result = executor._build_preamble("", "") - assert str(ex.StepExecutor.MAX_RETRIES) in result - - def test_includes_index_path(self, executor): - result = executor._build_preamble("", "") - assert "/phases/0-mvp/index.json" in result - - -# --------------------------------------------------------------------------- -# _update_top_index -# --------------------------------------------------------------------------- - -class TestUpdateTopIndex: - def test_completed(self, executor, top_index): - executor._top_index_file = top_index - executor._update_top_index("completed") - data = json.loads(top_index.read_text()) - mvp = next(p for p in data["phases"] if p["dir"] == "0-mvp") - assert mvp["status"] == "completed" - assert "completed_at" in mvp - - def test_error(self, executor, top_index): - executor._top_index_file = top_index - executor._update_top_index("error") - data = json.loads(top_index.read_text()) - mvp = next(p for p in data["phases"] if p["dir"] == "0-mvp") - assert mvp["status"] == "error" - assert "failed_at" in mvp - - def test_blocked(self, executor, top_index): - executor._top_index_file = top_index - executor._update_top_index("blocked") - data = json.loads(top_index.read_text()) - mvp = next(p for p in data["phases"] if p["dir"] == "0-mvp") - assert mvp["status"] == "blocked" - assert "blocked_at" in mvp - - def test_other_phases_unchanged(self, executor, top_index): - executor._top_index_file = top_index - executor._update_top_index("completed") - data = json.loads(top_index.read_text()) - polish = next(p for p in data["phases"] if p["dir"] == "1-polish") - assert polish["status"] == "pending" - - def test_nonexistent_dir_is_noop(self, executor, top_index): - executor._top_index_file = top_index - executor._phase_dir_name = "no-such-dir" - original = json.loads(top_index.read_text()) - executor._update_top_index("completed") - after = json.loads(top_index.read_text()) - for p_before, p_after in zip(original["phases"], after["phases"]): - assert p_before["status"] == p_after["status"] - - def test_no_top_index_file(self, executor, tmp_path): - executor._top_index_file = tmp_path / "nonexistent.json" - executor._update_top_index("completed") # should not raise - - -# --------------------------------------------------------------------------- -# _checkout_branch (mocked) -# --------------------------------------------------------------------------- - -class TestCheckoutBranch: - def _mock_git(self, executor, responses): - call_idx = {"i": 0} - def fake_git(*args): - idx = call_idx["i"] - call_idx["i"] += 1 - if idx < len(responses): - return responses[idx] - return MagicMock(returncode=0, stdout="", stderr="") - executor._run_git = fake_git - - def test_already_on_branch(self, executor): - self._mock_git(executor, [ - MagicMock(returncode=0, stdout="feat-mvp\n", stderr=""), - ]) - executor._checkout_branch() # should return without checkout - - def test_branch_exists_checkout(self, executor): - self._mock_git(executor, [ - MagicMock(returncode=0, stdout="main\n", stderr=""), - MagicMock(returncode=0, stdout="", stderr=""), - MagicMock(returncode=0, stdout="", stderr=""), - ]) - executor._checkout_branch() - - def test_branch_not_exists_create(self, executor): - self._mock_git(executor, [ - MagicMock(returncode=0, stdout="main\n", stderr=""), - MagicMock(returncode=1, stdout="", stderr="not found"), - MagicMock(returncode=0, stdout="", stderr=""), - ]) - executor._checkout_branch() - - def test_checkout_fails_exits(self, executor): - self._mock_git(executor, [ - MagicMock(returncode=0, stdout="main\n", stderr=""), - MagicMock(returncode=1, stdout="", stderr=""), - MagicMock(returncode=1, stdout="", stderr="dirty tree"), - ]) - with pytest.raises(SystemExit) as exc_info: - executor._checkout_branch() - assert exc_info.value.code == 1 - - def test_no_git_exits(self, executor): - self._mock_git(executor, [ - MagicMock(returncode=1, stdout="", stderr="not a git repo"), - ]) - with pytest.raises(SystemExit) as exc_info: - executor._checkout_branch() - assert exc_info.value.code == 1 - - -# --------------------------------------------------------------------------- -# _commit_step (mocked) -# --------------------------------------------------------------------------- - -class TestCommitStep: - def test_two_phase_commit(self, executor): - calls = [] - def fake_git(*args): - calls.append(args) - if args[:2] == ("diff", "--cached"): - return MagicMock(returncode=1) - return MagicMock(returncode=0, stdout="", stderr="") - executor._run_git = fake_git - - executor._commit_step(2, "ui") - - commit_calls = [c for c in calls if c[0] == "commit"] - assert len(commit_calls) == 2 - assert "feat(mvp):" in commit_calls[0][2] - assert "chore(mvp):" in commit_calls[1][2] - - def test_no_code_changes_skips_feat_commit(self, executor): - call_count = {"diff": 0} - calls = [] - def fake_git(*args): - calls.append(args) - if args[:2] == ("diff", "--cached"): - call_count["diff"] += 1 - if call_count["diff"] == 1: - return MagicMock(returncode=0) - return MagicMock(returncode=1) - return MagicMock(returncode=0, stdout="", stderr="") - executor._run_git = fake_git - - executor._commit_step(2, "ui") - - commit_msgs = [c[2] for c in calls if c[0] == "commit"] - assert len(commit_msgs) == 1 - assert "chore" in commit_msgs[0] - - -# --------------------------------------------------------------------------- -# _invoke_codex (mocked) -# --------------------------------------------------------------------------- - -class TestInvokeCodex: - def test_invokes_codex_with_correct_args(self, executor): - mock_result = MagicMock(returncode=0, stdout='{"result": "ok"}', stderr="") - step = {"step": 2, "name": "ui"} - preamble = "PREAMBLE\n" - - with patch("subprocess.run", return_value=mock_result) as mock_run: - output = executor._invoke_codex(step, preamble) - - cmd = mock_run.call_args[0][0] - kwargs = mock_run.call_args[1] - assert cmd[0] == "codex" - assert cmd[1] == "exec" - assert "--full-auto" in cmd - assert "--json" in cmd - assert "-o" in cmd - assert "PREAMBLE" in kwargs["input"] - assert "UI를 구현하세요" in kwargs["input"] - assert output["finalMessage"] is None - - def test_saves_output_json(self, executor): - def fake_run(*args, **kwargs): - cmd = args[0] - last_message_path = Path(cmd[cmd.index("-o") + 1]) - last_message_path.write_text("completed", encoding="utf-8") - return MagicMock(returncode=0, stdout='{"ok": true}', stderr="") - - step = {"step": 2, "name": "ui"} - - with patch("subprocess.run", side_effect=fake_run): - executor._invoke_codex(step, "preamble") - - output_file = executor._phase_dir / "step2-output.json" - assert output_file.exists() - data = json.loads(output_file.read_text()) - assert data["step"] == 2 - assert data["name"] == "ui" - assert data["exitCode"] == 0 - assert data["finalMessage"] == "completed" - - def test_nonexistent_step_file_exits(self, executor): - step = {"step": 99, "name": "nonexistent"} - with pytest.raises(SystemExit) as exc_info: - executor._invoke_codex(step, "preamble") - assert exc_info.value.code == 1 - - def test_timeout_is_1800(self, executor): - mock_result = MagicMock(returncode=0, stdout="{}", stderr="") - step = {"step": 2, "name": "ui"} - - with patch("subprocess.run", return_value=mock_result) as mock_run: - executor._invoke_codex(step, "preamble") - - assert mock_run.call_args[1]["timeout"] == 1800 - - -# --------------------------------------------------------------------------- -# progress_indicator (= 이전 Spinner) -# --------------------------------------------------------------------------- - -class TestProgressIndicator: - def test_context_manager(self): - import time - with ex.progress_indicator("test") as pi: - time.sleep(0.15) - assert pi.elapsed >= 0.1 - - def test_elapsed_increases(self): - import time - with ex.progress_indicator("test") as pi: - time.sleep(0.2) - assert pi.elapsed > 0 - - -# --------------------------------------------------------------------------- -# main() CLI 파싱 (mocked) -# --------------------------------------------------------------------------- - -class TestMainCli: - def test_no_args_exits(self): - with patch("sys.argv", ["execute.py"]): - with pytest.raises(SystemExit) as exc_info: - ex.main() - assert exc_info.value.code == 2 # argparse exits with 2 - - def test_invalid_phase_dir_exits(self): - with patch("sys.argv", ["execute.py", "nonexistent"]): - with patch.object(ex, "ROOT", Path("/tmp/fake_nonexistent")): - with pytest.raises(SystemExit) as exc_info: - ex.main() - assert exc_info.value.code == 1 - - def test_missing_index_exits(self, tmp_project): - (tmp_project / "phases" / "empty").mkdir() - with patch("sys.argv", ["execute.py", "empty"]): - with patch.object(ex, "ROOT", tmp_project): - with pytest.raises(SystemExit) as exc_info: - ex.main() - assert exc_info.value.code == 1 - - -# --------------------------------------------------------------------------- -# _check_blockers (= 이전 main() error/blocked 체크) -# --------------------------------------------------------------------------- - -class TestCheckBlockers: - def _make_executor_with_steps(self, tmp_project, steps): - d = tmp_project / "phases" / "test-phase" - d.mkdir(exist_ok=True) - index = {"project": "T", "phase": "test", "steps": steps} - (d / "index.json").write_text(json.dumps(index)) - - with patch.object(ex, "ROOT", tmp_project): - inst = ex.StepExecutor.__new__(ex.StepExecutor) - inst._root = str(tmp_project) - inst._phases_dir = tmp_project / "phases" - inst._phase_dir = d - inst._phase_dir_name = "test-phase" - inst._index_file = d / "index.json" - inst._top_index_file = tmp_project / "phases" / "index.json" - inst._phase_name = "test" - inst._total = len(steps) - return inst - - def test_error_step_exits_1(self, tmp_project): - steps = [ - {"step": 0, "name": "ok", "status": "completed"}, - {"step": 1, "name": "bad", "status": "error", "error_message": "fail"}, - ] - inst = self._make_executor_with_steps(tmp_project, steps) - with pytest.raises(SystemExit) as exc_info: - inst._check_blockers() - assert exc_info.value.code == 1 - - def test_blocked_step_exits_2(self, tmp_project): - steps = [ - {"step": 0, "name": "ok", "status": "completed"}, - {"step": 1, "name": "stuck", "status": "blocked", "blocked_reason": "API key"}, - ] - inst = self._make_executor_with_steps(tmp_project, steps) - with pytest.raises(SystemExit) as exc_info: - inst._check_blockers() - assert exc_info.value.code == 2 diff --git a/scripts/validate_workspace.py b/scripts/validate_workspace.py deleted file mode 100644 index 5b3f042..0000000 --- a/scripts/validate_workspace.py +++ /dev/null @@ -1,108 +0,0 @@ -#!/usr/bin/env python3 -"""Run repository validation commands for the Harness template.""" - -from __future__ import annotations - -import json -import os -import subprocess -import sys -from pathlib import Path - - -DEFAULT_NPM_ORDER = ("lint", "build", "test") - - -def load_env_commands() -> list[str]: - raw = os.environ.get("HARNESS_VALIDATION_COMMANDS", "") - return [line.strip() for line in raw.splitlines() if line.strip()] - - -def load_npm_commands(root: Path) -> list[str]: - package_json = root / "package.json" - if not package_json.exists(): - return [] - - try: - payload = json.loads(package_json.read_text(encoding="utf-8")) - except json.JSONDecodeError: - return [] - - scripts = payload.get("scripts", {}) - if not isinstance(scripts, dict): - return [] - - commands = [] - for name in DEFAULT_NPM_ORDER: - value = scripts.get(name) - if isinstance(value, str) and value.strip(): - commands.append(f"npm run {name}") - return commands - - -def load_cmake_commands(root: Path) -> list[str]: - if not (root / "CMakeLists.txt").exists(): - return [] - - build_dir = root / "build" - return [ - f'cmake -S "{root}" -B "{build_dir}"', - f'cmake --build "{build_dir}" --config Debug', - f'ctest --test-dir "{build_dir}" --output-on-failure -C Debug', - ] - - -def discover_commands(root: Path) -> list[str]: - env_commands = load_env_commands() - if env_commands: - return env_commands - cmake_commands = load_cmake_commands(root) - if cmake_commands: - return cmake_commands - return load_npm_commands(root) - - -def run_command(command: str, root: Path) -> subprocess.CompletedProcess: - return subprocess.run( - command, - cwd=root, - shell=True, - capture_output=True, - text=True, - encoding="utf-8", - errors="replace", - ) - - -def emit_stream(prefix: str, content: str, *, stream) -> None: - text = (content or "").strip() - if not text: - return - print(prefix, file=stream) - print(text, file=stream) - - -def main() -> int: - root = Path(__file__).resolve().parent.parent - commands = discover_commands(root) - - if not commands: - print("No validation commands configured.") - print("Set HARNESS_VALIDATION_COMMANDS or add npm scripts for lint/build/test.") - return 0 - - for command in commands: - print(f"$ {command}") - result = run_command(command, root) - emit_stream("[stdout]", result.stdout, stream=sys.stdout) - emit_stream("[stderr]", result.stderr, stream=sys.stderr) - if result.returncode != 0: - print(f"Validation failed: {command}", file=sys.stderr) - return result.returncode - - print("Validation succeeded.") - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/src/Analysis/Analysis.cpp b/src/Analysis/Analysis.cpp deleted file mode 100644 index b1d6cd8..0000000 --- a/src/Analysis/Analysis.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "fesa/Analysis/Analysis.hpp" diff --git a/src/Assembly/Assembly.cpp b/src/Assembly/Assembly.cpp deleted file mode 100644 index ffaabf3..0000000 --- a/src/Assembly/Assembly.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "fesa/Assembly/Assembly.hpp" diff --git a/src/Boundary/Boundary.cpp b/src/Boundary/Boundary.cpp deleted file mode 100644 index 51af5af..0000000 --- a/src/Boundary/Boundary.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "fesa/Boundary/Boundary.hpp" diff --git a/src/Core/Core.cpp b/src/Core/Core.cpp deleted file mode 100644 index dd9e527..0000000 --- a/src/Core/Core.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "fesa/Core/Core.hpp" diff --git a/src/Element/Element.cpp b/src/Element/Element.cpp deleted file mode 100644 index 198358f..0000000 --- a/src/Element/Element.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "fesa/Element/Element.hpp" diff --git a/src/IO/IO.cpp b/src/IO/IO.cpp deleted file mode 100644 index 2a5f91e..0000000 --- a/src/IO/IO.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "fesa/IO/IO.hpp" diff --git a/src/Load/Load.cpp b/src/Load/Load.cpp deleted file mode 100644 index c989cf4..0000000 --- a/src/Load/Load.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "fesa/Load/Load.hpp" diff --git a/src/Material/Material.cpp b/src/Material/Material.cpp deleted file mode 100644 index 4b8f664..0000000 --- a/src/Material/Material.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "fesa/Material/Material.hpp" diff --git a/src/Math/Math.cpp b/src/Math/Math.cpp deleted file mode 100644 index 0b8a4ec..0000000 --- a/src/Math/Math.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "fesa/Math/Math.hpp" diff --git a/src/Property/Property.cpp b/src/Property/Property.cpp deleted file mode 100644 index e4b2b32..0000000 --- a/src/Property/Property.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "fesa/Property/Property.hpp" diff --git a/src/Results/Results.cpp b/src/Results/Results.cpp deleted file mode 100644 index 9872fba..0000000 --- a/src/Results/Results.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "fesa/Results/Results.hpp" diff --git a/src/Util/Util.cpp b/src/Util/Util.cpp deleted file mode 100644 index de349a9..0000000 --- a/src/Util/Util.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "fesa/Util/Util.hpp" diff --git a/src/fesa.cpp b/src/fesa.cpp deleted file mode 100644 index abc6906..0000000 --- a/src/fesa.cpp +++ /dev/null @@ -1,8 +0,0 @@ -#include "fesa/fesa.hpp" - -namespace fesa { -static_assert(sizeof(GlobalId) == 8, "GlobalId must remain 64-bit"); -static_assert(sizeof(EquationId) == 8, "EquationId must remain 64-bit"); -static_assert(sizeof(SparseIndex) == 8, "SparseIndex must remain 64-bit"); -static_assert(sizeof(Real) == 8, "Real must remain double precision"); -} // namespace fesa diff --git a/tests/test_analysis_module_includes.cpp b/tests/test_analysis_module_includes.cpp deleted file mode 100644 index b4c12b5..0000000 --- a/tests/test_analysis_module_includes.cpp +++ /dev/null @@ -1,129 +0,0 @@ -#include "fesa/Analysis/Analysis.hpp" - -#include -#include -#include -#include -#include - -namespace { - -void check(bool value, const char* message) { - if (!value) { - throw std::runtime_error(message); - } -} - -void checkNear(fesa::Real actual, fesa::Real expected, fesa::Real tolerance, const char* message) { - if (std::fabs(actual - expected) > tolerance) { - throw std::runtime_error(message); - } -} - -std::string phase1Input() { - return R"inp( -*Node -1, 0, 0, 0 -2, 1, 0, 0 -3, 1, 1, 0 -4, 0, 1, 0 -*Element, type=S4, elset=EALL -1, 1, 2, 3, 4 -*Nset, nset=LEFT -1, 4 -*Nset, nset=RIGHT -2, 3 -*Elset, elset=EALL -1 -*Material, name=STEEL -*Elastic -1000.0, 0.3 -*Shell Section, elset=EALL, material=STEEL -0.1 -*Boundary -LEFT, 1, 6, 0 -RIGHT, 1, 2, 0 -RIGHT, 4, 6, 0 -*Cload -2, 3, -1 -3, 3, -1 -*Step, name=Step-1 -*Static -*End Step -)inp"; -} - -class RecordingSolver final : public fesa::LinearSolver { - public: - explicit RecordingSolver(std::vector solution) : solution_(std::move(solution)) {} - - fesa::SolveResult solve(fesa::DenseMatrix a, std::vector b) const override { - called = true; - captured_a = std::move(a); - captured_b = std::move(b); - return {solution_, {}}; - } - - mutable bool called = false; - mutable fesa::DenseMatrix captured_a; - mutable std::vector captured_b; - - private: - std::vector solution_; -}; - -} // namespace - -int main() { - const auto workflow = fesa::runLinearStaticInputString(phase1Input(), "analysis-module.inp"); - check(workflow.ok(), "linear static input workflow should remain valid"); - check(workflow.model.ok(), "analysis model should remain valid"); - check(workflow.model.step.name == "Step-1", "analysis step name changed"); - check(workflow.state.converged, "analysis convergence flag changed"); - check(workflow.state.u_full.size() == 24, "full displacement size changed"); - check(workflow.state.f_external_full.size() == 24, "full external-force size changed"); - check(workflow.state.f_internal_full.size() == 24, "full internal-force size changed"); - check(workflow.state.reaction_full.size() == 24, "full reaction size changed"); - check(workflow.result_file.steps.size() == 1, "result step count changed"); - const auto& frame = workflow.result_file.steps.front().frames.front(); - check(frame.field_outputs.count("U") == 1, "U field output missing"); - check(frame.field_outputs.count("RF") == 1, "RF field output missing"); - check(frame.field_outputs.at("U").component_labels == fesa::displacementComponentLabels(), "U labels changed"); - check(frame.field_outputs.at("RF").component_labels == fesa::reactionComponentLabels(), "RF labels changed"); - - fesa::Real total_rf_z = 0.0; - for (const auto& values : frame.field_outputs.at("RF").values) { - total_rf_z += values[2]; - } - checkNear(total_rf_z, 2.0, 1.0e-8, "full-vector RF balance changed"); - - fesa::AbaqusInputParser parser; - const auto parsed = parser.parseString(phase1Input()); - check(parsed.ok(), "phase1 analysis input parse changed"); - RecordingSolver solver({0.25, -0.50}); - fesa::LinearStaticAnalysis analysis(&solver); - const auto injected = analysis.run(parsed.domain); - check(injected.ok(), "solver-injected analysis should remain valid"); - check(solver.called, "linear solver adapter injection changed"); - check(solver.captured_a.rows() == 2 && solver.captured_a.cols() == 2, "captured reduced stiffness size changed"); - check(solver.captured_b.size() == 2, "captured reduced load size changed"); - - const fesa::DofManager dofs(parsed.domain); - checkNear(injected.state.u_full[static_cast(dofs.fullIndex(2, fesa::Dof::UZ))], 0.25, 1.0e-15, - "node 2 reconstructed UZ changed"); - checkNear(injected.state.u_full[static_cast(dofs.fullIndex(3, fesa::Dof::UZ))], -0.50, 1.0e-15, - "node 3 reconstructed UZ changed"); - for (std::size_t i = 0; i < injected.state.reaction_full.size(); ++i) { - checkNear(injected.state.reaction_full[i], - injected.state.f_internal_full[i] - injected.state.f_external_full[i], - 1.0e-10, - "full-vector reaction formula changed"); - } - - const auto parse_error = fesa::runLinearStaticInputString("*Part, name=P\n", "unsupported.inp"); - check(!parse_error.ok(), "parse errors should still be routed through analysis result"); - check(fesa::containsDiagnostic(parse_error.diagnostics, "FESA-PARSE-UNSUPPORTED-KEYWORD"), - "parse diagnostic routing changed"); - - return 0; -} diff --git a/tests/test_assembly_module_includes.cpp b/tests/test_assembly_module_includes.cpp deleted file mode 100644 index 3bbf93d..0000000 --- a/tests/test_assembly_module_includes.cpp +++ /dev/null @@ -1,83 +0,0 @@ -#include "fesa/Assembly/Assembly.hpp" - -#include -#include -#include - -namespace { - -void check(bool value, const char* message) { - if (!value) { - throw std::runtime_error(message); - } -} - -void checkNear(fesa::Real actual, fesa::Real expected, fesa::Real tolerance, const char* message) { - if (std::fabs(actual - expected) > tolerance) { - throw std::runtime_error(message); - } -} - -fesa::Domain assemblyDomain() { - fesa::Domain domain; - domain.nodes[1] = {1, {0.0, 0.0, 0.0}}; - domain.nodes[2] = {2, {1.0, 0.0, 0.0}}; - domain.nodes[3] = {3, {1.0, 1.0, 0.0}}; - domain.nodes[4] = {4, {0.0, 1.0, 0.0}}; - domain.elements[1] = {1, fesa::ElementType::MITC4, {1, 2, 3, 4}, "EALL"}; - domain.element_sets["eall"] = {"EALL", {1}}; - domain.node_sets["left"] = {"LEFT", {1, 4}}; - domain.node_sets["right"] = {"RIGHT", {2, 3}}; - domain.materials["steel"] = {"STEEL", 1000.0, 0.30}; - domain.shell_sections.push_back({"EALL", "STEEL", 0.1}); - domain.boundary_conditions.push_back({"LEFT", 1, 6, 0.0}); - domain.boundary_conditions.push_back({"RIGHT", 1, 2, 0.0}); - domain.boundary_conditions.push_back({"RIGHT", 4, 6, 0.0}); - domain.loads.push_back({"2", 3, -1.0}); - domain.loads.push_back({"3", 3, -1.0}); - return domain; -} - -} // namespace - -int main() { - const auto domain = assemblyDomain(); - const fesa::DofManager dofs(domain); - - const auto pattern = fesa::buildReducedSparsePattern(domain, dofs); - check(pattern.equation_count == dofs.freeDofCount(), "reduced sparse pattern equation count changed"); - check(pattern.nonzeroCount() > 0, "reduced sparse pattern should remain non-empty"); - - const auto assembly = fesa::assembleSystem(domain, dofs); - check(assembly.ok(), "assembly should remain valid"); - check(assembly.k_full.rows() == dofs.fullDofCount(), "full stiffness row count changed"); - check(assembly.k_full.cols() == dofs.fullDofCount(), "full stiffness column count changed"); - check(static_cast(assembly.f_full.size()) == dofs.fullDofCount(), "full load size changed"); - checkNear(assembly.f_full[static_cast(dofs.fullIndex(2, fesa::Dof::UZ))], -1.0, 1.0e-15, - "node 2 UZ load changed"); - checkNear(assembly.f_full[static_cast(dofs.fullIndex(3, fesa::Dof::UZ))], -1.0, 1.0e-15, - "node 3 UZ load changed"); - - const auto reduced = fesa::projectToReducedSystem(assembly, dofs); - check(reduced.ok(), "reduced system projection should remain valid"); - check(reduced.k.rows() == dofs.freeDofCount(), "reduced stiffness row count changed"); - check(reduced.k.cols() == dofs.freeDofCount(), "reduced stiffness column count changed"); - check(static_cast(reduced.f.size()) == dofs.freeDofCount(), "reduced load size changed"); - check(reduced.free_full_indices == dofs.freeFullIndices(), "free full-index map changed"); - checkNear(reduced.f[0], -1.0, 1.0e-15, "first reduced load changed"); - checkNear(reduced.f[1], -1.0, 1.0e-15, "second reduced load changed"); - - fesa::DenseMatrix k(3, 3); - k(0, 0) = 4.0; - k(1, 1) = 5.0; - k(2, 2) = 6.0; - const std::vector u = {0.5, -0.25, 2.0}; - const std::vector f = {1.0, 2.0, -3.0}; - const auto rf = fesa::recoverFullReaction(k, u, f); - check(rf.size() == 3, "full reaction size changed"); - checkNear(rf[0], 1.0, 1.0e-15, "RF component 0 changed"); - checkNear(rf[1], -3.25, 1.0e-15, "RF component 1 changed"); - checkNear(rf[2], 15.0, 1.0e-15, "RF component 2 changed"); - - return 0; -} diff --git a/tests/test_core_module_includes.cpp b/tests/test_core_module_includes.cpp deleted file mode 100644 index 465b044..0000000 --- a/tests/test_core_module_includes.cpp +++ /dev/null @@ -1,69 +0,0 @@ -#include "fesa/Boundary/Boundary.hpp" -#include "fesa/Core/Core.hpp" -#include "fesa/Load/Load.hpp" -#include "fesa/Property/Property.hpp" -#include "fesa/Util/Util.hpp" - -#include -#include -#include -#include - -namespace { - -void check(bool value, const char* message) { - if (!value) { - throw std::runtime_error(message); - } -} - -} // namespace - -int main() { - static_assert(std::is_same_v, "Real must remain double"); - static_assert(std::is_same_v, "GlobalId must remain int64"); - static_assert(std::is_same_v, "LocalIndex must remain int64"); - static_assert(std::is_same_v, "EquationId must remain int64"); - static_assert(std::is_same_v, "SparseIndex must remain int64"); - - fesa::Domain domain; - domain.nodes[10] = {10, {0.0, 0.0, 0.0}}; - domain.nodes[20] = {20, {1.0, 0.0, 0.0}}; - domain.nodes[30] = {30, {1.0, 1.0, 0.0}}; - domain.nodes[40] = {40, {0.0, 1.0, 0.0}}; - domain.elements[100] = {100, fesa::ElementType::MITC4, {10, 20, 30, 40}, "EALL"}; - domain.element_sets["eall"] = {"EALL", {100}}; - domain.node_sets["fixed"] = {"FIXED", {10, 40}}; - domain.materials["mat"] = {"MAT", 1000.0, 0.3}; - domain.shell_sections.push_back(fesa::ShellSection{"EALL", "MAT", 0.1}); - domain.boundary_conditions.push_back(fesa::BoundaryCondition{"FIXED", 1, 6, 0.0}); - domain.loads.push_back(fesa::NodalLoad{"20", 3, -1.0}); - domain.steps.push_back({"Step-1", "linear_static"}); - - check(fesa::Domain::key(" EALL ") == "eall", "Domain key normalization changed"); - check(fesa::dofFromAbaqus(6).value() == fesa::Dof::RZ, "Abaqus DOF mapping changed"); - check(std::string(fesa::dofLabel(fesa::Dof::UX)) == "UX", "DOF label changed"); - - const auto diagnostics = fesa::validateDomain(domain); - check(!fesa::hasError(diagnostics), "Direct module validation reported an error"); - - const fesa::DofManager dofs(domain); - check(dofs.fullDofCount() == 24, "DofManager full DOF count changed"); - check(dofs.constrainedDofCount() == 12, "DofManager constrained DOF count changed"); - check(dofs.freeDofCount() == 12, "DofManager free DOF count changed"); - check(dofs.equation(20, fesa::Dof::UZ) >= 0, "Free equation numbering changed"); - check(dofs.equation(10, fesa::Dof::UX) == -1, "Constrained equation numbering changed"); - - const auto diagnostic = fesa::makeDiagnostic(fesa::Severity::Warning, "FESA-SMOKE", "module smoke", "core"); - check(diagnostic.source.keyword == "core", "Diagnostic source keyword changed"); - - const auto model = fesa::buildLinearStaticAnalysisModel(domain); - check(model.ok(), "AnalysisModel direct module construction failed"); - check(model.active_element_ids.size() == 1, "AnalysisModel active elements changed"); - - fesa::AnalysisState state; - state.u_full = dofs.reconstructFullVector(std::vector(static_cast(dofs.freeDofCount()), 0.0)); - check(state.u_full.size() == static_cast(dofs.fullDofCount()), "AnalysisState full vector ownership changed"); - - return 0; -} diff --git a/tests/test_element_module_includes.cpp b/tests/test_element_module_includes.cpp deleted file mode 100644 index 92c318d..0000000 --- a/tests/test_element_module_includes.cpp +++ /dev/null @@ -1,173 +0,0 @@ -#include "fesa/Element/Element.hpp" - -#include -#include -#include - -namespace { - -void check(bool value, const char* message) { - if (!value) { - throw std::runtime_error(message); - } -} - -void checkNear(fesa::Real actual, fesa::Real expected, fesa::Real tolerance, const char* message) { - if (std::fabs(actual - expected) > tolerance) { - throw std::runtime_error(message); - } -} - -void checkVecNear(const fesa::Vec3& actual, const fesa::Vec3& expected, fesa::Real tolerance, const char* message) { - checkNear(actual.x, expected.x, tolerance, message); - checkNear(actual.y, expected.y, tolerance, message); - checkNear(actual.z, expected.z, tolerance, message); -} - -void checkRightHandedOrthonormal(const fesa::Vec3& e1, const fesa::Vec3& e2, const fesa::Vec3& e3) { - checkNear(fesa::norm(e1), 1.0, 1.0e-14, "e1 norm changed"); - checkNear(fesa::norm(e2), 1.0, 1.0e-14, "e2 norm changed"); - checkNear(fesa::norm(e3), 1.0, 1.0e-14, "e3 norm changed"); - checkNear(fesa::dot(e1, e2), 0.0, 1.0e-14, "e1/e2 orthogonality changed"); - checkNear(fesa::dot(e1, e3), 0.0, 1.0e-14, "e1/e3 orthogonality changed"); - checkNear(fesa::dot(e2, e3), 0.0, 1.0e-14, "e2/e3 orthogonality changed"); - checkNear(fesa::dot(fesa::cross(e1, e2), e3), 1.0, 1.0e-14, "basis handedness changed"); -} - -fesa::MITC4ElementDofVector zeroElementDofs() { - fesa::MITC4ElementDofVector values{}; - values.fill(0.0); - return values; -} - -} // namespace - -int main() { - const auto center = fesa::shapeFunctions(0.0, 0.0); - checkNear(center.n[0] + center.n[1] + center.n[2] + center.n[3], 1.0, 1.0e-15, "shape sum changed"); - checkNear(center.dr[0], -0.25, 1.0e-15, "center dN1/dxi changed"); - checkNear(center.ds[2], 0.25, 1.0e-15, "center dN3/deta changed"); - - const auto nodes = fesa::mitc4NodeNaturalCoordinates(); - checkNear(nodes[0].xi, -1.0, 1.0e-15, "node 1 xi changed"); - checkNear(nodes[0].eta, -1.0, 1.0e-15, "node 1 eta changed"); - checkNear(nodes[2].xi, 1.0, 1.0e-15, "node 3 xi changed"); - checkNear(nodes[2].eta, 1.0, 1.0e-15, "node 3 eta changed"); - for (std::size_t i = 0; i < 4; ++i) { - const auto corner = fesa::shapeFunctions(nodes[i].xi, nodes[i].eta); - checkNear(corner.n[i], 1.0, 1.0e-15, "corner interpolation changed"); - } - - const auto tying = fesa::mitc4TyingPoints(); - check(tying[0].label == "A" && tying[0].natural.xi == 0.0 && tying[0].natural.eta == -1.0, - "tying point A changed"); - check((tying[0].edge_node_indices == std::array{0, 1}), "tying edge A changed"); - check(tying[1].label == "B" && tying[1].natural.xi == -1.0 && tying[1].natural.eta == 0.0, - "tying point B changed"); - check((tying[1].edge_node_indices == std::array{0, 3}), "tying edge B changed"); - check(tying[2].label == "C" && tying[2].natural.xi == 0.0 && tying[2].natural.eta == 1.0, - "tying point C changed"); - check((tying[2].edge_node_indices == std::array{3, 2}), "tying edge C changed"); - check(tying[3].label == "D" && tying[3].natural.xi == 1.0 && tying[3].natural.eta == 0.0, - "tying point D changed"); - check((tying[3].edge_node_indices == std::array{1, 2}), "tying edge D changed"); - - const std::array coords = {{{0, 0, 0}, {1, 0, 0}, {1, 1, 0}, {0, 1, 0}}}; - const auto geometry = fesa::buildMITC4Geometry(coords, 0.2); - check(geometry.ok(), "flat geometry should remain valid"); - checkVecNear(geometry.center_normal, {0, 0, 1}, 1.0e-14, "center normal changed"); - for (const auto& frame : geometry.nodal_frames) { - checkVecNear(frame.v1, {1, 0, 0}, 1.0e-14, "director v1 changed"); - checkVecNear(frame.v2, {0, 1, 0}, 1.0e-14, "director v2 changed"); - checkVecNear(frame.vn, {0, 0, 1}, 1.0e-14, "director normal changed"); - checkRightHandedOrthonormal(frame.v1, frame.v2, frame.vn); - } - - const auto basis = fesa::computeMITC4IntegrationBasis(geometry, 0.0, 0.0, 0.0); - check(basis.ok(), "flat integration basis should remain valid"); - checkVecNear(basis.g1, {0.5, 0, 0}, 1.0e-14, "g1 changed"); - checkVecNear(basis.g2, {0, 0.5, 0}, 1.0e-14, "g2 changed"); - checkVecNear(basis.g3, {0, 0, 0.1}, 1.0e-14, "g3 changed"); - checkRightHandedOrthonormal(basis.local.e1, basis.local.e2, basis.local.e3); - checkNear(basis.jacobian, 0.025, 1.0e-14, "Jacobian changed"); - - const auto invalid_thickness = fesa::buildMITC4Geometry(coords, 0.0); - check(!invalid_thickness.ok(), "invalid thickness should remain diagnosed"); - check(fesa::containsDiagnostic(invalid_thickness.diagnostics, "FESA-MITC4-THICKNESS"), "thickness diagnostic changed"); - const std::array collinear = {{{0, 0, 0}, {1, 0, 0}, {2, 0, 0}, {3, 0, 0}}}; - const auto singular = fesa::buildMITC4Geometry(collinear, 0.1); - check(!singular.ok(), "singular normal should remain diagnosed"); - check(fesa::containsDiagnostic(singular.diagnostics, "FESA-MITC4-SINGULAR-NORMAL"), "normal diagnostic changed"); - - const auto rotations = fesa::mitc4LocalRotations(geometry.nodal_frames[0], {1.0, 2.0, 3.0}); - checkNear(rotations.alpha, 1.0, 1.0e-14, "alpha rotation changed"); - checkNear(rotations.beta, 2.0, 1.0e-14, "beta rotation changed"); - checkNear(rotations.gamma, 3.0, 1.0e-14, "gamma rotation changed"); - checkVecNear(fesa::mitc4DirectorIncrement(geometry.nodal_frames[0], {0.0, 2.0, 5.0}), {2, 0, 0}, 1.0e-14, - "director increment changed"); - - auto dofs = zeroElementDofs(); - for (std::size_t node = 0; node < 4; ++node) { - dofs[6 * node + 0] = 1.0; - dofs[6 * node + 1] = 0.5; - dofs[6 * node + 4] = 2.0; - dofs[6 * node + 5] = 99.0; - } - const auto mid = fesa::mitc4DisplacementDerivatives(geometry, dofs, 0.0, 0.0, 0.0); - check(mid.ok(), "midsurface displacement interpolation changed"); - checkVecNear(mid.displacement, {1.0, 0.5, 0.0}, 1.0e-14, "midsurface displacement value changed"); - const auto top = fesa::mitc4DisplacementDerivatives(geometry, dofs, 0.0, 0.0, 1.0); - check(top.ok(), "top displacement interpolation changed"); - checkVecNear(top.displacement, {1.2, 0.5, 0.0}, 1.0e-14, "top displacement value changed"); - - constexpr fesa::Real xi = 0.2; - constexpr fesa::Real eta = -0.3; - constexpr fesa::Real zeta = 0.4; - const auto rows = fesa::mitc4DirectCovariantStrainRows(geometry, xi, eta, zeta); - check(rows.ok(), "direct covariant strain rows should remain valid"); - check(fesa::mitc4StrainComponentLabels()[0] == "eps11", "strain label eps11 changed"); - check(fesa::strainComponentIndex(fesa::MITC4StrainComponent::Gamma23) == 3, "gamma23 index changed"); - check(fesa::strainComponentIndex(fesa::MITC4StrainComponent::Gamma13) == 4, "gamma13 index changed"); - - const fesa::Real h = 1.0e-6; - const std::array checked_dofs = {0, 7, 10, 11}; - for (std::size_t dof : checked_dofs) { - auto plus = zeroElementDofs(); - auto minus = zeroElementDofs(); - plus[dof] = h; - minus[dof] = -h; - const auto plus_strain = fesa::mitc4DirectCovariantStrain(geometry, plus, xi, eta, zeta); - const auto minus_strain = fesa::mitc4DirectCovariantStrain(geometry, minus, xi, eta, zeta); - check(plus_strain.ok() && minus_strain.ok(), "direct strain perturbation changed"); - for (std::size_t component = 0; component < 6; ++component) { - const fesa::Real finite_difference = (plus_strain.values[component] - minus_strain.values[component]) / (2.0 * h); - checkNear(rows.rows[component][dof], finite_difference, 1.0e-8, "direct strain row finite difference changed"); - } - } - - const auto gamma23 = fesa::strainComponentIndex(fesa::MITC4StrainComponent::Gamma23); - const auto gamma13 = fesa::strainComponentIndex(fesa::MITC4StrainComponent::Gamma13); - const auto direct_at_point = fesa::mitc4DirectCovariantStrainRows(geometry, 0.5, 0.25, 0.0); - const auto direct_a = fesa::mitc4DirectCovariantStrainRows(geometry, 0.0, -1.0, 0.0); - const auto direct_b = fesa::mitc4DirectCovariantStrainRows(geometry, -1.0, 0.0, 0.0); - const auto direct_c = fesa::mitc4DirectCovariantStrainRows(geometry, 0.0, 1.0, 0.0); - const auto direct_d = fesa::mitc4DirectCovariantStrainRows(geometry, 1.0, 0.0, 0.0); - const auto tied = fesa::mitc4TiedCovariantStrainRows(geometry, 0.5, 0.25, 0.0); - check(direct_at_point.ok() && tied.ok(), "MITC tied rows should remain valid"); - - const std::size_t node2_ry = 6 + 4; - const fesa::Real expected_gamma13 = - 0.5 * (1.0 - 0.25) * direct_a.rows[gamma13][node2_ry] + 0.5 * (1.0 + 0.25) * direct_c.rows[gamma13][node2_ry]; - checkNear(tied.rows[gamma13][node2_ry], expected_gamma13, 1.0e-14, "MITC gamma13 tying interpolation changed"); - check(std::fabs(tied.rows[gamma13][node2_ry] - direct_at_point.rows[gamma13][node2_ry]) > 1.0e-4, - "MITC gamma13 should not be direct Gauss shear"); - - const std::size_t node4_rx = 3 * 6 + 3; - const fesa::Real expected_gamma23 = - 0.5 * (1.0 - 0.5) * direct_b.rows[gamma23][node4_rx] + 0.5 * (1.0 + 0.5) * direct_d.rows[gamma23][node4_rx]; - checkNear(tied.rows[gamma23][node4_rx], expected_gamma23, 1.0e-14, "MITC gamma23 tying interpolation changed"); - check(std::fabs(tied.rows[gamma23][node4_rx] - direct_at_point.rows[gamma23][node4_rx]) > 1.0e-4, - "MITC gamma23 should not be direct Gauss shear"); - - return 0; -} diff --git a/tests/test_io_module_includes.cpp b/tests/test_io_module_includes.cpp deleted file mode 100644 index 1749526..0000000 --- a/tests/test_io_module_includes.cpp +++ /dev/null @@ -1,64 +0,0 @@ -#include "fesa/IO/IO.hpp" - -#include -#include -#include -#include - -namespace { - -void check(bool value, const char* message) { - if (!value) { - throw std::runtime_error(message); - } -} - -std::string sourceRoot() { -#ifdef FESA_SOURCE_DIR - return FESA_SOURCE_DIR; -#else - return "."; -#endif -} - -std::string readTextFile(const std::string& path) { - std::ifstream input(path); - if (!input) { - throw std::runtime_error("failed to open " + path); - } - std::ostringstream buffer; - buffer << input.rdbuf(); - return buffer.str(); -} - -} // namespace - -int main() { - const auto continued = fesa::parseKeywordLine("*Element, type=S4,"); - check(continued.name == "element", "Keyword name normalization changed"); - check(continued.parameters.at("type") == "S4", "Keyword parameter parsing changed"); - - const fesa::AbaqusInputParser parser; - - const auto normalized = parser.parseFile(sourceRoot() + "/references/quad_02_phase1.inp"); - check(normalized.ok(), "quad_02_phase1 normalized input should remain accepted"); - check(normalized.domain.nodes.size() == 121, "quad_02_phase1 node count changed"); - check(normalized.domain.elements.size() == 100, "quad_02_phase1 element count changed"); - check(normalized.domain.node_sets.at("fixed_boundary").node_ids.size() == 40, "quad_02_phase1 fixed set changed"); - check(normalized.domain.node_sets.at("load_node").node_ids.size() == 1, "quad_02_phase1 load set changed"); - check(normalized.domain.element_sets.at("all_elements").element_ids.size() == 100, "quad_02_phase1 element set changed"); - check(normalized.domain.materials.at("material_1").elastic_modulus == 7.0e10, "quad_02_phase1 material changed"); - check(normalized.domain.shell_sections.front().thickness == 1.0, "quad_02_phase1 shell section changed"); - - const auto original = parser.parseFile(sourceRoot() + "/references/quad_02.inp"); - check(!original.ok(), "original quad_02.inp should remain unsupported provenance"); - check(fesa::containsDiagnostic(original.diagnostics, "FESA-PARSE-UNSUPPORTED-KEYWORD"), - "original quad_02.inp unsupported keyword diagnostic changed"); - - const auto nonzero_bc = parser.parseString("*Node\n1, 0, 0, 0\n*Boundary\n1, 1, 1, 0.5\n", "nonzero_bc.inp"); - check(!nonzero_bc.ok(), "nonzero prescribed displacement should remain unsupported"); - check(fesa::containsDiagnostic(nonzero_bc.diagnostics, "FESA-PARSE-BOUNDARY-NONZERO"), - "nonzero prescribed displacement diagnostic changed"); - - return 0; -} diff --git a/tests/test_main.cpp b/tests/test_main.cpp deleted file mode 100644 index 8a2a477..0000000 --- a/tests/test_main.cpp +++ /dev/null @@ -1,2068 +0,0 @@ -#include "fesa/Analysis/Analysis.hpp" -#include "fesa/Assembly/Assembly.hpp" -#include "fesa/Boundary/Boundary.hpp" -#include "fesa/Core/Core.hpp" -#include "fesa/Element/Element.hpp" -#include "fesa/IO/IO.hpp" -#include "fesa/Load/Load.hpp" -#include "fesa/Math/Math.hpp" -#include "fesa/Material/Material.hpp" -#include "fesa/Property/Property.hpp" -#include "fesa/Results/Results.hpp" -#include "fesa/Util/Util.hpp" -#include "fesa/fesa.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static_assert(std::is_same_v, "Real must remain double"); -static_assert(std::is_same_v, "GlobalId must remain int64"); -static_assert(std::is_same_v, "LocalIndex must remain int64"); -static_assert(std::is_same_v, "EquationId must remain int64"); -static_assert(std::is_same_v, "SparseIndex must remain int64"); - -namespace { - -using TestFn = std::function; - -struct TestCase { - std::string name; - TestFn fn; -}; - -std::vector& registry() { - static std::vector tests; - return tests; -} - -struct RegisterTest { - RegisterTest(std::string name, TestFn fn) { - registry().push_back({std::move(name), std::move(fn)}); - } -}; - -#define FESA_TEST(name) \ - void name(); \ - RegisterTest register_##name(#name, name); \ - void name() - -#define FESA_CHECK(expr) \ - do { \ - if (!(expr)) { \ - throw std::runtime_error(std::string("check failed: ") + #expr); \ - } \ - } while (false) - -#define FESA_CHECK_NEAR(actual, expected, tol) \ - do { \ - const auto actual_value = (actual); \ - const auto expected_value = (expected); \ - if (std::fabs(actual_value - expected_value) > (tol)) { \ - throw std::runtime_error(std::string("near check failed: ") + #actual); \ - } \ - } while (false) - -std::string sourceRoot() { -#ifdef FESA_SOURCE_DIR - return FESA_SOURCE_DIR; -#else - return "."; -#endif -} - -std::string phase1Input() { - return R"inp( -*Node -1, 0, 0, 0 -2, 1, 0, 0 -3, 1, 1, 0 -4, 0, 1, 0 -*Element, type=S4, elset=EALL -1, 1, 2, 3, 4 -*Nset, nset=LEFT -1, 4 -*Nset, nset=RIGHT -2, 3 -*Elset, elset=EALL -1 -*Material, name=STEEL -*Elastic -1000.0, 0.3 -*Shell Section, elset=EALL, material=STEEL -0.1 -*Boundary -LEFT, 1, 6, 0 -RIGHT, 1, 2, 0 -RIGHT, 4, 6, 0 -*Cload -2, 3, -1 -3, 3, -1 -*Step, name=Step-1 -*Static -*End Step -)inp"; -} - -fesa::Domain parsedPhase1Domain() { - fesa::AbaqusInputParser parser; - auto parsed = parser.parseString(phase1Input()); - FESA_CHECK(parsed.ok()); - auto diagnostics = fesa::validateDomain(parsed.domain); - FESA_CHECK(!fesa::hasError(diagnostics)); - return parsed.domain; -} - -const fesa::Diagnostic* findDiagnostic(const std::vector& diagnostics, const std::string& code) { - for (const auto& diagnostic : diagnostics) { - if (diagnostic.code == code) { - return &diagnostic; - } - } - return nullptr; -} - -std::size_t diagnosticCount(const std::vector& diagnostics, const std::string& code) { - std::size_t count = 0; - for (const auto& diagnostic : diagnostics) { - if (diagnostic.code == code) { - ++count; - } - } - return count; -} - -void checkVecNear(const fesa::Vec3& actual, const fesa::Vec3& expected, fesa::Real tolerance) { - FESA_CHECK_NEAR(actual.x, expected.x, tolerance); - FESA_CHECK_NEAR(actual.y, expected.y, tolerance); - FESA_CHECK_NEAR(actual.z, expected.z, tolerance); -} - -void checkRightHandedOrthonormal(const fesa::Vec3& e1, const fesa::Vec3& e2, const fesa::Vec3& e3) { - FESA_CHECK_NEAR(fesa::norm(e1), 1.0, 1.0e-14); - FESA_CHECK_NEAR(fesa::norm(e2), 1.0, 1.0e-14); - FESA_CHECK_NEAR(fesa::norm(e3), 1.0, 1.0e-14); - FESA_CHECK_NEAR(fesa::dot(e1, e2), 0.0, 1.0e-14); - FESA_CHECK_NEAR(fesa::dot(e1, e3), 0.0, 1.0e-14); - FESA_CHECK_NEAR(fesa::dot(e2, e3), 0.0, 1.0e-14); - FESA_CHECK_NEAR(fesa::dot(fesa::cross(e1, e2), e3), 1.0, 1.0e-14); -} - -std::array zeroElementDofs() { - std::array values{}; - values.fill(0.0); - return values; -} - -std::vector zeroElementVector() { - return std::vector(24, 0.0); -} - -fesa::Real quadraticEnergy(const fesa::DenseMatrix& stiffness, const std::vector& values) { - const auto internal = stiffness.multiply(values); - fesa::Real energy = 0.0; - for (std::size_t i = 0; i < values.size(); ++i) { - energy += values[i] * internal[i]; - } - return energy; -} - -std::array flatUnitSquareCoordinates() { - return {fesa::Vec3{0.0, 0.0, 0.0}, - fesa::Vec3{1.0, 0.0, 0.0}, - fesa::Vec3{1.0, 1.0, 0.0}, - fesa::Vec3{0.0, 1.0, 0.0}}; -} - -using MITC4DofField = std::function; - -fesa::MITC4ElementDofVector elementDofsFromFields(const std::array& coords, - MITC4DofField translation, - MITC4DofField rotation) { - fesa::MITC4ElementDofVector values{}; - for (std::size_t node = 0; node < coords.size(); ++node) { - const auto displacement = translation(coords[node]); - const auto rotational_dof = rotation(coords[node]); - values[node * 6 + 0] = displacement.x; - values[node * 6 + 1] = displacement.y; - values[node * 6 + 2] = displacement.z; - values[node * 6 + 3] = rotational_dof.x; - values[node * 6 + 4] = rotational_dof.y; - values[node * 6 + 5] = rotational_dof.z; - } - return values; -} - -std::vector toVector(const fesa::MITC4ElementDofVector& values) { - return {values.begin(), values.end()}; -} - -std::string readTextFile(const std::string& path) { - std::ifstream input(path); - std::ostringstream buffer; - buffer << input.rdbuf(); - FESA_CHECK(input.good() || !buffer.str().empty()); - return buffer.str(); -} - -void checkComparisonPass(const fesa::ComparisonResult& comparison) { - if (!comparison.pass) { - std::string message = "reference comparison failed: max_abs_error=" + - std::to_string(comparison.max_abs_error) + - ", max_rel_error=" + std::to_string(comparison.max_rel_error) + - ", diagnostics=" + std::to_string(comparison.diagnostics.size()); - if (!comparison.diagnostics.empty()) { - message += ", first_diagnostic=" + comparison.diagnostics.front().message; - } - throw std::runtime_error(message); - } -} - -fesa::MITC4StrainVector localStrainAt(const fesa::MITC4Geometry& geometry, - const fesa::MITC4ElementDofVector& values, - fesa::Real xi, - fesa::Real eta, - fesa::Real zeta) { - const auto rows = fesa::mitc4TiedCovariantStrainRows(geometry, xi, eta, zeta); - FESA_CHECK(rows.ok()); - const auto convected_strain = fesa::evaluateMITC4StrainRows(rows, values); - FESA_CHECK(convected_strain.ok()); - const auto basis = fesa::computeMITC4IntegrationBasis(geometry, xi, eta, zeta); - FESA_CHECK(basis.ok()); - const auto transform = fesa::mitc4CovariantToLocalStrainTransform(basis); - FESA_CHECK(transform.ok()); - return fesa::multiplyMITC4MaterialMatrix(transform.matrix, convected_strain.values); -} - -fesa::Real singleElementCantileverTipUz(fesa::Real thickness) { - fesa::Domain domain; - domain.nodes[1] = {1, {0.0, 0.0, 0.0}}; - domain.nodes[2] = {2, {4.0, 0.0, 0.0}}; - domain.nodes[3] = {3, {4.0, 1.0, 0.0}}; - domain.nodes[4] = {4, {0.0, 1.0, 0.0}}; - domain.elements[1] = {1, fesa::ElementType::MITC4, {1, 2, 3, 4}, "SHELLS"}; - domain.node_sets["clamped"] = {"CLAMPED", {1, 4}}; - domain.element_sets["shells"] = {"SHELLS", {1}}; - domain.materials["shell_steel"] = {"SHELL_STEEL", 210000.0, 0.30}; - domain.shell_sections.push_back({"SHELLS", "SHELL_STEEL", thickness}); - domain.boundary_conditions.push_back({"CLAMPED", 1, 6, 0.0}); - domain.loads.push_back({"2", 3, -0.5}); - domain.loads.push_back({"3", 3, -0.5}); - - fesa::LinearStaticAnalysis analysis; - const auto result = analysis.run(domain); - FESA_CHECK(result.ok()); - fesa::DofManager dofs(domain); - const auto node2_uz = result.state.u_full[static_cast(dofs.fullIndex(2, fesa::Dof::UZ))]; - const auto node3_uz = result.state.u_full[static_cast(dofs.fullIndex(3, fesa::Dof::UZ))]; - return 0.5 * (node2_uz + node3_uz); -} - -class RecordingSolver final : public fesa::LinearSolver { - public: - explicit RecordingSolver(std::vector solution) : solution_(std::move(solution)) {} - - fesa::SolveResult solve(fesa::DenseMatrix a, std::vector b) const override { - called = true; - captured_a = std::move(a); - captured_b = std::move(b); - return {solution_, {}}; - } - - mutable bool called = false; - mutable fesa::DenseMatrix captured_a; - mutable std::vector captured_b; - - private: - std::vector solution_; -}; - -class FailingSolver final : public fesa::LinearSolver { - public: - fesa::SolveResult solve(fesa::DenseMatrix, std::vector) const override { - return {{}, {fesa::makeDiagnostic(fesa::Severity::Error, "FESA-SINGULAR-SOLVER", - "Injected reduced system singularity", "solver")}}; - } -}; - -fesa::Domain singleElementValidationDomain() { - fesa::Domain domain; - domain.nodes[1] = {1, {0, 0, 0}}; - domain.nodes[2] = {2, {1, 0, 0}}; - domain.nodes[3] = {3, {1, 1, 0}}; - domain.nodes[4] = {4, {0, 1, 0}}; - domain.elements[1] = {1, fesa::ElementType::MITC4, {1, 2, 3, 4}, "EALL"}; - domain.element_sets["eall"] = {"EALL", {1}}; - domain.node_sets["all_nodes"] = {"ALL_NODES", {1, 2, 3, 4}}; - domain.materials["mat"] = {"MAT", 1000.0, 0.3}; - domain.shell_sections.push_back({"EALL", "MAT", 0.1}); - domain.loads.push_back({"2", 3, -1.0}); - return domain; -} - -fesa::Domain noncontiguousDofDomain() { - fesa::Domain domain; - domain.nodes[30] = {30, {1, 1, 0}}; - domain.nodes[10] = {10, {0, 0, 0}}; - domain.nodes[40] = {40, {0, 1, 0}}; - domain.nodes[20] = {20, {1, 0, 0}}; - domain.elements[5] = {5, fesa::ElementType::MITC4, {10, 20, 30, 40}, "EALL"}; - domain.element_sets["eall"] = {"EALL", {5}}; - domain.node_sets["clamped"] = {"CLAMPED", {20}}; - domain.materials["mat"] = {"MAT", 1000.0, 0.3}; - domain.shell_sections.push_back({"EALL", "MAT", 0.1}); - domain.boundary_conditions.push_back({"CLAMPED", 1, 6, 0.0}); - domain.boundary_conditions.push_back({"30", 1, 2, 0.0}); - return domain; -} - -} // namespace - -FESA_TEST(core_types_and_dof_mapping_are_stable) { - FESA_CHECK(sizeof(fesa::Real) == 8); - FESA_CHECK(sizeof(fesa::GlobalId) == 8); - FESA_CHECK(sizeof(fesa::LocalIndex) == 8); - FESA_CHECK(sizeof(fesa::EquationId) == 8); - FESA_CHECK(sizeof(fesa::SparseIndex) == 8); - FESA_CHECK(std::numeric_limits::is_signed); - FESA_CHECK(std::numeric_limits::is_signed); - FESA_CHECK(std::numeric_limits::is_signed); - FESA_CHECK(std::numeric_limits::is_signed); - - const auto dofs = fesa::allDofs(); - FESA_CHECK(dofs.size() == 6); - for (std::size_t i = 0; i < dofs.size(); ++i) { - const int abaqus_number = static_cast(i + 1); - FESA_CHECK(fesa::abaqusDofNumber(dofs[i]) == abaqus_number); - FESA_CHECK(fesa::dofFromAbaqus(abaqus_number).value() == dofs[i]); - FESA_CHECK(std::string(fesa::dofLabel(dofs[i])) == fesa::displacementComponentLabels()[i]); - } - FESA_CHECK(!fesa::dofFromAbaqus(0).has_value()); - FESA_CHECK(!fesa::dofFromAbaqus(7).has_value()); -} - -FESA_TEST(module_scaffold_headers_are_include_compatible_with_umbrella) { - const auto modules = fesa::architectureModules(); - FESA_CHECK(modules.size() == 12); - FESA_CHECK(modules[0] == std::string_view("Analysis")); - FESA_CHECK(modules[1] == std::string_view("Assembly")); - FESA_CHECK(modules[2] == std::string_view("Boundary")); - FESA_CHECK(modules[3] == std::string_view("Core")); - FESA_CHECK(modules[4] == std::string_view("Element")); - FESA_CHECK(modules[5] == std::string_view("IO")); - FESA_CHECK(modules[6] == std::string_view("Load")); - FESA_CHECK(modules[7] == std::string_view("Math")); - FESA_CHECK(modules[8] == std::string_view("Material")); - FESA_CHECK(modules[9] == std::string_view("Property")); - FESA_CHECK(modules[10] == std::string_view("Results")); - FESA_CHECK(modules[11] == std::string_view("Util")); - FESA_CHECK(fesa::umbrellaFacadeHeader() == std::string_view("fesa/fesa.hpp")); - FESA_CHECK(fesa::dofFromAbaqus(1).value() == fesa::Dof::UX); -} - -FESA_TEST(parser_accepts_phase1_subset) { - fesa::AbaqusInputParser parser; - auto parsed = parser.parseString(phase1Input()); - FESA_CHECK(parsed.ok()); - FESA_CHECK(parsed.domain.nodes.size() == 4); - FESA_CHECK(parsed.domain.elements.size() == 1); - FESA_CHECK(parsed.domain.node_sets.at("left").node_ids.size() == 2); - FESA_CHECK(parsed.domain.element_sets.at("eall").element_ids.size() == 1); - FESA_CHECK(parsed.domain.materials.at("steel").elastic_modulus == 1000.0); - FESA_CHECK(parsed.domain.shell_sections.front().thickness == 0.1); - FESA_CHECK(parsed.domain.boundary_conditions.size() == 3); - FESA_CHECK(parsed.domain.loads.size() == 2); -} - -FESA_TEST(analysis_model_activates_single_linear_static_step) { - auto domain = parsedPhase1Domain(); - const auto node_count = domain.nodes.size(); - const auto element_count = domain.elements.size(); - const auto model = fesa::buildLinearStaticAnalysisModel(domain); - - FESA_CHECK(model.ok()); - FESA_CHECK(model.step.name == "Step-1"); - FESA_CHECK(model.step.analysis_type == "linear_static"); - FESA_CHECK(model.active_element_ids == std::vector({1})); - FESA_CHECK(model.active_boundary_condition_indices == std::vector({0, 1, 2})); - FESA_CHECK(model.active_load_indices == std::vector({0, 1})); - FESA_CHECK(model.active_shell_section_indices == std::vector({0})); - FESA_CHECK(model.active_material_keys == std::vector({"steel"})); - FESA_CHECK(domain.nodes.size() == node_count); - FESA_CHECK(domain.elements.size() == element_count); -} - -FESA_TEST(parser_accepts_repeated_and_generated_sets) { - const std::string text = R"inp( -*Node -1, 0, 0, 0 -2, 1, 0, 0 -3, 1, 1, 0 -4, 0, 1, 0 -*Element, type=S4, elset=EALL -1, 1, 2, 3, 4 -*Nset, nset=FIXED -1, 2, 2 -3 -*Nset, nset=FIXED, generate -3, 4, 1 -*Nset, nset=LOADS, generate -2, 4, 2 -*Elset, elset=EALL -1, 1 -*Elset, elset=CHECK, generate -1, 5, 2 -*Material, name=MAT -*Elastic -2.0D5, 0.25 -*Shell Section, elset=EALL, material=MAT -0.2 -*Boundary -FIXED, 1, 6 -*Cload -LOADS, 3, -2.5 -*Step, name=Step-A, nlgeom=NO -*Static -*End Step -)inp"; - fesa::AbaqusInputParser parser; - auto parsed = parser.parseString(text); - FESA_CHECK(parsed.ok()); - FESA_CHECK(parsed.domain.node_sets.at("fixed").node_ids == std::vector({1, 2, 3, 4})); - FESA_CHECK(parsed.domain.node_sets.at("loads").node_ids == std::vector({2, 4})); - FESA_CHECK(parsed.domain.element_sets.at("eall").element_ids == std::vector({1})); - FESA_CHECK(parsed.domain.element_sets.at("check").element_ids == std::vector({1, 3, 5})); - FESA_CHECK(parsed.domain.materials.at("mat").elastic_modulus == 2.0e5); - FESA_CHECK(parsed.domain.steps.front().name == "Step-A"); -} - -FESA_TEST(parser_accepts_keyword_line_continuation) { - const std::string text = R"inp( -*Node -1, 0, 0, 0 -2, 1, 0, 0 -3, 1, 1, 0 -4, 0, 1, 0 -*Element, - type=S4, elset=EALL -1, 1, 2, 3, 4 -*Nset, - nset=FIXED, generate -1, 4, 3 -*Elset, - elset=EALL -1 -*Material, - name=MAT -*Elastic -2.0e5, 0.25 -*Shell Section, - elset=EALL, material=MAT -0.2 -*Boundary -FIXED, 1, 6 -*Cload -2, 3, -1.0 -*Step, - name=Step-1 -*Static -*End Step -)inp"; - fesa::AbaqusInputParser parser; - auto parsed = parser.parseString(text); - FESA_CHECK(parsed.ok()); - FESA_CHECK(parsed.domain.elements.at(1).source_elset == "EALL"); - FESA_CHECK(parsed.domain.node_sets.at("fixed").node_ids == std::vector({1, 4})); - FESA_CHECK(parsed.domain.materials.count("mat") == 1); - FESA_CHECK(parsed.domain.shell_sections.front().material == "MAT"); -} - -FESA_TEST(parser_rejects_unsupported_features) { - const std::string text = R"inp( -*Part, name=P1 -*Assembly, name=A1 -*Instance, name=I1, part=P1 -*Include, input=other.inp -*Node -1, 0, 0, 0 -*Element, type=S4R -1, 1, 2, 3, 4 -*Density -7850 -*Step, nlgeom=YES -*End Step -)inp"; - fesa::AbaqusInputParser parser; - auto parsed = parser.parseString(text); - FESA_CHECK(!parsed.ok()); - FESA_CHECK(diagnosticCount(parsed.diagnostics, "FESA-PARSE-UNSUPPORTED-KEYWORD") >= 4); - FESA_CHECK(fesa::containsDiagnostic(parsed.diagnostics, "FESA-PARSE-UNSUPPORTED-KEYWORD")); - FESA_CHECK(fesa::containsDiagnostic(parsed.diagnostics, "FESA-PARSE-UNSUPPORTED-ELEMENT")); - FESA_CHECK(fesa::containsDiagnostic(parsed.diagnostics, "FESA-PARSE-UNSUPPORTED-NLGEOM")); -} - -FESA_TEST(parser_rejects_unsupported_keyword_parameters_and_modes) { - const std::string text = R"inp( -*Node, input=nodes.csv -1, 0, 0, 0 -2, 1, 0, 0 -3, 1, 1, 0 -4, 0, 1, 0 -*Element, type=S4, elset=EALL, orientation=OR1 -1, 1, 2, 3, 4 -*Nset, nset=FIXED, unsorted -1, 4 -*Material, name=MAT, description=bad -*Elastic, type=ENGINEERING CONSTANTS -2.0e5, 0.25 -*Shell Section, elset=EALL, material=MAT, offset=SPOS -0.2, 5 -*Boundary, op=NEW -FIXED, 1, 6 -*Cload, amplitude=A1 -2, 3, -1.0 -*Step, name=Step-1, inc=100 -*Static, stabilize -*End Step -)inp"; - fesa::AbaqusInputParser parser; - auto parsed = parser.parseString(text, "unsupported_modes.inp"); - FESA_CHECK(!parsed.ok()); - FESA_CHECK(diagnosticCount(parsed.diagnostics, "FESA-PARSE-UNSUPPORTED-PARAMETER") >= 8); - FESA_CHECK(fesa::containsDiagnostic(parsed.diagnostics, "FESA-PARSE-SHELL-SECTION-UNSUPPORTED")); -} - -FESA_TEST(parser_diagnostics_include_file_line_and_keyword) { - const std::string text = R"inp( -*Node -1, bad, 0, 0 -*Boundary -FIXED, 7, 7 -)inp"; - fesa::AbaqusInputParser parser; - auto parsed = parser.parseString(text, "malformed.inp"); - FESA_CHECK(!parsed.ok()); - - const fesa::Diagnostic* node = findDiagnostic(parsed.diagnostics, "FESA-PARSE-NODE-NUMERIC"); - FESA_CHECK(node != nullptr); - FESA_CHECK(node->source.file == "malformed.inp"); - FESA_CHECK(node->source.line == 3); - FESA_CHECK(node->source.keyword == "node"); - - const fesa::Diagnostic* boundary = findDiagnostic(parsed.diagnostics, "FESA-PARSE-BOUNDARY-DOF"); - FESA_CHECK(boundary != nullptr); - FESA_CHECK(boundary->source.file == "malformed.inp"); - FESA_CHECK(boundary->source.line == 5); - FESA_CHECK(boundary->source.keyword == "boundary"); -} - -FESA_TEST(quad01_reference_input_remains_unsupported) { - fesa::AbaqusInputParser parser; - auto parsed = parser.parseFile(sourceRoot() + "/references/quad_01.inp"); - FESA_CHECK(!parsed.ok()); - FESA_CHECK(fesa::containsDiagnostic(parsed.diagnostics, "FESA-PARSE-UNSUPPORTED-KEYWORD") || - fesa::containsDiagnostic(parsed.diagnostics, "FESA-PARSE-UNSUPPORTED-ELEMENT")); -} - -FESA_TEST(quad02_original_reference_input_remains_unsupported) { - fesa::AbaqusInputParser parser; - auto parsed = parser.parseFile(sourceRoot() + "/references/quad_02.inp"); - FESA_CHECK(!parsed.ok()); - FESA_CHECK(fesa::containsDiagnostic(parsed.diagnostics, "FESA-PARSE-UNSUPPORTED-KEYWORD")); -} - -FESA_TEST(quad02_phase1_normalized_input_uses_supported_subset) { - fesa::AbaqusInputParser parser; - auto parsed = parser.parseFile(sourceRoot() + "/references/quad_02_phase1.inp"); - FESA_CHECK(parsed.ok()); - FESA_CHECK(parsed.domain.nodes.size() == 121); - FESA_CHECK(parsed.domain.elements.size() == 100); - FESA_CHECK(parsed.domain.node_sets.at("fixed_boundary").node_ids.size() == 40); - FESA_CHECK(parsed.domain.node_sets.at("load_node").node_ids.size() == 1); - FESA_CHECK(parsed.domain.element_sets.at("all_elements").element_ids.size() == 100); - FESA_CHECK(parsed.domain.materials.at("material_1").elastic_modulus == 7.0e10); - FESA_CHECK(parsed.domain.shell_sections.front().thickness == 1.0); -} - -FESA_TEST(analysis_model_rejects_multiple_steps_for_phase1_execution) { - const std::string text = phase1Input() + R"inp( -*Step, name=Step-2 -*Static -*End Step -)inp"; - fesa::AbaqusInputParser parser; - auto parsed = parser.parseString(text); - FESA_CHECK(parsed.ok()); - FESA_CHECK(parsed.domain.steps.size() == 2); - const auto model = fesa::buildLinearStaticAnalysisModel(parsed.domain); - FESA_CHECK(!model.ok()); - FESA_CHECK(fesa::containsDiagnostic(model.diagnostics, "FESA-ANALYSIS-MULTIPLE-STEPS")); -} - -FESA_TEST(domain_validation_reports_missing_property_and_targets) { - fesa::Domain domain; - domain.nodes[1] = {1, {0, 0, 0}}; - domain.nodes[2] = {2, {1, 0, 0}}; - domain.nodes[3] = {3, {1, 1, 0}}; - domain.nodes[4] = {4, {0, 1, 0}}; - domain.elements[1] = {1, fesa::ElementType::MITC4, {1, 2, 3, 99}, ""}; - domain.shell_sections.push_back({"MISSING_ELSET", "MISSING_MAT", 0.1}); - domain.loads.push_back({"MISSING", 3, 1.0}); - auto diagnostics = fesa::validateDomain(domain); - FESA_CHECK(fesa::containsDiagnostic(diagnostics, "FESA-VALIDATION-ELEMENT-MISSING-NODE")); - FESA_CHECK(fesa::containsDiagnostic(diagnostics, "FESA-VALIDATION-MISSING-PROPERTY")); - FESA_CHECK(fesa::containsDiagnostic(diagnostics, "FESA-VALIDATION-MISSING-ELSET")); - FESA_CHECK(fesa::containsDiagnostic(diagnostics, "FESA-VALIDATION-MISSING-MATERIAL")); - FESA_CHECK(fesa::containsDiagnostic(diagnostics, "FESA-VALIDATION-MISSING-NSET")); - for (const auto& diagnostic : diagnostics) { - FESA_CHECK(!diagnostic.code.empty()); - FESA_CHECK(!diagnostic.message.empty()); - FESA_CHECK(!diagnostic.source.keyword.empty()); - } -} - -FESA_TEST(domain_validation_reports_missing_sets_and_set_members) { - auto domain = singleElementValidationDomain(); - domain.shell_sections.clear(); - domain.shell_sections.push_back({"MISSING_ELSET", "MISSING_MAT", 0.1}); - domain.node_sets["bad_nodes"] = {"BAD_NODES", {1, 99}}; - domain.element_sets["bad_elements"] = {"BAD_ELEMENTS", {1, 77}}; - domain.boundary_conditions.push_back({"MISSING_BOUNDARY_SET", 1, 6, 0.0}); - domain.boundary_conditions.push_back({"BAD_NODES", 1, 1, 0.0}); - domain.loads.push_back({"MISSING_LOAD_SET", 3, 1.0}); - domain.loads.push_back({"BAD_NODES", 3, 1.0}); - - auto diagnostics = fesa::validateDomain(domain); - FESA_CHECK(fesa::containsDiagnostic(diagnostics, "FESA-VALIDATION-MISSING-ELSET")); - FESA_CHECK(fesa::containsDiagnostic(diagnostics, "FESA-VALIDATION-MISSING-MATERIAL")); - FESA_CHECK(fesa::containsDiagnostic(diagnostics, "FESA-VALIDATION-MISSING-NSET")); - FESA_CHECK(fesa::containsDiagnostic(diagnostics, "FESA-VALIDATION-NSET-MISSING-NODE")); - FESA_CHECK(fesa::containsDiagnostic(diagnostics, "FESA-VALIDATION-ELSET-MISSING-ELEMENT")); - - const auto* missing_set = findDiagnostic(diagnostics, "FESA-VALIDATION-MISSING-NSET"); - FESA_CHECK(missing_set != nullptr); - FESA_CHECK(missing_set->message.find("MISSING_BOUNDARY_SET") != std::string::npos || - missing_set->message.find("MISSING_LOAD_SET") != std::string::npos); - - const auto* missing_node = findDiagnostic(diagnostics, "FESA-VALIDATION-NSET-MISSING-NODE"); - FESA_CHECK(missing_node != nullptr); - FESA_CHECK(missing_node->message.find("BAD_NODES") != std::string::npos); - FESA_CHECK(missing_node->message.find("99") != std::string::npos); -} - -FESA_TEST(domain_validation_reports_nonpositive_thickness_and_invalid_dofs) { - auto domain = singleElementValidationDomain(); - domain.shell_sections.front().thickness = 0.0; - domain.boundary_conditions.push_back({"ALL_NODES", 0, 1, 0.0}); - domain.loads.push_back({"2", 7, 1.0}); - - auto diagnostics = fesa::validateDomain(domain); - FESA_CHECK(fesa::containsDiagnostic(diagnostics, "FESA-VALIDATION-NONPOSITIVE-THICKNESS")); - FESA_CHECK(fesa::containsDiagnostic(diagnostics, "FESA-VALIDATION-BOUNDARY-DOF")); - FESA_CHECK(fesa::containsDiagnostic(diagnostics, "FESA-VALIDATION-CLOAD-DOF")); - - const auto* thickness = findDiagnostic(diagnostics, "FESA-VALIDATION-NONPOSITIVE-THICKNESS"); - FESA_CHECK(thickness != nullptr); - FESA_CHECK(thickness->message.find("EALL") != std::string::npos); - - const auto* load_dof = findDiagnostic(diagnostics, "FESA-VALIDATION-CLOAD-DOF"); - FESA_CHECK(load_dof != nullptr); - FESA_CHECK(load_dof->message.find("DOF 7") != std::string::npos); -} - -FESA_TEST(domain_validation_reports_no_active_elements_and_missing_load) { - fesa::Domain domain; - domain.nodes[1] = {1, {0, 0, 0}}; - auto diagnostics = fesa::validateDomain(domain); - FESA_CHECK(fesa::containsDiagnostic(diagnostics, "FESA-SINGULAR-NO-ACTIVE-ELEMENTS")); - FESA_CHECK(fesa::containsDiagnostic(diagnostics, "FESA-SINGULAR-NO-NONZERO-LOAD")); - for (const auto& diagnostic : diagnostics) { - FESA_CHECK(diagnostic.code.find("MESH") == std::string::npos); - } -} - -FESA_TEST(domain_validation_reports_no_free_dofs) { - auto domain = singleElementValidationDomain(); - domain.boundary_conditions.push_back({"ALL_NODES", 1, 6, 0.0}); - - auto diagnostics = fesa::validateDomain(domain); - const auto* no_free = findDiagnostic(diagnostics, "FESA-SINGULAR-NO-FREE-DOFS"); - FESA_CHECK(no_free != nullptr); - FESA_CHECK(no_free->source.keyword == "dof"); - FESA_CHECK(no_free->message.find("No free DOFs") != std::string::npos); -} - -FESA_TEST(domain_validation_reports_free_untouched_dofs_for_isolated_nodes) { - auto domain = singleElementValidationDomain(); - domain.nodes[99] = {99, {10, 0, 0}}; - domain.boundary_conditions.push_back({"ALL_NODES", 1, 6, 0.0}); - - auto diagnostics = fesa::validateDomain(domain); - const auto* untouched = findDiagnostic(diagnostics, "FESA-SINGULAR-DOF-UNTOUCHED"); - FESA_CHECK(untouched != nullptr); - FESA_CHECK(untouched->source.keyword == "dof"); - FESA_CHECK(untouched->message.find("Node 99") != std::string::npos); - FESA_CHECK(untouched->message.find("UX") != std::string::npos); -} - -FESA_TEST(domain_validation_reports_weak_drilling_dof_smoke) { - auto domain = singleElementValidationDomain(); - domain.boundary_conditions.push_back({"ALL_NODES", 1, 5, 0.0}); - - auto diagnostics = fesa::validateDomain(domain); - const auto* weak_drilling = findDiagnostic(diagnostics, "FESA-SINGULAR-WEAK-DRILLING-DOF"); - FESA_CHECK(weak_drilling != nullptr); - FESA_CHECK(weak_drilling->severity == fesa::Severity::Warning); - FESA_CHECK(weak_drilling->source.keyword == "dof"); - FESA_CHECK(weak_drilling->message.find("RZ") != std::string::npos); -} - -FESA_TEST(dof_manager_owns_equation_numbering_and_reconstruction) { - auto domain = parsedPhase1Domain(); - fesa::DofManager dofs(domain); - FESA_CHECK(dofs.fullDofCount() == 24); - FESA_CHECK(dofs.freeDofCount() == 2); - FESA_CHECK(dofs.isConstrained(1, fesa::Dof::UX)); - FESA_CHECK(dofs.equation(2, fesa::Dof::UZ) == 0); - FESA_CHECK(dofs.equation(3, fesa::Dof::UZ) == 1); - auto full = dofs.reconstructFullVector({-0.1, -0.2}); - FESA_CHECK_NEAR(full[static_cast(dofs.fullIndex(2, fesa::Dof::UZ))], -0.1, 1.0e-15); - FESA_CHECK_NEAR(full[static_cast(dofs.fullIndex(1, fesa::Dof::UX))], 0.0, 1.0e-15); -} - -FESA_TEST(dof_manager_preserves_global_order_for_noncontiguous_nodes) { - auto domain = noncontiguousDofDomain(); - fesa::DofManager dofs(domain); - - FESA_CHECK(dofs.nodeIds() == std::vector({10, 20, 30, 40})); - FESA_CHECK(dofs.fullDofCount() == 24); - for (std::size_t node_offset = 0; node_offset < dofs.nodeIds().size(); ++node_offset) { - const fesa::GlobalId node_id = dofs.nodeIds()[node_offset]; - for (fesa::Dof dof : fesa::allDofs()) { - const fesa::LocalIndex expected = static_cast(6 * node_offset + fesa::dofIndex(dof)); - FESA_CHECK(dofs.fullIndex(node_id, dof) == expected); - const auto address = dofs.fullDof(expected); - FESA_CHECK(address.node_id == node_id); - FESA_CHECK(address.dof == dof); - } - } -} - -FESA_TEST(dof_manager_partitions_constraints_and_equations_deterministically) { - auto domain = noncontiguousDofDomain(); - fesa::DofManager dofs(domain); - - FESA_CHECK(dofs.constrainedDofCount() == 8); - FESA_CHECK(dofs.freeDofCount() == 16); - FESA_CHECK(dofs.constrainedFullIndices() == std::vector({6, 7, 8, 9, 10, 11, 12, 13})); - FESA_CHECK(dofs.freeFullIndices() == - std::vector({0, 1, 2, 3, 4, 5, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23})); - FESA_CHECK(dofs.equation(10, fesa::Dof::UX) == 0); - FESA_CHECK(dofs.equation(20, fesa::Dof::RZ) == -1); - FESA_CHECK(dofs.equation(30, fesa::Dof::UX) == -1); - FESA_CHECK(dofs.equation(30, fesa::Dof::UZ) == 6); - FESA_CHECK(dofs.equation(40, fesa::Dof::RZ) == 15); -} - -FESA_TEST(dof_manager_reduces_and_reconstructs_full_vectors) { - auto domain = noncontiguousDofDomain(); - fesa::DofManager dofs(domain); - - std::vector full(static_cast(dofs.fullDofCount()), 0.0); - for (std::size_t i = 0; i < full.size(); ++i) { - full[i] = static_cast(100 + i); - } - - auto reduced = dofs.reduceFullVector(full); - FESA_CHECK(reduced.size() == static_cast(dofs.freeDofCount())); - FESA_CHECK_NEAR(reduced[0], full[0], 1.0e-15); - FESA_CHECK_NEAR(reduced[6], full[14], 1.0e-15); - FESA_CHECK_NEAR(reduced.back(), full[23], 1.0e-15); - - auto reconstructed = dofs.reconstructFullVector(reduced); - for (fesa::LocalIndex full_index = 0; full_index < dofs.fullDofCount(); ++full_index) { - const auto address = dofs.fullDof(full_index); - if (dofs.isConstrained(address.node_id, address.dof)) { - FESA_CHECK_NEAR(reconstructed[static_cast(full_index)], 0.0, 1.0e-15); - } else { - FESA_CHECK_NEAR(reconstructed[static_cast(full_index)], full[static_cast(full_index)], 1.0e-15); - } - } -} - -FESA_TEST(dof_manager_provides_element_sparse_connectivity_inputs) { - auto domain = parsedPhase1Domain(); - fesa::DofManager dofs(domain); - const auto& element = domain.elements.at(1); - - auto full_indices = dofs.elementFullDofIndices(element); - auto equation_ids = dofs.elementEquationIds(element); - - for (fesa::LocalIndex i = 0; i < 24; ++i) { - FESA_CHECK(full_indices[static_cast(i)] == i); - } - for (fesa::LocalIndex i = 0; i < 6; ++i) { - FESA_CHECK(equation_ids[static_cast(i)] == -1); - FESA_CHECK(equation_ids[static_cast(18 + i)] == -1); - } - FESA_CHECK(equation_ids[static_cast(6 + fesa::dofIndex(fesa::Dof::UZ))] == 0); - FESA_CHECK(equation_ids[static_cast(12 + fesa::dofIndex(fesa::Dof::UZ))] == 1); - FESA_CHECK(equation_ids[static_cast(6 + fesa::dofIndex(fesa::Dof::UX))] == -1); -} - -FESA_TEST(full_vector_reaction_recovery_uses_full_system_quantities) { - auto domain = noncontiguousDofDomain(); - fesa::DofManager dofs(domain); - fesa::DenseMatrix k_full(dofs.fullDofCount(), dofs.fullDofCount()); - std::vector u_full(static_cast(dofs.fullDofCount()), 0.0); - std::vector f_full(static_cast(dofs.fullDofCount()), 0.0); - - const fesa::LocalIndex support_ux = dofs.fullIndex(20, fesa::Dof::UX); - const fesa::LocalIndex free_uz = dofs.fullIndex(30, fesa::Dof::UZ); - k_full(support_ux, support_ux) = 10.0; - k_full(support_ux, free_uz) = 2.0; - k_full(free_uz, support_ux) = 2.0; - k_full(free_uz, free_uz) = 5.0; - u_full[static_cast(free_uz)] = 3.0; - f_full[static_cast(support_ux)] = 1.0; - f_full[static_cast(free_uz)] = 7.0; - - auto reaction = fesa::recoverFullReaction(k_full, u_full, f_full); - FESA_CHECK_NEAR(reaction[static_cast(support_ux)], 5.0, 1.0e-15); - FESA_CHECK_NEAR(reaction[static_cast(free_uz)], 8.0, 1.0e-15); -} - -FESA_TEST(reduced_sparse_pattern_is_deterministic_for_phase1_connectivity) { - auto domain = parsedPhase1Domain(); - fesa::DofManager dofs(domain); - const auto pattern = fesa::buildReducedSparsePattern(domain, dofs); - - FESA_CHECK(pattern.equation_count == dofs.freeDofCount()); - FESA_CHECK(pattern.nonzeroCount() == 4); - FESA_CHECK(pattern.contains(0, 0)); - FESA_CHECK(pattern.contains(0, 1)); - FESA_CHECK(pattern.contains(1, 0)); - FESA_CHECK(pattern.contains(1, 1)); - FESA_CHECK(pattern.entries[0].row == 0); - FESA_CHECK(pattern.entries[0].col == 0); - FESA_CHECK(pattern.entries[1].row == 0); - FESA_CHECK(pattern.entries[1].col == 1); - FESA_CHECK(pattern.entries[2].row == 1); - FESA_CHECK(pattern.entries[2].col == 0); - FESA_CHECK(pattern.entries[3].row == 1); - FESA_CHECK(pattern.entries[3].col == 1); -} - -FESA_TEST(assembly_projection_uses_dof_manager_free_indices) { - auto domain = parsedPhase1Domain(); - fesa::DofManager dofs(domain); - fesa::AssemblyResult assembly; - assembly.k_full = fesa::DenseMatrix(dofs.fullDofCount(), dofs.fullDofCount()); - assembly.f_full = std::vector(static_cast(dofs.fullDofCount()), 0.0); - assembly.reduced_pattern = fesa::buildReducedSparsePattern(domain, dofs); - - const auto node2_uz = dofs.fullIndex(2, fesa::Dof::UZ); - const auto node3_uz = dofs.fullIndex(3, fesa::Dof::UZ); - const auto support_uz = dofs.fullIndex(1, fesa::Dof::UZ); - assembly.k_full(node2_uz, node2_uz) = 3.0; - assembly.k_full(node2_uz, node3_uz) = 1.0; - assembly.k_full(node3_uz, node2_uz) = 1.0; - assembly.k_full(node3_uz, node3_uz) = 2.0; - assembly.k_full(support_uz, node2_uz) = 7.0; - assembly.f_full[static_cast(node2_uz)] = -1.0; - assembly.f_full[static_cast(node3_uz)] = -2.0; - - const auto reduced = fesa::projectToReducedSystem(assembly, dofs); - FESA_CHECK(reduced.ok()); - FESA_CHECK(reduced.k.rows() == 2); - FESA_CHECK(reduced.k.cols() == 2); - FESA_CHECK(reduced.free_full_indices == dofs.freeFullIndices()); - FESA_CHECK_NEAR(reduced.k(0, 0), 3.0, 1.0e-15); - FESA_CHECK_NEAR(reduced.k(0, 1), 1.0, 1.0e-15); - FESA_CHECK_NEAR(reduced.k(1, 0), 1.0, 1.0e-15); - FESA_CHECK_NEAR(reduced.k(1, 1), 2.0, 1.0e-15); - FESA_CHECK_NEAR(reduced.f[0], -1.0, 1.0e-15); - FESA_CHECK_NEAR(reduced.f[1], -2.0, 1.0e-15); - - fesa::GaussianEliminationSolver solver; - const auto solved = solver.solve(reduced.k, reduced.f); - FESA_CHECK(solved.ok()); - FESA_CHECK_NEAR(solved.x[0], 0.0, 1.0e-12); - FESA_CHECK_NEAR(solved.x[1], -1.0, 1.0e-12); -} - -FESA_TEST(assembly_preserves_full_space_stiffness_load_and_reduced_pattern) { - auto domain = parsedPhase1Domain(); - fesa::DofManager dofs(domain); - const auto assembly = fesa::assembleSystem(domain, dofs); - FESA_CHECK(!fesa::hasError(assembly.diagnostics)); - FESA_CHECK(assembly.k_full.rows() == dofs.fullDofCount()); - FESA_CHECK(assembly.k_full.cols() == dofs.fullDofCount()); - FESA_CHECK(assembly.f_full.size() == static_cast(dofs.fullDofCount())); - FESA_CHECK(assembly.reduced_pattern.equation_count == dofs.freeDofCount()); - FESA_CHECK(assembly.reduced_pattern.nonzeroCount() == 4); - - const auto node2_uz = dofs.fullIndex(2, fesa::Dof::UZ); - const auto node3_uz = dofs.fullIndex(3, fesa::Dof::UZ); - FESA_CHECK_NEAR(assembly.f_full[static_cast(node2_uz)], -1.0, 1.0e-15); - FESA_CHECK_NEAR(assembly.f_full[static_cast(node3_uz)], -1.0, 1.0e-15); - for (fesa::LocalIndex i = 0; i < assembly.k_full.rows(); ++i) { - for (fesa::LocalIndex j = 0; j < assembly.k_full.cols(); ++j) { - FESA_CHECK_NEAR(assembly.k_full(i, j), assembly.k_full(j, i), 1.0e-8); - } - } - - const auto reduced = fesa::projectToReducedSystem(assembly, dofs); - FESA_CHECK(reduced.ok()); - const auto solved = fesa::GaussianEliminationSolver{}.solve(reduced.k, reduced.f); - FESA_CHECK(solved.ok()); - const auto residual = reduced.k.multiply(solved.x); - for (std::size_t i = 0; i < residual.size(); ++i) { - FESA_CHECK_NEAR(residual[i], reduced.f[i], 1.0e-8); - } -} - -FESA_TEST(gaussian_solver_solves_and_diagnoses_singular_systems) { - fesa::DenseMatrix a(2, 2); - a(0, 0) = 2.0; - a(0, 1) = 1.0; - a(1, 0) = 1.0; - a(1, 1) = 3.0; - fesa::GaussianEliminationSolver solver; - auto solved = solver.solve(a, {1.0, 2.0}); - FESA_CHECK(solved.ok()); - FESA_CHECK_NEAR(solved.x[0], 0.2, 1.0e-12); - FESA_CHECK_NEAR(solved.x[1], 0.6, 1.0e-12); - - fesa::DenseMatrix singular(2, 2); - singular(0, 0) = 1.0; - singular(0, 1) = 2.0; - singular(1, 0) = 2.0; - singular(1, 1) = 4.0; - auto failed = solver.solve(singular, {1.0, 2.0}); - FESA_CHECK(!failed.ok()); - FESA_CHECK(fesa::containsDiagnostic(failed.diagnostics, "FESA-SINGULAR-SOLVER")); -} - -FESA_TEST(results_writer_uses_step_frame_fields_for_u_and_rf) { - auto domain = parsedPhase1Domain(); - fesa::DofManager dofs(domain); - std::vector u(static_cast(dofs.fullDofCount()), 0.0); - std::vector rf(static_cast(dofs.fullDofCount()), 0.0); - u[static_cast(dofs.fullIndex(2, fesa::Dof::UZ))] = -0.1; - rf[static_cast(dofs.fullIndex(1, fesa::Dof::UZ))] = 1.0; - fesa::InMemoryResultsWriter writer; - writer.writeLinearStatic(domain, dofs, u, rf); - const auto& result = writer.result(); - FESA_CHECK(result.schema_name == "FESA_RESULTS"); - FESA_CHECK(result.schema_version == 1); - FESA_CHECK(result.solver_name == "FESA"); - FESA_CHECK(result.dof_convention == "UX,UY,UZ,RX,RY,RZ"); - FESA_CHECK(result.sign_convention == "Abaqus-compatible"); - FESA_CHECK(result.precision == "double"); - FESA_CHECK(result.index_type == "int64"); - FESA_CHECK(result.element_types == std::vector({"MITC4"})); - FESA_CHECK(result.steps.size() == 1); - FESA_CHECK(result.steps[0].name == "Step-1"); - FESA_CHECK(result.steps[0].frames.size() == 1); - const auto& frame = result.steps[0].frames[0]; - FESA_CHECK(frame.frame_id == 0); - FESA_CHECK(frame.increment == 1); - FESA_CHECK(frame.iteration == 0); - FESA_CHECK(frame.converged); - FESA_CHECK_NEAR(frame.step_time, 1.0, 1.0e-15); - FESA_CHECK_NEAR(frame.total_time, 1.0, 1.0e-15); - FESA_CHECK(frame.field_outputs.count("U") == 1); - FESA_CHECK(frame.field_outputs.count("RF") == 1); - const auto& u_field = frame.field_outputs.at("U"); - const auto& rf_field = frame.field_outputs.at("RF"); - FESA_CHECK(u_field.name == "U"); - FESA_CHECK(u_field.position == "NODAL"); - FESA_CHECK(u_field.entity_type == "node"); - FESA_CHECK(u_field.basis == "GLOBAL"); - FESA_CHECK(u_field.component_labels == fesa::displacementComponentLabels()); - FESA_CHECK(rf_field.name == "RF"); - FESA_CHECK(rf_field.position == "NODAL"); - FESA_CHECK(rf_field.entity_type == "node"); - FESA_CHECK(rf_field.basis == "GLOBAL"); - FESA_CHECK(rf_field.component_labels == fesa::reactionComponentLabels()); - FESA_CHECK(u_field.component_labels[2] == "UZ"); - FESA_CHECK(rf_field.component_labels[2] == "RFZ"); - FESA_CHECK(u_field.entity_ids.size() == domain.nodes.size()); - FESA_CHECK(rf_field.entity_ids.size() == domain.nodes.size()); -} - -FESA_TEST(displacement_csv_loader_accepts_quad01_format) { - auto table = fesa::loadDisplacementCsv(sourceRoot() + "/references/quad_01_displacements.csv"); - FESA_CHECK(!fesa::hasError(table.diagnostics)); - FESA_CHECK(table.rows.size() == 121); - FESA_CHECK(table.rows.count(1) == 1); -} - -FESA_TEST(displacement_csv_loader_accepts_quad02_format) { - auto table = fesa::loadDisplacementCsv(sourceRoot() + "/references/quad_02_displacements.csv"); - FESA_CHECK(!fesa::hasError(table.diagnostics)); - FESA_CHECK(table.rows.size() == 121); - FESA_CHECK(table.rows.count(2) == 1); - FESA_CHECK(table.rows.at(2).values[2] < 0.0); -} - -FESA_TEST(reaction_csv_loader_accepts_quad02_reactionforces_format) { - const auto required_columns = fesa::reactionCsvRequiredColumns(); - FESA_CHECK(required_columns == std::vector({"Node Label", "RF-RF1", "RF-RF2", "RF-RF3", - "RM-RM1", "RM-RM2", "RM-RM3"})); - - auto table = fesa::loadReactionCsv(sourceRoot() + "/references/quad_02_reactionforces.csv"); - FESA_CHECK(!fesa::hasError(table.diagnostics)); - FESA_CHECK(table.rows.size() == 121); - FESA_CHECK(table.rows.count(1) == 1); - FESA_CHECK(table.rows.count(2) == 1); - FESA_CHECK_NEAR(table.rows.at(1).values[2], 6.86e3, 1.0e-9); - FESA_CHECK_NEAR(table.rows.at(1).values[4], 2.40e4, 1.0e-8); -} - -FESA_TEST(displacement_csv_loader_reports_required_header_errors) { - auto table = fesa::loadDisplacementCsvFromString("Node Label,U-U1,U-U2,U-U3,UR-UR1,UR-UR2\n" - "1,0,0,0,0,0\n", - "missing-header.csv"); - FESA_CHECK(fesa::containsDiagnostic(table.diagnostics, "FESA-CSV-MISSING-COLUMN")); -} - -FESA_TEST(reaction_csv_loader_reports_required_header_errors) { - auto table = fesa::loadReactionCsvFromString("Node Label,RF-RF1,RF-RF2,RF-RF3,RM-RM1,RM-RM2\n" - "1,0,0,0,0,0\n", - "missing-reaction-header.csv"); - FESA_CHECK(fesa::containsDiagnostic(table.diagnostics, "FESA-CSV-MISSING-COLUMN")); -} - -FESA_TEST(displacement_csv_loader_reports_duplicate_node_rows) { - auto table = fesa::loadDisplacementCsvFromString("Node Label,U-U1,U-U2,U-U3,UR-UR1,UR-UR2,UR-UR3\n" - "1,0,0,0,0,0,0\n" - "1,0,0,0,0,0,0\n", - "duplicate-node.csv"); - FESA_CHECK(fesa::containsDiagnostic(table.diagnostics, "FESA-CSV-DUPLICATE-NODE")); -} - -FESA_TEST(reaction_csv_loader_reports_duplicate_node_rows) { - auto table = fesa::loadReactionCsvFromString("Node Label,RF-RF1,RF-RF2,RF-RF3,RM-RM1,RM-RM2,RM-RM3\n" - "1,0,0,0,0,0,0\n" - "1,0,0,0,0,0,0\n", - "duplicate-reaction-node.csv"); - FESA_CHECK(fesa::containsDiagnostic(table.diagnostics, "FESA-CSV-DUPLICATE-NODE")); -} - -FESA_TEST(displacement_csv_loader_reports_missing_and_non_numeric_node_rows) { - auto table = fesa::loadDisplacementCsvFromString("Node Label,U-U1,U-U2,U-U3,UR-UR1,UR-UR2,UR-UR3\n" - ",0,0,0,0,0,0\n" - "two,0,0,0,0,0,0\n" - "3,0,not-a-number,0,0,0,0\n", - "invalid-node-row.csv"); - FESA_CHECK(diagnosticCount(table.diagnostics, "FESA-CSV-NODE") == 2); - FESA_CHECK(fesa::containsDiagnostic(table.diagnostics, "FESA-CSV-NUMERIC")); -} - -FESA_TEST(displacement_comparator_matches_by_node_id_not_row_order) { - fesa::FieldOutput actual; - actual.name = "U"; - actual.position = "NODAL"; - actual.entity_type = "node"; - actual.basis = "GLOBAL"; - actual.entity_ids = {2, 1}; - actual.component_labels = fesa::displacementComponentLabels(); - actual.values = {{{2, 0, 0, 0, 0, 0}}, {{1, 0, 0, 0, 0, 0}}}; - fesa::CsvDisplacementTable expected; - expected.rows[1] = {1, {1, 0, 0, 0, 0, 0}}; - expected.rows[2] = {2, {2, 0, 0, 0, 0, 0}}; - auto compared = fesa::compareDisplacements(actual, expected, {1.0e-12, 1.0e-12, 1.0}); - FESA_CHECK(compared.pass); -} - -FESA_TEST(reaction_comparator_matches_by_node_id_not_row_order) { - fesa::FieldOutput actual; - actual.name = "RF"; - actual.position = "NODAL"; - actual.entity_type = "node"; - actual.basis = "GLOBAL"; - actual.entity_ids = {2, 1}; - actual.component_labels = fesa::reactionComponentLabels(); - actual.values = {{{0, 0, 2, 0, 20, 0}}, {{0, 0, 1, 0, 10, 0}}}; - fesa::CsvReactionTable expected; - expected.rows[1] = {1, {0, 0, 1, 0, 10, 0}}; - expected.rows[2] = {2, {0, 0, 2, 0, 20, 0}}; - auto compared = fesa::compareReactions(actual, expected, {1.0e-12, 1.0e-12, 1.0}); - FESA_CHECK(compared.pass); -} - -FESA_TEST(displacement_comparator_uses_absolute_and_relative_tolerances) { - fesa::FieldOutput actual; - actual.name = "U"; - actual.position = "NODAL"; - actual.entity_type = "node"; - actual.basis = "GLOBAL"; - actual.entity_ids = {10}; - actual.component_labels = fesa::displacementComponentLabels(); - actual.values = {{{5.0e-7, 100.0005, 0, 0, 0, 0}}}; - fesa::CsvDisplacementTable expected; - expected.rows[10] = {10, {0.0, 100.0, 0, 0, 0, 0}}; - - auto loose = fesa::compareDisplacements(actual, expected, {1.0e-6, 1.0e-5, 1.0}); - FESA_CHECK(loose.pass); - FESA_CHECK_NEAR(loose.max_abs_error, 5.0e-4, 1.0e-12); - - auto strict = fesa::compareDisplacements(actual, expected, {1.0e-8, 1.0e-8, 1.0}); - FESA_CHECK(!strict.pass); - FESA_CHECK(fesa::containsDiagnostic(strict.diagnostics, "FESA-COMPARE-TOLERANCE")); -} - -FESA_TEST(displacement_comparator_rejects_wrong_component_labels_and_missing_nodes) { - fesa::FieldOutput actual; - actual.name = "U"; - actual.position = "NODAL"; - actual.entity_type = "node"; - actual.basis = "GLOBAL"; - actual.entity_ids = {1}; - actual.component_labels = fesa::reactionComponentLabels(); - actual.values = {{{0, 0, 0, 0, 0, 0}}}; - fesa::CsvDisplacementTable expected; - expected.rows[2] = {2, {0, 0, 0, 0, 0, 0}}; - - auto compared = fesa::compareDisplacements(actual, expected, {1.0e-12, 1.0e-12, 1.0}); - FESA_CHECK(!compared.pass); - FESA_CHECK(fesa::containsDiagnostic(compared.diagnostics, "FESA-COMPARE-COMPONENT-LABELS")); - FESA_CHECK(fesa::containsDiagnostic(compared.diagnostics, "FESA-COMPARE-MISSING-ACTUAL")); -} - -FESA_TEST(reaction_comparator_rejects_wrong_component_labels_and_missing_nodes) { - fesa::FieldOutput actual; - actual.name = "RF"; - actual.position = "NODAL"; - actual.entity_type = "node"; - actual.basis = "GLOBAL"; - actual.entity_ids = {1}; - actual.component_labels = fesa::displacementComponentLabels(); - actual.values = {{{0, 0, 0, 0, 0, 0}}}; - fesa::CsvReactionTable expected; - expected.rows[2] = {2, {0, 0, 0, 0, 0, 0}}; - - auto compared = fesa::compareReactions(actual, expected, {1.0e-12, 1.0e-12, 1.0}); - FESA_CHECK(!compared.pass); - FESA_CHECK(fesa::containsDiagnostic(compared.diagnostics, "FESA-COMPARE-COMPONENT-LABELS")); - FESA_CHECK(fesa::containsDiagnostic(compared.diagnostics, "FESA-COMPARE-MISSING-ACTUAL")); -} - -FESA_TEST(displacement_comparator_reports_duplicate_actual_nodes) { - fesa::FieldOutput actual; - actual.name = "U"; - actual.position = "NODAL"; - actual.entity_type = "node"; - actual.basis = "GLOBAL"; - actual.entity_ids = {1, 1}; - actual.component_labels = fesa::displacementComponentLabels(); - actual.values = {{{0, 0, 0, 0, 0, 0}}, {{0, 0, 0, 0, 0, 0}}}; - fesa::CsvDisplacementTable expected; - expected.rows[1] = {1, {0, 0, 0, 0, 0, 0}}; - - auto compared = fesa::compareDisplacements(actual, expected, {1.0e-12, 1.0e-12, 1.0}); - FESA_CHECK(!compared.pass); - FESA_CHECK(fesa::containsDiagnostic(compared.diagnostics, "FESA-COMPARE-DUPLICATE-ACTUAL")); -} - -FESA_TEST(quad02_reference_fixture_discovery_is_consistent) { - fesa::AbaqusInputParser parser; - const auto original = parser.parseFile(sourceRoot() + "/references/quad_02.inp"); - FESA_CHECK(!original.ok()); - FESA_CHECK(fesa::containsDiagnostic(original.diagnostics, "FESA-PARSE-UNSUPPORTED-KEYWORD")); - - const auto normalized = parser.parseFile(sourceRoot() + "/references/quad_02_phase1.inp"); - FESA_CHECK(normalized.ok()); - FESA_CHECK(normalized.domain.nodes.size() == 121); - FESA_CHECK(normalized.domain.elements.size() == 100); - FESA_CHECK(normalized.domain.steps.size() == 1); - FESA_CHECK(normalized.domain.steps.front().name == "Step-1"); - - const auto reference = fesa::loadDisplacementCsv(sourceRoot() + "/references/quad_02_displacements.csv"); - FESA_CHECK(!fesa::hasError(reference.diagnostics)); - FESA_CHECK(reference.rows.size() == normalized.domain.nodes.size()); - const auto reactions = fesa::loadReactionCsv(sourceRoot() + "/references/quad_02_reactionforces.csv"); - FESA_CHECK(!fesa::hasError(reactions.diagnostics)); - FESA_CHECK(reactions.rows.size() == normalized.domain.nodes.size()); - for (const auto& [node_id, node] : normalized.domain.nodes) { - (void)node; - FESA_CHECK(reference.rows.count(node_id) == 1); - FESA_CHECK(reactions.rows.count(node_id) == 1); - } -} - -FESA_TEST(quad02_phase1_stored_displacement_reference_regression) { - const auto input_text = readTextFile(sourceRoot() + "/references/quad_02_phase1.inp"); - const auto analysis = fesa::runLinearStaticInputString(input_text, "quad_02_phase1.inp"); - FESA_CHECK(analysis.ok()); - FESA_CHECK(analysis.state.converged); - FESA_CHECK(analysis.result_file.steps.size() == 1); - const auto& frame = analysis.result_file.steps[0].frames[0]; - FESA_CHECK(frame.field_outputs.count("U") == 1); - - const auto expected = fesa::loadDisplacementCsv(sourceRoot() + "/references/quad_02_displacements.csv"); - FESA_CHECK(!fesa::hasError(expected.diagnostics)); - const auto comparison = fesa::compareDisplacements(frame.field_outputs.at("U"), expected, {1.0e-12, 1.0e-5, 1.0}); - checkComparisonPass(comparison); - FESA_CHECK(comparison.max_abs_error <= 1.0e-5); - FESA_CHECK(comparison.max_rel_error <= 1.0e-5); -} - -FESA_TEST(quad02_phase1_stored_reaction_reference_comparison_reports_current_gap) { - const auto input_text = readTextFile(sourceRoot() + "/references/quad_02_phase1.inp"); - const auto analysis = fesa::runLinearStaticInputString(input_text, "quad_02_phase1.inp"); - FESA_CHECK(analysis.ok()); - FESA_CHECK(analysis.state.converged); - FESA_CHECK(analysis.result_file.steps.size() == 1); - const auto& frame = analysis.result_file.steps[0].frames[0]; - FESA_CHECK(frame.field_outputs.count("RF") == 1); - - const auto expected = fesa::loadReactionCsv(sourceRoot() + "/references/quad_02_reactionforces.csv"); - FESA_CHECK(!fesa::hasError(expected.diagnostics)); - const auto comparison = fesa::compareReactions(frame.field_outputs.at("RF"), expected, {1.0e-6, 1.0e-5, 1.0}); - FESA_CHECK(!comparison.pass); - FESA_CHECK(fesa::containsDiagnostic(comparison.diagnostics, "FESA-COMPARE-TOLERANCE")); - FESA_CHECK(comparison.max_abs_error > 100.0); - FESA_CHECK(comparison.max_rel_error > 0.1); -} - -FESA_TEST(mitc4_shape_functions_node_order_and_tying_points) { - auto center = fesa::shapeFunctions(0.0, 0.0); - const fesa::Real sum = center.n[0] + center.n[1] + center.n[2] + center.n[3]; - FESA_CHECK_NEAR(sum, 1.0, 1.0e-15); - FESA_CHECK_NEAR(center.dr[0], -0.25, 1.0e-15); - FESA_CHECK_NEAR(center.dr[1], 0.25, 1.0e-15); - FESA_CHECK_NEAR(center.dr[2], 0.25, 1.0e-15); - FESA_CHECK_NEAR(center.dr[3], -0.25, 1.0e-15); - FESA_CHECK_NEAR(center.ds[0], -0.25, 1.0e-15); - FESA_CHECK_NEAR(center.ds[1], -0.25, 1.0e-15); - FESA_CHECK_NEAR(center.ds[2], 0.25, 1.0e-15); - FESA_CHECK_NEAR(center.ds[3], 0.25, 1.0e-15); - - const auto node_points = fesa::mitc4NodeNaturalCoordinates(); - FESA_CHECK_NEAR(node_points[0].xi, -1.0, 1.0e-15); - FESA_CHECK_NEAR(node_points[0].eta, -1.0, 1.0e-15); - FESA_CHECK_NEAR(node_points[1].xi, 1.0, 1.0e-15); - FESA_CHECK_NEAR(node_points[1].eta, -1.0, 1.0e-15); - FESA_CHECK_NEAR(node_points[2].xi, 1.0, 1.0e-15); - FESA_CHECK_NEAR(node_points[2].eta, 1.0, 1.0e-15); - FESA_CHECK_NEAR(node_points[3].xi, -1.0, 1.0e-15); - FESA_CHECK_NEAR(node_points[3].eta, 1.0, 1.0e-15); - - for (std::size_t i = 0; i < 4; ++i) { - const auto corner = fesa::shapeFunctions(node_points[i].xi, node_points[i].eta); - FESA_CHECK_NEAR(corner.n[i], 1.0, 1.0e-15); - } - - const auto tying_points = fesa::mitc4TyingPoints(); - FESA_CHECK(tying_points[0].label == "A"); - FESA_CHECK_NEAR(tying_points[0].natural.xi, 0.0, 1.0e-15); - FESA_CHECK_NEAR(tying_points[0].natural.eta, -1.0, 1.0e-15); - FESA_CHECK((tying_points[0].edge_node_indices == std::array{0, 1})); - FESA_CHECK(tying_points[1].label == "B"); - FESA_CHECK_NEAR(tying_points[1].natural.xi, -1.0, 1.0e-15); - FESA_CHECK_NEAR(tying_points[1].natural.eta, 0.0, 1.0e-15); - FESA_CHECK((tying_points[1].edge_node_indices == std::array{0, 3})); - FESA_CHECK(tying_points[2].label == "C"); - FESA_CHECK_NEAR(tying_points[2].natural.xi, 0.0, 1.0e-15); - FESA_CHECK_NEAR(tying_points[2].natural.eta, 1.0, 1.0e-15); - FESA_CHECK((tying_points[2].edge_node_indices == std::array{3, 2})); - FESA_CHECK(tying_points[3].label == "D"); - FESA_CHECK_NEAR(tying_points[3].natural.xi, 1.0, 1.0e-15); - FESA_CHECK_NEAR(tying_points[3].natural.eta, 0.0, 1.0e-15); - FESA_CHECK((tying_points[3].edge_node_indices == std::array{1, 2})); -} - -FESA_TEST(mitc4_geometry_builds_flat_directors_and_integration_basis) { - const std::array coords = {{{0, 0, 0}, {1, 0, 0}, {1, 1, 0}, {0, 1, 0}}}; - auto geometry = fesa::buildMITC4Geometry(coords, 0.2); - FESA_CHECK(geometry.ok()); - checkVecNear(geometry.center_normal, {0, 0, 1}, 1.0e-14); - checkVecNear(geometry.g1_center, {0.5, 0, 0}, 1.0e-14); - checkVecNear(geometry.g2_center, {0, 0.5, 0}, 1.0e-14); - for (const auto& frame : geometry.nodal_frames) { - checkVecNear(frame.v1, {1, 0, 0}, 1.0e-14); - checkVecNear(frame.v2, {0, 1, 0}, 1.0e-14); - checkVecNear(frame.vn, {0, 0, 1}, 1.0e-14); - checkRightHandedOrthonormal(frame.v1, frame.v2, frame.vn); - } - - auto basis = fesa::computeMITC4IntegrationBasis(geometry, 0.0, 0.0, 0.0); - FESA_CHECK(basis.ok()); - checkVecNear(basis.g1, {0.5, 0, 0}, 1.0e-14); - checkVecNear(basis.g2, {0, 0.5, 0}, 1.0e-14); - checkVecNear(basis.g3, {0, 0, 0.1}, 1.0e-14); - checkVecNear(basis.local.e1, {1, 0, 0}, 1.0e-14); - checkVecNear(basis.local.e2, {0, 1, 0}, 1.0e-14); - checkVecNear(basis.local.e3, {0, 0, 1}, 1.0e-14); - checkRightHandedOrthonormal(basis.local.e1, basis.local.e2, basis.local.e3); - FESA_CHECK_NEAR(basis.jacobian, 0.025, 1.0e-14); -} - -FESA_TEST(mitc4_geometry_uses_deterministic_director_fallback_axis) { - const std::array coords = {{{0, 0, 0}, {1, 0, 0}, {1, 0, -1}, {0, 0, -1}}}; - auto geometry = fesa::buildMITC4Geometry(coords, 0.1); - FESA_CHECK(geometry.ok()); - checkVecNear(geometry.center_normal, {0, 1, 0}, 1.0e-14); - for (const auto& frame : geometry.nodal_frames) { - checkVecNear(frame.v1, {-1, 0, 0}, 1.0e-14); - checkVecNear(frame.v2, {0, 0, 1}, 1.0e-14); - checkVecNear(frame.vn, {0, 1, 0}, 1.0e-14); - checkRightHandedOrthonormal(frame.v1, frame.v2, frame.vn); - } -} - -FESA_TEST(mitc4_geometry_reports_singular_geometry_and_thickness) { - const std::array flat = {{{0, 0, 0}, {1, 0, 0}, {1, 1, 0}, {0, 1, 0}}}; - auto invalid_thickness = fesa::buildMITC4Geometry(flat, 0.0); - FESA_CHECK(!invalid_thickness.ok()); - FESA_CHECK(fesa::containsDiagnostic(invalid_thickness.diagnostics, "FESA-MITC4-THICKNESS")); - - const std::array collinear = {{{0, 0, 0}, {1, 0, 0}, {2, 0, 0}, {3, 0, 0}}}; - auto singular = fesa::buildMITC4Geometry(collinear, 0.1); - FESA_CHECK(!singular.ok()); - FESA_CHECK(fesa::containsDiagnostic(singular.diagnostics, "FESA-MITC4-SINGULAR-NORMAL")); - - const std::array collapsed_corner = {{{0, 0, 0}, {0, 0, 0}, {1, 1, 0}, {0, 1, 0}}}; - auto geometry = fesa::buildMITC4Geometry(collapsed_corner, 0.1); - FESA_CHECK(geometry.ok()); - auto corner_basis = fesa::computeMITC4IntegrationBasis(geometry, -1.0, -1.0, 0.0); - FESA_CHECK(!corner_basis.ok()); - FESA_CHECK(fesa::containsDiagnostic(corner_basis.diagnostics, "FESA-MITC4-SINGULAR-JACOBIAN")); -} - -FESA_TEST(mitc4_rotation_transform_and_displacement_interpolation) { - const std::array coords = {{{0, 0, 0}, {1, 0, 0}, {1, 1, 0}, {0, 1, 0}}}; - const auto geometry = fesa::buildMITC4Geometry(coords, 0.2); - FESA_CHECK(geometry.ok()); - const auto flat_rotation = fesa::mitc4LocalRotations(geometry.nodal_frames[0], {1.0, 2.0, 3.0}); - FESA_CHECK_NEAR(flat_rotation.alpha, 1.0, 1.0e-14); - FESA_CHECK_NEAR(flat_rotation.beta, 2.0, 1.0e-14); - FESA_CHECK_NEAR(flat_rotation.gamma, 3.0, 1.0e-14); - checkVecNear(fesa::mitc4DirectorIncrement(geometry.nodal_frames[0], {0.0, 0.0, 5.0}), {0, 0, 0}, 1.0e-14); - checkVecNear(fesa::mitc4DirectorIncrement(geometry.nodal_frames[0], {0.0, 2.0, 5.0}), {2, 0, 0}, 1.0e-14); - - std::array dofs = zeroElementDofs(); - for (std::size_t node = 0; node < 4; ++node) { - dofs[6 * node + 0] = 1.0; - dofs[6 * node + 1] = 0.5; - dofs[6 * node + 4] = 2.0; - dofs[6 * node + 5] = 99.0; - } - const auto mid = fesa::mitc4DisplacementDerivatives(geometry, dofs, 0.0, 0.0, 0.0); - FESA_CHECK(mid.ok()); - checkVecNear(mid.displacement, {1.0, 0.5, 0.0}, 1.0e-14); - const auto top = fesa::mitc4DisplacementDerivatives(geometry, dofs, 0.0, 0.0, 1.0); - FESA_CHECK(top.ok()); - checkVecNear(top.displacement, {1.2, 0.5, 0.0}, 1.0e-14); - - const std::array fallback_coords = {{{0, 0, 0}, {1, 0, 0}, {1, 0, -1}, {0, 0, -1}}}; - const auto fallback_geometry = fesa::buildMITC4Geometry(fallback_coords, 0.1); - FESA_CHECK(fallback_geometry.ok()); - const auto fallback_rotation = fesa::mitc4LocalRotations(fallback_geometry.nodal_frames[0], {2.0, 3.0, 5.0}); - FESA_CHECK_NEAR(fallback_rotation.alpha, -2.0, 1.0e-14); - FESA_CHECK_NEAR(fallback_rotation.beta, 5.0, 1.0e-14); - FESA_CHECK_NEAR(fallback_rotation.gamma, 3.0, 1.0e-14); -} - -FESA_TEST(mitc4_direct_covariant_strain_rows_match_finite_difference) { - const std::array coords = {{{0, 0, 0}, {1, 0, 0}, {1, 1, 0}, {0, 1, 0}}}; - const auto geometry = fesa::buildMITC4Geometry(coords, 0.2); - FESA_CHECK(geometry.ok()); - constexpr fesa::Real xi = 0.2; - constexpr fesa::Real eta = -0.3; - constexpr fesa::Real zeta = 0.4; - const auto rows = fesa::mitc4DirectCovariantStrainRows(geometry, xi, eta, zeta); - FESA_CHECK(rows.ok()); - FESA_CHECK(fesa::mitc4StrainComponentLabels()[0] == "eps11"); - FESA_CHECK(fesa::mitc4StrainComponentLabels()[3] == "gamma23"); - const std::array checked_dofs = {0, 1, 2, 7, 9, 10, 11}; - const fesa::Real h = 1.0e-6; - for (std::size_t dof : checked_dofs) { - auto plus = zeroElementDofs(); - auto minus = zeroElementDofs(); - plus[dof] = h; - minus[dof] = -h; - const auto plus_strain = fesa::mitc4DirectCovariantStrain(geometry, plus, xi, eta, zeta); - const auto minus_strain = fesa::mitc4DirectCovariantStrain(geometry, minus, xi, eta, zeta); - FESA_CHECK(plus_strain.ok()); - FESA_CHECK(minus_strain.ok()); - for (std::size_t component = 0; component < 6; ++component) { - const fesa::Real finite_difference = (plus_strain.values[component] - minus_strain.values[component]) / (2.0 * h); - FESA_CHECK_NEAR(rows.rows[component][dof], finite_difference, 1.0e-8); - } - } - - const auto gamma13 = fesa::strainComponentIndex(fesa::MITC4StrainComponent::Gamma13); - const auto gamma23 = fesa::strainComponentIndex(fesa::MITC4StrainComponent::Gamma23); - for (std::size_t node = 0; node < 4; ++node) { - const std::size_t drilling_dof = 6 * node + 5; - for (std::size_t component = 0; component < 6; ++component) { - FESA_CHECK_NEAR(rows.rows[component][drilling_dof], 0.0, 1.0e-14); - } - } - FESA_CHECK(gamma13 == 4); - FESA_CHECK(gamma23 == 3); -} - -FESA_TEST(mitc4_mitc_tying_rows_use_fesa_ac_bd_signs) { - const std::array coords = {{{0, 0, 0}, {1, 0, 0}, {1, 1, 0}, {0, 1, 0}}}; - const auto geometry = fesa::buildMITC4Geometry(coords, 0.2); - FESA_CHECK(geometry.ok()); - const auto gamma23 = fesa::strainComponentIndex(fesa::MITC4StrainComponent::Gamma23); - const auto gamma13 = fesa::strainComponentIndex(fesa::MITC4StrainComponent::Gamma13); - - const auto direct_a = fesa::mitc4DirectCovariantStrainRows(geometry, 0.0, -1.0, 0.0); - const auto direct_b = fesa::mitc4DirectCovariantStrainRows(geometry, -1.0, 0.0, 0.0); - const auto direct_c = fesa::mitc4DirectCovariantStrainRows(geometry, 0.0, 1.0, 0.0); - const auto direct_d = fesa::mitc4DirectCovariantStrainRows(geometry, 1.0, 0.0, 0.0); - const auto mitc_a = fesa::mitc4TiedCovariantStrainRows(geometry, 0.0, -1.0, 0.0); - const auto mitc_b = fesa::mitc4TiedCovariantStrainRows(geometry, -1.0, 0.0, 0.0); - const auto mitc_c = fesa::mitc4TiedCovariantStrainRows(geometry, 0.0, 1.0, 0.0); - const auto mitc_d = fesa::mitc4TiedCovariantStrainRows(geometry, 1.0, 0.0, 0.0); - FESA_CHECK(direct_a.ok()); - FESA_CHECK(direct_b.ok()); - FESA_CHECK(direct_c.ok()); - FESA_CHECK(direct_d.ok()); - FESA_CHECK(mitc_a.ok()); - FESA_CHECK(mitc_b.ok()); - FESA_CHECK(mitc_c.ok()); - FESA_CHECK(mitc_d.ok()); - for (std::size_t dof = 0; dof < 24; ++dof) { - FESA_CHECK_NEAR(mitc_a.rows[gamma13][dof], direct_a.rows[gamma13][dof], 1.0e-14); - FESA_CHECK_NEAR(mitc_c.rows[gamma13][dof], direct_c.rows[gamma13][dof], 1.0e-14); - FESA_CHECK_NEAR(mitc_b.rows[gamma23][dof], direct_b.rows[gamma23][dof], 1.0e-14); - FESA_CHECK_NEAR(mitc_d.rows[gamma23][dof], direct_d.rows[gamma23][dof], 1.0e-14); - } -} - -FESA_TEST(mitc4_mitc_gauss_shear_rows_are_interpolated_from_tying_rows) { - const std::array coords = {{{0, 0, 0}, {1, 0, 0}, {1, 1, 0}, {0, 1, 0}}}; - const auto geometry = fesa::buildMITC4Geometry(coords, 0.2); - FESA_CHECK(geometry.ok()); - constexpr fesa::Real xi = 0.5; - constexpr fesa::Real eta = 0.25; - constexpr fesa::Real zeta = 0.0; - const auto gamma23 = fesa::strainComponentIndex(fesa::MITC4StrainComponent::Gamma23); - const auto gamma13 = fesa::strainComponentIndex(fesa::MITC4StrainComponent::Gamma13); - const auto direct_at_point = fesa::mitc4DirectCovariantStrainRows(geometry, xi, eta, zeta); - const auto direct_a = fesa::mitc4DirectCovariantStrainRows(geometry, 0.0, -1.0, zeta); - const auto direct_b = fesa::mitc4DirectCovariantStrainRows(geometry, -1.0, 0.0, zeta); - const auto direct_c = fesa::mitc4DirectCovariantStrainRows(geometry, 0.0, 1.0, zeta); - const auto direct_d = fesa::mitc4DirectCovariantStrainRows(geometry, 1.0, 0.0, zeta); - const auto tied = fesa::mitc4TiedCovariantStrainRows(geometry, xi, eta, zeta); - FESA_CHECK(direct_at_point.ok()); - FESA_CHECK(tied.ok()); - - const std::size_t node2_ry = 6 + 4; - const fesa::Real expected_gamma13 = - 0.5 * (1.0 - eta) * direct_a.rows[gamma13][node2_ry] + 0.5 * (1.0 + eta) * direct_c.rows[gamma13][node2_ry]; - FESA_CHECK_NEAR(tied.rows[gamma13][node2_ry], expected_gamma13, 1.0e-14); - FESA_CHECK(std::fabs(tied.rows[gamma13][node2_ry] - direct_at_point.rows[gamma13][node2_ry]) > 1.0e-4); - - const std::size_t node4_rx = 3 * 6 + 3; - const fesa::Real expected_gamma23 = - 0.5 * (1.0 - xi) * direct_b.rows[gamma23][node4_rx] + 0.5 * (1.0 + xi) * direct_d.rows[gamma23][node4_rx]; - FESA_CHECK_NEAR(tied.rows[gamma23][node4_rx], expected_gamma23, 1.0e-14); - FESA_CHECK(std::fabs(tied.rows[gamma23][node4_rx] - direct_at_point.rows[gamma23][node4_rx]) > 1.0e-4); - - for (std::size_t component : {std::size_t{0}, std::size_t{1}, std::size_t{2}, std::size_t{5}}) { - for (std::size_t dof = 0; dof < 24; ++dof) { - FESA_CHECK_NEAR(tied.rows[component][dof], direct_at_point.rows[component][dof], 1.0e-14); - } - } -} - -FESA_TEST(mitc4_plane_stress_material_matrix_uses_documented_order_and_shear_correction) { - constexpr fesa::Real elastic_modulus = 210.0; - constexpr fesa::Real poisson_ratio = 0.3; - constexpr fesa::Real kappa = 5.0 / 6.0; - const auto law = fesa::mitc4PlaneStressMaterialMatrix(elastic_modulus, poisson_ratio, kappa); - FESA_CHECK(law.ok()); - const auto eps11 = fesa::strainComponentIndex(fesa::MITC4StrainComponent::Eps11); - const auto eps22 = fesa::strainComponentIndex(fesa::MITC4StrainComponent::Eps22); - const auto eps33 = fesa::strainComponentIndex(fesa::MITC4StrainComponent::Eps33); - const auto gamma23 = fesa::strainComponentIndex(fesa::MITC4StrainComponent::Gamma23); - const auto gamma13 = fesa::strainComponentIndex(fesa::MITC4StrainComponent::Gamma13); - const auto gamma12 = fesa::strainComponentIndex(fesa::MITC4StrainComponent::Gamma12); - const fesa::Real plane_stress_scale = elastic_modulus / (1.0 - poisson_ratio * poisson_ratio); - const fesa::Real shear_modulus = elastic_modulus / (2.0 * (1.0 + poisson_ratio)); - - FESA_CHECK(fesa::mitc4StrainComponentLabels() == - (std::array{"eps11", "eps22", "eps33", "gamma23", "gamma13", "gamma12"})); - FESA_CHECK_NEAR(law.matrix[eps11][eps11], plane_stress_scale, 1.0e-12); - FESA_CHECK_NEAR(law.matrix[eps11][eps22], poisson_ratio * plane_stress_scale, 1.0e-12); - FESA_CHECK_NEAR(law.matrix[eps22][eps11], poisson_ratio * plane_stress_scale, 1.0e-12); - FESA_CHECK_NEAR(law.matrix[eps22][eps22], plane_stress_scale, 1.0e-12); - for (std::size_t component = 0; component < 6; ++component) { - FESA_CHECK_NEAR(law.matrix[eps33][component], 0.0, 1.0e-15); - FESA_CHECK_NEAR(law.matrix[component][eps33], 0.0, 1.0e-15); - } - FESA_CHECK_NEAR(law.matrix[gamma23][gamma23], kappa * shear_modulus, 1.0e-12); - FESA_CHECK_NEAR(law.matrix[gamma13][gamma13], kappa * shear_modulus, 1.0e-12); - FESA_CHECK_NEAR(law.matrix[gamma12][gamma12], shear_modulus, 1.0e-12); - FESA_CHECK_NEAR(law.matrix[gamma13][gamma23], 0.0, 1.0e-15); - - fesa::MITC4StrainVector strain{}; - strain[gamma23] = 2.0; - strain[gamma13] = 3.0; - strain[gamma12] = 4.0; - const auto stress = fesa::multiplyMITC4MaterialMatrix(law.matrix, strain); - FESA_CHECK_NEAR(stress[gamma23], 2.0 * kappa * shear_modulus, 1.0e-12); - FESA_CHECK_NEAR(stress[gamma13], 3.0 * kappa * shear_modulus, 1.0e-12); - FESA_CHECK_NEAR(stress[gamma12], 4.0 * shear_modulus, 1.0e-12); -} - -FESA_TEST(mitc4_material_matrix_reports_invalid_elastic_inputs) { - auto invalid_e = fesa::mitc4PlaneStressMaterialMatrix(-1.0, 0.3); - FESA_CHECK(!invalid_e.ok()); - FESA_CHECK(fesa::containsDiagnostic(invalid_e.diagnostics, "FESA-MITC4-MATERIAL")); - - auto invalid_nu = fesa::mitc4PlaneStressMaterialMatrix(1000.0, 0.5); - FESA_CHECK(!invalid_nu.ok()); - FESA_CHECK(fesa::containsDiagnostic(invalid_nu.diagnostics, "FESA-MITC4-POISSON")); - - auto invalid_kappa = fesa::mitc4PlaneStressMaterialMatrix(1000.0, 0.25, 0.0); - FESA_CHECK(!invalid_kappa.ok()); - FESA_CHECK(fesa::containsDiagnostic(invalid_kappa.diagnostics, "FESA-MITC4-SHEAR-CORRECTION")); -} - -FESA_TEST(mitc4_gauss_integration_uses_2x2x2_points_and_unit_weights) { - const auto points = fesa::mitc4GaussQuadrature2x2x2(); - FESA_CHECK(points.size() == 8); - const fesa::Real gauss = 1.0 / std::sqrt(3.0); - fesa::Real total_weight = 0.0; - int positive_xi = 0; - int positive_eta = 0; - int positive_zeta = 0; - for (const auto& point : points) { - FESA_CHECK_NEAR(std::fabs(point.xi), gauss, 1.0e-15); - FESA_CHECK_NEAR(std::fabs(point.eta), gauss, 1.0e-15); - FESA_CHECK_NEAR(std::fabs(point.zeta), gauss, 1.0e-15); - FESA_CHECK_NEAR(point.weight, 1.0, 1.0e-15); - total_weight += point.weight; - positive_xi += point.xi > 0.0 ? 1 : 0; - positive_eta += point.eta > 0.0 ? 1 : 0; - positive_zeta += point.zeta > 0.0 ? 1 : 0; - } - FESA_CHECK_NEAR(total_weight, 8.0, 1.0e-15); - FESA_CHECK(positive_xi == 4); - FESA_CHECK(positive_eta == 4); - FESA_CHECK(positive_zeta == 4); -} - -FESA_TEST(mitc4_covariant_to_local_transform_is_identity_for_orthonormal_basis) { - fesa::MITC4IntegrationBasis basis; - basis.g1 = {1.0, 0.0, 0.0}; - basis.g2 = {0.0, 1.0, 0.0}; - basis.g3 = {0.0, 0.0, 1.0}; - basis.local = {{1.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {0.0, 0.0, 1.0}}; - basis.jacobian = 1.0; - const auto transform = fesa::mitc4CovariantToLocalStrainTransform(basis); - FESA_CHECK(transform.ok()); - for (std::size_t row = 0; row < 6; ++row) { - for (std::size_t col = 0; col < 6; ++col) { - FESA_CHECK_NEAR(transform.matrix[row][col], row == col ? 1.0 : 0.0, 1.0e-15); - } - } -} - -FESA_TEST(mitc4_flat_element_material_transform_scales_convected_strains_to_local_frame) { - const std::array coords = {{{0, 0, 0}, {1, 0, 0}, {1, 1, 0}, {0, 1, 0}}}; - const auto geometry = fesa::buildMITC4Geometry(coords, 0.2); - FESA_CHECK(geometry.ok()); - const auto basis = fesa::computeMITC4IntegrationBasis(geometry, 0.0, 0.0, 0.0); - FESA_CHECK(basis.ok()); - const auto transform = fesa::mitc4CovariantToLocalStrainTransform(basis); - FESA_CHECK(transform.ok()); - - const std::array expected_diagonal = {4.0, 4.0, 100.0, 20.0, 20.0, 4.0}; - for (std::size_t row = 0; row < 6; ++row) { - for (std::size_t col = 0; col < 6; ++col) { - FESA_CHECK_NEAR(transform.matrix[row][col], row == col ? expected_diagonal[row] : 0.0, 1.0e-13); - } - } - - const auto law = fesa::mitc4PlaneStressMaterialMatrix(1000.0, 0.25); - FESA_CHECK(law.ok()); - const auto convected = fesa::mitc4TransformMaterialMatrix(law.matrix, transform.matrix); - fesa::MITC4StrainVector local_strain{}; - local_strain[fesa::strainComponentIndex(fesa::MITC4StrainComponent::Eps11)] = 0.02; - local_strain[fesa::strainComponentIndex(fesa::MITC4StrainComponent::Eps22)] = -0.01; - local_strain[fesa::strainComponentIndex(fesa::MITC4StrainComponent::Gamma12)] = 0.03; - - fesa::MITC4StrainVector convected_strain{}; - for (std::size_t component = 0; component < 6; ++component) { - convected_strain[component] = local_strain[component] / expected_diagonal[component]; - } - const auto local_stress = fesa::multiplyMITC4MaterialMatrix(law.matrix, local_strain); - const auto convected_stress = fesa::multiplyMITC4MaterialMatrix(convected, convected_strain); - const fesa::Real local_energy = fesa::dotMITC4Vector(local_strain, local_stress); - const fesa::Real convected_energy = fesa::dotMITC4Vector(convected_strain, convected_stress); - FESA_CHECK_NEAR(convected_energy, local_energy, 1.0e-12); -} - -FESA_TEST(mitc4_material_integration_samples_carry_tied_rows_basis_and_transformed_material) { - const std::array coords = {{{0, 0, 0}, {1, 0, 0}, {1, 1, 0}, {0, 1, 0}}}; - const auto geometry = fesa::buildMITC4Geometry(coords, 0.2); - FESA_CHECK(geometry.ok()); - const auto data = fesa::mitc4BuildMaterialIntegrationData(geometry, 1000.0, 0.3); - FESA_CHECK(data.ok()); - FESA_CHECK(data.samples.size() == 8); - for (const auto& sample : data.samples) { - FESA_CHECK(sample.ok()); - FESA_CHECK_NEAR(sample.point.weight, 1.0, 1.0e-15); - FESA_CHECK(sample.basis.ok()); - FESA_CHECK(sample.strain_rows.ok()); - for (std::size_t i = 0; i < 6; ++i) { - for (std::size_t j = 0; j < 6; ++j) { - FESA_CHECK_NEAR(sample.convected_material[i][j], sample.convected_material[j][i], 1.0e-10); - } - } - } -} - -FESA_TEST(mitc4_element_stiffness_result_is_symmetric_and_uses_2x2x2_samples) { - const std::array coords = {{{0, 0, 0}, {1, 0, 0}, {1, 1, 0}, {0, 1, 0}}}; - const auto result = fesa::mitc4ElementStiffness(coords, 1000.0, 0.25, 0.2); - FESA_CHECK(result.ok()); - FESA_CHECK(result.integration_point_count == 8); - FESA_CHECK(result.local_without_drilling.rows() == 24); - FESA_CHECK(result.local_with_drilling.rows() == 24); - FESA_CHECK(result.global.rows() == 24); - FESA_CHECK(result.global.cols() == 24); - for (fesa::LocalIndex i = 0; i < 24; ++i) { - for (fesa::LocalIndex j = 0; j < 24; ++j) { - FESA_CHECK_NEAR(result.local_without_drilling(i, j), result.local_without_drilling(j, i), 1.0e-8); - FESA_CHECK_NEAR(result.local_with_drilling(i, j), result.local_with_drilling(j, i), 1.0e-8); - FESA_CHECK_NEAR(result.global(i, j), result.global(j, i), 1.0e-8); - } - } -} - -FESA_TEST(mitc4_drilling_stiffness_uses_minimum_positive_physical_local_diagonal) { - const std::array coords = {{{0, 0, 0}, {1, 0, 0}, {1, 1, 0}, {0, 1, 0}}}; - const auto result = fesa::mitc4ElementStiffness(coords, 1000.0, 0.25, 0.2); - FESA_CHECK(result.ok()); - fesa::Real expected_reference = std::numeric_limits::infinity(); - for (fesa::LocalIndex node = 0; node < 4; ++node) { - for (fesa::LocalIndex local_dof = 0; local_dof < 5; ++local_dof) { - const fesa::Real diagonal = result.local_without_drilling(6 * node + local_dof, 6 * node + local_dof); - if (std::isfinite(diagonal) && diagonal > 0.0) { - expected_reference = std::min(expected_reference, diagonal); - } - } - } - FESA_CHECK(std::isfinite(expected_reference)); - FESA_CHECK_NEAR(result.drilling_reference_diagonal, expected_reference, 1.0e-10); - FESA_CHECK_NEAR(result.drilling_stiffness_scale, 1.0e-3, 1.0e-15); - FESA_CHECK_NEAR(result.drilling_stiffness, 1.0e-3 * expected_reference, 1.0e-10); - for (fesa::LocalIndex node = 0; node < 4; ++node) { - const fesa::LocalIndex gamma = 6 * node + 5; - FESA_CHECK_NEAR(result.local_with_drilling(gamma, gamma) - result.local_without_drilling(gamma, gamma), - result.drilling_stiffness, 1.0e-12); - } - - fesa::ElementStiffnessOptions options; - options.drilling_stiffness_scale = 2.0e-3; - const auto scaled = fesa::mitc4ElementStiffness(coords, 1000.0, 0.25, 0.2, options); - FESA_CHECK(scaled.ok()); - FESA_CHECK_NEAR(scaled.drilling_reference_diagonal, result.drilling_reference_diagonal, 1.0e-10); - FESA_CHECK_NEAR(scaled.drilling_stiffness, 2.0 * result.drilling_stiffness, 1.0e-10); -} - -FESA_TEST(mitc4_drilling_reports_invalid_scale_and_missing_reference_diagonal) { - fesa::DenseMatrix zero_physical(24, 24); - auto missing_reference = fesa::mitc4ApplyDrillingStabilization(zero_physical, 1.0e-3); - FESA_CHECK(!missing_reference.ok()); - FESA_CHECK(fesa::containsDiagnostic(missing_reference.diagnostics, "FESA-MITC4-DRILLING-REFERENCE")); - - auto invalid_scale = fesa::mitc4ApplyDrillingStabilization(zero_physical, -1.0); - FESA_CHECK(!invalid_scale.ok()); - FESA_CHECK(fesa::containsDiagnostic(invalid_scale.diagnostics, "FESA-MITC4-DRILLING-SCALE")); -} - -FESA_TEST(mitc4_rigid_translation_energy_is_zero_and_drilling_energy_is_documented) { - const std::array coords = {{{0, 0, 0}, {1, 0, 0}, {1, 1, 0}, {0, 1, 0}}}; - const auto result = fesa::mitc4ElementStiffness(coords, 1000.0, 0.25, 0.2); - FESA_CHECK(result.ok()); - - auto translate_x = zeroElementVector(); - auto translate_y = zeroElementVector(); - auto translate_z = zeroElementVector(); - for (int node = 0; node < 4; ++node) { - translate_x[static_cast(6 * node + 0)] = 1.0; - translate_y[static_cast(6 * node + 1)] = 1.0; - translate_z[static_cast(6 * node + 2)] = 1.0; - } - FESA_CHECK(std::fabs(quadraticEnergy(result.global, translate_x)) < 1.0e-8); - FESA_CHECK(std::fabs(quadraticEnergy(result.global, translate_y)) < 1.0e-8); - FESA_CHECK(std::fabs(quadraticEnergy(result.global, translate_z)) < 1.0e-8); - - auto drilling = zeroElementVector(); - for (int node = 0; node < 4; ++node) { - drilling[static_cast(6 * node + 5)] = 1.0; - } - FESA_CHECK_NEAR(quadraticEnergy(result.global, drilling), 4.0 * result.drilling_stiffness, 1.0e-8); -} - -FESA_TEST(mitc4_constant_membrane_patch_strain_is_uniform) { - const auto coords = flatUnitSquareCoordinates(); - const auto geometry = fesa::buildMITC4Geometry(coords, 0.2); - FESA_CHECK(geometry.ok()); - const fesa::Real eps_x = 0.010; - const fesa::Real eps_y = -0.004; - const fesa::Real gamma_xy = 0.006; - const auto dofs = elementDofsFromFields( - coords, - [&](const fesa::Vec3& p) { - return fesa::Vec3{eps_x * p.x + 0.5 * gamma_xy * p.y, - eps_y * p.y + 0.5 * gamma_xy * p.x, - 0.0}; - }, - [](const fesa::Vec3&) { return fesa::Vec3{}; }); - - for (const auto& sample : fesa::mitc4GaussQuadrature2x2x2()) { - const auto strain = localStrainAt(geometry, dofs, sample.xi, sample.eta, sample.zeta); - FESA_CHECK_NEAR(strain[0], eps_x, 1.0e-12); - FESA_CHECK_NEAR(strain[1], eps_y, 1.0e-12); - FESA_CHECK_NEAR(strain[2], 0.0, 1.0e-12); - FESA_CHECK_NEAR(strain[3], 0.0, 1.0e-12); - FESA_CHECK_NEAR(strain[4], 0.0, 1.0e-12); - FESA_CHECK_NEAR(strain[5], gamma_xy, 1.0e-12); - } - - const auto stiffness = fesa::mitc4ElementStiffness(coords, 210000.0, 0.30, 0.2); - FESA_CHECK(stiffness.ok()); - const auto energy = quadraticEnergy(stiffness.local_without_drilling, toVector(dofs)); - FESA_CHECK(energy > 0.0); -} - -FESA_TEST(mitc4_pure_bending_patch_is_membrane_free_and_thickness_antisymmetric) { - const auto coords = flatUnitSquareCoordinates(); - const fesa::Real thickness = 0.2; - const auto geometry = fesa::buildMITC4Geometry(coords, thickness); - FESA_CHECK(geometry.ok()); - const fesa::Real curvature_x = 0.025; - const auto dofs = elementDofsFromFields( - coords, - [](const fesa::Vec3&) { return fesa::Vec3{}; }, - [&](const fesa::Vec3& p) { return fesa::Vec3{0.0, curvature_x * p.x, 0.0}; }); - - const fesa::Real xi = -0.30; - const fesa::Real eta = 0.45; - const fesa::Real zeta = 1.0 / std::sqrt(3.0); - const auto top = localStrainAt(geometry, dofs, xi, eta, zeta); - const auto mid = localStrainAt(geometry, dofs, xi, eta, 0.0); - const auto bottom = localStrainAt(geometry, dofs, xi, eta, -zeta); - - FESA_CHECK(std::fabs(top[0]) > 1.0e-6); - FESA_CHECK_NEAR(top[0] + bottom[0], 0.0, 1.0e-12); - FESA_CHECK_NEAR(mid[0], 0.0, 1.0e-12); - FESA_CHECK_NEAR(top[1] + bottom[1], 0.0, 1.0e-12); - FESA_CHECK_NEAR(mid[1], 0.0, 1.0e-12); - - const auto stiffness = fesa::mitc4ElementStiffness(coords, 210000.0, 0.30, thickness); - FESA_CHECK(stiffness.ok()); - const auto energy = quadraticEnergy(stiffness.local_without_drilling, toVector(dofs)); - FESA_CHECK(energy > 0.0); -} - -FESA_TEST(mitc4_pure_transverse_shear_patch_is_constant) { - const auto coords = flatUnitSquareCoordinates(); - const auto geometry = fesa::buildMITC4Geometry(coords, 0.2); - FESA_CHECK(geometry.ok()); - const fesa::Real gamma_xz = 0.012; - const auto dofs = elementDofsFromFields( - coords, - [&](const fesa::Vec3& p) { return fesa::Vec3{0.0, 0.0, gamma_xz * p.x}; }, - [](const fesa::Vec3&) { return fesa::Vec3{}; }); - - for (const auto& sample : fesa::mitc4GaussQuadrature2x2x2()) { - const auto strain = localStrainAt(geometry, dofs, sample.xi, sample.eta, sample.zeta); - FESA_CHECK_NEAR(strain[0], 0.0, 1.0e-12); - FESA_CHECK_NEAR(strain[1], 0.0, 1.0e-12); - FESA_CHECK_NEAR(strain[2], 0.0, 1.0e-12); - FESA_CHECK_NEAR(strain[3], 0.0, 1.0e-12); - FESA_CHECK_NEAR(strain[4], gamma_xz, 1.0e-12); - FESA_CHECK_NEAR(strain[5], 0.0, 1.0e-12); - } -} - -FESA_TEST(mitc4_pure_twist_patch_reproduces_bilinear_transverse_shear) { - const auto coords = flatUnitSquareCoordinates(); - const auto geometry = fesa::buildMITC4Geometry(coords, 0.2); - FESA_CHECK(geometry.ok()); - const fesa::Real twist = 0.018; - const auto dofs = elementDofsFromFields( - coords, - [&](const fesa::Vec3& p) { return fesa::Vec3{0.0, 0.0, twist * p.x * p.y}; }, - [](const fesa::Vec3&) { return fesa::Vec3{}; }); - - for (const auto& sample : fesa::mitc4GaussQuadrature2x2x2()) { - const auto x = 0.5 * (sample.xi + 1.0); - const auto y = 0.5 * (sample.eta + 1.0); - const auto strain = localStrainAt(geometry, dofs, sample.xi, sample.eta, sample.zeta); - FESA_CHECK_NEAR(strain[0], 0.0, 1.0e-12); - FESA_CHECK_NEAR(strain[1], 0.0, 1.0e-12); - FESA_CHECK_NEAR(strain[2], 0.0, 1.0e-12); - FESA_CHECK_NEAR(strain[3], twist * x, 1.0e-12); - FESA_CHECK_NEAR(strain[4], twist * y, 1.0e-12); - FESA_CHECK_NEAR(strain[5], 0.0, 1.0e-12); - } -} - -FESA_TEST(mitc4_membrane_patch_energy_is_not_hidden_by_drilling_scale) { - const auto coords = flatUnitSquareCoordinates(); - const auto dofs = elementDofsFromFields( - coords, - [](const fesa::Vec3& p) { return fesa::Vec3{0.003 * p.x, -0.002 * p.y, 0.0}; }, - [](const fesa::Vec3&) { return fesa::Vec3{}; }); - - fesa::ElementStiffnessOptions no_drilling; - no_drilling.drilling_stiffness_scale = 0.0; - fesa::ElementStiffnessOptions strong_drilling; - strong_drilling.drilling_stiffness_scale = 1.0e-1; - const auto physical = fesa::mitc4ElementStiffness(coords, 210000.0, 0.30, 0.2, no_drilling); - const auto stabilized = fesa::mitc4ElementStiffness(coords, 210000.0, 0.30, 0.2, strong_drilling); - FESA_CHECK(physical.ok()); - FESA_CHECK(stabilized.ok()); - - const auto physical_energy = quadraticEnergy(physical.local_with_drilling, toVector(dofs)); - const auto stabilized_energy = quadraticEnergy(stabilized.local_with_drilling, toVector(dofs)); - FESA_CHECK(physical_energy > 0.0); - FESA_CHECK_NEAR(stabilized_energy, physical_energy, physical_energy * 1.0e-12); -} - -FESA_TEST(mitc4_single_element_cantilever_is_thickness_sensitive) { - const auto thick_tip = singleElementCantileverTipUz(0.20); - const auto thin_tip = singleElementCantileverTipUz(0.10); - FESA_CHECK(thick_tip < 0.0); - FESA_CHECK(thin_tip < thick_tip); - const auto response_ratio = std::fabs(thin_tip / thick_tip); - FESA_CHECK(response_ratio > 2.0); -} - -FESA_TEST(mitc4_internal_force_is_stiffness_times_displacement_for_linear_phase1) { - const std::array coords = {{{0, 0, 0}, {1, 0, 0}, {1, 1, 0}, {0, 1, 0}}}; - const auto result = fesa::mitc4ElementStiffness(coords, 1000.0, 0.25, 0.2); - FESA_CHECK(result.ok()); - std::vector displacement(24, 0.0); - for (std::size_t i = 0; i < displacement.size(); ++i) { - displacement[i] = 0.001 * static_cast(i + 1); - } - const auto expected = result.global.multiply(displacement); - const auto actual = fesa::mitc4ElementInternalForce(result, displacement); - FESA_CHECK(actual.size() == expected.size()); - for (std::size_t i = 0; i < expected.size(); ++i) { - FESA_CHECK_NEAR(actual[i], expected[i], 1.0e-12); - } -} - -FESA_TEST(mitc4_shape_functions_and_stiffness_baseline) { - auto shape = fesa::shapeFunctions(0.25, -0.5); - const fesa::Real sum = shape.n[0] + shape.n[1] + shape.n[2] + shape.n[3]; - FESA_CHECK_NEAR(sum, 1.0, 1.0e-15); - - const std::array coords = {{{0, 0, 0}, {1, 0, 0}, {1, 1, 0}, {0, 1, 0}}}; - fesa::MITC4ElementKernel kernel; - auto k = kernel.stiffness(coords, 1000.0, 0.3, 0.1); - FESA_CHECK(k.rows() == 24); - FESA_CHECK(k.cols() == 24); - for (fesa::LocalIndex i = 0; i < 24; ++i) { - for (fesa::LocalIndex j = 0; j < 24; ++j) { - FESA_CHECK_NEAR(k(i, j), k(j, i), 1.0e-8); - } - } - - std::vector uniform_translation(24, 0.0); - for (int node = 0; node < 4; ++node) { - uniform_translation[static_cast(6 * node + 0)] = 1.0; - } - auto internal = k.multiply(uniform_translation); - fesa::Real norm = 0.0; - for (auto value : internal) { - norm += std::fabs(value); - } - FESA_CHECK(norm < 1.0e-8); -} - -FESA_TEST(linear_static_analysis_solves_u_and_recovers_full_vector_rf) { - auto domain = parsedPhase1Domain(); - fesa::LinearStaticAnalysis analysis; - auto result = analysis.run(domain); - FESA_CHECK(result.ok()); - FESA_CHECK(result.state.converged); - FESA_CHECK(result.state.f_internal_full.size() == result.state.u_full.size()); - FESA_CHECK(result.state.f_external_full.size() == result.state.u_full.size()); - FESA_CHECK(result.state.reaction_full.size() == result.state.u_full.size()); - FESA_CHECK(result.result_file.steps.size() == 1); - const auto& frame = result.result_file.steps[0].frames[0]; - FESA_CHECK(frame.field_outputs.count("U") == 1); - FESA_CHECK(frame.field_outputs.count("RF") == 1); - fesa::Real total_rf_z = 0.0; - fesa::DofManager dofs(domain); - for (auto node_id : dofs.nodeIds()) { - total_rf_z += result.state.reaction_full[static_cast(dofs.fullIndex(node_id, fesa::Dof::UZ))]; - } - FESA_CHECK_NEAR(total_rf_z, 2.0, 1.0e-8); -} - -FESA_TEST(linear_static_input_workflow_produces_schema_ready_u_and_rf_fields) { - const auto result = fesa::runLinearStaticInputString(phase1Input(), "phase1-workflow.inp"); - FESA_CHECK(result.ok()); - FESA_CHECK(result.model.ok()); - FESA_CHECK(result.model.step.name == "Step-1"); - FESA_CHECK(result.model.active_element_ids == std::vector({1})); - FESA_CHECK(result.state.converged); - FESA_CHECK(result.state.u_full.size() == 24); - FESA_CHECK(result.state.f_external_full.size() == 24); - FESA_CHECK(result.state.f_internal_full.size() == 24); - FESA_CHECK(result.state.reaction_full.size() == 24); - - const auto& result_file = result.result_file; - FESA_CHECK(result_file.schema_name == "FESA_RESULTS"); - FESA_CHECK(result_file.schema_version == 1); - FESA_CHECK(result_file.solver_name == "FESA"); - FESA_CHECK(result_file.dof_convention == "UX,UY,UZ,RX,RY,RZ"); - FESA_CHECK(result_file.sign_convention == "Abaqus-compatible"); - FESA_CHECK(result_file.precision == "double"); - FESA_CHECK(result_file.index_type == "int64"); - FESA_CHECK(result_file.node_ids == std::vector({1, 2, 3, 4})); - FESA_CHECK(result_file.element_ids == std::vector({1})); - FESA_CHECK(result_file.element_types == std::vector({"MITC4"})); - FESA_CHECK(result_file.connectivity.front() == (std::array{1, 2, 3, 4})); - FESA_CHECK(result_file.steps.size() == 1); - FESA_CHECK(result_file.steps[0].name == "Step-1"); - FESA_CHECK(result_file.steps[0].frames.size() == 1); - - const auto& frame = result_file.steps[0].frames[0]; - FESA_CHECK(frame.frame_id == 0); - FESA_CHECK(frame.increment == 1); - FESA_CHECK(frame.iteration == 0); - FESA_CHECK(frame.converged); - FESA_CHECK_NEAR(frame.step_time, 1.0, 1.0e-15); - FESA_CHECK_NEAR(frame.total_time, 1.0, 1.0e-15); - FESA_CHECK(frame.field_outputs.count("U") == 1); - FESA_CHECK(frame.field_outputs.count("RF") == 1); - const auto& u = frame.field_outputs.at("U"); - const auto& rf = frame.field_outputs.at("RF"); - FESA_CHECK(u.component_labels == fesa::displacementComponentLabels()); - FESA_CHECK(rf.component_labels == fesa::reactionComponentLabels()); - FESA_CHECK(u.entity_ids == result_file.node_ids); - FESA_CHECK(rf.entity_ids == result_file.node_ids); - FESA_CHECK(u.values.size() == result_file.node_ids.size()); - FESA_CHECK(rf.values.size() == result_file.node_ids.size()); - - fesa::Real total_rf_z = 0.0; - for (const auto& values : rf.values) { - total_rf_z += values[2]; - } - FESA_CHECK_NEAR(total_rf_z, 2.0, 1.0e-8); - FESA_CHECK_NEAR(u.values[0][2], 0.0, 1.0e-15); - FESA_CHECK_NEAR(u.values[3][2], 0.0, 1.0e-15); -} - -FESA_TEST(linear_static_input_workflow_routes_parse_errors) { - const std::string text = R"inp( -*Part, name=P1 -*Node -1, 0, 0, 0 -*Element, type=S4R -1, 1, 2, 3, 4 -*Step, nlgeom=YES -*Static -*End Step -)inp"; - const auto result = fesa::runLinearStaticInputString(text, "unsupported-workflow.inp"); - FESA_CHECK(!result.ok()); - FESA_CHECK(!result.state.converged); - FESA_CHECK(fesa::containsDiagnostic(result.diagnostics, "FESA-PARSE-UNSUPPORTED-KEYWORD")); - FESA_CHECK(fesa::containsDiagnostic(result.diagnostics, "FESA-PARSE-UNSUPPORTED-ELEMENT")); - FESA_CHECK(fesa::containsDiagnostic(result.diagnostics, "FESA-PARSE-UNSUPPORTED-NLGEOM")); -} - -FESA_TEST(linear_static_input_workflow_routes_validation_errors_without_bypassing_validator) { - const std::string text = R"inp( -*Node -1, 0, 0, 0 -2, 1, 0, 0 -3, 1, 1, 0 -4, 0, 1, 0 -*Element, type=S4 -1, 1, 2, 3, 4 -*Nset, nset=FIXED -1, 4 -*Boundary -FIXED, 1, 6 -*Cload -2, 3, -1 -*Step, name=Step-1 -*Static -*End Step -)inp"; - const auto result = fesa::runLinearStaticInputString(text, "missing-property-workflow.inp"); - FESA_CHECK(!result.ok()); - FESA_CHECK(!result.state.converged); - FESA_CHECK(fesa::containsDiagnostic(result.diagnostics, "FESA-VALIDATION-MISSING-PROPERTY")); -} - -FESA_TEST(linear_static_analysis_uses_solver_adapter_and_reconstructs_full_vectors) { - auto domain = parsedPhase1Domain(); - RecordingSolver solver({0.25, -0.50}); - fesa::LinearStaticAnalysis analysis(&solver); - const auto result = analysis.run(domain); - FESA_CHECK(result.ok()); - FESA_CHECK(solver.called); - FESA_CHECK(solver.captured_a.rows() == 2); - FESA_CHECK(solver.captured_a.cols() == 2); - FESA_CHECK(solver.captured_b.size() == 2); - FESA_CHECK_NEAR(solver.captured_b[0], -1.0, 1.0e-15); - FESA_CHECK_NEAR(solver.captured_b[1], -1.0, 1.0e-15); - - fesa::DofManager dofs(domain); - FESA_CHECK_NEAR(result.state.u_full[static_cast(dofs.fullIndex(2, fesa::Dof::UZ))], 0.25, 1.0e-15); - FESA_CHECK_NEAR(result.state.u_full[static_cast(dofs.fullIndex(3, fesa::Dof::UZ))], -0.50, 1.0e-15); - FESA_CHECK_NEAR(result.state.u_full[static_cast(dofs.fullIndex(1, fesa::Dof::UZ))], 0.0, 1.0e-15); - for (std::size_t i = 0; i < result.state.reaction_full.size(); ++i) { - FESA_CHECK_NEAR(result.state.reaction_full[i], - result.state.f_internal_full[i] - result.state.f_external_full[i], 1.0e-10); - } -} - -FESA_TEST(linear_static_analysis_propagates_solver_singular_diagnostic) { - auto domain = parsedPhase1Domain(); - FailingSolver solver; - fesa::LinearStaticAnalysis analysis(&solver); - const auto result = analysis.run(domain); - FESA_CHECK(!result.ok()); - FESA_CHECK(!result.state.converged); - FESA_CHECK(fesa::containsDiagnostic(result.diagnostics, "FESA-SINGULAR-SOLVER")); -} - -int main() { - int failed = 0; - for (const auto& test : registry()) { - try { - test.fn(); - std::cout << "[PASS] " << test.name << '\n'; - } catch (const std::exception& error) { - ++failed; - std::cerr << "[FAIL] " << test.name << ": " << error.what() << '\n'; - } - } - if (failed != 0) { - std::cerr << failed << " test(s) failed\n"; - return 1; - } - std::cout << registry().size() << " test(s) passed\n"; - return 0; -} diff --git a/tests/test_math_module_includes.cpp b/tests/test_math_module_includes.cpp deleted file mode 100644 index 307e80c..0000000 --- a/tests/test_math_module_includes.cpp +++ /dev/null @@ -1,71 +0,0 @@ -#include "fesa/Math/Math.hpp" - -#include -#include -#include -#include -#include -#include - -namespace { - -void check(bool value, const char* message) { - if (!value) { - throw std::runtime_error(message); - } -} - -void checkNear(fesa::Real actual, fesa::Real expected, fesa::Real tolerance, const char* message) { - if (std::fabs(actual - expected) > tolerance) { - throw std::runtime_error(message); - } -} - -} // namespace - -int main() { - static_assert(std::is_same_v, "SparseIndex must remain int64"); - static_assert(std::is_same_v, "EquationId must remain int64"); - - const fesa::Vec3 x{1.0, 0.0, 0.0}; - const fesa::Vec3 y{0.0, 1.0, 0.0}; - const auto z = fesa::cross(x, y); - checkNear(fesa::dot(x, y), 0.0, 1.0e-12, "Vector dot product changed"); - checkNear(z.z, 1.0, 1.0e-12, "Vector cross product changed"); - checkNear(fesa::norm(fesa::Vec3{3.0, 4.0, 0.0}), 5.0, 1.0e-12, "Vector norm changed"); - check(fesa::normalizedIfValid(fesa::Vec3{0.0, 0.0, 0.0}) == std::nullopt, "Invalid normalization changed"); - - fesa::SparsePattern pattern; - pattern.equation_count = 3; - pattern.entries.push_back({0, 0}); - pattern.entries.push_back({0, 2}); - check(pattern.nonzeroCount() == 2, "Sparse nonzero count changed"); - check(pattern.contains(0, 2), "Sparse pattern lookup changed"); - check(!pattern.contains(2, 0), "Sparse pattern lookup became nondirectional"); - - fesa::DenseMatrix matrix(2, 2); - matrix(0, 0) = 2.0; - matrix(0, 1) = 1.0; - matrix(1, 0) = 1.0; - matrix(1, 1) = 3.0; - const auto product = matrix.multiply({1.0, 2.0}); - checkNear(product[0], 4.0, 1.0e-12, "DenseMatrix multiply row 0 changed"); - checkNear(product[1], 7.0, 1.0e-12, "DenseMatrix multiply row 1 changed"); - - const fesa::LinearSolver& solver = fesa::GaussianEliminationSolver{}; - const auto solved = solver.solve(matrix, {1.0, 2.0}); - check(solved.ok(), "Gaussian solver unexpectedly failed"); - checkNear(solved.x[0], 0.2, 1.0e-12, "Gaussian solver x0 changed"); - checkNear(solved.x[1], 0.6, 1.0e-12, "Gaussian solver x1 changed"); - - fesa::DenseMatrix singular(2, 2); - singular(0, 0) = 1.0; - singular(0, 1) = 2.0; - singular(1, 0) = 2.0; - singular(1, 1) = 4.0; - const auto failed = solver.solve(singular, {1.0, 2.0}); - check(!failed.ok(), "Singular solve unexpectedly passed"); - check(fesa::containsDiagnostic(failed.diagnostics, "FESA-SINGULAR-SOLVER"), "Singular diagnostic code changed"); - - return 0; -} diff --git a/tests/test_mitc4_stiffness_module_includes.cpp b/tests/test_mitc4_stiffness_module_includes.cpp deleted file mode 100644 index b6894f9..0000000 --- a/tests/test_mitc4_stiffness_module_includes.cpp +++ /dev/null @@ -1,117 +0,0 @@ -#include "fesa/Element/Element.hpp" -#include "fesa/Element/MITC4Stiffness.hpp" -#include "fesa/Material/MITC4PlaneStressMaterial.hpp" -#include "fesa/Material/Material.hpp" - -#include -#include -#include -#include - -namespace { - -void check(bool value, const char* message) { - if (!value) { - throw std::runtime_error(message); - } -} - -void checkNear(fesa::Real actual, fesa::Real expected, fesa::Real tolerance, const char* message) { - if (std::fabs(actual - expected) > tolerance) { - throw std::runtime_error(message); - } -} - -fesa::Real matrixEnergy(const fesa::DenseMatrix& matrix, const std::vector& values) { - const auto product = matrix.multiply(values); - fesa::Real energy = 0.0; - for (std::size_t i = 0; i < values.size(); ++i) { - energy += values[i] * product[i]; - } - return energy; -} - -} // namespace - -int main() { - const auto law = fesa::mitc4PlaneStressMaterialMatrix(1000.0, 0.25); - check(law.ok(), "valid MITC4 plane-stress material should remain accepted"); - check(fesa::mitc4StrainComponentLabels()[0] == "eps11", "MITC4 strain labels changed"); - const auto eps11 = fesa::strainComponentIndex(fesa::MITC4StrainComponent::Eps11); - const auto eps22 = fesa::strainComponentIndex(fesa::MITC4StrainComponent::Eps22); - const auto gamma13 = fesa::strainComponentIndex(fesa::MITC4StrainComponent::Gamma13); - checkNear(law.matrix[eps11][eps11], 1066.6666666666667, 1.0e-10, "plane-stress D11 changed"); - checkNear(law.matrix[eps11][eps22], 266.6666666666667, 1.0e-10, "plane-stress D12 changed"); - checkNear(law.matrix[gamma13][gamma13], (5.0 / 6.0) * 400.0, 1.0e-10, - "plane-stress shear correction changed"); - - fesa::MITC4StrainVector strain{}; - strain[eps11] = 0.01; - strain[eps22] = -0.02; - strain[gamma13] = 0.03; - const auto stress = fesa::multiplyMITC4MaterialMatrix(law.matrix, strain); - check(fesa::dotMITC4Vector(strain, stress) > 0.0, "MITC4 material energy changed"); - - const auto invalid = fesa::mitc4PlaneStressMaterialMatrix(-1.0, 0.25); - check(!invalid.ok(), "invalid MITC4 material should remain diagnosed"); - check(fesa::containsDiagnostic(invalid.diagnostics, "FESA-MITC4-MATERIAL"), - "MITC4 invalid material diagnostic changed"); - - const auto points = fesa::mitc4GaussQuadrature2x2x2(); - check(points.size() == 8, "MITC4 integration point count changed"); - for (const auto& point : points) { - checkNear(point.weight, 1.0, 1.0e-15, "MITC4 integration weight changed"); - } - - const std::array coords = {{{0, 0, 0}, {1, 0, 0}, {1, 1, 0}, {0, 1, 0}}}; - const auto geometry = fesa::buildMITC4Geometry(coords, 0.2); - check(geometry.ok(), "flat MITC4 geometry should remain valid"); - const auto integration = fesa::mitc4BuildMaterialIntegrationData(geometry, 1000.0, 0.25); - check(integration.ok(), "MITC4 material integration data should remain valid"); - check(integration.samples.size() == 8, "MITC4 material integration sample count changed"); - for (const auto& sample : integration.samples) { - check(sample.ok(), "MITC4 material integration sample should remain valid"); - check(sample.basis.ok(), "MITC4 material integration basis should remain valid"); - } - - const auto stiffness = fesa::mitc4ElementStiffness(coords, 1000.0, 0.25, 0.2); - check(stiffness.ok(), "MITC4 stiffness should remain valid"); - check(stiffness.global.rows() == 24 && stiffness.global.cols() == 24, "MITC4 global stiffness size changed"); - check(stiffness.integration_point_count == 8, "MITC4 stiffness integration count changed"); - checkNear(stiffness.drilling_stiffness_scale, 1.0e-3, 1.0e-15, "MITC4 drilling scale changed"); - check(stiffness.drilling_reference_diagonal > 0.0, "MITC4 drilling reference diagonal changed"); - check(stiffness.drilling_stiffness > 0.0, "MITC4 drilling stiffness changed"); - for (fesa::LocalIndex i = 0; i < 24; ++i) { - for (fesa::LocalIndex j = 0; j < 24; ++j) { - checkNear(stiffness.global(i, j), stiffness.global(j, i), 1.0e-8, "MITC4 stiffness symmetry changed"); - } - } - - std::vector displacement(24, 0.0); - displacement[0] = 0.01; - displacement[7] = -0.02; - displacement[14] = 0.03; - displacement[21] = 0.04; - const auto internal_force = fesa::mitc4ElementInternalForce(stiffness, displacement); - check(internal_force.size() == displacement.size(), "MITC4 internal force size changed"); - const auto expected_internal_force = stiffness.global.multiply(displacement); - for (std::size_t i = 0; i < internal_force.size(); ++i) { - checkNear(internal_force[i], expected_internal_force[i], 1.0e-12, "MITC4 internal force changed"); - } - check(matrixEnergy(stiffness.global, displacement) > 0.0, "MITC4 stiffness energy changed"); - - fesa::ElementStiffnessOptions options; - options.drilling_stiffness_scale = 2.0e-3; - const auto scaled = fesa::mitc4ElementStiffness(coords, 1000.0, 0.25, 0.2, options); - check(scaled.ok(), "scaled MITC4 stiffness should remain valid"); - checkNear(scaled.drilling_reference_diagonal, stiffness.drilling_reference_diagonal, 1.0e-10, - "MITC4 drilling reference changed with scale"); - checkNear(scaled.drilling_stiffness, 2.0 * stiffness.drilling_stiffness, 1.0e-10, - "MITC4 drilling scale response changed"); - - fesa::MITC4ElementKernel kernel; - const auto kernel_stiffness = kernel.stiffness(coords, 1000.0, 0.25, 0.2); - check(kernel_stiffness.rows() == 24 && kernel_stiffness.cols() == 24, "MITC4 kernel stiffness size changed"); - - return 0; -} diff --git a/tests/test_results_module_includes.cpp b/tests/test_results_module_includes.cpp deleted file mode 100644 index e39ea8b..0000000 --- a/tests/test_results_module_includes.cpp +++ /dev/null @@ -1,137 +0,0 @@ -#include "fesa/Results/Results.hpp" - -#include -#include -#include -#include -#include - -namespace { - -void check(bool value, const char* message) { - if (!value) { - throw std::runtime_error(message); - } -} - -std::string sourceRoot() { -#ifdef FESA_SOURCE_DIR - return FESA_SOURCE_DIR; -#else - return "."; -#endif -} - -fesa::Domain makeDomain() { - fesa::Domain domain; - domain.nodes[1] = {1, {0.0, 0.0, 0.0}}; - domain.nodes[2] = {2, {1.0, 0.0, 0.0}}; - domain.nodes[3] = {3, {1.0, 1.0, 0.0}}; - domain.nodes[4] = {4, {0.0, 1.0, 0.0}}; - domain.elements[10] = {10, fesa::ElementType::MITC4, {1, 2, 3, 4}, "all_elements"}; - domain.steps.push_back({"Step-Results", "linear_static"}); - domain.boundary_conditions.push_back({"1", 1, 6, 0.0}); - return domain; -} - -std::string readTextFile(const std::string& path) { - std::ifstream input(path); - if (!input) { - throw std::runtime_error("failed to open " + path); - } - std::ostringstream buffer; - buffer << input.rdbuf(); - return buffer.str(); -} - -} // namespace - -int main() { - const auto domain = makeDomain(); - const fesa::DofManager dofs(domain); - std::vector u(static_cast(dofs.fullDofCount()), 0.0); - std::vector rf(static_cast(dofs.fullDofCount()), 0.0); - u[static_cast(dofs.fullIndex(2, fesa::Dof::UZ))] = -0.25; - rf[static_cast(dofs.fullIndex(1, fesa::Dof::UZ))] = 3.0; - - fesa::InMemoryResultsWriter writer; - writer.writeLinearStatic(domain, dofs, u, rf); - const auto& result = writer.result(); - check(result.schema_name == "FESA_RESULTS", "schema name changed"); - check(result.schema_version == 1, "schema version changed"); - check(result.solver_name == "FESA", "solver name changed"); - check(result.dof_convention == "UX,UY,UZ,RX,RY,RZ", "DOF convention changed"); - check(result.sign_convention == "Abaqus-compatible", "sign convention changed"); - check(result.precision == "double", "precision metadata changed"); - check(result.index_type == "int64", "index metadata changed"); - check(result.node_ids.size() == domain.nodes.size(), "node model mirror changed"); - check(result.element_types == std::vector({"MITC4"}), "element type label changed"); - check(result.steps.size() == 1, "result step count changed"); - check(result.steps[0].name == "Step-Results", "step name changed"); - check(result.steps[0].frames.size() == 1, "result frame count changed"); - - const auto& frame = result.steps[0].frames[0]; - check(frame.frame_id == 0, "Phase 1 frame id changed"); - check(frame.increment == 1, "Phase 1 increment changed"); - check(frame.iteration == 0, "Phase 1 iteration changed"); - check(frame.converged, "Phase 1 convergence flag changed"); - check(frame.field_outputs.count("U") == 1, "U field missing"); - check(frame.field_outputs.count("RF") == 1, "RF field missing"); - const auto& u_field = frame.field_outputs.at("U"); - const auto& rf_field = frame.field_outputs.at("RF"); - check(u_field.position == "NODAL" && u_field.entity_type == "node" && u_field.basis == "GLOBAL", - "U field metadata changed"); - check(rf_field.position == "NODAL" && rf_field.entity_type == "node" && rf_field.basis == "GLOBAL", - "RF field metadata changed"); - check(u_field.component_labels == fesa::displacementComponentLabels(), "U component labels changed"); - check(rf_field.component_labels == fesa::reactionComponentLabels(), "RF component labels changed"); - - const auto required_columns = fesa::displacementCsvRequiredColumns(); - check(required_columns.size() == 7, "required displacement CSV column count changed"); - check(required_columns.front() == "Node Label", "CSV node label column changed"); - check(required_columns.back() == "UR-UR3", "CSV rotation column changed"); - - const auto required_reaction_columns = fesa::reactionCsvRequiredColumns(); - check(required_reaction_columns.size() == 7, "required reaction CSV column count changed"); - check(required_reaction_columns.front() == "Node Label", "reaction CSV node label column changed"); - check(required_reaction_columns.back() == "RM-RM3", "reaction CSV moment column changed"); - - const auto missing_header = fesa::loadDisplacementCsvFromString("Node Label,U-U1,U-U2,U-U3,UR-UR1,UR-UR2\n" - "1,0,0,0,0,0\n", - "missing-header.csv"); - check(fesa::containsDiagnostic(missing_header.diagnostics, "FESA-CSV-MISSING-COLUMN"), - "missing CSV header diagnostic changed"); - - const auto duplicate_node = fesa::loadDisplacementCsvFromString("Node Label,U-U1,U-U2,U-U3,UR-UR1,UR-UR2,UR-UR3\n" - "1,0,0,0,0,0,0\n" - "1,0,0,0,0,0,0\n", - "duplicate-node.csv"); - check(fesa::containsDiagnostic(duplicate_node.diagnostics, "FESA-CSV-DUPLICATE-NODE"), - "duplicate CSV node diagnostic changed"); - - fesa::CsvDisplacementTable expected; - expected.rows[1] = {1, {0, 0, 0, 0, 0, 0}}; - expected.rows[2] = {2, {0, 0, -0.25, 0, 0, 0}}; - expected.rows[3] = {3, {0, 0, 0, 0, 0, 0}}; - expected.rows[4] = {4, {0, 0, 0, 0, 0, 0}}; - const auto comparison = fesa::compareDisplacements(u_field, expected, {1.0e-12, 1.0e-12, 1.0}); - check(comparison.pass, "displacement comparator no longer matches by node id"); - - fesa::CsvReactionTable expected_reactions; - expected_reactions.rows[1] = {1, {0, 0, 3.0, 0, 0, 0}}; - expected_reactions.rows[2] = {2, {0, 0, 0, 0, 0, 0}}; - expected_reactions.rows[3] = {3, {0, 0, 0, 0, 0, 0}}; - expected_reactions.rows[4] = {4, {0, 0, 0, 0, 0, 0}}; - const auto reaction_comparison = fesa::compareReactions(rf_field, expected_reactions, {1.0e-12, 1.0e-12, 1.0}); - check(reaction_comparison.pass, "reaction comparator no longer matches by node id"); - - const auto quad02 = fesa::loadDisplacementCsv(sourceRoot() + "/references/quad_02_displacements.csv"); - check(!fesa::hasError(quad02.diagnostics), "quad_02 displacement CSV no longer loads"); - check(quad02.rows.size() == 121, "quad_02 displacement CSV row count changed"); - - const auto quad02_reactions = fesa::loadReactionCsv(sourceRoot() + "/references/quad_02_reactionforces.csv"); - check(!fesa::hasError(quad02_reactions.diagnostics), "quad_02 reaction CSV no longer loads"); - check(quad02_reactions.rows.size() == 121, "quad_02 reaction CSV row count changed"); - - return 0; -}