Beispiel #1
0
def test_mole_and_mass_fraction_conversions():
    """Test mole <-> mass conversions work as expected."""
    # Passing database as a mass dict works
    dbf = Database(CUO_TDB)
    mole_fracs = {v.X('O'): 0.5}
    mass_fracs = v.get_mass_fractions(mole_fracs, v.Species('CU'), dbf)
    assert np.isclose(mass_fracs[v.W('O')], 0.20113144)  # TC
    # Conversion back works
    round_trip_mole_fracs = v.get_mole_fractions(mass_fracs, 'CU', dbf)
    assert all(
        np.isclose(round_trip_mole_fracs[mf], mole_fracs[mf])
        for mf in round_trip_mole_fracs.keys())

    # Using Thermo-Calc's define components to define Al2O3 and TiO2
    # Mass dict defined by hand
    md = {'AL': 26.982, 'TI': 47.88, 'O': 15.999}
    alumina = v.Species('AL2O3')
    mass_fracs = {v.W(alumina): 0.81, v.W("TIO2"): 0.13}
    mole_fracs = v.get_mole_fractions(mass_fracs, 'O', md)
    assert np.isclose(mole_fracs[v.X('AL2O3')], 0.59632604)  # TC
    assert np.isclose(mole_fracs[v.X('TIO2')], 0.12216562)  # TC
    # Conversion back works
    round_trip_mass_fracs = v.get_mass_fractions(mole_fracs, v.Species('O'),
                                                 md)
    assert all(
        np.isclose(round_trip_mass_fracs[mf], mass_fracs[mf])
        for mf in round_trip_mass_fracs.keys())
Beispiel #2
0
def test_eq_overdetermined_comps():
    """
    The number of composition conditions should yield exactly one dependent component.
    This is the overdetermined case.
    """
    equilibrium(ALFE_DBF, ['AL', 'FE'], 'LIQUID', {v.T: 2000, v.P: 101325,
                                                   v.X('FE'): 0.2, v.X('AL'): 0.8})
Beispiel #3
0
def test_eq_charge_ndzro():
    """Nd-Zr-O system (with charged species) are correctly calculated and charged balanced in equilibrium"""
    comps = ['ND', 'ZR', 'O', 'VA']
    phases = ['ND2O3_A', 'PYRO']
    conds = {v.P: 101325, v.N: 1, v.T: 1400, v.X('ND'): 0.25, v.X('O'): 0.625}
    # Higher point density is required for convergence. Lower point densities
    # Can result in either no phases, or only FLUO phase (incorrect)
    res = equilibrium(AL2O3_ND2O3_ZRO2_DBF, comps, phases, conds, verbose=True)
    # Values below checked with Thermo-Calc
    assert np.isclose(-432325.423784, res.GM.values.squeeze())
    assert np.all(res.Phase.values.squeeze() == np.array(['ND2O3_A', 'PYRO', '', '']))
    assert np.allclose(res.NP.values.squeeze()[:2], [0.30164254, 0.69835646])
    # site fractions of ND2O3_A
    Y_ND2O3_A = res.Y.values.squeeze()[0, :5]
    Y_PYRO = res.Y.values.squeeze()[1, :]
    SPEC_CHG_ND2O3_A = np.array([3, 4, -2, -2, 0])  # (ND+3,ZR+4):(O-2):(O-2,VA)
    SITE_RATIO_ND2O3_A = np.array([2, 2, 3, 1, 1])  # 2:3:1
    SPEC_CHG_PYRO = np.array([3, 4, 3, 4, -2, 0, -2, -2, 0])  # (ND+3,ZR+4):(ND+3,ZR+4):(O-2,VA):(O-2):(O-2,VA)
    SITE_RATIO_PYRO = np.array([2, 2, 2, 2, 6, 6, 1, 1, 1])  # 2:2:6:1:1
    CHG_ND2O3_A = np.dot(Y_ND2O3_A*SPEC_CHG_ND2O3_A, SITE_RATIO_ND2O3_A)
    CHG_PYRO = np.dot(Y_PYRO*SPEC_CHG_PYRO, SITE_RATIO_PYRO)
    print('CHARGE ND2O3_A', CHG_ND2O3_A)
    print('CHARGE PYRO', CHG_PYRO)
    assert np.isclose(CHG_ND2O3_A, 0)
    assert np.isclose(CHG_PYRO, 0)
    assert np.allclose(Y_ND2O3_A, [9.79497936e-01, 2.05020639e-02, 1.00000000e+00, 2.05020639e-02, 9.79497936e-01],
                       rtol=5e-4)
    assert np.allclose(Y_PYRO, [9.99970071e-01, 2.99288042e-05, 3.83395063e-02, 9.61660494e-01, 9.93381787e-01,
                                6.61821340e-03, 1.00000000e+00, 1.39970285e-03, 9.98600297e-01], rtol=5e-4)
