use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet};
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct Point {
pub x: f64,
pub y: f64,
pub z: f64,
}
impl Point {
pub fn new_2d(x: f64, y: f64) -> Self {
Self { x, y, z: 0.0 }
}
pub fn new_3d(x: f64, y: f64, z: f64) -> Self {
Self { x, y, z }
}
pub fn distance(&self, other: &Point) -> f64 {
let dx = self.x - other.x;
let dy = self.y - other.y;
let dz = self.z - other.z;
(dx * dx + dy * dy + dz * dz).sqrt()
}
pub fn midpoint(&self, other: &Point) -> Point {
Point {
x: 0.5 * (self.x + other.x),
y: 0.5 * (self.y + other.y),
z: 0.5 * (self.z + other.z),
}
}
}
impl From<(f64, f64)> for Point {
fn from(p: (f64, f64)) -> Self {
Point::new_2d(p.0, p.1)
}
}
impl From<(f64, f64, f64)> for Point {
fn from(p: (f64, f64, f64)) -> Self {
Point::new_3d(p.0, p.1, p.2)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum ElementType {
Triangle,
Quadrilateral,
Tetrahedron,
Hexahedron,
}
impl ElementType {
pub fn num_vertices(&self) -> usize {
match self {
ElementType::Triangle => 3,
ElementType::Quadrilateral => 4,
ElementType::Tetrahedron => 4,
ElementType::Hexahedron => 8,
}
}
pub fn num_nodes_p1(&self) -> usize {
self.num_vertices()
}
pub fn num_nodes_p2(&self) -> usize {
match self {
ElementType::Triangle => 6, ElementType::Quadrilateral => 9, ElementType::Tetrahedron => 10, ElementType::Hexahedron => 27, }
}
pub fn dimension(&self) -> usize {
match self {
ElementType::Triangle | ElementType::Quadrilateral => 2,
ElementType::Tetrahedron | ElementType::Hexahedron => 3,
}
}
pub fn num_edges(&self) -> usize {
match self {
ElementType::Triangle => 3,
ElementType::Quadrilateral => 4,
ElementType::Tetrahedron => 6,
ElementType::Hexahedron => 12,
}
}
pub fn num_faces(&self) -> usize {
match self {
ElementType::Triangle => 3,
ElementType::Quadrilateral => 4,
ElementType::Tetrahedron => 4,
ElementType::Hexahedron => 6,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum PolynomialDegree {
P1, P2, P3, }
impl PolynomialDegree {
pub fn as_usize(&self) -> usize {
match self {
PolynomialDegree::P1 => 1,
PolynomialDegree::P2 => 2,
PolynomialDegree::P3 => 3,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Element {
pub element_type: ElementType,
pub nodes: Vec<usize>,
pub id: usize,
pub parent_id: Option<usize>,
pub level: usize,
}
impl Element {
pub fn new(element_type: ElementType, nodes: Vec<usize>, id: usize) -> Self {
Self {
element_type,
nodes,
id,
parent_id: None,
level: 0,
}
}
pub fn vertices(&self) -> &[usize] {
&self.nodes[..self.element_type.num_vertices()]
}
pub fn edges(&self) -> Vec<(usize, usize)> {
let verts = self.vertices();
match self.element_type {
ElementType::Triangle => vec![
(verts[0], verts[1]),
(verts[1], verts[2]),
(verts[2], verts[0]),
],
ElementType::Quadrilateral => vec![
(verts[0], verts[1]),
(verts[1], verts[2]),
(verts[2], verts[3]),
(verts[3], verts[0]),
],
ElementType::Tetrahedron => vec![
(verts[0], verts[1]),
(verts[0], verts[2]),
(verts[0], verts[3]),
(verts[1], verts[2]),
(verts[1], verts[3]),
(verts[2], verts[3]),
],
ElementType::Hexahedron => vec![
(verts[0], verts[1]),
(verts[1], verts[2]),
(verts[2], verts[3]),
(verts[3], verts[0]),
(verts[4], verts[5]),
(verts[5], verts[6]),
(verts[6], verts[7]),
(verts[7], verts[4]),
(verts[0], verts[4]),
(verts[1], verts[5]),
(verts[2], verts[6]),
(verts[3], verts[7]),
],
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum BoundaryType {
Dirichlet,
Neumann,
Robin,
PML,
Interior,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BoundaryFace {
pub nodes: Vec<usize>,
pub boundary_type: BoundaryType,
pub marker: i32,
pub element_idx: usize,
pub local_idx: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Mesh {
pub dimension: usize,
pub nodes: Vec<Point>,
pub elements: Vec<Element>,
pub boundaries: Vec<BoundaryFace>,
pub(crate) next_element_id: usize,
#[serde(skip)]
node_to_elements: Option<Vec<Vec<usize>>>,
}
impl Mesh {
pub fn new(dimension: usize) -> Self {
assert!(dimension == 2 || dimension == 3, "Dimension must be 2 or 3");
Self {
dimension,
nodes: Vec::new(),
elements: Vec::new(),
boundaries: Vec::new(),
next_element_id: 0,
node_to_elements: None,
}
}
pub fn add_node(&mut self, point: Point) -> usize {
let idx = self.nodes.len();
self.nodes.push(point);
self.node_to_elements = None; idx
}
pub fn add_element(&mut self, element_type: ElementType, nodes: Vec<usize>) -> usize {
let idx = self.elements.len();
let id = self.next_element_id;
self.next_element_id += 1;
self.elements.push(Element::new(element_type, nodes, id));
self.node_to_elements = None; idx
}
pub fn add_boundary(
&mut self,
nodes: Vec<usize>,
boundary_type: BoundaryType,
marker: i32,
element_idx: usize,
local_idx: usize,
) {
self.boundaries.push(BoundaryFace {
nodes,
boundary_type,
marker,
element_idx,
local_idx,
});
}
pub fn num_nodes(&self) -> usize {
self.nodes.len()
}
pub fn num_elements(&self) -> usize {
self.elements.len()
}
pub fn node(&self, idx: usize) -> &Point {
&self.nodes[idx]
}
pub fn element(&self, idx: usize) -> &Element {
&self.elements[idx]
}
pub fn build_connectivity(&mut self) {
let mut node_to_elems: Vec<Vec<usize>> = vec![Vec::new(); self.nodes.len()];
for (elem_idx, elem) in self.elements.iter().enumerate() {
for &node_idx in &elem.nodes {
node_to_elems[node_idx].push(elem_idx);
}
}
self.node_to_elements = Some(node_to_elems);
}
pub fn elements_containing_node(&self, node_idx: usize) -> Option<&[usize]> {
self.node_to_elements
.as_ref()
.map(|c| c[node_idx].as_slice())
}
pub fn detect_boundaries(&mut self) {
self.boundaries.clear();
let mut face_count: HashMap<Vec<usize>, (usize, usize)> = HashMap::new();
for (elem_idx, elem) in self.elements.iter().enumerate() {
let faces = self.element_faces(elem);
for mut face in faces {
face.sort();
face_count
.entry(face)
.and_modify(|e| e.1 += 1)
.or_insert((elem_idx, 1));
}
}
for (face_nodes, (elem_idx, count)) in face_count {
if count == 1 {
let elem = &self.elements[elem_idx];
let faces = self.element_faces(elem);
let local_idx = faces
.iter()
.enumerate()
.find(|(_, f)| {
let mut sorted = (*f).clone();
sorted.sort();
sorted == face_nodes
})
.map(|(i, _)| i)
.unwrap_or(0);
self.boundaries.push(BoundaryFace {
nodes: face_nodes,
boundary_type: BoundaryType::Neumann, marker: 0,
element_idx: elem_idx,
local_idx,
});
}
}
}
fn element_faces(&self, elem: &Element) -> Vec<Vec<usize>> {
let verts = elem.vertices();
match elem.element_type {
ElementType::Triangle => vec![
vec![verts[0], verts[1]],
vec![verts[1], verts[2]],
vec![verts[2], verts[0]],
],
ElementType::Quadrilateral => vec![
vec![verts[0], verts[1]],
vec![verts[1], verts[2]],
vec![verts[2], verts[3]],
vec![verts[3], verts[0]],
],
ElementType::Tetrahedron => vec![
vec![verts[0], verts[1], verts[2]],
vec![verts[0], verts[1], verts[3]],
vec![verts[0], verts[2], verts[3]],
vec![verts[1], verts[2], verts[3]],
],
ElementType::Hexahedron => vec![
vec![verts[0], verts[1], verts[2], verts[3]], vec![verts[4], verts[5], verts[6], verts[7]], vec![verts[0], verts[1], verts[5], verts[4]], vec![verts[2], verts[3], verts[7], verts[6]], vec![verts[0], verts[3], verts[7], verts[4]], vec![verts[1], verts[2], verts[6], verts[5]], ],
}
}
pub fn set_boundary_condition<F>(
&mut self,
boundary_type: BoundaryType,
marker: i32,
predicate: F,
) where
F: Fn(&[Point]) -> bool,
{
for boundary in &mut self.boundaries {
let face_points: Vec<Point> = boundary.nodes.iter().map(|&i| self.nodes[i]).collect();
if predicate(&face_points) {
boundary.boundary_type = boundary_type;
boundary.marker = marker;
}
}
}
pub fn boundary_nodes(&self, boundary_type: BoundaryType) -> HashSet<usize> {
let mut nodes = HashSet::new();
for boundary in &self.boundaries {
if boundary.boundary_type == boundary_type {
nodes.extend(boundary.nodes.iter().cloned());
}
}
nodes
}
pub fn element_centroid(&self, elem_idx: usize) -> Point {
let elem = &self.elements[elem_idx];
let verts = elem.vertices();
let n = verts.len() as f64;
let mut cx = 0.0;
let mut cy = 0.0;
let mut cz = 0.0;
for &v in verts {
cx += self.nodes[v].x;
cy += self.nodes[v].y;
cz += self.nodes[v].z;
}
Point::new_3d(cx / n, cy / n, cz / n)
}
pub fn element_measure(&self, elem_idx: usize) -> f64 {
let elem = &self.elements[elem_idx];
let verts: Vec<&Point> = elem.vertices().iter().map(|&i| &self.nodes[i]).collect();
match elem.element_type {
ElementType::Triangle => {
let v1 = (verts[1].x - verts[0].x, verts[1].y - verts[0].y);
let v2 = (verts[2].x - verts[0].x, verts[2].y - verts[0].y);
0.5 * (v1.0 * v2.1 - v1.1 * v2.0).abs()
}
ElementType::Quadrilateral => {
let a1 = {
let v1 = (verts[1].x - verts[0].x, verts[1].y - verts[0].y);
let v2 = (verts[2].x - verts[0].x, verts[2].y - verts[0].y);
0.5 * (v1.0 * v2.1 - v1.1 * v2.0).abs()
};
let a2 = {
let v1 = (verts[2].x - verts[0].x, verts[2].y - verts[0].y);
let v2 = (verts[3].x - verts[0].x, verts[3].y - verts[0].y);
0.5 * (v1.0 * v2.1 - v1.1 * v2.0).abs()
};
a1 + a2
}
ElementType::Tetrahedron => {
let v1 = (
verts[1].x - verts[0].x,
verts[1].y - verts[0].y,
verts[1].z - verts[0].z,
);
let v2 = (
verts[2].x - verts[0].x,
verts[2].y - verts[0].y,
verts[2].z - verts[0].z,
);
let v3 = (
verts[3].x - verts[0].x,
verts[3].y - verts[0].y,
verts[3].z - verts[0].z,
);
let det = v1.0 * (v2.1 * v3.2 - v2.2 * v3.1) - v1.1 * (v2.0 * v3.2 - v2.2 * v3.0)
+ v1.2 * (v2.0 * v3.1 - v2.1 * v3.0);
det.abs() / 6.0
}
ElementType::Hexahedron => {
let _center = Point::new_3d(
verts.iter().map(|v| v.x).sum::<f64>() / 8.0,
verts.iter().map(|v| v.y).sum::<f64>() / 8.0,
verts.iter().map(|v| v.z).sum::<f64>() / 8.0,
);
let dx = verts[1].x - verts[0].x;
let dy = verts[3].y - verts[0].y;
let dz = verts[4].z - verts[0].z;
(dx * dy * dz).abs()
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_point_distance() {
let p1 = Point::new_2d(0.0, 0.0);
let p2 = Point::new_2d(3.0, 4.0);
assert!((p1.distance(&p2) - 5.0).abs() < 1e-10);
}
#[test]
fn test_point_midpoint() {
let p1 = Point::new_2d(0.0, 0.0);
let p2 = Point::new_2d(2.0, 4.0);
let mid = p1.midpoint(&p2);
assert!((mid.x - 1.0).abs() < 1e-10);
assert!((mid.y - 2.0).abs() < 1e-10);
}
#[test]
fn test_element_type_properties() {
assert_eq!(ElementType::Triangle.num_vertices(), 3);
assert_eq!(ElementType::Triangle.num_nodes_p2(), 6);
assert_eq!(ElementType::Tetrahedron.dimension(), 3);
}
#[test]
fn test_mesh_triangle_area() {
let mut mesh = Mesh::new(2);
mesh.add_node(Point::new_2d(0.0, 0.0));
mesh.add_node(Point::new_2d(1.0, 0.0));
mesh.add_node(Point::new_2d(0.0, 1.0));
mesh.add_element(ElementType::Triangle, vec![0, 1, 2]);
let area = mesh.element_measure(0);
assert!((area - 0.5).abs() < 1e-10);
}
}