feat: add linear static workflow model

This commit is contained in:
NINI
2026-05-04 23:45:13 +09:00
parent d373969732
commit 948a9448ff
5 changed files with 259 additions and 8 deletions
+93 -2
View File
@@ -229,6 +229,14 @@ struct Node {
enum class ElementType { MITC4 };
inline std::string elementTypeLabel(ElementType type) {
switch (type) {
case ElementType::MITC4:
return "MITC4";
}
return "UNKNOWN";
}
struct Element {
GlobalId id = 0;
ElementType type = ElementType::MITC4;
@@ -953,6 +961,61 @@ inline std::vector<Diagnostic> validateDomain(const Domain& domain) {
return diagnostics;
}
struct AnalysisModel {
StepDefinition step;
std::vector<GlobalId> active_element_ids;
std::vector<std::size_t> active_boundary_condition_indices;
std::vector<std::size_t> active_load_indices;
std::vector<std::size_t> active_shell_section_indices;
std::vector<std::string> active_material_keys;
std::vector<Diagnostic> diagnostics;
bool ok() const {
return !hasError(diagnostics);
}
};
inline AnalysisModel buildLinearStaticAnalysisModel(const Domain& domain, LocalIndex step_index = 0) {
AnalysisModel model;
if (domain.steps.empty()) {
model.step = {"Step-1", "linear_static"};
} else {
if (step_index < 0 || step_index >= static_cast<LocalIndex>(domain.steps.size())) {
model.diagnostics.push_back(makeDiagnostic(Severity::Error, "FESA-ANALYSIS-STEP-INDEX",
"Requested analysis step index is out of range", "analysis model"));
model.step = domain.steps.front();
} else {
model.step = domain.steps[static_cast<std::size_t>(step_index)];
}
}
if (domain.steps.size() > 1) {
model.diagnostics.push_back(makeDiagnostic(Severity::Error, "FESA-ANALYSIS-MULTIPLE-STEPS",
"Phase 1 execution supports one active linear static step", "analysis model"));
}
if (model.step.analysis_type != "linear_static") {
model.diagnostics.push_back(makeDiagnostic(Severity::Error, "FESA-ANALYSIS-UNSUPPORTED-STEP",
"Only linear static steps are supported in Phase 1", "analysis model"));
}
for (const auto& [element_id, element] : domain.elements) {
(void)element;
model.active_element_ids.push_back(element_id);
}
for (std::size_t i = 0; i < domain.boundary_conditions.size(); ++i) {
model.active_boundary_condition_indices.push_back(i);
}
for (std::size_t i = 0; i < domain.loads.size(); ++i) {
model.active_load_indices.push_back(i);
}
for (std::size_t i = 0; i < domain.shell_sections.size(); ++i) {
model.active_shell_section_indices.push_back(i);
}
for (const auto& [material_key, material] : domain.materials) {
(void)material;
model.active_material_keys.push_back(material_key);
}
return model;
}
struct DofAddress {
GlobalId node_id = 0;
Dof dof = Dof::UX;
@@ -2291,6 +2354,7 @@ struct ResultFile {
std::vector<GlobalId> node_ids;
std::vector<Vec3> coordinates;
std::vector<GlobalId> element_ids;
std::vector<std::string> element_types;
std::vector<std::array<GlobalId, 4>> connectivity;
std::vector<ResultStep> steps;
};
@@ -2298,6 +2362,12 @@ struct ResultFile {
class InMemoryResultsWriter {
public:
void writeLinearStatic(const Domain& domain, const DofManager& dofs, const std::vector<Real>& u_full, const std::vector<Real>& rf_full) {
const auto model = buildLinearStaticAnalysisModel(domain);
writeLinearStatic(domain, model, dofs, u_full, rf_full);
}
void writeLinearStatic(const Domain& domain, const AnalysisModel& model, const DofManager& dofs,
const std::vector<Real>& u_full, const std::vector<Real>& rf_full) {
result_ = ResultFile{};
for (const auto& [node_id, node] : domain.nodes) {
result_.node_ids.push_back(node_id);
@@ -2305,10 +2375,11 @@ class InMemoryResultsWriter {
}
for (const auto& [element_id, element] : domain.elements) {
result_.element_ids.push_back(element_id);
result_.element_types.push_back(elementTypeLabel(element.type));
result_.connectivity.push_back(element.node_ids);
}
ResultStep step;
step.name = domain.steps.empty() ? "Step-1" : domain.steps.front().name;
step.name = model.step.name.empty() ? "Step-1" : model.step.name;
ResultFrame frame;
frame.frame_id = 0;
frame.field_outputs["U"] = buildNodalField("U", displacementComponentLabels(), "Nodal displacement and rotation", domain, dofs, u_full);
@@ -2355,6 +2426,7 @@ struct AnalysisState {
};
struct AnalysisResult {
AnalysisModel model;
AnalysisState state;
ResultFile result_file;
std::vector<Diagnostic> diagnostics;
@@ -2391,6 +2463,11 @@ class LinearStaticAnalysis final : public Analysis {
protected:
void solve(const Domain& domain, AnalysisResult& result) const override {
result.model = buildLinearStaticAnalysisModel(domain);
result.diagnostics.insert(result.diagnostics.end(), result.model.diagnostics.begin(), result.model.diagnostics.end());
if (hasError(result.diagnostics)) {
return;
}
DofManager dofs(domain);
if (dofs.freeDofCount() == 0) {
result.diagnostics.push_back(makeDiagnostic(Severity::Error, "FESA-SINGULAR-NO-FREE-DOFS",
@@ -2424,7 +2501,7 @@ class LinearStaticAnalysis final : public Analysis {
result.state.reaction_full = recoverFullReaction(assembly.k_full, result.state.u_full, result.state.f_external_full);
result.state.converged = true;
InMemoryResultsWriter writer;
writer.writeLinearStatic(domain, dofs, result.state.u_full, result.state.reaction_full);
writer.writeLinearStatic(domain, result.model, dofs, result.state.u_full, result.state.reaction_full);
result.result_file = writer.result();
}
@@ -2437,6 +2514,20 @@ class LinearStaticAnalysis final : public Analysis {
const LinearSolver* solver_ = nullptr;
};
inline AnalysisResult runLinearStaticInputString(const std::string& text,
const std::string& source_name = "<memory>",
const LinearSolver* solver = nullptr) {
AbaqusInputParser parser;
const auto parsed = parser.parseString(text, source_name);
if (!parsed.ok()) {
AnalysisResult result;
result.diagnostics = parsed.diagnostics;
return result;
}
LinearStaticAnalysis analysis(solver);
return analysis.run(parsed.domain);
}
struct CsvDisplacementRow {
GlobalId node_id = 0;
std::array<Real, 6> values{};