Branch data Line data Source code
1 : : // *****************************************************************************
2 : : /*!
3 : : \file src/Inciter/Ghosts.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 Definitions file for generating ghost data structures
9 : : \details Definitions file for asynchronous distributed
10 : : ghost data structures using Charm++.
11 : : */
12 : : // *****************************************************************************
13 : :
14 : : #include "Ghosts.hpp"
15 : : #include "DerivedData.hpp"
16 : : #include "Reorder.hpp"
17 : : #include "Around.hpp"
18 : : #include "ChareStateCollector.hpp"
19 : :
20 : : extern tk::CProxy_ChareStateCollector stateProxy;
21 : :
22 : : using inciter::Ghosts;
23 : :
24 : 1143 : Ghosts::Ghosts( const CProxy_Discretization& disc,
25 : : const std::map< int, std::vector< std::size_t > >& bface,
26 : : const std::vector< std::size_t >& triinpoel,
27 : : std::size_t nunk,
28 : 1143 : CkCallback cbDone ) :
29 : : m_disc( disc ),
30 : : m_nunk( nunk ),
31 : : m_inpoel( Disc()->Inpoel() ),
32 : 1143 : m_coord( Disc()->Coord() ),
33 [ + - ][ + - ]: 2286 : m_fd( m_inpoel, bface, tk::remap(triinpoel,Disc()->Lid()) ),
34 : : m_geoFace( tk::genGeoFaceTri( m_fd.Nipfac(), m_fd.Inpofa(), m_coord) ),
35 : : m_geoElem( tk::genGeoElemTet( m_inpoel, m_coord ) ),
36 [ + + ]: 1143 : m_nfac( m_fd.Inpofa().size()/3 ),
37 : : m_bndFace(),
38 : : m_sendGhost(),
39 : : m_ghost(),
40 : : m_exptGhost(),
41 : : m_bid(),
42 : : m_esup(),
43 : : m_initial( 1 ),
44 : : m_ncomfac( 0 ),
45 : : m_nadj( 0 ),
46 : : m_ncomEsup( 0 ),
47 : : m_ipface(),
48 : : m_ghostData(),
49 : : m_ghostReq( 0 ),
50 : : m_expChBndFace(),
51 : : m_infaces(),
52 : : m_esupc(),
53 [ + - ][ + - ]: 3429 : m_cbAfterDone( cbDone )
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + + ]
54 : : // *****************************************************************************
55 : : // Constructor
56 : : //! \param[in] disc Discretization proxy
57 : : //! \param[in] bface Boundary-faces mapped to side set ids
58 : : //! \param[in] triinpoel Boundary-face connectivity
59 : : //! \param[in] nunk Number of unknowns
60 : : //! \param[in] cbDone Function to continue with when Ghosts have been computed
61 : : // *****************************************************************************
62 : : {
63 [ + + ]: 1143 : if (g_inputdeck.get< tag::cmd, tag::chare >() ||
64 [ + + ]: 1105 : g_inputdeck.get< tag::cmd, tag::quiescence >())
65 [ + - ][ + - ]: 1304 : stateProxy.ckLocalBranch()->insert( "Ghosts", thisIndex, CkMyPe(), Disc()->It(),
[ + - ][ + - ]
[ - + ]
66 : : "Ghosts" );
67 : 1143 : }
68 : :
69 : : void
70 : 1143 : Ghosts::startCommSetup()
71 : : // *****************************************************************************
72 : : // Start setup of communication maps for cell-centered schemes
73 : : // *****************************************************************************
74 : : {
75 : : // Ensure that mesh partition is not leaky
76 : : Assert( !tk::leakyPartition(m_fd.Esuel(), m_inpoel, m_coord),
77 : : "Input mesh to Ghosts leaky" );
78 : :
79 : : // Ensure mesh physical boundary for the entire problem not leaky,
80 : : // effectively checking if the user has specified boundary conditions on all
81 : : // physical boundary faces
82 : 1143 : bndIntegral();
83 : 1143 : }
84 : :
85 : : void
86 : 1143 : Ghosts::bndIntegral()
87 : : // *****************************************************************************
88 : : // Compute partial boundary surface integral and sum across all chares
89 : : //! \details This function computes a partial surface integral over the boundary
90 : : //! of the faces of this mesh partition then sends its contribution to perform
91 : : //! the integral acorss the total problem boundary. After the global sum a
92 : : //! non-zero vector result indicates a leak, e.g., a hole in the boundary
93 : : //! which indicates an error in the boundary face data structures used to
94 : : //! compute the partial surface integrals.
95 : : // *****************************************************************************
96 : : {
97 : : // Storage for surface integral over our mesh chunk physical boundary
98 : 1143 : std::vector< tk::real > s{{ 0.0, 0.0, 0.0 }};
99 : :
100 : : // Integrate over all physical boundary faces
101 [ + + ]: 126221 : for (std::size_t f=0; f<m_fd.Nbfac(); ++f) {
102 : 125078 : s[0] += m_geoFace(f,0) * m_geoFace(f,1);
103 : 125078 : s[1] += m_geoFace(f,0) * m_geoFace(f,2);
104 : 125078 : s[2] += m_geoFace(f,0) * m_geoFace(f,3);
105 : : }
106 : :
107 [ + - ]: 1143 : s.push_back( 1.0 ); // positive: call-back to resizeComm() after reduction
108 [ + - ][ + - ]: 1143 : s.push_back( static_cast< tk::real >( Disc()->MeshId() ) );
109 : :
110 : : // Send contribution to host summing partial surface integrals
111 : 1143 : contribute( s, CkReduction::sum_double,
112 [ + - ][ + - ]: 3429 : CkCallback(CkReductionTarget(Transporter,bndint), Disc()->Tr()) );
[ + - ][ - - ]
113 : 1143 : }
114 : :
115 : : void
116 : 1143 : Ghosts::resizeComm()
117 : : // *****************************************************************************
118 : : // Start sizing communication buffers and setting up ghost data
119 : : // *****************************************************************************
120 : : {
121 : : // Enable SDAG wait for setting up chare boundary faces
122 [ + - ]: 1143 : thisProxy[ thisIndex ].wait4fac();
123 : :
124 : 1143 : auto d = Disc();
125 : :
126 : 1143 : const auto& gid = d->Gid();
127 : : const auto& inpofa = m_fd.Inpofa();
128 : : const auto& esuel = m_fd.Esuel();
129 : :
130 : : // Perform leak test on mesh partition
131 : : Assert( !tk::leakyPartition( esuel, m_inpoel, m_coord ),
132 : : "Mesh partition leaky" );
133 : :
134 : : // Activate SDAG waits for face adjacency map (ghost data) calculation
135 [ + - ]: 1143 : thisProxy[ thisIndex ].wait4ghost();
136 [ + - ]: 2286 : thisProxy[ thisIndex ].wait4esup();
137 : :
138 : : // Invert inpofa to enable searching for faces based on (global) node triplets
139 : : Assert( inpofa.size() % 3 == 0, "Inpofa must contain triplets" );
140 [ + + ]: 599270 : for (std::size_t f=0; f<inpofa.size()/3; ++f)
141 : 598127 : m_ipface.insert( {{{ gid[ inpofa[f*3+0] ],
142 : 598127 : gid[ inpofa[f*3+1] ],
143 : 598127 : gid[ inpofa[f*3+2] ] }}} );
144 : :
145 : : // At this point ipface has node-id-triplets (faces) on the internal
146 : : // chare-domain and on the physical boundary but not on chare boundaries,
147 : : // hence the name internal + physical boundary faces.
148 : :
149 : : // Build a set of faces (each face given by 3 global node IDs) associated to
150 : : // chares we potentially share boundary faces with.
151 : : tk::UnsMesh::FaceSet potbndface;
152 [ + + ]: 279047 : for (std::size_t e=0; e<esuel.size()/4; ++e) { // for all our tets
153 : 277904 : auto mark = e*4;
154 [ + + ]: 1389520 : for (std::size_t f=0; f<4; ++f) // for all tet faces
155 [ + + ]: 1111616 : if (esuel[mark+f] == -1) { // if face has no outside-neighbor tet
156 : : // if does not exist among the internal and physical boundary faces,
157 : : // store as a potential chare-boundary face
158 : 165518 : tk::UnsMesh::Face t{{ gid[ m_inpoel[ mark + tk::lpofa[f][0] ] ],
159 : 165518 : gid[ m_inpoel[ mark + tk::lpofa[f][1] ] ],
160 : 165518 : gid[ m_inpoel[ mark + tk::lpofa[f][2] ] ] }};
161 [ + + ]: 165518 : if (m_ipface.find(t) == end(m_ipface)) {
162 : : Assert( m_expChBndFace.insert(t).second,
163 : : "Store expected chare-boundary face" );
164 : : potbndface.insert( t );
165 : : }
166 : : }
167 : : }
168 : :
169 [ - + ][ - - ]: 1143 : if ( g_inputdeck.get< tag::cmd, tag::feedback >() ) d->Tr().chbndface();
170 : :
171 : : // In the following we assume that the size of the (potential) boundary-face
172 : : // adjacency map above does not necessarily equal to that of the node
173 : : // adjacency map. This is because while a node can be shared at a single
174 : : // corner or along an edge, that does not necessarily share a face as well
175 : : // (in other words, shared nodes or edges can exist that are not part of a
176 : : // shared face). So the chares we communicate with across faces are not
177 : : // necessarily the same as the chares we would communicate nodes with.
178 : : //
179 : : // Since the sizes of the node and face adjacency maps are not the same, while
180 : : // sending the faces on chare boundaries would be okay, however, the receiver
181 : : // would not necessarily know how many chares it must receive from. To solve
182 : : // this problem we send to chares which we share at least a single node with,
183 : : // i.e., rely on the node-adjacency map. Note that to all chares we share at
184 : : // least a single node with we send all our potential chare-boundary faces.
185 : : // This is the same list of faces to all chares we send.
186 : : //
187 : : // Another underlying assumption here is, of course, that the size of the face
188 : : // adjacency map is always smaller than or equal to that of the node adjacency
189 : : // map, which is always true. Since the receive side already knows how many
190 : : // fellow chares it must receive shared node ids from, we use that to detect
191 : : // completion of the number of receives in comfac(). This simplifies the
192 : : // communication pattern and code.
193 : :
194 : : // Send sets of faces adjacent to chare boundaries to fellow workers (if any)
195 [ + + ]: 1143 : if (d->NodeCommMap().empty()) // in serial, skip setting up ghosts altogether
196 [ + - ]: 39 : faceAdj();
197 : : else
198 : : // for all chares we share nodes with
199 [ + + ]: 12304 : for (const auto& c : d->NodeCommMap()) {
200 [ + - ][ + - ]: 22400 : thisProxy[ c.first ].comfac( thisIndex, potbndface );
201 : : }
202 : :
203 [ + - ]: 1143 : ownfac_complete();
204 : 1143 : }
205 : :
206 : : void
207 : 11200 : Ghosts::comfac( int fromch, const tk::UnsMesh::FaceSet& infaces )
208 : : // *****************************************************************************
209 : : // Receive unique set of faces we potentially share with/from another chare
210 : : //! \param[in] fromch Sender chare id
211 : : //! \param[in] infaces Unique set of faces we potentially share with fromch
212 : : // *****************************************************************************
213 : : {
214 [ + + ]: 11200 : if (g_inputdeck.get< tag::cmd, tag::chare >() ||
215 [ + + ]: 10734 : g_inputdeck.get< tag::cmd, tag::quiescence >())
216 [ + - ][ + - ]: 13112 : stateProxy.ckLocalBranch()->insert( "Ghosts", thisIndex, CkMyPe(), Disc()->It(),
[ + - ][ + - ]
[ - + ]
217 : : "comfac" );
218 : :
219 : : // Buffer up incoming data
220 : : m_infaces[ fromch ] = infaces;
221 : :
222 : : // if we have heard from all fellow chares that we share at least a single
223 : : // node, edge, or face with
224 [ + + ]: 11200 : if (++m_ncomfac == Disc()->NodeCommMap().size()) {
225 : 1104 : m_ncomfac = 0;
226 : 1104 : comfac_complete();
227 : : }
228 : 11200 : }
229 : :
230 : : void
231 : 1104 : Ghosts::bndFaces()
232 : : // *****************************************************************************
233 : : // Compute chare-boundary faces
234 : : //! \details This is called when both send and receives are completed on a
235 : : //! chare and thus we are ready to compute chare-boundary faces and ghost data.
236 : : // *****************************************************************************
237 : : {
238 : 1104 : auto d = Disc();
239 [ - + ]: 1104 : if ( g_inputdeck.get< tag::cmd, tag::feedback >() ) d->Tr().chcomfac();
240 : : const auto& esuel = m_fd.Esuel();
241 : 1104 : const auto& gid = d->Gid();
242 : :
243 [ + + ]: 12304 : for (const auto& in : m_infaces) {
244 : : // Find sender chare among chares we potentially share faces with. Note that
245 : : // it is feasible that a sender chare called us but we do not have a set of
246 : : // faces associated to that chare. This can happen if we only share a single
247 : : // node or an edge but not a face with that chare.
248 : 11200 : auto& bndface = m_bndFace[ in.first ]; // will associate to sender chare
249 : : // Try to find incoming faces on our chare boundary with other chares. If
250 : : // found, generate and assign new local face ID, associated to sender chare.
251 [ + + ]: 688388 : for (std::size_t e=0; e<esuel.size()/4; ++e) { // for all our tets
252 : 677188 : auto mark = e*4;
253 [ + + ]: 3385940 : for (std::size_t f=0; f<4; ++f) { // for all cell faces
254 [ + + ]: 2708752 : if (esuel[mark+f] == -1) { // if face has no outside-neighbor tet
255 : 695778 : tk::UnsMesh::Face t{{ gid[ m_inpoel[ mark + tk::lpofa[f][0] ] ],
256 : 695778 : gid[ m_inpoel[ mark + tk::lpofa[f][1] ] ],
257 : 695778 : gid[ m_inpoel[ mark + tk::lpofa[f][2] ] ] }};
258 : : // if found among the incoming faces and if not one of our internal
259 : : // nor physical boundary faces
260 [ + + ][ + - ]: 736218 : if ( in.second.find(t) != end(in.second) &&
261 : : m_ipface.find(t) == end(m_ipface) ) {
262 : 40440 : bndface[t][0] = m_nfac++; // assign new local face ID
263 : : }
264 : : }
265 : : }
266 : : }
267 : : // If at this point if we have not found any face among our faces we
268 : : // potentially share with fromch, there is no need to keep an empty set of
269 : : // faces associated to fromch as we only share nodes or edges with it, but
270 : : // not faces.
271 [ + + ]: 11200 : if (bndface.empty()) m_bndFace.erase( in.first );
272 : : }
273 : :
274 : 1104 : tk::destroy(m_ipface);
275 : 1104 : tk::destroy(m_infaces);
276 : :
277 : : // Ensure all expected faces have been received
278 : : Assert( receivedChBndFaces(),
279 : : "Expected and received chare boundary faces mismatch" );
280 : :
281 : : // Basic error checking on chare-boundary-face map
282 : : Assert( m_bndFace.find( thisIndex ) == m_bndFace.cend(),
283 : : "Face-communication map should not contain data for own chare ID" );
284 : :
285 : : // Store (local) tet ID adjacent to our chare boundary from the inside
286 [ + + ]: 150154 : for (std::size_t e=0; e<esuel.size()/4; ++e) { // for all our tets
287 : 149050 : auto mark = e*4;
288 [ + + ]: 745250 : for (std::size_t f=0; f<4; ++f) { // for all cell faces
289 [ + + ]: 596200 : if (esuel[mark+f] == -1) { // if face has no outside-neighbor tet
290 : 112216 : tk::UnsMesh::Face t{{ gid[ m_inpoel[ mark + tk::lpofa[f][0] ] ],
291 : 112216 : gid[ m_inpoel[ mark + tk::lpofa[f][1] ] ],
292 : 112216 : gid[ m_inpoel[ mark + tk::lpofa[f][2] ] ] }};
293 : 112216 : auto c = findchare(t);
294 [ + + ]: 112216 : if (c > -1) {
295 : : auto& lbndface = tk::ref_find( m_bndFace, c );
296 : : auto& face = tk::ref_find( lbndface, t );
297 : 40440 : face[1] = e; // store (local) inner tet ID adjacent to face
298 : : }
299 : : }
300 : : }
301 : : }
302 : :
303 : : // At this point m_bndFace is complete on this PE. This means that starting
304 : : // from the sets of faces we potentially share with fellow chares we now
305 : : // only have those faces we actually share faces with (through which we need
306 : : // to communicate later). Also, m_bndFace not only has the unique faces
307 : : // associated to fellow chares, but also a newly assigned local face ID as
308 : : // well as the local id of the inner tet adjacent to the face. Continue by
309 : : // starting setting up ghost data
310 : 1104 : setupGhost();
311 : : // Besides setting up our own ghost data, we also issue requests (for ghost
312 : : // data) to those chares which we share faces with. Note that similar to
313 : : // comfac() we are calling reqGhost() by going through the node communication
314 : : // map instead, which may send requests to those chare we do not share faces
315 : : // with. This is so that we can test for completing by querying the size of
316 : : // the already complete node commincation map in reqGhost. Requests in
317 : : // sendGhost will only be fullfilled based on m_ghostData.
318 [ + + ]: 12304 : for (const auto& c : d->NodeCommMap()) // for all chares we share nodes with
319 [ + - ]: 22400 : thisProxy[ c.first ].reqGhost();
320 : 1104 : }
321 : :
322 : : void
323 : 1104 : Ghosts::setupGhost()
324 : : // *****************************************************************************
325 : : // Setup own ghost data on this chare
326 : : // *****************************************************************************
327 : : {
328 : 1104 : auto d = Disc();
329 : 1104 : const auto& gid = d->Gid();
330 : :
331 : : // Enlarge elements surrounding faces data structure for ghosts
332 : 1104 : m_fd.Esuf().resize( 2*m_nfac, -2 );
333 : 1104 : m_fd.Inpofa().resize( 3*m_nfac, 0 );
334 : : // Enlarge face geometry data structure for ghosts
335 : 1104 : m_geoFace.resize( m_nfac, 0.0 );
336 : :
337 : : const auto& esuel = m_fd.Esuel();
338 : :
339 : : // Collect tet ids, their face connectivity (given by 3 global node IDs, each
340 : : // triplet for potentially multiple faces on the chare boundary), and their
341 : : // elem geometry data (see GhostData) associated to fellow chares adjacent to
342 : : // chare boundaries. Once received by fellow chares, these tets will become
343 : : // known as ghost elements and their data as ghost data.
344 [ + + ]: 150154 : for (std::size_t e=0; e<esuel.size()/4; ++e) { // for all our tets
345 : 149050 : auto mark = e*4;
346 [ + + ]: 745250 : for (std::size_t f=0; f<4; ++f) { // for all cell faces
347 [ + + ]: 596200 : if (esuel[mark+f] == -1) { // if face has no outside-neighbor tet
348 [ + - ]: 112216 : tk::UnsMesh::Face t{{ gid[ m_inpoel[ mark + tk::lpofa[f][0] ] ],
349 : 112216 : gid[ m_inpoel[ mark + tk::lpofa[f][1] ] ],
350 : 112216 : gid[ m_inpoel[ mark + tk::lpofa[f][2] ] ] }};
351 [ + - ]: 112216 : auto c = findchare(t);
352 : : // It is possible that we do not find the chare for this face. We are
353 : : // looping through all of our tets and interrogating all faces that do
354 : : // not have neighboring tets but we only care about chare-boundary faces
355 : : // here as only those need ghost data. (esuel may also contain
356 : : // physical boundary faces)
357 [ + + ]: 112216 : if (c > -1) {
358 : : // Will store ghost data associated to neighbor chare
359 : : auto& ghost = m_ghostData[ c ];
360 : : // Store tet id adjacent to chare boundary as key for ghost data
361 : : auto& tuple = ghost[ e ];
362 : : // If tetid e has not yet been encountered, store geometry (only once)
363 : : auto& nodes = std::get< 0 >( tuple );
364 [ + + ]: 40440 : if (nodes.empty()) {
365 [ + - ]: 32040 : std::get< 1 >( tuple ) = m_geoElem[ e ];
366 : :
367 : : auto& ncoord = std::get< 2 >( tuple );
368 : 32040 : ncoord[0] = m_coord[0][ m_inpoel[ mark+f ] ];
369 : 32040 : ncoord[1] = m_coord[1][ m_inpoel[ mark+f ] ];
370 : 32040 : ncoord[2] = m_coord[2][ m_inpoel[ mark+f ] ];
371 : :
372 : 32040 : std::get< 3 >( tuple ) = f;
373 : :
374 : 32040 : std::get< 4 >( tuple ) = {{ gid[ m_inpoel[ mark ] ],
375 : 32040 : gid[ m_inpoel[ mark+1 ] ],
376 : 32040 : gid[ m_inpoel[ mark+2 ] ],
377 : 32040 : gid[ m_inpoel[ mark+3 ] ] }};
378 : : }
379 : : // (Always) store face node IDs on chare boundary, even if tetid e has
380 : : // already been stored. Thus we store potentially multiple faces along
381 : : // the same chare-boundary. This happens, e.g., when the boundary
382 : : // between chares is zig-zaggy enough to have 2 or even 3 faces of the
383 : : // same tet.
384 [ + - ]: 40440 : nodes.push_back( t[0] );
385 [ + - ]: 40440 : nodes.push_back( t[1] );
386 [ + - ]: 40440 : nodes.push_back( t[2] );
387 : : Assert( nodes.size() <= 4*3, "Overflow of faces/tet to send" );
388 : : }
389 : : }
390 : : }
391 : : }
392 : :
393 : : // Basic error checking on local ghost data
394 : : Assert( m_ghostData.find( thisIndex ) == m_ghostData.cend(),
395 : : "Chare-node adjacency map should not contain data for own chare ID" );
396 : :
397 : : // More in-depth error checking on local ghost data
398 [ + + ]: 6686 : for (const auto& c : m_ghostData)
399 [ + + ]: 37622 : for ([[maybe_unused]] const auto& t : c.second) {
400 : : Assert( !std::get< 0 >( t.second ).empty(),
401 : : "Emtpy face vector in ghost data" );
402 : : Assert( std::get< 0 >( t.second ).size() % 3 == 0,
403 : : "Face node IDs must be triplets" );
404 : : Assert( std::get< 0 >( t.second ).size() <= 4*3, // <= 4*3 (4*numfaces)
405 : : "Max number of faces for a single ghost tet is 4" );
406 : : Assert( !std::get< 1 >( t.second ).empty(),
407 : : "No elem geometry data for ghost" );
408 : : Assert( std::get< 1 >( t.second ).size() == m_geoElem.nprop(),
409 : : "Elem geometry data for ghost must be for single tet" );
410 : : Assert( !std::get< 2 >( t.second ).empty(),
411 : : "No nodal coordinate data for ghost" );
412 : : }
413 : :
414 : 1104 : ownghost_complete();
415 : 1104 : }
416 : :
417 : : void
418 : 11200 : Ghosts::reqGhost()
419 : : // *****************************************************************************
420 : : // Receive requests for ghost data
421 : : // *****************************************************************************
422 : : {
423 [ + + ]: 11200 : if (g_inputdeck.get< tag::cmd, tag::chare >() ||
424 [ + + ]: 10734 : g_inputdeck.get< tag::cmd, tag::quiescence >())
425 [ + - ][ + - ]: 13112 : stateProxy.ckLocalBranch()->insert( "Ghosts", thisIndex, CkMyPe(), Disc()->It(),
[ + - ][ + - ]
[ - + ]
426 : : "reqGhost" );
427 : :
428 : : // If every chare we communicate with has requested ghost data from us, we may
429 : : // fulfill the requests, but only if we have already setup our ghost data.
430 [ + + ]: 11200 : if (++m_ghostReq == Disc()->NodeCommMap().size()) {
431 : 1104 : m_ghostReq = 0;
432 : 1104 : reqghost_complete();
433 : : }
434 : 11200 : }
435 : :
436 : : void
437 : 1104 : Ghosts::sendGhost()
438 : : // *****************************************************************************
439 : : // Send all of our ghost data to fellow chares
440 : : // *****************************************************************************
441 : : {
442 [ + + ]: 1104 : if (g_inputdeck.get< tag::cmd, tag::chare >() ||
443 [ + + ]: 1066 : g_inputdeck.get< tag::cmd, tag::quiescence >())
444 [ + - ][ + - ]: 1230 : stateProxy.ckLocalBranch()->insert( "Ghosts", thisIndex, CkMyPe(), Disc()->It(),
[ + - ][ + - ]
[ - + ]
445 : : "sendGhost" );
446 : :
447 [ + + ]: 6686 : for (const auto& c : m_ghostData)
448 [ + - ]: 11164 : thisProxy[ c.first ].comGhost( thisIndex, c.second );
449 : :
450 [ - + ]: 1104 : if ( g_inputdeck.get< tag::cmd, tag::feedback >() ) Disc()->Tr().chghost();
451 : 1104 : }
452 : :
453 : : void
454 : 5582 : Ghosts::comGhost( int fromch, const GhostData& ghost )
455 : : // *****************************************************************************
456 : : // Receive ghost data on chare boundaries from fellow chare
457 : : //! \param[in] fromch Caller chare ID
458 : : //! \param[in] ghost Ghost data, see Inciter/FaceData.h for the type
459 : : // *****************************************************************************
460 : : {
461 [ + + ]: 5582 : if (g_inputdeck.get< tag::cmd, tag::chare >() ||
462 [ + + ]: 5342 : g_inputdeck.get< tag::cmd, tag::quiescence >())
463 [ + - ][ + - ]: 6236 : stateProxy.ckLocalBranch()->insert( "Ghosts", thisIndex, CkMyPe(), Disc()->It(),
[ + - ][ + - ]
[ - + ]
464 : : "comGhost" );
465 : :
466 : 5582 : auto d = Disc();
467 : 5582 : const auto& lid = d->Lid();
468 : : auto& inpofa = m_fd.Inpofa();
469 : 5582 : auto ncoord = m_coord[0].size();
470 : :
471 : : // nodelist with fromch, currently only used for an assert
472 : : [[maybe_unused]] const auto& nl = tk::cref_find( d->NodeCommMap(), fromch );
473 : :
474 : : auto& ghostelem = m_ghost[ fromch ]; // will associate to sender chare
475 : :
476 : : // Store ghost data coming from chare
477 [ + + ]: 37622 : for (const auto& g : ghost) { // loop over incoming ghost data
478 : 32040 : auto e = g.first; // remote/ghost tet id outside of chare boundary
479 : : const auto& nodes = std::get< 0 >( g.second ); // node IDs of face(s)
480 : : const auto& geo = std::get< 1 >( g.second ); // ghost elem geometry data
481 : : const auto& coordg = std::get< 2 >( g.second ); // coordinate of ghost node
482 : : const auto& inpoelg = std::get< 4 >( g.second ); // inpoel of ghost tet
483 : :
484 : : Assert( nodes.size() % 3 == 0, "Face node IDs must be triplets" );
485 : : Assert( nodes.size() <= 4*3, "Overflow of faces/tet received" );
486 : : Assert( geo.size() % 5 == 0, "Ghost geometry size mismatch" );
487 : : Assert( geo.size() == m_geoElem.nprop(), "Ghost geometry number mismatch" );
488 : : Assert( coordg.size() == 3, "Incorrect ghost node coordinate size" );
489 : : Assert( inpoelg.size() == 4, "Incorrect ghost inpoel size" );
490 : :
491 [ + + ]: 72480 : for (std::size_t n=0; n<nodes.size()/3; ++n) { // face(s) of ghost e
492 : : // node IDs of face on chare boundary
493 : 40440 : tk::UnsMesh::Face t{{ nodes[n*3+0], nodes[n*3+1], nodes[n*3+2] }};
494 : : // must find t in nodelist of chare-boundary adjacent to fromch
495 : : Assert( nl.find(t[0]) != end(nl) &&
496 : : nl.find(t[1]) != end(nl) &&
497 : : nl.find(t[2]) != end(nl),
498 : : "Ghost face not found in chare-node adjacency map on receiving end" );
499 : : // must find face in boundary-face adjacency map for fromch
500 : : Assert( tk::cref_find(m_bndFace,fromch).find( t ) !=
501 : : tk::cref_find(m_bndFace,fromch).cend(), "Ghost face not "
502 : : "found in boundary-face adjacency map on receiving end" );
503 : : // find local face & tet ids for t
504 : 40440 : auto id = tk::cref_find( tk::cref_find(m_bndFace,fromch), t );
505 : : // compute face geometry for chare-boundary face
506 [ + - ]: 40440 : addGeoFace(t, id);
507 : : // add node-triplet to node-face connectivity
508 : 40440 : inpofa[3*id[0]+0] = tk::cref_find( lid, t[2] );
509 : 40440 : inpofa[3*id[0]+1] = tk::cref_find( lid, t[1] );
510 : 40440 : inpofa[3*id[0]+2] = tk::cref_find( lid, t[0] );
511 : :
512 : : // if ghost tet id not yet encountered on boundary with fromch
513 : : auto i = ghostelem.find( e );
514 [ + + ]: 40440 : if (i != end(ghostelem)) {
515 : : // fill in elements surrounding face
516 [ + - ]: 8400 : addEsuf(id, i->second);
517 : : // fill in elements surrounding element
518 [ + - ]: 8400 : addEsuel(id, i->second, t);
519 : : } else {
520 : : // fill in elements surrounding face
521 [ + - ]: 32040 : addEsuf(id, m_nunk);
522 : : // fill in elements surrounding element
523 [ + - ]: 32040 : addEsuel(id, m_nunk, t);
524 [ + - ]: 32040 : ghostelem[e] = m_nunk; // assign new local tet id to remote ghost id
525 [ + - ]: 32040 : m_geoElem.push_back( geo );// store ghost elem geometry
526 : 32040 : ++m_nunk; // increase number of unknowns on this chare
527 : : std::size_t counter = 0;
528 [ + + ]: 160200 : for (std::size_t gp=0; gp<4; ++gp) {
529 : : auto it = lid.find( inpoelg[gp] );
530 : : std::size_t lp;
531 [ + + ]: 128160 : if (it != end(lid))
532 : 106254 : lp = it->second;
533 : : else {
534 : : Assert( nodes.size() == 3, "Expected node not found in lid" );
535 : : Assert( gp == std::get< 3 >( g.second ),
536 : : "Ghost node not matching correct entry in ghost inpoel" );
537 : 21906 : lp = ncoord;
538 : 21906 : ++counter;
539 : : }
540 [ + - ]: 128160 : m_inpoel.push_back( lp ); // store ghost element connectivity
541 : : }
542 : : // only a single or no ghost node should be found
543 : : Assert( counter <= 1, "Incorrect number of ghost nodes detected. "
544 : : "Detected "+ std::to_string(counter) +" ghost nodes" );
545 [ + + ]: 32040 : if (counter == 1) {
546 [ + - ]: 21906 : m_coord[0].push_back( coordg[0] ); // store ghost node coordinate
547 [ + - ]: 21906 : m_coord[1].push_back( coordg[1] );
548 [ + - ]: 21906 : m_coord[2].push_back( coordg[2] );
549 : : Assert( m_inpoel[ 4*(m_nunk-1)+std::get< 3 >( g.second ) ] == ncoord,
550 : : "Mismatch in extended inpoel for ghost element" );
551 : 21906 : ++ncoord; // increase number of nodes on this chare
552 : : }
553 : : }
554 : :
555 : : // additional tests to ensure that entries in inpoel and t/inpofa match
556 : : Assert( nodetripletMatch(id, t) == 3,
557 : : "Mismatch/Overmatch in inpoel and inpofa at chare-boundary face" );
558 : : }
559 : : }
560 : :
561 : : // Signal the runtime system that all workers have received their
562 : : // face-adjacency
563 [ + + ]: 5582 : if (++m_nadj == m_ghostData.size()) faceAdj();
564 : 5582 : }
565 : :
566 : : void
567 : 1143 : Ghosts::faceAdj()
568 : : // *****************************************************************************
569 : : // Continue after face adjacency communication map completed on this chare
570 : : //! \details At this point the face communication map has been established
571 : : //! on this chare. Proceed to set up the nodal-comm map.
572 : : // *****************************************************************************
573 : : {
574 : 1143 : m_nadj = 0;
575 : :
576 : 1143 : tk::destroy(m_bndFace);
577 : :
578 : : // Ensure that all elements surrounding faces (are correct) including those at
579 : : // chare boundaries
580 [ + + ]: 639710 : for (std::size_t f=0; f<m_nfac; ++f) {
581 : : Assert( m_fd.Esuf()[2*f] > -1,
582 : : "Left element in esuf cannot be physical ghost" );
583 : : if (f >= m_fd.Nbfac())
584 : : Assert( m_fd.Esuf()[2*f+1] > -1,
585 : : "Right element in esuf for internal/chare faces cannot be a ghost" );
586 : : }
587 : :
588 : : // Ensure that all elements surrounding elements are correct including those
589 : : // at chare boundaries
590 : : const auto& esuel = m_fd.Esuel();
591 : : std::size_t nbound = 0;
592 [ + + ]: 279047 : for (std::size_t e=0; e<esuel.size()/4; ++e) {
593 : : for (std::size_t f=0; f<4; ++f)
594 : : if (esuel[4*e+f] == -1) ++nbound;
595 : : }
596 : : Assert( nbound == m_fd.Nbfac(), "Incorrect number of ghost-element -1's in "
597 : : "updated esuel" );
598 : :
599 : : // Error checking on ghost data
600 [ + + ]: 6725 : for(const auto& n : m_ghostData)
601 [ + + ]: 37622 : for([[maybe_unused]] const auto& i : n.second)
602 : : Assert( i.first < m_fd.Esuel().size()/4, "Sender contains ghost tet id " );
603 : :
604 : : // Perform leak test on face geometry data structure enlarged by ghosts
605 : : Assert( !leakyAdjacency(), "Face adjacency leaky" );
606 : : Assert( faceMatch(), "Chare-boundary element-face "
607 : : "connectivity (esuf) does not match" );
608 : :
609 : : // Create new map of elements along chare boundary which are ghosts for
610 : : // neighboring chare, associated with that chare ID
611 [ + + ]: 6725 : for (const auto& [cid, cgd] : m_ghostData)
612 : : {
613 : : auto& sg = m_sendGhost[cid];
614 [ + + ]: 37622 : for (const auto& e : cgd)
615 : : {
616 : : Assert(sg.find(e.first) == sg.end(), "Repeating element found in "
617 : : "ghost data");
618 : 32040 : sg.insert(e.first);
619 : : }
620 : : Assert(sg.size() == cgd.size(), "Incorrect size for sendGhost");
621 : : }
622 : : Assert(m_sendGhost.size() == m_ghostData.size(), "Incorrect number of "
623 : : "chares in sendGhost");
624 : :
625 : : // Error checking on ghost data
626 [ + + ]: 6725 : for(const auto& n : m_sendGhost)
627 [ + + ]: 37622 : for([[maybe_unused]] const auto& i : n.second)
628 : : Assert( i < m_fd.Esuel().size()/4, "Sender contains ghost tet id. " );
629 : :
630 : : // Generate and store Esup data-structure in a map
631 : 1143 : auto esup = tk::genEsup(m_inpoel, 4);
632 [ + - ][ + + ]: 99526 : for (std::size_t p=0; p<Disc()->Gid().size(); ++p)
633 : : {
634 [ + + ]: 1316253 : for (auto e : tk::Around(esup, p))
635 : : {
636 : : // since inpoel has been augmented with the face-ghost cell previously,
637 : : // esup also contains cells which are not on this mesh-chunk, hence the
638 : : // following test
639 [ + + ][ + - ]: 1217870 : if (e < m_fd.Esuel().size()/4) m_esup[p].push_back(e);
[ + - ]
640 : : }
641 : : }
642 : :
643 : : // Error checking on Esup map
644 [ + + ]: 99526 : for(const auto& p : m_esup)
645 : : for([[maybe_unused]] const auto& e : p.second)
646 : : Assert( e < m_fd.Esuel().size()/4, "Esup contains tet id greater than "
647 : : + std::to_string(m_fd.Esuel().size()/4-1) +" : "+ std::to_string(e) );
648 : :
649 [ + - ]: 1143 : auto meshid = Disc()->MeshId();
650 [ + - ]: 1143 : contribute( sizeof(std::size_t), &meshid, CkReduction::nop,
651 [ + - ][ + - ]: 3429 : CkCallback(CkReductionTarget(Transporter,startEsup), Disc()->Tr()) );
652 : 1143 : }
653 : :
654 : : void
655 : 1143 : Ghosts::nodeNeighSetup()
656 : : // *****************************************************************************
657 : : // Setup node-neighborhood (esup)
658 : : //! \details At this point the face-ghost communication map has been established
659 : : //! on this chare. This function begins generating the node-ghost comm map.
660 : : // *****************************************************************************
661 : : {
662 [ + + ]: 1143 : if (Disc()->NodeCommMap().empty())
663 : : // in serial, skip setting up node-neighborhood
664 : 39 : { comesup_complete(); }
665 : : else
666 : : {
667 : 1104 : const auto& nodeCommMap = Disc()->NodeCommMap();
668 : :
669 : : // send out node-neighborhood map
670 [ + + ]: 12304 : for (const auto& [cid, nlist] : nodeCommMap)
671 : : {
672 : : std::unordered_map< std::size_t, std::vector< std::size_t > > bndEsup;
673 : : std::unordered_map< std::size_t, std::vector< tk::real > > nodeBndCells;
674 [ + + ]: 76540 : for (const auto& p : nlist)
675 : : {
676 [ + - ]: 130680 : auto pl = tk::cref_find(Disc()->Lid(), p);
677 : : // fill in the esup for the chare-boundary
678 : : const auto& pesup = tk::cref_find(m_esup, pl);
679 [ + - ]: 65340 : bndEsup[p] = pesup;
680 : :
681 : : // fill a map with the element ids from esup as keys and geoElem as
682 : : // values, and another map containing these elements associated with
683 : : // the chare id with which they are node-neighbors.
684 [ + + ]: 387634 : for (const auto& e : pesup)
685 : : {
686 [ + - ][ + - ]: 644588 : nodeBndCells[e] = m_geoElem[e];
687 : :
688 : : // add these esup-elements into map of elements along chare boundary
689 : : Assert( e < m_fd.Esuel().size()/4, "Sender contains ghost tet id." );
690 : : m_sendGhost[cid].insert(e);
691 : : }
692 : : }
693 : :
694 [ + - ][ + - ]: 22400 : thisProxy[cid].comEsup(thisIndex, bndEsup, nodeBndCells);
695 : : }
696 : : }
697 : :
698 : 1143 : ownesup_complete();
699 : 1143 : }
700 : :
701 : : void
702 : 11200 : Ghosts::comEsup( int fromch,
703 : : const std::unordered_map< std::size_t, std::vector< std::size_t > >& bndEsup,
704 : : const std::unordered_map< std::size_t, std::vector< tk::real > >&
705 : : nodeBndCells )
706 : : // *****************************************************************************
707 : : //! \brief Receive elements-surrounding-points data-structure for points on
708 : : // common boundary between receiving and sending neighbor chare, and the
709 : : // element geometries for these new elements
710 : : //! \param[in] fromch Sender chare id
711 : : //! \param[in] bndEsup Elements-surrounding-points data-structure from fromch
712 : : //! \param[in] nodeBndCells Map containing element geometries associated with
713 : : //! remote element IDs in the esup
714 : : // *****************************************************************************
715 : : {
716 : : auto& chghost = m_ghost[fromch];
717 : :
718 : : // Extend remote-local element id map and element geometry array
719 [ + + ]: 180886 : for (const auto& e : nodeBndCells)
720 : : {
721 : : // need to check following, because 'e' could have been added previously in
722 : : // remote-local element id map as a part of face-communication, i.e. as a
723 : : // face-ghost element
724 [ + + ]: 339372 : if (chghost.find(e.first) == chghost.end())
725 : : {
726 : 137646 : chghost[e.first] = m_nunk;
727 : 137646 : m_geoElem.push_back(e.second);
728 : 137646 : ++m_nunk;
729 : : }
730 : : }
731 : :
732 : : // Store incoming data in comm-map buffer for Esup
733 [ + + ]: 76540 : for (const auto& [node, elist] : bndEsup)
734 : : {
735 [ + - ]: 65340 : auto pl = tk::cref_find(Disc()->Lid(), node);
736 [ + - ]: 65340 : auto& pesup = m_esupc[pl];
737 [ + + ]: 387634 : for (auto e : elist)
738 : : {
739 : 322294 : auto el = tk::cref_find(chghost, e);
740 [ + - ]: 322294 : pesup.push_back(el);
741 : : }
742 : : }
743 : :
744 : : // if we have heard from all fellow chares that we share at least a single
745 : : // node, edge, or face with
746 [ + + ]: 11200 : if (++m_ncomEsup == Disc()->NodeCommMap().size()) {
747 : 1104 : m_ncomEsup = 0;
748 : 1104 : comesup_complete();
749 : : }
750 : 11200 : }
751 : :
752 : : void
753 : 1143 : Ghosts::adj()
754 : : // *****************************************************************************
755 : : // Finish up with adjacency maps, and do a global-sync to begin problem setup
756 : : //! \details At this point, the nodal- and face-adjacency has been set up. This
757 : : // function does some error checking on the nodal-adjacency and prepares
758 : : // for problem setup.
759 : : // *****************************************************************************
760 : : {
761 : : // combine own and communicated contributions to elements surrounding points
762 [ + + ]: 33560 : for (auto& [p, elist] : m_esupc)
763 : : {
764 : : auto& pesup = tk::ref_find(m_esup, p);
765 : : for ([[maybe_unused]] auto e : elist)
766 : : {
767 : : Assert( e >= m_fd.Esuel().size()/4, "Non-ghost element received from "
768 : : "esup buffer." );
769 : : }
770 : 32417 : tk::concat< std::size_t >(std::move(elist), pesup);
771 : : }
772 : :
773 : 1143 : tk::destroy(m_ghostData);
774 : 1143 : tk::destroy(m_esupc);
775 : :
776 [ - + ]: 1143 : if ( g_inputdeck.get< tag::cmd, tag::feedback >() ) Disc()->Tr().chadj();
777 : :
778 : : // Error checking on ghost data
779 [ + + ]: 12343 : for(const auto& n : m_sendGhost)
780 [ + + ]: 180886 : for([[maybe_unused]] const auto& i : n.second)
781 : : Assert( i < m_fd.Esuel().size()/4, "Sender contains ghost tet id. ");
782 : :
783 : : // Create a mapping between local ghost tet ids and zero-based boundary ids
784 : 1143 : std::vector< std::size_t > c( tk::sumvalsize( m_ghost ) );
785 : : std::size_t j = 0;
786 [ + + ]: 12343 : for (const auto& n : m_ghost) {
787 [ + + ]: 180886 : for(const auto& i : n.second) {
788 : 169686 : c[j++] = i.second;
789 : : }
790 : : }
791 [ + - ]: 2286 : m_bid = tk::assignLid( c );
792 : :
793 : : // Basic error checking on ghost tet ID map
794 : : Assert( m_ghost.find( thisIndex ) == m_ghost.cend(),
795 : : "Ghost id map should not contain data for own chare ID" );
796 : :
797 : : // Store expected ghost tet IDs
798 [ + + ]: 12343 : for (const auto& n : m_ghost)
799 [ + + ]: 180886 : for ([[maybe_unused]] const auto& g : n.second)
800 : : Assert( m_exptGhost.insert( g.second ).second,
801 : : "Failed to store local tetid as exptected ghost id" );
802 : :
803 : : // Callback function from DG/FV after ghost-setup is done
804 [ + - ]: 1143 : m_cbAfterDone.send();
805 : 1143 : }
806 : :
807 : : bool
808 : 0 : Ghosts::leakyAdjacency()
809 : : // *****************************************************************************
810 : : // Perform leak-test on chare boundary faces
811 : : //! \details This function computes a surface integral over the boundary of the
812 : : //! faces after the face adjacency communication map is completed. A non-zero
813 : : //! vector result indicates a leak, e.g., a hole in the partition (covered by
814 : : //! the faces of the face adjacency communication map), which indicates an
815 : : //! error upstream in the code that sets up the face communication data
816 : : //! structures.
817 : : //! \note Compared to tk::leakyPartition() this function performs the leak-test
818 : : //! on the face geometry data structure enlarged by ghost faces on this
819 : : //! partition by computing a discrete surface integral considering the
820 : : //! physical and chare boundary faces, which should be equal to zero for a
821 : : //! closed domain.
822 : : //! \return True if our chare face adjacency leaks.
823 : : // *****************************************************************************
824 : : {
825 : : // Storage for surface integral over our chunk of the adjacency
826 : : std::array< tk::real, 3 > s{{ 0.0, 0.0, 0.0 }};
827 : :
828 : : // physical boundary faces
829 [ - - ]: 0 : for (std::size_t f=0; f<m_fd.Nbfac(); ++f) {
830 : 0 : s[0] += m_geoFace(f,0) * m_geoFace(f,1);
831 : 0 : s[1] += m_geoFace(f,0) * m_geoFace(f,2);
832 : 0 : s[2] += m_geoFace(f,0) * m_geoFace(f,3);
833 : : }
834 : :
835 : : // chare-boundary faces
836 [ - - ]: 0 : for (std::size_t f=m_fd.Nipfac(); f<m_fd.Esuf().size()/2; ++f) {
837 : 0 : s[0] += m_geoFace(f,0) * m_geoFace(f,1);
838 : 0 : s[1] += m_geoFace(f,0) * m_geoFace(f,2);
839 : 0 : s[2] += m_geoFace(f,0) * m_geoFace(f,3);
840 : : }
841 : :
842 : : auto eps = std::numeric_limits< tk::real >::epsilon() * 100;
843 [ - - ][ - - ]: 0 : return std::abs(s[0]) > eps || std::abs(s[1]) > eps || std::abs(s[2]) > eps;
[ - - ]
844 : : }
845 : :
846 : : bool
847 : 0 : Ghosts::faceMatch()
848 : : // *****************************************************************************
849 : : // Check if esuf of chare-boundary faces matches
850 : : //! \details This function checks each chare-boundary esuf entry for the left
851 : : //! and right elements. Then, it tries to match all vertices of these
852 : : //! elements. Exactly three of these vertices must match if the esuf entry
853 : : //! has been updated correctly at chare-boundaries.
854 : : //! \return True if chare-boundary faces match.
855 : : // *****************************************************************************
856 : : {
857 : : const auto& esuf = m_fd.Esuf();
858 : : bool match(true);
859 : :
860 : : auto eps = std::numeric_limits< tk::real >::epsilon() * 100;
861 : :
862 [ - - ]: 0 : for (auto f=m_fd.Nipfac(); f<esuf.size()/2; ++f)
863 : : {
864 : 0 : std::size_t el = static_cast< std::size_t >(esuf[2*f]);
865 : 0 : std::size_t er = static_cast< std::size_t >(esuf[2*f+1]);
866 : :
867 : : std::size_t count = 0;
868 : :
869 [ - - ]: 0 : for (std::size_t i=0; i<4; ++i)
870 : : {
871 : 0 : auto ip = m_inpoel[4*el+i];
872 [ - - ]: 0 : for (std::size_t j=0; j<4; ++j)
873 : : {
874 [ - - ]: 0 : auto jp = m_inpoel[4*er+j];
875 [ - - ]: 0 : auto xdiff = std::abs( m_coord[0][ip] - m_coord[0][jp] );
876 : 0 : auto ydiff = std::abs( m_coord[1][ip] - m_coord[1][jp] );
877 : 0 : auto zdiff = std::abs( m_coord[2][ip] - m_coord[2][jp] );
878 : :
879 [ - - ][ - - ]: 0 : if ( xdiff<=eps && ydiff<=eps && zdiff<=eps ) ++count;
[ - - ]
880 : : }
881 : : }
882 : :
883 : 0 : match = (match && count == 3);
884 : : }
885 : :
886 : 0 : return match;
887 : : }
888 : :
889 : : bool
890 : 0 : Ghosts::receivedChBndFaces()
891 : : // *****************************************************************************
892 : : // Verify that all chare-boundary faces have been received
893 : : //! \return True if all chare-boundary faces have been received
894 : : // *****************************************************************************
895 : : {
896 : 0 : const auto& lid = Disc()->Lid();
897 : : tk::UnsMesh::FaceSet recvBndFace;
898 : :
899 : : // Collect chare-boundary faces that have been received and expected
900 [ - - ]: 0 : for (const auto& c : m_bndFace)
901 [ - - ]: 0 : for (const auto& f : c.second)
902 [ - - ]: 0 : if (m_expChBndFace.find(f.first) != end(m_expChBndFace))
903 : : recvBndFace.insert(f.first);
904 : :
905 : : // Collect info on expected but not received faces
906 [ - - ]: 0 : std::stringstream msg;
907 [ - - ]: 0 : for (const auto& f : m_expChBndFace)
908 [ - - ]: 0 : if (recvBndFace.find(f) == end(recvBndFace)) {
909 : : const auto& x = m_coord[0];
910 : : const auto& y = m_coord[1];
911 : : const auto& z = m_coord[2];
912 : 0 : auto A = tk::cref_find( lid, f[0] );
913 : 0 : auto B = tk::cref_find( lid, f[1] );
914 [ - - ]: 0 : auto C = tk::cref_find( lid, f[2] );
915 [ - - ][ - - ]: 0 : msg << '{' << A << ',' << B << ',' << C << "}:("
[ - - ]
916 [ - - ][ - - ]: 0 : << x[A] << ',' << y[A] << ',' << z[A] << ' '
[ - - ]
917 [ - - ][ - - ]: 0 : << x[B] << ',' << y[B] << ',' << z[B] << ' '
[ - - ]
918 [ - - ][ - - ]: 0 : << x[C] << ',' << y[C] << ',' << z[C] << ") ";
[ - - ][ - - ]
919 : : }
920 : :
921 : 0 : tk::destroy( m_expChBndFace );
922 : :
923 : : // Error out with info on missing faces
924 : : auto s = msg.str();
925 [ - - ]: 0 : if (!s.empty()) {
926 [ - - ][ - - ]: 0 : Throw( "Ghosts chare " + std::to_string(thisIndex) +
[ - - ][ - - ]
[ - - ][ - - ]
[ - - ][ - - ]
[ - - ][ - - ]
[ - - ][ - - ]
[ - - ][ - - ]
927 : : " missing face(s) {local node ids} (node coords): " + s );
928 : : } else {
929 : 0 : return true;
930 : : }
931 : : }
932 : :
933 : : int
934 : 224432 : Ghosts::findchare( const tk::UnsMesh::Face& t )
935 : : // *****************************************************************************
936 : : // Find any chare for face (given by 3 global node IDs)
937 : : //! \param[in] t Face given by three global node IDs
938 : : //! \return Chare ID if found among any of the chares we communicate along
939 : : //! faces with, -1 if the face cannot be found.
940 : : // *****************************************************************************
941 : : {
942 [ + + ]: 834620 : for (const auto& cf : m_bndFace)
943 : : // cppcheck-suppress useStlAlgorithm
944 [ + + ]: 691068 : if (cf.second.find(t) != end(cf.second))
945 : 80880 : return cf.first;
946 : : return -1;
947 : : }
948 : :
949 : : std::size_t
950 : 0 : Ghosts::nodetripletMatch(
951 : : const std::array< std::size_t, 2 >& id,
952 : : const tk::UnsMesh::Face& t )
953 : : // *****************************************************************************
954 : : // Check if entries in inpoel, inpofa and node-triplet are consistent
955 : : //! \param[in] id Local face and (inner) tet id adjacent to it
956 : : //! \param[in] t node-triplet associated with the chare boundary face
957 : : //! \return number of nodes in inpoel that matched with t and inpofa
958 : : // *****************************************************************************
959 : : {
960 : : const auto& esuf = m_fd.Esuf();
961 : : const auto& inpofa = m_fd.Inpofa();
962 : 0 : const auto& lid = Disc()->Lid();
963 : :
964 : : std::size_t counter = 0;
965 [ - - ]: 0 : for (std::size_t k=0; k<4; ++k)
966 : : {
967 : 0 : auto el = esuf[ 2*id[0] ];
968 : 0 : auto ip = m_inpoel[ 4*static_cast< std::size_t >( el )+k ];
969 : : Assert( el == static_cast< int >( id[1] ), "Mismatch in id and esuf" );
970 [ - - ]: 0 : for (std::size_t j=0; j<3; ++j)
971 : : {
972 : 0 : auto jp = tk::cref_find( lid, t[j] );
973 [ - - ]: 0 : auto fp = inpofa[ 3*id[0]+(2-j) ];
974 [ - - ]: 0 : if (ip == jp && ip == fp) ++counter;
975 : : }
976 : : }
977 : :
978 : 0 : return counter;
979 : : }
980 : :
981 : : void
982 : 40440 : Ghosts::addEsuf(
983 : : const std::array< std::size_t, 2 >& id,
984 : : std::size_t ghostid )
985 : : // *****************************************************************************
986 : : // Fill elements surrounding a face along chare boundary
987 : : //! \param[in] id Local face and (inner) tet id adjacent to it
988 : : //! \param[in] ghostid Local ID for ghost tet
989 : : //! \details This function extends and fills in the elements surrounding faces
990 : : //! data structure (esuf) so that the left and right element id is filled
991 : : //! in correctly on chare boundaries to contain the correct inner tet id and
992 : : //! the local tet id for the outer (ghost) tet, both adjacent to the given
993 : : //! chare-face boundary. Prior to this function, this data structure does not
994 : : //! have yet face-element connectivity adjacent to chare-boundary faces, only
995 : : //! for physical boundaries and internal faces that are not on the chare
996 : : //! boundary (this latter purely as a result of mesh partitioning). The remote
997 : : //! element id of the ghost is stored in a location that is local to our own
998 : : //! esuf. The face numbering is such that esuf stores the element-face
999 : : //! connectivity first for the physical-boundary faces, followed by that of
1000 : : //! the internal faces, followed by the chare-boundary faces. As a result,
1001 : : //! esuf can be used by physics algorithms in exactly the same way as would be
1002 : : //! used in serial. In serial, of course, this data structure is not extended
1003 : : //! at the end by the chare-boundaries.
1004 : : // *****************************************************************************
1005 : : {
1006 : : auto& esuf = m_fd.Esuf();
1007 : :
1008 : : Assert( 2*id[0]+1 < esuf.size(), "Indexing out of esuf" );
1009 : :
1010 : : // put in inner tet id
1011 : : Assert( esuf[ 2*id[0] ] == -2 && esuf[ 2*id[0]+1 ] == -2, "Updating esuf at "
1012 : : "wrong location instead of chare-boundary" );
1013 : 40440 : esuf[ 2*id[0]+0 ] = static_cast< int >( id[1] );
1014 : : // put in local id for outer/ghost tet
1015 : 40440 : esuf[ 2*id[0]+1 ] = static_cast< int >( ghostid );
1016 : 40440 : }
1017 : :
1018 : : void
1019 : 40440 : Ghosts::addEsuel(
1020 : : const std::array< std::size_t, 2 >& id,
1021 : : std::size_t ghostid,
1022 : : const tk::UnsMesh::Face& t )
1023 : : // *****************************************************************************
1024 : : // Fill elements surrounding a element along chare boundary
1025 : : //! \param[in] id Local face and (inner) tet id adjacent to it
1026 : : //! \param[in] ghostid Local ID for ghost tet
1027 : : //! \param[in] t node-triplet associated with the chare boundary face
1028 : : //! \details This function updates the elements surrounding element (esuel) data
1029 : : // structure for the (inner) tets adjacent to the chare-boundaries. It fills
1030 : : // esuel of this inner tet with the local tet-id that has been assigned to
1031 : : // the outer ghost tet in Ghosts::comGhost in place of the -1 before.
1032 : : // *****************************************************************************
1033 : : {
1034 : 40440 : const auto& lid = Disc()->Lid();
1035 : : [[maybe_unused]] const auto& esuf = m_fd.Esuf();
1036 : : auto& esuel = m_fd.Esuel();
1037 : :
1038 : : std::array< tk::UnsMesh::Face, 4 > face;
1039 [ + + ]: 202200 : for (std::size_t f = 0; f<4; ++f)
1040 [ + + ]: 647040 : for (std::size_t i = 0; i<3; ++i)
1041 : 485280 : face[f][i] = m_inpoel[ id[1]*4 + tk::lpofa[f][i] ];
1042 : :
1043 : 40440 : tk::UnsMesh::Face tl{{ tk::cref_find( lid, t[0] ),
1044 : 40440 : tk::cref_find( lid, t[1] ),
1045 : 121320 : tk::cref_find( lid, t[2] ) }};
1046 : :
1047 : : std::size_t i(0), nmatch(0);
1048 [ + + ]: 202200 : for (const auto& f : face) {
1049 [ + + ]: 161760 : if (tk::UnsMesh::Eq< 3 >()( tl, f )) {
1050 : : Assert( esuel[ id[1]*4 + i ] == -1, "Incorrect boundary element found in "
1051 : : "esuel");
1052 : 40440 : esuel[ id[1]*4 + i ] = static_cast<int>(ghostid);
1053 : : ++nmatch;
1054 : : Assert( esuel[ id[1]*4 + i ] == esuf[ 2*id[0]+1 ], "Incorrect boundary "
1055 : : "element entered in esuel" );
1056 : : Assert( static_cast<int>(id[1]) == esuf[ 2*id[0]+0 ], "Boundary "
1057 : : "element entered in incorrect esuel location" );
1058 : : }
1059 : 161760 : ++i;
1060 : : }
1061 : :
1062 : : // ensure that exactly one face matched
1063 : : Assert( nmatch == 1, "Incorrect number of node-triplets (faces) matched for "
1064 : : "updating esuel; matching faces = "+ std::to_string(nmatch) );
1065 : 40440 : }
1066 : :
1067 : : void
1068 : 40440 : Ghosts::addGeoFace(
1069 : : const tk::UnsMesh::Face& t,
1070 : : const std::array< std::size_t, 2 >& id )
1071 : : // *****************************************************************************
1072 : : // Fill face-geometry data along chare boundary
1073 : : //! \param[in] t Face (given by 3 global node IDs) on the chare boundary
1074 : : //! \param[in] id Local face and (inner) tet id adjacent to face t
1075 : : //! \details This function fills in the face geometry data along a chare
1076 : : //! boundary.
1077 : : // *****************************************************************************
1078 : : {
1079 : 40440 : const auto& lid = Disc()->Lid();
1080 : :
1081 : : // get global node IDs reversing order to get outward-pointing normal
1082 : 40440 : auto A = tk::cref_find( lid, t[2] );
1083 : 40440 : auto B = tk::cref_find( lid, t[1] );
1084 : 40440 : auto C = tk::cref_find( lid, t[0] );
1085 : 40440 : auto geochf = tk::geoFaceTri( {{m_coord[0][A], m_coord[0][B], m_coord[0][C]}},
1086 : 40440 : {{m_coord[1][A], m_coord[1][B], m_coord[1][C]}},
1087 : 40440 : {{m_coord[2][A], m_coord[2][B], m_coord[2][C]}} );
1088 : :
1089 [ + + ]: 323520 : for (std::size_t i=0; i<7; ++i)
1090 : 283080 : m_geoFace(id[0],i) = geochf(0,i);
1091 : 40440 : }
1092 : :
1093 : : #include "NoWarning/ghosts.def.h"
|