How to post-process simulation data

In this example we compute the scattering through an orifice plate in a circular duct with flow. The data is extracted from two Comsol Multiphysics simulations with a similar setup as in this study. The geometry is the same as in this study.

auto_examples/../../image/orifice.png

1. Initialization

First, we import the packages needed for this example.

import numpy
import matplotlib.pyplot as plt
import acdecom

The orifice is mounted inside a circular test duct with a radius of 15 mm. The highest frequency of interest is 4000 Hz. The bulk Mach-number is 0.042 and the temperature is 295 Kelvin.

section = "circular"
r = 0.015  # m
f_max = 4000  # Hz
M = 0.042
t = 295  # Kelvin

The graphic in the beginning of this examples shows a snapshot of the pressure field from the simulation. The “bubbles” downstream from the orifice plate are the vortex shed by the acoustic field. When we setup the decomposition domain, we want to be outside the shedding region, to avoid detecting artificial modes that do not belong to the acoustic field. Therefore, we will only use pressure values that are at least 10 duct diameters away from the orifice.

distance = 20.*r  # m

To analyze the measurement data, we create objects for the US and the DS test ducts.

td_US = acdecom.WaveGuide(dimensions=(r, ), cross_section=section, f_max=f_max, damping="no",
                          M=M, temperature=t, flip_flow=True)
td_DS = acdecom.WaveGuide(dimensions=(r, ), cross_section=section, f_max=f_max, damping="no",
                          M=M, temperature=t)

Note

The standard flow direction is in \(P_+\) direction. On the inlet side, the Mach-number therefore must be either set negative or the argument flipFlow must be set to True.

Note

We use no dissipation model. In the numerical simulation, we have turned off the boundary layer effects and we do not need to take them into account for our post-processing.

2. Data Preparation

We will post-process the direct data output using Comsol Multiphysics. The solution is two-dimensional and axi-symmetric. Using Comsol, we exported the two files comsolOrificeCase1.txt and comsolOrificeCase2.txt. They contain the same geometry. However, the orifice was either excited with an acoustic plane wave from the upstream direction or from the downstream direction. We will post-process both files in order to extract the sound scattering in both directions.

First, we print the header of the files to get a cleaner view of their structure.

with open("data/comsolOrifice1.txt","r") as pressurefile:
    for i in range(11):
        print(pressurefile.readline())

Out:

% Model:              orjfjceWhjstljng2D.mph

% Versjon:            COMSOL 5.5.0.306

% Date:               Jul 23 2020, 09:21

% Djmensjon:          2

% Nodes:              10000

% Expressjons:        71

% Descrjptjon:        Pressure

% Length unjt:        m

% r                       z                        p2 (Pa) @ freq=500                                p2 (Pa) @ freq=550                                p2 (Pa) @ freq=600                                p2 (Pa) @ freq=650                                p2 (Pa) @ freq=700                                p2 (Pa) @ freq=750                                p2 (Pa) @ freq=800                                p2 (Pa) @ freq=850                                p2 (Pa) @ freq=900                                p2 (Pa) @ freq=950                                p2 (Pa) @ freq=1000                               p2 (Pa) @ freq=1050                               p2 (Pa) @ freq=1100                               p2 (Pa) @ freq=1150                               p2 (Pa) @ freq=1200                               p2 (Pa) @ freq=1250                               p2 (Pa) @ freq=1300                               p2 (Pa) @ freq=1350                               p2 (Pa) @ freq=1400                               p2 (Pa) @ freq=1450                               p2 (Pa) @ freq=1500                               p2 (Pa) @ freq=1550                               p2 (Pa) @ freq=1600                               p2 (Pa) @ freq=1650                               p2 (Pa) @ freq=1700                               p2 (Pa) @ freq=1750                               p2 (Pa) @ freq=1800                               p2 (Pa) @ freq=1850                               p2 (Pa) @ freq=1900                               p2 (Pa) @ freq=1950                               p2 (Pa) @ freq=2000                               p2 (Pa) @ freq=2050                               p2 (Pa) @ freq=2100                               p2 (Pa) @ freq=2150                               p2 (Pa) @ freq=2200                               p2 (Pa) @ freq=2250                               p2 (Pa) @ freq=2300                               p2 (Pa) @ freq=2350                               p2 (Pa) @ freq=2400                               p2 (Pa) @ freq=2450                               p2 (Pa) @ freq=2500                               p2 (Pa) @ freq=2550                               p2 (Pa) @ freq=2600                               p2 (Pa) @ freq=2650                               p2 (Pa) @ freq=2700                               p2 (Pa) @ freq=2750                               p2 (Pa) @ freq=2800                               p2 (Pa) @ freq=2850                               p2 (Pa) @ freq=2900                               p2 (Pa) @ freq=2950                               p2 (Pa) @ freq=3000                               p2 (Pa) @ freq=3050                               p2 (Pa) @ freq=3100                               p2 (Pa) @ freq=3150                               p2 (Pa) @ freq=3200                               p2 (Pa) @ freq=3250                               p2 (Pa) @ freq=3300                               p2 (Pa) @ freq=3350                               p2 (Pa) @ freq=3400                               p2 (Pa) @ freq=3450                               p2 (Pa) @ freq=3500                               p2 (Pa) @ freq=3550                               p2 (Pa) @ freq=3600                               p2 (Pa) @ freq=3650                               p2 (Pa) @ freq=3700                               p2 (Pa) @ freq=3750                               p2 (Pa) @ freq=3800                               p2 (Pa) @ freq=3850                               p2 (Pa) @ freq=3900                               p2 (Pa) @ freq=3950                               p2 (Pa) @ freq=4000

