def atomic_ordering_energy(self, dbe): """ Return the atomic ordering contribution in symbolic form. Description follows Servant and Ansara, Calphad, 2001. """ phase = dbe.phases[self.phase_name] ordered_phase_name = phase.model_hints.get("ordered_phase", None) disordered_phase_name = phase.model_hints.get("disordered_phase", None) if phase.name != ordered_phase_name: return S.Zero disordered_model = self.__class__(dbe, self.components, disordered_phase_name) constituents = [ sorted(set(c).intersection(self.components)) for c in dbe.phases[ordered_phase_name].constituents ] # Fix variable names variable_rename_dict = {} for atom in disordered_model.energy.atoms(v.SiteFraction): # Replace disordered phase site fractions with mole fractions of # ordered phase site fractions. # Special case: Pure vacancy sublattices all_species_in_sublattice = dbe.phases[disordered_phase_name].constituents[atom.sublattice_index] if atom.species == "VA" and len(all_species_in_sublattice) == 1: # Assume: Pure vacancy sublattices are always last vacancy_subl_index = len(dbe.phases[ordered_phase_name].constituents) - 1 variable_rename_dict[atom] = v.SiteFraction(ordered_phase_name, vacancy_subl_index, atom.species) else: # All other cases: replace site fraction with mole fraction variable_rename_dict[atom] = self.mole_fraction( atom.species, ordered_phase_name, constituents, dbe.phases[ordered_phase_name].sublattices ) # Save all of the ordered energy contributions # This step is why this routine must be called _last_ in build_phase ordered_energy = Add(*list(self.models.values())) self.models.clear() # Copy the disordered energy contributions into the correct bins for name, value in disordered_model.models.items(): self.models[name] = value.xreplace(variable_rename_dict) # All magnetic parameters will be defined in the disordered model self.TC = self.curie_temperature = disordered_model.TC self.TC = self.curie_temperature = self.TC.xreplace(variable_rename_dict) molefraction_dict = {} # Construct a dictionary that replaces every site fraction with its # corresponding mole fraction in the disordered state for sitefrac in ordered_energy.atoms(v.SiteFraction): all_species_in_sublattice = dbe.phases[ordered_phase_name].constituents[sitefrac.sublattice_index] if sitefrac.species == "VA" and len(all_species_in_sublattice) == 1: # pure-vacancy sublattices should not be replaced # this handles cases like AL,NI,VA:AL,NI,VA:VA and # ensures the VA's don't get mixed up continue molefraction_dict[sitefrac] = self.mole_fraction( sitefrac.species, ordered_phase_name, constituents, dbe.phases[ordered_phase_name].sublattices ) return ordered_energy - ordered_energy.subs(molefraction_dict, simultaneous=True)
def _inverse_mellin_transform(F, s, x_, strip, as_meijerg=False): """ A helper for the real inverse_mellin_transform function, this one here assumes x to be real and positive. """ from sympy import (expand, expand_mul, hyperexpand, meijerg, And, Or, arg, pi, re, factor, Heaviside, gamma, Add) x = _dummy('t', 'inverse-mellin-transform', F, positive=True) # Actually, we won't try integration at all. Instead we use the definition # of the Meijer G function as a fairly general inverse mellin transform. F = F.rewrite(gamma) for g in [factor(F), expand_mul(F), expand(F)]: if g.is_Add: # do all terms separately ress = [_inverse_mellin_transform(G, s, x, strip, as_meijerg, noconds=False) \ for G in g.args] conds = [p[1] for p in ress] ress = [p[0] for p in ress] res = Add(*ress) if not as_meijerg: res = factor(res, gens=res.atoms(Heaviside)) return res.subs(x, x_), And(*conds) try: a, b, C, e, fac = _rewrite_gamma(g, s, strip[0], strip[1]) except IntegralTransformError: continue G = meijerg(a, b, C / x**e) if as_meijerg: h = G else: h = hyperexpand(G) if h.is_Piecewise and len(h.args) == 3: # XXX we break modularity here! h = Heaviside(x - abs(C))*h.args[0].args[0] \ + Heaviside(abs(C) - x)*h.args[1].args[0] # We must ensure that the intgral along the line we want converges, # and return that value. # See [L], 5.2 cond = [abs(arg(G.argument)) < G.delta * pi] # Note: we allow ">=" here, this corresponds to convergence if we let # limits go to oo symetrically. ">" corresponds to absolute convergence. cond += [ And(Or(len(G.ap) != len(G.bq), 0 >= re(G.nu) + 1), abs(arg(G.argument)) == G.delta * pi) ] cond = Or(*cond) if cond is False: raise IntegralTransformError('Inverse Mellin', F, 'does not converge') return (h * fac).subs(x, x_), cond raise IntegralTransformError('Inverse Mellin', F, '')
def _inverse_mellin_transform(F, s, x_, strip, as_meijerg=False): """ A helper for the real inverse_mellin_transform function, this one here assumes x to be real and positive. """ from sympy import (expand, expand_mul, hyperexpand, meijerg, And, Or, arg, pi, re, factor, Heaviside, gamma, Add) x = _dummy('t', 'inverse-mellin-transform', F, positive=True) # Actually, we won't try integration at all. Instead we use the definition # of the Meijer G function as a fairly general inverse mellin transform. F = F.rewrite(gamma) for g in [factor(F), expand_mul(F), expand(F)]: if g.is_Add: # do all terms separately ress = [_inverse_mellin_transform(G, s, x, strip, as_meijerg, noconds=False) \ for G in g.args] conds = [p[1] for p in ress] ress = [p[0] for p in ress] res = Add(*ress) if not as_meijerg: res = factor(res, gens=res.atoms(Heaviside)) return res.subs(x, x_), And(*conds) try: a, b, C, e, fac = _rewrite_gamma(g, s, strip[0], strip[1]) except IntegralTransformError: continue G = meijerg(a, b, C/x**e) if as_meijerg: h = G else: h = hyperexpand(G) if h.is_Piecewise and len(h.args) == 3: # XXX we break modularity here! h = Heaviside(x - abs(C))*h.args[0].args[0] \ + Heaviside(abs(C) - x)*h.args[1].args[0] # We must ensure that the intgral along the line we want converges, # and return that value. # See [L], 5.2 cond = [abs(arg(G.argument)) < G.delta*pi] # Note: we allow ">=" here, this corresponds to convergence if we let # limits go to oo symetrically. ">" corresponds to absolute convergence. cond += [And(Or(len(G.ap) != len(G.bq), 0 >= re(G.nu) + 1), abs(arg(G.argument)) == G.delta*pi)] cond = Or(*cond) if cond is False: raise IntegralTransformError('Inverse Mellin', F, 'does not converge') return (h*fac).subs(x, x_), cond raise IntegralTransformError('Inverse Mellin', F, '')
def atomic_ordering_energy(self, dbe): """ Return the atomic ordering contribution in symbolic form. Description follows Servant and Ansara, Calphad, 2001. """ phase = dbe.phases[self.phase_name] ordered_phase_name = phase.model_hints.get('ordered_phase', None) disordered_phase_name = phase.model_hints.get('disordered_phase', None) if phase.name != ordered_phase_name: return S.Zero disordered_model = self.__class__(dbe, sorted(self.components), disordered_phase_name) constituents = [sorted(set(c).intersection(self.components)) \ for c in dbe.phases[ordered_phase_name].constituents] # Fix variable names variable_rename_dict = {} for atom in disordered_model.energy.atoms(v.SiteFraction): # Replace disordered phase site fractions with mole fractions of # ordered phase site fractions. # Special case: Pure vacancy sublattices all_species_in_sublattice = \ dbe.phases[disordered_phase_name].constituents[ atom.sublattice_index] if atom.species == 'VA' and len(all_species_in_sublattice) == 1: # Assume: Pure vacancy sublattices are always last vacancy_subl_index = \ len(dbe.phases[ordered_phase_name].constituents)-1 variable_rename_dict[atom] = \ v.SiteFraction( ordered_phase_name, vacancy_subl_index, atom.species) else: # All other cases: replace site fraction with mole fraction variable_rename_dict[atom] = \ self.mole_fraction( atom.species, ordered_phase_name, constituents, dbe.phases[ordered_phase_name].sublattices ) # Save all of the ordered energy contributions # This step is why this routine must be called _last_ in build_phase ordered_energy = Add(*list(self.models.values())) self.models.clear() # Copy the disordered energy contributions into the correct bins for name, value in disordered_model.models.items(): self.models[name] = value.xreplace(variable_rename_dict) # All magnetic parameters will be defined in the disordered model self.TC = self.curie_temperature = disordered_model.TC self.TC = self.curie_temperature = self.TC.xreplace(variable_rename_dict) molefraction_dict = {} # Construct a dictionary that replaces every site fraction with its # corresponding mole fraction in the disordered state for sitefrac in ordered_energy.atoms(v.SiteFraction): all_species_in_sublattice = \ dbe.phases[ordered_phase_name].constituents[ sitefrac.sublattice_index] if sitefrac.species == 'VA' and len(all_species_in_sublattice) == 1: # pure-vacancy sublattices should not be replaced # this handles cases like AL,NI,VA:AL,NI,VA:VA and # ensures the VA's don't get mixed up continue molefraction_dict[sitefrac] = \ self.mole_fraction(sitefrac.species, ordered_phase_name, constituents, dbe.phases[ordered_phase_name].sublattices) return ordered_energy - ordered_energy.subs(molefraction_dict, simultaneous=True)