import pdb
import openmdao
import numpy as np
import openmdao.api as om
from pyNA.src.data import Data
from pyNA.src.settings import Settings
from pyNA.src.noise_src_py.split_subbands import split_subbands
from pyNA.src.noise_src_py.ground_reflections import ground_reflections
from pyNA.src.noise_src_py.lateral_attenuation import lateral_attenuation
[docs]class Propagation(om.ExplicitComponent):
"""
Computes propagation of mean-square acoustic pressure (msap):
* Distance-law: R2
* Characteristic impedance law
* Atmospheric absorption
Computes ground reflections and absorption of propagated mean-square acoustic pressure.
The *Propagation* component requires the following inputs:
* ``inputs['x']``: aircraft x-position [m]
* ``inputs['z']``: aircraft z-position [m]
* ``inputs['r']``: distance source to observer [m]
* ``inputs['c_bar']``: average ambient speed of sound between observer and source [m/s]
* ``inputs['rho_0']``: ambient density [kg/m3]
* ``inputs['I_0']``: characteristic impedance [kg/(m2 s)]
* ``inputs['beta']``: elevation angle [deg]
* ``inputs['msap_source']``: mean-square acoustic pressure of the source (re. rho_0,^2c_0^2) [-]
The *Propagation* component computes the following outputs:
* ``outputs['msap_prop']``: mean-square acoustic pressure, propagated to the observer (re. rho_0^2c_0^2) [-]
The *Propagation* component has the following options:
* ``settings``: pyna settings
* ``n_t``: number of time steps in the noise time series
* ``data``: pyna noise data
"""
[docs] def initialize(self):
# Declare data option
self.options.declare('settings', types=Settings)
self.options.declare('n_t', types=int, desc='Number of time steps in trajectory')
self.options.declare('data', types=Data)
[docs] def setup(self):
# Load options
settings = self.options['settings']
n_t = self.options['n_t']
# Number of observers
n_obs = np.shape(settings.x_observer_array)[0]
# Add inputs
self.add_input('x', val=np.ones(n_t), units='m', desc='aircraft x-position [m]')
self.add_input('z', val=np.ones(n_t), units='m', desc='aircraft z-position [m]')
self.add_input('r', val=np.ones((n_obs, n_t)), units='m', desc='distance source to observer [m]')
self.add_input('c_bar', val=np.ones((n_obs, n_t)), units='m/s', desc='average ambient speed of sound between observer and source [m/s]')
self.add_input('rho_0', val=np.ones(n_t), units='kg/m**3', desc='ambient density [kg/m3]')
self.add_input('I_0', val=np.ones(n_t), units='kg/m**2/s', desc='ambient characteristic impedance [kg/m2/s]')
self.add_input('beta', val=np.ones((n_obs, n_t)), units='deg', desc='elevation angle [deg]')
self.add_input('msap_source', val=np.ones((n_obs, n_t, settings.N_f)), desc='mean-square acoustic pressure of the source (re. rho_0,^2c_0^2) [-]')
self.add_output('msap_prop', val=np.ones((n_obs, n_t, settings.N_f)), desc='mean-square acoustic pressure, propagated to the observer (re. rho_0^2c_0^2) [-]')
[docs] def compute(self, inputs: openmdao.vectors.default_vector.DefaultVector, outputs: openmdao.vectors.default_vector.DefaultVector):
# Load options
settings = self.options['settings']
data = self.options['data']
n_t = self.options['n_t']
# Number of observers
n_obs = np.shape(settings.x_observer_array)[0]
# Extract inputs
r = inputs['r']
x = inputs['x']
z = inputs['z']
c_bar = inputs['c_bar']
rho_0 = inputs['rho_0']
I_0 = inputs['I_0']
beta = inputs['beta']
msap_source = inputs['msap_source']
I_0_obs = 409.74
for k in np.arange(n_obs):
for i in np.arange(n_t):
# Apply spherical spreading and characteristic impedance effects to the MSAP
# Source: Zorumski report 1982 part 1. Chapter 5.1 Equation 1
if settings.direct_propagation:
msap_r = msap_source[k, i, :] * (settings.r_0 ** 2 / r[k, i] ** 2) * (I_0_obs / I_0[i])
else:
msap_r = msap_source[k, i, :]
# Generate sub-banding
msap_prop_i = np.zeros(settings.N_f)
if settings.absorption or settings.groundeffects:
msap_sb = split_subbands(settings, msap_r)
# Initialize solution vectors
if settings.absorption:
# ---------- Apply atmospheric absorption on sub-bands ----------
# Compute average absorption factor between observer and source
alpha_f = data.abs_f(data.f_sb, z[i])
# Compute absorption (convert dB to Np: 1dB is 0.115Np)
# Source: Zorumski report 1982 part 1. Chapter 5.1 Equation 14
msap_sb = msap_sb * np.exp(-2 * 0.115 * alpha_f * (r[k, i] - settings.r_0))
# ---------- Apply ground effects on sub-bands ----------
if settings.groundeffects:
# Empirical lateral attenuation for microphone on sideline
if settings.lateral_attenuation and settings.x_observer_array[k, 1] != 0:
# Lateral attenuation factor
Lambda = lateral_attenuation(settings, beta[k, i], settings.x_observer_array[k, :])
# Compute elevation angle from center-line observer; set observer z-position to 0
r_cl = np.sqrt((x-settings.x_observer_array[k,1])**2 + 1**2 + z**2)
beta_cl = np.arcsin(z/r_cl) * 180. / np.pi
# Ground reflection factor for center-line
G_cl = ground_reflections(settings, data, r_cl, beta_cl, settings.x_observer_array[k, :], c_bar[k, i], rho_0[i])
# Apply ground effects
msap_sb = msap_sb * (G_cl * Lambda)
# No empirical lateral attenuation or microphone underneath flight path
else:
# Ground reflection factor
G = ground_reflections(settings, data, r[k, i], beta[k, i], settings.x_observer_array[k, :], c_bar[k, i], rho_0[i])
# Apply ground effects
msap_sb = msap_sb * G
# Compute absorbed msap by adding up the msap at all the sub-band frequencies
# Source: Zorumski report 1982 part 1. Chapter 5.1 Equation 22
for j in np.arange(settings.N_f):
msap_prop_i[j] = np.sum(msap_sb[j*settings.N_b:(j+1)*settings.N_b])
else:
msap_prop_i = msap_r
outputs['msap_prop'][k, i, :] = msap_prop_i.clip(min=1e-99)