Cabana 0.8.0-dev
 
Loading...
Searching...
No Matches
Cabana_SiloParticleOutput.hpp
Go to the documentation of this file.
1/****************************************************************************
2 * Copyright (c) 2018-2023 by the Cabana authors *
3 * All rights reserved. *
4 * *
5 * This file is part of the Cabana library. Cabana is distributed under a *
6 * BSD 3-clause license. For the licensing terms see the LICENSE file in *
7 * the top-level directory. *
8 * *
9 * SPDX-License-Identifier: BSD-3-Clause *
10 ****************************************************************************/
11
12/****************************************************************************
13 * Copyright (c) 2022 by the Picasso authors *
14 * All rights reserved. *
15 * *
16 * This file is part of the Picasso library. Picasso is distributed under a *
17 * BSD 3-clause license. For the licensing terms see the LICENSE file in *
18 * the top-level directory. *
19 * *
20 * SPDX-License-Identifier: BSD-3-Clause *
21 ****************************************************************************/
22
27
28#ifndef CABANA_SILOPARTICLEOUTPUT_HPP
29#define CABANA_SILOPARTICLEOUTPUT_HPP
30
31#include <Kokkos_Core.hpp>
32#include <Kokkos_Profiling_ScopedRegion.hpp>
33
34#include <Cabana_Slice.hpp>
35
36#include <silo.h>
37
38#include <mpi.h>
39
40#include <pmpio.h>
41
42#include <sstream>
43#include <string>
44#include <type_traits>
45#include <vector>
46
47namespace Cabana
48{
49namespace Experimental
50{
51namespace SiloParticleOutput
52{
53//---------------------------------------------------------------------------//
54// Silo Particle Field Output.
55//---------------------------------------------------------------------------//
57// Format traits.
58template <typename T>
59struct SiloTraits;
60
61template <>
62struct SiloTraits<short>
63{
64 static int type() { return DB_SHORT; }
65};
66
67template <>
68struct SiloTraits<int>
69{
70 static int type() { return DB_INT; }
71};
72
73template <>
74struct SiloTraits<float>
75{
76 static int type() { return DB_FLOAT; }
77};
78
79template <>
80struct SiloTraits<double>
81{
82 static int type() { return DB_DOUBLE; }
83};
84
85namespace Impl
86{
87//---------------------------------------------------------------------------//
88// Rank-0 field
89template <class SliceType>
90void writeFields(
91 DBfile* silo_file, const std::string& mesh_name, const std::size_t begin,
92 const std::size_t end, const SliceType& slice,
93
94 typename std::enable_if<
95 2 == SliceType::kokkos_view::traits::dimension::rank, int*>::type = 0 )
96{
97 // Reorder in a contiguous blocked format.
98 Kokkos::View<typename SliceType::value_type*,
99 typename SliceType::device_type>
100 view( Kokkos::ViewAllocateWithoutInitializing( "scalar_field" ),
101 end - begin );
102 copySliceToView( view, slice, begin, end );
103
104 // Mirror the field to the host.
105 auto host_view =
106 Kokkos::create_mirror_view_and_copy( Kokkos::HostSpace(), view );
107
108 // Write the field.
109 DBPutPointvar1( silo_file, slice.label().c_str(), mesh_name.c_str(),
110 host_view.data(), host_view.extent( 0 ),
111 SiloTraits<typename SliceType::value_type>::type(),
112 nullptr );
113}
114
115// Rank-1 field
116template <class SliceType>
117void writeFields(
118 DBfile* silo_file, const std::string& mesh_name, const std::size_t begin,
119 const std::size_t end, const SliceType& slice,
120 typename std::enable_if<
121 3 == SliceType::kokkos_view::traits::dimension::rank, int*>::type = 0 )
122{
123 // Reorder in a contiguous blocked format.
124 Kokkos::View<typename SliceType::value_type**, Kokkos::LayoutLeft,
125 typename SliceType::device_type>
126 view( Kokkos::ViewAllocateWithoutInitializing( "vector_field" ),
127 end - begin, slice.extent( 2 ) );
128 copySliceToView( view, slice, begin, end );
129
130 // Mirror the field to the host.
131 auto host_view =
132 Kokkos::create_mirror_view_and_copy( Kokkos::HostSpace(), view );
133
134 // Get the data pointers per dimension.
135 std::vector<typename SliceType::value_type*> ptrs( host_view.extent( 1 ) );
136 for ( std::size_t d0 = 0; d0 < host_view.extent( 1 ); ++d0 )
137 ptrs[d0] = Kokkos::subview( host_view, Kokkos::ALL(), d0 ).data();
138
139 // Write the field.
140 DBPutPointvar( silo_file, slice.label().c_str(), mesh_name.c_str(),
141 host_view.extent( 1 ), ptrs.data(), host_view.extent( 0 ),
142 SiloTraits<typename SliceType::value_type>::type(),
143 nullptr );
144}
145
146// Rank-2 field
147template <class SliceType>
148void writeFields(
149 DBfile* silo_file, const std::string& mesh_name, const std::size_t begin,
150 const std::size_t end, const SliceType& slice,
151 typename std::enable_if<
152 4 == SliceType::kokkos_view::traits::dimension::rank, int*>::type = 0 )
153{
154 // Reorder in a contiguous blocked format.
155 Kokkos::View<typename SliceType::value_type***, Kokkos::LayoutLeft,
156 typename SliceType::device_type>
157 view( Kokkos::ViewAllocateWithoutInitializing( "matrix_field" ),
158 end - begin, slice.extent( 2 ), slice.extent( 3 ) );
159 copySliceToView( view, slice, begin, end );
160
161 // Mirror the field to the host.
162 auto host_view =
163 Kokkos::create_mirror_view_and_copy( Kokkos::HostSpace(), view );
164
165 // Get the data pointers per dimension.
166 std::vector<typename SliceType::value_type*> ptrs;
167 ptrs.reserve( host_view.extent( 1 ) * host_view.extent( 2 ) );
168 for ( unsigned d0 = 0; d0 < host_view.extent( 1 ); ++d0 )
169 for ( unsigned d1 = 0; d1 < host_view.extent( 2 ); ++d1 )
170 ptrs.push_back(
171 Kokkos::subview( host_view, Kokkos::ALL(), d0, d1 ).data() );
172
173 // Write the field.
174 DBPutPointvar( silo_file, slice.label().c_str(), mesh_name.c_str(),
175 host_view.extent( 1 ) * host_view.extent( 2 ), ptrs.data(),
176 host_view.extent( 0 ),
177 SiloTraits<typename SliceType::value_type>::type(),
178 nullptr );
179}
180
181// Output full slice range for any rank field.
182template <class SliceType>
183void writeFields( DBfile* silo_file, const std::string& mesh_name,
184 const SliceType& slice )
185{
186 writeFields( silo_file, mesh_name, slice, 0, slice.size() );
187}
188
190} // namespace Impl
191
193template <class SliceType>
194void writeFields( DBfile* silo_file, const std::string& mesh_name,
195 const std::size_t begin, const std::size_t end,
196 const SliceType& slice )
197{
198 Impl::writeFields( silo_file, mesh_name, begin, end, slice );
199}
200
202template <class SliceType, class... FieldSliceTypes>
203void writeFields( DBfile* silo_file, const std::string& mesh_name,
204 const std::size_t begin, const std::size_t end,
205 const SliceType& slice, FieldSliceTypes&&... fields )
206{
207 Impl::writeFields( silo_file, mesh_name, begin, end, slice );
208 writeFields( silo_file, mesh_name, begin, end, fields... );
209}
210
211//---------------------------------------------------------------------------//
213inline void* createFile( const char* file_name, const char* dir_name,
214 void* user_data )
215{
216 std::ignore = user_data;
217 DBfile* silo_file =
218 DBCreate( file_name, DB_CLOBBER, DB_LOCAL, nullptr, DB_PDB );
219 if ( silo_file )
220 {
221 DBMkDir( silo_file, dir_name );
222 DBSetDir( silo_file, dir_name );
223 }
224
225 return (void*)silo_file;
226}
227
229inline void* openFile( const char* file_name, const char* dir_name,
230 PMPIO_iomode_t io_mode, void* user_data )
231{
232 std::ignore = io_mode;
233 std::ignore = user_data;
234 DBfile* silo_file = DBOpen( file_name, DB_PDB, DB_APPEND );
235 if ( silo_file )
236 {
237 DBMkDir( silo_file, dir_name );
238 DBSetDir( silo_file, dir_name );
239 }
240 return (void*)silo_file;
241}
242
244inline void closeFile( void* file, void* user_data )
245{
246 std::ignore = user_data;
247 DBfile* silo_file = (DBfile*)file;
248 if ( silo_file )
249 DBClose( silo_file );
250}
251
252namespace Impl
253{
255//---------------------------------------------------------------------------//
256// Get field names.
257template <class SliceType>
258void getFieldNames( std::vector<std::string>& names, const SliceType& slice )
259{
260 names.push_back( slice.label() );
261}
262
263// Get field names.
264template <class SliceType, class... FieldSliceTypes>
265void getFieldNames( std::vector<std::string>& names, const SliceType& slice,
266 FieldSliceTypes&&... fields )
267{
268 getFieldNames( names, slice );
269 getFieldNames( names, fields... );
270}
272} // namespace Impl
273
275template <class... FieldSliceTypes>
276std::vector<std::string> getFieldNames( FieldSliceTypes&&... fields )
277{
278 std::vector<std::string> names;
279 Impl::getFieldNames( names, fields... );
280 return names;
281}
282
283//---------------------------------------------------------------------------//
285template <class... FieldSliceTypes>
286void writeMultiMesh( PMPIO_baton_t* baton, DBfile* silo_file,
287 const int comm_size, const std::string& prefix,
288 const std::string& mesh_name, const int time_step_index,
289 const double time, FieldSliceTypes&&... fields )
290{
291 // Go to the root directory of the file.
292 DBSetDir( silo_file, "/" );
293
294 // Create the mesh block names.
295 std::vector<std::string> mb_names;
296 for ( int r = 0; r < comm_size; ++r )
297 {
298 int group_rank = PMPIO_GroupRank( baton, r );
299 if ( 0 == group_rank )
300 {
301 std::stringstream bname;
302 bname << "rank_" << r << "/" << mesh_name;
303 mb_names.push_back( bname.str() );
304 }
305 else
306 {
307 std::stringstream bname;
308 bname << prefix << "_" << time_step_index << "_group_" << group_rank
309 << ".silo:/rank_" << r << "/" << mesh_name;
310 mb_names.push_back( bname.str() );
311 }
312 }
313 char** mesh_block_names = (char**)malloc( comm_size * sizeof( char* ) );
314 for ( int r = 0; r < comm_size; ++r )
315 mesh_block_names[r] = const_cast<char*>( mb_names[r].c_str() );
316
317 std::vector<int> mesh_block_types( comm_size, DB_POINTMESH );
318
319 // Get the names of the fields.
320 std::vector<std::string> field_names = getFieldNames( fields... );
321
322 // Create the field block names.
323 int num_field = field_names.size();
324 std::vector<std::vector<std::string>> fb_names( num_field );
325 for ( int f = 0; f < num_field; ++f )
326 {
327 for ( int r = 0; r < comm_size; ++r )
328 {
329 int group_rank = PMPIO_GroupRank( baton, r );
330 if ( 0 == group_rank )
331 {
332 std::stringstream bname;
333 bname << "rank_" << r << "/" << field_names[f];
334 fb_names[f].push_back( bname.str() );
335 }
336 else
337 {
338 std::stringstream bname;
339 bname << prefix << "_" << time_step_index << "_group_"
340 << group_rank << ".silo:/rank_" << r << "/"
341 << field_names[f];
342 fb_names[f].push_back( bname.str() );
343 }
344 }
345 }
346
347 std::vector<char**> field_block_names( num_field );
348 for ( int f = 0; f < num_field; ++f )
349 {
350 field_block_names[f] = (char**)malloc( comm_size * sizeof( char* ) );
351 for ( int r = 0; r < comm_size; ++r )
352 field_block_names[f][r] =
353 const_cast<char*>( fb_names[f][r].c_str() );
354 }
355
356 std::vector<int> field_block_types( comm_size, DB_POINTVAR );
357
358 // Create options.
359 DBoptlist* options = DBMakeOptlist( 1 );
360 DBAddOption( options, DBOPT_DTIME, (void*)&time );
361 DBAddOption( options, DBOPT_CYCLE, (void*)&time_step_index );
362
363 // Add the multiblock mesh.
364 std::stringstream mbname;
365 mbname << "multi_" << mesh_name;
366 DBPutMultimesh( silo_file, mbname.str().c_str(), comm_size,
367 mesh_block_names, mesh_block_types.data(), options );
368
369 // Add the multiblock fields.
370 for ( int f = 0; f < num_field; ++f )
371 {
372 std::stringstream mfname;
373 mfname << "multi_" << field_names[f];
374 DBPutMultivar( silo_file, mfname.str().c_str(), comm_size,
375 field_block_names[f], field_block_types.data(),
376 options );
377 }
378
379 // Cleanup.
380 free( mesh_block_names );
381 for ( auto& f_name : field_block_names )
382 free( f_name );
383 DBFreeOptlist( options );
384}
385
386//---------------------------------------------------------------------------//
399template <class CoordSliceType, class... FieldSliceTypes>
400void writePartialRangeTimeStep( const std::string& prefix, MPI_Comm comm,
401 const int num_group, const int time_step_index,
402 const double time, const std::size_t begin,
403 const std::size_t end,
404 const CoordSliceType& coords,
405 FieldSliceTypes&&... fields )
406{
407 Kokkos::Profiling::ScopedRegion region( "Cabana::SiloParticleOutput" );
408
409 // Create the parallel baton.
410 int mpi_tag = 1948;
411 PMPIO_baton_t* baton =
412 PMPIO_Init( num_group, PMPIO_WRITE, comm, mpi_tag, createFile, openFile,
413 closeFile, nullptr );
414
415 // Allow empty.
416 DBSetAllowEmptyObjects( 1 );
417
418 // Compose a data file name.
419 int comm_rank;
420 MPI_Comm_rank( comm, &comm_rank );
421 int group_rank = PMPIO_GroupRank( baton, comm_rank );
422 std::stringstream file_name;
423
424 // Group 0 writes a master file for the time step.
425 if ( 0 == group_rank )
426 file_name << prefix << "_" << time_step_index << ".silo";
427 // The other groups write auxiliary files.
428 else
429 file_name << prefix << "_" << time_step_index << "_group_" << group_rank
430 << ".silo";
431
432 // Compose a directory name.
433 std::stringstream dir_name;
434 dir_name << "rank_" << comm_rank;
435
436 // Wait for our turn to write to the file.
437 DBfile* silo_file = (DBfile*)PMPIO_WaitForBaton(
438 baton, file_name.str().c_str(), dir_name.str().c_str() );
439
440 // Reorder the coordinates in a blocked format.
441 Kokkos::View<typename CoordSliceType::value_type**, Kokkos::LayoutLeft,
442 typename CoordSliceType::device_type>
443 view( Kokkos::ViewAllocateWithoutInitializing( "coords" ), end - begin,
444 coords.extent( 2 ) );
445 copySliceToView( view, coords, begin, end );
446
447 // Mirror the coordinates to the host.
448 auto host_coords =
449 Kokkos::create_mirror_view_and_copy( Kokkos::HostSpace(), view );
450
451 // Add the point mesh.
452 std::string mesh_name = prefix;
453 auto host_x = Kokkos::subview( host_coords, Kokkos::ALL(), 0 );
454 auto host_y = Kokkos::subview( host_coords, Kokkos::ALL(), 1 );
455 auto host_z = Kokkos::subview( host_coords, Kokkos::ALL(), 2 );
456 typename CoordSliceType::value_type* ptrs[3] = {
457 host_x.data(),
458 host_y.data(),
459 host_z.data(),
460 };
461 DBPutPointmesh( silo_file, mesh_name.c_str(), host_coords.extent( 1 ), ptrs,
462 host_coords.extent( 0 ),
463 SiloTraits<typename CoordSliceType::value_type>::type(),
464 nullptr );
465
466 // Add variables.
467 writeFields( silo_file, mesh_name, begin, end, fields... );
468
469 // Root rank writes the global multimesh hierarchy for parallel
470 // simulations.
471 int comm_size;
472 MPI_Comm_size( comm, &comm_size );
473 if ( 0 == comm_rank && comm_size > 1 )
474 writeMultiMesh( baton, silo_file, comm_size, prefix, mesh_name,
475 time_step_index, time, fields... );
476
477 // Hand off the baton.
478 PMPIO_HandOffBaton( baton, silo_file );
479
480 // Finish.
481 PMPIO_Finish( baton );
482}
483
494template <class CoordSliceType, class... FieldSliceTypes>
495void writeTimeStep( const std::string& prefix, MPI_Comm comm,
496 const int num_group, const int time_step_index,
497 const double time, const CoordSliceType& coords,
498 FieldSliceTypes&&... fields )
499{
500 writePartialRangeTimeStep( prefix, comm, num_group, time_step_index, time,
501 0, coords.size(), coords, fields... );
502}
503
504//---------------------------------------------------------------------------//
505
506} // namespace SiloParticleOutput
507} // namespace Experimental
508} // end namespace Cabana
509
510#endif // CABANA_SILOPARTICLEOUTPUT_HPP
void writeFields(HDF5Config, hid_t, std::size_t, std::size_t, hsize_t, int, const char *, const char *)
Write particle data to HDF5 output. Empty overload if only writing coords.
Definition Cabana_HDF5ParticleOutput.hpp:498
void writeTimeStep(HDF5Config h5_config, const std::string &prefix, MPI_Comm comm, const int time_step_index, const double time, const std::size_t n_local, const CoordSliceType &coords_slice, FieldSliceTypes &&... fields)
Write particle output in HDF5 format.
Definition Cabana_HDF5ParticleOutput.hpp:540
void closeFile(void *file, void *user_data)
Close Silo output file.
Definition Cabana_SiloParticleOutput.hpp:244
void * openFile(const char *file_name, const char *dir_name, PMPIO_iomode_t io_mode, void *user_data)
Open Silo output file.
Definition Cabana_SiloParticleOutput.hpp:229
std::vector< std::string > getFieldNames(FieldSliceTypes &&... fields)
Get Silo output property field names.
Definition Cabana_SiloParticleOutput.hpp:276
void writeMultiMesh(PMPIO_baton_t *baton, DBfile *silo_file, const int comm_size, const std::string &prefix, const std::string &mesh_name, const int time_step_index, const double time, FieldSliceTypes &&... fields)
Write a Silo multimesh hierarchy.
Definition Cabana_SiloParticleOutput.hpp:286
void * createFile(const char *file_name, const char *dir_name, void *user_data)
Create Silo output file.
Definition Cabana_SiloParticleOutput.hpp:213
void writePartialRangeTimeStep(const std::string &prefix, MPI_Comm comm, const int num_group, const int time_step_index, const double time, const std::size_t begin, const std::size_t end, const CoordSliceType &coords, FieldSliceTypes &&... fields)
Write particle output in Silo format.
Definition Cabana_SiloParticleOutput.hpp:400
Slice a single particle property from an AoSoA.
Core: particle data structures and algorithms.
Definition Cabana_AoSoA.hpp:36
void copySliceToView(ExecutionSpace exec_space, ViewType &view, const SliceType &slice, const std::size_t begin, const std::size_t end, typename std::enable_if< 2==SliceType::kokkos_view::traits::dimension::rank, int * >::type=0)
Copy from slice to View. Rank-0.
Definition Cabana_Slice.hpp:870
AoSoA_t::template member_slice_type< M > slice(const AoSoA_t &aosoa, const std::string &slice_label="")
Create a slice from an AoSoA.
Definition Cabana_AoSoA.hpp:77