Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
a4f4cf3
Semi-implicit model for OGGM dynamical runs (#125)
btobers Sep 22, 2025
f5d3b73
Use spinup flowlines for calibration and simulation (#134)
btobers Oct 7, 2025
5949dd4
Added functionality for importing daily ERA5 data (#136)
albinwwells Oct 7, 2025
5f3bacf
Issue 128 -- importing melt extent and snowline data into OGGM gdirs …
albinwwells Oct 9, 2025
e210814
Added reference DEM field to melt extent and snowline data imports (#…
albinwwells Oct 10, 2025
fd6a26c
Framework for 1D elevation change calibration (#137)
btobers Oct 16, 2025
b1f89f7
Export regional Glen A from calibrated inversion
btobers Oct 22, 2025
66b9bf0
Turn off inversion filter
btobers Oct 22, 2025
0b87829
Load calibrated calving_k values for dynamical calibration of tidewat…
btobers Oct 23, 2025
ef4a7f8
Convert to elevation change considering modeled densities
btobers Oct 23, 2025
96a105d
Ensure spinup_start_yr < 2000
btobers Oct 26, 2025
ef89bf5
Safely load elev_change_1d
btobers Oct 26, 2025
6451476
More robustly sample initials for any stuck chains
btobers Oct 28, 2025
c437c27
Enabling daily mass balance functionality (#143)
drounce Nov 4, 2025
476a2c7
Check if relative paths exist (#144)
btobers Nov 4, 2025
4829129
Replace run_calibration_reg_glena.py with run_inversion.py (#149)
btobers Nov 4, 2025
7ad4cae
Reformat MCMC calibration framework to make more modular (#155)
btobers Nov 4, 2025
5bd47a5
Ingest and process 2d dhdt data (#158)
btobers Nov 4, 2025
ea2977f
161 mcmc daily mbmaxloss update (#162)
drounce Nov 5, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions .github/workflows/test_suite.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: 'Install PyGEM and Run Test Suite'
on:
push:
branches:
- master
- main
- dev
paths:
- '**.py'
Expand Down Expand Up @@ -58,7 +58,7 @@ jobs:
- name: 'Clone the PyGEM-notebooks repo'
run: |
BRANCH=${GITHUB_REF#refs/heads/}
if [ "$BRANCH" = "master" ]; then
if [ "$BRANCH" = "main" ]; then
NOTEBOOK_BRANCH="main"
else
NOTEBOOK_BRANCH="dev"
Expand All @@ -73,4 +73,5 @@ jobs:
python3 -m pytest --cov=pygem -v --durations=20 pygem/tests/test_01_basics.py
python3 -m pytest --cov=pygem -v --durations=20 pygem/tests/test_02_config.py
python3 -m pytest --cov=pygem -v --durations=20 pygem/tests/test_03_notebooks.py
python3 -m pytest --cov=pygem -v --durations=20 pygem/tests/test_04_postproc.py
python3 -m pytest --cov=pygem -v --durations=20 pygem/tests/test_04_auxiliary.py
python3 -m pytest --cov=pygem -v --durations=20 pygem/tests/test_05_postproc.py
365 changes: 365 additions & 0 deletions config.yaml

Large diffs are not rendered by default.

11 changes: 11 additions & 0 deletions docs/_static/elev_change_1d/01.00570_elev_change_1d.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
bin_start,bin_stop,bin_area,date_start,date_end,dh,dh_sigma,ref_dem,ref_dem_year
1300.0,1400.0,79778.125,2016-06-01,2017-06-01,-4.50164794921875,2.37652587890625,COP30,2013
1400.0,1500.0,107009.375,2016-06-01,2017-06-01,-3.091949462890625,1.5350341796875,COP30,2013
1500.0,1600.0,96590.625,2016-06-01,2017-06-01,-2.728790283203125,1.53717041015625,COP30,2013
1600.0,1700.0,138362.5,2016-06-01,2017-06-01,-1.95526123046875,3.421600341796875,COP30,2013
1700.0,1800.0,176418.75,2016-06-01,2017-06-01,-2.159393310546875,7.1052093505859375,COP30,2013
1800.0,1900.0,231671.875,2016-06-01,2017-06-01,-1.4642333984375,1.008819580078125,COP30,2013
1900.0,2000.0,270537.5,2016-06-01,2017-06-01,-1.630615234375,1.78204345703125,COP30,2013
2000.0,2100.0,218462.5,2016-06-01,2017-06-01,-1.223602294921875,1.511993408203125,COP30,2013
2100.0,2200.0,137959.375,2016-06-01,2017-06-01,-1.411376953125,1.4329833984375,COP30,2013
2200.0,2300.0,100803.125,2016-06-01,2017-06-01,-1.162353515625,2.39971923828125,COP30,2013
1 change: 1 addition & 0 deletions docs/_static/elev_change_1d/01.00570_elev_change_1d.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"ref_dem": "COP30", "ref_dem_year": 2013, "dates": [["2016-06-01", "2017-06-01"]], "bin_edges": [1300.0, 1400.0, 1500.0, 1600.0, 1700.0, 1800.0, 1900.0, 2000.0, 2100.0, 2200.0, 2300.0], "bin_centers": [1350.0, 1450.0, 1550.0, 1650.0, 1750.0, 1850.0, 1950.0, 2050.0, 2150.0, 2250.0], "bin_area": [79778.125, 107009.375, 96590.625, 138362.5, 176418.75, 231671.875, 270537.5, 218462.5, 137959.375, 100803.125], "dh": [[-4.50164794921875, -3.091949462890625, -2.728790283203125, -1.95526123046875, -2.159393310546875, -1.4642333984375, -1.630615234375, -1.223602294921875, -1.411376953125, -1.162353515625]], "dh_sigma": [[2.37652587890625, 1.5350341796875, 1.53717041015625, 3.421600341796875, 7.1052093505859375, 1.008819580078125, 1.78204345703125, 1.511993408203125, 1.4329833984375, 2.39971923828125]]}
9 changes: 9 additions & 0 deletions docs/_static/elev_change_1d/README.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
"""
Python Glacier Evolution Model (PyGEM)

copyright © 2025 Brandon Tober <btober@cmu.edu>, David Rounce <drounce@cmu.edu>

Distributed under the MIT license
"""

This directory contains example 1d elevation change data following the format specifications of PyGEM.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

9 changes: 4 additions & 5 deletions docs/calibration_options.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@ Several calibration options exist, which vary with respect to complexity and com

| Calibration option | Overview | Reference |
| :--- | :--- | :--- |
| ['HH2015'](HH2015_target) | Finds single set of parameters.<br>Varies in order: $f_{snow}$, $k_{p}$, $T_{bias}$ | [Huss and Hock (2015)](https://www.frontiersin.org/articles/10.3389/feart.2015.00054/full) |
| ['HH2015mod'](HH2015mod_target) | Finds single set of parameters.<br>Varies in order: $k_{p}$, $T_{bias}$ | [Rounce et al. 2020](https://www.cambridge.org/core/journals/journal-of-glaciology/article/quantifying-parameter-uncertainty-in-a-largescale-glacier-evolution-model-using-bayesian-inference-application-to-high-mountain-asia/61D8956E9A6C27CC1A5AEBFCDADC0432) |
| ['emulator'](emulator_target) | Creates emulator for ['MCMC'](MCMC_target).<br>Finds single set of parameters with emulator following ['HH2015mod'](HH2015mod_target) | [Rounce et al. 2023](https://www.science.org/doi/10.1126/science.abo1324) |
| ['MCMC'](MCMC_target) | Finds multiple sets of parameters using Bayesian inference with [emulator](emulator_target).<br> Varies $f_{snow}$, $k_{p}$, $T_{bias}$ | [Rounce et al. 2023](https://www.science.org/doi/10.1126/science.abo1324) |
| ['MCMC_fullsim'](MCMC_target) | Finds multiple sets of parameters using Bayesian inference with full model simulations.<br> Varies $f_{snow}$, $k_{p}$, $T_{bias}$ | [Rounce et al. 2020](https://www.cambridge.org/core/journals/journal-of-glaciology/article/quantifying-parameter-uncertainty-in-a-largescale-glacier-evolution-model-using-bayesian-inference-application-to-high-mountain-asia/61D8956E9A6C27CC1A5AEBFCDADC0432) |
| ['HH2015'](HH2015_target) | Finds single set of parameters.<br>Varies in order: $f_{snow}$, $k_{p}$, $T_{bias}$ | [Huss and Hock, 2015](https://www.frontiersin.org/articles/10.3389/feart.2015.00054/full) |
| ['HH2015mod'](HH2015mod_target) | Finds single set of parameters.<br>Varies in order: $k_{p}$, $T_{bias}$ | [Rounce et al., 2020](https://www.cambridge.org/core/journals/journal-of-glaciology/article/quantifying-parameter-uncertainty-in-a-largescale-glacier-evolution-model-using-bayesian-inference-application-to-high-mountain-asia/61D8956E9A6C27CC1A5AEBFCDADC0432) |
| ['emulator'](emulator_target) | Creates emulator for ['MCMC'](MCMC_target).<br>Finds single set of parameters with emulator following ['HH2015mod'](HH2015mod_target) | [Rounce et al., 2023](https://www.science.org/doi/10.1126/science.abo1324) |
| ['MCMC'](MCMC_target) | Finds many sets of parameters using Bayesian inference. Setting `calib.MCMC_params.option_use_emulator=True` in ~/PyGEM/config.yaml will run Bayesian inference using the mass balance emulator. Setting `calib.MCMC_params.option_use_emulator=False` (or when performing dynamical calibration against elevation change data)$^*$ will run Bayesian inference calibration with full model simulations.<br>Varies $f_{snow}$, $k_{p}$, $T_{bias}$, (optionally $\rho_{ablation}$, $\rho_{accumulation}$)$^*$ | [Rounce et al., 2020](https://www.cambridge.org/core/journals/journal-of-glaciology/article/quantifying-parameter-uncertainty-in-a-largescale-glacier-evolution-model-using-bayesian-inference-application-to-high-mountain-asia/61D8956E9A6C27CC1A5AEBFCDADC0432); [2023](https://www.science.org/doi/10.1126/science.abo1324) |
| [Future options](cal_custom_target) | Stay tuned for new options coming in 2023/2024! | |

The output of each calibration is a .json file that holds a dictionary of the calibration options and the subsequent model parameters. Thus, the .json file will store several calibration options. Each calibration option is a key to the dictionary. The model parameters are also stored in a dictionary (i.e., a dictionary within a dictionary) with each model parameter being a key to the dictionary that provides access to a list of values for that specific model parameter. The following shows an example of how to print a list of the precipitation factors ($k_{p}$) for the calibration option specified in the input file:
Expand Down
2 changes: 1 addition & 1 deletion docs/install_pygem.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Next, choose your preferred PyGEM installation option:<br>

(stable_install_target)=
## Stable install
The simplest **stable** installation method is to use an environment file. Right-click and save PyGEM's recommended environment file from [this link](https://raw.githubusercontent.com/PyGEM-Community/PyGEM/refs/heads/master/docs/pygem_environment.yml).
The simplest **stable** installation method is to use an environment file. Right-click and save PyGEM's recommended environment file from [this link](https://raw.githubusercontent.com/PyGEM-Community/PyGEM/refs/heads/main/docs/pygem_environment.yml).

From the folder where you saved the file, run `conda env create -f pygem_environment.yml`.
```{note}
Expand Down
15 changes: 6 additions & 9 deletions docs/model_structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ If you use a different file structure and do not update the relative file paths
The model code itself is heavily commented with the hope that the code is easy to follow and develop further. After [installing PyGEM](install_pygem_target), downloading the required [input files](model_inputs_target), and setting up the [directory structure](directory_structure_target) (or modifying the *~/PyGEM/config.yaml* with your preferred directory structure) you are ready to run the code! Generally speaking, the workflow includes:
* [Pre-process data](preprocessing_target) <em>(optional if including more data)</em>
* [Set up configuration file](config_workflow_target)
* [Calibrate frontal ablation parameter](workflow_cal_frontalablation_target) <em>(optional for marine-terimating glaciers)</em>
* [Calibrate climatic mass balance parameters](workflow_cal_prms_target)
* [Calibrate ice viscosity parameter](workflow_cal_glena_target)
* [Calibrate frontal ablation parameter](workflow_cal_frontalablation_target) <em>(optional for marine-terimating glaciers)</em>
* [Calibrate ice viscosity parameter](workflow_run_inversion_target)
* [Run model simulation](workflow_sim_target)
* [Post-process output](workflow_post_target)
* [Analyze output](workflow_analyze_target)
Expand Down Expand Up @@ -92,17 +92,14 @@ Circularity issues exist in calibrating the frontal ablation parameter as the ma
```


(workflow_cal_glena_target)=
(workflow_run_inversion_target)=
### Calibrate ice viscosity model parameter
The ice viscosity ("Glen A") model parameter is calibrated such that the ice volume estimated using the calibrated mass balance gradients are consistent with the reference ice volume estimates ([Farinotti et al. (2019)](https://www.nature.com/articles/s41561-019-0300-3)) for each RGI region. This is done by running the following:
```
run_calibration_reg_glena
run_inversion
```

If successful, the script will run without error and output the following:
* ../Output/calibration/‘glena_region.csv’

For more details, see the [run_calibration_reg_glena.py Script Overview](run_calibration_reg_glena_overview_target).
For more details, see the [run_inversion.py Script Overview](run_inversion_overview_target).


(workflow_sim_target)=
Expand Down Expand Up @@ -130,7 +127,7 @@ For more details, see the [run_simulation.py Script Overview](run_simulation_tar
There are currently several scripts available to post-process PyGEM simulations. To aggregate simulations by RGI region, climate scenario, and variable, run the *postproc_compile_simulations.py* script. For example to compile all Alaska's glacier mass, area, runoff, etc. for various scenarios we would run the following:

```
compile_simulations -rgi_region 01 -scenario ssp245 ssp370 ssp585
postproc_compile_simulations -rgi_region 01 -scenario ssp245 ssp370 ssp585
```

(workflow_analyze_target)=
Expand Down
24 changes: 0 additions & 24 deletions docs/run_calibration_reg_glena_overview.md

This file was deleted.

22 changes: 22 additions & 0 deletions docs/run_inversion_overview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
(run_inversion_overview_target)=
# run_inversion.py
This script will perform ice thickness inversion while calibrating the ice viscosity ("Glen A") model parameter such that the modeled ice volume roughly matches the ice volume estimates from [Farinotti et al. (2019)](https://www.nature.com/articles/s41561-019-0300-3) for each RGI region. Run the script as follows:

```
run_inversion -rgi_region01 <region>
```

## Script Structure

Broadly speaking, the script follows:
* Load glaciers
* Load climate data
* Compute apparent mass balance and invert for initial ice thickness
* Use minimization to find agreement between our modeled and [Farinotti et al. (2019)](https://www.nature.com/articles/s41561-019-0300-3) modeled ice thickness estimates for each RGI region
* Export the calibrated parameters

## Special Considerations
The regional Glen A value is calibrated by inverting for the ice thickness of all glaciers in a given region without considering calving (all glaciers are considered land-terminating). After the "best" Glen A value is determined, a final round of ice thickness inversion is performed for tidewater glaciers with calving turned **on**. Running this script will by default export the regionally calibrated Glen A values to the path specfied by `sim.oggm_dynamics.glen_a_regional_relpath` in *~/PyGEM/config.yaml'*. The calibrated inversion parameters also get stored within a given glacier directories *diagnostics.json* file, e.g.:
```
{"dem_source": "COPDEM90", "flowline_type": "elevation_band", "apparent_mb_from_any_mb_residual": 2893.2237556771674, "inversion_glen_a": 3.784593106855888e-24, "inversion_fs": 0}
```
2 changes: 1 addition & 1 deletion docs/scripts_overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ maxdepth: 1
---

run_calibration_frontalablation_overview
run_inversion_overview
run_calibration_overview
run_calibration_reg_glena_overview
run_simulation_overview
```
2 changes: 1 addition & 1 deletion pygem/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

copyright © 2018 David Rounce <drounce@cmu.edu

Distrubted under the MIT lisence
Distributed under the MIT license
"""

from importlib.metadata import version
Expand Down
14 changes: 4 additions & 10 deletions pygem/bin/op/compress_gdirs.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

copyright © 2024 Brandon Tober <btober@cmu.edu> David Rounce <drounce@cmu.edu>

Distrubted under the MIT lisence
Distributed under the MIT license

compress OGGM glacier directories
"""
Expand All @@ -24,19 +24,15 @@

# Initialize OGGM subprocess
cfg.initialize(logging_level='WARNING')
cfg.PATHS['working_dir'] = (
f'{pygem_prms["root"]}/{pygem_prms["oggm"]["oggm_gdir_relpath"]}'
)
cfg.PATHS['working_dir'] = f'{pygem_prms["root"]}/{pygem_prms["oggm"]["oggm_gdir_relpath"]}'
cfg.PARAMS['border'] = pygem_prms['oggm']['border']
cfg.PARAMS['use_multiprocessing'] = True


def compress_region(region):
print(f'\n=== Compressing glacier directories for RGI Region: {region} ===')
# Get glacier IDs from the RGI shapefile
rgi_ids = gpd.read_file(
utils.get_rgi_region_file(str(region).zfill(2), version='62')
)['RGIId'].tolist()
rgi_ids = gpd.read_file(utils.get_rgi_region_file(str(region).zfill(2), version='62'))['RGIId'].tolist()

# Initialize glacier directories
gdirs = workflow.init_glacier_directories(rgi_ids)
Expand All @@ -52,9 +48,7 @@ def compress_region(region):


def main():
parser = argparse.ArgumentParser(
description='Script to compress and store OGGM glacier directories'
)
parser = argparse.ArgumentParser(description='Script to compress and store OGGM glacier directories')
# add arguments
parser.add_argument(
'-rgi_region01',
Expand Down
2 changes: 1 addition & 1 deletion pygem/bin/op/duplicate_gdirs.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

copyright © 2024 Brandon Tober <btober@cmu.edu> David Rounce <drounce@cmu.edu>

Distrubted under the MIT lisence
Distributed under the MIT license

duplicate OGGM glacier directories
"""
Expand Down
18 changes: 5 additions & 13 deletions pygem/bin/op/initialize.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

copyright © 2024 Brandon Tober <btober@cmu.edu> David Rounce <drounce@cmu.edu>

Distrubted under the MIT lisence
Distributed under the MIT license

initialization script (ensure config.yaml and get sample datasets)
"""
Expand All @@ -16,10 +16,8 @@

from pygem.setup.config import ConfigManager

# instantiate ConfigManager
# instantiate ConfigManager - store new config.yaml file
config_manager = ConfigManager(overwrite=True)
# read the config
pygem_prms = config_manager.read_config()


def print_file_tree(start_path, indent=''):
Expand Down Expand Up @@ -88,9 +86,7 @@ def download_and_unzip_from_google_drive(file_id, output_dir):
response = session.get(base_url, params={'id': file_id}, stream=True)
token = get_confirm_token(response)
if token:
response = session.get(
base_url, params={'id': file_id, 'confirm': token}, stream=True
)
response = session.get(base_url, params={'id': file_id, 'confirm': token}, stream=True)
save_response_content(response, zip_path)

# Unzip the file
Expand All @@ -99,11 +95,7 @@ def download_and_unzip_from_google_drive(file_id, output_dir):
zip_ref.extractall(tmppath)

# get root dir name of zipped files
dir = [
item
for item in os.listdir(tmppath)
if os.path.isdir(os.path.join(tmppath, item))
][0]
dir = [item for item in os.listdir(tmppath) if os.path.isdir(os.path.join(tmppath, item))][0]
unzip_dir = os.path.join(tmppath, dir)
# get unique name if root dir name already exists in output_dir
output_dir = get_unique_folder_name(os.path.join(output_dir, dir))
Expand All @@ -121,7 +113,7 @@ def main():
# Define the base directory
basedir = os.path.join(os.path.expanduser('~'), 'PyGEM')
# Google Drive file id for sample dataset
file_id = '1Wu4ZqpOKxnc4EYhcRHQbwGq95FoOxMfZ'
file_id = '1cRVG__7dVclut42LdQBjnXKpTvyYWBuK'
# download and unzip
out = download_and_unzip_from_google_drive(file_id, basedir)

Expand Down
10 changes: 3 additions & 7 deletions pygem/bin/op/list_failed_simulations.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

copyright © 2018 David Rounce <drounce@cmu.edu>

Distrubted under the MIT lisence
Distributed under the MIT license

script to check for failed glaciers for a given simulation and export a pickle file containing a list of said glacier numbers to be reprocessed
"""
Expand Down Expand Up @@ -63,18 +63,14 @@ def run(
# instantiate list of galcnos that are not in sim_dir
failed_glacnos = []

fps = glob.glob(
sim_dir + f'*_{calib_opt}_ba{bias_adj}_*_{sim_startyear}_{sim_endyear}_all.nc'
)
fps = glob.glob(sim_dir + f'*_{calib_opt}_ba{bias_adj}_*_{sim_startyear}_{sim_endyear}_all.nc')

# Glaciers with successful runs to process
glacno_ran = [x.split('/')[-1].split('_')[0] for x in fps]
glacno_ran = [x.split('.')[0].zfill(2) + '.' + x[-5:] for x in glacno_ran]

# print stats of successfully simualated glaciers
main_glac_rgi = main_glac_rgi_all.loc[
main_glac_rgi_all.apply(lambda x: x.rgino_str in glacno_ran, axis=1)
]
main_glac_rgi = main_glac_rgi_all.loc[main_glac_rgi_all.apply(lambda x: x.rgino_str in glacno_ran, axis=1)]
print(
f'{gcm} {str(sim_climate_scenario).replace("None", "")} glaciers successfully simulated:\n - {main_glac_rgi.shape[0]} of {main_glac_rgi_all.shape[0]} glaciers ({np.round(main_glac_rgi.shape[0] / main_glac_rgi_all.shape[0] * 100, 3)}%)'
)
Expand Down
Loading