示例#1
0
def axisanglerotationmatrix(axis_angle):
    R = axisanglemagnitude(axis_angle)

    x = Piecewise((axis_angle[0] / R, R > 0), (1, True))
    y = Piecewise((axis_angle[1] / R, R > 0), (0, True))
    z = Piecewise((axis_angle[2] / R, R > 0), (0, True))

    csr = sp.cos(R)
    one_minus_csr = (1 - csr)
    snr = sp.sin(R)

    return sp.Matrix([[
        csr + x * x * (1 - csr), x * y * one_minus_csr - z * snr,
        x * z * one_minus_csr + y * snr
    ],
                      [
                          y * x * one_minus_csr + z * snr,
                          csr + y * y * one_minus_csr,
                          y * z * one_minus_csr - x * snr
                      ],
                      [
                          z * x * one_minus_csr - y * snr,
                          z * y * one_minus_csr + x * snr,
                          csr + z * z * one_minus_csr
                      ]])
示例#2
0
def test_database_initialization_custom_refstate():
    """Test that a custom reference state with ficticious pure elements can be used to construct a Database"""
    refdata_stable = {
        "Q": Piecewise((symengine.oo, True)),
        "ZX": Piecewise((symengine.oo, True)),
    }
    refdata = {
        ("Q", "ALPHA"): Symbol("GHSERQQ"),
        ("Q", "BETA"): Symbol("GHSERQQ") + 10000.0,
        ("ZX", "BETA"): Symbol("GHSERZX"),
    }
    refdata_ser = {
        'Q': {
            'phase': 'ALPHA',
            'mass': 8.0,
            'H298': 80.0,
            'S298': 0.80
        },
        'ZX': {
            'phase': 'BETA',
            'mass': 52.0,
            'H298': 520.0,
            'S298': 5.20
        },
    }

    # Setup refdata
    CUSTOM_REFDATA_NAME = "CUSTOM"
    setattr(espei.refdata, CUSTOM_REFDATA_NAME + "Stable", refdata_stable)
    setattr(espei.refdata, CUSTOM_REFDATA_NAME, refdata)
    setattr(espei.refdata, CUSTOM_REFDATA_NAME + "SER", refdata_ser)

    # Test
    phase_models = {
        "components": ["Q", "ZX"],
        "phases": {
            "ALPHA": {
                "sublattice_model": [["Q"]],
                "sublattice_site_ratios": [1],
            },
            "BCC": {
                "aliases": ["BETA"],
                "sublattice_model": [["Q", "ZX"]],
                "sublattice_site_ratios": [1.0],
            },
        }
    }
    dbf = initialize_database(phase_models, CUSTOM_REFDATA_NAME)
    assert set(dbf.phases.keys()) == {"ALPHA", "BCC"}
    assert dbf.elements == {"Q", "ZX"}
    assert dbf.species == {v.Species("Q"), v.Species("ZX")}
    assert 'GHSERQQ' in dbf.symbols
    assert 'GHSERZX' in dbf.symbols
    assert dbf.refstates["Q"]["phase"] == "ALPHA"
    assert dbf.refstates["ZX"]["phase"] == "BCC"

    # Teardown refdata
    delattr(espei.refdata, CUSTOM_REFDATA_NAME + "Stable")
    delattr(espei.refdata, CUSTOM_REFDATA_NAME)
    delattr(espei.refdata, CUSTOM_REFDATA_NAME + "SER")
示例#3
0
def normalize_angle(angle):
    """
    Normalizes the angle to be -pi to +pi
    It takes and returns radians.
    """
    a = normalize_angle_positive(angle)
    return Piecewise([a - 2.0 * pi, a > pi], [a, True])
