Fitting

NTECARS.fit_spectrumFunction
fit_spectrum(; spec_exp, sim, parameter_update_function!, initial, lower, upper, 
    intensity_eval_function = x -> abs.(x ./ maximum(x)).^(1/2) , parameter_scaling_factor = initial,
    solver::Symbol = :LM, maxiters= 200, compute_uncertainties = true) -> FitResult

Fits the the model to a measured spectrum and returns a collection of results in a FitResult.

Arguments

  • spec_exp::Spectrum: The measured spectrum that should be fitted
  • sim::CARSSimulator: Should contain all required inputs such as linewidths
  • parameter_update_function!::Function: A function with the signature f(sim::CARSSimulator, param) that take a param array of length N and updates sim using these parameters.
  • initial::Vector{Float64}: Initial values of the fit parameters. Has to be of length N.
  • lower::Vector{Float64}: Lower boundary of the fit parameters. Has to be of length N.
  • upper::Vector{Float64}: upper boundary of the fit parameters. Has to be of length N.
  • weights::Union{Spectrum, Nothing}=nothing: weights used for weighted least squares (formular: residuals = weights .* (I_exp - I_sim).^2). For absolute uncertainties, the weights represent w=1/σ^2 where σ is standard deviation of the measured intensity.
  • absolute_sigma::Bool: Iftrue`, then the weights (sigmas) are used to give absolute uncertainties. Otherwise, sigma is scaled to match the sample variance between the fitted and experimenal spectral.
  • solver::Symbol: Options are :LM for LevenbergMarquardt and :IPOPT for the IPOPT solver.
  • maxiters::Int64: Maximum number of iterations
  • compute_uncertainties::Bool = true: Mostly an option to disable for cases is which the SVD throws an error.
  • tolerance::Float64 = 1e-5: Tolerance that is passed to the solver.

return

  • FitResult: contains parameters, spectra, uncertainties etc...

Example

Which parameters are fitted toegther is determined by the parameter_update_function!.

For a sim::CARSSimulator that contains the species N2Species as the first element in sim.species, the rotational and vibrational temperature can for example be fitted by defining the update function

function update_function!(sim::CARSSimulator, param)
    T_vib, T_rot = param
    sim.conditions.T_gas = T_rot
    sim.species[1].distribution = N2.MultiTemperatureDistribution(
        T_vib = T_vib, T_rot = T_rot)
end

result = fit_spectrum(;
    spec_exp     = experimental_spectrum,
    sim          = sim,
    initial      = [500.0, 500.0],
    lower        = [0.0, 0.0],
    upper        = [3000.0, 3000.0],
    parameter_update_function! = update_function!
)
source
NTECARS.FitResultType
FitResult

Stores the complete result of a fit_spectrum call.

Fields

  • param::Vector{Float64}: Optimal fit parameters found by the solver.
  • uncertainties::Vector{Float64}: Parameter uncertainties estimated from the Jacobian SVD at the optimal solution. Set to zeros if compute_uncertainties = false was passed to fit_spectrum.
  • fitted_spectrum::Spectrum: Simulated spectrum evaluated on the simulator's internal wavenumber grid.
  • fitted_spectrum_at_measurement::Spectrum: Simulated spectrum evaluated at the wavenumber positions of the experimental spectrum. Use this for direct comparison with experimental_spectrum.
  • sim::CARSSimulator: Deep copy of the simulator after convergence, updated with the optimal parameters via parameter_update_function!.
  • experimental_spectrum::Spectrum: The measured spectrum passed to fit_spectrum. Stored to allow serialization and later re-evaluation of the result.
  • parameter_update_function!::Function: The update function used during fitting. Stored to allow re-simulation or re-fitting from the result.
  • weights::Function: The weights used for weighted least-squares

Notes

  • fitted_spectrum and fitted_spectrum_at_measurement differ only in their wavenumber grids. For plotting against measured data, prefer fitted_spectrum_at_measurement.
  • All fields needed to reproduce or serialize the fit are stored directly in the struct, so no reference to the original sim or functions is required after fitting.
source
NTECARS.save_fit_resultsFunction
save_fit_results(folderpath, result::FitResult,
                 parameter_labels=["fit param $(i)" for i in eachindex(result.param)])

Saves the complete results of a FitResult to a folder structure of CSV files, including the fitted and experimental spectra, fit parameters with uncertainties, and rovibrational populations for each species.

Arguments

  • folderpath: Path to the output folder.
  • result::FitResult: The fit result returned by fit_spectrum.
  • parameter_labels: Labels for the fit parameters used in the output file. Defaults to ["fit param 1", "fit param 2", ...].

Output files

  • fit_and_experiment_at_measurement_points.csv: Fitted and experimental √(I_CARS) at the experimental wavenumber grid, including the residual. Contains anti-Stokes wavelength and Raman shift columns relative to both lasers.
  • fit_at_simulated_resolution.csv: Fitted √(I_CARS) at the full simulation resolution, which is typically finer than the experimental grid.
  • fit_parameters.csv: Fitted parameter values with absolute and relative uncertainties (%) for each parameter.
  • covariance_matrix.csv: The covaraince matrix of the fit. If absolute_chi=false, the covariance matrix was rescaled with the redced chi squared.
  • reduced_chi2.csv: The value of the reduced chi squared.
  • CO2_rovibrational_populations/: Created if a CO2Species is present in result.sim.species. Contains one CSV per vibrational state with rotational populations [CO2(v,J)]/[CO2] and their uncertainties, plus a summary vibrational_populations.csv.
  • N2_rovibrational_populations/: Created if an N2Species is present in result.sim.species. Contains one CSV per vibrational level with rotational populations [N2(v,J)]/[N2] and their uncertainties, plus a summary vibrational_populations.csv.

Notes

  • Rovibrational populations are computed with uncertainty propagation via the Measurements.jl package. Only the uncertainties of fit parameters and not the uncertainties of the polarizabilities and linewidths are considered.

Examples

save_fit_results("results/measurement_01", result)

# With custom parameter labels
save_fit_results("results/measurement_01", result,
    ["T_12 (K)", "T_3 (K)", "T_N2vib (K)", "T_rot (K)", "CO2 molar fraction"])
source