Nektar++
Loading...
Searching...
No Matches
Foundations/Basis.cpp
Go to the documentation of this file.
1///////////////////////////////////////////////////////////////////////////////
2//
3// File: Basis.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: Basis definition
32//
33///////////////////////////////////////////////////////////////////////////////
34
38
40{
43
44bool operator<(const BasisKey &lhs, const BasisKey &rhs)
45{
46 PointsKey lhsPointsKey = lhs.GetPointsKey();
47 PointsKey rhsPointsKey = rhs.GetPointsKey();
48
49 if (lhsPointsKey < rhsPointsKey)
50 {
51 return true;
52 }
53 if (lhsPointsKey != rhsPointsKey)
54 {
55 return false;
56 }
57
58 if (lhs.m_nummodes < rhs.m_nummodes)
59 {
60 return true;
61 }
62 if (lhs.m_nummodes > rhs.m_nummodes)
63 {
64 return false;
65 }
66
67 return (lhs.m_basistype < rhs.m_basistype);
68}
69
70bool operator>(const BasisKey &lhs, const BasisKey &rhs)
71{
72 return (rhs < lhs);
73}
74
76 const BasisKey &rhs) const
77{
78 return (lhs.m_basistype < rhs.m_basistype);
79}
80
81std::ostream &operator<<(std::ostream &os, const BasisKey &rhs)
82{
83 os << "NumModes: " << rhs.GetNumModes()
84 << " BasisType: " << BasisTypeMap[rhs.GetBasisType()];
85 os << " " << rhs.GetPointsKey() << std::endl;
86
87 return os;
88}
89
91 : m_basisKey(bkey), m_points(PointsManager()[bkey.GetPointsKey()]),
92 m_bdata(bkey.GetTotNumModes() * bkey.GetTotNumPoints()),
93 m_dbdata(bkey.GetTotNumModes() * bkey.GetTotNumPoints())
94{
95 m_InterpManager.RegisterGlobalCreator(
96 std::bind(&Basis::CalculateInterpMatrix, this, std::placeholders::_1));
97}
98
99std::shared_ptr<Basis> Basis::Create(const BasisKey &bkey)
100{
101 std::shared_ptr<Basis> returnval(new Basis(bkey));
102 returnval->Initialize();
103
104 return returnval;
105}
106
108{
109 ASSERTL0(GetNumModes() > 0,
110 "Cannot call Basis initialisation with zero or negative order");
111 ASSERTL0(GetTotNumPoints() > 0, "Cannot call Basis initialisation with "
112 "zero or negative numbers of points");
113
114 GenBasis();
115}
116
117/** \brief Calculate the interpolation Matrix for coefficient from
118 * one base (m_basisKey) to another (tbasis0)
119 * In the new version, we do not enforce the interpolation matrix to
120 * be square. The output dimension will be determined by tBasis and
121 * input dimension will be determined by m_basisKey.
122 * - If tBasis.GetNumModes() > m_basisKey.GetNumModes(), the interpolation
123 * matrix will be $B^{(l-1,l)T}_f B^{(-l)T}_t$, using tBasis.GetNumModes() as
124 * basis points.
125 * - If tBasis.GetNumModes() <= m_basisKey.GetNumModes(), the interpolation
126 * matrix will be $B^{(-l)}_f B^{(l,l-1)}_t$, using m_basisKey.GetNumModes() as
127 * basis points. which is the same as the old version.
128 */
129std::shared_ptr<NekMatrix<NekDouble>> Basis::CalculateInterpMatrix(
130 const BasisKey &tbasis0)
131{
132 // Get the number of modes of the target basis
133 size_t tdim = tbasis0.GetNumModes();
134 // Get the number of modes of the source basis
135 size_t fdim = m_basisKey.GetNumModes();
136 if (tdim < fdim)
137 {
138 size_t dim = fdim;
140 BasisKey fbkey(m_basisKey.GetBasisType(), fdim, pkey);
141 BasisKey tbkey(tbasis0.GetBasisType(), tdim, pkey);
142
143 // "Constructur" of the basis
144 BasisSharedPtr fbasis = BasisManager()[fbkey];
145 BasisSharedPtr tbasis = BasisManager()[tbkey];
146
147 // Get B Matrices
148 Array<OneD, NekDouble> fB_data = fbasis->GetBdata();
149 Array<OneD, NekDouble> tB_data = tbasis->GetBdata();
150
151 // Convert to a NekMatrix
152 NekMatrix<NekDouble> fB(fdim, dim, fB_data);
153 NekMatrix<NekDouble> tB(dim, tdim, tB_data);
154
155 // Invert the "from" matrix
156 fB.Invert();
157
158 // Compute transformation matrix
159 Array<OneD, NekDouble> zero1D(tdim * fdim, 0.0);
160 std::shared_ptr<NekMatrix<NekDouble>> ftB(
161 MemoryManager<NekMatrix<NekDouble>>::AllocateSharedPtr(fdim, tdim,
162 zero1D));
163 // tu := tB^T * fB^(-1)^T fu = ftB fu
164 (*ftB) = fB * tB;
165 // this operation will not acturally do the transpose, just set the
166 // transpose flag to 'T' ftB->Transpose(); we need to do the transpose
167 // manually
168 std::shared_ptr<NekMatrix<NekDouble>> ftB_T(
169 MemoryManager<NekMatrix<NekDouble>>::AllocateSharedPtr(tdim, fdim,
170 0.0));
171 for (size_t i = 0; i < tdim; ++i)
172 {
173 for (size_t j = 0; j < fdim; ++j)
174 {
175 (*ftB_T)(i, j) = (*ftB)(j, i);
176 }
177 }
178 return ftB_T;
179 }
180 else // original version
181 {
182 size_t dim = tdim;
184 BasisKey fbkey(m_basisKey.GetBasisType(), fdim, pkey);
185 BasisKey tbkey(tbasis0.GetBasisType(), tdim, pkey);
186
187 // "Constructur" of the basis
188 BasisSharedPtr fbasis = BasisManager()[fbkey];
189 BasisSharedPtr tbasis = BasisManager()[tbkey];
190
191 // Get B Matrices
192 Array<OneD, NekDouble> fB_data = fbasis->GetBdata();
193 Array<OneD, NekDouble> tB_data = tbasis->GetBdata();
194
195 // Convert to a NekMatrix
196 NekMatrix<NekDouble> fB(dim, fdim, fB_data);
197 NekMatrix<NekDouble> tB(dim, tdim, tB_data);
198
199 // Invert the "to" matrix: tu := tB^(-1)*fB fu = ftB fu
200 tB.Invert();
201
202 // Compute transformation matrix
203 Array<OneD, NekDouble> zero1D(tdim * fdim, 0.0);
204 std::shared_ptr<NekMatrix<NekDouble>> ftB(
205 MemoryManager<NekMatrix<NekDouble>>::AllocateSharedPtr(tdim, fdim,
206 zero1D));
207 (*ftB) = tB * fB;
208
209 return ftB;
210 }
211}
212
213// Method used to generate appropriate basis
214/** The following expansions are generated depending on the
215 * enum type defined in \a m_basisKey.m_basistype:
216 *
217 * NOTE: This definition does not follow the order in the
218 * Karniadakis \& Sherwin book since this leads to a more
219 * compact hierarchical pattern for implementation
220 * purposes. The order of these modes dictates the
221 * ordering of the expansion coefficients.
222 *
223 * In the following m_numModes = P
224 *
225 * \a eModified_A:
226 *
227 * m_bdata[i + j*m_numpoints] =
228 * \f$ \phi^a_i(z_j) = \left \{
229 * \begin{array}{ll} \left ( \frac{1-z_j}{2}\right ) & i = 0 \\
230 * \\
231 * \left ( \frac{1+z_j}{2}\right ) & i = 1 \\
232 * \\
233 * \left ( \frac{1-z_j}{2}\right )\left ( \frac{1+z_j}{2}\right )
234 * P^{1,1}_{i-2}(z_j) & 2\leq i < P\\
235 * \end{array} \right . \f$
236 *
237 * \a eModified_B:
238 *
239 * m_bdata[n(i,j) + k*m_numpoints] =
240 * \f$ \phi^b_{ij}(z_k) = \left \{ \begin{array}{lll}
241 * \phi^a_j(z_k) & i = 0, & 0\leq j < P \\
242 * \\
243 * \left ( \frac{1-z_k}{2}\right )^{i} & 1 \leq i < P,& j = 0 \\
244 * \\
245 * \left ( \frac{1-z_k}{2}\right )^{i} \left ( \frac{1+z_k}{2}\right )
246 * P^{2i-1,1}_{j-1}(z_k) & 1 \leq i < P,\ & 1\leq j < P-i\ \\
247 * \end{array} \right . , \f$
248 *
249 * where \f$ n(i,j) \f$ is a consecutive ordering of the
250 * triangular indices \f$ 0 \leq i, i+j < P \f$ where \a j
251 * runs fastest.
252 *
253 *
254 * \a eModified_C:
255 *
256 * m_bdata[n(i,j,k) + l*m_numpoints] =
257 * \f$ \phi^c_{ij,k}(z_l) = \phi^b_{i+j,k}(z_l) =
258 * \left \{ \begin{array}{llll}
259 * \phi^b_{j,k}(z_l) & i = 0, & 0\leq j < P & 0\leq k < P-j\\
260 * \\
261 * \left ( \frac{1-z_l}{2}\right )^{i+j} & 1\leq i < P,\
262 * & 0\leq j < P-i,\ & k = 0 \\
263 * \\
264 * \left ( \frac{1-z_l}{2}\right )^{i+j}
265 * \left ( \frac{1+z_l}{2}\right )
266 * P^{2i+2j-1,1}_{k-1}(z_k) & 1\leq i < P,& 0\leq j < P-i&
267 * 1\leq k < P-i-j \\
268 * \\
269 * \end{array} \right . , \f$
270 *
271 * where \f$ n(i,j,k) \f$ is a consecutive ordering of the
272 * triangular indices \f$ 0 \leq i, i+j, i+j+k < P \f$ where \a k
273 * runs fastest, then \a j and finally \a i.
274 *
275 */
277{
278 size_t i, p, q;
279 NekDouble scal;
280 Array<OneD, NekDouble> modeSharedArray;
281 NekDouble *mode;
284
285 m_points->GetZW(z, w);
286
287 const NekDouble *D = &(m_points->GetD()->GetPtr())[0];
288 size_t numModes = GetNumModes();
289 size_t numPoints = GetNumPoints();
290
291 switch (GetBasisType())
292 {
293
294 /** \brief Orthogonal basis A
295
296 \f$\tilde \psi_p^a (\eta_1) = L_p(\eta_1) = P_p^{0,0}(\eta_1)\f$
297
298 */
299 case eOrtho_A:
300 case eLegendre:
301 {
302 mode = m_bdata.data();
303
304 for (p = 0; p < numModes; ++p, mode += numPoints)
305 {
306 Polylib::jacobfd(numPoints, z.data(), mode, nullptr, p, 0.0,
307 0.0);
308 // normalise
309 scal = sqrt(0.5 * (2.0 * p + 1.0));
310 for (i = 0; i < numPoints; ++i)
311 {
312 mode[i] *= scal;
313 }
314 }
315
316 // Define derivative basis
317 Blas::Dgemm('n', 'n', numPoints, numModes, numPoints, 1.0, D,
318 numPoints, m_bdata.data(), numPoints, 0.0,
319 m_dbdata.data(), numPoints);
320 } // end scope
321 break;
322
323 /** \brief Orthogonal basis B
324
325 \f$\tilde \psi_{pq}^b(\eta_2) = \left ( {1 - \eta_2} \over 2
326 \right)^p
327 P_q^{2p+1,0}(\eta_2)\f$ \\
328
329 */
330
331 // This is tilde psi_pq in Spencer's book, page 105
332 // The 3-dimensional array is laid out in memory such that
333 // 1) Eta_y values are the changing the fastest, then q and p.
334 // 2) q index increases by the stride of numPoints.
335 case eOrtho_B:
336 {
337 mode = m_bdata.data();
338
339 for (size_t p = 0; p < numModes; ++p)
340 {
341 for (size_t q = 0; q < numModes - p; ++q, mode += numPoints)
342 {
343 Polylib::jacobfd(numPoints, z.data(), mode, nullptr, q,
344 2 * p + 1.0, 0.0);
345 for (size_t j = 0; j < numPoints; ++j)
346 {
347 mode[j] *=
348 sqrt(p + q + 1.0) * pow(0.5 * (1.0 - z[j]), p);
349 }
350 }
351 }
352
353 // Define derivative basis
354 Blas::Dgemm('n', 'n', numPoints, numModes * (numModes + 1) / 2,
355 numPoints, 1.0, D, numPoints, m_bdata.data(), numPoints,
356 0.0, m_dbdata.data(), numPoints);
357 }
358 break;
359
360 /** \brief Orthogonal basis C
361
362 \f$\tilde \psi_{pqr}^c = \left ( {1 - \eta_3} \over 2 \right)^{p+q}
363 P_r^{2p+2q+2, 0}(\eta_3)\f$ \ \
364
365 */
366
367 // This is tilde psi_pqr in Spencer's book, page 105
368 // The 4-dimensional array is laid out in memory such that
369 // 1) Eta_z values are the changing the fastest, then r, q, and finally
370 // p. 2) r index increases by the stride of numPoints.
371 case eOrtho_C:
372 {
373 size_t P = numModes - 1, Q = numModes - 1, R = numModes - 1;
374 mode = m_bdata.data();
375
376 for (size_t p = 0; p <= P; ++p)
377 {
378 for (size_t q = 0; q <= Q - p; ++q)
379 {
380 for (size_t r = 0; r <= R - p - q; ++r, mode += numPoints)
381 {
382 Polylib::jacobfd(numPoints, z.data(), mode, nullptr, r,
383 2 * p + 2 * q + 2.0, 0.0);
384 for (size_t k = 0; k < numPoints; ++k)
385 {
386 // Note factor of 0.5 is part of normalisation
387 mode[k] *= pow(0.5 * (1.0 - z[k]), p + q);
388
389 // finish normalisation
390 mode[k] *= sqrt(r + p + q + 1.5);
391 }
392 }
393 }
394 }
395
396 // Define derivative basis
397 Blas::Dgemm('n', 'n', numPoints,
398 numModes * (numModes + 1) * (numModes + 2) / 6,
399 numPoints, 1.0, D, numPoints, m_bdata.data(), numPoints,
400 0.0, m_dbdata.data(), numPoints);
401 }
402 break;
403
404 /** \brief Orthogonal basis C for Pyramid expansion
405 (which is richer than tets)
406
407 \f$\tilde \psi_{pqr}^c = \left ( {1 - \eta_3} \over
408 2\right)^{pq} P_r^{2pq+2, 0}(\eta_3)\f$ \f$ \mbox{where
409 }pq = max(p+q,0) \f$
410
411 This orthogonal expansion has modes that are
412 always in the Cartesian space, however the
413 equivalent ModifiedPyr_C has vertex modes that do
414 not lie in this space. If one chooses \f$pq =
415 max(p+q-1,0)\f$ then the expansion will space the
416 same space as the vertices but the order of the
417 expanion in 'r' is reduced by one.
418
419 1) Eta_z values are the changing the fastest, then
420 r, q, and finally p. 2) r index increases by the
421 stride of numPoints.
422 */
423 case eOrthoPyr_C:
424 {
425 size_t P = numModes - 1, Q = numModes - 1, R = numModes - 1;
426 mode = m_bdata.data();
427
428 for (size_t p = 0; p <= P; ++p)
429 {
430 for (size_t q = 0; q <= Q; ++q)
431 {
432 for (size_t r = 0; r <= R - std::max(p, q);
433 ++r, mode += numPoints)
434 {
435 // this offset allows for orthogonal
436 // expansion to span linear FE space
437 // of modified basis but means that
438 // the cartesian polynomial space
439 // spanned by the expansion is one
440 // order lower.
441 // size_t pq = max(p + q -1,0);
442 size_t pq = std::max(p + q, size_t(0));
443
444 Polylib::jacobfd(numPoints, z.data(), mode, nullptr, r,
445 2 * pq + 2.0, 0.0);
446 for (size_t k = 0; k < numPoints; ++k)
447 {
448 // Note factor of 0.5 is part of normalisation
449 mode[k] *= pow(0.5 * (1.0 - z[k]), pq);
450
451 // finish normalisation
452 mode[k] *= sqrt(r + pq + 1.5);
453 }
454 }
455 }
456 }
457
458 // Define derivative basis
459 Blas::Dgemm('n', 'n', numPoints,
460 numModes * (numModes + 1) * (numModes + 2) / 6,
461 numPoints, 1.0, D, numPoints, m_bdata.data(), numPoints,
462 0.0, m_dbdata.data(), numPoints);
463 }
464 break;
465
466 case eModified_A:
467 {
468 // Note the following packing deviates from the
469 // definition in the Book by Karniadakis in that we
470 // put the vertex degrees of freedom at the lower
471 // index range to follow a more hierarchic structure.
472
473 for (i = 0; i < numPoints; ++i)
474 {
475 m_bdata[i] = 0.5 * (1 - z[i]);
476 m_bdata[numPoints + i] = 0.5 * (1 + z[i]);
477 }
478
479 mode = m_bdata.data() + 2 * numPoints;
480
481 for (p = 2; p < numModes; ++p, mode += numPoints)
482 {
483 Polylib::jacobfd(numPoints, z.data(), mode, nullptr, p - 2, 1.0,
484 1.0);
485
486 for (i = 0; i < numPoints; ++i)
487 {
488 mode[i] *= m_bdata[i] * m_bdata[numPoints + i];
489 }
490 }
491
492 // Define derivative basis
493 Blas::Dgemm('n', 'n', numPoints, numModes, numPoints, 1.0, D,
494 numPoints, m_bdata.data(), numPoints, 0.0,
495 m_dbdata.data(), numPoints);
496 }
497 break;
498
499 case eModified_B:
500 {
501 // Note the following packing deviates from the
502 // definition in the Book by Karniadakis in two
503 // ways. 1) We put the vertex degrees of freedom
504 // at the lower index range to follow a more
505 // hierarchic structure. 2) We do not duplicate
506 // the singular vertex definition so that only a
507 // triangular number (i.e. (modes)*(modes+1)/2) of
508 // modes are required consistent with the
509 // orthogonal basis.
510
511 // In the current structure the q index runs
512 // faster than the p index so that the matrix has
513 // a more compact structure
514
515 const NekDouble *one_m_z_pow, *one_p_z;
516
517 // bdata should be of size order*(order+1)/2*zorder
518
519 // first fow
520 for (i = 0; i < numPoints; ++i)
521 {
522 m_bdata[0 * numPoints + i] = 0.5 * (1 - z[i]);
523 m_bdata[1 * numPoints + i] = 0.5 * (1 + z[i]);
524 }
525
526 mode = m_bdata.data() + 2 * numPoints;
527
528 for (q = 2; q < numModes; ++q, mode += numPoints)
529 {
530 Polylib::jacobfd(numPoints, z.data(), mode, nullptr, q - 2, 1.0,
531 1.0);
532
533 for (i = 0; i < numPoints; ++i)
534 {
535 mode[i] *= m_bdata[i] * m_bdata[numPoints + i];
536 }
537 }
538
539 // second row
540 for (i = 0; i < numPoints; ++i)
541 {
542 mode[i] = 0.5 * (1 - z[i]);
543 }
544
545 mode += numPoints;
546
547 for (q = 2; q < numModes; ++q, mode += numPoints)
548 {
549 Polylib::jacobfd(numPoints, z.data(), mode, nullptr, q - 2, 1.0,
550 1.0);
551
552 for (i = 0; i < numPoints; ++i)
553 {
554 mode[i] *= m_bdata[i] * m_bdata[numPoints + i];
555 }
556 }
557
558 // third and higher rows
559 one_m_z_pow = m_bdata.data();
560 one_p_z = m_bdata.data() + numPoints;
561
562 for (p = 2; p < numModes; ++p)
563 {
564 for (i = 0; i < numPoints; ++i)
565 {
566 mode[i] = m_bdata[i] * one_m_z_pow[i];
567 }
568
569 one_m_z_pow = mode;
570 mode += numPoints;
571
572 for (q = 1; q < numModes - p; ++q, mode += numPoints)
573 {
574 Polylib::jacobfd(numPoints, z.data(), mode, nullptr, q - 1,
575 2 * p - 1, 1.0);
576
577 for (i = 0; i < numPoints; ++i)
578 {
579 mode[i] *= one_m_z_pow[i] * one_p_z[i];
580 }
581 }
582 }
583
584 Blas::Dgemm('n', 'n', numPoints, numModes * (numModes + 1) / 2,
585 numPoints, 1.0, D, numPoints, m_bdata.data(), numPoints,
586 0.0, m_dbdata.data(), numPoints);
587 }
588 break;
589
590 case eModified_C:
591 {
592 // Note the following packing deviates from the
593 // definition in the Book by Karniadakis &
594 // Sherwin in two ways. 1) We put the vertex
595 // degrees of freedom at the lower index range to
596 // follow a more hierarchic structure. 2) We do
597 // not duplicate the singular vertex definition
598 // (or the duplicated face information in the book
599 // ) so that only a tetrahedral number
600 // (i.e. (modes)*(modes+1)*(modes+2)/6) of modes
601 // are required consistent with the orthogonal
602 // basis.
603
604 // In the current structure the r index runs
605 // fastest rollowed by q and than the p index so
606 // that the matrix has a more compact structure
607
608 // Note that eModified_C is a re-organisation/
609 // duplication of eModified_B so will get a
610 // temporary Modified_B expansion and copy the
611 // correct components.
612
613 // Generate Modified_B basis;
616 BasisSharedPtr ModB = BasisManager()[ModBKey];
617
618 Array<OneD, const NekDouble> ModB_data = ModB->GetBdata();
619
620 // Copy Modified_B basis into first
621 // (numModes*(numModes+1)/2)*numPoints entires of
622 // bdata. This fills in the complete (r,p) face.
623
624 // Set up \phi^c_{p,q,r} = \phi^b_{p+q,r}
625
626 size_t N;
627 size_t B_offset = 0;
628 size_t offset = 0;
629 for (p = 0; p < numModes; ++p)
630 {
631 N = numPoints * (numModes - p) * (numModes - p + 1) / 2;
632 Vmath::Vcopy(N, &ModB_data[0] + B_offset, 1,
633 &m_bdata[0] + offset, 1);
634 B_offset += numPoints * (numModes - p);
635 offset += N;
636 }
637
638 // set up derivative of basis.
639 Blas::Dgemm('n', 'n', numPoints,
640 numModes * (numModes + 1) * (numModes + 2) / 6,
641 numPoints, 1.0, D, numPoints, m_bdata.data(), numPoints,
642 0.0, m_dbdata.data(), numPoints);
643 }
644 break;
645
646 case eModifiedPyr_C:
647 {
648 // Note the following packing deviates from the
649 // definition in the Book by Karniadakis &
650 // Sherwin in two ways. 1) We put the vertex
651 // degrees of freedom at the lower index range to
652 // follow a more hierarchic structure. 2) We do
653 // not duplicate the singular vertex definition
654 // so that only a pyramidic number
655 // (i.e. (modes)*(modes+1)*(2*modes+1)/6) of modes
656 // are required consistent with the orthogonal
657 // basis.
658
659 // In the current structure the r index runs
660 // fastest rollowed by q and than the p index so
661 // that the matrix has a more compact structure
662
663 // Generate Modified_B basis;
666 BasisSharedPtr ModB = BasisManager()[ModBKey];
667
668 Array<OneD, const NekDouble> ModB_data = ModB->GetBdata();
669
670 // Copy Modified_B basis into first
671 // (numModes*(numModes+1)/2)*numPoints entires of
672 // bdata.
673
674 size_t N;
675 size_t B_offset = 0;
676 size_t offset = 0;
677
678 // Vertex 0,3,4, edges 3,4,7, face 4
679 N = numPoints * (numModes) * (numModes + 1) / 2;
680 Vmath::Vcopy(N, &ModB_data[0], 1, &m_bdata[0], 1);
681 offset += N;
682
683 B_offset += numPoints * (numModes);
684 // Vertex 1 edges 5
685 N = numPoints * (numModes - 1);
686 Vmath::Vcopy(N, &ModB_data[0] + B_offset, 1, &m_bdata[0] + offset,
687 1);
688 offset += N;
689
690 // Vertex 2 edges 1,6, face 2
691 N = numPoints * (numModes - 1) * (numModes) / 2;
692 Vmath::Vcopy(N, &ModB_data[0] + B_offset, 1, &m_bdata[0] + offset,
693 1);
694 offset += N;
695
696 B_offset += numPoints * (numModes - 1);
697
698 NekDouble *one_m_z_pow, *one_p_z;
699
700 mode = m_bdata.data() + offset;
701
702 for (p = 2; p < numModes; ++p)
703 {
704 // edges 0 2, faces 1 3
705 N = numPoints * (numModes - p);
706 Vmath::Vcopy(N, &ModB_data[0] + B_offset, 1, mode, 1);
707 mode += N;
708 Vmath::Vcopy(N, &ModB_data[0] + B_offset, 1, mode, 1);
709 mode += N;
710 B_offset += N;
711
712 one_p_z = m_bdata.data() + numPoints;
713
714 for (q = 2; q < numModes; ++q)
715 {
716 // face 0
717 for (i = 0; i < numPoints; ++i)
718 {
719 // [(1-z)/2]^{p+q-2} Note in book it
720 // seems to suggest p+q-1 but that
721 // does not seem to give complete
722 // polynomial space for pyramids
723 mode[i] = pow(m_bdata[i], p + q - 2);
724 }
725
726 one_m_z_pow = mode;
727 mode += numPoints;
728
729 // interior
730 for (size_t r = 1; r < numModes - std::max(p, q); ++r)
731 {
732 Polylib::jacobfd(numPoints, z.data(), mode, nullptr,
733 r - 1, 2 * p + 2 * q - 3, 1.0);
734
735 for (i = 0; i < numPoints; ++i)
736 {
737 mode[i] *= one_m_z_pow[i] * one_p_z[i];
738 }
739 mode += numPoints;
740 }
741 }
742 }
743
744 // set up derivative of basis.
745 Blas::Dgemm('n', 'n', numPoints,
746 numModes * (numModes + 1) * (2 * numModes + 1) / 6,
747 numPoints, 1.0, D, numPoints, m_bdata.data(), numPoints,
748 0.0, m_dbdata.data(), numPoints);
749 } // end scope
750 break;
751
752 case eGLL_Lagrange:
753 {
754 mode = m_bdata.data();
755 std::shared_ptr<Points<NekDouble>> m_points =
757 const Array<OneD, const NekDouble> &zp(m_points->GetZ());
758
759 for (p = 0; p < numModes; ++p, mode += numPoints)
760 {
761 for (q = 0; q < numPoints; ++q)
762 {
763 mode[q] =
764 Polylib::hglj(p, z[q], zp.data(), numModes, 0.0, 0.0);
765 }
766 }
767
768 // Define derivative basis
769 Blas::Dgemm('n', 'n', numPoints, numModes, numPoints, 1.0, D,
770 numPoints, m_bdata.data(), numPoints, 0.0,
771 m_dbdata.data(), numPoints);
772
773 } // end scope
774 break;
775
776 case eGauss_Lagrange:
777 {
778 mode = m_bdata.data();
779 std::shared_ptr<Points<NekDouble>> m_points =
781 const Array<OneD, const NekDouble> &zp(m_points->GetZ());
782
783 for (p = 0; p < numModes; ++p, mode += numPoints)
784 {
785 for (q = 0; q < numPoints; ++q)
786 {
787 mode[q] =
788 Polylib::hgj(p, z[q], zp.data(), numModes, 0.0, 0.0);
789 }
790 }
791
792 // Define derivative basis
793 Blas::Dgemm('n', 'n', numPoints, numModes, numPoints, 1.0, D,
794 numPoints, m_bdata.data(), numPoints, 0.0,
795 m_dbdata.data(), numPoints);
796
797 } // end scope
798 break;
799
800 case eFourier:
801 {
802 ASSERTL0(numModes % 2 == 0,
803 "Fourier modes should be a factor of 2");
804
805 for (i = 0; i < numPoints; ++i)
806 {
807 m_bdata[i] = 1.0;
808 m_bdata[numPoints + i] = 0.0;
809
810 m_dbdata[i] = m_dbdata[numPoints + i] = 0.0;
811 }
812
813 for (p = 1; p < numModes / 2; ++p)
814 {
815 for (i = 0; i < numPoints; ++i)
816 {
817 m_bdata[2 * p * numPoints + i] = cos(p * M_PI * (z[i] + 1));
818 m_bdata[(2 * p + 1) * numPoints + i] =
819 -sin(p * M_PI * (z[i] + 1));
820
821 m_dbdata[2 * p * numPoints + i] =
822 -p * M_PI * sin(p * M_PI * (z[i] + 1));
823 m_dbdata[(2 * p + 1) * numPoints + i] =
824 -p * M_PI * cos(p * M_PI * (z[i] + 1));
825 }
826 }
827 } // end scope
828 break;
829
830 // Fourier Single Mode (1st mode)
832 {
833 for (i = 0; i < numPoints; ++i)
834 {
835 m_bdata[i] = cos(M_PI * (z[i] + 1));
836 m_bdata[numPoints + i] = -sin(M_PI * (z[i] + 1));
837
838 m_dbdata[i] = -M_PI * sin(M_PI * (z[i] + 1));
839 m_dbdata[numPoints + i] = -M_PI * cos(M_PI * (z[i] + 1));
840 }
841
842 for (p = 1; p < numModes / 2; ++p)
843 {
844 for (i = 0; i < numPoints; ++i)
845 {
846 m_bdata[2 * p * numPoints + i] = 0.;
847 m_bdata[(2 * p + 1) * numPoints + i] = 0.;
848
849 m_dbdata[2 * p * numPoints + i] = 0.;
850 m_dbdata[(2 * p + 1) * numPoints + i] = 0.;
851 }
852 }
853 } // end scope
854 break;
855
856 // Fourier Real Half Mode
858 {
859 m_bdata[0] = cos(M_PI * z[0]);
860 m_dbdata[0] = -M_PI * sin(M_PI * z[0]);
861 } // end scope
862 break;
863
864 // Fourier Imaginary Half Mode
866 {
867 m_bdata[0] = -sin(M_PI * z[0]);
868 m_dbdata[0] = -M_PI * cos(M_PI * z[0]);
869 } // end scope
870 break;
871
872 case eChebyshev:
873 {
874 mode = m_bdata.data();
875
876 for (p = 0, scal = 1; p < numModes; ++p, mode += numPoints)
877 {
878 Polylib::jacobfd(numPoints, z.data(), mode, nullptr, p, -0.5,
879 -0.5);
880
881 for (i = 0; i < numPoints; ++i)
882 {
883 mode[i] *= scal;
884 }
885
886 scal *= 4 * (p + 1) * (p + 1) / (2 * p + 2) / (2 * p + 1);
887 }
888
889 // Define derivative basis
890 Blas::Dgemm('n', 'n', numPoints, numModes, numPoints, 1.0, D,
891 numPoints, m_bdata.data(), numPoints, 0.0,
892 m_dbdata.data(), numPoints);
893 } // end scope
894 break;
895
896 case eMonomial:
897 {
898 mode = m_bdata.data();
899
900 for (size_t p = 0; p < numModes; ++p, mode += numPoints)
901 {
902 for (size_t i = 0; i < numPoints; ++i)
903 {
904 mode[i] = pow(z[i], p);
905 }
906 }
907
908 // Define derivative basis
909 Blas::Dgemm('n', 'n', numPoints, numModes, numPoints, 1.0, D,
910 numPoints, m_bdata.data(), numPoints, 0.0,
911 m_dbdata.data(), numPoints);
912 } // end scope
913 break;
914
915 default:
916 NEKERROR(ErrorUtil::efatal, "Basis Type not known or "
917 "not implemented at this time.");
918 }
919}
920
921/** \brief Determine if polynomial basis can be exactly integrated
922 * with itself
923 */
925{
926 bool returnval = false;
927
928 switch (GetPointsType())
929 {
934 case eGaussKronrodLegendre:
937 returnval = (GetNumPoints() >= GetNumModes());
938 break;
939
940 default:
941 break;
942 }
943
944 return returnval;
945}
946
947/** \brief Determine if basis has collocation property,
948 * i.e. GLL_Lagrange with Lobatto integration of appropriate order,
949 * Gauss_Lagrange with Gauss integration of appropriate order.
950 */
960
961// BasisKey compared to BasisKey
962bool operator==(const BasisKey &x, const BasisKey &y)
963{
964 return (x.GetPointsKey() == y.GetPointsKey() &&
965 x.m_basistype == y.m_basistype &&
966 x.GetNumModes() == y.GetNumModes());
967}
968
969// BasisKey* compared to BasisKey
970bool operator==(const BasisKey *x, const BasisKey &y)
971{
972 return (*x == y);
973}
974
975// \brief BasisKey compared to BasisKey*
976bool operator==(const BasisKey &x, const BasisKey *y)
977{
978 return (x == *y);
979}
980
981// \brief BasisKey compared to BasisKey
982bool operator!=(const BasisKey &x, const BasisKey &y)
983{
984 return (!(x == y));
985}
986
987// BasisKey* compared to BasisKey
988bool operator!=(const BasisKey *x, const BasisKey &y)
989{
990 return (!(*x == y));
991}
992
993// BasisKey compared to BasisKey*
994bool operator!=(const BasisKey &x, const BasisKey *y)
995{
996 return (!(x == *y));
997}
998
999} // namespace Nektar::LibUtilities
#define ASSERTL0(condition, msg)
#define NEKERROR(type, msg)
Assert Level 0 – Fundamental assert which is used whether in FULLDEBUG, DEBUG or OPT compilation mode...
int GetNumModes() const
Return order of basis from the basis specification.
Definition Basis.h:207
Array< OneD, NekDouble > m_dbdata
Derivative Basis definition.
Definition Basis.h:324
Basis()=delete
Private default constructor.
static std::shared_ptr< Basis > Create(const BasisKey &bkey)
Returns a new instance of a Basis with given BasisKey.
PointsSharedPtr m_points
Set of points.
Definition Basis.h:322
int GetTotNumPoints() const
Return total number of points from the basis specification.
Definition Basis.h:225
BasisKey m_basisKey
Basis specification.
Definition Basis.h:321
BasisType GetBasisType() const
Return the type of expansion basis.
Definition Basis.h:231
int GetNumPoints() const
Return the number of points from the basis specification.
Definition Basis.h:219
std::shared_ptr< NekMatrix< NekDouble > > CalculateInterpMatrix(const BasisKey &tbasis0)
Calculate the interpolation Matrix for coefficient from one base (m_basisKey) to another (tbasis0) In...
NekManager< BasisKey, NekMatrix< NekDouble >, BasisKey::opLess > m_InterpManager
Definition Basis.h:326
void GenBasis()
Generate appropriate basis and their derivatives.
Array< OneD, NekDouble > m_bdata
Basis definition.
Definition Basis.h:323
static bool initBasisManager
Definition Basis.h:329
Describes the specification for a Basis.
Definition Basis.h:45
int GetNumPoints() const
Return points order at which basis is defined.
Definition Basis.h:120
BasisType GetBasisType() const
Return type of expansion basis.
Definition Basis.h:131
PointsKey GetPointsKey() const
Return distribution of points.
Definition Basis.h:137
size_t m_nummodes
Expansion order.
Definition Basis.h:185
int GetNumModes() const
Returns the order of the basis.
Definition Basis.h:74
PointsType GetPointsType() const
Return type of quadrature.
Definition Basis.h:143
BasisType m_basistype
Expansion type.
Definition Basis.h:186
bool Collocation() const
Determine if basis has collocation properties.
bool ExactIprodInt() const
Determine if basis has exact integration for inner product.
bool RegisterGlobalCreator(const CreateFuncType &createFunc)
Register the Global Create Function. The return value is just to facilitate calling statically.
Defines a specification for a set of points.
Definition Points.h:50
General purpose memory allocation routines with the ability to allocate from thread specific memory p...
static void Dgemm(const char &transa, const char &transb, const int &m, const int &n, const int &k, const double &alpha, const double *a, const int &lda, const double *b, const int &ldb, const double &beta, double *c, const int &ldc)
BLAS level 3: Matrix-matrix multiply C = A x B where op(A)[m x k], op(B)[k x n], C[m x n] DGEMM perfo...
Definition Blas.hpp:324
BasisManagerT & BasisManager(void)
bool operator==(const BasisKey &x, const BasisKey &y)
std::shared_ptr< Basis > BasisSharedPtr
const char *const BasisTypeMap[]
PointsManagerT & PointsManager(void)
bool operator<(const BasisKey &lhs, const BasisKey &rhs)
bool operator>(const BasisKey &lhs, const BasisKey &rhs)
bool operator!=(const BasisKey &x, const BasisKey &y)
std::ostream & operator<<(std::ostream &os, const BasisKey &rhs)
@ eGaussRadauMLegendre
1D Gauss-Radau-Legendre quadrature points, pinned at x=-1
Definition PointsType.h:47
@ eGaussRadauKronrodMLegendre
1D Gauss-Radau-Kronrod-Legendre quadrature points, pinned at x=-1
Definition PointsType.h:67
@ eGaussLobattoLegendre
1D Gauss-Lobatto-Legendre quadrature points
Definition PointsType.h:51
@ eGaussLobattoKronrodLegendre
1D Lobatto Kronrod quadrature points
Definition PointsType.h:72
@ eGaussGaussLegendre
1D Gauss-Gauss-Legendre quadrature points
Definition PointsType.h:46
@ eGaussRadauPLegendre
1D Gauss-Radau-Legendre quadrature points, pinned at x=1
Definition PointsType.h:49
@ eModified_B
Principle Modified Functions .
Definition BasisType.h:49
@ eGauss_Lagrange
Lagrange Polynomials using the Gauss points.
Definition BasisType.h:57
@ 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
@ eFourierSingleMode
Fourier ModifiedExpansion with just the first mode .
Definition BasisType.h:65
@ eOrtho_C
Principle Orthogonal Functions .
Definition BasisType.h:46
@ eModifiedPyr_C
Principle Modified Functions.
Definition BasisType.h:53
@ eOrtho_B
Principle Orthogonal Functions .
Definition BasisType.h:44
@ eModified_A
Principle Modified Functions .
Definition BasisType.h:48
@ eFourierHalfModeIm
Fourier Modified expansions with just the imaginary part of the first mode .
Definition BasisType.h:69
@ eFourierHalfModeRe
Fourier Modified expansions with just the real part of the first mode .
Definition BasisType.h:67
@ eOrthoPyr_C
Principle Orthogonal Functions .
Definition BasisType.h:51
@ eFourier
Fourier Expansion .
Definition BasisType.h:55
double hgj(const int i, const double z, const double *zgj, const int np, const double alpha, const double beta)
Compute the value of the i th Lagrangian interpolant through the np Gauss-Jacobi points zgj at the ar...
Definition Polylib.cpp:914
double hglj(const int i, const double z, const double *zglj, const int np, const double alpha, const double beta)
Compute the value of the i th Lagrangian interpolant through the np Gauss-Lobatto-Jacobi points zgrj ...
Definition Polylib.cpp:1025
void jacobfd(const int np, const double *z, double *poly_in, double *polyd, const int n, const double alpha, const double beta)
Routine to calculate Jacobi polynomials, , and their first derivative, .
Definition Polylib.cpp:1248
void Vcopy(int n, const T *x, const int incx, T *y, const int incy)
Definition Vmath.hpp:825
scalarT< T > sqrt(scalarT< T > in)
Definition scalar.hpp:290
bool operator()(const BasisKey &lhs, const BasisKey &rhs) const