feat: add mitc4 material integration scaffolding
This commit is contained in:
@@ -13,7 +13,7 @@ Every new agent session must read this file together with `PROGRESS.md` before p
|
|||||||
- If an item becomes obsolete, move it to `PROGRESS.md` with a short reason instead of silently deleting it.
|
- If an item becomes obsolete, move it to `PROGRESS.md` with a short reason instead of silently deleting it.
|
||||||
|
|
||||||
## Current Objective
|
## Current Objective
|
||||||
Continue the new Phase 1 rebaseline plan in `phases/1-linear-static-mitc4-rebaseline`, starting with P1R-09 material matrix, transform, and `2 x 2 x 2` integration scaffolding. The old `phases/1-linear-static-mitc4` path is historical and superseded by the paper-based MITC4 formulation reset.
|
Continue the new Phase 1 rebaseline plan in `phases/1-linear-static-mitc4-rebaseline`, starting with P1R-10 MITC4 stiffness, internal force, six-DOF transform, and drilling stabilization. The old `phases/1-linear-static-mitc4` path is historical and superseded by the paper-based MITC4 formulation reset.
|
||||||
|
|
||||||
## Required Reading For New Agents
|
## Required Reading For New Agents
|
||||||
1. `AGENTS.md`
|
1. `AGENTS.md`
|
||||||
@@ -36,7 +36,7 @@ Continue the new Phase 1 rebaseline plan in `phases/1-linear-static-mitc4-rebase
|
|||||||
## Active Phase Files
|
## Active Phase Files
|
||||||
- Active phase directory: `phases/1-linear-static-mitc4-rebaseline`
|
- Active phase directory: `phases/1-linear-static-mitc4-rebaseline`
|
||||||
- Execute with: `python scripts/execute.py 1-linear-static-mitc4-rebaseline`
|
- Execute with: `python scripts/execute.py 1-linear-static-mitc4-rebaseline`
|
||||||
- Step numbering is zero-based. `step0.md` is complete and recorded in `phases/1-linear-static-mitc4-rebaseline/step0-audit.md`; `step1.md` is complete and created the `quad_02_phase1.inp` normalized reference path; `step2.md` is complete and revalidated core harness guardrails; `step3.md` is complete and revalidated the Phase 1 parser/domain subset; `step4.md` is complete and strengthened validation/singular diagnostics; `step5.md` is complete and revalidated the DofManager/reaction foundation; `step6.md` is complete and revalidated the minimum result model plus displacement CSV comparator; `step7.md` is complete and revalidated MITC4 natural coordinates, tying points, center directors, and integration bases; `step8.md` is complete and revalidated degenerated-continuum displacement, direct covariant strain rows, and MITC shear tying rows; `step15.md` is the independent evaluator closeout.
|
- Step numbering is zero-based. `step0.md` is complete and recorded in `phases/1-linear-static-mitc4-rebaseline/step0-audit.md`; `step1.md` is complete and created the `quad_02_phase1.inp` normalized reference path; `step2.md` is complete and revalidated core harness guardrails; `step3.md` is complete and revalidated the Phase 1 parser/domain subset; `step4.md` is complete and strengthened validation/singular diagnostics; `step5.md` is complete and revalidated the DofManager/reaction foundation; `step6.md` is complete and revalidated the minimum result model plus displacement CSV comparator; `step7.md` is complete and revalidated MITC4 natural coordinates, tying points, center directors, and integration bases; `step8.md` is complete and revalidated degenerated-continuum displacement, direct covariant strain rows, and MITC shear tying rows; `step9.md` is complete and revalidated plane-stress material, convected-to-local transform, and `2 x 2 x 2` material integration scaffolding; `step15.md` is the independent evaluator closeout.
|
||||||
- Every step file contains a sprint contract with objective, required reading, scope, allowed files, explicit non-goals, tests to write first, reference artifacts, acceptance command, evaluator checklist, and handoff requirements.
|
- Every step file contains a sprint contract with objective, required reading, scope, allowed files, explicit non-goals, tests to write first, reference artifacts, acceptance command, evaluator checklist, and handoff requirements.
|
||||||
- Historical phase directory: `phases/1-linear-static-mitc4`
|
- Historical phase directory: `phases/1-linear-static-mitc4`
|
||||||
- Historical phase status: blocked/superseded. Do not resume the old P1-15/P1-16 path unless the user explicitly requests recovery of that exact phase.
|
- Historical phase status: blocked/superseded. Do not resume the old P1-15/P1-16 path unless the user explicitly requests recovery of that exact phase.
|
||||||
@@ -76,7 +76,7 @@ Each gate should be satisfied before moving to the next implementation band unle
|
|||||||
| G1 - Build and validation | satisfied | Build system, test framework, and `scripts/validate_workspace.py` run real checks. | Validation command output |
|
| G1 - Build and validation | satisfied | Build system, test framework, and `scripts/validate_workspace.py` run real checks. | Validation command output |
|
||||||
| G2 - Parser and domain | satisfied | Parser subset revalidated in step 3; validation and singular diagnostics revalidated in step 4. | Parser acceptance/rejection tests, validation negative tests, and validation output |
|
| G2 - Parser and domain | satisfied | Parser subset revalidated in step 3; validation and singular diagnostics revalidated in step 4. | Parser acceptance/rejection tests, validation negative tests, and validation output |
|
||||||
| G3 - DOF/math/results infrastructure | partial | Core aliases, DOF mapping, validation harness, model diagnostic context, DofManager, sparse-connectivity inputs, full-vector reaction formula, result model metadata, and displacement CSV comparator were revalidated in steps 2, 5, and 6; assembly remains for step 12. | P1R-02, P1R-05, and P1R-06 validation output |
|
| G3 - DOF/math/results infrastructure | partial | Core aliases, DOF mapping, validation harness, model diagnostic context, DofManager, sparse-connectivity inputs, full-vector reaction formula, result model metadata, and displacement CSV comparator were revalidated in steps 2, 5, and 6; assembly remains for step 12. | P1R-02, P1R-05, and P1R-06 validation output |
|
||||||
| G4 - MITC4 element readiness | partial | MITC4 formulation was rewritten from local papers; Steps 7 and 8 rebuilt geometry/director/local-basis scaffolding, displacement interpolation, direct covariant strain rows, and MITC shear tying rows, while material/integration, stiffness/drilling, and patch benchmarks remain for steps 9 through 11. | P1R-07 and P1R-08 validation output; future element tests |
|
| G4 - MITC4 element readiness | partial | MITC4 formulation was rewritten from local papers; Steps 7 through 9 rebuilt geometry/director/local-basis scaffolding, displacement interpolation, direct covariant strain rows, MITC shear tying rows, plane-stress material, convected-to-local transform, and `2 x 2 x 2` material integration scaffolding, while stiffness/drilling and patch benchmarks remain for steps 10 and 11. | P1R-07 through P1R-09 validation output; future element tests |
|
||||||
| G5 - End-to-end solver | reopened | Linear static path must be revalidated through steps 13 and 14 after the MITC4 rebuild and `quad_02` compatibility path. | Future integration/reference regression output |
|
| G5 - End-to-end solver | reopened | Linear static path must be revalidated through steps 13 and 14 after the MITC4 rebuild and `quad_02` compatibility path. | Future integration/reference regression output |
|
||||||
|
|
||||||
## Phase 1 Implementation Milestones
|
## Phase 1 Implementation Milestones
|
||||||
@@ -90,7 +90,7 @@ All milestones are intended to become one or more self-contained sprint contract
|
|||||||
| P1R-06 | completed | results generator | Rebuild minimum results model and displacement CSV comparator. | none | U/RF schema tests and CSV comparator tests |
|
| P1R-06 | completed | results generator | Rebuild minimum results model and displacement CSV comparator. | none | U/RF schema tests and CSV comparator tests |
|
||||||
| P1R-07 | completed | MITC4 generator | Implement MITC4 geometry, node order, tying points, directors, and local bases. | none | Shape/basis/diagnostic tests |
|
| P1R-07 | completed | MITC4 generator | Implement MITC4 geometry, node order, tying points, directors, and local bases. | none | Shape/basis/diagnostic tests |
|
||||||
| P1R-08 | completed | MITC4 generator | Implement degenerated-continuum displacement, covariant strain rows, and MITC shear tying. | P1R-07 | Finite-difference and tying interpolation tests |
|
| P1R-08 | completed | MITC4 generator | Implement degenerated-continuum displacement, covariant strain rows, and MITC shear tying. | P1R-07 | Finite-difference and tying interpolation tests |
|
||||||
| P1R-09 | pending | MITC4 generator | Implement material matrix, transform, and `2 x 2 x 2` integration scaffolding. | P1R-08 | Material/integration tests |
|
| P1R-09 | completed | MITC4 generator | Implement material matrix, transform, and `2 x 2 x 2` integration scaffolding. | P1R-08 | Material/integration tests |
|
||||||
| P1R-10 | pending | MITC4 generator | Assemble MITC4 stiffness/internal force with six-DOF transform and drilling stabilization. | P1R-09, P1R-05 | Symmetry, rigid body, drilling sensitivity tests |
|
| P1R-10 | pending | MITC4 generator | Assemble MITC4 stiffness/internal force with six-DOF transform and drilling stabilization. | P1R-09, P1R-05 | Symmetry, rigid body, drilling sensitivity tests |
|
||||||
| P1R-11 | pending | verification generator | Add MITC4 patch, locking-sensitivity, and benchmark tests. | P1R-10 | Membrane/bending/shear/twist/locking tests |
|
| P1R-11 | pending | verification generator | Add MITC4 patch, locking-sensitivity, and benchmark tests. | P1R-10 | Membrane/bending/shear/twist/locking tests |
|
||||||
| P1R-12 | pending | assembly generator | Rebuild assembly, solver adapter boundary, constrained solve, and full-vector RF recovery. | P1R-05, P1R-10 | Assembly and full-vector reaction tests |
|
| P1R-12 | pending | assembly generator | Rebuild assembly, solver adapter boundary, constrained solve, and full-vector RF recovery. | P1R-05, P1R-10 | Assembly and full-vector reaction tests |
|
||||||
|
|||||||
+27
-1
@@ -13,10 +13,36 @@ Every new agent session must read this file together with `PLAN.md` before plann
|
|||||||
- Do not remove history unless the user explicitly asks for archival cleanup.
|
- Do not remove history unless the user explicitly asks for archival cleanup.
|
||||||
|
|
||||||
## Current Status
|
## Current Status
|
||||||
Phase 1 has a new rebaseline phase definition in `phases/1-linear-static-mitc4-rebaseline`. Steps 0 through 8 are complete. `quad_02_phase1.inp` is now the normalized Phase 1-compatible input path for the stored `quad_02` S4 reference pair, while the original `quad_02.inp` remains preserved unsupported provenance. Core numeric aliases, DOF mapping, validation harness, model diagnostic context, the Phase 1 parser/domain subset, validation/singular diagnostics, DofManager/reaction foundation, minimum result model metadata, displacement CSV comparator foundation, MITC4 geometry/director scaffolding, and MITC4 displacement/strain/tying row scaffolding have been revalidated. The old `phases/1-linear-static-mitc4` path is historical and superseded after the MITC4 formulation reset.
|
Phase 1 has a new rebaseline phase definition in `phases/1-linear-static-mitc4-rebaseline`. Steps 0 through 9 are complete. `quad_02_phase1.inp` is now the normalized Phase 1-compatible input path for the stored `quad_02` S4 reference pair, while the original `quad_02.inp` remains preserved unsupported provenance. Core numeric aliases, DOF mapping, validation harness, model diagnostic context, the Phase 1 parser/domain subset, validation/singular diagnostics, DofManager/reaction foundation, minimum result model metadata, displacement CSV comparator foundation, MITC4 geometry/director scaffolding, MITC4 displacement/strain/tying row scaffolding, and MITC4 material/transform/integration scaffolding have been revalidated. The old `phases/1-linear-static-mitc4` path is historical and superseded after the MITC4 formulation reset.
|
||||||
|
|
||||||
## Completed Work
|
## Completed Work
|
||||||
|
|
||||||
|
### 2026-05-04 - P1R-09 MITC4 material integration scaffolding completed
|
||||||
|
Author: Codex
|
||||||
|
|
||||||
|
Changed files:
|
||||||
|
- `include/fesa/fesa.hpp`
|
||||||
|
- `tests/test_main.cpp`
|
||||||
|
- `phases/1-linear-static-mitc4-rebaseline/index.json`
|
||||||
|
- `PLAN.md`
|
||||||
|
- `PROGRESS.md`
|
||||||
|
|
||||||
|
Summary:
|
||||||
|
- Added tests and implementation for the documented isotropic local plane-stress shell material matrix in strain order `[eps11, eps22, eps33, gamma23, gamma13, gamma12]`, with `sigma_33 = 0` and transverse shear correction `kappa = 5/6`.
|
||||||
|
- Added material diagnostics for invalid elastic modulus, Poisson ratio, and shear correction factor.
|
||||||
|
- Added `2 x 2 x 2` Gauss integration point infrastructure with unit weights and total natural-domain weight `8`.
|
||||||
|
- Added covariant-to-local strain transformation and `D_convected = T^T D_hat T`, including identity and flat-element energy-equivalence tests.
|
||||||
|
- Added MITC4 material integration sample scaffolding that carries integration basis, tied strain rows, local material, strain transform, and convected material for the later stiffness rebuild.
|
||||||
|
|
||||||
|
Verification:
|
||||||
|
- First ran `python scripts/validate_workspace.py` after adding Step 9 tests; it failed as expected because the MITC4 material/integration APIs did not exist yet.
|
||||||
|
- After implementation, `python scripts/validate_workspace.py` configured CMake, built `fesa_core` and `fesa_tests`, and ran CTest successfully.
|
||||||
|
- CTest result: 1 test executable passed.
|
||||||
|
|
||||||
|
Follow-up:
|
||||||
|
- Continue with P1R-10 MITC4 stiffness/internal force rebuild, six-DOF local-to-global transform, and drilling stabilization.
|
||||||
|
- The legacy `MITC4ElementKernel::stiffness` path still needs to be rebuilt against the Step 7 through Step 9 formulation helpers; Step 9 intentionally did not add reduced integration, hourglass logic, stress/result output recovery, or the final drilling stiffness policy.
|
||||||
|
|
||||||
### 2026-05-04 - P1R-08 MITC4 covariant strain tying completed
|
### 2026-05-04 - P1R-08 MITC4 covariant strain tying completed
|
||||||
Author: Codex
|
Author: Codex
|
||||||
|
|
||||||
|
|||||||
@@ -1304,6 +1304,7 @@ struct MITC4IntegrationBasis {
|
|||||||
using MITC4ElementDofVector = std::array<Real, 24>;
|
using MITC4ElementDofVector = std::array<Real, 24>;
|
||||||
using MITC4StrainVector = std::array<Real, 6>;
|
using MITC4StrainVector = std::array<Real, 6>;
|
||||||
using MITC4StrainRow = std::array<Real, 24>;
|
using MITC4StrainRow = std::array<Real, 24>;
|
||||||
|
using MITC4MaterialMatrix = std::array<std::array<Real, 6>, 6>;
|
||||||
|
|
||||||
enum class MITC4StrainComponent {
|
enum class MITC4StrainComponent {
|
||||||
Eps11 = 0,
|
Eps11 = 0,
|
||||||
@@ -1359,6 +1360,54 @@ struct MITC4StrainRows {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct MITC4MaterialMatrixEvaluation {
|
||||||
|
MITC4MaterialMatrix matrix{};
|
||||||
|
std::vector<Diagnostic> diagnostics;
|
||||||
|
|
||||||
|
bool ok() const {
|
||||||
|
return !hasError(diagnostics);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MITC4StrainTransform {
|
||||||
|
MITC4MaterialMatrix matrix{};
|
||||||
|
std::vector<Diagnostic> diagnostics;
|
||||||
|
|
||||||
|
bool ok() const {
|
||||||
|
return !hasError(diagnostics);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MITC4IntegrationPoint {
|
||||||
|
Real xi = 0.0;
|
||||||
|
Real eta = 0.0;
|
||||||
|
Real zeta = 0.0;
|
||||||
|
Real weight = 0.0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MITC4MaterialIntegrationSample {
|
||||||
|
MITC4IntegrationPoint point;
|
||||||
|
MITC4IntegrationBasis basis;
|
||||||
|
MITC4StrainRows strain_rows;
|
||||||
|
MITC4MaterialMatrix local_material{};
|
||||||
|
MITC4MaterialMatrix strain_transform{};
|
||||||
|
MITC4MaterialMatrix convected_material{};
|
||||||
|
std::vector<Diagnostic> diagnostics;
|
||||||
|
|
||||||
|
bool ok() const {
|
||||||
|
return !hasError(diagnostics);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MITC4MaterialIntegrationData {
|
||||||
|
std::vector<MITC4MaterialIntegrationSample> samples;
|
||||||
|
std::vector<Diagnostic> diagnostics;
|
||||||
|
|
||||||
|
bool ok() const {
|
||||||
|
return !hasError(diagnostics);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
inline Vec3 globalEX() {
|
inline Vec3 globalEX() {
|
||||||
return {1.0, 0.0, 0.0};
|
return {1.0, 0.0, 0.0};
|
||||||
}
|
}
|
||||||
@@ -1624,6 +1673,209 @@ inline MITC4StrainRows mitc4TiedCovariantStrainRows(const MITC4Geometry& geometr
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline std::array<MITC4IntegrationPoint, 8> mitc4GaussQuadrature2x2x2() {
|
||||||
|
const Real gauss = 1.0 / std::sqrt(3.0);
|
||||||
|
const std::array<Real, 2> points = {-gauss, gauss};
|
||||||
|
std::array<MITC4IntegrationPoint, 8> integration_points{};
|
||||||
|
std::size_t index = 0;
|
||||||
|
for (Real xi : points) {
|
||||||
|
for (Real eta : points) {
|
||||||
|
for (Real zeta : points) {
|
||||||
|
integration_points[index++] = {xi, eta, zeta, 1.0};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return integration_points;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline MITC4MaterialMatrixEvaluation mitc4PlaneStressMaterialMatrix(Real elastic_modulus, Real poisson_ratio,
|
||||||
|
Real shear_correction = 5.0 / 6.0,
|
||||||
|
Real tolerance = 1.0e-12) {
|
||||||
|
MITC4MaterialMatrixEvaluation result;
|
||||||
|
if (!isFinite(elastic_modulus) || elastic_modulus <= tolerance) {
|
||||||
|
result.diagnostics.push_back(
|
||||||
|
mitc4Diagnostic("FESA-MITC4-MATERIAL", "MITC4 elastic modulus must be positive and finite"));
|
||||||
|
}
|
||||||
|
if (!isFinite(poisson_ratio) || poisson_ratio <= -1.0 || poisson_ratio >= 0.5) {
|
||||||
|
result.diagnostics.push_back(
|
||||||
|
mitc4Diagnostic("FESA-MITC4-POISSON", "MITC4 isotropic Poisson ratio must satisfy -1 < nu < 0.5"));
|
||||||
|
}
|
||||||
|
if (!isFinite(shear_correction) || shear_correction <= tolerance) {
|
||||||
|
result.diagnostics.push_back(
|
||||||
|
mitc4Diagnostic("FESA-MITC4-SHEAR-CORRECTION", "MITC4 shear correction factor must be positive and finite"));
|
||||||
|
}
|
||||||
|
if (hasError(result.diagnostics)) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Real scale = elastic_modulus / (1.0 - poisson_ratio * poisson_ratio);
|
||||||
|
const Real shear_modulus = elastic_modulus / (2.0 * (1.0 + poisson_ratio));
|
||||||
|
const std::size_t eps11 = strainComponentIndex(MITC4StrainComponent::Eps11);
|
||||||
|
const std::size_t eps22 = strainComponentIndex(MITC4StrainComponent::Eps22);
|
||||||
|
const std::size_t gamma23 = strainComponentIndex(MITC4StrainComponent::Gamma23);
|
||||||
|
const std::size_t gamma13 = strainComponentIndex(MITC4StrainComponent::Gamma13);
|
||||||
|
const std::size_t gamma12 = strainComponentIndex(MITC4StrainComponent::Gamma12);
|
||||||
|
|
||||||
|
result.matrix[eps11][eps11] = scale;
|
||||||
|
result.matrix[eps11][eps22] = poisson_ratio * scale;
|
||||||
|
result.matrix[eps22][eps11] = poisson_ratio * scale;
|
||||||
|
result.matrix[eps22][eps22] = scale;
|
||||||
|
result.matrix[gamma23][gamma23] = shear_correction * shear_modulus;
|
||||||
|
result.matrix[gamma13][gamma13] = shear_correction * shear_modulus;
|
||||||
|
result.matrix[gamma12][gamma12] = shear_modulus;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline MITC4StrainVector multiplyMITC4MaterialMatrix(const MITC4MaterialMatrix& matrix, const MITC4StrainVector& vector) {
|
||||||
|
MITC4StrainVector result{};
|
||||||
|
for (std::size_t row = 0; row < 6; ++row) {
|
||||||
|
for (std::size_t col = 0; col < 6; ++col) {
|
||||||
|
result[row] += matrix[row][col] * vector[col];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Real dotMITC4Vector(const MITC4StrainVector& a, const MITC4StrainVector& b) {
|
||||||
|
Real result = 0.0;
|
||||||
|
for (std::size_t i = 0; i < 6; ++i) {
|
||||||
|
result += a[i] * b[i];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline MITC4MaterialMatrix mitc4TransformMaterialMatrix(const MITC4MaterialMatrix& local_material,
|
||||||
|
const MITC4MaterialMatrix& covariant_to_local) {
|
||||||
|
MITC4MaterialMatrix result{};
|
||||||
|
for (std::size_t i = 0; i < 6; ++i) {
|
||||||
|
for (std::size_t j = 0; j < 6; ++j) {
|
||||||
|
Real value = 0.0;
|
||||||
|
for (std::size_t a = 0; a < 6; ++a) {
|
||||||
|
for (std::size_t b = 0; b < 6; ++b) {
|
||||||
|
value += covariant_to_local[a][i] * local_material[a][b] * covariant_to_local[b][j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result[i][j] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::array<std::array<Real, 3>, 3> mitc4TensorFromEngineeringComponent(std::size_t component) {
|
||||||
|
std::array<std::array<Real, 3>, 3> tensor{};
|
||||||
|
switch (static_cast<MITC4StrainComponent>(component)) {
|
||||||
|
case MITC4StrainComponent::Eps11:
|
||||||
|
tensor[0][0] = 1.0;
|
||||||
|
break;
|
||||||
|
case MITC4StrainComponent::Eps22:
|
||||||
|
tensor[1][1] = 1.0;
|
||||||
|
break;
|
||||||
|
case MITC4StrainComponent::Eps33:
|
||||||
|
tensor[2][2] = 1.0;
|
||||||
|
break;
|
||||||
|
case MITC4StrainComponent::Gamma23:
|
||||||
|
tensor[1][2] = 0.5;
|
||||||
|
tensor[2][1] = 0.5;
|
||||||
|
break;
|
||||||
|
case MITC4StrainComponent::Gamma13:
|
||||||
|
tensor[0][2] = 0.5;
|
||||||
|
tensor[2][0] = 0.5;
|
||||||
|
break;
|
||||||
|
case MITC4StrainComponent::Gamma12:
|
||||||
|
tensor[0][1] = 0.5;
|
||||||
|
tensor[1][0] = 0.5;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return tensor;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline MITC4StrainVector mitc4EngineeringVectorFromTensor(const std::array<std::array<Real, 3>, 3>& tensor) {
|
||||||
|
MITC4StrainVector vector{};
|
||||||
|
vector[strainComponentIndex(MITC4StrainComponent::Eps11)] = tensor[0][0];
|
||||||
|
vector[strainComponentIndex(MITC4StrainComponent::Eps22)] = tensor[1][1];
|
||||||
|
vector[strainComponentIndex(MITC4StrainComponent::Eps33)] = tensor[2][2];
|
||||||
|
vector[strainComponentIndex(MITC4StrainComponent::Gamma23)] = 2.0 * tensor[1][2];
|
||||||
|
vector[strainComponentIndex(MITC4StrainComponent::Gamma13)] = 2.0 * tensor[0][2];
|
||||||
|
vector[strainComponentIndex(MITC4StrainComponent::Gamma12)] = 2.0 * tensor[0][1];
|
||||||
|
return vector;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline MITC4StrainTransform mitc4CovariantToLocalStrainTransform(const MITC4IntegrationBasis& basis,
|
||||||
|
Real tolerance = 1.0e-12) {
|
||||||
|
MITC4StrainTransform result;
|
||||||
|
result.diagnostics = basis.diagnostics;
|
||||||
|
const Real jacobian = dot(cross(basis.g1, basis.g2), basis.g3);
|
||||||
|
if (!isFinite(jacobian) || std::fabs(jacobian) <= tolerance) {
|
||||||
|
result.diagnostics.push_back(
|
||||||
|
mitc4Diagnostic("FESA-MITC4-SINGULAR-JACOBIAN", "MITC4 material transform Jacobian is near zero"));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if (hasError(result.diagnostics)) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::array<Vec3, 3> contravariant = {
|
||||||
|
(1.0 / jacobian) * cross(basis.g2, basis.g3),
|
||||||
|
(1.0 / jacobian) * cross(basis.g3, basis.g1),
|
||||||
|
(1.0 / jacobian) * cross(basis.g1, basis.g2)};
|
||||||
|
const std::array<Vec3, 3> local = {basis.local.e1, basis.local.e2, basis.local.e3};
|
||||||
|
std::array<std::array<Real, 3>, 3> direction_cosines{};
|
||||||
|
for (std::size_t local_axis = 0; local_axis < 3; ++local_axis) {
|
||||||
|
for (std::size_t convected_axis = 0; convected_axis < 3; ++convected_axis) {
|
||||||
|
direction_cosines[local_axis][convected_axis] = dot(local[local_axis], contravariant[convected_axis]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::size_t column = 0; column < 6; ++column) {
|
||||||
|
const auto covariant_tensor = mitc4TensorFromEngineeringComponent(column);
|
||||||
|
std::array<std::array<Real, 3>, 3> local_tensor{};
|
||||||
|
for (std::size_t a = 0; a < 3; ++a) {
|
||||||
|
for (std::size_t b = 0; b < 3; ++b) {
|
||||||
|
for (std::size_t i = 0; i < 3; ++i) {
|
||||||
|
for (std::size_t j = 0; j < 3; ++j) {
|
||||||
|
local_tensor[a][b] += direction_cosines[a][i] * direction_cosines[b][j] * covariant_tensor[i][j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const auto local_vector = mitc4EngineeringVectorFromTensor(local_tensor);
|
||||||
|
for (std::size_t row = 0; row < 6; ++row) {
|
||||||
|
result.matrix[row][column] = local_vector[row];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline MITC4MaterialIntegrationData mitc4BuildMaterialIntegrationData(const MITC4Geometry& geometry, Real elastic_modulus,
|
||||||
|
Real poisson_ratio,
|
||||||
|
Real shear_correction = 5.0 / 6.0) {
|
||||||
|
MITC4MaterialIntegrationData data;
|
||||||
|
const auto material = mitc4PlaneStressMaterialMatrix(elastic_modulus, poisson_ratio, shear_correction);
|
||||||
|
appendDiagnostics(data.diagnostics, material.diagnostics);
|
||||||
|
if (hasError(data.diagnostics)) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const MITC4IntegrationPoint& point : mitc4GaussQuadrature2x2x2()) {
|
||||||
|
MITC4MaterialIntegrationSample sample;
|
||||||
|
sample.point = point;
|
||||||
|
sample.local_material = material.matrix;
|
||||||
|
sample.basis = computeMITC4IntegrationBasis(geometry, point.xi, point.eta, point.zeta);
|
||||||
|
appendDiagnostics(sample.diagnostics, sample.basis.diagnostics);
|
||||||
|
const auto transform = mitc4CovariantToLocalStrainTransform(sample.basis);
|
||||||
|
sample.strain_transform = transform.matrix;
|
||||||
|
appendDiagnostics(sample.diagnostics, transform.diagnostics);
|
||||||
|
sample.strain_rows = mitc4TiedCovariantStrainRows(geometry, point.xi, point.eta, point.zeta);
|
||||||
|
appendDiagnostics(sample.diagnostics, sample.strain_rows.diagnostics);
|
||||||
|
if (!hasError(sample.diagnostics)) {
|
||||||
|
sample.convected_material = mitc4TransformMaterialMatrix(sample.local_material, sample.strain_transform);
|
||||||
|
}
|
||||||
|
appendDiagnostics(data.diagnostics, sample.diagnostics);
|
||||||
|
data.samples.push_back(sample);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
inline LocalBasis computeLocalBasis(const std::array<Vec3, 4>& coordinates) {
|
inline LocalBasis computeLocalBasis(const std::array<Vec3, 4>& coordinates) {
|
||||||
const MITC4Geometry geometry = buildMITC4Geometry(coordinates, 1.0);
|
const MITC4Geometry geometry = buildMITC4Geometry(coordinates, 1.0);
|
||||||
if (!geometry.ok()) {
|
if (!geometry.ok()) {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
{ "step": 6, "name": "results-comparator-foundation", "status": "completed" },
|
{ "step": 6, "name": "results-comparator-foundation", "status": "completed" },
|
||||||
{ "step": 7, "name": "mitc4-geometry-directors", "status": "completed" },
|
{ "step": 7, "name": "mitc4-geometry-directors", "status": "completed" },
|
||||||
{ "step": 8, "name": "mitc4-covariant-strain-tying", "status": "completed" },
|
{ "step": 8, "name": "mitc4-covariant-strain-tying", "status": "completed" },
|
||||||
{ "step": 9, "name": "mitc4-material-integration", "status": "pending" },
|
{ "step": 9, "name": "mitc4-material-integration", "status": "completed" },
|
||||||
{ "step": 10, "name": "mitc4-stiffness-drilling", "status": "pending" },
|
{ "step": 10, "name": "mitc4-stiffness-drilling", "status": "pending" },
|
||||||
{ "step": 11, "name": "mitc4-patch-benchmark-tests", "status": "pending" },
|
{ "step": 11, "name": "mitc4-patch-benchmark-tests", "status": "pending" },
|
||||||
{ "step": 12, "name": "assembly-sparse-solver-path", "status": "pending" },
|
{ "step": 12, "name": "assembly-sparse-solver-path", "status": "pending" },
|
||||||
|
|||||||
@@ -1070,6 +1070,155 @@ FESA_TEST(mitc4_mitc_gauss_shear_rows_are_interpolated_from_tying_rows) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FESA_TEST(mitc4_plane_stress_material_matrix_uses_documented_order_and_shear_correction) {
|
||||||
|
constexpr fesa::Real elastic_modulus = 210.0;
|
||||||
|
constexpr fesa::Real poisson_ratio = 0.3;
|
||||||
|
constexpr fesa::Real kappa = 5.0 / 6.0;
|
||||||
|
const auto law = fesa::mitc4PlaneStressMaterialMatrix(elastic_modulus, poisson_ratio, kappa);
|
||||||
|
FESA_CHECK(law.ok());
|
||||||
|
const auto eps11 = fesa::strainComponentIndex(fesa::MITC4StrainComponent::Eps11);
|
||||||
|
const auto eps22 = fesa::strainComponentIndex(fesa::MITC4StrainComponent::Eps22);
|
||||||
|
const auto eps33 = fesa::strainComponentIndex(fesa::MITC4StrainComponent::Eps33);
|
||||||
|
const auto gamma23 = fesa::strainComponentIndex(fesa::MITC4StrainComponent::Gamma23);
|
||||||
|
const auto gamma13 = fesa::strainComponentIndex(fesa::MITC4StrainComponent::Gamma13);
|
||||||
|
const auto gamma12 = fesa::strainComponentIndex(fesa::MITC4StrainComponent::Gamma12);
|
||||||
|
const fesa::Real plane_stress_scale = elastic_modulus / (1.0 - poisson_ratio * poisson_ratio);
|
||||||
|
const fesa::Real shear_modulus = elastic_modulus / (2.0 * (1.0 + poisson_ratio));
|
||||||
|
|
||||||
|
FESA_CHECK(fesa::mitc4StrainComponentLabels() ==
|
||||||
|
(std::array<std::string, 6>{"eps11", "eps22", "eps33", "gamma23", "gamma13", "gamma12"}));
|
||||||
|
FESA_CHECK_NEAR(law.matrix[eps11][eps11], plane_stress_scale, 1.0e-12);
|
||||||
|
FESA_CHECK_NEAR(law.matrix[eps11][eps22], poisson_ratio * plane_stress_scale, 1.0e-12);
|
||||||
|
FESA_CHECK_NEAR(law.matrix[eps22][eps11], poisson_ratio * plane_stress_scale, 1.0e-12);
|
||||||
|
FESA_CHECK_NEAR(law.matrix[eps22][eps22], plane_stress_scale, 1.0e-12);
|
||||||
|
for (std::size_t component = 0; component < 6; ++component) {
|
||||||
|
FESA_CHECK_NEAR(law.matrix[eps33][component], 0.0, 1.0e-15);
|
||||||
|
FESA_CHECK_NEAR(law.matrix[component][eps33], 0.0, 1.0e-15);
|
||||||
|
}
|
||||||
|
FESA_CHECK_NEAR(law.matrix[gamma23][gamma23], kappa * shear_modulus, 1.0e-12);
|
||||||
|
FESA_CHECK_NEAR(law.matrix[gamma13][gamma13], kappa * shear_modulus, 1.0e-12);
|
||||||
|
FESA_CHECK_NEAR(law.matrix[gamma12][gamma12], shear_modulus, 1.0e-12);
|
||||||
|
FESA_CHECK_NEAR(law.matrix[gamma13][gamma23], 0.0, 1.0e-15);
|
||||||
|
|
||||||
|
fesa::MITC4StrainVector strain{};
|
||||||
|
strain[gamma23] = 2.0;
|
||||||
|
strain[gamma13] = 3.0;
|
||||||
|
strain[gamma12] = 4.0;
|
||||||
|
const auto stress = fesa::multiplyMITC4MaterialMatrix(law.matrix, strain);
|
||||||
|
FESA_CHECK_NEAR(stress[gamma23], 2.0 * kappa * shear_modulus, 1.0e-12);
|
||||||
|
FESA_CHECK_NEAR(stress[gamma13], 3.0 * kappa * shear_modulus, 1.0e-12);
|
||||||
|
FESA_CHECK_NEAR(stress[gamma12], 4.0 * shear_modulus, 1.0e-12);
|
||||||
|
}
|
||||||
|
|
||||||
|
FESA_TEST(mitc4_material_matrix_reports_invalid_elastic_inputs) {
|
||||||
|
auto invalid_e = fesa::mitc4PlaneStressMaterialMatrix(-1.0, 0.3);
|
||||||
|
FESA_CHECK(!invalid_e.ok());
|
||||||
|
FESA_CHECK(fesa::containsDiagnostic(invalid_e.diagnostics, "FESA-MITC4-MATERIAL"));
|
||||||
|
|
||||||
|
auto invalid_nu = fesa::mitc4PlaneStressMaterialMatrix(1000.0, 0.5);
|
||||||
|
FESA_CHECK(!invalid_nu.ok());
|
||||||
|
FESA_CHECK(fesa::containsDiagnostic(invalid_nu.diagnostics, "FESA-MITC4-POISSON"));
|
||||||
|
|
||||||
|
auto invalid_kappa = fesa::mitc4PlaneStressMaterialMatrix(1000.0, 0.25, 0.0);
|
||||||
|
FESA_CHECK(!invalid_kappa.ok());
|
||||||
|
FESA_CHECK(fesa::containsDiagnostic(invalid_kappa.diagnostics, "FESA-MITC4-SHEAR-CORRECTION"));
|
||||||
|
}
|
||||||
|
|
||||||
|
FESA_TEST(mitc4_gauss_integration_uses_2x2x2_points_and_unit_weights) {
|
||||||
|
const auto points = fesa::mitc4GaussQuadrature2x2x2();
|
||||||
|
FESA_CHECK(points.size() == 8);
|
||||||
|
const fesa::Real gauss = 1.0 / std::sqrt(3.0);
|
||||||
|
fesa::Real total_weight = 0.0;
|
||||||
|
int positive_xi = 0;
|
||||||
|
int positive_eta = 0;
|
||||||
|
int positive_zeta = 0;
|
||||||
|
for (const auto& point : points) {
|
||||||
|
FESA_CHECK_NEAR(std::fabs(point.xi), gauss, 1.0e-15);
|
||||||
|
FESA_CHECK_NEAR(std::fabs(point.eta), gauss, 1.0e-15);
|
||||||
|
FESA_CHECK_NEAR(std::fabs(point.zeta), gauss, 1.0e-15);
|
||||||
|
FESA_CHECK_NEAR(point.weight, 1.0, 1.0e-15);
|
||||||
|
total_weight += point.weight;
|
||||||
|
positive_xi += point.xi > 0.0 ? 1 : 0;
|
||||||
|
positive_eta += point.eta > 0.0 ? 1 : 0;
|
||||||
|
positive_zeta += point.zeta > 0.0 ? 1 : 0;
|
||||||
|
}
|
||||||
|
FESA_CHECK_NEAR(total_weight, 8.0, 1.0e-15);
|
||||||
|
FESA_CHECK(positive_xi == 4);
|
||||||
|
FESA_CHECK(positive_eta == 4);
|
||||||
|
FESA_CHECK(positive_zeta == 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
FESA_TEST(mitc4_covariant_to_local_transform_is_identity_for_orthonormal_basis) {
|
||||||
|
fesa::MITC4IntegrationBasis basis;
|
||||||
|
basis.g1 = {1.0, 0.0, 0.0};
|
||||||
|
basis.g2 = {0.0, 1.0, 0.0};
|
||||||
|
basis.g3 = {0.0, 0.0, 1.0};
|
||||||
|
basis.local = {{1.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {0.0, 0.0, 1.0}};
|
||||||
|
basis.jacobian = 1.0;
|
||||||
|
const auto transform = fesa::mitc4CovariantToLocalStrainTransform(basis);
|
||||||
|
FESA_CHECK(transform.ok());
|
||||||
|
for (std::size_t row = 0; row < 6; ++row) {
|
||||||
|
for (std::size_t col = 0; col < 6; ++col) {
|
||||||
|
FESA_CHECK_NEAR(transform.matrix[row][col], row == col ? 1.0 : 0.0, 1.0e-15);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FESA_TEST(mitc4_flat_element_material_transform_scales_convected_strains_to_local_frame) {
|
||||||
|
const std::array<fesa::Vec3, 4> coords = {{{0, 0, 0}, {1, 0, 0}, {1, 1, 0}, {0, 1, 0}}};
|
||||||
|
const auto geometry = fesa::buildMITC4Geometry(coords, 0.2);
|
||||||
|
FESA_CHECK(geometry.ok());
|
||||||
|
const auto basis = fesa::computeMITC4IntegrationBasis(geometry, 0.0, 0.0, 0.0);
|
||||||
|
FESA_CHECK(basis.ok());
|
||||||
|
const auto transform = fesa::mitc4CovariantToLocalStrainTransform(basis);
|
||||||
|
FESA_CHECK(transform.ok());
|
||||||
|
|
||||||
|
const std::array<fesa::Real, 6> expected_diagonal = {4.0, 4.0, 100.0, 20.0, 20.0, 4.0};
|
||||||
|
for (std::size_t row = 0; row < 6; ++row) {
|
||||||
|
for (std::size_t col = 0; col < 6; ++col) {
|
||||||
|
FESA_CHECK_NEAR(transform.matrix[row][col], row == col ? expected_diagonal[row] : 0.0, 1.0e-13);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto law = fesa::mitc4PlaneStressMaterialMatrix(1000.0, 0.25);
|
||||||
|
FESA_CHECK(law.ok());
|
||||||
|
const auto convected = fesa::mitc4TransformMaterialMatrix(law.matrix, transform.matrix);
|
||||||
|
fesa::MITC4StrainVector local_strain{};
|
||||||
|
local_strain[fesa::strainComponentIndex(fesa::MITC4StrainComponent::Eps11)] = 0.02;
|
||||||
|
local_strain[fesa::strainComponentIndex(fesa::MITC4StrainComponent::Eps22)] = -0.01;
|
||||||
|
local_strain[fesa::strainComponentIndex(fesa::MITC4StrainComponent::Gamma12)] = 0.03;
|
||||||
|
|
||||||
|
fesa::MITC4StrainVector convected_strain{};
|
||||||
|
for (std::size_t component = 0; component < 6; ++component) {
|
||||||
|
convected_strain[component] = local_strain[component] / expected_diagonal[component];
|
||||||
|
}
|
||||||
|
const auto local_stress = fesa::multiplyMITC4MaterialMatrix(law.matrix, local_strain);
|
||||||
|
const auto convected_stress = fesa::multiplyMITC4MaterialMatrix(convected, convected_strain);
|
||||||
|
const fesa::Real local_energy = fesa::dotMITC4Vector(local_strain, local_stress);
|
||||||
|
const fesa::Real convected_energy = fesa::dotMITC4Vector(convected_strain, convected_stress);
|
||||||
|
FESA_CHECK_NEAR(convected_energy, local_energy, 1.0e-12);
|
||||||
|
}
|
||||||
|
|
||||||
|
FESA_TEST(mitc4_material_integration_samples_carry_tied_rows_basis_and_transformed_material) {
|
||||||
|
const std::array<fesa::Vec3, 4> coords = {{{0, 0, 0}, {1, 0, 0}, {1, 1, 0}, {0, 1, 0}}};
|
||||||
|
const auto geometry = fesa::buildMITC4Geometry(coords, 0.2);
|
||||||
|
FESA_CHECK(geometry.ok());
|
||||||
|
const auto data = fesa::mitc4BuildMaterialIntegrationData(geometry, 1000.0, 0.3);
|
||||||
|
FESA_CHECK(data.ok());
|
||||||
|
FESA_CHECK(data.samples.size() == 8);
|
||||||
|
for (const auto& sample : data.samples) {
|
||||||
|
FESA_CHECK(sample.ok());
|
||||||
|
FESA_CHECK_NEAR(sample.point.weight, 1.0, 1.0e-15);
|
||||||
|
FESA_CHECK(sample.basis.ok());
|
||||||
|
FESA_CHECK(sample.strain_rows.ok());
|
||||||
|
for (std::size_t i = 0; i < 6; ++i) {
|
||||||
|
for (std::size_t j = 0; j < 6; ++j) {
|
||||||
|
FESA_CHECK_NEAR(sample.convected_material[i][j], sample.convected_material[j][i], 1.0e-10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
FESA_TEST(mitc4_shape_functions_and_stiffness_baseline) {
|
FESA_TEST(mitc4_shape_functions_and_stiffness_baseline) {
|
||||||
auto shape = fesa::shapeFunctions(0.25, -0.5);
|
auto shape = fesa::shapeFunctions(0.25, -0.5);
|
||||||
const fesa::Real sum = shape.n[0] + shape.n[1] + shape.n[2] + shape.n[3];
|
const fesa::Real sum = shape.n[0] + shape.n[1] + shape.n[2] + shape.n[3];
|
||||||
|
|||||||
Reference in New Issue
Block a user