diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index 4f0a30f..dca8c4e 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -3,50 +3,24 @@ name: Publish to PyPI on: push jobs: - build: - name: Build distribution 📦 + deploy: runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: "3.x" - - name: Install pypa/build - run: >- - python3 -m - pip install - build - --user - - name: Build a binary wheel and a source tarball - run: python3 -m build - - name: Store the distribution packages - uses: actions/upload-artifact@v3 - with: - name: python-package-distributions - path: dist/ - - publish-to-pypi: - name: >- - Publish to PyPI - if: startsWith(github.ref, 'refs/tags/') # only publish to PyPI on tag pushes - needs: - - build - runs-on: ubuntu-latest - environment: - name: pypi - url: https://pypi.org/p/ # Replace with your PyPI project name - permissions: - username: __token__ - password: ${{ secrets.PYPI_TOKEN }} - steps: - - name: Download all the dists - uses: actions/download-artifact@v3 - with: - name: python-package-distributions - path: dist/ - - name: Publish to PyPI - uses: pypa/gh-action-pypi-publish@release/v1 + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.x' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install -r requirements + pip install build + - name: Build package + run: python -m build + - name: Publish package + uses: pypa/gh-action-pypi-publish@v1 + with: + user: __token__ + password: ${{ secrets.PYPI_PASSWORD }} diff --git a/.gitignore b/.gitignore index a788232..348ae5c 100644 --- a/.gitignore +++ b/.gitignore @@ -68,8 +68,6 @@ instance/ # Scrapy stuff: .scrapy -# Sphinx documentation -docs/_build/ # PyBuilder target/ @@ -127,5 +125,3 @@ dmypy.json # Pyre type checker .pyre/ - -docs/ \ No newline at end of file diff --git a/docs/AcoustiBase.md b/docs/AcoustiBase.md new file mode 100644 index 0000000..7fd580a --- /dev/null +++ b/docs/AcoustiBase.md @@ -0,0 +1,2 @@ +# AcoustiBase +::: src.acoustipy.Database \ No newline at end of file diff --git a/docs/AcousticID.md b/docs/AcousticID.md new file mode 100644 index 0000000..2afa533 --- /dev/null +++ b/docs/AcousticID.md @@ -0,0 +1,2 @@ +# AcousticID +::: src.acoustipy.Params \ No newline at end of file diff --git a/docs/AcousticTMM.md b/docs/AcousticTMM.md new file mode 100644 index 0000000..b204da4 --- /dev/null +++ b/docs/AcousticTMM.md @@ -0,0 +1,3 @@ +# AcousticTMM +::: src.acoustipy.TMM + handler: python \ No newline at end of file diff --git a/docs/Examples/Hybrid.md b/docs/Examples/Hybrid.md new file mode 100644 index 0000000..ad554b7 --- /dev/null +++ b/docs/Examples/Hybrid.md @@ -0,0 +1,48 @@ +# Hybrid Characterization +Use a hybrid characterization method to identify parameters of the Johnson-Champoux-Allard model given data obtained from an impedance tube. + +```python +from acoustipy import AcousticTMM, AcousticID + +# Create an AcousticTMM object to generate toy impedance tube data +structure = AcousticTMM(incidence='Normal',air_temperature = 20) + +# Define the JCA and air gap material parameters for the toy data +layer1 = structure.Add_JCA_Layer(30, 46182,.917,2.1,83,128) +air = structure.Add_Air_Layer(thickness = 100) + +# Generate rigid backed reflection data and save to a csv file +s1 = structure.assemble_structure(layer1) +A1 = structure.reflection(s1) +structure.to_csv('no_gap',A1) + +# Generate air backed reflection data and save to a csv file +s2 = structure.assemble_structure(layer1,air) +A2 = structure.reflection(s2) +structure.to_csv('gap',A2) + +# Create an AcousticID object, specifying to mount types, data files, and data types +inv = AcousticID(mount_type='Dual',no_gap_file="no_gap.csv",gap_file = "gap.csv",input_type='reflection',air_temperature=20) + +# Call the Hybrid method to find the tortuosity, viscous, and thermal characteristic lengths of the material +res = inv.Hybrid(30,.917,air_gap=95,uncertainty = .15) + +# print the identified parameters +print(res) + +# Plot the absorption curves using the actual parameters vs those identified from the Indirect method +inv.plot_comparison(res) + +res >>> {'thickness': 29.982, + 'flow resistivity': 46209.81, + 'porosity': 0.918, + 'tortuosity': 2.103, + 'viscous characteristic length': 83.0, + 'thermal characteristic length': 128.0, + 'air gap': 100.0, + 'error': 1.3018228618069009e-17} +``` + +

