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

FECouplingValues. #15773

Merged
merged 1 commit into from Apr 11, 2024
Merged

FECouplingValues. #15773

merged 1 commit into from Apr 11, 2024

Conversation

luca-heltai
Copy link
Member

@luca-heltai luca-heltai commented Jul 20, 2023

This is a draft PR that I would like to use as a discussion a class that can be used for general coupling operators.

I would like to compute bilinear forms of the following abstract type

$$ \int_{T_1} \int_{T_2} K(x_1, x_2) f(x_1) g(x_2) dT_1 dT_2, $$

where $T_1$ and $T_2$ are two arbitrary sets (cells, faces, edges, or any
combination thereof), and $K$ is a (possibly singular) coupling kernel,
for three different types of Kernels $K$:

  • $K(x_1, x_2)$ is a non-singular Kernel function, for example, it is a
    function of positive powers $\alpha$ of the distance between the quadrature
    points $|x_1-x_2|^\alpha$;
  • $K(x_1, x_2)$ is a singular Kernel function, for example, it is a function
    of negative powers $\alpha$ of the distance between the quadrature points
    $|x_1-x_2|^\alpha$;
  • $K(x_1, x_2)$ is a Dirac delta distribution $\delta(x_1-x_2)$, such that
    the integral above is actually a single integral over the intersection of
    the two sets $T_1$ and $T_2$.

For the first case, one may think that the only natural way to proceed is to
compute the double integral by simply nesting two loops:

$$ \int_{T_1} \int_{T_2} K(x_1, x_2) \phi^1_i(x_1) \phi^2_j(x_2) dT_1 dT_2 \approx \sum_{q_1} \sum_{q_2} K(x_1^{q_1}, x_2^{q_2}) \phi^1_i(x_1^{q_1}) \phi^2_j(x_2^{q_2}) w_1^{q_1} w_2^{q_2}, $$

where $x_1^{q_1}$ and $x_2^{q_2}$ are the quadrature points in $T_1$ and
$T_2$ respectively, and $w_1^{q_1}$ and $w_2^{q_2}$ are the corresponding
quadrature weights.
This, however is not the only way to proceed. In fact, such an integral can
be rewritten as a single loop over two vectors of points with the same length
that can be thought of as a single quadrature rule on the set $T_1\times T_2$. For singular Kernels, for example, this is often the only way to
proceed, since the quadrature formula on $T_1\times T_2$ is usually not
written as a tensor product quadrature formula, and one needs to build a
custom quadrature formula for this purpose.

This class allows one to treat the three cases above in the same way, and to
approximate the integral as follows:

$$ \int_{T_1} \int_{T_2} K(x_1, x_2) \phi^1_i(x_1) \phi^2_j(x_2) dT_1 dT_2 \approx \sum_{i=1}^{N_q} K(x_1^{i}, x_2^{i}) \phi^1_i(x_1^{i}) \phi^2_j(x_2^{i}) w_1^{i} w_2^i, $$

Since the triple of objects $({q}, {w}, {\phi})$ is usually provided by
a class derived from the FEValuesBase class, this is the type that the class
needs at construction time. $T_1$ and $T_2$ can be two arbitrary cells,
faces, or edges belonging to possibly different meshes (or to meshes with
different topological dimensions), $\phi^1_i$ and $\phi^2_j$ are basis
functions defined on $T_1$ and $T_2$, respectively.
The most common case of the dirac Distribution is when $T_1$ and $T_2$
correspond to the common face of two neighboring cells. In this case, this
class provides a functionality which is similar to the FEInterfaceValues
class, and provides a way to access values of basis functions on the
neighboring cells, as well as their gradients and Hessians, in a unified
fashion, on the face.

Similarly, this class can be used to couple bulk and surface meshes across
the faces of the bulk mesh. In this case, the two FEValuesBase objects will
have different topological dimension (i.e., one will be a cell in a
co-dimension one triangulation, and the other a face of a bulk grid with
co-dimension zero), and the CouplingQuadratureType argument is usually chosen
to be CouplingQuadratureType::reorder, since the quadrature points of the two
different FEValuesBase objects are not necessarily generated with the same
ordering.

The type of integral to compute is controlled by the CouplingQuadratureType
argument (see the documentation of that enum class for more details).
An example usage of this class to assemble two coupled bilinear forms (for
example, for Boundary Element Methods) is the following:

... // double loop over cells that yields cell_1 and cell_2
fe_values_1.reinit(cell_1); fe_values_2.reinit(cell_2);
CouplingFEValues<dim> cfv(fe_values1, fe_values2,
                          CouplingQuadratureType::tensor_product);
FullMatrix<double> local_matrix (cfv.n_coupling_dofs,cfv.n_coupling_dofs);

// Extractor on left cell 
const auto v1 = cfv.left(FEValuesExtractor::Scalar(0)); 
const auto p1 = cfv.left(FEValuesExtractor::Scalar(1));
// Extractor on right cell 
const auto u2 = cfv.right(FEValuesExtractor::Scalar(0)); 
const auto q2 = cfv.right(FEValuesExtractor::Scalar(1));
...
for (const unsigned int q : cfv.quadrature_point_indices()) { 
  const auto &[x_q,y_q] = cfv.quadrature_point(q);
  for (const unsigned int i : cfv.coupling_dof_indices())
    {
      const auto &v_i = cfv[v1].value(i, q);
      const auto &p_i = cfv[p1].value(i, q);
      for (const unsigned int j : cfv.coupling_dof_indices())
        {
          const auto &u_j = cfv[u2].value(j, q);
          const auto &q_j = cfv[q2].value(j, q);
          local_matrix(i, j) += (K1(x_q, y_q) * v_i * u_j +
                                 K2(x_q, y_q) * p_i * q_j) *
                                cfv[0].JxW(q) *
                                cfv[1].JxW(q);
        }
    }
}

I would like some feedback on the interface before I dive into the implementation of more involuted complex things. The minimal test shows how the remapping of quadrature points and dof indices should be.

In principle, by augmenting the constructors of the current FEValuesViews with two renumbering vectors for dofs and quads (which are ignored if of zero lenght), I think we could use the full infrasctructure of the FEValuesViews which is already in place, without duplicating code.

Thoughts?

@pcafrica, @lwy5515, @fdrmrc

@luca-heltai luca-heltai added this to the Release 9.6 milestone Jul 20, 2023
@luca-heltai luca-heltai marked this pull request as draft July 20, 2023 23:25
@luca-heltai
Copy link
Member Author

@tjhei , would you mind giving a look at the snippet of code above and share your thoughts about the interface? Before I move forward and modify our FEvaluesViews classes, I'd like to have a consensus on how the interface should look like.

Copy link
Member

@tjhei tjhei left a comment

Choose a reason for hiding this comment

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

I think this looks very reasonable. Is there a way to avoid the overhead of always having the permutation arrays present? Would it make sense to treat the identity differently. I expect this would cost performance otherwise.

@luca-heltai
Copy link
Member Author

I'm not sure if this

const auto index = renumbering.empty() ? in_index :  renumbering[in_index];

is faster than unconditionally doing

const auto index = renumbering[in_index];

also for the identity. Opinions? I don't have a preference.

@luca-heltai
Copy link
Member Author

luca-heltai commented Aug 2, 2023

So these are the steps needed for the concept presented here to work:

  • Reorder an existing view (possibly without too many expensive operations) (see FEValuesViews::RenumberedView #15819)
  • Create the correct dof reordering vectors
  • Create the correct quadrature reordering vectors

@luca-heltai
Copy link
Member Author

@tjhei , #15819 uses a different approach (inspired by your comment). There I do not touch the existing views, but I create a thin wrapper around them that encapsulates the renumbering. Let me know which approach you prefer.

@tjhei
Copy link
Member

tjhei commented Aug 4, 2023

also for the identity. Opinions? I don't have a preference.

I would think a single if branch is the better choice over always storing the identity.

@tjhei , #15819 uses a different approach (inspired by your comment). There I do not touch the existing views, but I create a thin wrapper around them that encapsulates the renumbering. Let me know which approach you prefer.

Either option is fine with me. What do you prefer?

@luca-heltai luca-heltai force-pushed the fe-coupling-values branch 2 times, most recently from 521c52e to 4dfa2e1 Compare August 7, 2023 09:24
@luca-heltai
Copy link
Member Author

luca-heltai commented Aug 7, 2023

Depends on #15819. Only the second commit introduces the actual FECouplingValues class.

@luca-heltai luca-heltai force-pushed the fe-coupling-values branch 3 times, most recently from e0355a8 to c77b733 Compare August 10, 2023 15:33
@luca-heltai
Copy link
Member Author

This is now fully functional.

  • test 3: shows how to use this for BEM coupling (@lwy5515 )
  • test 4: shows how to use this as a replacement for FEInterfaceValues (@fdrmrc)
  • test 5: shows how to use this to compute bulk - surface coupling (@stefanozampini )

@luca-heltai luca-heltai marked this pull request as ready for review August 10, 2023 15:39
@luca-heltai luca-heltai changed the title Draft of FECouplingValues. FECouplingValues. Aug 10, 2023
@fdrmrc
Copy link
Contributor

fdrmrc commented Aug 10, 2023

The test fe_coupling_02.debug fails because the following assertion is not satisfied

Assert(dof_coupling_type != DoFCouplingType::independent,
ExcMessage("Dofs are coupled. You cannot ask only for left dofs."));

How would you loop (within this interface) over DoF indices if DoFCouplingType::independent is selected?

@luca-heltai
Copy link
Member Author

luca-heltai commented Aug 11, 2023

I added the assert later, and it was in the wrong direction. Now it should work.

Copy link
Member

@peterrum peterrum left a comment

Choose a reason for hiding this comment

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

Looks good to me. Some minor comments. My biggest issue is that this might only work in serial (I am not counting p:s:T as parallel). The surface mesh and the bulk mesh are normally independent meshes, which are independently partitioned.

include/deal.II/fe/fe_coupling_values.h Outdated Show resolved Hide resolved
include/deal.II/fe/fe_coupling_values.h Outdated Show resolved Hide resolved
include/deal.II/fe/fe_coupling_values.h Outdated Show resolved Hide resolved
Comment on lines 800 to 808
/**
* Helper struct to associate an extractor to the left FEValuesBase object.
*/
template <typename Extractor>
struct LeftCoupling
{
LeftCoupling(const Extractor &extractor)
: extractor(extractor)
{}

/**
* The actual extractor object.
*/
const Extractor extractor;
};

/**
* Helper struct to associate an extractor to the right FEValuesBase object.
*/
template <typename Extractor>
struct RightCoupling
{
RightCoupling(const Extractor &extractor)
: extractor(extractor)
{}

/**
* The actual extractor object.
*/
const Extractor extractor;
};
Copy link
Member

Choose a reason for hiding this comment

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

These two classes look identical!? What is the reason?

Copy link
Member Author

Choose a reason for hiding this comment

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

They are used to identify which of the two FEValuesBase you are addressing. Asking for fev[left] and fev[right] will have a different effect.

tests/fecoupling/fe_coupling_03.cc Outdated Show resolved Hide resolved
@luca-heltai
Copy link
Member Author

luca-heltai commented Sep 24, 2023

Looks good to me. Some minor comments. My biggest issue is that this might only work in serial (I am not counting p:s:T as parallel). The surface mesh and the bulk mesh are normally independent meshes, which are independently partitioned.

This actually works perfectly fine on p::d::T (bulk) coupled with p::s::T (surface). Moreover, for BEM problems, once you set up the hierarchical matrices (based on shared trees), the p::s::T can be safely discarded. And one can create a surface p::f::T where the partitioning is induced by the (bulk) p::f::T (or p::d::T).

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.

I think this looks good in principle, it is an exciting feature. However, I have two main concern, one regarding the naming left/right discussed further down, and one regarding the overall performance aspects. While I agree that the present class is nice, it is another incarnation (besides the FEValuesViews, FEInterfaceValues, etc) that fundamentally makes a slow-performance choice: For usability, we decide to return zeros whenever we are on the other side regarding the index. Do not get me wrong, this is not bad, but if one ever wants to us this in a context where performance matters, it seems this moves us in the wrong direction. This PR gets my approval because I do not have the resources to think for a good solution. Since we already have FEPointEvaluation or FEEvaluation in the matrix-free module that essentially provide 80% of what is required to assemble the coupling matrices, plus tensor product libraries like https://github.com/hyperdeal/hyperdeal that have algorithms for tensor product coupling, I strongly suggest that, in case performance starts to become relevant, to start looking there and talk to me.

include/deal.II/fe/fe_coupling_values.h Outdated Show resolved Hide resolved
include/deal.II/fe/fe_coupling_values.h Outdated Show resolved Hide resolved
include/deal.II/fe/fe_coupling_values.h Outdated Show resolved Hide resolved
include/deal.II/fe/fe_coupling_values.h Outdated Show resolved Hide resolved
include/deal.II/fe/fe_coupling_values.h Outdated Show resolved Hide resolved
include/deal.II/fe/fe_coupling_values.h Outdated Show resolved Hide resolved
include/deal.II/fe/fe_coupling_values.h Outdated Show resolved Hide resolved
include/deal.II/fe/fe_coupling_values.h Outdated Show resolved Hide resolved
include/deal.II/fe/fe_coupling_values.h Outdated Show resolved Hide resolved
include/deal.II/fe/fe_coupling_values.h Outdated Show resolved Hide resolved
@luca-heltai
Copy link
Member Author

I think this looks good in principle, it is an exciting feature. However, I have two main concern, one regarding the naming left/right discussed further down, and one regarding the overall performance aspects. While I agree that the present class is nice, it is another incarnation (besides the FEValuesViews, FEInterfaceValues, etc) that fundamentally makes a slow-performance choice: For usability, we decide to return zeros whenever we are on the other side regarding the index. Do not get me wrong, this is not bad, but if one ever wants to us this in a context where performance matters, it seems this moves us in the wrong direction. This PR gets my approval because I do not have the resources to think for a good solution. Since we already have FEPointEvaluation or FEEvaluation in the matrix-free module that essentially provide 80% of what is required to assemble the coupling matrices, plus tensor product libraries like https://github.com/hyperdeal/hyperdeal that have algorithms for tensor product coupling, I strongly suggest that, in case performance starts to become relevant, to start looking there and talk to me.

I perfectly agree with your concerns. This is not thought to be fast for the computer, but fast for the programmer, similarly to FEInterfaceValues, and FEValuesViews. I wanted to have something with which it is easy to prototype bulk-surface, BEM, fractional Laplacian, etc.

Indeed, if we want to go fast, we need to take another route. However, I think that having this (slow) approach available is also necessary if one wants to be able to have a readable way to build these problems. I think this is a possible interface that follows the same steps we took elsewhere, and it would make it very easy to write coupled problems (with the same overhead of FEValuesViews/FEValues, etc.). If you see obvious ways in which this could be made faster, let's discuss it further. :)

@blaisb blaisb requested a review from peterrum October 18, 2023 11:25
@luca-heltai
Copy link
Member Author

Ping @dealii/dealii ?

@bangerth
Copy link
Member

bangerth commented Apr 2, 2024

Would you mind rebasing this to see whether the tests still all succeed?

@luca-heltai luca-heltai force-pushed the fe-coupling-values branch 2 times, most recently from 989dd93 to 2dbe391 Compare April 3, 2024 15:21
@luca-heltai
Copy link
Member Author

luca-heltai commented Apr 3, 2024

Only 68ab103 is relevant.

@bangerth
Copy link
Member

bangerth commented Apr 3, 2024

Thanks. I will look at that over the next couple of days.

@bangerth
Copy link
Member

bangerth commented Apr 7, 2024

Could you rebase now that the other patch has been merged? That should make the first commit go away.

Copy link
Member

@bangerth bangerth left a comment

Choose a reason for hiding this comment

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

Part 1. The rest later today.

include/deal.II/fe/fe_coupling_values.h Outdated Show resolved Hide resolved
include/deal.II/fe/fe_coupling_values.h Show resolved Hide resolved
include/deal.II/fe/fe_coupling_values.h Outdated Show resolved Hide resolved
include/deal.II/fe/fe_coupling_values.h Outdated Show resolved Hide resolved
include/deal.II/fe/fe_coupling_values.h Outdated Show resolved Hide resolved
include/deal.II/fe/fe_coupling_values.h Show resolved Hide resolved
include/deal.II/fe/fe_coupling_values.h Outdated Show resolved Hide resolved
include/deal.II/fe/fe_coupling_values.h Outdated Show resolved Hide resolved
include/deal.II/fe/fe_coupling_values.h Outdated Show resolved Hide resolved
include/deal.II/fe/fe_coupling_values.h Outdated Show resolved Hide resolved
Copy link
Member

@bangerth bangerth left a comment

Choose a reason for hiding this comment

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

Here's the rest. Nothing that will require much work.

I appreciate how extensively all of this is documented, and that you have 5 new tests. Good work.

include/deal.II/fe/fe_coupling_values.h Outdated Show resolved Hide resolved
include/deal.II/fe/fe_coupling_values.h Show resolved Hide resolved
Comment on lines 778 to 808
/**
* Helper struct to associate an extractor to the first FEValuesBase object.
*/
template <typename Extractor>
struct FirstCoupling
{
FirstCoupling(const Extractor &extractor)
: extractor(extractor)
{}

/**
* The actual extractor object.
*/
const Extractor extractor;
};

/**
* Helper struct to associate an extractor to the second FEValuesBase object.
*/
template <typename Extractor>
struct SecondCoupling
{
SecondCoupling(const Extractor &extractor)
: extractor(extractor)
{}

/**
* The actual extractor object.
*/
const Extractor extractor;
};
Copy link
Member

Choose a reason for hiding this comment

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

What would you think of putting these classes into a namespace FEValuesExtractors::FECouplingValues? This way they show up in the documentation in a similar place as the other extractors.

include/deal.II/fe/fe_coupling_values.h Outdated Show resolved Hide resolved
include/deal.II/fe/fe_coupling_values.h Outdated Show resolved Hide resolved
include/deal.II/fe/fe_coupling_values.h Outdated Show resolved Hide resolved
include/deal.II/fe/fe_coupling_values.h Outdated Show resolved Hide resolved
include/deal.II/fe/fe_coupling_values.h Outdated Show resolved Hide resolved
include/deal.II/fe/fe_coupling_values.h Outdated Show resolved Hide resolved
include/deal.II/fe/fe_coupling_values.h Outdated Show resolved Hide resolved
@luca-heltai
Copy link
Member Author

@bangerth, I should have addressed all your comments. Thanks for your review!

@bangerth bangerth merged commit ab68047 into dealii:master Apr 11, 2024
16 checks passed
@bangerth
Copy link
Member

Thank you!

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

6 participants