MFEM v4.7.0
Finite element discretization library
Loading...
Searching...
No Matches
spacing.cpp
Go to the documentation of this file.
1// Copyright (c) 2010-2024, Lawrence Livermore National Security, LLC. Produced
2// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
3// LICENSE and NOTICE for details. LLNL-CODE-806117.
4//
5// This file is part of the MFEM library. For more information and source code
6// availability visit https://mfem.org.
7//
8// MFEM is free software; you can redistribute it and/or modify it under the
9// terms of the BSD-3 license. We welcome feedback and contributions, see file
10// CONTRIBUTING.md for details.
11
12#include "spacing.hpp"
13
14namespace mfem
15{
16
17std::unique_ptr<SpacingFunction> GetSpacingFunction(const SpacingType
18 spacingType,
19 Array<int> const& ipar,
20 Vector const& dpar)
21{
22 Array<int> iparsub, relN;
23
24 switch (spacingType)
25 {
27 MFEM_VERIFY(ipar.Size() == 1 &&
28 dpar.Size() == 0, "Invalid spacing function parameters");
29 return std::unique_ptr<SpacingFunction>(
30 new UniformSpacingFunction(ipar[0]));
32 MFEM_VERIFY(ipar.Size() == 3 &&
33 dpar.Size() == 1, "Invalid spacing function parameters");
34 return std::unique_ptr<SpacingFunction>(
35 new LinearSpacingFunction(ipar[0], (bool) ipar[1], dpar[0],
36 (bool) ipar[2]));
38 MFEM_VERIFY(ipar.Size() == 3 &&
39 dpar.Size() == 1, "Invalid spacing function parameters");
40 return std::unique_ptr<SpacingFunction>(
41 new GeometricSpacingFunction(ipar[0], (bool) ipar[1],
42 dpar[0], (bool) ipar[2]));
44 MFEM_VERIFY(ipar.Size() == 3 &&
45 dpar.Size() == 2, "Invalid spacing function parameters");
46 return std::unique_ptr<SpacingFunction>(
47 new BellSpacingFunction(ipar[0], (bool) ipar[1], dpar[0],
48 dpar[1], (bool) ipar[2]));
50 MFEM_VERIFY(ipar.Size() == 3 &&
51 dpar.Size() == 2, "Invalid spacing function parameters");
52 return std::unique_ptr<SpacingFunction>(
53 new GaussianSpacingFunction(ipar[0], (bool) ipar[1], dpar[0],
54 dpar[1], (bool) ipar[2]));
56 MFEM_VERIFY(ipar.Size() == 3 &&
57 dpar.Size() == 1, "Invalid spacing function parameters");
58 return std::unique_ptr<SpacingFunction>(
59 new LogarithmicSpacingFunction(ipar[0], (bool) ipar[1],
60 (bool) ipar[2], dpar[0]));
62 MFEM_VERIFY(ipar.Size() >= 3, "Invalid spacing function parameters");
63 ipar.GetSubArray(3, ipar[1], relN);
64 ipar.GetSubArray(3 + ipar[1], ipar.Size() - 3 - ipar[1], iparsub);
65 return std::unique_ptr<SpacingFunction>(
66 new PiecewiseSpacingFunction(ipar[0], ipar[1],
67 (bool) ipar[2], relN, iparsub,
68 dpar));
69 default:
70 MFEM_ABORT("Unknown spacing type \"" << int(spacingType) << "\"");
71 break;
72 }
73
74 MFEM_ABORT("Unknown spacing type");
75 return std::unique_ptr<SpacingFunction>(nullptr);
76}
77
78std::unique_ptr<SpacingFunction> SpacingFunction::Clone() const
79{
80 MFEM_ABORT("Base class SpacingFunction should not be cloned");
81 return std::unique_ptr<SpacingFunction>(nullptr);
82}
83
84void GeometricSpacingFunction::CalculateSpacing()
85{
86 // GeometricSpacingFunction requires more than 1 interval. If only 1
87 // interval is requested, just use uniform spacing.
88 if (n == 1) { return; }
89
90 // Find the root of g(r) = s * (r^n - 1) - r + 1 by Newton's method.
91
92 constexpr real_t convTol = 1.0e-8;
93 constexpr int maxIter = 20;
94
95 const real_t s_unif = 1.0 / ((real_t) n);
96
97 r = s < s_unif ? 1.5 : 0.5; // Initial guess
98
99 bool converged = false;
100 for (int iter=0; iter<maxIter; ++iter)
101 {
102 const real_t g = (s * (std::pow(r,n) - 1.0)) - r + 1.0;
103 const real_t dg = (n * s * std::pow(r,n-1)) - 1.0;
104 r -= g / dg;
105
106 if (std::abs(g / dg) < convTol)
107 {
108 converged = true;
109 break;
110 }
111 }
112
113 MFEM_VERIFY(converged, "Convergence failure in GeometricSpacingFunction");
114}
115
116void BellSpacingFunction::CalculateSpacing()
117{
118 s.SetSize(n);
119
120 // Bell spacing requires at least 3 intervals. If fewer than 3 are
121 // requested, we simply use uniform spacing.
122 if (n < 3)
123 {
124 s = 1.0 / ((real_t) n);
125 return;
126 }
127
128 MFEM_VERIFY(s0 + s1 < 1.0, "Sum of first and last Bell spacings must be"
129 << " less than 1");
130
131 s[0] = s0;
132 s[n-1] = s1;
133
134 // If there are only 3 intervals, the calculation is linear and trivial.
135 if (n == 3)
136 {
137 s[1] = 1.0 - s0 - s1;
138 return;
139 }
140
141 // For more than 3 intervals, solve a system iteratively.
142 real_t urk = 1.0;
143
144 // Initialize unknown entries of s.
145 real_t initialGuess = (1.0 - s0 - s1) / ((real_t) (n - 2));
146 for (int i=1; i<n-1; ++i)
147 {
148 s[i] = initialGuess;
149 }
150
151 Vector wk(7);
152 wk = 0.0;
153
154 Vector s_new(n);
155
156 Vector a(n+2);
157 Vector b(n+2);
158 Vector alpha(n+2);
159 Vector beta(n+2);
160 Vector gamma(n+2);
161
162 a = 0.5;
163 a[0] = 0.0;
164 a[1] = 0.0;
165
166 b = a;
167
168 alpha = 0.0;
169 beta = 0.0;
170 gamma = 0.0;
171
172 gamma[1] = s0;
173
174 constexpr int maxIter = 100;
175 constexpr real_t convTol = 1.0e-10;
176 bool converged = false;
177 for (int iter=0; iter<maxIter; ++iter)
178 {
179 int j;
180 for (j = 1; j <= n - 3; j++)
181 {
182 wk[0] = (s[j] + s[j+1]) * (s[j] + s[j+1]);
183 wk[1] = s[j-1];
184 wk[2] = (s[j-1] + s[j]) * (s[j-1] + s[j]) * (s[j-1] + s[j]);
185 wk[3] = s[j + 2];
186 wk[4] = (s[j+2] + s[j+1]) * (s[j+2] + s[j+1]) * (s[j+2] + s[j+1]);
187 wk[5] = wk[0] * wk[1] / wk[2];
188 wk[6] = wk[0] * wk[3] / wk[4];
189 a[j+1] = a[j+1] + urk*(wk[5] - a[j+1]);
190 b[j+1] = b[j+1] + urk*(wk[6] - b[j+1]);
191 }
192
193 for (j = 2; j <= n - 2; j++)
194 {
195 wk[0] = a[j]*(1.0 - 2.0*alpha[j - 1] + alpha[j - 1]*alpha[j - 2]
196 + beta[j - 2]) + b[j] + 2.0 - alpha[j - 1];
197 wk[1] = 1.0 / wk[0];
198 alpha[j] = wk[1]*(a[j]*beta[j - 1]*(2.0 - alpha[j - 2]) +
199 2.0*b[j] + beta[j - 1] + 1.0);
200 beta[j] = -b[j]*wk[1];
201 gamma[j] = wk[1]*(a[j]*(2.0*gamma[j - 1] - gamma[j - 2] -
202 alpha[j - 2]*gamma[j - 1]) + gamma[j - 1]);
203 }
204
205 s_new[0] = s[0];
206 for (j=1; j<n; ++j)
207 {
208 s_new[j] = s_new[j-1] + s[j];
209 }
210
211 for (j = n - 3; j >= 1; j--)
212 {
213 s_new[j] = alpha[j+1]*s_new[j + 1] +
214 beta[j+1]*s_new[j + 2] + gamma[j+1];
215 }
216
217 // Convert back from points to spacings
218 for (j=n-1; j>0; --j)
219 {
220 s_new[j] = s_new[j] - s_new[j-1];
221 }
222
223 wk[5] = wk[6] = 0.0;
224 for (j = n - 2; j >= 2; j--)
225 {
226 wk[5] = wk[5] + s_new[j]*s_new[j];
227 wk[6] = wk[6] + pow(s_new[j] - s[j], 2);
228 }
229
230 s = s_new;
231
232 const real_t res = sqrt(wk[6] / wk[5]);
233 if (res < convTol)
234 {
235 converged = true;
236 break;
237 }
238 }
239
240 MFEM_VERIFY(converged, "Convergence failure in BellSpacingFunction");
241}
242
243void GaussianSpacingFunction::CalculateSpacing()
244{
245 s.SetSize(n);
246 // Gaussian spacing requires at least 3 intervals. If fewer than 3 are
247 // requested, we simply use uniform spacing.
248 if (n < 3)
249 {
250 s = 1.0 / ((real_t) n);
251 return;
252 }
253
254 s[0] = s0;
255 s[n-1] = s1;
256
257 // If there are only 3 intervals, the calculation is linear and trivial.
258 if (n == 3)
259 {
260 s[1] = 1.0 - s0 - s1;
261 return;
262 }
263
264 // For more than 3 intervals, solve a system iteratively.
265
266 const real_t lnz01 = log(s0 / s1);
267
268 const real_t h = 1.0 / ((real_t) n-1);
269
270 // Determine concavity by first determining linear spacing and comparing
271 // the total spacing to 1.
272 // Linear formula: z_i = z0 + (i*h) * (z1-z0), 0 <= i <= n-1
273 // \sum_{i=0}^{nzones-1} z_i = n * z0 + h * (z1-z0) * nz * (nz-1) / 2
274
275 const real_t slinear = n * (s0 + (h * (s1 - s0) * 0.5 * (n-1)));
276
277 MFEM_VERIFY(std::abs(slinear - 1.0) > 1.0e-8, "Bell distribution is too "
278 << "close to linear.");
279
280 const real_t u = slinear < 1.0 ? 1.0 : -1.0;
281
282 real_t c = 0.3; // Initial guess
283
284 // Newton iterations
285 constexpr int maxIter = 10;
286 constexpr real_t convTol = 1.0e-8;
287 bool converged = false;
288 for (int iter=0; iter<maxIter; ++iter)
289 {
290 const real_t c2 = c * c;
291
292 const real_t m = 0.5 * (1.0 - (u * c2 * lnz01));
293 const real_t dmdc = -u * c * lnz01;
294
295 real_t r = 0.0; // Residual
296 real_t drdc = 0.0; // Derivative of residual
297
298 for (int i=0; i<n; ++i)
299 {
300 const real_t x = i * h;
301 const real_t ti = exp((-(x * x) + (2.0 * x * m)) * u / c2); // Gaussian
302 r += ti;
303
304 // Derivative of Gaussian
305 drdc += ((-2.0 * (-(x * x) + (2.0 * x * m)) / (c2 * c)) +
306 ((2.0 * x * dmdc) / c2)) * ti;
307 }
308
309 r *= s0;
310 r -= 1.0; // Sum of spacings should equal 1.
311
312 if (std::abs(r) < convTol)
313 {
314 converged = true;
315 break;
316 }
317
318 drdc *= s0 * u;
319
320 // Newton update is -r / drdc, limited by factors of 1/2 and 2.
321 real_t dc = std::max(-r / drdc, (real_t) -0.5*c);
322 dc = std::min(dc, (real_t) 2.0*c);
323
324 c += dc;
325 }
326
327 MFEM_VERIFY(converged, "Convergence failure in GaussianSpacingFunction");
328
329 const real_t c2 = c * c;
330 const real_t m = 0.5 * (1.0 - (u * c2 * lnz01));
331 const real_t q = s0 * exp(u*m*m / c2);
332
333 for (int i=0; i<n; ++i)
334 {
335 const real_t x = (i * h) - m;
336 s[i] = q * exp(-u*x*x / c2);
337 }
338}
339
340void LogarithmicSpacingFunction::CalculateSpacing()
341{
342 MFEM_VERIFY(n > 0 && logBase > 1.0,
343 "Invalid parameters in LogarithmicSpacingFunction");
344
345 if (sym) { CalculateSymmetric(); }
346 else { CalculateNonsymmetric(); }
347}
348
349void LogarithmicSpacingFunction::CalculateSymmetric()
350{
351 s.SetSize(n);
352
353 const bool odd = (n % 2 == 1);
354
355 const int M0 = n / 2;
356 const int M = odd ? (M0 + 1) : M0;
357
358 const real_t h = 1.0 / ((real_t) M);
359
360 real_t p = 1.0; // Initialize at right endpoint of [0,1].
361
362 for (int i=M-2; i>=0; --i)
363 {
364 const real_t p_i = (pow(logBase, (i+1)*h) - 1.0) / (logBase - 1.0);
365 s[i+1] = p - p_i;
366 p = p_i;
367 }
368
369 s[0] = p;
370
371 // Even case for spacing: [s[0], ..., s[M-1], s[M-1], s[M-2], ..., s[0]]
372 // covers interval [0,2]
373 // Odd case for spacing: [s[0], ..., s[M-1], s[M-2], ..., s[0]]
374 // covers interval [0,2-s[M-1]]
375
376 const real_t t = odd ? 1.0 / (2.0 - s[M-1]) : 0.5;
377
378 for (int i=0; i<M; ++i)
379 {
380 s[i] *= t;
381
382 if (i < (M-1) || !odd)
383 {
384 s[n - i - 1] = s[i];
385 }
386 }
387}
388
389void LogarithmicSpacingFunction::CalculateNonsymmetric()
390{
391 s.SetSize(n);
392
393 const real_t h = 1.0 / ((real_t) n);
394
395 real_t p = 1.0; // Initialize at right endpoint of [0,1].
396
397 for (int i=n-2; i>=0; --i)
398 {
399 const real_t p_i = (pow(logBase, (i+1)*h) - 1.0) / (logBase - 1.0);
400 s[i+1] = p - p_i;
401 p = p_i;
402 }
403
404 s[0] = p;
405}
406
408 Vector const& dpar)
409{
410 MFEM_VERIFY(partition.Size() == np - 1, "");
411 bool validPartition = true;
412
413 // Verify that partition has ascending numbers in (0,1).
414 for (int i=0; i<np-1; ++i)
415 {
416 partition[i] = dpar[i];
417
418 if (partition[i] <= 0.0 || partition[i] >= 1.0)
419 {
420 validPartition = false;
421 }
422
423 if (i > 0 && partition[i] <= partition[i-1])
424 {
425 validPartition = false;
426 }
427 }
428
429 MFEM_VERIFY(validPartition, "");
430
431 pieces.resize(np);
432
433 Array<int> ipar_p;
434 Vector dpar_p;
435
436 int osi = 0;
437 int osd = np - 1;
438 int n_total = 0;
439 for (int p=0; p<np; ++p)
440 {
441 // Setup piece p
442 const SpacingType type = (SpacingType) ipar[osi];
443 const int numIntParam = ipar[osi+1];
444 const int numDoubleParam = ipar[osi+2];
445
446 ipar_p.SetSize(numIntParam);
447 dpar_p.SetSize(numDoubleParam);
448
449 for (int i=0; i<numIntParam; ++i)
450 {
451 ipar_p[i] = ipar[osi + 3 + i];
452 }
453
454 for (int i=0; i<numDoubleParam; ++i)
455 {
456 dpar_p[i] = dpar[osd + i];
457 }
458
459 pieces[p] = GetSpacingFunction(type, ipar_p, dpar_p);
460
461 osi += 3 + numIntParam;
462 osd += numDoubleParam;
463 n_total += npartition[p];
464
465 MFEM_VERIFY(pieces[p]->Size() >= 1, "");
466 }
467
468 MFEM_VERIFY(osi == ipar.Size() && osd == dpar.Size(), "");
469 n0 = n_total;
470}
471
473{
474 for (auto& p : pieces) { p->ScaleParameters(a); }
475}
476
477void PiecewiseSpacingFunction::Print(std::ostream &os) const
478{
479 // SpacingType numIntParam numDoubleParam npartition {int params} {double params}
480 int inum = 3 + np;
481 int dnum = np-1;
482 for (auto& p : pieces)
483 {
484 // Add three for the type and the integer and double parameter counts.
485 inum += p->NumIntParameters() + 3;
486 dnum += p->NumDoubleParameters();
487 }
488
489 os << int(SpacingType::PIECEWISE) << " " << inum << " " << dnum << " "
490 << n << " " << np << " " << (int) reverse << "\n";
491
492 for (auto n : npartition)
493 {
494 os << n << " ";
495 }
496
497 // Write integer parameters for all pieces.
498 Array<int> ipar;
499 for (auto& p : pieces)
500 {
501 os << "\n" << int(p->GetSpacingType()) << " " << p->NumIntParameters()
502 << " " << p->NumDoubleParameters();
503
504 p->GetIntParameters(ipar);
505
506 for (auto& ip : ipar)
507 {
508 os << " " << ip;
509 }
510 }
511
512 os << "\n";
513 for (auto p : partition)
514 {
515 os << p << " ";
516 }
517
518 // Write double parameters for all pieces.
519 Vector dpar;
520 for (auto& p : pieces)
521 {
522 p->GetDoubleParameters(dpar);
523
524 if (dpar.Size() > 0)
525 {
526 os << "\n";
527 for (auto dp : dpar)
528 {
529 os << dp << " ";
530 }
531 }
532 }
533
534 os << "\n";
535}
536
537void PiecewiseSpacingFunction::CalculateSpacing()
538{
539 MFEM_VERIFY(n >= 1 && (n % n0 == 0 || n < n0), "");
540 const int ref = n / n0; // Refinement factor
541 const int cf = n0 / n; // Coarsening factor
542
543 s.SetSize(n);
544
545 bool coarsen = cf > 1 && n > 1;
546 // If coarsening, check whether all pieces have size divisible by cf.
547 if (coarsen)
548 {
549 for (int p=0; p<np; ++p)
550 {
551 const int csize = pieces[p]->Size() / cf;
552 if (pieces[p]->Size() != cf * csize)
553 {
554 coarsen = false;
555 }
556 }
557 }
558
559 if (n == 1)
560 {
561 s[0] = 1.0;
562 for (auto& p : pieces) { p->SetSize(1); }
563 return;
564 }
565
566 MFEM_VERIFY(coarsen || n >= n0,
567 "Invalid case in PiecewiseSpacingFunction::CalculateSpacing");
568
569 int n_total = 0;
570 for (int p=0; p<np; ++p)
571 {
572 // Calculate spacing for piece p.
573
574 if (coarsen)
575 {
576 pieces[p]->SetSize(npartition[p] / cf);
577 }
578 else
579 {
580 pieces[p]->SetSize(ref * npartition[p]);
581 }
582
583 const real_t p0 = (p == 0) ? 0.0 : partition[p-1];
584 const real_t p1 = (p == np - 1) ? 1.0 : partition[p];
585 const real_t h_p = p1 - p0;
586
587 for (int i=0; i<pieces[p]->Size(); ++i)
588 {
589 s[n_total + i] = h_p * pieces[p]->Eval(i);
590 }
591
592 n_total += pieces[p]->Size();
593 }
594
595 MFEM_VERIFY(n_total == n, "");
596}
597
599{
600 for (auto& p : pieces)
601 {
602 if (!p->Nested())
603 {
604 return false;
605 }
606 }
607
608 return true;
609}
610
611}
void SetSize(int nsize)
Change the logical size of the array, keep existing entries.
Definition array.hpp:697
int Size() const
Return the logical size of the array.
Definition array.hpp:144
Bell spacing function, which produces spacing resembling a Bell curve.
Definition spacing.hpp:357
Gaussian spacing function of the general form g(x) = a exp(-(x-m)^2 / c^2) for some scalar parameters...
Definition spacing.hpp:448
Geometric spacing function.
Definition spacing.hpp:276
Linear spacing function, defining the width of interval i as s + i * d.
Definition spacing.hpp:180
Logarithmic spacing function, uniform in log base 10 by default.
Definition spacing.hpp:536
Piecewise spacing function, with spacing functions defining spacing within arbitarily many fixed subi...
Definition spacing.hpp:617
void ScaleParameters(real_t a) override
Scales parameters by the factor a associated with Size().
Definition spacing.cpp:472
void Print(std::ostream &os) const override
Prints all the data necessary to define the spacing function and its current state (size and other pa...
Definition spacing.cpp:477
bool Nested() const override
Returns true if the spacing function is nested during refinement.
Definition spacing.cpp:598
void SetupPieces(Array< int > const &ipar, Vector const &dpar)
Definition spacing.cpp:407
int Size() const
Returns the size, or number of intervals (elements).
Definition spacing.hpp:42
virtual std::unique_ptr< SpacingFunction > Clone() const
Returns a clone (deep-copy) of this spacing function.
Definition spacing.cpp:78
bool reverse
Whether to reverse the spacing.
Definition spacing.hpp:104
int n
Size, or number of intervals (elements)
Definition spacing.hpp:103
Uniform spacing function, dividing the unit interval into Size() equally spaced intervals (elements).
Definition spacing.hpp:113
Vector data type.
Definition vector.hpp:80
int Size() const
Returns the size of the vector.
Definition vector.hpp:218
void SetSize(int s)
Resize the vector to size s.
Definition vector.hpp:538
Vector beta
const real_t alpha
Definition ex15.cpp:369
real_t b
Definition lissajous.cpp:42
real_t a
Definition lissajous.cpp:41
real_t u(const Vector &xvec)
Definition lor_mms.hpp:22
std::unique_ptr< SpacingFunction > GetSpacingFunction(const SpacingType spacingType, Array< int > const &ipar, Vector const &dpar)
Returns a new SpacingFunction instance defined by the type and parameters.
Definition spacing.cpp:17
SpacingType
Definition spacing.hpp:23
float real_t
Definition config.hpp:43
real_t p(const Vector &x, real_t t)
RefCoord t[3]