+ +![](../assets/ex_material_identification_hybrid.png) \ No newline at end of file diff --git a/docs/Examples/Indirect.md b/docs/Examples/Indirect.md new file mode 100644 index 0000000..b1f9f26 --- /dev/null +++ b/docs/Examples/Indirect.md @@ -0,0 +1,48 @@ +# Indirect Characterization +Use an indirect characterization method to identify parameters of the Johnson-Champaux-Allard model given data obtained from an impedance tube. + +```python +from acoustipy import AcousticTMM, AcousticID + +# Create an AcousticTMM object to generate toy impedance tube data +structure = AcousticTMM(incidence='Normal',air_temperature = 20) + +# Define the JCA and air gap material parameters for the toy data +layer1 = structure.Add_JCA_Layer(30, 46182,.917,2.1,83,128) +air = structure.Add_Air_Layer(thickness = 100) + +# Generate rigid backed reflection data and save to a csv file +s1 = structure.assemble_structure(layer1) +A1 = structure.reflection(s1) +structure.to_csv('no_gap',A1) + +# Generate air backed reflection data and save to a csv file +s2 = structure.assemble_structure(layer1,air) +A2 = structure.reflection(s2) +structure.to_csv('gap',A2) + +# Create an AcousticID object, specifying to mount types, data files, and data types +inv = AcousticID(mount_type='Dual',no_gap_file="no_gap.csv",gap_file = "gap.csv",air_temperature=20,input_type='reflection') + +# Call the Indirect method to find the flow resistivity, tortuosity, and viscous, and thermal characteristic lengths of the material +res = inv.Indirect(30,.917,air_gap=100) + +# Plot the absorption curves using the actual parameters vs those identified from the Indirect method +inv.plot_comparison(res) + +# print the identified parameters +print(res) + +res >> {'thickness': 30, + 'flow resistivity': 46182.79193470273, + 'porosity': 0.9326784023844896, + 'tortuosity': 2.1373594528548225, + 'viscous characteristic length': 83.16500367154856, + 'thermal characteristic length': 115.12539138619321, + 'air gap': 100} + +``` + +

+ +![](../assets/ex_material_identification_indirect.png) \ No newline at end of file diff --git a/docs/Examples/Inverse.md b/docs/Examples/Inverse.md new file mode 100644 index 0000000..65215e4 --- /dev/null +++ b/docs/Examples/Inverse.md @@ -0,0 +1,45 @@ +# Inverse JCA Characterization +Use an inverse characterization method to identify parameters of the Johnson-Champaux-Allard model given data obtained from an impedance tube. + +```python +from acoustipy import acousticTMM, AcousticID + +# Create an AcousticTMM object to generate toy impedance tube data +structure = acousticTMM(incidence='Normal',air_temperature = 20) + +# Define the JCA and air gap material parameters for the toy data +layer1 = structure.Add_JCA_Layer(thickness = 30, flow_resistivity = 46879, porosity = .93, tortuosity = 1.7, viscous_characteristic_length = 80, thermal_characteristic_length = 105) +air = structure.Add_Air_Layer(thickness = 375) + +# Generate rigid backed absorption data and save to a csv file +s1 = structure.assemble_structure(layer1) +A1 = structure.absorption(s1) +structure.to_csv('no_gap',A1) + +# Generate air backed absorption data and save to a csv file +s2 = structure.assemble_structure(layer1,air) +A2 = structure.absorption(s2) +structure.to_csv('gap',A2) + +# Create an AcousticID object, specifying to mount types, data files, and data types +inv = AcousticID(mount_type='Dual',no_gap_file="no_gap.csv", gap_file = 'gap.csv',air_temperature=20,input_type='absorption') + +# Call the Inverse method to find the tortuosity, viscous, and thermal characteristic lengths of the material +res = inv.Inverse(30, 47000,.926,air_gap=375,uncertainty=.2,verbose=True) + +# Display summary statistics about the optimization +stats = inv.stats(res) +print(stats) + +# Plot the results of the found parameters compared to the toy input data +inv.plot_comparison(res) + +# Save the optimization results to a csv +inv.to_csv("params.csv",res) + +stats = {'slope': 1.000037058594857, 'intercept': 9.276088883464206e-05, 'r_value': 0.9999999674493408, 'p_value': 0.0, 'std_err': 8.732362148426126e-06} +``` + +

