Source code for order.shift

# coding: utf-8

"""
Classes and helpers to describe and work with systematic shifts.
"""


__all__ = ["Shift"]


import scinum as sn

from order.unique import UniqueObject
from order.mixins import CopyMixin, AuxDataMixin, TagMixin, LabelMixin
from order.util import typed


[docs]class Shift(UniqueObject, CopyMixin, AuxDataMixin, TagMixin, LabelMixin): """ Description of a systematic shift. **Arguments** The shift *name* should either be ``"nominal"`` or it should have the format ``"<source>_<direction>"`` where *direction* is either ``"up"`` or ``"down"``. *type* describes the shift's effect, which is either only rate-changing (*RATE*) or also shape-changing (*SHAPE*). When *None*, *UNKNOWN* is used. *label* and *label_short* are forwarded to the :py:class:`~order.mixins.LabelMixin`, *tags* to the :py:class:`~order.mixins.TagMixin`, *aux* to the :py:class:`~order.mixins.AuxDataMixin` *name* and *id* (defaulting to an auto id) to the :py:class:`~order.unique.UniqueObject` constructor. **Copy behavior** ``copy()`` All attributes are copied. ``copy_shallow()`` No difference with respect to ``copy()``, all attributes are copied. **Example** .. code-block:: python import order as od s = od.Shift("nominal", 1) s.name # -> "nominal" s.is_up # -> False s = Shift("pdf_up", 2) s.source # -> "pdf" s.direction # -> "up" s.is_up # -> True **Members** .. py:classattribute:: NOMINAL type: string Flag denoting a nominal shift (``"nominal"``). Same as `scinum.Number.NOMINAL <https://scinum.readthedocs.io/en/latest/#scinum.Number.NOMINAL>`__. .. py:classattribute:: UP type: string Flag denoting an up variation (``"up"``). Same as `scinum.Number.UP <https://scinum.readthedocs.io/en/latest/#scinum.Number.UP>`__. .. py:classattribute:: DOWN type: string Flag denoting a down variation (``"down"``). Same as `scinum.Number.DOWN <https://scinum.readthedocs.io/en/latest/#scinum.Number.DOWN>`__. .. py:classattribute:: RATE type: string Flag denoting a rate-changing effect (``"rate"``). .. py:classattribute:: SHAPE type: string Flag denoting a shape-changing effect (``"shape"``). .. py:classattribute:: RATE_SHAPE type: string Flag denoting a both rate- and shape-changing effect (``"rate_shape"``). .. py:attribute:: source type: string (read-only) The source of this shift, e.g. *NOMINAL*, ``"pdf"``, etc. .. py:attribute:: direction type: string (read-only) The direction of this shift, either *NOMINAL*, *UP* or *DOWN*. .. py:attribute:: type type: string The type of this shift, either *RATE*, *SHAPE* or *RATE_SHAPE*. .. py:attribute:: is_nominal type: bool (read-only) Flag denoting if the shift is nominal. .. py:attribute:: is_up type: bool (read-only) Flag denoting if the shift direction is *UP*. .. py:attribute:: is_down type: bool (read-only) Flag denoting if the shift direction is *DOWN*. .. py:attribute:: is_rate type: bool (read-only) Flag denoting if the shift type is rate-changing only. .. py:attribute:: is_shape type: bool (read-only) Flag denoting if the shift type is shape-changing only. .. py:attribute:: is_rate_shape type: bool (read-only) Flag denoting if the shift type is rate- and shape-changing. """ cls_name_singular = "shift" cls_name_plural = "shifts" # nominal flag NOMINAL = sn.Number.NOMINAL # shift directions UP = sn.Number.UP DOWN = sn.Number.DOWN # shift types RATE = "rate" SHAPE = "shape" RATE_SHAPE = "rate_shape" # attributes for copying copy_specs = ( UniqueObject.copy_specs + AuxDataMixin.copy_specs + TagMixin.copy_specs + LabelMixin.copy_specs )
[docs] @classmethod def split_name(cls, name): """ Splits a shift *name* into its source and direction. If *name* is *NOMINAL*, both source and direction will be *NOMINAL*. Example: .. code-block:: python split_name("nominal") # -> ("nominal", "nominal") split_name("pdf_up") # -> ("pdf", "up") split_name("pdfup") # -> ValueError: invalid shift name format: pdfup """ if name == cls.NOMINAL: return (cls.NOMINAL, cls.NOMINAL) if "_" in name: source, direction = tuple(name.rsplit("_", 1)) if source == cls.NOMINAL: raise ValueError("pointless nominal shift name: {}".format(name)) if direction not in (cls.UP, cls.DOWN): raise ValueError("invalid shift direction: {}".format(direction)) return (source, direction) raise ValueError("invalid shift name format: {}".format(name))
[docs] @classmethod def join_name(cls, source, direction): """ Joins a shift *source* and a shift *direction* to return a shift name. If either *source* or *direction* is *None*, *None* is returned. If *source* is *NOMINAL*, *direction* must be *NOMINAL* as well. Otherwise, *direction* must be either *UP* or *DOWN*. Example: .. code-block:: python join_name("nominal", "nominal") # -> "nominal" join_name("nominal", "up") # -> ValueError: pointless nominal shift direction join_name("pdf", "up") # -> "pdf_up" join_name("pdf", "high") # -> ValueError: invalid shift direction """ if source == cls.NOMINAL: if direction == cls.NOMINAL: return cls.NOMINAL raise ValueError("pointless nominal shift direction: {}".format(direction)) if direction in (cls.UP, cls.DOWN): return "{}_{}".format(source, direction) raise ValueError("unknown shift direction: {}".format(direction))
def __init__(self, name, id, type=None, label=None, label_short=None, tags=None, aux=None): UniqueObject.__init__(self, name, id) CopyMixin.__init__(self) AuxDataMixin.__init__(self, aux=aux) TagMixin.__init__(self, tags=tags) LabelMixin.__init__(self, label=label, label_short=label_short) # register empty attributes self._source = None self._direction = None self._type = self.RATE_SHAPE # set initial values self._source, self._direction = self.split_name(self.name) if type is not None: self.type = type @property def source(self): # source getter return self._source @property def direction(self): # direction getter return self._direction @typed def type(self, type): # type parser if type not in (self.RATE, self.SHAPE, self.RATE_SHAPE): raise ValueError("unknown type: {}".format(type)) return type @property def is_nominal(self): # is_nominal getter return self.name == self.NOMINAL @property def is_up(self): # is_up getter return self.direction == self.UP @property def is_down(self): # is_down getter return self.direction == self.DOWN @property def is_rate(self): # is_rate getter return self.type == self.RATE @property def is_shape(self): # is_shape getter return self.type == self.SHAPE @property def is_rate_shape(self): # is_rate_shape getter return self.type == self.RATE_SHAPE