Skip to content

Commit

Permalink
update documentation for 0.2.0
Browse files Browse the repository at this point in the history
  • Loading branch information
gilesknight committed Jun 24, 2024
1 parent 82dd630 commit ca383bf
Show file tree
Hide file tree
Showing 5 changed files with 952 additions and 211 deletions.
8 changes: 4 additions & 4 deletions docs/blog/posts/glmpy-v0_2_0.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
draft: false
date: 2024-06-21
date: 2024-06-24
authors:
- gknight
categories:
Expand All @@ -23,13 +23,13 @@ reading and writing NML files.
existing classes from the `nml` module in `0.1.3`.
- Classes from `0.1.3` are automatically imported using
`from glmpy import nml` to maintain backwards compatibility until `1.0.0`.
- Classnames from `0.1.3` will be deprecated by `1.0.0` in favour of a new
- Class names from `0.1.3` will be deprecated by `1.0.0` in favour of a new
naming convention that ensures forwards compatibility with AED. Warnings are
raised to encourage you to migrate to the new class names.
- The new `nml` sub-module provides low-level tools for reading and writing any
NML file (GLM or AED).
- `NMLWriter` converts a nested Python dictionary to a NML file.
- `NMLReader` converts a NML file to a nested Python dictionary.
- `NMLWriter` converts a nested Python dictionary to an NML file.
- `NMLReader` converts an NML file to a nested Python dictionary.
- Both classes provide functionality to explicitly control how each parameter
is read/written to file.
- `InvertedTruncatedCone` class added to the `dimensions` module to calculate
Expand Down
174 changes: 50 additions & 124 deletions docs/blog/posts/sparkling-lake-tutorial.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
draft: false
date: 2024-03-22
date: 2024-06-24
authors:
- gknight
categories:
Expand All @@ -21,21 +21,21 @@ Sparkling Lake is an oligotrophic, northern temperate lake (89.7 ºN, 46.3 ºW)

<!-- more -->

If you haven't already, install glm-py using `pip`:
First, install glm-py using `pip`:

```
pip install glm-py
```

## Creating a GLM `.nml` file
## Creating a GLM NML file

To begin, start by importing the `nml` module from `glmpy`:
Next, import the `glm_nml` sub-module from the `nml` module of `glmpy`:

```python
from glmpy import nml
from glmpy.nml import glm_nml
```

The `nml` module provides a set of classes to construct GLM's namelist file (`.nml`). A `.nml` file is divided into multiple "blocks" that configure specific aspects of the model, e.g., the `&morphometry` block defines morphometry of the water body. The structure of a `.nml` file is shown below for the four minimum required blocks (`...` indicates that the block contains more parameters than shown):
The `glm_nml` module provides a set of classes to construct GLM's NML file (`.nml`). A NML file is divided into multiple "blocks" that configure specific aspects of the model, e.g., the `morphometry` block defines morphometry of the water body. The structure of a NML file is shown below for the four minimum required blocks (`...` indicates that the block contains more parameters than shown):

```
&glm_setup
Expand All @@ -58,7 +58,7 @@ The `nml` module provides a set of classes to construct GLM's namelist file (`.n

### Model setup

GLM simulates the dynamics of a water body by dividing it into a vertically stacked series of layers. The compulsory `&glm_setup` block defines the structure of these layers, e.g., the maximum number of layers, the minimum layer volume, and the minimum and maximum layer thicknesses. To configure the `&glm_setup` parameters for Sparkling Lake, you would typically write a `.nml` file that contains the following:
GLM simulates the dynamics of a water body by dividing it into a vertically stacked series of layers. The compulsory `glm_setup` block defines the structure of these layers, e.g., the maximum number of layers, the minimum layer volume, and the minimum and maximum layer thicknesses. To configure the `glm_setup` parameters for Sparkling Lake, you would typically write a NML file that contains the following:

