def test_reduced_CDSD_calc_noneq(verbose=True, warnings=True, *args, **kwargs): """ Compare calculated partition function at equilibrium and nonequilibrium using the CDSD-format """ from radis.misc.config import getDatabankEntries iso = 1 database = "HITEMP-CO2-HAMIL-TEST" # Compare tab to calculated energies = getDatabankEntries(database)["levels"] levelsfmt = getDatabankEntries(database)["levelsfmt"] Qfc = PartFuncCO2_CDSDcalc( energies[iso], levelsfmt=levelsfmt, isotope=iso, use_cached=True, verbose=verbose, ) assert np.isclose(Qfc.at(300), Qfc.at_noneq_3Tvib((300, 300, 300), 300), rtol=0.001) if verbose: printm( "Tested CDSD Q_calc at equilibrium and nonequilibrium give same output: OK" )
def test_CDSD_calc_vs_ref(warnings=True, verbose=True, *args, **kwargs): ''' Test partition functions calculated with CDSD energy levels against hardcoded values ''' from radis.misc.config import getDatabankEntries iso = 1 try: energies = getDatabankEntries('CDSD-HITEMP-PC')['levels'] levelsfmt = getDatabankEntries('CDSD-HITEMP-PC')['levelsfmt'] Qf = PartFuncCO2_CDSDcalc(energy_levels=energies[iso], isotope=iso, use_cached=True, levelsfmt=levelsfmt) assert np.isclose(Qf.at(300), 291.0447781984652, rtol=0.001) assert np.isclose(Qf.at(3000), 114689.88454184022, rtol=0.001) assert np.isclose(Qf.at_noneq(300, 300), 291.0447781984652, rtol=0.001) assert np.isclose(Qf.at_noneq(3000, 3000), 114689.88454184022, rtol=0.001) assert np.isclose(Qf.at_noneq(3000, 3000, overpopulation={'(0,1)': 3}, returnQvibQrot=True)[0], 120053.34252537244, rtol=0.001) if verbose: printm('Tested Q_CDSD values are correct : OK') return True except DatabankNotFound as err: assert IgnoreMissingDatabase(err, __file__, warnings)
def test_media_line_shift(plot=False, verbose=True, warnings=True, *args, **kwargs): ''' See wavelength difference in air and vacuum ''' if plot: # Make sure matplotlib is interactive so that test are not stuck in pytest plt.ion() try: if verbose: printm('>>> _test_media_line_shift') setup_test_line_databases( ) # add HITRAN-CO-TEST in ~/.radis if not there sf = SpectrumFactory(wavelength_min=4500, wavelength_max=4600, wstep=0.001, parallel=False, bplot=False, cutoff=1e-30, path_length=0.1, mole_fraction=400e-6, isotope=[1], db_use_cached=True, medium='vacuum', broadening_max_width=10, verbose=verbose) sf.warnings['MissingSelfBroadeningWarning'] = 'ignore' sf.warnings['GaussianBroadeningWarning'] = 'ignore' sf.load_databank('HITRAN-CO-TEST') # Calculate a spectrum s = sf.eq_spectrum(2000) # Compare if plot: fig = plt.figure(fig_prefix + 'Propagating media line shift') s.plot('radiance_noslit', wunit='nm_vac', nfig=fig.number, lw=2, label='Vacuum') plt.title('CO spectrum (2000 K)') s.plot('radiance_noslit', wunit='nm', nfig=fig.number, lw=2, color='r', label='Air') # ... there should be about ~1.25 nm shift at 4.5 µm: assert np.isclose( s.get('radiance_noslit', wunit='nm_vac')[0][0] - s.get('radiance_noslit', wunit='nm')[0][0], 1.2540436086346745) except DatabankNotFound as err: assert IgnoreMissingDatabase(err, __file__, warnings)
def test_line_survey_CO2(verbose=True, plot=True, warnings=True, *args, **kwargs): setup_test_line_databases() try: pl = SpectrumFactory( wavenum_min=2380, wavenum_max=2400, # wavelength_min=4170, # wavelength_max=4200, mole_fraction=400e-6, path_length=100, # cm parallel=False, cutoff=1e-30, isotope=[1], save_memory=True, db_use_cached=True, ) # 0.2) pl.warnings["MissingSelfBroadeningWarning"] = "ignore" pl.load_databank("HITRAN-CO2-TEST") s = pl.eq_spectrum(Tgas=1500) s.apply_slit(0.5) if plot: s.line_survey(overlay="transmittance", barwidth=0.01) if verbose: printm("no boolean defined for test_line_survey") return True # test not defined (just testing methods work) except DatabankNotFound as err: assert IgnoreMissingDatabase(err, __file__, warnings)
def test_CDSD_calc_vs_tab(verbose=True, warnings=True, *args, **kwargs): """ Test 1: compare calculated PartFunc to the tabulated one """ from radis.misc.config import getDatabankEntries iso = 1 database = "CDSD-HITEMP-PC" # Compare tab to hardcoded parfunc = getDatabankEntries(database)["parfunc"] Qf = PartFuncCO2_CDSDtab(iso, parfunc) assert np.isclose(Qf.at(300), 291.0447781984652, rtol=0.001) assert np.isclose(Qf.at(3000), 114689.88454184022, rtol=0.001) if verbose: printm("Tested CDSD tabulated is correct: OK") # Compare tab to calculated energies = getDatabankEntries(database)["levels"] levelsfmt = getDatabankEntries(database)["levelsfmt"] Qfc = PartFuncCO2_CDSDcalc(energies[iso], levelsfmt=levelsfmt, isotope=iso, use_cached=True) assert np.isclose(Qf.at(300), Qfc.at(300), rtol=0.001) assert np.isclose(Qf.at(3000), Qfc.at(3000), rtol=0.001) if verbose: printm("Tested CDSD Q_calc vs Q_tab give same output: OK") return True
def test_CDSD_calc_vs_ref(warnings=True, verbose=True, *args, **kwargs): """Test partition functions calculated with CDSD energy levels against hardcoded values""" from radis.misc.config import getDatabankEntries iso = 1 energies = getDatabankEntries("CDSD-HITEMP-PC")["levels"] levelsfmt = getDatabankEntries("CDSD-HITEMP-PC")["levelsfmt"] Qf = PartFuncCO2_CDSDcalc( energy_levels=energies[iso], isotope=iso, use_cached=True, levelsfmt=levelsfmt, ) assert np.isclose(Qf.at(300), 291.0447781984652, rtol=0.001) assert np.isclose(Qf.at(3000), 114689.88454184022, rtol=0.001) assert np.isclose(Qf.at_noneq(300, 300), 291.0447781984652, rtol=0.001) assert np.isclose(Qf.at_noneq(3000, 3000), 114689.88454184022, rtol=0.001) assert np.isclose( Qf.at_noneq(3000, 3000, overpopulation={"(0,1)": 3}, returnQvibQrot=True)[0], 120053.34252537244, rtol=0.001, ) if verbose: printm("Tested Q_CDSD values are correct : OK")
def check_wavelength_range(verbose=True, warnings=True, *args, **kwargs): """Check that input wavelength is correctly taken into account. See https://github.com/radis/radis/issues/214 """ if verbose: printm("Testing calc_spectrum wavelength range") wstep = 0.01 s = calc_spectrum( wavelength_min=4348, # nm wavelength_max=5000, molecule="CO", isotope="1,2,3", pressure=1.01325, # bar Tvib=1700, # K Trot=1700, # K databank="HITRAN-CO-TEST", wstep=wstep, ) w, I = s.get("radiance_noslit", wunit="nm", Iunit="mW/sr/cm2/nm") assert np.isclose(w.min(), 4348, atol=wstep) assert np.isclose(w.max(), 5000, atol=wstep) return True
def test_CDSD_calc_vs_tab(verbose=True, warnings=True, *args, **kwargs): ''' Test 1: compare calculated PartFunc to the tabulated one ''' from radis.misc.config import getDatabankEntries try: iso = 1 database = 'CDSD-HITEMP-PC' # Compare tab to hardcoded parfunc = getDatabankEntries(database)['parfunc'] Qf = PartFuncCO2_CDSDtab(iso, parfunc) assert np.isclose(Qf.at(300), 291.0447781984652, rtol=0.001) assert np.isclose(Qf.at(3000), 114689.88454184022, rtol=0.001) if verbose: printm('Tested CDSD tabulated is correct: OK') # Compare tab to calculated energies = getDatabankEntries(database)['levels'] levelsfmt = getDatabankEntries(database)['levelsfmt'] Qfc = PartFuncCO2_CDSDcalc(energies[iso], levelsfmt=levelsfmt, isotope=iso, use_cached=True) assert np.isclose(Qf.at(300), Qfc.at(300), rtol=0.001) assert np.isclose(Qf.at(3000), Qfc.at(3000), rtol=0.001) if verbose: printm('Tested CDSD Q_calc vs Q_tab give same output: OK') return True except DatabankNotFound as err: assert IgnoreMissingDatabase(err, __file__, warnings)
def test_rescaling_path_length(debug=False, plot=False, verbose=True, warnings=True, *args, **kwargs): """ Test rescaling functions """ if plot: # Make sure matplotlib is interactive so that test are not stuck plt.ion() try: from radis.lbl import SpectrumFactory setup_test_line_databases( ) # add HITRAN-CO-TEST in ~/.radis if not there Tgas = 1500 sf = SpectrumFactory( wavelength_min=4400, wavelength_max=4800, mole_fraction=0.01, # path_length=0.1, cutoff=1e-25, wstep=0.005, isotope=[1], db_use_cached=True, self_absorption=True, verbose=verbose, ) sf.warnings["MissingSelfBroadeningWarning"] = "ignore" # sf.warnings['NegativeEnergiesWarning'] = 'ignore' sf.load_databank("HITRAN-CO-TEST") s1 = sf.non_eq_spectrum(Tgas, Tgas, path_length=0.01) s2 = sf.non_eq_spectrum(Tgas, Tgas, path_length=3) s1.rescale_path_length(3) if plot: fig = plt.figure(fig_prefix + "Rescaling path length") s2.plot("radiance_noslit", nfig=fig.number, lw=3, label="L=3m") s1.plot( "radiance_noslit", nfig=fig.number, color="r", label="L=0.01m, rescaled to 3m", ) plt.title("Non optically thin rescaling") plt.legend() plt.tight_layout() if verbose: printm("Test rescaling:") printm("... Difference: {0:.2f}%".format( abs(s1.get_power() / s2.get_power() - 1) * 100)) assert np.isclose(s2.get_power(), s1.get_power(), 2e-3) except DatabankNotFound as err: assert IgnoreMissingDatabase(err, __file__, warnings)
def test_rescale_all_quantities(verbose=True, warnings=True, *args, **kwargs): new_mole_fraction = 0.5 new_path_length = 0.1 # Get spectrum s0 = load_spec(getTestFile('CO_Tgas1500K_mole_fraction0.01.spec'), binary=True) s0.update('all') # start with all possible quantities in s0 sscaled = s0.copy() sscaled.rescale_mole_fraction(new_mole_fraction) sscaled.rescale_path_length(new_path_length) s0.conditions['thermal_equilibrium'] = False # to prevent rescaling with Kirchoff # remove emissivity_no_slit (speciifc to equilibrium) del s0._q['emissivity_noslit'] # Determine all quantities that can be recomputed if verbose >= 2: import radis DEBUG_MODE = radis.DEBUG_MODE radis.DEBUG_MODE = True from radis.spectrum.rescale import get_reachable, ordered_keys, _build_update_graph # ordered_keys: all spectral quantities that can be rescaled can_be_recomputed = get_reachable(s0) # can_be_recomputed: all spectra quantities that can be rescaled for this # particular spectrum update_paths = _build_update_graph(s0) # update_paths: which quantities are needed to recompute the others rescale_list = [k for k in ordered_keys if can_be_recomputed[k]] for quantity in rescale_list: all_paths = update_paths[quantity] if verbose: printm('{0} can be recomputed from {1}'.format(quantity, ' or '.join( ['&'.join(combinations) for combinations in all_paths]))) # Now let's test all paths for combinations in all_paths: if verbose: printm('> computing {0} from {1}'.format(quantity, '&'.join(combinations))) s = s0.copy() # Delete all other quantities for k in s.get_vars(): if k not in combinations: del s._q[k] s.update(quantity, verbose=verbose) # Now rescale s.rescale_mole_fraction(new_mole_fraction) s.rescale_path_length(new_path_length) # Compare assert s.compare_with(sscaled, spectra_only=quantity, plot=False) if verbose >= 2: radis.DEBUG_MODE = DEBUG_MODE
def test_eq_vs_noneq_isotope(verbose=True, plot=False, warnings=True, *args, **kwargs): ''' Test same spectrum for 2 different calculation codes (equilibrium, non-equilibrium) in the presence of isotopes Notes ----- On the old NeQ package the test used [HITEMP-2010]_ Starting from RADIS 1.0.1, the test is run on [HITRAN-2016]_, which is not valid for these temperatures but can be more conveniently downloaded automatically and thus executed everytime with [Travis]_ ''' try: Tgas = 1500 sf = SpectrumFactory(wavelength_min=4250, wavelength_max=4350, mole_fraction=1, path_length=1, cutoff=1e-25, molecule='CO2', isotope='1,2', db_use_cached=True, verbose=verbose) sf.warnings['MissingSelfBroadeningWarning'] = 'ignore' sf.warnings['NegativeEnergiesWarning'] = 'ignore' sf.warnings['HighTemperatureWarning'] = 'ignore' sf.fetch_databank( ) # uses HITRAN: not really valid at this temperature, but runs on all machines without install # sf.load_databank('HITEMP-CO2-DUNHAM') s_nq = sf.non_eq_spectrum(Tvib=Tgas, Trot=Tgas, name='Non-eq') s_eq = sf.eq_spectrum(Tgas=Tgas, name='Eq') rtol = 5e-3 # 2nd isotope calculated with placeholder energies match_eq_vs_non_eq = s_eq.compare_with(s_nq, spectra_only='abscoeff', rtol=rtol, plot=plot) match_eq_vs_non_eq *= s_eq.compare_with(s_nq, spectra_only='radiance_noslit', rtol=rtol, plot=plot) if verbose: printm('Tested eq vs non-eq (<{0:.1f}% error) with isotopes: {1}'. format(rtol * 100, bool(match_eq_vs_non_eq))) assert match_eq_vs_non_eq except DatabankNotFound as err: assert IgnoreMissingDatabase(err, __file__, warnings)
def test_eq_vs_noneq_isotope(verbose=True, plot=False, warnings=True, *args, **kwargs): """Test same spectrum for 2 different calculation codes (equilibrium, non-equilibrium) in the presence of isotopes Notes ----- On the old NeQ package the test used [HITEMP-2010]_ Starting from RADIS 1.0.1, the test is run on [HITRAN-2016]_, which is not valid for these temperatures but can be more conveniently downloaded automatically and thus executed everytime with `Travis CI <https://travis-ci.com/radis/radis>`_ """ Tgas = 1500 sf = SpectrumFactory( wavelength_min=4250, wavelength_max=4350, mole_fraction=1, path_length=1, cutoff=1e-25, molecule="CO2", isotope="1,2", db_use_cached=True, verbose=verbose, ) sf.warnings["MissingSelfBroadeningWarning"] = "ignore" sf.warnings["NegativeEnergiesWarning"] = "ignore" sf.warnings["HighTemperatureWarning"] = "ignore" sf.fetch_databank( ) # uses HITRAN: not really valid at this temperature, but runs on all machines without install # sf.load_databank('HITEMP-CO2-DUNHAM') s_nq = sf.non_eq_spectrum(Tvib=Tgas, Trot=Tgas, name="Non-eq") s_eq = sf.eq_spectrum(Tgas=Tgas, name="Eq") rtol = 5e-3 # 2nd isotope calculated with placeholder energies match_eq_vs_non_eq = s_eq.compare_with(s_nq, spectra_only="abscoeff", rtol=rtol, plot=plot) match_eq_vs_non_eq *= s_eq.compare_with(s_nq, spectra_only="radiance_noslit", rtol=rtol, plot=plot) if verbose: printm( "Tested eq vs non-eq (<{0:.1f}% error) with isotopes: {1}".format( rtol * 100, bool(match_eq_vs_non_eq))) assert match_eq_vs_non_eq
def test_calculatedQ_match_HAPI_CO(vmax=11, jmax=300, plot=False, verbose=True, *args, **kwargs): """ Tested that Q ab_initio (Dunham) match HAPI for CO at different temperatures""" vmax = 11 vmax_morse = 48 jmax = 300 iso = 1 S = Molecules["CO"][iso]["X"] # Dont use cached: force recalculating db = PartFunc_Dunham(S, vmax=vmax, vmax_morse=vmax_morse, Jmax=jmax, use_cached=False) # , ZPE=1081.584383) assert not db.use_cached # if plot: db.plot_states() hapi = PartFuncHAPI( M=5, I=1, ) # CO # isotope us = [] hap = [] T = np.linspace(300, 3000) for Ti in T: us.append(db.at(Ti)) hap.append(hapi.at(Ti)) if plot: plt.figure(fig_prefix + "Partition function Dunham vs Precomputed") plt.plot(T, us, "ok", label="NeQ") plt.plot(T, hap, "or", label="HAPI") plt.legend() plt.xlabel("Temperature (K)") plt.ylabel("Partition function") plt.title("Ab-initio partition function calculations\n" + "(vmax:{0},jmax:{1})".format(vmax, jmax)) plt.tight_layout() # Compare Calculated vs HAPI assert np.allclose(us, hap, rtol=0.02) if verbose: printm( "Tested Q_CO ab_initio (Dunham) matches HAPI for 300 - 3000 K: OK") return True
def test_calculatedQ_match_HAPI(vmax=11, jmax=300, plot=False, verbose=True, *args, **kwargs): ''' Tested that Q_CO ab_initio (Dunham) match HAPI ''' vmax = 11 vmax_morse = 48 jmax = 300 iso = 1 S = Molecules['CO'][iso]['X'] # Dont use cached: force recalculating db = PartFunc_Dunham(S, vmax=vmax, vmax_morse=vmax_morse, Jmax=jmax, use_cached=False) # , ZPE=1081.584383) assert not db.use_cached # if plot: db.plot_states() hapi = PartFuncHAPI( M=5, # CO I=1, # isotope ) us = [] hap = [] T = np.linspace(300, 3000) for Ti in T: us.append(db.at(Ti)) hap.append(hapi.at(Ti)) if plot: plt.figure(fig_prefix + 'Partition function Dunham vs Precomputed') plt.plot(T, us, 'ok', label='NeQ') plt.plot(T, hap, 'or', label='HAPI') plt.legend() plt.xlabel('Temperature (K)') plt.ylabel('Partition function') plt.title('Ab-initio partition function calculations\n' + '(vmax:{0},jmax:{1})'.format(vmax, jmax)) plt.tight_layout() # Compare Calculated vs HAPI assert np.allclose(us, hap, rtol=0.02) if verbose: printm('Tested Q_CO ab_initio (Dunham) matches HAPI: OK') return True
def test_media_line_shift(plot=False, verbose=True, warnings=True, *args, **kwargs): """ See wavelength difference in air and vacuum """ if plot: # Make sure matplotlib is interactive so that test are not stuck in pytest plt.ion() if verbose: printm(">>> _test_media_line_shift") setup_test_line_databases() # add HITRAN-CO-TEST in ~/.radis if not there sf = SpectrumFactory( wavelength_min=4500, wavelength_max=4600, wstep=0.001, parallel=False, bplot=False, cutoff=1e-30, path_length=0.1, mole_fraction=400e-6, isotope=[1], db_use_cached=True, medium="vacuum", broadening_max_width=10, verbose=verbose, ) sf.warnings["MissingSelfBroadeningWarning"] = "ignore" sf.warnings["GaussianBroadeningWarning"] = "ignore" sf.load_databank("HITRAN-CO-TEST") # Calculate a spectrum s = sf.eq_spectrum(2000) # Compare if plot: fig = plt.figure(fig_prefix + "Propagating media line shift") s.plot("radiance_noslit", wunit="nm_vac", nfig=fig.number, lw=2, label="Vacuum") plt.title("CO spectrum (2000 K)") s.plot( "radiance_noslit", wunit="nm", nfig=fig.number, lw=2, color="r", label="Air", ) # ... there should be about ~1.25 nm shift at 4.5 µm: assert np.isclose( s.get("radiance_noslit", wunit="nm_vac")[0][0] - s.get("radiance_noslit", wunit="nm")[0][0], 1.2540436086346745, )
def test_calculatedQ_match_HAPI(plot=False, verbose=True, *args, **kwargs): """Tested that Q ab_initio (Dunham) match HAPI for different molecules and isotopes""" # molecule, isotope, temperature, absolute tolerance for molecule, iso, T, atol, rtol in [ ("CO2", 1, 300, 0.9, 0.02), ("CO2", 1, 1000, 5.0, 0.02), ("CO2", 1, 3000, 2150, 0.02), ("CO2", 2, 300, 1.8, 0.02), ("CO2", 2, 1000, 25, 0.02), ("CO2", 2, 3000, 4962, 0.02), ("CO", 1, 300, 0.16, 0.02), ("CO", 1, 1000, 1.9, 0.02), ("CO", 1, 3000, 27, 0.02), ("CO", 2, 300, 0.31, 0.02), ("CO", 2, 1000, 4.1, 0.02), ("CO", 2, 3000, 56.9, 0.02), ("CO", 3, 300, 0.16, 0.02), ("CO", 3, 1000, 2.1, 0.02), ("CO", 3, 3000, 28.6, 0.02), ]: S = Molecules[molecule][iso]["X"] # Dont use cached: force recalculating db = PartFunc_Dunham(S) from radis.db.classes import get_molecule_identifier hapi = PartFuncHAPI(M=get_molecule_identifier(molecule), I=iso) Q_radis = db.at(T) Q_hapi = hapi.at(T) if verbose: print("Q({0},iso={1},{2}K)\tRADIS: {3:.2f}\tHAPI={4:.2f}".format( molecule, iso, T, Q_radis, Q_hapi)) try: assert np.isclose(Q_radis, Q_hapi, atol=atol) assert np.isclose(Q_radis, Q_hapi, atol=atol) except AssertionError: raise AssertionError( "Partition function for {0}, iso={1} ".format(molecule, iso) + "at {0}K doesnt match in RADIS ".format(T) + "({0:.4f}) and HAPI ({1:.4f})".format(Q_radis, Q_hapi)) if verbose: printm("Tested Q ab_initio (Dunham) matches HAPI: OK") return True
def test_calculatedQ_match_HAPI(plot=False, verbose=True, *args, **kwargs): ''' Tested that Q ab_initio (Dunham) match HAPI for different molecules and isotopes''' # molecule, isotope, temperature, absolute tolerance for molecule, iso, T, atol in [('CO2', 1, 300, 0.9), ('CO2', 1, 1000, 2.0), ('CO2', 1, 3000, 2000), ('CO2', 2, 300, 1.8), ('CO2', 2, 1000, 25), ('CO2', 2, 3000, 4962), ('CO', 1, 300, 0.16), ('CO', 1, 1000, 1.9), ('CO', 1, 3000, 27), ('CO', 2, 300, 0.31), ('CO', 2, 1000, 4.1), ('CO', 2, 3000, 56.9), ('CO', 3, 300, 0.16), ('CO', 3, 1000, 2.1), ('CO', 3, 3000, 28.6), ]: S = Molecules[molecule][iso]['X'] # Dont use cached: force recalculating db = PartFunc_Dunham(S) from radis.io.hitran import get_molecule_identifier hapi = PartFuncHAPI(M=get_molecule_identifier(molecule), I=iso ) Q_radis = db.at(T) Q_hapi = hapi.at(T) if verbose: print('Q({0},iso={1},{2}K)\tRADIS: {3:.2f}\tHAPI={4:.2f}'.format( molecule, iso, T, Q_radis, Q_hapi)) try: assert np.isclose(Q_radis, Q_hapi, atol=atol) except AssertionError: raise AssertionError('Partition function for {0}, iso={1} '.format(molecule, iso)+\ 'at {0}K doesnt match in RADIS '.format(T)+\ '({0:.4f}) and HAPI ({1:.4f})'.format(Q_radis, Q_hapi)) if verbose: printm('Tested Q ab_initio (Dunham) matches HAPI: OK') return True
def test_Morse_Potential_effect_CO(T=3000, rtol=1e-4, verbose=True, warnings=True, *args, **kwargs): ''' Quantify effect of calculating upper levels near dissociation limit with Morse Potential Returns True if difference is less than rtol ''' vmax = 11 vmax_morse = 48 jmax = 300 iso = 1 S = Molecules['CO'][iso]['X'] # TODO: remove vmax, vmax_morse from Dunham method. Only use the one in ElecState db = PartFunc_Dunham(S, vmax=vmax, vmax_morse=0, Jmax=jmax, use_cached=False) Q_nomorse = db.at(T) db = PartFunc_Dunham(S, vmax=vmax, vmax_morse=vmax_morse, Jmax=jmax, use_cached=False) Q_morse = db.at(T) if verbose: printm('Morse vs no Morse potential (T={0}K)'.format(T)) printm('Q_morse: {0:.3f}'.format(Q_morse)) printm('Q_nomorse: {0:.3f}'.format(Q_nomorse)) printm('Difference: {0:.4f}%'.format( abs(Q_nomorse-Q_morse)/Q_morse*100)) assert abs(Q_nomorse-Q_morse)/Q_morse < rtol
def test_sPlanck_conversions(verbose=True, *args, **kwargs): if verbose: printm("Testing sPlanck conversions: ") s_cm = sPlanck(1000, 10000, T=1500, eps=0.3) I_cm2cm = s_cm.get("radiance_noslit", Iunit="mW/cm2/sr/cm_1")[1] I_cm2nm = s_cm.get("radiance_noslit", Iunit="mW/cm2/sr/nm")[1] s_nm = sPlanck(1000, 10000, T=1500, eps=0.3) I_nm2nm = s_nm.get("radiance_noslit", Iunit="mW/cm2/sr/nm")[1] I_nm2cm = s_nm.get("radiance_noslit", Iunit="mW/cm2/sr/cm_1")[1] assert np.allclose(I_cm2cm, I_nm2cm) assert np.allclose(I_nm2nm, I_cm2nm)
def test_cache_file_generation_and_update(verbose=True, *args, **kwargs): ''' Test that cache file process works correctly, using CO as an example. Expected behavior: - generated when 'regen' - updated when using 'True' but input parameters have changed - an error is raised when 'force' is used but input parameters have changed ''' # TODO: move it as a test of cache_files.py in RADIS S = Molecules['CO'][1]['X'] from os.path import exists, getmtime if verbose: printm('Testing behavior for Energy cache file with use_cached=') # Create a first cache file (or use existing one) db = PartFunc_Dunham(S, vmax=12, vmax_morse=48, Jmax=300, use_cached=True, verbose=verbose) # .. test file was created cachefile = db.cachefile assert exists(cachefile) last_modif = getmtime(cachefile) if verbose: printm('... True: energy cache file has been used/created (vmax=12)') # Force recalculating (using same inputs, but use_cached='regen') db = PartFunc_Dunham(S, vmax=12, vmax_morse=48, Jmax=300, use_cached='regen', verbose=verbose) # ... test that cache file was updated cachefile = db.cachefile assert getmtime(cachefile) > last_modif last_modif = getmtime(cachefile) if verbose: print("... 'regen': energy cache file has been created again (vmax=12)") # Recompute with different parameters # ... if using 'force', ensures that an error is raised with pytest.raises(DeprecatedFileError): # DeprecatedFileError is expected db = PartFunc_Dunham(S, vmax=11, vmax_morse=48, Jmax=300, use_cached='force', verbose=verbose) if verbose: printm( "... 'force': an error was correctly triggered for new input parameters (vmax=11)") # ... if using 'True', ensures that cache file is updated db = PartFunc_Dunham(S, vmax=11, vmax_morse=48, Jmax=300, use_cached=True, verbose=verbose) assert getmtime(cachefile) > last_modif if verbose: printm('... True: cache file was updated for new input parameters (vmax=11)')
def test_reduced_CDSD_calc_vs_tab(verbose=True, warnings=True, *args, **kwargs): """Test 1: compare calculated PartFunc to the tabulated one Version where we use the reduced set of CO2 levels (< 3000 cm-1)""" from radis.misc.config import getDatabankEntries iso = 1 database = "HITEMP-CO2-HAMIL-TEST" # Compare tab to hardcoded parfunc = getDatabankEntries(database)["parfunc"] Qf = PartFuncCO2_CDSDtab(iso, parfunc) assert np.isclose(Qf.at(300), 291.0447781984652, rtol=0.001) assert np.isclose(Qf.at(3000), 114689.88454184022, rtol=0.001) if verbose: printm("Tested CDSD tabulated is correct: OK") # Compare tab to calculated energies = getDatabankEntries(database)["levels"] levelsfmt = getDatabankEntries(database)["levelsfmt"] Qfc = PartFuncCO2_CDSDcalc( energies[iso], levelsfmt=levelsfmt, isotope=iso, use_cached=True, verbose=verbose, ) assert np.isclose( Qf.at(300), Qfc.at(300), rtol=0.01 ) # reduced rtol to accommodate for the reduced set of levels in the test set # assert np.isclose(Qf.at(3000), Qfc.at(3000), rtol=0.001) # of course doesnt work with the reduced set of levels in the test set if verbose: printm("Tested CDSD Q_calc vs Q_tab give same output: OK")
def test_recompute_Q_from_QvibQrot_CDSD_PC(verbose=True, warnings=True, *args, **kwargs): ''' Calculate vibrational and rotational partition functions: - in CDSD with (p,c) convention for vibrational levels - under nonequilibrium Recompute total partition function, and compare Test if partition function can be recomputed correctly from vibrational populations and rotational partition function (note that we are in a coupled case so partition function is not simply the product of Qvib, Qrot) ''' from radis.misc.config import getDatabankEntries iso = 1 try: energies = getDatabankEntries('CDSD-HITEMP-PC')['levels'] levelsfmt = getDatabankEntries('CDSD-HITEMP-PC')['levelsfmt'] Tvib = 1500 Trot = 300 Qf = PartFuncCO2_CDSDcalc(energies[iso], isotope=iso, use_cached=True, levelsfmt=levelsfmt) Q = Qf.at_noneq(Tvib, Trot) _, Qvib, dfQrot = Qf.at_noneq(Tvib, Trot, returnQvibQrot=True) if verbose: printm('Q', Q) if verbose: printm('Qvib', Qvib) # 1) Test Q vs Q recomputed from Qrot, Qvib # Recompute Qtot df = dfQrot Q2 = ((df.gvib * exp(-df.Evib * hc_k / Tvib)) * df.Qrot).sum() # Todo: non Boltzmann case assert np.isclose(Q, Q2) if verbose: printm('Tested Q vs recomputed from (Qvib, Qrot) are the same: OK') return True except DatabankNotFound as err: assert IgnoreMissingDatabase(err, __file__, warnings)
def test_Q_1Tvib_vs_Q_3Tvib(T=1500, verbose=True, warnings=True, *args, **kwargs): """Test if partition function calculated in 1-Tvib mode returns the same result as partition function calculated in 3-Tvib mode """ b = True # input M = "CO2" I = 1 # isotope S = Molecules[M][I]["X"] Qf = PartFunc_Dunham(S, use_cached=True) df = Qf.df # First make sure energies match if not (df.Evib == df.Evib1 + df.Evib2 + df.Evib3).all(): b *= False if warnings: printm( "WARNING in test_Q_1Tvib_vs_Q_3Tvib: Evib != Evib1 + Evib2 + Evib3" ) # Then calculate Q vs Q3T Q = Qf.at_noneq(T, T) if verbose: printm("Q", Q) Q3T = Qf.at_noneq_3Tvib((T, T, T), T) if verbose: printm("Q3T", Q3T) assert np.isclose(Q, Q3T) if verbose: printm( "Tested Q in 1-Tvib vs Q in 3-Tvib modes (T={0:.0f}K): OK".format( T)) return True
def test_recompute_Q_from_QvibQrot_Dunham_Evib123_Erot(verbose=True, warnings=True, *args, **kwargs): """Calculate vibrational and rotational partition functions: - with Dunham expansions. Evib, Erot = (Evib1+Evib2+Evib3, Erot) - under nonequilibrium Calculate total rovibrational partition function, and compare Test if partition function can be recomputed correctly from vibrational populations and rotational partition function (note that we are in a coupled case so partition function is not simply the product of Qvib, Qrot) """ iso = 1 Tvib = 1500 Trot = 300 S = Molecules["CO2"][iso]["X"] Qf = PartFunc_Dunham( S, use_cached=True, group_energy_modes_in_2T_model={ "CO2": (["Evib1", "Evib2", "Evib3"], ["Erot"]) }, ) Q = Qf.at_noneq(Tvib, Trot) _, Qvib, dfQrot = Qf.at_noneq(Tvib, Trot, returnQvibQrot=True) if verbose: printm("Q", Q) if verbose: printm("Qvib", Qvib) # 1) Test Q vs Q recomputed from Qrot, Qvib # Recompute Qtot df = dfQrot Q2 = ((df.gvib * exp(-df.Evib * hc_k / Tvib)) * df.Qrot).sum() # Todo: non Boltzmann case assert np.isclose(Q, Q2) if verbose: printm("Tested Q vs recomputed from (Qvib, Qrot) are the same: OK") return True
sf.warnings["MissingSelfBroadeningWarning"] = "ignore" # sf.load_databank('HITEMP-CO2-DUNHAM') sf.load_databank("HITEMP-CO2-TEST") # Calculate with Klarenaar fitted values T12 = 517 T3 = 2641 Trot = 491 s = sf.non_eq_spectrum((T12, T12, T3), Trot, Ttrans=Trot, vib_distribution="treanor", name="RADIS") if plot: plot_diff(s, s_exp, "transmittance_noslit") # plt.savefig('test_CO2_3Tvib_vs_klarenaar.png') assert get_residual(s, s_exp, "transmittance_noslit", ignore_nan=True) < 0.003 return True if __name__ == "__main__": printm( "test_CO2_3Tvib_vs_klarenaar:", test_klarenaar_validation_case(verbose=True, plot=True), )
def test_temperature_units_conversion( input_temperature, expected_temperature_K, verbose=True, *args, **kwargs ): setup_test_line_databases() # add HITRAN-CO-TEST in ~/.radis if not there sf = SpectrumFactory( wavelength_min=4300, wavelength_max=4500, wstep=0.01, cutoff=1e-30, pressure=1, mole_fraction=1, isotope=[1], Tref=300 * u.K, verbose=verbose, ) sf.load_databank("HITRAN-CO-TEST") s = sf.eq_spectrum( Tgas=input_temperature, pressure=20 * u.mbar, path_length=1 * u.mm ) assert np.isclose(s.conditions["Tgas"], expected_temperature_K) assert np.isclose(s.conditions["path_length"], 0.1) # cm assert np.isclose(s.conditions["pressure_mbar"], 20) # -------------------------- if __name__ == "__main__": printm("Testing factory:", pytest.main(["test_factory.py"])) # printm('Testing factory:', pytest.main(['test_factory.py', '-k', 'test_wavenumber_units_conversion']))
def test_power_integral(verbose=True, warnings=True, *args, **kwargs): """ Test direct calculation of power integral from Einstein coefficients matches integration of broadened spectrum in the optically thin case We compare: - direct calculation of power integral with equilibrium code :meth:`~radis.lbl.SpectrumFactory.optically_thin_power` (T) - direct calculation of power integral with non equilibrium code :meth:`~radis.lbl.SpectrumFactory.optically_thin_power` (T,T) - numerical integration of non equilibrium spectrum under optically thin conditions: :meth:`~radis.spectrum.spectrum.Spectrum.get_power` Test passes if error < 0.5% """ if verbose: printm(">>> _test_power_integral") setup_test_line_databases() # add HITRAN-CO-TEST in ~/.radis if not there sf = SpectrumFactory( wavelength_min=4300, wavelength_max=4666, wstep=0.001, parallel=False, bplot=False, cutoff=1e-30, path_length=10, mole_fraction=400e-6, isotope=[1], db_use_cached=True, broadening_max_width=10, verbose=verbose, ) sf.warnings.update( { "MissingSelfBroadeningWarning": "ignore", "OutOfRangeLinesWarning": "ignore", "HighTemperatureWarning": "ignore", } ) sf.load_databank("HITRAN-CO-TEST") unit = "µW/sr/cm2" T = 600 # Calculate: # ... direct calculation of power integral with equilibrium code Peq = sf.optically_thin_power(Tgas=T, unit=unit) # ... direct calculation of power integral with non equilibrium code Pneq = sf.optically_thin_power(Tvib=T, Trot=T, unit=unit) # ... numerical integration of non equilibrium spectrum under optically thin # ... conditions sf.input.self_absorption = False s = sf.non_eq_spectrum(T, T) assert s.conditions["self_absorption"] == False # Compare err = abs(Peq - s.get_power(unit=unit)) / Peq if verbose: printm("Emission integral:\t{0:.4g}".format(Peq), unit) printm("Emission (noneq code):\t{0:.4g}".format(Pneq), unit) printm("Integrated spectrum:\t{0:.4g}".format(s.get_power(unit=unit)), unit) printm("Error: {0:.2f}%".format(err * 100)) assert err < 0.005
test_reduced_CDSD_calc_noneq(verbose=verbose, warnings=warnings) # Test 5a, 5b: recompute Q from QvibQrot test_recompute_Q_from_QvibQrot_Dunham_Evib123_Erot(verbose=verbose, warnings=warnings) test_recompute_Q_from_QvibQrot_Dunham_Evib3_Evib12Erot(verbose=verbose, warnings=warnings) test_recompute_Q_from_QvibQrot_CDSD_PC(verbose=verbose, warnings=warnings) # test_recompute_Q_from_QvibQrot_CDSD_PCN(verbose=verbose, warnings=warnings) # ignore in released version # Test 6: test_Q_1Tvib_vs_Q_3Tvib(verbose=verbose, warnings=warnings) # Test 7: test_Morse_Potential_effect_CO(verbose=verbose, warnings=warnings) # Test 8: Regenerates levels file if it's manually changed test_levels_regeneration(verbose=True, warnings=True, *args, **kwargs) return True if __name__ == "__main__": printm("Testing parfunc: {0}".format(_run_testcases())) verbose = True warnings = True # test_CDSD_calc_vs_tab(verbose=verbose, warnings=warnings) # test_recompute_Q_from_QvibQrot_CDSD_PC(verbose=verbose, warnings=warnings) # test_recompute_Q_from_QvibQrot_CDSD_PCN(verbose=verbose, warnings=warnings)
path_length=1, # doesnt change anything wstep=wstep, pressure=p, isotope="1", chunksize="DLM", warnings={ "MissingSelfBroadeningWarning": "ignore", "NegativeEnergiesWarning": "ignore", "HighTemperatureWarning": "ignore", "GaussianBroadeningWarning": "ignore", }, ) sf._broadening_method = "fft" sf.load_databank( path=getTestFile("cdsd_hitemp_09_fragment.txt"), format="cdsd-4000", parfuncfmt="hapi", ) s_cpu = sf.eq_spectrum(Tgas=T) s_gpu = sf.eq_spectrum_gpu(Tgas=T) s_cpu.crop(wmin=2284.2, wmax=2284.8) # remove edge lines s_gpu.crop(wmin=2284.2, wmax=2284.8) assert s_cpu.compare_with(s_gpu, spectra_only=True, rtol=0.07, plot=False) # set the appropriate tolerance # -------------------------- if __name__ == "__main__": printm("Testing GPU spectrum calculation:", pytest.main(["test_gpu.py"]))
def test_spec_generation(plot=True, verbose=2, warnings=True, *args, **kwargs): """ Test spectrum generation Can be used as a base to generate spectra in your codes Non-regression test: compare with past version (see conditions below) Compare results from a reference case to results calculated on 30/12/2017 This is not a validation case (30/12/2017 results are not a physically validated case), but it makes sure results dont change over time Conditions (30/12/2017):: Physical Conditions ---------------------------------------- Tgas 300 K Trot 300 K Tvib 300 K pressure 1.01325 bar isotope 1,2 mole_fraction 1 molecule CO2 path_length 1 cm wavelength_max 4400.0 nm wavelength_min 4150.0 nm wavenum_max 2409.6385542168673 cm-1 wavenum_min 2272.7272727272725 cm-1 Computation Parameters ---------------------------------------- Tref 296 K broadening_max_width 10 cm-1 cutoff 1e-25 cm-1/(#.cm-2) db_assumed_sorted True db_use_cached True dbformat cdsd dbpath # USER-DEPENDANT: CDSD-HITEMP fillmissinglevelswithzero False levelsfmt cdsd levelspath # USER-DEPENDANT: CDSD-4000 medium vacuum parfuncfmt cdsd parfuncpath # USER-DEPENDANT: CDSD-4000 rot_distribution boltzmann self_absorption True vib_distribution boltzmann wavenum_max_calc 2414.6385542168673 cm-1 wavenum_min_calc 2267.7272727272725 cm-1 waveunit cm-1 wstep 0.01 cm-1 ---------------------------------------- Notes ----- Performance test. How long it tooks to calculate this Spectrum? Test with cutoff 1e-25, broadening_max_width=10 - 0.9.15: >>> 33s - 0.9.16*: (replaced groupby().apply() with iteration over indexes) >>> 32s [but large impact expected on big files] - 0.9.16*: (upgraded cache files to h5) >>> 25s - 0.9.16*: (also added h5 cache file for levels) >>> 21s - 0.9.16*: (with Whiting slit voigt function) >>> 5.8s Test with cutoff 1e-27, broadening_max_width=50 : ("Spectrum calculated in ... ", including database loading time) - 0.9.16*: (same code as last) >>> 12.5s including 7.6s of broadening - 0.9.16**: (with pseudo_continuum_threshold=0.01) >>> 7.8s including 2.3s of broadening - 0.9.18 (normal code, no pseudo continuum). >>> ? - 0.9.21 (normal code) >>> 13.7s, including 8.7s of broadening (with pseudo_continuum_threshold=0.01) >>> 4.3s, including 2.6s of broadening - 0.9.21* >>> 14.0s (added the manual lineshape normalization instead of Whitings's polynomial) - 0.9.22 (normal code) >>> 11.3s (without energy level lookup, for eq. calculations) (with pseudo_continuum_threshold=0.01) >>> 5.9s - 0.9.23 (normal code) >>> 7.2s (added jit in Voigt broadening) >>> 7.1s (chunksize = None) (and much faster for more lines) (with pseudo_continuum_threshold=0.01) >>> 4.9s RADIS: - 0.9.19 (normal code) >>> 6.3 s - 0.9.20 (normal code) >>> 6.3 s (with pseudo_continuum_threshold=0.01) >>> ??? (with DLM) >>> 2.3 s """ if plot: # Make sure matplotlib is interactive so that test are not stuck in pytest plt.ion() from time import time t0 = time() if verbose: printm(">>> _test_spec_generation") # This is how you get a spectrum (see calc.py for front-end functions # that do just that) sf = SpectrumFactory( wavelength_min=4150, wavelength_max=4400, parallel=False, bplot=False, cutoff=1e-27, isotope="1,2", db_use_cached=True, broadening_max_width=50, # chunksize='DLM', # pseudo_continuum_threshold=0.01, medium="vacuum", verbose=verbose, ) sf.warnings["MissingSelfBroadeningWarning"] = "ignore" sf.warnings["NegativeEnergiesWarning"] = "ignore" sf.load_databank( "HITEMP-CO2-DUNHAM", load_energies=False, # no need to load energies at equilibrium ) s = sf.eq_spectrum(Tgas=300) if verbose: printm( ">>> _test_spec_generation: Spectrum calculated in {0:.2f}s".format( time() - t0 ) ) if plot: plt.figure(fig_prefix + "Reference spectrum CDSD-HITEMP (radiance)") # Iunit is arbitrary. Use whatever makes sense s.plot("radiance_noslit", Iunit="µW/cm2/sr/nm", nfig="same") s.rescale_path_length(0.01) # Here we get some extra informations: if plot: sf.plot_broadening(i=0) # show broadening of one line plt.xlim((2267.20, 2268.30)) # Compare with harcoded results # ... code previously used to export hardcoded results: # ... and header contains all input conditions: # np.savetxt('output.txt', np.vstack(s.get('abscoeff', wunit='nm')).T[::10]) # print(s) # ................ from radis.test.utils import getTestFile wref, Iref = np.loadtxt(getTestFile("CO2abscoeff_300K_4150_4400nm.txt")).T match_reference = np.allclose(s.get("abscoeff", wunit="nm")[1][::10], Iref) if not match_reference: # give some more information before raising error printm( "Error: {0:.2f}%".format( np.mean(abs(s.get("abscoeff", wunit="nm")[1][::10] / Iref - 1)) * 100 ) ) # Store the faulty spectrum s.store( "test_factory_failed_{0}.spec".format(radis.get_version()), if_exists_then="replace", ) # Plot comparison if plot: plt.figure(fig_prefix + "Reference spectrum (abscoeff)") # , show_points=True) # show_points to have an s.plot( "abscoeff", wunit="nm", medium="air", nfig="same", lw=3, label="RADIS, this version", ) # idea of the resolution plt.plot(wref, Iref, "or", ms=3, label="version NEQ 0.9.20 (12/05/18)") plt.legend() plt.title("All close: {0}".format(match_reference)) plt.tight_layout() # Another example, at higher temperature. # Removed because no test is associated with it and it takes time for # nothing # s2 = sf.non_eq_spectrum(Tvib=1000, Trot=300) # if plot: s2.plot('abscoeff', wunit='nm') if verbose: printm( "Spectrum calculation (no database loading) took {0:.1f}s\n".format( s.conditions["calculation_time"] ) ) printm("_test_spec_generation finished in {0:.1f}s\n".format(time() - t0)) assert match_reference