Skip to content

DG parallelization #1415

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

Open
wants to merge 12 commits into
base: develop
Choose a base branch
from
Open

DG parallelization #1415

wants to merge 12 commits into from

Conversation

lihanyu97
Copy link
Contributor

This is work in progress to parallelize DG scheme relying MFEM's parallel communication infrastructure (ExchangeFaceNbrData). The first phase of this work to configure residual computation in parallel is complete. The main effort is to properly prepare input data that includes ghost dofs for Functional and record the correct index in element restriction to ensure the evaluation and AD at each quadrature point accesses the right ghost dof value. The comment associated with each major implementation is included.

Please point out anything that is obviously wrong or clumsy, and I will avoid them in the next phase.

Copy link
Collaborator

@tupek2 tupek2 left a comment

Choose a reason for hiding this comment

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

So, maybe I'm going dense... but where is the test that you get the same field solution on both sides of the dg interface for pre-specified linear/quadratic fields?

Looks like amazing progress. Are you intending to push this in incrementally, or wait until the jacobian is done?

@lihanyu97
Copy link
Contributor Author

lihanyu97 commented Jun 26, 2025

So, maybe I'm going dense... but where is the test that you get the same field solution on both sides of the dg interface for pre-specified linear/quadratic fields?

Looks like amazing progress. Are you intending to push this in incrementally, or wait until the jacobian is done?

The new test is here: https://github.com/LLNL/serac/blob/42804174f653ba821530ff9d09210a401aa283e6/src/serac/numerics/functional/tests/dg_ghost_face_index.cpp

I would say let's push everything in after jacobian is fixed bacause this PR is making some fundamental changes on the typedef.
mfem::Mesh --> mfem::ParMesh
mfem::FiniteElementSpace --> mfem::ParFiniteElementSpace

@lihanyu97 lihanyu97 marked this pull request as ready for review July 13, 2025 04:53
@lihanyu97 lihanyu97 changed the title WIP: DG parallelization DG parallelization Jul 13, 2025
@lihanyu97
Copy link
Contributor Author

lihanyu97 commented Jul 13, 2025

@tupek2 @btalamini This DG PR is ready for review. All debug footprints (std::out / mpi::out) are removed.

@lihanyu97 lihanyu97 added the ready for review Ready for active inspection by reviewers label Jul 13, 2025
@@ -96,6 +96,9 @@ struct Domain {
*/
std::map<FunctionSpace, BlockElementRestriction> restriction_operators;

/// @brief Shared interior face list
Copy link
Member

Choose a reason for hiding this comment

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

This comment just repeats the variable name, and so isn't useful. Think of a user who has never seen this class before and is reading these. What does "shared" mean in this context? Tell them what this is for. Here's a suggestion:

/// @brief Ids of interior faces that lie on the boundary shared by two processors

But feel free to change this if you can make it clearer.

* @brief Find the list of interior faces shared by two processors to make sure
* these faces are only integrated once.
*/
void insert_shared_interior_face_list();
Copy link
Member

Choose a reason for hiding this comment

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

I think this function should be considered logically private, and therefore be called inside InteriorFaces(...) (and any other factory function - maybe ofEdges?) . In my opinion, if one calls InteriorFaces(...) and the resulting Domain has an empty shared_interior_face_ids_, it's in an invalid state. The user shouldn't need to know to call an additional method for the class to be usable in parallel. @lihanyu97 and @tupek2, do you agree?

SLIC_ERROR_IF(type_ != Domain::Type::InteriorFaces, "This method is only for interior face domains");

// make a list if we don't already have one
if (shared_interior_face_ids_.size() == 0) {
Copy link
Member

Choose a reason for hiding this comment

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

I prefer this, as the intent is clearer:

Suggested change
if (shared_interior_face_ids_.size() == 0) {
if (shared_interior_face_ids_.empty()) {

}

domain.insert_shared_interior_face_list();
Copy link
Member

Choose a reason for hiding this comment

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

As noted above, I think the shared face list should be filled on the domain construction and not be the responsibility of the user here.

Copy link
Contributor Author

@lihanyu97 lihanyu97 Aug 2, 2025

Choose a reason for hiding this comment

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

The reason I implemented this way is because 1) shared interior face list is only used in qoi functionals, and 2) if the qoi involves integrating over a set of interior faces. This means if we always fill this interior_face_ids_ container for all InteriorFace domains, we are requesting additional memory that we may never use. Therefore, I only require this container to be filled automatically from AddInteriorFaceIntegral in functional_qoi.inl. The user doesn't have to worry about filling this container. You can see that AddInteriorFaceIntegral in functional.hpp doesn't have this line.

Also, this method is similar to insert_restriction, which doesn't do anything if there's already an element restriction for a given trial space. So for example, if we our functional qoi is computed by adding two different interior face integrals, we need to call AddInteriorFaceIntegral twice. But insert_shared_interior_face_list only execute once.

Copy link
Member

Choose a reason for hiding this comment

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

I understand. But I think the decision of whether to allocate that memory is made when the user decides to instantiate a Domain of interior faces. The point of the domains is to integrate over them, and the shared face information is needed to compute integrals on this type of domain. If it doesn't have it, it can't do its job in all cases. I would view that as having an invalid state.

"Users" in this case are not just people making Functional objects, they're also developers using the Domain class. If someone writes another integrator - like the dFEM one, or example - it won't be obvious to them that they need to call an additional method on interior face domains to be able to use the shared face info.

I can see why the restriction operators lead you to this. But it turns out that this is not a good model. They were tacked onto Domain as an afterthought. We might pull them back out. I don't want to replicate that pattern.

Really, the underlying problem is that Domain should be a class with constructors, data hiding, and a clear contract. That's not your fault. We'll address it later.

Comment on lines +151 to +153
* @brief helper function to locally cast away const on FE space to update face neighbor data.
* this is ok because the original FE space is declared without const and we constrained
* the non-constness locally
Copy link
Member

Choose a reason for hiding this comment

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

I don't understand this comment. What I hope it means is that this function exists because the MakeRef method in MFEM is not const correct, but we know and trust that it does not actually change the trial space. If my statement is wrong, we need to revisit this.

Copy link
Contributor Author

@lihanyu97 lihanyu97 Aug 2, 2025

Choose a reason for hiding this comment

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

MakeRef is not the problem. ExchangeFaceNbrData is the method that doesn't have const keyword because it creates/recreates the FaceNbrData vector and other infos related to the face neighbor data. The catch here is MakeRef links the ParGridFunction with external data, so it doesn't create any additional memory footprint. Now if the external data is const, then we cannot invoke ExchangeFaceNbrData on the ParGridFunction.

So what I meant in the comment is that, we can cast away the const locally in this method because we want to invoke ExchangeFaceNbrData. This is ok because we are not changing any of the locally owned data on the trial space. All we are changing/updating is the face neighbor data.

void Domain::insert_shared_interior_face_list()
{
// Weights only need to be computed for Domain of InteriorFaces type
SLIC_ERROR_IF(type_ != Domain::Type::InteriorFaces, "This method is only for interior face domains");
Copy link
Member

Choose a reason for hiding this comment

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

I think in this case you should use SLIC_ERROR_ROOT_IF to avoid echoing the output on all ranks.

using namespace serac::profiling;

template <int dim, int p>
void L2_index_test(std::string meshfile)
Copy link
Member

Choose a reason for hiding this comment

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

I would appreciate if you write a short comment block that explains the intent of the test. If it ever fails, it will help the person who broke it understand what the test's job is and hopefully give a clue as to how they broke it. As a group, we do not usually do this, so this is a hypocritical complaint.

Copy link
Contributor Author

@lihanyu97 lihanyu97 Aug 2, 2025

Choose a reason for hiding this comment

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

Sure I will add comments to this test.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ready for review Ready for active inspection by reviewers
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants