1
- use std:: path:: Path ;
2
-
3
- use splashsurf_lib:: mesh:: PointCloud3d ;
1
+ use splashsurf_lib:: generic_tree:: VisitableTree ;
4
2
use splashsurf_lib:: nalgebra:: Vector3 ;
5
3
use splashsurf_lib:: octree:: Octree ;
6
4
use splashsurf_lib:: { grid_for_reconstruction, Index , Real , SubdivisionCriterion , UniformGrid } ;
7
-
8
- use vtkio:: model:: UnstructuredGridPiece ;
5
+ use std:: path:: Path ;
9
6
10
7
use super :: io;
11
- use splashsurf_lib:: generic_tree:: VisitableTree ;
12
8
9
+ /*
13
10
#[allow(dead_code)]
14
11
fn particles_to_file<P: AsRef<Path>, R: Real>(particles: Vec<Vector3<R>>, path: P) {
15
12
let points = PointCloud3d { points: particles };
@@ -20,6 +17,7 @@ fn particles_to_file<P: AsRef<Path>, R: Real>(particles: Vec<Vector3<R>>, path:
20
17
)
21
18
.unwrap();
22
19
}
20
+ */
23
21
24
22
#[ allow( dead_code) ]
25
23
fn octree_to_file < P : AsRef < Path > , I : Index , R : Real > (
@@ -28,7 +26,7 @@ fn octree_to_file<P: AsRef<Path>, I: Index, R: Real>(
28
26
path : P ,
29
27
) {
30
28
let mesh = octree. hexmesh ( & grid, false ) ;
31
- io:: vtk:: write_vtk ( UnstructuredGridPiece :: from ( & mesh) , path. as_ref ( ) , "octree" ) . unwrap ( ) ;
29
+ io:: vtk:: write_vtk ( mesh. to_dataset ( ) , path. as_ref ( ) , "octree" ) . unwrap ( ) ;
32
30
}
33
31
34
32
#[ test]
@@ -139,7 +137,7 @@ fn build_octree_from_vtk() {
139
137
particle_count += node_particles. len ( ) ;
140
138
141
139
// Ensure that all particles are within extents of octree cell
142
- let aabb = node. aabb ( & grid ) ;
140
+ let aabb = node. aabb ( ) ;
143
141
for idx in node_particles. iter ( ) . copied ( ) {
144
142
let particle = particles[ idx] ;
145
143
assert ! ( aabb. contains_point( & particle) ) ;
@@ -152,45 +150,121 @@ fn build_octree_from_vtk() {
152
150
//octree_to_file(&octree, &grid, "U:\\double_dam_break_frame_26_4732_particles_octree.vtk");
153
151
}
154
152
155
- #[ test]
156
- fn build_octree_par_consistency ( ) {
157
- let file = "../data/double_dam_break_frame_26_4732_particles.vtk" ;
158
- let particles = io:: vtk:: particles_from_vtk :: < f64 , _ > ( file) . unwrap ( ) ;
159
- //println!("Loaded {} particles from {}", particles.len(), file);
153
+ struct TestParameters < R : Real > {
154
+ particle_radius : R ,
155
+ compact_support_radius : R ,
156
+ cube_size : R ,
157
+ margin : Option < R > ,
158
+ max_particles_per_cell : Option < usize > ,
159
+ compare_seq_par : bool ,
160
+ }
160
161
161
- let grid = grid_for_reconstruction :: < i64 , _ > (
162
- particles. as_slice ( ) ,
163
- 0.025 ,
164
- 4.0 * 0.025 ,
165
- 0.2 ,
166
- None ,
167
- true ,
168
- )
169
- . unwrap ( ) ;
162
+ impl < R : Real > Default for TestParameters < R > {
163
+ fn default ( ) -> Self {
164
+ let particle_radius = R :: from_f64 ( 0.025 ) . unwrap ( ) ;
165
+ let compact_support_radius = particle_radius. times_f64 ( 4.0 ) ;
166
+ let cube_size = particle_radius. times_f64 ( 0.5 ) ;
167
+
168
+ Self {
169
+ particle_radius,
170
+ compact_support_radius,
171
+ cube_size,
172
+ margin : None ,
173
+ max_particles_per_cell : None ,
174
+ compare_seq_par : true ,
175
+ }
176
+ }
177
+ }
170
178
171
- let mut octree_seq = Octree :: new ( & grid, particles. as_slice ( ) . len ( ) ) ;
172
- octree_seq. subdivide_recursively_margin (
173
- & grid,
174
- particles. as_slice ( ) ,
175
- SubdivisionCriterion :: MaxParticleCount ( 20 ) ,
176
- 0.0 ,
177
- false ,
178
- ) ;
179
+ impl < R : Real > TestParameters < R > {
180
+ fn new ( particle_radius : f64 , compact_support_factor : f64 , cube_size_factor : f64 ) -> Self {
181
+ let params = Self :: default ( ) ;
182
+ params. with_parameters ( particle_radius, compact_support_factor, cube_size_factor)
183
+ }
179
184
180
- let mut octree_par = Octree :: new ( & grid, particles. as_slice ( ) . len ( ) ) ;
181
- octree_par. subdivide_recursively_margin_par (
182
- & grid,
183
- particles. as_slice ( ) ,
184
- SubdivisionCriterion :: MaxParticleCount ( 20 ) ,
185
- 0.0 ,
186
- false ,
185
+ fn with_margin ( mut self , margin : Option < f64 > ) -> Self {
186
+ self . margin = margin. map ( |m| self . particle_radius . times_f64 ( m) ) ;
187
+ self
188
+ }
189
+ fn with_max_particles_per_cell ( mut self , max_particles_per_cell : Option < usize > ) -> Self {
190
+ self . max_particles_per_cell = max_particles_per_cell;
191
+ self
192
+ }
193
+
194
+ fn with_compare_seq_par ( mut self , compare_seq_par : bool ) -> Self {
195
+ self . compare_seq_par = compare_seq_par;
196
+ self
197
+ }
198
+
199
+ fn with_parameters (
200
+ mut self ,
201
+ particle_radius : f64 ,
202
+ compact_support_factor : f64 ,
203
+ cube_size_factor : f64 ,
204
+ ) -> Self {
205
+ self . particle_radius = R :: from_f64 ( particle_radius) . unwrap ( ) ;
206
+ self . compact_support_radius = self . particle_radius . times_f64 ( compact_support_factor) ;
207
+ self . cube_size = self . particle_radius . times_f64 ( cube_size_factor) ;
208
+ self
209
+ }
210
+
211
+ fn build_grid < I : Index > ( & self , particle_positions : & [ Vector3 < R > ] ) -> UniformGrid < I , R > {
212
+ grid_for_reconstruction (
213
+ particle_positions,
214
+ self . particle_radius ,
215
+ self . compact_support_radius ,
216
+ self . cube_size ,
217
+ None ,
218
+ true ,
219
+ )
220
+ . unwrap ( )
221
+ }
222
+ }
223
+
224
+ /// Returns a vector containing per particle how often it is a non-ghost particle in the octree
225
+ fn count_non_ghost_particles < I : Index , R : Real > (
226
+ particle_positions : & [ Vector3 < R > ] ,
227
+ octree : & Octree < I , R > ,
228
+ ) -> Vec < usize > {
229
+ let mut non_ghost_particles = vec ! [ 0 ; particle_positions. len( ) ] ;
230
+ for node in octree. root ( ) . dfs_iter ( ) {
231
+ if let Some ( particle_set) = node. data ( ) . particle_set ( ) {
232
+ for idx in particle_set. particles . iter ( ) . copied ( ) {
233
+ if node. aabb ( ) . contains_point ( & particle_positions[ idx] ) {
234
+ non_ghost_particles[ idx] += 1 ;
235
+ }
236
+ }
237
+ }
238
+ }
239
+
240
+ non_ghost_particles
241
+ }
242
+
243
+ /// Asserts whether each particle has a unique octree node where it is not a ghost particle
244
+ fn assert_unique_node_per_particle < I : Index , R : Real > (
245
+ particle_positions : & [ Vector3 < R > ] ,
246
+ octree : & Octree < I , R > ,
247
+ ) {
248
+ let non_ghost_particles_counts = count_non_ghost_particles ( particle_positions, octree) ;
249
+ let no_unique_node_particles: Vec < _ > = non_ghost_particles_counts
250
+ . into_iter ( )
251
+ . enumerate ( )
252
+ . filter ( |& ( _, count) | count != 1 )
253
+ . collect ( ) ;
254
+
255
+ assert_eq ! (
256
+ no_unique_node_particles,
257
+ vec![ ] ,
258
+ "There are nodes that don't have a unique octree node where they are not ghost particles!"
187
259
) ;
260
+ }
188
261
189
- let mut particle_count = 0 ;
190
- for ( node_seq, node_par) in octree_seq
262
+ /// Asserts whether both trees have equivalent particle sets in the same octree nodes
263
+ fn assert_tree_equivalence < I : Index , R : Real > ( left_tree : & Octree < I , R > , right_tree : & Octree < I , R > ) {
264
+ for ( node_seq, node_par) in left_tree
191
265
. root ( )
192
266
. dfs_iter ( )
193
- . zip ( octree_par . root ( ) . dfs_iter ( ) )
267
+ . zip ( right_tree . root ( ) . dfs_iter ( ) )
194
268
{
195
269
match (
196
270
node_seq. data ( ) . particle_set ( ) ,
@@ -201,8 +275,7 @@ fn build_octree_par_consistency() {
201
275
let particles_par = & particles_par. particles ;
202
276
203
277
// Ensure that we have the same number of particles for sequential and parallel result
204
- assert_eq ! ( particles_seq. len( ) , particles_par. len( ) ) ;
205
- particle_count += particles_seq. len ( ) ;
278
+ assert_eq ! ( particles_seq. len( ) , particles_par. len( ) , "Found corresponding leaves in the trees that don't have the same number of particles!" ) ;
206
279
207
280
// Sort the particle lists
208
281
let mut particles_seq = particles_seq. to_vec ( ) ;
@@ -211,19 +284,126 @@ fn build_octree_par_consistency() {
211
284
particles_par. sort_unstable ( ) ;
212
285
213
286
// Ensure that the particle lists are identical
214
- assert_eq ! ( particles_seq, particles_par) ;
287
+ assert_eq ! ( particles_seq, particles_par, "Found corresponding leaves in the trees that don't have the same sorted particle lists!" ) ;
215
288
}
216
289
( None , None ) => {
217
290
// Both nodes are leaves
218
291
continue ;
219
292
}
220
293
_ => {
221
- // Parallel and sequential node do not match (one is leaf, one has children)
222
- assert ! ( false ) ;
294
+ // Parallel and sequential nodes do not match (one is leaf, one has children)
295
+ panic ! ( "Encountered a node where one octree has a particle set but the other does not!" ) ;
223
296
}
224
297
}
225
298
}
299
+ }
226
300
227
- // Check if all particles were visited once
228
- assert_eq ! ( particle_count, particles. len( ) ) ;
301
+ fn build_octree_par_consistency < I : Index , R : Real , P : AsRef < Path > > (
302
+ file : P ,
303
+ parameters : TestParameters < R > ,
304
+ ) {
305
+ let particles = io:: vtk:: particles_from_vtk :: < R , _ > ( file) . unwrap ( ) ;
306
+
307
+ let grid = parameters. build_grid :: < I > ( particles. as_slice ( ) ) ;
308
+
309
+ let octree_seq = if parameters. compare_seq_par {
310
+ let mut octree_seq = Octree :: new ( & grid, particles. as_slice ( ) . len ( ) ) ;
311
+ octree_seq. subdivide_recursively_margin (
312
+ & grid,
313
+ particles. as_slice ( ) ,
314
+ parameters
315
+ . max_particles_per_cell
316
+ . map ( |n| SubdivisionCriterion :: MaxParticleCount ( n) )
317
+ . unwrap_or ( SubdivisionCriterion :: MaxParticleCountAuto ) ,
318
+ parameters. margin . unwrap_or ( R :: zero ( ) ) ,
319
+ false ,
320
+ ) ;
321
+
322
+ assert_unique_node_per_particle ( particles. as_slice ( ) , & octree_seq) ;
323
+
324
+ Some ( octree_seq)
325
+ } else {
326
+ None
327
+ } ;
328
+
329
+ let mut octree_par = Octree :: new ( & grid, particles. as_slice ( ) . len ( ) ) ;
330
+ octree_par. subdivide_recursively_margin_par (
331
+ & grid,
332
+ particles. as_slice ( ) ,
333
+ parameters
334
+ . max_particles_per_cell
335
+ . map ( |n| SubdivisionCriterion :: MaxParticleCount ( n) )
336
+ . unwrap_or ( SubdivisionCriterion :: MaxParticleCountAuto ) ,
337
+ parameters. margin . unwrap_or ( R :: zero ( ) ) ,
338
+ false ,
339
+ ) ;
340
+
341
+ assert_unique_node_per_particle ( particles. as_slice ( ) , & octree_par) ;
342
+
343
+ if let Some ( octree_seq) = octree_seq {
344
+ assert_tree_equivalence ( & octree_seq, & octree_par)
345
+ }
346
+ }
347
+
348
+ #[ test]
349
+ fn build_octree_cube ( ) {
350
+ build_octree_par_consistency :: < i64 , f64 , _ > (
351
+ "../data/cube_2366_particles.vtk" ,
352
+ TestParameters :: default ( )
353
+ . with_margin ( Some ( 1.0 ) )
354
+ . with_max_particles_per_cell ( Some ( 70 ) ) ,
355
+ ) ;
356
+ }
357
+
358
+ #[ test]
359
+ fn build_octree_double_dam_break ( ) {
360
+ build_octree_par_consistency :: < i64 , f64 , _ > (
361
+ "../data/double_dam_break_frame_26_4732_particles.vtk" ,
362
+ TestParameters :: default ( )
363
+ . with_margin ( Some ( 1.0 ) )
364
+ . with_max_particles_per_cell ( Some ( 200 ) ) ,
365
+ ) ;
366
+ }
367
+
368
+ #[ test]
369
+ fn build_octree_dam_break ( ) {
370
+ build_octree_par_consistency :: < i64 , f64 , _ > (
371
+ "../data/dam_break_frame_23_24389_particles.vtk" ,
372
+ TestParameters :: default ( )
373
+ . with_margin ( Some ( 1.0 ) )
374
+ . with_max_particles_per_cell ( Some ( 1000 ) ) ,
375
+ ) ;
376
+ }
377
+
378
+ #[ test]
379
+ fn build_octree_bunny ( ) {
380
+ build_octree_par_consistency :: < i64 , f64 , _ > (
381
+ "../data/bunny_frame_14_7705_particles.vtk" ,
382
+ TestParameters :: default ( )
383
+ . with_margin ( Some ( 1.0 ) )
384
+ . with_max_particles_per_cell ( Some ( 200 ) ) ,
385
+ ) ;
386
+ }
387
+
388
+ #[ test]
389
+ fn build_octree_hilbert ( ) {
390
+ build_octree_par_consistency :: < i64 , f64 , _ > (
391
+ "../data/hilbert_46843_particles.vtk" ,
392
+ TestParameters :: default ( )
393
+ . with_margin ( Some ( 1.0 ) )
394
+ . with_max_particles_per_cell ( Some ( 1000 ) ) ,
395
+ ) ;
396
+ }
397
+
398
+ /*
399
+ #[test]
400
+ fn build_octree_canyon() {
401
+ build_octree_par_consistency::<i64, f64, _>(
402
+ "../../canyon_13353401_particles.vtk",
403
+ TestParameters::new(0.011, 4.0, 1.5)
404
+ .with_margin(Some(1.0))
405
+ .with_max_particles_per_cell(Some(52161))
406
+ .with_compare_seq_par(false),
407
+ );
229
408
}
409
+ */
0 commit comments