Skip to content

Comments

Halo add corners#1005

Open
TomMelt wants to merge 5 commits intodevelopfrom
halo-add-corners
Open

Halo add corners#1005
TomMelt wants to merge 5 commits intodevelopfrom
halo-add-corners

Conversation

@TomMelt
Copy link
Contributor

@TomMelt TomMelt commented Dec 16, 2025

Add corner neighbours into halo exchange logic

Task List

  • Linked an issue above that captures the requirements of this PR
  • Defined the tests that specify a complete and functioning change
  • Implemented the source code change that satisfies the tests
  • Commented all code so that it can be understood without additional context
  • No new warnings are generated or they are mentioned below
  • The documentation has been updated (or an issue has been created to do so)
  • Relevant labels (e.g., enhancement, bug) have been applied to this PR
  • This change conforms to the conventions described in the README

Change Description

This PR has the following key changes:

  • adds support for corner neighbours to the existing halo exchange logic (which previously just exchanged edges)
  • add support for CGVector and DGVectorHolder exchange (this is required for corners and edges)
  • updates metadata tests and closed-boundary (CB) halo test to work with corner neighbours
  • removes the Periodic-Boundary (PB) halo exchange test for now, this will be addressed in a subsequent PR Halo add pb test for halo exchange #1044

Test Description

HaloExchangePB_test.cpp

HaloExchangeCB_test.cpp creates test data for all the array types:

  • HField
  • VertexField
  • DGField
  • DGVector
  • CGVector

I modified the previous version, so that the model data are created in such a way that the value of each cell can be calculated based on its indices. Therefore we can check after exchange that all of the cells have been exchanged successfully. This also now allows us to change the number of ranks arbitrarily (assuming you also provide the appropriate partition_metadata file.

ModelMetadataCB_test.cpp and ModelMetadataPB_test.cpp

Small modifications have been made to the periodic and closed boundary ModelMetadata tests.

The changes now add the additional corner neighbour metadata and check that it is ready correctly.

partition_metadata_3_cb.cdl and partition_metadata_3_pb.cdl

These files are now generated at compile time by running the decomp tool on a new file halo_test_mask.cdl. This is in line with similar changes introduced by @joewallwork on the XIOS tests.


Other Details

  • domain_decop git commit has now been bumped to the latest version on main. This version of the decomp tool contains the corner neighbour metadata.

Further work

@TomMelt TomMelt self-assigned this Dec 16, 2025
@TomMelt TomMelt added the ICCS Tasks or reviews for the ICCS team label Dec 16, 2025
@TomMelt TomMelt changed the base branch from halo-dynamics to develop February 16, 2026 08:50
@TomMelt TomMelt added the enhancement New feature or request label Feb 16, 2026
Copy link
Contributor

@joewallwork joewallwork left a comment

Choose a reason for hiding this comment

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

Thanks for this @TomMelt. Here's some initial feedback. I haven't checked all of the logic but let me know if there's anything in particular you want checking.

@@ -52,11 +54,36 @@ class Halo {

/*!
* @brief Constructs a halo object from DGVector
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
* @brief Constructs a halo object from DGVector
* @brief Constructs a halo object from DGVectorHolder

Comment on lines +59 to 88
template <int N> Halo(DGVectorHolder<N>& dgvh)
{
m_numComps = N;
setSpatialDims();
intializeHaloMetadata();
}

/*!
* @brief Constructs a halo object from DGVector
* @param dgv DGVector object to create halo from
*/
template <int N> Halo(DGVector<N>& dgv)
{
m_numComps = N;
isVertex = false;
setSpatialDims();
intializeHaloMetadata();
}

/*!
* @brief Constructs a halo object from CGVector
* @param cgv CGVector object to create halo from
*/
template <int N> Halo(CGVector<N>& cgv)
{
m_numComps = 1;
isCG = true;
CGdegree = N;
nCells = CGdegree;
setSpatialDims();
intializeHaloMetadata();
Copy link
Contributor

Choose a reason for hiding this comment

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

In none of these cases do the constructors depend on their argument, right? The only information that's being used is the value of N and the type. So do we actually need to provide dgv, dgvh, or cgv as arguments?

Copy link
Contributor

Choose a reason for hiding this comment

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

There seems to be quite a lot of implementation in this header file. Might it be better to separate it out into core/src/Halo.cpp?

Copy link
Member

Choose a reason for hiding this comment

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

Agreed. Function definitions should go in a source file if possible. Exceptions are (public) template functions which have to be defined in the header and maybe one-liners.

Comment on lines +265 to +269
for (size_t comp = 0; comp < m_numComps; ++comp) {
Eigen::Map<Eigen::ArrayXXd, 0, Eigen::InnerStride<Eigen::Dynamic>> source_map(
source.col(comp).data(), m_Nx, m_Ny, Eigen::InnerStride<>(m_numComps));
Eigen::Map<Eigen::ArrayXXd> buffer_map(send[comp].data(), buffer_len, nCells);
for (auto edge : edges) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This code could benefit from some comments as I'm struggling to understand what it does. numComps is the N value in the template constructor, right?

Comment on lines +276 to +280
/**
* @brief Calculate which edge the send position sendPos would fall under in the send buffer
*
*/
Edge edgeFromSendPos(int sendPos, int fromRank)
Copy link
Contributor

Choose a reason for hiding this comment

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

Missing @params etc in docstring

count = 1;
disp = disp + sendEdge;
recvOffset = recvOffset + 4;
// this is messy
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this a TODO to do it differently?

Comment on lines +691 to +699
if (metadata.cornerRanks[corner].size() > 0) {
// non-periodic case
hasCorner = true;
isPeriodic = false;
} else if (metadata.cornerRanksPeriodic[corner].size() > 0) {
// periodic case
hasCorner = true;
isPeriodic = true;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Presumably it's impossible for both conditions to be true at the same time? Or would it be useful to include a failure mode in case it happens due to a bug?

}
}

template <typename T> void populateTarget(T& target)
Copy link
Contributor

Choose a reason for hiding this comment

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

Please add a docstring.


readNeighbourData(ncFile);

// cornerHaloRecv doesn't need to be read because it can be easily calculated.
Copy link
Contributor

Choose a reason for hiding this comment

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

I guess there's an assumption (probably right) that a small amount of computation is cheaper than a small amount of I/O?

Copy link
Contributor

Choose a reason for hiding this comment

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

Nice to reduce the amount of numbers hard-coded in code here, well done!

Copy link
Member

@Thanduriel Thanduriel left a comment

Choose a reason for hiding this comment

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

Here is my feedback.

neighbourArray neighbourHaloSendPeriodic;
neighbourArray neighbourHaloRecvPeriodic;

typedef std::array<std::vector<int>, N_CORNER> cornerArray;
Copy link
Member

Choose a reason for hiding this comment

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

Types should be in CamelCase.

using CornerArray = std::array<std::vector<int>, N_CORNER>;

std::array<std::string, N_CORNER> cornerNames
= { "top_left", "top_right", "bottom_right", "bottom_left" };

typedef std::array<std::vector<int>, N_EDGE> neighbourArray;
Copy link
Member

Choose a reason for hiding this comment

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

Types should be in CamelCase. Prefer using over typedef (see CppCoreGuidlines).

using NeighbourArray = std::array<std::vector<int>, N_EDGE>;

* @brief Gets the extents of the grid in the X direction for all ranks.
* @return A vector containing the number of grid points in X for each rank.
*/
std::vector<int> getRankExtentsX() const;
Copy link
Member

Choose a reason for hiding this comment

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

Return by const& to prevent unnecessary copies.

* @brief Gets the extents of the grid in the Y direction for all ranks.
* @return A vector containing the number of grid points in Y for each rank.
*/
std::vector<int> getRankExtentsY() const;
Copy link
Member

Choose a reason for hiding this comment

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

Return by const& to prevent unnecessary copies.

Comment on lines +684 to +685
int fromRank;
size_t disp;
Copy link
Member

Choose a reason for hiding this comment

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

These variables could be defined closer to where they are used, i.e. in the if (hasCorner) branch.

auto buffer_len = recvBufferSize / CGdegree;
for (size_t comp = 0; comp < m_numComps; ++comp) {
Eigen::Map<Eigen::ArrayXXd> rmap(recv[comp].data(), buffer_len, nCells);
auto offset = buffer_len - (4 - corner) * nCells;
Copy link
Member

Choose a reason for hiding this comment

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

Where does this 4 come from? Is it just Corner::N_CORNER?

Comment on lines +225 to +226
std::vector<int> ModelMetadata::getRankExtentsX() const { return rankExtentsX; }
std::vector<int> ModelMetadata::getRankExtentsY() const { return rankExtentsY; }
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
std::vector<int> ModelMetadata::getRankExtentsX() const { return rankExtentsX; }
std::vector<int> ModelMetadata::getRankExtentsY() const { return rankExtentsY; }
const std::vector<int>& ModelMetadata::getRankExtentsX() const { return rankExtentsX; }
const std::vector<int>& ModelMetadata::getRankExtentsY() const { return rankExtentsY; }

Comment on lines +26 to +29
constexpr ModelMetadata::Corner TOP_LEFT = ModelMetadata::Corner::TOP_LEFT;
constexpr ModelMetadata::Corner TOP_RIGHT = ModelMetadata::Corner::TOP_RIGHT;
constexpr ModelMetadata::Corner BOTTOM_RIGHT = ModelMetadata::Corner::BOTTOM_RIGHT;
constexpr ModelMetadata::Corner BOTTOM_LEFT = ModelMetadata::Corner::BOTTOM_LEFT;
Copy link
Member

@Thanduriel Thanduriel Feb 18, 2026

Choose a reason for hiding this comment

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

If the intend here is just to introduce shorter names, using would be more clear:

using enum ModelMetadata::Corner;

Comment on lines +285 to +286
auto extentX = metadata.getRankExtentsX()[fromRank];
auto extentY = metadata.getRankExtentsY()[fromRank];
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 it would be better to not use auto if the expression has a simple type. Here the type is just int while in many other places the standard size type size_t is used instead. The distinction between signed and unsigned ints is important because the following conditions would not work with an unsigned type.

Comment on lines +346 to +347
// TODO this is too big. Should only be 2 x CGdegree * haloWidth (extentX + extentY)
auto fromRankSendBufferSize = 2 * haloWidth * CGdegree * (extentX + extentY);
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 todo outdated? The line below looks just like suggested.

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

Labels

enhancement New feature or request ICCS Tasks or reviews for the ICCS team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants