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 "Cabana::Grid::BovWriter::Reorder",
147 createExecutionPolicy( index_space, exec_space ),
148 KOKKOS_LAMBDA( const int k, const int j, const int i, const int l ) {
149 target( k, j, i, l ) = source( i, j, k, l );
150 } );
151 exec_space.fence();
152}
153
155template <class TargetView, class SourceView, class Indices, class ExecSpace>
156std::enable_if_t<3 == TargetView::rank, void>
157reorderView( TargetView& target, const SourceView& source,
158 const Indices& index_space, const ExecSpace& exec_space )
159{
160 Kokkos::parallel_for(
161 "Cabana::Grid::BovWriter::Reorder",
162 createExecutionPolicy( index_space, exec_space ),
163 KOKKOS_LAMBDA( const int j, const int i, const int l ) {
164 target( j, i, l ) = source( i, j, l );
165 } );
166 exec_space.fence();
167}
168
169//---------------------------------------------------------------------------//
183template <class ExecutionSpace, class Array_t>
184void writeTimeStep( ExecutionSpace, const std::string& prefix,
185 const int time_step_index, const double time,
186 const Array_t& array, const bool gather_array = true )
187{
189 "ViSIT BOV writer can only be used with uniform mesh" );
190
191 // Types
192 using entity_type = typename Array_t::entity_type;
193 using value_type = typename Array_t::value_type;
194 using memory_space = typename Array_t::memory_space;
195 const std::size_t num_space_dim = Array_t::num_space_dim;
196
197 // Get the global grid.
198 const auto& global_grid = array.layout()->localGrid()->globalGrid();
199
200 // Get the global mesh.
201 const auto& global_mesh = global_grid.globalMesh();
202
203 // If this is a node field, determine periodicity so we can add the last
204 // node back to the visualization if needed.
205 std::array<long, num_space_dim + 1> global_extents;
206 for ( std::size_t i = 0; i < num_space_dim + 1; ++i )
207 {
208 global_extents[i] = -1;
209 }
210 for ( std::size_t d = 0; d < num_space_dim; ++d )
211 {
212 if ( std::is_same<entity_type, Cell>::value )
213 global_extents[d] = global_grid.globalNumEntity( Cell(), d );
214 else if ( std::is_same<entity_type, Node>::value )
215 global_extents[d] = global_grid.globalNumEntity( Cell(), d ) + 1;
216 }
217 global_extents[num_space_dim] = array.layout()->dofsPerEntity();
218
219 auto owned_index_space = array.layout()->indexSpace( Own(), Local() );
220 std::array<long, num_space_dim + 1> owned_extents;
221 for ( std::size_t i = 0; i < num_space_dim + 1; ++i )
222 {
223 owned_extents[i] = -1;
224 }
225 for ( std::size_t d = 0; d < num_space_dim; ++d )
226 {
227 if ( std::is_same<entity_type, Cell>::value )
228 {
229 owned_extents[d] = owned_index_space.extent( d );
230 }
231 else if ( std::is_same<entity_type, Node>::value )
232 {
233 if ( !global_grid.isPeriodic( d ) ||
234 global_grid.dimBlockId( d ) <
235 global_grid.dimNumBlock( d ) - 1 )
236 owned_extents[d] = owned_index_space.extent( d );
237 else
238 owned_extents[d] = owned_index_space.extent( d ) + 1;
239 }
240 }
241 owned_extents[num_space_dim] = array.layout()->dofsPerEntity();
242
243 // Gather halo data if any dimensions are periodic.
244 if ( gather_array )
245 {
246 for ( std::size_t d = 0; d < num_space_dim; ++d )
247 {
248 if ( global_grid.isPeriodic( d ) )
249 {
250 auto halo =
252 halo->gather( ExecutionSpace(), array );
253 break;
254 }
255 }
256 }
257
258 // Create a contiguous array of the owned array values. Note that we
259 // reorder to KJI grid ordering to conform to the BOV format.
260 std::array<long, num_space_dim + 1> local_space_min;
261 std::array<long, num_space_dim + 1> local_space_max;
262 for ( std::size_t d = 0; d < num_space_dim; ++d )
263 {
264 local_space_min[d] = owned_index_space.min( d );
265 local_space_max[d] = owned_index_space.min( d ) + owned_extents[d];
266 }
267 local_space_min.back() = 0;
268 local_space_max.back() = owned_extents.back();
269 IndexSpace<num_space_dim + 1> local_space( local_space_min,
270 local_space_max );
271 auto owned_subview = createSubview( array.view(), local_space );
272
273 std::array<long, num_space_dim + 1> reorder_space_size;
274 for ( std::size_t d = 0; d < num_space_dim; ++d )
275 {
276 reorder_space_size[d] = owned_extents[num_space_dim - d - 1];
277 }
278 reorder_space_size.back() = owned_extents.back();
279 IndexSpace<num_space_dim + 1> reorder_space( reorder_space_size );
281 array.label(), reorder_space );
282 reorderView( owned_view, owned_subview, reorder_space, ExecutionSpace() );
283
284 // Compose a data file name prefix.
285 std::stringstream file_name;
286 file_name << prefix << "_" << std::setfill( '0' ) << std::setw( 6 )
287 << time_step_index;
288
289 // Open a binary data file.
290 std::string data_file_name = file_name.str() + ".dat";
291 MPI_File data_file;
292 MPI_File_open( global_grid.comm(), data_file_name.c_str(),
293 MPI_MODE_WRONLY | MPI_MODE_CREATE, MPI_INFO_NULL,
294 &data_file );
295
296 // Create the global subarray in which we are writing the local data.
297 auto subarray = createSubarray( array, owned_extents, global_extents );
298 MPI_Type_commit( &subarray );
299
300 // Set the data in the file this process is going to write to.
301 MPI_File_set_view( data_file, 0, MpiTraits<value_type>::type(), subarray,
302 "native", MPI_INFO_NULL );
303
304 // Write the view to binary.
305 MPI_Status status;
306 MPI_File_write_all( data_file, owned_view.data(), owned_view.size(),
307 MpiTraits<value_type>::type(), &status );
308
309 // Clean up.
310 MPI_File_close( &data_file );
311 MPI_Type_free( &subarray );
312
313 // Create a VisIt BOV header with global data. Only create the header
314 // on rank 0.
315 int rank;
316 MPI_Comm_rank( global_grid.comm(), &rank );
317 if ( 0 == rank )
318 {
319 // Open a file for writing.
320 std::string header_file_name = file_name.str() + ".bov";
321 std::fstream header;
322 header.open( header_file_name, std::fstream::out );
323
324 // Write the current time.
325 header << "TIME: " << time << std::endl;
326
327 // Data file name.
328 header << "DATA_FILE: " << data_file_name << std::endl;
329
330 // Global data size.
331 header << "DATA_SIZE: ";
332 for ( std::size_t d = 0; d < num_space_dim; ++d )
333 {
334 header << global_extents[d] << " ";
335 }
336 for ( std::size_t d = num_space_dim; d < 3; ++d )
337 {
338 header << 1;
339 }
340 header << std::endl;
341
342 // Data format.
343 header << "DATA_FORMAT: " << BovFormat<value_type>::value()
344 << std::endl;
345
346 // Variable name.
347 header << "VARIABLE: " << array.label() << std::endl;
348
349 // Endian order
350 header << "DATA_ENDIAN: LITTLE" << std::endl;
351
352 // Data location.
353 header << "CENTERING: " << BovCentering<entity_type>::value()
354 << std::endl;
355
356 // Mesh low corner.
357 header << "BRICK_ORIGIN: ";
358 for ( std::size_t d = 0; d < num_space_dim; ++d )
359 {
360 header << global_mesh.lowCorner( d ) << " ";
361 }
362 for ( std::size_t d = num_space_dim; d < 3; ++d )
363 {
364 header << 0.0;
365 }
366 header << std::endl;
367
368 // Mesh global width
369 header << "BRICK_SIZE: ";
370 for ( std::size_t d = 0; d < num_space_dim; ++d )
371 {
372 header << global_grid.globalNumEntity( Cell(), d ) *
373 global_mesh.cellSize( d )
374 << " ";
375 }
376 for ( std::size_t d = num_space_dim; d < 3; ++d )
377 {
378 header << 0.0;
379 }
380 header << std::endl;
381
382 // Number of data components. Scalar and vector types are
383 // supported.
384 header << "DATA_COMPONENTS: " << global_extents[num_space_dim]
385 << std::endl;
386
387 // Close the header.
388 header.close();
389 }
390}
391
405template <class Array_t>
406void writeTimeStep( const std::string& prefix, const int time_step_index,
407 const double time, const Array_t& array,
408 const bool gather_array = true )
409{
410 using exec_space = typename Array_t::execution_space;
411 writeTimeStep( exec_space{}, prefix, time_step_index, time, array,
412 gather_array );
413}
414
427template <class ExecutionSpace, class Array_t,
428 typename std::enable_if<
429 Kokkos::is_execution_space<ExecutionSpace>::value, int>::type = 0>
430void writeTimeStep( ExecutionSpace, const int time_step_index,
431 const double time, const Array_t& array,
432 const bool gather_array = true )
433{
434 writeTimeStep( ExecutionSpace{}, "grid_" + array.label(), time_step_index,
435 time, array, gather_array );
436}
437
450template <class Array_t>
451void writeTimeStep( const int time_step_index, const double time,
452 const Array_t& array, const bool gather_array = true )
453{
454 using exec_space = typename Array_t::execution_space;
455 writeTimeStep( exec_space{}, "grid_" + array.label(), time_step_index, time,
456 array, gather_array );
457}
458
459//---------------------------------------------------------------------------//
460
461} // end namespace BovWriter
462} // end namespace Experimental
463} // namespace Grid
464} // namespace Cabana
465
466#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:184
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:955
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