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