7.500000000000002E-4      -0.59895045              -6.594015731195159E-4-9.964393396585407E-4j       -0.0011860537981138028+2.0822981814437445E-4j     -2.812573635939459E-4+0.0011749602856567277j      9.65163291498519E-4+7.295779500173048E-4j         0.0010579064704968448-5.915102194563302E-4j       -1.1299633810741019E-4-0.0012110236740786606j     -0.0011644015797601338-3.8728480818294044E-4j     -8.210587933093777E-4+9.209466744689457E-4j       5.180658103629975E-4+0.0011268455748456206j       0.0012409686455777142-3.272963654214609E-5j       4.740400915810269E-4-0.0011390373155900377j       -8.450716377997362E-4-8.995013121466687E-4j       -0.001169599847273644+4.0110425814857635E-4j      -1.3039831303748967E-4+0.0012465289850809096j     0.0010961551517094925+6.33386654213946E-4j        0.0010449437044638252-7.41766012782377E-4j        -2.3672338342449196E-4-0.0012808716360936038j     -0.0012987480945725624-3.454406262477366E-4j      -9.227312833426118E-4+0.0010964143515957135j      6.862927003256785E-4+0.0013871489725009168j       0.0016607944827237197-1.295756102817592E-4j       4.919128462650932E-4-0.00169648915713534j         -0.0014688616953822992-0.0010823016057667663j     -0.001568971916333237+0.00100217299216616j        3.7564168773138423E-4+0.0018767549272077574j      0.0019516330154208338+3.255542912689751E-4j       9.857703509767491E-4-0.001785248765610653j        -0.001371365248556543-0.0015055457972722754j      -0.0018276254989431557+7.696057665401723E-4j      6.550840143500225E-5+0.0019071981064098945j       0.0017413207893079527+6.364522425065958E-4j       0.0012280725339732749-0.0013566148788370853j      -8.015001573148758E-4-0.0016254122041068062j      -0.0017799442549510831+1.5785775219420768E-4j     -4.8454594201322975E-4+0.0016792859949273694j     0.0013495732542571244+0.001029970253953009j       0.001405996254230656-8.452371419258992E-4j        -2.5203561266966864E-4-0.001567797361693215j      -0.0014983420558976558-3.340089694426043E-4j      -8.257257002655388E-4+0.0012154454801489233j      7.712479784668012E-4+0.00115740452476719j         0.0012876952843918889-2.4592391686245347E-4j      2.742755753953378E-4-0.0012050110194263088j       -9.348915082832261E-4-7.09317490721921E-4j        -9.93247241574712E-4+5.319373712666111E-4j        6.915017202975234E-5+0.0010874313485564335j       9.857681005918568E-4+3.752915489932123E-4j        7.275117798349058E-4-7.167475285822072E-4j        -3.3367704998420904E-4-9.310611528533997E-4j      -9.572825706231807E-4-9.228772847174834E-5j       -4.847697719494012E-4+8.071131767974492E-4j       5.121672883921741E-4+7.76492695449235E-4j         9.179860936588964E-4-1.284257113454432E-4j        2.75136841222777E-4-8.86756471068437E-4j          -6.916919435819609E-4-6.272952084798296E-4j       -8.659481817208404E-4+3.6987286708238824E-4j      -2.042958072472771E-5+9.489871539630312E-4j       8.620807973518022E-4+4.1000280423645236E-4j       7.297379831001061E-4-6.202347678876438E-4j        -2.6537801623804935E-4-9.232939138473764E-4j      -9.554864208060885E-4-1.410769577346627E-4j       -5.294939363672612E-4+8.1919862519288E-4j         5.371274837476116E-4+8.331122667556481E-4j        9.980645644894418E-4-1.5671397363416304E-4j       2.586716091031291E-4-9.948264845250757E-4j        -8.225210499320335E-4-6.390812021337053E-4j       -9.198072939928214E-4+5.099500924965665E-4j       1.0861720927420949E-4+0.001051845546824562j       0.0010120857110790396+3.143191525288762E-4j       6.875837007028902E-4-8.059670838888746E-4j        -4.671443879336398E-4-9.475783524930404E-4j

