Skip to content

Commit bbef385

Browse files
authored
Merge pull request #588 from EnergySystemsModellingLab/document-input-format
Document input format
2 parents 30bb59d + 58cec12 commit bbef385

24 files changed

Lines changed: 700 additions & 12 deletions

.github/actions/generate-docs/action.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ runs:
1111
- name: Update CLI documentation
1212
shell: bash
1313
run: cargo run -- --markdown-help > docs/command_line_help.md
14+
- name: Install Python dependencies
15+
shell: bash
16+
run: pip install -r doc-requirements.txt
17+
- name: Generate input format documentation
18+
shell: bash
19+
run: python docs/generate_input_format_doc.py
1420
- name: Build API docs
1521
shell: bash
1622
run: |

.gitignore

Lines changed: 161 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,165 @@ target/
1919
book/
2020
index.html
2121

22-
# Ignore virtual environment directories
22+
# Byte-compiled / optimized / DLL files
23+
__pycache__/
24+
*.py[cod]
25+
*$py.class
26+
27+
# C extensions
28+
*.so
29+
30+
# Distribution / packaging
31+
.Python
32+
build/
33+
develop-eggs/
34+
dist/
35+
downloads/
36+
eggs/
37+
.eggs/
38+
lib/
39+
lib64/
40+
parts/
41+
sdist/
42+
var/
43+
wheels/
44+
share/python-wheels/
45+
*.egg-info/
46+
.installed.cfg
47+
*.egg
48+
MANIFEST
49+
50+
# PyInstaller
51+
# Usually these files are written by a python script from a template
52+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
53+
*.manifest
54+
*.spec
55+
56+
# Installer logs
57+
pip-log.txt
58+
pip-delete-this-directory.txt
59+
60+
# Unit test / coverage reports
61+
htmlcov/
62+
.tox/
63+
.nox/
64+
.coverage
65+
.coverage.*
66+
.cache
67+
nosetests.xml
68+
coverage.xml
69+
*.cover
70+
*.py,cover
71+
.hypothesis/
72+
.pytest_cache/
73+
cover/
74+
75+
# Translations
76+
*.mo
77+
*.pot
78+
79+
# Django stuff:
80+
*.log
81+
local_settings.py
82+
db.sqlite3
83+
db.sqlite3-journal
84+
85+
# Flask stuff:
86+
instance/
87+
.webassets-cache
88+
89+
# Scrapy stuff:
90+
.scrapy
91+
92+
# Sphinx documentation
93+
docs/_build/
94+
95+
# PyBuilder
96+
.pybuilder/
97+
target/
98+
99+
# Jupyter Notebook
100+
.ipynb_checkpoints
101+
102+
# IPython
103+
profile_default/
104+
ipython_config.py
105+
106+
# pyenv
107+
# For a library or package, you might want to ignore these files since the code is
108+
# intended to run in multiple environments; otherwise, check them in:
109+
# .python-version
110+
111+
# pipenv
112+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
113+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
114+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
115+
# install all needed dependencies.
116+
#Pipfile.lock
117+
118+
# poetry
119+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
120+
# This is especially recommended for binary packages to ensure reproducibility, and is more
121+
# commonly ignored for libraries.
122+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
123+
#poetry.lock
124+
125+
# pdm
126+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
127+
#pdm.lock
128+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
129+
# in version control.
130+
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
131+
.pdm.toml
132+
.pdm-python
133+
.pdm-build/
134+
135+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
136+
__pypackages__/
137+
138+
# Celery stuff
139+
celerybeat-schedule
140+
celerybeat.pid
141+
142+
# SageMath parsed files
143+
*.sage.py
144+
145+
# Environments
146+
.env
147+
.venv
148+
env/
23149
venv/
24-
.venv/
25-
.env/
150+
ENV/
151+
env.bak/
152+
venv.bak/
153+
154+
# Spyder project settings
155+
.spyderproject
156+
.spyproject
157+
158+
# Rope project settings
159+
.ropeproject
160+
161+
# mkdocs documentation
162+
/site
163+
164+
# mypy
165+
.mypy_cache/
166+
.dmypy.json
167+
dmypy.json
168+
169+
# Pyre type checker
170+
.pyre/
171+
172+
# pytype static type analyzer
173+
.pytype/
174+
175+
# Cython debug symbols
176+
cython_debug/
177+
178+
# PyCharm
179+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
180+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
181+
# and can be added to the global gitignore or merged into this file. For a more nuclear
182+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
183+
#.idea/

doc-requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
table2md

docs/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Generated documentation files
2+
command_line_help.md
3+
input_format.md

docs/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
[Introduction](./introduction.md)
44

55
- [User Guide](./user_guide.md)
6+
- [Input Format](./input_format.md)
67
- [Command Line Help](./command_line_help.md)
78
- [Model Description](./model_description.md)
89
- [Dispatch Optimisation](./dispatch_optimisation.md)

docs/command_line_help.md

Lines changed: 0 additions & 9 deletions
This file was deleted.

