refineable_quad_spectral_element.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-2025 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#ifndef OOMPH_REFINEABLE_QUAD_SPECTRAL_ELEMENT_HEADER
27#define OOMPH_REFINEABLE_QUAD_SPECTRAL_ELEMENT_HEADER
28
29
30// Config header
31#ifdef HAVE_CONFIG_H
32#include <oomph-lib-config.h>
33#endif
34
35
36// oomph-lib headers
38
39namespace oomph
40{
41 //=======================================================================
42 /// Refineable version of QuadElements that add functionality for spectral
43 /// Elements.
44 //=======================================================================
45 template<>
47 {
48 public:
49 /// Constructor
51 {
52#ifdef LEAK_CHECK
53 LeakCheckNames::RefineableQSpectralElement<2> _build += 1;
54#endif
55 }
56
57
58 /// Broken copy constructor
60 delete;
61
62 /// Broken assignment operator
63 // Commented out broken assignment operator because this can lead to a
64 // conflict warning when used in the virtual inheritence hierarchy.
65 // Essentially the compiler doesn't realise that two separate
66 // implementations of the broken function are the same and so, quite
67 // rightly, it shouts.
68 /*void operator=(const RefineableQSpectralElement<2>&) = delete;*/
69
70 /// Destructor
72 {
73#ifdef LEAK_CHECK
74 LeakCheckNames::RefineableQSpectralElement<2> _build -= 1;
75#endif
76 }
77
78 /// The only thing to add is rebuild from sons
79 void rebuild_from_sons(Mesh*& mesh_pt)
80 {
81 // The timestepper should be the same for all nodes and node 0 should
82 // never be deleted.
83 if (this->node_pt(0) == 0)
84 {
85 throw OomphLibError("The Corner node (0) does not exist",
88 }
89
90 TimeStepper* time_stepper_pt = this->node_pt(0)->time_stepper_pt();
91 unsigned ntstorage = time_stepper_pt->ntstorage();
92
93 unsigned jnod = 0;
95 // Loop over the nodes in the element
96 unsigned n_p = this->nnode_1d();
97 for (unsigned i0 = 0; i0 < n_p; i0++)
98 {
99 // Get the fractional position of the node
100 s_fraction[0] = this->local_one_d_fraction_of_node(i0, 0);
101 // Local coordinate
102 s[0] = -1.0 + 2.0 * s_fraction[0];
103
104 for (unsigned i1 = 0; i1 < n_p; i1++)
105 {
106 // Get the fractional position of the node in the direction of s[1]
107 s_fraction[1] = this->local_one_d_fraction_of_node(i1, 1);
108 // Local coordinate in father element
109 s[1] = -1.0 + 2.0 * s_fraction[1];
110
111 // Set the local node number
112 jnod = i0 + n_p * i1;
113
114 // If the node has not been built
115 if (this->node_pt(jnod) == 0)
116 {
117 // Has the node been created by one of its neighbours
118 bool is_periodic = false;
120 this->node_created_by_neighbour(s_fraction, is_periodic);
121
122 // If it has set the pointer
123 if (created_node_pt != 0)
124 {
125 // If the node is periodic
126 if (is_periodic)
127 {
128 throw OomphLibError("Cannot handle periodic nodes in "
129 "refineable spectral elements yet",
132 }
133 // Non-periodic case, just set the pointer
134 else
135 {
136 this->node_pt(jnod) = created_node_pt;
137 }
138 }
139 // Otherwise, we need to build it
140 else
141 {
142 // First, find the son element in which the node should live
143
144 // Find coordinates in the sons
146 using namespace QuadTreeNames;
147 int son = -10;
148 // If negative on the west side
149 if (s_fraction[0] < 0.5)
150 {
151 // On the south side
152 if (s_fraction[1] < 0.5)
153 {
154 // It's the southwest son
155 son = SW;
156 s_in_son[0] = -1.0 + 4.0 * s_fraction[0];
157 s_in_son[1] = -1.0 + 4.0 * s_fraction[1];
158 }
159 // On the north side
160 else
161 {
162 // It's the northwest son
163 son = NW;
164 s_in_son[0] = -1.0 + 4.0 * s_fraction[0];
165 s_in_son[1] = -1.0 + 4.0 * (s_fraction[1] - 0.5);
166 }
167 }
168 else
169 {
170 // On the south side
171 if (s_fraction[1] < 0.5)
172 {
173 // It's the southeast son
174 son = SE;
175 s_in_son[0] = -1.0 + 4.0 * (s_fraction[0] - 0.5);
176 s_in_son[1] = -1.0 + 4.0 * s_fraction[1];
177 }
178 // On the north side
179 else
180 {
181 // It's the northeast son
182 son = NE;
183 s_in_son[0] = -1.0 + 4.0 * (s_fraction[0] - 0.5);
184 s_in_son[1] = -1.0 + 4.0 * (s_fraction[1] - 0.5);
185 }
186 }
187
188 // Get the pointer to the son element in which the new node
189 // would live
191 dynamic_cast<RefineableQSpectralElement<2>*>(
192 this->tree_pt()->son_pt(son)->object_pt());
193
194 // If we are rebuilding, then worry about the boundary conditions
195 // Find the boundary of the node
196 // Initially none
197 int boundary = Tree::OMEGA;
198 // If we are on the western boundary
199 if (i0 == 0)
200 {
201 boundary = W;
202 }
203 // If we are on the eastern boundary
204 else if (i0 == n_p - 1)
205 {
206 boundary = E;
207 }
208
209 // If we are on the southern boundary
210 if (i1 == 0)
211 {
212 // If we already have already set the boundary, we're on a
213 // corner
214 switch (boundary)
215 {
216 case W:
217 boundary = SW;
218 break;
219 case E:
220 boundary = SE;
221 break;
222 // Boundary not set
223 default:
224 boundary = S;
225 break;
226 }
227 }
228 // If we are the northern bounadry
229 else if (i1 == n_p - 1)
230 {
231 // If we already have a boundary
232 switch (boundary)
233 {
234 case W:
235 boundary = NW;
236 break;
237 case E:
238 boundary = NE;
239 break;
240 default:
241 boundary = N;
242 break;
243 }
244 }
245
246 // set of boundaries that this edge in the son lives on
247 std::set<unsigned> boundaries;
248
249 // Now get the boundary conditions from the son
250 // The boundaries will be common to the son because there can be
251 // no rotations here
252 if (boundary != Tree::OMEGA)
253 {
254 son_el_pt->get_boundaries(boundary, boundaries);
255 }
256
257 // If the node lives on a boundary:
258 // Construct a boundary node,
259 // Get boundary conditions and
260 // update all lookup schemes
261 if (boundaries.size() > 0)
262 {
263 // Construct the new node
264 this->node_pt(jnod) =
265 this->construct_boundary_node(jnod, time_stepper_pt);
266
267 // Get the boundary conditions from the son
268 Vector<int> bound_cons(ncont_interpolated_values());
269 son_el_pt->get_bcs(boundary, bound_cons);
270
271 // Loop over the values and pin if necessary
272 unsigned nval = this->node_pt(jnod)->nvalue();
273 for (unsigned k = 0; k < nval; k++)
274 {
275 if (bound_cons[k])
276 {
277 this->node_pt(jnod)->pin(k);
278 }
279 }
280
281 // Solid node? If so, deal with the positional boundary
282 // conditions:
284 dynamic_cast<SolidNode*>(this->node_pt(jnod));
285 if (solid_node_pt != 0)
286 {
287 // Get the positional boundary conditions from the father:
288 unsigned n_dim = this->node_pt(jnod)->ndim();
292#ifdef PARANOID
293 if (son_solid_el_pt == 0)
294 {
295 std::string error_message =
296 "We have a SolidNode outside a refineable SolidElement\n";
297 error_message +=
298 "during mesh refinement -- this doesn't make sense\n";
299
300 throw OomphLibError(error_message,
303 }
304#endif
305 son_solid_el_pt->get_solid_bcs(boundary, solid_bound_cons);
306
307 // Loop over the positions and pin, if necessary
308 for (unsigned k = 0; k < n_dim; k++)
309 {
310 if (solid_bound_cons[k])
311 {
312 solid_node_pt->pin_position(k);
313 }
314 }
315 }
316
317 // Next we update the boundary look-up schemes
318 // Loop over the boundaries stored in the set
319 for (std::set<unsigned>::iterator it = boundaries.begin();
320 it != boundaries.end();
321 ++it)
322 {
323 // Add the node to the boundary
324 mesh_pt->add_boundary_node(*it, this->node_pt(jnod));
325
326 // If we have set an intrinsic coordinate on this
327 // mesh boundary then it must also be interpolated on
328 // the new node
329 // Now interpolate the intrinsic boundary coordinate
330 if (mesh_pt->boundary_coordinate_exists(*it) == true)
331 {
333 son_el_pt->interpolated_zeta_on_edge(
334 *it, boundary, s_in_son, zeta);
335
336 this->node_pt(jnod)->set_coordinates_on_boundary(*it, zeta);
337 }
338 }
339 }
340 // Otherwise the node is not on a Mesh boundary
341 // and we create a normal "bulk" node
342 else
343 {
344 // Construct the new node
345 this->node_pt(jnod) =
346 this->construct_node(jnod, time_stepper_pt);
347 }
348
349 // Now we set the position and values at the newly created node
350
351 // In the first instance use macro element or FE representation
352 // to create past and present nodal positions.
353 // (THIS STEP SHOULD NOT BE SKIPPED FOR ALGEBRAIC
354 // ELEMENTS AS NOT ALL OF THEM NECESSARILY IMPLEMENT
355 // NONTRIVIAL NODE UPDATE FUNCTIONS. CALLING
356 // THE NODE UPDATE FOR SUCH ELEMENTS/NODES WILL LEAVE
357 // THEIR NODAL POSITIONS WHERE THEY WERE (THIS IS APPROPRIATE
358 // ONCE THEY HAVE BEEN GIVEN POSITIONS) BUT WILL
359 // NOT ASSIGN SENSIBLE INITIAL POSITONS!
360
361 // Loop over # of history values
362 // Loop over # of history values
363 for (unsigned t = 0; t < ntstorage; t++)
364 {
365 using namespace QuadTreeNames;
366 // Get the position from the son
368
369 // Now let's fill in the value
371 for (unsigned i = 0; i < 2; i++)
372 {
373 this->node_pt(jnod)->x(t, i) = x_prev[i];
374 }
375 }
376
377 // Now set up the values
378 // Loop over all history values
379 for (unsigned t = 0; t < ntstorage; t++)
380 {
381 // Get values from father element
382 // Note: get_interpolated_values() sets Vector size itself.
384 son_el_pt->get_interpolated_values(t, s_in_son, prev_values);
385
386 // Initialise the values at the new node
387 for (unsigned k = 0; k < this->node_pt(jnod)->nvalue(); k++)
388 {
389 this->node_pt(jnod)->set_value(t, k, prev_values[k]);
390 }
391 }
392
393 // Add the node to the mesh
394 mesh_pt->add_node_pt(this->node_pt(jnod));
395
396 } // End of the case when we build the node ourselvesx
397
398 // Algebraic stuff here
399 // Check whether the element is an algebraic element
401 dynamic_cast<AlgebraicElementBase*>(this);
402
403 // If we do have an algebraic element
404 if (alg_el_pt != 0)
405 {
406 std::string error_message =
407 "Have not implemented rebuilding from sons for";
408 error_message += "Algebraic Spectral elements yet\n";
409
410 throw OomphLibError(
411 error_message,
412 "RefineableQSpectralElement::rebuild_from_sons()",
414 }
415 }
416 }
417 }
418 }
419
420
421 /// Overload the nodes built function
422 virtual bool nodes_built()
423 {
424 unsigned n_node = this->nnode();
425 for (unsigned n = 0; n < n_node; n++)
426 {
427 if (node_pt(n) == 0)
428 {
429 return false;
430 }
431 }
432 // If we get to here, OK
433 return true;
434 }
435 };
436
437} // namespace oomph
438
439#endif
static char t char * s
Definition cfortran.h:568
cstr elem_len * i
Definition cfortran.h:603
char t
Definition cfortran.h:568
Base class for algebraic elements.
double size() const
Calculate the size of the element (length, area, volume,...) in Eulerian computational coordinates....
Definition elements.cc:4320
void get_x(const Vector< double > &s, Vector< double > &x) const
Global coordinates as function of local coordinates. Either via FE representation or via macro-elemen...
Definition elements.h:1889
unsigned ndim() const
Access function to # of Eulerian coordinates.
A general mesh class.
Definition mesh.h:67
void add_boundary_node(const unsigned &b, Node *const &node_pt)
Add a (pointer to) a node to the b-th boundary.
Definition mesh.cc:243
void add_node_pt(Node *const &node_pt)
Add a (pointer to a) node to the mesh.
Definition mesh.h:619
bool boundary_coordinate_exists(const unsigned &i) const
Indicate whether the i-th boundary has an intrinsic coordinate.
Definition mesh.h:571
Nodes are derived from Data, but, in addition, have a definite (Eulerian) position in a space of a gi...
Definition nodes.h:906
An OomphLibError object which should be thrown when an run-time error is encountered....
RefineableElements are FiniteElements that may be subdivided into children to provide a better local ...
A class that is used to template the refineable Q elements by dimension. It's really nothing more tha...
Definition Qelements.h:2259
virtual ~RefineableQSpectralElement()
Broken assignment operator.
virtual bool nodes_built()
Overload the nodes built function.
void rebuild_from_sons(Mesh *&mesh_pt)
The only thing to add is rebuild from sons.
RefineableQSpectralElement(const RefineableQSpectralElement< 2 > &dummy)=delete
Broken copy constructor.
A class that is used to template the refineable Q spectral elements by dimension. It's really nothing...
A Class for nodes that deform elastically (i.e. position is an unknown in the problem)....
Definition nodes.h:1686
TAdvectionDiffusionReactionElement<NREAGENT,DIM,NNODE_1D> elements are isoparametric triangular DIM-d...
Base class for time-stepping schemes. Timestepper provides an approximation of the temporal derivativ...
unsigned ntstorage() const
Return the number of doubles required to represent history (one for steady)
static const int OMEGA
Default value for an unassigned neighbour.
Definition tree.h:262
DRAIG: Change all instances of (SPATIAL_DIM) to (DIM-1).