Skip to content

Commit

Permalink
Merge branch 'support_mks' into development
Browse files Browse the repository at this point in the history
  • Loading branch information
mnlevy1981 committed Aug 31, 2023
2 parents 523461f + 07d58a2 commit 951d1cd
Show file tree
Hide file tree
Showing 46 changed files with 2,747 additions and 1,231 deletions.
9 changes: 7 additions & 2 deletions MARBL_tools/MARBL_generate_settings_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@
A file that overrides values in JSON (default: None)
-o SETTINGS_FILE_OUT, --settings_file_out SETTINGS_FILE_OUT
Name of file to be written (default: marbl.settings)
-u {cgs,mks}, --unit_system {cgs,mks}
Unit system for parameter values (default: cgs)
"""

#######################################
Expand Down Expand Up @@ -85,6 +86,10 @@ def _parse_args(marbl_root):
parser.add_argument('-o', '--settings_file_out', action='store', dest='settings_file_out', default='marbl.settings',
help='Name of file to be written')

# Command line argument to where to write the settings file being generated
parser.add_argument('-u', '--unit_system', action='store', dest='unit_system', default='cgs',
choices=['cgs', 'mks'], help='Unit system for parameter values')

return parser.parse_args()

#######################################
Expand All @@ -103,7 +108,7 @@ def _parse_args(marbl_root):
logging.basicConfig(format='%(levelname)s (%(funcName)s): %(message)s', level=logging.DEBUG)

from MARBL_tools import MARBL_settings_class
DefaultSettings = MARBL_settings_class(args.default_settings_file, args.saved_state_vars_source, args.grid, args.settings_file_in)
DefaultSettings = MARBL_settings_class(args.default_settings_file, args.saved_state_vars_source, args.grid, args.settings_file_in, args.unit_system)

# Write the settings file
generate_settings_file(DefaultSettings, args.settings_file_out)
86 changes: 65 additions & 21 deletions MARBL_tools/MARBL_settings_file_class.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class MARBL_settings_class(object):
# CONSTRUCTOR #
###############

def __init__(self, default_settings_file, saved_state_vars_source="settings_file", grid=None, input_file=None):
def __init__(self, default_settings_file, saved_state_vars_source="settings_file", grid=None, input_file=None, unit_system='cgs'):
""" Class constructor: set up a dictionary of config keywords for when multiple
default values are provided, read the JSON file, and then populate
self.settings_dict and self.tracers_dict.
Expand Down Expand Up @@ -47,7 +47,7 @@ def __init__(self, default_settings_file, saved_state_vars_source="settings_file
self.tracers_dict = None
for cat_name in self.get_category_names():
for var_name in self.get_variable_names(cat_name):
self._process_variable_value(cat_name, var_name)
self._process_variable_value(cat_name, var_name, unit_system)
# 5b. Need tracer count after determining PFT_derived_types, which means
# determining which tracers are active
if cat_name == "PFT_derived_types":
Expand All @@ -63,6 +63,11 @@ def __init__(self, default_settings_file, saved_state_vars_source="settings_file
logger.error(message)
MARBL_tools.abort(1)

if unit_system not in ['cgs', 'mks']:
message = "'%s' is not a valid unit system" % unit_system
logger.error(message)
MARBL_tools.abort(1)

################################################################################
# PUBLIC CLASS METHODS #
################################################################################
Expand Down Expand Up @@ -211,7 +216,7 @@ def _get_tracers(self):

################################################################################

def _process_variable_value(self, category_name, variable_name):
def _process_variable_value(self, category_name, variable_name, unit_system):
""" For a given variable in a given category, call _update_settings_dict()
* If variable is a derived type, _update_settings_dict() needs to be called element by element
Expand All @@ -221,7 +226,7 @@ def _process_variable_value(self, category_name, variable_name):

if not isinstance(this_var["datatype"], dict):
this_var['_list_of_settings_names'] = []
self._update_settings_dict(this_var, variable_name)
self._update_settings_dict(this_var, variable_name, unit_system)
return

# Process derived type!
Expand Down Expand Up @@ -259,15 +264,15 @@ def _process_variable_value(self, category_name, variable_name):
this_component['_list_of_settings_names']
except:
this_component['_list_of_settings_names'] = []
self._update_settings_dict(this_component, base_name+key, base_name)
self._update_settings_dict(this_component, base_name+key, unit_system, base_name)

if append_to_keys:
# Remove PFT-specific key
del self._config_keyword[-1]

################################################################################

def _update_settings_dict(self, this_var, var_name, base_name=''):
def _update_settings_dict(self, this_var, var_name, unit_system, base_name=''):
""" For a given variable in a given category, add to the self.settings_dict dictionary
* For derived types, user passes in component as well as base_name ("variable_name%")
* For arrays, multiple entries will be added to self.settings_dict
Expand All @@ -290,34 +295,41 @@ def _update_settings_dict(self, this_var, var_name, base_name=''):
# For each element, get value from either input file or JSON
for n, elem_index in enumerate(_get_array_info(array_len, self.settings_dict, self.tracers_dict, base_name)):
full_name = var_name + elem_index
var_value = _get_var_value(full_name, this_var, self._config_keyword, self._input_dict)
this_var['_list_of_settings_names'].append(full_name)
self.settings_dict[full_name] = dict()
self.settings_dict[full_name]['attrs'] = dict()
for key in settings_dict_attrs:
if key == "units":
self.settings_dict[full_name]['attrs'][key] = _get_correct_units(this_var[key], unit_system)
else:
self.settings_dict[full_name]['attrs'][key] = this_var[key]
var_value = _get_var_value(full_name, this_var, self._config_keyword, self._input_dict, this_var["units"], unit_system)
self.settings_dict[full_name]['attrs'][key] = _get_correct_units(this_var[key], unit_system)
if isinstance(var_value, list):
if this_var["datatype"] == "string" and n>=len(var_value):
self.settings_dict[full_name]['value'] = '""'
else:
self.settings_dict[full_name]['value'] = _translate_JSON_value(var_value[n], this_var["datatype"])
self.settings_dict[full_name]['value'] = _translate_JSON_value(var_value[n], this_var["datatype"], this_var["units"], unit_system)
else:
self.settings_dict[full_name]['value'] = var_value
this_var['_list_of_settings_names'].append(full_name)
for key in settings_dict_attrs:
self.settings_dict[full_name]['attrs'][key] = this_var[key]

else:
this_var['_list_of_settings_names'].append(var_name)
# get value from either input file or JSON
self.settings_dict[var_name] = dict()
self.settings_dict[var_name]['attrs'] = dict()
self.settings_dict[var_name]['value'] = _get_var_value(var_name, this_var, self._config_keyword, self._input_dict)
for key in settings_dict_attrs:
self.settings_dict[var_name]['attrs'][key] = this_var[key]
this_var['_list_of_settings_names'].append(var_name)
if key == "units":
self.settings_dict[var_name]['attrs'][key] = _get_correct_units(this_var[key], unit_system)
else:
self.settings_dict[var_name]['attrs'][key] = this_var[key]
self.settings_dict[var_name]['value'] = _get_var_value(var_name, this_var, self._config_keyword, self._input_dict, this_var["units"], unit_system)

################################################################################
# PRIVATE MODULE METHODS #
################################################################################

def _get_var_value(varname, var_dict, provided_keys, input_dict):
def _get_var_value(varname, var_dict, provided_keys, input_dict, units, unit_system):
""" Return the correct default value for a variable in the MARBL JSON parameter
file INPUTS:
* dictionary containing variable information (req: longname, datatype
Expand Down Expand Up @@ -366,7 +378,7 @@ def _get_var_value(varname, var_dict, provided_keys, input_dict):
def_value = var_dict["default_value"]

# call translate value from JSON file to format F90 expects
value = _translate_JSON_value(def_value, var_dict["datatype"])
value = _translate_JSON_value(def_value, var_dict["datatype"], units, unit_system)

# Append to config keywords if JSON wants it
if "_append_to_config_keywords" in var_dict.keys():
Expand All @@ -383,7 +395,7 @@ def _get_var_value(varname, var_dict, provided_keys, input_dict):

################################################################################

def _translate_JSON_value(value, datatype):
def _translate_JSON_value(value, datatype, units, unit_system):
""" The value provided in the JSON file needs to be adjusted depending on the datatype
of the variable. Strings need to be wrapped in "", and numbers written in
scientific notation need to be formatted consistently.
Expand All @@ -400,17 +412,49 @@ def _translate_JSON_value(value, datatype):
if datatype == "logical":
return _get_F90_logical(value)
# if variable is a real but value is unicode evaluate it
if datatype == "real" and isinstance(value, type(u'')):
return "%24.16e" % eval(value)
if datatype == "real":
if isinstance(value, str):
return "%24.16e" % (eval(value)*_get_scale_factor(units, unit_system))
else:
return value*_get_scale_factor(units, unit_system)
# if variable is an integer but value is unicode convert it
if datatype == "integer" and isinstance(value, type(u'')):
if datatype == "integer" and isinstance(value, str):
return int(value)

# Otherwise return value unchanged
return value

################################################################################

def _unit_conv_dict():
new_dict = dict()
new_dict['mks'] = dict()
new_dict['mks']['cm'] = {'new_units': 'm', 'scale_factor': 0.01}
new_dict['mks']['1/cm'] = {'new_units': '1/m', 'scale_factor': 100.}
new_dict['mks']['cm^2/ng s/yr'] = {'new_units': 'm^2/mg s/yr', 'scale_factor': 100.}

new_dict['cgs'] = dict()
new_dict['cgs']['m'] = {'new_units': 'cm', 'scale_factor': 100.}
new_dict['cgs']['1/m'] = {'new_units': '1/cm', 'scale_factor': 0.01}
new_dict['cgs']['m^2/mg s/yr'] = {'new_units': 'cm^2/ng s/yr', 'scale_factor': 0.01}
return new_dict

def _get_scale_factor(units, unit_system):
try:
return _unit_conv_dict()[unit_system][units]['scale_factor']
except:
return 1.

################################################################################

def _get_correct_units(units, unit_system):
try:
return _unit_conv_dict()[unit_system][units]['new_units']
except:
return units

################################################################################

def _get_F90_logical(value):
""" Return '.true.' if value is a valid Fortran logical = .true.
Return '.false.' if value is a valid Fortran logical = .false.
Expand Down Expand Up @@ -462,7 +506,7 @@ def _get_value(val_in, settings_dict, tracers_dict, dict_prefix=''):

# If val_in is a string, then it is a variable name that should be in
# settings_dict already
if isinstance(val_in, type(u'')):
if isinstance(val_in, str):
# If val_in = _tracer_list, that's a special case where we want
# to find a list of tracers according to current settings
if val_in == '_tracer_list':
Expand Down
Loading

0 comments on commit 951d1cd

Please sign in to comment.