# FESA Phase 1 Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Build and internally qualify a C++20/MSVC linear-static finite-element solver that reads the agreed Abaqus `.inp` subset, solves 2-node 3D Timoshenko Beam models, and writes self-contained HDF5 results. **Architecture:** Implement one end-to-end vertical slice first, then complete the input, numerical, parallel, result, and reference-verification contracts behind the module boundaries in `docs/ARCHITECTURE.md`. Keep the semantic model independent of Abaqus syntax and isolate oneMKL, oneTBB, and HDF5 behind adapters. **Tech Stack:** C++20, Visual Studio 2022 MSVC v143 x64, CMake/CMake Presets/CTest, GoogleTest/GoogleMock, Intel oneAPI MKL PARDISO, Intel oneAPI TBB, HDF5 C API, Python 3 Harness. ## Global Constraints - Only the Phase 1 scope in `docs/PRD.md` may be implemented. - Public headers live under `include/fesa/`; implementations live under `src/fesa/`; tests live under `tests/`. - Write a failing test before every production behavior change. - Do not add a fake stiffness matrix or a test-only solver path. - Do not introduce empty future modules, a generic registry, MPC, iterative solvers, nonlinear state, or additional element types. - FESA performs no unit conversion. - Abaqus inputs may use a flat mesh or one untransformed Part/Assembly/Instance. - When transverse shear stiffness is omitted, use \(A_{sy}=A_{sz}=5A/6\) and `SCF=0`. - Reference comparison requests name their quantities and CSV paths; no per-model metadata file is required. - A pipeline milestone is not a numerically qualified release. - No new MSVC warnings are allowed. - The existing Harness contract in `docs/HARNESS.md` and `.agents/skills/harness/SKILL.md` remains unchanged. ## Environment Audit The planning environment currently has: - CMake 4.4.0 - oneMKL CMake package at `C:/Program Files (x86)/Intel/oneAPI/2026.1/lib/cmake/mkl` - oneTBB CMake package at `C:/Program Files (x86)/Intel/oneAPI/2026.1/lib/cmake/tbb` - no HDF5 or GoogleTest CMake package found in the standard Program Files trees - `MSBuild.exe` not currently available on `PATH` Task 1 must stop as `blocked` rather than downloading packages if HDF5, GoogleTest, or the MSVC toolchain is still unavailable. ## Required Research Record Before the related production task begins, record the relevant equations, assumptions, API contracts, and FESA decisions from these sources: - K. J. Bathe, *Finite Element Procedures*, 2nd edition: finite-element discretization, assembly, constraints, and verification. - T. J. R. Hughes, *The Finite Element Method: Linear Static and Dynamic Finite Element Analysis*: variational formulation and numerical integration. - K. J. Bathe and S. Bolourchi, “Large Displacement Analysis of Three-Dimensional Beam Structures,” 1979: three-dimensional isoparametric Beam coordinates and transformations. Phase 1 uses only the linearized subset. - T. J. R. Hughes, R. L. Taylor, and W. Kanoknukulchai, “A Simple and Efficient Finite Element for Plate Bending,” 1977: selective reduced integration rationale. Do not copy its plate kinematics into the Beam formulation. - [Abaqus 2024—Choosing a Beam Element](https://docs.software.vt.edu/abaqusv2024/English/SIMACAEELMRefMap/simaelm-c-beamelem.htm): B31 shear-flexible behavior and slenderness compensation. - [Abaqus 2024—BEAM GENERAL SECTION](https://docs.software.vt.edu/abaqusv2024/English/SIMACAEKEYRefMap/simakey-r-beamgeneralsection.htm): supported general-section data and orientation. - [Intel oneMKL PARDISO reference](https://www.intel.com/content/www/us/en/docs/onemkl/developer-reference-c/2025-0/pardiso.html): matrix type, CSR indexing, phases, checks, and error codes. - [oneTBB reduction guide](https://uxlfoundation.github.io/oneTBB/main/tbb_userguide/design_patterns/Reduction.html): deterministic floating-point reduction. - [HDF5 data model](https://support.hdfgroup.org/documentation/hdf5/latest/_intro_h_d_f5.html): groups, datasets, dataspaces, and attributes. - [CMake FindHDF5](https://cmake.org/cmake/help/latest/module/FindHDF5.html): installed C-library discovery and imported targets. `docs/formulation/timoshenko-beam-3d.md`, `docs/HDF5_SCHEMA.md`, and `docs/VALIDATION.md` must cite the applicable source and state where FESA intentionally differs. ## Harness Phase Map | Order | Harness phase | Plan tasks | Independent deliverable | | ---: | --- | --- | --- | | 0 | `solver-bootstrap` | 1-2 | Reproducible C++20 build, dependency smoke tests, core IDs and diagnostics | | 1 | `domain-and-input-skeleton` | 3-4 | Flat or single-Instance B31 input becomes an immutable normalized `Domain` | | 2 | `fem-and-beam-kernel` | 5-6 | Real Timoshenko Beam local stiffness with analytical sanity tests | | 3 | `equation-and-linear-solve` | 7-8 | Deterministic serial CSR system solved by PARDISO | | 4 | `results-and-pipeline` | 9-10 | CLI runs one deck end-to-end and writes readable HDF5 | | 5 | `abaqus-subset-completion` | 11 | Full agreed scoped keyword, set, material, section, load, and BC subset | | 6 | `deterministic-parallel-assembly` | 12 | oneTBB assembly matches serial output across thread counts | | 7 | `result-contract-completion` | 13 | Complete self-contained HDF5 schema and Beam result recovery | | 8 | `beam-reference-qualification` | 14 | Analytical suite and available Abaqus displacement/reaction data pass tolerance | | 9 | `internal-release` | 15 | Debug/Release validation, 100k-DOF benchmark, install tree, reports | Create `phases/index.json`, phase indexes, and step files only after this plan and its phase split are approved. ## Planned File Map ```text CMakeLists.txt root targets and project policies CMakePresets.json windows-debug/windows-release workflows cmake/FesaDependencies.cmake installed dependency discovery .harness/config.json Harness CMake preset selection include/fesa/core/ IDs, vectors, source locations, diagnostics include/fesa/model/ immutable semantic entities and Domain include/fesa/io/abaqus/ deck records, parser, semantic mapper include/fesa/fem/ quadrature, shape functions, frames, DOFs include/fesa/elements/beam/ Beam3D2 input, contribution, recovery contract include/fesa/assembly/ symmetric COO/CSR and assembly include/fesa/constraints/ essential-BC elimination and reconstruction include/fesa/solvers/linear/ backend contract and PARDISO adapter include/fesa/results/ step/frame/field/diagnostic result model include/fesa/io/hdf5/ schema constants, writer, reader include/fesa/analysis/ analysis lifecycle and linear-static procedure include/fesa/validation/ comparison metrics and CSV mapping src/fesa/ implementations mirroring public modules src/fesa/cli/main.cpp thin `fesa` command-line executable tests/unit/ single-module behavior tests/integration/ public input-to-output path tests/reference/ FESA HDF5 to golden CSV comparisons tests/fixtures/ small invalid and valid decks reference/cantilever beam/ supplied Abaqus input and available golden CSV data docs/formulation/ signed-off equations and conventions docs/HDF5_SCHEMA.md versioned output contract docs/VALIDATION.md benchmark matrix and qualification result ``` --- ### Task 1: CMake, Installed Dependencies, and Test Bootstrap **Files:** - Create: `CMakeLists.txt` - Create: `CMakePresets.json` - Create: `cmake/FesaDependencies.cmake` - Create: `.harness/config.json` - Create: `include/fesa/core/version.hpp` - Create: `src/fesa/core/version.cpp` - Create: `src/fesa/cli/main.cpp` - Create: `tests/CMakeLists.txt` - Create: `tests/unit/core/version_test.cpp` - Modify: `.gitignore` **Interfaces:** - Produces: `std::string_view fesa::version() noexcept` - Produces CMake targets: `fesa_core`, `fesa_cli`, `fesa_unit_tests` - Produces presets: `windows-debug`, `windows-release` - Later tasks consume the common warning and include-directory policies. - [ ] **Step 1: Verify required installed packages without changing the machine** Run: ```powershell cmake --version Get-Command MSBuild.exe Get-ChildItem "C:\Program Files (x86)\Intel\oneAPI" -Recurse -Filter MKLConfig.cmake Get-ChildItem "C:\Program Files (x86)\Intel\oneAPI" -Recurse -Filter TBBConfig.cmake Get-ChildItem "C:\Program Files" -Recurse -Filter hdf5-config.cmake Get-ChildItem "C:\Program Files" -Recurse -Filter GTestConfig.cmake ``` Expected: MSVC, oneMKL, oneTBB, HDF5, and GoogleTest are all discoverable. If any are missing, mark the Harness step `blocked` and name the missing package; do not download it. - [ ] **Step 2: Write the failing version test** ```cpp #include #include TEST(Version, ReportsPhaseOneSemanticVersion) { EXPECT_EQ(fesa::version(), "0.1.0"); } ``` - [ ] **Step 3: Add configure files and verify the test fails before implementation** `cmake/FesaDependencies.cmake` must set the oneMKL choices before package discovery: ```cmake cmake_minimum_required(VERSION 3.30) set(MKL_LINK dynamic) set(MKL_THREADING tbb_thread) set(MKL_INTERFACE lp64) find_package(MKL CONFIG REQUIRED) find_package(TBB CONFIG REQUIRED COMPONENTS tbb) find_package(HDF5 REQUIRED COMPONENTS C) find_package(GTest CONFIG REQUIRED) ``` Link `MKL::MKL`, `TBB::tbb`, `HDF5::HDF5`, and `GTest::gtest_main` only to targets that use them. Configure and build: Compile FESA targets with `/W4 /permissive- /EHsc`; do not apply FESA warning flags to imported targets. ```powershell cmake --preset windows-debug cmake --build --preset windows-debug ``` Expected: build fails because `fesa::version()` has no definition. - [ ] **Step 4: Implement the minimum version API and thin CLI** ```cpp namespace fesa { std::string_view version() noexcept; } ``` The CLI accepts only `--version` in this task. Any other command prints usage and returns a nonzero exit code. - [ ] **Step 5: Run focused and full bootstrap validation** ```powershell cmake --build --preset windows-debug ctest --preset windows-debug --output-on-failure .\out\build\windows-debug\Debug\fesa.exe --version ``` Expected: one test passes and the CLI prints `0.1.0`. - [ ] **Step 6: Commit** ```powershell git add CMakeLists.txt CMakePresets.json cmake .harness/config.json include/fesa/core/version.hpp src/fesa/core/version.cpp src/fesa/cli/main.cpp tests/CMakeLists.txt tests/unit/core/version_test.cpp .gitignore git commit -m "build: bootstrap FESA CMake project" ``` --- ### Task 2: Core IDs, Vectors, Source Locations, and Diagnostics **Files:** - Create: `include/fesa/core/entity_id.hpp` - Create: `include/fesa/core/vec3.hpp` - Create: `include/fesa/core/source_location.hpp` - Create: `include/fesa/core/diagnostic.hpp` - Create: `tests/unit/core/entity_id_test.cpp` - Create: `tests/unit/core/vec3_test.cpp` - Create: `tests/unit/core/diagnostic_test.cpp` - Modify: `CMakeLists.txt` - Modify: `tests/CMakeLists.txt` **Interfaces:** ```cpp template class EntityId final { public: explicit constexpr EntityId(std::int64_t value); [[nodiscard]] constexpr std::int64_t value() const noexcept; auto operator<=>(const EntityId&) const = default; }; struct Vec3 final { double x; double y; double z; }; [[nodiscard]] bool is_finite(Vec3 value) noexcept; struct SourceLocation final { std::filesystem::path file; std::size_t line; std::size_t column; }; enum class DiagnosticStage { io, lexical, syntax, semantic, model, equation, solver, results }; struct Diagnostic final { DiagnosticStage stage; std::string code; std::string message; std::optional source; std::optional entity_id; }; ``` - [ ] **Step 1: Write failing tests** Cover negative/zero entity-ID rejection, typed-ID non-interchangeability at compile time, finite `Vec3` validation, and full diagnostic context preservation. - [ ] **Step 2: Run focused tests and confirm compile or assertion failure** ```powershell cmake --build --preset windows-debug ctest --preset windows-debug -R "Core" --output-on-failure ``` Expected: failure because the core types do not exist. - [ ] **Step 3: Implement only the declared core types** Use `double` for all Phase 1 real values and `std::int64_t` for external Abaqus IDs. Do not introduce a unit library, generic error monad, matrix class, or logging framework. - [ ] **Step 4: Run all tests** ```powershell cmake --build --preset windows-debug ctest --preset windows-debug --output-on-failure ``` Expected: all tests pass with no new MSVC warnings. - [ ] **Step 5: Commit** ```powershell git add include/fesa/core tests/unit/core CMakeLists.txt tests/CMakeLists.txt git commit -m "feat(core): add typed IDs and diagnostics" ``` --- ### Task 3: Immutable Semantic Domain **Files:** - Create: `include/fesa/model/ids.hpp` - Create: `include/fesa/model/entity_origin.hpp` - Create: `include/fesa/model/node.hpp` - Create: `include/fesa/model/material.hpp` - Create: `include/fesa/model/beam_section.hpp` - Create: `include/fesa/model/beam_element.hpp` - Create: `include/fesa/model/entity_set.hpp` - Create: `include/fesa/model/step_definition.hpp` - Create: `include/fesa/model/domain.hpp` - Create: `include/fesa/model/domain_builder.hpp` - Create: `src/fesa/model/domain.cpp` - Create: `src/fesa/model/domain_builder.cpp` - Create: `tests/unit/model/domain_builder_test.cpp` - Modify: root and test CMake files **Interfaces:** ```cpp using NodeId = EntityId; using ElementId = EntityId; using MaterialId = EntityId; using SectionId = EntityId; struct EntityOrigin final { std::string part_name; std::string instance_name; std::int64_t local_label; }; struct Node final { NodeId id; EntityOrigin origin; Vec3 position; }; struct IsotropicElastic final { MaterialId id; std::string name; double young; double poisson; }; struct BeamSection final { SectionId id; std::string name; double area; double iy; double iz; double torsion_j; double shear_area_y; double shear_area_z; Vec3 orientation; std::vector> recovery_points; }; struct BeamElement final { ElementId id; EntityOrigin origin; std::array nodes; MaterialId material; SectionId section; }; struct NodeSet final { std::string name; std::vector members; }; struct ElementSet final { std::string name; std::vector members; }; struct PrescribedDof final { NodeId node; std::uint8_t dof; double value; }; struct NodalLoad final { NodeId node; std::array values; }; struct StepDefinition final { std::string name; std::vector prescribed_dofs; std::vector nodal_loads; }; class Domain final { public: [[nodiscard]] std::span nodes() const noexcept; [[nodiscard]] std::span beam_elements() const noexcept; [[nodiscard]] const Node& node(NodeId id) const; [[nodiscard]] const Node& node(const EntityOrigin& origin) const; }; struct DomainBuildResult final { std::optional domain; std::vector diagnostics; }; class DomainBuilder final { public: void add_node(Node value); void add_material(IsotropicElastic value); void add_section(BeamSection value); void add_beam_element(BeamElement value); void add_node_set(NodeSet value); void add_element_set(ElementSet value); void set_step(StepDefinition value); [[nodiscard]] DomainBuildResult build() &&; }; ``` `DomainBuilder::build()` returns either one immutable `Domain` or a nonempty diagnostic list. It must resolve every reference and reject duplicate internal IDs, duplicate `(instance_name, local_label)` origins, nonfinite values, invalid material constants, invalid section properties, zero-length elements, missing assignments, and invalid orientation vectors. Empty Part/Instance names denote the flat global scope. - [ ] **Step 1: Write failing builder tests** Write one valid two-node domain test and one test for every rejection listed above. - [ ] **Step 2: Run the model tests and confirm failure** ```powershell cmake --build --preset windows-debug ctest --preset windows-debug -R "Domain" --output-on-failure ``` - [ ] **Step 3: Implement the minimum immutable storage and validation** Preserve external IDs and create private dense lookup maps. Do not store equation numbers in `Node` or `BeamElement`. - [ ] **Step 4: Run all tests and commit** ```powershell cmake --build --preset windows-debug ctest --preset windows-debug --output-on-failure git add include/fesa/model src/fesa/model tests/unit/model CMakeLists.txt tests/CMakeLists.txt git commit -m "feat(model): add immutable beam domain" ``` --- ### Task 4: Minimal Scoped Abaqus Deck to Domain **Files:** - Create: `include/fesa/io/abaqus/deck_record.hpp` - Create: `include/fesa/io/abaqus/parser.hpp` - Create: `include/fesa/io/abaqus/semantic_mapper.hpp` - Create: `src/fesa/io/abaqus/parser.cpp` - Create: `src/fesa/io/abaqus/semantic_mapper.cpp` - Create: `tests/fixtures/abaqus/minimal_cantilever.inp` - Create: `tests/fixtures/abaqus/minimal_part_instance_cantilever.inp` - Create: `tests/fixtures/abaqus/unsupported_keyword.inp` - Create: `tests/unit/io/abaqus/parser_test.cpp` - Create: `tests/integration/io/minimal_deck_to_domain_test.cpp` - Modify: CMake files **Interfaces:** ```cpp struct DeckRecord final { std::string keyword; std::map> parameters; std::vector> data; SourceLocation source; }; struct ParsedPart final { std::string name; std::vector records; SourceLocation source; }; struct ParsedInstance final { std::string name; std::string part_name; std::vector> transform_data; SourceLocation source; }; struct ParsedAssembly final { std::string name; std::vector instances; std::vector records; SourceLocation source; }; struct ParsedDeck final { std::vector global_records; std::vector parts; std::optional assembly; }; struct ParseDeckResult final { std::optional deck; std::vector diagnostics; }; [[nodiscard]] ParseDeckResult parse_deck(const std::filesystem::path& path); [[nodiscard]] DomainBuildResult map_deck_to_domain(const ParsedDeck& deck); ``` Each minimal fixture contains two nodes, one B31 element, one material, one general section, one node set, one element set, one boundary definition, one concentrated load, and one static step. One fixture is flat; the other contains one Part, one Assembly, and one untransformed Instance. - [ ] **Step 1: Write failing parser and integration tests** Tests must call `parse_deck()` and `map_deck_to_domain()`; they may not construct the `Domain` directly. Assert that both organizations produce equivalent active analysis entities and that the hierarchical Domain retains Part/Instance provenance. - [ ] **Step 2: Confirm failure** ```powershell cmake --build --preset windows-debug ctest --preset windows-debug -R "Abaqus|Deck" --output-on-failure ``` - [ ] **Step 3: Implement the smallest case-insensitive keyword parser** Support comments, blank lines, comma-separated parameters and data, UTF-8 input, exact source lines, `*PART/*END PART`, `*ASSEMBLY/*END ASSEMBLY`, and `*INSTANCE/*END INSTANCE`. In this task, accept only the keywords used by the two minimal fixtures. Treat `*INCLUDE` and every other keyword as an explicit unsupported-keyword error. - [ ] **Step 4: Implement semantic mapping for the fixture** For hierarchical input, require exactly one Assembly and one Instance, reject nonempty Instance transform data, activate only the referenced Part, and map the result to the same flat `Domain` used by orphan meshes. When transverse stiffness is omitted, set \(A_{sy}=A_{sz}=5A/6\) and `SCF=0`; supported explicit values override the default. - [ ] **Step 5: Validate and commit** ```powershell cmake --build --preset windows-debug ctest --preset windows-debug --output-on-failure git add include/fesa/io src/fesa/io tests/fixtures tests/unit/io tests/integration/io CMakeLists.txt tests/CMakeLists.txt git commit -m "feat(input): parse minimal Abaqus B31 deck" ``` --- ### Task 5: FEM Primitives and DOF Management **Files:** - Create: `include/fesa/fem/gauss_rule.hpp` - Create: `include/fesa/fem/line2_shape.hpp` - Create: `include/fesa/fem/local_frame.hpp` - Create: `include/fesa/fem/dof_manager.hpp` - Create: `src/fesa/fem/local_frame.cpp` - Create: `src/fesa/fem/dof_manager.cpp` - Create: `tests/unit/fem/gauss_rule_test.cpp` - Create: `tests/unit/fem/line2_shape_test.cpp` - Create: `tests/unit/fem/local_frame_test.cpp` - Create: `tests/unit/fem/dof_manager_test.cpp` **Interfaces:** ```cpp struct GaussPoint1D final { double xi; double weight; }; [[nodiscard]] std::array gauss_rule_1(); [[nodiscard]] std::array gauss_rule_2(); [[nodiscard]] std::array line2_shape(double xi); [[nodiscard]] std::array line2_shape_derivative(); struct LocalFrame final { Vec3 ex; Vec3 ey; Vec3 ez; double length; }; [[nodiscard]] LocalFrame make_beam_frame(Vec3 first, Vec3 second, Vec3 orientation); class DofManager final { public: explicit DofManager(const Domain& domain); [[nodiscard]] std::size_t full_dof_count() const noexcept; [[nodiscard]] std::array beam_dofs(ElementId id) const; }; ``` - [ ] **Step 1: Write failing mathematical invariant tests** Test Gauss exactness through degree 3, shape-function partition of unity, derivative sum zero, right-handed orthonormal frames, nearly parallel orientation rejection, stable external-ID ordering, and 12-DOF element maps. - [ ] **Step 2: Confirm failure, implement, and rerun** ```powershell cmake --build --preset windows-debug ctest --preset windows-debug -R "Fem|Dof|Frame" --output-on-failure ``` - [ ] **Step 3: Run full validation and commit** ```powershell ctest --preset windows-debug --output-on-failure git add include/fesa/fem src/fesa/fem tests/unit/fem CMakeLists.txt tests/CMakeLists.txt git commit -m "feat(fem): add quadrature frames and DOF mapping" ``` --- ### Task 6: Minimal Real Timoshenko Beam Kernel **Files:** - Create: `docs/formulation/timoshenko-beam-3d.md` - Create: `include/fesa/elements/beam/beam3d2.hpp` - Create: `src/fesa/elements/beam/beam3d2.cpp` - Create: `tests/unit/elements/beam3d2_test.cpp` - Modify: CMake files **Interfaces:** ```cpp struct Beam3D2Input final { std::array coordinates; IsotropicElastic material; BeamSection section; }; struct Beam3D2Contribution final { std::array stiffness; std::array equivalent_load; LocalFrame frame; }; [[nodiscard]] Beam3D2Contribution evaluate_beam3d2(const Beam3D2Input& input); ``` - [ ] **Step 1: Write and review the formulation document before production code** The document must define DOF order, local axes, strain measures, constitutive diagonal, Jacobian, transformation, Gauss rules, matrix storage order, force/moment signs, and reference sources. It must show that axial/bending/torsion use two points and shear uses one point. - [ ] **Step 2: Write failing kernel tests** Test symmetry, six rigid-body modes, positive strain energy for non-rigid modes, analytical axial stiffness \(EA/L\), analytical torsional stiffness \(GJ/L\), coordinate-rotation invariance, and finite values. - [ ] **Step 3: Confirm the tests fail** ```powershell cmake --build --preset windows-debug ctest --preset windows-debug -R "Beam3D2" --output-on-failure ``` - [ ] **Step 4: Implement the minimum kernel from the signed-off equations** Use fixed-size `std::array` storage and small explicit loops. Do not add a dynamic matrix abstraction or copy Abaqus slenderness compensation. - [ ] **Step 5: Validate and commit** ```powershell cmake --build --preset windows-debug ctest --preset windows-debug --output-on-failure git add docs/formulation include/fesa/elements src/fesa/elements tests/unit/elements CMakeLists.txt tests/CMakeLists.txt git commit -m "feat(elements): add 3D Timoshenko beam kernel" ``` --- ### Task 7: Deterministic Serial Assembly and Essential BC Elimination **Files:** - Create: `include/fesa/assembly/symmetric_coo.hpp` - Create: `include/fesa/assembly/symmetric_csr.hpp` - Create: `include/fesa/assembly/assembler.hpp` - Create: `src/fesa/assembly/assembler.cpp` - Create: `include/fesa/constraints/essential_bc.hpp` - Create: `src/fesa/constraints/essential_bc.cpp` - Create: `tests/unit/assembly/assembler_test.cpp` - Create: `tests/unit/constraints/essential_bc_test.cpp` **Interfaces:** ```cpp struct CooEntry final { std::size_t row; std::size_t column; ElementId source_element; std::size_t local_order; double value; }; struct SymmetricCsr final { std::vector row_offsets; std::vector column_indices; std::vector values; }; struct EquationSystem final { SymmetricCsr stiffness; std::vector load; }; [[nodiscard]] EquationSystem assemble_serial( const Domain& domain, const DofManager& dofs); struct ReducedSystem final { SymmetricCsr stiffness; std::vector rhs; std::vector free_dofs; std::vector prescribed_full_values; }; ``` - [ ] **Step 1: Write failing assembly tests** Cover a one-element matrix, a two-element shared-node chain, stable sort/reduction order, upper-triangle storage with every diagonal present, nonzero prescribed values, reduced RHS correction, and full-vector reconstruction. - [ ] **Step 2: Confirm failure and implement serial baseline** Sort COO entries by `(row, column, source_element, local_order)` and then sum. This serial result is the oracle for Task 12. - [ ] **Step 3: Verify and commit** ```powershell cmake --build --preset windows-debug ctest --preset windows-debug -R "Assembly|EssentialBc" --output-on-failure ctest --preset windows-debug --output-on-failure git add include/fesa/assembly src/fesa/assembly include/fesa/constraints src/fesa/constraints tests/unit/assembly tests/unit/constraints CMakeLists.txt tests/CMakeLists.txt git commit -m "feat(assembly): assemble and constrain beam systems" ``` --- ### Task 8: MKL PARDISO Linear Solver Adapter **Files:** - Create: `include/fesa/solvers/linear/linear_solver.hpp` - Create: `include/fesa/solvers/linear/pardiso_solver.hpp` - Create: `src/fesa/solvers/linear/pardiso_solver.cpp` - Create: `tests/unit/solvers/pardiso_solver_test.cpp` - Modify: `cmake/FesaDependencies.cmake` - Modify: CMake files **Interfaces:** ```cpp struct LinearSolveResult final { std::vector solution; double relative_residual; std::vector diagnostics; }; class LinearSolver { public: virtual ~LinearSolver() = default; [[nodiscard]] virtual LinearSolveResult solve( const SymmetricCsr& matrix, std::span rhs) = 0; }; class PardisoLinearSolver final : public LinearSolver { public: PardisoLinearSolver(); ~PardisoLinearSolver() override; PardisoLinearSolver(const PardisoLinearSolver&) = delete; PardisoLinearSolver& operator=(const PardisoLinearSolver&) = delete; [[nodiscard]] LinearSolveResult solve( const SymmetricCsr& matrix, std::span rhs) override; }; ``` - [ ] **Step 1: Write failing adapter tests** Use a hand-calculated 3x3 SPD system, multiple RHS calls on one adapter, invalid CSR, dimension mismatch, and a singular matrix. Assert `mtype=2`, LP64-compatible index checks, 0-based indexing, and residual reporting through observable behavior. - [ ] **Step 2: Confirm failure** ```powershell cmake --build --preset windows-debug ctest --preset windows-debug -R "Pardiso" --output-on-failure ``` - [ ] **Step 3: Implement RAII PARDISO phases** Set `iparm[34]=1` for zero-based indexing and enable the matrix checker. Run symbolic analysis, numerical factorization, solve, and release. Convert every MKL error to `DiagnosticStage::solver`. - [ ] **Step 4: Validate and commit** ```powershell cmake --build --preset windows-debug ctest --preset windows-debug --output-on-failure git add include/fesa/solvers src/fesa/solvers tests/unit/solvers cmake/FesaDependencies.cmake CMakeLists.txt tests/CMakeLists.txt git commit -m "feat(solver): add PARDISO linear backend" ``` --- ### Task 9: Result Model and Minimal HDF5 Round Trip **Files:** - Create: `docs/HDF5_SCHEMA.md` - Create: `include/fesa/results/result_database.hpp` - Create: `include/fesa/io/hdf5/schema.hpp` - Create: `include/fesa/io/hdf5/writer.hpp` - Create: `include/fesa/io/hdf5/reader.hpp` - Create: `src/fesa/io/hdf5/writer.cpp` - Create: `src/fesa/io/hdf5/reader.cpp` - Create: `tests/unit/io/hdf5_round_trip_test.cpp` - Modify: CMake files **Interfaces:** ```cpp struct NodalFrame final { std::vector node_ids; std::vector> displacement; std::vector> reaction; }; struct ResultFrame final { double step_time; NodalFrame nodal; std::vector diagnostics; }; struct ResultStep final { std::string name; std::vector frames; }; struct ResultDatabase final { std::string schema_version; std::vector steps; }; struct Hdf5ReadResult final { std::optional database; std::vector diagnostics; }; [[nodiscard]] std::vector write_hdf5( const std::filesystem::path&, const Domain&, const ResultDatabase&); [[nodiscard]] Hdf5ReadResult read_hdf5_results(const std::filesystem::path&); ``` - [ ] **Step 1: Write schema version `1.0.0` before writer code** Define exact group paths, dataset ranks, scalar types, dense ID mappings, Part/Instance/local-label origins, coordinate-system attributes, applied transverse-shear values and their input/default source, required HDF5 metadata, and compatibility rules. - [ ] **Step 2: Write a failing round-trip test** Use a two-node hierarchical-origin `Domain` and one result frame. Reopen through the FESA reader and compare every stored value, origin mapping, transverse-shear source, and schema attribute. - [ ] **Step 3: Confirm failure and implement minimum C-API RAII wrappers** Do not use global HDF5 handles. Convert every failing HDF5 call into a results-stage diagnostic or exception caught at the adapter boundary. - [ ] **Step 4: Validate with tests and HDF5 tools** ```powershell cmake --build --preset windows-debug ctest --preset windows-debug -R "Hdf5" --output-on-failure h5ls -r .\out\build\windows-debug\Testing\Temporary\fesa-round-trip.h5 ctest --preset windows-debug --output-on-failure ``` - [ ] **Step 5: Commit** ```powershell git add docs/HDF5_SCHEMA.md include/fesa/results include/fesa/io/hdf5 src/fesa/io/hdf5 tests/unit/io CMakeLists.txt tests/CMakeLists.txt git commit -m "feat(results): add versioned HDF5 result adapter" ``` --- ### Task 10: Linear Static Analysis and End-to-End CLI Slice **Files:** - Create: `include/fesa/analysis/analysis.hpp` - Create: `include/fesa/analysis/linear_static_analysis.hpp` - Create: `src/fesa/analysis/analysis.cpp` - Create: `src/fesa/analysis/linear_static_analysis.cpp` - Create: `include/fesa/analysis/run_solver.hpp` - Create: `src/fesa/analysis/run_solver.cpp` - Modify: `src/fesa/cli/main.cpp` - Create: `tests/integration/pipeline/minimal_cantilever_test.cpp` - Modify: CMake files **Interfaces:** ```cpp struct AnalysisRequest final { std::filesystem::path input_path; std::filesystem::path output_path; }; struct AnalysisRunResult final { bool succeeded; std::vector diagnostics; }; [[nodiscard]] AnalysisRunResult run_solver(const AnalysisRequest& request); ``` The CLI contract is: ```text fesa solve --output fesa --version ``` - [ ] **Step 1: Write the failing end-to-end test** Invoke only `run_solver()` or the CLI with `tests/fixtures/abaqus/minimal_cantilever.inp`, then use the public HDF5 reader to assert node IDs, finite displacement, equilibrium residual, and result paths. - [ ] **Step 2: Confirm the pipeline test fails** ```powershell cmake --build --preset windows-debug ctest --preset windows-debug -R "MinimalCantileverPipeline" --output-on-failure ``` - [ ] **Step 3: Implement the analysis lifecycle** Connect parser, `Domain`, `DofManager`, Beam kernel, serial assembly, essential BC, PARDISO, full-vector reconstruction, reaction recovery, result model, and HDF5 writer. Keep CLI parsing out of `fesa_core`. - [ ] **Step 4: Validate the vertical slice** ```powershell cmake --build --preset windows-debug ctest --preset windows-debug --output-on-failure .\out\build\windows-debug\Debug\fesa.exe solve tests\fixtures\abaqus\minimal_cantilever.inp --output out\minimal-cantilever.h5 h5ls -r out\minimal-cantilever.h5 ``` Expected: full pipeline succeeds. Do not label the Beam numerically qualified yet. - [ ] **Step 5: Commit** ```powershell git add include/fesa/analysis src/fesa/analysis src/fesa/cli/main.cpp tests/integration/pipeline CMakeLists.txt tests/CMakeLists.txt git commit -m "feat(analysis): connect linear static pipeline" ``` --- ### Task 11: Complete the Agreed Abaqus Input Subset **Files:** - Modify: Abaqus parser and mapper files from Task 4 - Create: `tests/unit/io/abaqus/set_resolution_test.cpp` - Create: `tests/unit/io/abaqus/scope_resolution_test.cpp` - Create: `tests/unit/io/abaqus/semantic_validation_test.cpp` - Create: `tests/integration/io/multiple_properties_test.cpp` - Create: `tests/integration/io/supplied_cantilever_to_domain_test.cpp` - Create fixtures under: `tests/fixtures/abaqus/valid/` - Create fixtures under: `tests/fixtures/abaqus/invalid/` - Create: `docs/ABAQUS_INPUT_SUBSET.md` **Interfaces:** - Existing parser and mapper signatures remain unchanged. - `docs/ABAQUS_INPUT_SUBSET.md` becomes the normative keyword/parameter/data-line contract. - [ ] **Step 1: Write the contract document and failing fixture matrix** Cover `*NODE`, B31 `*ELEMENT`, `*PART/*END PART`, `*ASSEMBLY/*END ASSEMBLY`, `*INSTANCE/*END INSTANCE`, `*NSET`, `*ELSET`, explicit members, `GENERATE`, nested set references, `INSTANCE=`, `*MATERIAL`, `*ELASTIC`, general Beam section, optional transverse shear stiffness, `*BOUNDARY`, `*CLOAD`, and one static step. Document `*HEADING`, `*PREPRINT`, `*RESTART`, and `*OUTPUT` as recognized no-op directives. - [ ] **Step 2: Add invalid tests before parser changes** Cover duplicate IDs and origins, missing references, set cycles, invalid ranges, multiple section assignments, missing material, conflicting prescribed values, unsupported options, `*INCLUDE`, multiple Assemblies, multiple Instances, Instance translation/rotation data, instance-local mesh changes, mixed flat/hierarchical meshes, nonzero `SCF`, multiple steps, and source-line accuracy. - [ ] **Step 3: Run and confirm the new tests fail** ```powershell cmake --build --preset windows-debug ctest --preset windows-debug -R "Abaqus|SetResolution|MultipleProperties" --output-on-failure ``` - [ ] **Step 4: Implement only the documented subset** Resolve Part and Assembly scopes before normalizing only the active Part and Instance. Resolve nested sets with explicit cycle detection and canonical sorted-unique membership. Recognized no-op directives must be consumed deliberately; do not add a general ignore-unknown path. Verify the supplied `reference/cantilever beam/cantilever beam.inp` maps to the expected 11 nodes, 10 elements, one active material/section, six fixed DOFs, and one nodal load. - [ ] **Step 5: Validate and commit** ```powershell cmake --build --preset windows-debug ctest --preset windows-debug --output-on-failure git add docs/ABAQUS_INPUT_SUBSET.md include/fesa/io/abaqus src/fesa/io/abaqus tests/fixtures/abaqus tests/unit/io/abaqus tests/integration/io git commit -m "feat(input): complete Phase 1 Abaqus subset" ``` --- ### Task 12: Deterministic oneTBB Assembly **Files:** - Modify: `include/fesa/assembly/assembler.hpp` - Modify: `src/fesa/assembly/assembler.cpp` - Create: `tests/unit/assembly/parallel_assembler_test.cpp` - Create: `tests/integration/assembly/thread_count_determinism_test.cpp` - Create: `tests/performance/assembly_benchmark.cpp` - Modify: CMake files **Interfaces:** ```cpp struct AssemblyOptions final { std::size_t max_threads; std::size_t grain_size; }; [[nodiscard]] EquationSystem assemble_parallel( const Domain& domain, const DofManager& dofs, AssemblyOptions options); ``` - [ ] **Step 1: Write failing serial-versus-parallel tests** Generate fixed chain and branched Beam domains. Compare CSR row offsets and column indices exactly and values bit-for-bit for thread counts 1, 2, and the available concurrency. - [ ] **Step 2: Confirm failure** ```powershell cmake --build --preset windows-debug ctest --preset windows-debug -R "ParallelAssembly|ThreadCount" --output-on-failure ``` - [ ] **Step 3: Implement parallel element evaluation with deterministic merge** Use oneTBB for independent element evaluation. Each contribution retains `(row, column, element ID, local order)`. Sort and reduce in the same order as the serial oracle. Do not perform concurrent unordered writes to CSR values. - [ ] **Step 4: Validate correctness before measuring performance** ```powershell cmake --build --preset windows-debug ctest --preset windows-debug --output-on-failure .\out\build\windows-debug\Debug\fesa_assembly_benchmark.exe ``` Record timings without asserting a speedup in unit tests. - [ ] **Step 5: Commit** ```powershell git add include/fesa/assembly src/fesa/assembly tests/unit/assembly tests/integration/assembly tests/performance CMakeLists.txt tests/CMakeLists.txt git commit -m "perf(assembly): add deterministic TBB assembly" ``` --- ### Task 13: Complete Beam Recovery and Self-Contained HDF5 Contract **Files:** - Modify: `include/fesa/elements/beam/beam3d2.hpp` - Modify: `src/fesa/elements/beam/beam3d2.cpp` - Modify: result-model and HDF5 files from Task 9 - Modify: `docs/HDF5_SCHEMA.md` - Create: `tests/unit/elements/beam3d2_recovery_test.cpp` - Create: `tests/integration/results/self_contained_hdf5_test.cpp` **Interfaces:** ```cpp struct BeamSectionResult final { double xi; NodeId end_node; std::array section_strain; std::array section_force; double centroid_sigma_xx; std::vector sigma_xx; }; [[nodiscard]] std::vector recover_beam3d2( const Beam3D2Input& input, std::span element_displacement, std::span> recovery_points); ``` - [ ] **Step 1: Write failing recovery tests** Cover pure axial force, pure torsion, bending about each principal axis, combined axial/biaxial bending, force sign at both element ends, `centroid_sigma_xx=N/A`, and recovery-point ordering. - [ ] **Step 2: Write the failing self-contained-file test** Reopen one result file and reconstruct node coordinates, connectivity, Part/Instance origins, sets, material, section, applied shear values and their source, step, solver settings, local frame, nodal fields, element section fields including centroid stress, ID maps, and diagnostics. - [ ] **Step 3: Implement recovery and schema additions** Do not output point shear or point torsional stress. Store section shear resultants and torsional moment as generalized quantities. - [ ] **Step 4: Validate and commit** ```powershell cmake --build --preset windows-debug ctest --preset windows-debug -R "Beam3D2Recovery|SelfContainedHdf5" --output-on-failure ctest --preset windows-debug --output-on-failure git add include/fesa/elements src/fesa/elements include/fesa/results include/fesa/io/hdf5 src/fesa/io/hdf5 docs/HDF5_SCHEMA.md tests/unit/elements tests/integration/results git commit -m "feat(results): recover and store beam section results" ``` --- ### Task 14: Analytical and Available Abaqus 2024 Qualification **Files:** - Create: `include/fesa/validation/comparison.hpp` - Create: `include/fesa/validation/reference_csv.hpp` - Create: `src/fesa/validation/comparison.cpp` - Create: `src/fesa/validation/reference_csv.cpp` - Create: `src/fesa/validation/reference_compare_main.cpp` - Create: `tests/unit/validation/comparison_test.cpp` - Create: `tests/unit/validation/reference_csv_test.cpp` - Create: `tests/fixtures/reference/internalforces.csv` - Create: `tests/fixtures/reference/stresses.csv` - Create: `tests/reference/CMakeLists.txt` - Create: `tests/reference/cantilever_reference_test.cpp` - Create: `docs/VALIDATION.md` - Modify: CMake files **Interfaces:** ```cpp struct Tolerance final { double relative; double absolute_scale; }; enum class ReferenceQuantity { displacement, reaction, internal_force, centroid_stress }; struct ResultPosition final { std::string instance_name; std::int64_t entity_label; std::optional end_node_label; }; struct ComparisonSample final { ReferenceQuantity quantity; ResultPosition position; std::vector reference; std::vector actual; Tolerance tolerance; }; struct ReferenceRow final { ReferenceQuantity quantity; ResultPosition position; std::vector values; }; struct ComparisonReport final { bool passed; double maximum_normalized_error; std::vector failures; }; [[nodiscard]] ComparisonReport compare_samples( std::span samples); struct ReferenceCsvReadResult final { std::vector rows; std::vector diagnostics; }; [[nodiscard]] ReferenceCsvReadResult read_reference_csv( ReferenceQuantity quantity, const std::filesystem::path& path, std::string_view single_instance_name, Tolerance tolerance); ``` For each scalar component, define \[ e_n=\frac{|a-r|}{a_\mathrm{scale}+r_\mathrm{tol}|r|} \] and pass only when \(e_n\leq1\). Reject nonfinite inputs before computing the metric. - [ ] **Step 1: Write failing metric and entity-matching tests** Test the normalized error, near-zero absolute scale, nonfinite values, duplicate positions, unknown entities, invalid element-node pairs, and component-count mismatches. - [ ] **Step 2: Write failing CSV adapter tests for all four quantities** Accept the supplied displacement/reaction headers after trimming whitespace. For a single Instance, allow the `Part Instance Name` column to be absent. Use these exact element schemas: ```text Part Instance Name, Element Label, Node Label, SF-SF1, SF-SF2, SF-SF3, SM-SM1, SM-SM2, SM-SM3 Part Instance Name, Element Label, Node Label, Sxx ``` Map `SF1,SF2,SF3,SM1,SM2,SM3` to \(N,V_y,V_z,T,M_y,M_z\). Compare `Sxx` with the element-end section-centroid value \(N/A\). The synthetic fixtures exercise both element result schemas before real files exist. - [ ] **Step 3: Write the failing supplied-cantilever reference test** Run FESA through the public parser, analysis, and HDF5 reader. Create an explicit request selecting only: ```text reference/cantilever beam/cantilever beam displacements.csv reference/cantilever beam/cantilever beam reactions.csv ``` Use relative tolerance \(10^{-5}\) and test-registered absolute scales. Do not look for metadata, internal-force CSV, or stress CSV in this reference test. The equivalent command-line contract is: ```powershell fesa-reference-compare ` --results out\cantilever-beam.h5 ` --instance Part-1-1 ` --displacements "reference\cantilever beam\cantilever beam displacements.csv" ` --reactions "reference\cantilever beam\cantilever beam reactions.csv" ` --relative-tolerance 1e-5 ` --displacement-absolute-scale 1e-10 ` --reaction-absolute-scale 1e-8 ``` - [ ] **Step 4: Confirm failures before numerical corrections** ```powershell cmake --preset windows-debug cmake --build --preset windows-debug ctest --preset windows-debug -R "Reference" --output-on-failure ``` - [ ] **Step 5: Correct only evidenced formulation or recovery defects** For every change, add or tighten the smallest analytical test that reproduces the discrepancy. Do not widen tolerance to hide a defect. Record justified tolerance differences in `docs/VALIDATION.md` and the CTest registration. - [ ] **Step 6: Run the full qualification suite** ```powershell ctest --preset windows-debug --output-on-failure ctest --preset windows-debug -R "Reference" --output-on-failure ``` Expected: all analytical, physics-sanity, pipeline, deterministic-parallel, CSV-adapter, and available Abaqus displacement/reaction comparisons pass. Do not claim Abaqus qualification of element internal force or stress until those CSV files are supplied and selected. - [ ] **Step 7: Complete the validation report and commit** `docs/VALIDATION.md` must list each benchmark, analytical/reference source, Abaqus configuration, selected quantities, tolerance, maximum observed error, and disposition. It must distinguish synthetic adapter coverage from Abaqus-backed quantity qualification. ```powershell git add include/fesa/validation src/fesa/validation tests/unit/validation tests/fixtures/reference tests/reference docs/VALIDATION.md CMakeLists.txt tests/CMakeLists.txt git commit -m "test(validation): qualify Beam solver against Abaqus" ``` --- ### Task 15: Internal Release Gate **Files:** - Create: `cmake/install.cmake` - Create: `cmake/FESAConfig.cmake.in` - Create: `docs/BUILDING.md` - Create: `docs/INPUT_FORMAT.md` - Create: `docs/RELEASE_CHECKLIST.md` - Create: `tests/performance/phase1_scale_benchmark.cpp` - Modify: root CMake files - Modify: `docs/VALIDATION.md` **Interfaces:** - Produces install tree containing `fesa.exe`, the static library, public headers, required runtime DLL inventory, example input, schema, and validation report. - No public ABI compatibility promise is made for Phase 1. - [ ] **Step 1: Write the release checklist before packaging changes** Include environment versions, Debug/Release builds, zero-warning requirement, CTest count, selected reference quantities, synthetic four-quantity adapter coverage, HDF5 inspection, 100k-DOF memory/time measurement, runtime DLL inventory, example run, and clean install-tree smoke test. - [ ] **Step 2: Add a failing install-tree smoke test** The test configures a small consumer against installed headers and the static library, runs `fesa --version`, solves the example, and opens its HDF5 output. - [ ] **Step 3: Implement CMake install rules** Use `cmake --install`; do not add an installer, registry writes, package download, or external-customer SDK promise. - [ ] **Step 4: Run Debug, Release, performance, and install validation** ```powershell cmake --preset windows-debug cmake --build --preset windows-debug ctest --preset windows-debug --output-on-failure cmake --preset windows-release cmake --build --preset windows-release ctest --preset windows-release --output-on-failure .\out\build\windows-release\Release\fesa_phase1_scale_benchmark.exe cmake --install out\build\windows-release --config Release --prefix out\package\fesa ``` Expected: all tests pass, no new MSVC warnings exist, the benchmark completes within documented resources, and the clean install-tree smoke test passes. - [ ] **Step 5: Verify every PRD release criterion** Map every item in `docs/PRD.md` section 8 to fresh command output or a validation-report entry. Stop the release if any item lacks evidence. - [ ] **Step 6: Commit** ```powershell git add cmake/install.cmake docs/BUILDING.md docs/INPUT_FORMAT.md docs/RELEASE_CHECKLIST.md docs/VALIDATION.md tests/performance CMakeLists.txt git commit -m "chore(release): prepare FESA Phase 1 internal package" ``` ## Plan Execution Gate Before implementation: 1. Review and approve this phase split. 2. Use the Harness skill to draft `phases/index.json`, each phase index, and self-contained step files. 3. Review the phase-file draft before creating it. 4. Execute one Harness phase at a time. 5. Do not begin the next phase until its tests, review, and acceptance commands pass.