-
Notifications
You must be signed in to change notification settings - Fork 65
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 bandwidth-minimising mesh sorting routine #515
Conversation
Just keeping this in draft for now @xylar --- before going too far I think we should check whether this reindexing will solve the locality issues wrt. the E3SM coupler and I've reached out to the team re: benchmark cases. |
@dengwirda. beautiful! Keep me posted. |
Picasso, by the way, is turning over in his grave. The unsorted way it clearly more cubist. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dengwirda, I have some suggestions that are purely aesthetic to do with integrating this code a bit more with the style of mpas_tools
overall. None of this has any bearing on functionality.
I'm going to cherry-pick the current commit onto a branch I have for making release candidates for testing in compass. I'll let you know how that testing goes.
""" | ||
SORT-MESH: sort cells, edges and duals in the mesh | ||
to improve cache-locality. | ||
|
||
""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's the docstring format we use in MPAS-Tools:
""" | |
SORT-MESH: sort cells, edges and duals in the mesh | |
to improve cache-locality. | |
""" | |
""" | |
Sort cells, edges and duals in the mesh | |
to improve cache-locality. | |
Parameters | |
---------- | |
mesh : xarray.Dataset | |
A dataset containing an MPAS mesh to sort | |
Returns | |
------- | |
mesh : xarray.Dataset | |
A dataset containing the sorted MPAS mesh | |
""" |
from scipy.sparse.csgraph import reverse_cuthill_mckee | ||
|
||
|
||
def sort_fwd(data, fwd): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's indicate that this is "private":
def sort_fwd(data, fwd): | |
def _sort_fwd(data, fwd): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's also move it after sort_mesh()
. Similarly for other "private" functions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe add a very brief docstring to at least say what the function does.
return vals | ||
|
||
|
||
def sort_rev(data, rev): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
def sort_rev(data, rev): | |
def _sort_rev(data, rev): |
Make this private, move it below the "public" functions and add a brief docstring.
for var in mesh.keys(): | ||
dims = mesh.variables[var].dims | ||
if ("nCells" in dims): | ||
mesh[var][:] = sort_fwd(mesh[var], cell_fwd) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mesh[var][:] = sort_fwd(mesh[var], cell_fwd) | |
mesh[var][:] = _sort_fwd(mesh[var], cell_fwd) |
for var in mesh.keys(): | ||
dims = mesh.variables[var].dims | ||
if ("nVertices" in dims): | ||
mesh[var][:] = sort_fwd(mesh[var], dual_fwd) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mesh[var][:] = sort_fwd(mesh[var], dual_fwd) | |
mesh[var][:] = _sort_fwd(mesh[var], dual_fwd) |
return mesh | ||
|
||
|
||
def cell_del2(mesh): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
def cell_del2(mesh): | |
def _cell_del2(mesh): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, move this below main()
along with the other "private" functions.
Alternatively, if you want to keep this "public", it should have a fleshed out docstring like I suggested for sort_mesh()
.
|
||
def cell_del2(mesh): | ||
""" | ||
CELL-DEL2: form cell-to-cell sparse adjacency graph |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
CELL-DEL2: form cell-to-cell sparse adjacency graph | |
form cell-to-cell sparse adjacency graph |
|
||
# sort cells via RCM ordering of adjacency matrix | ||
|
||
cell_fwd = reverse_cuthill_mckee(cell_del2(mesh)) + 1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cell_fwd = reverse_cuthill_mckee(cell_del2(mesh)) + 1 | |
cell_fwd = reverse_cuthill_mckee(_cell_del2(mesh)) + 1 |
ncells = mesh.dims["nCells"] | ||
nedges = np.count_nonzero(cellsOnCell) // 2 | ||
|
||
fptr.write("{} {}\n".format(ncells, nedges)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Going forward, we have a pretty strong preference for f-strings over using the .format()
method.
fptr.write("{} {}\n".format(ncells, nedges)) | |
fptr.write(f"{ncells} {nedges}\n") |
data = cellsOnCell[cell, :] | ||
data = data[data > 0] | ||
for item in data: | ||
fptr.write("{} ".format(item)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fptr.write("{} ".format(item)) | |
fptr.write(f"{item} ") |
One more thing: It would be good to add the entry point (redundantly, I know) to this file: I added the entry point and a test like I'm suggesting to the |
return csr_matrix((xvec, (ivec, jvec))) | ||
|
||
|
||
if (__name__ == "__main__"): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To work properly as an entry point, this needs to be a function:
if (__name__ == "__main__"): | |
def main(): |
Then, at the bottom of the file, you can have:
if (__name__ == "__main__"):\
main()
I tested things out in MPAS-Dev/compass#657 and everything worked well. I need to visualize the @dengwirda would you like to make the changes I mentioned above? If so, that would be my preference but I'm also happy to make them if you don't have time. |
@dengwirda, using Paraview, I looked at all the following fields from the ECwISC mesh I tested earlier:
and they look great to me. I won't post a bunch of identical-looking plots here to prove it. So I think this is good to go as soon as my earlier clean-up comments are addressed. |
This comment was marked as resolved.
This comment was marked as resolved.
Thanks for the review @xylar, I believe I got all of those changes in but let me know if you need anything further. |
@dengwirda, it looks excellent! Let me just make another test build with both this and the fixes I have been making in #514. If my testing goes well, I'll approve and merge this. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After using this sorting in MPAS-Dev/compass#657 and re-testing, I uncovered 2 unrelated issues that my testing of this work happened to expose:
#518
MPAS-Dev/compass#661
After those fixes, I was able to run successfully with this mesh sorting for QU240, QUwISC240, EC30to60 and ECwISC30to60 meshes all the way through files_for_e3sm
in compass.
This PR adds a mesh sorting routine
mpas_tools.mesh.creation.sort_mesh
that can be used to reindex an MPAS mesh file to bring the cells, edges and vertices into a more cache-local sequence. This undoes the somewhat 'random' ordering introduced by the mesh conversion tools, and may be desirable re: efficiency for downstream dycore + coupler applications.The reordering is implemented via a reverse cuthill-mckee approach --- minimising the bandwidth of the cell-to-cell adjacency graph and then permuting the edges and vertices to match. Sorting both culled and unculled meshes is supported, as well as meshes containing additional variables, which are reindexed according to their underlying
nCells
,nEdges
ornVertices
dimensions.The mesh sorter should be callable as a cmd-line tool:
and/or inline:
Tested on
pm-cpu
using a QU30km MPAS-O standalone config., which successfully spun-up to 90 days after a sort of the culled mesh. Sorting improves the locality of the various remapping arrays (verticesOnCell
,edgesOnVertex
, etc, etc) with all showing smooth distributions.edgesOnCell=unsorted
:edgesOnCell=issorted
: