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

T-Laplacian for POP tripolar grid for varying dx, dy #29

Merged
merged 32 commits into from
Apr 1, 2021

Conversation

NoraLoose
Copy link
Member

This adds the POP_TRIPOLAR_T_GRID and the associated Laplacian POPTripolarLaplacianTpoint. This addition is for creating general filters on the POP tripolar grid and needs the user to input stuff like dxe, dye, dxn, dyn - grid length spacing of the eastern and northern T-cell edges. (In contrast, the Laplacian in PR #26 is only for fixed factor filtering and doesn't need any grid information except wet_mask.)

I also included a notebook that compares the action of the 5 different Laplacian kernels that we have so far (on POP model output), and compares the timing of these on CPU.

This PR sits on top of #26, so #26 should be merged first.

* get rid off "position" argument, and instead have two separate
  kernels POPTripolarSimpleLaplacianTpoint and
  POPTripolarSimpleLaplacianUpoint
* comment out the check that U-field is folded properly (until this   discussion in PR#26 is resoloved)
 # Please enter the commit message for your changes. Lines starting
* comment out conservation tests for tripolar U-points until we
  have figured out how/if we can impose this property (PR ocean-eddy-cpt#26)
@codecov-io
Copy link

codecov-io commented Feb 24, 2021

Codecov Report

Merging #29 (79aef47) into master (ed1a007) will increase coverage by 1.58%.
The diff coverage is 100.00%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master      #29      +/-   ##
==========================================
+ Coverage   96.22%   97.81%   +1.58%     
==========================================
  Files           7        7              
  Lines         265      457     +192     
==========================================
+ Hits          255      447     +192     
  Misses         10       10              
Flag Coverage Δ
unittests 97.81% <100.00%> (+1.58%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
gcm_filters/filter.py 94.95% <ø> (+0.51%) ⬆️
gcm_filters/kernels.py 100.00% <100.00%> (ø)
tests/test_filter.py 100.00% <100.00%> (ø)
tests/test_kernels.py 100.00% <100.00%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update ed1a007...79aef47. Read the comment docs.

@rabernat
Copy link
Contributor

rabernat commented Mar 4, 2021

Is this ready for review?

@NoraLoose
Copy link
Member Author

Yes, ready for review.

Copy link
Contributor

@rabernat rabernat left a comment

Choose a reason for hiding this comment

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

This is really excellent work Nora! I tried hard to find areas to improve your code but could not! 😊 I especially like your tests for the tripolar boundary condition.

My only question is about our terminology to describe these different grids.

"CARTESIAN",
"CARTESIAN_WITH_LAND",
"IRREGULAR_CARTESIAN_WITH_LAND",
"POP_SIMPLE_TRIPOLAR_T_GRID",
Copy link
Contributor

Choose a reason for hiding this comment

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

This kernel strikes me as very similar to the CARTESIAN_WITH_LAND, the only difference being the boundary condition. Do we want to think about harmonizing the names? What about TRIPOLAR_CARTESIAN_WITH_LAND?

Or maybe we want to rename things like REGULAR, REGULAR_WITH_LAND, TRIPOLAR_REGULAR_WITH_LAND, etc?

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks, Ryan! Good point about harmonizing the names. I like your suggestion, and vote for: REGULAR, REGULAR_WITH_LAND, IRREGULAR_WITH_LAND, TRIPOLAR_REGULAR_WITH_LAND, TRIPOLAR_IRREGULAR_WITH_LAND. Or we could keep the latter as POP_TRIPOLAR_T_GRID, and change it later to something more generic (e.g., TRIPOLAR_IRREGULAR_WITH_LAND), as you suggested here: ocean-eddy-cpt/gcm-filters-paper#18 (comment)

Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should some distinguish between three types of grids:

  • Regular + Rectangular: dx=dy=const, dimensions are globally orthogonal (what VTK calls "image data")
  • Irregular + Rectangular, dimensions are globally orthogonal, but dx and dy can vary independently (what VTK calls "rectilinear"
  • Curvilinear, dimensions are locally orthogonal, but the grid curves (what vtk calls "structure")
  • Unstructured mesh (e.g. triangles, hexagons, etc.) - probably out of scope for now

In addition to this, we seem to have two independent option spaces

  • Boundary conditions (e.g. singly or doubly periodic, tripolar, cubed-sphere, etc.)
  • Land mask or not

(VTK reference: http://www.bu.edu/tech/support/research/training-consulting/online-tutorials/vtk/)

We don't need to resolve all of this now, but maybe we can use these concepts to inform the naming convention.

Down the road (future PR), we could maybe even use multiple inheritance to define kernels, like

class POPCurvilinearGrid(CurvilinearGrid, GridWithLandMask, TripolarBoundaryCondition):
    pass

Copy link
Member Author

Choose a reason for hiding this comment

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

Agreed, these are useful guidelines.

The thing is that all Laplacians that we have implemented so far can be used on curvilinear grids, even the ones where dx=dy=1, e.g., CARTESIAN_WITH_LAND or POP_SIMPLE_TRIPOLAR_T_GRID. The latter are the Laplacians for fixed factor filtering, but they work for the POP curvilinear grid, for instance.

I like your idea about multiple inheritance to define kernels - this is more or less what I have imagined too. But I appreciate that this is maybe more of a long-term goal, and will come with its own challenges.

Copy link
Member Author

Choose a reason for hiding this comment

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

Since all Laplacians are good for curvilinear grids, I decided to go with

  • "regular": Laplacian needs no grid info (for fixed factor filtering)
  • "irregular": Laplacian needs grid info (for more general filtering, e.g., with fixed scale)

I'm happy to change this if there are better suggestions, either now or later.

Comment on lines +179 to +180
if self.wet_mask[..., 0, :].any():
raise AssertionError("Wet mask requires zeros in southernmost row")
Copy link
Member Author

Choose a reason for hiding this comment

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

@rabernat, related to your comment on checks here, I'm assuming you would suggest to remove this check (and associated test) as well. I added this check because this is a global Laplacian (on a tripole grid), but the southern boundary must be land in order to disable the periodic boundary condition in y-direction. Otherwise, the filtered values in the very south will be contaminated by stuff in the Arctic.

Again, an alternative is to document the requirement of wet mask zeros in the southernmost row carefully, and I am happy to remove this check.

Copy link
Contributor

Choose a reason for hiding this comment

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

Good point.

I will think about this and get back to you. Perhaps fixing the problem at the root (only call __post_init__ once) would be the best path forward.

Copy link
Contributor

Choose a reason for hiding this comment

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

I did some profiling and decided that this is not actually a big performance hit. So we can leave things as are for now.


folded = field[..., [-1], :] # grab northernmost row
folded = folded[..., ::-1] # mirror it
field_extended = np.concatenate((field, folded), axis=-2) # append it
Copy link
Contributor

Choose a reason for hiding this comment

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

👌 perfect style 🤩

Copy link
Contributor

@rabernat rabernat left a comment

Choose a reason for hiding this comment

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

This is great work. I think we can merge it.

- correct np.roll(field, +1, axis=ax) --> np.roll(field, -1, axis=ax)
  and vice versa
- this bug was present in two Laplacians:
  * IRREGULAR_WITH_LAND
  * TRIPOLAR_POP_WITH_LAND
@NoraLoose
Copy link
Member Author

Good that we hadn't merged this. I discovered a sign error in my rolling operations (which I made so consistently throughout my code that the tests did not uncover it). I fixed it, and we should now be ready to merge.

@rabernat
Copy link
Contributor

Nice!

Can you think what sort of test would have caught your sign error? Is there a new type of test we should consider adding to the kernel test suite?

@rabernat
Copy link
Contributor

Is there a new type of test we should consider adding to the kernel test suite?

Hi Nora! Any chance we can think of a test that would have caught the sign error? If not, no big deal.

@NoraLoose
Copy link
Member Author

Yes, I think I have an idea. I have time to work on this today.

If I come up with a successful test, would you like me to submit it as part of this PR? (I would design the test for all irregular Laplacians, i.e., all Laplacians that need grid info dx, dy.)

@rabernat
Copy link
Contributor

If I come up with a successful test, would you like me to submit it as part of this PR?

Yes, that would be great! Thanks so much.

@NoraLoose
Copy link
Member Author

NoraLoose commented Mar 30, 2021

I added two new tests in test_kernels.py that would have caught the sign error: test_flux_in_y_direction, test_flux_in_x_direction. I verified that the old version of the code (before commit ba228f7) would have failed the test.

Edit: This is ready for review now.

Copy link
Contributor

@rabernat rabernat left a comment

Choose a reason for hiding this comment

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

Thanks again @NoraLoose for this amazing PR! And sorry for being slow to review.

My only real concern that, if we are changing the names of things, our documentation tutorials need to be updated for consistency. In https://gcm-filters.readthedocs.io/en/latest/tutorial.html#create-a-filter for example we still use CARTESIAN_WITH_LAND, etc.

Also a few nit-picking code style issues.

With this we will finally be ready to merge. Thanks for your patience and your excellent work.

[
"REGULAR",
"REGULAR_WITH_LAND",
"IRREGULAR_WITH_LAND",
Copy link
Contributor

Choose a reason for hiding this comment

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

For the grid types changing name, we will need to update the tutorial notebooks to be consistent.

self.w_wet_mask = self.wet_mask * np.roll(self.wet_mask, -1, axis=-1)
self.s_wet_mask = self.wet_mask * np.roll(self.wet_mask, -1, axis=-2)
self.w_wet_mask = self.wet_mask * np.roll(self.wet_mask, 1, axis=-1)
self.s_wet_mask = self.wet_mask * np.roll(self.wet_mask, 1, axis=-2)
Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps a comment here explaining the sign convention of the shift argument would be helpful to future developers.

################## Tripolar grid tests ##############################################
tripolar_grids = [
member
for name, member in GridType.__members__.items()
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do you need to use GridType.__members__.items() here? Does for name in GridType not work?

Copy link
Member Author

Choose a reason for hiding this comment

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

for name in GridType raises the error:

TypeError: argument of type 'GridType' is not iterable

Copy link
Contributor

@rabernat rabernat Apr 1, 2021

Choose a reason for hiding this comment

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

That is puzzling to me. GridType is an enum. The following works.

import enum
GridType = enum.Enum("GRID_TYPE", ["A", "B"])
assert [gt.name for gt in GridType] == ["A", 'B']

# would be: all grids that have len(required_grid_vars)>1 (more than just a wet_mask)
irregular_grids = [
member
for name, member in GridType.__members__.items()
Copy link
Contributor

Choose a reason for hiding this comment

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

Same

@NoraLoose
Copy link
Member Author

Thanks for the review @rabernat!

  • I will add some comments on the sign convention of the shift argument, as you suggest.
  • Re the Laplacian name changes: I'm happy to update the tutorials. Do we think that the new naming convention is superior to the old one? It does shorten the names by a little bit...

@NoraLoose
Copy link
Member Author

I think I have made all changes you suggested. Please let me know if you can think of anything else. Thanks!

@rabernat
Copy link
Contributor

rabernat commented Apr 1, 2021

Fantastic! Thanks again for your excellent work on this. I'm excited to keep pushing it forward!

@rabernat rabernat merged commit f3f644a into ocean-eddy-cpt:master Apr 1, 2021
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