Skip to content

Commit

Permalink
Merge pull request #532 from radis/develop
Browse files Browse the repository at this point in the history
0.14
  • Loading branch information
erwanp committed Nov 8, 2022
2 parents f63f377 + 0ea4672 commit 73e4b9e
Show file tree
Hide file tree
Showing 58 changed files with 6,412 additions and 3,890 deletions.
5 changes: 3 additions & 2 deletions .travis.yml
Expand Up @@ -25,12 +25,13 @@ jobs:
# version is the same.
# For now use http instead of https according to this comment:
# https://github.com/mamba-org/mamba/issues/1675#issuecomment-1127160021
- wget -qO- http://micromamba.snakepit.net/api/micromamba/linux-64/latest | tar -xvj bin/micromamba
- wget -qO- https://micro.mamba.pm/api/micromamba/linux-64/0.25.1 | tar -xvj bin/micromamba
- ./bin/micromamba shell init -s bash -p ~/micromamba
- source ~/.bashrc
- micromamba activate base
# Useful for debugging any issues with conda
# Useful for debugging any issues with conda/mamba
- micromamba info
- micromamba config list
# Update python version in environment
- sed -i -E 's/(python=)(.*)/\1'$TRAVIS_PYTHON_VERSION'/' ./environment.yml
# Create conda environment
Expand Down
3 changes: 2 additions & 1 deletion MANIFEST.in
Expand Up @@ -15,4 +15,5 @@ recursive-include radis/db *.json
include radis/test/validation/test_CO2_3Tvib_vs_klarenaar_data/*.csv
include radis/test/validation/test_compare_torch_CO2_data/*.dat
include radis/test/validation/test_validation_vs_specair_noneqCO_data/*.spec
include radis/cython/radis_cython_extensions.pyx
include radis/cython/*.pyx
include radis/cython/*.pxd
1 change: 1 addition & 0 deletions docs/conf.py
Expand Up @@ -177,6 +177,7 @@ def setup(app):
"fitroom": ("https://fitroom.readthedocs.io/en/latest/", None),
"habanero": ("https://habanero.readthedocs.io/en/latest/", None),
"joblib": ("https://joblib.readthedocs.io/en/latest/", None),
"lmfit": ("https://lmfit.github.io/lmfit-py/", None),
"numpy": ("https://numpy.org/doc/stable/", None),
"matplotlib": ("https://matplotlib.org/stable/", None),
"pandas": ("https://pandas.pydata.org/pandas-docs/stable/", None),
Expand Down
5 changes: 4 additions & 1 deletion docs/dev/_test.rst
Expand Up @@ -108,6 +108,9 @@ adding the following lines within your test function::
import matplotlib.pyplot as plt
plt.ion() # dont get stuck with Matplotlib if executing through pytest

You can refer to Pull Request: https://github.com/radis/radis/pull/495 to see
how test cases are written when a new feature is added.

See: https://github.com/statsmodels/statsmodels/issues/3697


Expand All @@ -127,7 +130,7 @@ The path to these test files can be retrieved using the :py:func:`~radis.test.ut
Load a line database file ::

from radis.test.utils import getTestFile
from radis.io.hitran import hit2df
from radis.api.hitranapi import hit2df
df = hit2df(getTestFile("hitran_CO_fragment.par"))

print(df) # replace with your test code
Expand Down
122 changes: 122 additions & 0 deletions examples/plot_newfitting_Tgas-molfrac.py
@@ -0,0 +1,122 @@
# -*- coding: utf-8 -*-
"""
================================================================================
Fit an LTE spectrum with multiple fit parameters using new fitting module
================================================================================
With the new fitting module introduced in :py:func:`~radis.tools.new_fitting.fit_spectrum` function,
and in the example of Tgas fitting using new fitting module, we can see its 1-temperature fitting
performance for equilibrium condition.
This example features how new fitting module can fit an equilibrium spectrum, with multiple fit
parameters, including gas temperature, mole fraction, and wavelength offset.
This is a real fitting case introduced by Mr. Nicolas Minesi, featuring CO spectrum with absorbance
as spectral quantity to be fitted. As we can see, he stored the experimental result in a MATLAB file,
and from there a Spectrum object is generated. It is worth noticing that, the result seems to differ
slightly from ground-truth, due to the fact that currently RADIS uses air broadening parameteres for
calculation, while this experiment was originally conducted in Argon. Future updates on other molecules'
broadening coefficients will increase the accuracy of these cases with non-air diluents.
"""

import astropy.units as u
import scipy.io

from radis import Spectrum
from radis.test.utils import getTestFile
from radis.tools.new_fitting import fit_spectrum

# ------------------------------------ Step 1. Load experimental spectrum ------------------------------------ #


data_file = "trimmed_1857_VoigtCO_Minesi.mat"
data = scipy.io.loadmat(getTestFile(data_file), simplify_cells=True)["CO_resu_Voigt"]
index = 20
s_experimental = Spectrum.from_array(
data["nu"], data["A_exp"][:, index], "absorbance", wunit="cm-1", unit=""
) # adimensioned


# ------------------------------------ Step 2. Fill ground-truths and data ------------------------------------ #


# Experimental conditions which will be used for spectrum modeling. Basically, these are known ground-truths.
experimental_conditions = {
"molecule": "CO", # Molecule ID
"isotope": "1", # Isotope ID, can have multiple at once
"wmin": 2010.6, # Starting wavelength/wavenumber to be cropped out from the original experimental spectrum.
"wmax": 2011.6, # Ending wavelength/wavenumber for the cropping range.
"wunit": "cm-1", # Accompanying unit of those 2 wavelengths/wavenumbers above.
"pressure": 1
* u.bar, # Total pressure of gas, in "bar" unit by default, but you can use Astropy units too.
"path_length": 10
* u.cm, # Experimental path length, in "cm" unit by default, but you can use Astropy units too.
"databank": "hitemp", # Databank used for calculation. Must be stated.
}

# List of parameters to be fitted.
fit_parameters = {
"Tgas": 5000, # Fit parameter, accompanied by its initial value.
"mole_fraction": 0.05, # Species mole fraction, from 0 to 1.
"offset": "0 cm-1", # Experimental offset, must be a blank space separating offset amount and unit.
}

# List of bounding ranges applied for those fit parameters above.
bounding_ranges = {
"Tgas": [
2000,
9000,
], # Bounding ranges for each fit parameter stated above. You can skip this step, but not recommended.
"mole_fraction": [0, 1], # Species mole fraction, from 0 to 1.
"offset": [
-0.1,
0.1,
], # Experimental offset, must be a blank space separating offset amount and unit
}

# Fitting pipeline setups.
fit_properties = {
"method": "lbfgsb", # Preferred fitting method. By default, "leastsq".
"fit_var": "absorbance", # Spectral quantity to be extracted for fitting process, such as "radiance", "absorbance", etc.
"normalize": False, # Either applying normalization on both spectra or not.
"max_loop": 300, # Max number of loops allowed. By default, 100.
"tol": 1e-20, # Fitting tolerance, only applicable for "lbfgsb" method.
}

"""
For the fitting method, you can try one among 17 different fitting methods and algorithms of LMFIT,
introduced in `LMFIT method list <https://lmfit.github.io/lmfit-py/fitting.html#choosing-different-fitting-methods>`.
You can see the benchmark result of these algorithms here:
`RADIS Newfitting Algorithm Benchmark <https://github.com/radis/radis-benchmark/blob/master/manual_benchmarks/plot_newfitting_comparison_algorithm.py>`.
"""


# ------------------------------------ Step 3. Run the fitting and retrieve results ------------------------------------ #


# Conduct the fitting process!
s_best, result, log = fit_spectrum(
s_exp=s_experimental,
fit_params=fit_parameters,
bounds=bounding_ranges,
model=experimental_conditions,
pipeline=fit_properties,
)


# Now investigate the log

print("\nResidual history: \n")
print(log["residual"])

print("\nFitted values history: \n")
for fit_val in log["fit_vals"]:
print(fit_val)

print("\nTotal fitting time: ")
print(log["time_fitting"], end=" s\n")
132 changes: 132 additions & 0 deletions examples/plot_newfitting_Tgas.py
@@ -0,0 +1,132 @@
# -*- coding: utf-8 -*-
"""
================================================================================
New fitting module introduction - simple 1-temperature LTE case
================================================================================
RADIS has its own fitting feature, as shown in
`1 temperature fit example <https://radis.readthedocs.io/en/latest/auto_examples/plot_1T_fit.html>`,
where you have to manually create the spectrum model, input the experimental spectrum and other
ground-truths into numerous RADIS native functions, and adjust the fitting pipeline yourself.
Now with the new fitting module released, all you have to do is to prepare a .spec file containing
your experimental spectrum, fill some JSON forms describing the ground-truth conditions just like how
you fill a medical checkup paper, call the function :py:func:`~radis.tools.new_fitting.fit_spectrum`
and let it do all the work! If you are not satisfied with the result, you can simply adjust the
parameters in your JSON such as slit and path_length, recall the function until the results are good.
Instruction:
- Step 1: prepare a .spec file. Create a .spec file containing your experimental spectrum. You can
do it with RADIS by saving a Spectrum object with :py:meth:`~radis.spectrum.spectrum.Spectrum.store`.
If your current data is not a Spectrum object, you can convert it to a Spectrum object from Python
arrays or from text files, and then save it as .spec file as above.
- Step 2: fill the JSON forms. There are 4 JSON forms you need to fill: `experimental_conditions` with
ground-truth data about your experimental environment, `fit_parameters` with the parameters you need
to fit (such as Tgas, mole fraction, etc.), `bounding_ranges` with fitting ranges for parameters you
listed in `fit_parameters`, and `fit_properties` for some fitting pipeline references.
- Step 3: call :py:func:`~radis.tools.new_fitting.fit_spectrum` with the experimental spectrum and 4
JSON forms, then see the result.
This example features fitting an experimental spectrum with Tgas, using new fitting modules.
"""

import astropy.units as u

from radis import load_spec
from radis.test.utils import getTestFile
from radis.tools.new_fitting import fit_spectrum

# ------------------------------------ Step 1. Load experimental spectrum ------------------------------------ #


# Load an experimental spectrum. You can prepare yours, or fetch one of them in the radis/test/files directory.
my_spec = getTestFile("synth-NH3-1-500-2000cm-P10-mf0.01-p1.spec")
s_experimental = load_spec(my_spec)


# ------------------------------------ Step 2. Fill ground-truths and data ------------------------------------ #


# Experimental conditions which will be used for spectrum modeling. Basically, these are known ground-truths.
experimental_conditions = {
"molecule": "NH3", # Molecule ID
"isotope": "1", # Isotope ID, can have multiple at once
"wmin": 1000, # Starting wavelength/wavenumber to be cropped out from the original experimental spectrum.
"wmax": 1050, # Ending wavelength/wavenumber for the cropping range.
"wunit": "cm-1", # Accompanying unit of those 2 wavelengths/wavenumbers above.
"mole_fraction": 0.01, # Species mole fraction, from 0 to 1.
"pressure": 1e6
* u.Pa, # Total pressure of gas, in "bar" unit by default, but you can also use Astropy units.
"path_length": 10
* u.mm, # Experimental path length, in "cm" unit by default, but you can also use Astropy units.
"slit": "1 nm", # Experimental slit, must be a blank space separating slit amount and unit.
"offset": "-0.2 nm", # Experimental offset, must be a blank space separating offset amount and unit.
"databank": "hitran", # Databank used for the spectrum calculation. Must be stated.
}

# List of parameters to be fitted, accompanied by their initial values.
fit_parameters = {
"Tgas": 700, # Gas temperature, in K.
}

# List of bounding ranges applied for those fit parameters above.
# You can skip this step and let it use default bounding ranges, but this is not recommended.
# Bounding range must be at format [<lower bound>, <upper bound>].
bounding_ranges = {
"Tgas": [
500,
2000,
],
}

# Fitting pipeline setups.
fit_properties = {
"method": "least_squares", # Preferred fitting method from the 17 confirmed methods of LMFIT stated in week 4 blog. By default, "leastsq".
"fit_var": "radiance", # Spectral quantity to be extracted for fitting process, such as "radiance", "absorbance", etc.
"normalize": False, # Either applying normalization on both spectra or not.
"max_loop": 150, # Max number of loops allowed. By default, 200.
"tol": 1e-15, # Fitting tolerance, only applicable for "lbfgsb" method.
}

"""
For the fitting method, you can try one among 17 different fitting methods and algorithms of LMFIT,
introduced in `LMFIT method list <https://lmfit.github.io/lmfit-py/fitting.html#choosing-different-fitting-methods>`.
You can see the benchmark result of these algorithms here:
`RADIS Newfitting Algorithm Benchmark <https://github.com/radis/radis-benchmark/blob/master/manual_benchmarks/plot_newfitting_comparison_algorithm.py>`.
"""


# ------------------------------------ Step 3. Run the fitting and retrieve results ------------------------------------ #


# Conduct the fitting process!
s_best, result, log = fit_spectrum(
s_exp=s_experimental, # Experimental spectrum.
fit_params=fit_parameters, # Fit parameters.
bounds=bounding_ranges, # Bounding ranges for those fit parameters.
model=experimental_conditions, # Experimental ground-truths conditions.
pipeline=fit_properties, # Fitting pipeline references.
fit_kws={"gtol": 1e-12},
)


# Now investigate the result logs for additional information about what's going on during the fitting process

print("\nResidual history: \n")
print(log["residual"])

print("\nFitted values history: \n")
for fit_val in log["fit_vals"]:
print(fit_val)

print("\nTotal fitting time: ")
print(log["time_fitting"], end=" s\n")

0 comments on commit 73e4b9e

Please sign in to comment.