feat(domain-and-input-skeleton): step 3 — active-instance-domain-normalization
This commit is contained in:
@@ -0,0 +1,684 @@
|
||||
#include <fesa/io/abaqus/semantic_mapper.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <charconv>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace fesa {
|
||||
namespace {
|
||||
|
||||
using LabelMap = std::map<std::int64_t, NodeId>;
|
||||
using ElementLabelMap = std::map<std::int64_t, ElementId>;
|
||||
using RawSetMap = std::map<std::string, std::vector<std::int64_t>, std::less<>>;
|
||||
|
||||
const std::string* parameter(
|
||||
const DeckRecord& record,
|
||||
const std::string_view name) {
|
||||
const auto found = record.parameters.find(name);
|
||||
return found == record.parameters.end() ? nullptr : &found->second;
|
||||
}
|
||||
|
||||
bool is_mesh_record(const DeckRecord& record) {
|
||||
return record.keyword == "NODE" || record.keyword == "ELEMENT";
|
||||
}
|
||||
|
||||
class DeckMapper final {
|
||||
public:
|
||||
explicit DeckMapper(const ParsedDeck& deck) : deck_{deck} {}
|
||||
|
||||
[[nodiscard]] DomainBuildResult map() {
|
||||
if (!select_active_scope()) {
|
||||
return failure();
|
||||
}
|
||||
|
||||
collect_materials();
|
||||
collect_nodes();
|
||||
collect_raw_sets(*active_records_, false);
|
||||
if (active_assembly_ != nullptr) {
|
||||
collect_raw_sets(active_assembly_->records, true);
|
||||
}
|
||||
collect_sections();
|
||||
collect_elements();
|
||||
emit_sets();
|
||||
collect_step();
|
||||
|
||||
if (!diagnostics_.empty()) {
|
||||
return failure();
|
||||
}
|
||||
return std::move(builder_).build();
|
||||
}
|
||||
|
||||
private:
|
||||
struct SectionAssignment final {
|
||||
MaterialId material;
|
||||
SectionId section;
|
||||
};
|
||||
|
||||
[[nodiscard]] DomainBuildResult failure() {
|
||||
return {std::nullopt, std::move(diagnostics_)};
|
||||
}
|
||||
|
||||
void add_error(
|
||||
std::string code,
|
||||
std::string message,
|
||||
const SourceLocation& source) {
|
||||
diagnostics_.push_back({
|
||||
DiagnosticStage::semantic,
|
||||
Severity::error,
|
||||
std::move(code),
|
||||
std::move(message),
|
||||
source,
|
||||
});
|
||||
}
|
||||
|
||||
const std::string* require_parameter(
|
||||
const DeckRecord& record,
|
||||
const std::string_view name) {
|
||||
const std::string* value = parameter(record, name);
|
||||
if (value == nullptr || value->empty()) {
|
||||
add_error(
|
||||
"abaqus.semantic.missing_parameter",
|
||||
"*" + record.keyword + " requires parameter " +
|
||||
std::string{name} + ".",
|
||||
record.source);
|
||||
return nullptr;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
std::optional<std::int64_t> parse_label(
|
||||
const std::string_view text,
|
||||
const DeckRecord& record,
|
||||
const std::string_view purpose) {
|
||||
std::int64_t value = 0;
|
||||
const auto parsed =
|
||||
std::from_chars(text.data(), text.data() + text.size(), value);
|
||||
if (parsed.ec != std::errc{} ||
|
||||
parsed.ptr != text.data() + text.size() || value <= 0) {
|
||||
add_error(
|
||||
"abaqus.semantic.invalid_label",
|
||||
"Invalid " + std::string{purpose} + " label '" +
|
||||
std::string{text} + "'.",
|
||||
record.source);
|
||||
return std::nullopt;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
std::optional<std::uint8_t> parse_dof(
|
||||
const std::string_view text,
|
||||
const DeckRecord& record) {
|
||||
const std::optional<std::int64_t> value =
|
||||
parse_label(text, record, "degree-of-freedom");
|
||||
if (!value.has_value()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
if (*value > 6) {
|
||||
add_error(
|
||||
"abaqus.semantic.invalid_dof",
|
||||
"Phase 1 supports only degrees of freedom 1 through 6.",
|
||||
record.source);
|
||||
return std::nullopt;
|
||||
}
|
||||
return static_cast<std::uint8_t>(*value);
|
||||
}
|
||||
|
||||
std::optional<double> parse_real(
|
||||
const std::string_view text,
|
||||
const DeckRecord& record,
|
||||
const std::string_view purpose) {
|
||||
double value = 0.0;
|
||||
const auto parsed = std::from_chars(
|
||||
text.data(),
|
||||
text.data() + text.size(),
|
||||
value,
|
||||
std::chars_format::general);
|
||||
if (parsed.ec != std::errc{} ||
|
||||
parsed.ptr != text.data() + text.size()) {
|
||||
add_error(
|
||||
"abaqus.semantic.invalid_number",
|
||||
"Invalid " + std::string{purpose} + " value '" +
|
||||
std::string{text} + "'.",
|
||||
record.source);
|
||||
return std::nullopt;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
bool select_active_scope() {
|
||||
const bool has_hierarchical_input =
|
||||
!deck_.parts.empty() || deck_.assembly.has_value();
|
||||
if (!has_hierarchical_input) {
|
||||
active_records_ = &deck_.global_records;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (std::ranges::any_of(deck_.global_records, is_mesh_record)) {
|
||||
add_error(
|
||||
"abaqus.semantic.mixed_mesh_organization",
|
||||
"Flat mesh records cannot be mixed with Part/Assembly input.",
|
||||
std::ranges::find_if(
|
||||
deck_.global_records, is_mesh_record)->source);
|
||||
return false;
|
||||
}
|
||||
if (!deck_.assembly.has_value()) {
|
||||
const SourceLocation source =
|
||||
deck_.parts.empty() ? SourceLocation{} : deck_.parts[0].source;
|
||||
add_error(
|
||||
"abaqus.semantic.assembly_count",
|
||||
"Hierarchical Phase 1 input requires exactly one Assembly.",
|
||||
source);
|
||||
return false;
|
||||
}
|
||||
|
||||
active_assembly_ = &*deck_.assembly;
|
||||
if (active_assembly_->instances.size() != 1U) {
|
||||
add_error(
|
||||
"abaqus.semantic.instance_count",
|
||||
"Phase 1 requires exactly one Instance.",
|
||||
active_assembly_->source);
|
||||
return false;
|
||||
}
|
||||
|
||||
const ParsedInstance& instance = active_assembly_->instances.front();
|
||||
if (!instance.transform_data.empty()) {
|
||||
add_error(
|
||||
"abaqus.semantic.instance_transform",
|
||||
"Instance translation and rotation data are unsupported.",
|
||||
instance.source);
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto part = std::ranges::find(
|
||||
deck_.parts, instance.part_name, &ParsedPart::name);
|
||||
if (part == deck_.parts.end()) {
|
||||
add_error(
|
||||
"abaqus.semantic.missing_part",
|
||||
"Instance '" + instance.name + "' references missing Part '" +
|
||||
instance.part_name + "'.",
|
||||
instance.source);
|
||||
return false;
|
||||
}
|
||||
|
||||
active_records_ = &part->records;
|
||||
part_name_ = part->name;
|
||||
instance_name_ = instance.name;
|
||||
return true;
|
||||
}
|
||||
|
||||
void collect_materials() {
|
||||
const std::string* current_name = nullptr;
|
||||
for (const DeckRecord& record : deck_.global_records) {
|
||||
if (record.keyword == "MATERIAL") {
|
||||
current_name = require_parameter(record, "NAME");
|
||||
continue;
|
||||
}
|
||||
if (record.keyword != "ELASTIC") {
|
||||
continue;
|
||||
}
|
||||
if (current_name == nullptr) {
|
||||
add_error(
|
||||
"abaqus.semantic.elastic_without_material",
|
||||
"*ELASTIC requires a preceding *MATERIAL.",
|
||||
record.source);
|
||||
continue;
|
||||
}
|
||||
if (record.data.empty() || record.data[0].size() < 2U) {
|
||||
add_error(
|
||||
"abaqus.semantic.invalid_elastic_data",
|
||||
"*ELASTIC requires Young's modulus and Poisson ratio.",
|
||||
record.source);
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto young =
|
||||
parse_real(record.data[0][0], record, "Young's modulus");
|
||||
const auto poisson =
|
||||
parse_real(record.data[0][1], record, "Poisson ratio");
|
||||
if (!young.has_value() || !poisson.has_value()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const MaterialId id{next_material_id_++};
|
||||
if (!material_ids_.emplace(*current_name, id).second) {
|
||||
add_error(
|
||||
"abaqus.semantic.duplicate_material",
|
||||
"Material '" + *current_name + "' is defined more than once.",
|
||||
record.source);
|
||||
continue;
|
||||
}
|
||||
builder_.add_material({id, *current_name, *young, *poisson});
|
||||
}
|
||||
}
|
||||
|
||||
void collect_nodes() {
|
||||
for (const DeckRecord& record : *active_records_) {
|
||||
if (record.keyword != "NODE") {
|
||||
continue;
|
||||
}
|
||||
for (const auto& row : record.data) {
|
||||
if (row.size() < 4U) {
|
||||
add_error(
|
||||
"abaqus.semantic.invalid_node_data",
|
||||
"*NODE requires a label and three coordinates.",
|
||||
record.source);
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto label = parse_label(row[0], record, "node");
|
||||
const auto x = parse_real(row[1], record, "node coordinate");
|
||||
const auto y = parse_real(row[2], record, "node coordinate");
|
||||
const auto z = parse_real(row[3], record, "node coordinate");
|
||||
if (!label.has_value() || !x.has_value() || !y.has_value() ||
|
||||
!z.has_value()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const NodeId id{next_node_id_++};
|
||||
node_ids_.emplace(*label, id);
|
||||
builder_.add_node({
|
||||
id,
|
||||
EntityOrigin{part_name_, instance_name_, *label},
|
||||
Vec3{*x, *y, *z},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void collect_raw_sets(
|
||||
const std::vector<DeckRecord>& records,
|
||||
const bool assembly_scope) {
|
||||
for (const DeckRecord& record : records) {
|
||||
const bool is_node_set = record.keyword == "NSET";
|
||||
const bool is_element_set = record.keyword == "ELSET";
|
||||
if (!is_node_set && !is_element_set) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::string* name =
|
||||
require_parameter(record, is_node_set ? "NSET" : "ELSET");
|
||||
if (name == nullptr) {
|
||||
continue;
|
||||
}
|
||||
if (assembly_scope) {
|
||||
const std::string* instance = parameter(record, "INSTANCE");
|
||||
if (instance == nullptr || *instance != instance_name_) {
|
||||
add_error(
|
||||
"abaqus.semantic.wrong_instance",
|
||||
"Assembly set '" + *name +
|
||||
"' must reference the active Instance.",
|
||||
record.source);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::int64_t> labels;
|
||||
for (const auto& row : record.data) {
|
||||
for (const std::string& field : row) {
|
||||
if (field.empty()) {
|
||||
continue;
|
||||
}
|
||||
const auto label =
|
||||
parse_label(field, record, "set member");
|
||||
if (label.has_value()) {
|
||||
labels.push_back(*label);
|
||||
}
|
||||
}
|
||||
}
|
||||
RawSetMap& sets = is_node_set ? raw_node_sets_ : raw_element_sets_;
|
||||
sets[*name] = std::move(labels);
|
||||
}
|
||||
}
|
||||
|
||||
void collect_sections() {
|
||||
for (const DeckRecord& record : *active_records_) {
|
||||
if (record.keyword != "BEAM GENERAL SECTION") {
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::string* element_set =
|
||||
require_parameter(record, "ELSET");
|
||||
const std::string* material_name =
|
||||
require_parameter(record, "MATERIAL");
|
||||
if (element_set == nullptr || material_name == nullptr) {
|
||||
continue;
|
||||
}
|
||||
const auto material = material_ids_.find(*material_name);
|
||||
if (material == material_ids_.end()) {
|
||||
add_error(
|
||||
"abaqus.semantic.missing_material",
|
||||
"Beam section references missing Material '" +
|
||||
*material_name + "'.",
|
||||
record.source);
|
||||
continue;
|
||||
}
|
||||
if (record.data.size() < 2U ||
|
||||
record.data[0].size() < 5U ||
|
||||
record.data[1].size() < 3U) {
|
||||
add_error(
|
||||
"abaqus.semantic.invalid_section_data",
|
||||
"*BEAM GENERAL SECTION requires general properties and "
|
||||
"an orientation vector.",
|
||||
record.source);
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto area = parse_real(record.data[0][0], record, "area");
|
||||
const auto iy = parse_real(record.data[0][1], record, "Iy");
|
||||
const auto iz = parse_real(record.data[0][3], record, "Iz");
|
||||
const auto torsion =
|
||||
parse_real(record.data[0][4], record, "torsion J");
|
||||
const auto ox =
|
||||
parse_real(record.data[1][0], record, "orientation");
|
||||
const auto oy =
|
||||
parse_real(record.data[1][1], record, "orientation");
|
||||
const auto oz =
|
||||
parse_real(record.data[1][2], record, "orientation");
|
||||
if (!area.has_value() || !iy.has_value() || !iz.has_value() ||
|
||||
!torsion.has_value() || !ox.has_value() || !oy.has_value() ||
|
||||
!oz.has_value()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const SectionId section_id{next_section_id_++};
|
||||
if (!section_assignments_
|
||||
.emplace(
|
||||
*element_set,
|
||||
SectionAssignment{material->second, section_id})
|
||||
.second) {
|
||||
add_error(
|
||||
"abaqus.semantic.duplicate_section_assignment",
|
||||
"Element set '" + *element_set +
|
||||
"' has more than one section assignment.",
|
||||
record.source);
|
||||
continue;
|
||||
}
|
||||
builder_.add_section({
|
||||
section_id,
|
||||
*element_set,
|
||||
*area,
|
||||
*iy,
|
||||
*iz,
|
||||
*torsion,
|
||||
5.0 * *area / 6.0,
|
||||
5.0 * *area / 6.0,
|
||||
ShearPropertySource::phase1_default,
|
||||
Vec3{*ox, *oy, *oz},
|
||||
{},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void collect_elements() {
|
||||
for (const DeckRecord& record : *active_records_) {
|
||||
if (record.keyword != "ELEMENT") {
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::string* type = require_parameter(record, "TYPE");
|
||||
const std::string* element_set =
|
||||
require_parameter(record, "ELSET");
|
||||
if (type == nullptr || element_set == nullptr) {
|
||||
continue;
|
||||
}
|
||||
if (*type != "B31") {
|
||||
add_error(
|
||||
"abaqus.semantic.unsupported_element",
|
||||
"Phase 1 supports only B31 elements.",
|
||||
record.source);
|
||||
continue;
|
||||
}
|
||||
const auto assignment = section_assignments_.find(*element_set);
|
||||
if (assignment == section_assignments_.end()) {
|
||||
add_error(
|
||||
"abaqus.semantic.missing_section",
|
||||
"Element set '" + *element_set +
|
||||
"' has no Beam section assignment.",
|
||||
record.source);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const auto& row : record.data) {
|
||||
if (row.size() < 3U) {
|
||||
add_error(
|
||||
"abaqus.semantic.invalid_element_data",
|
||||
"B31 element data requires a label and two nodes.",
|
||||
record.source);
|
||||
continue;
|
||||
}
|
||||
const auto label = parse_label(row[0], record, "element");
|
||||
const auto first_label =
|
||||
parse_label(row[1], record, "node");
|
||||
const auto second_label =
|
||||
parse_label(row[2], record, "node");
|
||||
if (!label.has_value() || !first_label.has_value() ||
|
||||
!second_label.has_value()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto first = node_ids_.find(*first_label);
|
||||
const auto second = node_ids_.find(*second_label);
|
||||
if (first == node_ids_.end() || second == node_ids_.end()) {
|
||||
add_error(
|
||||
"abaqus.semantic.missing_node",
|
||||
"B31 element references a missing node label.",
|
||||
record.source);
|
||||
continue;
|
||||
}
|
||||
|
||||
const ElementId id{next_element_id_++};
|
||||
element_ids_.emplace(*label, id);
|
||||
builder_.add_beam_element({
|
||||
id,
|
||||
EntityOrigin{part_name_, instance_name_, *label},
|
||||
{first->second, second->second},
|
||||
assignment->second.material,
|
||||
assignment->second.section,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <class Id, class Lookup>
|
||||
std::vector<Id> resolve_set(
|
||||
const std::vector<std::int64_t>& labels,
|
||||
const Lookup& lookup,
|
||||
const std::string& name) {
|
||||
std::vector<Id> members;
|
||||
for (const std::int64_t label : labels) {
|
||||
const auto found = lookup.find(label);
|
||||
if (found == lookup.end()) {
|
||||
diagnostics_.push_back({
|
||||
DiagnosticStage::semantic,
|
||||
Severity::error,
|
||||
"abaqus.semantic.missing_set_member",
|
||||
"Set '" + name + "' references a missing entity label " +
|
||||
std::to_string(label) + ".",
|
||||
std::nullopt,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
members.push_back(found->second);
|
||||
}
|
||||
std::ranges::sort(members);
|
||||
members.erase(std::ranges::unique(members).begin(), members.end());
|
||||
return members;
|
||||
}
|
||||
|
||||
void emit_sets() {
|
||||
for (const auto& [name, labels] : raw_node_sets_) {
|
||||
std::vector<NodeId> members =
|
||||
resolve_set<NodeId>(labels, node_ids_, name);
|
||||
node_sets_[name] = members;
|
||||
builder_.add_node_set({name, std::move(members)});
|
||||
}
|
||||
for (const auto& [name, labels] : raw_element_sets_) {
|
||||
std::vector<ElementId> members =
|
||||
resolve_set<ElementId>(labels, element_ids_, name);
|
||||
builder_.add_element_set({name, std::move(members)});
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<NodeId> resolve_node_target(
|
||||
const std::string& target,
|
||||
const DeckRecord& record) {
|
||||
std::int64_t label = 0;
|
||||
const auto parsed =
|
||||
std::from_chars(target.data(), target.data() + target.size(), label);
|
||||
if (parsed.ec == std::errc{} &&
|
||||
parsed.ptr == target.data() + target.size()) {
|
||||
const auto found = node_ids_.find(label);
|
||||
if (found != node_ids_.end()) {
|
||||
return {found->second};
|
||||
}
|
||||
} else {
|
||||
const auto found = node_sets_.find(target);
|
||||
if (found != node_sets_.end()) {
|
||||
return found->second;
|
||||
}
|
||||
}
|
||||
|
||||
add_error(
|
||||
"abaqus.semantic.missing_node_target",
|
||||
"Node target '" + target + "' does not resolve.",
|
||||
record.source);
|
||||
return {};
|
||||
}
|
||||
|
||||
void collect_boundary(
|
||||
const DeckRecord& record,
|
||||
StepDefinition& step) {
|
||||
for (const auto& row : record.data) {
|
||||
if (row.size() < 2U) {
|
||||
add_error(
|
||||
"abaqus.semantic.invalid_boundary_data",
|
||||
"*BOUNDARY requires a target and degree of freedom.",
|
||||
record.source);
|
||||
continue;
|
||||
}
|
||||
const auto first_dof = parse_dof(row[1], record);
|
||||
const auto last_dof = row.size() >= 3U && !row[2].empty()
|
||||
? parse_dof(row[2], record)
|
||||
: first_dof;
|
||||
const auto value = row.size() >= 4U && !row[3].empty()
|
||||
? parse_real(
|
||||
row[3], record, "prescribed value")
|
||||
: std::optional<double>{0.0};
|
||||
if (!first_dof.has_value() || !last_dof.has_value() ||
|
||||
!value.has_value()) {
|
||||
continue;
|
||||
}
|
||||
if (*last_dof < *first_dof) {
|
||||
add_error(
|
||||
"abaqus.semantic.invalid_dof_range",
|
||||
"*BOUNDARY degree-of-freedom range is reversed.",
|
||||
record.source);
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::vector<NodeId> nodes =
|
||||
resolve_node_target(row[0], record);
|
||||
for (const NodeId node : nodes) {
|
||||
for (std::uint8_t dof = *first_dof; dof <= *last_dof; ++dof) {
|
||||
step.prescribed_dofs.push_back({node, dof, *value});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void collect_loads(
|
||||
const DeckRecord& record,
|
||||
std::map<std::int64_t, std::array<double, 6>>& loads) {
|
||||
for (const auto& row : record.data) {
|
||||
if (row.size() < 3U) {
|
||||
add_error(
|
||||
"abaqus.semantic.invalid_cload_data",
|
||||
"*CLOAD requires a target, degree of freedom, and value.",
|
||||
record.source);
|
||||
continue;
|
||||
}
|
||||
const auto dof = parse_dof(row[1], record);
|
||||
const auto value = parse_real(row[2], record, "concentrated load");
|
||||
if (!dof.has_value() || !value.has_value()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::vector<NodeId> nodes =
|
||||
resolve_node_target(row[0], record);
|
||||
for (const NodeId node : nodes) {
|
||||
loads[node.value()][static_cast<std::size_t>(*dof - 1U)] +=
|
||||
*value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void collect_step() {
|
||||
StepDefinition step;
|
||||
std::map<std::int64_t, std::array<double, 6>> loads;
|
||||
bool has_step = false;
|
||||
|
||||
for (const DeckRecord& record : deck_.global_records) {
|
||||
if (record.keyword == "STEP") {
|
||||
has_step = true;
|
||||
const std::string* name = parameter(record, "NAME");
|
||||
step.name = name == nullptr ? "Step-1" : *name;
|
||||
} else if (record.keyword == "BOUNDARY") {
|
||||
collect_boundary(record, step);
|
||||
} else if (record.keyword == "CLOAD") {
|
||||
collect_loads(record, loads);
|
||||
}
|
||||
}
|
||||
if (!has_step) {
|
||||
const SourceLocation source =
|
||||
deck_.global_records.empty()
|
||||
? SourceLocation{}
|
||||
: deck_.global_records.front().source;
|
||||
add_error(
|
||||
"abaqus.semantic.missing_step",
|
||||
"Phase 1 input requires one *STEP.",
|
||||
source);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto& [node, values] : loads) {
|
||||
step.nodal_loads.push_back({NodeId{node}, values});
|
||||
}
|
||||
builder_.set_step(std::move(step));
|
||||
}
|
||||
|
||||
const ParsedDeck& deck_;
|
||||
DomainBuilder builder_;
|
||||
std::vector<Diagnostic> diagnostics_;
|
||||
const std::vector<DeckRecord>* active_records_ = nullptr;
|
||||
const ParsedAssembly* active_assembly_ = nullptr;
|
||||
std::string part_name_;
|
||||
std::string instance_name_;
|
||||
LabelMap node_ids_;
|
||||
ElementLabelMap element_ids_;
|
||||
std::map<std::string, MaterialId, std::less<>> material_ids_;
|
||||
std::map<std::string, SectionAssignment, std::less<>>
|
||||
section_assignments_;
|
||||
RawSetMap raw_node_sets_;
|
||||
RawSetMap raw_element_sets_;
|
||||
std::map<std::string, std::vector<NodeId>, std::less<>> node_sets_;
|
||||
std::int64_t next_node_id_ = 0;
|
||||
std::int64_t next_element_id_ = 0;
|
||||
std::int64_t next_material_id_ = 0;
|
||||
std::int64_t next_section_id_ = 0;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
DomainBuildResult map_deck_to_domain(const ParsedDeck& deck) {
|
||||
return DeckMapper{deck}.map();
|
||||
}
|
||||
|
||||
} // namespace fesa
|
||||
Reference in New Issue
Block a user