+ +![](../assets/ex_material_identification_inverse.png) \ No newline at end of file diff --git a/docs/Examples/impedance_tube.md b/docs/Examples/impedance_tube.md new file mode 100644 index 0000000..e1c046a --- /dev/null +++ b/docs/Examples/impedance_tube.md @@ -0,0 +1,69 @@ +# Using impedance tube data +Estimate the diffuse sound field absorption coefficients of a structure +using the normal incidence reflection coefficients obtained via impedance +tube. + +```python +from acoustipy import AcousticTMM + +# Generate synthetic "impedance tube" data +structure = AcousticTMM(incidence='Normal', air_temperature = 20) + +# Define the JCA and air gap material parameters for the synthetic data +layer1 = structure.Add_JCA_Layer(25.4, 60000, .91, 1.3, 85, 110) +air = structure.Add_Air_Layer(thickness = 100) + +# Generate rigid backed reflection data and save to a csv file +s1 = structure.assemble_structure(layer1) +r1 = structure.reflection(s1) +structure.to_csv('no_gap_tube',r1) + +# Generate air backed absorption data and save to a csv file +s2 = structure.assemble_structure(layer1,air) +r2 = structure.reflection(s2) +structure.to_csv('gap_tube',r2) + +# Create a new structure to estimate the diffuse field absorption coefficients +estimated_structure = AcousticTMM(incidence='Diffuse', air_temperature = 20) + +# Load the synthetic impedance tube data and define a new air layer +estimated_layer = estimated_structure.Add_Layer_From_Tube(no_gap_file = 'no_gap_tube.csv', gap_file='gap_tube.csv', sample_thickness=25.4, air_gap_thickness=100) +air2 = estimated_structure.Add_Air_Layer(thickness = 400) + +# Generate narrow band absorption data for the new structure +s3 = estimated_structure.assemble_structure(estimated_layer,air2) +absorption = structure.absorption(s3) + +# Calculate the 3rd octave bands absorption coefficients +bands = structure.octave_bands(absorption) + +# Calculate the four frequency average absorption +ffa_estimate = structure.FFA(bands) +print(ffa_estimate) + +>>> 0.717 + +# Compare the estimated FFA to that calculated from the original layer data +air3 = structure.Add_Air_Layer(thickness = 400) +s4 = structure.assemble_structure(layer1,air3) +abs1 = structure.absorption(s4) +bands1 = structure.octave_bands(abs1) +ffa_original = structure.FFA(bands1) +print(ffa_original) + +>>> 0.717 +``` + + + + + + + + + + + + + + diff --git a/docs/Examples/layer_to_database.md b/docs/Examples/layer_to_database.md new file mode 100644 index 0000000..68db385 --- /dev/null +++ b/docs/Examples/layer_to_database.md @@ -0,0 +1,26 @@ +# Saving layers to a database +Save the parameters of individual layers to a built-in SQLite database. + +```python +from acoustipy import AcousticTMM, AcoustiBase + + +structure = AcousticTMM(incidence='Normal',air_temperature = 20) + +# For each Add_XXX_Layer method, enable the save_layer parameter and give it a unique layer name +db = structure.Add_DB_Layer(25.4, 50000,save_layer=True,layer_name='test_DB') +dbm = structure.Add_DBM_Layer(25.4, 50000,save_layer=True,layer_name='test_DBM') +jca = structure.Add_JCA_Layer(25.4, 50000,.90,1.2,80,110,save_layer=True,layer_name='test_JCA') +jcal = structure.Add_JCAL_Layer(25.4, 50000,.90,1.2,80,110,50,save_layer=True,layer_name='test_JCAL') +jcapl = structure.Add_JCAPL_Layer(25.4, 50000, .90, 1.2, 80, 110, 50, 70, 65,save_layer=True,layer_name='test_JCAPL') +horoshenkov = structure.Add_Horoshenkov_Layer(40, .90, 147, .325, save_layer=True, layer_name='test_horoshenkov') +biot_limp = structure.Add_Biot_Limp_Layer('JCA',25.4, 50000,200, .90, 1.2, 80, 110,save_layer=True,layer_name='test_biot_limp') +biot_rigid = structure.Add_Biot_Rigid_Layer('JCA',25.4, 50000,200, .90, 1.2, 80, 110,save_layer=True,layer_name='test2_biot_rigid') +screen = structure.Add_Resistive_Screen(2.54, 50000, .90,save_layer=True,layer_name='test_screen') +maa = structure.Add_MAA_MPP_Layer(2.54, .5, 1,save_layer=True,layer_name='test_maa_mpp') +ef_mpp = structure.Add_MPP_EF_Layer(2.54, .5, 1,save_layer=True,layer_name='test_ef_mpp') + +# Pull the layer information from the database and save to a .csv file +s = AcoustiBase() +s.summarize_layers() +``` \ No newline at end of file diff --git a/docs/Examples/layers_from_database.md b/docs/Examples/layers_from_database.md new file mode 100644 index 0000000..d263749 --- /dev/null +++ b/docs/Examples/layers_from_database.md @@ -0,0 +1,32 @@ +# Load layers from a database +Load the previously saved layers from the database and calculate the transmission loss +of one of the layers. + +```python +from acoustipy import AcousticTMM + +structure = AcousticTMM(incidence='Normal',air_temperature = 20) + +# Add layers from the database +db = structure.Add_Layer_From_Database('test_DB') +dbm = structure.Add_Layer_From_Database('test_DBM') +jca = structure.Add_Layer_From_Database('test_JCA') +jcal = structure.Add_Layer_From_Database('test_JCAL') +jcapl = structure.Add_Layer_From_Database('test_JCAPL') +horoshenkov = structure.Add_Layer_From_Database('test_horoshenkov') +biot_limp = structure.Add_Layer_From_Database('test_biot_limp') +biot_rigid = structure.Add_Layer_From_Database('test2_biot_rigid') +screen = structure.Add_Layer_From_Database('test_screen') +maa = structure.Add_Layer_From_Database('test_maa_mpp') +ef_mpp = structure.Add_Layer_From_Database('test_ef_mpp') + +# Calculate th narrow band transmission coefficients for the JCA layer +s = structure.assemble_structure(jca) +tl = structure.transmission_loss(s) + +# Plot the transmission coefficients +structure.plot_curve([tl]) +``` +
+ +![](../assets/ex_layers_from_database.png) \ No newline at end of file diff --git a/docs/Examples/multilayer_structure.md b/docs/Examples/multilayer_structure.md new file mode 100644 index 0000000..b6b8d4a --- /dev/null +++ b/docs/Examples/multilayer_structure.md @@ -0,0 +1,38 @@ +# Characterize a multilayer structure + +Calculate the absorption coefficients of a multilayer structure and plot the narrow +and 3rd octave bands using matplotlib. + +```Python +from acoustipy import AcousticTMM + +# Create an AcousticTMM object, specifying a diffuse sound field at 20C +structure = AcousticTMM(incidence='Diffuse',air_temperature=20) + +# Define the layers of the material using various models +layer1 = structure.Add_Resistive_Screen(thickness=1,flow_resistivity=100000,porosity=.86) +layer2 = structure.Add_DBM_Layer(thickness = 25.4,flow_resistivity=60000) +layer3 = structure.Add_Resistive_Screen(thickness = 1, flow_resistivity=500000,porosity=.75) + +# Specify the material backing condition -- in this case a 400mm air gap +air = structure.Add_Air_Layer(thickness = 400) + +# Build the total transfer matrix of the structure + air gap +transfer_matrix = structure.assemble_structure(layer1,layer2,layer3,air) + +# Calculate the frequency dependent narrow band absorption coefficients +absorption = structure.absorption(transfer_matrix) + +# Calculate the 3rd octave bands absorption coefficients +bands = structure.octave_bands(absorption) + +# Calculate the four frequency average absorption +FFA = structure.FFA(bands) + +# Plot and display the narrow and 3rd band coefficients on the same figure +structure.plot_curve([absorption,bands],["absorption","third octave"]) +``` + +
+ +![](../assets/ex_multilayer_structure.png) \ No newline at end of file diff --git a/docs/Examples/structure_to_database.md b/docs/Examples/structure_to_database.md new file mode 100644 index 0000000..6b0cda9 --- /dev/null +++ b/docs/Examples/structure_to_database.md @@ -0,0 +1,35 @@ +# Saving structures to a database +Save a structure consisting of mulitple layers to a built-in SQLite database + +```python +from acoustipy import AcousticTMM, AcoustiBase + +s = AcousticTMM(incidence='Diffuse',air_temperature=20) + +# Define a arbitrary structure made of a porous DBM layer sandwhiched between 2 porous screens +layer1 = s.Add_Resistive_Screen(thickness=1,flow_resistivity=100000,porosity=.86,save_layer=True,layer_name='test_screen1') +layer2 = s.Add_DBM_Layer(thickness = 25.4,flow_resistivity=60000,save_layer=True,layer_name='test_DBM') +layer3 = s.Add_Resistive_Screen(thickness = 1, flow_resistivity=500000,porosity=.75,save_layer=True,layer_name='test_screen2') +air = s.Add_Air_Layer(thickness = 400,save_layer=True,layer_name='test_AIR') + +# Save the structure to the database +structure = s.assemble_structure(layer1,layer2,layer3,air,save_structure=True,structure_name='test') + +# Load the structure from the database +s2 = s.assemble_from_database('test') + +# Compare the narrow band absorption curves from the original structure and the one loaded from the database +abs1 = s.absorption(structure) +absorption = s.absorption(s2) + +s.plot_curve([absorption,abs1],['From Database','Original']) + +# Pull the information from the database and save to .csv files +st = AcoustiBase() +st.summarize_layers() +st.summarize_structures() +``` + +
+ +![](../assets/ex_structure_to_database.png) \ No newline at end of file diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..a15a081 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,103 @@ +Investigate and optimize the acoustic performance of porous and microperforate materials with acoustipy. Use the acoustic transfer matrix method to explore new material designs and identify unique properties of existing materials via inverse, indirect, and hybrid optimization schemes. +

