unifhy-org / unifhy

A Unified Framework for Hydrology
https://unifhy-org.github.io/unifhy
BSD 3-Clause "New" or "Revised" License
11 stars 5 forks source link

Create subclasses of Model for Water/Energy/Nutrient cycles #14

Open ThibHlln opened 3 years ago

ThibHlln commented 3 years ago

At the moment the framework only focusses on the water cycle, but we aim to include at least the nutrient cycle. Once one other cycle is included in the framework, and the two cycles are able to communicate, adding any additional cycle is likely to be trivial.

At this stage, I think the best way to achieve this is by subclassing the Model class into WaterModel (then NutrientModel, etc.). Each will take one component for each part of the terrestrial water cycle. But their inward/outward transfers will need to be specific to the cycle considered (i.e. compliant with the interface of the given cycle). This is likely to be done by renaming current SurfaceLayerComponent/SubSurfaceComponent/OpenWaterComponent to WaterSurfaceLayerComponent etc. and by creating additional ones for NutrientSurfaceLayerComponent etc. They should all be able to directly be subclasses of Component, as in currently the case for the water cycle components. The superclass Model should retain the Clock and the Coupler.

For now water and energy cycles are likely to be considered together, because they are very intertwined in JULES at the moment, and it would require substantial efforts to isolate them (if possible at all). But, by bringing an additional cycle in the framework, most of the challenges will already have been addressed and, separating water and energy cycles would only require to remove energy fluxes from the water cycle interface + create a new energy cycle + breakdown actual science components to comply with the new interfaces.

ThibHlln commented 2 years ago

One important decision to make here is whether we allow components of other cycles than water+energy to run on different temporal/spatial resolution of the water+energy cycle.

For now, we don't see a use case where different resolutions would be required for different cycles.

mattjbr123 commented 8 months ago

Starting development of separate Nutrient subclasses. I'm seeing this as the master-thread that collates together the various tasks and issues that will need to be completed to successfully add separate nutrient components to unifhy.

An initial, but temporary, solution was to integrate the nutrient components within the existing Surface, Subsurface and Openwater Components which are considered the 'water energy' Components. This means that the necessary transfers between Components are already implemented and the main remaining task is to separate these out into separate 'Nutrient' Components.

To ensure integrity of the code and minimise the chance of breaking stuff, I will follow the 'test-driven-development' idea which requires tests for the code to be developed before the code itself. The tests should fail, and subsequent development should focus on passing the test and no more. As comprehensive tests have already been designed for unifhy, I see the first step towards proper 'test-driven-development' as editing these tests to expect 3 additional Components - the NutrientSurface, NutrientSubsurface and NutrientOpenwater Components. This will cause the tests to fail, with subsequent development just enough to cause the tests to pass and no more. Once this has been achieved I can start following the 'test-driven-development' idea properly and implement much smaller changes to the tests and subsequent development.

To that end the first issue is to catalogue and then implement all the changes that need to be made to the tests to support the 3 additional components. See #93

rich-HJ commented 8 months ago

Great to see you have started on this. Let me know if there is anything I can do to help.

mattjbr123 commented 8 months ago

Will do! I've been quietly working a lot on it the last few weeks, mostly just deep-diving into the code and getting to grips with how it's structured and where the changes are most likely to be made.

ThibHlln commented 8 months ago

Happy to help as well if you have questions/need code reviews. :-)

mattjbr123 commented 8 months ago

Thanks Thibault! At the moment I think feedback on the approach I'm taking and stuff I'm planning to do (when I get to it) would be most useful. I intend to document what I intend to do here first before implementing anything. But extra code reviewers certainly can't hurt too!

mattjbr123 commented 4 months ago

Test updates (finally!) complete. Issue #93 closed. I will now update the framework to add in the nutrient components, to allow the tests to pass.

mattjbr123 commented 3 months ago

unifhy.model

nutrientsubsurface: NutrientSubSurfaceComponent object The Component responsible for the subsurface compartment of any nutrient modelling.

nutrientopenwater: NutrientOpenWaterComponent object The Component responsible for the open water compartment of any nutrient modelling.