Beispiel #4
0
def test_eq_missing_component():
    """
    Specifying a condition involving a non-existent component raises an error.
    """
    # No Co or Cr in this database ; Co condition specification should cause failure
    equilibrium(ALNIFCC4SL_DBF, ['AL', 'NI', 'VA'], ['LIQUID'],
                {v.T: 1523, v.X('AL'): 0.88811111111111107,
                 v.X('CO'): 0.11188888888888888, v.P: 101325})
Beispiel #5
0
def test_eq_charge_alfeo():
    """Phases with charged species are correctly calculated and charged balanced in equilibrium"""
    alfeo = ALFEO_DBF
    comps = ['AL', 'FE', 'O', 'VA']
    phases = list(alfeo.phases.keys())
    conds = {v.P: 101325, v.N: 1, v.T: 500, v.X('FE'): 0.2, v.X('O'): 0.6}
    res = equilibrium(alfeo, comps, phases, conds, verbose=True)
    # Values below checked with Thermo-Calc
    assert np.allclose(res.NP.values.squeeze()[:2], [0.47826862, 0.52173138])
    assert np.allclose(res.GM.values.flat[0], -257462.33)
def test_eq_ternary_edge_case_mass():
    """
    Equilibrium along an edge of composition space will still balance mass.
    """
    eq = equilibrium(ALCOCRNI_DBF, ['AL', 'CO', 'CR', 'VA'], ['L12_FCC', 'BCC_B2', 'LIQUID'],
                     {v.T: 1523, v.X('AL'): 0.88811111111111107,
                      v.X('CO'): 0.11188888888888888, v.P: 101325}, verbose=True)
    mass_error = np.nansum(np.squeeze(eq.NP * eq.X), axis=-2) - \
                 [0.88811111111111107, 0.11188888888888888, 0]
    assert np.all(np.abs(mass_error) < 0.01)
def test_eq_ternary_edge_misc_gap():
    """
    Equilibrium at edge of miscibility gap will still balance mass.
    """
    eq = equilibrium(ALCOCRNI_DBF, ['AL', 'CO', 'CR', 'VA'], ['L12_FCC', 'BCC_B2', 'LIQUID'],
                     {v.T: 1523, v.X('AL'): 0.33366666666666667,
                      v.X('CO'): 0.44455555555555554, v.P: 101325}, verbose=True)
    mass_error = np.nansum(np.squeeze(eq.NP * eq.X), axis=-2) - \
                 [0.33366666666666667, 0.44455555555555554, 0.22177777777777785]
    assert np.all(np.abs(mass_error) < 0.001)
def test_eq_ternary_inside_mass():
    """
    Equilibrium in interior of composition space will still balance mass.
    """
    # This test cannot be checked in TC due to a lack of significant figures in the composition
    eq = equilibrium(ALCOCRNI_DBF, ['AL', 'CO', 'CR', 'VA'], ['L12_FCC', 'BCC_B2', 'LIQUID'],
                     {v.T: 1523, v.X('AL'): 0.44455555555555554,
                      v.X('CO'): 0.22277777777777777, v.P: 101325}, verbose=True)
    assert_allclose(eq.GM.values, -105871.20, atol=0.1)
    assert_allclose(eq.MU.values.flatten(), [-104655.532294, -142591.644379,  -82905.085459], atol=0.1)
Beispiel #9
0
def test_eqplot_ternary():
    """
    eqplot should return an axes object that has a traingular projection when
    two independent components and one independent potential are passed.
    """
    eq = equilibrium(ALCOCRNI_DBF, ['AL', 'CO', 'CR', 'VA'], ['LIQUID'],
                     {v.T: 2500, v.X('AL'): (0,0.5,0.33), v.X('CO'): (0,0.5,0.3), v.P: 101325})
    ax = eqplot(eq)
    assert isinstance(ax, Axes)
    assert ax.name == 'triangular'
def test_eq_issue43_chempots_tricky_potentials():
    """
    Ternary equilibrium with difficult convergence for chemical potentials (gh-43).
    """
    eq = equilibrium(ISSUE43_DBF, ['AL', 'NI', 'CR', 'VA'], ['FCC_A1', 'GAMMA_PRIME'],
                     {v.X('AL'): .1246, v.X('CR'): 0.6, v.T: 1273, v.P: 101325},
                     verbose=True)
    chempots = np.array([-135620.9960449, -47269.29002414, -92304.23688281])
    assert_allclose(eq.GM.values, -70680.53695)
    assert_allclose(np.squeeze(eq.MU.values), chempots)
Beispiel #11
0
def test_eq_ternary_inside_mass():
    """
    Equilibrium in interior of composition space will still balance mass.
    """
    eq = equilibrium(ALCOCRNI_DBF, ['AL', 'CO', 'CR', 'VA'], ['L12_FCC', 'BCC_B2', 'LIQUID'],
                     {v.T: 1523, v.X('AL'): 0.44455555555555554,
                      v.X('CO'): 0.22277777777777777, v.P: 101325}, verbose=True)
    assert_allclose(eq.GM.values, -105871.54, atol=0.1)  # Thermo-Calc: -105871.54
    # Thermo-Calc: [-104653.83, -142595.49, -82905.784]
    assert_allclose(eq.MU.values.flatten(), [-104653.83, -142595.49, -82905.784], atol=0.1)
Beispiel #12
0
def test_eq_issue43_chempots_misc_gap():
    """
    Equilibrium for complex ternary miscibility gap (gh-43).
    """
    eq = equilibrium(ISSUE43_DBF, ['AL', 'NI', 'CR', 'VA'], 'GAMMA_PRIME',
                     {v.X('AL'): .1246, v.X('CR'): 1e-9, v.T: 1273, v.P: 101325},
                     verbose=True)
    chempots = np.array([-206144.57, -272150.79, -64253.652])
    assert_allclose(np.nansum(np.squeeze(eq.NP * eq.X), axis=-2), [0.1246, 1e-9, 1-(.1246+1e-9)], rtol=3e-5)
    assert_allclose(np.squeeze(eq.MU.values), chempots, rtol=1e-5)
    assert_allclose(np.squeeze(eq.GM.values), -81933.259)
def test_eq_issue43_chempots_misc_gap():
    """
    Equilibrium for complex ternary miscibility gap (gh-43).
    """
    eq = equilibrium(ISSUE43_DBF, ['AL', 'NI', 'CR', 'VA'], 'GAMMA_PRIME',
                     {v.X('AL'): .1246, v.X('CR'): 1e-9, v.T: 1273, v.P: 101325},
                     verbose=True)
    chempots = 8.31451 * np.squeeze(eq['T'].values) * np.array([-19.47631644, -25.71249032,  -6.0706158])
    mass_error = np.nansum(np.squeeze(eq.NP * eq.X), axis=-2) - \
                 [0.1246, 1e-9, 1-(.1246+1e-9)]
    assert np.max(np.fabs(mass_error)) < 1e-9
    assert_allclose(np.squeeze(eq.GM.values), -81933.259)
    assert_allclose(np.squeeze(eq.MU.values), chempots, atol=1)
