Files
MKSE_elasticity/src/FEM.cpp
T
2026-09-01 10:00:04 +03:00

275 lines
6.9 KiB
C++

#include <iostream>
#include <stdexcept>
#include <array>
#include <cmath>
#include "Elasticity.h"
#include "FEMAxisymmetric.h"
#include "FEMCartesian.h"
namespace {
constexpr double kAxisymmetricTwoPi = 6.28318530717958647692;
constexpr double kAxisymmetricRadiusEps = 1e-14;
struct TriangleQuadraturePoint {
double weight;
std::array<double, 3> phi;
};
const std::array<TriangleQuadraturePoint, 3> 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 } }
} };
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);
}
}
std::ostream& operator<<(std::ostream& output, const Point& p) {
output << "{ " << p.x << "; " << p.y << " }";
return output;
}
Point operator+(const Point& a, const Point& b) {
return { a.x + b.x, a.y + b.y };
}
Point operator-(const Point& a, const Point& b) {
return { a.x - b.x, a.y - b.y };
}
Point operator*(const double a, const Point& p) {
return { p.x * a, p.y * a };
}
Point operator/(const Point& p, const double a) {
return { p.x / a, p.y / a };
}
function mul(function a, function b) {
return [a, b](const Point& p) { return b(p) * a(p); };
}
function get_x(vec_function a) {
return [a](const Point& p) { return a(p).x; };
}
function get_y(vec_function a) {
return [a](const Point& p) { return a(p).y; };
}
FEM::FEM(const Point& a, const Point& b, size_t n, size_t m,
CoordinateSystem coordinate_system)
: mx(m), ny(n), A(2 * n * m, 2 * n * m), F(2 * n * m),
left_down(a), right_up(b), coordinate_system(coordinate_system) {
double dx = (b.x - a.x) / (m - 1), dy = (b.y - a.y) / (n - 1);
u = std::vector<Point>(m * n, { NAN, NAN });
triangle_area = dx * dy / 2;
points.resize(m * n);
f.resize(m * n);
for (size_t i = 0; i != points.size(); ++i) {
points[i] = { a.x + (i % m) * dx, a.y + (i / m) * dy };
}
triangles.resize(2 * (m - 1) * (n - 1));
for (size_t i = 0; i != triangles.size(); ++i) {
size_t x = i % (2 * (m - 1)), y = i / (2 * (m - 1));
if (x % 2 == 0) { // "верхний" треугольник
triangles[i] = {
(y * m) + x / 2,
((y + 1) * m) + x / 2,
((y + 1) * m) + x / 2 + 1
};
}
else { // "нижний" треугольник
triangles[i] = {
(y * m) + x / 2,
((y + 1) * m) + x / 2 + 1,
(y * m) + x / 2 + 1
};
}
}
}
Point& FEM::get_point(size_t n) {
return points[n];
}
const Point& FEM::get_point(size_t n) const {
return points[n];
}
Triangle& FEM::get_triangle(size_t n) {
return triangles[n];
}
const Triangle& FEM::get_triangle(size_t n) const {
return triangles[n];
}
void FEM::print_points() const {
for (size_t i = 0; i < points.size(); ++i) {
std::cout << (*this)[i] << "\t";
}
std::cout << '\n';
}
void FEM::print_triangles() const {
for (size_t i = 0; i < triangles.size(); ++i) {
std::cout << "[ " <<
(*this)(i).a << ", " <<
(*this)(i).b << ", " <<
(*this)(i).c << "]\n";
}
}
void FEM::set_boundaries(char side, const vec_function& g) {
if (side == 'S') { // Нижняя граница
for (size_t j = 0; j != mx; ++j) {
Point t = g(points[j]);
if(isnan(u[j].x))
u[j].x = t.x;
if (isnan(u[j].y))
u[j].y = t.y;
}
}
else if (side == 'E') { // Правая граница
for (size_t i = 0; i != ny; ++i) {
Point t = g(points[mx - 1 + i * mx]);
if (isnan(u[mx - 1 + i * mx].x))
u[mx - 1 + i * mx].x = t.x;
if (isnan(u[mx - 1 + i * mx].y))
u[mx - 1 + i * mx].y = t.y;
}
}
else if (side == 'N') { // Верхняя граница
for (size_t j = 0; j != mx; ++j) {
Point t = g(points[mx * (ny - 1) + j]);
if (isnan(u[mx * (ny - 1) + j].x))
u[mx * (ny - 1) + j].x = t.x;
if (isnan(u[mx * (ny - 1) + j].y))
u[mx * (ny - 1) + j].y = t.y;
}
}
else { // Левая граница
for (size_t i = 0; i != ny; ++i) {
Point t = g(points[i * mx]);
if (isnan(u[i * mx].x))
u[i * mx].x = t.x;
if (isnan(u[i * mx].y))
u[i * mx].y = t.y;
}
}
}
void FEM::construct_AF(double E, double nu, vec_function body_force) {
if (coordinate_system == CoordinateSystem::Cartesian) {
fem_cartesian::assemble(A, F, points, triangles, triangle_area, E, nu, body_force);
}
else {
fem_axisymmetric::assemble(A, F, points, triangles, triangle_area, E, nu, body_force);
}
}
void FEM::apply_boundaries() {
for (size_t i = 0; i < psize(); ++i) {
if (!isnan(u[i].x)) {
F[2 * i] = u[i].x;
for (size_t j = 0; j < A[2 * i].size(); ++j)
A[2 * i][j] = 0;
A[2 * i][2 * i] = 1;
}
if (!isnan(u[i].y)) {
F[2 * i + 1] = u[i].y;
for (size_t j = 0; j < A[2 * i + 1].size(); ++j)
A[2 * i + 1][j] = 0;
A[2 * i + 1][2 * i + 1] = 1;
}
}
}
std::vector<Point> FEM::solve() {
auto [L, U] = LU_decomposition(A);
std::vector<double> dofs = solveLU(L, U, F);
std::vector<Point> res(psize());
for (size_t i = 0; i < psize(); ++i) {
res[i].x = dofs[2 * i];
res[i].y = dofs[2 * i + 1];
}
clear_AFu();
return res;
}
void FEM::clear_AFu() {
for (size_t i = 0; i < 2 * psize(); ++i) {
F[i] = 0;
if (i % 2 == 0)
u[i / 2] = { NAN, NAN };
for (size_t j = 0; j < 2 * psize(); ++j)
A[i][j] = 0;
}
}
std::pair<Matrix, std::vector<double>> FEM::get_AF() {
return { A, F };
}
void FEM::set_AF(const Matrix& A_new, const std::vector<double>& F_new) {
A = A_new;
F = F_new;
}
void FEM::bc2_side(lambda_func j, size_t start, size_t finish, double len,
int side, const std::vector<vec_function>& g,
std::vector<double>& p_vec) {
for (size_t i = start; i < finish; ++i) { // индекс по границе как в мксэ
const Point midpoint = (points[j(i)] + points[j(i + 1)]) / 2;
const double weight = (coordinate_system == CoordinateSystem::Cartesian)
? fem_cartesian::boundary_segment_weight(midpoint, len)
: fem_axisymmetric::boundary_segment_weight(midpoint, len);
Point integral = weight * g[side](midpoint);
// текущий узел
p_vec[2 * j(i)] += integral.x;
p_vec[2 * j(i) + 1] += integral.y;
p_vec[2 * j(i + 1)] += integral.x;
p_vec[2 * j(i + 1) + 1] += integral.y;
}
}
void FEM::calculate_bc2(const std::vector<size_t>& pos,
const std::vector<vec_function>& g, std::vector<double>& p_vec) {
double len_vert = (right_up.y - left_down.y) / (ny - 1),
len_hor = (right_up.x - left_down.x) / (mx - 1);
if (pos[0]) // слева ГУ 2 рода
bc2_side([&](size_t i) { return i * mx; }, 0, ny - 1,
len_vert, 0, g, p_vec);
if (pos[1]) // сверху ГУ 2 рода
bc2_side([&](size_t i) { return (mx - 1) * (ny - 1) + i; },
ny - 1, ny + mx - 2, len_hor, 1, g, p_vec);
if (pos[2]) // справа ГУ 2 рода
bc2_side([&](size_t i) { return mx * (2 * ny + mx - i - 2) - 1; },
ny + mx - 2, 2 * ny + mx - 3, len_vert,
2, g, p_vec);
if (pos[3]) // снизу ГУ 2 рода
bc2_side([&](size_t i) { return 1 + 2 * (ny + mx) - 5 - i; },
2 * ny + mx - 3, 2 * (ny + mx) - 4, len_hor,
3, g, p_vec);
}