+ +# Installation + + +### (recommended) Create and activate a new virtual environment + mkdir + python -m venv .venv + cd .venv + (Windows) cd Scripts && activate.bat + (Linux) source bin/activate + +### Install from PyPI + pip install acoustipy + +# Basic Usage +Examples of most of the functionality of acoustipy can be found in the Examples section. The snippet below corresponds to the multilayer structure example and highlights a core feature of acoustipy -- the acoustic transfer matrix method. +

+ + + + +```Python +from acoustipy import acousticTMM + +# Create an AcousticTMM object, specifying a diffuse sound field at 20C +structure = AcousticTMM(incidence='Diffuse',air_temperature=20) + +# Define the layers of the material using various models +layer1 = structure.Add_Resistive_Screen(thickness=1,flow_resistivity=100000,porosity=.86) +layer2 = structure.Add_DBM_Layer(thickness = 25.4,flow_resistivity=60000) +layer3 = structure.Add_Resistive_Screen(thickness = 1, flow_resistivity=500000,porosity=.75) + +# Specify the material backing condition -- in this case a 400mm air gap +air = structure.Add_Air_Layer(thickness = 400) + +# Build the total transfer matrix of the structure + air gap +transfer_matrix = structure.assemble_structure(layer1,layer2,layer3,air) + +# Calculate the frequency dependent narrow band absorption coefficients +absorption = structure.absorption(transfer_matrix) + +# Calculate the 3rd octave bands absorption coefficients +bands = structure.octave_bands(absorption) + +# Calculate the four frequency average absorption +FFA = structure.FFA(bands) + +# Plot and display the narrow and 3rd band coefficients on the same figure +structure.plot_curve([absorption,bands],["absorption","third octave"]) +``` + +
+ +![](assets/ex_multilayer_structure.png) + +
+The example below demonstrates another core feature of acoustipy -- optimization routines that are able to identify the JCA model parameters of porous materials from impedance tube measurements. The snippet can also be found under the Inverse Method in the Examples section. +

