#pragma once // Axisymmetric finite-element integration kernels. #include #include #include "Elasticity.h" namespace fem_axisymmetric { constexpr double kTwoPi = 6.28318530717958647692; struct TriangleQuadraturePoint { double weight; std::array phi; }; inline const std::array kTriangleQuadrature = { { { 1.0 / 3.0, { 1.0 / 6.0, 1.0 / 6.0, 2.0 / 3.0 } }, { 1.0 / 3.0, { 1.0 / 6.0, 2.0 / 3.0, 1.0 / 6.0 } }, { 1.0 / 3.0, { 2.0 / 3.0, 1.0 / 6.0, 1.0 / 6.0 } } } }; inline double signed_double_area(const Point& p1, const Point& p2, const Point& p3) { return (p2.x - p1.x) * (p3.y - p1.y) - (p3.x - p1.x) * (p2.y - p1.y); } inline void assemble( Matrix& A, std::vector& F, const std::vector& points, const std::vector& triangles, double, double E, double nu, const vec_function& f) { const double lambda = (E * nu) / ((1.0 + nu) * (1.0 - 2.0 * nu)); const double mu = E / (2.0 * (1.0 + nu)); const Matrix C({ { lambda + 2.0 * mu, lambda, lambda, 0.0 }, { lambda, lambda + 2.0 * mu, lambda, 0.0 }, { lambda, lambda, lambda + 2.0 * mu, 0.0 }, { 0.0, 0.0, 0.0, mu } }); for (const Triangle& T : triangles) { const Point& p1 = points[T.a]; const Point& p2 = points[T.b]; const Point& p3 = points[T.c]; const double two_area = signed_double_area(p1, p2, p3); const double area = 0.5 * std::fabs(two_area); const Matrix grad_phi({ { (p2.y - p3.y) / two_area, (p3.x - p2.x) / two_area }, { (p3.y - p1.y) / two_area, (p1.x - p3.x) / two_area }, { (p1.y - p2.y) / two_area, (p2.x - p1.x) / two_area } }); Matrix Ae(6ull); std::vector Fe(6, 0.0); for (const auto& qp : kTriangleQuadrature) { const Point q = qp.phi[0] * p1 + qp.phi[1] * p2 + qp.phi[2] * p3; const double r_q = q.x; Matrix B(4ull, 6ull); for (size_t p = 0; p < 3; ++p) { const double dphi_dr = grad_phi[p][0]; const double dphi_dz = grad_phi[p][1]; const double phi = qp.phi[p]; B[0][2 * p] = dphi_dr; B[1][2 * p + 1] = dphi_dz; B[2][2 * p] = phi / r_q; B[3][2 * p] = dphi_dz; B[3][2 * p + 1] = dphi_dr; } const Matrix stiffness_q = B.T().dot(C).dot(B); const double weight = kTwoPi * area * qp.weight * r_q; for (size_t i = 0; i < 6; ++i) for (size_t j = 0; j < 6; ++j) Ae[i][j] += weight * stiffness_q[i][j]; const Point body_force = f(q); for (size_t p = 0; p < 3; ++p) { const double shape_value = qp.phi[p]; Fe[2 * p] += weight * shape_value * body_force.x; Fe[2 * p + 1] += weight * shape_value * body_force.y; } } auto dof = [T](size_t i) -> size_t { if (i == 0) return 2 * T.a; if (i == 1) return 2 * T.a + 1; if (i == 2) return 2 * T.b; if (i == 3) return 2 * T.b + 1; if (i == 4) return 2 * T.c; return 2 * T.c + 1; }; for (size_t i = 0; i < 6; ++i) { F[dof(i)] += Fe[i]; for (size_t j = 0; j < 6; ++j) A[dof(i)][dof(j)] += Ae[i][j]; } } } inline double boundary_segment_weight(const Point& midpoint, double len) { return 0.5 * kTwoPi * midpoint.x * len; } } // namespace fem_axisymmetric