Skip to content
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

update style to more modern python idioms and remove dead imports #157

Merged
merged 8 commits into from
Aug 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 3 additions & 3 deletions python/ngen_cal/src/ngen/cal/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
def _loaded_plugins(pm: PluginManager) -> str:
from .utils import type_as_import_string

plugins: List[str] = []
plugins: list[str] = []
for (name, plugin) in pm.list_name_plugin():
if not name:
continue
Expand All @@ -34,7 +34,7 @@ def _loaded_plugins(pm: PluginManager) -> str:
return f"Plugins Loaded: {', '.join(plugins)}"


def main(general: General, model_conf: "Mapping[str, Any]"):
def main(general: General, model_conf: Mapping[str, Any]):
#seed the random number generators if requested
if general.random_seed is not None:
import random
Expand Down Expand Up @@ -72,7 +72,7 @@ def main(general: General, model_conf: "Mapping[str, Any]"):
print("Restart not supported for PSO search, starting at 0")
func = pso_search

print("Starting Iteration: {}".format(start_iteration))
print(f"Starting Iteration: {start_iteration}")
# print("Starting Best param: {}".format(meta.best_params))
# print("Starting Best score: {}".format(meta.best_score))
print("Starting calibration loop")
Expand Down
1 change: 0 additions & 1 deletion python/ngen_cal/src/ngen/cal/_hookspec.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
from ngen.cal.configuration import General
from ngen.cal.meta import JobMeta
from pandas import Series
from pathlib import Path

hookspec = pluggy.HookspecMarker(PROJECT_SLUG)

Expand Down
22 changes: 12 additions & 10 deletions python/ngen_cal/src/ngen/cal/agent.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
from __future__ import annotations

from abc import ABC, abstractmethod
from ngen.cal.meta import JobMeta
from ngen.cal.configuration import Model
from ngen.cal.utils import pushd
from pathlib import Path
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from typing import Sequence, Optional, Mapping, Any
from typing import Sequence, Mapping, Any
from pandas import DataFrame
from pathlib import Path
from ngen.cal.calibratable import Adjustable

class BaseAgent(ABC):

@property
def adjustables(self) -> 'Sequence[Adjustable]':
def adjustables(self) -> Sequence[Adjustable]:
return self.model.adjustables

def restart(self) -> int:
Expand All @@ -29,15 +31,15 @@ def restart(self) -> int:

@property
@abstractmethod
def model(self) -> 'Model':
def model(self) -> Model:
pass

@property
@abstractmethod
def job(self) -> 'JobMeta':
def job(self) -> JobMeta:
pass

def update_config(self, i: int, params: 'DataFrame', id: str):
def update_config(self, i: int, params: DataFrame, id: str):
"""
For a given calibration iteration, i, update the input files/configuration to prepare for that iterations
calibration run.
Expand All @@ -57,7 +59,7 @@ def best_params(self) -> str:

class Agent(BaseAgent):

def __init__(self, model_conf, workdir: 'Path', log: bool=False, restart: bool=False, parameters: 'Optional[Mapping[str, Any]]' = {}):
def __init__(self, model_conf, workdir: Path, log: bool=False, restart: bool=False, parameters: Mapping[str, Any] | None = {}):
self._workdir = workdir
self._job = None
if restart:
Expand Down Expand Up @@ -86,11 +88,11 @@ def __init__(self, model_conf, workdir: 'Path', log: bool=False, restart: bool=F
self._params = parameters

@property
def parameters(self) -> 'Mapping[str, Any]':
def parameters(self) -> Mapping[str, Any]:
return self._params

@property
def workdir(self) -> 'Path':
def workdir(self) -> Path:
return self._workdir

@property
Expand All @@ -106,9 +108,9 @@ def cmd(self) -> str:
"""
Proxy method to build command from contained model binary and args
"""
return "{} {}".format(self.model.get_binary(), self.model.get_args())
return f"{self.model.get_binary()} {self.model.get_args()}"

def duplicate(self) -> 'Agent':
def duplicate(self) -> Agent:
#serialize a copy of the model
#FIXME ??? if you do self.model.resolve_paths() here, the duplicated agent
#doesn't have fully qualified paths...but if you do it in constructor, it works fine...
Expand Down
32 changes: 16 additions & 16 deletions python/ngen_cal/src/ngen/cal/calibratable.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@

from abc import ABC, abstractmethod
from pandas import Series, read_parquet # type: ignore
from typing import Optional, TYPE_CHECKING
from typing import TYPE_CHECKING
from pathlib import Path

if TYPE_CHECKING:
from pandas import DataFrame, Series
from pathlib import Path
from datetime import datetime
from typing import Tuple, Callable, Optional
from typing import Callable
from ngen.cal.model import EvaluationOptions
from ngen.cal.meta import JobMeta

Expand All @@ -18,11 +18,11 @@ class Adjustable(ABC):
An Adjustable interface defning required properties for adjusting an object's state
"""

