1use num::{one, zero, FromPrimitive};
7
8use crate::{
9 constants::CustomFloat,
10 data::mc_vector::MCVector,
11 geometry::{
12 facets::{MCGeneralPlane, MCNearestFacet},
13 mc_domain::MCMeshDomain,
14 N_FACETS_OUT, N_POINTS_INTERSEC, N_POINTS_PER_FACET,
15 },
16 particles::mc_particle::MCParticle,
17 utils::mc_rng_state::rng_sample,
18};
19
20pub fn nearest_facet<T: CustomFloat>(
28 particle: &mut MCParticle<T>,
29 mesh: &MCMeshDomain<T>,
30) -> MCNearestFacet<T> {
31 let mut nearest_facet = mct_nf_3dg(particle, mesh);
32
33 nearest_facet.distance_to_facet = nearest_facet.distance_to_facet.max(zero());
34 assert!(nearest_facet.distance_to_facet <= T::huge_float());
35
36 nearest_facet
37}
38
39pub fn generate_coordinate_3dg<T: CustomFloat>(
41 seed: &mut u64,
42 mesh: &MCMeshDomain<T>,
43 cell_idx: usize,
44 cell_volume: T,
45) -> MCVector<T> {
46 let six: T = FromPrimitive::from_f64(6.0).unwrap();
47 let one: T = one();
48
49 let center: MCVector<T> = cell_position_3dg(mesh, cell_idx);
50
51 let which_volume = rng_sample::<T>(seed) * six * cell_volume;
52
53 let mut current_volume: T = zero();
54 let mut points = [MCVector::default(); N_POINTS_PER_FACET];
55
56 for facet_idx in 0..N_FACETS_OUT {
58 points = mesh.get_facet_coords(cell_idx, facet_idx);
59
60 let subvolume = compute_volume(&points, ¢er);
61 current_volume += subvolume;
62 if current_volume >= which_volume {
63 break;
64 }
65 }
66
67 let mut r1: T = rng_sample(seed);
69 let mut r2: T = rng_sample(seed);
70 let mut r3: T = rng_sample(seed);
71 if r1 + r2 > one {
72 r1 = one - r1;
73 r2 = one - r2;
74 }
75 if r2 + r3 > one {
76 let tmp = r3;
77 r3 = one - r1 - r2;
78 r2 = one - tmp;
79 } else if r1 + r2 + r3 > one {
80 let tmp = r3;
81 r3 = r1 + r2 + r3 - one;
82 r1 = one - r2 - tmp;
83 }
84 let r4: T = one - r1 - r2 - r3;
85
86 points[0] * r1 + points[1] * r2 + points[2] * r3 + center * r4
87}
88
89pub fn cell_position_3dg<T: CustomFloat>(mesh: &MCMeshDomain<T>, cell_idx: usize) -> MCVector<T> {
91 let mut coordinate: MCVector<T> = mesh.cell_connectivity[cell_idx]
92 .point
93 .map(|point_idx| mesh.node[point_idx])
94 .iter()
95 .sum();
96
97 coordinate /= FromPrimitive::from_usize(N_POINTS_INTERSEC).unwrap();
98
99 coordinate
100}
101
102pub fn reflect_particle<T: CustomFloat>(particle: &mut MCParticle<T>, plane: &MCGeneralPlane<T>) {
108 let facet_normal: MCVector<T> = MCVector {
109 x: plane.a,
110 y: plane.b,
111 z: plane.c,
112 };
113
114 let two: T = FromPrimitive::from_f64(2.0).unwrap();
115 let dot: T = two * particle.direction.dot(&facet_normal);
116
117 if dot > zero() {
118 particle.direction -= facet_normal * dot;
119 }
120}
121
122fn mct_nf_3dg<T: CustomFloat>(
127 particle: &mut MCParticle<T>,
128 mesh: &MCMeshDomain<T>,
129) -> MCNearestFacet<T> {
130 let coords = particle.coordinate;
131 let direction = particle.direction;
132
133 let mut facet_coords: [MCVector<T>; N_POINTS_PER_FACET] = Default::default();
134 let mut iteration: usize = 0;
135 let mut move_factor: T = <T as FromPrimitive>::from_f64(0.5).unwrap() * T::small_float();
136
137 let tmp: T = FromPrimitive::from_f64(1e-16).unwrap();
138 let plane_tolerance: T =
139 tmp * (coords.x * coords.x + coords.y * coords.y + coords.z * coords.z);
140
141 let planes = &mesh.cell_geometry[particle.cell];
142 let mut distance_to_facet: [T; N_FACETS_OUT] = [T::huge_float(); N_FACETS_OUT];
143
144 loop {
145 distance_to_facet.fill(T::huge_float());
148 distance_to_facet
149 .iter_mut()
150 .enumerate()
151 .zip(planes.iter())
152 .for_each(|((facet_idx, dist), plane)| {
153 let numerator: T = -one::<T>()
154 * (plane.a * coords.x + plane.b * coords.y + plane.c * coords.z + plane.d);
155 let facet_normal_dot_dcos: T =
156 plane.a * direction.x + plane.b * direction.y + plane.c * direction.z;
157
158 if (facet_normal_dot_dcos <= zero())
159 | (numerator < zero()) & (numerator * numerator > plane_tolerance)
160 {
161 return;
162 }
163
164 facet_coords = mesh.get_facet_coords(particle.cell, facet_idx);
165 let distance = numerator / facet_normal_dot_dcos;
166 let intersection_pt: MCVector<T> = coords + direction * distance;
167
168 if mct_nf_3dg_dist_to_segment(&intersection_pt, plane, &facet_coords) {
169 *dist = distance;
170 }
171 });
172
173 let nearest_facet = mct_nf_compute_nearest(distance_to_facet);
174 let retry = check_nearest_validity(
175 particle,
176 mesh,
177 &mut iteration,
178 &mut move_factor,
179 &nearest_facet,
180 );
181
182 if !retry {
183 return nearest_facet;
184 }
185 }
186}
187
188fn compute_volume<T: CustomFloat>(vertices: &[MCVector<T>], origin: &MCVector<T>) -> T {
191 assert_eq!(vertices.len(), N_POINTS_PER_FACET);
192 let tmp0 = vertices[0] - *origin;
193 let tmp1 = vertices[1] - *origin;
194 let tmp2 = vertices[2] - *origin;
195
196 tmp0.dot(&tmp1.cross(&tmp2)) }
198
199fn mct_nf_compute_nearest<T: CustomFloat>(
200 distance_to_facet: [T; N_FACETS_OUT],
201) -> MCNearestFacet<T> {
202 let huge_f: T = T::huge_float();
203 let mut nearest_facet: MCNearestFacet<T> = Default::default();
204 let mut nearest_negative_facet: MCNearestFacet<T> = MCNearestFacet {
205 distance_to_facet: -huge_f,
206 ..Default::default()
207 };
208
209 distance_to_facet
211 .iter()
212 .enumerate()
213 .filter(|(_, dist)| **dist > zero())
214 .for_each(|(facet_idx, dist)| {
215 if *dist <= nearest_facet.distance_to_facet {
216 nearest_facet.distance_to_facet = *dist;
217 nearest_facet.facet = facet_idx;
218 }
219 });
220
221 if nearest_facet.distance_to_facet != huge_f {
222 return nearest_facet;
223 }
224
225 distance_to_facet
226 .iter()
227 .enumerate()
228 .filter(|(_, dist)| **dist <= zero())
229 .for_each(|(facet_idx, dist)| {
230 if *dist > nearest_negative_facet.distance_to_facet {
231 nearest_negative_facet.distance_to_facet = *dist;
232 nearest_negative_facet.facet = facet_idx;
233 }
234 });
235
236 if nearest_negative_facet.distance_to_facet != -huge_f {
237 return nearest_negative_facet;
238 }
239
240 nearest_facet
241}
242
243fn check_nearest_validity<T: CustomFloat>(
244 particle: &mut MCParticle<T>,
245 mesh: &MCMeshDomain<T>,
246 iteration: &mut usize,
247 move_factor: &mut T,
248 nearest_facet: &MCNearestFacet<T>,
249) -> bool {
250 const MAX_ALLOWED_SEGMENTS: usize = 10000000;
251 const MAX_ITERATION: usize = 1000;
252 let max: T = FromPrimitive::from_usize(MAX_ALLOWED_SEGMENTS).unwrap();
253
254 let coord = &mut particle.coordinate;
255
256 if (nearest_facet.distance_to_facet == T::huge_float())
257 | ((particle.num_segments > max) & (nearest_facet.distance_to_facet <= zero()))
258 {
259 let two: T = FromPrimitive::from_f64(2.0).unwrap();
260 let threshold: T = FromPrimitive::from_f64(1.0e-2).unwrap();
261
262 let move_to = cell_position_3dg(mesh, particle.cell);
264 *coord += (move_to - *coord) * *move_factor;
265
266 *iteration += 1;
268 *move_factor = threshold.min(*move_factor * two);
269
270 return *iteration != MAX_ITERATION;
271 }
272 false
273}
274
275fn mct_nf_3dg_dist_to_segment<T: CustomFloat>(
276 intersection_pt: &MCVector<T>,
277 plane: &MCGeneralPlane<T>,
278 facet_coords: &[MCVector<T>],
279) -> bool {
280 let pfive: T = FromPrimitive::from_f64(0.5).unwrap();
281 let bounding_box_tolerance: T = FromPrimitive::from_f64(1e-9).unwrap();
282
283 macro_rules! belongs_or_return {
285 ($axis: ident) => {
286 let below: bool = (facet_coords[0].$axis
287 > intersection_pt.$axis + bounding_box_tolerance)
288 & (facet_coords[1].$axis > intersection_pt.$axis + bounding_box_tolerance)
289 & (facet_coords[2].$axis > intersection_pt.$axis + bounding_box_tolerance);
290 let above: bool = (facet_coords[0].$axis
291 < intersection_pt.$axis - bounding_box_tolerance)
292 & (facet_coords[1].$axis < intersection_pt.$axis - bounding_box_tolerance)
293 & (facet_coords[2].$axis < intersection_pt.$axis - bounding_box_tolerance);
294 if below | above {
295 return false;
297 }
298 };
299 }
300
301 macro_rules! ab_cross_ac {
303 ($ax: expr, $ay: expr, $bx: expr, $by: expr, $cx: expr, $cy: expr) => {
304 ($bx - $ax) * ($cy - $ay) - ($by - $ay) * ($cx - $ax)
305 };
306 }
307
308 let crosses = if plane.c.abs() > pfive {
309 belongs_or_return!(x);
310 belongs_or_return!(y);
311 [
313 ab_cross_ac!(
314 facet_coords[0].x,
315 facet_coords[0].y,
316 facet_coords[1].x,
317 facet_coords[1].y,
318 intersection_pt.x,
319 intersection_pt.y
320 ),
321 ab_cross_ac!(
322 facet_coords[1].x,
323 facet_coords[1].y,
324 facet_coords[2].x,
325 facet_coords[2].y,
326 intersection_pt.x,
327 intersection_pt.y
328 ),
329 ab_cross_ac!(
330 facet_coords[2].x,
331 facet_coords[2].y,
332 facet_coords[0].x,
333 facet_coords[0].y,
334 intersection_pt.x,
335 intersection_pt.y
336 ),
337 ]
338 } else if plane.b.abs() > pfive {
339 belongs_or_return!(x);
340 belongs_or_return!(z);
341 [
343 ab_cross_ac!(
344 facet_coords[0].z,
345 facet_coords[0].x,
346 facet_coords[1].z,
347 facet_coords[1].x,
348 intersection_pt.z,
349 intersection_pt.x
350 ),
351 ab_cross_ac!(
352 facet_coords[1].z,
353 facet_coords[1].x,
354 facet_coords[2].z,
355 facet_coords[2].x,
356 intersection_pt.z,
357 intersection_pt.x
358 ),
359 ab_cross_ac!(
360 facet_coords[2].z,
361 facet_coords[2].x,
362 facet_coords[0].z,
363 facet_coords[0].x,
364 intersection_pt.z,
365 intersection_pt.x
366 ),
367 ]
368 } else if plane.a.abs() > pfive {
369 belongs_or_return!(z);
370 belongs_or_return!(y);
371 [
373 ab_cross_ac!(
374 facet_coords[0].y,
375 facet_coords[0].z,
376 facet_coords[1].y,
377 facet_coords[1].z,
378 intersection_pt.y,
379 intersection_pt.z
380 ),
381 ab_cross_ac!(
382 facet_coords[1].y,
383 facet_coords[1].z,
384 facet_coords[2].y,
385 facet_coords[2].z,
386 intersection_pt.y,
387 intersection_pt.z
388 ),
389 ab_cross_ac!(
390 facet_coords[2].y,
391 facet_coords[2].z,
392 facet_coords[0].y,
393 facet_coords[0].z,
394 intersection_pt.y,
395 intersection_pt.z
396 ),
397 ]
398 } else {
399 [zero(); 3]
400 };
401
402 let cross_tolerance: T = bounding_box_tolerance * (crosses[0] + crosses[1] + crosses[2]).abs();
403
404 if ((crosses[0] > -cross_tolerance)
405 & (crosses[1] > -cross_tolerance)
406 & (crosses[2] > -cross_tolerance))
407 | ((crosses[0] < cross_tolerance)
408 & (crosses[1] < cross_tolerance)
409 & (crosses[2] < cross_tolerance))
410 {
411 return true;
412 }
413 false
414}