```
&glm_setup
Expand All @@ -72,10 +72,10 @@ GLM simulates the dynamics of a water body by dividing it into a vertically stac
/
```

Using glm-py, you instead configure the `&glm_setup` block by using the `NMLGLMSetup` class from the `nml` module. Each model parameter of the `&glm_setup` block has a corresponding attribute in the `NMLGLMSetup` class:
Using glm-py, you instead configure the `glm_setup` block by using the `SetupBlock` class from the `glm_nml` module. Each model parameter of the `glm_setup` block has a corresponding attribute in the `SetupBlock` class:

```python
glm_setup = nml.NMLGLMSetup(
glm_setup = glm_nml.SetupBlock(
sim_name='Sparkling Lake',
max_layers=500,
min_layer_vol=0.5,
Expand All @@ -86,62 +86,29 @@ glm_setup = nml.NMLGLMSetup(
)
```

This approach offers a number of advantages over editing a raw `.nml` file:
This approach offers a number of advantages over editing a raw NML file:

- Explicit type hinting for parameter types
- Native Python syntax
- Error checking

Alternatively, these parameters can also be defined in a dictionary and set as class attributes using the `set_attributes()` method:
Once the attributes are set, you can return a dictionary of the consolidated model parameters by calling the `get_params` method:

```python
glm_setup = nml.NMLGLMSetup()

glm_setup_attrs = {
'sim_name': 'Sparkling Lake',
'max_layers': 500,
'min_layer_vol': 0.5,
'min_layer_thick': 0.15,
'max_layer_thick': 0.5,
'density_model': 1,
'non_avg': True
}

glm_setup.set_attributes(glm_setup_attrs)
```

Once the attributes are set, you can return a dictionary of the consolidated model parameters by calling the instance of the `NMLGLMSetup()` class:

```python
glm_setup_parameters = glm_setup()
print(glm_setup_parameters)
glm_setup_params = glm_setup.get_params()
print(glm_setup_params)
```

```
{'sim_name': 'Sparkling Lake', 'max_layers': 500, 'min_layer_vol': 0.5, 'min_layer_thick': 0.15, 'max_layer_thick': 0.5, 'density_model': 1, 'non_avg': True}
```
The call method provides an optional `check_errors` parameter. If set to `True`, glm-py will validate the model parameters and raise errors if non-compliance is detected. Note, `check_errors` is not fully implemented in glm-py `0.0.1`.

```
FutureWarning: Error checking is not stable and lacks complete coverage. Erroneous parameters may not be raised.
glm_setup(check_errors=True)
{'sim_name': 'Sparkling Lake',
'max_layers': 500,
'min_layer_vol': 0.5,
'min_layer_thick': 0.15,
'max_layer_thick': 0.5,
'density_model': 1,
'non_avg': True}
```


### Mixing and morphometry

Next, let's set the parameters that control the mixing processes within Sparkling Lake. Just as `NMLGLMSetup` defines the `&glm_setup` block, we can configure the `&mixing` block using the `NMLMixing` class:
Next, let's set the parameters that control the mixing processes within Sparkling Lake. Just as `SetupBlock` defines the `glm_setup` block, we can configure the `mixing` block using the `MixingBlock` class:

```python
mixing = nml.NMLMixing(
mixing = glm_nml.MixingBlock(
surface_mixing=1,
coef_mix_conv=0.2,
coef_wind_stir=0.402,
Expand All @@ -154,10 +121,10 @@ mixing = nml.NMLMixing(
)
```

Let's repeat the same for the `&morphometry` block - use the `NMLMorphometry` class:
Let's repeat the same for the `morphometry` block - use the `MorphometryBlock` class:

