Branch data Line data Source code
1 : : // *****************************************************************************
2 : : /*!
3 : : \file src/IO/MeshWriter.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 Charm++ group for outputing mesh data to file
10 : : \details Charm++ group definition used to output data associated to
11 : : unstructured meshes to file(s). Charm++ chares (work units) send mesh and
12 : : field data associated to mesh entities to the MeshWriter class defined here
13 : : to write the data to file(s).
14 : : */
15 : : // *****************************************************************************
16 : :
17 : : #include "XystBuildConfig.hpp"
18 : : #include "MeshWriter.hpp"
19 : : #include "Reorder.hpp"
20 : : #include "ExodusIIMeshWriter.hpp"
21 : :
22 : : using tk::MeshWriter;
23 : :
24 : : void
25 : 3368 : MeshWriter::write(
26 : : std::size_t meshid,
27 : : bool meshoutput,
28 : : bool fieldoutput,
29 : : uint64_t itr,
30 : : uint64_t itf,
31 : : tk::real time,
32 : : int chareid,
33 : : const std::string& basefilename,
34 : : const std::vector< std::size_t >& inpoel,
35 : : const UnsMesh::Coords& coord,
36 : : const std::map< int, std::vector< std::size_t > >& bface,
37 : : const std::map< int, std::vector< std::size_t > >& bnode,
38 : : const std::vector< std::size_t >& triinpoel,
39 : : const std::vector< std::string >& elemfieldnames,
40 : : const std::vector< std::string >& nodefieldnames,
41 : : const std::vector< std::string >& elemsurfnames,
42 : : const std::vector< std::string >& nodesurfnames,
43 : : const std::vector< std::vector< tk::real > >& elemfields,
44 : : const std::vector< std::vector< tk::real > >& nodefields,
45 : : const std::vector< std::vector< tk::real > >& elemsurfs,
46 : : const std::vector< std::vector< tk::real > >& nodesurfs,
47 : : const std::set< int >& outsets,
48 : : CkCallback c )
49 : : // *****************************************************************************
50 : : // Output unstructured mesh into file
51 : : //! \param[in] meshid Mesh Id
52 : : //! \param[in] meshoutput True if mesh is to be written
53 : : //! \param[in] fieldoutput True if field data is to be written
54 : : //! \param[in] itr Iteration count since a new mesh. New mesh in this context
55 : : //! means that either the mesh is moved and/or its topology has changed.
56 : : //! \param[in] itf Field output iteration count
57 : : //! \param[in] time Physical time this at this field output dump
58 : : //! \param[in] chareid The chare id the write-to-file request is coming from
59 : : //! \param[in] basefilename String to use as the base of the filename
60 : : //! \param[in] inpoel Mesh connectivity for the mesh chunk to be written with
61 : : //! local ids
62 : : //! \param[in] coord Node coordinates of the mesh chunk to be written
63 : : //! \param[in] bface Map of boundary-face lists mapped to corresponding side set
64 : : //! ids for this mesh chunk
65 : : //! \param[in] bnode Map of boundary-node lists mapped to corresponding side set
66 : : //! ids for this mesh chunk with local ids
67 : : //! \param[in] triinpoel Interconnectivity of points and boundary-face in this
68 : : //! mesh chunk with local ids
69 : : //! \param[in] elemfieldnames Names of element fields to be output to file
70 : : //! \param[in] nodefieldnames Names of node fields to be output to file
71 : : //! \param[in] elemsurfnames Names of eleme surface fields to be output to file
72 : : //! \param[in] nodesurfnames Names of node surface fields to be output to file
73 : : //! \param[in] elemfields Field data in mesh elements to output to file
74 : : //! \param[in] nodefields Field data in mesh nodes to output to file
75 : : //! \param[in] elemsurfs Surface field data in mesh elements to output to file
76 : : //! \param[in] nodesurfs Surface field data in mesh nodes to output to file
77 : : //! \param[in] outsets Unique set of surface side set ids along which to save
78 : : //! solution field variables
79 : : //! \param[in] c Function to continue with after the write
80 : : // *****************************************************************************
81 : : {
82 [ - + ][ - - ]: 3368 : if (m_benchmark) { c.send(); return; }
83 : :
84 : : // Generate filenames for volume and surface field output
85 [ + - ]: 3368 : auto vf = filename( basefilename, meshid, itr, chareid );
86 : :
87 [ + + ]: 3368 : if (meshoutput) {
88 : : // Write volume mesh and field names
89 [ + - ]: 805 : ExodusIIMeshWriter ev( vf, ExoWriter::CREATE );
90 : : // Write chare mesh (do not write side sets in parallel)
91 [ + + ]: 805 : if (m_nchare == 1) {
92 [ + - ][ + - ]: 77 : ev.writeMesh( tk::UnsMesh( inpoel, coord, bnode ) );
93 : : } else {
94 [ + - ]: 728 : ev.writeMesh< 4 >( inpoel, coord );
95 : : }
96 [ + - ]: 805 : ev.writeElemVarNames( elemfieldnames );
97 [ - + ][ - - ]: 805 : Assert( nodefieldnames.size() == nodefields.size(), "Size mismatch" );
[ - - ][ - - ]
98 [ + - ]: 805 : ev.writeNodeVarNames( nodefieldnames );
99 : :
100 : : // Write surface meshes and surface variable field names
101 [ + + ]: 841 : for (auto s : outsets) {
102 [ + - ]: 36 : auto sf = filename( basefilename, meshid, itr, chareid, s );
103 [ + - ]: 36 : ExodusIIMeshWriter es( sf, ExoWriter::CREATE );
104 [ + - ]: 36 : auto b = bface.find(s);
105 [ + + ]: 36 : if (b == end(bface)) {
106 : : // If a side set does not exist on a chare, write out a
107 : : // connectivity for a single triangle with its node coordinates of
108 : : // zero. This is so the paraview series reader can load side sets
109 : : // distributed across multiple files. See also
110 : : // https://www.paraview.org/Wiki/Restarted_Simulation_Readers.
111 [ + - ][ + - ]: 3 : es.writeMesh< 3 >( std::vector< std::size_t >{1,2,3},
112 : 3 : UnsMesh::Coords{{ {{0,0,0}}, {{0,0,0}}, {{0,0,0}} }} );
113 : 3 : es.writeElemVarNames( elemsurfnames );
114 [ + - ]: 3 : es.writeNodeVarNames( nodesurfnames );
115 : 3 : continue;
116 : : }
117 : 33 : std::vector< std::size_t > nodes;
118 [ + + ]: 5493 : for (auto f : b->second) {
119 [ + - ]: 5460 : nodes.push_back( triinpoel[f*3+0] );
120 [ + - ]: 5460 : nodes.push_back( triinpoel[f*3+1] );
121 [ + - ]: 5460 : nodes.push_back( triinpoel[f*3+2] );
122 : : }
123 [ + - ]: 33 : auto [inp,gid,lid] = tk::global2local( nodes );
124 [ + - ]: 33 : tk::unique( nodes );
125 : 33 : auto nnode = nodes.size();
126 : 33 : UnsMesh::Coords scoord;
127 [ + - ]: 33 : scoord[0].resize( nnode );
128 [ + - ]: 33 : scoord[1].resize( nnode );
129 [ + - ]: 33 : scoord[2].resize( nnode );
130 : 33 : std::size_t j = 0;
131 [ + + ]: 3783 : for (auto i : nodes) {
132 : 3750 : scoord[0][j] = coord[0][i];
133 : 3750 : scoord[1][j] = coord[1][i];
134 : 3750 : scoord[2][j] = coord[2][i];
135 : 3750 : ++j;
136 : : }
137 [ + - ]: 33 : es.writeMesh< 3 >( inp, scoord );
138 [ + - ]: 33 : es.writeElemVarNames( elemsurfnames );
139 [ + - ]: 33 : es.writeNodeVarNames( nodesurfnames );
140 [ + + ][ + + ]: 39 : }
141 : 805 : }
142 : :
143 [ + - ]: 3368 : if (fieldoutput) {
144 : : // Write volume variable fields
145 [ + - ]: 3368 : ExodusIIMeshWriter ev( vf, ExoWriter::OPEN );
146 [ + - ]: 3368 : ev.writeTimeStamp( itf, time );
147 : : // Write volume element variable fields
148 : 3368 : int varid = 0;
149 [ + - ][ + + ]: 3662 : for (const auto& v : elemfields) ev.writeElemScalar( itf, ++varid, v );
150 : : // Write volume node variable fields
151 : 3368 : varid = 0;
152 [ + - ][ + + ]: 36169 : for (const auto& v : nodefields) ev.writeNodeScalar( itf, ++varid, v );
153 : :
154 : : // Write surface node variable fields
155 : 3368 : std::size_t j = 0;
156 : 3368 : std::size_t k = 0;
157 : 3368 : auto nvar = static_cast< int >( nodesurfnames.size() ) ;
158 : 3368 : auto nevar = static_cast< int >( elemsurfnames.size() ) ;
159 [ + + ]: 3524 : for (auto s : outsets) {
160 [ + - ]: 156 : auto sf = filename( basefilename, meshid, itr, chareid, s );
161 [ + - ]: 156 : ExodusIIMeshWriter es( sf, ExoWriter::OPEN );
162 [ + - ]: 156 : es.writeTimeStamp( itf, time );
163 [ + - ][ + + ]: 156 : if (bface.find(s) == end(bface)) {
164 : : // If a side set does not exist on a chare, write out a
165 : : // a node field for a single triangle with zeros. This is so the
166 : : // paraview series reader can load side sets distributed across
167 : : // multiple files. See also
168 : : // https://www.paraview.org/Wiki/Restarted_Simulation_Readers.
169 [ + - ][ + - ]: 126 : for (int i=1; i<=nvar; ++i) es.writeNodeScalar( itf, i, {0,0,0} );
[ + + ]
170 [ - - ][ - - ]: 18 : for (int i=1; i<=nevar; ++i) es.writeElemScalar( itf, i, {0} );
[ - + ]
171 : 18 : continue;
172 : 18 : }
173 [ + + ]: 966 : for (int i=1; i<=nvar; ++i)
174 [ + - ]: 828 : es.writeNodeScalar( itf, i, nodesurfs[j++] );
175 [ - + ]: 138 : for (int i=1; i<=nevar; ++i)
176 [ - - ]: 0 : es.writeElemScalar( itf, i, elemsurfs[k++] );
177 [ + + ][ + + ]: 174 : }
178 : 3368 : }
179 : :
180 [ + - ]: 3368 : c.send();
181 [ + - ][ + - ]: 3374 : }
[ + - ][ + - ]
[ - - ][ - - ]
182 : :
183 : : std::string
184 : 3560 : MeshWriter::filename( const std::string& basefilename,
185 : : std::size_t meshid,
186 : : uint64_t itr,
187 : : int chareid,
188 : : int surfid ) const
189 : : // *****************************************************************************
190 : : // Compute filename
191 : : //! \param[in] basefilename String use as the base filename.
192 : : //! \param[in] meshid Mesh Id
193 : : //! \param[in] itr Iteration count since a new mesh. New mesh in this context
194 : : //! means that either the mesh is moved and/or its topology has changed.
195 : : //! \param[in] chareid The chare id the write-to-file request is coming from
196 : : //! \param[in] surfid Surface ID if computing a surface filename
197 : : //! \details We use a file naming convention for large field output data that
198 : : //! allows ParaView to glue multiple files into a single simulation output by
199 : : //! only loading a single file. The base filename is followed by ".e-s.",
200 : : //! which probably stands for Exodus Sequence, followed by 3 integers:
201 : : //! (1) {RS}: counts the number of "restart dumps", but we use this for
202 : : //! counting the number of outputs with a different mesh, e.g., due to
203 : : //! mesh refinement, thus if this first number is new the mesh is new
204 : : //! compared to the previous (first) number afer ".e-s.",
205 : : //! (2) {NP}: total number of partitions (workers, chares), this is more than
206 : : //! the number of PEs with nonzero virtualization (overdecomposition), and
207 : : //! (3) {RANK}: worker (chare) id.
208 : : //! Thus {RANK} does spatial partitioning, while {RS} partitions in time, but
209 : : //! a single {RS} id may contain multiple time steps, which equals to the
210 : : //! number of time steps at which field output is saved without refining the
211 : : //! mesh.
212 : : //! \return Filename computed
213 : : //! \see https://www.paraview.org/Wiki/Restarted_Simulation_Readers
214 : : // *****************************************************************************
215 : : {
216 [ + + ][ + - ]: 7120 : return basefilename + (surfid ? "-surf." + std::to_string(surfid) : "")
[ + - ][ + - ]
[ + + ][ + + ]
[ - - ][ - - ]
217 [ - + ][ - - ]: 14240 : + (m_nmesh > 1 ? '.' + std::to_string(meshid) : "")
[ - - ][ + - ]
[ + - ][ + - ]
[ - + ][ - - ]
[ - - ]
218 [ + - ]: 7120 : + ".e-s"
219 [ + - ][ + - ]: 14240 : + '.' + std::to_string( itr ) // iteration count with new mesh
[ + - ]
220 [ + - ][ + - ]: 14240 : + '.' + std::to_string( m_nchare ) // total number of workers
221 [ + - ][ + - ]: 14240 : + '.' + std::to_string( chareid ); // new file per worker
222 : : }
223 : :
224 : : #include "NoWarning/meshwriter.def.h"
|