Ffowcs Williams & Hawkings Analogy

This treatment predicts fluctuating pressure in far-field using the Ffowcs Williams & Hawkings Analogy. The interested reader can make reference to the papers of Casalino (2003) and Najafi-yazdi et al. (2010).

The FW-H acoustic analogy involves enclosing the sound sources with a control surface that is mathematically represented by a function, f (x, t)=0. The acoustic signature at any observer position can be obtained from the FW-H equation:

\[\begin{aligned} p ^ { \prime } ( \mathbf { x } , t ) = \frac { \partial } { \partial t } \int _ { f = 0 } \left[ \frac { Q _ { i } n _ { i } } { 4 \pi | \mathbf { x } - \mathbf { y } | } \right] _ { \tau _ { e } } \mathrm { d } S - \frac { \partial } { \partial x _ { i } } \int _ { f = 0 } \left[ \frac { L _ { i j } n _ { j } } { 4 \pi | \mathbf { x } - \mathbf { y } | } \right] _ { \tau _ { \mathrm { e } } } \mathrm { d } S + \frac { \partial ^ { 2 } } { \partial x _ { i } \partial x _ { j } } \int _ { f > 0 } \left[ \frac { T _ { i j } } { 4 \pi | \mathbf { x } - \mathbf { y } | } \right] _ { \tau _ { \mathrm { e } } } \mathrm { d } V \end{aligned}\]

where \([ ]_{\tau_e}\) denotes evaluation at the emission time \(\tau_e\). The source terms under the integral sign are:

\[\begin{split}\begin{aligned} Q _ { i } & = \rho \left( u _ { i } - v _ { i } \right) + \rho _ { 0 } v _ { i } \\ L _ { i j } & = \rho u _ { i } \left( u _ { j } - v _ { j } \right) + P _ { i j } \end{aligned}\end{split}\]

and \(T_{ij}\) is referred to as Lighthill’s stress tensor:

\[T _ { i j } = \rho u _ { i } u _ { j } + \left[ \left( p - p _ { 0 } \right) - c _ { 0 } ^ { 2 } \left( \rho - \rho _ { 0 } \right) \right] \delta _ { i j } - \sigma _ { i j }\]

The vectors \(u\) and \(v\) are the flow and the surface velocities, respectively. See Najafi-yazdi et al. (2010) for the definition of other quantities. The compression tensor \(P_{ij}\) is defined as:

\[P _ { i j } = \left( p - p _ { 0 } \right) \delta _ { i j } - \sigma _ { i j }\]

Features:

  • Farassat 1A in a medium at rest

  • convective Nayafi-Yasdi 1C (with mean flow)

  • both unstructured & structured surfaces

  • porous and solid formulations

  • Multi-code tool, tested and validated on:

    • AVBP

    • elsA

    • ProLB

    • Cosmic (University of Leicester)

    • FLUSEPA

References

Casalino D., An advanced time approach for acoustic analogy predictions, Journal of Sound and Vibration vol. 261, p. 583-612, 2003.

Najafi-Yazdi A., Brès G. A. and Mongeau L., An acoustic analogy formulation for moving sources in uniformly moving media, Proceedings of the Royal Society A, vol. 467, issue 2125, 2010.

Mandatory

Users must give the observer file in a ‘column’ format.

Running commands

Run in parallel with:

mpirun -np NPROCS python script.py

Run in serial with:

python script.py

Warning

dependency on mpi4py (even in serial)


Input Parameters

