Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MatrixFreeTools::compute_diagonal() and compute_matrix() for face and boundary integrals #17029

Merged
merged 1 commit into from
Jun 7, 2024

Conversation

peterrum
Copy link
Member

This feature was already requested multiple times (also on the mailing list).

@bangerth
Copy link
Member

Did you mean to perhaps merge some of the commits? ;-)

Comment on lines +1615 to +1639
for (unsigned int i = 0; i < phi_m.dofs_per_cell; ++i)
{
helper_m.prepare_basis_vector(i);
helper_p.zero_basis_vector();
face_operation(phi_m, phi_p);
helper_m.submit();
}

helper_m.distribute_local_to_global(diagonal_global_components);

for (unsigned int i = 0; i < phi_p.dofs_per_cell; ++i)
{
helper_m.zero_basis_vector();
helper_p.prepare_basis_vector(i);
face_operation(phi_m, phi_p);
helper_p.submit();
}

helper_p.distribute_local_to_global(diagonal_global_components);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is how the diagonal for the face integral is determined. Currently, there is a limitation that not arbitrary constraints are supported (which should be fine as a first step, since in the DG constraints you normally don't have constraints).

Comment on lines +1676 to +1689
for (unsigned int i = 0; i < phi.dofs_per_cell; ++i)
{
helper.prepare_basis_vector(i);
boundary_operation(phi);
helper.submit();
}

helper.distribute_local_to_global(diagonal_global_components);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly, the diagonal entries related to boundary values are determined here (without any limitations).

return;

// shortcut for faces containing purely FE_Nothing
if (internal::is_fe_nothing<false>(matrix_free,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We ignore cells/faces with FE_Nothing attached to them.

internal::create_new_affine_constraints_if_needed(
matrix, constraints_in, constraints_for_matrix);

const auto batch_operation =
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the relevant function that computes the stiffness matrices. It is generalized in the sense that it works on a list of FEEvaluationData (blocks). This is useful, since it allows to have one code for computing stiffness matrices related to cell (1 FEEvaluation), face (2 FEFaceEvaluation), and boundary integrals (1 FEFaceEvaluation). In the future, one should also be able to pass in multiple FEEvaluation (as needed in the Navier-Stokes case: velocity and pressure).

Comment on lines +35 to +58
namespace internal
{
template <int dim, typename Number, bool is_face_>
class ComputeMatrixScratchData
{
public:
using FEEvalType = FEEvaluationData<dim, Number, is_face_>;

std::vector<unsigned int> dof_numbers;
std::vector<unsigned int> quad_numbers;
std::vector<unsigned int> first_selected_components;
std::vector<unsigned int> batch_type;
static const bool is_face = is_face_;

std::function<std::vector<std::unique_ptr<FEEvalType>>(
const std::pair<unsigned int, unsigned int> &)>
op_create;
std::function<void(std::vector<std::unique_ptr<FEEvalType>> &,
const unsigned int)>
op_reinit;
std::function<void(std::vector<std::unique_ptr<FEEvalType>> &)>
op_compute;
};
} // namespace internal
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is configured by this struct.

Comment on lines +1934 to +2088

data_face.op_create =
[&](const std::pair<unsigned int, unsigned int> &range) {
std::vector<
std::unique_ptr<FEEvaluationData<dim, VectorizedArrayType, true>>>
phi;

if (!internal::is_fe_nothing<true>(matrix_free,
range,
dof_no,
quad_no,
first_selected_component,
fe_degree,
n_q_points_1d,
true) &&
!internal::is_fe_nothing<true>(matrix_free,
range,
dof_no,
quad_no,
first_selected_component,
fe_degree,
n_q_points_1d,
false))
{
integrator.reinit(cell);
phi.emplace_back(
std::make_unique<FEFaceEvalType>(matrix_free,
range,
true,
dof_no,
quad_no,
first_selected_component));
phi.emplace_back(
std::make_unique<FEFaceEvalType>(matrix_free,
range,
false,
dof_no,
quad_no,
first_selected_component));
}

const unsigned int n_filled_lanes =
matrix_free.n_active_entries_per_cell_batch(cell);
return phi;
};

for (unsigned int v = 0; v < n_filled_lanes; ++v)
matrices[v] = 0.0;
data_face.op_reinit = [](auto &phi, const unsigned batch) {
static_cast<FEFaceEvalType &>(*phi[0]).reinit(batch);
static_cast<FEFaceEvalType &>(*phi[1]).reinit(batch);
};

for (unsigned int j = 0; j < dofs_per_cell; ++j)
{
for (unsigned int i = 0; i < dofs_per_cell; ++i)
integrator.begin_dof_values()[i] =
static_cast<Number>(i == j);
if (face_operation)
data_face.op_compute = [&](auto &phi) {
face_operation(static_cast<FEFaceEvalType &>(*phi[0]),
static_cast<FEFaceEvalType &>(*phi[1]));
};

internal::ComputeMatrixScratchData<dim, VectorizedArrayType, true>
data_boundary;

data_boundary.dof_numbers = {dof_no};
data_boundary.quad_numbers = {dof_no};
data_boundary.first_selected_components = {first_selected_component};
data_boundary.batch_type = {1};

data_boundary
.op_create = [&](const std::pair<unsigned int, unsigned int> &range) {
std::vector<
std::unique_ptr<FEEvaluationData<dim, VectorizedArrayType, true>>>
phi;

if (!internal::is_fe_nothing<true>(matrix_free,
range,
dof_no,
quad_no,
first_selected_component,
fe_degree,
n_q_points_1d,
true))
phi.emplace_back(std::make_unique<FEFaceEvalType>(
matrix_free, range, true, dof_no, quad_no, first_selected_component));

return phi;
};

data_boundary.op_reinit = [](auto &phi, const unsigned batch) {
static_cast<FEFaceEvalType &>(*phi[0]).reinit(batch);
};

if (boundary_operation)
data_boundary.op_compute = [&](auto &phi) {
boundary_operation(static_cast<FEFaceEvalType &>(*phi[0]));
};
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here, the struct is filled for the cell/face/boundary integral case.

Comment on lines +37 to +38
template <int dim, typename Number, bool is_face_>
class ComputeMatrixScratchData
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently, this class and the corresponding compute_matrix() function are internal. I apply these in different contexts to see the applicability. Once the interface seems to be general enough, I would suggest to make these public so that users have full flexibility.

include/deal.II/matrix_free/tools.h Outdated Show resolved Hide resolved
include/deal.II/matrix_free/tools.h Outdated Show resolved Hide resolved
include/deal.II/matrix_free/tools.h Outdated Show resolved Hide resolved
@peterrum
Copy link
Member Author

The failing test is unrelated.

Copy link
Member

@kronbichler kronbichler left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks OK, thank you for tackling it. I have a few questions below. If I read the code correctly, there is no change (apart from empty std::functions) in the diagonal code for cells before, and the main extension is in the matrix creation in terms of multiple evaluators (op.create()).

doc/news/changes/minor/20240516Munch Outdated Show resolved Hide resolved
include/deal.II/lac/full_matrix.h Outdated Show resolved Hide resolved
include/deal.II/matrix_free/tools.h Outdated Show resolved Hide resolved
Comment on lines +1318 to +1328
const auto init_data = dealii::internal::
extract_initialization_data<is_face, dim, Number, VectorizedArrayType>(
matrix_free,
dof_no,
first_selected_component,
quad_no,
fe_degree,
static_n_q_points,
active_fe_index,
numbers::invalid_unsigned_int /*active_quad_index*/,
numbers::invalid_unsigned_int /*face_type*/);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the simplest way to get the case of FE_Nothing? I would have looked up the active fe index and then checked the matrix_free.get_dof_handler(dof_no).get_fe_collection()[active_fe_index]? Is there something not working, e.g. for the faces?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason why we do this is that we did not copy the (somewhat complicated) logic in:

init_data.active_fe_index =
fe_degree != numbers::invalid_unsigned_int ?
init_data.dof_info->fe_index_from_degree(first_selected_component,
fe_degree) :
(active_fe_index_given != numbers::invalid_unsigned_int ?
active_fe_index_given :
0);

If we would copy these lines, I am afraid that the codes will diverge sometime. @kronbichler What is your opinion on this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So in other words, the active_fe_index variable you have at your disposal here is not the index we need to check within the actual FECollection class in the DoFHandler, right? In that case, I think I agree with the code here, even though it is not pretty.

include/deal.II/matrix_free/tools.h Outdated Show resolved Hide resolved
include/deal.II/matrix_free/tools.h Show resolved Hide resolved
@peterrum
Copy link
Member Author

peterrum commented Jun 7, 2024

@kronbichler Thank you for the review.

This looks OK, thank you for tackling it. I have a few questions below. If I read the code correctly, there is no change (apart from empty std::functions) in the diagonal code for cells before, and the main extension is in the matrix creation in terms of multiple evaluators (op.create()).

The main new feature of this PR is that is to compute the face/boundary contributions for the computation of the diagonal and matrix.

That one can handle now multiple evaluators in the case of the matrix is a side effect of the extensive refactoring (now cell/face/boundary integrals take the same code path). One should do similar changes to the computation of the diagonal (and I am planing to do this). But, there it is more complex, since we resolve the constraints there on-the-fly (with the main limitation that the same constraints are applied from left and right) without using AffineConstraints.

@kronbichler kronbichler merged commit 57738c4 into dealii:master Jun 7, 2024
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants