Qspectral_elements.h
Go to the documentation of this file.
1 // LIC// ====================================================================
2 // LIC// This file forms part of oomph-lib, the object-oriented,
3 // LIC// multi-physics finite-element library, available
4 // LIC// at http://www.oomph-lib.org.
5 // LIC//
6 // LIC// Copyright (C) 2006-2022 Matthias Heil and Andrew Hazel
7 // LIC//
8 // LIC// This library is free software; you can redistribute it and/or
9 // LIC// modify it under the terms of the GNU Lesser General Public
10 // LIC// License as published by the Free Software Foundation; either
11 // LIC// version 2.1 of the License, or (at your option) any later version.
12 // LIC//
13 // LIC// This library is distributed in the hope that it will be useful,
14 // LIC// but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // LIC// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 // LIC// Lesser General Public License for more details.
17 // LIC//
18 // LIC// You should have received a copy of the GNU Lesser General Public
19 // LIC// License along with this library; if not, write to the Free Software
20 // LIC// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 // LIC// 02110-1301 USA.
22 // LIC//
23 // LIC// The authors may be contacted at oomph-lib@maths.man.ac.uk.
24 // LIC//
25 // LIC//====================================================================
26 // Header functions for classes that define Qelements
27 
28 // Include guards to prevent multiple inclusion of the header
29 #ifndef OOMPH_QSPECTRAL_ELEMENT_HEADER
30 #define OOMPH_QSPECTRAL_ELEMENT_HEADER
31 
32 // Config header generated by autoconfig
33 #ifdef HAVE_CONFIG_H
34 #include <oomph-lib-config.h>
35 #endif
36 
37 // oomph-lib headers
38 #include "Qelements.h"
39 
40 namespace oomph
41 {
42  //=====================================================================
44  //=====================================================================
46  {
47  public:
48  static std::map<unsigned, Vector<double>> z;
49 
51  static inline void calculate_nodal_positions(const unsigned& order)
52  {
53  // If we haven't already calculated these
54  if (z.find(order) == z.end())
55  {
56  Orthpoly::gll_nodes(order, z[order]);
57  }
58  }
59 
60  static inline double nodal_position(const unsigned& order,
61  const unsigned& n)
62  {
63  return z[order][n];
64  }
65 
67  OneDLegendreShapeParam(const unsigned& order, const double& s)
68  : Shape(order)
69  {
70  using namespace Orthpoly;
71 
72  unsigned p = order - 1;
73  // Now populate the shape function
74  for (unsigned i = 0; i < order; i++)
75  {
76  // If we're at one of the nodes, the value must be 1.0
77  if (std::fabs(s - z[order][i]) < Orthpoly::eps)
78  {
79  (*this)[i] = 1.0;
80  }
81  // Otherwise use the lagrangian interpolant
82  else
83  {
84  (*this)[i] =
85  (1.0 - s * s) * dlegendre(p, s) /
86  (p * (p + 1) * legendre(p, z[order][i]) * (z[order][i] - s));
87  }
88  }
89  }
90  };
91 
92 
94  {
95  public:
96  // Constructor
97  OneDLegendreDShapeParam(const unsigned& order, const double& s)
98  : Shape(order)
99  {
100  unsigned p = order - 1;
102 
103  bool root = false;
104  for (unsigned i = 0; i < order; i++)
105  {
106  unsigned rootnum = 0;
107  for (unsigned j = 0; j < order; j++)
108  { // Loop over roots to check if
109  if (std::fabs(s - z[j]) < 10.0 * Orthpoly::eps)
110  { // s happens to be a root.
111  root = true;
112  break;
113  }
114  rootnum += 1;
115  }
116  if (root == true)
117  {
118  if (i == rootnum && i == 0)
119  {
120  (*this)[i] = -(1.0 + p) * p / 4.0;
121  }
122  else if (i == rootnum && i == p)
123  {
124  (*this)[i] = (1.0 + p) * p / 4.0;
125  }
126  else if (i == rootnum)
127  {
128  (*this)[i] = 0.0;
129  }
130  else
131  {
132  (*this)[i] = Orthpoly::legendre(p, z[rootnum]) /
133  Orthpoly::legendre(p, z[i]) / (z[rootnum] - z[i]);
134  }
135  }
136  else
137  {
138  (*this)[i] =
139  ((1 + s * (s - 2 * z[i])) / (s - z[i]) * Orthpoly::dlegendre(p, s) -
140  (1 - s * s) * Orthpoly::ddlegendre(p, s)) /
141  p / (p + 1.0) / Orthpoly::legendre(p, z[i]) / (s - z[i]);
142  }
143  root = false;
144  }
145  }
146  };
147 
148 
149  //========================================================================
150  // A Base class for Spectral elements
151  //========================================================================
152  class SpectralElement : public virtual FiniteElement
153  {
154  protected:
157 
160 
163 
166 
167  public:
168  SpectralElement() : FiniteElement(), Spectral_data_pt(0) {}
169 
171  {
172  if (Spectral_data_pt != 0)
173  {
174  delete Spectral_data_pt;
175  Spectral_data_pt = 0;
176  }
177  }
178 
179 
182  Data* spectral_data_pt(const unsigned& i) const
183  {
184  return (*Spectral_data_pt)[i];
185  }
186 
195  virtual void describe_local_dofs(std::ostream& out,
196  const std::string& current_string) const
197  {
198  // Do the standard finite element stuff
199  FiniteElement::describe_local_dofs(out, current_string);
200  // Now get the number of spectral data.
201  unsigned n_spectral = nspectral();
202 
203  // Now loop over the nodes again and assign local equation numbers
204  for (unsigned n = 0; n < n_spectral; n++)
205  {
206  // Can we upcast to a node
207  Node* cast_node_pt = dynamic_cast<Node*>(spectral_data_pt(n));
208  if (cast_node_pt)
209  {
210  std::stringstream conversion;
211  conversion << " of Node " << n << current_string;
212  std::string in(conversion.str());
213  cast_node_pt->describe_dofs(out, in);
214  }
215  // Otherwise it is data.
216  else
217  {
218  Data* data_pt = spectral_data_pt(n);
219  std::stringstream conversion;
220  conversion << " of Data " << n << current_string;
221  std::string in(conversion.str());
222  data_pt->describe_dofs(out, in);
223  }
224  }
225  }
226 
230  const bool& store_local_dof_pt)
231  {
232  // Do the standard finite element stuff
234 
235  // Now need to loop over the spectral data
236  unsigned n_spectral = nspectral();
237  if (n_spectral > 0)
238  {
239  // Find the number of local equations assigned so far
240  unsigned local_eqn_number = ndof();
241 
242  // Find number of values stored at the first node
243  unsigned max_n_value = spectral_data_pt(0)->nvalue();
244  // Loop over the other nodes and find the maximum number of values
245  // stored
246  for (unsigned n = 1; n < n_spectral; n++)
247  {
248  unsigned n_value = spectral_data_pt(n)->nvalue();
249  if (n_value > max_n_value)
250  {
251  max_n_value = n_value;
252  }
253  }
254 
255  // Resize the storage for the nodal local equation numbers
256  // initially all local equations are unclassified
257  Spectral_local_eqn.resize(
258  n_spectral, max_n_value, Data::Is_unclassified);
259 
260  // Construct a set of pointers to the nodes
261  std::set<Data*> set_of_node_pt;
262  unsigned n_node = nnode();
263  for (unsigned n = 0; n < n_node; n++)
264  {
265  set_of_node_pt.insert(node_pt(n));
266  }
267 
268  // A local queue to store the global equation numbers
269  std::deque<unsigned long> global_eqn_number_queue;
270 
271  // Now loop over the nodes again and assign local equation numbers
272  for (unsigned n = 0; n < n_spectral; n++)
273  {
274  // Need to find whether the data is, in fact a node
275  //(and hence already assigned)
276 
277  // Can we upcast to a node
278  Node* cast_node_pt = dynamic_cast<Node*>(spectral_data_pt(n));
279  if ((cast_node_pt) &&
280  (set_of_node_pt.find(cast_node_pt) != set_of_node_pt.end()))
281  {
282  // Find the number of values
283  unsigned n_value = cast_node_pt->nvalue();
284  // Copy them across
285  for (unsigned j = 0; j < n_value; j++)
286  {
287  Spectral_local_eqn(n, j) =
288  nodal_local_eqn(get_node_number(cast_node_pt), j);
289  }
290  // Remove from the set
291  set_of_node_pt.erase(cast_node_pt);
292  }
293  // Otherwise it's just data
294  else
295  {
296  Data* const data_pt = spectral_data_pt(n);
297  unsigned n_value = data_pt->nvalue();
298  // Loop over the number of values
299  for (unsigned j = 0; j < n_value; j++)
300  {
301  // Get the GLOBAL equation number
302  long eqn_number = data_pt->eqn_number(j);
303  // If the GLOBAL equation number is positive (a free variable)
304  if (eqn_number >= 0)
305  {
306  // Add the GLOBAL equation number to the
307  // local-to-global translation
308  // scheme
309  global_eqn_number_queue.push_back(eqn_number);
310  // Add pointer to the dof to the queue if required
311  if (store_local_dof_pt)
312  {
314  data_pt->value_pt(j));
315  }
316  // Add the local equation number to the local scheme
317  Spectral_local_eqn(n, j) = local_eqn_number;
318  // Increase the local number
319  local_eqn_number++;
320  }
321  else
322  {
323  // Set the local scheme to be pinned
324  Spectral_local_eqn(n, j) = Data::Is_pinned;
325  }
326  }
327  } // End of case when it's not a nodal dof
328  }
329 
330  // Now add our global equations numbers to the internal element storage
331  add_global_eqn_numbers(global_eqn_number_queue,
333  // Clear the memory used in the deque
334  if (store_local_dof_pt)
335  {
336  std::deque<double*>().swap(GeneralisedElement::Dof_pt_deque);
337  }
338 
339  } // End of case when there are spectral degrees of freedom
340  }
341 
342  unsigned nspectral() const
343  {
344  if (Spectral_data_pt == 0)
345  {
346  return 0;
347  }
348  else
349  {
350  return Spectral_data_pt->size();
351  }
352  }
353  };
354 
355 
356  //=======================================================================
360  //=======================================================================
361  template<unsigned DIM, unsigned NNODE_1D>
363  {
364  };
365 
366 
367  //=======================================================================
369  //=======================================================================
370  template<unsigned NNODE_1D>
371  class QSpectralElement<1, NNODE_1D> : public virtual SpectralElement,
372  public virtual LineElementBase
373  {
374  private:
377  // This is sort of optimal, because it means that the integration is exact
378  // for the shape functions. Can overwrite this in specific element
379  // defintion.
381 
382  public:
385  {
386  // There are NNODE_1D nodes in this element
387  this->set_n_node(NNODE_1D);
388  Spectral_order.resize(1, NNODE_1D);
389  Nodal_spectral_order.resize(1, NNODE_1D);
390  // Set the elemental and nodal dimensions
391  this->set_dimension(1);
392  // Assign integral point
393  this->set_integration_scheme(&integral);
394  // Calculate the nodal positions for the shape functions
396  // OneDLegendreShapeParam::calculate_nodal_positions(NNODE_1D);
397  }
398 
400  double s_min() const
401  {
402  return -1.0;
403  }
404 
406  double s_max() const
407  {
408  return 1.0;
409  }
410 
412  unsigned nvertex_node() const
413  {
414  return 2;
415  }
416 
418  Node* vertex_node_pt(const unsigned& j) const
419  {
420  unsigned n_node_1d = nnode_1d();
421  Node* nod_pt;
422  switch (j)
423  {
424  case 0:
425  nod_pt = this->node_pt(0);
426  break;
427  case 1:
428  nod_pt = this->node_pt(n_node_1d - 1);
429  break;
430  default:
431  std::ostringstream error_message;
432  error_message << "Vertex node number is " << j
433  << " but must be from 0 to 1\n";
434 
435  throw OomphLibError(error_message.str(),
438  }
439  return nod_pt;
440  }
441 
443  void local_coordinate_of_node(const unsigned& n, Vector<double>& s) const
444  {
445  s.resize(1);
447  }
448 
450  void local_fraction_of_node(const unsigned& n, Vector<double>& s_fraction)
451  {
452  this->local_coordinate_of_node(n, s_fraction);
453  // Resize
454  s_fraction[0] = 0.5 * (s_fraction[0] + 1.0);
455  }
456 
458  double local_one_d_fraction_of_node(const unsigned& n1d, const unsigned& i)
459  {
460  return 0.5 *
462  }
463 
465  inline void shape(const Vector<double>& s, Shape& psi) const;
466 
469  inline void dshape_local(const Vector<double>& s,
470  Shape& psi,
471  DShape& dpsids) const;
472 
476  inline void d2shape_local(const Vector<double>& s,
477  Shape& psi,
478  DShape& dpsids,
479  DShape& d2psids) const;
480 
485  DenseMatrix<double>& inverse_jacobian) const
486  {
487  return FiniteElement::invert_jacobian<1>(jacobian, inverse_jacobian);
488  }
489 
490 
492  unsigned nnode_1d() const
493  {
494  return NNODE_1D;
495  }
496 
498  void output(FILE* file_pt)
499  {
500  FiniteElement::output(file_pt);
501  }
502 
504  void output(FILE* file_pt, const unsigned& n_plot)
505  {
506  FiniteElement::output(file_pt, n_plot);
507  }
508 
510  void output(std::ostream& outfile);
511 
513  void output(std::ostream& outfile, const unsigned& n_plot);
514 
518  const unsigned& i,
519  const unsigned& nplot,
520  Vector<double>& s,
521  const bool& use_equally_spaced_interior_sample_points = false) const
522  {
523  if (nplot > 1)
524  {
525  s[0] = -1.0 + 2.0 * double(i) / double(nplot - 1);
526  if (use_equally_spaced_interior_sample_points)
527  {
528  double range = 2.0;
529  double dx_new = range / double(nplot);
530  double range_new = double(nplot - 1) * dx_new;
531  s[0] = -1.0 + 0.5 * dx_new + range_new * (1.0 + s[0]) / range;
532  }
533  }
534  else
535  {
536  s[0] = 0.0;
537  }
538  }
539 
542  std::string tecplot_zone_string(const unsigned& nplot) const
543  {
544  std::ostringstream header;
545  header << "ZONE I=" << nplot << "\n";
546  return header.str();
547  }
548 
551  unsigned nplot_points(const unsigned& nplot) const
552  {
553  unsigned DIM = 1;
554  unsigned np = 1;
555  for (unsigned i = 0; i < DIM; i++)
556  {
557  np *= nplot;
558  }
559  return np;
560  }
561 
567  void build_face_element(const int& face_index,
568  FaceElement* face_element_pt);
569  };
570 
571 
572  //=======================================================================
574  //=======================================================================
575  template<unsigned NNODE_1D>
577  Shape& psi) const
578  {
579  // Call the OneDimensional Shape functions
581  // OneDLegendreShapeParam psi1(NNODE_1D,s[0]);
582 
583  // Now let's loop over the nodal points in the element
584  // and copy the values back in
585  for (unsigned i = 0; i < NNODE_1D; i++)
586  {
587  psi(i) = psi1[i];
588  }
589  }
590 
591  //=======================================================================
593  //=======================================================================
594  template<unsigned NNODE_1D>
596  Shape& psi,
597  DShape& dpsids) const
598  {
599  // Call the shape functions and derivatives
602 
603  // OneDLegendreShapeParam psi1(NNODE_1D,s[0]);
604  // OneDLegendreDShapeParam dpsi1ds(NNODE_1D,s[0]);
605 
606  // Loop over shape functions in element and assign to psi
607  for (unsigned l = 0; l < NNODE_1D; l++)
608  {
609  psi(l) = psi1[l];
610  dpsids(l, 0) = dpsi1ds[l];
611  }
612  }
613 
614  //=======================================================================
617  //=======================================================================
618  template<unsigned NNODE_1D>
620  Shape& psi,
621  DShape& dpsids,
622  DShape& d2psids) const
623  {
624  std::ostringstream error_message;
625  error_message
626  << "\nd2shpe_local currently not implemented for this element\n";
627  throw OomphLibError(
628  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
629 
630  /* //Call the shape functions and derivatives */
631  /* OneDimensionalShape<NNODE_1D> psi1(s[0]); */
632  /* OneDimensionalDShape<NNODE_1D> dpsi1ds(s[0]); */
633  /* OneDimensionalD2Shape<NNODE_1D> d2psi1ds(s[0]); */
634 
635  /* //Loop over shape functions in element and assign to psi */
636  /* for(unsigned l=0;l<NNODE_1D;l++) */
637  /* { */
638  /* psi[l] = psi1[l]; */
639  /* dpsids[l][0] = dpsi1ds[l]; */
640  /* d2psids[l][0] = d2psi1ds[l]; */
641  /* } */
642  }
643 
644 
645  //=======================================================================
647  //=======================================================================
648  template<unsigned NNODE_1D>
649  class QSpectralElement<2, NNODE_1D> : public virtual SpectralElement,
650  public virtual QuadElementBase
651  {
652  private:
655  // This is sort of optimal, because it means that the integration is exact
656  // for the shape functions. Can overwrite this in specific element
657  // defintion.
659 
660  public:
663  {
664  // There are NNODE_1D^2 nodes in this element
665  this->set_n_node(NNODE_1D * NNODE_1D);
666  Spectral_order.resize(2, NNODE_1D);
667  Nodal_spectral_order.resize(2, NNODE_1D);
668  // Set the elemental and nodal dimensions
669  this->set_dimension(2);
670  // Assign integral pointer
671  this->set_integration_scheme(&integral);
672  // Calculate the nodal positions for the shape functions
674  // OneDLegendreShapeParam::calculate_nodal_positions(NNODE_1D);
675  }
676 
678  double s_min() const
679  {
680  return -1.0;
681  }
682 
684  double s_max() const
685  {
686  return 1.0;
687  }
688 
690  unsigned nvertex_node() const
691  {
692  return 4;
693  }
694 
696  Node* vertex_node_pt(const unsigned& j) const
697  {
698  unsigned n_node_1d = nnode_1d();
699  Node* nod_pt;
700  switch (j)
701  {
702  case 0:
703  nod_pt = this->node_pt(0);
704  break;
705  case 1:
706  nod_pt = this->node_pt(n_node_1d - 1);
707  break;
708  case 2:
709  nod_pt = this->node_pt(n_node_1d * (n_node_1d - 1));
710  break;
711  case 3:
712  nod_pt = this->node_pt(n_node_1d * n_node_1d - 1);
713  break;
714  default:
715  std::ostringstream error_message;
716  error_message << "Vertex node number is " << j
717  << " but must be from 0 to 3\n";
718 
719  throw OomphLibError(error_message.str(),
722  }
723  return nod_pt;
724  }
725 
726 
728  void local_coordinate_of_node(const unsigned& n, Vector<double>& s) const
729  {
730  s.resize(2);
731  unsigned n0 = n % NNODE_1D;
732  unsigned n1 = unsigned(double(n) / double(NNODE_1D));
735  }
736 
738  void local_fraction_of_node(const unsigned& n, Vector<double>& s_fraction)
739  {
740  this->local_coordinate_of_node(n, s_fraction);
741  // Resize
742  s_fraction[0] = 0.5 * (s_fraction[0] + 1.0);
743  s_fraction[1] = 0.5 * (s_fraction[1] + 1.0);
744  }
745 
747  double local_one_d_fraction_of_node(const unsigned& n1d, const unsigned& i)
748  {
749  return 0.5 *
751  }
752 
754  inline void shape(const Vector<double>& s, Shape& psi) const;
755 
758  inline void dshape_local(const Vector<double>& s,
759  Shape& psi,
760  DShape& dpsids) const;
761 
767  inline void d2shape_local(const Vector<double>& s,
768  Shape& psi,
769  DShape& dpsids,
770  DShape& d2psids) const;
771 
776  DenseMatrix<double>& inverse_jacobian) const
777  {
778  return FiniteElement::invert_jacobian<2>(jacobian, inverse_jacobian);
779  }
780 
781 
783  unsigned nnode_1d() const
784  {
785  return NNODE_1D;
786  }
787 
789  void output(FILE* file_pt)
790  {
791  FiniteElement::output(file_pt);
792  }
793 
795  void output(FILE* file_pt, const unsigned& n_plot)
796  {
797  FiniteElement::output(file_pt, n_plot);
798  }
799 
801  void output(std::ostream& outfile);
802 
804  void output(std::ostream& outfile, const unsigned& n_plot);
805 
809  const unsigned& i,
810  const unsigned& nplot,
811  Vector<double>& s,
812  const bool& use_equally_spaced_interior_sample_points = false) const
813  {
814  if (nplot > 1)
815  {
816  unsigned i0 = i % nplot;
817  unsigned i1 = (i - i0) / nplot;
818 
819  s[0] = -1.0 + 2.0 * double(i0) / double(nplot - 1);
820  s[1] = -1.0 + 2.0 * double(i1) / double(nplot - 1);
821  if (use_equally_spaced_interior_sample_points)
822  {
823  double range = 2.0;
824  double dx_new = range / double(nplot);
825  double range_new = double(nplot - 1) * dx_new;
826  s[0] = -1.0 + 0.5 * dx_new + range_new * (1.0 + s[0]) / range;
827  s[1] = -1.0 + 0.5 * dx_new + range_new * (1.0 + s[1]) / range;
828  }
829  }
830  else
831  {
832  s[0] = 0.0;
833  s[1] = 0.0;
834  }
835  }
836 
837 
840  std::string tecplot_zone_string(const unsigned& nplot) const
841  {
842  std::ostringstream header;
843  header << "ZONE I=" << nplot << ", J=" << nplot << "\n";
844  return header.str();
845  }
846 
849  unsigned nplot_points(const unsigned& nplot) const
850  {
851  unsigned DIM = 2;
852  unsigned np = 1;
853  for (unsigned i = 0; i < DIM; i++)
854  {
855  np *= nplot;
856  }
857  return np;
858  }
859 
867  void build_face_element(const int& face_index,
868  FaceElement* face_element_pt);
869  };
870 
871 
872  //=======================================================================
874  //=======================================================================
875  template<unsigned NNODE_1D>
877  Shape& psi) const
878  {
879  // Call the OneDimensional Shape functions
880  OneDimensionalLegendreShape<NNODE_1D> psi1(s[0]), psi2(s[1]);
881  // OneDLegendreShapeParam psi1(NNODE_1D,s[0]), psi2(NNODE_1D,s[1]);
882 
883  // Now let's loop over the nodal points in the element
884  // and copy the values back in
885  for (unsigned i = 0; i < NNODE_1D; i++)
886  {
887  for (unsigned j = 0; j < NNODE_1D; j++)
888  {
889  psi(NNODE_1D * i + j) = psi2[i] * psi1[j];
890  }
891  }
892  }
893 
894  //=======================================================================
896  //=======================================================================
897  template<unsigned NNODE_1D>
899  Shape& psi,
900  DShape& dpsids) const
901  {
902  // Call the shape functions and derivatives
903  OneDimensionalLegendreShape<NNODE_1D> psi1(s[0]), psi2(s[1]);
904  OneDimensionalLegendreDShape<NNODE_1D> dpsi1ds(s[0]), dpsi2ds(s[1]);
905 
906  // OneDLegendreShapeParam psi1(NNODE_1D,s[0]), psi2(NNODE_1D,s[1]);
907  // OneDLegendreDShapeParam dpsi1ds(NNODE_1D,s[0]), dpsi2ds(NNODE_1D,s[1]);
908 
909  // Index for the shape functions
910  unsigned index = 0;
911  // Loop over shape functions in element
912  for (unsigned i = 0; i < NNODE_1D; i++)
913  {
914  for (unsigned j = 0; j < NNODE_1D; j++)
915  {
916  // Assign the values
917  dpsids(index, 0) = psi2[i] * dpsi1ds[j];
918  dpsids(index, 1) = dpsi2ds[i] * psi1[j];
919  psi[index] = psi2[i] * psi1[j];
920  // Increment the index
921  ++index;
922  }
923  }
924  }
925 
926 
927  //=======================================================================
932  //=======================================================================
933  template<unsigned NNODE_1D>
935  Shape& psi,
936  DShape& dpsids,
937  DShape& d2psids) const
938  {
939  std::ostringstream error_message;
940  error_message
941  << "\nd2shpe_local currently not implemented for this element\n";
942  throw OomphLibError(
943  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
944 
945  /* //Call the shape functions and derivatives */
946  /* OneDimensionalShape<NNODE_1D> psi1(s[0]); */
947  /* OneDimensionalDShape<NNODE_1D> dpsi1ds(s[0]); */
948  /* OneDimensionalD2Shape<NNODE_1D> d2psi1ds(s[0]); */
949 
950  /* //Loop over shape functions in element and assign to psi */
951  /* for(unsigned l=0;l<NNODE_1D;l++) */
952  /* { */
953  /* psi[l] = psi1[l]; */
954  /* dpsids[l][0] = dpsi1ds[l]; */
955  /* d2psids[l][0] = d2psi1ds[l]; */
956  /* } */
957  }
958 
959 
960  //=======================================================================
962  //=======================================================================
963  template<unsigned NNODE_1D>
964  class QSpectralElement<3, NNODE_1D> : public virtual SpectralElement,
965  public virtual BrickElementBase
966  {
967  private:
970  // This is sort of optimal, because it means that the integration is exact
971  // for the shape functions. Can overwrite this in specific element
972  // defintion.
974 
975  public:
978  {
979  // There are NNODE_1D^3 nodes in this element
980  this->set_n_node(NNODE_1D * NNODE_1D * NNODE_1D);
981  Spectral_order.resize(3, NNODE_1D);
982  Nodal_spectral_order.resize(3, NNODE_1D);
983  // Set the elemental and nodal dimensions
984  this->set_dimension(3);
985  // Assign integral point
986  this->set_integration_scheme(&integral);
987  // Calculate the nodal positions for the shape functions
989  // OneDLegendreShapeParam::calculate_nodal_positions(NNODE_1D);
990  }
991 
993  double s_min() const
994  {
995  return -1.0;
996  }
997 
999  double s_max() const
1000  {
1001  return 1.0;
1002  }
1003 
1005  unsigned nvertex_node() const
1006  {
1007  return 8;
1008  }
1009 
1011  Node* vertex_node_pt(const unsigned& j) const
1012  {
1013  unsigned n_node_1d = nnode_1d();
1014  Node* nod_pt;
1015  switch (j)
1016  {
1017  case 0:
1018  nod_pt = this->node_pt(0);
1019  break;
1020  case 1:
1021  nod_pt = this->node_pt(n_node_1d - 1);
1022  break;
1023  case 2:
1024  nod_pt = this->node_pt(n_node_1d * (n_node_1d - 1));
1025  break;
1026  case 3:
1027  nod_pt = this->node_pt(n_node_1d * n_node_1d - 1);
1028  break;
1029  case 4:
1030  nod_pt = this->node_pt(n_node_1d * n_node_1d * (n_node_1d - 1));
1031  break;
1032  case 5:
1033  nod_pt = this->node_pt(n_node_1d * n_node_1d * (n_node_1d - 1) +
1034  (n_node_1d - 1));
1035  break;
1036  case 6:
1037  nod_pt = this->node_pt(n_node_1d * n_node_1d * n_node_1d - n_node_1d);
1038  break;
1039  case 7:
1040  nod_pt = this->node_pt(n_node_1d * n_node_1d * n_node_1d - 1);
1041  break;
1042  default:
1043  std::ostringstream error_message;
1044  error_message << "Vertex node number is " << j
1045  << " but must be from 0 to 7\n";
1046 
1047  throw OomphLibError(error_message.str(),
1050  }
1051  return nod_pt;
1052  }
1053 
1054 
1056  void local_coordinate_of_node(const unsigned& n, Vector<double>& s) const
1057  {
1058  s.resize(3);
1059  unsigned n0 = n % NNODE_1D;
1060  unsigned n1 = unsigned(double(n) / double(NNODE_1D)) % NNODE_1D;
1061  unsigned n2 = unsigned(double(n) / double(NNODE_1D * NNODE_1D));
1065  }
1066 
1068  void local_fraction_of_node(const unsigned& n, Vector<double>& s_fraction)
1069  {
1070  this->local_coordinate_of_node(n, s_fraction);
1071  // Resize
1072  s_fraction[0] = 0.5 * (s_fraction[0] + 1.0);
1073  s_fraction[1] = 0.5 * (s_fraction[1] + 1.0);
1074  s_fraction[2] = 0.5 * (s_fraction[2] + 1.0);
1075  }
1076 
1078  double local_one_d_fraction_of_node(const unsigned& n1d, const unsigned& i)
1079  {
1080  return 0.5 *
1082  }
1083 
1085  inline void shape(const Vector<double>& s, Shape& psi) const;
1086 
1089  inline void dshape_local(const Vector<double>& s,
1090  Shape& psi,
1091  DShape& dpsids) const;
1092 
1101  inline void d2shape_local(const Vector<double>& s,
1102  Shape& psi,
1103  DShape& dpsids,
1104  DShape& d2psids) const;
1105 
1106 
1111  DenseMatrix<double>& inverse_jacobian) const
1112  {
1113  return FiniteElement::invert_jacobian<3>(jacobian, inverse_jacobian);
1114  }
1115 
1117  unsigned nnode_1d() const
1118  {
1119  return NNODE_1D;
1120  }
1121 
1123  void output(FILE* file_pt)
1124  {
1125  FiniteElement::output(file_pt);
1126  }
1127 
1129  void output(FILE* file_pt, const unsigned& n_plot)
1130  {
1131  FiniteElement::output(file_pt, n_plot);
1132  }
1133 
1135  void output(std::ostream& outfile);
1136 
1138  void output(std::ostream& outfile, const unsigned& nplot);
1139 
1143  const unsigned& i,
1144  const unsigned& nplot,
1145  Vector<double>& s,
1146  const bool& use_equally_spaced_interior_sample_points = false) const
1147  {
1148  if (nplot > 1)
1149  {
1150  unsigned i01 = i % (nplot * nplot);
1151  unsigned i0 = i01 % nplot;
1152  unsigned i1 = (i01 - i0) / nplot;
1153  unsigned i2 = (i - i01) / (nplot * nplot);
1154 
1155  s[0] = -1.0 + 2.0 * double(i0) / double(nplot - 1);
1156  s[1] = -1.0 + 2.0 * double(i1) / double(nplot - 1);
1157  s[2] = -1.0 + 2.0 * double(i2) / double(nplot - 1);
1158  if (use_equally_spaced_interior_sample_points)
1159  {
1160  double range = 2.0;
1161  double dx_new = range / double(nplot);
1162  double range_new = double(nplot - 1) * dx_new;
1163  s[0] = -1.0 + 0.5 * dx_new + range_new * (1.0 + s[0]) / range;
1164  s[1] = -1.0 + 0.5 * dx_new + range_new * (1.0 + s[1]) / range;
1165  s[2] = -1.0 + 0.5 * dx_new + range_new * (1.0 + s[2]) / range;
1166  }
1167  }
1168  else
1169  {
1170  s[0] = 0.0;
1171  s[1] = 0.0;
1172  s[2] = 0.0;
1173  }
1174  }
1175 
1176 
1179  std::string tecplot_zone_string(const unsigned& nplot) const
1180  {
1181  std::ostringstream header;
1182  header << "ZONE I=" << nplot << ", J=" << nplot << ", K=" << nplot
1183  << "\n";
1184  return header.str();
1185  }
1186 
1189  unsigned nplot_points(const unsigned& nplot) const
1190  {
1191  unsigned DIM = 3;
1192  unsigned np = 1;
1193  for (unsigned i = 0; i < DIM; i++)
1194  {
1195  np *= nplot;
1196  }
1197  return np;
1198  }
1199 
1209  void build_face_element(const int& face_index,
1210  FaceElement* face_element_pt);
1211  };
1212 
1213 
1214  //=======================================================================
1216  //=======================================================================
1217  template<unsigned NNODE_1D>
1219  Shape& psi) const
1220  {
1221  // Call the OneDimensional Shape functions
1222  OneDimensionalLegendreShape<NNODE_1D> psi1(s[0]), psi2(s[1]), psi3(s[2]);
1223  // OneDLegendreShapeParam psi1(NNODE_1D,s[0]), psi2(NNODE_1D,s[1]),
1224  // psi3(NNODE_1D,s[2]);
1225 
1226  // Now let's loop over the nodal points in the element
1227  // and copy the values back in
1228  for (unsigned i = 0; i < NNODE_1D; i++)
1229  {
1230  for (unsigned j = 0; j < NNODE_1D; j++)
1231  {
1232  for (unsigned k = 0; k < NNODE_1D; k++)
1233  {
1234  psi(NNODE_1D * NNODE_1D * i + NNODE_1D * j + k) =
1235  psi3[i] * psi2[j] * psi1[k];
1236  }
1237  }
1238  }
1239  }
1240 
1241  //=======================================================================
1243  //=======================================================================
1244  template<unsigned NNODE_1D>
1246  Shape& psi,
1247  DShape& dpsids) const
1248  {
1249  // Call the shape functions and derivatives
1250  // OneDLegendreShapeParam psi1(NNODE_1D,s[0]), psi2(NNODE_1D,s[1]),
1251  // psi3(NNODE_1D,s[2]);
1252  // OneDLegendreDShapeParam dpsi1ds(NNODE_1D,s[0]), dpsi2ds(NNODE_1D,s[1]),
1253  // dpsi3ds(NNODE_1D,s[2]);
1254  OneDimensionalLegendreShape<NNODE_1D> psi1(s[0]), psi2(s[1]), psi3(s[2]);
1255  OneDimensionalLegendreDShape<NNODE_1D> dpsi1ds(s[0]), dpsi2ds(s[1]),
1256  dpsi3ds(s[2]);
1257 
1258 
1259  // Index for the shape functions
1260  unsigned index = 0;
1261 
1262  // Loop over shape functions in element
1263  for (unsigned i = 0; i < NNODE_1D; i++)
1264  {
1265  for (unsigned j = 0; j < NNODE_1D; j++)
1266  {
1267  for (unsigned k = 0; k < NNODE_1D; k++)
1268  {
1269  // Assign the values
1270  dpsids(index, 0) = psi3[i] * psi2[j] * dpsi1ds[k];
1271  dpsids(index, 1) = psi3[i] * dpsi2ds[j] * psi1[k];
1272  dpsids(index, 2) = dpsi3ds[i] * psi2[j] * psi1[k];
1273  psi[index] = psi3[i] * psi2[j] * psi1[k];
1274  // Increment the index
1275  ++index;
1276  }
1277  }
1278  }
1279  }
1280 
1281 
1282  //=======================================================================
1290  //=======================================================================
1291  template<unsigned NNODE_1D>
1293  Shape& psi,
1294  DShape& dpsids,
1295  DShape& d2psids) const
1296  {
1297  std::ostringstream error_message;
1298  error_message
1299  << "\nd2shpe_local currently not implemented for this element\n";
1300  throw OomphLibError(
1301  error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
1302 
1303  /* //Call the shape functions and derivatives */
1304  /* OneDimensionalShape<NNODE_1D> psi1(s[0]); */
1305  /* OneDimensionalDShape<NNODE_1D> dpsi1ds(s[0]); */
1306  /* OneDimensionalD2Shape<NNODE_1D> d2psi1ds(s[0]); */
1307 
1308  /* //Loop over shape functions in element and assign to psi */
1309  /* for(unsigned l=0;l<NNODE_1D;l++) */
1310  /* { */
1311  /* psi[l] = psi1[l]; */
1312  /* dpsids[l][0] = dpsi1ds[l]; */
1313  /* d2psids[l][0] = d2psi1ds[l]; */
1314  /* } */
1315  }
1316 
1317  //==============================================================
1320  //=============================================================
1321  template<unsigned DIM>
1323  {
1324  public:
1327  };
1328 
1329 } // namespace oomph
1330 
1331 #endif
int i
Definition: BiCGSTAB_step_by_step.cpp:9
const unsigned n
Definition: CG3DPackingUnitTest.cpp:11
float * p
Definition: Tutorial_Map_using.cpp:9
Base class for all brick elements.
Definition: Qelements.h:1233
Definition: shape.h:278
Definition: nodes.h:86
long & eqn_number(const unsigned &i)
Return the equation number of the i-th stored variable.
Definition: nodes.h:367
virtual void describe_dofs(std::ostream &out, const std::string &current_string) const
Definition: nodes.cc:939
static long Is_pinned
Static "Magic number" to indicate pinned values.
Definition: nodes.h:183
double * value_pt(const unsigned &i) const
Definition: nodes.h:324
unsigned nvalue() const
Return number of values stored in data object (incl pinned ones).
Definition: nodes.h:483
static long Is_unclassified
Definition: nodes.h:192
void resize(const unsigned long &n)
Definition: matrices.h:498
Definition: elements.h:4338
Definition: elements.h:1313
virtual void output(std::ostream &outfile)
Definition: elements.h:3050
virtual void describe_local_dofs(std::ostream &out, const std::string &current_string) const
Definition: elements.cc:1709
virtual void assign_all_generic_local_eqn_numbers(const bool &store_local_dof_pt)
Definition: elements.h:2164
static std::deque< double * > Dof_pt_deque
Definition: elements.h:231
Base class for all line elements.
Definition: Qelements.h:466
Definition: nodes.h:906
Definition: Qspectral_elements.h:94
OneDLegendreDShapeParam(const unsigned &order, const double &s)
Definition: Qspectral_elements.h:97
Class that returns the shape functions associated with legendre.
Definition: Qspectral_elements.h:46
static void calculate_nodal_positions(const unsigned &order)
Static function used to populate the stored positions.
Definition: Qspectral_elements.h:51
static std::map< unsigned, Vector< double > > z
Definition: Qspectral_elements.h:48
static double nodal_position(const unsigned &order, const unsigned &n)
Definition: Qspectral_elements.h:60
OneDLegendreShapeParam(const unsigned &order, const double &s)
Constructor.
Definition: Qspectral_elements.h:67
Definition: shape.h:1288
Class that returns the shape functions associated with legendre.
Definition: shape.h:1234
static double nodal_position(const unsigned &n)
Definition: shape.h:1250
static void calculate_nodal_positions()
Static function used to populate the stored positions.
Definition: shape.h:1241
Definition: oomph_definitions.h:222
double invert_jacobian_mapping(const DenseMatrix< double > &jacobian, DenseMatrix< double > &inverse_jacobian) const
Definition: Qspectral_elements.h:484
static GaussLobattoLegendre< 1, NNODE_1D > integral
Assign the static integral.
Definition: Qspectral_elements.h:380
std::string tecplot_zone_string(const unsigned &nplot) const
Definition: Qspectral_elements.h:542
double s_min() const
Min. value of local coordinate.
Definition: Qspectral_elements.h:400
double local_one_d_fraction_of_node(const unsigned &n1d, const unsigned &i)
The local one-d fraction is the same.
Definition: Qspectral_elements.h:458
unsigned nplot_points(const unsigned &nplot) const
Definition: Qspectral_elements.h:551
Node * vertex_node_pt(const unsigned &j) const
Pointer to the j-th vertex node in the element.
Definition: Qspectral_elements.h:418
QSpectralElement()
Constructor.
Definition: Qspectral_elements.h:384
void local_coordinate_of_node(const unsigned &n, Vector< double > &s) const
Get local coordinates of node j in the element; vector sets its own size.
Definition: Qspectral_elements.h:443
void output(FILE *file_pt)
C-style output.
Definition: Qspectral_elements.h:498
unsigned nnode_1d() const
Number of nodes along each element edge.
Definition: Qspectral_elements.h:492
void local_fraction_of_node(const unsigned &n, Vector< double > &s_fraction)
Get the local fractino of node j in the element.
Definition: Qspectral_elements.h:450
void output(FILE *file_pt, const unsigned &n_plot)
C_style output at n_plot points.
Definition: Qspectral_elements.h:504
double s_max() const
Max. value of local coordinate.
Definition: Qspectral_elements.h:406
void get_s_plot(const unsigned &i, const unsigned &nplot, Vector< double > &s, const bool &use_equally_spaced_interior_sample_points=false) const
Definition: Qspectral_elements.h:517
unsigned nvertex_node() const
Number of vertex nodes in the element.
Definition: Qspectral_elements.h:412
void output(FILE *file_pt)
C-style output.
Definition: Qspectral_elements.h:789
void output(FILE *file_pt, const unsigned &n_plot)
C_style output at n_plot points.
Definition: Qspectral_elements.h:795
void local_coordinate_of_node(const unsigned &n, Vector< double > &s) const
Get local coordinates of node j in the element; vector sets its own size.
Definition: Qspectral_elements.h:728
std::string tecplot_zone_string(const unsigned &nplot) const
Definition: Qspectral_elements.h:840
double invert_jacobian_mapping(const DenseMatrix< double > &jacobian, DenseMatrix< double > &inverse_jacobian) const
Definition: Qspectral_elements.h:775
Node * vertex_node_pt(const unsigned &j) const
Pointer to the j-th vertex node in the element.
Definition: Qspectral_elements.h:696
void get_s_plot(const unsigned &i, const unsigned &nplot, Vector< double > &s, const bool &use_equally_spaced_interior_sample_points=false) const
Definition: Qspectral_elements.h:808
QSpectralElement()
Constructor.
Definition: Qspectral_elements.h:662
void local_fraction_of_node(const unsigned &n, Vector< double > &s_fraction)
Get the local fractino of node j in the element.
Definition: Qspectral_elements.h:738
unsigned nplot_points(const unsigned &nplot) const
Definition: Qspectral_elements.h:849
unsigned nnode_1d() const
Number of nodes along each element edge.
Definition: Qspectral_elements.h:783
unsigned nvertex_node() const
Number of vertex nodes in the element.
Definition: Qspectral_elements.h:690
double s_max() const
Max. value of local coordinate.
Definition: Qspectral_elements.h:684
double s_min() const
Min. value of local coordinate.
Definition: Qspectral_elements.h:678
static GaussLobattoLegendre< 2, NNODE_1D > integral
Assign the static integral.
Definition: Qspectral_elements.h:658
double local_one_d_fraction_of_node(const unsigned &n1d, const unsigned &i)
The local one-d fraction is the same.
Definition: Qspectral_elements.h:747
void output(FILE *file_pt, const unsigned &n_plot)
C_style output at n_plot points.
Definition: Qspectral_elements.h:1129
QSpectralElement()
Constructor.
Definition: Qspectral_elements.h:977
void output(FILE *file_pt)
C-style output.
Definition: Qspectral_elements.h:1123
Node * vertex_node_pt(const unsigned &j) const
Pointer to the j-th vertex node in the element.
Definition: Qspectral_elements.h:1011
void get_s_plot(const unsigned &i, const unsigned &nplot, Vector< double > &s, const bool &use_equally_spaced_interior_sample_points=false) const
Definition: Qspectral_elements.h:1142
static GaussLobattoLegendre< 3, NNODE_1D > integral
Assign the static integral.
Definition: Qspectral_elements.h:973
void local_fraction_of_node(const unsigned &n, Vector< double > &s_fraction)
Get the local fractino of node j in the element.
Definition: Qspectral_elements.h:1068
void local_coordinate_of_node(const unsigned &n, Vector< double > &s) const
Get local coordinates of node j in the element; vector sets its own size.
Definition: Qspectral_elements.h:1056
double s_min() const
Min. value of local coordinate.
Definition: Qspectral_elements.h:993
double local_one_d_fraction_of_node(const unsigned &n1d, const unsigned &i)
The local one-d fraction is the same.
Definition: Qspectral_elements.h:1078
double invert_jacobian_mapping(const DenseMatrix< double > &jacobian, DenseMatrix< double > &inverse_jacobian) const
Definition: Qspectral_elements.h:1110
double s_max() const
Max. value of local coordinate.
Definition: Qspectral_elements.h:999
unsigned nvertex_node() const
Number of vertex nodes in the element.
Definition: Qspectral_elements.h:1005
unsigned nnode_1d() const
Number of nodes along each element edge.
Definition: Qspectral_elements.h:1117
std::string tecplot_zone_string(const unsigned &nplot) const
Definition: Qspectral_elements.h:1179
unsigned nplot_points(const unsigned &nplot) const
Definition: Qspectral_elements.h:1189
Definition: Qspectral_elements.h:363
Base class for all quad elements.
Definition: Qelements.h:821
Definition: Qspectral_elements.h:1323
RefineableQSpectralElement()
Empty constuctor.
Definition: Qspectral_elements.h:1326
Definition: shape.h:76
Definition: Qspectral_elements.h:153
Vector< Data * > * Spectral_data_pt
Additional Storage for shared spectral data.
Definition: Qspectral_elements.h:156
DenseMatrix< int > Spectral_local_eqn
Local equation numbers for the spectral degrees of freedom.
Definition: Qspectral_elements.h:165
virtual void assign_all_generic_local_eqn_numbers(const bool &store_local_dof_pt)
Definition: Qspectral_elements.h:229
virtual void describe_local_dofs(std::ostream &out, const std::string &current_string) const
Definition: Qspectral_elements.h:195
unsigned nspectral() const
Definition: Qspectral_elements.h:342
virtual ~SpectralElement()
Definition: Qspectral_elements.h:170
Data * spectral_data_pt(const unsigned &i) const
Definition: Qspectral_elements.h:182
Vector< unsigned > Nodal_spectral_order
Vector that represents the nodal spectral order.
Definition: Qspectral_elements.h:162
SpectralElement()
Definition: Qspectral_elements.h:168
Vector< unsigned > Spectral_order
Vector that represents the spectral order in each dimension.
Definition: Qspectral_elements.h:159
RealScalar s
Definition: level1_cplx_impl.h:130
char char char int int * k
Definition: level2_impl.h:374
#define DIM
Definition: linearised_navier_stokes_elements.h:44
Real fabs(const Real &a)
Definition: boostmultiprec.cpp:117
std::string string(const unsigned &i)
Definition: oomph_definitions.cc:286
void shape(const double &s, double *Psi)
Definition: shape.h:564
double dlegendre(const unsigned &p, const double &x)
Definition: orthpoly.h:121
const double eps
Definition: orthpoly.h:52
double ddlegendre(const unsigned &p, const double &x)
Definition: orthpoly.h:144
double legendre(const unsigned &p, const double &x)
Definition: orthpoly.h:57
void gll_nodes(const unsigned &Nnode, Vector< double > &x)
Calculates the Gauss Lobatto Legendre abscissas for degree p = NNode-1.
Definition: orthpoly.cc:33
DRAIG: Change all instances of (SPATIAL_DIM) to (DIM-1).
Definition: AnisotropicHookean.h:10
void output(std::ostream &outfile)
Output with default number of plot points.
Definition: gen_axisym_advection_diffusion_elements.h:161
#define OOMPH_EXCEPTION_LOCATION
Definition: oomph_definitions.h:61
#define OOMPH_CURRENT_FUNCTION
Definition: oomph_definitions.h:86
std::ofstream out("Result.txt")
std::ptrdiff_t j
Definition: tut_arithmetic_redux_minmax.cpp:2