Source code for atsim.potentials._modifiers

import functools
import logging

from .config._common import ConfigurationException, MultiRangeDefinitionTuple

from atsim.potentials import plus

from .spline import Custom_SplinePotential, Spline_Point, Exp_Spline, Buck4_Spline


[docs]def modifier(func): func.is_modifier = True return func
[docs]def is_modifier(obj): return hasattr(obj, "is_modifier") and obj.is_modifier
# As we're about to shadow it - save the builtin sum to _sum so we can still access it. _sum = sum def _modifier_from_func_reduce(logger_name, func, potential_forms, potential_form_builder): logger = logging.getLogger(__name__).getChild(logger_name) logger.debug("Creating '{}' modifier for:".format(logger_name)) pot_callables = [] for i,pfi in enumerate(potential_forms): logger.debug(" {}: {}".format(i+1, pfi)) pot_callable = potential_form_builder.create_potential_function(pfi) pot_callables.append(pot_callable) mod = functools.reduce(func, pot_callables) return mod @modifier
[docs]def sum(potential_forms, potential_form_builder): """Modifier that sums all the potential instances given as arguments. :param potential_forms: List of tuples that can be passed to `atsim.potentials.config._potential_form_builder.Potential_Form_Builder.create_potential_function()` to create potential callables. :param potential_form_builder: `atsim.potentials.config._potential_form_builder.Potential_Form_Builder` used to create potential instances. :returns: Potential callable sums that values from a number of potential instances.""" mod = _modifier_from_func_reduce("sum", plus, potential_forms, potential_form_builder) return mod
@modifier
[docs]def product(potential_forms, potential_form_builder): """Modifier that takes the product of the potential instances given as arguments. :param potential_forms: List of tuples that can be passed to `atsim.potentials.config._potential_form_builder.Potential_Form_Builder.create_potential_function()` to create potential callables. :param potential_form_builder: `atsim.potentials.config._potential_form_builder.Potential_Form_Builder` used to create potential instances. :returns: Potential callable that takes the product of a number of potential instances.""" from atsim.potentials import product mod = _modifier_from_func_reduce("product", product, potential_forms, potential_form_builder) return mod
@modifier
[docs]def pow(potential_forms, potential_form_builder): """Modifier that takes two arguments and raises the first potential-form to the power of the second. e.g. pow(as.buck 1000.0 0.2 32.0, as.constant 2.0) Would square the given Buckingham potential. :param potential_forms: List of tuples that can be passed to `atsim.potentials.config._potential_form_builder.Potential_Form_Builder.create_potential_function()` to create potential callables. :param potential_form_builder: `atsim.potentials.config._potential_form_builder.Potential_Form_Builder` used to create potential instances. :returns: Potential callable that takes the product of a number of potential instances.""" from atsim.potentials import pow mod = _modifier_from_func_reduce("pow", pow, potential_forms, potential_form_builder) return mod
class _Exp_Spline_Factory(object): """Helper class used by spline_potential() modifier to instantiate :class:`Exp_Spline` objects when `exp_spline` spline type is specified in config file.""" spline_keyword = "exp_spline" def build_spline(self, detach_point, attach_point, spline_defn): if spline_defn.parameters: raise ConfigurationException("spline modifier 'exp_spline' middle potential form does not take any parameters. The following parameters were specified: {}".format(pot2.parameters)) spline = Exp_Spline(detach_point, attach_point) return spline class _Buck4_Spline_Factory(object): """Helper class used by spline_potential() modifier to instantiate :class:`Buck4_Spline` objects when `buck4_spline` spline type is specified in config file.""" spline_keyword = "buck4_spline" def build_spline(self, detach_point, attach_point, spline_defn): if not len(spline_defn.parameters) == 1: raise ConfigurationException("spline modifier with 'buck4_spline' requires a single parameter to define r_min. The following parameters were specified: {}".format(pot2.parameters)) r_min = spline_defn.parameters[0] if not r_min < attach_point.r and not r_min > detach_point.r: raise ConfigurationException("spline modifier with 'buck4_spline' r_min parameter does not lie between detach and attach values ({} < r_min < {}). r_min = {}".format( detach_point.r, attach_point.r, r_min)) spline = Buck4_Spline(detach_point, attach_point, r_min) return spline @modifier
[docs]def spline(potential_forms, potential_form_builder): """Modifier that smoothly splines between two potential forms by linking them with an intermediate spline. The `potential_forms` list must contain a single `PotentialFormInstanceTuple` entre. The tuple must only define three sections - potential form A -> spline -> potential form B. The extent of the spline is defined by where the middle region starts and potential form B starts. As a configuration string this might be defined as: `>0 as.zbl 14 8 >=0.8 exp_spline >=1.4 as.buck 180003 0.3 32.0` Which would create a `zbl` and Buckingham potential connected by a spline when `r` is between 0.8 and 1.4. At present the potential form label for the spline must be `exp_spline` or `buck4_spline`. :param potential_forms: List of tuples that can be passed to `atsim.potentials.config._potential_form_builder.Potential_Form_Builder.create_potential_function()` to create potential callables. :param potential_form_builder: `atsim.potentials.config._potential_form_builder.Potential_Form_Builder` used to create potential instances. :returns: Potential callable representing splined potentials.""" spline_factories = [ _Exp_Spline_Factory(), _Buck4_Spline_Factory() ] if len(potential_forms) != 1: raise ConfigurationException("spline modifier only accepts a single multi range potential definition as its argument") logger = logging.getLogger(__name__).getChild("spline") # Extract the ranges and potential forms. pform = potential_forms[0] if pform.next is None: raise ConfigurationException("spline modifier requires three sub-potentials to be defined only one specified.") # ... set the 'next' attribute to None in pot1 and pot2 pot1 = pform._replace(next = None) pot2 = pform.next._replace(next = None) allowed_spline_types = [s.spline_keyword for s in spline_factories] if not pot2.potential_form in allowed_spline_types: allowed_spline_types_str = ["'{}'".format(t) for t in allowed_spline_types] allowed_spline_types_str = ",".join(allowed_spline_types_str) raise ConfigurationException("spline modifier only accepts spline types {} for middle potential form. '{}' was found instead".format( allowed_spline_types_str, pot2.potential_form)) if pform.next.next is None: raise ConfigurationException("spline modifier requires three sub-potentials to be defined only two specified.") if not pform.next.next.next is None: raise ConfigurationException("spline modifier requires three sub-potentials to be defined more than three have been given.") # ... we need to be able to evaluate the potential at the start point # this allows values to be calculated at any point (as long as the potential's well behaved there) pot3_old_start = pform.next.next.start.start pot3 = pform.next.next._replace( start = MultiRangeDefinitionTuple(u">", float("-inf")), next = None) # Determine detachment point if not pot1.start.start < pot2.start.start: raise ConfigurationException("spline modifier range error. Start of 1st potential should be less than start of 2nd ! {} < {}".format( pot1.start.start, pot2.start.start)) if not pot2.start.start < pot3_old_start: raise ConfigurationException("spline modifier range error. Start of 2nd potential (exp_spline) should be less than start of 3rd ! {} < {}".format( pot2.start.start, pot3_old_start)) detach_point_r = pform.next.start.start attach_point_r = pform.next.next.start.start pot1_func = potential_form_builder.create_potential_function(pot1) pot3_func = potential_form_builder.create_potential_function(pot3) detach_point = Spline_Point(pot1_func, detach_point_r) attach_point = Spline_Point(pot3_func, attach_point_r) spline_factory = [s for s in spline_factories if s.spline_keyword == pot2.potential_form ][0] logger.debug("spline modifier: connecting '{}' with {} to '{}' in range {} to {}".format( pot1.potential_form, pot2.potential_form, pot2.potential_form, detach_point, attach_point)) # Now build the spline object try: spline = spline_factory.build_spline(detach_point, attach_point, pot2) except ImportError as ie: raise_e = ConfigurationException("When using the '{}' spline type an additional package is required. Please install. {}".format( pot2.potential_form, str(ie.args[0]) )) raise raise_e spot_obj = Custom_SplinePotential(spline) return spot_obj
@modifier
[docs]def trans(potential_forms, potential_form_builder): """Modifier that takes two arguments the first is a potential form and the second must be an instance of as.constant. This modifier performs the operation: potential_form(r+X) where X is the value parameter for the as.constant argument. e.g. trans(as.buck 1000.0 0.2 32.0, as.constant 2.0) :param potential_forms: List of tuples that can be passed to `atsim.potentials.config._potential_form_builder.Potential_Form_Builder.create_potential_function()` to create potential callables. :param potential_form_builder: `atsim.potentials.config._potential_form_builder.Potential_Form_Builder` used to create potential instances. :returns: Potential callable transforms the input to the potential-form.""" if not len(potential_forms) == 2: raise ConfigurationException("trans() potential modifier only accepts two arguments") second_form = potential_forms[1] if second_form.potential_form != 'as.constant': raise ConfigurationException("the second argument to the trans() potential modifier must be 'as.constant' found {}".format(second_form)) if len(second_form.parameters) != 1: raise ConfigurationException("the second parameter to trans(), 'as.constant' should have exactly one parameter defining shift. {} parameters found.".format(len(second_form.parameters))) logger = logging.getLogger(__name__).getChild("trans") potential_func = potential_form_builder.create_potential_function(potential_forms[0]) trans_value = second_form.parameters[0] logger.debug("Creating trans() modifier with offset of {}".format(trans_value)) def transformed(r): return potential_func(r+trans_value) if hasattr(potential_func, 'deriv'): def deriv(r): return potential_func.deriv(r+trans_value) transformed.deriv = deriv if hasattr(potential_func, 'deriv2'): def deriv2(r): return potential_func.deriv2(r+trans_value) transformed.deriv2 = deriv2 return transformed