OGS
NumLib::LocalLinearLeastSquaresExtrapolator Class Reference

Detailed Description

Extrapolator doing a linear least squares extrapolation locally in each element.

The results of the element-wise least squares are averaged at each node.

Note

The described procedure is not least-squares optimal on the whole mesh! But the residuals are computed from the actual interpolation result that is being returned by this class.

The number of integration points in each element must be greater than or equal to the number of nodes of that element. This restriction is due to the use of the least squares which requires an exact or overdetermined equation system.

Definition at line 31 of file LocalLinearLeastSquaresExtrapolator.h.

#include <LocalLinearLeastSquaresExtrapolator.h>

Inheritance diagram for NumLib::LocalLinearLeastSquaresExtrapolator:
[legend]
Collaboration diagram for NumLib::LocalLinearLeastSquaresExtrapolator:
[legend]

Classes

struct  CachedData
 Stores a matrix and its Moore-Penrose pseudo-inverse. More...

Public Member Functions

 LocalLinearLeastSquaresExtrapolator (NumLib::LocalToGlobalIndexMap const &dof_table)
void extrapolate (const unsigned num_components, ExtrapolatableElementCollection const &extrapolatables, const double t, std::vector< GlobalVector * > const &x, std::vector< NumLib::LocalToGlobalIndexMap const * > const &dof_table) override
 Extrapolates the given property from the given local assemblers.
void calculateResiduals (const unsigned num_components, ExtrapolatableElementCollection const &extrapolatables, const double t, std::vector< GlobalVector * > const &x, std::vector< NumLib::LocalToGlobalIndexMap const * > const &dof_table) override
GlobalVector const & getNodalValues () const override
GlobalVector const & getElementResiduals () const override
Public Member Functions inherited from NumLib::Extrapolator
virtual ~Extrapolator ()=default

Private Member Functions

void extrapolateElement (std::size_t const element_index, const unsigned num_components, ExtrapolatableElementCollection const &extrapolatables, const double t, std::vector< GlobalVector * > const &x, std::vector< NumLib::LocalToGlobalIndexMap const * > const &dof_table, GlobalVector &counts)
 Extrapolate one element.
void calculateResidualElement (std::size_t const element_index, const unsigned num_components, ExtrapolatableElementCollection const &extrapolatables, const double t, std::vector< GlobalVector * > const &x, std::vector< NumLib::LocalToGlobalIndexMap const * > const &dof_table)
 Compute the residuals for one element.

Private Attributes

std::unique_ptr< GlobalVector_nodal_values
 extrapolated nodal values
std::unique_ptr< GlobalVector_residuals
 extrapolation residuals
NumLib::LocalToGlobalIndexMap const & _dof_table_single_component
 DOF table used for writing to global vectors.
std::vector< double > _integration_point_values_cache
 Avoids frequent reallocations.
std::map< std::pair< unsigned, unsigned >, CachedData_qr_decomposition_cache

Constructor & Destructor Documentation

◆ LocalLinearLeastSquaresExtrapolator()

NumLib::LocalLinearLeastSquaresExtrapolator::LocalLinearLeastSquaresExtrapolator ( NumLib::LocalToGlobalIndexMap const & dof_table)
explicit

Constructs a new instance.

Note
The dof_table must point to a d.o.f. table for one single-component variable.

Definition at line 18 of file LocalLinearLeastSquaresExtrapolator.cpp.

21{
22 /* Note in case the following assertion fails:
23 * If you copied the extrapolation code, for your processes from
24 * somewhere, note that the code from the groundwater flow process might
25 * not suit your needs: It is a special case and is therefore most
26 * likely too simplistic. You better adapt the extrapolation code from
27 * some more advanced process, like the TES process.
28 */
29 if (dof_table.getNumberOfGlobalComponents() != 1)
30 {
32 "The d.o.f. table passed must be for one variable that has only "
33 "one component!");
34 }
35}
#define OGS_FATAL(...)
Definition Error.h:19
NumLib::LocalToGlobalIndexMap const & _dof_table_single_component
DOF table used for writing to global vectors.

References _dof_table_single_component, and NumLib::LocalToGlobalIndexMap::getNumberOfGlobalComponents().

Member Function Documentation

◆ calculateResidualElement()

void NumLib::LocalLinearLeastSquaresExtrapolator::calculateResidualElement ( std::size_t const element_index,
const unsigned num_components,
ExtrapolatableElementCollection const & extrapolatables,
const double t,
std::vector< GlobalVector * > const & x,
std::vector< NumLib::LocalToGlobalIndexMap const * > const & dof_table )
private

