Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
all_comps = set()
for sublattice in constituent_array:
if len(sublattice) != 1:
return False
all_comps.add(sublattice[0].name)
pure_els = all_comps.intersection(model_pure_elements)
return len(pure_els) == 1
# Remove interactions from a copy of the Database, avoids any element/VA interactions.
endmember_only_dbe = copy.deepcopy(dbe)
endmember_only_dbe._parameters.remove(~where('constituent_array').test(_pure_element_test))
reference_dict = {out: [] for out in output} # output: terms list
for ref_state in reference_states:
if ref_state.species not in self.components:
continue
mod_pure = self.__class__(endmember_only_dbe, [ref_state.species, v.Species('VA')], ref_state.phase_name, parameters=self._parameters_arg)
# apply the modifications to the Models
for contrib, new_val in contrib_mods.items():
mod_pure.models[contrib] = new_val
# set all the free site fractions to one, this should effectively delete any mixing terms spuriously added, e.g. idmix
site_frac_subs = {sf: 1 for sf in mod_pure.ast.free_symbols if isinstance(sf, v.SiteFraction)}
for mod_key, mod_val in mod_pure.models.items():
mod_pure.models[mod_key] = mod_val.subs(site_frac_subs)
moles = self.moles(ref_state.species)
# get the output property of interest, substitute the fixed state variables (e.g. T=298.15) and add the pure element moles weighted term to the list of terms
# substitution of fixed state variables has to happen after getting the attribute in case there are any derivatives involving that state variable
for out in reference_dict.keys():
mod_out = getattr(mod_pure, out).subs(ref_state.fixed_statevars)
reference_dict[out].append(mod_out*moles)
# set the attribute on the class
for out, terms in reference_dict.items():
def _interaction_test(self, constituent_array):
"""
Check if constituent array has more than one active species in
its array for at least one sublattice.
"""
result = False
if len(constituent_array) != len(self.constituents):
return False
for sublattice in constituent_array:
# check if all elements involved are also active
valid = set(sublattice).issubset(self.components) \
or sublattice[0] == v.Species('*')
if len(sublattice) > 1 and valid:
result = True
if not valid:
result = False
break
return result
def __new__(cls, species, **assumptions):
species = Species(species)
varname = 'MU_' + species.escaped_name.upper()
new_self = StateVariable.__new__(cls, varname, **assumptions)
new_self.species = species
return new_self
self.components |= subl_comps
# Support for variable site ratios in ionic liquid model
if phase.model_hints.get('ionic_liquid_2SL', False):
if idx == 0:
subl_idx = 1
elif idx == 1:
subl_idx = 0
else:
raise ValueError('Two-sublattice ionic liquid specified with more than two sublattices')
self.site_ratios[subl_idx] = Add(*[v.SiteFraction(self.phase_name, idx, spec) * abs(spec.charge) for spec in subl_comps])
if phase.model_hints.get('ionic_liquid_2SL', False):
# Special treatment of "neutral" vacancies in 2SL ionic liquid
# These are treated as having variable valence
for idx, sublattice in enumerate(phase.constituents):
subl_comps = set(sublattice).intersection(active_species)
if v.Species('VA') in subl_comps:
if idx == 0:
subl_idx = 1
elif idx == 1:
subl_idx = 0
else:
raise ValueError('Two-sublattice ionic liquid specified with more than two sublattices')
self.site_ratios[subl_idx] += self.site_ratios[idx] * v.SiteFraction(self.phase_name, idx, v.Species('VA'))
self.site_ratios = tuple(self.site_ratios)
# Verify that this phase is still possible to build
is_pure_VA = set()
for sublattice in phase.constituents:
sublattice_comps = set(sublattice).intersection(self.components)
if len(sublattice_comps) == 0:
# None of the components in a sublattice are active
# We cannot build a model of this phase
dbf : Database
Thermodynamic database containing elements and species.
comps : list
Names of components to consider in the calculation.
Returns
-------
set
Set of Species objects
"""
# Constrain possible components to those within phase's d.o.f
# Assume for the moment that comps contains a list of pure element strings
# We want to add all the species which can be created by a combination of
# the user-specified pure elements
species_dict = {s.name: s for s in dbf.species}
possible_comps = {v.Species(species_dict.get(x, x)) for x in comps}
desired_active_pure_elements = [list(x.constituents.keys()) for x in possible_comps]
# Flatten nested list
desired_active_pure_elements = [el.upper() for constituents in desired_active_pure_elements for el in constituents]
eligible_species_from_database = {x for x in dbf.species if
set(x.constituents.keys()).issubset(desired_active_pure_elements)}
return eligible_species_from_database
params.extend((order_one, order_two))
# Include variable indicated by parameter order index
# Perform Muggianu adjustment to site fractions
mixing_term *= comp_symbols[param['parameter_order']].subs(
self._Muggianu_correction_dict(comp_symbols),
simultaneous=True)
if phase.model_hints.get('ionic_liquid_2SL', False):
# Special normalization rules for parameters apply under this model
# Reference: Bo Sundman, "Modification of the two-sublattice model for liquids",
# Calphad, Volume 15, Issue 2, 1991, Pages 109-119, ISSN 0364-5916
if not any([m.species.charge < 0 for m in mixing_term.free_symbols]):
pair_rule = {}
# Cation site fractions must always appear with vacancy site fractions
va_subls = [(v.Species('VA') in phase.constituents[idx]) for idx in range(len(phase.constituents))]
va_subl_idx = (len(phase.constituents) - 1) - va_subls[::-1].index(True)
va_present = any((v.Species('VA') in c) for c in param['constituent_array'])
if va_present and (max(len(c) for c in param['constituent_array']) == 1):
# No need to apply pair rule for VA-containing endmember
pass
elif va_subl_idx > -1:
for sym in mixing_term.free_symbols:
if sym.species.charge > 0:
pair_rule[sym] = sym * v.SiteFraction(sym.phase_name, va_subl_idx, v.Species('VA'))
mixing_term = mixing_term.xreplace(pair_rule)
# This parameter is normalized differently due to the variable charge valence of vacancies
mixing_term *= self.site_ratios[va_subl_idx]
rk_terms.append(mixing_term * param['parameter'])
return Add(*rk_terms)
def moles(self, species):
"Number of moles of species or elements."
species = v.Species(species)
is_pure_element = (len(species.constituents.keys()) == 1 and
list(species.constituents.keys())[0] == species.name)
result = S.Zero
normalization = S.Zero
if is_pure_element:
element = list(species.constituents.keys())[0]
for idx, sublattice in enumerate(self.constituents):
active = set(sublattice).intersection(self.components)
result += self.site_ratios[idx] * \
sum(int(spec.number_of_atoms > 0) * spec.constituents.get(element, 0) * v.SiteFraction(self.phase_name, idx, spec)
for spec in active)
normalization += self.site_ratios[idx] * \
sum(spec.number_of_atoms * v.SiteFraction(self.phase_name, idx, spec)
for spec in active)
else:
for idx, sublattice in enumerate(self.constituents):
def _process_species(db, sp_name, sp_comp, charge=0, *args):
"""Add a species to the Database. If charge not specified, the Species will be neutral."""
# process the species composition list of [element1, ratio1, element2, ratio2, ..., elementN, ratioN]
constituents = {sp_comp[i]: sp_comp[i+1] for i in range(0, len(sp_comp), 2)}
db.species.add(Species(sp_name, constituents, charge=charge))
def __init__(self, species, reference_phase, fixed_statevars=None):
"""
Parameters
----------
species : str or Species
Species to define the reference state for. Only pure elements supported.
reference_phase : str
Name of phase
fixed_statevars : None, optional
Dictionary of {StateVariable: value} that will be fixed, e.g. {v.T: 298.15, v.P: 101325}
If None (the default), an empty dict will be created.
"""
if isinstance(species, v.Species):
self.species = species
else:
self.species = v.Species(species)
self.phase_name = reference_phase
self.fixed_statevars = fixed_statevars if fixed_statevars is not None else {}
const_arr = tuple([tuple(map(lambda x: v.Species(x), subl)) for subl in map(tuplify, configuration)])
# parameter order doesn't matter here, since the generated might not exactly match. Always override.