feat(cpp-object-oriented-modular-refactoring): step 4 - model-element-google-style
This commit is contained in:
+32
-30
@@ -1,66 +1,68 @@
|
||||
#include "fesa/model/domain.hpp"
|
||||
#include "fesa/model/domain.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace fesa {
|
||||
|
||||
Result<Domain> Domain::create(ModelDefinition definition) {
|
||||
return Result<Domain>::Success(Domain{std::move(definition)});
|
||||
Result<Domain> Domain::Create(ModelDefinition definition) {
|
||||
return Result<Domain>::Success(Domain{std::move(definition)});
|
||||
}
|
||||
|
||||
const std::vector<Node>& Domain::nodes() const noexcept {
|
||||
return definition_.nodes;
|
||||
const std::vector<Node>& Domain::Nodes() const noexcept {
|
||||
return definition_.nodes;
|
||||
}
|
||||
|
||||
const std::vector<EulerBeam3DDefinition>& Domain::elements() const noexcept {
|
||||
return definition_.elements;
|
||||
const std::vector<EulerBeam3DDefinition>& Domain::Elements() const noexcept {
|
||||
return definition_.elements;
|
||||
}
|
||||
|
||||
const std::vector<Mitc4ShellDefinition>& Domain::shellElements() const noexcept {
|
||||
return definition_.shellElements;
|
||||
const std::vector<Mitc4ShellDefinition>& Domain::ShellElements()
|
||||
const noexcept {
|
||||
return definition_.shell_elements;
|
||||
}
|
||||
|
||||
const std::vector<LinearElasticMaterial>& Domain::materials() const noexcept {
|
||||
return definition_.materials;
|
||||
const std::vector<LinearElasticMaterial>& Domain::Materials() const noexcept {
|
||||
return definition_.materials;
|
||||
}
|
||||
|
||||
const std::vector<GeneralBeamSection>& Domain::sections() const noexcept {
|
||||
return definition_.sections;
|
||||
const std::vector<GeneralBeamSection>& Domain::Sections() const noexcept {
|
||||
return definition_.sections;
|
||||
}
|
||||
|
||||
const std::vector<ShellSection>& Domain::shellSections() const noexcept {
|
||||
return definition_.shellSections;
|
||||
const std::vector<ShellSection>& Domain::ShellSections() const noexcept {
|
||||
return definition_.shell_sections;
|
||||
}
|
||||
|
||||
const std::vector<ShellNodeInitialFrame>& Domain::shellNodeInitialFrames() const noexcept {
|
||||
return definition_.shellNodeInitialFrames;
|
||||
const std::vector<ShellNodeInitialFrame>& Domain::ShellNodeInitialFrames()
|
||||
const noexcept {
|
||||
return definition_.shell_node_initial_frames;
|
||||
}
|
||||
|
||||
const std::vector<NodeSet>& Domain::nodeSets() const noexcept {
|
||||
return definition_.nodeSets;
|
||||
const std::vector<NodeSet>& Domain::NodeSets() const noexcept {
|
||||
return definition_.node_sets;
|
||||
}
|
||||
|
||||
const std::vector<ElementSet>& Domain::elementSets() const noexcept {
|
||||
return definition_.elementSets;
|
||||
const std::vector<ElementSet>& Domain::ElementSets() const noexcept {
|
||||
return definition_.element_sets;
|
||||
}
|
||||
|
||||
const std::vector<StaticStepDefinition>& Domain::steps() const noexcept {
|
||||
return definition_.steps;
|
||||
const std::vector<StaticStepDefinition>& Domain::Steps() const noexcept {
|
||||
return definition_.steps;
|
||||
}
|
||||
|
||||
const std::vector<Diagnostic>& Domain::warnings() const noexcept {
|
||||
return definition_.warnings;
|
||||
const std::vector<Diagnostic>& Domain::Warnings() const noexcept {
|
||||
return definition_.warnings;
|
||||
}
|
||||
|
||||
const std::filesystem::path& Domain::sourcePath() const noexcept {
|
||||
return definition_.sourcePath;
|
||||
const std::filesystem::path& Domain::SourcePath() const noexcept {
|
||||
return definition_.source_path;
|
||||
}
|
||||
|
||||
const std::string& Domain::sourceContentIdentity() const noexcept {
|
||||
return definition_.sourceContentIdentity;
|
||||
const std::string& Domain::SourceContentIdentity() const noexcept {
|
||||
return definition_.source_content_identity;
|
||||
}
|
||||
|
||||
Domain::Domain(ModelDefinition definition)
|
||||
: definition_{std::move(definition)} {}
|
||||
|
||||
} // namespace fesa
|
||||
} // namespace fesa
|
||||
|
||||
+379
-413
@@ -1,4 +1,4 @@
|
||||
#include "fesa/model/shell_geometry.hpp"
|
||||
#include "fesa/model/shell_geometry.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
@@ -14,478 +14,444 @@ constexpr std::array<double, 4> kXiSigns{-1.0, 1.0, 1.0, -1.0};
|
||||
constexpr std::array<double, 4> kEtaSigns{-1.0, -1.0, 1.0, 1.0};
|
||||
|
||||
struct ShapeData {
|
||||
std::array<double, 4> values;
|
||||
std::array<double, 4> xiDerivatives;
|
||||
std::array<double, 4> etaDerivatives;
|
||||
std::array<double, 4> values;
|
||||
std::array<double, 4> xi_derivatives;
|
||||
std::array<double, 4> eta_derivatives;
|
||||
};
|
||||
|
||||
struct ElementWork {
|
||||
std::array<Vector3, 4> coordinates;
|
||||
Vector3 normal;
|
||||
double areaWeight;
|
||||
std::array<Vector3, 4> coordinates;
|
||||
Vector3 normal;
|
||||
double area_weight;
|
||||
};
|
||||
|
||||
Vector3 add(const Vector3& left, const Vector3& right) {
|
||||
return {
|
||||
left[0] + right[0], left[1] + right[1], left[2] + right[2]};
|
||||
Vector3 Add(const Vector3& left, const Vector3& right) {
|
||||
return {left[0] + right[0], left[1] + right[1], left[2] + right[2]};
|
||||
}
|
||||
|
||||
Vector3 subtract(const Vector3& left, const Vector3& right) {
|
||||
return {
|
||||
left[0] - right[0], left[1] - right[1], left[2] - right[2]};
|
||||
Vector3 Subtract(const Vector3& left, const Vector3& right) {
|
||||
return {left[0] - right[0], left[1] - right[1], left[2] - right[2]};
|
||||
}
|
||||
|
||||
Vector3 scale(double factor, const Vector3& value) {
|
||||
return {factor * value[0], factor * value[1], factor * value[2]};
|
||||
Vector3 Scale(double factor, const Vector3& value) {
|
||||
return {factor * value[0], factor * value[1], factor * value[2]};
|
||||
}
|
||||
|
||||
double dot(const Vector3& left, const Vector3& right) {
|
||||
return left[0] * right[0] + left[1] * right[1] + left[2] * right[2];
|
||||
double Dot(const Vector3& left, const Vector3& right) {
|
||||
return left[0] * right[0] + left[1] * right[1] + left[2] * right[2];
|
||||
}
|
||||
|
||||
Vector3 cross(const Vector3& left, const Vector3& right) {
|
||||
return {
|
||||
left[1] * right[2] - left[2] * right[1],
|
||||
left[2] * right[0] - left[0] * right[2],
|
||||
left[0] * right[1] - left[1] * right[0]};
|
||||
Vector3 Cross(const Vector3& left, const Vector3& right) {
|
||||
return {left[1] * right[2] - left[2] * right[1],
|
||||
left[2] * right[0] - left[0] * right[2],
|
||||
left[0] * right[1] - left[1] * right[0]};
|
||||
}
|
||||
|
||||
double norm(const Vector3& value) {
|
||||
return std::hypot(value[0], value[1], value[2]);
|
||||
double Norm(const Vector3& value) {
|
||||
return std::hypot(value[0], value[1], value[2]);
|
||||
}
|
||||
|
||||
bool isFinite(const Vector3& value) {
|
||||
return std::all_of(value.begin(), value.end(), [](double component) {
|
||||
return std::isfinite(component);
|
||||
});
|
||||
bool IsFinite(const Vector3& value) {
|
||||
return std::all_of(value.begin(), value.end(),
|
||||
[](double component) { return std::isfinite(component); });
|
||||
}
|
||||
|
||||
ShapeData shapeData(double xi, double eta) {
|
||||
ShapeData data{};
|
||||
for (std::size_t node = 0U; node < 4U; ++node) {
|
||||
data.values[node] =
|
||||
0.25 * (1.0 + kXiSigns[node] * xi) *
|
||||
(1.0 + kEtaSigns[node] * eta);
|
||||
data.xiDerivatives[node] =
|
||||
0.25 * kXiSigns[node] * (1.0 + kEtaSigns[node] * eta);
|
||||
data.etaDerivatives[node] =
|
||||
0.25 * kEtaSigns[node] * (1.0 + kXiSigns[node] * xi);
|
||||
}
|
||||
return data;
|
||||
ShapeData ShapeDataAt(double xi, double eta) {
|
||||
ShapeData data{};
|
||||
for (std::size_t node = 0U; node < 4U; ++node) {
|
||||
data.values[node] =
|
||||
0.25 * (1.0 + kXiSigns[node] * xi) * (1.0 + kEtaSigns[node] * eta);
|
||||
data.xi_derivatives[node] =
|
||||
0.25 * kXiSigns[node] * (1.0 + kEtaSigns[node] * eta);
|
||||
data.eta_derivatives[node] =
|
||||
0.25 * kEtaSigns[node] * (1.0 + kXiSigns[node] * xi);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
Vector3 weightedSum(
|
||||
const std::array<double, 4>& weights,
|
||||
const std::array<Vector3, 4>& values) {
|
||||
Vector3 result{};
|
||||
for (std::size_t node = 0U; node < values.size(); ++node) {
|
||||
result = add(result, scale(weights[node], values[node]));
|
||||
}
|
||||
return result;
|
||||
Vector3 WeightedSum(const std::array<double, 4>& weights,
|
||||
const std::array<Vector3, 4>& values) {
|
||||
Vector3 result{};
|
||||
for (std::size_t node = 0U; node < values.size(); ++node) {
|
||||
result = Add(result, Scale(weights[node], values[node]));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Vector3 derivativeSum(
|
||||
const std::array<double, 4>& derivatives,
|
||||
const std::array<Vector3, 4>& coordinates) {
|
||||
// Shape derivatives sum to zero, so translating by node 1 improves the
|
||||
// numerical cancellation without changing the covariant tangent.
|
||||
std::array<Vector3, 4> relative{};
|
||||
for (std::size_t node = 0U; node < coordinates.size(); ++node) {
|
||||
relative[node] = subtract(coordinates[node], coordinates[0]);
|
||||
}
|
||||
return weightedSum(derivatives, relative);
|
||||
/// @brief Forms a translation-invariant covariant tangent in node order.
|
||||
Vector3 DerivativeSum(const std::array<double, 4>& derivatives,
|
||||
const std::array<Vector3, 4>& coordinates) {
|
||||
// Shape derivatives sum to zero, so translating by node 1 improves the
|
||||
// numerical cancellation without changing the covariant tangent.
|
||||
std::array<Vector3, 4> relative{};
|
||||
for (std::size_t node = 0U; node < coordinates.size(); ++node) {
|
||||
relative[node] = Subtract(coordinates[node], coordinates[0]);
|
||||
}
|
||||
return WeightedSum(derivatives, relative);
|
||||
}
|
||||
|
||||
Result<ShellGeometry> geometryFailure(
|
||||
std::string code,
|
||||
const SourceLocation& location,
|
||||
std::string keyword,
|
||||
std::string identity,
|
||||
std::string message) {
|
||||
return Result<ShellGeometry>::Failure(Status::Failure(
|
||||
FailureCategory::kModel,
|
||||
{{Severity::kError,
|
||||
std::move(code),
|
||||
location,
|
||||
std::move(keyword),
|
||||
std::move(identity),
|
||||
std::move(message)}}));
|
||||
Result<ShellGeometry> GeometryFailure(std::string code,
|
||||
const SourceLocation& location,
|
||||
std::string keyword, std::string identity,
|
||||
std::string message) {
|
||||
return Result<ShellGeometry>::Failure(Status::Failure(
|
||||
FailureCategory::kModel,
|
||||
{{Severity::kError, std::move(code), location, std::move(keyword),
|
||||
std::move(identity), std::move(message)}}));
|
||||
}
|
||||
|
||||
bool sameCoordinates(const Vector3& left, const Vector3& right) {
|
||||
return left == right;
|
||||
bool SameCoordinates(const Vector3& left, const Vector3& right) {
|
||||
return left == right;
|
||||
}
|
||||
|
||||
double orientation(
|
||||
const Vector3& first,
|
||||
const Vector3& second,
|
||||
const Vector3& third,
|
||||
const Vector3& normal) {
|
||||
return dot(cross(subtract(second, first), subtract(third, first)), normal);
|
||||
double Orientation(const Vector3& first, const Vector3& second,
|
||||
const Vector3& third, const Vector3& normal) {
|
||||
return Dot(Cross(Subtract(second, first), Subtract(third, first)), normal);
|
||||
}
|
||||
|
||||
bool hasOppositeSigns(double first, double second) {
|
||||
return (first < 0.0 && second > 0.0) ||
|
||||
(first > 0.0 && second < 0.0);
|
||||
bool HasOppositeSigns(double first, double second) {
|
||||
return (first < 0.0 && second > 0.0) || (first > 0.0 && second < 0.0);
|
||||
}
|
||||
|
||||
bool segmentsProperlyIntersect(
|
||||
const Vector3& firstStart,
|
||||
const Vector3& firstEnd,
|
||||
const Vector3& secondStart,
|
||||
const Vector3& secondEnd,
|
||||
const Vector3& normal) {
|
||||
const double firstSideStart =
|
||||
orientation(firstStart, firstEnd, secondStart, normal);
|
||||
const double firstSideEnd =
|
||||
orientation(firstStart, firstEnd, secondEnd, normal);
|
||||
const double secondSideStart =
|
||||
orientation(secondStart, secondEnd, firstStart, normal);
|
||||
const double secondSideEnd =
|
||||
orientation(secondStart, secondEnd, firstEnd, normal);
|
||||
return hasOppositeSigns(firstSideStart, firstSideEnd) &&
|
||||
hasOppositeSigns(secondSideStart, secondSideEnd);
|
||||
/// @brief Detects a proper projected edge crossing without a tolerance clamp.
|
||||
bool SegmentsProperlyIntersect(const Vector3& first_start,
|
||||
const Vector3& first_end,
|
||||
const Vector3& second_start,
|
||||
const Vector3& second_end,
|
||||
const Vector3& normal) {
|
||||
const double first_side_start =
|
||||
Orientation(first_start, first_end, second_start, normal);
|
||||
const double first_side_end =
|
||||
Orientation(first_start, first_end, second_end, normal);
|
||||
const double second_side_start =
|
||||
Orientation(second_start, second_end, first_start, normal);
|
||||
const double second_side_end =
|
||||
Orientation(second_start, second_end, first_end, normal);
|
||||
return HasOppositeSigns(first_side_start, first_side_end) &&
|
||||
HasOppositeSigns(second_side_start, second_side_end);
|
||||
}
|
||||
|
||||
bool sourceIdentityLess(
|
||||
const Mitc4ShellDefinition& left,
|
||||
std::size_t leftIndex,
|
||||
const Mitc4ShellDefinition& right,
|
||||
std::size_t rightIndex) {
|
||||
return std::tie(
|
||||
left.sourceId.instance_name,
|
||||
left.sourceId.source_label,
|
||||
left.sourceId.source_label_text,
|
||||
leftIndex) <
|
||||
std::tie(
|
||||
right.sourceId.instance_name,
|
||||
right.sourceId.source_label,
|
||||
right.sourceId.source_label_text,
|
||||
rightIndex);
|
||||
/// @brief Orders incident shell contributions by stable source identity.
|
||||
bool SourceIdentityLess(const Mitc4ShellDefinition& left,
|
||||
std::size_t left_index,
|
||||
const Mitc4ShellDefinition& right,
|
||||
std::size_t right_index) {
|
||||
return std::tie(left.source_id.instance_name, left.source_id.source_label,
|
||||
left.source_id.source_label_text, left_index) <
|
||||
std::tie(right.source_id.instance_name, right.source_id.source_label,
|
||||
right.source_id.source_label_text, right_index);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
const std::array<ShellGeometryValidationPoint, 17>&
|
||||
shellGeometryValidationPoints() noexcept {
|
||||
static const std::array<ShellGeometryValidationPoint, 17> points = [] {
|
||||
const double g = 1.0 / std::sqrt(3.0);
|
||||
return std::array<ShellGeometryValidationPoint, 17>{
|
||||
ShellGeometryValidationPoint{
|
||||
ShellGeometryPointKind::center, 0U, {0.0, 0.0, 0.0}},
|
||||
{ShellGeometryPointKind::stiffness, 0U, {-g, -g, -g}},
|
||||
{ShellGeometryPointKind::stiffness, 1U, {-g, -g, g}},
|
||||
{ShellGeometryPointKind::stiffness, 2U, {g, -g, -g}},
|
||||
{ShellGeometryPointKind::stiffness, 3U, {g, -g, g}},
|
||||
{ShellGeometryPointKind::stiffness, 4U, {g, g, -g}},
|
||||
{ShellGeometryPointKind::stiffness, 5U, {g, g, g}},
|
||||
{ShellGeometryPointKind::stiffness, 6U, {-g, g, -g}},
|
||||
{ShellGeometryPointKind::stiffness, 7U, {-g, g, g}},
|
||||
{ShellGeometryPointKind::tying, 0U, {0.0, -1.0, 0.0}},
|
||||
{ShellGeometryPointKind::tying, 1U, {0.0, 1.0, 0.0}},
|
||||
{ShellGeometryPointKind::tying, 2U, {-1.0, 0.0, 0.0}},
|
||||
{ShellGeometryPointKind::tying, 3U, {1.0, 0.0, 0.0}},
|
||||
{ShellGeometryPointKind::recovery, 0U, {-g, -g, 0.0}},
|
||||
{ShellGeometryPointKind::recovery, 1U, {g, -g, 0.0}},
|
||||
{ShellGeometryPointKind::recovery, 2U, {g, g, 0.0}},
|
||||
{ShellGeometryPointKind::recovery, 3U, {-g, g, 0.0}}};
|
||||
}();
|
||||
return points;
|
||||
ShellGeometryValidationPoints() noexcept {
|
||||
static const std::array<ShellGeometryValidationPoint, 17> points = [] {
|
||||
const double g = 1.0 / std::sqrt(3.0);
|
||||
return std::array<ShellGeometryValidationPoint, 17>{
|
||||
ShellGeometryValidationPoint{
|
||||
ShellGeometryPointKind::kCenter, 0U, {0.0, 0.0, 0.0}},
|
||||
{ShellGeometryPointKind::kStiffness, 0U, {-g, -g, -g}},
|
||||
{ShellGeometryPointKind::kStiffness, 1U, {-g, -g, g}},
|
||||
{ShellGeometryPointKind::kStiffness, 2U, {g, -g, -g}},
|
||||
{ShellGeometryPointKind::kStiffness, 3U, {g, -g, g}},
|
||||
{ShellGeometryPointKind::kStiffness, 4U, {g, g, -g}},
|
||||
{ShellGeometryPointKind::kStiffness, 5U, {g, g, g}},
|
||||
{ShellGeometryPointKind::kStiffness, 6U, {-g, g, -g}},
|
||||
{ShellGeometryPointKind::kStiffness, 7U, {-g, g, g}},
|
||||
{ShellGeometryPointKind::kTying, 0U, {0.0, -1.0, 0.0}},
|
||||
{ShellGeometryPointKind::kTying, 1U, {0.0, 1.0, 0.0}},
|
||||
{ShellGeometryPointKind::kTying, 2U, {-1.0, 0.0, 0.0}},
|
||||
{ShellGeometryPointKind::kTying, 3U, {1.0, 0.0, 0.0}},
|
||||
{ShellGeometryPointKind::kRecovery, 0U, {-g, -g, 0.0}},
|
||||
{ShellGeometryPointKind::kRecovery, 1U, {g, -g, 0.0}},
|
||||
{ShellGeometryPointKind::kRecovery, 2U, {g, g, 0.0}},
|
||||
{ShellGeometryPointKind::kRecovery, 3U, {-g, g, 0.0}}};
|
||||
}();
|
||||
return points;
|
||||
}
|
||||
|
||||
Result<ShellGeometry> preprocessShellGeometry(
|
||||
Result<ShellGeometry> PreprocessShellGeometry(
|
||||
const std::vector<Node>& nodes,
|
||||
const std::vector<Mitc4ShellDefinition>& elements,
|
||||
const std::vector<ShellSection>& sections) {
|
||||
ShellGeometry geometry;
|
||||
geometry.elementData.reserve(elements.size());
|
||||
std::vector<ElementWork> work;
|
||||
work.reserve(elements.size());
|
||||
ShellGeometry geometry;
|
||||
geometry.element_data.reserve(elements.size());
|
||||
std::vector<ElementWork> work;
|
||||
work.reserve(elements.size());
|
||||
|
||||
const double g = 1.0 / std::sqrt(3.0);
|
||||
const std::array<Vector3, 4> surfaceGaussPoints{
|
||||
Vector3{-g, -g, 0.0},
|
||||
Vector3{g, -g, 0.0},
|
||||
Vector3{g, g, 0.0},
|
||||
Vector3{-g, g, 0.0}};
|
||||
const double g = 1.0 / std::sqrt(3.0);
|
||||
const std::array<Vector3, 4> surface_gauss_points{
|
||||
Vector3{-g, -g, 0.0}, Vector3{g, -g, 0.0}, Vector3{g, g, 0.0},
|
||||
Vector3{-g, g, 0.0}};
|
||||
|
||||
for (std::size_t elementIndex = 0U;
|
||||
elementIndex < elements.size();
|
||||
++elementIndex) {
|
||||
const auto& element = elements[elementIndex];
|
||||
ElementWork current{};
|
||||
for (std::size_t localNode = 0U; localNode < 4U; ++localNode) {
|
||||
if (element.nodeIndices[localNode] >= nodes.size()) {
|
||||
return geometryFailure(
|
||||
"invalid-shell-geometry", element.location, "ELEMENT",
|
||||
element.sourceId.source_label_text,
|
||||
"Shell geometry references an unavailable internal node.");
|
||||
}
|
||||
current.coordinates[localNode] =
|
||||
nodes[element.nodeIndices[localNode]].coordinates;
|
||||
if (!isFinite(current.coordinates[localNode])) {
|
||||
return geometryFailure(
|
||||
"invalid-shell-geometry", element.location, "ELEMENT",
|
||||
element.sourceId.source_label_text,
|
||||
"Shell geometry contains a nonfinite source coordinate.");
|
||||
}
|
||||
for (std::size_t element_index = 0U; element_index < elements.size();
|
||||
++element_index) {
|
||||
const auto& element = elements[element_index];
|
||||
ElementWork current{};
|
||||
for (std::size_t local_node = 0U; local_node < 4U; ++local_node) {
|
||||
if (element.node_indices[local_node] >= nodes.size()) {
|
||||
return GeometryFailure(
|
||||
"invalid-shell-geometry", element.location, "ELEMENT",
|
||||
element.source_id.source_label_text,
|
||||
"Shell geometry references an unavailable internal node.");
|
||||
}
|
||||
current.coordinates[local_node] =
|
||||
nodes[element.node_indices[local_node]].coordinates;
|
||||
if (!IsFinite(current.coordinates[local_node])) {
|
||||
return GeometryFailure(
|
||||
"invalid-shell-geometry", element.location, "ELEMENT",
|
||||
element.source_id.source_label_text,
|
||||
"Shell geometry contains a nonfinite source coordinate.");
|
||||
}
|
||||
}
|
||||
for (std::size_t first = 0U; first < 4U; ++first) {
|
||||
for (std::size_t second = first + 1U; second < 4U; ++second) {
|
||||
if (element.node_indices[first] == element.node_indices[second] ||
|
||||
SameCoordinates(current.coordinates[first],
|
||||
current.coordinates[second])) {
|
||||
return GeometryFailure("invalid-shell-geometry", element.location,
|
||||
"ELEMENT", element.source_id.source_label_text,
|
||||
"Shell geometry contains duplicate nodes.");
|
||||
}
|
||||
for (std::size_t first = 0U; first < 4U; ++first) {
|
||||
for (std::size_t second = first + 1U; second < 4U; ++second) {
|
||||
if (element.nodeIndices[first] == element.nodeIndices[second] ||
|
||||
sameCoordinates(
|
||||
current.coordinates[first], current.coordinates[second])) {
|
||||
return geometryFailure(
|
||||
"invalid-shell-geometry", element.location, "ELEMENT",
|
||||
element.sourceId.source_label_text,
|
||||
"Shell geometry contains duplicate nodes.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const ShapeData center = shapeData(0.0, 0.0);
|
||||
const Vector3 centerXi =
|
||||
derivativeSum(center.xiDerivatives, current.coordinates);
|
||||
const Vector3 centerEta =
|
||||
derivativeSum(center.etaDerivatives, current.coordinates);
|
||||
const Vector3 centerCross = cross(centerXi, centerEta);
|
||||
const double centerMeasure = norm(centerCross);
|
||||
if (!isFinite(centerXi) || !isFinite(centerEta) ||
|
||||
!isFinite(centerCross) || !std::isfinite(centerMeasure) ||
|
||||
!(centerMeasure > 0.0)) {
|
||||
return geometryFailure(
|
||||
"invalid-shell-geometry", element.location, "ELEMENT",
|
||||
element.sourceId.source_label_text,
|
||||
"Shell center has no finite nonzero normal candidate.");
|
||||
}
|
||||
current.normal = scale(1.0 / centerMeasure, centerCross);
|
||||
|
||||
if (segmentsProperlyIntersect(
|
||||
current.coordinates[0], current.coordinates[1],
|
||||
current.coordinates[2], current.coordinates[3], current.normal) ||
|
||||
segmentsProperlyIntersect(
|
||||
current.coordinates[1], current.coordinates[2],
|
||||
current.coordinates[3], current.coordinates[0], current.normal)) {
|
||||
return geometryFailure(
|
||||
"invalid-shell-geometry", element.location, "ELEMENT",
|
||||
element.sourceId.source_label_text,
|
||||
"Shell boundary is self-intersecting in the center-normal projection.");
|
||||
}
|
||||
|
||||
double areaWeight = 0.0;
|
||||
for (const auto& point : surfaceGaussPoints) {
|
||||
const ShapeData shape = shapeData(point[0], point[1]);
|
||||
const Vector3 tangentXi =
|
||||
derivativeSum(shape.xiDerivatives, current.coordinates);
|
||||
const Vector3 tangentEta =
|
||||
derivativeSum(shape.etaDerivatives, current.coordinates);
|
||||
const Vector3 areaVector = cross(tangentXi, tangentEta);
|
||||
const double measure = norm(areaVector);
|
||||
if (!isFinite(tangentXi) || !isFinite(tangentEta) ||
|
||||
!isFinite(areaVector) || !std::isfinite(measure) ||
|
||||
!(measure > 0.0) || !(dot(areaVector, current.normal) > 0.0)) {
|
||||
return geometryFailure(
|
||||
"invalid-shell-geometry", element.location, "ELEMENT",
|
||||
element.sourceId.source_label_text,
|
||||
"Shell surface is zero-area or locally reversed at a required point.");
|
||||
}
|
||||
areaWeight += measure;
|
||||
}
|
||||
if (!std::isfinite(areaWeight) || !(areaWeight > 0.0)) {
|
||||
return geometryFailure(
|
||||
"invalid-shell-geometry", element.location, "ELEMENT",
|
||||
element.sourceId.source_label_text,
|
||||
"Shell surface-area weight is nonfinite or zero.");
|
||||
}
|
||||
current.areaWeight = areaWeight;
|
||||
work.push_back(current);
|
||||
geometry.elementData.push_back({
|
||||
static_cast<EntityIndex>(elementIndex), current.normal, areaWeight});
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::vector<std::size_t>> incident(nodes.size());
|
||||
for (std::size_t elementIndex = 0U;
|
||||
elementIndex < elements.size();
|
||||
++elementIndex) {
|
||||
for (const EntityIndex nodeIndex : elements[elementIndex].nodeIndices) {
|
||||
incident[nodeIndex].push_back(elementIndex);
|
||||
}
|
||||
const ShapeData center = ShapeDataAt(0.0, 0.0);
|
||||
const Vector3 center_xi =
|
||||
DerivativeSum(center.xi_derivatives, current.coordinates);
|
||||
const Vector3 center_eta =
|
||||
DerivativeSum(center.eta_derivatives, current.coordinates);
|
||||
const Vector3 center_cross = Cross(center_xi, center_eta);
|
||||
const double center_measure = Norm(center_cross);
|
||||
if (!IsFinite(center_xi) || !IsFinite(center_eta) ||
|
||||
!IsFinite(center_cross) || !std::isfinite(center_measure) ||
|
||||
!(center_measure > 0.0)) {
|
||||
return GeometryFailure(
|
||||
"invalid-shell-geometry", element.location, "ELEMENT",
|
||||
element.source_id.source_label_text,
|
||||
"Shell center has no finite nonzero normal candidate.");
|
||||
}
|
||||
current.normal = Scale(1.0 / center_measure, center_cross);
|
||||
|
||||
if (SegmentsProperlyIntersect(
|
||||
current.coordinates[0], current.coordinates[1],
|
||||
current.coordinates[2], current.coordinates[3], current.normal) ||
|
||||
SegmentsProperlyIntersect(
|
||||
current.coordinates[1], current.coordinates[2],
|
||||
current.coordinates[3], current.coordinates[0], current.normal)) {
|
||||
return GeometryFailure("invalid-shell-geometry", element.location,
|
||||
"ELEMENT", element.source_id.source_label_text,
|
||||
"Shell boundary is self-intersecting in the "
|
||||
"center-normal projection.");
|
||||
}
|
||||
|
||||
geometry.nodalFrames.reserve(nodes.size());
|
||||
std::vector<const ShellNodeInitialFrame*> frameByNode(nodes.size(), nullptr);
|
||||
for (std::size_t nodeIndex = 0U; nodeIndex < incident.size(); ++nodeIndex) {
|
||||
auto& nodeIncident = incident[nodeIndex];
|
||||
if (nodeIncident.empty()) {
|
||||
continue;
|
||||
}
|
||||
std::sort(
|
||||
nodeIncident.begin(), nodeIncident.end(),
|
||||
[&elements](std::size_t left, std::size_t right) {
|
||||
return sourceIdentityLess(
|
||||
elements[left], left, elements[right], right);
|
||||
});
|
||||
for (std::size_t first = 0U; first < nodeIncident.size(); ++first) {
|
||||
for (std::size_t second = first + 1U;
|
||||
second < nodeIncident.size();
|
||||
++second) {
|
||||
const double pairDot = dot(
|
||||
work[nodeIncident[first]].normal,
|
||||
work[nodeIncident[second]].normal);
|
||||
if (!std::isfinite(pairDot) || !(pairDot > 0.0)) {
|
||||
return geometryFailure(
|
||||
"opposed-incident-normal", nodes[nodeIndex].location,
|
||||
"NODE", nodes[nodeIndex].sourceId.source_label_text,
|
||||
"Incident shell normal candidates do not share a positive orientation hemisphere.");
|
||||
}
|
||||
}
|
||||
}
|
||||
double area_weight = 0.0;
|
||||
for (const auto& point : surface_gauss_points) {
|
||||
const ShapeData shape = ShapeDataAt(point[0], point[1]);
|
||||
const Vector3 tangent_xi =
|
||||
DerivativeSum(shape.xi_derivatives, current.coordinates);
|
||||
const Vector3 tangent_eta =
|
||||
DerivativeSum(shape.eta_derivatives, current.coordinates);
|
||||
const Vector3 area_vector = Cross(tangent_xi, tangent_eta);
|
||||
const double measure = Norm(area_vector);
|
||||
if (!IsFinite(tangent_xi) || !IsFinite(tangent_eta) ||
|
||||
!IsFinite(area_vector) || !std::isfinite(measure) ||
|
||||
!(measure > 0.0) || !(Dot(area_vector, current.normal) > 0.0)) {
|
||||
return GeometryFailure("invalid-shell-geometry", element.location,
|
||||
"ELEMENT", element.source_id.source_label_text,
|
||||
"Shell surface is zero-area or locally reversed "
|
||||
"at a required point.");
|
||||
}
|
||||
area_weight += measure;
|
||||
}
|
||||
if (!std::isfinite(area_weight) || !(area_weight > 0.0)) {
|
||||
return GeometryFailure("invalid-shell-geometry", element.location,
|
||||
"ELEMENT", element.source_id.source_label_text,
|
||||
"Shell surface-area weight is nonfinite or zero.");
|
||||
}
|
||||
current.area_weight = area_weight;
|
||||
work.push_back(current);
|
||||
geometry.element_data.push_back(
|
||||
{static_cast<EntityIndex>(element_index), current.normal, area_weight});
|
||||
}
|
||||
|
||||
double maximumWeight = 0.0;
|
||||
for (const std::size_t elementIndex : nodeIncident) {
|
||||
maximumWeight = std::max(maximumWeight, work[elementIndex].areaWeight);
|
||||
}
|
||||
Vector3 directorSum{};
|
||||
for (const std::size_t elementIndex : nodeIncident) {
|
||||
directorSum = add(
|
||||
directorSum,
|
||||
scale(
|
||||
work[elementIndex].areaWeight / maximumWeight,
|
||||
work[elementIndex].normal));
|
||||
}
|
||||
const double directorNorm = norm(directorSum);
|
||||
if (!isFinite(directorSum) || !std::isfinite(directorNorm) ||
|
||||
!(directorNorm > 0.0)) {
|
||||
return geometryFailure(
|
||||
"invalid-shell-director", nodes[nodeIndex].location, "NODE",
|
||||
nodes[nodeIndex].sourceId.source_label_text,
|
||||
"Area-weighted shell director is nonfinite or zero.");
|
||||
}
|
||||
const Vector3 director = scale(1.0 / directorNorm, directorSum);
|
||||
std::vector<std::vector<std::size_t>> incident(nodes.size());
|
||||
for (std::size_t element_index = 0U; element_index < elements.size();
|
||||
++element_index) {
|
||||
for (const EntityIndex node_index : elements[element_index].node_indices) {
|
||||
incident[node_index].push_back(element_index);
|
||||
}
|
||||
}
|
||||
|
||||
const std::array<Vector3, 3> globalAxes{
|
||||
Vector3{1.0, 0.0, 0.0},
|
||||
Vector3{0.0, 1.0, 0.0},
|
||||
Vector3{0.0, 0.0, 1.0}};
|
||||
std::size_t selectedAxis = 0U;
|
||||
double selectedAlignment = std::abs(dot(globalAxes[0], director));
|
||||
for (std::size_t axis = 1U; axis < globalAxes.size(); ++axis) {
|
||||
const double alignment = std::abs(dot(globalAxes[axis], director));
|
||||
if (alignment < selectedAlignment) {
|
||||
selectedAlignment = alignment;
|
||||
selectedAxis = axis;
|
||||
}
|
||||
geometry.nodal_frames.reserve(nodes.size());
|
||||
std::vector<const ShellNodeInitialFrame*> frame_by_node(nodes.size(),
|
||||
nullptr);
|
||||
for (std::size_t node_index = 0U; node_index < incident.size();
|
||||
++node_index) {
|
||||
auto& node_incident = incident[node_index];
|
||||
if (node_incident.empty()) {
|
||||
continue;
|
||||
}
|
||||
std::sort(node_incident.begin(), node_incident.end(),
|
||||
[&elements](std::size_t left, std::size_t right) {
|
||||
return SourceIdentityLess(elements[left], left, elements[right],
|
||||
right);
|
||||
});
|
||||
for (std::size_t first = 0U; first < node_incident.size(); ++first) {
|
||||
for (std::size_t second = first + 1U; second < node_incident.size();
|
||||
++second) {
|
||||
const double pair_dot = Dot(work[node_incident[first]].normal,
|
||||
work[node_incident[second]].normal);
|
||||
if (!std::isfinite(pair_dot) || !(pair_dot > 0.0)) {
|
||||
return GeometryFailure("opposed-incident-normal",
|
||||
nodes[node_index].location, "NODE",
|
||||
nodes[node_index].source_id.source_label_text,
|
||||
"Incident shell normal candidates do not "
|
||||
"share a positive orientation hemisphere.");
|
||||
}
|
||||
const Vector3 tangentCandidate = subtract(
|
||||
globalAxes[selectedAxis],
|
||||
scale(dot(globalAxes[selectedAxis], director), director));
|
||||
const double tangentNorm = norm(tangentCandidate);
|
||||
if (!isFinite(tangentCandidate) || !std::isfinite(tangentNorm) ||
|
||||
!(tangentNorm > 0.0)) {
|
||||
return geometryFailure(
|
||||
"invalid-shell-director", nodes[nodeIndex].location, "NODE",
|
||||
nodes[nodeIndex].sourceId.source_label_text,
|
||||
"Least-aligned-axis tangent frame construction failed.");
|
||||
}
|
||||
const Vector3 tangentA = scale(1.0 / tangentNorm, tangentCandidate);
|
||||
const Vector3 tangentB = cross(director, tangentA);
|
||||
if (!isFinite(tangentB) || !(norm(tangentB) > 0.0)) {
|
||||
return geometryFailure(
|
||||
"invalid-shell-director", nodes[nodeIndex].location, "NODE",
|
||||
nodes[nodeIndex].sourceId.source_label_text,
|
||||
"Right-handed shell tangent frame construction failed.");
|
||||
}
|
||||
geometry.nodalFrames.push_back({
|
||||
static_cast<EntityIndex>(nodeIndex), director, tangentA, tangentB});
|
||||
}
|
||||
}
|
||||
|
||||
// Build lookup only after frame storage is complete so later code never
|
||||
// observes a pointer invalidated by vector growth.
|
||||
for (const auto& frame : geometry.nodalFrames) {
|
||||
frameByNode[frame.nodeIndex] = &frame;
|
||||
double maximum_weight = 0.0;
|
||||
for (const std::size_t element_index : node_incident) {
|
||||
maximum_weight =
|
||||
std::max(maximum_weight, work[element_index].area_weight);
|
||||
}
|
||||
Vector3 director_sum{};
|
||||
for (const std::size_t element_index : node_incident) {
|
||||
director_sum = Add(director_sum,
|
||||
Scale(work[element_index].area_weight / maximum_weight,
|
||||
work[element_index].normal));
|
||||
}
|
||||
const double director_norm = Norm(director_sum);
|
||||
if (!IsFinite(director_sum) || !std::isfinite(director_norm) ||
|
||||
!(director_norm > 0.0)) {
|
||||
return GeometryFailure(
|
||||
"invalid-shell-director", nodes[node_index].location, "NODE",
|
||||
nodes[node_index].source_id.source_label_text,
|
||||
"Area-weighted shell director is nonfinite or zero.");
|
||||
}
|
||||
const Vector3 director = Scale(1.0 / director_norm, director_sum);
|
||||
|
||||
const std::array<Vector3, 3> global_axes{
|
||||
Vector3{1.0, 0.0, 0.0}, Vector3{0.0, 1.0, 0.0}, Vector3{0.0, 0.0, 1.0}};
|
||||
std::size_t selected_axis = 0U;
|
||||
double selected_alignment = std::abs(Dot(global_axes[0], director));
|
||||
for (std::size_t axis = 1U; axis < global_axes.size(); ++axis) {
|
||||
const double alignment = std::abs(Dot(global_axes[axis], director));
|
||||
if (alignment < selected_alignment) {
|
||||
selected_alignment = alignment;
|
||||
selected_axis = axis;
|
||||
}
|
||||
}
|
||||
const Vector3 tangent_candidate =
|
||||
Subtract(global_axes[selected_axis],
|
||||
Scale(Dot(global_axes[selected_axis], director), director));
|
||||
const double tangent_norm = Norm(tangent_candidate);
|
||||
if (!IsFinite(tangent_candidate) || !std::isfinite(tangent_norm) ||
|
||||
!(tangent_norm > 0.0)) {
|
||||
return GeometryFailure(
|
||||
"invalid-shell-director", nodes[node_index].location, "NODE",
|
||||
nodes[node_index].source_id.source_label_text,
|
||||
"Least-aligned-axis tangent frame construction failed.");
|
||||
}
|
||||
const Vector3 tangent_a = Scale(1.0 / tangent_norm, tangent_candidate);
|
||||
const Vector3 tangent_b = Cross(director, tangent_a);
|
||||
if (!IsFinite(tangent_b) || !(Norm(tangent_b) > 0.0)) {
|
||||
return GeometryFailure(
|
||||
"invalid-shell-director", nodes[node_index].location, "NODE",
|
||||
nodes[node_index].source_id.source_label_text,
|
||||
"Right-handed shell tangent frame construction failed.");
|
||||
}
|
||||
geometry.nodal_frames.push_back(
|
||||
{static_cast<EntityIndex>(node_index), director, tangent_a, tangent_b});
|
||||
}
|
||||
|
||||
// Build lookup only after frame storage is complete so later code never
|
||||
// observes a pointer invalidated by vector growth.
|
||||
for (const auto& frame : geometry.nodal_frames) {
|
||||
frame_by_node[frame.node_index] = &frame;
|
||||
}
|
||||
|
||||
for (std::size_t element_index = 0U; element_index < elements.size();
|
||||
++element_index) {
|
||||
const auto& element = elements[element_index];
|
||||
if (element.section_index >= sections.size()) {
|
||||
return GeometryFailure("invalid-shell-jacobian", element.location,
|
||||
"ELEMENT", element.source_id.source_label_text,
|
||||
"Shell geometry cannot resolve its thickness for "
|
||||
"Jacobian validation.");
|
||||
}
|
||||
const double thickness = sections[element.section_index].thickness;
|
||||
std::array<Vector3, 4> directors{};
|
||||
for (std::size_t local_node = 0U; local_node < 4U; ++local_node) {
|
||||
const auto* frame = frame_by_node[element.node_indices[local_node]];
|
||||
if (frame == nullptr) {
|
||||
return GeometryFailure("invalid-shell-director", element.location,
|
||||
"ELEMENT", element.source_id.source_label_text,
|
||||
"Shell element is missing a nodal director.");
|
||||
}
|
||||
directors[local_node] = frame->director;
|
||||
}
|
||||
|
||||
for (std::size_t elementIndex = 0U;
|
||||
elementIndex < elements.size();
|
||||
++elementIndex) {
|
||||
const auto& element = elements[elementIndex];
|
||||
if (element.sectionIndex >= sections.size()) {
|
||||
return geometryFailure(
|
||||
"invalid-shell-jacobian", element.location, "ELEMENT",
|
||||
element.sourceId.source_label_text,
|
||||
"Shell geometry cannot resolve its thickness for Jacobian validation.");
|
||||
}
|
||||
const double thickness = sections[element.sectionIndex].thickness;
|
||||
std::array<Vector3, 4> directors{};
|
||||
for (std::size_t localNode = 0U; localNode < 4U; ++localNode) {
|
||||
const auto* frame = frameByNode[element.nodeIndices[localNode]];
|
||||
if (frame == nullptr) {
|
||||
return geometryFailure(
|
||||
"invalid-shell-director", element.location, "ELEMENT",
|
||||
element.sourceId.source_label_text,
|
||||
"Shell element is missing a nodal director.");
|
||||
}
|
||||
directors[localNode] = frame->director;
|
||||
}
|
||||
for (const auto& point : ShellGeometryValidationPoints()) {
|
||||
const double xi = point.natural_coordinates[0];
|
||||
const double eta = point.natural_coordinates[1];
|
||||
const double zeta = point.natural_coordinates[2];
|
||||
const ShapeData shape = ShapeDataAt(xi, eta);
|
||||
const Vector3 midsurface_xi =
|
||||
DerivativeSum(shape.xi_derivatives, work[element_index].coordinates);
|
||||
const Vector3 midsurface_eta =
|
||||
DerivativeSum(shape.eta_derivatives, work[element_index].coordinates);
|
||||
const Vector3 area_vector = Cross(midsurface_xi, midsurface_eta);
|
||||
const double surface_measure = Norm(area_vector);
|
||||
if (!IsFinite(midsurface_xi) || !IsFinite(midsurface_eta) ||
|
||||
!IsFinite(area_vector) || !std::isfinite(surface_measure) ||
|
||||
!(surface_measure > 0.0) ||
|
||||
!(Dot(area_vector, work[element_index].normal) > 0.0)) {
|
||||
return GeometryFailure("invalid-shell-geometry", element.location,
|
||||
"ELEMENT", element.source_id.source_label_text,
|
||||
"Shell surface basis is nonfinite, zero, or "
|
||||
"reversed at a required point.");
|
||||
}
|
||||
|
||||
for (const auto& point : shellGeometryValidationPoints()) {
|
||||
const double xi = point.naturalCoordinates[0];
|
||||
const double eta = point.naturalCoordinates[1];
|
||||
const double zeta = point.naturalCoordinates[2];
|
||||
const ShapeData shape = shapeData(xi, eta);
|
||||
const Vector3 midsurfaceXi =
|
||||
derivativeSum(shape.xiDerivatives, work[elementIndex].coordinates);
|
||||
const Vector3 midsurfaceEta =
|
||||
derivativeSum(shape.etaDerivatives, work[elementIndex].coordinates);
|
||||
const Vector3 areaVector = cross(midsurfaceXi, midsurfaceEta);
|
||||
const double surfaceMeasure = norm(areaVector);
|
||||
if (!isFinite(midsurfaceXi) || !isFinite(midsurfaceEta) ||
|
||||
!isFinite(areaVector) || !std::isfinite(surfaceMeasure) ||
|
||||
!(surfaceMeasure > 0.0) ||
|
||||
!(dot(areaVector, work[elementIndex].normal) > 0.0)) {
|
||||
return geometryFailure(
|
||||
"invalid-shell-geometry", element.location, "ELEMENT",
|
||||
element.sourceId.source_label_text,
|
||||
"Shell surface basis is nonfinite, zero, or reversed at a required point.");
|
||||
}
|
||||
|
||||
const Vector3 directorXi =
|
||||
weightedSum(shape.xiDerivatives, directors);
|
||||
const Vector3 directorEta =
|
||||
weightedSum(shape.etaDerivatives, directors);
|
||||
const Vector3 directorValue = weightedSum(shape.values, directors);
|
||||
const Vector3 covariantXi = add(
|
||||
midsurfaceXi, scale(0.5 * thickness * zeta, directorXi));
|
||||
const Vector3 covariantEta = add(
|
||||
midsurfaceEta, scale(0.5 * thickness * zeta, directorEta));
|
||||
const Vector3 covariantZeta = scale(0.5 * thickness, directorValue);
|
||||
const double jacobian =
|
||||
dot(covariantXi, cross(covariantEta, covariantZeta));
|
||||
if (!isFinite(covariantXi) || !isFinite(covariantEta) ||
|
||||
!isFinite(covariantZeta) || !std::isfinite(jacobian) ||
|
||||
!(jacobian > 0.0)) {
|
||||
return geometryFailure(
|
||||
"invalid-shell-jacobian", element.location, "ELEMENT",
|
||||
element.sourceId.source_label_text,
|
||||
"Shell Jacobian is nonfinite or nonpositive at a required point.");
|
||||
}
|
||||
const Vector3 reciprocalXi =
|
||||
scale(1.0 / jacobian, cross(covariantEta, covariantZeta));
|
||||
const Vector3 reciprocalEta =
|
||||
scale(1.0 / jacobian, cross(covariantZeta, covariantXi));
|
||||
const Vector3 reciprocalZeta =
|
||||
scale(1.0 / jacobian, cross(covariantXi, covariantEta));
|
||||
if (!isFinite(reciprocalXi) || !isFinite(reciprocalEta) ||
|
||||
!isFinite(reciprocalZeta)) {
|
||||
return geometryFailure(
|
||||
"invalid-shell-jacobian", element.location, "ELEMENT",
|
||||
element.sourceId.source_label_text,
|
||||
"Shell reciprocal basis is nonfinite at a required point.");
|
||||
}
|
||||
}
|
||||
const Vector3 director_xi = WeightedSum(shape.xi_derivatives, directors);
|
||||
const Vector3 director_eta =
|
||||
WeightedSum(shape.eta_derivatives, directors);
|
||||
const Vector3 director_value = WeightedSum(shape.values, directors);
|
||||
const Vector3 covariant_xi =
|
||||
Add(midsurface_xi, Scale(0.5 * thickness * zeta, director_xi));
|
||||
const Vector3 covariant_eta =
|
||||
Add(midsurface_eta, Scale(0.5 * thickness * zeta, director_eta));
|
||||
const Vector3 covariant_zeta = Scale(0.5 * thickness, director_value);
|
||||
const double jacobian =
|
||||
Dot(covariant_xi, Cross(covariant_eta, covariant_zeta));
|
||||
if (!IsFinite(covariant_xi) || !IsFinite(covariant_eta) ||
|
||||
!IsFinite(covariant_zeta) || !std::isfinite(jacobian) ||
|
||||
!(jacobian > 0.0)) {
|
||||
return GeometryFailure(
|
||||
"invalid-shell-jacobian", element.location, "ELEMENT",
|
||||
element.source_id.source_label_text,
|
||||
"Shell Jacobian is nonfinite or nonpositive at a required point.");
|
||||
}
|
||||
const Vector3 reciprocal_xi =
|
||||
Scale(1.0 / jacobian, Cross(covariant_eta, covariant_zeta));
|
||||
const Vector3 reciprocal_eta =
|
||||
Scale(1.0 / jacobian, Cross(covariant_zeta, covariant_xi));
|
||||
const Vector3 reciprocal_zeta =
|
||||
Scale(1.0 / jacobian, Cross(covariant_xi, covariant_eta));
|
||||
if (!IsFinite(reciprocal_xi) || !IsFinite(reciprocal_eta) ||
|
||||
!IsFinite(reciprocal_zeta)) {
|
||||
return GeometryFailure(
|
||||
"invalid-shell-jacobian", element.location, "ELEMENT",
|
||||
element.source_id.source_label_text,
|
||||
"Shell reciprocal basis is nonfinite at a required point.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Result<ShellGeometry>::Success(std::move(geometry));
|
||||
return Result<ShellGeometry>::Success(std::move(geometry));
|
||||
}
|
||||
|
||||
} // namespace fesa
|
||||
} // namespace fesa
|
||||
|
||||
Reference in New Issue
Block a user