Skip to content

Commit

Permalink
Merge pull request #66 from noaa-ocs-modeling/develop
Browse files Browse the repository at this point in the history
move functions from NEMS ADCIRC into ADCIRC submodule
  • Loading branch information
zacharyburnett committed Apr 7, 2021
2 parents 970b842 + 009add4 commit 74124d0
Show file tree
Hide file tree
Showing 18 changed files with 668 additions and 693 deletions.
4 changes: 3 additions & 1 deletion coupledmodeldriver/configure/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
from .base import ModelDriverJSON, SlurmJSON
from .base import ModelDriverJSON, NEMSJSON, SlurmJSON
from .forcings import *
from .models import *
79 changes: 78 additions & 1 deletion coupledmodeldriver/configure/base.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
from abc import ABC, abstractmethod
from datetime import timedelta
from datetime import datetime, timedelta
import json
from os import PathLike
from pathlib import Path
from typing import Any

from adcircpy.server import SlurmConfig
from nemspy import ModelingSystem
from nemspy.model import ModelEntry

from ..platforms import Platform
Expand Down Expand Up @@ -161,6 +162,82 @@ def to_file(self, filename: PathLike = None, overwrite: bool = False):
LOGGER.debug(f'skipping existing file "{filename}"')


class NEMSJSON(ConfigurationJSON):
name = 'nems'
default_filename = f'configure_nems.json'
field_types = {
'executable_path': Path,
'modeled_start_time': datetime,
'modeled_end_time': datetime,
'interval': timedelta,
'models': [ModelEntry],
'connections': [[str]],
'mediations': [str],
'sequence': [str],
}

def __init__(
self,
executable_path: PathLike,
modeled_start_time: datetime,
modeled_end_time: datetime,
interval: timedelta = None,
models: [ModelEntry] = None,
connections: [[str]] = None,
mediations: [[str]] = None,
sequence: [str] = None,
**kwargs,
):
if 'fields' not in kwargs:
kwargs['fields'] = {}
kwargs['fields'].update(NEMSJSON.field_types)

ConfigurationJSON.__init__(self, **kwargs)

self['executable_path'] = executable_path
self['modeled_start_time'] = modeled_start_time
self['modeled_end_time'] = modeled_end_time
self['interval'] = interval
self['models'] = models
self['connections'] = connections
self['mediations'] = mediations
self['sequence'] = sequence

@property
def nemspy_modeling_system(self) -> ModelingSystem:
modeling_system = ModelingSystem(
start_time=self['modeled_start_time'],
end_time=self['modeled_end_time'],
interval=self['interval'],
**{model.model_type.value.lower(): model for model in self['models']},
)
for connection in self['connections']:
modeling_system.connect(*connection)
for mediation in self['mediations']:
modeling_system.mediate(*mediation)

modeling_system.sequence = self['sequence']

return modeling_system

def to_nemspy(self) -> ModelingSystem:
return self.nemspy_modeling_system

@classmethod
def from_nemspy(cls, modeling_system: ModelingSystem, executable_path: PathLike = None):
if executable_path is None:
executable_path = 'NEMS.x'
return cls(
executable_path=executable_path,
modeled_start_time=modeling_system.start_time,
modeled_end_time=modeling_system.end_time,
interval=modeling_system.interval,
models=modeling_system.models,
connections=modeling_system.connections,
sequence=modeling_system.sequence,
)


