Branch data Line data Source code
1 : : // *****************************************************************************
2 : : /*!
3 : : \file src/Inciter/OversetFE.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 OversetFE for a PDE system with continuous Galerkin FE + RK
9 : : \details OversetFE advances a system of partial differential equations
10 : : using a continuous Galerkin (CG) finite element (FE) spatial discretization
11 : : (using linear shapefunctions on tetrahedron elements) combined with a
12 : : Runge-Kutta (RK) time stepping scheme and overset grids.
13 : : \see The documentation in OversetFE.hpp.
14 : : */
15 : : // *****************************************************************************
16 : :
17 : : #include "QuinoaBuildConfig.hpp"
18 : : #include "OversetFE.hpp"
19 : : #include "Vector.hpp"
20 : : #include "Reader.hpp"
21 : : #include "ContainerUtil.hpp"
22 : : #include "UnsMesh.hpp"
23 : : #include "ExodusIIMeshWriter.hpp"
24 : : #include "Inciter/InputDeck/InputDeck.hpp"
25 : : #include "DerivedData.hpp"
26 : : #include "CGPDE.hpp"
27 : : #include "Discretization.hpp"
28 : : #include "DiagReducer.hpp"
29 : : #include "NodeBC.hpp"
30 : : #include "Refiner.hpp"
31 : : #include "Reorder.hpp"
32 : : #include "Around.hpp"
33 : : #include "CGPDE.hpp"
34 : : #include "FieldOutput.hpp"
35 : :
36 : : namespace inciter {
37 : :
38 : : extern ctr::InputDeck g_inputdeck;
39 : : extern std::vector< CGPDE > g_cgpde;
40 : :
41 : : //! Runge-Kutta coefficients
42 : : static const std::array< tk::real, 3 > rkcoef{{ 1.0/3.0, 1.0/2.0, 1.0 }};
43 : :
44 : : } // inciter::
45 : :
46 : : using inciter::OversetFE;
47 : :
48 : 299 : OversetFE::OversetFE( const CProxy_Discretization& disc,
49 : : const CProxy_Ghosts&,
50 : : const std::map< int, std::vector< std::size_t > >& bface,
51 : : const std::map< int, std::vector< std::size_t > >& bnode,
52 : 299 : const std::vector< std::size_t >& triinpoel ) :
53 : : m_disc( disc ),
54 : : m_nsol( 0 ),
55 : : m_ngrad( 0 ),
56 : : m_nrhs( 0 ),
57 : : m_nbnorm( 0 ),
58 : : m_ndfnorm( 0 ),
59 : : m_nmblk( 0 ),
60 : : m_bnode( bnode ),
61 : : m_bface( bface ),
62 : : m_triinpoel( tk::remap( triinpoel, Disc()->Lid() ) ),
63 : : m_bndel( Disc()->bndel() ),
64 : : m_dfnorm(),
65 : : m_dfnormc(),
66 : : m_dfn(),
67 : : m_esup( tk::genEsup( Disc()->Inpoel(), 4 ) ),
68 : 299 : m_psup( tk::genPsup( Disc()->Inpoel(), 4, m_esup ) ),
69 : 299 : m_u( Disc()->Gid().size(),
70 : 299 : g_inputdeck.get< tag::ncomp >() ),
71 : 299 : m_uc( m_u.nunk(), m_u.nprop()+1 ),
72 : : m_un( m_u.nunk(), m_u.nprop() ),
73 : : m_rhs( m_u.nunk(), m_u.nprop() ),
74 : : m_rhsc(),
75 : 299 : m_chBndGrad( Disc()->Bid().size(), m_u.nprop()*3 ),
76 : : m_dirbc(),
77 : : m_chBndGradc(),
78 : 0 : m_blank( m_u.nunk(), 1.0 ),
79 : : m_diag(),
80 : : m_bnorm(),
81 : : m_bnormc(),
82 : : m_symbcnodes(),
83 : : m_farfieldbcnodes(),
84 : : m_slipwallbcnodes(),
85 : : m_symbctri(),
86 : : m_slipwallbctri(),
87 : : m_timedepbcnodes(),
88 : : m_timedepbcFn(),
89 : : m_stage( 0 ),
90 : : m_boxnodes(),
91 : : m_edgenode(),
92 : : m_edgeid(),
93 : 0 : m_dtp( m_u.nunk(), 0.0 ),
94 : 299 : m_tp( m_u.nunk(), g_inputdeck.get< tag::t0 >() ),
95 : : m_finished( 0 ),
96 : : m_movedmesh( 0 ),
97 : : m_nusermeshblk( 0 ),
98 : : m_nodeblockid(),
99 : : m_nodeblockidc(),
100 : 0 : m_srcFlag(m_u.nunk(), 1),
101 : : m_ixfer(0),
102 : : m_surfForce({{0, 0, 0}}),
103 : : m_surfTorque({{0, 0, 0}}),
104 : : m_centMass({{0, 0, 0}}),
105 : : m_centMassVel({{0, 0, 0}}),
106 : : m_angVelMesh(0),
107 : : m_centMassn({{0, 0, 0}}),
108 : : m_centMassVeln({{0, 0, 0}}),
109 [ + - ][ + - ]: 1196 : m_angVelMeshn(0)
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + - ]
110 : : // *****************************************************************************
111 : : // Constructor
112 : : //! \param[in] disc Discretization proxy
113 : : //! \param[in] bface Boundary-faces mapped to side sets used in the input file
114 : : //! \param[in] bnode Boundary-node lists mapped to side sets used in input file
115 : : //! \param[in] triinpoel Boundary-face connectivity where BCs set (global ids)
116 : : // *****************************************************************************
117 : : //! [Constructor]
118 : : {
119 : 299 : usesAtSync = true; // enable migration at AtSync
120 : :
121 [ + - ]: 299 : auto d = Disc();
122 : :
123 : : // Perform optional operator-access-pattern mesh node reordering
124 [ - + ]: 299 : if (g_inputdeck.get< tag::operator_reorder >()) {
125 : :
126 : : // Create new local ids based on access pattern of PDE operators
127 : 0 : std::unordered_map< std::size_t, std::size_t > map;
128 : 0 : std::size_t n = 0;
129 : :
130 [ - - ]: 0 : for (std::size_t p=0; p<m_u.nunk(); ++p) { // for each point p
131 [ - - ][ - - ]: 0 : if (map.find(p) == end(map)) map[p] = n++;
[ - - ]
132 [ - - ]: 0 : for (auto q : tk::Around(m_psup,p)) { // for each edge p-q
133 [ - - ][ - - ]: 0 : if (map.find(q) == end(map)) map[q] = n++;
[ - - ]
134 : : }
135 : : }
136 : :
137 [ - - ][ - - ]: 0 : Assert( map.size() == d->Gid().size(), "Map size mismatch" );
[ - - ][ - - ]
138 : :
139 : : // Remap data in bound Discretization object
140 [ - - ]: 0 : d->remap( map );
141 : : // Recompute elements surrounding points
142 [ - - ]: 0 : m_esup = tk::genEsup( d->Inpoel(), 4 );
143 : : // Recompute points surrounding points
144 [ - - ]: 0 : m_psup = tk::genPsup( d->Inpoel(), 4, m_esup );
145 : : // Remap boundary triangle face connectivity
146 [ - - ]: 0 : tk::remap( m_triinpoel, map );
147 : : }
148 : :
149 [ + + ]: 1196 : for (std::size_t i=0; i<3; ++i)
150 : 1794 : m_centMass[i] = g_inputdeck.get< tag::mesh >()[d->MeshId()].get<
151 : 897 : tag::center_of_mass >()[i];
152 : :
153 : : // Query/update boundary-conditions-related data structures from user input
154 [ + - ]: 299 : getBCNodes();
155 : :
156 : : // Activate SDAG wait for initially computing normals, and mesh blocks
157 [ + - ][ + - ]: 299 : thisProxy[ thisIndex ].wait4norm();
158 [ + - ][ + - ]: 299 : thisProxy[ thisIndex ].wait4meshblk();
159 : :
160 [ - + ][ - + ]: 299 : if (g_inputdeck.get< tag::steady_state >() &&
161 [ - - ]: 0 : g_inputdeck.get< tag::rigid_body_motion >().get< tag::rigid_body_movt >())
162 [ - - ][ - - ]: 0 : Throw("Rigid body motion cannot be activated for steady state problem");
[ - - ]
163 : :
164 [ + - ]: 299 : d->comfinal();
165 : :
166 : 299 : }
167 : : //! [Constructor]
168 : :
169 : : void
170 : 299 : OversetFE::getBCNodes()
171 : : // *****************************************************************************
172 : : // Query/update boundary-conditions-related data structures from user input
173 : : // *****************************************************************************
174 : : {
175 [ + - ]: 299 : auto d = Disc();
176 : :
177 : : // Prepare unique set of symmetry BC nodes
178 [ + - ]: 598 : auto sym = d->bcnodes< tag::symmetry >( m_bface, m_triinpoel );
179 [ - + ]: 299 : for (const auto& [s,nodes] : sym)
180 [ - - ]: 0 : m_symbcnodes.insert( begin(nodes), end(nodes) );
181 : :
182 : : // Prepare unique set of farfield BC nodes
183 [ + - ]: 598 : auto far = d->bcnodes< tag::farfield >( m_bface, m_triinpoel );
184 [ + + ]: 307 : for (const auto& [s,nodes] : far)
185 [ + - ]: 8 : m_farfieldbcnodes.insert( begin(nodes), end(nodes) );
186 : :
187 : : // Prepare unique set of slip wall BC nodes
188 [ + - ]: 598 : auto slip = d->bcnodes< tag::slipwall >( m_bface, m_triinpoel );
189 [ - + ]: 299 : for (const auto& [s,nodes] : slip)
190 [ - - ]: 0 : m_slipwallbcnodes.insert( begin(nodes), end(nodes) );
191 : :
192 : : // If farfield BC is set on a node, will not also set symmetry and slip BC
193 [ + + ]: 959 : for (auto fn : m_farfieldbcnodes) {
194 [ + - ]: 660 : m_symbcnodes.erase(fn);
195 [ + - ]: 660 : m_slipwallbcnodes.erase(fn);
196 : : }
197 : :
198 : : // If symmetry BC is set on a node, will not also set slip BC
199 [ - + ]: 299 : for (auto fn : m_symbcnodes) {
200 [ - - ]: 0 : m_slipwallbcnodes.erase(fn);
201 : : }
202 : :
203 : : // Prepare boundary nodes contiguously accessible from a triangle-face loop,
204 : : // which contain both symmetry and no slip walls
205 [ + - ]: 299 : m_symbctri.resize( m_triinpoel.size()/3, 0 );
206 [ + + ]: 10853 : for (std::size_t e=0; e<m_triinpoel.size()/3; ++e)
207 [ + - ][ - + ]: 10554 : if (m_symbcnodes.find(m_triinpoel[e*3+0]) != end(m_symbcnodes))
208 : 0 : m_symbctri[e] = 1;
209 : :
210 : : // Prepare the above for slip walls, which are needed for pressure integrals
211 : : // to obtain force on overset walls
212 [ + - ]: 299 : m_slipwallbctri.resize( m_triinpoel.size()/3, 0 );
213 [ + + ]: 10853 : for (std::size_t e=0; e<m_triinpoel.size()/3; ++e)
214 [ + - ][ - + ]: 10554 : if (m_slipwallbcnodes.find(m_triinpoel[e*3+0]) != end(m_slipwallbcnodes))
215 : 0 : m_slipwallbctri[e] = 1;
216 : :
217 : : // Prepare unique set of time dependent BC nodes
218 : 299 : m_timedepbcnodes.clear();
219 : 299 : m_timedepbcFn.clear();
220 : : const auto& timedep =
221 : 299 : g_inputdeck.get< tag::bc >()[d->MeshId()].get< tag::timedep >();
222 [ + + ]: 299 : if (!timedep.empty()) {
223 [ + - ]: 1 : m_timedepbcnodes.resize(timedep.size());
224 [ + - ]: 1 : m_timedepbcFn.resize(timedep.size());
225 : 1 : std::size_t ib=0;
226 [ + + ]: 2 : for (const auto& bndry : timedep) {
227 : 2 : std::unordered_set< std::size_t > nodes;
228 [ + + ]: 2 : for (const auto& s : bndry.template get< tag::sideset >()) {
229 [ + - ]: 1 : auto k = m_bnode.find(static_cast<int>(s));
230 [ + - ]: 1 : if (k != end(m_bnode)) {
231 [ + + ]: 12 : for (auto g : k->second) { // global node ids on side set
232 [ + - ][ + - ]: 11 : nodes.insert( tk::cref_find(d->Lid(),g) );
233 : : }
234 : : }
235 : : }
236 [ + - ]: 1 : m_timedepbcnodes[ib].insert( begin(nodes), end(nodes) );
237 : :
238 : : // Store user defined discrete function in time. This is done in the same
239 : : // loop as the BC nodes, so that the indices for the two vectors
240 : : // m_timedepbcnodes and m_timedepbcFn are consistent with each other
241 [ + - ]: 1 : auto fn = bndry.template get< tag::fn >();
242 [ + + ]: 4 : for (std::size_t ir=0; ir<fn.size()/6; ++ir) {
243 [ + - ]: 6 : m_timedepbcFn[ib].push_back({{ fn[ir*6+0], fn[ir*6+1], fn[ir*6+2],
244 : 3 : fn[ir*6+3], fn[ir*6+4], fn[ir*6+5] }});
245 : : }
246 : 1 : ++ib;
247 : : }
248 : : }
249 : :
250 [ - + ][ - - ]: 299 : Assert(m_timedepbcFn.size() == m_timedepbcnodes.size(), "Incorrect number of "
[ - - ][ - - ]
251 : : "time dependent functions.");
252 : 299 : }
253 : :
254 : : void
255 : 299 : OversetFE::norm()
256 : : // *****************************************************************************
257 : : // Start (re-)computing boundary point-, and dual-face normals
258 : : // *****************************************************************************
259 : : {
260 [ + - ]: 299 : auto d = Disc();
261 : :
262 : : // Query nodes at which symmetry BCs are specified
263 [ + - ]: 598 : auto bn = d->bcnodes< tag::symmetry >( m_bface, m_triinpoel );
264 : :
265 : : // Query nodes at which farfield BCs are specified
266 [ + - ]: 598 : auto far = d->bcnodes< tag::farfield >( m_bface, m_triinpoel );
267 : : // Merge BC data where boundary-point normals are required
268 [ + + ][ + - ]: 307 : for (const auto& [s,n] : far) bn[s].insert( begin(n), end(n) );
[ + - ]
269 : :
270 : : // Query nodes at which slip wall BCs are specified
271 [ + - ]: 598 : auto slip = d->bcnodes< tag::slipwall >( m_bface, m_triinpoel );
272 : : // Merge BC data where boundary-point normals are required
273 [ - + ][ - - ]: 299 : for (const auto& [s,n] : slip) bn[s].insert( begin(n), end(n) );
[ - - ]
274 : :
275 : : // Query nodes at which mesh velocity symmetry BCs are specified
276 : 598 : std::unordered_map<int, std::unordered_set< std::size_t >> ms;
277 [ - + ]: 299 : for (const auto& s : g_inputdeck.get< tag::ale, tag::symmetry >()) {
278 [ - - ]: 0 : auto k = m_bface.find(static_cast<int>(s));
279 [ - - ]: 0 : if (k != end(m_bface)) {
280 [ - - ]: 0 : auto& n = ms[ k->first ];
281 [ - - ]: 0 : for (auto f : k->second) {
282 [ - - ]: 0 : n.insert( m_triinpoel[f*3+0] );
283 [ - - ]: 0 : n.insert( m_triinpoel[f*3+1] );
284 [ - - ]: 0 : n.insert( m_triinpoel[f*3+2] );
285 : : }
286 : : }
287 : : }
288 : : // Merge BC data where boundary-point normals are required
289 [ - + ][ - - ]: 299 : for (const auto& [s,n] : ms) bn[s].insert( begin(n), end(n) );
[ - - ]
290 : :
291 : : // Compute boundary point normals
292 [ + - ]: 299 : bnorm( bn );
293 : :
294 : : // Compute dual-face normals associated to edges
295 [ + - ]: 299 : dfnorm();
296 : 299 : }
297 : :
298 : : std::array< tk::real, 3 >
299 : 65433 : OversetFE::edfnorm( const tk::UnsMesh::Edge& edge,
300 : : const std::unordered_map< tk::UnsMesh::Edge,
301 : : std::vector< std::size_t >,
302 : : tk::UnsMesh::Hash<2>, tk::UnsMesh::Eq<2> >& esued )
303 : : const
304 : : // *****************************************************************************
305 : : // Compute normal of dual-mesh associated to edge
306 : : //! \param[in] edge Edge whose dual-face normal to compute given by local ids
307 : : //! \param[in] esued Elements surrounding edges
308 : : //! \return Dual-face normal for edge
309 : : // *****************************************************************************
310 : : {
311 : 65433 : auto d = Disc();
312 : 65433 : const auto& inpoel = d->Inpoel();
313 : 65433 : const auto& coord = d->Coord();
314 : 65433 : const auto& x = coord[0];
315 : 65433 : const auto& y = coord[1];
316 : 65433 : const auto& z = coord[2];
317 : :
318 : 65433 : std::array< tk::real, 3 > n{ 0.0, 0.0, 0.0 };
319 : :
320 [ + - ][ + + ]: 312339 : for (auto e : tk::cref_find(esued,edge)) {
321 : : // access node IDs
322 : : const std::array< std::size_t, 4 >
323 : 246906 : N{ inpoel[e*4+0], inpoel[e*4+1], inpoel[e*4+2], inpoel[e*4+3] };
324 : : // compute element Jacobi determinant
325 : : const std::array< tk::real, 3 >
326 : 246906 : ba{{ x[N[1]]-x[N[0]], y[N[1]]-y[N[0]], z[N[1]]-z[N[0]] }},
327 : 246906 : ca{{ x[N[2]]-x[N[0]], y[N[2]]-y[N[0]], z[N[2]]-z[N[0]] }},
328 : 246906 : da{{ x[N[3]]-x[N[0]], y[N[3]]-y[N[0]], z[N[3]]-z[N[0]] }};
329 : 246906 : const auto J = tk::triple( ba, ca, da ); // J = 6V
330 [ - + ][ - - ]: 246906 : Assert( J > 0, "Element Jacobian non-positive" );
[ - - ][ - - ]
331 : : // shape function derivatives, nnode*ndim [4][3]
332 : : std::array< std::array< tk::real, 3 >, 4 > grad;
333 : 246906 : grad[1] = tk::crossdiv( ca, da, J );
334 : 246906 : grad[2] = tk::crossdiv( da, ba, J );
335 : 246906 : grad[3] = tk::crossdiv( ba, ca, J );
336 [ + + ]: 987624 : for (std::size_t i=0; i<3; ++i)
337 : 740718 : grad[0][i] = -grad[1][i]-grad[2][i]-grad[3][i];
338 : : // sum normal contributions
339 : : // The constant 1/48: Eq (12) from Waltz et al. Computers & fluids (92) 2014
340 : : // The result of the integral of shape function N on a tet is V/4.
341 : : // This can be written as J/(6*4). Eq (12) has a 1/2 multiplier.
342 : : // This leads to J/48.
343 : 246906 : auto J48 = J/48.0;
344 [ + + ]: 1728342 : for (const auto& [a,b] : tk::lpoed) {
345 [ + - ]: 1481436 : auto s = tk::orient( {N[a],N[b]}, edge );
346 [ + + ]: 5925744 : for (std::size_t j=0; j<3; ++j)
347 : 4444308 : n[j] += J48 * s * (grad[a][j] - grad[b][j]);
348 : : }
349 : : }
350 : :
351 : 65433 : return n;
352 : : }
353 : :
354 : : void
355 : 299 : OversetFE::dfnorm()
356 : : // *****************************************************************************
357 : : // Compute dual-face normals associated to edges
358 : : // *****************************************************************************
359 : : {
360 [ + - ]: 299 : auto d = Disc();
361 : 299 : const auto& inpoel = d->Inpoel();
362 : 299 : const auto& gid = d->Gid();
363 : :
364 : : // compute derived data structures
365 [ + - ][ + - ]: 598 : auto esued = tk::genEsued( inpoel, 4, tk::genEsup( inpoel, 4 ) );
366 : :
367 : : // Compute dual-face normals for domain edges
368 [ + + ]: 14509 : for (std::size_t p=0; p<gid.size(); ++p) // for each point p
369 [ + + ]: 145076 : for (auto q : tk::Around(m_psup,p)) // for each edge p-q
370 [ + + ]: 130866 : if (gid[p] < gid[q])
371 [ + - ][ + - ]: 65433 : m_dfnorm[{gid[p],gid[q]}] = edfnorm( {p,q}, esued );
372 : :
373 : : // Send our dual-face normal contributions to neighbor chares
374 [ + + ]: 299 : if (d->EdgeCommMap().empty())
375 [ + - ]: 3 : comdfnorm_complete();
376 : : else {
377 [ + + ]: 3774 : for (const auto& [c,edges] : d->EdgeCommMap()) {
378 : 3478 : decltype(m_dfnorm) exp;
379 [ + + ][ + - ]: 23824 : for (const auto& e : edges) exp[e] = tk::cref_find(m_dfnorm,e);
[ + - ]
380 [ + - ][ + - ]: 3478 : thisProxy[c].comdfnorm( exp );
381 : : }
382 : : }
383 : :
384 [ + - ]: 299 : owndfnorm_complete();
385 : 299 : }
386 : :
387 : : void
388 : 3478 : OversetFE::comdfnorm( const std::unordered_map< tk::UnsMesh::Edge,
389 : : std::array< tk::real, 3 >,
390 : : tk::UnsMesh::Hash<2>, tk::UnsMesh::Eq<2> >& dfnorm )
391 : : // *****************************************************************************
392 : : // Receive contributions to dual-face normals on chare-boundaries
393 : : //! \param[in] dfnorm Incoming partial sums of dual-face normals associated to
394 : : //! chare-boundary edges
395 : : // *****************************************************************************
396 : : {
397 : : // Buffer up inccoming contributions to dual-face normals
398 [ + + ]: 23824 : for (const auto& [e,n] : dfnorm) {
399 [ + - ]: 20346 : auto& dfn = m_dfnormc[e];
400 : 20346 : dfn[0] += n[0];
401 : 20346 : dfn[1] += n[1];
402 : 20346 : dfn[2] += n[2];
403 : : }
404 : :
405 [ + + ]: 3478 : if (++m_ndfnorm == Disc()->EdgeCommMap().size()) {
406 : 296 : m_ndfnorm = 0;
407 : 296 : comdfnorm_complete();
408 : : }
409 : 3478 : }
410 : :
411 : : void
412 : 299 : OversetFE::bnorm( const std::unordered_map< int,
413 : : std::unordered_set< std::size_t > >& bcnodes )
414 : : // *****************************************************************************
415 : : // Compute boundary point normals
416 : : //! \param[in] bcnodes Local node ids associated to side set ids at which BCs
417 : : //! are set that require normals
418 : : //*****************************************************************************
419 : : {
420 : 299 : auto d = Disc();
421 : :
422 : 299 : m_bnorm = cg::bnorm( m_bface, m_triinpoel, d->Coord(), d->Gid(), bcnodes );
423 : :
424 : : // Send our nodal normal contributions to neighbor chares
425 [ + + ]: 299 : if (d->NodeCommMap().empty())
426 : 3 : comnorm_complete();
427 : : else
428 [ + + ]: 3774 : for (const auto& [ neighborchare, sharednodes ] : d->NodeCommMap()) {
429 : : std::unordered_map< int,
430 : 3478 : std::unordered_map< std::size_t, std::array< tk::real, 4 > > > exp;
431 [ + + ]: 18116 : for (auto i : sharednodes) {
432 [ + + ]: 15512 : for (const auto& [s,norms] : m_bnorm) {
433 [ + - ]: 874 : auto j = norms.find(i);
434 [ + + ][ + - ]: 874 : if (j != end(norms)) exp[s][i] = j->second;
[ + - ]
435 : : }
436 : : }
437 [ + - ][ + - ]: 3478 : thisProxy[ neighborchare ].comnorm( exp );
438 : : }
439 : :
440 : 299 : ownnorm_complete();
441 : 299 : }
442 : :
443 : : void
444 : 3478 : OversetFE::comnorm( const std::unordered_map< int,
445 : : std::unordered_map< std::size_t, std::array< tk::real, 4 > > >& innorm )
446 : : // *****************************************************************************
447 : : // Receive boundary point normals on chare-boundaries
448 : : //! \param[in] innorm Incoming partial sums of boundary point normal
449 : : //! contributions to normals (first 3 components), inverse distance squared
450 : : //! (4th component), associated to side set ids
451 : : // *****************************************************************************
452 : : {
453 : : // Buffer up incoming boundary-point normal vector contributions
454 [ + + ]: 3484 : for (const auto& [s,norms] : innorm) {
455 [ + - ]: 6 : auto& bnorms = m_bnormc[s];
456 [ + + ]: 101 : for (const auto& [p,n] : norms) {
457 [ + - ]: 95 : auto& bnorm = bnorms[p];
458 : 95 : bnorm[0] += n[0];
459 : 95 : bnorm[1] += n[1];
460 : 95 : bnorm[2] += n[2];
461 : 95 : bnorm[3] += n[3];
462 : : }
463 : : }
464 : :
465 [ + + ]: 3478 : if (++m_nbnorm == Disc()->NodeCommMap().size()) {
466 : 296 : m_nbnorm = 0;
467 : 296 : comnorm_complete();
468 : : }
469 : 3478 : }
470 : :
471 : : void
472 : 529 : OversetFE::registerReducers()
473 : : // *****************************************************************************
474 : : // Configure Charm++ reduction types initiated from this chare array
475 : : //! \details Since this is a [initnode] routine, the runtime system executes the
476 : : //! routine exactly once on every logical node early on in the Charm++ init
477 : : //! sequence. Must be static as it is called without an object. See also:
478 : : //! Section "Initializations at Program Startup" at in the Charm++ manual
479 : : //! http://charm.cs.illinois.edu/manuals/html/charm++/manual.html.
480 : : // *****************************************************************************
481 : : {
482 : 529 : NodeDiagnostics::registerReducers();
483 : 529 : }
484 : :
485 : : void
486 : 2697 : OversetFE::ResumeFromSync()
487 : : // *****************************************************************************
488 : : // Return from migration
489 : : //! \details This is called when load balancing (LB) completes. The presence of
490 : : //! this function does not affect whether or not we block on LB.
491 : : // *****************************************************************************
492 : : {
493 [ - + ][ - - ]: 2697 : if (Disc()->It() == 0) Throw( "it = 0 in ResumeFromSync()" );
[ - - ][ - - ]
494 : :
495 [ + - ]: 2697 : if (!g_inputdeck.get< tag::cmd, tag::nonblocking >()) next();
496 : 2697 : }
497 : :
498 : : //! [setup]
499 : : void
500 : 299 : OversetFE::setup()
501 : : // *****************************************************************************
502 : : // Start setup for solution
503 : : // *****************************************************************************
504 : : {
505 : 299 : auto d = Disc();
506 : :
507 : : // Determine nodes inside user-defined IC box
508 : 598 : g_cgpde[d->MeshId()].IcBoxNodes( d->Coord(), d->Inpoel(),
509 : 299 : d->ElemBlockId(), m_boxnodes, m_nodeblockid, m_nusermeshblk );
510 : :
511 : : // Communicate mesh block nodes to other chares on chare-boundary
512 [ + + ]: 299 : if (d->NodeCommMap().empty()) // in serial we are done
513 : 3 : comblk_complete();
514 : : else // send mesh block information to chare-boundary nodes to fellow chares
515 [ + + ]: 3774 : for (const auto& [c,n] : d->NodeCommMap()) {
516 : : // data structure assigning block ids (set of values) to nodes (index).
517 : : // although nodeblockid is a map with key-blockid and value-nodeid, the
518 : : // sending data structure has to be inverted, because of how communication
519 : : // data is handled.
520 [ + - ]: 3478 : std::vector< std::set< std::size_t > > mb( n.size() );
521 : 3478 : std::size_t j = 0;
522 [ + + ]: 18116 : for (auto i : n) {
523 [ + + ]: 31024 : for (const auto& [blid, ndset] : m_nodeblockid) {
524 : : // if node was found in a block, add to send-data
525 [ + - ][ + - ]: 16386 : if (ndset.find(tk::cref_find(d->Lid(),i)) != ndset.end())
[ + + ]
526 [ + - ]: 14851 : mb[j].insert(blid);
527 : : }
528 [ - + ]: 14638 : if (m_nusermeshblk > 0)
529 [ - - ][ - - ]: 0 : Assert(mb[j].size() > 0, "Sending no block data for node");
[ - - ][ - - ]
530 : 14638 : ++j;
531 : : }
532 [ + - ][ + - ]: 3478 : thisProxy[c].comblk( std::vector<std::size_t>(begin(n),end(n)), mb );
[ + - ]
533 : : }
534 : :
535 : 299 : ownblk_complete();
536 : 299 : }
537 : :
538 : : void
539 : 3478 : OversetFE::comblk( const std::vector< std::size_t >& gid,
540 : : const std::vector< std::set< std::size_t > >& mb )
541 : : // *****************************************************************************
542 : : // Receive mesh block information for nodes on chare-boundaries
543 : : //! \param[in] gid Global mesh node IDs at which we receive RHS contributions
544 : : //! \param[in] mb Block ids for each node on chare-boundaries
545 : : //! \details This function receives mesh block information for nodes on chare
546 : : //! boundaries. While m_nodeblockid stores block information for own nodes,
547 : : //! m_nodeblockidc collects the neighbor chare information during
548 : : //! communication. This way work on m_nodeblockid and m_nodeblockidc is
549 : : //! overlapped. The two are combined in continueSetup().
550 : : // *****************************************************************************
551 : : {
552 [ - + ][ - - ]: 3478 : Assert( mb.size() == gid.size(), "Size mismatch" );
[ - - ][ - - ]
553 : :
554 [ + + ]: 18116 : for (std::size_t i=0; i<gid.size(); ++i) {
555 [ + + ]: 29489 : for (const auto& blid : mb[i]) {
556 [ + - ][ + - ]: 14851 : m_nodeblockidc[blid].insert(gid[i]);
557 : : }
558 : : }
559 : :
560 : : // When we have heard from all chares we communicate with, this chare is done
561 [ + + ]: 3478 : if (++m_nmblk == Disc()->NodeCommMap().size()) {
562 : 296 : m_nmblk = 0;
563 : 296 : comblk_complete();
564 : : }
565 : 3478 : }
566 : :
567 : : void
568 : 299 : OversetFE::continueSetup()
569 : : // *****************************************************************************
570 : : // Continue setup for solution, after communication for mesh blocks
571 : : // *****************************************************************************
572 : : {
573 : 299 : auto d = Disc();
574 : :
575 : : // Combine own and communicated mesh block information
576 [ + + ]: 601 : for (const auto& [blid, ndset] : m_nodeblockidc) {
577 [ + + ]: 7347 : for (const auto& i : ndset) {
578 [ + - ]: 7045 : auto lid = tk::cref_find(d->Lid(), i);
579 [ + - ][ + - ]: 7045 : m_nodeblockid[blid].insert(lid);
580 : : }
581 : : }
582 : :
583 : : // clear receive buffer
584 : 299 : tk::destroy(m_nodeblockidc);
585 : :
586 : : // Compute volume of user-defined box IC
587 : 299 : d->boxvol( m_boxnodes, m_nodeblockid, m_nusermeshblk );
588 : :
589 : : // Query time history field output labels from all PDEs integrated
590 : 299 : const auto& hist_points = g_inputdeck.get< tag::history_output, tag::point >();
591 [ - + ]: 299 : if (!hist_points.empty()) {
592 : 0 : std::vector< std::string > histnames;
593 [ - - ]: 0 : auto n = g_cgpde[d->MeshId()].histNames();
594 [ - - ]: 0 : histnames.insert( end(histnames), begin(n), end(n) );
595 [ - - ]: 0 : d->histheader( std::move(histnames) );
596 : : }
597 : 299 : }
598 : : //! [setup]
599 : :
600 : : void
601 : 299 : OversetFE::box( tk::real v, const std::vector< tk::real >& blkvols )
602 : : // *****************************************************************************
603 : : // Receive total box IC volume and set conditions in box
604 : : //! \param[in] v Total volume within user-specified box
605 : : //! \param[in] blkvols Vector of mesh block discrete volumes with user ICs
606 : : // *****************************************************************************
607 : : {
608 [ - + ][ - - ]: 299 : Assert(blkvols.size() == m_nusermeshblk,
[ - - ][ - - ]
609 : : "Incorrect size of block volume vector");
610 : 299 : auto d = Disc();
611 : :
612 : : // Store user-defined box/block IC volume
613 : 299 : d->Boxvol() = v;
614 : 299 : d->MeshBlkVol() = blkvols;
615 : :
616 : : // Set initial conditions for all PDEs
617 : 598 : g_cgpde[d->MeshId()].initialize( d->Coord(), m_u, d->T(), d->Boxvol(),
618 : 299 : m_boxnodes, d->MeshBlkVol(), m_nodeblockid );
619 : : // Initialize overset mesh velocity to zero
620 : 299 : auto& u_mesh = d->MeshVel();
621 [ + + ]: 14509 : for (std::size_t p=0; p<u_mesh.nunk(); ++p) {
622 [ + + ]: 56840 : for (std::size_t i=0; i<3; ++i) u_mesh(p,i) = 0.0;
623 : : }
624 : :
625 : : // Initialize nodal mesh volumes at previous time step stage
626 : 299 : d->Voln() = d->Vol();
627 : :
628 : : // Initiate solution transfer (if coupled)
629 : 299 : transferSol();
630 : 299 : }
631 : :
632 : : void
633 : 9311 : OversetFE::transferSol()
634 : : // *****************************************************************************
635 : : // Transfer solution to other solver and mesh if coupled
636 : : // *****************************************************************************
637 : : {
638 : : // Set up transfer-flags for receiving mesh
639 [ + + ]: 9311 : if (m_ixfer == 1) {
640 : 24 : applySolTransfer(0);
641 : : }
642 : 9311 : setTransferFlags(m_ixfer);
643 : 9311 : ++m_ixfer;
644 : :
645 : : // Initiate IC transfer (if coupled)
646 [ + - ]: 18622 : Disc()->transfer( m_uc, m_ixfer-1,
647 [ + - ][ + - ]: 18622 : CkCallback(CkIndex_OversetFE::lhs(), thisProxy[thisIndex]) );
648 : 9311 : }
649 : :
650 : : //! [Compute lhs]
651 : : void
652 : 9287 : OversetFE::lhs()
653 : : // *****************************************************************************
654 : : // Compute the left-hand side of transport equations
655 : : //! \details Also (re-)compute all data structures if the mesh changed.
656 : : // *****************************************************************************
657 : : {
658 : : // Do corrections in solution based on incoming transfer
659 : 9287 : applySolTransfer(1);
660 : 9287 : m_ixfer = 0;
661 : :
662 : : // No need for LHS in OversetFE
663 : :
664 : : // If mesh moved: (Re-)compute boundary point- and dual-face normals, and
665 : : // then proceed to stage()
666 : : // If mesh did not move: shortcut to stage()
667 [ + - ][ + + ]: 9287 : if (m_movedmesh || Disc()->Initial()) norm();
[ + + ]
668 : 8988 : else stage();
669 : 9287 : }
670 : : //! [Compute lhs]
671 : :
672 : : //! [Merge normals and continue]
673 : : void
674 : 299 : OversetFE::mergelhs()
675 : : // *****************************************************************************
676 : : // The own and communication portion of the left-hand side is complete
677 : : // *****************************************************************************
678 : : {
679 : : // Combine own and communicated contributions of normals
680 : 299 : normfinal();
681 : :
682 : : // Start with time stepping logic
683 [ + - ]: 299 : if (Disc()->Initial()) {
684 : : // Output initial conditions to file and then start time stepping
685 [ + - ][ + - ]: 299 : writeFields( CkCallback(CkIndex_OversetFE::start(), thisProxy[thisIndex]) );
[ + - ]
686 : : }
687 : 0 : else stage();
688 : 299 : }
689 : : //! [Merge normals and continue]
690 : :
691 : : //! [start]
692 : : void
693 : 299 : OversetFE::start()
694 : : // *****************************************************************************
695 : : // Start time stepping
696 : : // *****************************************************************************
697 : : {
698 : : // Set flag that indicates that we are now during time stepping
699 : 299 : Disc()->Initial( 0 );
700 : : // Start timer measuring time stepping wall clock time
701 : 299 : Disc()->Timer().zero();
702 : : // Zero grind-timer
703 : 299 : Disc()->grindZero();
704 : : // Continue to first time step
705 : 299 : next();
706 : 299 : }
707 : : //! [start]
708 : :
709 : : void
710 : 9311 : OversetFE::applySolTransfer(
711 : : std::size_t dirn )
712 : : // *****************************************************************************
713 : : // \brief Apply the transferred solution to the solution vector based on
714 : : // transfer flags previously set up
715 : : //! \param[in] dirn 0 if called from B to O, 1 if called from O to B
716 : : // *****************************************************************************
717 : : {
718 : : // Change solution only if:
719 : : // 1. undergoing transfer from B to O, and currently on O
720 [ + + ][ + + ]: 9311 : if (dirn == 0 && Disc()->MeshId() != 0) {
[ + + ]
721 : :
722 [ + + ]: 1056 : for (auto i : m_farfieldbcnodes) {
723 : : // overset-BC nodes: use transferred solution and blank nodes.
724 : : // the transfer-flag from m_uc is not used since it has been overwritten
725 : : // by Disc()->transfer() with the flag from B
726 [ + + ]: 6264 : for (ncomp_t c=0; c<m_u.nprop(); ++c) { // Loop over number of equations
727 [ + - ][ + - ]: 5220 : m_u(i,c) = m_uc(i,c);
728 : : }
729 : 1044 : m_blank[i] = 0.0;
730 : : }
731 : :
732 : : }
733 : : // 2. undergoing transfer from O to B, and currently on B
734 [ + + ][ + + ]: 9299 : else if (dirn == 1 && Disc()->MeshId() == 0) {
[ + + ]
735 : :
736 : : //TODO: index the flag in a better way
737 : 9275 : std::size_t iflag = m_uc.nprop()-1;
738 : :
739 : : // Zero out solution space for nodes with a specific transfer flag set
740 [ + + ]: 364766 : for (std::size_t i=0; i<m_uc.nunk(); ++i) { // Check flag value
741 : :
742 [ + + ]: 355491 : if (std::abs(m_uc(i,iflag) - 1.0) < 1e-4) {
743 : : // overset-BC nodes: use transferred solution and blank nodes
744 [ + + ]: 528 : for (ncomp_t c=0; c<m_u.nprop(); ++c) { // Loop over number of equations
745 : 440 : m_u(i,c) = m_uc(i,c);
746 : : }
747 : 88 : m_blank[i] = 0.0;
748 : : }
749 [ - + ]: 355403 : else if (std::abs(m_uc(i,iflag) - 2.0) < 1e-4) {
750 : : // hole: blank nodes
751 : 0 : m_blank[i] = 0.0;
752 : : }
753 : : else {
754 : : // do nothing
755 : 355403 : m_blank[i] = 1.0;
756 : : }
757 : :
758 : : }
759 : :
760 : : }
761 : 9311 : }
762 : :
763 : : void
764 : 9311 : OversetFE::setTransferFlags(
765 : : std::size_t dirn )
766 : : // *****************************************************************************
767 : : // Set flags informing solution transfer decisions
768 : : //! \param[in] dirn 0 if called from B to O, 1 if called from O to B
769 : : // *****************************************************************************
770 : : {
771 : : // Copy solution and reset flags
772 : : //TODO: index the flag in a better way
773 : 9311 : std::size_t iflag = m_uc.nprop()-1;
774 : :
775 [ + + ]: 399626 : for (std::size_t i=0; i<m_u.nunk(); ++i) {
776 [ + + ]: 2341890 : for (std::size_t c=0; c<m_u.nprop(); ++c) {
777 : 1951575 : m_uc(i,c) = m_u(i,c);
778 : : }
779 : : // Reset flags
780 : 390315 : m_uc(i,iflag) = 0.0;
781 : :
782 : : // reset blanking coefficient
783 : 390315 : m_blank[i] = 1.0;
784 : : }
785 : :
786 : : // Transfer flags for O to B are based on block-ids that are hardcoded
787 : : // TODO: remove hardcoding
788 : :
789 : : // Called from transfer-B-to-O
790 [ + + ]: 9311 : if (dirn == 0) {
791 [ + + ]: 9287 : if (Disc()->MeshId() != 0) {
792 : : // Overset meshes: assign appropriate values to flag
793 [ + + ][ + - ]: 1056 : for (auto i : m_farfieldbcnodes) m_uc(i,iflag) = 1.0;
794 : : }
795 : : }
796 : : // Called from transfer-O-to-B
797 : : else {
798 [ + + ]: 24 : if (Disc()->MeshId() != 0) {
799 : : // Overset meshes: assign appropriate values to flag
800 [ + + ]: 48 : for (const auto& [blid, ndset] : m_nodeblockid) {
801 [ + + ]: 36 : if (blid == 103) {
802 [ + + ][ + - ]: 7896 : for (auto i : ndset) m_uc(i,iflag) = 1.0;
803 : : }
804 [ - + ]: 24 : else if (blid == 104) {
805 [ - - ][ - - ]: 0 : for (auto i : ndset) m_uc(i,iflag) = 2.0;
806 : : }
807 : : }
808 : : }
809 : : }
810 : 9311 : }
811 : :
812 : : void
813 : 299 : OversetFE::normfinal()
814 : : // *****************************************************************************
815 : : // Finish computing dual-face and boundary point normals
816 : : // *****************************************************************************
817 : : {
818 [ + - ]: 299 : auto d = Disc();
819 : 299 : const auto& lid = d->Lid();
820 : :
821 : : // Combine own and communicated contributions to boundary point normals
822 [ + + ]: 302 : for (const auto& [s,norms] : m_bnormc) {
823 [ + - ]: 3 : auto& bnorms = m_bnorm[s];
824 [ + + ]: 92 : for (const auto& [p,n] : norms) {
825 [ + - ]: 89 : auto& norm = bnorms[p];
826 : 89 : norm[0] += n[0];
827 : 89 : norm[1] += n[1];
828 : 89 : norm[2] += n[2];
829 : 89 : norm[3] += n[3];
830 : : }
831 : : }
832 : 299 : tk::destroy( m_bnormc );
833 : :
834 : : // Divide summed point normals by the sum of inverse distance squared
835 [ + + ]: 307 : for (auto& [s,norms] : m_bnorm)
836 [ + + ]: 815 : for (auto& [p,n] : norms) {
837 : 807 : n[0] /= n[3];
838 : 807 : n[1] /= n[3];
839 : 807 : n[2] /= n[3];
840 [ - + ][ - - ]: 807 : Assert( (n[0]*n[0] + n[1]*n[1] + n[2]*n[2] - 1.0) <
[ - - ][ - - ]
841 : : 1.0e+3*std::numeric_limits< tk::real >::epsilon(),
842 : : "Non-unit normal" );
843 : : }
844 : :
845 : : // Replace global->local ids associated to boundary point normals
846 : 598 : decltype(m_bnorm) bnorm;
847 [ + + ]: 307 : for (auto& [s,norms] : m_bnorm) {
848 [ + - ]: 8 : auto& bnorms = bnorm[s];
849 [ + + ]: 815 : for (auto&& [g,n] : norms)
850 [ + - ][ + - ]: 807 : bnorms[ tk::cref_find(lid,g) ] = std::move(n);
851 : : }
852 : 299 : m_bnorm = std::move(bnorm);
853 : :
854 : : // Count contributions to chare-boundary edges
855 : : std::unordered_map< tk::UnsMesh::Edge, std::size_t,
856 : 598 : tk::UnsMesh::Hash<2>, tk::UnsMesh::Eq<2> > edge_node_count;
857 [ + + ]: 3777 : for (const auto& [c,edges] : d->EdgeCommMap())
858 [ + + ]: 23824 : for (const auto& e : edges)
859 [ + - ]: 20346 : ++edge_node_count[e];
860 : :
861 : : // Combine and weigh communicated contributions to dual-face normals
862 [ + + ]: 16242 : for (auto& [e,n] : m_dfnormc) {
863 [ + - ]: 15943 : const auto& dfn = tk::cref_find( m_dfnorm, e );
864 : 15943 : n[0] += dfn[0];
865 : 15943 : n[1] += dfn[1];
866 : 15943 : n[2] += dfn[2];
867 [ + - ]: 15943 : auto count = static_cast< tk::real >( tk::cref_find( edge_node_count, e ) );
868 : 15943 : auto factor = 1.0/(count + 1.0);
869 [ + + ]: 63772 : for (auto & x : n) x *= factor;
870 : : }
871 : :
872 : : // Generate list of unique edges
873 : 598 : tk::UnsMesh::EdgeSet uedge;
874 [ + + ]: 14509 : for (std::size_t p=0; p<m_u.nunk(); ++p)
875 [ + + ]: 145076 : for (auto q : tk::Around(m_psup,p))
876 [ + - ]: 130866 : uedge.insert( {p,q} );
877 : :
878 : : // Flatten edge list
879 [ + - ]: 299 : m_edgenode.resize( uedge.size() * 2 );
880 : 299 : std::size_t f = 0;
881 : 299 : const auto& gid = d->Gid();
882 [ + + ]: 65732 : for (auto&& [p,q] : uedge) {
883 [ - + ]: 65433 : if (gid[p] > gid[q]) {
884 : 0 : m_edgenode[f+0] = std::move(q);
885 : 0 : m_edgenode[f+1] = std::move(p);
886 : : } else {
887 : 65433 : m_edgenode[f+0] = std::move(p);
888 : 65433 : m_edgenode[f+1] = std::move(q);
889 : : }
890 : 65433 : f += 2;
891 : : }
892 : 299 : tk::destroy(uedge);
893 : :
894 : : // Convert dual-face normals to streamable (and vectorizable) data structure
895 [ + - ]: 299 : m_dfn.resize( m_edgenode.size() * 3 ); // 2 vectors per access
896 : : std::unordered_map< tk::UnsMesh::Edge, std::size_t,
897 : 598 : tk::UnsMesh::Hash<2>, tk::UnsMesh::Eq<2> > eid;
898 [ + + ]: 65732 : for (std::size_t e=0; e<m_edgenode.size()/2; ++e) {
899 : 65433 : auto p = m_edgenode[e*2+0];
900 : 65433 : auto q = m_edgenode[e*2+1];
901 [ + - ]: 65433 : eid[{p,q}] = e;
902 : 65433 : std::array< std::size_t, 2 > g{ gid[p], gid[q] };
903 [ + - ]: 65433 : auto n = tk::cref_find( m_dfnorm, g );
904 : : // figure out if this is an edge on the parallel boundary
905 [ + - ]: 65433 : auto nit = m_dfnormc.find( g );
906 [ + + ]: 65433 : auto m = ( nit != m_dfnormc.end() ) ? nit->second : n;
907 : 65433 : m_dfn[e*6+0] = n[0];
908 : 65433 : m_dfn[e*6+1] = n[1];
909 : 65433 : m_dfn[e*6+2] = n[2];
910 : 65433 : m_dfn[e*6+3] = m[0];
911 : 65433 : m_dfn[e*6+4] = m[1];
912 : 65433 : m_dfn[e*6+5] = m[2];
913 : : }
914 : :
915 : 299 : tk::destroy( m_dfnorm );
916 : 299 : tk::destroy( m_dfnormc );
917 : :
918 : : // Flatten edge id data structure
919 [ + - ]: 299 : m_edgeid.resize( m_psup.first.size() );
920 [ + + ]: 14509 : for (std::size_t p=0,k=0; p<m_u.nunk(); ++p)
921 [ + + ]: 145076 : for (auto q : tk::Around(m_psup,p))
922 [ + - ]: 130866 : m_edgeid[k++] = tk::cref_find( eid, {p,q} );
923 : 299 : }
924 : :
925 : : void
926 : 8988 : OversetFE::BC()
927 : : // *****************************************************************************
928 : : // Apply boundary conditions
929 : : // \details The following BC enforcement changes the initial condition or
930 : : //! updated solution (dependending on when it is called) to ensure strong
931 : : //! imposition of the BCs. This is a matter of choice. Another alternative is
932 : : //! to only apply BCs when computing fluxes at boundary faces, thereby only
933 : : //! weakly enforcing the BCs. The former is conventionally used in continunous
934 : : //! Galerkin finite element methods (such as OversetFE implements), whereas the
935 : : //! latter, in finite volume methods.
936 : : // *****************************************************************************
937 : : {
938 : 8988 : auto d = Disc();
939 : 8988 : const auto& coord = d->Coord();
940 : :
941 : 8988 : const auto& bcmesh = g_inputdeck.get< tag::bc >();
942 : :
943 [ + + ]: 17994 : for (const auto& bci : bcmesh) {
944 : 9006 : const auto& bcm = bci.get< tag::mesh >();
945 [ + + ]: 18012 : for (const auto& im : bcm) {
946 : : // only if this bc is meant for current mesh
947 [ + + ]: 9006 : if (im-1 == d->MeshId()) {
948 : :
949 : : // Query and match user-specified Dirichlet boundary conditions to side sets
950 : 8988 : const auto steady = g_inputdeck.get< tag::steady_state >();
951 [ - + ][ - - ]: 8988 : if (steady) for (auto& deltat : m_dtp) deltat *= rkcoef[m_stage];
952 [ + - ]: 17976 : m_dirbc = match( d->MeshId(), m_u.nprop(), d->T(), rkcoef[m_stage] * d->Dt(),
953 : 8988 : m_tp, m_dtp, d->Coord(), d->Lid(), m_bnode,
954 : 8988 : /* increment = */ false );
955 [ - + ][ - - ]: 8988 : if (steady) for (auto& deltat : m_dtp) deltat /= rkcoef[m_stage];
956 : :
957 : : // Apply Dirichlet BCs
958 [ + + ]: 188055 : for (const auto& [b,bc] : m_dirbc)
959 [ + + ]: 1074402 : for (ncomp_t c=0; c<m_u.nprop(); ++c)
960 [ + - ][ + - ]: 895335 : if (bc[c].first) m_u(b,c) = bc[c].second;
961 : :
962 : : // Apply symmetry BCs
963 [ + - ]: 8988 : g_cgpde[d->MeshId()].symbc( m_u, coord, m_bnorm, m_symbcnodes );
964 : :
965 : : // Apply farfield BCs
966 [ + + ][ + + ]: 8988 : if (bci.get< tag::farfield >().empty() || (d->MeshId() == 0)) {
[ + + ]
967 [ + - ]: 8979 : g_cgpde[d->MeshId()].farfieldbc( m_u, coord, m_bnorm, m_farfieldbcnodes );
968 : : }
969 : :
970 : : // Apply slip wall BCs
971 [ + - ]: 17976 : g_cgpde[d->MeshId()].slipwallbc( m_u, d->MeshVel(), coord, m_bnorm,
972 : 8988 : m_slipwallbcnodes );
973 : :
974 : : // Apply user defined time dependent BCs
975 [ + - ]: 17976 : g_cgpde[d->MeshId()].timedepbc( d->T(), m_u, m_timedepbcnodes,
976 : 8988 : m_timedepbcFn );
977 : : }
978 : : }
979 : : }
980 : 8988 : }
981 : :
982 : : void
983 : 2996 : OversetFE::UpdateCenterOfMass()
984 : : // *****************************************************************************
985 : : // Update quantities associated with the center of mass at a new time step
986 : : // *****************************************************************************
987 : : {
988 : 2996 : m_centMassn = m_centMass;
989 : 2996 : m_centMassVeln = m_centMassVel;
990 : 2996 : m_angVelMeshn = m_angVelMesh;
991 : 2996 : }
992 : :
993 : : void
994 : 2996 : OversetFE::next()
995 : : // *****************************************************************************
996 : : // Continue to next time step
997 : : // *****************************************************************************
998 : : {
999 : 2996 : dt();
1000 : 2996 : }
1001 : :
1002 : : void
1003 : 2996 : OversetFE::dt()
1004 : : // *****************************************************************************
1005 : : // Compute time step size
1006 : : // *****************************************************************************
1007 : : {
1008 : 2996 : tk::real mindt = std::numeric_limits< tk::real >::max();
1009 : :
1010 : 2996 : auto const_dt = g_inputdeck.get< tag::dt >();
1011 : 2996 : auto eps = std::numeric_limits< tk::real >::epsilon();
1012 : :
1013 [ + - ]: 2996 : auto d = Disc();
1014 : :
1015 : : // use constant dt if configured
1016 [ + + ]: 2996 : if (std::abs(const_dt) > eps) {
1017 : :
1018 : 2920 : mindt = const_dt;
1019 : :
1020 : : } else { // compute dt based on CFL
1021 : :
1022 : : //! [Find the minimum dt across all PDEs integrated]
1023 [ - + ]: 76 : if (g_inputdeck.get< tag::steady_state >()) {
1024 : :
1025 : : // compute new dt for each mesh point
1026 [ - - ]: 0 : g_cgpde[d->MeshId()].dt( d->It(), d->Vol(), m_u, m_dtp );
1027 : :
1028 : : // find the smallest dt of all nodes on this chare
1029 [ - - ]: 0 : mindt = *std::min_element( begin(m_dtp), end(m_dtp) );
1030 : :
1031 : : } else { // compute new dt for this chare
1032 : :
1033 : : // find the smallest dt of all equations on this chare
1034 [ + - ]: 228 : auto eqdt = g_cgpde[d->MeshId()].dt( d->Coord(), d->Inpoel(), d->T(),
1035 : 152 : d->Dtn(), m_u, d->Vol(), d->Voln(), m_srcFlag );
1036 [ + - ]: 76 : if (eqdt < mindt) mindt = eqdt;
1037 : :
1038 : : }
1039 : : //! [Find the minimum dt across all PDEs integrated]
1040 : :
1041 : : }
1042 : :
1043 : : //! [Advance]
1044 : : // Actiavate SDAG waits for next time step stage
1045 [ + - ][ + - ]: 2996 : thisProxy[ thisIndex ].wait4grad();
1046 [ + - ][ + - ]: 2996 : thisProxy[ thisIndex ].wait4rhs();
1047 : :
1048 : : // Compute own portion of force on boundary for overset mesh rigid body motion
1049 [ + - ]: 5992 : std::vector< tk::real > F(6, 0.0);
1050 : 2996 : if (g_inputdeck.get< tag::rigid_body_motion >().get< tag::rigid_body_movt >()
1051 [ - + ][ - - ]: 2996 : && d->MeshId() > 0) {
[ - + ]
1052 [ - - ]: 0 : g_cgpde[d->MeshId()].bndPressureInt( d->Coord(), m_triinpoel, m_slipwallbctri,
1053 : 0 : m_u, m_centMass, F );
1054 : : }
1055 : :
1056 : : // Tuple-reduction for min-dt and sum-F
1057 : 2996 : int tupleSize = 7;
1058 : : CkReduction::tupleElement advancingData[] = {
1059 : : CkReduction::tupleElement (sizeof(tk::real), &mindt, CkReduction::min_double),
1060 : 2996 : CkReduction::tupleElement (sizeof(tk::real), &F[0], CkReduction::sum_double),
1061 : 2996 : CkReduction::tupleElement (sizeof(tk::real), &F[1], CkReduction::sum_double),
1062 : 2996 : CkReduction::tupleElement (sizeof(tk::real), &F[2], CkReduction::sum_double),
1063 : 2996 : CkReduction::tupleElement (sizeof(tk::real), &F[3], CkReduction::sum_double),
1064 : 2996 : CkReduction::tupleElement (sizeof(tk::real), &F[4], CkReduction::sum_double),
1065 : 2996 : CkReduction::tupleElement (sizeof(tk::real), &F[5], CkReduction::sum_double)
1066 [ + - ][ + - ]: 26964 : };
[ + - ][ + - ]
[ + - ][ + - ]
[ + - ][ + + ]
[ - - ]
1067 : : CkReductionMsg* advMsg =
1068 [ + - ]: 2996 : CkReductionMsg::buildFromTuple(advancingData, tupleSize);
1069 : :
1070 : : // Contribute to minimum dt across all chares, find minimum dt across all
1071 : : // meshes, and eventually broadcast to OversetFE::advance()
1072 [ + - ][ + - ]: 5992 : CkCallback cb(CkReductionTarget(Transporter,collectDtAndForces), d->Tr());
1073 : 2996 : advMsg->setCallback(cb);
1074 [ + - ]: 2996 : contribute(advMsg);
1075 : : //! [Advance]
1076 : 2996 : }
1077 : :
1078 : : void
1079 : 2996 : OversetFE::advance( tk::real newdt, std::array< tk::real, 6 > F )
1080 : : // *****************************************************************************
1081 : : // Advance equations to next time step
1082 : : //! \param[in] newdt The smallest dt across the whole problem
1083 : : //! \param[in] F Total surface force on this mesh
1084 : : // *****************************************************************************
1085 : : {
1086 : 2996 : auto d = Disc();
1087 : :
1088 : : // Set new time step size
1089 [ + - ]: 2996 : if (m_stage == 0) d->setdt( newdt );
1090 : :
1091 [ + + ]: 11984 : for (std::size_t i=0; i<3; ++i) {
1092 : 8988 : m_surfForce[i] = F[i];
1093 : 8988 : m_surfTorque[i] = F[i+3];
1094 : : }
1095 : :
1096 : : // Compute gradients for next time step
1097 : 2996 : chBndGrad();
1098 : 2996 : }
1099 : :
1100 : : void
1101 : 8988 : OversetFE::chBndGrad()
1102 : : // *****************************************************************************
1103 : : // Compute nodal gradients at chare-boundary nodes. Gradients at internal nodes
1104 : : // are calculated locally as needed and are not stored.
1105 : : // *****************************************************************************
1106 : : {
1107 : 8988 : auto d = Disc();
1108 : :
1109 : : // Compute own portion of gradients for all equations
1110 : 17976 : g_cgpde[d->MeshId()].chBndGrad( d->Coord(), d->Inpoel(), m_bndel, d->Gid(),
1111 : 8988 : d->Bid(), m_u, m_chBndGrad );
1112 : :
1113 : : // Communicate gradients to other chares on chare-boundary
1114 [ + + ]: 8988 : if (d->NodeCommMap().empty()) // in serial we are done
1115 : 270 : comgrad_complete();
1116 : : else // send gradients contributions to chare-boundary nodes to fellow chares
1117 [ + + ]: 112788 : for (const auto& [c,n] : d->NodeCommMap()) {
1118 [ + - ]: 104070 : std::vector< std::vector< tk::real > > g( n.size() );
1119 : 104070 : std::size_t j = 0;
1120 [ + + ][ + - ]: 500496 : for (auto i : n) g[ j++ ] = m_chBndGrad[ tk::cref_find(d->Bid(),i) ];
[ + - ]
1121 [ + - ][ + - ]: 104070 : thisProxy[c].comChBndGrad( std::vector<std::size_t>(begin(n),end(n)), g );
[ + - ]
1122 : : }
1123 : :
1124 : 8988 : owngrad_complete();
1125 : 8988 : }
1126 : :
1127 : : void
1128 : 104070 : OversetFE::comChBndGrad( const std::vector< std::size_t >& gid,
1129 : : const std::vector< std::vector< tk::real > >& G )
1130 : : // *****************************************************************************
1131 : : // Receive contributions to nodal gradients on chare-boundaries
1132 : : //! \param[in] gid Global mesh node IDs at which we receive grad contributions
1133 : : //! \param[in] G Partial contributions of gradients to chare-boundary nodes
1134 : : //! \details This function receives contributions to m_chBndGrad, which stores
1135 : : //! nodal gradients at mesh chare-boundary nodes. While m_chBndGrad stores
1136 : : //! own contributions, m_chBndGradc collects the neighbor chare
1137 : : //! contributions during communication. This way work on m_chBndGrad and
1138 : : //! m_chBndGradc is overlapped. The two are combined in rhs().
1139 : : // *****************************************************************************
1140 : : {
1141 [ - + ][ - - ]: 104070 : Assert( G.size() == gid.size(), "Size mismatch" );
[ - - ][ - - ]
1142 : :
1143 : : using tk::operator+=;
1144 : :
1145 [ + + ]: 500496 : for (std::size_t i=0; i<gid.size(); ++i) m_chBndGradc[ gid[i] ] += G[i];
1146 : :
1147 [ + + ]: 104070 : if (++m_ngrad == Disc()->NodeCommMap().size()) {
1148 : 8718 : m_ngrad = 0;
1149 : 8718 : comgrad_complete();
1150 : : }
1151 : 104070 : }
1152 : :
1153 : : void
1154 : 8988 : OversetFE::rhs()
1155 : : // *****************************************************************************
1156 : : // Compute right-hand side of transport equations
1157 : : // *****************************************************************************
1158 : : {
1159 : 8988 : auto d = Disc();
1160 : :
1161 : : // Combine own and communicated contributions to nodal gradients
1162 [ + + ]: 172779 : for (const auto& [gid,g] : m_chBndGradc) {
1163 [ + - ]: 163791 : auto bid = tk::cref_find( d->Bid(), gid );
1164 [ + + ]: 2620656 : for (ncomp_t c=0; c<m_chBndGrad.nprop(); ++c)
1165 [ + - ]: 2456865 : m_chBndGrad(bid,c) += g[c];
1166 : : }
1167 : :
1168 : : // clear gradients receive buffer
1169 : 8988 : tk::destroy(m_chBndGradc);
1170 : :
1171 : 8988 : const auto steady = g_inputdeck.get< tag::steady_state >();
1172 : :
1173 : : // Compute own portion of right-hand side for all equations
1174 [ + + ]: 8988 : auto prev_rkcoef = m_stage == 0 ? 0.0 : rkcoef[m_stage-1];
1175 [ - + ]: 8988 : if (steady)
1176 [ - - ]: 0 : for (std::size_t p=0; p<m_tp.size(); ++p) m_tp[p] += prev_rkcoef * m_dtp[p];
1177 : 26964 : g_cgpde[d->MeshId()].rhs( d->T() + prev_rkcoef * d->Dt(), d->Coord(), d->Inpoel(),
1178 : 8988 : m_triinpoel, d->Gid(), d->Bid(), d->Lid(), m_dfn, m_psup, m_esup,
1179 : 8988 : m_symbctri, m_slipwallbctri, d->Vol(), m_edgenode, m_edgeid,
1180 : 8988 : m_boxnodes, m_chBndGrad, m_u, d->MeshVel(), m_tp, d->Boxvol(),
1181 : 8988 : m_rhs, m_srcFlag );
1182 [ - + ]: 8988 : if (steady)
1183 [ - - ]: 0 : for (std::size_t p=0; p<m_tp.size(); ++p) m_tp[p] -= prev_rkcoef * m_dtp[p];
1184 : :
1185 : : // Communicate rhs to other chares on chare-boundary
1186 [ + + ]: 8988 : if (d->NodeCommMap().empty()) // in serial we are done
1187 : 270 : comrhs_complete();
1188 : : else // send contributions of rhs to chare-boundary nodes to fellow chares
1189 [ + + ]: 112788 : for (const auto& [c,n] : d->NodeCommMap()) {
1190 [ + - ]: 104070 : std::vector< std::vector< tk::real > > r( n.size() );
1191 : 104070 : std::size_t j = 0;
1192 [ + + ][ + - ]: 500496 : for (auto i : n) r[ j++ ] = m_rhs[ tk::cref_find(d->Lid(),i) ];
[ + - ]
1193 [ + - ][ + - ]: 104070 : thisProxy[c].comrhs( std::vector<std::size_t>(begin(n),end(n)), r );
[ + - ]
1194 : : }
1195 : :
1196 : 8988 : ownrhs_complete();
1197 : 8988 : }
1198 : :
1199 : : void
1200 : 104070 : OversetFE::comrhs( const std::vector< std::size_t >& gid,
1201 : : const std::vector< std::vector< tk::real > >& R )
1202 : : // *****************************************************************************
1203 : : // Receive contributions to right-hand side vector on chare-boundaries
1204 : : //! \param[in] gid Global mesh node IDs at which we receive RHS contributions
1205 : : //! \param[in] R Partial contributions of RHS to chare-boundary nodes
1206 : : //! \details This function receives contributions to m_rhs, which stores the
1207 : : //! right hand side vector at mesh nodes. While m_rhs stores own
1208 : : //! contributions, m_rhsc collects the neighbor chare contributions during
1209 : : //! communication. This way work on m_rhs and m_rhsc is overlapped. The two
1210 : : //! are combined in solve().
1211 : : // *****************************************************************************
1212 : : {
1213 [ - + ][ - - ]: 104070 : Assert( R.size() == gid.size(), "Size mismatch" );
[ - - ][ - - ]
1214 : :
1215 : : using tk::operator+=;
1216 : :
1217 [ + + ]: 500496 : for (std::size_t i=0; i<gid.size(); ++i) m_rhsc[ gid[i] ] += R[i];
1218 : :
1219 : : // When we have heard from all chares we communicate with, this chare is done
1220 [ + + ]: 104070 : if (++m_nrhs == Disc()->NodeCommMap().size()) {
1221 : 8718 : m_nrhs = 0;
1222 : 8718 : comrhs_complete();
1223 : : }
1224 : 104070 : }
1225 : :
1226 : : void
1227 : 8988 : OversetFE::solve()
1228 : : // *****************************************************************************
1229 : : // Advance systems of equations
1230 : : // *****************************************************************************
1231 : : {
1232 : 8988 : auto d = Disc();
1233 : :
1234 : : // Combine own and communicated contributions to rhs
1235 [ + + ]: 172779 : for (const auto& b : m_rhsc) {
1236 [ + - ]: 163791 : auto lid = tk::cref_find( d->Lid(), b.first );
1237 [ + + ][ + - ]: 982746 : for (ncomp_t c=0; c<m_rhs.nprop(); ++c) m_rhs(lid,c) += b.second[c];
1238 : : }
1239 : :
1240 : : // clear receive buffer
1241 : 8988 : tk::destroy(m_rhsc);
1242 : :
1243 : : // Update state at time n
1244 [ + + ]: 8988 : if (m_stage == 0) {
1245 : 2996 : m_un = m_u;
1246 : 2996 : d->UpdateCoordn();
1247 : 2996 : UpdateCenterOfMass();
1248 : : }
1249 : :
1250 : : // Explicit time-stepping using RK3
1251 : 8988 : const auto steady = g_inputdeck.get< tag::steady_state >();
1252 [ + + ]: 360993 : for (std::size_t i=0; i<m_u.nunk(); ++i) {
1253 : : // time-step
1254 : 352005 : auto dtp = d->Dt();
1255 [ - + ]: 352005 : if (steady) dtp = m_dtp[i];
1256 : :
1257 [ + + ]: 2112030 : for (ncomp_t c=0; c<m_u.nprop(); ++c)
1258 : 3520050 : m_u(i,c) = m_un(i,c) + m_blank[i] * rkcoef[m_stage] * dtp * m_rhs(i,c)
1259 : 1760025 : / d->Vol()[i];
1260 : : }
1261 : :
1262 : : // Kinematics for rigid body motion of overset meshes
1263 : 8988 : if (g_inputdeck.get< tag::rigid_body_motion >().get< tag::rigid_body_movt >()
1264 [ - + ][ - - ]: 8988 : && d->MeshId() > 0) {
[ - + ]
1265 : :
1266 : : // Remove symmetry directions if 3 DOF motion
1267 : 0 : if (g_inputdeck.get< tag::rigid_body_motion >().get< tag::rigid_body_dof >()
1268 [ - - ]: 0 : == 3) {
1269 : :
1270 : : auto sym_dir =
1271 : 0 : g_inputdeck.get< tag::rigid_body_motion >().get< tag::symmetry_plane >();
1272 : :
1273 : 0 : m_surfForce[sym_dir] = 0.0;
1274 [ - - ]: 0 : for (std::size_t i=0; i<3; ++i) {
1275 [ - - ]: 0 : if (i != sym_dir) m_surfTorque[i] = 0.0;
1276 : : }
1277 : : }
1278 : :
1279 : : // Mark if mesh moved
1280 [ - - ][ - - ]: 0 : if (std::sqrt(tk::dot(m_surfForce, m_surfForce)) > 1e-12 ||
1281 [ - - ]: 0 : std::sqrt(tk::dot(m_surfTorque, m_surfTorque)) > 1e-12)
1282 : 0 : m_movedmesh = 1;
1283 : : else
1284 : 0 : m_movedmesh = 0;
1285 : :
1286 [ - - ]: 0 : if (m_movedmesh == 1) {
1287 : : auto mass_mesh =
1288 : 0 : g_inputdeck.get< tag::mesh >()[d->MeshId()].get< tag::mass >();
1289 : 0 : auto mI_mesh = g_inputdeck.get< tag::mesh >()[d->MeshId()].get<
1290 : 0 : tag::moment_of_inertia >();
1291 : 0 : auto dtp = rkcoef[m_stage] * d->Dt();
1292 : : auto sym_dir =
1293 : 0 : g_inputdeck.get< tag::rigid_body_motion >().get< tag::symmetry_plane >();
1294 : :
1295 : 0 : auto pi = 4.0*std::atan(1.0);
1296 : :
1297 : : // mesh acceleration
1298 : : std::array< tk::real, 3 > a_mesh;
1299 [ - - ]: 0 : for (std::size_t i=0; i<3; ++i) a_mesh[i] = m_surfForce[i] / mass_mesh;
1300 : 0 : auto alpha_mesh = m_surfTorque[sym_dir]/mI_mesh; // angular acceleration
1301 : :
1302 : 0 : auto& u_mesh = d->MeshVel();
1303 : :
1304 [ - - ]: 0 : for (std::size_t p=0; p<u_mesh.nunk(); ++p) {
1305 : :
1306 : : // rotation (this is currently only configured for planar motion)
1307 : : // ---------------------------------------------------------------------
1308 : : std::array< tk::real, 3 > rCM{{
1309 : 0 : d->Coordn()[0][p] - m_centMassn[0],
1310 : 0 : d->Coordn()[1][p] - m_centMassn[1],
1311 : 0 : d->Coordn()[2][p] - m_centMassn[2] }};
1312 : :
1313 : : // obtain tangential velocity
1314 : 0 : tk::real r_mag(0.0);
1315 [ - - ]: 0 : for (std::size_t i=0; i<3; ++i) {
1316 [ - - ]: 0 : if (i != sym_dir) r_mag += rCM[i]*rCM[i];
1317 : : }
1318 : 0 : r_mag = std::sqrt(r_mag);
1319 : 0 : auto a_tgt = alpha_mesh*r_mag;
1320 : :
1321 : : // get the other two directions
1322 : 0 : auto i1 = (sym_dir+1)%3;
1323 : 0 : auto i2 = (sym_dir+2)%3;
1324 : :
1325 : : // project tangential velocity to these two directions
1326 : 0 : auto theta = std::atan2(rCM[i2],rCM[i1]);
1327 : 0 : auto a1 = a_tgt*std::cos((pi/2.0)+theta);
1328 : 0 : auto a2 = a_tgt*std::sin((pi/2.0)+theta);
1329 : :
1330 : : // angle of rotation
1331 : 0 : auto dtheta = m_angVelMesh*dtp + 0.5*alpha_mesh*dtp*dtp;
1332 : :
1333 : : // add contribution of rotation to mesh displacement
1334 : 0 : std::array< tk::real, 3 > angles{{ 0, 0, 0 }};
1335 : 0 : angles[sym_dir] = dtheta * 180.0/pi;
1336 : 0 : tk::rotatePoint(angles, rCM);
1337 : :
1338 : : // rectilinear motion
1339 : : // ---------------------------------------------------------------------
1340 : :
1341 : : tk::real dsT, dsR;
1342 : :
1343 [ - - ]: 0 : for (std::size_t i=0; i<3; ++i) {
1344 : : // mesh displacement from translation
1345 : 0 : dsT = m_centMassVel[i]*dtp + 0.5*a_mesh[i]*dtp*dtp;
1346 : : // mesh displacement from rotation
1347 : 0 : dsR = rCM[i] + m_centMass[i] - d->Coordn()[i][p];
1348 : : // add both contributions
1349 : 0 : d->Coord()[i][p] = d->Coordn()[i][p] + dsT + dsR;
1350 : : // mesh velocity change from translation
1351 [ - - ]: 0 : u_mesh(p,i) += a_mesh[i]*dtp;
1352 : : }
1353 : :
1354 : : // add contribution of rotation to mesh velocity
1355 [ - - ]: 0 : u_mesh(p,i1) += a1*dtp;
1356 [ - - ]: 0 : u_mesh(p,i2) += a2*dtp;
1357 : : }
1358 : :
1359 : : // update angular velocity
1360 : 0 : m_angVelMesh = m_angVelMeshn + alpha_mesh*dtp;
1361 : :
1362 : : // move center of mass
1363 [ - - ]: 0 : for (std::size_t i=0; i<3; ++i) {
1364 : 0 : m_centMass[i] = m_centMassn[i] + m_centMassVel[i]*dtp + 0.5*a_mesh[i]*dtp*dtp;
1365 : 0 : m_centMassVel[i] = m_centMassVeln[i] + a_mesh[i]*dtp; // no rotational component
1366 : : }
1367 : : }
1368 : :
1369 : : }
1370 : : // Remove surface force values for background mesh
1371 [ + + ]: 8988 : else if (d->MeshId() == 0) {
1372 [ + + ]: 35916 : for (std::size_t i=0; i<3; ++i) m_surfForce[i] = 0.0;
1373 : : }
1374 : :
1375 : : // Apply boundary-conditions
1376 : 8988 : BC();
1377 : :
1378 : : // Increment Runge-Kutta stage counter
1379 : 8988 : ++m_stage;
1380 : :
1381 : : // Activate SDAG wait for next time step stage
1382 [ + - ]: 8988 : thisProxy[ thisIndex ].wait4grad();
1383 [ + - ]: 8988 : thisProxy[ thisIndex ].wait4rhs();
1384 : :
1385 : : // Compute diagnostics, and finish-up time step (if m_stage == 3)
1386 : 8988 : bool diag_computed(false);
1387 [ + + ]: 8988 : if (m_stage == 3) {
1388 : : // Compute diagnostics, e.g., residuals
1389 : 5992 : diag_computed = m_diag.compute( *d, m_u, m_un, m_surfForce, m_bnorm,
1390 : 2996 : m_symbcnodes, m_farfieldbcnodes,
1391 : 2996 : m_slipwallbcnodes );
1392 : : // Increase number of iterations and physical time
1393 : 2996 : d->next();
1394 : : // Advance physical time for local time stepping
1395 [ - + ]: 2996 : if (g_inputdeck.get< tag::steady_state >())
1396 [ - - ]: 0 : for (std::size_t i=0; i<m_u.nunk(); ++i) m_tp[i] += m_dtp[i];
1397 : : }
1398 : : // Continue to finish-up time-step-stage
1399 : : // Note: refine is called via a bcast if diag_computed == true
1400 [ + + ][ + - ]: 8988 : if (!diag_computed) refine( std::vector< tk::real >( m_u.nprop(), 1.0 ) );
[ + - ]
1401 : 8988 : }
1402 : :
1403 : : //! [Refine]
1404 : : void
1405 : 8988 : OversetFE::refine( const std::vector< tk::real >& l2res )
1406 : : // *****************************************************************************
1407 : : // Finish up end of time-step procedures and continue to moving mesh
1408 : : //! \param[in] l2res L2-norms of the residual for each scalar component
1409 : : //! computed across the whole problem
1410 : : // *****************************************************************************
1411 : : {
1412 : 8988 : auto d = Disc();
1413 : :
1414 [ + + ]: 8988 : if (m_stage == 3) {
1415 : 2996 : const auto steady = g_inputdeck.get< tag::steady_state >();
1416 : 2996 : const auto residual = g_inputdeck.get< tag::residual >();
1417 : 2996 : const auto rc = g_inputdeck.get< tag::rescomp >() - 1;
1418 : :
1419 [ - + ]: 2996 : if (m_movedmesh) {
1420 : 0 : d->Itf() = 0; // Zero field output iteration count if mesh moved
1421 : 0 : ++d->Itr(); // Increase number of iterations with a change in the mesh
1422 : : }
1423 : :
1424 [ - + ]: 2996 : if (steady) {
1425 : :
1426 : : // this is the last time step if max time of max number of time steps
1427 : : // reached or the residual has reached its convergence criterion
1428 [ - - ][ - - ]: 0 : if (d->finished() or l2res[rc] < residual) m_finished = 1;
[ - - ]
1429 : :
1430 : : } else {
1431 : :
1432 : : // this is the last time step if max time or max iterations reached
1433 [ + + ]: 2996 : if (d->finished()) m_finished = 1;
1434 : :
1435 : : }
1436 : : }
1437 : :
1438 [ - + ]: 8988 : if (m_movedmesh) {
1439 : : // Normals need to be recomputed if overset mesh has been moved
1440 [ - - ]: 0 : thisProxy[ thisIndex ].wait4norm();
1441 : : }
1442 : :
1443 : : // Start solution transfer
1444 : 8988 : transferSol();
1445 : 8988 : }
1446 : : //! [Refine]
1447 : :
1448 : : //! [stage]
1449 : : void
1450 : 8988 : OversetFE::stage()
1451 : : // *****************************************************************************
1452 : : // Evaluate whether to continue with next time step stage
1453 : : // *****************************************************************************
1454 : : {
1455 : : // if not all Runge-Kutta stages complete, continue to next time stage,
1456 : : // otherwise start next time step
1457 [ + + ]: 8988 : if (m_stage == 3) {
1458 : : // output field data and start with next time step
1459 : 2996 : out();
1460 : : }
1461 : : else {
1462 : : // start with next time-step stage
1463 : 5992 : chBndGrad();
1464 : : }
1465 : 8988 : }
1466 : : //! [stage]
1467 : :
1468 : : void
1469 : 602 : OversetFE::writeFields( CkCallback c )
1470 : : // *****************************************************************************
1471 : : // Output mesh-based fields to file
1472 : : //! \param[in] c Function to continue with after the write
1473 : : // *****************************************************************************
1474 : : {
1475 [ + + ]: 602 : if (g_inputdeck.get< tag::cmd, tag::benchmark >()) {
1476 : :
1477 : 584 : c.send();
1478 : :
1479 : : } else {
1480 : :
1481 [ + - ]: 18 : auto d = Disc();
1482 : 18 : const auto& coord = d->Coord();
1483 : :
1484 : : //// if coupled: depvars: src:'a', dst:'b','c',...
1485 : : //char depvar = 0;
1486 : : //if (not d->Transfers().empty()) {
1487 : : // depvar = 'a' + static_cast< char >( d->MeshId() );
1488 : : //}
1489 : :
1490 : : // Query fields names requested by user
1491 [ + - ]: 36 : auto nodefieldnames = numericFieldNames( tk::Centering::NODE );
1492 : :
1493 : : // Collect field output from numerical solution requested by user
1494 : 18 : auto nodefields = numericFieldOutput( m_u, tk::Centering::NODE,
1495 [ + - ][ + - ]: 36 : g_cgpde[Disc()->MeshId()].OutVarFn(), m_u );
[ + - ]
1496 : :
1497 : : // Collect field output names for analytical solutions
1498 [ + - ]: 18 : analyticFieldNames( g_cgpde[d->MeshId()], tk::Centering::NODE,
1499 : : nodefieldnames );
1500 : :
1501 : : // Collect field output from analytical solutions (if exist)
1502 [ + - ]: 36 : analyticFieldOutput( g_cgpde[d->MeshId()], tk::Centering::NODE, coord[0],
1503 : 18 : coord[1], coord[2], d->T(), nodefields );
1504 : :
1505 : : // Query and collect nodal block and surface field names from PDEs integrated
1506 : 36 : std::vector< std::string > nodesurfnames;
1507 [ + - ]: 36 : auto sn = g_cgpde[d->MeshId()].surfNames();
1508 [ + - ]: 18 : nodesurfnames.insert( end(nodesurfnames), begin(sn), end(sn) );
1509 : :
1510 : : // Collect nodal block and surface field solution
1511 : 36 : std::vector< std::vector< tk::real > > nodesurfs;
1512 : 18 : auto so = g_cgpde[d->MeshId()].surfOutput( tk::bfacenodes(m_bface,
1513 [ + - ][ + - ]: 36 : m_triinpoel), m_u );
1514 [ + - ]: 18 : nodesurfs.insert( end(nodesurfs), begin(so), end(so) );
1515 : :
1516 : : // Collect elemental block and surface field names from PDEs integrated
1517 [ + - ]: 36 : auto elemsurfnames = nodesurfnames;
1518 : :
1519 : : // Collect elemental block and surface field solution
1520 : 36 : std::vector< std::vector< tk::real > > elemsurfs;
1521 [ + - ]: 18 : auto eso = g_cgpde[d->MeshId()].elemSurfOutput( m_bface, m_triinpoel, m_u );
1522 [ + - ]: 18 : elemsurfs.insert( end(elemsurfs), begin(eso), end(eso) );
1523 : :
1524 [ - + ][ - - ]: 18 : Assert( nodefieldnames.size() == nodefields.size(), "Size mismatch" );
[ - - ][ - - ]
1525 : :
1526 : : // Send mesh and fields data (solution dump) for output to file
1527 [ + - ][ + - ]: 36 : d->write( d->Inpoel(), coord, m_bface, tk::remap(m_bnode,d->Lid()),
1528 : 18 : m_triinpoel, {}, nodefieldnames, elemsurfnames,
1529 : : nodesurfnames, {}, nodefields, elemsurfs, nodesurfs, c );
1530 : :
1531 : : }
1532 : 602 : }
1533 : :
1534 : : void
1535 : 2996 : OversetFE::out()
1536 : : // *****************************************************************************
1537 : : // Output mesh field data and continue to next time step
1538 : : // *****************************************************************************
1539 : : {
1540 : 2996 : auto d = Disc();
1541 : :
1542 : : // Output time history
1543 [ + - ][ + - ]: 2996 : if (d->histiter() or d->histtime() or d->histrange()) {
[ - + ][ - + ]
1544 : 0 : std::vector< std::vector< tk::real > > hist;
1545 [ - - ]: 0 : auto h = g_cgpde[d->MeshId()].histOutput( d->Hist(), d->Inpoel(), m_u );
1546 [ - - ]: 0 : hist.insert( end(hist), begin(h), end(h) );
1547 [ - - ]: 0 : d->history( std::move(hist) );
1548 : : }
1549 : :
1550 : : // Output field data
1551 [ + - ][ + + ]: 2996 : if (d->fielditer() or d->fieldtime() or d->fieldrange() or m_finished)
[ + - ][ + + ]
[ + + ]
1552 [ + - ][ + - ]: 303 : writeFields(CkCallback( CkIndex_OversetFE::step(), thisProxy[thisIndex]) );
[ + - ]
1553 : : else
1554 : 2693 : step();
1555 : 2996 : }
1556 : :
1557 : : void
1558 : 2697 : OversetFE::evalLB( int nrestart )
1559 : : // *****************************************************************************
1560 : : // Evaluate whether to do load balancing
1561 : : //! \param[in] nrestart Number of times restarted
1562 : : // *****************************************************************************
1563 : : {
1564 : 2697 : auto d = Disc();
1565 : :
1566 : : // Detect if just returned from a checkpoint and if so, zero timers and
1567 : : // finished flag
1568 [ - + ]: 2697 : if (d->restarted( nrestart )) m_finished = 0;
1569 : :
1570 : 2697 : const auto lbfreq = g_inputdeck.get< tag::cmd, tag::lbfreq >();
1571 : 2697 : const auto nonblocking = g_inputdeck.get< tag::cmd, tag::nonblocking >();
1572 : :
1573 : : // Load balancing if user frequency is reached or after the second time-step
1574 [ - + ][ - - ]: 2697 : if ( (d->It()) % lbfreq == 0 || d->It() == 2 ) {
[ + - ]
1575 : :
1576 : 2697 : AtSync();
1577 [ - + ]: 2697 : if (nonblocking) next();
1578 : :
1579 : : } else {
1580 : :
1581 : 0 : next();
1582 : :
1583 : : }
1584 : 2697 : }
1585 : :
1586 : : void
1587 : 2697 : OversetFE::evalRestart()
1588 : : // *****************************************************************************
1589 : : // Evaluate whether to save checkpoint/restart
1590 : : // *****************************************************************************
1591 : : {
1592 : 2697 : auto d = Disc();
1593 : :
1594 : 2697 : const auto rsfreq = g_inputdeck.get< tag::cmd, tag::rsfreq >();
1595 : 2697 : const auto benchmark = g_inputdeck.get< tag::cmd, tag::benchmark >();
1596 : :
1597 [ + + ][ - + ]: 2697 : if (not benchmark and not (d->It() % rsfreq)) {
[ - + ]
1598 : :
1599 [ - - ]: 0 : std::vector< std::size_t > meshdata{ /* finished = */ 0, d->MeshId() };
1600 [ - - ]: 0 : contribute( meshdata, CkReduction::nop,
1601 [ - - ][ - - ]: 0 : CkCallback(CkReductionTarget(Transporter,checkpoint), d->Tr()) );
1602 : :
1603 : : } else {
1604 : :
1605 : 2697 : evalLB( /* nrestart = */ -1 );
1606 : :
1607 : : }
1608 : 2697 : }
1609 : :
1610 : : void
1611 : 2996 : OversetFE::step()
1612 : : // *****************************************************************************
1613 : : // Evaluate whether to continue with next time step
1614 : : // *****************************************************************************
1615 : : {
1616 : 2996 : auto d = Disc();
1617 : :
1618 : : // Output one-liner status report to screen
1619 : 2996 : d->status();
1620 : : // Reset Runge-Kutta stage counter
1621 : 2996 : m_stage = 0;
1622 : :
1623 [ + + ]: 2996 : if (not m_finished) {
1624 : :
1625 : 2697 : evalRestart();
1626 : :
1627 : : } else {
1628 : :
1629 : 299 : auto meshid = d->MeshId();
1630 [ + - ]: 299 : d->contribute( sizeof(std::size_t), &meshid, CkReduction::nop,
1631 [ + - ][ + - ]: 598 : CkCallback(CkReductionTarget(Transporter,finish), d->Tr()) );
1632 : :
1633 : : }
1634 : 2996 : }
1635 : :
1636 : : #include "NoWarning/oversetfe.def.h"
|