def test_optim_fun(setup_cte, mocker, excitations): '''Test optim_fun''' mocked_dyn = mocker.patch('simetuc.simulations.Simulations.simulate_dynamics') class mocked_dyn_res: errors = np.ones((setup_cte.states['activator_states'] + setup_cte.states['sensitizer_states'],), dtype=np.float64) average = False mocked_dyn.return_value = mocked_dyn_res sim = simulations.Simulations(setup_cte) sim.cte['optimization']['excitations'] = excitations # Processes to optimize. If not given, all ET parameters will be optimized process_list = setup_cte.optimization['processes'] # create a set of Parameters params = Parameters() for process in process_list: max_val = 1e15 if isinstance(process, EneryTransferProcess) else 1 # don't let ET processes go to zero. min_val = 1 if isinstance(process, EneryTransferProcess) else 0 params.add(process.name, value=process.value, min=min_val, max=max_val) optimize.optim_fun_dynamics(params, sim, average=False) sim.cte['concentration_dependence']['concentrations'] = [(0, 0.3), (0.1, 0.3), (0.1, 0)] optimize.optim_fun_dynamics_conc(params, sim) assert mocked_dyn.called
def test_sim_conc_dep_dyn(setup_cte_sim, mocker): '''Test that the concentration dependence works''' with temp_bin_filename() as temp_filename: mocked = mocker.patch('simetuc.odesolver._solve_ode') # the num_states changes when the temp lattice is created, # allocate 2x so that we're safe. Also make the num_points 1000. mocked.return_value = np.random.random( (1000, 2 * setup_cte_sim['states']['energy_states'])) conc_list = [(0, 0.3), (0.1, 0.3), (0.1, 0)] sim = simulations.Simulations(setup_cte_sim, full_path=temp_filename) solution = sim.simulate_concentration_dependence(conc_list, dynamics=True) # dynamics call solve_ode twice (pulse and relaxation) assert mocked.call_count == 2 * len(conc_list) assert solution solution.plot() solution.log_errors() with temp_config_filename('') as filename: solution.save(filename) sol_hdf5 = simulations.ConcentrationDependenceSolution.load(filename) assert sol_hdf5 assert sol_hdf5 == solution sol_hdf5.plot() plotter.plt.close('all')
def test_sim_steady2(setup_cte_sim): '''Test average steady state''' setup_cte_sim['excitations']['Vis_473'][0].t_pulse = 1e-08 with temp_bin_filename() as temp_filename: sim = simulations.Simulations(setup_cte_sim, full_path=temp_filename) solution = sim.simulate_avg_steady_state() assert solution
def test_sim_dyn_save_hdf5(setup_cte_sim, mocker): '''Test that the dynamics solution is saved a loaded correctly''' with temp_bin_filename() as temp_filename: mocked = mocker.patch('simetuc.odesolver._solve_ode') # the num_states changes when the temp lattice is created, # allocate 2x so that we're safe. Also make the num_points 1000. mocked.return_value = np.random.random( (1000, 2 * setup_cte_sim.states['energy_states'])) sim = simulations.Simulations(setup_cte_sim, full_path=temp_filename) solution = sim.simulate_dynamics() assert mocked.call_count == 2 with temp_bin_filename() as filename: solution.save(filename) sol_hdf5 = simulations.DynamicsSolution.load(filename) assert sol_hdf5 assert sol_hdf5.cte == solution.cte assert np.allclose(sol_hdf5.y_sol, solution.y_sol) assert np.allclose(sol_hdf5.t_sol, solution.t_sol) assert sol_hdf5.index_S_i == solution.index_S_i assert sol_hdf5.index_A_j == solution.index_A_j assert sol_hdf5 == solution sol_hdf5.log_errors() sol_hdf5.plot() plotter.plt.close('all')
def test_sim_power_dep_no_plot(setup_cte_sim, mocker): '''A plot was requested, but no_plot is set''' setup_cte_sim['no_plot'] = True with temp_bin_filename() as temp_filename: mocked = mocker.patch( 'simetuc.simulations.Simulations.simulate_pulsed_steady_state') mocked.return_value = simulations.SteadyStateSolution( np.empty((1000, )), np.empty((1000, 2 * setup_cte_sim['states']['energy_states'])), [], [], setup_cte_sim) sim = simulations.Simulations(setup_cte_sim, full_path=temp_filename) power_dens_list = np.logspace(1, 2, 3) solution = sim.simulate_power_dependence(power_dens_list) assert mocked.call_count == len(power_dens_list) with pytest.warns(plotter.PlotWarning) as warnings: solution.plot() # assert len(warnings) == 1 # one warning warning = warnings.pop(plotter.PlotWarning) assert issubclass(warning.category, plotter.PlotWarning) assert 'A plot was requested, but no_plot setting is set' in str( warning.message) plotter.plt.close('all')
def test_sim_conc_dep_no_plot(setup_cte_sim, mocker): '''A plot was requested, but no_plot is set''' setup_cte_sim['no_plot'] = True with temp_bin_filename() as temp_filename: mocked = mocker.patch('simetuc.odesolver._solve_ode') # the num_states changes when the temp lattice is created, # allocate 2x so that we're safe. Also make the num_points 1000. mocked.return_value = np.random.random( (1000, 2 * setup_cte_sim['states']['energy_states'])) sim = simulations.Simulations(setup_cte_sim, full_path=temp_filename) conc_list = [(0.01, 0.3), (0.1, 0.3)] solution = sim.simulate_concentration_dependence(conc_list, dynamics=False) assert mocked.call_count == 2 * len(conc_list) with pytest.warns(plotter.PlotWarning) as warnings: solution.plot() # assert len(warnings) == 1 # one warning warning = warnings.pop(plotter.PlotWarning) assert issubclass(warning.category, plotter.PlotWarning) assert 'A plot was requested, but no_plot setting is set' in str( warning.message) plotter.plt.close('all')
def test_sim_dyn_no_t_pulse(setup_cte_sim): '''Test that the dynamics gives an error if t_pulse is not defined''' del setup_cte_sim['excitations']['Vis_473'][0].t_pulse with temp_bin_filename() as temp_filename: sim = simulations.Simulations(setup_cte_sim, full_path=temp_filename) with pytest.raises(AttributeError): sim.simulate_dynamics()
def test_sim_dyn_save_txt(setup_cte_sim): '''Test that the dynamics solution is saved a loaded correctly''' with temp_bin_filename() as temp_filename: sim = simulations.Simulations(setup_cte_sim, full_path=temp_filename) solution = sim.simulate_dynamics() with temp_config_filename('') as filename: solution.save_txt(filename)
def test_sim_average_dyn(setup_cte_sim): '''Test average dynamics.''' setup_cte_sim['lattice']['S_conc'] = 0 with temp_bin_filename() as temp_filename: sim = simulations.Simulations(setup_cte_sim, full_path=temp_filename) solution = sim.simulate_avg_dynamics() assert solution
def test_sim_power_dep_save_txt(setup_cte_sim, mocker): '''Test that the power dep solution is saved as text correctly''' with temp_bin_filename() as temp_filename: sim = simulations.Simulations(setup_cte_sim, full_path=temp_filename) power_dens_list = np.logspace(1, 2, 3) solution = sim.simulate_power_dependence(power_dens_list, average=True) with temp_config_filename('') as filename: solution.save_txt(filename)
def test_sim_dyn_diff(setup_cte_sim): '''Test that the two dynamics are different''' setup_cte_sim['lattice']['S_conc'] = 0 setup_cte_sim['lattice']['A_conc'] = 0.2 with temp_bin_filename() as temp_filename: sim1 = simulations.Simulations(setup_cte_sim, full_path=temp_filename) solution1 = sim1.simulate_dynamics() solution1.total_error setup_cte_sim['ions'] = {} setup_cte_sim['lattice']['S_conc'] = 0.2 setup_cte_sim['lattice']['A_conc'] = 0 with temp_bin_filename() as temp_filename: sim2 = simulations.Simulations(setup_cte_sim, full_path=temp_filename) solution2 = sim2.simulate_dynamics() solution2.total_error assert sim1 != sim2 assert solution1 != solution2
def test_sim_dyn_errors(setup_cte_sim): '''Test that the dynamics work''' setup_cte_sim['lattice']['S_conc'] = 0 with temp_bin_filename() as temp_filename: sim = simulations.Simulations(setup_cte_sim, full_path=temp_filename) solution = sim.simulate_dynamics() solution.errors assert isinstance(solution.errors, np.ndarray) solution.log_errors()
def test_sim_dyn_wrong_state_plot(setup_cte_sim): '''Test that you can't plot a wrong state.''' setup_cte_sim['lattice']['S_conc'] = 0 with temp_bin_filename() as temp_filename: sim = simulations.Simulations(setup_cte_sim, full_path=temp_filename) solution = sim.simulate_dynamics() with pytest.raises(ValueError): solution.plot(state=10) plotter.plt.close('all')
def test_sim_conc_dep_save_txt(setup_cte_sim, mocker): '''Test that the conc dep solution is saved as text correctly''' with temp_bin_filename() as temp_filename: conc_list = [(0, 0.3), (0.1, 0.3), (0.1, 0)] sim = simulations.Simulations(setup_cte_sim, full_path=temp_filename) solution = sim.simulate_concentration_dependence(conc_list, dynamics=False, average=True) with temp_config_filename('') as filename: solution.save_txt(filename)
def test_sim_power_dep(setup_cte_sim, mocker, average, excitation_name): '''Test that the power dependence works''' for exc_name, exc_list in setup_cte_sim.excitations.items(): for exc in exc_list: if exc_name is excitation_name: exc.active = True else: exc.active = False with temp_bin_filename() as temp_filename: mocked = mocker.patch('simetuc.odesolver._solve_ode') # the num_states changes when the temp lattice is created, # allocate 2x so that we're safe. Also make the num_points 1000. mocked.return_value = np.random.random( (1000, 2 * setup_cte_sim.states['energy_states'])) sim = simulations.Simulations(setup_cte_sim, full_path=temp_filename) assert sim.cte == setup_cte_sim power_dens_list = np.logspace(1, 3, 3 - 1 + 1) solution = sim.simulate_power_dependence(power_dens_list, average=average) assert (mocked.call_count == 2 * len(power_dens_list)) or (mocked.call_count == len(power_dens_list)) assert solution solution.plot() with temp_config_filename('') as filename: solution.save_txt(filename) with temp_config_filename('') as filename: solution.save(filename) solution_hdf5 = simulations.PowerDependenceSolution.load(filename) assert solution_hdf5 for sol, sol_hdf5 in zip(solution.solution_list, solution_hdf5.solution_list): assert sol.y_sol.shape == sol_hdf5.y_sol.shape assert np.allclose(sol.t_sol, sol_hdf5.t_sol) assert np.allclose(sol.y_sol, sol_hdf5.y_sol) assert sol.cte == sol_hdf5.cte print(type(sol.index_S_i), type(sol_hdf5.index_S_i)) print(sol.index_S_i, sol_hdf5.index_S_i) assert sol.index_S_i == sol_hdf5.index_S_i assert sol.index_A_j == sol_hdf5.index_A_j assert sol == sol_hdf5 assert solution_hdf5 == solution solution_hdf5.plot() plotter.plt.close('all')
def test_sim_dyn(setup_cte_sim): '''Test that the dynamics work''' setup_cte_sim['lattice']['S_conc'] = 0 with temp_bin_filename() as temp_filename: sim = simulations.Simulations(setup_cte_sim, full_path=temp_filename) assert sim.cte == setup_cte_sim solution = sim.simulate_dynamics() assert solution solution.plot() solution.plot(state=7) solution.plot(state=1) plotter.plt.close('all')
def test_sim_dyn_2S_2A(setup_cte_sim): '''Test that the dynamics have the right result for a simple system''' test_filename = os.path.join(test_folder_path, 'data_2S_2A.hdf5') sim = simulations.Simulations(setup_cte_sim, full_path=test_filename) solution = sim.simulate_dynamics() assert solution.index_A_j == [0, -1, -1, 11] assert solution.index_S_i == [-1, 7, 9, -1] with h5py.File(os.path.join(test_folder_path, 't_sol_2S_2A.hdf5')) as file: t_sol = np.array(file['t_sol']) assert np.allclose(t_sol, solution.t_sol) with h5py.File(os.path.join(test_folder_path, 'y_sol_2S_2A.hdf5')) as file: y_sol = np.array(file['y_sol']) assert np.allclose(y_sol, solution.y_sol)
def test_sim_power_dep_correct_power_dens(setup_cte_sim, mocker): '''Check that the solutions have the right power_dens''' setup_cte_sim['no_plot'] = True with temp_bin_filename() as temp_filename: mocked = mocker.patch('simetuc.odesolver._solve_ode') # the num_states changes when the temp lattice is created, # allocate 2x so that we're safe. Also make the num_points 1000. mocked.return_value = np.random.random( (1000, 2 * setup_cte_sim['states']['energy_states'])) sim = simulations.Simulations(setup_cte_sim, full_path=temp_filename) power_dens_list = np.logspace(1, 3, 3) solution = sim.simulate_power_dependence(power_dens_list) assert mocked.call_count == 2 * len(power_dens_list) for num, pow_dens in enumerate(power_dens_list): assert solution[num].power_dens == pow_dens
def test_sim_conc_dep_empty_conc(setup_cte_sim): '''Conc list is empty''' with temp_bin_filename() as temp_filename: sim = simulations.Simulations(setup_cte_sim, full_path=temp_filename) conc_list = [] solution = sim.simulate_concentration_dependence(conc_list) with pytest.warns(plotter.PlotWarning) as warnings: solution.plot() # assert len(warnings) == 1 # one warning warning = warnings.pop(plotter.PlotWarning) assert issubclass(warning.category, plotter.PlotWarning) assert 'Nothing to plot! The concentration_dependence list is emtpy!' in str( warning.message) plotter.plt.close('all')
def test_sim_no_plot(setup_cte_sim): '''Test that no plot works''' setup_cte_sim['no_plot'] = True with temp_bin_filename() as temp_filename: sim = simulations.Simulations(setup_cte_sim, full_path=temp_filename) solution = sim.simulate_dynamics() with pytest.warns(plotter.PlotWarning) as warnings: solution.plot() # assert len(warnings) == 1 # one warning warning = warnings.pop(plotter.PlotWarning) assert issubclass(warning.category, plotter.PlotWarning) assert 'A plot was requested, but no_plot setting is set' in str( warning.message) plotter.plt.close('all')
def test_sim_sample_dynamics(setup_cte_sim, mocker, N_samples): '''Test that sampling the dynamics works''' setup_cte_sim['lattice']['S_conc'] = 0 mocked = mocker.patch('simetuc.odesolver._solve_ode') # the num_states changes when the temp lattice is created, # allocate 2x so that we're safe. Also make the num_points 1000. mocked.return_value = np.random.random( (1000, 2 * setup_cte_sim.states['energy_states'])) with temp_bin_filename() as temp_filename: sim = simulations.Simulations(setup_cte_sim, full_path=temp_filename) assert sim.cte == setup_cte_sim solution = sim.sample_simulation(sim.simulate_dynamics, N_samples=N_samples) assert solution assert mocked.call_count == 2 * N_samples
def test_sim_conc_dep_only_S(setup_cte_sim, mocker): '''Conc list has only S changing''' with temp_bin_filename() as temp_filename: mocked = mocker.patch('simetuc.odesolver._solve_ode') # the num_states changes when the temp lattice is created, # allocate 2x so that we're safe. Also make the num_points 1000. mocked.return_value = np.random.random( (1000, 2 * setup_cte_sim['states']['energy_states'])) sim = simulations.Simulations(setup_cte_sim, full_path=temp_filename) conc_list = [(0.01, 0.3), (0.1, 0.3), (0.3, 0.3)] solution = sim.simulate_concentration_dependence(conc_list, dynamics=False) assert mocked.call_count == 2 * len(conc_list) assert solution solution.plot() plotter.plt.close('all')
def test_sim_conc_dep_list(setup_cte_sim, mocker): '''Test that the concentration dependence works''' with temp_bin_filename() as temp_filename: mocked = mocker.patch('simetuc.odesolver._solve_ode') # the num_states changes when the temp lattice is created, # allocate 2x so that we're safe. Also make the num_points 1000. mocked.return_value = np.random.random( (1000, 2 * setup_cte_sim['states']['energy_states'])) conc_list = [(0, 0.3), (0.1, 0.3), (0.1, 0)] sim = simulations.Simulations(setup_cte_sim, full_path=temp_filename) solution = sim.simulate_concentration_dependence(conc_list, dynamics=True) # dynamics call solve_ode twice (pulse and relaxation) assert mocked.call_count == 2 * len(conc_list) for num, conc in enumerate(conc_list): assert solution[num].concentration == conc
def test_sim_steady1(setup_cte_sim): '''Test that the steady state solution is saved a loaded correctly''' setup_cte_sim['excitations']['Vis_473'][0].t_pulse = 1e-08 with temp_bin_filename() as temp_filename: sim = simulations.Simulations(setup_cte_sim, full_path=temp_filename) assert sim.cte == setup_cte_sim solution = sim.simulate_steady_state() assert solution solution.log_populations() solution.plot() solution.log_populations() # redo with temp_config_filename('') as filename: solution.save(filename) sol_hdf5 = simulations.SteadyStateSolution.load(filename) assert sol_hdf5 assert sol_hdf5 == solution sol_hdf5.plot() plotter.plt.close('all')
def optimize(function: Callable, cte: settings.Settings, average: bool = False, material_text: str = '', N_samples: int = None, full_path: str = None) -> OptimSolution: ''' Minimize the error between experimental data and simulation for the settings in cte average = True -> optimize average rate equations instead of microscopic ones. function returns the error vector and accepts: parameters, sim, and average. ''' logger = logging.getLogger(__name__) optim_progress = [] # type: List[str] def callback_fun(params: Parameters, iter_num: int, resid: np.array, sim: simulations.Simulations, average: bool = False, N_samples: int = None) -> None: ''' This function is called after every minimization step It prints the current parameters and error from the cache ''' optim_progbar.update(1) if not cte['no_console']: val_list = ', '.join('{:.3e}'.format(par.value) for par in params.values()) error = np.sqrt((resid * resid).sum()) msg = '{}, \t\t{}, \t{:.4e},\t[{}]'.format( iter_num, datetime.datetime.now().strftime('%H:%M:%S'), error, val_list) tqdm.tqdm.write(msg) logger.info(msg) optim_progress.append(msg) start_time = datetime.datetime.now() logger.info('Decay curves optimization of ' + material_text) cte['no_plot'] = True sim = simulations.Simulations(cte, full_path=full_path) method, parameters, options_dict = setup_optim(cte) optim_progbar = tqdm.tqdm(desc='Optimizing', unit='points', disable=cte['no_console']) param_names = ', '.join(name for name in parameters.keys()) header = 'Iter num\tTime\t\tRMSD\t\tParameters ({})'.format(param_names) optim_progress.append(header) tqdm.tqdm.write(header) minimizer = Minimizer(function, parameters, fcn_args=(sim, average, N_samples), iter_cb=callback_fun) # minimize logging only warnings or worse to console. with disable_loggers([ 'simetuc.simulations', 'simetuc.precalculate', 'simetuc.lattice', 'simetuc.simulations.conc_dep' ]): with disable_console_handler(__name__): result = minimizer.minimize(method=method, **options_dict) optim_progbar.update(1) optim_progbar.close() # fit results report_fit(result.params) logger.info(fit_report(result)) best_x = np.array([par.value for par in result.params.values()]) if 'brute' in method: min_f = np.sqrt(result.candidates[0].score) else: min_f = np.sqrt((result.residual**2).sum()) total_time = datetime.datetime.now() - start_time hours, remainder = divmod(total_time.total_seconds(), 3600) minutes, seconds = divmod(remainder, 60) tqdm.tqdm.write('') formatted_time = '{:.0f}h {:02.0f}m {:02.0f}s'.format( hours, minutes, seconds) logger.info('Minimum reached! Total time: %s.', formatted_time) logger.info('Optimized RMS error: %.3e.', min_f) logger.info('Parameters name and value:') for name, best_val in zip(parameters.keys(), best_x.T): logger.info('%s: %.3e.', name, best_val) optim_solution = OptimSolution(result, cte, optim_progress, total_time.total_seconds()) return optim_solution
def test_sim(setup_cte_sim): '''Test that the simulations work''' setup_cte_sim['lattice']['S_conc'] = 0 sim = simulations.Simulations(setup_cte_sim) assert sim.cte assert sim
def command_simulation(args: Dict) -> None: '''User invoked one of the simulation commands''' if args['--verbose']: no_console = False elif args['--quiet']: no_console = True else: no_console = False # show plots or not if args['--no-plot']: no_plot = True else: no_plot = False # load config file logger = logging.getLogger('simetuc') logger.info('Loading configuration...') cte = settings.load(args['<config_filename>']) cte['no_console'] = no_console cte['no_plot'] = no_plot N_samples = args.get('--N-samples', None) N_samples = int(N_samples) if N_samples else None cte['N_samples'] = N_samples # solution of the simulation solution: Union[simulations.Solution, simulations.SolutionList, optimize.OptimSolution, None] = None # choose what to do if args['--lattice']: # create lattice logger.info('Creating and plotting lattice...') lattice.generate(cte) elif args['--dynamics'] and not args[ '--concentration-dependence']: # simulate dynamics logger.info('Simulating dynamics...') sim = simulations.Simulations(cte) if args['--N-samples'] is not None: solution = sim.sample_simulation(sim.simulate_dynamics, N_samples=N_samples, average=args['--average']) else: solution = sim.simulate_dynamics(average=args['--average']) solution.log_errors() elif args['--steady-state']: # simulate steady state logger.info('Simulating steady state...') sim = simulations.Simulations(cte) solution = sim.simulate_steady_state(average=args['--average']) solution.log_populations() elif args['--power-dependence']: # simulate power dependence logger.info('Simulating power dependence...') sim = simulations.Simulations(cte) power_dens_list = cte.power_dependence solution = sim.simulate_power_dependence(power_dens_list, average=args['--average']) print('') elif args['--concentration-dependence'] and not args[ '--optimize']: # simulate concentration dependence logger.info('Simulating concentration dependence...') sim = simulations.Simulations(cte) conc_list = cte.concentration_dependence['concentrations'] N_uc_list = cte.concentration_dependence['N_uc_list'] if args['--N-samples'] is not None: solution = sim.sample_simulation( sim.simulate_concentration_dependence, N_samples=N_samples, concentrations=conc_list, N_uc_list=N_uc_list, dynamics=args['--dynamics'], average=args['--average']) else: solution = sim.simulate_concentration_dependence( conc_list, N_uc_list, dynamics=args['--dynamics'], average=args['--average']) solution.log_errors() elif args['--optimize']: # optimize logger.info('Optimizing parameters...') if args['--concentration'] or args['--concentration-dependence']: solution = optimize.optimize_concentrations( cte, average=args['--average'], N_samples=N_samples) else: solution = optimize.optimize_dynamics(cte, average=args['--average'], N_samples=N_samples) # save results to disk if solution is not None and not args['--no-save']: logger.info('Saving results to file.') solution.save() solution.save_txt(cmd=' '.join(sys.argv)) logger.info('Program finished!') # show all plots # the user needs to close the window to exit the program if not args['--no-plot']: if solution is not None: solution.plot() logger.info('Close the plot window to exit.') plt.show()
def test_sim_power_dep_ESA(): '''Make sure the simulated solution for a simple problem is equal to the theoretical one.''' test_filename = os.path.join(test_folder_path, 'data_0S_1A.hdf5') cte = { 'version': 1, 'decay': { 'decay_A': { DecayTransition(IonType.A, 1, 0, decay_rate=1e3), DecayTransition(IonType.A, 2, 0, decay_rate=1e6) }, 'decay_S': {DecayTransition(IonType.S, 1, 0, decay_rate=1e1)}, 'branching_S': {}, 'branching_A': {} }, 'excitations': { 'ESA': [ Excitation(IonType.A, 0, 1, True, 0, 1e-3, 1e6, t_pulse=None), Excitation(IonType.A, 1, 2, True, 0, 1e-3, 1e6, t_pulse=None) ] }, 'energy_transfer': {}, 'lattice': { 'A_conc': 0.3, 'N_uc': 20, 'S_conc': 0.0, 'a': 5.9738, 'alpha': 90.0, 'b': 5.9738, 'beta': 90.0, 'c': 3.5297, 'gamma': 120.0, 'name': 'bNaYF4', 'sites_occ': [1.0, 0.5], 'sites_pos': [(0.0, 0.0, 0.0), (2 / 3, 1 / 3, 0.5)], 'spacegroup': 'P-6' }, 'no_console': False, 'no_plot': False, 'simulation_params': { 'N_steps': 1000, 'N_steps_pulse': 100, 'atol': 1e-15, 'rtol': 0.001 }, 'states': { 'activator_ion_label': 'Tm', 'activator_states': 3, 'activator_states_labels': ['GS', 'ES1', 'ES2'], 'energy_states': 3, 'sensitizer_ion_label': 'Yb', 'sensitizer_states': 2, 'sensitizer_states_labels': ['GS', 'ES'] } } simple_cte = Settings.load_from_dict(cte) power_dens_list = np.logspace(1, 6, 6) sim = simulations.Simulations(simple_cte, full_path=test_filename) solution = sim.simulate_power_dependence(power_dens_list, average=True) # check that the ES1 and ES2 populations are close to the theoretical values for sol in solution: GS = sol.steady_state_populations[2] ES1 = sol.steady_state_populations[3] ES2 = sol.steady_state_populations[4] P = sol.power_dens * sol.cte.excitations['ESA'][0].pump_rate k1 = 1e3 k2 = 1e6 theo_ES1 = GS * P / (k1 + P) theo_ES2 = GS * P**2 / ((k1 + P) * k2) # print('ES1: {}, theo_ES1: {}'.format(ES1, theo_ES1)) # print('ES2: {}, theo_ES2: {}'.format(ES2, theo_ES2)) assert np.allclose(theo_ES1, ES1, rtol=1e-4) assert np.allclose(theo_ES2, ES2, rtol=1e-4)