示例#4
0
文件: tdb.py 项目: bocklund/pycalphad
def _make_piecewise_ast(toks):
    """
    Convenience function for converting tokens into a piecewise symengine object.
    """
    cur_tok = 0
    expr_cond_pairs = []

    # Only one token: Not a piecewise function; just return the AST
    if len(toks) == 1:
        return _sympify_string(toks[0].strip(' ,'))

    while cur_tok < len(toks) - 1:
        low_temp = toks[cur_tok]
        try:
            high_temp = toks[cur_tok + 2]
        except IndexError:
            # No temperature limit specified
            high_temp = None

        if high_temp is None:
            expr_cond_pairs.append(
                (_sympify_string(toks[cur_tok + 1]), And(low_temp <= v.T)))
        else:
            expr_cond_pairs.append((_sympify_string(toks[cur_tok + 1]),
                                    And(low_temp <= v.T, v.T < high_temp)))
        cur_tok = cur_tok + 2
    expr_cond_pairs.append((0, True))
    return Piecewise(*expr_cond_pairs)
示例#5
0
def test_tc_printer_no_division_symbols():
    "TCPrinter does not produce division symbols in string output of symbolic expressions."
    test_expr = Piecewise((
        S('VV0000/T + T*VV0004 + T**2*VV0001 + T**3*VV0002 + T*LOG(T)*VV0003'),
        v.T > 0))
    result = TCPrinter().doprint(test_expr)
    assert '/' not in result
示例#6
0
def test_symbol_names_are_propagated_through_symbols_and_parameters():
    """A map of old symbol names to new symbol names should propagate through symbol and parameter SymPy expressions"""
    tdb_propagate_str = """$ Mangled function names should propagate through other symbols and parameters
    ELEMENT A PH 0 0 0 !
    FUNCTION FN1  298.15 -42; 6000 N !
    FUNCTION FN2 298.15 FN1#; 6000 N !
    PARAMETER G(PH,A;0) 298.15 FN1# + FN2#; 6000 N !
    """
    test_dbf = Database.from_string(tdb_propagate_str, fmt='tdb')
    rename_map = {'FN1': 'RENAMED_FN1', 'FN2': 'RENAMED_FN2'}
    _apply_new_symbol_names(test_dbf, rename_map)
    assert 'RENAMED_FN1' in test_dbf.symbols
    assert 'FN1' not in test_dbf.symbols  # check that the old key was removed
    assert test_dbf.symbols['RENAMED_FN2'] == Piecewise(
        (Symbol('RENAMED_FN1'), And(v.T < 6000.0, v.T >= 298.15)), (0, True))
    assert test_dbf._parameters.all()[0]['parameter'] == Piecewise(
        (Symbol('RENAMED_FN1') + Symbol('RENAMED_FN2'),
         And(v.T < 6000.0, v.T >= 298.15)), (0, True))
示例#7
0
文件: tdb.py 项目: bocklund/pycalphad
 def write_parameter(param_to_write):
     constituents = ':'.join([
         ','.join(sorted([i.name.upper() for i in subl]))
         for subl in param_to_write.constituent_array
     ])
     # TODO: Handle references
     paramx = param_to_write.parameter
     if not isinstance(paramx, Piecewise):
         # Non-piecewise parameters need to be wrapped to print correctly
         # Otherwise TC's TDB parser will fail
         paramx = Piecewise((paramx, And(v.T >= 1, v.T < 10000)))
     exprx = TCPrinter().doprint(paramx).upper()
     if ';' not in exprx:
         exprx += '; N'
     if param_to_write.diffusing_species != Species(None):
         ds = "&" + param_to_write.diffusing_species.name
     else:
         ds = ""
     return "PARAMETER {}({}{},{};{}) {} !\n".format(
         param_to_write.parameter_type.upper(),
         param_to_write.phase_name.upper(), ds, constituents,
         param_to_write.parameter_order, exprx)