def __init__(self, df: Optional['DataFrame'] = None):
def __init__(self, df: DataFrame | None = None):
self._df = df

@property
def df(self) -> 'DataFrame':
def df(self) -> DataFrame:
"""
A dataframe of the objects parameter values to calculate indexed relative to the variables
being calibrated. The columns of the dataframe will be appended to with each search iterations
Expand All @@ -46,14 +46,14 @@ def id(self) -> str:
pass

@property
def variables(self) -> 'Series':
def variables(self) -> Series:
"""
Index series of variables
"""
return Series(self.df.index.values)

@property
def bounds(self) -> 'Tuple[Series, Series]':
def bounds(self) -> tuple[Series, Series]:
"""The bounds of each parameter that is adjustable

Returns:
Expand All @@ -77,11 +77,11 @@ def update_params(self, iteration: int) -> None:
pass

@property
def check_point_file(self) -> 'Path':
def check_point_file(self) -> Path:
"""
Filename checkpoint files are saved to
"""
return Path('{}_parameter_df_state.parquet'.format(self.id))
return Path(f'{self.id}_parameter_df_state.parquet')

def check_point(self, iteration: int, info: JobMeta) -> None:
"""
Expand All @@ -90,7 +90,7 @@ def check_point(self, iteration: int, info: JobMeta) -> None:
path = info.workdir
self.df.to_parquet(path/self.check_point_file)

def load_df(self, path: 'Path') -> None:
def load_df(self, path: Path) -> None:
"""
Load saved calibration information
"""
Expand All @@ -104,9 +104,9 @@ class Evaluatable(ABC):
An Evaluatable interface defining required properties for a evaluating and object's state
"""

eval_params: 'EvaluationOptions'
eval_params: EvaluationOptions

def __init__(self, eval_params: 'EvaluationOptions', **kwargs):
def __init__(self, eval_params: EvaluationOptions, **kwargs):
"""
Args:
eval_params (EvaluationOptions): The options configuring this evaluatable
Expand All @@ -115,7 +115,7 @@ def __init__(self, eval_params: 'EvaluationOptions', **kwargs):

@property
@abstractmethod
def output(self) -> 'DataFrame':
def output(self) -> DataFrame:
"""
The output data for the calibrated object
Calibration re-reads the output each call, as the output for given calibration is expected to change
Expand All @@ -125,7 +125,7 @@ def output(self) -> 'DataFrame':

@property
@abstractmethod
def observed(self) -> 'DataFrame':
def observed(self) -> DataFrame:
"""
The observed data for this calibratable.
This should be rather static, and can be set at initialization then accessed via the property
Expand All @@ -134,15 +134,15 @@ def observed(self) -> 'DataFrame':

@property
@abstractmethod
def evaluation_range(self) -> 'Optional[Tuple[datetime, datetime]]':
def evaluation_range(self) -> tuple[datetime, datetime] | None:
"""
The datetime range to evaluate the model results at.
This should be a tuple in the form of (start_time, end_time).
"""
pass

@property
def objective(self, *args, **kwargs) -> 'Callable':
def objective(self, *args, **kwargs) -> Callable:
"""
The objective function to compute cost values with.

Expand Down Expand Up @@ -191,5 +191,5 @@ class Calibratable(Adjustable, Evaluatable):
"""
A Calibratable interface defining required properties for a calibratable object
"""
def __init__(self, df: Optional['DataFrame'] = None):
def __init__(self, df: DataFrame | None = None):
Adjustable.__init__(self, df)
21 changes: 11 additions & 10 deletions python/ngen_cal/src/ngen/cal/calibration_cathment.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

