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

Runfile timeloop prephysics computations and configuration #1081

Merged
merged 22 commits into from
Mar 23, 2021

Conversation

brianhenn
Copy link
Contributor

@brianhenn brianhenn commented Mar 11, 2021

In order to override the coarse model radiative fluxes with predicted values prior to the land surface model call during a prognostic run, this PR implement a prephysics steps in the sklearn_runfile's loop, after dynamics. It allows for configuring this optional prephysics computation by specifying a machine learning model, in the style of a MachineLearningConfig used by the existing python computations after the physics routine (renamed as the postphysics step by this PR.

Added public API:

  • a PrephysicsConfig specification for prognostic runs, an optional MachineLearningConfig specification

Significant internal changes:

  • added an MLStateStepper object to runtime.steppers.machine_learning which returns ML-predicted states (as opposed to tendencies)

  • runtime.loop adds a _prephysics steps to the loop

  • runtime.loop supports having two Stepper objects, one for the prephysics and one for the previous python computations after physics, now called postphysics

  • Tests added

class Prescriber:
"""A pre-physics stepper which obtains prescribed values from an external source

TODO: Implement methods
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is a stub which does nothing, for the follow-on PR

Copy link
Contributor

Choose a reason for hiding this comment

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

Can you remove it then? I think this should be unit tested before merging to master.

@@ -14,4 +14,6 @@
- fcc46bebe36ea131688f8e15700e18d4
Copy link
Contributor Author

Choose a reason for hiding this comment

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

These checksums were preserved but the test name was not

@brianhenn brianhenn changed the title Feature/timeloop prephysics steps Runfile timeloop prephysics computations and configuration Mar 12, 2021
@brianhenn
Copy link
Contributor Author

Would also be nice to get the fv3gfs-wrapper PR merged first so that we can point at master here.

Copy link
Member

@spencerkclark spencerkclark left a comment

Choose a reason for hiding this comment

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

In my reading this looks like a pretty clean approach to solving the multiple stepper issue. I just have a couple comments / suggestions, but I'll let @nbren12 give the final approval.

I agree it would be good if the fv3gfs-wrapper PR was merged; the changes there are pretty trivial but there seems to be an issue with the CI (I recall I was able to get the tests to pass locally).

Copy link
Contributor

@nbren12 nbren12 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 putting this together. I have a fair number of comments, and am happy to chat over zoom to explain any that don't have clear motivations. Some overall thoughts:

  • we should wait until fv3gfs-wrapper is merged until master
  • Revert changes to machine_learning.py and refactor unique "MLStateStepper" logic to either MLStepper or Loop.
  • Remove Prescriber class and related configs until that feature is functional.
  • Simplify coupled fixture hierarchy in tests.

class Prescriber:
"""A pre-physics stepper which obtains prescribed values from an external source

TODO: Implement methods
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you remove it then? I think this should be unit tested before merging to master.



@dataclasses.dataclass
class PrephysicsConfig:
Copy link
Contributor

Choose a reason for hiding this comment

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

Why make a new object for this? You can use Union directly in UserConfig. It's odd to have a config object with one attribute named "config".


Attributes:
variables: list variable names to prescribe
data_source: path to the source of the data to prescribe
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you mention what kind of data must be at this path? Zarr?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Moot since reverting

