diff --git a/README.md b/README.md index 9bab38e..a23e468 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,55 @@ See the [SEALS documentation](https://justinandrewjohnson.com/earth_economy_devstack/seals_overview.html) for full details. -## Developer notes +--- -To manually (wihtout using github actions) push a new release to Pypi, run the following command: +## Getting Started +### Environment Setup + +See [docs/environment_setup.md](docs/environment_setup.md) for detailed instructions. + +Quick start: ```bash -python -m build -python -m twine upload dist/* -``` \ No newline at end of file +conda create -n hazelbean_env python=3.10 +conda activate hazelbean_env +pip install hazelbean seals +``` + +### Project Setup + +1. **Create project folder structure** + ```bash + mkdir -p ~/Files/seals/projects/my_project/my_project_dev + ``` + - `my_project/` - runtime folder (not version controlled) + - `my_project_dev/` - version controlled code and inputs + +2. **Copy and rename the run script** + ```bash + cp seals_dev/run_seals_standard.py projects/my_project/my_project_dev/run_my_project.py + ``` + +3. **Modify run_my_project.py** + - Set `project_name = 'my_project'` + - Set `project_dir = '..'` (parent of _dev folder) + - Set `input_data_dir = 'input/my_project_input'` + - Configure scenarios, years, AOI as needed + +4. **Create version-controlled input folder** + ```bash + mkdir -p my_project_dev/input/my_project_input + ``` + +5. **Add your inputs** to `my_project_dev/input/my_project_input/`: + - [scenarios.csv](docs/scenarios_format.md) - scenario definitions + +6. **Run the script** + ```bash + conda activate hazelbean_env + cd my_project_dev + python run_my_project.py + ``` + This creates in `my_project/`: + - `input/` - copied from `my_project_dev/input/my_project_input/` + - `intermediate/` - SEALS outputs and results diff --git a/docs/environment_setup.md b/docs/environment_setup.md new file mode 100644 index 0000000..0714901 --- /dev/null +++ b/docs/environment_setup.md @@ -0,0 +1,38 @@ +# SEALS Environment Setup + +## 1. Install Miniforge/Conda + +If not already installed, download and install [Miniforge](https://github.com/conda-forge/miniforge). + +## 2. Create hazelbean environment + +```bash +conda create -n hazelbean_env python=3.10 +conda activate hazelbean_env +``` + +## 3. Install dependencies + +**From PyPI:** +```bash +pip install hazelbean +pip install seals +``` + +**Or from source (for development):** +```bash +cd ~/Files/seals/hazelbean_dev +pip install -e . + +cd ~/Files/seals/seals_dev +pip install -e . +``` + +Both methods install all dependencies automatically. + +## 4. Activate environment + +Always activate before running SEALS scripts: +```bash +conda activate hazelbean_env +``` diff --git a/docs/scenarios_format.md b/docs/scenarios_format.md new file mode 100644 index 0000000..2f5db0f --- /dev/null +++ b/docs/scenarios_format.md @@ -0,0 +1,43 @@ +# scenarios.csv Format + +The `scenarios.csv` file defines your SEALS scenarios. Each row represents one scenario. + +## Column Reference + +| Column | Description | Example | +|--------|-------------|---------| +| `scenario_label` | Unique scenario identifier | `baseline`, `ssp2_rcp45` | +| `scenario_type` | `baseline` or `policy` | `baseline` | +| `aoi` | Area of interest | `global` or `from_regional_projections_input_path` | +| `exogenous_label` | Exogenous scenario name | `baseline` | +| `climate_label` | Climate scenario | `rcp45` | +| `model_label` | Model source | `gtap`, `magpie`, `luh2` | +| `counterfactual_label` | Counterfactual reference | `baseline` | +| `years` | Space-separated projection years | `2030 2050` | +| `baseline_reference_label` | Reference scenario for policy | `baseline` (blank for baseline) | +| `base_years` | Base year(s) | `2017` | +| `key_base_year` | Key base year for LULC | `2017` | +| `comparison_counterfactual_labels` | For comparisons | (usually blank) | +| `time_dim_adjustment` | Time adjustment | `add2015` | +| `coarse_projections_input_path` | Path to LUH2 data | `luh2/raw_data/rcp45_ssp2/...nc` | +| `lulc_src_label` | LULC source | `esa` | +| `lulc_simplification_label` | LULC class scheme | `seals7` | +| `lulc_correspondence_path` | LULC correspondence file | `seals/default_inputs/esa_seals7_correspondence.csv` | +| `coarse_src_label` | Coarse data source | `luh2-14` | +| `coarse_simplification_label` | Coarse class scheme | `seals7` | +| `coarse_correspondence_path` | Coarse correspondence file | `seals/default_inputs/luh2-14_seals7_correspondence.csv` | +| `lc_class_varname` | Land class variable | `all_variables` | +| `dimensions` | Dimensions | `time` | +| `calibration_parameters_source` | Calibration file | `seals/default_inputs/default_global_coefficients.csv` | +| `base_year_lulc_path` | Base year LULC raster | `lulc/esa/lulc_esa_2017.tif` | +| `regional_projections_input_path` | Regional projections CSV (optional) | `regional_projections/my_projections.csv` | +| `regions_vector_path` | Regions shapefile | `cartographic/countries_iso3_with_label.gpkg` | +| `regions_column_label` | Region column name | `iso3_label` | + +## Example + +```csv +scenario_label,scenario_type,aoi,exogenous_label,climate_label,model_label,counterfactual_label,years,baseline_reference_label,base_years,key_base_year,comparison_counterfactual_labels,time_dim_adjustment,coarse_projections_input_path,lulc_src_label,lulc_simplification_label,lulc_correspondence_path,coarse_src_label,coarse_simplification_label,coarse_correspondence_path,lc_class_varname,dimensions,calibration_parameters_source,base_year_lulc_path,regional_projections_input_path,regions_vector_path,regions_column_label +baseline,baseline,global,baseline,rcp45,luh2,baseline,2017,,2017,2017,,add2015,luh2/raw_data/rcp45_ssp2/multiple-states_input4MIPs_landState_ScenarioMIP_UofMD-MESSAGE-ssp245-2-1-f_gn_2015-2100.nc,esa,seals7,seals/default_inputs/esa_seals7_correspondence.csv,luh2-14,seals7,seals/default_inputs/luh2-14_seals7_correspondence.csv,all_variables,time,seals/default_inputs/default_global_coefficients.csv,lulc/esa/lulc_esa_2017.tif,,cartographic/countries_iso3_with_label.gpkg,iso3_label +ssp2_rcp45,policy,global,ssp2,rcp45,luh2,ssp2_rcp45,2030 2050,baseline,2017,2017,,add2015,luh2/raw_data/rcp45_ssp2/multiple-states_input4MIPs_landState_ScenarioMIP_UofMD-MESSAGE-ssp245-2-1-f_gn_2015-2100.nc,esa,seals7,seals/default_inputs/esa_seals7_correspondence.csv,luh2-14,seals7,seals/default_inputs/luh2-14_seals7_correspondence.csv,all_variables,time,seals/default_inputs/default_global_coefficients.csv,lulc/esa/lulc_esa_2017.tif,,cartographic/countries_iso3_with_label.gpkg,iso3_label +``` diff --git a/seals/seals_utils.py b/seals/seals_utils.py index 46b9beb..520b569 100644 --- a/seals/seals_utils.py +++ b/seals/seals_utils.py @@ -715,7 +715,14 @@ def set_derived_attributes(p): p.fine_resolution = hb.get_cell_size_from_path(p.base_year_lulc_path) p.fine_resolution_arcseconds = hb.pyramid_compatible_resolution_to_arcseconds[p.fine_resolution] - if hb.path_exists(p.coarse_projections_input_path): + # Check for explicit coarse resolution from scenarios.csv column. + # Regional (non-global) NetCDFs need this because auto-detection assumes + # global extent (180/num_lat_cells) and computes wrong values. + explicit_coarse_resolution = getattr(p, 'coarse_resolution_arcseconds', None) + if explicit_coarse_resolution is not None: + p.coarse_resolution_arcseconds = float(explicit_coarse_resolution) + p.coarse_resolution = hb.pyramid_compatible_resolutions[p.coarse_resolution_arcseconds] + elif hb.path_exists(p.coarse_projections_input_path): p.coarse_resolution = hb.get_cell_size_from_path(p.coarse_projections_input_path) p.coarse_resolution_arcseconds = hb.pyramid_compatible_resolution_to_arcseconds[p.coarse_resolution] else: