1 /*============================================================================
2 * STL reader and writer.
3 *============================================================================*/
4
5 /*
6 This file is part of Code_Saturne, a general-purpose CFD tool.
7
8 Copyright (C) 1998-2021 EDF S.A.
9
10 This program is free software; you can redistribute it and/or modify it under
11 the terms of the GNU General Public License as published by the Free Software
12 Foundation; either version 2 of the License, or (at your option) any later
13 version.
14
15 This program is distributed in the hope that it will be useful, but WITHOUT
16 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
18 details.
19
20 You should have received a copy of the GNU General Public License along with
21 this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
22 Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 */
24
25 /*----------------------------------------------------------------------------*/
26
27 #include "cs_defs.h"
28
29 /*----------------------------------------------------------------------------
30 * Standard C library headers
31 *----------------------------------------------------------------------------*/
32
33 #include <assert.h>
34 #include <errno.h>
35 #include <math.h>
36 #include <stdarg.h>
37 #include <stdlib.h>
38 #include <stdio.h>
39 #include <string.h>
40
41 /*----------------------------------------------------------------------------
42 * Local headers
43 *----------------------------------------------------------------------------*/
44
45 #include "bft_mem.h"
46 #include "bft_error.h"
47 #include "bft_printf.h"
48
49 #include "fvm_nodal.h"
50 #include "fvm_writer.h"
51 #include "cs_post.h"
52
53 #include "fvm_nodal_append.h"
54 #include "fvm_neighborhood.h"
55
56 #include "cs_math.h"
57 #include "cs_mesh_headers.h"
58 #include "cs_order.h"
59 #include "cs_parall.h"
60 #include "cs_rotation.h"
61
62 /*----------------------------------------------------------------------------
63 * Header of the current file
64 *----------------------------------------------------------------------------*/
65
66 #include "cs_stl.h"
67
68 /*----------------------------------------------------------------------------*/
69
70 BEGIN_C_DECLS
71
72 /*! \cond DOXYGEN_SHOULD_SKIP_THIS */
73
74 /*=============================================================================
75 * Static global variables
76 *============================================================================*/
77
78 static cs_stl_mesh_info_t _stl_meshes = {NULL, 0, 0};
79
80 cs_stl_mesh_info_t *cs_glob_stl_meshes = &_stl_meshes;
81
82 /*=============================================================================
83 * Private function definitions
84 *============================================================================*/
85
86 /*----------------------------------------------------------------------------
87 * Function to cast 4 uint8_t value into one single uint32_t value
88 *
89 * parameters:
90 * val <-- array of 4 uint8_t
91 *
92 * returns :
93 * the uint32_t corresponding value
94 ----------------------------------------------------------------------------*/
95
96 static uint32_t
_cast32(uint8_t * val)97 _cast32(uint8_t *val)
98 {
99 uint32_t retval;
100
101 /* We have here 4 8bits unsigned int
102 * and we cast then in a single 32bit
103 * unsigned int by progessive shifts */
104
105 retval = (uint32_t)val[0]
106 + ( (uint32_t)val[1]<<8 )
107 + ( (uint32_t)val[2]<<16 )
108 + ( (uint32_t)val[3]<<24 );
109
110 return retval;
111 }
112
113 /*----------------------------------------------------------------------------
114 * Function to cut one uint32_t value into an array of 4 uint8_t value
115 *
116 * parameters:
117 * out --> cut array of 4 uint8_t
118 * val <-- input uint32_t value
119 ----------------------------------------------------------------------------*/
120
121 static void
_cut32(uint8_t * out,int val)122 _cut32(uint8_t *out, int val)
123 {
124 out[0] = (val ) & 0xff;
125 out[1] = (val >> 8 ) & 0xff;
126 out[2] = (val >> 16 ) & 0xff;
127 out[3] = (val >> 24 ) & 0xff;
128 }
129
130 /*----------------------------------------------------------------------------
131 * Function to compute the normal of a triangle defined by 3 points
132 *
133 * parameters:
134 * coords <-- triangle coordinates
135 * normal --> triangle normal
136 ----------------------------------------------------------------------------*/
137
138 static void
_compute_normal(cs_real_t coords[3][3],cs_real_t normal[3])139 _compute_normal(cs_real_t coords[3][3],
140 cs_real_t normal[3])
141 {
142 /* Compute and Write the face normal coordinates */
143 const cs_real_t *a = coords[0], *b = coords[1], *c = coords[2];
144
145 // ab vect ac
146 normal[0] = (b[1]-a[1])*(c[2]-a[2]) - (b[2]-a[2])*(c[1]-a[1]);
147 normal[1] = (b[2]-a[2])*(c[0]-a[0]) - (b[0]-a[0])*(c[2]-a[2]);
148 normal[2] = (b[0]-a[0])*(c[1]-a[1]) - (b[1]-a[1])*(c[0]-a[0]);
149
150 cs_real_t norm = cs_math_3_norm(normal);
151
152 for (int dir = 0; dir < 3; dir ++)
153 normal[dir] /= norm;
154 }
155
156 /*----------------------------------------------------------------------------
157 * Function to check if an AABB (Axis Aligned Bounding Box) is overlaped
158 * by a triangle. It is based on the SAT (Separating Axis Theorem).
159 *
160 * Warning : the user must first be sure that the bounding boxes of
161 * the AABB and the triangle intersects, otherwise, this functions might
162 * give wrong results.
163 *
164 * parameters:
165 * box_extents <-- coordinates of the bounding box (xmin,...,xmax,...)
166 * tria_coords <-- coordinates of the triangle vertices
167 *
168 * returns:
169 * a boolean
170 ----------------------------------------------------------------------------*/
171
172 static bool
_triangle_box_intersect(const cs_real_t box_extents[6],const cs_real_t tria_coords[3][3])173 _triangle_box_intersect(const cs_real_t box_extents[6],
174 const cs_real_t tria_coords[3][3])
175 {
176
177 // Origin axis
178 cs_real_t e[3][3] = {{1.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {0.0, 0.0, 1.0}};
179
180 // Width of the box, center of the box
181 cs_real_t h[3], c[3];
182 h[0] = 0.5*(box_extents[3]-box_extents[0]);
183 h[1] = 0.5*(box_extents[4]-box_extents[1]);
184 h[2] = 0.5*(box_extents[5]-box_extents[2]);
185
186 c[0] = box_extents[0] + h[0];
187 c[1] = box_extents[1] + h[1];
188 c[2] = box_extents[2] + h[2];
189
190 // Move triangle so that the box is virtually centered on 0
191 cs_real_t v[3][3];
192 for (int vi = 0; vi < 3; vi++) {
193 for (int dir = 0; dir < 3; dir++)
194 v[vi][dir] = tria_coords[vi][dir] - c[dir];
195 }
196
197 // Compute triangle edges
198 cs_real_t f[3][3];
199 for (int dir = 0; dir < 3; dir++) {
200 f[0][dir] = v[1][dir] - v[0][dir];
201 f[1][dir] = v[2][dir] - v[1][dir];
202 f[2][dir] = v[0][dir] - v[2][dir];
203 }
204
205 // Construction of the 9 testing axis resulting from
206 // cross product combinations of the edges of the
207 // triangle and the edges of the AABB on which vertices
208 // will be projected.
209
210 for (int i = 0; i < 3; i++) {
211 for (int j = 0; j < 3; j++) {
212
213 cs_real_t axis[3];
214 cs_math_3_cross_product(f[i], e[j], axis);
215
216 // Projection of the triangle's vertices on the
217 // seperated axis
218 cs_real_t p[3];
219 for (int dir = 0; dir < 3; dir ++)
220 p[dir] = cs_math_3_dot_product(axis, v[dir]);
221
222 // Projection of the box on the seperated axis
223 // computation of the "radius" r
224 cs_real_t r = h[0] * CS_ABS(axis[0])
225 + h[1] * CS_ABS(axis[1])
226 + h[2] * CS_ABS(axis[2]);
227
228 cs_real_t pmin = fmin(p[0], fmin(p[1], p[2]));
229 cs_real_t pmax = fmax(p[0], fmax(p[1], p[2]));
230
231 // Finally, we test if most extreme
232 // of the triangle points intersects r
233 if (pmin > r || pmax < -r)
234 return false;
235 }
236 }
237
238 // Construction of the last testing axis resulting from
239 // the triangle normal, repeat the process
240 cs_real_t n[3];
241 cs_math_3_cross_product(f[0], f[1], n);
242
243 cs_real_t p[3];
244 for (int dir = 0; dir < 3; dir ++)
245 p[dir] = cs_math_3_dot_product(n, v[dir]);
246
247 cs_real_t r = h[0] * CS_ABS(n[0])
248 + h[1] * CS_ABS(n[1])
249 + h[2] * CS_ABS(n[2]);
250
251 cs_real_t pmin = fmin(p[0], fmin(p[1],p[2]));
252 cs_real_t pmax = fmax(p[0], fmax(p[1],p[2]));
253
254 if ( pmin > r || pmax < -r )
255 return false;
256
257
258 return true;
259 }
260
261 /*----------------------------------------------------------------------------
262 * Function to check if a point is "inside" or "outside" a plane, according
263 * to its normal. Basically, performs the dot product between the point and
264 * the normal and check its sign.
265 *
266 * parameters:
267 * plane <-- plane definition (x,y,z, Nx,Ny,Nz)
268 * x <-- the point considered
269 *
270 * returns:
271 * true if the point is "inside", false otherwise
272 ----------------------------------------------------------------------------*/
273
274 static bool
_is_point_inside_plane(cs_real_t plane[6],cs_real_t x[3])275 _is_point_inside_plane(cs_real_t plane[6],
276 cs_real_t x[3])
277 {
278 bool val = false;
279
280 cs_real_t *p = &plane[0];
281 cs_real_t *n = &plane[3];
282
283 cs_real_t psca = cs_math_3_distance_dot_product(p,x,n);
284
285 if (psca <= 0.0)
286 val = true;
287
288 return val;
289 }
290
291 /*----------------------------------------------------------------------------
292 * Function that performs the intersection between a plane and a polygon.
293 * It returns the resulting polygon at the "inner" side of the plane
294 * according to its normal.
295 *
296 * parameters:
297 * nb_vertex <--> number of vertices of the polygon
298 * vertex_coords <--> coordinates of the vertices (size : 3*nb_vertex)
299 * plane <-- plane definition (point + normal)
300 *
301 ----------------------------------------------------------------------------*/
302
303 static void
_polygon_plane_intersection(int * nb_vertex,cs_real_3_t ** vertex_coord,cs_real_t plane[6])304 _polygon_plane_intersection(int *nb_vertex,
305 cs_real_3_t **vertex_coord,
306 cs_real_t plane[6])
307 {
308 /* Initial number of vertices in the polygon */
309 int n_vtx = *nb_vertex;
310 cs_real_3_t *vtx = *vertex_coord;
311
312 cs_real_3_t *new_vtx = NULL;
313 BFT_MALLOC(new_vtx, n_vtx + 1, cs_real_3_t);
314 int j = 0;
315
316 /* Now we check which edge is intersected by the plane */
317 for (int i = 0; i < n_vtx; i++) {
318 int v1 = i;
319 int v2 = (i+1) % n_vtx;
320
321 cs_real_t xn = cs_math_3_distance_dot_product(vtx[v1], &plane[0], &plane[3]);
322 cs_real_t xd = cs_math_3_distance_dot_product(vtx[v1], vtx[v2], &plane[3]);
323
324 // if the edge is //, we keep the vertex
325 if ( CS_ABS(xd) < 1.0e-12 ) {
326
327 if(_is_point_inside_plane(plane, vtx[v2])){
328 for (int dir = 0; dir < 3; dir ++)
329 new_vtx[j][dir] = vtx[v2][dir];
330 j++;
331 }
332
333 // if the edge is not // to the plane
334 } else {
335 cs_real_t t = xn/xd;
336
337 // If intersection, new vertex
338 if (t > 0 && t < 1.0){
339 for (int dir = 0; dir < 3; dir ++)
340 new_vtx[j][dir] = vtx[v1][dir] + t * (vtx[v2][dir] - vtx[v1][dir]);
341 j++;
342 }
343
344 // We check if the second point is "inside" inside the plane
345 // if yes, add the vertex to the new vertex list
346 if(_is_point_inside_plane(plane, vtx[v2])){
347 for (int dir = 0; dir < 3; dir ++)
348 new_vtx[j][dir] = vtx[v2][dir];
349 j++;
350 }
351 }
352 }
353
354 BFT_REALLOC(new_vtx, j, cs_real_3_t);
355 BFT_REALLOC(vtx, j, cs_real_3_t);
356
357 for (int i = 0; i < j; i++) {
358 for (int dir = 0; dir < 3; dir ++)
359 vtx[i][dir] = new_vtx[i][dir];
360 }
361
362 *nb_vertex = j;
363 *vertex_coord = vtx;
364
365 BFT_FREE(new_vtx);
366 }
367
368 /*----------------------------------------------------------------------------
369 * Function to compute the approximate surface of a triangle overlapping
370 * an AABB (Axis Aligned Bounding Box). It is based on a Monte-Carlo like
371 * method that randomly draw points in the triangle.
372 *
373 * parameters:
374 * box_extents <-- coordinates of the bounding box (xmin,...,xmax,...)
375 * tria_coords <-- coordinates of the triangle vertices
376 *
377 * returns:
378 * the surface of the triangle inside the bounding box
379 ----------------------------------------------------------------------------*/
380
381 static cs_real_t
_triangle_box_surface_intersection(const cs_real_t box_extents[6],const cs_real_t tria_coords[3][3])382 _triangle_box_surface_intersection(const cs_real_t box_extents[6],
383 const cs_real_t tria_coords[3][3])
384 {
385 cs_real_t surf = 0.;
386
387 const cs_real_t *a = tria_coords[0];
388 const cs_real_t *b = tria_coords[1];
389 const cs_real_t *c = tria_coords[2];
390
391 /* Computation of the total triangle surface */
392 cs_real_t ab[3], ac[3], vec[3];
393
394 for (int i = 0; i < 3; i++) {
395 ab[i] = b[i]-a[i];
396 ac[i] = c[i]-a[i];
397 }
398
399 cs_math_3_cross_product(ab,ac,vec);
400 surf = 0.5 * cs_math_3_norm(vec);
401
402 /* Particular case if the triangle is totally included in the box */
403 cs_real_t bbox[6];
404 bbox[0] = HUGE_VAL; bbox[1] = HUGE_VAL; bbox[2] = HUGE_VAL;
405 bbox[3] = -HUGE_VAL; bbox[4] = -HUGE_VAL; bbox[5] = -HUGE_VAL;
406
407 for (int vtx_id = 0; vtx_id < 3; vtx_id ++) {
408 bbox[0] = CS_MIN(bbox[0], tria_coords[vtx_id][0]);
409 bbox[3] = CS_MAX(bbox[3], tria_coords[vtx_id][0]);
410 bbox[1] = CS_MIN(bbox[1], tria_coords[vtx_id][1]);
411 bbox[4] = CS_MAX(bbox[4], tria_coords[vtx_id][1]);
412 bbox[2] = CS_MIN(bbox[2], tria_coords[vtx_id][2]);
413 bbox[5] = CS_MAX(bbox[5], tria_coords[vtx_id][2]);
414 }
415
416 if ( bbox[0] >= box_extents[0]
417 && bbox[1] >= box_extents[1]
418 && bbox[2] >= box_extents[2]
419 && bbox[3] <= box_extents[3]
420 && bbox[4] <= box_extents[4]
421 && bbox[5] <= box_extents[5])
422 return surf;
423
424 /* Approximation of the surface enclosed in the AABB */
425
426 int max_draw = 5000;
427 int nb_point_in = 0;
428 cs_real_t px[3];
429
430 for (int nb_draw = 0; nb_draw < max_draw; nb_draw ++) {
431 /* Random draw in [[0:1]] */
432 cs_real_t ra = (cs_real_t)rand() / (cs_real_t)RAND_MAX;
433 cs_real_t rb = (cs_real_t)rand() / (cs_real_t)RAND_MAX;
434
435 if (ra+rb >= 1.0){
436 ra = 1.0-ra;
437 rb = 1.0-rb;
438 }
439
440 /* Contruction of the random point */
441 for (int i = 0; i < 3; i++)
442 px[i] = a[i] + ra*(b[i]-a[i]) + rb*(c[i]-a[i]);
443
444 if ( px[0] >= box_extents[0]
445 && px[1] >= box_extents[1]
446 && px[2] >= box_extents[2]
447 && px[0] <= box_extents[3]
448 && px[1] <= box_extents[4]
449 && px[2] <= box_extents[5])
450 nb_point_in ++;
451 }
452
453 surf *= (cs_real_t)nb_point_in / (cs_real_t)max_draw;
454
455 return surf;
456 }
457
458 /*----------------------------------------------------------------------------
459 * Function to compute the exact surface of a triangle overlapping
460 * an AABB (Axis Aligned Bounding Box). It is based on recursive cut
461 * of the triangle by the planes defined by the AABB faces.
462 *
463 * parameters:
464 * box_extents <-- coordinates of the bounding box (xmin,...,xmax,...)
465 * tria_coords <-- coordinates of the triangle vertices
466 *
467 * returns:
468 * the surface of the triangle inside the bounding box
469 ----------------------------------------------------------------------------*/
470
471 static cs_real_t
_exact_triangle_box_surface_intersection(const cs_real_t box_extents[6],const cs_real_t tria_coords[3][3])472 _exact_triangle_box_surface_intersection(const cs_real_t box_extents[6],
473 const cs_real_t tria_coords[3][3])
474 {
475 cs_real_t surf = 0.;
476
477 const cs_real_t *a = tria_coords[0];
478 const cs_real_t *b = tria_coords[1];
479 const cs_real_t *c = tria_coords[2];
480
481 /* Computation of the total triangle surface */
482 cs_real_t ab[3], ac[3], vec[3];
483
484 for (int i = 0; i < 3; i++) {
485 ab[i] = b[i]-a[i];
486 ac[i] = c[i]-a[i];
487 }
488
489 cs_math_3_cross_product(ab,ac,vec);
490 surf = 0.5 * cs_math_3_norm(vec);
491
492 /* Particular case if the triangle is totally included in the box */
493 cs_real_t bbox[6];
494 bbox[0] = HUGE_VAL; bbox[1] = HUGE_VAL; bbox[2] = HUGE_VAL;
495 bbox[3] = -HUGE_VAL; bbox[4] = -HUGE_VAL; bbox[5] = -HUGE_VAL;
496
497 for (int vtx_id = 0; vtx_id < 3; vtx_id ++) {
498 bbox[0] = CS_MIN(bbox[0], tria_coords[vtx_id][0]);
499 bbox[3] = CS_MAX(bbox[3], tria_coords[vtx_id][0]);
500 bbox[1] = CS_MIN(bbox[1], tria_coords[vtx_id][1]);
501 bbox[4] = CS_MAX(bbox[4], tria_coords[vtx_id][1]);
502 bbox[2] = CS_MIN(bbox[2], tria_coords[vtx_id][2]);
503 bbox[5] = CS_MAX(bbox[5], tria_coords[vtx_id][2]);
504 }
505
506 if ( bbox[0] >= box_extents[0]
507 && bbox[1] >= box_extents[1]
508 && bbox[2] >= box_extents[2]
509 && bbox[3] <= box_extents[3]
510 && bbox[4] <= box_extents[4]
511 && bbox[5] <= box_extents[5])
512 return surf;
513
514 /* Successively cut the triangle by the planes
515 * defined byt the box faces */
516 int nv = 3;
517 cs_real_3_t *coords = NULL ;
518 BFT_MALLOC(coords, nv, cs_real_3_t);
519
520 /* Polygon init */
521 for (int i = 0; i < nv; i ++){
522 for (int dir = 0; dir < 3; dir ++)
523 coords[i][dir] = tria_coords[i][dir];
524 }
525
526 /* Plane definition */
527 cs_real_t plane[6][6];
528 cs_real_t e[3][3] = {{1.0, 0.0, 0.0}, {0.0, 1.0, 0.0}, {0.0, 0.0, 1.0}};
529
530 for (int i = 0; i < 3; i++){
531 for (int j = 0; j < 3; j++){
532 plane[i][j] = box_extents[j];
533 plane[i][j+3] = -e[i][j];
534 }
535 }
536
537 for (int i = 3; i < 6; i++){
538 for (int j = 0; j < 3; j++){
539 plane[i][j] = box_extents[j+3];
540 plane[i][j+3] = e[i-3][j];
541 }
542 }
543
544 /* Recursively cut the tiangle by the planes */
545 for (int f = 0; f < 6; f++)
546 _polygon_plane_intersection(&nv, &coords, plane[f]);
547
548 /* If no intersection, surface is 0*/
549 if (nv ==0) {
550 surf = 0;
551 return surf;
552 }
553
554 /* Compute approximate face center coordinates for the polygon */
555 cs_real_t a_center[3] = {0, 0, 0};
556 cs_real_t f_norm[3] = {0, 0, 0};
557
558 for (int i = 0; i < nv; i++) {
559 for (int dir = 0; dir < 3; dir++)
560 a_center[dir] += coords[i][dir];
561 }
562
563 for (cs_lnum_t i = 0; i < 3; i++)
564 a_center[i] /= nv;
565
566 /* Surface computation */
567 cs_real_t vc0[3], vc1[3], vn[3];
568
569 for (cs_lnum_t i = 0; i < nv; i++) {
570
571 const int v0 = i;
572 const int v1 = (i+1) % nv;
573
574 for (cs_lnum_t dir = 0; dir < 3; dir++) {
575 vc0[dir] = coords[v0][dir] - a_center[dir];
576 vc1[dir] = coords[v1][dir] - a_center[dir];
577 }
578
579 cs_math_3_cross_product(vc0, vc1, vn);
580
581 for (cs_lnum_t dir = 0; dir < 3; dir++)
582 f_norm[dir] += 0.5*vn[dir];
583 }
584
585 surf = cs_math_3_norm(f_norm);
586
587 BFT_FREE(coords);
588
589 return surf;
590 }
591
592 /*----------------------------------------------------------------------------
593 * Function to compute the volume of a tetrahedron
594 *
595 * parameters:
596 * x1 <-- coordinates of the tetrahedra (x1,y1,z1)
597 * x2 <-- coordinates of the tetrahedra (x2,y2,z2)
598 * x3 <-- coordinates of the tetrahedra (x3,y3,z3)
599 * x4 <-- coordinates of the tetrahedra (x4,y4,z4)
600 *
601 * returns:
602 * the volume of the tetrahedron
603 ----------------------------------------------------------------------------*/
604
605 static cs_real_t
_tetra_vol(cs_real_t x1[3],cs_real_t x2[3],cs_real_t x3[3],cs_real_t x4[3])606 _tetra_vol(cs_real_t x1[3],
607 cs_real_t x2[3],
608 cs_real_t x3[3],
609 cs_real_t x4[3])
610 {
611 cs_real_t tetra_vol;
612
613 tetra_vol = cs_math_fabs(( (x1[0]-x4[0])*(x2[1]-x4[1])
614 - (x1[1]-x4[1])*(x2[0]-x4[0])) * (x3[2]-x4[2])
615 + ( (x1[1]-x4[1])*(x2[2]-x4[2])
616 - (x1[2]-x4[2])*(x2[1]-x4[1])) * (x3[0]-x4[0])
617 + ( (x1[2]-x4[2])*(x2[0]-x4[0])
618 - (x1[0]-x4[0])*(x2[2]-x4[2])) * (x3[1]-x4[1]));
619
620 tetra_vol *= cs_math_1ov6;
621
622 return tetra_vol;
623 }
624
625 /*----------------------------------------------------------------------------
626 * Function to compute the volume of a pyramid, whose base is defined by
627 * x1, x2, x3 and x4.
628 *
629 * parameters:
630 * x1 <-- coordinates of the pyramid (x1,y1,z1)
631 * x2 <-- coordinates of the pyramid (x2,y2,z2)
632 * x3 <-- coordinates of the pyramid (x3,y3,z3)
633 * x4 <-- coordinates of the pyramid (x4,y4,z4)
634 * x5 <-- coordinates of the pyramid (x5,y5,z5)
635 *
636 * returns:
637 * the volume of the pyramid
638 ----------------------------------------------------------------------------*/
639
640 static cs_real_t
_pyramid_vol(cs_real_t x1[3],cs_real_t x2[3],cs_real_t x3[3],cs_real_t x4[3],cs_real_t x5[3])641 _pyramid_vol(cs_real_t x1[3],
642 cs_real_t x2[3],
643 cs_real_t x3[3],
644 cs_real_t x4[3],
645 cs_real_t x5[3])
646 {
647 cs_real_t xc[3];
648
649 for (int i = 0; i < 3; i++)
650 xc[i] = 0.25 * (x1[i]+x2[i]+x3[i]+x4[i]);
651
652 cs_real_t vol12 = _tetra_vol(x1, x2, xc, x5);
653 cs_real_t vol23 = _tetra_vol(x2, x3, xc, x5);
654 cs_real_t vol34 = _tetra_vol(x3, x4, xc, x5);
655 cs_real_t vol41 = _tetra_vol(x4, x1, xc, x5);
656
657 cs_real_t pyram_vol = vol12 + vol23 + vol34 + vol41;
658
659 return pyram_vol;
660 }
661
662 /*----------------------------------------------------------------------------
663 * Function to compute the volume of a prism, whose base is defined by
664 * x1, x2, x3 and x4.
665 *
666 * parameters:
667 * x1 <-- coordinates of the prism (x1,y1,z1)
668 * x2 <-- coordinates of the prism (x2,y2,z2)
669 * x3 <-- coordinates of the prism (x3,y3,z3)
670 * x4 <-- coordinates of the prism (x4,y4,z4)
671 * x5 <-- coordinates of the prism (x5,y5,z5)
672 * x6 <-- coordinates of the prism (x5,y5,z5)
673 *
674 * returns:
675 * the volume of the prism
676 ----------------------------------------------------------------------------*/
677
678 static cs_real_t
_prism_vol(cs_real_t x1[3],cs_real_t x2[3],cs_real_t x3[3],cs_real_t x4[3],cs_real_t x5[3],cs_real_t x6[3])679 _prism_vol(cs_real_t x1[3],
680 cs_real_t x2[3],
681 cs_real_t x3[3],
682 cs_real_t x4[3],
683 cs_real_t x5[3],
684 cs_real_t x6[3])
685 {
686 cs_real_t xc[3];
687
688 for (int i = 0; i < 3; i++)
689 xc[i] = (x1[i]+x2[i]+x3[i]+x4[i]+x5[i]+x6[i]) * cs_math_1ov6;
690
691 cs_real_t vol136c = _tetra_vol(x1, x3, x6, xc);
692 cs_real_t vol245c = _tetra_vol(x2, x4, x5, xc);
693 cs_real_t vol1256c = _pyramid_vol(x1, x2, x5, x6, xc);
694 cs_real_t vol1243c = _pyramid_vol(x1, x2, x4, x3, xc);
695 cs_real_t vol3456c = _pyramid_vol(x3, x4, x5, x6, xc);
696
697 cs_real_t prisme_vol = vol136c + vol245c + vol1256c + vol1243c + vol3456c;
698
699 return prisme_vol;
700 }
701
702 /*----------------------------------------------------------------------------
703 * Function to compute the "outside" length of a segment cut by a plan,
704 * according to its normal
705 *
706 * parameters:
707 * x1 <-- coordinates of the tetrahedra (x1,y1,z1)
708 * x2 <-- coordinates of the tetrahedra (x2,y2,z2)
709 * plane <-- coordinates of the triangle vertices and normals
710 * (x1,y1,z1,nx,ny,nz)
711 *
712 * returns:
713 * the volume of the tetrahedra under the plane (according to its normal)
714 ----------------------------------------------------------------------------*/
715
716 static cs_real_t
_plane_segment_intersection(cs_real_t x1[3],cs_real_t x2[3],cs_real_t plane[6])717 _plane_segment_intersection(cs_real_t x1[3],
718 cs_real_t x2[3],
719 cs_real_t plane[6])
720 {
721 cs_real_t length = cs_math_3_distance(x1, x2);
722 cs_real_t *a = &plane[0];
723 cs_real_t *n = &plane[3];
724
725 int v1_is_inside = 0; int v2_is_inside = 0;
726
727 if (_is_point_inside_plane(plane, x1))
728 v1_is_inside = 1;
729
730 if (_is_point_inside_plane(plane, x2))
731 v2_is_inside = 1;
732
733
734 if (v1_is_inside == 0 && v2_is_inside == 0)
735 length = 0.0;
736
737 if (v1_is_inside == 1 && v2_is_inside == 0) {
738
739 cs_real_t den = cs_math_3_distance_dot_product(x2, x1, n);
740 cs_real_t num = cs_math_3_distance_dot_product(x2, a, n);
741
742 cs_real_t lambda = 1.0;
743 if (cs_math_fabs(den) > 1.e-20)
744 lambda = num / den;
745
746 length *= lambda;
747 }
748
749 if (v1_is_inside == 0 && v2_is_inside == 1) {
750
751 cs_real_t den = cs_math_3_distance_dot_product(x1, x2, n);
752 cs_real_t num = cs_math_3_distance_dot_product(x1, a, n);
753
754 cs_real_t lambda = 1.0;
755 if (cs_math_fabs(den) > 1.e-20)
756 lambda = num / den;
757
758 length *= lambda;
759 }
760
761 return length;
762 }
763
764 /*----------------------------------------------------------------------------
765 * Function to compute the volume of a tetrahedra cut by a plane surface
766 *
767 * parameters:
768 * x1 <-- coordinates of the tetrahedra (x1,y1,z1)
769 * x2 <-- coordinates of the tetrahedra (x2,y2,z2)
770 * x3 <-- coordinates of the tetrahedra (x3,y3,z3)
771 * x4 <-- coordinates of the tetrahedra (x4,y4,z4)
772 * plane <-- coordinates of the triangle vertices and normals
773 * (x1,y1,z1,nx,ny,nz)
774 *
775 * returns:
776 * the volume of the tetrahedra under the plane (according to its normal)
777 ----------------------------------------------------------------------------*/
778
779 static cs_real_t
_tetrahedron_plane_volume_intersection(cs_real_t x1[3],cs_real_t x2[3],cs_real_t x3[3],cs_real_t x4[3],cs_real_t plane[6])780 _tetrahedron_plane_volume_intersection(cs_real_t x1[3],
781 cs_real_t x2[3],
782 cs_real_t x3[3],
783 cs_real_t x4[3],
784 cs_real_t plane[6])
785 {
786 cs_real_t vol = 0.0;
787
788 int v1_is_inside = 0; int v2_is_inside = 0;
789 int v3_is_inside = 0; int v4_is_inside = 0;
790
791 /* Test if the tetra vertices are "inside" or "outside" the plane,
792 * according to its normal */
793
794 if (_is_point_inside_plane(plane, x1))
795 v1_is_inside = 1;
796
797 if (_is_point_inside_plane(plane, x2))
798 v2_is_inside = 1;
799
800 if (_is_point_inside_plane(plane, x3))
801 v3_is_inside = 1;
802
803 if (_is_point_inside_plane(plane, x4))
804 v4_is_inside = 1;
805
806 int nb_point_inside_plane = v1_is_inside + v2_is_inside + v3_is_inside + v4_is_inside;
807
808 switch(nb_point_inside_plane) {
809
810 case 0: /* entire tetra "outside" the plane */
811
812 vol = _tetra_vol(x1, x2, x3, x4);
813
814 break;
815
816 case 1: /* 3 intersection points with one point inside */
817 {
818 vol = _tetra_vol(x1, x2, x3, x4);
819
820 /* Looking for the point located inside */
821 cs_real_t *a, *b, *c, *d;
822
823 if (v1_is_inside == 1) {
824 a = &x1[0];
825 b = &x2[0];
826 c = &x3[0];
827 d = &x4[0];
828 } else if (v2_is_inside == 1) {
829 a = &x2[0];
830 b = &x1[0];
831 c = &x3[0];
832 d = &x4[0];
833 } else if (v3_is_inside == 1) {
834 a = &x3[0];
835 b = &x1[0];
836 c = &x2[0];
837 d = &x4[0];
838 } else if (v4_is_inside == 1) {
839 a = &x4[0];
840 b = &x1[0];
841 c = &x2[0];
842 d = &x3[0];
843 }
844
845
846 cs_real_t ab[3], ac[3], ad[3];
847 for (int i = 0; i < 3; i++) {
848 ab[i] = a[i] - b[i];
849 ac[i] = a[i] - c[i];
850 ad[i] = a[i] - d[i];
851 }
852
853 cs_real_t lab = cs_math_3_norm(ab);
854 cs_real_t lac = cs_math_3_norm(ac);
855 cs_real_t lad = cs_math_3_norm(ad);
856
857 cs_real_t lab_inside = lab - _plane_segment_intersection(a, b, plane);
858 cs_real_t lac_inside = lac - _plane_segment_intersection(a, c, plane);
859 cs_real_t lad_inside = lad - _plane_segment_intersection(a, d, plane);
860
861 cs_real_t lab_frac = lab_inside / lab;
862 cs_real_t lac_frac = lac_inside / lac;
863 cs_real_t lad_frac = lad_inside / lad;
864
865 vol *= (1.0 - lab_frac * lac_frac * lad_frac);
866
867 break;
868 }
869
870 case 3: /* 3 intersection points with 3 points inside*/
871 {
872 vol = _tetra_vol(x1, x2, x3, x4);
873
874 /* Lookink for the point located outside */
875 cs_real_t *a, *b, *c, *d;
876
877 if (v1_is_inside == 0){
878 a = &x1[0];
879 b = &x2[0];
880 c = &x3[0];
881 d = &x4[0];
882 } else if (v2_is_inside == 0) {
883 a = &x2[0];
884 b = &x1[0];
885 c = &x3[0];
886 d = &x4[0];
887 } else if (v3_is_inside == 0) {
888 a = &x3[0];
889 b = &x1[0];
890 c = &x2[0];
891 d = &x4[0];
892 } else if (v4_is_inside == 0) {
893 a = &x4[0];
894 b = &x1[0];
895 c = &x2[0];
896 d = &x3[0];
897 }
898
899 cs_real_t ab[3], ac[3], ad[3];
900 for (int i = 0; i < 3; i++) {
901 ab[i] = a[i] - b[i];
902 ac[i] = a[i] - c[i];
903 ad[i] = a[i] - d[i];
904 }
905
906 cs_real_t lab = cs_math_3_norm(ab);
907 cs_real_t lac = cs_math_3_norm(ac);
908 cs_real_t lad = cs_math_3_norm(ad);
909
910 cs_real_t lab_inside = _plane_segment_intersection(a, b, plane);
911 cs_real_t lac_inside = _plane_segment_intersection(a, c, plane);
912 cs_real_t lad_inside = _plane_segment_intersection(a, d, plane);
913
914 cs_real_t lab_frac = lab_inside / lab;
915 cs_real_t lac_frac = lac_inside / lac;
916 cs_real_t lad_frac = lad_inside / lad;
917
918 vol *= lab_frac * lac_frac * lad_frac;
919
920 break;
921 }
922
923 case 2: /* 4 intersection points */
924 {
925 /* Looking for the 2 inside and 2 outside points */
926 cs_real_t *a, *b, *c, *d;
927
928 if (v1_is_inside && v2_is_inside) {
929 a = &x1[0];
930 b = &x2[0];
931 c = &x3[0];
932 d = &x4[0];
933 } else if (v1_is_inside && v3_is_inside) {
934 a = &x1[0];
935 b = &x3[0];
936 c = &x2[0];
937 d = &x4[0];
938 } else if (v1_is_inside && v4_is_inside) {
939 a = &x1[0];
940 b = &x4[0];
941 c = &x2[0];
942 d = &x3[0];
943 } else if (v2_is_inside && v3_is_inside) {
944 a = &x2[0];
945 b = &x3[0];
946 c = &x1[0];
947 d = &x4[0];
948 } else if (v2_is_inside && v4_is_inside) {
949 a = &x2[0];
950 b = &x4[0];
951 c = &x1[0];
952 d = &x3[0];
953 } else if (v3_is_inside && v4_is_inside) {
954 a = &x3[0];
955 b = &x4[0];
956 c = &x1[0];
957 d = &x2[0];
958 }
959
960 cs_real_t ac[3], ad[3], bc[3], bd[3];
961 for (int i = 0; i < 3; i++) {
962 ac[i] = c[i] - a[i];
963 ad[i] = d[i] - a[i];
964 bc[i] = c[i] - b[i];
965 bd[i] = d[i] - b[i];
966 }
967
968 cs_real_t lac = cs_math_3_norm(ac);
969 cs_real_t lad = cs_math_3_norm(ad);
970 cs_real_t lbc = cs_math_3_norm(bc);
971 cs_real_t lbd = cs_math_3_norm(bd);
972
973 cs_real_t lac_inside = _plane_segment_intersection(a, c, plane);
974 cs_real_t lad_inside = _plane_segment_intersection(a, d, plane);
975 cs_real_t lbc_inside = _plane_segment_intersection(b, c, plane);
976 cs_real_t lbd_inside = _plane_segment_intersection(b, d, plane);
977
978 cs_real_t lac_frac = lac_inside / lac;
979 cs_real_t lad_frac = lad_inside / lad;
980 cs_real_t lbc_frac = lbc_inside / lbc;
981 cs_real_t lbd_frac = lbd_inside / lbd;
982
983 cs_real_t ac_inter[3], ad_inter[3], bc_inter[3], bd_inter[3];
984 /* Coordinates of the intersection point between the tetra and the plane */
985 for (int i = 0; i < 3; i++) {
986 ac_inter[i] = a[i] + lac_frac * ac[i];
987 ad_inter[i] = a[i] + lad_frac * ad[i];
988 bc_inter[i] = b[i] + lbc_frac * bc[i];
989 bd_inter[i] = b[i] + lbd_frac * bd[i];
990 }
991
992 vol = _prism_vol(a, b, ac_inter, bc_inter, bd_inter, ad_inter);
993
994 break;
995 }
996
997 case 4:
998 vol = 0.0;
999 break;
1000
1001 default:
1002 bft_error(__FILE__, __LINE__, 0,
1003 "Error in function _tetrahedra_plane_volume_intersection\n");
1004 break;
1005 }
1006
1007 return vol;
1008
1009 }
1010 /*----------------------------------------------------------------------------
1011 * Function to update the copy of the mesh at initialization
1012 * (necessary is the STL mesh needs to move with time)
1013 *
1014 * parameters:
1015 * stl_mesh <-- a STL mesh structure
1016 ----------------------------------------------------------------------------*/
1017
1018 static void
_update_init_mesh(cs_stl_mesh_t * stl_mesh)1019 _update_init_mesh(cs_stl_mesh_t *stl_mesh)
1020 {
1021 cs_lnum_t n_tria = stl_mesh->n_faces;
1022 cs_lnum_t n_points = n_tria*3;
1023
1024 for (cs_lnum_t i = 0; i < n_points; i++) {
1025 for (int j = 0; j < 3; j++) {
1026 stl_mesh->coords_ini[i][j] = stl_mesh->coords[i][j];
1027 }
1028 }
1029
1030 }
1031
1032 /*! (DOXYGEN_SHOULD_SKIP_THIS) \endcond */
1033
1034 /*=============================================================================
1035 * Public function definitions
1036 *============================================================================*/
1037
1038 /*----------------------------------------------------------------------------*/
1039 /*!
1040 * \brief Add a new STL mesh structure.
1041 *
1042 * \param[in] name name of the STL mesh
1043 *
1044 * \return pointer to the new STL mesh structure
1045 */
1046 /*----------------------------------------------------------------------------*/
1047
1048 cs_stl_mesh_t *
cs_stl_mesh_add(const char * name)1049 cs_stl_mesh_add(const char *name)
1050 {
1051 /* First check if it already exists */
1052 cs_stl_mesh_t *stl_mesh = cs_stl_mesh_get_by_name(name);
1053
1054 if (stl_mesh != NULL) {
1055 // The STL mesh already exists
1056 bft_error(__FILE__, __LINE__, 0,
1057 _("Error creating stl mesh: mesh %s already exists."), name);
1058
1059 }
1060 else {
1061 // If it does not exists create it
1062 _stl_meshes.n_meshes++;
1063 BFT_REALLOC(_stl_meshes.mesh_list, _stl_meshes.n_meshes, cs_stl_mesh_t *);
1064
1065 BFT_MALLOC(stl_mesh, 1, cs_stl_mesh_t);
1066
1067 if (name != NULL) {
1068 strncpy(stl_mesh->name, name, 19);
1069 stl_mesh->name[19] = '\0';
1070 }
1071 else
1072 bft_error(__FILE__, __LINE__, 0,
1073 _("Error creating stl mesh: no name given."));
1074
1075 memset(stl_mesh->header, 0, 80);
1076 stl_mesh->n_faces = 0;
1077 stl_mesh->coords = NULL;
1078 stl_mesh->n_seeds = 0;
1079 stl_mesh->seed_coords = NULL;
1080 stl_mesh->is_porous = false;
1081 stl_mesh->ext_mesh = NULL;
1082
1083 _stl_meshes.mesh_list[_stl_meshes.n_meshes - 1] = stl_mesh;
1084 }
1085
1086 return stl_mesh;
1087 }
1088
1089 /*----------------------------------------------------------------------------*/
1090 /*!
1091 * \brief Return a pointer to a STL mesh based on its name if present.
1092 *
1093 * \param[in] name name of the STL mesh
1094 *
1095 * If no STL mesh of the given name is defined, NULL is returned.
1096 *
1097 * \return pointer to the STL mesh structure, or NULL
1098 */
1099 /*----------------------------------------------------------------------------*/
1100
1101 cs_stl_mesh_t *
cs_stl_mesh_get_by_name(const char * name)1102 cs_stl_mesh_get_by_name(const char *name)
1103 {
1104 cs_stl_mesh_t *ptr = NULL;
1105
1106 for (int s_id = 0; s_id < _stl_meshes.n_meshes; s_id ++) {
1107 cs_stl_mesh_t *stl_mesh = _stl_meshes.mesh_list[s_id];
1108 int test = strcmp(stl_mesh->name, name);
1109 if (test == 0)
1110 ptr = stl_mesh;
1111 }
1112
1113 return ptr;
1114 }
1115
1116 /*----------------------------------------------------------------------------*/
1117 /*!
1118 * \brief Free all allocated STL mesh structures
1119 */
1120 /*----------------------------------------------------------------------------*/
1121
1122 void
cs_stl_mesh_destroy_all(void)1123 cs_stl_mesh_destroy_all(void)
1124 {
1125 for (int s_id = 0; s_id < _stl_meshes.n_meshes; s_id ++) {
1126 cs_stl_mesh_t *ptr = _stl_meshes.mesh_list[s_id];
1127 BFT_FREE(ptr->coords);
1128 BFT_FREE(ptr->coords_ini);
1129 BFT_FREE(ptr->seed_coords);
1130 }
1131
1132 BFT_FREE(_stl_meshes.mesh_list);
1133 }
1134
1135 /*----------------------------------------------------------------------------*/
1136 /*!
1137 * \brief Read a binary STL file and store its content in a STL mesh structure
1138 *
1139 * Each STL file composed of the following header:
1140 *
1141 * uint8[80] – Header
1142 * uint32 – Number of triangles
1143 *
1144 * followed by 50 byte blocks for each triangle:
1145 *
1146 * - real32[3] – Normal vector
1147 * - real32[3] – Vertex 1 coordinates
1148 * - real32[3] – Vertex 2 coordinates
1149 * - real32[3] – Vertex 3 coordiantes
1150 * - uint16 – Attribute (any other information, usually void)
1151 *
1152 * \param[in] path path to the STL file
1153 * \param[in] stl_mesh pointer to the associated STL mesh structure
1154 */
1155 /*----------------------------------------------------------------------------*/
1156
1157 void
cs_stl_file_read(cs_stl_mesh_t * stl_mesh,const char * path)1158 cs_stl_file_read(cs_stl_mesh_t *stl_mesh,
1159 const char *path)
1160 {
1161 uint8_t buf[128];
1162 FILE *fp;
1163 float *loc_coords = NULL;
1164
1165 cs_lnum_t n_tria = 0;
1166 cs_lnum_t n_tria_new = 0;
1167
1168 if (cs_glob_rank_id < 1) {
1169
1170 /* Open STL file */
1171 fp = fopen(path, "rb");
1172 if (fp == NULL) {
1173 bft_error(__FILE__, __LINE__, 0,
1174 _("Error opening file \"%s\":\n\n"
1175 " %s"), path, strerror(errno));
1176 }
1177
1178 /* Check if the file is ASCII or Binary */
1179 char temp[6] ;
1180 fread(temp, 5, 1, fp);
1181 temp[5] = '\0';
1182 int test = strcmp(temp, "solid");
1183
1184 if (test == 0)
1185 bft_error(__FILE__, __LINE__, 0,
1186 _("Error opening file \"%s\":\n"
1187 "You are probably tyring to open an ASCII STL file\n"
1188 "Please convert your file to binary :-)"), path);
1189
1190
1191 /* Read and copy header */
1192 rewind(fp);
1193 fread(buf, 84, 1, fp);
1194 memcpy(stl_mesh->header, buf, 80);
1195
1196 /* Read number of triangles */
1197 uint32_t ntri;
1198 ntri = _cast32(buf+80);
1199 n_tria = (int)ntri;
1200
1201 stl_mesh->n_faces = n_tria;
1202
1203 /* Allocation*/
1204 BFT_MALLOC(stl_mesh->coords , 3*n_tria, cs_real_3_t);
1205 BFT_MALLOC(loc_coords , 9, float);
1206
1207 /* Loop on triangle faces
1208 ---------------------- */
1209 for (uint32_t i = 0; i < ntri; i++) {
1210
1211 // Each triangle data are contained in 50bytes blocks
1212 fread(buf, 50, 1, fp);
1213 uint8_t *start = buf + 12;
1214
1215 // Read the 3 vertices for the current triangle
1216 for (uint32_t vi = 0; vi < 3; vi ++) {
1217
1218 // Read the 3 coordinates for each vertex
1219 for (cs_lnum_t dir = 0; dir < 3; dir ++) {
1220
1221 uint32_t v_temp = _cast32(start + 3*4*vi + 4*dir);
1222 float *dir_coo = (float *)(&v_temp);
1223 loc_coords[3*vi + dir] = *dir_coo;
1224 }
1225 }
1226
1227 // Check if the current triangle has a null area
1228
1229 cs_real_t a[3], b[3], c[3], n[3];
1230
1231 for (int dir = 0; dir < 3; dir ++) {
1232 a[dir] = (cs_real_t)loc_coords[3*0 + dir];
1233 b[dir] = (cs_real_t)loc_coords[3*1 + dir];
1234 c[dir] = (cs_real_t)loc_coords[3*2 + dir];
1235 }
1236
1237 n[0] = (b[1]-a[1])*(c[2]-a[2]) - (b[2]-a[2])*(c[1]-a[1]);
1238 n[1] = (b[2]-a[2])*(c[0]-a[0]) - (b[0]-a[0])*(c[2]-a[2]);
1239 n[2] = (b[0]-a[0])*(c[1]-a[1]) - (b[1]-a[1])*(c[0]-a[0]);
1240
1241 cs_real_t nn = cs_math_3_norm(n);
1242
1243 if (nn > 1.e-20) {
1244 for (cs_lnum_t dir = 0; dir < 3; dir ++) {
1245 for (cs_lnum_t vi = 0; vi < 3; vi ++) {
1246 stl_mesh->coords[3*n_tria_new + vi][dir]
1247 = (cs_real_t)loc_coords[3*vi + dir];
1248 }
1249 }
1250 n_tria_new ++;
1251 }
1252
1253 }
1254
1255 /* Some log information */
1256 bft_printf(_("\n"
1257 " ** Reading of STL mesh \"%s\"\n"
1258 " Number of triangles: %d\n"
1259 " Number of removed triangles: %d\n\n"),
1260 stl_mesh->name, (int)n_tria_new, (int)n_tria-n_tria_new);
1261
1262 n_tria = n_tria_new;
1263 stl_mesh->n_faces = n_tria;
1264
1265 /* Re-allocation*/
1266 BFT_REALLOC(stl_mesh->coords , 3*n_tria, cs_real_3_t);
1267
1268 /* Copy coordinates to a work aray that
1269 * will contain all the init coordinates */
1270 BFT_MALLOC(stl_mesh->coords_ini , 3*n_tria, cs_real_3_t);
1271 for (int i = 0; i < 3*n_tria; i++)
1272 for (int j = 0; j < 3; j++)
1273 stl_mesh->coords_ini[i][j] = stl_mesh->coords[i][j];
1274
1275 BFT_FREE(loc_coords);
1276 fclose(fp);
1277
1278 }
1279
1280 /* Broadcast to other ranks */
1281
1282 cs_parall_bcast(0, /* root_rank */
1283 1,
1284 CS_LNUM_TYPE,
1285 &(stl_mesh->n_faces));
1286
1287 if (cs_glob_rank_id > 0) {
1288 BFT_MALLOC(stl_mesh->coords , stl_mesh->n_faces*3, cs_real_3_t);
1289 BFT_MALLOC(stl_mesh->coords_ini, stl_mesh->n_faces*3, cs_real_3_t);
1290 }
1291
1292 cs_parall_bcast(0, /* root_rank */
1293 stl_mesh->n_faces*9,
1294 CS_REAL_TYPE,
1295 stl_mesh->coords);
1296
1297 cs_parall_bcast(0, /* root_rank */
1298 stl_mesh->n_faces*9,
1299 CS_REAL_TYPE,
1300 stl_mesh->coords_ini);
1301
1302
1303 /* Merge identical vertices
1304 ------------------------ */
1305
1306 #if 0
1307 cs_lnum_t n_coo_ini = n_tria*3;
1308
1309 cs_coord_t *_face_vtx_coord = NULL;
1310 BFT_MALLOC(_face_vtx_coord, n_coo_ini*3, cs_coord_t);
1311 for (cs_lnum_t i = 0; i < n_coo_ini*3; i++)
1312 _face_vtx_coord[i] = stl_mesh->coords[i];
1313
1314 fvm_io_num_t *v_io_num = fvm_io_num_create_from_sfc(_face_vtx_coord,
1315 3,
1316 n_coo_ini,
1317 FVM_IO_NUM_SFC_MORTON_BOX);
1318
1319 BFT_FREE(_face_vtx_coord);
1320
1321 cs_gnum_t *vtx_gnum = fvm_io_num_transfer_global_num(v_io_num);
1322
1323 cs_lnum_t *order = cs_order_gnum(NULL, vtx_gnum, n_coo_ini);
1324
1325 v_io_num = fvm_io_num_destroy(v_io_num);
1326
1327 cs_coord_t *vertex_coord = NULL;
1328 cs_lnum_t *vertex_num = NULL;
1329
1330 if (cs_glob_rank_id < 1) {
1331
1332 BFT_MALLOC(vertex_coord, n_coo_ini*3, cs_coord_t);
1333 BFT_MALLOC(vertex_num, n_tria*3, cs_lnum_t);
1334
1335 for (cs_lnum_t i = 0; i < n_coo_ini; i++)
1336 vertex_num[i] = -1;
1337
1338 cs_lnum_t n_coo_new = 0;
1339 for (cs_lnum_t i = 0; i < n_coo_ini; i++) {
1340 cs_lnum_t j = order[i];
1341 vertex_num[j] = n_coo_new + 1;
1342 bool identical = false;
1343 if (! identical) {
1344 for (cs_lnum_t k = 0; k < 3; k++)
1345 vertex_coord[n_coo_new*3 + k] = stl_mesh->coords[j*3 + k];
1346 n_coo_new++;
1347 }
1348 }
1349
1350 BFT_REALLOC(vertex_coord, n_coo_new*3, cs_coord_t);
1351
1352 }
1353 #endif
1354
1355 cs_coord_3_t *vertex_coord = NULL;
1356 cs_lnum_t *vertex_num = NULL;
1357 cs_gnum_t *vertex_gnum = NULL;
1358 cs_gnum_t *faces_gnum = NULL;
1359
1360 if (cs_glob_rank_id < 1) {
1361 BFT_MALLOC(vertex_coord, n_tria*3, cs_coord_3_t);
1362 BFT_MALLOC(vertex_num , n_tria*3, cs_lnum_t);
1363 BFT_MALLOC(vertex_gnum , n_tria*3, cs_gnum_t);
1364 BFT_MALLOC(faces_gnum , n_tria, cs_gnum_t);
1365
1366 for (cs_lnum_t j = 0; j < n_tria*3; j++) {
1367 vertex_num[j] = j+1;
1368 vertex_gnum[j] = j+1;
1369 for (cs_lnum_t k = 0; k < 3; k++)
1370 vertex_coord[j][k] = stl_mesh->coords[j][k];
1371 }
1372 for (cs_lnum_t j = 0; j < n_tria; j++)
1373 faces_gnum[j] = j+1;
1374
1375 }
1376
1377 fvm_nodal_t *ext_mesh = fvm_nodal_create(stl_mesh->name, 3);
1378
1379 /* Generate FVM structure */
1380
1381 fvm_nodal_append_by_transfer(ext_mesh,
1382 n_tria,
1383 FVM_FACE_TRIA,
1384 NULL,
1385 NULL,
1386 NULL,
1387 vertex_num,
1388 NULL);
1389
1390 if (cs_glob_rank_id < 1) {
1391 fvm_nodal_set_shared_vertices(ext_mesh, (const cs_coord_t *)stl_mesh->coords);
1392 } else {
1393 fvm_nodal_set_shared_vertices(ext_mesh, NULL);
1394 }
1395
1396 fvm_nodal_init_io_num(ext_mesh, faces_gnum , 2);
1397 fvm_nodal_init_io_num(ext_mesh, vertex_gnum, 0);
1398
1399 stl_mesh->ext_mesh = ext_mesh;
1400
1401 if (cs_glob_rank_id < 1) {
1402 BFT_FREE(vertex_gnum);
1403 BFT_FREE(faces_gnum);
1404 }
1405 }
1406
1407 /*----------------------------------------------------------------------------*/
1408 /*!
1409 * \brief Apply a transformation matrix to a STL mesh structure.
1410 *
1411 * \param[in] stl_mesh pointer to the associated STL mesh structure
1412 * \param[in] matrix transformation matrix
1413 */
1414 /*----------------------------------------------------------------------------*/
1415
1416 void
cs_stl_mesh_transform(cs_stl_mesh_t * stl_mesh,double matrix[3][4])1417 cs_stl_mesh_transform(cs_stl_mesh_t *stl_mesh,
1418 double matrix[3][4])
1419 {
1420 cs_lnum_t n_tria = stl_mesh->n_faces;
1421 cs_lnum_t n_points = n_tria*3;
1422
1423 for (cs_lnum_t i = 0; i < n_points; i++) {
1424
1425 cs_real_t *c = stl_mesh->coords[i];
1426
1427 double c_a[4] = {c[0], c[1], c[2], 1.}; /* homogeneous coords */
1428 double c_b[3] = {0, 0, 0};
1429
1430 for (cs_lnum_t j = 0; j < 3; j++)
1431 for (int k = 0; k < 4; k++)
1432 c_b[j] += matrix[j][k]*c_a[k];
1433
1434 for (cs_lnum_t j = 0; j < 3; j++)
1435 c[j] = c_b[j];
1436
1437 }
1438
1439 _update_init_mesh(stl_mesh);
1440
1441 }
1442
1443 /*----------------------------------------------------------------------------*/
1444 /*!
1445 * \brief Apply a transformation matrix to a STL mesh structure, but use
1446 * \brief the initial coordinates as inputs
1447 *
1448 * \param[in] stl_mesh pointer to the associated STL mesh structure
1449 * \param[in] matrix transformation matrix
1450 */
1451 /*----------------------------------------------------------------------------*/
1452
1453 void
cs_stl_mesh_transform_from_init(cs_stl_mesh_t * stl_mesh,double matrix[3][4])1454 cs_stl_mesh_transform_from_init(cs_stl_mesh_t *stl_mesh,
1455 double matrix[3][4])
1456 {
1457 cs_lnum_t n_tria = stl_mesh->n_faces;
1458 cs_lnum_t n_points = n_tria*3;
1459
1460 for (cs_lnum_t i = 0; i < n_points; i++) {
1461
1462 cs_real_t *c = stl_mesh->coords[i];
1463 cs_real_t *ci = stl_mesh->coords_ini[i];
1464
1465 double c_a[4] = {ci[0], ci[1], ci[2], 1.}; /* homogeneous coords */
1466 double c_b[3] = {0, 0, 0};
1467
1468 for (cs_lnum_t j = 0; j < 3; j++)
1469 for (int k = 0; k < 4; k++)
1470 c_b[j] += matrix[j][k]*c_a[k];
1471
1472 for (cs_lnum_t j = 0; j < 3; j++)
1473 c[j] = c_b[j];
1474
1475 }
1476 }
1477
1478 /*----------------------------------------------------------------------------*/
1479 /*!
1480 * \brief Apply a translation to a STL mesh structure.
1481 *
1482 * \param[in] stl_mesh pointer to the associated STL mesh structure
1483 * \param[in] vector translation vector
1484 */
1485 /*----------------------------------------------------------------------------*/
1486
1487 void
cs_stl_mesh_translate(cs_stl_mesh_t * stl_mesh,cs_real_t vector[3])1488 cs_stl_mesh_translate(cs_stl_mesh_t *stl_mesh,
1489 cs_real_t vector[3])
1490 {
1491 cs_lnum_t n_tria = stl_mesh->n_faces;
1492 cs_lnum_t n_points = n_tria*3;
1493
1494 for (cs_lnum_t i = 0; i < n_points; i++) {
1495 for (cs_lnum_t j = 0; j < 3; j++) {
1496 stl_mesh->coords[i][j] += vector[j];
1497 }
1498 }
1499
1500 _update_init_mesh(stl_mesh);
1501
1502 }
1503
1504 /*----------------------------------------------------------------------------*/
1505 /*!
1506 * \brief Apply a rotation to a STL mesh structure.
1507 *
1508 * \param[in] stl_mesh pointer to the associated STL mesh structure
1509 * \param[in] theta rotation angle
1510 * \param[in] axis rotation axis
1511 * \param[in] center rotation center
1512 */
1513 /*----------------------------------------------------------------------------*/
1514
1515 void
cs_stl_mesh_rotate(cs_stl_mesh_t * stl_mesh,double theta,double axis[3],double center[3])1516 cs_stl_mesh_rotate(cs_stl_mesh_t *stl_mesh,
1517 double theta,
1518 double axis[3],
1519 double center[3])
1520 {
1521 double matrix[3][4];
1522
1523 cs_rotation_matrix(theta, axis, center, matrix);
1524
1525 cs_stl_mesh_transform(stl_mesh, matrix);
1526 }
1527
1528 /*----------------------------------------------------------------------------*/
1529 /*!
1530 * \brief Apply a scaling to a STL mesh structure.
1531 *
1532 * \param[in] stl_mesh pointer to the associated STL mesh structure
1533 * \param[in] vector translation vector
1534 */
1535 /*----------------------------------------------------------------------------*/
1536
1537 void
cs_stl_mesh_scale(cs_stl_mesh_t * stl_mesh,double scale)1538 cs_stl_mesh_scale(cs_stl_mesh_t *stl_mesh,
1539 double scale)
1540 {
1541 cs_lnum_t n_tria = stl_mesh->n_faces;
1542 cs_lnum_t n_points = n_tria*3;
1543
1544 for (cs_lnum_t i = 0; i < n_points; i++) {
1545 for (cs_lnum_t j = 0; j < 3; j++) {
1546 stl_mesh->coords[i][j] *= scale;
1547 }
1548 }
1549 }
1550
1551 /*----------------------------------------------------------------------------*/
1552 /*!
1553 * \brief Set the points outside he STL geometry. Those points will be used as
1554 * \brief seeds to propagate porosity values outside the STL geometry.
1555 *
1556 * \param[in] stl_mesh pointer to the associated STL mesh structure
1557 * \param[in] n_points number of points
1558 * \param[in] coords coordinates (x1,y1,z1,...,xn,yn,zn) (size : 3*n_point)
1559 */
1560 /*----------------------------------------------------------------------------*/
1561
1562 void
cs_stl_set_porosity_seed(cs_stl_mesh_t * stl_mesh,int n_points,cs_real_t * coords)1563 cs_stl_set_porosity_seed(cs_stl_mesh_t *stl_mesh,
1564 int n_points,
1565 cs_real_t *coords)
1566 {
1567 stl_mesh->n_seeds = n_points;
1568 BFT_REALLOC(stl_mesh->seed_coords, n_points*3, cs_real_t);
1569
1570 for (int i = 0; i < 3*n_points; i++)
1571 stl_mesh->seed_coords[i] = coords[i];
1572 }
1573
1574 /*----------------------------------------------------------------------------*/
1575 /*!
1576 * \brief Return writer_id used for stl meshes. 0 means unused.
1577 */
1578 /*----------------------------------------------------------------------------*/
1579
1580 int
cs_stl_post_get_writer_id(void)1581 cs_stl_post_get_writer_id(void)
1582 {
1583
1584 return _stl_meshes.writer_id;
1585
1586 }
1587
1588 /*----------------------------------------------------------------------------*/
1589 /*!
1590 * \brief Create a new writer that will contains the STL mesh added by the user
1591 * \brief The writer_id is stored in the cs_glob_stl_meshes structure.
1592 *
1593 * \param[in] time_dep > 1 if the writer is transient, else writer is fixed
1594 */
1595 /*----------------------------------------------------------------------------*/
1596
1597 void
cs_stl_post_init_writer(const char * case_name,const char * dir_name,const char * fmt_name,const char * fmt_opts,fvm_writer_time_dep_t time_dep,bool output_at_start,bool output_at_end,int frequency_n,double frequency_t)1598 cs_stl_post_init_writer(const char *case_name,
1599 const char *dir_name,
1600 const char *fmt_name,
1601 const char *fmt_opts,
1602 fvm_writer_time_dep_t time_dep,
1603 bool output_at_start,
1604 bool output_at_end,
1605 int frequency_n,
1606 double frequency_t)
1607 {
1608 /* We check if a writer_id has already been defined.*/
1609 bool writer_exists = false;
1610 if ( _stl_meshes.writer_id != 0)
1611 writer_exists = true;
1612
1613 /* If a writer do no already exist, create it */
1614 if (!writer_exists) {
1615 int writer_id = cs_post_get_free_writer_id();
1616 _stl_meshes.writer_id = writer_id;
1617
1618 /* Add writer */
1619 cs_post_define_writer(writer_id, /* writer_id */
1620 case_name, /* writer name */
1621 dir_name, /* directory name */
1622 fmt_name, /* format_name */
1623 fmt_opts, /* format_options */
1624 time_dep,
1625 output_at_start, /* output_at_start */
1626 output_at_end, /* output_at_end */
1627 frequency_n, /* frequency_n */
1628 frequency_t); /* frequency_t */
1629 }
1630 }
1631
1632 /*----------------------------------------------------------------------------*/
1633 /*!
1634 * \brief Associate a STL mesh to the default writer
1635 *
1636 * \param[in] stl_mesh pointer to the associated STL mesh structure
1637 */
1638 /*----------------------------------------------------------------------------*/
1639
1640 void
cs_stl_post_add_mesh(cs_stl_mesh_t * stl_mesh)1641 cs_stl_post_add_mesh(cs_stl_mesh_t *stl_mesh)
1642 {
1643 if (_stl_meshes.writer_id == 0)
1644 bft_error(__FILE__, __LINE__, 0,
1645 _("No writer was defined for STL mesh output\n"
1646 "cs_stl_post_init_writer should be called first.\n"));
1647
1648 int writer_ids[] = {_stl_meshes.writer_id};
1649 int stl_mesh_id = cs_post_get_free_mesh_id();
1650 cs_post_define_existing_mesh(stl_mesh_id,
1651 stl_mesh->ext_mesh,
1652 0,
1653 true,
1654 false,
1655 1,
1656 writer_ids);
1657
1658 cs_post_write_meshes(NULL);
1659 }
1660
1661 /*----------------------------------------------------------------------------*/
1662 /*!
1663 * \brief Write a binary STL file according to a given STL mesh structure
1664 *
1665 * \param[in] stl_mesh pointer to the associated STL mesh structure
1666 * \param[in] path path to the STL file
1667 */
1668 /*----------------------------------------------------------------------------*/
1669
1670 void
cs_stl_file_write(cs_stl_mesh_t * stl_mesh,const char * path)1671 cs_stl_file_write(cs_stl_mesh_t *stl_mesh,
1672 const char *path)
1673 {
1674 /* Output only on rank 0 for now */
1675 if (cs_glob_rank_id > 0)
1676 return;
1677
1678 uint8_t buf[128];
1679 FILE *fp;
1680
1681 /* Open STL file */
1682 fp = fopen(path,"wb");
1683 if (fp == NULL) {
1684 bft_error(__FILE__, __LINE__, 0,
1685 _("Error opening file \"%s\":\n\n"
1686 " %s"), path, strerror(errno));
1687 }
1688
1689 /* Write header */
1690 char header[80] = "Exported from code_saturne";
1691 memcpy(buf, header, 80);
1692
1693 /* Cut number of triangles in 4 8bytes unsigned int */
1694 uint32_t ntri = (uint32_t)stl_mesh->n_faces;
1695 _cut32(buf+80,ntri);
1696
1697 fwrite(buf, 84, 1, fp);
1698
1699 /* Loop on each triangle */
1700 for (int i = 0; i < stl_mesh->n_faces; i++) {
1701
1702 uint8_t *start = buf + 12;
1703
1704 /* Compute and Write the face normal coordinates */
1705 float normals[3];
1706 float a[3], b[3], c[3];
1707
1708 for (int dir = 0; dir < 3; dir ++) {
1709 a[dir] = (float)stl_mesh->coords[3*i + 0][dir];
1710 b[dir] = (float)stl_mesh->coords[3*i + 1][dir];
1711 c[dir] = (float)stl_mesh->coords[3*i + 2][dir];
1712 }
1713
1714 // ab vect ac
1715 normals[0] = (b[1]-a[1])*(c[2]-a[2]) - (b[2]-a[2])*(c[1]-a[1]);
1716 normals[1] = (b[2]-a[2])*(c[0]-a[0]) - (b[0]-a[0])*(c[2]-a[2]);
1717 normals[2] = (b[0]-a[0])*(c[1]-a[1]) - (b[1]-a[1])*(c[0]-a[0]);
1718
1719 float norm = sqrt( normals[0]*normals[0]
1720 + normals[1]*normals[1]
1721 + normals[2]*normals[2]);
1722
1723 for (int dir = 0; dir < 3; dir ++)
1724 normals[dir] /= norm;
1725
1726 uint32_t n_temp[3];
1727 for (int dir = 0; dir < 3; dir ++) {
1728 memcpy(&n_temp[dir],
1729 &normals[dir],
1730 sizeof n_temp[dir]);
1731
1732 _cut32(buf + 4*dir, n_temp[dir]);
1733 }
1734
1735 /* Write the 3 vertices for the current triangle */
1736 for (int vi = 0; vi < 3; vi ++) {
1737
1738 uint32_t v_temp[3];
1739 for (int dir = 0; dir < 3; dir ++) {
1740 float loc_coord = (float)stl_mesh->coords[3*i + vi][dir];
1741 memcpy(&v_temp[dir],
1742 &loc_coord,
1743 sizeof v_temp[dir]);
1744 _cut32(start + 3*4*vi + 4*dir, v_temp[dir]);
1745 }
1746 }
1747
1748 fwrite(buf, 50, 1, fp);
1749 }
1750
1751 fclose(fp);
1752 }
1753
1754 /*----------------------------------------------------------------------------*/
1755 /*!
1756 * \brief Compute intersection between a STL mesh and the main mesh.
1757 *
1758 * \param[in] stl_mesh pointer to the associated STL mesh structure
1759 * \param[in] n_input number of cells on which intersection is done
1760 * \param[in] input_idx index of input cells (size: input_idx)
1761 * \param[out] n_selected_cells number of output intersecting cells
1762 * \param[out] selected_cells index of output cells (size: output_idx)
1763 * \param[out] tria_in_cell_idx start index of triangle intersecting each cell
1764 * (size: n_output)
1765 * \param[out] tria_in_cell_lst list of triangles in intersecting cells
1766 * \param[in,out] max_size maximum size of tria_in_cell_lst array
1767 */
1768 /*----------------------------------------------------------------------------*/
1769
1770 void
cs_stl_intersection(cs_stl_mesh_t * stl_mesh,cs_lnum_t n_input,cs_lnum_t * input_idx,cs_lnum_t * n_selected_cells,cs_lnum_t * selected_cells,cs_lnum_t * tria_in_cell_idx,cs_lnum_t ** tria_in_cell_lst,cs_lnum_t * max_size)1771 cs_stl_intersection(cs_stl_mesh_t *stl_mesh,
1772 cs_lnum_t n_input,
1773 cs_lnum_t *input_idx,
1774 cs_lnum_t *n_selected_cells,
1775 cs_lnum_t *selected_cells,
1776 cs_lnum_t *tria_in_cell_idx,
1777 cs_lnum_t **tria_in_cell_lst,
1778 cs_lnum_t *max_size)
1779 {
1780 cs_mesh_t *m = cs_glob_mesh;
1781
1782 cs_lnum_t n_cells = n_input;// Local number of cells of the main mesh
1783 cs_lnum_t n_tria_stl = stl_mesh->n_faces; // Local number of triangles of the STL
1784 cs_lnum_t n_boxes = n_cells + n_tria_stl;
1785 int dim = 3;
1786
1787 cs_lnum_t *_tria_in_cell_lst = NULL;
1788 if (tria_in_cell_lst != NULL)
1789 _tria_in_cell_lst = *tria_in_cell_lst;
1790
1791 cs_gnum_t *box_gnum = NULL;
1792 cs_coord_t *extents = NULL;
1793
1794 BFT_MALLOC(box_gnum, n_boxes, cs_gnum_t);
1795 BFT_MALLOC(extents , 2*dim*n_boxes, cs_coord_t);
1796
1797 /* Global numbering construction */
1798 for (cs_lnum_t i = 0; i < n_cells; i++)
1799 box_gnum[i] = i + 1;
1800
1801 for (cs_lnum_t i = 0; i < n_tria_stl; i++)
1802 box_gnum[i + n_cells] = n_cells + i + 1;
1803
1804
1805 /* Compute extents */
1806
1807 // For the main mesh
1808 cs_real_6_t *bbox = NULL;
1809 bbox = cs_mesh_quantities_cell_extents(m, 0.0);
1810
1811 for (cs_lnum_t i = 0; i < n_cells; i++)
1812 for (int id = 0; id < 6; id ++)
1813 extents[6*i + id] = bbox[input_idx[i]][id];
1814
1815 BFT_FREE(bbox);
1816
1817 // For the STL mesh
1818 BFT_MALLOC(bbox, n_tria_stl, cs_real_6_t);
1819
1820 for (cs_lnum_t i = 0; i < n_tria_stl; i++) {
1821 bbox[i][0] = HUGE_VAL;
1822 bbox[i][1] = HUGE_VAL;
1823 bbox[i][2] = HUGE_VAL;
1824 bbox[i][3] = -HUGE_VAL;
1825 bbox[i][4] = -HUGE_VAL;
1826 bbox[i][5] = -HUGE_VAL;
1827 }
1828
1829 for (cs_lnum_t i = 0; i < n_tria_stl; i++) {
1830 for (int vtx_id = 0; vtx_id < 3; vtx_id ++) {
1831 bbox[i][0] = CS_MIN(bbox[i][0], stl_mesh->coords[3*i + vtx_id][0]);
1832 bbox[i][3] = CS_MAX(bbox[i][3], stl_mesh->coords[3*i + vtx_id][0]);
1833 bbox[i][1] = CS_MIN(bbox[i][1], stl_mesh->coords[3*i + vtx_id][1]);
1834 bbox[i][4] = CS_MAX(bbox[i][4], stl_mesh->coords[3*i + vtx_id][1]);
1835 bbox[i][2] = CS_MIN(bbox[i][2], stl_mesh->coords[3*i + vtx_id][2]);
1836 bbox[i][5] = CS_MAX(bbox[i][5], stl_mesh->coords[3*i + vtx_id][2]);
1837 }
1838 }
1839
1840 for (cs_lnum_t i = 0; i < n_tria_stl; i++) {
1841 for (cs_lnum_t id = 0; id < 6; id ++)
1842 extents[6*(i+n_cells) + id] = bbox[i][id];
1843 }
1844
1845 BFT_FREE(bbox);
1846
1847 fvm_neighborhood_t *cell_neighborhood = NULL;
1848
1849 #if defined HAVE_MPI
1850 cell_neighborhood = fvm_neighborhood_create(MPI_COMM_NULL);
1851 #else
1852 cell_neighborhood = fvm_neighborhood_create();
1853 #endif
1854
1855 // Compute intersections by boxes (transfer ownership)
1856 fvm_neighborhood_by_boxes( cell_neighborhood,
1857 dim,
1858 n_boxes,
1859 box_gnum,
1860 extents,
1861 NULL,
1862 NULL );
1863
1864 cs_lnum_t n_elts = 0;
1865 cs_gnum_t *elt_num = NULL;
1866 cs_lnum_t *neighbor_index = NULL;
1867 cs_gnum_t *neighbor_num = NULL;
1868
1869 // Get the data back
1870 fvm_neighborhood_transfer_data( cell_neighborhood,
1871 &n_elts,
1872 &elt_num,
1873 &neighbor_index,
1874 &neighbor_num );
1875
1876 cs_gnum_t _n_cells = n_cells;
1877 cs_lnum_t _n_selected_cells = 0;
1878 cs_lnum_t idx_num = 0;
1879
1880 /* Init of list if needed */
1881 if (tria_in_cell_idx != NULL)
1882 tria_in_cell_idx[_n_selected_cells] = 0;
1883
1884 /* Loop on the elements that have neighbors */
1885 for (cs_lnum_t i = 0; i < n_elts; i++) {
1886
1887 // Check if the element is a box from the main mesh
1888 if (elt_num[i] <= _n_cells) {
1889
1890 cs_lnum_t cell_id = elt_num[i] - 1;
1891 cs_lnum_t nb_tri_in_cell = 0;
1892
1893 // Loop on the neighbors
1894 for (cs_lnum_t j = neighbor_index[i]; j < neighbor_index[i+1]; j++) {
1895
1896 // If the current neighbor is a box from the STL,
1897 // -> the BB from the cell intersects the BB from
1898 // the STL
1899 if (neighbor_num[j] > _n_cells) {
1900
1901 // Local number of the triangle
1902 cs_lnum_t ntria_id = neighbor_num[j] - n_cells - 1;
1903
1904 cs_real_t tri_coords[3][3];
1905 for (cs_lnum_t k = 0; k < 3; k++) {
1906 for (cs_lnum_t l = 0; l < 3; l++)
1907 tri_coords[k][l] = stl_mesh->coords[3*ntria_id + k][l];
1908 }
1909
1910 // Additional tests to know weather the triangle
1911 // overlap the cell bounding box
1912 bool triangle_box_intersect
1913 = _triangle_box_intersect(&extents[6*cell_id],
1914 tri_coords);
1915
1916
1917 if (triangle_box_intersect) {
1918 nb_tri_in_cell++;
1919 if (tria_in_cell_lst != NULL) {
1920 if (idx_num >= *max_size) {
1921 *max_size *= 2;
1922 BFT_REALLOC(_tria_in_cell_lst, *max_size, cs_lnum_t);
1923 }
1924 _tria_in_cell_lst[idx_num] = ntria_id;
1925 idx_num ++;
1926 }
1927
1928 }
1929
1930 }
1931
1932 } // End loop on neighbors
1933
1934 // If at least one time a triangle neigbor was found in the
1935 // previous loop, tag the cell.
1936 if (nb_tri_in_cell > 0) {
1937 selected_cells[_n_selected_cells] = input_idx[cell_id];
1938 if (tria_in_cell_idx != NULL)
1939 tria_in_cell_idx[_n_selected_cells + 1] = idx_num;
1940 _n_selected_cells ++;
1941 }
1942
1943 }
1944 }
1945
1946 *n_selected_cells = _n_selected_cells;
1947 if (tria_in_cell_lst != NULL)
1948 *tria_in_cell_lst = _tria_in_cell_lst;
1949
1950 fvm_neighborhood_destroy(&cell_neighborhood);
1951
1952 BFT_FREE(elt_num);
1953 BFT_FREE(neighbor_index);
1954 BFT_FREE(neighbor_num);
1955 BFT_FREE(box_gnum);
1956 BFT_FREE(extents);
1957 }
1958
1959 /*----------------------------------------------------------------------------*/
1960 /*!
1961 * \brief Refine the mesh following a given STL mesh
1962 *
1963 * \param[in] stl_mesh pointer to the associated STL mesh structure
1964 * \param[in] n_ref level of refinement
1965 * \param[in] n_add_layer additional layers between two refinement stage
1966 */
1967 /*----------------------------------------------------------------------------*/
1968
1969 void
cs_stl_refine(cs_stl_mesh_t * stl_mesh,int n_ref,int n_add_layer)1970 cs_stl_refine(cs_stl_mesh_t *stl_mesh,
1971 int n_ref,
1972 int n_add_layer)
1973 {
1974 cs_mesh_t *m = cs_glob_mesh;
1975
1976 /* Initialisation of the selection criteria (first level) */
1977 cs_lnum_t n_input_cells = m->n_cells;
1978 cs_lnum_t *input_cells = NULL;
1979 BFT_MALLOC(input_cells, m->n_cells, cs_lnum_t);
1980 for (cs_lnum_t i = 0; i < m->n_cells; i++)
1981 input_cells[i] = i;
1982
1983 /* Begining of the refinement loop */
1984 for (int n_level = 0; n_level < n_ref+1; n_level ++) {
1985
1986 if (cs_glob_rank_id < 1)
1987 bft_printf("Refinement %d\n",n_level);
1988
1989 cs_lnum_t n_selected_cells = 0;
1990 cs_lnum_t *selected_cells = NULL;
1991 BFT_MALLOC(selected_cells, m->n_cells, cs_lnum_t);
1992
1993 /* Select previous refined cells */
1994 if (n_level > 0) {
1995 BFT_REALLOC(input_cells, m->n_cells, cs_lnum_t);
1996
1997 char criteria[100];
1998 sprintf(criteria,"STL_refined_region_%d",n_level-1);
1999 cs_selector_get_cell_list(criteria,
2000 &n_input_cells,
2001 input_cells );
2002 }
2003
2004 /* Compute mesh/STL intersection */
2005 cs_stl_intersection( stl_mesh,
2006 n_input_cells,
2007 input_cells,
2008 &n_selected_cells,
2009 selected_cells,
2010 NULL,
2011 NULL,
2012 NULL);
2013
2014 /* If no intersected cells, do not perform refinement */
2015 cs_lnum_t n_intersected_cells = n_selected_cells;
2016 cs_parall_sum(1, CS_LNUM_TYPE, &n_intersected_cells);
2017 if (n_intersected_cells == 0)
2018 bft_error(__FILE__, __LINE__, 0,
2019 "Error in function cs_stl_refine: no intersection\n"
2020 "detected between the given STL file and the main mesh \n");
2021
2022 /* Add incremented group */
2023 char group_name[100];
2024 sprintf(group_name,"STL_refined_region_%d",n_level);
2025 cs_mesh_group_cells_add(m,
2026 group_name,
2027 n_selected_cells,
2028 selected_cells);
2029
2030 /* Perform refinement */
2031 if (n_level < n_ref) {
2032
2033 int *cell_tag;
2034 BFT_MALLOC(cell_tag, m->n_cells_with_ghosts, int);
2035
2036 for (cs_lnum_t c_id = 0; c_id < m->n_cells; c_id ++) {
2037 cell_tag[c_id] = 0;
2038 for (cs_lnum_t i = 0; i < n_selected_cells; i++) {
2039 if (c_id == selected_cells[i]) cell_tag[c_id] = 1;
2040 }
2041 }
2042
2043 if (m->halo!=NULL) {
2044 cs_halo_set_use_barrier(1);
2045 cs_halo_sync_num(m->halo, CS_HALO_STANDARD, cell_tag);
2046 }
2047
2048 /* Here we add additionnal layers of refined cells
2049 * around the original STL selected cells */
2050 int nn = 1;
2051
2052 for (int k = 0; k < n_add_layer ; k++){
2053 for (cs_lnum_t face_id = 0; face_id < m->n_i_faces; face_id++){
2054 cs_lnum_t c1 = m->i_face_cells[face_id][0];
2055 cs_lnum_t c2 = m->i_face_cells[face_id][1];
2056 if (cell_tag[c1] == 0 && cell_tag[c2] == nn)
2057 cell_tag[c1] = nn+1;
2058 if (cell_tag[c2] == 0 && cell_tag[c1] == nn)
2059 cell_tag[c2] = nn+1;
2060 }
2061
2062 if (m->halo!=NULL)
2063 cs_halo_sync_num(m->halo, CS_HALO_STANDARD, cell_tag);
2064 nn ++;
2065 }
2066
2067 n_selected_cells = 0;
2068 for (cs_lnum_t c_id = 0; c_id < m->n_cells; c_id ++) {
2069 if (cell_tag[c_id] > 0) {
2070 selected_cells[n_selected_cells] = c_id;
2071 n_selected_cells ++;
2072 }
2073 }
2074
2075 BFT_FREE(cell_tag);
2076
2077 cs_mesh_refine_simple_selected(m,
2078 false,
2079 n_selected_cells,
2080 selected_cells);
2081 }
2082
2083 BFT_FREE(selected_cells);
2084
2085 /* Every 2 refinemet stages and at the end, re-partition
2086 * the mesh to limit imbalance in the processors load */
2087
2088 if ((n_level%2 == 0 || n_level == n_ref -1)
2089 && cs_glob_rank_id > -1) {
2090
2091 cs_mesh_builder_destroy(&cs_glob_mesh_builder);
2092 cs_glob_mesh_builder = cs_mesh_builder_create();
2093 cs_mesh_to_builder(m, cs_glob_mesh_builder, true, NULL);
2094
2095 cs_partition(m, cs_glob_mesh_builder, CS_PARTITION_MAIN);
2096
2097 cs_mesh_from_builder(m, cs_glob_mesh_builder);
2098 cs_mesh_init_halo(m, cs_glob_mesh_builder, m->halo_type, -1, true);
2099 }
2100
2101 cs_mesh_update_auxiliary(m);
2102 }
2103
2104 BFT_FREE(input_cells);
2105 }
2106
2107 /*----------------------------------------------------------------------------*/
2108 /*!
2109 * \brief Compute porosity field according to a given STL mesh
2110 *
2111 * \param[in] stl_mesh pointer to the associated STL mesh structure
2112 * \param[out] porosity interpolated porosity field
2113 * \param[out] indic indicator of the STL location
2114 */
2115 /*----------------------------------------------------------------------------*/
2116
2117 void
cs_stl_compute_porosity(cs_stl_mesh_t * stl_mesh,cs_real_t * porosity,int * indic)2118 cs_stl_compute_porosity(cs_stl_mesh_t *stl_mesh,
2119 cs_real_t *porosity,
2120 int *indic)
2121 {
2122 cs_mesh_t *m = cs_glob_mesh;
2123 cs_mesh_quantities_t *mq = cs_glob_mesh_quantities;
2124
2125 cs_stl_mesh_t *stl = stl_mesh;
2126
2127 const cs_real_t *volume = (const cs_real_t *)mq->cell_vol;
2128 const cs_real_3_t *xyzcen = (const cs_real_3_t *)mq->cell_cen;
2129 const cs_real_3_t *cdgfac = (const cs_real_3_t *)mq->i_face_cog;
2130 const cs_real_3_t *cdgfbo = (const cs_real_3_t *)mq->b_face_cog;
2131 const cs_lnum_2_t *ifacel = (const cs_lnum_2_t *)m->i_face_cells;
2132 const cs_lnum_t *ifabor = (const cs_lnum_t *)m->b_face_cells;
2133
2134 /* If no reference point outside the STL file is given
2135 * by the user, the porosity computation will fail */
2136 if (stl_mesh->seed_coords == NULL || stl_mesh->n_seeds == 0)
2137 bft_error(__FILE__, __LINE__, 0,
2138 _("Error in STL porosity computation: no seed points"
2139 "for STL mesh %s is given."), stl_mesh->name);
2140
2141 /* Initialisation */
2142 cs_lnum_t n_input_cells = m->n_cells;
2143 cs_lnum_t n_selected_cells = 0;
2144 cs_lnum_t *input_cells = NULL;
2145 cs_lnum_t *selected_cells = NULL;
2146 cs_lnum_t *tria_in_cell_lst = NULL;
2147 cs_lnum_t *tria_in_cell_idx = NULL;
2148 cs_lnum_t *cell_selected_idx = NULL;
2149 cs_real_t *mean_plane_def = NULL;
2150 cs_real_t *stl_normals = NULL;
2151
2152 cs_lnum_t max_size = stl->n_faces;
2153
2154 BFT_MALLOC(input_cells , m->n_cells , cs_lnum_t);
2155 BFT_MALLOC(selected_cells , m->n_cells , cs_lnum_t);
2156 BFT_MALLOC(tria_in_cell_idx , m->n_cells , cs_lnum_t);
2157 BFT_MALLOC(tria_in_cell_lst , max_size , cs_lnum_t);
2158 BFT_MALLOC(cell_selected_idx, m->n_cells , cs_lnum_t);
2159 BFT_MALLOC(stl_normals , stl->n_faces*3 , cs_real_t);
2160 BFT_MALLOC(mean_plane_def , m->n_cells*6 , cs_real_t);
2161
2162 /* Input cells are all[] cells and initialisation */
2163 for (cs_lnum_t i = 0; i < m->n_cells; i++) {
2164 input_cells[i] = i;
2165 cell_selected_idx[i] = -1;
2166 porosity[i] = 0.0;
2167 }
2168
2169 /* Compute normals */
2170 for (cs_lnum_t i = 0; i < stl->n_faces; i++)
2171 _compute_normal(stl_mesh->coords + (3*i), stl_normals + (3*i));
2172
2173 /* Get the intersection
2174 * ==================== */
2175 cs_stl_intersection(stl_mesh,
2176 n_input_cells,
2177 input_cells,
2178 &n_selected_cells,
2179 selected_cells,
2180 tria_in_cell_idx,
2181 &tria_in_cell_lst,
2182 &max_size);
2183
2184 /* Compute the bounding boxes of the main mesh */
2185 cs_real_6_t *bbox = NULL;
2186 bbox = cs_mesh_quantities_cell_extents(m, 0.0);
2187
2188 /* If a cell is overlaped by more than 1 triangle,
2189 * replace those triangles by a mean plane
2190 * ============================================== */
2191 for (cs_lnum_t i = 0; i < n_selected_cells; i ++) {
2192
2193 cs_lnum_t start_id = tria_in_cell_idx[i];
2194 cs_lnum_t end_id = tria_in_cell_idx[i+1];
2195 cs_lnum_t n_tria_in_cell = end_id - start_id;
2196
2197 cs_lnum_t cell_id = selected_cells[i];
2198
2199 if (n_tria_in_cell > 1) {
2200
2201 cs_real_t surftot = 0;
2202
2203 // Mean plane defined by normal and point
2204 cs_real_t n_plan[3], p_plan[3];
2205 for (int k = 0; k < 3; k++) {
2206 n_plan[k] = 0.;
2207 p_plan[k] = 0.;
2208 }
2209
2210 /* Loop on triangles intersecting the current cell */
2211 for (cs_lnum_t tri = start_id; tri < end_id; tri ++) {
2212
2213 cs_lnum_t f_tria_id = tria_in_cell_lst[tri];
2214
2215 cs_real_3_t *coords = stl_mesh->coords + (3*f_tria_id);
2216 cs_real_t *n = stl_normals + (3*f_tria_id);
2217 cs_real_t *aabb = &bbox[cell_id][0];
2218 const cs_real_t *cog = xyzcen[cell_id];
2219
2220 /* Compute the surface of the triangle in the box */
2221 cs_real_t surf = _exact_triangle_box_surface_intersection(aabb, coords);
2222 surftot += surf;
2223
2224 /* Center of gravity of triangle */
2225 cs_real_t cog_tri[3];
2226 for (int k = 0; k < 3; k++)
2227 cog_tri[k] = (coords[0][k] + coords[1][k] + coords[2][k]) / 3.0;
2228
2229 double psca = cs_math_3_distance_dot_product(cog, cog_tri, n);
2230 psca *= surf;
2231
2232 /* Weigh triangle normal and point by the
2233 * surface included in the box */
2234 for (int k = 0; k < 3; k++) {
2235 n_plan[k] += surf * n[k];
2236 p_plan[k] += psca * n[k];
2237
2238 }
2239
2240 } // End loop on tri intersecting the cell
2241
2242 /* Normalisation */
2243 cs_real_t nn = cs_math_3_norm(n_plan);
2244
2245 for (int k = 0; k < 3; k++) {
2246 n_plan[k] /= cs_math_fmax(nn, 1.e-20);
2247 p_plan[k] /= cs_math_fmax(surftot, 1.e-20);
2248 }
2249
2250 /* Mean plane definition storage */
2251 for (int k = 0; k < 3; k++) {
2252 mean_plane_def[6*i + k] = p_plan[k] + xyzcen[cell_id][k];
2253 mean_plane_def[6*i + 3 + k] = n_plan[k];
2254 }
2255
2256
2257 /* else if only one triangle in cell */
2258 } else {
2259 cs_lnum_t f_tria_id = tria_in_cell_lst[start_id];
2260
2261 for (int k = 0; k < 3; k++) {
2262 mean_plane_def[6*i + k] = stl_mesh->coords[3*f_tria_id][k];
2263 mean_plane_def[6*i + 3 + k] = stl_normals[3*f_tria_id + k];
2264 }
2265
2266 }// End test on n_tria_in_cell
2267
2268 // Building indirection
2269 cell_selected_idx[cell_id] = i;
2270 } // End loop on selected_cells
2271
2272 /* Porosity computation on intersecting cells
2273 * ========================================== */
2274
2275 cs_real_t vol;
2276
2277 /* Loop on internal faces */
2278 for (cs_lnum_t face_id = 0; face_id < m->n_i_faces; face_id ++) {
2279
2280 /* Loop on adjacent cells */
2281 for (int k = 0; k < 2; k++) {
2282 cs_lnum_t cell_id = ifacel[face_id][k];
2283 cs_lnum_t idx;
2284
2285 /* Test if the cell is ghost cell */
2286 if (cell_id < m->n_cells) {
2287 idx = cell_selected_idx[cell_id];
2288
2289 /* Test if the cell was a previously selected
2290 * intersecting cell*/
2291 if (idx > -1) {
2292 cs_real_t xf[3], xc[3];
2293
2294 for (int i = 0; i < 3; i++) {
2295 xf[i] = cdgfac[face_id][i];
2296 xc[i] = xyzcen[cell_id][i];
2297 }
2298
2299 cs_real_t *tria_mean_plane = &mean_plane_def[6*idx];
2300
2301 /* For each face, tetrahedra based one edge, the cog of
2302 * the face and the cog of the cell, are built*/
2303 cs_lnum_t vtx_start = m->i_face_vtx_idx[face_id];
2304 cs_lnum_t vtx_end = m->i_face_vtx_idx[face_id + 1];
2305 cs_lnum_t nb_vtx = vtx_end - vtx_start;
2306
2307 for (cs_lnum_t vtx = vtx_start; vtx < vtx_end; vtx++) {
2308
2309 // When vtx = vtx_end-1, vtx+1 has to point towards vtx_start
2310 cs_lnum_t vtx1 = vtx;
2311 cs_lnum_t vtx2 = vtx_start + (vtx+1-vtx_start) % nb_vtx;
2312
2313 cs_lnum_t vtx1_id = m->i_face_vtx_lst[vtx1];
2314 cs_lnum_t vtx2_id = m->i_face_vtx_lst[vtx2];
2315
2316 cs_real_t *x1 = &(m->vtx_coord[3*vtx1_id]);
2317 cs_real_t *x2 = &(m->vtx_coord[3*vtx2_id]);
2318
2319 vol = _tetrahedron_plane_volume_intersection(x1, x2, xf, xc, tria_mean_plane);
2320
2321 porosity[cell_id] += vol;
2322 }
2323
2324 } // End test if it is an intersecting cell
2325
2326 } // End test if cell in not a ghost
2327
2328 } // End loop on adjacent cells
2329
2330 } // End loop on internal faces
2331
2332 /* Loop on boundary faces */
2333 for (cs_lnum_t face_id = 0; face_id < m->n_b_faces; face_id ++) {
2334
2335 cs_lnum_t cell_id = ifabor[face_id];
2336 cs_lnum_t idx;
2337
2338 /* Test if the cell is ghost cell */
2339 if (cell_id < m->n_cells) {
2340 idx = cell_selected_idx[cell_id];
2341
2342 /* Test if the cell was a previously selected
2343 * intersecting cell*/
2344 if (idx > -1) {
2345 cs_real_t xf[3], xc[3];
2346
2347 for (int i = 0; i < 3; i++) {
2348 xf[i] = cdgfbo[face_id][i];
2349 xc[i] = xyzcen[cell_id][i];
2350 }
2351
2352 cs_real_t *tria_mean_plane = &mean_plane_def[6*idx];
2353
2354 /* For each face, tetrahedra based one edge, the cog of
2355 * the face and the cog of the cell, are built*/
2356 cs_lnum_t vtx_start = m->b_face_vtx_idx[face_id];
2357 cs_lnum_t vtx_end = m->b_face_vtx_idx[face_id + 1];
2358 cs_lnum_t nb_vtx = vtx_end - vtx_start;
2359
2360 for (cs_lnum_t vtx = vtx_start; vtx < vtx_end; vtx++) {
2361
2362 // When vtx = vtx_end-1, vtx+1 has to point towards vtx_start
2363 cs_lnum_t vtx1 = vtx;
2364 cs_lnum_t vtx2 = vtx_start + (vtx+1-vtx_start) % nb_vtx;
2365
2366 cs_lnum_t vtx1_id = m->b_face_vtx_lst[vtx1];
2367 cs_lnum_t vtx2_id = m->b_face_vtx_lst[vtx2];
2368
2369 cs_real_t *x1 = &(m->vtx_coord[3*vtx1_id]);
2370 cs_real_t *x2 = &(m->vtx_coord[3*vtx2_id]);
2371
2372 vol = _tetrahedron_plane_volume_intersection(x1, x2, xf, xc, tria_mean_plane);
2373
2374 porosity[cell_id] += vol;
2375
2376 }
2377
2378 } // End test if it is an intersecting cell
2379
2380 } // End test if cell in not a ghost
2381
2382 } // End loop on boundary faces
2383
2384 /* Normalisation and clipping of the porosity field */
2385 for (cs_lnum_t i = 0; i < n_selected_cells; i++) {
2386 cs_lnum_t cell_id = selected_cells[i];
2387 porosity[cell_id] /= volume[cell_id];
2388 porosity[cell_id] = CS_MAX(porosity[cell_id],0.0);
2389 porosity[cell_id] = CS_MIN(porosity[cell_id],1.0);
2390
2391 if (porosity[cell_id] < 1.e-5)
2392 porosity[cell_id] = 0.0;
2393 }
2394
2395 if (m->halo!=NULL)
2396 cs_halo_sync_var(m->halo, CS_HALO_STANDARD, porosity);
2397
2398 /* Porosity : filling the entire domain
2399 * ==================================== */
2400
2401 int *cell_tag = NULL;
2402 BFT_MALLOC(cell_tag, m->n_cells_with_ghosts, int);
2403
2404 /*-------------------------
2405 * cell_tag is :
2406 * - 1 inside solid
2407 * - 0 on the solid border
2408 * - -1 outside the solid
2409 * ------------------------*/
2410
2411 for (cs_lnum_t cell_id = 0; cell_id < m->n_cells_with_ghosts; cell_id ++) {
2412 cell_tag[cell_id] = 1;
2413 if (cell_id < m->n_cells)
2414 /* If the cell in a border of the STL mesh */
2415 if (cell_selected_idx[cell_id] > -1)
2416 cell_tag[cell_id] = 0;
2417 }
2418
2419
2420 /* Tag outside cells according to user presribed points */
2421 for (int p = 0; p < stl_mesh->n_seeds; p++) {
2422
2423 cs_real_t *xyz_ref = &(stl_mesh->seed_coords[3*p]);
2424 cs_lnum_t ref_id;
2425 int ref_rank;
2426
2427 cs_geom_closest_point(m->n_cells,
2428 (const cs_real_3_t *)(mq->cell_cen),
2429 xyz_ref,
2430 &ref_id,
2431 &ref_rank);
2432
2433 if (ref_rank == cs_glob_rank_id)
2434 cell_tag[ref_id] = -1;
2435 }
2436
2437 /* Sychronize */
2438 if (m->halo!=NULL)
2439 cs_halo_sync_num(m->halo, CS_HALO_STANDARD, cell_tag);
2440
2441 /* Loop on internal faces "Algo glouton" */
2442
2443 bool new_cells_found = true;
2444
2445 while (new_cells_found) {
2446
2447 cs_gnum_t cpt = 0;
2448
2449 for (cs_lnum_t face_id = 0; face_id < m->n_i_faces; face_id ++) {
2450
2451 cs_lnum_t ii = ifacel[face_id][0];
2452 cs_lnum_t jj = ifacel[face_id][1];
2453
2454 int ti = cell_tag[ii];
2455 int tj = cell_tag[jj];
2456
2457 if (ti == 1 && tj == -1) {
2458 cell_tag[ii] = -1;
2459 cpt ++;
2460 }
2461
2462 if (ti == -1 && tj == 1) {
2463 cell_tag[jj] = -1;
2464 cpt ++;
2465 }
2466 }
2467
2468 if (cs_glob_rank_id >= 0)
2469 cs_parall_counter(&cpt, 1);
2470
2471 if (cpt == 0)
2472 new_cells_found = false;
2473
2474 if (m->halo!=NULL)
2475 cs_halo_sync_num(m->halo, CS_HALO_STANDARD, cell_tag);
2476 }
2477
2478 for (cs_lnum_t cell_id = 0; cell_id < m->n_cells; cell_id ++) {
2479 if (indic != NULL)
2480 indic[cell_id] = cell_tag[cell_id];
2481 if (cell_tag[cell_id] == -1)
2482 porosity[cell_id] = 1.0;
2483 }
2484
2485 BFT_FREE(cell_tag);
2486 BFT_FREE(cell_selected_idx);
2487 BFT_FREE(mean_plane_def);
2488 BFT_FREE(stl_normals);
2489 BFT_FREE(bbox);
2490 BFT_FREE(input_cells);
2491 BFT_FREE(selected_cells);
2492 BFT_FREE(tria_in_cell_lst);
2493 BFT_FREE(tria_in_cell_idx);
2494 }
2495
2496 /*----------------------------------------------------------------------------*/
2497
2498 END_C_DECLS
2499