BEM++  2.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Groups Pages
default_evaluator_for_integral_operators_imp.hpp
1 // Copyright (C) 2011-2012 by the BEM++ Authors
2 //
3 // Permission is hereby granted, free of charge, to any person obtaining a copy
4 // of this software and associated documentation files (the "Software"), to deal
5 // in the Software without restriction, including without limitation the rights
6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 // copies of the Software, and to permit persons to whom the Software is
8 // furnished to do so, subject to the following conditions:
9 //
10 // The above copyright notice and this permission notice shall be included in
11 // all copies or substantial portions of the Software.
12 //
13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 // THE SOFTWARE.
20 
21 #include "default_evaluator_for_integral_operators.hpp" // keep IDEs happy
22 
23 #include "../common/common.hpp"
24 
25 #include "basis_data.hpp"
26 #include "collection_of_basis_transformations.hpp"
27 #include "geometrical_data.hpp"
28 #include "collection_of_kernels.hpp"
29 #include "collection_of_2d_arrays.hpp"
30 #include "collection_of_3d_arrays.hpp"
31 #include "collection_of_4d_arrays.hpp"
32 #include "kernel_trial_integral.hpp"
33 #include "numerical_quadrature.hpp"
34 #include "opencl_handler.hpp"
35 #include "raw_grid_geometry.hpp"
36 #include "serial_blas_region.hpp"
37 #include "shapeset.hpp"
38 
39 #include <tbb/parallel_for.h>
40 #include <tbb/task_scheduler_init.h>
41 
42 namespace Fiber
43 {
44 
45 namespace
46 {
47 
48 template <typename BasisFunctionType, typename KernelType, typename ResultType>
49 class EvaluationLoopBody
50 {
51 public:
52  typedef typename ScalarTraits<ResultType>::RealType CoordinateType;
53 
54  EvaluationLoopBody(
55  size_t chunkSize,
56  const arma::Mat<CoordinateType>& points,
57  const GeometricalData<CoordinateType>& trialGeomData,
58  const CollectionOf2dArrays<ResultType>& trialTransfValues,
59  const std::vector<CoordinateType>& weights,
60  const CollectionOfKernels<KernelType>& kernels,
61  const KernelTrialIntegral<BasisFunctionType, KernelType, ResultType>& integral,
62  arma::Mat<ResultType>& result) :
63  m_chunkSize(chunkSize),
64  m_points(points), m_trialGeomData(trialGeomData),
65  m_trialTransfValues(trialTransfValues), m_weights(weights),
66  m_kernels(kernels), m_integral(integral), m_result(result),
67  m_pointCount(result.n_cols), m_outputComponentCount(result.n_rows)
68  {
69  }
70 
71  void operator() (const tbb::blocked_range<size_t>& r) const {
72  CollectionOf4dArrays<KernelType> kernelValues;
73  GeometricalData<CoordinateType> evalPointGeomData;
74  for (size_t i = r.begin(); i < r.end(); ++i)
75  {
76  size_t start = m_chunkSize * i;
77  size_t end = std::min(start + m_chunkSize, m_pointCount);
78  evalPointGeomData.globals = m_points.cols(start, end - 1 /* inclusive */);
79  m_kernels.evaluateOnGrid(evalPointGeomData, m_trialGeomData, kernelValues);
80  // View into the current chunk of the "result" array
81  _2dArray<ResultType> resultChunk(m_outputComponentCount, end - start,
82  m_result.colptr(start));
83  m_integral.evaluate(m_trialGeomData,
84  kernelValues,
85  m_trialTransfValues,
86  m_weights,
87  resultChunk);
88  }
89  }
90 
91 private:
92  size_t m_chunkSize;
93  const arma::Mat<CoordinateType>& m_points;
94  const GeometricalData<CoordinateType>& m_trialGeomData;
95  const CollectionOf2dArrays<ResultType>& m_trialTransfValues;
96  const std::vector<CoordinateType>& m_weights;
97  const CollectionOfKernels<KernelType>& m_kernels;
98  const KernelTrialIntegral<BasisFunctionType, KernelType, ResultType>& m_integral;
99  arma::Mat<ResultType>& m_result;
100  size_t m_pointCount;
101  size_t m_outputComponentCount;
102 };
103 
104 } // namespace
105 
106 template <typename BasisFunctionType, typename KernelType,
107  typename ResultType, typename GeometryFactory>
108 DefaultEvaluatorForIntegralOperators<BasisFunctionType, KernelType,
109 ResultType, GeometryFactory>::DefaultEvaluatorForIntegralOperators(
110  const shared_ptr<const GeometryFactory>& geometryFactory,
111  const shared_ptr<const RawGridGeometry<CoordinateType> >& rawGeometry,
112  const shared_ptr<const std::vector<const Shapeset<BasisFunctionType>*> >& trialShapesets,
113  const shared_ptr<const CollectionOfKernels<KernelType> >& kernels,
114  const shared_ptr<const CollectionOfShapesetTransformations<CoordinateType> >& trialTransformations,
115  const shared_ptr<const KernelTrialIntegral<BasisFunctionType, KernelType, ResultType> >& integral,
116  const shared_ptr<const std::vector<std::vector<ResultType> > >& argumentLocalCoefficients,
117  const shared_ptr<const OpenClHandler >& openClHandler,
118  const ParallelizationOptions& parallelizationOptions,
119  const shared_ptr<const QuadratureDescriptorSelectorForPotentialOperators<
120  BasisFunctionType> >& quadDescSelector,
121  const shared_ptr<const SingleQuadratureRuleFamily<
122  CoordinateType> >& quadRuleFamily) :
123  m_geometryFactory(geometryFactory), m_rawGeometry(rawGeometry),
124  m_trialShapesets(trialShapesets), m_kernels(kernels),
125  m_trialTransformations(trialTransformations), m_integral(integral),
126  m_argumentLocalCoefficients(argumentLocalCoefficients),
127  m_openClHandler(openClHandler),
128  m_parallelizationOptions(parallelizationOptions),
129  m_quadDescSelector(quadDescSelector),
130  m_quadRuleFamily(quadRuleFamily)
131 {
132  const size_t elementCount = rawGeometry->elementCount();
133  if (!rawGeometry->auxData().is_empty() &&
134  rawGeometry->auxData().n_cols != elementCount)
135  throw std::invalid_argument(
136  "DefaultEvaluatorForIntegralOperators::"
137  "DefaultEvaluatorForIntegralOperators(): "
138  "number of columns of auxData must match that of "
139  "elementCornerIndices");
140  if (trialShapesets->size() != elementCount)
141  throw std::invalid_argument(
142  "DefaultEvaluatorForIntegralOperators::"
143  "DefaultEvaluatorForIntegralOperators(): "
144  "size of testShapesetss must match the number of columns of "
145  "elementCornerIndices");
146 
147  // Cache "trial data" such as the values of the argument at quadrature
148  // points, vectors normal to surface at quadrature points etc.
149  cacheTrialData();
150 }
151 
152 template <typename BasisFunctionType, typename KernelType,
153  typename ResultType, typename GeometryFactory>
154 void DefaultEvaluatorForIntegralOperators<BasisFunctionType, KernelType,
155 ResultType, GeometryFactory>::evaluate(
156  Region region,
157  const arma::Mat<CoordinateType>& points, arma::Mat<ResultType>& result) const
158 {
159  const size_t pointCount = points.n_cols;
160  const int outputComponentCount = m_integral->resultDimension();
161 
162  result.set_size(outputComponentCount, pointCount);
163  result.fill(0.);
164 
165  const GeometricalData<CoordinateType>& trialGeomData =
166  (region == EvaluatorForIntegralOperators<ResultType>::NEAR_FIELD) ?
167  m_nearFieldTrialGeomData :
168  m_farFieldTrialGeomData;
169  const CollectionOf2dArrays<ResultType>& trialTransfValues =
170  (region == EvaluatorForIntegralOperators<ResultType>::NEAR_FIELD) ?
171  m_nearFieldTrialTransfValues :
172  m_farFieldTrialTransfValues;
173  const std::vector<CoordinateType>& weights =
174  (region == EvaluatorForIntegralOperators<ResultType>::NEAR_FIELD) ?
175  m_nearFieldWeights :
176  m_farFieldWeights;
177 
178  // Do things in chunks -- in order to avoid creating
179  // too large arrays of kernel values
180 
181  // For the time being, we don't take into account the possibly tensorial
182  // nature of kernels nor the fact that there may be more than one kernel
183  const size_t kernelValuesSizePerEvalPoint =
184  trialGeomData.globals.n_cols * sizeof(KernelType);
185  const size_t chunkSize =
186  std::max(size_t(1), 10 * 1024 * 1024 / kernelValuesSizePerEvalPoint);
187  const size_t chunkCount = (pointCount + chunkSize - 1) / chunkSize;
188 
189  int maxThreadCount = 1;
190  if (!m_parallelizationOptions.isOpenClEnabled()) {
191  if (m_parallelizationOptions.maxThreadCount() ==
192  ParallelizationOptions::AUTO)
193  maxThreadCount = tbb::task_scheduler_init::automatic;
194  else
195  maxThreadCount = m_parallelizationOptions.maxThreadCount();
196  }
197  tbb::task_scheduler_init scheduler(maxThreadCount);
198  typedef EvaluationLoopBody<
199  BasisFunctionType, KernelType, ResultType> Body;
200  {
202  tbb::parallel_for(tbb::blocked_range<size_t>(0, chunkCount),
203  Body(chunkSize,
204  points, trialGeomData, trialTransfValues, weights,
205  *m_kernels, *m_integral, result));
206  }
207 
208 // // Old serial version
209 // CollectionOf4dArrays<KernelType> kernelValues;
210 // GeometricalData<CoordinateType> evalPointGeomData;
211 // for (size_t start = 0; start < pointCount; start += chunkSize)
212 // {
213 // size_t end = std::min(start + chunkSize, pointCount);
214 // evalPointGeomData.globals = points.cols(start, end - 1 /* inclusive */);
215 // m_kernels->evaluateOnGrid(evalPointGeomData, trialGeomData, kernelValues);
216 // // View into the current chunk of the "result" array
217 // _2dArray<ResultType> resultChunk(outputComponentCount, end - start,
218 // result.colptr(start));
219 // m_integral->evaluate(trialGeomData,
220 // kernelValues,
221 // weightedTrialTransfValues,
222 // resultChunk);
223 // }
224 }
225 
226 template <typename BasisFunctionType, typename KernelType,
227  typename ResultType, typename GeometryFactory>
228 void DefaultEvaluatorForIntegralOperators<BasisFunctionType, KernelType,
229 ResultType, GeometryFactory>::cacheTrialData()
230 {
231  size_t testGeomDeps = 0, trialGeomDeps = 0;
232  m_kernels->addGeometricalDependencies(testGeomDeps, trialGeomDeps);
233  if (testGeomDeps != 0 && testGeomDeps != GLOBALS)
234  throw std::runtime_error(
235  "DefaultEvaluatorForIntegralOperators::cacheTrialData(): "
236  "potentials cannot contain kernels that depend on other test data "
237  "than global coordinates");
238 
239  calcTrialData(EvaluatorForIntegralOperators<ResultType>::FAR_FIELD,
240  trialGeomDeps, m_farFieldTrialGeomData,
241  m_farFieldTrialTransfValues, m_farFieldWeights);
242  // near field is currently not treated in any special way
243  calcTrialData(EvaluatorForIntegralOperators<ResultType>::FAR_FIELD,
244  trialGeomDeps, m_nearFieldTrialGeomData,
245  m_nearFieldTrialTransfValues, m_nearFieldWeights);
246 }
247 
248 template <typename BasisFunctionType, typename KernelType,
249  typename ResultType, typename GeometryFactory>
250 void DefaultEvaluatorForIntegralOperators<BasisFunctionType, KernelType,
251 ResultType, GeometryFactory>::calcTrialData(
252  Region region,
253  int kernelTrialGeomDeps,
254  GeometricalData<CoordinateType>& trialGeomData,
255  CollectionOf2dArrays<ResultType>& trialTransfValues,
256  std::vector<CoordinateType>& weights) const
257 {
258  if (region != EvaluatorForIntegralOperators<ResultType>::FAR_FIELD)
259  throw std::invalid_argument(
260  "DefaultEvaluatorForIntegralOperators::calcTrialData(): "
261  "currently region must be set to FAR_FIELD");
262 
263  const int elementCount = m_rawGeometry->elementCount();
264  const int worldDim = m_rawGeometry->worldDimension();
265  const int gridDim = m_rawGeometry->gridDimension();
266  const int transformationCount = m_trialTransformations->transformationCount();
267 
268  // Find out which basis data need to be calculated
269  size_t basisDeps = 0;
270  // Find out which geometrical data need to be calculated, in addition
271  // to those needed by the kernel
272  size_t trialGeomDeps = kernelTrialGeomDeps;
273  m_trialTransformations->addDependencies(basisDeps, trialGeomDeps);
274  trialGeomDeps |= INTEGRATION_ELEMENTS;
275 
276  // Initialise the geometry factory
277  typedef typename GeometryFactory::Geometry Geometry;
278  std::auto_ptr<Geometry> geometry(m_geometryFactory->make());
279 
280  // Find all unique trial shapesets
281  // Set of unique quadrature variants
282  typedef std::set<const Shapeset<BasisFunctionType>*> ShapesetSet;
283  ShapesetSet uniqueTrialShapesets(m_trialShapesets->begin(), m_trialShapesets->end());
284 
285  // Initialise temporary (per-element) data containers
286  std::vector<GeometricalData<CoordinateType> > geomDataPerElement(elementCount);
287  std::vector<CollectionOf2dArrays<ResultType> >
288  trialTransfValuesPerElement(elementCount);
289  std::vector<std::vector<CoordinateType> > weightsPerElement(elementCount);
290 
291  int quadPointCount = 0; // Quadrature point counter
292 
293  for (typename ShapesetSet::const_iterator it = uniqueTrialShapesets.begin();
294  it != uniqueTrialShapesets.end(); ++it)
295  {
296  const Shapeset<BasisFunctionType>& activeShapeset = *(*it);
297 
298  // Find out the element type
299  int elementCornerCount = 0;
300  for (int e = 0; e < elementCount; ++e)
301  if ((*m_trialShapesets)[e] == &activeShapeset)
302  {
303  // elementCornerCount = m_rawGeometry->elementCornerCount(e);
304  // This implementation prevents a segmentation fault on Macs
305  // when compiled with llvm in 64-bit mode with -O2 or -O3
306  const arma::Mat<int>& elementCornerIndices =
307  m_rawGeometry->elementCornerIndices();
308  for (size_t i = 0; i < elementCornerIndices.n_rows; ++i)
309  if (elementCornerIndices(i, e) >= 0)
310  elementCornerCount = i + 1;
311  else
312  break;
313 
314  break;
315  }
316 
317  // Get quadrature points and weights
318  SingleQuadratureDescriptor desc =
319  m_quadDescSelector->farFieldQuadratureDescriptor(
320  activeShapeset, elementCornerCount);
321  arma::Mat<CoordinateType> localQuadPoints;
322  std::vector<CoordinateType> quadWeights;
323  m_quadRuleFamily->fillQuadraturePointsAndWeights(
324  desc, localQuadPoints, quadWeights);
325 
326  // Get basis data
327  BasisData<BasisFunctionType> basisData;
328  activeShapeset.evaluate(basisDeps, localQuadPoints, ALL_DOFS, basisData);
329 
330  BasisData<ResultType> argumentData;
331  if (basisDeps & VALUES)
332  argumentData.values.set_size(basisData.values.extent(0),
333  1, // just one function
334  basisData.values.extent(2));
335  if (basisDeps & DERIVATIVES)
336  argumentData.derivatives.set_size(basisData.derivatives.extent(0),
337  basisData.derivatives.extent(1),
338  1, // just one function
339  basisData.derivatives.extent(3));
340 
341  // Loop over elements and process those that use the active shapeset
342  CollectionOf3dArrays<ResultType> trialValues;
343  for (int e = 0; e < elementCount; ++e)
344  {
345  if ((*m_trialShapesets)[e] != &activeShapeset)
346  continue;
347 
348  // Local coefficients of the argument in the current element
349  const std::vector<ResultType>& localCoefficients =
350  (*m_argumentLocalCoefficients)[e];
351 
352  // Calculate the argument function's values and/or derivatives
353  // at quadrature points in the current element
354  if (basisDeps & VALUES)
355  {
356  std::fill(argumentData.values.begin(),
357  argumentData.values.end(), 0.);
358  assert(localCoefficients.size() == basisData.values.extent(1));
359  for (size_t point = 0; point < basisData.values.extent(2); ++point)
360  for (size_t dim = 0; dim < basisData.values.extent(0); ++dim)
361  for (size_t fun = 0; fun < basisData.values.extent(1); ++fun)
362  argumentData.values(dim, 0, point) +=
363  basisData.values(dim, fun, point) *
364  localCoefficients[fun];
365  }
366  if (basisDeps & DERIVATIVES)
367  {
368  std::fill(argumentData.derivatives.begin(),
369  argumentData.derivatives.end(), 0.);
370  assert(localCoefficients.size() == basisData.derivatives.extent(2));
371  for (size_t point = 0; point < basisData.derivatives.extent(3); ++point)
372  for (size_t dim = 0; dim < basisData.derivatives.extent(1); ++dim)
373  for (size_t comp = 0; comp < basisData.derivatives.extent(0); ++comp)
374  for (size_t fun = 0; fun < basisData.derivatives.extent(2); ++fun)
375  argumentData.derivatives(comp, dim, 0, point) +=
376  basisData.derivatives(comp, dim, fun, point) *
377  localCoefficients[fun];
378  }
379 
380  // Get geometrical data
381  m_rawGeometry->setupGeometry(e, *geometry);
382  geometry->getData(trialGeomDeps, localQuadPoints,
383  geomDataPerElement[e]);
384 
385  m_trialTransformations->evaluate(argumentData, geomDataPerElement[e],
386  trialValues);
387 
388 // weightedTrialTransfValuesPerElement[e].set_size(transformationCount);
389 // for (int transf = 0; transf < transformationCount; ++transf)
390 // {
391 // const size_t dimCount = trialValues[transf].extent(0);
392 // const size_t quadPointCount = trialValues[transf].extent(2);
393 // weightedTrialTransfValuesPerElement[e][transf].set_size(
394 // dimCount, quadPointCount);
395 // for (size_t point = 0; point < quadPointCount; ++point)
396 // for (size_t dim = 0; dim < dimCount; ++dim)
397 // weightedTrialTransfValuesPerElement[e][transf](dim, point) =
398 // trialValues[transf](dim, 0, point) *
399 // geomDataPerElement[e].integrationElements(point) *
400 // quadWeights[point];
401 // } // end of loop over transformations
402 
403  const size_t localQuadPointCount = quadWeights.size();
404 
405  trialTransfValuesPerElement[e].set_size(transformationCount);
406  for (int transf = 0; transf < transformationCount; ++transf)
407  {
408  const size_t dimCount = trialValues[transf].extent(0);
409  assert(trialValues[transf].extent(2) == localQuadPointCount);
410  trialTransfValuesPerElement[e][transf].set_size(
411  dimCount, localQuadPointCount);
412  for (size_t point = 0; point < localQuadPointCount; ++point)
413  for (size_t dim = 0; dim < dimCount; ++dim)
414  trialTransfValuesPerElement[e][transf](dim, point) =
415  trialValues[transf](dim, 0, point);
416  } // end of loop over transformations
417 
418  weightsPerElement[e].resize(localQuadPointCount);
419  for (size_t point = 0; point < localQuadPointCount; ++point)
420  weightsPerElement[e][point] = quadWeights[point] *
421  geomDataPerElement[e].integrationElements(point);
422 
423  quadPointCount += quadWeights.size();
424  } // end of loop over elements
425  } // end of loop over unique shapesets
426 
427  // In the following, weightedTrialExprValuesPerElement[e][transf].extent(1) is used
428  // repeatedly as the number of quadrature points in e'th element
429 
430  // Now convert std::vectors of arrays into unique big arrays
431  // and store them in trialGeomData and weightedTrialTransfValues
432 
433  // Fill member matrices of trialGeomData
434  if (kernelTrialGeomDeps & GLOBALS)
435  trialGeomData.globals.set_size(worldDim, quadPointCount);
436  if (kernelTrialGeomDeps & INTEGRATION_ELEMENTS)
437  trialGeomData.integrationElements.set_size(quadPointCount);
438  if (kernelTrialGeomDeps & NORMALS)
439  trialGeomData.normals.set_size(worldDim, quadPointCount);
440  if (kernelTrialGeomDeps & JACOBIANS_TRANSPOSED)
441  trialGeomData.jacobiansTransposed.set_size(gridDim, worldDim, quadPointCount);
442  if (kernelTrialGeomDeps & JACOBIAN_INVERSES_TRANSPOSED)
443  trialGeomData.jacobianInversesTransposed.set_size(worldDim, gridDim, quadPointCount);
444 // weightedTrialTransfValues.set_size(transformationCount);
445 // for (int transf = 0; transf < transformationCount; ++transf)
446 // weightedTrialTransfValues[transf].set_size(
447 // m_trialTransformations->resultDimension(transf), quadPointCount);
448  trialTransfValues.set_size(transformationCount);
449  for (int transf = 0; transf < transformationCount; ++transf)
450  trialTransfValues[transf].set_size(
451  m_trialTransformations->resultDimension(transf), quadPointCount);
452  weights.resize(quadPointCount);
453 
454  for (int e = 0, startCol = 0;
455  e < elementCount;
456  startCol += trialTransfValuesPerElement[e][0].extent(1), ++e)
457  {
458  int endCol = startCol + trialTransfValuesPerElement[e][0].extent(1) - 1;
459  if (kernelTrialGeomDeps & GLOBALS)
460  trialGeomData.globals.cols(startCol, endCol) =
461  geomDataPerElement[e].globals;
462  if (kernelTrialGeomDeps & INTEGRATION_ELEMENTS)
463  trialGeomData.integrationElements.cols(startCol, endCol) =
464  geomDataPerElement[e].integrationElements;
465  if (kernelTrialGeomDeps & NORMALS)
466  trialGeomData.normals.cols(startCol, endCol) =
467  geomDataPerElement[e].normals;
468  if (kernelTrialGeomDeps & JACOBIANS_TRANSPOSED) {
469  const size_t m =
470  trialGeomData.jacobiansTransposed.extent(0);
471  const size_t n =
472  trialGeomData.jacobiansTransposed.extent(1);
473  for (int col = startCol; col <= endCol; ++col)
474  for (int c = 0; c < n; ++c)
475  for (int r = 0; r < n; ++r)
476  trialGeomData.jacobiansTransposed(r, c, col) =
477  geomDataPerElement[e].jacobiansTransposed(
478  r, c, col - startCol);
479  }
480  if (kernelTrialGeomDeps & JACOBIAN_INVERSES_TRANSPOSED) {
481  const size_t m =
482  trialGeomData.jacobianInversesTransposed.extent(0);
483  const size_t n =
484  trialGeomData.jacobianInversesTransposed.extent(1);
485  for (int col = startCol; col <= endCol; ++col)
486  for (int c = 0; c < n; ++c)
487  for (int r = 0; r < n; ++r)
488  trialGeomData.jacobianInversesTransposed(r, c, col) =
489  geomDataPerElement[e].jacobianInversesTransposed(
490  r, c, col - startCol);
491  }
492  for (int transf = 0; transf < transformationCount; ++transf)
493  for (size_t point = 0; point < trialTransfValuesPerElement[e][transf].extent(1); ++point)
494  for (size_t dim = 0; dim < trialTransfValuesPerElement[e][transf].extent(0); ++dim)
495  trialTransfValues[transf](dim, startCol + point) =
496  trialTransfValuesPerElement[e][transf](dim, point);
497  for (size_t point = 0; point < trialTransfValuesPerElement[e][0].extent(1); ++point)
498  weights[startCol + point] = weightsPerElement[e][point];
499  }
500 }
501 
502 } // namespace Fiber
virtual void addGeometricalDependencies(size_t &testGeomDeps, size_t &trialGeomDeps) const =0
Retrieve types of geometrical data on which the kernels depend.
Management of the number of threads used by BLAS and LAPACK.
Definition: serial_blas_region.hpp:30
virtual void evaluateOnGrid(const GeometricalData< CoordinateType > &testGeomData, const GeometricalData< CoordinateType > &trialGeomData, CollectionOf4dArrays< ValueType > &result) const =0
Evaluate the kernels on a tensor grid of test and trial points.