fix(input): validate inactive Part records

This commit is contained in:
KOKO\Mimi
2026-08-01 04:07:18 +09:00
parent af886f3f90
commit cbb621bb28
10 changed files with 383 additions and 151 deletions
+236 -149
View File
@@ -77,18 +77,21 @@ public:
instance_name_ = std::move(selected.input->instance_name);
collect_materials();
collect_nodes();
collect_nodes(active_records_, node_ids_, true);
SetResolutionResult resolved_sets = resolve_sets(deck_);
if (!resolved_sets.diagnostics.empty()) {
diagnostics_.insert(
diagnostics_.end(),
std::make_move_iterator(resolved_sets.diagnostics.begin()),
std::make_move_iterator(resolved_sets.diagnostics.end()));
} else {
}
validate_inactive_parts(resolved_sets.sets);
if (resolved_sets.diagnostics.empty()) {
collect_resolved_sets(resolved_sets.sets);
}
collect_sections();
collect_elements();
collect_sections(active_records_, active_element_sets_, true);
collect_elements(
active_records_, node_ids_, element_ids_, true);
emit_sets();
collect_step();
@@ -318,8 +321,11 @@ private:
}
}
void collect_nodes() {
for (const DeckRecord& record : active_records_) {
void collect_nodes(
const std::span<const DeckRecord> records,
LabelMap& node_ids,
const bool emit) {
for (const DeckRecord& record : records) {
if (record.keyword != "NODE") {
continue;
}
@@ -356,7 +362,7 @@ private:
continue;
}
if (node_ids_.contains(*label)) {
if (node_ids.contains(*label)) {
add_error(
"abaqus.semantic.duplicate_node_label",
"Node label " + std::to_string(*label) +
@@ -365,17 +371,151 @@ private:
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},
});
const NodeId id = emit
? NodeId{next_node_id_++}
: NodeId{static_cast<std::int64_t>(
node_ids.size())};
node_ids.emplace(*label, id);
if (emit) {
builder_.add_node({
id,
EntityOrigin{part_name_, instance_name_, *label},
Vec3{*x, *y, *z},
});
}
}
}
}
void validate_inactive_mesh(const ParsedPart& part) {
LabelMap node_ids;
ElementLabelMap element_ids;
collect_nodes(part.records, node_ids, false);
collect_elements(part.records, node_ids, element_ids, false);
}
struct SectionData final {
std::array<double, 5> properties{};
std::array<double, 3> orientation{};
std::optional<std::array<double, 3>> shear_values;
bool valid = true;
};
SectionData validate_section_data(
const DeckRecord& record,
const DeckRecord* shear) {
SectionData result;
if (record.data.size() != 2U ||
record.data[0].size() != result.properties.size() ||
record.data[1].size() != result.orientation.size()) {
SourceLocation source = record.source;
if (!record.data.empty()) {
source = record.data[0].size() != result.properties.size()
? data_source(record, 0U)
: data_source(
record,
record.data.size() > 2U ? 2U : 1U);
}
add_error(
"abaqus.semantic.invalid_section_data",
"*BEAM GENERAL SECTION requires exactly A, Iy, Iyz, Iz, J "
"and one three-component orientation row.",
source);
result.valid = false;
} else {
bool valid_properties = true;
for (std::size_t field = 0; field < result.properties.size();
++field) {
valid_properties =
parse_number(
record.data[0][field], result.properties[field]) &&
valid_properties;
}
valid_properties =
valid_properties && result.properties[0] > 0.0 &&
result.properties[1] > 0.0 && result.properties[2] == 0.0 &&
result.properties[3] > 0.0 && result.properties[4] > 0.0;
if (!valid_properties) {
add_error(
"abaqus.semantic.invalid_section_data",
"General Beam properties require finite positive A, Iy, "
"Iz, J and Iyz=0.",
data_source(record, 0U));
}
bool valid_orientation = true;
for (std::size_t field = 0; field < result.orientation.size();
++field) {
valid_orientation =
parse_number(
record.data[1][field], result.orientation[field]) &&
valid_orientation;
}
if (!valid_orientation) {
add_error(
"abaqus.semantic.invalid_section_data",
"Beam section orientation components must be finite.",
data_source(record, 1U));
}
result.valid = valid_properties && valid_orientation;
}
if (shear == nullptr) {
return result;
}
const SourceLocation source =
shear->data.empty() ? shear->source : data_source(*shear, 0U);
std::array<double, 3> values{};
bool valid_shear = shear->data.size() == 1U &&
shear->data[0].size() == values.size();
if (valid_shear) {
for (std::size_t field = 0; field < values.size(); ++field) {
valid_shear =
parse_number(shear->data[0][field], values[field]) &&
valid_shear;
}
}
if (!valid_shear || values[0] <= 0.0 || values[1] <= 0.0) {
add_error(
"abaqus.semantic.invalid_transverse_shear_data",
"*TRANSVERSE SHEAR STIFFNESS requires finite positive K23, "
"K13 and numeric SCF=0.",
source);
result.valid = false;
} else if (values[2] != 0.0) {
add_error(
"abaqus.semantic.nonzero_scf",
"Phase 1 supports only SCF=0.",
source);
result.valid = false;
} else {
result.shear_values = values;
}
return result;
}
void validate_inactive_parts(
const std::vector<ResolvedSet>& resolved_sets) {
if (flat_) {
return;
}
for (const ParsedPart& part : deck_.parts) {
if (part.name == part_name_) {
continue;
}
validate_inactive_mesh(part);
RawSetMap element_sets;
for (const ResolvedSet& set : resolved_sets) {
if (set.scope == ResolvedSetScope::part &&
set.scope_name == part.name &&
set.kind == ResolvedSetKind::element) {
element_sets[set.set_name] = set.sorted_unique_labels;
}
}
collect_sections(part.records, element_sets, false);
}
}
void collect_resolved_sets(const std::vector<ResolvedSet>& sets) {
for (const ResolvedSet& set : sets) {
const bool mesh_scope =
@@ -417,9 +557,14 @@ private:
}
}
void collect_sections() {
for (std::size_t index = 0; index < active_records_.size(); ++index) {
const DeckRecord& record = active_records_[index];
void collect_sections(
const std::span<const DeckRecord> records,
const RawSetMap& element_sets,
const bool emit) {
std::set<std::string, std::less<>> assigned_set_names;
std::set<std::int64_t> assigned_elements;
for (std::size_t index = 0; index < records.size(); ++index) {
const DeckRecord& record = records[index];
if (record.keyword == "TRANSVERSE SHEAR STIFFNESS") {
add_error(
"abaqus.semantic.orphan_transverse_shear",
@@ -433,10 +578,10 @@ private:
}
const DeckRecord* shear = nullptr;
if (index + 1U < active_records_.size() &&
active_records_[index + 1U].keyword ==
if (index + 1U < records.size() &&
records[index + 1U].keyword ==
"TRANSVERSE SHEAR STIFFNESS") {
shear = &active_records_[index + 1U];
shear = &records[index + 1U];
++index;
}
@@ -459,7 +604,7 @@ private:
}
if (element_set != nullptr &&
!assigned_element_set_names_.insert(*element_set).second) {
!assigned_set_names.insert(*element_set).second) {
add_error(
"abaqus.semantic.duplicate_section_assignment",
"Element set '" + *element_set +
@@ -481,10 +626,10 @@ private:
}
}
auto resolved_set = active_element_sets_.end();
auto resolved_set = element_sets.end();
if (element_set != nullptr) {
resolved_set = active_element_sets_.find(*element_set);
if (resolved_set == active_element_sets_.end()) {
resolved_set = element_sets.find(*element_set);
if (resolved_set == element_sets.end()) {
add_error(
"abaqus.semantic.missing_element_set",
"Beam section references missing element set '" +
@@ -494,132 +639,48 @@ private:
}
}
std::array<double, 5> properties{};
std::array<double, 3> orientation{};
if (record.data.size() != 2U ||
record.data[0].size() != properties.size() ||
record.data[1].size() != orientation.size()) {
SourceLocation section_data_source = record.source;
if (!record.data.empty()) {
if (record.data[0].size() != properties.size()) {
section_data_source = data_source(record, 0U);
} else if (record.data.size() > 2U) {
section_data_source = data_source(record, 2U);
} else if (record.data.size() == 2U) {
section_data_source = data_source(record, 1U);
}
}
add_error(
"abaqus.semantic.invalid_section_data",
"*BEAM GENERAL SECTION requires exactly A, Iy, Iyz, Iz, "
"J and one three-component orientation row.",
section_data_source);
valid = false;
} else {
bool valid_properties = true;
for (std::size_t field = 0; field < properties.size(); ++field) {
valid_properties = parse_number(
record.data[0][field],
properties[field]) &&
valid_properties;
}
valid_properties =
valid_properties && properties[0] > 0.0 &&
properties[1] > 0.0 && properties[2] == 0.0 &&
properties[3] > 0.0 && properties[4] > 0.0;
if (!valid_properties) {
add_error(
"abaqus.semantic.invalid_section_data",
"General Beam properties require finite positive A, "
"Iy, Iz, J and Iyz=0.",
data_source(record, 0U));
}
bool valid_orientation = true;
for (std::size_t field = 0; field < orientation.size(); ++field) {
valid_orientation = parse_number(
record.data[1][field],
orientation[field]) &&
valid_orientation;
}
if (!valid_orientation) {
add_error(
"abaqus.semantic.invalid_section_data",
"Beam section orientation components must be finite.",
data_source(record, 1U));
}
valid = valid && valid_properties && valid_orientation;
}
SectionData section_data = validate_section_data(record, shear);
valid = valid && section_data.valid;
double shear_area_y = 0.0;
double shear_area_z = 0.0;
ShearPropertySource shear_source =
ShearPropertySource::phase1_default;
if (shear == nullptr) {
shear_area_y = 5.0 * properties[0] / 6.0;
shear_area_y = 5.0 * section_data.properties[0] / 6.0;
shear_area_z = shear_area_y;
} else {
} else if (section_data.shear_values.has_value() &&
material != materials_.end()) {
const SourceLocation shear_source_location =
shear->data.empty() ? shear->source
: data_source(*shear, 0U);
std::array<double, 3> values{};
bool valid_shear =
shear->data.size() == 1U &&
shear->data[0].size() == values.size();
if (valid_shear) {
for (std::size_t field = 0; field < values.size(); ++field) {
valid_shear = parse_number(
shear->data[0][field], values[field]) &&
valid_shear;
}
}
if (!valid_shear || values[0] <= 0.0 || values[1] <= 0.0) {
const double shear_modulus =
material->second.young /
(2.0 * (1.0 + material->second.poisson));
shear_area_y = (*section_data.shear_values)[0] / shear_modulus;
shear_area_z = (*section_data.shear_values)[1] / shear_modulus;
if (!std::isfinite(shear_area_y) ||
!std::isfinite(shear_area_z) || shear_area_y <= 0.0 ||
shear_area_z <= 0.0) {
add_error(
"abaqus.semantic.invalid_transverse_shear_data",
"*TRANSVERSE SHEAR STIFFNESS requires finite positive "
"K23, K13 and numeric SCF=0.",
"Transverse stiffness produces an invalid effective "
"shear area.",
shear_source_location);
valid = false;
} else if (values[2] != 0.0) {
add_error(
"abaqus.semantic.nonzero_scf",
"Phase 1 supports only SCF=0.",
shear_source_location);
valid = false;
} else if (material != materials_.end()) {
const double shear_modulus =
material->second.young /
(2.0 * (1.0 + material->second.poisson));
shear_area_y = values[0] / shear_modulus;
shear_area_z = values[1] / shear_modulus;
if (!std::isfinite(shear_area_y) ||
!std::isfinite(shear_area_z) ||
shear_area_y <= 0.0 || shear_area_z <= 0.0) {
add_error(
"abaqus.semantic.invalid_transverse_shear_data",
"Transverse stiffness produces an invalid effective "
"shear area.",
shear_source_location);
valid = false;
}
shear_source = ShearPropertySource::input;
}
shear_source = ShearPropertySource::input;
}
if (!valid || element_set == nullptr ||
material == materials_.end() ||
resolved_set == active_element_sets_.end()) {
resolved_set == element_sets.end()) {
continue;
}
const SectionId section_id{next_section_id_++};
const SectionAssignment assignment{
material->second.id,
section_id,
};
bool duplicate_member = false;
for (const std::int64_t label : resolved_set->second) {
if (element_assignments_.contains(label)) {
if (assigned_elements.contains(label)) {
duplicate_member = true;
break;
}
@@ -631,6 +692,18 @@ private:
record.source);
continue;
}
for (const std::int64_t label : resolved_set->second) {
assigned_elements.insert(label);
}
if (!emit) {
continue;
}
const SectionId section_id{next_section_id_++};
const SectionAssignment assignment{
material->second.id,
section_id,
};
for (const std::int64_t label : resolved_set->second) {
element_assignments_.emplace(label, assignment);
}
@@ -638,21 +711,28 @@ private:
builder_.add_section({
section_id,
*element_set,
properties[0],
properties[1],
properties[3],
properties[4],
section_data.properties[0],
section_data.properties[1],
section_data.properties[3],
section_data.properties[4],
shear_area_y,
shear_area_z,
shear_source,
Vec3{orientation[0], orientation[1], orientation[2]},
Vec3{
section_data.orientation[0],
section_data.orientation[1],
section_data.orientation[2]},
{},
});
}
}
void collect_elements() {
for (const DeckRecord& record : active_records_) {
void collect_elements(
const std::span<const DeckRecord> records,
const LabelMap& node_ids,
ElementLabelMap& element_ids,
const bool emit) {
for (const DeckRecord& record : records) {
if (record.keyword != "ELEMENT") {
continue;
}
@@ -702,7 +782,7 @@ private:
continue;
}
if (element_ids_.contains(*label)) {
if (element_ids.contains(*label)) {
add_error(
"abaqus.semantic.duplicate_element_label",
"Element label " + std::to_string(*label) +
@@ -711,19 +791,21 @@ private:
continue;
}
const auto assignment = element_assignments_.find(*label);
if (assignment == element_assignments_.end()) {
add_error(
"abaqus.semantic.missing_section",
"B31 element " + std::to_string(*label) +
" has no Beam section assignment.",
record.source);
continue;
auto assignment = element_assignments_.end();
if (emit) {
assignment = element_assignments_.find(*label);
if (assignment == element_assignments_.end()) {
add_error(
"abaqus.semantic.missing_section",
"B31 element " + std::to_string(*label) +
" has no Beam section assignment.",
source);
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()) {
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.",
@@ -731,8 +813,14 @@ private:
continue;
}
const ElementId id{next_element_id_++};
element_ids_.emplace(*label, id);
const ElementId id = emit
? ElementId{next_element_id_++}
: ElementId{static_cast<std::int64_t>(
element_ids.size())};
element_ids.emplace(*label, id);
if (!emit) {
continue;
}
builder_.add_beam_element({
id,
EntityOrigin{part_name_, instance_name_, *label},
@@ -1041,7 +1129,6 @@ private:
std::map<std::string, std::vector<std::int64_t>, std::less<>>
active_element_sets_;
std::map<std::int64_t, SectionAssignment> element_assignments_;
std::set<std::string, std::less<>> assigned_element_set_names_;
RawSetMap raw_node_sets_;
RawSetMap raw_assembly_node_sets_;
RawSetMap raw_element_sets_;