def test_thermochemical_error_with_multiple_T_X_points(datasets_db): """Multiple temperature and composition datapoints in a dataset for a mixing phase should be successful.""" datasets_db.insert(CU_MG_SM_MIX_T_X_FCC_A1) dbf = Database(CU_MG_TDB) error = calculate_thermochemical_error(dbf, ['CU', 'MG', 'VA'], list(dbf.phases.keys()), datasets_db) assert np.isclose(float(error), -31830.303030303032, atol=0.01)
def test_thermochemical_error_with_multiple_X_points(datasets_db): """Multiple composition datapoints in a dataset for a mixing phase should be successful.""" datasets_db.insert(CU_MG_CPM_MIX_X_HCP_A3) dbf = Database(CU_MG_TDB) error = calculate_thermochemical_error(dbf, ['CU','MG','VA'], list(dbf.phases.keys()), datasets_db) assert np.isclose(float(error), -520.0, atol=0.01)
def test_thermochemical_error_with_multiple_T_points(datasets_db): """Multiple temperature datapoints in a dataset for a stoichiometric comnpound should be successful.""" datasets_db.insert(CU_MG_HM_MIX_T_CUMG2) dbf = Database(CU_MG_TDB) error = calculate_thermochemical_error(dbf, ['CU','MG','VA'], list(dbf.phases.keys()), datasets_db) assert np.isclose(float(error), -3672.727272727273, atol=0.01)
def test_thermochemical_error_for_mixing_entropy_error_is_excess_only( datasets_db): """Tests that error in mixing entropy data is excess only (the ideal part is removed).""" # If this fails, make sure the ideal mixing contribution is removed. phase_models = { "components": ["AL", "B"], "phases": { "LIQUID": { "sublattice_model": [["AL", "B"]], "sublattice_site_ratios": [1] }, "FCC_A1": { "sublattice_model": [["AL", "B"]], "sublattice_site_ratios": [1] } } } dataset_excess_mixing = { "components": ["AL", "B"], "phases": ["FCC_A1"], "solver": { "sublattice_site_ratios": [1], "sublattice_occupancies": [[[0.5, 0.5]]], "sublattice_configurations": [[["AL", "B"]]], "mode": "manual" }, "conditions": { "P": 101325, "T": 298.15 }, "output": "SM_MIX", "values": [[[10]]], "excluded_model_contributions": ["idmix"] } datasets_db.insert(dataset_excess_mixing) dbf = generate_parameters(phase_models, datasets_db, 'SGTE91', 'linear') assert dbf.elements == {'AL', 'B'} assert set(dbf.phases.keys()) == {'LIQUID', 'FCC_A1'} assert len(dbf._parameters.search(where('parameter_type') == 'L')) == 1 phases = list(dbf.phases.keys()) comps = list(dbf.elements) # the error should be exactly 0 because we are only fitting to one point # the dataset is excess only zero_error_prob = scipy.stats.norm(loc=0, scale=0.2).logpdf( 0.0) # SM weight = 0.2 # Explicitly pass parameters={} to not try fitting anything thermochemical_data = get_thermochemical_data(dbf, comps, phases, datasets_db, symbols_to_fit=[]) error = calculate_thermochemical_error(dbf, comps, thermochemical_data) assert np.isclose(error, zero_error_prob, atol=1e-6)
def lnprob( params, comps=None, dbf=None, phases=None, datasets=None, symbols_to_fit=None, phase_models=None, scheduler=None, callables=None, thermochemical_callables=None, ): """ Returns the error from multiphase fitting as a log probability. """ starttime = time.time() parameters = { param_name: param for param_name, param in zip(symbols_to_fit, params) } try: multi_phase_error = calculate_zpf_error(dbf, comps, phases, datasets, phase_models, parameters=parameters, callables=callables) except (ValueError, LinAlgError) as e: multi_phase_error = [np.inf] multi_phase_error = [ np.inf if np.isnan(x) else x**2 for x in multi_phase_error ] multi_phase_error = -np.sum(multi_phase_error) single_phase_error = calculate_thermochemical_error( dbf, comps, phases, datasets, parameters, phase_models=phase_models, callables=thermochemical_callables) actvity_error = calculate_activity_error(dbf, comps, phases, datasets, parameters=parameters, phase_models=phase_models, callables=callables) total_error = multi_phase_error + single_phase_error + actvity_error logging.debug( 'Single phase error: {:0.2f}. Multi phase error: {:0.2f}. Activity Error: {:0.2f}. Total error: {:0.2f}' .format(single_phase_error, multi_phase_error, actvity_error, total_error)) logging.debug('lnprob time: {}'.format(time.time() - starttime)) return np.array(total_error, dtype=np.float64)
def test_datasets_convert_thermochemical_string_values_producing_correct_value( datasets_db): """Strings where floats are expected should give correct answers for thermochemical datasets""" datasets_db.insert( clean_dataset(CU_MG_DATASET_THERMOCHEMICAL_STRING_VALUES)) dbf = Database(CU_MG_TDB) error = calculate_thermochemical_error(dbf, ['CU', 'MG', 'VA'], list(dbf.phases.keys()), datasets_db) assert np.isclose(float(error), -3672.727272727273, atol=0.01)
def test_thermochemical_error_with_multiple_T_X_points(datasets_db): """Multiple temperature and composition datapoints in a dataset for a mixing phase should be successful.""" datasets_db.insert(CU_MG_SM_MIX_T_X_FCC_A1) dbf = Database(CU_MG_TDB) phases = list(dbf.phases.keys()) comps = ['CU', 'MG', 'VA'] thermochemical_data = get_thermochemical_data(dbf, comps, phases, datasets_db) error = calculate_thermochemical_error(dbf, comps, thermochemical_data) assert np.isclose(float(error), -3282497.2380024833, rtol=1e-6)
def test_thermochemical_error_with_multiple_T_points(datasets_db): """Multiple temperature datapoints in a dataset for a stoichiometric comnpound should be successful.""" datasets_db.insert(CU_MG_HM_MIX_T_CUMG2) dbf = Database(CU_MG_TDB) phases = list(dbf.phases.keys()) comps = ['CU', 'MG', 'VA'] thermochemical_data = get_thermochemical_data(dbf, comps, phases, datasets_db) error = calculate_thermochemical_error(dbf, comps, thermochemical_data) assert np.isclose(error, -14.287293263253728, rtol=1e-6)
def test_mixing_energies_are_fit(datasets_db): """Tests that given mixing energy data, the excess parameter is fit.""" phase_models = { "components": ["AL", "B"], "phases": { "LIQUID": { "sublattice_model": [["AL", "B"]], "sublattice_site_ratios": [1] }, "FCC_A1": { "sublattice_model": [["AL", "B"]], "sublattice_site_ratios": [1] } } } dataset_excess_mixing = { "components": ["AL", "B"], "phases": ["FCC_A1"], "solver": { "sublattice_site_ratios": [1], "sublattice_occupancies": [[[0.5, 0.5]]], "sublattice_configurations": [[["AL", "B"]]], "mode": "manual" }, "conditions": { "P": 101325, "T": 298.15 }, "output": "HM_MIX", "values": [[[-10000]]] } datasets_db.insert(dataset_excess_mixing) dbf = generate_parameters(phase_models, datasets_db, 'SGTE91', 'linear') assert dbf.elements == {'AL', 'B'} assert set(dbf.phases.keys()) == {'LIQUID', 'FCC_A1'} assert len(dbf._parameters.search(where('parameter_type') == 'L')) == 1 assert dbf.symbols['VV0000'] == -40000 # check that read/write is ok read_dbf = dbf.from_string(dbf.to_string(fmt='tdb'), fmt='tdb') assert read_dbf.elements == {'AL', 'B'} assert set(read_dbf.phases.keys()) == {'LIQUID', 'FCC_A1'} assert len( read_dbf._parameters.search(where('parameter_type') == 'L')) == 1 # the error should be exactly 0 because we are only fitting to one point from espei.error_functions import calculate_thermochemical_error assert calculate_thermochemical_error(read_dbf, sorted(dbf.elements), sorted(dbf.phases.keys()), datasets_db) == 0
def test_thermochemical_error_with_multiple_X_points(datasets_db): """Multiple composition datapoints in a dataset for a mixing phase should be successful.""" datasets_db.insert(CU_MG_CPM_MIX_X_HCP_A3) dbf = Database(CU_MG_TDB) phases = list(dbf.phases.keys()) comps = ['CU', 'MG', 'VA'] thermochemical_data = get_thermochemical_data(dbf, comps, phases, datasets_db) error = calculate_thermochemical_error(dbf, comps, thermochemical_data) assert np.isclose(error, -4061.119001241541, rtol=1e-6)
def test_thermochemical_error_for_mixing_entropy_error_is_excess_only( datasets_db): """Tests that error in mixing entropy data is excess only (the ideal part is removed).""" # If this fails, make sure the ideal mixing contribution is removed. phase_models = { "components": ["AL", "B"], "phases": { "LIQUID": { "sublattice_model": [["AL", "B"]], "sublattice_site_ratios": [1] }, "FCC_A1": { "sublattice_model": [["AL", "B"]], "sublattice_site_ratios": [1] } } } dataset_excess_mixing = { "components": ["AL", "B"], "phases": ["FCC_A1"], "solver": { "sublattice_site_ratios": [1], "sublattice_occupancies": [[[0.5, 0.5]]], "sublattice_configurations": [[["AL", "B"]]], "mode": "manual" }, "conditions": { "P": 101325, "T": 298.15 }, "output": "SM_MIX", "values": [[[10]]] } datasets_db.insert(dataset_excess_mixing) dbf = generate_parameters(phase_models, datasets_db, 'SGTE91', 'linear') assert dbf.elements == {'AL', 'B'} assert set(dbf.phases.keys()) == {'LIQUID', 'FCC_A1'} assert len(dbf._parameters.search(where('parameter_type') == 'L')) == 1 # the error should be exactly 0 because we are only fitting to one point # the dataset is excess only from espei.error_functions import calculate_thermochemical_error assert calculate_thermochemical_error(dbf, sorted(dbf.elements), sorted(dbf.phases.keys()), datasets_db) == 0
def predict(params, **ctx): """ Calculate lnprob = lnlike + lnprior """ logging.debug('Parameters - {}'.format(params)) # lnprior prior_rvs = ctx['prior_rvs'] lnprior_multivariate = [rv.logpdf(theta) for rv, theta in zip(prior_rvs, params)] logging.debug('Priors: {}'.format(lnprior_multivariate)) lnprior = np.sum(lnprior_multivariate) if np.isneginf(lnprior): # It doesn't matter what the likelihood is. We can skip calculating it to save time. logging.log(TRACE, 'Proposal - lnprior: {:0.4f}, lnlike: {}, lnprob: {:0.4f}'.format(lnprior, np.nan, lnprior)) return lnprior # lnlike parameters = {param_name: param for param_name, param in zip(ctx['symbols_to_fit'], params)} zpf_kwargs = ctx.get('zpf_kwargs') activity_kwargs = ctx.get('activity_kwargs') thermochemical_kwargs = ctx.get('thermochemical_kwargs') starttime = time.time() if zpf_kwargs is not None: try: multi_phase_error = calculate_zpf_error(parameters=parameters, **zpf_kwargs) except (ValueError, np.linalg.LinAlgError) as e: raise e print(e) multi_phase_error = -np.inf else: multi_phase_error = 0 if activity_kwargs is not None and False: actvity_error = calculate_activity_error(parameters=parameters, **activity_kwargs) else: actvity_error = 0 if thermochemical_kwargs is not None: single_phase_error = calculate_thermochemical_error(parameters=parameters, **thermochemical_kwargs) else: single_phase_error = 0 total_error = multi_phase_error + single_phase_error + actvity_error logging.log(TRACE, 'Likelihood - {:0.2f}s - Thermochemical: {:0.3f}. ZPF: {:0.3f}. Activity: {:0.3f}. Total: {:0.3f}.'.format(time.time() - starttime, single_phase_error, multi_phase_error, actvity_error, total_error)) lnlike = np.array(total_error, dtype=np.float64) lnprob = lnprior + lnlike logging.log(TRACE, 'Proposal - lnprior: {:0.4f}, lnlike: {:0.4f}, lnprob: {:0.4f}'.format(lnprior, lnlike, lnprob)) return lnprob
def predict(params, ctx): parameters = { param_name: param for param_name, param in zip(ctx['symbols_to_fit'], params) } zpf_kwargs = ctx.get('zpf_kwargs') activity_kwargs = ctx.get('activity_kwargs') thermochemical_kwargs = ctx.get('thermochemical_kwargs') starttime = time.time() if zpf_kwargs is not None: try: multi_phase_error = calculate_zpf_error(parameters=parameters, **zpf_kwargs) except (ValueError, np.linalg.LinAlgError) as e: raise e print(e) multi_phase_error = -np.inf else: multi_phase_error = 0 if activity_kwargs is not None: actvity_error = calculate_activity_error(parameters=parameters, **activity_kwargs) else: actvity_error = 0 if thermochemical_kwargs is not None: single_phase_error = calculate_thermochemical_error( parameters=parameters, **thermochemical_kwargs) else: single_phase_error = 0 total_error = multi_phase_error + single_phase_error + actvity_error logging.log( TRACE, 'Likelihood - {:0.2f}s - Thermochemical: {:0.3f}. ZPF: {:0.3f}. Activity: {:0.3f}. Total: {:0.3f}.' .format(time.time() - starttime, single_phase_error, multi_phase_error, actvity_error, total_error)) error = np.array(total_error, dtype=np.float64) return error
def test_mixing_energies_are_fit(datasets_db): """Tests that given mixing energy data, the excess parameter is fit.""" phase_models = { "components": ["AL", "B"], "phases": { "LIQUID": { "sublattice_model": [["AL", "B"]], "sublattice_site_ratios": [1] }, "FCC_A1": { "sublattice_model": [["AL", "B"]], "sublattice_site_ratios": [1] } } } dataset_excess_mixing = { "components": ["AL", "B"], "phases": ["FCC_A1"], "solver": { "sublattice_site_ratios": [1], "sublattice_occupancies": [[[0.5, 0.5]]], "sublattice_configurations": [[["AL", "B"]]], "mode": "manual" }, "conditions": { "P": 101325, "T": 298.15 }, "output": "HM_MIX", "values": [[[-10000]]] } datasets_db.insert(dataset_excess_mixing) dbf = generate_parameters(phase_models, datasets_db, 'SGTE91', 'linear') assert dbf.elements == {'AL', 'B'} assert set(dbf.phases.keys()) == {'LIQUID', 'FCC_A1'} assert len(dbf._parameters.search(where('parameter_type') == 'L')) == 1 assert dbf.symbols['VV0000'] == -40000 # check that read/write is ok read_dbf = dbf.from_string(dbf.to_string(fmt='tdb'), fmt='tdb') assert read_dbf.elements == {'AL', 'B'} assert set(read_dbf.phases.keys()) == {'LIQUID', 'FCC_A1'} assert len( read_dbf._parameters.search(where('parameter_type') == 'L')) == 1 from espei.error_functions import calculate_thermochemical_error, get_thermochemical_data # the error should be exactly 0 because we are only fitting to one point zero_error_prob = scipy.stats.norm(loc=0, scale=500.0).logpdf( 0.0) # HM weight = 500 # Explicitly pass parameters={} to not try fitting anything thermochemical_data = get_thermochemical_data(dbf, sorted(read_dbf.elements), list(read_dbf.phases.keys()), datasets_db, symbols_to_fit=[]) error = calculate_thermochemical_error(dbf, sorted(read_dbf.elements), thermochemical_data) assert np.isclose(error, zero_error_prob, atol=1e-6)