Source code for haskoning_atr_tools.human_induced_vibrations.step_load

### ===================================================================================================================
###   Definition of the step load
### ===================================================================================================================
# This calculation tool is based on the Dutch SBR guideline **Trillingen van vloeren door lopen**
# Copyright ©2025 Haskoning Nederland B.V.

### ===================================================================================================================
###   1. Import modules
### ===================================================================================================================

# General imports
from warnings import warn
from pathlib import Path
from typing import Optional, List, Tuple, Union

# References for functions and classes in the haskoning_atr_tools package
from haskoning_atr_tools.config import Config

# Import third-party libraries
import matplotlib
matplotlib.use(Config.MPL_NONINTERACTIVE(notify=False))
import matplotlib.pyplot as plt


### ===================================================================================================================
###   2. StepLoad class
### ===================================================================================================================

[docs] class StepLoad: """ The StepLoad class represents the load generated by a single walking step."""
[docs] def __init__(self, step_frequency: float, body_mass: float): """ Input: - step_frequency (float): The frequency of the steps, in [Hz]. The frequency should be between 1.64 and 3Hz. According SBR art 4.1.1 when calculating or simulating floor vibrations due to walking, only walking frequencies between 1.64 Hz and 3.00 Hz are considered relevant for the dynamic response of the floor. - body_mass (float): The mass of the body, in [kg]. The body mass should be between 40 and 125kg. SBR art 4.1.2 only provides for values for the mass of the walking person in range from 30 kg to 125 kg, in steps of 5 kg. """ self.step_frequency = step_frequency self.body_mass = body_mass
@property def step_frequency(self): return self.__step_frequency @step_frequency.setter def step_frequency(self, new_step_frequency: float): if not isinstance(new_step_frequency, (float, int)): raise TypeError( f"ERROR: The step-frequency for the step load should be provided as float. Provided was " f"{new_step_frequency} of type {type(new_step_frequency)}. Please correct your input.") if not (1.64 <= new_step_frequency <= 3.00): raise ValueError( f"ERROR: The value of the step-frequency for the step load should be larger than 1.64 Hz and smaller " f"than 3.00 Hz. Provided was {new_step_frequency} Hz.") self.__step_frequency = new_step_frequency @property def body_mass(self): return self.__body_mass @body_mass.setter def body_mass(self, new_body_mass: float): if not isinstance(new_body_mass, (float, int)): raise TypeError( f"ERROR: The body mass for the step load should be provided as float. Provided was " f"{type(new_body_mass)}. Please correct your input.") if not (40 <= new_body_mass <= 125): raise ValueError( f"ERROR: The value of the body mass for the step load should be larger than 40 kg and smaller than 125 " f"kg. Provided was {new_body_mass} kg.") self.__body_mass = new_body_mass @property def method(self) -> str: """ Returns the method used for the calculation, currently only SBR is available, as string.""" return 'SBR' @property def step_load_duration(self) -> float: """ Returns the step load duration based on the step frequency, as float, in [s].""" return 2.6606 - 1.757 * self.step_frequency + 0.3844 * self.step_frequency ** 2 @property def k_coefficients(self) -> List[float]: """ Returns the calculated k-coefficients based on the step frequency, as list of floats, in [-].""" if self.step_frequency <= 1.75: return [ -8 * self.step_frequency + 38, 376 * self.step_frequency - 844, -2804 * self.step_frequency + 6025, 6308 * self.step_frequency - 16573, 1732 * self.step_frequency + 13619, -24648 * self.step_frequency + 16045, 31836 * self.step_frequency - 33614, -12948 * self.step_frequency + 15532] elif 1.75 < self.step_frequency < 2: return [ 24 * self.step_frequency - 18, -404 * self.step_frequency + 521, 4224 * self.step_frequency - 6274, -29144 * self.step_frequency + 45468, 109976 * self.step_frequency - 175808, -217424 * self.step_frequency + 353403, 212776 * self.step_frequency - 350259, -81572 * self.step_frequency + 135624] return [ 75 * self.step_frequency - 120.4, -1720 * self.step_frequency + 3153, 17055 * self.step_frequency - 31936, -94265 * self.step_frequency + 175710, 298940 * self.step_frequency - 553736, -529390 * self.step_frequency + 977335, 481665 * self.step_frequency - 888037, -174265 * self.step_frequency + 321008] @property def step_load_diagram(self) -> Tuple[List[float], List[float]]: """ Returns the step load diagram based on default step size. The tuple specifies list of the time-steps as float, in [s] and the loads as float, in [N].""" return self.calculate_step_load()
[docs] def calculate_step_load_at_time(self, defined_time: float = 0) -> float: """ Calculate the step load at a specific time according to SBR section 4.4. Input: - defined_time (float): The specific time step at which to calculate the load, in [s]. Default value is 0. Output: - Returns the value of the step load at a specified time step, in [N]. """ # Check defined_time input if defined_time > self.step_load_duration: defined_time = self.step_load_duration warn(f"WARNING: Time step selected is larger than step_load_duration. The time step is set to " f"step_load_duration = {defined_time}s.") elif defined_time < 0: defined_time = 0 warn(f"WARNING: Time step selected is lower than 0. The time step is set to {defined_time}s.") # Step load at specific time step equation = \ sum(k * defined_time ** i for i, k in enumerate(self.k_coefficients, start=1)) * self.body_mass if equation < 0: step_load_defined_time = 0 else: step_load_defined_time = equation return step_load_defined_time
[docs] def calculate_step_load(self, step_size: Optional[float] = None) -> Tuple[List[float], List[float]]: """ Calculate the step load of a single step along the specified time duration according to SBR 4.1. Input: - step_size (float): Optional input for the step size within the defined time domain of the load, in [s]. Default value None, defines a step size equal to 1/1000 of the step load duration. Output: - Returns the step load as list of all the loads along the specified time duration as float, in [N]. """ # Generate time values for step_load if step_size is None: step_size = self.step_load_duration * (1 / 1000) elif step_size <= 0: step_size = self.step_load_duration * (1 / 1000) warn(f"WARNING: The step size selected is smaller or equal to 0. Step_size is set to " f"{self.step_load_duration * (1 / 1000)}.") elif step_size > self.step_load_duration: step_size = self.step_load_duration * (1 / 1000) warn(f"WARNING: The step size selected is larger than step_interval. Step_size is set to " f"{self.step_load_duration * (1 / 1000)}.") time_values = [] counter = 0 while counter <= self.step_load_duration: time_values.append(counter) counter += step_size # Ensure the endpoint is included if time_values[-1] < self.step_load_duration: time_values.append(self.step_load_duration) # Calculate step load value for each step time load_values = [] for t in time_values: step_load_time_value = self.calculate_step_load_at_time(defined_time=t) load_values.append(step_load_time_value) # Return the values as tuple return time_values, load_values
[docs] def plot( self, show: bool = True, save_file: Optional[Union[str, Path]] = None, step_size: Optional[float] = None, normalised: bool = False): """ Generate a plot of the step load over time. Input: - show (bool): Switch to indicate if the pop-up screen of the graph should appear. Default value is True. - save_file (str or Path): The path of the image if the plot should be saved. If None the image will not be saved. Default is None. - step_size (float): Optional input for the step size within the defined domain, in [s]. Default value None, defines a step size equal to 1/1000 of the step domain. - normalised (bool): Select to create the plot for the normalised load over time. Default value is False. Output: - Returns the matplotlib figure when save_file is None. - Returns the path to the created image if save_file has been provided. """ # Calculate the values for the plot time_values, load_values = self.calculate_step_load(step_size=step_size) # Check if normalised plot is requested if normalised: load_values = [v / self.body_mass for v in load_values] # Initialise matplotlib for viewing if show: matplotlib.use(Config.MPL_GUI()) # Create the plot plt.close() plt.style.use(Config.PLOT_STYLE_FILE.as_posix()) plt.plot(time_values, load_values) plt.xlabel('Time [s]') y_min, y_max = plt.ylim() plt.ylim(y_min, y_max * 1.02) if normalised: plt.ylabel('Normalised load [-]') plt.title('Normalised load vs time') else: plt.ylabel('Load [kg]') plt.title('Load vs time') plt.grid(True) # Show the plot if requested if show: plt.show() # Set matplotlib backend back to non-interactive if changed to GUI backend matplotlib.use(Config.MPL_NONINTERACTIVE()) # Return the file if there is a file generated plt.close() if save_file: return save_file return plt
### =================================================================================================================== ### 3. End of script ### ===================================================================================================================