Compute the residuals for one element.

Definition at line 274 of file LocalLinearLeastSquaresExtrapolator.cpp.

281{
282 auto const& int_pt_vals = extrapolatables.getIntegrationPointValues(
283 element_index, t, x, dof_table, _integration_point_values_cache);
284
285 auto const num_values = static_cast<unsigned>(int_pt_vals.size());
286 if (num_values % num_components != 0)
287 {
288 OGS_FATAL(
289 "The number of computed integration point values is not divisible "
290 "by the number of num_components. Maybe the computed property is "
291 "not a {:d}-component vector for each integration point.",
292 num_components);
293 }
294
295 // number of integration points in the element
296 const auto num_int_pts = num_values / num_components;
297
298 const auto& global_indices =
299 _dof_table_single_component(element_index, 0).rows;
300 const auto num_nodes = static_cast<unsigned>(global_indices.size());
301
302 auto const& interpolation_matrix =
303 _qr_decomposition_cache.find({num_nodes, num_int_pts})->second.A;
304
305 Eigen::VectorXd nodal_vals_element(num_nodes);
306 auto const int_pt_vals_mat =
307 MathLib::toMatrix(int_pt_vals, num_components, num_int_pts);
308
310 *_nodal_values); // For access in the for-loop.
311 for (unsigned comp = 0; comp < num_components; ++comp)
312 {
313 // filter nodal values of the current element
314 for (unsigned i = 0; i < num_nodes; ++i)
315 {
316 // TODO PETSc negative indices?
317 auto const idx = num_components * global_indices[i] + comp;
318 nodal_vals_element[i] = _nodal_values->get(idx);
319 }
320
321 double const residual = (interpolation_matrix * nodal_vals_element -
322 int_pt_vals_mat.row(comp).transpose())
323 .squaredNorm();
324
325 auto const eidx =
326 static_cast<GlobalIndexType>(num_components * element_index + comp);
327 // The residual is set to the root mean square value.
328 auto const root_mean_square = std::sqrt(residual / num_int_pts);
329 _residuals->set(eidx, root_mean_square);
330 }
331}
GlobalMatrix::IndexType GlobalIndexType
std::unique_ptr< GlobalVector > _nodal_values
extrapolated nodal values
std::unique_ptr< GlobalVector > _residuals
extrapolation residuals
std::vector< double > _integration_point_values_cache
Avoids frequent reallocations.
std::map< std::pair< unsigned, unsigned >, CachedData > _qr_decomposition_cache
void setLocalAccessibleVector(PETScVector const &x)
Definition LinAlg.cpp:20
Eigen::Map< const Matrix > toMatrix(std::vector< double > const &data, Eigen::MatrixXd::Index rows, Eigen::MatrixXd::Index cols)

References _dof_table_single_component, _integration_point_values_cache, _nodal_values, _qr_decomposition_cache, _residuals, NumLib::ExtrapolatableElementCollection::getIntegrationPointValues(), OGS_FATAL, MathLib::LinAlg::setLocalAccessibleVector(), and MathLib::toMatrix().

Referenced by calculateResiduals().

◆ calculateResiduals()

void NumLib::LocalLinearLeastSquaresExtrapolator::calculateResiduals ( const unsigned num_components,
ExtrapolatableElementCollection const & extrapolatables,
const double t,
std::vector< GlobalVector * > const & x,
std::vector< NumLib::LocalToGlobalIndexMap const * > const & dof_table )
overridevirtual

Computes residuals from the extrapolation of the given property.

The residuals are computed as element values.

Precondition
extrapolate() must have been called before with the same arguments.

The computed residuals are root-mean-square of the difference between the integration point values obtained from the local assemblers and the extrapolation results when interpolated back to the integration points again.

Implements NumLib::Extrapolator.

Definition at line 100 of file LocalLinearLeastSquaresExtrapolator.cpp.

106{
107 auto const num_element_dof_result = static_cast<GlobalIndexType>(
108 _dof_table_single_component.size() * num_components);
109
110 if (!_residuals || _residuals->size() != num_element_dof_result)
111 {
112#ifndef USE_PETSC
113 _residuals.reset(new GlobalVector{num_element_dof_result});
114#else
115 _residuals.reset(new GlobalVector{num_element_dof_result, false});
116#endif
117 }
118
119 if (static_cast<std::size_t>(num_element_dof_result) !=
120 extrapolatables.size() * num_components)
121 {
122 OGS_FATAL("mismatch in number of D.o.F.");
123 }
124
125 auto const size = extrapolatables.size();
126 for (std::size_t i = 0; i < size; ++i)
127 {
128 calculateResidualElement(i, num_components, extrapolatables, t, x,
129 dof_table);
130 }
132}
MathLib::EigenVector GlobalVector
void calculateResidualElement(std::size_t const element_index, const unsigned num_components, ExtrapolatableElementCollection const &extrapolatables, const double t, std::vector< GlobalVector * > const &x, std::vector< NumLib::LocalToGlobalIndexMap const * > const &dof_table)
Compute the residuals for one element.
void finalizeAssembly(PETScMatrix &A)
Definition LinAlg.cpp:191
constexpr int size(int const displacement_dim)
Vectorized tensor size for given displacement dimension.

References _dof_table_single_component, _residuals, calculateResidualElement(), MathLib::LinAlg::finalizeAssembly(), OGS_FATAL, and NumLib::ExtrapolatableElementCollection::size().

◆ extrapolate()

void NumLib::LocalLinearLeastSquaresExtrapolator::extrapolate ( const unsigned num_components,
ExtrapolatableElementCollection const & extrapolatables,
const double t,
std::vector< GlobalVector * > const & x,
std::vector< NumLib::LocalToGlobalIndexMap const * > const & dof_table )
overridevirtual

Extrapolates the given property from the given local assemblers.

Implements NumLib::Extrapolator.

Definition at line 37 of file LocalLinearLeastSquaresExtrapolator.cpp.

43{
44 auto const num_nodal_dof_result =
45 _dof_table_single_component.dofSizeWithoutGhosts() * num_components;
46
47 std::vector<GlobalIndexType> ghost_indices;
48 { // Create num_components times version of ghost_indices arranged by
49 // location. For example for 3 components and ghost_indices {5,6,10} we
50 // compute {15, 16, 17, 18, 19, 20, 30, 31, 32}.
51 auto const& single_component_ghost_indices =
52 _dof_table_single_component.getGhostIndices();
53 auto const single_component_ghost_indices_size =
54 single_component_ghost_indices.size();
55 ghost_indices.reserve(single_component_ghost_indices_size *
56 num_components);
57 for (unsigned i = 0; i < single_component_ghost_indices_size; ++i)
58 {
59 for (unsigned c = 0; c < num_components; ++c)
60 {
61 ghost_indices.push_back(
62 single_component_ghost_indices[i] * num_components + c);
63 }
64 }
65 }
66
67 if (!_nodal_values ||
68#ifdef USE_PETSC
69 _nodal_values->getLocalSize() + _nodal_values->getGhostSize()
70#else
71 _nodal_values->size()
72#endif
73 != static_cast<GlobalIndexType>(num_nodal_dof_result))
74 {
75 _nodal_values = MathLib::MatrixVectorTraits<GlobalVector>::newInstance(
76 {num_nodal_dof_result, num_nodal_dof_result, &ghost_indices,
77 nullptr});
78 }
79 _nodal_values->setZero();
80
81 // counts the writes to each nodal value, i.e., the summands in order to
82 // compute the average afterwards
83 auto counts =
84 MathLib::MatrixVectorTraits<GlobalVector>::newInstance(*_nodal_values);
85 counts->setZero();
86
87 auto const size = extrapolatables.size();
88 for (std::size_t i = 0; i < size; ++i)
89 {
90 extrapolateElement(i, num_components, extrapolatables, t, x, dof_table,
91 *counts);
92 }
95
97 *counts);
98}
void extrapolateElement(std::size_t const element_index, const unsigned num_components, ExtrapolatableElementCollection const &extrapolatables, const double t, std::vector< GlobalVector * > const &x, std::vector< NumLib::LocalToGlobalIndexMap const * > const &dof_table, GlobalVector &counts)
Extrapolate one element.
void componentwiseDivide(PETScVector &w, PETScVector const &x, PETScVector const &y)
Definition LinAlg.cpp:74

References _dof_table_single_component, _nodal_values, MathLib::LinAlg::componentwiseDivide(), extrapolateElement(), MathLib::LinAlg::finalizeAssembly(), and NumLib::ExtrapolatableElementCollection::size().

◆ extrapolateElement()

void NumLib::LocalLinearLeastSquaresExtrapolator::extrapolateElement ( std::size_t const element_index,
const unsigned num_components,
ExtrapolatableElementCollection const & extrapolatables,
const double t,
std::vector< GlobalVector * > const & x,
std::vector< NumLib::LocalToGlobalIndexMap const * > const & dof_table,
GlobalVector & counts )
private

Extrapolate one element.

Definition at line 134 of file LocalLinearLeastSquaresExtrapolator.cpp.

142{
143 auto const& integration_point_values =
144 extrapolatables.getIntegrationPointValues(
145 element_index, t, x, dof_table, _integration_point_values_cache);
146
147 // Empty vector means to ignore the values and not to change the counts.
148 if (integration_point_values.empty())
149 {
150 return;
151 }
152
153 auto const& N_0 = extrapolatables.getShapeMatrix(element_index, 0);
154 auto const num_nodes = static_cast<unsigned>(N_0.cols());
155 auto const num_values =
156 static_cast<unsigned>(integration_point_values.size());
157
158 if (num_values % num_components != 0)
159 {
160 OGS_FATAL(
161 "The number of computed integration point values is not divisible "
162 "by the number of num_components. Maybe the computed property is "
163 "not a {:d}-component vector for each integration point.",
164 num_components);
165 }
166
167 // number of integration points in the element
168 const auto num_int_pts = num_values / num_components;
169
170 if (num_int_pts < num_nodes)
171 {
172 OGS_FATAL(
173 "Least squares is not possible if there are more nodes than "
174 "integration points.");
175 }
176
177 auto const pair_it_inserted = _qr_decomposition_cache.emplace(
178 std::make_pair(num_nodes, num_int_pts), CachedData{});
179
180 auto& cached_data = pair_it_inserted.first->second;
181 if (pair_it_inserted.second)
182 {
183 DBUG("Computing new singular value decomposition");
184
185 // interpolation_matrix * nodal_values = integration_point_values
186 // We are going to pseudo-invert this relation now using singular value
187 // decomposition.
188 auto& interpolation_matrix = cached_data.A;
189 interpolation_matrix.resize(num_int_pts, num_nodes);
190
191 interpolation_matrix.row(0) = N_0;
192 for (unsigned int_pt = 1; int_pt < num_int_pts; ++int_pt)
193 {
194 auto const& shp_mat =
195 extrapolatables.getShapeMatrix(element_index, int_pt);
196 assert(shp_mat.cols() == num_nodes);
197
198 // copy shape matrix to extrapolation matrix row-wise
199 interpolation_matrix.row(int_pt) = shp_mat;
200 }
201
202 // JacobiSVD is extremely reliable, but fast only for small matrices.
203 // But we usually have small matrices and we don't compute very often.
204 // Cf.
205 // http://eigen.tuxfamily.org/dox/group__TopicLinearAlgebraDecompositions.html
206 //
207 // Decomposes interpolation_matrix = U S V^T.
208 Eigen::JacobiSVD<Eigen::MatrixXd> svd(
209 interpolation_matrix, Eigen::ComputeThinU | Eigen::ComputeThinV);
210
211 auto const& S = svd.singularValues();
212 auto const& U = svd.matrixU();
213 auto const& V = svd.matrixV();
214
215 // Compute and save the pseudo inverse V * S^{-1} * U^T.
216 auto const rank = svd.rank();
217 assert(rank == num_nodes);
218
219 // cf. http://eigen.tuxfamily.org/dox/JacobiSVD_8h_source.html
220 cached_data.A_pinv.noalias() = V.leftCols(rank) *
221 S.head(rank).asDiagonal().inverse() *
222 U.leftCols(rank).transpose();
223 }
224 else if (cached_data.A.row(0) != N_0)
225 {
226 OGS_FATAL("The cached and the passed shapematrices differ.");
227 }
228
229 auto const& global_indices =
230 _dof_table_single_component(element_index, 0).rows;
231
232 if (num_components == 1)
233 {
234 auto const integration_point_values_vec =
235 MathLib::toVector(integration_point_values);
236
237 // Apply the pre-computed pseudo-inverse.
238 Eigen::VectorXd const nodal_values =
239 cached_data.A_pinv * integration_point_values_vec;
240
241 // TODO does that give rise to PETSc problems? E.g., writing to ghost
242 // nodes? Furthermore: Is ghost nodes communication necessary for PETSc?
243 _nodal_values->add(global_indices, nodal_values);
244 counts.add(global_indices,
245 std::vector<double>(global_indices.size(), 1.0));
246 }
247 else
248 {
249 auto const integration_point_values_mat = MathLib::toMatrix(
250 integration_point_values, num_components, num_int_pts);
251
252 // Apply the pre-computed pseudo-inverse.
253 Eigen::MatrixXd const nodal_values =
254 cached_data.A_pinv * integration_point_values_mat.transpose();
255
256 std::vector<GlobalIndexType> indices;
257 indices.reserve(num_components * global_indices.size());
258
259 // _nodal_values is ordered location-wise
260 for (unsigned comp = 0; comp < num_components; ++comp)
261 {
262 transform(cbegin(global_indices), cend(global_indices),
263 back_inserter(indices),
264 [&](auto const i) { return num_components * i + comp; });
265 }
266
267 // Nodal_values are passed as a raw pointer, because PETScVector and
268 // EigenVector implementations differ slightly.
269 _nodal_values->add(indices, nodal_values.data());
270 counts.add(indices, std::vector<double>(indices.size(), 1.0));
271 }
272}
void DBUG(fmt::format_string< Args... > fmt, Args &&... args)
Definition Logging.h:22
void add(IndexType rowId, double v)
add entry
Definition EigenVector.h:70
Eigen::Map< const Vector > toVector(std::vector< double > const &data, Eigen::VectorXd::Index size)
Creates an Eigen mapped vector from the given data vector.
Stores a matrix and its Moore-Penrose pseudo-inverse.

