Source code for choice.choice_interf

"""
Module choice_interf

Choice interface: used to set the required parameters for the noise calculation and call the physics-based methods from
the module choice_physics.
"""

import sys
import math
import numpy as np
import choice.choice_physics as choice_physics
import choice.choice_aux as choice_aux
import choice.choice_data as choice_data
from scipy.interpolate import interp1d

maxNoPts = 3  # Maximum number of operating points
version_num = '1.0'


[docs]def get_version_num(fname): """ Writes CHOICE version number in output file. """ with open(fname, 'w') as f: f.write('! Choice version: ' + version_num)
[docs]class WeightChoice: """ Instantiate the engine size and architecture data. :param dict weightFile: Engine sizing data. """ def __init__(self, weightFile): """ Constructs all the necessary attributes for the noise calculation, from the provided weightAircraft file.""" self.MtipD_fan = weightFile.get('MtipFan') if self.MtipD_fan is not None and (self.MtipD_fan > 3.0 or self.MtipD_fan < 0.7): print('unexpected value on MtipD_fan in WeightChoice in choice_interf') self.xnlD_fan = weightFile.get('xnl') if self.xnlD_fan is not None and self.xnlD_fan > 600: print('xnl from weightFile very large. Unit should always be rps!', 'setFan', 'SetWeightChoice') self.rss_fan = weightFile.get('FanRss') if self.rss_fan is not None and (self.rss_fan > 300.0 or self.rss_fan < 5.0): print('unexpected value on rss_fan in WeightChoice in choice_interf') self.N_rotors_fan = int(weightFile.get('FanR1BNb')) self.N_stators_fan = int(weightFile.get('FanVsOgvNb')) self.A2_fan = weightFile.get('FanA2') if self.A2_fan is not None and (self.A2_fan > 50.0 or self.A2_fan < 0.1): print('unexpected value on A2_fan in WeightChoice in choice_interf') self.D1_fan = weightFile.get('FanR1BDiaOuter') if self.D1_fan is not None and (self.D1_fan < 0.1 or self.D1_fan > 10.0): print('unexpected value on D1_fan in WeightChoice in choice_interf') self.n_stages_LPC = weightFile.get('stages_LPC') self.gbx_ratio = weightFile.get('GBX_ratio') self.MtipD_lpc = weightFile.get('MtipLpc') self.xnlD_lpc = self.xnlD_fan * self.gbx_ratio self.rss_lpc = weightFile.get('RSS_compr') self.N_rotors_lpc = int(weightFile.get('N_compr')) self.N_stators_lpc = int(weightFile.get('S_compr')) self.D1_lpc = 2 * weightFile.get('r_compr') self.Dh1_lpc = 2 * weightFile.get('rh_compr') self.gbx_ratio = weightFile.get('GBX_ratio') self.N_rotors_lpt = weightFile.get('LptStgLastBNb') self.SRS_lpt = weightFile.get('SRS') self.n_stages_lpt = weightFile.get('stages_LPT') self.De_lpt = weightFile.get('LptStgLastDiaOuter') self.Ae_lpt = weightFile.get('LptStgLastAExit') # Build-in combustor model data, derived from the noise prediction method development literature self.type_comb = 'SAC' self.Nfmax_comb = 40 # total number of DAC fuel nozzles self.Aec_comb = 0.13926166 / (choice_data.ft2m ** 2) # combustor exit area (ft^2) self.De_comb = 10.543032 / choice_data.ft2m # exhaust nozzle exit plane effective diameter (ft) self.Dh_comb = 4.218432 / choice_data.ft2m # exhaust nozzle exit plane hydraulic diameter (ft) self.Lc_comb = 0.13968984 / choice_data.ft2m # combustor nominal length (ft) self.h_comb = 0.06348984 / choice_data.ft2m # annulus height at combustor exit (ft) if weightFile.get('CombType') is not None: self.type_comb = weightFile.get('CombType') if weightFile.get('maxNo_nozzles_dac') is not None: self.Nfmax_comb = weightFile.get('maxNo_nozzles_dac') if weightFile.get('A_comb_exit') is not None: self.Aec_comb = weightFile.get('A_comb_exit') / (choice_data.ft2m ** 2) if weightFile.get('Deff_comb') is not None: self.De_comb = weightFile.get('Deff_comb') / choice_data.ft2m if weightFile.get('Dhyd_comb') is not None: self.Dh_comb = weightFile.get('Dhyd_comb') / choice_data.ft2m if weightFile.get('Lc_comb') is not None: self.Lc_comb = weightFile.get('Lc_comb') / choice_data.ft2m if weightFile.get('h_annulus_comb') is not None: self.h_comb = weightFile.get('h_annulus_comb') / choice_data.ft2m self.A_core_caj = weightFile.get('A_core') self.A_bypass_caj = weightFile.get('A_bypass') self.MtipD_ff = weightFile.get('Mtipff') if self.MtipD_ff is not None and (self.MtipD_ff > 3.0 or self.MtipD_ff < 0.7): print('unexpected value on MtipD_ff in WeightChoice in choice_interf') self.xnlD_ff = weightFile.get('xnlff') if self.xnlD_ff is not None and self.xnlD_ff > 600: print('xnlff from weightFile very large. Unit should always be rps!', 'setff', 'SetWeightChoice') self.rss_ff = weightFile.get('ffRss') if self.rss_ff is not None and (self.rss_ff > 300.0 or self.rss_ff) < 5.0: print('unexpected value on rss_ff in WeightChoice in choice_interf') self.N_rotors_ff = weightFile.get('ffR1BNb') self.N_stators_ff = weightFile.get('ffVsOgvNb') self.A2_ff = weightFile.get('ffA2') if self.A2_ff is not None and (self.A2_ff > 50.0 or self.A2_ff < 0.1): print('unexpected value on A2_ff in WeightChoice in choice_interf') self.D1_ff = weightFile.get('ffR1BDiaOuter') if self.D1_ff is not None and (self.D1_ff < 0.1 or self.D1_ff > 10.0): print('unexpected value on D1_ff in WeightChoice in choice_interf') self.A_core_caj_ffn = weightFile.get('A_core_ffn') self.A_bypass_caj_ffn = weightFile.get('A_bypass_ffn')
[docs]class NoiseChoice: """ Instantiate the required input arguments for the noise prediction cases to be run. :param dict noiseFile: Definition of the noise cases to be performed. """ def __init__(self, noiseFile): """ Constructs all the necessary attributes for the noise calculation, from the provided inputNoise file. """ self.opPnt = noiseFile.get('point').split() self.nops = len(self.opPnt) if self.nops > maxNoPts: sys.exit('number of noise points greater than allowed') self.trajectory_performance = False if 'true' in noiseFile.get('trajectory_performance'): self.trajectory_performance = True self.use_trajectory_preparser = False if 'true' in noiseFile.get('use_trajectory_preparser'): self.use_trajectory_preparser = True self.gen_noise_source_matr = False if 'true' in noiseFile.get('generate_noise_sources'): self.gen_noise_source_matr = True self.plot_source_spectra = False if 'true' in noiseFile.get('plot_source_spectra'): self.plot_source_spectra = True self.directivity_plot = float(noiseFile.get('directivity_angle_to_plot')) else: self.directivity_plot = None self.use_ground_reflection = True if 'false' in noiseFile.get('use_ground_reflection'): self.use_ground_reflection = False self.use_spherical_spreading = True if 'false' in noiseFile.get('use_spherical_spreading'): self.use_spherical_spreading = False self.use_atmospheric_attenuation = True if 'false' in noiseFile.get('use_atmospheric_attenuation'): self.use_atmospheric_attenuation = False self.use_suppression_factor = False self.S_fan_inlet = 0 self.S_fan_dis = 0 self.S_lpt = 0 if 'true' in noiseFile.get('use_suppression_factor'): self.use_suppression_factor = True self.S_fan_inlet = noiseFile.get('fan_inlet_suppression') self.S_fan_dis = noiseFile.get('fan_dis_suppression') self.S_lpt = noiseFile.get('lpt_suppression') # type_nozzles only affects the total EPNL calculation. If it is set to mix, the fan discharge noise source # will be removed. The default is separate nozzles. self.type_nozzles = "separate" self.type_nozzles = noiseFile.get('type_nozzles') # "separate" or "mix", other type will not be recognized self.fan_distortion = False if noiseFile.get('fan_distortion') == 1: self.fan_distortion = True self.ff_distortion = False if noiseFile.get('ff_distortion') == 1: self.ff_distortion = True self.fuselage_fan = False if 'true' in noiseFile.get('fuselage_fan'): self.fuselage_fan = True self.xmic = np.zeros(3) self.ymic = np.zeros(3) self.zmic = np.zeros(3) self.dt_mic = noiseFile.get('dt_mic') self.elevation = noiseFile.get('ground_elevation') self.Nf_comb_ign = np.zeros(3) self.Nf_comb_pattern = np.zeros(3) if self.nops == 1: self.xmic[0] = noiseFile.get('xmic') self.ymic[0] = noiseFile.get('ymic') self.zmic[0] = noiseFile.get('zmic') # Combustor ignited nozzles self.Nf_comb_ign[0] = int(noiseFile.get('comb_ign_nozzles')) self.Nf_comb_pattern = float(noiseFile.get('dac_nozzle_pattern')) elif self.nops > 1: if self.nops > maxNoPts: sys.stop('number of noise points greater than allowed') self.xmic = np.array([float(xm) for xm in noiseFile.get('xmic').split()]) if len(self.xmic) != self.nops: sys.exit('xmic data not consistent with number of points') self.ymic = np.array([float(ym) for ym in noiseFile.get('ymic').split()]) if len(self.ymic) != self.nops: sys.exit('ymic data not consistent with number of points') self.zmic = np.array([float(zm) for zm in noiseFile.get('zmic').split()]) if len(self.zmic) != self.nops: sys.exit('zmic data not consistent with number of points') # Combustor ignited nozzles self.Nf_comb_ign = np.array([int(nf) for nf in noiseFile.get('comb_ign_nozzles').split()]) self.Nf_comb_pattern = np.array([float(nf) for nf in noiseFile.get('dac_nozzle_pattern').split()]) self.Sw_airfrm = noiseFile.get('wing_area') # wing area m2 self.span_airfrm = noiseFile.get('wing_span') # wing span m self.ND = int(noiseFile.get('jet_aircraft_coef')) # coefficient for te noise (1 -> +6 dB) self.flap_type = noiseFile.get('flap_type') self.NoFlap = int(noiseFile.get('noFlaps')) if self.NoFlap == 1: self.S_flap_airfrm = noiseFile.get('flap_area') # flap area m2 self.span_flap_airfrm = noiseFile.get('flap_span') # flap span m else: self.S_flap_airfrm = np.array([float(sf) for sf in noiseFile.get('flap_area').split()]) self.span_flap_airfrm = np.array([float(bf) for bf in noiseFile.get('flap_span').split()]) self.slat_type = noiseFile.get('leading_edge_type') self.span_hor_tail = noiseFile.get('horizontal_tail_span') # horizontal tail span m self.Sht = noiseFile.get('horizontal_tail_area') # horizontal tail area m2 self.span_ver_tail = noiseFile.get('vertical_tail_span') # vertical tail span m self.Svt = noiseFile.get('vertical_tail_area') # vertical tail area m2 self.no_engines = int(noiseFile.get('no_engines')) self.h_engine = noiseFile.get('engine_height') self.dtIsa = noiseFile.get('dtIsa') self.total_weight_airfrm = noiseFile.get('aircraft_mass') self.nlg_airfrm = int(noiseFile.get('noLG')) if self.nlg_airfrm == 1: self.d_wheel_airfrm[0] = noiseFile.get('d_wheel') / choice_data.ft2m self.d_strut_airfrm[0] = noiseFile.get('d_strut') / choice_data.ft2m self.d_wire_airfrm[0] = noiseFile.get('d_wire') / choice_data.ft2m self.N_wheels_airfrm[0] = noiseFile.get('Nwheels') self.N_struts_airfrm[0] = noiseFile.get('Nstruts') else: self.d_wheel_airfrm = np.array([float(dw) for dw in noiseFile.get('d_wheel').split()]) / choice_data.ft2m self.d_strut_airfrm = np.array([float(ds) for ds in noiseFile.get('d_strut').split()]) / choice_data.ft2m self.d_wire_airfrm = np.array([float(dw) for dw in noiseFile.get('d_wire').split()]) / choice_data.ft2m self.N_wheels_airfrm = np.array([float(nw) for nw in noiseFile.get('Nwheels').split()]) self.N_struts_airfrm = np.array([float(ns) for ns in noiseFile.get('Nstruts').split()]) self.defl_flap_airfrm_vec = np.zeros(3) self.defl_slat_airfrm_vec = np.zeros(3) self.LandingGear = np.zeros(3) if self.nops == 1: self.defl_flap_airfrm_vec[0] = noiseFile.get('defl_flap') self.defl_slat_airfrm_vec[0] = noiseFile.get('defl_slat') self.LandingGear[0] = noiseFile.get('LandingGear_vec') else: self.defl_flap_airfrm_vec = np.array([float(df) for df in noiseFile.get('defl_flap').split()]) self.defl_slat_airfrm_vec = np.array([float(ds) for ds in noiseFile.get('defl_slat').split()]) self.LandingGear = np.array([int(lg) for lg in noiseFile.get('LandingGear_vec').split()])
[docs]class Trajectory: """ Instantiate the trajectory data of the aircraft. :param int ipnt: Operating point (trajectory) number in order of appearance in the inputNoise.txt :param str opPnt: Operating point (trajectory), e.g. Approach, Cutback, Sideline :param NoiseChoice noise_choice: Noise prediction input data """ def __init__(self, ipnt, opPnt, noise_choice, input_folder): """ Constructs all the necessary attributes for the trajectory object to be used in the noise calculation, for the selected trajectories. Computes distance to mic (r), flight path angle (clgr), time and angle between aircraft and microphone (xsi). """ self.read_trajectory_input(opPnt, input_folder, noise_choice.h_engine) # read trajectory data from file if 'Approach' in opPnt and self.x[0]>0: self.x = self.x - self.x[-1] self.r = np.sqrt((self.x - noise_choice.xmic[ipnt]) ** 2 + (self.y - noise_choice.ymic[ipnt] - noise_choice.elevation) ** 2 + noise_choice.zmic[ipnt] ** 2) # distance along trajectory to microphone self.clgr = np.zeros(self.n_traj_pts) # flight path angle along trajectory for i in range(self.n_traj_pts - 1): if self.y[i + 1] != 0: self.clgr[i] = np.rad2deg(math.atan((self.y[i + 1] - self.y[i]) / (self.x[i + 1] - self.x[i]))) if np.isnan(self.clgr[i]): sys.exit('not a number (NaN) computed for clgr') self.clgr[self.n_traj_pts - 1] = self.clgr[self.n_traj_pts - 2] self.get_xsi(noise_choice.xmic[ipnt], noise_choice.ymic[ipnt] + noise_choice.elevation, noise_choice.zmic[ipnt]) if np.isnan(self.xsi).any(): sys.exit('not a number computed for xsi') self.xsid = np.rad2deg(self.xsi) # time is computed for 2D trajectory self.time = np.zeros(self.n_traj_pts) for i in range(1, self.n_traj_pts): if math.sqrt((self.y[i] - self.y[i - 1]) ** 2 + (self.x[i] - self.x[i - 1]) ** 2) == 0: self.time[i] = self.time[i - 1] + 1 # aircraft is not moving - impossible to compute time => set a fictitious time else: # use average velocity self.V_avg = (self.Va[i] + self.Va[i - 1]) / 2 self.time[i] = self.time[i - 1] + math.sqrt( (self.y[i] - self.y[i - 1]) ** 2 + (self.x[i] - self.x[i - 1]) ** 2) / self.V_avg if np.isnan(self.time[i]): sys.exit('not a number computed for time')
[docs] def set_time_vector(self, ipnt, noise_choice): """ Computes the retarded time. :param int ipnt: Operating point (trajectory) number in order of appearance in the inputNoise.txt :param NoiseChoice noise_choice: NoiseChoice object """ dt = noise_choice.dt_mic # create tmic (starting from when the first noise reaches the microphone from xstart (i.e. r[0]/a[0]) to when # the noise reaches the microphone from the last point) self.tmic = np.arange(self.r[0] / self.a[0], self.time[-1] + self.r[-1] / self.a[-1], dt) self.n_times = len(self.tmic) self.t_source = np.zeros(self.n_times) self.x_source = np.zeros(self.n_times) self.y_source = np.zeros(self.n_times) self.va_source = np.zeros(self.n_times) self.a_source = np.zeros(self.n_times) self.r1 = np.zeros(self.n_times) self.clgr_source = np.zeros(self.n_times) self.x_source[0] = self.x[0] - noise_choice.xmic[ipnt] # use mic-related coordinate system self.y_source[0] = self.y[0] - (noise_choice.ymic[ipnt] + noise_choice.elevation) # use mic-related coordinate system self.va_source[0] = self.Va[0] self.a_source[0] = self.a[0] self.clgr_source[0] = self.clgr[0] self.z_source = noise_choice.zmic[ipnt] self.r1[0] = math.sqrt(self.x_source[0] ** 2 + self.y_source[0] ** 2 + self.z_source ** 2) # distance between source and microphone. for i in range(1, self.n_times): if self.va_source[i - 1] == 0: dx = 0 else: dx = self.get_dx(self.x_source[i - 1], self.y_source[i - 1], self.z_source, dt, self.clgr_source[i - 1], self.a_source[i - 1], self.va_source[i - 1], self.r1[i - 1] / self.a_source[i - 1] + dt) # update using dx self.x_source[i] = self.x_source[i - 1] + dx dy = dx * math.tan(np.radians(self.clgr_source[i - 1])) self.y_source[i] = self.y_source[i - 1] + dy dr = math.sqrt(dx ** 2 + dy ** 2) self.r1[i] = math.sqrt(self.x_source[i] ** 2 + self.y_source[i] ** 2 + self.z_source ** 2) if dr == 0: self.t_source[i] = self.t_source[i - 1] + dt else: self.t_source[i] = self.t_source[i - 1] + dr / self.va_source[i - 1] # interpolate self.clgr_source[i] = interp1d(self.x, self.clgr, fill_value="extrapolate")( self.x_source[i] + noise_choice.xmic[ipnt]) self.va_source[i] = interp1d(self.x, self.Va, fill_value="extrapolate")( self.x_source[i] + noise_choice.xmic[ipnt]) self.a_source[i] = interp1d(self.x, self.a, fill_value="extrapolate")( self.x_source[i] + noise_choice.xmic[ipnt])
[docs] def set_velocity(self, dtIsa): """ Computes ambient temperature (ta), speed of sound (a) and Mach number (Ma) along the trajectory :param float dtIsa: Deviation from ISA temperature """ R = choice_aux.get_R(0.0) self.pa = choice_physics.AtmosphericEffects.get_p_ambient(self.y) self.ta = choice_physics.AtmosphericEffects.get_t_ambient(self.y, dtIsa) self.a = choice_physics.AtmosphericEffects.get_sound_speed(self.ta) self.Ma = self.Va / self.a
[docs] @staticmethod def get_dx(x1, y1, z1, dt, gamma, c0, Va, tnext): """ Computes distance travelled until the location of the next noise sample generation. :param float x1: x coordinate of first sample (in relation to microphone) :param float y1: y coordinate (height) of first sample (in relation to microphone) :param float z1: z coordinate of microphone :param float dt: Time interval between sounds reaching the microphone :param float gamma: Flight path angle :param float c0: Speed of sound at aircraft location :param float Va: Aircraft speed :param float tnext: Next sampling time :return float: The distance that the aircraft travelled """ solution_found = False tg = math.tan(np.radians(gamma)) r1 = math.sqrt(x1 ** 2 + y1 ** 2 + z1 ** 2) a = ((1.0 + tg ** 2) * (c0 ** 2 - Va ** 2)) / Va ** 2 b = -2.0 * (r1 + c0 * dt) * c0 * math.sqrt(1.0 + tg ** 2) / Va - 2.0 * x1 - 2.0 * y1 * tg c = (c0 ** 2) * (dt ** 2) + 2 * r1 * c0 * dt sol1 = (-b + math.sqrt(b ** 2 - 4.0 * a * c)) / (2.0 * a) sol2 = (-b - math.sqrt(b ** 2 - 4.0 * a * c)) / (2.0 * a) # pick solution x2 = x1 + sol1 y2 = y1 + sol1 * tg z2 = z1 dr = math.sqrt(sol1 ** 2 + (sol1 * tg) ** 2) attempt1 = math.sqrt(x2 ** 2 + y2 ** 2 + z2 ** 2) / c0 + dr / Va res = abs(attempt1 - tnext) / max(1.0, tnext) if res < 1000 * sys.float_info.epsilon: dx = sol1 solution_found = True x2 = x1 + sol2 y2 = y1 + sol2 * tg dr = math.sqrt(sol2 ** 2 + (sol2 * tg) ** 2) attempt2 = math.sqrt(x2 ** 2 + y2 ** 2 + z2 ** 2) / c0 + dr / Va res = abs(attempt2 - tnext) / max(1.0, tnext) if res < 1000 * sys.float_info.epsilon: dx = sol2 solution_found = True if not solution_found: print('failed to establish solution in get_dx in choice_interf') return else: return dx
[docs] def get_xsi(self, xmic, ymic, zmic): """ Computes the angle between the direction of the aircraft's motion and the sound propagation path. :param float xmic: Microphone/observer horizontal distance (m) :param float ymic: Microphone/observer height (m) :param float zmic: Microphone/observer lateral horizontal distance (m) """ if zmic > 0: ex = (xmic - self.x) / np.sqrt((xmic - self.x)**2 + zmic**2 + (ymic - self.y)**2) ey = (self.y - ymic) / np.sqrt((xmic - self.x)**2 + zmic**2 + (ymic - self.y)**2) ez = zmic / np.sqrt((xmic - self.x)**2 + zmic**2 + (ymic - self.y)**2) ex_body = ex * np.cos(np.deg2rad(self.clgr)) - ey * np.sin(np.deg2rad(self.clgr)) ey_body = ex * np.sin(np.deg2rad(self.clgr)) + ey * np.cos(np.deg2rad(self.clgr)) ez_body = ez fi = np.arccos(ex_body) self.xsi = fi self.phi = np.degrees(np.arctan2(ey, ez)) ex_body_w_alpha = ex * np.cos(np.deg2rad(self.clgr + self.alpha)) - \ ey * np.sin(np.deg2rad(self.clgr + self.alpha)) self.xsi_alpha = np.arccos(ex_body_w_alpha) else: fi = np.arctan((xmic - self.x) / (self.y - ymic)) fi[self.y <= ymic] = math.pi / 2 psi = math.pi / 2 - fi self.xsi = psi + np.deg2rad(self.clgr) self.xsi_alpha = psi + np.deg2rad(self.clgr + self.alpha)
[docs] def read_trajectory_input(self, opPnt, input_folder, h_engine): """ Reads and stores the trajectory data (position, velocity and angle of attack) for the provided operating point :param str opPnt: Operating point """ file = input_folder + opPnt.strip() + '.txt' x = [] y = [] Va = [] alpha = [] with open(file) as fp: for line in fp: if line: # omit blank lines li = line.strip() if not li.startswith('!'): string = li.split() x.append(float(string[0])) y.append(float(string[1])) Va.append(float(string[2])) alpha.append(float(string[3])) else: continue self.x = np.asarray(x) self.y = np.asarray(y) + h_engine self.Va = np.asarray(Va) self.alpha = np.asarray(alpha) self.n_traj_pts = len(self.x)
[docs] @classmethod def set(cls, ipnt, opPnt, noise_choice, input_folder): """ Sets the required trajectory details for the noise calculation. :param int ipnt: Operating point (trajectory) number in order of appearance in the inputNoise.txt :param str opPnt: Operating point (trajectory), e.g. Approach, Cutback, Sideline :param NoiseChoice noise_choice: NoiseChoice object :return Trajectory: A trajectory object """ traj = cls(ipnt, opPnt, noise_choice, input_folder) # Compute r, clgr, xsi and time traj.set_velocity(noise_choice.dtIsa) # compute ta, a and Ma traj.set_time_vector(ipnt, noise_choice) # compute "retarded" vectors at source return traj
[docs]def set_rotational_speeds_choice(mpd, weightChoice, comp): """ Estimates absolute speeds from relative rotating speeds (from performance) using several points. :param MultptPerfData mpd: Multipoint performance data :param WeightChoice weightChoice: Engine sizing data :param str comp: Engine component :return MultptPerfData: Updated multipoint performance data """ N = {} if comp == 'Fan': param = 'NL' xnlD = weightChoice.xnlD_fan elif comp == 'Fuselage_fan': param = 'NFF' xnlD = weightChoice.xnlD_ff for key in mpd.__dict__: N[key] = mpd.__dict__[key].get(param) if len(mpd.perf_file_sl) > 0: N["perf_file_to"] = N["perf_file_sl"] # use sideline data instead of take-off if # sideline exists. # form ratios and use the WEICO value for key in N.keys(): N[key] = xnlD * (N[key] / N["perf_file_toc"]) mpd.__dict__[key][param] = N[key] return mpd
[docs]class PerformanceChoice: """ Instantiate the engine performance data. :param int ptr: Operating point (trajectory) number in order of appearance in the inputNoise.txt :param bool trajPerf: If true, trajectory data are used, else, single point study is performed :param str point: Operating point :param int n_traj: Number of trajectory points :param WeightChoice Weight_choice: Engine sizing data :param MultptPerfData mpd: Multipoint performance data :param str input_folder: Input files folder path """ def __init__(self, ptr, trajPerf, point, n_traj, weight_choice, mpd, input_folder): """ Constructs the basic attributes for the engine performance that are required for the noise calculation. """ self.ptr = ptr self.trajPerf = trajPerf self.point = point self.openRotor = False self.n_traj = n_traj self.weight_choice = weight_choice self.mpd = mpd self.input_folder = input_folder
[docs] def getIsOpenRotor(self, modules): """ Checks if it is an open rotor. """ for module in modules: if module.rstrip() == 'Prop': self.openRotor = True break return self.openRotor
[docs] def coAxialJet(self): """ Sets the jet performance parameters. """ file_path = self.input_folder + self.point.rstrip() + '_coAxialJet_performance.txt' if self.trajPerf: storage_mat = choice_aux.loadStorageMat(file_path, self.n_traj, 6) self.dmdt_1_caj = storage_mat[:, 0] self.dmdt_2_caj = storage_mat[:, 1] self.v_1_caj = storage_mat[:, 2] self.v_2_caj = storage_mat[:, 3] self.T_1_caj = storage_mat[:, 4] self.T_2_caj = storage_mat[:, 5] else: # 2 is cold. W8 = self.get_variable(self.point, 'W8') W18 = self.get_variable(self.point, 'W18') M8 = self.get_variable(self.point, 'M8') M18 = self.get_variable(self.point, 'M18') T8 = self.get_variable(self.point, 'T8') T18 = self.get_variable(self.point, 'T18') C18 = choice_physics.AtmosphericEffects.getVel(choice_data.gamma_air, T18, M18) C8 = choice_physics.AtmosphericEffects.getVel(choice_data.gamma_gas, T8, M8) self.dmdt_1_caj = np.full(self.n_traj, W8) self.dmdt_2_caj = np.full(self.n_traj, W18) self.v_1_caj = np.full(self.n_traj, C8) self.v_2_caj = np.full(self.n_traj, C18) self.T_1_caj = np.full(self.n_traj, T8) self.T_2_caj = np.full(self.n_traj, T18)
[docs] def setAirfrm(self, noise_choice): """ Sets the airframe performance parameters. """ file_path = self.input_folder + self.point.rstrip() + '_airfrm_performance.txt' if self.trajPerf: storage_mat = choice_aux.loadStorageMat(file_path, self.n_traj, 4) self.defl_flap_airfrm = storage_mat[:, 1] self.defl_slat_airfrm = storage_mat[:, 2] self.LandingGear = storage_mat[:, 3] else: self.defl_flap_airfrm = np.full(self.n_traj, noise_choice.defl_flap_airfrm_vec[self.ptr]) self.defl_slat_airfrm = np.full(self.n_traj, noise_choice.defl_slat_airfrm_vec[self.ptr]) self.LandingGear = np.full(self.n_traj, noise_choice.LandingGear[self.ptr])
[docs] def setLpt(self): """ Sets the low pressure turbine performance parameters. """ file_path = self.input_folder + self.point.rstrip() + '_lpt_performance.txt' if self.trajPerf: if choice_aux.containsStars(file_path): print('Discovered raw trajectory mission file (lpt) - computing *** and ** from other data and ' 're-writing file (this is done only once)') choice_aux.preProcessLptFile(file_path, self.weight_choice.De_lpt, self.weight_choice.Ae_lpt) storage_mat = choice_aux.loadStorageMat(file_path, self.n_traj, 5) self.Vtr_lpt = storage_mat[:, 0] self.Texit_lpt = storage_mat[:, 1] self.xnl_lpt = storage_mat[:, 2] self.mcore_lpt = storage_mat[:, 3] self.Cax_lpt = storage_mat[:, 4] else: # use performanceResults.txt file and additional data to estimate the trajectory variables. xnl = self.get_variable(self.point, 'NL') t5 = self.get_variable(self.point, 'T5') w5 = self.get_variable(self.point, 'W5') p5 = self.get_variable(self.point, 'P5') Cax = choice_aux.get_Cax(choice_data.gamma_gas, p5, t5, w5, self.weight_choice.Ae_lpt) xnl_lpt = xnl * self.weight_choice.gbx_ratio # xnl should be fan speed self.Vtr_lpt = np.full(self.n_traj, (math.pi * self.weight_choice.De_lpt) * xnl_lpt * 0.70) # when the relative tip speed is unknown, use 70% of the blade tip speed self.Texit_lpt = np.full(self.n_traj, t5) self.xnl_lpt = np.full(self.n_traj, xnl * self.weight_choice.gbx_ratio) # xnl should be fan speed self.mcore_lpt = np.full(self.n_traj, w5) self.Cax_lpt = np.full(self.n_traj, Cax)
[docs] def setComb(self): """ Sets the combustor performance parameters. """ file_path = self.input_folder + self.point.rstrip() + '_comb_performance.txt' if self.trajPerf: storage_mat = choice_aux.loadStorageMat(file_path, self.n_traj, 7) self.P3_comb = storage_mat[:, 0] self.P4_comb = storage_mat[:, 1] self.P7_comb = storage_mat[:, 2] self.T3_comb = storage_mat[:, 3] self.T4_comb = storage_mat[:, 4] self.T5_comb = storage_mat[:, 5] self.W3_comb = storage_mat[:, 6] else: # use performanceResults.txt file and additional data to estimate the trajectory variables. P3 = self.get_variable(self.point, 'P3') P4 = self.get_variable(self.point, 'P4') P7 = self.get_variable(self.point, 'P5') T3 = self.get_variable(self.point, 'T3') T4 = self.get_variable(self.point, 'T4') T5 = self.get_variable(self.point, 'T5') W3 = self.get_variable(self.point, 'W3') self.P3_comb = np.full(self.n_traj, P3) self.P4_comb = np.full(self.n_traj, P4) self.P7_comb = np.full(self.n_traj, P7) self.T3_comb = np.full(self.n_traj, T3) self.T4_comb = np.full(self.n_traj, T4) self.T5_comb = np.full(self.n_traj, T5) self.W3_comb = np.full(self.n_traj, W3)
[docs] def setFan(self): """ Sets the fan performance parameters. """ file_path = self.input_folder + self.point.rstrip() + '_fan_performance.txt' if self.trajPerf: if choice_aux.containsStars(file_path): print('Discovered raw trajectory mission file (fan) - computing Mtip and Mu from other data and ' 're-writing file (this is done only once)') choice_aux.preProcessFanFile(file_path, self.weight_choice.D1_fan, self.weight_choice.A2_fan) storage_mat = choice_aux.loadStorageMat(file_path, self.n_traj, 5) self.Mtip_fan = storage_mat[:, 0] self.Mu_fan = storage_mat[:, 1] self.xnl_fan = storage_mat[:, 2] self.dt_fan = storage_mat[:, 3] self.g1_fan = storage_mat[:, 4] else: # use performanceResults.txt file and additional data to estimate the trajectory variables. xnl = self.get_variable(self.point, 'NL') g1 = self.get_variable(self.point, 'W2') t1 = self.get_variable(self.point, 'T2') dt = self.get_variable(self.point, 'T13') - t1 p1 = self.get_variable(self.point, 'P2') [Mtip, Mu, Umid] = choice_aux.setMachNumbers(p1, t1, g1, self.weight_choice.A2_fan, self.weight_choice.D1_fan, xnl, choice_data.gamma_air) self.xnl_fan = np.full(self.n_traj, xnl) self.dt_fan = np.full(self.n_traj, dt) self.g1_fan = np.full(self.n_traj, g1) self.Mtip_fan = np.full(self.n_traj, Mtip) self.Mu_fan = np.full(self.n_traj, Mu)
[docs] def setLpc(self): """ Sets the low pressure compressor performance parameters. """ file_path = self.input_folder + self.point.rstrip() + '_lpc_performance.txt' if self.trajPerf: if choice_aux.containsStars(file_path): print('Discovered raw trajectory mission file (lpc) - computing Mtip and Mu from other data and ' 're-writing file (this is done only once)') A2_lpc = math.pi * ((self.weight_choice.D1_lpc / 2) ** 2 - (self.weight_choice.Dh1_lpc / 2) ** 2) choice_aux.preProcessFanFile(file_path, self.weight_choice.D1_lpc, A2_lpc) storage_mat = choice_aux.loadStorageMat(file_path, self.n_traj, 5) self.Mtip_lpc = storage_mat[:, 0] self.Mu_lpc = storage_mat[:, 1] self.xnl_lpc = storage_mat[:, 2] self.dt_lpc = storage_mat[:, 3] / self.weight_choice.n_stages_LPC self.g1_lpc = storage_mat[:, 4] else: # use performanceResults.txt file and additional data to estimate the trajectory variables. if not self.openRotor: xnl = self.get_variable(self.point, 'NL') xni = xnl * self.weight_choice.gbx_ratio else: sys.exit('no open rotor method yet - lpc') g1 = self.get_variable(self.point, 'W23') t1 = self.get_variable(self.point, 'T23') p1 = self.get_variable(self.point, 'P23') A = (math.pi / 4) * (self.weight_choice.D1_lpc ** 2 - self.weight_choice.Dh1_lpc ** 2) [Mtip, Mu, Umid] = choice_aux.setMachNumbers(p1, t1, g1, A, self.weight_choice.D1_lpc, xni, choice_data.gamma_air) if self.weight_choice.gbx_ratio == 1: dt = (0.6 * Umid ** 2) / choice_data.cp_air else: dt = (0.45 * Umid ** 2) / choice_data.cp_air self.xnl_lpc = np.full(self.n_traj, xni) self.dt_lpc = np.full(self.n_traj, dt) self.g1_lpc = np.full(self.n_traj, g1) self.Mtip_lpc = np.full(self.n_traj, Mtip) self.Mu_lpc = np.full(self.n_traj, Mu)
[docs] def setff(self): """ Sets the fuselage fan performance parameters. """ file_path = self.input_folder + self.point.rstrip() + '_fuselagefan_performance.txt' if self.trajPerf: if choice_aux.containsStars(file_path): print('Discovered raw trajectory mission file (fuselagefan) - computing Mtip and Mu from other data ' 'and re-writing file (this is done only once)') choice_aux.preProcessFanFile(file_path, self.weight_choice.D1_ff, self.weight_choice.A2_ff) storage_mat = choice_aux.loadStorageMat(file_path, self.n_traj, 5) self.Mtip_ff = storage_mat[:, 0] self.Mu_ff = storage_mat[:, 1] self.xnl_ff = storage_mat[:, 2] self.dt_ff = storage_mat[:, 3] self.g1_ff = storage_mat[:, 4] else: # use performanceResults.txt file and additional data to estimate the trajectory variables. xnl = self.get_variable(self.point, 'NFF') g1 = self.get_variable(self.point, 'Wff2') t1 = self.get_variable(self.point, 'Tff2') dt = self.get_variable(self.point, 'Tff3') - t1 p1 = self.get_variable(self.point, 'Pff2') [Mtip, Mu, Umid] = choice_aux.setMachNumbers(p1, t1, g1, self.weight_choice.A2_ff, self.weight_choice.D1_ff, xnl, choice_data.gamma_air) self.xnl_ff = np.full(self.n_traj, xnl) self.dt_ff = np.full(self.n_traj, dt) self.g1_ff = np.full(self.n_traj, g1) self.Mtip_ff = np.full(self.n_traj, Mtip) self.Mu_ff = np.full(self.n_traj, Mu)
[docs] def coAxialJet_ffn(self): """ Sets the jet performance parameters for fuselage fan nozzle . """ if self.trajPerf: storage_mat = choice_aux.loadStorageMat(self.point.rstrip() + '_ffnJet_performance.txt', self.n_traj, 6) self.dmdt_1_caj_ffn = storage_mat[:, 0] self.dmdt_2_caj_ffn = storage_mat[:, 1] self.v_1_caj_ffn = storage_mat[:, 2] self.v_2_caj_ffn = storage_mat[:, 3] self.T_1_caj_ffn = storage_mat[:, 4] self.T_2_caj_ffn = storage_mat[:, 5] else: # 2 is cold. Wffn8 = self.get_variable(self.point, 'Wffn8') Wffn18 = self.get_variable(self.point, 'Wffn18') Mffn8 = self.get_variable(self.point, 'Mffn8') Mffn18 = self.get_variable(self.point, 'Mffn18') Tffn8 = self.get_variable(self.point, 'Tffn8') Tffn18 = self.get_variable(self.point, 'Tffn18') Cffn18 = choice_physics.getVel(choice_data.gamma_air, Tffn18, Mffn18) Cffn8 = choice_physics.getVel(choice_data.gamma_gas, Tffn8, Mffn8) self.dmdt_1_caj_ffn = np.full(self.n_traj, Wffn8) self.dmdt_2_caj_ffn = np.full(self.n_traj, Wffn18) self.v_1_caj_ffn = np.full(self.n_traj, Cffn8) self.v_2_caj_ffn = np.full(self.n_traj, Cffn18) self.T_1_caj_ffn = np.full(self.n_traj, Tffn8) self.T_2_caj_ffn = np.full(self.n_traj, Tffn18)
[docs] def get_variable(self, point, name): """ Returns the value of the item with the specified key for the provided point. """ if point == 'Take-off': return self.mpd.perf_file_to.get(name) elif point == 'Sideline': return self.mpd.perf_file_sl.get(name) elif point == 'Cutback': return self.mpd.perf_file_cutback.get(name) elif point == 'Approach': return self.mpd.perf_file_approach.get(name) elif point == 'Cruise': return self.mpd.perf_file_cr.get(name) elif point == 'Top-of-climb': return self.mpd.perf_file_toc.get(name)
[docs] @classmethod def set(cls, ptr, modules, n_traj, mpd, noise_choice, weight_choice, input_folder): """ Sets the performance data for each component. :param int ptr: Operating point (trajectory) number in order of appearance in the inputNoise.txt :param list modules: Engine components :param int n_traj: Number of trajectory points :param MultptPerfData mpd: Multipoint performance data :param NoiseChoice noise_choice: Noise prediction input data :param WeightChoice weight_choice: Engine sizing data :param str input_folder: Input files folder :return PerformanceChoice: A PerformanceChoice object """ performance_choice = cls(ptr, noise_choice.trajectory_performance, noise_choice.opPnt[ptr].strip(), n_traj, weight_choice, mpd, input_folder) openRotor = performance_choice.getIsOpenRotor(modules) if openRotor: sys.exit('Model is open rotor - noise modelling not implemented yet') for module in modules: if module == 'Fan': performance_choice.setFan() if module == 'Ipc' or module == 'Lpc': performance_choice.setLpc() if module == 'Lpt': performance_choice.setLpt() if module == 'Comb': performance_choice.setComb() if module == 'Cold_nozzle': performance_choice.coAxialJet() if module == 'Fuselage_fan': performance_choice.setff() if module == 'Ff_nozzle': performance_choice.coAxialJet_ffn() performance_choice.setAirfrm(noise_choice) return performance_choice
[docs]class Prms: """ Instantiate rms acoustic pressure for each aircraft noise component. :param int npts: Number of trajectory points """ def __init__(self, npts): """ Initializes the prms matrix for each component. """ nfreq = choice_data.nfreq nthet = choice_data.nthet self.Fan_inlet_tone = np.zeros((nfreq, nthet, npts)) self.Fan_discharge_tone = np.zeros((nfreq, nthet, npts)) self.Fan_inlet_broadband = np.zeros((nfreq, nthet, npts)) self.Fan_discharge_broadband = np.zeros((nfreq, nthet, npts)) self.Fan_inlet_combination = np.zeros((nfreq, nthet, npts)) self.Fan_inlet = np.zeros((nfreq, nthet, npts)) self.Fan_discharge = np.zeros((nfreq, nthet, npts)) self.Lpc_inlet_tone = np.zeros((nfreq, nthet, npts)) self.Lpc_discharge_tone = np.zeros((nfreq, nthet, npts)) self.Lpc_inlet_broadband = np.zeros((nfreq, nthet, npts)) self.Lpc_discharge_broadband = np.zeros((nfreq, nthet, npts)) self.Lpc_inlet_combination = np.zeros((nfreq, nthet, npts)) self.Lpc_inlet = np.zeros((nfreq, nthet, npts)) self.Lpc_discharge = np.zeros((nfreq, nthet, npts)) self.Comb = np.zeros((nfreq, nthet, npts)) self.Lpt = np.zeros((nfreq, nthet, npts)) self.Caj = np.zeros((nfreq, nthet, npts)) self.Airfrm = np.zeros((nfreq, nthet, npts)) self.Airfrm_wing = np.zeros((nfreq, nthet, npts)) self.Airfrm_hor_tail = np.zeros((nfreq, nthet, npts)) self.Airfrm_slats = np.zeros((nfreq, nthet, npts)) self.Airfrm_flaps = np.zeros((nfreq, nthet, npts)) self.Airfrm_nose_lg = np.zeros((nfreq, nthet, npts)) self.Airfrm_main_lg = np.zeros((nfreq, nthet, npts)) self.Airfrm_lg = np.zeros((nfreq, nthet, npts)) self.Ff_inlet_tone = np.zeros((nfreq, nthet, npts)) self.Ff_discharge_tone = np.zeros((nfreq, nthet, npts)) self.Ff_inlet_broadband = np.zeros((nfreq, nthet, npts)) self.Ff_discharge_broadband = np.zeros((nfreq, nthet, npts)) self.Ff_inlet_combination = np.zeros((nfreq, nthet, npts)) self.Ff_inlet = np.zeros((nfreq, nthet, npts)) self.Ff_discharge = np.zeros((nfreq, nthet, npts)) self.Caj_ffn = np.zeros((nfreq, nthet, npts))
[docs]class NoiseSources: """ Instantiate noise source object :param Prms prms: Acoustic pressure arrays for each aircraft noise component :param ndarray theta: 1D array containg directivity angles :param ndarray fband: 1D array containg the 1/3 octave band frequencies """ def __init__(self, prms, theta, fband): self.prms = prms self.theta = theta self.fband = fband
[docs] @classmethod def compute(cls, traj, modules, noise, weight, performance, nop, output_folder, ext=None): """ Compute the acoustic pressure for each noise component. :param Trajectory traj: A Trajectory object with the trajectory data :param list modules: Fan, Lpc, Lpt, etc. :param NoiseChoice noise: A NoiseChoice object with the required input arguments for the noise prediction :param WeightChoice weight: A WeightChoice object with the engine size and architecture :param PerformanceChoice performance: A PerformanceChoice object with the engine performance data :param int nop: Number of operating point :param str output_folder: Output folder path :param str ext: File extension for noise source matrices :return NoiseSources: A Prms object with the rms acoustic pressure for each component, the directivity angles and the 1/3 octave band frequencies """ theta = np.array([0.0 + i * 5.0 for i in range(choice_data.nthet)]) [fband, f, freq] = choice_aux.set_frequencies(choice_data.nb, choice_data.nfreq, float(choice_data.fmin), float(choice_data.fmax)) # flyover plane prms = Prms(traj.n_traj_pts) operatingPoint = noise.opPnt[nop].strip() airframe = choice_physics.Airframe(operatingPoint, noise.N_wheels_airfrm, noise.N_struts_airfrm, noise.nlg_airfrm, noise.d_wheel_airfrm, noise.d_strut_airfrm, noise.d_wire_airfrm, noise.NoFlap, noise.S_flap_airfrm, noise.span_flap_airfrm, noise.Sw_airfrm, noise.span_airfrm, noise.ND, noise.span_hor_tail, noise.Sht, noise.span_ver_tail, noise.Svt, noise.flap_type, noise.slat_type, theta, fband) if 'Cold_nozzle' in modules: jet = choice_physics.Jet(weight.A_core_caj, weight.A_bypass_caj, noise.type_nozzles, theta, fband) if 'Comb' in modules: comb = choice_physics.Combustor(weight.type_comb, weight.Aec_comb, weight.De_comb, weight.Dh_comb, weight.Lc_comb, weight.h_comb, weight.Nfmax_comb, theta, fband) if 'Lpt' in modules: turb = choice_physics.Turbine(weight.N_rotors_lpt, weight.SRS_lpt, theta, fband, f) if 'Fan' in modules: fan = choice_physics.FanCompressor('Fan', weight.MtipD_fan, weight.N_rotors_fan, weight.N_stators_fan, weight.rss_fan, theta, fband, f, noise.fan_distortion) if 'Ipc' in modules: lpc = choice_physics.FanCompressor('Ipc', weight.MtipD_lpc, weight.N_rotors_lpc, weight.N_stators_lpc, weight.rss_lpc, theta, fband, f) if 'Lpc' in modules: lpc = choice_physics.FanCompressor('Lpc', weight.MtipD_lpc, weight.N_rotors_lpc, weight.N_stators_lpc, weight.rss_lpc, theta, fband, f) if 'Fuselage_fan' in modules: ffan = choice_physics.FanCompressor('Fuselage_fan', weight.MtipD_ff, weight.N_rotors_ff, weight.N_stators_ff, weight.rss_ff, theta, fband, f, noise.ff_distortion) if 'Ff_nozzle' in modules: ffjet = choice_physics.Jet(weight.A_core_caj_ffn, weight.A_bypass_caj_ffn, 'mix') # evaluate component noise models for i in range(traj.n_traj_pts): # for all points along trajectory - evaluate noise sources pa = choice_physics.AtmosphericEffects.get_p_ambient(traj.y[i]) for module in modules: # evaluate all noise sources in this engine model if module.rstrip() == 'Fan': temp = fan.calc(operatingPoint, performance.Mtip_fan[i], performance.Mu_fan[i], performance.dt_fan[i], performance.xnl_fan[i], performance.g1_fan[i]) if noise.use_suppression_factor: prms.Fan_inlet[:, :, i] = fan.suppression(temp[5], noise.S_fan_inlet) prms.Fan_discharge[:, :, i] = fan.suppression(temp[6], noise.S_fan_dis) prms.Fan_inlet_tone[:, :, i] = fan.suppression(temp[0], noise.S_fan_inlet) prms.Fan_discharge_tone[:, :, i] = fan.suppression(temp[1], noise.S_fan_dis) prms.Fan_inlet_broadband[:, :, i] = fan.suppression(temp[2], noise.S_fan_inlet) prms.Fan_discharge_broadband[:, :, i] = fan.suppression(temp[3], noise.S_fan_dis) prms.Fan_inlet_combination[:, :, i] = fan.suppression(temp[4], noise.S_fan_inlet) else: prms.Fan_inlet[:, :, i] = temp[5] prms.Fan_discharge[:, :, i] = temp[6] prms.Fan_inlet_tone[:, :, i] = temp[0] prms.Fan_discharge_tone[:, :, i] = temp[1] prms.Fan_inlet_broadband[:, :, i] = temp[2] prms.Fan_discharge_broadband[:, :, i] = temp[3] prms.Fan_inlet_combination[:, :, i] = temp[4] elif module == 'Ipc' or module == 'Lpc': temp = lpc.calc(operatingPoint, performance.Mtip_lpc[i], performance.Mu_lpc[i], performance.dt_lpc[i], performance.xnl_lpc[i], performance.g1_lpc[i]) prms.Lpc_inlet_tone[:, :, i] = temp[0] prms.Lpc_inlet_broadband[:, :, i] = temp[2] prms.Lpc_inlet_combination[:, :, i] = temp[4] prms.Lpc_inlet[:, :, i] = temp[5] elif module == 'Lpt': prms.Lpt[:, :, i] = turb.calc(performance.Vtr_lpt[i], performance.Texit_lpt[i], performance.xnl_lpt[i], performance.mcore_lpt[i], performance.Cax_lpt[i]) if noise.use_suppression_factor: prms.Lpt[:, :, i] = turb.suppression(prms.Lpt[:, :, i], noise.S_lpt) elif module == 'Comb': prms.Comb[:, :, i] = comb.calc(min(noise.Nf_comb_ign[nop], weight.Nfmax_comb), noise.Nf_comb_pattern, pa, performance.P3_comb[i], performance.P4_comb[i], performance.P7_comb[i], traj.ta[i], performance.T3_comb[i], performance.T4_comb[i], performance.T5_comb[i], performance.W3_comb[i]) elif module == 'Cold_nozzle': prms.Caj[:, :, i] = jet.calc(performance.dmdt_1_caj[i], performance.dmdt_2_caj[i], performance.v_1_caj[i], performance.v_2_caj[i], performance.T_1_caj[i], performance.T_2_caj[i], traj.ta[i], pa) elif module == 'Fuselage_fan': temp = ffan.calc(module, operatingPoint, performance.Mtip_ff[i], performance.Mu_ff[i], performance.dt_ff[i], performance.xnl_ff[i], performance.g1_ff[i]) prms.Ff_inlet_tone[:, :, i] = temp[0] prms.Ff_discharge_tone[:, :, i] = temp[1] prms.Ff_inlet_broadband[:, :, i] = temp[2] prms.Ff_discharge_broadband[:, :, i] = temp[3] prms.Ff_inlet_combination[:, :, i] = temp[4] prms.Ff_inlet[:, :, i] = temp[5] prms.Ff_discharge[:, :, i] = temp[6] elif module == 'ff_nozzle': prms.Caj_ffn[:, :, i] = ffjet.calc(performance.dmdt_1_caj_ffn[i], 0.0, performance.v_1_caj_ffn[i], 0.0, performance.T_1_caj_ffn[i], 0.0, traj.ta[i], pa) else: pass if hasattr(traj, 'phi'): temp = airframe.calc(traj.ta[i], traj.y[i], traj.Ma[i], traj.Va[i], 90 - traj.phi[i], performance.defl_flap_airfrm[i], performance.defl_slat_airfrm[i], performance.LandingGear[i]) else: temp = airframe.calc(traj.ta[i], traj.y[i], traj.Ma[i], traj.Va[i], 0, performance.defl_flap_airfrm[i], performance.defl_slat_airfrm[i], performance.LandingGear[i]) prms.Airfrm[:, :, i] = temp[0] prms.Airfrm_wing[:, :, i] = temp[1] prms.Airfrm_hor_tail[:, :, i] = temp[2] prms.Airfrm_slats[:, :, i] = temp[3] prms.Airfrm_flaps[:, :, i] = temp[4] prms.Airfrm_nose_lg[:, :, i] = temp[5] prms.Airfrm_main_lg[:, :, i] = temp[6] prms.Airfrm_lg[:, :, i] = temp[7] # include multiple engines for key in prms.__dict__: if 'Airfrm' not in key: prms.__dict__[key] = cls.include_multiple_engines(noise.no_engines, prms.__dict__[key]) if noise.gen_noise_source_matr: choice_aux.gen_noise_source_matr_subr(output_folder, operatingPoint, choice_data.nfreq, choice_data.nthet, traj.n_traj_pts, prms, ext) if noise.plot_source_spectra: i_plot = np.argmin(abs(traj.xsid - noise.directivity_plot)) choice_aux.plot_source(fband, theta, prms, noise.directivity_plot, i_plot, modules) choice_aux.plot_airframe_source(fband, theta, prms.Airfrm, prms.Airfrm_wing, prms.Airfrm_hor_tail, prms.Airfrm_flaps, prms.Airfrm_slats, prms.Airfrm_main_lg, prms.Airfrm_lg, prms.Airfrm_nose_lg, noise.directivity_plot, i_plot) return cls(prms, theta, fband)
[docs] @staticmethod def include_multiple_engines(no_engines, prms): """ Account for multiple engines on the aircraft. """ return np.sqrt(float(no_engines) * prms ** 2)
[docs]class NoiseMatrices: """ A class that defines the noise level for each aircraft noise component. :param list modules: Fan, Lpc, Lpt, etc. """ def __init__(self, modules=None): """ Initializes the noise level matrix for each component. """ if modules is not None: if 'Fan' in modules: self.Fan_inlet = [] self.Fan_discharge = [] self.Fan_inlet_tone = [] self.Fan_discharge_tone = [] self.Fan_inlet_broadband = [] self.Fan_discharge_broadband = [] self.Fan_inlet_combination = [] if 'Ipc' or 'Lpc' in modules: self.Lpc_inlet = [] self.Lpc_inlet_tone = [] self.Lpc_inlet_broadband = [] self.Lpc_inlet_combination = [] if 'Lpt' in modules: self.Lpt = [] if 'Comb' in modules: self.Comb = [] if 'Cold_nozzle' in modules: self.Caj = [] if 'Fuselage_fan' in modules: self.Ff_inlet = [] self.Ff_discharge = [] if 'Ff_nozzle' in modules: self.Caj_ffn = [] self.Airfrm = []
[docs]def interpolate_to_t_source(traj, modules, prms): """ Computes the Sound Pressure Level for the times that the sound reaches the microphone. :param Trajectory traj: A Trajectory object with the trajectory data :param list modules: Fan, Lpc, Lpt, etc. :param Prms prms: A Prms object with the rms acoustic pressure for each component :return: For the times that the sound reaches the microphone, a NoiseMatrices object with the Sound Pressure Level for each component, the observation angle, the Mach number and the angle of attack """ traj.xsii = interp1d(traj.time, traj.xsi, fill_value="extrapolate")(traj.t_source) traj.Mai = interp1d(traj.time, traj.Ma, fill_value="extrapolate")(traj.t_source) traj.Tai = interp1d(traj.time, traj.ta, fill_value="extrapolate")(traj.t_source) traj.xsi_alphai = interp1d(traj.time, traj.xsi_alpha, fill_value="extrapolate")(traj.t_source) traj.alphai = interp1d(traj.time, traj.alpha, fill_value="extrapolate")(traj.t_source) traj.pai = interp1d(traj.time, traj.pa, fill_value="extrapolate")(traj.t_source) if hasattr(traj, 'phi'): traj.phii = interp1d(traj.time, traj.phi, fill_value="extrapolate")(traj.t_source) SPLi = NoiseMatrices(modules) for key in SPLi.__dict__: SPLi.__dict__[key] = compute_SPLi(source_interpolation(prms.__dict__[key], traj)) return [SPLi, traj]
[docs]def compute_SPLi(prmsi): """ Computes Sound Pressure Level matrix from rms acoustic pressure. """ prmsi[prmsi <= 0] = choice_data.p0 return 20.0 * np.log10(prmsi / choice_data.p0)
[docs]def source_interpolation(prms, traj): """ Interpolate the rms acoustic pressure for the times that reach the microphone. """ return np.array([[interp1d(traj.time, prms[ifr, jth, :], fill_value="extrapolate")(traj.t_source) for jth in range(choice_data.nthet)] for ifr in range(choice_data.nfreq)])
class GroundNoise: def __init__(self, SPLp, fobs): self.SPLp = SPLp self.fobs = fobs @classmethod def compute_flight_effects(cls, use_ground_refl, spherical_spr, atm_atten, traj, ymic, dTisa, elevation, SPLi, theta, fband): """ Computes the sound pressure level matrices along the trajectory accounting for propagation effects. :param bool use_ground_refl: True to account for ground reflection or False else :param bool spherical_spr: True to account for spherical spreading or False else :param bool atm_atten: True to account for atmospheric attenuation or False else :param Trajectory traj: A Trajectory object with the trajectory data :param float ymic: Microphone height (m) :param float dTisa: Deviation from ISA temperature (K) :param float elevation: Ground elevation at microphone location (m) :param ndarray SPLi: 3D array containing Sound Pressure Level :param ndarray theta: 1D array containing directivity angles (deg) :param ndarray fband: 1D array containing 1/3 octave band frequencies (Hz) :return GroundNoise: Sound Pressure Level accounting for flight effects and shifted frequency due to aircraft motion """ propagation = choice_physics.PropagationEffects(ymic, use_ground_refl, spherical_spr, atm_atten, fband, traj.xsii, traj.Mai, traj.xsi_alphai, dTisa, elevation) if hasattr(traj, 'phii'): phi = traj.phii else: phi = np.nan SPLp = NoiseMatrices() prmsp = NoiseMatrices() for key in SPLi.__dict__: [SPLp.__dict__[key], prmsp.__dict__[key]] = \ propagation.flightEffects(traj.n_times, theta, traj.x_source, traj.y_source, traj.r1, traj.Tai, SPLi.__dict__[key], key, phi) return cls(SPLp, propagation.fobs)
[docs]class NoiseMatricesCerification: """ A class that defines the noise level for each aircraft noise component. :param list modules: Fan, Lpc, Lpt, etc. """ def __init__(self, modules=None): """ Initializes the noise level matrix for each component. """ if modules is not None: if 'Fan' in modules: self.Fan_inlet = [] self.Fan_discharge = [] if 'Ipc' or 'Lpc' in modules: self.Lpc_inlet = [] if 'Lpt' in modules: self.Lpt = [] if 'Comb' in modules: self.Comb = [] if 'Cold_nozzle' in modules: self.Caj = [] if 'Fuselage_fan' in modules: self.Ff_inlet = [] self.Ff_discharge = [] if 'Ff_nozzle' in modules: self.Caj_ffn = [] self.Airfrm = []
class CertificationData: @staticmethod def compute(n_times, fobs, fband, SPLp, modules, dt_mic): """ Computes the Effective Perceived Noise Level (EPNL) for each aircraft noise source and for the total aircraft. :param int n_times: Number of points :param ndarray fobs: 1D array containing Doppler shifted frequency (Hz) :param ndarray fband: 1D array containing 1/3 octave band frequencies (Hz) :param NoiseSources SPLp: Sound Pressure Level (dB) :param list modules: Fan, Lpc, Lpt, etc. :param float dt_mic: Sampling time interval or timestep at microphone (sec) :return NoiseSources: EPNL for each aircraft noise source and for the total aircraft """ PNL = NoiseMatricesCerification(modules) PNLT = NoiseMatricesCerification(modules) EPNL = NoiseMatricesCerification(modules) SPLp_total = np.zeros_like(SPLp.Airfrm) for key in PNL.__dict__: PNL.__dict__[key] = choice_physics.PerceivedNoiseMetrics.getPNL(n_times, fobs, SPLp.__dict__[key]) PNLT.__dict__[key] = choice_physics.PerceivedNoiseMetrics.getPNLT(n_times, fband, PNL.__dict__[key], SPLp.__dict__[key]) EPNL.__dict__[key] = choice_physics.PerceivedNoiseMetrics.getEPNL(PNLT.__dict__[key], dt_mic) SPLp_total += 10 ** (SPLp.__dict__[key] / 10) SPLp_total = 10 * np.log10(SPLp_total) PNL.tot = choice_physics.PerceivedNoiseMetrics.getPNL(n_times, fobs, SPLp_total) PNLT.tot = choice_physics.PerceivedNoiseMetrics.getPNLT(n_times, fband, PNL.tot, SPLp_total) EPNL.tot = choice_physics.PerceivedNoiseMetrics.getEPNL(PNLT.tot, dt_mic) return EPNL
[docs]def certificationLimits(noEngines, MTOW): """ Computes EPNL certification limits. """ EPNL = choice_aux.chapter3(noEngines, MTOW) EPNL_cum = EPNL.lateral + EPNL.cutback + EPNL.approach