```python
morphometry = nml.NMLMorphometry(
morphometry = glm_nml.MorphometryBlock(
lake_name='Sparkling',
latitude=46.00881,
longitude=-89.69953,
Expand All @@ -180,117 +147,76 @@ morphometry = nml.NMLMorphometry(

### Setting the remaining blocks

There are up to 14 configurable blocks in the GLM namelist file - setting each will take some time! Let's speed up the process by importing a JSON file that contains the parameters for the remaining blocks. We'll use the `JSONReader` class from the `glm_json` module to extract the relevant parameters from each respective block. Download the JSON file to your working directory using `curl`:
There are up to 14 configurable blocks in the GLM NML file - setting each will take some time! Let's speed up the process by reading the remaining blocks from an existing NML file. Download the NML file to your working directory using `curl`:

```
curl https://raw.githubusercontent.com/WET-tool/glm-py/main/notebooks/glmpy-demo/sparkling-nml.json --output sparkling-nml.json
!curl https://raw.githubusercontent.com/AquaticEcoDynamics/glm-py/main/notebooks/glmpy-demo/glm3.nml --output sparkling_lake.nml
```

Now import the `glm_json` module and initalise the `JSONReader` class by passing in the file path of the JSON file we just downloaded:
To read `sparkling_lake.nml`, we'll use the `NMLReader` class from the `nml.nml` sub-module. After importing the sub-module, initalise the `NMLReader` class and provide the NML file path:

```python
from glmpy import glm_json
from glmpy.nml import nml

