Nektar++
FieldUtils/Interpolator.cpp
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // File: Interpolator.cpp
4 //
5 // For more information, please see: http://www.nektar.info/
6 //
7 // The MIT License
8 //
9 // Copyright (c) 2016 Kilian Lackhove
10 // Copyright (c) 2006 Division of Applied Mathematics, Brown University (USA),
11 // Department of Aeronautics, Imperial College London (UK), and Scientific
12 // Computing and Imaging Institute, University of Utah (USA).
13 //
14 // Permission is hereby granted, free of charge, to any person obtaining a
15 // copy of this software and associated documentation files (the "Software"),
16 // to deal in the Software without restriction, including without limitation
17 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
18 // and/or sell copies of the Software, and to permit persons to whom the
19 // Software is furnished to do so, subject to the following conditions:
20 //
21 // The above copyright notice and this permission notice shall be included
22 // in all copies or substantial portions of the Software.
23 //
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
25 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
27 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
29 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
30 // DEALINGS IN THE SOFTWARE.
31 //
32 // Description: Interpolator
33 //
34 ////////////////////////////////////////////////////////////////////////////////
35 
37 #include <boost/geometry.hpp>
38 
39 using namespace std;
40 
41 namespace bg = boost::geometry;
42 namespace bgi = boost::geometry::index;
43 
44 namespace Nektar
45 {
46 namespace FieldUtils
47 {
48 
49 /**
50  * @brief Interpolate from one expansion to an other
51  *
52  * @param expInField input field
53  * @param expOutField output field
54  *
55  *
56  * In and output fields must have the same dimension and number of fields.
57  * Weights are currently not stored for later use.
58  * The interpolation is performed by evaluating the expInField at the quadrature
59  * points of expOutField, so only eNoMethod is supported.
60  * If both expansions use the same mesh, use LibUtilities/Foundations/Interp.h
61  * instead.
62  */
63 template <typename T>
64 void Interpolator<T>::Interpolate(const T expInField, T &expOutField,
65  NekDouble def_value)
66 {
67  ASSERTL0(expInField.size() == expOutField.size(),
68  "number of fields does not match");
69  ASSERTL0(expInField[0]->GetCoordim(0) <= GetDim(),
70  "too many dimensions in inField");
71  ASSERTL0(expOutField[0]->GetCoordim(0) <= GetDim(),
72  "too many dimensions in outField");
73  ASSERTL0(GetInterpMethod() == LibUtilities::eNoMethod,
74  "only direct evaluation supported for this interpolation");
75 
76  int nFields = max((int)expInField.size(), (int)expOutField.size());
77  int nOutPts = expOutField[0]->GetTotPoints();
78  int outNumHomDir = 0;
79  if (expOutField[0]->GetExpType() == MultiRegions::e3DH1D ||
80  expOutField[0]->GetExpType() == MultiRegions::e2DH1D)
81  {
82  outNumHomDir = 1;
83  }
84  else if (expOutField[0]->GetExpType() == MultiRegions::e3DH2D)
85  {
86  outNumHomDir = 2;
87  }
88  int outDim = expOutField[0]->GetCoordim(0) + outNumHomDir;
89 
90  // create intermediate Ptsfield that wraps the expOutField
92  for (int i = 0; i < outDim; ++i)
93  {
94  pts[i] = Array<OneD, NekDouble>(nOutPts);
95  }
96  if (outDim == 1)
97  {
98  expOutField[0]->GetCoords(pts[0]);
99  }
100  else if (outDim == 2)
101  {
102  expOutField[0]->GetCoords(pts[0], pts[1]);
103  }
104  else if (outDim == 3)
105  {
106  expOutField[0]->GetCoords(pts[0], pts[1], pts[2]);
107  }
108 
110  MemoryManager<LibUtilities::PtsField>::AllocateSharedPtr(outDim, pts);
111  for (int f = 0; f < expOutField.size(); ++f)
112  {
113  tmpPts->AddField(expOutField[f]->GetPhys(), "DefaultVar");
114  }
115  // interpolate m_ptsInField to this intermediate field
116  Interpolate(expInField, tmpPts, def_value);
117  // write the intermediate fields data into our expOutField
118  for (int i = 0; i < nFields; i++)
119  {
120  int ptsi = outDim + i;
121  for (int j = 0; j < nOutPts; ++j)
122  {
123  expOutField[i]->UpdatePhys()[j] = tmpPts->GetPointVal(ptsi, j);
124  }
125  }
126 }
127 
128 /**
129  * @brief Interpolate from an expansion to a pts field
130  *
131  * @param expInField input field
132  * @param ptsOutField output field
133  *
134  * In and output fields must have the same dimension and number of fields.
135  * Weights are currently not stored for later use.
136  * The interpolation is performed by evaluating the expInField at the points
137  * of ptsOutField, so only eNoMethod is supported.
138  */
139 template <typename T>
140 void Interpolator<T>::Interpolate(const T expInField,
141  LibUtilities::PtsFieldSharedPtr &ptsOutField,
142  NekDouble def_value)
143 {
144  ASSERTL0(expInField.size() == ptsOutField->GetNFields(),
145  "number of fields does not match");
146  ASSERTL0(expInField[0]->GetCoordim(0) <= GetDim(),
147  "too many dimesions in inField");
148  ASSERTL0(ptsOutField->GetDim() <= GetDim(),
149  "too many dimesions in outField");
150  ASSERTL0(ptsOutField->GetDim() >= expInField[0]->GetCoordim(0),
151  "too few dimesions in outField");
152  ASSERTL0(GetInterpMethod() == LibUtilities::eNoMethod,
153  "only direct evaluation supported for this interpolation");
154  ASSERTL0(expInField[0]->GetExpType() != MultiRegions::e3DH2D,
155  "interpolation from 3DH2D expansion unsupported");
156 
157  m_ptsOutField = ptsOutField;
158 
159  int nInDim = expInField[0]->GetCoordim(0);
160  int nOutPts = m_ptsOutField->GetNpoints();
161  int lastProg = 0;
162 
163  int elmtid = -1;
164  for (int i = 0; i < nOutPts; ++i)
165  {
166  Array<OneD, NekDouble> Lcoords(nInDim, 0.0);
167  Array<OneD, NekDouble> coords(3, 0.0);
168  for (int j = 0; j < m_ptsOutField->GetDim(); ++j)
169  {
170  coords[j] = m_ptsOutField->GetPointVal(j, i);
171  }
172 
173  // Obtain Element and LocalCoordinate to interpolate.
174  elmtid = expInField[0]->GetExpIndex(
175  coords, Lcoords, NekConstants::kGeomFactorsTol, true, elmtid,
177 
178  // we use kGeomFactorsTol as tolerance, while StdPhysEvaluate has
179  // kNekZeroTol hardcoded, so we need to limit Lcoords to not produce
180  // a ton of warnings
181  for (int j = 0; j < nInDim; ++j)
182  {
183  Lcoords[j] = std::max(Lcoords[j], -1.0);
184  Lcoords[j] = std::min(Lcoords[j], 1.0);
185  }
186 
187  if (elmtid >= 0)
188  {
189  int offset = expInField[0]->GetPhys_Offset(elmtid);
190 
191  for (int f = 0; f < expInField.size(); ++f)
192  {
193  NekDouble value;
194 
195  if (expInField[f]->GetExpType() == MultiRegions::e3DH1D ||
196  expInField[f]->GetExpType() == MultiRegions::e2DH1D)
197  {
198  ASSERTL0(expInField[f]->GetWaveSpace(),
199  "interpolation from 3DH1D/2DH1D requires field in "
200  "wavespace");
201  NekDouble lHom = expInField[f]->GetHomoLen();
202  NekDouble BetaT =
203  2. * M_PI * fmod(coords[nInDim], lHom) / lHom;
204  int nPlanes =
205  expInField[f]->GetHomogeneousBasis()->GetZ().size();
206  NekDouble coeff = 0.;
208  expInField[f]->GetZIDs();
209  value = 0.;
210 
211  for (size_t n = 0; n < planes.size(); ++n)
212  {
213  auto planeExp = expInField[f]->GetPlane(planes[n]);
214  coeff = planeExp->GetExp(elmtid)->StdPhysEvaluate(
215  Lcoords, planeExp->GetPhys() + offset);
216 
217  if (planes[n] == 0)
218  {
219  value += coeff;
220  }
221  else if (planes[n] == 1)
222  {
223  value += cos(0.5 * nPlanes * BetaT) * coeff;
224  }
225  else if (planes[n] % 2 == 0)
226  {
227  NekDouble phase = (planes[n] >> 1) * BetaT;
228  value += cos(phase) * coeff;
229  }
230  else
231  {
232  NekDouble phase = (planes[n] >> 1) * BetaT;
233  value += -sin(phase) * coeff;
234  }
235  }
236  }
237  else
238  {
239  value = expInField[f]->GetExp(elmtid)->StdPhysEvaluate(
240  Lcoords, expInField[f]->GetPhys() + offset);
241  }
242 
243  if ((boost::math::isnan)(value))
244  {
245  ASSERTL0(false, "new value is not a number");
246  }
247  else
248  {
249  m_ptsOutField->SetPointVal(m_ptsOutField->GetDim() + f, i,
250  value);
251  }
252  }
253  }
254  else
255  {
256  for (int f = 0; f < expInField.size(); ++f)
257  {
258  m_ptsOutField->SetPointVal(m_ptsOutField->GetDim() + f, i,
259  def_value);
260  }
261  }
262 
263  int progress = int(100 * i / nOutPts);
264  if (m_progressCallback && progress > lastProg)
265  {
266  m_progressCallback(i, nOutPts);
267  lastProg = progress;
268  }
269  }
270 }
271 
272 /**
273  * @brief Interpolate from a pts field to an expansion
274  *
275  * @param ptsInField input field
276  * @param expOutField output field
277  *
278  * In and output fields must have the same dimension and number of fields.
279  */
280 template <typename T>
282  const LibUtilities::PtsFieldSharedPtr ptsInField, T &expOutField)
283 {
284  ASSERTL0(expOutField.size() == ptsInField->GetNFields(),
285  "number of fields does not match");
286  ASSERTL0(ptsInField->GetDim() <= GetDim(), "too many dimesions in inField");
287  ASSERTL0(expOutField[0]->GetCoordim(0) <= GetDim(),
288  "too many dimesions in outField");
289 
290  m_ptsInField = ptsInField;
291 
292  int nFields = max((int)ptsInField->GetNFields(), (int)expOutField.size());
293  int nOutPts = expOutField[0]->GetTotPoints();
294  int outNumHomDir = 0;
295  if (expOutField[0]->GetExpType() == MultiRegions::e3DH1D ||
296  expOutField[0]->GetExpType() == MultiRegions::e2DH1D)
297  {
298  outNumHomDir = 1;
299  }
300  else if (expOutField[0]->GetExpType() == MultiRegions::e3DH2D)
301  {
302  outNumHomDir = 2;
303  }
304  int outDim = expOutField[0]->GetCoordim(0) + outNumHomDir;
305 
306  // create intermediate Ptsfield that wraps the expOutField
308  for (int i = 0; i < outDim; ++i)
309  {
310  pts[i] = Array<OneD, NekDouble>(nOutPts);
311  }
312  if (outDim == 1)
313  {
314  expOutField[0]->GetCoords(pts[0]);
315  }
316  else if (outDim == 2)
317  {
318  expOutField[0]->GetCoords(pts[0], pts[1]);
319  }
320  else if (outDim == 3)
321  {
322  expOutField[0]->GetCoords(pts[0], pts[1], pts[2]);
323  }
324 
327 
328  for (int f = 0; f < expOutField.size(); ++f)
329  {
330  tmpPts->AddField(expOutField[f]->GetPhys(),
331  m_ptsInField->GetFieldName(f));
332  }
333  // interpolate m_ptsInField to this intermediate field
334  LibUtilities::Interpolator::Interpolate(m_ptsInField, tmpPts);
335  // write the intermediate fields data into our expOutField
336  for (int i = 0; i < nFields; i++)
337  {
338  int ptsi = outDim + i;
339  for (int j = 0; j < nOutPts; ++j)
340  {
341  expOutField[i]->UpdatePhys()[j] = tmpPts->GetPointVal(ptsi, j);
342  }
343  }
344 }
345 
346 template <typename T>
347 void Interpolator<T>::InterpExp1ToExp2(const T exp1, T &exp2)
348 {
349 
350  // Interpolation from exp1 -> exp2 assuming that exp1 and exp2 are the
351  // same explists, but at potentially different polynomial orders.
352  if (exp1.size() != exp2.size())
353  {
354  NEKERROR(ErrorUtil::efatal, "not the same mesh")
355  }
356 
357  for (int n = 0; n < exp1.size(); ++n)
358  {
359  // Interpolation from exp1 -> exp2 assuming that exp1 and exp2 are the
360  // same explists, but at potentially different polynomial orders.
361  if (exp1[n]->GetExpSize() != exp2[n]->GetExpSize())
362  {
363  NEKERROR(ErrorUtil::efatal, "not the same mesh")
364  }
365 
366  // If same polynomial orders, simply copy solution
367  if (exp1[n]->GetTotPoints() == exp2[n]->GetTotPoints())
368  {
369  Vmath::Vcopy(exp1[n]->GetTotPoints(), exp1[n]->GetPhys(), 1,
370  exp2[n]->UpdatePhys(), 1);
371  }
372  // If different polynomial orders, interpolate solution
373  else
374  {
375  // Transform solution from physical to coefficient space
376  exp1[n]->FwdTransLocalElmt(exp1[n]->GetPhys(),
377  exp1[n]->UpdateCoeffs());
378 
379  for (int i = 0; i < exp1[n]->GetExpSize(); ++i)
380  {
381  // Get the elements
382  LocalRegions::ExpansionSharedPtr elmt1 = exp1[n]->GetExp(i),
383  elmt2 = exp2[n]->GetExp(i);
384 
385  // Get the offset of elements in the storage arrays.
386  int offset1 = exp1[n]->GetCoeff_Offset(i);
387  int offset2 = exp2[n]->GetCoeff_Offset(i);
388 
389  // Get number of modes
391  elmt1->GetBase();
392  std::vector<unsigned int> nummodes1(base1.size());
393  std::vector<LibUtilities::BasisType> btype1(base1.size());
394  for (int j = 0; j < nummodes1.size(); ++j)
395  {
396  nummodes1[j] = base1[j]->GetNumModes();
397  btype1[j] = base1[j]->GetBasisType();
398  }
399 
400  // Extract data from exp1 -> exp2.
401  elmt2->ExtractDataToCoeffs(
402  &exp1[n]->GetCoeffs()[offset1], nummodes1, 0,
403  &exp2[n]->UpdateCoeffs()[offset2], btype1);
404  }
405 
406  // Transform solution back to physical space
407  exp2[n]->BwdTrans(exp2[n]->GetCoeffs(), exp2[n]->UpdatePhys());
408  }
409  }
410 }
411 
412 template <typename T>
414  const LibUtilities::PtsFieldSharedPtr ptsInField,
415  LibUtilities::PtsFieldSharedPtr &ptsOutField)
416 {
417  LibUtilities::Interpolator::Interpolate(ptsInField, ptsOutField);
418 }
419 
422 } // namespace FieldUtils
423 } // namespace Nektar
#define ASSERTL0(condition, msg)
Definition: ErrorUtil.hpp:215
#define NEKERROR(type, msg)
Assert Level 0 – Fundamental assert which is used whether in FULLDEBUG, DEBUG or OPT compilation mode...
Definition: ErrorUtil.hpp:209
A class that contains algorithms for interpolation between pts fields, expansions and different meshe...
General purpose memory allocation routines with the ability to allocate from thread specific memory p...
std::shared_ptr< PtsField > PtsFieldSharedPtr
Definition: PtsField.h:190
std::shared_ptr< Expansion > ExpansionSharedPtr
Definition: Expansion.h:68
static const NekDouble kGeomFactorsTol
The above copyright notice and this permission notice shall be included.
Definition: CoupledSolver.h:2
double NekDouble
void Vcopy(int n, const T *x, const int incx, T *y, const int incy)
Definition: Vmath.cpp:1255