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