0.0022500000000000003     -0.59895045              -6.598115227077373E-4-9.966036279599882E-4j       -0.001186282718359856+2.0866397903526702E-4j      -2.8111675933929363E-4+0.0011751987633198313j     9.652398183496232E-4+7.291329745029828E-4j        0.001057923729561758-5.912473327832849E-4j        -1.1326562504414308E-4-0.001211984191634732j      -0.0011653792383793929-3.868434198036737E-4j      -8.238066406174163E-4+9.218457046765307E-4j       5.201400917075564E-4+0.0011263736961986612j       0.0012381928192308606-3.0203811357715177E-5j      4.725440742955169E-4-0.0011381783230260728j       -8.440197961507028E-4-8.98211707781202E-4j        -0.0011704441414358332+3.9915894765788043E-4j     -1.2447948092705977E-4+0.0012431724035378059j     0.0010941192470542518+6.332820372351979E-4j       0.0010450328454385581-7.39583078427309E-4j        -2.3277848835822907E-4-0.0012845399824699616j     -0.0013034745926918823-3.521620533863789E-4j      -9.254949007485222E-4+0.001095703319259605j       6.85619859634462E-4+0.0013902329999068749j        0.001664168514639363-1.2866300962543247E-4j       4.931251526764727E-4-0.0017001298470275474j       -0.0014726758946456658-0.0010838771234689733j     -0.0015710317224750352+0.0010060500868684095j     3.7951733116016467E-4+0.0018793774529327807j      0.0019548210214740064+3.217641649940683E-4j       9.821488423463016E-4-0.0017888990613899882j       -0.0013752913594157722-0.0015023024663686048j     -0.001824993783673856+7.736873527617381E-4j       6.965138246314807E-5+0.001905357699627487j        0.001740344689598924+6.323291627693415E-4j        0.0012240891908249973-0.0013564641818033009j      -8.020846770305993E-4-0.0016217373396053914j      -0.0017767433088535477+1.590587263955981E-4j      -4.828565683612734E-4+0.001676694781004929j       0.001347668543045332+0.0010279374885355666j       0.0014037664128551926-8.440412408293864E-4j       -2.515063082365174E-4-0.0015655089705026788j      -0.001496127636736117-3.3396479775920545E-4j      -8.25225552293132E-4+0.0012134217677317572j       7.694992985756365E-4+0.0011565658802350249j       0.0012866293333335219-2.444889009042483E-4j       2.753948670858984E-4-0.001203815066077736j        -9.336367086660469E-4-7.101497966353269E-4j       -9.93843423941002E-4+5.30671498265087E-4j         6.790079491545605E-5+0.001087846790556794j        9.86045169783435E-4+3.765149082217435E-4j         7.287156555458019E-4-7.169101458836145E-4j        -3.3372744919464884E-4-9.322578087965604E-4j      -9.584832543674161E-4-9.236647436104054E-5j       -4.850057752778102E-4+8.083182216298214E-4j       5.133614902732322E-4+7.769177494798084E-4j        9.186235670945579E-4-1.2957622500132352E-4j       2.7407994082416997E-4-8.876143318356613E-4j       -6.92758352741272E-4-6.263898525091252E-4j        -8.65249732227945E-4+3.711114876052003E-4j        -1.9079356364642515E-5+9.485396673479142E-4j      8.619095645174047E-4+4.086185353649782E-4j        7.284043167159529E-4-6.20339701031345E-4j         -2.6573130198694647E-4-9.220899774393541E-4j      -9.544752497635772E-4-1.4053002986854214E-4j      -5.288276006638048E-4+8.184181638180472E-4j       5.365840898822465E-4+8.324108363466979E-4j        9.974122222583238E-4-1.5638317717412602E-4j       2.5883947790036606E-4-9.94292816627131E-4j        -8.221497702184529E-4-6.391541867643751E-4j       -9.198623231818279E-4+5.097527154440146E-4j       1.0857274989180938E-4+0.0010519562582322634j      0.0010123109841390409+3.142604573046745E-4j       6.874917145292474E-4-8.063419543968825E-4j        -4.67674467303052E-4-9.475314950079873E-4j

There are a few header lines starting with %, followed by the pressure data for different frequencies. The first and second columns are the r and z coordinates of the grid points. The remaining columns hold the pressure at those positions for different frequencies. The frequencies range from 500 Hz to 4000 Hz in steps of 50 Hz. We can create variables that will be useful later in the study.

z_col = 1
r_col = 0
f = numpy.arange(500,4001,50)
header = "%"

Note

There is no column for the circumferential coordinate. The reason is, that we did a two dimensional, axi-symmetric simulation that does not require a circumferential coordinate.

We read both simulation files.

pressure_1 = numpy.loadtxt("data/comsolOrifice1.txt", dtype=complex, comments=header)
pressure_2 = numpy.loadtxt("data/comsolOrifice2.txt", dtype=complex, comments=header)

We delete all positions that are too close to the orifice plate. Furthermore, we split the simulation in upstream (negative z) and downstream (positive z) sides.

pressure_1us = pressure_1[pressure_1[:, z_col] < -distance, :]
pressure_1ds = pressure_1[pressure_1[:, z_col] > distance, :]

pressure_2us = pressure_2[pressure_2[:, z_col] < -distance, :]
pressure_2ds = pressure_2[pressure_2[:, z_col] > distance, :]

We can check how many grid points we have on the upstream and the downstream side.

print("Probes on US side ", pressure_1us.shape[0], ". Probes on DS side: ", pressure_1ds.shape[0])

Out:

Probes on US side  2500 . Probes on DS side:  2500

Generally, we can use all grid points for post processing. However, this is not the best method if we have a large grid with many points. Instead, we will use a random choice of points.

number_of_probes = 100
mics_rows_US = numpy.random.choice(pressure_1us.shape[0], size=number_of_probes, replace=False)
mics_rows_DS = numpy.random.choice(pressure_1ds.shape[0], size=number_of_probes, replace=False)

We extract the coordinates of the random grid points.

z_DS = numpy.abs(pressure_1ds[mics_rows_DS, z_col])  # m
r_DS = numpy.abs(pressure_1ds[mics_rows_DS, r_col])   # m
phi_DS = numpy.zeros((number_of_probes, ))  # deg

z_US = numpy.abs(pressure_1us[mics_rows_US, z_col])  # m
r_US = numpy.abs(pressure_1us[mics_rows_US, r_col])   # m
phi_US = numpy.zeros((number_of_probes, ))  # deg

We assign the random grid points as microphones to the object.

td_US.set_microphone_positions(z_US, r_US, phi_US)
td_DS.set_microphone_positions(z_DS, r_DS, phi_DS)

In order to decompose the simulation data, we must express it in a format that can be processed by WaveGuide.decompose().

First, we remove all grid points except the random points that we have chosen for the decomposition. Furthermore, we remove the two columns that contain the coordinates of the grid points.

pressure_1us = numpy.delete(pressure_1us[mics_rows_US, :], [z_col,r_col], axis=1)
pressure_1ds = numpy.delete(pressure_1ds[mics_rows_DS, :], [z_col,r_col], axis=1)

pressure_2us = numpy.delete(pressure_2us[mics_rows_US, :], [z_col,r_col], axis=1)
pressure_2ds = numpy.delete(pressure_2ds[mics_rows_DS, :], [z_col,r_col], axis=1)

