feat(cpp-object-oriented-modular-refactoring): step 8 - element-geometry-vector3
This commit is contained in:
@@ -5,11 +5,11 @@
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
#include "fesa/math/vector3.h"
|
||||
|
||||
namespace fesa {
|
||||
namespace {
|
||||
|
||||
using Vector3 = std::array<double, 3>;
|
||||
|
||||
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};
|
||||
|
||||
@@ -25,37 +25,6 @@ struct ElementWork {
|
||||
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 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]};
|
||||
}
|
||||
|
||||
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]};
|
||||
}
|
||||
|
||||
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); });
|
||||
}
|
||||
|
||||
ShapeData ShapeDataAt(double xi, double eta) {
|
||||
ShapeData data{};
|
||||
for (std::size_t node = 0U; node < 4U; ++node) {
|
||||
@@ -73,7 +42,7 @@ 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]));
|
||||
result = result + weights[node] * values[node];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -85,7 +54,7 @@ Vector3 DerivativeSum(const std::array<double, 4>& derivatives,
|
||||
// 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]);
|
||||
relative[node] = coordinates[node] - coordinates[0];
|
||||
}
|
||||
return WeightedSum(derivatives, relative);
|
||||
}
|
||||
@@ -106,7 +75,7 @@ bool SameCoordinates(const Vector3& left, const Vector3& 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);
|
||||
return (second - first).Cross(third - first).Dot(normal);
|
||||
}
|
||||
|
||||
bool HasOppositeSigns(double first, double second) {
|
||||
@@ -197,8 +166,8 @@ Result<ShellGeometry> PreprocessShellGeometry(
|
||||
"Shell geometry references an unavailable internal node.");
|
||||
}
|
||||
current.coordinates[local_node] =
|
||||
nodes[element.node_indices[local_node]].coordinates;
|
||||
if (!IsFinite(current.coordinates[local_node])) {
|
||||
Vector3{nodes[element.node_indices[local_node]].coordinates};
|
||||
if (!current.coordinates[local_node].IsFinite()) {
|
||||
return GeometryFailure(
|
||||
"invalid-shell-geometry", element.location, "ELEMENT",
|
||||
element.source_id.source_label_text,
|
||||
@@ -222,17 +191,17 @@ Result<ShellGeometry> PreprocessShellGeometry(
|
||||
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) ||
|
||||
const Vector3 center_cross = center_xi.Cross(center_eta);
|
||||
const double center_measure = center_cross.Norm();
|
||||
if (!center_xi.IsFinite() || !center_eta.IsFinite() ||
|
||||
!center_cross.IsFinite() || !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);
|
||||
current.normal = (1.0 / center_measure) * center_cross;
|
||||
|
||||
if (SegmentsProperlyIntersect(
|
||||
current.coordinates[0], current.coordinates[1],
|
||||
@@ -253,11 +222,11 @@ Result<ShellGeometry> PreprocessShellGeometry(
|
||||
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)) {
|
||||
const Vector3 area_vector = tangent_xi.Cross(tangent_eta);
|
||||
const double measure = area_vector.Norm();
|
||||
if (!tangent_xi.IsFinite() || !tangent_eta.IsFinite() ||
|
||||
!area_vector.IsFinite() || !std::isfinite(measure) ||
|
||||
!(measure > 0.0) || !(area_vector.Dot(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 "
|
||||
@@ -272,8 +241,8 @@ Result<ShellGeometry> PreprocessShellGeometry(
|
||||
}
|
||||
current.area_weight = area_weight;
|
||||
work.push_back(current);
|
||||
geometry.element_data.push_back(
|
||||
{static_cast<EntityIndex>(element_index), current.normal, area_weight});
|
||||
geometry.element_data.push_back({static_cast<EntityIndex>(element_index),
|
||||
current.normal.Components(), area_weight});
|
||||
}
|
||||
|
||||
std::vector<std::vector<std::size_t>> incident(nodes.size());
|
||||
@@ -301,8 +270,8 @@ Result<ShellGeometry> PreprocessShellGeometry(
|
||||
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);
|
||||
const double pair_dot = work[node_incident[first]].normal.Dot(
|
||||
work[node_incident[second]].normal);
|
||||
if (!std::isfinite(pair_dot) || !(pair_dot > 0.0)) {
|
||||
return GeometryFailure("opposed-incident-normal",
|
||||
nodes[node_index].location, "NODE",
|
||||
@@ -320,52 +289,53 @@ Result<ShellGeometry> PreprocessShellGeometry(
|
||||
}
|
||||
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));
|
||||
director_sum =
|
||||
director_sum + (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) ||
|
||||
const double director_norm = director_sum.Norm();
|
||||
if (!director_sum.IsFinite() || !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 Vector3 director = (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));
|
||||
double selected_alignment = std::abs(global_axes[0].Dot(director));
|
||||
for (std::size_t axis = 1U; axis < global_axes.size(); ++axis) {
|
||||
const double alignment = std::abs(Dot(global_axes[axis], director));
|
||||
const double alignment = std::abs(global_axes[axis].Dot(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) ||
|
||||
global_axes[selected_axis] -
|
||||
global_axes[selected_axis].Dot(director) * director;
|
||||
const double tangent_norm = tangent_candidate.Norm();
|
||||
if (!tangent_candidate.IsFinite() || !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)) {
|
||||
const Vector3 tangent_a = (1.0 / tangent_norm) * tangent_candidate;
|
||||
const Vector3 tangent_b = director.Cross(tangent_a);
|
||||
if (!tangent_b.IsFinite() || !(tangent_b.Norm() > 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});
|
||||
{static_cast<EntityIndex>(node_index), director.Components(),
|
||||
tangent_a.Components(), tangent_b.Components()});
|
||||
}
|
||||
|
||||
// Build lookup only after frame storage is complete so later code never
|
||||
@@ -392,7 +362,7 @@ Result<ShellGeometry> PreprocessShellGeometry(
|
||||
"ELEMENT", element.source_id.source_label_text,
|
||||
"Shell element is missing a nodal director.");
|
||||
}
|
||||
directors[local_node] = frame->director;
|
||||
directors[local_node] = Vector3{frame->director};
|
||||
}
|
||||
|
||||
for (const auto& point : ShellGeometryValidationPoints()) {
|
||||
@@ -404,12 +374,12 @@ Result<ShellGeometry> PreprocessShellGeometry(
|
||||
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) ||
|
||||
const Vector3 area_vector = midsurface_xi.Cross(midsurface_eta);
|
||||
const double surface_measure = area_vector.Norm();
|
||||
if (!midsurface_xi.IsFinite() || !midsurface_eta.IsFinite() ||
|
||||
!area_vector.IsFinite() || !std::isfinite(surface_measure) ||
|
||||
!(surface_measure > 0.0) ||
|
||||
!(Dot(area_vector, work[element_index].normal) > 0.0)) {
|
||||
!(area_vector.Dot(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 "
|
||||
@@ -421,14 +391,14 @@ Result<ShellGeometry> PreprocessShellGeometry(
|
||||
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));
|
||||
midsurface_xi + 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);
|
||||
midsurface_eta + 0.5 * thickness * zeta * director_eta;
|
||||
const Vector3 covariant_zeta = 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) ||
|
||||
covariant_xi.Dot(covariant_eta.Cross(covariant_zeta));
|
||||
if (!covariant_xi.IsFinite() || !covariant_eta.IsFinite() ||
|
||||
!covariant_zeta.IsFinite() || !std::isfinite(jacobian) ||
|
||||
!(jacobian > 0.0)) {
|
||||
return GeometryFailure(
|
||||
"invalid-shell-jacobian", element.location, "ELEMENT",
|
||||
@@ -436,13 +406,13 @@ Result<ShellGeometry> PreprocessShellGeometry(
|
||||
"Shell Jacobian is nonfinite or nonpositive at a required point.");
|
||||
}
|
||||
const Vector3 reciprocal_xi =
|
||||
Scale(1.0 / jacobian, Cross(covariant_eta, covariant_zeta));
|
||||
(1.0 / jacobian) * covariant_eta.Cross(covariant_zeta);
|
||||
const Vector3 reciprocal_eta =
|
||||
Scale(1.0 / jacobian, Cross(covariant_zeta, covariant_xi));
|
||||
(1.0 / jacobian) * covariant_zeta.Cross(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)) {
|
||||
(1.0 / jacobian) * covariant_xi.Cross(covariant_eta);
|
||||
if (!reciprocal_xi.IsFinite() || !reciprocal_eta.IsFinite() ||
|
||||
!reciprocal_zeta.IsFinite()) {
|
||||
return GeometryFailure(
|
||||
"invalid-shell-jacobian", element.location, "ELEMENT",
|
||||
element.source_id.source_label_text,
|
||||
|
||||
Reference in New Issue
Block a user