class NEMSCapJSON(ConfigurationJSON, ABC):
field_types = {
'processors': int,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
from adcircpy.forcing.base import Forcing
from nemspy.model import ModelEntry

from coupledmodeldriver.configure.forcings.base import ForcingJSON
from .base import ConfigurationJSON, NEMSCapJSON
from .forcings.base import ForcingJSON


class RunConfiguration(ABC):
Expand Down
3 changes: 1 addition & 2 deletions coupledmodeldriver/configure/forcings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@
from adcircpy.forcing.winds.owi import OwiForcing
from nemspy.model import AtmosphericMeshEntry, WaveMeshEntry

from coupledmodeldriver.configure.base import ConfigurationJSON, \
NEMSCapJSON
from ..base import ConfigurationJSON, NEMSCapJSON


class ForcingJSON(ConfigurationJSON, ABC):
Expand Down
2 changes: 1 addition & 1 deletion coupledmodeldriver/configure/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
from adcircpy.server import SlurmConfig
from nemspy.model import ADCIRCEntry

from coupledmodeldriver.configure.forcings.base import ForcingJSON
from .base import ConfigurationJSON, NEMSCapJSON, SlurmJSON
from .forcings.base import ForcingJSON
from ..utilities import LOGGER


Expand Down
1 change: 0 additions & 1 deletion coupledmodeldriver/generate/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
from .adcirc import *
from .nems import *
6 changes: 3 additions & 3 deletions coupledmodeldriver/generate/adcirc/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
from .configure import ADCIRCRunConfiguration
from .generate import generate_adcirc_configuration
from .script import ADCIRCGenerationScript
from .configure import ADCIRCRunConfiguration, NEMSADCIRCRunConfiguration
from .generate import generate_adcirc_configuration, generate_nems_adcirc_configuration
from .script import ADCIRCGenerationScript, NEMSADCIRCGenerationScript
155 changes: 149 additions & 6 deletions coupledmodeldriver/generate/adcirc/configure.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,21 @@
from pathlib import Path

from adcircpy import AdcircMesh, AdcircRun, Tides
from adcircpy.forcing.base import Forcing
from adcircpy.forcing.waves.ww3 import WaveWatch3DataForcing
from adcircpy.forcing.winds.atmesh import AtmosphericMeshForcing
from nemspy import ModelingSystem

from coupledmodeldriver.configure.base import ModelDriverJSON, SlurmJSON
from coupledmodeldriver.configure.forcings import (
from ...configure.base import ModelDriverJSON, NEMSJSON, SlurmJSON
from ...configure.configure import RunConfiguration
from ...configure.forcings.base import (
ATMESHForcingJSON,
ForcingJSON,
TidalForcingJSON,
WW3DATAForcingJSON,
)
from coupledmodeldriver.configure.forcings.base import ForcingJSON
from coupledmodeldriver.configure.models import ADCIRCJSON
from coupledmodeldriver.configure.run import RunConfiguration
from coupledmodeldriver.platforms import Platform
from ...configure.models import ADCIRCJSON
from ...platforms import Platform


class ADCIRCRunConfiguration(RunConfiguration):
Expand Down Expand Up @@ -180,3 +182,144 @@ def read_directory(cls, directory: PathLike) -> 'ADCIRCRunConfiguration':
adcirc=configurations[2],
forcings=forcings,
)


class NEMSADCIRCRunConfiguration(ADCIRCRunConfiguration):
required = [
ModelDriverJSON,
NEMSJSON,
SlurmJSON,
ADCIRCJSON,
]

def __init__(
self,
fort13: PathLike,
fort14: PathLike,
modeled_start_time: datetime,
modeled_end_time: datetime,
modeled_timestep: timedelta,
nems_interval: timedelta,
nems_connections: [str],
nems_mediations: [str],
nems_sequence: [str],
tidal_spinup_duration: timedelta = None,
platform: Platform = None,
runs: {str: (float, str)} = None,
forcings: [ForcingJSON] = None,
adcirc_processors: int = None,
slurm_job_duration: timedelta = None,
slurm_partition: str = None,
slurm_email_address: str = None,
nems_executable: PathLike = None,
adcprep_executable: PathLike = None,
source_filename: PathLike = None,
):
self.__nems = None

super().__init__(
fort13=fort13,
fort14=fort14,
modeled_start_time=modeled_start_time,
modeled_end_time=modeled_end_time,
modeled_timestep=modeled_timestep,
tidal_spinup_duration=tidal_spinup_duration,
platform=platform,
runs=runs,
forcings=None,
adcirc_processors=adcirc_processors,
slurm_job_duration=slurm_job_duration,
slurm_partition=slurm_partition,
slurm_email_address=slurm_email_address,
adcprep_executable=adcprep_executable,
source_filename=source_filename,
)

nems = NEMSJSON(
executable_path=nems_executable,
modeled_start_time=modeled_start_time,
modeled_end_time=modeled_end_time,
interval=nems_interval,
models=self.nemspy_entries,
connections=nems_connections,
mediations=nems_mediations,
sequence=nems_sequence,
)

self.configurations[nems.name] = nems

for forcing in forcings:
self.add_forcing(forcing)

self['slurm']['tasks'] = self['nems'].nemspy_modeling_system.processors

@property
def nemspy_modeling_system(self) -> ModelingSystem:
return self['nems'].nemspy_modeling_system

def add_forcing(self, forcing: Forcing):
if not isinstance(forcing, ForcingJSON):
if isinstance(forcing, AtmosphericMeshForcing):
forcing = ATMESHForcingJSON.from_adcircpy(forcing)
self['nems']['models'].append(forcing.nemspy_entry)
elif isinstance(forcing, WaveWatch3DataForcing):
forcing = WW3DATAForcingJSON.from_adcircpy(forcing)
self['nems']['models'].append(forcing.nemspy_entry)
elif isinstance(forcing, Tides):
forcing = TidalForcingJSON.from_adcircpy(forcing)
else:
raise NotImplementedError(f'unable to parse object of type {type(forcing)}')

if forcing not in self:
self[forcing.name] = forcing
self['adcirc'].forcings.append(forcing)

@classmethod
def from_configurations(
cls,
driver: ModelDriverJSON,
nems: NEMSJSON,
slurm: SlurmJSON,
adcirc: ADCIRCJSON,
forcings: [ForcingJSON] = None,
) -> 'NEMSADCIRCRunConfiguration':
instance = super().from_configurations(
driver=driver, slurm=slurm, adcirc=adcirc, forcings=None,
)
instance.__class__ = cls
instance.configurations['nems'] = nems

if forcings is not None:
for forcing in forcings:
instance.add_forcing(forcing)

return instance

@classmethod
def read_directory(cls, directory: PathLike) -> 'NEMSADCIRCRunConfiguration':
if not isinstance(directory, Path):
directory = Path(directory)
if directory.is_file():
directory = directory.parent

configurations = []
for configuration_class in cls.required:
filename = directory / configuration_class.default_filename
if filename.exists():
configurations.append(configuration_class.from_file(filename))
else:
raise FileNotFoundError(f'missing required configuration file "{filename}"')

forcings = []
for configuration_class in cls.forcings:
filename = directory / configuration_class.default_filename
if filename.exists():
forcings.append(configuration_class.from_file(filename))

return cls.from_configurations(
driver=configurations[0],
nems=configurations[1],
slurm=configurations[2],
adcirc=configurations[3],
forcings=forcings,
)
Loading

0 comments on commit 74124d0

Please sign in to comment.