#include #include #include #include #include #include #include namespace fesa { namespace { void add_error( std::vector& diagnostics, std::string code, std::string message) { diagnostics.push_back({ DiagnosticStage::results, Severity::error, std::move(code), std::move(message), std::nullopt, }); } bool is_finite(const std::array& field) { return std::ranges::all_of( field, [](const double component) { return std::isfinite(component); }); } void validate_nodal_frame( const NodalFrame& nodal, std::vector& diagnostics) { if ( nodal.displacement.size() != nodal.node_ids.size() || nodal.reaction.size() != nodal.node_ids.size()) { add_error( diagnostics, "results.nodal_size_mismatch", "Nodal IDs, displacement, and reaction fields must have " "matching sizes."); } if (nodal.origins.size() != nodal.node_ids.size()) { add_error( diagnostics, "results.nodal_origin_size_mismatch", "Nodal IDs and origins must have matching sizes."); } std::set node_ids; for (const NodeId node_id : nodal.node_ids) { if (!node_ids.insert(node_id.value()).second) { add_error( diagnostics, "results.duplicate_node_id", "Nodal frame contains duplicate node ID " + std::to_string(node_id.value()) + "."); } } for (const auto& displacement : nodal.displacement) { if (!is_finite(displacement)) { add_error( diagnostics, "results.nonfinite_value", "Nodal displacement contains a nonfinite component."); } } for (const auto& reaction : nodal.reaction) { if (!is_finite(reaction)) { add_error( diagnostics, "results.nonfinite_value", "Nodal reaction contains a nonfinite component."); } } } void validate_beam_section_result( const BeamSectionResult& result, std::vector& diagnostics) { if ( !std::isfinite(result.xi) || !is_finite(result.section_strain) || !is_finite(result.section_force) || !std::isfinite(result.centroid_sigma_xx) || !std::ranges::all_of( result.sigma_xx, [](const double value) { return std::isfinite(value); })) { add_error( diagnostics, "results.nonfinite_value", "Beam section result contains a nonfinite value."); } } void validate_element_frame( const ElementFrame& element, const NodalFrame& nodal, std::vector& diagnostics) { std::set nodal_ids; for (const NodeId node_id : nodal.node_ids) { nodal_ids.insert(node_id.value()); } std::set element_ids; for (const BeamElementFrame& beam : element.beams) { if (!element_ids.insert(beam.element.value()).second) { add_error( diagnostics, "results.duplicate_element_id", "Element frame contains duplicate element ID " + std::to_string(beam.element.value()) + "."); } if ( !is_finite(beam.local_frame.ex) || !is_finite(beam.local_frame.ey) || !is_finite(beam.local_frame.ez)) { add_error( diagnostics, "results.nonfinite_value", "Beam local frame contains a nonfinite component."); } const BeamSectionResult& first = beam.end_results[0]; const BeamSectionResult& second = beam.end_results[1]; if (first.end_node == second.end_node) { add_error( diagnostics, "results.duplicate_beam_end_node", "Beam element result contains the same node at both ends."); } if ( first.xi != -1.0 || second.xi != 1.0 || !nodal_ids.contains(first.end_node.value()) || !nodal_ids.contains(second.end_node.value())) { add_error( diagnostics, "results.invalid_beam_connectivity", "Beam end results must follow (-1, +1) connectivity and " "reference nodes in the nodal frame."); } if (first.sigma_xx.size() != second.sigma_xx.size()) { add_error( diagnostics, "results.recovery_point_count_mismatch", "Beam end results must have matching recovery-point " "counts."); } validate_beam_section_result(first, diagnostics); validate_beam_section_result(second, diagnostics); } } } // namespace Status validate_result_database(const ResultDatabase& database) { std::vector diagnostics; std::set step_names; for (const ResultStep& step : database.steps) { if (!step_names.insert(step.name).second) { add_error( diagnostics, "results.duplicate_step_name", "Result database contains duplicate step name '" + step.name + "'."); } std::set frame_times; for (const ResultFrame& frame : step.frames) { if (!std::isfinite(frame.step_time)) { add_error( diagnostics, "results.nonfinite_value", "Result frame has a nonfinite step time."); } else if (!frame_times.insert(frame.step_time).second) { add_error( diagnostics, "results.duplicate_frame_time", "Result step '" + step.name + "' contains duplicate frame time " + std::to_string(frame.step_time) + "."); } validate_nodal_frame(frame.nodal, diagnostics); validate_element_frame( frame.element, frame.nodal, diagnostics); } } return {diagnostics.empty(), std::move(diagnostics)}; } } // namespace fesa