Development & Contributing¶
Overview¶
This page provides guidance for developers contributing to the food-opt project, including code conventions and best practices.
For AI coding agents, see AGENTS.md
in the repository root for specific instructions.
Development Setup¶
Prerequisites¶
Python >= 3.12
Git
uv (dependency manager)
Installation¶
Clone the repository:
git clone <repository-url> cd food-opt
Install dependencies:
uv sync
Install development tools:
uv sync --extra dev
Set up pre-commit hooks:
uv run pre-commit install
Code Conventions¶
Style Guidelines¶
The project uses ruff for linting and formatting, enforcing:
PEP 8 style (with 88-character line length)
Import sorting (isort)
Type hints (where practical)
Docstrings for public functions
Run linter:
uv run ruff check .
Auto-format code:
uv run ruff format .
Run from pre-commit (automatic on git commit
):
uv run pre-commit run --all-files
Specific Conventions¶
Fail early: Validate external inputs; trust internal invariants
Concise logic: Prefer simple control flow; avoid over-engineering
Docstrings: Use NumPy style for functions with non-obvious behavior
Licensing¶
Code: GPL-3.0-or-later (use SPDX header in
.py
files)Documentation: CC-BY-4.0 (use SPDX header in
.rst
,.md
files)
SPDX headers (required in all source files):
# SPDX-FileCopyrightText: 2025 <Author>
#
# SPDX-License-Identifier: GPL-3.0-or-later
Repository Structure¶
food-opt/
├── config/ # Scenario configuration files
├── data/ # Input data (not committed)
├── docs/ # Documentation (Sphinx)
├── processing/ # Intermediate outputs (not committed)
├── results/ # Model results (not committed)
├── workflow/ # Snakemake workflow
│ ├── Snakefile # Main workflow definition
│ ├── rules/ # Modular rule files
│ └── scripts/ # Python scripts for processing/modeling
├── tools/ # Utility wrappers (e.g., smk)
├── notebooks/ # Exploratory Jupyter notebooks
├── vendor/ # Bundled third-party code (customized PyPSA/linopy)
├── .gitignore
├── pyproject.toml # Dependencies and tool config
├── README.md
└── AGENTS.md # AI agent guidance
Adding New Features¶
Adding a New Visualization¶
Create script
workflow/scripts/plotting/plot_my_metric.py
:import pypsa import matplotlib.pyplot as plt n = pypsa.Network(snakemake.input.network) # Extract and process data metric_data = extract_my_metric(n) # Plot fig, ax = plt.subplots() metric_data.plot(kind="bar", ax=ax) ax.set_ylabel("My Metric") ax.set_title("My Analysis") plt.savefig(snakemake.output.plot, bbox_inches="tight")
Add rule in
workflow/rules/plotting.smk
:rule plot_my_metric: input: network="results/{name}/solved/model.nc" output: plot="results/{name}/plots/my_metric.pdf" script: "../scripts/plotting/plot_my_metric.py"
Add to
all
rule (optional):rule all: input: # ... f"results/{name}/plots/my_metric.pdf"
Run:
tools/smk --configfile config/my_scenario.yaml results/my_scenario/plots/my_metric.pdf
Version Control¶
Git Workflow¶
Branch for features:
git checkout -b feature/my-new-feature
Commit frequently with descriptive messages:
git commit -m "feat: Add minimum legume production constraint"
Push to remote:
git push origin feature/my-new-feature
Create pull request for review
Commit Messages¶
Follow conventional commit style:
feat: Add new crop to GAEZ mapping
fix: Correct water requirement unit conversion
docs: Update health module documentation
refactor: Simplify resource class computation
test: Add validation for quickstart config
What to Commit¶
DO commit:
Code (
.py
,.smk
)Configuration (
.yaml
)Documentation (
.rst
,.md
)Static data files (
data/*.csv
if < 1 MB)
DO NOT commit:
Downloaded datasets (
data/downloads/
)Processed intermediate files (
processing/
)Results (
results/
)Large binary files (> 1 MB)
These are excluded via .gitignore
.
Documentation¶
Building Documentation Locally¶
tools/smk -j4 --configfile config/doc_figures.yaml -- build_docs
# Open _build/html/index.html in browser
Updating Documentation¶
Edit
.rst
files indocs/
Rebuild:
tools/smk -j4 --configfile config/doc_figures.yaml --forcerun build_docs -- build_docs
The
--forcerun build_docs
is necessary when only documentation text has been updated and no other files/figures.Check for warnings/errors
Commit documentation changes
Docstring Guidelines¶
Use NumPy-style docstrings:
def my_function(param1: int, param2: str) -> float:
"""One-line summary.
Longer description if needed, explaining purpose, algorithm, etc.
Parameters
----------
param1 : int
Description of param1
param2 : str
Description of param2
Returns
-------
float
Description of return value
Raises
------
ValueError
If param1 is negative
Notes
-----
Additional implementation notes, references, etc.
"""
Contributing Guidelines¶
Before Submitting a Pull Request¶
Run linter:
uv run ruff check . && uv run ruff format .
(this is taken care of automatically if you set uppre-commit
)Test workflow: Verify that the default configuration runs successfully
Update documentation: If changing user-facing behavior
Write commit messages: Descriptive and following conventions
Pull Request Process¶
Fork the repository
Create a feature branch
Make changes with clear commits
Push to your fork
Open pull request with description of changes
Address review feedback
Merge once approved