VTK  9.7.1
vtkDGOperationEvaluator.h
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
2 // SPDX-License-Identifier: BSD-3-Clause
3 #ifndef vtkDGOperationEvaluator_h
4 #define vtkDGOperationEvaluator_h
5 
6 #include "vtkCompiler.h" // For export macro.
7 #include "vtkDGOperationState.h"
8 #include "vtkDGOperationStateEntry.h" // For API.
9 #include "vtkDoubleArray.h" // For API.
10 #include "vtkMatrix3x3.h" // For API.
11 
21 {
24 };
25 
28 {
31 };
32 
41 {
43  None,
45 };
46 
54 template <typename InputIterator, typename OutputIterator, vtkDGSharingType DOFSharing,
55  vtkDGSideType SourceType, vtkDGShapeModifier Modifier,
56  vtkDGSharingType ShapeSharing = Discontinuous>
58 {
59 public:
60  using InputIteratorType = InputIterator;
61  using OutputIteratorType = OutputIterator;
62 
64  // Attribute arrays/operation
65  vtkDGOperatorEntry& op, vtkDataArray* connectivity, vtkDataArray* values,
66  vtkDataArray* sideConn, vtkTypeUInt64 offset,
67  // Shape arrays/operation
68  vtkDGOperatorEntry shapeGradient = vtkDGOperatorEntry(),
69  vtkDataArray* shapeConnectivity = nullptr, vtkDataArray* shapeValues = nullptr)
71  op, connectivity, values, sideConn, offset, shapeGradient, shapeConnectivity, shapeValues)
72  {
73  if (!op)
74  {
75  throw std::logic_error("Must have non-null operator.");
76  }
77  if (Modifier != None && !shapeGradient)
78  {
79  throw std::logic_error("Must have non-null shape gradient operator.");
80  }
81  this->BasisTuple.resize(op.NumberOfFunctions * op.OperatorSize);
82  int ncc = 0;
83  if (this->CellConnectivity)
84  {
85  ncc = this->CellConnectivity->GetNumberOfComponents();
86  this->ConnTuple.resize(ncc);
87  }
88  else if (DOFSharing == SharedDOF)
89  {
90  throw std::logic_error("DOF sharing requires a cell-connectivity array.");
91  }
92  int nvc = this->CellValues->GetNumberOfComponents();
93  if (DOFSharing == SharedDOF)
94  {
95  this->NumberOfValuesPerFunction = nvc;
96  this->ValueTuple.resize(nvc * ncc);
97  }
98  else
99  {
100  this->NumberOfValuesPerFunction = nvc / this->OpEntry.NumberOfFunctions;
101  this->ValueTuple.resize(nvc);
102  }
103 
104  // If we must also evaluate the shape-attribute modifier for each
105  // result value, then prepare tuples to hold shape data.
106  if (Modifier != None)
107  {
108  this->Jacobian.resize(9); // TODO: Handle 2-d Jacobians differently eventually.
109  this->ShapeBasisTuple.resize(shapeGradient.NumberOfFunctions * shapeGradient.OperatorSize);
110  int nsc = 0;
111  if (this->ShapeConnectivity)
112  {
113  nsc = this->ShapeConnectivity->GetNumberOfComponents();
114  this->ShapeConnTuple.resize(nsc);
115  }
116  else if (ShapeSharing == SharedDOF)
117  {
118  throw std::logic_error("Shape DOF-sharing requires a shape-connectivity array.");
119  }
120  int nvs = this->ShapeValues->GetNumberOfComponents();
121  if (ShapeSharing == SharedDOF)
122  {
123  this->NumberOfShapeValuesPerFunction = nvs;
124  this->ShapeValueTuple.resize(nvs * nsc);
125  }
126  else
127  {
128  this->NumberOfShapeValuesPerFunction = nvs / this->ShapeGradientEntry.NumberOfFunctions;
129  this->ShapeValueTuple.resize(nvs);
130  }
131  }
132  }
133 
134  void CloneInto(vtkDGOperationStateEntryBase& entry) const override
135  {
136  if (auto* typedEntry =
138  {
139  vtkDGOperationEvaluator<InputIterator, OutputIterator, DOFSharing, SourceType, Modifier,
140  ShapeSharing>::prepEntry(*typedEntry, this->OpEntry, this->CellConnectivity,
141  this->CellValues, this->SideConnectivity, this->Offset, this->ShapeGradientEntry,
142  this->ShapeConnectivity, this->ShapeValues);
143  }
144  // TODO: else throw exception?
145  }
146 
149  void InnerProduct(vtkTypeUInt64 tt, OutputIterator& outIter) const
150  {
151  auto xx(outIter[tt]);
152  int nc = static_cast<int>(xx.size());
153  // Zero out the tuple:
154  for (int ii = 0; ii < nc; ++ii)
155  {
156  xx[ii] = 0.;
157  }
158  // Sum the inner product of the basis and the coefficient values into the tuple:
159  for (int ii = 0; ii < this->NumberOfValuesPerFunction; ++ii)
160  {
161  for (int jj = 0; jj < this->OpEntry.OperatorSize; ++jj)
162  {
163  for (int kk = 0; kk < this->OpEntry.NumberOfFunctions; ++kk)
164  {
165  xx[ii * this->OpEntry.OperatorSize + jj] +=
166  this->BasisTuple[kk * this->OpEntry.OperatorSize + jj] *
167  this->ValueTuple[kk * this->NumberOfValuesPerFunction + ii];
168  }
169  }
170  }
171  }
172 
175  void ShapeInnerProduct() const
176  {
177  constexpr int nc = 9; // TODO: Use cell dimension instead (9 for 3-d, 4 for 2-d)?
178  assert(nc == this->ShapeGradientEntry.OperatorSize * this->NumberOfShapeValuesPerFunction);
179  // Zero out the tuple:
180  for (int ii = 0; ii < nc; ++ii)
181  {
182  this->Jacobian[ii] = 0.;
183  }
184  for (int ii = 0; ii < this->NumberOfShapeValuesPerFunction; ++ii)
185  {
186  for (int jj = 0; jj < this->ShapeGradientEntry.OperatorSize; ++jj)
187  {
188  for (int kk = 0; kk < this->ShapeGradientEntry.NumberOfFunctions; ++kk)
189  {
190  this->Jacobian[jj + this->NumberOfShapeValuesPerFunction * ii] +=
191  this->ShapeBasisTuple[kk * this->ShapeGradientEntry.OperatorSize + jj] *
192  this->ShapeValueTuple[kk * this->NumberOfShapeValuesPerFunction + ii];
193  }
194  }
195  }
196  }
197 
199  void ComputeJacobian() const
200  {
201  if (ShapeSharing == SharedDOF)
202  {
203  if (this->LastShapeCellId != this->LastCellId)
204  {
205  this->ShapeConnectivity->GetUnsignedTuple(this->LastCellId, this->ShapeConnTuple.data());
206  std::size_t nc = this->ShapeConnTuple.size();
207  int nv = this->ShapeValues->GetNumberOfComponents();
208  for (std::size_t jj = 0; jj < nc; ++jj)
209  {
210  this->ShapeValues->GetTuple(
211  this->ShapeConnTuple[jj], this->ShapeValueTuple.data() + nv * jj);
212  }
213  this->LastShapeCellId = this->LastCellId;
214  }
215  }
216  else if (ShapeSharing == Discontinuous)
217  {
218  if (this->LastShapeCellId != this->LastCellId)
219  {
220  this->ShapeValues->GetTuple(this->LastCellId, this->ShapeValueTuple.data());
221  this->LastShapeCellId = this->LastCellId;
222  }
223  }
224  else
225  {
226  throw std::logic_error("Invalid shape DOF-sharing enumerant.");
227  }
228  this->ShapeGradientEntry.Op(this->RST, this->ShapeBasisTuple);
229  this->ShapeInnerProduct();
230  }
231 
232  // Compute the inverse Jacobian and multiply the \a ii-th tuple of result by it.
233  //
234  // This performs the multiplication in place.
235  void ApplyInverseJacobian(vtkTypeUInt64 ii, OutputIterator& outIter) const
236  {
237  this->ComputeJacobian();
238  // Invert Jacobian and multiply result's ii-th tuple by it.
239  std::array<double, 9> inverseJacobian;
240  // Transpose required; oddly, ApplyScaledJacobian and ApplyInverseJacobian
241  // cannot both use the same Jacobian matrix.
242  vtkMatrix3x3::Transpose(this->Jacobian.data(), this->Jacobian.data());
243  vtkMatrix3x3::Invert(this->Jacobian.data(), inverseJacobian.data());
244  auto rr(outIter[ii]);
245  const int nc = static_cast<int>(rr.size());
246  if (nc % 3 != 0)
247  {
248  throw std::logic_error("Jacobian must apply to vector or matrix values.");
249  }
250  for (int vv = 0; vv < nc / 3; ++vv)
251  {
252  vtkTypeUInt64 mm = 3 * vv;
253  double* vec = rr.data() + mm;
254  vtkMatrix3x3::MultiplyPoint(inverseJacobian.data(), vec, vec);
255  }
256  }
257 
258  // Compute the Jacobian scaled by its determinant; multiply the \a ii-th tuple of result by it.
259  //
260  // This performs the multiplication in place.
261  void ApplyScaledJacobian(vtkTypeUInt64 ii, OutputIterator& outIter) const
262  {
263  this->ComputeJacobian();
264  // Compute the Jacobian and multiply result's ii-th tuple
265  // by the Jacobian normalized by its determinant.
266  double norm = 1.0 / vtkMatrix3x3::Determinant(this->Jacobian.data());
267  std::array<double, 3> vec;
268  auto rr(outIter[ii]);
269  const int nc = static_cast<int>(rr.size());
270  if (nc % 3 != 0)
271  {
272  throw std::logic_error("Jacobian must apply to vector or matrix values.");
273  }
274  for (int vv = 0; vv < nc / 3; ++vv)
275  {
276  vtkTypeUInt64 mm = 3 * vv;
277  vec = { { rr[mm], rr[mm + 1], rr[mm + 2] } };
278  // TODO: Is this J^T * vec? or J*vec? Depends on row-major vs column-major Jacobian.
279  // clang-format off
280  rr[mm ] = norm * (this->Jacobian[0] * vec[0] + this->Jacobian[1] * vec[1] + this->Jacobian[2] * vec[2]);
281  rr[mm + 1] = norm * (this->Jacobian[3] * vec[0] + this->Jacobian[4] * vec[1] + this->Jacobian[5] * vec[2]);
282  rr[mm + 2] = norm * (this->Jacobian[6] * vec[0] + this->Jacobian[7] * vec[1] + this->Jacobian[8] * vec[2]);
283  // clang-format on
284  }
285  }
286 
288  InputIterator& inIter, OutputIterator& outIter, vtkTypeUInt64 begin, vtkTypeUInt64 end) const
289  {
290  vtkTypeUInt64 currId;
291  if (DOFSharing == SharedDOF && SourceType == Sides)
292  {
293  for (vtkTypeUInt64 ii = begin; ii != end; ++ii)
294  {
295  currId = inIter.GetCellId(ii);
296  this->SideConnectivity->GetUnsignedTuple(currId - this->Offset, this->SideTuple.data());
297  currId = this->SideTuple[0];
298  if (this->LastCellId != currId)
299  {
300  this->CellConnectivity->GetUnsignedTuple(currId, this->ConnTuple.data());
301  std::size_t nc = this->ConnTuple.size();
302  int nv = this->CellValues->GetNumberOfComponents();
303  for (std::size_t jj = 0; jj < nc; ++jj)
304  {
305  this->CellValues->GetTuple(this->ConnTuple[jj], this->ValueTuple.data() + nv * jj);
306  }
307  this->LastCellId = currId;
308  }
309  auto param = inIter.GetParameter(ii);
310  for (int jj = 0; jj < 3; ++jj)
311  {
312  this->RST[jj] = param[jj];
313  }
314  this->OpEntry.Op(this->RST, this->BasisTuple);
315  this->InnerProduct(ii, outIter);
316  if (Modifier == InverseJacobian)
317  {
318  this->ApplyInverseJacobian(ii, outIter);
319  }
320  else if (Modifier == ScaledJacobian)
321  {
322  this->ApplyScaledJacobian(ii, outIter);
323  }
324  }
325  }
326  else if (DOFSharing == SharedDOF && SourceType == Cells)
327  {
328  for (vtkTypeUInt64 ii = begin; ii != end; ++ii)
329  {
330  currId = inIter.GetCellId(ii);
331  if (this->LastCellId != currId)
332  {
333  // NB: We could ask for currId - this->Offset here, but perhaps we should
334  // assume this->Offset will always be 0 for the CellSpec?
335  this->CellConnectivity->GetUnsignedTuple(currId, this->ConnTuple.data());
336  std::size_t nc = this->ConnTuple.size();
337  int nv = this->CellValues->GetNumberOfComponents();
338  for (std::size_t jj = 0; jj < nc; ++jj)
339  {
340  this->CellValues->GetTuple(this->ConnTuple[jj], this->ValueTuple.data() + nv * jj);
341  }
342  this->LastCellId = currId;
343  }
344  auto param = inIter.GetParameter(ii);
345  for (int jj = 0; jj < 3; ++jj)
346  {
347  this->RST[jj] = param[jj];
348  }
349  this->OpEntry.Op(this->RST, this->BasisTuple);
350  this->InnerProduct(ii, outIter);
351  if (Modifier == InverseJacobian)
352  {
353  this->ApplyInverseJacobian(ii, outIter);
354  }
355  else if (Modifier == ScaledJacobian)
356  {
357  this->ApplyScaledJacobian(ii, outIter);
358  }
359  }
360  }
361  else if (DOFSharing == Discontinuous && SourceType == Sides)
362  {
363  for (vtkTypeUInt64 ii = begin; ii != end; ++ii)
364  {
365  currId = inIter.GetCellId(ii);
366  this->SideConnectivity->GetUnsignedTuple(currId - this->Offset, this->SideTuple.data());
367  currId = this->SideTuple[0];
368  if (this->LastCellId != currId)
369  {
370  this->CellValues->GetTuple(currId, this->ValueTuple.data());
371  this->LastCellId = currId;
372  }
373  auto param = inIter.GetParameter(ii);
374  for (int jj = 0; jj < 3; ++jj)
375  {
376  this->RST[jj] = param[jj];
377  }
378  this->OpEntry.Op(this->RST, this->BasisTuple);
379  this->InnerProduct(ii, outIter);
380  if (Modifier == InverseJacobian)
381  {
382  this->ApplyInverseJacobian(ii, outIter);
383  }
384  else if (Modifier == ScaledJacobian)
385  {
386  this->ApplyScaledJacobian(ii, outIter);
387  }
388  }
389  }
390  else // DOFSharing == Discontinuous && SourceType == Cells
391  {
392  for (vtkTypeUInt64 ii = begin; ii != end; ++ii)
393  {
394  currId = inIter.GetCellId(ii);
395  // NB: We could subtract this->Offset from currId, but assume for
396  // now that CellSpec always has an offset of 0.
397  if (this->LastCellId != currId)
398  {
399  this->CellValues->GetTuple(currId, this->ValueTuple.data());
400  this->LastCellId = currId;
401  }
402  auto param = inIter.GetParameter(ii);
403  for (int jj = 0; jj < 3; ++jj)
404  {
405  this->RST[jj] = param[jj];
406  }
407  this->OpEntry.Op(this->RST, this->BasisTuple);
408  this->InnerProduct(ii, outIter);
409  if (Modifier == InverseJacobian)
410  {
411  this->ApplyInverseJacobian(ii, outIter);
412  }
413  else if (Modifier == ScaledJacobian)
414  {
415  this->ApplyScaledJacobian(ii, outIter);
416  }
417  }
418  }
419  }
420 
428  vtkDGOperatorEntry op, vtkDataArray* conn, vtkDataArray* values, vtkDataArray* sides,
429  vtkTypeUInt64 offset, vtkDGOperatorEntry shapeGradient = vtkDGOperatorEntry(),
430  vtkDataArray* shapeConnectivity = nullptr, vtkDataArray* shapeValues = nullptr)
431  {
432  entry.State = std::unique_ptr<vtkDGOperationState>(new vtkDGOperationEvaluator<InputIterator,
433  OutputIterator, DOFSharing, SourceType, Modifier, ShapeSharing>(
434  op, conn, values, sides, offset, shapeGradient, shapeConnectivity, shapeValues));
435  entry.Function = [&entry](InputIterator& inIter, OutputIterator& outIter, vtkTypeUInt64 begin,
436  vtkTypeUInt64 end)
437  {
438  auto* eval = reinterpret_cast<vtkDGOperationEvaluator<InputIterator, OutputIterator,
439  DOFSharing, SourceType, Modifier, ShapeSharing>*>(entry.State.get());
440  return (*eval)(inIter, outIter, begin, end);
441  };
442  }
443 };
444 
445 #endif // vtkDGOperationEvaluator_h
446 // VTK-HeaderTest-Exclude: vtkDGOperationEvaluator.h
int NumberOfFunctions
The number of functions in the basis.
static void prepEntry(vtkDGOperationStateEntry< InputIterator, OutputIterator > &entry, vtkDGOperatorEntry op, vtkDataArray *conn, vtkDataArray *values, vtkDataArray *sides, vtkTypeUInt64 offset, vtkDGOperatorEntry shapeGradient=vtkDGOperatorEntry(), vtkDataArray *shapeConnectivity=nullptr, vtkDataArray *shapeValues=nullptr)
Prepare an entry for evaluating op with the given data arrays and class template parameters.
void operator()(InputIterator &inIter, OutputIterator &outIter, vtkTypeUInt64 begin, vtkTypeUInt64 end) const
vtkDGCellRangeEvaluator< InputIterator, OutputIterator > Function
double Determinant()
Compute the determinant of the matrix and return it.
Definition: vtkMatrix3x3.h:150
void ApplyScaledJacobian(vtkTypeUInt64 ii, OutputIterator &outIter) const
int OperatorSize
The number of coordinates each operator-function evaluates to.
Degrees of freedom are not shared.
This is a base class that exists so that vtkDGOperationState can provide a virtual CloneInto method t...
void ComputeJacobian() const
Compute the shape-attribute Jacobian matrix, storing it in this->Jacobian.
Degrees of freedom (DOF) are shared.
void Transpose()
Definition: vtkMatrix3x3.h:110
!< For HCURL
A side specified by a cell ID and an integer index into sides.
Encapsulate the state required to evaluate DG cell-attributes.
abstract superclass for arrays of numeric data
Definition: vtkDataArray.h:44
vtkDGSharingType
Whether degrees of freedom (DOF) are shared between cells.
void InnerProduct(vtkTypeUInt64 tt, OutputIterator &outIter) const
Compute the inner product of this->BasisTuple and this->ValueTuple, storing the result in the tt-th t...
Encapsulate the state required to evaluate DG cell-attributes.
A cell specified by degrees of freedom held in arrays.
Evaluate a vtkDGOperationEntry on a provided cell ID at provided parametric coordinates.
A record for a basis in a function space that is specific to one cell shape.
std::unique_ptr< vtkDGOperationState > State
void CloneInto(vtkDGOperationStateEntryBase &entry) const override
vtkDGOperationEvaluator(vtkDGOperatorEntry &op, vtkDataArray *connectivity, vtkDataArray *values, vtkDataArray *sideConn, vtkTypeUInt64 offset, vtkDGOperatorEntry shapeGradient=vtkDGOperatorEntry(), vtkDataArray *shapeConnectivity=nullptr, vtkDataArray *shapeValues=nullptr)
void Invert()
Definition: vtkMatrix3x3.h:99
void MultiplyPoint(const float in[3], float out[3])
Multiply a homogeneous coordinate by this matrix, i.e.
Definition: vtkMatrix3x3.h:117
void ShapeInnerProduct() const
Compute the inner product of this->ShapeBasisTuple and this->ShapeValueTuple, storing the result in t...
#define VTK_ALWAYS_EXPORT
Definition: vtkCompiler.h:65
vtkDGSideType
Whether cells are stand-alone or sides of other cells.
#define VTK_WRAPEXCLUDE
void ApplyInverseJacobian(vtkTypeUInt64 ii, OutputIterator &outIter) const
vtkDGShapeModifier
Which type of shape-function post-processing is required.