From 6350f85bb7feb3b69a6933b9ac2cd534a8d77f4b Mon Sep 17 00:00:00 2001 From: Carolyn Begeman Date: Sun, 26 Apr 2026 13:23:54 -0700 Subject: [PATCH 1/5] If decode_times=True, use attr of ds.Time to convert time variable to float days --- polaris/ocean/model/time.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/polaris/ocean/model/time.py b/polaris/ocean/model/time.py index b8ac2a311c..7ab28da3e4 100644 --- a/polaris/ocean/model/time.py +++ b/polaris/ocean/model/time.py @@ -1,6 +1,7 @@ import time from datetime import datetime +import cftime import numpy as np import pandas as pd @@ -26,10 +27,18 @@ def get_days_since_start(ds): ] t_arr = np.array(seconds_since_start, dtype=float) / 86400.0 elif 'Time' in ds.keys(): - t_vals = ds['Time'].values - t_pd = pd.to_datetime(t_vals) - t_arr = 1.0e9 * (t_pd - t_pd[0]) / np.timedelta64(1, 's') - t_arr = t_arr.astype(float) / 86400.0 + # This option works if decode_times=True when loading xr.Dataset + if 'Time' in ds['Time'].coords: + t_arr = cftime.date2num( + ds['Time'].values, + units=ds['Time'].Units.replace('seconds', 'days'), + calendar=ds['Time'].dt.calendar, + has_year_zero=True, + ) + else: + t_pd = pd.to_datetime(ds['Time'].values) + t_arr = 1.0e9 * (t_pd - t_pd[0]) / np.timedelta64(1, 's') + t_arr = t_arr.astype(float) / 86400.0 else: raise ValueError('Could not find a time variable in dataset') return t_arr From 1bd1b529a3d53ee6088d4c414d29974754902653 Mon Sep 17 00:00:00 2001 From: Carolyn Begeman Date: Wed, 29 Apr 2026 12:16:19 -0700 Subject: [PATCH 2/5] Change time_since_start so that it supports a format with decimal seconds ... and uses the default start time for MPAS-O and Omega --- polaris/mpas/time.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/polaris/mpas/time.py b/polaris/mpas/time.py index 78dfd8419b..ce60e8e06e 100644 --- a/polaris/mpas/time.py +++ b/polaris/mpas/time.py @@ -28,7 +28,7 @@ def time_index_from_xtime(xtime, dt_target, start_xtime=None): return time_index -def time_since_start(xtime, start_xtime=None): +def time_since_start(xtime, start_xtime='0001-01-01_01:00:00'): """ Determine the time elapsed since the start of the simulation @@ -48,9 +48,14 @@ def time_since_start(xtime, start_xtime=None): if start_xtime is None: start_xtime = xtime[0].decode() - t0 = datetime.datetime.strptime(start_xtime, '%Y-%m-%d_%H:%M:%S') + try: + time_format = '%Y-%m-%d_%H:%M:%S.%f' + t0 = datetime.datetime.strptime(start_xtime, time_format) + except ValueError: + time_format = '%Y-%m-%d_%H:%M:%S' + t0 = datetime.datetime.strptime(start_xtime, time_format) dt = np.zeros((len(xtime),)) for idx, xt in enumerate(xtime): - t = datetime.datetime.strptime(xt.decode(), '%Y-%m-%d_%H:%M:%S') + t = datetime.datetime.strptime(xt.decode(), time_format) dt[idx] = (t - t0).total_seconds() return dt From 26232fbe5645d6d9615a7efac9078425cef63c93 Mon Sep 17 00:00:00 2001 From: Carolyn Begeman Date: Wed, 29 Apr 2026 12:17:16 -0700 Subject: [PATCH 3/5] Use time_since_start for get_days_since_start with xtime variable --- polaris/ocean/model/time.py | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/polaris/ocean/model/time.py b/polaris/ocean/model/time.py index 7ab28da3e4..3c345a0e35 100644 --- a/polaris/ocean/model/time.py +++ b/polaris/ocean/model/time.py @@ -1,10 +1,11 @@ import time -from datetime import datetime import cftime import numpy as np import pandas as pd +from polaris.mpas.time import time_since_start + def get_days_since_start(ds): """ @@ -14,17 +15,9 @@ def get_days_since_start(ds): if 'daysSinceStartOfSim' in ds.keys(): t_arr = ds.daysSinceStartOfSim.values.astype(float) elif 'xtime' in ds.keys(): - timestamps = [] - for time_str in ds.xtime.values.astype(str): - try: - timestamp = datetime.strptime(time_str, '%Y-%m-%d_%H:%M:%S.%f') - except ValueError: - timestamp = datetime.strptime(time_str, '%Y-%m-%d_%H:%M:%S') - timestamps.append(timestamp) # Calculate seconds since the first timestamp - seconds_since_start = [ - (ts - timestamps[0]).total_seconds() for ts in timestamps - ] + seconds_since_start = time_since_start(ds.xtime.values) + # Convert to days t_arr = np.array(seconds_since_start, dtype=float) / 86400.0 elif 'Time' in ds.keys(): # This option works if decode_times=True when loading xr.Dataset From 1c6ca6c20326bc1ea9f9cd147bb75c6758ba1201 Mon Sep 17 00:00:00 2001 From: Carolyn Begeman Date: Wed, 29 Apr 2026 11:29:58 -0700 Subject: [PATCH 4/5] Use linear eos with seamount test --- polaris/tasks/ocean/seamount/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/polaris/tasks/ocean/seamount/__init__.py b/polaris/tasks/ocean/seamount/__init__.py index 1a020f7d24..fdedef053f 100644 --- a/polaris/tasks/ocean/seamount/__init__.py +++ b/polaris/tasks/ocean/seamount/__init__.py @@ -13,6 +13,7 @@ def add_seamount_tasks(component): indir = 'planar/seamount' config_filename = 'seamount.cfg' config = PolarisConfigParser(filepath=f'{indir}/{config_filename}') + config.add_from_package('polaris.ocean.eos', 'linear.cfg') config.add_from_package('polaris.tasks.ocean.seamount', config_filename) init_step = Init(component=component, name='init', indir=indir) From be04b9da2c11293f450e6dd3c74f6b0ca1d35298 Mon Sep 17 00:00:00 2001 From: Carolyn Begeman Date: Wed, 29 Apr 2026 11:33:48 -0700 Subject: [PATCH 5/5] Test out get_days_since_start with seamount viz --- polaris/tasks/ocean/seamount/viz.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/polaris/tasks/ocean/seamount/viz.py b/polaris/tasks/ocean/seamount/viz.py index 8c02e8ead4..493fd93c12 100644 --- a/polaris/tasks/ocean/seamount/viz.py +++ b/polaris/tasks/ocean/seamount/viz.py @@ -1,16 +1,15 @@ import cmocean # noqa: F401 import matplotlib.pyplot as plt import numpy as np -import pandas as pd import xarray as xr from mpas_tools.ocean.viz.transect import compute_transect, plot_transect -from polaris import Step from polaris.mpas import cell_mask_to_edge_mask +from polaris.ocean.model import OceanIOStep, get_days_since_start from polaris.viz import plot_horiz_field -class Viz(Step): +class Viz(OceanIOStep): """ A step for plotting the results of the default seamount forward step """ @@ -40,9 +39,11 @@ def run(self): """ Run this step of the task """ - ds_mesh = xr.load_dataset('mesh.nc') - ds_init = xr.load_dataset('init.nc') - ds = xr.load_dataset('output.nc') + ds_mesh = self.open_model_dataset('mesh.nc') + ds_init = self.open_model_dataset('init.nc', self.config) + ds = self.open_model_dataset( + 'output.nc', self.config, decode_times=True + ) x_min = ds_mesh.xVertex.min().values x_max = ds_mesh.xVertex.max().values @@ -115,10 +116,8 @@ def run(self): # Plot the time series of max velocity plt.figure(figsize=[12, 6], dpi=100) umax = np.amax(ds.normalVelocity[:, :, 0].values, axis=1) - t = ds.daysSinceStartOfSim.values - time = pd.to_timedelta(t) - days_float = time / pd.Timedelta(days=1) - plt.plot(days_float, umax, 'k-o', label='max(normalVelocity)') + t_days = get_days_since_start(ds) + plt.plot(t_days, umax, 'k-o', label='max(normalVelocity)') plt.xlabel('Time (days)') plt.ylabel('Maximum Velocity (m/s)') plt.legend()