fix(input): validate inactive Part records
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
"step": 0,
|
||||
"name": "abaqus-input-contract",
|
||||
"status": "completed",
|
||||
"summary": "Defined the normative Phase 1 Abaqus subset and expanded the public parser/mapper fixture matrix to 69 cases covering strict scopes, parameter forms, data ownership, exact mesh rows, duplicate Parts, Assembly-only hierarchical targets, and source-accurate diagnostics.",
|
||||
"summary": "Defined the normative Phase 1 Abaqus subset and expanded the public parser/mapper fixture matrix to 74 cases covering strict scopes, parameter forms, data ownership, exact mesh rows and references in active and inactive Parts, duplicate Parts, Assembly-only hierarchical targets, and source-accurate diagnostics.",
|
||||
"started_at": "2026-08-01T02:16:39+0900",
|
||||
"completed_at": "2026-08-01T02:24:23+0900"
|
||||
},
|
||||
|
||||
@@ -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_;
|
||||
|
||||
Vendored
+5
@@ -68,3 +68,8 @@ hierarchical_part_target_invalid invalid invalid/hierarchical_part_set_target.in
|
||||
material_data_invalid invalid invalid/material_data.inp syntax abaqus.syntax.data_without_keyword 2 - - - - - - - - - - -
|
||||
node_coordinate_invalid invalid invalid/node_invalid_coordinate.inp semantic abaqus.semantic.invalid_number 2 - - - - - - - - - - -
|
||||
element_missing_node_invalid invalid invalid/element_missing_node.inp semantic abaqus.semantic.missing_node 4 - - - - - - - - - - -
|
||||
inactive_part_node_invalid invalid invalid/inactive_part_node_surplus.inp semantic abaqus.semantic.invalid_node_data 3 - - - - - - - - - - -
|
||||
inactive_part_element_invalid invalid invalid/inactive_part_element_type.inp semantic abaqus.semantic.unsupported_element 2 - - - - - - - - - - -
|
||||
inactive_part_section_invalid invalid invalid/inactive_part_section_data.inp semantic abaqus.semantic.invalid_section_data 3 - - - - - - - - - - -
|
||||
inactive_part_duplicate_node_invalid invalid invalid/inactive_part_duplicate_node.inp semantic abaqus.semantic.duplicate_node_label 4 - - - - - - - - - - -
|
||||
inactive_part_missing_node_invalid invalid invalid/inactive_part_missing_node.inp semantic abaqus.semantic.missing_node 5 - - - - - - - - - - -
|
||||
|
||||
|
@@ -0,0 +1,11 @@
|
||||
*PART, NAME=Unused
|
||||
*NODE
|
||||
1, 0.0, 0.0, 0.0
|
||||
1, 1.0, 0.0, 0.0
|
||||
*END PART
|
||||
*PART, NAME=Active
|
||||
*END PART
|
||||
*ASSEMBLY, NAME=RootAssembly
|
||||
*INSTANCE, NAME=Active-1, PART=Active
|
||||
*END INSTANCE
|
||||
*END ASSEMBLY
|
||||
@@ -0,0 +1,10 @@
|
||||
*PART, NAME=Unused
|
||||
*ELEMENT, TYPE=B32
|
||||
1, 1, 2
|
||||
*END PART
|
||||
*PART, NAME=Active
|
||||
*END PART
|
||||
*ASSEMBLY, NAME=RootAssembly
|
||||
*INSTANCE, NAME=Active-1, PART=Active
|
||||
*END INSTANCE
|
||||
*END ASSEMBLY
|
||||
@@ -0,0 +1,12 @@
|
||||
*PART, NAME=Unused
|
||||
*NODE
|
||||
1, 0.0, 0.0, 0.0
|
||||
*ELEMENT, TYPE=B31
|
||||
1, 1, 2
|
||||
*END PART
|
||||
*PART, NAME=Active
|
||||
*END PART
|
||||
*ASSEMBLY, NAME=RootAssembly
|
||||
*INSTANCE, NAME=Active-1, PART=Active
|
||||
*END INSTANCE
|
||||
*END ASSEMBLY
|
||||
@@ -0,0 +1,10 @@
|
||||
*PART, NAME=Unused
|
||||
*NODE
|
||||
1, 0.0, 0.0, 0.0, 9.0
|
||||
*END PART
|
||||
*PART, NAME=Active
|
||||
*END PART
|
||||
*ASSEMBLY, NAME=RootAssembly
|
||||
*INSTANCE, NAME=Active-1, PART=Active
|
||||
*END INSTANCE
|
||||
*END ASSEMBLY
|
||||
@@ -0,0 +1,11 @@
|
||||
*PART, NAME=Unused
|
||||
*BEAM GENERAL SECTION, SECTION=GENERAL, ELSET=Beam, MATERIAL=Steel
|
||||
0.0, 1.0, 0.0, 1.0, 1.0
|
||||
0.0, 1.0, 0.0
|
||||
*END PART
|
||||
*PART, NAME=Active
|
||||
*END PART
|
||||
*ASSEMBLY, NAME=RootAssembly
|
||||
*INSTANCE, NAME=Active-1, PART=Active
|
||||
*END INSTANCE
|
||||
*END ASSEMBLY
|
||||
@@ -333,6 +333,92 @@ TEST(ActiveInstance, ExcludesPartsNotReferencedByTheInstance) {
|
||||
EXPECT_EQ(result.domain->nodes()[1].origin.part_name, "BeamPart");
|
||||
}
|
||||
|
||||
TEST(ActiveInstance, RejectsInvalidRecordsInUnreferencedParts) {
|
||||
struct Case final {
|
||||
std::string_view name;
|
||||
std::string_view unused_records;
|
||||
std::string_view code;
|
||||
std::size_t line;
|
||||
};
|
||||
const Case cases[]{
|
||||
{
|
||||
"node-surplus",
|
||||
"*NODE\n1, 0.0, 0.0, 0.0, 9.0\n",
|
||||
"abaqus.semantic.invalid_node_data",
|
||||
3U,
|
||||
},
|
||||
{
|
||||
"element-type",
|
||||
"*ELEMENT, TYPE=B32\n1, 1, 2\n",
|
||||
"abaqus.semantic.unsupported_element",
|
||||
2U,
|
||||
},
|
||||
{
|
||||
"duplicate-node",
|
||||
"*NODE\n1, 0.0, 0.0, 0.0\n1, 1.0, 0.0, 0.0\n",
|
||||
"abaqus.semantic.duplicate_node_label",
|
||||
4U,
|
||||
},
|
||||
{
|
||||
"missing-node",
|
||||
"*NODE\n1, 0.0, 0.0, 0.0\n"
|
||||
"*ELEMENT, TYPE=B31\n1, 1, 2\n",
|
||||
"abaqus.semantic.missing_node",
|
||||
5U,
|
||||
},
|
||||
{
|
||||
"section-data",
|
||||
"*BEAM GENERAL SECTION, SECTION=GENERAL, ELSET=Beam, "
|
||||
"MATERIAL=Steel\n"
|
||||
"0.0, 1.0, 0.0, 1.0, 1.0\n"
|
||||
"0.0, 1.0, 0.0\n",
|
||||
"abaqus.semantic.invalid_section_data",
|
||||
3U,
|
||||
},
|
||||
};
|
||||
const std::string active_model{
|
||||
"*PART, NAME=BeamPart\n"
|
||||
"*NODE\n"
|
||||
"1, 0.0, 0.0, 0.0\n"
|
||||
"2, 1.0, 0.0, 0.0\n"
|
||||
"*ELEMENT, TYPE=B31, ELSET=Beam\n"
|
||||
"1, 1, 2\n"
|
||||
"*ELSET, ELSET=Beam\n"
|
||||
"1\n"
|
||||
"*BEAM GENERAL SECTION, SECTION=GENERAL, ELSET=Beam, MATERIAL=Steel\n"
|
||||
"1.0, 1.0, 0.0, 1.0, 1.0\n"
|
||||
"0.0, 1.0, 0.0\n"
|
||||
"*END PART\n"
|
||||
"*ASSEMBLY, NAME=RootAssembly\n"
|
||||
"*INSTANCE, NAME=Beam-1, PART=BeamPart\n"
|
||||
"*END INSTANCE\n"
|
||||
"*END ASSEMBLY\n"
|
||||
"*MATERIAL, NAME=Steel\n"
|
||||
"*ELASTIC\n"
|
||||
"210000.0, 0.3\n"
|
||||
"*STEP\n"
|
||||
"*STATIC\n"
|
||||
"*END STEP\n"};
|
||||
|
||||
for (const auto& test_case : cases) {
|
||||
const TemporaryDeck input{
|
||||
"fesa-inactive-part-" + std::string{test_case.name} + ".inp",
|
||||
"*PART, NAME=Unused\n" + std::string{test_case.unused_records} +
|
||||
"*END PART\n" + active_model,
|
||||
};
|
||||
|
||||
const auto result = parse_and_map(input.path());
|
||||
|
||||
SCOPED_TRACE(test_case.name);
|
||||
EXPECT_FALSE(result.domain.has_value());
|
||||
const fesa::Diagnostic* diagnostic =
|
||||
find_diagnostic(result, test_case.code);
|
||||
ASSERT_NE(diagnostic, nullptr);
|
||||
ASSERT_TRUE(diagnostic->source.has_value());
|
||||
EXPECT_EQ(diagnostic->source->line, test_case.line);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(ActiveInstance, RejectsInstanceTransformWithSourceDiagnostic) {
|
||||
const TemporaryDeck input{
|
||||
"fesa-instance-transform.inp",
|
||||
|
||||
@@ -315,7 +315,7 @@ TEST(BeamSection, RejectsElementWithoutSectionAssignment) {
|
||||
|
||||
const auto result = parse_and_map(input);
|
||||
|
||||
expect_diagnostic(result, "abaqus.semantic.missing_section", 4U);
|
||||
expect_diagnostic(result, "abaqus.semantic.missing_section", 5U);
|
||||
}
|
||||
|
||||
TEST(BeamSection, RejectsDuplicateSectionAssignment) {
|
||||
|
||||
Reference in New Issue
Block a user