VTK  9.5.20251120
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
25
32
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 {
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
if(abs(tt - 1.0)< eps)
Definition PyrC1Basis.h:1
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
std::vector< double > ShapeBasisTuple
std::vector< double > ShapeValueTuple
vtkDGOperatorEntry ShapeGradientEntry
std::vector< vtkTypeUInt64 > ShapeConnTuple
vtkDGOperatorEntry OpEntry
std::vector< double > ValueTuple
std::vector< double > Jacobian
vtkDataArray * SideConnectivity
std::vector< double > BasisTuple
std::array< vtkTypeUInt64, 2 > SideTuple
vtkDGOperationState(vtkDGOperatorEntry &op, vtkDataArray *connectivity, vtkDataArray *values, vtkDataArray *sideConn, vtkTypeUInt64 offset, vtkDGOperatorEntry shapeGradient=vtkDGOperatorEntry(), vtkDataArray *shapeConnectivity=nullptr, vtkDataArray *shapeValues=nullptr)
vtkTypeUInt64 LastShapeCellId
std::vector< vtkTypeUInt64 > ConnTuple
vtkDataArray * CellConnectivity
std::array< double, 3 > RST
vtkDataArray * ShapeConnectivity
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.
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 vtkDataArray
#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