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 ]])
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")
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])
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)
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
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))
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)
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))
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))