Source code for kwave.utils.data

from datetime import datetime
from math import floor

import numpy as np
from beartype import beartype as typechecker
from beartype.typing import Optional, Tuple, Union

from kwave.data import Vector
from kwave.utils.typing import NUMERIC_WITH_COMPLEX


[docs] @typechecker def get_smallest_possible_type( max_array_val: Union[NUMERIC_WITH_COMPLEX, Vector], target_type_group: str, default: Optional[str] = None ) -> Union[str, None]: """ Returns the smallest possible type for the given array. Args: max_array_val: The maximum value in the array. target_type_group: The type group to search for the smallest possible type. default: The default type to return if no type is found. Returns: The smallest possible type for the given array. """ types = {"uint", "int"} assert target_type_group in types for bit_count in [8, 16, 32]: type_ = f"{target_type_group}{bit_count}" if max_array_val < intmax(type_): return type_ type_ = default return type_
[docs] @typechecker def intmax(dtype: str) -> int: """ Returns the maximum value for the given integer type. Args: dtype: The integer type. Returns The maximum value for the given integer type. """ return np.iinfo(getattr(np, dtype)).max
[docs] @typechecker def scale_time(seconds: Union[int, float]) -> str: """ Converts an integer number of seconds into hours, minutes, and seconds, and returns a string with this information. Args: seconds: number of seconds Returns: String of scaled time. """ # switch to calculating years, weeks, and days if larger than 100 hours if seconds > (60 * 60 * 100): years = floor(seconds / (60 * 60 * 24 * 365)) seconds = seconds - years * 60 * 60 * 24 * 365 weeks = floor(seconds / (60 * 60 * 24 * 7)) seconds = seconds - weeks * 60 * 60 * 24 * 7 days = floor(seconds / (60 * 60 * 24)) seconds = seconds - days * 60 * 60 * 24 else: years = 0 weeks = 0 days = 0 # calculate hours, minutes, and seconds hours = floor(seconds / (60 * 60)) seconds = seconds - hours * 60 * 60 minutes = floor(seconds / 60) seconds = seconds - minutes * 60 # write out as a string, to keep the output manageable, only the largest # three units are written if years > 0: time = f"{years} years, {weeks} weeks, and {days} days" elif weeks > 0: time = f"{weeks} weeks, {days} days, and {hours} hours" elif days > 0: time = f"{days} days, {hours} hours, and {minutes} min" elif hours > 0: seconds = np.round(seconds, 4) if np.abs(seconds - int(seconds)) < 1e-4: seconds = int(seconds) time = f"{hours}hours {minutes}min {seconds}s" elif minutes > 0: seconds = np.round(seconds, 4) if np.abs(seconds - int(seconds)) < 1e-4: seconds = int(seconds) time = f"{minutes}min {seconds}s" else: precision = 10 # manually tuned number seconds = round(seconds, precision) time = f"{seconds}s" return time
[docs] @typechecker def scale_SI(x: Union[float, np.ndarray]) -> Tuple[str, Union[int, float], str, str]: """ Scale a number to the nearest SI unit prefix. Args: x: The number to scale. Returns: A tuple containing a string of the scaled number, a numeric scaling factor, the prefix, and the unit. """ # force the input to be a scalar x = np.max(x) # check for a negative input if x < 0: x = -x negative = True else: negative = False if x == 0: # if x is zero, don't scale x_sc = x prefix = "" prefix_fullname = "" scale = 1 elif x < 1: # update index and input x_sc = x * 1e3 sym_index = 1 # find scaling parameter while x_sc < 1 and sym_index < 8: x_sc = x_sc * 1e3 sym_index = sym_index + 1 # define SI unit scalings units = { 1: ("m", "milli", 1e3), 2: ("u", "micro", 1e6), 3: ("n", "nano", 1e9), 4: ("p", "pico", 1e12), 5: ("f", "femto", 1e15), 6: ("a", "atto", 1e18), 7: ("z", "zepto", 1e21), 8: ("y", "yocto", 1e24), } prefix, prefix_fullname, scale = units[sym_index] elif x >= 1000: # update index and input x_sc = x * 1e-3 sym_index = 1 # find scaling parameter while x_sc >= 1000 and sym_index < 8: x_sc = x_sc * 1e-3 sym_index = sym_index + 1 # define SI unit scalings units = { 1: ("k", "kilo", 1e-3), 2: ("M", "mega", 1e-6), 3: ("G", "giga", 1e-9), 4: ("T", "tera", 1e-12), 5: ("P", "peta", 1e-15), 6: ("E", "exa", 1e-18), 7: ("Z", "zetta", 1e-21), 8: ("Y", "yotta", 1e-24), } prefix, prefix_fullname, scale = units[sym_index] else: # if x is between 1 and 1000, don't scale x_sc = x prefix = "" prefix_fullname = "" scale = 1 # form scaling into a string round_decimals = 6 # TODO this needs to be tuned x_sc = x_sc.round(round_decimals) if (x_sc - int(x_sc)) < (0.1**round_decimals): # avoid values like X.0, instead have only X x_sc = int(x_sc) x_sc = f"-{x_sc}{prefix}" if negative else f"{x_sc}{prefix}" return x_sc, scale, prefix, prefix_fullname
[docs] def get_date_string() -> str: return datetime.now().strftime("%d-%b-%Y-%H-%M-%S")