bpiyush's picture
Upload folder using huggingface_hub
eafbf97 verified
raw
history blame
9.59 kB
import numpy as np
import torch
# Universal constants
C = 340. * 100. # Speed of sound in air (cm/s)
def compute_length_of_air_column_cylindrical(
timestamps, duration, height, b, **kwargs,
):
"""
Randomly chooses a l(t) curve satisfying the two point equations.
"""
L = height * ( (1 - np.exp(b * (duration - timestamps))) / (1 - np.exp(b * duration)) )
return L
def compute_axial_frequency_cylindrical(
lengths, radius, beta=0.62, mode=1, **kwargs,
):
"""
Computes axial resonance frequency for cylindrical container at given timestamps.
"""
if mode == 1:
harmonic_weight = 1.
elif mode == 2:
harmonic_weight = 3.
elif mode == 3:
harmonic_weight = 5.
else:
raise ValueError
# Compute fundamental frequency curve
F0 = harmonic_weight * (0.25 * C) * (1. / (lengths + (beta * radius)))
return F0
def compute_axial_frequency_bottleneck(
lengths, radius, height, Rn, Hn, beta_bottle=(0.6 + 8/np.pi), **kwargs,
):
# Here, R and H are base radius and height of the bottleneck
eps = 1e-6
kappa = (0.5 * C / np.pi) * (Rn/radius) * np.sqrt(1 / (Hn + beta_bottle * Rn))
frequencies = kappa * np.sqrt(1 / (lengths + eps))
return frequencies
def compute_f0_cylindrical(Y, rho_g, a, R, H, mode=1, **kwargs,):
if mode == 1:
m = 1.875
n = 2
elif mode == 2:
m = 4.694
n = 3
elif mode == 3:
m = 7.855
n = 4
else:
raise ValueError
term = ( ((n**2 - 1)**2) + ((m * R/H)**4) ) / (1 + (1./n**2))
f0 = (1. / (12 * np.pi)) * np.sqrt(3 * Y / rho_g) * (a / (R**2)) * np.sqrt(term)
return f0
def compute_xi_cylindrical(rho_l, rho_g, R, a, **kwargs,):
"""
Different papers use different multipliers.
For us, using 12. * (4./9.) works best empirically.
"""
xi = 12. * (4. / 9.) * (rho_l/rho_g) * (R/a)
return xi
def compute_radial_frequency_cylindrical(
heights, R, H, Y, rho_g, a, rho_l, power=3, mode=1, **kwargs,
):
"""
Computes radial resonance frequency for cylindrical.
Args:
heights (np.ndarray): height of liquid at pre-defined time stamps
"""
# Only f0 changes for higher modes
f0 = compute_f0_cylindrical(Y, rho_g, a, R, H, mode=mode)
xi = compute_xi_cylindrical(rho_l, rho_g, R, a)
frequencies = f0 / np.sqrt(1 + xi * ((heights/H) ** power) )
return frequencies
def compute_slant_lengths_semiconical(
timestamps, duration, r_top, r_bot, height, **kwargs,
):
# Top radius / base radius
rf = r_bot / r_top
# Time fraction
tf = timestamps/duration
# Height fractions: h(t) / H
height_fractions = (1. / (rf - 1)) * (np.cbrt(((rf**3 - 1) * (tf)) + 1) - 1)
# Slant air column lengths
heights = height_fractions * height
slant_lengths = np.sqrt(1 - ((r_top - r_bot) / height)**2) * (height - heights)
return slant_lengths
def compute_axial_frequency_semiconical(slant_lengths, r_top, r_bot, beta=1.28, **kwargs):
"""
Computes axial resonance frequency for cylinder.
Args:
slant_lengths (np.ndarray): slant length of air column
r_top (float): top radius
r_bot (float): base radius
beta (float): end correction coefficient
"""
frequencies_axial = (C / 2) * (1 / (slant_lengths + (beta * (r_bot + r_top))))
return frequencies_axial
def get_frequencies(
t,
params,
container_shape="cylindrical",
harmonic=None,
vibration_type="axial",
semiconical_as_cylinder=False,
):
"""
Computes requires frequency f(t) for given t.
"""
if container_shape == "semiconical":
# Makes an assumption that semiconical shape is similar to cylindrical
if semiconical_as_cylinder:
container_shape = "cylindrical"
if (container_shape == "cylindrical") or (container_shape == "bottleneck_as_cylindrical"):
# Compute length of air column first
lengths = compute_length_of_air_column_cylindrical(t, **params)
if vibration_type == "axial":
frequencies = compute_axial_frequency_cylindrical(lengths, **params)
if harmonic is not None:
assert harmonic > 0 and isinstance(harmonic, int)
frequencies = frequencies * harmonic
elif vibration_type == "radial":
if harmonic is None:
mode = 1
else:
assert isinstance(harmonic, int)
assert harmonic in [1, 2]
mode = harmonic + 1
frequencies = compute_radial_frequency_cylindrical(
lengths, mode=mode, **params,
)
else:
raise NotImplementedError
elif container_shape == "semiconical":
# Compute length of air column first
slant_lengths = compute_slant_lengths_semiconical(t, **params)
if vibration_type == "axial":
frequencies = compute_axial_frequency_semiconical(
slant_lengths, **params,
)
if harmonic is not None:
assert harmonic > 0 and isinstance(harmonic, int)
frequencies = frequencies * harmonic
else:
raise NotImplementedError
elif container_shape == "bottleneck":
# Compute length of air column first assuming
# base of the bottle is a cylindrical
lengths = compute_length_of_air_column_cylindrical(t, **params)
if vibration_type == "axial":
frequencies = compute_axial_frequency_bottleneck(
lengths, **params,
)
if harmonic is not None:
assert harmonic > 0 and isinstance(harmonic, int)
frequencies = frequencies * harmonic
else:
raise NotImplementedError
else:
raise ValueError
return frequencies
def get_params(row, semiconical_as_cylinder=False):
m = row["measurements"]
duration = row["end_time"] - row["start_time"]
params = dict(duration=duration)
if row["shape"] == "cylindrical":
radius = 0.25 * (m["diameter_top"] + m["diameter_bottom"])
height = m["net_height"]
params.update(
height=height,
radius=radius,
beta=row.get("beta", 0.62),
# Constant flow
b=0.01,
)
elif row["shape"] == "semiconical":
if semiconical_as_cylinder:
# Assume semiconical shape as cylindrical
radius = 0.25 * (m["diameter_top"] + m["diameter_bottom"])
height = m["net_height"]
params.update(
height=height,
radius=radius,
beta=0.62,
# Constant flow
b=0.01,
)
else:
r_top = 0.5 * m["diameter_top"]
r_bot = 0.5 * m["diameter_bottom"]
height = m["net_height"]
beta = 1.28
params.update(
r_top=r_top,
r_bot=r_bot,
height=height,
beta=beta,
)
elif row["shape"] == "bottleneck":
radius = 0.5 * m["diameter_bottom"]
Rn = 0.5 * m["diameter_top"]
Hn = m["neck_height"]
height = m["net_height"] - Hn
params.update(
height=height,
radius=radius,
Rn=Rn,
Hn=Hn,
# Constant flow
b=0.01,
)
elif row["shape"] == "bottleneck_as_cylindrical":
# Approximates bottleneck as cylindrical
radius = 0.5 * m["diameter_bottom"]
height = m["net_height"] + m["neck_height"]
params.update(
height=height,
radius=radius,
beta=row.get("beta", 0.62),
# Constant flow
b=0.01,
)
else:
raise ValueError
return params
def frequency_to_wavelength(f):
"""
Converts frequency to wavelength.
Args:
f (float): frequency
"""
return C / f
def wavelength_to_frequency(l):
"""
Converts wavelength to frequency.
Args:
l (float): wavelength
"""
return C / l
def get_cylinder_radius(m):
return 0.25 * (m['diameter_top'] + m['diameter_bottom'])
def get_cylinder_height(m):
return m['net_height']
def get_flow_rate(m, duration):
r = get_cylinder_radius(m)
h = get_cylinder_height(m)
volume = np.pi * (r**2) * h
q = volume / duration
return q
def get_length_of_air_column(m, duration, timestamps):
h = get_cylinder_height(m)
l = (-h/duration) * timestamps + h
l = torch.from_numpy(l)
return l
def estimate_cylinder_radius(wavelengths, timestamps=None, beta=0.62):
radius_pred = ((1. / beta) * (wavelengths[-1] / 4.)).item()
return radius_pred
def estimate_cylinder_height(wavelengths, timestamps=None, beta=0.62):
height_pred = wavelengths[0] / 4. - wavelengths[-1] / 4.
return height_pred.item()
def estimate_flow_rate(wavelengths, timestamps=None, output_fps=49.):
radius = estimate_cylinder_radius(wavelengths)
l_pred = (wavelengths - wavelengths[-1]) / 4.
slope = np.gradient(l_pred).mean() * output_fps
Q_pred = -np.pi * (radius**2) * slope
return Q_pred
def estimate_length_of_air_column(wavelengths, timestamps=None):
l_pred = (wavelengths - wavelengths[-1]) / 4.
return l_pred