Skip to content

first draft of initialize_from_configuration #130

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 35 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
3c5b7f6
first draft of `initialize_from_configuration`
zacharyburnett Nov 22, 2021
45c7e63
use `importlib`
zacharyburnett Nov 30, 2021
812b67d
add usage examples to CLI client page
zacharyburnett Dec 2, 2021
df5dd74
simplify headers
zacharyburnett Dec 2, 2021
f839c6c
add usage examples to JSON configurations
zacharyburnett Dec 2, 2021
b49bd72
fix header positions
zacharyburnett Dec 2, 2021
1a520c3
add docs badge
zacharyburnett Dec 2, 2021
29b85b7
add explicit link
zacharyburnett Dec 2, 2021
907e14e
fix logger
zacharyburnett Dec 3, 2021
76a8d08
add docstring
zacharyburnett Dec 3, 2021
5397d75
ensure path object
zacharyburnett Dec 10, 2021
0b51f5c
only use aswip parameters if they are defined
zacharyburnett Dec 10, 2021
fb06762
fix log filename to be slurm run name
zacharyburnett Dec 13, 2021
c185217
wrap connection attempt in try block
zacharyburnett Dec 22, 2021
2a58cc7
use print
zacharyburnett Dec 27, 2021
bcaee44
sort names
zacharyburnett Dec 27, 2021
86e1b4c
add absolute write argument
zacharyburnett Dec 27, 2021
57b5ecb
formatting
zacharyburnett Dec 27, 2021
d967747
use absolute paths
zacharyburnett Dec 27, 2021
13f813b
add parallel and bigmem partitions
zacharyburnett Dec 27, 2021
b4cd9c2
update reference files
zacharyburnett Dec 28, 2021
ad42ad3
TPXO host URL no longer works; need to find a more reliable way to ho…
zacharyburnett Dec 28, 2021
9a1da99
add descriptions to extra arguments
zacharyburnett Jan 12, 2022
49855bc
fix type error
zacharyburnett Jan 12, 2022
05d698b
sanitize extra arguments input
zacharyburnett Jan 12, 2022
e4f0d51
update documentation
zacharyburnett Jan 14, 2022
881578d
update documentation
zacharyburnett Jan 14, 2022
8db7cb2
fix test
zacharyburnett Jan 20, 2022
ec36b9e
update reference files
zacharyburnett Jan 20, 2022
b4a2c7f
add organizational responsibility to README, and fix authorship / cop…
zacharyburnett Jan 20, 2022
e38e5bb
first draft of `initialize_from_configuration`
zacharyburnett Nov 22, 2021
9468f2d
Merge branch 'main' into feature/from_configuration
zacharyburnett Jan 20, 2022
60d6ecb
formatting
zacharyburnett Jan 20, 2022
981ff27
Merge branch 'main' into feature/from_configuration
zacharyburnett Apr 27, 2022
6bd0f14
formatting
zacharyburnett Apr 27, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions coupledmodeldriver/client/check_completion.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from coupledmodeldriver.generate.adcirc.check import (
check_adcirc_completion,
CompletionStatus,
is_adcirc_run_directory,
is_adcirc_directory,
)
from coupledmodeldriver.utilities import ProcessPoolExecutorStackTraced

Expand Down Expand Up @@ -59,7 +59,7 @@ def is_model_directory(directory: PathLike, model: ModelJSON = None) -> bool:
model = ADCIRCJSON

if model == ADCIRCJSON:
is_model_directory = is_adcirc_run_directory(directory)
is_model_directory = is_adcirc_directory(directory)
else:
raise NotImplementedError(f'model "{model}" not implemented')

