Skip to content

Commit 813a9c3

Browse files
authored
docs: update models/programs api sections, add dev docs to rtd site (#278)
1 parent 5aa9248 commit 813a9c3

5 files changed

Lines changed: 542 additions & 74 deletions

File tree

docs/index.rst

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,14 @@ The `modflow-devtools` package provides a set of tools for developing and testin
2828
:maxdepth: 2
2929
:caption: Miscellaneous
3030

31-
md/dfn.md
31+
md/dfns.md
3232
md/download.md
3333
md/latex.md
3434
md/models.md
3535
md/ostags.md
36-
md/zip.md
36+
md/programs.md
3737
md/timed.md
38+
md/zip.md
3839

3940

4041
.. toctree::
@@ -43,3 +44,11 @@ The `modflow-devtools` package provides a set of tools for developing and testin
4344

4445
md/act.md
4546
md/doctoc.md
47+
48+
.. toctree::
49+
:maxdepth: 2
50+
:caption: Developer docs
51+
52+
md/dev/dfns.md
53+
md/dev/models.md
54+
md/dev/programs.md
File renamed without changes.

docs/md/models.md

Lines changed: 233 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,118 +1,287 @@
11
# Models API
22

3-
The `modflow_devtools.models` module provides programmatic access to MODFLOW 6 (and other) models.
3+
The `modflow_devtools.models` module provides programmatic access to MODFLOW 6 (and other) model input files from official test and example repositories. It can also be used with local model repositories.
44

5-
**Note**: While this module leans heavily on [Pooch](https://www.fatiando.org/pooch/latest/index.html), it is an independent layer with opinions about how to train (configure) it.
5+
This module builds on [Pooch](https://www.fatiando.org/pooch/latest/index.html) for file fetching and caching. While it leverages Pooch's capabilities, it provides an independent layer with:
66

7-
## `ModelRegistry`
7+
- Registration, discovery and synchronization
8+
- Support for multiple sources and refs
9+
- Hierarchical model addressing
810

9-
The `ModelRegistry` base class represents a set of models living in a GitHub repository or on the local filesystem. This package provides an "official" GitHub-backed registry. Local registries may be created as needed.
11+
Model registries can be synchronized from remote sources on demand. The user or developer can inspect and load models published by the MODFLOW organization, from a personal fork, or from the local filesystem.
1012

11-
All `ModelRegistry` subclasses expose the following properties:
13+
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
14+
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
1215

13-
- `files`: a map of model input files to file-scoped info
14-
- `models`: a map of model names to model input files
15-
- `examples`: a map of example scenarios to models
1616

17-
An *example* is a set of related models which run in a particular order.
17+
- [Overview](#overview)
18+
- [Usage](#usage)
19+
- [Syncing registries](#syncing-registries)
20+
- [Inspecting available models](#inspecting-available-models)
21+
- [Copying models to a workspace](#copying-models-to-a-workspace)
22+
- [Using the default registry](#using-the-default-registry)
23+
- [Customizing model sources](#customizing-model-sources)
24+
- [Working with specific sources](#working-with-specific-sources)
25+
- [Model Names](#model-names)
26+
- [Local Registries](#local-registries)
27+
- [Cache Management](#cache-management)
28+
- [Automatic Synchronization](#automatic-synchronization)
29+
- [Repository Integration](#repository-integration)
1830

19-
Dictionary keys are consistently strings. Dictionary values may vary depending on the type of registry. For instance, values in `PoochRegistry.files` are dictionaries including a hash and url.
31+
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
2032

21-
### Subclasses
33+
## Overview
2234

23-
A registry backed by a remote repository is called a `PoochRegistry`. A `PoochRegistry` maintains a persistent index on disk.
35+
The Models API provides:
2436

25-
A `LocalRegistry` lives in-memory only. Its purpose is simply to store some knowledge about where model files are on disk, and provide an identical API for accessing them as the official `PoochRegistry`.
37+
- **Model registration**: Index local or remote model repositories
38+
- **Model discovery**: Browse available models from multiple repositories
39+
- **Model retrieval**: Copy model input files to local workspaces
2640

27-
Most users will interact only with the default `PoochRegistry`, available at `modflow_devtools.models.DEFAULT_REGISTRY`. A `LocalRegistry` can be useful for developing and debugging MODFLOW and/or MODFLOW models alongside one another.
41+
Model metadata is provided by **registries** which are published by model repositories. On first use, `modflow-devtools` automatically attempts to sync these registries.
2842

29-
### Listing models
43+
A model registry contains three main components:
3044

31-
This module provides convenience functions to access the default registry.
45+
- **`files`**: Map of model input files to metadata (hash, path/URL)
46+
- **`models`**: Map of model names to lists of their input files
47+
- **`examples`**: Map of example scenarios to lists of models
3248

33-
For instance, the `get_models()` function aliases `DEFAULT_REGISTRY.models`.
49+
An **example** is an ordered set of models which together form a complete example scenario.
50+
51+
The MODFLOW organization publishes a set of models for demonstration and testing, some of which are grouped into example scenarios, from the following repositories:
52+
53+
- `MODFLOW-ORG/modflow6-examples`
54+
- `MODFLOW-ORG/modflow6-testmodels`
55+
- `MODFLOW-ORG/modflow6-largetestmodels`
56+
57+
## Usage
58+
59+
### Syncing registries
60+
61+
Registries can be manually synchronized:
3462

3563
```python
36-
from pprint import pprint
37-
from modflow_devtools.models import DEFAULT_REGISTRY, get_models
64+
from modflow_devtools.models import ModelSourceConfig
3865

39-
pprint(list(get_models())[:5])
40-
```
66+
config = ModelSourceConfig.load()
4167

68+
# Sync all configured sources
69+
results = config.sync(verbose=True)
70+
71+
# Sync specific source
72+
results = config.sync(source="modflow6-testmodels", verbose=True)
73+
74+
# Check sync status
75+
status = config.status
76+
for source_name, source_status in status.items():
77+
print(f"{source_name}: {source_status.cached_refs}")
4278
```
43-
['mf6/example/ex-gwe-ates',
44-
'mf6/example/ex-gwe-barends/mf6gwe',
45-
'mf6/example/ex-gwe-barends/mf6gwf',
46-
'mf6/example/ex-gwe-danckwerts',
47-
'mf6/example/ex-gwe-geotherm/mf6gwe']
48-
```
4979

50-
#### Model names
80+
Or via CLI:
81+
82+
```bash
83+
python -m modflow_devtools.models sync
84+
python -m modflow_devtools.models sync --source modflow6-testmodels
85+
python -m modflow_devtools.models sync --source modflow6-testmodels --ref develop
86+
python -m modflow_devtools.models sync --force
87+
```
5188

52-
Model names follow a hierarchical addressing scheme.
89+
### Inspecting available models
5390

54-
The leading part identifies the kind of model, e.g. `mf6`, `mf2005`, etc. Subsequent parts may be used to classify the model.
91+
```python
92+
from modflow_devtools.models import get_models, get_examples
5593

56-
Currently the following prefixes are in use:
94+
models = get_models()
95+
print(f"Available models: {len(models)}")
96+
for name in list(models.keys())[:5]:
97+
print(f" {name}")
5798

58-
- `mf6/example/...`: mf6 example models in https://github.com/MODFLOW-ORG/modflow6-examples
59-
- `mf6/test/...`: mf6 test models in https://github.com/MODFLOW-ORG/modflow6-testmodels
60-
- `mf6/large/...`: large mf6 test models in https://github.com/MODFLOW-ORG/modflow6-largetestmodels
61-
- `mf2005/...`: mf2005 models in https://github.com/MODFLOW-ORG/modflow6-testmodels
99+
examples = get_examples()
100+
for example_name, model_list in list(examples.items())[:3]:
101+
print(f"{example_name}: {len(model_list)} models")
102+
```
62103

63-
The remaining parts may reflect the relative location of the model within the source repository.
104+
Or by CLI:
64105

65-
**Note**: Until this module stabilizes, model naming conventions may change without notice.
106+
```bash
66107

67-
### Using models
108+
python -m modflow_devtools.models info # Show sync status
109+
python -m modflow_devtools.models list # Show model summary...
110+
python -m modflow_devtools.models list --verbose # ..or full list
111+
# Filter by source
112+
python -m modflow_devtools.models list --source mf6/test --verbose
113+
```
68114

69-
To copy model input files to a workspace of your choosing, call `copy_to` on the registry.
115+
### Copying models to a workspace
70116

71117
```python
72118
from tempfile import TemporaryDirectory
73119
from modflow_devtools.models import copy_to
74120

75-
with TemporaryDirectory() as td:
76-
workspace = DEFAULT_REGISTRY.copy_to(td, "example/ex-gwe-ates", verbose=True)
77-
# or, the module provides a shortcut for this too
78-
workspace = copy_to(td, "example/ex-gwe-ates", verbose=True)
121+
with TemporaryDirectory() as workspace:
122+
model_path = copy_to(workspace, "mf6/example/ex-gwf-twri01", verbose=True)
123+
```
124+
125+
### Using the default registry
126+
127+
The module provides explicit access to the default registry used by `get_models()` etc.
128+
129+
```python
130+
from modflow_devtools.models import DEFAULT_REGISTRY
131+
132+
models = DEFAULT_REGISTRY.models
133+
files = DEFAULT_REGISTRY.files
134+
examples = DEFAULT_REGISTRY.examples
135+
136+
workspace = DEFAULT_REGISTRY.copy_to("./workspace", "mf6/example/ex-gwf-twri01")
137+
```
138+
139+
### Customizing model sources
140+
141+
Create a user config file to add custom sources or override defaults:
142+
143+
- **Windows**: `%APPDATA%/modflow-devtools/models.toml`
144+
- **macOS**: `~/Library/Application Support/modflow-devtools/models.toml`
145+
- **Linux**: `~/.config/modflow-devtools/models.toml`
146+
147+
Example user config:
148+
149+
```toml
150+
[sources.modflow6-testmodels]
151+
repo = "myusername/modflow6-testmodels" # Use a fork for testing
152+
name = "mf6/test"
153+
refs = ["feature-branch"]
154+
```
155+
156+
The user config is automatically merged with the bundled config, allowing you to test against forks or add private repositories.
157+
158+
### Working with specific sources
159+
160+
Access individual model sources:
161+
162+
```python
163+
from modflow_devtools.models import ModelSourceConfig, _DEFAULT_CACHE
164+
165+
# Load configuration
166+
config = ModelSourceConfig.load()
167+
168+
# Work with specific source
169+
source = config.sources["modflow6-testmodels"]
170+
171+
# Check if synced
172+
if source.is_synced("develop"):
173+
print("Already cached!")
174+
175+
# List synced refs
176+
synced_refs = source.list_synced_refs()
177+
178+
# Sync specific ref
179+
result = source.sync(ref="develop", verbose=True)
180+
181+
# Load cached registry
182+
registry = _DEFAULT_CACHE.load("mf6/test", "develop")
183+
if registry:
184+
print(f"Models: {len(registry.models)}")
185+
print(f"Files: {len(registry.files)}")
79186
```
80187

81-
If the target directory doesn't exist, it will be created.
188+
## Model Names
82189

83-
## Creating a registry
190+
Model names follow a hierarchical addressing scheme: `{source}@{ref}/{path/to/model}`.
84191

85-
### Local registries
192+
The `path/to/` part is referred to as the **prefix**. Valid prefixes include:
86193

87-
To prepare a local registry, just create it and call `index` once or more. The `path` to index must be a directory containing model subdirectories at arbitrary depth. Model subdirectories are identified by the presence of a namefile matching `namefile_pattern`. By default `namefile_pattern="mfsim.nam"`, causing only MODFLOW 6 models to be returned.
194+
- **`mf6/example/...`**: MODFLOW 6 example models from [modflow6-examples](https://github.com/MODFLOW-ORG/modflow6-examples)
195+
- **`mf6/test/...`**: MODFLOW 6 test models from [modflow6-testmodels](https://github.com/MODFLOW-ORG/modflow6-testmodels)
196+
- **`mf6/large/...`**: Large MODFLOW 6 test models from [modflow6-largetestmodels](https://github.com/MODFLOW-ORG/modflow6-largetestmodels)
197+
- **`mf2005/...`**: MODFLOW-2005 models from [modflow6-testmodels](https://github.com/MODFLOW-ORG/modflow6-testmodels)
88198

89-
For instance, to load all MODFLOW models (pre-MF6 as well):
199+
Example model names:
200+
```
201+
mf6/example/ex-gwf-twri01
202+
mf6/test/test001a_Tharmonic
203+
mf6/large/prudic2004t2
204+
```
205+
206+
## Local Registries
207+
208+
For development or testing with local models, create a local registry:
90209

91210
```python
211+
from modflow_devtools.models import LocalRegistry
212+
213+
# Create and index a local registry
92214
registry = LocalRegistry()
93-
registry.index("path/to/models", namefile_pattern="*.nam")
215+
registry.index("path/to/models")
216+
217+
# Index with custom namefile pattern (e.g., for MODFLOW-2005)
218+
registry.index("path/to/mf2005/models", namefile_pattern="*.nam")
219+
220+
# Use the local registry
221+
models = registry.models
222+
workspace = registry.copy_to("./workspace", "my-model-name")
94223
```
95224

96-
### Pooch registry
225+
Model subdirectories are identified by the presence of a namefile. By default, only MODFLOW 6 models are indexed (`mfsim.nam`). Use `namefile_pattern` to include other model types.
97226

98-
The `make_registry.py` script is responsible for generating a registry text file and a mapping between files and models.
227+
## Cache Management
99228

100-
The generated registry file and model mapping are used to create a pooch instance for fetching model files, and should be distributed with the package.
229+
Model registries and files are cached locally for fast access:
101230

102-
The script can be executed with `python -m modflow_devtools.models.make_registry`. It accepts a single positional argument, specifying the base directory containing model directories. It accepts two named arguments:
231+
- **Registries**: `~/.cache/modflow-devtools/models/registries/{source}/{ref}/`
232+
- **Model files**: `~/.cache/modflow-devtools/models/` (managed by Pooch)
103233

104-
- `--append` or `-a`: If specified, the script will append to the existing registry file instead of overwriting it.
105-
- `--url` or `-u`: Specifies the base URL for the registry file. If not provided, the default base URL is used.
106-
- `--model-name-prefix`: Optionally specify a string to prepend to model names. Useful for avoiding collisions.
107-
- `--namefile`: Optionally specify the glob pattern for namefiles. By default, only `mfsim.nam` (MF6) are found.
234+
The cache enables:
235+
- Fast model access without re-downloading
236+
- Offline access to previously used models
237+
- Efficient switching between repository refs
108238

109-
For example, to create the "default" registry of models in the MF6 examples and test models repositories, assuming each is checked out next to this project:
239+
Check cache status:
110240

111-
```shell
112-
python -m modflow_devtools.models.make_registry -p ../modflow6-examples/examples --url https://github.com/MODFLOW-ORG/modflow6-examples/releases/download/current/mf6examples.zip --model-name-prefix mf6/example
113-
python -m modflow_devtools.models.make_registry -p ../modflow6-testmodels/mf6 --url https://github.com/MODFLOW-ORG/modflow6-testmodels/raw/master/mf6 --model-name-prefix mf6/test
114-
python -m modflow_devtools.models.make_registry -p ../modflow6-largetestmodels --url https://github.com/MODFLOW-ORG/modflow6-largetestmodels/raw/master --model-name-prefix mf6/large
115-
python -m modflow_devtools.models.make_registry -p ../modflow6-testmodels/mf5to6 --url https://github.com/MODFLOW-ORG/modflow6-testmodels/raw/master/mf5to6 --model-name-prefix mf2005 --namefile "*.nam"
241+
```python
242+
from modflow_devtools.models import _DEFAULT_CACHE
243+
244+
# List all cached registries
245+
cached = _DEFAULT_CACHE.list() # Returns: [(source, ref), ...]
246+
for source, ref in cached:
247+
print(f"{source}@{ref}")
248+
249+
# Check specific cache
250+
is_cached = _DEFAULT_CACHE.has("mf6/test", "develop")
251+
252+
# Clear cache (if needed)
253+
_DEFAULT_CACHE.clear()
254+
```
255+
256+
## Automatic Synchronization
257+
258+
By default, `modflow-devtools` attempts to sync registries:
259+
- On first import (best-effort, fails silently on network errors)
260+
- When accessing models (unless `MODFLOW_DEVTOOLS_NO_AUTO_SYNC=1`)
261+
262+
To disable auto-sync:
263+
264+
```bash
265+
export MODFLOW_DEVTOOLS_NO_AUTO_SYNC=1
116266
```
117267

118-
As a shortcut to create the default registry, the script can be run with no arguments: `python -m modflow_devtools.models.make_registry`.
268+
Then manually sync when needed:
269+
270+
```bash
271+
python -m modflow_devtools.models sync
272+
```
273+
274+
## Repository Integration
275+
276+
For model repository maintainers who want to publish their models:
277+
278+
Model repositories should publish a `models.toml` registry file either:
279+
1. As a release asset (for repositories that build models in CI)
280+
2. Under version control in a `.registry/` directory
281+
282+
Registry files contain:
283+
- **`files`**: Map of filenames to hashes
284+
- **`models`**: Map of model names to file lists
285+
- **`examples`**: Map of example names to model lists
286+
287+
The `make_registry.py` tool (part of `modflow-devtools`) can generate these registry files. See the [developer documentation](dev/models.md) for details on registry creation.

0 commit comments

Comments
 (0)