deal.II version GIT relicensing-6834-g5b78e6bcdf 2026-10-01 11:20:01+00:00
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
Loading...
Searching...
No Matches
fe_values_views.h
Go to the documentation of this file.
1// -----------------------------------------------------------------------------
2//
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception OR LGPL-2.1-or-later
4// Copyright (C) 2023 - 2026 by the deal.II authors
5//
6// This file is part of the deal.II library.
7//
8// Detailed license information governing the source code and contributions
9// can be found in LICENSE.md and CONTRIBUTING.md at the top level directory.
10//
11// -----------------------------------------------------------------------------
12
13#ifndef dealii_fe_values_views_h
14#define dealii_fe_values_views_h
15
16#include <deal.II/base/config.h>
17
19#include <deal.II/base/lazy.h>
22#include <deal.II/base/tensor.h>
23
26
28
29#include <type_traits>
30#include <variant>
31#include <vector>
32
34
35// Forward declaration
36#ifndef DOXYGEN
37template <int dim, int spacedim = dim>
38class FEValuesBase;
39#endif
40
41namespace internal
42{
67 template <int dim, typename NumberType = double>
68 using CurlType = std::conditional_t<
69 dim == 3,
71 std::conditional_t<dim == 2, NumberType, std::monostate>>;
72} // namespace internal
73
74
75
97namespace FEValuesViews
98{
110 template <int dim, int spacedim = dim>
111 class Scalar
112 {
113 public:
119 using value_type = double;
120
127
134
141
148 template <typename Number>
150
157 template <typename Number>
160
167 template <typename Number>
170
177 template <typename Number>
180
187 template <typename Number>
190
217
221 Scalar();
222
228 Scalar(const FEValuesBase<dim, spacedim> &fe_values_base,
229 const unsigned int component);
230
235 Scalar(const Scalar<dim, spacedim> &) = delete;
236
240 // NOLINTNEXTLINE OSX does not compile with noexcept
242
246 ~Scalar() = default;
247
252 Scalar &
254
258 Scalar &
259 operator=(Scalar<dim, spacedim> &&) noexcept = default;
260
275 value(const unsigned int shape_function, const unsigned int q_point) const;
276
288 gradient(const unsigned int shape_function,
289 const unsigned int q_point) const;
290
302 hessian(const unsigned int shape_function,
303 const unsigned int q_point) const;
304
316 third_derivative(const unsigned int shape_function,
317 const unsigned int q_point) const;
318
336 template <typename Number>
337 void
338 get_function_values(const ReadVector<Number> &fe_function,
339 std::vector<solution_value_type<Number>> &values) const;
340
375 template <class InputVector>
376 void
378 const InputVector &dof_values,
379 std::vector<solution_value_type<typename InputVector::value_type>>
380 &values) const;
381
399 template <typename Number>
400 void
402 const ReadVector<Number> &fe_function,
403 std::vector<solution_gradient_type<Number>> &gradients) const;
404
411 template <class InputVector>
412 void
414 const InputVector &dof_values,
415 std::vector<solution_gradient_type<typename InputVector::value_type>>
416 &gradients) const;
417
435 template <typename Number>
436 void
438 const ReadVector<Number> &fe_function,
439 std::vector<solution_hessian_type<Number>> &hessians) const;
440
447 template <class InputVector>
448 void
450 const InputVector &dof_values,
451 std::vector<solution_hessian_type<typename InputVector::value_type>>
452 &hessians) const;
453
454
473 template <typename Number>
474 void
476 const ReadVector<Number> &fe_function,
477 std::vector<solution_laplacian_type<Number>> &laplacians) const;
478
485 template <class InputVector>
486 void
488 const InputVector &dof_values,
489 std::vector<solution_laplacian_type<typename InputVector::value_type>>
490 &laplacians) const;
491
492
511 template <typename Number>
512 void
514 const ReadVector<Number> &fe_function,
515 std::vector<solution_third_derivative_type<Number>> &third_derivatives)
516 const;
517
524 template <class InputVector>
525 void
527 const InputVector &dof_values,
528 std::vector<
529 solution_third_derivative_type<typename InputVector::value_type>>
530 &third_derivatives) const;
531
532
533 private:
538
543 unsigned int component;
544
549 };
550
551
552
582 template <int dim, int spacedim = dim>
583 class Vector
584 {
585 public:
592
602
614
620 using divergence_type = double;
621
628 using curl_type = typename ::internal::CurlType<spacedim>;
629
636
643
650 template <typename Number>
652
659 template <typename Number>
662
669 template <typename Number>
672
679 template <typename Number>
682
689 template <typename Number>
692
699 template <typename Number>
701
708 template <typename Number>
711
718 template <typename Number>
721
727 {
737
747 unsigned int row_index[spacedim];
748
759 };
760
764 Vector();
765
774 Vector(const FEValuesBase<dim, spacedim> &fe_values_base,
775 const unsigned int first_vector_component);
776
781 Vector(const Vector<dim, spacedim> &) = delete;
782
786 // NOLINTNEXTLINE OSX does not compile with noexcept
788
792 ~Vector() = default;
793
798 Vector &
800
804 // NOLINTNEXTLINE OSX does not compile with noexcept
805 Vector &
806 operator=(Vector<dim, spacedim> &&) = default; // NOLINT
807
825 value(const unsigned int shape_function, const unsigned int q_point) const;
826
841 gradient(const unsigned int shape_function,
842 const unsigned int q_point) const;
843
860 symmetric_gradient(const unsigned int shape_function,
861 const unsigned int q_point) const;
862
874 divergence(const unsigned int shape_function,
875 const unsigned int q_point) const;
876
898 curl(const unsigned int shape_function, const unsigned int q_point) const;
899
911 hessian(const unsigned int shape_function,
912 const unsigned int q_point) const;
913
925 third_derivative(const unsigned int shape_function,
926 const unsigned int q_point) const;
927
945 template <typename Number>
946 void
947 get_function_values(const ReadVector<Number> &fe_function,
948 std::vector<solution_value_type<Number>> &values) const;
949
984 template <class InputVector>
985 void
987 const InputVector &dof_values,
989 &values) const;
990
1008 template <typename Number>
1009 void
1011 const ReadVector<Number> &fe_function,
1012 std::vector<solution_gradient_type<Number>> &gradients) const;
1013
1020 template <class InputVector>
1021 void
1023 const InputVector &dof_values,
1025 &gradients) const;
1026
1050 template <typename Number>
1051 void
1052 get_function_symmetric_gradients(
1053 const ReadVector<Number> &fe_function,
1055 &symmetric_gradients) const;
1056
1063 template <class InputVector>
1064 void
1065 get_function_symmetric_gradients_from_local_dof_values(
1066 const InputVector &dof_values,
1067 std::vector<
1069 &symmetric_gradients) const;
1070
1089 template <typename Number>
1090 void
1091 get_function_divergences(
1092 const ReadVector<Number> &fe_function,
1093 std::vector<solution_divergence_type<Number>> &divergences) const;
1094
1101 template <class InputVector>
1102 void
1103 get_function_divergences_from_local_dof_values(
1104 const InputVector &dof_values,
1106 &divergences) const;
1107
1126 template <typename Number>
1127 void
1128 get_function_curls(const ReadVector<Number> &fe_function,
1129 std::vector<solution_curl_type<Number>> &curls) const;
1130
1137 template <class InputVector>
1138 void
1139 get_function_curls_from_local_dof_values(
1140 const InputVector &dof_values,
1142 const;
1143
1161 template <typename Number>
1162 void
1164 const ReadVector<Number> &fe_function,
1165 std::vector<solution_hessian_type<Number>> &hessians) const;
1166
1173 template <class InputVector>
1174 void
1176 const InputVector &dof_values,
1178 &hessians) const;
1179
1198 template <typename Number>
1199 void
1201 const ReadVector<Number> &fe_function,
1202 std::vector<solution_laplacian_type<Number>> &laplacians) const;
1203
1210 template <class InputVector>
1211 void
1213 const InputVector &dof_values,
1215 &laplacians) const;
1216
1235 template <typename Number>
1236 void
1238 const ReadVector<Number> &fe_function,
1239 std::vector<solution_third_derivative_type<Number>> &third_derivatives)
1240 const;
1241
1248 template <class InputVector>
1249 void
1251 const InputVector &dof_values,
1252 std::vector<
1254 &third_derivatives) const;
1255
1256 private:
1261
1267
1271 std::vector<ShapeFunctionData> shape_function_data;
1272 };
1273
1274
1275 template <int rank, int dim, int spacedim = dim>
1277
1300 template <int dim, int spacedim>
1301 class SymmetricTensor<2, dim, spacedim>
1302 {
1303 public:
1311
1322
1329 template <typename Number>
1331
1338 template <typename Number>
1341
1342
1347 struct ShapeFunctionData
1348 {
1357 bool is_nonzero_shape_function_component
1358 [value_type::n_independent_components];
1359
1369 unsigned int row_index[value_type::n_independent_components];
1370
1380
1385 };
1386
1391
1401 SymmetricTensor(const FEValuesBase<dim, spacedim> &fe_values_base,
1402 const unsigned int first_tensor_component);
1403
1409
1413 // NOLINTNEXTLINE OSX does not compile with noexcept
1415
1422
1428
1447 value(const unsigned int shape_function, const unsigned int q_point) const;
1448
1463 divergence(const unsigned int shape_function,
1464 const unsigned int q_point) const;
1465
1483 template <typename Number>
1484 void
1485 get_function_values(const ReadVector<Number> &fe_function,
1486 std::vector<solution_value_type<Number>> &values) const;
1487
1522 template <class InputVector>
1523 void
1524 get_function_values_from_local_dof_values(
1525 const InputVector &dof_values,
1526 std::vector<solution_value_type<typename InputVector::value_type>>
1527 &values) const;
1528
1550 template <typename Number>
1551 void
1552 get_function_divergences(
1553 const ReadVector<Number> &fe_function,
1554 std::vector<solution_divergence_type<Number>> &divergences) const;
1555
1562 template <class InputVector>
1563 void
1564 get_function_divergences_from_local_dof_values(
1565 const InputVector &dof_values,
1566 std::vector<solution_divergence_type<typename InputVector::value_type>>
1567 &divergences) const;
1568
1569 private:
1573 ObserverPointer<const FEValuesBase<dim, spacedim>> fe_values;
1574
1579 unsigned int first_tensor_component;
1580
1584 std::vector<ShapeFunctionData> shape_function_data;
1585 };
1586
1587
1588 template <int rank, int dim, int spacedim = dim>
1589 class Tensor;
1590
1609 template <int dim, int spacedim>
1610 class Tensor<2, dim, spacedim>
1611 {
1612 public:
1618
1623
1629
1636 template <typename Number>
1638
1645 template <typename Number>
1648
1655 template <typename Number>
1658
1659
1664 struct ShapeFunctionData
1665 {
1674 bool is_nonzero_shape_function_component
1675 [value_type::n_independent_components];
1676
1686 unsigned int row_index[value_type::n_independent_components];
1687
1697
1702 };
1703
1707 Tensor();
1708
1714
1718 // NOLINTNEXTLINE OSX does not compile with noexcept
1720
1724 ~Tensor() = default;
1725
1735 Tensor(const FEValuesBase<dim, spacedim> &fe_values_base,
1736 const unsigned int first_tensor_component);
1737
1738
1743 Tensor &
1745
1749 Tensor &
1750 operator=(Tensor<2, dim, spacedim> &&) = default; // NOLINT
1751
1769 value(const unsigned int shape_function, const unsigned int q_point) const;
1770
1785 divergence(const unsigned int shape_function,
1786 const unsigned int q_point) const;
1787
1802 gradient(const unsigned int shape_function,
1803 const unsigned int q_point) const;
1804
1822 template <typename Number>
1823 void
1824 get_function_values(const ReadVector<Number> &fe_function,
1825 std::vector<solution_value_type<Number>> &values) const;
1826
1861 template <class InputVector>
1862 void
1863 get_function_values_from_local_dof_values(
1864 const InputVector &dof_values,
1866 &values) const;
1867
1889 template <typename Number>
1890 void
1891 get_function_divergences(
1892 const ReadVector<Number> &fe_function,
1893 std::vector<solution_divergence_type<Number>> &divergences) const;
1894
1901 template <class InputVector>
1902 void
1903 get_function_divergences_from_local_dof_values(
1904 const InputVector &dof_values,
1906 &divergences) const;
1907
1924 template <typename Number>
1925 void
1926 get_function_gradients(
1927 const ReadVector<Number> &fe_function,
1928 std::vector<solution_gradient_type<Number>> &gradients) const;
1929
1936 template <class InputVector>
1937 void
1938 get_function_gradients_from_local_dof_values(
1939 const InputVector &dof_values,
1941 &gradients) const;
1942
1943 private:
1948
1954
1958 std::vector<ShapeFunctionData> shape_function_data;
1959 };
1960
1961} // namespace FEValuesViews
1962
1963
1964namespace internal
1965{
1967 {
1972 template <int dim, int spacedim, typename Extractor>
1974 {};
1975
1983 template <int dim, int spacedim>
1984 struct ViewType<dim, spacedim, FEValuesExtractors::Scalar>
1985 {
1986 using type = typename ::FEValuesViews::Scalar<dim, spacedim>;
1987 };
1988
1996 template <int dim, int spacedim>
1997 struct ViewType<dim, spacedim, FEValuesExtractors::Vector>
1998 {
1999 using type = typename ::FEValuesViews::Vector<dim, spacedim>;
2000 };
2001
2009 template <int dim, int spacedim, int rank>
2010 struct ViewType<dim, spacedim, FEValuesExtractors::Tensor<rank>>
2011 {
2012 using type = typename ::FEValuesViews::Tensor<rank, dim, spacedim>;
2013 };
2014
2022 template <int dim, int spacedim, int rank>
2023 struct ViewType<dim, spacedim, FEValuesExtractors::SymmetricTensor<rank>>
2024 {
2025 using type =
2026 typename ::FEValuesViews::SymmetricTensor<rank, dim, spacedim>;
2027 };
2028
2036 template <int dim, int spacedim>
2037 struct Cache
2038 {
2043 std::vector<Lazy<::FEValuesViews::Scalar<dim, spacedim>>> scalars;
2044 std::vector<Lazy<::FEValuesViews::Vector<dim, spacedim>>> vectors;
2045 std::vector<
2048 std::vector<Lazy<::FEValuesViews::Tensor<2, dim, spacedim>>>
2050
2054 Cache(const FEValuesBase<dim, spacedim> &fe_values);
2055 };
2056 } // namespace FEValuesViews
2057} // namespace internal
2058
2059namespace FEValuesViews
2060{
2065 template <int dim, int spacedim, typename Extractor>
2066 using View = typename ::internal::FEValuesViews::
2067 ViewType<dim, spacedim, Extractor>::type;
2068} // namespace FEValuesViews
2069
2070#ifndef DOXYGEN
2071
2072/*---------------- Inline functions: namespace FEValuesViews -----------------*/
2073
2074namespace FEValuesViews
2075{
2076 template <int dim, int spacedim>
2077 inline typename Scalar<dim, spacedim>::value_type
2078 Scalar<dim, spacedim>::value(const unsigned int shape_function,
2079 const unsigned int q_point) const
2080 {
2081 AssertIndexRange(shape_function, fe_values->fe->n_dofs_per_cell());
2082 Assert(
2083 fe_values->update_flags & update_values,
2085 "update_values"))));
2086
2087 // an adaptation of the FEValuesBase::shape_value_component function
2088 // except that here we know the component as fixed and we have
2089 // pre-computed and cached a bunch of information. See the comments there.
2090 if (shape_function_data[shape_function].is_nonzero_shape_function_component)
2091 return fe_values->finite_element_output.shape_values(
2092 shape_function_data[shape_function].row_index, q_point);
2093 else
2094 return 0;
2095 }
2096
2097
2098
2099 template <int dim, int spacedim>
2100 inline typename Scalar<dim, spacedim>::gradient_type
2101 Scalar<dim, spacedim>::gradient(const unsigned int shape_function,
2102 const unsigned int q_point) const
2103 {
2104 AssertIndexRange(shape_function, fe_values->fe->n_dofs_per_cell());
2105 Assert(fe_values->update_flags & update_gradients,
2107 "update_gradients")));
2108
2109 // an adaptation of the FEValuesBase::shape_grad_component
2110 // function except that here we know the component as fixed and we have
2111 // pre-computed and cached a bunch of information. See the comments there.
2112 if (shape_function_data[shape_function].is_nonzero_shape_function_component)
2113 return fe_values->finite_element_output
2114 .shape_gradients[shape_function_data[shape_function].row_index]
2115 [q_point];
2116 else
2117 return gradient_type();
2118 }
2119
2120
2121
2122 template <int dim, int spacedim>
2123 inline typename Scalar<dim, spacedim>::hessian_type
2124 Scalar<dim, spacedim>::hessian(const unsigned int shape_function,
2125 const unsigned int q_point) const
2126 {
2127 AssertIndexRange(shape_function, fe_values->fe->n_dofs_per_cell());
2128 Assert(fe_values->update_flags & update_hessians,
2130 "update_hessians")));
2131
2132 // an adaptation of the FEValuesBase::shape_hessian_component
2133 // function except that here we know the component as fixed and we have
2134 // pre-computed and cached a bunch of information. See the comments there.
2135 if (shape_function_data[shape_function].is_nonzero_shape_function_component)
2136 return fe_values->finite_element_output
2137 .shape_hessians[shape_function_data[shape_function].row_index][q_point];
2138 else
2139 return hessian_type();
2140 }
2141
2142
2143
2144 template <int dim, int spacedim>
2145 inline typename Scalar<dim, spacedim>::third_derivative_type
2146 Scalar<dim, spacedim>::third_derivative(const unsigned int shape_function,
2147 const unsigned int q_point) const
2148 {
2149 AssertIndexRange(shape_function, fe_values->fe->n_dofs_per_cell());
2150 Assert(fe_values->update_flags & update_3rd_derivatives,
2152 "update_3rd_derivatives")));
2153
2154 // an adaptation of the FEValuesBase::shape_3rdderivative_component
2155 // function except that here we know the component as fixed and we have
2156 // pre-computed and cached a bunch of information. See the comments there.
2157 if (shape_function_data[shape_function].is_nonzero_shape_function_component)
2158 return fe_values->finite_element_output
2159 .shape_3rd_derivatives[shape_function_data[shape_function].row_index]
2160 [q_point];
2161 else
2162 return third_derivative_type();
2163 }
2164
2165
2166
2167 template <int dim, int spacedim>
2168 inline typename Vector<dim, spacedim>::value_type
2169 Vector<dim, spacedim>::value(const unsigned int shape_function,
2170 const unsigned int q_point) const
2171 {
2172 AssertIndexRange(shape_function, fe_values->fe->n_dofs_per_cell());
2173 Assert(fe_values->update_flags & update_values,
2175 "update_values")));
2176
2177 // same as for the scalar case except that we have one more index
2178 const int snc =
2179 shape_function_data[shape_function].single_nonzero_component;
2180 if (snc == -2)
2181 return value_type();
2182 else if (snc != -1)
2183 {
2184 value_type return_value;
2185 return_value[shape_function_data[shape_function]
2186 .single_nonzero_component_index] =
2187 fe_values->finite_element_output.shape_values(snc, q_point);
2188 return return_value;
2189 }
2190 else
2191 {
2192 value_type return_value;
2193 for (unsigned int d = 0; d < spacedim; ++d)
2194 if (shape_function_data[shape_function]
2195 .is_nonzero_shape_function_component[d])
2196 return_value[d] = fe_values->finite_element_output.shape_values(
2197 shape_function_data[shape_function].row_index[d], q_point);
2198
2199 return return_value;
2200 }
2201 }
2202
2203
2204
2205 template <int dim, int spacedim>
2207 Vector<dim, spacedim>::gradient(const unsigned int shape_function,
2208 const unsigned int q_point) const
2209 {
2210 AssertIndexRange(shape_function, fe_values->fe->n_dofs_per_cell());
2211 Assert(fe_values->update_flags & update_gradients,
2213 "update_gradients")));
2214
2215 // same as for the scalar case except that we have one more index
2216 const int snc =
2217 shape_function_data[shape_function].single_nonzero_component;
2218 if (snc == -2)
2219 return gradient_type();
2220 else if (snc != -1)
2221 {
2222 gradient_type return_value;
2223 return_value[shape_function_data[shape_function]
2224 .single_nonzero_component_index] =
2225 fe_values->finite_element_output.shape_gradients[snc][q_point];
2226 return return_value;
2227 }
2228 else
2229 {
2230 gradient_type return_value;
2231 for (unsigned int d = 0; d < spacedim; ++d)
2232 if (shape_function_data[shape_function]
2233 .is_nonzero_shape_function_component[d])
2234 return_value[d] =
2235 fe_values->finite_element_output.shape_gradients
2236 [shape_function_data[shape_function].row_index[d]][q_point];
2237
2238 return return_value;
2239 }
2240 }
2241
2242
2243
2244 template <int dim, int spacedim>
2246 Vector<dim, spacedim>::divergence(const unsigned int shape_function,
2247 const unsigned int q_point) const
2248 {
2249 // this function works like in the case above
2250 AssertIndexRange(shape_function, fe_values->fe->n_dofs_per_cell());
2251 Assert(fe_values->update_flags & update_gradients,
2253 "update_gradients")));
2254
2255 // same as for the scalar case except that we have one more index
2256 const int snc =
2257 shape_function_data[shape_function].single_nonzero_component;
2258 if (snc == -2)
2259 return divergence_type();
2260 else if (snc != -1)
2261 return fe_values->finite_element_output
2262 .shape_gradients[snc][q_point][shape_function_data[shape_function]
2263 .single_nonzero_component_index];
2264 else
2265 {
2266 divergence_type return_value = 0;
2267 for (unsigned int d = 0; d < spacedim; ++d)
2268 if (shape_function_data[shape_function]
2269 .is_nonzero_shape_function_component[d])
2270 return_value +=
2271 fe_values->finite_element_output.shape_gradients
2272 [shape_function_data[shape_function].row_index[d]][q_point][d];
2273
2274 return return_value;
2275 }
2276 }
2277
2278
2279
2280 template <int dim, int spacedim>
2281 inline typename Vector<dim, spacedim>::curl_type
2282 Vector<dim, spacedim>::curl(const unsigned int shape_function,
2283 const unsigned int q_point) const
2284 {
2285 // this function works like in the case above
2286
2287 AssertIndexRange(shape_function, fe_values->fe->n_dofs_per_cell());
2288 Assert(fe_values->update_flags & update_gradients,
2290 "update_gradients")));
2291 // same as for the scalar case except that we have one more index
2292 const int snc =
2293 shape_function_data[shape_function].single_nonzero_component;
2294
2295 if (snc == -2)
2296 return curl_type();
2297
2298 else
2299 {
2300 if constexpr (spacedim == 1)
2301 {
2302 (void)q_point;
2303 Assert(false,
2304 ExcMessage(
2305 "Computing the curl in 1d is not a useful operation"));
2306 return curl_type();
2307 }
2308 else if constexpr (spacedim == 2)
2309 {
2310 if (snc != -1)
2311 {
2312 curl_type return_value;
2313
2314 // the single nonzero component can only be zero or one in 2d
2315 if (shape_function_data[shape_function]
2316 .single_nonzero_component_index == 0)
2317 return_value = -1.0 * fe_values->finite_element_output
2318 .shape_gradients[snc][q_point][1];
2319 else
2320 return_value = fe_values->finite_element_output
2321 .shape_gradients[snc][q_point][0];
2322
2323 return return_value;
2324 }
2325 else
2326 {
2327 curl_type return_value;
2328
2329 return_value = 0.0;
2330
2331 if (shape_function_data[shape_function]
2332 .is_nonzero_shape_function_component[0])
2333 return_value -=
2334 fe_values->finite_element_output
2335 .shape_gradients[shape_function_data[shape_function]
2336 .row_index[0]][q_point][1];
2337
2338 if (shape_function_data[shape_function]
2339 .is_nonzero_shape_function_component[1])
2340 return_value +=
2341 fe_values->finite_element_output
2342 .shape_gradients[shape_function_data[shape_function]
2343 .row_index[1]][q_point][0];
2344
2345 return return_value;
2346 }
2347 }
2348 else if constexpr (spacedim == 3)
2349 {
2350 if (snc != -1)
2351 {
2352 curl_type return_value;
2353
2354 switch (shape_function_data[shape_function]
2355 .single_nonzero_component_index)
2356 {
2357 case 0:
2358 {
2359 return_value[0] = 0;
2360 return_value[1] = fe_values->finite_element_output
2361 .shape_gradients[snc][q_point][2];
2362 return_value[2] =
2363 -1.0 * fe_values->finite_element_output
2364 .shape_gradients[snc][q_point][1];
2365 return return_value;
2366 }
2367
2368 case 1:
2369 {
2370 return_value[0] =
2371 -1.0 * fe_values->finite_element_output
2372 .shape_gradients[snc][q_point][2];
2373 return_value[1] = 0;
2374 return_value[2] = fe_values->finite_element_output
2375 .shape_gradients[snc][q_point][0];
2376 return return_value;
2377 }
2378
2379 default:
2380 {
2381 return_value[0] = fe_values->finite_element_output
2382 .shape_gradients[snc][q_point][1];
2383 return_value[1] =
2384 -1.0 * fe_values->finite_element_output
2385 .shape_gradients[snc][q_point][0];
2386 return_value[2] = 0;
2387 return return_value;
2388 }
2389 }
2390 }
2391 else
2392 {
2393 curl_type return_value;
2394
2395 for (unsigned int i = 0; i < dim; ++i)
2396 return_value[i] = 0.0;
2397
2398 if (shape_function_data[shape_function]
2399 .is_nonzero_shape_function_component[0])
2400 {
2401 return_value[1] +=
2402 fe_values->finite_element_output
2403 .shape_gradients[shape_function_data[shape_function]
2404 .row_index[0]][q_point][2];
2405 return_value[2] -=
2406 fe_values->finite_element_output
2407 .shape_gradients[shape_function_data[shape_function]
2408 .row_index[0]][q_point][1];
2409 }
2410
2411 if (shape_function_data[shape_function]
2412 .is_nonzero_shape_function_component[1])
2413 {
2414 return_value[0] -=
2415 fe_values->finite_element_output
2416 .shape_gradients[shape_function_data[shape_function]
2417 .row_index[1]][q_point][2];
2418 return_value[2] +=
2419 fe_values->finite_element_output
2420 .shape_gradients[shape_function_data[shape_function]
2421 .row_index[1]][q_point][0];
2422 }
2423
2424 if (shape_function_data[shape_function]
2425 .is_nonzero_shape_function_component[2])
2426 {
2427 return_value[0] +=
2428 fe_values->finite_element_output
2429 .shape_gradients[shape_function_data[shape_function]
2430 .row_index[2]][q_point][1];
2431 return_value[1] -=
2432 fe_values->finite_element_output
2433 .shape_gradients[shape_function_data[shape_function]
2434 .row_index[2]][q_point][0];
2435 }
2436
2437 return return_value;
2438 }
2439 }
2440 }
2441 // should not end up here
2443 return curl_type();
2444 }
2445
2446
2447
2448 template <int dim, int spacedim>
2450 Vector<dim, spacedim>::hessian(const unsigned int shape_function,
2451 const unsigned int q_point) const
2452 {
2453 // this function works like in the case above
2454 AssertIndexRange(shape_function, fe_values->fe->n_dofs_per_cell());
2455 Assert(fe_values->update_flags & update_hessians,
2457 "update_hessians")));
2458
2459 // same as for the scalar case except that we have one more index
2460 const int snc =
2461 shape_function_data[shape_function].single_nonzero_component;
2462 if (snc == -2)
2463 return hessian_type();
2464 else if (snc != -1)
2465 {
2466 hessian_type return_value;
2467 return_value[shape_function_data[shape_function]
2468 .single_nonzero_component_index] =
2469 fe_values->finite_element_output.shape_hessians[snc][q_point];
2470 return return_value;
2471 }
2472 else
2473 {
2474 hessian_type return_value;
2475 for (unsigned int d = 0; d < spacedim; ++d)
2476 if (shape_function_data[shape_function]
2477 .is_nonzero_shape_function_component[d])
2478 return_value[d] =
2479 fe_values->finite_element_output.shape_hessians
2480 [shape_function_data[shape_function].row_index[d]][q_point];
2481
2482 return return_value;
2483 }
2484 }
2485
2486
2487
2488 template <int dim, int spacedim>
2490 Vector<dim, spacedim>::third_derivative(const unsigned int shape_function,
2491 const unsigned int q_point) const
2492 {
2493 // this function works like in the case above
2494 AssertIndexRange(shape_function, fe_values->fe->n_dofs_per_cell());
2495 Assert(fe_values->update_flags & update_3rd_derivatives,
2497 "update_3rd_derivatives")));
2498
2499 // same as for the scalar case except that we have one more index
2500 const int snc =
2501 shape_function_data[shape_function].single_nonzero_component;
2502 if (snc == -2)
2503 return third_derivative_type();
2504 else if (snc != -1)
2505 {
2506 third_derivative_type return_value;
2507 return_value[shape_function_data[shape_function]
2508 .single_nonzero_component_index] =
2509 fe_values->finite_element_output.shape_3rd_derivatives[snc][q_point];
2510 return return_value;
2511 }
2512 else
2513 {
2514 third_derivative_type return_value;
2515 for (unsigned int d = 0; d < spacedim; ++d)
2516 if (shape_function_data[shape_function]
2517 .is_nonzero_shape_function_component[d])
2518 return_value[d] =
2519 fe_values->finite_element_output.shape_3rd_derivatives
2520 [shape_function_data[shape_function].row_index[d]][q_point];
2521
2522 return return_value;
2523 }
2524 }
2525
2526
2527
2528 namespace internal
2529 {
2534 inline ::SymmetricTensor<2, 1>
2535 symmetrize_single_row(const unsigned int n, const ::Tensor<1, 1> &t)
2536 {
2537 AssertIndexRange(n, 1);
2538 (void)n;
2539
2540 return {{t[0]}};
2541 }
2542
2543
2544
2545 inline ::SymmetricTensor<2, 2>
2546 symmetrize_single_row(const unsigned int n, const ::Tensor<1, 2> &t)
2547 {
2548 switch (n)
2549 {
2550 case 0:
2551 {
2552 return {{t[0], 0, t[1] / 2}};
2553 }
2554 case 1:
2555 {
2556 return {{0, t[1], t[0] / 2}};
2557 }
2558 default:
2559 {
2560 AssertIndexRange(n, 2);
2561 return {};
2562 }
2563 }
2564 }
2565
2566
2567
2568 inline ::SymmetricTensor<2, 3>
2569 symmetrize_single_row(const unsigned int n, const ::Tensor<1, 3> &t)
2570 {
2571 switch (n)
2572 {
2573 case 0:
2574 {
2575 return {{t[0], 0, 0, t[1] / 2, t[2] / 2, 0}};
2576 }
2577 case 1:
2578 {
2579 return {{0, t[1], 0, t[0] / 2, 0, t[2] / 2}};
2580 }
2581 case 2:
2582 {
2583 return {{0, 0, t[2], 0, t[0] / 2, t[1] / 2}};
2584 }
2585 default:
2586 {
2587 AssertIndexRange(n, 3);
2588 return {};
2589 }
2590 }
2591 }
2592 } // namespace internal
2593
2594
2595
2596 template <int dim, int spacedim>
2598 Vector<dim, spacedim>::symmetric_gradient(const unsigned int shape_function,
2599 const unsigned int q_point) const
2600 {
2601 AssertIndexRange(shape_function, fe_values->fe->n_dofs_per_cell());
2602 Assert(fe_values->update_flags & update_gradients,
2604 "update_gradients")));
2605
2606 // same as for the scalar case except that we have one more index
2607 const int snc =
2608 shape_function_data[shape_function].single_nonzero_component;
2609 if (snc == -2)
2610 return symmetric_gradient_type();
2611 else if (snc != -1)
2612 return internal::symmetrize_single_row(
2613 shape_function_data[shape_function].single_nonzero_component_index,
2614 fe_values->finite_element_output.shape_gradients[snc][q_point]);
2615 else
2616 {
2617 gradient_type return_value;
2618 for (unsigned int d = 0; d < spacedim; ++d)
2619 if (shape_function_data[shape_function]
2620 .is_nonzero_shape_function_component[d])
2621 return_value[d] =
2622 fe_values->finite_element_output.shape_gradients
2623 [shape_function_data[shape_function].row_index[d]][q_point];
2624
2625 return symmetrize(return_value);
2626 }
2627 }
2628
2629
2630
2631 template <int dim, int spacedim>
2633 SymmetricTensor<2, dim, spacedim>::value(const unsigned int shape_function,
2634 const unsigned int q_point) const
2635 {
2636 AssertIndexRange(shape_function, fe_values->fe->n_dofs_per_cell());
2637 Assert(fe_values->update_flags & update_values,
2639 "update_values")));
2640
2641 // similar to the vector case where we have more then one index and we need
2642 // to convert between unrolled and component indexing for tensors
2643 const int snc =
2644 shape_function_data[shape_function].single_nonzero_component;
2645
2646 if (snc == -2)
2647 {
2648 // shape function is zero for the selected components
2649 return value_type();
2650 }
2651 else if (snc != -1)
2652 {
2653 value_type return_value;
2654 const unsigned int comp =
2655 shape_function_data[shape_function].single_nonzero_component_index;
2656 return_value[value_type::unrolled_to_component_indices(comp)] =
2657 fe_values->finite_element_output.shape_values(snc, q_point);
2658 return return_value;
2659 }
2660 else
2661 {
2662 value_type return_value;
2663 for (unsigned int d = 0; d < value_type::n_independent_components; ++d)
2664 if (shape_function_data[shape_function]
2665 .is_nonzero_shape_function_component[d])
2666 return_value[value_type::unrolled_to_component_indices(d)] =
2667 fe_values->finite_element_output.shape_values(
2668 shape_function_data[shape_function].row_index[d], q_point);
2669 return return_value;
2670 }
2671 }
2672
2673
2674
2675 template <int dim, int spacedim>
2678 const unsigned int shape_function,
2679 const unsigned int q_point) const
2680 {
2681 AssertIndexRange(shape_function, fe_values->fe->n_dofs_per_cell());
2682 Assert(fe_values->update_flags & update_gradients,
2684 "update_gradients")));
2685
2686 const int snc =
2687 shape_function_data[shape_function].single_nonzero_component;
2688
2689 if (snc == -2)
2690 {
2691 // shape function is zero for the selected components
2692 return divergence_type();
2693 }
2694 else if (snc != -1)
2695 {
2696 // we have a single non-zero component when the symmetric tensor is
2697 // represented in unrolled form. this implies we potentially have
2698 // two non-zero components when represented in component form! we
2699 // will only have one non-zero entry if the non-zero component lies on
2700 // the diagonal of the tensor.
2701 //
2702 // the divergence of a second-order tensor is a first order tensor.
2703 //
2704 // assume the second-order tensor is A with components A_{ij}. then
2705 // A_{ij} = A_{ji} and there is only one (if diagonal) or two non-zero
2706 // entries in the tensorial representation. define the
2707 // divergence as:
2708 // b_i \dealcoloneq \dfrac{\partial phi_{ij}}{\partial x_j}.
2709 // (which is incidentally also
2710 // b_j \dealcoloneq \dfrac{\partial phi_{ij}}{\partial x_i}).
2711 // In both cases, a sum is implied.
2712 //
2713 // Now, we know the nonzero component in unrolled form: it is indicated
2714 // by 'snc'. we can figure out which tensor components belong to this:
2715 const unsigned int comp =
2716 shape_function_data[shape_function].single_nonzero_component_index;
2717 const unsigned int ii =
2718 value_type::unrolled_to_component_indices(comp)[0];
2719 const unsigned int jj =
2720 value_type::unrolled_to_component_indices(comp)[1];
2721
2722 // given the form of the divergence above, if ii=jj there is only a
2723 // single nonzero component of the full tensor and the gradient
2724 // equals
2725 // b_ii \dealcoloneq \dfrac{\partial phi_{ii,ii}}{\partial x_ii}.
2726 // all other entries of 'b' are zero
2727 //
2728 // on the other hand, if ii!=jj, then there are two nonzero entries in
2729 // the full tensor and
2730 // b_ii \dealcoloneq \dfrac{\partial phi_{ii,jj}}{\partial x_ii}.
2731 // b_jj \dealcoloneq \dfrac{\partial phi_{ii,jj}}{\partial x_jj}.
2732 // again, all other entries of 'b' are zero
2733 const ::Tensor<1, spacedim> &phi_grad =
2734 fe_values->finite_element_output.shape_gradients[snc][q_point];
2735
2736 divergence_type return_value;
2737 return_value[ii] = phi_grad[jj];
2738
2739 if (ii != jj)
2740 return_value[jj] = phi_grad[ii];
2741
2742 return return_value;
2743 }
2744 else
2745 {
2747 divergence_type return_value;
2748 return return_value;
2749 }
2750 }
2751
2752
2753
2754 template <int dim, int spacedim>
2756 Tensor<2, dim, spacedim>::value(const unsigned int shape_function,
2757 const unsigned int q_point) const
2758 {
2759 AssertIndexRange(shape_function, fe_values->fe->n_dofs_per_cell());
2760 Assert(fe_values->update_flags & update_values,
2762 "update_values")));
2763
2764 // similar to the vector case where we have more then one index and we need
2765 // to convert between unrolled and component indexing for tensors
2766 const int snc =
2767 shape_function_data[shape_function].single_nonzero_component;
2768
2769 if (snc == -2)
2770 {
2771 // shape function is zero for the selected components
2772 return value_type();
2773 }
2774 else if (snc != -1)
2775 {
2776 value_type return_value;
2777 const unsigned int comp =
2778 shape_function_data[shape_function].single_nonzero_component_index;
2779 const TableIndices<2> indices =
2781 return_value[indices] =
2782 fe_values->finite_element_output.shape_values(snc, q_point);
2783 return return_value;
2784 }
2785 else
2786 {
2787 value_type return_value;
2788 for (unsigned int d = 0; d < dim * dim; ++d)
2789 if (shape_function_data[shape_function]
2790 .is_nonzero_shape_function_component[d])
2791 {
2792 const TableIndices<2> indices =
2794 return_value[indices] =
2795 fe_values->finite_element_output.shape_values(
2796 shape_function_data[shape_function].row_index[d], q_point);
2797 }
2798 return return_value;
2799 }
2800 }
2801
2802
2803
2804 template <int dim, int spacedim>
2806 Tensor<2, dim, spacedim>::divergence(const unsigned int shape_function,
2807 const unsigned int q_point) const
2808 {
2809 AssertIndexRange(shape_function, fe_values->fe->n_dofs_per_cell());
2810 Assert(fe_values->update_flags & update_gradients,
2812 "update_gradients")));
2813
2814 const int snc =
2815 shape_function_data[shape_function].single_nonzero_component;
2816
2817 if (snc == -2)
2818 {
2819 // shape function is zero for the selected components
2820 return divergence_type();
2821 }
2822 else if (snc != -1)
2823 {
2824 // we have a single non-zero component when the tensor is
2825 // represented in unrolled form.
2826 //
2827 // the divergence of a second-order tensor is a first order tensor.
2828 //
2829 // assume the second-order tensor is A with components A_{ij},
2830 // then divergence is d_i := \frac{\partial A_{ij}}{\partial x_j}
2831 //
2832 // Now, we know the nonzero component in unrolled form: it is indicated
2833 // by 'snc'. we can figure out which tensor components belong to this:
2834 const unsigned int comp =
2835 shape_function_data[shape_function].single_nonzero_component_index;
2836 const TableIndices<2> indices =
2838 const unsigned int ii = indices[0];
2839 const unsigned int jj = indices[1];
2840
2841 const ::Tensor<1, spacedim> &phi_grad =
2842 fe_values->finite_element_output.shape_gradients[snc][q_point];
2843
2844 divergence_type return_value;
2845 // note that we contract \nabla from the right
2846 return_value[ii] = phi_grad[jj];
2847
2848 return return_value;
2849 }
2850 else
2851 {
2853 divergence_type return_value;
2854 return return_value;
2855 }
2856 }
2857
2858
2859
2860 template <int dim, int spacedim>
2862 Tensor<2, dim, spacedim>::gradient(const unsigned int shape_function,
2863 const unsigned int q_point) const
2864 {
2865 AssertIndexRange(shape_function, fe_values->fe->n_dofs_per_cell());
2866 Assert(fe_values->update_flags & update_gradients,
2868 "update_gradients")));
2869
2870 const int snc =
2871 shape_function_data[shape_function].single_nonzero_component;
2872
2873 if (snc == -2)
2874 {
2875 // shape function is zero for the selected components
2876 return gradient_type();
2877 }
2878 else if (snc != -1)
2879 {
2880 // we have a single non-zero component when the tensor is
2881 // represented in unrolled form.
2882 //
2883 // the gradient of a second-order tensor is a third order tensor.
2884 //
2885 // assume the second-order tensor is A with components A_{ij},
2886 // then gradient is B_{ijk} := \frac{\partial A_{ij}}{\partial x_k}
2887 //
2888 // Now, we know the nonzero component in unrolled form: it is indicated
2889 // by 'snc'. we can figure out which tensor components belong to this:
2890 const unsigned int comp =
2891 shape_function_data[shape_function].single_nonzero_component_index;
2892 const TableIndices<2> indices =
2894 const unsigned int ii = indices[0];
2895 const unsigned int jj = indices[1];
2896
2897 const ::Tensor<1, spacedim> &phi_grad =
2898 fe_values->finite_element_output.shape_gradients[snc][q_point];
2899
2900 gradient_type return_value;
2901 return_value[ii][jj] = phi_grad;
2902
2903 return return_value;
2904 }
2905 else
2906 {
2908 gradient_type return_value;
2909 return return_value;
2910 }
2911 }
2912} // namespace FEValuesViews
2913
2914#endif
2915
2917
2918#endif
void get_function_third_derivatives_from_local_dof_values(const InputVector &dof_values, std::vector< solution_third_derivative_type< typename InputVector::value_type > > &third_derivatives) const
Scalar & operator=(const Scalar< dim, spacedim > &)=delete
void get_function_laplacians(const ReadVector< Number > &fe_function, std::vector< solution_laplacian_type< Number > > &laplacians) const
typename ProductType< Number, hessian_type >::type solution_hessian_type
value_type value(const unsigned int shape_function, const unsigned int q_point) const
void get_function_hessians_from_local_dof_values(const InputVector &dof_values, std::vector< solution_hessian_type< typename InputVector::value_type > > &hessians) const
void get_function_values_from_local_dof_values(const InputVector &dof_values, std::vector< solution_value_type< typename InputVector::value_type > > &values) const
gradient_type gradient(const unsigned int shape_function, const unsigned int q_point) const
std::vector< ShapeFunctionData > shape_function_data
void get_function_gradients(const ReadVector< Number > &fe_function, std::vector< solution_gradient_type< Number > > &gradients) const
typename ProductType< Number, value_type >::type solution_laplacian_type
Scalar & operator=(Scalar< dim, spacedim > &&) noexcept=default
third_derivative_type third_derivative(const unsigned int shape_function, const unsigned int q_point) const
ObserverPointer< const FEValuesBase< dim, spacedim > > fe_values
typename ProductType< Number, third_derivative_type >::type solution_third_derivative_type
typename ProductType< Number, value_type >::type solution_value_type
typename ProductType< Number, gradient_type >::type solution_gradient_type
void get_function_values(const ReadVector< Number > &fe_function, std::vector< solution_value_type< Number > > &values) const
void get_function_laplacians_from_local_dof_values(const InputVector &dof_values, std::vector< solution_laplacian_type< typename InputVector::value_type > > &laplacians) const
void get_function_gradients_from_local_dof_values(const InputVector &dof_values, std::vector< solution_gradient_type< typename InputVector::value_type > > &gradients) const
Scalar(Scalar< dim, spacedim > &&)=default
Scalar(const Scalar< dim, spacedim > &)=delete
hessian_type hessian(const unsigned int shape_function, const unsigned int q_point) const
void get_function_third_derivatives(const ReadVector< Number > &fe_function, std::vector< solution_third_derivative_type< Number > > &third_derivatives) const
void get_function_hessians(const ReadVector< Number > &fe_function, std::vector< solution_hessian_type< Number > > &hessians) const
SymmetricTensor(const SymmetricTensor< 2, dim, spacedim > &)=delete
SymmetricTensor(SymmetricTensor< 2, dim, spacedim > &&)=default
SymmetricTensor & operator=(SymmetricTensor< 2, dim, spacedim > &&) noexcept=default
typename ProductType< Number, value_type >::type solution_value_type
SymmetricTensor & operator=(const SymmetricTensor< 2, dim, spacedim > &)=delete
typename ProductType< Number, divergence_type >::type solution_divergence_type
typename ProductType< Number, value_type >::type solution_value_type
typename ProductType< Number, divergence_type >::type solution_divergence_type
typename ProductType< Number, gradient_type >::type solution_gradient_type
divergence_type divergence(const unsigned int shape_function, const unsigned int q_point) const
Tensor(const Tensor< 2, dim, spacedim > &)=delete
ObserverPointer< const FEValuesBase< dim, spacedim > > fe_values
Tensor & operator=(const Tensor< 2, dim, spacedim > &)=delete
Tensor & operator=(Tensor< 2, dim, spacedim > &&)=default
value_type value(const unsigned int shape_function, const unsigned int q_point) const
gradient_type gradient(const unsigned int shape_function, const unsigned int q_point) const
Tensor(Tensor< 2, dim, spacedim > &&)=default
std::vector< ShapeFunctionData > shape_function_data
typename ProductType< Number, third_derivative_type >::type solution_third_derivative_type
typename ProductType< Number, divergence_type >::type solution_divergence_type
Vector(const Vector< dim, spacedim > &)=delete
unsigned int first_vector_component
hessian_type hessian(const unsigned int shape_function, const unsigned int q_point) const
typename ProductType< Number, hessian_type >::type solution_hessian_type
Vector & operator=(Vector< dim, spacedim > &&)=default
typename ProductType< Number, symmetric_gradient_type >::type solution_symmetric_gradient_type
gradient_type gradient(const unsigned int shape_function, const unsigned int q_point) const
divergence_type divergence(const unsigned int shape_function, const unsigned int q_point) const
Vector & operator=(const Vector< dim, spacedim > &)=delete
third_derivative_type third_derivative(const unsigned int shape_function, const unsigned int q_point) const
typename ProductType< Number, gradient_type >::type solution_gradient_type
ObserverPointer< const FEValuesBase< dim, spacedim > > fe_values
typename ProductType< Number, value_type >::type solution_value_type
symmetric_gradient_type symmetric_gradient(const unsigned int shape_function, const unsigned int q_point) const
typename ::internal::CurlType< spacedim > curl_type
Vector(Vector< dim, spacedim > &&)=default
typename ProductType< Number, curl_type >::type solution_curl_type
std::vector< ShapeFunctionData > shape_function_data
value_type value(const unsigned int shape_function, const unsigned int q_point) const
curl_type curl(const unsigned int shape_function, const unsigned int q_point) const
typename ProductType< Number, value_type >::type solution_laplacian_type
Definition lazy.h:133
std::conditional_t< rank_==1, Number, Tensor< rank_ - 1, dim, Number > > value_type
Definition tensor.h:505
static constexpr TableIndices< rank_ > unrolled_to_component_indices(const unsigned int i)
Number value_type
Definition vector.h:115
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:38
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:39
#define DEAL_II_ASSERT_UNREACHABLE()
#define DEAL_II_NOT_IMPLEMENTED()
#define Assert(cond, exc)
#define AssertIndexRange(index, range)
static ::ExceptionBase & ExcMessage(std::string arg1)
@ update_hessians
Second derivatives of shape functions.
@ update_values
Shape function values.
@ update_3rd_derivatives
Third derivatives of shape functions.
@ update_gradients
Shape function gradients.
typename ::internal::FEValuesViews::ViewType< dim, spacedim, Extractor >::type View
SymmetricTensor< 2, dim, Number > d(const Tensor< 2, dim, Number > &F, const Tensor< 2, dim, Number > &dF_dt)
std::conditional_t< dim==3, Tensor< 1, 3, NumberType >, std::conditional_t< dim==2, NumberType, std::monostate > > CurlType
STL namespace.
typename internal::ProductTypeImpl< std::decay_t< T >, std::decay_t< U > >::type type
std::vector< Lazy<::FEValuesViews::Vector< dim, spacedim > > > vectors
std::vector< Lazy<::FEValuesViews::SymmetricTensor< 2, dim, spacedim > > > symmetric_second_order_tensors
std::vector< Lazy<::FEValuesViews::Tensor< 2, dim, spacedim > > > second_order_tensors
std::vector< Lazy<::FEValuesViews::Scalar< dim, spacedim > > > scalars
constexpr SymmetricTensor< 2, dim, Number > symmetrize(const Tensor< 2, dim, Number > &t)