Skip to content

Commit

Permalink
link mesh if it is the same (#121)
Browse files Browse the repository at this point in the history
* link mesh if it is the same

* also respect `use_original_mesh`

* deconstruct mesh if provided

* add comment

* only log if exists

* reconstruct mesh from base on-demand

* log full path of input

* destroy cached copy of mesh before it is submitted to the pickler

* bump adcircpy version to use new mesh equality and copying

* add default formatter to logging

* verbose message
  • Loading branch information
zacharyburnett authored Sep 21, 2021
1 parent 17d14d9 commit 1dc3c5a
Show file tree
Hide file tree
Showing 27 changed files with 122 additions and 104 deletions.
114 changes: 60 additions & 54 deletions coupledmodeldriver/generate/adcirc/base.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
from datetime import datetime, timedelta
import os
from os import PathLike
from pathlib import Path
import sys
from typing import Any, Union

from adcircpy import AdcircMesh, AdcircRun, Tides
from adcircpy.forcing import BestTrackForcing
from adcircpy.forcing.base import Forcing
from adcircpy.mesh.fort13 import NodalAttributes
from adcircpy.mesh.mesh import ModelForcings
from adcircpy.server import SlurmConfig
from nemspy.model import ADCIRCEntry

Expand Down Expand Up @@ -217,6 +217,7 @@ def __init__(
:param attributes: attributes to set in `adcircpy.AdcircRun` object
"""

self.__mesh = None
self.__base_mesh = None

if tidal_spinup_timestep is None:
Expand Down Expand Up @@ -309,70 +310,72 @@ def adcircpy_forcings(self) -> [Forcing]:

@property
def adcircpy_mesh(self) -> AdcircMesh:
if self.__base_mesh is None:
self.__base_mesh = self['fort_14_path']

if not isinstance(self.__base_mesh, AdcircMesh):
LOGGER.info(f'opening mesh "{self.__base_mesh}"')
self.__base_mesh = AdcircMesh.open(self.__base_mesh, crs=4326)

mesh = self.__base_mesh.copy()

if self['fort_13_path'] is not None:
LOGGER.info(
f'reading attributes from "{os.path.relpath(self["fort_13_path"].resolve(), Path.cwd())}"'
)
if self['fort_13_path'].exists():
mesh.import_nodal_attributes(self['fort_13_path'])
for attribute_name in mesh.get_nodal_attribute_names():
mesh.set_nodal_attribute_state(
attribute_name, coldstart=True, hotstart=True
if self.__mesh is None:
mesh = self.base_mesh.copy()

# reconstruct mesh from base
mesh.forcings = ModelForcings(mesh)
mesh.nodal_attributes = NodalAttributes(mesh)

if self['fort_13_path'] is not None:
if self['fort_13_path'].exists():
LOGGER.info(f'reading attributes from "{self["fort_13_path"]}"')
mesh.import_nodal_attributes(self['fort_13_path'])
for attribute_name in mesh.get_nodal_attribute_names():
mesh.set_nodal_attribute_state(
attribute_name, coldstart=True, hotstart=True
)
else:
LOGGER.warning(
f'mesh values (nodal attributes) not found at "{self["fort_13_path"]}"'
)
else:
LOGGER.warning(
f'mesh values (nodal attributes) not found at "{os.path.relpath(self["fort_13_path"].resolve(), Path.cwd())}"'
)

LOGGER.debug(f'adding {len(self.forcings)} forcing(s) to mesh')
for adcircpy_forcing in self.adcircpy_forcings:
if isinstance(adcircpy_forcing, (Tides, BestTrackForcing)):
adcircpy_forcing.start_date = self['modeled_start_time']
adcircpy_forcing.end_date = self['modeled_end_time']
LOGGER.debug(f'adding {len(self.forcings)} forcing(s) to mesh')
for adcircpy_forcing in self.adcircpy_forcings:
if isinstance(adcircpy_forcing, (Tides, BestTrackForcing)):
adcircpy_forcing.start_date = self['modeled_start_time']
adcircpy_forcing.end_date = self['modeled_end_time']

if (
isinstance(adcircpy_forcing, Tides)
and self['tidal_spinup_duration'] is not None
):
adcircpy_forcing.spinup_time = self['tidal_spinup_duration']
adcircpy_forcing.start_date -= self['tidal_spinup_duration']
# elif isinstance(adcircpy_forcing, BestTrackForcing):
# adcircpy_forcing.clip_to_bbox(mesh.get_bbox(output_type='bbox'), mesh.crs)
if (
isinstance(adcircpy_forcing, Tides)
and self['tidal_spinup_duration'] is not None
):
adcircpy_forcing.spinup_time = self['tidal_spinup_duration']
adcircpy_forcing.start_date -= self['tidal_spinup_duration']
# elif isinstance(adcircpy_forcing, BestTrackForcing):
# adcircpy_forcing.clip_to_bbox(mesh.get_bbox(output_type='bbox'), mesh.crs)

mesh.add_forcing(adcircpy_forcing)
mesh.add_forcing(adcircpy_forcing)

if not mesh.has_nodal_attribute('primitive_weighting_in_continuity_equation'):
LOGGER.debug(f'generating tau0 in mesh')
mesh.generate_tau0()
if not mesh.has_nodal_attribute('primitive_weighting_in_continuity_equation'):
LOGGER.debug(f'generating tau0 in mesh')
mesh.generate_tau0()

return mesh
self.__mesh = mesh

return self.__mesh

@adcircpy_mesh.setter
def adcircpy_mesh(self, adcircpy_mesh: Union[AdcircMesh, PathLike]):
if isinstance(adcircpy_mesh, AdcircMesh):
try:
adcircpy_mesh = adcircpy_mesh.copy()
LOGGER.debug(f'copying mesh object ({sys.getsizeof(adcircpy_mesh)} bytes)')
except Exception as error:
LOGGER.warning(f'unable to copy mesh object: {error}')

self.__base_mesh = adcircpy_mesh
self.__mesh = adcircpy_mesh

@property
def base_mesh(self) -> AdcircMesh:
if self.__base_mesh is None:
if self.__mesh is not None:
self.__base_mesh = self.__mesh
else:
self.__base_mesh = self['fort_14_path']
if not isinstance(self.__base_mesh, AdcircMesh):
LOGGER.info(f'opening mesh "{self.__base_mesh}"')
self.__base_mesh = AdcircMesh.open(self.__base_mesh, crs=4326)
# deconstruct mesh into a base mesh that can be pickled
self.__base_mesh.forcings = None
self.__base_mesh.nodal_attributes = None
return self.__base_mesh

@base_mesh.setter
def base_mesh(self, base_mesh: AdcircMesh):
def base_mesh(self, base_mesh: Union[AdcircMesh, PathLike]):
self.__base_mesh = base_mesh

@property
Expand All @@ -389,9 +392,7 @@ def adcircpy_driver(self) -> AdcircRun:
)

if self['stations_file_path'] is not None:
LOGGER.info(
f'importing stations from "{os.path.relpath(self["stations_file_path"].resolve(), Path.cwd())}"'
)
LOGGER.info(f'importing stations from "{self["stations_file_path"]}"')
driver.import_stations(self['stations_file_path'])

if self['modeled_timestep'] is not None:
Expand Down Expand Up @@ -501,7 +502,12 @@ def adcircpy_driver(self) -> AdcircRun:
def nemspy_entry(self) -> ADCIRCEntry:
return ADCIRCEntry(processors=self['processors'], **self['nems_parameters'])

def __setitem__(self, key: str, value: Any):
super().__setitem__(key, value)
self.__mesh = None

def __copy__(self) -> 'ADCIRCJSON':
instance = super().__copy__()
instance.base_mesh = self.base_mesh
instance.forcings = self.forcings
return instance
13 changes: 8 additions & 5 deletions coupledmodeldriver/generate/adcirc/configure.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,10 +152,18 @@ def forcings(self) -> [ForcingJSON]:
def adcircpy_mesh(self) -> AdcircMesh:
return self['adcirc'].adcircpy_mesh

@adcircpy_mesh.setter
def adcircpy_mesh(self, adcircpy_mesh: AdcircMesh):
self['adcirc'].adcircpy_mesh = adcircpy_mesh

@property
def adcircpy_driver(self) -> AdcircRun:
return self['adcirc'].adcircpy_driver

@adcircpy_driver.setter
def adcircpy_driver(self, adcircpy_driver: AdcircRun):
self['adcirc'].adcircpy_driver = adcircpy_driver

def __copy__(self) -> 'ADCIRCRunConfiguration':
return self.__class__.from_configurations(
[copy(configuration) for configuration in self.configurations]
Expand Down Expand Up @@ -305,11 +313,6 @@ def add_forcing(self, forcing: ForcingJSON):
forcing = self[self.add(forcing)]
self['adcirc'].add_forcing(forcing)

def __copy__(self) -> 'NEMSADCIRCRunConfiguration':
return self.__class__.from_configurations(
[copy(configuration) for configuration in self.configurations]
)

@classmethod
def from_configurations(
cls, configurations: [ConfigurationJSON]
Expand Down
51 changes: 30 additions & 21 deletions coupledmodeldriver/generate/adcirc/generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,15 +173,17 @@ def generate_adcirc_configuration(
if do_spinup:
spinup_directory = output_directory / 'spinup'

spinup_configuration = copy(base_configuration)

spinup_kwargs = {
'directory': spinup_directory,
'configuration': copy(base_configuration),
'configuration': spinup_configuration,
'duration': spinup_duration,
'relative_paths': relative_paths,
'overwrite': overwrite,
'use_original_mesh': use_original_mesh,
'local_fort13_filename': local_fort13_filename,
'local_fort14_filename': local_fort14_filename,
'link_mesh': True,
'relative_paths': relative_paths,
'overwrite': overwrite,
'platform': platform,
'adcirc_processors': adcirc_processors,
'slurm_account': slurm_account,
Expand All @@ -202,16 +204,21 @@ def generate_adcirc_configuration(
for run_name, run_configuration in perturbations.items():
run_directory = runs_directory / run_name

link_mesh = (
use_original_mesh
or run_configuration.adcircpy_mesh == base_configuration.adcircpy_mesh
)

run_kwargs = {
'directory': run_directory,
'name': run_name,
'phase': run_phase,
'configuration': run_configuration,
'relative_paths': relative_paths,
'overwrite': overwrite,
'use_original_mesh': use_original_mesh,
'local_fort13_filename': local_fort13_filename,
'local_fort14_filename': local_fort14_filename,
'link_mesh': link_mesh,
'relative_paths': relative_paths,
'overwrite': overwrite,
'platform': platform,
'adcirc_processors': adcirc_processors,
'slurm_account': slurm_account,
Expand All @@ -224,6 +231,8 @@ def generate_adcirc_configuration(
}

if parallel:
# destroy stored copy of adcircpy mesh, because it cannot be pickled across processes
run_configuration.adcircpy_mesh = None
futures.append(process_pool.submit(write_run_directory, **run_kwargs))
else:
write_run_directory(**run_kwargs)
Expand Down Expand Up @@ -264,9 +273,9 @@ def write_spinup_directory(
duration: timedelta,
local_fort14_filename: PathLike,
local_fort13_filename: PathLike = None,
link_mesh: bool = False,
relative_paths: bool = False,
overwrite: bool = False,
use_original_mesh: bool = False,
platform: Platform = None,
adcirc_processors: int = None,
slurm_account: str = None,
Expand All @@ -277,7 +286,7 @@ def write_spinup_directory(
) -> Path:
if not isinstance(directory, Path):
directory = Path(directory)
if not isinstance(local_fort13_filename, Path):
if local_fort13_filename is not None and not isinstance(local_fort13_filename, Path):
local_fort13_filename = Path(local_fort13_filename)

if not directory.exists():
Expand Down Expand Up @@ -404,17 +413,17 @@ def write_spinup_directory(
adcircpy_driver.write(
directory,
overwrite=overwrite,
fort13=None if use_original_mesh else 'fort.13',
fort14=None,
fort13=None if link_mesh else 'fort.13',
fort14=None if link_mesh else 'fort.14',
fort22='fort.22' if 'besttrack' in configuration else None,
coldstart='fort.15',
hotstart=None,
driver=None,
)
if use_original_mesh:
if local_fort13_filename.exists():
if link_mesh:
if local_fort13_filename is not None and local_fort13_filename.exists():
create_symlink(local_fort13_filename, directory / 'fort.13', relative=True)
create_symlink(local_fort14_filename, directory / 'fort.14', relative=True)
create_symlink(local_fort14_filename, directory / 'fort.14', relative=True)

return directory

Expand All @@ -426,9 +435,9 @@ def write_run_directory(
configuration: Union[ADCIRCRunConfiguration, NEMSADCIRCRunConfiguration],
local_fort14_filename: PathLike,
local_fort13_filename: PathLike = None,
link_mesh: bool = False,
relative_paths: bool = False,
overwrite: bool = False,
use_original_mesh: bool = False,
platform: Platform = None,
adcirc_processors: int = None,
slurm_account: str = None,
Expand All @@ -443,7 +452,7 @@ def write_run_directory(
directory = Path(directory)
if spinup_directory is not None and not isinstance(spinup_directory, Path):
spinup_directory = Path(spinup_directory)
if not isinstance(local_fort13_filename, Path):
if local_fort13_filename is not None and not isinstance(local_fort13_filename, Path):
local_fort13_filename = Path(local_fort13_filename)

if not directory.exists():
Expand Down Expand Up @@ -564,17 +573,17 @@ def write_run_directory(
adcircpy_driver.write(
directory,
overwrite=overwrite,
fort13=None if use_original_mesh else 'fort.13',
fort14=None,
fort13=None if link_mesh else 'fort.13',
fort14=None if link_mesh else 'fort.14',
fort22='fort.22' if 'besttrack' in configuration else None,
coldstart=None,
hotstart='fort.15',
driver=None,
)
if use_original_mesh:
if local_fort13_filename.exists():
if link_mesh:
if local_fort13_filename is not None and local_fort13_filename.exists():
create_symlink(local_fort13_filename, directory / 'fort.13', relative=True)
create_symlink(local_fort14_filename, directory / 'fort.14', relative=True)
create_symlink(local_fort14_filename, directory / 'fort.14', relative=True)

if do_spinup:
for hotstart_filename in ['fort.67.nc', 'fort.68.nc']:
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from setuptools import config, find_packages, setup

DEPENDENCIES = {
'adcircpy>=1.0.39': ['gdal', 'fiona'],
'adcircpy>=1.0.41': ['gdal', 'fiona'],
'filelock': [],
'file-read-backwards': [],
'nemspy>=1.0.3': [],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
created on 2021-09-07 10:26 ! RUNDES - 32 CHARACTER ALPHANUMERIC RUN DESCRIPTION
created on 2021-09-07 14:16 ! RUNDES - 32 CHARACTER ALPHANUMERIC RUN DESCRIPTION
Shinacock Inlet Coarse Grid ! RUNID - 24 CHARACTER ALPANUMERIC RUN IDENTIFICATION
1 ! NFOVER - NONFATAL ERROR OVERRIDE OPTION
1 ! NABOUT - ABREVIATED OUTPUT OPTION PARAMETER
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
created on 2021-09-07 10:26 ! RUNDES - 32 CHARACTER ALPHANUMERIC RUN DESCRIPTION
created on 2021-09-07 14:16 ! RUNDES - 32 CHARACTER ALPHANUMERIC RUN DESCRIPTION
Shinacock Inlet Coarse Grid ! RUNID - 24 CHARACTER ALPANUMERIC RUN IDENTIFICATION
1 ! NFOVER - NONFATAL ERROR OVERRIDE OPTION
1 ! NABOUT - ABREVIATED OUTPUT OPTION PARAMETER
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
created on 2021-09-07 10:26 ! RUNDES - 32 CHARACTER ALPHANUMERIC RUN DESCRIPTION
created on 2021-09-07 14:16 ! RUNDES - 32 CHARACTER ALPHANUMERIC RUN DESCRIPTION
Shinacock Inlet Coarse Grid ! RUNID - 24 CHARACTER ALPANUMERIC RUN IDENTIFICATION
1 ! NFOVER - NONFATAL ERROR OVERRIDE OPTION
1 ! NABOUT - ABREVIATED OUTPUT OPTION PARAMETER
Expand Down
2 changes: 1 addition & 1 deletion tests/data/reference/test_hera_adcirc_tidal/spinup/fort.15
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
created on 2021-09-07 10:26 ! RUNDES - 32 CHARACTER ALPHANUMERIC RUN DESCRIPTION
created on 2021-09-07 14:16 ! RUNDES - 32 CHARACTER ALPHANUMERIC RUN DESCRIPTION
Shinacock Inlet Coarse Grid ! RUNID - 24 CHARACTER ALPANUMERIC RUN IDENTIFICATION
1 ! NFOVER - NONFATAL ERROR OVERRIDE OPTION
1 ! NABOUT - ABREVIATED OUTPUT OPTION PARAMETER
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
created on 2021-09-07 10:26 ! RUNDES - 32 CHARACTER ALPHANUMERIC RUN DESCRIPTION
created on 2021-09-07 14:17 ! RUNDES - 32 CHARACTER ALPHANUMERIC RUN DESCRIPTION
Shinacock Inlet Coarse Grid ! RUNID - 24 CHARACTER ALPANUMERIC RUN IDENTIFICATION
1 ! NFOVER - NONFATAL ERROR OVERRIDE OPTION
1 ! NABOUT - ABREVIATED OUTPUT OPTION PARAMETER
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
created on 2021-09-07 10:26 ! RUNDES - 32 CHARACTER ALPHANUMERIC RUN DESCRIPTION
created on 2021-09-07 14:17 ! RUNDES - 32 CHARACTER ALPHANUMERIC RUN DESCRIPTION
Shinacock Inlet Coarse Grid ! RUNID - 24 CHARACTER ALPANUMERIC RUN IDENTIFICATION
1 ! NFOVER - NONFATAL ERROR OVERRIDE OPTION
1 ! NABOUT - ABREVIATED OUTPUT OPTION PARAMETER
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
created on 2021-09-07 10:27 ! RUNDES - 32 CHARACTER ALPHANUMERIC RUN DESCRIPTION
created on 2021-09-07 14:17 ! RUNDES - 32 CHARACTER ALPHANUMERIC RUN DESCRIPTION
Shinacock Inlet Coarse Grid ! RUNID - 24 CHARACTER ALPANUMERIC RUN IDENTIFICATION
1 ! NFOVER - NONFATAL ERROR OVERRIDE OPTION
1 ! NABOUT - ABREVIATED OUTPUT OPTION PARAMETER
Expand Down
Loading

0 comments on commit 1dc3c5a

Please sign in to comment.