示例#8
0
文件: tdb.py 项目: bocklund/pycalphad
def write_tdb(dbf, fd, groupby='subsystem', if_incompatible='warn'):
    """
    Write a TDB file from a pycalphad Database object.

    The goal is to produce TDBs that conform to the most restrictive subset of database specifications. Some of these
    can be adjusted for automatically, such as the Thermo-Calc line length limit of 78. Others require changing the
    database in non-trivial ways, such as the maximum length of function names (8). The default is to warn the user when
    attempting to write an incompatible database and the user must choose whether to warn and write the file anyway or
    to fix the incompatibility.

    Currently the supported compatibility fixes are:
    - Line length <= 78 characters (Thermo-Calc)
    - Function names <= 8 characters (Thermo-Calc)

    The current unsupported fixes include:
    - Keyword length <= 2000 characters (Thermo-Calc)
    - Element names <= 2 characters (Thermo-Calc)
    - Phase names <= 24 characters (Thermo-Calc)

    Other TDB compatibility issues required by Thermo-Calc or other software should be reported to the issue tracker.

    Parameters
    ----------
    dbf : Database
        A pycalphad Database.
    fd : file-like
        File descriptor.
    groupby : ['subsystem', 'phase'], optional
        Desired grouping of parameters in the file.
    if_incompatible : string, optional ['raise', 'warn', 'fix']
        Strategy if the database does not conform to the most restrictive database specification.
        The 'warn' option (default) will write out the incompatible database with a warning.
        The 'raise' option will raise a DatabaseExportError.
        The 'ignore' option will write out the incompatible database silently.
        The 'fix' option will rectify the incompatibilities e.g. through name mangling.
    """
    # Before writing anything, check that the TDB is valid and take the appropriate action if not
    if if_incompatible not in ['warn', 'raise', 'ignore', 'fix']:
        raise ValueError(
            'Incorrect options passed to \'if_invalid\'. Valid args are \'raise\', \'warn\', or \'fix\'.'
        )
    # Handle function names > 8 characters
    long_function_names = {k for k in dbf.symbols.keys() if len(k) > 8}
    if len(long_function_names) > 0:
        if if_incompatible == 'raise':
            raise DatabaseExportError(
                'The following function names are beyond the 8 character TDB limit: {}. Use the keyword argument \'if_incompatible\' to control this behavior.'
                .format(long_function_names))
        elif if_incompatible == 'fix':
            # if we are going to make changes, make the changes to a copy and leave the original object untouched
            dbf = deepcopy(
                dbf)  # TODO: if we do multiple fixes, we should only copy once
            symbol_name_map = {}
            for name in long_function_names:
                hashed_name = 'F' + str(
                    hashlib.md5(name.encode('UTF-8')).hexdigest()).upper(
                    )[:7]  # this is implictly upper(), but it is explicit here
                symbol_name_map[name] = hashed_name
            _apply_new_symbol_names(dbf, symbol_name_map)
        elif if_incompatible == 'warn':
            warnings.warn(
                'Ignoring that the following function names are beyond the 8 character TDB limit: {}. Use the keyword argument \'if_incompatible\' to control this behavior.'
                .format(long_function_names))
    # Begin constructing the written database
    writetime = datetime.datetime.now()
    maxlen = 78
    output = ""
    # Comment header block
    # Import here to prevent circular imports
    from pycalphad import __version__
    try:
        # getuser() will raise on Windows if it can't find a username: https://bugs.python.org/issue32731
        username = getpass.getuser()
    except:
        # if we can't find a good username, just choose a default and move on
        username = '******'
    output += ("$" * maxlen) + "\n"
    output += "$ Date: {}\n".format(writetime.strftime("%Y-%m-%d %H:%M"))
    output += "$ Components: {}\n".format(', '.join(sorted(dbf.elements)))
    output += "$ Phases: {}\n".format(', '.join(sorted(dbf.phases.keys())))
    output += "$ Generated by {} (pycalphad {})\n".format(
        username, __version__)
    output += ("$" * maxlen) + "\n\n"
    for element in sorted(dbf.elements):
        ref = dbf.refstates.get(element, {})
        refphase = ref.get('phase', 'BLANK')
        mass = ref.get('mass', 0.0)
        H298 = ref.get('H298', 0.0)
        S298 = ref.get('S298', 0.0)
        output += "ELEMENT {0} {1} {2} {3} {4} !\n".format(
            element.upper(), refphase, mass, H298, S298)
    if len(dbf.elements) > 0:
        output += "\n"
    for species in sorted(dbf.species, key=lambda s: s.name):
        if species.name not in dbf.elements:
            # construct the charge part of the specie
            if species.charge != 0:
                if species.charge > 0:
                    charge_sign = '+'
                else:
                    charge_sign = ''
                charge = '/{}{}'.format(charge_sign, species.charge)
            else:
                charge = ''
            species_constituents = ''.join([
                '{}{}'.format(el, val)
                for el, val in sorted(species.constituents.items(),
                                      key=lambda t: t[0])
            ])
            output += "SPECIES {0} {1}{2} !\n".format(species.name.upper(),
                                                      species_constituents,
                                                      charge)
    if len(dbf.species) > 0:
        output += "\n"
    # Write FUNCTION block
    for name, expr in sorted(dbf.symbols.items()):
        if not isinstance(expr, Piecewise):
            # Non-piecewise exprs need to be wrapped to print
            # Otherwise TC's TDB parser will complain
            expr = Piecewise((expr, And(v.T >= 1, v.T < 10000)))
        expr = TCPrinter().doprint(expr).upper()
        if ';' not in expr:
            expr += '; N'
        output += "FUNCTION {0} {1} !\n".format(name.upper(), expr)
    output += "\n"
    # Boilerplate code
    output += "TYPE_DEFINITION % SEQ * !\n"
    output += "DEFINE_SYSTEM_DEFAULT ELEMENT 2 !\n"
    default_elements = [
        i.upper() for i in sorted(dbf.elements)
        if i.upper() == 'VA' or i.upper() == '/-'
    ]
    if len(default_elements) > 0:
        output += 'DEFAULT_COMMAND DEFINE_SYSTEM_ELEMENT {} !\n'.format(
            ' '.join(default_elements))
    output += "\n"
    typedef_chars = list("^&*()'ABCDEFGHIJKLMNOPQSRTUVWXYZ")[::-1]
    #  Write necessary TYPE_DEF based on model hints
    typedefs = defaultdict(lambda: ["%"])
    for name, phase_obj in sorted(dbf.phases.items()):
        model_hints = phase_obj.model_hints.copy()
        possible_options = set(phase_options.keys()).intersection(model_hints)
        # Phase options are handled later
        for option in possible_options:
            del model_hints[option]
        if ('ordered_phase'
                in model_hints.keys()) and (model_hints['ordered_phase']
                                            == name):
            new_char = typedef_chars.pop()
            typedefs[name].append(new_char)
            typedefs[model_hints['disordered_phase']].append(new_char)
            output += 'TYPE_DEFINITION {} GES AMEND_PHASE_DESCRIPTION {} DISORDERED_PART {} !\n'\
                .format(new_char, model_hints['ordered_phase'].upper(),
                        model_hints['disordered_phase'].upper())
            del model_hints['ordered_phase']
            del model_hints['disordered_phase']
        if ('disordered_phase'
                in model_hints.keys()) and (model_hints['disordered_phase']
                                            == name):
            # We handle adding the correct typedef when we write the ordered phase
            del model_hints['ordered_phase']
            del model_hints['disordered_phase']
        if 'ihj_magnetic_afm_factor' in model_hints.keys():
            new_char = typedef_chars.pop()
            typedefs[name].append(new_char)
            output += 'TYPE_DEFINITION {} GES AMEND_PHASE_DESCRIPTION {} MAGNETIC {} {} !\n'\
                .format(new_char, name.upper(), model_hints['ihj_magnetic_afm_factor'],
                        model_hints['ihj_magnetic_structure_factor'])
            del model_hints['ihj_magnetic_afm_factor']
            del model_hints['ihj_magnetic_structure_factor']
        if len(model_hints) > 0:
            # Some model hints were not properly consumed
            raise ValueError(
                'Not all model hints are supported: {}'.format(model_hints))
    # Perform a second loop now that all typedefs / model hints are consistent
    for name, phase_obj in sorted(dbf.phases.items()):
        # model_hints may also contain "phase options", e.g., ionic liquid
        model_hints = phase_obj.model_hints.copy()
        name_with_options = str(name.upper())
        possible_options = set(phase_options.keys()).intersection(
            model_hints.keys())
        if len(possible_options) > 0:
            name_with_options += ':'
        for option in possible_options:
            name_with_options += phase_options[option]
        output += "PHASE {0} {1}  {2} {3} !\n".format(
            name_with_options, ''.join(typedefs[name]),
            len(phase_obj.sublattices),
            ' '.join([str(i) for i in phase_obj.sublattices]))
        constituents = ':'.join([
            ', '.join([spec.name for spec in sorted(subl)])
            for subl in phase_obj.constituents
        ])
        output += "CONSTITUENT {0} :{1}: !\n".format(name_with_options,
                                                     constituents)
        output += "\n"

    # PARAMETERs by subsystem
    param_sorted = defaultdict(lambda: list())
    paramtuple = namedtuple('ParamTuple', [
        'phase_name', 'parameter_type', 'complexity', 'constituent_array',
        'parameter_order', 'diffusing_species', 'parameter', 'reference'
    ])
    for param in dbf._parameters.all():
        if groupby == 'subsystem':
            components = set()
            for subl in param['constituent_array']:
                components |= set(subl)
            if param['diffusing_species'] != Species(None):
                components |= {param['diffusing_species']}
            # Wildcard operator is not a component
            components -= {'*'}
            desired_active_pure_elements = [
                list(x.constituents.keys()) for x in components
            ]
            components = set([
                el.upper() for constituents in desired_active_pure_elements
                for el in constituents
            ])
            # Remove vacancy if it's not the only component (pure vacancy endmember)
            if len(components) > 1:
                components -= {'VA'}
            components = tuple(sorted([c.upper() for c in components]))
            grouping = components
        elif groupby == 'phase':
            grouping = param['phase_name'].upper()
        else:
            raise ValueError(
                'Unknown groupby attribute \'{}\''.format(groupby))
        # We use the complexity parameter to help with sorting the parameters logically
        param_sorted[grouping].append(
            paramtuple(param['phase_name'], param['parameter_type'],
                       sum([len(i) for i in param['constituent_array']]),
                       param['constituent_array'], param['parameter_order'],
                       param['diffusing_species'], param['parameter'],
                       param['reference']))

    def write_parameter(param_to_write):
        constituents = ':'.join([
            ','.join(sorted([i.name.upper() for i in subl]))
            for subl in param_to_write.constituent_array
        ])
        # TODO: Handle references
        paramx = param_to_write.parameter
        if not isinstance(paramx, Piecewise):
            # Non-piecewise parameters need to be wrapped to print correctly
            # Otherwise TC's TDB parser will fail
            paramx = Piecewise((paramx, And(v.T >= 1, v.T < 10000)))
        exprx = TCPrinter().doprint(paramx).upper()
        if ';' not in exprx:
            exprx += '; N'
        if param_to_write.diffusing_species != Species(None):
            ds = "&" + param_to_write.diffusing_species.name
        else:
            ds = ""
        return "PARAMETER {}({}{},{};{}) {} !\n".format(
            param_to_write.parameter_type.upper(),
            param_to_write.phase_name.upper(), ds, constituents,
            param_to_write.parameter_order, exprx)

    if groupby == 'subsystem':
        for num_species in range(1, 5):
            subsystems = list(
                itertools.combinations(
                    sorted([i.name.upper() for i in dbf.species]),
                    num_species))
            for subsystem in subsystems:
                parameters = sorted(param_sorted[subsystem])
                if len(parameters) > 0:
                    output += "\n\n"
                    output += "$" * maxlen + "\n"
                    output += "$ {}".format('-'.join(sorted(subsystem)).center(
                        maxlen, " ")[2:-1]) + "$\n"
                    output += "$" * maxlen + "\n"
                    output += "\n"
                    for parameter in parameters:
                        output += write_parameter(parameter)
        # Don't generate combinatorics for multi-component subsystems or we'll run out of memory
        if len(dbf.species) > 4:
            subsystems = [k for k in param_sorted.keys() if len(k) > 4]
            for subsystem in subsystems:
                parameters = sorted(param_sorted[subsystem])
                for parameter in parameters:
                    output += write_parameter(parameter)
    elif groupby == 'phase':
        for phase_name in sorted(dbf.phases.keys()):
            parameters = sorted(param_sorted[phase_name])
            if len(parameters) > 0:
                output += "\n\n"
                output += "$" * maxlen + "\n"
                output += "$ {}".format(phase_name.upper().center(
                    maxlen, " ")[2:-1]) + "$\n"
                output += "$" * maxlen + "\n"
                output += "\n"
                for parameter in parameters:
                    output += write_parameter(parameter)
    else:
        raise ValueError('Unknown groupby attribute {}'.format(groupby))
    # Reflow text to respect character limit per line
    fd.write(reflow_text(output, linewidth=maxlen))