Beispiel #14
0
def test_eq_ternary_edge_case_mass():
    """
    Equilibrium along an edge of composition space will still balance mass.
    """
    eq = equilibrium(ALCOCRNI_DBF, ['AL', 'CO', 'CR', 'VA'], ['L12_FCC', 'BCC_B2', 'LIQUID'],
                     {v.T: 1523, v.X('AL'): 0.8881111111,
                      v.X('CO'): 0.1118888888, v.P: 101325}, verbose=True)
    mass_error = np.nansum(np.squeeze(eq.NP * eq.X), axis=-2) - \
                 [0.8881111111, 0.1118888888, 1e-10]
    assert_allclose(eq.GM.values, -97913.542)  # from Thermo-Calc 2017b
    result_chempots = eq.MU.values.flatten()
    assert_allclose(result_chempots[:2], [-86994.575, -184582.17], atol=0.1)  # from Thermo-Calc 2017b
    assert result_chempots[2] < -300000  # Estimated
    assert np.all(np.abs(mass_error) < 1.5e-10)
Beispiel #15
0
def test_eq_ternary_inside_mass():
    """
    Equilibrium in interior of composition space will still balance mass.
    """
    eq = equilibrium(ALCOCRNI_DBF, ['AL', 'CO', 'CR', 'VA'],
                     ['L12_FCC', 'BCC_B2', 'LIQUID'], {
                         v.T: 1523,
                         v.X('AL'): 0.44455555555555554,
                         v.X('CO'): 0.22277777777777777,
                         v.P: 101325
                     },
                     verbose=True)
    mass_error = np.nansum(np.squeeze(eq.NP * eq.X), axis=-2) - \
                 [0.44455555555555554, 0.22277777777777777, 0.333]
    assert np.all(np.abs(mass_error) < 0.01)
def test_eq_issue62_last_component_not_va():
    """
    VA is not last when components are sorted alphabetically.
    """
    test_tdb = """
    ELEMENT VA   VACUUM                    0.0000E+00  0.0000E+00  0.0000E+00!
    ELEMENT AL   FCC_A1                    2.6982E+01  4.5773E+03  2.8322E+01!
    ELEMENT CO   HCP_A3                    5.8933E+01  4.7656E+03  3.0040E+00!
    ELEMENT CR   BCC_A2                    5.1996E+01  4.0500E+03  2.3560E+01!
    ELEMENT W    BCC_A2                    1.8385E+02  4.9700E+03  3.2620E+01!
    PHASE FCC_A1  %  2 1   1 !
    CONSTITUENT FCC_A1  :AL,CO,CR,W : VA% :  !
    """
    equilibrium(Database(test_tdb), ['AL', 'CO', 'CR', 'W', 'VA'], ['FCC_A1'],
                {"T": 1248, "P": 101325, v.X("AL"): 0.081, v.X("CR"): 0.020, v.X("W"): 0.094})
def test_eq_b2_without_all_comps():
    """
    All-vacancy endmembers are correctly excluded from the computation when fewer than
    all components in a Database are selected for the calculation.
    """
    equilibrium(Database(ALNIPT_TDB), ['AL', 'NI', 'VA'], 'BCC_B2', {v.X('NI'): 0.4, v.P: 101325, v.T: 1200},
                verbose=True)
def test_eq_model_phase_name():
    """
    Phase name is set in PhaseRecord when using Model-based JIT compilation.
    """
    eq = equilibrium(ALFE_DBF, ['AL', 'FE', 'VA'], 'LIQUID',
                     {v.X('FE'): 0.3, v.T: 1000, v.P: 101325}, model=Model)
    assert eq.Phase.sel(vertex=0).isel(T=0, P=0, X_FE=0) == 'LIQUID'
Beispiel #19
0
def test_equlibrium_no_opt_solver():
    """Passing in a solver with `ignore_convergence = True` gives a result."""
    class NoOptSolver(InteriorPointSolver):
        ignore_convergence = True

    comps = ['PB', 'SN', 'VA']
    phases = list(PBSN_DBF.phases.keys())
    conds = {v.T: 300, v.P: 101325, v.X('SN'): 0.50}
    ipopt_solver_eq_res = equilibrium(PBSN_DBF,
                                      comps,
                                      phases,
                                      conds,
                                      solver=InteriorPointSolver(),
                                      verbose=True)
    no_opt_eq_res = equilibrium(PBSN_DBF,
                                comps,
                                phases,
                                conds,
                                solver=NoOptSolver(),
                                verbose=True)

    ipopt_GM = ipopt_solver_eq_res.GM.values.squeeze()
    no_opt_GM = no_opt_eq_res.GM.values.squeeze()
    no_opt_MU = no_opt_eq_res.MU.values.squeeze()
    assert ipopt_GM != no_opt_GM  # global min energy is different from lower convex hull
    assert np.allclose([-17452.5115967],
                       no_opt_GM)  # energy from lower convex hull
    assert np.allclose([-19540.6522632, -15364.3709302],
                       no_opt_MU)  # chempots from lower convex hull
def test_equilibrium_solidification_result_properties():
    """Test that SolidificationResult objects produced by equilibrium have the required properties."""
    # Required properties are that the shape of the output arrays are matching
    # NOTE: final phase amounts are not tested because they are not guaranteed
    # to be 0.0 or 1.0 in the same way as in the Scheil simulations.

    dbf = Database(os.path.join(os.path.dirname(__file__), 'alzn_mey.tdb'))
    comps = ['AL', 'ZN', 'VA']
    phases = sorted(dbf.phases.keys())

    liquid_phase_name = 'LIQUID'
    initial_composition = {v.X('ZN'): 0.3}
    start_temperature = 850
    end_temperature = 650

    sol_res = simulate_equilibrium_solidification(
        dbf,
        comps,
        phases,
        initial_composition,
        start_temperature=start_temperature,
        end_temperature=end_temperature,
        step_temperature=20.0)

    num_temperatures = len(sol_res.temperatures)
    assert num_temperatures == len(sol_res.x_liquid)
    assert num_temperatures == len(sol_res.fraction_liquid)
    assert num_temperatures == len(sol_res.fraction_solid)
    assert all(
        [num_temperatures == len(np) for np in sol_res.phase_amounts.values()])
Beispiel #21
0
def plot_phase_diagram(dbf,
                       trace,
                       lnprob,
                       datasets,
                       temperatures=(300, 2500, 10),
                       fname="phase_diagram.png"):
    # enable making an initial plot
    if (trace is not None) and (lnprob is not None):
        opt_parameters = optimal_parameters_dict(dbf, trace, lnprob)
    else:
        opt_parameters = dict()
    comps = [sp.name for sp in dbf.species]
    non_va_comps = sorted(set(comps) - {"VA"})
    phases = list(dbf.phases.keys())

    ax = multiplot(
        dbf,
        comps,
        phases,
        {
            v.P: 101325,
            v.T: temperatures,
            v.X(non_va_comps[-1]): (0, 1, 0.05)
        },
        datasets,
        eq_kwargs={"parameters": opt_parameters},
    )

    fig = ax.figure
    ax.set_xlim(0, 1)
    ax.set_ylim(*(temperatures[:2]))
    fig.savefig(fname)
    return fig
def test_eq_on_endmember():
    """
    When the composition condition is right on top of an end-member
    the convex hull is still correctly constructed (gh-28).
    """
    equilibrium(ALFE_DBF, ['AL', 'FE', 'VA'], ['LIQUID', 'B2_BCC'],
                {v.X('AL'): [0.4, 0.5, 0.6], v.T: [300, 600], v.P: 101325}, verbose=True)
