From d9ae6264342e38916ea0f7a867095bef66cfb676 Mon Sep 17 00:00:00 2001 From: nhartney Date: Thu, 23 Oct 2025 15:34:55 +0100 Subject: [PATCH 1/8] start of trying to change I/O so that we can use Gusto in the pde solution --- gusto/timestepping/timestepper.py | 32 +++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/gusto/timestepping/timestepper.py b/gusto/timestepping/timestepper.py index d7c80391b..4bb233997 100644 --- a/gusto/timestepping/timestepper.py +++ b/gusto/timestepping/timestepper.py @@ -180,7 +180,7 @@ def log_field_stats(self): max_val = field_data.max() logger.debug(f'{field_name}, min: {min_val}, max: {max_val}') - def run(self, t, tmax, pick_up=False): + def run(self, t, tmax, pick_up=False, first_run=True): """ Runs the model for the specified time, from t to tmax @@ -189,16 +189,24 @@ def run(self, t, tmax, pick_up=False): tmax (float): the end time of the run pick_up: (bool): specify whether to pick_up from a previous run """ + self.first_run = first_run + if self.first_run == True: + # Set up diagnostics, which may set up some fields necessary to pick up + self.io.setup_diagnostics(self.fields) + self.io.setup_log_courant(self.fields) + if self.equation.domain.mesh.extruded: + self.io.setup_log_courant(self.fields, component='horizontal') + self.io.setup_log_courant(self.fields, component='vertical') + if self.transporting_velocity != "prognostic": + self.io.setup_log_courant(self.fields, name='transporting_velocity', + expression=self.transporting_velocity) + + # Set up dump, which may also include an initial dump + with timed_stage("Dump output"): + logger.debug('Dumping output to disk') + self.io.setup_dump(self.fields, t, pick_up) - # Set up diagnostics, which may set up some fields necessary to pick up - self.io.setup_diagnostics(self.fields) - self.io.setup_log_courant(self.fields) - if self.equation.domain.mesh.extruded: - self.io.setup_log_courant(self.fields, component='horizontal') - self.io.setup_log_courant(self.fields, component='vertical') - if self.transporting_velocity != "prognostic": - self.io.setup_log_courant(self.fields, name='transporting_velocity', - expression=self.transporting_velocity) + self.first_run = False if pick_up: # Pick up fields, and return other info to be picked up @@ -213,10 +221,6 @@ def run(self, t, tmax, pick_up=False): else: self.step = 1 - # Set up dump, which may also include an initial dump - with timed_stage("Dump output"): - logger.debug('Dumping output to disk') - self.io.setup_dump(self.fields, t, pick_up) self.log_field_stats() From ead2c44d7b30ccde8cf1cc81243acf60ee81f3be Mon Sep 17 00:00:00 2001 From: nhartney Date: Thu, 6 Nov 2025 14:32:57 +0000 Subject: [PATCH 2/8] fix lint --- gusto/timestepping/timestepper.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/gusto/timestepping/timestepper.py b/gusto/timestepping/timestepper.py index 4bb233997..8e8948ca3 100644 --- a/gusto/timestepping/timestepper.py +++ b/gusto/timestepping/timestepper.py @@ -190,7 +190,7 @@ def run(self, t, tmax, pick_up=False, first_run=True): pick_up: (bool): specify whether to pick_up from a previous run """ self.first_run = first_run - if self.first_run == True: + if self.first_run: # Set up diagnostics, which may set up some fields necessary to pick up self.io.setup_diagnostics(self.fields) self.io.setup_log_courant(self.fields) @@ -221,7 +221,6 @@ def run(self, t, tmax, pick_up=False, first_run=True): else: self.step = 1 - self.log_field_stats() self.t.assign(t) From f6949fca7a2382cc94105a314ab6d69fc7aac1c7 Mon Sep 17 00:00:00 2001 From: nhartney Date: Mon, 10 Nov 2025 15:22:09 +0000 Subject: [PATCH 3/8] rename attribute and set it in the BaseTimestepper init --- gusto/timestepping/timestepper.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/gusto/timestepping/timestepper.py b/gusto/timestepping/timestepper.py index 8e8948ca3..eba1a658f 100644 --- a/gusto/timestepping/timestepper.py +++ b/gusto/timestepping/timestepper.py @@ -20,11 +20,12 @@ class BaseTimestepper(object, metaclass=ABCMeta): """Base class for timesteppers.""" - def __init__(self, equation, io): + def __init__(self, equation, io, init_io=True): """ Args: equation (:class:`PrognosticEquation`): the prognostic equation. io (:class:`IO`): the model's object for controlling input/output. + init_io (:bool): whether or not to set up the IO """ self.equation = equation @@ -39,6 +40,8 @@ def __init__(self, equation, io): self.io.log_parameters(equation) + self.init_io = init_io + @abstractproperty def transporting_velocity(self): return NotImplementedError @@ -180,7 +183,7 @@ def log_field_stats(self): max_val = field_data.max() logger.debug(f'{field_name}, min: {min_val}, max: {max_val}') - def run(self, t, tmax, pick_up=False, first_run=True): + def run(self, t, tmax, pick_up=False): """ Runs the model for the specified time, from t to tmax @@ -189,8 +192,7 @@ def run(self, t, tmax, pick_up=False, first_run=True): tmax (float): the end time of the run pick_up: (bool): specify whether to pick_up from a previous run """ - self.first_run = first_run - if self.first_run: + if self.init_io: # Set up diagnostics, which may set up some fields necessary to pick up self.io.setup_diagnostics(self.fields) self.io.setup_log_courant(self.fields) @@ -206,7 +208,7 @@ def run(self, t, tmax, pick_up=False, first_run=True): logger.debug('Dumping output to disk') self.io.setup_dump(self.fields, t, pick_up) - self.first_run = False + self.init_io = False if pick_up: # Pick up fields, and return other info to be picked up From ca9d65e01b34b01fb1cad7c9648e66313aff52f0 Mon Sep 17 00:00:00 2001 From: jshipton Date: Mon, 17 Nov 2025 13:49:38 +0000 Subject: [PATCH 4/8] init_io does not need to be in the interface --- gusto/timestepping/timestepper.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gusto/timestepping/timestepper.py b/gusto/timestepping/timestepper.py index eba1a658f..dc520e3e6 100644 --- a/gusto/timestepping/timestepper.py +++ b/gusto/timestepping/timestepper.py @@ -20,7 +20,7 @@ class BaseTimestepper(object, metaclass=ABCMeta): """Base class for timesteppers.""" - def __init__(self, equation, io, init_io=True): + def __init__(self, equation, io): """ Args: equation (:class:`PrognosticEquation`): the prognostic equation. @@ -30,6 +30,7 @@ def __init__(self, equation, io, init_io=True): self.equation = equation self.io = io + self.init_io = True # flag so that IO is only set up once self.dt = self.equation.domain.dt self.t = self.equation.domain.t self.reference_profiles_initialised = False From 9d0f2ae877867e35f17a133eb55e8ff7d6ab60b1 Mon Sep 17 00:00:00 2001 From: nhartney Date: Mon, 17 Nov 2025 13:53:06 +0000 Subject: [PATCH 5/8] fix init_io being passed in --- gusto/timestepping/timestepper.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/gusto/timestepping/timestepper.py b/gusto/timestepping/timestepper.py index dc520e3e6..3bc241a90 100644 --- a/gusto/timestepping/timestepper.py +++ b/gusto/timestepping/timestepper.py @@ -41,8 +41,6 @@ def __init__(self, equation, io): self.io.log_parameters(equation) - self.init_io = init_io - @abstractproperty def transporting_velocity(self): return NotImplementedError From d3fb5d367649ec0a988766e09dae4167e6d12379 Mon Sep 17 00:00:00 2001 From: jshipton Date: Sun, 21 Dec 2025 20:19:44 +0000 Subject: [PATCH 6/8] fixes to docstrings --- gusto/core/io.py | 2 +- gusto/timestepping/timestepper.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/gusto/core/io.py b/gusto/core/io.py index ebba47815..618aa066c 100644 --- a/gusto/core/io.py +++ b/gusto/core/io.py @@ -372,7 +372,7 @@ def setup_dump(self, state_fields, t, pick_up=False): Sets up a series of things used for outputting. This prepares the model for outputting. First it checks for the - existence the specified outputting directory, so prevent it being + existence of the specified output directory to prevent it being overwritten unintentionally. It then sets up the output files and the checkpointing file. diff --git a/gusto/timestepping/timestepper.py b/gusto/timestepping/timestepper.py index ab5f3d357..ec57a8425 100644 --- a/gusto/timestepping/timestepper.py +++ b/gusto/timestepping/timestepper.py @@ -25,7 +25,6 @@ def __init__(self, equation, io): Args: equation (:class:`PrognosticEquation`): the prognostic equation. io (:class:`IO`): the model's object for controlling input/output. - init_io (:bool): whether or not to set up the IO """ self.equation = equation From 98db81e22d6bda31667e388c400e80e28be24b1d Mon Sep 17 00:00:00 2001 From: jshipton Date: Sun, 21 Dec 2025 20:22:03 +0000 Subject: [PATCH 7/8] this might fix checkpointing test --- gusto/timestepping/timestepper.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/gusto/timestepping/timestepper.py b/gusto/timestepping/timestepper.py index ec57a8425..57974e25b 100644 --- a/gusto/timestepping/timestepper.py +++ b/gusto/timestepping/timestepper.py @@ -197,17 +197,17 @@ def run(self, t, tmax, pick_up=False): tmax (float): the end time of the run pick_up: (bool): specify whether to pick_up from a previous run """ - if self.init_io: - # Set up diagnostics, which may set up some fields necessary to pick up - self.io.setup_diagnostics(self.fields) - self.io.setup_log_courant(self.fields) - if self.equation.domain.mesh.extruded: - self.io.setup_log_courant(self.fields, component='horizontal') - self.io.setup_log_courant(self.fields, component='vertical') - if self.transporting_velocity != "prognostic": - self.io.setup_log_courant(self.fields, name='transporting_velocity', - expression=self.transporting_velocity) + # Set up diagnostics, which may set up some fields necessary to pick up + self.io.setup_diagnostics(self.fields) + self.io.setup_log_courant(self.fields) + if self.equation.domain.mesh.extruded: + self.io.setup_log_courant(self.fields, component='horizontal') + self.io.setup_log_courant(self.fields, component='vertical') + if self.transporting_velocity != "prognostic": + self.io.setup_log_courant(self.fields, name='transporting_velocity', + expression=self.transporting_velocity) + if self.init_io: # Set up dump, which may also include an initial dump with timed_stage("Dump output"): logger.debug('Dumping output to disk') From 8e08e918cae7861d52dd6d9f2ff38643536b4b9a Mon Sep 17 00:00:00 2001 From: jshipton Date: Wed, 7 Jan 2026 11:45:31 +0000 Subject: [PATCH 8/8] slightly sanitise logic --- gusto/core/io.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/gusto/core/io.py b/gusto/core/io.py index 618aa066c..c7f7f083b 100644 --- a/gusto/core/io.py +++ b/gusto/core/io.py @@ -390,9 +390,10 @@ def setup_dump(self, state_fields, t, pick_up=False): raise_parallel_exception = 0 error = None - if any([self.output.dump_vtus, self.output.dump_nc, + setup_dir = any([self.output.dump_vtus, self.output.dump_nc, self.output.dumplist_latlon, self.output.dump_diagnostics, - self.output.point_data, self.output.checkpoint and not pick_up]): + self.output.point_data, self.output.checkpoint]) and not pick_up + if setup_dir: # setup output directory and check that it does not already exist self.dumpdir = path.join("results", self.output.dirname) running_tests = '--running-tests' in sys.argv or "pytest" in self.output.dirname