Source code for calculation.aperture

from traits.api import HasTraits, Enum, Range, Float, Bool, TraitError, Union
from traitsui.api import View, Item, Group
import numpy as np
from numpy import pi

[docs] class Aperture(HasTraits): """ Models the aperture of a Helmholtz resonator, either as a tube or a slit. Attributes: form (str): Shape of the aperture ('tube' or 'slit'). length (float): Length of the aperture. radius (float): Radius for 'tube' form. width (float): Width for 'slit' form. height (float): Height for 'slit' form. amount (int): Number of apertures. inner_ending (str): Boundary condition inside ('open' or 'flange'). outer_ending (str): Boundary condition outside ('open' or 'flange'). additional_dampening (bool): Whether porous damping is used. xi (float): Damping coefficient (required if damping enabled). area (float): Computed cross-sectional area. inner_end_correction (float): End correction at inner boundary. outer_end_correction (float): End correction at outer boundary. """ form = Enum('tube', 'slit') length = Range(0.001, 0.5) radius = Range(0.005, 1.0, value=None, allow_none=True) width = Range(0.001, 0.5, value=None, allow_none=True) height = Range(0.001, 0.5, value=None, allow_none=True) amount = Range(1, 100, 1) inner_ending = Enum('open', 'flange') outer_ending = Enum('flange', 'open') additional_dampening = Bool(False) xi = Union(None, Float) area = Float inner_end_correction = Float outer_end_correction = Float def __init__(self, **kwargs): """Initialize and validate aperture configuration.""" super().__init__(**kwargs) self._validate_logical_dependencies() self._compute_area_and_corrections() def _validate_logical_dependencies(self): """Validates consistency of traits based on form and dampening.""" if self.form == 'tube' and self.radius is None: raise TraitError("Radius required if form='tube'.") elif self.form == 'slit' and (self.width is None or self.height is None): raise TraitError("form='slit' requires width and height.") if self.additional_dampening and self.xi is None: raise TraitError("xi required if additional_dampening=True.") def _compute_area_and_corrections(self): """Computes the aperture area and end corrections.""" if self.form == 'tube': self.area = pi * self.radius**2 * self.amount self.inner_end_correction = self.get_tube_correction(self.inner_ending) self.outer_end_correction = self.get_tube_correction(self.outer_ending) elif self.form == 'slit': self.area = self.width * self.height * self.amount self.radius = np.sqrt(self.area / pi) correction = self.get_slit_end_correction(self.width, self.height) self.inner_end_correction = correction self.outer_end_correction = correction self.radius = 0.5 * self.width
[docs] def get_tube_correction(self, ending): """Returns the empirical end correction for a tube end. Args: ending (str): 'open' or 'flange'. Returns: float: End correction length. """ if ending == 'open': return 0.6 * self.radius elif ending == 'flange': return 0.85 * self.radius else: raise ValueError("Invalid ending. Choose 'open' or 'flange'. ")
[docs] def get_slit_end_correction(self, x: float, y: float) -> float: """ Calculates end correction for slits using Mechel's formulation. Args: x (float): Width of slit. y (float): Height of slit. Returns: float: End correction length. """ if x < y: a, b = 0.5 * x, 0.5 * y else: a, b = 0.5 * y, 0.5 * x beta = a / b delta_l_a = 2 / (3 * pi) * (beta + (1 - (1 + beta**2)**(3/2)) / beta**2) delta_l_a += (2 / pi) * (1 / beta * np.log(beta + np.sqrt(1 + beta**2)) + np.log(1 / beta * (1 + np.sqrt(1 + beta**2)))) return delta_l_a * a
[docs] def to_dict(self): """Returns the aperture as a dictionary, including only relevant fields based on form.""" base = { 'form': self.form, 'length': self.length, 'amount': self.amount, 'inner_ending': self.inner_ending, 'outer_ending': self.outer_ending, 'additional_dampening': self.additional_dampening, 'xi': self.xi if self.additional_dampening else None, 'area': self.area, 'inner_end_correction': self.inner_end_correction, 'outer_end_correction': self.outer_end_correction} if self.form == 'tube': base.update({'radius': self.radius}) elif self.form == 'slit': base.update({'width': self.width, 'height': self.height}) return base
[docs] @classmethod def from_dict(cls, data): """Creates an Aperture instance from a dictionary.""" return cls( form=data['form'], length=data['length'], radius=data.get('radius'), inner_ending=data.get('inner_ending', 'open'), outer_ending=data.get('outer_ending', 'flange'), additional_dampening=data.get('additional_dampening', False), xi=data.get('xi'), amount=data.get('amount', 1), width=data.get('width'), height=data.get('height') )
# TraitsUI View traits_view = View( Group( Item('form', label="Form der Öffnung"), Item('length', label="Länge (m)"), Item('radius', label="Radius (m)", enabled_when='form == "tube"'), Item('width', label="Breite (m)", enabled_when='form == "slit"'), Item('height', label="Höhe (m)", enabled_when='form == "slit"'), Item('amount', label="Anzahl Öffnungen"), ), title="Aperture", buttons=['OK', 'Cancel'], resizable=True )
# Beispiel GUI starten if __name__ == "__main__": aperture = Aperture(form='tube', length=0.1, radius=0.02, amount=1) aperture.configure_traits()