Beispiel #23
0
def test_adding_compsets_to_zpf_boundary_sets():
    """Test that new composition sets can be added to ZPFBoundarySets successfully."""
    compsets_298 = CompsetPair([
        BinaryCompset('P1', 298.15, 'B', 0.5, [0.5, 0.5]),
        BinaryCompset('P2', 298.15, 'B', 0.8, [0.2, 0.8]),
    ])

    compsets_300 = CompsetPair([
        BinaryCompset('P1', 300, 'B', 0.5, [0.5, 0.5]),
        BinaryCompset('P2', 300, 'B', 0.8, [0.2, 0.8]),
    ])

    compsets_300_diff_phases = CompsetPair([
        BinaryCompset('P2', 300, 'B', 0.5, [0.5, 0.5]),
        BinaryCompset('P3', 300, 'B', 0.8, [0.2, 0.8]),
    ])

    zpfbs = ZPFBoundarySets(['A', 'B'], v.X('B'))
    assert zpfbs.components == ['A', 'B']
    assert len(zpfbs.two_phase_regions) == 0
    assert len(zpfbs.all_compsets) == 0

    zpfbs.add_compsets(compsets_298)
    assert len(zpfbs.all_compsets) == 1
    assert len(zpfbs.two_phase_regions) == 1

    zpfbs.add_compsets(compsets_300)  # same region, different temperature
    assert len(zpfbs.all_compsets) == 2
    assert len(zpfbs.two_phase_regions) == 1

    zpfbs.add_compsets(
        compsets_300_diff_phases)  # new region, different phases
    assert len(zpfbs.all_compsets) == 3
    assert len(zpfbs.two_phase_regions) == 2
def test_eq_output_property():
    """
    Extra properties can be specified to `equilibrium`.
    """
    equilibrium(ALFE_DBF, ['AL', 'FE', 'VA'], ['LIQUID', 'B2_BCC'],
                {v.X('AL'): 0.25, v.T: (300, 2000, 500), v.P: 101325},
                output=['heat_capacity', 'degree_of_ordering'])
Beispiel #25
0
def multi_phase_fit(dbf, comps, phases, datasets, phase_models, parameters=None, scheduler=None):
    scheduler = scheduler or dask.local
    # TODO: support distributed schedulers for multi_phase_fit.
    # This can be done if the scheduler passed is a distributed.worker_client
    if scheduler is not dask.local:
        raise NotImplementedError('Schedulers other than dask.local are not currently supported for multiphase fitting.')
    desired_data = datasets.search((tinydb.where('output') == 'ZPF') &
                                   (tinydb.where('components').test(lambda x: set(x).issubset(comps))) &
                                   (tinydb.where('phases').test(lambda x: len(set(phases).intersection(x)) > 0)))

    def safe_get(itms, idxx):
        try:
            return itms[idxx]
        except IndexError:
            return None

    fit_jobs = []
    for data in desired_data:
        payload = data['values']
        conditions = data['conditions']
        data_comps = list(set(data['components']).union({'VA'}))
        phase_regions = defaultdict(lambda: list())
        # TODO: Fix to only include equilibria listed in 'phases'
        for idx, p in enumerate(payload):
            phase_key = tuple(sorted(rp[0] for rp in p))
            if len(phase_key) < 2:
                # Skip single-phase regions for fitting purposes
                continue
            # Need to sort 'p' here so we have the sorted ordering used in 'phase_key'
            # rp[3] optionally contains additional flags, e.g., "disordered", to help the solver
            comp_dicts = [(dict(zip([v.X(x.upper()) for x in rp[1]], rp[2])), safe_get(rp, 3))
                          for rp in sorted(p, key=operator.itemgetter(0))]
            cur_conds = {}
            for key, value in conditions.items():
                value = np.atleast_1d(np.asarray(value))
                if len(value) > 1:
                    value = value[idx]
                cur_conds[getattr(v, key)] = float(value)
            phase_regions[phase_key].append((cur_conds, comp_dicts))
        for region, region_eq in phase_regions.items():
            for req in region_eq:
                # We are now considering a particular tie region
                current_statevars, comp_dicts = req
                region_chemical_potentials = \
                    dask.delayed(estimate_hyperplane)(dbf, data_comps, phases, current_statevars, comp_dicts,
                                                      phase_models, parameters)
                # Now perform the equilibrium calculation for the isolated phases and add the result to the error record
                for current_phase, cond_dict in zip(region, comp_dicts):
                    # TODO: Messy unpacking
                    cond_dict, phase_flag = cond_dict
                    # We are now considering a particular tie vertex
                    for key, val in cond_dict.items():
                        if val is None:
                            cond_dict[key] = np.nan
                    cond_dict.update(current_statevars)
                    error = dask.delayed(tieline_error)(dbf, data_comps, current_phase, cond_dict, region_chemical_potentials, phase_flag,
                                                        phase_models, parameters)
                    fit_jobs.append(error)
    errors = dask.compute(*fit_jobs, get=scheduler.get_sync)
    return errors