docs/generate_input_format_doc.py

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
#!/usr/bin/env python3
2+
#
3+
# A script to generate markdown documentation from table schemas.
4+
5+
from table2md import MarkdownTable
6+
import yaml
7+
from pathlib import Path
8+
9+
_DOCS_DIR = Path(__file__).parent
10+
_SCHEMA_DIR = _DOCS_DIR.parent / "schemas" / "input"
11+
_FILE_ORDER = {
12+
"Time slices": ["time_slices"],
13+
"Regions": ["regions"],
14+
"Agents": ["agents", "agent_*"],
15+
"Assets": ["assets"],
16+
"Commodities": ["commodities", "commodity_costs", "demand", "demand_slicing"],
17+
"Processes": ["processes", "process_*"],
18+
}
19+
20+
21+
def generate_markdown() -> str:
22+
out = (
23+
"# Input file format\n"
24+
f"<!-- Automatically generated by {Path(__file__).name}. Do not edit manually. -->\n"
25+
"<!-- markdownlint-disable MD013 -->\n"
26+
"<!-- markdownlint-disable MD033 -->\n"
27+
)
28+
29+
for title, patterns in _FILE_ORDER.items():
30+
out += f"\n## {title}\n"
31+
32+
for pattern in patterns:
33+
paths = map(str, _SCHEMA_DIR.glob(f"{pattern}.yaml"))
34+
for path in map(Path, sorted(paths)):
35+
out += process_file(path)
36+
37+
return out
38+
39+
40+
def process_file(path: Path) -> str:
41+
out = f"\n### `{path.stem}.csv`\n\n"
42+
with path.open() as f:
43+
data = yaml.safe_load(f)
44+
45+
out += f"{add_full_stop(data['title'])}\n\n"
46+
47+
try:
48+
table_str, notes_str = fields2table(data["fields"])
49+
out += table_str
50+
except KeyError:
51+
print(f"MISSING VALUE IN {path}")
52+
raise
53+
54+
desc = data.get("description", "")
55+
if not desc and not notes_str:
56+
return out
57+
58+
out += "\n#### Notes\n\n"
59+
60+
if desc:
61+
out += f"{add_full_stop(desc)}\n\n"
62+
63+
if notes_str:
64+
out += notes_str
65+
66+
return out
67+
68+
69+
def add_full_stop(s: str) -> str:
70+
s = s.rstrip()
71+
if s == "" or s.endswith("."):
72+
return s
73+
else:
74+
return f"{s}."
75+
76+
77+
def fields2table(fields: list[dict[str, str]]) -> tuple[str, str]:
78+
data = []
79+
notes = []
80+
for f in fields:
81+
row = {"Field": f"`{f['name']}`", "Description": f["title"]}
82+
data.append(row)
83+
84+
if desc := f.get("description", ""):
85+
# MarkdownTable can't handle newlines, so replace with HTML equivalent
86+
desc = desc.replace("\n\n", "<br /><br />").replace("\n", " ")
87+
row = {"Field": f"`{f['name']}`", "Notes": desc}
88+
notes.append(row)
89+
90+
data = [
91+
{
92+
"Field": f"`{f['name']}`",
93+
"Description": f["title"],
94+
}
95+
for f in fields
96+
]
97+
98+
table_str = str(MarkdownTable.from_dicts(data))
99+
notes_str = str(MarkdownTable.from_dicts(notes)) if notes else ""
100+
return table_str, notes_str
101+
102+
103+
if __name__ == "__main__":
104+
output_path = _DOCS_DIR / "input_format.md"
105+
output_path.write_text(generate_markdown(), encoding="utf-8")

docs/user_guide.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# User Guide
22

3+
## Running MUSE 2.0
4+
5+
Once you have installed MUSE 2.0, you should be able to run it via the `muse2` command-line program.
6+
For details of the command-line interface, [see here](./command_line_help.md).
7+
38
## Setting the log level
49

510
MUSE uses the [`fern`] crate for logging. The default log level is `info`, though this can be
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
title: Commodity demand portions for agents
2+
description: |
3+
Portions of commodity demand for which agents are responsible.
4+
5+
If an entry is specified for one agent and commodity, there must be entries covering all milestone
6+
years. For each agent listed in this file, the total portions for each region/commodity/year
7+
combination must sum to one. In addition, there must be entries for every SVD and SED commodity
8+
for all regions and milestone years.
9+
10+
fields:
11+
- name: agent_id
12+
type: string
13+
title: The agent to apply these values to
14+
- name: commodity_id
15+
type: string
16+
title: The commodity for which the agent is responsible
17+
- name: years
18+
type: string
19+
title: The year(s) to which this entry applies
20+
description: One or more milestone years separated by semicolons or `all`
21+
- name: commodity_portion
22+
type: number
23+
title: Portion of commodity demand
24+
description: Value must be >0 and <=1. The portion applies only to the specified years.
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
title: Agent cost limits
2+
description: |
3+
Limits on expenditure for agents.
4+
5+
If cost limits are provided for an agent, they must be present for all years.
6+
7+
fields:
8+
- name: agent_id
9+
type: string
10+
title: The agent to apply these values to
11+
- name: years
12+
type: string
13+
title: The year(s) to which this entry applies
14+
description: One or more milestone years separated by semicolons or `all`
15+
- name: capex_limit
16+
type: number
17+
title: Maximum capital cost the agent will pay
18+
description: Must be >0. Optional (defaults to infinity).
19+
- name: annual_cost_limit
20+
type: number
21+
title: Maximum annual operating cost
22+
description: |
23+
The maximum annual operating cost (fuel plus variable operating cost etc.) that the agent will
24+
pay. Must be >0. Optional (defaults to infinity).

0 commit comments

Comments
 (0)