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

Add CLI to run WSIMOD #38

Merged
merged 28 commits into from
Nov 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
80a16ac
Add initial version of the CLI
dalonsoa Nov 13, 2023
ea2fa79
Add initial arguments for the CLI
dalonsoa Nov 13, 2023
0af6a9f
Add initial arguments for the CLI
dalonsoa Nov 13, 2023
de301b8
Add initial arguments for the CLI
dalonsoa Nov 14, 2023
0a1a1e6
Add validation of input paths
dalonsoa Nov 14, 2023
a40007d
Implements loading date into the settings
dalonsoa Nov 14, 2023
88f5d51
Add missing docstirngs and fix error in process options
dalonsoa Nov 14, 2023
07e78bd
Scan lists in settings and load items if any
dalonsoa Nov 14, 2023
062cc6f
Add run model function
dalonsoa Nov 14, 2023
bc4ebc8
Move run model to __main__.py
dalonsoa Nov 14, 2023
b691908
Modify 'run' function to actually run the model
dalonsoa Nov 14, 2023
d01d77a
Ignore scratch folder
dalonsoa Nov 14, 2023
fcf9790
Improve data reader
dalonsoa Nov 14, 2023
2006ad4
Change order of operations in load data
dalonsoa Nov 15, 2023
e879bc2
Separate data loading step
dalonsoa Nov 15, 2023
cf81b95
Simplify defining inputs and give meaningful error message
dalonsoa Nov 15, 2023
b37b0ea
Add example settings file
dalonsoa Nov 15, 2023
6112bd2
Run examples in YAML as tests
dalonsoa Nov 15, 2023
3c86e14
Fix CI to install wsimod
dalonsoa Nov 15, 2023
6cbcea7
Facilitate wsimod to run with -m
dalonsoa Nov 15, 2023
f60b143
Facilitate wsimod to run with -m
dalonsoa Nov 15, 2023
14eb701
Merge branch 'cli' of https://github.com/ImperialCollegeLondon/wsi in…
dalonsoa Nov 15, 2023
3952090
Fix failing CI
dalonsoa Nov 15, 2023
32e8a8d
Fix failing CI, again
dalonsoa Nov 15, 2023
5a7c886
Use command string rather than list in test
dalonsoa Nov 15, 2023
00deb81
Include review comments
dalonsoa Nov 21, 2023
8c4ee87
Add back what I should not have removed...
dalonsoa Nov 21, 2023
619cbd4
Use pip-tools correctly
dalonsoa Nov 21, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@ jobs:
- uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}

- name: Install and build
run: python -m pip install -r requirements-dev.txt
run: |
python -m pip install -r requirements-dev.txt
python -m pip install .

Choose a reason for hiding this comment

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

You are not currently installing the pinned versions of the main packages here because they are not specified in the requirements-dev.txt. You can see that pandas etc is being installed at the python -m pip install . step in the CI https://github.com/ImperialCollegeLondon/wsi/actions/runs/6875139142/job/18698156546


- name: Run tests
run: python -m pytest
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -152,4 +152,8 @@ cython_debug/
.idea/

# VScode configuration
.vscode
.vscode

# Scratch folder to do testing
scratch
results
87 changes: 87 additions & 0 deletions docs/demo/examples/quickstart_demo.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
inputs: docs/demo/data/processed
outputs: results/quickstart_results

data:
my_land_data:
filename: timeseries_data.csv
filter:
- where: site
is: oxford_land
scaling:
- where: variable
is: precipitation
variable: value
factor: "MM_TO_M"
format: dict
index: ['variable', 'date']
output: 'value'
options: parse_dates=['date']

dates_data:
filename: timeseries_data.csv
options: usecols=['date'],parse_dates=['date']

dates: data:dates_data

nodes:
- type_: Sewer
name: my_sewer
capacity: 0.04

- type_: Land
name: my_land
data_input_dict: data:my_land_data
surfaces:
- type_: ImperviousSurface
surface: urban
area: 10
pollutant_load:
phosphate: 1.0e-07
- type_: PerviousSurface
surface: rural
area: 100
depth: 0.5
pollutant_load:
phosphate: 1.0e-07

- type_: Groundwater
name: my_groundwater
capacity: 100
area: 100

- type_: Node
name: my_river

- type_: Waste
name: my_outlet

arcs:
- type_: Arc
name: urban_drainage
in_port: my_land
out_port: my_sewer

- type_: Arc
name: percolation
in_port: my_land
out_port: my_groundwater

- type_: Arc
name: runoff
in_port: my_land
out_port: my_river

- type_: Arc
name: storm_outflow
in_port: my_sewer
out_port: my_river

- type_: Arc
name: baseflow
in_port: my_groundwater
out_port: my_river

- type_: Arc
name: catchment_outflow
in_port: my_river
out_port: my_outlet
7 changes: 5 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,13 @@ requires-python = ">=3.9"
dependencies = [
"PyYAML",
"tqdm",
"dill"
"dill",
"pandas"
]

