Branch data Line data Source code
1 : : // *****************************************************************************
2 : : /*!
3 : : \file src/Inciter/Partitioner.cpp
4 : : \copyright 2012-2015 J. Bakosi,
5 : : 2016-2018 Los Alamos National Security, LLC.,
6 : : 2019-2021 Triad National Security, LLC.,
7 : : 2022-2024 J. Bakosi
8 : : All rights reserved. See the LICENSE file for details.
9 : : \brief Charm++ chare partitioner nodegroup used to perform mesh
10 : : partitioning
11 : : \details Charm++ chare partitioner nodegroup used to perform mesh read and
12 : : partitioning, one worker per compute node.
13 : : */
14 : : // *****************************************************************************
15 : :
16 : : #include <numeric>
17 : :
18 : : #include "PUPUtil.hpp"
19 : : #include "Partitioner.hpp"
20 : : #include "DerivedData.hpp"
21 : : #include "Reorder.hpp"
22 : : #include "ExodusIIMeshReader.hpp"
23 : : #include "UnsMesh.hpp"
24 : : #include "ContainerUtil.hpp"
25 : : #include "Callback.hpp"
26 : : #include "ZoltanGeom.hpp"
27 : : #include "ZoltanGraph.hpp"
28 : : #include "InciterConfig.hpp"
29 : : #include "GraphReducer.hpp"
30 : : #include "PartsReducer.hpp"
31 : : #include "Around.hpp"
32 : :
33 : : namespace inciter {
34 : :
35 : : static CkReduction::reducerType GraphMerger;
36 : : static CkReduction::reducerType PartsMerger;
37 : : extern ctr::Config g_cfg;
38 : :
39 : : } // inciter::
40 : :
41 : : using inciter::Partitioner;
42 : :
43 : 739 : Partitioner::Partitioner(
44 : : std::size_t meshid,
45 : : const std::string& filename,
46 : : const tk::PartitionerCallback& cbp,
47 : : const tk::RefinerCallback& cbr,
48 : : const tk::SorterCallback& cbs,
49 : : const CProxy_Transporter& host,
50 : : const CProxy_Refiner& refiner,
51 : : const CProxy_Sorter& sorter,
52 : : const tk::CProxy_MeshWriter& meshwriter,
53 : : const CProxy_Discretization& discretization,
54 : : const CProxy_RieCG& riecg,
55 : : const CProxy_LaxCG& laxcg,
56 : : const CProxy_ZalCG& zalcg,
57 : : const CProxy_KozCG& kozcg,
58 : : const CProxy_ChoCG& chocg,
59 : : const CProxy_LohCG& lohcg,
60 : : const tk::CProxy_ConjugateGradients& cgpre,
61 : : const tk::CProxy_ConjugateGradients& cgmom,
62 : : const std::map< int, std::vector< std::size_t > >& bface,
63 : : const std::map< int, std::vector< std::size_t > >& faces,
64 : 739 : const std::map< int, std::vector< std::size_t > >& bnode ) :
65 : 739 : m_meshid( meshid ),
66 : 739 : m_cbp( cbp ),
67 : 739 : m_cbr( cbr ),
68 : 739 : m_cbs( cbs ),
69 : 739 : m_host( host ),
70 [ + - ]: 739 : m_refiner( refiner ),
71 [ + - ]: 739 : m_sorter( sorter ),
72 [ + - ]: 739 : m_meshwriter( meshwriter ),
73 [ + - ]: 739 : m_discretization( discretization ),
74 [ + - ]: 739 : m_riecg( riecg ),
75 [ + - ]: 739 : m_laxcg( laxcg ),
76 [ + - ]: 739 : m_zalcg( zalcg ),
77 [ + - ]: 739 : m_kozcg( kozcg ),
78 [ + - ]: 739 : m_chocg( chocg ),
79 [ + - ]: 739 : m_lohcg( lohcg ),
80 [ + - ]: 739 : m_cgpre( cgpre ),
81 [ + - ]: 739 : m_cgmom( cgmom ),
82 : 739 : m_ndist( 0 ),
83 : 739 : m_nchare( 0 ),
84 [ + - ]: 739 : m_bface( bface ),
85 [ + - ][ + - ]: 2217 : m_bnode( bnode )
86 : : // *****************************************************************************
87 : : // Constructor
88 : : //! \param[in] meshid Mesh ID
89 : : //! \param[in] filename Input mesh filename to read from
90 : : //! \param[in] cbp Charm++ callbacks for Partitioner
91 : : //! \param[in] cbr Charm++ callbacks for Refiner
92 : : //! \param[in] cbs Charm++ callbacks for Sorter
93 : : //! \param[in] host Host Charm++ proxy we are being called from
94 : : //! \param[in] refiner Mesh refiner proxy
95 : : //! \param[in] sorter Mesh reordering (sorter) proxy
96 : : //! \param[in] meshwriter Mesh writer proxy
97 : : //! \param[in] discretization Discretization base
98 : : //! \param[in] riecg Discretization scheme
99 : : //! \param[in] laxcg Discretization scheme
100 : : //! \param[in] zalcg Discretization scheme
101 : : //! \param[in] kozcg Discretization scheme
102 : : //! \param[in] chocg Discretization scheme
103 : : //! \param[in] lohcg Discretization scheme
104 : : //! \param[in] cgpre ConjugateGradients Charm++ proxy for pressure solve
105 : : //! \param[in] cgmom ConjugateGradients Charm++ proxy for momentum solve
106 : : //! \param[in] bface File-internal elem ids of side sets (whole mesh)
107 : : //! \param[in] faces Elem-relative face ids of side sets (whole mesh)
108 : : //! \param[in] bnode Node lists of side sets (whole mesh)
109 : : // *****************************************************************************
110 : : {
111 : : // Create mesh reader
112 [ + - ]: 739 : tk::ExodusIIMeshReader mr( filename );
113 : :
114 : : // Read this compute node's chunk of the mesh (graph and coords) from file
115 : 739 : std::vector< std::size_t > triinpoel;
116 [ + - ]: 739 : mr.readMeshPart( m_ginpoel, m_inpoel, triinpoel, m_lid, m_coord,
117 : : CkNumNodes(), CkMyNode() );
118 : :
119 : : // Compute triangle connectivity for side sets, reduce boundary face for side
120 : : // sets to this compute node only and to compute-node-local face ids
121 [ + - ]: 739 : m_triinpoel = mr.triinpoel( m_bface, faces, m_ginpoel, triinpoel );
122 : :
123 : : // Keep those nodes for side sets that reside on this compute node only
124 : 739 : std::map< int, std::vector< std::size_t > > own_bnode;
125 [ + + ]: 2618 : for (const auto& [ setid, nodes ] : m_bnode) {
126 [ + - ]: 1879 : auto& b = own_bnode[ setid ];
127 [ + + ]: 246242 : for (auto n : nodes) {
128 [ + - ]: 244363 : auto i = m_lid.find( n );
129 [ + + ][ + - ]: 244363 : if (i != end(m_lid)) b.push_back( n );
130 : : }
131 [ + + ][ + - ]: 1879 : if (b.empty()) own_bnode.erase( setid );
132 : : }
133 : 739 : m_bnode = std::move(own_bnode);
134 : :
135 : : // Compute unqiue mesh graph if needed
136 [ + + ]: 739 : if ( g_cfg.get< tag::part >() == "phg" ) {
137 : : // Generate global node ids
138 [ + - ]: 16 : const auto& [ inpoel, gid, lid ] = tk::global2local( m_ginpoel );
139 : : // Generate points surrounding points of this sub-graph with local node ids
140 [ + - ][ + - ]: 16 : const auto psup = tk::genPsup( m_inpoel, 4, tk::genEsup(m_inpoel,4) );
141 : : // Put sub-graph into a map for aggregation
142 [ + + ]: 87410 : for (std::size_t p=0; p<gid.size(); ++p) {
143 [ + - ]: 87394 : auto& m = m_graph[ gid[p] ];
144 [ + - ]: 87394 : m.push_back( static_cast< std::size_t >( CkMyNode() ) );
145 [ + - ][ + + ]: 907664 : for (auto i : tk::Around(psup,p)) m.push_back( gid[i] );
146 : : }
147 : 16 : }
148 : :
149 : : // Aggregate graph across to all Partitioners
150 [ + - ]: 739 : auto stream = tk::serialize( m_graph );
151 [ + - ]: 739 : contribute( stream.first, stream.second.get(), GraphMerger,
152 [ + - ][ + - ]: 1478 : CkCallback( CkIndex_Partitioner::graph(nullptr), thisProxy ) );
153 : 739 : }
154 : :
155 : : void
156 : 768 : Partitioner::registerReducers()
157 : : // *****************************************************************************
158 : : // Configure Charm++ reduction types
159 : : //! \details Since this is a [initnode] routine, see the .ci file, the
160 : : //! Charm++ runtime system executes the routine exactly once on every
161 : : //! logical node early on in the Charm++ init sequence. Must be static as
162 : : //! it is called without an object. See also: Section "Initializations at
163 : : //! Program Startup" at in the Charm++ manual
164 : : //! http://charm.cs.illinois.edu/manuals/html/charm++/manual.html.
165 : : // *****************************************************************************
166 : : {
167 : 768 : GraphMerger = CkReduction::addReducer( tk::mergeGraph );
168 : 768 : PartsMerger = CkReduction::addReducer( tk::mergeParts );
169 : 768 : }
170 : :
171 : : void
172 : 739 : Partitioner::graph( CkReductionMsg* msg )
173 : : // *****************************************************************************
174 : : // Reduction target yielding the aggregated mesh graph on each Partitioner
175 : : //! \param[in] msg Serialized aggregated mesh graph
176 : : // *****************************************************************************
177 : : {
178 [ + - ]: 739 : if (msg) {
179 : : // Deserialize aggregated mesh graph
180 : 739 : PUP::fromMem creator( msg->getData() );
181 : 739 : std::unordered_map< std::size_t, std::vector< std::size_t > > graph;
182 [ + - ]: 739 : creator | graph;
183 [ + - ][ + - ]: 739 : delete msg;
184 : :
185 : : // Keep owned node graph only
186 [ + + ]: 88133 : for (auto& [g,n] : m_graph) {
187 [ + - ]: 87394 : auto own = graph.find( g );
188 [ - + ]: 87394 : if (own == end(graph)) continue;
189 : 87394 : n.clear();
190 [ + + ]: 87394 : if (own->second[0] == static_cast< std::size_t >( CkMyNode() )) {
191 [ + - ]: 36185 : n.insert( end(n), own->second.begin()+1, own->second.end() );
192 : : }
193 [ + - ]: 87394 : tk::unique( n );
194 : : }
195 : :
196 : : // Remove connectivity of those nodes not owned
197 : 739 : graph.clear();
198 [ + + ][ + - ]: 88133 : for (auto&& [g,n] : m_graph) if (!n.empty()) graph[g] = std::move(n);
[ + + ]
199 : 739 : m_graph = std::move( graph );
200 : 739 : }
201 : :
202 : : // Sum number of cells across distributed mesh
203 [ + - ]: 739 : std::vector< std::size_t > meshdata{ m_meshid, m_ginpoel.size()/4 };
204 [ + - ]: 739 : contribute( meshdata, CkReduction::sum_ulong, m_cbp.get< tag::load >() );
205 : 739 : }
206 : :
207 : : void
208 : 739 : Partitioner::partition( int nchare )
209 : : // *****************************************************************************
210 : : // Partition the computational mesh into a number of chares
211 : : //! \param[in] nchare Number of parts the mesh will be partitioned into
212 : : //! \details This function calls the mesh partitioner to partition the mesh. The
213 : : //! number of partitions equals the number nchare argument which must be no
214 : : //! lower than the number of compute nodes.
215 : : // *****************************************************************************
216 : : {
217 [ - + ][ - - ]: 739 : Assert( nchare >= CkNumNodes(), "Number of chares must not be lower than the "
[ - - ][ - - ]
218 : : "number of compute nodes" );
219 : :
220 : 739 : m_nchare = nchare;
221 : 739 : const auto& alg = g_cfg.get< tag::part >();
222 : 739 : const auto& params = g_cfg.get< tag::zoltan_params >();
223 : :
224 [ + + ]: 739 : if ( alg == "phg" ) {
225 : :
226 : : // Partition mesh with graph partitioner
227 [ + - ]: 16 : auto chp = graphPartMesh( m_ginpoel, m_graph, params, nchare );
228 : :
229 : : // Aggregate partition assginments
230 [ + - ]: 16 : auto stream = tk::serialize( chp );
231 [ + - ]: 16 : contribute( stream.first, stream.second.get(), PartsMerger,
232 [ + - ][ + - ]: 32 : CkCallback( CkIndex_Partitioner::parts(nullptr), thisProxy ) );
233 : :
234 : 16 : } else {
235 : :
236 : : // Partition mesh with coordinate-based partitioner
237 [ + - ]: 723 : auto che = geomPartMesh( alg.c_str(), params, m_inpoel, m_coord, nchare );
238 : :
239 : : // Distribute partition assignments
240 [ + - ]: 723 : partitioned( std::move(che) );
241 : :
242 : 723 : }
243 : 739 : }
244 : :
245 : : void
246 : 16 : Partitioner::parts( CkReductionMsg* msg )
247 : : // *****************************************************************************
248 : : // Reduction target to aggregate mesh partition assignments
249 : : //! \param[in] msg Serialized aggregated mesh nodes partition assignments
250 : : // *****************************************************************************
251 : : {
252 : : // Deserialize mesh partition assignments
253 : 16 : PUP::fromMem creator( msg->getData() );
254 : 16 : std::unordered_map< std::size_t, std::size_t > parts;
255 [ + - ]: 16 : creator | parts;
256 [ + - ][ + - ]: 16 : delete msg;
257 : :
258 : : // Assign mesh elements based on node assignments
259 : : using std::min;
260 [ + - ]: 16 : std::vector< std::size_t > che( m_ginpoel.size()/4 );
261 [ + + ]: 186722 : for (std::size_t e=0; e<m_ginpoel.size()/4; ++e) {
262 : 186706 : const auto g = m_ginpoel.data() + e*4;
263 [ + - ]: 186706 : std::size_t chp[4] = { tk::cref_find( parts, g[0] ),
264 : 373412 : tk::cref_find( parts, g[1] ),
265 : 373412 : tk::cref_find( parts, g[2] ),
266 [ + - ][ + - ]: 186706 : tk::cref_find( parts, g[3] ) };
[ + - ]
267 : 186706 : che[e] = min( chp[0], min( chp[1], min( chp[2], chp[3] ) ) );
268 : : }
269 : :
270 [ + - ]: 16 : partitioned( std::move(che) );
271 : 16 : }
272 : :
273 : : void
274 : 739 : Partitioner::partitioned( std::vector< std::size_t >&& che )
275 : : // *****************************************************************************
276 : : // Continue after partitioning finished
277 : : // *****************************************************************************
278 : : {
279 [ + + ]: 739 : if ( g_cfg.get< tag::feedback >() ) m_host.pepartitioned();
280 : :
281 : 739 : contribute( sizeof(std::size_t), &m_meshid, CkReduction::nop,
282 : 739 : m_cbp.get< tag::partitioned >() );
283 : :
284 : : // Categorize mesh elements (given by their gobal node IDs) by target chare
285 : : // and distribute to their compute nodes based on mesh partitioning.
286 [ + - ][ + - ]: 739 : distribute( categorize( che ) );
287 : 739 : }
288 : :
289 : : void
290 : 2832 : Partitioner::addMesh(
291 : : int fromnode,
292 : : const std::unordered_map< int, // chare id
293 : : std::tuple<
294 : : std::vector< std::size_t >, // tet connectivity
295 : : tk::UnsMesh::CoordMap, // node coords
296 : : std::unordered_map< int, std::vector< std::size_t > >, // bface conn
297 : : std::unordered_map< int, std::vector< std::size_t > > // bnodes
298 : : > >& chmesh )
299 : : // *****************************************************************************
300 : : // Receive mesh associated to chares we own after refinement
301 : : //! \param[in] fromnode Compute node call coming from
302 : : //! \param[in] chmesh Map associating mesh connectivities to global node ids
303 : : //! and node coordinates for mesh chunks we are assigned by the partitioner
304 : : // *****************************************************************************
305 : : {
306 : : // Store mesh connectivity and global node coordinates categorized by chares.
307 : : // The send side also writes to the data written here, so concat.
308 [ + + ]: 10902 : for (const auto& [ chareid, chunk ] : chmesh) {
309 [ + - ][ - + ]: 8070 : Assert( node(chareid) == CkMyNode(), "Compute node "
[ - - ][ - - ]
[ - - ][ - - ]
310 : : + std::to_string(CkMyNode()) +
311 : : " received a mesh whose chare it does not own" );
312 : : // Store domain element (tetrahedron) connectivity
313 : 8070 : const auto& inpoel = std::get< 0 >( chunk );
314 [ + - ]: 8070 : auto& inp = m_chinpoel[ chareid ]; // will store tetrahedron connectivity
315 [ + - ]: 8070 : inp.insert( end(inp), begin(inpoel), end(inpoel) );
316 : : // Store mesh node coordinates associated to global node IDs
317 : 8070 : const auto& coord = std::get< 1 >( chunk );
318 [ + - ][ - + ]: 8070 : Assert( tk::uniquecopy(inpoel).size() == coord.size(), "Size mismatch" );
[ - - ][ - - ]
[ - - ]
319 [ + - ]: 8070 : auto& chcm = m_chcoordmap[ chareid ]; // will store node coordinates
320 [ + - ]: 8070 : chcm.insert( begin(coord), end(coord) ); // concatenate node coords
321 : : // Store boundary side set id + face ids + face connectivities
322 : 8070 : const auto& bconn = std::get< 2 >( chunk );
323 [ + - ]: 8070 : auto& bface = m_chbface[ chareid ]; // for side set id + boundary face ids
324 [ + - ]: 8070 : auto& t = m_chtriinpoel[ chareid ]; // for boundary face connectivity
325 [ + - ]: 8070 : auto& f = m_nface[ chareid ]; // use counter for chare
326 [ + + ]: 19681 : for (const auto& [ setid, faceids ] : bconn) {
327 [ + - ]: 11611 : auto& b = bface[ setid ];
328 [ + + ]: 93821 : for (std::size_t i=0; i<faceids.size()/3; ++i) {
329 [ + - ]: 82210 : b.push_back( f++ );
330 [ + - ]: 82210 : t.push_back( faceids[i*3+0] );
331 [ + - ]: 82210 : t.push_back( faceids[i*3+1] );
332 [ + - ]: 82210 : t.push_back( faceids[i*3+2] );
333 : : }
334 : : }
335 : : // Store boundary side set id + node lists
336 : 8070 : const auto& bnode = std::get< 3 >( chunk );
337 [ + - ]: 8070 : auto& nodes = m_chbnode[ chareid ]; // for side set id + boundary nodes
338 [ + + ]: 14292 : for (const auto& [ setid, bnodes ] : bnode) {
339 [ + - ]: 6222 : auto& b = nodes[ setid ];
340 [ + - ]: 6222 : b.insert( end(b), begin(bnodes), end(bnodes) );
341 : : }
342 : : }
343 : :
344 [ + - ][ + - ]: 2832 : thisProxy[ fromnode ].recvMesh();
345 : 2832 : }
346 : :
347 : : int
348 : 16140 : Partitioner::node( int id ) const
349 : : // *****************************************************************************
350 : : // Return nodegroup id for chare id
351 : : //! \param[in] id Chare id
352 : : //! \return Nodegroup that creates the chare
353 : : //! \details This is computed based on a simple contiguous linear
354 : : //! distribution of chare ids to compute nodes.
355 : : // *****************************************************************************
356 : : {
357 [ - + ][ - - ]: 16140 : Assert( m_nchare > 0, "Number of chares must be a positive number" );
[ - - ][ - - ]
358 : 16140 : auto p = id / (m_nchare / CkNumNodes());
359 [ + + ]: 16140 : if (p >= CkNumNodes()) p = CkNumNodes()-1;
360 [ - + ][ - - ]: 16140 : Assert( p < CkNumNodes(), "Assigning to nonexistent node" );
[ - - ][ - - ]
361 : 16140 : return p;
362 : : }
363 : :
364 : : void
365 : 2832 : Partitioner::recvMesh()
366 : : // *****************************************************************************
367 : : // Acknowledge received mesh chunk and its nodes after mesh refinement
368 : : // *****************************************************************************
369 : : {
370 [ + + ]: 2832 : if (--m_ndist == 0) {
371 [ + + ]: 644 : if (g_cfg.get< tag::feedback >()) m_host.pedistributed();
372 : 644 : contribute( sizeof(std::size_t), &m_meshid, CkReduction::nop,
373 : 644 : m_cbp.get< tag::distributed >() );
374 : : }
375 : 2832 : }
376 : :
377 : : void
378 : 739 : Partitioner::refine()
379 : : // *****************************************************************************
380 : : // Optionally start refining the mesh
381 : : // *****************************************************************************
382 : : {
383 [ + - ]: 739 : auto dist = distribution( m_nchare );
384 : :
385 : 739 : std::size_t error = 0;
386 [ - + ]: 739 : if (m_chinpoel.size() < static_cast<std::size_t>(dist[1])) {
387 : :
388 : 0 : error = 1;
389 : :
390 : : } else {
391 : :
392 [ + + ]: 3198 : for (int c=0; c<dist[1]; ++c) {
393 : : // compute chare ID
394 : 2459 : auto cid = CkMyNode() * dist[0] + c;
395 : : // create refiner Charm++ chare array element using dynamic insertion
396 [ + - ]: 4918 : m_refiner[ cid ].insert( m_meshid,
397 : 2459 : m_host,
398 : 2459 : m_sorter,
399 : 2459 : m_meshwriter,
400 : 2459 : m_discretization,
401 : 2459 : m_riecg,
402 : 2459 : m_laxcg,
403 : 2459 : m_zalcg,
404 : 2459 : m_kozcg,
405 : 2459 : m_chocg,
406 : 2459 : m_lohcg,
407 : 2459 : m_cgpre,
408 : 2459 : m_cgmom,
409 : 2459 : m_cbr,
410 : 2459 : m_cbs,
411 [ + - ]: 2459 : tk::cref_find(m_chinpoel,cid),
412 [ + - ]: 2459 : tk::cref_find(m_chcoordmap,cid),
413 [ + - ]: 2459 : tk::cref_find(m_chbface,cid),
414 [ + - ]: 2459 : tk::cref_find(m_chtriinpoel,cid),
415 [ + - ][ + - ]: 2459 : tk::cref_find(m_chbnode,cid),
416 : : m_nchare );
417 : : }
418 : :
419 : : }
420 : :
421 : 739 : tk::destroy( m_ginpoel );
422 : 739 : tk::destroy( m_coord );
423 : 739 : tk::destroy( m_inpoel );
424 : 739 : tk::destroy( m_lid );
425 : 739 : tk::destroy( m_nface );
426 : 739 : tk::destroy( m_nodech );
427 : 739 : tk::destroy( m_linnodes );
428 : 739 : tk::destroy( m_chinpoel );
429 : 739 : tk::destroy( m_chcoordmap );
430 : 739 : tk::destroy( m_chbface );
431 : 739 : tk::destroy( m_chtriinpoel );
432 : 739 : tk::destroy( m_chbnode );
433 : 739 : tk::destroy( m_bnodechares );
434 : 739 : tk::destroy( m_bface );
435 : 739 : tk::destroy( m_triinpoel );
436 : 739 : tk::destroy( m_bnode );
437 : :
438 [ + - ]: 739 : std::vector< std::size_t > meshdata{ m_meshid, error };
439 [ + - ]: 739 : contribute( meshdata, CkReduction::max_ulong, m_cbp.get<tag::refinserted>() );
440 : 739 : }
441 : :
442 : : std::unordered_map< int, Partitioner::MeshData >
443 : 739 : Partitioner::categorize( const std::vector< std::size_t >& target ) const
444 : : // *****************************************************************************
445 : : // Categorize mesh data by target
446 : : //! \param[in] target Target chares of mesh elements, size: number of
447 : : //! elements in the chunk of the mesh graph on this compute node.
448 : : //! \return Vector of global mesh node ids connecting elements owned by each
449 : : //! target chare.
450 : : // *****************************************************************************
451 : : {
452 [ - + ][ - - ]: 739 : Assert( target.size() == m_ginpoel.size()/4, "Size mismatch");
[ - - ][ - - ]
453 : :
454 : : using Face = tk::UnsMesh::Face;
455 : :
456 : : // Build hash map associating side set id to boundary faces
457 : : std::unordered_map< Face, int,
458 : 739 : tk::UnsMesh::Hash<3>, tk::UnsMesh::Eq<3> > faceside;
459 [ + + ]: 4757 : for (const auto& [ setid, faceids ] : m_bface)
460 [ + + ]: 229856 : for (auto f : faceids)
461 : 225838 : faceside[ {{ m_triinpoel[f*3+0],
462 : 225838 : m_triinpoel[f*3+1],
463 [ + - ]: 225838 : m_triinpoel[f*3+2] }} ] = setid;
464 : :
465 : : // Build hash map associating side set ids to boundary nodes
466 : 739 : std::unordered_map< std::size_t, std::unordered_set< int > > nodeside;
467 [ + + ]: 2610 : for (const auto& [ setid, nodes ] : m_bnode)
468 [ + + ]: 163170 : for (auto n : nodes)
469 [ + - ][ + - ]: 161299 : nodeside[ n ].insert( setid );
470 : :
471 : : // Categorize mesh data (tets, node coordinates, and boundary data) by target
472 : : // chare based on which chare the partitioner assigned elements (tets) to
473 : 739 : std::unordered_map< int, MeshData > chmesh;
474 [ + + ]: 917819 : for (std::size_t e=0; e<target.size(); ++e) {
475 : : // Construct a tetrahedron with global node ids
476 : 917080 : tk::UnsMesh::Tet t{{ m_ginpoel[e*4+0], m_ginpoel[e*4+1],
477 : 917080 : m_ginpoel[e*4+2], m_ginpoel[e*4+3] }};
478 : : // Categorize tetrahedron (domain element) connectivity
479 [ + - ]: 917080 : auto& mesh = chmesh[ static_cast<int>(target[e]) ];
480 : 917080 : auto& inpoel = std::get< 0 >( mesh );
481 [ + - ]: 917080 : inpoel.insert( end(inpoel), begin(t), end(t) );
482 : : // Categorize boundary face connectivity
483 : 917080 : auto& bconn = std::get< 1 >( mesh );
484 : 917080 : std::array<Face,4> face{{ {{t[0],t[2],t[1]}}, {{t[0],t[1],t[3]}},
485 : 917080 : {{t[0],t[3],t[2]}}, {{t[1],t[2],t[3]}} }};
486 [ + + ]: 4585400 : for (const auto& f : face) {
487 [ + - ]: 3668320 : auto it = faceside.find( f );
488 [ + + ]: 3668320 : if (it != end(faceside)) {
489 [ + - ]: 225838 : auto& s = bconn[ it->second ];
490 [ + - ]: 225838 : s.insert( end(s), begin(f), end(f) );
491 : : }
492 : : }
493 : : // Categorize boundary node lists
494 : 917080 : auto& bnode = std::get< 2 >( mesh );
495 [ + + ]: 4585400 : for (const auto& n : t) {
496 [ + - ]: 3668320 : auto it = nodeside.find( n );
497 [ + + ]: 3668320 : if (it != end(nodeside))
498 [ + + ]: 1933363 : for (auto s : it->second)
499 [ + - ][ + - ]: 995304 : bnode[ s ].push_back( n );
500 : : }
501 : : }
502 : :
503 : : // Make boundary node lists unique per side set
504 [ + + ]: 10944 : for (auto& c : chmesh)
505 [ + + ]: 18825 : for (auto& n : std::get<2>(c.second))
506 [ + - ]: 8620 : tk::unique( n.second );
507 : :
508 : : // Make sure all compute nodes have target chares assigned
509 [ - + ][ - - ]: 739 : Assert( !chmesh.empty(), "No elements have been assigned to a chare" );
[ - - ][ - - ]
510 : :
511 : : // This check should always be done, hence ErrChk and not Assert, as it
512 : : // can result from particular pathological combinations of (1) too large
513 : : // degree of virtualization, (2) too many compute nodes, and/or (3) too small
514 : : // of a mesh and not due to programmer error.
515 [ + + ]: 10944 : for(const auto& c : chmesh)
516 [ - + ][ - - ]: 10205 : ErrChk( !std::get<0>(c.second).empty(),
[ - - ][ - - ]
517 : : "Overdecomposition of the mesh is too large compared to the "
518 : : "number of work units computed based on the degree of "
519 : : "virtualization desired. As a result, there would be at least "
520 : : "one work unit with no mesh elements to work on, i.e., nothing "
521 : : "to do. Solution 1: decrease the virtualization to a lower "
522 : : "value using the command-line argument '-u'. Solution 2: "
523 : : "decrease the number processing elements (PEs and/or compute "
524 : : "nodes) using the charmrun command-line argument '+pN' where N is "
525 : : "the number of PEs (or in SMP-mode in combination with +ppn to "
526 : : "reduce the number of compute nodes), which implicitly increases "
527 : : "the size (and thus decreases the number) of work units.)" );
528 : :
529 : 1478 : return chmesh;
530 : 739 : }
531 : :
532 : : tk::UnsMesh::CoordMap
533 : 10205 : Partitioner::coordmap( const std::vector< std::size_t >& inpoel )
534 : : // *****************************************************************************
535 : : // Extract coordinates associated to global nodes of a mesh chunk
536 : : //! \param[in] inpoel Mesh connectivity
537 : : //! \return Map storing the coordinates of unique nodes associated to global
538 : : //! node IDs in mesh given by inpoel
539 : : // *****************************************************************************
540 : : {
541 [ - + ][ - - ]: 10205 : Assert( inpoel.size() % 4 == 0, "Incomplete mesh connectivity" );
[ - - ][ - - ]
542 : :
543 : 10205 : tk::UnsMesh::CoordMap map;
544 : :
545 [ + - ][ + + ]: 465513 : for (auto g : tk::uniquecopy(inpoel)) {
546 [ + - ]: 455308 : auto i = tk::cref_find( m_lid, g );
547 [ + - ]: 455308 : auto& c = map[g];
548 : 455308 : c[0] = m_coord[0][i];
549 : 455308 : c[1] = m_coord[1][i];
550 : 455308 : c[2] = m_coord[2][i];
551 : 10205 : }
552 : :
553 [ + - ][ - + ]: 10205 : Assert( tk::uniquecopy(inpoel).size() == map.size(), "Size mismatch" );
[ - - ][ - - ]
[ - - ]
554 : :
555 : 10205 : return map;
556 : 0 : }
557 : :
558 : : void
559 : 739 : Partitioner::distribute( std::unordered_map< int, MeshData >&& mesh )
560 : : // *****************************************************************************
561 : : // Distribute mesh to target compute nodes after mesh partitioning
562 : : //! \param[in] mesh Mesh data categorized by target by target chares
563 : : // *****************************************************************************
564 : : {
565 [ + - ]: 739 : auto dist = distribution( m_nchare );
566 : :
567 : : // Extract mesh data whose chares are on ("owned by") this compute node
568 [ + + ]: 3198 : for (int c=0; c<dist[1]; ++c) {
569 : 2459 : auto chid = CkMyNode() * dist[0] + c; // compute owned chare ID
570 [ + - ]: 2459 : const auto it = mesh.find( chid ); // attempt to find its mesh data
571 [ + + ]: 2459 : if (it != end(mesh)) { // if found
572 : : // Store own tetrahedron connectivity
573 : 2135 : const auto& inpoel = std::get<0>( it->second );
574 [ + - ]: 2135 : auto& inp = m_chinpoel[ chid ]; // will store own mesh connectivity
575 [ + - ]: 2135 : inp.insert( end(inp), begin(inpoel), end(inpoel) );
576 : : // Store own node coordinates
577 [ + - ]: 2135 : auto& chcm = m_chcoordmap[ chid ]; // will store own node coordinates
578 [ + - ]: 2135 : auto cm = coordmap( inpoel ); // extract node coordinates
579 [ + - ]: 2135 : chcm.insert( begin(cm), end(cm) ); // concatenate node coords
580 : : // Store own boundary face connectivity
581 : 2135 : const auto& bconn = std::get<1>( it->second );
582 [ + - ]: 2135 : auto& bface = m_chbface[ chid ]; // will store own boundary faces
583 [ + - ]: 2135 : auto& t = m_chtriinpoel[ chid ]; // wil store own boundary face conn
584 [ + - ]: 2135 : auto& f = m_nface[ chid ]; // use counter for chare
585 [ + + ]: 6465 : for (const auto& [ setid, faceids ] : bconn) {
586 [ + - ]: 4330 : auto& b = bface[ setid ];
587 [ + + ]: 147958 : for (std::size_t i=0; i<faceids.size()/3; ++i) {
588 [ + - ]: 143628 : b.push_back( f++ );
589 [ + - ]: 143628 : t.push_back( faceids[i*3+0] );
590 [ + - ]: 143628 : t.push_back( faceids[i*3+1] );
591 [ + - ]: 143628 : t.push_back( faceids[i*3+2] );
592 : : }
593 : : }
594 : : // Store own boundary node lists
595 : 2135 : const auto& bnode = std::get<2>( it->second );
596 [ + - ]: 2135 : auto& nodes = m_chbnode[ chid ]; // will store own boundary nodes
597 [ + + ]: 4533 : for (const auto& [ setid, nodeids ] : bnode) {
598 [ + - ]: 2398 : auto& b = nodes[ setid ];
599 [ + - ]: 2398 : b.insert( end(b), begin(nodeids), end(nodeids) );
600 : : }
601 : : // Remove chare ID and mesh data
602 [ + - ]: 2135 : mesh.erase( it );
603 : 2135 : }
604 [ + - ][ - + ]: 2459 : Assert( mesh.find(chid) == end(mesh), "Not all owned mesh data stored" );
[ - - ][ - - ]
[ - - ]
605 : : }
606 : :
607 : : // Construct export map (associating mesh connectivities with global node
608 : : // indices and node coordinates) for mesh chunks associated to chare IDs
609 : : // owned by chares we do not own.
610 : : std::unordered_map< int, // target compute node
611 : : std::unordered_map< int, // chare ID
612 : : std::tuple<
613 : : // (domain-element) tetrahedron connectivity
614 : : std::vector< std::size_t >,
615 : : // (domain) node IDs & coordinates
616 : : tk::UnsMesh::CoordMap,
617 : : // boundary side set + face connectivity
618 : : std::unordered_map< int, std::vector< std::size_t > >,
619 : : // boundary side set + node list
620 : : std::unordered_map< int, std::vector< std::size_t > >
621 : 739 : > > > exp;
622 : :
623 [ + + ]: 8809 : for (const auto& c : mesh)
624 [ + - ][ + - ]: 8070 : exp[ node(c.first) ][ c.first ] =
[ + - ]
625 [ + - ]: 16140 : std::make_tuple( std::get<0>(c.second),
626 [ + - ]: 16140 : coordmap(std::get<0>(c.second)),
627 : 8070 : std::get<1>(c.second),
628 : 16140 : std::get<2>(c.second) );
629 : :
630 : : // Export chare IDs and mesh we do not own to fellow compute nodes
631 [ + + ]: 739 : if (exp.empty()) {
632 [ + + ][ + - ]: 95 : if (g_cfg.get< tag::feedback >()) m_host.pedistributed();
633 [ + - ]: 95 : contribute( sizeof(std::size_t), &m_meshid, CkReduction::nop,
634 : 95 : m_cbp.get< tag::distributed >() );
635 : : } else {
636 : 644 : m_ndist += exp.size();
637 [ + + ]: 3476 : for (const auto& [ targetnode, chunk ] : exp)
638 [ + - ][ + - ]: 2832 : thisProxy[ targetnode ].addMesh( CkMyNode(), chunk );
639 : : }
640 : 739 : }
641 : :
642 : : std::array< int, 2 >
643 : 1478 : Partitioner::distribution( int npart ) const
644 : : // *****************************************************************************
645 : : // Compute chare (partition) distribution
646 : : //! \param[in] npart Total number of chares (partitions) to distribute
647 : : //! \return Chunksize, i.e., number of chares per all compute nodes except the
648 : : //! last one, and the number of chares for this compute node.
649 : : //! \details Chare ids are distributed to compute nodes in a linear continguous
650 : : //! order with the last compute node taking the remainder if the number of
651 : : //! compute nodes is not divisible by the number chares. For example, if
652 : : //! nchare=7 and nnode=3, the chare distribution is node0: 0 1, node1: 2 3,
653 : : //! and node2: 4 5 6. As a result of this distribution, all compute nodes will
654 : : //! have their chare-categorized element connectivity filled with the global
655 : : //! mesh node IDs associated to the Charm++ chare IDs each compute node owns.
656 : : // *****************************************************************************
657 : : {
658 : 1478 : auto chunksize = npart / CkNumNodes();
659 : 1478 : auto mynchare = chunksize;
660 [ + + ]: 1478 : if (CkMyNode() == CkNumNodes()-1) mynchare += npart % CkNumNodes();
661 : 1478 : return {{ chunksize, mynchare }};
662 : : }
663 : :
664 : : #include "NoWarning/partitioner.def.h"
|