Branch data Line data Source code
1 : : // *****************************************************************************
2 : : /*!
3 : : \file src/PDE/Reconstruction.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 Reconstruction for reconstructed discontinuous Galerkin methods
9 : : \details This file contains functions that reconstruct an "n"th order
10 : : polynomial to an "n+1"th order polynomial using a least-squares
11 : : reconstruction procedure.
12 : : */
13 : : // *****************************************************************************
14 : :
15 : : #include <array>
16 : : #include <vector>
17 : : #include <iostream>
18 : : #include <iomanip>
19 : :
20 : : #include "Vector.hpp"
21 : : #include "Around.hpp"
22 : : #include "Base/HashMapReducer.hpp"
23 : : #include "Reconstruction.hpp"
24 : : #include "Inciter/Options/PDE.hpp"
25 : : #include "MultiMat/MultiMatIndexing.hpp"
26 : : #include "Inciter/InputDeck/InputDeck.hpp"
27 : : #include "Limiter.hpp"
28 : : #include "Integrate/Mass.hpp"
29 : :
30 : : namespace inciter {
31 : : extern ctr::InputDeck g_inputdeck;
32 : : }
33 : :
34 : : namespace tk {
35 : :
36 : : void
37 : 2955200 : recoLeastSqExtStencil(
38 : : std::size_t rdof,
39 : : std::size_t e,
40 : : const std::map< std::size_t, std::vector< std::size_t > >& esup,
41 : : const std::vector< std::size_t >& inpoel,
42 : : const Fields& geoElem,
43 : : Fields& W,
44 : : const std::vector< std::size_t >& varList )
45 : : // *****************************************************************************
46 : : // \brief Reconstruct the second-order solution using least-squares approach
47 : : // from an extended stencil involving the node-neighbors
48 : : //! \param[in] rdof Maximum number of reconstructed degrees of freedom
49 : : //! \param[in] e Element whoes solution is being reconstructed
50 : : //! \param[in] esup Elements surrounding points
51 : : //! \param[in] inpoel Element-node connectivity
52 : : //! \param[in] geoElem Element geometry array
53 : : //! \param[in,out] W Solution vector to be reconstructed at recent time step
54 : : //! \param[in] varList List of indices in W, that need to be reconstructed
55 : : //! \details A second-order (piecewise linear) solution polynomial is obtained
56 : : //! from the first-order (piecewise constant) FV solutions by using a
57 : : //! least-squares (LS) reconstruction process. This LS reconstruction function
58 : : //! using the nodal-neighbors of a cell, to get an overdetermined system of
59 : : //! equations for the derivatives of the solution. This overdetermined system
60 : : //! is solved in the least-squares sense using the normal equations approach.
61 : : // *****************************************************************************
62 : : {
63 : : // lhs matrix
64 : : std::array< std::array< tk::real, 3 >, 3 >
65 : 2955200 : lhs_ls( {{ {{0.0, 0.0, 0.0}},
66 : : {{0.0, 0.0, 0.0}},
67 : : {{0.0, 0.0, 0.0}} }} );
68 : : // rhs matrix
69 : : std::vector< std::array< tk::real, 3 > >
70 [ + - ]: 5910400 : rhs_ls( varList.size(), {{ 0.0, 0.0, 0.0 }} );
71 : :
72 : : // loop over all nodes of the element e
73 [ + + ]: 14776000 : for (std::size_t lp=0; lp<4; ++lp)
74 : : {
75 : 11820800 : auto p = inpoel[4*e+lp];
76 [ + - ]: 11820800 : const auto& pesup = cref_find(esup, p);
77 : :
78 : : // loop over all the elements surrounding this node p
79 [ + + ]: 196580000 : for (auto er : pesup)
80 : : {
81 : : // centroid distance
82 [ + - ][ + - ]: 184759200 : std::array< real, 3 > wdeltax{{ geoElem(er,1)-geoElem(e,1),
83 [ + - ]: 184759200 : geoElem(er,2)-geoElem(e,2),
84 [ + - ][ + - ]: 369518400 : geoElem(er,3)-geoElem(e,3) }};
[ + - ]
85 : :
86 : : // contribute to lhs matrix
87 [ + + ]: 739036800 : for (std::size_t idir=0; idir<3; ++idir)
88 [ + + ]: 2217110400 : for (std::size_t jdir=0; jdir<3; ++jdir)
89 : 1662832800 : lhs_ls[idir][jdir] += wdeltax[idir] * wdeltax[jdir];
90 : :
91 : : // compute rhs matrix
92 [ + + ]: 950174100 : for (std::size_t i=0; i<varList.size(); i++)
93 : : {
94 : 765414900 : auto mark = varList[i]*rdof;
95 [ + + ]: 3061659600 : for (std::size_t idir=0; idir<3; ++idir)
96 : 2296244700 : rhs_ls[i][idir] +=
97 [ + - ][ + - ]: 2296244700 : wdeltax[idir] * (W(er,mark)-W(e,mark));
98 : :
99 : : }
100 : : }
101 : : }
102 : :
103 : : // solve least-square normal equation system using Cramer's rule
104 [ + + ]: 15160580 : for (std::size_t i=0; i<varList.size(); i++)
105 : : {
106 : 12205380 : auto mark = varList[i]*rdof;
107 : :
108 : 12205380 : auto ux = tk::cramer( lhs_ls, rhs_ls[i] );
109 : :
110 : : // Update the P1 dofs with the reconstructioned gradients.
111 : : // Since this reconstruction does not affect the cell-averaged solution,
112 : : // W(e,mark+0) is unchanged.
113 [ + - ]: 12205380 : W(e,mark+1) = ux[0];
114 [ + - ]: 12205380 : W(e,mark+2) = ux[1];
115 [ + - ]: 12205380 : W(e,mark+3) = ux[2];
116 : : }
117 : 2955200 : }
118 : :
119 : : void
120 : 2955200 : transform_P0P1( std::size_t rdof,
121 : : std::size_t e,
122 : : const std::vector< std::size_t >& inpoel,
123 : : const UnsMesh::Coords& coord,
124 : : Fields& W,
125 : : const std::vector< std::size_t >& varList )
126 : : // *****************************************************************************
127 : : // Transform the reconstructed P1-derivatives to the Dubiner dofs
128 : : //! \param[in] rdof Total number of reconstructed dofs
129 : : //! \param[in] e Element for which reconstruction is being calculated
130 : : //! \param[in] inpoel Element-node connectivity
131 : : //! \param[in] coord Array of nodal coordinates
132 : : //! \param[in,out] W Second-order reconstructed vector which gets transformed to
133 : : //! the Dubiner reference space
134 : : //! \param[in] varList List of indices in W, that need to be reconstructed
135 : : //! \details Since the DG solution (and the primitive quantities) are assumed to
136 : : //! be stored in the Dubiner space, this transformation from Taylor
137 : : //! coefficients to Dubiner coefficients is necessary.
138 : : // *****************************************************************************
139 : : {
140 : 2955200 : const auto& cx = coord[0];
141 : 2955200 : const auto& cy = coord[1];
142 : 2955200 : const auto& cz = coord[2];
143 : :
144 : : // Extract the element coordinates
145 : : std::array< std::array< real, 3>, 4 > coordel {{
146 : 8865600 : {{ cx[ inpoel[4*e ] ], cy[ inpoel[4*e ] ], cz[ inpoel[4*e ] ] }},
147 : 8865600 : {{ cx[ inpoel[4*e+1] ], cy[ inpoel[4*e+1] ], cz[ inpoel[4*e+1] ] }},
148 : 8865600 : {{ cx[ inpoel[4*e+2] ], cy[ inpoel[4*e+2] ], cz[ inpoel[4*e+2] ] }},
149 : 8865600 : {{ cx[ inpoel[4*e+3] ], cy[ inpoel[4*e+3] ], cz[ inpoel[4*e+3] ] }}
150 : 32507200 : }};
151 : :
152 : : auto jacInv =
153 : 2955200 : tk::inverseJacobian( coordel[0], coordel[1], coordel[2], coordel[3] );
154 : :
155 : : // Compute the derivatives of basis function for DG(P1)
156 [ + - ]: 5910400 : auto dBdx = tk::eval_dBdx_p1( rdof, jacInv );
157 : :
158 [ + + ]: 15160580 : for (std::size_t i=0; i<varList.size(); ++i)
159 : : {
160 : 12205380 : auto mark = varList[i]*rdof;
161 : :
162 : : // solve system using Cramer's rule
163 : 36616140 : auto ux = tk::cramer( {{ {{dBdx[0][1], dBdx[0][2], dBdx[0][3]}},
164 : 36616140 : {{dBdx[1][1], dBdx[1][2], dBdx[1][3]}},
165 : 36616140 : {{dBdx[2][1], dBdx[2][2], dBdx[2][3]}} }},
166 [ + - ]: 12205380 : {{ W(e,mark+1),
167 : 24410760 : W(e,mark+2),
168 [ + - ][ + - ]: 122053800 : W(e,mark+3) }} );
169 : :
170 : : // replace physical derivatives with transformed dofs
171 [ + - ]: 12205380 : W(e,mark+1) = ux[0];
172 [ + - ]: 12205380 : W(e,mark+2) = ux[1];
173 [ + - ]: 12205380 : W(e,mark+3) = ux[2];
174 : : }
175 : 2955200 : }
176 : :
177 : : void
178 : 6508700 : THINCReco( std::size_t rdof,
179 : : std::size_t nmat,
180 : : std::size_t e,
181 : : const std::vector< std::size_t >& inpoel,
182 : : const UnsMesh::Coords& coord,
183 : : const Fields& geoElem,
184 : : const std::array< real, 3 >& ref_xp,
185 : : const Fields& U,
186 : : const Fields& P,
187 : : bool intInd,
188 : : const std::vector< std::size_t >& matInt,
189 : : [[maybe_unused]] const std::vector< real >& vfmin,
190 : : [[maybe_unused]] const std::vector< real >& vfmax,
191 : : std::vector< real >& state )
192 : : // *****************************************************************************
193 : : // Compute THINC reconstructions at quadrature point for multi-material flows
194 : : //! \param[in] rdof Total number of reconstructed dofs
195 : : //! \param[in] nmat Total number of materials
196 : : //! \param[in] e Element for which interface reconstruction is being calculated
197 : : //! \param[in] inpoel Element-node connectivity
198 : : //! \param[in] coord Array of nodal coordinates
199 : : //! \param[in] geoElem Element geometry array
200 : : //! \param[in] ref_xp Quadrature point in reference space
201 : : //! \param[in] U Solution vector
202 : : //! \param[in] P Vector of primitives
203 : : //! \param[in] intInd Boolean which indicates if the element contains a
204 : : //! material interface
205 : : //! \param[in] matInt Array indicating which material has an interface
206 : : //! \param[in] vfmin Vector containing min volume fractions for each material
207 : : //! in this cell
208 : : //! \param[in] vfmax Vector containing max volume fractions for each material
209 : : //! in this cell
210 : : //! \param[in,out] state Unknown/state vector at quadrature point, modified
211 : : //! if near interfaces using THINC
212 : : //! \details This function is an interface for the multimat PDEs that use the
213 : : //! algebraic multi-material THINC reconstruction. This particular function
214 : : //! should only be called for multimat.
215 : : // *****************************************************************************
216 : : {
217 : : using inciter::volfracDofIdx;
218 : : using inciter::densityDofIdx;
219 : : using inciter::momentumDofIdx;
220 : : using inciter::energyDofIdx;
221 : : using inciter::pressureDofIdx;
222 : : using inciter::velocityDofIdx;
223 : : using inciter::deformDofIdx;
224 : : using inciter::stressDofIdx;
225 : : using inciter::volfracIdx;
226 : : using inciter::densityIdx;
227 : : using inciter::momentumIdx;
228 : : using inciter::energyIdx;
229 : : using inciter::pressureIdx;
230 : : using inciter::velocityIdx;
231 : : using inciter::deformIdx;
232 : : using inciter::stressIdx;
233 : :
234 : : auto bparam = inciter::g_inputdeck.get< tag::multimat,
235 : 6508700 : tag::intsharp_param >();
236 : 6508700 : const auto ncomp = U.nprop()/rdof;
237 : : const auto& solidx = inciter::g_inputdeck.get< tag::matidxmap,
238 : 6508700 : tag::solidx >();
239 : :
240 : : // Step-1: Perform THINC reconstruction
241 : : // create a vector of volume-fractions and pass it to the THINC function
242 [ + - ]: 13017400 : std::vector< real > alSol(rdof*nmat, 0.0);
243 [ + - ]: 13017400 : std::vector< real > alReco(nmat, 0.0);
244 [ + + ]: 19526100 : for (std::size_t k=0; k<nmat; ++k) {
245 : 13017400 : auto mark = k*rdof;
246 [ + + ]: 65087000 : for (std::size_t i=0; i<rdof; ++i) {
247 [ + - ]: 52069600 : alSol[mark+i] = U(e, volfracDofIdx(nmat,k,rdof,i));
248 : : }
249 : : // initialize with TVD reconstructions which will be modified if near
250 : : // material interface
251 : 13017400 : alReco[k] = state[volfracIdx(nmat,k)];
252 : : }
253 [ + - ][ + - ]: 6508700 : THINCFunction(rdof, nmat, e, inpoel, coord, ref_xp, geoElem(e,0), bparam,
254 : : alSol, intInd, matInt, alReco);
255 : :
256 : : // check reconstructed volfracs for positivity
257 : 6508700 : bool neg_vf = false;
258 [ + + ]: 19526100 : for (std::size_t k=0; k<nmat; ++k) {
259 [ - + ][ - - ]: 13017400 : if (alReco[k] < 1e-16 && matInt[k] > 0) neg_vf = true;
[ - + ]
260 : : }
261 [ + + ]: 19526100 : for (std::size_t k=0; k<nmat; ++k) {
262 [ - + ]: 13017400 : if (neg_vf) {
263 [ - - ][ - - ]: 0 : std::cout << "Material-id: " << k << std::endl;
[ - - ]
264 [ - - ][ - - ]: 0 : std::cout << "Volume-fraction: " << std::setprecision(18) << alReco[k]
[ - - ]
265 [ - - ]: 0 : << std::endl;
266 [ - - ][ - - ]: 0 : std::cout << "Cell-avg vol-frac: " << std::setprecision(18) <<
267 [ - - ][ - - ]: 0 : U(e,volfracDofIdx(nmat,k,rdof,0)) << std::endl;
[ - - ]
268 [ - - ][ - - ]: 0 : std::cout << "Material-interface? " << intInd << std::endl;
[ - - ]
269 [ - - ][ - - ]: 0 : std::cout << "Mat-k-involved? " << matInt[k] << std::endl;
[ - - ]
270 : : }
271 : : }
272 [ - + ][ - - ]: 6508700 : if (neg_vf) Throw("Material has negative volume fraction after THINC "
[ - - ][ - - ]
273 : : "reconstruction.");
274 : :
275 : : // Step-2: Perform consistent reconstruction on other conserved quantities
276 [ + + ]: 6508700 : if (intInd)
277 : : {
278 : 299614 : auto rhobCC(0.0), rhobHO(0.0);
279 [ + + ]: 898842 : for (std::size_t k=0; k<nmat; ++k)
280 : : {
281 [ + - ]: 599228 : auto alCC = U(e, volfracDofIdx(nmat,k,rdof,0));
282 : 599228 : alCC = std::max(1e-14, alCC);
283 : :
284 [ + - ]: 599228 : if (matInt[k])
285 : : {
286 : 599228 : state[volfracIdx(nmat,k)] = alReco[k];
287 : 1198456 : state[densityIdx(nmat,k)] = alReco[k]
288 [ + - ]: 599228 : * U(e, densityDofIdx(nmat,k,rdof,0))/alCC;
289 : 1198456 : state[energyIdx(nmat,k)] = alReco[k]
290 [ + - ]: 599228 : * U(e, energyDofIdx(nmat,k,rdof,0))/alCC;
291 : 1198456 : state[ncomp+pressureIdx(nmat,k)] = alReco[k]
292 [ + - ]: 599228 : * P(e, pressureDofIdx(nmat,k,rdof,0))/alCC;
293 [ - + ]: 599228 : if (solidx[k] > 0) {
294 [ - - ]: 0 : for (std::size_t i=0; i<3; ++i)
295 [ - - ]: 0 : for (std::size_t j=0; j<3; ++j)
296 : 0 : state[deformIdx(nmat,solidx[k],i,j)] =
297 [ - - ]: 0 : U(e, deformDofIdx(nmat,solidx[k],i,j,rdof,0));
298 : :
299 [ - - ]: 0 : for (std::size_t i=0; i<6; ++i)
300 : 0 : state[ncomp+stressIdx(nmat,solidx[k],i)] = alReco[k]
301 [ - - ]: 0 : * P(e, stressDofIdx(nmat,solidx[k],i,rdof,0))/alCC;
302 : : }
303 : : }
304 : :
305 [ + - ]: 599228 : rhobCC += U(e, densityDofIdx(nmat,k,rdof,0));
306 : 599228 : rhobHO += state[densityIdx(nmat,k)];
307 : : }
308 : :
309 : : // consistent reconstruction for bulk momentum
310 [ + + ]: 1198456 : for (std::size_t i=0; i<3; ++i)
311 : : {
312 : 898842 : state[momentumIdx(nmat,i)] = rhobHO
313 [ + - ]: 898842 : * U(e, momentumDofIdx(nmat,i,rdof,0))/rhobCC;
314 : 898842 : state[ncomp+velocityIdx(nmat,i)] =
315 [ + - ]: 898842 : P(e, velocityDofIdx(nmat,i,rdof,0));
316 : : }
317 : : }
318 : 6508700 : }
319 : :
320 : : void
321 : 0 : THINCRecoTransport( std::size_t rdof,
322 : : std::size_t,
323 : : std::size_t e,
324 : : const std::vector< std::size_t >& inpoel,
325 : : const UnsMesh::Coords& coord,
326 : : const Fields& geoElem,
327 : : const std::array< real, 3 >& ref_xp,
328 : : const Fields& U,
329 : : const Fields&,
330 : : [[maybe_unused]] const std::vector< real >& vfmin,
331 : : [[maybe_unused]] const std::vector< real >& vfmax,
332 : : std::vector< real >& state )
333 : : // *****************************************************************************
334 : : // Compute THINC reconstructions at quadrature point for transport
335 : : //! \param[in] rdof Total number of reconstructed dofs
336 : : //! \param[in] e Element for which interface reconstruction is being calculated
337 : : //! \param[in] inpoel Element-node connectivity
338 : : //! \param[in] coord Array of nodal coordinates
339 : : //! \param[in] geoElem Element geometry array
340 : : //! \param[in] ref_xp Quadrature point in reference space
341 : : //! \param[in] U Solution vector
342 : : //! \param[in] vfmin Vector containing min volume fractions for each material
343 : : //! in this cell
344 : : //! \param[in] vfmax Vector containing max volume fractions for each material
345 : : //! in this cell
346 : : //! \param[in,out] state Unknown/state vector at quadrature point, modified
347 : : //! if near interfaces using THINC
348 : : //! \details This function is an interface for the transport PDEs that use the
349 : : //! algebraic multi-material THINC reconstruction. This particular function
350 : : //! should only be called for transport.
351 : : // *****************************************************************************
352 : : {
353 : : auto bparam = inciter::g_inputdeck.get< tag::transport,
354 : 0 : tag::intsharp_param >();
355 : 0 : auto ncomp = U.nprop()/rdof;
356 : :
357 : : // interface detection
358 [ - - ]: 0 : std::vector< std::size_t > matInt(ncomp, 0);
359 [ - - ]: 0 : std::vector< tk::real > alAvg(ncomp, 0.0);
360 [ - - ]: 0 : for (std::size_t k=0; k<ncomp; ++k)
361 [ - - ]: 0 : alAvg[k] = U(e, k*rdof);
362 [ - - ]: 0 : auto intInd = inciter::interfaceIndicator(ncomp, alAvg, matInt);
363 : :
364 : : // create a vector of volume-fractions and pass it to the THINC function
365 [ - - ]: 0 : std::vector< real > alSol(rdof*ncomp, 0.0);
366 : : // initialize with TVD reconstructions (modified if near interface)
367 [ - - ]: 0 : auto alReco = state;
368 [ - - ]: 0 : for (std::size_t k=0; k<ncomp; ++k) {
369 : 0 : auto mark = k*rdof;
370 [ - - ]: 0 : for (std::size_t i=0; i<rdof; ++i) {
371 [ - - ]: 0 : alSol[mark+i] = U(e,mark+i);
372 : : }
373 : : }
374 [ - - ][ - - ]: 0 : THINCFunction(rdof, ncomp, e, inpoel, coord, ref_xp, geoElem(e,0), bparam,
375 : : alSol, intInd, matInt, alReco);
376 : :
377 [ - - ]: 0 : state = alReco;
378 : 0 : }
379 : :
380 : : void
381 : 6508700 : THINCFunction( std::size_t rdof,
382 : : std::size_t nmat,
383 : : std::size_t e,
384 : : const std::vector< std::size_t >& inpoel,
385 : : const UnsMesh::Coords& coord,
386 : : const std::array< real, 3 >& ref_xp,
387 : : real vol,
388 : : real bparam,
389 : : const std::vector< real >& alSol,
390 : : bool intInd,
391 : : const std::vector< std::size_t >& matInt,
392 : : std::vector< real >& alReco )
393 : : // *****************************************************************************
394 : : // Old version of the Multi-Medium THINC reconstruction function
395 : : //! \param[in] rdof Total number of reconstructed dofs
396 : : //! \param[in] nmat Total number of materials
397 : : //! \param[in] e Element for which interface reconstruction is being calculated
398 : : //! \param[in] inpoel Element-node connectivity
399 : : //! \param[in] coord Array of nodal coordinates
400 : : //! \param[in] ref_xp Quadrature point in reference space
401 : : //! \param[in] vol Element volume
402 : : //! \param[in] bparam User specified Beta for THINC, from the input file
403 : : //! \param[in] alSol Volume fraction solution vector for element e
404 : : //! \param[in] intInd Interface indicator, true if e is interface element
405 : : //! \param[in] matInt Vector indicating materials which constitute interface
406 : : //! \param[in,out] alReco Unknown/state vector at quadrature point, which gets
407 : : //! modified if near interface using MM-THINC
408 : : //! \details This function computes the interface reconstruction using the
409 : : //! algebraic multi-material THINC reconstruction for each material at the
410 : : //! given (ref_xp) quadrature point. This function is based on the following:
411 : : //! Pandare A. K., Waltz J., & Bakosi J. (2021) Multi-Material Hydrodynamics
412 : : //! with Algebraic Sharp Interface Capturing. Computers & Fluids,
413 : : //! doi: https://doi.org/10.1016/j.compfluid.2020.104804.
414 : : //! This function will be removed after the newer version (see
415 : : //! THINCFunction_new) is sufficiently tested.
416 : : // *****************************************************************************
417 : : {
418 : : // determine number of materials with interfaces in this cell
419 : 6508700 : auto epsl(1e-4), epsh(1e-1), bred(1.25), bmod(bparam);
420 : 6508700 : std::size_t nIntMat(0);
421 [ + + ]: 19526100 : for (std::size_t k=0; k<nmat; ++k)
422 : : {
423 : 13017400 : auto alk = alSol[k*rdof];
424 [ + + ]: 13017400 : if (alk > epsl)
425 : : {
426 : 6754951 : ++nIntMat;
427 [ + - ][ + + ]: 6754951 : if ((alk > epsl) && (alk < epsh))
428 : 185863 : bmod = std::min(bmod,
429 : 185863 : (alk-epsl)/(epsh-epsl) * (bred - bparam) + bparam);
430 [ + - ]: 6569088 : else if (alk > epsh)
431 : 6569088 : bmod = bred;
432 : : }
433 : : }
434 : :
435 [ - + ]: 6508700 : if (nIntMat > 2) bparam = bmod;
436 : :
437 : : // compression parameter
438 : 6508700 : auto beta = bparam/std::cbrt(6.0*vol);
439 : :
440 [ + + ]: 6508700 : if (intInd)
441 : : {
442 : : // 1. Get unit normals to material interface
443 : :
444 : : // Compute Jacobian matrix for converting Dubiner dofs to derivatives
445 : 299614 : const auto& cx = coord[0];
446 : 299614 : const auto& cy = coord[1];
447 : 299614 : const auto& cz = coord[2];
448 : :
449 : : std::array< std::array< real, 3>, 4 > coordel {{
450 : 898842 : {{ cx[ inpoel[4*e ] ], cy[ inpoel[4*e ] ], cz[ inpoel[4*e ] ] }},
451 : 898842 : {{ cx[ inpoel[4*e+1] ], cy[ inpoel[4*e+1] ], cz[ inpoel[4*e+1] ] }},
452 : 898842 : {{ cx[ inpoel[4*e+2] ], cy[ inpoel[4*e+2] ], cz[ inpoel[4*e+2] ] }},
453 : 898842 : {{ cx[ inpoel[4*e+3] ], cy[ inpoel[4*e+3] ], cz[ inpoel[4*e+3] ] }}
454 : 3295754 : }};
455 : :
456 : : auto jacInv =
457 : 299614 : tk::inverseJacobian( coordel[0], coordel[1], coordel[2], coordel[3] );
458 : :
459 [ + - ]: 599228 : auto dBdx = tk::eval_dBdx_p1( rdof, jacInv );
460 : :
461 : : std::array< real, 3 > nInt;
462 [ + - ]: 599228 : std::vector< std::array< real, 3 > > ref_n(nmat, {{0.0, 0.0, 0.0}});
463 : :
464 : : // Get normals
465 [ + + ]: 898842 : for (std::size_t k=0; k<nmat; ++k)
466 : : {
467 : : // Get derivatives from moments in Dubiner space
468 [ + + ]: 2396912 : for (std::size_t i=0; i<3; ++i)
469 : 3595368 : nInt[i] = dBdx[i][1] * alSol[k*rdof+1]
470 : 1797684 : + dBdx[i][2] * alSol[k*rdof+2]
471 : 1797684 : + dBdx[i][3] * alSol[k*rdof+3];
472 : :
473 : 599228 : auto nMag = std::sqrt(tk::dot(nInt, nInt)) + 1e-14;
474 : :
475 [ + + ]: 2396912 : for (std::size_t i=0; i<3; ++i)
476 : 1797684 : nInt[i] /= nMag;
477 : :
478 : : // project interface normal onto local/reference coordinate system
479 [ + + ]: 2396912 : for (std::size_t i=0; i<3; ++i)
480 : : {
481 : : std::array< real, 3 > axis{
482 : 1797684 : coordel[i+1][0]-coordel[0][0],
483 : 1797684 : coordel[i+1][1]-coordel[0][1],
484 : 3595368 : coordel[i+1][2]-coordel[0][2] };
485 : 1797684 : ref_n[k][i] = tk::dot(nInt, axis);
486 : : }
487 : : }
488 : :
489 : : // 2. Reconstruct volume fractions using THINC
490 : 299614 : auto max_lim = 1.0 - (static_cast<tk::real>(nmat-1)*1.0e-12);
491 : 299614 : auto min_lim = 1e-12;
492 : 299614 : auto sum_inter(0.0), sum_non_inter(0.0);
493 [ + + ]: 898842 : for (std::size_t k=0; k<nmat; ++k)
494 : : {
495 [ + - ]: 599228 : if (matInt[k])
496 : : {
497 : : // get location of material interface (volume fraction 0.5) from the
498 : : // assumed tanh volume fraction distribution, and cell-averaged
499 : : // volume fraction
500 : 599228 : auto alCC(alSol[k*rdof]);
501 : 599228 : auto Ac(0.0), Bc(0.0), Qc(0.0);
502 : 599228 : if ((std::abs(ref_n[k][0]) > std::abs(ref_n[k][1]))
503 [ + + ][ + + ]: 599228 : && (std::abs(ref_n[k][0]) > std::abs(ref_n[k][2])))
[ + + ]
504 : : {
505 : 245734 : Ac = std::exp(0.5*beta*ref_n[k][0]);
506 : 245734 : Bc = std::exp(0.5*beta*(ref_n[k][1]+ref_n[k][2]));
507 : 245734 : Qc = std::exp(0.5*beta*ref_n[k][0]*(2.0*alCC-1.0));
508 : : }
509 : 353494 : else if ((std::abs(ref_n[k][1]) > std::abs(ref_n[k][0]))
510 [ + + ][ + + ]: 353494 : && (std::abs(ref_n[k][1]) > std::abs(ref_n[k][2])))
[ + + ]
511 : : {
512 : 262586 : Ac = std::exp(0.5*beta*ref_n[k][1]);
513 : 262586 : Bc = std::exp(0.5*beta*(ref_n[k][0]+ref_n[k][2]));
514 : 262586 : Qc = std::exp(0.5*beta*ref_n[k][1]*(2.0*alCC-1.0));
515 : : }
516 : : else
517 : : {
518 : 90908 : Ac = std::exp(0.5*beta*ref_n[k][2]);
519 : 90908 : Bc = std::exp(0.5*beta*(ref_n[k][0]+ref_n[k][1]));
520 : 90908 : Qc = std::exp(0.5*beta*ref_n[k][2]*(2.0*alCC-1.0));
521 : : }
522 : 599228 : auto d = std::log((1.0-Ac*Qc) / (Ac*Bc*(Qc-Ac))) / (2.0*beta);
523 : :
524 : : // THINC reconstruction
525 : 599228 : auto al_c = 0.5 * (1.0 + std::tanh(beta*(tk::dot(ref_n[k], ref_xp) + d)));
526 : :
527 : 599228 : alReco[k] = std::min(max_lim, std::max(min_lim, al_c));
528 : :
529 : 599228 : sum_inter += alReco[k];
530 : : } else
531 : : {
532 : 0 : sum_non_inter += alReco[k];
533 : : }
534 : : // else, if this material does not have an interface close-by, the TVD
535 : : // reconstructions must be used for state variables. This is ensured by
536 : : // initializing the alReco vector as the TVD state.
537 : : }
538 : :
539 : : // Rescale volume fractions of interface-materials to ensure unit sum
540 : 299614 : auto sum_rest = 1.0 - sum_non_inter;
541 [ + + ]: 898842 : for (std::size_t k=0; k<nmat; ++k)
542 [ + - ]: 599228 : if(matInt[k])
543 : 599228 : alReco[k] = alReco[k] * sum_rest / sum_inter;
544 : : }
545 : 6508700 : }
546 : :
547 : : void
548 : 0 : THINCFunction_new( std::size_t rdof,
549 : : std::size_t nmat,
550 : : std::size_t e,
551 : : const std::vector< std::size_t >& inpoel,
552 : : const UnsMesh::Coords& coord,
553 : : const std::array< real, 3 >& ref_xp,
554 : : real vol,
555 : : real bparam,
556 : : const std::vector< real >& alSol,
557 : : bool intInd,
558 : : const std::vector< std::size_t >& matInt,
559 : : std::vector< real >& alReco )
560 : : // *****************************************************************************
561 : : // New Multi-Medium THINC reconstruction function for volume fractions
562 : : //! \param[in] rdof Total number of reconstructed dofs
563 : : //! \param[in] nmat Total number of materials
564 : : //! \param[in] e Element for which interface reconstruction is being calculated
565 : : //! \param[in] inpoel Element-node connectivity
566 : : //! \param[in] coord Array of nodal coordinates
567 : : //! \param[in] ref_xp Quadrature point in reference space
568 : : //! \param[in] vol Element volume
569 : : //! \param[in] bparam User specified Beta for THINC, from the input file
570 : : //! \param[in] alSol Volume fraction solution vector for element e
571 : : //! \param[in] intInd Interface indicator, true if e is interface element
572 : : //! \param[in] matInt Vector indicating materials which constitute interface
573 : : //! \param[in,out] alReco Unknown/state vector at quadrature point, which gets
574 : : //! modified if near interface using MM-THINC
575 : : //! \details This function computes the interface reconstruction using the
576 : : //! algebraic multi-material THINC reconstruction for each material at the
577 : : //! given (ref_xp) quadrature point. This function succeeds the older version
578 : : //! of the mm-THINC (see THINCFunction), but is still under testing and is
579 : : //! currently experimental.
580 : : // *****************************************************************************
581 : : {
582 : : // compression parameter
583 : 0 : auto beta = bparam/std::cbrt(6.0*vol);
584 : :
585 : : // If the cell is not material interface, return this function
586 [ - - ]: 0 : if (not intInd) return;
587 : :
588 : : // If the cell is material interface, THINC reconstruction is applied
589 : : // Step 1. Get unit normals to material interface
590 : : // -------------------------------------------------------------------------
591 : :
592 : : // Compute Jacobian matrix for converting Dubiner dofs to derivatives
593 : 0 : const auto& cx = coord[0];
594 : 0 : const auto& cy = coord[1];
595 : 0 : const auto& cz = coord[2];
596 : :
597 : : std::array< std::array< real, 3>, 4 > coordel {{
598 : 0 : {{ cx[ inpoel[4*e ] ], cy[ inpoel[4*e ] ], cz[ inpoel[4*e ] ] }},
599 : 0 : {{ cx[ inpoel[4*e+1] ], cy[ inpoel[4*e+1] ], cz[ inpoel[4*e+1] ] }},
600 : 0 : {{ cx[ inpoel[4*e+2] ], cy[ inpoel[4*e+2] ], cz[ inpoel[4*e+2] ] }},
601 : 0 : {{ cx[ inpoel[4*e+3] ], cy[ inpoel[4*e+3] ], cz[ inpoel[4*e+3] ] }}
602 : 0 : }};
603 : :
604 : : auto jacInv =
605 : 0 : tk::inverseJacobian( coordel[0], coordel[1], coordel[2], coordel[3] );
606 : :
607 [ - - ]: 0 : auto dBdx = tk::eval_dBdx_p1( rdof, jacInv );
608 : :
609 : : std::array< real, 3 > nInt;
610 : 0 : std::array< real, 3 > ref_n{0.0, 0.0, 0.0};
611 : 0 : auto almax(0.0);
612 : 0 : std::size_t kmax(0);
613 : :
614 : : // Determine index of material present in majority
615 [ - - ]: 0 : for (std::size_t k=0; k<nmat; ++k)
616 : : {
617 : 0 : auto alk = alSol[k*rdof];
618 [ - - ]: 0 : if (alk > almax)
619 : : {
620 : 0 : almax = alk;
621 : 0 : kmax = k;
622 : : }
623 : : }
624 : :
625 : : // Get normals of material present in majority
626 : : // Get derivatives from moments in Dubiner space
627 [ - - ]: 0 : for (std::size_t i=0; i<3; ++i)
628 : 0 : nInt[i] = dBdx[i][1] * alSol[kmax*rdof+1]
629 : 0 : + dBdx[i][2] * alSol[kmax*rdof+2]
630 : 0 : + dBdx[i][3] * alSol[kmax*rdof+3];
631 : :
632 : 0 : auto nMag = std::sqrt(tk::dot(nInt, nInt)) + 1e-14;
633 : :
634 [ - - ]: 0 : for (std::size_t i=0; i<3; ++i)
635 : 0 : nInt[i] /= nMag;
636 : :
637 : : // project interface normal onto local/reference coordinate system
638 [ - - ]: 0 : for (std::size_t i=0; i<3; ++i)
639 : : {
640 : : std::array< real, 3 > axis{
641 : 0 : coordel[i+1][0]-coordel[0][0],
642 : 0 : coordel[i+1][1]-coordel[0][1],
643 : 0 : coordel[i+1][2]-coordel[0][2] };
644 : 0 : ref_n[i] = tk::dot(nInt, axis);
645 : : }
646 : :
647 : : // Step 2. Reconstruct volume fraction of majority material using THINC
648 : : // -------------------------------------------------------------------------
649 : :
650 : 0 : auto al_max = 1.0 - (static_cast<tk::real>(nmat-1)*1.0e-12);
651 : 0 : auto al_min = 1e-12;
652 : 0 : auto alsum(0.0);
653 : : // get location of material interface (volume fraction 0.5) from the
654 : : // assumed tanh volume fraction distribution, and cell-averaged
655 : : // volume fraction
656 : 0 : auto alCC(alSol[kmax*rdof]);
657 : 0 : auto Ac(0.0), Bc(0.0), Qc(0.0);
658 : 0 : if ((std::abs(ref_n[0]) > std::abs(ref_n[1]))
659 [ - - ][ - - ]: 0 : && (std::abs(ref_n[0]) > std::abs(ref_n[2])))
[ - - ]
660 : : {
661 : 0 : Ac = std::exp(0.5*beta*ref_n[0]);
662 : 0 : Bc = std::exp(0.5*beta*(ref_n[1]+ref_n[2]));
663 : 0 : Qc = std::exp(0.5*beta*ref_n[0]*(2.0*alCC-1.0));
664 : : }
665 : 0 : else if ((std::abs(ref_n[1]) > std::abs(ref_n[0]))
666 [ - - ][ - - ]: 0 : && (std::abs(ref_n[1]) > std::abs(ref_n[2])))
[ - - ]
667 : : {
668 : 0 : Ac = std::exp(0.5*beta*ref_n[1]);
669 : 0 : Bc = std::exp(0.5*beta*(ref_n[0]+ref_n[2]));
670 : 0 : Qc = std::exp(0.5*beta*ref_n[1]*(2.0*alCC-1.0));
671 : : }
672 : : else
673 : : {
674 : 0 : Ac = std::exp(0.5*beta*ref_n[2]);
675 : 0 : Bc = std::exp(0.5*beta*(ref_n[0]+ref_n[1]));
676 : 0 : Qc = std::exp(0.5*beta*ref_n[2]*(2.0*alCC-1.0));
677 : : }
678 : 0 : auto d = std::log((1.0-Ac*Qc) / (Ac*Bc*(Qc-Ac))) / (2.0*beta);
679 : :
680 : : // THINC reconstruction
681 : 0 : auto al_c = 0.5 * (1.0 + std::tanh(beta*(tk::dot(ref_n, ref_xp) + d)));
682 : :
683 : 0 : alReco[kmax] = std::min(al_max, std::max(al_min, al_c));
684 : 0 : alsum += alReco[kmax];
685 : :
686 : : // if this material does not have an interface close-by, the TVD
687 : : // reconstructions must be used for state variables. This is ensured by
688 : : // initializing the alReco vector as the TVD state.
689 [ - - ]: 0 : for (std::size_t k=0; k<nmat; ++k) {
690 [ - - ]: 0 : if (!matInt[k]) {
691 : 0 : alsum += alReco[k];
692 : : }
693 : : }
694 : :
695 : : // Step 3. Do multimaterial cell corrections
696 : : // -------------------------------------------------------------------------
697 : :
698 : : // distribute remaining volume to rest of materials
699 : 0 : auto sum_left = 1.0 - alsum;
700 : 0 : real den = 0.0;
701 [ - - ]: 0 : for (std::size_t k=0; k<nmat; ++k) {
702 [ - - ][ - - ]: 0 : if (matInt[k] && k != kmax) {
[ - - ]
703 : 0 : auto mark = k * rdof;
704 : 0 : alReco[k] = sum_left * alSol[mark];
705 : 0 : den += alSol[mark];
706 : : }
707 : : }
708 : : // the distributed volfracs might be below al_min, correct that
709 : 0 : real err = 0.0;
710 [ - - ]: 0 : for (std::size_t k=0; k<nmat; ++k) {
711 [ - - ][ - - ]: 0 : if (matInt[k] && k != kmax) {
[ - - ]
712 : 0 : alReco[k] /= den;
713 [ - - ]: 0 : if (alReco[k] < al_min) {
714 : 0 : err += al_min - alReco[k];
715 : 0 : alReco[k] = al_min;
716 : : }
717 : : }
718 : : }
719 : :
720 : : // balance out errors
721 : 0 : alReco[kmax] -= err;
722 : : }
723 : :
724 : : void
725 : 0 : computeTemperaturesFV(
726 : : const std::vector< inciter::EOS >& mat_blk,
727 : : std::size_t nmat,
728 : : const std::vector< std::size_t >& inpoel,
729 : : const tk::UnsMesh::Coords& coord,
730 : : const tk::Fields& geoElem,
731 : : const tk::Fields& unk,
732 : : const tk::Fields& prim,
733 : : const std::vector< int >& srcFlag,
734 : : tk::Fields& T )
735 : : // *****************************************************************************
736 : : // Compute the temperatures based on FV conserved quantities
737 : : //! \param[in] mat_blk EOS material block
738 : : //! \param[in] nmat Number of materials in this PDE system
739 : : //! \param[in] inpoel Element-node connectivity
740 : : //! \param[in] coord Array of nodal coordinates
741 : : //! \param[in] geoElem Element geometry array
742 : : //! \param[in] unk Array of conservative variables
743 : : //! \param[in] prim Array of primitive variables
744 : : //! \param[in] srcFlag Whether the energy source was added
745 : : //! \param[in,out] T Array of material temperature dofs
746 : : //! \details This function computes the dofs of material temperatures based on
747 : : //! conservative quantities from an FV scheme, using EOS calls. It uses the
748 : : //! weak form m_{ij} T_i = \int T_{EOS}(rho, rhoE, u) B_j.
749 : : // *****************************************************************************
750 : : {
751 : 0 : const auto rdof = inciter::g_inputdeck.get< tag::rdof >();
752 : 0 : std::size_t ncomp = unk.nprop()/rdof;
753 : 0 : std::size_t nprim = prim.nprop()/rdof;
754 : : const auto intsharp = inciter::g_inputdeck.get< tag::multimat,
755 : 0 : tag::intsharp >();
756 : 0 : auto nelem = unk.nunk();
757 : :
758 [ - - ]: 0 : for (std::size_t e=0; e<nelem; ++e) {
759 : : // Here we pre-compute the left-hand-side (mass) matrix. The lhs in
760 : : // DG.cpp is not used here because the size of the mass matrix in this
761 : : // projection procedure should be rdof instead of ndof.
762 [ - - ][ - - ]: 0 : auto L = tk::massMatrixDubiner(rdof, geoElem(e,0));
763 : :
764 [ - - ]: 0 : std::vector< tk::real > R(nmat*rdof, 0.0);
765 : :
766 : 0 : std::size_t ng = 11;
767 : :
768 : : // Arrays for quadrature points
769 : 0 : std::array< std::vector< tk::real >, 3 > coordgp;
770 : 0 : std::vector< tk::real > wgp;
771 : :
772 [ - - ]: 0 : coordgp[0].resize( ng );
773 [ - - ]: 0 : coordgp[1].resize( ng );
774 [ - - ]: 0 : coordgp[2].resize( ng );
775 [ - - ]: 0 : wgp.resize( ng );
776 : :
777 [ - - ]: 0 : tk::GaussQuadratureTet( ng, coordgp, wgp );
778 : :
779 : : // Loop over quadrature points in element e
780 [ - - ]: 0 : for (std::size_t igp=0; igp<ng; ++igp) {
781 : : // Compute the basis function
782 : 0 : auto B = tk::eval_basis( rdof, coordgp[0][igp], coordgp[1][igp],
783 [ - - ]: 0 : coordgp[2][igp] );
784 : :
785 [ - - ]: 0 : auto w = wgp[igp] * geoElem(e, 0);
786 : :
787 : : // Evaluate the solution at quadrature point
788 : : auto state = evalFVSol(mat_blk, intsharp, ncomp, nprim, rdof,
789 : : nmat, e, inpoel, coord, geoElem,
790 : 0 : {{coordgp[0][igp], coordgp[1][igp], coordgp[2][igp]}}, B, unk, prim,
791 [ - - ]: 0 : srcFlag[e]);
792 : :
793 : : // Velocity vector at quadrature point
794 : : std::array< tk::real, 3 >
795 : 0 : vel{ state[ncomp+inciter::velocityIdx(nmat, 0)],
796 : 0 : state[ncomp+inciter::velocityIdx(nmat, 1)],
797 : 0 : state[ncomp+inciter::velocityIdx(nmat, 2)] };
798 : :
799 : : // Evaluate the right-hand-side vector (for temperature)
800 [ - - ]: 0 : for(std::size_t k=0; k<nmat; k++) {
801 [ - - ]: 0 : auto tk = mat_blk[k].compute< inciter::EOS::temperature >(
802 : 0 : state[inciter::densityIdx(nmat, k)], vel[0], vel[1], vel[2],
803 : 0 : state[inciter::energyIdx(nmat, k)],
804 : 0 : state[inciter::volfracIdx(nmat, k)] );
805 : 0 : auto mark = k * rdof;
806 [ - - ]: 0 : for(std::size_t idof=0; idof<rdof; idof++)
807 : 0 : R[mark+idof] += w * tk * B[idof];
808 : : }
809 : : }
810 : :
811 : : // Update the high order dofs of the temperature
812 [ - - ]: 0 : for(std::size_t k=0; k<nmat; k++) {
813 : 0 : auto mark = k * rdof;
814 [ - - ]: 0 : for(std::size_t idof=1; idof<rdof; idof++)
815 [ - - ]: 0 : T(e, mark+idof) = R[mark+idof] / L[idof];
816 : : }
817 : : }
818 : 0 : }
819 : :
820 : : std::vector< tk::real >
821 : 188097632 : evalPolynomialSol( const std::vector< inciter::EOS >& mat_blk,
822 : : int intsharp,
823 : : std::size_t ncomp,
824 : : std::size_t nprim,
825 : : std::size_t rdof,
826 : : std::size_t nmat,
827 : : std::size_t e,
828 : : std::size_t dof_e,
829 : : const std::vector< std::size_t >& inpoel,
830 : : const UnsMesh::Coords& coord,
831 : : const Fields& geoElem,
832 : : const std::array< real, 3 >& ref_gp,
833 : : const std::vector< real >& B,
834 : : const Fields& U,
835 : : const Fields& P )
836 : : // *****************************************************************************
837 : : // Evaluate polynomial solution at quadrature point
838 : : //! \param[in] mat_blk EOS material block
839 : : //! \param[in] intsharp Interface reconstruction indicator
840 : : //! \param[in] ncomp Number of components in the PDE system
841 : : //! \param[in] nprim Number of primitive quantities
842 : : //! \param[in] rdof Total number of reconstructed dofs
843 : : //! \param[in] nmat Total number of materials
844 : : //! \param[in] e Element for which polynomial solution is being evaluated
845 : : //! \param[in] dof_e Degrees of freedom for element
846 : : //! \param[in] inpoel Element-node connectivity
847 : : //! \param[in] coord Array of nodal coordinates
848 : : //! \param[in] geoElem Element geometry array
849 : : //! \param[in] ref_gp Quadrature point in reference space
850 : : //! \param[in] B Basis function at given quadrature point
851 : : //! \param[in] U Solution vector
852 : : //! \param[in] P Vector of primitives
853 : : //! \return High-order unknown/state vector at quadrature point, modified
854 : : //! if near interfaces using THINC
855 : : // *****************************************************************************
856 : : {
857 : 188097632 : std::vector< real > state;
858 : 376195264 : std::vector< real > sprim;
859 : :
860 [ + - ]: 188097632 : state = eval_state( ncomp, rdof, dof_e, e, U, B );
861 [ + - ]: 188097632 : sprim = eval_state( nprim, rdof, dof_e, e, P, B );
862 : :
863 : : // interface detection
864 [ + - ]: 376195264 : std::vector< std::size_t > matInt(nmat, 0);
865 : 188097632 : bool intInd(false);
866 [ + + ]: 188097632 : if (nmat > 1) {
867 [ + - ]: 26059670 : std::vector< tk::real > alAvg(nmat, 0.0);
868 [ + + ]: 83728770 : for (std::size_t k=0; k<nmat; ++k)
869 [ + - ]: 57669100 : alAvg[k] = U(e, inciter::volfracDofIdx(nmat,k,rdof,0));
870 [ + - ]: 26059670 : intInd = inciter::interfaceIndicator(nmat, alAvg, matInt);
871 : : }
872 : :
873 : : // consolidate primitives into state vector
874 [ + - ]: 188097632 : state.insert(state.end(), sprim.begin(), sprim.end());
875 : :
876 [ + + ]: 188097632 : if (intsharp > 0)
877 : : {
878 [ + - ][ + - ]: 12411000 : std::vector< tk::real > vfmax(nmat, 0.0), vfmin(nmat, 0.0);
879 : :
880 : : // Until the appropriate setup for activating THINC with Transport
881 : : // is ready, the following two chunks of code will need to be commented
882 : : // for using THINC with Transport
883 : : //for (std::size_t k=0; k<nmat; ++k) {
884 : : // vfmin[k] = VolFracMax(el, 2*k, 0);
885 : : // vfmax[k] = VolFracMax(el, 2*k+1, 0);
886 : : //}
887 [ + - ]: 6205500 : tk::THINCReco(rdof, nmat, e, inpoel, coord, geoElem,
888 : : ref_gp, U, P, intInd, matInt, vfmin, vfmax, state);
889 : :
890 : : // Until the appropriate setup for activating THINC with Transport
891 : : // is ready, the following lines will need to be uncommented for
892 : : // using THINC with Transport
893 : : //tk::THINCRecoTransport(rdof, nmat, el, inpoel, coord,
894 : : // geoElem, ref_gp_l, U, P, vfmin, vfmax, state[0]);
895 : : }
896 : :
897 : : // physical constraints
898 [ + - ]: 188097632 : enforcePhysicalConstraints(mat_blk, ncomp, nmat, state);
899 : :
900 : 376195264 : return state;
901 : : }
902 : :
903 : : std::vector< tk::real >
904 : 843456 : evalFVSol( const std::vector< inciter::EOS >& mat_blk,
905 : : int intsharp,
906 : : std::size_t ncomp,
907 : : std::size_t nprim,
908 : : std::size_t rdof,
909 : : std::size_t nmat,
910 : : std::size_t e,
911 : : const std::vector< std::size_t >& inpoel,
912 : : const UnsMesh::Coords& coord,
913 : : const Fields& geoElem,
914 : : const std::array< real, 3 >& ref_gp,
915 : : const std::vector< real >& B,
916 : : const Fields& U,
917 : : const Fields& P,
918 : : int srcFlag )
919 : : // *****************************************************************************
920 : : // Evaluate second-order FV solution at quadrature point
921 : : //! \param[in] mat_blk EOS material block
922 : : //! \param[in] intsharp Interface reconstruction indicator
923 : : //! \param[in] ncomp Number of components in the PDE system
924 : : //! \param[in] nprim Number of primitive quantities
925 : : //! \param[in] rdof Total number of reconstructed dofs
926 : : //! \param[in] nmat Total number of materials
927 : : //! \param[in] e Element for which polynomial solution is being evaluated
928 : : //! \param[in] inpoel Element-node connectivity
929 : : //! \param[in] coord Array of nodal coordinates
930 : : //! \param[in] geoElem Element geometry array
931 : : //! \param[in] ref_gp Quadrature point in reference space
932 : : //! \param[in] B Basis function at given quadrature point
933 : : //! \param[in] U Solution vector
934 : : //! \param[in] P Vector of primitives
935 : : //! \param[in] srcFlag Whether the energy source was added to element e
936 : : //! \return High-order unknown/state vector at quadrature point, modified
937 : : //! if near interfaces using THINC
938 : : // *****************************************************************************
939 : : {
940 : : using inciter::pressureIdx;
941 : : using inciter::velocityIdx;
942 : : using inciter::volfracIdx;
943 : : using inciter::densityIdx;
944 : : using inciter::energyIdx;
945 : : using inciter::momentumIdx;
946 : :
947 : 843456 : std::vector< real > state;
948 : 1686912 : std::vector< real > sprim;
949 : :
950 [ + - ]: 843456 : state = eval_state( ncomp, rdof, rdof, e, U, B );
951 [ + - ]: 843456 : sprim = eval_state( nprim, rdof, rdof, e, P, B );
952 : :
953 : : // interface detection so that eos is called on the appropriate quantities
954 [ + - ]: 1686912 : std::vector< std::size_t > matInt(nmat, 0);
955 [ + - ]: 1686912 : std::vector< tk::real > alAvg(nmat, 0.0);
956 [ + + ]: 2767424 : for (std::size_t k=0; k<nmat; ++k)
957 [ + - ]: 1923968 : alAvg[k] = U(e, inciter::volfracDofIdx(nmat,k,rdof,0));
958 [ + - ]: 843456 : auto intInd = inciter::interfaceIndicator(nmat, alAvg, matInt);
959 : :
960 : : // get mat-energy from reconstructed mat-pressure
961 : 843456 : auto rhob(0.0);
962 [ + + ]: 2767424 : for (std::size_t k=0; k<nmat; ++k) {
963 : 1923968 : auto alk = state[volfracIdx(nmat,k)];
964 [ + + ]: 1923968 : if (matInt[k]) {
965 : 135648 : alk = std::max(std::min(alk, 1.0-static_cast<tk::real>(nmat-1)*1e-12),
966 : 67824 : 1e-12);
967 : : }
968 : 1923968 : state[energyIdx(nmat,k)] = alk *
969 : 3847936 : mat_blk[k].compute< inciter::EOS::totalenergy >(
970 [ + - ]: 1923968 : state[densityIdx(nmat,k)]/alk, sprim[velocityIdx(nmat,0)],
971 : 1923968 : sprim[velocityIdx(nmat,1)], sprim[velocityIdx(nmat,2)],
972 : 1923968 : sprim[pressureIdx(nmat,k)]/alk);
973 : 1923968 : rhob += state[densityIdx(nmat,k)];
974 : : }
975 : : // get momentum from reconstructed velocity and bulk density
976 [ + + ]: 3373824 : for (std::size_t i=0; i<3; ++i) {
977 : 2530368 : state[momentumIdx(nmat,i)] = rhob * sprim[velocityIdx(nmat,i)];
978 : : }
979 : :
980 : : // consolidate primitives into state vector
981 [ + - ]: 843456 : state.insert(state.end(), sprim.begin(), sprim.end());
982 : :
983 [ + + ][ + - ]: 843456 : if (intsharp > 0 && srcFlag == 0)
984 : : {
985 [ + - ][ + - ]: 606400 : std::vector< tk::real > vfmax(nmat, 0.0), vfmin(nmat, 0.0);
986 : :
987 [ + - ]: 303200 : tk::THINCReco(rdof, nmat, e, inpoel, coord, geoElem,
988 : : ref_gp, U, P, intInd, matInt, vfmin, vfmax, state);
989 : : }
990 : :
991 : : // physical constraints
992 [ + - ]: 843456 : enforcePhysicalConstraints(mat_blk, ncomp, nmat, state);
993 : :
994 : 1686912 : return state;
995 : : }
996 : :
997 : : void
998 : 188941088 : enforcePhysicalConstraints(
999 : : const std::vector< inciter::EOS >& mat_blk,
1000 : : std::size_t ncomp,
1001 : : std::size_t nmat,
1002 : : std::vector< tk::real >& state )
1003 : : // *****************************************************************************
1004 : : // Enforce physical constraints on state at quadrature point
1005 : : //! \param[in] mat_blk EOS material block
1006 : : //! \param[in] ncomp Number of components in the PDE system
1007 : : //! \param[in] nmat Total number of materials
1008 : : //! \param[in,out] state state at quadrature point
1009 : : // *****************************************************************************
1010 : : {
1011 : 188941088 : auto myPDE = inciter::g_inputdeck.get< tag::pde >();
1012 : :
1013 : : // unfortunately have to query PDEType here. alternative will potentially
1014 : : // require refactor that passes PDEType from DGPDE to this level.
1015 [ + + ]: 188941088 : if (myPDE == inciter::ctr::PDEType::MULTIMAT) {
1016 : : using inciter::pressureIdx;
1017 : : using inciter::volfracIdx;
1018 : : using inciter::densityIdx;
1019 : :
1020 [ + + ]: 86496194 : for (std::size_t k=0; k<nmat; ++k) {
1021 : 59593068 : state[ncomp+pressureIdx(nmat,k)] = constrain_pressure( mat_blk,
1022 : 59593068 : state[ncomp+pressureIdx(nmat,k)], state[densityIdx(nmat,k)],
1023 : 59593068 : state[volfracIdx(nmat,k)], k );
1024 : : }
1025 : : }
1026 : : else if (myPDE == inciter::ctr::PDEType::MULTISPECIES) {
1027 : : // TODO: consider clipping temperature here
1028 : : }
1029 : 188941088 : }
1030 : :
1031 : : void
1032 : 0 : safeReco( std::size_t rdof,
1033 : : std::size_t nmat,
1034 : : std::size_t el,
1035 : : int er,
1036 : : const Fields& U,
1037 : : std::array< std::vector< real >, 2 >& state )
1038 : : // *****************************************************************************
1039 : : // Compute safe reconstructions near material interfaces
1040 : : //! \param[in] rdof Total number of reconstructed dofs
1041 : : //! \param[in] nmat Total number of material is PDE system
1042 : : //! \param[in] el Element on the left-side of face
1043 : : //! \param[in] er Element on the right-side of face
1044 : : //! \param[in] U Solution vector at recent time-stage
1045 : : //! \param[in,out] state Second-order reconstructed state, at cell-face, that
1046 : : //! is being modified for safety
1047 : : //! \details When the consistent limiting is applied, there is a possibility
1048 : : //! that the material densities and energies violate TVD bounds. This function
1049 : : //! enforces the TVD bounds locally
1050 : : // *****************************************************************************
1051 : : {
1052 : : using inciter::densityIdx;
1053 : : using inciter::energyIdx;
1054 : : using inciter::densityDofIdx;
1055 : : using inciter::energyDofIdx;
1056 : :
1057 [ - - ][ - - ]: 0 : if (er < 0) Throw("safe limiting cannot be called for boundary cells");
[ - - ][ - - ]
1058 : :
1059 : 0 : auto eR = static_cast< std::size_t >(er);
1060 : :
1061 : : // define a lambda for the safe limiting
1062 : 0 : auto safeLimit = [&]( std::size_t c, real ul, real ur )
1063 : : {
1064 : : // find min/max at the face
1065 : 0 : auto uMin = std::min(ul, ur);
1066 : 0 : auto uMax = std::max(ul, ur);
1067 : :
1068 : : // left-state limiting
1069 : 0 : state[0][c] = std::min(uMax, std::max(uMin, state[0][c]));
1070 : :
1071 : : // right-state limiting
1072 : 0 : state[1][c] = std::min(uMax, std::max(uMin, state[1][c]));
1073 : 0 : };
1074 : :
1075 [ - - ]: 0 : for (std::size_t k=0; k<nmat; ++k)
1076 : : {
1077 : 0 : real ul(0.0), ur(0.0);
1078 : :
1079 : : // Density
1080 : : // establish left- and right-hand states
1081 [ - - ]: 0 : ul = U(el, densityDofIdx(nmat, k, rdof, 0));
1082 [ - - ]: 0 : ur = U(eR, densityDofIdx(nmat, k, rdof, 0));
1083 : :
1084 : : // limit reconstructed density
1085 [ - - ]: 0 : safeLimit(densityIdx(nmat,k), ul, ur);
1086 : :
1087 : : // Energy
1088 : : // establish left- and right-hand states
1089 [ - - ]: 0 : ul = U(el, energyDofIdx(nmat, k, rdof, 0));
1090 [ - - ]: 0 : ur = U(eR, energyDofIdx(nmat, k, rdof, 0));
1091 : :
1092 : : // limit reconstructed energy
1093 [ - - ]: 0 : safeLimit(energyIdx(nmat,k), ul, ur);
1094 : : }
1095 : 0 : }
1096 : :
1097 : : } // tk::
|