Skip to content

CpGrid: build point communication interfaces from all points of a cell - #1056

Open
hnil wants to merge 5 commits into
OPM:masterfrom
hnil:pr/point-comm-partition-of-unity
Open

CpGrid: build point communication interfaces from all points of a cell#1056
hnil wants to merge 5 commits into
OPM:masterfrom
hnil:pr/point-comm-partition-of-unity

Conversation

@hnil

@hnil hnil commented Jul 31, 2026

Copy link
Copy Markdown
Member

On a faulted corner-point grid the hanging nodes along the fault are not canonical corners of the neighbouring cells, so point interfaces built from the 8 canonical corners never include them: they receive no message and vertex ownership is not a partition of unity across ranks (breaks vertex-based fields, e.g. mechanics).

First commit adds a test that fails on master (np 2/4); second builds the interfaces from all points of a cell, after which np 1/2/4 pass and distribution_test is unchanged.

One invariant worth stating: the per-cell point rows are traversal-order deduplicated, deliberately not sorted — the interface pairs sender/receiver rows positionally across ranks, and local point indices differ per rank, so sort+unique would silently reintroduce the bug.

@hnil hnil added the manual:bugfix This PR is a bug fix and should be noted in the manual label Jul 31, 2026
@hnil
hnil requested review from akva2, atgeirr and blattms July 31, 2026 08:42

@blattms blattms left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I need to think about this a little. Did not look at the tests. So this is a first assessement. More to follow,

It is nice to have this fixed. We never needed communication between points before, hence this surely needed some love.

I think we should make sure that we do not add permanent addtional memory usage and maybe we make this more efficient, even.

