remove all files
This commit is contained in:
@@ -1,129 +0,0 @@
|
||||
#include "fesa/Analysis/Analysis.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
|
||||
void check(bool value, const char* message) {
|
||||
if (!value) {
|
||||
throw std::runtime_error(message);
|
||||
}
|
||||
}
|
||||
|
||||
void checkNear(fesa::Real actual, fesa::Real expected, fesa::Real tolerance, const char* message) {
|
||||
if (std::fabs(actual - expected) > tolerance) {
|
||||
throw std::runtime_error(message);
|
||||
}
|
||||
}
|
||||
|
||||
std::string phase1Input() {
|
||||
return R"inp(
|
||||
*Node
|
||||
1, 0, 0, 0
|
||||
2, 1, 0, 0
|
||||
3, 1, 1, 0
|
||||
4, 0, 1, 0
|
||||
*Element, type=S4, elset=EALL
|
||||
1, 1, 2, 3, 4
|
||||
*Nset, nset=LEFT
|
||||
1, 4
|
||||
*Nset, nset=RIGHT
|
||||
2, 3
|
||||
*Elset, elset=EALL
|
||||
1
|
||||
*Material, name=STEEL
|
||||
*Elastic
|
||||
1000.0, 0.3
|
||||
*Shell Section, elset=EALL, material=STEEL
|
||||
0.1
|
||||
*Boundary
|
||||
LEFT, 1, 6, 0
|
||||
RIGHT, 1, 2, 0
|
||||
RIGHT, 4, 6, 0
|
||||
*Cload
|
||||
2, 3, -1
|
||||
3, 3, -1
|
||||
*Step, name=Step-1
|
||||
*Static
|
||||
*End Step
|
||||
)inp";
|
||||
}
|
||||
|
||||
class RecordingSolver final : public fesa::LinearSolver {
|
||||
public:
|
||||
explicit RecordingSolver(std::vector<fesa::Real> solution) : solution_(std::move(solution)) {}
|
||||
|
||||
fesa::SolveResult solve(fesa::DenseMatrix a, std::vector<fesa::Real> b) const override {
|
||||
called = true;
|
||||
captured_a = std::move(a);
|
||||
captured_b = std::move(b);
|
||||
return {solution_, {}};
|
||||
}
|
||||
|
||||
mutable bool called = false;
|
||||
mutable fesa::DenseMatrix captured_a;
|
||||
mutable std::vector<fesa::Real> captured_b;
|
||||
|
||||
private:
|
||||
std::vector<fesa::Real> solution_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
int main() {
|
||||
const auto workflow = fesa::runLinearStaticInputString(phase1Input(), "analysis-module.inp");
|
||||
check(workflow.ok(), "linear static input workflow should remain valid");
|
||||
check(workflow.model.ok(), "analysis model should remain valid");
|
||||
check(workflow.model.step.name == "Step-1", "analysis step name changed");
|
||||
check(workflow.state.converged, "analysis convergence flag changed");
|
||||
check(workflow.state.u_full.size() == 24, "full displacement size changed");
|
||||
check(workflow.state.f_external_full.size() == 24, "full external-force size changed");
|
||||
check(workflow.state.f_internal_full.size() == 24, "full internal-force size changed");
|
||||
check(workflow.state.reaction_full.size() == 24, "full reaction size changed");
|
||||
check(workflow.result_file.steps.size() == 1, "result step count changed");
|
||||
const auto& frame = workflow.result_file.steps.front().frames.front();
|
||||
check(frame.field_outputs.count("U") == 1, "U field output missing");
|
||||
check(frame.field_outputs.count("RF") == 1, "RF field output missing");
|
||||
check(frame.field_outputs.at("U").component_labels == fesa::displacementComponentLabels(), "U labels changed");
|
||||
check(frame.field_outputs.at("RF").component_labels == fesa::reactionComponentLabels(), "RF labels changed");
|
||||
|
||||
fesa::Real total_rf_z = 0.0;
|
||||
for (const auto& values : frame.field_outputs.at("RF").values) {
|
||||
total_rf_z += values[2];
|
||||
}
|
||||
checkNear(total_rf_z, 2.0, 1.0e-8, "full-vector RF balance changed");
|
||||
|
||||
fesa::AbaqusInputParser parser;
|
||||
const auto parsed = parser.parseString(phase1Input());
|
||||
check(parsed.ok(), "phase1 analysis input parse changed");
|
||||
RecordingSolver solver({0.25, -0.50});
|
||||
fesa::LinearStaticAnalysis analysis(&solver);
|
||||
const auto injected = analysis.run(parsed.domain);
|
||||
check(injected.ok(), "solver-injected analysis should remain valid");
|
||||
check(solver.called, "linear solver adapter injection changed");
|
||||
check(solver.captured_a.rows() == 2 && solver.captured_a.cols() == 2, "captured reduced stiffness size changed");
|
||||
check(solver.captured_b.size() == 2, "captured reduced load size changed");
|
||||
|
||||
const fesa::DofManager dofs(parsed.domain);
|
||||
checkNear(injected.state.u_full[static_cast<std::size_t>(dofs.fullIndex(2, fesa::Dof::UZ))], 0.25, 1.0e-15,
|
||||
"node 2 reconstructed UZ changed");
|
||||
checkNear(injected.state.u_full[static_cast<std::size_t>(dofs.fullIndex(3, fesa::Dof::UZ))], -0.50, 1.0e-15,
|
||||
"node 3 reconstructed UZ changed");
|
||||
for (std::size_t i = 0; i < injected.state.reaction_full.size(); ++i) {
|
||||
checkNear(injected.state.reaction_full[i],
|
||||
injected.state.f_internal_full[i] - injected.state.f_external_full[i],
|
||||
1.0e-10,
|
||||
"full-vector reaction formula changed");
|
||||
}
|
||||
|
||||
const auto parse_error = fesa::runLinearStaticInputString("*Part, name=P\n", "unsupported.inp");
|
||||
check(!parse_error.ok(), "parse errors should still be routed through analysis result");
|
||||
check(fesa::containsDiagnostic(parse_error.diagnostics, "FESA-PARSE-UNSUPPORTED-KEYWORD"),
|
||||
"parse diagnostic routing changed");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
#include "fesa/Assembly/Assembly.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
|
||||
void check(bool value, const char* message) {
|
||||
if (!value) {
|
||||
throw std::runtime_error(message);
|
||||
}
|
||||
}
|
||||
|
||||
void checkNear(fesa::Real actual, fesa::Real expected, fesa::Real tolerance, const char* message) {
|
||||
if (std::fabs(actual - expected) > tolerance) {
|
||||
throw std::runtime_error(message);
|
||||
}
|
||||
}
|
||||
|
||||
fesa::Domain assemblyDomain() {
|
||||
fesa::Domain domain;
|
||||
domain.nodes[1] = {1, {0.0, 0.0, 0.0}};
|
||||
domain.nodes[2] = {2, {1.0, 0.0, 0.0}};
|
||||
domain.nodes[3] = {3, {1.0, 1.0, 0.0}};
|
||||
domain.nodes[4] = {4, {0.0, 1.0, 0.0}};
|
||||
domain.elements[1] = {1, fesa::ElementType::MITC4, {1, 2, 3, 4}, "EALL"};
|
||||
domain.element_sets["eall"] = {"EALL", {1}};
|
||||
domain.node_sets["left"] = {"LEFT", {1, 4}};
|
||||
domain.node_sets["right"] = {"RIGHT", {2, 3}};
|
||||
domain.materials["steel"] = {"STEEL", 1000.0, 0.30};
|
||||
domain.shell_sections.push_back({"EALL", "STEEL", 0.1});
|
||||
domain.boundary_conditions.push_back({"LEFT", 1, 6, 0.0});
|
||||
domain.boundary_conditions.push_back({"RIGHT", 1, 2, 0.0});
|
||||
domain.boundary_conditions.push_back({"RIGHT", 4, 6, 0.0});
|
||||
domain.loads.push_back({"2", 3, -1.0});
|
||||
domain.loads.push_back({"3", 3, -1.0});
|
||||
return domain;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main() {
|
||||
const auto domain = assemblyDomain();
|
||||
const fesa::DofManager dofs(domain);
|
||||
|
||||
const auto pattern = fesa::buildReducedSparsePattern(domain, dofs);
|
||||
check(pattern.equation_count == dofs.freeDofCount(), "reduced sparse pattern equation count changed");
|
||||
check(pattern.nonzeroCount() > 0, "reduced sparse pattern should remain non-empty");
|
||||
|
||||
const auto assembly = fesa::assembleSystem(domain, dofs);
|
||||
check(assembly.ok(), "assembly should remain valid");
|
||||
check(assembly.k_full.rows() == dofs.fullDofCount(), "full stiffness row count changed");
|
||||
check(assembly.k_full.cols() == dofs.fullDofCount(), "full stiffness column count changed");
|
||||
check(static_cast<fesa::LocalIndex>(assembly.f_full.size()) == dofs.fullDofCount(), "full load size changed");
|
||||
checkNear(assembly.f_full[static_cast<std::size_t>(dofs.fullIndex(2, fesa::Dof::UZ))], -1.0, 1.0e-15,
|
||||
"node 2 UZ load changed");
|
||||
checkNear(assembly.f_full[static_cast<std::size_t>(dofs.fullIndex(3, fesa::Dof::UZ))], -1.0, 1.0e-15,
|
||||
"node 3 UZ load changed");
|
||||
|
||||
const auto reduced = fesa::projectToReducedSystem(assembly, dofs);
|
||||
check(reduced.ok(), "reduced system projection should remain valid");
|
||||
check(reduced.k.rows() == dofs.freeDofCount(), "reduced stiffness row count changed");
|
||||
check(reduced.k.cols() == dofs.freeDofCount(), "reduced stiffness column count changed");
|
||||
check(static_cast<fesa::LocalIndex>(reduced.f.size()) == dofs.freeDofCount(), "reduced load size changed");
|
||||
check(reduced.free_full_indices == dofs.freeFullIndices(), "free full-index map changed");
|
||||
checkNear(reduced.f[0], -1.0, 1.0e-15, "first reduced load changed");
|
||||
checkNear(reduced.f[1], -1.0, 1.0e-15, "second reduced load changed");
|
||||
|
||||
fesa::DenseMatrix k(3, 3);
|
||||
k(0, 0) = 4.0;
|
||||
k(1, 1) = 5.0;
|
||||
k(2, 2) = 6.0;
|
||||
const std::vector<fesa::Real> u = {0.5, -0.25, 2.0};
|
||||
const std::vector<fesa::Real> f = {1.0, 2.0, -3.0};
|
||||
const auto rf = fesa::recoverFullReaction(k, u, f);
|
||||
check(rf.size() == 3, "full reaction size changed");
|
||||
checkNear(rf[0], 1.0, 1.0e-15, "RF component 0 changed");
|
||||
checkNear(rf[1], -3.25, 1.0e-15, "RF component 1 changed");
|
||||
checkNear(rf[2], 15.0, 1.0e-15, "RF component 2 changed");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
#include "fesa/Boundary/Boundary.hpp"
|
||||
#include "fesa/Core/Core.hpp"
|
||||
#include "fesa/Load/Load.hpp"
|
||||
#include "fesa/Property/Property.hpp"
|
||||
#include "fesa/Util/Util.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
namespace {
|
||||
|
||||
void check(bool value, const char* message) {
|
||||
if (!value) {
|
||||
throw std::runtime_error(message);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main() {
|
||||
static_assert(std::is_same_v<fesa::Real, double>, "Real must remain double");
|
||||
static_assert(std::is_same_v<fesa::GlobalId, std::int64_t>, "GlobalId must remain int64");
|
||||
static_assert(std::is_same_v<fesa::LocalIndex, std::int64_t>, "LocalIndex must remain int64");
|
||||
static_assert(std::is_same_v<fesa::EquationId, std::int64_t>, "EquationId must remain int64");
|
||||
static_assert(std::is_same_v<fesa::SparseIndex, std::int64_t>, "SparseIndex must remain int64");
|
||||
|
||||
fesa::Domain domain;
|
||||
domain.nodes[10] = {10, {0.0, 0.0, 0.0}};
|
||||
domain.nodes[20] = {20, {1.0, 0.0, 0.0}};
|
||||
domain.nodes[30] = {30, {1.0, 1.0, 0.0}};
|
||||
domain.nodes[40] = {40, {0.0, 1.0, 0.0}};
|
||||
domain.elements[100] = {100, fesa::ElementType::MITC4, {10, 20, 30, 40}, "EALL"};
|
||||
domain.element_sets["eall"] = {"EALL", {100}};
|
||||
domain.node_sets["fixed"] = {"FIXED", {10, 40}};
|
||||
domain.materials["mat"] = {"MAT", 1000.0, 0.3};
|
||||
domain.shell_sections.push_back(fesa::ShellSection{"EALL", "MAT", 0.1});
|
||||
domain.boundary_conditions.push_back(fesa::BoundaryCondition{"FIXED", 1, 6, 0.0});
|
||||
domain.loads.push_back(fesa::NodalLoad{"20", 3, -1.0});
|
||||
domain.steps.push_back({"Step-1", "linear_static"});
|
||||
|
||||
check(fesa::Domain::key(" EALL ") == "eall", "Domain key normalization changed");
|
||||
check(fesa::dofFromAbaqus(6).value() == fesa::Dof::RZ, "Abaqus DOF mapping changed");
|
||||
check(std::string(fesa::dofLabel(fesa::Dof::UX)) == "UX", "DOF label changed");
|
||||
|
||||
const auto diagnostics = fesa::validateDomain(domain);
|
||||
check(!fesa::hasError(diagnostics), "Direct module validation reported an error");
|
||||
|
||||
const fesa::DofManager dofs(domain);
|
||||
check(dofs.fullDofCount() == 24, "DofManager full DOF count changed");
|
||||
check(dofs.constrainedDofCount() == 12, "DofManager constrained DOF count changed");
|
||||
check(dofs.freeDofCount() == 12, "DofManager free DOF count changed");
|
||||
check(dofs.equation(20, fesa::Dof::UZ) >= 0, "Free equation numbering changed");
|
||||
check(dofs.equation(10, fesa::Dof::UX) == -1, "Constrained equation numbering changed");
|
||||
|
||||
const auto diagnostic = fesa::makeDiagnostic(fesa::Severity::Warning, "FESA-SMOKE", "module smoke", "core");
|
||||
check(diagnostic.source.keyword == "core", "Diagnostic source keyword changed");
|
||||
|
||||
const auto model = fesa::buildLinearStaticAnalysisModel(domain);
|
||||
check(model.ok(), "AnalysisModel direct module construction failed");
|
||||
check(model.active_element_ids.size() == 1, "AnalysisModel active elements changed");
|
||||
|
||||
fesa::AnalysisState state;
|
||||
state.u_full = dofs.reconstructFullVector(std::vector<fesa::Real>(static_cast<std::size_t>(dofs.freeDofCount()), 0.0));
|
||||
check(state.u_full.size() == static_cast<std::size_t>(dofs.fullDofCount()), "AnalysisState full vector ownership changed");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,173 +0,0 @@
|
||||
#include "fesa/Element/Element.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace {
|
||||
|
||||
void check(bool value, const char* message) {
|
||||
if (!value) {
|
||||
throw std::runtime_error(message);
|
||||
}
|
||||
}
|
||||
|
||||
void checkNear(fesa::Real actual, fesa::Real expected, fesa::Real tolerance, const char* message) {
|
||||
if (std::fabs(actual - expected) > tolerance) {
|
||||
throw std::runtime_error(message);
|
||||
}
|
||||
}
|
||||
|
||||
void checkVecNear(const fesa::Vec3& actual, const fesa::Vec3& expected, fesa::Real tolerance, const char* message) {
|
||||
checkNear(actual.x, expected.x, tolerance, message);
|
||||
checkNear(actual.y, expected.y, tolerance, message);
|
||||
checkNear(actual.z, expected.z, tolerance, message);
|
||||
}
|
||||
|
||||
void checkRightHandedOrthonormal(const fesa::Vec3& e1, const fesa::Vec3& e2, const fesa::Vec3& e3) {
|
||||
checkNear(fesa::norm(e1), 1.0, 1.0e-14, "e1 norm changed");
|
||||
checkNear(fesa::norm(e2), 1.0, 1.0e-14, "e2 norm changed");
|
||||
checkNear(fesa::norm(e3), 1.0, 1.0e-14, "e3 norm changed");
|
||||
checkNear(fesa::dot(e1, e2), 0.0, 1.0e-14, "e1/e2 orthogonality changed");
|
||||
checkNear(fesa::dot(e1, e3), 0.0, 1.0e-14, "e1/e3 orthogonality changed");
|
||||
checkNear(fesa::dot(e2, e3), 0.0, 1.0e-14, "e2/e3 orthogonality changed");
|
||||
checkNear(fesa::dot(fesa::cross(e1, e2), e3), 1.0, 1.0e-14, "basis handedness changed");
|
||||
}
|
||||
|
||||
fesa::MITC4ElementDofVector zeroElementDofs() {
|
||||
fesa::MITC4ElementDofVector values{};
|
||||
values.fill(0.0);
|
||||
return values;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main() {
|
||||
const auto center = fesa::shapeFunctions(0.0, 0.0);
|
||||
checkNear(center.n[0] + center.n[1] + center.n[2] + center.n[3], 1.0, 1.0e-15, "shape sum changed");
|
||||
checkNear(center.dr[0], -0.25, 1.0e-15, "center dN1/dxi changed");
|
||||
checkNear(center.ds[2], 0.25, 1.0e-15, "center dN3/deta changed");
|
||||
|
||||
const auto nodes = fesa::mitc4NodeNaturalCoordinates();
|
||||
checkNear(nodes[0].xi, -1.0, 1.0e-15, "node 1 xi changed");
|
||||
checkNear(nodes[0].eta, -1.0, 1.0e-15, "node 1 eta changed");
|
||||
checkNear(nodes[2].xi, 1.0, 1.0e-15, "node 3 xi changed");
|
||||
checkNear(nodes[2].eta, 1.0, 1.0e-15, "node 3 eta changed");
|
||||
for (std::size_t i = 0; i < 4; ++i) {
|
||||
const auto corner = fesa::shapeFunctions(nodes[i].xi, nodes[i].eta);
|
||||
checkNear(corner.n[i], 1.0, 1.0e-15, "corner interpolation changed");
|
||||
}
|
||||
|
||||
const auto tying = fesa::mitc4TyingPoints();
|
||||
check(tying[0].label == "A" && tying[0].natural.xi == 0.0 && tying[0].natural.eta == -1.0,
|
||||
"tying point A changed");
|
||||
check((tying[0].edge_node_indices == std::array<fesa::LocalIndex, 2>{0, 1}), "tying edge A changed");
|
||||
check(tying[1].label == "B" && tying[1].natural.xi == -1.0 && tying[1].natural.eta == 0.0,
|
||||
"tying point B changed");
|
||||
check((tying[1].edge_node_indices == std::array<fesa::LocalIndex, 2>{0, 3}), "tying edge B changed");
|
||||
check(tying[2].label == "C" && tying[2].natural.xi == 0.0 && tying[2].natural.eta == 1.0,
|
||||
"tying point C changed");
|
||||
check((tying[2].edge_node_indices == std::array<fesa::LocalIndex, 2>{3, 2}), "tying edge C changed");
|
||||
check(tying[3].label == "D" && tying[3].natural.xi == 1.0 && tying[3].natural.eta == 0.0,
|
||||
"tying point D changed");
|
||||
check((tying[3].edge_node_indices == std::array<fesa::LocalIndex, 2>{1, 2}), "tying edge D changed");
|
||||
|
||||
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);
|
||||
check(geometry.ok(), "flat geometry should remain valid");
|
||||
checkVecNear(geometry.center_normal, {0, 0, 1}, 1.0e-14, "center normal changed");
|
||||
for (const auto& frame : geometry.nodal_frames) {
|
||||
checkVecNear(frame.v1, {1, 0, 0}, 1.0e-14, "director v1 changed");
|
||||
checkVecNear(frame.v2, {0, 1, 0}, 1.0e-14, "director v2 changed");
|
||||
checkVecNear(frame.vn, {0, 0, 1}, 1.0e-14, "director normal changed");
|
||||
checkRightHandedOrthonormal(frame.v1, frame.v2, frame.vn);
|
||||
}
|
||||
|
||||
const auto basis = fesa::computeMITC4IntegrationBasis(geometry, 0.0, 0.0, 0.0);
|
||||
check(basis.ok(), "flat integration basis should remain valid");
|
||||
checkVecNear(basis.g1, {0.5, 0, 0}, 1.0e-14, "g1 changed");
|
||||
checkVecNear(basis.g2, {0, 0.5, 0}, 1.0e-14, "g2 changed");
|
||||
checkVecNear(basis.g3, {0, 0, 0.1}, 1.0e-14, "g3 changed");
|
||||
checkRightHandedOrthonormal(basis.local.e1, basis.local.e2, basis.local.e3);
|
||||
checkNear(basis.jacobian, 0.025, 1.0e-14, "Jacobian changed");
|
||||
|
||||
const auto invalid_thickness = fesa::buildMITC4Geometry(coords, 0.0);
|
||||
check(!invalid_thickness.ok(), "invalid thickness should remain diagnosed");
|
||||
check(fesa::containsDiagnostic(invalid_thickness.diagnostics, "FESA-MITC4-THICKNESS"), "thickness diagnostic changed");
|
||||
const std::array<fesa::Vec3, 4> collinear = {{{0, 0, 0}, {1, 0, 0}, {2, 0, 0}, {3, 0, 0}}};
|
||||
const auto singular = fesa::buildMITC4Geometry(collinear, 0.1);
|
||||
check(!singular.ok(), "singular normal should remain diagnosed");
|
||||
check(fesa::containsDiagnostic(singular.diagnostics, "FESA-MITC4-SINGULAR-NORMAL"), "normal diagnostic changed");
|
||||
|
||||
const auto rotations = fesa::mitc4LocalRotations(geometry.nodal_frames[0], {1.0, 2.0, 3.0});
|
||||
checkNear(rotations.alpha, 1.0, 1.0e-14, "alpha rotation changed");
|
||||
checkNear(rotations.beta, 2.0, 1.0e-14, "beta rotation changed");
|
||||
checkNear(rotations.gamma, 3.0, 1.0e-14, "gamma rotation changed");
|
||||
checkVecNear(fesa::mitc4DirectorIncrement(geometry.nodal_frames[0], {0.0, 2.0, 5.0}), {2, 0, 0}, 1.0e-14,
|
||||
"director increment changed");
|
||||
|
||||
auto dofs = zeroElementDofs();
|
||||
for (std::size_t node = 0; node < 4; ++node) {
|
||||
dofs[6 * node + 0] = 1.0;
|
||||
dofs[6 * node + 1] = 0.5;
|
||||
dofs[6 * node + 4] = 2.0;
|
||||
dofs[6 * node + 5] = 99.0;
|
||||
}
|
||||
const auto mid = fesa::mitc4DisplacementDerivatives(geometry, dofs, 0.0, 0.0, 0.0);
|
||||
check(mid.ok(), "midsurface displacement interpolation changed");
|
||||
checkVecNear(mid.displacement, {1.0, 0.5, 0.0}, 1.0e-14, "midsurface displacement value changed");
|
||||
const auto top = fesa::mitc4DisplacementDerivatives(geometry, dofs, 0.0, 0.0, 1.0);
|
||||
check(top.ok(), "top displacement interpolation changed");
|
||||
checkVecNear(top.displacement, {1.2, 0.5, 0.0}, 1.0e-14, "top displacement value changed");
|
||||
|
||||
constexpr fesa::Real xi = 0.2;
|
||||
constexpr fesa::Real eta = -0.3;
|
||||
constexpr fesa::Real zeta = 0.4;
|
||||
const auto rows = fesa::mitc4DirectCovariantStrainRows(geometry, xi, eta, zeta);
|
||||
check(rows.ok(), "direct covariant strain rows should remain valid");
|
||||
check(fesa::mitc4StrainComponentLabels()[0] == "eps11", "strain label eps11 changed");
|
||||
check(fesa::strainComponentIndex(fesa::MITC4StrainComponent::Gamma23) == 3, "gamma23 index changed");
|
||||
check(fesa::strainComponentIndex(fesa::MITC4StrainComponent::Gamma13) == 4, "gamma13 index changed");
|
||||
|
||||
const fesa::Real h = 1.0e-6;
|
||||
const std::array<std::size_t, 4> checked_dofs = {0, 7, 10, 11};
|
||||
for (std::size_t dof : checked_dofs) {
|
||||
auto plus = zeroElementDofs();
|
||||
auto minus = zeroElementDofs();
|
||||
plus[dof] = h;
|
||||
minus[dof] = -h;
|
||||
const auto plus_strain = fesa::mitc4DirectCovariantStrain(geometry, plus, xi, eta, zeta);
|
||||
const auto minus_strain = fesa::mitc4DirectCovariantStrain(geometry, minus, xi, eta, zeta);
|
||||
check(plus_strain.ok() && minus_strain.ok(), "direct strain perturbation changed");
|
||||
for (std::size_t component = 0; component < 6; ++component) {
|
||||
const fesa::Real finite_difference = (plus_strain.values[component] - minus_strain.values[component]) / (2.0 * h);
|
||||
checkNear(rows.rows[component][dof], finite_difference, 1.0e-8, "direct strain row finite difference changed");
|
||||
}
|
||||
}
|
||||
|
||||
const auto gamma23 = fesa::strainComponentIndex(fesa::MITC4StrainComponent::Gamma23);
|
||||
const auto gamma13 = fesa::strainComponentIndex(fesa::MITC4StrainComponent::Gamma13);
|
||||
const auto direct_at_point = fesa::mitc4DirectCovariantStrainRows(geometry, 0.5, 0.25, 0.0);
|
||||
const auto direct_a = fesa::mitc4DirectCovariantStrainRows(geometry, 0.0, -1.0, 0.0);
|
||||
const auto direct_b = fesa::mitc4DirectCovariantStrainRows(geometry, -1.0, 0.0, 0.0);
|
||||
const auto direct_c = fesa::mitc4DirectCovariantStrainRows(geometry, 0.0, 1.0, 0.0);
|
||||
const auto direct_d = fesa::mitc4DirectCovariantStrainRows(geometry, 1.0, 0.0, 0.0);
|
||||
const auto tied = fesa::mitc4TiedCovariantStrainRows(geometry, 0.5, 0.25, 0.0);
|
||||
check(direct_at_point.ok() && tied.ok(), "MITC tied rows should remain valid");
|
||||
|
||||
const std::size_t node2_ry = 6 + 4;
|
||||
const fesa::Real expected_gamma13 =
|
||||
0.5 * (1.0 - 0.25) * direct_a.rows[gamma13][node2_ry] + 0.5 * (1.0 + 0.25) * direct_c.rows[gamma13][node2_ry];
|
||||
checkNear(tied.rows[gamma13][node2_ry], expected_gamma13, 1.0e-14, "MITC gamma13 tying interpolation changed");
|
||||
check(std::fabs(tied.rows[gamma13][node2_ry] - direct_at_point.rows[gamma13][node2_ry]) > 1.0e-4,
|
||||
"MITC gamma13 should not be direct Gauss shear");
|
||||
|
||||
const std::size_t node4_rx = 3 * 6 + 3;
|
||||
const fesa::Real expected_gamma23 =
|
||||
0.5 * (1.0 - 0.5) * direct_b.rows[gamma23][node4_rx] + 0.5 * (1.0 + 0.5) * direct_d.rows[gamma23][node4_rx];
|
||||
checkNear(tied.rows[gamma23][node4_rx], expected_gamma23, 1.0e-14, "MITC gamma23 tying interpolation changed");
|
||||
check(std::fabs(tied.rows[gamma23][node4_rx] - direct_at_point.rows[gamma23][node4_rx]) > 1.0e-4,
|
||||
"MITC gamma23 should not be direct Gauss shear");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
#include "fesa/IO/IO.hpp"
|
||||
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
namespace {
|
||||
|
||||
void check(bool value, const char* message) {
|
||||
if (!value) {
|
||||
throw std::runtime_error(message);
|
||||
}
|
||||
}
|
||||
|
||||
std::string sourceRoot() {
|
||||
#ifdef FESA_SOURCE_DIR
|
||||
return FESA_SOURCE_DIR;
|
||||
#else
|
||||
return ".";
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string readTextFile(const std::string& path) {
|
||||
std::ifstream input(path);
|
||||
if (!input) {
|
||||
throw std::runtime_error("failed to open " + path);
|
||||
}
|
||||
std::ostringstream buffer;
|
||||
buffer << input.rdbuf();
|
||||
return buffer.str();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main() {
|
||||
const auto continued = fesa::parseKeywordLine("*Element, type=S4,");
|
||||
check(continued.name == "element", "Keyword name normalization changed");
|
||||
check(continued.parameters.at("type") == "S4", "Keyword parameter parsing changed");
|
||||
|
||||
const fesa::AbaqusInputParser parser;
|
||||
|
||||
const auto normalized = parser.parseFile(sourceRoot() + "/references/quad_02_phase1.inp");
|
||||
check(normalized.ok(), "quad_02_phase1 normalized input should remain accepted");
|
||||
check(normalized.domain.nodes.size() == 121, "quad_02_phase1 node count changed");
|
||||
check(normalized.domain.elements.size() == 100, "quad_02_phase1 element count changed");
|
||||
check(normalized.domain.node_sets.at("fixed_boundary").node_ids.size() == 40, "quad_02_phase1 fixed set changed");
|
||||
check(normalized.domain.node_sets.at("load_node").node_ids.size() == 1, "quad_02_phase1 load set changed");
|
||||
check(normalized.domain.element_sets.at("all_elements").element_ids.size() == 100, "quad_02_phase1 element set changed");
|
||||
check(normalized.domain.materials.at("material_1").elastic_modulus == 7.0e10, "quad_02_phase1 material changed");
|
||||
check(normalized.domain.shell_sections.front().thickness == 1.0, "quad_02_phase1 shell section changed");
|
||||
|
||||
const auto original = parser.parseFile(sourceRoot() + "/references/quad_02.inp");
|
||||
check(!original.ok(), "original quad_02.inp should remain unsupported provenance");
|
||||
check(fesa::containsDiagnostic(original.diagnostics, "FESA-PARSE-UNSUPPORTED-KEYWORD"),
|
||||
"original quad_02.inp unsupported keyword diagnostic changed");
|
||||
|
||||
const auto nonzero_bc = parser.parseString("*Node\n1, 0, 0, 0\n*Boundary\n1, 1, 1, 0.5\n", "nonzero_bc.inp");
|
||||
check(!nonzero_bc.ok(), "nonzero prescribed displacement should remain unsupported");
|
||||
check(fesa::containsDiagnostic(nonzero_bc.diagnostics, "FESA-PARSE-BOUNDARY-NONZERO"),
|
||||
"nonzero prescribed displacement diagnostic changed");
|
||||
|
||||
return 0;
|
||||
}
|
||||
-2068
File diff suppressed because it is too large
Load Diff
@@ -1,71 +0,0 @@
|
||||
#include "fesa/Math/Math.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
|
||||
void check(bool value, const char* message) {
|
||||
if (!value) {
|
||||
throw std::runtime_error(message);
|
||||
}
|
||||
}
|
||||
|
||||
void checkNear(fesa::Real actual, fesa::Real expected, fesa::Real tolerance, const char* message) {
|
||||
if (std::fabs(actual - expected) > tolerance) {
|
||||
throw std::runtime_error(message);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main() {
|
||||
static_assert(std::is_same_v<fesa::SparseIndex, std::int64_t>, "SparseIndex must remain int64");
|
||||
static_assert(std::is_same_v<fesa::EquationId, std::int64_t>, "EquationId must remain int64");
|
||||
|
||||
const fesa::Vec3 x{1.0, 0.0, 0.0};
|
||||
const fesa::Vec3 y{0.0, 1.0, 0.0};
|
||||
const auto z = fesa::cross(x, y);
|
||||
checkNear(fesa::dot(x, y), 0.0, 1.0e-12, "Vector dot product changed");
|
||||
checkNear(z.z, 1.0, 1.0e-12, "Vector cross product changed");
|
||||
checkNear(fesa::norm(fesa::Vec3{3.0, 4.0, 0.0}), 5.0, 1.0e-12, "Vector norm changed");
|
||||
check(fesa::normalizedIfValid(fesa::Vec3{0.0, 0.0, 0.0}) == std::nullopt, "Invalid normalization changed");
|
||||
|
||||
fesa::SparsePattern pattern;
|
||||
pattern.equation_count = 3;
|
||||
pattern.entries.push_back({0, 0});
|
||||
pattern.entries.push_back({0, 2});
|
||||
check(pattern.nonzeroCount() == 2, "Sparse nonzero count changed");
|
||||
check(pattern.contains(0, 2), "Sparse pattern lookup changed");
|
||||
check(!pattern.contains(2, 0), "Sparse pattern lookup became nondirectional");
|
||||
|
||||
fesa::DenseMatrix matrix(2, 2);
|
||||
matrix(0, 0) = 2.0;
|
||||
matrix(0, 1) = 1.0;
|
||||
matrix(1, 0) = 1.0;
|
||||
matrix(1, 1) = 3.0;
|
||||
const auto product = matrix.multiply({1.0, 2.0});
|
||||
checkNear(product[0], 4.0, 1.0e-12, "DenseMatrix multiply row 0 changed");
|
||||
checkNear(product[1], 7.0, 1.0e-12, "DenseMatrix multiply row 1 changed");
|
||||
|
||||
const fesa::LinearSolver& solver = fesa::GaussianEliminationSolver{};
|
||||
const auto solved = solver.solve(matrix, {1.0, 2.0});
|
||||
check(solved.ok(), "Gaussian solver unexpectedly failed");
|
||||
checkNear(solved.x[0], 0.2, 1.0e-12, "Gaussian solver x0 changed");
|
||||
checkNear(solved.x[1], 0.6, 1.0e-12, "Gaussian solver x1 changed");
|
||||
|
||||
fesa::DenseMatrix singular(2, 2);
|
||||
singular(0, 0) = 1.0;
|
||||
singular(0, 1) = 2.0;
|
||||
singular(1, 0) = 2.0;
|
||||
singular(1, 1) = 4.0;
|
||||
const auto failed = solver.solve(singular, {1.0, 2.0});
|
||||
check(!failed.ok(), "Singular solve unexpectedly passed");
|
||||
check(fesa::containsDiagnostic(failed.diagnostics, "FESA-SINGULAR-SOLVER"), "Singular diagnostic code changed");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
#include "fesa/Element/Element.hpp"
|
||||
#include "fesa/Element/MITC4Stiffness.hpp"
|
||||
#include "fesa/Material/MITC4PlaneStressMaterial.hpp"
|
||||
#include "fesa/Material/Material.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
|
||||
void check(bool value, const char* message) {
|
||||
if (!value) {
|
||||
throw std::runtime_error(message);
|
||||
}
|
||||
}
|
||||
|
||||
void checkNear(fesa::Real actual, fesa::Real expected, fesa::Real tolerance, const char* message) {
|
||||
if (std::fabs(actual - expected) > tolerance) {
|
||||
throw std::runtime_error(message);
|
||||
}
|
||||
}
|
||||
|
||||
fesa::Real matrixEnergy(const fesa::DenseMatrix& matrix, const std::vector<fesa::Real>& values) {
|
||||
const auto product = matrix.multiply(values);
|
||||
fesa::Real energy = 0.0;
|
||||
for (std::size_t i = 0; i < values.size(); ++i) {
|
||||
energy += values[i] * product[i];
|
||||
}
|
||||
return energy;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main() {
|
||||
const auto law = fesa::mitc4PlaneStressMaterialMatrix(1000.0, 0.25);
|
||||
check(law.ok(), "valid MITC4 plane-stress material should remain accepted");
|
||||
check(fesa::mitc4StrainComponentLabels()[0] == "eps11", "MITC4 strain labels changed");
|
||||
const auto eps11 = fesa::strainComponentIndex(fesa::MITC4StrainComponent::Eps11);
|
||||
const auto eps22 = fesa::strainComponentIndex(fesa::MITC4StrainComponent::Eps22);
|
||||
const auto gamma13 = fesa::strainComponentIndex(fesa::MITC4StrainComponent::Gamma13);
|
||||
checkNear(law.matrix[eps11][eps11], 1066.6666666666667, 1.0e-10, "plane-stress D11 changed");
|
||||
checkNear(law.matrix[eps11][eps22], 266.6666666666667, 1.0e-10, "plane-stress D12 changed");
|
||||
checkNear(law.matrix[gamma13][gamma13], (5.0 / 6.0) * 400.0, 1.0e-10,
|
||||
"plane-stress shear correction changed");
|
||||
|
||||
fesa::MITC4StrainVector strain{};
|
||||
strain[eps11] = 0.01;
|
||||
strain[eps22] = -0.02;
|
||||
strain[gamma13] = 0.03;
|
||||
const auto stress = fesa::multiplyMITC4MaterialMatrix(law.matrix, strain);
|
||||
check(fesa::dotMITC4Vector(strain, stress) > 0.0, "MITC4 material energy changed");
|
||||
|
||||
const auto invalid = fesa::mitc4PlaneStressMaterialMatrix(-1.0, 0.25);
|
||||
check(!invalid.ok(), "invalid MITC4 material should remain diagnosed");
|
||||
check(fesa::containsDiagnostic(invalid.diagnostics, "FESA-MITC4-MATERIAL"),
|
||||
"MITC4 invalid material diagnostic changed");
|
||||
|
||||
const auto points = fesa::mitc4GaussQuadrature2x2x2();
|
||||
check(points.size() == 8, "MITC4 integration point count changed");
|
||||
for (const auto& point : points) {
|
||||
checkNear(point.weight, 1.0, 1.0e-15, "MITC4 integration weight changed");
|
||||
}
|
||||
|
||||
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);
|
||||
check(geometry.ok(), "flat MITC4 geometry should remain valid");
|
||||
const auto integration = fesa::mitc4BuildMaterialIntegrationData(geometry, 1000.0, 0.25);
|
||||
check(integration.ok(), "MITC4 material integration data should remain valid");
|
||||
check(integration.samples.size() == 8, "MITC4 material integration sample count changed");
|
||||
for (const auto& sample : integration.samples) {
|
||||
check(sample.ok(), "MITC4 material integration sample should remain valid");
|
||||
check(sample.basis.ok(), "MITC4 material integration basis should remain valid");
|
||||
}
|
||||
|
||||
const auto stiffness = fesa::mitc4ElementStiffness(coords, 1000.0, 0.25, 0.2);
|
||||
check(stiffness.ok(), "MITC4 stiffness should remain valid");
|
||||
check(stiffness.global.rows() == 24 && stiffness.global.cols() == 24, "MITC4 global stiffness size changed");
|
||||
check(stiffness.integration_point_count == 8, "MITC4 stiffness integration count changed");
|
||||
checkNear(stiffness.drilling_stiffness_scale, 1.0e-3, 1.0e-15, "MITC4 drilling scale changed");
|
||||
check(stiffness.drilling_reference_diagonal > 0.0, "MITC4 drilling reference diagonal changed");
|
||||
check(stiffness.drilling_stiffness > 0.0, "MITC4 drilling stiffness changed");
|
||||
for (fesa::LocalIndex i = 0; i < 24; ++i) {
|
||||
for (fesa::LocalIndex j = 0; j < 24; ++j) {
|
||||
checkNear(stiffness.global(i, j), stiffness.global(j, i), 1.0e-8, "MITC4 stiffness symmetry changed");
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<fesa::Real> displacement(24, 0.0);
|
||||
displacement[0] = 0.01;
|
||||
displacement[7] = -0.02;
|
||||
displacement[14] = 0.03;
|
||||
displacement[21] = 0.04;
|
||||
const auto internal_force = fesa::mitc4ElementInternalForce(stiffness, displacement);
|
||||
check(internal_force.size() == displacement.size(), "MITC4 internal force size changed");
|
||||
const auto expected_internal_force = stiffness.global.multiply(displacement);
|
||||
for (std::size_t i = 0; i < internal_force.size(); ++i) {
|
||||
checkNear(internal_force[i], expected_internal_force[i], 1.0e-12, "MITC4 internal force changed");
|
||||
}
|
||||
check(matrixEnergy(stiffness.global, displacement) > 0.0, "MITC4 stiffness energy changed");
|
||||
|
||||
fesa::ElementStiffnessOptions options;
|
||||
options.drilling_stiffness_scale = 2.0e-3;
|
||||
const auto scaled = fesa::mitc4ElementStiffness(coords, 1000.0, 0.25, 0.2, options);
|
||||
check(scaled.ok(), "scaled MITC4 stiffness should remain valid");
|
||||
checkNear(scaled.drilling_reference_diagonal, stiffness.drilling_reference_diagonal, 1.0e-10,
|
||||
"MITC4 drilling reference changed with scale");
|
||||
checkNear(scaled.drilling_stiffness, 2.0 * stiffness.drilling_stiffness, 1.0e-10,
|
||||
"MITC4 drilling scale response changed");
|
||||
|
||||
fesa::MITC4ElementKernel kernel;
|
||||
const auto kernel_stiffness = kernel.stiffness(coords, 1000.0, 0.25, 0.2);
|
||||
check(kernel_stiffness.rows() == 24 && kernel_stiffness.cols() == 24, "MITC4 kernel stiffness size changed");
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
#include "fesa/Results/Results.hpp"
|
||||
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
|
||||
void check(bool value, const char* message) {
|
||||
if (!value) {
|
||||
throw std::runtime_error(message);
|
||||
}
|
||||
}
|
||||
|
||||
std::string sourceRoot() {
|
||||
#ifdef FESA_SOURCE_DIR
|
||||
return FESA_SOURCE_DIR;
|
||||
#else
|
||||
return ".";
|
||||
#endif
|
||||
}
|
||||
|
||||
fesa::Domain makeDomain() {
|
||||
fesa::Domain domain;
|
||||
domain.nodes[1] = {1, {0.0, 0.0, 0.0}};
|
||||
domain.nodes[2] = {2, {1.0, 0.0, 0.0}};
|
||||
domain.nodes[3] = {3, {1.0, 1.0, 0.0}};
|
||||
domain.nodes[4] = {4, {0.0, 1.0, 0.0}};
|
||||
domain.elements[10] = {10, fesa::ElementType::MITC4, {1, 2, 3, 4}, "all_elements"};
|
||||
domain.steps.push_back({"Step-Results", "linear_static"});
|
||||
domain.boundary_conditions.push_back({"1", 1, 6, 0.0});
|
||||
return domain;
|
||||
}
|
||||
|
||||
std::string readTextFile(const std::string& path) {
|
||||
std::ifstream input(path);
|
||||
if (!input) {
|
||||
throw std::runtime_error("failed to open " + path);
|
||||
}
|
||||
std::ostringstream buffer;
|
||||
buffer << input.rdbuf();
|
||||
return buffer.str();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main() {
|
||||
const auto domain = makeDomain();
|
||||
const fesa::DofManager dofs(domain);
|
||||
std::vector<fesa::Real> u(static_cast<std::size_t>(dofs.fullDofCount()), 0.0);
|
||||
std::vector<fesa::Real> rf(static_cast<std::size_t>(dofs.fullDofCount()), 0.0);
|
||||
u[static_cast<std::size_t>(dofs.fullIndex(2, fesa::Dof::UZ))] = -0.25;
|
||||
rf[static_cast<std::size_t>(dofs.fullIndex(1, fesa::Dof::UZ))] = 3.0;
|
||||
|
||||
fesa::InMemoryResultsWriter writer;
|
||||
writer.writeLinearStatic(domain, dofs, u, rf);
|
||||
const auto& result = writer.result();
|
||||
check(result.schema_name == "FESA_RESULTS", "schema name changed");
|
||||
check(result.schema_version == 1, "schema version changed");
|
||||
check(result.solver_name == "FESA", "solver name changed");
|
||||
check(result.dof_convention == "UX,UY,UZ,RX,RY,RZ", "DOF convention changed");
|
||||
check(result.sign_convention == "Abaqus-compatible", "sign convention changed");
|
||||
check(result.precision == "double", "precision metadata changed");
|
||||
check(result.index_type == "int64", "index metadata changed");
|
||||
check(result.node_ids.size() == domain.nodes.size(), "node model mirror changed");
|
||||
check(result.element_types == std::vector<std::string>({"MITC4"}), "element type label changed");
|
||||
check(result.steps.size() == 1, "result step count changed");
|
||||
check(result.steps[0].name == "Step-Results", "step name changed");
|
||||
check(result.steps[0].frames.size() == 1, "result frame count changed");
|
||||
|
||||
const auto& frame = result.steps[0].frames[0];
|
||||
check(frame.frame_id == 0, "Phase 1 frame id changed");
|
||||
check(frame.increment == 1, "Phase 1 increment changed");
|
||||
check(frame.iteration == 0, "Phase 1 iteration changed");
|
||||
check(frame.converged, "Phase 1 convergence flag changed");
|
||||
check(frame.field_outputs.count("U") == 1, "U field missing");
|
||||
check(frame.field_outputs.count("RF") == 1, "RF field missing");
|
||||
const auto& u_field = frame.field_outputs.at("U");
|
||||
const auto& rf_field = frame.field_outputs.at("RF");
|
||||
check(u_field.position == "NODAL" && u_field.entity_type == "node" && u_field.basis == "GLOBAL",
|
||||
"U field metadata changed");
|
||||
check(rf_field.position == "NODAL" && rf_field.entity_type == "node" && rf_field.basis == "GLOBAL",
|
||||
"RF field metadata changed");
|
||||
check(u_field.component_labels == fesa::displacementComponentLabels(), "U component labels changed");
|
||||
check(rf_field.component_labels == fesa::reactionComponentLabels(), "RF component labels changed");
|
||||
|
||||
const auto required_columns = fesa::displacementCsvRequiredColumns();
|
||||
check(required_columns.size() == 7, "required displacement CSV column count changed");
|
||||
check(required_columns.front() == "Node Label", "CSV node label column changed");
|
||||
check(required_columns.back() == "UR-UR3", "CSV rotation column changed");
|
||||
|
||||
const auto required_reaction_columns = fesa::reactionCsvRequiredColumns();
|
||||
check(required_reaction_columns.size() == 7, "required reaction CSV column count changed");
|
||||
check(required_reaction_columns.front() == "Node Label", "reaction CSV node label column changed");
|
||||
check(required_reaction_columns.back() == "RM-RM3", "reaction CSV moment column changed");
|
||||
|
||||
const auto missing_header = fesa::loadDisplacementCsvFromString("Node Label,U-U1,U-U2,U-U3,UR-UR1,UR-UR2\n"
|
||||
"1,0,0,0,0,0\n",
|
||||
"missing-header.csv");
|
||||
check(fesa::containsDiagnostic(missing_header.diagnostics, "FESA-CSV-MISSING-COLUMN"),
|
||||
"missing CSV header diagnostic changed");
|
||||
|
||||
const auto duplicate_node = fesa::loadDisplacementCsvFromString("Node Label,U-U1,U-U2,U-U3,UR-UR1,UR-UR2,UR-UR3\n"
|
||||
"1,0,0,0,0,0,0\n"
|
||||
"1,0,0,0,0,0,0\n",
|
||||
"duplicate-node.csv");
|
||||
check(fesa::containsDiagnostic(duplicate_node.diagnostics, "FESA-CSV-DUPLICATE-NODE"),
|
||||
"duplicate CSV node diagnostic changed");
|
||||
|
||||
fesa::CsvDisplacementTable expected;
|
||||
expected.rows[1] = {1, {0, 0, 0, 0, 0, 0}};
|
||||
expected.rows[2] = {2, {0, 0, -0.25, 0, 0, 0}};
|
||||
expected.rows[3] = {3, {0, 0, 0, 0, 0, 0}};
|
||||
expected.rows[4] = {4, {0, 0, 0, 0, 0, 0}};
|
||||
const auto comparison = fesa::compareDisplacements(u_field, expected, {1.0e-12, 1.0e-12, 1.0});
|
||||
check(comparison.pass, "displacement comparator no longer matches by node id");
|
||||
|
||||
fesa::CsvReactionTable expected_reactions;
|
||||
expected_reactions.rows[1] = {1, {0, 0, 3.0, 0, 0, 0}};
|
||||
expected_reactions.rows[2] = {2, {0, 0, 0, 0, 0, 0}};
|
||||
expected_reactions.rows[3] = {3, {0, 0, 0, 0, 0, 0}};
|
||||
expected_reactions.rows[4] = {4, {0, 0, 0, 0, 0, 0}};
|
||||
const auto reaction_comparison = fesa::compareReactions(rf_field, expected_reactions, {1.0e-12, 1.0e-12, 1.0});
|
||||
check(reaction_comparison.pass, "reaction comparator no longer matches by node id");
|
||||
|
||||
const auto quad02 = fesa::loadDisplacementCsv(sourceRoot() + "/references/quad_02_displacements.csv");
|
||||
check(!fesa::hasError(quad02.diagnostics), "quad_02 displacement CSV no longer loads");
|
||||
check(quad02.rows.size() == 121, "quad_02 displacement CSV row count changed");
|
||||
|
||||
const auto quad02_reactions = fesa::loadReactionCsv(sourceRoot() + "/references/quad_02_reactionforces.csv");
|
||||
check(!fesa::hasError(quad02_reactions.diagnostics), "quad_02 reaction CSV no longer loads");
|
||||
check(quad02_reactions.rows.size() == 121, "quad_02 reaction CSV row count changed");
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user