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 inside site-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/seals subdirectory. The nesting looks redundant but is how Python packaging works
  • Inside it, __init__.py is 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 logic
    • seals_initialize_project.py — the task-tree builders
    • seals_utils.py — helpers
    • run_seals_standard.py — the run file

Run files

  • You do not run seals_main.py directly
  • 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 2050
    • run_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 only p
  • 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 p

Part 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 to p
  • p carries 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 p and 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_mode decides how much prior work is reused:
    • 'check' — reuse the stable dir, recompute only what is missing (the default)
    • 'fresh_intermediate' — redo all computation, keep input/ (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 p from 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’s input/ 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_type is a column in the CSV, not something a run file sets
  • baseline — the observed anchor; not downscaled, supplies base-year LULC
  • policy — a trajectory to downscale, compared against a counterfactual
  • bau — business-as-usual, being deprecated in favour of baseline; 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 project input/ dir, then base_data, then a cloud bucket, downloading what is missing
  • p.base_data_dir is 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_data to 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