Expand Down
4 changes: 3 additions & 1 deletion coupledmodeldriver/client/initialize_adcirc.py
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,7 @@ def initialize_adcirc(
absolute_paths: bool = True,
overwrite: bool = None,
verbose: bool = False,
):
) -> ADCIRCRunConfiguration:
"""
creates a set of JSON configuration files according to the given parameters

Expand Down Expand Up @@ -445,6 +445,8 @@ def initialize_adcirc(
)
generation_job_script.write(filename=output_directory / 'generate.job', overwrite=True)

return configuration


def get_argument(
argument: str,
Expand Down
80 changes: 80 additions & 0 deletions coupledmodeldriver/client/initialize_from_configuration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
from argparse import ArgumentParser
from os import PathLike
from pathlib import Path

from typepigeon import convert_value

from coupledmodeldriver.configure import ModelJSON
from coupledmodeldriver.configure.configure import RunConfiguration
from coupledmodeldriver.generate import ADCIRCRunConfiguration
from coupledmodeldriver.generate.adcirc.base import ADCIRCJSON

MODELS = {model.name.lower(): model for model in ModelJSON.__subclasses__()}


def parse_initialize_from_configuration_arguments():
argument_parser = ArgumentParser()
argument_parser.add_argument(
'input-directory',
nargs='*',
default=Path.cwd(),
help='directory containing model configuration',
)
argument_parser.add_argument(
'output-directory',
nargs='*',
default=Path.cwd(),
help='directory to write JSON configuration',
)
argument_parser.add_argument('--model', help='model of configuraiton, one of: `ADCIRC`')
argument_parser.add_argument(
'--skip-existing', action='store_true', help='skip existing files',
)

arguments = argument_parser.parse_args()

input_directory = convert_value(arguments.input_directory, Path)
output_directory = convert_value(arguments.output_directory, Path)

model = arguments.model
if model is not None:
model = MODELS[model.lower()]

overwrite = not arguments.skip_existing

return {
'input_directory': input_directory,
'output_directory': output_directory,
'model': model,
'overwrite': overwrite,
}


def initialize_from_model_configuration_directory(
directory: PathLike, model: ModelJSON = None
) -> RunConfiguration:
if model is None:
model = ADCIRCJSON

if model == ADCIRCJSON:
run_configuration = ADCIRCRunConfiguration.from_model_configuration_directory(
directory=directory
)
else:
raise NotImplementedError(f'model "{model}" not implemented')

return run_configuration


def main():
arguments = parse_initialize_from_configuration_arguments()
run_configuration = initialize_from_model_configuration_directory(
directory=arguments['input_directory'], model=arguments['model'],
)
run_configuration.write_directory(
directory=arguments['output_directory'], overwrite=arguments['overwrite'],
)


if __name__ == '__main__':
main()
8 changes: 6 additions & 2 deletions coupledmodeldriver/configure/configure.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
from abc import ABC
from copy import copy
from os import PathLike
from pathlib import Path
from typing import Any, Collection, Dict, List, Mapping, Set, Union

from coupledmodeldriver.configure import ConfigurationJSON
from coupledmodeldriver.configure.base import ConfigurationJSON, ModelDriverJSON
from coupledmodeldriver.configure.forcings.base import ADCIRCPY_FORCING_CLASSES, ForcingJSON
from coupledmodeldriver.utilities import LOGGER


class RunConfiguration(ABC):
class RunConfiguration:
"""
abstraction of a set of model run configurations, encapsulated in JSON files
"""
Expand Down Expand Up @@ -150,6 +150,10 @@ def from_configurations(
) -> 'RunConfiguration':
return cls(configurations)

@classmethod
def from_model_configuration_directory(cls, directory: PathLike) -> 'RunConfiguration':
raise NotImplementedError


def from_user_input(value: Any) -> ConfigurationJSON:
"""
Expand Down
6 changes: 5 additions & 1 deletion coupledmodeldriver/generate/adcirc/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from pathlib import Path
from typing import Any, Dict, List, Union

from adcircpy import AdcircMesh, AdcircRun, Tides
from adcircpy import AdcircMesh, AdcircRun, Fort15, Tides
from adcircpy.forcing import BestTrackForcing
from adcircpy.forcing.base import Forcing
from adcircpy.mesh.fort13 import NodalAttributes
Expand Down Expand Up @@ -529,3 +529,7 @@ def __copy__(self) -> 'ADCIRCJSON':
instance.base_mesh = self.base_mesh
instance.forcings = self.forcings
return instance

