Branch data Line data Source code
1 : : // *****************************************************************************
2 : : /*!
3 : : \file src/IO/ExodusIIMeshWriter.cpp
4 : : \copyright 2012-2015 J. Bakosi,
5 : : 2016-2018 Los Alamos National Security, LLC.,
6 : : 2019-2021 Triad National Security, LLC.,
7 : : 2022-2024 J. Bakosi
8 : : All rights reserved. See the LICENSE file for details.
9 : : \brief ExodusII mesh-based data writer
10 : : \details ExodusII mesh-based data writer class definition.
11 : : */
12 : : // *****************************************************************************
13 : :
14 : : #include <numeric>
15 : :
16 : : #include "NoWarning/exodusII.hpp"
17 : :
18 : : #include "ExodusIIMeshWriter.hpp"
19 : : #include "Exception.hpp"
20 : : #include "UnsMesh.hpp"
21 : :
22 : : using tk::ExodusIIMeshWriter;
23 : :
24 : 4377 : ExodusIIMeshWriter::ExodusIIMeshWriter( const std::string& filename,
25 : : ExoWriter mode,
26 : : int cpuwordsize,
27 : 4377 : int iowordsize ) :
28 : 4377 : m_filename( filename ), m_outFile( 0 )
29 : : // *****************************************************************************
30 : : // Constructor: create/open Exodus II file
31 : : //! \param[in] filename File to open as ExodusII file
32 : : //! \param[in] mode ExodusII writer constructor mode: ExoWriter::CREATE for
33 : : //! creating a new file, ExoWriter::OPEN for opening an existing file for
34 : : //! appending
35 : : //! \param[in] cpuwordsize Set CPU word size, see ExodusII documentation
36 : : //! \param[in] iowordsize Set I/O word size, see ExodusII documentation
37 : : // *****************************************************************************
38 : : {
39 : : // Increase verbosity from ExodusII library in debug mode
40 : : #ifndef NDEBUG
41 : : ex_opts( EX_DEBUG | EX_VERBOSE );
42 : : #endif
43 : :
44 [ + + ]: 4377 : if (mode == ExoWriter::CREATE) {
45 : :
46 [ + - ]: 853 : m_outFile = ex_create( filename.c_str(),
47 : : EX_CLOBBER | EX_LARGE_MODEL,
48 : : &cpuwordsize,
49 : : &iowordsize );
50 : :
51 [ + - ]: 3524 : } else if (mode == ExoWriter::OPEN) {
52 : :
53 : : float version;
54 [ + - ]: 3524 : m_outFile = ex_open( filename.c_str(),
55 : : EX_WRITE,
56 : : &cpuwordsize,
57 : : &iowordsize,
58 : : &version );
59 : :
60 [ - - ][ - - ]: 0 : } else Throw( "Unknown ExodusII writer constructor mode" );
[ - - ][ - - ]
[ - - ][ - - ]
[ - - ]
61 : :
62 [ - + ][ - - ]: 4377 : ErrChk( m_outFile > 0, "Failed to create/open ExodusII file: " + filename );
[ - - ][ - - ]
[ - - ][ - - ]
[ - - ][ - - ]
63 : 4377 : }
64 : :
65 : 4377 : ExodusIIMeshWriter::~ExodusIIMeshWriter() noexcept
66 : : // *****************************************************************************
67 : : // Destructor
68 : : // *****************************************************************************
69 : : {
70 [ - + ]: 4377 : if ( ex_close(m_outFile) < 0 )
71 : 0 : printf( ">>> WARNING: Failed to close ExodusII file: %s\n",
72 : : m_filename.c_str() );
73 : 4377 : }
74 : :
75 : : void
76 : 853 : ExodusIIMeshWriter::writeMesh( const UnsMesh& mesh ) const
77 : : // *****************************************************************************
78 : : // Write ExodusII mesh file taking a tk::UnsMesh object
79 : : //! \param[in] mesh Unstructured mesh object
80 : : // *****************************************************************************
81 : : {
82 : 853 : writeHeader( mesh );
83 : 853 : writeNodes( mesh );
84 : 853 : writeElements( mesh );
85 : 853 : writeSidesets( mesh );
86 : 853 : writeNodesets( mesh );
87 : 853 : writeTimeValues( mesh.vartimes() );
88 : 853 : writeNodeVarNames( mesh.nodevarnames() );
89 : 853 : writeNodeScalars( mesh.nodevars() );
90 : 853 : }
91 : :
92 : : void
93 : 853 : ExodusIIMeshWriter::writeHeader( const UnsMesh& mesh ) const
94 : : // *****************************************************************************
95 : : // Write ExodusII header
96 : : //! \param[in] mesh Unstructured mesh object
97 : : // *****************************************************************************
98 : : {
99 [ - + ][ - - ]: 853 : ErrChk(
[ - - ][ - - ]
[ - - ][ - - ]
[ - - ][ - - ]
100 : : ex_put_init( m_outFile,
101 : : "Written by Xyst",
102 : : 3, // number of dimensions
103 : : static_cast< int64_t >( mesh.nnode() ),
104 : : static_cast< int64_t >( mesh.triinpoel().size()/3 +
105 : : mesh.tetinpoel().size()/4 ),
106 : : static_cast< int64_t >( mesh.neblk() ),
107 : : static_cast< int64_t >( mesh.bnode().size() ),
108 : : static_cast< int64_t >( mesh.bface().size() )
109 : : ) == 0,
110 : : "Failed to write header to file: " + m_filename );
111 : 853 : }
112 : :
113 : : void
114 : 853 : ExodusIIMeshWriter::writeNodes( const UnsMesh& mesh ) const
115 : : // *****************************************************************************
116 : : // Write node coordinates to ExodusII file
117 : : //! \param[in] mesh Unstructured mesh object
118 : : // *****************************************************************************
119 : : {
120 [ - + ][ - - ]: 853 : ErrChk( ex_put_coord( m_outFile, mesh.x().data(), mesh.y().data(),
[ - - ][ - - ]
[ - - ][ - - ]
[ - - ][ - - ]
121 : : mesh.z().data() ) == 0,
122 : : "Failed to write coordinates to ExodusII file: " + m_filename );
123 : 853 : }
124 : :
125 : : void
126 : 853 : ExodusIIMeshWriter::writeElements( const UnsMesh& mesh ) const
127 : : // *****************************************************************************
128 : : // Write element connectivity to ExodusII file
129 : : //! \param[in] mesh Unstructured mesh object
130 : : // *****************************************************************************
131 : : {
132 : 853 : int elclass = 0;
133 : :
134 : : // For meshes that have no triangle element block (only tetrahedra), keeping
135 : : // the order as tets first followed by triangles allows keeping the tet ids
136 : : // associated to side sets the same when adding the missing triangle elements
137 : : // by meshconv. Hence this order should be kept as tets first triangles next.
138 : :
139 [ + - ]: 1706 : writeElemBlock( elclass, 4, "TETRAHEDRA", mesh.tetinpoel() );
140 [ + - ]: 853 : writeElemBlock( elclass, 3, "TRIANGLES", mesh.triinpoel() );
141 : 853 : }
142 : :
143 : : void
144 [ + + ]: 1706 : ExodusIIMeshWriter::writeElemBlock( int& elclass,
145 : : int64_t nnpe,
146 : : const std::string& eltype,
147 : : const std::vector< std::size_t >& inpoel )
148 : : const
149 : : // *****************************************************************************
150 : : // Write element block to ExodusII file
151 : : //! \param[inout] elclass Count element class ids in file
152 : : //! \param[in] nnpe Number of nodes per element for block
153 : : //! \param[in] eltype String describing element type
154 : : //! \param[in] inpoel Element connectivity
155 : : // *****************************************************************************
156 : : {
157 [ + + ]: 1706 : if (inpoel.empty()) return;
158 : :
159 : : // increase number of element classes in file
160 : 862 : ++elclass;
161 : :
162 : : // Compute number of edges and number of faces for triangles and tetrahedra
163 : : int nedge = 0, nface = 0;
164 [ + + ]: 862 : if (nnpe == 4) {
165 : : nedge = 6;
166 : : nface = 4;
167 [ - + ]: 47 : } else if (nnpe == 3) {
168 : : nedge = 3;
169 : : nface = 1;
170 [ - - ][ - - ]: 0 : } else Throw( "Write ExodusII element block does not support elements with "
[ - - ][ - - ]
[ - - ][ - - ]
[ - - ]
171 : : + std::to_string(nnpe) + " nodes" );
172 : :
173 : : // Write element block information
174 [ - + ][ - - ]: 862 : ErrChk(
[ - - ][ - - ]
[ - - ][ - - ]
[ - - ][ - - ]
175 : : ex_put_block( m_outFile, // exo file handle
176 : : EX_ELEM_BLOCK, // block type: elem block
177 : : elclass, // element class id
178 : : eltype.c_str(), // element block description
179 : : static_cast< int64_t >( inpoel.size() ) / nnpe, // nof cells
180 : : nnpe, // number of nodes per element
181 : : nedge,// number of edges per element
182 : : nface,// number of faces per element
183 : : 0 // number of attributes per element
184 : : ) == 0,
185 : : "Failed to write " + eltype + " element block to ExodusII file: " +
186 : : m_filename );
187 : :
188 : : // Write element connectivity with 1-based node ids
189 : 862 : std::vector< int > inp( inpoel.size() );
190 : : std::size_t i = 0;
191 [ + + ]: 5803045 : for (auto p : inpoel) inp[ i++ ] = static_cast< int >( p+1 );
192 [ + - ][ - + ]: 862 : ErrChk( ex_put_conn( m_outFile, EX_ELEM_BLOCK, elclass, inp.data(),
[ - - ][ - - ]
[ - - ][ - - ]
[ - - ][ - - ]
[ - - ]
193 : : nullptr, nullptr ) == 0,
194 : : "Failed to write " + eltype + " element connectivity to ExodusII "
195 : : "file: " + m_filename );
196 : : }
197 : :
198 : : void
199 : 853 : ExodusIIMeshWriter::writeSidesets( const UnsMesh& mesh ) const
200 : : // *****************************************************************************
201 : : // Write side sets and their face connectivity to ExodusII file
202 : : //! \param[in] mesh Unstructured mesh object
203 : : // *****************************************************************************
204 : : {
205 : : // Write all side sets face list and connectivity in mesh
206 [ + + ]: 870 : for (const auto& s : mesh.bface()) {
207 : : // Write side set parameters
208 [ - + ][ - - ]: 17 : ErrChk( ex_put_set_param( m_outFile, EX_SIDE_SET, s.first,
[ - - ][ - - ]
[ - - ][ - - ]
[ - - ][ - - ]
209 : : static_cast<int64_t>(s.second.size()), 0 ) == 0,
210 : : "Failed to write side set parameters to ExodusII file: " + m_filename );
211 : :
212 : : // ExodusII wants 32-bit integers as IDs of element ids
213 : 17 : std::vector< int > bface( s.second.size() );
214 : : std::size_t i = 0;
215 [ + + ]: 7443 : for (auto f : s.second) bface[ i++ ] = static_cast<int>(f)+1;
216 : : // ExodusII wants 32-bit integers as element-relative face IDs
217 : : const auto& fi = tk::cref_find( mesh.faceid(), s.first );
218 [ + - ][ - - ]: 17 : std::vector< int > faceid( fi.size() );
219 : : i = 0;
220 [ + + ]: 7443 : for (auto f : fi) faceid[ i++ ] = static_cast<int>(f)+1;
221 : :
222 : : // Write side set data: ExodusII-file internal element ids adjacent to side
223 : : // set and face id relative to element indicating which face is aligned with
224 : : // the side set.
225 [ + - ][ - + ]: 17 : ErrChk( ex_put_set( m_outFile, EX_SIDE_SET, s.first, bface.data(),
[ - - ][ - - ]
[ - - ][ - - ]
[ - - ][ - - ]
[ - - ]
226 : : faceid.data() ) == 0,
227 : : "Failed to write side set face list to ExodusII file: " + m_filename );
228 : : }
229 : 853 : }
230 : :
231 : : void
232 : 853 : ExodusIIMeshWriter::writeNodesets( const UnsMesh& mesh ) const
233 : : // *****************************************************************************
234 : : // Write side sets and their node list to ExodusII file
235 : : //! \param[in] mesh Unstructured mesh object
236 : : // *****************************************************************************
237 : : {
238 : : // Write all side set node lists in mesh
239 [ + + ]: 1226 : for (const auto& s : mesh.bnode()) {
240 : : // Write side set parameters
241 [ - + ][ - - ]: 373 : ErrChk( ex_put_set_param( m_outFile, EX_NODE_SET, s.first,
[ - - ][ - - ]
[ - - ][ - - ]
[ - - ][ - - ]
242 : : static_cast<int64_t>(s.second.size()), 0 ) == 0,
243 : : "Failed to write side set parameters to ExodusII file: " + m_filename );
244 : :
245 : : // ExodusII wants 32-bit integers as IDs of node ids
246 : 373 : std::vector< int > bnode( s.second.size() );
247 : : std::size_t i = 0;
248 [ + + ]: 54426 : for (auto n : s.second) bnode[ i++ ] = static_cast<int>(n)+1;
249 : :
250 : : // Write side set data
251 [ + - ][ - + ]: 373 : ErrChk( ex_put_set( m_outFile, EX_NODE_SET, s.first, bnode.data(),
[ - - ][ - - ]
[ - - ][ - - ]
[ - - ][ - - ]
[ - - ]
252 : : nullptr ) == 0,
253 : : "Failed to write side set node list to ExodusII file: " + m_filename );
254 : : }
255 : 853 : }
256 : :
257 : : void
258 : 3524 : ExodusIIMeshWriter::writeTimeStamp( uint64_t it, tk::real time ) const
259 : : // *****************************************************************************
260 : : // Write time stamp to ExodusII file
261 : : //! \param[in] it Iteration number
262 : : //! \param[in] time Time
263 : : // *****************************************************************************
264 : : {
265 [ - + ][ - - ]: 3524 : ErrChk( ex_put_time( m_outFile, static_cast<int>(it), &time ) == 0,
[ - - ][ - - ]
[ - - ][ - - ]
[ - - ][ - - ]
266 : : "Failed to write time stamp to ExodusII file: " + m_filename );
267 : 3524 : }
268 : :
269 : : void
270 : 853 : ExodusIIMeshWriter::writeTimeValues( const std::vector< tk::real >& tv ) const
271 : : // *****************************************************************************
272 : : // Write time values to ExodusII file
273 : : //! \param[in] tv Time values for all time steps
274 : : // *****************************************************************************
275 : : {
276 : : int i = 0;
277 [ + + ]: 856 : for (const auto& v : tv) {
278 [ - + ][ - - ]: 3 : ErrChk( ex_put_time( m_outFile, ++i, &v ) == 0,
[ - - ][ - - ]
[ - - ][ - - ]
[ - - ][ - - ]
279 : : "Failed to write time value for a time step to ExodusII file: " +
280 : : m_filename );
281 : : }
282 : 853 : }
283 : :
284 : : void
285 [ + + ]: 1694 : ExodusIIMeshWriter::writeNodeVarNames( const std::vector< std::string >& nv )
286 : : const
287 : : // *****************************************************************************
288 : : // Write the names of nodal output variables to ExodusII file
289 : : //! \param[in] nv Nodal variable names
290 : : // *****************************************************************************
291 : : {
292 [ + + ]: 1694 : if (!nv.empty()) {
293 : :
294 : : #if defined(__clang__)
295 : : #pragma clang diagnostic push
296 : : #pragma clang diagnostic ignored "-Wvla"
297 : : #pragma clang diagnostic ignored "-Wvla-extension"
298 : : #elif defined(STRICT_GNUC)
299 : : #pragma GCC diagnostic push
300 : : #pragma GCC diagnostic ignored "-Wvla"
301 : : #endif
302 : :
303 [ - + ][ - - ]: 842 : ErrChk(
[ - - ][ - - ]
[ - - ][ - - ]
[ - - ][ - - ]
304 : : ex_put_variable_param( m_outFile, EX_NODE_BLOCK,
305 : : static_cast< int >( nv.size() ) ) == 0,
306 : : "Failed to write nodal output variable parameters to ExodusII file: " +
307 : : m_filename );
308 : :
309 : 842 : char* names[ nv.size() ];
310 : : std::size_t i = 0;
311 [ + + ]: 8125 : for (const auto& n : nv) names[ i++ ] = const_cast< char* >( n.c_str() );
312 : :
313 [ - + ][ - - ]: 842 : ErrChk( ex_put_variable_names( m_outFile,
[ - - ][ - - ]
[ - - ][ - - ]
[ - - ][ - - ]
314 : : EX_NODE_BLOCK,
315 : : static_cast< int >( nv.size() ),
316 : : names ) == 0,
317 : : "Failed to write nodal output variable names to ExodusII file: " +
318 : : m_filename );
319 : :
320 : : #if defined(__clang__)
321 : : #pragma clang diagnostic pop
322 : : #elif defined(STRICT_GNUC)
323 : : #pragma GCC diagnostic pop
324 : : #endif
325 : 842 : }
326 : 1694 : }
327 : :
328 : : void
329 [ + + ]: 841 : ExodusIIMeshWriter::writeElemVarNames( const std::vector< std::string >& ev )
330 : : const
331 : : // *****************************************************************************
332 : : // Write the names of element output variables to ExodusII file
333 : : //! \param[in] ev Elem variable names
334 : : // *****************************************************************************
335 : : {
336 [ + + ]: 841 : if (!ev.empty()) {
337 : :
338 : : #if defined(__clang__)
339 : : #pragma clang diagnostic push
340 : : #pragma clang diagnostic ignored "-Wvla"
341 : : #pragma clang diagnostic ignored "-Wvla-extension"
342 : : #elif defined(STRICT_GNUC)
343 : : #pragma GCC diagnostic push
344 : : #pragma GCC diagnostic ignored "-Wvla"
345 : : #endif
346 : :
347 [ - + ][ - - ]: 98 : ErrChk(
[ - - ][ - - ]
[ - - ][ - - ]
[ - - ][ - - ]
348 : : ex_put_variable_param( m_outFile, EX_ELEM_BLOCK,
349 : : static_cast< int >( ev.size() ) ) == 0,
350 : : "Failed to write element output variable parameters to ExodusII file: " +
351 : : m_filename );
352 : :
353 : 98 : char* names[ ev.size() ];
354 : : std::size_t i = 0;
355 [ + + ]: 392 : for (const auto& n : ev) names[ i++ ] = const_cast< char* >( n.c_str() );
356 : :
357 [ - + ][ - - ]: 98 : ErrChk( ex_put_variable_names( m_outFile,
[ - - ][ - - ]
[ - - ][ - - ]
[ - - ][ - - ]
358 : : EX_ELEM_BLOCK,
359 : : static_cast< int >( ev.size() ),
360 : : names ) == 0,
361 : : "Failed to write element output variable names to ExodusII file: " +
362 : : m_filename );
363 : :
364 : : #if defined(__clang__)
365 : : #pragma clang diagnostic pop
366 : : #elif defined(STRICT_GNUC)
367 : : #pragma GCC diagnostic pop
368 : : #endif
369 : 98 : }
370 : 841 : }
371 : :
372 : : void
373 : 853 : ExodusIIMeshWriter::writeNodeScalars(
374 : : const std::vector< std::vector< std::vector< tk::real > > >& var ) const
375 : : // *****************************************************************************
376 : : // Write multiple node scalar fields to ExodusII file at multiple time steps
377 : : //! \param[in] var Vector of nodal variables to write: inner vector: nodes,
378 : : //! middle vector: (physics) variable, outer vector: time step
379 : : // *****************************************************************************
380 : : {
381 : : uint64_t time = 0;
382 : : int varid = 0;
383 : :
384 [ + + ]: 856 : for (const auto& t : var) { // for all times
385 : 3 : ++time;
386 [ + + ]: 21 : for (const auto& v : t) { // for all variables
387 : 18 : writeNodeScalar( time, ++varid, v );
388 : : }
389 : : varid = 0;
390 : : }
391 : 853 : }
392 : :
393 : : void
394 [ + - ]: 33755 : ExodusIIMeshWriter::writeNodeScalar( uint64_t it,
395 : : int varid,
396 : : const std::vector< tk::real >& var ) const
397 : : // *****************************************************************************
398 : : // Write node scalar field to ExodusII file
399 : : //! \param[in] it Iteration number
400 : : //! \param[in] varid Variable id
401 : : //! \param[in] var Vector of variable to output
402 : : // *****************************************************************************
403 : : {
404 [ + - ]: 33755 : if (!var.empty()) {
405 [ - + ][ - - ]: 33755 : ErrChk( ex_put_var( m_outFile,
[ - - ][ - - ]
[ - - ][ - - ]
[ - - ][ - - ]
406 : : static_cast< int >( it ),
407 : : EX_NODE_BLOCK,
408 : : varid,
409 : : 1,
410 : : static_cast< int64_t >( var.size() ),
411 : : var.data() ) == 0,
412 : : "Failed to write node scalar to ExodusII file: " + m_filename );
413 : : }
414 : 33755 : }
415 : :
416 : : void
417 [ + - ]: 294 : ExodusIIMeshWriter::writeElemScalar( uint64_t it,
418 : : int varid,
419 : : const std::vector< tk::real >& var ) const
420 : : // *****************************************************************************
421 : : // Write elem scalar field to ExodusII file
422 : : //! \param[in] it Iteration number
423 : : //! \param[in] varid Variable id
424 : : //! \param[in] var Vector of variable to output
425 : : // *****************************************************************************
426 : : {
427 [ + - ]: 294 : if (!var.empty()) {
428 [ - + ][ - - ]: 294 : ErrChk( ex_put_var( m_outFile,
[ - - ][ - - ]
[ - - ][ - - ]
[ - - ][ - - ]
429 : : static_cast< int >( it ),
430 : : EX_ELEM_BLOCK,
431 : : varid,
432 : : 1,
433 : : static_cast< int64_t >( var.size() ),
434 : : var.data() ) == 0,
435 : : "Failed to write elem scalar to ExodusII file: " + m_filename );
436 : : }
437 : 294 : }
|