The following keys can be set:

  • base – (default = None, type = antares.Base ) – The base on which the FWH analogy will be performed. The object will be modified at the output.

  • meshfile – (default = None, type = tuple(string) or list(string) ) – The path of the mesh of the fwh surface and is format

  • datafile – (default = None, type = tuple(string) or list(string) ) – The path of the fwh surface solution, is format, location of data

  • analogy – (type = str ) – acoustic analogy formulation: 1A or 1C

  • type – (default = porous, type = str ) – the type of acoustic analogy formulation: porous or solid

  • obs_file – (type = str ) – observers file

  • pref – (default = 101325.0, type = float ) – The value of the ambient pressure

  • tref – (default = 298.0, type = float ) – The value of the ambient temperature

  • point – (default = [0.0, 0.0, 0.0], type = tuple(float) or list(float) ) – The reference point used to determine the surface vector orientation

  • line – (default = False, type = bool ) – Line in the flow direction to determine the surface vector orientation

  • direction – (default = x, type = str ) – direction of the line to determine the surface vector orientationor mean flow direction if the choosen analogy is 1C

  • redim – (default = None, type = tuple(float) or list(float) ) – conversion reference values: length, density, velocity, pressure, temperature, time

  • dt – (type = float ) – time step between two instants

  • tinit – (default = 0.0, type = float ) – initial time

  • mach – (default = 0.0, type = float ) – Ambient Mach number

  • sample – (default = 1, type = int ) – The number of time step between two instants

  • move – (default = False, type = bool ) – Define moving FWH surface with analogy 1A

  • output – (default = fwh_result, type = str ) – Output file

  • PressEq – (default = None, type = str ) – Equation to compute pressure if needed,otherwise psta equation is used

Warning

The structure of the input base is modified at the output.

Main functions

class antares.treatment.TreatmentFWH.TreatmentFWH
execute()

Execute the treatment.

Returns

Write a file (default: fwh_result.h5 in hdf_antares format) with a base that contains as many zones as the input observers file. By convention, the first zone of the output base will contains the observer time. Each zone contains one instant and two variables: the fluctuating pressure and the convergence of the signal at the related observer position.

Example

"""
Ffowcs Williams & Hawkings Analogy

In parallel: mpirun -np NPROCS python fwh_mono.py
In serial: python fwh_mono.py
"""
import os
if not os.path.isdir('OUTPUT'):
    os.makedirs('OUTPUT')

import antares



# Read base
r = antares.Reader('bin_tp')
r['filename'] = os.path.join('..', 'data', 'FWH', 'Monopole', 'monopole_<instant>.plt')
base = r.read()

# Apply FWH treatment
treatment = antares.Treatment('fwh')
treatment['base'] = base
treatment['analogy'] = '1A'
treatment['type'] = 'porous'
treatment['obs_file'] = os.path.join('..', 'data', 'FWH', 'fwhobservers_monopole.dat')
treatment['pref'] = 97640.7142857
treatment['tref'] = 287.6471952408
treatment['dt'] = 0.00666666666667
fwhbase = treatment.execute()

os.rename("fwh_result.h5", os.path.join('OUTPUT', "fwh_result.h5"))

Post-treatment example

"""

Post-Treatment script for a FW-H solver output database

input: FW-H database
output: raw signal, RMS, SPL, OASPL

"""

# -------------------------------------------------------------- #
# Import modules
# -------------------------------------------------------------- #

import os
if not os.path.isdir('OUTPUT'):
    os.makedirs('OUTPUT')

import numpy as np
from antares import *
from antares.utils.Signal import *

# -------------------------------------------------------------- #
# Input parameters
# -------------------------------------------------------------- #

# Input path
IN_PATH = 'OUTPUT' + os.sep
INPUT_FILE = ['fwh_result.h5']
# Can sum different surface if needed, then give a list
# INPUT_FILE = ['surface1.h5', 'surface2.h5', 'surface3.h5']

# Output path
OUT_PATH = 'OUTPUT' + os.sep
# output name extension
NAME = 'FWH_'
# output name extension
EXT = 'CLOSED'

# Azimuthal Average
AZIM = False

# Convert in dB
LOG_OUT = False

# Keep the largest convective time (azimuthal mean should not work)
MAX_CONV = False

# Extract converged pressure signal in separate files
RAW = False

# Extract R.M.S from raw pressure fluctuation (only if AZIM=False)
RMS = False

# Compute Third-Octave
ThirdOctave = False

# PSD parameter
PSD_Block = 1
PSD_Overlap = 0.6
PSD_Pad = 1

