Nektar++
Loading...
Searching...
No Matches
StdTetExp.cpp
Go to the documentation of this file.
1///////////////////////////////////////////////////////////////////////////////
2//
3// File: StdTetExp.cpp
4//
5// For more information, please see: http://www.nektar.info
6//
7// The MIT License
8//
9// Copyright (c) 2006 Division of Applied Mathematics, Brown University (USA),
10// Department of Aeronautics, Imperial College London (UK), and Scientific
11// Computing and Imaging Institute, University of Utah (USA).
12//
13// Permission is hereby granted, free of charge, to any person obtaining a
14// copy of this software and associated documentation files (the "Software"),
15// to deal in the Software without restriction, including without limitation
16// the rights to use, copy, modify, merge, publish, distribute, sublicense,
17// and/or sell copies of the Software, and to permit persons to whom the
18// Software is furnished to do so, subject to the following conditions:
19//
20// The above copyright notice and this permission notice shall be included
21// in all copies or substantial portions of the Software.
22//
23// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29// DEALINGS IN THE SOFTWARE.
30//
31// Description: Header field for tetrahedral routines built upon
32// StdExpansion3D
33//
34///////////////////////////////////////////////////////////////////////////////
35
39
40using namespace std;
44
45namespace Nektar::StdRegions
46{
47// Declaretion of scalar routine
51
53 const LibUtilities::BasisKey &Bb,
54 const LibUtilities::BasisKey &Bc)
55 : StdExpansion(LibUtilities::StdTetData::getNumberOfCoefficients(
56 Ba.GetNumModes(), Bb.GetNumModes(), Bc.GetNumModes()),
57 3, Ba, Bb, Bc),
58 StdExpansion3D(LibUtilities::StdTetData::getNumberOfCoefficients(
59 Ba.GetNumModes(), Bb.GetNumModes(), Bc.GetNumModes()),
60 Ba, Bb, Bc)
61{
62 ASSERTL0(Ba.GetNumModes() <= Bb.GetNumModes(),
63 "order in 'a' direction is higher than order "
64 "in 'b' direction");
65 ASSERTL0(Ba.GetNumModes() <= Bc.GetNumModes(),
66 "order in 'a' direction is higher than order "
67 "in 'c' direction");
68 ASSERTL0(Bb.GetNumModes() <= Bc.GetNumModes(),
69 "order in 'b' direction is higher than order "
70 "in 'c' direction");
71
72 // cache integration weights for future use
73 m_weights.push_back(m_base[0]->GetW());
74
75 StdFacKey w1key(eWeights1, Bb);
76 // get weights[1] from manager where points are rescaled
77 m_weights.push_back(GetStdFac(w1key));
78
79 StdFacKey w2key(eWeights2, Bc);
80 // get weights[2] from manager where points are rescaled
81 m_weights.push_back(GetStdFac(w2key));
82}
83
84//----------------------------
85// Differentiation Methods
86//----------------------------
87/**
88 * \brief Calculate the derivative of the physical points
89 *
90 * The derivative is evaluated at the nodal physical points.
91 * Derivatives with respect to the local Cartesian coordinates
92 *
93 * \f$\begin{Bmatrix} \frac {\partial} {\partial \xi_1} \\ \frac
94 * {\partial} {\partial \xi_2} \\ \frac {\partial} {\partial \xi_3}
95 * \end{Bmatrix} = \begin{Bmatrix} \frac 4 {(1-\eta_2)(1-\eta_3)}
96 * \frac \partial {\partial \eta_1} \ \ \frac {2(1+\eta_1)}
97 * {(1-\eta_2)(1-\eta_3)} \frac \partial {\partial \eta_1} + \frac 2
98 * {1-\eta_3} \frac \partial {\partial \eta_3} \\ \frac {2(1 +
99 * \eta_1)} {2(1 - \eta_2)(1-\eta_3)} \frac \partial {\partial \eta_1}
100 * + \frac {1 + \eta_2} {1 - \eta_3} \frac \partial {\partial \eta_2}
101 * + \frac \partial {\partial \eta_3} \end{Bmatrix}\f$
102 **/
104 Array<OneD, NekDouble> &out_dxi0,
105 Array<OneD, NekDouble> &out_dxi1,
106 Array<OneD, NekDouble> &out_dxi2)
107{
108 int Q0 = m_base[0]->GetNumPoints();
109 int Q1 = m_base[1]->GetNumPoints();
110 int Q2 = m_base[2]->GetNumPoints();
111 int Qtot = Q0 * Q1 * Q2;
112
113 // Compute the physical derivative
114 Array<OneD, NekDouble> out_dEta0(3 * Qtot, 0.0);
115 Array<OneD, NekDouble> out_dEta1 = out_dEta0 + Qtot;
116 Array<OneD, NekDouble> out_dEta2 = out_dEta1 + Qtot;
117
118 bool Do_2 = (out_dxi2.size() > 0) ? true : false;
119 bool Do_1 = (out_dxi1.size() > 0) ? true : false;
120
121 if (Do_2) // Need all local derivatives
122 {
123 PhysTensorDeriv(inarray, out_dEta0, out_dEta1, out_dEta2);
124 }
125 else if (Do_1) // Need 0 and 1 derivatives
126 {
127 PhysTensorDeriv(inarray, out_dEta0, out_dEta1, NullNekDouble1DArray);
128 }
129 else // Only need Eta0 derivaitve
130 {
131 PhysTensorDeriv(inarray, out_dEta0, NullNekDouble1DArray,
133 }
134
135 Array<OneD, const NekDouble> eta_0, eta_1, eta_2;
136 eta_0 = m_base[0]->GetZ();
137 eta_1 = m_base[1]->GetZ();
138 eta_2 = m_base[2]->GetZ();
139
140 // calculate 2.0/((1-eta_1)(1-eta_2)) Out_dEta0
141
142 NekDouble *dEta0 = &out_dEta0[0];
143 NekDouble fac;
144 for (int k = 0; k < Q2; ++k)
145 {
146 for (int j = 0; j < Q1; ++j, dEta0 += Q0)
147 {
148 Vmath::Smul(Q0, 2.0 / (1.0 - eta_1[j]), dEta0, 1, dEta0, 1);
149 }
150 fac = 1.0 / (1.0 - eta_2[k]);
151 Vmath::Smul(Q0 * Q1, fac, &out_dEta0[0] + k * Q0 * Q1, 1,
152 &out_dEta0[0] + k * Q0 * Q1, 1);
153 }
154
155 if (out_dxi0.size() > 0)
156 {
157 // out_dxi0 = 4.0/((1-eta_1)(1-eta_2)) Out_dEta0
158 Vmath::Smul(Qtot, 2.0, out_dEta0, 1, out_dxi0, 1);
159 }
160
161 if (Do_1 || Do_2)
162 {
163 Array<OneD, NekDouble> Fac0(Q0);
164 Vmath::Sadd(Q0, 1.0, eta_0, 1, Fac0, 1);
165
166 // calculate 2.0*(1+eta_0)/((1-eta_1)(1-eta_2)) Out_dEta0
167 for (int k = 0; k < Q1 * Q2; ++k)
168 {
169 Vmath::Vmul(Q0, &Fac0[0], 1, &out_dEta0[0] + k * Q0, 1,
170 &out_dEta0[0] + k * Q0, 1);
171 }
172 // calculate 2/(1.0-eta_2) out_dEta1
173 for (int k = 0; k < Q2; ++k)
174 {
175 Vmath::Smul(Q0 * Q1, 2.0 / (1.0 - eta_2[k]),
176 &out_dEta1[0] + k * Q0 * Q1, 1,
177 &out_dEta1[0] + k * Q0 * Q1, 1);
178 }
179
180 if (Do_1)
181 {
182 // calculate out_dxi1 = 2.0(1+eta_0)/((1-eta_1)(1-eta_2)) Out_dEta0
183 // + 2/(1.0-eta_2) out_dEta1
184 Vmath::Vadd(Qtot, out_dEta0, 1, out_dEta1, 1, out_dxi1, 1);
185 }
186
187 if (Do_2)
188 {
189 // calculate (1 + eta_1)/(1 -eta_2)*out_dEta1
190 NekDouble *dEta1 = &out_dEta1[0];
191 for (int k = 0; k < Q2; ++k)
192 {
193 for (int j = 0; j < Q1; ++j, dEta1 += Q0)
194 {
195 Vmath::Smul(Q0, (1.0 + eta_1[j]) / 2.0, dEta1, 1, dEta1, 1);
196 }
197 }
198
199 // calculate out_dxi2 =
200 // 2.0(1+eta_0)/((1-eta_1)(1-eta_2)) Out_dEta0 +
201 // (1 + eta_1)/(1 -eta_2)*out_dEta1 + out_dEta2
202 Vmath::Vadd(Qtot, out_dEta0, 1, out_dEta1, 1, out_dxi2, 1);
203 Vmath::Vadd(Qtot, out_dEta2, 1, out_dxi2, 1, out_dxi2, 1);
204 }
205 }
206}
207
208//---------------------------------------
209// Transforms
210//---------------------------------------
211
212/**
213 * @note 'r' (base[2]) runs fastest in this element
214 *
215 * \f$ u^{\delta} (\xi_{1i}, \xi_{2j}, \xi_{3k}) = \sum_{m(pqr)} \hat
216 * u_{pqr} \phi_{pqr} (\xi_{1i}, \xi_{2j}, \xi_{3k})\f$
217 *
218 * Backward transformation is three dimensional tensorial expansion
219 * \f$ u (\xi_{1i}, \xi_{2j}, \xi_{3k}) = \sum_{p=0}^{Q_x} \psi_p^a
220 * (\xi_{1i}) \lbrace { \sum_{q=0}^{Q_y} \psi_{pq}^b (\xi_{2j})
221 * \lbrace { \sum_{r=0}^{Q_z} \hat u_{pqr} \psi_{pqr}^c (\xi_{3k})
222 * \rbrace} \rbrace}. \f$ And sumfactorizing step of the form is as:\\
223 *
224 * \f$ f_{pq} (\xi_{3k}) = \sum_{r=0}^{Q_z} \hat u_{pqr} \psi_{pqr}^c
225 * (\xi_{3k}),\\
226 *
227 * g_{p} (\xi_{2j}, \xi_{3k}) = \sum_{r=0}^{Q_y} \psi_{pq}^b
228 * (\xi_{2j}) f_{pq} (\xi_{3k}),\\
229 *
230 * u(\xi_{1i}, \xi_{2j}, \xi_{3k}) = \sum_{p=0}^{Q_x} \psi_{p}^a
231 * (\xi_{1i}) g_{p} (\xi_{2j}, \xi_{3k}). \f$
232 */
234 Array<OneD, NekDouble> &outarray)
235{
238 "Basis[1] is not a general tensor type");
239
242 "Basis[2] is not a general tensor type");
243
244 const Array<OneD, const NekDouble> base0 = m_base[0]->GetBdata();
245 const Array<OneD, const NekDouble> base1 = m_base[1]->GetBdata();
246 const Array<OneD, const NekDouble> base2 = m_base[2]->GetBdata();
247
248 int nquad0 = m_base[0]->GetNumPoints();
249 int nquad1 = m_base[1]->GetNumPoints();
250 int nquad2 = m_base[2]->GetNumPoints();
251
252 int nmodes0 = m_base[0]->GetNumModes();
253 int nmodes1 = m_base[1]->GetNumModes();
254 int nmodes2 = m_base[2]->GetNumModes();
255
256 bool isModified = (m_base[0]->GetBasisType() == LibUtilities::eModified_A);
257
258 std::vector<vec_t, tinysimd::allocator<vec_t>> wsp0(nmodes0 * nmodes1),
259 wsp1(nmodes0);
260
261 // Switch statment using boost_pp and macros. This unfolls intwo a
262 // nested swtich statement where the outer swtich statement runs
263 // from SMIN to SMAX for modal order and the inner switch
264 // statemets run from the outer value of the case to 2*SMAX for
265 // the quadrature order. If you want to see it unwrapped compile
266 // in verbose mode and add --preprocess to the c++ command.
267 // Default case
268#undef BWDTRANS_DEF
269#define BWDTRANS_DEF \
270 BwdTransTetKernel(nmodes0, nmodes1, nmodes2, nquad0, nquad1, nquad2, \
271 isModified, (const vec_t *)base0.data(), \
272 (const vec_t *)base1.data(), \
273 (const vec_t *)base2.data(), wsp0.data(), wsp1.data(), \
274 (const vec_t *)inarray.data(), (vec_t *)outarray.data())
275
276 // Inner loop case over quarature points
277#undef BWDTRANS_Q
278#define BWDTRANS_Q(r, i) \
279 case NQ(i): \
280 BwdTransTetKernel( \
281 NM(i), NM(i), NM(i), NQ(i), NQ_M1(i), NQ_M1(i), isModified, \
282 (const vec_t *)base0.data(), (const vec_t *)base1.data(), \
283 (const vec_t *)base2.data(), wsp0.data(), wsp1.data(), \
284 (const vec_t *)inarray.data(), (vec_t *)outarray.data()); \
285 break;
286
287 // outer loop case over modes
288#undef BWDTRANS_M
289#define BWDTRANS_M(r, i) \
290 case NM(i): \
291 { \
292 switch (nquad0) \
293 { \
294 BOOST_PP_FOR_##r((NM(i), NM_P1(i), BOOST_PP_MUL(2, NM(i))), \
295 STDLEV2TEST1, STDLEV2UPDATE1, BWDTRANS_Q) default \
296 : BWDTRANS_DEF; \
297 break; \
298 } \
299 } \
300 break;
301
302 // templated cases on equi-ordered modes and standard quad
303 // usage where quad order goes from mode order to 2(*mode
304 // order)
305 if ((nmodes0 == nmodes1) && (nmodes1 == nmodes2) &&
306 (nquad0 == nquad1 + 1) && (nquad1 == nquad2))
307 {
308 switch (nmodes0)
309 {
310 BOOST_PP_FOR((SMIN, 0, SMAX), STDLEV2TEST, STDLEV2UPDATE,
312 default:
314 break;
315 }
316 }
317 else
318 {
320 }
321}
322
323//---------------------------------------
324// Inner product functions
325//---------------------------------------
326/** \brief Inner product of \a inarray over region with respect to the
327 * expansion basis (this)->m_base[0] and return in \a outarray
328 *
329 * @param base0 - An array containing the values of the basis in the
330 * 0-direction at the quarature poitns
331 * @param base1 - An array containing the values of the basis in the
332 * 1-direction at the quarature poitns
333 * @param base2 - An array containing the values of the basis in the
334 * 2-direction at the quarature poitns
335 * @param inarray - Array of values evaluated at the physical
336 * quadrature points
337 * @param outarray the values of the inner product with respect to
338 * each basis over region will be stored in the array \a outarray as
339 * output of the function
340 * @param jac - An array of size 1 if not deformed or the number of
341 * quadrature points if deformed holding the values of the jacobian
342 * @param Deformed - a bool identifying if the inner product is to be
343 * treated as a deformed or regular integration which just relates to
344 * how the \param jac array is treated
345 */
347 const Array<OneD, const NekDouble> &base0,
348 const Array<OneD, const NekDouble> &base1,
349 const Array<OneD, const NekDouble> &base2,
350 const Array<OneD, const NekDouble> &inarray,
352 const bool Deformed, [[maybe_unused]] bool CollDir0,
353 [[maybe_unused]] bool CollDir1, [[maybe_unused]] bool CollDir2)
354{
357 "Basis[1] is not a general tensor type");
358
361 "Basis[2] is not a general tensor type");
362
363 int nquad0 = m_base[0]->GetNumPoints();
364 int nquad1 = m_base[1]->GetNumPoints();
365 int nquad2 = m_base[2]->GetNumPoints();
366
367 int order0 = m_base[0]->GetNumModes();
368 int order1 = m_base[1]->GetNumModes();
369 int order2 = m_base[2]->GetNumModes();
370
371 const bool isModified =
372 (m_base[0]->GetBasisType() == LibUtilities::eModified_A);
373
374 std::vector<vec_t, tinysimd::allocator<vec_t>> wsp0(nquad1 * nquad2),
375 wsp1(nquad2);
376
377 // Swith statment using boost_pp and macros. This unfolls intwo a
378 // nested swtich statement where the outer swtich statement runs
379 // from SMIN to SMAX for modal order and the inner switch
380 // statemets run from the outer value of the case to 2*SMAX for
381 // the quadrature order. If you want to see it unwrapped compile
382 // in verbose mode and add --preprocess to the c++ command.
383 if (Deformed)
384 {
385 // Default case
386#undef IPRODUCTWRTBASE_DEF
387#define IPRODUCTWRTBASE_DEF \
388 IProductTetKernel<false, false, true>( \
389 order0, order1, order2, nquad0, nquad1, nquad2, isModified, \
390 (const vec_t *)inarray.data(), (const vec_t *)base0.data(), \
391 (const vec_t *)base1.data(), (const vec_t *)base2.data(), \
392 (const vec_t *)m_weights[0].data(), \
393 (const vec_t *)m_weights[1].data(), \
394 (const vec_t *)m_weights[2].data(), (const vec_t *)jac.data(), \
395 (vec_t *)wsp0.data(), (vec_t *)wsp1.data(), (vec_t *)outarray.data())
396
397 // Inner loop case over quarature points
398#undef IPRODUCTWRTBASE_Q
399#define IPRODUCTWRTBASE_Q(r, i) \
400 case NQ(i): \
401 IProductTetKernel<false, false, true>( \
402 NM(i), NM(i), NM(i), NQ(i), NQ_M1(i), NQ_M1(i), isModified, \
403 (const vec_t *)inarray.data(), (const vec_t *)base0.data(), \
404 (const vec_t *)base1.data(), (const vec_t *)base2.data(), \
405 (const vec_t *)m_weights[0].data(), \
406 (const vec_t *)m_weights[1].data(), \
407 (const vec_t *)m_weights[2].data(), (const vec_t *)jac.data(), \
408 (vec_t *)wsp0.data(), (vec_t *)wsp1.data(), \
409 (vec_t *)outarray.data()); \
410 break;
411
412 // outer loop case over modes
413#undef IPRODUCTWRTBASE_M
414#define IPRODUCTWRTBASE_M(r, i) \
415 case NM(i): \
416 { \
417 switch (nquad0) \
418 { \
419 BOOST_PP_FOR_##r((NM(i), NM_P1(i), BOOST_PP_MUL(2, NM(i))), \
420 STDLEV2TEST1, STDLEV2UPDATE1, \
421 IPRODUCTWRTBASE_Q) default : IPRODUCTWRTBASE_DEF; \
422 break; \
423 } \
424 } \
425 break;
426
427 // templated cases on equi-ordered modes and standard quad usage
428 // where quad order goes from mode order to 2(*mode order)
429 if ((order0 == order1) && (order1 == order2) &&
430 (nquad0 == nquad1 + 1) && (nquad1 == nquad2))
431 {
432 switch (order0)
433 {
434 BOOST_PP_FOR((SMIN, 0, SMAX), STDLEV2TEST, STDLEV2UPDATE,
436 default:
438 break;
439 }
440 }
441 else
442 {
444 }
445 }
446 else // non-deformed case
447 {
448 // Default case
449#undef IPRODUCTWRTBASE_DEF
450#define IPRODUCTWRTBASE_DEF \
451 IProductTetKernel<false, false, false>( \
452 order0, order1, order2, nquad0, nquad1, nquad2, isModified, \
453 (const vec_t *)inarray.data(), (const vec_t *)base0.data(), \
454 (const vec_t *)base1.data(), (const vec_t *)base2.data(), \
455 (const vec_t *)m_weights[0].data(), \
456 (const vec_t *)m_weights[1].data(), \
457 (const vec_t *)m_weights[2].data(), (const vec_t *)jac.data(), \
458 (vec_t *)wsp0.data(), (vec_t *)wsp1.data(), (vec_t *)outarray.data())
459
460 // Inner loop case over quarature points
461#undef IPRODUCTWRTBASE_Q
462#define IPRODUCTWRTBASE_Q(r, i) \
463 case NQ(i): \
464 IProductTetKernel<false, false, false>( \
465 NM(i), NM(i), NM(i), NQ(i), NQ_M1(i), NQ_M1(i), isModified, \
466 (const vec_t *)inarray.data(), (const vec_t *)base0.data(), \
467 (const vec_t *)base1.data(), (const vec_t *)base2.data(), \
468 (const vec_t *)m_weights[0].data(), \
469 (const vec_t *)m_weights[1].data(), \
470 (const vec_t *)m_weights[2].data(), (const vec_t *)jac.data(), \
471 (vec_t *)wsp0.data(), (vec_t *)wsp1.data(), \
472 (vec_t *)outarray.data()); \
473 break;
474
475 // outer loop case over modes
476#undef IPRODUCTWRTBASE_M
477#define IPRODUCTWRTBASE_M(r, i) \
478 case NM(i): \
479 { \
480 switch (nquad0) \
481 { \
482 BOOST_PP_FOR_##r((NM(i), NM_P1(i), BOOST_PP_MUL(2, NM(i))), \
483 STDLEV2TEST1, STDLEV2UPDATE1, \
484 IPRODUCTWRTBASE_Q) default : IPRODUCTWRTBASE_DEF; \
485 break; \
486 } \
487 } \
488 break;
489
490 // templated cases on equi-ordered modes and standard quad usage
491 // where quad order goes from mode order to 2(*mode order)
492 if ((order0 == order1) && (order1 == order2) &&
493 (nquad0 == nquad1 + 1) && (nquad1 == nquad2))
494 {
495 switch (order0)
496 {
497 BOOST_PP_FOR((SMIN, 0, SMAX), STDLEV2TEST, STDLEV2UPDATE,
499 default:
501 break;
502 }
503 }
504 else
505 {
507 }
508 }
509}
510
511/**
512 * @param inarray Function evaluated at physical collocation
513 * points.
514 * @param outarray Inner product with respect to each basis
515 * function over the element.
516 */
518 const int dir, const Array<OneD, const NekDouble> &inarray,
519 Array<OneD, NekDouble> &outarray)
520{
521 int i;
522 int nquad0 = m_base[0]->GetNumPoints();
523 int nquad1 = m_base[1]->GetNumPoints();
524 int nquad2 = m_base[2]->GetNumPoints();
525 int nqtot = nquad0 * nquad1 * nquad2;
526 int nmodes0 = m_base[0]->GetNumModes();
527 int nmodes1 = m_base[1]->GetNumModes();
528
530 Array<OneD, NekDouble> wsp(nquad1 * nquad2 * nmodes0 +
531 nquad2 * nmodes0 * (2 * nmodes1 - nmodes0 + 1) /
532 2);
533
534 StdFacKey fackey0(eHalfMultOnePlusZ0, m_base[0]->GetBasisKey());
536 StdFacKey fackey1(eTwoOverOneMinusZ1, m_base[1]->GetBasisKey());
538 StdFacKey fackey2(eTwoOverOneMinusZ2, m_base[2]->GetBasisKey());
540
541 // Derivative in first direction is always scaled as follows
542 for (i = 0; i < nquad1 * nquad2; ++i)
543 {
544 Vmath::Smul(nquad0, gfac1[i % nquad1], &inarray[0] + i * nquad0, 1,
545 &tmp0[0] + i * nquad0, 1);
546 }
547 for (i = 0; i < nquad2; ++i)
548 {
549 Vmath::Smul(nquad0 * nquad1, gfac2[i], &tmp0[0] + i * nquad0 * nquad1,
550 1, &tmp0[0] + i * nquad0 * nquad1, 1);
551 }
552
553 const Array<OneD, const NekDouble> one(1, 1.0);
554
555 switch (dir)
556 {
557 case 0:
558 {
560 m_base[0]->GetDbdata(), m_base[1]->GetBdata(),
561 m_base[2]->GetBdata(), tmp0, outarray, one, false);
562 }
563 break;
564 case 1:
565 {
567
568 for (i = 0; i < nquad1 * nquad2; ++i)
569 {
570 Vmath::Vmul(nquad0, &gfac0[0], 1, &tmp0[0] + i * nquad0, 1,
571 &tmp0[0] + i * nquad0, 1);
572 }
573
575 m_base[0]->GetDbdata(), m_base[1]->GetBdata(),
576 m_base[2]->GetBdata(), tmp0, tmp3, one, false);
577
578 for (i = 0; i < nquad2; ++i)
579 {
580 Vmath::Smul(nquad0 * nquad1, gfac2[i],
581 &inarray[0] + i * nquad0 * nquad1, 1,
582 &tmp0[0] + i * nquad0 * nquad1, 1);
583 }
584
586 m_base[0]->GetBdata(), m_base[1]->GetDbdata(),
587 m_base[2]->GetBdata(), tmp0, outarray, one, false);
588 Vmath::Vadd(m_ncoeffs, &tmp3[0], 1, &outarray[0], 1, &outarray[0],
589 1);
590 }
591 break;
592 case 2:
593 {
596 StdFacKey fackey1a(eHalfMultOnePlusZ1, m_base[1]->GetBasisKey());
597 gfac1 = GetStdFac(fackey1a);
598
599 for (i = 0; i < nquad1 * nquad2; ++i)
600 {
601 Vmath::Vmul(nquad0, &gfac0[0], 1, &tmp0[0] + i * nquad0, 1,
602 &tmp0[0] + i * nquad0, 1);
603 }
605 m_base[0]->GetDbdata(), m_base[1]->GetBdata(),
606 m_base[2]->GetBdata(), tmp0, tmp3, one, false);
607
608 for (i = 0; i < nquad2; ++i)
609 {
610 Vmath::Smul(nquad0 * nquad1, gfac2[i],
611 &inarray[0] + i * nquad0 * nquad1, 1,
612 &tmp0[0] + i * nquad0 * nquad1, 1);
613 }
614 for (i = 0; i < nquad1 * nquad2; ++i)
615 {
616 Vmath::Smul(nquad0, gfac1[i % nquad1], &tmp0[0] + i * nquad0, 1,
617 &tmp0[0] + i * nquad0, 1);
618 }
619
621 m_base[0]->GetBdata(), m_base[1]->GetDbdata(),
622 m_base[2]->GetBdata(), tmp0, tmp4, one, false);
623
625 m_base[0]->GetBdata(), m_base[1]->GetBdata(),
626 m_base[2]->GetDbdata(), inarray, outarray, one, false);
627 Vmath::Vadd(m_ncoeffs, &tmp3[0], 1, &outarray[0], 1, &outarray[0],
628 1);
629 Vmath::Vadd(m_ncoeffs, &tmp4[0], 1, &outarray[0], 1, &outarray[0],
630 1);
631 }
632 break;
633 default:
634 {
635 ASSERTL1(false, "input dir is out of range");
636 }
637 break;
638 }
639}
640
641//---------------------------------------
642// Evaluation functions
643//---------------------------------------
644
647{
648 NekDouble d2 = 1.0 - xi[2];
649 NekDouble d12 = -xi[1] - xi[2];
650 if (fabs(d2) < NekConstants::kNekZeroTol)
651 {
652 if (d2 >= 0.)
653 {
655 }
656 else
657 {
659 }
660 }
661 if (fabs(d12) < NekConstants::kNekZeroTol)
662 {
663 if (d12 >= 0.)
664 {
666 }
667 else
668 {
670 }
671 }
672 eta[0] = 2.0 * (1.0 + xi[0]) / d12 - 1.0;
673 eta[1] = 2.0 * (1.0 + xi[1]) / d2 - 1.0;
674 eta[2] = xi[2];
675}
676
679{
680 xi[2] = eta[2];
681 xi[1] = (1.0 + eta[1]) * (1.0 - xi[2]) * 0.5 - 1.0;
682 xi[0] = (1.0 + eta[0]) * (-xi[1] - xi[2]) * 0.5 - 1.0;
683}
684
685void StdTetExp::v_FillMode(const int mode, Array<OneD, NekDouble> &outarray)
686{
688 tmp[mode] = 1.0;
689 StdTetExp::v_BwdTrans(tmp, outarray);
690}
691
693 const Array<OneD, const NekDouble> &coords, int mode)
694{
696 LocCoordToLocCollapsed(coords, coll);
697
698 const int nm1 = m_base[1]->GetNumModes();
699 const int nm2 = m_base[2]->GetNumModes();
700
701 const int b = 2 * nm2 + 1;
702 const int mode0 = floor(0.5 * (b - sqrt(b * b - 8.0 * mode / nm1)));
703 const int tmp =
704 mode - nm1 * (mode0 * (nm2 - 1) + 1 - (mode0 - 2) * (mode0 - 1) / 2);
705 const int mode1 = tmp / (nm2 - mode0);
706 const int mode2 = tmp % (nm2 - mode0);
707
709 {
710 // Handle the collapsed vertices and edges in the modified
711 // basis.
712 if (mode == 1)
713 {
714 // Collapsed top vertex
715 return StdExpansion::BaryEvaluateBasis<2>(coll[2], 1);
716 }
717 else if (mode0 == 0 && mode2 == 1)
718 {
719 return StdExpansion::BaryEvaluateBasis<1>(coll[1], 0) *
720 StdExpansion::BaryEvaluateBasis<2>(coll[2], 1);
721 }
722 else if (mode0 == 1 && mode1 == 1 && mode2 == 0)
723 {
724 return StdExpansion::BaryEvaluateBasis<0>(coll[0], 0) *
725 StdExpansion::BaryEvaluateBasis<1>(coll[1], 1);
726 }
727 }
728
729 return StdExpansion::BaryEvaluateBasis<0>(coll[0], mode0) *
730 StdExpansion::BaryEvaluateBasis<1>(coll[1], mode1) *
731 StdExpansion::BaryEvaluateBasis<2>(coll[2], mode2);
732}
733
735 const Array<OneD, NekDouble> &coord,
736 const Array<OneD, const NekDouble> &inarray,
737 std::array<NekDouble, 3> &firstOrderDerivs)
738{
739 // Collapse coordinates
740 Array<OneD, NekDouble> coll(3, 0.0);
741 LocCoordToLocCollapsed(coord, coll);
742
743 // If near singularity do the old interpolation matrix method
744 if ((1 - coll[1]) < 1e-5 || (1 - coll[2]) < 1e-5)
745 {
746 int totPoints = GetTotPoints();
747 Array<OneD, NekDouble> EphysDeriv0(totPoints), EphysDeriv1(totPoints),
748 EphysDeriv2(totPoints);
749 v_PhysDeriv(inarray, EphysDeriv0, EphysDeriv1, EphysDeriv2);
750
752 I[0] = GetBase()[0]->GetI(coll);
753 I[1] = GetBase()[1]->GetI(coll + 1);
754 I[2] = GetBase()[2]->GetI(coll + 2);
755
756 firstOrderDerivs[0] = PhysEvaluate(I, EphysDeriv0);
757 firstOrderDerivs[1] = PhysEvaluate(I, EphysDeriv1);
758 firstOrderDerivs[2] = PhysEvaluate(I, EphysDeriv2);
759 return PhysEvaluate(I, inarray);
760 }
761
762 std::array<NekDouble, 3> interDeriv;
763 NekDouble val = BaryTensorDeriv(coll, inarray, interDeriv);
764
765 // calculate 2.0/((1-eta_1)(1-eta_2)) * Out_dEta0
766 NekDouble temp = 2.0 / ((1 - coll[1]) * (1 - coll[2]));
767 interDeriv[0] *= temp;
768
769 // out_dxi0 = 4.0/((1-eta_1)(1-eta_2)) * Out_dEta0
770 firstOrderDerivs[0] = 2 * interDeriv[0];
771
772 // fac0 = 1 + eta_0
773 NekDouble fac0;
774 fac0 = 1 + coll[0];
775
776 // calculate 2.0*(1+eta_0)/((1-eta_1)(1-eta_2)) * Out_dEta0
777 interDeriv[0] *= fac0;
778
779 // calculate 2/(1.0-eta_2) * out_dEta1
780 fac0 = 2 / (1 - coll[2]);
781 interDeriv[1] *= fac0;
782
783 // calculate out_dxi1 = 2.0(1+eta_0)/((1-eta_1)(1-eta_2))
784 // * Out_dEta0 + 2/(1.0-eta_2) out_dEta1
785 firstOrderDerivs[1] = interDeriv[0] + interDeriv[1];
786
787 // calculate (1 + eta_1)/(1 -eta_2)*out_dEta1
788 fac0 = (1 + coll[1]) / 2;
789 interDeriv[1] *= fac0;
790
791 // calculate out_dxi2 =
792 // 2.0(1+eta_0)/((1-eta_1)(1-eta_2)) Out_dEta0 +
793 // (1 + eta_1)/(1 -eta_2)*out_dEta1 + out_dEta2
794 firstOrderDerivs[2] = interDeriv[0] + interDeriv[1] + interDeriv[2];
795
796 return val;
797}
798
799void StdTetExp::v_GetTraceNumModes(const int fid, int &numModes0,
800 int &numModes1,
801 [[maybe_unused]] Orientation faceOrient)
802{
803 int nummodes[3] = {m_base[0]->GetNumModes(), m_base[1]->GetNumModes(),
804 m_base[2]->GetNumModes()};
805 switch (fid)
806 {
807 case 0:
808 {
809 numModes0 = nummodes[0];
810 numModes1 = nummodes[1];
811 }
812 break;
813 case 1:
814 {
815 numModes0 = nummodes[0];
816 numModes1 = nummodes[2];
817 }
818 break;
819 case 2:
820 case 3:
821 {
822 numModes0 = nummodes[1];
823 numModes1 = nummodes[2];
824 }
825 break;
826 }
827}
828
829//---------------------------
830// Helper functions
831//---------------------------
832
834{
835 return 4;
836}
837
839{
840 return 6;
841}
842
844{
845 return 4;
846}
847
852
854{
857 "BasisType is not a boundary interior form");
860 "BasisType is not a boundary interior form");
863 "BasisType is not a boundary interior form");
864
865 int P = m_base[0]->GetNumModes();
866 int Q = m_base[1]->GetNumModes();
867 int R = m_base[2]->GetNumModes();
868
870}
871
873{
876 "BasisType is not a boundary interior form");
879 "BasisType is not a boundary interior form");
882 "BasisType is not a boundary interior form");
883
884 int P = m_base[0]->GetNumModes() - 1;
885 int Q = m_base[1]->GetNumModes() - 1;
886 int R = m_base[2]->GetNumModes() - 1;
887
888 return (Q + 1) + P * (1 + 2 * Q - P) / 2 // base face
889 + (R + 1) + P * (1 + 2 * R - P) / 2 // front face
890 + 2 * (R + 1) + Q * (1 + 2 * R - Q); // back two faces
891}
892
893int StdTetExp::v_GetTraceNcoeffs(const int i) const
894{
895 ASSERTL2((i >= 0) && (i <= 3), "face id is out of range");
896 int nFaceCoeffs = 0;
897 int nummodesA, nummodesB, P, Q;
898 if (i == 0)
899 {
900 nummodesA = GetBasisNumModes(0);
901 nummodesB = GetBasisNumModes(1);
902 }
903 else if ((i == 1) || (i == 2))
904 {
905 nummodesA = GetBasisNumModes(0);
906 nummodesB = GetBasisNumModes(2);
907 }
908 else
909 {
910 nummodesA = GetBasisNumModes(1);
911 nummodesB = GetBasisNumModes(2);
912 }
913 P = nummodesA - 1;
914 Q = nummodesB - 1;
915 nFaceCoeffs = Q + 1 + (P * (1 + 2 * Q - P)) / 2;
916 return nFaceCoeffs;
917}
918
920{
921 ASSERTL2((i >= 0) && (i <= 3), "face id is out of range");
922 int Pi = m_base[0]->GetNumModes() - 2;
923 int Qi = m_base[1]->GetNumModes() - 2;
924 int Ri = m_base[2]->GetNumModes() - 2;
925
926 if ((i == 0))
927 {
928 return Pi * (2 * Qi - Pi - 1) / 2;
929 }
930 else if ((i == 1))
931 {
932 return Pi * (2 * Ri - Pi - 1) / 2;
933 }
934 else
935 {
936 return Qi * (2 * Ri - Qi - 1) / 2;
937 }
938}
939
940int StdTetExp::v_GetTraceNumPoints(const int i) const
941{
942 ASSERTL2(i >= 0 && i <= 3, "face id is out of range");
943
944 if (i == 0)
945 {
946 return m_base[0]->GetNumPoints() * m_base[1]->GetNumPoints();
947 }
948 else if (i == 1)
949 {
950 return m_base[0]->GetNumPoints() * m_base[2]->GetNumPoints();
951 }
952 else
953 {
954 return m_base[1]->GetNumPoints() * m_base[2]->GetNumPoints();
955 }
956}
957
958int StdTetExp::v_GetEdgeNcoeffs(const int i) const
959{
960 ASSERTL2((i >= 0) && (i <= 5), "edge id is out of range");
961 int P = m_base[0]->GetNumModes();
962 int Q = m_base[1]->GetNumModes();
963 int R = m_base[2]->GetNumModes();
964
965 if (i == 0)
966 {
967 return P;
968 }
969 else if (i == 1 || i == 2)
970 {
971 return Q;
972 }
973 else
974 {
975 return R;
976 }
977}
978
980 const int j) const
981{
982 ASSERTL2(i >= 0 && i <= 3, "face id is out of range");
983 ASSERTL2(j == 0 || j == 1, "face direction is out of range");
984
985 if (i == 0)
986 {
987 return m_base[j]->GetPointsKey();
988 }
989 else if (i == 1)
990 {
991 return m_base[2 * j]->GetPointsKey();
992 }
993 else
994 {
995 return m_base[j + 1]->GetPointsKey();
996 }
997}
998
1000 const std::vector<unsigned int> &nummodes, int &modes_offset)
1001{
1003 nummodes[modes_offset], nummodes[modes_offset + 1],
1004 nummodes[modes_offset + 2]);
1005 modes_offset += 3;
1006
1007 return nmodes;
1008}
1009
1011 const int k,
1012 bool UseGLL) const
1013{
1014 ASSERTL2(i >= 0 && i <= 4, "face id is out of range");
1015 ASSERTL2(k == 0 || k == 1, "face direction out of range");
1016
1017 int dir = k;
1018 switch (i)
1019 {
1020 case 0:
1021 dir = k; // retrun facedir=0-> 0 facedir=1->1
1022 break;
1023 case 1:
1024 dir = 2 * k; // retrun facedir=0-> 0 facedir=1->2
1025 break;
1026 case 2:
1027 case 3:
1028 dir = k + 1; // retrun facedir=0-> 1 facedir=1->2
1029 break;
1030 }
1031
1032 return EvaluateTriFaceBasisKey(k, m_base[dir], UseGLL);
1033}
1034
1038{
1039 Array<OneD, const NekDouble> eta_x = m_base[0]->GetZ();
1040 Array<OneD, const NekDouble> eta_y = m_base[1]->GetZ();
1041 Array<OneD, const NekDouble> eta_z = m_base[2]->GetZ();
1042 int Qx = GetNumPoints(0);
1043 int Qy = GetNumPoints(1);
1044 int Qz = GetNumPoints(2);
1045
1046 // Convert collapsed coordinates into cartesian coordinates: eta
1047 // --> xi
1048 for (int k = 0; k < Qz; ++k)
1049 {
1050 for (int j = 0; j < Qy; ++j)
1051 {
1052 for (int i = 0; i < Qx; ++i)
1053 {
1054 int s = i + Qx * (j + Qy * k);
1055 xi_x[s] =
1056 (eta_x[i] + 1.0) * (1.0 - eta_y[j]) * (1.0 - eta_z[k]) / 4 -
1057 1.0;
1058 xi_y[s] = (eta_y[j] + 1.0) * (1.0 - eta_z[k]) / 2 - 1.0;
1059 xi_z[s] = eta_z[k];
1060 }
1061 }
1062 }
1063}
1064
1066{
1067 return (m_base[0]->GetBasisType() == LibUtilities::eModified_A) &&
1068 (m_base[1]->GetBasisType() == LibUtilities::eModified_B) &&
1070}
1071
1072//--------------------------
1073// Mappings
1074//--------------------------
1075int StdTetExp::v_GetVertexMap(const int localVertexId, bool useCoeffPacking)
1076{
1080 "Mapping not defined for this type of basis");
1081
1082 int localDOF = 0;
1083 if (useCoeffPacking == true) // follow packing of coefficients i.e q,r,p
1084 {
1085 switch (localVertexId)
1086 {
1087 case 0:
1088 {
1089 localDOF = GetMode(0, 0, 0);
1090 break;
1091 }
1092 case 1:
1093 {
1094 localDOF = GetMode(0, 0, 1);
1095 break;
1096 }
1097 case 2:
1098 {
1099 localDOF = GetMode(0, 1, 0);
1100 break;
1101 }
1102 case 3:
1103 {
1104 localDOF = GetMode(1, 0, 0);
1105 break;
1106 }
1107 default:
1108 {
1109 ASSERTL0(false, "Vertex ID must be between 0 and 3");
1110 break;
1111 }
1112 }
1113 }
1114 else
1115 {
1116 switch (localVertexId)
1117 {
1118 case 0:
1119 {
1120 localDOF = GetMode(0, 0, 0);
1121 break;
1122 }
1123 case 1:
1124 {
1125 localDOF = GetMode(1, 0, 0);
1126 break;
1127 }
1128 case 2:
1129 {
1130 localDOF = GetMode(0, 1, 0);
1131 break;
1132 }
1133 case 3:
1134 {
1135 localDOF = GetMode(0, 0, 1);
1136 break;
1137 }
1138 default:
1139 {
1140 ASSERTL0(false, "Vertex ID must be between 0 and 3");
1141 break;
1142 }
1143 }
1144 }
1145
1146 return localDOF;
1147}
1148
1149/**
1150 * Maps interior modes of an edge to the elemental modes.
1151 */
1152
1153/**
1154 * List of all interior modes in the expansion.
1155 */
1157{
1160 "BasisType is not a boundary interior form");
1163 "BasisType is not a boundary interior form");
1166 "BasisType is not a boundary interior form");
1167
1168 int P = m_base[0]->GetNumModes();
1169 int Q = m_base[1]->GetNumModes();
1170 int R = m_base[2]->GetNumModes();
1171
1172 int nIntCoeffs = m_ncoeffs - NumBndryCoeffs();
1173
1174 if (outarray.size() != nIntCoeffs)
1175 {
1176 outarray = Array<OneD, unsigned int>(nIntCoeffs);
1177 }
1178
1179 int idx = 0;
1180 for (int i = 2; i < P; ++i)
1181 {
1182 for (int j = 1; j < Q - i; ++j)
1183 {
1184 for (int k = 1; k < R - i - j; ++k)
1185 {
1186 outarray[idx++] = GetMode(i, j, k);
1187 }
1188 }
1189 }
1190}
1191
1192/**
1193 * List of all boundary modes in the the expansion.
1194 */
1196{
1199 "BasisType is not a boundary interior form");
1202 "BasisType is not a boundary interior form");
1205 "BasisType is not a boundary interior form");
1206
1207 int P = m_base[0]->GetNumModes();
1208 int Q = m_base[1]->GetNumModes();
1209 int R = m_base[2]->GetNumModes();
1210
1211 int i, j, k;
1212 int idx = 0;
1213
1214 int nBnd = NumBndryCoeffs();
1215
1216 if (outarray.size() != nBnd)
1217 {
1218 outarray = Array<OneD, unsigned int>(nBnd);
1219 }
1220
1221 for (i = 0; i < P; ++i)
1222 {
1223 // First two Q-R planes are entirely boundary modes
1224 if (i < 2)
1225 {
1226 for (j = 0; j < Q - i; j++)
1227 {
1228 for (k = 0; k < R - i - j; ++k)
1229 {
1230 outarray[idx++] = GetMode(i, j, k);
1231 }
1232 }
1233 }
1234 // Remaining Q-R planes contain boundary modes on bottom and
1235 // left edge.
1236 else
1237 {
1238 for (k = 0; k < R - i; ++k)
1239 {
1240 outarray[idx++] = GetMode(i, 0, k);
1241 }
1242 for (j = 1; j < Q - i; ++j)
1243 {
1244 outarray[idx++] = GetMode(i, j, 0);
1245 }
1246 }
1247 }
1248}
1249
1250void StdTetExp::v_GetTraceCoeffMap(const unsigned int fid,
1251 Array<OneD, unsigned int> &maparray)
1252{
1253 int i, j, k;
1254 int P = 0, Q = 0, idx = 0;
1255 int nFaceCoeffs = 0;
1256
1257 switch (fid)
1258 {
1259 case 0:
1260 P = m_base[0]->GetNumModes();
1261 Q = m_base[1]->GetNumModes();
1262 break;
1263 case 1:
1264 P = m_base[0]->GetNumModes();
1265 Q = m_base[2]->GetNumModes();
1266 break;
1267 case 2:
1268 case 3:
1269 P = m_base[1]->GetNumModes();
1270 Q = m_base[2]->GetNumModes();
1271 break;
1272 default:
1273 ASSERTL0(false, "fid must be between 0 and 3");
1274 }
1275
1276 nFaceCoeffs = P * (2 * Q - P + 1) / 2;
1277
1278 if (maparray.size() != nFaceCoeffs)
1279 {
1280 maparray = Array<OneD, unsigned int>(nFaceCoeffs);
1281 }
1282
1283 switch (fid)
1284 {
1285 case 0:
1286 idx = 0;
1287 for (i = 0; i < P; ++i)
1288 {
1289 for (j = 0; j < Q - i; ++j)
1290 {
1291 maparray[idx++] = GetMode(i, j, 0);
1292 }
1293 }
1294 break;
1295 case 1:
1296 idx = 0;
1297 for (i = 0; i < P; ++i)
1298 {
1299 for (k = 0; k < Q - i; ++k)
1300 {
1301 maparray[idx++] = GetMode(i, 0, k);
1302 }
1303 }
1304 break;
1305 case 2:
1306 idx = 0;
1307 for (j = 0; j < P - 1; ++j)
1308 {
1309 for (k = 0; k < Q - 1 - j; ++k)
1310 {
1311 maparray[idx++] = GetMode(1, j, k);
1312 // Incorporate modes from zeroth plane where needed.
1313 // Add in top vertex
1314 if (j == 0 && k == 0)
1315 {
1316 maparray[idx++] = GetMode(0, 0, 1);
1317 }
1318 // Add in bottom singular vertex plus singular edge
1319 if (j == 0 && k == Q - 2)
1320 {
1321 for (int r = 0; r < Q - 1; ++r)
1322 {
1323 maparray[idx++] = GetMode(0, 1, r);
1324 }
1325 }
1326 }
1327 }
1328 break;
1329 case 3:
1330 idx = 0;
1331 for (j = 0; j < P; ++j)
1332 {
1333 for (k = 0; k < Q - j; ++k)
1334 {
1335 maparray[idx++] = GetMode(0, j, k);
1336 }
1337 }
1338 break;
1339 default:
1340 ASSERTL0(false, "Element map not available.");
1341 }
1342}
1343
1344void StdTetExp::v_GetElmtTraceToTraceMap(const unsigned int fid,
1345 Array<OneD, unsigned int> &maparray,
1346 Array<OneD, int> &signarray,
1347 Orientation faceOrient, int P, int Q)
1348{
1349 int nummodesA = 0, nummodesB = 0, i, j, k, idx;
1350
1352 "Method only implemented for Modified_A BasisType (x "
1353 "direction), Modified_B BasisType (y direction), and "
1354 "Modified_C BasisType(z direction)");
1355
1356 int nFaceCoeffs = 0;
1357
1358 switch (fid)
1359 {
1360 case 0:
1361 nummodesA = m_base[0]->GetNumModes();
1362 nummodesB = m_base[1]->GetNumModes();
1363 break;
1364 case 1:
1365 nummodesA = m_base[0]->GetNumModes();
1366 nummodesB = m_base[2]->GetNumModes();
1367 break;
1368 case 2:
1369 case 3:
1370 nummodesA = m_base[1]->GetNumModes();
1371 nummodesB = m_base[2]->GetNumModes();
1372 break;
1373 default:
1374 ASSERTL0(false, "fid must be between 0 and 3");
1375 }
1376
1377 if (P == -1)
1378 {
1379 P = nummodesA;
1380 Q = nummodesB;
1381 }
1382
1383 nFaceCoeffs = P * (2 * Q - P + 1) / 2;
1384
1385 // Allocate the map array and sign array; set sign array to ones (+)
1386 if (maparray.size() != nFaceCoeffs)
1387 {
1388 maparray = Array<OneD, unsigned int>(nFaceCoeffs, 1);
1389 }
1390
1391 if (signarray.size() != nFaceCoeffs)
1392 {
1393 signarray = Array<OneD, int>(nFaceCoeffs, 1);
1394 }
1395 else
1396 {
1397 fill(signarray.data(), signarray.data() + nFaceCoeffs, 1);
1398 }
1399
1400 // zero signmap and set maparray to zero if elemental
1401 // modes are not as large as face modesl
1402 idx = 0;
1403 int cnt = 0;
1404 int minPA = min(nummodesA, P);
1405 int minQB = min(nummodesB, Q);
1406
1407 for (j = 0; j < minPA; ++j)
1408 {
1409 // set maparray
1410 for (k = 0; k < minQB - j; ++k, ++cnt)
1411 {
1412 maparray[idx++] = cnt;
1413 }
1414
1415 cnt += nummodesB - minQB;
1416
1417 for (k = nummodesB - j; k < Q - j; ++k)
1418 {
1419 signarray[idx] = 0.0;
1420 maparray[idx++] = maparray[0];
1421 }
1422 }
1423
1424 for (j = nummodesA; j < P; ++j)
1425 {
1426 for (k = 0; k < Q - j; ++k)
1427 {
1428 signarray[idx] = 0.0;
1429 maparray[idx++] = maparray[0];
1430 }
1431 }
1432
1433 if (faceOrient == eDir1BwdDir1_Dir2FwdDir2)
1434 {
1435 idx = 0;
1436 for (i = 0; i < P; ++i)
1437 {
1438 for (j = 0; j < Q - i; ++j, idx++)
1439 {
1440 if (i > 1)
1441 {
1442 signarray[idx] = (i % 2 ? -1 : 1);
1443 }
1444 }
1445 }
1446
1447 swap(maparray[0], maparray[Q]);
1448
1449 for (i = 1; i < Q - 1; ++i)
1450 {
1451 swap(maparray[i + 1], maparray[Q + i]);
1452 }
1453 }
1454}
1455
1456/**
1457 * Maps interior modes of an edge to the elemental modes.
1458 */
1460 const int eid, Array<OneD, unsigned int> &maparray,
1461 Array<OneD, int> &signarray, const Orientation edgeOrient)
1462{
1463 int i;
1464 const int P = m_base[0]->GetNumModes();
1465 const int Q = m_base[1]->GetNumModes();
1466 const int R = m_base[2]->GetNumModes();
1467
1468 const int nEdgeIntCoeffs = v_GetEdgeNcoeffs(eid) - 2;
1469
1470 if (maparray.size() != nEdgeIntCoeffs)
1471 {
1472 maparray = Array<OneD, unsigned int>(nEdgeIntCoeffs);
1473 }
1474 else
1475 {
1476 fill(maparray.data(), maparray.data() + nEdgeIntCoeffs, 0);
1477 }
1478
1479 if (signarray.size() != nEdgeIntCoeffs)
1480 {
1481 signarray = Array<OneD, int>(nEdgeIntCoeffs, 1);
1482 }
1483 else
1484 {
1485 fill(signarray.data(), signarray.data() + nEdgeIntCoeffs, 1);
1486 }
1487
1488 switch (eid)
1489 {
1490 case 0:
1491 for (i = 0; i < P - 2; ++i)
1492 {
1493 maparray[i] = GetMode(i + 2, 0, 0);
1494 }
1495 if (edgeOrient == eBackwards)
1496 {
1497 for (i = 1; i < nEdgeIntCoeffs; i += 2)
1498 {
1499 signarray[i] = -1;
1500 }
1501 }
1502 break;
1503 case 1:
1504 for (i = 0; i < Q - 2; ++i)
1505 {
1506 maparray[i] = GetMode(1, i + 1, 0);
1507 }
1508 if (edgeOrient == eBackwards)
1509 {
1510 for (i = 1; i < nEdgeIntCoeffs; i += 2)
1511 {
1512 signarray[i] = -1;
1513 }
1514 }
1515 break;
1516 case 2:
1517 for (i = 0; i < Q - 2; ++i)
1518 {
1519 maparray[i] = GetMode(0, i + 2, 0);
1520 }
1521 if (edgeOrient == eBackwards)
1522 {
1523 for (i = 1; i < nEdgeIntCoeffs; i += 2)
1524 {
1525 signarray[i] = -1;
1526 }
1527 }
1528 break;
1529 case 3:
1530 for (i = 0; i < R - 2; ++i)
1531 {
1532 maparray[i] = GetMode(0, 0, i + 2);
1533 }
1534 if (edgeOrient == eBackwards)
1535 {
1536 for (i = 1; i < nEdgeIntCoeffs; i += 2)
1537 {
1538 signarray[i] = -1;
1539 }
1540 }
1541 break;
1542 case 4:
1543 for (i = 0; i < R - 2; ++i)
1544 {
1545 maparray[i] = GetMode(1, 0, i + 1);
1546 }
1547 if (edgeOrient == eBackwards)
1548 {
1549 for (i = 1; i < nEdgeIntCoeffs; i += 2)
1550 {
1551 signarray[i] = -1;
1552 }
1553 }
1554 break;
1555 case 5:
1556 for (i = 0; i < R - 2; ++i)
1557 {
1558 maparray[i] = GetMode(0, 1, i + 1);
1559 }
1560 if (edgeOrient == eBackwards)
1561 {
1562 for (i = 1; i < nEdgeIntCoeffs; i += 2)
1563 {
1564 signarray[i] = -1;
1565 }
1566 }
1567 break;
1568 default:
1569 ASSERTL0(false, "Edge not defined.");
1570 break;
1571 }
1572}
1573
1575 const int fid, Array<OneD, unsigned int> &maparray,
1576 Array<OneD, int> &signarray, const Orientation faceOrient)
1577{
1578 int i, j, idx, k;
1579 const int P = m_base[0]->GetNumModes();
1580 const int Q = m_base[1]->GetNumModes();
1581 const int R = m_base[2]->GetNumModes();
1582
1583 const int nFaceIntCoeffs = v_GetTraceIntNcoeffs(fid);
1584
1585 if (maparray.size() != nFaceIntCoeffs)
1586 {
1587 maparray = Array<OneD, unsigned int>(nFaceIntCoeffs);
1588 }
1589
1590 if (signarray.size() != nFaceIntCoeffs)
1591 {
1592 signarray = Array<OneD, int>(nFaceIntCoeffs, 1);
1593 }
1594 else
1595 {
1596 fill(signarray.data(), signarray.data() + nFaceIntCoeffs, 1);
1597 }
1598
1599 switch (fid)
1600 {
1601 case 0:
1602 idx = 0;
1603 for (i = 2; i < P; ++i)
1604 {
1605 for (j = 1; j < Q - i; ++j)
1606 {
1607 if (faceOrient == eDir1BwdDir1_Dir2FwdDir2)
1608 {
1609 signarray[idx] = (i % 2 ? -1 : 1);
1610 }
1611 maparray[idx++] = GetMode(i, j, 0);
1612 }
1613 }
1614 break;
1615 case 1:
1616 idx = 0;
1617 for (i = 2; i < P; ++i)
1618 {
1619 for (k = 1; k < R - i; ++k)
1620 {
1621 if (faceOrient == eDir1BwdDir1_Dir2FwdDir2)
1622 {
1623 signarray[idx] = (i % 2 ? -1 : 1);
1624 }
1625 maparray[idx++] = GetMode(i, 0, k);
1626 }
1627 }
1628 break;
1629 case 2:
1630 idx = 0;
1631 for (j = 1; j < Q - 1; ++j)
1632 {
1633 for (k = 1; k < R - 1 - j; ++k)
1634 {
1635 if (faceOrient == eDir1BwdDir1_Dir2FwdDir2)
1636 {
1637 signarray[idx] = ((j + 1) % 2 ? -1 : 1);
1638 }
1639 maparray[idx++] = GetMode(1, j, k);
1640 }
1641 }
1642 break;
1643 case 3:
1644 idx = 0;
1645 for (j = 2; j < Q; ++j)
1646 {
1647 for (k = 1; k < R - j; ++k)
1648 {
1649 if (faceOrient == eDir1BwdDir1_Dir2FwdDir2)
1650 {
1651 signarray[idx] = (j % 2 ? -1 : 1);
1652 }
1653 maparray[idx++] = GetMode(0, j, k);
1654 }
1655 }
1656 break;
1657 default:
1658 ASSERTL0(false, "Face interior map not available.");
1659 break;
1660 }
1661}
1662//---------------------------------------
1663// Wrapper functions
1664//---------------------------------------
1666{
1667
1668 MatrixType mtype = mkey.GetMatrixType();
1669
1670 DNekMatSharedPtr Mat;
1671
1672 switch (mtype)
1673 {
1675 {
1676 int nq0 = m_base[0]->GetNumPoints();
1677 int nq1 = m_base[1]->GetNumPoints();
1678 int nq2 = m_base[2]->GetNumPoints();
1679 int nq;
1680
1681 // take definition from key
1683 {
1684 nq = (int)mkey.GetConstFactor(eFactorConst);
1685 }
1686 else
1687 {
1688 nq = max(nq0, max(nq1, nq2));
1689 }
1690
1691 int neq =
1694 Array<OneD, NekDouble> coll(3);
1696 Array<OneD, NekDouble> tmp(nq0);
1697
1698 Mat =
1699 MemoryManager<DNekMat>::AllocateSharedPtr(neq, nq0 * nq1 * nq2);
1700 int cnt = 0;
1701
1702 for (int i = 0; i < nq; ++i)
1703 {
1704 for (int j = 0; j < nq - i; ++j)
1705 {
1706 for (int k = 0; k < nq - i - j; ++k, ++cnt)
1707 {
1708 coords[cnt] = Array<OneD, NekDouble>(3);
1709 coords[cnt][0] = -1.0 + 2 * k / (NekDouble)(nq - 1);
1710 coords[cnt][1] = -1.0 + 2 * j / (NekDouble)(nq - 1);
1711 coords[cnt][2] = -1.0 + 2 * i / (NekDouble)(nq - 1);
1712 }
1713 }
1714 }
1715
1716 for (int i = 0; i < neq; ++i)
1717 {
1718 LocCoordToLocCollapsed(coords[i], coll);
1719
1720 I[0] = m_base[0]->GetI(coll);
1721 I[1] = m_base[1]->GetI(coll + 1);
1722 I[2] = m_base[2]->GetI(coll + 2);
1723
1724 // interpolate first coordinate direction
1725 NekDouble fac;
1726 for (int k = 0; k < nq2; ++k)
1727 {
1728 for (int j = 0; j < nq1; ++j)
1729 {
1730
1731 fac = (I[1]->GetPtr())[j] * (I[2]->GetPtr())[k];
1732 Vmath::Smul(nq0, fac, I[0]->GetPtr(), 1, tmp, 1);
1733
1734 Vmath::Vcopy(nq0, &tmp[0], 1,
1735 Mat->GetRawPtr() + k * nq0 * nq1 * neq +
1736 j * nq0 * neq + i,
1737 neq);
1738 }
1739 }
1740 }
1741 }
1742 break;
1743 case ePhysInterpToGLL:
1744 {
1745 int nq0 = m_base[0]->GetNumPoints();
1746 int nq1 = m_base[1]->GetNumPoints();
1747 int nq2 = m_base[2]->GetNumPoints();
1748 int nq;
1749
1750 // take definition from key
1752 {
1753 nq = (int)mkey.GetConstFactor(eFactorConst);
1754 }
1755 else
1756 {
1757 nq = max(nq0, max(nq1, nq2));
1758 }
1759
1760 int neq =
1762 Array<OneD, NekDouble> coords(3);
1763 Array<OneD, NekDouble> coll(3);
1765 Array<OneD, NekDouble> tmp(nq0);
1766
1767 Mat =
1768 MemoryManager<DNekMat>::AllocateSharedPtr(neq, nq0 * nq1 * nq2);
1769
1771
1773 LibUtilities::PointsManager()[key]->GetPoints(x, y, z);
1774
1775 Array<OneD, int> sorted;
1777
1778 for (int i = 0; i < neq; ++i)
1779 {
1780 coords[0] = x[sorted[i]];
1781 coords[1] = y[sorted[i]];
1782 coords[2] = z[sorted[i]];
1783
1784 LocCoordToLocCollapsed(coords, coll);
1785
1786 I[0] = m_base[0]->GetI(coll);
1787 I[1] = m_base[1]->GetI(coll + 1);
1788 I[2] = m_base[2]->GetI(coll + 2);
1789
1790 // interpolate first coordinate direction
1791 NekDouble fac;
1792 for (int k = 0; k < nq2; ++k)
1793 {
1794 for (int j = 0; j < nq1; ++j)
1795 {
1796
1797 fac = (I[1]->GetPtr())[j] * (I[2]->GetPtr())[k];
1798 Vmath::Smul(nq0, fac, I[0]->GetPtr(), 1, tmp, 1);
1799
1800 Vmath::Vcopy(nq0, &tmp[0], 1,
1801 Mat->GetRawPtr() + k * nq0 * nq1 * neq +
1802 j * nq0 * neq + i,
1803 neq);
1804 }
1805 }
1806 }
1807 // need to set up test?
1808 }
1809 break;
1810 default:
1811 {
1813 }
1814 break;
1815 }
1816
1817 return Mat;
1818}
1819
1821{
1822 return v_GenMatrix(mkey);
1823}
1824
1825//---------------------------------------
1826// Private helper functions
1827//---------------------------------------
1828
1829/**
1830 * @brief Compute the mode number in the expansion for a particular
1831 * tensorial combination.
1832 *
1833 * Modes are numbered with the r index travelling fastest, followed by
1834 * q and then p, and each q-r plane is of size
1835 * (Q+1)*(Q+2)/2+max(0,R-Q-p)*Q. For example, when P=2, Q=3 and R=4 (nm0=3, nm1
1836 * = 4, nm2 = 5) the indexing inside each q-r plane (with r increasing upwards
1837 * and q to the right) is:
1838 *
1839 * 4
1840 * 3 8 17
1841 * 2 7 11 16 20 25
1842 * 1 6 10 13 15 19 22 24 27
1843 * 0 5 9 12 14 18 21 23 26
1844 *
1845 * Geometrically they can be interpreted as
1846 * p = 0: p = 2: p = 1:
1847 * ----------------------------------
1848 * 1
1849 * 4 8 17
1850 * 3 11 7 25 16 20
1851 * 2 10 13 6 24 27 15 19 22
1852 * 0 9 12 5 23 26 14 18 21
1853 *
1854 * so we have the following breakdown
1855 *
1856 * Vertices V[0,1,2,3] = [0, 14, 5, 1]
1857 * Edges E[0,1,2,3,4,5,6] =[[23],[18, 21],
1858 * [9, 12], [2,3,4], [15, 16, 17], [6,7,8]]
1859 * Faces F[0.1,2,3] = [[26], [24,25], [19, 22, 20], [10, 13, 11]
1860 * Interior [27]
1861 * Note that in this element, we must have that \f$ P \leq Q \leq
1862 * R\f$.
1863 */
1864int StdTetExp::GetMode(const int I, const int J, const int K)
1865{
1866 const int Q = m_base[1]->GetNumModes();
1867 const int R = m_base[2]->GetNumModes();
1868
1869 int i, j, q_hat, k_hat;
1870 int cnt = 0;
1871
1872 // Traverse to q-r plane number I
1873 for (i = 0; i < I; ++i)
1874 {
1875 // Size of triangle part
1876 q_hat = Q - i;
1877 // Size of rectangle part
1878 k_hat = R - Q;
1879 cnt += q_hat * (q_hat + 1) / 2 + k_hat * (Q - i);
1880 }
1881
1882 // Traverse to q column J
1883 q_hat = R - I;
1884 for (j = 0; j < J; ++j)
1885 {
1886 cnt += q_hat;
1887 q_hat--;
1888 }
1889
1890 // Traverse up stacks to K
1891 cnt += K;
1892
1893 return cnt;
1894}
1895
1897 const StdMatrixKey &mkey)
1898{
1899 // To do : 1) add a test to ensure 0 \leq SvvCutoff \leq 1.
1900 // 2) check if the transfer function needs an analytical
1901 // Fourier transform.
1902 // 3) if it doesn't : find a transfer function that renders
1903 // the if( cutoff_a ...) useless to reduce computational
1904 // cost.
1905 // 4) add SVVDiffCoef to both models!!
1906
1907 int qa = m_base[0]->GetNumPoints();
1908 int qb = m_base[1]->GetNumPoints();
1909 int qc = m_base[2]->GetNumPoints();
1910 int nmodes_a = m_base[0]->GetNumModes();
1911 int nmodes_b = m_base[1]->GetNumModes();
1912 int nmodes_c = m_base[2]->GetNumModes();
1913
1914 // Declare orthogonal basis.
1918
1922
1923 StdTetExp OrthoExp(Ba, Bb, Bc);
1924
1925 Array<OneD, NekDouble> orthocoeffs(OrthoExp.GetNcoeffs());
1926 int i, j, k, cnt = 0;
1927
1928 // project onto physical space.
1929 OrthoExp.FwdTrans(array, orthocoeffs);
1930
1932 {
1933 // Rodrigo's power kernel
1935 NekDouble SvvDiffCoeff =
1938
1939 for (i = 0; i < nmodes_a; ++i)
1940 {
1941 for (j = 0; j < nmodes_b - j; ++j)
1942 {
1943 NekDouble fac1 = std::max(
1944 pow((1.0 * i) / (nmodes_a - 1), cutoff * nmodes_a),
1945 pow((1.0 * j) / (nmodes_b - 1), cutoff * nmodes_b));
1946
1947 for (k = 0; k < nmodes_c - i - j; ++k)
1948 {
1949 NekDouble fac =
1950 std::max(fac1, pow((1.0 * k) / (nmodes_c - 1),
1951 cutoff * nmodes_c));
1952
1953 orthocoeffs[cnt] *= SvvDiffCoeff * fac;
1954 cnt++;
1955 }
1956 }
1957 }
1958 }
1959 else if (mkey.ConstFactorExists(
1960 eFactorSVVDGKerDiffCoeff)) // Rodrigo/Mansoor's DG Kernel
1961 {
1964
1965 int max_abc = max(nmodes_a - kSVVDGFiltermodesmin,
1966 nmodes_b - kSVVDGFiltermodesmin);
1967 max_abc = max(max_abc, nmodes_c - kSVVDGFiltermodesmin);
1968 // clamp max_abc
1969 max_abc = max(max_abc, 0);
1970 max_abc = min(max_abc, kSVVDGFiltermodesmax - kSVVDGFiltermodesmin);
1971
1972 for (i = 0; i < nmodes_a; ++i)
1973 {
1974 for (j = 0; j < nmodes_b - j; ++j)
1975 {
1976 int maxij = max(i, j);
1977
1978 for (k = 0; k < nmodes_c - i - j; ++k)
1979 {
1980 int maxijk = max(maxij, k);
1981 maxijk = min(maxijk, kSVVDGFiltermodesmax - 1);
1982
1983 orthocoeffs[cnt] *=
1984 SvvDiffCoeff * kSVVDGFilter[max_abc][maxijk];
1985 cnt++;
1986 }
1987 }
1988 }
1989 }
1990 else
1991 {
1992
1993 // SVV filter paramaters (how much added diffusion
1994 // relative to physical one and fraction of modes from
1995 // which you start applying this added diffusion)
1996
1997 NekDouble SvvDiffCoeff =
1999 NekDouble SVVCutOff =
2001
2002 // Defining the cut of mode
2003 int cutoff_a = (int)(SVVCutOff * nmodes_a);
2004 int cutoff_b = (int)(SVVCutOff * nmodes_b);
2005 int cutoff_c = (int)(SVVCutOff * nmodes_c);
2006 int nmodes = min(min(nmodes_a, nmodes_b), nmodes_c);
2007 NekDouble cutoff = min(min(cutoff_a, cutoff_b), cutoff_c);
2008 NekDouble epsilon = 1;
2009
2010 //------"New" Version August 22nd '13--------------------
2011 for (i = 0; i < nmodes_a; ++i)
2012 {
2013 for (j = 0; j < nmodes_b - i; ++j)
2014 {
2015 for (k = 0; k < nmodes_c - i - j; ++k)
2016 {
2017 if (i + j + k >= cutoff)
2018 {
2019 orthocoeffs[cnt] *= ((SvvDiffCoeff)*exp(
2020 -(i + j + k - nmodes) * (i + j + k - nmodes) /
2021 ((NekDouble)((i + j + k - cutoff + epsilon) *
2022 (i + j + k - cutoff + epsilon)))));
2023 }
2024 else
2025 {
2026 orthocoeffs[cnt] *= 0.0;
2027 }
2028 cnt++;
2029 }
2030 }
2031 }
2032 }
2033
2034 // backward transform to physical space
2035 OrthoExp.BwdTrans(orthocoeffs, array);
2036}
2037
2039 const Array<OneD, const NekDouble> &inarray,
2040 Array<OneD, NekDouble> &outarray)
2041{
2042 int nquad0 = m_base[0]->GetNumPoints();
2043 int nquad1 = m_base[1]->GetNumPoints();
2044 int nquad2 = m_base[2]->GetNumPoints();
2045 int nqtot = nquad0 * nquad1 * nquad2;
2046 int nmodes0 = m_base[0]->GetNumModes();
2047 int nmodes1 = m_base[1]->GetNumModes();
2048 int nmodes2 = m_base[2]->GetNumModes();
2049 int numMax = nmodes0;
2050
2052 Array<OneD, NekDouble> coeff_tmp1(m_ncoeffs, 0.0);
2053 Array<OneD, NekDouble> coeff_tmp2(m_ncoeffs, 0.0);
2054 Array<OneD, NekDouble> phys_tmp(nqtot, 0.0);
2055 Array<OneD, NekDouble> tmp, tmp2, tmp3, tmp4;
2056
2057 Vmath::Vcopy(m_ncoeffs, inarray, 1, coeff_tmp2, 1);
2058
2059 const LibUtilities::PointsKey Pkey0 = m_base[0]->GetPointsKey();
2060 const LibUtilities::PointsKey Pkey1 = m_base[1]->GetPointsKey();
2061 const LibUtilities::PointsKey Pkey2 = m_base[2]->GetPointsKey();
2062
2063 LibUtilities::BasisKey bortho0(LibUtilities::eOrtho_A, nmodes0, Pkey0);
2064 LibUtilities::BasisKey bortho1(LibUtilities::eOrtho_B, nmodes1, Pkey1);
2065 LibUtilities::BasisKey bortho2(LibUtilities::eOrtho_C, nmodes2, Pkey2);
2066
2067 Vmath::Zero(m_ncoeffs, coeff_tmp2, 1);
2068
2071 bortho0, bortho1, bortho2);
2072
2073 BwdTrans(inarray, phys_tmp);
2074 OrthoTetExp->FwdTrans(phys_tmp, coeff);
2075
2076 Vmath::Zero(m_ncoeffs, outarray, 1);
2077
2078 // filtering
2079 int cnt = 0;
2080 for (int u = 0; u < numMin; ++u)
2081 {
2082 for (int i = 0; i < numMin - u; ++i)
2083 {
2084 Vmath::Vcopy(numMin - u - i, tmp = coeff + cnt, 1,
2085 tmp2 = coeff_tmp1 + cnt, 1);
2086 cnt += numMax - u - i;
2087 }
2088 for (int i = numMin; i < numMax - u; ++i)
2089 {
2090 cnt += numMax - u - i;
2091 }
2092 }
2093
2094 OrthoTetExp->BwdTrans(coeff_tmp1, phys_tmp);
2095 FwdTrans(phys_tmp, outarray);
2096}
2097
2099 Array<OneD, int> &conn, [[maybe_unused]] bool standard)
2100{
2101 int np0 = m_base[0]->GetNumPoints();
2102 int np1 = m_base[1]->GetNumPoints();
2103 int np2 = m_base[2]->GetNumPoints();
2104 int np = max(np0, max(np1, np2));
2105
2106 conn = Array<OneD, int>(4 * (np - 1) * (np - 1) * (np - 1));
2107
2108 int row = 0;
2109 int rowp1 = 0;
2110 int plane = 0;
2111 int row1 = 0;
2112 int row1p1 = 0;
2113 int planep1 = 0;
2114 int cnt = 0;
2115 for (int i = 0; i < np - 1; ++i)
2116 {
2117 planep1 += (np - i) * (np - i + 1) / 2;
2118 row = 0; // current plane row offset
2119 rowp1 = 0; // current plane row plus one offset
2120 row1 = 0; // next plane row offset
2121 row1p1 = 0; // nex plane row plus one offset
2122 for (int j = 0; j < np - i - 1; ++j)
2123 {
2124 rowp1 += np - i - j;
2125 row1p1 += np - i - j - 1;
2126 for (int k = 0; k < np - i - j - 2; ++k)
2127 {
2128 conn[cnt++] = plane + row + k + 1;
2129 conn[cnt++] = plane + row + k;
2130 conn[cnt++] = plane + rowp1 + k;
2131 conn[cnt++] = planep1 + row1 + k;
2132
2133 conn[cnt++] = plane + row + k + 1;
2134 conn[cnt++] = plane + rowp1 + k + 1;
2135 conn[cnt++] = planep1 + row1 + k + 1;
2136 conn[cnt++] = planep1 + row1 + k;
2137
2138 conn[cnt++] = plane + rowp1 + k + 1;
2139 conn[cnt++] = plane + row + k + 1;
2140 conn[cnt++] = plane + rowp1 + k;
2141 conn[cnt++] = planep1 + row1 + k;
2142
2143 conn[cnt++] = planep1 + row1 + k;
2144 conn[cnt++] = planep1 + row1p1 + k;
2145 conn[cnt++] = plane + rowp1 + k;
2146 conn[cnt++] = plane + rowp1 + k + 1;
2147
2148 conn[cnt++] = planep1 + row1 + k;
2149 conn[cnt++] = planep1 + row1p1 + k;
2150 conn[cnt++] = planep1 + row1 + k + 1;
2151 conn[cnt++] = plane + rowp1 + k + 1;
2152
2153 if (k < np - i - j - 3)
2154 {
2155 conn[cnt++] = plane + rowp1 + k + 1;
2156 conn[cnt++] = planep1 + row1p1 + k + 1;
2157 conn[cnt++] = planep1 + row1 + k + 1;
2158 conn[cnt++] = planep1 + row1p1 + k;
2159 }
2160 }
2161
2162 conn[cnt++] = plane + row + np - i - j - 1;
2163 conn[cnt++] = plane + row + np - i - j - 2;
2164 conn[cnt++] = plane + rowp1 + np - i - j - 2;
2165 conn[cnt++] = planep1 + row1 + np - i - j - 2;
2166
2167 row += np - i - j;
2168 row1 += np - i - j - 1;
2169 }
2170 plane += (np - i) * (np - i + 1) / 2;
2171 }
2172}
2173
2174} // namespace Nektar::StdRegions
#define ASSERTL0(condition, msg)
#define ASSERTL1(condition, msg)
Assert Level 1 – Debugging which is used whether in FULLDEBUG or DEBUG compilation mode....
#define ASSERTL2(condition, msg)
Assert Level 2 – Debugging which is used FULLDEBUG compilation mode. This level assert is designed to...
#define BWDTRANS_M(r, i)
#define IPRODUCTWRTBASE_DEF
#define BWDTRANS_DEF
#define IPRODUCTWRTBASE_M(r, i)
#define STDLEV2TEST(r, state)
#define STDLEV2UPDATE(r, state)
Describes the specification for a Basis.
Definition Basis.h:45
int GetNumModes() const
Returns the order of the basis.
Definition Basis.h:74
static void CartesianOrdering(const int nq, Array< OneD, int > &sorted)
Definition NodalUtil.h:256
Defines a specification for a set of points.
Definition Points.h:50
static std::shared_ptr< DataType > AllocateSharedPtr(const Args &...args)
Allocate a shared pointer from the memory pool.
NekDouble BaryTensorDeriv(const Array< OneD, NekDouble > &coord, const Array< OneD, const NekDouble > &inarray, std::array< NekDouble, 3 > &firstOrderDerivs)
void PhysTensorDeriv(const Array< OneD, const NekDouble > &inarray, Array< OneD, NekDouble > &out_d0, Array< OneD, NekDouble > &out_d1, Array< OneD, NekDouble > &out_d2)
Calculate the 3D derivative in the local tensor/collapsed coordinate at the physical points.
void v_PhysDeriv(const int dir, const Array< OneD, const NekDouble > &inarray, Array< OneD, NekDouble > &outarray) override
Calculate the derivative of the physical points in a given direction.
The base class for all shapes.
int GetNcoeffs(void) const
This function returns the total number of coefficients used in the expansion.
int GetTotPoints() const
This function returns the total number of quadrature points used in the element.
LibUtilities::BasisType GetBasisType(const int dir) const
This function returns the type of basis used in the dir direction.
void LocCoordToLocCollapsed(const Array< OneD, const NekDouble > &xi, Array< OneD, NekDouble > &eta)
Convert local cartesian coordinate xi into local collapsed coordinates eta.
const Array< OneD, const LibUtilities::BasisSharedPtr > & GetBase() const
This function gets the shared point to basis.
DNekMatSharedPtr CreateGeneralMatrix(const StdMatrixKey &mkey)
this function generates the mass matrix
NekDouble PhysEvaluate(const Array< OneD, const NekDouble > &coords, const Array< OneD, const NekDouble > &physvals)
This function evaluates the expansion at a single (arbitrary) point of the domain.
void BwdTrans(const Array< OneD, const NekDouble > &inarray, Array< OneD, NekDouble > &outarray)
This function performs the Backward transformation from coefficient space to physical space.
LibUtilities::PointsType GetPointsType(const int dir) const
This function returns the type of quadrature points used in the dir direction.
void FwdTrans(const Array< OneD, const NekDouble > &inarray, Array< OneD, NekDouble > &outarray)
int GetNumPoints(const int dir) const
This function returns the number of quadrature points in the dir direction.
Array< OneD, const NekDouble > GetStdFac(const StdFacKey &mkey)
int GetBasisNumModes(const int dir) const
This function returns the number of expansion modes in the dir direction.
Array< OneD, LibUtilities::BasisSharedPtr > m_base
std::vector< Array< OneD, const NekDouble > > m_weights
MatrixType GetMatrixType() const
NekDouble GetConstFactor(const ConstFactorType &factor) const
bool ConstFactorExists(const ConstFactorType &factor) const
int v_GetNtraces() const override
int v_GetTraceNcoeffs(const int i) const override
NekDouble v_PhysEvalFirstDeriv(const Array< OneD, NekDouble > &coord, const Array< OneD, const NekDouble > &inarray, std::array< NekDouble, 3 > &firstOrderDerivs) override
void v_GetTraceNumModes(const int fid, int &numModes0, int &numModes1, Orientation traceOrient=eDir1FwdDir1_Dir2FwdDir2) override
int v_GetTraceIntNcoeffs(const int i) const override
void v_FillMode(const int mode, Array< OneD, NekDouble > &outarray) override
void v_GetInteriorMap(Array< OneD, unsigned int > &outarray) override
int v_GetNedges() const override
int GetMode(const int i, const int j, const int k)
Compute the mode number in the expansion for a particular tensorial combination.
int v_GetEdgeNcoeffs(const int i) const override
bool v_IsBoundaryInteriorExpansion() const override
LibUtilities::PointsKey v_GetTracePointsKey(const int i, const int j) const override
NekDouble v_PhysEvaluateBasis(const Array< OneD, const NekDouble > &coords, int mode) final
void v_GetEdgeInteriorToElementMap(const int tid, Array< OneD, unsigned int > &maparray, Array< OneD, int > &signarray, const Orientation traceOrient=eDir1FwdDir1_Dir2FwdDir2) override
int v_GetTraceNumPoints(const int i) const override
void v_SVVLaplacianFilter(Array< OneD, NekDouble > &array, const StdMatrixKey &mkey) override
DNekMatSharedPtr v_CreateStdMatrix(const StdMatrixKey &mkey) override
void v_LocCoordToLocCollapsed(const Array< OneD, const NekDouble > &xi, Array< OneD, NekDouble > &eta) override
void v_GetBoundaryMap(Array< OneD, unsigned int > &outarray) override
void v_GetSimplexEquiSpacedConnectivity(Array< OneD, int > &conn, bool standard=true) override
int v_NumBndryCoeffs() const override
void v_BwdTrans(const Array< OneD, const NekDouble > &inarray, Array< OneD, NekDouble > &outarray) override
void v_IProductWRTDerivBase(const int dir, const Array< OneD, const NekDouble > &inarray, Array< OneD, NekDouble > &outarray) override
void v_ReduceOrderCoeffs(int numMin, const Array< OneD, const NekDouble > &inarray, Array< OneD, NekDouble > &outarray) override
void v_GetCoords(Array< OneD, NekDouble > &coords_x, Array< OneD, NekDouble > &coords_y, Array< OneD, NekDouble > &coords_z) override
void v_GetTraceCoeffMap(const unsigned int fid, Array< OneD, unsigned int > &maparray) override
void v_GetTraceInteriorToElementMap(const int tid, Array< OneD, unsigned int > &maparray, Array< OneD, int > &signarray, const Orientation traceOrient=eDir1FwdDir1_Dir2FwdDir2) override
void v_GetElmtTraceToTraceMap(const unsigned int tid, Array< OneD, unsigned int > &maparray, Array< OneD, int > &signarray, Orientation traceOrient=eForwards, int P=-1, int Q=-1) override
int v_GetVertexMap(int localVertexId, bool useCoeffPacking=false) override
int v_GetNverts() const override
void v_StdPhysDeriv(const Array< OneD, const NekDouble > &inarray, Array< OneD, NekDouble > &out_dx, Array< OneD, NekDouble > &out_dy, Array< OneD, NekDouble > &out_dz) override
Calculate the derivative of the physical points.
const LibUtilities::BasisKey v_GetTraceBasisKey(const int i, const int k, bool UseGLL=false) const override
LibUtilities::ShapeType v_DetShapeType() const override
void v_LocCollapsedToLocCoord(const Array< OneD, const NekDouble > &eta, Array< OneD, NekDouble > &xi) override
int v_NumDGBndryCoeffs() const override
void v_IProductWRTBaseKernel(const Array< OneD, const NekDouble > &base0, const Array< OneD, const NekDouble > &base1, const Array< OneD, const NekDouble > &base2, const Array< OneD, const NekDouble > &inarray, Array< OneD, NekDouble > &outarray, const Array< OneD, NekDouble > &jac, const bool Deformed, bool CollDir0=false, bool CollDir1=false, bool CollDir2=false) override
Inner product of inarray over region with respect to the expansion basis (this)->m_base[0] and return...
int v_CalcNumberOfCoefficients(const std::vector< unsigned int > &nummodes, int &modes_offset) override
DNekMatSharedPtr v_GenMatrix(const StdMatrixKey &mkey) override
constexpr int getNumberOfCoefficients(int Na, int Nb, int Nc)
constexpr int getNumberOfBndCoefficients(int Na, int Nb, int Nc)
PointsManagerT & PointsManager(void)
@ eNodalTetElec
3D Nodal Electrostatic Points on a Tetrahedron
Definition PointsType.h:85
@ eModified_B
Principle Modified Functions .
Definition BasisType.h:49
@ eOrtho_A
Principle Orthogonal Functions .
Definition BasisType.h:42
@ eModified_C
Principle Modified Functions .
Definition BasisType.h:50
@ eGLL_Lagrange
Lagrange for SEM basis .
Definition BasisType.h:56
@ eOrtho_C
Principle Orthogonal Functions .
Definition BasisType.h:46
@ eOrtho_B
Principle Orthogonal Functions .
Definition BasisType.h:44
@ eModified_A
Principle Modified Functions .
Definition BasisType.h:48
static const NekDouble kNekZeroTol
std::shared_ptr< StdTetExp > StdTetExpSharedPtr
Definition StdTetExp.h:187
LibUtilities::BasisKey EvaluateTriFaceBasisKey(const int facedir, const LibUtilities::BasisSharedPtr &faceDirBasis, bool UseGLL)
const int kSVVDGFiltermodesmin
tinysimd::scalarT< double > vec_t
const int kSVVDGFiltermodesmax
const NekDouble kSVVDGFilter[9][11]
static Array< OneD, NekDouble > NullNekDouble1DArray
std::shared_ptr< DNekMat > DNekMatSharedPtr
void Vmul(int n, const T *x, const int incx, const T *y, const int incy, T *z, const int incz)
Multiply vector z = x*y.
Definition Vmath.hpp:72
void Vadd(int n, const T *x, const int incx, const T *y, const int incy, T *z, const int incz)
Add vector z = x+y.
Definition Vmath.hpp:180
void Smul(int n, const T alpha, const T *x, const int incx, T *y, const int incy)
Scalar multiply y = alpha*x.
Definition Vmath.hpp:100
void Zero(int n, T *x, const int incx)
Zero vector.
Definition Vmath.hpp:273
void Sadd(int n, const T alpha, const T *x, const int incx, T *y, const int incy)
Add vector y = alpha + x.
Definition Vmath.hpp:194
void Vcopy(int n, const T *x, const int incx, T *y, const int incy)
Definition Vmath.hpp:825
STL namespace.
scalarT< T > max(scalarT< T > lhs, scalarT< T > rhs)
Definition scalar.hpp:305
scalarT< T > min(scalarT< T > lhs, scalarT< T > rhs)
Definition scalar.hpp:300
scalarT< T > sqrt(scalarT< T > in)
Definition scalar.hpp:290