Next, we add a new row that contains the frequencies (similar to the line in the header of our file), and one more that contains the test case number. This row is has the value 0 for case 1, and 1 for case 2. Subsequently, we transpose the array, to have the data format required by WaveGuide.decompose().

pressure_1us = numpy.vstack([pressure_1us, f, numpy.zeros((f.shape[0],))]).T
pressure_2us = numpy.vstack([pressure_2us, f, numpy.ones((f.shape[0],))]).T

pressure_1ds = numpy.vstack([pressure_1ds, f, numpy.zeros((f.shape[0],))]).T
pressure_2ds = numpy.vstack([pressure_2ds, f, numpy.ones((f.shape[0],))]).T

Finally, we combine the two cases at the upstream and the downstream sides to create two large data sets.

pressure_US = numpy.vstack([pressure_1us, pressure_2us])
pressure_DS = numpy.vstack([pressure_1ds, pressure_2ds])

The pressure at the different grid points is stored in the first number_of_probes columns. The frequency is stored in the second last column; and the case number is stored in the last column. We create variables that we will use later in this study.

mic_col = range(0, number_of_probes)
frequ_col = -2
case_col = -1

3. Decomposition

With the pre-processed data, we can run the decomposition.

decomp_us, headers_us = td_US.decompose(pressure_US, frequ_col, mic_col, case_col=case_col)

decomp_DS, headers_DS = td_DS.decompose(pressure_DS, frequ_col, mic_col, case_col=case_col)

4. Further Post-processing

We can print the headersDS to see the names of the columns of the arrays that store the decomposed sound fields.

print(headers_us)

Out:

['(0,0) plus Direction', '(0,0) minus Direction', 'f', 'Mach_number', 'temperature', 'Ps', 'condition number', 'case']

We use that information to extract the modal data.

minusmodes = [1]  # from headers_us
plusmodes = [0]

Furthermore, we acquire the unique decomposed frequency points.

frequs = numpy.abs(numpy.unique(decomp_us[:, headers_us.index("f")]))
nof = frequs.shape[0]

For each of the frequencies we compute the scattering matrix by solving a linear system of equations \(S = p_+ p_-^{-1}\), wherein \(S\) is the scattering matrix and \(p_{\pm}\) are matrices containing the acoustic modes that are placed in rows and the different test cases that are placed in columns.

Note

Details for the computation of the Scattering Matrix and the procedure to measure the different test-cases can be found in this study.

S = numpy.zeros((2,2,nof),dtype = complex)

for fIndx, f in enumerate(frequs):
    frequ_rows = numpy.where(decomp_us[:, headers_us.index("f")] == f)
    ppm_us = decomp_us[frequ_rows]
    ppm_DS = decomp_DS[frequ_rows]
    pp = numpy.concatenate((ppm_us[:,plusmodes].T, ppm_DS[:,plusmodes].T))
    pm = numpy.concatenate((ppm_us[:,minusmodes].T, ppm_DS[:,minusmodes].T))
    S[:,:,fIndx] = numpy.dot(pp, numpy.linalg.pinv(pm))

5. Plot

We can plot the transmission coefficients. Transmission coefficients higher than 1 indicate frequencies where amplification can occur.

amplification_us = numpy.abs(S[1, 0, :]) > 1
amplification_ds = numpy.abs(S[0, 1, :]) > 1

plt.hlines(1, frequs[0], frequs[-1], linestyles="dashed", color="grey")

plt.plot(frequs, numpy.abs(S[1, 0, :]),
         ls="-", color="#D38D7B", alpha=0.5)
plt.plot(frequs, numpy.abs(S[0, 1, :]),
         ls="-", color="#67A3C1", alpha=0.5)

plt.plot(frequs[amplification_us], numpy.abs(S[1, 0, amplification_us]),
         ls="-", color="#D38D7B", label="Transmission Upstream")
plt.plot(frequs[amplification_ds], numpy.abs(S[0, 1, amplification_ds]),
         ls="-", color="#67A3C1", label="Transmission Downstream")

plt.xlabel("Frequency [Hz]")
plt.ylabel("Scattering Magnitude")
plt.title("Sound transmission including absorption and amplification at \n an orifice plate with flow.")

plt.legend()
plt.show()
Sound transmission including absorption and amplification at   an orifice plate with flow.

Total running time of the script: ( 0 minutes 3.719 seconds)

Gallery generated by Sphinx-Gallery