feat: add property model foundation

This commit is contained in:
김경종
2026-06-09 11:56:42 +09:00
parent f4196efb10
commit 7ea08441ed
23 changed files with 661 additions and 25 deletions
+10
View File
@@ -10,6 +10,7 @@ add_library(fesa_core STATIC
src/boundary/SinglePointConstraint.cpp
src/core/Domain.cpp
src/element/Mitc4Element.cpp
src/io/Hdf5ResultWriter.cpp
src/load/NodalLoad.cpp
src/material/LinearElasticMaterial.cpp
src/property/ShellProperty.cpp
@@ -45,9 +46,18 @@ add_executable(fesa_model_object_tests
tests/load/load_base_test.cpp
tests/material/material_base_test.cpp
tests/model_object_main.cpp
tests/property/property_base_test.cpp
tests/property/shell_property_test.cpp
)
target_link_libraries(fesa_model_object_tests PRIVATE fesa_core)
add_test(NAME model-object.base COMMAND fesa_model_object_tests)
set_tests_properties(model-object.base PROPERTIES LABELS "model-object;core")
add_executable(fesa_io_tests
tests/io/hdf5_result_writer_test.cpp
)
target_link_libraries(fesa_io_tests PRIVATE fesa_core)
add_test(NAME io.hdf5-result-writer COMMAND fesa_io_tests)
set_tests_properties(io.hdf5-result-writer PROPERTIES LABELS "io;hdf5")
+7
View File
@@ -38,6 +38,9 @@
- 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.
- Created `docs/implementation-plans/property-model-foundation-implementation-plan.md` and `phases/property-model-foundation/`.
- Added runtime `Property` base class and `PropertyKind`, made `ShellProperty` derive from `Property`, and migrated `Domain` property ownership to `std::unique_ptr<Property>`.
- Added a minimal `Hdf5ResultWriter` skeleton with path validation only; it does not link HDF5 or write files yet.
## In Progress
- Ready for the next upstream MITC4 stage: new solver feature requirements analysis for `mitc4-linear-static-shell`.
@@ -59,6 +62,10 @@
- 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-09: After property model foundation implementation, `ctest --test-dir build/msvc-debug --output-on-failure -C Debug -R "domain|model-object|io"` passed. 3 CTest executables ran successfully.
- 2026-06-09: After property model foundation implementation, `python -m unittest discover -s scripts -p "test_*.py"` passed. 89 tests ran successfully.
- 2026-06-09: After property 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-09: After property model foundation implementation, `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,108 @@
# Property Model Foundation Implementation Plan
## Objective
Introduce a runtime `Property` base class for element property and section objects, then make `ShellProperty` and `Domain` use that base class consistently.
This phase keeps property objects as model data only. It does not implement shell section stiffness, constitutive matrices, assembly, solver logic, HDF5 output, or reference comparison.
## Phase Overview
1. `property-base-contract`
- Record the runtime property model contract and exclusions.
2. `property-base-interface`
- Add `PropertyKind` and abstract `Property`.
- Add unit tests for polymorphic use and virtual deletion.
3. `shell-property-polymorphism`
- Make `ShellProperty` derive from `Property`.
- Preserve id, material id, thickness, and positive-thickness validation.
4. `domain-property-ownership`
- Change `Domain` property storage to `std::unique_ptr<Property>`.
- Validate duplicate property ids and shell-property material references.
- Validate element property references against runtime property storage.
5. `validation-report-handoff`
- Run targeted CTest, harness self-tests, workspace validation, and whitespace checks.
- Update handoff documents.
## Runtime Property Contract
`Property` represents element property and section data that is owned by `Domain` and referenced by elements through `PropertyId`.
Required interface:
```cpp
namespace fesa::property {
enum class PropertyKind {
Shell
};
class Property {
public:
virtual ~Property() = default;
virtual PropertyId id() const noexcept = 0;
virtual PropertyKind kind() const noexcept = 0;
};
} // namespace fesa::property
```
`ShellProperty`:
- derives from `Property`;
- returns `PropertyKind::Shell`;
- stores `PropertyId`, `MaterialId`, and thickness;
- rejects non-positive thickness;
- does not compute shell stiffness in this phase.
## Domain Contract
`Domain` stores runtime property objects by ownership:
```cpp
std::unordered_map<PropertyId, std::unique_ptr<fesa::property::Property>>
```
`Domain` validates:
- null property pointer rejection;
- duplicate property id rejection;
- missing material id for `ShellProperty`;
- missing property id for elements.
## Hdf5ResultWriter Constraint
The adjacent `Hdf5ResultWriter` work requested for this slice is limited to a skeleton class only. It may expose construction and basic path access, but it must not link HDF5, create files, define result datasets, write metadata, or claim HDF5 output support.
## Non-Goals
- No MITC4 element formulation.
- No shell section stiffness.
- No material constitutive behavior.
- No DofManager work.
- No assembly, solver, sparse matrix, or reaction recovery.
- No HDF5 file writing or result schema implementation.
- No parser/factory implementation.
## Tests
Primary C++ tests:
- `/tests/property/property_base_test.cpp`
- `/tests/property/shell_property_test.cpp`
- `/tests/core/domain_storage_test.cpp`
- `/tests/core/domain_model_object_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
```
+8 -2
View File
@@ -7,6 +7,7 @@
#include "fesa/element/Element.hpp"
#include "fesa/load/Load.hpp"
#include "fesa/material/Material.hpp"
#include "fesa/property/Property.hpp"
#include "fesa/property/ShellProperty.hpp"
#include <cstddef>
@@ -22,7 +23,8 @@ public:
void addNode(Node node);
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 addProperty(std::unique_ptr<fesa::property::Property> property);
void addShellProperty(std::unique_ptr<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(std::unique_ptr<fesa::boundary::BoundaryCondition> boundary);
@@ -41,6 +43,10 @@ public:
const fesa::material::Material& material(MaterialId id) const;
std::size_t materialCount() const noexcept;
const fesa::property::Property* findProperty(PropertyId id) const noexcept;
const fesa::property::Property& property(PropertyId id) const;
std::size_t propertyCount() const noexcept;
const fesa::property::ShellProperty* findShellProperty(PropertyId id) const noexcept;
const fesa::property::ShellProperty& shellProperty(PropertyId id) const;
std::size_t shellPropertyCount() const noexcept;
@@ -69,7 +75,7 @@ private:
std::unordered_map<NodeId, Node> nodes_;
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<PropertyId, std::unique_ptr<fesa::property::Property>> properties_;
std::unordered_map<std::string, std::vector<NodeId>> node_sets_;
std::unordered_map<std::string, std::vector<ElementId>> element_sets_;
std::vector<std::unique_ptr<fesa::boundary::BoundaryCondition>> boundary_conditions_;
+17
View File
@@ -0,0 +1,17 @@
#pragma once
#include <string>
namespace fesa::io {
class Hdf5ResultWriter {
public:
explicit Hdf5ResultWriter(std::string file_path);
const std::string& filePath() const noexcept;
private:
std::string file_path_;
};
} // namespace fesa::io
+21
View File
@@ -0,0 +1,21 @@
#pragma once
#include "fesa/core/ModelTypes.hpp"
namespace fesa::property {
using fesa::core::PropertyId;
enum class PropertyKind {
Shell
};
class Property {
public:
virtual ~Property() = default;
virtual PropertyId id() const noexcept = 0;
virtual PropertyKind kind() const noexcept = 0;
};
} // namespace fesa::property
+4 -2
View File
@@ -1,17 +1,19 @@
#pragma once
#include "fesa/core/ModelTypes.hpp"
#include "fesa/property/Property.hpp"
namespace fesa::property {
using fesa::core::MaterialId;
using fesa::core::PropertyId;
class ShellProperty {
class ShellProperty final : public Property {
public:
ShellProperty(PropertyId id, MaterialId material_id, double thickness);
PropertyId id() const noexcept;
PropertyId id() const noexcept override;
PropertyKind kind() const noexcept override;
MaterialId materialId() const noexcept;
double thickness() const noexcept;
+4
View File
@@ -11,6 +11,10 @@
{
"dir": "domain-runtime-storage",
"status": "completed"
},
{
"dir": "property-model-foundation",
"status": "completed"
}
]
}
@@ -0,0 +1,36 @@
{
"project": "FESA Structural Solver",
"phase": "property-model-foundation",
"steps": [
{
"step": 0,
"name": "property-base-contract",
"status": "completed",
"summary": "Created the property model foundation implementation plan with runtime Property ownership rules and HDF5 skeleton limits."
},
{
"step": 1,
"name": "property-base-interface",
"status": "completed",
"summary": "Added PropertyKind and abstract Property base with polymorphic ownership tests."
},
{
"step": 2,
"name": "shell-property-polymorphism",
"status": "completed",
"summary": "Made ShellProperty derive from Property while preserving material id and positive-thickness validation."
},
{
"step": 3,
"name": "domain-property-ownership",
"status": "completed",
"summary": "Migrated Domain property storage to std::unique_ptr<Property> with shell-property compatibility accessors and cross-reference validation."
},
{
"step": 4,
"name": "validation-report-handoff",
"status": "completed",
"summary": "Validated targeted CTest, workspace validation, script self-tests, and whitespace checks; updated project progress."
}
]
}
+54
View File
@@ -0,0 +1,54 @@
# Step 0: property-base-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-runtime-storage-implementation-plan.md`
- `/include/fesa/property/ShellProperty.hpp`
- `/include/fesa/core/Domain.hpp`
- `/src/core/Domain.cpp`
## Task
Create `/docs/implementation-plans/property-model-foundation-implementation-plan.md`.
The plan must define the runtime property model contract:
- `Property` is the base class for element properties and sections.
- `PropertyKind` identifies concrete property families; phase 1 supports only `Shell`.
- `ShellProperty` derives from `Property`.
- `Domain` owns property objects through RAII.
- `Domain` validates duplicate property ids and missing material ids.
- `Element::propertyId()` remains an id reference; elements do not own property objects.
Keep out of scope:
- shell section stiffness;
- material constitutive matrices;
- shear correction;
- element formulation;
- assembly, solver, HDF5 output, and reference comparison.
## 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 change MITC4 formulation or tolerance policy.
2. Confirm the plan keeps solver state out of `Domain`.
3. Update `phases/property-model-foundation/index.json` for this step result.
+52
View File
@@ -0,0 +1,52 @@
# Step 1: property-base-interface
## Read First
Read these files before editing:
- `/AGENTS.md`
- `/docs/AGENT_RULES.md`
- `/docs/implementation-plans/property-model-foundation-implementation-plan.md`
- `/include/fesa/core/ModelTypes.hpp`
- `/include/fesa/property/ShellProperty.hpp`
- `/tests/property/shell_property_test.cpp`
- `/CMakeLists.txt`
## Task
Add the runtime property base interface.
Required API:
- File: `/include/fesa/property/Property.hpp`
- Namespace: `fesa::property`
- `enum class PropertyKind { Shell };`
- `class Property`
- virtual destructor;
- `virtual PropertyId id() const noexcept = 0;`
- `virtual PropertyKind kind() const noexcept = 0;`
Rules:
- Use `fesa::core::PropertyId`.
- Do not add section stiffness or material behavior.
- Keep this header independent from HDF5, MKL, TBB, and parser headers.
## Tests To Write First
- Add `/tests/property/property_base_test.cpp`.
- Test that a small derived class can be used through `const Property&`.
- Test virtual deletion through `std::unique_ptr<Property>`.
## Acceptance Criteria
```powershell
ctest --test-dir build/msvc-debug --output-on-failure -C Debug -R model-object
python scripts/validate_workspace.py
```
## Verification Notes
1. Run targeted CTest before implementation and confirm the expected compile failure.
2. Keep all code C++17/MSVC-compatible.
3. Update `phases/property-model-foundation/index.json` for this step result.
+49
View File
@@ -0,0 +1,49 @@
# Step 2: shell-property-polymorphism
## Read First
Read these files before editing:
- `/AGENTS.md`
- `/docs/AGENT_RULES.md`
- `/docs/implementation-plans/property-model-foundation-implementation-plan.md`
- `/include/fesa/property/Property.hpp`
- `/include/fesa/property/ShellProperty.hpp`
- `/src/property/ShellProperty.cpp`
- `/tests/property/shell_property_test.cpp`
## Task
Make `ShellProperty` derive from `Property`.
Required behavior:
- `ShellProperty::id()` overrides `Property::id()`.
- `ShellProperty::kind()` returns `PropertyKind::Shell`.
- `ShellProperty` keeps `materialId()` and `thickness()` accessors.
- Constructor still rejects `thickness <= 0.0`.
Rules:
- Do not rename `ShellProperty` in this phase.
- Do not add shell section stiffness.
- Do not add material lookup inside `ShellProperty`; Domain validates cross references.
## Tests To Write First
- Extend `/tests/property/shell_property_test.cpp`.
- Test `const Property& base = shell_property` exposes id and kind.
- Preserve positive-thickness validation tests.
## Acceptance Criteria
```powershell
ctest --test-dir build/msvc-debug --output-on-failure -C Debug -R model-object
python scripts/validate_workspace.py
```
## Verification Notes
1. Run targeted CTest before production edits and confirm failure.
2. Keep `ShellProperty` free of solver state.
3. Update `phases/property-model-foundation/index.json` for this step result.
+60
View File
@@ -0,0 +1,60 @@
# Step 3: domain-property-ownership
## Read First
Read these files before editing:
- `/AGENTS.md`
- `/docs/AGENT_RULES.md`
- `/docs/implementation-plans/property-model-foundation-implementation-plan.md`
- `/include/fesa/core/Domain.hpp`
- `/src/core/Domain.cpp`
- `/include/fesa/property/Property.hpp`
- `/include/fesa/property/ShellProperty.hpp`
- `/tests/core/domain_storage_test.cpp`
- `/tests/core/domain_model_object_test.cpp`
## Task
Make `Domain` own runtime property objects through the `Property` base class.
Required API shape:
- `void Domain::addProperty(std::unique_ptr<fesa::property::Property> property);`
- `const fesa::property::Property* Domain::findProperty(PropertyId id) const noexcept;`
- `const fesa::property::Property& Domain::property(PropertyId id) const;`
- `std::size_t Domain::propertyCount() const noexcept;`
Compatibility helpers may remain if useful:
- `addShellProperty(std::unique_ptr<fesa::property::ShellProperty>)`
- `findShellProperty(PropertyId)`
- `shellProperty(PropertyId)`
- `shellPropertyCount()`
Rules:
- Property storage must be `std::unique_ptr<Property>`.
- Reject null property pointers.
- Reject duplicate property ids.
- For `ShellProperty`, reject missing material id.
- `Domain::addElement` must validate `Element::propertyId()` using runtime property storage.
## Tests To Write First
- Rewrite relevant property assertions in `/tests/core/domain_storage_test.cpp` to use runtime property ownership.
- Add a test that `const Domain::property(id)` returns `const Property&`.
- Preserve direct `ShellProperty` lookup for now if compatibility helpers remain.
## 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. Do not add solver vectors, equation ids, or integration-point state to `Domain`.
3. Update `phases/property-model-foundation/index.json` for this step result.
+42
View File
@@ -0,0 +1,42 @@
# Step 4: 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/property-model-foundation-implementation-plan.md`
- `/phases/property-model-foundation/index.json`
## Task
Record validation evidence and handoff notes after the property model foundation implementation.
Required updates:
- Update `/docs/PROGRESS.md` with completed property base and Domain property ownership work.
- Update `/docs/PLAN.md` only if sequencing or acceptance criteria changed.
- Update `/docs/WORKNOTE.md` only if there were failures, repeated mistakes, or environment traps.
- Mark `/phases/property-model-foundation/index.json` steps and `/phases/index.json` 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 MITC4 formulation correctness.
2. Do not claim HDF5 result output is implemented.
3. Keep validation evidence concrete: command, result, and scope.
+40 -11
View File
@@ -172,8 +172,8 @@ void Domain::addElement(std::unique_ptr<fesa::element::Element> element) {
throw std::invalid_argument("element references missing node id");
}
}
if (findShellProperty(element->propertyId()) == nullptr) {
throw std::invalid_argument("element references missing shell property id");
if (findProperty(element->propertyId()) == nullptr) {
throw std::invalid_argument("element references missing property id");
}
elements_.emplace(id, std::move(element));
}
@@ -189,15 +189,28 @@ void Domain::addMaterial(std::unique_ptr<fesa::material::Material> material) {
}
}
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");
void Domain::addProperty(std::unique_ptr<fesa::property::Property> property) {
if (!property) {
throw std::invalid_argument("property is null");
}
if (findMaterial(property.materialId()) == nullptr) {
const PropertyId id = property->id();
if (properties_.find(id) != properties_.end()) {
throw std::invalid_argument("duplicate property id");
}
if (property->kind() == fesa::property::PropertyKind::Shell) {
const auto* shell_property = dynamic_cast<const fesa::property::ShellProperty*>(property.get());
if (shell_property == nullptr) {
throw std::invalid_argument("shell property kind does not match shell property type");
}
if (findMaterial(shell_property->materialId()) == nullptr) {
throw std::invalid_argument("shell property references missing material id");
}
shell_properties_.emplace(id, std::move(property));
}
properties_.emplace(id, std::move(property));
}
void Domain::addShellProperty(std::unique_ptr<fesa::property::ShellProperty> property) {
addProperty(std::move(property));
}
void Domain::addNodeSet(std::string name, std::vector<NodeId> node_ids) {
@@ -338,9 +351,25 @@ std::size_t Domain::materialCount() const noexcept {
return materials_.size();
}
const fesa::property::Property* Domain::findProperty(PropertyId id) const noexcept {
const auto it = properties_.find(id);
return it == properties_.end() ? nullptr : it->second.get();
}
const fesa::property::Property& Domain::property(PropertyId id) const {
const fesa::property::Property* found = findProperty(id);
if (found == nullptr) {
throw std::out_of_range("property id not found");
}
return *found;
}
std::size_t Domain::propertyCount() const noexcept {
return properties_.size();
}
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;
return dynamic_cast<const fesa::property::ShellProperty*>(findProperty(id));
}
const fesa::property::ShellProperty& Domain::shellProperty(PropertyId id) const {
@@ -352,7 +381,7 @@ const fesa::property::ShellProperty& Domain::shellProperty(PropertyId id) const
}
std::size_t Domain::shellPropertyCount() const noexcept {
return shell_properties_.size();
return properties_.size();
}
const std::vector<NodeId>* Domain::findNodeSet(const std::string& name) const noexcept {
+19
View File
@@ -0,0 +1,19 @@
#include "fesa/io/Hdf5ResultWriter.hpp"
#include <stdexcept>
#include <utility>
namespace fesa::io {
Hdf5ResultWriter::Hdf5ResultWriter(std::string file_path)
: file_path_(std::move(file_path)) {
if (file_path_.empty()) {
throw std::invalid_argument("HDF5 result writer path must not be empty");
}
}
const std::string& Hdf5ResultWriter::filePath() const noexcept {
return file_path_;
}
} // namespace fesa::io
+4
View File
@@ -15,6 +15,10 @@ PropertyId ShellProperty::id() const noexcept {
return id_;
}
PropertyKind ShellProperty::kind() const noexcept {
return PropertyKind::Shell;
}
MaterialId ShellProperty::materialId() const noexcept {
return material_id_;
}
+1 -1
View File
@@ -33,7 +33,7 @@ fesa::core::Domain populated_domain() {
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});
domain.addShellProperty(std::make_unique<fesa::property::ShellProperty>(500, 700, 0.01));
return domain;
}
+31 -8
View File
@@ -39,7 +39,7 @@ void add_four_nodes(fesa::core::Domain& domain) {
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});
domain.addShellProperty(std::make_unique<fesa::property::ShellProperty>(500, 700, 0.01));
}
void add_element(fesa::core::Domain& domain) {
@@ -215,21 +215,29 @@ int add_and_retrieve_material_and_property() {
return result;
}
const fesa::property::ShellProperty* property = domain.findShellProperty(500);
const fesa::property::Property* property = domain.findProperty(500);
if (const int result = require(property != nullptr); result != 0) {
return result;
}
if (const int result = require(property->materialId() == 700); result != 0) {
if (const int result = require(property->kind() == fesa::property::PropertyKind::Shell); result != 0) {
return result;
}
if (const int result = require(property->thickness() == 0.01); result != 0) {
const fesa::property::ShellProperty* shell_property = domain.findShellProperty(500);
if (const int result = require(shell_property != nullptr); result != 0) {
return result;
}
if (const int result = require(shell_property->materialId() == 700); result != 0) {
return result;
}
if (const int result = require(shell_property->thickness() == 0.01); result != 0) {
return result;
}
if (const int result = require(domain.material(700).id() == 700); result != 0) {
return result;
}
return require(domain.shellProperty(500).id() == 500);
return require(domain.property(500).id() == 500);
}
int duplicate_material_and_property_ids_throw() {
@@ -244,7 +252,7 @@ int duplicate_material_and_property_ids_throw() {
}
return require_throws<std::invalid_argument>([&domain]() {
domain.addShellProperty(fesa::property::ShellProperty{500, 700, 0.02});
domain.addShellProperty(std::make_unique<fesa::property::ShellProperty>(500, 700, 0.02));
});
}
@@ -252,7 +260,15 @@ int shell_property_referencing_missing_material_throws() {
fesa::core::Domain domain;
return require_throws<std::invalid_argument>([&domain]() {
domain.addShellProperty(fesa::property::ShellProperty{500, 700, 0.01});
domain.addShellProperty(std::make_unique<fesa::property::ShellProperty>(500, 700, 0.01));
});
}
int null_property_rejected() {
fesa::core::Domain domain;
return require_throws<std::invalid_argument>([&domain]() {
domain.addProperty(nullptr);
});
}
@@ -517,6 +533,10 @@ int const_domain_retrieval_returns_const_runtime_model_data() {
result != 0) {
return result;
}
if (const int result = require((std::is_same<decltype(const_domain.property(500)), const fesa::property::Property&>::value));
result != 0) {
return result;
}
if (const int result = require((std::is_same<decltype(const_domain.shellProperty(500)), const fesa::property::ShellProperty&>::value));
result != 0) {
return result;
@@ -567,7 +587,7 @@ int failed_inserts_do_not_mutate_counts() {
}
if (const int result = require_throws<std::invalid_argument>([&domain]() {
domain.addShellProperty(fesa::property::ShellProperty{501, 404, 0.01});
domain.addShellProperty(std::make_unique<fesa::property::ShellProperty>(501, 404, 0.01));
});
result != 0) {
return result;
@@ -642,6 +662,9 @@ int run_domain_storage_tests() {
if (const int result = shell_property_referencing_missing_material_throws(); result != 0) {
return result;
}
if (const int result = null_property_rejected(); result != 0) {
return result;
}
if (const int result = add_and_retrieve_sets(); result != 0) {
return result;
}
+44
View File
@@ -0,0 +1,44 @@
#include "fesa/io/Hdf5ResultWriter.hpp"
#include <stdexcept>
namespace {
int require(bool condition) {
return condition ? 0 : 1;
}
template <typename Exception, typename Function>
int require_throws(Function&& function) {
try {
function();
} catch (const Exception&) {
return 0;
} catch (...) {
return 1;
}
return 1;
}
int construct_writer_preserves_output_path() {
const fesa::io::Hdf5ResultWriter writer{"results.h5"};
return require(writer.filePath() == "results.h5");
}
int empty_output_path_throws() {
return require_throws<std::invalid_argument>([]() {
(void)fesa::io::Hdf5ResultWriter{""};
});
}
} // namespace
int main() {
if (const int result = construct_writer_preserves_output_path(); result != 0) {
return result;
}
if (const int result = empty_output_path_throws(); result != 0) {
return result;
}
return 0;
}
+4
View File
@@ -3,6 +3,7 @@ int run_element_base_tests();
int run_load_base_tests();
int run_material_base_tests();
int run_mitc4_element_model_tests();
int run_property_base_tests();
int run_shell_property_tests();
int main() {
@@ -18,6 +19,9 @@ int main() {
if (const int result = run_material_base_tests(); result != 0) {
return result;
}
if (const int result = run_property_base_tests(); result != 0) {
return result;
}
if (const int result = run_shell_property_tests(); result != 0) {
return result;
}
+37
View File
@@ -0,0 +1,37 @@
#include "fesa/property/Property.hpp"
#include <memory>
namespace {
int require(bool condition) {
return condition ? 0 : 1;
}
class TestProperty final : public fesa::property::Property {
public:
explicit TestProperty(fesa::core::PropertyId id) : id_(id) {}
fesa::core::PropertyId id() const noexcept override {
return id_;
}
fesa::property::PropertyKind kind() const noexcept override {
return fesa::property::PropertyKind::Shell;
}
private:
fesa::core::PropertyId id_;
};
} // namespace
int run_property_base_tests() {
std::unique_ptr<fesa::property::Property> owned = std::make_unique<TestProperty>(500);
const fesa::property::Property& property = *owned;
if (const int result = require(property.id() == 500); result != 0) {
return result;
}
return require(property.kind() == fesa::property::PropertyKind::Shell);
}
+8
View File
@@ -1,4 +1,5 @@
#include "fesa/property/ShellProperty.hpp"
#include "fesa/property/Property.hpp"
#include <stdexcept>
@@ -24,10 +25,17 @@ int require_throws(Function&& function) {
int run_shell_property_tests() {
const fesa::property::ShellProperty property{500, 700, 0.01};
const fesa::property::Property& base = property;
if (const int result = require(property.id() == 500); result != 0) {
return result;
}
if (const int result = require(base.id() == 500); result != 0) {
return result;
}
if (const int result = require(base.kind() == fesa::property::PropertyKind::Shell); result != 0) {
return result;
}
if (const int result = require(property.materialId() == 700); result != 0) {
return result;
}