Cabana 0.8.0-dev
 
Loading...
Searching...
No Matches
Cabana_Grid_BovWriter.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
16#ifndef CABANA_GRID_BOVWRITER_HPP
17#define CABANA_GRID_BOVWRITER_HPP
18
19#include <Cabana_Grid_Array.hpp>
20#include <Cabana_Grid_Halo.hpp>
23#include <Cabana_Grid_Types.hpp>
24
25#include <Kokkos_Core.hpp>
26
27#include <mpi.h>
28
29#include <array>
30#include <fstream>
31#include <iomanip>
32#include <sstream>
33#include <string>
34#include <type_traits>
35
36namespace Cabana
37{
38namespace Grid
39{
40namespace Experimental
41{
42namespace BovWriter
43{
44//---------------------------------------------------------------------------//
45// VisIt Brick-of-Values (BOV) grid field writer.
46//---------------------------------------------------------------------------//
47
50template <typename T>
51struct BovFormat;
52
54template <>
55struct BovFormat<short>
56{
58 static std::string value() { return "SHORT"; }
59};
60
62template <>
63struct BovFormat<int>
64{
66 static std::string value() { return "INT"; }
67};
68
70template <>
71struct BovFormat<float>
72{
74 static std::string value() { return "FLOAT"; }
75};
76
78template <>
79struct BovFormat<double>
80{
82 static std::string value() { return "DOUBLE"; }
83};
84
87template <typename T>
89
91template <>
93{
95 static std::string value() { return "zonal"; }
96};
97
99template <>
101{
103 static std::string value() { return "nodal"; }
104};
105
106//---------------------------------------------------------------------------//
108template <class Array_t, std::size_t N>
109MPI_Datatype createSubarray( const Array_t& array,
110 const std::array<long, N>& owned_extents,
111 const std::array<long, N>& global_extents )
112{
113 using value_type = typename Array_t::value_type;
114 const auto& global_grid = array.layout()->localGrid()->globalGrid();
115
116 std::array<int, N> local_start;
117 std::array<int, N> local_size;
118 std::array<int, N> global_size;
119 for ( std::size_t i = 0; i < N - 1; ++i )
120 {
121 local_start[i] =
122 static_cast<int>( global_grid.globalOffset( N - i - 2 ) );
123 local_size[i] = static_cast<int>( owned_extents[N - i - 2] );
124 global_size[i] = static_cast<int>( global_extents[N - i - 2] );
125 }
126 local_start.back() = 0;
127 local_size.back() = owned_extents.back();
128 global_size.back() = global_extents.back();
129
130 MPI_Datatype subarray;
131 MPI_Type_create_subarray( N, global_size.data(), local_size.data(),
132 local_start.data(), MPI_ORDER_C,
133 MpiTraits<value_type>::type(), &subarray );
134
135 return subarray;
136}
137
138//---------------------------------------------------------------------------//
140template <class TargetView, class SourceView, class Indices, class ExecSpace>
141std::enable_if_t<4 == TargetView::rank, void>
142reorderView( TargetView& target, const SourceView& source,
143 const Indices& index_space, const ExecSpace& exec_space )
144{
145 Kokkos::parallel_for(
146 "bov_reorder", createExecutionPolicy( index_space, exec_space ),
147 KOKKOS_LAMBDA( const int k, const int j, const int i, const int l ) {
148 target( k, j, i, l ) = source( i, j, k, l );
149 } );
150 exec_space.fence();
151}
152
154template <class TargetView, class SourceView, class Indices, class ExecSpace>
155std::enable_if_t<3 == TargetView::rank, void>
156reorderView( TargetView& target, const SourceView& source,
157 const Indices& index_space, const ExecSpace& exec_space )
158{
159 Kokkos::parallel_for(
160 "bov_reorder", createExecutionPolicy( index_space, exec_space ),
161 KOKKOS_LAMBDA( const int j, const int i, const int l ) {
162 target( j, i, l ) = source( i, j, l );
163 } );
164 exec_space.fence();
165}
166
167//---------------------------------------------------------------------------//
181template <class ExecutionSpace, class Array_t>
182void writeTimeStep( ExecutionSpace, const std::string& prefix,
183 const int time_step_index, const double time,
184 const Array_t& array, const bool gather_array = true )
185{
187 "ViSIT BOV writer can only be used with uniform mesh" );
188
189 // Types
190 using entity_type = typename Array_t::entity_type;
191 using value_type = typename Array_t::value_type;
192 using memory_space = typename Array_t::memory_space;
193 const std::size_t num_space_dim = Array_t::num_space_dim;
194
195 // Get the global grid.
196 const auto& global_grid = array.layout()->localGrid()->globalGrid();
197
198 // Get the global mesh.
199 const auto& global_mesh = global_grid.globalMesh();
200
201 // If this is a node field, determine periodicity so we can add the last
202 // node back to the visualization if needed.
203 std::array<long, num_space_dim + 1> global_extents;
204 for ( std::size_t i = 0; i < num_space_dim + 1; ++i )
205 {
206 global_extents[i] = -1;
207 }
208 for ( std::size_t d = 0; d < num_space_dim; ++d )
209 {
210 if ( std::is_same<entity_type, Cell>::value )
211 global_extents[d] = global_grid.globalNumEntity( Cell(), d );
212 else if ( std::is_same<entity_type, Node>::value )
213 global_extents[d] = global_grid.globalNumEntity( Cell(), d ) + 1;
214 }
215 global_extents[num_space_dim] = array.layout()->dofsPerEntity();
216
217 auto owned_index_space = array.layout()->indexSpace( Own(), Local() );
218 std::array<long, num_space_dim + 1> owned_extents;
219 for ( std::size_t i = 0; i < num_space_dim + 1; ++i )
220 {
221 owned_extents[i] = -1;
222 }
223 for ( std::size_t d = 0; d < num_space_dim; ++d )
224 {
225 if ( std::is_same<entity_type, Cell>::value )
226 {
227 owned_extents[d] = owned_index_space.extent( d );
228 }
229 else if ( std::is_same<entity_type, Node>::value )
230 {
231 if ( !global_grid.isPeriodic( d ) ||
232 global_grid.dimBlockId( d ) <
233 global_grid.dimNumBlock( d ) - 1 )
234 owned_extents[d] = owned_index_space.extent( d );
235 else
236 owned_extents[d] = owned_index_space.extent( d ) + 1;
237 }
238 }
239 owned_extents[num_space_dim] = array.layout()->dofsPerEntity();
240
241 // Gather halo data if any dimensions are periodic.
242 if ( gather_array )
243 {
244 for ( std::size_t d = 0; d < num_space_dim; ++d )
245 {
246 if ( global_grid.isPeriodic( d ) )
247 {
248 auto halo =
250 halo->gather( ExecutionSpace(), array );
251 break;
252 }
253 }
254 }
255
256 // Create a contiguous array of the owned array values. Note that we
257 // reorder to KJI grid ordering to conform to the BOV format.
258 std::array<long, num_space_dim + 1> local_space_min;
259 std::array<long, num_space_dim + 1> local_space_max;
260 for ( std::size_t d = 0; d < num_space_dim; ++d )
261 {
262 local_space_min[d] = owned_index_space.min( d );
263 local_space_max[d] = owned_index_space.min( d ) + owned_extents[d];
264 }
265 local_space_min.back() = 0;
266 local_space_max.back() = owned_extents.back();
267 IndexSpace<num_space_dim + 1> local_space( local_space_min,
268 local_space_max );
269 auto owned_subview = createSubview( array.view(), local_space );
270
271 std::array<long, num_space_dim + 1> reorder_space_size;
272 for ( std::size_t d = 0; d < num_space_dim; ++d )
273 {
274 reorder_space_size[d] = owned_extents[num_space_dim - d - 1];
275 }
276 reorder_space_size.back() = owned_extents.back();
277 IndexSpace<num_space_dim + 1> reorder_space( reorder_space_size );
279 array.label(), reorder_space );
280 reorderView( owned_view, owned_subview, reorder_space, ExecutionSpace() );
281
282 // Compose a data file name prefix.
283 std::stringstream file_name;
284 file_name << prefix << "_" << std::setfill( '0' ) << std::setw( 6 )
285 << time_step_index;
286
287 // Open a binary data file.
288 std::string data_file_name = file_name.str() + ".dat";
289 MPI_File data_file;
290 MPI_File_open( global_grid.comm(), data_file_name.c_str(),
291 MPI_MODE_WRONLY | MPI_MODE_CREATE, MPI_INFO_NULL,
292 &data_file );
293
294 // Create the global subarray in which we are writing the local data.
295 auto subarray = createSubarray( array, owned_extents, global_extents );
296 MPI_Type_commit( &subarray );
297
298 // Set the data in the file this process is going to write to.
299 MPI_File_set_view( data_file, 0, MpiTraits<value_type>::type(), subarray,
300 "native", MPI_INFO_NULL );
301
302 // Write the view to binary.
303 MPI_Status status;
304 MPI_File_write_all( data_file, owned_view.data(), owned_view.size(),
305 MpiTraits<value_type>::type(), &status );
306
307 // Clean up.
308 MPI_File_close( &data_file );
309 MPI_Type_free( &subarray );
310
311 // Create a VisIt BOV header with global data. Only create the header
312 // on rank 0.
313 int rank;
314 MPI_Comm_rank( global_grid.comm(), &rank );
315 if ( 0 == rank )
316 {
317 // Open a file for writing.
318 std::string header_file_name = file_name.str() + ".bov";
319 std::fstream header;
320 header.open( header_file_name, std::fstream::out );
321
322 // Write the current time.
323 header << "TIME: " << time << std::endl;
324
325 // Data file name.
326 header << "DATA_FILE: " << data_file_name << std::endl;
327
328 // Global data size.
329 header << "DATA_SIZE: ";
330 for ( std::size_t d = 0; d < num_space_dim; ++d )
331 {
332 header << global_extents[d] << " ";
333 }
334 for ( std::size_t d = num_space_dim; d < 3; ++d )
335 {
336 header << 1;
337 }
338 header << std::endl;
339
340 // Data format.
341 header << "DATA_FORMAT: " << BovFormat<value_type>::value()
342 << std::endl;
343
344 // Variable name.
345 header << "VARIABLE: " << array.label() << std::endl;
346
347 // Endian order
348 header << "DATA_ENDIAN: LITTLE" << std::endl;
349
350 // Data location.
351 header << "CENTERING: " << BovCentering<entity_type>::value()
352 << std::endl;
353
354 // Mesh low corner.
355 header << "BRICK_ORIGIN: ";
356 for ( std::size_t d = 0; d < num_space_dim; ++d )
357 {
358 header << global_mesh.lowCorner( d ) << " ";
359 }
360 for ( std::size_t d = num_space_dim; d < 3; ++d )
361 {
362 header << 0.0;
363 }
364 header << std::endl;
365
366 // Mesh global width
367 header << "BRICK_SIZE: ";
368 for ( std::size_t d = 0; d < num_space_dim; ++d )
369 {
370 header << global_grid.globalNumEntity( Cell(), d ) *
371 global_mesh.cellSize( d )
372 << " ";
373 }
374 for ( std::size_t d = num_space_dim; d < 3; ++d )
375 {
376 header << 0.0;
377 }
378 header << std::endl;
379
380 // Number of data components. Scalar and vector types are
381 // supported.
382 header << "DATA_COMPONENTS: " << global_extents[num_space_dim]
383 << std::endl;
384
385 // Close the header.
386 header.close();
387 }
388}
389
403template <class Array_t>
404void writeTimeStep( const std::string& prefix, const int time_step_index,
405 const double time, const Array_t& array,
406 const bool gather_array = true )
407{
408 using exec_space = typename Array_t::execution_space;
409 writeTimeStep( exec_space{}, prefix, time_step_index, time, array,
410 gather_array );
411}
412
425template <class ExecutionSpace, class Array_t,
426 typename std::enable_if<
427 Kokkos::is_execution_space<ExecutionSpace>::value, int>::type = 0>
428void writeTimeStep( ExecutionSpace, const int time_step_index,
429 const double time, const Array_t& array,
430 const bool gather_array = true )
431{
432 writeTimeStep( ExecutionSpace{}, "grid_" + array.label(), time_step_index,
433 time, array, gather_array );
434}
435
448template <class Array_t>
449void writeTimeStep( const int time_step_index, const double time,
450 const Array_t& array, const bool gather_array = true )
451{
452 using exec_space = typename Array_t::execution_space;
453 writeTimeStep( exec_space{}, "grid_" + array.label(), time_step_index, time,
454 array, gather_array );
455}
456
457//---------------------------------------------------------------------------//
458
459} // end namespace BovWriter
460} // end namespace Experimental
461} // namespace Grid
462} // namespace Cabana
463
464#endif // end CABANA_GRID_BOVWRITER_HPP
Grid field arrays.
MPI_Datatype createSubarray(const Array_t &array, const std::array< long, N > &owned_extents, const std::array< long, N > &global_extents)
Create the MPI subarray for the given array.
Definition Cabana_Grid_BovWriter.hpp:109
void writeTimeStep(ExecutionSpace, const std::string &prefix, const int time_step_index, const double time, const Array_t &array, const bool gather_array=true)
Write a grid array to a VisIt BOV.
Definition Cabana_Grid_BovWriter.hpp:182
std::enable_if_t< 4==TargetView::rank, void > reorderView(TargetView &target, const SourceView &source, const Indices &index_space, const ExecSpace &exec_space)
Reorder a view to the required ordering for I/O.
Definition Cabana_Grid_BovWriter.hpp:142
Multi-node grid scatter/gather.
auto createHalo(const Pattern &pattern, const int width, const ArrayTypes &... arrays)
Halo creation function.
Definition Cabana_Grid_Halo.hpp:949
Logical grid indexing.
KOKKOS_INLINE_FUNCTION auto createSubview(const ViewType &view, const IndexSpace< 1 > &index_space) -> decltype(Kokkos::subview(view, index_space.range(0)))
Given a view create a subview over the given index space.
Definition Cabana_Grid_IndexSpace.hpp:369
Kokkos::RangePolicy< ExecutionSpace > createExecutionPolicy(const IndexSpace< 1 > &index_space, const ExecutionSpace &)
Create a multi-dimensional execution policy over an index space.
Definition Cabana_Grid_IndexSpace.hpp:175
Kokkos::View< Scalar *, Params... > createView(const std::string &label, const IndexSpace< 1 > &index_space)
Given an index space create a view over the extent of that index space.
Definition Cabana_Grid_IndexSpace.hpp:235
Grid type tags.
Structured index space.
Definition Cabana_Grid_IndexSpace.hpp:37
Definition Cabana_Grid_Halo.hpp:76
Core: particle data structures and algorithms.
Definition Cabana_AoSoA.hpp:36
Mesh cell tag.
Definition Cabana_Grid_Types.hpp:49
static std::string value()
Get BOV value type.
Definition Cabana_Grid_BovWriter.hpp:95
static std::string value()
Get BOV value type.
Definition Cabana_Grid_BovWriter.hpp:103
Definition Cabana_Grid_BovWriter.hpp:88
static std::string value()
Get BOV value type.
Definition Cabana_Grid_BovWriter.hpp:82
static std::string value()
Get BOV value type.
Definition Cabana_Grid_BovWriter.hpp:74
static std::string value()
Get BOV value type.
Definition Cabana_Grid_BovWriter.hpp:66
static std::string value()
Get BOV value type.
Definition Cabana_Grid_BovWriter.hpp:58
Definition Cabana_Grid_BovWriter.hpp:51
Local index tag.
Definition Cabana_Grid_Types.hpp:208
Definition Cabana_Grid_MpiTraits.hpp:32
Mesh node tag.
Definition Cabana_Grid_Types.hpp:56
Owned decomposition tag.
Definition Cabana_Grid_Types.hpp:190
Definition Cabana_Grid_Types.hpp:302