def eqdataplot(eq, datasets, ax=None, plot_kwargs=None): """ Plot datapoints corresponding to the components and phases in the eq Dataset. A convenience function for dataplot. Parameters ---------- eq : xarray.Dataset Result of equilibrium calculation. datasets : PickleableTinyDB Database of phase equilibria datasets ax : matplotlib.Axes Default axes used if not specified. plot_kwargs : dict Keyword arguments to pass to dataplot Returns ------- A plot of phase equilibria points as a figure Examples -------- >>> from pycalphad import equilibrium, Database, variables as v >>> from pycalphad.plot.eqplot import eqplot >>> from espei.datasets import load_datasets, recursive_glob >>> datasets = load_datasets(recursive_glob('.', '*.json')) >>> dbf = Database('my_databases.tdb') >>> my_phases = list(dbf.phases.keys()) >>> eq = equilibrium(dbf, ['CU', 'MG', 'VA'], my_phases, {v.P: 101325, v.T: (500, 1000, 10), v.X('MG'): (0, 1, 0.01)}) >>> ax = eqplot(eq) >>> ax = eqdataplot(eq, datasets, ax=ax) """ # TODO: support reference legend conds = OrderedDict([ (_map_coord_to_variable(key), unpack_condition(np.asarray(value))) for key, value in sorted(eq.coords.items(), key=str) if (key == 'T') or (key == 'P') or (key.startswith('X_')) ]) phases = list( map( str, sorted(set(np.array(eq.Phase.values.ravel(), dtype='U')) - {''}, key=str))) comps = list( map( str, sorted(np.array(eq.coords['component'].values, dtype='U'), key=str))) ax = dataplot(comps, phases, conds, datasets, ax=ax, plot_kwargs=plot_kwargs) return ax
def eqdataplot(eq, datasets, ax=None, plot_kwargs=None): """ Plot datapoints corresponding to the components and phases in the eq Dataset. A convenience function for dataplot. Parameters ---------- eq : xarray.Dataset Result of equilibrium calculation. datasets : PickleableTinyDB Database of phase equilibria datasets ax : matplotlib.Axes Default axes used if not specified. plot_kwargs : dict Keyword arguments to pass to dataplot Returns ------- A plot of phase equilibria points as a figure Examples -------- >>> from pycalphad import equilibrium, Database, variables as v # doctest: +SKIP >>> from pycalphad.plot.eqplot import eqplot # doctest: +SKIP >>> from espei.datasets import load_datasets, recursive_glob # doctest: +SKIP >>> datasets = load_datasets(recursive_glob('.', '*.json')) # doctest: +SKIP >>> dbf = Database('my_databases.tdb') # doctest: +SKIP >>> my_phases = list(dbf.phases.keys()) # doctest: +SKIP >>> eq = equilibrium(dbf, ['CU', 'MG', 'VA'], my_phases, {v.P: 101325, v.T: (500, 1000, 10), v.X('MG'): (0, 1, 0.01)}) # doctest: +SKIP >>> ax = eqplot(eq) # doctest: +SKIP >>> ax = eqdataplot(eq, datasets, ax=ax) # doctest: +SKIP """ deprecation_msg = ( "`espei.plot.eqdataplot` is deprecated and will be removed in ESPEI 0.9. " "Users depending on plotting from an `pycalphad.equilibrium` result should use " "`pycalphad.plot.eqplot.eqplot` along with `espei.plot.dataplot` directly. " "Note that pycalphad's mapping can offer signficant reductions in calculation " "time compared to using `equilibrium` followed by `eqplot`." ) warnings.warn(deprecation_msg, category=FutureWarning) # TODO: support reference legend conds = OrderedDict([(_map_coord_to_variable(key), unpack_condition(np.asarray(value))) for key, value in sorted(eq.coords.items(), key=str) if (key == 'T') or (key == 'P') or (key.startswith('X_'))]) phases = list(map(str, sorted(set(np.array(eq.Phase.values.ravel(), dtype='U')) - {''}, key=str))) comps = list(map(str, sorted(np.array(eq.coords['component'].values, dtype='U'), key=str))) ax = dataplot(comps, phases, conds, datasets, ax=ax, plot_kwargs=plot_kwargs) return ax
def calculate_activity_error(dbf, comps, phases, datasets, parameters=None, phase_models=None, callables=None, grad_callables=None, hess_callables=None, massfuncs=None, massgradfuncs=None): """ Return the sum of square error from activity data Parameters ---------- dbf : pycalphad.Database Database to consider comps : list List of active component names phases : list List of phases to consider datasets : espei.utils.PickleableTinyDB Datasets that contain single phase data parameters : dict Dictionary of symbols that will be overridden in pycalphad.equilibrium phase_models : dict Phase models to pass to pycalphad calculations callables : dict Callables to pass to pycalphad grad_callables : dict Gradient callables to pass to pycalphad hess_callables : dict Hessian callables to pass to pycalphad massfuncs : dict Callables of mass derivatives to pass to pycalphad massgradfuncs : dict Gradient callables of mass derivatives to pass to pycalphad Returns ------- float A single float of the sum of square errors Notes ----- General procedure: 1. Get the datasets 2. For each dataset a. Calculate reference state equilibrium b. Calculate current chemical potentials c. Find the target chemical potentials d. Calculate error due to chemical potentials """ if parameters is None: parameters = {} activity_datasets = datasets.search( (tinydb.where('output').test(lambda x: 'ACR' in x)) & (tinydb.where('components').test(lambda x: set(x).issubset(comps)))) error = 0 if len(activity_datasets) == 0: return error for ds in activity_datasets: acr_component = ds['output'].split('_')[1] # the component of interest # calculate the reference state equilibrium ref = ds['reference_state'] ref_conditions = { _map_coord_to_variable(coord): val for coord, val in ref['conditions'].items() } ref_result = equilibrium( dbf, ds['components'], ref['phases'], ref_conditions, model=phase_models, parameters=parameters, massfuncs=massfuncs, massgradfuncs=massgradfuncs, callables=callables, grad_callables=grad_callables, hess_callables=hess_callables, ) # calculate current chemical potentials # get the conditions conditions = {} # first make sure the conditions are paired # only get the compositions, P and T are special cased conds_list = [(cond, value) for cond, value in ds['conditions'].items() if cond not in ('P', 'T')] # ravel the conditions # we will ravel each composition individually, since they all must have the same shape for comp_name, comp_x in conds_list: P, T, X = ravel_conditions(ds['values'], ds['conditions']['P'], ds['conditions']['T'], comp_x) conditions[v.P] = P conditions[v.T] = T conditions[_map_coord_to_variable(comp_name)] = X # do the calculations # we cannot currently turn broadcasting off, so we have to do equilibrium one by one # invert the conditions dicts to make a list of condition dicts rather than a condition dict of lists # assume now that the ravelled conditions all have the same size conditions_list = [{c: conditions[c][i] for c in conditions.keys()} for i in range(len(conditions[v.T]))] current_chempots = [] for conds in conditions_list: sample_eq_res = equilibrium( dbf, ds['components'], phases, conds, model=phase_models, parameters=parameters, massfuncs=massfuncs, massgradfuncs=massgradfuncs, callables=callables, grad_callables=grad_callables, hess_callables=hess_callables, ) current_chempots.append( sample_eq_res.MU.sel( component=acr_component).values.flatten()[0]) current_chempots = np.array(current_chempots) # calculate target chempots target_chempots = target_chempots_from_activity( acr_component, np.array(ds['values']).flatten(), conditions[v.T], ref_result) # calculate the error error += chempot_error(current_chempots, target_chempots) # TODO: write a test for this if np.any(np.isnan(np.array([ error ], dtype=np.float64))): # must coerce sympy.core.numbers.Float to float64 return -np.inf return error
def calculate_activity_error(dbf, comps, phases, datasets, parameters=None, phase_models=None, callables=None, data_weight=1.0): """ Return the sum of square error from activity data Parameters ---------- dbf : pycalphad.Database Database to consider comps : list List of active component names phases : list List of phases to consider datasets : espei.utils.PickleableTinyDB Datasets that contain single phase data parameters : dict Dictionary of symbols that will be overridden in pycalphad.equilibrium phase_models : dict Phase models to pass to pycalphad calculations callables : dict Callables to pass to pycalphad data_weight : float Weight for standard deviation of activity measurements, dimensionless. Corresponds to the standard deviation of differences in chemical potential in typical measurements of activity, in J/mol. Returns ------- float A single float of the sum of square errors Notes ----- General procedure: 1. Get the datasets 2. For each dataset a. Calculate reference state equilibrium b. Calculate current chemical potentials c. Find the target chemical potentials d. Calculate error due to chemical potentials """ std_dev = 500 # J/mol if parameters is None: parameters = {} activity_datasets = datasets.search( (tinydb.where('output').test(lambda x: 'ACR' in x)) & (tinydb.where('components').test(lambda x: set(x).issubset(comps)))) error = 0 if len(activity_datasets) == 0: return error for ds in activity_datasets: acr_component = ds['output'].split('_')[1] # the component of interest # calculate the reference state equilibrium ref = ds['reference_state'] # data_comps and data_phases ensures that we only do calculations on # the subsystem of the system defining the data. data_comps = ds['components'] data_phases = filter_phases(dbf, unpack_components(dbf, data_comps), candidate_phases=phases) ref_conditions = { _map_coord_to_variable(coord): val for coord, val in ref['conditions'].items() } ref_result = equilibrium(dbf, data_comps, ref['phases'], ref_conditions, model=phase_models, parameters=parameters, callables=callables) # calculate current chemical potentials # get the conditions conditions = {} # first make sure the conditions are paired # only get the compositions, P and T are special cased conds_list = [(cond, value) for cond, value in ds['conditions'].items() if cond not in ('P', 'T')] # ravel the conditions # we will ravel each composition individually, since they all must have the same shape for comp_name, comp_x in conds_list: P, T, X = ravel_conditions(ds['values'], ds['conditions']['P'], ds['conditions']['T'], comp_x) conditions[v.P] = P conditions[v.T] = T conditions[_map_coord_to_variable(comp_name)] = X # do the calculations # we cannot currently turn broadcasting off, so we have to do equilibrium one by one # invert the conditions dicts to make a list of condition dicts rather than a condition dict of lists # assume now that the ravelled conditions all have the same size conditions_list = [{c: conditions[c][i] for c in conditions.keys()} for i in range(len(conditions[v.T]))] current_chempots = [] for conds in conditions_list: sample_eq_res = equilibrium(dbf, data_comps, data_phases, conds, model=phase_models, parameters=parameters, callables=callables) current_chempots.append( sample_eq_res.MU.sel( component=acr_component).values.flatten()[0]) current_chempots = np.array(current_chempots) # calculate target chempots samples = np.array(ds['values']).flatten() target_chempots = target_chempots_from_activity( acr_component, samples, conditions[v.T], ref_result) # calculate the error weight = ds.get('weight', 1.0) pe = chempot_error(current_chempots, target_chempots, std_dev=std_dev / data_weight / weight) error += np.sum(pe) _log.debug( 'Data: %s, chemical potential difference: %s, probability: %s, reference: %s', samples, current_chempots - target_chempots, pe, ds["reference"]) # TODO: write a test for this if np.any(np.isnan(np.array([ error ], dtype=np.float64))): # must coerce sympy.core.numbers.Float to float64 return -np.inf return error