From 88ebcc117679227768e71e8778285ff00c2be1e5 Mon Sep 17 00:00:00 2001 From: albinwwells Date: Tue, 7 Oct 2025 15:09:42 -0400 Subject: [PATCH 1/7] Lat/lon distace check for class_climate.py --- pygem/class_climate.py | 23 +++++++++++++++++++---- pygem/setup/config.py | 3 +-- pygem/setup/config.yaml | 1 - 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/pygem/class_climate.py b/pygem/class_climate.py index d2564eff..fcac5572 100755 --- a/pygem/class_climate.py +++ b/pygem/class_climate.py @@ -192,10 +192,7 @@ def __init__(self, name=str(), sim_climate_scenario=str(), realization=None): self.elev_fn = pygem_prms['climate']['paths']['era5_elev_fn'] self.lr_fn = pygem_prms['climate']['paths']['era5_lr_fn'] # Variable filepaths - if pygem_prms['climate']['paths']['era5_fullpath']: - self.var_fp = '' - self.fx_fp = '' - else: + if pygem_prms['climate']['paths']['era5_relpath']: self.var_fp = ( pygem_prms['root'] + pygem_prms['climate']['paths']['era5_relpath'] @@ -204,6 +201,10 @@ def __init__(self, name=str(), sim_climate_scenario=str(), realization=None): pygem_prms['root'] + pygem_prms['climate']['paths']['era5_relpath'] ) + else: + self.var_fp = '' + self.fx_fp = '' + # Extra information self.timestep = pygem_prms['time']['timestep'] self.rgi_lat_colname = pygem_prms['rgi']['rgi_lat_colname'] @@ -232,6 +233,7 @@ def __init__(self, name=str(), sim_climate_scenario=str(), realization=None): pygem_prms['root'] + pygem_prms['climate']['paths']['eraint_relpath'] ) + # Extra information self.timestep = pygem_prms['time']['timestep'] self.rgi_lat_colname = pygem_prms['rgi']['rgi_lat_colname'] @@ -691,6 +693,19 @@ def importGCMvarnearestneighbor_xarray( start_idx : end_idx + 1, latlon[0], latlon[1] ].values + # Check all glacier use appropriate climate data + for i, latlon in enumerate(latlon_nearidx): + rgi_id = main_glac_rgi[pygem_prms['rgi']['rgi_glacno_float_colname']].values[i] + lat_res = abs(np.diff(data[vn][self.lat_vn].values)[0]) + lon_res = abs(np.diff(data[vn][self.lon_vn].values)[0]) + lat_dd = abs(main_glac_rgi[self.rgi_lat_colname].values[i] - data[vn][self.lat_vn].values[latlon[0]]) + lon_dd = abs(main_glac_rgi[self.rgi_lon_colname].values[i] - data[vn][self.lon_vn].values[latlon[1]]) + + assert lat_dd <= lat_res and lon_dd <= lon_res, ( + f'Climate data pixel too from glacier {rgi_id}: Δlat={lat_dd:.3f}, ' + + f'Δlon={lon_dd:.3f}, res=({lat_res:.3f}, {lon_res:.3f})' + ) + # Convert to series glac_variable_series = np.array( [glac_variable_dict[x] for x in latlon_nearidx] diff --git a/pygem/setup/config.py b/pygem/setup/config.py index 608daf7b..f54598d7 100644 --- a/pygem/setup/config.py +++ b/pygem/setup/config.py @@ -157,8 +157,7 @@ def _validate_config(self, config): 'climate.sim_wateryear': str, 'climate.constantarea_years': int, 'climate.paths': dict, - 'climate.paths.era5_fullpath': bool, - 'climate.paths.era5_relpath': str, + 'climate.paths.era5_relpath': (str, type(None)), 'climate.paths.era5_temp_fn': str, 'climate.paths.era5_tempstd_fn': str, 'climate.paths.era5_prec_fn': str, diff --git a/pygem/setup/config.yaml b/pygem/setup/config.yaml index 9755acee..6798b205 100644 --- a/pygem/setup/config.yaml +++ b/pygem/setup/config.yaml @@ -66,7 +66,6 @@ climate: # ===== CLIMATE DATA FILEPATHS AND FILENAMES ===== paths: # ERA5 (default reference climate data) - era5_fullpath: False # bool. 'True' ignores 'root' and 'era5_relpath' for ERA5 data and assumes below filenames are absolute era5_relpath: /climate_data/ERA5/ era5_temp_fn: ERA5_temp_monthly.nc era5_tempstd_fn: ERA5_tempstd_monthly.nc From 13cd1e860d4c4324f2bafba8b1262780a5850ed0 Mon Sep 17 00:00:00 2001 From: albinwwells Date: Thu, 9 Oct 2025 11:07:25 -0400 Subject: [PATCH 2/7] Added functionality to load melt extent and snowline data into OGGM gdirs --- .../sample_melt_extent_elev.csv | 336 ++++++++++++++++++ .../sample_snowline_elev.csv | 336 ++++++++++++++++++ pygem/bin/run/run_calibration.py | 59 +-- pygem/bin/run/run_simulation.py | 321 ++++++++--------- pygem/class_climate.py | 28 +- pygem/oggm_compat.py | 26 +- pygem/setup/config.py | 4 + pygem/setup/config.yaml | 9 + pygem/shop/meltextent_and_snowline_1d.py | 283 +++++++++++++++ 9 files changed, 1202 insertions(+), 200 deletions(-) create mode 100644 docs/_static/example_meltextent_and_snowline_1d/sample_melt_extent_elev.csv create mode 100644 docs/_static/example_meltextent_and_snowline_1d/sample_snowline_elev.csv create mode 100644 pygem/shop/meltextent_and_snowline_1d.py diff --git a/docs/_static/example_meltextent_and_snowline_1d/sample_melt_extent_elev.csv b/docs/_static/example_meltextent_and_snowline_1d/sample_melt_extent_elev.csv new file mode 100644 index 00000000..62bc29de --- /dev/null +++ b/docs/_static/example_meltextent_and_snowline_1d/sample_melt_extent_elev.csv @@ -0,0 +1,336 @@ +date,z,z_min,z_max,direction,ref_dem_year +2016-11-24,1229.0,1182.0,1260.0,ascending,2013 +2016-12-18,1182.0,1182.0,1186.0,ascending,2013 +2017-01-11,1182.0,1182.0,1182.0,ascending,2013 +2017-02-04,1182.0,1182.0,1182.0,ascending,2013 +2017-02-28,1182.0,1182.0,1182.0,ascending,2013 +2017-03-12,1182.0,1182.0,1182.0,ascending,2013 +2017-03-24,1182.0,1182.0,1182.0,ascending,2013 +2017-04-05,1182.0,1182.0,1182.0,ascending,2013 +2017-04-17,1239.0,1209.0,1265.0,ascending,2013 +2017-04-29,1543.0,1497.0,1579.0,ascending,2013 +2017-05-11,1915.0,1864.0,1966.0,ascending,2013 +2017-05-23,1844.0,1812.0,1878.0,ascending,2013 +2017-06-04,2312.0,2273.0,2387.0,ascending,2013 +2017-06-16,2324.0,2307.0,2371.0,ascending,2013 +2017-06-28,2329.0,2314.0,2371.0,ascending,2013 +2017-07-10,2360.0,2326.0,2371.0,ascending,2013 +2017-07-22,2340.0,2318.0,2371.0,ascending,2013 +2017-08-15,2335.0,2315.0,2387.0,ascending,2013 +2017-08-27,2346.0,2318.0,2387.0,ascending,2013 +2017-09-08,2364.0,2346.0,2385.0,ascending,2013 +2017-09-20,2288.0,2239.0,2360.0,ascending,2013 +2017-10-02,2215.0,2155.0,2303.0,ascending,2013 +2017-11-07,1428.0,1186.0,1625.0,ascending,2013 +2017-12-01,1182.0,1182.0,1182.0,ascending,2013 +2017-12-13,1182.0,1182.0,1182.0,ascending,2013 +2018-01-06,1182.0,1182.0,1182.0,ascending,2013 +2018-01-18,1182.0,1182.0,1182.0,ascending,2013 +2018-02-11,1182.0,1182.0,1182.0,ascending,2013 +2018-02-23,1182.0,1182.0,1182.0,ascending,2013 +2018-03-07,1182.0,1182.0,1182.0,ascending,2013 +2018-03-19,1182.0,1182.0,1182.0,ascending,2013 +2018-03-31,1182.0,1182.0,1182.0,ascending,2013 +2018-04-12,1304.0,1284.0,1327.0,ascending,2013 +2018-04-24,1365.0,1353.0,1374.0,ascending,2013 +2018-05-06,1199.0,1186.0,1211.0,ascending,2013 +2018-05-18,1729.0,1704.0,1757.0,ascending,2013 +2018-05-30,2184.0,2139.0,2231.0,ascending,2013 +2018-06-11,2385.0,2371.0,2387.0,ascending,2013 +2018-06-23,2385.0,2371.0,2387.0,ascending,2013 +2018-07-05,2369.0,2360.0,2387.0,ascending,2013 +2018-07-17,2371.0,2369.0,2387.0,ascending,2013 +2018-07-29,2333.0,2314.0,2387.0,ascending,2013 +2018-08-10,2368.0,2356.0,2387.0,ascending,2013 +2018-08-22,2329.0,2312.0,2385.0,ascending,2013 +2018-09-03,2335.0,2315.0,2387.0,ascending,2013 +2018-09-15,2364.0,2340.0,2387.0,ascending,2013 +2018-10-09,2329.0,2312.0,2385.0,ascending,2013 +2018-10-21,2011.0,1859.0,2247.0,ascending,2013 +2018-11-02,1807.0,1272.0,2066.0,ascending,2013 +2018-11-14,1853.0,1500.0,2063.0,ascending,2013 +2018-12-08,1560.0,1186.0,1782.0,ascending,2013 +2018-12-20,1404.0,1182.0,1577.0,ascending,2013 +2019-01-01,1310.0,1182.0,1418.0,ascending,2013 +2019-01-13,1200.0,1182.0,1214.0,ascending,2013 +2019-01-25,1182.0,1182.0,1182.0,ascending,2013 +2019-02-06,1182.0,1182.0,1182.0,ascending,2013 +2019-02-18,1182.0,1182.0,1182.0,ascending,2013 +2019-03-02,1182.0,1182.0,1182.0,ascending,2013 +2019-03-14,1182.0,1182.0,1182.0,ascending,2013 +2019-03-26,1182.0,1182.0,1182.0,ascending,2013 +2019-04-07,1182.0,1182.0,1182.0,ascending,2013 +2019-04-19,1342.0,1251.0,1413.0,ascending,2013 +2019-05-01,2132.0,2048.0,2252.0,ascending,2013 +2019-05-13,1744.0,1678.0,1804.0,ascending,2013 +2019-05-25,2387.0,2387.0,2387.0,ascending,2013 +2019-06-06,2385.0,2371.0,2387.0,ascending,2013 +2019-06-18,2361.0,2333.0,2387.0,ascending,2013 +2019-06-30,2326.0,2307.0,2387.0,ascending,2013 +2019-07-12,2324.0,2307.0,2385.0,ascending,2013 +2019-07-24,2346.0,2318.0,2387.0,ascending,2013 +2019-08-05,2346.0,2318.0,2387.0,ascending,2013 +2019-08-17,2340.0,2317.0,2387.0,ascending,2013 +2019-08-29,2324.0,2299.0,2385.0,ascending,2013 +2019-09-10,2370.0,2364.0,2387.0,ascending,2013 +2019-09-22,2323.0,2307.0,2364.0,ascending,2013 +2019-10-04,2229.0,2190.0,2287.0,ascending,2013 +2019-10-16,1806.0,1305.0,2045.0,ascending,2013 +2019-10-28,1615.0,1224.0,1837.0,ascending,2013 +2019-11-09,1369.0,1200.0,1501.0,ascending,2013 +2019-11-21,1312.0,1182.0,1418.0,ascending,2013 +2019-12-03,1220.0,1182.0,1244.0,ascending,2013 +2019-12-15,1187.0,1182.0,1194.0,ascending,2013 +2019-12-27,1182.0,1182.0,1182.0,ascending,2013 +2020-01-08,1186.0,1182.0,1191.0,ascending,2013 +2020-01-20,1182.0,1182.0,1182.0,ascending,2013 +2020-02-01,1182.0,1182.0,1182.0,ascending,2013 +2020-02-13,1182.0,1182.0,1182.0,ascending,2013 +2020-02-25,1182.0,1182.0,1186.0,ascending,2013 +2020-03-08,1182.0,1182.0,1182.0,ascending,2013 +2020-03-20,1182.0,1182.0,1182.0,ascending,2013 +2020-04-01,1182.0,1182.0,1182.0,ascending,2013 +2020-04-13,1182.0,1182.0,1182.0,ascending,2013 +2020-04-25,1521.0,1509.0,1534.0,ascending,2013 +2020-05-07,1953.0,1880.0,2059.0,ascending,2013 +2020-05-19,2230.0,2180.0,2312.0,ascending,2013 +2020-05-31,2371.0,2369.0,2387.0,ascending,2013 +2020-06-12,2387.0,2387.0,2387.0,ascending,2013 +2020-06-24,2387.0,2387.0,2387.0,ascending,2013 +2020-07-06,2371.0,2369.0,2387.0,ascending,2013 +2020-07-18,2361.0,2335.0,2385.0,ascending,2013 +2020-07-30,2335.0,2315.0,2387.0,ascending,2013 +2020-08-11,2370.0,2364.0,2387.0,ascending,2013 +2020-08-23,2360.0,2329.0,2387.0,ascending,2013 +2020-09-04,2387.0,2387.0,2387.0,ascending,2013 +2020-09-16,2356.0,2324.0,2385.0,ascending,2013 +2020-09-28,2318.0,2293.0,2370.0,ascending,2013 +2020-10-10,2270.0,2228.0,2324.0,ascending,2013 +2020-11-03,2197.0,2147.0,2248.0,ascending,2013 +2020-11-15,1757.0,1194.0,2007.0,ascending,2013 +2020-11-27,1628.0,1186.0,1864.0,ascending,2013 +2020-12-09,1450.0,1186.0,1664.0,ascending,2013 +2020-12-21,1253.0,1182.0,1321.0,ascending,2013 +2021-01-02,1182.0,1182.0,1182.0,ascending,2013 +2021-01-14,1182.0,1182.0,1182.0,ascending,2013 +2021-01-26,1187.0,1182.0,1194.0,ascending,2013 +2021-02-07,1182.0,1182.0,1182.0,ascending,2013 +2021-02-19,1182.0,1182.0,1182.0,ascending,2013 +2021-03-03,1182.0,1182.0,1182.0,ascending,2013 +2021-03-15,1182.0,1182.0,1182.0,ascending,2013 +2021-03-27,1182.0,1182.0,1182.0,ascending,2013 +2021-04-08,1182.0,1182.0,1182.0,ascending,2013 +2021-04-20,1955.0,1851.0,2093.0,ascending,2013 +2021-05-02,1664.0,1517.0,1767.0,ascending,2013 +2021-05-14,2060.0,1980.0,2163.0,ascending,2013 +2021-05-26,2203.0,2151.0,2251.0,ascending,2013 +2021-06-07,2361.0,2333.0,2387.0,ascending,2013 +2021-06-19,2371.0,2369.0,2387.0,ascending,2013 +2021-07-01,2361.0,2333.0,2387.0,ascending,2013 +2021-07-13,2370.0,2364.0,2387.0,ascending,2013 +2021-07-25,2324.0,2303.0,2387.0,ascending,2013 +2021-08-06,2361.0,2333.0,2387.0,ascending,2013 +2021-08-18,2387.0,2387.0,2387.0,ascending,2013 +2021-08-30,2387.0,2387.0,2387.0,ascending,2013 +2021-09-11,2361.0,2333.0,2387.0,ascending,2013 +2021-09-23,2368.0,2356.0,2387.0,ascending,2013 +2021-10-05,2310.0,2281.0,2361.0,ascending,2013 +2021-10-17,2281.0,2237.0,2324.0,ascending,2013 +2021-10-29,2224.0,2185.0,2273.0,ascending,2013 +2021-11-10,1745.0,1182.0,1990.0,ascending,2013 +2021-11-22,1673.0,1187.0,1905.0,ascending,2013 +2021-12-04,1565.0,1182.0,1792.0,ascending,2013 +2021-12-16,1368.0,1182.0,1509.0,ascending,2013 +2017-06-14,2392.0,2391.0,2407.0,descending,2013 +2017-06-26,2388.0,2371.0,2407.0,descending,2013 +2017-07-08,2388.0,2371.0,2407.0,descending,2013 +2017-07-20,2371.0,2347.0,2407.0,descending,2013 +2017-08-01,2371.0,2339.0,2392.0,descending,2013 +2017-08-13,2377.0,2354.0,2407.0,descending,2013 +2017-08-25,2407.0,2407.0,2407.0,descending,2013 +2017-09-06,2383.0,2362.0,2407.0,descending,2013 +2017-09-18,2294.0,2250.0,2377.0,descending,2013 +2017-09-30,2236.0,2195.0,2299.0,descending,2013 +2017-10-12,2201.0,2153.0,2247.0,descending,2013 +2017-10-24,1541.0,1186.0,1766.0,descending,2013 +2017-11-05,1204.0,1185.0,1220.0,descending,2013 +2017-11-17,1191.0,1185.0,1199.0,descending,2013 +2017-11-29,1186.0,1185.0,1191.0,descending,2013 +2017-12-11,1185.0,1185.0,1185.0,descending,2013 +2017-12-23,1185.0,1185.0,1185.0,descending,2013 +2018-01-04,1185.0,1185.0,1185.0,descending,2013 +2018-01-16,1185.0,1185.0,1185.0,descending,2013 +2018-01-28,1185.0,1185.0,1185.0,descending,2013 +2018-02-09,1185.0,1185.0,1185.0,descending,2013 +2018-02-21,1185.0,1185.0,1185.0,descending,2013 +2018-03-05,1185.0,1185.0,1185.0,descending,2013 +2018-03-17,1185.0,1185.0,1185.0,descending,2013 +2018-03-29,1185.0,1185.0,1185.0,descending,2013 +2018-04-10,1185.0,1185.0,1185.0,descending,2013 +2018-04-22,1185.0,1185.0,1185.0,descending,2013 +2018-05-04,1185.0,1185.0,1185.0,descending,2013 +2018-05-16,1574.0,1533.0,1608.0,descending,2013 +2018-06-09,2407.0,2407.0,2407.0,descending,2013 +2018-06-21,2407.0,2407.0,2407.0,descending,2013 +2018-07-03,2407.0,2407.0,2407.0,descending,2013 +2018-07-15,2407.0,2407.0,2407.0,descending,2013 +2018-07-27,2371.0,2347.0,2407.0,descending,2013 +2018-08-08,2388.0,2371.0,2407.0,descending,2013 +2018-08-20,2391.0,2383.0,2407.0,descending,2013 +2018-09-01,2407.0,2407.0,2407.0,descending,2013 +2018-09-25,2285.0,2229.0,2407.0,descending,2013 +2018-10-07,2096.0,1963.0,2339.0,descending,2013 +2018-10-19,1998.0,1846.0,2244.0,descending,2013 +2018-10-31,1720.0,1289.0,1926.0,descending,2013 +2018-11-12,1932.0,1849.0,2039.0,descending,2013 +2018-12-06,1220.0,1185.0,1249.0,descending,2013 +2018-12-18,1185.0,1185.0,1186.0,descending,2013 +2018-12-30,1185.0,1185.0,1186.0,descending,2013 +2019-01-11,1185.0,1185.0,1185.0,descending,2013 +2019-01-23,1185.0,1185.0,1185.0,descending,2013 +2019-02-04,1185.0,1185.0,1185.0,descending,2013 +2019-02-16,1185.0,1185.0,1185.0,descending,2013 +2019-02-28,1185.0,1185.0,1185.0,descending,2013 +2019-03-12,1185.0,1185.0,1185.0,descending,2013 +2019-03-24,1185.0,1185.0,1185.0,descending,2013 +2019-04-05,1185.0,1185.0,1185.0,descending,2013 +2019-04-17,1185.0,1185.0,1185.0,descending,2013 +2019-04-29,1185.0,1185.0,1185.0,descending,2013 +2019-08-15,2388.0,2371.0,2407.0,descending,2013 +2019-08-27,2329.0,2309.0,2388.0,descending,2013 +2019-09-08,2391.0,2383.0,2407.0,descending,2013 +2019-09-20,2343.0,2320.0,2392.0,descending,2013 +2019-10-02,2209.0,2155.0,2262.0,descending,2013 +2019-10-14,1644.0,1243.0,1862.0,descending,2013 +2019-10-26,1403.0,1215.0,1567.0,descending,2013 +2019-11-07,1250.0,1199.0,1311.0,descending,2013 +2019-11-19,1207.0,1185.0,1230.0,descending,2013 +2019-12-01,1186.0,1185.0,1191.0,descending,2013 +2019-12-13,1185.0,1185.0,1185.0,descending,2013 +2019-12-25,1185.0,1185.0,1185.0,descending,2013 +2020-01-18,1185.0,1185.0,1186.0,descending,2013 +2020-01-30,1185.0,1185.0,1185.0,descending,2013 +2020-02-11,1185.0,1185.0,1186.0,descending,2013 +2020-02-23,1185.0,1185.0,1185.0,descending,2013 +2020-03-06,1185.0,1185.0,1185.0,descending,2013 +2020-03-18,1185.0,1185.0,1185.0,descending,2013 +2020-03-30,1185.0,1185.0,1185.0,descending,2013 +2020-04-11,1185.0,1185.0,1185.0,descending,2013 +2020-04-23,1530.0,1515.0,1539.0,descending,2013 +2020-05-05,1386.0,1342.0,1415.0,descending,2013 +2020-05-29,2323.0,2298.0,2392.0,descending,2013 +2020-06-10,2392.0,2391.0,2407.0,descending,2013 +2020-06-22,2407.0,2407.0,2407.0,descending,2013 +2020-07-04,2392.0,2391.0,2407.0,descending,2013 +2020-07-16,2407.0,2407.0,2407.0,descending,2013 +2020-08-09,2371.0,2347.0,2407.0,descending,2013 +2020-08-21,2383.0,2362.0,2407.0,descending,2013 +2020-09-02,2407.0,2407.0,2407.0,descending,2013 +2020-09-14,2392.0,2391.0,2407.0,descending,2013 +2020-09-26,2325.0,2303.0,2371.0,descending,2013 +2020-10-08,2304.0,2279.0,2359.0,descending,2013 +2020-10-20,2241.0,2197.0,2307.0,descending,2013 +2020-11-01,2126.0,2080.0,2200.0,descending,2013 +2020-11-13,1514.0,1186.0,1738.0,descending,2013 +2020-11-25,1249.0,1185.0,1311.0,descending,2013 +2020-12-07,1203.0,1185.0,1218.0,descending,2013 +2020-12-19,1185.0,1185.0,1186.0,descending,2013 +2020-12-31,1185.0,1185.0,1186.0,descending,2013 +2021-01-12,1185.0,1185.0,1185.0,descending,2013 +2021-01-24,1185.0,1185.0,1185.0,descending,2013 +2021-02-05,1185.0,1185.0,1185.0,descending,2013 +2021-02-17,1185.0,1185.0,1185.0,descending,2013 +2021-03-01,1185.0,1185.0,1185.0,descending,2013 +2021-03-13,1185.0,1185.0,1185.0,descending,2013 +2021-03-25,1185.0,1185.0,1185.0,descending,2013 +2021-04-06,1185.0,1185.0,1185.0,descending,2013 +2021-04-18,1185.0,1185.0,1185.0,descending,2013 +2021-04-30,1645.0,1526.0,1738.0,descending,2013 +2021-05-12,1814.0,1753.0,1861.0,descending,2013 +2021-05-24,1894.0,1854.0,1926.0,descending,2013 +2021-08-04,2391.0,2383.0,2407.0,descending,2013 +2021-08-28,2407.0,2407.0,2407.0,descending,2013 +2021-09-21,2392.0,2391.0,2407.0,descending,2013 +2021-10-03,2362.0,2343.0,2388.0,descending,2013 +2021-10-15,2292.0,2264.0,2321.0,descending,2013 +2021-10-27,2219.0,2178.0,2270.0,descending,2013 +2021-11-08,1665.0,1185.0,1902.0,descending,2013 +2021-11-20,1499.0,1185.0,1724.0,descending,2013 +2021-12-02,1350.0,1185.0,1479.0,descending,2013 +2021-12-14,1216.0,1186.0,1240.0,descending,2013 +2021-12-26,1191.0,1185.0,1199.0,descending,2013 +2022-01-07,1185.0,1185.0,1185.0,descending,2013 +2022-01-19,1186.0,1185.0,1191.0,descending,2013 +2022-01-31,1185.0,1185.0,1185.0,descending,2013 +2022-02-12,1185.0,1185.0,1185.0,descending,2013 +2022-02-24,1185.0,1185.0,1185.0,descending,2013 +2022-03-08,1185.0,1185.0,1185.0,descending,2013 +2022-03-20,1185.0,1185.0,1185.0,descending,2013 +2022-04-13,1185.0,1185.0,1185.0,descending,2013 +2022-05-07,1485.0,1439.0,1536.0,descending,2013 +2022-06-24,2391.0,2383.0,2407.0,descending,2013 +2022-08-11,2407.0,2407.0,2407.0,descending,2013 +2022-08-23,2377.0,2354.0,2407.0,descending,2013 +2022-09-04,2407.0,2407.0,2407.0,descending,2013 +2022-09-16,2407.0,2407.0,2407.0,descending,2013 +2022-09-28,2371.0,2347.0,2407.0,descending,2013 +2022-10-10,2304.0,2279.0,2371.0,descending,2013 +2022-10-22,2264.0,2221.0,2323.0,descending,2013 +2022-11-03,2236.0,2191.0,2307.0,descending,2013 +2022-11-15,2201.0,2136.0,2278.0,descending,2013 +2022-11-27,2202.0,2152.0,2255.0,descending,2013 +2022-12-09,2194.0,2145.0,2236.0,descending,2013 +2022-12-21,1760.0,1185.0,2011.0,descending,2013 +2023-01-02,1732.0,1186.0,1973.0,descending,2013 +2023-01-14,1637.0,1186.0,1876.0,descending,2013 +2023-01-26,1601.0,1186.0,1830.0,descending,2013 +2023-02-07,1296.0,1186.0,1406.0,descending,2013 +2023-02-19,1192.0,1185.0,1201.0,descending,2013 +2023-03-03,1185.0,1185.0,1185.0,descending,2013 +2023-03-15,1185.0,1185.0,1185.0,descending,2013 +2023-03-27,1185.0,1185.0,1185.0,descending,2013 +2023-04-08,1185.0,1185.0,1185.0,descending,2013 +2023-04-20,1185.0,1185.0,1185.0,descending,2013 +2023-05-02,1185.0,1185.0,1185.0,descending,2013 +2023-06-07,2307.0,2273.0,2392.0,descending,2013 +2023-06-19,2388.0,2371.0,2407.0,descending,2013 +2023-07-01,2407.0,2407.0,2407.0,descending,2013 +2023-07-13,2407.0,2407.0,2407.0,descending,2013 +2023-07-25,2359.0,2327.0,2407.0,descending,2013 +2023-08-06,2377.0,2354.0,2407.0,descending,2013 +2023-08-18,2383.0,2362.0,2407.0,descending,2013 +2023-08-30,2391.0,2383.0,2407.0,descending,2013 +2023-09-11,2392.0,2391.0,2407.0,descending,2013 +2023-09-23,2371.0,2339.0,2392.0,descending,2013 +2023-10-05,2299.0,2267.0,2354.0,descending,2013 +2023-10-17,2383.0,2383.0,2388.0,descending,2013 +2023-10-29,2407.0,2407.0,2407.0,descending,2013 +2023-11-10,2407.0,2407.0,2407.0,descending,2013 +2023-11-22,1510.0,1186.0,1734.0,descending,2013 +2023-12-16,1200.0,1185.0,1213.0,descending,2013 +2023-12-28,1185.0,1185.0,1185.0,descending,2013 +2024-01-21,1185.0,1185.0,1185.0,descending,2013 +2024-02-02,1185.0,1185.0,1185.0,descending,2013 +2024-02-14,1185.0,1185.0,1185.0,descending,2013 +2024-02-26,1185.0,1185.0,1185.0,descending,2013 +2024-03-09,1185.0,1185.0,1185.0,descending,2013 +2024-03-21,1185.0,1185.0,1185.0,descending,2013 +2024-04-02,1186.0,1185.0,1191.0,descending,2013 +2024-04-14,1185.0,1185.0,1185.0,descending,2013 +2024-04-26,1356.0,1289.0,1413.0,descending,2013 +2024-05-20,1979.0,1917.0,2058.0,descending,2013 +2024-06-01,2074.0,2031.0,2121.0,descending,2013 +2024-06-13,2407.0,2407.0,2407.0,descending,2013 +2024-06-25,2392.0,2391.0,2407.0,descending,2013 +2024-07-31,2377.0,2354.0,2407.0,descending,2013 +2024-08-12,2407.0,2407.0,2407.0,descending,2013 +2024-08-24,2383.0,2362.0,2407.0,descending,2013 +2024-09-05,2391.0,2383.0,2407.0,descending,2013 +2024-09-17,2354.0,2325.0,2407.0,descending,2013 +2024-09-29,2235.0,2176.0,2326.0,descending,2013 +2024-10-11,2270.0,2229.0,2309.0,descending,2013 +2024-11-04,2407.0,2407.0,2407.0,descending,2013 +2024-11-16,1685.0,1185.0,1918.0,descending,2013 +2024-11-28,1557.0,1185.0,1785.0,descending,2013 +2024-12-10,1425.0,1191.0,1629.0,descending,2013 +2024-12-22,1224.0,1186.0,1253.0,descending,2013 diff --git a/docs/_static/example_meltextent_and_snowline_1d/sample_snowline_elev.csv b/docs/_static/example_meltextent_and_snowline_1d/sample_snowline_elev.csv new file mode 100644 index 00000000..2a6fb36f --- /dev/null +++ b/docs/_static/example_meltextent_and_snowline_1d/sample_snowline_elev.csv @@ -0,0 +1,336 @@ +date,z,z_min,z_max,direction,ref_dem_year +2016-11-24,1182.0,1182.0,1182.0,ascending,2013 +2016-12-18,1182.0,1182.0,1182.0,ascending,2013 +2017-01-11,1182.0,1182.0,1182.0,ascending,2013 +2017-02-04,1182.0,1182.0,1182.0,ascending,2013 +2017-02-28,1182.0,1182.0,1182.0,ascending,2013 +2017-03-12,1182.0,1182.0,1182.0,ascending,2013 +2017-03-24,1182.0,1182.0,1182.0,ascending,2013 +2017-04-05,1182.0,1182.0,1182.0,ascending,2013 +2017-04-17,1199.0,1196.0,1204.0,ascending,2013 +2017-04-29,1203.0,1186.0,1239.0,ascending,2013 +2017-05-11,1429.0,1347.0,1546.0,ascending,2013 +2017-05-23,1270.0,1182.0,1354.0,ascending,2013 +2017-06-04,1429.0,1404.0,1491.0,ascending,2013 +2017-06-16,1600.0,1546.0,1645.0,ascending,2013 +2017-06-28,1637.0,1593.0,1698.0,ascending,2013 +2017-07-10,1747.0,1694.0,1811.0,ascending,2013 +2017-07-22,1804.0,1736.0,1868.0,ascending,2013 +2017-08-15,1704.0,1652.0,1746.0,ascending,2013 +2017-08-27,1740.0,1706.0,1780.0,ascending,2013 +2017-09-08,1714.0,1672.0,1774.0,ascending,2013 +2017-09-20,1611.0,1534.0,1718.0,ascending,2013 +2017-10-02,1729.0,1657.0,1877.0,ascending,2013 +2017-11-07,1182.0,1182.0,1182.0,ascending,2013 +2017-12-01,1182.0,1182.0,1182.0,ascending,2013 +2017-12-13,1182.0,1182.0,1182.0,ascending,2013 +2018-01-06,1182.0,1182.0,1182.0,ascending,2013 +2018-01-18,1182.0,1182.0,1182.0,ascending,2013 +2018-02-11,1182.0,1182.0,1182.0,ascending,2013 +2018-02-23,1182.0,1182.0,1182.0,ascending,2013 +2018-03-07,1182.0,1182.0,1182.0,ascending,2013 +2018-03-19,1182.0,1182.0,1182.0,ascending,2013 +2018-03-31,1182.0,1182.0,1182.0,ascending,2013 +2018-04-12,1278.0,1275.0,1281.0,ascending,2013 +2018-04-24,1239.0,1232.0,1336.0,ascending,2013 +2018-05-06,1182.0,1182.0,1182.0,ascending,2013 +2018-05-18,1182.0,1182.0,1276.0,ascending,2013 +2018-05-30,1182.0,1182.0,1435.0,ascending,2013 +2018-06-11,1420.0,1389.0,1469.0,ascending,2013 +2018-06-23,1479.0,1440.0,1527.0,ascending,2013 +2018-07-05,1573.0,1556.0,1610.0,ascending,2013 +2018-07-17,1603.0,1582.0,1628.0,ascending,2013 +2018-07-29,1705.0,1652.0,1772.0,ascending,2013 +2018-08-10,1754.0,1705.0,1813.0,ascending,2013 +2018-08-22,1741.0,1713.0,1855.0,ascending,2013 +2018-09-03,1657.0,1614.0,1706.0,ascending,2013 +2018-09-15,1806.0,1745.0,1883.0,ascending,2013 +2018-10-09,1766.0,1746.0,2049.0,ascending,2013 +2018-10-21,1332.0,1312.0,1786.0,ascending,2013 +2018-11-02,1270.0,1270.0,1270.0,ascending,2013 +2018-11-14,1488.0,1476.0,1497.0,ascending,2013 +2018-12-08,1182.0,1182.0,1182.0,ascending,2013 +2018-12-20,1182.0,1182.0,1182.0,ascending,2013 +2019-01-01,1182.0,1182.0,1182.0,ascending,2013 +2019-01-13,1182.0,1182.0,1182.0,ascending,2013 +2019-01-25,1182.0,1182.0,1182.0,ascending,2013 +2019-02-06,1182.0,1182.0,1182.0,ascending,2013 +2019-02-18,1182.0,1182.0,1182.0,ascending,2013 +2019-03-02,1182.0,1182.0,1182.0,ascending,2013 +2019-03-14,1182.0,1182.0,1182.0,ascending,2013 +2019-03-26,1182.0,1182.0,1182.0,ascending,2013 +2019-04-07,1182.0,1182.0,1182.0,ascending,2013 +2019-04-19,1250.0,1250.0,1250.0,ascending,2013 +2019-05-01,1187.0,1182.0,1839.0,ascending,2013 +2019-05-13,1182.0,1182.0,1286.0,ascending,2013 +2019-05-25,1295.0,1265.0,1342.0,ascending,2013 +2019-06-06,1444.0,1419.0,1521.0,ascending,2013 +2019-06-18,1541.0,1510.0,1578.0,ascending,2013 +2019-06-30,1681.0,1648.0,1812.0,ascending,2013 +2019-07-12,1818.0,1785.0,1923.0,ascending,2013 +2019-07-24,1935.0,1878.0,2047.0,ascending,2013 +2019-08-05,1908.0,1850.0,2012.0,ascending,2013 +2019-08-17,1894.0,1841.0,1980.0,ascending,2013 +2019-08-29,1905.0,1840.0,2040.0,ascending,2013 +2019-09-10,1905.0,1859.0,1997.0,ascending,2013 +2019-09-22,1518.0,1509.0,1738.0,ascending,2013 +2019-10-04,1698.0,1673.0,2044.0,ascending,2013 +2019-10-16,1304.0,1304.0,1304.0,ascending,2013 +2019-10-28,1223.0,1223.0,1223.0,ascending,2013 +2019-11-09,1199.0,1199.0,1199.0,ascending,2013 +2019-11-21,1182.0,1182.0,1182.0,ascending,2013 +2019-12-03,1182.0,1182.0,1182.0,ascending,2013 +2019-12-15,1182.0,1182.0,1182.0,ascending,2013 +2019-12-27,1182.0,1182.0,1182.0,ascending,2013 +2020-01-08,1182.0,1182.0,1182.0,ascending,2013 +2020-01-20,1182.0,1182.0,1182.0,ascending,2013 +2020-02-01,1182.0,1182.0,1182.0,ascending,2013 +2020-02-13,1182.0,1182.0,1182.0,ascending,2013 +2020-02-25,1182.0,1182.0,1182.0,ascending,2013 +2020-03-08,1182.0,1182.0,1182.0,ascending,2013 +2020-03-20,1182.0,1182.0,1182.0,ascending,2013 +2020-04-01,1182.0,1182.0,1182.0,ascending,2013 +2020-04-13,1182.0,1182.0,1182.0,ascending,2013 +2020-04-25,1182.0,1182.0,1295.0,ascending,2013 +2020-05-07,1182.0,1182.0,1718.0,ascending,2013 +2020-05-19,1265.0,1193.0,1628.0,ascending,2013 +2020-05-31,1396.0,1358.0,1430.0,ascending,2013 +2020-06-12,1457.0,1429.0,1535.0,ascending,2013 +2020-06-24,1334.0,1314.0,1356.0,ascending,2013 +2020-07-06,1602.0,1544.0,1702.0,ascending,2013 +2020-07-18,1608.0,1594.0,1773.0,ascending,2013 +2020-07-30,1721.0,1665.0,1868.0,ascending,2013 +2020-08-11,1706.0,1684.0,1840.0,ascending,2013 +2020-08-23,1884.0,1809.0,1990.0,ascending,2013 +2020-09-04,1583.0,1570.0,1664.0,ascending,2013 +2020-09-16,1731.0,1700.0,1906.0,ascending,2013 +2020-09-28,1724.0,1661.0,1945.0,ascending,2013 +2020-10-10,1730.0,1716.0,2014.0,ascending,2013 +2020-11-03,1846.0,1846.0,2136.0,ascending,2013 +2020-11-15,1193.0,1193.0,1193.0,ascending,2013 +2020-11-27,1182.0,1182.0,1182.0,ascending,2013 +2020-12-09,1182.0,1182.0,1182.0,ascending,2013 +2020-12-21,1182.0,1182.0,1182.0,ascending,2013 +2021-01-02,1182.0,1182.0,1182.0,ascending,2013 +2021-01-14,1182.0,1182.0,1182.0,ascending,2013 +2021-01-26,1182.0,1182.0,1182.0,ascending,2013 +2021-02-07,1182.0,1182.0,1182.0,ascending,2013 +2021-02-19,1182.0,1182.0,1182.0,ascending,2013 +2021-03-03,1182.0,1182.0,1182.0,ascending,2013 +2021-03-15,1182.0,1182.0,1182.0,ascending,2013 +2021-03-27,1182.0,1182.0,1182.0,ascending,2013 +2021-04-08,1182.0,1182.0,1182.0,ascending,2013 +2021-04-20,1182.0,1182.0,1698.0,ascending,2013 +2021-05-02,1186.0,1182.0,1314.0,ascending,2013 +2021-05-14,1219.0,1196.0,1588.0,ascending,2013 +2021-05-26,1344.0,1296.0,1603.0,ascending,2013 +2021-06-07,1414.0,1381.0,1485.0,ascending,2013 +2021-06-19,1524.0,1508.0,1554.0,ascending,2013 +2021-07-01,1625.0,1604.0,1671.0,ascending,2013 +2021-07-13,1653.0,1638.0,1687.0,ascending,2013 +2021-07-25,1720.0,1694.0,1798.0,ascending,2013 +2021-08-06,1819.0,1760.0,1885.0,ascending,2013 +2021-08-18,1559.0,1535.0,1576.0,ascending,2013 +2021-08-30,1729.0,1672.0,1807.0,ascending,2013 +2021-09-11,1761.0,1724.0,1795.0,ascending,2013 +2021-09-23,1868.0,1826.0,1924.0,ascending,2013 +2021-10-05,1840.0,1828.0,1987.0,ascending,2013 +2021-10-17,1987.0,1941.0,2137.0,ascending,2013 +2021-10-29,1881.0,1878.0,2114.0,ascending,2013 +2021-11-10,1182.0,1182.0,1182.0,ascending,2013 +2021-11-22,1186.0,1186.0,1186.0,ascending,2013 +2021-12-04,1182.0,1182.0,1182.0,ascending,2013 +2021-12-16,1182.0,1182.0,1182.0,ascending,2013 +2017-06-14,1715.0,1671.0,1929.0,descending,2013 +2017-06-26,1878.0,1805.0,1951.0,descending,2013 +2017-07-08,1885.0,1859.0,2059.0,descending,2013 +2017-07-20,1894.0,1872.0,2049.0,descending,2013 +2017-08-01,1895.0,1878.0,2090.0,descending,2013 +2017-08-13,1862.0,1861.0,2136.0,descending,2013 +2017-08-25,1869.0,1836.0,1906.0,descending,2013 +2017-09-06,1931.0,1919.0,2151.0,descending,2013 +2017-09-18,1889.0,1857.0,2153.0,descending,2013 +2017-09-30,1720.0,1698.0,2115.0,descending,2013 +2017-10-12,1898.0,1894.0,2151.0,descending,2013 +2017-10-24,1185.0,1185.0,1185.0,descending,2013 +2017-11-05,1185.0,1185.0,1185.0,descending,2013 +2017-11-17,1185.0,1185.0,1185.0,descending,2013 +2017-11-29,1185.0,1185.0,1185.0,descending,2013 +2017-12-11,1185.0,1185.0,1185.0,descending,2013 +2017-12-23,1185.0,1185.0,1185.0,descending,2013 +2018-01-04,1185.0,1185.0,1185.0,descending,2013 +2018-01-16,1185.0,1185.0,1185.0,descending,2013 +2018-01-28,1185.0,1185.0,1185.0,descending,2013 +2018-02-09,1185.0,1185.0,1185.0,descending,2013 +2018-02-21,1185.0,1185.0,1185.0,descending,2013 +2018-03-05,1185.0,1185.0,1185.0,descending,2013 +2018-03-17,1185.0,1185.0,1185.0,descending,2013 +2018-03-29,1185.0,1185.0,1185.0,descending,2013 +2018-04-10,1185.0,1185.0,1185.0,descending,2013 +2018-04-22,1185.0,1185.0,1185.0,descending,2013 +2018-05-04,1185.0,1185.0,1185.0,descending,2013 +2018-05-16,1185.0,1185.0,1230.0,descending,2013 +2018-06-09,1299.0,1282.0,1345.0,descending,2013 +2018-06-21,1391.0,1356.0,1416.0,descending,2013 +2018-07-03,1567.0,1542.0,1596.0,descending,2013 +2018-07-15,1603.0,1575.0,1629.0,descending,2013 +2018-07-27,1788.0,1705.0,1872.0,descending,2013 +2018-08-08,1782.0,1738.0,1855.0,descending,2013 +2018-08-20,1859.0,1809.0,1926.0,descending,2013 +2018-09-01,1727.0,1671.0,1805.0,descending,2013 +2018-09-25,1653.0,1264.0,2003.0,descending,2013 +2018-10-07,1813.0,1682.0,1929.0,descending,2013 +2018-10-19,1248.0,1244.0,1708.0,descending,2013 +2018-10-31,1288.0,1288.0,1288.0,descending,2013 +2018-11-12,1685.0,1425.0,1845.0,descending,2013 +2018-12-06,1185.0,1185.0,1185.0,descending,2013 +2018-12-18,1185.0,1185.0,1185.0,descending,2013 +2018-12-30,1185.0,1185.0,1185.0,descending,2013 +2019-01-11,1185.0,1185.0,1185.0,descending,2013 +2019-01-23,1185.0,1185.0,1185.0,descending,2013 +2019-02-04,1185.0,1185.0,1185.0,descending,2013 +2019-02-16,1185.0,1185.0,1185.0,descending,2013 +2019-02-28,1185.0,1185.0,1185.0,descending,2013 +2019-03-12,1185.0,1185.0,1185.0,descending,2013 +2019-03-24,1185.0,1185.0,1185.0,descending,2013 +2019-04-05,1185.0,1185.0,1185.0,descending,2013 +2019-04-17,1185.0,1185.0,1185.0,descending,2013 +2019-04-29,1185.0,1185.0,1185.0,descending,2013 +2019-08-15,1908.0,1864.0,1996.0,descending,2013 +2019-08-27,1887.0,1857.0,2115.0,descending,2013 +2019-09-08,1916.0,1877.0,2043.0,descending,2013 +2019-09-20,1487.0,1466.0,1780.0,descending,2013 +2019-10-02,1715.0,1687.0,2077.0,descending,2013 +2019-10-14,1240.0,1240.0,1240.0,descending,2013 +2019-10-26,1213.0,1213.0,1213.0,descending,2013 +2019-11-07,1193.0,1193.0,1193.0,descending,2013 +2019-11-19,1185.0,1185.0,1185.0,descending,2013 +2019-12-01,1185.0,1185.0,1185.0,descending,2013 +2019-12-13,1185.0,1185.0,1185.0,descending,2013 +2019-12-25,1185.0,1185.0,1185.0,descending,2013 +2020-01-18,1185.0,1185.0,1185.0,descending,2013 +2020-01-30,1185.0,1185.0,1185.0,descending,2013 +2020-02-11,1185.0,1185.0,1185.0,descending,2013 +2020-02-23,1185.0,1185.0,1185.0,descending,2013 +2020-03-06,1185.0,1185.0,1185.0,descending,2013 +2020-03-18,1185.0,1185.0,1185.0,descending,2013 +2020-03-30,1185.0,1185.0,1185.0,descending,2013 +2020-04-11,1185.0,1185.0,1185.0,descending,2013 +2020-04-23,1185.0,1185.0,1238.0,descending,2013 +2020-05-05,1191.0,1185.0,1261.0,descending,2013 +2020-05-29,1255.0,1231.0,1425.0,descending,2013 +2020-06-10,1378.0,1345.0,1439.0,descending,2013 +2020-06-22,1269.0,1249.0,1290.0,descending,2013 +2020-07-04,1568.0,1517.0,1607.0,descending,2013 +2020-07-16,1641.0,1603.0,1804.0,descending,2013 +2020-08-09,1788.0,1725.0,1866.0,descending,2013 +2020-08-21,1851.0,1791.0,1926.0,descending,2013 +2020-09-02,1575.0,1567.0,1709.0,descending,2013 +2020-09-14,1729.0,1717.0,1949.0,descending,2013 +2020-09-26,1639.0,1552.0,1998.0,descending,2013 +2020-10-08,1773.0,1738.0,2104.0,descending,2013 +2020-10-20,1904.0,1893.0,2176.0,descending,2013 +2020-11-01,1905.0,1905.0,2077.0,descending,2013 +2020-11-13,1185.0,1185.0,1185.0,descending,2013 +2020-11-25,1185.0,1185.0,1185.0,descending,2013 +2020-12-07,1185.0,1185.0,1185.0,descending,2013 +2020-12-19,1185.0,1185.0,1185.0,descending,2013 +2020-12-31,1185.0,1185.0,1185.0,descending,2013 +2021-01-12,1185.0,1185.0,1185.0,descending,2013 +2021-01-24,1185.0,1185.0,1185.0,descending,2013 +2021-02-05,1185.0,1185.0,1185.0,descending,2013 +2021-02-17,1185.0,1185.0,1185.0,descending,2013 +2021-03-01,1185.0,1185.0,1185.0,descending,2013 +2021-03-13,1185.0,1185.0,1185.0,descending,2013 +2021-03-25,1185.0,1185.0,1185.0,descending,2013 +2021-04-06,1185.0,1185.0,1185.0,descending,2013 +2021-04-18,1185.0,1185.0,1185.0,descending,2013 +2021-04-30,1185.0,1185.0,1304.0,descending,2013 +2021-05-12,1189.0,1185.0,1403.0,descending,2013 +2021-05-24,1339.0,1264.0,1558.0,descending,2013 +2021-08-04,1821.0,1774.0,1903.0,descending,2013 +2021-08-28,1722.0,1695.0,1753.0,descending,2013 +2021-09-21,1848.0,1812.0,1930.0,descending,2013 +2021-10-03,1897.0,1872.0,2077.0,descending,2013 +2021-10-15,1987.0,1957.0,2176.0,descending,2013 +2021-10-27,1986.0,1975.0,2141.0,descending,2013 +2021-11-08,1185.0,1185.0,1185.0,descending,2013 +2021-11-20,1185.0,1185.0,1185.0,descending,2013 +2021-12-02,1185.0,1185.0,1185.0,descending,2013 +2021-12-14,1185.0,1185.0,1185.0,descending,2013 +2021-12-26,1185.0,1185.0,1185.0,descending,2013 +2022-01-07,1185.0,1185.0,1185.0,descending,2013 +2022-01-19,1185.0,1185.0,1185.0,descending,2013 +2022-01-31,1185.0,1185.0,1185.0,descending,2013 +2022-02-12,1185.0,1185.0,1185.0,descending,2013 +2022-02-24,1185.0,1185.0,1185.0,descending,2013 +2022-03-08,1185.0,1185.0,1185.0,descending,2013 +2022-03-20,1185.0,1185.0,1185.0,descending,2013 +2022-04-13,1185.0,1185.0,1185.0,descending,2013 +2022-05-07,1185.0,1185.0,1255.0,descending,2013 +2022-06-24,1671.0,1613.0,1739.0,descending,2013 +2022-08-11,1552.0,1542.0,1565.0,descending,2013 +2022-08-23,1834.0,1800.0,1888.0,descending,2013 +2022-09-04,1644.0,1629.0,1660.0,descending,2013 +2022-09-16,1320.0,1306.0,1556.0,descending,2013 +2022-09-28,1426.0,1398.0,1682.0,descending,2013 +2022-10-10,1207.0,1203.0,1794.0,descending,2013 +2022-10-22,1883.0,1840.0,2138.0,descending,2013 +2022-11-03,1775.0,1756.0,2109.0,descending,2013 +2022-11-15,1892.0,1880.0,2108.0,descending,2013 +2022-11-27,1900.0,1894.0,2131.0,descending,2013 +2022-12-09,1897.0,1896.0,2121.0,descending,2013 +2022-12-21,1185.0,1185.0,1185.0,descending,2013 +2023-01-02,1185.0,1185.0,1185.0,descending,2013 +2023-01-14,1185.0,1185.0,1185.0,descending,2013 +2023-01-26,1185.0,1185.0,1185.0,descending,2013 +2023-02-07,1185.0,1185.0,1185.0,descending,2013 +2023-02-19,1185.0,1185.0,1185.0,descending,2013 +2023-03-03,1185.0,1185.0,1185.0,descending,2013 +2023-03-15,1185.0,1185.0,1185.0,descending,2013 +2023-03-27,1185.0,1185.0,1185.0,descending,2013 +2023-04-08,1185.0,1185.0,1185.0,descending,2013 +2023-04-20,1185.0,1185.0,1185.0,descending,2013 +2023-05-02,1185.0,1185.0,1185.0,descending,2013 +2023-06-07,1207.0,1193.0,1473.0,descending,2013 +2023-06-19,1330.0,1299.0,1364.0,descending,2013 +2023-07-01,1481.0,1459.0,1510.0,descending,2013 +2023-07-13,1659.0,1608.0,1738.0,descending,2013 +2023-07-25,1796.0,1717.0,1891.0,descending,2013 +2023-08-06,1852.0,1795.0,1932.0,descending,2013 +2023-08-18,1813.0,1777.0,1898.0,descending,2013 +2023-08-30,1907.0,1852.0,2022.0,descending,2013 +2023-09-11,1613.0,1561.0,1796.0,descending,2013 +2023-09-23,1677.0,1645.0,1945.0,descending,2013 +2023-10-05,1859.0,1840.0,2096.0,descending,2013 +2023-10-17,1898.0,1883.0,2240.0,descending,2013 +2023-10-29,1963.0,1941.0,2377.0,descending,2013 +2023-11-10,1918.0,1913.0,2354.0,descending,2013 +2023-11-22,1185.0,1185.0,1185.0,descending,2013 +2023-12-16,1185.0,1185.0,1185.0,descending,2013 +2023-12-28,1185.0,1185.0,1185.0,descending,2013 +2024-01-21,1185.0,1185.0,1185.0,descending,2013 +2024-02-02,1185.0,1185.0,1185.0,descending,2013 +2024-02-14,1185.0,1185.0,1185.0,descending,2013 +2024-02-26,1185.0,1185.0,1185.0,descending,2013 +2024-03-09,1185.0,1185.0,1185.0,descending,2013 +2024-03-21,1185.0,1185.0,1185.0,descending,2013 +2024-04-02,1185.0,1185.0,1185.0,descending,2013 +2024-04-14,1185.0,1185.0,1185.0,descending,2013 +2024-04-26,1267.0,1249.0,1286.0,descending,2013 +2024-05-20,1429.0,1189.0,1734.0,descending,2013 +2024-06-01,1248.0,1224.0,1517.0,descending,2013 +2024-06-13,1387.0,1347.0,1414.0,descending,2013 +2024-06-25,1615.0,1567.0,1691.0,descending,2013 +2024-07-31,1805.0,1745.0,1861.0,descending,2013 +2024-08-12,1489.0,1466.0,1567.0,descending,2013 +2024-08-24,1650.0,1589.0,1769.0,descending,2013 +2024-09-05,1623.0,1606.0,1809.0,descending,2013 +2024-09-17,1332.0,1248.0,1593.0,descending,2013 +2024-09-29,1685.0,1618.0,1857.0,descending,2013 +2024-10-11,1729.0,1716.0,1957.0,descending,2013 +2024-11-04,1897.0,1888.0,2282.0,descending,2013 +2024-11-16,1185.0,1185.0,1185.0,descending,2013 +2024-11-28,1185.0,1185.0,1185.0,descending,2013 +2024-12-10,1189.0,1189.0,1189.0,descending,2013 +2024-12-22,1185.0,1185.0,1185.0,descending,2013 diff --git a/pygem/bin/run/run_calibration.py b/pygem/bin/run/run_calibration.py index be0f01ed..a1b610ea 100755 --- a/pygem/bin/run/run_calibration.py +++ b/pygem/bin/run/run_calibration.py @@ -636,9 +636,9 @@ def run(list_packed_vars): # Tidewater glaciers - use climatic mass balance since calving_k already calibrated separately if gdir.is_tidewater: - assert 'mb_clim_mwea' in gdir.mbdata.keys(), ( - 'include_frontalablation is set as true, but fontal ablation has yet to be calibrated.' - ) + assert ( + 'mb_clim_mwea' in gdir.mbdata.keys() + ), 'include_frontalablation is set as true, but fontal ablation has yet to be calibrated.' mb_obs_mwea = gdir.mbdata['mb_clim_mwea'] mb_obs_mwea_err = gdir.mbdata['mb_clim_mwea_err'] # non-tidewater - use geodetic mass balance @@ -702,9 +702,9 @@ def run(list_packed_vars): fls = None if debug: - assert os.path.exists(mbdata_fn), ( - 'Mass balance data missing. Check dataset and column names' - ) + assert os.path.exists( + mbdata_fn + ), 'Mass balance data missing. Check dataset and column names' # if spinup, grab appropriate flowlines if args.spinup: @@ -1207,9 +1207,9 @@ def single_param_optimizer( Computationally more robust and sometimes faster than scipy minimize """ - assert prm2opt is not None, ( - 'For single_param_optimizer you must specify parameter to optimize' - ) + assert ( + prm2opt is not None + ), 'For single_param_optimizer you must specify parameter to optimize' if prm2opt == 'kp': prm_bndlow = kp_bnds[0] @@ -1707,9 +1707,9 @@ def single_param_optimizer( + glacier_str.split('.')[0].zfill(2) + '/' ) - assert os.path.exists(em_mod_fp + em_mod_fn), ( - f'emulator output does not exist : {em_mod_fp + em_mod_fn}' - ) + assert os.path.exists( + em_mod_fp + em_mod_fn + ), f'emulator output does not exist : {em_mod_fp + em_mod_fn}' mbEmulator = massbalEmulator.load(em_mod_path=em_mod_fp + em_mod_fn) outpath_sfix = '' # output file path suffix if using emulator else: @@ -2039,18 +2039,23 @@ def must_melt(kp, tbias, ddfsnow, **kwargs): sampler = mcmc.Metropolis(mb.means, mb.stds) # draw samples - m_chain_z, pred_chain, m_primes_z, pred_primes, _, ar = ( - sampler.sample( - initial_guesses_z, - mb.log_posterior, - n_samples=args.chain_length, - h=pygem_prms['calib']['MCMC_params']['mcmc_step'], - burnin=int(args.burn_pct / 100 * args.chain_length), - thin_factor=pygem_prms['calib']['MCMC_params'][ - 'thin_interval' - ], - progress_bar=args.progress_bar, - ) + ( + m_chain_z, + pred_chain, + m_primes_z, + pred_primes, + _, + ar, + ) = sampler.sample( + initial_guesses_z, + mb.log_posterior, + n_samples=args.chain_length, + h=pygem_prms['calib']['MCMC_params']['mcmc_step'], + burnin=int(args.burn_pct / 100 * args.chain_length), + thin_factor=pygem_prms['calib']['MCMC_params'][ + 'thin_interval' + ], + progress_bar=args.progress_bar, ) # Check condition at the end @@ -2326,9 +2331,9 @@ def single_param_optimizer( mb_mwea_threshold=0.005, debug=False, ): - assert prm2opt is not None, ( - 'For single_param_optimizer you must specify parameter to optimize' - ) + assert ( + prm2opt is not None + ), 'For single_param_optimizer you must specify parameter to optimize' if prm2opt == 'kp': prm_bndlow = kp_bnds[0] diff --git a/pygem/bin/run/run_simulation.py b/pygem/bin/run/run_simulation.py index 0f365d7c..6a7ed25b 100755 --- a/pygem/bin/run/run_simulation.py +++ b/pygem/bin/run/run_simulation.py @@ -480,16 +480,18 @@ def run(list_packed_vars): args.ref_startyear, ) # Precipitation bias correction - gcm_prec_adj, gcm_elev_adj, gcm_prec_biasadj_frac = ( - gcmbiasadj.prec_biasadj_opt1( - ref_prec, - ref_elev, - gcm_prec, - dates_table_ref, - dates_table_full, - args.sim_startyear, - args.ref_startyear, - ) + ( + gcm_prec_adj, + gcm_elev_adj, + gcm_prec_biasadj_frac, + ) = gcmbiasadj.prec_biasadj_opt1( + ref_prec, + ref_elev, + gcm_prec, + dates_table_ref, + dates_table_full, + args.sim_startyear, + args.ref_startyear, ) # OPTION 2: Adjust temp and prec using Huss and Hock (2015) elif args.option_bias_adjustment == 2: @@ -504,14 +506,16 @@ def run(list_packed_vars): args.ref_startyear, ) # Precipitation bias correction - gcm_prec_adj, gcm_elev_adj, gcm_prec_biasadj_frac = ( - gcmbiasadj.prec_biasadj_HH2015( - ref_prec, - ref_elev, - gcm_prec, - dates_table_ref, - dates_table_full, - ) + ( + gcm_prec_adj, + gcm_elev_adj, + gcm_prec_biasadj_frac, + ) = gcmbiasadj.prec_biasadj_HH2015( + ref_prec, + ref_elev, + gcm_prec, + dates_table_ref, + dates_table_full, ) # OPTION 3: Adjust temp and prec using quantile delta mapping, Cannon et al. (2015) elif args.option_bias_adjustment == 3: @@ -567,7 +571,7 @@ def run(list_packed_vars): # Lapse rate if sim_climate_name in ['ERA-Interim', 'ERA5']: gcm_lr, gcm_dates = gcm.importGCMvarnearestneighbor_xarray( - gcm.lr_fn, gcm.lr_vn, main_glac_rgi, dates_table + gcm.lr_fn, gcm.lr_vn, main_glac_rgi, dates_table, upscale_var_timestep=True ) ref_lr = gcm_lr else: @@ -591,12 +595,9 @@ def run(list_packed_vars): else: nsims = 1 - # Number of years (for OGGM's run_until_and_store) - if pygem_prms['time']['timestep'] == 'monthly': - nyears = int(dates_table.shape[0] / 12) - nyears_ref = int(dates_table_ref.shape[0] / 12) - else: - assert True == False, 'Adjust nyears for non-monthly timestep' + # Number of years + nyears = dates_table.year.unique()[-1] - dates_table.year.unique()[0] + 1 + nyears_ref = dates_table_ref.year.unique()[-1] - dates_table.year.unique()[0] + 1 for glac in range(main_glac_rgi.shape[0]): if glac == 0: @@ -676,9 +677,9 @@ def run(list_packed_vars): + '/' ) + modelprms_fn - assert os.path.exists(modelprms_fp), ( - 'Calibrated parameters do not exist.' - ) + assert os.path.exists( + modelprms_fp + ), 'Calibrated parameters do not exist.' with open(modelprms_fp, 'r') as f: modelprms_dict = json.load(f) @@ -745,9 +746,9 @@ def run(list_packed_vars): else: # Load quality controlled frontal ablation data fp = f'{pygem_prms["root"]}/{pygem_prms["calib"]["data"]["frontalablation"]["frontalablation_relpath"]}/analysis/{pygem_prms["calib"]["data"]["frontalablation"]["frontalablation_cal_fn"]}' - assert os.path.exists(fp), ( - 'Calibrated calving dataset does not exist' - ) + assert os.path.exists( + fp + ), 'Calibrated calving dataset does not exist' calving_df = pd.read_csv(fp) calving_rgiids = list(calving_df.RGIId) @@ -1108,9 +1109,9 @@ def run(list_packed_vars): try: diag = ev_model.run_until_and_store(args.sim_endyear + 1) - ev_model.mb_model.glac_wide_volume_annual[-1] = ( - diag.volume_m3[-1] - ) + ev_model.mb_model.glac_wide_volume_annual[ + -1 + ] = diag.volume_m3[-1] ev_model.mb_model.glac_wide_area_annual[-1] = diag.area_m2[ -1 ] @@ -1453,9 +1454,9 @@ def run(list_packed_vars): ).sum() / mbmod.glacier_area_initial.sum() mb_all.append(glac_wide_mb_mwea) mbmod.glac_wide_area_annual[-1] = mbmod.glac_wide_area_annual[0] - mbmod.glac_wide_volume_annual[-1] = ( - mbmod.glac_wide_volume_annual[0] - ) + mbmod.glac_wide_volume_annual[ + -1 + ] = mbmod.glac_wide_volume_annual[0] diag['area_m2'] = mbmod.glac_wide_area_annual diag['volume_m3'] = mbmod.glac_wide_volume_annual diag['volume_bsl_m3'] = 0 @@ -1533,20 +1534,20 @@ def run(list_packed_vars): output_glac_temp_monthly[:, n_iter] = mbmod.glac_wide_temp output_glac_prec_monthly[:, n_iter] = mbmod.glac_wide_prec output_glac_acc_monthly[:, n_iter] = mbmod.glac_wide_acc - output_glac_refreeze_monthly[:, n_iter] = ( - mbmod.glac_wide_refreeze - ) + output_glac_refreeze_monthly[ + :, n_iter + ] = mbmod.glac_wide_refreeze output_glac_melt_monthly[:, n_iter] = mbmod.glac_wide_melt - output_glac_frontalablation_monthly[:, n_iter] = ( - mbmod.glac_wide_frontalablation - ) - output_glac_massbaltotal_monthly[:, n_iter] = ( - mbmod.glac_wide_massbaltotal - ) + output_glac_frontalablation_monthly[ + :, n_iter + ] = mbmod.glac_wide_frontalablation + output_glac_massbaltotal_monthly[ + :, n_iter + ] = mbmod.glac_wide_massbaltotal output_glac_runoff_monthly[:, n_iter] = mbmod.glac_wide_runoff - output_glac_snowline_monthly[:, n_iter] = ( - mbmod.glac_wide_snowline - ) + output_glac_snowline_monthly[ + :, n_iter + ] = mbmod.glac_wide_snowline output_glac_area_annual[:, n_iter] = diag.area_m2.values output_glac_mass_annual[:, n_iter] = ( diag.volume_m3.values @@ -1563,16 +1564,16 @@ def run(list_packed_vars): output_glac_ELA_annual[:, n_iter] = mbmod.glac_wide_ELA_annual output_offglac_prec_monthly[:, n_iter] = mbmod.offglac_wide_prec - output_offglac_refreeze_monthly[:, n_iter] = ( - mbmod.offglac_wide_refreeze - ) + output_offglac_refreeze_monthly[ + :, n_iter + ] = mbmod.offglac_wide_refreeze output_offglac_melt_monthly[:, n_iter] = mbmod.offglac_wide_melt - output_offglac_snowpack_monthly[:, n_iter] = ( - mbmod.offglac_wide_snowpack - ) - output_offglac_runoff_monthly[:, n_iter] = ( - mbmod.offglac_wide_runoff - ) + output_offglac_snowpack_monthly[ + :, n_iter + ] = mbmod.offglac_wide_snowpack + output_offglac_runoff_monthly[ + :, n_iter + ] = mbmod.offglac_wide_runoff if output_glac_bin_icethickness_annual is None: output_glac_bin_area_annual_sim = ( @@ -1602,9 +1603,9 @@ def run(list_packed_vars): fl_section[fl_widths_m > 0] / fl_widths_m[fl_widths_m > 0] ) - output_glac_bin_icethickness_annual_sim[:, -1, 0] = ( - icethickness_t0 - ) + output_glac_bin_icethickness_annual_sim[ + :, -1, 0 + ] = icethickness_t0 # mass glacier_vol_t0 = ( fl_widths_m * fl_dx_meter * icethickness_t0 @@ -1625,9 +1626,9 @@ def run(list_packed_vars): output_glac_bin_massbalclim_annual_sim = np.zeros( mbmod.glac_bin_icethickness_annual.shape ) - output_glac_bin_massbalclim_annual_sim[:, :-1] = ( - mbmod.glac_bin_massbalclim_annual - ) + output_glac_bin_massbalclim_annual_sim[ + :, :-1 + ] = mbmod.glac_bin_massbalclim_annual output_glac_bin_massbalclim_annual = ( output_glac_bin_massbalclim_annual_sim[:, :, np.newaxis] ) @@ -1697,9 +1698,9 @@ def run(list_packed_vars): fl_section[fl_widths_m > 0] / fl_widths_m[fl_widths_m > 0] ) - output_glac_bin_icethickness_annual_sim[:, -1, 0] = ( - icethickness_t0 - ) + output_glac_bin_icethickness_annual_sim[ + :, -1, 0 + ] = icethickness_t0 # mass glacier_vol_t0 = ( fl_widths_m * fl_dx_meter * icethickness_t0 @@ -1726,9 +1727,9 @@ def run(list_packed_vars): output_glac_bin_massbalclim_annual_sim = np.zeros( mbmod.glac_bin_icethickness_annual.shape ) - output_glac_bin_massbalclim_annual_sim[:, :-1] = ( - mbmod.glac_bin_massbalclim_annual - ) + output_glac_bin_massbalclim_annual_sim[ + :, :-1 + ] = mbmod.glac_bin_massbalclim_annual output_glac_bin_massbalclim_annual = np.append( output_glac_bin_massbalclim_annual, output_glac_bin_massbalclim_annual_sim[ @@ -1816,21 +1817,21 @@ def run(list_packed_vars): output_stats.create_xr_ds() output_ds_all_stats = output_stats.get_xr_ds() # fill values - output_ds_all_stats['glac_runoff_monthly'].values[0, :] = ( - output_glac_runoff_monthly[:, n_iter] - ) - output_ds_all_stats['glac_area_annual'].values[0, :] = ( - output_glac_area_annual[:, n_iter] - ) - output_ds_all_stats['glac_mass_annual'].values[0, :] = ( - output_glac_mass_annual[:, n_iter] - ) - output_ds_all_stats['glac_mass_bsl_annual'].values[0, :] = ( - output_glac_mass_bsl_annual[:, n_iter] - ) - output_ds_all_stats['glac_ELA_annual'].values[0, :] = ( - output_glac_ELA_annual[:, n_iter] - ) + output_ds_all_stats['glac_runoff_monthly'].values[ + 0, : + ] = output_glac_runoff_monthly[:, n_iter] + output_ds_all_stats['glac_area_annual'].values[ + 0, : + ] = output_glac_area_annual[:, n_iter] + output_ds_all_stats['glac_mass_annual'].values[ + 0, : + ] = output_glac_mass_annual[:, n_iter] + output_ds_all_stats['glac_mass_bsl_annual'].values[ + 0, : + ] = output_glac_mass_bsl_annual[:, n_iter] + output_ds_all_stats['glac_ELA_annual'].values[ + 0, : + ] = output_glac_ELA_annual[:, n_iter] output_ds_all_stats['offglac_runoff_monthly'].values[ 0, : ] = output_offglac_runoff_monthly[:, n_iter] @@ -1841,9 +1842,9 @@ def run(list_packed_vars): output_ds_all_stats['glac_prec_monthly'].values[ 0, : ] = output_glac_prec_monthly[:, n_iter] - output_ds_all_stats['glac_acc_monthly'].values[0, :] = ( - output_glac_acc_monthly[:, n_iter] - ) + output_ds_all_stats['glac_acc_monthly'].values[ + 0, : + ] = output_glac_acc_monthly[:, n_iter] output_ds_all_stats['glac_refreeze_monthly'].values[ 0, : ] = output_glac_refreeze_monthly[:, n_iter] @@ -1969,82 +1970,82 @@ def run(list_packed_vars): ) # output mean/median from all simulations - output_ds_all_stats['glac_runoff_monthly'].values[0, :] = ( - output_glac_runoff_monthly_stats[:, 0] - ) - output_ds_all_stats['glac_area_annual'].values[0, :] = ( - output_glac_area_annual_stats[:, 0] - ) - output_ds_all_stats['glac_mass_annual'].values[0, :] = ( - output_glac_mass_annual_stats[:, 0] - ) - output_ds_all_stats['glac_mass_bsl_annual'].values[0, :] = ( - output_glac_mass_bsl_annual_stats[:, 0] - ) - output_ds_all_stats['glac_ELA_annual'].values[0, :] = ( - output_glac_ELA_annual_stats[:, 0] - ) - output_ds_all_stats['offglac_runoff_monthly'].values[0, :] = ( - output_offglac_runoff_monthly_stats[:, 0] - ) + output_ds_all_stats['glac_runoff_monthly'].values[ + 0, : + ] = output_glac_runoff_monthly_stats[:, 0] + output_ds_all_stats['glac_area_annual'].values[ + 0, : + ] = output_glac_area_annual_stats[:, 0] + output_ds_all_stats['glac_mass_annual'].values[ + 0, : + ] = output_glac_mass_annual_stats[:, 0] + output_ds_all_stats['glac_mass_bsl_annual'].values[ + 0, : + ] = output_glac_mass_bsl_annual_stats[:, 0] + output_ds_all_stats['glac_ELA_annual'].values[ + 0, : + ] = output_glac_ELA_annual_stats[:, 0] + output_ds_all_stats['offglac_runoff_monthly'].values[ + 0, : + ] = output_offglac_runoff_monthly_stats[:, 0] if args.export_extra_vars: output_ds_all_stats['glac_temp_monthly'].values[0, :] = ( output_glac_temp_monthly_stats[:, 0] + 273.15 ) - output_ds_all_stats['glac_prec_monthly'].values[0, :] = ( - output_glac_prec_monthly_stats[:, 0] - ) - output_ds_all_stats['glac_acc_monthly'].values[0, :] = ( - output_glac_acc_monthly_stats[:, 0] - ) - output_ds_all_stats['glac_refreeze_monthly'].values[0, :] = ( - output_glac_refreeze_monthly_stats[:, 0] - ) - output_ds_all_stats['glac_melt_monthly'].values[0, :] = ( - output_glac_melt_monthly_stats[:, 0] - ) + output_ds_all_stats['glac_prec_monthly'].values[ + 0, : + ] = output_glac_prec_monthly_stats[:, 0] + output_ds_all_stats['glac_acc_monthly'].values[ + 0, : + ] = output_glac_acc_monthly_stats[:, 0] + output_ds_all_stats['glac_refreeze_monthly'].values[ + 0, : + ] = output_glac_refreeze_monthly_stats[:, 0] + output_ds_all_stats['glac_melt_monthly'].values[ + 0, : + ] = output_glac_melt_monthly_stats[:, 0] output_ds_all_stats['glac_frontalablation_monthly'].values[ 0, : ] = output_glac_frontalablation_monthly_stats[:, 0] output_ds_all_stats['glac_massbaltotal_monthly'].values[ 0, : ] = output_glac_massbaltotal_monthly_stats[:, 0] - output_ds_all_stats['glac_snowline_monthly'].values[0, :] = ( - output_glac_snowline_monthly_stats[:, 0] - ) + output_ds_all_stats['glac_snowline_monthly'].values[ + 0, : + ] = output_glac_snowline_monthly_stats[:, 0] output_ds_all_stats['glac_mass_change_ignored_annual'].values[ 0, : ] = output_glac_mass_change_ignored_annual_stats[:, 0] - output_ds_all_stats['offglac_prec_monthly'].values[0, :] = ( - output_offglac_prec_monthly_stats[:, 0] - ) - output_ds_all_stats['offglac_melt_monthly'].values[0, :] = ( - output_offglac_melt_monthly_stats[:, 0] - ) - output_ds_all_stats['offglac_refreeze_monthly'].values[0, :] = ( - output_offglac_refreeze_monthly_stats[:, 0] - ) - output_ds_all_stats['offglac_snowpack_monthly'].values[0, :] = ( - output_offglac_snowpack_monthly_stats[:, 0] - ) + output_ds_all_stats['offglac_prec_monthly'].values[ + 0, : + ] = output_offglac_prec_monthly_stats[:, 0] + output_ds_all_stats['offglac_melt_monthly'].values[ + 0, : + ] = output_offglac_melt_monthly_stats[:, 0] + output_ds_all_stats['offglac_refreeze_monthly'].values[ + 0, : + ] = output_offglac_refreeze_monthly_stats[:, 0] + output_ds_all_stats['offglac_snowpack_monthly'].values[ + 0, : + ] = output_offglac_snowpack_monthly_stats[:, 0] # output median absolute deviation if nsims > 1: - output_ds_all_stats['glac_runoff_monthly_mad'].values[0, :] = ( - output_glac_runoff_monthly_stats[:, 1] - ) - output_ds_all_stats['glac_area_annual_mad'].values[0, :] = ( - output_glac_area_annual_stats[:, 1] - ) - output_ds_all_stats['glac_mass_annual_mad'].values[0, :] = ( - output_glac_mass_annual_stats[:, 1] - ) - output_ds_all_stats['glac_mass_bsl_annual_mad'].values[0, :] = ( - output_glac_mass_bsl_annual_stats[:, 1] - ) - output_ds_all_stats['glac_ELA_annual_mad'].values[0, :] = ( - output_glac_ELA_annual_stats[:, 1] - ) + output_ds_all_stats['glac_runoff_monthly_mad'].values[ + 0, : + ] = output_glac_runoff_monthly_stats[:, 1] + output_ds_all_stats['glac_area_annual_mad'].values[ + 0, : + ] = output_glac_area_annual_stats[:, 1] + output_ds_all_stats['glac_mass_annual_mad'].values[ + 0, : + ] = output_glac_mass_annual_stats[:, 1] + output_ds_all_stats['glac_mass_bsl_annual_mad'].values[ + 0, : + ] = output_glac_mass_bsl_annual_stats[:, 1] + output_ds_all_stats['glac_ELA_annual_mad'].values[ + 0, : + ] = output_glac_ELA_annual_stats[:, 1] output_ds_all_stats['offglac_runoff_monthly_mad'].values[ 0, : ] = output_offglac_runoff_monthly_stats[:, 1] @@ -2055,9 +2056,9 @@ def run(list_packed_vars): output_ds_all_stats['glac_prec_monthly_mad'].values[ 0, : ] = output_glac_prec_monthly_stats[:, 1] - output_ds_all_stats['glac_acc_monthly_mad'].values[0, :] = ( - output_glac_acc_monthly_stats[:, 1] - ) + output_ds_all_stats['glac_acc_monthly_mad'].values[ + 0, : + ] = output_glac_acc_monthly_stats[:, 1] output_ds_all_stats['glac_refreeze_monthly_mad'].values[ 0, : ] = output_glac_refreeze_monthly_stats[:, 1] @@ -2141,9 +2142,9 @@ def run(list_packed_vars): output_binned.create_xr_ds() output_ds_binned_stats = output_binned.get_xr_ds() # fill values - output_ds_binned_stats['bin_distance'].values[0, :] = ( - output_glac_bin_dist - ) + output_ds_binned_stats['bin_distance'].values[ + 0, : + ] = output_glac_bin_dist output_ds_binned_stats['bin_surface_h_initial'].values[ 0, : ] = surface_h_initial @@ -2309,12 +2310,12 @@ def main(): args = parser.parse_args() # date range check try: - assert args.ref_startyear < args.ref_endyear, ( - f'ref_startyear [{args.ref_startyear}] must be less than ref_endyear [{args.ref_endyear}]' - ) - assert args.sim_startyear < args.sim_endyear, ( - f'sim_startyear [{args.sim_startyear}] must be less than sim_endyear [{args.sim_endyear}]' - ) + assert ( + args.ref_startyear < args.ref_endyear + ), f'ref_startyear [{args.ref_startyear}] must be less than ref_endyear [{args.ref_endyear}]' + assert ( + args.sim_startyear < args.sim_endyear + ), f'sim_startyear [{args.sim_startyear}] must be less than sim_endyear [{args.sim_endyear}]' except AssertionError as err: print('error: ', err) sys.exit(1) diff --git a/pygem/class_climate.py b/pygem/class_climate.py index fcac5572..48748de6 100755 --- a/pygem/class_climate.py +++ b/pygem/class_climate.py @@ -44,9 +44,9 @@ def __init__(self, name=str(), sim_climate_scenario=str(), realization=None): """ if pygem_prms['rgi']['rgi_lon_colname'] not in ['CenLon_360']: - assert 1 == 0, ( - 'Longitude does not use 360 degrees. Check how negative values are handled!' - ) + assert ( + 1 == 0 + ), 'Longitude does not use 360 degrees. Check how negative values are handled!' # Source of climate data self.name = name @@ -695,16 +695,24 @@ def importGCMvarnearestneighbor_xarray( # Check all glacier use appropriate climate data for i, latlon in enumerate(latlon_nearidx): - rgi_id = main_glac_rgi[pygem_prms['rgi']['rgi_glacno_float_colname']].values[i] - lat_res = abs(np.diff(data[vn][self.lat_vn].values)[0]) + rgi_id = main_glac_rgi[ + pygem_prms['rgi']['rgi_glacno_float_colname'] + ].values[i] + lat_res = abs(np.diff(data[vn][self.lat_vn].values)[0]) lon_res = abs(np.diff(data[vn][self.lon_vn].values)[0]) - lat_dd = abs(main_glac_rgi[self.rgi_lat_colname].values[i] - data[vn][self.lat_vn].values[latlon[0]]) - lon_dd = abs(main_glac_rgi[self.rgi_lon_colname].values[i] - data[vn][self.lon_vn].values[latlon[1]]) + lat_dd = abs( + main_glac_rgi[self.rgi_lat_colname].values[i] + - data[vn][self.lat_vn].values[latlon[0]] + ) + lon_dd = abs( + main_glac_rgi[self.rgi_lon_colname].values[i] + - data[vn][self.lon_vn].values[latlon[1]] + ) assert lat_dd <= lat_res and lon_dd <= lon_res, ( - f'Climate data pixel too from glacier {rgi_id}: Δlat={lat_dd:.3f}, ' + - f'Δlon={lon_dd:.3f}, res=({lat_res:.3f}, {lon_res:.3f})' - ) + f'Climate data pixel for {vn} too from glacier {rgi_id}: Δlat={lat_dd:.3f}, ' + + f'Δlon={lon_dd:.3f}, res=({lat_res:.3f}, {lon_res:.3f})' + ) # Convert to series glac_variable_series = np.array( diff --git a/pygem/oggm_compat.py b/pygem/oggm_compat.py index 7c2c7ddf..a8bb6614 100755 --- a/pygem/oggm_compat.py +++ b/pygem/oggm_compat.py @@ -25,7 +25,7 @@ from pygem.setup.config import ConfigManager # from oggm.shop import rgitopo -from pygem.shop import debris, icethickness, mbdata +from pygem.shop import debris, icethickness, mbdata, meltextent_and_snowline_1d # instantiate ConfigManager config_manager = ConfigManager() @@ -44,7 +44,7 @@ def single_flowline_glacier_directory( prepro_border=pygem_prms['oggm']['border'], logging_level=pygem_prms['oggm']['logging_level'], has_internet=pygem_prms['oggm']['has_internet'], - working_dir=f'{pygem_prms["root"]}/{pygem_prms["oggm"]["oggm_gdir_relpath"]}', + working_dir=f"{pygem_prms['root']}/{pygem_prms['oggm']['oggm_gdir_relpath']}", ): """Prepare a GlacierDirectory for PyGEM (single flowline to start with) @@ -127,6 +127,16 @@ def single_flowline_glacier_directory( ): workflow.execute_entity_task(debris.debris_to_gdir, gdir) workflow.execute_entity_task(debris.debris_binned, gdir) + # 1d melt extent calibration data + if not os.path.isfile(gdir.get_filepath('meltextent_1d')): + workflow.execute_entity_task( + meltextent_and_snowline_1d.meltextent_1d_to_gdir, gdir + ) + # 1d snowline calibration data + if not os.path.isfile(gdir.get_filepath('snowline_1d')): + workflow.execute_entity_task( + meltextent_and_snowline_1d.snowline_1d_to_gdir, gdir + ) return gdir @@ -138,7 +148,7 @@ def single_flowline_glacier_directory_with_calving( k_calving=1, logging_level=pygem_prms['oggm']['logging_level'], has_internet=pygem_prms['oggm']['has_internet'], - working_dir=pygem_prms['root'] + pygem_prms['oggm']['oggm_gdir_relpath'], + working_dir=f"{pygem_prms['root']}/{pygem_prms['oggm']['oggm_gdir_relpath']}", facorrected=pygem_prms['setup']['include_frontalablation'], ): """Prepare a GlacierDirectory for PyGEM (single flowline to start with) @@ -223,6 +233,16 @@ def single_flowline_glacier_directory_with_calving( workflow.execute_entity_task( mbdata.mb_df_to_gdir, gdir, **{'facorrected': facorrected} ) + # 1d melt extent calibration data + if not os.path.isfile(gdir.get_filepath('meltextent_1d')): + workflow.execute_entity_task( + meltextent_and_snowline_1d.meltextent_1d_to_gdir, gdir + ) + # 1d snowline calibration data + if not os.path.isfile(gdir.get_filepath('snowline_1d')): + workflow.execute_entity_task( + meltextent_and_snowline_1d.snowline_1d_to_gdir, gdir + ) return gdir diff --git a/pygem/setup/config.py b/pygem/setup/config.py index f54598d7..5ab8e51e 100644 --- a/pygem/setup/config.py +++ b/pygem/setup/config.py @@ -222,6 +222,8 @@ def _validate_config(self, config): 'calib.emulator_params.ftol_opt': float, 'calib.emulator_params.eps_opt': float, 'calib.MCMC_params': dict, + 'calib.MCMC_params.option_calib_meltextent_1d': bool, + 'calib.MCMC_params.option_calib_snowline_1d': bool, 'calib.MCMC_params.option_use_emulator': bool, 'calib.MCMC_params.emulator_sims': int, 'calib.MCMC_params.tbias_step': float, @@ -262,6 +264,8 @@ def _validate_config(self, config): 'calib.data.icethickness': dict, 'calib.data.icethickness.h_ref_relpath': str, 'calib.icethickness_cal_frac_byarea': float, + 'calib.data.meltextent_1d.meltextent_1d_relpath': (str, type(None)), + 'calib.data.snowline_1d.snowline_1d_relpath': (str, type(None)), 'sim': dict, 'sim.option_dynamics': (str, type(None)), 'sim.option_bias_adjustment': int, diff --git a/pygem/setup/config.yaml b/pygem/setup/config.yaml index 6798b205..5fce2d1c 100644 --- a/pygem/setup/config.yaml +++ b/pygem/setup/config.yaml @@ -150,6 +150,8 @@ calib: # MCMC params MCMC_params: + option_calib_meltextent_1d: false # option to calibrate against 1d melt extent data (true or false) + option_calib_snowline_1d: false # option to calibrate against 1d snowline data (true or false) option_use_emulator: true # use emulator or full model (if true, calibration must have first been run with option_calibretion=='emulator') emulator_sims: 100 tbias_step: 0.1 @@ -198,6 +200,13 @@ calib: # ice thickness icethickness: h_ref_relpath: /IceThickness_Farinotti/composite_thickness_RGI60-all_regions/ + # 1d melt extents + meltextent_1d: + meltextent_1d_relpath: /SAR_data/merged/ # relative to main data path. per-glacier files within will be expected as _melt_extent_elev.csv (e.g., 01.00570_melt_extent_elev.csv) + # 1d snowlines + snowline_1d: + snowline_1d_relpath: /SAR_data/merged/ # relative to main data path. per-glacier files within will be expected as _snowline_elev.csv (e.g., 01.00570_snowline_elev.csv) + icethickness_cal_frac_byarea: 0.9 # Regional glacier area fraction that is used to calibrate the ice thickness # e.g., 0.9 means only the largest 90% of glaciers by area will be used to calibrate diff --git a/pygem/shop/meltextent_and_snowline_1d.py b/pygem/shop/meltextent_and_snowline_1d.py new file mode 100644 index 00000000..cd227667 --- /dev/null +++ b/pygem/shop/meltextent_and_snowline_1d.py @@ -0,0 +1,283 @@ +""" +Python Glacier Evolution Model (PyGEM) + +copyright © 2025 Brandon Tober , David Rounce + +Distributed under the MIT license +""" + +# Built-in libaries +import os +import datetime +import logging + +import pandas as pd + +# External libraries +# Local libraries +from oggm import cfg +from oggm.utils import entity_task + +# pygem imports +from pygem.setup.config import ConfigManager + +# instantiate ConfigManager +config_manager = ConfigManager() +# read the config +pygem_prms = config_manager.read_config() + + +# Module logger +log = logging.getLogger(__name__) + +# Add the new name "snowline_1d" to the list of things that the GlacierDirectory understands +if 'meltextent_1d' not in cfg.BASENAMES: + cfg.BASENAMES['meltextent_1d'] = ( + 'meltextent_1d.json', + '1D snowline data', + ) +if 'snowline_1d' not in cfg.BASENAMES: + cfg.BASENAMES['snowline_1d'] = ( + 'snowline_1d.json', + '1D snowline data', + ) + + +@entity_task(log, writes=['snowline_1d']) +def meltextent_1d_to_gdir( + gdir, +): + """ + Add 1d melt extent observations to the given glacier directory + + Parameters + ---------- + gdir : :py:class:`oggm.GlacierDirectory` + where to write the data + + expected csv structure: + Columns: 'date', 'z', 'z_min', 'z_max', 'direction' + 'date': Observation date, stored as a string in 'YYYY-MM-DD' format + 'z': Melt extent elevation (meters) + 'z_min': Melt extent elevation minimum (meters) + 'z_max': Melt extent elevation maximum (meters) + 'direction': SAR path direction, stored as a string (e.g., 'ascending' or 'descending') + 'ref_dem_year': Reference DEM year for elevation value of observations (m a.s.l.) (e.g., 2013 if using COP30) + """ + # get dataset file path + meltextent_1d_fp = ( + f'{pygem_prms["root"]}/' + f'{pygem_prms["calib"]["data"]["meltextent_1d"]["meltextent_1d_relpath"]}/' + f'{gdir.rgi_id.split("-")[1]}_melt_extent_elev.csv' + ) + + # check for file + if os.path.exists(meltextent_1d_fp): + meltextent_1d_df = pd.read_csv(meltextent_1d_fp) + else: + log.debug('No melt extent data to load, skipping task.') + raise Warning('No melt extent data to load') # file not found, skip + + validate_meltextent_1d_structure(meltextent_1d_df) + meltextent_1d_dict = meltextent_csv_to_dict(meltextent_1d_df) + gdir.write_json(meltextent_1d_dict, 'meltextent_1d') + + +def validate_meltextent_1d_structure(data): + """Validate that meltextent_1d CSV structure matches expected format.""" + + required_cols = ['date', 'z', 'z_min', 'z_max', 'direction', 'ref_dem_year'] + for col in required_cols: + if col not in data.columns: + raise ValueError(f"Missing required column '{col}' in melt extent CSV.") + + # Validate dates + dates = data['date'] + if not isinstance(dates, pd.Series) or len(dates) == 0: + raise ValueError("'dates' must be a non-empty series.") + for i, date_str in enumerate(dates): + try: + datetime.datetime.strptime(date_str, '%Y-%m-%d') + except ValueError: + raise ValueError( + f"Invalid date format in 'dates[{i}]': {date_str}" + ) from None + + # Validate z + z = data['z'] + if not (isinstance(z, pd.Series) and len(z) == len(dates)): + raise ValueError(f"'z' must be a series of length {len(dates)}.") + if not all(isinstance(x, (int, float)) for x in z): + raise ValueError("All 'z' values must be numeric.") + + # Validate z_min + z_min = data['z_min'] + if not (isinstance(z_min, pd.Series) and len(z_min) == len(dates)): + raise ValueError(f"'z_min' must be a series of length {len(dates)}.") + if not all(isinstance(x, (int, float)) for x in z_min): + raise ValueError("All 'z_min' values must be numeric.") + + # Validate z_max + z_max = data['z_max'] + if not (isinstance(z_max, pd.Series) and len(z_max) == len(dates)): + raise ValueError(f"'z_max' must be a series of length {len(dates)}.") + if not all(isinstance(x, (int, float)) for x in z_max): + raise ValueError("All 'z_max' values must be numeric.") + + # Validate direction + direction = data['direction'] + if not (isinstance(direction, pd.Series) and len(direction) == len(dates)): + raise ValueError(f"'direction' must be a series of length {len(dates)}.") + if not all(isinstance(x, str) for x in direction): + raise ValueError("All 'direction' values must be strings.") + + # Validate reference DEM year + dem_year = data['ref_dem_year'] + if not (isinstance(dem_year, pd.Series) and len(dem_year) == len(dates)): + raise ValueError(f"'ref_dem_year' must be a series of length {len(dates)}.") + if len(set(dem_year)) != 1: + raise ValueError("All 'ref_dem_year' values must be identical.") + if not all(isinstance(x, int) for x in dem_year): + raise ValueError("All 'ref_dem_year' values must be an integer year.") + + return True + + +def meltextent_csv_to_dict(data): + """Convert snowline_1d CSV to JSON for OGGM ingestion.""" + dates = data['date'].astype(str).tolist() + z = data['z'].astype(float).tolist() + z_min = data['z_min'].astype(float).tolist() + z_max = data['z_max'].astype(float).tolist() + direction = data['direction'].astype(str).tolist() + ref_dem_year = data['ref_dem_year'].astype(int).tolist()[0] + + data_dict = { + 'date': dates, + 'z': z, + 'z_min': z_min, + 'z_max': z_max, + 'direction': direction, + 'ref_dem_year': ref_dem_year, + } + return data_dict + + +@entity_task(log, writes=['snowline_1d']) +def snowline_1d_to_gdir( + gdir, +): + """ + Add 1d snowline observations to the given glacier directory + + Parameters + ---------- + gdir : :py:class:`oggm.GlacierDirectory` + where to write the data + + expected csv structure: + Columns: 'date', 'z', 'z_min', 'z_max', 'direction' + 'date': Observation date, stored as a string in 'YYYY-MM-DD' format + 'z': Snowline elevation (m a.s.l.) + 'z_min': Snowline elevation minimum (m a.s.l.) + 'z_max': Snowline elevation maximum (m a.s.l.) + 'direction': SAR path direction, stored as a string (e.g., 'ascending' or 'descending') + 'ref_dem_year': Reference DEM year for elevation value of observations (m a.s.l.) (e.g., 2013 if using COP30) + """ + # get dataset file path + snowline_1d_fp = ( + f'{pygem_prms["root"]}/' + f'{pygem_prms["calib"]["data"]["snowline_1d"]["snowline_1d_relpath"]}/' + f'{gdir.rgi_id.split("-")[1]}_snowline_elev.csv' + ) + + # check for file + if os.path.exists(snowline_1d_fp): + snowline_1d_df = pd.read_csv(snowline_1d_fp) + else: + log.debug('No snowline data to load, skipping task.') + raise Warning('No snowline data to load') # file not found, skip + + validate_snowline_1d_structure(snowline_1d_df) + snowline_1d_dict = snowline_csv_to_dict(snowline_1d_df) + gdir.write_json(snowline_1d_dict, 'snowline_1d') + + +def validate_snowline_1d_structure(data): + """Validate that snowline_1d CSV structure matches expected format.""" + + required_cols = ['date', 'z', 'z_min', 'z_max', 'direction', 'ref_dem_year'] + for col in required_cols: + if col not in data.columns: + raise ValueError(f"Missing required column '{col}' in snowline CSV.") + + # Validate dates + dates = data['date'] + if not isinstance(dates, pd.Series) or len(dates) == 0: + raise ValueError("'dates' must be a non-empty series.") + for i, date_str in enumerate(dates): + try: + datetime.datetime.strptime(date_str, '%Y-%m-%d') + except ValueError: + raise ValueError( + f"Invalid date format in 'dates[{i}]': {date_str}" + ) from None + + # Validate z + z = data['z'] + if not (isinstance(z, pd.Series) and len(z) == len(dates)): + raise ValueError(f"'z' must be a series of length {len(dates)}.") + if not all(isinstance(x, (int, float)) for x in z): + raise ValueError("All 'z' values must be numeric.") + + # Validate z_min + z_min = data['z_min'] + if not (isinstance(z_min, pd.Series) and len(z_min) == len(dates)): + raise ValueError(f"'z_min' must be a series of length {len(dates)}.") + if not all(isinstance(x, (int, float)) for x in z_min): + raise ValueError("All 'z_min' values must be numeric.") + + # Validate z_max + z_max = data['z_max'] + if not (isinstance(z_max, pd.Series) and len(z_max) == len(dates)): + raise ValueError(f"'z_max' must be a series of length {len(dates)}.") + if not all(isinstance(x, (int, float)) for x in z_max): + raise ValueError("All 'z_max' values must be numeric.") + + # Validate direction + direction = data['direction'] + if not (isinstance(direction, pd.Series) and len(direction) == len(dates)): + raise ValueError(f"'direction' must be a series of length {len(dates)}.") + if not all(isinstance(x, str) for x in direction): + raise ValueError("All 'direction' values must be strings.") + + # Validate reference DEM year + dem_year = data['ref_dem_year'] + if not (isinstance(dem_year, pd.Series) and len(dem_year) == len(dates)): + raise ValueError(f"'ref_dem_year' must be a series of length {len(dates)}.") + if len(set(dem_year)) != 1: + raise ValueError("All 'ref_dem_year' values must be identical.") + if not all(isinstance(x, int) for x in dem_year): + raise ValueError("All 'ref_dem_year' values must be an integer year.") + + return True + + +def snowline_csv_to_dict(data): + """Convert snowline_1d CSV to JSON for OGGM ingestion.""" + dates = data['date'].astype(str).tolist() + z = data['z'].astype(float).tolist() + z_min = data['z_min'].astype(float).tolist() + z_max = data['z_max'].astype(float).tolist() + direction = data['direction'].astype(str).tolist() + ref_dem_year = data['ref_dem_year'].astype(int).tolist()[0] + + data_dict = { + 'date': dates, + 'z': z, + 'z_min': z_min, + 'z_max': z_max, + 'direction': direction, + 'ref_dem_year': ref_dem_year, + } + return data_dict From 8b27e37746a7905ee77aeb33513171e95600ea84 Mon Sep 17 00:00:00 2001 From: albinwwells Date: Thu, 9 Oct 2025 11:10:33 -0400 Subject: [PATCH 3/7] Improved handling for reference DEM year validation for snowline and melt extent data --- pygem/shop/meltextent_and_snowline_1d.py | 30 +++++++++++++----------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/pygem/shop/meltextent_and_snowline_1d.py b/pygem/shop/meltextent_and_snowline_1d.py index cd227667..d52743e2 100644 --- a/pygem/shop/meltextent_and_snowline_1d.py +++ b/pygem/shop/meltextent_and_snowline_1d.py @@ -132,13 +132,14 @@ def validate_meltextent_1d_structure(data): raise ValueError("All 'direction' values must be strings.") # Validate reference DEM year - dem_year = data['ref_dem_year'] - if not (isinstance(dem_year, pd.Series) and len(dem_year) == len(dates)): - raise ValueError(f"'ref_dem_year' must be a series of length {len(dates)}.") - if len(set(dem_year)) != 1: - raise ValueError("All 'ref_dem_year' values must be identical.") - if not all(isinstance(x, int) for x in dem_year): - raise ValueError("All 'ref_dem_year' values must be an integer year.") + dem_year = data['ref_dem_year'].dropna().unique() + if len(dem_year) != 1: + raise ValueError( + f"'ref_dem_year' must have exactly one unique value, " + f"but found {len(dem_year)}: {dem_year}" + ) + if not isinstance(dem_year, (int)): + raise TypeError(f"'ref_dem_year' must be an integer, but got {dem_year} ({type(dem_year).__name__}).") return True @@ -252,13 +253,14 @@ def validate_snowline_1d_structure(data): raise ValueError("All 'direction' values must be strings.") # Validate reference DEM year - dem_year = data['ref_dem_year'] - if not (isinstance(dem_year, pd.Series) and len(dem_year) == len(dates)): - raise ValueError(f"'ref_dem_year' must be a series of length {len(dates)}.") - if len(set(dem_year)) != 1: - raise ValueError("All 'ref_dem_year' values must be identical.") - if not all(isinstance(x, int) for x in dem_year): - raise ValueError("All 'ref_dem_year' values must be an integer year.") + dem_year = data['ref_dem_year'].dropna().unique() + if len(dem_year) != 1: + raise ValueError( + f"'ref_dem_year' must have exactly one unique value, " + f"but found {len(dem_year)}: {dem_year}" + ) + if not isinstance(dem_year, (int)): + raise TypeError(f"'ref_dem_year' must be an integer, but got {dem_year} ({type(dem_year).__name__}).") return True From 0403c6a341344f122ccab7948b576cf321ec5170 Mon Sep 17 00:00:00 2001 From: "brandon s. tober" Date: Thu, 9 Oct 2025 11:40:19 -0400 Subject: [PATCH 4/7] Ruff linting/formatting --- pygem/bin/run/run_calibration.py | 60 +++-- pygem/bin/run/run_simulation.py | 270 +++++++++++------------ pygem/class_climate.py | 6 +- pygem/oggm_compat.py | 4 +- pygem/shop/meltextent_and_snowline_1d.py | 18 +- 5 files changed, 178 insertions(+), 180 deletions(-) diff --git a/pygem/bin/run/run_calibration.py b/pygem/bin/run/run_calibration.py index a1b610ea..307b2429 100755 --- a/pygem/bin/run/run_calibration.py +++ b/pygem/bin/run/run_calibration.py @@ -636,9 +636,9 @@ def run(list_packed_vars): # Tidewater glaciers - use climatic mass balance since calving_k already calibrated separately if gdir.is_tidewater: - assert ( - 'mb_clim_mwea' in gdir.mbdata.keys() - ), 'include_frontalablation is set as true, but fontal ablation has yet to be calibrated.' + assert 'mb_clim_mwea' in gdir.mbdata.keys(), ( + 'include_frontalablation is set as true, but fontal ablation has yet to be calibrated.' + ) mb_obs_mwea = gdir.mbdata['mb_clim_mwea'] mb_obs_mwea_err = gdir.mbdata['mb_clim_mwea_err'] # non-tidewater - use geodetic mass balance @@ -702,9 +702,9 @@ def run(list_packed_vars): fls = None if debug: - assert os.path.exists( - mbdata_fn - ), 'Mass balance data missing. Check dataset and column names' + assert os.path.exists(mbdata_fn), ( + 'Mass balance data missing. Check dataset and column names' + ) # if spinup, grab appropriate flowlines if args.spinup: @@ -1207,9 +1207,9 @@ def single_param_optimizer( Computationally more robust and sometimes faster than scipy minimize """ - assert ( - prm2opt is not None - ), 'For single_param_optimizer you must specify parameter to optimize' + assert prm2opt is not None, ( + 'For single_param_optimizer you must specify parameter to optimize' + ) if prm2opt == 'kp': prm_bndlow = kp_bnds[0] @@ -1707,9 +1707,9 @@ def single_param_optimizer( + glacier_str.split('.')[0].zfill(2) + '/' ) - assert os.path.exists( - em_mod_fp + em_mod_fn - ), f'emulator output does not exist : {em_mod_fp + em_mod_fn}' + assert os.path.exists(em_mod_fp + em_mod_fn), ( + f'emulator output does not exist : {em_mod_fp + em_mod_fn}' + ) mbEmulator = massbalEmulator.load(em_mod_path=em_mod_fp + em_mod_fn) outpath_sfix = '' # output file path suffix if using emulator else: @@ -2039,24 +2039,18 @@ def must_melt(kp, tbias, ddfsnow, **kwargs): sampler = mcmc.Metropolis(mb.means, mb.stds) # draw samples - ( - m_chain_z, - pred_chain, - m_primes_z, - pred_primes, - _, - ar, - ) = sampler.sample( - initial_guesses_z, - mb.log_posterior, - n_samples=args.chain_length, - h=pygem_prms['calib']['MCMC_params']['mcmc_step'], - burnin=int(args.burn_pct / 100 * args.chain_length), - thin_factor=pygem_prms['calib']['MCMC_params'][ - 'thin_interval' - ], - progress_bar=args.progress_bar, - ) + m_chain_z, pred_chain, m_primes_z, pred_primes, _, ar = ( + sampler.sample( + initial_guesses_z, + mb.log_posterior, + n_samples=args.chain_length, + h=pygem_prms['calib']['MCMC_params']['mcmc_step'], + burnin=int(args.burn_pct / 100 * args.chain_length), + thin_factor=pygem_prms['calib']['MCMC_params'][ + 'thin_interval' + ], + progress_bar=args.progress_bar, + ) # Check condition at the end if (m_chain_z[:, 0] == m_chain_z[0, 0]).all(): @@ -2331,9 +2325,9 @@ def single_param_optimizer( mb_mwea_threshold=0.005, debug=False, ): - assert ( - prm2opt is not None - ), 'For single_param_optimizer you must specify parameter to optimize' + assert prm2opt is not None, ( + 'For single_param_optimizer you must specify parameter to optimize' + ) if prm2opt == 'kp': prm_bndlow = kp_bnds[0] diff --git a/pygem/bin/run/run_simulation.py b/pygem/bin/run/run_simulation.py index 6a7ed25b..0f8cd94d 100755 --- a/pygem/bin/run/run_simulation.py +++ b/pygem/bin/run/run_simulation.py @@ -677,9 +677,9 @@ def run(list_packed_vars): + '/' ) + modelprms_fn - assert os.path.exists( - modelprms_fp - ), 'Calibrated parameters do not exist.' + assert os.path.exists(modelprms_fp), ( + 'Calibrated parameters do not exist.' + ) with open(modelprms_fp, 'r') as f: modelprms_dict = json.load(f) @@ -746,9 +746,9 @@ def run(list_packed_vars): else: # Load quality controlled frontal ablation data fp = f'{pygem_prms["root"]}/{pygem_prms["calib"]["data"]["frontalablation"]["frontalablation_relpath"]}/analysis/{pygem_prms["calib"]["data"]["frontalablation"]["frontalablation_cal_fn"]}' - assert os.path.exists( - fp - ), 'Calibrated calving dataset does not exist' + assert os.path.exists(fp), ( + 'Calibrated calving dataset does not exist' + ) calving_df = pd.read_csv(fp) calving_rgiids = list(calving_df.RGIId) @@ -1109,9 +1109,9 @@ def run(list_packed_vars): try: diag = ev_model.run_until_and_store(args.sim_endyear + 1) - ev_model.mb_model.glac_wide_volume_annual[ - -1 - ] = diag.volume_m3[-1] + ev_model.mb_model.glac_wide_volume_annual[-1] = ( + diag.volume_m3[-1] + ) ev_model.mb_model.glac_wide_area_annual[-1] = diag.area_m2[ -1 ] @@ -1454,9 +1454,9 @@ def run(list_packed_vars): ).sum() / mbmod.glacier_area_initial.sum() mb_all.append(glac_wide_mb_mwea) mbmod.glac_wide_area_annual[-1] = mbmod.glac_wide_area_annual[0] - mbmod.glac_wide_volume_annual[ - -1 - ] = mbmod.glac_wide_volume_annual[0] + mbmod.glac_wide_volume_annual[-1] = ( + mbmod.glac_wide_volume_annual[0] + ) diag['area_m2'] = mbmod.glac_wide_area_annual diag['volume_m3'] = mbmod.glac_wide_volume_annual diag['volume_bsl_m3'] = 0 @@ -1534,20 +1534,20 @@ def run(list_packed_vars): output_glac_temp_monthly[:, n_iter] = mbmod.glac_wide_temp output_glac_prec_monthly[:, n_iter] = mbmod.glac_wide_prec output_glac_acc_monthly[:, n_iter] = mbmod.glac_wide_acc - output_glac_refreeze_monthly[ - :, n_iter - ] = mbmod.glac_wide_refreeze + output_glac_refreeze_monthly[:, n_iter] = ( + mbmod.glac_wide_refreeze + ) output_glac_melt_monthly[:, n_iter] = mbmod.glac_wide_melt - output_glac_frontalablation_monthly[ - :, n_iter - ] = mbmod.glac_wide_frontalablation - output_glac_massbaltotal_monthly[ - :, n_iter - ] = mbmod.glac_wide_massbaltotal + output_glac_frontalablation_monthly[:, n_iter] = ( + mbmod.glac_wide_frontalablation + ) + output_glac_massbaltotal_monthly[:, n_iter] = ( + mbmod.glac_wide_massbaltotal + ) output_glac_runoff_monthly[:, n_iter] = mbmod.glac_wide_runoff - output_glac_snowline_monthly[ - :, n_iter - ] = mbmod.glac_wide_snowline + output_glac_snowline_monthly[:, n_iter] = ( + mbmod.glac_wide_snowline + ) output_glac_area_annual[:, n_iter] = diag.area_m2.values output_glac_mass_annual[:, n_iter] = ( diag.volume_m3.values @@ -1564,16 +1564,16 @@ def run(list_packed_vars): output_glac_ELA_annual[:, n_iter] = mbmod.glac_wide_ELA_annual output_offglac_prec_monthly[:, n_iter] = mbmod.offglac_wide_prec - output_offglac_refreeze_monthly[ - :, n_iter - ] = mbmod.offglac_wide_refreeze + output_offglac_refreeze_monthly[:, n_iter] = ( + mbmod.offglac_wide_refreeze + ) output_offglac_melt_monthly[:, n_iter] = mbmod.offglac_wide_melt - output_offglac_snowpack_monthly[ - :, n_iter - ] = mbmod.offglac_wide_snowpack - output_offglac_runoff_monthly[ - :, n_iter - ] = mbmod.offglac_wide_runoff + output_offglac_snowpack_monthly[:, n_iter] = ( + mbmod.offglac_wide_snowpack + ) + output_offglac_runoff_monthly[:, n_iter] = ( + mbmod.offglac_wide_runoff + ) if output_glac_bin_icethickness_annual is None: output_glac_bin_area_annual_sim = ( @@ -1603,9 +1603,9 @@ def run(list_packed_vars): fl_section[fl_widths_m > 0] / fl_widths_m[fl_widths_m > 0] ) - output_glac_bin_icethickness_annual_sim[ - :, -1, 0 - ] = icethickness_t0 + output_glac_bin_icethickness_annual_sim[:, -1, 0] = ( + icethickness_t0 + ) # mass glacier_vol_t0 = ( fl_widths_m * fl_dx_meter * icethickness_t0 @@ -1626,9 +1626,9 @@ def run(list_packed_vars): output_glac_bin_massbalclim_annual_sim = np.zeros( mbmod.glac_bin_icethickness_annual.shape ) - output_glac_bin_massbalclim_annual_sim[ - :, :-1 - ] = mbmod.glac_bin_massbalclim_annual + output_glac_bin_massbalclim_annual_sim[:, :-1] = ( + mbmod.glac_bin_massbalclim_annual + ) output_glac_bin_massbalclim_annual = ( output_glac_bin_massbalclim_annual_sim[:, :, np.newaxis] ) @@ -1698,9 +1698,9 @@ def run(list_packed_vars): fl_section[fl_widths_m > 0] / fl_widths_m[fl_widths_m > 0] ) - output_glac_bin_icethickness_annual_sim[ - :, -1, 0 - ] = icethickness_t0 + output_glac_bin_icethickness_annual_sim[:, -1, 0] = ( + icethickness_t0 + ) # mass glacier_vol_t0 = ( fl_widths_m * fl_dx_meter * icethickness_t0 @@ -1727,9 +1727,9 @@ def run(list_packed_vars): output_glac_bin_massbalclim_annual_sim = np.zeros( mbmod.glac_bin_icethickness_annual.shape ) - output_glac_bin_massbalclim_annual_sim[ - :, :-1 - ] = mbmod.glac_bin_massbalclim_annual + output_glac_bin_massbalclim_annual_sim[:, :-1] = ( + mbmod.glac_bin_massbalclim_annual + ) output_glac_bin_massbalclim_annual = np.append( output_glac_bin_massbalclim_annual, output_glac_bin_massbalclim_annual_sim[ @@ -1817,21 +1817,21 @@ def run(list_packed_vars): output_stats.create_xr_ds() output_ds_all_stats = output_stats.get_xr_ds() # fill values - output_ds_all_stats['glac_runoff_monthly'].values[ - 0, : - ] = output_glac_runoff_monthly[:, n_iter] - output_ds_all_stats['glac_area_annual'].values[ - 0, : - ] = output_glac_area_annual[:, n_iter] - output_ds_all_stats['glac_mass_annual'].values[ - 0, : - ] = output_glac_mass_annual[:, n_iter] - output_ds_all_stats['glac_mass_bsl_annual'].values[ - 0, : - ] = output_glac_mass_bsl_annual[:, n_iter] - output_ds_all_stats['glac_ELA_annual'].values[ - 0, : - ] = output_glac_ELA_annual[:, n_iter] + output_ds_all_stats['glac_runoff_monthly'].values[0, :] = ( + output_glac_runoff_monthly[:, n_iter] + ) + output_ds_all_stats['glac_area_annual'].values[0, :] = ( + output_glac_area_annual[:, n_iter] + ) + output_ds_all_stats['glac_mass_annual'].values[0, :] = ( + output_glac_mass_annual[:, n_iter] + ) + output_ds_all_stats['glac_mass_bsl_annual'].values[0, :] = ( + output_glac_mass_bsl_annual[:, n_iter] + ) + output_ds_all_stats['glac_ELA_annual'].values[0, :] = ( + output_glac_ELA_annual[:, n_iter] + ) output_ds_all_stats['offglac_runoff_monthly'].values[ 0, : ] = output_offglac_runoff_monthly[:, n_iter] @@ -1842,9 +1842,9 @@ def run(list_packed_vars): output_ds_all_stats['glac_prec_monthly'].values[ 0, : ] = output_glac_prec_monthly[:, n_iter] - output_ds_all_stats['glac_acc_monthly'].values[ - 0, : - ] = output_glac_acc_monthly[:, n_iter] + output_ds_all_stats['glac_acc_monthly'].values[0, :] = ( + output_glac_acc_monthly[:, n_iter] + ) output_ds_all_stats['glac_refreeze_monthly'].values[ 0, : ] = output_glac_refreeze_monthly[:, n_iter] @@ -1970,82 +1970,82 @@ def run(list_packed_vars): ) # output mean/median from all simulations - output_ds_all_stats['glac_runoff_monthly'].values[ - 0, : - ] = output_glac_runoff_monthly_stats[:, 0] - output_ds_all_stats['glac_area_annual'].values[ - 0, : - ] = output_glac_area_annual_stats[:, 0] - output_ds_all_stats['glac_mass_annual'].values[ - 0, : - ] = output_glac_mass_annual_stats[:, 0] - output_ds_all_stats['glac_mass_bsl_annual'].values[ - 0, : - ] = output_glac_mass_bsl_annual_stats[:, 0] - output_ds_all_stats['glac_ELA_annual'].values[ - 0, : - ] = output_glac_ELA_annual_stats[:, 0] - output_ds_all_stats['offglac_runoff_monthly'].values[ - 0, : - ] = output_offglac_runoff_monthly_stats[:, 0] + output_ds_all_stats['glac_runoff_monthly'].values[0, :] = ( + output_glac_runoff_monthly_stats[:, 0] + ) + output_ds_all_stats['glac_area_annual'].values[0, :] = ( + output_glac_area_annual_stats[:, 0] + ) + output_ds_all_stats['glac_mass_annual'].values[0, :] = ( + output_glac_mass_annual_stats[:, 0] + ) + output_ds_all_stats['glac_mass_bsl_annual'].values[0, :] = ( + output_glac_mass_bsl_annual_stats[:, 0] + ) + output_ds_all_stats['glac_ELA_annual'].values[0, :] = ( + output_glac_ELA_annual_stats[:, 0] + ) + output_ds_all_stats['offglac_runoff_monthly'].values[0, :] = ( + output_offglac_runoff_monthly_stats[:, 0] + ) if args.export_extra_vars: output_ds_all_stats['glac_temp_monthly'].values[0, :] = ( output_glac_temp_monthly_stats[:, 0] + 273.15 ) - output_ds_all_stats['glac_prec_monthly'].values[ - 0, : - ] = output_glac_prec_monthly_stats[:, 0] - output_ds_all_stats['glac_acc_monthly'].values[ - 0, : - ] = output_glac_acc_monthly_stats[:, 0] - output_ds_all_stats['glac_refreeze_monthly'].values[ - 0, : - ] = output_glac_refreeze_monthly_stats[:, 0] - output_ds_all_stats['glac_melt_monthly'].values[ - 0, : - ] = output_glac_melt_monthly_stats[:, 0] + output_ds_all_stats['glac_prec_monthly'].values[0, :] = ( + output_glac_prec_monthly_stats[:, 0] + ) + output_ds_all_stats['glac_acc_monthly'].values[0, :] = ( + output_glac_acc_monthly_stats[:, 0] + ) + output_ds_all_stats['glac_refreeze_monthly'].values[0, :] = ( + output_glac_refreeze_monthly_stats[:, 0] + ) + output_ds_all_stats['glac_melt_monthly'].values[0, :] = ( + output_glac_melt_monthly_stats[:, 0] + ) output_ds_all_stats['glac_frontalablation_monthly'].values[ 0, : ] = output_glac_frontalablation_monthly_stats[:, 0] output_ds_all_stats['glac_massbaltotal_monthly'].values[ 0, : ] = output_glac_massbaltotal_monthly_stats[:, 0] - output_ds_all_stats['glac_snowline_monthly'].values[ - 0, : - ] = output_glac_snowline_monthly_stats[:, 0] + output_ds_all_stats['glac_snowline_monthly'].values[0, :] = ( + output_glac_snowline_monthly_stats[:, 0] + ) output_ds_all_stats['glac_mass_change_ignored_annual'].values[ 0, : ] = output_glac_mass_change_ignored_annual_stats[:, 0] - output_ds_all_stats['offglac_prec_monthly'].values[ - 0, : - ] = output_offglac_prec_monthly_stats[:, 0] - output_ds_all_stats['offglac_melt_monthly'].values[ - 0, : - ] = output_offglac_melt_monthly_stats[:, 0] - output_ds_all_stats['offglac_refreeze_monthly'].values[ - 0, : - ] = output_offglac_refreeze_monthly_stats[:, 0] - output_ds_all_stats['offglac_snowpack_monthly'].values[ - 0, : - ] = output_offglac_snowpack_monthly_stats[:, 0] + output_ds_all_stats['offglac_prec_monthly'].values[0, :] = ( + output_offglac_prec_monthly_stats[:, 0] + ) + output_ds_all_stats['offglac_melt_monthly'].values[0, :] = ( + output_offglac_melt_monthly_stats[:, 0] + ) + output_ds_all_stats['offglac_refreeze_monthly'].values[0, :] = ( + output_offglac_refreeze_monthly_stats[:, 0] + ) + output_ds_all_stats['offglac_snowpack_monthly'].values[0, :] = ( + output_offglac_snowpack_monthly_stats[:, 0] + ) # output median absolute deviation if nsims > 1: - output_ds_all_stats['glac_runoff_monthly_mad'].values[ - 0, : - ] = output_glac_runoff_monthly_stats[:, 1] - output_ds_all_stats['glac_area_annual_mad'].values[ - 0, : - ] = output_glac_area_annual_stats[:, 1] - output_ds_all_stats['glac_mass_annual_mad'].values[ - 0, : - ] = output_glac_mass_annual_stats[:, 1] - output_ds_all_stats['glac_mass_bsl_annual_mad'].values[ - 0, : - ] = output_glac_mass_bsl_annual_stats[:, 1] - output_ds_all_stats['glac_ELA_annual_mad'].values[ - 0, : - ] = output_glac_ELA_annual_stats[:, 1] + output_ds_all_stats['glac_runoff_monthly_mad'].values[0, :] = ( + output_glac_runoff_monthly_stats[:, 1] + ) + output_ds_all_stats['glac_area_annual_mad'].values[0, :] = ( + output_glac_area_annual_stats[:, 1] + ) + output_ds_all_stats['glac_mass_annual_mad'].values[0, :] = ( + output_glac_mass_annual_stats[:, 1] + ) + output_ds_all_stats['glac_mass_bsl_annual_mad'].values[0, :] = ( + output_glac_mass_bsl_annual_stats[:, 1] + ) + output_ds_all_stats['glac_ELA_annual_mad'].values[0, :] = ( + output_glac_ELA_annual_stats[:, 1] + ) output_ds_all_stats['offglac_runoff_monthly_mad'].values[ 0, : ] = output_offglac_runoff_monthly_stats[:, 1] @@ -2056,9 +2056,9 @@ def run(list_packed_vars): output_ds_all_stats['glac_prec_monthly_mad'].values[ 0, : ] = output_glac_prec_monthly_stats[:, 1] - output_ds_all_stats['glac_acc_monthly_mad'].values[ - 0, : - ] = output_glac_acc_monthly_stats[:, 1] + output_ds_all_stats['glac_acc_monthly_mad'].values[0, :] = ( + output_glac_acc_monthly_stats[:, 1] + ) output_ds_all_stats['glac_refreeze_monthly_mad'].values[ 0, : ] = output_glac_refreeze_monthly_stats[:, 1] @@ -2142,9 +2142,9 @@ def run(list_packed_vars): output_binned.create_xr_ds() output_ds_binned_stats = output_binned.get_xr_ds() # fill values - output_ds_binned_stats['bin_distance'].values[ - 0, : - ] = output_glac_bin_dist + output_ds_binned_stats['bin_distance'].values[0, :] = ( + output_glac_bin_dist + ) output_ds_binned_stats['bin_surface_h_initial'].values[ 0, : ] = surface_h_initial @@ -2310,12 +2310,12 @@ def main(): args = parser.parse_args() # date range check try: - assert ( - args.ref_startyear < args.ref_endyear - ), f'ref_startyear [{args.ref_startyear}] must be less than ref_endyear [{args.ref_endyear}]' - assert ( - args.sim_startyear < args.sim_endyear - ), f'sim_startyear [{args.sim_startyear}] must be less than sim_endyear [{args.sim_endyear}]' + assert args.ref_startyear < args.ref_endyear, ( + f'ref_startyear [{args.ref_startyear}] must be less than ref_endyear [{args.ref_endyear}]' + ) + assert args.sim_startyear < args.sim_endyear, ( + f'sim_startyear [{args.sim_startyear}] must be less than sim_endyear [{args.sim_endyear}]' + ) except AssertionError as err: print('error: ', err) sys.exit(1) diff --git a/pygem/class_climate.py b/pygem/class_climate.py index 48748de6..09d2e1c9 100755 --- a/pygem/class_climate.py +++ b/pygem/class_climate.py @@ -44,9 +44,9 @@ def __init__(self, name=str(), sim_climate_scenario=str(), realization=None): """ if pygem_prms['rgi']['rgi_lon_colname'] not in ['CenLon_360']: - assert ( - 1 == 0 - ), 'Longitude does not use 360 degrees. Check how negative values are handled!' + assert 1 == 0, ( + 'Longitude does not use 360 degrees. Check how negative values are handled!' + ) # Source of climate data self.name = name diff --git a/pygem/oggm_compat.py b/pygem/oggm_compat.py index a8bb6614..e7c95a3c 100755 --- a/pygem/oggm_compat.py +++ b/pygem/oggm_compat.py @@ -44,7 +44,7 @@ def single_flowline_glacier_directory( prepro_border=pygem_prms['oggm']['border'], logging_level=pygem_prms['oggm']['logging_level'], has_internet=pygem_prms['oggm']['has_internet'], - working_dir=f"{pygem_prms['root']}/{pygem_prms['oggm']['oggm_gdir_relpath']}", + working_dir=f'{pygem_prms["root"]}/{pygem_prms["oggm"]["oggm_gdir_relpath"]}', ): """Prepare a GlacierDirectory for PyGEM (single flowline to start with) @@ -148,7 +148,7 @@ def single_flowline_glacier_directory_with_calving( k_calving=1, logging_level=pygem_prms['oggm']['logging_level'], has_internet=pygem_prms['oggm']['has_internet'], - working_dir=f"{pygem_prms['root']}/{pygem_prms['oggm']['oggm_gdir_relpath']}", + working_dir=f'{pygem_prms["root"]}/{pygem_prms["oggm"]["oggm_gdir_relpath"]}', facorrected=pygem_prms['setup']['include_frontalablation'], ): """Prepare a GlacierDirectory for PyGEM (single flowline to start with) diff --git a/pygem/shop/meltextent_and_snowline_1d.py b/pygem/shop/meltextent_and_snowline_1d.py index d52743e2..863eb6e3 100644 --- a/pygem/shop/meltextent_and_snowline_1d.py +++ b/pygem/shop/meltextent_and_snowline_1d.py @@ -7,9 +7,9 @@ """ # Built-in libaries -import os import datetime import logging +import os import pandas as pd @@ -130,16 +130,18 @@ def validate_meltextent_1d_structure(data): raise ValueError(f"'direction' must be a series of length {len(dates)}.") if not all(isinstance(x, str) for x in direction): raise ValueError("All 'direction' values must be strings.") - + # Validate reference DEM year dem_year = data['ref_dem_year'].dropna().unique() if len(dem_year) != 1: raise ValueError( f"'ref_dem_year' must have exactly one unique value, " - f"but found {len(dem_year)}: {dem_year}" + f'but found {len(dem_year)}: {dem_year}' ) if not isinstance(dem_year, (int)): - raise TypeError(f"'ref_dem_year' must be an integer, but got {dem_year} ({type(dem_year).__name__}).") + raise TypeError( + f"'ref_dem_year' must be an integer, but got {dem_year} ({type(dem_year).__name__})." + ) return True @@ -251,16 +253,18 @@ def validate_snowline_1d_structure(data): raise ValueError(f"'direction' must be a series of length {len(dates)}.") if not all(isinstance(x, str) for x in direction): raise ValueError("All 'direction' values must be strings.") - + # Validate reference DEM year dem_year = data['ref_dem_year'].dropna().unique() if len(dem_year) != 1: raise ValueError( f"'ref_dem_year' must have exactly one unique value, " - f"but found {len(dem_year)}: {dem_year}" + f'but found {len(dem_year)}: {dem_year}' ) if not isinstance(dem_year, (int)): - raise TypeError(f"'ref_dem_year' must be an integer, but got {dem_year} ({type(dem_year).__name__}).") + raise TypeError( + f"'ref_dem_year' must be an integer, but got {dem_year} ({type(dem_year).__name__})." + ) return True From 95e7f5c5a3d401b27d506ea5af603ad857a03ca8 Mon Sep 17 00:00:00 2001 From: "brandon s. tober" Date: Thu, 9 Oct 2025 11:50:52 -0400 Subject: [PATCH 5/7] Update ruff ignores --- pyproject.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index dc276675..85565cb4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -81,6 +81,7 @@ ignore = [ "B007", # Loop control variable not used within loop body "B008", # Function call `range` in argument defaults "B023", # Function definition does not bind loop variable + "B905", # Missing explicit `strict=` parameter in `zip()` call "C405", # Unnecessary list literal "C408", # Unnecessary `dict()` call "C414", # Unnecessary `list()` call @@ -97,4 +98,4 @@ ignore = [ [tool.coverage.report] omit = ["pygem/tests/*"] show_missing = true -skip_empty = true +skip_empty = true \ No newline at end of file From fea425ce8f599163be32875b4ac7b600c092df81 Mon Sep 17 00:00:00 2001 From: "brandon s. tober" Date: Thu, 9 Oct 2025 12:19:31 -0400 Subject: [PATCH 6/7] Ruffed --- pygem/bin/run/run_calibration.py | 1 + pygem/bin/run/run_simulation.py | 40 ++++++++++++++------------------ 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/pygem/bin/run/run_calibration.py b/pygem/bin/run/run_calibration.py index 307b2429..be0f01ed 100755 --- a/pygem/bin/run/run_calibration.py +++ b/pygem/bin/run/run_calibration.py @@ -2051,6 +2051,7 @@ def must_melt(kp, tbias, ddfsnow, **kwargs): ], progress_bar=args.progress_bar, ) + ) # Check condition at the end if (m_chain_z[:, 0] == m_chain_z[0, 0]).all(): diff --git a/pygem/bin/run/run_simulation.py b/pygem/bin/run/run_simulation.py index 0f8cd94d..5fb3308d 100755 --- a/pygem/bin/run/run_simulation.py +++ b/pygem/bin/run/run_simulation.py @@ -480,18 +480,16 @@ def run(list_packed_vars): args.ref_startyear, ) # Precipitation bias correction - ( - gcm_prec_adj, - gcm_elev_adj, - gcm_prec_biasadj_frac, - ) = gcmbiasadj.prec_biasadj_opt1( - ref_prec, - ref_elev, - gcm_prec, - dates_table_ref, - dates_table_full, - args.sim_startyear, - args.ref_startyear, + gcm_prec_adj, gcm_elev_adj, gcm_prec_biasadj_frac = ( + gcmbiasadj.prec_biasadj_opt1( + ref_prec, + ref_elev, + gcm_prec, + dates_table_ref, + dates_table_full, + args.sim_startyear, + args.ref_startyear, + ) ) # OPTION 2: Adjust temp and prec using Huss and Hock (2015) elif args.option_bias_adjustment == 2: @@ -506,16 +504,14 @@ def run(list_packed_vars): args.ref_startyear, ) # Precipitation bias correction - ( - gcm_prec_adj, - gcm_elev_adj, - gcm_prec_biasadj_frac, - ) = gcmbiasadj.prec_biasadj_HH2015( - ref_prec, - ref_elev, - gcm_prec, - dates_table_ref, - dates_table_full, + gcm_prec_adj, gcm_elev_adj, gcm_prec_biasadj_frac = ( + gcmbiasadj.prec_biasadj_HH2015( + ref_prec, + ref_elev, + gcm_prec, + dates_table_ref, + dates_table_full, + ) ) # OPTION 3: Adjust temp and prec using quantile delta mapping, Cannon et al. (2015) elif args.option_bias_adjustment == 3: From b2450f99b91a8445cd56f84c8ed3ff172d8a223c Mon Sep 17 00:00:00 2001 From: "brandon s. tober" Date: Thu, 9 Oct 2025 13:16:28 -0400 Subject: [PATCH 7/7] Handle len() == 1 lat/lon --- pygem/bin/run/run_calibration.py | 13 ++++-- .../run/run_calibration_frontalablation.py | 8 ++-- pygem/bin/run/run_calibration_reg_glena.py | 12 ++++-- pygem/bin/run/run_inversion.py | 6 +-- pygem/bin/run/run_simulation.py | 25 ++++++++---- pygem/bin/run/run_spinup.py | 6 +-- pygem/class_climate.py | 40 ++++++++++++------- 7 files changed, 70 insertions(+), 40 deletions(-) diff --git a/pygem/bin/run/run_calibration.py b/pygem/bin/run/run_calibration.py index be0f01ed..036a5c16 100755 --- a/pygem/bin/run/run_calibration.py +++ b/pygem/bin/run/run_calibration.py @@ -565,17 +565,17 @@ def run(list_packed_vars): gcm = class_climate.GCM(name=ref_climate_name) # Air temperature [degC] gcm_temp, gcm_dates = gcm.importGCMvarnearestneighbor_xarray( - gcm.temp_fn, gcm.temp_vn, main_glac_rgi, dates_table + gcm.temp_fn, gcm.temp_vn, main_glac_rgi, dates_table, verbose=debug ) if pygem_prms['mb']['option_ablation'] == 2 and ref_climate_name in ['ERA5']: gcm_tempstd, gcm_dates = gcm.importGCMvarnearestneighbor_xarray( - gcm.tempstd_fn, gcm.tempstd_vn, main_glac_rgi, dates_table + gcm.tempstd_fn, gcm.tempstd_vn, main_glac_rgi, dates_table, verbose=debug ) else: gcm_tempstd = np.zeros(gcm_temp.shape) # Precipitation [m] gcm_prec, gcm_dates = gcm.importGCMvarnearestneighbor_xarray( - gcm.prec_fn, gcm.prec_vn, main_glac_rgi, dates_table + gcm.prec_fn, gcm.prec_vn, main_glac_rgi, dates_table, verbose=debug ) # Elevation [m asl] gcm_elev = gcm.importGCMfxnearestneighbor_xarray( @@ -583,7 +583,12 @@ def run(list_packed_vars): ) # Lapse rate [degC m-1] (always monthly) gcm_lr, gcm_dates = gcm.importGCMvarnearestneighbor_xarray( - gcm.lr_fn, gcm.lr_vn, main_glac_rgi, dates_table, upscale_var_timestep=True + gcm.lr_fn, + gcm.lr_vn, + main_glac_rgi, + dates_table, + upscale_var_timestep=True, + verbose=debug, ) # ===== LOOP THROUGH GLACIERS TO RUN CALIBRATION ===== diff --git a/pygem/bin/run/run_calibration_frontalablation.py b/pygem/bin/run/run_calibration_frontalablation.py index efe2084b..9e347e7b 100644 --- a/pygem/bin/run/run_calibration_frontalablation.py +++ b/pygem/bin/run/run_calibration_frontalablation.py @@ -136,17 +136,17 @@ def reg_calving_flux( gcm = class_climate.GCM(name=args.ref_climate_name) # Air temperature [degC] gcm_temp, gcm_dates = gcm.importGCMvarnearestneighbor_xarray( - gcm.temp_fn, gcm.temp_vn, main_glac_rgi, dates_table + gcm.temp_fn, gcm.temp_vn, main_glac_rgi, dates_table, verbose=debug ) if pygem_prms['mb']['option_ablation'] == 2 and args.ref_climate_name in ['ERA5']: gcm_tempstd, gcm_dates = gcm.importGCMvarnearestneighbor_xarray( - gcm.tempstd_fn, gcm.tempstd_vn, main_glac_rgi, dates_table + gcm.tempstd_fn, gcm.tempstd_vn, main_glac_rgi, dates_table, verbose=debug ) else: gcm_tempstd = np.zeros(gcm_temp.shape) # Precipitation [m] gcm_prec, gcm_dates = gcm.importGCMvarnearestneighbor_xarray( - gcm.prec_fn, gcm.prec_vn, main_glac_rgi, dates_table + gcm.prec_fn, gcm.prec_vn, main_glac_rgi, dates_table, verbose=debug ) # Elevation [m asl] gcm_elev = gcm.importGCMfxnearestneighbor_xarray( @@ -154,7 +154,7 @@ def reg_calving_flux( ) # Lapse rate [degC m-1] gcm_lr, gcm_dates = gcm.importGCMvarnearestneighbor_xarray( - gcm.lr_fn, gcm.lr_vn, main_glac_rgi, dates_table + gcm.lr_fn, gcm.lr_vn, main_glac_rgi, dates_table, verbose=debug ) # ===== CALIBRATE ALL THE GLACIERS AT ONCE ===== diff --git a/pygem/bin/run/run_calibration_reg_glena.py b/pygem/bin/run/run_calibration_reg_glena.py index 5efd9dae..29cfd19e 100644 --- a/pygem/bin/run/run_calibration_reg_glena.py +++ b/pygem/bin/run/run_calibration_reg_glena.py @@ -341,17 +341,21 @@ def main(): gcm = class_climate.GCM(name=sim_climate_name) # Air temperature [degC] gcm_temp, gcm_dates = gcm.importGCMvarnearestneighbor_xarray( - gcm.temp_fn, gcm.temp_vn, main_glac_rgi_subset, dates_table + gcm.temp_fn, gcm.temp_vn, main_glac_rgi_subset, dates_table, verbose=debug ) if pygem_prms['mbmod']['option_ablation'] == 2 and sim_climate_name in ['ERA5']: gcm_tempstd, gcm_dates = gcm.importGCMvarnearestneighbor_xarray( - gcm.tempstd_fn, gcm.tempstd_vn, main_glac_rgi_subset, dates_table + gcm.tempstd_fn, + gcm.tempstd_vn, + main_glac_rgi_subset, + dates_table, + verbose=debug, ) else: gcm_tempstd = np.zeros(gcm_temp.shape) # Precipitation [m] gcm_prec, gcm_dates = gcm.importGCMvarnearestneighbor_xarray( - gcm.prec_fn, gcm.prec_vn, main_glac_rgi_subset, dates_table + gcm.prec_fn, gcm.prec_vn, main_glac_rgi_subset, dates_table, verbose=debug ) # Elevation [m asl] gcm_elev = gcm.importGCMfxnearestneighbor_xarray( @@ -359,7 +363,7 @@ def main(): ) # Lapse rate [degC m-1] gcm_lr, gcm_dates = gcm.importGCMvarnearestneighbor_xarray( - gcm.lr_fn, gcm.lr_vn, main_glac_rgi_subset, dates_table + gcm.lr_fn, gcm.lr_vn, main_glac_rgi_subset, dates_table, verbose=debug ) # ===== RUN MASS BALANCE ===== diff --git a/pygem/bin/run/run_inversion.py b/pygem/bin/run/run_inversion.py index 323224fc..aff4c5e6 100644 --- a/pygem/bin/run/run_inversion.py +++ b/pygem/bin/run/run_inversion.py @@ -70,11 +70,11 @@ def run( # Air temperature [degC] temp, _ = ref_clim.importGCMvarnearestneighbor_xarray( - ref_clim.temp_fn, ref_clim.temp_vn, main_glac_rgi, dt + ref_clim.temp_fn, ref_clim.temp_vn, main_glac_rgi, dt, verbose=debug ) # Precipitation [m] prec, _ = ref_clim.importGCMvarnearestneighbor_xarray( - ref_clim.prec_fn, ref_clim.prec_vn, main_glac_rgi, dt + ref_clim.prec_fn, ref_clim.prec_vn, main_glac_rgi, dt, verbose=debug ) # Elevation [m asl] elev = ref_clim.importGCMfxnearestneighbor_xarray( @@ -82,7 +82,7 @@ def run( ) # Lapse rate [degC m-1] lr, _ = ref_clim.importGCMvarnearestneighbor_xarray( - ref_clim.lr_fn, ref_clim.lr_vn, main_glac_rgi, dt + ref_clim.lr_fn, ref_clim.lr_vn, main_glac_rgi, dt, verbose=debug ) # load prior regionally averaged modelprms (from Rounce et al. 2023) diff --git a/pygem/bin/run/run_simulation.py b/pygem/bin/run/run_simulation.py index 5fb3308d..bc5f9f38 100755 --- a/pygem/bin/run/run_simulation.py +++ b/pygem/bin/run/run_simulation.py @@ -430,17 +430,17 @@ def run(list_packed_vars): # ----- Select Temperature and Precipitation Data ----- # Air temperature [degC] gcm_temp, gcm_dates = gcm.importGCMvarnearestneighbor_xarray( - gcm.temp_fn, gcm.temp_vn, main_glac_rgi, dates_table_full + gcm.temp_fn, gcm.temp_vn, main_glac_rgi, dates_table_full, verbose=debug ) ref_temp, ref_dates = ref_gcm.importGCMvarnearestneighbor_xarray( - ref_gcm.temp_fn, ref_gcm.temp_vn, main_glac_rgi, dates_table_ref + ref_gcm.temp_fn, ref_gcm.temp_vn, main_glac_rgi, dates_table_ref, verbose=debug ) # Precipitation [m] gcm_prec, gcm_dates = gcm.importGCMvarnearestneighbor_xarray( - gcm.prec_fn, gcm.prec_vn, main_glac_rgi, dates_table_full + gcm.prec_fn, gcm.prec_vn, main_glac_rgi, dates_table_full, verbose=debug ) ref_prec, ref_dates = ref_gcm.importGCMvarnearestneighbor_xarray( - ref_gcm.prec_fn, ref_gcm.prec_vn, main_glac_rgi, dates_table_ref + ref_gcm.prec_fn, ref_gcm.prec_vn, main_glac_rgi, dates_table_ref, verbose=debug ) # Elevation [m asl] try: @@ -548,13 +548,17 @@ def run(list_packed_vars): ref_tempstd = np.zeros((main_glac_rgi.shape[0], dates_table_ref.shape[0])) elif pygem_prms['mb']['option_ablation'] == 2 and sim_climate_name in ['ERA5']: gcm_tempstd, gcm_dates = gcm.importGCMvarnearestneighbor_xarray( - gcm.tempstd_fn, gcm.tempstd_vn, main_glac_rgi, dates_table + gcm.tempstd_fn, gcm.tempstd_vn, main_glac_rgi, dates_table, verbose=debug ) ref_tempstd = gcm_tempstd elif pygem_prms['mb']['option_ablation'] == 2 and args.ref_climate_name in ['ERA5']: # Compute temp std based on reference climate data ref_tempstd, ref_dates = ref_gcm.importGCMvarnearestneighbor_xarray( - ref_gcm.tempstd_fn, ref_gcm.tempstd_vn, main_glac_rgi, dates_table_ref + ref_gcm.tempstd_fn, + ref_gcm.tempstd_vn, + main_glac_rgi, + dates_table_ref, + verbose=debug, ) # Monthly average from reference climate data gcm_tempstd = gcmbiasadj.monthly_avg_array_rolled( @@ -567,13 +571,18 @@ def run(list_packed_vars): # Lapse rate if sim_climate_name in ['ERA-Interim', 'ERA5']: gcm_lr, gcm_dates = gcm.importGCMvarnearestneighbor_xarray( - gcm.lr_fn, gcm.lr_vn, main_glac_rgi, dates_table, upscale_var_timestep=True + gcm.lr_fn, + gcm.lr_vn, + main_glac_rgi, + dates_table, + upscale_var_timestep=True, + verbose=debug, ) ref_lr = gcm_lr else: # Compute lapse rates based on reference climate data ref_lr, ref_dates = ref_gcm.importGCMvarnearestneighbor_xarray( - ref_gcm.lr_fn, ref_gcm.lr_vn, main_glac_rgi, dates_table_ref + ref_gcm.lr_fn, ref_gcm.lr_vn, main_glac_rgi, dates_table_ref, verbose=debug ) # Monthly average from reference climate data gcm_lr = gcmbiasadj.monthly_avg_array_rolled( diff --git a/pygem/bin/run/run_spinup.py b/pygem/bin/run/run_spinup.py index 102e93a8..76f48554 100644 --- a/pygem/bin/run/run_spinup.py +++ b/pygem/bin/run/run_spinup.py @@ -39,11 +39,11 @@ def run(glacno_list, mb_model_params, debug=False, **kwargs): # Air temperature [degC] temp, _ = ref_clim.importGCMvarnearestneighbor_xarray( - ref_clim.temp_fn, ref_clim.temp_vn, main_glac_rgi, dt + ref_clim.temp_fn, ref_clim.temp_vn, main_glac_rgi, dt, verbose=debug ) # Precipitation [m] prec, _ = ref_clim.importGCMvarnearestneighbor_xarray( - ref_clim.prec_fn, ref_clim.prec_vn, main_glac_rgi, dt + ref_clim.prec_fn, ref_clim.prec_vn, main_glac_rgi, dt, verbose=debug ) # Elevation [m asl] elev = ref_clim.importGCMfxnearestneighbor_xarray( @@ -51,7 +51,7 @@ def run(glacno_list, mb_model_params, debug=False, **kwargs): ) # Lapse rate [degC m-1] lr, _ = ref_clim.importGCMvarnearestneighbor_xarray( - ref_clim.lr_fn, ref_clim.lr_vn, main_glac_rgi, dt + ref_clim.lr_fn, ref_clim.lr_vn, main_glac_rgi, dt, verbose=debug ) # load prior regionally averaged modelprms (from Rounce et al. 2023) diff --git a/pygem/class_climate.py b/pygem/class_climate.py index 09d2e1c9..7d964067 100755 --- a/pygem/class_climate.py +++ b/pygem/class_climate.py @@ -9,6 +9,7 @@ class of climate data and functions associated with manipulating the dataset to """ import os +import warnings import numpy as np @@ -456,6 +457,7 @@ def importGCMvarnearestneighbor_xarray( dates_table, realizations=['r1i1p1f1', 'r4i1p1f1'], upscale_var_timestep=False, + verbose=False, ): """ Import time series of variables and extract nearest neighbor. @@ -698,21 +700,31 @@ def importGCMvarnearestneighbor_xarray( rgi_id = main_glac_rgi[ pygem_prms['rgi']['rgi_glacno_float_colname'] ].values[i] - lat_res = abs(np.diff(data[vn][self.lat_vn].values)[0]) - lon_res = abs(np.diff(data[vn][self.lon_vn].values)[0]) - lat_dd = abs( - main_glac_rgi[self.rgi_lat_colname].values[i] - - data[vn][self.lat_vn].values[latlon[0]] - ) - lon_dd = abs( - main_glac_rgi[self.rgi_lon_colname].values[i] - - data[vn][self.lon_vn].values[latlon[1]] - ) + if (len(data[vn][self.lat_vn].values) == 1) or ( + len(data[vn][self.lon_vn].values) == 1 + ): + if verbose: + warnings.warn( + f'{vn} data has only one latitude or longitude value; check that the correct data is being used', + Warning, + stacklevel=2, + ) + else: + lat_res = abs(np.diff(data[vn][self.lat_vn].values)[0]) + lon_res = abs(np.diff(data[vn][self.lon_vn].values)[0]) + lat_dd = abs( + main_glac_rgi[self.rgi_lat_colname].values[i] + - data[vn][self.lat_vn].values[latlon[0]] + ) + lon_dd = abs( + main_glac_rgi[self.rgi_lon_colname].values[i] + - data[vn][self.lon_vn].values[latlon[1]] + ) - assert lat_dd <= lat_res and lon_dd <= lon_res, ( - f'Climate data pixel for {vn} too from glacier {rgi_id}: Δlat={lat_dd:.3f}, ' - + f'Δlon={lon_dd:.3f}, res=({lat_res:.3f}, {lon_res:.3f})' - ) + assert lat_dd <= lat_res and lon_dd <= lon_res, ( + f'Climate data pixel for {vn} too from glacier {rgi_id}: Δlat={lat_dd:.3f}, ' + + f'Δlon={lon_dd:.3f}, res=({lat_res:.3f}, {lon_res:.3f})' + ) # Convert to series glac_variable_series = np.array(