def test_transition(): '''Test generic transitions''' # labels are skipped in comparisons and hashing t1 = Transition(IonType.S, 1, 0) t2 = Transition(IonType.S, 1, 0, label_ion='Yb', label_i='ES', label_f='GS') assert t1 == t2 assert hash(t1) == hash(t2) # different states yield different Transitions t3 = Transition(IonType.S, 2, 0) assert t3 != t1 assert t3 != t2 assert hash(t3) != hash(t1) assert hash(t3) != hash(t2) # different ions yield different Transitions t4 = Transition(IonType.A, 1, 0) assert t4 != t1 assert hash(t4) != hash(t1)
def test_optim_ok_proc(data_proc): # ok data = data_proc[0] + '''optimization: processes: [ETU53, {}] '''.format(data_proc[1]) with temp_config_filename(data) as filename: cte = settings.load(filename) ETU53 = EneryTransferProcess([Transition(IonType.A, 5, 6), Transition(IonType.A, 3, 1)], mult=6, strength=2.5e8, name='ETU53') assert cte.optimization['processes'] == [ETU53, data_proc[2]]
def test_ET_process(): '''Test excitation transitions''' strength = 1e9 t1 = Transition(IonType.S, 1, 0) t2 = Transition(IonType.S, 0, 1) et1 = EneryTransferProcess([t1, t2], mult=6, strength=strength) et2 = EneryTransferProcess([t1, t2], mult=8, strength=strength) assert et1 != et2 t3 = Transition(IonType.S, 0, 1, label_ion='Yb') et3 = EneryTransferProcess([t1, t3], mult=6, strength=strength) assert et1 == et3 assert et1.strength == strength assert et1.strength_avg == strength strength_avg = 1e3 et4 = EneryTransferProcess([t1, t2], mult=8, strength=strength, strength_avg=strength_avg) assert et4.strength_avg == strength_avg
def setup_cte_sim(setup_cte): '''Load the settings for simulations''' # test_sim_dyn_2S_2A was created with these settings setup_cte['decay']['branching_A'].add(DecayTransition(IonType.A, 3, 2, branching_ratio=0.1)) setup_cte['energy_transfer'] = { 'CR50': EneryTransferProcess([Transition(IonType.A, 5, 3), Transition(IonType.A, 0, 2)], mult=6, strength=2893199540.0), 'ETU53': EneryTransferProcess([Transition(IonType.A, 5, 6), Transition(IonType.A, 3, 1)], mult=6, strength=254295690.0), 'BackET': EneryTransferProcess([Transition(IonType.A, 3, 0), Transition(IonType.S, 0, 1)], mult=6, strength=4502.20614), 'EM': EneryTransferProcess([Transition(IonType.S, 1, 0), Transition(IonType.S, 0, 1)], mult=6, strength=45022061400.0), 'ETU1': EneryTransferProcess([Transition(IonType.S, 1, 0), Transition(IonType.A, 0, 2)], mult=6, strength=10000.0) } return Settings.load_from_dict(setup_cte)
def test_decay_transition(): '''Test generic decay or branching transitions''' # labels are skipped in comparisons and hashing t1 = DecayTransition(IonType.S, 1, 0, branching_ratio=0.4) t2 = DecayTransition(IonType.S, 1, 0, branching_ratio=0.4, label_ion='Yb', label_i='ES', label_f='GS') assert t1 == t2 assert hash(t1) == hash(t2) t1_dec = DecayTransition(IonType.S, 1, 0, decay_rate=1e5) t2_dec = DecayTransition(IonType.S, 1, 0, decay_rate=1e5, label_ion='Yb', label_i='ES', label_f='GS') assert t1_dec == t2_dec assert hash(t1_dec) == hash(t2_dec) # different branching_ratios: not equal but same hash t3 = DecayTransition(IonType.S, 1, 0, branching_ratio=0.1) assert t3 != t1 assert t3 != t2 assert hash(t3) == hash(t1) assert hash(t3) == hash(t2) # compare Transitions with DecayTransitions, ignores values t4 = Transition(IonType.S, 1, 0) assert t1 == t4 assert t4 == t1 assert hash(t1) == hash(t4)
def setup_cte(): '''Load the cte data structure''' class Cte(dict): __getattr__= dict.__getitem__ __setattr__= dict.__setitem__ __delattr__= dict.__delitem__ cte = Cte({'version': 1, 'energy_transfer': OrderedDict({ 'CR50': EneryTransferProcess([Transition(IonType.A, 5, 3), Transition(IonType.A, 0, 2)], mult=6, strength=887920884.0, name='CR50'), 'ETU53': EneryTransferProcess([Transition(IonType.A, 5, 6), Transition(IonType.A, 3, 1)], mult=6, strength=450220614.0, name='ETU53'), 'ETU55': EneryTransferProcess([Transition(IonType.A, 5, 6), Transition(IonType.A, 5, 4)], mult=6, strength=0.0, name='ETU55'), 'BackET': EneryTransferProcess([Transition(IonType.A, 3, 0), Transition(IonType.S, 0, 1)], mult=6, strength=4502.20614, name='BackET'), 'EM': EneryTransferProcess([Transition(IonType.S, 1, 0), Transition(IonType.S, 0, 1)], mult=6, strength=45022061400.0, name='EM'), 'ETU1': EneryTransferProcess([Transition(IonType.S, 1, 0), Transition(IonType.A, 0, 2)], mult=6, strength=10000.0, name='ETU1'), 'coop1': EneryTransferProcess([Transition(IonType.S, 1, 0), Transition(IonType.S, 1, 0), Transition(IonType.A, 0, 5)], mult=6, strength=1000.0, name='coop1') }), 'decay': { 'branching_A': {DecayTransition(IonType.A, 2, 1, branching_ratio=0.4), DecayTransition(IonType.A, 3, 1, branching_ratio=0.3), DecayTransition(IonType.A, 4, 3, branching_ratio=0.999), DecayTransition(IonType.A, 5, 1, branching_ratio=0.15), DecayTransition(IonType.A, 5, 2, branching_ratio=0.16), DecayTransition(IonType.A, 5, 3, branching_ratio=0.04), DecayTransition(IonType.A, 5, 4, branching_ratio=0.0), DecayTransition(IonType.A, 6, 1, branching_ratio=0.43)}, 'branching_S': {DecayTransition(IonType.S, 1, 0, branching_ratio=1.0)}, 'decay_A': {DecayTransition(IonType.A, 1, 0, decay_rate=83.33333333333333), DecayTransition(IonType.A, 2, 0, decay_rate=40000.0), DecayTransition(IonType.A, 3, 0, decay_rate=500.0), DecayTransition(IonType.A, 4, 0, decay_rate=500000.0), DecayTransition(IonType.A, 5, 0, decay_rate=1315.7894736842104), DecayTransition(IonType.A, 6, 0, decay_rate=14814.814814814814)}, 'decay_S': {DecayTransition(IonType.S, 1, 0, decay_rate=400.0)} }, 'excitations': { 'NIR_1470': [Excitation(IonType.A, 5, 6, False, 9/5, 2e-4, 1e7, 1e-8)], 'NIR_800': [Excitation(IonType.A, 0, 3, False, 13/9, 0.0044, 1e7, 1e-8), Excitation(IonType.A, 2, 5, False, 11/9, 0.002, 1e7, 1e-8)], 'NIR_980': [Excitation(IonType.S, 0, 1, False, 4/3, 0.0044, 1e7, 1e-8)], 'Vis_473': [Excitation(IonType.A, 0, 5, True, 13/9, 0.00093, 1e6, 1e-8)]}, 'ions': {'activators': 113, 'sensitizers': 0, 'total': 113}, 'lattice': {'A_conc': 0.3, 'N_uc': 20, 'S_conc': 0.3, 'a': 5.9738, 'alpha': 90.0, 'b': 5.9738, 'beta': 90.0, 'c': 3.5297, 'gamma': 120.0, 'd_max': 100.0, 'd_max_coop': 25.0, 'name': 'bNaYF4', 'sites_occ': [1.0, 0.5], 'sites_pos': [(0.0, 0.0, 0.0), (0.6666666666666666, 0.3333333333333333, 0.5)], 'spacegroup': 'P-6'}, 'no_console': False, 'no_plot': False, 'concentration_dependence': {'concentrations': [(0.0, 0.1), (0.0, 0.3), (0.0, 0.5), (0.0, 1.0)], 'N_uc_list': [65, 40, 35, 25]}, 'power_dependence': [10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, 10000000.0], 'optimization': {'method': 'SLSQP', 'processes': [EneryTransferProcess([Transition(IonType.A, 5, 3), Transition(IonType.A, 0, 2)], mult=6, strength=2893199540.0, name='CR50'), DecayTransition(IonType.A, 3, 1, branching_ratio=0.3)], 'options': {'tol': 1e-3, 'N_points': 30, 'min_factor': 1e-2, 'max_factor': 2}, 'excitations': ['Vis_473', 'NIR_980'] }, 'simulation_params': {'N_steps': 1000, 'N_steps_pulse': 2, 'atol': 1e-15, 'rtol': 0.001}, 'states': {'activator_ion_label': 'Tm', 'activator_states': 7, 'activator_states_labels': ['3H6', '3F4', '3H5', '3H4', '3F3', '1G4', '1D2'], 'sensitizer_ion_label': 'Yb', 'sensitizer_states': 2, 'sensitizer_states_labels': ['GS', 'ES'], 'energy_states': 791} } ) cte['config_file'] = ''' version: 1 lattice: name: bNaYF4 N_uc: 20 S_conc: 0.3 # concentration A_conc: 0.3 # unit cell a: 5.9738 # distances in Angstrom b: 5.9738 c: 3.5297 alpha: 90 # angles in degree beta: 90 gamma: 120 spacegroup: P-6 # the number is also ok for the spacegroup # info about sites sites_pos: [[0, 0, 0], [2/3, 1/3, 1/2]] sites_occ: [1, 1/2] # optional # maximum distance of interaction for normal ET and for cooperative # if not present, both default to infinite d_max: 100.0 # it's strongly advised to keep this number low, # the number of coop interactions is very large (~num_atoms^3) d_max_coop: 25.0 states: # all fields here are mandatory, # leave empty if necessary (i.e.: just "sensitizer_ion_label" on a line), but don't delete them sensitizer_ion_label: Yb sensitizer_states_labels: [GS, ES] activator_ion_label: Tm activator_states_labels: [3H6, 3F4, 3H5, 3H4, 3F3, 1G4, 1D2] excitations: # the excitation label can be any text # at this point, only one active excitation is suported # the t_pulse value is only mandatory for the dynamics, it's ignored in the steady state Vis_473: active: True power_dens: 1e6 # power density W/cm^2 t_pulse: 1e-8 # pulse width, seconds process: Tm(3H6) -> Tm(1G4) # both ion labels are required degeneracy: 13/9 # initial_state_g/final_state_g pump_rate: 9.3e-4 # cm2/J NIR_1470: active: False power_dens: 1e7 # power density W/cm^2 t_pulse: 1e-8 # pulse width, seconds process: Tm(1G4) -> Tm(1D2) # both ion labels are required degeneracy: 9/5 # initial_state_g/final_state_g pump_rate: 2e-4 # cm2/J NIR_980: active: False power_dens: 1e7 # power density W/cm^2 t_pulse: 1e-8 # pulse width, seconds process: Yb(GS)->Yb(ES) degeneracy: 4/3 pump_rate: 4.4e-3 # cm2/J NIR_800: # ESA: list of processes, degeneracies and pump rates active: False power_dens: 1e7 # power density W/cm^2 t_pulse: 1e-8 # pulse width, seconds process: [Tm(3H6)->Tm(3H4), Tm(3H5)->Tm(1G4)] degeneracy: [13/9, 11/9] pump_rate: [4.4e-3, 2e-3] # cm2/J sensitizer_decay: # lifetimes in s ES: 2.5e-3 activator_decay: # lifetimes in s 3F4: 12e-3 3H5: 25e-6 3H4: 2e-3 3F3: 2e-6 1G4: 760e-6 1D2: 67.5e-6 activator_branching_ratios: # 3H5 and 3H4 to 3F4 3H5->3F4: 0.4 3H4->3F4: 0.3 # 3F3 to 3H4 3F3->3H4: 0.999 # 1G4 to 3F4, 3H5, 3H4 and 3F3 1G4->3F4: 0.15 1G4->3H5: 0.16 1G4->3H4: 0.04 1G4->3F3: 0.00 # 1D2 to 3F4 1D2->3F4: 0.43 energy_transfer: CR50: process: Tm(1G4) + Tm(3H6) -> Tm(3H4) + Tm(3H5) multipolarity: 6 strength: 8.87920884e+08 ETU53: process: Tm(1G4) + Tm(3H4) -> Tm(1D2) + Tm(3F4) multipolarity: 6 strength: 4.50220614e+08 ETU55: process: Tm(1G4) + Tm(1G4) -> Tm(1D2) + Tm(3F3) multipolarity: 6 strength: 0 # 4.50220614e+7 ETU1: process: Yb(ES) + Tm(3H6) -> Yb(GS) + Tm(3H5) multipolarity: 6 strength: 1e4 BackET: process: Tm(3H4) + Yb(GS) -> Tm(3H6) + Yb(ES) multipolarity: 6 strength: 4502.20614 EM: process: Yb(ES) + Yb(GS) -> Yb(GS) + Yb(ES) multipolarity: 6 strength: 4.50220614e+10 coop1: process: Yb(ES) + Yb(ES) + Tm(3H6) -> Yb(GS) + Yb(GS) + Tm(1G4) multipolarity: 6 strength: 1000 optimization: processes: [CR50, 3H4->3F4] method: SLSQP options: tol: 1e-3 N_points: 30 min_factor: 1e-2 max_factor: 2 excitations: [Vis_473, NIR_980] simulation_params: # default values for certain parameters in the ODE solver rtol: 1e-3 # relative tolerance atol: 1e-15 # absolute tolerance N_steps_pulse: 2 # number of steps for the pulse (only for dynamics) N_steps: 1000 # number of steps for relaxation (also for steady state) power_dependence: [1e1, 1e7, 7] concentration_dependence: concentrations: [[0], [0.1, 0.3, 0.5, 1.0]] N_uc_list: [65, 40, 35, 25] ''' cte['no_console'] = False cte['no_plot'] = False return cte
@author: Pedro """ import pytest import numpy as np import warnings from lmfit import Parameters import simetuc.optimize as optimize import simetuc.simulations as simulations from simetuc.util import IonType, DecayTransition, EneryTransferProcess, Transition from simetuc.util import temp_bin_filename, temp_config_filename B_43 = DecayTransition(IonType.A, 3, 1, branching_ratio=0.3) CR50 = EneryTransferProcess([Transition(IonType.A, 5, 3), Transition(IonType.A, 0, 2)], mult=6, strength=2893199540.0, name='CR50') def idfn_param(param): '''Returns the name of the test according to the parameters''' return 'method={}'.format(param) def idfn_avg(param): '''Returns the name of the test according to the parameters''' return 'avg={}'.format(param) def idfn_proc(param): '''Returns the name of the test according to the parameters''' return 'num={}'.format(len(param)) @pytest.mark.parametrize('method', ['COBYLA', 'L-BFGS-B', 'TNC', 'SLSQP', 'brute', 'leastsq'], ids=idfn_param) @pytest.mark.parametrize('function', ['optimize_dynamics', 'optimize_concentrations']) @pytest.mark.parametrize('average', [True, False], ids=idfn_avg)
settings.load(filename) assert excinfo.match(r"Wrong labels in optimization: processes") assert excinfo.type == settings.LabelError def test_optim_wrong_B_proc_label(): '''Wrong branching ration optimization process''' data = data_ET_ok + '''optimization: processes: [3H145->3F4] ''' with pytest.raises(settings.LabelError) as excinfo: # wrong ET process label with temp_config_filename(data) as filename: settings.load(filename) assert excinfo.match(r"Wrong labels in optimization: processes") assert excinfo.type == settings.LabelError @pytest.mark.parametrize('data_proc', [(data_ET_ok, '3H5->3F4', Transition(IonType.A, 2, 1)), (data_ET_ok_full_S, '3ES->1ES', Transition(IonType.S, 3, 1))]) def test_optim_ok_proc(data_proc): # ok data = data_proc[0] + '''optimization: processes: [ETU53, {}] '''.format(data_proc[1]) with temp_config_filename(data) as filename: cte = settings.load(filename) ETU53 = EneryTransferProcess([Transition(IonType.A, 5, 6), Transition(IonType.A, 3, 1)], mult=6, strength=2.5e8, name='ETU53') assert cte.optimization['processes'] == [ETU53, data_proc[2]] def test_optim_method(): # ok data = data_ET_ok + '''optimization: method: COBYLA'''
def _parse_ET(parsed_settings: Settings) -> Dict: '''Parse the energy transfer processes''' dict_states = parsed_settings.states sensitizer_ion_label = dict_states['sensitizer_ion_label'] activator_ion_label = dict_states['activator_ion_label'] list_ion_label = [sensitizer_ion_label, activator_ion_label] sensitizer_labels = dict_states['sensitizer_states_labels'] activator_labels = dict_states['activator_states_labels'] tuple_state_labels = (sensitizer_labels, activator_labels) # ET PROCESSES. ET_dict = {} # type: Dict for num, (name, et_subdict) in enumerate( parsed_settings['energy_transfer'].items()): process = et_subdict['process'] mult = et_subdict['multipolarity'] strength = et_subdict['strength'] strength_avg = et_subdict.get('strength_avg', None) # get the ions and states labels involved list_init_final = _get_ion_and_state_labels(process) list_ions_num = [ _get_ion_index(list_ion_label, ion) for ion, label in list_init_final ] list_indices = [ _get_state_index(tuple_state_labels[ion_num], label, section='ET process', process=process, num=num) for ion_num, (ion_label, label) in zip(list_ions_num, list_init_final) ] # list with all information about this ET process # tuples with ion and states labels and numbers list_ion_states = [(ion_label, state_label, ion_num, state_num) for (ion_label, state_label), ion_num, state_num in zip(list_init_final, list_ions_num, list_indices)] # fold the list of ion, state labels in two # so that each tuple has two tuples with the states belonging to the same transition folded_lst = list( zip(list_ion_states[:len(list_ion_states) // 2], list_ion_states[len(list_ion_states) // 2:])) # store the data trans_lst = [ Transition(IonType(tuple_i[2]), tuple_i[3], tuple_f[3], label_ion=tuple_i[0], label_i=tuple_i[1], label_f=tuple_f[1]) for tuple_i, tuple_f in folded_lst ] # print(trans_lst) ET_dict[name] = EneryTransferProcess(trans_lst, mult=mult, strength=strength, strength_avg=strength_avg, name=name) return ET_dict