Source code for gala.integrate.timespec

"""Helper function for turning different ways of specifying the integration
times into an array of times.
"""

import numpy as np

__all__ = ["parse_time_specification"]


[docs] def parse_time_specification(units, dt=None, n_steps=None, t1=None, t2=None, t=None): """ Parse different ways of specifying integration times into an array of times. This function accepts several different combinations of parameters to specify the times at which to evaluate the integrated orbit. The supported combinations allow for flexible time specification in orbit integration. Parameters ---------- units : :class:`~gala.units.UnitSystem` The unit system to use for dimensionful time quantities. dt : float or array_like, optional Timestep(s) for integration. Can be a scalar for fixed timesteps or an array of timesteps for variable spacing. n_steps : int, optional Number of integration steps to take. t1 : float, optional Initial time for the integration. t2 : float, optional Final time for the integration. t : array_like, optional Explicit array of times at which to evaluate the orbit. Returns ------- times : :class:`~numpy.ndarray` Array of times at which the orbit will be evaluated. Raises ------ ValueError If the time specification is invalid or incomplete, or if the signs of ``dt`` and ``(t2-t1)`` are inconsistent. Examples -------- Fixed timestep with number of steps:: >>> times = parse_time_specification(units, dt=0.1, n_steps=100) Fixed timestep with start and end times:: >>> times = parse_time_specification(units, dt=0.1, t1=0, t2=10) Explicit array of times:: >>> import numpy as np >>> t_array = np.linspace(0, 10, 101) >>> times = parse_time_specification(units, t=t_array) Notes ----- The following parameter combinations are supported: * ``dt, n_steps[, t1]`` : Fixed timestep and number of steps * ``dt, t1, t2`` : Fixed timestep with start and end times * ``dt, t1`` : Array of timesteps with initial time (dt must be array) * ``n_steps, t1, t2`` : Number of steps between start and end times * ``t`` : Explicit array of times """ if n_steps is not None: # parse and validate n_steps n_steps = int(n_steps) if hasattr(dt, "unit"): dt = dt.decompose(units).value if hasattr(t1, "unit"): t1 = t1.decompose(units).value if hasattr(t2, "unit"): t2 = t2.decompose(units).value if hasattr(t, "unit"): t = t.decompose(units).value # t : array_like if t is not None: times = t return times.astype(np.float64) if dt is None and (t1 is None or t2 is None or n_steps is None): raise ValueError( "Invalid specification of integration time. See docstring for more " "information." ) # dt, n_steps[, t1] : (numeric, int[, numeric]) if dt is not None and n_steps is not None: if t1 is None: t1 = 0.0 times = parse_time_specification(units, dt=np.ones(n_steps + 1) * dt, t1=t1) # dt, t1, t2 : (numeric, numeric, numeric) elif dt is not None and t1 is not None and t2 is not None: if t2 < t1 and dt < 0: t_i = t1 times = [] ii = 0 while (t_i > t2) and (ii < 1e6): times.append(t_i) t_i += dt if times[-1] != t2: times.append(t2) return np.array(times, dtype=np.float64) if t2 > t1 and dt > 0: t_i = t1 times = [] ii = 0 while (t_i < t2) and (ii < 1e6): times.append(t_i) t_i += dt return np.array(times, dtype=np.float64) if dt == 0: raise ValueError("dt must be non-zero.") raise ValueError( "If t2 < t1, dt must be negative. If t1 < t2, dt must be positive." ) # dt, t1 : (array_like, numeric) elif isinstance(dt, np.ndarray) and t1 is not None: times = np.cumsum(np.append([0.0], dt)) + t1 times = times[:-1] # n_steps, t1, t2 : (int, numeric, numeric) elif dt is None and not (t1 is None or t2 is None or n_steps is None): times = np.linspace(t1, t2, n_steps, endpoint=True) else: raise ValueError("Invalid options. See docstring.") return times.astype(np.float64)