Comment on lines +1719 to +1733
const std::size_t nc = cell_to_point_.size();
std::vector<int> points;
for (std::size_t cell = 0; cell < nc; ++cell) {
points.clear();
const auto& faces = cell_to_face_[cpgrid::EntityRep<0>(cell, true)];
const int nf = faces.size();
for (int f = 0; f < nf; ++f) {
for (const auto& fv : face_to_point_[faces[f].index()]) {
if (std::find(points.begin(), points.end(), fv) == points.end()) {
points.push_back(fv);
}
}
}
cell_to_allpoint_.appendRow(points.begin(), points.end());
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think the best place to do this is during (computeFace2Point)[https://github.com/hnil/opm-grid/blob/99aa1cc1f1761123ba5d75e3f94d4cde8d108ea9/opm/grid/cpgrid/CpGridData.cpp#L1241] namely during the last grid.scatterData which uses a FaceViaCellHandleWrapper<SparseTableDataHandle>

One possibility would be to optionally make SparseTableDataHandle populate your container with the values during its scatter method.

Comment thread opm/grid/cpgrid/CpGridData.cpp Outdated
mark_.resize(size(0));
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

We should not add trailing whitespace

Comment thread opm/grid/cpgrid/CpGridData.hpp Outdated
Comment on lines +778 to +782
/** @brief All points of each cell, i.e. every point of every face of the
* cell, not only the eight canonical corners. On corner-point grids
* with hanging nodes the extra points are essential for building
* complete codim-3 communication interfaces. */
Opm::SparseTable<int> cell_to_allpoint_;

@blattms blattms Aug 10, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is not really used after the set up and should be a local variable in some function

Comment thread opm/grid/cpgrid/DataHandleWrappers.hpp Outdated
{
using DataType = typename Handle::DataType;
using C2PTable = std::vector< std::array<int,8> >;
using C2PTable = Opm::SparseTable<int>;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I don't think we even use the PointViaCellHandle. So maybe remove it?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Removed. Checked first that nothing uses it - not opm-grid, not opm-simulators, and not opm-flowgeomechanics - and PointViaCellWarner existed only as its base, so that and the translation unit holding the warning went too. 113 lines. All 84 opm-grid tests pass.

hnil and others added 3 commits August 10, 2026 11:14
New 4-rank test (partition_of_unity_test): a 4x4x2 corner-point grid whose
right half is shifted down by half a cell, partitioned so the fault plane
coincides with a processor boundary. Fault-face processing produces hanging
nodes -- face nodes that are not canonical corners of the neighbouring cell.
The test communicates a codim-3 data handle over All_All_Interface and
requires every non-interior vertex to receive at least one message.

Before the all-points communication interface fix each rank reports two
unreached hanging nodes ('Owner is not a partition of unity' in vertex-based
mechanics assembly); with cell_to_allpoint_-based interfaces the test passes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The codim-3 (point) communication interface and the point-via-cell data
handle were built from cell_to_point_, i.e. only the eight canonical corners
of each cell. On corner-point grids with hanging nodes a face can reference
nodes that are not canonical corners of the neighbouring cell; such nodes
never entered point_interfaces_, so they were invisible to codim-3
communication and their ownership failed to be a partition of unity across
ranks (observed as 'Owner is not a partition of unity' in vertex-based
mechanics assembly).

Introduce cell_to_allpoint_ (SparseTable: every node of every face of each
cell, sorted and deduplicated) and use it for the point interface and for
PointViaCellHandleWrapper. AttributeDataHandle::fixedSize() now returns
false since rows vary in length, and row iteration uses the SparseTable
iterators.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…lone

- cell_to_allpoint is now a local in the function that builds the point
  interfaces, so nothing is retained after set-up.  This was the main
  objection: no permanent additional memory.
- The duplicate check is a per-cell marker array instead of std::find,
  so building the rows is linear rather than quadratic in the points of
  a cell.  Traversal order is untouched, which is the invariant the
  positional gather/scatter depends on.
- PointViaCellHandleWrapper is left exactly as it was; its C2PTable
  typedef no longer needs changing.  It has no users at all, but
  removing it is a separate cleanup.
- Dropped the trailing whitespace this branch introduced.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@hnil
hnil force-pushed the pr/point-comm-partition-of-unity branch from 99aa1cc to a4c0e2e Compare August 10, 2026 09:16
@hnil

hnil commented Aug 10, 2026

Copy link
Copy Markdown
Member Author

Thanks — three of four done, and the memory objection is fully addressed.

  • No persistent table. cell_to_allpoint is now a local in the function that builds the point interfaces; nothing is retained after set-up.
  • Cheaper, too. The duplicate check is a per-cell marker array instead of std::find, so the rows build in linear rather than quadratic time in a cell's points. Traversal order is unchanged — that is the invariant the positional gather/scatter depends on.
  • PointViaCellHandleWrapper left alone. You are right that nothing uses it — I checked, it has no callers anywhere in opm-grid. With the table local, my typedef change was unnecessary, so it is reverted and the struct is untouched. Removing it entirely seems like its own cleanup; happy to do that separately if you want it.
  • Trailing whitespace gone.

Not done: building this during computeFace2Point's last scatterData. That is the better home and I would rather you confirm the shape first — making SparseTableDataHandle optionally populate a cell-indexed container during scatter means threading that container through computeFace2Point's signature, which touches the distribution path rather than just this feature. Say the word and I will do it.

Rebased onto master (after #1051). np 1/2/4 pass, full opm-grid suite 84/84.

The file was added with a SINTEF header copied from a neighbour; the work
is Equinor's.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@hnil

hnil commented Aug 11, 2026

Copy link
Copy Markdown
Member Author

What do I do with this @blattms

PointViaCellHandleWrapper has no users anywhere - not in opm-grid, not in
opm-simulators, not in opm-flowgeomechanics - and PointViaCellWarner existed
only to serve it, so both go, along with the translation unit that held the
warning.

Removing dead code rather than carrying it through the point-communication
rework this PR performs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@hnil
hnil requested a review from blattms August 20, 2026 12:07
@hnil

hnil commented Aug 21, 2026

Copy link
Copy Markdown
Member Author

jenkins build this please

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

manual:bugfix This PR is a bug fix and should be noted in the manual

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants