Program Listing for File reference_cross_section.cc¶
↰ Return to documentation for file (source/reference_cross_section.cc)
// ---------------------------------------------------------------------
//
// Copyright (C) 2024 by Luca Heltai
//
// This file is part of the ImmersX application, based on
// the deal.II library.
//
// The ImmersX application is free software; you can use
// it, redistribute it, and/or modify it under the terms of the Apache-2.0
// License WITH LLVM-exception as published by the Free Software Foundation;
// either version 3.0 of the License, or (at your option) any later version. The
// full text of the license can be found in the file LICENSE.md at the top level
// of the ImmersX distribution.
//
// ---------------------------------------------------------------------
#include "reference_cross_section.h"
#include <deal.II/base/function.h>
#include <deal.II/dofs/dof_tools.h>
#include <deal.II/fe/fe_q.h>
#include <deal.II/fe/fe_values.h>
#include <deal.II/grid/grid_generator.h>
#include <deal.II/grid/manifold_lib.h>
#include <deal.II/lac/dynamic_sparsity_pattern.h>
#include <deal.II/lac/lapack_full_matrix.h>
#include <deal.II/numerics/matrix_tools.h>
#include <deal.II/numerics/vector_tools.h>
#include <deal.II/physics/transformations.h>
#include <deal.II/physics/vector_relations.h>
#include <string>
template <int dim, int spacedim, int n_components>
ReferenceCrossSection<dim, spacedim, n_components>::ReferenceCrossSection(
const ReferenceCrossSectionParameters<dim, spacedim, n_components> &par)
: par(par)
, polynomials(par.inclusion_degree)
, quadrature_formula(2 * par.inclusion_degree + 1)
, fe(FE_Q<dim, spacedim>(std::max(par.inclusion_degree, 1u)), n_components)
, mapping(fe.degree)
, dof_handler(triangulation)
{
initialize();
}
template <int dim, int spacedim, int n_components>
void
ReferenceCrossSection<dim, spacedim, n_components>::make_grid()
{
if (par.inclusion_type == "hyper_ball")
{
if constexpr (dim == 1 && spacedim == 3)
{
// 1D inclusion in 3D space. A disk.
Triangulation<1, 2> tria_12;
GridGenerator::hyper_sphere(tria_12);
GridGenerator::flatten_triangulation(tria_12, triangulation);
triangulation.set_all_manifold_ids(1);
triangulation.set_manifold(1, PolarManifold<dim, spacedim>());
}
else if constexpr (dim == spacedim - 1)
GridGenerator::hyper_sphere(triangulation);
else
GridGenerator::hyper_ball(triangulation);
}
else if (par.inclusion_type == "hyper_cube")
GridGenerator::hyper_cube(triangulation, -1, 1);
else
{
AssertThrow(false,
ExcMessage("Unknown inclusion type. Please check the "
"parameter file."));
}
triangulation.refine_global(par.refinement_level);
}
template <int dim, int spacedim, int n_components>
void
ReferenceCrossSection<dim, spacedim, n_components>::setup_dofs()
{
dof_handler.distribute_dofs(fe);
DynamicSparsityPattern dsp(dof_handler.n_dofs(), dof_handler.n_dofs());
DoFTools::make_sparsity_pattern(dof_handler, dsp);
sparsity_pattern.copy_from(dsp);
sparsity_pattern.compress();
mass_matrix.reinit(sparsity_pattern);
MatrixTools::create_mass_matrix(dof_handler, quadrature_formula, mass_matrix);
const auto n_global_q_points =
triangulation.n_active_cells() * quadrature_formula.size();
std::vector<Point<spacedim>> points;
std::vector<double> weights;
points.reserve(n_global_q_points);
weights.reserve(n_global_q_points);
FEValues<dim, spacedim> fe_values(mapping,
fe,
quadrature_formula,
update_quadrature_points |
update_JxW_values);
reference_measure = 0;
for (const auto &cell : dof_handler.active_cell_iterators())
{
fe_values.reinit(cell);
points.insert(points.end(),
fe_values.get_quadrature_points().begin(),
fe_values.get_quadrature_points().end());
weights.insert(weights.end(),
fe_values.get_JxW_values().begin(),
fe_values.get_JxW_values().end());
for (const auto &w : fe_values.get_JxW_values())
reference_measure += w;
}
global_quadrature = Quadrature<spacedim>(points, weights);
}
template <int dim, int spacedim, int n_components>
void
ReferenceCrossSection<dim, spacedim, n_components>::compute_basis()
{
basis_functions.resize(polynomials.n() * n_components,
Vector<double>(dof_handler.n_dofs()));
for (unsigned int i = 0; i < basis_functions.size(); ++i)
{
const auto comp_i = i % n_components;
const auto poly_i = i / n_components;
VectorFunctionFromScalarFunctionObject<spacedim> function(
[&](const Point<spacedim> &p) {
return polynomials.compute_value(poly_i, p);
},
comp_i,
n_components);
VectorTools::interpolate(dof_handler, function, basis_functions[i]);
}
// Grahm-Schmidt orthogonalization
std::vector<bool> is_zero(basis_functions.size(), false);
for (unsigned int i = 0; i < basis_functions.size(); ++i)
{
for (unsigned int j = 0; j < i; ++j)
{
const auto coeff =
mass_matrix.matrix_scalar_product(basis_functions[i],
basis_functions[j]);
basis_functions[i].sadd(1,
-coeff / reference_measure,
basis_functions[j]);
}
const auto coeff = mass_matrix.matrix_scalar_product(basis_functions[i],
basis_functions[i]);
// If the basis functions are on a plane, and we are in 3d, they may be
// zero
if (coeff > 1e-10)
basis_functions[i] *= std::sqrt(reference_measure) / std::sqrt(coeff);
else
is_zero[i] = true;
}
// Remove the zero basis functions
for (int i = is_zero.size() - 1; i >= 0; --i)
if (is_zero[i] == true)
basis_functions.erase(basis_functions.begin() + i);
// functions
if (par.selected_coefficients.empty())
{
par.selected_coefficients.resize(basis_functions.size());
std::iota(par.selected_coefficients.begin(),
par.selected_coefficients.end(),
0);
}
else
{
for (const auto &index : par.selected_coefficients)
{
AssertThrow(index < basis_functions.size(),
ExcIndexRange(index, 0, basis_functions.size()));
}
}
selected_basis_functions.resize(par.selected_coefficients.size(),
Vector<double>(dof_handler.n_dofs()));
// Compute the selected basis functions as the rows of the inverse metric
// times the basis functions
for (unsigned int i = 0; i < par.selected_coefficients.size(); ++i)
{
selected_basis_functions[i] =
basis_functions[par.selected_coefficients[i]];
}
// Now compute the Matrix of the selected basis functions evaluated on the
// quadrature points
basis_functions_on_qpoints.reinit(global_quadrature.size(),
selected_basis_functions.size() *
n_components);
std::vector<Vector<double>> values(quadrature_formula.size(),
Vector<double>(fe.n_components()));
FEValues<dim, spacedim> fe_values(mapping,
fe,
quadrature_formula,
update_values);
for (const auto &cell : dof_handler.active_cell_iterators())
{
fe_values.reinit(cell);
const unsigned int shift =
cell->global_active_cell_index() * quadrature_formula.size();
for (unsigned int i = 0; i < selected_basis_functions.size(); ++i)
{
fe_values.get_function_values(selected_basis_functions[i], values);
for (unsigned int q = 0; q < quadrature_formula.size(); ++q)
{
for (unsigned int comp = 0; comp < n_components; ++comp)
{
basis_functions_on_qpoints(q + shift,
i * n_components + comp) =
values[q][comp];
}
}
}
}
}
template <int dim, int spacedim, int n_components>
double
ReferenceCrossSection<dim, spacedim, n_components>::measure(
const double scale) const
{
return reference_measure * std::pow(scale, dim);
};
template <int dim, int spacedim, int n_components>
auto
ReferenceCrossSection<dim, spacedim, n_components>::get_global_quadrature()
const -> const Quadrature<spacedim> &
{
return global_quadrature;
}
template <int dim, int spacedim, int n_components>
auto
ReferenceCrossSection<dim, spacedim, n_components>::get_basis_functions() const
-> const std::vector<Vector<double>> &
{
return selected_basis_functions;
}
template <int dim, int spacedim, int n_components>
const double &
ReferenceCrossSection<dim, spacedim, n_components>::shape_value(
const unsigned int i,
const unsigned int q,
const unsigned int comp) const
{
AssertIndexRange(i, selected_basis_functions.size());
AssertIndexRange(q, global_quadrature.size());
AssertIndexRange(comp, n_components);
// Check that the matrix is not empty
AssertThrow(!basis_functions_on_qpoints.empty(),
ExcMessage(
"The basis functions on quadrature points are empty."));
return basis_functions_on_qpoints(q, i * n_components + comp);
};
template <int dim, int spacedim, int n_components>
auto
ReferenceCrossSection<dim, spacedim, n_components>::get_mass_matrix() const
-> const SparseMatrix<double> &
{
return mass_matrix;
}
template <int dim, int spacedim, int n_components>
auto
ReferenceCrossSection<dim, spacedim, n_components>::n_selected_basis() const
-> unsigned int
{
return selected_basis_functions.size();
}
template <int dim, int spacedim, int n_components>
unsigned int
ReferenceCrossSection<dim, spacedim, n_components>::max_n_basis() const
{
return basis_functions.size();
}
template <int dim, int spacedim, int n_components>
ReferenceCrossSectionParameters<dim, spacedim, n_components>::
ReferenceCrossSectionParameters()
: ParameterAcceptor("Cross section")
{
add_parameter("Maximum inclusion degree", inclusion_degree);
add_parameter(
"Selected indices",
selected_coefficients,
"This allows one to select a subset of the components of the "
"basis functions used for the representation of the data "
"(boundary data or forcing data). Notice that these indices are "
"w.r.t. to the total number of components of the problem, that "
"is, dimension of the space P^{inclusion_degree} number of Fourier coefficients x number of vector "
"components. In particular any entry of this list must be in "
"the set [0,inclusion_degree*n_components). ");
add_parameter("Inclusion type", inclusion_type);
add_parameter("Refinement level", refinement_level);
}
template <int dim, int spacedim, int n_components>
Quadrature<spacedim>
ReferenceCrossSection<dim, spacedim, n_components>::get_transformed_quadrature(
const Point<spacedim> &new_origin,
const Tensor<1, spacedim> &new_vertical,
const double scale) const
{
AssertThrow(new_vertical.norm() > 0,
ExcMessage("The new vertical direction must be non-zero."));
Tensor<2, spacedim> rotation;
Tensor<1, spacedim> vertical;
vertical[spacedim - 1] = 1;
if constexpr (spacedim == 3)
{
Tensor<1, spacedim> axis = cross_product_3d(vertical, new_vertical);
const double axis_norm = axis.norm();
if (axis_norm < 1e-10)
{
// The two vectors are parallel, no rotation needed
for (unsigned int i = 0; i < spacedim; ++i)
rotation[i][i] = 1;
}
else
{
axis /= axis_norm;
double angle = Physics::VectorRelations::signed_angle(vertical,
new_vertical,
axis);
rotation =
Physics::Transformations::Rotations::rotation_matrix_3d(axis,
angle);
}
}
else if constexpr (spacedim == 2)
{
double angle = Physics::VectorRelations::angle(vertical, new_vertical);
rotation = Physics::Transformations::Rotations::rotation_matrix_2d(angle);
}
// Create transformed quadrature by applying the mapping to points and weights
std::vector<Point<spacedim>> transformed_points(global_quadrature.size());
std::vector<double> transformed_weights(global_quadrature.size());
const auto &original_points = global_quadrature.get_points();
const auto &original_weights = global_quadrature.get_weights();
// Calculate scale factor for weights
const double weight_scale_factor = std::pow(scale, dim);
for (unsigned int q = 0; q < original_points.size(); ++q)
{
transformed_points[q] =
rotation * (scale * original_points[q]) + new_origin;
// Scale the weight by scale^dim
transformed_weights[q] = original_weights[q] * weight_scale_factor;
}
return Quadrature<spacedim>(transformed_points, transformed_weights);
}
template <int dim, int spacedim, int n_components>
unsigned int
ReferenceCrossSection<dim, spacedim, n_components>::n_quadrature_points() const
{
return global_quadrature.size();
}
template <int dim, int spacedim, int n_components>
void
ReferenceCrossSection<dim, spacedim, n_components>::initialize()
{
dof_handler.clear();
triangulation.clear();
make_grid();
setup_dofs();
compute_basis();
}
// Scalar case
template class ReferenceCrossSection<1, 2, 1>;
template class ReferenceCrossSection<1, 3, 1>;
template class ReferenceCrossSection<2, 2, 1>;
template class ReferenceCrossSection<2, 3, 1>;
template class ReferenceCrossSection<3, 3, 1>;
// Vector case
template class ReferenceCrossSection<1, 2, 2>;
template class ReferenceCrossSection<1, 3, 3>;
template class ReferenceCrossSection<2, 2, 2>;
template class ReferenceCrossSection<2, 3, 3>;
template class ReferenceCrossSection<3, 3, 3>;
// Explicit instantiations for ReferenceCrossSectionParameters
// Scalar case
template struct ReferenceCrossSectionParameters<1, 2, 1>;
template struct ReferenceCrossSectionParameters<1, 3, 1>;
template struct ReferenceCrossSectionParameters<2, 2, 1>;
template struct ReferenceCrossSectionParameters<2, 3, 1>;
template struct ReferenceCrossSectionParameters<3, 3, 1>;
// Vector case
template struct ReferenceCrossSectionParameters<1, 2, 2>;
template struct ReferenceCrossSectionParameters<1, 3, 3>;
template struct ReferenceCrossSectionParameters<2, 2, 2>;
template struct ReferenceCrossSectionParameters<2, 3, 3>;
template struct ReferenceCrossSectionParameters<3, 3, 3>;