From 1d7922781f22b47658b44d5cfdbffd5cc3a0e723 Mon Sep 17 00:00:00 2001 From: ElliottKasoar <45317199+ElliottKasoar@users.noreply.github.com> Date: Fri, 10 Jan 2025 16:38:09 +0000 Subject: [PATCH] Improve logging and carbon tracking consistency (#371) * Raise error if tracking not set up * Add troubleshooting for carbon tracking * Ensure track_carbon flag set correctly * Pass tracking flag to sub-calculations * Allow extra kwargs to be passed to carbon tracker * Set attach_logger correctly * Refactor setting log and tracker defaults --- docs/source/user_guide/index.rst | 1 + docs/source/user_guide/troubleshooting.rst | 56 ++++++++++++++++++++++ janus_core/calculations/base.py | 31 +++++++----- janus_core/calculations/descriptors.py | 24 ++++++---- janus_core/calculations/eos.py | 25 ++++++---- janus_core/calculations/geom_opt.py | 20 ++++---- janus_core/calculations/md.py | 25 ++++++---- janus_core/calculations/phonons.py | 25 ++++++---- janus_core/calculations/single_point.py | 24 ++++++---- janus_core/helpers/log.py | 10 ++++ janus_core/helpers/utils.py | 35 ++++++++++++++ janus_core/training/preprocess.py | 18 ++++--- janus_core/training/train.py | 18 ++++--- tests/test_log.py | 25 +++++++++- 14 files changed, 254 insertions(+), 83 deletions(-) create mode 100644 docs/source/user_guide/troubleshooting.rst diff --git a/docs/source/user_guide/index.rst b/docs/source/user_guide/index.rst index 2926d959..172ea8f3 100644 --- a/docs/source/user_guide/index.rst +++ b/docs/source/user_guide/index.rst @@ -7,3 +7,4 @@ User guide command_line python + troubleshooting diff --git a/docs/source/user_guide/troubleshooting.rst b/docs/source/user_guide/troubleshooting.rst new file mode 100644 index 00000000..3abf419d --- /dev/null +++ b/docs/source/user_guide/troubleshooting.rst @@ -0,0 +1,56 @@ +=============== +Troubleshooting +=============== + +Carbon tracking +--------------- + +Enabling tracking (Python) +++++++++++++++++++++++++++ + +Carbon tracking can be enabled through the ``track_carbon`` option. +By default, this is ``True`` if logging is enabled, but requires setting ``attach_logger``, as this defaults to ``False``. + +For example, to track the carbon emissions during a single point calculation: + +.. code-block:: python + + from janus_core.calculations.single_point import SinglePoint + + sp = SinglePoint( + struct_path="tests/data/NaCl.cif", + attach_logger=True, + track_carbon=True, + ) + +This generates a log file, ``NaCl-singlepoint-log.yml``, which stores the emissions for the calculation. + + +In the case of multiple calculations, such as geometry optimisation triggered during molecular dynamics, +the emissions for each component of the calculation will be separate items in the log. + + +Disabling tracking (CLI) +++++++++++++++++++++++++ + +Currently, carbon tracking is enabled by default when using the command line interface, +saving the total calculating emissions to the generated summary file, as well as additional details and +per-calculation emissions to the log file. + +This can be disabled by passing the ``--no-tracker`` flag to any command. For example: + +.. code-block:: bash + + janus singlepoint --struct tests/data/NaCl.cif --no-tracker + + +Sudo access ++++++++++++ + +On some systems, such as MacOS, the carbon tracker may prompt for your password, if you have sudo access. +To avoid this, you can: + +1. Disable carbon tracking, as described in `Disabling tracking (CLI)`_. +3. Modify your sudoers file, as described `here `_, to provide sudo rights for all future calculations. +2. Provide your password. This may be saved for a period of time, but will need to be entered again in future. +4. Fail authentication, for example by entering an invalid or no password three times, which triggers the tracking to default to a constant power. diff --git a/janus_core/calculations/base.py b/janus_core/calculations/base.py index eb8b3756..22b5a9e6 100644 --- a/janus_core/calculations/base.py +++ b/janus_core/calculations/base.py @@ -16,7 +16,7 @@ ) from janus_core.helpers.log import config_logger, config_tracker from janus_core.helpers.struct_io import input_structs -from janus_core.helpers.utils import FileNameMixin, none_to_dict +from janus_core.helpers.utils import FileNameMixin, none_to_dict, set_log_tracker class BaseCalculation(FileNameMixin): @@ -47,12 +47,14 @@ class BaseCalculation(FileNameMixin): Keyword arguments to pass to the selected calculator. Default is {}. set_calc : bool | None Whether to set (new) calculators for structures. Default is None. - attach_logger : bool - Whether to attach a logger. Default is False. + attach_logger : bool | None + Whether to attach a logger. Default is True if "filename" is passed in + log_kwargs, else False. log_kwargs : dict[str, Any] | None Keyword arguments to pass to `config_logger`. Default is {}. - track_carbon : bool - Whether to track carbon emissions of calculation. Default is True. + track_carbon : bool | None + Whether to track carbon emissions of calculation. Requires attach_logger. + Default is True if attach_logger is True, else False. tracker_kwargs : dict[str, Any] | None Keyword arguments to pass to `config_tracker`. Default is {}. file_prefix : PathLike | None @@ -83,9 +85,9 @@ def __init__( sequence_allowed: bool = True, calc_kwargs: dict[str, Any] | None = None, set_calc: bool | None = None, - attach_logger: bool = False, + attach_logger: bool | None = None, log_kwargs: dict[str, Any] | None = None, - track_carbon: bool = True, + track_carbon: bool | None = None, tracker_kwargs: dict[str, Any] | None = None, file_prefix: PathLike | None = None, additional_prefix: str | None = None, @@ -118,12 +120,14 @@ def __init__( Keyword arguments to pass to the selected calculator. Default is {}. set_calc : bool | None Whether to set (new) calculators for structures. Default is None. - attach_logger : bool - Whether to attach a logger. Default is False. + attach_logger : bool | None + Whether to attach a logger. Default is True if "filename" is passed in + log_kwargs, else False. log_kwargs : dict[str, Any] | None Keyword arguments to pass to `config_logger`. Default is {}. - track_carbon : bool - Whether to track carbon emissions of calculation. Default is True. + track_carbon : bool | None + Whether to track carbon emissions of calculation. Requires attach_logger. + Default is True if attach_logger is True, else False. tracker_kwargs : dict[str, Any] | None Keyword arguments to pass to `config_tracker`. Default is {}. file_prefix : PathLike | None @@ -146,12 +150,15 @@ def __init__( self.read_kwargs = read_kwargs self.calc_kwargs = calc_kwargs self.log_kwargs = log_kwargs - self.track_carbon = track_carbon self.tracker_kwargs = tracker_kwargs if not self.model_path and "model_path" in self.calc_kwargs: raise ValueError("`model_path` must be passed explicitly") + attach_logger, self.track_carbon = set_log_tracker( + attach_logger, log_kwargs, track_carbon + ) + # Read structures and/or attach calculators # Note: logger not set up so yet so not passed here self.struct = input_structs( diff --git a/janus_core/calculations/descriptors.py b/janus_core/calculations/descriptors.py index 6b71a56e..f3a42eb7 100644 --- a/janus_core/calculations/descriptors.py +++ b/janus_core/calculations/descriptors.py @@ -47,12 +47,14 @@ class Descriptors(BaseCalculation): Keyword arguments to pass to the selected calculator. Default is {}. set_calc : bool | None Whether to set (new) calculators for structures. Default is None. - attach_logger : bool - Whether to attach a logger. Default is False. + attach_logger : bool | None + Whether to attach a logger. Default is True if "filename" is passed in + log_kwargs, else False. log_kwargs : dict[str, Any] | None Keyword arguments to pass to `config_logger`. Default is {}. - track_carbon : bool - Whether to track carbon emissions of calculation. Default is True. + track_carbon : bool | None + Whether to track carbon emissions of calculation. Default is True if + attach_logger is True, else False. tracker_kwargs : dict[str, Any] | None Keyword arguments to pass to `config_tracker`. Default is {}. invariants_only : bool @@ -83,9 +85,9 @@ def __init__( read_kwargs: ASEReadArgs | None = None, calc_kwargs: dict[str, Any] | None = None, set_calc: bool | None = None, - attach_logger: bool = False, + attach_logger: bool | None = None, log_kwargs: dict[str, Any] | None = None, - track_carbon: bool = True, + track_carbon: bool | None = None, tracker_kwargs: dict[str, Any] | None = None, invariants_only: bool = True, calc_per_element: bool = False, @@ -117,12 +119,14 @@ def __init__( Keyword arguments to pass to the selected calculator. Default is {}. set_calc : bool | None Whether to set (new) calculators for structures. Default is None. - attach_logger : bool - Whether to attach a logger. Default is False. + attach_logger : bool | None + Whether to attach a logger. Default is True if "filename" is passed in + log_kwargs, else False. log_kwargs : dict[str, Any] | None Keyword arguments to pass to `config_logger`. Default is {}. - track_carbon : bool - Whether to track carbon emissions of calculation. Default is True. + track_carbon : bool | None + Whether to track carbon emissions of calculation. Requires attach_logger. + Default is True if attach_logger is True, else False. tracker_kwargs : dict[str, Any] | None Keyword arguments to pass to `config_tracker`. Default is {}. invariants_only : bool diff --git a/janus_core/calculations/eos.py b/janus_core/calculations/eos.py index bd7bc863..236e3fa9 100644 --- a/janus_core/calculations/eos.py +++ b/janus_core/calculations/eos.py @@ -50,12 +50,14 @@ class EoS(BaseCalculation): Keyword arguments to pass to the selected calculator. Default is {}. set_calc : bool | None Whether to set (new) calculators for structures. Default is None. - attach_logger : bool - Whether to attach a logger. Default is False. + attach_logger : bool | None + Whether to attach a logger. Default is True if "filename" is passed in + log_kwargs, else False. log_kwargs : dict[str, Any] | None Keyword arguments to pass to `config_logger`. Default is {}. - track_carbon : bool - Whether to track carbon emissions of calculation. Default is True. + track_carbon : bool | None + Whether to track carbon emissions of calculation. Default is True if + attach_logger is True, else False. tracker_kwargs : dict[str, Any] | None Keyword arguments to pass to `config_tracker`. Default is {}. min_volume : float @@ -115,9 +117,9 @@ def __init__( read_kwargs: ASEReadArgs | None = None, calc_kwargs: dict[str, Any] | None = None, set_calc: bool | None = None, - attach_logger: bool = False, + attach_logger: bool | None = None, log_kwargs: dict[str, Any] | None = None, - track_carbon: bool = True, + track_carbon: bool | None = None, tracker_kwargs: dict[str, Any] | None = None, min_volume: float = 0.95, max_volume: float = 1.05, @@ -157,12 +159,14 @@ def __init__( Keyword arguments to pass to the selected calculator. Default is {}. set_calc : bool | None Whether to set (new) calculators for structures. Default is None. - attach_logger : bool - Whether to attach a logger. Default is False. + attach_logger : bool | None + Whether to attach a logger. Default is True if "filename" is passed in + log_kwargs, else False. log_kwargs : dict[str, Any] | None Keyword arguments to pass to `config_logger`. Default is {}. - track_carbon : bool - Whether to track carbon emissions of calculation. Default is True. + track_carbon : bool | None + Whether to track carbon emissions of calculation. Requires attach_logger. + Default is True if attach_logger is True, else False. tracker_kwargs : dict[str, Any] | None Keyword arguments to pass to `config_tracker`. Default is {}. min_volume : float @@ -263,6 +267,7 @@ def __init__( "name": self.logger.name, "filemode": "a", } + self.minimize_kwargs["track_carbon"] = self.track_carbon # Set output files self.write_kwargs.setdefault("filename", None) diff --git a/janus_core/calculations/geom_opt.py b/janus_core/calculations/geom_opt.py index 6077fddd..cb26e227 100644 --- a/janus_core/calculations/geom_opt.py +++ b/janus_core/calculations/geom_opt.py @@ -51,11 +51,13 @@ class GeomOpt(BaseCalculation): set_calc : bool | None Whether to set (new) calculators for structures. Default is None. attach_logger : bool - Whether to attach a logger. Default is False. + Whether to attach a logger. Default is True if "filename" is passed in + log_kwargs, else False. log_kwargs : dict[str, Any] | None Keyword arguments to pass to `config_logger`. Default is {}. - track_carbon : bool - Whether to track carbon emissions of calculation. Default is True. + track_carbon : bool | None + Whether to track carbon emissions of calculation. Requires attach_logger. + Default is True if attach_logger is True, else False. tracker_kwargs : dict[str, Any] | None Keyword arguments to pass to `config_tracker`. Default is {}. fmax : float @@ -107,9 +109,9 @@ def __init__( read_kwargs: ASEReadArgs | None = None, calc_kwargs: dict[str, Any] | None = None, set_calc: bool | None = None, - attach_logger: bool = False, + attach_logger: bool | None = None, log_kwargs: dict[str, Any] | None = None, - track_carbon: bool = True, + track_carbon: bool | None = None, tracker_kwargs: dict[str, Any] | None = None, fmax: float = 0.1, steps: int = 1000, @@ -149,11 +151,13 @@ def __init__( set_calc : bool | None Whether to set (new) calculators for structures. Default is None. attach_logger : bool - Whether to attach a logger. Default is False. + Whether to attach a logger. Default is True if "filename" is passed in + log_kwargs, else False. log_kwargs : dict[str, Any] | None Keyword arguments to pass to `config_logger`. Default is {}. - track_carbon : bool - Whether to track carbon emissions of calculation. Default is True. + track_carbon : bool | None + Whether to track carbon emissions of calculation. Requires attach_logger. + Default is True if attach_logger is True, else False. tracker_kwargs : dict[str, Any] | None Keyword arguments to pass to `config_tracker`. Default is {}. fmax : float diff --git a/janus_core/calculations/md.py b/janus_core/calculations/md.py index bbe3c63f..b0581cd6 100644 --- a/janus_core/calculations/md.py +++ b/janus_core/calculations/md.py @@ -70,12 +70,14 @@ class MolecularDynamics(BaseCalculation): Keyword arguments to pass to the selected calculator. Default is {}. set_calc : bool | None Whether to set (new) calculators for structures. Default is None. - attach_logger : bool - Whether to attach a logger. Default is False. + attach_logger : bool | None + Whether to attach a logger. Default is True if "filename" is passed in + log_kwargs, else False. log_kwargs : dict[str, Any] | None Keyword arguments to pass to `config_logger`. Default is {}. - track_carbon : bool - Whether to track carbon emissions of calculation. Default is True. + track_carbon : bool | None + Whether to track carbon emissions of calculation. Requires attach_logger. + Default is True if attach_logger is True, else False. tracker_kwargs : dict[str, Any] | None Keyword arguments to pass to `config_tracker`. Default is {}. struct : Atoms @@ -185,9 +187,9 @@ def __init__( read_kwargs: ASEReadArgs | None = None, calc_kwargs: dict[str, Any] | None = None, set_calc: bool | None = None, - attach_logger: bool = False, + attach_logger: bool | None = None, log_kwargs: dict[str, Any] | None = None, - track_carbon: bool = True, + track_carbon: bool | None = None, tracker_kwargs: dict[str, Any] | None = None, ensemble: Ensembles | None = None, steps: int = 0, @@ -247,12 +249,14 @@ def __init__( Keyword arguments to pass to the selected calculator. Default is {}. set_calc : bool | None Whether to set (new) calculators for structures. Default is None. - attach_logger : bool - Whether to attach a logger. Default is False. + attach_logger : bool | None + Whether to attach a logger. Default is True if "filename" is passed in + log_kwargs, else False. log_kwargs : dict[str, Any] | None Keyword arguments to pass to `config_logger`. Default is {}. - track_carbon : bool - Whether to track carbon emissions of calculation. Default is True. + track_carbon : bool | None + Whether to track carbon emissions of calculation. Requires attach_logger. + Default is True if attach_logger is True, else False. tracker_kwargs : dict[str, Any] | None Keyword arguments to pass to `config_tracker`. Default is {}. ensemble : Ensembles @@ -497,6 +501,7 @@ def __init__( "name": self.logger.name, "filemode": "a", } + self.minimize_kwargs["track_carbon"] = self.track_carbon self.dyn: Langevin | VelocityVerlet | ASE_NPT self.n_atoms = len(self.struct) diff --git a/janus_core/calculations/phonons.py b/janus_core/calculations/phonons.py index fd936fb6..64b9047f 100644 --- a/janus_core/calculations/phonons.py +++ b/janus_core/calculations/phonons.py @@ -55,12 +55,14 @@ class Phonons(BaseCalculation): Keyword arguments to pass to the selected calculator. Default is {}. set_calc : bool | None Whether to set (new) calculators for structures. Default is None. - attach_logger : bool - Whether to attach a logger. Default is False. + attach_logger : bool | None + Whether to attach a logger. Default is True if "filename" is passed in + log_kwargs, else False. log_kwargs : dict[str, Any] | None Keyword arguments to pass to `config_logger`. Default is {}. - track_carbon : bool - Whether to track carbon emissions of calculation. Default is True. + track_carbon : bool | None + Whether to track carbon emissions of calculation. Requires attach_logger. + Default is True if attach_logger is True, else False. tracker_kwargs : dict[str, Any] | None Keyword arguments to pass to `config_tracker`. Default is {}. calcs : MaybeSequence[PhononCalcs] | None @@ -163,9 +165,9 @@ def __init__( read_kwargs: ASEReadArgs | None = None, calc_kwargs: dict[str, Any] | None = None, set_calc: bool | None = None, - attach_logger: bool = False, + attach_logger: bool | None = None, log_kwargs: dict[str, Any] | None = None, - track_carbon: bool = True, + track_carbon: bool | None = None, tracker_kwargs: dict[str, Any] | None = None, calcs: MaybeSequence[PhononCalcs] = (), supercell: MaybeList[int] = 2, @@ -213,12 +215,14 @@ def __init__( Keyword arguments to pass to the selected calculator. Default is {}. set_calc : bool | None Whether to set (new) calculators for structures. Default is None. - attach_logger : bool - Whether to attach a logger. Default is False. + attach_logger : bool | None + Whether to attach a logger. Default is True if "filename" is passed in + log_kwargs, else False. log_kwargs : dict[str, Any] | None Keyword arguments to pass to `config_logger`. Default is {}. - track_carbon : bool - Whether to track carbon emissions of calculation. Default is True. + track_carbon : bool | None + Whether to track carbon emissions of calculation. Requires attach_logger. + Default is True if attach_logger is True, else False. tracker_kwargs : dict[str, Any] | None Keyword arguments to pass to `config_tracker`. Default is {}. calcs : MaybeSequence[PhononCalcs] | None @@ -351,6 +355,7 @@ def __init__( "name": self.logger.name, "filemode": "a", } + self.minimize_kwargs["track_carbon"] = self.track_carbon # Write out file by default self.minimize_kwargs.setdefault("write_results", True) diff --git a/janus_core/calculations/single_point.py b/janus_core/calculations/single_point.py index 953f0070..07db9d4a 100644 --- a/janus_core/calculations/single_point.py +++ b/janus_core/calculations/single_point.py @@ -51,12 +51,14 @@ class SinglePoint(BaseCalculation): Keyword arguments to pass to the selected calculator. Default is {}. set_calc : bool | None Whether to set (new) calculators for structures. Default is None. - attach_logger : bool - Whether to attach a logger. Default is False. + attach_logger : bool | None + Whether to attach a logger. Default is True if "filename" is passed in + log_kwargs, else False. log_kwargs : dict[str, Any] | None Keyword arguments to pass to `config_logger`. Default is {}. - track_carbon : bool - Whether to track carbon emissions of calculation. Default is True. + track_carbon : bool | None + Whether to track carbon emissions of calculation. Requires attach_logger. + Default is True if attach_logger is True, else False. tracker_kwargs : dict[str, Any] | None Keyword arguments to pass to `config_tracker`. Default is {}. properties : MaybeSequence[Properties] @@ -90,9 +92,9 @@ def __init__( read_kwargs: ASEReadArgs | None = None, calc_kwargs: dict[str, Any] | None = None, set_calc: bool | None = None, - attach_logger: bool = False, + attach_logger: bool | None = None, log_kwargs: dict[str, Any] | None = None, - track_carbon: bool = True, + track_carbon: bool | None = None, tracker_kwargs: dict[str, Any] | None = None, properties: MaybeSequence[Properties] = (), write_results: bool = False, @@ -123,12 +125,14 @@ def __init__( Keyword arguments to pass to the selected calculator. Default is {}. set_calc : bool | None Whether to set (new) calculators for structures. Default is None. - attach_logger : bool - Whether to attach a logger. Default is False. + attach_logger : bool | None + Whether to attach a logger. Default is True if "filename" is passed in + log_kwargs, else False. log_kwargs : dict[str, Any] | None Keyword arguments to pass to `config_logger`. Default is {}. - track_carbon : bool - Whether to track carbon emissions of calculation. Default is True. + track_carbon : bool | None + Whether to track carbon emissions of calculation. Requires attach_logger. + Default is True if attach_logger is True, else False. tracker_kwargs : dict[str, Any] | None Keyword arguments to pass to `config_tracker`. Default is {}. properties : MaybeSequence[Properties] diff --git a/janus_core/helpers/log.py b/janus_core/helpers/log.py index 870b4446..0c19d19d 100644 --- a/janus_core/helpers/log.py +++ b/janus_core/helpers/log.py @@ -161,6 +161,7 @@ def config_tracker( country_iso_code: str = "GBR", save_to_file: bool = False, log_level: Literal["debug", "info", "warning", "error", "critical"] = "critical", + **kwargs, ) -> OfflineEmissionsTracker | None: """ Configure codecarbon tracker to log outputs. @@ -177,6 +178,8 @@ def config_tracker( Whether to also output results to a csv file. Default is False. log_level : Literal["debug", "info", "warning", "error", "critical"] Log level of internal carbon tracker log. Default is "critical". + **kwargs + Additional keyword arguments to pass to OfflineEmissionsTracker. Returns ------- @@ -193,6 +196,7 @@ def config_tracker( project_name="janus-core", log_level=log_level, allow_multiple_runs=True, + **kwargs, ) # Suppress further logging from codecarbon @@ -200,6 +204,12 @@ def config_tracker( while carbon_logger.hasHandlers(): carbon_logger.removeHandler(carbon_logger.handlers[0]) + if not hasattr(tracker, "_emissions"): + raise ValueError( + "Carbon tracker has not been configured correctly. Please try " + "reconfiguring, or disable the tracker." + ) + else: tracker = None diff --git a/janus_core/helpers/utils.py b/janus_core/helpers/utils.py index 32ed6043..b1976c32 100644 --- a/janus_core/helpers/utils.py +++ b/janus_core/helpers/utils.py @@ -517,3 +517,38 @@ def selector_len(slc: SliceLike | list, selectable_length: int) -> int: if stop is None: stop = selectable_length return len(range(start, stop, step)) + + +def set_log_tracker( + attach_logger: bool, log_kwargs: dict, track_carbon: bool +) -> tuple[bool, bool]: + """ + Set attach_logger and track_carbon default values. + + Parameters + ---------- + attach_logger : bool + Whether to attach a logger. + log_kwargs : dict[str, Any] + Keyword arguments to pass to `config_logger`. + track_carbon : bool + Whether to track carbon emissions of calculation. + + Returns + ------- + tuple[bool, bool] + Default values for attach_logger and track_carbon. + """ + if "filename" in log_kwargs: + attach_logger = True + else: + attach_logger = attach_logger if attach_logger else False + + if not attach_logger: + if track_carbon: + raise ValueError("Carbon tracking requires logging to be enabled") + track_carbon = False + else: + track_carbon = track_carbon if track_carbon is not None else True + + return attach_logger, track_carbon diff --git a/janus_core/training/preprocess.py b/janus_core/training/preprocess.py index 49b74a6f..dd02536f 100644 --- a/janus_core/training/preprocess.py +++ b/janus_core/training/preprocess.py @@ -11,7 +11,7 @@ from janus_core.helpers.janus_types import PathLike from janus_core.helpers.log import config_logger, config_tracker -from janus_core.helpers.utils import check_files_exist, none_to_dict +from janus_core.helpers.utils import check_files_exist, none_to_dict, set_log_tracker def preprocess( @@ -19,7 +19,7 @@ def preprocess( req_file_keys: Sequence[PathLike] = ("train_file", "test_file", "valid_file"), attach_logger: bool = False, log_kwargs: dict[str, Any] | None = None, - track_carbon: bool = True, + track_carbon: bool | None = None, tracker_kwargs: dict[str, Any] | None = None, ) -> None: """ @@ -35,12 +35,14 @@ def preprocess( req_file_keys : Sequence[PathLike] List of files that must exist if defined in the configuration file. Default is ("train_file", "test_file", "valid_file"). - attach_logger : bool - Whether to attach a logger. Default is False. + attach_logger : bool | None + Whether to attach a logger. Default is True if "filename" is passed in + log_kwargs, else False. log_kwargs : dict[str, Any] | None Keyword arguments to pass to `config_logger`. Default is {}. - track_carbon : bool - Whether to track carbon emissions of calculation. Default is True. + track_carbon : bool | None + Whether to track carbon emissions of calculation. Requires attach_logger. + Default is True if attach_logger is True, else False. tracker_kwargs : dict[str, Any] | None Keyword arguments to pass to `config_tracker`. Default is {}. """ @@ -51,6 +53,10 @@ def preprocess( options = yaml.safe_load(file) check_files_exist(options, req_file_keys) + attach_logger, track_carbon = set_log_tracker( + attach_logger, log_kwargs, track_carbon + ) + # Configure logging if attach_logger: log_kwargs.setdefault("filename", "preprocess-log.yml") diff --git a/janus_core/training/train.py b/janus_core/training/train.py index 85ccefe4..8abc33f7 100644 --- a/janus_core/training/train.py +++ b/janus_core/training/train.py @@ -11,7 +11,7 @@ from janus_core.helpers.janus_types import PathLike from janus_core.helpers.log import config_logger, config_tracker -from janus_core.helpers.utils import check_files_exist, none_to_dict +from janus_core.helpers.utils import check_files_exist, none_to_dict, set_log_tracker def train( @@ -24,7 +24,7 @@ def train( ), attach_logger: bool = False, log_kwargs: dict[str, Any] | None = None, - track_carbon: bool = True, + track_carbon: bool | None = None, tracker_kwargs: dict[str, Any] | None = None, ) -> None: """ @@ -40,12 +40,14 @@ def train( req_file_keys : Sequence[PathLike] List of files that must exist if defined in the configuration file. Default is ("train_file", "test_file", "valid_file", "statistics_file"). - attach_logger : bool - Whether to attach a logger. Default is False. + attach_logger : bool | None + Whether to attach a logger. Default is True if "filename" is passed in + log_kwargs, else False. log_kwargs : dict[str, Any] | None Keyword arguments to pass to `config_logger`. Default is {}. - track_carbon : bool - Whether to track carbon emissions of calculation. Default is True. + track_carbon : bool | None + Whether to track carbon emissions of calculation. Requires attach_logger. + Default is True if attach_logger is True, else False. tracker_kwargs : dict[str, Any] | None Keyword arguments to pass to `config_tracker`. Default is {}. """ @@ -56,6 +58,10 @@ def train( options = yaml.safe_load(file) check_files_exist(options, req_file_keys) + attach_logger, track_carbon = set_log_tracker( + attach_logger, log_kwargs, track_carbon + ) + # Configure logging if attach_logger: log_kwargs.setdefault("filename", "train-log.yml") diff --git a/tests/test_log.py b/tests/test_log.py index 63a9593c..5fce17cf 100644 --- a/tests/test_log.py +++ b/tests/test_log.py @@ -2,9 +2,11 @@ from __future__ import annotations +from codecarbon import OfflineEmissionsTracker +import pytest import yaml -from janus_core.helpers.log import config_logger +from janus_core.helpers.log import config_logger, config_tracker from tests.utils import assert_log_contains @@ -30,3 +32,24 @@ def test_multiline_log(tmp_path): assert len(log_dicts[0]["message"]) == 3 assert_log_contains(log_path, includes=["Line 1", "Line 4"]) + + +def test_tracker(tmp_path): + """Test tracker can be correctly set up.""" + from pathlib import Path + + tmp_path = Path(".") + log_path = tmp_path / "test.log" + + logger = config_logger(name=__name__, filename=log_path) + tracker = config_tracker(janus_logger=logger) + assert isinstance(tracker, OfflineEmissionsTracker) + + +def test_tracker_error(tmp_path): + """Test tracker raises error if not set up correctly.""" + log_path = tmp_path / "test.log" + + logger = config_logger(name=__name__, filename=log_path) + with pytest.raises(ValueError): + config_tracker(janus_logger=logger, country_iso_code="TEST")