First SEALS run walkthrough
Getting set up
- Follow the installation page first, or the shorter quickstart if you just want to run the model
- Clone SEALS and Hazelbean into the devstack layout — each repo in its own project folder under
~/Files/:~/Files/hazelbean/hazelbean_dev~/Files/seals/seals_dev
- The layout is not cosmetic: ProjectFlow reads it to decide where project directories go
- You know the install worked if this prints two paths inside
~/Files/, not insidesite-packages:
python -c "import hazelbean, seals; print(hazelbean.__file__); print(seals.__file__)"Explore the SEALS code
- The repo root
seals_dev/holds more than the library — docs, tests, scripts - The library itself is the
seals_dev/sealssubdirectory. The nesting looks redundant but is how Python packaging works - Inside it,
__init__.pyis what makes the directory importable as a package - The modules are split by what they hold:
seals_main.py,seals_tasks.py,seals_generate_base_data.py— task functions, the actual model logicseals_initialize_project.py— the task-tree buildersseals_utils.py— helpersrun_seals_standard.py— the run file
Run files
- You do not run
seals_main.pydirectly - A run file configures a project and executes a task tree built from the library’s task functions
- Two ship with SEALS, both in
seals_dev/seals:run_seals_standard.py— three scenarios, 2030 and 2050run_seals_standard_test.py— the same pipeline pared to a baseline, one BAU scenario, one year, Rwanda. Start here
- Open
run_seals_standard.py. It is about 60 lines and has three parts
Part 1: the task tree
build_task_tree(p)answers one question: what is this pipeline?- It contains nothing but tree construction. For stock SEALS it is one line, delegating to the shared library builder
- Compose extra library subtrees or your own tasks here if your project diverges
def build_task_tree(p):
seals_initialize_project.build_standard_task_tree(p)Part 2: run_project
run_project(p)answers how is it executed? It takes onlyp- The rule that decides what goes where:
run_project(p)sets what no variant ever changes — base data location, processing resolution- the caller sets what a variant might — project name, run mode, scenarios CSV
- When a constant starts varying, it moves one line up into the caller. There is no signature to edit and no default to keep in sync
def run_project(p):
p.run_in_parallel = 1 # before the tree: it has parallel iterators
build_task_tree(p)
p.skip_tasks(p.tasks_to_skip)
p.base_data_dir = os.path.join(p.user_dir, 'Files', 'base_data')
p.processing_resolution = 1.0
p.scenario_definitions_path = os.path.join(p.input_dir, p.scenario_definitions_filename)
seals_initialize_project.initialize_scenario_definitions(p)
seals_initialize_project.set_advanced_options(p)
p.execute()
return pPart 3: the __main__ guard
- This is where the project is configured — two lines of actual choice
- The guard is mandatory: importing a run file must never start a run. That is what lets a second run file import this pipeline instead of copying it
if __name__ == '__main__':
p = hb.ProjectFlow(project_name='seals_standard', run_mode='check')
p.scenario_definitions_filename = 'standard_scenarios.csv'
run_project(p)The p object
hb.ProjectFlow()is a class — a recipe for an object. Calling it produces an object, which we assign toppcarries the project’s attributes:p.base_data_dir,p.scenario_definitions_filename, and so on- It also has methods that act on it:
p.add_task(),p.skip_tasks(),p.get_path(),p.execute() - Every task receives
pand reads its configuration from it. That is how configuration set in__main__reaches code deep in the library
Where the project directory goes
- Not into the repo. Because the run file sits inside a cloned repo, ProjectFlow places the project dir just outside it, at
~/Files/seals/projects/<project_name>/ - You do not compute this path yourself, and you should not save data in
seals_dev run_modedecides how much prior work is reused:'check'— reuse the stable dir, recompute only what is missing (the default)'fresh_intermediate'— redo all computation, keepinput/(test projects only)'full'— a fresh timestamped dir; everything re-runs
Scenario definitions
The scenarios CSV
- The scenarios CSV specifies the runs you want. Each row is one scenario
- As the model iterates, it re-hydrates
pfrom the row it is on — which is why scenario-varying values belong in the CSV and not in the run file - The tracked copy lives in
input_template/beside the run file. ProjectFlow copies anything missing into the project’sinput/on first run and never overwrites your working copy - If the named file is nowhere to be found, SEALS generates a default so a first run still works
p.scenario_definitions_filename = 'standard_scenarios.csv'Scenario types
scenario_typeis a column in the CSV, not something a run file setsbaseline— the observed anchor; not downscaled, supplies base-year LULCpolicy— a trajectory to downscale, compared against a counterfactualbau— business-as-usual, being deprecated in favour ofbaseline; it still appears in shipped CSVs- Full column reference: scenarios_format.md
Automatically downloading data
- Paths in the CSV are ref_paths — relative locations resolved by
p.get_path(), which searches the projectinput/dir, thenbase_data, then a cloud bucket, downloading what is missing p.base_data_diris where those downloads land. Point it somewhere with room for very large files; the same base data serves every project- The directory must be named
base_datato match the bucket convention - The default bucket is public — no credentials needed
p.base_data_dir = os.path.join(p.user_dir, 'Files', 'base_data')Running it
Run the model
conda activate <your_env>
cd seals_dev/seals
python run_seals_standard_test.py- On startup SEALS prints the task tree it is about to compute
- To understand the model in depth, read the task functions behind those names
- Run the same command a second time: it finishes almost immediately, because every task is behind an existence check and completed work is skipped
Find the output
- Go to
~/Files/seals/projects/seals_standard_test/intermediate/ - There is one directory per task in the tree
- The downscaled maps are in
stitched_lulc_simplified_scenarios/, for example:
lulc_esa_seals7_ssp2_rcp45_luh2-message_bau_2030.tif
- Open it in QGIS alongside the base-year LULC and compare — that is your high-resolution land-use change projection
Next
- Quickstart — the condensed version of this page
- Conventions — the full run-file rules
- Project complexity — why run files are shaped this way