from pandas import DataFrame, read_csv # type: ignore
import shutil
from typing import TYPE_CHECKING
Expand All @@ -7,7 +9,6 @@
from pathlib import Path
from geopandas import GeoSeries
from datetime import datetime
from typing import Tuple, Optional
from .model import EvaluationOptions

from hypy.catchment import FormulatableCatchment # type: ignore
Expand All @@ -21,7 +22,7 @@ class AdjustableCatchment(FormulatableCatchment, Adjustable):
parameteters used by the catchment.
"""

def __init__(self, workdir: 'Path', id: str, nexus, params: dict = {}):
def __init__(self, workdir: Path, id: str, nexus, params: dict = {}):
"""Create an adjustable catchment and initialize its parameter space

Args:
Expand All @@ -34,7 +35,7 @@ def __init__(self, workdir: 'Path', id: str, nexus, params: dict = {}):
FormulatableCatchment.__init__(self=self, catchment_id=id, params=params, outflow=nexus)
Adjustable.__init__(self=self, df=DataFrame(params).rename(columns={'init': '0'}))
#FIXME paramterize
self._output_file = workdir/'{}.csv'.format(self.id)
self._output_file = workdir/f'{self.id}.csv'
self._workdir = workdir

def save_output(self, i) -> None:
Expand All @@ -43,7 +44,7 @@ def save_output(self, i) -> None:
"""
#FIXME ensure _output_file exists
#FIXME re-enable this once more complete
shutil.move(self._output_file, '{}_last'.format(self._output_file))
shutil.move(self._output_file, f'{self._output_file}_last')

#update handled in meta, TODO remove this method???
def update_params(self, iteration: int) -> None:
Expand All @@ -54,7 +55,7 @@ class EvaluatableCatchment(Evaluatable):
A catchment which is "observable" which means model output can be evaluated against
these observations for this catchment.
"""
def __init__(self, nexus: Nexus, start_time: str, end_time: str, fabric: "GeoSeries", output_var: str, eval_params: 'EvaluationOptions'):
def __init__(self, nexus: Nexus, start_time: str, end_time: str, fabric: GeoSeries, output_var: str, eval_params: EvaluationOptions):
"""Initialize the evaluatable catchment

Args:
Expand All @@ -81,11 +82,11 @@ def __init__(self, nexus: Nexus, start_time: str, end_time: str, fabric: "GeoSer
self._eval_range = self.eval_params._eval_range

@property
def evaluation_range(self) -> 'Optional[Tuple[datetime, datetime]]':
def evaluation_range(self) -> tuple[datetime, datetime] | None:
return self._eval_range

@property
def output(self) -> 'DataFrame':
def output(self) -> DataFrame:
"""
The model output hydrograph for this catchment
This re-reads the output file each call, as the output for given calibration catchment changes
Expand All @@ -112,13 +113,13 @@ def output(self, df):
self._output = df

@property
def observed(self) -> 'DataFrame':
def observed(self) -> DataFrame:
"""
The observed hydrograph for this catchment FIXME move output/observed to calibratable?
"""
hydrograph = self._observed
if hydrograph is None:
raise(RuntimeError("Error reading observation for {}".format(self._id)))
raise(RuntimeError(f"Error reading observation for {self._id}"))
return hydrograph

@observed.setter
Expand All @@ -129,7 +130,7 @@ class CalibrationCatchment(AdjustableCatchment, EvaluatableCatchment):
"""
A Calibratable interface defining required properties for a calibratable object
"""
def __init__(self, workdir: str, id: str, nexus: Nexus, start_time: str, end_time: str, fabric: "GeoSeries", output_var: str, eval_params: 'EvaluationOptions', params: dict = {}):
def __init__(self, workdir: str, id: str, nexus: Nexus, start_time: str, end_time: str, fabric: GeoSeries, output_var: str, eval_params: EvaluationOptions, params: dict = {}):
EvaluatableCatchment.__init__(self, nexus, start_time, end_time, fabric, output_var, eval_params)
AdjustableCatchment.__init__(self, workdir, id, nexus, params)

Expand Down
Loading
Loading