Source code for rolland.deflection

"""Defines deflection classes for FDM simulation.

.. autosummary::
    :toctree: deflection

    Deflection
    DeflectionEBBVertic
"""

import inspect
from abc import ABC, abstractmethod
from dataclasses import dataclass, field

from numpy import empty, linspace, zeros
from scipy.sparse.linalg import splu

from .discretization import Discretization
from .excitation import Excitation
from .track import Track


[docs] @dataclass(kw_only=True) class Deflection(ABC): r"""Abstract base class for deflection classes. Attributes ---------- excit : Excitation Excitation instance. discr : Discretization Discretization instance. """ discr: Discretization excit: Excitation track: Track = field(init=False) def __post_init__(self, *args, **kwargs): """post_init method to set track attribute after initialization.""" self.track = self.discr.track
[docs] @abstractmethod def validate_deflection(self): """Validate deflection."""
[docs] class DeflectionEBBVertic(Deflection): r"""Calculate deflection according to :cite:t:`stampka2022a`. Attributes ---------- track : Track Track instance. excit : Excitation Excitation instance. discr : Discretization Discretization instance. deflection : numpy.ndarray Deflection array :math:`[m]`. ind_excit : int Index of excitation point :math:`[-]`. """ def __init__(self, *args, **kwargs): """ Initialize the DeflectionFDMStampka class. Parameters ---------- *args : tuple Variable length argument list. **kwargs : dict Arbitrary keyword arguments. Attributes ---------- deflection : numpy.ndarray Array of calculated deflections with shape (2 * nx, nt + 1). """ super().__init__(*args, **kwargs) # Initialize starting values self.calc_force() defl = self.initialize_start_values() # Calculate deflection self.deflection = self.calc_deflection(defl) __init__.__signature__ = inspect.signature(Deflection.__init__)
[docs] def validate_deflection(self): """Validate deflection."""
[docs] def initialize_start_values(self): """Set starting values of deflections to zero. Returns ------- defl : numpy.ndarray Array of deflections initialized to zero with shape (2 * nx, nt + 1). """ defl = empty((2 * self.discr.nx, self.discr.nt + 1)) # Set starting values to zero for two time steps defl[:, 0:2] = zeros((2 * self.discr.nx, 2)) return defl
[docs] def calc_force(self): """Calculate force array.""" t = linspace(0, self.discr.sim_t, self.discr.nt) self.force = self.excit.force(t)
[docs] def calc_rightside_crank_nicolson(self, u1, u0, ind_excit, t): """Calculate the right-hand side of the equation according to :cite:t:`stampka2022a`. Parameters ---------- u1 : numpy.ndarray Deflection array at the current time step. u0 : numpy.ndarray Deflection array at the previous time step. ind_excit : int Index of the excitation point. t : int Current time step. Returns ------- numpy.ndarray Right-hand side of the equation. """ # Write excitation force for time step t into force array f = zeros(2 * self.discr.nx) if isinstance(ind_excit, list): for idx in ind_excit: f[idx] = self.force[t] else: f[ind_excit] = self.force[t] return (self.discr.B.dot(u1) + self.discr.C.dot(u0) + self.discr.dt ** 2 / (self.track.rail.mr * self.discr.dx) * f)
[docs] def calc_deflection(self, defl): """ Calculate deflection. Parameters ---------- defl : numpy.ndarray Array of deflections initialized to zero with shape (2 * nx, nt + 1). Returns ------- defl : numpy.ndarray Array of calculated deflections with shape (2 * nx, nt + 1). """ # Index of excitation point/points if isinstance(self.excit.x_excit, list): self.ind_excit = [int(x / self.discr.dx) for x in self.excit.x_excit] else: self.ind_excit = int(self.excit.x_excit / self.discr.dx) # Factorization of matrix A (LU decomposition) factoriz = splu(self.discr.A) for t in range(1, self.discr.nt): # Calculate right hand side of equation b = self.calc_rightside_crank_nicolson(u1=defl[:, t], u0=defl[:, t - 1], ind_excit=self.ind_excit, t=t) # Calculate deflection for time step t u = factoriz.solve(b) defl[:, t + 1] = u[0:2 * self.discr.nx] return defl