mkse
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
#pragma once
|
||||
|
||||
// Contact discretization based on the passive body's trace nodes.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "Elasticity.h"
|
||||
|
||||
namespace contact_slave_nodes {
|
||||
|
||||
constexpr double kContactEps = 1e-12;
|
||||
|
||||
inline bool almost_equal(double lhs, double rhs) {
|
||||
return std::fabs(lhs - rhs) < kContactEps;
|
||||
}
|
||||
|
||||
inline std::vector<double> collect_overlap_nodes(
|
||||
const std::vector<double>& bottom_x,
|
||||
const std::vector<double>& top_x,
|
||||
double contact_left,
|
||||
double contact_right) {
|
||||
|
||||
std::vector<double> nodes;
|
||||
nodes.reserve(bottom_x.size() + top_x.size() + 2);
|
||||
nodes.push_back(contact_left);
|
||||
nodes.push_back(contact_right);
|
||||
|
||||
auto append_inside = [&](const std::vector<double>& x_nodes) {
|
||||
for (double x : x_nodes)
|
||||
if (x >= contact_left - kContactEps && x <= contact_right + kContactEps)
|
||||
nodes.push_back(x);
|
||||
};
|
||||
|
||||
append_inside(bottom_x);
|
||||
append_inside(top_x);
|
||||
|
||||
std::sort(nodes.begin(), nodes.end());
|
||||
nodes.erase(std::unique(nodes.begin(), nodes.end(), almost_equal), nodes.end());
|
||||
return nodes;
|
||||
}
|
||||
|
||||
inline std::vector<double> collect_active_lambda_nodes(
|
||||
const std::vector<double>& passive_x,
|
||||
double contact_left,
|
||||
double contact_right) {
|
||||
|
||||
std::vector<double> active_nodes;
|
||||
active_nodes.reserve(passive_x.size());
|
||||
|
||||
for (size_t i = 0; i < passive_x.size(); ++i) {
|
||||
const double support_left = (i == 0) ? passive_x[i] : passive_x[i - 1];
|
||||
const double support_right = (i + 1 == passive_x.size()) ? passive_x[i] : passive_x[i + 1];
|
||||
|
||||
if (support_right > contact_left + kContactEps &&
|
||||
support_left < contact_right - kContactEps) {
|
||||
active_nodes.push_back(passive_x[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (active_nodes.size() < 2)
|
||||
throw std::runtime_error("Not enough passive contact nodes for Lagrange multipliers.");
|
||||
|
||||
return active_nodes;
|
||||
}
|
||||
|
||||
inline ContactDiscretization build(
|
||||
const std::vector<double>& bottom_x,
|
||||
const std::vector<double>& top_x,
|
||||
double contact_left,
|
||||
double contact_right,
|
||||
ContactSlaveBody passive_body) {
|
||||
|
||||
const std::vector<double>& passive_x =
|
||||
(passive_body == ContactSlaveBody::Bottom) ? bottom_x : top_x;
|
||||
|
||||
ContactDiscretization discretization;
|
||||
discretization.lambda_nodes = collect_active_lambda_nodes(
|
||||
passive_x, contact_left, contact_right);
|
||||
|
||||
const std::vector<double> integration_nodes = collect_overlap_nodes(
|
||||
bottom_x, top_x, contact_left, contact_right);
|
||||
discretization.mortar_elements.reserve(integration_nodes.size() - 1);
|
||||
for (size_t seg = 0; seg + 1 < integration_nodes.size(); ++seg)
|
||||
discretization.mortar_elements.push_back({ integration_nodes[seg], integration_nodes[seg + 1] });
|
||||
|
||||
return discretization;
|
||||
}
|
||||
|
||||
} // namespace contact_slave_nodes
|
||||
@@ -0,0 +1,81 @@
|
||||
#pragma once
|
||||
|
||||
// Contact discretization on an independent uniform multiplier grid.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
#include "Elasticity.h"
|
||||
|
||||
namespace contact_uniform_lambda_partition {
|
||||
|
||||
constexpr double kContactEps = 1e-12;
|
||||
|
||||
inline size_t count_contact_nodes(
|
||||
const std::vector<double>& x_nodes,
|
||||
double contact_left,
|
||||
double contact_right) {
|
||||
|
||||
size_t count = 0;
|
||||
for (double x : x_nodes)
|
||||
if (x >= contact_left - kContactEps && x <= contact_right + kContactEps)
|
||||
++count;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
inline size_t default_lambda_node_count(
|
||||
const std::vector<double>& bottom_x,
|
||||
const std::vector<double>& top_x,
|
||||
double contact_left,
|
||||
double contact_right) {
|
||||
|
||||
return std::max<size_t>(
|
||||
2,
|
||||
std::max(
|
||||
count_contact_nodes(bottom_x, contact_left, contact_right),
|
||||
count_contact_nodes(top_x, contact_left, contact_right)));
|
||||
}
|
||||
|
||||
inline std::vector<double> build_uniform_lambda_nodes(
|
||||
double contact_left,
|
||||
double contact_right,
|
||||
size_t lambda_node_count) {
|
||||
|
||||
std::vector<double> lambda_nodes(lambda_node_count);
|
||||
const double step = (contact_right - contact_left) / (lambda_node_count - 1);
|
||||
|
||||
for (size_t i = 0; i < lambda_node_count; ++i)
|
||||
lambda_nodes[i] = contact_left + i * step;
|
||||
|
||||
lambda_nodes.front() = contact_left;
|
||||
lambda_nodes.back() = contact_right;
|
||||
return lambda_nodes;
|
||||
}
|
||||
|
||||
inline ContactDiscretization build(
|
||||
const std::vector<double>& bottom_x,
|
||||
const std::vector<double>& top_x,
|
||||
double contact_left,
|
||||
double contact_right,
|
||||
size_t lambda_node_count) {
|
||||
|
||||
if (lambda_node_count == 0)
|
||||
lambda_node_count = default_lambda_node_count(
|
||||
bottom_x, top_x, contact_left, contact_right);
|
||||
|
||||
ContactDiscretization discretization;
|
||||
discretization.lambda_nodes = build_uniform_lambda_nodes(
|
||||
contact_left, contact_right, std::max<size_t>(2, lambda_node_count));
|
||||
|
||||
discretization.mortar_elements.reserve(discretization.lambda_nodes.size() - 1);
|
||||
for (size_t seg = 0; seg + 1 < discretization.lambda_nodes.size(); ++seg)
|
||||
discretization.mortar_elements.push_back({
|
||||
discretization.lambda_nodes[seg],
|
||||
discretization.lambda_nodes[seg + 1]
|
||||
});
|
||||
|
||||
return discretization;
|
||||
}
|
||||
|
||||
} // namespace contact_uniform_lambda_partition
|
||||
@@ -0,0 +1,73 @@
|
||||
#pragma once
|
||||
|
||||
// Contact discretization on a uniform refinement of the combined traces.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
#include "Elasticity.h"
|
||||
#include "ContactUniformLambdaPartition.h"
|
||||
|
||||
namespace contact_uniform_union_partition {
|
||||
|
||||
constexpr double kContactEps = 1e-12;
|
||||
|
||||
inline bool almost_equal(double lhs, double rhs) {
|
||||
return std::fabs(lhs - rhs) < kContactEps;
|
||||
}
|
||||
|
||||
inline std::vector<MortarElement> build_mortar_elements(
|
||||
const std::vector<double>& bottom_x,
|
||||
const std::vector<double>& top_x,
|
||||
const std::vector<double>& lambda_nodes,
|
||||
double contact_left,
|
||||
double contact_right) {
|
||||
|
||||
std::vector<double> partition;
|
||||
partition.reserve(bottom_x.size() + top_x.size() + lambda_nodes.size() + 2);
|
||||
partition.push_back(contact_left);
|
||||
partition.push_back(contact_right);
|
||||
|
||||
auto append_contact_nodes = [&](const std::vector<double>& nodes) {
|
||||
for (double x : nodes)
|
||||
if (x >= contact_left - kContactEps && x <= contact_right + kContactEps)
|
||||
partition.push_back(x);
|
||||
};
|
||||
|
||||
append_contact_nodes(bottom_x);
|
||||
append_contact_nodes(top_x);
|
||||
append_contact_nodes(lambda_nodes);
|
||||
|
||||
std::sort(partition.begin(), partition.end());
|
||||
partition.erase(std::unique(partition.begin(), partition.end(), almost_equal), partition.end());
|
||||
|
||||
std::vector<MortarElement> mortar_elements;
|
||||
mortar_elements.reserve(partition.size() - 1);
|
||||
for (size_t seg = 0; seg + 1 < partition.size(); ++seg)
|
||||
mortar_elements.push_back({ partition[seg], partition[seg + 1] });
|
||||
|
||||
return mortar_elements;
|
||||
}
|
||||
|
||||
inline ContactDiscretization build(
|
||||
const std::vector<double>& bottom_x,
|
||||
const std::vector<double>& top_x,
|
||||
double contact_left,
|
||||
double contact_right,
|
||||
size_t lambda_node_count) {
|
||||
|
||||
ContactDiscretization discretization =
|
||||
contact_uniform_lambda_partition::build(
|
||||
bottom_x, top_x, contact_left, contact_right, lambda_node_count);
|
||||
|
||||
discretization.mortar_elements = build_mortar_elements(
|
||||
bottom_x,
|
||||
top_x,
|
||||
discretization.lambda_nodes,
|
||||
contact_left,
|
||||
contact_right);
|
||||
|
||||
return discretization;
|
||||
}
|
||||
|
||||
} // namespace contact_uniform_union_partition
|
||||
@@ -0,0 +1,265 @@
|
||||
#pragma once
|
||||
|
||||
// Core data structures and solvers for the finite superelement model.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
struct Point {
|
||||
double x, y;
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& output, const Point& p);
|
||||
};
|
||||
|
||||
Point operator+(const Point& a, const Point& b);
|
||||
Point operator-(const Point& a, const Point& b);
|
||||
Point operator*(double a, const Point& p);
|
||||
Point operator/(const Point& p, double a);
|
||||
|
||||
std::vector<double> operator-(const std::vector<double>& a,
|
||||
const std::vector<double>& b);
|
||||
|
||||
using function = std::function<double(const Point&)>;
|
||||
using vec_function = std::function<Point(const Point&)>;
|
||||
using lambda_func = std::function<size_t(size_t)>;
|
||||
|
||||
enum class CoordinateSystem {
|
||||
Cartesian,
|
||||
Axisymmetric
|
||||
};
|
||||
|
||||
enum class ContactMethod {
|
||||
SlaveNodes,
|
||||
UniformLambdaPartition,
|
||||
UniformUnionPartition
|
||||
};
|
||||
|
||||
enum class ContactSlaveBody {
|
||||
Bottom,
|
||||
Top
|
||||
};
|
||||
|
||||
struct ContactOptions {
|
||||
CoordinateSystem coordinate_system = CoordinateSystem::Axisymmetric;
|
||||
ContactMethod method = ContactMethod::UniformUnionPartition;
|
||||
size_t lambda_node_count = 0;
|
||||
ContactSlaveBody slave_body = ContactSlaveBody::Bottom;
|
||||
};
|
||||
|
||||
std::string to_string(CoordinateSystem coordinate_system);
|
||||
std::string to_string(ContactMethod contact_method);
|
||||
std::string to_string(ContactSlaveBody slave_body);
|
||||
|
||||
struct Triangle {
|
||||
size_t a, b, c;
|
||||
};
|
||||
|
||||
struct Rectangle {
|
||||
size_t a, b, c, d;
|
||||
};
|
||||
|
||||
struct MortarElement {
|
||||
double chi_left;
|
||||
double chi_right;
|
||||
};
|
||||
|
||||
struct ContactDiscretization {
|
||||
std::vector<double> lambda_nodes;
|
||||
std::vector<MortarElement> mortar_elements;
|
||||
};
|
||||
|
||||
class Matrix {
|
||||
private:
|
||||
std::vector<std::vector<double>> matrix;
|
||||
|
||||
public:
|
||||
Matrix(size_t n, double a = 0);
|
||||
Matrix(size_t n, size_t m, double a = 0);
|
||||
Matrix(std::vector<std::vector<double>> m) : matrix(m) {};
|
||||
Matrix(Matrix* M) { matrix = M->matrix; };
|
||||
|
||||
std::vector<double>& operator[](size_t i) { return matrix[i]; };
|
||||
const std::vector<double>& operator[](size_t i) const { return matrix[i]; };
|
||||
Matrix operator*(double a);
|
||||
Matrix& operator*=(double a);
|
||||
|
||||
Matrix T() const;
|
||||
size_t size(short axis = 0) const {
|
||||
return axis ? matrix[0].size() : matrix.size();
|
||||
};
|
||||
Matrix dot(const Matrix& m) const;
|
||||
std::vector<double> dot(const std::vector<double>& v) const;
|
||||
void print() const;
|
||||
|
||||
static Matrix eye(size_t n, double a = 1);
|
||||
};
|
||||
|
||||
std::vector<double> solveGaussFullPivot(
|
||||
const Matrix& A,
|
||||
const std::vector<double>& b,
|
||||
double eps = 1e-12
|
||||
);
|
||||
|
||||
class FEM {
|
||||
size_t mx, ny;
|
||||
Point left_down;
|
||||
Point right_up;
|
||||
std::vector<Point> points;
|
||||
std::vector<Triangle> triangles;
|
||||
double triangle_area;
|
||||
std::vector<Point> f;
|
||||
std::vector<Point> u;
|
||||
CoordinateSystem coordinate_system;
|
||||
|
||||
std::vector<double> F;
|
||||
Matrix A;
|
||||
|
||||
public:
|
||||
FEM(const Point& a, const Point& b, size_t n, size_t m,
|
||||
CoordinateSystem coordinate_system = CoordinateSystem::Axisymmetric);
|
||||
|
||||
Point& get_point(size_t n);
|
||||
const Point& get_point(size_t n) const;
|
||||
Triangle& get_triangle(size_t n);
|
||||
const Triangle& get_triangle(size_t n) const;
|
||||
|
||||
size_t psize() const { return points.size(); };
|
||||
size_t tsize() const { return triangles.size(); };
|
||||
size_t xsize() const { return mx; };
|
||||
size_t ysize() const { return ny; };
|
||||
CoordinateSystem get_coordinate_system() const { return coordinate_system; };
|
||||
|
||||
void print_points() const;
|
||||
void print_triangles() const;
|
||||
|
||||
Point& operator[](size_t n) { return get_point(n); };
|
||||
const Point& operator[](size_t n) const { return get_point(n); };
|
||||
|
||||
Triangle& operator()(size_t n) { return get_triangle(n); };
|
||||
const Triangle& operator()(size_t n) const { return get_triangle(n); };
|
||||
|
||||
void set_boundaries(char side, const vec_function& g);
|
||||
|
||||
void construct_AF(double E, double nu, vec_function body_force);
|
||||
void apply_boundaries();
|
||||
std::vector<Point> solve();
|
||||
|
||||
void clear_AFu();
|
||||
|
||||
void 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);
|
||||
|
||||
void calculate_bc2(const std::vector<size_t>& pos,
|
||||
const std::vector<vec_function>& g, std::vector<double>& p_vec);
|
||||
|
||||
std::pair<Matrix, std::vector<double>> get_AF();
|
||||
void set_AF(const Matrix& A_new, const std::vector<double>& F_new);
|
||||
};
|
||||
|
||||
Matrix operator*(double a, Matrix m);
|
||||
|
||||
std::tuple<Matrix, Matrix> LU_decomposition(const Matrix& m);
|
||||
|
||||
std::vector<double> solveLU(const Matrix& L, const Matrix& U,
|
||||
const std::vector<double>& b);
|
||||
|
||||
Point zero(const Point& p);
|
||||
|
||||
class FSEM {
|
||||
double E;
|
||||
double nu;
|
||||
Point a;
|
||||
Point b;
|
||||
size_t n_side_x;
|
||||
size_t n_side_y;
|
||||
std::vector<Point> nodes;
|
||||
size_t coef_x;
|
||||
size_t coef_y;
|
||||
|
||||
std::vector<std::vector<Point>> basis;
|
||||
std::vector<Point> basis_coefficients;
|
||||
|
||||
Matrix K;
|
||||
std::vector<double> f;
|
||||
CoordinateSystem coordinate_system;
|
||||
|
||||
public:
|
||||
FEM fem;
|
||||
|
||||
FSEM(double E, double nu, const Point& a, const Point& b,
|
||||
size_t n_x, size_t n_y, int coef_val_x = 1, int coef_val_y = 1,
|
||||
CoordinateSystem coordinate_system = CoordinateSystem::Axisymmetric);
|
||||
|
||||
Point& get_node(size_t n);
|
||||
const Point& get_node(size_t n) const;
|
||||
|
||||
size_t nsize() const { return nodes.size(); };
|
||||
CoordinateSystem get_coordinate_system() const { return coordinate_system; };
|
||||
|
||||
Point& operator[](size_t n) { return get_node(n); };
|
||||
const Point& operator[](size_t n) const { return get_node(n); };
|
||||
|
||||
void print_nodes() const;
|
||||
|
||||
void construct_basis();
|
||||
|
||||
Matrix matrix_form_basis();
|
||||
|
||||
const Matrix& get_K() const { return K; }
|
||||
const std::vector<double>& get_f() const { return f; }
|
||||
const std::vector<std::vector<Point>>& get_basis() const { return basis; }
|
||||
|
||||
void construct_f_bc2(const std::vector<size_t>& pos,
|
||||
const std::vector<vec_function>& g);
|
||||
|
||||
std::vector<Point> find_answer();
|
||||
std::vector<Point> find_answer(const std::vector<double>& coefs, size_t start = 0);
|
||||
|
||||
void set_bc1(char side, const vec_function& g);
|
||||
|
||||
void calculate_coef_Matrix_bc2(const int finish, const int i,
|
||||
bool cur_pos, bool prev_pos, int& add_B, int& add_C, const int add_basis,
|
||||
Matrix& B, Matrix& C);
|
||||
|
||||
void save_bc1(std::vector<double>& coefs_Dirichle, int& dir_id, const int i);
|
||||
|
||||
void set_bc2(const std::vector<size_t>& pos,
|
||||
const std::vector<vec_function>& g);
|
||||
|
||||
std::vector<size_t> get_side_nodes(char side) const;
|
||||
std::vector<size_t> get_side_fem_nodes(char side) const;
|
||||
|
||||
Point coefficient(int i, Point coef_val) {
|
||||
return std::isnan(basis_coefficients[i].x) ? coef_val : basis_coefficients[i];
|
||||
}
|
||||
|
||||
std::vector<std::pair<size_t, double>> get_known_dofs() const;
|
||||
};
|
||||
|
||||
double mortar_shape_func(size_t i, const std::vector<double>& s, double cur);
|
||||
|
||||
std::vector<double> solve_mortar_contact(
|
||||
FSEM& bottom_body,
|
||||
FSEM& top_body,
|
||||
const std::vector<double>& rhs_bottom,
|
||||
const std::vector<double>& rhs_top,
|
||||
const ContactOptions& options);
|
||||
|
||||
std::vector<double> solve_mortar_contact(
|
||||
FSEM& bottom_body,
|
||||
FSEM& top_body,
|
||||
const std::vector<double>& rhs_bottom,
|
||||
const std::vector<double>& rhs_top,
|
||||
size_t lambda_node_count = 0);
|
||||
|
||||
std::vector<double> solveWithLU(const Matrix& A,
|
||||
const std::vector<double>& b,
|
||||
double eps = 1e-15);
|
||||
@@ -0,0 +1,115 @@
|
||||
#pragma once
|
||||
|
||||
// Axisymmetric finite-element integration kernels.
|
||||
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
|
||||
#include "Elasticity.h"
|
||||
|
||||
namespace fem_axisymmetric {
|
||||
|
||||
constexpr double kTwoPi = 6.28318530717958647692;
|
||||
|
||||
struct TriangleQuadraturePoint {
|
||||
double weight;
|
||||
std::array<double, 3> phi;
|
||||
};
|
||||
|
||||
inline 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 } }
|
||||
} };
|
||||
|
||||
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<double>& F,
|
||||
const std::vector<Point>& points,
|
||||
const std::vector<Triangle>& 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<double> 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
|
||||
@@ -0,0 +1,76 @@
|
||||
#pragma once
|
||||
|
||||
// Cartesian finite-element integration kernels.
|
||||
|
||||
#include "Elasticity.h"
|
||||
|
||||
namespace fem_cartesian {
|
||||
|
||||
inline void assemble(
|
||||
Matrix& A,
|
||||
std::vector<double>& F,
|
||||
const std::vector<Point>& points,
|
||||
const std::vector<Triangle>& triangles,
|
||||
double triangle_area,
|
||||
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, 0.0 },
|
||||
{ lambda, lambda + 2.0 * mu, 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 Matrix grad_phi({
|
||||
{ (p2.y - p3.y) / (2.0 * triangle_area), (p3.x - p2.x) / (2.0 * triangle_area) },
|
||||
{ (p3.y - p1.y) / (2.0 * triangle_area), (p1.x - p3.x) / (2.0 * triangle_area) },
|
||||
{ (p1.y - p2.y) / (2.0 * triangle_area), (p2.x - p1.x) / (2.0 * triangle_area) }
|
||||
});
|
||||
|
||||
Matrix R(3, 6ull);
|
||||
for (size_t p = 0; p < 3; ++p) {
|
||||
R[0][2 * p] = grad_phi[p][0];
|
||||
R[1][2 * p + 1] = grad_phi[p][1];
|
||||
R[2][2 * p] = grad_phi[p][1];
|
||||
R[2][2 * p + 1] = grad_phi[p][0];
|
||||
}
|
||||
|
||||
Matrix Ae = triangle_area * R.T().dot(C).dot(R);
|
||||
|
||||
std::vector<double> Fe(6, 0.0);
|
||||
const Point center = (p1 + p2 + p3) / 3.0;
|
||||
const Point body_force = f(center);
|
||||
for (size_t p = 0; p < 3; ++p) {
|
||||
Fe[2 * p] = body_force.x * triangle_area / 3.0;
|
||||
Fe[2 * p + 1] = body_force.y * triangle_area / 3.0;
|
||||
}
|
||||
|
||||
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&, double len) {
|
||||
return 0.5 * len;
|
||||
}
|
||||
|
||||
} // namespace fem_cartesian
|
||||
@@ -0,0 +1,114 @@
|
||||
#pragma once
|
||||
|
||||
// Manufactured solutions used to validate the numerical model.
|
||||
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "Elasticity.h"
|
||||
|
||||
struct MeshSize {
|
||||
size_t bottom_x;
|
||||
size_t bottom_y;
|
||||
size_t top_x;
|
||||
size_t top_y;
|
||||
size_t lambda_nodes;
|
||||
};
|
||||
|
||||
struct TestCase {
|
||||
std::function<vec_function(double, double)> exact_solution;
|
||||
double E;
|
||||
double nu;
|
||||
Point bottom_a;
|
||||
Point bottom_b;
|
||||
Point top_a;
|
||||
Point top_b;
|
||||
MeshSize mesh;
|
||||
std::array<char, 3> bottom_dirichlet_sides;
|
||||
std::array<char, 3> top_dirichlet_sides;
|
||||
};
|
||||
|
||||
const std::map<std::string, TestCase> TESTS = {
|
||||
{
|
||||
"axisymmetric_inverse_r",
|
||||
TestCase{
|
||||
[](double, double) {
|
||||
return [](const Point& p) {
|
||||
return Point{ 5.0 / p.x, 0.0 };
|
||||
};
|
||||
},
|
||||
21e10,
|
||||
0.3,
|
||||
Point{ 1.0, 0.0 },
|
||||
Point{ 3.0, 0.5 },
|
||||
Point{ 1.0, 0.5 },
|
||||
Point{ 3.0, 3.0 },
|
||||
MeshSize{ 18, 18, 18, 18, 18 },
|
||||
std::array<char, 3>{ 'W', 'E', 'S' },
|
||||
std::array<char, 3>{ 'W', 'N', 'E' }
|
||||
}
|
||||
},
|
||||
{
|
||||
"axisymmetric_linear",
|
||||
TestCase{
|
||||
[](double, double) {
|
||||
return [](const Point& p) {
|
||||
return Point{ 2.0 * p.x + 5.0 / p.x, 34.0 * p.y };
|
||||
};
|
||||
},
|
||||
21e10,
|
||||
0.3,
|
||||
Point{ 1.0, 0.0 },
|
||||
Point{ 3.0, 0.5 },
|
||||
Point{ 1.0, 0.5 },
|
||||
Point{ 3.0, 3.0 },
|
||||
MeshSize{ 10, 10, 10, 10, 10 },
|
||||
std::array<char, 3>{ 'W', 'E', 'S' },
|
||||
std::array<char, 3>{ 'W', 'N', 'E' }
|
||||
}
|
||||
},
|
||||
{
|
||||
"cartesian_exp",
|
||||
TestCase{
|
||||
[](double, double) {
|
||||
return [](const Point& p) {
|
||||
return Point{
|
||||
std::exp(p.x) * std::cos(p.y - 0.5),
|
||||
-std::exp(p.x) * std::sin(p.y - 0.5)
|
||||
};
|
||||
};
|
||||
},
|
||||
21e10,
|
||||
0.3,
|
||||
Point{ 0.0, 0.0 },
|
||||
Point{ 1.0, 0.5 },
|
||||
Point{ 0.0, 0.5 },
|
||||
Point{ 1.0, 1.0 },
|
||||
MeshSize{ 6, 6, 10, 10, 10 },
|
||||
std::array<char, 3>{ 'W', 'E', 'S' },
|
||||
std::array<char, 3>{ 'W', 'N', 'E' }
|
||||
}
|
||||
},
|
||||
{
|
||||
"cartesian_linear",
|
||||
TestCase{
|
||||
[](double, double) {
|
||||
return [](const Point& p) {
|
||||
return Point{ -21.0 * p.x, 13.0 * p.y };
|
||||
};
|
||||
},
|
||||
21e10,
|
||||
0.3,
|
||||
Point{ 0.0, 0.0 },
|
||||
Point{ 3.0, 1.0 },
|
||||
Point{ 0.0, 1.0 },
|
||||
Point{ 2.0, 4.0 },
|
||||
MeshSize{ 5, 5, 10, 10, 10 },
|
||||
std::array<char, 3>{ 'W', 'E', 'S' },
|
||||
std::array<char, 3>{ 'W', 'N', 'E' }
|
||||
}
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user