def forster_coupling_extended(initial, final, ref_index=1, transitions=(), longitude=3, n_divisions=300): """ Compute Forster coupling in eV :param initial: initial states :param final: final states :param longitude: extension length of the dipole :param n_divisions: number of subdivisions. To use with longitude. Increase until convergence. :return: Forster coupling """ d_transition = Transition(initial[0], final[1]) a_transition = Transition(initial[1], final[0]) d_orientation = initial[0].get_center().molecular_orientation() a_orientation = initial[1].get_center().molecular_orientation() r_vector = initial[0].get_coordinates_absolute( ) - final[0].get_coordinates_absolute() hash_string = generate_hash_2( inspect.currentframe().f_code.co_name, d_transition, a_transition, d_orientation, a_orientation, r_vector, [ref_index, tuple(transitions), longitude, n_divisions]) if hash_string in coupling_data: return coupling_data[hash_string] mu_a = transitions[transitions.index(a_transition)].tdm mu_d = transitions[transitions.index(d_transition)].tdm mu_d = rotate_vector(mu_d, d_orientation) * ATOMIC_TO_ANGS_EL mu_a = rotate_vector(mu_a, a_orientation) * ATOMIC_TO_ANGS_EL # mu_d = donor.get_transition_moment(to_state=_GS_) # transition dipole moment (donor) e*angs # mu_a = acceptor.get_transition_moment(to_state=donor.state) # transition dipole moment (acceptor) e*angs #r_vector = intermolecular_vector(donor, acceptor, supercell, cell_increment) # position vector between donor and acceptor coupling_data[hash_string] = forster.dipole_extended( r_vector, mu_a, mu_d, n=ref_index, longitude=longitude, n_divisions=n_divisions) return coupling_data[hash_string]
def forster_coupling_py(initial, final, ref_index=1, transitions=()): """ Compute Forster coupling in eV :param initial: initial states :param final: final states :return: Forster coupling """ d_transition = Transition(initial[0], final[1]) a_transition = Transition(initial[1], final[0]) d_orientation = initial[0].get_center().molecular_orientation() a_orientation = initial[1].get_center().molecular_orientation() # r_vector = initial[1].get_coordinates_absolute() - final[1].get_coordinates_absolute() r_vector = initial[0].get_coordinates_absolute( ) - final[0].get_coordinates_absolute() hash_string = generate_hash_2(inspect.currentframe().f_code.co_name, d_transition, a_transition, d_orientation, a_orientation, r_vector, [ref_index, tuple(transitions)]) if hash_string in coupling_data: return coupling_data[hash_string] try: mu_a = transitions[transitions.index(a_transition)].tdm mu_d = transitions[transitions.index(d_transition)].tdm except ValueError: raise Exception('TDM for {} / {} not defined'.format( a_transition, d_transition)) mu_d = rotate_vector(mu_d, d_orientation) * ATOMIC_TO_ANGS_EL mu_a = rotate_vector(mu_a, a_orientation) * ATOMIC_TO_ANGS_EL distance = np.linalg.norm(r_vector) k = orientation_factor(mu_d, mu_a, r_vector) # orientation factor between molecules k_e = 1.0 / (4.0 * np.pi * VAC_PERMITTIVITY) coupling_data[hash_string] = k_e * k * np.dot( mu_d, mu_a) / (ref_index**2 * distance**3) return coupling_data[hash_string]
def get_vib_spectrum(self, target_state, origin_state): # elec_trans_ene = self.state_energies[transition[1]] - self.state_energies[transition[0]] elec_trans_ene = target_state.energy - origin_state.energy transition = Transition(target_state, origin_state, symmetric=False) temp = self.temperature # temperature (K) if transition in self._transitions: reorg_ene = self._transitions[self._transitions.index( transition)].reorganization_energy #if transition in self.reorganization_energies: # reorg_ene = np.sum(self.reorganization_energies[transition]) else: raise Exception( 'Reorganization energy for transition {} not defined'.format( transition)) sign = np.sign(elec_trans_ene) # print('T', temp, molecule.reorganization_energies[transition], elec_trans_ene, -sign) def vib_spectrum(e): return 1.0 / (np.sqrt(4.0 * np.pi * BOLTZMANN_CONSTANT * temp * reorg_ene)) * \ np.exp(-(elec_trans_ene - e * sign + reorg_ene) ** 2 / (4 * BOLTZMANN_CONSTANT * temp * reorg_ene)) return vib_spectrum
def einstein_radiative_decay(initial, final, g1=1, g2=1, transitions=None, transition_moment=None): """ Einstein radiative decay :param molecule: :param g1: degeneracy of target state :param g2: degeneracy of origin state :return: decay rate constant """ deexcitation_energy = initial[0].energy - final[0].energy mu = np.array(transitions[transitions.index( Transition(initial[0], final[0]))].tdm) * ATOMIC_TO_ANGS_EL # mu = np.array(transition_moment[Transition(initial[0], final[0])]) * ATOMIC_TO_ANGS_EL mu2 = np.dot(mu, mu) # transition moment square norm. alpha = 1.0 / 137.036 return float(g1) / g2 * alpha * 4 * deexcitation_energy**3 * mu2 / ( 3 * SPEED_OF_LIGHT**2 * HBAR_PLANCK**3)
def forster_coupling(initial, final, ref_index=1, transitions=()): """ Compute Forster coupling in eV :param initial: initial states :param final: final states :return: Forster coupling """ d_transition = Transition(initial[0], final[1]) a_transition = Transition(initial[1], final[0]) d_orientation = initial[0].get_center().molecular_orientation() a_orientation = initial[1].get_center().molecular_orientation() #r_vector = initial[1].get_coordinates_absolute() - final[1].get_coordinates_absolute() r_vector = initial[0].get_coordinates_absolute( ) - final[0].get_coordinates_absolute() hash_string = generate_hash_2(inspect.currentframe().f_code.co_name, d_transition, a_transition, d_orientation, a_orientation, r_vector, [ref_index, tuple(transitions)]) if hash_string in coupling_data: return coupling_data[hash_string] try: mu_a = transitions[transitions.index(a_transition)].tdm mu_d = transitions[transitions.index(d_transition)].tdm except ValueError: raise Exception('TDM for {} / {} not defined'.format( a_transition, d_transition)) mu_d = rotate_vector(mu_d, d_orientation) * ATOMIC_TO_ANGS_EL mu_a = rotate_vector(mu_a, a_orientation) * ATOMIC_TO_ANGS_EL coupling_data[hash_string] = forster.dipole(mu_d, mu_a, r_vector, n=ref_index) return coupling_data[hash_string]
def get_vib_spectrum(self, target_state, origin_state): elec_trans_ene = target_state.energy - origin_state.energy transition = Transition(target_state, origin_state, symmetric=False) #elec_trans_ene = self.state_energies[transition[1]] - self.state_energies[transition[0]] temp = self.temperature # temperature (K) ext_reorg_ene = self.external_reorganization_energies[transition] reorg_ene = np.array(self.reorganization_energies[transition]) sign = np.sign(elec_trans_ene) angl_freqs = np.array( self.frequencies[transition] ) * 29.9792558 * 2 * np.pi # cm-1 -> ns-1 , angular frequency freq_classic_limit = BOLTZMANN_CONSTANT * temp / HBAR_PLANCK # ns^-1, angular frequency indices_cl = np.where(angl_freqs < freq_classic_limit)[0] indices_qm = np.where(angl_freqs >= freq_classic_limit)[0] l_cl = np.sum(reorg_ene[indices_cl]) + ext_reorg_ene l_qm = reorg_ene[indices_qm] ang_freq_qm = angl_freqs[indices_qm] s = np.true_divide(l_qm, ang_freq_qm) / HBAR_PLANCK s_eff = np.sum(s) w_eff = np.sum(np.multiply(s, ang_freq_qm)) / s_eff # angular frequency # print('l_qm', np.sum(l_qm)) # print('s_eff', s_eff) # print('w_eff', w_eff) def vib_spectrum(e): e = np.array(e, dtype=float) fcwd_term = np.zeros_like(e) for m in range(10): fcwd_term += s_eff**m / np.math.factorial(m) * np.exp( -s_eff) * np.exp(-(elec_trans_ene - e * sign + l_cl + m * HBAR_PLANCK * w_eff)**2 / (4 * BOLTZMANN_CONSTANT * temp * l_cl)) return 1.0 / (np.sqrt( 4 * np.pi * BOLTZMANN_CONSTANT * temp * l_cl)) * fcwd_term return vib_spectrum
def get_vib_spectrum(self, target_state, origin_state): """ :param donor: energy diference between states :param acceptor: deviation in energy units :return: Franck-Condon-weighted density of states in gaussian aproximation """ elec_trans_ene = target_state.energy - origin_state.energy transition = Transition(target_state, origin_state, symmetric=False) #elec_trans_ene = self.state_energies[transition[1]] - self.state_energies[transition[0]] reorg_ene = np.sum(self.reorganization_energies[transition]) deviation = self.deviations[transition] # atomic units sign = np.sign(elec_trans_ene) def vib_spectrum(e): return np.exp( -(elec_trans_ene - e * sign + reorg_ene)**22 / (2 * deviation)**2) / (2 * np.sqrt(np.pi) * deviation) return vib_spectrum
def test_kmc_algorithm(self): np.random.seed( 0 ) # set random seed in order for the examples to reproduce the exact references transitions = [ Transition( s1, gs, tdm=[0.5, 0.2], # a.u. reorganization_energy=0.08, # eV symmetric=True) ] vibrational_model = MarcusModel(transitions=transitions, temperature=300) # Kelvin # list of transfer functions by state self.system.process_scheme = [ GoldenRule(initial_states=(s1, gs), final_states=(gs, s1), electronic_coupling_function=forster_coupling_extended, description='Forster', arguments={ 'ref_index': 2, 'longitude': 2, 'n_divisions': 30, 'transitions': transitions }, vibrations=vibrational_model), DecayRate(initial_state=s1, final_state=gs, decay_rate_function=einstein_radiative_decay, arguments={'transitions': transitions}, description='singlet_radiative_decay') ] self.system.cutoff_radius = 10.0 # interaction cutoff radius in Angstrom # some system analyze functions system_test_info(self.system) trajectories = calculate_kmc( self.system, num_trajectories=10, # number of trajectories that will be simulated max_steps=100, # maximum number of steps for trajectory allowed silent=True) # Results analysis analysis = TrajectoryAnalysis(trajectories) print('diffusion coefficient: {:9.5f} Angs^2/ns'.format( analysis.diffusion_coefficient('s1'))) print('lifetime: {:9.5f} ns'.format( analysis.lifetime('s1'))) print('diffusion length: {:9.5f} Angs'.format( analysis.diffusion_length('s1'))) print('diffusion tensor (Angs^2/ns)') print(analysis.diffusion_coeff_tensor('s1')) print('diffusion length square tensor (Angs)') print(analysis.diffusion_length_square_tensor('s1')) test = { 'diffusion coefficient': np.around(analysis.diffusion_coefficient('s1'), decimals=4), 'lifetime': np.around(analysis.lifetime('s1'), decimals=4), 'diffusion length': np.around(analysis.diffusion_length('s1'), decimals=4), 'diffusion tensor': np.around(analysis.diffusion_coeff_tensor('s1', unit_cell=[[0.0, 0.5], [0.2, 0.0]]), decimals=4).tolist(), 'diffusion length tensor': np.around(analysis.diffusion_length_square_tensor( 's1', unit_cell=[[0.0, 0.5], [0.2, 0.0]]), decimals=6).tolist() } print(test) ref = { 'diffusion coefficient': 1586.4162, 'lifetime': 0.3621, 'diffusion length': 47.9375, 'diffusion tensor': [[2048.6421, 3.4329], [3.4329, 1124.1904]], 'diffusion length tensor': [[3142.8, 42.0], [42.0, 1453.2]] } self.assertDictEqual(ref, test)
def test_kmc_algorithm(self): np.random.seed(0) # set random seed in order for the examples to reproduce the exact references transitions = [Transition(s1, gs, tdm=[0.3, 0.1], # a.u. reorganization_energy=0.08, # eV symmetric=True)] marcus = MarcusModel(transitions=transitions, temperature=300) # Kelvin # list of transfer functions by state self.system.process_scheme = [GoldenRule(initial_states=(s1, gs), final_states=(gs, s1), electronic_coupling_function=forster_coupling_extended, description='Forster', arguments={'ref_index': 2, 'longitude': 2, 'n_divisions': 30, 'transitions': transitions}, vibrations=marcus ), DecayRate(initial_state=s1, final_state=gs, decay_rate_function=einstein_radiative_decay, arguments={'transitions': transitions}, description='singlet_radiative_decay') ] self.system.cutoff_radius = 10.0 # interaction cutoff radius in Angstrom # some system analyze functions system_test_info(self.system) trajectories = calculate_kmc(self.system, num_trajectories=10, # number of trajectories that will be simulated max_steps=100, # maximum number of steps for trajectory allowed silent=True) # Results analysis analysis = TrajectoryAnalysis(trajectories) print('diffusion coefficient: {:9.5f} Angs^2/ns'.format(analysis.diffusion_coefficient('s1'))) print('lifetime: {:9.5f} ns'.format(analysis.lifetime('s1'))) print('diffusion length: {:9.5f} Angs'.format(analysis.diffusion_length('s1'))) print('diffusion tensor (Angs^2/ns)') print(analysis.diffusion_coeff_tensor('s1')) print('diffusion length square tensor (Angs)') print(analysis.diffusion_length_square_tensor('s1')) test = {'diffusion coefficient': np.around(analysis.diffusion_coefficient('s1'), decimals=4), 'lifetime': np.around(analysis.lifetime('s1'), decimals=4), 'diffusion length': np.around(analysis.diffusion_length('s1'), decimals=4), 'diffusion tensor': np.around(analysis.diffusion_coeff_tensor('s1', unit_cell=[[5.0, 1.0], [1.0, 5.0]]), decimals=4).tolist(), 'diffusion length tensor': np.around(analysis.diffusion_length_square_tensor('s1', unit_cell=[[5.0, 1.0], [1.0, 5.0]]), decimals=4).tolist() } ref = {'diffusion coefficient': 120.7623, 'lifetime': 2.1215, 'diffusion length': 33.0968, 'diffusion tensor': [[198.6035, -72.076], [-72.076, 98.3641]], 'diffusion length tensor': [[1606.8, -728.0], [-728.0, 1144.0]] } self.maxDiff = None __import__('sys').modules['unittest.util']._MAX_LENGTH = 999999999 self.assertDictEqual(ref, test)
def get_vib_spectrum(self, target_state, origin_state): transition = Transition(target_state, origin_state, symmetric=False) # Temperature is not actually used. This is to keep common interface return self.empirical_function[transition]
def test_kmc_algorithm(self): np.random.seed( 0 ) # set random seed in order for the examples to reproduce the exact references transitions = [Transition(s1, gs, symmetric=True)] # list of transfer functions by state self.system.process_scheme = [ SimpleRate(initial_states=(s1, gs), final_states=(gs, s1), rate_constant=0.1), SimpleRate(initial_states=(s1, ), final_states=(gs, ), rate_constant=0.01) ] # some system analyze functions system_test_info(self.system) trajectories = calculate_kmc( self.system, num_trajectories= 500, # number of trajectories that will be simulated max_steps=1000, # maximum number of steps for trajectory allowed silent=True) # Results analysis analysis = TrajectoryAnalysis(trajectories) print('diffusion coefficient: {:9.5f} Angs^2/ns'.format( analysis.diffusion_coefficient('s1'))) print('lifetime: {:9.5f} ns'.format( analysis.lifetime('s1'))) print('diffusion length: {:9.5f} Angs'.format( analysis.diffusion_length('s1'))) print('diffusion tensor (Angs^2/ns)') print(analysis.diffusion_coeff_tensor('s1')) print('diffusion length square tensor (Angs)') print(analysis.diffusion_length_square_tensor('s1')) test = { 'diffusion coefficient': np.around(analysis.diffusion_coefficient('s1'), decimals=4), 'lifetime': np.around(analysis.lifetime('s1'), decimals=4), 'diffusion length': np.around(analysis.diffusion_length('s1'), decimals=4), 'diffusion tensor': np.around(analysis.diffusion_coeff_tensor('s1', unit_cell=[[0.0, 0.5], [0.2, 0.0]]), decimals=4).tolist(), 'diffusion length tensor': np.around(analysis.diffusion_length_square_tensor( 's1', unit_cell=[[0.0, 0.5], [0.2, 0.0]]), decimals=6).tolist() } print(test) ref = { 'diffusion coefficient': 2.4439, 'lifetime': 92.7496, 'diffusion length': 30.1679, 'diffusion tensor': [[2.1338, 0.1308], [0.1308, 2.7539]], 'diffusion length tensor': [[798.7, 25.7], [25.7, 1021.5]] } # This is just for visual comparison (not accounted in the test) decay = 0.01 transfer = 0.1 distance = 5 print('analytical model') print('----------------') data = get_analytical_model(distance, analysis.n_dim, transfer, decay) print('results analytical:', data) self.assertDictEqual(ref, test)
from kimonet.system.vibrations import MarcusModel, LevichJortnerModel, EmpiricalModel from kimonet.core.processes.transitions import Transition from kimonet import calculate_kmc, calculate_kmc_parallel, calculate_kmc_parallel_py2 from kimonet.system.state import ground_state as gs from kimonet.utils import old_distance_between_molecules, distance_vector_periodic from kimonet.fileio import store_trajectory_list, load_trajectory_list import numpy as np # states list s1 = State(label='s1', energy=2.9705, multiplicity=1, size=1) tt = State(label='tt', energy=2.0, multiplicity=1, size=2, connected_distance=8) t1 = State(label='t1', energy=1.5, multiplicity=3, size=1) # transition moments transition_moment = {Transition(s1, gs): [0.1, 0.0]} def direction_transfer(initial, final, rate_constant=1): """ Allows transfer along a single direction :param initial: :param final: :param rate_constant: :return: """ r = initial[0].get_center().get_coordinates() - initial[1].get_center().get_coordinates() dot = np.dot(r, initial[0].get_center().get_orientation_vector())
from kimonet.analysis import plot_polar_plot from kimonet import calculate_kmc, calculate_kmc_parallel, calculate_kmc_parallel_py2 from kimonet.system.state import State from kimonet.system.state import ground_state as gs from kimonet.core.processes.transitions import Transition import numpy as np # states list s1 = State(label='s1', energy=20.0, multiplicity=1) # transition moments transitions = [ Transition( s1, gs, tdm=[0.1, 0.0], # a.u. reorganization_energy=0.08) ] # eV # define system as a crystal molecule = Molecule() #print(molecule, molecule.state, molecule.state.get_center()) molecule2 = Molecule(site_energy=2) print(molecule2, molecule2.state, molecule2.state.get_center()) print(molecule, molecule.state, molecule.state.get_center()) system = crystal_system(
def forster_coupling_sine_py(initial, final, ref_index=1, transitions=(), longitude=0.0, n_divisions=1): """ :param initial: initial states :param final: final states :param ref_index: :param transitions: :param longitude: extension length of the dipole :param n_divisions: number of subdivisions. To use with longitude. Increase until convergence :return: """ n_divisions += 2 d_transition = Transition(initial[0], final[1]) a_transition = Transition(initial[1], final[0]) d_orientation = initial[0].get_center().molecular_orientation() a_orientation = initial[1].get_center().molecular_orientation() r_vector = initial[0].get_coordinates_absolute( ) - final[0].get_coordinates_absolute() hash_string = generate_hash_2( inspect.currentframe().f_code.co_name, d_transition, a_transition, d_orientation, a_orientation, r_vector, [ref_index, tuple(transitions), longitude, n_divisions]) if hash_string in coupling_data: return coupling_data[hash_string] mu_a = transitions[transitions.index(a_transition)].tdm mu_d = transitions[transitions.index(d_transition)].tdm mu_d = rotate_vector(mu_d, d_orientation) * ATOMIC_TO_ANGS_EL mu_a = rotate_vector(mu_a, a_orientation) * ATOMIC_TO_ANGS_EL k_e = 1.0 / (4.0 * np.pi * VAC_PERMITTIVITY) forster_coupling = 0 for i, x in enumerate( np.linspace(-1 + 1 / n_divisions, 1 - 1 / n_divisions, n_divisions)): #test=[] for j, y in enumerate( np.linspace(-1 + 1 / n_divisions, 1 - 1 / n_divisions, n_divisions)): if n_divisions - 1 > i > 0 and n_divisions - 1 > j > 0: #if True: n_d = n_divisions - 1 mu_ai = np.sin(i * np.pi / n_d) / np.sum( [np.sin(ik * np.pi / n_d) for ik in range(n_d)]) * mu_a mu_di = np.sin(j * np.pi / n_d) / np.sum( [np.sin(jk * np.pi / n_d) for jk in range(n_d)]) * mu_d #print(x, y, np.sin(j*np.pi/n_d)/np.sum([np.sin(jk*np.pi/n_d) for jk in range(n_d)])) dr_a = mu_a / np.linalg.norm(mu_a) * longitude * x dr_d = mu_d / np.linalg.norm(mu_d) * longitude * y r_vectori = r_vector + dr_a - dr_d distance = np.linalg.norm(r_vectori) #if np.linalg.norm(mu_ai) != 0 and np.linalg.norm(mu_di) != 0: # print(i, j) k = orientation_factor( mu_ai, mu_di, r_vectori) # orientation factor between molecules forster_coupling += k_e * k * np.dot(mu_ai, mu_di) / (distance **3) #test.append(np.linalg.norm(mu_di)) #plt.plot(test, 'o-') #plt.show() coupling_data[ hash_string] = forster_coupling # memory update for new couplings return coupling_data[hash_string]
def forster_coupling_extended_py(initial, final, ref_index=1, transitions=(), longitude=3, n_divisions=300): """ Compute Forster coupling in eV (pure python version) :param initial: initial states :param final: final states :param longitude: extension length of the dipole :param n_divisions: number of subdivisions. To use with longitude. Increase until convergence. :return: Forster coupling """ d_transition = Transition(initial[0], final[1]) a_transition = Transition(initial[1], final[0]) d_orientation = initial[0].get_center().molecular_orientation() a_orientation = initial[1].get_center().molecular_orientation() r_vector = initial[0].get_coordinates_absolute( ) - final[0].get_coordinates_absolute() hash_string = generate_hash_2( inspect.currentframe().f_code.co_name, d_transition, a_transition, d_orientation, a_orientation, r_vector, [ref_index, tuple(transitions), longitude, n_divisions]) if hash_string in coupling_data: return coupling_data[hash_string] mu_a = transitions[transitions.index(a_transition)].tdm mu_d = transitions[transitions.index(d_transition)].tdm mu_d = rotate_vector(mu_d, d_orientation) * ATOMIC_TO_ANGS_EL mu_a = rotate_vector(mu_a, a_orientation) * ATOMIC_TO_ANGS_EL # mu_d = donor.get_transition_moment(to_state=_GS_) # transition dipole moment (donor) e*angs # mu_a = acceptor.get_transition_moment(to_state=donor.state) # transition dipole moment (acceptor) e*angs #r_vector = intermolecular_vector(donor, acceptor, supercell, cell_increment) # position vector between donor and acceptor mu_ai = mu_a / n_divisions mu_di = mu_d / n_divisions k_e = 1.0 / (4.0 * np.pi * VAC_PERMITTIVITY) forster_coupling = 0 for x in np.linspace(-0.5 + 0.5 / n_divisions, 0.5 - 0.5 / n_divisions, n_divisions): for y in np.linspace(-0.5 + 0.5 / n_divisions, 0.5 - 0.5 / n_divisions, n_divisions): #print(x, y) dr_a = mu_a / np.linalg.norm(mu_a) * longitude * x dr_d = mu_d / np.linalg.norm(mu_d) * longitude * y r_vector_i = r_vector + dr_a + dr_d distance = np.linalg.norm(r_vector_i) k = orientation_factor( mu_ai, mu_di, r_vector_i) # orientation factor between molecules forster_coupling += k_e * k * np.dot( mu_ai, mu_di) / (ref_index**2 * distance**3) coupling_data[ hash_string] = forster_coupling # memory update for new couplings return forster_coupling
def test_kmc_algorithm_2(self): np.random.seed( 0 ) # set random seed in order for the examples to reproduce the exact references transitions = [ Transition(s1, gs, tdm=[0.01], reorganization_energy=0.07, symmetric=True) ] # set additional system parameters self.system.process_scheme = [ GoldenRule(initial_states=(s1, gs), final_states=(gs, s1), electronic_coupling_function=forster_coupling, description='Forster coupling', arguments={ 'ref_index': 1, 'transitions': transitions }, vibrations=MarcusModel(transitions=transitions)), DecayRate(initial_state=s1, final_state=gs, decay_rate_function=decay_rate, description='custom decay rate') ] self.system.cutoff_radius = 10.0 # interaction cutoff radius in Angstrom # some system analyze functions system_test_info(self.system) trajectories = calculate_kmc( self.system, num_trajectories=10, # number of trajectories that will be simulated max_steps=10000, # maximum number of steps for trajectory allowed silent=True) # Results analysis analysis = TrajectoryAnalysis(trajectories) print('diffusion coefficient: {:9.5f} Angs^2/ns'.format( analysis.diffusion_coefficient('s1'))) print('lifetime: {:9.5f} ns'.format( analysis.lifetime('s1'))) print('diffusion length: {:9.5f} Angs'.format( analysis.diffusion_length('s1'))) print('diffusion tensor (Angs^2/ns)') print(analysis.diffusion_coeff_tensor('s1')) print('diffusion length square tensor (Angs)') print(analysis.diffusion_length_square_tensor('s1')) test = { 'diffusion coefficient': np.around(analysis.diffusion_coefficient('s1'), decimals=4), 'lifetime': np.around(analysis.lifetime('s1'), decimals=4), 'diffusion length': np.around(analysis.diffusion_length('s1'), decimals=4), 'diffusion tensor': np.around(analysis.diffusion_coeff_tensor('s1'), decimals=4).tolist(), 'diffusion length tensor': np.around(np.sqrt(analysis.diffusion_length_square_tensor('s1')), decimals=6).tolist() } print(test) ref = { 'diffusion coefficient': 0.4265, 'lifetime': 24.1692, 'diffusion length': 5.4498, 'diffusion tensor': [[0.4265]], 'diffusion length tensor': [[5.449771]] } self.assertDictEqual(ref, test)