to the docstring of the [\_\_init\_\_ ](https://github.com/unifhy-org/unifhy/blob/5aa2e61b7abf4d44ffc50c097c63f53b2a53c128/unifhy/model.py#L27) function
- [x] [\_\_init\_\_](https://github.com/unifhy-org/unifhy/blob/5aa2e61b7abf4d44ffc50c097c63f53b2a53c128/unifhy/model.py#L27) needs to accept the three extra nutrient components as arguments
- [x] [\_\_init\_\_](https://github.com/unifhy-org/unifhy/blob/5aa2e61b7abf4d44ffc50c097c63f53b2a53c128/unifhy/model.py#L27): Needs to be a _process_component_type call for the three nutrient components
- [x] [_check_components_plugging](https://github.com/unifhy-org/unifhy/blob/5aa2e61b7abf4d44ffc50c097c63f53b2a53c128/unifhy/model.py#L125): Add three nutrients components to the loop
- [x] [identifier.setter](https://github.com/unifhy-org/unifhy/blob/5aa2e61b7abf4d44ffc50c097c63f53b2a53c128/unifhy/model.py#L98): Add lines for the nutrient components
- [x] [\_\_str\_\_](https://github.com/unifhy-org/unifhy/blob/5aa2e61b7abf4d44ffc50c097c63f53b2a53c128/unifhy/model.py#L141): Add the nutrient component info as for the other components
- [x] [from_config](https://github.com/unifhy-org/unifhy/blob/5aa2e61b7abf4d44ffc50c097c63f53b2a53c128/unifhy/model.py#L154):  add import_module statements for the nutrient components as for the other components
- [x] And add them to the return statement of the method similarly
- [x] [to_config](https://github.com/unifhy-org/unifhy/blob/5aa2e61b7abf4d44ffc50c097c63f53b2a53c128/unifhy/model.py#L175): Add the nutrient components in the return statement as the other components
- [x] Add

nutrientsurfacelayer: tests.components.nutrientsurfacelayer.dummy nutrientsubsurface: tests.components.nutrientsubsurface.dummy nutrientopenwater: tests.components.nutrientopenwater.dummy


to the docstring of the [from_yaml](https://github.com/unifhy-org/unifhy/blob/5aa2e61b7abf4d44ffc50c097c63f53b2a53c128/unifhy/model.py#L231) method
- [x] [initialise_transfers_from_dump](https://github.com/unifhy-org/unifhy/blob/5aa2e61b7abf4d44ffc50c097c63f53b2a53c128/unifhy/model.py#L275): Add nutrient components to Compass, Clock and Exchanger calls, as they are for the other components
- [x] [spin_up](https://github.com/unifhy-org/unifhy/blob/5aa2e61b7abf4d44ffc50c097c63f53b2a53c128/unifhy/model.py#L351): Add get_spin_up_timedomain calls, add main_??_td lines/assignments and in two places add self.nutrientlayername.timedomain lines/assignments 
- [x] [_initialise](https://github.com/unifhy-org/unifhy/blob/5aa2e61b7abf4d44ffc50c097c63f53b2a53c128/unifhy/model.py#L488): Add self.nutrientlayername.initialse_(...) lines like for the other modules
- [x] [_run](https://github.com/unifhy-org/unifhy/blob/5aa2e61b7abf4d44ffc50c097c63f53b2a53c128/unifhy/model.py#L494): Add the nutrient components to the Compass, Clock and Exchanger sections as for the other components
- [x] Add run_nutrientlayernames to the [loop over clock](https://github.com/unifhy-org/unifhy/blob/5aa2e61b7abf4d44ffc50c097c63f53b2a53c128/unifhy/model.py#L537), then add dump lines in the [if dumping:](https://github.com/unifhy-org/unifhy/blob/5aa2e61b7abf4d44ffc50c097c63f53b2a53c128/unifhy/model.py#L540) section, then add run_nutrientlayername blocks for the three nutrient components, like the other components 
- [x] [_finalise](https://github.com/unifhy-org/unifhy/blob/5aa2e61b7abf4d44ffc50c097c63f53b2a53c128/unifhy/model.py#L575): Add self.nutrientlayername.finalise\_() lines like for the other components
- [x] [resume](https://github.com/unifhy-org/unifhy/blob/5aa2e61b7abf4d44ffc50c097c63f53b2a53c128/unifhy/model.py#L583): Add the nutrient components to the [loop](https://github.com/unifhy-org/unifhy/blob/5aa2e61b7abf4d44ffc50c097c63f53b2a53c128/unifhy/model.py#L627) to initialise component states and records, and to the remaining time domain calculation [loop](https://github.com/unifhy-org/unifhy/blob/5aa2e61b7abf4d44ffc50c097c63f53b2a53c128/unifhy/model.py#L739)

### unifhy.component
- [x] In the Component Class, change the documentation of the [category getter](https://github.com/unifhy-org/unifhy/blob/5aa2e61b7abf4d44ffc50c097c63f53b2a53c128/unifhy/component.py#L966) to include nutrients, i.e. "part of the water*/nutrients* cycle"
- [x] Add NutrientSurfaceLayerComponent, NutrientSubSurfaceComponent and NutrientOpenWaterComponent Classes, following the structure of the pre-existing Components, and moving the nutrients entries in their _input and _output_info dictionaries to their respective nutrients Components (i.e. from SurfaceLayerComponent to NutrientSurfaceLayerComponent, SubSurfaceComponent to NutrientSubSurfaceComponent, OpenWaterComponent to NutrientOpenWaterComponent)

### Elsewhere
- [x] Calls anywhere to unifhy.Model() need to accept nutrient components as an additional three arguments
mattjbr123 commented 3 months ago

Now to check and commit after a break...

mattjbr123 commented 1 month ago

Documentation also needs updating: #99