1use ndarray::{Array1, Array2};
7use num_complex::Complex64;
8use std::f64::consts::PI;
9
10use crate::core::types::{BoundaryCondition, Element, ElementProperty, ElementType, Mesh};
11
12pub fn generate_sphere_mesh(radius: f64, n_theta: usize, n_phi: usize) -> Mesh {
30 let mut nodes = Vec::new();
31 let mut elements = Vec::new();
32
33 nodes.push([0.0, 0.0, radius]);
36
37 for i in 1..n_theta {
39 let theta = PI * i as f64 / n_theta as f64;
40 let sin_theta = theta.sin();
41 let cos_theta = theta.cos();
42
43 for j in 0..n_phi {
44 let phi = 2.0 * PI * j as f64 / n_phi as f64;
45 let x = radius * sin_theta * phi.cos();
46 let y = radius * sin_theta * phi.sin();
47 let z = radius * cos_theta;
48 nodes.push([x, y, z]);
49 }
50 }
51
52 nodes.push([0.0, 0.0, -radius]);
54
55 let n_nodes = nodes.len();
56 let south_pole_idx = n_nodes - 1;
57
58 for j in 0..n_phi {
61 let j_next = (j + 1) % n_phi;
62 elements.push(vec![0, 1 + j, 1 + j_next]);
63 }
64
65 for i in 0..(n_theta - 2) {
67 let row_start = 1 + i * n_phi;
68 let next_row_start = 1 + (i + 1) * n_phi;
69
70 for j in 0..n_phi {
71 let j_next = (j + 1) % n_phi;
72
73 let n0 = row_start + j;
75 let n1 = row_start + j_next;
76 let n2 = next_row_start + j;
78 let n3 = next_row_start + j_next;
79
80 elements.push(vec![n0, n2, n1]);
82 elements.push(vec![n1, n2, n3]);
83 }
84 }
85
86 let last_row_start = 1 + (n_theta - 2) * n_phi;
88 for j in 0..n_phi {
89 let j_next = (j + 1) % n_phi;
90 elements.push(vec![
91 last_row_start + j,
92 south_pole_idx,
93 last_row_start + j_next,
94 ]);
95 }
96
97 create_mesh_from_data(nodes, elements, radius)
98}
99
100pub fn generate_icosphere_mesh(radius: f64, subdivisions: usize) -> Mesh {
111 let phi = (1.0 + 5.0_f64.sqrt()) / 2.0;
113 let _scale = radius / (1.0 + phi * phi).sqrt();
114
115 let mut vertices: Vec<[f64; 3]> = vec![
117 [-1.0, phi, 0.0],
118 [1.0, phi, 0.0],
119 [-1.0, -phi, 0.0],
120 [1.0, -phi, 0.0],
121 [0.0, -1.0, phi],
122 [0.0, 1.0, phi],
123 [0.0, -1.0, -phi],
124 [0.0, 1.0, -phi],
125 [phi, 0.0, -1.0],
126 [phi, 0.0, 1.0],
127 [-phi, 0.0, -1.0],
128 [-phi, 0.0, 1.0],
129 ];
130
131 for v in &mut vertices {
133 let len = (v[0] * v[0] + v[1] * v[1] + v[2] * v[2]).sqrt();
134 v[0] /= len;
135 v[1] /= len;
136 v[2] /= len;
137 }
138
139 let mut faces: Vec<[usize; 3]> = vec![
141 [0, 11, 5],
142 [0, 5, 1],
143 [0, 1, 7],
144 [0, 7, 10],
145 [0, 10, 11],
146 [1, 5, 9],
147 [5, 11, 4],
148 [11, 10, 2],
149 [10, 7, 6],
150 [7, 1, 8],
151 [3, 9, 4],
152 [3, 4, 2],
153 [3, 2, 6],
154 [3, 6, 8],
155 [3, 8, 9],
156 [4, 9, 5],
157 [2, 4, 11],
158 [6, 2, 10],
159 [8, 6, 7],
160 [9, 8, 1],
161 ];
162
163 for _ in 0..subdivisions {
165 let mut new_faces = Vec::new();
166 let mut edge_midpoints: std::collections::HashMap<(usize, usize), usize> =
167 std::collections::HashMap::new();
168
169 for face in &faces {
170 let v0 = face[0];
171 let v1 = face[1];
172 let v2 = face[2];
173
174 let m01 = get_midpoint(&mut vertices, &mut edge_midpoints, v0, v1);
176 let m12 = get_midpoint(&mut vertices, &mut edge_midpoints, v1, v2);
177 let m20 = get_midpoint(&mut vertices, &mut edge_midpoints, v2, v0);
178
179 new_faces.push([v0, m01, m20]);
181 new_faces.push([v1, m12, m01]);
182 new_faces.push([v2, m20, m12]);
183 new_faces.push([m01, m12, m20]);
184 }
185
186 faces = new_faces;
187 }
188
189 let nodes: Vec<[f64; 3]> = vertices
191 .iter()
192 .map(|v| [v[0] * radius, v[1] * radius, v[2] * radius])
193 .collect();
194
195 let elements: Vec<Vec<usize>> = faces.iter().map(|f| vec![f[0], f[1], f[2]]).collect();
196
197 create_mesh_from_data(nodes, elements, radius)
198}
199
200fn get_midpoint(
202 vertices: &mut Vec<[f64; 3]>,
203 cache: &mut std::collections::HashMap<(usize, usize), usize>,
204 v0: usize,
205 v1: usize,
206) -> usize {
207 let key = if v0 < v1 { (v0, v1) } else { (v1, v0) };
208
209 if let Some(&idx) = cache.get(&key) {
210 return idx;
211 }
212
213 let mid = [
215 (vertices[v0][0] + vertices[v1][0]) / 2.0,
216 (vertices[v0][1] + vertices[v1][1]) / 2.0,
217 (vertices[v0][2] + vertices[v1][2]) / 2.0,
218 ];
219
220 let len = (mid[0] * mid[0] + mid[1] * mid[1] + mid[2] * mid[2]).sqrt();
221 let normalized = [mid[0] / len, mid[1] / len, mid[2] / len];
222
223 let idx = vertices.len();
224 vertices.push(normalized);
225 cache.insert(key, idx);
226
227 idx
228}
229
230pub fn generate_cylinder_mesh(
243 radius: f64,
244 height: f64,
245 n_circumference: usize,
246 n_height: usize,
247) -> Mesh {
248 let mut nodes = Vec::new();
249 let mut elements = Vec::new();
250
251 let z_min = -height / 2.0;
252 let dz = height / n_height as f64;
253
254 for i in 0..=n_height {
256 let z = z_min + i as f64 * dz;
257
258 for j in 0..n_circumference {
259 let phi = 2.0 * PI * j as f64 / n_circumference as f64;
260 let x = radius * phi.cos();
261 let y = radius * phi.sin();
262 nodes.push([x, y, z]);
263 }
264 }
265
266 for i in 0..n_height {
268 let row_start = i * n_circumference;
269 let next_row_start = (i + 1) * n_circumference;
270
271 for j in 0..n_circumference {
272 let j_next = (j + 1) % n_circumference;
273
274 let n0 = row_start + j;
275 let n1 = row_start + j_next;
276 let n2 = next_row_start + j_next;
277 let n3 = next_row_start + j;
278
279 elements.push(vec![n0, n1, n2, n3]);
280 }
281 }
282
283 create_mesh_from_data(nodes, elements, radius)
284}
285
286pub fn generate_closed_cylinder_mesh(
288 radius: f64,
289 height: f64,
290 n_circumference: usize,
291 n_height: usize,
292 n_cap_rings: usize,
293) -> Mesh {
294 let mut nodes = Vec::new();
295 let mut elements = Vec::new();
296
297 let z_min = -height / 2.0;
298 let z_max = height / 2.0;
299 let dz = height / n_height as f64;
300
301 for i in 0..=n_height {
303 let z = z_min + i as f64 * dz;
304 for j in 0..n_circumference {
305 let phi = 2.0 * PI * j as f64 / n_circumference as f64;
306 nodes.push([radius * phi.cos(), radius * phi.sin(), z]);
307 }
308 }
309
310 let _lateral_node_count = nodes.len();
311
312 let bottom_center_idx = nodes.len();
314 nodes.push([0.0, 0.0, z_min]);
315
316 for ring in 1..=n_cap_rings {
318 let r = radius * ring as f64 / n_cap_rings as f64;
319 for j in 0..n_circumference {
320 let phi = 2.0 * PI * j as f64 / n_circumference as f64;
321 nodes.push([r * phi.cos(), r * phi.sin(), z_min]);
322 }
323 }
324
325 let top_center_idx = nodes.len();
327 nodes.push([0.0, 0.0, z_max]);
328
329 for ring in 1..=n_cap_rings {
331 let r = radius * ring as f64 / n_cap_rings as f64;
332 for j in 0..n_circumference {
333 let phi = 2.0 * PI * j as f64 / n_circumference as f64;
334 nodes.push([r * phi.cos(), r * phi.sin(), z_max]);
335 }
336 }
337
338 for i in 0..n_height {
340 let row_start = i * n_circumference;
341 let next_row_start = (i + 1) * n_circumference;
342 for j in 0..n_circumference {
343 let j_next = (j + 1) % n_circumference;
344 elements.push(vec![
345 row_start + j,
346 row_start + j_next,
347 next_row_start + j_next,
348 next_row_start + j,
349 ]);
350 }
351 }
352
353 let bottom_ring1_start = bottom_center_idx + 1;
356 for j in 0..n_circumference {
357 let j_next = (j + 1) % n_circumference;
358 elements.push(vec![
359 bottom_center_idx,
360 bottom_ring1_start + j_next,
361 bottom_ring1_start + j,
362 ]);
363 }
364
365 for ring in 0..(n_cap_rings - 1) {
367 let ring_start = bottom_center_idx + 1 + ring * n_circumference;
368 let next_ring_start = ring_start + n_circumference;
369 for j in 0..n_circumference {
370 let j_next = (j + 1) % n_circumference;
371 elements.push(vec![
372 ring_start + j,
373 ring_start + j_next,
374 next_ring_start + j_next,
375 next_ring_start + j,
376 ]);
377 }
378 }
379
380 let outer_bottom_ring = bottom_center_idx + 1 + (n_cap_rings - 1) * n_circumference;
382 for j in 0..n_circumference {
383 let j_next = (j + 1) % n_circumference;
384 elements.push(vec![
385 outer_bottom_ring + j,
386 outer_bottom_ring + j_next,
387 j_next,
388 j,
389 ]);
390 }
391
392 let top_ring1_start = top_center_idx + 1;
394 for j in 0..n_circumference {
395 let j_next = (j + 1) % n_circumference;
396 elements.push(vec![
397 top_center_idx,
398 top_ring1_start + j,
399 top_ring1_start + j_next,
400 ]);
401 }
402
403 for ring in 0..(n_cap_rings - 1) {
405 let ring_start = top_center_idx + 1 + ring * n_circumference;
406 let next_ring_start = ring_start + n_circumference;
407 for j in 0..n_circumference {
408 let j_next = (j + 1) % n_circumference;
409 elements.push(vec![
410 ring_start + j,
411 next_ring_start + j,
412 next_ring_start + j_next,
413 ring_start + j_next,
414 ]);
415 }
416 }
417
418 let outer_top_ring = top_center_idx + 1 + (n_cap_rings - 1) * n_circumference;
420 let top_lateral_row = n_height * n_circumference;
421 for j in 0..n_circumference {
422 let j_next = (j + 1) % n_circumference;
423 elements.push(vec![
424 top_lateral_row + j,
425 top_lateral_row + j_next,
426 outer_top_ring + j_next,
427 outer_top_ring + j,
428 ]);
429 }
430
431 create_mesh_from_data(nodes, elements, radius)
432}
433
434fn create_mesh_from_data(
436 nodes: Vec<[f64; 3]>,
437 connectivity: Vec<Vec<usize>>,
438 _char_length: f64,
439) -> Mesh {
440 let n_nodes = nodes.len();
441 let n_elements = connectivity.len();
442
443 let mut node_array = Array2::zeros((n_nodes, 3));
445 for (i, node) in nodes.iter().enumerate() {
446 node_array[[i, 0]] = node[0];
447 node_array[[i, 1]] = node[1];
448 node_array[[i, 2]] = node[2];
449 }
450
451 let mut elements = Vec::with_capacity(n_elements);
453 for (idx, conn) in connectivity.iter().enumerate() {
454 let element_type = if conn.len() == 3 {
455 ElementType::Tri3
456 } else {
457 ElementType::Quad4
458 };
459
460 let mut elem = Element {
461 connectivity: conn.clone(),
462 element_type,
463 property: ElementProperty::Surface,
464 normal: Array1::zeros(3),
465 node_normals: Array2::zeros((element_type.num_nodes(), 3)),
466 center: Array1::zeros(3),
467 area: 0.0,
468 boundary_condition: BoundaryCondition::Velocity(vec![Complex64::new(0.0, 0.0)]),
469 group: 0,
470 dof_addresses: vec![idx],
471 };
472
473 compute_element_geometry(&mut elem, &node_array);
475 elements.push(elem);
476 }
477
478 let mut node_counts = vec![0usize; n_nodes];
480
481 for elem in &elements {
482 for &node_idx in &elem.connectivity {
483 node_counts[node_idx] += 1;
484 }
485 }
486
487 for elem in &mut elements {
489 for (local_idx, &_node_idx) in elem.connectivity.iter().enumerate() {
490 for j in 0..3 {
492 elem.node_normals[[local_idx, j]] = elem.normal[j];
493 }
494 }
495 }
496
497 Mesh {
498 nodes: node_array,
499 elements,
500 external_node_numbers: (0..n_nodes as i32).collect(),
501 external_element_numbers: (0..n_elements as i32).collect(),
502 num_boundary_nodes: n_nodes,
503 num_evaluation_nodes: 0,
504 num_boundary_elements: n_elements,
505 num_evaluation_elements: 0,
506 symmetry_planes: [0, 0, 0],
507 symmetry_coordinates: [0.0, 0.0, 0.0],
508 num_reflections: 0,
509 }
510}
511
512fn compute_element_geometry(elem: &mut Element, nodes: &Array2<f64>) {
514 let n = elem.connectivity.len();
515
516 elem.center = Array1::zeros(3);
518 for &node_idx in &elem.connectivity {
519 for j in 0..3 {
520 elem.center[j] += nodes[[node_idx, j]];
521 }
522 }
523 for j in 0..3 {
524 elem.center[j] /= n as f64;
525 }
526
527 if n == 3 {
529 let n0 = elem.connectivity[0];
531 let n1 = elem.connectivity[1];
532 let n2 = elem.connectivity[2];
533
534 let v1 = [
535 nodes[[n1, 0]] - nodes[[n0, 0]],
536 nodes[[n1, 1]] - nodes[[n0, 1]],
537 nodes[[n1, 2]] - nodes[[n0, 2]],
538 ];
539 let v2 = [
540 nodes[[n2, 0]] - nodes[[n0, 0]],
541 nodes[[n2, 1]] - nodes[[n0, 1]],
542 nodes[[n2, 2]] - nodes[[n0, 2]],
543 ];
544
545 let cross = [
547 v1[1] * v2[2] - v1[2] * v2[1],
548 v1[2] * v2[0] - v1[0] * v2[2],
549 v1[0] * v2[1] - v1[1] * v2[0],
550 ];
551
552 let len = (cross[0] * cross[0] + cross[1] * cross[1] + cross[2] * cross[2]).sqrt();
553 elem.area = len / 2.0;
554
555 if len > 1e-15 {
556 elem.normal = Array1::from_vec(vec![cross[0] / len, cross[1] / len, cross[2] / len]);
557 }
558 } else {
559 let n0 = elem.connectivity[0];
561 let n1 = elem.connectivity[1];
562 let n2 = elem.connectivity[2];
563 let n3 = elem.connectivity[3];
564
565 let d1 = [
566 nodes[[n2, 0]] - nodes[[n0, 0]],
567 nodes[[n2, 1]] - nodes[[n0, 1]],
568 nodes[[n2, 2]] - nodes[[n0, 2]],
569 ];
570 let d2 = [
571 nodes[[n3, 0]] - nodes[[n1, 0]],
572 nodes[[n3, 1]] - nodes[[n1, 1]],
573 nodes[[n3, 2]] - nodes[[n1, 2]],
574 ];
575
576 let cross = [
577 d1[1] * d2[2] - d1[2] * d2[1],
578 d1[2] * d2[0] - d1[0] * d2[2],
579 d1[0] * d2[1] - d1[1] * d2[0],
580 ];
581
582 let len = (cross[0] * cross[0] + cross[1] * cross[1] + cross[2] * cross[2]).sqrt();
583 elem.area = len / 2.0; if len > 1e-15 {
586 elem.normal = Array1::from_vec(vec![cross[0] / len, cross[1] / len, cross[2] / len]);
587 }
588 }
589
590 let n_dot_c = elem.normal[0] * elem.center[0]
593 + elem.normal[1] * elem.center[1]
594 + elem.normal[2] * elem.center[2];
595
596 if n_dot_c < 0.0 {
597 elem.normal[0] = -elem.normal[0];
599 elem.normal[1] = -elem.normal[1];
600 elem.normal[2] = -elem.normal[2];
601 }
602}
603
604#[cfg(test)]
605mod tests {
606 use super::*;
607
608 #[test]
609 fn test_sphere_mesh_generation() {
610 let mesh = generate_sphere_mesh(1.0, 8, 16);
611
612 assert!(mesh.nodes.nrows() > 0);
613 assert!(!mesh.elements.is_empty());
614
615 for i in 0..mesh.nodes.nrows() {
617 let r = (mesh.nodes[[i, 0]].powi(2)
618 + mesh.nodes[[i, 1]].powi(2)
619 + mesh.nodes[[i, 2]].powi(2))
620 .sqrt();
621 assert!((r - 1.0).abs() < 1e-10, "Node {} not on sphere: r={}", i, r);
622 }
623
624 for elem in &mesh.elements {
626 assert_eq!(elem.connectivity.len(), 3);
627 assert!(elem.area > 0.0);
628 }
629 }
630
631 #[test]
632 fn test_icosphere_mesh_generation() {
633 let mesh = generate_icosphere_mesh(1.0, 2);
634
635 assert_eq!(mesh.nodes.nrows(), 162);
637
638 for i in 0..mesh.nodes.nrows() {
640 let r = (mesh.nodes[[i, 0]].powi(2)
641 + mesh.nodes[[i, 1]].powi(2)
642 + mesh.nodes[[i, 2]].powi(2))
643 .sqrt();
644 assert!((r - 1.0).abs() < 1e-10);
645 }
646 }
647
648 #[test]
649 fn test_cylinder_mesh_generation() {
650 let mesh = generate_cylinder_mesh(1.0, 2.0, 16, 8);
651
652 assert!(mesh.nodes.nrows() > 0);
653 assert!(!mesh.elements.is_empty());
654
655 for i in 0..mesh.nodes.nrows() {
657 let r = (mesh.nodes[[i, 0]].powi(2) + mesh.nodes[[i, 1]].powi(2)).sqrt();
658 assert!(
659 (r - 1.0).abs() < 1e-10,
660 "Node {} not on cylinder: r={}",
661 i,
662 r
663 );
664 }
665
666 for elem in &mesh.elements {
668 assert_eq!(elem.connectivity.len(), 4);
669 assert!(elem.area > 0.0);
670 }
671 }
672
673 #[test]
674 fn test_sphere_normals_point_outward() {
675 let mesh = generate_icosphere_mesh(1.0, 1);
676
677 for elem in &mesh.elements {
678 let dot = elem.normal[0] * elem.center[0]
680 + elem.normal[1] * elem.center[1]
681 + elem.normal[2] * elem.center[2];
682 assert!(dot > 0.0, "Normal should point outward");
683 }
684 }
685
686 #[test]
687 fn test_sphere_surface_area() {
688 let mesh = generate_icosphere_mesh(1.0, 4);
690
691 let total_area: f64 = mesh.elements.iter().map(|e| e.area).sum();
692 let expected_area = 4.0 * PI; let error = (total_area - expected_area).abs() / expected_area;
695 assert!(error < 0.01, "Surface area error: {:.2}%", error * 100.0);
696 }
697}