+ +```python +from acoustipy import acousticTMM, AcousticID + +# Create an AcousticTMM object to generate toy impedance tube data +structure = acousticTMM(incidence='Normal',air_temperature = 20) + +# Define the JCA and air gap material parameters for the toy data +layer1 = structure.Add_JCA_Layer(thickness = 30, flow_resistivity = 46879, porosity = .93, tortuosity = 1.7, viscous_characteristic_length = 80, thermal_characteristic_length = 105) +air = structure.Add_Air_Layer(thickness = 375) + +#Generate rigid backed absorption data and save to a csv file +s1 = structure.assemble_structure(layer1) +A1 = structure.absorption(s1) +structure.to_csv('no_gap',A1) + +# Generate air backed absorption data and save to a csv file +s2 = structure.assemble_structure(layer1,air) +A2 = structure.absorption(s2) +structure.to_csv('gap',A2) + +# Create an AcousticID object, specifying to mount types, data files, and data types +inv = AcousticID(mount_type='Dual',no_gap_file="no_gap.csv", gap_file = 'gap.csv',air_temperature=20,input_type='absorption') + +# Call the Inverse method to find the tortuosity, viscous, and thermal characteristic lengths of the material +res = inv.Inverse(30, 47000,.926,air_gap=375,uncertainty=.2,verbose=True) + +# Display summary statistics about the optimization +stats = inv.stats(res) +print(stats) + +# Plot the results of the found parameters compared to the toy input data +inv.plot_comparison(res) + +# Save the optimization results to a csv +inv.to_csv("params.csv",res) + +stats = {'slope': 1.000037058594857, 'intercept': 9.276088883464206e-05, 'r_value': 0.9999999674493408, 'p_value': 0.0, 'std_err': 8.732362148426126e-06} +``` + +

