-
Notifications
You must be signed in to change notification settings - Fork 37
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 baroclinic gyre tests #547
Add baroclinic gyre tests #547
Conversation
4ccb04f
to
a3baa9c
Compare
Nice! All tests passing! |
""" | ||
super().__init__(mpas_core=mpas_core, name='mitgcm_baroclinic_gyre') | ||
|
||
for resolution in ['80km']: |
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.
Consider adding a comment that resolutions here must be added with the 'km' suffix (since later you chop off the last 2 characters to get the float)
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.
An alternative would be to provide the resolution as a float and add the km
suffix to the subdirectory. I think that might be cleaner. (I know we were following ziso, so that one should probably be fixed in a similar way.)
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.
By the way, I have something along these lines here, where the resolution is always given in meters
compass/compass/ocean/tests/turbulence_closure/default/__init__.py
Lines 38 to 41 in f8af494
if resolution >= 1e3: | |
res_name = f'{int(resolution/1e3)}km' | |
else: | |
res_name = f'{int(resolution)}m' |
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.
@cbegeman Working on this resolution
change now. A few questions/suggestions:
- Are you satisfied with resolution in meters by default? It makes sense for turbulence closure since we go to fine resolution. Is it best to be consistent across cases? Happy to switch mine, just wanted to check.
- the comment blocks in your
forward
(L12, L25) andinitial_state
(L22, L34) still carry comments where resolution is expected to be string, though it is the float that is passed (as far as I understand). It may be good to propagate the update. - The
run
inforward
still assumes a string resolution with units. I couldn't find a call for it so maybe it's just stale code, but worth updating in case it ever get exercised.
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.
- If the
resolution
variable is only present in your case-specific python code (e.g., not in the config file), I think you can use whatever units are convenient for your cases. We care more about standardizingdc
(in config files) and the resolution directory name across cases. - Thanks for pointing this out. I'm not actively developing that branch (and case) any more because we didn't have success validating mpas_les. If I return to it, I'll fix this up.
- Same as (2)
bottT = temperature[0, 200, -1].values | ||
surfT = temperature[0, 200, 0].values | ||
print(f'bottom T: {bottT} and surface : {surfT}') | ||
salinity = 34.0 * xr.ones_like(temperature) |
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.
Consider making certain initial temperature and salinity parameters a part of the mitgcm_baroclinic_gyre
section in your config file
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.
The initial temperature is a function of depth. @alicebarthel has constructed a function that's a pretty good approximation of the table of numbers used in the MITgcm version of the test case. It's full of "magic" numbers so they wouldn't be super easy to add to the config file. But maybe there would be a way to give the top (z=0) and bottom (z=bottom_depth) temperature and have them vary between those two with your prescribed functional form? That would seem cleaner to me.
I wouldn't hardcode sampling bottT
and surfT
at the 200th grid cell here. Either you should take grid cell 0 or you'll want to have a number somehow based on nCells
. We don't necessarily know that the mesh will have a 200th grid cell (though it's obviously highly likely) since we're flexible about the resolution.
Regarding the initial salinity, I agree for sure that that needs to be a config option rather than a magic number here.
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.
Good point @xylar re. cell 200. @cbegeman I like the idea of it being more general (setting T in cfg) but I have not come up with a satisfying way to do that at this stage. For the original purpose of matching MITgcm, hard-coding the structure was easiest. It can extend to a deeper set-up, but approximates their values over 0-1800m. I'll look to see about setting it from top and bottom T keeping the existing functional form. We could do a prettier functional form with, say, a thermocline depth parameter setting the change in T=f(z) shape, but I have not looked into it that closely.
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.
@alicebarthel, couldn't you have config options that are the temperatures in the middle of the top and bottom layers respectively? Then, couldn't you take your existing functional form but stretch it linearly so it hits the desired top and bottom temperatures?
Alternatively, if you want to be very particular about having the same temperature profile independent of resolution, couldn't you do the same but instead of the middle of the top and bottom layers, you would use z = 0 and z=1800 m. The default values would be chosen such that the temperature in the middle of the top and bottom layers is quite close to the value you currently get with your functional form.
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.
Moved the constant salinity value to the cfg file. The initial temperature profile is now set (still with a "magical" functional form and a fitted parameter) from the top and bottom temperature set at the surface (z=0m) and bottom (z = bottom_depth). The default values approximate the mitgcm stratification, but we could add a table to reach to be more exact.
@alicebarthel I think this is coming together really nicely! I left only minor comments at this point. |
0c341eb
to
3bac95a
Compare
4145dce
to
b976f7a
Compare
85775b3
to
6a48d21
Compare
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.
I had some issues mainly with the docs. A few things to clean up there.
I successfully ran the |
@alicebarthel, the outputs you posted above look great to me! I think we're almost there... |
Thanks @xylar, that's super helpful. Attached are updated mean state plots for year 100 of the 80km. It looks nicely spun-up to me, at least dynamically: |
@alicebarthel This is awesome progress! Is this ready for review? Are you planning on adding a visualization or analysis step to aid comparison with MITgcm https://mitgcm.readthedocs.io/en/latest/examples/baroclinic_gyre/baroclinic_gyre.html#model-solution? The heat flux figure you posted looks like a good comparison with Fig 2.7. Do you think we want the analogs to Figs 4.8 or 4.9? |
Thanks @cbegeman! For now, all the analysis is offline. Yes, there are a few more plots I want to make, including Figure 4.9b. Figure 4.8 may be good too - although at this stage the SSH contour looks good enough for me. Adding the barotropic streamfunction could be nice but feels lower priority for me.
At this stage, I am focusing on:
I welcome comments and suggestions as I work on this. |
How about keeping the long test and having analysis/viz steps included in that test (and plot the last time slice in the output) but not include these steps in the performance test? I think it's reasonable to have the users change the duration. I didn't read through the docs yet, but let's make sure that ~50 year duration is recommended there. |
if self.name == '3_year_test': | ||
replacements = {'config_do_restart': '.true.', | ||
'config_start_time': "'file'"} | ||
self.update_namelist_at_runtime(replacements) |
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.
This isn't working because the name of the step is always forward
, not 3_year_test
. One fix is:
if self.name == '3_year_test': | |
replacements = {'config_do_restart': '.true.', | |
'config_start_time': "'file'"} | |
self.update_namelist_at_runtime(replacements) | |
if self.test_case.name == '3_year_test': | |
replacements = {'config_do_restart': '.true.', | |
'config_start_time': "'file'"} | |
self.update_namelist_at_runtime(replacements) |
Or I think it might be more logical to store the long
argument as an attribute and use it here:
if self.name == '3_year_test': | |
replacements = {'config_do_restart': '.true.', | |
'config_start_time': "'file'"} | |
self.update_namelist_at_runtime(replacements) | |
if self.long: | |
replacements = {'config_do_restart': '.true.', | |
'config_start_time': "'file'"} | |
self.update_namelist_at_runtime(replacements) |
Merge branch 'add-mitgcm-moc-analysis' into add-mitgcm-baroclinic-gyre
print(f'analytical bottom T: {val_bot} at \ | ||
depth : {bottom_depth}') |
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 and in similar places, this is the preferred way to continue an f-string on a subsequent line:
print(f'analytical bottom T: {val_bot} at \ | |
depth : {bottom_depth}') | |
print(f'analytical bottom T: {val_bot} at ' | |
f'depth : {bottom_depth}') |
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.
Thanks @xylar , I modified these. Let me know if I missed others.
6aeafb9
to
5d571a9
Compare
@xylar @cbegeman This is ready for a review. I ran the performance and 3_year tests on Chrysalis without issue. I also ran the 3_year test for longer (50years, which takes ~2hours for 80km set-up). |
@alicebarthel, that looks awesome! I'm glad we got the surface restoring worked out. I'll review it tomorrow. |
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.
@alicebarthel, this is some amazing work! It's very nearly there!
I was able to run the 80km tests without a hitch! I'm running the 20 km tests now. In the meantime, I have several small suggestions that you could make, or we could discuss further.
docs/developers_guide/ocean/api.rst
Outdated
BaroclinicGyre | ||
|
||
GyreTestCase | ||
GyreTestCase.configure | ||
|
||
forward.Forward | ||
forward.Forward.run | ||
|
||
initial_state.InitialState | ||
initial_state.InitialState.run |
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.
The other classes should be added here as well (CullMesh
, Moc
and Viz
).
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.
Thanks! Is this supposed to be alphabetical or does the order matter?
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.
Yes, alphabetical is easiest.
At this stage, the test case is available at 80-km and 20-km horizontal | ||
resolution. By default, the 15 vertical layers vary linearly in thickness with depth, from 50m at the surface to 190m at depth (full depth: 1800m). | ||
|
||
The test group includes 2 test cases, called ``performance_test`` for a short (10-day) run, and ``3_year_test`` for a 3-year simulation. Note that 3 years are insufficient to bring the standard test case to full equilibrium. Both test cases have 2 steps, |
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.
The test group includes 2 test cases, called ``performance_test`` for a short (10-day) run, and ``3_year_test`` for a 3-year simulation. Note that 3 years are insufficient to bring the standard test case to full equilibrium. Both test cases have 2 steps, | |
The test group includes 2 test cases, called ``performance_test`` for a short (3-time-step) run, and ``3_year_test`` for a 3-year simulation. Note that 3 years are insufficient to bring the standard test case to full equilibrium. Both test cases have 2 steps, |
Framework | ||
--------- |
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.
It seems like cull_mesh
, moc
and viz
need to be added to this.
maxloc = 'at lat = {} and z = {}m'.format( | ||
latbins[idx[-1]].values, int(dsMesh.refInterfaces[idx[0]].values)) | ||
maxval = 'max MOC = {:.1e} at lat={}-{}'.format( | ||
round(np.max(moc[:, 175]), 1), | ||
latbins[175 - 1].values, latbins[175].values) |
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.
Ideally, break this into several lines where you store these various inputs in variables and then use f-strings rather than the .format()
methods to put them in place.
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.
I think this looks great! I just visually inspected the code. Would you like me to run tests on any particular machine or build docs?
# Number of vertical levels | ||
vert_levels = 15 | ||
|
||
# Total water column depth |
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.
# Total water column depth | |
# Total water column depth in m |
# the type of vertical grid | ||
grid_type = linear_dz | ||
|
||
# the linear rate of thickness increase for linear_dz |
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.
units?
restoring_temp_timescale = 30. | ||
|
||
# config options for the mean state visualization | ||
[mean_state_viz] |
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.
[mean_state_viz] | |
[baroclinic_gyre_viz] |
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.
Thanks! I modified this to include both viz and moc calculation parameters.
:py:class:`compass.mesh.QuasiUniformSphericalMeshStep`. Then, the mesh is | ||
culled to keep a single ocean basin (with lat, lon bounds set in `.cfg` file). A vertical grid is generated, | ||
with 15 layers of thickness that increases linearly with depth (10m increase by default), from 50m at the surface to 190m at depth (full depth: 1800m). | ||
Finally, the initial temperature field is initialized with a vertical profile to approximate the discrete values set in the `MITgcm test case <https://mitgcm.readthedocs.io/en/latest/examples/baroclinic_gyre/baroclinic_gyre.html>`_, uniform in horizontal space. The surface and bottom values are set in the `.cfg` file. Salinity is set to a constant value (34 psu, set in the `.cfg` file) and initial |
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.
Break up into multiple lines here and elsewhere. I think max length should be 89 char.
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.
Yes, 79 characters actually. The width of a default terminal is 80, so that's the reasoning.
@@ -12,6 +12,7 @@ coming months. | |||
:titlesonly: | |||
|
|||
baroclinic_channel | |||
baroclinic_gyre |
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.
@xylar Is this what you meant in your review of the users_guide above?
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.
Looks great! Thanks for making the requested changes.
This PR aims to add the MITgcm baroclinic test case to compass. Started as a Work In Progress [WIP] PR to allow discussion and debugging before merging.
Checklist
api.rst
) has any new or modified class, method and/or functions listedTesting
in this PR) any testing that was used to verify the changes