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

Fix bugs in ESMF interpolation method #544

Merged

Conversation

matthewhoffman
Copy link
Member

@matthewhoffman matthewhoffman commented Dec 17, 2023

This merge fixes a number of issues with the 'esmf' method for interpolation in the MALI interpolation script:

  • indexing was off by one, which made interpolation with unstructured source meshes garbage. For structured source meshes, it made interpolation shifted by one grid cell.
  • support MPAS source fields with a vertical dimension when using the 'esmf' method
  • refactor to use sparse matrix multiply, which speeds up interpolation a few hundred times
  • add destination mesh area normalization support. This is necessary when using the ESMF 'conserve' method for destination cells that are only partly overlapped by the source cells.

Two issues remain for interpolating between two MPAS meshes with the 'esmf' method:

  1. If the destination mesh is larger than the source mesh, those locations are filled with zeros. The ESMF 'conserve' method does not support extrapolation, and there is no obvious solution to this issue and it would need to be handled manually on a case by case basis.
  2. Some fields are only defined on subdomains (e.g. temperature is only defined where ice thickness is nonzero) and the script currently has no mechanism for masking them. This results in garbage values getting interpolated in, e.g. temperature values near the margin will have values around 100 K because of interpolating realistic values around 250K with garbage values of 0K. This issue applies to the barycentric method as well. However, this situation can be worked around by performing extrapolation of the temperature field on the source mesh before doing interpolation between meshes.

This PR also includes a refactoring of create_SCRIP_file_from_planar_rectangular_grid.py that speeds it up by orders of magnitude for large meshes, as well as a minor update to define_cullMask.py

This bug yielded garbage results when interpolating between two MPAS
meshes.  When interpolating from a regular grid to an MPAS grid, it
would cause a shift of one cell on the regular grid.
For interpolating from 4km AIS (385379 cells) to 8km AIS (98341 cells)
this reduced interpolation time for one variable from 13 seconds to
0.04 seconds (~300x speedup).
This was never attempted before and needed additional handling.
@matthewhoffman
Copy link
Member Author

@trhille , I decided to leave this branch as is and ask for review now (no rush). The issue about the conserve method filling 0s in regions of extrapolation of the source mesh is a larger problem that it is not obvious how to resolve, so I left it as a known issue for now. But if you have ideas about appropriate functionality for that situation, I'm happy to do further work in this PR.

@matthewhoffman
Copy link
Member Author

@xylar , there is a commit in this PR that makes an update to mesh_tools/create_SCRIP_files/create_SCRIP_file_from_planar_rectangular_grid.py. However, it is unclear to me if I should also be updating the copy of this file at conda_package/mesh_tools/create_SCRIP_files/create_SCRIP_file_from_planar_rectangular_grid.py. It seems that I need to do that for compass to be able to see my changes, but when I try to commit that change, I get this error:

The following paths are ignored by one of your .gitignore files:
conda_package/mesh_tools

Can you explain to me how this is supposed to be handled? I've never fully understood how the conda package interacts (or doesn't) with the legacy scripts.

@xylar
Copy link
Collaborator

xylar commented Dec 21, 2023

@matthewhoffman, when you do a local install of the conda_package into a conda environment, e.g. with:

python -m pip install conda_package

it makes a copy of the mesh_tools, ocean, land_ice and other directories that contain scripts because it can't include anything in the python package that lives outside of conda_package. So if you're modifying scripts outside of conda_package, you should run the command above again to first copy the script(s) over again and then install them into the conda environment.

Ideally, if we want to have mpas_tools be a "proper" python package, we would stop making scripts and switch to entry points that live within conda_package/mpas_tools. But given that that's not the workflow that you all are used to, this copying over is necessary.

@matthewhoffman
Copy link
Member Author

@xylar , thanks for clarifying that. I had run python -m pip install conda_package but I hadn't appreciated that because of the legacy scripts, that that command would need to be rerun every time one of those scripts was modified. Now I understand the workflow and have more incentive to move things into the conda package. :)

