Source code for openbandparams.iii_v.ternary

#
#   Copyright (c) 2013-2014, Scott J Maddox
#
#   This file is part of openbandparams.
#
#   openbandparams is free software: you can redistribute it and/or modify
#   it under the terms of the GNU Affero General Public License as published
#   by the Free Software Foundation, either version 3 of the License, or
#   (at your option) any later version.
#
#   openbandparams is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU Affero General Public License for more details.
#
#   You should have received a copy of the GNU Affero General Public License
#   along with openbandparams.  If not, see <http://www.gnu.org/licenses/>.
#
#############################################################################

from openbandparams.iii_v.base_material import BaseType, Base
from openbandparams.algorithms import bisect
from openbandparams.utils import classinstancemethod


[docs]class TernaryType(BaseType): def __getattr__(self, name): # acts like a class method for the Ternary class if (hasattr(self.binaries[0], name) and hasattr(self.binaries[1], name)): def _param_accessor(**kwargs): return self._interpolate(name, **kwargs) return _param_accessor else: raise AttributeError(name)
[docs]class Ternary(Base): __metaclass__ = TernaryType def __init__(self, **kwargs): Base.__init__(self) self._x = self._get_x(kwargs) def __getattr__(self, name): if (hasattr(self.binaries[0], name) and hasattr(self.binaries[1], name)): def _param_accessor(**kwargs): return self._interpolate(name, **kwargs) return _param_accessor else: raise AttributeError(name) def __str__(self): return self.name def __eq__(self, other): return (type(self) == type(other) and self._x == other._x) @classmethod def _get_bowing(cls, param, x): if hasattr(cls, '_bowing_%s' % param): # a bowing parameter exists - use it C = getattr(cls, '_bowing_%s' % param) if callable(C): # assume the bowing paramter is composition dependent # if it's callable return C(x) else: return C else: return None @classinstancemethod def _interpolate(self, cls, param, **kwargs): if self is not None: x = self._x else: x = cls._get_x(kwargs) vals = [] for b in [cls.binaries[0], cls.binaries[1]]: try: vals.append(getattr(b, param)) except AttributeError as e: e.message += '. Binary `%s`' % b.name e.message += ' missing param `%s`' % param raise e if param[0] == '_': # assume it's a hard coded parameter if it starts with '_' A = vals[0] B = vals[1] else: # otherwise it's an accessor function A = vals[0](**kwargs) B = vals[1](**kwargs) C = cls._get_bowing(param, x) if C is not None: # a bowing parameter exists - use it return A * x + B * (1 - x) - C * x * (1 - x) else: # otherwise, use linear interpolation return A * x + B * (1 - x)
[docs]class Ternary1(Ternary): ''' For alloys of the A_{x}B_{1-x}C type, where A and B are Group III elements, and C is a Group V element. ''' def __repr__(self): return '{}({}={})'.format(self.name, self.elements[0], self._x) @classinstancemethod
[docs] def LaTeX(self, cls): if self is not None: return "{A}_{{{:g}}}{B}_{{{:g}}}{C}".format(self._x, 1 - self._x, A=self.elements[0], B=self.elements[1], C=self.elements[2]) else: return "{A}_{{x}}{B}_{{1-x}}{C}".format(A=cls.elements[0], B=cls.elements[1], C=cls.elements[2])
@classmethod def _get_x(cls, kwargs): if 'x' in kwargs: return float(kwargs['x']) elif cls.elements[0] in kwargs: return float(kwargs[cls.elements[0]]) elif cls.elements[1] in kwargs: return 1 - float(kwargs[cls.elements[1]]) elif 'a' in kwargs: # lattice match to the given lattice constant a = kwargs['a'] T = kwargs.get('T', 300) # make sure the lattice constant is available b1a = cls.binaries[0].a(T=T) b2a = cls.binaries[1].a(T=T) amin = min(b1a, b2a) amax = max(b1a, b2a) if a < amin or a > amax: raise ValueError('a out of range [%.3f, %.3f]' % (amin, amax)) # find the correct composition, x x = bisect(func=lambda x: cls.a(x=x, T=T) - a, a=0, b=1) return x else: raise TypeError("Missing required key word argument." "'x', '%s', or '%s' is needed." % (cls.elements[0], cls.elements[1]))
[docs] def elementFraction(self, element): if element == self.elements[0]: return self._x elif element == self.elements[1]: return (1 - self._x) elif element == self.elements[2]: return 1 else: return 0
[docs]class Ternary2(Ternary): ''' For alloys of the AB_{x}C_{1-x} type, where A is a Group III element, and B and C are Group V elements. ''' def __repr__(self): return '{}({}={})'.format(self.name, self.elements[1], self._x) @classinstancemethod
[docs] def LaTeX(self, cls): if self is not None: return "{A}{B}_{{{:g}}}{C}_{{{:g}}}".format(self._x, 1 - self._x, A=self.elements[0], B=self.elements[1], C=self.elements[2]) else: return "{A}{B}_{{x}}{C}_{{1-x}}".format(A=cls.elements[0], B=cls.elements[1], C=cls.elements[2])
@classmethod def _get_x(cls, kwargs): if 'x' in kwargs: return float(kwargs['x']) elif cls.elements[1] in kwargs: return float(kwargs[cls.elements[1]]) elif cls.elements[2] in kwargs: return 1 - float(kwargs[cls.elements[2]]) elif 'a' in kwargs: # lattice match to the given lattice constant if 'T' not in kwargs: raise TypeError('Lattice matching temperature, T, missing.') a = kwargs['a'] T = kwargs['T'] # make sure the lattice constant is available b1a = cls.binaries[0].a(T=T) b2a = cls.binaries[1].a(T=T) amin = min(b1a, b2a) amax = max(b1a, b2a) if a < amin or a > amax: raise ValueError('a out of range [%.3f, %.3f]' % (amin, amax)) # find the correct composition, x x = bisect(func=lambda x: cls.a(x=x, T=T) - a, a=0, b=1) return x else: raise TypeError("Missing required key word argument." "'x', '%s', or '%s' is needed." % (cls.elements[1], cls.elements[2]))
[docs] def elementFraction(self, element): if element == self.elements[0]: return 1 elif element == self.elements[1]: return self._x elif element == self.elements[2]: return (1 - self._x) else: return 0 # class ReversedTernary(Ternary): # @classmethod # def _get_bowing(cls, param, x): # if hasattr(cls._ternary, '_bowing_%s'%param): # # a bowing parameter exists - use it # C = getattr(cls._ternary, '_bowing_%s'%param) # if callable(C): # # assume the bowing paramter is composition dependent # # if it's callable # return C(1-x) # reverse the composition # else: # return C # else: # return None # def create_reversed_ternary(name, ternary): # new_type = type(name, (ReversedTernary,), {}) # new_type.name = name # new_type.element1 = ternary.element2 # new_type.binary1 = ternary.binary2 # new_type.element2 = ternary.element1 # new_type.binary2 = ternary.binary1 # new_type._ternary = ternary # return new_type