Source code for time_grid

# Copyright 2025-2026 Onera
# This file is part of the Noda package
# SPDX-License-Identifier: GPL-3.0-or-later

"""Define time grid."""

import numpy as np

import noda.utils as ut
from noda.log_utils import get_and_log


[docs] class TimeGrid: """ Store time-related parameters. Attributes ---------- th : float Simulation time in h. ts : float Simulation time in s. dt_multiplier : float Factor by which default time step is multiplied (see :meth:`make_time_steps`). dt : float Time step size in s. nt : int Number of time steps (positions in time sequence), including 0. num_out : int Number of time steps where simulation results are stored and saved on file. saved_steps : list of int Steps for which simulation results are stored and saved in log. saved_th : list of float Times in h for which simulation results are stored and saved in log. default_params : dict Parameters used when not specified in user input. logger : :class:`log_utils.CustomLogger` Logger. """
[docs] def __init__(self, params, x, dz, DT_funx, default_parameters, logger): """ Class constructor. The time step dt is calculated to ensure the stability of explicit schemes, with Fourier given in default_parameters. The DT value is calculated from the initial atom fraction profile. The time step can be made smaller using the user-specified dt_multiplier or nt_multiplier. Parameters ---------- params : dict Time-related input parameters. x : dict of 1D array Initial atom fraction profile. dz : 1D array Initial space step. DT_funx : function Calculates tracer diffusion coefficients. default_parameters : dict Parameters used when not specified in user input. logger : :class:`log_utils.CustomLogger` Logger. Raises ------ :class:`utils.UserInputError` If params contains both th and ts. :class:`utils.UserInputError` If params contains both nt_multipler and dt_multiplier. """ msg = "Reading 'time' table. " cond1 = 'th' in params cond2 = 'ts' in params if (cond1 and cond2) or (not cond1 and not cond2): msg += "Expecting either 'th' or 'ts'." raise ut.UserInputError(msg) from None if 'th' in params: self.th = params['th'] self.ts = self.th * 3600 elif 'ts' in params: self.ts = params['ts'] self.th = self.ts / 3600 if 'dt_multiplier' in params and 'nt_multiplier' in params: msg += "Cannot specify both 'dt_multiplier' and 'nt_multiplier'." raise ut.UserInputError(msg) from None if 'nt_multiplier' in params: nt_multiplier = params['nt_multiplier'] self.dt_multiplier = 1/nt_multiplier else: self.dt_multiplier = params.get('dt_multiplier', 1) self.default_parameters = default_parameters self.logger = logger self.dt, self.nt = self.make_time_steps(x, dz, DT_funx) self.saved_steps, self.saved_th = self.make_saved_steps(params) self.num_out = self.saved_steps.size
[docs] def make_time_steps(self, x, dz, DT_funx): """Calculate time steps.""" x_arr = np.array(list(x.values())) DTmax = np.max(DT_funx(x_arr)) Fo = self.default_parameters['Fourier_number'] dt = Fo*self.dt_multiplier*dz.min()**2/DTmax nt = int(self.ts/dt) + 1 # Adjust dt to recover ts (rounding nt introduces error) dt = self.ts/(nt - 1) # Impose lower bound on number of time steps if nt < self.default_parameters['min_number_time_steps']: nt = self.default_parameters['min_number_time_steps'] dt = self.ts/(nt - 1) return dt, nt
[docs] def make_saved_steps(self, params): """ Add list of time steps where simulation results will be saved to file. The time steps are determined based on the num_out parameter. """ if 'num_out' in params: if params['num_out'] < 2: msg = ("'num_out' must be greater than 2. " f"Found 'num_out = {params['num_out']}'") raise ut.UserInputError(msg) from None num_out = get_and_log(params, 'num_out', self.default_parameters['num_out'], self.logger) if num_out == 'all': num_out = self.nt steps = np.linspace(0, self.nt - 1, num=num_out) saved_steps = np.around(steps).astype(int) saved_th = saved_steps*self.dt/3600 if len(saved_steps) > 1e4: question = """ The simulation will generate a large output file. Do you want to proceed ? [y/N] (Check "num_out" parameter in input file.)""" ans = input(question) or 'n' if ans != ('y' or 'Y'): raise ut.UserInputError('Canceled') return saved_steps, saved_th