Beispiel #26
0
def get_zpf_data(comps, phases, datasets):
    """
    Return the ZPF data used in the calculation of ZPF error

    Parameters
    ----------
    comps : list
        List of active component names
    phases : list
        List of phases to consider
    datasets : espei.utils.PickleableTinyDB
        Datasets that contain single phase data

    Returns
    -------
    list
        List of data dictionaries with keys ``weight``, ``data_comps`` and
        ``phase_regions``. ``data_comps`` are the components for the data in
        question. ``phase_regions`` are the ZPF phases, state variables and compositions.
    """
    desired_data = datasets.search(
        (tinydb.where('output') == 'ZPF')
        & (tinydb.where('components').test(lambda x: set(x).issubset(comps)))
        & (tinydb.where('phases').test(
            lambda x: len(set(phases).intersection(x)) > 0)))

    zpf_data = []
    for data in desired_data:
        payload = data['values']
        conditions = data['conditions']
        # create a dictionary of each set of phases containing a list of individual points on the tieline
        # individual tieline points are tuples of (conditions, {composition dictionaries})
        phase_regions = defaultdict(lambda: list())
        # TODO: Fix to only include equilibria listed in 'phases'
        for idx, p in enumerate(payload):
            phase_key = tuple(sorted(rp[0] for rp in p))
            if len(phase_key) < 2:
                # Skip single-phase regions for fitting purposes
                continue
            # Need to sort 'p' here so we have the sorted ordering used in 'phase_key'
            # rp[3] optionally contains additional flags, e.g., "disordered", to help the solver
            comp_dicts = [(dict(zip([v.X(x.upper()) for x in rp[1]],
                                    rp[2])), _safe_index(rp, 3))
                          for rp in sorted(p, key=operator.itemgetter(0))]
            cur_conds = {}
            for key, value in conditions.items():
                value = np.atleast_1d(np.asarray(value))
                if len(value) > 1:
                    value = value[idx]
                cur_conds[getattr(v, key)] = float(value)
            phase_regions[phase_key].append((cur_conds, comp_dicts))

        data_dict = {
            'weight': data.get('weight', 1.0),
            'data_comps': list(set(data['components']).union({'VA'})),
            'phase_regions': list(phase_regions.items()),
            'dataset_reference': data['reference']
        }
        zpf_data.append(data_dict)
    return zpf_data
Beispiel #27
0
def test_eq_issue43_chempots_misc_gap():
    """
    Equilibrium for complex ternary miscibility gap (gh-43).
    """
    eq = equilibrium(ISSUE43_DBF, ['AL', 'NI', 'CR', 'VA'],
                     'GAMMA_PRIME', {
                         v.X('AL'): .1246,
                         v.X('CR'): 1e-9,
                         v.T: 1273,
                         v.P: 101325
                     },
                     verbose=True)
    chempots = 8.31451 * np.squeeze(eq['T'].values) * np.array(
        [[[[[-19.47631644, -25.71249032, -6.0706158]]]]])
    assert_allclose(eq.GM.values, -81933.259)
    assert_allclose(eq.MU.values, chempots, atol=1)
