Branch data Line data Source code
1 : : // *****************************************************************************
2 : : /*!
3 : : \file src/Inciter/Refiner.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 Mesh refiner for interfacing the mesh refinement library
9 : : \see Refiner.h for more info.
10 : : */
11 : : // *****************************************************************************
12 : :
13 : : #include <vector>
14 : : #include <algorithm>
15 : :
16 : : #include "Refiner.hpp"
17 : : #include "Reorder.hpp"
18 : : #include "AMR/mesh_adapter.hpp"
19 : : #include "AMR/Error.hpp"
20 : : #include "Inciter/InputDeck/InputDeck.hpp"
21 : : #include "CGPDE.hpp"
22 : : #include "DGPDE.hpp"
23 : : #include "FVPDE.hpp"
24 : : #include "DerivedData.hpp"
25 : : #include "UnsMesh.hpp"
26 : : #include "Centering.hpp"
27 : : #include "Around.hpp"
28 : : #include "Sorter.hpp"
29 : : #include "Discretization.hpp"
30 : :
31 : : namespace inciter {
32 : :
33 : : extern ctr::InputDeck g_inputdeck;
34 : : extern ctr::InputDeck g_inputdeck_defaults;
35 : : extern std::vector< CGPDE > g_cgpde;
36 : : extern std::vector< DGPDE > g_dgpde;
37 : : extern std::vector< FVPDE > g_fvpde;
38 : :
39 : : } // inciter::
40 : :
41 : : using inciter::Refiner;
42 : :
43 : 1943 : Refiner::Refiner( std::size_t meshid,
44 : : const CProxy_Transporter& transporter,
45 : : const CProxy_Sorter& sorter,
46 : : const tk::CProxy_MeshWriter& meshwriter,
47 : : const std::vector< Scheme >& scheme,
48 : : const tk::RefinerCallback& cbr,
49 : : const tk::SorterCallback& cbs,
50 : : const std::vector< std::size_t >& ginpoel,
51 : : const tk::UnsMesh::CoordMap& coordmap,
52 : : const std::map< int, std::vector< std::size_t > >& bface,
53 : : const std::vector< std::size_t >& triinpoel,
54 : : const std::map< int, std::vector< std::size_t > >& bnode,
55 : : const std::vector< std::size_t >& elemblid,
56 : 1943 : int nchare ) :
57 : : m_meshid( meshid ),
58 : : m_ncit(0),
59 : : m_host( transporter ),
60 : : m_sorter( sorter ),
61 : : m_meshwriter( meshwriter ),
62 : : m_scheme( scheme ),
63 : : m_cbr( cbr ),
64 : : m_cbs( cbs ),
65 : : m_ginpoel( ginpoel ),
66 : : m_el( tk::global2local( ginpoel ) ), // fills m_inpoel, m_gid, m_lid
67 : : m_coordmap( coordmap ),
68 : : m_coord( flatcoord(coordmap) ),
69 : : m_bface( bface ),
70 : : m_bnode( bnode ),
71 : : m_triinpoel( triinpoel ),
72 : : m_elemblockid(),
73 : : m_nchare( nchare ),
74 : : m_mode( RefMode::T0REF ),
75 : 1943 : m_initref( g_inputdeck.get< tag::amr, tag::initial >() ),
76 : 1943 : m_ninitref( g_inputdeck.get< tag::amr, tag::initial >().size() ),
77 : 1943 : m_refiner( g_inputdeck.get< tag::amr, tag::maxlevels >(), m_inpoel ),
78 : : m_nref( 0 ),
79 : : m_nbnd( 0 ),
80 : : m_extra( 0 ),
81 : : m_ch(),
82 : : m_edgech(),
83 : : m_chedge(),
84 : : m_localEdgeData(),
85 : : m_remoteEdgeData(),
86 : : m_nodeCommMap(),
87 : : m_oldTets(),
88 : : m_addedNodes(),
89 : : m_addedTets(),
90 : : m_removedNodes(),
91 : : m_amrNodeMap(),
92 : : m_oldntets( 0 ),
93 : : m_coarseBndFaces(),
94 : : m_coarseBndNodes(),
95 : : m_coarseBlkElems(),
96 : 1943 : m_rid( m_coord[0].size() ),
97 : : // m_oldrid(),
98 : : m_lref( m_rid.size() ),
99 : : // m_oldparent(),
100 : : m_writeCallback(),
101 : : m_outref_ginpoel(),
102 : : m_outref_el(),
103 : : m_outref_coord(),
104 : : m_outref_addedNodes(),
105 : : m_outref_addedTets(),
106 : : m_outref_nodeCommMap(),
107 : : m_outref_bface(),
108 : : m_outref_bnode(),
109 [ + - ][ + - ]: 5829 : m_outref_triinpoel()
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
110 : : // *****************************************************************************
111 : : // Constructor
112 : : //! \param[in] meshid Mesh ID
113 : : //! \param[in] transporter Transporter (host) proxy
114 : : //! \param[in] sorter Mesh reordering (sorter) proxy
115 : : //! \param[in] meshwriter Mesh writer proxy
116 : : //! \param[in] scheme Discretization schemes (one per mesh)
117 : : //! \param[in] cbr Charm++ callbacks for Refiner
118 : : //! \param[in] cbs Charm++ callbacks for Sorter
119 : : //! \param[in] ginpoel Mesh connectivity (this chare) using global node IDs
120 : : //! \param[in] coordmap Mesh node coordinates (this chare) for global node IDs
121 : : //! \param[in] bface File-internal elem ids of side sets
122 : : //! \param[in] triinpoel Triangle face connectivity with global IDs
123 : : //! \param[in] bnode Node lists of side sets
124 : : //! \param[in] elemblid Mesh block ids associated to local tet ids
125 : : //! \param[in] nchare Total number of refiner chares (chare array elements)
126 : : // *****************************************************************************
127 : : {
128 [ - + ][ - - ]: 1943 : Assert( !m_ginpoel.empty(), "No elements assigned to refiner chare" );
[ - - ][ - - ]
129 [ + - ][ - + ]: 1943 : Assert( tk::positiveJacobians( m_inpoel, m_coord ),
[ - - ][ - - ]
[ - - ]
130 : : "Input mesh to Refiner Jacobian non-positive" );
131 [ + - ][ + - ]: 1943 : Assert( !tk::leakyPartition(
[ + - ][ - + ]
[ - - ][ - - ]
[ - - ]
132 : : tk::genEsuelTet( m_inpoel, tk::genEsup(m_inpoel,4) ),
133 : : m_inpoel, m_coord ),
134 : : "Input mesh to Refiner leaky" );
135 : :
136 : : // Construct data structure assigning sets of element ids to mesh blocks
137 [ + + ]: 448500 : for (std::size_t ie=0; ie<elemblid.size(); ++ie) {
138 [ + - ][ + - ]: 446557 : m_elemblockid[elemblid[ie]].insert(ie);
139 : : }
140 : :
141 : : #if not defined(__INTEL_COMPILER) || defined(NDEBUG)
142 : : // The above ifdef skips running the conformity test with the intel compiler
143 : : // in debug mode only. This is necessary because in tk::conforming(), filling
144 : : // up the map can fail with some meshes (only in parallel), e.g., tube.exo,
145 : : // used by some regression tests, due to the intel compiler generating some
146 : : // garbage incorrect code - only in debug, only in parallel, only with that
147 : : // mesh.
148 [ + - ][ - + ]: 1943 : Assert( tk::conforming( m_inpoel, m_coord, true, m_rid ),
[ - - ][ - - ]
[ - - ]
149 : : "Input mesh to Refiner not conforming" );
150 : : #endif
151 : :
152 : : // Generate local -> refiner lib node id map and its inverse
153 [ + - ]: 1943 : libmap();
154 : :
155 : : // Reverse initial mesh refinement type list (will pop from back)
156 [ + - ]: 1943 : std::reverse( begin(m_initref), end(m_initref) );
157 : :
158 : : // Generate boundary data structures for coarse mesh
159 [ + - ]: 1943 : coarseMesh();
160 : :
161 : : // If initial mesh refinement is configured, start initial mesh refinement.
162 : : // See also tk::grm::check_amr_errors in Control/Inciter/InputDeck/Ggrammar.h.
163 [ + + ][ + - ]: 1943 : if (g_inputdeck.get< tag::amr, tag::t0ref >() && m_ninitref > 0)
[ + + ]
164 [ + - ]: 38 : t0ref();
165 : : else
166 [ + - ]: 1905 : endt0ref();
167 : 1943 : }
168 : :
169 : : void
170 : 1943 : Refiner::libmap()
171 : : // *****************************************************************************
172 : : // (Re-)generate local -> refiner lib node id map and its inverse
173 : : // *****************************************************************************
174 : : {
175 : : // Fill initial (matching) mapping between local and refiner node ids
176 : 1943 : std::iota( begin(m_rid), end(m_rid), 0 );
177 : :
178 : : // Fill in inverse, mapping refiner to local node ids
179 : 1943 : std::size_t i = 0;
180 [ + + ][ + - ]: 164234 : for (auto r : m_rid) m_lref[r] = i++;
181 : 1943 : }
182 : :
183 : : void
184 : 1961 : Refiner::coarseMesh()
185 : : // *****************************************************************************
186 : : // (Re-)generate side set and block data structures for coarse mesh
187 : : // *****************************************************************************
188 : : {
189 : : // Generate unique set of faces for each side set of the input (coarsest) mesh
190 : 1961 : m_coarseBndFaces.clear();
191 [ + + ]: 4820 : for (const auto& [ setid, faceids ] : m_bface) {
192 [ + - ]: 2859 : auto& faces = m_coarseBndFaces[ setid ];
193 [ + + ]: 169839 : for (auto f : faceids) {
194 [ + - ]: 166980 : faces.insert(
195 : 166980 : {{{ m_triinpoel[f*3+0], m_triinpoel[f*3+1], m_triinpoel[f*3+2] }}} );
196 : : }
197 : : }
198 : :
199 : : // Generate unique set of nodes for each side set of the input (coarsest) mesh
200 : 1961 : m_coarseBndNodes.clear();
201 [ + + ]: 2499 : for (const auto& [ setid, nodes ] : m_bnode) {
202 [ + - ][ + - ]: 538 : m_coarseBndNodes[ setid ].insert( begin(nodes), end(nodes) );
203 : : }
204 : :
205 : : // Generate set of elements for each mesh block of the input (coarsest) mesh
206 : 1961 : m_coarseBlkElems.clear();
207 [ + + ]: 3940 : for (const auto& [blid, elids] : m_elemblockid) {
208 [ + + ]: 453034 : for (auto ie : elids) {
209 [ + - ][ + - ]: 902110 : m_coarseBlkElems[blid].insert( {{{m_inpoel[ie*4+0], m_inpoel[ie*4+1],
210 : 451055 : m_inpoel[ie*4+2], m_inpoel[ie*4+3]}}} );
211 : : }
212 : : }
213 : 1961 : }
214 : :
215 : : void
216 : 1943 : Refiner::sendProxy()
217 : : // *****************************************************************************
218 : : // Send Refiner proxy to Discretization objects
219 : : //! \details This should be called when bound Discretization chare array
220 : : //! elements have already been created.
221 : : // *****************************************************************************
222 : : {
223 : : // Make sure (bound) Discretization chare is already created and accessible
224 [ + - ][ - + ]: 1943 : Assert( m_scheme[m_meshid].disc()[thisIndex].ckLocal() != nullptr,
[ - - ][ - - ]
[ - - ]
225 : : "About to dereference nullptr" );
226 : :
227 : : // Pass Refiner Charm++ chare proxy to fellow (bound) Discretization object
228 [ + - ][ + - ]: 1943 : m_scheme[m_meshid].disc()[thisIndex].ckLocal()->setRefiner( thisProxy );
229 : 1943 : }
230 : :
231 : : void
232 : 18 : Refiner::reorder()
233 : : // *****************************************************************************
234 : : // Query Sorter and update local mesh with the reordered one
235 : : // *****************************************************************************
236 : : {
237 : 18 : m_sorter[thisIndex].ckLocal()->
238 [ + - ][ + - ]: 18 : mesh( m_ginpoel, m_coordmap, m_triinpoel, m_bnode );
239 : :
240 : : // Update local mesh data based on data just received from Sorter
241 : 18 : m_el = tk::global2local( m_ginpoel ); // fills m_inpoel, m_gid, m_lid
242 : 18 : m_coord = flatcoord( m_coordmap );
243 : :
244 : : // Re-generate boundary data structures for coarse mesh
245 : 18 : coarseMesh();
246 : :
247 : : // WARNING: This re-creates the AMR lib which is probably not what we
248 : : // ultimately want, beacuse this deletes its history recorded during initial
249 : : // (t<0) refinement. However, this appears to correctly update the local mesh
250 : : // based on the reordered one (from Sorter) at least when t0ref is off.
251 : 36 : m_refiner = AMR::mesh_adapter_t(
252 : 36 : g_inputdeck.get< tag::amr, tag::maxlevels >(), m_inpoel );
253 : 18 : }
254 : :
255 : : tk::UnsMesh::Coords
256 : 1961 : Refiner::flatcoord( const tk::UnsMesh::CoordMap& coordmap )
257 : : // *****************************************************************************
258 : : // Generate flat coordinate data from coordinate map
259 : : //! \param[in] coordmap Coordinates associated to global node IDs of mesh chunk
260 : : //! \return Flat coordinate data
261 : : // *****************************************************************************
262 : : {
263 : 1961 : tk::UnsMesh::Coords coord;
264 : :
265 : : // Convert node coordinates associated to global node IDs to a flat vector
266 : 1961 : auto npoin = coordmap.size();
267 [ - + ][ - - ]: 1961 : Assert( m_gid.size() == npoin, "Size mismatch" );
[ - - ][ - - ]
268 [ + - ]: 1961 : coord[0].resize( npoin );
269 [ + - ]: 1961 : coord[1].resize( npoin );
270 [ + - ]: 1961 : coord[2].resize( npoin );
271 [ + + ]: 176641 : for (const auto& [ gid, coords ] : coordmap) {
272 [ + - ]: 174680 : auto i = tk::cref_find( m_lid, gid );
273 [ - + ][ - - ]: 174680 : Assert( i < npoin, "Indexing out of coordinate map" );
[ - - ][ - - ]
274 : 174680 : coord[0][i] = coords[0];
275 : 174680 : coord[1][i] = coords[1];
276 : 174680 : coord[2][i] = coords[2];
277 : : }
278 : :
279 : 1961 : return coord;
280 : : }
281 : :
282 : : void
283 : 0 : Refiner::dtref( const std::map< int, std::vector< std::size_t > >& bface,
284 : : const std::map< int, std::vector< std::size_t > >& bnode,
285 : : const std::vector< std::size_t >& triinpoel )
286 : : // *****************************************************************************
287 : : // Start mesh refinement (during time stepping, t>0)
288 : : //! \param[in] bface Boundary-faces mapped to side set ids
289 : : //! \param[in] bnode Boundary-node lists mapped to side set ids
290 : : //! \param[in] triinpoel Boundary-face connectivity
291 : : // *****************************************************************************
292 : : {
293 : 0 : m_mode = RefMode::DTREF;
294 : :
295 : : // Update boundary node lists
296 : 0 : m_bface = bface;
297 : 0 : m_bnode = bnode;
298 : 0 : m_triinpoel = tk::remap(triinpoel, m_gid);
299 : :
300 : 0 : start();
301 : 0 : }
302 : :
303 : : void
304 : 66 : Refiner::outref( const std::map< int, std::vector< std::size_t > >& bface,
305 : : const std::map< int, std::vector< std::size_t > >& bnode,
306 : : const std::vector< std::size_t >& triinpoel,
307 : : CkCallback c,
308 : : RefMode mode )
309 : : // *****************************************************************************
310 : : // Start mesh refinement (for field output)
311 : : //! \param[in] bface Boundary-faces mapped to side set ids
312 : : //! \param[in] bnode Boundary-node lists mapped to side set ids
313 : : //! \param[in] triinpoel Boundary-face connectivity
314 : : //! \param[in] c Function to continue with after the writing field output
315 : : //! \param[in] mode Refinement mode
316 : : // *****************************************************************************
317 : : {
318 : 66 : m_mode = mode;
319 : :
320 : 66 : m_writeCallback = c;
321 : :
322 : : // Update boundary node lists
323 : 66 : m_bface = bface;
324 : 66 : m_bnode = bnode;
325 : 66 : m_triinpoel = triinpoel;
326 : :
327 : 66 : start();
328 : 66 : }
329 : :
330 : : void
331 : 111 : Refiner::t0ref()
332 : : // *****************************************************************************
333 : : // Output mesh to file before a new step mesh refinement
334 : : // *****************************************************************************
335 : : {
336 [ - + ][ - - ]: 111 : Assert( m_ninitref > 0, "No initial mesh refinement steps configured" );
[ - - ][ - - ]
337 : : // Output initial mesh to file
338 : 111 : auto l = m_ninitref - m_initref.size(); // num initref steps completed
339 : 111 : auto t0 = g_inputdeck.get< tag::t0 >();
340 [ + + ]: 111 : if (l == 0) {
341 [ + - ][ + - ]: 38 : writeMesh( "t0ref", l, t0-1.0,
342 [ + - ][ + - ]: 76 : CkCallback( CkIndex_Refiner::start(), thisProxy[thisIndex] ) );
343 : : } else {
344 : 73 : start();
345 : : }
346 : 111 : }
347 : :
348 : : void
349 : 177 : Refiner::start()
350 : : // *****************************************************************************
351 : : // Start new step of mesh refinement
352 : : // *****************************************************************************
353 : : {
354 : 177 : m_extra = 0;
355 : 177 : m_ch.clear();
356 : 177 : m_remoteEdgeData.clear();
357 : 177 : m_remoteEdges.clear();
358 : :
359 : 177 : updateEdgeData();
360 : :
361 : : // Generate and communicate boundary edges
362 : 177 : bndEdges();
363 : 177 : }
364 : :
365 : : void
366 : 177 : Refiner::bndEdges()
367 : : // *****************************************************************************
368 : : // Generate boundary edges and send them to all chares
369 : : //! \details Extract edges on the boundary only. The boundary edges (shared by
370 : : //! multiple chares) will be agreed on a refinement that yields a conforming
371 : : //! mesh across chares boundaries.
372 : : // *****************************************************************************
373 : : {
374 : : // Compute the number of edges (chunksize) a chare will respond to when
375 : : // computing shared edges
376 : 177 : auto N = static_cast< std::size_t >( m_nchare );
377 : 177 : std::size_t chunksize = std::numeric_limits< std::size_t >::max() / N;
378 : :
379 : : // Generate boundary edges of our mesh chunk
380 : 354 : std::unordered_map< int, EdgeSet > chbedges;
381 [ + - ]: 354 : auto esup = tk::genEsup( m_inpoel, 4 ); // elements surrounding points
382 [ + - ]: 354 : auto esuel = tk::genEsuelTet( m_inpoel, esup ); // elems surrounding elements
383 [ + + ]: 294172 : for (std::size_t e=0; e<esuel.size()/4; ++e) {
384 : 293995 : auto mark = e*4;
385 [ + + ]: 1469975 : for (std::size_t f=0; f<4; ++f) {
386 [ + + ]: 1175980 : if (esuel[mark+f] == -1) {
387 : 111628 : auto A = m_ginpoel[ mark+tk::lpofa[f][0] ];
388 : 111628 : auto B = m_ginpoel[ mark+tk::lpofa[f][1] ];
389 : 111628 : auto C = m_ginpoel[ mark+tk::lpofa[f][2] ];
390 [ + - ][ - + ]: 111628 : Assert( m_lid.find( A ) != end(m_lid), "Local node ID not found" );
[ - - ][ - - ]
[ - - ]
391 [ + - ][ - + ]: 111628 : Assert( m_lid.find( B ) != end(m_lid), "Local node ID not found" );
[ - - ][ - - ]
[ - - ]
392 [ + - ][ - + ]: 111628 : Assert( m_lid.find( C ) != end(m_lid), "Local node ID not found" );
[ - - ][ - - ]
[ - - ]
393 : : // assign edges to bins a single chare will respond to when computing
394 : : // shared edges
395 : 111628 : auto bin = A / chunksize;
396 [ - + ][ - - ]: 111628 : Assert( bin < N, "Will index out of number of chares" );
[ - - ][ - - ]
397 [ + - ][ + - ]: 111628 : chbedges[ static_cast<int>(bin) ].insert( {A,B} );
398 : 111628 : bin = B / chunksize;
399 [ - + ][ - - ]: 111628 : Assert( bin < N, "Will index out of number of chares" );
[ - - ][ - - ]
400 [ + - ][ + - ]: 111628 : chbedges[ static_cast<int>(bin) ].insert( {B,C} );
401 : 111628 : bin = C / chunksize;
402 [ - + ][ - - ]: 111628 : Assert( bin < N, "Will index out of number of chares" );
[ - - ][ - - ]
403 [ + - ][ + - ]: 111628 : chbedges[ static_cast<int>(bin) ].insert( {C,A} );
404 : : }
405 : : }
406 : : }
407 : :
408 : : // Send edges in bins to chares that will compute shared edges
409 : 177 : m_nbnd = chbedges.size();
410 [ - + ]: 177 : if (m_nbnd == 0)
411 [ - - ]: 0 : contribute( sizeof(std::size_t), &m_meshid, CkReduction::nop,
412 : 0 : m_cbr.get< tag::queried >() );
413 : : else
414 [ + + ]: 488 : for (const auto& [ targetchare, bndedges ] : chbedges)
415 [ + - ][ + - ]: 311 : thisProxy[ targetchare ].query( thisIndex, bndedges );
416 : 177 : }
417 : :
418 : : void
419 : 311 : Refiner::query( int fromch, const EdgeSet& edges )
420 : : // *****************************************************************************
421 : : // Incoming query for a list boundary edges for which this chare compiles
422 : : // shared edges
423 : : //! \param[in] fromch Sender chare ID
424 : : //! \param[in] edges Chare-boundary edge list from another chare
425 : : // *****************************************************************************
426 : : {
427 : : // Store incoming edges in edge->chare and its inverse, chare->edge, maps
428 [ + + ][ + - ]: 202130 : for (const auto& e : edges) m_edgech[ e ].push_back( fromch );
[ + - ]
429 : 311 : m_chedge[ fromch ].insert( begin(edges), end(edges) );
430 : : // Report back to chare message received from
431 [ + - ]: 311 : thisProxy[ fromch ].recvquery();
432 : 311 : }
433 : :
434 : : void
435 : 311 : Refiner::recvquery()
436 : : // *****************************************************************************
437 : : // Receive receipt of boundary edge lists to query
438 : : // *****************************************************************************
439 : : {
440 [ + + ]: 311 : if (--m_nbnd == 0)
441 : 177 : contribute( sizeof(std::size_t), &m_meshid, CkReduction::nop,
442 : 177 : m_cbr.get< tag::queried >() );
443 : 311 : }
444 : :
445 : : void
446 : 177 : Refiner::response()
447 : : // *****************************************************************************
448 : : // Respond to boundary edge list queries
449 : : // *****************************************************************************
450 : : {
451 : 354 : std::unordered_map< int, std::vector< int > > exp;
452 : :
453 : : // Compute shared edges whose chare ids will be sent back to querying chares
454 [ + + ]: 488 : for (const auto& [ neighborchare, bndedges ] : m_chedge) {
455 [ + - ]: 311 : auto& e = exp[ neighborchare ];
456 [ + + ]: 202130 : for (const auto& ed : bndedges)
457 [ + - ][ + + ]: 429236 : for (auto d : tk::cref_find(m_edgech,ed))
458 [ + + ]: 227417 : if (d != neighborchare)
459 [ + - ]: 25598 : e.push_back( d );
460 : : }
461 : :
462 : : // Send chare ids of shared edges to chares that issued a query to us. Shared
463 : : // boundary edges assigned to chare ids sharing the boundary edge were
464 : : // computed above for those chares that queried this map from us. These
465 : : // boundary edges form a distributed table and we only work on a chunk of it.
466 : : // Note that we only send data back to those chares that have queried us. The
467 : : // receiving sides do not know in advance if they receive messages or not.
468 : : // Completion is detected by having the receiver respond back and counting
469 : : // the responses on the sender side, i.e., this chare.
470 : 177 : m_nbnd = exp.size();
471 [ + + ]: 177 : if (m_nbnd == 0)
472 [ + - ]: 50 : contribute( sizeof(std::size_t), &m_meshid, CkReduction::nop,
473 : 50 : m_cbr.get< tag::responded >() );
474 : : else
475 [ + + ]: 438 : for (const auto& [ targetchare, bndedges ] : exp)
476 [ + - ][ + - ]: 311 : thisProxy[ targetchare ].bnd( thisIndex, bndedges );
477 : 177 : }
478 : :
479 : : void
480 : 311 : Refiner::bnd( int fromch, const std::vector< int >& chares )
481 : : // *****************************************************************************
482 : : // Receive shared boundary edges for our mesh chunk
483 : : //! \param[in] fromch Sender chare ID
484 : : //! \param[in] chares Chare ids we share edges with
485 : : // *****************************************************************************
486 : : {
487 : : // Store chare ids we share edges with
488 : 311 : m_ch.insert( begin(chares), end(chares) );
489 : :
490 : : // Report back to chare message received from
491 [ + - ]: 311 : thisProxy[ fromch ].recvbnd();
492 : 311 : }
493 : :
494 : : void
495 : 311 : Refiner::recvbnd()
496 : : // *****************************************************************************
497 : : // Receive receipt of shared boundary edges
498 : : // *****************************************************************************
499 : : {
500 [ + + ]: 311 : if (--m_nbnd == 0)
501 : 127 : contribute( sizeof(std::size_t), &m_meshid, CkReduction::nop,
502 : 127 : m_cbr.get< tag::responded >() );
503 : 311 : }
504 : :
505 : : void
506 : 177 : Refiner::refine()
507 : : // *****************************************************************************
508 : : // Do a single step of mesh refinement (really, only tag edges)
509 : : //! \details During initial (t<0, t0ref) mesh refinement, this is a single step
510 : : //! in a potentially multiple-entry list of initial adaptive mesh refinement
511 : : //! steps. Distribution of the chare-boundary edges must have preceded this
512 : : //! step, so that boundary edges (shared by multiple chares) can agree on a
513 : : //! refinement that yields a conforming mesh across chare boundaries.
514 : : //!
515 : : //! During-timestepping (t>0, dtref) mesh refinement this is called once, as
516 : : //! we only do a single step during time stepping.
517 : : //!
518 : : //! During field-output (outref) mesh refinement, this may be called multiple
519 : : //! times, depending the number of refinement levels needed for the field
520 : : //! output.
521 : : // *****************************************************************************
522 : : {
523 : : // Free memory used for computing shared boundary edges
524 : 177 : tk::destroy( m_edgech );
525 : 177 : tk::destroy( m_chedge );
526 : :
527 : : // Perform leak test on old mesh
528 [ + - ][ + - ]: 177 : Assert( !tk::leakyPartition(
[ - + ][ - - ]
[ - - ][ - - ]
529 : : tk::genEsuelTet( m_inpoel, tk::genEsup(m_inpoel,4) ),
530 : : m_inpoel, m_coord ),
531 : : "Mesh partition before refinement leaky" );
532 : :
533 [ + + ]: 177 : if (m_mode == RefMode::T0REF) {
534 : :
535 : : // Refine mesh based on next initial refinement type
536 [ + - ]: 111 : if (!m_initref.empty()) {
537 : 111 : auto r = m_initref.back(); // consume (reversed) list from its back
538 [ + + ]: 111 : if (r == ctr::AMRInitialType::UNIFORM)
539 : 57 : uniformRefine();
540 [ + + ]: 54 : else if (r == ctr::AMRInitialType::UNIFORM_DEREFINE)
541 : 36 : uniformDeRefine();
542 [ + + ]: 18 : else if (r == ctr::AMRInitialType::INITIAL_CONDITIONS)
543 : 16 : errorRefine();
544 [ + - ]: 2 : else if (r == ctr::AMRInitialType::COORDINATES)
545 : 2 : coordRefine();
546 [ - - ]: 0 : else if (r == ctr::AMRInitialType::EDGELIST)
547 : 0 : edgelistRefine();
548 [ - - ][ - - ]: 0 : else Throw( "Initial AMR type not implemented" );
[ - - ]
549 : : }
550 : :
551 [ - + ]: 66 : } else if (m_mode == RefMode::DTREF) {
552 : :
553 [ - - ]: 0 : if (g_inputdeck.get< tag::amr, tag::dtref_uniform >())
554 : 0 : uniformRefine();
555 : : else
556 : 0 : errorRefine();
557 : :
558 [ + + ]: 66 : } else if (m_mode == RefMode::OUTREF) {
559 : :
560 : 33 : uniformRefine();
561 : :
562 [ + - ]: 33 : } else if (m_mode == RefMode::OUTDEREF) {
563 : :
564 : 33 : uniformDeRefine();
565 : :
566 [ - - ][ - - ]: 0 : } else Throw( "RefMode not implemented" );
[ - - ]
567 : :
568 : : // Communicate extra edges
569 : 177 : comExtra();
570 : 177 : }
571 : :
572 : : void
573 : 181 : Refiner::comExtra()
574 : : // *****************************************************************************
575 : : // Communicate extra edges along chare boundaries
576 : : // *****************************************************************************
577 : : {
578 : : // Export extra added nodes on our mesh chunk boundary to other chares
579 [ + + ]: 181 : if (m_ch.empty()) {
580 : 41 : correctref();
581 : : } else {
582 [ + + ]: 456 : for (auto c : m_ch) { // for all chares we share at least an edge with
583 [ + - ][ + - ]: 316 : thisProxy[c].addRefBndEdges(thisIndex, m_localEdgeData, m_intermediates);
[ + - ]
584 : : }
585 : : }
586 : 181 : }
587 : :
588 : : void
589 : 316 : Refiner::addRefBndEdges(
590 : : int fromch,
591 : : const AMR::EdgeData& ed,
592 : : const std::unordered_set< std::size_t >& intermediates )
593 : : // *****************************************************************************
594 : : //! Receive edges on our chare boundary from other chares
595 : : //! \param[in] fromch Chare call coming from
596 : : //! \param[in] ed Edges on chare boundary
597 : : //! \param[in] intermediates Intermediate nodes
598 : : //! \details Other than update remoteEdge data, this function also updates
599 : : //! locking information for such edges whos nodes are marked as intermediate
600 : : //! by neighboring chares.
601 : : // *****************************************************************************
602 : : {
603 : : // Save/augment buffers of edge data for each sender chare
604 : 316 : auto& red = m_remoteEdgeData[ fromch ];
605 : 316 : auto& re = m_remoteEdges[ fromch ];
606 : : using edge_data_t = std::tuple< Edge, int, int, AMR::Edge_Lock_Case >;
607 [ + + ]: 374792 : for (const auto& [ edge, data ] : ed) {
608 [ + - ][ + - ]: 748952 : red.push_back( edge_data_t{ edge, std::get<0>(data), std::get<1>(data),
609 : 374476 : std::get<2>(data) } );
610 [ + - ]: 374476 : re.push_back( edge );
611 : : }
612 : :
613 : : // Add intermediates to mesh refiner lib
614 : : // needs to be done only when mesh has been actually updated, i.e. first iter
615 [ + + ]: 316 : if (m_ncit == 0) {
616 [ + - ]: 624 : auto esup = tk::genEsup( m_inpoel, 4 );
617 [ + - ]: 624 : auto psup = tk::genPsup( m_inpoel, 4, esup );
618 [ + + ]: 406 : for (const auto g : intermediates) {
619 [ + - ]: 94 : auto l = m_lid.find( g ); // convert to local node ids
620 [ + + ]: 94 : if (l != end(m_lid)) {
621 : : // lock all edges connected to intermediate node
622 : 2 : auto p = l->second;
623 [ + + ]: 30 : for (auto q : tk::Around(psup,p)) {
624 [ + - ]: 28 : AMR::edge_t e(m_rid[p], m_rid[q]);
625 [ + - ]: 28 : auto& refedge = m_refiner.tet_store.edge_store.get(e);
626 [ + + ]: 28 : if (refedge.lock_case == AMR::Edge_Lock_Case::unlocked) {
627 : 1 : refedge.lock_case = AMR::Edge_Lock_Case::temporary; //intermediate;
628 : 1 : refedge.needs_refining = 0;
629 : : }
630 : : }
631 : : }
632 : : }
633 : : }
634 : :
635 : : // Heard from every worker we share at least a single edge with
636 [ + + ]: 316 : if (++m_nref == m_ch.size()) {
637 : 140 : m_nref = 0;
638 : :
639 [ + - ]: 140 : updateBndEdgeData();
640 : :
641 [ + - ]: 280 : std::vector< std::size_t > meshdata{ m_meshid };
642 [ + - ]: 140 : contribute( meshdata, CkReduction::max_ulong,
643 : 140 : m_cbr.get< tag::compatibility >() );
644 : : }
645 : 316 : }
646 : :
647 : : void
648 : 181 : Refiner::correctref()
649 : : // *****************************************************************************
650 : : // Correct extra edges to arrive at conforming mesh across chare boundaries
651 : : //! \details This function is called repeatedly until there is not a a single
652 : : //! edge that needs correction for the whole distributed problem to arrive at
653 : : //! a conforming mesh across chare boundaries during a mesh refinement step.
654 : : // *****************************************************************************
655 : : {
656 : 181 : auto unlocked = AMR::Edge_Lock_Case::unlocked;
657 : :
658 : : // Storage for edge data that need correction to yield a conforming mesh
659 : 362 : AMR::EdgeData extra;
660 : 181 : std::size_t neigh_extra(0);
661 : :
662 : : // Vars for debugging purposes
663 : 181 : std::size_t nlocked(0);
664 : 181 : std::array< std::size_t, 4 > ncorrcase{{0,0,0,0}};
665 : :
666 : : // loop through all edges shared with other chares
667 [ + + ]: 497 : for (const auto& [ neighborchare, edgedata ] : m_remoteEdgeData) {
668 : 748952 : for (const auto& [edge,remote_needs_refining,remote_needs_derefining,
669 [ + + ]: 1123744 : remote_lock_case] : edgedata) {
670 : : // find local data of remote edge
671 [ + - ]: 374476 : auto it = m_localEdgeData.find( edge );
672 [ + + ]: 374476 : if (it != end(m_localEdgeData)) {
673 : 17154 : auto& local = it->second;
674 : 17154 : auto& local_needs_refining = std::get<0>(local);
675 : 17154 : auto& local_needs_derefining = std::get<1>(local);
676 : 17154 : auto& local_lock_case = std::get<2>(local);
677 : :
678 : 17154 : auto local_needs_refining_orig = local_needs_refining;
679 : 17154 : auto local_needs_derefining_orig = local_needs_derefining;
680 : 17154 : auto local_lock_case_orig = local_lock_case;
681 : :
682 [ + + ][ - + ]: 17154 : Assert( !(local_lock_case > unlocked && local_needs_refining),
[ - - ][ - - ]
[ - - ]
683 : : "Invalid local edge: locked & needs refining" );
684 [ + + ][ - + ]: 17154 : Assert( !(remote_lock_case > unlocked && remote_needs_refining),
[ - - ][ - - ]
[ - - ]
685 : : "Invalid remote edge: locked & needs refining" );
686 [ + + ][ - + ]: 17154 : Assert( !(local_needs_derefining == 1 && local_needs_refining > 0),
[ - - ][ - - ]
[ - - ]
687 : : "Invalid local edge: needs refining and derefining" );
688 : :
689 : : // The parallel compatibility (par-compat) algorithm
690 : :
691 : : // compute lock from local and remote locks as most restrictive
692 : 17154 : local_lock_case = std::max( local_lock_case, remote_lock_case );
693 : :
694 [ + + ]: 17154 : if (local_lock_case > unlocked) {
695 : 524 : local_needs_refining = 0;
696 [ + + ]: 524 : if (local_needs_refining != local_needs_refining_orig ||
697 [ + + ]: 503 : local_lock_case != local_lock_case_orig)
698 : 30 : ++ncorrcase[0];
699 : : }
700 : :
701 : : // Possible combinations of remote-local ref-deref decisions
702 : : // rows 1, 5, 9: no action needed.
703 : : // rows 4, 7, 8: no action on local-side; comm needed.
704 : : //
705 : : // LOCAL | REMOTE | Result
706 : : // 1 d | d | d
707 : : // 2 d | - | -
708 : : // 3 d | r | r
709 : : // 4 - | d | -
710 : : // 5 - | - | -
711 : : // 6 - | r | r
712 : : // 7 r | d | r
713 : : // 8 r | - | r
714 : : // 9 r | r | r
715 : :
716 : : // Rows 3, 6
717 : : // If remote wants to refine
718 [ + + ]: 17154 : if (remote_needs_refining == 1) {
719 [ + + ]: 5191 : if (local_lock_case == unlocked) {
720 : 5170 : local_needs_refining = 1;
721 : 5170 : local_needs_derefining = false;
722 [ + - ]: 5170 : if (local_needs_refining != local_needs_refining_orig ||
723 [ - + ]: 5170 : local_needs_derefining != local_needs_derefining_orig)
724 : 0 : ++ncorrcase[1];
725 : : }
726 : : else {
727 : 21 : ++nlocked;
728 : : }
729 : : }
730 : :
731 : : // Row 2
732 : : // If remote neither wants to refine nor derefine
733 [ + + ][ + + ]: 17154 : if (remote_needs_refining == 0 && remote_needs_derefining == false) {
734 : 677 : local_needs_derefining = false;
735 [ - + ]: 677 : if (local_needs_derefining != local_needs_derefining_orig)
736 : 0 : ++ncorrcase[2];
737 : : }
738 : :
739 : : // Row 1: special case
740 : : // If remote wants to deref-ref (either of 8:4, 8:2, 4:2)
741 : : // and local does not want to refine (neither pure ref nor deref-ref)
742 [ - + ][ - - ]: 17154 : if (remote_needs_refining == 2 && local_needs_refining == 0) {
743 [ - - ]: 0 : if (local_lock_case == unlocked) {
744 : 0 : local_needs_refining = 1;
745 : 0 : local_needs_derefining = false;
746 [ - - ]: 0 : if (local_needs_refining != local_needs_refining_orig ||
747 [ - - ]: 0 : local_needs_derefining != local_needs_derefining_orig)
748 : 0 : ++ncorrcase[3];
749 : : }
750 : : else {
751 : 0 : ++nlocked;
752 : : }
753 : : }
754 : :
755 : : // Rows 4, 7, 8
756 : :
757 : : // if the remote sent us data that makes us change our local state,
758 : : // e.g., local{-,0} + remote{r,0} -> local{r,0}, i.e., I changed my
759 : : // state I need to tell the world about it
760 [ + + ]: 17154 : if (local_lock_case != local_lock_case_orig ||
761 [ + - ]: 17124 : local_needs_refining != local_needs_refining_orig ||
762 [ - + ]: 17124 : local_needs_derefining != local_needs_derefining_orig)
763 : : {
764 [ + - ]: 30 : auto l1 = tk::cref_find( m_lid, edge[0] );
765 [ + - ]: 30 : auto l2 = tk::cref_find( m_lid, edge[1] );
766 [ - + ][ - - ]: 30 : Assert( l1 != l2, "Edge end-points local ids are the same" );
[ - - ][ - - ]
767 : 30 : auto r1 = m_rid[ l1 ];
768 : 30 : auto r2 = m_rid[ l2 ];
769 [ - + ][ - - ]: 30 : Assert( r1 != r2, "Edge end-points refiner ids are the same" );
[ - - ][ - - ]
770 : : //std::cout << thisIndex << ": " << r1 << ", " << r2 << std::endl;
771 : : //if (m_refiner.tet_store.edge_store.get(AMR::edge_t(r1,r2)).lock_case > local_lock_case) {
772 : : // std::cout << thisIndex << ": edge " << r1 << "-" << r2 <<
773 : : // "; prev=" << local_lock_case_orig <<
774 : : // "; new=" << local_lock_case <<
775 : : // "; amr-lib=" << m_refiner.tet_store.edge_store.get(AMR::edge_t(r1,r2)).lock_case <<
776 : : // " | parcompatiter " << m_ncit << std::endl;
777 : : //}
778 [ + - ]: 30 : extra[ {{ std::min(r1,r2), std::max(r1,r2) }} ] =
779 [ + - ]: 60 : { local_needs_refining, local_needs_derefining, local_lock_case };
780 : : }
781 : : // or if the remote data is inconsistent with what I think, e.g.,
782 : : // local{r,0} + remote{-,0} -> local{r,0}, i.e., the remote does not
783 : : // yet agree, so another par-compat iteration will be pursued. but
784 : : // I do not have to locally run ref-compat.
785 [ + + ]: 17124 : else if (local_lock_case != remote_lock_case ||
786 [ + - ]: 17094 : local_needs_refining != remote_needs_refining ||
787 [ - + ]: 17094 : local_needs_derefining != remote_needs_derefining)
788 : : {
789 : 30 : ++neigh_extra;
790 : : }
791 : : }
792 : : }
793 : : }
794 : :
795 : 181 : m_remoteEdgeData.clear();
796 : 181 : m_extra = extra.size();
797 : : //std::cout << thisIndex << ": amr correction reqd for nedge: " << m_extra << std::endl;
798 : : //std::cout << thisIndex << ": amr correction reqd for neighbor edges: " << neigh_extra << std::endl;
799 : : //std::cout << thisIndex << ": edge counts by correction case: " << ncorrcase[0]
800 : : // << ", " << ncorrcase[1] << ", " << ncorrcase[2] << ", " << ncorrcase[3] << std::endl;
801 : : //std::cout << thisIndex << ": locked edges that req corr: " << nlocked << std::endl;
802 : :
803 [ + + ]: 181 : if (!extra.empty()) {
804 : : //std::cout << thisIndex << ": redoing markings" << std::endl;
805 : : // Do refinement compatibility (ref-compat) for edges that need correction
806 [ + - ]: 4 : m_refiner.mark_error_refinement_corr( extra );
807 : 4 : ++m_ncit;
808 : : // Update our extra-edge store based on refiner
809 [ + - ]: 4 : updateEdgeData();
810 : 4 : m_remoteEdges.clear();
811 : : }
812 [ + - ]: 177 : else if (neigh_extra == 0) {
813 : 177 : m_ncit = 0;
814 : : }
815 : :
816 : : // Aggregate number of extra edges that still need correction and some
817 : : // refinement/derefinement statistics
818 : 181 : const auto& tet_store = m_refiner.tet_store;
819 : : std::vector< std::size_t >
820 : 181 : m{ m_meshid,
821 : 181 : m_extra,
822 : 181 : tet_store.marked_refinements.size(),
823 : 181 : tet_store.marked_derefinements.size(),
824 [ + - ]: 362 : static_cast< std::underlying_type_t< RefMode > >( m_mode ) };
825 [ + - ]: 181 : contribute( m, CkReduction::sum_ulong, m_cbr.get< tag::matched >() );
826 : 181 : }
827 : :
828 : : void
829 : 358 : Refiner::updateEdgeData()
830 : : // *****************************************************************************
831 : : // Query AMR lib and update our local store of edge data
832 : : // *****************************************************************************
833 : : {
834 : 358 : m_localEdgeData.clear();
835 : 358 : m_intermediates.clear();
836 : :
837 : : // This currently takes ALL edges from the AMR lib, i.e., on the whole
838 : : // domain. We should eventually only collect edges here that are shared with
839 : : // other chares.
840 : 358 : const auto& ref_edges = m_refiner.tet_store.edge_store.edges;
841 : 358 : const auto& refinpoel = m_refiner.tet_store.get_active_inpoel();
842 : :
843 [ + + ]: 589912 : for (std::size_t e=0; e<refinpoel.size()/4; ++e) {
844 : 589554 : auto A = refinpoel[e*4+0];
845 : 589554 : auto B = refinpoel[e*4+1];
846 : 589554 : auto C = refinpoel[e*4+2];
847 : 589554 : auto D = refinpoel[e*4+3];
848 : : std::array<Edge,6> edges{{ {{A,B}}, {{B,C}}, {{A,C}},
849 : 589554 : {{A,D}}, {{B,D}}, {{C,D}} }};
850 [ + + ]: 4126878 : for (const auto& ed : edges) {
851 : 3537324 : auto ae = AMR::edge_t{{{ std::min(ed[0],ed[1]), std::max(ed[0],ed[1]) }}};
852 [ + - ]: 3537324 : auto r = tk::cref_find( ref_edges, ae );
853 [ + - ]: 3537324 : const auto ged = Edge{{ m_gid[ tk::cref_find( m_lref, ed[0] ) ],
854 [ + - ]: 3537324 : m_gid[ tk::cref_find( m_lref, ed[1] ) ] }};
855 [ + - ]: 3537324 : m_localEdgeData[ ged ] = { r.needs_refining, r.needs_derefining,
856 [ + - ]: 7074648 : r.lock_case };
857 : : }
858 : : }
859 : :
860 : : // Build intermediates to send. This currently takes ALL intermediates from
861 : : // the AMR lib, i.e., on the whole domain. We should eventually only collect
862 : : // edges here that are shared with other chares.
863 [ + + ]: 728 : for (const auto& r : m_refiner.tet_store.intermediate_list) {
864 [ + - ][ + - ]: 370 : m_intermediates.insert( m_gid[ tk::cref_find( m_lref, r ) ] );
865 : : }
866 : 358 : }
867 : :
868 : : void
869 : 140 : Refiner::updateBndEdgeData()
870 : : // *****************************************************************************
871 : : // Query AMR lib and update our local store of boundary edge data
872 : : // *****************************************************************************
873 : : {
874 : : // This currently takes ALL edges from the AMR lib, i.e., on the whole
875 : : // domain. We should eventually only collect edges here that are shared with
876 : : // other chares.
877 : 140 : const auto& ref_edges = m_refiner.tet_store.edge_store.edges;
878 : 140 : const auto& refinpoel = m_refiner.tet_store.get_active_inpoel();
879 : :
880 [ + + ]: 109715 : for (std::size_t e=0; e<refinpoel.size()/4; ++e) {
881 : 109575 : auto A = refinpoel[e*4+0];
882 : 109575 : auto B = refinpoel[e*4+1];
883 : 109575 : auto C = refinpoel[e*4+2];
884 : 109575 : auto D = refinpoel[e*4+3];
885 : : std::array<Edge,6> edges{{ {{A,B}}, {{B,C}}, {{A,C}},
886 : 109575 : {{A,D}}, {{B,D}}, {{C,D}} }};
887 [ + + ]: 767025 : for (const auto& ed : edges) {
888 : 657450 : auto ae = AMR::edge_t{{{ std::min(ed[0],ed[1]), std::max(ed[0],ed[1]) }}};
889 [ + - ]: 657450 : auto r = tk::cref_find( ref_edges, ae );
890 [ + - ]: 657450 : const auto ged = Edge{{ m_gid[ tk::cref_find( m_lref, ed[0] ) ],
891 [ + - ]: 657450 : m_gid[ tk::cref_find( m_lref, ed[1] ) ] }};
892 : : // only update edges that are on chare boundary OR unlocked
893 [ + - ][ + - ]: 1314900 : if (m_localEdgeData.find(ged) == m_localEdgeData.end() ||
[ + + ]
894 [ + - ][ + + ]: 657450 : std::get<2>(m_localEdgeData[ged]) == AMR::Edge_Lock_Case::unlocked) {
895 [ + - ]: 640031 : m_localEdgeData[ ged ] = { r.needs_refining, r.needs_derefining,
896 [ + - ]: 1280062 : r.lock_case };
897 : : }
898 : : }
899 : : }
900 : 140 : }
901 : :
902 : : std::tuple< std::vector< std::string >,
903 : : std::vector< std::vector< tk::real > >,
904 : : std::vector< std::string >,
905 : : std::vector< std::vector< tk::real > > >
906 : 149 : Refiner::refinementFields() const
907 : : // *****************************************************************************
908 : : // Collect mesh output fields from refiner lib
909 : : //! \return Names and fields of mesh refinement data in mesh cells and nodes
910 : : // *****************************************************************************
911 : : {
912 : : // Find number of nodes in current mesh
913 [ + - ]: 149 : auto npoin = tk::npoin_in_graph( m_inpoel );
914 : : // Generate edges surrounding points in current mesh
915 [ + - ]: 298 : auto esup = tk::genEsup( m_inpoel, 4 );
916 : :
917 : : // Update solution on current mesh
918 [ + - ]: 298 : const auto& u = solution( npoin, esup );
919 [ - + ][ - - ]: 149 : Assert( u.nunk() == npoin, "Solution uninitialized or wrong size" );
[ - - ][ - - ]
920 : :
921 : : // Compute error in edges on current mesh
922 [ + - ]: 298 : auto edgeError = errorsInEdges( npoin, esup, u );
923 : :
924 : : // Transfer error from edges to cells for field output
925 [ + - ]: 298 : std::vector< tk::real > error( m_inpoel.size()/4, 0.0 );
926 [ + + ]: 226755 : for (std::size_t e=0; e<m_inpoel.size()/4; ++e) {
927 : 226606 : auto A = m_inpoel[e*4+0];
928 : 226606 : auto B = m_inpoel[e*4+1];
929 : 226606 : auto C = m_inpoel[e*4+2];
930 : 226606 : auto D = m_inpoel[e*4+3];
931 : : std::array<Edge,6> edges{{ {{A,B}}, {{B,C}}, {{A,C}},
932 : 226606 : {{A,D}}, {{B,D}}, {{C,D}} }};
933 : : // sum error from edges to elements
934 [ + + ][ + - ]: 1586242 : for (const auto& ed : edges) error[e] += tk::cref_find( edgeError, ed );
935 : 226606 : error[e] /= 6.0; // assign edge-average error to element
936 : : }
937 : :
938 : : // Prepare element fields with mesh refinement data
939 : : std::vector< std::string >
940 [ + - ][ + - ]: 1043 : elemfieldnames{ "refinement level", "cell type", "error" };
[ + - ][ + - ]
941 : 149 : auto& tet_store = m_refiner.tet_store;
942 : : std::vector< std::vector< tk::real > > elemfields{
943 : : tet_store.get_refinement_level_list(),
944 : : tet_store.get_cell_type_list(),
945 [ + - ][ + - ]: 745 : error };
[ + - ][ + - ]
946 : :
947 : : using tuple_t = std::tuple< std::vector< std::string >,
948 : : std::vector< std::vector< tk::real > >,
949 : : std::vector< std::string >,
950 : : std::vector< std::vector< tk::real > > >;
951 [ + - ]: 298 : return tuple_t{ elemfieldnames, elemfields, {}, {} };
952 : : }
953 : :
954 : : void
955 : 149 : Refiner::writeMesh( const std::string& basefilename,
956 : : uint64_t itr,
957 : : tk::real t,
958 : : CkCallback c ) const
959 : : // *****************************************************************************
960 : : // Output mesh to file(s)
961 : : //! \param[in] basefilename File name to append to
962 : : //! \param[in] itr Iteration count since a new mesh
963 : : //! \param[in] t "Physical time" to write to file. "Time" here is used to
964 : : //! designate a new time step at which the mesh is saved.
965 : : //! \param[in] c Function to continue with after the write
966 : : // *****************************************************************************
967 : : {
968 [ + - ]: 298 : auto r = refinementFields();
969 : 149 : auto& elemfieldnames = std::get< 0 >( r );
970 : 149 : auto& elemfields = std::get< 1 >( r );
971 : 149 : auto& nodefieldnames = std::get< 2 >( r );
972 : 149 : auto& nodefields = std::get< 3 >( r );
973 : :
974 : : // Prepare solution field names: depvar + component id for all eqs
975 : 149 : auto nprop = g_inputdeck.get< tag::ncomp >();
976 : 298 : std::vector< std::string > solfieldnames;
977 [ + + ]: 346 : for (std::size_t i=0; i<nprop; ++i) {
978 [ + - ]: 197 : solfieldnames.push_back(
979 [ + - ][ + - ]: 394 : g_inputdeck.get< tag::depvar >()[0] + std::to_string(i+1) );
980 : : }
981 [ - + ][ - - ]: 149 : Assert( solfieldnames.size() == nprop, "Size mismatch" );
[ - - ][ - - ]
982 : :
983 : 149 : const auto scheme = g_inputdeck.get< tag::scheme >();
984 [ + - ][ + - ]: 149 : const auto centering = ctr::Scheme().centering( scheme );
985 : 149 : auto t0 = g_inputdeck.get< tag::t0 >();
986 : :
987 : : // list of nodes/elements at which box ICs are defined
988 : 298 : std::vector< std::unordered_set< std::size_t > > inbox;
989 : 149 : tk::real V = 1.0;
990 : 298 : std::vector< tk::real > blkvols;
991 : 298 : std::unordered_map< std::size_t, std::set< std::size_t > > nodeblockid,
992 : 149 : elemblockid;
993 : :
994 : : // Prepare node or element fields for output to file
995 [ + + ]: 149 : if (centering == tk::Centering::NODE) {
996 : :
997 : : // Augment element field names with solution variable names + field ids
998 : 83 : nodefieldnames.insert( end(nodefieldnames),
999 [ + - ]: 166 : begin(solfieldnames), end(solfieldnames) );
1000 : :
1001 : : // Evaluate initial conditions on current mesh at t0
1002 [ + - ]: 166 : tk::Fields u( m_coord[0].size(), nprop );
1003 [ + - ]: 83 : g_cgpde[m_meshid].initialize( m_coord, u, t0, V, inbox, blkvols,
1004 : : nodeblockid );
1005 : :
1006 : : // Extract all scalar components from solution for output to file
1007 [ + + ]: 198 : for (std::size_t i=0; i<nprop; ++i)
1008 [ + - ][ + - ]: 115 : nodefields.push_back( u.extract_comp( i ) );
1009 : :
1010 [ + - ]: 66 : } else if (centering == tk::Centering::ELEM) {
1011 : :
1012 : : // Augment element field names with solution variable names + field ids
1013 : 66 : elemfieldnames.insert( end(elemfieldnames),
1014 [ + - ]: 132 : begin(solfieldnames), end(solfieldnames) );
1015 : :
1016 : 66 : auto ndof = g_inputdeck.get< tag::ndof >();
1017 [ + - ]: 132 : tk::Fields lhs( m_inpoel.size()/4, ndof*nprop );
1018 : :
1019 : : // Generate left hand side for DG and evaluate initial conditions on
1020 : : // current mesh at t0
1021 [ + - ]: 132 : auto geoElem = tk::genGeoElemTet( m_inpoel, m_coord );
1022 [ + - ]: 132 : auto u = lhs;
1023 [ - + ]: 66 : if (scheme == ctr::SchemeType::FV) {
1024 [ - - ]: 0 : g_fvpde[m_meshid].lhs( geoElem, lhs );
1025 [ - - ]: 0 : g_fvpde[m_meshid].initialize( lhs, m_inpoel, m_coord, inbox, elemblockid,
1026 : 0 : u, t0, m_inpoel.size()/4 );
1027 : : }
1028 : : else {
1029 [ + - ]: 66 : g_dgpde[m_meshid].lhs( geoElem, lhs );
1030 [ + - ]: 132 : g_dgpde[m_meshid].initialize( lhs, m_inpoel, m_coord, inbox, elemblockid,
1031 : 66 : u, t0, m_inpoel.size()/4 );
1032 : : }
1033 : :
1034 : : // Extract all scalar components from solution for output to file
1035 [ + + ]: 148 : for (std::size_t i=0; i<nprop; ++i)
1036 [ + - ][ + - ]: 82 : elemfields.push_back( u.extract_comp( i ) );
1037 : : }
1038 : :
1039 : : // Output mesh
1040 : : m_meshwriter[ CkNodeFirst( CkMyNode() ) ].
1041 [ + - ]: 298 : write( m_meshid, /*meshoutput = */ true, /*fieldoutput = */ true, itr, 1, t,
1042 [ + - ]: 149 : thisIndex, basefilename, m_inpoel, m_coord, m_bface,
1043 [ + - ][ + - ]: 298 : tk::remap(m_bnode,m_lid), tk::remap(m_triinpoel,m_lid),
1044 : : elemfieldnames, nodefieldnames, {}, {}, elemfields, nodefields, {},
1045 : : {}, {}, c );
1046 : 149 : }
1047 : :
1048 : : void
1049 : 177 : Refiner::perform()
1050 : : // *****************************************************************************
1051 : : // Perform mesh refinement and decide how to continue
1052 : : //! \details First the mesh refiner object is called to perform a single step
1053 : : //! of mesh refinement. Then, if this function is called during a step
1054 : : //! (potentially multiple levels of) initial AMR, it evaluates whether to do
1055 : : //! another one. If it is called during time stepping, this concludes the
1056 : : //! single mesh refinement step and the new mesh is sent to the PDE worker
1057 : : //! (Discretization).
1058 : : // *****************************************************************************
1059 : : {
1060 : : // Save old tets and their ids before performing refinement. Outref is always
1061 : : // followed by outderef, so to the outside world, the mesh is uchanged, thus
1062 : : // no update.
1063 [ + + ][ + + ]: 177 : if (m_mode != RefMode::OUTREF && m_mode != RefMode::OUTDEREF) {
1064 : 111 : m_oldTets.clear();
1065 [ + + ]: 129885 : for (const auto& [ id, tet ] : m_refiner.tet_store.tets) {
1066 [ + - ]: 129774 : m_oldTets.insert( tet );
1067 : : }
1068 : 111 : m_oldntets = m_oldTets.size();
1069 : : }
1070 : :
1071 [ + + ]: 177 : if (m_mode == RefMode::T0REF) {
1072 : :
1073 : : // Refine mesh based on next initial refinement type
1074 [ + - ]: 111 : if (!m_initref.empty()) {
1075 : 111 : auto r = m_initref.back(); // consume (reversed) list from its back
1076 [ + + ]: 111 : if (r == ctr::AMRInitialType::UNIFORM_DEREFINE)
1077 : 36 : m_refiner.perform_derefinement();
1078 : : else
1079 : 75 : m_refiner.perform_refinement();
1080 : : }
1081 : :
1082 : : } else {
1083 : :
1084 : : // TODO: does not work yet, fix as above
1085 : 66 : m_refiner.perform_refinement();
1086 : 66 : m_refiner.perform_derefinement();
1087 : : }
1088 : :
1089 : : // Remove temporary edge-locks resulting from the parallel compatibility
1090 : 177 : m_refiner.remove_edge_locks(1);
1091 : 177 : m_refiner.remove_edge_temp_locks();
1092 : :
1093 : : //auto& tet_store = m_refiner.tet_store;
1094 : : //std::cout << "before ref: " << tet_store.marked_refinements.size() << ", " << tet_store.marked_derefinements.size() << ", " << tet_store.size() << ", " << tet_store.get_active_inpoel().size() << '\n';
1095 : : //std::cout << "after ref: " << tet_store.marked_refinements.size() << ", " << tet_store.marked_derefinements.size() << ", " << tet_store.size() << ", " << tet_store.get_active_inpoel().size() << '\n';
1096 : : //std::cout << "after deref: " << tet_store.marked_refinements.size() << ", " << tet_store.marked_derefinements.size() << ", " << tet_store.size() << ", " << tet_store.get_active_inpoel().size() << '\n';
1097 : :
1098 : : // Update volume and boundary mesh
1099 : 177 : updateMesh();
1100 : :
1101 : : // Save mesh at every initial refinement step (mainly for debugging). Will
1102 : : // replace with just a 'next()' in production.
1103 [ + + ]: 177 : if (m_mode == RefMode::T0REF) {
1104 : :
1105 : 111 : auto l = m_ninitref - m_initref.size() + 1; // num initref steps completed
1106 : 111 : auto t0 = g_inputdeck.get< tag::t0 >();
1107 : : // Generate times equally subdividing t0-1...t0 to ninitref steps
1108 : 111 : auto t =
1109 : 111 : t0 - 1.0 + static_cast<tk::real>(l)/static_cast<tk::real>(m_ninitref);
1110 : 111 : auto itr = l;
1111 : : // Output mesh after refinement step
1112 [ + - ][ + - ]: 111 : writeMesh( "t0ref", itr, t,
1113 [ + - ][ + - ]: 222 : CkCallback( CkIndex_Refiner::next(), thisProxy[thisIndex] ) );
1114 : :
1115 : : } else {
1116 : :
1117 : 66 : next();
1118 : :
1119 : : }
1120 : 177 : }
1121 : :
1122 : : void
1123 : 177 : Refiner::next()
1124 : : // *****************************************************************************
1125 : : // Continue after finishing a refinement step
1126 : : // *****************************************************************************
1127 : : {
1128 [ + + ]: 177 : if (m_mode == RefMode::T0REF) {
1129 : :
1130 : : // Remove initial mesh refinement step from list
1131 [ + - ]: 111 : if (!m_initref.empty()) m_initref.pop_back();
1132 : : // Continue to next initial AMR step or finish
1133 [ + + ]: 111 : if (!m_initref.empty()) t0ref(); else endt0ref();
1134 : :
1135 [ - + ]: 66 : } else if (m_mode == RefMode::DTREF) {
1136 : :
1137 : : // Send new mesh, solution, and communication data back to PDE worker
1138 [ - - ]: 0 : m_scheme[m_meshid].ckLocal< Scheme::resizePostAMR >( thisIndex, m_ginpoel,
1139 : 0 : m_el, m_coord, m_addedNodes, m_addedTets, m_removedNodes, m_amrNodeMap,
1140 : 0 : m_nodeCommMap, m_bface, m_bnode, m_triinpoel, m_elemblockid );
1141 : :
1142 [ + + ]: 66 : } else if (m_mode == RefMode::OUTREF) {
1143 : :
1144 : : // Store field output mesh
1145 : 33 : m_outref_ginpoel = m_ginpoel;
1146 : 33 : m_outref_el = m_el;
1147 : 33 : m_outref_coord = m_coord;
1148 : 33 : m_outref_addedNodes = m_addedNodes;
1149 : 33 : m_outref_addedTets = m_addedTets;
1150 : 33 : m_outref_nodeCommMap = m_nodeCommMap;
1151 : 33 : m_outref_bface = m_bface;
1152 : 33 : m_outref_bnode = m_bnode;
1153 : 33 : m_outref_triinpoel = m_triinpoel;
1154 : :
1155 : : // Derefine mesh to the state previous to field output
1156 [ + - ]: 33 : outref( m_outref_bface, m_outref_bnode, m_outref_triinpoel, m_writeCallback,
1157 : : RefMode::OUTDEREF );
1158 : :
1159 [ + - ]: 33 : } else if (m_mode == RefMode::OUTDEREF) {
1160 : :
1161 : : // Send field output mesh to PDE worker
1162 [ + - ]: 66 : m_scheme[m_meshid].ckLocal< Scheme::extractFieldOutput >( thisIndex,
1163 : 33 : m_outref_ginpoel, m_outref_el, m_outref_coord, m_outref_addedNodes,
1164 : 33 : m_outref_addedTets, m_outref_nodeCommMap, m_outref_bface, m_outref_bnode,
1165 : 33 : m_outref_triinpoel, m_writeCallback );
1166 : :
1167 [ - - ][ - - ]: 0 : } else Throw( "RefMode not implemented" );
[ - - ]
1168 : 177 : }
1169 : :
1170 : : void
1171 : 1943 : Refiner::endt0ref()
1172 : : // *****************************************************************************
1173 : : // Finish initial mesh refinement
1174 : : //! \details This function is called as after initial mesh refinement has
1175 : : //! finished. If initial mesh reifnement was not configured by the user, this
1176 : : //! is the point where we continue after the constructor, by computing the
1177 : : //! total number of elements across the whole problem.
1178 : : // *****************************************************************************
1179 : : {
1180 : : // create sorter Charm++ chare array elements using dynamic insertion
1181 [ + - ][ + - ]: 3886 : m_sorter[ thisIndex ].insert( m_meshid, m_host, m_meshwriter, m_cbs, m_scheme,
1182 [ + - ][ + - ]: 3886 : CkCallback(CkIndex_Refiner::reorder(), thisProxy[thisIndex]), m_ginpoel,
1183 [ + - ]: 1943 : m_coordmap, m_el, m_bface, m_triinpoel, m_bnode, m_elemblockid, m_nchare );
1184 : :
1185 : : // Compute final number of cells across whole problem
1186 : : std::vector< std::size_t >
1187 [ + - ]: 3886 : meshdata{ m_meshid, m_ginpoel.size()/4, m_coord[0].size() };
1188 [ + - ]: 1943 : contribute( meshdata, CkReduction::sum_ulong, m_cbr.get< tag::refined >() );
1189 : :
1190 : : // // Free up memory if no dtref
1191 : : // if (!g_inputdeck.get< tag::amr, tag::dtref >()) {
1192 : : // tk::destroy( m_ginpoel );
1193 : : // tk::destroy( m_el );
1194 : : // tk::destroy( m_coordmap );
1195 : : // tk::destroy( m_coord );
1196 : : // tk::destroy( m_bface );
1197 : : // tk::destroy( m_bnode );
1198 : : // tk::destroy( m_triinpoel );
1199 : : // tk::destroy( m_initref );
1200 : : // tk::destroy( m_ch );
1201 : : // tk::destroy( m_edgech );
1202 : : // tk::destroy( m_chedge );
1203 : : // tk::destroy( m_localEdgeData );
1204 : : // tk::destroy( m_remoteEdgeData );
1205 : : // tk::destroy( m_remoteEdges );
1206 : : // tk::destroy( m_intermediates );
1207 : : // tk::destroy( m_nodeCommMap );
1208 : : // tk::destroy( m_oldTets );
1209 : : // tk::destroy( m_addedNodes );
1210 : : // tk::destroy( m_addedTets );
1211 : : // tk::destroy( m_coarseBndFaces );
1212 : : // tk::destroy( m_coarseBndNodes );
1213 : : // tk::destroy( m_rid );
1214 : : // // tk::destroy( m_oldrid );
1215 : : // tk::destroy( m_lref );
1216 : : // // tk::destroy( m_oldparent );
1217 : : // }
1218 : 1943 : }
1219 : :
1220 : : void
1221 : 90 : Refiner::uniformRefine()
1222 : : // *****************************************************************************
1223 : : // Do uniform mesh refinement
1224 : : // *****************************************************************************
1225 : : {
1226 : : // Do uniform refinement
1227 : 90 : m_refiner.mark_uniform_refinement();
1228 : :
1229 : : // Update our extra-edge store based on refiner
1230 : 90 : updateEdgeData();
1231 : :
1232 : : // Set number of extra edges to be zero, skipping correction (if this is the
1233 : : // only step in this refinement step)
1234 : 90 : m_extra = 0;
1235 : 90 : }
1236 : :
1237 : : void
1238 : 69 : Refiner::uniformDeRefine()
1239 : : // *****************************************************************************
1240 : : // Do uniform mesh derefinement
1241 : : // *****************************************************************************
1242 : : {
1243 : : // Do uniform derefinement
1244 : 69 : m_refiner.mark_uniform_derefinement();
1245 : :
1246 : : // Update our extra-edge store based on refiner
1247 : 69 : updateEdgeData();
1248 : :
1249 : : // Set number of extra edges to be zero, skipping correction (if this is the
1250 : : // only step in this refinement step)
1251 : 69 : m_extra = 0;
1252 : 69 : }
1253 : :
1254 : : Refiner::EdgeError
1255 : 165 : Refiner::errorsInEdges(
1256 : : std::size_t npoin,
1257 : : const std::pair< std::vector<std::size_t>, std::vector<std::size_t> >& esup,
1258 : : const tk::Fields& u ) const
1259 : : // *****************************************************************************
1260 : : // Compute errors in edges
1261 : : //! \param[in] npoin Number nodes in current mesh (partition)
1262 : : //! \param[in] esup Elements surrounding points linked vectors
1263 : : //! \param[in] u Solution evaluated at mesh nodes for all scalar components
1264 : : //! \return A map associating errors (real values between 0.0 and 1.0 incusive)
1265 : : //! to edges (2 local node IDs)
1266 : : // *****************************************************************************
1267 : : {
1268 : : // Get the indices (in the system of systems) of refinement variables and the
1269 : : // error indicator configured
1270 : 165 : auto ncomp = g_inputdeck.get< tag::ncomp >();
1271 : 165 : auto errtype = g_inputdeck.get< tag::amr, tag::error >();
1272 : :
1273 : : // Compute points surrounding points
1274 [ + - ]: 330 : auto psup = tk::genPsup( m_inpoel, 4, esup );
1275 : :
1276 : : // Compute errors in ICs and define refinement criteria for edges
1277 : : AMR::Error error;
1278 : 165 : EdgeError edgeError;
1279 : :
1280 [ + + ]: 60985 : for (std::size_t p=0; p<npoin; ++p) { // for all mesh nodes on this chare
1281 [ + + ]: 721982 : for (auto q : tk::Around(psup,p)) { // for all nodes surrounding p
1282 : 661162 : tk::real cmax = 0.0;
1283 [ + - ]: 661162 : AMR::edge_t e(p,q);
1284 [ + + ]: 1550636 : for (std::size_t i=0; i<ncomp; ++i) { // for all refinement variables
1285 [ + - ]: 889474 : auto c = error.scalar( u, e, i, m_coord, m_inpoel, esup, errtype );
1286 [ + + ]: 889474 : if (c > cmax) cmax = c; // find max error at edge
1287 : : }
1288 [ + - ]: 661162 : edgeError[ {{p,q}} ] = cmax; // associate error to edge
1289 : : }
1290 : : }
1291 : :
1292 : 330 : return edgeError;
1293 : : }
1294 : :
1295 : : tk::Fields
1296 : 165 : Refiner::solution( std::size_t npoin,
1297 : : const std::pair< std::vector< std::size_t >,
1298 : : std::vector< std::size_t > >& esup ) const
1299 : : // *****************************************************************************
1300 : : // Update (or evaluate) solution on current mesh
1301 : : //! \param[in] npoin Number nodes in current mesh (partition)
1302 : : //! \param[in] esup Elements surrounding points linked vectors
1303 : : //! \return Solution updated/evaluated for all scalar components
1304 : : // *****************************************************************************
1305 : : {
1306 : : // Get solution whose error to evaluate
1307 : 165 : tk::Fields u;
1308 : :
1309 [ + - ]: 165 : if (m_mode == RefMode::T0REF) {
1310 : :
1311 : : // Evaluate initial conditions at mesh nodes
1312 [ + - ]: 165 : u = nodeinit( npoin, esup );
1313 : :
1314 [ - - ]: 0 : } else if (m_mode == RefMode::DTREF) {
1315 : :
1316 : : // Query current solution
1317 [ - - ]: 0 : u = m_scheme[m_meshid].ckLocal< Scheme::solution >( thisIndex );
1318 : :
1319 : 0 : const auto scheme = g_inputdeck.get< tag::scheme >();
1320 [ - - ][ - - ]: 0 : const auto centering = ctr::Scheme().centering( scheme );
1321 [ - - ]: 0 : if (centering == tk::Centering::ELEM) {
1322 : :
1323 : : // ...
1324 [ - - ][ - - ]: 0 : Throw("Element-based solution handling not implemented in Refiner");
[ - - ]
1325 : :
1326 : : }
1327 : :
1328 [ - - ]: 0 : } else if (m_mode == RefMode::OUTREF) {
1329 : :
1330 : :
1331 : :
1332 [ - - ]: 0 : } else if (m_mode == RefMode::OUTDEREF) {
1333 : :
1334 : :
1335 : :
1336 [ - - ][ - - ]: 0 : } else Throw( "RefMode not implemented" );
[ - - ]
1337 : :
1338 : 165 : return u;
1339 : : }
1340 : :
1341 : : void
1342 : 16 : Refiner::errorRefine()
1343 : : // *****************************************************************************
1344 : : // Do error-based mesh refinement and derefinement
1345 : : // *****************************************************************************
1346 : : {
1347 : : // Find number of nodes in old mesh
1348 [ + - ]: 16 : auto npoin = tk::npoin_in_graph( m_inpoel );
1349 : : // Generate edges surrounding points in old mesh
1350 [ + - ]: 32 : auto esup = tk::genEsup( m_inpoel, 4 );
1351 : :
1352 : : // Update solution on current mesh
1353 [ + - ]: 32 : const auto& u = solution( npoin, esup );
1354 [ - + ][ - - ]: 16 : Assert( u.nunk() == npoin, "Solution uninitialized or wrong size" );
[ - - ][ - - ]
1355 : :
1356 : : using AMR::edge_t;
1357 : : using AMR::edge_tag;
1358 : :
1359 : : // Compute error in edges. Tag edge for refinement if error exceeds
1360 : : // refinement tolerance, tag edge for derefinement if error is below
1361 : : // derefinement tolerance.
1362 : 16 : auto tolref = g_inputdeck.get< tag::amr, tag::tol_refine >();
1363 : 16 : auto tolderef = g_inputdeck.get< tag::amr, tag::tol_derefine >();
1364 : 16 : std::vector< std::pair< edge_t, edge_tag > > tagged_edges;
1365 [ + - ][ + + ]: 3378 : for (const auto& e : errorsInEdges(npoin,esup,u)) {
1366 [ + + ]: 3362 : if (e.second > tolref) {
1367 [ + - ][ + - ]: 3312 : tagged_edges.push_back( { edge_t( m_rid[e.first[0]], m_rid[e.first[1]] ),
1368 : 3312 : edge_tag::REFINE } );
1369 [ + + ]: 1706 : } else if (e.second < tolderef) {
1370 [ + - ][ + - ]: 3132 : tagged_edges.push_back( { edge_t( m_rid[e.first[0]], m_rid[e.first[1]] ),
1371 : 3132 : edge_tag::DEREFINE } );
1372 : : }
1373 : : }
1374 : :
1375 : : // Do error-based refinement
1376 [ + - ]: 16 : m_refiner.mark_error_refinement( tagged_edges );
1377 : :
1378 : : // Update our extra-edge store based on refiner
1379 [ + - ]: 16 : updateEdgeData();
1380 : :
1381 : : // Set number of extra edges to a nonzero number, triggering correction
1382 : 16 : m_extra = 1;
1383 : 16 : }
1384 : :
1385 : : void
1386 : 0 : Refiner::edgelistRefine()
1387 : : // *****************************************************************************
1388 : : // Do mesh refinement based on user explicitly tagging edges
1389 : : // *****************************************************************************
1390 : : {
1391 : : // Get user-defined node-pairs (edges) to tag for refinement
1392 : 0 : const auto& edgenodelist = g_inputdeck.get< tag::amr, tag::edgelist >();
1393 : :
1394 [ - - ]: 0 : if (!edgenodelist.empty()) { // if user explicitly tagged edges
1395 : : // Find number of nodes in old mesh
1396 [ - - ]: 0 : auto npoin = tk::npoin_in_graph( m_inpoel );
1397 : : // Generate edges surrounding points in old mesh
1398 [ - - ]: 0 : auto esup = tk::genEsup( m_inpoel, 4 );
1399 [ - - ]: 0 : auto psup = tk::genPsup( m_inpoel, 4, esup );
1400 : :
1401 : 0 : EdgeSet useredges;
1402 [ - - ]: 0 : for (std::size_t i=0; i<edgenodelist.size()/2; ++i)
1403 [ - - ]: 0 : useredges.insert( {{ {edgenodelist[i*2+0], edgenodelist[i*2+1]} }} );
1404 : :
1405 : : using AMR::edge_t;
1406 : : using AMR::edge_tag;
1407 : :
1408 : : // Tag edges the user configured
1409 : 0 : std::vector< std::pair< edge_t, edge_tag > > tagged_edges;
1410 [ - - ]: 0 : for (std::size_t p=0; p<npoin; ++p) // for all mesh nodes on this chare
1411 [ - - ]: 0 : for (auto q : tk::Around(psup,p)) { // for all nodes surrounding p
1412 : 0 : Edge e{{ m_gid[p], m_gid[q] }};
1413 [ - - ]: 0 : auto f = useredges.find(e);
1414 [ - - ]: 0 : if (f != end(useredges)) { // tag edge if on user's list
1415 [ - - ][ - - ]: 0 : tagged_edges.push_back( { edge_t( m_rid[p], m_rid[q] ),
1416 : 0 : edge_tag::REFINE } );
1417 [ - - ]: 0 : useredges.erase( f );
1418 : : }
1419 : : }
1420 : :
1421 : : // Do error-based refinement
1422 [ - - ]: 0 : m_refiner.mark_error_refinement( tagged_edges );
1423 : :
1424 : : // Update our extra-edge store based on refiner
1425 [ - - ]: 0 : updateEdgeData();
1426 : :
1427 : : // Set number of extra edges to a nonzero number, triggering correction
1428 : 0 : m_extra = 1;
1429 : : }
1430 : 0 : }
1431 : :
1432 : : void
1433 : 2 : Refiner::coordRefine()
1434 : : // *****************************************************************************
1435 : : // Do mesh refinement based on tagging edges based on end-point coordinates
1436 : : // *****************************************************************************
1437 : : {
1438 : : // Get user-defined half-world coordinates
1439 : 2 : const auto& amr_coord = g_inputdeck.get< tag::amr, tag::coords >();
1440 : 2 : auto xminus = amr_coord.get< tag::xminus >();
1441 : 2 : auto xplus = amr_coord.get< tag::xplus >();
1442 : 2 : auto yminus = amr_coord.get< tag::yminus >();
1443 : 2 : auto yplus = amr_coord.get< tag::yplus >();
1444 : 2 : auto zminus = amr_coord.get< tag::zminus >();
1445 : 2 : auto zplus = amr_coord.get< tag::zplus >();
1446 : :
1447 : : // The default is the largest representable double
1448 : 2 : auto eps =
1449 : : std::numeric_limits< tk::real >::epsilon();
1450 : 2 : const auto& amr_defcoord = g_inputdeck_defaults.get< tag::amr, tag::coords >();
1451 : 2 : auto xminus_default = amr_defcoord.get< tag::xminus >();
1452 : 2 : auto xplus_default = amr_defcoord.get< tag::xplus >();
1453 : 2 : auto yminus_default = amr_defcoord.get< tag::yminus >();
1454 : 2 : auto yplus_default = amr_defcoord.get< tag::yplus >();
1455 : 2 : auto zminus_default = amr_defcoord.get< tag::zminus >();
1456 : 2 : auto zplus_default = amr_defcoord.get< tag::zplus >();
1457 : :
1458 : : // Decide if user has configured the half-world
1459 : 2 : bool xm = std::abs(xminus - xminus_default) > eps ? true : false;
1460 : 2 : bool xp = std::abs(xplus - xplus_default) > eps ? true : false;
1461 : 2 : bool ym = std::abs(yminus - yminus_default) > eps ? true : false;
1462 : 2 : bool yp = std::abs(yplus - yplus_default) > eps ? true : false;
1463 : 2 : bool zm = std::abs(zminus - zminus_default) > eps ? true : false;
1464 : 2 : bool zp = std::abs(zplus - zplus_default) > eps ? true : false;
1465 : :
1466 : : using AMR::edge_t;
1467 : : using AMR::edge_tag;
1468 : :
1469 [ - + ][ - - ]: 2 : if (xm || xp || ym || yp || zm || zp) { // if any half-world configured
[ - - ][ - - ]
[ - - ][ - - ]
1470 : : // Find number of nodes in old mesh
1471 [ + - ]: 2 : auto npoin = tk::npoin_in_graph( m_inpoel );
1472 : : // Generate edges surrounding points in old mesh
1473 [ + - ]: 4 : auto esup = tk::genEsup( m_inpoel, 4 );
1474 [ + - ]: 4 : auto psup = tk::genPsup( m_inpoel, 4, esup );
1475 : : // Get access to node coordinates
1476 : 2 : const auto& x = m_coord[0];
1477 : 2 : const auto& y = m_coord[1];
1478 : 2 : const auto& z = m_coord[2];
1479 : : // Compute edges to be tagged for refinement
1480 : 2 : std::vector< std::pair< edge_t, edge_tag > > tagged_edges;
1481 [ + + ]: 621 : for (std::size_t p=0; p<npoin; ++p) // for all mesh nodes on this chare
1482 [ + + ]: 7181 : for (auto q : tk::Around(psup,p)) { // for all nodes surrounding p
1483 : 6562 : Edge e{{p,q}};
1484 : :
1485 : 6562 : bool t = true;
1486 [ + - ][ + + ]: 6562 : if (xm) { if (x[p]>xminus && x[q]>xminus) t = false; }
[ + + ][ + + ]
1487 [ - + ][ - - ]: 6562 : if (xp) { if (x[p]<xplus && x[q]<xplus) t = false; }
[ - - ][ - - ]
1488 [ - + ][ - - ]: 6562 : if (ym) { if (y[p]>yminus && y[q]>yminus) t = false; }
[ - - ][ - - ]
1489 [ - + ][ - - ]: 6562 : if (yp) { if (y[p]<yplus && y[q]<yplus) t = false; }
[ - - ][ - - ]
1490 [ - + ][ - - ]: 6562 : if (zm) { if (z[p]>zminus && z[q]>zminus) t = false; }
[ - - ][ - - ]
1491 [ - + ][ - - ]: 6562 : if (zp) { if (z[p]<zplus && z[q]<zplus) t = false; }
[ - - ][ - - ]
1492 : :
1493 [ + + ]: 6562 : if (t) {
1494 [ + - ][ + - ]: 9596 : tagged_edges.push_back( { edge_t( m_rid[e[0]], m_rid[e[1]] ),
1495 : 9596 : edge_tag::REFINE } );
1496 : : }
1497 : : }
1498 : :
1499 : : // Do error-based refinement
1500 [ + - ]: 2 : m_refiner.mark_error_refinement( tagged_edges );
1501 : :
1502 : : // Update our extra-edge store based on refiner
1503 [ + - ]: 2 : updateEdgeData();
1504 : :
1505 : : // Set number of extra edges to a nonzero number, triggering correction
1506 : 2 : m_extra = 1;
1507 : : }
1508 : 2 : }
1509 : :
1510 : : tk::Fields
1511 : 165 : Refiner::nodeinit( std::size_t npoin,
1512 : : const std::pair< std::vector< std::size_t >,
1513 : : std::vector< std::size_t > >& esup ) const
1514 : : // *****************************************************************************
1515 : : // Evaluate initial conditions (IC) at mesh nodes
1516 : : //! \param[in] npoin Number points in mesh (on this chare)
1517 : : //! \param[in] esup Elements surrounding points as linked lists, see tk::genEsup
1518 : : //! \return Initial conditions (evaluated at t0) at nodes
1519 : : // *****************************************************************************
1520 : : {
1521 : 165 : auto t0 = g_inputdeck.get< tag::t0 >();
1522 : 165 : auto nprop = g_inputdeck.get< tag::ncomp >();
1523 : :
1524 : : // Will store nodal ICs
1525 [ + - ]: 165 : tk::Fields u( m_coord[0].size(), nprop );
1526 : :
1527 : : // Evaluate ICs differently depending on nodal or cell-centered discretization
1528 : 165 : const auto scheme = g_inputdeck.get< tag::scheme >();
1529 [ + - ][ + - ]: 165 : const auto centering = ctr::Scheme().centering( scheme );
1530 : : // list of nodes/elements at which box ICs are defined
1531 : 330 : std::vector< std::unordered_set< std::size_t > > inbox;
1532 : 165 : tk::real V = 1.0;
1533 : 330 : std::vector< tk::real > blkvols;
1534 : 330 : std::unordered_map< std::size_t, std::set< std::size_t > > nodeblockid,
1535 : 330 : elemblockid;
1536 : :
1537 [ + + ]: 165 : if (centering == tk::Centering::NODE) {
1538 : :
1539 : : // Evaluate ICs for all scalar components integrated
1540 [ + - ]: 99 : g_cgpde[m_meshid].initialize( m_coord, u, t0, V, inbox, blkvols,
1541 : : nodeblockid );
1542 : :
1543 [ + - ]: 66 : } else if (centering == tk::Centering::ELEM) {
1544 : :
1545 [ + - ]: 132 : auto esuel = tk::genEsuelTet( m_inpoel, esup ); // elems surrounding elements
1546 : : // Initialize cell-based unknowns
1547 [ + - ]: 132 : tk::Fields ue( m_inpoel.size()/4, nprop );
1548 [ + - ]: 132 : auto lhs = ue;
1549 [ + - ]: 132 : auto geoElem = tk::genGeoElemTet( m_inpoel, m_coord );
1550 [ - + ]: 66 : if (scheme == ctr::SchemeType::FV) {
1551 [ - - ]: 0 : g_fvpde[m_meshid].lhs( geoElem, lhs );
1552 [ - - ]: 0 : g_fvpde[m_meshid].initialize( lhs, m_inpoel, m_coord, inbox, elemblockid,
1553 : 0 : ue, t0, esuel.size()/4 );
1554 : : }
1555 : : else {
1556 [ + - ]: 66 : g_dgpde[m_meshid].lhs( geoElem, lhs );
1557 [ + - ]: 132 : g_dgpde[m_meshid].initialize( lhs, m_inpoel, m_coord, inbox, elemblockid,
1558 : 66 : ue, t0, esuel.size()/4 );
1559 : : }
1560 : :
1561 : : // Transfer initial conditions from cells to nodes
1562 [ + + ]: 34172 : for (std::size_t p=0; p<npoin; ++p) { // for all mesh nodes on this chare
1563 [ + - ]: 68212 : std::vector< tk::real > up( nprop, 0.0 );
1564 : 34106 : tk::real vol = 0.0;
1565 [ + + ]: 533606 : for (auto e : tk::Around(esup,p)) { // for all cells around node p
1566 : : // compute nodal volume: every element contributes their volume / 4
1567 [ + - ]: 499500 : vol += geoElem(e,0) / 4.0;
1568 : : // sum cell value to node weighed by cell volume / 4
1569 [ + + ]: 1209240 : for (std::size_t c=0; c<nprop; ++c)
1570 [ + - ][ + - ]: 709740 : up[c] += ue[e][c] * geoElem(e,0) / 4.0;
1571 : : }
1572 : : // store nodal value
1573 [ + + ][ + - ]: 81412 : for (std::size_t c=0; c<nprop; ++c) u(p,c) = up[c] / vol;
1574 : : }
1575 : :
1576 [ - - ][ - - ]: 0 : } else Throw( "Scheme centring not handled for nodal initialization" );
[ - - ]
1577 : :
1578 [ - + ][ - - ]: 165 : Assert( u.nunk() == m_coord[0].size(), "Size mismatch" );
[ - - ][ - - ]
1579 [ - + ][ - - ]: 165 : Assert( u.nprop() == nprop, "Size mismatch" );
[ - - ][ - - ]
1580 : :
1581 : 330 : return u;
1582 : : }
1583 : :
1584 : : void
1585 : 177 : Refiner::updateMesh()
1586 : : // *****************************************************************************
1587 : : // Update old mesh after refinement
1588 : : // *****************************************************************************
1589 : : {
1590 : : // Get refined mesh connectivity
1591 [ + - ]: 177 : const auto& refinpoel = m_refiner.tet_store.get_active_inpoel();
1592 [ - + ][ - - ]: 177 : Assert( refinpoel.size()%4 == 0, "Inconsistent refined mesh connectivity" );
[ - - ][ - - ]
1593 : :
1594 : : // Generate unique node lists of old and refined mesh using local ids
1595 [ + - ]: 354 : auto rinpoel = m_inpoel;
1596 [ + - ]: 177 : tk::remap( rinpoel, m_rid );
1597 [ + - ]: 354 : std::unordered_set< std::size_t > old( begin(rinpoel), end(rinpoel) );
1598 [ + - ]: 177 : std::unordered_set< std::size_t > ref( begin(refinpoel), end(refinpoel) );
1599 : :
1600 : : // Augment refiner id -> local node id map with newly added nodes
1601 : 177 : std::size_t l = m_lref.size();
1602 [ + + ][ + - ]: 103036 : for (auto r : ref) if (old.find(r) == end(old)) m_lref[r] = l++;
[ + + ][ + - ]
1603 : :
1604 : : // Get nodal communication map from Discretization worker
1605 [ + - ]: 177 : if ( m_mode == RefMode::DTREF ||
1606 [ + + ]: 177 : m_mode == RefMode::OUTREF ||
1607 [ + + ]: 144 : m_mode == RefMode::OUTDEREF ) {
1608 : : m_nodeCommMap =
1609 [ + - ][ + - ]: 66 : m_scheme[m_meshid].disc()[thisIndex].ckLocal()->NodeCommMap();
[ + - ]
1610 : : }
1611 : :
1612 : : // Update mesh and solution after refinement
1613 [ + - ]: 177 : newVolMesh( old, ref );
1614 : :
1615 : : // Update mesh connectivity from refiner lib, remapping refiner to local ids
1616 [ + - ][ + - ]: 177 : m_inpoel = m_refiner.tet_store.get_active_inpoel();
1617 [ + - ]: 177 : tk::remap( m_inpoel, m_lref );
1618 : :
1619 : : // Update mesh connectivity with new global node ids
1620 [ + - ]: 177 : m_ginpoel = m_inpoel;
1621 [ + - ][ - + ]: 177 : Assert( tk::uniquecopy(m_ginpoel).size() == m_coord[0].size(),
[ - - ][ - - ]
[ - - ]
1622 : : "Size mismatch" );
1623 [ + - ]: 177 : tk::remap( m_ginpoel, m_gid );
1624 : :
1625 : : // Update boundary face and node information
1626 [ + - ]: 177 : newBndMesh( ref );
1627 : :
1628 : : // Augment node communication map with newly added nodes on chare-boundary
1629 [ + - ][ + + ]: 177 : if (m_mode == RefMode::DTREF || m_mode == RefMode::OUTREF) {
1630 [ + + ]: 105 : for (const auto& [ neighborchare, edges ] : m_remoteEdges) {
1631 [ + - ]: 72 : auto& nodes = tk::ref_find( m_nodeCommMap, neighborchare );
1632 [ + + ]: 24192 : for (const auto& e : edges) {
1633 : : // If parent nodes were part of the node communication map for chare
1634 [ + - ][ + + ]: 24120 : if (nodes.find(e[0]) != end(nodes) && nodes.find(e[1]) != end(nodes)) {
[ + - ][ + + ]
[ + + ]
1635 : : // Add new node if local id was generated for it
1636 [ + - ]: 2418 : auto n = Hash<2>()( e );
1637 [ + - ][ + + ]: 2418 : if (m_lid.find(n) != end(m_lid)) nodes.insert( n );
[ + - ]
1638 : : }
1639 : : }
1640 : : }
1641 : : }
1642 : :
1643 : : // Ensure valid mesh after refinement
1644 [ + - ][ - + ]: 177 : Assert( tk::positiveJacobians( m_inpoel, m_coord ),
[ - - ][ - - ]
[ - - ]
1645 : : "Refined mesh cell Jacobian non-positive" );
1646 : :
1647 [ + - ][ - + ]: 177 : Assert( tk::conforming( m_inpoel, m_coord, true, m_rid ),
[ - - ][ - - ]
[ - - ][ - - ]
[ - - ]
1648 : : "Chare-"+std::to_string(thisIndex)+
1649 : : " mesh not conforming after updating mesh after mesh refinement" );
1650 : :
1651 : : // Perform leak test on new mesh
1652 [ + - ][ + - ]: 177 : Assert( !tk::leakyPartition(
[ + - ][ - + ]
[ - - ][ - - ]
[ - - ]
1653 : : tk::genEsuelTet( m_inpoel, tk::genEsup(m_inpoel,4) ),
1654 : : m_inpoel, m_coord ),
1655 : : "Refined mesh partition leaky" );
1656 : 177 : }
1657 : :
1658 : : void
1659 : 177 : Refiner::newVolMesh( const std::unordered_set< std::size_t >& old,
1660 : : const std::unordered_set< std::size_t >& ref )
1661 : : // *****************************************************************************
1662 : : // Compute new volume mesh after mesh refinement
1663 : : //! \param[in] old Unique nodes of the old (unrefined) mesh using
1664 : : //! refiner-lib ids
1665 : : //! \param[in] ref Unique nodes of the refined mesh using refiner-lib ids
1666 : : // *****************************************************************************
1667 : : {
1668 : 177 : const auto& x = m_coord[0];
1669 : 177 : const auto& y = m_coord[1];
1670 : 177 : const auto& z = m_coord[2];
1671 : :
1672 : : // Generate coordinates and ids to newly added nodes after refinement
1673 : 354 : std::unordered_map< std::size_t, std::size_t > gid_add;
1674 : 177 : tk::destroy( m_addedNodes );
1675 : 177 : tk::destroy( m_removedNodes );
1676 : 177 : tk::destroy( m_amrNodeMap );
1677 [ + + ]: 103036 : for (auto r : ref) { // for all unique nodes of the refined mesh
1678 [ + - ][ + + ]: 102859 : if (old.find(r) == end(old)) { // if node is newly added
1679 : : // get (local) parent ids of newly added node
1680 [ + - ]: 73546 : auto p = m_refiner.node_connectivity.get( r );
1681 [ - + ][ - - ]: 73546 : Assert(p[0] != p[1], "Node without parent edge in newVolMesh");
[ - - ][ - - ]
1682 [ + - ][ + - ]: 73546 : Assert( old.find(p[0]) != end(old) && old.find(p[1]) != end(old),
[ + - ][ + - ]
[ - - ][ - - ]
[ - - ]
1683 : : "Parent(s) not in old mesh" );
1684 : : // local parent ids
1685 [ + - ][ + - ]: 73546 : decltype(p) lp{{tk::cref_find(m_lref,p[0]), tk::cref_find(m_lref,p[1])}};
1686 : : // global parent ids
1687 : 73546 : decltype(p) gp{{m_gid[lp[0]], m_gid[lp[1]]}};
1688 : : // generate new global ID for newly added node
1689 [ + - ]: 73546 : auto g = Hash<2>()( gp );
1690 : :
1691 : : // if node added by AMR lib has not yet been added to Refiner's new mesh
1692 [ + - ][ + - ]: 73546 : if (m_coordmap.find(g) == end(m_coordmap)) {
1693 [ - + ][ - - ]: 73546 : Assert( g >= old.size(), "Hashed id overwriting old id" );
[ - - ][ - - ]
1694 [ + - ][ - + ]: 73546 : Assert( m_lid.find(g) == end(m_lid),
[ - - ][ - - ]
[ - - ]
1695 : : "Overwriting entry global->local node ID map" );
1696 [ + - ]: 73546 : auto l = tk::cref_find( m_lref, r );
1697 : : // store newly added node id and their parent ids (local ids)
1698 [ + - ]: 73546 : m_addedNodes[r] = lp; // key = r for later update to local
1699 : : // assign new node to refiner->global map
1700 [ + - ]: 73546 : gid_add[r] = g; // key = r for later search
1701 : : // assign new node to global->local map
1702 [ + - ]: 73546 : m_lid[g] = l;
1703 : : // generate and store coordinates for newly added node
1704 : 73546 : m_coordmap.insert( {g, {{ (x[lp[0]] + x[lp[1]])/2.0,
1705 : 73546 : (y[lp[0]] + y[lp[1]])/2.0,
1706 [ + - ]: 220638 : (z[lp[0]] + z[lp[1]])/2.0 }} } );
1707 : : }
1708 : : }
1709 : : }
1710 : 177 : tk::destroy( m_coord );
1711 : :
1712 : : // generate a node map based on oldnodes+addednodes
1713 [ + - ]: 354 : std::vector< size_t > nodeVec(m_coordmap.size());
1714 [ + + ]: 152950 : for (size_t j=0; j<nodeVec.size(); ++j) {
1715 : 152773 : nodeVec[j] = j;
1716 : : }
1717 : :
1718 : : // Remove coordinates and ids of removed nodes due to derefinement
1719 : 354 : std::unordered_map< std::size_t, std::size_t > gid_rem;
1720 [ + + ]: 79404 : for (auto o : old) { // for all unique nodes of the old mesh
1721 [ + - ][ + + ]: 79227 : if (ref.find(o) == end(ref)) { // if node is no longer in new mesh
1722 [ + - ]: 49914 : auto l = tk::cref_find( m_lref, o );
1723 : 49914 : auto g = m_gid[l];
1724 : : // store local-ids of removed nodes
1725 [ + - ]: 49914 : m_removedNodes.insert(l);
1726 : : // remove derefined nodes from node comm map
1727 [ + + ]: 74034 : for (auto& [neighborchare, sharednodes] : m_nodeCommMap) {
1728 [ + - ][ - + ]: 24120 : if (sharednodes.find(g) != sharednodes.end()) {
1729 [ - - ]: 0 : sharednodes.erase(g);
1730 : : }
1731 : : }
1732 [ + - ]: 49914 : gid_rem[l] = g;
1733 [ + - ]: 49914 : m_lid.erase( g );
1734 [ + - ]: 49914 : m_coordmap.erase( g );
1735 : : }
1736 : : }
1737 : :
1738 : : // update the node map by removing the derefined nodes
1739 [ - + ][ - - ]: 177 : if (m_mode == RefMode::DTREF && m_removedNodes.size() > 0) {
[ - + ]
1740 : : // remove derefined nodes
1741 : 0 : size_t remCount = 0;
1742 : 0 : size_t origSize = nodeVec.size();
1743 [ - - ]: 0 : for (size_t j=0; j<origSize; ++j) {
1744 : 0 : auto nd = nodeVec[j-remCount];
1745 : :
1746 : 0 : bool no_change = false;
1747 : 0 : size_t nodeidx = 0;
1748 [ - - ]: 0 : for (const auto& rn : m_removedNodes) {
1749 [ - - ]: 0 : if (nd < *m_removedNodes.cbegin()) {
1750 : 0 : no_change = true;
1751 : 0 : break;
1752 : : }
1753 [ - - ]: 0 : else if (nd <= rn) {
1754 : 0 : nodeidx = rn;
1755 : 0 : break;
1756 : : }
1757 : : }
1758 : :
1759 : : // if node is out-or-range of removed nodes list, continue with next entry
1760 [ - - ]: 0 : if (no_change)
1761 : 0 : continue;
1762 : : // if not is within range of removed nodes list, erase node appropriately
1763 [ - - ]: 0 : else if (nodeidx == nd) {
1764 : : //! Difference type for iterator/pointer arithmetics
1765 : : using diff_type = std::vector< std::size_t >::difference_type;
1766 [ - - ]: 0 : nodeVec.erase(nodeVec.begin()+static_cast< diff_type >(j-remCount));
1767 : 0 : ++remCount;
1768 : : }
1769 : : }
1770 : :
1771 [ - - ][ - - ]: 0 : Assert(remCount == m_removedNodes.size(), "Incorrect number of nodes removed "
[ - - ][ - - ]
1772 : : "from node map.");
1773 : : }
1774 : :
1775 : : // invert node vector to get node map
1776 [ + + ]: 152950 : for (size_t i=0; i<nodeVec.size(); ++i) {
1777 [ + - ]: 152773 : m_amrNodeMap[nodeVec[i]] = i;
1778 : : }
1779 : :
1780 : : //// Save previous states of refiner-local node id maps before update
1781 : : //m_oldrid = m_rid;
1782 : : //m_oldlref = m_lref;
1783 : :
1784 : : // Generate new node id maps for nodes kept
1785 : 177 : tk::destroy( m_lref );
1786 [ + - ]: 354 : std::vector< std::size_t > rid( ref.size() );
1787 [ + - ]: 354 : std::vector< std::size_t > gid( ref.size() );
1788 : 177 : std::size_t l = 0; // will generate new local node id
1789 [ + + ]: 79404 : for (std::size_t i=0; i<m_gid.size(); ++i) {
1790 [ + - ][ + + ]: 79227 : if (gid_rem.find(i) == end(gid_rem)) {
1791 : 29313 : gid[l] = m_gid[i];
1792 : 29313 : rid[l] = m_rid[i];
1793 [ + - ]: 29313 : m_lref[ m_rid[i] ] = l;
1794 : 29313 : ++l;
1795 : : }
1796 : : }
1797 : : // Add newly added nodes due to refinement to node id maps
1798 [ + - ]: 354 : decltype(m_addedNodes) addedNodes( m_addedNodes.size() );
1799 [ + + ]: 73723 : for (const auto& n : gid_add) {
1800 : 73546 : auto r = n.first;
1801 : 73546 : auto g = n.second;
1802 : 73546 : gid[l] = g;
1803 : 73546 : rid[l] = r;
1804 [ + - ][ - + ]: 73546 : Assert(m_lref.find(r) == m_lref.end(), "Overwriting lref");
[ - - ][ - - ]
[ - - ]
1805 [ + - ]: 73546 : m_lref[r] = l;
1806 [ + - ]: 73546 : auto it = m_addedNodes.find( r );
1807 [ - + ][ - - ]: 73546 : Assert( it != end(m_addedNodes), "Cannot find added node" );
[ - - ][ - - ]
1808 [ + - ]: 73546 : addedNodes[l] = std::move(it->second);
1809 [ + - ][ + - ]: 73546 : addedNodes.at(l)[0] = m_amrNodeMap[addedNodes.at(l)[0]];
[ + - ]
1810 [ + - ][ + - ]: 73546 : addedNodes.at(l)[1] = m_amrNodeMap[addedNodes.at(l)[1]];
[ + - ]
1811 : 73546 : ++l;
1812 : : }
1813 [ - + ][ - - ]: 177 : Assert( m_lref.size() == ref.size(), "Size mismatch" );
[ - - ][ - - ]
1814 : : //for (auto r : ref) {
1815 : : // Assert(m_lref.find(r) != m_lref.end(), "Node missing in lref");
1816 : : //}
1817 : : //const auto& int_list = m_refiner.tet_store.intermediate_list;
1818 : : //for (auto in : int_list) {
1819 : : // Assert(m_lref.find(in) != m_lref.end(), "Interm node missing in lref: "
1820 : : // + std::to_string(in));
1821 : : //}
1822 : 177 : m_rid = std::move( rid );
1823 [ - + ][ - - ]: 177 : Assert( m_rid.size() == ref.size(), "Size mismatch" );
[ - - ][ - - ]
1824 : 177 : m_addedNodes = std::move( addedNodes );
1825 : :
1826 : : // Update node coordinates, ids, and id maps
1827 : 177 : auto& rx = m_coord[0];
1828 : 177 : auto& ry = m_coord[1];
1829 : 177 : auto& rz = m_coord[2];
1830 [ + - ]: 177 : rx.resize( ref.size() );
1831 [ + - ]: 177 : ry.resize( ref.size() );
1832 [ + - ]: 177 : rz.resize( ref.size() );
1833 [ + + ]: 103036 : for (std::size_t i=0; i<gid.size(); ++i) {
1834 [ + - ]: 102859 : tk::ref_find( m_lid, gid[i] ) = i;
1835 [ + - ]: 102859 : const auto& c = tk::cref_find( m_coordmap, gid[i] );
1836 : 102859 : rx[i] = c[0];
1837 : 102859 : ry[i] = c[1];
1838 : 102859 : rz[i] = c[2];
1839 : : }
1840 : 177 : m_gid = std::move( gid );
1841 [ + - ][ + - ]: 177 : Assert( m_gid.size() == m_lid.size() && m_gid.size() == ref.size(),
[ - - ][ - - ]
[ - - ]
1842 : : "Size mismatch" );
1843 : 177 : }
1844 : :
1845 : : std::unordered_set< std::size_t >
1846 : 3743942 : Refiner::ancestors( std::size_t n )
1847 : : // *****************************************************************************
1848 : : // Find the oldest parents of a mesh node in the AMR hierarchy
1849 : : //! \param[in] n Local node id whose ancestors to search
1850 : : //! \return Parents of local node id from the coarsest (original) mesh
1851 : : // *****************************************************************************
1852 : : {
1853 [ + - ]: 3743942 : auto d = m_refiner.node_connectivity.get( m_rid[n] );
1854 [ + - ]: 3743942 : decltype(d) p{{ tk::cref_find( m_lref, d[0] ),
1855 [ + - ]: 3743942 : tk::cref_find( m_lref, d[1] ) }};
1856 : :
1857 : 3743942 : std::unordered_set< std::size_t > s;
1858 : :
1859 [ + - ][ + + ]: 3743942 : if (p != AMR::node_pair_t{{n,n}}) {
1860 [ + - ]: 2145350 : auto q = ancestors( p[0] );
1861 [ + - ]: 1072675 : s.insert( begin(q), end(q) );
1862 [ + - ]: 2145350 : auto r = ancestors( p[1] );
1863 [ + - ]: 1072675 : s.insert( begin(r), end(r) );
1864 : : } else {
1865 [ + - ]: 2671267 : s.insert( begin(p), end(p) );
1866 : : }
1867 : :
1868 : 7487884 : return s;
1869 : : }
1870 : :
1871 : : Refiner::BndFaceData
1872 : 177 : Refiner::boundary()
1873 : : // *****************************************************************************
1874 : : // Generate boundary data structures used to update refined/derefined boundary
1875 : : // faces and nodes of side sets
1876 : : //! \return A tuple of boundary face data
1877 : : //! \details The output of this function is used to regenerate physical boundary
1878 : : //! face and node data structures after refinement, see updateBndData().
1879 : : // *****************************************************************************
1880 : : {
1881 : : // Generate the inverse of AMR's tet store.
1882 : 354 : std::unordered_map< Tet, std::size_t, Hash<4>, Eq<4> > invtets;
1883 [ + + ]: 439679 : for (const auto& [key, tet] : m_refiner.tet_store.tets)
1884 [ + - ]: 439502 : invtets[ tet ] = key;
1885 : :
1886 : : //std::cout << thisIndex << " invt: " << invtets.size() << '\n';
1887 : : //std::cout << thisIndex << " active inpoel size: " << m_refiner.tet_store.get_active_inpoel().size() << '\n';
1888 : : //std::cout << thisIndex << " marked derefinement size: " << m_refiner.tet_store.marked_derefinements.size() << '\n';
1889 : :
1890 : : // Generate data structure pcFaceTets for the new (post-AMR) mesh:
1891 : : // pcFaceTets is a map that associates all triangle boundary faces (physical
1892 : : // and chare) to the id of the tet adjacent to the said face.
1893 : : // Key: Face-nodes' global id; Value: tet-id.
1894 : 177 : std::unordered_map< Face, std::size_t, Hash<3>, Eq<3> > pcFaceTets;
1895 [ + - ][ + - ]: 354 : auto esuel = tk::genEsuelTet( m_inpoel, tk::genEsup(m_inpoel,4) );
1896 [ + + ]: 392662 : for (std::size_t e=0; e<esuel.size()/4; ++e) {
1897 : 392485 : auto m = e*4;
1898 [ + + ]: 1962425 : for (std::size_t f=0; f<4; ++f) {
1899 [ + + ]: 1569940 : if (esuel[m+f] == -1) { // if a face does not have an adjacent tet
1900 : 139186 : Face b{{ m_ginpoel[ m+tk::lpofa[f][0] ],
1901 : 139186 : m_ginpoel[ m+tk::lpofa[f][1] ],
1902 : 278372 : m_ginpoel[ m+tk::lpofa[f][2] ] }};
1903 [ + - ][ + - ]: 139186 : Assert( m_inpoel[m+0] < m_rid.size() &&
[ + - ][ + - ]
[ - - ][ - - ]
[ - - ]
1904 : : m_inpoel[m+1] < m_rid.size() &&
1905 : : m_inpoel[m+2] < m_rid.size() &&
1906 : : m_inpoel[m+3] < m_rid.size(), "Indexing out of rid" );
1907 : 278372 : Tet t{{ m_rid[ m_inpoel[m+0] ], m_rid[ m_inpoel[m+1] ],
1908 : 278372 : m_rid[ m_inpoel[m+2] ], m_rid[ m_inpoel[m+3] ] }};
1909 : : //Tet t{{ m_inpoel[m+0], m_inpoel[m+1],
1910 : : // m_inpoel[m+2], m_inpoel[m+3] }};
1911 : : // associate tet id to adjacent (physical or chare) boundary face
1912 [ + - ]: 139186 : auto i = invtets.find( t );
1913 [ + - ][ - + ]: 139186 : Assert(m_refiner.tet_store.is_active(i->second),
[ - - ][ - - ]
[ - - ]
1914 : : "Inactive element while regenerating boundary data");
1915 [ + - ]: 139186 : if (i != end(invtets)) {
1916 : : //std::cout << "refacetets: " <<
1917 : : // b[0] << "-" << b[1] << "-" << b[2] << std::endl;
1918 [ + - ]: 139186 : pcFaceTets[ b ] = i->second;
1919 : : } else {
1920 [ - - ][ - - ]: 0 : Throw("Active element not found in tet_store");
[ - - ]
1921 : : }
1922 : : }
1923 : : }
1924 : : }
1925 : :
1926 : : // Generate child->parent tet and id maps after refinement/derefinement step
1927 : : // tk::destroy( m_oldparent );
1928 : 177 : m_addedTets.clear();
1929 : 177 : std::size_t p = 0;
1930 : 177 : std::size_t c = 0;
1931 : 177 : const auto& tet_store = m_refiner.tet_store;
1932 [ + + ]: 439679 : for (const auto& t : tet_store.tets) {
1933 : : // query number of children of tet
1934 [ + - ]: 439502 : auto nc = tet_store.data( t.first ).children.size();
1935 [ + + ]: 813096 : for (decltype(nc) i=0; i<nc; ++i ) { // for all child tets
1936 : : // get child tet id
1937 [ + - ]: 373594 : auto childtet = tet_store.get_child_id( t.first, i );
1938 [ + - ]: 373594 : auto ct = tet_store.tets.find( childtet );
1939 [ - + ][ - - ]: 373594 : Assert(ct != tet_store.tets.end(), "Child not found in tet store");
[ - - ][ - - ]
1940 : : // //auto cA = tk::cref_find( m_lref, ct->second[0] );
1941 : : // //auto cB = tk::cref_find( m_lref, ct->second[1] );
1942 : : // //auto cC = tk::cref_find( m_lref, ct->second[2] );
1943 : : // //auto cD = tk::cref_find( m_lref, ct->second[3] );
1944 : : // // get nodes of parent tet
1945 : : // //auto pA = tk::cref_find( m_lref, t.second[0] );
1946 : : // //auto pB = tk::cref_find( m_lref, t.second[1] );
1947 : : // //auto pC = tk::cref_find( m_lref, t.second[2] );
1948 : : // //auto pD = tk::cref_find( m_lref, t.second[3] );
1949 : : // // assign parent tet to child tet
1950 : : // //m_oldparent[ {{cA,cB,cC,cD}} ] = {{pA,pB,pC,pD}};
1951 : : // m_oldparent[ ct->second ] = t.second; //{{pA,pB,pC,pD}};
1952 [ + - ][ + + ]: 373594 : if (m_oldTets.find(ct->second) == end(m_oldTets)) {
1953 : : // TODO: the following code can assign negative ids to newly added tets.
1954 : : // This needs to be corrected before applying to cell-based schemes.
1955 : : //Assert((p-m_oldntets) > 0, "Negative id assigned to added tet");
1956 [ + - ]: 350030 : m_addedTets[ c++ ] = p - m_oldntets;
1957 : : }
1958 : : }
1959 : 439502 : ++p;
1960 : : }
1961 : :
1962 : : //std::cout << thisIndex << " added: " << m_addedTets.size() << '\n';
1963 : : //std::cout << thisIndex << " parent: " << m_oldparent.size() << '\n';
1964 : : //std::cout << thisIndex << " pcret: " << pcFaceTets.size() << '\n';
1965 : :
1966 : : //for (std::size_t f=0; f<m_triinpoel.size()/3; ++f) {
1967 : : // std::cout << "triinpoel: " <<
1968 : : // m_triinpoel[f*3+0] << "-" << m_triinpoel[f*3+1] << "-" <<
1969 : : // m_triinpoel[f*3+2] << std::endl;
1970 : : //}
1971 : :
1972 : 354 : return pcFaceTets;
1973 : : }
1974 : :
1975 : : void
1976 : 177 : Refiner::newBndMesh( const std::unordered_set< std::size_t >& ref )
1977 : : // *****************************************************************************
1978 : : // Update boundary data structures after mesh refinement
1979 : : //! \param[in] ref Unique nodes of the refined mesh using refiner-lib ids
1980 : : // *****************************************************************************
1981 : : {
1982 : : // Generate boundary face data structures used to regenerate boundary face
1983 : : // and node data after mesh refinement
1984 [ + - ]: 354 : auto pcFaceTets = boundary();
1985 : :
1986 : : // Regerate boundary faces and nodes after AMR step
1987 [ + - ]: 177 : updateBndData( ref, pcFaceTets );
1988 : 177 : }
1989 : :
1990 : : void
1991 : 177 : Refiner::updateBndData(
1992 : : [[maybe_unused]] const std::unordered_set< std::size_t >& ref,
1993 : : const BndFaceData& pcFaceTets )
1994 : : // *****************************************************************************
1995 : : // Regenerate boundary faces and nodes after AMR step
1996 : : //! \param[in] ref Unique nodes of the refined mesh using refiner-lib ids
1997 : : //! \param[in] pcFaceTets Boundary face data
1998 : : // *****************************************************************************
1999 : : {
2000 : : // storage for boundary faces associated to side-set IDs of the refined mesh
2001 : 177 : tk::destroy( m_bface );
2002 : : // storage for boundary faces-node connectivity of the refined mesh
2003 : 177 : tk::destroy( m_triinpoel );
2004 : : // storage for boundary nodes associated to side-set IDs of the refined mesh
2005 : 177 : tk::destroy( m_bnode );
2006 : :
2007 : : // face id counter
2008 : 177 : std::size_t facecnt = 0;
2009 : : // will collect unique faces added for each side set
2010 : 354 : std::unordered_map< int, FaceSet > bf;
2011 : :
2012 : : // Lambda to associate a boundary face and connectivity to a side set.
2013 : : // Argument 's' is the list of faces (ids) to add the new face to. Argument
2014 : : // 'ss' is the side set id to which the face is added. Argument 'f' is the
2015 : : // triangle face connectivity to add.
2016 : 103510 : auto addBndFace = [&]( std::vector< std::size_t >& s, int ss, const Face& f )
2017 : : {
2018 : : // only add face if it has not yet been aded to this side set
2019 [ + - ]: 103510 : if (bf[ ss ].insert( f ).second) {
2020 [ + - ]: 103510 : s.push_back( facecnt++ );
2021 [ + - ]: 103510 : m_triinpoel.insert( end(m_triinpoel), begin(f), end(f) );
2022 [ - + ][ - - ]: 103510 : Assert(m_triinpoel.size()/3 == facecnt, "Incorrect size of triinpoel");
[ - - ][ - - ]
2023 : : }
2024 : 103510 : };
2025 : :
2026 : : // Lambda to search the parents in the coarsest mesh of a mesh node and if
2027 : : // found, add its global id to boundary node lists associated to the side
2028 : : // set(s) of its parents. Argument 'n' is the local id of the mesh node id
2029 : : // whose parents to search.
2030 : 1181034 : auto addBndNodes = [&]( std::size_t n ){
2031 [ + - ]: 2362068 : auto a = ancestors( n ); // find parents of n in coarse mesh
2032 [ + + ]: 1181034 : if (a.size() == 1) {
2033 : : // node was part of the coarse mesh
2034 [ - + ][ - - ]: 434862 : Assert(*a.cbegin() == n, "Single ancestor not self");
[ - - ][ - - ]
2035 [ + - ]: 869724 : auto ss = keys( m_coarseBndNodes, m_gid[*a.cbegin()] );
2036 [ + + ]: 442398 : for (auto s : ss)
2037 [ + - ][ + - ]: 7536 : m_bnode[ s ].push_back( m_gid[n] );
2038 [ + - ]: 746172 : } else if (a.size() == 2) {
2039 : : // node was added to an edge of a coarse face
2040 [ + - ]: 1492344 : std::vector< std::size_t > p( begin(a), end(a) );
2041 [ + - ]: 1492344 : auto ss1 = keys( m_coarseBndNodes, m_gid[p[0]] );
2042 [ + - ]: 1492344 : auto ss2 = keys( m_coarseBndNodes, m_gid[p[1]] );
2043 [ + + ]: 765996 : for (auto s : ss1) {
2044 : : // only add 'n' to bnode if all parent nodes are in same side set, else
2045 : : // 'n' is not a boundary node
2046 [ + - ][ + + ]: 19824 : if (ss2.find(s) != end(ss2)) {
2047 [ + - ][ + - ]: 17280 : m_bnode[ s ].push_back( m_gid[n] );
2048 : : }
2049 : : }
2050 [ - - ]: 0 : } else if (a.size() == 3) {
2051 : : // node was added inside of a coarse face
2052 [ - - ]: 0 : std::vector< std::size_t > p( begin(a), end(a) );
2053 [ - - ]: 0 : auto ss1 = keys( m_coarseBndNodes, m_gid[p[0]] );
2054 [ - - ]: 0 : auto ss2 = keys( m_coarseBndNodes, m_gid[p[1]] );
2055 [ - - ]: 0 : auto ss3 = keys( m_coarseBndNodes, m_gid[p[2]] );
2056 [ - - ]: 0 : for (auto s : ss1) {
2057 : : // only add 'n' to bnode if all parent nodes are in same side set, else
2058 : : // 'n' is not a boundary node
2059 [ - - ][ - - ]: 0 : if (ss2.find(s) != end(ss2) && ss3.find(s) != end(ss3)) {
[ - - ][ - - ]
[ - - ]
2060 [ - - ][ - - ]: 0 : m_bnode[ s ].push_back( m_gid[n] );
2061 : : }
2062 : : }
2063 : : }
2064 : 1181034 : };
2065 : :
2066 : : // Regenerate boundary faces for new mesh along side sets. For all faces
2067 : : // associated to side sets, we find the ancestors (parents of nodes in the
2068 : : // original/coarsest mesh) of the nodes comprising the physical and chare
2069 : : // boundary faces of the new mesh.
2070 : : //bool faceNoSs = false;
2071 : : // for all P/C faces in current (post-AMR) mesh
2072 [ + + ]: 139363 : for (const auto& [ face, tetid ] : pcFaceTets) {
2073 : : // find ancestors of face
2074 : 278372 : std::unordered_set< std::size_t > ans;
2075 [ + + ]: 556744 : for (std::size_t i=0; i<3; ++i) {
2076 [ + - ][ + - ]: 835116 : auto ai = ancestors(tk::cref_find(m_lid, face[i]));
2077 [ + - ]: 417558 : ans.insert(ai.begin(), ai.end());
2078 : : }
2079 [ - + ][ - - ]: 139186 : Assert(ans.size() == 3, "Incorrect number of ancestors in refined face");
[ - - ][ - - ]
2080 : : Face af;
2081 : 139186 : std::size_t i = 0;
2082 [ + + ]: 556744 : for (auto ai:ans) {
2083 : 417558 : af[i] = m_gid[ai];
2084 : 417558 : ++i;
2085 : : }
2086 : : // for all boundary faces in original mesh
2087 : : //std::size_t fss = 0;
2088 [ + + ]: 532864 : for (const auto& [ss, cfaceset] : m_coarseBndFaces) {
2089 [ + - ][ + + ]: 393678 : if (cfaceset.find(af) != cfaceset.end()) {
2090 [ + - ][ + - ]: 103510 : addBndFace(m_bface[ss], ss, face);
2091 : : //++fss;
2092 : : }
2093 [ + + ]: 1574712 : for (auto j : face) {
2094 [ + - ][ + - ]: 1181034 : addBndNodes(tk::cref_find(m_lid, j));
2095 : : }
2096 : : }
2097 : : //if (fss==0) {
2098 : : // std::cout << "Face added to no/multiple side sets; " << fss << std::endl;
2099 : : // faceNoSs = true;
2100 : : //}
2101 : : }
2102 : :
2103 : : // Commented code below, since diagcg can work without sideset/bcs
2104 : : //Assert(!faceNoSs, "Face/s added to incorrect number of side sets");
2105 : :
2106 : : // Make boundary node IDs unique for each physical boundary (side set)
2107 [ + + ][ + - ]: 193 : for (auto& s : m_bnode) tk::unique( s.second );
2108 : :
2109 : : //for (const auto& [ setid, faceids ] : m_bface) {
2110 : : // std::cout << "sset: " << setid << std::endl;
2111 : : // for (auto f : faceids) {
2112 : : // Assert(f<m_triinpoel.size()/3, "Out of bounds access into triinpoel");
2113 : : // std::cout << "new bndfaces: " <<
2114 : : // m_triinpoel[f*3+0] << "-" << m_triinpoel[f*3+1] << "-" <<
2115 : : // m_triinpoel[f*3+2] << std::endl;
2116 : : // }
2117 : : //}
2118 : :
2119 : : //for (std::size_t f=0; f<m_triinpoel.size()/3; ++f) {
2120 : : // std::cout << "new triinpoel: " <<
2121 : : // m_triinpoel[f*3+0] << "-" << m_triinpoel[f*3+1] << "-" <<
2122 : : // m_triinpoel[f*3+2] << std::endl;
2123 : : //}
2124 : :
2125 : : //std::cout << thisIndex << " bf: " << tk::sumvalsize( m_bface ) << '\n';
2126 : :
2127 : : //std::cout << thisIndex << " bn: " << tk::sumvalsize( m_bnode ) << '\n';
2128 : :
2129 : : // Perform leak-test on boundary face data just updated (only in DEBUG)
2130 [ + - ][ - + ]: 177 : Assert( bndIntegral(), "Partial boundary integral" );
[ - - ][ - - ]
[ - - ]
2131 : 177 : }
2132 : :
2133 : : bool
2134 : 177 : Refiner::bndIntegral()
2135 : : // *****************************************************************************
2136 : : // Compute partial boundary surface integral and sum across all chares
2137 : : //! \return true so we don't trigger assert in client code
2138 : : //! \details This function computes a partial surface integral over the boundary
2139 : : //! of the faces of this mesh partition then sends its contribution to perform
2140 : : //! the integral acorss the total problem boundary. After the global sum a
2141 : : //! non-zero vector result indicates a leak, e.g., a hole in the boundary
2142 : : //! which indicates an error in the boundary face data structures used to
2143 : : //! compute the partial surface integrals.
2144 : : // *****************************************************************************
2145 : : {
2146 : 177 : const auto& x = m_coord[0];
2147 : 177 : const auto& y = m_coord[1];
2148 : 177 : const auto& z = m_coord[2];
2149 : :
2150 [ + - ]: 177 : std::vector< tk::real > s{{ 0.0, 0.0, 0.0 }};
2151 : :
2152 [ + + ]: 607 : for (const auto& [ setid, faceids ] : m_bface) {
2153 [ + + ]: 103940 : for (auto f : faceids) {
2154 [ + - ]: 103510 : auto A = tk::cref_find( m_lid, m_triinpoel[f*3+0] );
2155 [ + - ]: 103510 : auto B = tk::cref_find( m_lid, m_triinpoel[f*3+1] );
2156 [ + - ]: 103510 : auto C = tk::cref_find( m_lid, m_triinpoel[f*3+2] );
2157 : : // Compute geometry data for face
2158 : 310530 : auto geoface = tk::geoFaceTri( {{x[A], x[B], x[C]}},
2159 : 310530 : {{y[A], y[B], y[C]}},
2160 [ + - ]: 103510 : {{z[A], z[B], z[C]}} );
2161 : : // Sum up face area * face unit-normal
2162 [ + - ][ + - ]: 103510 : s[0] += geoface(0,0) * geoface(0,1);
2163 [ + - ][ + - ]: 103510 : s[1] += geoface(0,0) * geoface(0,2);
2164 [ + - ][ + - ]: 103510 : s[2] += geoface(0,0) * geoface(0,3);
2165 : : }
2166 : : }
2167 : :
2168 [ + - ]: 177 : s.push_back( -1.0 ); // negative: no call-back after reduction
2169 [ + - ]: 177 : s.push_back( static_cast< tk::real >( m_meshid ) );
2170 : :
2171 : : // Send contribution to host summing partial surface integrals
2172 [ + - ]: 177 : contribute( s, CkReduction::sum_double, m_cbr.get< tag::bndint >() );
2173 : :
2174 : 354 : return true; // don't trigger the assert in client code
2175 : : }
2176 : :
2177 : : #include "NoWarning/refiner.def.h"
|