refactor: store runtime objects in domain

This commit is contained in:
김경종
2026-06-09 10:08:34 +09:00
parent 8f24213ab7
commit f4196efb10
20 changed files with 754 additions and 368 deletions
+2 -2
View File
@@ -64,9 +64,9 @@
**Tradeoff**: Implementation can be blocked until required reference artifacts are supplied. **Tradeoff**: Implementation can be blocked until required reference artifacts are supplied.
## ADR-010: Domain, AnalysisModel, And AnalysisState Are Separate ## ADR-010: Domain, AnalysisModel, And AnalysisState Are Separate
**Decision**: `Domain` owns parsed model definition, `AnalysisModel` owns the active step execution view, and `AnalysisState` owns mutable solution and iteration state. **Decision**: `Domain` owns the validated runtime model object graph created from parsed input, `AnalysisModel` owns the active step execution view, and `AnalysisState` owns mutable solution and iteration state.
**Reason**: This prevents equation ids, displacement vectors, residuals, and future nonlinear/time states from leaking into input model objects. It also keeps the static solver compatible with future nonlinear, dynamic, and thermal workflows. **Reason**: This prevents parser DTOs, equation ids, displacement vectors, residuals, and future nonlinear/time states from leaking into the persistent domain model. It also keeps the static solver compatible with future nonlinear, dynamic, and thermal workflows.
**Tradeoff**: The initial implementation has more object boundaries than a single monolithic model container. **Tradeoff**: The initial implementation has more object boundaries than a single monolithic model container.
+6 -3
View File
@@ -80,7 +80,7 @@ tests/
The current repository may not yet contain all directories. They are intended ownership boundaries for implementation planning. The current repository may not yet contain all directories. They are intended ownership boundaries for implementation planning.
## Core Runtime Objects ## Core Runtime Objects
- `Domain`: owns model definitions from input: nodes, elements, materials, properties, sets, boundary conditions, loads, and step definitions. - `Domain`: owns the validated runtime model object graph created from input: nodes, elements, materials, properties, sets, boundary conditions, loads, and step definitions.
- `AnalysisModel`: step-local execution view over active elements, loads, boundary conditions, properties, materials, and equation system view. - `AnalysisModel`: step-local execution view over active elements, loads, boundary conditions, properties, materials, and equation system view.
- `AnalysisState`: owns changing physical quantities and iteration/time state such as displacement, velocity, acceleration, temperature, forces, residual, current time, increment, and element/integration-point state. - `AnalysisState`: owns changing physical quantities and iteration/time state such as displacement, velocity, acceleration, temperature, forces, residual, current time, increment, and element/integration-point state.
- `Node`: stores node id, coordinates, and six DOF values in order `U1, U2, U3, UR1, UR2, UR3`. - `Node`: stores node id, coordinates, and six DOF values in order `U1, U2, U3, UR1, UR2, UR3`.
@@ -100,7 +100,7 @@ The current repository may not yet contain all directories. They are intended ow
## State Ownership ## State Ownership
### Domain ### Domain
`Domain` represents parsed model definition and should not store equation ids, solver vectors, current displacement, or iteration state. `Domain` represents the validated model definition as runtime objects and should not store parser DTOs, equation ids, solver vectors, current displacement, or iteration state.
Included: Included:
- nodes and elements - nodes and elements
@@ -109,6 +109,8 @@ Included:
- loads and boundary conditions - loads and boundary conditions
- analysis step definitions - analysis step definitions
Parser-style definition records may exist as temporary input or factory records, but they are not persistent `Domain` storage. `Domain` owns runtime objects such as `Element`, `Material`, `ShellProperty`, `Load`, and `BoundaryCondition`.
### AnalysisModel ### AnalysisModel
`AnalysisModel` is built per active step. It references `Domain` objects by id or stable reference and defines what participates in the current solve. `AnalysisModel` is built per active step. It references `Domain` objects by id or stable reference and defines what participates in the current solve.
@@ -152,8 +154,9 @@ Results use:
```text ```text
Abaqus input file Abaqus input file
-> InputParser -> InputParser
-> temporary parsed records
-> Factory/Registry object creation -> Factory/Registry object creation
-> Domain -> Domain runtime object graph
-> StepDefinition loop -> StepDefinition loop
-> AnalysisModel -> AnalysisModel
-> DofManager -> DofManager
+8
View File
@@ -34,6 +34,10 @@
- Added identity-only model object base classes and concrete skeletons for `Element`/`Mitc4Element`, `Material`/`LinearElasticMaterial`, `ShellProperty`, `Load`/`NodalLoad`, and `BoundaryCondition`/`SinglePointConstraint`. - Added identity-only model object base classes and concrete skeletons for `Element`/`Mitc4Element`, `Material`/`LinearElasticMaterial`, `ShellProperty`, `Load`/`NodalLoad`, and `BoundaryCondition`/`SinglePointConstraint`.
- Extended `Domain` with RAII `std::unique_ptr` ownership APIs for polymorphic element, material, load, and boundary model objects while preserving existing `*Definition` storage APIs. - Extended `Domain` with RAII `std::unique_ptr` ownership APIs for polymorphic element, material, load, and boundary model objects while preserving existing `*Definition` storage APIs.
- Added `fesa_model_object_tests` CTest target and model-object tests under `tests/element/`, `tests/material/`, `tests/property/`, `tests/load/`, and `tests/boundary/`. - Added `fesa_model_object_tests` CTest target and model-object tests under `tests/element/`, `tests/material/`, `tests/property/`, `tests/load/`, and `tests/boundary/`.
- Created `docs/implementation-plans/domain-runtime-storage-implementation-plan.md` and `phases/domain-runtime-storage/`.
- Migrated `Domain` canonical storage away from parser-style definition DTOs to runtime model objects for elements, materials, shell properties, loads, and boundary conditions.
- Moved `ElementType` into `ModelTypes.hpp` so runtime element interfaces no longer include `ElementDefinition.hpp`.
- Updated Domain storage tests so element sets and linear static step indices validate against runtime element, load, and boundary containers.
## In Progress ## In Progress
- Ready for the next upstream MITC4 stage: new solver feature requirements analysis for `mitc4-linear-static-shell`. - Ready for the next upstream MITC4 stage: new solver feature requirements analysis for `mitc4-linear-static-shell`.
@@ -51,6 +55,10 @@
- 2026-06-09: After analysis model object implementation, `python scripts/validate_workspace.py` configured CMake with Visual Studio 17 2022 x64, built Debug targets, ran CTest, and passed. - 2026-06-09: After analysis model object implementation, `python scripts/validate_workspace.py` configured CMake with Visual Studio 17 2022 x64, built Debug targets, ran CTest, and passed.
- 2026-06-09: After analysis model object implementation, `ctest --test-dir build/msvc-debug --output-on-failure -C Debug -R "domain|model-object"` passed. 2 CTest executables ran successfully. - 2026-06-09: After analysis model object implementation, `ctest --test-dir build/msvc-debug --output-on-failure -C Debug -R "domain|model-object"` passed. 2 CTest executables ran successfully.
- 2026-06-09: After analysis model object implementation, `git diff --check` passed. - 2026-06-09: After analysis model object implementation, `git diff --check` passed.
- 2026-06-09: After Domain runtime storage migration, `ctest --test-dir build/msvc-debug --output-on-failure -C Debug -R "domain|model-object"` passed. 2 CTest executables ran successfully.
- 2026-06-09: After Domain runtime storage migration, `python -m unittest discover -s scripts -p "test_*.py"` passed. 89 tests ran successfully.
- 2026-06-09: After Domain runtime storage migration, `python scripts/validate_workspace.py` configured CMake with Visual Studio 17 2022 x64, built Debug targets, ran CTest, and passed.
- 2026-06-09: After Domain runtime storage migration, `git diff --check` passed with only Git line-ending normalization warnings.
- 2026-06-08: After Domain model foundation implementation, `python -m unittest discover -s scripts -p "test_*.py"` passed. 89 tests ran successfully. - 2026-06-08: After Domain model foundation implementation, `python -m unittest discover -s scripts -p "test_*.py"` passed. 89 tests ran successfully.
- 2026-06-08: After Domain model foundation implementation, `python scripts/validate_workspace.py` configured CMake with Visual Studio 17 2022 x64, built Debug targets, ran CTest, and passed. - 2026-06-08: After Domain model foundation implementation, `python scripts/validate_workspace.py` configured CMake with Visual Studio 17 2022 x64, built Debug targets, ran CTest, and passed.
- 2026-06-08: After Domain model foundation implementation, `ctest --test-dir build/msvc-debug --output-on-failure -C Debug -R domain` passed. 1 domain/core test executable ran successfully. - 2026-06-08: After Domain model foundation implementation, `ctest --test-dir build/msvc-debug --output-on-failure -C Debug -R domain` passed. 1 domain/core test executable ran successfully.
@@ -0,0 +1,98 @@
# Domain Runtime Storage Implementation Plan
## Objective
Make `Domain` store runtime model objects as its canonical model representation instead of persisting parser-style definition DTOs.
This follows the approved direction:
```text
Abaqus .inp parser
-> temporary parsed or semantic records
-> factory / DomainBuilder
-> Domain owns runtime objects
```
The parser and factory are not implemented in this phase. Definition DTOs may remain as temporary parser/factory-local records, but `Domain` must not use them as persistent storage.
## Phase Overview
1. `runtime-storage-contract`
- Record the migration contract and clarify which model objects are canonical in `Domain`.
- Keep parser/factory work out of scope.
2. `element-material-property-runtime-storage`
- Store elements as `fesa::element::Element`.
- Store materials as `fesa::material::Material`.
- Store shell properties as `fesa::property::ShellProperty`.
- Move `ElementType` to `ModelTypes.hpp` so runtime element interfaces do not depend on `ElementDefinition`.
3. `load-boundary-runtime-storage`
- Store loads as `fesa::load::Load`.
- Store boundary conditions as `fesa::boundary::BoundaryCondition`.
- Validate runtime `NodalLoad` and `SinglePointConstraint` node references during insertion.
4. `step-and-set-runtime-references`
- Validate element sets against runtime elements.
- Validate step load and boundary indices against runtime containers.
- Keep `LinearStaticStepDefinition` as a step configuration record until a dedicated runtime step object is designed.
5. `legacy-definition-extraction`
- Remove `core::*Definition` DTOs from `Domain` includes, fields, and public API.
- Keep focused DTO tests only for parser/factory-local record compatibility.
6. `validation-report-handoff`
- Run harness and CMake/CTest validation.
- Update project handoff documents with concrete evidence.
## Canonical Domain Storage
`Domain` stores:
- `fesa::core::Node` values;
- `std::unique_ptr<fesa::element::Element>`;
- `std::unique_ptr<fesa::material::Material>`;
- `fesa::property::ShellProperty` values;
- `std::unique_ptr<fesa::load::Load>`;
- `std::unique_ptr<fesa::boundary::BoundaryCondition>`;
- node sets and element sets by id;
- `LinearStaticStepDefinition` as a temporary step configuration record.
`Domain` must not store:
- `ElementDefinition`;
- `LinearElasticMaterialDefinition`;
- `ShellPropertyDefinition`;
- `NodalLoadDefinition`;
- `fesa::core::BoundaryCondition`.
## Non-Goals
- No Abaqus parser implementation.
- No factory/registry implementation.
- No MITC4 stiffness, mass, residual, stress, or tangent implementation.
- No equation numbering or solver state in `Domain`.
- No HDF5, MKL, TBB, reference artifact, tolerance, or formulation changes.
## Tests
Primary C++ tests:
- `/tests/core/domain_storage_test.cpp`
- `/tests/core/domain_model_object_test.cpp`
- `/tests/core/model_types_test.cpp`
- `/tests/element/element_base_test.cpp`
- `/tests/element/mitc4_element_model_test.cpp`
- `/tests/material/material_base_test.cpp`
- `/tests/property/shell_property_test.cpp`
- `/tests/load/load_base_test.cpp`
- `/tests/boundary/boundary_base_test.cpp`
Required validation:
```powershell
ctest --test-dir build/msvc-debug --output-on-failure -C Debug -R "domain|model-object"
python -m unittest discover -s scripts -p "test_*.py"
python scripts/validate_workspace.py
git diff --check
```
+22 -48
View File
@@ -1,17 +1,13 @@
#pragma once #pragma once
#include "fesa/boundary/BoundaryCondition.hpp" #include "fesa/boundary/BoundaryCondition.hpp"
#include "fesa/core/BoundaryCondition.hpp"
#include "fesa/core/ElementDefinition.hpp"
#include "fesa/core/LoadDefinition.hpp"
#include "fesa/core/MaterialDefinition.hpp"
#include "fesa/core/ModelTypes.hpp" #include "fesa/core/ModelTypes.hpp"
#include "fesa/core/Node.hpp" #include "fesa/core/Node.hpp"
#include "fesa/core/PropertyDefinition.hpp"
#include "fesa/core/StepDefinition.hpp" #include "fesa/core/StepDefinition.hpp"
#include "fesa/element/Element.hpp" #include "fesa/element/Element.hpp"
#include "fesa/load/Load.hpp" #include "fesa/load/Load.hpp"
#include "fesa/material/Material.hpp" #include "fesa/material/Material.hpp"
#include "fesa/property/ShellProperty.hpp"
#include <cstddef> #include <cstddef>
#include <memory> #include <memory>
@@ -24,33 +20,29 @@ namespace fesa::core {
class Domain { class Domain {
public: public:
void addNode(Node node); void addNode(Node node);
void addElement(ElementDefinition element); void addElement(std::unique_ptr<fesa::element::Element> element);
void addMaterial(LinearElasticMaterialDefinition material); void addMaterial(std::unique_ptr<fesa::material::Material> material);
void addShellProperty(ShellPropertyDefinition property); void addShellProperty(fesa::property::ShellProperty property);
void addNodeSet(std::string name, std::vector<NodeId> node_ids); void addNodeSet(std::string name, std::vector<NodeId> node_ids);
void addElementSet(std::string name, std::vector<ElementId> element_ids); void addElementSet(std::string name, std::vector<ElementId> element_ids);
std::size_t addBoundaryCondition(BoundaryCondition condition); std::size_t addBoundaryCondition(std::unique_ptr<fesa::boundary::BoundaryCondition> boundary);
std::size_t addNodalLoad(NodalLoadDefinition load); std::size_t addLoad(std::unique_ptr<fesa::load::Load> load);
void addStep(LinearStaticStepDefinition step); void addStep(LinearStaticStepDefinition step);
void addElementObject(std::unique_ptr<fesa::element::Element> element);
void addMaterialObject(std::unique_ptr<fesa::material::Material> material);
std::size_t addLoadObject(std::unique_ptr<fesa::load::Load> load);
std::size_t addBoundaryObject(std::unique_ptr<fesa::boundary::BoundaryCondition> boundary);
const Node* findNode(NodeId id) const noexcept; const Node* findNode(NodeId id) const noexcept;
const Node& node(NodeId id) const; const Node& node(NodeId id) const;
std::size_t nodeCount() const noexcept; std::size_t nodeCount() const noexcept;
const ElementDefinition* findElement(ElementId id) const noexcept; const fesa::element::Element* findElement(ElementId id) const noexcept;
const ElementDefinition& element(ElementId id) const; const fesa::element::Element& element(ElementId id) const;
std::size_t elementCount() const noexcept; std::size_t elementCount() const noexcept;
const LinearElasticMaterialDefinition* findMaterial(MaterialId id) const noexcept; const fesa::material::Material* findMaterial(MaterialId id) const noexcept;
const LinearElasticMaterialDefinition& material(MaterialId id) const; const fesa::material::Material& material(MaterialId id) const;
std::size_t materialCount() const noexcept; std::size_t materialCount() const noexcept;
const ShellPropertyDefinition* findShellProperty(PropertyId id) const noexcept; const fesa::property::ShellProperty* findShellProperty(PropertyId id) const noexcept;
const ShellPropertyDefinition& shellProperty(PropertyId id) const; const fesa::property::ShellProperty& shellProperty(PropertyId id) const;
std::size_t shellPropertyCount() const noexcept; std::size_t shellPropertyCount() const noexcept;
const std::vector<NodeId>* findNodeSet(const std::string& name) const noexcept; const std::vector<NodeId>* findNodeSet(const std::string& name) const noexcept;
@@ -61,46 +53,28 @@ public:
const std::vector<ElementId>& elementSet(const std::string& name) const; const std::vector<ElementId>& elementSet(const std::string& name) const;
std::size_t elementSetCount() const noexcept; std::size_t elementSetCount() const noexcept;
const BoundaryCondition& boundaryCondition(std::size_t index) const; const fesa::boundary::BoundaryCondition* findBoundaryCondition(std::size_t index) const noexcept;
const fesa::boundary::BoundaryCondition& boundaryCondition(std::size_t index) const;
std::size_t boundaryConditionCount() const noexcept; std::size_t boundaryConditionCount() const noexcept;
const NodalLoadDefinition& nodalLoad(std::size_t index) const; const fesa::load::Load* findLoad(std::size_t index) const noexcept;
std::size_t nodalLoadCount() const noexcept; const fesa::load::Load& load(std::size_t index) const;
std::size_t loadCount() const noexcept;
const LinearStaticStepDefinition* findStep(StepId id) const noexcept; const LinearStaticStepDefinition* findStep(StepId id) const noexcept;
const LinearStaticStepDefinition& step(StepId id) const; const LinearStaticStepDefinition& step(StepId id) const;
std::size_t stepCount() const noexcept; std::size_t stepCount() const noexcept;
const fesa::element::Element* findElementObject(ElementId id) const noexcept;
const fesa::element::Element& elementObject(ElementId id) const;
std::size_t elementObjectCount() const noexcept;
const fesa::material::Material* findMaterialObject(MaterialId id) const noexcept;
const fesa::material::Material& materialObject(MaterialId id) const;
std::size_t materialObjectCount() const noexcept;
const fesa::load::Load* findLoadObject(std::size_t index) const noexcept;
const fesa::load::Load& loadObject(std::size_t index) const;
std::size_t loadObjectCount() const noexcept;
const fesa::boundary::BoundaryCondition* findBoundaryObject(std::size_t index) const noexcept;
const fesa::boundary::BoundaryCondition& boundaryObject(std::size_t index) const;
std::size_t boundaryObjectCount() const noexcept;
private: private:
std::unordered_map<NodeId, Node> nodes_; std::unordered_map<NodeId, Node> nodes_;
std::unordered_map<ElementId, ElementDefinition> elements_; std::unordered_map<ElementId, std::unique_ptr<fesa::element::Element>> elements_;
std::unordered_map<MaterialId, LinearElasticMaterialDefinition> materials_; std::unordered_map<MaterialId, std::unique_ptr<fesa::material::Material>> materials_;
std::unordered_map<PropertyId, ShellPropertyDefinition> shell_properties_; std::unordered_map<PropertyId, fesa::property::ShellProperty> shell_properties_;
std::unordered_map<std::string, std::vector<NodeId>> node_sets_; std::unordered_map<std::string, std::vector<NodeId>> node_sets_;
std::unordered_map<std::string, std::vector<ElementId>> element_sets_; std::unordered_map<std::string, std::vector<ElementId>> element_sets_;
std::vector<BoundaryCondition> boundary_conditions_; std::vector<std::unique_ptr<fesa::boundary::BoundaryCondition>> boundary_conditions_;
std::vector<NodalLoadDefinition> nodal_loads_; std::vector<std::unique_ptr<fesa::load::Load>> loads_;
std::unordered_map<StepId, LinearStaticStepDefinition> steps_; std::unordered_map<StepId, LinearStaticStepDefinition> steps_;
std::unordered_map<ElementId, std::unique_ptr<fesa::element::Element>> element_objects_;
std::unordered_map<MaterialId, std::unique_ptr<fesa::material::Material>> material_objects_;
std::vector<std::unique_ptr<fesa::load::Load>> load_objects_;
std::vector<std::unique_ptr<fesa::boundary::BoundaryCondition>> boundary_objects_;
}; };
} // namespace fesa::core } // namespace fesa::core
-4
View File
@@ -6,10 +6,6 @@
namespace fesa::core { namespace fesa::core {
enum class ElementType {
Mitc4
};
class ElementDefinition { class ElementDefinition {
public: public:
ElementDefinition( ElementDefinition(
+4
View File
@@ -21,6 +21,10 @@ enum class Dof : std::uint8_t {
UR3 = 5 UR3 = 5
}; };
enum class ElementType {
Mitc4
};
constexpr std::size_t kDofPerNode = 6; constexpr std::size_t kDofPerNode = 6;
} // namespace fesa::core } // namespace fesa::core
+1 -1
View File
@@ -1,6 +1,6 @@
#pragma once #pragma once
#include "fesa/core/ElementDefinition.hpp" #include "fesa/core/ModelTypes.hpp"
#include <array> #include <array>
#include <cstddef> #include <cstddef>
+42
View File
@@ -0,0 +1,42 @@
{
"project": "FESA Structural Solver",
"phase": "domain-runtime-storage",
"steps": [
{
"step": 0,
"name": "runtime-storage-contract",
"status": "completed",
"summary": "Created the Domain runtime storage implementation plan and recorded the parser-record-to-factory-to-runtime-Domain contract."
},
{
"step": 1,
"name": "element-material-property-runtime-storage",
"status": "completed",
"summary": "Migrated Domain element, material, and shell property storage to runtime objects and moved ElementType to ModelTypes."
},
{
"step": 2,
"name": "load-boundary-runtime-storage",
"status": "completed",
"summary": "Migrated Domain load and boundary storage to runtime polymorphic objects with node-reference and duplicate-key validation."
},
{
"step": 3,
"name": "step-and-set-runtime-references",
"status": "completed",
"summary": "Updated element set and linear static step validation to reference runtime element, load, and boundary containers."
},
{
"step": 4,
"name": "legacy-definition-extraction",
"status": "completed",
"summary": "Removed core definition DTOs from Domain public API and persistent storage while keeping focused definition tests for parser/factory records."
},
{
"step": 5,
"name": "validation-report-handoff",
"status": "completed",
"summary": "Validated with script self-tests, targeted CTest, workspace validation, and git diff whitespace checks."
}
]
}
+50
View File
@@ -0,0 +1,50 @@
# Step 0: runtime-storage-contract
## Read First
Read these files before editing:
- `/AGENTS.md`
- `/docs/PLAN.md`
- `/docs/PROGRESS.md`
- `/docs/WORKNOTE.md`
- `/docs/AGENT_RULES.md`
- `/docs/ARCHITECTURE.md`
- `/docs/ADR.md`
- `/docs/implementation-plans/domain-model-foundation-implementation-plan.md`
- `/docs/implementation-plans/analysis-model-objects-implementation-plan.md`
- `/include/fesa/core/Domain.hpp`
- `/src/core/Domain.cpp`
## Task
Create `/docs/implementation-plans/domain-runtime-storage-implementation-plan.md`.
The plan must state that `Domain` owns runtime model objects as its canonical storage:
- nodes remain value objects under `fesa::core::Node`;
- elements are stored as `fesa::element::Element` objects;
- materials are stored as `fesa::material::Material` objects;
- shell properties are stored as `fesa::property::ShellProperty` runtime objects;
- loads are stored as `fesa::load::Load` objects;
- boundary conditions are stored as `fesa::boundary::BoundaryCondition` objects;
- steps may remain `LinearStaticStepDefinition` until an analysis-step object is introduced, but step indices must reference runtime load and boundary containers;
- `core::*Definition` classes may remain as parser/factory-local records, but `Domain` must not persist them.
The plan must also list the migration order and identify the C++ tests that will be rewritten or added.
## Tests To Write First
- Documentation-only step. No C++ test is required in this step.
## Acceptance Criteria
```powershell
python -m unittest discover -s scripts -p "test_*.py"
```
## Verification Notes
1. Confirm the plan does not introduce parser implementation work.
2. Confirm the plan does not change MITC4 formulation, reference artifacts, or tolerance policy.
3. Update `phases/domain-runtime-storage/index.json` for this step result.
+64
View File
@@ -0,0 +1,64 @@
# Step 1: element-material-property-runtime-storage
## Read First
Read these files before editing:
- `/AGENTS.md`
- `/docs/AGENT_RULES.md`
- `/docs/implementation-plans/domain-runtime-storage-implementation-plan.md`
- `/include/fesa/core/Domain.hpp`
- `/include/fesa/core/ModelTypes.hpp`
- `/include/fesa/core/ElementDefinition.hpp`
- `/include/fesa/element/Element.hpp`
- `/include/fesa/element/Mitc4Element.hpp`
- `/include/fesa/material/Material.hpp`
- `/include/fesa/material/LinearElasticMaterial.hpp`
- `/include/fesa/property/ShellProperty.hpp`
- `/src/core/Domain.cpp`
- `/tests/core/domain_storage_test.cpp`
- `/tests/core/domain_model_object_test.cpp`
## Task
Make runtime element, material, and shell property objects the canonical Domain storage.
Required API shape:
- `void Domain::addElement(std::unique_ptr<fesa::element::Element> element)`
- `const fesa::element::Element* Domain::findElement(ElementId id) const noexcept`
- `const fesa::element::Element& Domain::element(ElementId id) const`
- `std::size_t Domain::elementCount() const noexcept`
- `void Domain::addMaterial(std::unique_ptr<fesa::material::Material> material)`
- `const fesa::material::Material* Domain::findMaterial(MaterialId id) const noexcept`
- `const fesa::material::Material& Domain::material(MaterialId id) const`
- `std::size_t Domain::materialCount() const noexcept`
- `void Domain::addShellProperty(fesa::property::ShellProperty property)`
- `const fesa::property::ShellProperty* Domain::findShellProperty(PropertyId id) const noexcept`
- `const fesa::property::ShellProperty& Domain::shellProperty(PropertyId id) const`
- `std::size_t Domain::shellPropertyCount() const noexcept`
Rules:
- Move `ElementType` to `/include/fesa/core/ModelTypes.hpp` so runtime `Element` no longer includes `/include/fesa/core/ElementDefinition.hpp`.
- `Domain::addShellProperty` must reject duplicate property ids and missing material ids.
- `Domain::addElement` must reject null pointers, duplicate element ids, missing node ids, and missing shell property ids.
- Keep C++17/MSVC compatibility and RAII ownership.
## Tests To Write First
- Rewrite or extend `/tests/core/domain_storage_test.cpp` so element, material, and shell property storage uses runtime objects instead of `ElementDefinition`, `LinearElasticMaterialDefinition`, and `ShellPropertyDefinition`.
- Add or adjust a test that proves `Element` no longer depends on `ElementDefinition` for `ElementType`.
## Acceptance Criteria
```powershell
ctest --test-dir build/msvc-debug --output-on-failure -C Debug -R "domain|model-object"
python scripts/validate_workspace.py
```
## Verification Notes
1. Run the targeted CTest before and after implementation to capture RED then GREEN.
2. Do not delete definition classes in this step; only remove them from Domain storage.
3. Update `phases/domain-runtime-storage/index.json` for this step result.
+59
View File
@@ -0,0 +1,59 @@
# Step 2: load-boundary-runtime-storage
## Read First
Read these files before editing:
- `/AGENTS.md`
- `/docs/AGENT_RULES.md`
- `/docs/implementation-plans/domain-runtime-storage-implementation-plan.md`
- `/include/fesa/core/Domain.hpp`
- `/include/fesa/load/Load.hpp`
- `/include/fesa/load/NodalLoad.hpp`
- `/include/fesa/boundary/BoundaryCondition.hpp`
- `/include/fesa/boundary/SinglePointConstraint.hpp`
- `/src/core/Domain.cpp`
- `/tests/core/domain_storage_test.cpp`
- `/tests/core/domain_model_object_test.cpp`
## Task
Make runtime load and boundary objects the canonical Domain storage.
Required API shape:
- `std::size_t Domain::addLoad(std::unique_ptr<fesa::load::Load> load)`
- `const fesa::load::Load* Domain::findLoad(std::size_t index) const noexcept`
- `const fesa::load::Load& Domain::load(std::size_t index) const`
- `std::size_t Domain::loadCount() const noexcept`
- `std::size_t Domain::addBoundaryCondition(std::unique_ptr<fesa::boundary::BoundaryCondition> boundary)`
- `const fesa::boundary::BoundaryCondition* Domain::findBoundaryCondition(std::size_t index) const noexcept`
- `const fesa::boundary::BoundaryCondition& Domain::boundaryCondition(std::size_t index) const`
- `std::size_t Domain::boundaryConditionCount() const noexcept`
Rules:
- Reject null load and boundary pointers.
- `NodalLoad` must reference an existing node.
- `SinglePointConstraint` must reference an existing node.
- Duplicate `(node, dof)` nodal loads must throw.
- Duplicate `(node, dof)` single-point constraints must throw.
- Runtime load and boundary storage must not use `core::NodalLoadDefinition` or `core::BoundaryCondition`.
## Tests To Write First
- Rewrite or extend `/tests/core/domain_storage_test.cpp` so load and boundary storage uses runtime objects.
- Add missing-node tests for `NodalLoad` and `SinglePointConstraint` runtime insertion.
## Acceptance Criteria
```powershell
ctest --test-dir build/msvc-debug --output-on-failure -C Debug -R "domain|model-object"
python scripts/validate_workspace.py
```
## Verification Notes
1. Run targeted CTest before production edits and confirm failure.
2. Keep solver state out of `Domain`.
3. Update `phases/domain-runtime-storage/index.json` for this step result.
+42
View File
@@ -0,0 +1,42 @@
# Step 3: step-and-set-runtime-references
## Read First
Read these files before editing:
- `/AGENTS.md`
- `/docs/AGENT_RULES.md`
- `/docs/implementation-plans/domain-runtime-storage-implementation-plan.md`
- `/include/fesa/core/Domain.hpp`
- `/include/fesa/core/StepDefinition.hpp`
- `/src/core/Domain.cpp`
- `/tests/core/domain_storage_test.cpp`
## Task
Make Domain set and step validation reference runtime storage.
Rules:
- `Domain::addElementSet` must validate ids against runtime elements stored by `Domain::findElement`.
- `Domain::addStep(LinearStaticStepDefinition)` may remain for now, but its boundary and load indices must validate against runtime boundary and load containers.
- Failed insertions must not mutate Domain counts.
- No equation ids, solver vectors, residuals, reactions, current time, or iteration state may be added to Domain.
## Tests To Write First
- Rewrite `/tests/core/domain_storage_test.cpp` set and step tests so they build elements, loads, and boundary conditions through runtime object APIs.
- Preserve count-stability tests for failed insertions.
## Acceptance Criteria
```powershell
ctest --test-dir build/msvc-debug --output-on-failure -C Debug -R domain
python scripts/validate_workspace.py
```
## Verification Notes
1. Run targeted CTest before production edits and confirm failure.
2. Keep `LinearStaticStepDefinition` as the only remaining step record until a separate step-model phase exists.
3. Update `phases/domain-runtime-storage/index.json` for this step result.
+47
View File
@@ -0,0 +1,47 @@
# Step 4: legacy-definition-extraction
## Read First
Read these files before editing:
- `/AGENTS.md`
- `/docs/AGENT_RULES.md`
- `/docs/implementation-plans/domain-runtime-storage-implementation-plan.md`
- `/include/fesa/core/Domain.hpp`
- `/src/core/Domain.cpp`
- `/tests/core/domain_storage_test.cpp`
- `/CMakeLists.txt`
## Task
Remove parser-definition DTO persistence from `Domain`.
Rules:
- `Domain` must no longer include or store:
- `fesa/core/BoundaryCondition.hpp`
- `fesa/core/ElementDefinition.hpp`
- `fesa/core/LoadDefinition.hpp`
- `fesa/core/MaterialDefinition.hpp`
- `fesa/core/PropertyDefinition.hpp`
- `Domain` may still include `StepDefinition.hpp` until a runtime step object is designed.
- Definition classes may remain in the repository and their focused tests may remain, but they must not be part of Domain storage or Domain public API.
- Do not implement parser or factory behavior in this step.
## Tests To Write First
- Add or update a compile-time assertion in `/tests/core/domain_storage_test.cpp` or a dedicated domain API test proving Domain retrieval returns runtime types for elements, materials, properties, loads, and boundary conditions.
## Acceptance Criteria
```powershell
python -m unittest discover -s scripts -p "test_*.py"
python scripts/validate_workspace.py
git diff --check
```
## Verification Notes
1. Confirm no `Domain` method returns a definition DTO except `LinearStaticStepDefinition`.
2. Confirm `include/fesa/element/Element.hpp` no longer includes `ElementDefinition.hpp`.
3. Update `phases/domain-runtime-storage/index.json` for this step result.
+42
View File
@@ -0,0 +1,42 @@
# Step 5: validation-report-handoff
## Read First
Read these files before editing:
- `/AGENTS.md`
- `/docs/PLAN.md`
- `/docs/PROGRESS.md`
- `/docs/WORKNOTE.md`
- `/docs/AGENT_RULES.md`
- `/docs/implementation-plans/domain-runtime-storage-implementation-plan.md`
- `/phases/domain-runtime-storage/index.json`
## Task
Record validation evidence and handoff notes after the runtime Domain storage migration.
Required updates:
- Update `/docs/PROGRESS.md` with completed runtime Domain storage work and validation commands.
- Update `/docs/PLAN.md` only if this migration changes sequencing or acceptance criteria.
- Update `/docs/WORKNOTE.md` only if there were failures, repeated mistakes, or environment traps.
- Mark `/phases/domain-runtime-storage/index.json` steps and `/phases/index.json` phase status according to actual results.
## Tests To Write First
- Documentation/reporting step. No new C++ test is required.
## Acceptance Criteria
```powershell
python -m unittest discover -s scripts -p "test_*.py"
python scripts/validate_workspace.py
git diff --check
```
## Verification Notes
1. Do not claim numerical solver correctness.
2. Do not mark the phase completed unless all previous steps are complete and validation passed.
3. Keep validation evidence concrete: command, result, and scope.
+4
View File
@@ -7,6 +7,10 @@
{ {
"dir": "analysis-model-objects", "dir": "analysis-model-objects",
"status": "completed" "status": "completed"
},
{
"dir": "domain-runtime-storage",
"status": "completed"
} }
] ]
} }
+89 -164
View File
@@ -1,6 +1,11 @@
#include "fesa/core/Domain.hpp" #include "fesa/core/Domain.hpp"
#include "fesa/boundary/SinglePointConstraint.hpp" #include "fesa/boundary/SinglePointConstraint.hpp"
#include "fesa/core/BoundaryCondition.hpp"
#include "fesa/core/ElementDefinition.hpp"
#include "fesa/core/LoadDefinition.hpp"
#include "fesa/core/MaterialDefinition.hpp"
#include "fesa/core/PropertyDefinition.hpp"
#include "fesa/load/NodalLoad.hpp" #include "fesa/load/NodalLoad.hpp"
#include <stdexcept> #include <stdexcept>
@@ -154,28 +159,37 @@ void Domain::addNode(Node node) {
} }
} }
void Domain::addElement(ElementDefinition element) { void Domain::addElement(std::unique_ptr<fesa::element::Element> element) {
const ElementId id = element.id(); if (!element) {
throw std::invalid_argument("element is null");
}
const ElementId id = element->id();
if (elements_.find(id) != elements_.end()) { if (elements_.find(id) != elements_.end()) {
throw std::invalid_argument("duplicate element id"); throw std::invalid_argument("duplicate element id");
} }
for (const NodeId node_id : element.connectivity()) { for (const NodeId node_id : element->connectivity()) {
if (findNode(node_id) == nullptr) { if (findNode(node_id) == nullptr) {
throw std::invalid_argument("element references missing node id"); throw std::invalid_argument("element references missing node id");
} }
} }
if (findShellProperty(element->propertyId()) == nullptr) {
throw std::invalid_argument("element references missing shell property id");
}
elements_.emplace(id, std::move(element)); elements_.emplace(id, std::move(element));
} }
void Domain::addMaterial(LinearElasticMaterialDefinition material) { void Domain::addMaterial(std::unique_ptr<fesa::material::Material> material) {
const MaterialId id = material.id(); if (!material) {
throw std::invalid_argument("material is null");
}
const MaterialId id = material->id();
const auto inserted = materials_.emplace(id, std::move(material)); const auto inserted = materials_.emplace(id, std::move(material));
if (!inserted.second) { if (!inserted.second) {
throw std::invalid_argument("duplicate material id"); throw std::invalid_argument("duplicate material id");
} }
} }
void Domain::addShellProperty(ShellPropertyDefinition property) { void Domain::addShellProperty(fesa::property::ShellProperty property) {
const PropertyId id = property.id(); const PropertyId id = property.id();
if (shell_properties_.find(id) != shell_properties_.end()) { if (shell_properties_.find(id) != shell_properties_.end()) {
throw std::invalid_argument("duplicate shell property id"); throw std::invalid_argument("duplicate shell property id");
@@ -210,21 +224,48 @@ void Domain::addElementSet(std::string name, std::vector<ElementId> element_ids)
element_sets_.emplace(std::move(name), std::move(element_ids)); element_sets_.emplace(std::move(name), std::move(element_ids));
} }
std::size_t Domain::addBoundaryCondition(BoundaryCondition condition) { std::size_t Domain::addBoundaryCondition(std::unique_ptr<fesa::boundary::BoundaryCondition> boundary) {
if (findNode(condition.nodeId()) == nullptr) { if (!boundary) {
throw std::invalid_argument("boundary condition is null");
}
if (const auto* spc = dynamic_cast<const fesa::boundary::SinglePointConstraint*>(boundary.get())) {
if (findNode(spc->nodeId()) == nullptr) {
throw std::invalid_argument("boundary condition references missing node id"); throw std::invalid_argument("boundary condition references missing node id");
} }
for (const auto& existing : boundary_conditions_) {
const auto* existing_spc =
dynamic_cast<const fesa::boundary::SinglePointConstraint*>(existing.get());
if (existing_spc != nullptr &&
existing_spc->nodeId() == spc->nodeId() &&
existing_spc->dof() == spc->dof()) {
throw std::invalid_argument("duplicate boundary condition key");
}
}
}
const std::size_t index = boundary_conditions_.size(); const std::size_t index = boundary_conditions_.size();
boundary_conditions_.push_back(condition); boundary_conditions_.push_back(std::move(boundary));
return index; return index;
} }
std::size_t Domain::addNodalLoad(NodalLoadDefinition load) { std::size_t Domain::addLoad(std::unique_ptr<fesa::load::Load> load) {
if (findNode(load.nodeId()) == nullptr) { if (!load) {
throw std::invalid_argument("load is null");
}
if (const auto* nodal = dynamic_cast<const fesa::load::NodalLoad*>(load.get())) {
if (findNode(nodal->nodeId()) == nullptr) {
throw std::invalid_argument("nodal load references missing node id"); throw std::invalid_argument("nodal load references missing node id");
} }
const std::size_t index = nodal_loads_.size(); for (const auto& existing : loads_) {
nodal_loads_.push_back(load); const auto* existing_nodal = dynamic_cast<const fesa::load::NodalLoad*>(existing.get());
if (existing_nodal != nullptr &&
existing_nodal->nodeId() == nodal->nodeId() &&
existing_nodal->dof() == nodal->dof()) {
throw std::invalid_argument("duplicate nodal load key");
}
}
}
const std::size_t index = loads_.size();
loads_.push_back(std::move(load));
return index; return index;
} }
@@ -239,79 +280,13 @@ void Domain::addStep(LinearStaticStepDefinition step) {
} }
} }
for (const std::size_t index : step.loadIndices()) { for (const std::size_t index : step.loadIndices()) {
if (index >= nodal_loads_.size()) { if (index >= loads_.size()) {
throw std::invalid_argument("step references missing nodal load"); throw std::invalid_argument("step references missing load");
} }
} }
steps_.emplace(id, std::move(step)); steps_.emplace(id, std::move(step));
} }
void Domain::addElementObject(std::unique_ptr<fesa::element::Element> element) {
if (!element) {
throw std::invalid_argument("element object is null");
}
const ElementId id = element->id();
if (element_objects_.find(id) != element_objects_.end()) {
throw std::invalid_argument("duplicate element object id");
}
for (const NodeId node_id : element->connectivity()) {
if (findNode(node_id) == nullptr) {
throw std::invalid_argument("element object references missing node id");
}
}
element_objects_.emplace(id, std::move(element));
}
void Domain::addMaterialObject(std::unique_ptr<fesa::material::Material> material) {
if (!material) {
throw std::invalid_argument("material object is null");
}
const MaterialId id = material->id();
const auto inserted = material_objects_.emplace(id, std::move(material));
if (!inserted.second) {
throw std::invalid_argument("duplicate material object id");
}
}
std::size_t Domain::addLoadObject(std::unique_ptr<fesa::load::Load> load) {
if (!load) {
throw std::invalid_argument("load object is null");
}
if (const auto* nodal = dynamic_cast<const fesa::load::NodalLoad*>(load.get())) {
for (const auto& existing : load_objects_) {
const auto* existing_nodal = dynamic_cast<const fesa::load::NodalLoad*>(existing.get());
if (existing_nodal != nullptr &&
existing_nodal->nodeId() == nodal->nodeId() &&
existing_nodal->dof() == nodal->dof()) {
throw std::invalid_argument("duplicate nodal load key");
}
}
}
const std::size_t index = load_objects_.size();
load_objects_.push_back(std::move(load));
return index;
}
std::size_t Domain::addBoundaryObject(std::unique_ptr<fesa::boundary::BoundaryCondition> boundary) {
if (!boundary) {
throw std::invalid_argument("boundary object is null");
}
if (const auto* spc = dynamic_cast<const fesa::boundary::SinglePointConstraint*>(boundary.get())) {
for (const auto& existing : boundary_objects_) {
const auto* existing_spc =
dynamic_cast<const fesa::boundary::SinglePointConstraint*>(existing.get());
if (existing_spc != nullptr &&
existing_spc->nodeId() == spc->nodeId() &&
existing_spc->dof() == spc->dof()) {
throw std::invalid_argument("duplicate boundary object key");
}
}
}
const std::size_t index = boundary_objects_.size();
boundary_objects_.push_back(std::move(boundary));
return index;
}
const Node* Domain::findNode(NodeId id) const noexcept { const Node* Domain::findNode(NodeId id) const noexcept {
const auto it = nodes_.find(id); const auto it = nodes_.find(id);
return it == nodes_.end() ? nullptr : &it->second; return it == nodes_.end() ? nullptr : &it->second;
@@ -329,13 +304,13 @@ std::size_t Domain::nodeCount() const noexcept {
return nodes_.size(); return nodes_.size();
} }
const ElementDefinition* Domain::findElement(ElementId id) const noexcept { const fesa::element::Element* Domain::findElement(ElementId id) const noexcept {
const auto it = elements_.find(id); const auto it = elements_.find(id);
return it == elements_.end() ? nullptr : &it->second; return it == elements_.end() ? nullptr : it->second.get();
} }
const ElementDefinition& Domain::element(ElementId id) const { const fesa::element::Element& Domain::element(ElementId id) const {
const ElementDefinition* found = findElement(id); const fesa::element::Element* found = findElement(id);
if (found == nullptr) { if (found == nullptr) {
throw std::out_of_range("element id not found"); throw std::out_of_range("element id not found");
} }
@@ -346,13 +321,13 @@ std::size_t Domain::elementCount() const noexcept {
return elements_.size(); return elements_.size();
} }
const LinearElasticMaterialDefinition* Domain::findMaterial(MaterialId id) const noexcept { const fesa::material::Material* Domain::findMaterial(MaterialId id) const noexcept {
const auto it = materials_.find(id); const auto it = materials_.find(id);
return it == materials_.end() ? nullptr : &it->second; return it == materials_.end() ? nullptr : it->second.get();
} }
const LinearElasticMaterialDefinition& Domain::material(MaterialId id) const { const fesa::material::Material& Domain::material(MaterialId id) const {
const LinearElasticMaterialDefinition* found = findMaterial(id); const fesa::material::Material* found = findMaterial(id);
if (found == nullptr) { if (found == nullptr) {
throw std::out_of_range("material id not found"); throw std::out_of_range("material id not found");
} }
@@ -363,13 +338,13 @@ std::size_t Domain::materialCount() const noexcept {
return materials_.size(); return materials_.size();
} }
const ShellPropertyDefinition* Domain::findShellProperty(PropertyId id) const noexcept { const fesa::property::ShellProperty* Domain::findShellProperty(PropertyId id) const noexcept {
const auto it = shell_properties_.find(id); const auto it = shell_properties_.find(id);
return it == shell_properties_.end() ? nullptr : &it->second; return it == shell_properties_.end() ? nullptr : &it->second;
} }
const ShellPropertyDefinition& Domain::shellProperty(PropertyId id) const { const fesa::property::ShellProperty& Domain::shellProperty(PropertyId id) const {
const ShellPropertyDefinition* found = findShellProperty(id); const fesa::property::ShellProperty* found = findShellProperty(id);
if (found == nullptr) { if (found == nullptr) {
throw std::out_of_range("shell property id not found"); throw std::out_of_range("shell property id not found");
} }
@@ -414,20 +389,36 @@ std::size_t Domain::elementSetCount() const noexcept {
return element_sets_.size(); return element_sets_.size();
} }
const BoundaryCondition& Domain::boundaryCondition(std::size_t index) const { const fesa::boundary::BoundaryCondition* Domain::findBoundaryCondition(std::size_t index) const noexcept {
return boundary_conditions_.at(index); return index < boundary_conditions_.size() ? boundary_conditions_[index].get() : nullptr;
}
const fesa::boundary::BoundaryCondition& Domain::boundaryCondition(std::size_t index) const {
const fesa::boundary::BoundaryCondition* found = findBoundaryCondition(index);
if (found == nullptr) {
throw std::out_of_range("boundary condition index not found");
}
return *found;
} }
std::size_t Domain::boundaryConditionCount() const noexcept { std::size_t Domain::boundaryConditionCount() const noexcept {
return boundary_conditions_.size(); return boundary_conditions_.size();
} }
const NodalLoadDefinition& Domain::nodalLoad(std::size_t index) const { const fesa::load::Load* Domain::findLoad(std::size_t index) const noexcept {
return nodal_loads_.at(index); return index < loads_.size() ? loads_[index].get() : nullptr;
} }
std::size_t Domain::nodalLoadCount() const noexcept { const fesa::load::Load& Domain::load(std::size_t index) const {
return nodal_loads_.size(); const fesa::load::Load* found = findLoad(index);
if (found == nullptr) {
throw std::out_of_range("load index not found");
}
return *found;
}
std::size_t Domain::loadCount() const noexcept {
return loads_.size();
} }
const LinearStaticStepDefinition* Domain::findStep(StepId id) const noexcept { const LinearStaticStepDefinition* Domain::findStep(StepId id) const noexcept {
@@ -447,70 +438,4 @@ std::size_t Domain::stepCount() const noexcept {
return steps_.size(); return steps_.size();
} }
const fesa::element::Element* Domain::findElementObject(ElementId id) const noexcept {
const auto it = element_objects_.find(id);
return it == element_objects_.end() ? nullptr : it->second.get();
}
const fesa::element::Element& Domain::elementObject(ElementId id) const {
const fesa::element::Element* found = findElementObject(id);
if (found == nullptr) {
throw std::out_of_range("element object id not found");
}
return *found;
}
std::size_t Domain::elementObjectCount() const noexcept {
return element_objects_.size();
}
const fesa::material::Material* Domain::findMaterialObject(MaterialId id) const noexcept {
const auto it = material_objects_.find(id);
return it == material_objects_.end() ? nullptr : it->second.get();
}
const fesa::material::Material& Domain::materialObject(MaterialId id) const {
const fesa::material::Material* found = findMaterialObject(id);
if (found == nullptr) {
throw std::out_of_range("material object id not found");
}
return *found;
}
std::size_t Domain::materialObjectCount() const noexcept {
return material_objects_.size();
}
const fesa::load::Load* Domain::findLoadObject(std::size_t index) const noexcept {
return index < load_objects_.size() ? load_objects_[index].get() : nullptr;
}
const fesa::load::Load& Domain::loadObject(std::size_t index) const {
const fesa::load::Load* found = findLoadObject(index);
if (found == nullptr) {
throw std::out_of_range("load object index not found");
}
return *found;
}
std::size_t Domain::loadObjectCount() const noexcept {
return load_objects_.size();
}
const fesa::boundary::BoundaryCondition* Domain::findBoundaryObject(std::size_t index) const noexcept {
return index < boundary_objects_.size() ? boundary_objects_[index].get() : nullptr;
}
const fesa::boundary::BoundaryCondition& Domain::boundaryObject(std::size_t index) const {
const fesa::boundary::BoundaryCondition* found = findBoundaryObject(index);
if (found == nullptr) {
throw std::out_of_range("boundary object index not found");
}
return *found;
}
std::size_t Domain::boundaryObjectCount() const noexcept {
return boundary_objects_.size();
}
} // namespace fesa::core } // namespace fesa::core
+25 -24
View File
@@ -3,6 +3,7 @@
#include "fesa/element/Mitc4Element.hpp" #include "fesa/element/Mitc4Element.hpp"
#include "fesa/load/NodalLoad.hpp" #include "fesa/load/NodalLoad.hpp"
#include "fesa/material/LinearElasticMaterial.hpp" #include "fesa/material/LinearElasticMaterial.hpp"
#include "fesa/property/ShellProperty.hpp"
#include <memory> #include <memory>
#include <stdexcept> #include <stdexcept>
@@ -31,46 +32,46 @@ fesa::core::Domain populated_domain() {
domain.addNode(fesa::core::Node{2, 1.0, 0.0, 0.0}); domain.addNode(fesa::core::Node{2, 1.0, 0.0, 0.0});
domain.addNode(fesa::core::Node{3, 1.0, 1.0, 0.0}); domain.addNode(fesa::core::Node{3, 1.0, 1.0, 0.0});
domain.addNode(fesa::core::Node{4, 0.0, 1.0, 0.0}); domain.addNode(fesa::core::Node{4, 0.0, 1.0, 0.0});
domain.addMaterial(std::make_unique<fesa::material::LinearElasticMaterial>(700, 210.0, 0.3));
domain.addShellProperty(fesa::property::ShellProperty{500, 700, 0.01});
return domain; return domain;
} }
int domain_owns_element_and_material_objects() { int domain_owns_element_and_material_objects() {
fesa::core::Domain domain = populated_domain(); fesa::core::Domain domain = populated_domain();
domain.addElementObject(std::make_unique<fesa::element::Mitc4Element>( domain.addElement(std::make_unique<fesa::element::Mitc4Element>(
100, 100,
std::array<fesa::core::NodeId, 4>{1, 2, 3, 4}, std::array<fesa::core::NodeId, 4>{1, 2, 3, 4},
500)); 500));
domain.addMaterialObject(std::make_unique<fesa::material::LinearElasticMaterial>(700, 210.0, 0.3));
const fesa::element::Element* element = domain.findElementObject(100); const fesa::element::Element* element = domain.findElement(100);
if (const int result = require(element != nullptr); result != 0) { if (const int result = require(element != nullptr); result != 0) {
return result; return result;
} }
if (const int result = require(element->type() == fesa::core::ElementType::Mitc4); result != 0) { if (const int result = require(element->type() == fesa::core::ElementType::Mitc4); result != 0) {
return result; return result;
} }
if (const int result = require(domain.elementObject(100).propertyId() == 500); result != 0) { if (const int result = require(domain.element(100).propertyId() == 500); result != 0) {
return result; return result;
} }
const fesa::material::Material* material = domain.findMaterialObject(700); const fesa::material::Material* material = domain.findMaterial(700);
if (const int result = require(material != nullptr); result != 0) { if (const int result = require(material != nullptr); result != 0) {
return result; return result;
} }
return require(domain.materialObject(700).id() == 700); return require(domain.material(700).id() == 700);
} }
int duplicate_element_and_material_object_ids_throw() { int duplicate_element_and_material_object_ids_throw() {
fesa::core::Domain domain = populated_domain(); fesa::core::Domain domain = populated_domain();
domain.addElementObject(std::make_unique<fesa::element::Mitc4Element>( domain.addElement(std::make_unique<fesa::element::Mitc4Element>(
100, 100,
std::array<fesa::core::NodeId, 4>{1, 2, 3, 4}, std::array<fesa::core::NodeId, 4>{1, 2, 3, 4},
500)); 500));
domain.addMaterialObject(std::make_unique<fesa::material::LinearElasticMaterial>(700, 210.0, 0.3));
if (const int result = require_throws<std::invalid_argument>([&domain]() { if (const int result = require_throws<std::invalid_argument>([&domain]() {
domain.addElementObject(std::make_unique<fesa::element::Mitc4Element>( domain.addElement(std::make_unique<fesa::element::Mitc4Element>(
100, 100,
std::array<fesa::core::NodeId, 4>{1, 2, 3, 4}, std::array<fesa::core::NodeId, 4>{1, 2, 3, 4},
500)); 500));
@@ -79,36 +80,36 @@ int duplicate_element_and_material_object_ids_throw() {
return result; return result;
} }
return require_throws<std::invalid_argument>([&domain]() { return require_throws<std::invalid_argument>([&domain]() {
domain.addMaterialObject(std::make_unique<fesa::material::LinearElasticMaterial>(700, 100.0, 0.25)); domain.addMaterial(std::make_unique<fesa::material::LinearElasticMaterial>(700, 100.0, 0.25));
}); });
} }
int missing_model_object_direct_lookup_throws() { int missing_model_object_direct_lookup_throws() {
const fesa::core::Domain domain; const fesa::core::Domain domain;
if (const int result = require(domain.findElementObject(404) == nullptr); result != 0) { if (const int result = require(domain.findElement(404) == nullptr); result != 0) {
return result; return result;
} }
if (const int result = require(domain.findMaterialObject(404) == nullptr); result != 0) { if (const int result = require(domain.findMaterial(404) == nullptr); result != 0) {
return result; return result;
} }
if (const int result = require_throws<std::out_of_range>([&domain]() { if (const int result = require_throws<std::out_of_range>([&domain]() {
(void)domain.elementObject(404); (void)domain.element(404);
}); });
result != 0) { result != 0) {
return result; return result;
} }
return require_throws<std::out_of_range>([&domain]() { return require_throws<std::out_of_range>([&domain]() {
(void)domain.materialObject(404); (void)domain.material(404);
}); });
} }
int domain_owns_load_and_boundary_objects_by_index() { int domain_owns_load_and_boundary_objects_by_index() {
fesa::core::Domain domain = populated_domain(); fesa::core::Domain domain = populated_domain();
const std::size_t load_index = domain.addLoadObject( const std::size_t load_index = domain.addLoad(
std::make_unique<fesa::load::NodalLoad>(1, fesa::core::Dof::U3, -100.0)); std::make_unique<fesa::load::NodalLoad>(1, fesa::core::Dof::U3, -100.0));
const std::size_t boundary_index = domain.addBoundaryObject( const std::size_t boundary_index = domain.addBoundaryCondition(
std::make_unique<fesa::boundary::SinglePointConstraint>(1, fesa::core::Dof::U1, 0.0)); std::make_unique<fesa::boundary::SinglePointConstraint>(1, fesa::core::Dof::U1, 0.0));
if (const int result = require(load_index == 0); result != 0) { if (const int result = require(load_index == 0); result != 0) {
@@ -117,33 +118,33 @@ int domain_owns_load_and_boundary_objects_by_index() {
if (const int result = require(boundary_index == 0); result != 0) { if (const int result = require(boundary_index == 0); result != 0) {
return result; return result;
} }
if (const int result = require(domain.findLoadObject(load_index) != nullptr); result != 0) { if (const int result = require(domain.findLoad(load_index) != nullptr); result != 0) {
return result; return result;
} }
if (const int result = require(domain.findBoundaryObject(boundary_index) != nullptr); result != 0) { if (const int result = require(domain.findBoundaryCondition(boundary_index) != nullptr); result != 0) {
return result; return result;
} }
if (const int result = require(domain.loadObject(load_index).kind() == fesa::load::LoadKind::Nodal); result != 0) { if (const int result = require(domain.load(load_index).kind() == fesa::load::LoadKind::Nodal); result != 0) {
return result; return result;
} }
return require( return require(
domain.boundaryObject(boundary_index).kind() == domain.boundaryCondition(boundary_index).kind() ==
fesa::boundary::BoundaryConditionKind::SinglePointConstraint); fesa::boundary::BoundaryConditionKind::SinglePointConstraint);
} }
int duplicate_load_and_boundary_keys_throw() { int duplicate_load_and_boundary_keys_throw() {
fesa::core::Domain domain = populated_domain(); fesa::core::Domain domain = populated_domain();
domain.addLoadObject(std::make_unique<fesa::load::NodalLoad>(1, fesa::core::Dof::U3, -100.0)); domain.addLoad(std::make_unique<fesa::load::NodalLoad>(1, fesa::core::Dof::U3, -100.0));
domain.addBoundaryObject(std::make_unique<fesa::boundary::SinglePointConstraint>(1, fesa::core::Dof::U1, 0.0)); domain.addBoundaryCondition(std::make_unique<fesa::boundary::SinglePointConstraint>(1, fesa::core::Dof::U1, 0.0));
if (const int result = require_throws<std::invalid_argument>([&domain]() { if (const int result = require_throws<std::invalid_argument>([&domain]() {
domain.addLoadObject(std::make_unique<fesa::load::NodalLoad>(1, fesa::core::Dof::U3, -200.0)); domain.addLoad(std::make_unique<fesa::load::NodalLoad>(1, fesa::core::Dof::U3, -200.0));
}); });
result != 0) { result != 0) {
return result; return result;
} }
return require_throws<std::invalid_argument>([&domain]() { return require_throws<std::invalid_argument>([&domain]() {
domain.addBoundaryObject(std::make_unique<fesa::boundary::SinglePointConstraint>(1, fesa::core::Dof::U1, 1.0)); domain.addBoundaryCondition(std::make_unique<fesa::boundary::SinglePointConstraint>(1, fesa::core::Dof::U1, 1.0));
}); });
} }
+144 -120
View File
@@ -1,12 +1,14 @@
#include "fesa/core/BoundaryCondition.hpp" #include "fesa/boundary/SinglePointConstraint.hpp"
#include "fesa/core/Domain.hpp" #include "fesa/core/Domain.hpp"
#include "fesa/core/ElementDefinition.hpp"
#include "fesa/core/LoadDefinition.hpp"
#include "fesa/core/MaterialDefinition.hpp"
#include "fesa/core/Node.hpp" #include "fesa/core/Node.hpp"
#include "fesa/core/PropertyDefinition.hpp"
#include "fesa/core/StepDefinition.hpp" #include "fesa/core/StepDefinition.hpp"
#include "fesa/element/Mitc4Element.hpp"
#include "fesa/load/NodalLoad.hpp"
#include "fesa/material/LinearElasticMaterial.hpp"
#include "fesa/property/ShellProperty.hpp"
#include <array>
#include <memory>
#include <stdexcept> #include <stdexcept>
#include <type_traits> #include <type_traits>
@@ -28,6 +30,25 @@ int require_throws(Function&& function) {
return 1; return 1;
} }
void add_four_nodes(fesa::core::Domain& domain) {
domain.addNode(fesa::core::Node{1, 0.0, 0.0, 0.0});
domain.addNode(fesa::core::Node{2, 1.0, 0.0, 0.0});
domain.addNode(fesa::core::Node{3, 1.0, 1.0, 0.0});
domain.addNode(fesa::core::Node{4, 0.0, 1.0, 0.0});
}
void add_material_and_property(fesa::core::Domain& domain) {
domain.addMaterial(std::make_unique<fesa::material::LinearElasticMaterial>(700, 210.0, 0.3));
domain.addShellProperty(fesa::property::ShellProperty{500, 700, 0.01});
}
void add_element(fesa::core::Domain& domain) {
domain.addElement(std::make_unique<fesa::element::Mitc4Element>(
100,
std::array<fesa::core::NodeId, 4>{1, 2, 3, 4},
500));
}
int add_and_retrieve_node_by_id() { int add_and_retrieve_node_by_id() {
fesa::core::Domain domain; fesa::core::Domain domain;
@@ -55,11 +76,7 @@ int add_and_retrieve_node_by_id() {
} }
const fesa::core::Node& direct = domain.node(10); const fesa::core::Node& direct = domain.node(10);
if (const int result = require(direct.id() == 10); result != 0) { return require(direct.id() == 10);
return result;
}
return 0;
} }
int missing_node_lookup_contracts() { int missing_node_lookup_contracts() {
@@ -83,28 +100,18 @@ int duplicate_node_id_throws() {
}); });
} }
void add_four_nodes(fesa::core::Domain& domain) {
domain.addNode(fesa::core::Node{1, 0.0, 0.0, 0.0});
domain.addNode(fesa::core::Node{2, 1.0, 0.0, 0.0});
domain.addNode(fesa::core::Node{3, 1.0, 1.0, 0.0});
domain.addNode(fesa::core::Node{4, 0.0, 1.0, 0.0});
}
int add_and_retrieve_element_by_id() { int add_and_retrieve_element_by_id() {
fesa::core::Domain domain; fesa::core::Domain domain;
add_four_nodes(domain); add_four_nodes(domain);
add_material_and_property(domain);
domain.addElement(fesa::core::ElementDefinition{ add_element(domain);
100,
fesa::core::ElementType::Mitc4,
{1, 2, 3, 4},
500});
if (const int result = require(domain.elementCount() == 1); result != 0) { if (const int result = require(domain.elementCount() == 1); result != 0) {
return result; return result;
} }
const fesa::core::ElementDefinition* found = domain.findElement(100); const fesa::element::Element* found = domain.findElement(100);
if (const int result = require(found != nullptr); result != 0) { if (const int result = require(found != nullptr); result != 0) {
return result; return result;
} }
@@ -130,25 +137,21 @@ int add_and_retrieve_element_by_id() {
return result; return result;
} }
const fesa::core::ElementDefinition& direct = domain.element(100); const fesa::element::Element& direct = domain.element(100);
return require(direct.id() == 100); return require(direct.id() == 100);
} }
int duplicate_element_id_throws() { int duplicate_element_id_throws() {
fesa::core::Domain domain; fesa::core::Domain domain;
add_four_nodes(domain); add_four_nodes(domain);
domain.addElement(fesa::core::ElementDefinition{ add_material_and_property(domain);
100, add_element(domain);
fesa::core::ElementType::Mitc4,
{1, 2, 3, 4},
500});
return require_throws<std::invalid_argument>([&domain]() { return require_throws<std::invalid_argument>([&domain]() {
domain.addElement(fesa::core::ElementDefinition{ domain.addElement(std::make_unique<fesa::element::Mitc4Element>(
100, 100,
fesa::core::ElementType::Mitc4, std::array<fesa::core::NodeId, 4>{1, 2, 3, 4},
{1, 2, 3, 4}, 500));
500});
}); });
} }
@@ -157,13 +160,26 @@ int element_referencing_missing_node_throws() {
domain.addNode(fesa::core::Node{1, 0.0, 0.0, 0.0}); domain.addNode(fesa::core::Node{1, 0.0, 0.0, 0.0});
domain.addNode(fesa::core::Node{2, 1.0, 0.0, 0.0}); domain.addNode(fesa::core::Node{2, 1.0, 0.0, 0.0});
domain.addNode(fesa::core::Node{3, 1.0, 1.0, 0.0}); domain.addNode(fesa::core::Node{3, 1.0, 1.0, 0.0});
add_material_and_property(domain);
return require_throws<std::invalid_argument>([&domain]() { return require_throws<std::invalid_argument>([&domain]() {
domain.addElement(fesa::core::ElementDefinition{ domain.addElement(std::make_unique<fesa::element::Mitc4Element>(
100, 100,
fesa::core::ElementType::Mitc4, std::array<fesa::core::NodeId, 4>{1, 2, 3, 4},
{1, 2, 3, 4}, 500));
500}); });
}
int element_referencing_missing_property_throws() {
fesa::core::Domain domain;
add_four_nodes(domain);
domain.addMaterial(std::make_unique<fesa::material::LinearElasticMaterial>(700, 210.0, 0.3));
return require_throws<std::invalid_argument>([&domain]() {
domain.addElement(std::make_unique<fesa::element::Mitc4Element>(
100,
std::array<fesa::core::NodeId, 4>{1, 2, 3, 4},
500));
}); });
} }
@@ -179,11 +195,6 @@ int missing_element_lookup_contracts() {
}); });
} }
void add_material_and_property(fesa::core::Domain& domain) {
domain.addMaterial(fesa::core::LinearElasticMaterialDefinition{700, 210.0, 0.3});
domain.addShellProperty(fesa::core::ShellPropertyDefinition{500, 700, 0.01});
}
int add_and_retrieve_material_and_property() { int add_and_retrieve_material_and_property() {
fesa::core::Domain domain; fesa::core::Domain domain;
@@ -196,18 +207,15 @@ int add_and_retrieve_material_and_property() {
return result; return result;
} }
const fesa::core::LinearElasticMaterialDefinition* material = domain.findMaterial(700); const fesa::material::Material* material = domain.findMaterial(700);
if (const int result = require(material != nullptr); result != 0) { if (const int result = require(material != nullptr); result != 0) {
return result; return result;
} }
if (const int result = require(material->youngModulus() == 210.0); result != 0) { if (const int result = require(material->id() == 700); result != 0) {
return result;
}
if (const int result = require(material->poissonRatio() == 0.3); result != 0) {
return result; return result;
} }
const fesa::core::ShellPropertyDefinition* property = domain.findShellProperty(500); const fesa::property::ShellProperty* property = domain.findShellProperty(500);
if (const int result = require(property != nullptr); result != 0) { if (const int result = require(property != nullptr); result != 0) {
return result; return result;
} }
@@ -229,14 +237,14 @@ int duplicate_material_and_property_ids_throw() {
add_material_and_property(domain); add_material_and_property(domain);
if (const int result = require_throws<std::invalid_argument>([&domain]() { if (const int result = require_throws<std::invalid_argument>([&domain]() {
domain.addMaterial(fesa::core::LinearElasticMaterialDefinition{700, 100.0, 0.25}); domain.addMaterial(std::make_unique<fesa::material::LinearElasticMaterial>(700, 100.0, 0.25));
}); });
result != 0) { result != 0) {
return result; return result;
} }
return require_throws<std::invalid_argument>([&domain]() { return require_throws<std::invalid_argument>([&domain]() {
domain.addShellProperty(fesa::core::ShellPropertyDefinition{500, 700, 0.02}); domain.addShellProperty(fesa::property::ShellProperty{500, 700, 0.02});
}); });
} }
@@ -244,18 +252,15 @@ int shell_property_referencing_missing_material_throws() {
fesa::core::Domain domain; fesa::core::Domain domain;
return require_throws<std::invalid_argument>([&domain]() { return require_throws<std::invalid_argument>([&domain]() {
domain.addShellProperty(fesa::core::ShellPropertyDefinition{500, 700, 0.01}); domain.addShellProperty(fesa::property::ShellProperty{500, 700, 0.01});
}); });
} }
int add_and_retrieve_sets() { int add_and_retrieve_sets() {
fesa::core::Domain domain; fesa::core::Domain domain;
add_four_nodes(domain); add_four_nodes(domain);
domain.addElement(fesa::core::ElementDefinition{ add_material_and_property(domain);
100, add_element(domain);
fesa::core::ElementType::Mitc4,
{1, 2, 3, 4},
500});
domain.addNodeSet("left-edge", {1, 4}); domain.addNodeSet("left-edge", {1, 4});
domain.addElementSet("shells", {100}); domain.addElementSet("shells", {100});
@@ -294,11 +299,8 @@ int add_and_retrieve_sets() {
int duplicate_set_names_throw() { int duplicate_set_names_throw() {
fesa::core::Domain domain; fesa::core::Domain domain;
add_four_nodes(domain); add_four_nodes(domain);
domain.addElement(fesa::core::ElementDefinition{ add_material_and_property(domain);
100, add_element(domain);
fesa::core::ElementType::Mitc4,
{1, 2, 3, 4},
500});
domain.addNodeSet("left-edge", {1, 4}); domain.addNodeSet("left-edge", {1, 4});
domain.addElementSet("shells", {100}); domain.addElementSet("shells", {100});
@@ -316,11 +318,8 @@ int duplicate_set_names_throw() {
int sets_referencing_missing_ids_throw() { int sets_referencing_missing_ids_throw() {
fesa::core::Domain domain; fesa::core::Domain domain;
add_four_nodes(domain); add_four_nodes(domain);
domain.addElement(fesa::core::ElementDefinition{ add_material_and_property(domain);
100, add_element(domain);
fesa::core::ElementType::Mitc4,
{1, 2, 3, 4},
500});
if (const int result = require_throws<std::invalid_argument>([&domain]() { if (const int result = require_throws<std::invalid_argument>([&domain]() {
domain.addNodeSet("bad-nodes", {1, 99}); domain.addNodeSet("bad-nodes", {1, 99});
@@ -338,7 +337,7 @@ int add_and_retrieve_boundary_condition() {
domain.addNode(fesa::core::Node{1, 0.0, 0.0, 0.0}); domain.addNode(fesa::core::Node{1, 0.0, 0.0, 0.0});
const std::size_t index = domain.addBoundaryCondition( const std::size_t index = domain.addBoundaryCondition(
fesa::core::BoundaryCondition{1, fesa::core::Dof::U1, 0.0}); std::make_unique<fesa::boundary::SinglePointConstraint>(1, fesa::core::Dof::U1, 0.0));
if (const int result = require(index == 0); result != 0) { if (const int result = require(index == 0); result != 0) {
return result; return result;
@@ -347,38 +346,47 @@ int add_and_retrieve_boundary_condition() {
return result; return result;
} }
const fesa::core::BoundaryCondition& condition = domain.boundaryCondition(index); const fesa::boundary::BoundaryCondition& condition = domain.boundaryCondition(index);
if (const int result = require(condition.nodeId() == 1); result != 0) { if (const int result = require(condition.kind() == fesa::boundary::BoundaryConditionKind::SinglePointConstraint);
result != 0) {
return result; return result;
} }
if (const int result = require(condition.dof() == fesa::core::Dof::U1); result != 0) { const auto& spc = static_cast<const fesa::boundary::SinglePointConstraint&>(condition);
if (const int result = require(spc.nodeId() == 1); result != 0) {
return result; return result;
} }
return require(condition.value() == 0.0); if (const int result = require(spc.dof() == fesa::core::Dof::U1); result != 0) {
return result;
}
return require(spc.value() == 0.0);
} }
int add_and_retrieve_nodal_load() { int add_and_retrieve_nodal_load() {
fesa::core::Domain domain; fesa::core::Domain domain;
domain.addNode(fesa::core::Node{1, 0.0, 0.0, 0.0}); domain.addNode(fesa::core::Node{1, 0.0, 0.0, 0.0});
const std::size_t index = domain.addNodalLoad( const std::size_t index = domain.addLoad(
fesa::core::NodalLoadDefinition{1, fesa::core::Dof::U3, -100.0}); std::make_unique<fesa::load::NodalLoad>(1, fesa::core::Dof::U3, -100.0));
if (const int result = require(index == 0); result != 0) { if (const int result = require(index == 0); result != 0) {
return result; return result;
} }
if (const int result = require(domain.nodalLoadCount() == 1); result != 0) { if (const int result = require(domain.loadCount() == 1); result != 0) {
return result; return result;
} }
const fesa::core::NodalLoadDefinition& load = domain.nodalLoad(index); const fesa::load::Load& load = domain.load(index);
if (const int result = require(load.nodeId() == 1); result != 0) { if (const int result = require(load.kind() == fesa::load::LoadKind::Nodal); result != 0) {
return result; return result;
} }
if (const int result = require(load.dof() == fesa::core::Dof::U3); result != 0) { const auto& nodal_load = static_cast<const fesa::load::NodalLoad&>(load);
if (const int result = require(nodal_load.nodeId() == 1); result != 0) {
return result; return result;
} }
return require(load.value() == -100.0); if (const int result = require(nodal_load.dof() == fesa::core::Dof::U3); result != 0) {
return result;
}
return require(nodal_load.value() == -100.0);
} }
int missing_node_boundary_condition_and_load_throw() { int missing_node_boundary_condition_and_load_throw() {
@@ -386,15 +394,36 @@ int missing_node_boundary_condition_and_load_throw() {
if (const int result = require_throws<std::invalid_argument>([&domain]() { if (const int result = require_throws<std::invalid_argument>([&domain]() {
(void)domain.addBoundaryCondition( (void)domain.addBoundaryCondition(
fesa::core::BoundaryCondition{99, fesa::core::Dof::U1, 0.0}); std::make_unique<fesa::boundary::SinglePointConstraint>(99, fesa::core::Dof::U1, 0.0));
}); });
result != 0) { result != 0) {
return result; return result;
} }
return require_throws<std::invalid_argument>([&domain]() { return require_throws<std::invalid_argument>([&domain]() {
(void)domain.addNodalLoad( (void)domain.addLoad(
fesa::core::NodalLoadDefinition{99, fesa::core::Dof::U3, -100.0}); std::make_unique<fesa::load::NodalLoad>(99, fesa::core::Dof::U3, -100.0));
});
}
int duplicate_load_and_boundary_keys_throw() {
fesa::core::Domain domain;
domain.addNode(fesa::core::Node{1, 0.0, 0.0, 0.0});
domain.addBoundaryCondition(
std::make_unique<fesa::boundary::SinglePointConstraint>(1, fesa::core::Dof::U1, 0.0));
domain.addLoad(std::make_unique<fesa::load::NodalLoad>(1, fesa::core::Dof::U3, -100.0));
if (const int result = require_throws<std::invalid_argument>([&domain]() {
(void)domain.addBoundaryCondition(
std::make_unique<fesa::boundary::SinglePointConstraint>(1, fesa::core::Dof::U1, 1.0));
});
result != 0) {
return result;
}
return require_throws<std::invalid_argument>([&domain]() {
(void)domain.addLoad(
std::make_unique<fesa::load::NodalLoad>(1, fesa::core::Dof::U3, -200.0));
}); });
} }
@@ -402,9 +431,9 @@ int add_and_retrieve_linear_static_step() {
fesa::core::Domain domain; fesa::core::Domain domain;
domain.addNode(fesa::core::Node{1, 0.0, 0.0, 0.0}); domain.addNode(fesa::core::Node{1, 0.0, 0.0, 0.0});
const std::size_t bc = domain.addBoundaryCondition( const std::size_t bc = domain.addBoundaryCondition(
fesa::core::BoundaryCondition{1, fesa::core::Dof::U1, 0.0}); std::make_unique<fesa::boundary::SinglePointConstraint>(1, fesa::core::Dof::U1, 0.0));
const std::size_t load = domain.addNodalLoad( const std::size_t load = domain.addLoad(
fesa::core::NodalLoadDefinition{1, fesa::core::Dof::U3, -100.0}); std::make_unique<fesa::load::NodalLoad>(1, fesa::core::Dof::U3, -100.0));
domain.addStep(fesa::core::LinearStaticStepDefinition{1, "load-step", {bc}, {load}}); domain.addStep(fesa::core::LinearStaticStepDefinition{1, "load-step", {bc}, {load}});
@@ -438,9 +467,9 @@ int duplicate_and_invalid_step_references_throw() {
fesa::core::Domain domain; fesa::core::Domain domain;
domain.addNode(fesa::core::Node{1, 0.0, 0.0, 0.0}); domain.addNode(fesa::core::Node{1, 0.0, 0.0, 0.0});
const std::size_t bc = domain.addBoundaryCondition( const std::size_t bc = domain.addBoundaryCondition(
fesa::core::BoundaryCondition{1, fesa::core::Dof::U1, 0.0}); std::make_unique<fesa::boundary::SinglePointConstraint>(1, fesa::core::Dof::U1, 0.0));
const std::size_t load = domain.addNodalLoad( const std::size_t load = domain.addLoad(
fesa::core::NodalLoadDefinition{1, fesa::core::Dof::U3, -100.0}); std::make_unique<fesa::load::NodalLoad>(1, fesa::core::Dof::U3, -100.0));
domain.addStep(fesa::core::LinearStaticStepDefinition{1, "load-step", {bc}, {load}}); domain.addStep(fesa::core::LinearStaticStepDefinition{1, "load-step", {bc}, {load}});
@@ -461,22 +490,17 @@ int duplicate_and_invalid_step_references_throw() {
}); });
} }
int const_domain_retrieval_returns_const_model_data() { int const_domain_retrieval_returns_const_runtime_model_data() {
fesa::core::Domain domain; fesa::core::Domain domain;
add_four_nodes(domain); add_four_nodes(domain);
domain.addMaterial(fesa::core::LinearElasticMaterialDefinition{700, 210.0, 0.3}); add_material_and_property(domain);
domain.addShellProperty(fesa::core::ShellPropertyDefinition{500, 700, 0.01}); add_element(domain);
domain.addElement(fesa::core::ElementDefinition{
100,
fesa::core::ElementType::Mitc4,
{1, 2, 3, 4},
500});
domain.addNodeSet("left-edge", {1, 4}); domain.addNodeSet("left-edge", {1, 4});
domain.addElementSet("shells", {100}); domain.addElementSet("shells", {100});
const std::size_t bc = domain.addBoundaryCondition( const std::size_t bc = domain.addBoundaryCondition(
fesa::core::BoundaryCondition{1, fesa::core::Dof::U1, 0.0}); std::make_unique<fesa::boundary::SinglePointConstraint>(1, fesa::core::Dof::U1, 0.0));
const std::size_t load = domain.addNodalLoad( const std::size_t load = domain.addLoad(
fesa::core::NodalLoadDefinition{1, fesa::core::Dof::U3, -100.0}); std::make_unique<fesa::load::NodalLoad>(1, fesa::core::Dof::U3, -100.0));
domain.addStep(fesa::core::LinearStaticStepDefinition{1, "load-step", {bc}, {load}}); domain.addStep(fesa::core::LinearStaticStepDefinition{1, "load-step", {bc}, {load}});
const fesa::core::Domain& const_domain = domain; const fesa::core::Domain& const_domain = domain;
@@ -485,15 +509,15 @@ int const_domain_retrieval_returns_const_model_data() {
result != 0) { result != 0) {
return result; return result;
} }
if (const int result = require((std::is_same<decltype(const_domain.element(100)), const fesa::core::ElementDefinition&>::value)); if (const int result = require((std::is_same<decltype(const_domain.element(100)), const fesa::element::Element&>::value));
result != 0) { result != 0) {
return result; return result;
} }
if (const int result = require((std::is_same<decltype(const_domain.material(700)), const fesa::core::LinearElasticMaterialDefinition&>::value)); if (const int result = require((std::is_same<decltype(const_domain.material(700)), const fesa::material::Material&>::value));
result != 0) { result != 0) {
return result; return result;
} }
if (const int result = require((std::is_same<decltype(const_domain.shellProperty(500)), const fesa::core::ShellPropertyDefinition&>::value)); if (const int result = require((std::is_same<decltype(const_domain.shellProperty(500)), const fesa::property::ShellProperty&>::value));
result != 0) { result != 0) {
return result; return result;
} }
@@ -505,11 +529,11 @@ int const_domain_retrieval_returns_const_model_data() {
result != 0) { result != 0) {
return result; return result;
} }
if (const int result = require((std::is_same<decltype(const_domain.boundaryCondition(0)), const fesa::core::BoundaryCondition&>::value)); if (const int result = require((std::is_same<decltype(const_domain.boundaryCondition(0)), const fesa::boundary::BoundaryCondition&>::value));
result != 0) { result != 0) {
return result; return result;
} }
if (const int result = require((std::is_same<decltype(const_domain.nodalLoad(0)), const fesa::core::NodalLoadDefinition&>::value)); if (const int result = require((std::is_same<decltype(const_domain.load(0)), const fesa::load::Load&>::value));
result != 0) { result != 0) {
return result; return result;
} }
@@ -519,27 +543,21 @@ int const_domain_retrieval_returns_const_model_data() {
int failed_inserts_do_not_mutate_counts() { int failed_inserts_do_not_mutate_counts() {
fesa::core::Domain domain; fesa::core::Domain domain;
add_four_nodes(domain); add_four_nodes(domain);
domain.addMaterial(fesa::core::LinearElasticMaterialDefinition{700, 210.0, 0.3}); add_material_and_property(domain);
domain.addShellProperty(fesa::core::ShellPropertyDefinition{500, 700, 0.01}); add_element(domain);
domain.addElement(fesa::core::ElementDefinition{
100,
fesa::core::ElementType::Mitc4,
{1, 2, 3, 4},
500});
domain.addNodeSet("left-edge", {1, 4}); domain.addNodeSet("left-edge", {1, 4});
domain.addElementSet("shells", {100}); domain.addElementSet("shells", {100});
const std::size_t bc = domain.addBoundaryCondition( const std::size_t bc = domain.addBoundaryCondition(
fesa::core::BoundaryCondition{1, fesa::core::Dof::U1, 0.0}); std::make_unique<fesa::boundary::SinglePointConstraint>(1, fesa::core::Dof::U1, 0.0));
const std::size_t load = domain.addNodalLoad( const std::size_t load = domain.addLoad(
fesa::core::NodalLoadDefinition{1, fesa::core::Dof::U3, -100.0}); std::make_unique<fesa::load::NodalLoad>(1, fesa::core::Dof::U3, -100.0));
domain.addStep(fesa::core::LinearStaticStepDefinition{1, "load-step", {bc}, {load}}); domain.addStep(fesa::core::LinearStaticStepDefinition{1, "load-step", {bc}, {load}});
if (const int result = require_throws<std::invalid_argument>([&domain]() { if (const int result = require_throws<std::invalid_argument>([&domain]() {
domain.addElement(fesa::core::ElementDefinition{ domain.addElement(std::make_unique<fesa::element::Mitc4Element>(
101, 101,
fesa::core::ElementType::Mitc4, std::array<fesa::core::NodeId, 4>{1, 2, 3, 99},
{1, 2, 3, 99}, 500));
500});
}); });
result != 0) { result != 0) {
return result; return result;
@@ -549,7 +567,7 @@ int failed_inserts_do_not_mutate_counts() {
} }
if (const int result = require_throws<std::invalid_argument>([&domain]() { if (const int result = require_throws<std::invalid_argument>([&domain]() {
domain.addShellProperty(fesa::core::ShellPropertyDefinition{501, 404, 0.01}); domain.addShellProperty(fesa::property::ShellProperty{501, 404, 0.01});
}); });
result != 0) { result != 0) {
return result; return result;
@@ -570,7 +588,7 @@ int failed_inserts_do_not_mutate_counts() {
if (const int result = require_throws<std::invalid_argument>([&domain]() { if (const int result = require_throws<std::invalid_argument>([&domain]() {
(void)domain.addBoundaryCondition( (void)domain.addBoundaryCondition(
fesa::core::BoundaryCondition{99, fesa::core::Dof::U1, 0.0}); std::make_unique<fesa::boundary::SinglePointConstraint>(99, fesa::core::Dof::U1, 0.0));
}); });
result != 0) { result != 0) {
return result; return result;
@@ -609,6 +627,9 @@ int run_domain_storage_tests() {
if (const int result = element_referencing_missing_node_throws(); result != 0) { if (const int result = element_referencing_missing_node_throws(); result != 0) {
return result; return result;
} }
if (const int result = element_referencing_missing_property_throws(); result != 0) {
return result;
}
if (const int result = missing_element_lookup_contracts(); result != 0) { if (const int result = missing_element_lookup_contracts(); result != 0) {
return result; return result;
} }
@@ -639,13 +660,16 @@ int run_domain_storage_tests() {
if (const int result = missing_node_boundary_condition_and_load_throw(); result != 0) { if (const int result = missing_node_boundary_condition_and_load_throw(); result != 0) {
return result; return result;
} }
if (const int result = duplicate_load_and_boundary_keys_throw(); result != 0) {
return result;
}
if (const int result = add_and_retrieve_linear_static_step(); result != 0) { if (const int result = add_and_retrieve_linear_static_step(); result != 0) {
return result; return result;
} }
if (const int result = duplicate_and_invalid_step_references_throw(); result != 0) { if (const int result = duplicate_and_invalid_step_references_throw(); result != 0) {
return result; return result;
} }
if (const int result = const_domain_retrieval_returns_const_model_data(); result != 0) { if (const int result = const_domain_retrieval_returns_const_runtime_model_data(); result != 0) {
return result; return result;
} }
if (const int result = failed_inserts_do_not_mutate_counts(); result != 0) { if (const int result = failed_inserts_do_not_mutate_counts(); result != 0) {
+3
View File
@@ -23,6 +23,9 @@ int run_model_types_tests() {
if (const int result = require(kDofPerNode == 6); result != 0) { if (const int result = require(kDofPerNode == 6); result != 0) {
return result; return result;
} }
if (const int result = require(ElementType::Mitc4 == ElementType::Mitc4); result != 0) {
return result;
}
if (const int result = require(static_cast<int>(Dof::U1) == 0); result != 0) { if (const int result = require(static_cast<int>(Dof::U1) == 0); result != 0) {
return result; return result;
} }