Source code for space

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

"""Define initial space grid."""

import numpy as np

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


[docs] class SpaceGrid: """ Store initial domain dimensions. Attributes ---------- default_params : dict Parameters used when not specified in user input. geometry : str Domain geometry ('planar', 'cylindrical' or 'spherical'). work_dir : pathlib.Path Work directory. logger : :class:`log_utils.CustomLogger` Logger. zmin : float Initial position of left-hand domain boundary (m). zmax : float Initial position of right-hand domain boundary (m). nz_init : int Number of steps in initial space grid. z_init : 1D array Initial node positions. Ranges from 0 to zmax, size nz. This is where fluxes are evaluated. dz_init : 1D array Initial space step, shape (nz - 1,). zm_init : 1D array Initial midpoint positions, shape (nz - 1,). This is where composition variables are evaluated. """
[docs] def __init__(self, params, default_params, work_dir, logger): """Class constructor.""" self.default_params = default_params self.work_dir = work_dir self.logger = logger self.geometry = get_and_log(params, 'geometry', default_params['geometry'], logger) if self.geometry not in ('planar', 'cylindrical', 'spherical'): msg = f"Invalid geometry parameter {self.geometry}." raise ut.UserInputError(msg) from None self.z_init = self.make_grid(params) self.zmin = self.z_init[0] self.zmax = self.z_init[-1] self.nz_init = self.z_init.size self.dz_init = np.diff(self.z_init) self.zm_init = (self.z_init[:-1] + self.z_init[1:])/2
[docs] def make_grid(self, params): """ Make initial space grid from input file or parameters. If 'file' is given, the grid is read from work_dir/filename using np.genfromtxt, and zmin, zmax and nz are inferred from the grid. If not, see :meth:`make_grid_from_dict`. Raises ------ :class:`utils.UserInputError` If params contains both 'file' and space parameters. """ if 'file' in params: for x in ['nz', 'zmin', 'zmax', 'grid_type']: if x in params: msg = ("Found 'file' in space parameters. " f"Cannot specify {x}.") raise ut.UserInputError(msg) from None fpath = self.work_dir / params['file'] z_init = np.genfromtxt(fpath) else: z_init = self.make_grid_from_dict(params) return z_init
[docs] def make_grid_from_dict(self, params): """ Make grid from parameters. The type of grid is specified via the optional parameter 'grid_type', which can be 'linear' or 'geometric' and defaults to 'linear' : * 'linear': linear grid from zmin to zmax with size nz. * 'geometric': geometric grid from zmin to zmax with size nz and common ratio q. In both cases : * 'zmax' must be included in input * 'zmin' is optional and defaults to 0 * 'nz' is optional and defaults to value in default_params (can be set in 'user_data.toml'). * 'q' is optional and defaults to value in default_params. Raises ------ :class:`utils.UserInputError` If 'zmax' is not found in input dict. :class:`utils.UserInputError` If 'zmin' or 'zmax' are not compatible with the domain geometry. """ grid_type = get_and_log(params, 'grid_type', 'linear', self.logger) zmin = params.get('zmin', 0) try: zmax = params['zmax'] except AttributeError as exc: msg = "Space parameters : zmax is required." raise ut.UserInputError(msg) from exc nz = get_and_log(params, 'nz', self.default_params['nz'], self.logger) if grid_type == 'linear': z_init = np.linspace(zmin, zmax, num=nz) elif grid_type == 'geometric': q = get_and_log(params, 'q', self.default_params['q'], self.logger) z_init = geo_grid(zmin, zmax, nz, q) else: msg = f"Invalid grid_type '{grid_type}'." raise ut.UserInputError(msg) from None if self.geometry in ['cylindrical', 'spherical']: if zmin < 0: msg = (f"Found strictly negative zmin in {self.geometry} " f"geometry (zmin = '{zmin}'). " "zmin should be positive.") raise ut.UserInputError(msg) from None if zmax <= 0: msg = (f"Found negative zmax in {self.geometry} geometry " f"(zmax = '{zmax}'). " "zmax should be strictly positive.") raise ut.UserInputError(msg) from None if zmin >= zmax: msg = (f"Found zmin >= zmax in {self.geometry} geometry " f"(zmin = '{zmin}', zmax = '{zmax}'). " "zmin should be strictly smaller than zmax.") raise ut.UserInputError(msg) from None return z_init
[docs] def geo_grid(zmin, zmax, nz, q): """ Make space grid according to geometric progression. Parameters ---------- zmin : float Start position. zmax : float End position. nz : int Number of elements. q : float Common ratio. Returns ------- 1D array Geometric grid. """ dz0 = (zmax - zmin)*(1 - q)/(1 - q**(nz - 1)) idx = np.arange(nz) return zmin + dz0*(1 - q**idx)/(1 - q)