refactor: store runtime objects in domain
This commit is contained in:
+2
-2
@@ -64,9 +64,9 @@
|
||||
**Tradeoff**: Implementation can be blocked until required reference artifacts are supplied.
|
||||
|
||||
## 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.
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ tests/
|
||||
The current repository may not yet contain all directories. They are intended ownership boundaries for implementation planning.
|
||||
|
||||
## 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.
|
||||
- `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`.
|
||||
@@ -100,7 +100,7 @@ The current repository may not yet contain all directories. They are intended ow
|
||||
## State Ownership
|
||||
|
||||
### 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:
|
||||
- nodes and elements
|
||||
@@ -109,6 +109,8 @@ Included:
|
||||
- loads and boundary conditions
|
||||
- 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` 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
|
||||
Abaqus input file
|
||||
-> InputParser
|
||||
-> temporary parsed records
|
||||
-> Factory/Registry object creation
|
||||
-> Domain
|
||||
-> Domain runtime object graph
|
||||
-> StepDefinition loop
|
||||
-> AnalysisModel
|
||||
-> DofManager
|
||||
|
||||
@@ -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`.
|
||||
- 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/`.
|
||||
- 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
|
||||
- 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, `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 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 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.
|
||||
|
||||
@@ -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
|
||||
```
|
||||
@@ -1,17 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#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/Node.hpp"
|
||||
#include "fesa/core/PropertyDefinition.hpp"
|
||||
#include "fesa/core/StepDefinition.hpp"
|
||||
#include "fesa/element/Element.hpp"
|
||||
#include "fesa/load/Load.hpp"
|
||||
#include "fesa/material/Material.hpp"
|
||||
#include "fesa/property/ShellProperty.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
@@ -24,33 +20,29 @@ namespace fesa::core {
|
||||
class Domain {
|
||||
public:
|
||||
void addNode(Node node);
|
||||
void addElement(ElementDefinition element);
|
||||
void addMaterial(LinearElasticMaterialDefinition material);
|
||||
void addShellProperty(ShellPropertyDefinition property);
|
||||
void addElement(std::unique_ptr<fesa::element::Element> element);
|
||||
void addMaterial(std::unique_ptr<fesa::material::Material> material);
|
||||
void addShellProperty(fesa::property::ShellProperty property);
|
||||
void addNodeSet(std::string name, std::vector<NodeId> node_ids);
|
||||
void addElementSet(std::string name, std::vector<ElementId> element_ids);
|
||||
std::size_t addBoundaryCondition(BoundaryCondition condition);
|
||||
std::size_t addNodalLoad(NodalLoadDefinition load);
|
||||
std::size_t addBoundaryCondition(std::unique_ptr<fesa::boundary::BoundaryCondition> boundary);
|
||||
std::size_t addLoad(std::unique_ptr<fesa::load::Load> load);
|
||||
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& node(NodeId id) const;
|
||||
std::size_t nodeCount() const noexcept;
|
||||
|
||||
const ElementDefinition* findElement(ElementId id) const noexcept;
|
||||
const ElementDefinition& element(ElementId id) const;
|
||||
const fesa::element::Element* findElement(ElementId id) const noexcept;
|
||||
const fesa::element::Element& element(ElementId id) const;
|
||||
std::size_t elementCount() const noexcept;
|
||||
|
||||
const LinearElasticMaterialDefinition* findMaterial(MaterialId id) const noexcept;
|
||||
const LinearElasticMaterialDefinition& material(MaterialId id) const;
|
||||
const fesa::material::Material* findMaterial(MaterialId id) const noexcept;
|
||||
const fesa::material::Material& material(MaterialId id) const;
|
||||
std::size_t materialCount() const noexcept;
|
||||
|
||||
const ShellPropertyDefinition* findShellProperty(PropertyId id) const noexcept;
|
||||
const ShellPropertyDefinition& shellProperty(PropertyId id) const;
|
||||
const fesa::property::ShellProperty* findShellProperty(PropertyId id) const noexcept;
|
||||
const fesa::property::ShellProperty& shellProperty(PropertyId id) const;
|
||||
std::size_t shellPropertyCount() 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;
|
||||
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;
|
||||
|
||||
const NodalLoadDefinition& nodalLoad(std::size_t index) const;
|
||||
std::size_t nodalLoadCount() const noexcept;
|
||||
const fesa::load::Load* findLoad(std::size_t index) 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& step(StepId id) const;
|
||||
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:
|
||||
std::unordered_map<NodeId, Node> nodes_;
|
||||
std::unordered_map<ElementId, ElementDefinition> elements_;
|
||||
std::unordered_map<MaterialId, LinearElasticMaterialDefinition> materials_;
|
||||
std::unordered_map<PropertyId, ShellPropertyDefinition> shell_properties_;
|
||||
std::unordered_map<ElementId, std::unique_ptr<fesa::element::Element>> elements_;
|
||||
std::unordered_map<MaterialId, std::unique_ptr<fesa::material::Material>> materials_;
|
||||
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<ElementId>> element_sets_;
|
||||
std::vector<BoundaryCondition> boundary_conditions_;
|
||||
std::vector<NodalLoadDefinition> nodal_loads_;
|
||||
std::vector<std::unique_ptr<fesa::boundary::BoundaryCondition>> boundary_conditions_;
|
||||
std::vector<std::unique_ptr<fesa::load::Load>> loads_;
|
||||
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
|
||||
|
||||
@@ -6,10 +6,6 @@
|
||||
|
||||
namespace fesa::core {
|
||||
|
||||
enum class ElementType {
|
||||
Mitc4
|
||||
};
|
||||
|
||||
class ElementDefinition {
|
||||
public:
|
||||
ElementDefinition(
|
||||
|
||||
@@ -21,6 +21,10 @@ enum class Dof : std::uint8_t {
|
||||
UR3 = 5
|
||||
};
|
||||
|
||||
enum class ElementType {
|
||||
Mitc4
|
||||
};
|
||||
|
||||
constexpr std::size_t kDofPerNode = 6;
|
||||
|
||||
} // namespace fesa::core
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "fesa/core/ElementDefinition.hpp"
|
||||
#include "fesa/core/ModelTypes.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
|
||||
@@ -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."
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -7,6 +7,10 @@
|
||||
{
|
||||
"dir": "analysis-model-objects",
|
||||
"status": "completed"
|
||||
},
|
||||
{
|
||||
"dir": "domain-runtime-storage",
|
||||
"status": "completed"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
+91
-166
@@ -1,6 +1,11 @@
|
||||
#include "fesa/core/Domain.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 <stdexcept>
|
||||
@@ -154,28 +159,37 @@ void Domain::addNode(Node node) {
|
||||
}
|
||||
}
|
||||
|
||||
void Domain::addElement(ElementDefinition element) {
|
||||
const ElementId id = element.id();
|
||||
void Domain::addElement(std::unique_ptr<fesa::element::Element> element) {
|
||||
if (!element) {
|
||||
throw std::invalid_argument("element is null");
|
||||
}
|
||||
const ElementId id = element->id();
|
||||
if (elements_.find(id) != elements_.end()) {
|
||||
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) {
|
||||
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));
|
||||
}
|
||||
|
||||
void Domain::addMaterial(LinearElasticMaterialDefinition material) {
|
||||
const MaterialId id = material.id();
|
||||
void Domain::addMaterial(std::unique_ptr<fesa::material::Material> material) {
|
||||
if (!material) {
|
||||
throw std::invalid_argument("material is null");
|
||||
}
|
||||
const MaterialId id = material->id();
|
||||
const auto inserted = materials_.emplace(id, std::move(material));
|
||||
if (!inserted.second) {
|
||||
throw std::invalid_argument("duplicate material id");
|
||||
}
|
||||
}
|
||||
|
||||
void Domain::addShellProperty(ShellPropertyDefinition property) {
|
||||
void Domain::addShellProperty(fesa::property::ShellProperty property) {
|
||||
const PropertyId id = property.id();
|
||||
if (shell_properties_.find(id) != shell_properties_.end()) {
|
||||
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));
|
||||
}
|
||||
|
||||
std::size_t Domain::addBoundaryCondition(BoundaryCondition condition) {
|
||||
if (findNode(condition.nodeId()) == nullptr) {
|
||||
throw std::invalid_argument("boundary condition references missing node id");
|
||||
std::size_t Domain::addBoundaryCondition(std::unique_ptr<fesa::boundary::BoundaryCondition> boundary) {
|
||||
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");
|
||||
}
|
||||
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();
|
||||
boundary_conditions_.push_back(condition);
|
||||
boundary_conditions_.push_back(std::move(boundary));
|
||||
return index;
|
||||
}
|
||||
|
||||
std::size_t Domain::addNodalLoad(NodalLoadDefinition load) {
|
||||
if (findNode(load.nodeId()) == nullptr) {
|
||||
throw std::invalid_argument("nodal load references missing node id");
|
||||
std::size_t Domain::addLoad(std::unique_ptr<fesa::load::Load> load) {
|
||||
if (!load) {
|
||||
throw std::invalid_argument("load is null");
|
||||
}
|
||||
const std::size_t index = nodal_loads_.size();
|
||||
nodal_loads_.push_back(load);
|
||||
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");
|
||||
}
|
||||
for (const auto& existing : loads_) {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -239,79 +280,13 @@ void Domain::addStep(LinearStaticStepDefinition step) {
|
||||
}
|
||||
}
|
||||
for (const std::size_t index : step.loadIndices()) {
|
||||
if (index >= nodal_loads_.size()) {
|
||||
throw std::invalid_argument("step references missing nodal load");
|
||||
if (index >= loads_.size()) {
|
||||
throw std::invalid_argument("step references missing load");
|
||||
}
|
||||
}
|
||||
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 auto it = nodes_.find(id);
|
||||
return it == nodes_.end() ? nullptr : &it->second;
|
||||
@@ -329,13 +304,13 @@ std::size_t Domain::nodeCount() const noexcept {
|
||||
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);
|
||||
return it == elements_.end() ? nullptr : &it->second;
|
||||
return it == elements_.end() ? nullptr : it->second.get();
|
||||
}
|
||||
|
||||
const ElementDefinition& Domain::element(ElementId id) const {
|
||||
const ElementDefinition* found = findElement(id);
|
||||
const fesa::element::Element& Domain::element(ElementId id) const {
|
||||
const fesa::element::Element* found = findElement(id);
|
||||
if (found == nullptr) {
|
||||
throw std::out_of_range("element id not found");
|
||||
}
|
||||
@@ -346,13 +321,13 @@ std::size_t Domain::elementCount() const noexcept {
|
||||
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);
|
||||
return it == materials_.end() ? nullptr : &it->second;
|
||||
return it == materials_.end() ? nullptr : it->second.get();
|
||||
}
|
||||
|
||||
const LinearElasticMaterialDefinition& Domain::material(MaterialId id) const {
|
||||
const LinearElasticMaterialDefinition* found = findMaterial(id);
|
||||
const fesa::material::Material& Domain::material(MaterialId id) const {
|
||||
const fesa::material::Material* found = findMaterial(id);
|
||||
if (found == nullptr) {
|
||||
throw std::out_of_range("material id not found");
|
||||
}
|
||||
@@ -363,13 +338,13 @@ std::size_t Domain::materialCount() const noexcept {
|
||||
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);
|
||||
return it == shell_properties_.end() ? nullptr : &it->second;
|
||||
}
|
||||
|
||||
const ShellPropertyDefinition& Domain::shellProperty(PropertyId id) const {
|
||||
const ShellPropertyDefinition* found = findShellProperty(id);
|
||||
const fesa::property::ShellProperty& Domain::shellProperty(PropertyId id) const {
|
||||
const fesa::property::ShellProperty* found = findShellProperty(id);
|
||||
if (found == nullptr) {
|
||||
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();
|
||||
}
|
||||
|
||||
const BoundaryCondition& Domain::boundaryCondition(std::size_t index) const {
|
||||
return boundary_conditions_.at(index);
|
||||
const fesa::boundary::BoundaryCondition* Domain::findBoundaryCondition(std::size_t index) const noexcept {
|
||||
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 {
|
||||
return boundary_conditions_.size();
|
||||
}
|
||||
|
||||
const NodalLoadDefinition& Domain::nodalLoad(std::size_t index) const {
|
||||
return nodal_loads_.at(index);
|
||||
const fesa::load::Load* Domain::findLoad(std::size_t index) const noexcept {
|
||||
return index < loads_.size() ? loads_[index].get() : nullptr;
|
||||
}
|
||||
|
||||
std::size_t Domain::nodalLoadCount() const noexcept {
|
||||
return nodal_loads_.size();
|
||||
const fesa::load::Load& Domain::load(std::size_t index) const {
|
||||
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 {
|
||||
@@ -447,70 +438,4 @@ std::size_t Domain::stepCount() const noexcept {
|
||||
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
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "fesa/element/Mitc4Element.hpp"
|
||||
#include "fesa/load/NodalLoad.hpp"
|
||||
#include "fesa/material/LinearElasticMaterial.hpp"
|
||||
#include "fesa/property/ShellProperty.hpp"
|
||||
|
||||
#include <memory>
|
||||
#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{3, 1.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;
|
||||
}
|
||||
|
||||
int domain_owns_element_and_material_objects() {
|
||||
fesa::core::Domain domain = populated_domain();
|
||||
|
||||
domain.addElementObject(std::make_unique<fesa::element::Mitc4Element>(
|
||||
domain.addElement(std::make_unique<fesa::element::Mitc4Element>(
|
||||
100,
|
||||
std::array<fesa::core::NodeId, 4>{1, 2, 3, 4},
|
||||
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) {
|
||||
return result;
|
||||
}
|
||||
if (const int result = require(element->type() == fesa::core::ElementType::Mitc4); result != 0) {
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
return result;
|
||||
}
|
||||
return require(domain.materialObject(700).id() == 700);
|
||||
return require(domain.material(700).id() == 700);
|
||||
}
|
||||
|
||||
int duplicate_element_and_material_object_ids_throw() {
|
||||
fesa::core::Domain domain = populated_domain();
|
||||
domain.addElementObject(std::make_unique<fesa::element::Mitc4Element>(
|
||||
domain.addElement(std::make_unique<fesa::element::Mitc4Element>(
|
||||
100,
|
||||
std::array<fesa::core::NodeId, 4>{1, 2, 3, 4},
|
||||
500));
|
||||
domain.addMaterialObject(std::make_unique<fesa::material::LinearElasticMaterial>(700, 210.0, 0.3));
|
||||
|
||||
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,
|
||||
std::array<fesa::core::NodeId, 4>{1, 2, 3, 4},
|
||||
500));
|
||||
@@ -79,36 +80,36 @@ int duplicate_element_and_material_object_ids_throw() {
|
||||
return result;
|
||||
}
|
||||
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() {
|
||||
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;
|
||||
}
|
||||
if (const int result = require(domain.findMaterialObject(404) == nullptr); result != 0) {
|
||||
if (const int result = require(domain.findMaterial(404) == nullptr); result != 0) {
|
||||
return result;
|
||||
}
|
||||
if (const int result = require_throws<std::out_of_range>([&domain]() {
|
||||
(void)domain.elementObject(404);
|
||||
(void)domain.element(404);
|
||||
});
|
||||
result != 0) {
|
||||
return result;
|
||||
}
|
||||
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() {
|
||||
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));
|
||||
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));
|
||||
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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 require(
|
||||
domain.boundaryObject(boundary_index).kind() ==
|
||||
domain.boundaryCondition(boundary_index).kind() ==
|
||||
fesa::boundary::BoundaryConditionKind::SinglePointConstraint);
|
||||
}
|
||||
|
||||
int duplicate_load_and_boundary_keys_throw() {
|
||||
fesa::core::Domain domain = populated_domain();
|
||||
domain.addLoadObject(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.addLoad(std::make_unique<fesa::load::NodalLoad>(1, fesa::core::Dof::U3, -100.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]() {
|
||||
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) {
|
||||
return result;
|
||||
}
|
||||
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
@@ -1,12 +1,14 @@
|
||||
#include "fesa/core/BoundaryCondition.hpp"
|
||||
#include "fesa/boundary/SinglePointConstraint.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/PropertyDefinition.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 <type_traits>
|
||||
|
||||
@@ -28,6 +30,25 @@ int require_throws(Function&& function) {
|
||||
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() {
|
||||
fesa::core::Domain domain;
|
||||
|
||||
@@ -55,11 +76,7 @@ int add_and_retrieve_node_by_id() {
|
||||
}
|
||||
|
||||
const fesa::core::Node& direct = domain.node(10);
|
||||
if (const int result = require(direct.id() == 10); result != 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return require(direct.id() == 10);
|
||||
}
|
||||
|
||||
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() {
|
||||
fesa::core::Domain domain;
|
||||
add_four_nodes(domain);
|
||||
add_material_and_property(domain);
|
||||
|
||||
domain.addElement(fesa::core::ElementDefinition{
|
||||
100,
|
||||
fesa::core::ElementType::Mitc4,
|
||||
{1, 2, 3, 4},
|
||||
500});
|
||||
add_element(domain);
|
||||
|
||||
if (const int result = require(domain.elementCount() == 1); result != 0) {
|
||||
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) {
|
||||
return result;
|
||||
}
|
||||
@@ -130,25 +137,21 @@ int add_and_retrieve_element_by_id() {
|
||||
return result;
|
||||
}
|
||||
|
||||
const fesa::core::ElementDefinition& direct = domain.element(100);
|
||||
const fesa::element::Element& direct = domain.element(100);
|
||||
return require(direct.id() == 100);
|
||||
}
|
||||
|
||||
int duplicate_element_id_throws() {
|
||||
fesa::core::Domain domain;
|
||||
add_four_nodes(domain);
|
||||
domain.addElement(fesa::core::ElementDefinition{
|
||||
100,
|
||||
fesa::core::ElementType::Mitc4,
|
||||
{1, 2, 3, 4},
|
||||
500});
|
||||
add_material_and_property(domain);
|
||||
add_element(domain);
|
||||
|
||||
return require_throws<std::invalid_argument>([&domain]() {
|
||||
domain.addElement(fesa::core::ElementDefinition{
|
||||
domain.addElement(std::make_unique<fesa::element::Mitc4Element>(
|
||||
100,
|
||||
fesa::core::ElementType::Mitc4,
|
||||
{1, 2, 3, 4},
|
||||
500});
|
||||
std::array<fesa::core::NodeId, 4>{1, 2, 3, 4},
|
||||
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{2, 1.0, 0.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]() {
|
||||
domain.addElement(fesa::core::ElementDefinition{
|
||||
domain.addElement(std::make_unique<fesa::element::Mitc4Element>(
|
||||
100,
|
||||
fesa::core::ElementType::Mitc4,
|
||||
{1, 2, 3, 4},
|
||||
500});
|
||||
std::array<fesa::core::NodeId, 4>{1, 2, 3, 4},
|
||||
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() {
|
||||
fesa::core::Domain domain;
|
||||
|
||||
@@ -196,18 +207,15 @@ int add_and_retrieve_material_and_property() {
|
||||
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) {
|
||||
return result;
|
||||
}
|
||||
if (const int result = require(material->youngModulus() == 210.0); result != 0) {
|
||||
return result;
|
||||
}
|
||||
if (const int result = require(material->poissonRatio() == 0.3); result != 0) {
|
||||
if (const int result = require(material->id() == 700); result != 0) {
|
||||
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) {
|
||||
return result;
|
||||
}
|
||||
@@ -229,14 +237,14 @@ int duplicate_material_and_property_ids_throw() {
|
||||
add_material_and_property(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) {
|
||||
return result;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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() {
|
||||
fesa::core::Domain domain;
|
||||
add_four_nodes(domain);
|
||||
domain.addElement(fesa::core::ElementDefinition{
|
||||
100,
|
||||
fesa::core::ElementType::Mitc4,
|
||||
{1, 2, 3, 4},
|
||||
500});
|
||||
add_material_and_property(domain);
|
||||
add_element(domain);
|
||||
|
||||
domain.addNodeSet("left-edge", {1, 4});
|
||||
domain.addElementSet("shells", {100});
|
||||
@@ -294,11 +299,8 @@ int add_and_retrieve_sets() {
|
||||
int duplicate_set_names_throw() {
|
||||
fesa::core::Domain domain;
|
||||
add_four_nodes(domain);
|
||||
domain.addElement(fesa::core::ElementDefinition{
|
||||
100,
|
||||
fesa::core::ElementType::Mitc4,
|
||||
{1, 2, 3, 4},
|
||||
500});
|
||||
add_material_and_property(domain);
|
||||
add_element(domain);
|
||||
domain.addNodeSet("left-edge", {1, 4});
|
||||
domain.addElementSet("shells", {100});
|
||||
|
||||
@@ -316,11 +318,8 @@ int duplicate_set_names_throw() {
|
||||
int sets_referencing_missing_ids_throw() {
|
||||
fesa::core::Domain domain;
|
||||
add_four_nodes(domain);
|
||||
domain.addElement(fesa::core::ElementDefinition{
|
||||
100,
|
||||
fesa::core::ElementType::Mitc4,
|
||||
{1, 2, 3, 4},
|
||||
500});
|
||||
add_material_and_property(domain);
|
||||
add_element(domain);
|
||||
|
||||
if (const int result = require_throws<std::invalid_argument>([&domain]() {
|
||||
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});
|
||||
|
||||
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) {
|
||||
return result;
|
||||
@@ -347,38 +346,47 @@ int add_and_retrieve_boundary_condition() {
|
||||
return result;
|
||||
}
|
||||
|
||||
const fesa::core::BoundaryCondition& condition = domain.boundaryCondition(index);
|
||||
if (const int result = require(condition.nodeId() == 1); result != 0) {
|
||||
const fesa::boundary::BoundaryCondition& condition = domain.boundaryCondition(index);
|
||||
if (const int result = require(condition.kind() == fesa::boundary::BoundaryConditionKind::SinglePointConstraint);
|
||||
result != 0) {
|
||||
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 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() {
|
||||
fesa::core::Domain domain;
|
||||
domain.addNode(fesa::core::Node{1, 0.0, 0.0, 0.0});
|
||||
|
||||
const std::size_t index = domain.addNodalLoad(
|
||||
fesa::core::NodalLoadDefinition{1, fesa::core::Dof::U3, -100.0});
|
||||
const std::size_t index = domain.addLoad(
|
||||
std::make_unique<fesa::load::NodalLoad>(1, fesa::core::Dof::U3, -100.0));
|
||||
|
||||
if (const int result = require(index == 0); result != 0) {
|
||||
return result;
|
||||
}
|
||||
if (const int result = require(domain.nodalLoadCount() == 1); result != 0) {
|
||||
if (const int result = require(domain.loadCount() == 1); result != 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
const fesa::core::NodalLoadDefinition& load = domain.nodalLoad(index);
|
||||
if (const int result = require(load.nodeId() == 1); result != 0) {
|
||||
const fesa::load::Load& load = domain.load(index);
|
||||
if (const int result = require(load.kind() == fesa::load::LoadKind::Nodal); result != 0) {
|
||||
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 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() {
|
||||
@@ -386,15 +394,36 @@ int missing_node_boundary_condition_and_load_throw() {
|
||||
|
||||
if (const int result = require_throws<std::invalid_argument>([&domain]() {
|
||||
(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) {
|
||||
return result;
|
||||
}
|
||||
|
||||
return require_throws<std::invalid_argument>([&domain]() {
|
||||
(void)domain.addNodalLoad(
|
||||
fesa::core::NodalLoadDefinition{99, fesa::core::Dof::U3, -100.0});
|
||||
(void)domain.addLoad(
|
||||
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;
|
||||
domain.addNode(fesa::core::Node{1, 0.0, 0.0, 0.0});
|
||||
const std::size_t bc = domain.addBoundaryCondition(
|
||||
fesa::core::BoundaryCondition{1, fesa::core::Dof::U1, 0.0});
|
||||
const std::size_t load = domain.addNodalLoad(
|
||||
fesa::core::NodalLoadDefinition{1, fesa::core::Dof::U3, -100.0});
|
||||
std::make_unique<fesa::boundary::SinglePointConstraint>(1, fesa::core::Dof::U1, 0.0));
|
||||
const std::size_t load = domain.addLoad(
|
||||
std::make_unique<fesa::load::NodalLoad>(1, fesa::core::Dof::U3, -100.0));
|
||||
|
||||
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;
|
||||
domain.addNode(fesa::core::Node{1, 0.0, 0.0, 0.0});
|
||||
const std::size_t bc = domain.addBoundaryCondition(
|
||||
fesa::core::BoundaryCondition{1, fesa::core::Dof::U1, 0.0});
|
||||
const std::size_t load = domain.addNodalLoad(
|
||||
fesa::core::NodalLoadDefinition{1, fesa::core::Dof::U3, -100.0});
|
||||
std::make_unique<fesa::boundary::SinglePointConstraint>(1, fesa::core::Dof::U1, 0.0));
|
||||
const std::size_t load = domain.addLoad(
|
||||
std::make_unique<fesa::load::NodalLoad>(1, fesa::core::Dof::U3, -100.0));
|
||||
|
||||
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;
|
||||
add_four_nodes(domain);
|
||||
domain.addMaterial(fesa::core::LinearElasticMaterialDefinition{700, 210.0, 0.3});
|
||||
domain.addShellProperty(fesa::core::ShellPropertyDefinition{500, 700, 0.01});
|
||||
domain.addElement(fesa::core::ElementDefinition{
|
||||
100,
|
||||
fesa::core::ElementType::Mitc4,
|
||||
{1, 2, 3, 4},
|
||||
500});
|
||||
add_material_and_property(domain);
|
||||
add_element(domain);
|
||||
domain.addNodeSet("left-edge", {1, 4});
|
||||
domain.addElementSet("shells", {100});
|
||||
const std::size_t bc = domain.addBoundaryCondition(
|
||||
fesa::core::BoundaryCondition{1, fesa::core::Dof::U1, 0.0});
|
||||
const std::size_t load = domain.addNodalLoad(
|
||||
fesa::core::NodalLoadDefinition{1, fesa::core::Dof::U3, -100.0});
|
||||
std::make_unique<fesa::boundary::SinglePointConstraint>(1, fesa::core::Dof::U1, 0.0));
|
||||
const std::size_t load = domain.addLoad(
|
||||
std::make_unique<fesa::load::NodalLoad>(1, fesa::core::Dof::U3, -100.0));
|
||||
domain.addStep(fesa::core::LinearStaticStepDefinition{1, "load-step", {bc}, {load}});
|
||||
|
||||
const fesa::core::Domain& const_domain = domain;
|
||||
@@ -485,15 +509,15 @@ int const_domain_retrieval_returns_const_model_data() {
|
||||
result != 0) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
return result;
|
||||
}
|
||||
@@ -505,11 +529,11 @@ int const_domain_retrieval_returns_const_model_data() {
|
||||
result != 0) {
|
||||
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) {
|
||||
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) {
|
||||
return result;
|
||||
}
|
||||
@@ -519,27 +543,21 @@ int const_domain_retrieval_returns_const_model_data() {
|
||||
int failed_inserts_do_not_mutate_counts() {
|
||||
fesa::core::Domain domain;
|
||||
add_four_nodes(domain);
|
||||
domain.addMaterial(fesa::core::LinearElasticMaterialDefinition{700, 210.0, 0.3});
|
||||
domain.addShellProperty(fesa::core::ShellPropertyDefinition{500, 700, 0.01});
|
||||
domain.addElement(fesa::core::ElementDefinition{
|
||||
100,
|
||||
fesa::core::ElementType::Mitc4,
|
||||
{1, 2, 3, 4},
|
||||
500});
|
||||
add_material_and_property(domain);
|
||||
add_element(domain);
|
||||
domain.addNodeSet("left-edge", {1, 4});
|
||||
domain.addElementSet("shells", {100});
|
||||
const std::size_t bc = domain.addBoundaryCondition(
|
||||
fesa::core::BoundaryCondition{1, fesa::core::Dof::U1, 0.0});
|
||||
const std::size_t load = domain.addNodalLoad(
|
||||
fesa::core::NodalLoadDefinition{1, fesa::core::Dof::U3, -100.0});
|
||||
std::make_unique<fesa::boundary::SinglePointConstraint>(1, fesa::core::Dof::U1, 0.0));
|
||||
const std::size_t load = domain.addLoad(
|
||||
std::make_unique<fesa::load::NodalLoad>(1, fesa::core::Dof::U3, -100.0));
|
||||
domain.addStep(fesa::core::LinearStaticStepDefinition{1, "load-step", {bc}, {load}});
|
||||
|
||||
if (const int result = require_throws<std::invalid_argument>([&domain]() {
|
||||
domain.addElement(fesa::core::ElementDefinition{
|
||||
domain.addElement(std::make_unique<fesa::element::Mitc4Element>(
|
||||
101,
|
||||
fesa::core::ElementType::Mitc4,
|
||||
{1, 2, 3, 99},
|
||||
500});
|
||||
std::array<fesa::core::NodeId, 4>{1, 2, 3, 99},
|
||||
500));
|
||||
});
|
||||
result != 0) {
|
||||
return result;
|
||||
@@ -549,7 +567,7 @@ int failed_inserts_do_not_mutate_counts() {
|
||||
}
|
||||
|
||||
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) {
|
||||
return result;
|
||||
@@ -570,7 +588,7 @@ int failed_inserts_do_not_mutate_counts() {
|
||||
|
||||
if (const int result = require_throws<std::invalid_argument>([&domain]() {
|
||||
(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) {
|
||||
return result;
|
||||
@@ -609,6 +627,9 @@ int run_domain_storage_tests() {
|
||||
if (const int result = element_referencing_missing_node_throws(); result != 0) {
|
||||
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) {
|
||||
return result;
|
||||
}
|
||||
@@ -639,13 +660,16 @@ int run_domain_storage_tests() {
|
||||
if (const int result = missing_node_boundary_condition_and_load_throw(); result != 0) {
|
||||
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) {
|
||||
return result;
|
||||
}
|
||||
if (const int result = duplicate_and_invalid_step_references_throw(); result != 0) {
|
||||
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;
|
||||
}
|
||||
if (const int result = failed_inserts_do_not_mutate_counts(); result != 0) {
|
||||
|
||||
@@ -23,6 +23,9 @@ int run_model_types_tests() {
|
||||
if (const int result = require(kDofPerNode == 6); result != 0) {
|
||||
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) {
|
||||
return result;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user