1375 lines
48 KiB
Markdown
1375 lines
48 KiB
Markdown
# 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` | Harness baseline, 1-2 | Harness self-tests, 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 before CMake bootstrap: `tests/harness/test_config.py`
|
|
- Create before CMake bootstrap: `tests/harness/test_discovery.py`
|
|
- Create before CMake bootstrap: `tests/harness/test_process.py`
|
|
- 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.
|
|
|
|
Before the CMake task, add characterization tests for the existing Harness
|
|
configuration, project discovery, and validation-result contracts. This
|
|
precondition makes the repository-level pytest command collect at least one
|
|
test without changing production Harness behavior.
|
|
|
|
- [ ] **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 <gtest/gtest.h>
|
|
#include <fesa/core/version.hpp>
|
|
|
|
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 Tag>
|
|
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<SourceLocation> source;
|
|
std::optional<std::int64_t> 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<struct NodeTag>;
|
|
using ElementId = EntityId<struct ElementTag>;
|
|
using MaterialId = EntityId<struct MaterialTag>;
|
|
using SectionId = EntityId<struct SectionTag>;
|
|
|
|
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<std::array<double, 2>> recovery_points;
|
|
};
|
|
struct BeamElement final {
|
|
ElementId id;
|
|
EntityOrigin origin;
|
|
std::array<NodeId, 2> nodes;
|
|
MaterialId material;
|
|
SectionId section;
|
|
};
|
|
struct NodeSet final { std::string name; std::vector<NodeId> members; };
|
|
struct ElementSet final { std::string name; std::vector<ElementId> members; };
|
|
struct PrescribedDof final { NodeId node; std::uint8_t dof; double value; };
|
|
struct NodalLoad final { NodeId node; std::array<double, 6> values; };
|
|
struct StepDefinition final {
|
|
std::string name;
|
|
std::vector<PrescribedDof> prescribed_dofs;
|
|
std::vector<NodalLoad> nodal_loads;
|
|
};
|
|
|
|
class Domain final {
|
|
public:
|
|
[[nodiscard]] std::span<const Node> nodes() const noexcept;
|
|
[[nodiscard]] std::span<const BeamElement> 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> domain;
|
|
std::vector<Diagnostic> 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<std::string, std::string, std::less<>> parameters;
|
|
std::vector<std::vector<std::string>> data;
|
|
SourceLocation source;
|
|
};
|
|
|
|
struct ParsedPart final {
|
|
std::string name;
|
|
std::vector<DeckRecord> records;
|
|
SourceLocation source;
|
|
};
|
|
|
|
struct ParsedInstance final {
|
|
std::string name;
|
|
std::string part_name;
|
|
std::vector<std::vector<std::string>> transform_data;
|
|
SourceLocation source;
|
|
};
|
|
|
|
struct ParsedAssembly final {
|
|
std::string name;
|
|
std::vector<ParsedInstance> instances;
|
|
std::vector<DeckRecord> records;
|
|
SourceLocation source;
|
|
};
|
|
|
|
struct ParsedDeck final {
|
|
std::vector<DeckRecord> global_records;
|
|
std::vector<ParsedPart> parts;
|
|
std::optional<ParsedAssembly> assembly;
|
|
};
|
|
|
|
struct ParseDeckResult final {
|
|
std::optional<ParsedDeck> deck;
|
|
std::vector<Diagnostic> 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<GaussPoint1D, 1> gauss_rule_1();
|
|
[[nodiscard]] std::array<GaussPoint1D, 2> gauss_rule_2();
|
|
[[nodiscard]] std::array<double, 2> line2_shape(double xi);
|
|
[[nodiscard]] std::array<double, 2> 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<std::size_t, 12> 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<Vec3, 2> coordinates;
|
|
IsotropicElastic material;
|
|
BeamSection section;
|
|
};
|
|
|
|
struct Beam3D2Contribution final {
|
|
std::array<double, 144> stiffness;
|
|
std::array<double, 12> 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<double, 144>` 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<std::int32_t> row_offsets;
|
|
std::vector<std::int32_t> column_indices;
|
|
std::vector<double> values;
|
|
};
|
|
|
|
struct EquationSystem final {
|
|
SymmetricCsr stiffness;
|
|
std::vector<double> load;
|
|
};
|
|
|
|
[[nodiscard]] EquationSystem assemble_serial(
|
|
const Domain& domain,
|
|
const DofManager& dofs);
|
|
|
|
struct ReducedSystem final {
|
|
SymmetricCsr stiffness;
|
|
std::vector<double> rhs;
|
|
std::vector<std::size_t> free_dofs;
|
|
std::vector<double> 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<double> solution;
|
|
double relative_residual;
|
|
std::vector<Diagnostic> diagnostics;
|
|
};
|
|
|
|
class LinearSolver {
|
|
public:
|
|
virtual ~LinearSolver() = default;
|
|
[[nodiscard]] virtual LinearSolveResult solve(
|
|
const SymmetricCsr& matrix,
|
|
std::span<const double> 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<const double> 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<NodeId> node_ids;
|
|
std::vector<std::array<double, 6>> displacement;
|
|
std::vector<std::array<double, 6>> reaction;
|
|
};
|
|
|
|
struct ResultFrame final {
|
|
double step_time;
|
|
NodalFrame nodal;
|
|
std::vector<Diagnostic> diagnostics;
|
|
};
|
|
|
|
struct ResultStep final {
|
|
std::string name;
|
|
std::vector<ResultFrame> frames;
|
|
};
|
|
|
|
struct ResultDatabase final {
|
|
std::string schema_version;
|
|
std::vector<ResultStep> steps;
|
|
};
|
|
|
|
struct Hdf5ReadResult final {
|
|
std::optional<ResultDatabase> database;
|
|
std::vector<Diagnostic> diagnostics;
|
|
};
|
|
|
|
[[nodiscard]] std::vector<Diagnostic> 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<Diagnostic> diagnostics;
|
|
};
|
|
|
|
[[nodiscard]] AnalysisRunResult run_solver(const AnalysisRequest& request);
|
|
```
|
|
|
|
The CLI contract is:
|
|
|
|
```text
|
|
fesa solve <model.inp> --output <results.h5>
|
|
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<double, 6> section_strain;
|
|
std::array<double, 6> section_force;
|
|
double centroid_sigma_xx;
|
|
std::vector<double> sigma_xx;
|
|
};
|
|
|
|
[[nodiscard]] std::vector<BeamSectionResult> recover_beam3d2(
|
|
const Beam3D2Input& input,
|
|
std::span<const double, 12> element_displacement,
|
|
std::span<const std::array<double, 2>> 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<std::int64_t> end_node_label;
|
|
};
|
|
|
|
struct ComparisonSample final {
|
|
ReferenceQuantity quantity;
|
|
ResultPosition position;
|
|
std::vector<double> reference;
|
|
std::vector<double> actual;
|
|
Tolerance tolerance;
|
|
};
|
|
|
|
struct ReferenceRow final {
|
|
ReferenceQuantity quantity;
|
|
ResultPosition position;
|
|
std::vector<double> values;
|
|
};
|
|
|
|
struct ComparisonReport final {
|
|
bool passed;
|
|
double maximum_normalized_error;
|
|
std::vector<Diagnostic> failures;
|
|
};
|
|
|
|
[[nodiscard]] ComparisonReport compare_samples(
|
|
std::span<const ComparisonSample> samples);
|
|
|
|
struct ReferenceCsvReadResult final {
|
|
std::vector<ReferenceRow> rows;
|
|
std::vector<Diagnostic> 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.
|