Branch data Line data Source code
1 : : // *****************************************************************************
2 : : /*!
3 : : \file src/Inciter/Transporter.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 Transporter drives the time integration of transport equations
9 : : \details Transporter drives the time integration of transport equations.
10 : : The implementation uses the Charm++ runtime system and is fully asynchronous,
11 : : overlapping computation, communication as well as I/O. The algorithm
12 : : utilizes the structured dagger (SDAG) Charm++ functionality. The high-level
13 : : overview of the algorithm structure and how it interfaces with Charm++ is
14 : : discussed in the Charm++ interface file src/Inciter/transporter.ci.
15 : : */
16 : : // *****************************************************************************
17 : :
18 : : #include <string>
19 : : #include <iostream>
20 : : #include <cstddef>
21 : : #include <unordered_set>
22 : : #include <limits>
23 : : #include <cmath>
24 : :
25 : : #include <brigand/algorithms/for_each.hpp>
26 : :
27 : : #include "Macro.hpp"
28 : : #include "Transporter.hpp"
29 : : #include "Fields.hpp"
30 : : #include "PDEStack.hpp"
31 : : #include "UniPDF.hpp"
32 : : #include "PDFWriter.hpp"
33 : : #include "ContainerUtil.hpp"
34 : : #include "LoadDistributor.hpp"
35 : : #include "MeshReader.hpp"
36 : : #include "Inciter/InputDeck/InputDeck.hpp"
37 : : #include "NodeDiagnostics.hpp"
38 : : #include "ElemDiagnostics.hpp"
39 : : #include "DiagWriter.hpp"
40 : : #include "Callback.hpp"
41 : : #include "CartesianProduct.hpp"
42 : :
43 : : #include "NoWarning/inciter.decl.h"
44 : : #include "NoWarning/partitioner.decl.h"
45 : :
46 : : extern CProxy_Main mainProxy;
47 : :
48 : : namespace inciter {
49 : :
50 : : extern ctr::InputDeck g_inputdeck_defaults;
51 : : extern ctr::InputDeck g_inputdeck;
52 : : extern std::vector< CGPDE > g_cgpde;
53 : : extern std::vector< DGPDE > g_dgpde;
54 : : extern std::vector< FVPDE > g_fvpde;
55 : :
56 : : }
57 : :
58 : : using inciter::Transporter;
59 : :
60 : 192 : Transporter::Transporter() :
61 : : m_input( input() ),
62 : : m_nchare( m_input.size() ),
63 : : m_meshid(),
64 : 0 : m_ncit( m_nchare.size(), 0 ),
65 : : m_nload( 0 ),
66 : : m_ntrans( 0 ),
67 : : m_ndtmsh( 0 ),
68 : : m_dtmsh(),
69 : : m_npart( 0 ),
70 : : m_nstat( 0 ),
71 : : m_ndisc( 0 ),
72 : : m_nchk( 0 ),
73 : : m_ncom( 0 ),
74 : 0 : m_nt0refit( m_nchare.size(), 0 ),
75 : 0 : m_ndtrefit( m_nchare.size(), 0 ),
76 : 0 : m_noutrefit( m_nchare.size(), 0 ),
77 : 0 : m_noutderefit( m_nchare.size(), 0 ),
78 : : m_scheme(),
79 : : m_partitioner(),
80 : : m_refiner(),
81 : : m_meshwriter(),
82 : : m_sorter(),
83 : : m_nelem( m_nchare.size() ),
84 : : m_npoin(),
85 : 0 : m_finished( m_nchare.size(), 0 ),
86 : : m_meshvol( m_nchare.size() ),
87 : : m_minstat( m_nchare.size() ),
88 : : m_maxstat( m_nchare.size() ),
89 : : m_avgstat( m_nchare.size() ),
90 : : m_timer(),
91 : 384 : m_progMesh( g_inputdeck.get< tag::cmd, tag::feedback >(),
92 : : ProgMeshPrefix, ProgMeshLegend ),
93 : 192 : m_progWork( g_inputdeck.get< tag::cmd, tag::feedback >(),
94 [ + - ][ + - ]: 1152 : ProgWorkPrefix, ProgWorkLegend )
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + + ][ + - ]
95 : : // *****************************************************************************
96 : : // Constructor
97 : : // *****************************************************************************
98 : : {
99 : : // Echo configuration to screen
100 [ + - ][ + - ]: 192 : info( printer() );
101 : :
102 : 192 : const auto nstep = g_inputdeck.get< tag::nstep >();
103 : 192 : const auto t0 = g_inputdeck.get< tag::t0 >();
104 : 192 : const auto term = g_inputdeck.get< tag::term >();
105 : 192 : const auto constdt = g_inputdeck.get< tag::dt >();
106 : :
107 : : // If the desired max number of time steps is larger than zero, and the
108 : : // termination time is larger than the initial time, and the constant time
109 : : // step size (if that is used) is smaller than the duration of the time to be
110 : : // simulated, we have work to do, otherwise, finish right away. If a constant
111 : : // dt is not used, that part of the logic is always true as the default
112 : : // constdt is zero, see inciter::ctr::InputDeck::InputDeck().
113 [ + - ][ + - ]: 192 : if ( nstep != 0 && term > t0 && constdt < term-t0 ) {
[ + - ]
114 : :
115 : : // Enable SDAG waits for collecting mesh statistics
116 [ + - ]: 192 : thisProxy.wait4stat();
117 : :
118 : : // Configure and write diagnostics file header
119 [ + - ]: 192 : diagHeader();
120 : :
121 : : // Create mesh partitioner AND boundary condition object group
122 [ + - ]: 192 : createPartitioner();
123 : :
124 [ - - ]: 0 : } else finish(); // stop if no time stepping requested
125 : 192 : }
126 : :
127 : 2 : Transporter::Transporter( CkMigrateMessage* m ) :
128 : : CBase_Transporter( m ),
129 : 4 : m_progMesh( g_inputdeck.get< tag::cmd, tag::feedback >(),
130 : : ProgMeshPrefix, ProgMeshLegend ),
131 : 2 : m_progWork( g_inputdeck.get< tag::cmd, tag::feedback >(),
132 [ + - ][ + + ]: 12 : ProgWorkPrefix, ProgWorkLegend )
[ + - ]
133 : : // *****************************************************************************
134 : : // Migrate constructor: returning from a checkpoint
135 : : //! \param[in] m Charm++ migrate message
136 : : // *****************************************************************************
137 : : {
138 [ + - ]: 4 : auto print = printer();
139 [ + - ][ + - ]: 2 : print.diag( "Restarted from checkpoint" );
140 [ + - ]: 2 : info( print );
141 [ + - ]: 2 : inthead( print );
142 : 2 : }
143 : :
144 : : std::vector< std::string >
145 : 192 : Transporter::input()
146 : : // *****************************************************************************
147 : : // Generate list of input mesh filenames configured by the user
148 : : //! \return List of input mesh filenames configured by the user
149 : : //! \details If the input file is given on the command line, a single solver
150 : : //! will be instantiated on the single mesh, solving potentially multiple
151 : : //! systems of (potentially coupled) equations. If the input file is not given
152 : : //! on the command line, the mesh files are expected to be configured in the
153 : : //! control/input file, associating a potentially different mesh to each
154 : : //! solver. Both configurations allow the solution of coupled systems, but the
155 : : //! first one solves all equations on the same mesh, while the latter can
156 : : //! couple solutions computed on multiple different meshes.
157 : : // *****************************************************************************
158 : : {
159 : : // Query input mesh filename specified on the command line
160 : 192 : const auto& cmdinput = g_inputdeck.get< tag::cmd, tag::io, tag::input >();
161 : :
162 : : // Extract mesh filenames specified in the control file (assigned to solvers)
163 : 384 : std::vector< std::string > ctrinput;
164 [ + + ]: 386 : for (const auto& im : g_inputdeck.get< tag::mesh >()) {
165 [ + - ]: 194 : ctrinput.push_back(im.get< tag::filename >());
166 : : }
167 : :
168 [ + + ][ - + ]: 192 : ErrChk( not cmdinput.empty() or not ctrinput.empty(),
[ - - ][ - - ]
[ - - ]
169 : : "Either a single input mesh must be given on the command line or multiple "
170 : : "meshes must be configured in the control file." );
171 : :
172 : : // Prepend control file path to mesh filenames in given in control file
173 [ + - ]: 192 : if (not ctrinput.empty()) {
174 : 192 : const auto& ctr = g_inputdeck.get< tag::cmd, tag::io, tag::control >();
175 [ + - ]: 384 : auto path = ctr.substr( 0, ctr.find_last_of("/")+1 );
176 [ + + ][ + - ]: 386 : for (auto& f : ctrinput) f = path + f;
177 : : }
178 : :
179 [ + + ][ + - ]: 374 : if (cmdinput.empty()) return ctrinput; else return { cmdinput };
[ + - ][ + + ]
[ - - ]
180 : : }
181 : :
182 : : void
183 : 194 : Transporter::info( const InciterPrint& print )
184 : : // *****************************************************************************
185 : : // Echo configuration to screen
186 : : //! \param[in] print Pretty printer object to use for printing
187 : : // *****************************************************************************
188 : : {
189 [ + - ][ + - ]: 194 : print.part( "Factory" );
190 : :
191 : : // Print out info data layout
192 [ + - ][ + - ]: 194 : print.list( "Unknowns data layout (CMake: FIELD_DATA_LAYOUT)",
193 [ + - ][ + - ]: 582 : std::list< std::string >{ tk::Fields::layout() } );
[ + + ][ - - ]
194 : :
195 : : // Re-create partial differential equations stack for output
196 [ + - ]: 388 : PDEStack stack;
197 : :
198 : : // Print out information on PDE factories
199 [ + - ][ + - ]: 388 : print.eqlist( "Registered PDEs using continuous Galerkin (CG) methods",
200 : 194 : stack.cgfactory(), stack.cgntypes() );
201 [ + - ][ + - ]: 388 : print.eqlist( "Registered PDEs using discontinuous Galerkin (DG) methods",
202 : 194 : stack.dgfactory(), stack.dgntypes() );
203 [ + - ][ + - ]: 388 : print.eqlist( "Registered PDEs using finite volume (DG) methods",
204 : 194 : stack.fvfactory(), stack.fvntypes() );
205 [ + - ]: 194 : print.endpart();
206 : :
207 : : // Print out information on problem
208 [ + - ][ + - ]: 194 : print.part( "Problem" );
209 : :
210 : : // Print out info on problem title
211 [ + - ]: 194 : if ( !g_inputdeck.get< tag::title >().empty() )
212 [ + - ]: 194 : print.title( g_inputdeck.get< tag::title >() );
213 : :
214 : 194 : const auto nstep = g_inputdeck.get< tag::nstep >();
215 : 194 : const auto t0 = g_inputdeck.get< tag::t0 >();
216 : 194 : const auto term = g_inputdeck.get< tag::term >();
217 : 194 : const auto constdt = g_inputdeck.get< tag::dt >();
218 : 194 : const auto cfl = g_inputdeck.get< tag::cfl >();
219 : 194 : const auto scheme = g_inputdeck.get< tag::scheme >();
220 : :
221 : : // Print discretization parameters
222 [ + - ][ + - ]: 194 : print.section( "Discretization parameters" );
223 [ + - ]: 194 : print.Item< ctr::Scheme, tag::scheme >();
224 [ + - ][ + - ]: 194 : print.item( "Implicit-Explicit Runge-Kutta",
225 : 194 : g_inputdeck.get< tag::imex_runge_kutta >() );
226 : :
227 [ + - ][ + + ]: 194 : if (g_inputdeck.centering() == tk::Centering::ELEM)
228 : : {
229 [ + - ]: 108 : print.Item< ctr::Limiter, tag::limiter >();
230 : :
231 [ + - ][ + - ]: 108 : print.item("Shock detection based limiting",
232 : 108 : g_inputdeck.get< tag::shock_detector_coeff >());
233 : :
234 [ - + ]: 108 : if (g_inputdeck.get< tag::accuracy_test >())
235 : : {
236 [ - - ][ - - ]: 0 : print.item("WARNING: order-of-accuracy testing enabled",
237 : : "robustness corrections inactive");
238 : : }
239 : :
240 [ + - ][ + - ]: 108 : print.item("Limited solution projection",
241 : 108 : g_inputdeck.get< tag::limsol_projection >());
242 : : }
243 [ + - ][ + - ]: 194 : print.item( "PE-locality mesh reordering",
244 : 194 : g_inputdeck.get< tag::pelocal_reorder >() );
245 [ + - ][ + - ]: 194 : print.item( "Operator-access mesh reordering",
246 : 194 : g_inputdeck.get< tag::operator_reorder >() );
247 : 194 : auto steady = g_inputdeck.get< tag::steady_state >();
248 [ + - ][ + - ]: 194 : print.item( "Local time stepping", steady );
249 [ + + ]: 194 : if (steady) {
250 [ + - ][ + - ]: 2 : print.item( "L2-norm residual convergence criterion",
251 : 2 : g_inputdeck.get< tag::residual >() );
252 [ + - ][ + - ]: 2 : print.item( "Convergence criterion component index",
253 : 2 : g_inputdeck.get< tag::rescomp >() );
254 : : }
255 [ + - ][ + - ]: 194 : print.item( "Number of time steps", nstep );
256 [ + - ][ + - ]: 194 : print.item( "Start time", t0 );
257 [ + - ][ + - ]: 194 : print.item( "Terminate time", term );
258 : :
259 [ + + ]: 194 : if (constdt > std::numeric_limits< tk::real >::epsilon())
260 [ + - ][ + - ]: 125 : print.item( "Constant time step size", constdt );
261 [ + - ]: 69 : else if (cfl > std::numeric_limits< tk::real >::epsilon())
262 : : {
263 [ + - ][ + - ]: 69 : print.item( "CFL coefficient", cfl );
264 : : }
265 : :
266 : 194 : const auto& meshes = g_inputdeck.get< tag::mesh >();
267 : 194 : const auto& depvars = g_inputdeck.get< tag::depvar >();
268 [ + + ]: 390 : for (std::size_t i=0; i<meshes.size(); ++i) {
269 [ + - ][ + - ]: 588 : print.item( "Dependent var name and assoc. mesh", std::string{depvars[i]}
[ + - ]
270 [ + - ][ + - ]: 588 : + " - " + meshes[i].get< tag::filename >() );
271 : : }
272 : :
273 : : // Print out info on settings of selected partial differential equations
274 [ + - ][ + - ]: 194 : print.pdes( "Partial differential equations integrated", stack.info() );
[ + - ]
275 : :
276 : : // Print out adaptive polynomial refinement configuration
277 [ + + ]: 194 : if (scheme == ctr::SchemeType::PDG) {
278 [ + - ][ + - ]: 12 : print.section( "Polynomial refinement (p-ref)" );
279 [ + - ][ + - ]: 12 : print.item( "p-refinement",
280 : 12 : g_inputdeck.get< tag::pref, tag::pref >() );
281 [ + - ]: 12 : print.Item< ctr::PrefIndicator, tag::pref, tag::indicator >();
282 [ + - ][ + - ]: 12 : print.item( "Max degrees of freedom",
283 : 12 : g_inputdeck.get< tag::pref, tag::ndofmax >() );
284 [ + - ][ + - ]: 12 : print.item( "Tolerance",
285 : 12 : g_inputdeck.get< tag::pref, tag::tolref >() );
286 : : }
287 : :
288 : : // Print out adaptive mesh refinement configuration
289 : 194 : const auto amr = g_inputdeck.get< tag::amr, tag::amr >();
290 [ + + ]: 194 : if (amr) {
291 [ + - ][ + - ]: 20 : print.section( "Mesh refinement (h-ref)" );
292 : 20 : auto maxlevels = g_inputdeck.get< tag::amr, tag::maxlevels >();
293 [ + - ][ + - ]: 20 : print.item( "Maximum mesh refinement levels", maxlevels );
294 [ + - ]: 20 : print.Item< ctr::AMRError, tag::amr, tag::error >();
295 : 20 : auto t0ref = g_inputdeck.get< tag::amr, tag::t0ref >();
296 [ + - ][ + - ]: 20 : print.item( "Refinement at t<0 (t0ref)", t0ref );
297 [ + - ]: 20 : if (t0ref) {
298 : 20 : const auto& initref = g_inputdeck.get< tag::amr, tag::initial >();
299 [ + - ][ + - ]: 20 : print.item( "Initial refinement steps", initref.size() );
300 : :
301 : 20 : auto eps = std::numeric_limits< tk::real >::epsilon();
302 : :
303 : 20 : const auto& amr_coord = g_inputdeck.get< tag::amr, tag::coords >();
304 : 20 : const auto& amr_defcoord = g_inputdeck_defaults.get< tag::amr, tag::coords >();
305 : :
306 : 20 : auto xminus = amr_coord.get< tag::xminus >();
307 : 20 : auto xminus_default = amr_defcoord.get< tag::xminus >();
308 [ + + ]: 20 : if (std::abs( xminus - xminus_default ) > eps)
309 [ + - ][ + - ]: 1 : print.item( "Initial refinement x-", xminus );
310 : 20 : auto xplus = amr_coord.get< tag::xplus >();
311 : 20 : auto xplus_default = amr_defcoord.get< tag::xplus >();
312 [ - + ]: 20 : if (std::abs( xplus - xplus_default ) > eps)
313 [ - - ][ - - ]: 0 : print.item( "Initial refinement x+", xplus );
314 : :
315 : 20 : auto yminus = amr_coord.get< tag::yminus >();
316 : 20 : auto yminus_default = amr_defcoord.get< tag::yminus >();
317 [ - + ]: 20 : if (std::abs( yminus - yminus_default ) > eps)
318 [ - - ][ - - ]: 0 : print.item( "Initial refinement y-", yminus );
319 : 20 : auto yplus = amr_coord.get< tag::yplus >();
320 : 20 : auto yplus_default = amr_defcoord.get< tag::yplus >();
321 [ - + ]: 20 : if (std::abs( yplus - yplus_default ) > eps)
322 [ - - ][ - - ]: 0 : print.item( "Initial refinement y+", yplus );
323 : :
324 : 20 : auto zminus = amr_coord.get< tag::zminus >();
325 : 20 : auto zminus_default = amr_defcoord.get< tag::zminus >();
326 [ - + ]: 20 : if (std::abs( zminus - zminus_default ) > eps)
327 [ - - ][ - - ]: 0 : print.item( "Initial refinement z-", zminus );
328 : 20 : auto zplus = amr_coord.get< tag::zplus >();
329 : 20 : auto zplus_default = amr_defcoord.get< tag::zplus >();
330 [ - + ]: 20 : if (std::abs( zplus - zplus_default ) > eps)
331 [ - - ][ - - ]: 0 : print.item( "Initial refinement z+", zplus );
332 : : }
333 : 20 : auto dtref = g_inputdeck.get< tag::amr, tag::dtref >();
334 [ + - ][ + - ]: 20 : print.item( "Refinement at t>0 (dtref)", dtref );
335 [ - + ]: 20 : if (dtref) {
336 : 0 : auto dtfreq = g_inputdeck.get< tag::amr, tag::dtfreq >();
337 [ - - ][ - - ]: 0 : print.item( "Mesh refinement frequency, t>0", dtfreq );
338 [ - - ][ - - ]: 0 : print.item( "Uniform-only mesh refinement, t>0",
339 : 0 : g_inputdeck.get< tag::amr, tag::dtref_uniform >() );
340 : : }
341 [ + - ][ + - ]: 20 : print.item( "Refinement tolerance",
342 : 20 : g_inputdeck.get< tag::amr, tag::tol_refine >() );
343 [ + - ][ + - ]: 20 : print.item( "De-refinement tolerance",
344 : 20 : g_inputdeck.get< tag::amr, tag::tol_derefine >() );
345 : : }
346 : :
347 : : // Print out ALE configuration
348 : 194 : const auto ale = g_inputdeck.get< tag::ale, tag::ale >();
349 [ + + ]: 194 : if (ale) {
350 [ + - ][ + - ]: 10 : print.section( "Arbitrary Lagrangian-Eulerian (ALE) mesh motion" );
351 : 10 : auto dvcfl = g_inputdeck.get< tag::ale, tag::dvcfl >();
352 [ + - ][ + - ]: 10 : print.item( "Volume-change CFL coefficient", dvcfl );
353 [ + - ]: 10 : print.Item< ctr::MeshVelocity, tag::ale, tag::mesh_velocity >();
354 [ + - ]: 10 : print.Item< ctr::MeshVelocitySmoother, tag::ale, tag::smoother >();
355 [ + - ][ + - ]: 10 : print.item( "Mesh motion dimensions", tk::parameters(
[ + - ]
356 : 10 : g_inputdeck.get< tag::ale, tag::mesh_motion >() ) );
357 : 10 : const auto& meshforce = g_inputdeck.get< tag::ale, tag::meshforce >();
358 [ + - ][ + - ]: 10 : print.item( "Mesh velocity force coefficients", tk::parameters(meshforce) );
[ + - ]
359 [ + - ][ + - ]: 10 : print.item( "Vorticity multiplier",
360 : 10 : g_inputdeck.get< tag::ale, tag::vortmult >() );
361 [ + - ][ + - ]: 10 : print.item( "Mesh velocity linear solver tolerance",
362 : 10 : g_inputdeck.get< tag::ale, tag::tolerance >() );
363 [ + - ][ + - ]: 10 : print.item( "Mesh velocity linear solver maxit",
364 : 10 : g_inputdeck.get< tag::ale, tag::maxit >() );
365 : 10 : const auto& dir = g_inputdeck.get< tag::ale, tag::dirichlet >();
366 [ + + ]: 10 : if (not dir.empty())
367 [ + - ][ + - ]: 5 : print.item( "Mesh velocity Dirichlet BC sideset(s)",
368 [ + - ]: 10 : tk::parameters( dir ) );
369 : 10 : const auto& sym = g_inputdeck.get< tag::ale, tag::symmetry >();
370 [ + + ]: 10 : if (not sym.empty())
371 [ + - ][ + - ]: 3 : print.item( "Mesh velocity symmetry BC sideset(s)",
372 [ + - ]: 6 : tk::parameters( sym ) );
373 : 10 : std::size_t i = 1;
374 [ + + ]: 12 : for (const auto& m : g_inputdeck.get< tag::ale, tag::move >()) {
375 [ + - ]: 2 : tk::ctr::UserTable opt;
376 [ + - ][ + - ]: 2 : print.item( opt.group() + ' ' + std::to_string(i) + " interpreted as",
[ + - ][ + - ]
377 [ + - ][ + - ]: 2 : opt.name( m.get< tag::fntype >() ) );
378 : 2 : const auto& s = m.get< tag::sideset >();
379 [ + - ]: 2 : if (not s.empty())
380 [ + - ][ + - ]: 2 : print.item( "Moving sideset(s) with table " + std::to_string(i),
[ + - ]
381 [ + - ]: 4 : tk::parameters(s));
382 : 2 : ++i;
383 : : }
384 : : }
385 : :
386 : : // Print I/O filenames
387 [ + - ][ + - ]: 194 : print.section( "Input/Output filenames and directories" );
388 [ + - ][ + - ]: 194 : print.item( "Input mesh(es)", tk::parameters( m_input ) );
[ + - ]
389 : 194 : const auto& of = g_inputdeck.get< tag::cmd, tag::io, tag::output >();
390 [ + - ][ + - ]: 194 : print.item( "Volume field output file(s)",
391 [ + - ]: 388 : of + ".e-s.<meshid>.<numchares>.<chareid>" );
392 [ + - ][ + - ]: 194 : print.item( "Surface field output file(s)",
393 [ + - ]: 388 : of + "-surf.<surfid>.e-s.<meshid>.<numchares>.<chareid>" );
394 [ + - ][ + - ]: 194 : print.item( "History output file(s)", of + ".hist.{pointid}" );
[ + - ]
395 [ + - ][ + - ]: 194 : print.item( "Diagnostics file",
396 : 194 : g_inputdeck.get< tag::cmd, tag::io, tag::diag >() );
397 [ + - ][ + - ]: 194 : print.item( "Checkpoint/restart directory",
398 [ + - ]: 388 : g_inputdeck.get< tag::cmd, tag::io, tag::restart >() + '/' );
399 : :
400 : : // Print output intervals
401 [ + - ][ + - ]: 194 : print.section( "Output intervals (in units of iteration count)" );
402 [ + - ][ + - ]: 194 : print.item( "TTY", g_inputdeck.get< tag::ttyi>() );
403 [ + - ][ + - ]: 194 : print.item( "Field and surface",
404 : 194 : g_inputdeck.get< tag::field_output, tag::interval >() );
405 [ + - ][ + - ]: 194 : print.item( "History",
406 : 194 : g_inputdeck.get< tag::history_output, tag::interval >() );
407 [ + - ][ + - ]: 194 : print.item( "Diagnostics",
408 : 194 : g_inputdeck.get< tag::diagnostics, tag::interval >() );
409 [ + - ][ + - ]: 194 : print.item( "Checkpoint/restart",
410 : 194 : g_inputdeck.get< tag::cmd, tag::rsfreq >() );
411 : 194 : auto tf = g_inputdeck.get< tag::field_output, tag::time_interval >();
412 : 194 : auto th = g_inputdeck.get< tag::history_output, tag::time_interval >();
413 [ - + ][ - - ]: 194 : if (tf>0.0 || th>0.0) {
414 [ + - ][ + - ]: 194 : print.section( "Output intervals (in units of physics time)" );
415 [ + - ][ + - ]: 194 : if (tf > 0.0) print.item( "Field and surface", tf );
[ + - ]
416 [ + - ][ + - ]: 194 : if (th > 0.0) print.item( "History", th );
[ + - ]
417 : : }
418 : 194 : const auto& rf = g_inputdeck.get< tag::field_output, tag::time_range >();
419 : 194 : const auto& rh = g_inputdeck.get< tag::history_output, tag::time_range >();
420 [ + - ][ - + ]: 194 : if (not rf.empty() or not rh.empty()) {
[ - + ]
421 [ - - ][ - - ]: 0 : print.section( "Output time ranges (in units of physics time)" );
422 [ - - ][ - - ]: 0 : print.item("Field output { mintime, maxtime, dt }", tk::parameters(rf));
[ - - ]
423 [ - - ][ - - ]: 0 : print.item("History output { mintime, maxtime, dt }", tk::parameters(rh));
[ - - ]
424 : : }
425 : :
426 : : //// Print output variables: fields and surfaces
427 : : //const auto nodeoutvars = g_inputdeck.outvars( tk::Centering::NODE );
428 : : //const auto elemoutvars = g_inputdeck.outvars( tk::Centering::ELEM );
429 : : //const auto outsets = g_inputdeck.outsets();
430 : : //if (!nodeoutvars.empty() || !elemoutvars.empty() || !outsets.empty())
431 : : // print.section( "Output fields" );
432 : : //if (!nodeoutvars.empty())
433 : : // print.item( "Node field(s)", tk::parameters(nodeoutvars) );
434 : : //if (!elemoutvars.empty())
435 : : // print.item( "Elem field(s)", tk::parameters(elemoutvars) );
436 : : //if (!aliases.empty())
437 : : // print.item( "Alias(es)", tk::parameters(aliases) );
438 : : //if (!outsets.empty())
439 : : // print.item( "Surface side set(s)", tk::parameters(outsets) );
440 : :
441 : : // Print output variables: history
442 : 194 : const auto& pt = g_inputdeck.get< tag::history_output, tag::point >();
443 [ + + ]: 194 : if (!pt.empty()) {
444 [ + - ][ + - ]: 5 : print.section( "Output time history" );
445 [ + + ]: 14 : for (std::size_t p=0; p<pt.size(); ++p) {
446 : 9 : const auto& id = pt[p].get< tag::id >();
447 [ + - ]: 9 : std::stringstream ss;
448 : 9 : auto prec = g_inputdeck.get< tag::history_output, tag::precision >();
449 [ + - ]: 9 : ss << std::setprecision( static_cast<int>(prec) );
450 [ + - ][ + - ]: 9 : ss << of << ".hist." << id;
[ + - ]
451 [ + - ][ + - ]: 9 : print.longitem( "At point " + id + ' ' +
[ + - ][ + - ]
452 [ + - ][ + - ]: 18 : tk::parameters(pt[p].get<tag::coord>()), ss.str() );
453 : : }
454 : : }
455 : :
456 [ + - ]: 194 : print.endsubsection();
457 : 194 : }
458 : :
459 : : bool
460 : 280 : Transporter::matchBCs( std::map< int, std::vector< std::size_t > >& bnd )
461 : : // *****************************************************************************
462 : : // Verify that side sets specified in the control file exist in mesh file
463 : : //! \details This function does two things: (1) it verifies that the side
464 : : //! sets used in the input file (either to which boundary conditions (BC)
465 : : //! are assigned or listed as field output by the user in the
466 : : //! input file) all exist among the side sets read from the input mesh
467 : : //! file and errors out if at least one does not, and (2) it matches the
468 : : //! side set ids at which the user has configured BCs (or listed as an output
469 : : //! surface) to side set ids read from the mesh file and removes those face
470 : : //! and node lists associated to side sets that the user did not set BCs or
471 : : //! listed as field output on (as they will not need processing further since
472 : : //! they will not be used).
473 : : //! \param[in,out] bnd Node or face lists mapped to side set ids
474 : : //! \return True if sidesets have been used and found in mesh
475 : : // *****************************************************************************
476 : : {
477 : : // Query side set ids at which BCs assigned for all BC types for all PDEs
478 : 560 : std::unordered_set< int > usedsets;
479 : :
480 : : using bclist = ctr::bclist::Keys;
481 : 280 : const auto& bcs = g_inputdeck.get< tag::bc >();
482 [ + - ]: 280 : brigand::for_each< bclist >( UserBC( g_inputdeck, usedsets ) );
483 : :
484 : : // Query side sets of time dependent and inlet BCs (since these are not a part
485 : : // of tag::bc)
486 [ + + ]: 568 : for (const auto& bci : bcs) {
487 [ + + ]: 297 : for (const auto& b : bci.get< tag::inlet >()) {
488 [ + + ]: 18 : for (auto i : b.get< tag::sideset >())
489 [ + - ]: 9 : usedsets.insert(static_cast<int>(i));
490 : : }
491 [ + + ]: 292 : for (const auto& b : bci.get< tag::timedep >()) {
492 [ + + ]: 8 : for (auto i : b.get< tag::sideset >())
493 [ + - ]: 4 : usedsets.insert(static_cast<int>(i));
494 : : }
495 : : }
496 : :
497 : : // Query side sets of boundaries prescribed as moving with ALE
498 [ + + ]: 284 : for (const auto& move : g_inputdeck.get< tag::ale, tag::move >())
499 [ + + ]: 8 : for (auto i : move.get< tag::sideset >())
500 [ + - ]: 4 : usedsets.insert(static_cast<int>(i));
501 : :
502 : : // Add sidesets requested for field output
503 : 280 : const auto& ss = g_inputdeck.get< tag::cmd, tag::io, tag::surface >();
504 [ - + ]: 280 : for (const auto& s : ss) {
505 [ - - ]: 0 : std::stringstream conv( s );
506 : : int num;
507 [ - - ]: 0 : conv >> num;
508 [ - - ]: 0 : usedsets.insert( num );
509 : : }
510 : :
511 : : // Find user-configured side set ids among side sets read from mesh file
512 : 280 : std::unordered_set< int > sidesets_used;
513 [ + + ]: 1195 : for (auto i : usedsets) { // for all side sets used in control file
514 [ + - ][ + + ]: 915 : if (bnd.find(i) != end(bnd)) // used set found among side sets in file
515 [ + - ]: 885 : sidesets_used.insert( i ); // store side set id configured as BC
516 : : //else {
517 : : // Throw( "Boundary conditions specified on side set " +
518 : : // std::to_string(i) + " which does not exist in mesh file" );
519 : : //}
520 : : }
521 : :
522 : : // Remove sidesets not used (will not process those further)
523 [ + - ]: 280 : tk::erase_if( bnd, [&]( auto& item ) {
524 [ + - ]: 1383 : return sidesets_used.find( item.first ) == end(sidesets_used);
525 : : });
526 : :
527 : 560 : return !bnd.empty();
528 : : }
529 : :
530 : : void
531 : 192 : Transporter::createPartitioner()
532 : : // *****************************************************************************
533 : : // Create mesh partitioner AND boundary conditions group
534 : : // *****************************************************************************
535 : : {
536 : 192 : auto scheme = g_inputdeck.get< tag::scheme >();
537 [ + - ][ + - ]: 192 : auto centering = ctr::Scheme().centering( scheme );
538 [ + - ]: 384 : auto print = printer();
539 : :
540 : : // Create partitioner callbacks (order important)
541 : : tk::PartitionerCallback cbp {{
542 [ + - ][ + - ]: 192 : CkCallback( CkReductionTarget(Transporter,load), thisProxy )
543 [ + - ][ + - ]: 384 : , CkCallback( CkReductionTarget(Transporter,partitioned), thisProxy )
544 [ + - ][ + - ]: 384 : , CkCallback( CkReductionTarget(Transporter,distributed), thisProxy )
545 [ + - ][ + - ]: 384 : , CkCallback( CkReductionTarget(Transporter,refinserted), thisProxy )
546 [ + - ][ + - ]: 384 : , CkCallback( CkReductionTarget(Transporter,refined), thisProxy )
547 [ + - ]: 576 : }};
548 : :
549 : : // Create refiner callbacks (order important)
550 : : tk::RefinerCallback cbr {{
551 [ + - ][ + - ]: 192 : CkCallback( CkReductionTarget(Transporter,queriedRef), thisProxy )
552 [ + - ][ + - ]: 384 : , CkCallback( CkReductionTarget(Transporter,respondedRef), thisProxy )
553 [ + - ][ + - ]: 384 : , CkCallback( CkReductionTarget(Transporter,compatibility), thisProxy )
554 [ + - ][ + - ]: 384 : , CkCallback( CkReductionTarget(Transporter,bndint), thisProxy )
555 [ + - ][ + - ]: 384 : , CkCallback( CkReductionTarget(Transporter,matched), thisProxy )
556 [ + - ][ + - ]: 384 : , CkCallback( CkReductionTarget(Transporter,refined), thisProxy )
557 [ + - ]: 576 : }};
558 : :
559 : : // Create sorter callbacks (order important)
560 : : tk::SorterCallback cbs {{
561 [ + - ][ + - ]: 192 : CkCallback( CkReductionTarget(Transporter,queried), thisProxy )
562 [ + - ][ + - ]: 384 : , CkCallback( CkReductionTarget(Transporter,responded), thisProxy )
563 [ + - ][ + - ]: 384 : , CkCallback( CkReductionTarget(Transporter,discinserted), thisProxy )
564 [ + - ][ + - ]: 384 : , CkCallback( CkReductionTarget(Transporter,workinserted), thisProxy )
565 [ + - ]: 576 : }};
566 : :
567 : : // Start timer measuring preparation of mesh(es) for partitioning
568 [ + - ]: 192 : m_timer[ TimerTag::MESH_READ ];
569 : :
570 : : // Start preparing mesh(es)
571 [ + - ][ + - ]: 192 : print.diag( "Reading mesh(es)" );
572 : :
573 : : // Create (discretization) Scheme chare worker arrays for all meshes
574 [ + + ]: 386 : for ([[maybe_unused]] const auto& filename : m_input)
575 : : m_scheme.emplace_back( g_inputdeck.get< tag::scheme >(),
576 : : g_inputdeck.get< tag::ale, tag::ale >(),
577 : 194 : need_linearsolver(),
578 [ + - ][ + - ]: 194 : centering );
579 : :
580 [ - + ][ - - ]: 192 : ErrChk( !m_input.empty(), "No input mesh" );
[ - - ][ - - ]
581 : :
582 : : // Read boundary (side set) data from a list of input mesh files
583 : 192 : std::size_t meshid = 0;
584 [ + + ]: 386 : for (const auto& filename : m_input) {
585 : : // Create mesh reader for reading side sets from file
586 [ + - ]: 388 : tk::MeshReader mr( filename );
587 : :
588 : : // Read out total number of mesh points from mesh file
589 [ + - ][ + - ]: 194 : m_npoin.push_back( mr.npoin() );
590 : :
591 : 388 : std::map< int, std::vector< std::size_t > > bface;
592 : 388 : std::map< int, std::vector< std::size_t > > faces;
593 : 388 : std::map< int, std::vector< std::size_t > > bnode;
594 : :
595 : : // Read boundary-face connectivity on side sets
596 [ + - ]: 194 : mr.readSidesetFaces( bface, faces );
597 : :
598 : 194 : bool bcs_set = false;
599 [ + + ]: 194 : if (centering == tk::Centering::ELEM) {
600 : :
601 : : // Verify boundarty condition (BC) side sets used exist in mesh file
602 [ + - ]: 108 : bcs_set = matchBCs( bface );
603 : :
604 [ + - ]: 86 : } else if (centering == tk::Centering::NODE) {
605 : :
606 : : // Read node lists on side sets
607 [ + - ]: 86 : bnode = mr.readSidesetNodes();
608 : : // Verify boundarty condition (BC) side sets used exist in mesh file
609 [ + - ]: 86 : bool bcnode_set = matchBCs( bnode );
610 [ + - ]: 86 : bool bcface_set = matchBCs( bface );
611 [ + + ][ - + ]: 86 : bcs_set = bcface_set or bcnode_set;
612 : : }
613 : :
614 : : // Warn on no BCs
615 [ + + ][ + - ]: 194 : if (!bcs_set) print << "\n>>> WARNING: No boundary conditions set\n\n";
616 : :
617 [ + - ]: 194 : auto opt = m_scheme[meshid].arrayoptions();
618 : : // Create empty mesh refiner chare array (bound to workers)
619 [ + - ][ + - ]: 194 : m_refiner.push_back( CProxy_Refiner::ckNew(opt) );
[ + - ]
620 : : // Create empty mesh sorter Charm++ chare array (bound to workers)
621 [ + - ][ + - ]: 194 : m_sorter.push_back( CProxy_Sorter::ckNew(opt) );
[ + - ]
622 : :
623 : : // Create MeshWriter chare group for mesh
624 [ + - ][ + - ]: 194 : m_meshwriter.push_back(
625 : : tk::CProxy_MeshWriter::ckNew(
626 : 194 : g_inputdeck.get< tag::field_output, tag::filetype >(),
627 : : centering,
628 : 194 : g_inputdeck.get< tag::cmd, tag::benchmark >(),
629 : 194 : m_input.size() ) );
630 : :
631 : : // Create mesh partitioner Charm++ chare nodegroup for all meshes
632 [ + - ]: 194 : m_partitioner.push_back(
633 : : CProxy_Partitioner::ckNew( meshid, filename, cbp, cbr, cbs,
634 [ + - ]: 194 : thisProxy, m_refiner.back(), m_sorter.back(), m_meshwriter.back(),
635 : 194 : m_scheme, bface, faces, bnode ) );
636 : :
637 : 194 : ++meshid;
638 : : }
639 : 192 : }
640 : :
641 : : void
642 : 194 : Transporter::load( std::size_t meshid, std::size_t nelem )
643 : : // *****************************************************************************
644 : : // Reduction target: the mesh has been read from file on all PEs
645 : : //! \param[in] meshid Mesh id (summed accross all compute nodes)
646 : : //! \param[in] nelem Number of mesh elements per mesh (summed across all
647 : : //! compute nodes)
648 : : // *****************************************************************************
649 : : {
650 : 194 : meshid /= static_cast< std::size_t >( CkNumNodes() );
651 [ - + ][ - - ]: 194 : Assert( meshid < m_nelem.size(), "MeshId indexing out" );
[ - - ][ - - ]
652 : 194 : m_nelem[meshid] = nelem;
653 : :
654 : : // Compute load distribution given total work (nelem) and user-specified
655 : : // virtualization
656 : : uint64_t chunksize, remainder;
657 : 388 : m_nchare[meshid] = static_cast<int>(
658 [ + - ]: 388 : tk::linearLoadDistributor(
659 : 194 : g_inputdeck.get< tag::cmd, tag::virtualization >(),
660 : 194 : m_nelem[meshid], CkNumPes(), chunksize, remainder ) );
661 : :
662 : : // Store sum of meshids (across all chares, key) for each meshid (value).
663 : : // This is used to look up the mesh id after collectives that sum their data.
664 [ + - ]: 194 : m_meshid[ static_cast<std::size_t>(m_nchare[meshid])*meshid ] = meshid;
665 [ - + ][ - - ]: 194 : Assert( meshid < m_nelem.size(), "MeshId indexing out" );
[ - - ][ - - ]
666 : :
667 : : // Tell the meshwriter for this mesh the total number of its chares
668 [ + - ]: 194 : m_meshwriter[meshid].nchare( meshid, m_nchare[meshid] );
669 : :
670 [ + + ]: 194 : if (++m_nload == m_nelem.size()) { // all meshes have been loaded
671 : 192 : m_nload = 0;
672 [ + - ]: 384 : auto print = printer();
673 : :
674 : : // Start timer measuring preparation of the mesh for partitioning
675 [ + - ]: 192 : const auto& timer = tk::cref_find( m_timer, TimerTag::MESH_READ );
676 [ + - ][ + - ]: 192 : print.diag( "Mesh read time: " + std::to_string( timer.dsec() ) + " sec" );
[ + - ][ + - ]
[ + - ]
677 : :
678 : : // Print out mesh partitioning configuration
679 [ + - ][ + - ]: 192 : print.section( "Mesh partitioning" );
680 [ + - ]: 192 : print.Item< tk::ctr::PartitioningAlgorithm, tag::partitioning >();
681 [ + - ][ + - ]: 192 : print.item( "Virtualization [0.0...1.0]",
682 : 192 : g_inputdeck.get< tag::cmd, tag::virtualization >() );
683 : : // Print out initial mesh statistics
684 [ + - ][ + - ]: 192 : meshstat( "Initial load distribution" );
685 : :
686 : : // Query number of initial mesh refinement steps
687 : 192 : int nref = 0;
688 [ + + ]: 192 : if (g_inputdeck.get< tag::amr, tag::t0ref >())
689 : 20 : nref = static_cast<int>(g_inputdeck.get< tag::amr,
690 : 20 : tag::initial >().size());
691 : :
692 [ + - ][ + - ]: 384 : m_progMesh.start( print, "Preparing mesh", {{ CkNumPes(), CkNumPes(), nref,
693 : 192 : m_nchare[0], m_nchare[0], m_nchare[0], m_nchare[0] }} );
694 : :
695 : : // Partition first mesh
696 [ + - ]: 192 : m_partitioner[0].partition( m_nchare[0] );
697 : : }
698 : 194 : }
699 : :
700 : : void
701 : 194 : Transporter::partitioned( std::size_t meshid )
702 : : // *****************************************************************************
703 : : // Reduction target: a mesh has been partitioned
704 : : //! \param[in] meshid Mesh id
705 : : // *****************************************************************************
706 : : {
707 [ + + ]: 194 : if (++m_npart == m_nelem.size()) { // all meshes have been partitioned
708 : 192 : m_npart = 0;
709 : : } else { // partition next mesh
710 : 2 : m_partitioner[meshid+1].partition( m_nchare[meshid+1] );
711 : : }
712 : 194 : }
713 : :
714 : : void
715 : 194 : Transporter::distributed( std::size_t meshid )
716 : : // *****************************************************************************
717 : : // Reduction target: all compute nodes have distributed their mesh after
718 : : // partitioning
719 : : //! \param[in] meshid Mesh id
720 : : // *****************************************************************************
721 : : {
722 : 194 : m_partitioner[meshid].refine();
723 : 194 : }
724 : :
725 : : void
726 : 194 : Transporter::refinserted( std::size_t meshid, std::size_t error )
727 : : // *****************************************************************************
728 : : // Reduction target: all compute nodes have created the mesh refiners
729 : : //! \param[in] meshid Mesh id (aggregated across all compute nodes with operator
730 : : //! max)
731 : : //! \param[in] error Error code (aggregated across all compute nodes with
732 : : //! operator max)
733 : : // *****************************************************************************
734 : : {
735 [ - + ]: 194 : if (error) {
736 : :
737 : 0 : printer() << "\n>>> ERROR: A worker chare was not assigned any mesh "
738 [ - - ][ - - ]: 0 : "elements after distributing mesh " + std::to_string(meshid) +
[ - - ]
739 : : ". This can happen in SMP-mode with a large +ppn "
740 : : "parameter (number of worker threads per logical node) and is "
741 : : "most likely the fault of the mesh partitioning algorithm not "
742 : : "tolerating the case when it is asked to divide the "
743 : : "computational domain into a number of partitions different "
744 : : "than the number of ranks it is called on, i.e., in case of "
745 : : "overdecomposition and/or calling the partitioner in SMP mode "
746 : : "with +ppn larger than 1. Solution 1: Try a different "
747 : : "partitioning algorithm (e.g., rcb instead of mj). Solution 2: "
748 [ - - ]: 0 : "Decrease +ppn.";
749 : 0 : finish( meshid );
750 : :
751 : : } else {
752 : :
753 : 194 : m_refiner[meshid].doneInserting();
754 : :
755 : : }
756 : 194 : }
757 : :
758 : : void
759 : 87 : Transporter::queriedRef( std::size_t meshid )
760 : : // *****************************************************************************
761 : : // Reduction target: all Refiner chares have queried their boundary edges
762 : : //! \param[in] meshid Mesh id
763 : : // *****************************************************************************
764 : : {
765 : 87 : m_refiner[meshid].response();
766 : 87 : }
767 : :
768 : : void
769 : 87 : Transporter::respondedRef( std::size_t meshid )
770 : : // *****************************************************************************
771 : : // Reduction target: all Refiner chares have setup their boundary edges
772 : : //! \param[in] meshid Mesh id
773 : : // *****************************************************************************
774 : : {
775 : 87 : m_refiner[meshid].refine();
776 : 87 : }
777 : :
778 : : void
779 : 48 : Transporter::compatibility( std::size_t meshid )
780 : : // *****************************************************************************
781 : : // Reduction target: all Refiner chares have received a round of edges,
782 : : // and have run their compatibility algorithm
783 : : //! \param[in] meshid Mesh id (aggregated across all chares using operator max)
784 : : //! \details This is called iteratively, until convergence by Refiner. At this
785 : : //! point all Refiner chares have received a round of edge data (tags whether
786 : : //! an edge needs to be refined, etc.), and applied the compatibility
787 : : //! algorithm independent of other Refiner chares. We keep going until the
788 : : //! mesh is no longer modified by the compatibility algorithm, based on a new
789 : : //! round of edge data communication started in Refiner::comExtra().
790 : : // *****************************************************************************
791 : : {
792 : 48 : m_refiner[meshid].correctref();
793 : 48 : }
794 : :
795 : : void
796 : 89 : Transporter::matched( std::size_t summeshid,
797 : : std::size_t nextra,
798 : : std::size_t nref,
799 : : std::size_t nderef,
800 : : std::size_t sumrefmode )
801 : : // *****************************************************************************
802 : : // Reduction target: all Refiner chares have matched/corrected the tagging
803 : : // of chare-boundary edges, all chares are ready to perform refinement.
804 : : //! \param[in] summeshid Mesh id (summed across all chares)
805 : : //! \param[in] nextra Sum (across all chares) of the number of edges on each
806 : : //! chare that need correction along chare boundaries
807 : : //! \param[in] nref Sum of number of refined tetrahedra across all chares.
808 : : //! \param[in] nderef Sum of number of derefined tetrahedra across all chares.
809 : : //! \param[in] sumrefmode Sum of contributions from all chares, encoding
810 : : //! refinement mode of operation.
811 : : // *****************************************************************************
812 : : {
813 : 89 : auto meshid = tk::cref_find( m_meshid, summeshid );
814 : :
815 : : // If at least a single edge on a chare still needs correction, do correction,
816 : : // otherwise, this mesh refinement step is complete
817 [ + + ]: 89 : if (nextra > 0) {
818 : :
819 : 2 : ++m_ncit[meshid];
820 : 2 : m_refiner[meshid].comExtra();
821 : :
822 : : } else {
823 : :
824 [ + - ]: 174 : auto print = printer();
825 : :
826 : : // decode refmode
827 : : auto refmode = static_cast< Refiner::RefMode >(
828 : 87 : sumrefmode / static_cast<std::size_t>(m_nchare[meshid]) );
829 : :
830 [ + + ]: 87 : if (refmode == Refiner::RefMode::T0REF) {
831 : :
832 [ + - ]: 57 : if (!g_inputdeck.get< tag::cmd, tag::feedback >()) {
833 [ + - ]: 57 : ctr::AMRInitial opt;
834 [ + - ][ + - ]: 855 : print.diag( { "meshid", "t0ref", "type", "nref", "nderef", "ncorr" },
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + + ]
[ + + ][ - - ]
[ - - ]
835 : : { std::to_string(meshid),
836 : 57 : std::to_string(m_nt0refit[meshid]),
837 : : "initial",
838 : : std::to_string(nref),
839 : : std::to_string(nderef),
840 : 741 : std::to_string(m_ncit[meshid]) } );
841 : 57 : ++m_nt0refit[meshid];
842 : : }
843 [ + - ]: 57 : m_progMesh.inc< REFINE >( print );
844 : :
845 [ - + ]: 30 : } else if (refmode == Refiner::RefMode::DTREF) {
846 : :
847 : 0 : auto dtref_uni = g_inputdeck.get< tag::amr, tag::dtref_uniform >();
848 [ - - ][ - - ]: 0 : print.diag( { "meshid", "dtref", "type", "nref", "nderef", "ncorr" },
[ - - ][ - - ]
[ - - ][ - - ]
[ - - ][ - - ]
[ - - ][ - - ]
[ - - ][ - - ]
[ - - ][ - - ]
[ - - ][ - - ]
[ - - ][ - - ]
[ - - ][ - - ]
849 : : { std::to_string(meshid),
850 : 0 : std::to_string(++m_ndtrefit[meshid]),
851 : : (dtref_uni?"uniform":"error"),
852 : : std::to_string(nref),
853 : : std::to_string(nderef),
854 : 0 : std::to_string(m_ncit[meshid]) },
855 : 0 : false );
856 : :
857 [ + + ]: 30 : } else if (refmode == Refiner::RefMode::OUTREF) {
858 : :
859 [ + - ][ + - ]: 195 : print.diag( { "meshid", "outref", "nref", "nderef", "ncorr" },
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + + ]
[ + + ][ - - ]
[ - - ]
860 : : { std::to_string(meshid),
861 : 15 : std::to_string(++m_noutrefit[meshid]),
862 : : std::to_string(nref),
863 : : std::to_string(nderef),
864 : 165 : std::to_string(m_ncit[meshid]) }, false );
865 : :
866 [ + - ]: 15 : } else if (refmode == Refiner::RefMode::OUTDEREF) {
867 : :
868 [ + - ][ + - ]: 195 : print.diag( { "meshid", "outderef", "nref", "nderef", "ncorr" },
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + + ]
[ + + ][ - - ]
[ - - ]
869 : : { std::to_string(meshid),
870 : 15 : std::to_string(++m_noutderefit[meshid]),
871 : : std::to_string(nref),
872 : : std::to_string(nderef),
873 : 15 : std::to_string(m_ncit[meshid]) },
874 : 150 : false );
875 : :
876 [ - - ][ - - ]: 0 : } else Throw( "RefMode not implemented" );
[ - - ]
877 : :
878 : 87 : m_ncit[meshid] = 0;
879 [ + - ]: 87 : m_refiner[meshid].perform();
880 : :
881 : : }
882 : 89 : }
883 : :
884 : : void
885 : 195 : Transporter::bndint( tk::real sx, tk::real sy, tk::real sz, tk::real cb,
886 : : tk::real summeshid )
887 : : // *****************************************************************************
888 : : // Compute surface integral across the whole problem and perform leak-test
889 : : //! \param[in] sx X component of vector summed
890 : : //! \param[in] sy Y component of vector summed
891 : : //! \param[in] sz Z component of vector summed
892 : : //! \param[in] cb Invoke callback if positive
893 : : //! \param[in] summeshid Mesh id (summed accross all chares)
894 : : //! \details This function aggregates partial surface integrals across the
895 : : //! boundary faces of the whole problem. After this global sum a
896 : : //! non-zero vector result indicates a leak, e.g., a hole in the boundary,
897 : : //! which indicates an error in the boundary face data structures used to
898 : : //! compute the partial surface integrals.
899 : : // *****************************************************************************
900 : : {
901 [ + - ]: 195 : auto meshid = tk::cref_find( m_meshid, static_cast<std::size_t>(summeshid) );
902 : :
903 [ + - ]: 390 : std::stringstream err;
904 [ + + ]: 195 : if (cb < 0.0) {
905 : : err << "Mesh boundary leaky after mesh refinement step; this is due to a "
906 : : "problem with updating the side sets used to specify boundary conditions "
907 [ + - ]: 87 : "on faces: ";
908 [ + - ]: 108 : } else if (cb > 0.0) {
909 : : err << "Mesh boundary leaky during initialization; this is due to "
910 : : "incorrect or incompletely specified boundary conditions for a given input "
911 [ + - ]: 108 : "mesh: ";
912 : : }
913 : :
914 : 195 : auto eps = 1.0e-10;
915 [ + - ][ + - ]: 195 : if (std::abs(sx) > eps || std::abs(sy) > eps || std::abs(sz) > eps) {
[ - + ][ - + ]
916 [ - - ][ - - ]: 0 : err << "Integral result must be a zero vector: " << std::setprecision(12) <<
917 [ - - ][ - - ]: 0 : std::abs(sx) << ", " << std::abs(sy) << ", " << std::abs(sz) <<
[ - - ][ - - ]
[ - - ]
918 [ - - ][ - - ]: 0 : ", eps = " << eps;
919 [ - - ][ - - ]: 0 : Throw( err.str() );
[ - - ]
920 : : }
921 : :
922 [ + + ][ + - ]: 195 : if (cb > 0.0) m_scheme[meshid].ghosts().resizeComm();
923 : 195 : }
924 : :
925 : : void
926 : 194 : Transporter::refined( std::size_t summeshid,
927 : : std::size_t nelem,
928 : : std::size_t npoin )
929 : : // *****************************************************************************
930 : : // Reduction target: all chares have refined their mesh
931 : : //! \param[in] summeshid Mesh id (summed accross all Refiner chares)
932 : : //! \param[in] nelem Total number of elements in mesh summed across the
933 : : //! distributed mesh
934 : : //! \param[in] npoin Total number of mesh points summed across the distributed
935 : : //! mesh. Note that in parallel this is larger than the number of points in
936 : : //! the mesh, because the boundary nodes are multi-counted. But we only need
937 : : //! an equal or larger than npoin for Sorter::setup, so this is okay.
938 : : // *****************************************************************************
939 : : {
940 : 194 : auto meshid = tk::cref_find( m_meshid, summeshid );
941 : :
942 : : // Store new number of elements for initially refined mesh
943 : 194 : m_nelem[meshid] = nelem;
944 : :
945 : 194 : m_sorter[meshid].doneInserting();
946 : 194 : m_sorter[meshid].setup( npoin );
947 : 194 : }
948 : :
949 : : void
950 : 194 : Transporter::queried( std::size_t meshid )
951 : : // *****************************************************************************
952 : : // Reduction target: all Sorter chares have queried their boundary edges
953 : : //! \param[in] meshid Mesh id
954 : : // *****************************************************************************
955 : : {
956 : 194 : m_sorter[meshid].response();
957 : 194 : }
958 : :
959 : : void
960 : 194 : Transporter::responded( std::size_t meshid )
961 : : // *****************************************************************************
962 : : // Reduction target: all Sorter chares have responded with their boundary edges
963 : : //! \param[in] meshid Mesh id
964 : : // *****************************************************************************
965 : : {
966 : 194 : m_sorter[meshid].start();
967 : 194 : }
968 : :
969 : : void
970 : 360 : Transporter::resized( std::size_t meshid )
971 : : // *****************************************************************************
972 : : // Reduction target: all worker chares have resized their own mesh data after
973 : : // AMR or ALE
974 : : //! \param[in] meshid Mesh id
975 : : //! \note Only used for nodal schemes
976 : : // *****************************************************************************
977 : : {
978 : 360 : m_scheme[meshid].disc().vol();
979 : 360 : m_scheme[meshid].bcast< Scheme::lhs >();
980 : 360 : }
981 : :
982 : : void
983 : 108 : Transporter::startEsup( std::size_t meshid )
984 : : // *****************************************************************************
985 : : // Reduction target: all worker chares have generated their own esup
986 : : //! \param[in] meshid Mesh id
987 : : //! \note Only used for cell-centered schemes
988 : : // *****************************************************************************
989 : : {
990 : 108 : m_scheme[meshid].ghosts().nodeNeighSetup();
991 : 108 : }
992 : :
993 : : void
994 : 194 : Transporter::discinserted( std::size_t meshid )
995 : : // *****************************************************************************
996 : : // Reduction target: all Discretization chares have been inserted
997 : : //! \param[in] meshid Mesh id
998 : : // *****************************************************************************
999 : : {
1000 : 194 : m_scheme[meshid].disc().doneInserting();
1001 : 194 : }
1002 : :
1003 : : void
1004 : 212 : Transporter::meshstat( const std::string& header ) const
1005 : : // *****************************************************************************
1006 : : // Print out mesh statistics
1007 : : //! \param[in] header Section header
1008 : : // *****************************************************************************
1009 : : {
1010 [ + - ]: 424 : auto print = printer();
1011 : :
1012 [ + - ]: 212 : print.section( header );
1013 : :
1014 [ + + ]: 212 : if (m_nelem.size() > 1) {
1015 [ + - ][ + - ]: 2 : print.item( "Number of tetrahedra (per mesh)",tk::parameters(m_nelem) );
[ + - ]
1016 [ + - ][ + - ]: 2 : print.item( "Number of points (per mesh)", tk::parameters(m_npoin) );
[ + - ]
1017 [ + - ][ + - ]: 2 : print.item( "Number of work units (per mesh)", tk::parameters(m_nchare) );
[ + - ]
1018 : : }
1019 : :
1020 [ + - ][ + - ]: 212 : print.item( "Total number of tetrahedra",
1021 : 212 : std::accumulate( begin(m_nelem), end(m_nelem), 0UL ) );
1022 [ + - ][ + - ]: 212 : print.item( "Total number of points",
1023 : 212 : std::accumulate( begin(m_npoin), end(m_npoin), 0UL ) );
1024 [ + - ][ + - ]: 212 : print.item( "Total number of work units",
1025 : 212 : std::accumulate( begin(m_nchare), end(m_nchare), 0 ) );
1026 : :
1027 [ + - ]: 212 : print.endsubsection();
1028 : 212 : }
1029 : :
1030 : : bool
1031 : 388 : Transporter::need_linearsolver() const
1032 : : // *****************************************************************************
1033 : : // Decide if we need a linear solver for ALE
1034 : : //! \return True if ALE will neeed a linear solver
1035 : : // *****************************************************************************
1036 : : {
1037 : 388 : auto smoother = g_inputdeck.get< tag::ale, tag::smoother >();
1038 : :
1039 [ + + ][ + + ]: 398 : if ( g_inputdeck.get< tag::ale, tag::ale >() and
[ + + ]
1040 [ + + ]: 10 : (smoother == ctr::MeshVelocitySmootherType::LAPLACE ||
1041 : : smoother == ctr::MeshVelocitySmootherType::HELMHOLTZ) )
1042 : : {
1043 : 12 : return true;
1044 : : } else {
1045 : 376 : return false;
1046 : : }
1047 : : }
1048 : :
1049 : : void
1050 : 194 : Transporter::disccreated( std::size_t summeshid, std::size_t npoin )
1051 : : // *****************************************************************************
1052 : : // Reduction target: all Discretization constructors have been called
1053 : : //! \param[in] summeshid Mesh id (summed accross all chares)
1054 : : //! \param[in] npoin Total number of mesh points (summed across all chares)
1055 : : //! Note that as opposed to npoin in refined(), this npoin is not
1056 : : //! multi-counted, and thus should be correct in parallel.
1057 : : // *****************************************************************************
1058 : : {
1059 : 194 : auto meshid = tk::cref_find( m_meshid, summeshid );
1060 : : //std::cout << "Trans: " << meshid << " Transporter::disccreated()\n";
1061 : :
1062 : : // Update number of mesh points for mesh, since it may have been refined
1063 [ + + ]: 194 : if (g_inputdeck.get< tag::amr, tag::t0ref >()) m_npoin[meshid] = npoin;
1064 : :
1065 [ + + ]: 194 : if (++m_ndisc == m_nelem.size()) { // all Disc arrays have been created
1066 : 192 : m_ndisc = 0;
1067 [ + - ]: 384 : auto print = printer();
1068 [ + - ]: 192 : m_progMesh.end( print );
1069 [ + + ]: 192 : if (g_inputdeck.get< tag::amr, tag::t0ref >())
1070 [ + - ][ + - ]: 20 : meshstat( "Initially (t<0) refined mesh graph statistics" );
1071 : : }
1072 : :
1073 : 194 : m_refiner[meshid].sendProxy();
1074 : :
1075 [ + + ]: 194 : if (g_inputdeck.get< tag::ale, tag::ale >())
1076 : 10 : m_scheme[meshid].ale().doneInserting();
1077 : :
1078 [ + + ]: 194 : if (need_linearsolver())
1079 : 6 : m_scheme[meshid].conjugategradients().doneInserting();
1080 : :
1081 : 194 : m_scheme[meshid].disc().vol();
1082 : 194 : }
1083 : :
1084 : : void
1085 : 194 : Transporter::workinserted( std::size_t meshid )
1086 : : // *****************************************************************************
1087 : : // Reduction target: all worker (derived discretization) chares have been
1088 : : // inserted
1089 : : //! \param[in] meshid Mesh id
1090 : : // *****************************************************************************
1091 : : {
1092 : 194 : m_scheme[meshid].bcast< Scheme::doneInserting >();
1093 : 194 : }
1094 : :
1095 : : void
1096 : 192 : Transporter::diagHeader()
1097 : : // *****************************************************************************
1098 : : // Configure and write diagnostics file header
1099 : : // *****************************************************************************
1100 : : {
1101 [ + + ]: 386 : for (std::size_t m=0; m<m_input.size(); ++m) {
1102 : :
1103 : : // Output header for diagnostics output file
1104 [ + + ][ + - ]: 578 : auto mid = m_input.size() > 1 ? std::string( '.' + std::to_string(m) ) : "";
[ + - ][ + - ]
[ + + ][ + + ]
[ - - ][ - - ]
1105 [ + - ]: 194 : tk::DiagWriter dw( g_inputdeck.get< tag::cmd, tag::io, tag::diag >() + mid,
1106 : 194 : g_inputdeck.get< tag::diagnostics, tag::format >(),
1107 [ + - ]: 582 : g_inputdeck.get< tag::diagnostics, tag::precision >() );
1108 : :
1109 : : // Collect variables names for integral/diagnostics output
1110 : 388 : std::vector< std::string > var;
1111 : 194 : const auto scheme = g_inputdeck.get< tag::scheme >();
1112 [ + + ][ + + ]: 194 : if ( scheme == ctr::SchemeType::ALECG ||
1113 : : scheme == ctr::SchemeType::OversetFE )
1114 [ + + ][ + - ]: 176 : for (const auto& eq : g_cgpde) varnames( eq, var );
1115 [ + + ][ + + ]: 108 : else if ( scheme == ctr::SchemeType::DG ||
1116 [ + + ]: 57 : scheme == ctr::SchemeType::P0P1 ||
1117 [ + + ]: 40 : scheme == ctr::SchemeType::DGP1 ||
1118 [ + + ]: 32 : scheme == ctr::SchemeType::DGP2 ||
1119 : : scheme == ctr::SchemeType::PDG )
1120 [ + + ][ + - ]: 176 : for (const auto& eq : g_dgpde) varnames( eq, var );
1121 [ + - ]: 20 : else if (scheme == ctr::SchemeType::FV)
1122 [ + + ][ + - ]: 40 : for (const auto& eq : g_fvpde) varnames( eq, var );
1123 [ - - ][ - - ]: 0 : else Throw( "Diagnostics header not handled for discretization scheme" );
[ - - ]
1124 : :
1125 [ + - ]: 388 : const tk::ctr::Error opt;
1126 : 194 : auto nv = var.size() / m_input.size();
1127 : 388 : std::vector< std::string > d;
1128 : :
1129 : : // Add 'L2(var)' for all variables as those are always computed
1130 [ + - ]: 194 : const auto& l2name = opt.name( tk::ctr::ErrorType::L2 );
1131 [ + + ][ + - ]: 995 : for (std::size_t i=0; i<nv; ++i) d.push_back( l2name + '(' + var[i] + ')' );
[ + - ][ + - ]
[ + - ]
1132 : :
1133 : : // Query user-requested diagnostics and augment diagnostics file header by
1134 : : // 'err(var)', where 'err' is the error type configured, and var is the
1135 : : // variable computed, for all variables and all error types configured.
1136 : 194 : const auto& err = g_inputdeck.get< tag::diagnostics, tag::error >();
1137 [ + - ]: 194 : const auto& errname = opt.name( err );
1138 [ + + ]: 995 : for (std::size_t i=0; i<nv; ++i)
1139 [ + - ][ + - ]: 801 : d.push_back( errname + '(' + var[i] + "-IC)" );
[ + - ][ + - ]
1140 : :
1141 : : // Augment diagnostics variables by L2-norm of the residual and total energy
1142 [ + + ][ + + ]: 194 : if ( scheme == ctr::SchemeType::ALECG ||
1143 [ + + ]: 108 : scheme == ctr::SchemeType::OversetFE ||
1144 : : scheme == ctr::SchemeType::FV )
1145 : : {
1146 [ + + ][ + - ]: 562 : for (std::size_t i=0; i<nv; ++i) d.push_back( "L2(d" + var[i] + ')' );
[ + - ][ + - ]
1147 : : }
1148 [ + - ][ + - ]: 194 : d.push_back( "mE" );
1149 : :
1150 : : // Write diagnostics header
1151 [ + - ]: 194 : dw.header( d );
1152 : :
1153 : : }
1154 : 192 : }
1155 : :
1156 : : void
1157 : 108 : Transporter::doneInsertingGhosts(std::size_t meshid)
1158 : : // *****************************************************************************
1159 : : // Reduction target indicating all "ghosts" insertions are done
1160 : : //! \param[in] meshid Mesh id
1161 : : // *****************************************************************************
1162 : : {
1163 : 108 : m_scheme[meshid].ghosts().doneInserting();
1164 : 108 : m_scheme[meshid].ghosts().startCommSetup();
1165 : 108 : }
1166 : :
1167 : : void
1168 : 194 : Transporter::comfinal( std::size_t initial, std::size_t summeshid )
1169 : : // *****************************************************************************
1170 : : // Reduction target indicating that communication maps have been setup
1171 : : //! \param[in] initial Sum of contributions from all chares. If larger than
1172 : : //! zero, we are during time stepping and if zero we are during setup.
1173 : : //! \param[in] summeshid Mesh id (summed accross the distributed mesh)
1174 : : // *****************************************************************************
1175 : : // [Discretization-specific communication maps]
1176 : : {
1177 [ + - ]: 194 : auto meshid = tk::cref_find( m_meshid, static_cast<std::size_t>(summeshid) );
1178 : :
1179 [ + - ]: 194 : if (initial > 0) {
1180 : 194 : m_scheme[meshid].bcast< Scheme::setup >();
1181 : : // Turn on automatic load balancing
1182 [ + + ]: 194 : if (++m_ncom == m_nelem.size()) { // all worker arrays have finished
1183 : 192 : m_ncom = 0;
1184 [ + - ]: 192 : auto print = printer();
1185 [ + - ]: 192 : m_progWork.end( print );
1186 [ + - ]: 192 : tk::CProxy_LBSwitch::ckNew();
1187 [ + - ][ + - ]: 192 : print.diag( "Load balancing on (if enabled in Charm++)" );
1188 : : }
1189 : : } else {
1190 : 0 : m_scheme[meshid].bcast< Scheme::lhs >();
1191 : : }
1192 : 194 : }
1193 : : // [Discretization-specific communication maps]
1194 : :
1195 : : void
1196 : 554 : Transporter::totalvol( tk::real v, tk::real initial, tk::real summeshid )
1197 : : // *****************************************************************************
1198 : : // Reduction target summing total mesh volume across all workers
1199 : : //! \param[in] v Mesh volume summed across the distributed mesh
1200 : : //! \param[in] initial Sum of contributions from all chares. If larger than
1201 : : //! zero, we are during setup, if zero, during time stepping.
1202 : : //! \param[in] summeshid Mesh id (summed accross the distributed mesh)
1203 : : // *****************************************************************************
1204 : : {
1205 [ + - ]: 554 : auto meshid = tk::cref_find( m_meshid, static_cast<std::size_t>(summeshid) );
1206 : :
1207 : 554 : m_meshvol[meshid] = v;
1208 : :
1209 [ + + ]: 554 : if (initial > 0.0) // during initialization
1210 : 194 : m_scheme[meshid].disc().stat( v );
1211 : : else // during ALE or AMR
1212 : 360 : m_scheme[meshid].bcast< Scheme::resized >();
1213 : 554 : }
1214 : :
1215 : : void
1216 : 194 : Transporter::minstat( tk::real d0, tk::real d1, tk::real d2, tk::real rmeshid )
1217 : : // *****************************************************************************
1218 : : // Reduction target yielding minimum mesh statistcs across all workers
1219 : : //! \param[in] d0 Minimum mesh statistics collected over all chares
1220 : : //! \param[in] d1 Minimum mesh statistics collected over all chares
1221 : : //! \param[in] d2 Minimum mesh statistics collected over all chares
1222 : : //! \param[in] rmeshid Mesh id as a real
1223 : : // *****************************************************************************
1224 : : {
1225 : 194 : auto meshid = static_cast<std::size_t>(rmeshid);
1226 : :
1227 : 194 : m_minstat[meshid][0] = d0; // minimum edge length
1228 : 194 : m_minstat[meshid][1] = d1; // minimum cell volume cubic root
1229 : 194 : m_minstat[meshid][2] = d2; // minimum number of cells on chare
1230 : :
1231 : 194 : minstat_complete(meshid);
1232 : 194 : }
1233 : :
1234 : : void
1235 : 194 : Transporter::maxstat( tk::real d0, tk::real d1, tk::real d2, tk::real rmeshid )
1236 : : // *****************************************************************************
1237 : : // Reduction target yielding the maximum mesh statistics across all workers
1238 : : //! \param[in] d0 Maximum mesh statistics collected over all chares
1239 : : //! \param[in] d1 Maximum mesh statistics collected over all chares
1240 : : //! \param[in] d2 Maximum mesh statistics collected over all chares
1241 : : //! \param[in] rmeshid Mesh id as a real
1242 : : // *****************************************************************************
1243 : : {
1244 : 194 : auto meshid = static_cast<std::size_t>(rmeshid);
1245 : :
1246 : 194 : m_maxstat[meshid][0] = d0; // maximum edge length
1247 : 194 : m_maxstat[meshid][1] = d1; // maximum cell volume cubic root
1248 : 194 : m_maxstat[meshid][2] = d2; // maximum number of cells on chare
1249 : :
1250 : 194 : maxstat_complete(meshid);
1251 : 194 : }
1252 : :
1253 : : void
1254 : 194 : Transporter::sumstat( tk::real d0, tk::real d1, tk::real d2, tk::real d3,
1255 : : tk::real d4, tk::real d5, tk::real summeshid )
1256 : : // *****************************************************************************
1257 : : // Reduction target yielding the sum mesh statistics across all workers
1258 : : //! \param[in] d0 Sum mesh statistics collected over all chares
1259 : : //! \param[in] d1 Sum mesh statistics collected over all chares
1260 : : //! \param[in] d2 Sum mesh statistics collected over all chares
1261 : : //! \param[in] d3 Sum mesh statistics collected over all chares
1262 : : //! \param[in] d4 Sum mesh statistics collected over all chares
1263 : : //! \param[in] d5 Sum mesh statistics collected over all chares
1264 : : //! \param[in] summeshid Mesh id (summed accross the distributed mesh)
1265 : : // *****************************************************************************
1266 : : {
1267 [ + - ]: 194 : auto meshid = tk::cref_find( m_meshid, static_cast<std::size_t>(summeshid) );
1268 : :
1269 : 194 : m_avgstat[meshid][0] = d1 / d0; // average edge length
1270 : 194 : m_avgstat[meshid][1] = d3 / d2; // average cell volume cubic root
1271 : 194 : m_avgstat[meshid][2] = d5 / d4; // average number of cells per chare
1272 : :
1273 : 194 : sumstat_complete(meshid);
1274 : 194 : }
1275 : :
1276 : : void
1277 : 194 : Transporter::pdfstat( CkReductionMsg* msg )
1278 : : // *****************************************************************************
1279 : : // Reduction target yielding PDF of mesh statistics across all workers
1280 : : //! \param[in] msg Serialized PDF
1281 : : // *****************************************************************************
1282 : : {
1283 : : std::size_t meshid;
1284 : 388 : std::vector< tk::UniPDF > pdf;
1285 : :
1286 : : // Deserialize final PDF
1287 [ + - ]: 388 : PUP::fromMem creator( msg->getData() );
1288 [ + - ]: 194 : creator | meshid;
1289 [ + - ]: 194 : creator | pdf;
1290 [ + - ][ + - ]: 194 : delete msg;
1291 : :
1292 [ + - ]: 388 : auto id = std::to_string(meshid);
1293 : :
1294 : : // Create new PDF file (overwrite if exists)
1295 [ + - ][ + - ]: 582 : tk::PDFWriter pdfe( "mesh_edge_pdf." + id + ".txt" );
[ + - ]
1296 : : // Output edgelength PDF
1297 : : // cppcheck-suppress containerOutOfBounds
1298 [ + - ]: 194 : pdfe.writeTxt( pdf[0],
1299 [ + - ][ + - ]: 582 : tk::ctr::PDFInfo{ {"PDF"}, {}, {"edgelength"}, 0, 0.0 } );
[ + - ][ + + ]
[ - - ]
1300 : :
1301 : : // Create new PDF file (overwrite if exists)
1302 [ + - ][ + - ]: 582 : tk::PDFWriter pdfv( "mesh_vol_pdf." + id + ".txt" );
[ + - ]
1303 : : // Output cell volume cubic root PDF
1304 [ + - ]: 194 : pdfv.writeTxt( pdf[1],
1305 [ + - ][ + - ]: 582 : tk::ctr::PDFInfo{ {"PDF"}, {}, {"V^{1/3}"}, 0, 0.0 } );
[ + - ][ + + ]
[ - - ]
1306 : :
1307 : : // Create new PDF file (overwrite if exists)
1308 [ + - ][ + - ]: 582 : tk::PDFWriter pdfn( "mesh_ntet_pdf." + id + ".txt" );
[ + - ]
1309 : : // Output number of cells PDF
1310 [ + - ]: 194 : pdfn.writeTxt( pdf[2],
1311 [ + - ][ + - ]: 582 : tk::ctr::PDFInfo{ {"PDF"}, {}, {"ntets"}, 0, 0.0 } );
[ + - ][ + + ]
[ - - ]
1312 : :
1313 [ + - ]: 194 : pdfstat_complete(meshid);
1314 : 194 : }
1315 : :
1316 : : void
1317 : 194 : Transporter::stat()
1318 : : // *****************************************************************************
1319 : : // Echo diagnostics on mesh statistics
1320 : : // *****************************************************************************
1321 : : {
1322 [ + - ]: 388 : auto print = printer();
1323 : :
1324 [ + + ]: 194 : if (++m_nstat == m_nelem.size()) { // stats from all meshes have arrived
1325 : 192 : m_nstat = 0;
1326 [ + + ]: 386 : for (std::size_t i=0; i<m_nelem.size(); ++i) {
1327 [ + - ]: 194 : print.diag(
1328 [ + - ][ + - ]: 388 : "Mesh " + std::to_string(i) +
[ + - ]
1329 [ + - ]: 388 : " distribution statistics: min/max/avg(edgelength) = " +
1330 [ + - ][ + - ]: 776 : std::to_string( m_minstat[i][0] ) + " / " +
[ + - ]
1331 [ + - ][ + - ]: 776 : std::to_string( m_maxstat[i][0] ) + " / " +
[ + - ]
1332 [ + - ][ + - ]: 776 : std::to_string( m_avgstat[i][0] ) + ", " +
[ + - ]
1333 [ + - ]: 388 : "min/max/avg(V^{1/3}) = " +
1334 [ + - ][ + - ]: 776 : std::to_string( m_minstat[i][1] ) + " / " +
[ + - ]
1335 [ + - ][ + - ]: 776 : std::to_string( m_maxstat[i][1] ) + " / " +
[ + - ]
1336 [ + - ][ + - ]: 776 : std::to_string( m_avgstat[i][1] ) + ", " +
[ + - ]
1337 [ + - ]: 388 : "min/max/avg(ntets) = " +
1338 [ + - ][ + - ]: 776 : std::to_string( static_cast<std::size_t>(m_minstat[i][2]) ) + " / " +
[ + - ]
1339 [ + - ][ + - ]: 776 : std::to_string( static_cast<std::size_t>(m_maxstat[i][2]) ) + " / " +
[ + - ]
1340 [ + - ]: 388 : std::to_string( static_cast<std::size_t>(m_avgstat[i][2]) ) );
1341 : : }
1342 : :
1343 : : // Print out time integration header to screen
1344 [ + - ]: 192 : inthead( print );
1345 : :
1346 [ + - ][ + - ]: 192 : m_progWork.start( print, "Preparing workers",
1347 : 192 : {{ m_nchare[0], m_nchare[0], m_nchare[0], m_nchare[0], m_nchare[0] }} );
1348 : :
1349 : : // Create "derived-class" workers
1350 [ + + ][ + - ]: 386 : for (std::size_t i=0; i<m_nelem.size(); ++i) m_sorter[i].createWorkers();
1351 : : }
1352 : 194 : }
1353 : :
1354 : : void
1355 : 194 : Transporter::boxvol( tk::real* meshdata, int n )
1356 : : // *****************************************************************************
1357 : : // Reduction target computing total volume of IC mesh blocks and box
1358 : : //! \param[in] meshdata Vector containing volumes of all IC mesh blocks,
1359 : : //! volume of IC box, and mesh id as a real summed across the distributed mesh
1360 : : //! \param[in] n Size of vector, automatically computed by Charm
1361 : : // *****************************************************************************
1362 : : {
1363 [ - + ][ - - ]: 194 : Assert(n>=2, "mesh data size incorrect");
[ - - ][ - - ]
1364 : :
1365 : : // extract summed mesh id from vector
1366 : 194 : tk::real summeshid = meshdata[n-1];
1367 [ + - ]: 194 : auto meshid = tk::cref_find( m_meshid, static_cast<std::size_t>(summeshid) );
1368 : :
1369 : : // extract summed box volume from vector
1370 : 194 : tk::real v = meshdata[n-2];
1371 [ + + ][ + - ]: 194 : if (v > 0.0) printer().diag( "Box IC volume: " + std::to_string(v) );
[ + - ][ + - ]
[ + - ]
1372 : :
1373 : : // extract summed mesh block volumes from the vector
1374 : 388 : std::vector< tk::real > blockvols;
1375 [ - + ]: 194 : for (std::size_t blid=0; blid<(static_cast<std::size_t>(n)-2); ++blid) {
1376 [ - - ]: 0 : blockvols.push_back(meshdata[blid]);
1377 [ - - ]: 0 : if (blockvols[blid] > 0.0)
1378 [ - - ][ - - ]: 0 : printer().diag( "Mesh block " + std::to_string(blid) +
[ - - ][ - - ]
[ - - ]
1379 [ - - ][ - - ]: 0 : " discrete volume: " + std::to_string(blockvols[blid]) );
1380 : : }
1381 : :
1382 [ + - ]: 194 : m_scheme[meshid].bcast< Scheme::box >( v, blockvols );
1383 : 194 : }
1384 : :
1385 : : void
1386 : 310 : Transporter::solutionTransferred()
1387 : : // *****************************************************************************
1388 : : // Reduction target broadcasting to Schemes after mesh transfer
1389 : : // *****************************************************************************
1390 : : {
1391 [ + + ]: 310 : if (++m_ntrans == m_nelem.size()) { // all meshes have been loaded
1392 : 155 : m_ntrans = 0;
1393 [ + + ][ + - ]: 465 : for (auto& m : m_scheme) m.bcast< Scheme::transferSol >();
1394 : : }
1395 : 310 : }
1396 : :
1397 : : void
1398 : 352 : Transporter::minDtAcrossMeshes( tk::real* reducndata, [[maybe_unused]] int n )
1399 : : // *****************************************************************************
1400 : : // Reduction target that computes minimum timestep across all meshes
1401 : : //! \param[in] reducndata Vector containing minimum values of dt and mesh-moved
1402 : : //! flags, collected across all meshes
1403 : : //! \param[in] n Size of vector, automatically computed by Charm
1404 : : // *****************************************************************************
1405 : : {
1406 [ - + ][ - - ]: 352 : Assert(static_cast<std::size_t>(n-1) == m_nelem.size(),
[ - - ][ - - ]
1407 : : "Incorrectly sized reduction vector");
1408 : 352 : m_dtmsh.push_back(reducndata[0]);
1409 : :
1410 [ + + ]: 352 : if (++m_ndtmsh == m_nelem.size()) { // all meshes have been loaded
1411 [ - + ][ - - ]: 301 : Assert(m_dtmsh.size() == m_nelem.size(), "Incorrect size of dtmsh");
[ - - ][ - - ]
1412 : :
1413 : : // compute minimum dt across meshes
1414 : 301 : tk::real dt = std::numeric_limits< tk::real >::max();
1415 [ + + ]: 653 : for (auto idt : m_dtmsh) dt = std::min(dt, idt);
1416 : :
1417 : : // clear dt-vector and counter
1418 : 301 : m_dtmsh.clear();
1419 : 301 : m_ndtmsh = 0;
1420 : :
1421 : : // broadcast to advance time step
1422 : 301 : std::size_t ic(0);
1423 [ + + ]: 653 : for (auto& m : m_scheme) {
1424 [ + - ]: 352 : m.bcast< Scheme::advance >( dt, reducndata[ic+1] );
1425 : 352 : ++ic;
1426 : : }
1427 : : }
1428 : 352 : }
1429 : :
1430 : : void
1431 : 194 : Transporter::inthead( const InciterPrint& print )
1432 : : // *****************************************************************************
1433 : : // Print out time integration header to screen
1434 : : //! \param[in] print Pretty printer object to use for printing
1435 : : // *****************************************************************************
1436 : : {
1437 : 194 : auto refined = g_inputdeck.get< tag::field_output, tag::refined >();
1438 : 194 : const auto scheme = g_inputdeck.get< tag::scheme >();
1439 [ + + ][ - + ]: 194 : if (refined && scheme == ctr::SchemeType::DG) {
1440 [ - - ]: 0 : printer() << "\n>>> WARNING: Ignoring refined field output for DG(P0)\n\n";
1441 : 0 : refined = false;
1442 : : }
1443 : :
1444 [ + - ][ + - ]: 582 : print.inthead( "Time integration", "Euler/Navier-Stokes solver",
[ + - ][ + - ]
1445 : : "Legend: it - iteration count\n"
1446 : : " t - physics time\n"
1447 : : " dt - physics time step size\n"
1448 : : " ETE - estimated wall-clock time elapsed (h:m:s)\n"
1449 : : " ETA - estimated wall-clock time for accomplishment (h:m:s)\n"
1450 : : " EGT - estimated grind wall-clock time (ms/timestep)\n"
1451 : : " flg - status flags, legend:\n"
1452 [ + + ][ + - ]: 388 : " f - " + std::string(refined ? "refined " : "")
[ + - ]
1453 [ + - ]: 388 : + "field (volume and surface)\n"
1454 : : " d - diagnostics\n"
1455 : : " t - physics time history\n"
1456 : : " h - h-refinement\n"
1457 : : " l - load balancing\n"
1458 : : " r - checkpoint\n"
1459 : : " a - ALE mesh velocity linear solver did not converge\n",
1460 : : "\n it t dt ETE ETA EGT flg\n"
1461 : : " -------------------------------------------------------------------------\n" );
1462 : 194 : }
1463 : :
1464 : : void
1465 : 3325 : Transporter::diagnostics( CkReductionMsg* msg )
1466 : : // *****************************************************************************
1467 : : // Reduction target optionally collecting diagnostics, e.g., residuals
1468 : : //! \param[in] msg Serialized diagnostics vector aggregated across all PEs
1469 : : //! \note Only used for nodal schemes
1470 : : // *****************************************************************************
1471 : : {
1472 : : std::size_t meshid, ncomp;
1473 : 6650 : std::vector< std::vector< tk::real > > d;
1474 : :
1475 : : // Deserialize diagnostics vector
1476 [ + - ]: 6650 : PUP::fromMem creator( msg->getData() );
1477 [ + - ]: 3325 : creator | meshid;
1478 [ + - ]: 3325 : creator | ncomp;
1479 [ + - ]: 3325 : creator | d;
1480 [ + - ][ + - ]: 3325 : delete msg;
1481 : :
1482 [ + - ]: 6650 : auto id = std::to_string(meshid);
1483 : :
1484 [ - + ][ - - ]: 3325 : Assert( ncomp > 0, "Number of scalar components must be positive");
[ - - ][ - - ]
1485 [ - + ][ - - ]: 3325 : Assert( d.size() == NUMDIAG, "Diagnostics vector size mismatch" );
[ - - ][ - - ]
1486 : :
1487 [ + + ]: 29925 : for (std::size_t i=0; i<d.size(); ++i)
1488 [ - + ][ - - ]: 26600 : Assert( d[i].size() == ncomp, "Size mismatch at final stage of "
[ - - ][ - - ]
1489 : : "diagnostics aggregation for mesh " + id );
1490 : :
1491 : : // Allocate storage for those diagnostics that are always computed
1492 [ + - ]: 6650 : std::vector< tk::real > diag( ncomp, 0.0 );
1493 : :
1494 : : // Finish computing diagnostics
1495 [ + + ]: 18157 : for (std::size_t i=0; i<d[L2SOL].size(); ++i)
1496 : 14832 : diag[i] = sqrt( d[L2SOL][i] / m_meshvol[meshid] );
1497 : :
1498 : : // Query user-requested error types to output
1499 : 3325 : const auto& error = g_inputdeck.get< tag::diagnostics, tag::error >();
1500 : :
1501 : 3325 : decltype(ncomp) n = 0;
1502 : 3325 : n += ncomp;
1503 [ + + ]: 3325 : if (error == tk::ctr::ErrorType::L2) {
1504 : : // Finish computing the L2 norm of the numerical - analytical solution
1505 [ + + ]: 18133 : for (std::size_t i=0; i<d[L2ERR].size(); ++i)
1506 [ + - ]: 14820 : diag.push_back( sqrt( d[L2ERR][i] / m_meshvol[meshid] ) );
1507 [ + - ]: 12 : } else if (error == tk::ctr::ErrorType::LINF) {
1508 : : // Finish computing the Linf norm of the numerical - analytical solution
1509 [ + + ]: 24 : for (std::size_t i=0; i<d[LINFERR].size(); ++i)
1510 [ + - ]: 12 : diag.push_back( d[LINFERR][i] );
1511 : : }
1512 : :
1513 : : // Finish computing the L2 norm of the residual and append
1514 : 3325 : const auto scheme = g_inputdeck.get< tag::scheme >();
1515 [ + - ]: 6650 : std::vector< tk::real > l2res( d[L2RES].size(), 0.0 );
1516 [ + + ][ + + ]: 3325 : if (scheme == ctr::SchemeType::ALECG || scheme == ctr::SchemeType::OversetFE) {
1517 [ + + ]: 6514 : for (std::size_t i=0; i<d[L2RES].size(); ++i) {
1518 : 5029 : l2res[i] = std::sqrt( d[L2RES][i] / m_meshvol[meshid] );
1519 [ + - ]: 5029 : diag.push_back( l2res[i] );
1520 : 1485 : }
1521 : : }
1522 [ + + ]: 1840 : else if (scheme == ctr::SchemeType::FV) {
1523 [ + + ]: 968 : for (std::size_t i=0; i<d[L2RES].size(); ++i) {
1524 : 882 : l2res[i] = std::sqrt( d[L2RES][i] );
1525 [ + - ]: 882 : diag.push_back( l2res[i] );
1526 : : }
1527 : : }
1528 : :
1529 : : // Append total energy
1530 [ + - ]: 3325 : diag.push_back( d[TOTALSOL][0] );
1531 : :
1532 : : // Append diagnostics file at selected times
1533 [ + - ]: 6650 : auto filename = g_inputdeck.get< tag::cmd, tag::io, tag::diag >();
1534 [ + + ][ + - ]: 3325 : if (m_nelem.size() > 1) filename += '.' + id;
[ + - ]
1535 : : tk::DiagWriter dw( filename,
1536 : 3325 : g_inputdeck.get< tag::diagnostics, tag::format >(),
1537 : 3325 : g_inputdeck.get< tag::diagnostics, tag::precision >(),
1538 [ + - ]: 6650 : std::ios_base::app );
1539 [ + - ]: 3325 : dw.diag( static_cast<uint64_t>(d[ITER][0]), d[TIME][0], d[DT][0], diag );
1540 : :
1541 : : // Continue time step
1542 [ + - ]: 3325 : m_scheme[meshid].bcast< Scheme::refine >( l2res );
1543 : 3325 : }
1544 : :
1545 : : void
1546 : 196 : Transporter::resume()
1547 : : // *****************************************************************************
1548 : : // Resume execution from checkpoint/restart files
1549 : : //! \details This is invoked by Charm++ after the checkpoint is done, as well as
1550 : : //! when the restart (returning from a checkpoint) is complete
1551 : : // *****************************************************************************
1552 : : {
1553 [ + + ]: 394 : if (std::any_of(begin(m_finished), end(m_finished), [](auto f){return !f;})) {
1554 : : // If just restarted from a checkpoint, Main( CkMigrateMessage* msg ) has
1555 : : // increased nrestart in g_inputdeck, but only on PE 0, so broadcast.
1556 : 2 : auto nrestart = g_inputdeck.get< tag::cmd, tag::io, tag::nrestart >();
1557 [ + + ]: 4 : for (std::size_t i=0; i<m_nelem.size(); ++i)
1558 [ + - ]: 2 : m_scheme[i].bcast< Scheme::evalLB >( nrestart );
1559 : : } else
1560 : 194 : mainProxy.finalize();
1561 : 196 : }
1562 : :
1563 : : void
1564 : 196 : Transporter::checkpoint( std::size_t finished, std::size_t meshid )
1565 : : // *****************************************************************************
1566 : : // Save checkpoint/restart files
1567 : : //! \param[in] finished Nonzero if finished with time stepping
1568 : : //! \param[in] meshid Mesh id
1569 : : // *****************************************************************************
1570 : : {
1571 : 196 : m_finished[meshid] = finished;
1572 : :
1573 [ + + ]: 196 : if (++m_nchk == m_nelem.size()) { // all worker arrays have checkpointed
1574 : 194 : m_nchk = 0;
1575 [ + + ]: 194 : if (not g_inputdeck.get< tag::cmd, tag::benchmark >()) {
1576 : 122 : const auto& restart = g_inputdeck.get< tag::cmd, tag::io, tag::restart >();
1577 [ + - ][ + - ]: 244 : CkCallback res( CkIndex_Transporter::resume(), thisProxy );
1578 [ + - ]: 122 : CkStartCheckpoint( restart.c_str(), res );
1579 : : } else {
1580 : 72 : resume();
1581 : : }
1582 : : }
1583 : 196 : }
1584 : :
1585 : : void
1586 : 196 : Transporter::finish( std::size_t meshid )
1587 : : // *****************************************************************************
1588 : : // Normal finish of time stepping
1589 : : //! \param[in] meshid Mesh id
1590 : : // *****************************************************************************
1591 : : {
1592 : 196 : checkpoint( /* finished = */ 1, meshid );
1593 : 196 : }
1594 : :
1595 : : #include "NoWarning/transporter.def.h"
|