示例#9
0
def test_get_data_quantities_AL_NI_VA_interaction():
    """Test that an interaction with a VA produces the correct data quantities

    We just have a template database that has the phase defined. We then hot
    patch the Model object to have the GM from the fixed model we printed out
    and the data we printed out. The hot patch is needed because this is
    formation enthalpy data and the model needs to have the lower order terms
    in composition.

    One possible issue is that the new GM in the fixed model does not have any
    individual contributions, so it cannot be used to test excluded model
    contributions. The only excluded model contributions in this data are
    idmix, but the property we are testing is HM_FORM, so the feature transform
    of the idmix property should be zero.

    """
    # Hack the namespace to make the copy-pasted Gibbs energy function work
    from symengine import log, Piecewise, And
    T = v.T

    data = [{
        'components': ['AL', 'NI', 'VA'],
        'phases': ['BCC_B2'],
        'solver': {
            'mode':
            'manual',
            'sublattice_occupancies': [[1.0, [0.5, 0.5], 1.0],
                                       [1.0, [0.75, 0.25], 1.0]],
            'sublattice_site_ratios': [0.5, 0.5, 1.0],
            'sublattice_configurations':
            (('AL', ('NI', 'VA'), 'VA'), ('AL', ('NI', 'VA'), 'VA')),
            'comment':
            'BCC_B2 sublattice configuration (2SL)'
        },
        'conditions': {
            'P': 101325.0,
            'T': np.array([300.])
        },
        'reference_state': 'SGTE91',
        'output': 'HM_FORM',
        'values': np.array([[[-40316.61077, -56361.58554]]]),
        'reference': 'C. Jiang 2009 (constrained SQS)',
        'excluded_model_contributions': ['idmix']
    }, {
        'components': ['AL', 'NI', 'VA'],
        'phases': ['BCC_B2'],
        'solver': {
            'mode':
            'manual',
            'sublattice_occupancies': [[1.0, [0.5, 0.5], 1.0],
                                       [1.0, [0.75, 0.25], 1.0]],
            'sublattice_site_ratios': [0.5, 0.5, 1.0],
            'sublattice_configurations':
            (('AL', ('NI', 'VA'), 'VA'), ('AL', ('NI', 'VA'), 'VA')),
            'comment':
            'BCC_B2 sublattice configuration (2SL)'
        },
        'conditions': {
            'P': 101325.0,
            'T': np.array([300.])
        },
        'reference_state': 'SGTE91',
        'output': 'HM_FORM',
        'values': np.array([[[-41921.43363, -57769.49473]]]),
        'reference': 'C. Jiang 2009 (relaxed SQS)',
        'excluded_model_contributions': ['idmix']
    }]
    NEW_GM = 8.3145 * T * (
        0.5 * Piecewise((v.SiteFraction("BCC_B2", 0, "AL") *
                         log(v.SiteFraction("BCC_B2", 0, "AL")),
                         v.SiteFraction("BCC_B2", 0, "AL") > 1.0e-16),
                        (0, True)) /
        (0.5 * v.SiteFraction("BCC_B2", 0, "AL") + 0.5 * v.SiteFraction(
            "BCC_B2", 0, "NI") + 0.5 * v.SiteFraction("BCC_B2", 1, "AL") +
         0.5 * v.SiteFraction("BCC_B2", 1, "NI")) + 0.5 * Piecewise(
             (v.SiteFraction("BCC_B2", 0, "NI") *
              log(v.SiteFraction("BCC_B2", 0, "NI")),
              v.SiteFraction("BCC_B2", 0, "NI") > 1.0e-16), (0, True)) /
        (0.5 * v.SiteFraction("BCC_B2", 0, "AL") + 0.5 * v.SiteFraction(
            "BCC_B2", 0, "NI") + 0.5 * v.SiteFraction("BCC_B2", 1, "AL") +
         0.5 * v.SiteFraction("BCC_B2", 1, "NI")) + 0.5 * Piecewise(
             (v.SiteFraction("BCC_B2", 0, "VA") *
              log(v.SiteFraction("BCC_B2", 0, "VA")),
              v.SiteFraction("BCC_B2", 0, "VA") > 1.0e-16), (0, True)) /
        (0.5 * v.SiteFraction("BCC_B2", 0, "AL") + 0.5 * v.SiteFraction(
            "BCC_B2", 0, "NI") + 0.5 * v.SiteFraction("BCC_B2", 1, "AL") +
         0.5 * v.SiteFraction("BCC_B2", 1, "NI")) + 0.5 * Piecewise(
             (v.SiteFraction("BCC_B2", 1, "AL") *
              log(v.SiteFraction("BCC_B2", 1, "AL")),
              v.SiteFraction("BCC_B2", 1, "AL") > 1.0e-16), (0, True)) /
        (0.5 * v.SiteFraction("BCC_B2", 0, "AL") + 0.5 * v.SiteFraction(
            "BCC_B2", 0, "NI") + 0.5 * v.SiteFraction("BCC_B2", 1, "AL") +
         0.5 * v.SiteFraction("BCC_B2", 1, "NI")) + 0.5 * Piecewise(
             (v.SiteFraction("BCC_B2", 1, "NI") *
              log(v.SiteFraction("BCC_B2", 1, "NI")),
              v.SiteFraction("BCC_B2", 1, "NI") > 1.0e-16), (0, True)) /
        (0.5 * v.SiteFraction("BCC_B2", 0, "AL") + 0.5 * v.SiteFraction(
            "BCC_B2", 0, "NI") + 0.5 * v.SiteFraction("BCC_B2", 1, "AL") +
         0.5 * v.SiteFraction("BCC_B2", 1, "NI")) + 0.5 * Piecewise(
             (v.SiteFraction("BCC_B2", 1, "VA") *
              log(v.SiteFraction("BCC_B2", 1, "VA")),
              v.SiteFraction("BCC_B2", 1, "VA") > 1.0e-16), (0, True)) /
        (0.5 * v.SiteFraction("BCC_B2", 0, "AL") + 0.5 * v.SiteFraction(
            "BCC_B2", 0, "NI") + 0.5 * v.SiteFraction("BCC_B2", 1, "AL") +
         0.5 * v.SiteFraction("BCC_B2", 1, "NI")) + Piecewise(
             (v.SiteFraction("BCC_B2", 2, "VA") *
              log(v.SiteFraction("BCC_B2", 2, "VA")),
              v.SiteFraction("BCC_B2", 2, "VA") > 1.0e-16), (0, True)) /
        (0.5 * v.SiteFraction("BCC_B2", 0, "AL") + 0.5 * v.SiteFraction(
            "BCC_B2", 0, "NI") + 0.5 * v.SiteFraction("BCC_B2", 1, "AL") +
         0.5 * v.SiteFraction("BCC_B2", 1, "NI"))
    ) + (
        45262.9 * v.SiteFraction("BCC_B2", 0, "AL") *
        v.SiteFraction("BCC_B2", 0, "NI") * v.SiteFraction("BCC_B2", 1, "AL") *
        v.SiteFraction("BCC_B2", 2, "VA") +
        45262.9 * v.SiteFraction("BCC_B2", 0, "AL") *
        v.SiteFraction("BCC_B2", 1, "AL") * v.SiteFraction("BCC_B2", 1, "NI") *
        v.SiteFraction("BCC_B2", 2, "VA")) / (
            0.5 * v.SiteFraction("BCC_B2", 0, "AL") + 0.5 * v.SiteFraction(
                "BCC_B2", 0, "NI") + 0.5 * v.SiteFraction("BCC_B2", 1, "AL") +
            0.5 * v.SiteFraction("BCC_B2", 1, "NI")
        ) + (1.0 * v.SiteFraction("BCC_B2", 0, "AL") * v.SiteFraction(
            "BCC_B2", 1, "AL") * v.SiteFraction("BCC_B2", 2, "VA") * Piecewise(
                (10083 - 4.813 * T, And((T >= 298.15),
                                        (T < 2900.0))), (0, True)) +
             v.SiteFraction("BCC_B2", 0, "AL") * v.SiteFraction(
                 "BCC_B2", 1, "NI") * v.SiteFraction("BCC_B2", 2, "VA") *
             (9.52839e-8 * T**3 + 0.00123463 * T**2 +
              0.000871898 * T * log(T) + 1.31471 * T - 64435.3 + 23095.2 / T) +
             v.SiteFraction("BCC_B2", 0, "AL") * v.SiteFraction(
                 "BCC_B2", 1, "VA") * v.SiteFraction("BCC_B2", 2, "VA") *
             (10.0 * T + 16432.5) +
             v.SiteFraction("BCC_B2", 0, "NI") * v.SiteFraction(
                 "BCC_B2", 1, "AL") * v.SiteFraction("BCC_B2", 2, "VA") *
             (9.52839e-8 * T**3 + 0.00123463 * T**2 + 0.000871898 * T * log(T)
              + 1.31471 * T - 64435.3 + 23095.2 / T) + 1.0 * v.SiteFraction(
                  "BCC_B2", 0, "NI") * v.SiteFraction("BCC_B2", 1, "NI") *
             v.SiteFraction("BCC_B2", 2, "VA") * Piecewise(
                 (8715.084 - 3.556 * T, And((T >= 298.15),
                                            (T < 3000.0))), (0, True)) +
             32790.6 * v.SiteFraction("BCC_B2", 0, "NI") * v.SiteFraction(
                 "BCC_B2", 1, "VA") * v.SiteFraction("BCC_B2", 2, "VA") +
             v.SiteFraction("BCC_B2", 0, "VA") * v.SiteFraction(
                 "BCC_B2", 1, "AL") * v.SiteFraction("BCC_B2", 2, "VA") *
             (10.0 * T + 16432.5) +
             32790.6 * v.SiteFraction("BCC_B2", 0, "VA") * v.SiteFraction(
                 "BCC_B2", 1, "NI") * v.SiteFraction("BCC_B2", 2, "VA")) / (
                     0.5 * v.SiteFraction("BCC_B2", 0, "AL") +
                     0.5 * v.SiteFraction("BCC_B2", 0, "NI") +
                     0.5 * v.SiteFraction("BCC_B2", 1, "AL") +
                     0.5 * v.SiteFraction("BCC_B2", 1, "NI"))

    dbf = Database(
        """$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
    $ Date: 2019-12-08 18:05
    $ Components: AL, NI, VA
    $ Phases: BCC_B2
    $ Generated by brandon (pycalphad 0.8.1.post1)
    $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

    ELEMENT AL FCC_A1 26.982 4577.3 28.322 !
    ELEMENT NI FCC_A1 58.69 4787.0 29.796 !
    ELEMENT VA VACUUM 0.0 0.0 0.0 !

    TYPE_DEFINITION % SEQ * !
    DEFINE_SYSTEM_DEFAULT ELEMENT 2 !
    DEFAULT_COMMAND DEFINE_SYSTEM_ELEMENT VA !

    PHASE BCC_B2 %  3 0.5 0.5 1 !
    CONSTITUENT BCC_B2 :AL,NI,VA:AL,NI,VA:VA: !

    """)
    mod = Model(dbf, ['AL', 'NI', 'VA'], 'BCC_B2')
    dd = {ky: 0.0 for ky in mod.models.keys()}
    dd['GM'] = NEW_GM
    mod.models = dd
    print(mod.HM)
    config_tup = (('AL', ), ('NI', 'VA'), ('VA', ))
    calculate_dict = get_prop_samples(data, config_tup)
    sample_condition_dicts = _get_sample_condition_dicts(
        calculate_dict, list(map(len, config_tup)))
    qty = get_data_quantities('HM_FORM', mod, [0], data,
                              sample_condition_dicts)
    print(qty)
    assert np.all(
        np.isclose(
            [-6254.7802775, -5126.1206475, -7458.3974225, -6358.04118875],
            qty))