References _dof_table_single_component, _integration_point_values_cache, _nodal_values, _qr_decomposition_cache, MathLib::EigenVector::add(), DBUG(), NumLib::ExtrapolatableElementCollection::getIntegrationPointValues(), NumLib::ExtrapolatableElementCollection::getShapeMatrix(), OGS_FATAL, MathLib::toMatrix(), and MathLib::toVector().

Referenced by extrapolate().

◆ getElementResiduals()

GlobalVector const & NumLib::LocalLinearLeastSquaresExtrapolator::getElementResiduals ( ) const
inlineoverridevirtual

Returns the extrapolation residuals.

Todo
Maybe write directly to a MeshProperty.

Implements NumLib::Extrapolator.

Definition at line 70 of file LocalLinearLeastSquaresExtrapolator.h.

71 {
72 return *_residuals;
73 }

References _residuals.

◆ getNodalValues()

GlobalVector const & NumLib::LocalLinearLeastSquaresExtrapolator::getNodalValues ( ) const
inlineoverridevirtual

Returns the extrapolated nodal values.

Todo
Maybe write directly to a MeshProperty.

Implements NumLib::Extrapolator.

Definition at line 65 of file LocalLinearLeastSquaresExtrapolator.h.

66 {
67 return *_nodal_values;
68 }

References _nodal_values.

Member Data Documentation

◆ _dof_table_single_component

NumLib::LocalToGlobalIndexMap const& NumLib::LocalLinearLeastSquaresExtrapolator::_dof_table_single_component
private

DOF table used for writing to global vectors.

Definition at line 97 of file LocalLinearLeastSquaresExtrapolator.h.

Referenced by LocalLinearLeastSquaresExtrapolator(), calculateResidualElement(), calculateResiduals(), extrapolate(), and extrapolateElement().

◆ _integration_point_values_cache

std::vector<double> NumLib::LocalLinearLeastSquaresExtrapolator::_integration_point_values_cache
private

Avoids frequent reallocations.

Definition at line 100 of file LocalLinearLeastSquaresExtrapolator.h.

Referenced by calculateResidualElement(), and extrapolateElement().

◆ _nodal_values

std::unique_ptr<GlobalVector> NumLib::LocalLinearLeastSquaresExtrapolator::_nodal_values
private

extrapolated nodal values

Definition at line 93 of file LocalLinearLeastSquaresExtrapolator.h.

Referenced by calculateResidualElement(), extrapolate(), extrapolateElement(), and getNodalValues().

◆ _qr_decomposition_cache

std::map<std::pair<unsigned, unsigned>, CachedData> NumLib::LocalLinearLeastSquaresExtrapolator::_qr_decomposition_cache
private

Maps (#nodes, #int_pts) to (N_0, QR decomposition), where N_0 is the shape matrix of the first integration point.

Note
It is assumed that the pair (#nodes, #int_pts) uniquely identifies the set of all shape matrices N for a mesh element (i.e., only N, not dN/dx etc.).
Todo
Add the element dimension as identifying criterion, or change to typeid.

Definition at line 122 of file LocalLinearLeastSquaresExtrapolator.h.

Referenced by calculateResidualElement(), and extrapolateElement().

◆ _residuals

std::unique_ptr<GlobalVector> NumLib::LocalLinearLeastSquaresExtrapolator::_residuals
private

extrapolation residuals

Definition at line 94 of file LocalLinearLeastSquaresExtrapolator.h.

Referenced by calculateResidualElement(), calculateResiduals(), and getElementResiduals().


The documentation for this class was generated from the following files: