Branch data Line data Source code
1 : : // *****************************************************************************
2 : : /*!
3 : : \file src/Inciter/DG.cpp
4 : : \copyright 2012-2015 J. Bakosi,
5 : : 2016-2018 Los Alamos National Security, LLC.,
6 : : 2019-2021 Triad National Security, LLC.
7 : : All rights reserved. See the LICENSE file for details.
8 : : \brief DG advances a system of PDEs with the discontinuous Galerkin scheme
9 : : \details DG advances a system of partial differential equations (PDEs) using
10 : : discontinuous Galerkin (DG) finite element (FE) spatial discretization (on
11 : : tetrahedron elements) combined with Runge-Kutta (RK) time stepping.
12 : : \see The documentation in DG.h.
13 : : */
14 : : // *****************************************************************************
15 : :
16 : : #include <algorithm>
17 : : #include <numeric>
18 : : #include <sstream>
19 : :
20 : : #include "DG.hpp"
21 : : #include "Discretization.hpp"
22 : : #include "DGPDE.hpp"
23 : : #include "DiagReducer.hpp"
24 : : #include "DerivedData.hpp"
25 : : #include "ElemDiagnostics.hpp"
26 : : #include "Inciter/InputDeck/InputDeck.hpp"
27 : : #include "Refiner.hpp"
28 : : #include "Limiter.hpp"
29 : : #include "Reorder.hpp"
30 : : #include "Vector.hpp"
31 : : #include "Around.hpp"
32 : : #include "Integrate/Basis.hpp"
33 : : #include "FieldOutput.hpp"
34 : : #include "ChareStateCollector.hpp"
35 : :
36 : : namespace inciter {
37 : :
38 : : extern ctr::InputDeck g_inputdeck;
39 : : extern ctr::InputDeck g_inputdeck_defaults;
40 : : extern std::vector< DGPDE > g_dgpde;
41 : :
42 : : //! Runge-Kutta coefficients
43 : : static const std::array< std::array< tk::real, 3 >, 2 >
44 : : rkcoef{{ {{ 0.0, 3.0/4.0, 1.0/3.0 }}, {{ 1.0, 1.0/4.0, 2.0/3.0 }} }};
45 : :
46 : : } // inciter::
47 : :
48 : : extern tk::CProxy_ChareStateCollector stateProxy;
49 : :
50 : : using inciter::DG;
51 : :
52 : 918 : DG::DG( const CProxy_Discretization& disc,
53 : : const std::map< int, std::vector< std::size_t > >& bface,
54 : : const std::map< int, std::vector< std::size_t > >& /* bnode */,
55 : 918 : const std::vector< std::size_t >& triinpoel ) :
56 : : m_disc( disc ),
57 : : m_ndof_NodalExtrm( 3 ), // for the first order derivatives in 3 directions
58 : : m_ncomfac( 0 ),
59 : : m_nadj( 0 ),
60 : : m_ncomEsup( 0 ),
61 : : m_nsol( 0 ),
62 : : m_ninitsol( 0 ),
63 : : m_nlim( 0 ),
64 : : m_nnod( 0 ),
65 : : m_nreco( 0 ),
66 : : m_nnodalExtrema( 0 ),
67 : : m_inpoel( Disc()->Inpoel() ),
68 : 918 : m_coord( Disc()->Coord() ),
69 [ + - ]: 918 : m_fd( m_inpoel, bface, tk::remap(triinpoel,Disc()->Lid()) ),
70 : 918 : m_u( m_inpoel.size()/4,
71 : 918 : g_inputdeck.get< tag::discr, tag::rdof >()*
72 : 918 : g_inputdeck.get< tag::component >().nprop() ),
73 : : m_un( m_u.nunk(), m_u.nprop() ),
74 : : m_p( m_u.nunk(),
75 : 918 : g_inputdeck.get< tag::discr, tag::rdof >()*
76 : 918 : std::accumulate( begin(g_dgpde), end(g_dgpde), 0u,
77 : 918 : [](std::size_t s, const DGPDE& eq){ return s + eq.nprim(); } ) ),
78 : : m_geoFace( tk::genGeoFaceTri( m_fd.Nipfac(), m_fd.Inpofa(), m_coord) ),
79 : : m_geoElem( tk::genGeoElemTet( m_inpoel, m_coord ) ),
80 : : m_lhs( m_u.nunk(),
81 : 918 : g_inputdeck.get< tag::discr, tag::ndof >()*
82 : 918 : g_inputdeck.get< tag::component >().nprop() ),
83 : : m_rhs( m_u.nunk(), m_lhs.nprop() ),
84 : : m_uNodalExtrm(),
85 : : m_pNodalExtrm(),
86 : : m_uNodalExtrmc(),
87 : : m_pNodalExtrmc(),
88 : 918 : m_nfac( m_fd.Inpofa().size()/3 ),
89 : 918 : m_nunk( m_u.nunk() ),
90 : 918 : m_npoin( m_coord[0].size() ),
91 : : m_ipface(),
92 : : m_bndFace(),
93 : : m_ghostData(),
94 : : m_sendGhost(),
95 : : m_ghostReq( 0 ),
96 : : m_ghost(),
97 : : m_exptGhost(),
98 : : m_recvGhost(),
99 : : m_diag(),
100 : : m_stage( 0 ),
101 : : m_ndof(),
102 : : m_numEqDof(),
103 : : m_bid(),
104 : : m_uc(),
105 : : m_pc(),
106 : : m_ndofc(),
107 : : m_initial( 1 ),
108 : : m_expChBndFace(),
109 : : m_infaces(),
110 : : m_esup(),
111 : : m_esupc(),
112 : : m_elemfields(),
113 : : m_nodefields(),
114 : : m_nodefieldsc(),
115 : : m_outmesh(),
116 : : m_boxelems(),
117 [ + - ][ + - ]: 6426 : m_shockmarker(m_u.nunk())
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
118 : : // *****************************************************************************
119 : : // Constructor
120 : : //! \param[in] disc Discretization proxy
121 : : //! \param[in] bface Boundary-faces mapped to side set ids
122 : : //! \param[in] triinpoel Boundary-face connectivity
123 : : // *****************************************************************************
124 : : {
125 [ + + ]: 918 : if (g_inputdeck.get< tag::cmd, tag::chare >() ||
126 [ + + ]: 880 : g_inputdeck.get< tag::cmd, tag::quiescence >())
127 [ + - ][ + - ]: 1082 : stateProxy.ckLocalBranch()->insert( "DG", thisIndex, CkMyPe(), Disc()->It(),
[ + - ][ + - ]
[ - + ]
128 : : "DG" );
129 : :
130 : : // assign number of dofs for each equation in all pde systems
131 [ + + ]: 1836 : for (const auto& eq : g_dgpde) {
132 : : eq.numEquationDofs(m_numEqDof);
133 : : }
134 : :
135 : : // Allocate storage for the vector of nodal extrema
136 [ + - ][ + - ]: 918 : m_uNodalExtrm.resize( Disc()->Bid().size(), std::vector<tk::real>( 2*
137 [ + - ]: 918 : m_ndof_NodalExtrm*g_inputdeck.get< tag::component >().nprop() ) );
138 [ + - ][ + - ]: 918 : m_pNodalExtrm.resize( Disc()->Bid().size(), std::vector<tk::real>( 2*
139 [ + - ]: 918 : m_ndof_NodalExtrm*m_p.nprop()/g_inputdeck.get< tag::discr, tag::rdof >()));
140 : :
141 : : // Initialization for the buffer vector of nodal extrema
142 [ + - ]: 918 : resizeNodalExtremac();
143 : :
144 : 918 : usesAtSync = true; // enable migration at AtSync
145 : :
146 : : // Enable SDAG wait for setting up chare boundary faces
147 [ + - ][ + - ]: 918 : thisProxy[ thisIndex ].wait4fac();
[ - - ]
148 : :
149 : : // Ensure that mesh partition is not leaky
150 : : Assert( !tk::leakyPartition(m_fd.Esuel(), m_inpoel, m_coord),
151 : : "Input mesh to DG leaky" );
152 : :
153 : : // Ensure mesh physical boundary for the entire problem not leaky,
154 : : // effectively checking if the user has specified boundary conditions on all
155 : : // physical boundary faces
156 [ + - ]: 918 : bndIntegral();
157 : 918 : }
158 : :
159 : : void
160 : 918 : DG::bndIntegral()
161 : : // *****************************************************************************
162 : : // Compute partial boundary surface integral and sum across all chares
163 : : //! \details This function computes a partial surface integral over the boundary
164 : : //! of the faces of this mesh partition then sends its contribution to perform
165 : : //! the integral acorss the total problem boundary. After the global sum a
166 : : //! non-zero vector result indicates a leak, e.g., a hole in the boundary
167 : : //! which indicates an error in the boundary face data structures used to
168 : : //! compute the partial surface integrals.
169 : : // *****************************************************************************
170 : : {
171 : : // Storage for surface integral over our mesh chunk physical boundary
172 : 918 : std::vector< tk::real > s{{ 0.0, 0.0, 0.0 }};
173 : :
174 : : // Integrate over all physical boundary faces
175 [ + + ]: 232884 : for (std::size_t f=0; f<m_fd.Nbfac(); ++f) {
176 : 231966 : s[0] += m_geoFace(f,0,0) * m_geoFace(f,1,0);
177 : 231966 : s[1] += m_geoFace(f,0,0) * m_geoFace(f,2,0);
178 : 231966 : s[2] += m_geoFace(f,0,0) * m_geoFace(f,3,0);
179 : : }
180 : :
181 [ + - ]: 918 : s.push_back( 1.0 ); // positive: call-back to resizeComm() after reduction
182 [ + - ][ + - ]: 918 : s.push_back( static_cast< tk::real >( Disc()->MeshId() ) );
183 : :
184 : : // Send contribution to host summing partial surface integrals
185 : 918 : contribute( s, CkReduction::sum_double,
186 [ + - ][ + - ]: 1836 : CkCallback(CkReductionTarget(Transporter,bndint), Disc()->Tr()) );
[ - - ]
187 : 918 : }
188 : :
189 : : void
190 : 1026 : DG::resizeComm()
191 : : // *****************************************************************************
192 : : // Start sizing communication buffers and setting up ghost data
193 : : // *****************************************************************************
194 : : {
195 : 1026 : auto d = Disc();
196 : :
197 : 1026 : const auto& gid = d->Gid();
198 : : const auto& inpofa = m_fd.Inpofa();
199 : : const auto& esuel = m_fd.Esuel();
200 : :
201 : : // Perform leak test on mesh partition
202 : : Assert( !tk::leakyPartition( esuel, m_inpoel, m_coord ),
203 : : "Mesh partition leaky" );
204 : :
205 : : // Activate SDAG waits for face adjacency map (ghost data) calculation
206 [ + - ]: 1026 : thisProxy[ thisIndex ].wait4ghost();
207 [ + - ]: 1026 : thisProxy[ thisIndex ].wait4esup();
208 : :
209 : : // Enable SDAG wait for initially building the solution vector and limiting
210 [ + + ]: 1026 : if (m_initial) {
211 [ + - ]: 918 : thisProxy[ thisIndex ].wait4sol();
212 [ + - ]: 918 : thisProxy[ thisIndex ].wait4reco();
213 [ + - ]: 918 : thisProxy[ thisIndex ].wait4nodalExtrema();
214 [ + - ]: 918 : thisProxy[ thisIndex ].wait4lim();
215 [ + - ]: 1836 : thisProxy[ thisIndex ].wait4nod();
216 : : }
217 : :
218 : : // Invert inpofa to enable searching for faces based on (global) node triplets
219 : : Assert( inpofa.size() % 3 == 0, "Inpofa must contain triplets" );
220 [ + + ]: 1837956 : for (std::size_t f=0; f<inpofa.size()/3; ++f)
221 : 1836930 : m_ipface.insert( {{{ gid[ inpofa[f*3+0] ],
222 : 1836930 : gid[ inpofa[f*3+1] ],
223 : 1836930 : gid[ inpofa[f*3+2] ] }}} );
224 : :
225 : : // At this point ipface has node-id-triplets (faces) on the internal
226 : : // chare-domain and on the physical boundary but not on chare boundaries,
227 : : // hence the name internal + physical boundary faces.
228 : :
229 : : // Build a set of faces (each face given by 3 global node IDs) associated to
230 : : // chares we potentially share boundary faces with.
231 : : tk::UnsMesh::FaceSet potbndface;
232 [ + + ]: 892757 : for (std::size_t e=0; e<esuel.size()/4; ++e) { // for all our tets
233 : 891731 : auto mark = e*4;
234 [ + + ]: 4458655 : for (std::size_t f=0; f<4; ++f) // for all tet faces
235 [ + + ]: 3566924 : if (esuel[mark+f] == -1) { // if face has no outside-neighbor tet
236 : : // if does not exist among the internal and physical boundary faces,
237 : : // store as a potential chare-boundary face
238 : 379396 : tk::UnsMesh::Face t{{ gid[ m_inpoel[ mark + tk::lpofa[f][0] ] ],
239 : 379396 : gid[ m_inpoel[ mark + tk::lpofa[f][1] ] ],
240 : 379396 : gid[ m_inpoel[ mark + tk::lpofa[f][2] ] ] }};
241 [ + + ]: 379396 : if (m_ipface.find(t) == end(m_ipface)) {
242 : : Assert( m_expChBndFace.insert(t).second,
243 : : "Store expected chare-boundary face" );
244 : : potbndface.insert( t );
245 : : }
246 : : }
247 : : }
248 : :
249 [ - + ][ - - ]: 1026 : if ( g_inputdeck.get< tag::cmd, tag::feedback >() ) d->Tr().chbndface();
250 : :
251 : : // In the following we assume that the size of the (potential) boundary-face
252 : : // adjacency map above does not necessarily equal to that of the node
253 : : // adjacency map. This is because while a node can be shared at a single
254 : : // corner or along an edge, that does not necessarily share a face as well
255 : : // (in other words, shared nodes or edges can exist that are not part of a
256 : : // shared face). So the chares we communicate with across faces are not
257 : : // necessarily the same as the chares we would communicate nodes with.
258 : : //
259 : : // Since the sizes of the node and face adjacency maps are not the same, while
260 : : // sending the faces on chare boundaries would be okay, however, the receiver
261 : : // would not necessarily know how many chares it must receive from. To solve
262 : : // this problem we send to chares which we share at least a single node with,
263 : : // i.e., rely on the node-adjacency map. Note that to all chares we share at
264 : : // least a single node with we send all our potential chare-boundary faces.
265 : : // This is the same list of faces to all chares we send.
266 : : //
267 : : // Another underlying assumption here is, of course, that the size of the face
268 : : // adjacency map is always smaller than or equal to that of the node adjacency
269 : : // map, which is always true. Since the receive side already knows how many
270 : : // fellow chares it must receive shared node ids from, we use that to detect
271 : : // completion of the number of receives in comfac(). This simplifies the
272 : : // communication pattern and code.
273 : :
274 : : // Send sets of faces adjacent to chare boundaries to fellow workers (if any)
275 [ + + ]: 1026 : if (d->NodeCommMap().empty()) // in serial, skip setting up ghosts altogether
276 [ + - ]: 43 : faceAdj();
277 : : else
278 : : // for all chares we share nodes with
279 [ + + ]: 9211 : for (const auto& c : d->NodeCommMap()) {
280 [ + - ][ + - ]: 16456 : thisProxy[ c.first ].comfac( thisIndex, potbndface );
281 : : }
282 : :
283 [ + - ]: 1026 : ownfac_complete();
284 : 1026 : }
285 : :
286 : : bool
287 : 0 : DG::leakyAdjacency()
288 : : // *****************************************************************************
289 : : // Perform leak-test on chare boundary faces
290 : : //! \details This function computes a surface integral over the boundary of the
291 : : //! faces after the face adjacency communication map is completed. A non-zero
292 : : //! vector result indicates a leak, e.g., a hole in the partition (covered by
293 : : //! the faces of the face adjacency communication map), which indicates an
294 : : //! error upstream in the code that sets up the face communication data
295 : : //! structures.
296 : : //! \note Compared to tk::leakyPartition() this function performs the leak-test
297 : : //! on the face geometry data structure enlarged by ghost faces on this
298 : : //! partition by computing a discrete surface integral considering the
299 : : //! physical and chare boundary faces, which should be equal to zero for a
300 : : //! closed domain.
301 : : //! \return True if our chare face adjacency leaks.
302 : : // *****************************************************************************
303 : : {
304 : : // Storage for surface integral over our chunk of the adjacency
305 : : std::array< tk::real, 3 > s{{ 0.0, 0.0, 0.0 }};
306 : :
307 : : // physical boundary faces
308 [ - - ]: 0 : for (std::size_t f=0; f<m_fd.Nbfac(); ++f) {
309 : 0 : s[0] += m_geoFace(f,0,0) * m_geoFace(f,1,0);
310 : 0 : s[1] += m_geoFace(f,0,0) * m_geoFace(f,2,0);
311 : 0 : s[2] += m_geoFace(f,0,0) * m_geoFace(f,3,0);
312 : : }
313 : :
314 : : // chare-boundary faces
315 [ - - ]: 0 : for (std::size_t f=m_fd.Nipfac(); f<m_fd.Esuf().size()/2; ++f) {
316 : 0 : s[0] += m_geoFace(f,0,0) * m_geoFace(f,1,0);
317 : 0 : s[1] += m_geoFace(f,0,0) * m_geoFace(f,2,0);
318 : 0 : s[2] += m_geoFace(f,0,0) * m_geoFace(f,3,0);
319 : : }
320 : :
321 : : auto eps = std::numeric_limits< tk::real >::epsilon() * 100;
322 [ - - ][ - - ]: 0 : return std::abs(s[0]) > eps || std::abs(s[1]) > eps || std::abs(s[2]) > eps;
[ - - ]
323 : : }
324 : :
325 : : bool
326 : 0 : DG::faceMatch()
327 : : // *****************************************************************************
328 : : // Check if esuf of chare-boundary faces matches
329 : : //! \details This function checks each chare-boundary esuf entry for the left
330 : : //! and right elements. Then, it tries to match all vertices of these
331 : : //! elements. Exactly three of these vertices must match if the esuf entry
332 : : //! has been updated correctly at chare-boundaries.
333 : : //! \return True if chare-boundary faces match.
334 : : // *****************************************************************************
335 : : {
336 : : const auto& esuf = m_fd.Esuf();
337 : : bool match(true);
338 : :
339 : : auto eps = std::numeric_limits< tk::real >::epsilon() * 100;
340 : :
341 [ - - ]: 0 : for (auto f=m_fd.Nipfac(); f<esuf.size()/2; ++f)
342 : : {
343 : 0 : std::size_t el = static_cast< std::size_t >(esuf[2*f]);
344 : 0 : std::size_t er = static_cast< std::size_t >(esuf[2*f+1]);
345 : :
346 : : std::size_t count = 0;
347 : :
348 [ - - ]: 0 : for (std::size_t i=0; i<4; ++i)
349 : : {
350 : 0 : auto ip = m_inpoel[4*el+i];
351 [ - - ]: 0 : for (std::size_t j=0; j<4; ++j)
352 : : {
353 [ - - ]: 0 : auto jp = m_inpoel[4*er+j];
354 [ - - ]: 0 : auto xdiff = std::abs( m_coord[0][ip] - m_coord[0][jp] );
355 : 0 : auto ydiff = std::abs( m_coord[1][ip] - m_coord[1][jp] );
356 : 0 : auto zdiff = std::abs( m_coord[2][ip] - m_coord[2][jp] );
357 : :
358 [ - - ][ - - ]: 0 : if ( xdiff<=eps && ydiff<=eps && zdiff<=eps ) ++count;
[ - - ]
359 : : }
360 : : }
361 : :
362 : 0 : match = (match && count == 3);
363 : : }
364 : :
365 : 0 : return match;
366 : : }
367 : :
368 : : void
369 : 8228 : DG::comfac( int fromch, const tk::UnsMesh::FaceSet& infaces )
370 : : // *****************************************************************************
371 : : // Receive unique set of faces we potentially share with/from another chare
372 : : //! \param[in] fromch Sender chare id
373 : : //! \param[in] infaces Unique set of faces we potentially share with fromch
374 : : // *****************************************************************************
375 : : {
376 [ + + ]: 8228 : if (g_inputdeck.get< tag::cmd, tag::chare >() ||
377 [ + + ]: 7762 : g_inputdeck.get< tag::cmd, tag::quiescence >())
378 [ + - ][ + - ]: 9680 : stateProxy.ckLocalBranch()->insert( "DG", thisIndex, CkMyPe(), Disc()->It(),
[ + - ][ + - ]
[ - + ]
379 : : "comfac" );
380 : :
381 : : // Buffer up incoming data
382 : : m_infaces[ fromch ] = infaces;
383 : :
384 : : // if we have heard from all fellow chares that we share at least a single
385 : : // node, edge, or face with
386 [ + + ]: 8228 : if (++m_ncomfac == Disc()->NodeCommMap().size()) {
387 : 983 : m_ncomfac = 0;
388 : 983 : comfac_complete();
389 : : }
390 : 8228 : }
391 : :
392 : : void
393 : 983 : DG::bndFaces()
394 : : // *****************************************************************************
395 : : // Compute chare-boundary faces
396 : : //! \details This is called when both send and receives are completed on a
397 : : //! chare and thus we are ready to compute chare-boundary faces and ghost data.
398 : : // *****************************************************************************
399 : : {
400 : 983 : auto d = Disc();
401 [ - + ]: 983 : if ( g_inputdeck.get< tag::cmd, tag::feedback >() ) d->Tr().chcomfac();
402 : : const auto& esuel = m_fd.Esuel();
403 : 983 : const auto& gid = d->Gid();
404 : :
405 [ + + ]: 9211 : for (const auto& in : m_infaces) {
406 : : // Find sender chare among chares we potentially share faces with. Note that
407 : : // it is feasible that a sender chare called us but we do not have a set of
408 : : // faces associated to that chare. This can happen if we only share a single
409 : : // node or an edge but not a face with that chare.
410 : 8228 : auto& bndface = m_bndFace[ in.first ]; // will associate to sender chare
411 : : // Try to find incoming faces on our chare boundary with other chares. If
412 : : // found, generate and assign new local face ID, associated to sender chare.
413 [ + + ]: 3886374 : for (std::size_t e=0; e<esuel.size()/4; ++e) { // for all our tets
414 : 3878146 : auto mark = e*4;
415 [ + + ]: 19390730 : for (std::size_t f=0; f<4; ++f) { // for all cell faces
416 [ + + ]: 15512584 : if (esuel[mark+f] == -1) { // if face has no outside-neighbor tet
417 : 2014270 : tk::UnsMesh::Face t{{ gid[ m_inpoel[ mark + tk::lpofa[f][0] ] ],
418 : 2014270 : gid[ m_inpoel[ mark + tk::lpofa[f][1] ] ],
419 : 2014270 : gid[ m_inpoel[ mark + tk::lpofa[f][2] ] ] }};
420 : : // if found among the incoming faces and if not one of our internal
421 : : // nor physical boundary faces
422 [ + + ][ + - ]: 2150500 : if ( in.second.find(t) != end(in.second) &&
423 : : m_ipface.find(t) == end(m_ipface) ) {
424 : 136230 : bndface[t][0] = m_nfac++; // assign new local face ID
425 : : }
426 : : }
427 : : }
428 : : }
429 : : // If at this point if we have not found any face among our faces we
430 : : // potentially share with fromch, there is no need to keep an empty set of
431 : : // faces associated to fromch as we only share nodes or edges with it, but
432 : : // not faces.
433 [ + + ]: 8228 : if (bndface.empty()) m_bndFace.erase( in.first );
434 : : }
435 : :
436 : 983 : tk::destroy(m_ipface);
437 : 983 : tk::destroy(m_infaces);
438 : :
439 : : // Ensure all expected faces have been received
440 : : Assert( receivedChBndFaces(),
441 : : "Expected and received chare boundary faces mismatch" );
442 : :
443 : : // Basic error checking on chare-boundary-face map
444 : : Assert( m_bndFace.find( thisIndex ) == m_bndFace.cend(),
445 : : "Face-communication map should not contain data for own chare ID" );
446 : :
447 : : // Store (local) tet ID adjacent to our chare boundary from the inside
448 [ + + ]: 683566 : for (std::size_t e=0; e<esuel.size()/4; ++e) { // for all our tets
449 : 682583 : auto mark = e*4;
450 [ + + ]: 3412915 : for (std::size_t f=0; f<4; ++f) { // for all cell faces
451 [ + + ]: 2730332 : if (esuel[mark+f] == -1) { // if face has no outside-neighbor tet
452 : 309050 : tk::UnsMesh::Face t{{ gid[ m_inpoel[ mark + tk::lpofa[f][0] ] ],
453 : 309050 : gid[ m_inpoel[ mark + tk::lpofa[f][1] ] ],
454 : 309050 : gid[ m_inpoel[ mark + tk::lpofa[f][2] ] ] }};
455 : 309050 : auto c = findchare( t );
456 [ + + ]: 309050 : if (c > -1) {
457 : : auto& lbndface = tk::ref_find( m_bndFace, c );
458 : : auto& face = tk::ref_find( lbndface, t );
459 : 136230 : face[1] = e; // store (local) inner tet ID adjacent to face
460 : : }
461 : : }
462 : : }
463 : : }
464 : :
465 : : // At this point m_bndFace is complete on this PE. This means that starting
466 : : // from the sets of faces we potentially share with fellow chares we now
467 : : // only have those faces we actually share faces with (through which we need
468 : : // to communicate later). Also, m_bndFace not only has the unique faces
469 : : // associated to fellow chares, but also a newly assigned local face ID as
470 : : // well as the local id of the inner tet adjacent to the face. Continue by
471 : : // starting setting up ghost data
472 : 983 : setupGhost();
473 : : // Besides setting up our own ghost data, we also issue requests (for ghost
474 : : // data) to those chares which we share faces with. Note that similar to
475 : : // comfac() we are calling reqGhost() by going through the node communication
476 : : // map instead, which may send requests to those chare we do not share faces
477 : : // with. This is so that we can test for completing by querying the size of
478 : : // the already complete node commincation map in reqGhost. Requests in
479 : : // sendGhost will only be fullfilled based on m_ghostData.
480 [ + + ]: 9211 : for (const auto& c : d->NodeCommMap()) // for all chares we share nodes with
481 [ + - ]: 16456 : thisProxy[ c.first ].reqGhost();
482 : 983 : }
483 : :
484 : : bool
485 : 0 : DG::receivedChBndFaces()
486 : : // *****************************************************************************
487 : : // Verify that all chare-boundary faces have been received
488 : : //! \return True if all chare-boundary faces have been received
489 : : // *****************************************************************************
490 : : {
491 : 0 : auto d = Disc();
492 : : tk::UnsMesh::FaceSet recvBndFace;
493 : :
494 : : // Collect chare-boundary faces that have been received and expected
495 [ - - ]: 0 : for (const auto& c : m_bndFace)
496 [ - - ]: 0 : for (const auto& f : c.second)
497 [ - - ]: 0 : if (m_expChBndFace.find(f.first) != end(m_expChBndFace))
498 : : recvBndFace.insert(f.first);
499 : :
500 : : // Collect info on expected but not received faces
501 [ - - ]: 0 : std::stringstream msg;
502 [ - - ]: 0 : for (const auto& f : m_expChBndFace)
503 [ - - ]: 0 : if (recvBndFace.find(f) == end(recvBndFace)) {
504 : : const auto& x = m_coord[0];
505 : : const auto& y = m_coord[1];
506 : : const auto& z = m_coord[2];
507 : 0 : auto A = tk::cref_find( d->Lid(), f[0] );
508 : 0 : auto B = tk::cref_find( d->Lid(), f[1] );
509 [ - - ]: 0 : auto C = tk::cref_find( d->Lid(), f[2] );
510 [ - - ][ - - ]: 0 : msg << '{' << A << ',' << B << ',' << C << "}:("
[ - - ]
511 [ - - ][ - - ]: 0 : << x[A] << ',' << y[A] << ',' << z[A] << ' '
[ - - ]
512 [ - - ][ - - ]: 0 : << x[B] << ',' << y[B] << ',' << z[B] << ' '
[ - - ]
513 [ - - ][ - - ]: 0 : << x[C] << ',' << y[C] << ',' << z[C] << ") ";
[ - - ][ - - ]
514 : : }
515 : :
516 : 0 : tk::destroy( m_expChBndFace );
517 : :
518 : : // Error out with info on missing faces
519 : : auto s = msg.str();
520 [ - - ]: 0 : if (!s.empty()) {
521 [ - - ][ - - ]: 0 : Throw( "DG chare " + std::to_string(thisIndex) +
[ - - ][ - - ]
[ - - ][ - - ]
[ - - ][ - - ]
[ - - ][ - - ]
[ - - ][ - - ]
[ - - ][ - - ]
522 : : " missing face(s) {local node ids} (node coords): " + s );
523 : : } else {
524 : 0 : return true;
525 : : }
526 : : }
527 : :
528 : : void
529 : 983 : DG::setupGhost()
530 : : // *****************************************************************************
531 : : // Setup own ghost data on this chare
532 : : // *****************************************************************************
533 : : {
534 : 983 : auto d = Disc();
535 : 983 : const auto& gid = d->Gid();
536 : :
537 : : // Enlarge elements surrounding faces data structure for ghosts
538 : 983 : m_fd.Esuf().resize( 2*m_nfac, -2 );
539 : 983 : m_fd.Inpofa().resize( 3*m_nfac, 0 );
540 : : // Enlarge face geometry data structure for ghosts
541 : 983 : m_geoFace.resize( m_nfac, 0.0 );
542 : :
543 : : const auto& esuel = m_fd.Esuel();
544 : :
545 : : // Collect tet ids, their face connectivity (given by 3 global node IDs, each
546 : : // triplet for potentially multiple faces on the chare boundary), and their
547 : : // elem geometry data (see GhostData) associated to fellow chares adjacent to
548 : : // chare boundaries. Once received by fellow chares, these tets will become
549 : : // known as ghost elements and their data as ghost data.
550 [ + + ]: 683566 : for (std::size_t e=0; e<esuel.size()/4; ++e) { // for all our tets
551 : 682583 : auto mark = e*4;
552 [ + + ]: 3412915 : for (std::size_t f=0; f<4; ++f) { // for all cell faces
553 [ + + ]: 2730332 : if (esuel[mark+f] == -1) { // if face has no outside-neighbor tet
554 [ + - ]: 309050 : tk::UnsMesh::Face t{{ gid[ m_inpoel[ mark + tk::lpofa[f][0] ] ],
555 : 309050 : gid[ m_inpoel[ mark + tk::lpofa[f][1] ] ],
556 : 309050 : gid[ m_inpoel[ mark + tk::lpofa[f][2] ] ] }};
557 [ + - ]: 309050 : auto c = findchare( t );
558 : : // It is possible that we do not find the chare for this face. We are
559 : : // looping through all of our tets and interrogating all faces that do
560 : : // not have neighboring tets but we only care about chare-boundary faces
561 : : // here as only those need ghost data. (esuel may also contain
562 : : // physical boundary faces)
563 [ + + ]: 309050 : if (c > -1) {
564 : : // Will store ghost data associated to neighbor chare
565 : : auto& ghost = m_ghostData[ c ];
566 : : // Store tet id adjacent to chare boundary as key for ghost data
567 : : auto& tuple = ghost[ e ];
568 : : // If tetid e has not yet been encountered, store geometry (only once)
569 : : auto& nodes = std::get< 0 >( tuple );
570 [ + + ]: 136230 : if (nodes.empty()) {
571 [ + - ]: 106332 : std::get< 1 >( tuple ) = m_geoElem[ e ];
572 : :
573 : : auto& ncoord = std::get< 2 >( tuple );
574 : 106332 : ncoord[0] = m_coord[0][ m_inpoel[ mark+f ] ];
575 : 106332 : ncoord[1] = m_coord[1][ m_inpoel[ mark+f ] ];
576 : 106332 : ncoord[2] = m_coord[2][ m_inpoel[ mark+f ] ];
577 : :
578 : 106332 : std::get< 3 >( tuple ) = f;
579 : :
580 : 106332 : std::get< 4 >( tuple ) = {{ gid[ m_inpoel[ mark ] ],
581 : 106332 : gid[ m_inpoel[ mark+1 ] ],
582 : 106332 : gid[ m_inpoel[ mark+2 ] ],
583 : 106332 : gid[ m_inpoel[ mark+3 ] ] }};
584 : : }
585 : : // (Always) store face node IDs on chare boundary, even if tetid e has
586 : : // already been stored. Thus we store potentially multiple faces along
587 : : // the same chare-boundary. This happens, e.g., when the boundary
588 : : // between chares is zig-zaggy enough to have 2 or even 3 faces of the
589 : : // same tet.
590 [ + - ]: 136230 : nodes.push_back( t[0] );
591 [ + - ]: 136230 : nodes.push_back( t[1] );
592 [ + - ]: 136230 : nodes.push_back( t[2] );
593 : : Assert( nodes.size() <= 4*3, "Overflow of faces/tet to send" );
594 : : }
595 : : }
596 : : }
597 : : }
598 : :
599 : : // Basic error checking on local ghost data
600 : : Assert( m_ghostData.find( thisIndex ) == m_ghostData.cend(),
601 : : "Chare-node adjacency map should not contain data for own chare ID" );
602 : :
603 : : // More in-depth error checking on local ghost data
604 [ + + ]: 6165 : for (const auto& c : m_ghostData)
605 [ + + ]: 111514 : for ([[maybe_unused]] const auto& t : c.second) {
606 : : Assert( !std::get< 0 >( t.second ).empty(),
607 : : "Emtpy face vector in ghost data" );
608 : : Assert( std::get< 0 >( t.second ).size() % 3 == 0,
609 : : "Face node IDs must be triplets" );
610 : : Assert( std::get< 0 >( t.second ).size() <= 4*3, // <= 4*3 (4*numfaces)
611 : : "Max number of faces for a single ghost tet is 4" );
612 : : Assert( !std::get< 1 >( t.second ).empty(),
613 : : "No elem geometry data for ghost" );
614 : : Assert( std::get< 1 >( t.second ).size() == m_geoElem.nprop(),
615 : : "Elem geometry data for ghost must be for single tet" );
616 : : Assert( !std::get< 2 >( t.second ).empty(),
617 : : "No nodal coordinate data for ghost" );
618 : : }
619 : :
620 : 983 : ownghost_complete();
621 : 983 : }
622 : :
623 : : void
624 : 8228 : DG::reqGhost()
625 : : // *****************************************************************************
626 : : // Receive requests for ghost data
627 : : // *****************************************************************************
628 : : {
629 [ + + ]: 8228 : if (g_inputdeck.get< tag::cmd, tag::chare >() ||
630 [ + + ]: 7762 : g_inputdeck.get< tag::cmd, tag::quiescence >())
631 [ + - ][ + - ]: 9680 : stateProxy.ckLocalBranch()->insert( "DG", thisIndex, CkMyPe(), Disc()->It(),
[ + - ][ + - ]
[ - + ]
632 : : "reqGhost" );
633 : :
634 : : // If every chare we communicate with has requested ghost data from us, we may
635 : : // fulfill the requests, but only if we have already setup our ghost data.
636 [ + + ]: 8228 : if (++m_ghostReq == Disc()->NodeCommMap().size()) {
637 : 983 : m_ghostReq = 0;
638 : 983 : reqghost_complete();
639 : : }
640 : 8228 : }
641 : :
642 : : void
643 : 983 : DG::sendGhost()
644 : : // *****************************************************************************
645 : : // Send all of our ghost data to fellow chares
646 : : // *****************************************************************************
647 : : {
648 [ + + ]: 983 : if (g_inputdeck.get< tag::cmd, tag::chare >() ||
649 [ + + ]: 945 : g_inputdeck.get< tag::cmd, tag::quiescence >())
650 [ + - ][ + - ]: 1110 : stateProxy.ckLocalBranch()->insert( "DG", thisIndex, CkMyPe(), Disc()->It(),
[ + - ][ + - ]
[ - + ]
651 : : "sendGhost" );
652 : :
653 [ + + ]: 6165 : for (const auto& c : m_ghostData)
654 [ + - ]: 10364 : thisProxy[ c.first ].comGhost( thisIndex, c.second );
655 : :
656 [ - + ]: 983 : if ( g_inputdeck.get< tag::cmd, tag::feedback >() ) Disc()->Tr().chghost();
657 : 983 : }
658 : :
659 : : int
660 : 618100 : DG::findchare( const tk::UnsMesh::Face& t )
661 : : // *****************************************************************************
662 : : // Find any chare for face (given by 3 global node IDs)
663 : : //! \param[in] t Face given by three global node IDs
664 : : //! \return Chare ID if found among any of the chares we communicate along
665 : : //! faces with, -1 if the face cannot be found.
666 : : // *****************************************************************************
667 : : {
668 [ + + ]: 2721284 : for (const auto& cf : m_bndFace)
669 : : // cppcheck-suppress useStlAlgorithm
670 [ + + ]: 2375644 : if (cf.second.find(t) != end(cf.second))
671 : 272460 : return cf.first;
672 : : return -1;
673 : : }
674 : :
675 : : void
676 : 5182 : DG::comGhost( int fromch, const GhostData& ghost )
677 : : // *****************************************************************************
678 : : // Receive ghost data on chare boundaries from fellow chare
679 : : //! \param[in] fromch Caller chare ID
680 : : //! \param[in] ghost Ghost data, see Inciter/FaceData.h for the type
681 : : // *****************************************************************************
682 : : {
683 [ + + ]: 5182 : if (g_inputdeck.get< tag::cmd, tag::chare >() ||
684 [ + + ]: 4942 : g_inputdeck.get< tag::cmd, tag::quiescence >())
685 [ + - ][ + - ]: 5760 : stateProxy.ckLocalBranch()->insert( "DG", thisIndex, CkMyPe(), Disc()->It(),
[ + - ][ + - ]
[ - + ]
686 : : "comGhost" );
687 : :
688 : 5182 : auto d = Disc();
689 : 5182 : const auto& lid = d->Lid();
690 : : auto& inpofa = m_fd.Inpofa();
691 : 5182 : auto ncoord = m_coord[0].size();
692 : :
693 : : // nodelist with fromch, currently only used for an assert
694 : : [[maybe_unused]] const auto& nl = tk::cref_find( d->NodeCommMap(), fromch );
695 : :
696 : : auto& ghostelem = m_ghost[ fromch ]; // will associate to sender chare
697 : :
698 : : // Store ghost data coming from chare
699 [ + + ]: 111514 : for (const auto& g : ghost) { // loop over incoming ghost data
700 : 106332 : auto e = g.first; // remote/ghost tet id outside of chare boundary
701 : : const auto& nodes = std::get< 0 >( g.second ); // node IDs of face(s)
702 : : const auto& geo = std::get< 1 >( g.second ); // ghost elem geometry data
703 : : const auto& coordg = std::get< 2 >( g.second ); // coordinate of ghost node
704 : : const auto& inpoelg = std::get< 4 >( g.second ); // inpoel of ghost tet
705 : :
706 : : Assert( nodes.size() % 3 == 0, "Face node IDs must be triplets" );
707 : : Assert( nodes.size() <= 4*3, "Overflow of faces/tet received" );
708 : : Assert( geo.size() % 5 == 0, "Ghost geometry size mismatch" );
709 : : Assert( geo.size() == m_geoElem.nprop(), "Ghost geometry number mismatch" );
710 : : Assert( coordg.size() == 3, "Incorrect ghost node coordinate size" );
711 : : Assert( inpoelg.size() == 4, "Incorrect ghost inpoel size" );
712 : :
713 [ + + ]: 242562 : for (std::size_t n=0; n<nodes.size()/3; ++n) { // face(s) of ghost e
714 : : // node IDs of face on chare boundary
715 : 136230 : tk::UnsMesh::Face t{{ nodes[n*3+0], nodes[n*3+1], nodes[n*3+2] }};
716 : : // must find t in nodelist of chare-boundary adjacent to fromch
717 : : Assert( nl.find(t[0]) != end(nl) &&
718 : : nl.find(t[1]) != end(nl) &&
719 : : nl.find(t[2]) != end(nl),
720 : : "Ghost face not found in chare-node adjacency map on receiving end" );
721 : : // must find face in boundary-face adjacency map for fromch
722 : : Assert( tk::cref_find(m_bndFace,fromch).find( t ) !=
723 : : tk::cref_find(m_bndFace,fromch).cend(), "Ghost face not "
724 : : "found in boundary-face adjacency map on receiving end" );
725 : : // find local face & tet ids for t
726 : 136230 : auto id = tk::cref_find( tk::cref_find(m_bndFace,fromch), t );
727 : : // compute face geometry for chare-boundary face
728 [ + - ]: 136230 : addGeoFace( t, id );
729 : : // add node-triplet to node-face connectivity
730 : 136230 : inpofa[3*id[0]+0] = tk::cref_find( lid, t[2] );
731 : 136230 : inpofa[3*id[0]+1] = tk::cref_find( lid, t[1] );
732 : 136230 : inpofa[3*id[0]+2] = tk::cref_find( lid, t[0] );
733 : :
734 : : // if ghost tet id not yet encountered on boundary with fromch
735 : : auto i = ghostelem.find( e );
736 [ + + ]: 136230 : if (i != end(ghostelem)) {
737 [ + - ]: 29898 : addEsuf( id, i->second ); // fill in elements surrounding face
738 [ + - ]: 29898 : addEsuel( id, i->second, t ); // fill in elements surrounding element
739 : : } else {
740 [ + - ]: 106332 : addEsuf( id, m_nunk ); // fill in elements surrounding face
741 [ + - ]: 106332 : addEsuel( id, m_nunk, t ); // fill in elements surrounding element
742 [ + - ]: 106332 : ghostelem[e] = m_nunk; // assign new local tet id to remote ghost id
743 [ + - ]: 106332 : m_geoElem.push_back( geo );// store ghost elem geometry
744 : 106332 : ++m_nunk; // increase number of unknowns on this chare
745 : : std::size_t counter = 0;
746 [ + + ]: 531660 : for (std::size_t gp=0; gp<4; ++gp) {
747 : : auto it = lid.find( inpoelg[gp] );
748 : : std::size_t lp;
749 [ + + ]: 425328 : if (it != end(lid))
750 : 357378 : lp = it->second;
751 : : else {
752 : : Assert( nodes.size() == 3, "Expected node not found in lid" );
753 : : Assert( gp == std::get< 3 >( g.second ),
754 : : "Ghost node not matching correct entry in ghost inpoel" );
755 : 67950 : lp = ncoord;
756 : 67950 : ++counter;
757 : : }
758 [ + - ]: 425328 : m_inpoel.push_back( lp ); // store ghost element connectivity
759 : : }
760 : : // only a single or no ghost node should be found
761 : : Assert( counter <= 1, "Incorrect number of ghost nodes detected. "
762 : : "Detected "+ std::to_string(counter) +" ghost nodes" );
763 [ + + ]: 106332 : if (counter == 1) {
764 [ + - ]: 67950 : m_coord[0].push_back( coordg[0] ); // store ghost node coordinate
765 [ + - ]: 67950 : m_coord[1].push_back( coordg[1] );
766 [ + - ]: 67950 : m_coord[2].push_back( coordg[2] );
767 : : Assert( m_inpoel[ 4*(m_nunk-1)+std::get< 3 >( g.second ) ] == ncoord,
768 : : "Mismatch in extended inpoel for ghost element" );
769 : 67950 : ++ncoord; // increase number of nodes on this chare
770 : : }
771 : : }
772 : :
773 : : // additional tests to ensure that entries in inpoel and t/inpofa match
774 : : Assert( nodetripletMatch(id, t) == 3, "Mismatch/Overmatch in inpoel and "
775 : : "inpofa at chare-boundary face" );
776 : : }
777 : : }
778 : :
779 : : // Signal the runtime system that all workers have received their
780 : : // face-adjacency
781 [ + + ]: 5182 : if (++m_nadj == m_ghostData.size()) faceAdj();
782 : 5182 : }
783 : :
784 : : std::size_t
785 : 0 : DG::nodetripletMatch( const std::array< std::size_t, 2 >& id,
786 : : const tk::UnsMesh::Face& t )
787 : : // *****************************************************************************
788 : : // Check if entries in inpoel, inpofa and node-triplet are consistent
789 : : //! \param[in] id Local face and (inner) tet id adjacent to it
790 : : //! \param[in] t node-triplet associated with the chare boundary face
791 : : //! \return number of nodes in inpoel that matched with t and inpofa
792 : : // *****************************************************************************
793 : : {
794 : 0 : const auto& lid = Disc()->Lid();
795 : : const auto& esuf = m_fd.Esuf();
796 : : const auto& inpofa = m_fd.Inpofa();
797 : :
798 : : std::size_t counter = 0;
799 [ - - ]: 0 : for (std::size_t k=0; k<4; ++k)
800 : : {
801 : 0 : auto el = esuf[ 2*id[0] ];
802 : 0 : auto ip = m_inpoel[ 4*static_cast< std::size_t >( el )+k ];
803 : : Assert( el == static_cast< int >( id[1] ), "Mismatch in id and esuf" );
804 [ - - ]: 0 : for (std::size_t j=0; j<3; ++j)
805 : : {
806 : 0 : auto jp = tk::cref_find( lid, t[j] );
807 [ - - ]: 0 : auto fp = inpofa[ 3*id[0]+(2-j) ];
808 [ - - ]: 0 : if (ip == jp && ip == fp) ++counter;
809 : : }
810 : : }
811 : :
812 : 0 : return counter;
813 : : }
814 : :
815 : : void
816 : 136230 : DG::addEsuf( const std::array< std::size_t, 2 >& id, std::size_t ghostid )
817 : : // *****************************************************************************
818 : : // Fill elements surrounding a face along chare boundary
819 : : //! \param[in] id Local face and (inner) tet id adjacent to it
820 : : //! \param[in] ghostid Local ID for ghost tet
821 : : //! \details This function extends and fills in the elements surrounding faces
822 : : //! data structure (esuf) so that the left and right element id is filled
823 : : //! in correctly on chare boundaries to contain the correct inner tet id and
824 : : //! the local tet id for the outer (ghost) tet, both adjacent to the given
825 : : //1 chare-face boundary. Prior to this function, this data structure does not
826 : : //! have yet face-element connectivity adjacent to chare-boundary faces, only
827 : : //! for physical boundaries and internal faces that are not on the chare
828 : : //! boundary (this latter purely as a result of mesh partitioning). The remote
829 : : //! element id of the ghost is stored in a location that is local to our own
830 : : //! esuf. The face numbering is such that esuf stores the element-face
831 : : //! connectivity first for the physical-boundary faces, followed by that of
832 : : //! the internal faces, followed by the chare-boundary faces. As a result,
833 : : //! esuf can be used by physics algorithms in exactly the same way as would be
834 : : //! used in serial. In serial, of course, this data structure is not extended
835 : : //! at the end by the chare-boundaries.
836 : : // *****************************************************************************
837 : : {
838 : : auto& esuf = m_fd.Esuf();
839 : : Assert( 2*id[0]+1 < esuf.size(), "Indexing out of esuf" );
840 : :
841 : : // put in inner tet id
842 : : Assert( esuf[ 2*id[0] ] == -2 && esuf[ 2*id[0]+1 ] == -2, "Updating esuf at "
843 : : "wrong location instead of chare-boundary" );
844 : 136230 : esuf[ 2*id[0]+0 ] = static_cast< int >( id[1] );
845 : : // put in local id for outer/ghost tet
846 : 136230 : esuf[ 2*id[0]+1 ] = static_cast< int >( ghostid );
847 : 136230 : }
848 : :
849 : : void
850 : 136230 : DG::addEsuel( const std::array< std::size_t, 2 >& id,
851 : : std::size_t ghostid,
852 : : const tk::UnsMesh::Face& t )
853 : : // *****************************************************************************
854 : : // Fill elements surrounding a element along chare boundary
855 : : //! \param[in] id Local face and (inner) tet id adjacent to it
856 : : //! \param[in] ghostid Local ID for ghost tet
857 : : //! \param[in] t node-triplet associated with the chare boundary face
858 : : //! \details This function updates the elements surrounding element (esuel) data
859 : : // structure for the (inner) tets adjacent to the chare-boundaries. It fills
860 : : // esuel of this inner tet with the local tet-id that has been assigned to
861 : : // the outer ghost tet in DG::comGhost in place of the -1 before.
862 : : // *****************************************************************************
863 : : {
864 : 136230 : auto d = Disc();
865 : : [[maybe_unused]] const auto& esuf = m_fd.Esuf();
866 : 136230 : const auto& lid = d->Lid();
867 : :
868 : : std::array< tk::UnsMesh::Face, 4 > face;
869 [ + + ]: 681150 : for (std::size_t f = 0; f<4; ++f)
870 [ + + ]: 2179680 : for (std::size_t i = 0; i<3; ++i)
871 : 1634760 : face[f][i] = m_inpoel[ id[1]*4 + tk::lpofa[f][i] ];
872 : :
873 : 136230 : tk::UnsMesh::Face tl{{ tk::cref_find( lid, t[0] ),
874 : 136230 : tk::cref_find( lid, t[1] ),
875 : 408690 : tk::cref_find( lid, t[2] ) }};
876 : :
877 : : auto& esuel = m_fd.Esuel();
878 : : std::size_t i(0), nmatch(0);
879 [ + + ]: 681150 : for (const auto& f : face) {
880 [ + + ]: 544920 : if (tk::UnsMesh::Eq< 3 >()( tl, f )) {
881 : : Assert( esuel[ id[1]*4 + i ] == -1, "Incorrect boundary element found in "
882 : : "esuel");
883 : 136230 : esuel[ id[1]*4 + i ] = static_cast<int>(ghostid);
884 : : ++nmatch;
885 : : Assert( esuel[ id[1]*4 + i ] == esuf[ 2*id[0]+1 ], "Incorrect boundary "
886 : : "element entered in esuel" );
887 : : Assert( static_cast<int>(id[1]) == esuf[ 2*id[0]+0 ], "Boundary "
888 : : "element entered in incorrect esuel location" );
889 : : }
890 : 544920 : ++i;
891 : : }
892 : :
893 : : // ensure that exactly one face matched
894 : : Assert( nmatch == 1, "Incorrect number of node-triplets (faces) matched for "
895 : : "updating esuel; matching faces = "+ std::to_string(nmatch) );
896 : 136230 : }
897 : :
898 : : void
899 : 136230 : DG::addGeoFace( const tk::UnsMesh::Face& t,
900 : : const std::array< std::size_t, 2 >& id )
901 : : // *****************************************************************************
902 : : // Fill face-geometry data along chare boundary
903 : : //! \param[in] t Face (given by 3 global node IDs) on the chare boundary
904 : : //! \param[in] id Local face and (inner) tet id adjacent to face t
905 : : //! \details This function fills in the face geometry data along a chare
906 : : //! boundary.
907 : : // *****************************************************************************
908 : : {
909 : 136230 : auto d = Disc();
910 : 136230 : const auto& lid = d->Lid();
911 : :
912 : : // get global node IDs reversing order to get outward-pointing normal
913 : 136230 : auto A = tk::cref_find( lid, t[2] );
914 : 136230 : auto B = tk::cref_find( lid, t[1] );
915 : 136230 : auto C = tk::cref_find( lid, t[0] );
916 : 136230 : auto geochf = tk::geoFaceTri( {{m_coord[0][A], m_coord[0][B], m_coord[0][C]}},
917 : 136230 : {{m_coord[1][A], m_coord[1][B], m_coord[1][C]}},
918 : 136230 : {{m_coord[2][A], m_coord[2][B], m_coord[2][C]}} );
919 : :
920 [ + + ]: 1089840 : for (std::size_t i=0; i<7; ++i)
921 : 953610 : m_geoFace(id[0],i,0) = geochf(0,i,0);
922 : 136230 : }
923 : :
924 : : void
925 : 1026 : DG::faceAdj()
926 : : // *****************************************************************************
927 : : // Continue after face adjacency communication map completed on this chare
928 : : //! \details At this point the face communication map has been established
929 : : //! on this chare. Proceed to set up the nodal-comm map.
930 : : // *****************************************************************************
931 : : {
932 : 1026 : m_nadj = 0;
933 : :
934 : 1026 : tk::destroy(m_bndFace);
935 : :
936 : : // Ensure that all elements surrounding faces (are correct) including those at
937 : : // chare boundaries
938 [ + + ]: 1974186 : for (std::size_t f=0; f<m_nfac; ++f) {
939 : : Assert( m_fd.Esuf()[2*f] > -1,
940 : : "Left element in esuf cannot be physical ghost" );
941 : : if (f >= m_fd.Nbfac())
942 : : Assert( m_fd.Esuf()[2*f+1] > -1,
943 : : "Right element in esuf for internal/chare faces cannot be a ghost" );
944 : : }
945 : :
946 : : // Ensure that all elements surrounding elements are correct including those
947 : : // at chare boundaries
948 : : const auto& esuel = m_fd.Esuel();
949 : : std::size_t nbound = 0;
950 [ + + ]: 892757 : for (std::size_t e=0; e<esuel.size()/4; ++e) {
951 : : for (std::size_t f=0; f<4; ++f)
952 : : if (esuel[4*e+f] == -1) ++nbound;
953 : : }
954 : : Assert( nbound == m_fd.Nbfac(), "Incorrect number of ghost-element -1's in "
955 : : "updated esuel" );
956 : :
957 : : // Error checking on ghost data
958 [ + + ]: 6208 : for(const auto& n : m_ghostData)
959 [ + + ]: 111514 : for([[maybe_unused]] const auto& i : n.second)
960 : : Assert( i.first < m_fd.Esuel().size()/4, "Sender contains ghost tet id " );
961 : :
962 : : // Perform leak test on face geometry data structure enlarged by ghosts
963 : : Assert( !leakyAdjacency(), "Face adjacency leaky" );
964 : : Assert( faceMatch(), "Chare-boundary element-face connectivity (esuf) does "
965 : : "not match" );
966 : :
967 : : // Create new map of elements along chare boundary which are ghosts for
968 : : // neighboring chare, associated with that chare ID
969 [ + + ]: 6208 : for (const auto& [cid, cgd] : m_ghostData)
970 : : {
971 : : auto& sg = m_sendGhost[cid];
972 [ + + ]: 111514 : for (const auto& e : cgd)
973 : : {
974 : : Assert(sg.find(e.first) == sg.end(), "Repeating element found in "
975 : : "ghost data");
976 : 106332 : sg.insert(e.first);
977 : : }
978 : : Assert(sg.size() == cgd.size(), "Incorrect size for sendGhost");
979 : : }
980 : : Assert(m_sendGhost.size() == m_ghostData.size(), "Incorrect number of "
981 : : "chares in sendGhost");
982 : :
983 : : // Error checking on ghost data
984 [ + + ]: 6208 : for(const auto& n : m_sendGhost)
985 [ + + ]: 111514 : for([[maybe_unused]] const auto& i : n.second)
986 : : Assert( i < m_fd.Esuel().size()/4, "Sender contains ghost tet id. " );
987 : :
988 : : // Generate and store Esup data-structure in a map
989 : 1026 : auto esup = tk::genEsup(m_inpoel, 4);
990 [ + - ][ + + ]: 254759 : for (std::size_t p=0; p<Disc()->Gid().size(); ++p)
991 : : {
992 [ + + ]: 4178035 : for (auto e : tk::Around(esup, p))
993 : : {
994 : : // since inpoel has been augmented with the face-ghost cell previously,
995 : : // esup also contains cells which are not on this mesh-chunk, hence the
996 : : // following test
997 [ + + ][ + - ]: 3924302 : if (e < m_fd.Esuel().size()/4) m_esup[p].push_back(e);
[ + - ]
998 : : }
999 : : }
1000 : :
1001 : : // Error checking on Esup map
1002 [ + + ]: 254759 : for(const auto& p : m_esup)
1003 : : for([[maybe_unused]] const auto& e : p.second)
1004 : : Assert( e < m_fd.Esuel().size()/4, "Esup contains tet id greater than "
1005 : : + std::to_string(m_fd.Esuel().size()/4-1) +" : "+ std::to_string(e) );
1006 : :
1007 [ + - ]: 1026 : auto meshid = Disc()->MeshId();
1008 [ + - ]: 1026 : contribute( sizeof(std::size_t), &meshid, CkReduction::nop,
1009 [ + - ]: 2052 : CkCallback(CkReductionTarget(Transporter,startEsup), Disc()->Tr()) );
1010 : 1026 : }
1011 : :
1012 : : void
1013 : 1026 : DG::nodeNeighSetup()
1014 : : // *****************************************************************************
1015 : : // Setup node-neighborhood (esup)
1016 : : //! \details At this point the face-ghost communication map has been established
1017 : : //! on this chare. This function begins generating the node-ghost comm map.
1018 : : // *****************************************************************************
1019 : : {
1020 [ + + ]: 1026 : if (Disc()->NodeCommMap().empty())
1021 : : // in serial, skip setting up node-neighborhood
1022 : 43 : { comesup_complete(); }
1023 : : else
1024 : : {
1025 : 983 : const auto& nodeCommMap = Disc()->NodeCommMap();
1026 : :
1027 : : // send out node-neighborhood map
1028 [ + + ]: 9211 : for (const auto& [cid, nlist] : nodeCommMap)
1029 : : {
1030 : : std::unordered_map< std::size_t, std::vector< std::size_t > > bndEsup;
1031 : : std::unordered_map< std::size_t, std::vector< tk::real > > nodeBndCells;
1032 [ + + ]: 133890 : for (const auto& p : nlist)
1033 : : {
1034 [ + - ]: 251324 : auto pl = tk::cref_find(Disc()->Lid(), p);
1035 : : // fill in the esup for the chare-boundary
1036 : : const auto& pesup = tk::cref_find(m_esup, pl);
1037 [ + - ]: 125662 : bndEsup[p] = pesup;
1038 : :
1039 : : // fill a map with the element ids from esup as keys and geoElem as
1040 : : // values, and another map containing these elements associated with
1041 : : // the chare id with which they are node-neighbors.
1042 [ + + ]: 1107729 : for (const auto& e : pesup)
1043 : : {
1044 [ + - ][ + - ]: 1964134 : nodeBndCells[e] = m_geoElem[e];
1045 : :
1046 : : // add these esup-elements into map of elements along chare boundary
1047 : : Assert( e < m_fd.Esuel().size()/4, "Sender contains ghost tet id." );
1048 : : m_sendGhost[cid].insert(e);
1049 : : }
1050 : : }
1051 : :
1052 [ + - ][ + - ]: 16456 : thisProxy[cid].comEsup(thisIndex, bndEsup, nodeBndCells);
1053 : : }
1054 : : }
1055 : :
1056 : 1026 : ownesup_complete();
1057 : 1026 : }
1058 : :
1059 : : void
1060 : 8228 : DG::comEsup( int fromch,
1061 : : const std::unordered_map< std::size_t, std::vector< std::size_t > >& bndEsup,
1062 : : const std::unordered_map< std::size_t, std::vector< tk::real > >&
1063 : : nodeBndCells )
1064 : : // *****************************************************************************
1065 : : //! \brief Receive elements-surrounding-points data-structure for points on
1066 : : // common boundary between receiving and sending neighbor chare, and the
1067 : : // element geometries for these new elements
1068 : : //! \param[in] fromch Sender chare id
1069 : : //! \param[in] bndEsup Elements-surrounding-points data-structure from fromch
1070 : : //! \param[in] nodeBndCells Map containing element geometries associated with
1071 : : //! remote element IDs in the esup
1072 : : // *****************************************************************************
1073 : : {
1074 : : auto& chghost = m_ghost[fromch];
1075 : :
1076 : : // Extend remote-local element id map and element geometry array
1077 [ + + ]: 488199 : for (const auto& e : nodeBndCells)
1078 : : {
1079 : : // need to check following, because 'e' could have been added previously in
1080 : : // remote-local element id map as a part of face-communication, i.e. as a
1081 : : // face-ghost element
1082 [ + + ]: 959942 : if (chghost.find(e.first) == chghost.end())
1083 : : {
1084 : 373639 : chghost[e.first] = m_nunk;
1085 : 373639 : m_geoElem.push_back(e.second);
1086 : 373639 : ++m_nunk;
1087 : : }
1088 : : }
1089 : :
1090 : : // Store incoming data in comm-map buffer for Esup
1091 [ + + ]: 133890 : for (const auto& [node, elist] : bndEsup)
1092 : : {
1093 [ + - ]: 125662 : auto pl = tk::cref_find(Disc()->Lid(), node);
1094 [ + - ]: 125662 : auto& pesup = m_esupc[pl];
1095 [ + + ]: 1107729 : for (auto e : elist)
1096 : : {
1097 : 982067 : auto el = tk::cref_find(chghost, e);
1098 [ + - ]: 982067 : pesup.push_back(el);
1099 : : }
1100 : : }
1101 : :
1102 : : // if we have heard from all fellow chares that we share at least a single
1103 : : // node, edge, or face with
1104 [ + + ]: 8228 : if (++m_ncomEsup == Disc()->NodeCommMap().size()) {
1105 : 983 : m_ncomEsup = 0;
1106 : 983 : comesup_complete();
1107 : : }
1108 : 8228 : }
1109 : :
1110 : : void
1111 : 1026 : DG::adj()
1112 : : // *****************************************************************************
1113 : : // Finish up with adjacency maps, and do a global-sync to begin problem setup
1114 : : //! \details At this point, the nodal- and face-adjacency has been set up. This
1115 : : // function does some error checking on the nodal-adjacency and prepares
1116 : : // for problem setup.
1117 : : // *****************************************************************************
1118 : : {
1119 : : // combine own and communicated contributions to elements surrounding points
1120 [ + + ]: 85882 : for (auto& [p, elist] : m_esupc)
1121 : : {
1122 : : auto& pesup = tk::ref_find(m_esup, p);
1123 : : for ([[maybe_unused]] auto e : elist)
1124 : : {
1125 : : Assert( e >= m_fd.Esuel().size()/4, "Non-ghost element received from "
1126 : : "esup buffer." );
1127 : : }
1128 : 84856 : tk::concat< std::size_t >(std::move(elist), pesup);
1129 : : }
1130 : :
1131 : 1026 : tk::destroy(m_ghostData);
1132 : 1026 : tk::destroy(m_esupc);
1133 : :
1134 [ - + ]: 1026 : if ( g_inputdeck.get< tag::cmd, tag::feedback >() ) Disc()->Tr().chadj();
1135 : :
1136 : : // Error checking on ghost data
1137 [ + + ]: 9254 : for(const auto& n : m_sendGhost)
1138 [ + + ]: 488199 : for([[maybe_unused]] const auto& i : n.second)
1139 : : Assert( i < m_fd.Esuel().size()/4, "Sender contains ghost tet id. ");
1140 : :
1141 : : // Resize solution vectors, lhs and rhs by the number of ghost tets
1142 : 1026 : m_u.resize( m_nunk );
1143 : 1026 : m_un.resize( m_nunk );
1144 : 1026 : m_p.resize( m_nunk );
1145 : 1026 : m_lhs.resize( m_nunk );
1146 : 1026 : m_rhs.resize( m_nunk );
1147 : :
1148 : : // Create a mapping between local ghost tet ids and zero-based boundary ids
1149 : 1026 : std::vector< std::size_t > c( tk::sumvalsize( m_ghost ) );
1150 : : std::size_t j = 0;
1151 [ + + ]: 9254 : for (const auto& n : m_ghost) {
1152 [ + + ]: 488199 : for(const auto& i : n.second) {
1153 : 479971 : c[j++] = i.second;
1154 : : }
1155 : : }
1156 [ + - ]: 2052 : m_bid = tk::assignLid( c );
1157 : :
1158 : : // Size communication buffer that receives number of degrees of freedom
1159 [ + + ][ + - ]: 4104 : for (auto& n : m_ndofc) n.resize( m_bid.size() );
1160 [ + + ][ + - ]: 4104 : for (auto& u : m_uc) u.resize( m_bid.size() );
1161 [ + + ][ + - ]: 4104 : for (auto& p : m_pc) p.resize( m_bid.size() );
1162 : :
1163 : : // Initialize number of degrees of freedom in mesh elements
1164 : 1026 : const auto pref = g_inputdeck.get< tag::pref, tag::pref >();
1165 [ + + ]: 1026 : if( pref )
1166 : : {
1167 : 134 : const auto ndofmax = g_inputdeck.get< tag::pref, tag::ndofmax >();
1168 [ + - ]: 134 : m_ndof.resize( m_nunk, ndofmax );
1169 : : }
1170 : : else
1171 : : {
1172 : 892 : const auto ndof = g_inputdeck.get< tag::discr, tag::ndof >();
1173 [ + - ]: 892 : m_ndof.resize( m_nunk, ndof );
1174 : : }
1175 : :
1176 : : // Ensure that we also have all the geometry and connectivity data
1177 : : // (including those of ghosts)
1178 : : Assert( m_geoElem.nunk() == m_u.nunk(), "GeoElem unknowns size mismatch" );
1179 : :
1180 : : // Basic error checking on ghost tet ID map
1181 : : Assert( m_ghost.find( thisIndex ) == m_ghost.cend(),
1182 : : "Ghost id map should not contain data for own chare ID" );
1183 : :
1184 : : // Store expected ghost tet IDs
1185 [ + + ]: 9254 : for (const auto& n : m_ghost)
1186 [ + + ]: 488199 : for ([[maybe_unused]] const auto& g : n.second)
1187 : : Assert( m_exptGhost.insert( g.second ).second,
1188 : : "Failed to store local tetid as exptected ghost id" );
1189 : :
1190 : : // Signal the runtime system that all workers have received their adjacency
1191 [ + - ][ + - ]: 1026 : std::vector< std::size_t > meshdata{ m_initial, Disc()->MeshId() };
[ - - ]
1192 : 1026 : contribute( meshdata, CkReduction::sum_ulong,
1193 [ + - ][ + - ]: 2052 : CkCallback(CkReductionTarget(Transporter,comfinal), Disc()->Tr()) );
[ - - ]
1194 : 1026 : }
1195 : :
1196 : : void
1197 : 666 : DG::registerReducers()
1198 : : // *****************************************************************************
1199 : : // Configure Charm++ reduction types
1200 : : //! \details Since this is a [initnode] routine, the runtime system executes the
1201 : : //! routine exactly once on every logical node early on in the Charm++ init
1202 : : //! sequence. Must be static as it is called without an object. See also:
1203 : : //! Section "Initializations at Program Startup" at in the Charm++ manual
1204 : : //! http://charm.cs.illinois.edu/manuals/html/charm++/manual.html.
1205 : : // *****************************************************************************
1206 : : {
1207 : 666 : ElemDiagnostics::registerReducers();
1208 : 666 : }
1209 : :
1210 : : void
1211 : 20822 : DG::ResumeFromSync()
1212 : : // *****************************************************************************
1213 : : // Return from migration
1214 : : //! \details This is called when load balancing (LB) completes. The presence of
1215 : : //! this function does not affect whether or not we block on LB.
1216 : : // *****************************************************************************
1217 : : {
1218 [ - + ][ - - ]: 20822 : if (Disc()->It() == 0) Throw( "it = 0 in ResumeFromSync()" );
[ - - ][ - - ]
[ - - ][ - - ]
[ - - ]
1219 : :
1220 [ + - ]: 20822 : if (!g_inputdeck.get< tag::cmd, tag::nonblocking >()) next();
1221 : 20822 : }
1222 : :
1223 : : void
1224 : 918 : DG::setup()
1225 : : // *****************************************************************************
1226 : : // Set initial conditions, generate lhs, output mesh
1227 : : // *****************************************************************************
1228 : : {
1229 [ + + ]: 918 : if (g_inputdeck.get< tag::cmd, tag::chare >() ||
1230 [ + + ]: 880 : g_inputdeck.get< tag::cmd, tag::quiescence >())
1231 [ + - ][ + - ]: 1082 : stateProxy.ckLocalBranch()->insert( "DG", thisIndex, CkMyPe(), Disc()->It(),
[ + - ][ + - ]
[ - + ]
1232 : : "setup" );
1233 : :
1234 : 918 : auto d = Disc();
1235 : :
1236 : : // Basic error checking on sizes of element geometry data and connectivity
1237 : : Assert( m_geoElem.nunk() == m_lhs.nunk(), "Size mismatch in DG::setup()" );
1238 : :
1239 : : // Compute left-hand side of discrete PDEs
1240 : 918 : lhs();
1241 : :
1242 : : // Determine elements inside user-defined IC box
1243 [ + + ]: 1836 : for (auto& eq : g_dgpde)
1244 : 918 : eq.IcBoxElems( m_geoElem, m_fd.Esuel().size()/4, m_boxelems );
1245 : :
1246 : : // Compute volume of user-defined box IC
1247 [ + - ][ - + ]: 918 : d->boxvol( {} ); // punt for now
1248 : :
1249 : : // Query time history field output labels from all PDEs integrated
1250 : : const auto& hist_points = g_inputdeck.get< tag::history, tag::point >();
1251 [ - + ]: 918 : if (!hist_points.empty()) {
1252 : 0 : std::vector< std::string > histnames;
1253 [ - - ]: 0 : for (const auto& eq : g_dgpde) {
1254 : 0 : auto n = eq.histNames();
1255 [ - - ]: 0 : histnames.insert( end(histnames), begin(n), end(n) );
1256 : : }
1257 [ - - ]: 0 : d->histheader( std::move(histnames) );
1258 : : }
1259 : 918 : }
1260 : :
1261 : : void
1262 : 918 : DG::box( tk::real v )
1263 : : // *****************************************************************************
1264 : : // Receive total box IC volume and set conditions in box
1265 : : //! \param[in] v Total volume within user-specified box
1266 : : // *****************************************************************************
1267 : : {
1268 : 918 : auto d = Disc();
1269 : :
1270 : : // Store user-defined box IC volume
1271 : 918 : d->Boxvol() = v;
1272 : :
1273 : : // Set initial conditions for all PDEs
1274 [ + + ]: 1836 : for (const auto& eq : g_dgpde)
1275 : : {
1276 : 918 : eq.initialize( m_lhs, m_inpoel, m_coord, m_boxelems, m_u, d->T(),
1277 : 918 : m_fd.Esuel().size()/4 );
1278 : 918 : eq.updatePrimitives( m_u, m_lhs, m_geoElem, m_p, m_fd.Esuel().size()/4 );
1279 : : }
1280 : :
1281 : : m_un = m_u;
1282 : :
1283 : : // Output initial conditions to file (regardless of whether it was requested)
1284 [ + - ][ + - ]: 2754 : startFieldOutput( CkCallback(CkIndex_DG::start(), thisProxy[thisIndex]) );
[ - + ][ - - ]
1285 : 918 : }
1286 : :
1287 : : void
1288 : 918 : DG::start()
1289 : : // *****************************************************************************
1290 : : // Start time stepping
1291 : : // *****************************************************************************
1292 : : {
1293 : : // Free memory storing output mesh
1294 : 918 : m_outmesh.destroy();
1295 : :
1296 : : // Start timer measuring time stepping wall clock time
1297 : 918 : Disc()->Timer().zero();
1298 : : // Zero grind-timer
1299 : 918 : Disc()->grindZero();
1300 : : // Start time stepping by computing the size of the next time step)
1301 : 918 : next();
1302 : 918 : }
1303 : :
1304 : : void
1305 : 25778 : DG::startFieldOutput( CkCallback c )
1306 : : // *****************************************************************************
1307 : : // Start preparing fields for output to file
1308 : : //! \param[in] c Function to continue with after the write
1309 : : // *****************************************************************************
1310 : : {
1311 : : // No field output in benchmark mode or if field output frequency not hit
1312 [ + + ][ + + ]: 25778 : if (g_inputdeck.get< tag::cmd, tag::benchmark >() || !fieldOutput()) {
1313 : :
1314 : 23014 : c.send();
1315 : :
1316 : : } else {
1317 : :
1318 : : // Optionally refine mesh for field output
1319 : 2764 : auto d = Disc();
1320 : :
1321 [ + + ]: 2764 : if (refinedOutput()) {
1322 : :
1323 : 33 : const auto& tr = tk::remap( m_fd.Triinpoel(), d->Gid() );
1324 [ + - ][ + - ]: 66 : d->Ref()->outref( m_fd.Bface(), {}, tr, c );
[ + - ][ - - ]
1325 : :
1326 : : } else {
1327 : :
1328 : : // cut off ghosts from mesh connectivity and coordinates
1329 : 2731 : const auto& tr = tk::remap( m_fd.Triinpoel(), d->Gid() );
1330 [ + - ][ + + ]: 5462 : extractFieldOutput( {}, d->Chunk(), d->Coord(), {}, {},
[ - - ]
1331 : : d->NodeCommMap(), m_fd.Bface(), {}, tr, c );
1332 : :
1333 : : }
1334 : :
1335 : : }
1336 : 25778 : }
1337 : :
1338 : : void
1339 : 74580 : DG::next()
1340 : : // *****************************************************************************
1341 : : // Advance equations to next time step
1342 : : // *****************************************************************************
1343 : : {
1344 : 74580 : const auto pref = g_inputdeck.get< tag::pref, tag::pref >();
1345 : :
1346 : 74580 : auto d = Disc();
1347 : :
1348 [ + + ][ + + ]: 74580 : if (pref && m_stage == 0 && d->T() > 0)
[ + + ]
1349 [ + + ]: 3272 : for (const auto& eq : g_dgpde)
1350 : 1636 : eq.eval_ndof( m_nunk, m_coord, m_inpoel, m_fd, m_u,
1351 : : g_inputdeck.get< tag::pref, tag::indicator >(),
1352 : : g_inputdeck.get< tag::discr, tag::ndof >(),
1353 : : g_inputdeck.get< tag::pref, tag::ndofmax >(),
1354 : : g_inputdeck.get< tag::pref, tag::tolref >(),
1355 : 1636 : m_ndof );
1356 : :
1357 : : // communicate solution ghost data (if any)
1358 [ + + ]: 74580 : if (m_sendGhost.empty())
1359 : 4395 : comsol_complete();
1360 : : else
1361 [ + + ]: 569715 : for(const auto& [cid, ghostdata] : m_sendGhost) {
1362 [ + - ][ + - ]: 499530 : std::vector< std::size_t > tetid( ghostdata.size() );
1363 [ + - ][ + - ]: 999060 : std::vector< std::vector< tk::real > > u( ghostdata.size() ),
[ - - ]
1364 [ + - ]: 999060 : prim( ghostdata.size() );
1365 : : std::vector< std::size_t > ndof;
1366 : : std::size_t j = 0;
1367 [ + + ]: 19317180 : for(const auto& i : ghostdata) {
1368 : : Assert( i < m_fd.Esuel().size()/4, "Sending solution ghost data" );
1369 [ + - ]: 18817650 : tetid[j] = i;
1370 [ + - ][ - + ]: 18817650 : u[j] = m_u[i];
1371 [ + - ][ - + ]: 18817650 : prim[j] = m_p[i];
1372 [ + + ][ + + ]: 18817650 : if (pref && m_stage == 0) ndof.push_back( m_ndof[i] );
[ + - ]
1373 : 18817650 : ++j;
1374 : : }
1375 [ + - ][ + - ]: 999060 : thisProxy[ cid ].comsol( thisIndex, m_stage, tetid, u, prim, ndof );
[ + + ][ - - ]
1376 : : }
1377 : :
1378 : 74580 : ownsol_complete();
1379 : 74580 : }
1380 : :
1381 : : void
1382 : 499530 : DG::comsol( int fromch,
1383 : : std::size_t fromstage,
1384 : : const std::vector< std::size_t >& tetid,
1385 : : const std::vector< std::vector< tk::real > >& u,
1386 : : const std::vector< std::vector< tk::real > >& prim,
1387 : : const std::vector< std::size_t >& ndof )
1388 : : // *****************************************************************************
1389 : : // Receive chare-boundary solution ghost data from neighboring chares
1390 : : //! \param[in] fromch Sender chare id
1391 : : //! \param[in] fromstage Sender chare time step stage
1392 : : //! \param[in] tetid Ghost tet ids we receive solution data for
1393 : : //! \param[in] u Solution ghost data
1394 : : //! \param[in] prim Primitive variables in ghost cells
1395 : : //! \param[in] ndof Number of degrees of freedom for chare-boundary elements
1396 : : //! \details This function receives contributions to the unlimited solution
1397 : : //! from fellow chares.
1398 : : // *****************************************************************************
1399 : : {
1400 : : Assert( u.size() == tetid.size(), "Size mismatch in DG::comsol()" );
1401 : : Assert( prim.size() == tetid.size(), "Size mismatch in DG::comsol()" );
1402 : :
1403 : 499530 : const auto pref = g_inputdeck.get< tag::pref, tag::pref >();
1404 : :
1405 : 499530 : if (pref && fromstage == 0)
1406 : : Assert( ndof.size() == tetid.size(), "Size mismatch in DG::comsol()" );
1407 : :
1408 : : // Find local-to-ghost tet id map for sender chare
1409 : : const auto& n = tk::cref_find( m_ghost, fromch );
1410 : :
1411 [ + + ]: 19317180 : for (std::size_t i=0; i<tetid.size(); ++i) {
1412 : 18817650 : auto j = tk::cref_find( n, tetid[i] );
1413 : : Assert( j >= m_fd.Esuel().size()/4, "Receiving solution non-ghost data" );
1414 : 18817650 : auto b = tk::cref_find( m_bid, j );
1415 : : Assert( b < m_uc[0].size(), "Indexing out of bounds" );
1416 : 18817650 : m_uc[0][b] = u[i];
1417 : 18817650 : m_pc[0][b] = prim[i];
1418 [ + + ]: 18817650 : if (pref && fromstage == 0) {
1419 : : Assert( b < m_ndofc[0].size(), "Indexing out of bounds" );
1420 : 395110 : m_ndofc[0][b] = ndof[i];
1421 : : }
1422 : : }
1423 : :
1424 : : // if we have received all solution ghost contributions from neighboring
1425 : : // chares (chares we communicate along chare-boundary faces with), and
1426 : : // contributed our solution to these neighbors, proceed to reconstructions
1427 [ + + ]: 499530 : if (++m_nsol == m_sendGhost.size()) {
1428 : 70185 : m_nsol = 0;
1429 : 70185 : comsol_complete();
1430 : : }
1431 : 499530 : }
1432 : :
1433 : : void
1434 : 2764 : DG::extractFieldOutput(
1435 : : const std::vector< std::size_t >& /*ginpoel*/,
1436 : : const tk::UnsMesh::Chunk& chunk,
1437 : : const tk::UnsMesh::Coords& coord,
1438 : : const std::unordered_map< std::size_t, tk::UnsMesh::Edge >& /*addedNodes*/,
1439 : : const std::unordered_map< std::size_t, std::size_t >& addedTets,
1440 : : const tk::NodeCommMap& nodeCommMap,
1441 : : const std::map< int, std::vector< std::size_t > >& bface,
1442 : : const std::map< int, std::vector< std::size_t > >& /* bnode */,
1443 : : const std::vector< std::size_t >& triinpoel,
1444 : : CkCallback c )
1445 : : // *****************************************************************************
1446 : : // Extract field output going to file
1447 : : //! \param[in] chunk Field-output mesh chunk (connectivity and global<->local
1448 : : //! id maps)
1449 : : //! \param[in] coord Field-output mesh node coordinates
1450 : : //! \param[in] addedTets Field-output mesh cells and their parents (local ids)
1451 : : //! \param[in] nodeCommMap Field-output mesh node communication map
1452 : : //! \param[in] bface Field-output meshndary-faces mapped to side set ids
1453 : : //! \param[in] triinpoel Field-output mesh boundary-face connectivity
1454 : : //! \param[in] c Function to continue with after the write
1455 : : // *****************************************************************************
1456 : : {
1457 : : m_outmesh.chunk = chunk;
1458 : : m_outmesh.coord = coord;
1459 : 2764 : m_outmesh.triinpoel = triinpoel;
1460 : : m_outmesh.bface = bface;
1461 : : m_outmesh.nodeCommMap = nodeCommMap;
1462 : :
1463 : : const auto& inpoel = std::get< 0 >( chunk );
1464 : 2764 : auto nelem = inpoel.size() / 4;
1465 : :
1466 : : // Evaluate element solution on incoming mesh
1467 : 2764 : auto [ue,pe,un,pn] = evalSolution( inpoel, coord, addedTets );
1468 : :
1469 : : // Collect field output from numerical solution requested by user
1470 [ + - ]: 2764 : m_elemfields = numericFieldOutput( ue, tk::Centering::ELEM, pe );
1471 [ + - ]: 2764 : m_nodefields = numericFieldOutput( un, tk::Centering::NODE, pn );
1472 : :
1473 : : // Collect field output from analytical solutions (if exist)
1474 [ + - ]: 2764 : auto geoElem = tk::genGeoElemTet( inpoel, coord );
1475 [ + - ]: 2764 : auto t = Disc()->T();
1476 [ + + ]: 5528 : for (const auto& eq : g_dgpde) {
1477 [ + - ][ + - ]: 5528 : analyticFieldOutput( eq, tk::Centering::ELEM, geoElem.extract(1,0),
[ + - ][ - - ]
1478 [ + - ][ + - ]: 8292 : geoElem.extract(2,0), geoElem.extract(3,0), t, m_elemfields );
[ + - ][ + - ]
[ - - ]
1479 [ + - ]: 2764 : analyticFieldOutput( eq, tk::Centering::NODE, coord[0], coord[1], coord[2],
1480 : : t, m_nodefields );
1481 : : }
1482 : :
1483 : : // Add adaptive indicator array to element-centered field output
1484 [ + + ]: 2764 : if (g_inputdeck.get< tag::pref, tag::pref >()) {
1485 [ + - ]: 402 : std::vector< tk::real > ndof( begin(m_ndof), end(m_ndof) );
1486 [ + - ]: 402 : ndof.resize( nelem );
1487 [ + + ]: 87834 : for (const auto& [child,parent] : addedTets)
1488 : 87432 : ndof[child] = static_cast< tk::real >( m_ndof[parent] );
1489 [ + - ]: 402 : m_elemfields.push_back( ndof );
1490 : : }
1491 : :
1492 : : // Add shock detection marker array to element-centered field output
1493 [ + - ][ - - ]: 2764 : std::vector< tk::real > shockmarker( begin(m_shockmarker), end(m_shockmarker) );
1494 : : // Here m_shockmarker has a size of m_u.nunk() which is the number of the
1495 : : // elements within this partition (nelem) plus the ghost partition cells. In
1496 : : // terms of output purpose, we only need the solution data within this
1497 : : // partition. Therefore, resizing it to nelem removes the extra partition
1498 : : // boundary allocations in the shockmarker vector. Since the code assumes that
1499 : : // the boundary elements are on the top, the resize operation keeps the lower
1500 : : // portion.
1501 [ + - ]: 2764 : shockmarker.resize( nelem );
1502 [ + + ]: 160276 : for (const auto& [child,parent] : addedTets)
1503 : 157512 : shockmarker[child] = static_cast< tk::real >(m_shockmarker[parent]);
1504 [ + - ]: 2764 : m_elemfields.push_back( shockmarker );
1505 : :
1506 : : // Send node fields contributions to neighbor chares
1507 [ + + ]: 2764 : if (nodeCommMap.empty())
1508 [ + - ]: 200 : comnodeout_complete();
1509 : : else {
1510 : : const auto& lid = std::get< 2 >( chunk );
1511 [ + - ]: 5128 : auto esup = tk::genEsup( inpoel, 4 );
1512 [ + + ]: 21250 : for(const auto& [ch,nodes] : nodeCommMap) {
1513 : : // Pack node field data in chare boundary nodes
1514 : : std::vector< std::vector< tk::real > >
1515 [ + - ][ + - ]: 56058 : l( m_nodefields.size(), std::vector< tk::real >( nodes.size() ) );
1516 [ - + ]: 18686 : for (std::size_t f=0; f<m_nodefields.size(); ++f) {
1517 : : std::size_t j = 0;
1518 [ - - ]: 0 : for (auto g : nodes)
1519 : 0 : l[f][j++] = m_nodefields[f][ tk::cref_find(lid,g) ];
1520 : : }
1521 : : // Pack (partial) number of elements surrounding chare boundary nodes
1522 [ + - ]: 18686 : std::vector< std::size_t > nesup( nodes.size() );
1523 : : std::size_t j = 0;
1524 [ + + ]: 179040 : for (auto g : nodes) {
1525 : 160354 : auto i = tk::cref_find( lid, g );
1526 : 160354 : nesup[j++] = esup.second[i+1] - esup.second[i];
1527 : : }
1528 [ + - ][ + - ]: 56058 : thisProxy[ch].comnodeout(
[ + - ][ - - ]
1529 [ + - ][ - + ]: 37372 : std::vector<std::size_t>(begin(nodes),end(nodes)), nesup, l );
1530 : : }
1531 : : }
1532 : :
1533 [ + - ][ + - ]: 5528 : ownnod_complete( c );
[ - - ]
1534 : 2764 : }
1535 : :
1536 : : std::tuple< tk::Fields, tk::Fields, tk::Fields, tk::Fields >
1537 : 2764 : DG::evalSolution(
1538 : : const std::vector< std::size_t >& inpoel,
1539 : : const tk::UnsMesh::Coords& coord,
1540 : : const std::unordered_map< std::size_t, std::size_t >& addedTets )
1541 : : // *****************************************************************************
1542 : : // Evaluate solution on incomping (a potentially refined) mesh
1543 : : //! \param[in] inpoel Incoming (potentially refined field-output) mesh
1544 : : //! connectivity
1545 : : //! \param[in] coord Incoming (potentially refined Field-output) mesh node
1546 : : //! coordinates
1547 : : //! \param[in] addedTets Field-output mesh cells and their parents (local ids)
1548 : : //! \details This function evaluates the solution on the incoming mesh. The
1549 : : //! incoming mesh can be refined but can also be just the mesh the numerical
1550 : : //! solution is computed on.
1551 : : //! \note If the incoming mesh is refined (for field putput) compared to the
1552 : : //! mesh the numerical solution is computed on, the solution is evaluated in
1553 : : //! cells as wells as in nodes. If the solution is not refined, the solution
1554 : : //! is evaluated in nodes.
1555 : : //! \return Solution in cells, primitive variables in cells, solution in nodes,
1556 : : //! primitive variables in nodes of incoming mesh.
1557 : : // *****************************************************************************
1558 : : {
1559 : : using tk::dot;
1560 : : using tk::real;
1561 : :
1562 : 2764 : const auto nelem = inpoel.size()/4;
1563 : 2764 : const auto rdof = g_inputdeck.get< tag::discr, tag::rdof >();
1564 : 2764 : const auto uncomp = m_u.nprop() / rdof;
1565 : 2764 : const auto pncomp = m_p.nprop() / rdof;
1566 : : auto ue = m_u;
1567 : : auto pe = m_p;
1568 : :
1569 : : // If mesh is not refined for field output, cut off ghosts from element
1570 : : // solution. (No need to output ghosts and writer would error.) If mesh is
1571 : : // refined for field output, resize element solution fields to refined mesh.
1572 : : ue.resize( nelem );
1573 : : pe.resize( nelem );
1574 : :
1575 [ + - ]: 2764 : auto npoin = coord[0].size();
1576 [ + - ]: 2764 : tk::Fields un( npoin, m_u.nprop()/rdof );
1577 [ + - ]: 2764 : tk::Fields pn( npoin, m_p.nprop()/rdof );
1578 : : un.fill(0.0);
1579 : : pn.fill(0.0);
1580 : :
1581 : : const auto& x = coord[0];
1582 : : const auto& y = coord[1];
1583 : : const auto& z = coord[2];
1584 : :
1585 : : // If mesh is not refined for output, evaluate solution in nodes
1586 [ + + ]: 2764 : if (addedTets.empty()) {
1587 : :
1588 [ + + ]: 1330862 : for (std::size_t e=0; e<nelem; ++e) {
1589 : 1328131 : auto e4 = e*4;
1590 : : // Extract element node coordinates
1591 : : std::array< std::array< real, 3>, 4 > ce{{
1592 : 1328131 : {{ x[inpoel[e4 ]], y[inpoel[e4 ]], z[inpoel[e4 ]] }},
1593 : 1328131 : {{ x[inpoel[e4+1]], y[inpoel[e4+1]], z[inpoel[e4+1]] }},
1594 : 1328131 : {{ x[inpoel[e4+2]], y[inpoel[e4+2]], z[inpoel[e4+2]] }},
1595 : 1328131 : {{ x[inpoel[e4+3]], y[inpoel[e4+3]], z[inpoel[e4+3]] }} }};
1596 : : // Compute inverse Jacobian
1597 : 1328131 : auto J = tk::inverseJacobian( ce[0], ce[1], ce[2], ce[3] );
1598 : : // Evaluate solution in child nodes
1599 [ + + ]: 6640655 : for (std::size_t j=0; j<4; ++j) {
1600 : : std::array< real, 3 >
1601 [ + - ]: 5312524 : h{{ce[j][0]-ce[0][0], ce[j][1]-ce[0][1], ce[j][2]-ce[0][2] }};
1602 [ + - ]: 5312524 : auto Bn = tk::eval_basis( m_ndof[e],
1603 [ + - ]: 5312524 : dot(J[0],h), dot(J[1],h), dot(J[2],h) );
1604 [ + - ][ - - ]: 5312524 : auto u = eval_state( uncomp, 0, rdof, m_ndof[e], e, m_u, Bn, {0, uncomp-1} );
1605 [ + - ][ - - ]: 5312524 : auto p = eval_state( pncomp, 0, rdof, m_ndof[e], e, m_p, Bn, {0, pncomp-1} );
1606 : : // Assign child node solution
1607 [ + + ]: 17638080 : for (std::size_t i=0; i<uncomp; ++i) un(inpoel[e4+j],i,0) += u[i];
1608 [ + + ]: 6686076 : for (std::size_t i=0; i<pncomp; ++i) pn(inpoel[e4+j],i,0) += p[i];
1609 : : }
1610 : : }
1611 : :
1612 : : // If mesh is refed for output, evaluate solution in elements and nodes of
1613 : : // refined mesh
1614 : : } else {
1615 : :
1616 [ + - ]: 33 : const auto& pinpoel = Disc()->Inpoel(); // unrefined (parent) mesh
1617 : :
1618 [ + + ]: 157545 : for ([[maybe_unused]] const auto& [child,parent] : addedTets) {
1619 : : Assert( child < nelem, "Indexing out of new solution vector" );
1620 : : Assert( parent < pinpoel.size()/4,
1621 : : "Indexing out of old solution vector" );
1622 : : }
1623 : :
1624 [ + + ]: 157545 : for (const auto& [child,parent] : addedTets) {
1625 : : // Extract parent element's node coordinates
1626 : 157512 : auto p4 = 4*parent;
1627 : : std::array< std::array< real, 3>, 4 > cp{{
1628 : 157512 : {{ x[pinpoel[p4 ]], y[pinpoel[p4 ]], z[pinpoel[p4 ]] }},
1629 : 157512 : {{ x[pinpoel[p4+1]], y[pinpoel[p4+1]], z[pinpoel[p4+1]] }},
1630 : 157512 : {{ x[pinpoel[p4+2]], y[pinpoel[p4+2]], z[pinpoel[p4+2]] }},
1631 : 157512 : {{ x[pinpoel[p4+3]], y[pinpoel[p4+3]], z[pinpoel[p4+3]] }} }};
1632 : : // Evaluate inverse Jacobian of the parent
1633 : 157512 : auto Jp = tk::inverseJacobian( cp[0], cp[1], cp[2], cp[3] );
1634 : : // Compute child cell centroid
1635 : 157512 : auto c4 = 4*child;
1636 [ + - ]: 157512 : auto cx = (x[inpoel[c4 ]] + x[inpoel[c4+1]] +
1637 : 157512 : x[inpoel[c4+2]] + x[inpoel[c4+3]]) / 4.0;
1638 : 157512 : auto cy = (y[inpoel[c4 ]] + y[inpoel[c4+1]] +
1639 : 157512 : y[inpoel[c4+2]] + y[inpoel[c4+3]]) / 4.0;
1640 : 157512 : auto cz = (z[inpoel[c4 ]] + z[inpoel[c4+1]] +
1641 : 157512 : z[inpoel[c4+2]] + z[inpoel[c4+3]]) / 4.0;
1642 : : // Compute solution in child centroid
1643 [ + - ]: 157512 : std::array< real, 3 > h{{cx-cp[0][0], cy-cp[0][1], cz-cp[0][2] }};
1644 [ + - ]: 157512 : auto B = tk::eval_basis( m_ndof[parent],
1645 [ + - ]: 157512 : dot(Jp[0],h), dot(Jp[1],h), dot(Jp[2],h) );
1646 [ + - ][ - - ]: 157512 : auto u = eval_state( uncomp, 0, rdof, m_ndof[parent], parent, m_u, B, {0, uncomp-1} );
1647 [ + - ][ - - ]: 157512 : auto p = eval_state( pncomp, 0, rdof, m_ndof[parent], parent, m_p, B, {0, pncomp-1} );
1648 : : // Assign cell center solution from parent to child
1649 [ + + ]: 945072 : for (std::size_t i=0; i<uncomp; ++i) ue(child,i*rdof,0) = u[i];
1650 [ - + ]: 157512 : for (std::size_t i=0; i<pncomp; ++i) pe(child,i*rdof,0) = p[i];
1651 : : // Extract child element's node coordinates
1652 : : std::array< std::array< real, 3>, 4 > cc{{
1653 : 157512 : {{ x[inpoel[c4 ]], y[inpoel[c4 ]], z[inpoel[c4 ]] }},
1654 : 157512 : {{ x[inpoel[c4+1]], y[inpoel[c4+1]], z[inpoel[c4+1]] }},
1655 : 157512 : {{ x[inpoel[c4+2]], y[inpoel[c4+2]], z[inpoel[c4+2]] }},
1656 : 157512 : {{ x[inpoel[c4+3]], y[inpoel[c4+3]], z[inpoel[c4+3]] }} }};
1657 : : // Evaluate solution in child nodes
1658 [ + + ]: 787560 : for (std::size_t j=0; j<4; ++j) {
1659 : : std::array< real, 3 >
1660 [ + - ]: 630048 : hn{{cc[j][0]-cp[0][0], cc[j][1]-cp[0][1], cc[j][2]-cp[0][2] }};
1661 [ + - ]: 630048 : auto Bn = tk::eval_basis( m_ndof[parent],
1662 [ + - ]: 630048 : dot(Jp[0],hn), dot(Jp[1],hn), dot(Jp[2],hn) );
1663 [ + - ][ - - ]: 630048 : auto cnu = eval_state(uncomp, 0, rdof, m_ndof[parent], parent, m_u, Bn, {0, uncomp-1});
1664 [ + - ][ - - ]: 630048 : auto cnp = eval_state(pncomp, 0, rdof, m_ndof[parent], parent, m_p, Bn, {0, pncomp-1});
1665 : : // Assign child node solution
1666 [ + + ]: 3780288 : for (std::size_t i=0; i<uncomp; ++i) un(inpoel[c4+j],i,0) += cnu[i];
1667 [ - + ]: 630048 : for (std::size_t i=0; i<pncomp; ++i) pn(inpoel[c4+j],i,0) += cnp[i];
1668 : : }
1669 : : }
1670 : : }
1671 : :
1672 : 2764 : return { ue, pe, un, pn };
1673 : : }
1674 : :
1675 : : void
1676 : 1026 : DG::lhs()
1677 : : // *****************************************************************************
1678 : : // Compute left-hand side of discrete transport equations
1679 : : // *****************************************************************************
1680 : : {
1681 [ + + ]: 2052 : for (const auto& eq : g_dgpde) eq.lhs( m_geoElem, m_lhs );
1682 : :
1683 [ + + ]: 1026 : if (!m_initial) stage();
1684 : 1026 : }
1685 : :
1686 : : void
1687 : 74580 : DG::reco()
1688 : : // *****************************************************************************
1689 : : // Compute reconstructions
1690 : : // *****************************************************************************
1691 : : {
1692 : 74580 : const auto pref = g_inputdeck.get< tag::pref, tag::pref >();
1693 : 74580 : const auto rdof = g_inputdeck.get< tag::discr, tag::rdof >();
1694 : :
1695 : : // Combine own and communicated contributions of unreconstructed solution and
1696 : : // degrees of freedom in cells (if p-adaptive)
1697 [ + + ]: 18892230 : for (const auto& b : m_bid) {
1698 : : Assert( m_uc[0][b.second].size() == m_u.nprop(), "ncomp size mismatch" );
1699 : : Assert( m_pc[0][b.second].size() == m_p.nprop(), "ncomp size mismatch" );
1700 [ + + ]: 168233925 : for (std::size_t c=0; c<m_u.nprop(); ++c) {
1701 : 149416275 : m_u(b.first,c,0) = m_uc[0][b.second][c];
1702 : : }
1703 [ + + ]: 38425875 : for (std::size_t c=0; c<m_p.nprop(); ++c) {
1704 : 19608225 : m_p(b.first,c,0) = m_pc[0][b.second][c];
1705 : : }
1706 [ + + ][ + + ]: 18817650 : if (pref && m_stage == 0) {
1707 : 395110 : m_ndof[ b.first ] = m_ndofc[0][ b.second ];
1708 : : }
1709 : : }
1710 : :
1711 [ + + ][ + + ]: 74580 : if (pref && m_stage==0) propagate_ndof();
1712 : :
1713 [ + + ]: 74580 : if (rdof > 1) {
1714 : 44760 : auto d = Disc();
1715 : :
1716 : : // Reconstruct second-order solution and primitive quantities
1717 [ + + ]: 89520 : for (const auto& eq : g_dgpde)
1718 : 44760 : eq.reconstruct( d->T(), m_geoFace, m_geoElem, m_fd, m_esup, m_inpoel,
1719 : 44760 : m_coord, m_u, m_p );
1720 : : }
1721 : :
1722 : : // Send reconstructed solution to neighboring chares
1723 [ + + ]: 74580 : if (m_sendGhost.empty())
1724 : 4395 : comreco_complete();
1725 : : else
1726 [ + + ]: 569715 : for(const auto& [cid, ghostdata] : m_sendGhost) {
1727 [ + - ][ + - ]: 499530 : std::vector< std::size_t > tetid( ghostdata.size() );
1728 [ + - ][ + - ]: 999060 : std::vector< std::vector< tk::real > > u( ghostdata.size() ),
[ - - ]
1729 [ + - ]: 999060 : prim( ghostdata.size() );
1730 : : std::vector< std::size_t > ndof;
1731 : : std::size_t j = 0;
1732 [ + + ]: 19317180 : for(const auto& i : ghostdata) {
1733 : : Assert( i < m_fd.Esuel().size()/4, "Sending reconstructed ghost "
1734 : : "data" );
1735 [ + - ]: 18817650 : tetid[j] = i;
1736 [ + - ][ - + ]: 18817650 : u[j] = m_u[i];
1737 [ + - ][ - + ]: 18817650 : prim[j] = m_p[i];
1738 [ + + ][ + + ]: 18817650 : if (pref && m_stage == 0) ndof.push_back( m_ndof[i] );
[ + - ]
1739 : 18817650 : ++j;
1740 : : }
1741 [ + - ][ + - ]: 999060 : thisProxy[ cid ].comreco( thisIndex, tetid, u, prim, ndof );
[ + + ][ - - ]
1742 : : }
1743 : :
1744 : 74580 : ownreco_complete();
1745 : 74580 : }
1746 : :
1747 : : void
1748 : 499530 : DG::comreco( int fromch,
1749 : : const std::vector< std::size_t >& tetid,
1750 : : const std::vector< std::vector< tk::real > >& u,
1751 : : const std::vector< std::vector< tk::real > >& prim,
1752 : : const std::vector< std::size_t >& ndof )
1753 : : // *****************************************************************************
1754 : : // Receive chare-boundary reconstructed ghost data from neighboring chares
1755 : : //! \param[in] fromch Sender chare id
1756 : : //! \param[in] tetid Ghost tet ids we receive solution data for
1757 : : //! \param[in] u Reconstructed high-order solution
1758 : : //! \param[in] prim Limited high-order primitive quantities
1759 : : //! \param[in] ndof Number of degrees of freedom for chare-boundary elements
1760 : : //! \details This function receives contributions to the reconstructed solution
1761 : : //! from fellow chares.
1762 : : // *****************************************************************************
1763 : : {
1764 : : Assert( u.size() == tetid.size(), "Size mismatch in DG::comreco()" );
1765 : : Assert( prim.size() == tetid.size(), "Size mismatch in DG::comreco()" );
1766 : :
1767 : 499530 : const auto pref = g_inputdeck.get< tag::pref, tag::pref >();
1768 : :
1769 : : if (pref && m_stage == 0)
1770 : : Assert( ndof.size() == tetid.size(), "Size mismatch in DG::comreco()" );
1771 : :
1772 : : // Find local-to-ghost tet id map for sender chare
1773 : : const auto& n = tk::cref_find( m_ghost, fromch );
1774 : :
1775 [ + + ]: 19317180 : for (std::size_t i=0; i<tetid.size(); ++i) {
1776 : 18817650 : auto j = tk::cref_find( n, tetid[i] );
1777 : : Assert( j >= m_fd.Esuel().size()/4, "Receiving solution non-ghost data" );
1778 : 18817650 : auto b = tk::cref_find( m_bid, j );
1779 : : Assert( b < m_uc[1].size(), "Indexing out of bounds" );
1780 : : Assert( b < m_pc[1].size(), "Indexing out of bounds" );
1781 : 18817650 : m_uc[1][b] = u[i];
1782 : 18817650 : m_pc[1][b] = prim[i];
1783 [ + + ][ + + ]: 18817650 : if (pref && m_stage == 0) {
1784 : : Assert( b < m_ndofc[1].size(), "Indexing out of bounds" );
1785 : 395110 : m_ndofc[1][b] = ndof[i];
1786 : : }
1787 : : }
1788 : :
1789 : : // if we have received all solution ghost contributions from neighboring
1790 : : // chares (chares we communicate along chare-boundary faces with), and
1791 : : // contributed our solution to these neighbors, proceed to limiting
1792 [ + + ]: 499530 : if (++m_nreco == m_sendGhost.size()) {
1793 : 70185 : m_nreco = 0;
1794 : 70185 : comreco_complete();
1795 : : }
1796 : 499530 : }
1797 : :
1798 : : void
1799 : 74580 : DG::nodalExtrema()
1800 : : // *****************************************************************************
1801 : : // Compute nodal extrema at chare-boundary nodes. Extrema at internal nodes
1802 : : // are calculated in limiter function.
1803 : : // *****************************************************************************
1804 : : {
1805 : 74580 : auto d = Disc();
1806 : 74580 : auto gid = d->Gid();
1807 : : auto bid = d->Bid();
1808 : 74580 : const auto rdof = g_inputdeck.get< tag::discr, tag::rdof >();
1809 : 74580 : const auto pref = g_inputdeck.get< tag::pref, tag::pref >();
1810 : 74580 : const auto ncomp = m_u.nprop() / rdof;
1811 : 74580 : const auto nprim = m_p.nprop() / rdof;
1812 : :
1813 : : // Combine own and communicated contributions of unlimited solution, and
1814 : : // if a p-adaptive algorithm is used, degrees of freedom in cells
1815 [ + + ]: 18892230 : for (const auto& [boundary, localtet] : m_bid) {
1816 : : Assert( m_uc[1][localtet].size() == m_u.nprop(), "ncomp size mismatch" );
1817 : : Assert( m_pc[1][localtet].size() == m_p.nprop(), "ncomp size mismatch" );
1818 [ + + ]: 168233925 : for (std::size_t c=0; c<m_u.nprop(); ++c) {
1819 : 149416275 : m_u(boundary,c,0) = m_uc[1][localtet][c];
1820 : : }
1821 [ + + ]: 38425875 : for (std::size_t c=0; c<m_p.nprop(); ++c) {
1822 : 19608225 : m_p(boundary,c,0) = m_pc[1][localtet][c];
1823 : : }
1824 [ + + ][ + + ]: 18817650 : if (pref && m_stage == 0) {
1825 : 395110 : m_ndof[ boundary ] = m_ndofc[1][ localtet ];
1826 : : }
1827 : : }
1828 : :
1829 : : // Initialize nodal extrema vector
1830 : : auto large = std::numeric_limits< tk::real >::max();
1831 [ + + ]: 3905130 : for(std::size_t i = 0; i<bid.size(); i++)
1832 : : {
1833 [ + + ]: 11312280 : for (std::size_t c=0; c<ncomp; ++c)
1834 : : {
1835 [ + + ]: 29926920 : for(std::size_t idof=0; idof<m_ndof_NodalExtrm; idof++)
1836 : : {
1837 : 22445190 : auto max_mark = 2*c*m_ndof_NodalExtrm + 2*idof;
1838 : 22445190 : auto min_mark = max_mark + 1;
1839 : 22445190 : m_uNodalExtrm[i][max_mark] = -large;
1840 : 22445190 : m_uNodalExtrm[i][min_mark] = large;
1841 : : }
1842 : : }
1843 [ + + ]: 4960200 : for (std::size_t c=0; c<nprim; ++c)
1844 : : {
1845 [ + + ]: 4518600 : for(std::size_t idof=0; idof<m_ndof_NodalExtrm; idof++)
1846 : : {
1847 : 3388950 : auto max_mark = 2*c*m_ndof_NodalExtrm + 2*idof;
1848 : 3388950 : auto min_mark = max_mark + 1;
1849 : 3388950 : m_pNodalExtrm[i][max_mark] = -large;
1850 : 3388950 : m_pNodalExtrm[i][min_mark] = large;
1851 : : }
1852 : : }
1853 : : }
1854 : :
1855 : : // Evaluate the max/min value for the chare-boundary nodes
1856 [ + + ]: 74580 : if(rdof > 4) {
1857 [ + - ]: 15570 : evalNodalExtrm(ncomp, nprim, m_ndof_NodalExtrm, d->bndel(), m_inpoel,
1858 [ + - ]: 7785 : m_coord, gid, bid, m_u, m_p, m_uNodalExtrm, m_pNodalExtrm);
1859 : : }
1860 : :
1861 : : // Communicate extrema at nodes to other chares on chare-boundary
1862 [ + + ]: 74580 : if (d->NodeCommMap().empty()) // in serial we are done
1863 [ + - ]: 4395 : comnodalExtrema_complete();
1864 : : else // send nodal extrema to chare-boundary nodes to fellow chares
1865 : : {
1866 [ + + ]: 569715 : for (const auto& [c,n] : d->NodeCommMap()) {
1867 [ + - ][ + - ]: 999060 : std::vector< std::vector< tk::real > > g1( n.size() ), g2( n.size() );
1868 : : std::size_t j = 0;
1869 [ + + ]: 6215640 : for (auto i : n)
1870 : : {
1871 : 5716110 : auto p = tk::cref_find(d->Bid(),i);
1872 [ + - ]: 5716110 : g1[ j ] = m_uNodalExtrm[ p ];
1873 [ + - ]: 5716110 : g2[ j++ ] = m_pNodalExtrm[ p ];
1874 : : }
1875 [ + - ][ + - ]: 999060 : thisProxy[c].comnodalExtrema( std::vector<std::size_t>(begin(n),end(n)),
[ + - ][ - + ]
1876 : : g1, g2 );
1877 : : }
1878 : : }
1879 [ + - ]: 74580 : ownnodalExtrema_complete();
1880 : 74580 : }
1881 : :
1882 : : void
1883 : 499530 : DG::comnodalExtrema( const std::vector< std::size_t >& gid,
1884 : : const std::vector< std::vector< tk::real > >& G1,
1885 : : const std::vector< std::vector< tk::real > >& G2 )
1886 : : // *****************************************************************************
1887 : : // Receive contributions to nodal extrema on chare-boundaries
1888 : : //! \param[in] gid Global mesh node IDs at which we receive grad contributions
1889 : : //! \param[in] G1 Partial contributions of extrema for conservative variables to
1890 : : //! chare-boundary nodes
1891 : : //! \param[in] G2 Partial contributions of extrema for primitive variables to
1892 : : //! chare-boundary nodes
1893 : : //! \details This function receives contributions to m_uNodalExtrm/m_pNodalExtrm
1894 : : //! , which stores nodal extrems at mesh chare-boundary nodes. While
1895 : : //! m_uNodalExtrm/m_pNodalExtrm stores own contributions, m_uNodalExtrmc
1896 : : //! /m_pNodalExtrmc collects the neighbor chare contributions during
1897 : : //! communication.
1898 : : // *****************************************************************************
1899 : : {
1900 : : Assert( G1.size() == gid.size(), "Size mismatch" );
1901 : : Assert( G2.size() == gid.size(), "Size mismatch" );
1902 : :
1903 : 499530 : const auto rdof = g_inputdeck.get< tag::discr, tag::rdof >();
1904 : 499530 : const auto ncomp = m_u.nprop() / rdof;
1905 : 499530 : const auto nprim = m_p.nprop() / rdof;
1906 : :
1907 [ + + ]: 6215640 : for (std::size_t i=0; i<gid.size(); ++i)
1908 : : {
1909 : : auto& u = m_uNodalExtrmc[gid[i]];
1910 : 5716110 : auto& p = m_pNodalExtrmc[gid[i]];
1911 [ + + ]: 19335360 : for (std::size_t c=0; c<ncomp; ++c)
1912 : : {
1913 [ + + ]: 54477000 : for(std::size_t idof=0; idof<m_ndof_NodalExtrm; idof++)
1914 : : {
1915 : 40857750 : auto max_mark = 2*c*m_ndof_NodalExtrm + 2*idof;
1916 : 40857750 : auto min_mark = max_mark + 1;
1917 [ + + ][ + + ]: 41225181 : u[max_mark] = std::max( G1[i][max_mark], u[max_mark] );
1918 : 40857750 : u[min_mark] = std::min( G1[i][min_mark], u[min_mark] );
1919 : : }
1920 : : }
1921 [ + + ]: 8418660 : for (std::size_t c=0; c<nprim; ++c)
1922 : : {
1923 [ + + ]: 10810200 : for(std::size_t idof=0; idof<m_ndof_NodalExtrm; idof++)
1924 : : {
1925 : 8107650 : auto max_mark = 2*c*m_ndof_NodalExtrm + 2*idof;
1926 : 8107650 : auto min_mark = max_mark + 1;
1927 [ - + ][ - + ]: 8107650 : p[max_mark] = std::max( G2[i][max_mark], p[max_mark] );
1928 : 8107650 : p[min_mark] = std::min( G2[i][min_mark], p[min_mark] );
1929 : : }
1930 : : }
1931 : : }
1932 : :
1933 [ + + ]: 499530 : if (++m_nnodalExtrema == Disc()->NodeCommMap().size())
1934 : : {
1935 : 70185 : m_nnodalExtrema = 0;
1936 : 70185 : comnodalExtrema_complete();
1937 : : }
1938 : 499530 : }
1939 : :
1940 : 75606 : void DG::resizeNodalExtremac()
1941 : : // *****************************************************************************
1942 : : // Resize the buffer vector of nodal extrema
1943 : : // *****************************************************************************
1944 : : {
1945 : 75606 : const auto rdof = g_inputdeck.get< tag::discr, tag::rdof >();
1946 : 75606 : const auto ncomp = m_u.nprop() / rdof;
1947 : 75606 : const auto nprim = m_p.nprop() / rdof;
1948 : :
1949 : 75606 : auto large = std::numeric_limits< tk::real >::max();
1950 [ + - ][ + + ]: 658970 : for (const auto& [c,n] : Disc()->NodeCommMap())
1951 : : {
1952 [ + + ][ + - ]: 6349530 : for (auto i : n) {
1953 : : auto& u = m_uNodalExtrmc[i];
1954 : : auto& p = m_pNodalExtrmc[i];
1955 [ + - ]: 5841772 : u.resize( 2*m_ndof_NodalExtrm*ncomp, large );
1956 [ + - ]: 5841772 : p.resize( 2*m_ndof_NodalExtrm*nprim, large );
1957 : :
1958 : : // Initialize the minimum nodal extrema
1959 [ + + ]: 23367088 : for(std::size_t idof=0; idof<m_ndof_NodalExtrm; idof++)
1960 : : {
1961 [ + + ]: 59087718 : for(std::size_t k = 0; k < ncomp; k++)
1962 : 41562402 : u[2*k*m_ndof_NodalExtrm+2*idof] = -large;
1963 [ + + ]: 25736712 : for(std::size_t k = 0; k < nprim; k++)
1964 : 8211396 : p[2*k*m_ndof_NodalExtrm+2*idof] = -large;
1965 : : }
1966 : : }
1967 : : }
1968 : 75606 : }
1969 : :
1970 : 7785 : void DG::evalNodalExtrm( const std::size_t ncomp,
1971 : : const std::size_t nprim,
1972 : : const std::size_t ndof_NodalExtrm,
1973 : : const std::vector< std::size_t >& bndel,
1974 : : const std::vector< std::size_t >& inpoel,
1975 : : const tk::UnsMesh::Coords& coord,
1976 : : const std::vector< std::size_t >& gid,
1977 : : const std::unordered_map< std::size_t, std::size_t >&
1978 : : bid,
1979 : : const tk::Fields& U,
1980 : : const tk::Fields& P,
1981 : : std::vector< std::vector<tk::real> >& uNodalExtrm,
1982 : : std::vector< std::vector<tk::real> >& pNodalExtrm )
1983 : : // *****************************************************************************
1984 : : // Compute the nodal extrema for chare-boundary nodes
1985 : : //! \param[in] ncomp Number of conservative variables
1986 : : //! \param[in] nprim Number of primitive variables
1987 : : //! \param[in] ndof_NodalExtrm Degree of freedom for nodal extrema
1988 : : //! \param[in] bndel List of elements contributing to chare-boundary nodes
1989 : : //! \param[in] inpoel Element-node connectivity for element e
1990 : : //! \param[in] coord Array of nodal coordinates
1991 : : //! \param[in] gid Local->global node id map
1992 : : //! \param[in] bid Local chare-boundary node ids (value) associated to
1993 : : //! global node ids (key)
1994 : : //! \param[in] U Vector of conservative variables
1995 : : //! \param[in] P Vector of primitive variables
1996 : : //! \param[in,out] uNodalExtrm Chare-boundary nodal extrema for conservative
1997 : : //! variables
1998 : : //! \param[in,out] pNodalExtrm Chare-boundary nodal extrema for primitive
1999 : : //! variables
2000 : : // *****************************************************************************
2001 : : {
2002 : 7785 : const auto rdof = g_inputdeck.get< tag::discr, tag::rdof >();
2003 : :
2004 [ + + ]: 741345 : for (auto e : bndel)
2005 : : {
2006 : : // access node IDs
2007 : : const std::vector<std::size_t> N
2008 [ + - ]: 733560 : { inpoel[e*4+0], inpoel[e*4+1], inpoel[e*4+2], inpoel[e*4+3] };
2009 : :
2010 : : // Loop over nodes of element e
2011 [ + + ]: 3667800 : for(std::size_t ip=0; ip<4; ++ip)
2012 : : {
2013 : 2934240 : auto i = bid.find( gid[N[ip]] );
2014 [ + + ]: 2934240 : if (i != end(bid)) // If ip is the chare boundary point
2015 : : {
2016 : : // If DG(P2) is applied, find the nodal extrema of the gradients of
2017 : : // conservative/primitive variables in the physical domain
2018 : :
2019 : : // Vector used to store the first order derivatives for both
2020 : : // conservative and primitive variables
2021 [ + - ][ - - ]: 1946835 : std::vector< std::array< tk::real, 3 > > gradc(ncomp, {0.0, 0.0, 0.0});
2022 [ + - ][ - - ]: 1946835 : std::vector< std::array< tk::real, 3 > > gradp(ncomp, {0.0, 0.0, 0.0});
2023 : :
2024 : : const auto& cx = coord[0];
2025 : : const auto& cy = coord[1];
2026 : : const auto& cz = coord[2];
2027 : :
2028 : : std::array< std::array< tk::real, 3>, 4 > coordel {{
2029 : 1946835 : {{ cx[ N[0] ], cy[ N[0] ], cz[ N[0] ] }},
2030 : 1946835 : {{ cx[ N[1] ], cy[ N[1] ], cz[ N[1] ] }},
2031 : 1946835 : {{ cx[ N[2] ], cy[ N[2] ], cz[ N[2] ] }},
2032 : 1946835 : {{ cx[ N[3] ], cy[ N[3] ], cz[ N[3] ] }}
2033 : 1946835 : }};
2034 : :
2035 : : auto jacInv = tk::inverseJacobian( coordel[0], coordel[1],
2036 : 1946835 : coordel[2], coordel[3] );
2037 : :
2038 : : // Compute the derivatives of basis functions
2039 [ + - ]: 1946835 : auto dBdx = tk::eval_dBdx_p1( rdof, jacInv );
2040 : :
2041 : : std::array< std::vector< tk::real >, 3 > center;
2042 [ + - ]: 1946835 : center[0].resize(1, 0.25);
2043 [ + - ]: 1946835 : center[1].resize(1, 0.25);
2044 [ + - ]: 1946835 : center[2].resize(1, 0.25);
2045 [ + - ]: 1946835 : tk::eval_dBdx_p2(0, center, jacInv, dBdx);
2046 : :
2047 : : // Evaluate the first order derivative in physical domain
2048 [ + + ]: 11254110 : for(std::size_t icomp = 0; icomp < ncomp; icomp++)
2049 : : {
2050 : 9307275 : auto mark = icomp * rdof;
2051 [ + + ]: 37229100 : for(std::size_t idir = 0; idir < 3; idir++)
2052 : : {
2053 : 27921825 : gradc[icomp][idir] = 0;
2054 [ + + ]: 279218250 : for(std::size_t idof = 1; idof < rdof; idof++)
2055 : 251296425 : gradc[icomp][idir] += U(e, mark+idof, 0) * dBdx[idir][idof];
2056 : : }
2057 : : }
2058 [ - + ]: 1946835 : for(std::size_t icomp = 0; icomp < nprim; icomp++)
2059 : : {
2060 : 0 : auto mark = icomp * rdof;
2061 [ - - ]: 0 : for(std::size_t idir = 0; idir < 3; idir++)
2062 : : {
2063 : 0 : gradp[icomp][idir] = 0;
2064 [ - - ]: 0 : for(std::size_t idof = 1; idof < rdof; idof++)
2065 : 0 : gradp[icomp][idir] += P(e, mark+idof, 0) * dBdx[idir][idof];
2066 : : }
2067 : : }
2068 : :
2069 : : // Store the extrema for the gradients
2070 [ + + ]: 11254110 : for (std::size_t c=0; c<ncomp; ++c)
2071 : : {
2072 [ + + ]: 37229100 : for (std::size_t idof = 0; idof < ndof_NodalExtrm; idof++)
2073 : : {
2074 : 27921825 : auto max_mark = 2*c*m_ndof_NodalExtrm + 2*idof;
2075 : 27921825 : auto min_mark = max_mark + 1;
2076 [ + + ]: 27921825 : auto& ex = uNodalExtrm[i->second];
2077 [ + + ][ + + ]: 34676831 : ex[max_mark] = std::max(ex[max_mark], gradc[c][idof-1]);
2078 : 27921825 : ex[min_mark] = std::min(ex[min_mark], gradc[c][idof-1]);
2079 : : }
2080 : : }
2081 [ - + ]: 1946835 : for (std::size_t c=0; c<nprim; ++c)
2082 : : {
2083 [ - - ]: 0 : for (std::size_t idof = 0; idof < ndof_NodalExtrm; idof++)
2084 : : {
2085 : 0 : auto max_mark = 2*c*m_ndof_NodalExtrm + 2*idof;
2086 : 0 : auto min_mark = max_mark + 1;
2087 [ - - ]: 0 : auto& ex = pNodalExtrm[i->second];
2088 [ - - ][ - - ]: 0 : ex[max_mark] = std::max(ex[max_mark], gradp[c][idof-1]);
2089 : 0 : ex[min_mark] = std::min(ex[min_mark], gradp[c][idof-1]);
2090 : : }
2091 : : }
2092 : : }
2093 : : }
2094 : : }
2095 : 7785 : }
2096 : :
2097 : : void
2098 : 74580 : DG::lim()
2099 : : // *****************************************************************************
2100 : : // Compute limiter function
2101 : : // *****************************************************************************
2102 : : {
2103 : 74580 : auto d = Disc();
2104 : 74580 : const auto pref = g_inputdeck.get< tag::pref, tag::pref >();
2105 : 74580 : const auto rdof = g_inputdeck.get< tag::discr, tag::rdof >();
2106 : 74580 : const auto ncomp = m_u.nprop() / rdof;
2107 : 74580 : const auto nprim = m_p.nprop() / rdof;
2108 : :
2109 : : // Combine own and communicated contributions to nodal extrema
2110 [ + + ]: 3905130 : for (const auto& [gid,g] : m_uNodalExtrmc) {
2111 : 3830550 : auto bid = tk::cref_find( d->Bid(), gid );
2112 [ + + ]: 11312280 : for (ncomp_t c=0; c<ncomp; ++c)
2113 : : {
2114 [ + + ]: 29926920 : for(std::size_t idof=0; idof<m_ndof_NodalExtrm; idof++)
2115 : : {
2116 : 22445190 : auto max_mark = 2*c*m_ndof_NodalExtrm + 2*idof;
2117 : 22445190 : auto min_mark = max_mark + 1;
2118 : 22445190 : m_uNodalExtrm[bid][max_mark] =
2119 [ + + ][ + + ]: 23137612 : std::max(g[max_mark], m_uNodalExtrm[bid][max_mark]);
2120 : 22445190 : m_uNodalExtrm[bid][min_mark] =
2121 : 22445190 : std::min(g[min_mark], m_uNodalExtrm[bid][min_mark]);
2122 : : }
2123 : : }
2124 : : }
2125 [ + + ]: 3905130 : for (const auto& [gid,g] : m_pNodalExtrmc) {
2126 : 3830550 : auto bid = tk::cref_find( d->Bid(), gid );
2127 [ + + ]: 4960200 : for (ncomp_t c=0; c<nprim; ++c)
2128 : : {
2129 [ + + ]: 4518600 : for(std::size_t idof=0; idof<m_ndof_NodalExtrm; idof++)
2130 : : {
2131 : 3388950 : auto max_mark = 2*c*m_ndof_NodalExtrm + 2*idof;
2132 : 3388950 : auto min_mark = max_mark + 1;
2133 : 3388950 : m_pNodalExtrm[bid][max_mark] =
2134 [ - + ][ - + ]: 3388950 : std::max(g[max_mark], m_pNodalExtrm[bid][max_mark]);
2135 : 3388950 : m_pNodalExtrm[bid][min_mark] =
2136 : 3388950 : std::min(g[min_mark], m_pNodalExtrm[bid][min_mark]);
2137 : : }
2138 : : }
2139 : : }
2140 : :
2141 : : // clear gradients receive buffer
2142 : 74580 : tk::destroy(m_uNodalExtrmc);
2143 : 74580 : tk::destroy(m_pNodalExtrmc);
2144 : :
2145 [ + + ]: 74580 : if (rdof > 1)
2146 [ + + ]: 89520 : for (const auto& eq : g_dgpde)
2147 : 44760 : eq.limit( d->T(), m_geoFace, m_geoElem, m_fd, m_esup, m_inpoel, m_coord,
2148 : 44760 : m_ndof, d->Gid(), d->Bid(), m_uNodalExtrm, m_pNodalExtrm, m_u,
2149 : 44760 : m_p, m_shockmarker );
2150 : :
2151 : : // Send limited solution to neighboring chares
2152 [ + + ]: 74580 : if (m_sendGhost.empty())
2153 : 4395 : comlim_complete();
2154 : : else
2155 [ + + ]: 569715 : for(const auto& [cid, ghostdata] : m_sendGhost) {
2156 [ + - ][ + - ]: 499530 : std::vector< std::size_t > tetid( ghostdata.size() );
2157 [ + - ][ + - ]: 999060 : std::vector< std::vector< tk::real > > u( ghostdata.size() ),
[ - - ]
2158 [ + - ]: 999060 : prim( ghostdata.size() );
2159 : : std::vector< std::size_t > ndof;
2160 : : std::size_t j = 0;
2161 [ + + ]: 19317180 : for(const auto& i : ghostdata) {
2162 : : Assert( i < m_fd.Esuel().size()/4, "Sending limiter ghost data" );
2163 [ + - ]: 18817650 : tetid[j] = i;
2164 [ + - ][ - + ]: 18817650 : u[j] = m_u[i];
2165 [ + - ][ - + ]: 18817650 : prim[j] = m_p[i];
2166 [ + + ][ + + ]: 18817650 : if (pref && m_stage == 0) ndof.push_back( m_ndof[i] );
[ + - ]
2167 : 18817650 : ++j;
2168 : : }
2169 [ + - ][ + - ]: 999060 : thisProxy[ cid ].comlim( thisIndex, tetid, u, prim, ndof );
[ + + ][ - - ]
2170 : : }
2171 : :
2172 : 74580 : ownlim_complete();
2173 : 74580 : }
2174 : :
2175 : : void
2176 : 1770 : DG::propagate_ndof()
2177 : : // *****************************************************************************
2178 : : // p-refine all elements that are adjacent to p-refined elements
2179 : : //! \details This function p-refines all the neighbors of an element that has
2180 : : //! been p-refined as a result of an error indicator.
2181 : : // *****************************************************************************
2182 : : {
2183 : : const auto& esuf = m_fd.Esuf();
2184 : :
2185 : : // Copy number of degrees of freedom for each cell
2186 : 1770 : auto ndof = m_ndof;
2187 : :
2188 : : // p-refine all neighboring elements of elements that have been p-refined as a
2189 : : // result of error indicators
2190 [ + + ]: 753210 : for( auto f=m_fd.Nbfac(); f<esuf.size()/2; ++f )
2191 : : {
2192 [ + + ]: 751440 : std::size_t el = static_cast< std::size_t >(esuf[2*f]);
2193 : 751440 : std::size_t er = static_cast< std::size_t >(esuf[2*f+1]);
2194 : :
2195 [ + + ]: 751440 : if (m_ndof[el] > m_ndof[er])
2196 : 5078 : ndof[er] = m_ndof[el];
2197 : :
2198 [ + + ]: 751440 : if (m_ndof[el] < m_ndof[er])
2199 : 4865 : ndof[el] = m_ndof[er];
2200 : : }
2201 : :
2202 : : // Update number of degrees of freedom for each cell
2203 [ + - ]: 1770 : m_ndof = ndof;
2204 : 1770 : }
2205 : :
2206 : : void
2207 : 499530 : DG::comlim( int fromch,
2208 : : const std::vector< std::size_t >& tetid,
2209 : : const std::vector< std::vector< tk::real > >& u,
2210 : : const std::vector< std::vector< tk::real > >& prim,
2211 : : const std::vector< std::size_t >& ndof )
2212 : : // *****************************************************************************
2213 : : // Receive chare-boundary limiter ghost data from neighboring chares
2214 : : //! \param[in] fromch Sender chare id
2215 : : //! \param[in] tetid Ghost tet ids we receive solution data for
2216 : : //! \param[in] u Limited high-order solution
2217 : : //! \param[in] prim Limited high-order primitive quantities
2218 : : //! \param[in] ndof Number of degrees of freedom for chare-boundary elements
2219 : : //! \details This function receives contributions to the limited solution from
2220 : : //! fellow chares.
2221 : : // *****************************************************************************
2222 : : {
2223 : : Assert( u.size() == tetid.size(), "Size mismatch in DG::comlim()" );
2224 : : Assert( prim.size() == tetid.size(), "Size mismatch in DG::comlim()" );
2225 : :
2226 : 499530 : const auto pref = g_inputdeck.get< tag::pref, tag::pref >();
2227 : :
2228 : : if (pref && m_stage == 0)
2229 : : Assert( ndof.size() == tetid.size(), "Size mismatch in DG::comlim()" );
2230 : :
2231 : : // Find local-to-ghost tet id map for sender chare
2232 : : const auto& n = tk::cref_find( m_ghost, fromch );
2233 : :
2234 [ + + ]: 19317180 : for (std::size_t i=0; i<tetid.size(); ++i) {
2235 : 18817650 : auto j = tk::cref_find( n, tetid[i] );
2236 : : Assert( j >= m_fd.Esuel().size()/4, "Receiving solution non-ghost data" );
2237 : 18817650 : auto b = tk::cref_find( m_bid, j );
2238 : : Assert( b < m_uc[2].size(), "Indexing out of bounds" );
2239 : : Assert( b < m_pc[2].size(), "Indexing out of bounds" );
2240 : 18817650 : m_uc[2][b] = u[i];
2241 : 18817650 : m_pc[2][b] = prim[i];
2242 [ + + ][ + + ]: 18817650 : if (pref && m_stage == 0) {
2243 : : Assert( b < m_ndofc[2].size(), "Indexing out of bounds" );
2244 : 395110 : m_ndofc[2][b] = ndof[i];
2245 : : }
2246 : : }
2247 : :
2248 : : // if we have received all solution ghost contributions from neighboring
2249 : : // chares (chares we communicate along chare-boundary faces with), and
2250 : : // contributed our solution to these neighbors, proceed to limiting
2251 [ + + ]: 499530 : if (++m_nlim == m_sendGhost.size()) {
2252 : 70185 : m_nlim = 0;
2253 : 70185 : comlim_complete();
2254 : : }
2255 : 499530 : }
2256 : :
2257 : : void
2258 : 74580 : DG::dt()
2259 : : // *****************************************************************************
2260 : : // Compute time step size
2261 : : // *****************************************************************************
2262 : : {
2263 : 74580 : const auto pref = g_inputdeck.get< tag::pref, tag::pref >();
2264 : 74580 : auto d = Disc();
2265 : :
2266 : :
2267 : : // Combine own and communicated contributions of limited solution and degrees
2268 : : // of freedom in cells (if p-adaptive)
2269 [ + + ]: 18892230 : for (const auto& b : m_bid) {
2270 : : Assert( m_uc[2][b.second].size() == m_u.nprop(), "ncomp size mismatch" );
2271 : : Assert( m_pc[2][b.second].size() == m_p.nprop(), "ncomp size mismatch" );
2272 [ + + ]: 168233925 : for (std::size_t c=0; c<m_u.nprop(); ++c) {
2273 : 149416275 : m_u(b.first,c,0) = m_uc[2][b.second][c];
2274 : : }
2275 [ + + ]: 38425875 : for (std::size_t c=0; c<m_p.nprop(); ++c) {
2276 : 19608225 : m_p(b.first,c,0) = m_pc[2][b.second][c];
2277 : : }
2278 [ + + ][ + + ]: 18817650 : if (pref && m_stage == 0) {
2279 : 395110 : m_ndof[ b.first ] = m_ndofc[2][ b.second ];
2280 : : }
2281 : : }
2282 : :
2283 : 74580 : auto mindt = std::numeric_limits< tk::real >::max();
2284 : :
2285 [ + + ]: 74580 : if (m_stage == 0)
2286 : : {
2287 : 24860 : auto const_dt = g_inputdeck.get< tag::discr, tag::dt >();
2288 : 24860 : auto def_const_dt = g_inputdeck_defaults.get< tag::discr, tag::dt >();
2289 : : auto eps = std::numeric_limits< tk::real >::epsilon();
2290 : :
2291 : : // use constant dt if configured
2292 [ + + ]: 24860 : if (std::abs(const_dt - def_const_dt) > eps) {
2293 : :
2294 : 22415 : mindt = const_dt;
2295 : :
2296 : : } else { // compute dt based on CFL
2297 : :
2298 : : // find the minimum dt across all PDEs integrated
2299 [ + + ]: 4890 : for (const auto& eq : g_dgpde) {
2300 : : auto eqdt =
2301 : 2445 : eq.dt( m_coord, m_inpoel, m_fd, m_geoFace, m_geoElem, m_ndof,
2302 : 2445 : m_u, m_p, m_fd.Esuel().size()/4 );
2303 [ + - ]: 2445 : if (eqdt < mindt) mindt = eqdt;
2304 : : }
2305 : :
2306 : 2445 : mindt *= g_inputdeck.get< tag::discr, tag::cfl >();
2307 : : }
2308 : : }
2309 : : else
2310 : : {
2311 : 49720 : mindt = d->Dt();
2312 : : }
2313 : :
2314 : : // Resize the buffer vector of nodal extrema
2315 : 74580 : resizeNodalExtremac();
2316 : :
2317 : : // Contribute to minimum dt across all chares then advance to next step
2318 [ + - ]: 74580 : contribute( sizeof(tk::real), &mindt, CkReduction::min_double,
2319 : 74580 : CkCallback(CkReductionTarget(DG,solve), thisProxy) );
2320 : 74580 : }
2321 : :
2322 : : void
2323 : 74580 : DG::solve( tk::real newdt )
2324 : : // *****************************************************************************
2325 : : // Compute right-hand side of discrete transport equations
2326 : : //! \param[in] newdt Size of this new time step
2327 : : // *****************************************************************************
2328 : : {
2329 : : // Enable SDAG wait for building the solution vector during the next stage
2330 [ + - ]: 74580 : thisProxy[ thisIndex ].wait4sol();
2331 [ + - ]: 74580 : thisProxy[ thisIndex ].wait4reco();
2332 [ + - ]: 74580 : thisProxy[ thisIndex ].wait4nodalExtrema();
2333 [ + - ]: 74580 : thisProxy[ thisIndex ].wait4lim();
2334 [ + - ]: 74580 : thisProxy[ thisIndex ].wait4nod();
2335 : :
2336 : 74580 : auto d = Disc();
2337 : 74580 : const auto rdof = g_inputdeck.get< tag::discr, tag::rdof >();
2338 : 74580 : const auto ndof = g_inputdeck.get< tag::discr, tag::ndof >();
2339 : 74580 : const auto neq = m_u.nprop()/rdof;
2340 : :
2341 : : // Set new time step size
2342 [ + + ]: 74580 : if (m_stage == 0) d->setdt( newdt );
2343 : :
2344 : 74580 : const auto pref = g_inputdeck.get< tag::pref, tag::pref >();
2345 [ + + ][ + + ]: 74580 : if (pref && m_stage == 0)
2346 : : {
2347 : : // When the element are coarsened, high order terms should be zero
2348 [ + + ]: 809600 : for(std::size_t e = 0; e < m_nunk; e++)
2349 : : {
2350 : 807830 : const auto ncomp= m_u.nprop()/rdof;
2351 [ + + ]: 807830 : if(m_ndof[e] == 1)
2352 : : {
2353 [ + + ]: 3824190 : for (std::size_t c=0; c<ncomp; ++c)
2354 : : {
2355 : 3186825 : auto mark = c*rdof;
2356 : 3186825 : m_u(e, mark+1, 0) = 0.0;
2357 : 3186825 : m_u(e, mark+2, 0) = 0.0;
2358 : 3186825 : m_u(e, mark+3, 0) = 0.0;
2359 : : }
2360 [ + + ]: 170465 : } else if(m_ndof[e] == 4)
2361 : : {
2362 [ + + ]: 498132 : for (std::size_t c=0; c<ncomp; ++c)
2363 : : {
2364 : 415110 : auto mark = c*ndof;
2365 : 415110 : m_u(e, mark+4, 0) = 0.0;
2366 : 415110 : m_u(e, mark+5, 0) = 0.0;
2367 : 415110 : m_u(e, mark+6, 0) = 0.0;
2368 : 415110 : m_u(e, mark+7, 0) = 0.0;
2369 : 415110 : m_u(e, mark+8, 0) = 0.0;
2370 : 415110 : m_u(e, mark+9, 0) = 0.0;
2371 : : }
2372 : : }
2373 : : }
2374 : : }
2375 : :
2376 : : // Update Un
2377 [ + + ]: 74580 : if (m_stage == 0) m_un = m_u;
2378 : :
2379 [ + + ]: 149160 : for (const auto& eq : g_dgpde)
2380 : 74580 : eq.rhs( d->T(), m_geoFace, m_geoElem, m_fd, m_inpoel, m_boxelems, m_coord,
2381 : 74580 : m_u, m_p, m_ndof, m_rhs );
2382 : :
2383 : : // Explicit time-stepping using RK3 to discretize time-derivative
2384 [ + + ]: 54466800 : for(std::size_t e=0; e<m_nunk; ++e)
2385 [ + + ]: 170108760 : for(std::size_t c=0; c<neq; ++c)
2386 : : {
2387 [ + + ]: 457366830 : for (std::size_t k=0; k<m_numEqDof[c]; ++k)
2388 : : {
2389 : 341650290 : auto rmark = c*rdof+k;
2390 : 341650290 : auto mark = c*ndof+k;
2391 [ + + ]: 341650290 : m_u(e, rmark, 0) = rkcoef[0][m_stage] * m_un(e, rmark, 0)
2392 : 341650290 : + rkcoef[1][m_stage] * ( m_u(e, rmark, 0)
2393 : 341650290 : + d->Dt() * m_rhs(e, mark, 0)/m_lhs(e, mark, 0) );
2394 [ + + ]: 341650290 : if(fabs(m_u(e, rmark, 0)) < 1e-16)
2395 : 170243229 : m_u(e, rmark, 0) = 0;
2396 : : }
2397 : : // zero out unused/reconstructed dofs of equations using reduced dofs
2398 : : // (see DGMultiMat::numEquationDofs())
2399 [ + + ]: 115716540 : if (m_numEqDof[c] < rdof) {
2400 [ + + ]: 66551100 : for (std::size_t k=m_numEqDof[c]; k<rdof; ++k)
2401 : : {
2402 : 49913325 : auto rmark = c*rdof+k;
2403 : 49913325 : m_u(e, rmark, 0) = 0.0;
2404 : : }
2405 : : }
2406 : : }
2407 : :
2408 : : // Update primitives based on the evolved solution
2409 [ + + ]: 149160 : for (const auto& eq : g_dgpde)
2410 : : {
2411 : 74580 : eq.updateInterfaceCells( m_u, m_fd.Esuel().size()/4, m_ndof );
2412 : 74580 : eq.updatePrimitives( m_u, m_lhs, m_geoElem, m_p, m_fd.Esuel().size()/4 );
2413 : 74580 : eq.cleanTraceMaterial( m_geoElem, m_u, m_p, m_fd.Esuel().size()/4 );
2414 : : }
2415 : :
2416 [ + + ]: 74580 : if (m_stage < 2) {
2417 : :
2418 : : // continue with next time step stage
2419 : 49720 : stage();
2420 : :
2421 : : } else {
2422 : :
2423 : : // Compute diagnostics, e.g., residuals
2424 : 24860 : auto diag_computed = m_diag.compute( *d, m_u.nunk()-m_fd.Esuel().size()/4,
2425 : 24860 : m_geoElem, m_ndof, m_u );
2426 : :
2427 : : // Increase number of iterations and physical time
2428 : 24860 : d->next();
2429 : :
2430 : : // Continue to mesh refinement (if configured)
2431 [ + + ][ + - ]: 36612 : if (!diag_computed) refine( std::vector< tk::real >( m_u.nprop(), 0.0 ) );
[ + - ]
2432 : :
2433 : : }
2434 : 74580 : }
2435 : :
2436 : : void
2437 : 24860 : DG::refine( [[maybe_unused]] const std::vector< tk::real >& l2res )
2438 : : // *****************************************************************************
2439 : : // Optionally refine/derefine mesh
2440 : : //! \param[in] l2res L2-norms of the residual for each scalar component
2441 : : //! computed across the whole problem
2442 : : // *****************************************************************************
2443 : : {
2444 : 24860 : auto d = Disc();
2445 : :
2446 : 24860 : auto dtref = g_inputdeck.get< tag::amr, tag::dtref >();
2447 : 24860 : auto dtfreq = g_inputdeck.get< tag::amr, tag::dtfreq >();
2448 : :
2449 : : // if t>0 refinement enabled and we hit the dtref frequency
2450 [ + + ][ + + ]: 24860 : if (dtref && !(d->It() % dtfreq)) { // refine
2451 : :
2452 : 108 : d->startvol();
2453 [ + - ][ + - ]: 216 : d->Ref()->dtref( m_fd.Bface(), {}, tk::remap(m_fd.Triinpoel(),d->Gid()) );
[ - - ]
2454 : 108 : d->refined() = 1;
2455 : :
2456 : : } else { // do not refine
2457 : :
2458 : 24752 : d->refined() = 0;
2459 : 24752 : stage();
2460 : :
2461 : : }
2462 : 24860 : }
2463 : :
2464 : : void
2465 : 108 : DG::resizePostAMR(
2466 : : const std::vector< std::size_t >& /*ginpoel*/,
2467 : : const tk::UnsMesh::Chunk& chunk,
2468 : : const tk::UnsMesh::Coords& coord,
2469 : : const std::unordered_map< std::size_t, tk::UnsMesh::Edge >& /*addedNodes*/,
2470 : : const std::unordered_map< std::size_t, std::size_t >& addedTets,
2471 : : const std::set< std::size_t >& /*removedNodes*/,
2472 : : const tk::NodeCommMap& nodeCommMap,
2473 : : const std::map< int, std::vector< std::size_t > >& bface,
2474 : : const std::map< int, std::vector< std::size_t > >& /* bnode */,
2475 : : const std::vector< std::size_t >& triinpoel )
2476 : : // *****************************************************************************
2477 : : // Receive new mesh from Refiner
2478 : : //! \param[in] chunk New mesh chunk (connectivity and global<->local id maps)
2479 : : //! \param[in] coord New mesh node coordinates
2480 : : //! \param[in] addedTets Newly added mesh cells and their parents (local ids)
2481 : : //! \param[in] nodeCommMap New node communication map
2482 : : //! \param[in] bface Boundary-faces mapped to side set ids
2483 : : //! \param[in] triinpoel Boundary-face connectivity
2484 : : // *****************************************************************************
2485 : : {
2486 : 108 : auto d = Disc();
2487 : :
2488 : : // Set flag that indicates that we are during time stepping
2489 : 108 : m_initial = 0;
2490 : :
2491 : : // Zero field output iteration count between two mesh refinement steps
2492 : 108 : d->Itf() = 0;
2493 : :
2494 : : // Increase number of iterations with mesh refinement
2495 : 108 : ++d->Itr();
2496 : :
2497 : : // Save old number of elements
2498 : : [[maybe_unused]] auto old_nelem = m_inpoel.size()/4;
2499 : :
2500 : : // Resize mesh data structures
2501 : 108 : d->resizePostAMR( chunk, coord, nodeCommMap );
2502 : :
2503 : : // Update state
2504 : 108 : m_inpoel = d->Inpoel();
2505 : : m_coord = d->Coord();
2506 : 108 : auto nelem = m_inpoel.size()/4;
2507 : : m_p.resize( nelem );
2508 : : m_u.resize( nelem );
2509 : : m_un.resize( nelem );
2510 : : m_lhs.resize( nelem );
2511 : : m_rhs.resize( nelem );
2512 [ + - ][ + - ]: 108 : m_uNodalExtrm.resize( Disc()->Bid().size(), std::vector<tk::real>( 2*
2513 [ + - ]: 108 : m_ndof_NodalExtrm*g_inputdeck.get< tag::component >().nprop() ) );
2514 [ + - ][ + - ]: 108 : m_pNodalExtrm.resize( Disc()->Bid().size(), std::vector<tk::real>( 2*
2515 [ + - ]: 108 : m_ndof_NodalExtrm*m_p.nprop()/g_inputdeck.get< tag::discr, tag::rdof >()));
2516 : :
2517 : : // Resize the buffer vector of nodal extrema
2518 : 108 : resizeNodalExtremac();
2519 : :
2520 [ + - ][ + - ]: 216 : m_fd = FaceData( m_inpoel, bface, tk::remap(triinpoel,d->Lid()) );
2521 : :
2522 : : m_geoFace =
2523 : 108 : tk::Fields( tk::genGeoFaceTri( m_fd.Nipfac(), m_fd.Inpofa(), coord ) );
2524 : 108 : m_geoElem = tk::Fields( tk::genGeoElemTet( m_inpoel, coord ) );
2525 : :
2526 : 108 : m_nfac = m_fd.Inpofa().size()/3;
2527 : 108 : m_nunk = nelem;
2528 : 108 : m_npoin = coord[0].size();
2529 : : m_bndFace.clear();
2530 : : m_exptGhost.clear();
2531 : : m_sendGhost.clear();
2532 : : m_ghost.clear();
2533 : : m_esup.clear();
2534 : :
2535 : : // Update solution on new mesh, P0 (cell center value) only for now
2536 : : m_un = m_u;
2537 : : auto pn = m_p;
2538 : 108 : auto unprop = m_u.nprop();
2539 : : auto pnprop = m_p.nprop();
2540 [ + + ]: 64620 : for (const auto& [child,parent] : addedTets) {
2541 : : Assert( child < nelem, "Indexing out of new solution vector" );
2542 : : Assert( parent < old_nelem, "Indexing out of old solution vector" );
2543 [ + + ]: 129024 : for (std::size_t i=0; i<unprop; ++i) m_u(child,i,0) = m_un(parent,i,0);
2544 [ - + ]: 64512 : for (std::size_t i=0; i<pnprop; ++i) m_p(child,i,0) = pn(parent,i,0);
2545 : : }
2546 : : m_un = m_u;
2547 : :
2548 : : // Enable SDAG wait for setting up chare boundary faces
2549 [ + - ][ + - ]: 108 : thisProxy[ thisIndex ].wait4fac();
[ - - ]
2550 : :
2551 : : // Resize communication buffers
2552 [ + - ]: 108 : resizeComm();
2553 : 108 : }
2554 : :
2555 : : bool
2556 : 22434 : DG::fieldOutput() const
2557 : : // *****************************************************************************
2558 : : // Decide wether to output field data
2559 : : //! \return True if field data is output in this step
2560 : : // *****************************************************************************
2561 : : {
2562 : 22434 : auto d = Disc();
2563 : :
2564 : : // Output field data
2565 [ + + ][ + - ]: 22434 : return d->fielditer() or d->fieldtime() or d->fieldrange() or d->finished();
[ + - ][ - + ]
2566 : : }
2567 : :
2568 : : bool
2569 : 2764 : DG::refinedOutput() const
2570 : : // *****************************************************************************
2571 : : // Decide if we write field output using a refined mesh
2572 : : //! \return True if field output will use a refined mesh
2573 : : // *****************************************************************************
2574 : : {
2575 [ + + ]: 2764 : return g_inputdeck.get< tag::cmd, tag::io, tag::refined >() &&
2576 [ - + ]: 33 : g_inputdeck.get< tag::discr, tag::scheme >() != ctr::SchemeType::DG;
2577 : : }
2578 : :
2579 : : void
2580 : 2764 : DG::writeFields( CkCallback c )
2581 : : // *****************************************************************************
2582 : : // Output mesh field data
2583 : : //! \param[in] c Function to continue with after the write
2584 : : // *****************************************************************************
2585 : : {
2586 : 2764 : auto d = Disc();
2587 : :
2588 : : // Output time history
2589 [ + - ][ + - ]: 2764 : if (d->histiter() or d->histtime() or d->histrange()) {
[ - + ]
2590 : 0 : std::vector< std::vector< tk::real > > hist;
2591 [ - - ]: 0 : for (const auto& eq : g_dgpde) {
2592 [ - - ]: 0 : auto h = eq.histOutput( d->Hist(), m_inpoel, m_coord, m_u, m_p );
2593 [ - - ]: 0 : hist.insert( end(hist), begin(h), end(h) );
2594 : : }
2595 [ - - ]: 0 : d->history( std::move(hist) );
2596 : : }
2597 : :
2598 : : const auto& inpoel = std::get< 0 >( m_outmesh.chunk );
2599 : 5528 : auto esup = tk::genEsup( inpoel, 4 );
2600 : :
2601 : : // Combine own and communicated contributions and finish averaging of node
2602 : : // field output in chare boundary nodes
2603 : : const auto& lid = std::get< 2 >( m_outmesh.chunk );
2604 [ + + ]: 105140 : for (const auto& [g,f] : m_nodefieldsc) {
2605 : : Assert( m_nodefields.size() == f.first.size(), "Size mismatch" );
2606 : 102376 : auto p = tk::cref_find( lid, g );
2607 [ - + ]: 102376 : for (std::size_t i=0; i<f.first.size(); ++i) {
2608 : 0 : m_nodefields[i][p] += f.first[i];
2609 : 0 : m_nodefields[i][p] /= static_cast< tk::real >(
2610 : 0 : esup.second[p+1] - esup.second[p] + f.second );
2611 : : }
2612 : : }
2613 : 2764 : tk::destroy( m_nodefieldsc );
2614 : :
2615 : : // Lambda to decide if a node (global id) is on a chare boundary of the field
2616 : : // output mesh. p - global node id, return true if node is on the chare
2617 : : // boundary.
2618 : : auto chbnd = [ this ]( std::size_t p ) {
2619 : : return
2620 : : std::any_of( m_outmesh.nodeCommMap.cbegin(), m_outmesh.nodeCommMap.cend(),
2621 : : [&](const auto& s) { return s.second.find(p) != s.second.cend(); } );
2622 : : };
2623 : :
2624 : : // Finish computing node field output averages in internal nodes
2625 : 2764 : auto npoin = m_outmesh.coord[0].size();
2626 : : auto& gid = std::get< 1 >( m_outmesh.chunk );
2627 [ + + ]: 451828 : for (std::size_t p=0; p<npoin; ++p) {
2628 [ + + ]: 449064 : if (!chbnd(gid[p])) {
2629 : 346688 : auto n = static_cast< tk::real >( esup.second[p+1] - esup.second[p] );
2630 [ - + ]: 346688 : for (auto& f : m_nodefields) f[p] /= n;
2631 : : }
2632 : : }
2633 : :
2634 : : // Query fields names requested by user
2635 [ + - ]: 5528 : auto elemfieldnames = numericFieldNames( tk::Centering::ELEM );
2636 [ + - ]: 2764 : auto nodefieldnames = numericFieldNames( tk::Centering::NODE );
2637 : :
2638 : : // Collect field output names for analytical solutions
2639 [ + + ]: 5528 : for (const auto& eq : g_dgpde) {
2640 [ + - ]: 2764 : analyticFieldNames( eq, tk::Centering::ELEM, elemfieldnames );
2641 [ + - ]: 2764 : analyticFieldNames( eq, tk::Centering::NODE, nodefieldnames );
2642 : : }
2643 : :
2644 [ + + ]: 2764 : if (g_inputdeck.get< tag::pref, tag::pref >())
2645 [ + - ]: 804 : elemfieldnames.push_back( "NDOF" );
2646 : :
2647 [ + - ]: 2764 : elemfieldnames.push_back( "shock_marker" );
2648 : :
2649 : : Assert( elemfieldnames.size() == m_elemfields.size(), "Size mismatch" );
2650 : : Assert( nodefieldnames.size() == m_nodefields.size(), "Size mismatch" );
2651 : :
2652 : : // Output chare mesh and fields metadata to file
2653 : 2764 : const auto& triinpoel = m_outmesh.triinpoel;
2654 [ + - ][ + - ]: 8292 : d->write( inpoel, m_outmesh.coord, m_outmesh.bface, {},
[ + + ][ - - ]
2655 : 2764 : tk::remap( triinpoel, lid ), elemfieldnames, nodefieldnames,
2656 [ + - ]: 2764 : {}, m_elemfields, m_nodefields, {}, c );
2657 : 2764 : }
2658 : :
2659 : : void
2660 : 18686 : DG::comnodeout( const std::vector< std::size_t >& gid,
2661 : : const std::vector< std::size_t >& nesup,
2662 : : const std::vector< std::vector< tk::real > >& L )
2663 : : // *****************************************************************************
2664 : : // Receive chare-boundary nodal solution (for field output) contributions from
2665 : : // neighboring chares
2666 : : //! \param[in] gid Global mesh node IDs at which we receive contributions
2667 : : //! \param[in] nesup Number of elements surrounding points
2668 : : //! \param[in] L Partial contributions of node fields to chare-boundary nodes
2669 : : // *****************************************************************************
2670 : : {
2671 : : Assert( gid.size() == nesup.size(), "Size mismatch" );
2672 [ + - ]: 18686 : for (std::size_t f=0; f<L.size(); ++f)
2673 : : Assert( gid.size() == L[f].size(), "Size mismatch" );
2674 : :
2675 [ + + ]: 179040 : for (std::size_t i=0; i<gid.size(); ++i) {
2676 : : auto& nf = m_nodefieldsc[ gid[i] ];
2677 : 160354 : nf.first.resize( L.size() );
2678 [ - + ]: 160354 : for (std::size_t f=0; f<L.size(); ++f) nf.first[f] += L[f][i];
2679 : 160354 : nf.second += nesup[i];
2680 : : }
2681 : :
2682 : : // When we have heard from all chares we communicate with, this chare is done
2683 [ + + ]: 18686 : if (++m_nnod == Disc()->NodeCommMap().size()) {
2684 : 2564 : m_nnod = 0;
2685 : 2564 : comnodeout_complete();
2686 : : }
2687 : 18686 : }
2688 : :
2689 : : void
2690 : 74580 : DG::stage()
2691 : : // *****************************************************************************
2692 : : // Evaluate whether to continue with next time step stage
2693 : : // *****************************************************************************
2694 : : {
2695 : : // Increment Runge-Kutta stage counter
2696 : 74580 : ++m_stage;
2697 : :
2698 : : // if not all Runge-Kutta stages complete, continue to next time stage,
2699 : : // otherwise prepare for nodal field output
2700 [ + + ]: 74580 : if (m_stage < 3)
2701 : 49720 : next();
2702 : : else
2703 [ + - ][ + - ]: 74580 : startFieldOutput( CkCallback(CkIndex_DG::step(), thisProxy[thisIndex]) );
[ - + ][ - - ]
2704 : 74580 : }
2705 : :
2706 : : void
2707 : 23942 : DG::evalLB( int nrestart )
2708 : : // *****************************************************************************
2709 : : // Evaluate whether to do load balancing
2710 : : //! \param[in] nrestart Number of times restarted
2711 : : // *****************************************************************************
2712 : : {
2713 : 23942 : auto d = Disc();
2714 : :
2715 : : // Detect if just returned from a checkpoint and if so, zero timers
2716 : 23942 : d->restarted( nrestart );
2717 : :
2718 : 23942 : const auto lbfreq = g_inputdeck.get< tag::cmd, tag::lbfreq >();
2719 : 23942 : const auto nonblocking = g_inputdeck.get< tag::cmd, tag::nonblocking >();
2720 : :
2721 : : // Load balancing if user frequency is reached or after the second time-step
2722 [ + + ][ + + ]: 23942 : if ( (d->It()) % lbfreq == 0 || d->It() == 2 ) {
2723 : :
2724 : 20822 : AtSync();
2725 [ - + ]: 20822 : if (nonblocking) next();
2726 : :
2727 : : } else {
2728 : :
2729 : 3120 : next();
2730 : :
2731 : : }
2732 : 23942 : }
2733 : :
2734 : : void
2735 : 23942 : DG::evalRestart()
2736 : : // *****************************************************************************
2737 : : // Evaluate whether to save checkpoint/restart
2738 : : // *****************************************************************************
2739 : : {
2740 : 23942 : auto d = Disc();
2741 : :
2742 : 23942 : const auto rsfreq = g_inputdeck.get< tag::cmd, tag::rsfreq >();
2743 : 23942 : const auto benchmark = g_inputdeck.get< tag::cmd, tag::benchmark >();
2744 : :
2745 [ + + ][ - + ]: 23942 : if ( !benchmark && (d->It()) % rsfreq == 0 ) {
2746 : :
2747 : 0 : std::vector< std::size_t > meshdata{ /* finished = */ 0, d->MeshId() };
2748 [ - - ]: 0 : contribute( meshdata, CkReduction::nop,
2749 [ - - ][ - - ]: 0 : CkCallback(CkReductionTarget(Transporter,checkpoint), d->Tr()) );
2750 : :
2751 : : } else {
2752 : :
2753 : 23942 : evalLB( /* nrestart = */ -1 );
2754 : :
2755 : : }
2756 : 23942 : }
2757 : :
2758 : : void
2759 : 24860 : DG::step()
2760 : : // *****************************************************************************
2761 : : // Evaluate wether to continue with next time step
2762 : : // *****************************************************************************
2763 : : {
2764 : : // Free memory storing output mesh
2765 : 24860 : m_outmesh.destroy();
2766 : :
2767 : 24860 : auto d = Disc();
2768 : :
2769 : : // Output one-liner status report to screen
2770 : 24860 : d->status();
2771 : : // Reset Runge-Kutta stage counter
2772 : 24860 : m_stage = 0;
2773 : :
2774 : 24860 : const auto term = g_inputdeck.get< tag::discr, tag::term >();
2775 : 24860 : const auto nstep = g_inputdeck.get< tag::discr, tag::nstep >();
2776 : : const auto eps = std::numeric_limits< tk::real >::epsilon();
2777 : :
2778 : : // If neither max iterations nor max time reached, continue, otherwise finish
2779 [ + - ][ + + ]: 24860 : if (std::fabs(d->T()-term) > eps && d->It() < nstep) {
2780 : :
2781 : 23942 : evalRestart();
2782 : :
2783 : : } else {
2784 : :
2785 : 918 : auto meshid = d->MeshId();
2786 [ + - ]: 1836 : d->contribute( sizeof(std::size_t), &meshid, CkReduction::nop,
2787 : 918 : CkCallback(CkReductionTarget(Transporter,finish), d->Tr()) );
2788 : :
2789 : : }
2790 : 24860 : }
2791 : :
2792 : : #include "NoWarning/dg.def.h"
|