[project.scripts]
wsimod = "wsimod.__main__:run"

[project.optional-dependencies]
dev = [
"pytest",
Expand All @@ -33,7 +37,6 @@ dev = [
]

demos = [
"pandas",
"geopandas",
"matplotlib",
"shapely"
Expand Down
12 changes: 12 additions & 0 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,15 @@ mypy-extensions==1.0.0
# via black
nodeenv==1.8.0
# via pre-commit
numpy==1.26.2
# via pandas
packaging==23.2
# via
# black
# build
# pytest
pandas==2.1.3
# via wsimod (pyproject.toml)
pathspec==0.11.2
# via black
pip-tools==7.3.0
Expand All @@ -61,14 +65,22 @@ pytest==7.4.3
# wsimod (pyproject.toml)
pytest-cov==4.1.0
# via wsimod (pyproject.toml)
python-dateutil==2.8.2
# via pandas
pytz==2023.3.post1
# via pandas
pyyaml==6.0.1
# via
# pre-commit
# wsimod (pyproject.toml)
ruff==0.1.4
# via wsimod (pyproject.toml)
six==1.16.0
# via python-dateutil
tqdm==4.66.1
# via wsimod (pyproject.toml)
tzdata==2023.3
# via pandas
virtualenv==20.24.6
# via pre-commit
wheel==0.41.3
Expand Down
14 changes: 13 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,25 @@
# This file is autogenerated by pip-compile with Python 3.11
# by the following command:
#
# pip-compile
# pip-compile --output-file=requirements.txt
#
colorama==0.4.6
# via tqdm
dill==0.3.7
# via wsimod (pyproject.toml)
numpy==1.26.2
# via pandas
pandas==2.1.3
# via wsimod (pyproject.toml)
python-dateutil==2.8.2
# via pandas
pytz==2023.3.post1
# via pandas
pyyaml==6.0.1
# via wsimod (pyproject.toml)
six==1.16.0
# via python-dateutil
tqdm==4.66.1
# via wsimod (pyproject.toml)
tzdata==2023.3
# via pandas
20 changes: 20 additions & 0 deletions tests/test_example_files.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from pytest import mark
from pathlib import Path
import subprocess


def collect_examples() -> list[Path]:
root = Path.cwd() / "docs" / "demo" / "examples"
return list(root.glob("**/*.yaml"))


@mark.parametrize("example", collect_examples())
def test_examples(example: Path, tmp_path: Path) -> None:
result = subprocess.run(
f"wsimod {str(example)} -o {str(tmp_path)}",
shell=True,
check=True,
)
assert (tmp_path / "flows.csv").exists()
assert (tmp_path / "tanks.csv").exists()
assert (tmp_path / "surfaces.csv").exists()
72 changes: 72 additions & 0 deletions wsimod/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
"""The entry point for the myproject program."""
from argparse import ArgumentParser
from pathlib import Path
from typing import Any, cast

import pandas as pd

from wsimod.orchestration.model import Model
from wsimod.validation import assign_data_to_settings, load_data_files, validate_io_args


def create_parser() -> ArgumentParser:
"""Create the CLI argument parser."""
parser = ArgumentParser(prog="WSIMOD")
parser.add_argument(
"settings",
type=Path,
help="Path to the WSIMOD input file, in YAML format.",
)
parser.add_argument(
"--inputs",
"-i",
type=Path,
help="Base directory for all input files. If present, overwrites value in the"
" settings file.",
)
parser.add_argument(
"--outputs",
"-o",
type=Path,
help="Base directory for all output files. If present, overwrites value in the"
" settings file.",
)

return parser


def run_model(settings: dict[str, Any], outputs: Path) -> None:
"""Runs the mode with the chosen settings and saves the outputs as csv.

Args:
settings (dict[str, Any]): Settings dictionary with loaded data.
outputs(Path): Directory where to save the outputs.
"""
model = Model()

model.dates = cast(pd.Series, settings["dates"]).drop_duplicates()
model.add_nodes(settings["nodes"])
model.add_arcs(settings["arcs"])

flows, tanks, _, surfaces = model.run()

pd.DataFrame(flows).to_csv(outputs / "flows.csv")
pd.DataFrame(tanks).to_csv(outputs / "tanks.csv")
pd.DataFrame(surfaces).to_csv(outputs / "surfaces.csv")


def run() -> None:
"""Main entry point of the application."""
args = vars(create_parser().parse_args())
settings = validate_io_args(**args)

inputs = settings.pop("inputs")
outputs = settings.pop("outputs")
loaded_data = load_data_files(settings.pop("data", {}), inputs)
loaded_settings = assign_data_to_settings(settings, loaded_data)

run_model(loaded_settings, outputs)


if __name__ == "__main__":
run()
Loading
Loading