diff --git a/docs/developers_guide/quick_start.md b/docs/developers_guide/quick_start.md index 0092202f2..b5fac4b66 100644 --- a/docs/developers_guide/quick_start.md +++ b/docs/developers_guide/quick_start.md @@ -478,6 +478,11 @@ make The same applies to MPAS-Seaice except with `mpas-seaice` in the path above. +To set up tasks and suites to use MPAS-Ocean, it should be sufficient to point +to an MPAS-Ocean build with the `-p` flag to `polaris setup` or +`polaris suite`. MPAS-Ocean should automatically be detected. You can use +the flag `--model mpas-ocean` to be explicit, though. + (dev-omega-build)= ### Omega @@ -526,9 +531,10 @@ location you like. If you build in a location other than relative or absolute path using the `-p` flag when you call `polaris setup` or `polaris suite`. -To set up tasks and suites to use Omega, you need to supply `--model=omega` -to `polaris setup` or `polaris suite`. Otherwise, it will default to -MPAS-Ocean. +As with MPAS-Ocean, to set up tasks and suites to use Omega, it should be +sufficient to point to an Omega build with the `-p` flag to `polaris setup` or +`polaris suite`, which should allow Omega to be detected automatically. +But you can use the flag `--model omega` to indicate this explicitly. (dev-working-with-polaris)= diff --git a/polaris/ocean/ocean.cfg b/polaris/ocean/ocean.cfg index bbb328c87..d57eb2103 100644 --- a/polaris/ocean/ocean.cfg +++ b/polaris/ocean/ocean.cfg @@ -3,8 +3,8 @@ # Options related the ocean component [ocean] -# Which model, MPAS-Ocean or Omega, is used -model = mpas-ocean +# Which model, MPAS-Ocean or Omega, is used, or "detect" to auto-detect +model = detect # the number of cells per core to aim for goal_cells_per_core = 200 diff --git a/polaris/setup.py b/polaris/setup.py index c5aa8fb71..a5f972342 100644 --- a/polaris/setup.py +++ b/polaris/setup.py @@ -112,9 +112,15 @@ def setup_tasks( component = tasks[first_path].component basic_config = _get_basic_config( - config_file, machine, component_path, component, model + config_file=config_file, + machine=machine, + component_path=component_path, + component=component, + model=model, ) + component.configure(basic_config) + provenance.write(work_dir, tasks, config=basic_config) if clean: @@ -124,14 +130,11 @@ def setup_tasks( print('') _setup_configs( - component, - tasks, - work_dir, - config_file, - machine, - component_path, - copy_executable, - model, + basic_config=basic_config, + component=component, + tasks=tasks, + work_dir=work_dir, + copy_executable=copy_executable, ) # do this after _setup_configs() in case tasks mark additional steps @@ -432,20 +435,15 @@ def _expand_and_mark_cached_steps(tasks, cached_steps): def _setup_configs( + basic_config, component, tasks, work_dir, - config_file, - machine, - component_path, copy_executable, - model, ): """Set up config parsers for this component""" - common_config = _get_basic_config( - config_file, machine, component_path, component, model - ) + common_config = basic_config.copy() if copy_executable: common_config.set('setup', 'copy_executable', 'True') @@ -718,8 +716,6 @@ def _get_basic_config(config_file, machine, component_path, component, model): exception=False, ) - component.configure(config) - # set the component_path path from the command line if provided if component_path is not None: component_path = os.path.abspath(component_path) diff --git a/polaris/tasks/ocean/__init__.py b/polaris/tasks/ocean/__init__.py index 1ff62db03..963e5f01d 100644 --- a/polaris/tasks/ocean/__init__.py +++ b/polaris/tasks/ocean/__init__.py @@ -1,4 +1,5 @@ import importlib.resources as imp_res +import os from typing import Dict, Union import xarray as xr @@ -44,6 +45,10 @@ def configure(self, config): """ section = config['ocean'] model = section.get('model') + if model == 'detect': + model = self._detect_model(config) + print('Detected ocean model:', model) + config.set('ocean', 'model', model) configs = {'mpas-ocean': 'mpas_ocean.cfg', 'omega': 'omega.cfg'} if model not in configs: raise ValueError(f'Unknown ocean model {model} in config options') @@ -228,6 +233,72 @@ def _read_var_map(self): self.mpaso_to_omega_dim_map = nested_dict['dimensions'] self.mpaso_to_omega_var_map = nested_dict['variables'] + def _detect_model(self, config) -> str: + """ + Detect which ocean model to use + """ + # build config options for each model, so the default component_path + # can be read if it hasn't been overridden + omega_config = config.copy() + omega_config.add_from_package('polaris.ocean', 'omega.cfg') + omega_path = omega_config.get('paths', 'component_path') + + mpas_ocean_config = config.copy() + mpas_ocean_config.add_from_package('polaris.ocean', 'mpas_ocean.cfg') + mpas_ocean_path = mpas_ocean_config.get('paths', 'component_path') + + if self._detect_omega_build(omega_path): + return 'omega' + elif self._detect_mpas_ocean_build(mpas_ocean_path): + return 'mpas-ocean' + else: + raise ValueError( + f'Could not detect ocean model; neither MPAS-Ocean ' + f'nor Omega appear to be available; ' + f'searched {omega_path} and {mpas_ocean_path}.' + ) + + def _detect_omega_build(self, path) -> bool: + """ + Detect if Omega is available + """ + required_files = [ + 'configs/Default.yml', + 'src/omega.exe', + ] + path = os.path.abspath(path) + + all_found = True + for required_file in required_files: + if not os.path.exists(os.path.join(path, required_file)): + all_found = False + break + return all_found + + def _detect_mpas_ocean_build(self, path) -> bool: + """ + Detect if MPAS-Ocean is available + + Returns + ------- + is_mpas_ocean : bool + True if MPAS-Ocean appears to be available, False otherwise + """ + required_files = [ + 'default_inputs/namelist.ocean.forward', + 'default_inputs/streams.ocean.forward', + 'src/Registry_processed.xml', + 'ocean_model', + ] + path = os.path.abspath(path) + + all_found = True + for required_file in required_files: + if not os.path.exists(os.path.join(path, required_file)): + all_found = False + break + return all_found + # create a single module-level instance available to other components ocean = Ocean()