VTK  9.4.20241217
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.
8#include "vtkDGOperationStateEntry.h" // For API.
9#include "vtkDoubleArray.h" // For API.
10#include "vtkMatrix3x3.h" // For API.
11
21{
24};
25
28{
30 Sides
31};
32
41{
45};
46
54template <typename InputIterator, typename OutputIterator, vtkDGSharingType DOFSharing,
55 vtkDGSideType SourceType, vtkDGShapeModifier Modifier,
56 vtkDGSharingType ShapeSharing = Discontinuous>
58{
59public:
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
Evaluate a vtkDGOperationEntry on a provided cell ID at provided parametric coordinates.
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 ApplyScaledJacobian(vtkTypeUInt64 ii, OutputIterator &outIter) const
vtkDGOperationEvaluator(vtkDGOperatorEntry &op, vtkDataArray *connectivity, vtkDataArray *values, vtkDataArray *sideConn, vtkTypeUInt64 offset, vtkDGOperatorEntry shapeGradient=vtkDGOperatorEntry(), vtkDataArray *shapeConnectivity=nullptr, vtkDataArray *shapeValues=nullptr)
void CloneInto(vtkDGOperationStateEntryBase &entry) const override
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...
void operator()(InputIterator &inIter, OutputIterator &outIter, vtkTypeUInt64 begin, vtkTypeUInt64 end) const
void ComputeJacobian() const
Compute the shape-attribute Jacobian matrix, storing it in this->Jacobian.
void ShapeInnerProduct() const
Compute the inner product of this->ShapeBasisTuple and this->ShapeValueTuple, storing the result in t...
void ApplyInverseJacobian(vtkTypeUInt64 ii, OutputIterator &outIter) const
This is a base class that exists so that vtkDGOperationState can provide a virtual CloneInto method t...
Encapsulate the state required to evaluate DG cell-attributes.
std::unique_ptr< vtkDGOperationState > State
vtkDGCellRangeEvaluator< InputIterator, OutputIterator > Function
Encapsulate the state required to evaluate DG cell-attributes.
A record for a basis in a function space that is specific to one cell shape.
int OperatorSize
The number of coordinates each operator-function evaluates to.
int NumberOfFunctions
The number of functions in the basis.
abstract superclass for arrays of numeric data
double Determinant()
Compute the determinant of the matrix and return it.
void MultiplyPoint(const float in[3], float out[3])
Multiply a homogeneous coordinate by this matrix, i.e.
#define VTK_ALWAYS_EXPORT
Definition vtkCompiler.h:65
vtkDGShapeModifier
Which type of shape-function post-processing is required.
@ ScaledJacobian
!< For HGRAD
@ None
!< For HCURL
vtkDGSharingType
Whether degrees of freedom (DOF) are shared between cells.
@ Discontinuous
Degrees of freedom are not shared.
@ SharedDOF
Degrees of freedom (DOF) are shared.
vtkDGSideType
Whether cells are stand-alone or sides of other cells.
@ Cells
A cell specified by degrees of freedom held in arrays.
@ Sides
A side specified by a cell ID and an integer index into sides.
#define VTK_WRAPEXCLUDE