Beispiel #28
0
def test_equilibrium_solidification_result_properties():
    """Test that SolidificationResult objects produced by equilibrium have the required properties."""
    # Required properties are that the shape of the output arrays are matching
    # NOTE: final phase amounts are not tested because they are not guaranteed
    # to be 0.0 or 1.0 in the same way as in the Scheil simulations.

    dbf = Database(os.path.join(os.path.dirname(__file__), 'alzn_mey.tdb'))
    comps = ['AL', 'ZN', 'VA']
    phases = sorted(dbf.phases.keys())

    initial_composition = {v.X('ZN'): 0.3}
    start_temperature = 850

    sol_res = simulate_equilibrium_solidification(
        dbf,
        comps,
        phases,
        initial_composition,
        start_temperature=start_temperature,
        step_temperature=20.0,
        verbose=True)

    num_temperatures = len(sol_res.temperatures)
    assert num_temperatures == len(sol_res.fraction_liquid)
    assert num_temperatures == len(sol_res.fraction_solid)
    assert all(
        [num_temperatures == len(np) for np in sol_res.phase_amounts.values()])
    assert all([
        num_temperatures == len(liq_comps)
        for liq_comps in sol_res.x_liquid.values()
    ])
    assert all([
        num_temperatures == len(nphase)
        for nphase in sol_res.cum_phase_amounts.values()
    ])
    # The final cumulative solid phase amounts is 1.0
    assert np.isclose(
        np.sum([amnts[-1] for amnts in sol_res.cum_phase_amounts.values()]),
        1.0)
    # The final instantaneous phase amounts is not 1.0 (only the amount of new solid phase added
    assert np.sum([amnts[-1]
                   for amnts in sol_res.phase_amounts.values()]) < 1.0

    # Test serialization
    for ky, vl in sol_res.to_dict().items():
        print(ky, vl, type(ky), type(vl))
        json.dumps({ky: vl})
    json.dumps(sol_res.to_dict())

    # Test round tripping to/from dict
    rnd_trip_sol_res = SolidificationResult.from_dict(sol_res.to_dict())
    assert rnd_trip_sol_res.fraction_liquid == sol_res.fraction_liquid
    assert rnd_trip_sol_res.fraction_solid == sol_res.fraction_solid
    assert rnd_trip_sol_res.x_liquid == sol_res.x_liquid
    assert rnd_trip_sol_res.cum_phase_amounts == sol_res.cum_phase_amounts
    assert rnd_trip_sol_res.phase_amounts == sol_res.phase_amounts
    assert rnd_trip_sol_res.temperatures == sol_res.temperatures
    assert rnd_trip_sol_res.converged == sol_res.converged
    assert rnd_trip_sol_res.method == sol_res.method
def test_eq_binary():
    "Binary phase diagram point equilibrium calculation with magnetism."
    my_phases = ['LIQUID', 'FCC_A1', 'HCP_A3', 'AL5FE2',
                 'AL2FE', 'AL13FE4', 'AL5FE4']
    comps = ['AL', 'FE', 'VA']
    conds = {v.T: 1400, v.P: 101325, v.X('AL'): 0.55}
    eqx = equilibrium(ALFE_DBF, comps, my_phases, conds, verbose=True)
    assert_allclose(eqx.GM.values.flat[0], -9.608807e4)
def test_eq_avoid_phase_cycling():
    """
    Converge without getting stuck in an add/remove phase cycle.
    """
    # This set of conditions is known to trigger the issue
    my_phases_alfe = ['LIQUID', 'B2_BCC', 'FCC_A1', 'HCP_A3', 'AL5FE2', 'AL2FE', 'AL13FE4', 'AL5FE4']
    equilibrium(ALFE_DBF, ['AL', 'FE', 'VA'], my_phases_alfe, {v.X('AL'): 0.44,
                                                               v.T: 1600, v.P: 101325}, verbose=True)