For large grids, this script was very slow.  For the 500m BedMachine-AIS
dataset, it's been running for over half an hour and is not complete.
After refactoring, it took about 3 minutes.
@matthewhoffman
Copy link
Member Author

Testing

These updated scripts were used for the 2km AIS mesh generation in MPAS-Dev/compass#750 and it worked as expected end to end. The refactoring sped up those individual operations dramatically.

Copy link
Collaborator

@trhille trhille left a comment

Choose a reason for hiding this comment

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

@matthewhoffman, this is fantastic! Thanks for making these changes, which collectively reduce the pain of mesh generation by a huge margin. I have one measly complaint about an inscrutable variable name, but otherwise this is ready to go.

wfile.close()
#----------------------------

# convert to sparse matrix format
wt_csr = scipy.sparse.coo_array((S, (row - 1, col - 1)), shape=(n_b, n_a)).tocsr()
Copy link
Collaborator

Choose a reason for hiding this comment

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

I dislike the variable name wt_csr, because it requires googling to figure out what it means. Can we use a longer, more descriptive name? Also, is wt short for weight?

Copy link
Member Author

Choose a reason for hiding this comment

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

Updated in b0e6147.

sourceFieldFlat = sourceField.flatten() # Flatten source field
for i in range(len(row)):
destinationField[row[i]-1] = destinationField[row[i]-1] + S[i] * sourceFieldFlat[col[i]]
source_csr = scipy.sparse.csr_matrix(sourceField.flatten()[:, np.newaxis])
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same complaint as above about the csr in the variable name, although here it's adjacent to the scipy method that helps explain it some.

Copy link
Member Author

Choose a reason for hiding this comment

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

Updated in b0e6147.

@@ -328,7 +337,7 @@ def interpolate_field_with_layers(MPASfieldName):
if filetype=='cism':
print(' Input layer {}, layer {} min/max: {} {}'.format(z, InputFieldName, InputField[z,:,:].min(), InputField[z,:,:].max()))
elif filetype=='mpas':
print(' Input layer {}, layer {} min/max: {} {}'.format(z, InputFieldName, InputField[:,z].min(), InputField[z,:].max()))
Copy link
Collaborator

Choose a reason for hiding this comment

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

Was this just a typo?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, this was a typo that has been present from whenever this print statement was introduced. It wasn't obviously incorrect from the output because the typo version still yielded reasonable values.

@xylar
Copy link
Collaborator

xylar commented Jan 8, 2024

I'm about to make a new release to fix #547. If this can be merged today, I'll wait for it. Otherwise, it will be in the next release.

@xylar
Copy link
Collaborator

xylar commented Jan 8, 2024

If this can be merged today

On second thought, I'll be testing in E3SM-Unified for a few more days most likely so if this gets merged in the next 2 or 3 days, that should be soon enough.

@matthewhoffman
Copy link
Member Author

@trhille , thanks for the feedback on these changes. I've added a little more detail to the comments and improved the new variable name. If you prefer more detail, let me know.

@trhille
Copy link
Collaborator

trhille commented Jan 18, 2024

@matthewhoffman, thanks! That looks great. This is ready to merge, as far as I'm concerned, although I see that two of the CI checks were not successful.

@matthewhoffman
Copy link
Member Author

@trhille , thanks for catching that. Not sure why they didn't run, but I manually retriggered them and they all pass now. @xylar , did you have any other feedback on this given it touches one non-MALI specific tool?

@matthewhoffman matthewhoffman self-assigned this Jan 18, 2024
Copy link
Collaborator

@xylar xylar left a comment

Choose a reason for hiding this comment

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

The SCRIP changes look good to me!

@xylar
Copy link
Collaborator

xylar commented Jan 19, 2024

I need to make an MPAS-Tools release ASAP so let me know if this can get merged today.

@matthewhoffman matthewhoffman merged commit 6c89e44 into MPAS-Dev:master Jan 19, 2024
7 checks passed
@matthewhoffman matthewhoffman deleted the landice/esmf_interp_fix branch January 19, 2024 16:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants