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

Add support for merging well cells for NLDD partitioning #6103

Conversation

jakobtorben
Copy link
Contributor

NLDD Improvements for Partitioning and Better Handling of Wells and Isolated Cells

This PR introduces several improvements to the Nonlinear Domain Decomposition (NLDD) implementation, with a main focus on better handling cells perforated by wells during subdomain partitioning.

Improvements

Well Cell Merging

  • Cells perforated by the same well are now merged into a single entity before partitioning
  • This ensures that all perforated cells of a well stay in the same subdomain
  • Prevents wells from being split across NLDD subdomains, which is a requirement for the NLDD implementation
  • Creates naturally better partitions than the previous fix to manually move the distributed cells after partitioning
  • Uses the new vertex merging functionality from Add vertex merging functionality to CSRGraphFromCoordinates opm-common#4537 in opm-common

Well Neighborhood Control

  • Added a new method to grow partitions around wells
  • Controlled with the parameter local_domains_partition_well_neighbor_levels_ which specifies the number of levels of neighbours to merge into one entity:
    • A value of 0 keeps only the merged well cells in the subdomain
    • A value of 1 (default) adds immediate neighbours of cells perforated by the well
    • A value of 2 adds all the neighbours of the cells in the previous levels, etc.
  • Improves numerical behaviour by creating more buffer cells around wells to avoid large changes on the boundary of domains and automatically places wells in the same NLDD subdomain if placed too close to each other

Enhanced Partition Visualization

Code Consistency

  • Renamed internal parameters for consistency (from local_domain_* to local_domains_*)

Testing

  • Added a large suite of tests for NLDD partitioning
  • The changes have been thoroughly tested on multiple complex field cases with various well configurations
  • Tests show improved convergence behaviour with wells properly contained within subdomains
  • Verified that isolated cells are correctly identified and handled in problematic cases
  • Tested with various values of the well neighbour level parameter to confirm proper behaviour
  • The ResInsight-compatible output files have been verified to work correctly with visualisation tools

@jakobtorben jakobtorben requested review from bska and atgeirr March 25, 2025 10:06
@jakobtorben
Copy link
Contributor Author

jenkins build this opm-common=4537 please

1 similar comment
@bska
Copy link
Member

bska commented Mar 25, 2025

jenkins build this opm-common=4537 please

Copy link
Member

@bska bska 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 really good. I mostly have minor comments, although there's one part that I'd write differently.

Comment on lines 115 to 122
// Fix-up for an extreme case: Interior cells whose neighbours are all
// on a different rank--i.e., interior cells which do not have on-rank
// neighbours. Make each such cell be a separate domain on this rank.
// Don't remove this code unless you've taken remedial action elsewhere
// to ensure the situation doesn't happen. For what it's worth, we've
// seen this case occur in practice when testing on field cases.
// The non-reachable cells are marked with -1 in the partition vector.
// Here we collect them into a single domain, per rank.
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 appears to be a little inconsistent now. The first part says that we make each isolated cell be a separate single domain, while the last part says that we put all of these cells into one combined domain. Perhaps you can clarify the comment a little?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It was misleading, indeed. It should be clearer now.

Note that before, we did not skip this domain. I mostly added this functionality as I experienced getting around 7 times as many domains as expected on a field case where there were many single isolated cells, which will be safe to skip. From the earlier comment, it sounded like the isolated cells could have neighbours on different ranks. But it should be safe to skip anyways, as NLDD would not update any solutions outside of this rank anyways. Right?

const auto nDigit = 1 + static_cast<int>(std::floor(std::log10(comm.size())));
auto partition_fname = odir / fmt::format("{1:0>{0}}", nDigit, comm.rank());
std::ofstream pfile { partition_fname };
if (!pfile) {
Copy link
Member

Choose a reason for hiding this comment

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

You're not supposed to need this check. The std::ofstream constructor already throws an exception if the file cannot be opened. I suppose there's something to be said for making this code robust in parallel where we might risk that some processes/ranks are able to open the file while others aren't. In that case we'd get weird hangs. Handling that, however, will require introducing parallel exception handling.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Aha, that makes sense. Removed it now. I think there is work to be done for better parallel error handling and parallel output for NLDD, but I will not target that in this PR.


// Breadth-first search for neighbor cells up to num_levels away
for (int level = 0; level < num_levels && !frontier.empty(); ++level) {
new_frontier.reserve(frontier.size() * 2);
Copy link
Member

Choose a reason for hiding this comment

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

There's a subtle problem here. Since you move() the new_frontier object into frontier below, it will be in a "valid but unspecified state" if this loop runs (at least) twice. I think I might personally write this loop using vector<>::swap() instead, i.e., something like

for (level) {
    new_frontier.clear();
    new_frontier.reserve(...);

    for (cell) {
        // ...
    }

    frontier.swap(new_frontier);
}

which avoids that semantic pitfall.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point. Adopted swap instead now.

@jakobtorben jakobtorben force-pushed the add_support_for_merging_well_cells_for_nldd_partitioning branch 2 times, most recently from 4fc91ea to 0988b37 Compare March 27, 2025 12:53
@jakobtorben
Copy link
Contributor Author

jenkins build this please

Copy link
Member

@bska bska left a comment

Choose a reason for hiding this comment

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

Thanks a lot for the updates. This looks good to me now and you've addressed all my remarks. Unless @atgeirr disagrees I think this should be merged into the master branch now.

@jakobtorben jakobtorben force-pushed the add_support_for_merging_well_cells_for_nldd_partitioning branch from 0988b37 to 36cda5a Compare March 28, 2025 08:27
@jakobtorben
Copy link
Contributor Author

jenkins build this please

Copy link
Member

@bska bska left a comment

Choose a reason for hiding this comment

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

Thanks a lot for the updates. This looks good to me now and as the build check is green I'll merge into master.

@bska bska merged commit 9717533 into OPM:master Mar 28, 2025
1 check passed
Copy link
Member

@atgeirr atgeirr left a comment

Choose a reason for hiding this comment

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

Only a few minor issues. Should be merged when addressed.

@@ -222,6 +241,9 @@ class BlackoilModelNldd
for (const int domain_index : domain_order) {
const auto& domain = domains_[domain_index];
SimulatorReportSingle local_report;
if (std::find(domains_to_skip_.begin(), domains_to_skip_.end(), domain.index) != domains_to_skip_.end()) {
Copy link
Member

Choose a reason for hiding this comment

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

It is not a priori clear to me that it is a good idea to skip these domains of isolated cells, as a local solve might reduce newton iterations. However, given that this code has been tested thoroughly, I assume the skipping approach has been validated?

Could store a flag in the domain itself rather than doing a lookup here, to save a little time.

@atgeirr
Copy link
Member

atgeirr commented Mar 28, 2025

Did not notice it was merged while I looked at it... Anyway not problematic, but I would like the comments to be addressed in a small follow-up PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants