diff --git a/README.md b/README.md index 88d86f7..bb0d400 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Checks ISMIP7 NetCDF simulation datasets for compliance with the [ISMIP7 data re 4. **Time** — time dimension is present, unlimited, and monotonically increasing; annual cadence; experiment start/end dates and duration match `experiments_ismip7.csv`, accounting for the variable type (ST or FL, see [Time encoding](#time-encoding)). 5. **Attributes** — required global and coordinate attributes are present and have correct values; `standard_name` matches data request; `_FillValue` equals the NetCDF4 default for the variable's dtype; variable and time are float32; `scale_factor` and `add_offset` are not allowed. -Compliance criteria are defined in `conventions/ISMIP7_variable_request.xlsx` (variable metadata) and `experiments_ismip7.csv` (valid experiment year ranges and durations). +Compliance criteria are defined in `conventions/ISMIP7_variable_request.csv` (variable metadata) and `experiments_ismip7.csv` (valid experiment year ranges and durations). --- @@ -38,7 +38,7 @@ conda env create -f isschecker_env.yml conda activate isschecker ``` -Dependencies: Python 3.14, `numpy` 2.4, `pandas` 3.0, `openpyxl` 3.1, `xarray` 2026.4, `cftime` 1.6, `netCDF4` 1.7, `tqdm` 4.67. +Dependencies: Python 3.14, `numpy` 2.4, `pandas` 3.0, `xarray` 2026.4, `cftime` 1.6, `netCDF4` 1.7, `tqdm` 4.67. --- diff --git a/compliance_checker.py b/compliance_checker.py index 9877508..85fad6b 100755 --- a/compliance_checker.py +++ b/compliance_checker.py @@ -54,7 +54,7 @@ DEFAULT_VARIABLE_LIST = "ismip7_scalars" VARIABLE_LIST_CHOICES = ("ismip7_scalars", "ismip7_xyt", "ismip7") -VARIABLE_REQUEST_XLSX = os.path.join("conventions", "ISMIP7_variable_request.xlsx") +VARIABLE_REQUEST_CSV = os.path.join("conventions", "ISMIP7_variable_request.csv") EXPERIMENTS_ISMIP7_CSV_FILENAME = "experiments_ismip7.csv" @@ -162,7 +162,7 @@ def run_checker( ismip_var=ismip_var, mandatory_variables=mandatory_variables, experiments=experiments_ismip7, - criteria_file=VARIABLE_REQUEST_XLSX, + criteria_file=VARIABLE_REQUEST_CSV, ) log_path = os.path.join(source_path, "compliance_checker_log.txt") @@ -206,13 +206,13 @@ def _parse_args() -> argparse.Namespace: def _load_criteria(workdir: str, variable_list: str): - excel_path = os.path.join(workdir, VARIABLE_REQUEST_XLSX) + csv_path = os.path.join(workdir, VARIABLE_REQUEST_CSV) try: - df = pd.read_excel(excel_path, sheet_name="ISM") + df = pd.read_csv(csv_path) except IOError: print( "ERROR: Unable to open the variable request file. Is the path correct? " - + excel_path + + csv_path ) raise diff --git a/conventions/ISMIP7_variable_request.csv b/conventions/ISMIP7_variable_request.csv new file mode 100644 index 0000000..22e8cd4 --- /dev/null +++ b/conventions/ISMIP7_variable_request.csv @@ -0,0 +1,40 @@ +long_name,Dim,Type,Variable Name,standard_name,units,Mandatory (yes/no),Comment,min_value_ais,max_value_ais,min_value_gris,max_value_gris +Ice thickness,"x,y,t",ST,lithk,land_ice_thickness,m,yes,Ice thickness of the ice sheet,0,5000,0,5000 +Surface elevation,"x,y,t",ST,orog,surface_altitude,m,yes,Surface elevation of the ice sheet,0,4500,0,4500 +Bedrock elevation,"x,y,t",ST,topg,bedrock_altitude,m,yes,The bedrock topography (may change during the projections),-7000,4000,-4000,4000 +Ice base elevation,"x,y,t",ST,base,,m,yes,Should get standard name land_ice_temperature,-4000,4000,-4000,4000 +Geothermal heat flux,"x,y,t",FL,hfgeoubed,upward_geothermal_heat_flux_in_land_ice,W m-2,no,Geothermal Heat flux at the ice interface,0,0.3,0,0.3 +Surface mass balance flux,"x,y,t",FL,acabf,land_ice_surface_specific_mass_balance_flux,kg m-2 s-1,yes,Surface Mass Balance flux,-0.0006,0.001,-0.0006,0.001 +Basal mass balance flux beneath grounded ice,"x,y,t",FL,libmassbfgr,land_ice_basal_specific_mass_balance_flux,kg m-2 s-1,yes,Basal mass balance flux (only beneath grounded ice),-0.0003,0.0001,-0.0003,0.0001 +Basal mass balance flux beneath floating ice,"x,y,t",FL,libmassbffl,land_ice_basal_specific_mass_balance_flux,kg m-2 s-1,yes,Basal mass balance flux (only beneath floating ice),-0.008,0.001,-0.008,0.001 +Ice thickness imbalance,"x,y,t",FL,dlithkdt,tendency_of_land_ice_thickness,m s-1,yes,dHdt,-0.0001,0.0001,-0.0001,0.0001 +Surface velocity in x,"x,y,t",ST,xvelsurf,land_ice_surface_x_velocity,m s-1,no,u-velocity at land ice surface,-0.0004,0.0004,-0.0008,0.0008 +Surface velocity in y,"x,y,t",ST,yvelsurf,land_ice_surface_y_velocity,m s-1,no,v-velocity at land ice surface,-0.0004,0.0004,-0.0008,0.0008 +Surface velocity in z,"x,y,t",ST,zvelsurf,land_ice_surface_upward_velocity,m s-1,no,w-velocity at land ice surface,-4.00E-06,4e-06,-8.00E-06,8e-06 +Basal velocity in x,"x,y,t",ST,xvelbase,land_ice_basal_x_velocity,m s-1,no,u-velocity at land ice base,-0.0004,0.0004,-0.0008,0.0008 +Basal velocity in y,"x,y,t",ST,yvelbase,land_ice_basal_y_velocity,m s-1,no,v-velocity at land ice base,-0.0004,0.0004,-0.0008,0.0008 +Basal velocity in z,"x,y,t",ST,zvelbase,land_ice_basal_upward_velocity,m s-1,no,w-velocity at land ice base,-5.00E-06,5e-06,-8.00E-06,8e-06 +Mean velocity in x,"x,y,t",ST,xvelmean,land_ice_vertical_mean_x_velocity,m s-1,yes,Vertical mean land ice velocity ,-0.0004,0.0004,-0.0008,0.0008 +Mean velocity in y,"x,y,t",ST,yvelmean,land_ice_vertical_mean_y_velocity,m s-1,yes,The vertical mean land ice velocity is the average from the bedrock to the surface of the ice,-0.0004,0.0004,-0.0008,0.0008 +Surface temperature,"x,y,t",ST,litemptop,temperature_at_top_of_ice_sheet_model,K,no,Ice temperature at surface,183,290,183,290 +Depth average temperature,"x,y,t",ST,litempavg,,K,no,Should get standard name land_ice_temperature,183,290,183,290 +Vertical Basal temperature gradient beneath grounded ice sheet,"x,y,t",ST,litempgradgr,,K m-1,no,"Vertical Basal temperature gradient beneath grounded ice sheet, should get a standard name",-10,10,-10,10 +Vertical Basal temperature gradient beneath floating ice shelf,"x,y,t",ST,litempgradfl,,K m-1,no,"Vertical Basal temperature gradient beneath floating ice shelf, should get a standard name",-10,10,-10,10 +Basal temperature beneath grounded ice sheet,"x,y,t",ST,litempbotgr,temperature_at_base_of_ice_sheet_model,K,no,Ice temperature at base of grounded ice sheet,183,290,183,290 +Basal temperature beneath floating ice shelf,"x,y,t",ST,litempbotfl,temperature_at_base_of_ice_sheet_model,K,no,Ice temperature at base of floating ice shelf,183,290,183,290 +Basal drag,"x,y,t",ST,strbasemag,land_ice_basal_drag,Pa,yes,Basal drag,0,300000,0,300000 +Calving flux,"x,y,t",FL,licalvf,land_ice_specific_mass_flux_due_to_calving,kg m-2 s-1,yes,Ice mass change resulting from iceberg calving. Only for grid cells in contact with ocean,-1E+11,0,-1E+11,0 +Ice front melt flux,"x,y,t",FL,lifmassbf,,kg m-2 s-1,yes,Ice mass change resulting from ice front melting. Only for grid cells in contact with ocean. Should get standard name: land_ice_specific_mass_flux_due_to_ice_front_melting,-1E+11,0,-1E+11,0 +Land ice area fraction,"x,y,t",ST,sftgif,land_ice_area_fraction,1,yes,"Fraction of grid cell covered by land ice (ice sheet, ice shelf, ice cap, glacier)",0,1,0,1 +Grounded ice sheet area fraction,"x,y,t",ST,sftgrf,grounded_ice_sheet_area_fraction,1,yes,"Fraction of grid cell covered by grounded ice sheet, where grounded indicates that the quantity correspond to the ice sheet that flows over bedrock",0,1,0,1 +Floating ice sheet area fraction,"x,y,t",ST,sftflf,floating_ice_shelf_area_fraction,1,yes,Fraction of grid cell covered by ice sheet flowing over seawater,0,1,0,1 +Total ice mass,t,ST,lim,land_ice_mass,kg,yes,"spatial integration, volume times density",0,1e+25,0,1e+25 +Mass above floatation,t,ST,limnsw,land_ice_mass_not_displacing_sea_water,kg,yes,"spatial integration, volume times density",0,1e+25,0,1e+25 +Grounded ice area,t,ST,iareagr,grounded_ice_sheet_area,m^2,yes,spatial integration,0,1e+25,0,1e+25 +Floating ice area,t,ST,iareafl,floating_ice_shelf_area,m^2,yes,spatial integration,0,1e+25,0,1e+25 +Total SMB flux,t,FL,tendacabf,tendency_of_land_ice_mass_due_to_surface_mass_balance,kg s-1,yes,spatial integration,0,1e+25,0,1e+25 +Total BMB flux beneath grounded ice,t,FL,tendlibmassbfgr,tendency_of_land_ice_mass_due_to_basal_mass_balance,kg s-1,yes,spatial integration,0,1e+25,0,1e+25 +Total BMB flux beneath floating ice,t,FL,tendlibmassbffl,tendency_of_land_ice_mass_due_to_basal_mass_balance,kg s-1,yes,spatial integration (computed beneath floating ice only),0,1e+25,0,1e+25 +Total calving flux,t,FL,tendlicalvf,tendency_of_land_ice_mass_due_to_calving,kg s-1,yes,spatial integration,0,1e+25,0,1e+25 +Total ice front melting flux,t,FL,tendlifmassbf,,kg s-1,yes,"spatial integration, should get standard name: tendency_of_land_ice_mass_due_to_ice_front_melting",0,1e+25,0,1e+25 +Total grounding line flux,t,FL,tendligroundf,,kg s-1,yes,"spatial integration, should get a standard name",0,1e+25,0,1e+25 \ No newline at end of file diff --git a/conventions/ISMIP7_variable_request.xlsx b/conventions/ISMIP7_variable_request.xlsx deleted file mode 100644 index 92a42d4..0000000 Binary files a/conventions/ISMIP7_variable_request.xlsx and /dev/null differ diff --git a/generate/README.md b/generate/README.md index 1818f12..ed482c8 100644 --- a/generate/README.md +++ b/generate/README.md @@ -65,6 +65,6 @@ python generate/generate_test_files.py --grid GrIS_16000m --scenario ctrl \ ## Notes -- Variable metadata is read from `conventions/ISMIP7_variable_request.xlsx`; grid definitions from the top-level `gdfs/` directory. +- Variable metadata is read from `conventions/ISMIP7_variable_request.csv`; grid definitions from the top-level `gdfs/` directory. - `group`, `model`, `contact_name`, and `contact_email` are hardcoded in `create_netcdf_file()` to synthetic defaults — edit there to customise. - Generated files are synthetic and intended for testing the compliance checker, not for scientific use. diff --git a/generate/generate_test_files.py b/generate/generate_test_files.py index 348a827..828cf57 100644 --- a/generate/generate_test_files.py +++ b/generate/generate_test_files.py @@ -84,14 +84,14 @@ def parse_grid_file(gdf_file): return grid_params -def read_variable_criteria(excel_file, include_non_mandatory=False): +def read_variable_criteria(csv_file, include_non_mandatory=False): """ - Read variable criteria from Excel file. + Read variable criteria from CSV file. Parameters ---------- - excel_file : str - Path to the Excel file + csv_file : str + Path to the CSV file include_non_mandatory : bool Whether to include non-mandatory variables @@ -104,8 +104,8 @@ def read_variable_criteria(excel_file, include_non_mandatory=False): variables = {} - # Read the Excel file - df = pd.read_excel(excel_file, sheet_name='ISM') + # Read the CSV file + df = pd.read_csv(csv_file) # Filter out rows that don't have variable names df = df.dropna(subset=['Variable Name']) @@ -128,13 +128,13 @@ def read_variable_criteria(excel_file, include_non_mandatory=False): variables[var_name] = { 'dimensions': dimensions, 'type': row['Type'], - 'description': row['long_name'], # Use long_name from Excel + 'description': row['long_name'], # Use long_name from CSV 'standard_name': row['standard_name'] if pd.notna(row['standard_name']) else '', 'units': str(row['units']) if pd.notna(row['units']) else '', 'mandatory': row['Mandatory (yes/no)'].lower() == 'yes', } - # Collect any min_/max_ columns (case-insensitive) from the Excel sheet + # Collect any min_/max_ columns (case-insensitive) from the CSV # Normalize keys to lowercase (e.g., 'min_gris', 'max_ais') and store for col in df.columns: try: @@ -261,13 +261,13 @@ def create_netcdf_file(output_file, grid_name='GrIS_16000m', scenario='ctrl', st xinc = grid_params['xinc'] yinc = grid_params['yinc'] - # Read variable criteria from Excel file - excel_file = conventions_dir / 'ISMIP7_variable_request.xlsx' - if not excel_file.exists(): - print(f"Warning: {excel_file} not found") + # Read variable criteria from CSV file + csv_file = conventions_dir / 'ISMIP7_variable_request.csv' + if not csv_file.exists(): + print(f"Warning: {csv_file} not found") variables = {} else: - variables = read_variable_criteria(str(excel_file), include_non_mandatory) + variables = read_variable_criteria(str(csv_file), include_non_mandatory) # Create coordinate arrays x = np.arange(nx, dtype=np.float32) * xinc + xfirst @@ -688,7 +688,7 @@ def create_multiple_files(output_dir=None, n_files=3, conventions_dir=None, parser.add_argument( '--include-non-mandatory', action='store_true', - help='Include non-mandatory variables from the ISM Excel variable list' + help='Include non-mandatory variables from the ISM CSV variable list' ) parser.add_argument( '--multiple', diff --git a/isschecker_env.yml b/isschecker_env.yml index 7e8a849..2823a79 100644 --- a/isschecker_env.yml +++ b/isschecker_env.yml @@ -8,7 +8,6 @@ dependencies: # Core scientific python - numpy=2.4.3 - pandas=3.0.2 - - openpyxl=3.1.5 - pytest=8.4.2 - tqdm=4.67.3