@classmethod
def from_fort15(cls, filename: PathLike):
Fort15()
4 changes: 2 additions & 2 deletions coupledmodeldriver/generate/adcirc/check.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class CompletionStatus(Enum):
COMPLETED = 0


def is_adcirc_run_directory(directory: PathLike = None) -> bool:
def is_adcirc_directory(directory: PathLike = None) -> bool:
"""
check if the given directory has the baseline ADCIRC configuration files

Expand Down Expand Up @@ -63,7 +63,7 @@ def check_adcirc_completion(

completion = {entry: {} for entry in CompletionStatus}

if not is_adcirc_run_directory(directory):
if not is_adcirc_directory(directory):
completion[CompletionStatus.NOT_CONFIGURED][
'fort.14,fort.15'
] = f'ADCIRC configuration files not found'
Expand Down
35 changes: 32 additions & 3 deletions coupledmodeldriver/generate/adcirc/configure.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
WW3DATAForcingJSON,
)
from coupledmodeldriver.generate.adcirc.base import ADCIRCJSON
from coupledmodeldriver.generate.adcirc.check import is_adcirc_directory
from coupledmodeldriver.platforms import Platform
from coupledmodeldriver.utilities import LOGGER

Expand Down Expand Up @@ -267,17 +268,45 @@ def read_directory(

return super().read_directory(directory, required, supplementary)

@classmethod
def from_model_configuration_directory(cls, directory: PathLike) -> 'RunConfiguration':
if not isinstance(directory, Path):
directory = Path(directory)

configurations = []

spinup_directory = directory / 'spinup'
if spinup_directory.exists():
if not is_adcirc_directory(spinup_directory):
raise FileNotFoundError(f'not an ADCIRC directory: "{spinup_directory}"')

configurations.append(ADCIRCJSON.from_fort15(spinup_directory / 'fort.15'))

runs_directory = directory / 'runs'
if runs_directory.exists():
perturbations = {}
for run_directory in runs_directory.iterdir():
pass

if not is_adcirc_directory(directory):
raise FileNotFoundError('not an ADCIRC directory')

required = {configuration_class: None for configuration_class in cls.REQUIRED}
supplementary = {
configuration_class: None for configuration_class in cls.SUPPLEMENTARY
}

return cls()


class NEMSADCIRCRunConfiguration(ADCIRCRunConfiguration):
"""
run configuration coupling ADCIRC with other models / forcings using NUOPC NEMS
"""

REQUIRED = {
ModelDriverJSON,
*ADCIRCRunConfiguration.REQUIRED,
NEMSJSON,
SlurmJSON,
ADCIRCJSON,
}

def __init__(
Expand Down
129 changes: 128 additions & 1 deletion tests/test_generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@

from coupledmodeldriver import Platform
from coupledmodeldriver.client.initialize_adcirc import initialize_adcirc
from coupledmodeldriver.generate import generate_adcirc_configuration
from coupledmodeldriver.generate import (
ADCIRCRunConfiguration,
generate_adcirc_configuration,
NEMSADCIRCRunConfiguration,
)
from tests import (
check_reference_directory,
INPUT_DIRECTORY,
Expand Down Expand Up @@ -902,3 +906,126 @@ def test_stampede2_adcirc_tidal_nems_atmesh_ww3data():
'nems.configure': [0],
},
)


def test_adcirc_run_configuration():
output_directory = OUTPUT_DIRECTORY / 'test_adcirc_run_configuration'

platform = Platform.HERA
mesh = 'shinnecock'
adcirc_processors = 15 * platform.value['processors_per_node']
modeled_start_time = datetime(2008, 8, 23)
modeled_duration = timedelta(days=14.5)
modeled_timestep = timedelta(seconds=2)
tidal_spinup_duration = timedelta(days=12.5)
job_duration = timedelta(hours=6)

mesh_directory = INPUT_DIRECTORY / 'meshes' / mesh

tidal_forcing = Tides(tidal_source=TidalSource.HAMTIDE)
tidal_forcing.use_all()
forcings = [tidal_forcing]

configuration = initialize_adcirc(
platform=platform,
mesh_directory=mesh_directory,
modeled_start_time=modeled_start_time,
modeled_duration=modeled_duration,
modeled_timestep=modeled_timestep,
tidal_spinup_duration=tidal_spinup_duration,
perturbations=None,
nems_interval=None,
nems_connections=None,
nems_mediations=None,
nems_sequence=None,
modulefile=INPUT_DIRECTORY / 'modulefiles' / 'envmodules_intel.hera',
forcings=forcings,
adcirc_executable=INPUT_DIRECTORY / 'bin' / 'padcirc',
adcprep_executable=INPUT_DIRECTORY / 'bin' / 'adcprep',
aswip_executable=None,
adcirc_processors=adcirc_processors,
job_duration=job_duration,
output_directory=output_directory,
absolute_paths=False,
overwrite=True,
verbose=False,
)
generate_adcirc_configuration(output_directory, relative_paths=True, overwrite=True)

parsed_configuration = ADCIRCRunConfiguration.from_model_configuration_directory(
output_directory
)

assert parsed_configuration == configuration


def test_nems_adcirc_run_configuration():
output_directory = OUTPUT_DIRECTORY / 'test_nems_adcirc_run_configuration'

platform = Platform.HERA
mesh = 'shinnecock'
storm = 'ike'
adcirc_processors = 15 * platform.value['processors_per_node']
modeled_start_time = datetime(2008, 8, 23)
modeled_duration = timedelta(days=14.5)
modeled_timestep = timedelta(seconds=2)
tidal_spinup_duration = None
nems_interval = timedelta(hours=1)
job_duration = timedelta(hours=6)

mesh_directory = INPUT_DIRECTORY / 'meshes' / mesh
forcings_directory = INPUT_DIRECTORY / 'forcings' / storm

nems_connections = ['ATM -> OCN', 'WAV -> OCN']
nems_mediations = None
nems_sequence = [
'ATM -> OCN',
'WAV -> OCN',
'ATM',
'WAV',
'OCN',
]

wind_forcing = AtmosphericMeshForcing(
filename=forcings_directory / 'wind_atm_fin_ch_time_vec.nc',
nws=17,
interval_seconds=3600,
)
wave_forcing = WaveWatch3DataForcing(
filename=forcings_directory / 'ww3.Constant.20151214_sxy_ike_date.nc',
nrs=5,
interval_seconds=3600,
)
forcings = [wind_forcing, wave_forcing]

configuration = initialize_adcirc(
platform=platform,
mesh_directory=mesh_directory,
modeled_start_time=modeled_start_time,
modeled_duration=modeled_duration,
modeled_timestep=modeled_timestep,
tidal_spinup_duration=tidal_spinup_duration,
perturbations=None,
nems_interval=nems_interval,
nems_connections=nems_connections,
nems_mediations=nems_mediations,
nems_sequence=nems_sequence,
modulefile=INPUT_DIRECTORY / 'modulefiles' / 'envmodules_intel.hera',
forcings=forcings,
adcirc_executable=INPUT_DIRECTORY / 'bin' / 'NEMS.x',
adcprep_executable=INPUT_DIRECTORY / 'bin' / 'adcprep',
aswip_executable=None,
adcirc_processors=adcirc_processors,
job_duration=job_duration,
output_directory=output_directory,
absolute_paths=False,
overwrite=True,
verbose=False,
)
generate_adcirc_configuration(output_directory, relative_paths=True, overwrite=True)

parsed_configuration = NEMSADCIRCRunConfiguration.from_model_configuration_directory(
output_directory
)

assert parsed_configuration == configuration