my_json_file = glm_json.JSONReader("sparkling-nml.json")
my_nml_file = nml.NMLReader("sparkling_lake.nml")
```
Next, let's extract the parameters for the `&meteorology` block using the `get_nml_parameters()` method:
Next, let's extract the parameters for the `meteorology` block using the `get_block` method:

```python
meteorology_attrs = my_json_file.get_nml_parameters("&meteorology")
meteorology_params = my_nml_file.get_block("meteorology")
```

Take a look at what `meteorology_attrs` contains:
Take a look at what `meteorology_params` contains:

```python
print(meteorology_attrs)
```

```
{'met_sw': True, 'lw_type': 'LW_IN', 'rain_sw': False, 'atm_stab': 0, 'catchrain': False, 'rad_mode': 1, 'albedo_mode': 1, 'cloud_mode': 4, 'fetch_mode': 0, 'subdaily': False, 'meteo_fl': 'bcs/nldas_driver.csv', 'wind_factor': 1, 'sw_factor': 1.08, 'lw_factor': 1, 'at_factor': 1, 'rh_factor': 1, 'rain_factor': 1, 'ce': 0.00132, 'ch': 0.0014, 'cd': 0.0013, 'rain_threshold': 0.01, 'runoff_coef': 0.3}
```

This is a dictionary containing all parameters for the `&meteorology` block. Let's
pass these to the `NMLMeteorology` class with the `set_attributes()` method:

```python
meteorology = nml.NMLMeteorology()
meteorology.set_attributes(meteorology_attrs)
print(meteorology())
```

```
{'met_sw': True, 'meteo_fl': 'bcs/nldas_driver.csv', 'subdaily': False, 'time_fmt': None, 'rad_mode': 1, 'albedo_mode': 1, 'sw_factor': 1.08, 'lw_type': 'LW_IN', 'cloud_mode': 4, 'lw_factor': 1, 'atm_stab': 0, 'rh_factor': 1, 'at_factor': 1, 'ce': 0.00132, 'ch': 0.0014, 'rain_sw': False, 'rain_factor': 1, 'catchrain': False, 'rain_threshold': 0.01, 'runoff_coef': 0.3, 'cd': 0.0013, 'wind_factor': 1, 'fetch_mode': 0, 'Aws': None, 'Xws': None, 'num_dir': None, 'wind_dir': None, 'fetch_scale': None}
```

Easy! But before we go any futher, look closely at the `meteo_fl` parameter - what's `bcs/nldas_driver.csv`? This is a path to a CSV that contains boundary condition data for Sparkling Lake, e.g., daily rainfall, wind speed, and air temperature. You'll need this file to run the model. Let's download it with `curl` and place it in sub-directory called `bcs`:

```
mkdir bcs
curl https://raw.githubusercontent.com/WET-tool/glm-py/main/notebooks/glmpy-demo/bcs/nldas_driver.csv --output bcs/nldas_driver.csv
{'met_sw': True, 'lw_type': 'LW_IN', 'rain_sw': False, 'atm_stab': 0, 'catchrain': False, 'rad_mode': 1, 'albedo_mode': 1, 'cloud_mode': 4, 'fetch_mode': 0, 'subdaily': False, 'meteo_fl': 'bcs/nldas_driver.csv', 'wind_factor': 1.0, 'sw_factor': 1.08, 'lw_factor': 1.0, 'at_factor': 1.0, 'rh_factor': 1.0, 'rain_factor': 1.0, 'ce': 0.00132, 'ch': 0.0014, 'cd': 0.0013, 'rain_threshold': 0.01, 'runoff_coef': 0.3}
```

Now, let's setup the remaining blocks: `&output`, `&init_profiles`, `&time`, `&bird_model`, `&light`, `&sediment`. We'll use `get_nml_parameters` to return dictionaries of parameters that will set the attributes of the corresponding `nml.NML*` classes:
This is a dictionary containing all `meteorology` parameters from `sparkling_lake.nml` in Python data types. Look closely at the `meteo_fl` parameter - what's `bcs/nldas_driver.csv`? This is a path to a CSV that contains boundary condition data for Sparkling Lake, e.g., daily rainfall, wind speed, and air temperature. You'll need this file to run the model. Let's download it with `curl` and place it in sub-directory called `bcs`:

```python
output_attrs=my_json_file.get_nml_parameters("&output")
init_profiles_attrs=my_json_file.get_nml_parameters("&init_profiles")
time_attrs=my_json_file.get_nml_parameters("&time")
light_attrs=my_json_file.get_nml_parameters("&light")
bird_model_attrs=my_json_file.get_nml_parameters("&bird_model")
sediment_attrs=my_json_file.get_nml_parameters("&sediment")
wq_setup_attrs=my_json_file.get_nml_parameters("&wq_setup")
```

Now initialise the respective classes:

```python
output = nml.NMLOutput()
init_profiles = nml.NMLInitProfiles()
time = nml.NMLTime()
light = nml.NMLLight()
bird_model = nml.NMLBirdModel()
sediment = nml.NMLSediment()
wq_setup = nml.NMLWQSetup()
!mkdir bcs
!curl https://raw.githubusercontent.com/AquaticEcoDynamics/glm-py/main/notebooks/glmpy-demo/bcs/nldas_driver.csv --output bcs/nldas_driver.csv
```

And set the attributes:
Now, let's get the parameters for the remaining blocks: `output`, `init_profiles`, `time`, `bird_model`, `light`, `sediment`. We'll use the `get_block` method from our instance of `NMLReader`:

```python
output.set_attributes(output_attrs)
init_profiles.set_attributes(init_profiles_attrs)
time.set_attributes(time_attrs)
light.set_attributes(light_attrs)
bird_model.set_attributes(bird_model_attrs)
sediment.set_attributes(sediment_attrs)
wq_setup.set_attributes(wq_setup_attrs)
output_params=my_nml_file.get_block("output")
init_profiles_params=my_nml_file.get_block("init_profiles")
time_params=my_nml_file.get_block("time")
light_params=my_nml_file.get_block("light")
bird_model_params=my_nml_file.get_block("bird_model")
sediment_params=my_nml_file.get_block("sediment")
```

If you're want to find out more about the attributes for each block, check out glm-py's documentation website.

### Writing the namelist file
### Writing the NML file

We now have the attributes set for each block. Let's combine them to create the `.nml` file. First, create an instance of the `NML` class. Then pass in the dictionaries of consolidated parameters, i.e., from `glm_setup()`, `mixing()`, `morphometry()`, etc:
We now have a dictionary of model parameters for each block. Let's combine them to create the NML file. First, create an instance of the `GLMNML` class from the `glm_nml` sub-module. Then pass in the dictionaries of parameters:

```python
nml = nml.NML(
glm_setup=glm_setup(),
mixing=mixing(),
morphometry=morphometry(),
time=time(),
output=output(),
init_profiles=init_profiles(),
meteorology=meteorology(),
bird_model=bird_model(),
light=light(),
sediment=sediment()
my_nml = glm_nml.GLMNML(
glm_setup=glm_setup.get_params(),
mixing=mixing.get_params(),
morphometry=morphometry.get_params(),
time=time_params,
output=output_params,
init_profiles=init_profiles_params,
meteorology=meteorology_params,
bird_model=bird_model_params,
light=light_params,
sediment=sediment_params
)
```

Finally, use the `write_nml()` method to save the `.nml` to your working directory:

```python
nml.write_nml(nml_file_path='glm3.nml')

my_nml.write_nml(nml_file_path='glm3.nml')
```

## Running the model
Expand Down
50 changes: 26 additions & 24 deletions docs/contributing/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,41 +5,43 @@

glm-py is an open source project that is actively maintained by the [Aquatic
EcoDynamics research group][aed-group] at the University of Western Australia.
Whether you're an experienced GLM user, or are new to modelling hydrodynamics,
we would appreciate your feedback and input on using this package.

## Suggested contributions
If you'd like to contribute to the project, please familiarise yourself with
the contributing guide below.

## Environment

A Docker container can be used to create a development environment. You can
either build the Docker image:

<div class="grid cards" markdown>
```
docker build -t glm-py-dev .devcontainer
```
Or, you can develop glm-py using a dev-container.

## Code style

- :material-file-document-remove-outline: &nbsp;
__Documenting model parameters__
Code linting and formatting uses ruff and black. A script to format the glm-py
repository can be run: `./scripts/format.sh`.

---
pre-commit is used to run ruff and black.

Many GLM parameters are lacking adequate documentation in their respective
`nml.NML*` classes. Please open an issue if you can improve on the
documentation or provide parameter units (where applicable).
## Tests

---
<a href="https://docs.pytest.org/en/7.4.x/" target="_blank">pytest</a> is used
for testing glm-py.

[:octicons-arrow-right-24: Open a docs issue][open-issue]
If testing, please add tests under the `tests` directory. If you need test data
for running tests, add them as `pytest.fixtures` in `conftest.py`.

- :material-bug-outline: &nbsp;
__Found a bug?__
## Pull requests

---
Submit pull requests to the `next-release` branch. This is where glm-py is
actively developed.

While glm-py is in its infancy, you may encounter a bug in unexpected use
cases. Please open an issue and provide a reproducible example if you've
identified a bug.

---
## Suggested contributions

[:octicons-arrow-right-24: Report a bug][open-issue]
- An `aed_nml` sub-module for the `nml` module that mirrors the functionality
of the `glm_nml` sub-module.
- Parameter documentation for the `glm_nml` sub-module.
- Additional simple morphometries the `dimensions` module.

</div>

2 changes: 2 additions & 0 deletions docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ To run GLM, you will either need to source a pre-compiled binary or [compile GLM
The following table lists the GLM version that is bundled with the built distribution of each glm-py release:

[glm_py_0_1_3]: https://github.com/AquaticEcoDynamics/glm-py/releases/tag/v0.1.3
[glm_py_0_2_0]: https://github.com/AquaticEcoDynamics/glm-py/releases/tag/v0.2.0

| glm-py version | GLM version |
| -------------- | ----------- |
| [`0.2.0`][glm_py_0_2_0] | `3.3.1a12 ` |
| [`0.1.3`][glm_py_0_1_3] | `3.3.1a12 ` |
Loading

0 comments on commit ca383bf

Please sign in to comment.