# smoothing filter for psd (Only for a broadband noise)
Smooth = False
# 1: mean around a frequency, 2: convolution
type_smooth = 1
# percent of frequency to filter
alpha = 0.08

# filter initial signal (if frequency = -1.0 no filtering)
Filter = False
Lowfreq = -1.0
Highfreq = -1.0

# downsampling signal if necessary
downsampling = False
# 1: order 8 Chebyshev type I filter, 2: fourier method filter, 3: Gaussian 3 points filter
type_down = 3
# downsampling factor
step = 3

# frequency to keep for OASPL (if not all, give a range [100, 1000] Hz)
FREQ_RANGE = 'all'

# Observers informations
# number of obs. in azimuthal direction
NB_AZIM = 1
# Angle between two microphone in  the streamwise direction
dTH = 90.
# Minimum and maximum angle of observers in the streamwise direction
a_min = 0.0
a_max = 270.0
# observers
observers = np.arange(a_min, a_max+dTH, dTH)
nb_observer = len(observers)*NB_AZIM

# -------------------------------------------------------------- #
# Script template
# -------------------------------------------------------------- #

print(' \n>>> -----------------------------------------------')
print(' >>>        Post-processing of FW-H results')
print(' >>> -----------------------------------------------')

nbfile = len(INPUT_FILE)
PSD = [PSD_Block, PSD_Overlap, PSD_Pad]
FILTER = [Filter, Lowfreq, Highfreq, Smooth, type_smooth, alpha, downsampling, type_down, step]
obs_info = [nb_observer, NB_AZIM, dTH, observers]
outfile = [OUT_PATH, NAME, EXT]

spl_datafile, oaspl_datafile, oaspl_raw_datafile, raw_datafile = build_outputfile_name(outfile, FILTER, AZIM, LOG_OUT)

if nbfile == 1:
    r = Reader('hdf_antares')
    r['filename'] = IN_PATH+INPUT_FILE[0]
    base = r.read()
else:
    r = Reader('hdf_antares')
    r['filename'] = IN_PATH+INPUT_FILE[0]
    base = r.read()
    for file in INPUT_FILE[1:]:

        r = Reader('hdf_antares')
        r['filename'] = IN_PATH+file
        base2add = r.read()

        # Sum signal
        #  + CONV=True: only the common converged part of each surfaces is keep
        #  + CONV=False: Keep all the converged part
        base = AddBase2(base, base2add, CONV=True)

# Keep only converged signal
#   + if MAX_CONV = False the signal will be truncated to the common converged part between each observers
base, observers_name = prepare_data(base, obs_info, MAX_CONV=True)

# Compute SPL and OASPL
# OUT_BASE
SPL, OASPL, OASPL_RAW, RAW, SPL_BAND = compute_FWH(base, obs_info, PSD, FILTER,
                                                   MAX_CONV, LOG_OUT,
                                                   FREQ_RANGE,
                                                   RAW_DATA=RAW,
                                                   AZIMUTHAL_MEAN=AZIM,
                                                   CHECK_RMS=RMS,
                                                   BAND=ThirdOctave)

# Checking if the output_path exists
OUT_PATH = '.' + os.sep
if not os.path.exists(OUT_PATH):
    os.makedirs(OUT_PATH)

# write result
w = Writer('column')
w['base'] = SPL
w['filename'] = spl_datafile
w.dump()

w = Writer('column')
w['base'] = OASPL
w['filename'] = oaspl_datafile
w.dump()

if OASPL_RAW is not None:
    w = Writer('column')
    w['base'] = OASPL_RAW
    w['filename'] = oaspl_raw_datafile
    w.dump()

if RAW is not None:
    w = Writer('column')
    w['base'] = RAW
    w['filename'] = raw_datafile
    w.dump()

if SPL_BAND is not None:
    index = spl_datafile.find('.dat')
    spl_third_octave = spl_datafile[:index] + '_third_octave' + spl_datafile[index:]
    w = Writer('column')
    w['base'] = SPL_BAND
    w['filename'] = spl_third_octave
    w.dump()