示例#1
0
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
示例#2
0
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
示例#3
0
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
示例#4
0
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