Source code for seapy.subsystems.subsystem
"""
ABC
---
.. autoclass:: seapy.subsystems.subsystem.Subsystem
"""
from ..base import Base, ComponentLink, LinkedList, Spectrum
import abc
import math
import cmath
import numpy as np
from weakref import WeakSet
import logging
from seapy.excitations import excitations_map # Circular dependency!
[docs]class Subsystem(Base):
"""Abstract Base Class for subsystems."""
SORT = 'Subsystem'
_DEPENDENCIES = ['component']
component = ComponentLink()
"""
Component this subsystem uses.
"""
linked_couplings_from = LinkedList()
"""
Set of couplings in which the subsystem is in the From field.
"""
linked_couplings_to = LinkedList()
"""
Set of couplings in which the subsystem is in the To field.
"""
linked_excitations = LinkedList()
"""
Set of excitations this subsystem experiences.
"""
#def __init__(self, name, component, **properties):
[docs] def __init__(self, name, system, **properties):
"""Constructor.
:param name: Identifier
:type name: string
:param component: Component
:type component: :class:`SeaPy.components.Component`
"""
super().__init__(name, system, **properties)
#super().__init__(name, component.system, **properties)
#self.component = component
#"""
#Component this subsystem uses.
#"""
def __del__(self):
"""Destructor. Destroy linked couplings. Remove references from components"""
for coupling in self.linked_couplings_from:
logging.info("Destructor %s: Deleting linked coupling %s", self.name, coupling)
self.system.removeObject(coupling)
for coupling in self.linked_couplings_to:
logging.info("Destructor %s: Deleting linked coupling %s", self.name, coupling)
self.system.removeObject(coupling)
for excitation in self.linked_excitations:
logging.info("Destructor %s: Deleting linked excitation %s", self.name, excitation)
self.system.removeObject(excitation)
try:
self.component.__dict__['linked_subsystems'].remove(self.name)
except ReferenceError:
pass
#self.system.subsystems.remove(self.name)
super().__del__() # Inherit destructor from base class
[docs] def disable(self, couplings=False):
"""
Disable this subsystem. Optionally disable dependent couplings as well.
:param couplings: Disable couplings
:type couplings: bool
"""
self._enabled = False
if couplings:
for coupling in self.linked_couplings:
coupling.disable()
[docs] def enable(self, couplings=False):
"""
Enable this subsystem. Optionally enable dependent couplings as well.
:param couplings: Enable couplings
:type couplings: bool
"""
self._enabled = True
if couplings:
for coupling in self.linked_couplings:
coupling.enable()
[docs] def addExcitation(self, name, model, **properties):
"""Add excitation to subsystem.
"""
obj = excitations_map[model](name, self.system.getObject(self.name), **properties)
self.system._objects.append(obj)
obj = self.system.getObject(obj.name)
#self.linked_excitations.add(obj)
return obj
modal_energy = Spectrum(dtype='float64')
"""Modal energy.
"""
#def _set_modal_overlap_factor(self, x):
#self._modal_overlap_factor = x
#def _get_modal_overlap_factor(self):
#if not self._modal_overlap_factor:
#return self.component.material.loss_factor
#else:
#self._modal_overlap_factor
#_modal_overlap_factor = None
#modal_overlap_factor = property(fget=_get_modal_overlap_factor, fset=_set_modal_overlap_factor)
#"""
#Modal overlap factor. Initial value is based on damping loss factor of subsystem.
#After solving the system a first time, this value is updated to its results.
#This value is iteratively updated.
#"""
@property
@abc.abstractmethod
[docs] def soundspeed_phase(self):
"""
Phase velocity in a subsystem.
"""
return
@property
@abc.abstractmethod
[docs] def soundspeed_group(self):
"""
Group velocity in a subsystem.
"""
return
@property
@abc.abstractmethod
[docs] def average_frequency_spacing(self):
""""
Average frequency spacing.
"""
return
@property
[docs] def modal_density(self):
"""
Modal density.
.. math:: n(\\omega) = \\frac{1}{2 \\pi \\overline{\\delta f}}
See Lyon, eq. 8.1.6
"""
try:
return 1.0 / (2.0 * np.pi * self.average_frequency_spacing)
except FloatingPointError:
return np.zeros(self.frequency.amount)
@property
[docs] def modal_overlap(self):
"""
Modal overlap.
.. math:: M = \\eta_d \\omega n
with:
* damping loss factor :math:`\\eta`
* angular frequency :math:`\\omega`
* modal density :math:`n`
See Craik, equation 2.23, page 41.
"""
return self.dlf * self.frequency.angular * self.modal_density
@property
#@abc.abstractmethod
[docs] def wavenumber(self):
"""
Wave number.
"""
raise NotImplementedError
@property
[docs] def impedance(self):
"""
Impedance :math:`Z`
"""
raise NotImplementedError
@property
[docs] def resistance(self):
"""
Resistance :math:`R`, the real part of the impedance :math:`Z`.
.. math:: R = \\Re{Z}
"""
return np.real(self.impedance)
@property
[docs] def conductance(self):
"""
Conductance :math:`G`.
.. math:: G = \\frac{1}{R}
"""
return 1.0 / self.resistance
@property
[docs] def mobility(self):
"""
Mobility `Y`
.. math:: Y = \\frac{1}{Z}
"""
try:
return 1.0 / self.impedance
except FloatingPointError:
return np.zeros(self.frequency.amount)
@property
[docs] def damping_term(self):
"""
The damping term is the ratio of the modal half-power bandwidth to the average modal frequency spacing.
.. math:: \\beta_{ii} = \\frac{f \\eta_{loss} }{\\overline{\\delta f}}
See Lyon, above equation 12.1.4
"""
try:
return self.frequency.center * self.component.material.loss_factor / self.average_frequency_spacing
except FloatingPointError:
return np.zeros(self.frequency.amount)
@property
[docs] def modal_overlap_factor(self):
"""
Modal overlap factor.
.. math:: M = \\frac{ \\pi \\beta_{ii} }{2}
See Lyon, above equation 12.1.4
"""
return np.pi * self.damping_term / 2.0
@property
@property
[docs] def energy(self):
"""
Total energy :math:`E` in subsystem.
.. math:: E = M(\\omega) n(\\omega)
"""
return self.modal_energy * self.modal_density
@property
def dlf(self):
"""Damping loss factor of subsystem.
By default this is the loss factor of the material of the component.
"""
if self._dlf:
return self._dlf
else:
return self.component.material.loss_factor
_dlf = None
@dlf.setter
def dlf(self, x):
self._dlf = x
@dlf.deleter
[docs] def dlf(self):
self._dlf = None
@property
[docs] def tlf(self):
"""Total loss factor.
.. math:: \\eta_i = \\eta_{id} + \\sum_{j=1, j \\neq i}^{n} \\eta_{ij}
See Craik, equation 3.18, page 60.
"""
return np.sum((coupling.clf for coupling in self.linked_couplings_from if coupling.included), axis=0) + self.dlf * self.included