feat(cpp-object-oriented-modular-refactoring): step 8 - element-geometry-vector3
This commit is contained in:
@@ -10,8 +10,6 @@
|
||||
namespace fesa {
|
||||
namespace {
|
||||
|
||||
using Vector3 = std::array<double, 3>;
|
||||
|
||||
constexpr std::size_t kNodeCount = 4U;
|
||||
constexpr std::size_t kPhysicalDofsPerNode = 5U;
|
||||
constexpr std::size_t kGlobalDofsPerNode = 6U;
|
||||
@@ -20,46 +18,11 @@ constexpr std::size_t kGlobalDofCount = 24U;
|
||||
constexpr double kShearCorrection = 5.0 / 6.0;
|
||||
constexpr double kFrameTolerance = 1.0e-12;
|
||||
|
||||
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); });
|
||||
}
|
||||
|
||||
Vector3 Normalized(const Vector3& value) {
|
||||
return Scale(1.0 / Norm(value), value);
|
||||
}
|
||||
|
||||
Vector3 WeightedSum(const std::array<double, kNodeCount>& weights,
|
||||
const std::array<Vector3, kNodeCount>& values) {
|
||||
Vector3 result{};
|
||||
for (std::size_t node = 0U; node < kNodeCount; ++node) {
|
||||
result = Add(result, Scale(weights[node], values[node]));
|
||||
result = result + weights[node] * values[node];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -68,7 +31,7 @@ Vector3 DerivativeSum(const std::array<double, kNodeCount>& derivatives,
|
||||
const std::array<Vector3, kNodeCount>& values) {
|
||||
std::array<Vector3, kNodeCount> relative{};
|
||||
for (std::size_t node = 0U; node < kNodeCount; ++node) {
|
||||
relative[node] = Subtract(values[node], values[0]);
|
||||
relative[node] = values[node] - values[0];
|
||||
}
|
||||
return WeightedSum(derivatives, relative);
|
||||
}
|
||||
@@ -86,18 +49,18 @@ std::array<Vector3, kNodeCount> NodalTangentsA(
|
||||
std::array<Vector3, kNodeCount> tangents{};
|
||||
for (std::size_t node = 0U; node < kNodeCount; ++node) {
|
||||
std::size_t selected = 0U;
|
||||
double alignment = std::abs(Dot(global_axes[0], directors[node]));
|
||||
double alignment = std::abs(global_axes[0].Dot(directors[node]));
|
||||
for (std::size_t axis = 1U; axis < global_axes.size(); ++axis) {
|
||||
const double candidate =
|
||||
std::abs(Dot(global_axes[axis], directors[node]));
|
||||
const double candidate = std::abs(global_axes[axis].Dot(directors[node]));
|
||||
if (candidate < alignment) {
|
||||
selected = axis;
|
||||
alignment = candidate;
|
||||
}
|
||||
}
|
||||
tangents[node] = Normalized(Subtract(
|
||||
global_axes[selected],
|
||||
Scale(Dot(global_axes[selected], directors[node]), directors[node])));
|
||||
const Vector3 tangent_candidate =
|
||||
global_axes[selected] -
|
||||
global_axes[selected].Dot(directors[node]) * directors[node];
|
||||
tangents[node] = (1.0 / tangent_candidate.Norm()) * tangent_candidate;
|
||||
}
|
||||
return tangents;
|
||||
}
|
||||
@@ -108,7 +71,7 @@ std::array<Vector3, kNodeCount> NodalTangentsB(
|
||||
const std::array<Vector3, kNodeCount>& tangent_a) {
|
||||
std::array<Vector3, kNodeCount> tangents{};
|
||||
for (std::size_t node = 0U; node < kNodeCount; ++node) {
|
||||
tangents[node] = Cross(directors[node], tangent_a[node]);
|
||||
tangents[node] = directors[node].Cross(tangent_a[node]);
|
||||
}
|
||||
return tangents;
|
||||
}
|
||||
@@ -144,9 +107,8 @@ std::array<std::array<double, 3>, 3> CovariantStrainColumn(
|
||||
std::array<std::array<double, 3>, 3> strain{};
|
||||
for (std::size_t first = 0U; first < 3U; ++first) {
|
||||
for (std::size_t second = 0U; second < 3U; ++second) {
|
||||
strain[first][second] =
|
||||
0.5 * (Dot(covariant[first], derivatives[second]) +
|
||||
Dot(covariant[second], derivatives[first]));
|
||||
strain[first][second] = 0.5 * (covariant[first].Dot(derivatives[second]) +
|
||||
covariant[second].Dot(derivatives[first]));
|
||||
}
|
||||
}
|
||||
// Thickness stretch is excluded from the five-component shell law.
|
||||
@@ -190,11 +152,13 @@ double FrameComponent(const Vector3& left,
|
||||
std::array<double, 5> LocalEngineeringComponents(
|
||||
const std::array<std::array<double, 3>, 3>& tensor,
|
||||
const Mitc4LocalFrame& frame) {
|
||||
return {FrameComponent(frame.e1, tensor, frame.e1),
|
||||
FrameComponent(frame.e2, tensor, frame.e2),
|
||||
2.0 * FrameComponent(frame.e1, tensor, frame.e2),
|
||||
2.0 * FrameComponent(frame.e1, tensor, frame.e3),
|
||||
2.0 * FrameComponent(frame.e2, tensor, frame.e3)};
|
||||
const Vector3 e1{frame.e1};
|
||||
const Vector3 e2{frame.e2};
|
||||
const Vector3 e3{frame.e3};
|
||||
return {FrameComponent(e1, tensor, e1), FrameComponent(e2, tensor, e2),
|
||||
2.0 * FrameComponent(e1, tensor, e2),
|
||||
2.0 * FrameComponent(e1, tensor, e3),
|
||||
2.0 * FrameComponent(e2, tensor, e3)};
|
||||
}
|
||||
|
||||
Matrix ScaledMatrix(const Matrix& source, double factor) {
|
||||
@@ -287,8 +251,8 @@ Result<Mitc4Shell> Mitc4Shell::Create(
|
||||
|
||||
std::array<Vector3, kNodeCount> coordinates{};
|
||||
for (std::size_t node = 0U; node < kNodeCount; ++node) {
|
||||
coordinates[node] = nodes[node]->coordinates;
|
||||
if (!IsFinite(coordinates[node])) {
|
||||
coordinates[node] = Vector3{nodes[node]->coordinates};
|
||||
if (!coordinates[node].IsFinite()) {
|
||||
return ModelFailure("invalid-shell-geometry", nodes[node]->location,
|
||||
identity, "MITC4 node coordinates must be finite.");
|
||||
}
|
||||
@@ -301,9 +265,12 @@ Result<Mitc4Shell> Mitc4Shell::Create(
|
||||
}
|
||||
}
|
||||
|
||||
const std::array<Vector3, kNodeCount> directors{
|
||||
Vector3{initial_directors[0U]}, Vector3{initial_directors[1U]},
|
||||
Vector3{initial_directors[2U]}, Vector3{initial_directors[3U]}};
|
||||
for (std::size_t node = 0U; node < kNodeCount; ++node) {
|
||||
const double director_norm = Norm(initial_directors[node]);
|
||||
if (!IsFinite(initial_directors[node]) || !std::isfinite(director_norm) ||
|
||||
const double director_norm = directors[node].Norm();
|
||||
if (!directors[node].IsFinite() || !std::isfinite(director_norm) ||
|
||||
std::abs(director_norm - 1.0) > kFrameTolerance) {
|
||||
return ModelFailure(
|
||||
"invalid-shell-director", nodes[node]->location, identity,
|
||||
@@ -329,28 +296,28 @@ Result<Mitc4Shell> Mitc4Shell::Create(
|
||||
DerivativeSum(center_shape.xi_derivatives, coordinates);
|
||||
const Vector3 center_eta =
|
||||
DerivativeSum(center_shape.eta_derivatives, coordinates);
|
||||
const Vector3 center_area = Cross(center_xi, center_eta);
|
||||
const double center_measure = Norm(center_area);
|
||||
if (!IsFinite(center_area) || !std::isfinite(center_measure) ||
|
||||
const Vector3 center_area = center_xi.Cross(center_eta);
|
||||
const double center_measure = center_area.Norm();
|
||||
if (!center_area.IsFinite() || !std::isfinite(center_measure) ||
|
||||
!(center_measure > 0.0)) {
|
||||
return ModelFailure(
|
||||
"invalid-shell-geometry", nodes[0]->location, identity,
|
||||
"MITC4 center surface basis must be finite and nonzero.");
|
||||
}
|
||||
const Vector3 normal_candidate = Scale(1.0 / center_measure, center_area);
|
||||
if (std::any_of(initial_directors.begin(), initial_directors.end(),
|
||||
const Vector3 normal_candidate = (1.0 / center_measure) * center_area;
|
||||
if (std::any_of(directors.begin(), directors.end(),
|
||||
[&normal_candidate](const Vector3& director) {
|
||||
return !(Dot(normal_candidate, director) > 0.0);
|
||||
return !(normal_candidate.Dot(director) > 0.0);
|
||||
})) {
|
||||
return ModelFailure(
|
||||
"invalid-shell-director", nodes[0]->location, identity,
|
||||
"MITC4 directors must follow the source-order positive face.");
|
||||
}
|
||||
|
||||
const auto tangent_a = NodalTangentsA(initial_directors);
|
||||
const auto tangent_b = NodalTangentsB(initial_directors, tangent_a);
|
||||
const auto tangent_a = NodalTangentsA(directors);
|
||||
const auto tangent_b = NodalTangentsB(directors, tangent_a);
|
||||
Mitc4Shell shell{coordinates,
|
||||
initial_directors,
|
||||
directors,
|
||||
tangent_a,
|
||||
tangent_b,
|
||||
normal_candidate,
|
||||
@@ -805,48 +772,47 @@ bool Mitc4Shell::EvaluateGeometry(double xi, double eta, double zeta,
|
||||
const Vector3 director_value = WeightedSum(shape.values, directors_);
|
||||
const double half_thickness = 0.5 * thickness_;
|
||||
|
||||
result.covariant[0] =
|
||||
Add(midsurface_xi, Scale(half_thickness * zeta, director_xi));
|
||||
result.covariant[1] =
|
||||
Add(midsurface_eta, Scale(half_thickness * zeta, director_eta));
|
||||
result.covariant[2] = Scale(half_thickness, director_value);
|
||||
result.covariant[0] = midsurface_xi + half_thickness * zeta * director_xi;
|
||||
result.covariant[1] = midsurface_eta + half_thickness * zeta * director_eta;
|
||||
result.covariant[2] = half_thickness * director_value;
|
||||
result.jacobian =
|
||||
Dot(result.covariant[0], Cross(result.covariant[1], result.covariant[2]));
|
||||
if (!IsFinite(result.covariant[0]) || !IsFinite(result.covariant[1]) ||
|
||||
!IsFinite(result.covariant[2]) || !std::isfinite(result.jacobian) ||
|
||||
result.covariant[0].Dot(result.covariant[1].Cross(result.covariant[2]));
|
||||
if (!result.covariant[0].IsFinite() || !result.covariant[1].IsFinite() ||
|
||||
!result.covariant[2].IsFinite() || !std::isfinite(result.jacobian) ||
|
||||
!(result.jacobian > 0.0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
result.reciprocal[0] = Scale(1.0 / result.jacobian,
|
||||
Cross(result.covariant[1], result.covariant[2]));
|
||||
result.reciprocal[1] = Scale(1.0 / result.jacobian,
|
||||
Cross(result.covariant[2], result.covariant[0]));
|
||||
result.reciprocal[2] = Scale(1.0 / result.jacobian,
|
||||
Cross(result.covariant[0], result.covariant[1]));
|
||||
result.reciprocal[0] =
|
||||
(1.0 / result.jacobian) * result.covariant[1].Cross(result.covariant[2]);
|
||||
result.reciprocal[1] =
|
||||
(1.0 / result.jacobian) * result.covariant[2].Cross(result.covariant[0]);
|
||||
result.reciprocal[2] =
|
||||
(1.0 / result.jacobian) * result.covariant[0].Cross(result.covariant[1]);
|
||||
|
||||
const Vector3 area = Cross(midsurface_xi, midsurface_eta);
|
||||
const double director_norm = Norm(director_value);
|
||||
if (!IsFinite(area) || !IsFinite(director_value) ||
|
||||
const Vector3 area = midsurface_xi.Cross(midsurface_eta);
|
||||
const double director_norm = director_value.Norm();
|
||||
if (!area.IsFinite() || !director_value.IsFinite() ||
|
||||
!std::isfinite(director_norm) || !(director_norm > 0.0) ||
|
||||
!(Dot(area, normal_candidate_) > 0.0)) {
|
||||
!(area.Dot(normal_candidate_) > 0.0)) {
|
||||
return false;
|
||||
}
|
||||
result.frame.e3 = Scale(1.0 / director_norm, director_value);
|
||||
if (!(Dot(area, result.frame.e3) > 0.0)) {
|
||||
const Vector3 e3 = (1.0 / director_norm) * director_value;
|
||||
result.frame.e3 = e3.Components();
|
||||
if (!(area.Dot(e3) > 0.0)) {
|
||||
return false;
|
||||
}
|
||||
const Vector3 e1_candidate =
|
||||
Subtract(midsurface_xi,
|
||||
Scale(Dot(midsurface_xi, result.frame.e3), result.frame.e3));
|
||||
const double e1_norm = Norm(e1_candidate);
|
||||
if (!IsFinite(e1_candidate) || !std::isfinite(e1_norm) || !(e1_norm > 0.0)) {
|
||||
const Vector3 e1_candidate = midsurface_xi - midsurface_xi.Dot(e3) * e3;
|
||||
const double e1_norm = e1_candidate.Norm();
|
||||
if (!e1_candidate.IsFinite() || !std::isfinite(e1_norm) || !(e1_norm > 0.0)) {
|
||||
return false;
|
||||
}
|
||||
result.frame.e1 = Scale(1.0 / e1_norm, e1_candidate);
|
||||
result.frame.e2 = Cross(result.frame.e3, result.frame.e1);
|
||||
return IsFinite(result.reciprocal[0]) && IsFinite(result.reciprocal[1]) &&
|
||||
IsFinite(result.reciprocal[2]) && IsFinite(result.frame.e2);
|
||||
const Vector3 e1 = (1.0 / e1_norm) * e1_candidate;
|
||||
const Vector3 e2 = e3.Cross(e1);
|
||||
result.frame.e1 = e1.Components();
|
||||
result.frame.e2 = e2.Components();
|
||||
return result.reciprocal[0].IsFinite() && result.reciprocal[1].IsFinite() &&
|
||||
result.reciprocal[2].IsFinite() && e2.IsFinite();
|
||||
}
|
||||
|
||||
std::array<std::array<Vector3, 3>, 20> Mitc4Shell::BasisDerivatives(
|
||||
@@ -861,24 +827,24 @@ std::array<std::array<Vector3, 3>, 20> Mitc4Shell::BasisDerivatives(
|
||||
const std::size_t offset = node * kPhysicalDofsPerNode;
|
||||
for (std::size_t component = 0U; component < 3U; ++component) {
|
||||
derivatives[offset + component][0] =
|
||||
Scale(shape.xi_derivatives[node], global_axes[component]);
|
||||
shape.xi_derivatives[node] * global_axes[component];
|
||||
derivatives[offset + component][1] =
|
||||
Scale(shape.eta_derivatives[node], global_axes[component]);
|
||||
shape.eta_derivatives[node] * global_axes[component];
|
||||
}
|
||||
|
||||
const Vector3 alpha_direction = Scale(-half_thickness, tangent_b_[node]);
|
||||
const Vector3 alpha_direction = -half_thickness * tangent_b_[node];
|
||||
derivatives[offset + 3U][0] =
|
||||
Scale(zeta * shape.xi_derivatives[node], alpha_direction);
|
||||
zeta * shape.xi_derivatives[node] * alpha_direction;
|
||||
derivatives[offset + 3U][1] =
|
||||
Scale(zeta * shape.eta_derivatives[node], alpha_direction);
|
||||
derivatives[offset + 3U][2] = Scale(shape.values[node], alpha_direction);
|
||||
zeta * shape.eta_derivatives[node] * alpha_direction;
|
||||
derivatives[offset + 3U][2] = shape.values[node] * alpha_direction;
|
||||
|
||||
const Vector3 beta_direction = Scale(half_thickness, tangent_a_[node]);
|
||||
const Vector3 beta_direction = half_thickness * tangent_a_[node];
|
||||
derivatives[offset + 4U][0] =
|
||||
Scale(zeta * shape.xi_derivatives[node], beta_direction);
|
||||
zeta * shape.xi_derivatives[node] * beta_direction;
|
||||
derivatives[offset + 4U][1] =
|
||||
Scale(zeta * shape.eta_derivatives[node], beta_direction);
|
||||
derivatives[offset + 4U][2] = Scale(shape.values[node], beta_direction);
|
||||
zeta * shape.eta_derivatives[node] * beta_direction;
|
||||
derivatives[offset + 4U][2] = shape.values[node] * beta_direction;
|
||||
}
|
||||
return derivatives;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user