+ +![](assets/ex_material_identification_inverse.png) diff --git a/docs/__init__.py b/docs/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/docs/assets/ex_layers_from_database.png b/docs/assets/ex_layers_from_database.png new file mode 100644 index 0000000..2fde5d4 Binary files /dev/null and b/docs/assets/ex_layers_from_database.png differ diff --git a/docs/assets/ex_material_identification_hybrid.png b/docs/assets/ex_material_identification_hybrid.png new file mode 100644 index 0000000..ff5f936 Binary files /dev/null and b/docs/assets/ex_material_identification_hybrid.png differ diff --git a/docs/assets/ex_material_identification_indirect.png b/docs/assets/ex_material_identification_indirect.png new file mode 100644 index 0000000..bc8843b Binary files /dev/null and b/docs/assets/ex_material_identification_indirect.png differ diff --git a/docs/assets/ex_material_identification_inverse.png b/docs/assets/ex_material_identification_inverse.png new file mode 100644 index 0000000..d1abde0 Binary files /dev/null and b/docs/assets/ex_material_identification_inverse.png differ diff --git a/docs/assets/ex_multilayer_structure.png b/docs/assets/ex_multilayer_structure.png new file mode 100644 index 0000000..3dd2d4a Binary files /dev/null and b/docs/assets/ex_multilayer_structure.png differ diff --git a/docs/assets/ex_structure_to_database.png b/docs/assets/ex_structure_to_database.png new file mode 100644 index 0000000..00e69bc Binary files /dev/null and b/docs/assets/ex_structure_to_database.png differ diff --git a/docs/javascripts/mathjax.js b/docs/javascripts/mathjax.js new file mode 100644 index 0000000..e648674 --- /dev/null +++ b/docs/javascripts/mathjax.js @@ -0,0 +1,16 @@ +window.MathJax = { + tex: { + inlineMath: [["\\(", "\\)"]], + displayMath: [["\\[", "\\]"]], + processEscapes: true, + processEnvironments: true + }, + options: { + ignoreHtmlClass: ".*|", + processHtmlClass: "arithmatex" + } + }; + + document$.subscribe(() => { + MathJax.typesetPromise() + }) \ No newline at end of file