prephysics: Optional[PrephysicsConfig]
if "prephysics" in config_dict:
prephysics = dacite.from_dict(
PrephysicsConfig, {"config": config_dict["prephysics"]}
Copy link
Contributor

Choose a reason for hiding this comment

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

This renaming breaks down the 1-1 correspondence between the UserConfig attribute names and the yaml representation. This makes the documentation less useful. Again, PrephysicsConfig doesn't seem like it should be a class.

@@ -109,6 +109,22 @@ It can be used multiple times to specify multiple models. For example::
--model_url path/to_another/model
> fv3config.yaml

Prephysics
Copy link
Contributor

Choose a reason for hiding this comment

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

You can document the prephysics config in the docstring of the UserConfig class.

@@ -60,6 +80,33 @@ def get_mock_sklearn_model() -> fv3fit.Predictor:
return model


def get_mock_rad_flux_model() -> fv3fit.Predictor:
Copy link
Contributor

Choose a reason for hiding this comment

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

Looks like a lot of copy paste here. Can we add some parameters to get_mock_sklearn_model?

@@ -191,30 +218,64 @@ def _get_states_to_output(self, config: UserConfig) -> Sequence[str]:
states_to_output = diagnostic.variables # type: ignore
return states_to_output

def _get_stepper(self, config: UserConfig) -> Optional[Stepper]:
def _get_steppers(self, config: UserConfig) -> Mapping[str, Optional[Stepper]]:
Copy link
Contributor

Choose a reason for hiding this comment

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

We heavily rely on mypy to detect errors in this un-testable Loop class, and the old design was more amenable to static analysis . The Mapping[str, Optional[Stepper]] return type will prevent mypy from detecting errors statically (e.g. if "_compute_physics" is not inside this dict). It will also add more indirection for the reader.

Can you revert these changes, and put the prephysics specific code into _get_prephysics_stepper method and store the stepper in self._prephysics_stepper?

@@ -177,7 +204,7 @@ def __init__(

self._states_to_output: Sequence[str] = self._get_states_to_output(config)
self._log_debug(f"States to output: {self._states_to_output}")
self.stepper = self._get_stepper(config)
self.steppers = self._get_steppers(config)
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
self.steppers = self._get_steppers(config)
self.post_physics_stepper = self._get_stepper(config)
self.pre_physics_stepper = self._get_pre_physics_stepper(config)

@@ -27,6 +27,26 @@ def _model_dataset() -> xr.Dataset:
return data


def _rad_model_dataset() -> xr.Dataset:
Copy link
Contributor

@nbren12 nbren12 Mar 12, 2021

Choose a reason for hiding this comment

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

Copy paste here. Can you add these new variables to _model_dataset and delete this function?

Copy link
Contributor

@nbren12 nbren12 left a comment

Choose a reason for hiding this comment

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

Looking good. Thanks. There was one place below I briefly got confused.

self._log_info("Downloading ML Model")
if self.rank == 0:
local_model_paths = download_model(
ml_config, os.path.join(step, "ml_model")
Copy link
Contributor

Choose a reason for hiding this comment

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

I got a little worried seeing this path logic. At first glance it looks like it could be a backwards incompatible change to how the user configuration is interpreted, but really it is just a temporary local cache. Can you note this in a comment or better yet...modify download_model to use tempfile.NamedTemporaryDirectory?

Comment on lines 339 to 340
self._compute_prephysics,
self._apply_prephysics,
Copy link
Contributor

Choose a reason for hiding this comment

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

Since these functions occur in sequence would it be simpler to group them in one function?

Copy link
Member

@spencerkclark spencerkclark 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 the updates @brianhenn! It seems like we should be able to merge the wrapper PR today too.

@nbren12 nbren12 enabled auto-merge (squash) March 19, 2021 19:49
@nbren12 nbren12 disabled auto-merge March 19, 2021 19:51
Copy link
Member

@spencerkclark spencerkclark left a comment

Choose a reason for hiding this comment

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

ai2cm/fv3gfs-wrapper#244 has now been merged, so we should be able to merge this PR soon, with a few more minor updates.

Comment on lines 114 to 116
"total_sky_downward_shortwave_flux_at_surface_override",
"total_sky_net_shortwave_flux_at_surface_override",
"total_sky_downward_longwave_flux_at_surface_override",
Copy link
Member

Choose a reason for hiding this comment

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

These have been renamed in the merged version of ai2cm/fv3gfs-wrapper#244:

Suggested change
"total_sky_downward_shortwave_flux_at_surface_override",
"total_sky_net_shortwave_flux_at_surface_override",
"total_sky_downward_longwave_flux_at_surface_override",
"override_for_time_adjusted_total_sky_downward_shortwave_flux_at_surface",
"override_for_time_adjusted_total_sky_net_shortwave_flux_at_surface",
"override_for_time_adjusted_total_sky_downward_longwave_flux_at_surface",

Comment on lines 362 to 364
"total_sky_downward_shortwave_flux_at_surface_override",
"total_sky_net_shortwave_flux_at_surface_override",
"total_sky_downward_longwave_flux_at_surface_override",
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
"total_sky_downward_shortwave_flux_at_surface_override",
"total_sky_net_shortwave_flux_at_surface_override",
"total_sky_downward_longwave_flux_at_surface_override",
"override_for_time_adjusted_total_sky_downward_shortwave_flux_at_surface",
"override_for_time_adjusted_total_sky_net_shortwave_flux_at_surface",
"override_for_time_adjusted_total_sky_downward_longwave_flux_at_surface",

@brianhenn brianhenn merged commit 149b545 into master Mar 23, 2021
@brianhenn brianhenn deleted the feature/timeloop-prephysics-steps branch March 23, 2021 23:58
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