Skip to content

The simple.utils Namespace

The utils submodule contains various utilities and helpers that are used throughout the package.


simple.utils.OptionalArg module-attribute

OptionalArg = object()

simple.utils.REATTR module-attribute

REATTR = '(?:[ ]*([+-]?[0-9]*[.]?[0-9]*(?:[Ee]?[+-]?[0-9]+)?)[ ]*|(.*))'

simple.utils.REINDEX module-attribute

REINDEX = '([-]?[0-9]*)[:]([-]?[0-9]*)[:]?([-]?[0-9]*)|([-]?[0-9]+)'

simple.utils.SimpleHandler module-attribute

SimpleHandler = StreamHandler()

simple.utils.SimpleLogger module-attribute

SimpleLogger = getLogger('SIMPLE')

simple.utils.UNITS module-attribute

UNITS = dict(mass=['mass', 'massfrac', 'wt', 'wt%'], mole=['mole', 'moles', 'mol', 'molfrac'])

A dictionary containing the names associated with different unit types

Current unit types are:

  • mass that represents data being stored in a mass unit or as mass fractions.
  • mole that represents data being stored in moles or as mole fractions.

simple.utils.mask_eval module-attribute

mask_eval = MaskEval()

simple.utils.simple_eval module-attribute

simple_eval = AttrEval()

simple.utils.AttrEval

AttrEval()
Source code in simple/utils.py
1219
1220
def __init__(self):
    self.ab_evalstrings = []

ab_evalstrings instance-attribute

ab_evalstrings = []

add_ab_evaluator

add_ab_evaluator(opstr, operator)
Source code in simple/utils.py
1222
1223
def add_ab_evaluator(self, opstr, operator):
    self.ab_evalstrings.append((opstr, f'{REATTR}{opstr}{REATTR}', operator))

eval

eval(item, where, **where_kwargs)
Source code in simple/utils.py
1258
1259
1260
def eval(self, item, where, **where_kwargs):
    evaluate = self.parse_where(where)
    return evaluate(item, where_kwargs)

parse_where

parse_where(where)
Source code in simple/utils.py
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
def parse_where(self, where):
    eval = BoolEvaluator()

    if type(where) is not str:
        eval.add(None, where)
        return eval

    if "&" in where and "|" in where:
        raise ValueError('Where strings cannot contain both & and |')
    elif "|" in where:
        evalstrings = where.split('|')
        eval._and = False
    else:
        evalstrings = where.split('&')

    for evalstr in evalstrings:
        evalstr = evalstr.strip()
        if len(evalstr) == 0: continue

        for opstr, regex, opfunc in self.ab_evalstrings:
            # The first check significantly speeds up the evaluation
            if (opstr in evalstr) and (m := re.fullmatch(regex, evalstr.strip())):
                a_number, a_string, b_number, b_string = m.groups()
                eval.add(opfunc, EvalArg(a_number, a_string), EvalArg(b_number, b_string))
                break
        else:
            if (m := re.fullmatch(REATTR, evalstr.strip())):
                a_number, a_string = m.groups()
                eval.add(None, EvalArg(a_number, a_string))
            else:
                raise ValueError(f'Unable to parse condition "{evalstr}"')
    return eval

simple.utils.BoolEvaluator

BoolEvaluator()
Source code in simple/utils.py
1144
1145
1146
def __init__(self):
    self._evaluators = []
    self._and = True

add

add(operator, *args)
Source code in simple/utils.py
1148
1149
def add(self, operator, *args):
    self._evaluators.append((operator, args))

eval

eval(item, kwargs=None)
Source code in simple/utils.py
1176
1177
def eval(self, item, kwargs=None):
    return self.__call__(item, kwargs)

simple.utils.DefaultKwargs

DefaultKwargs(func, default_kwargs, inherits)

A callable wrapper that provides dynamic default keyword argument management for functions.

Allows attaching shortcuts with alternative default kwargs, inheritance from other DefaultKwargs instances, and matching function signatures while still accepting extended kwargs or **kwargs_. Optionally exposes the original wrapped function as ._func.

Parameters:

  • func (Callable) –

    The function to be wrapped.

  • parent (DefaultKwargs) –

    If supplied, all kwargs from this instance will be inherited unless overridden.

  • **kwargs

    Default keyword arguments that will be applied unless explicitly overridden.

Source code in simple/utils.py
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
def __init__(self, func, default_kwargs, inherits):
    self._func = func
    self._kwargs = default_kwargs

    if inherits:
        if type(inherits) is not self.__class__:
            raise TypeError('Can only inherit from other DefaultKwargs instances')
        elif inherits is self:
            raise ValueError('Cannot inherit from itself')
        else:
            self._inherits_from = inherits
    else:
        self._inherits_from = None

    self._shortcuts = {}

    parameters = inspect.signature(func).parameters

    if any([param.kind == inspect.Parameter.POSITIONAL_ONLY
            for name, param in parameters.items()]):
        raise ValueError('The DefaultKwargs wrapper does not work on functions with positional only arguments')


    # All the arguments that can be positional
    self._func_pos_names = [name
                            for name, param in parameters.items()
                            if param.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD]

    # All the arguments that can be used as keywords
    self._func_kw_names = [name
                           for name, param in parameters.items()
                           if param.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD
                           or param.kind == inspect.Parameter.KEYWORD_ONLY]

    # All the arguments with default values.
    self._func_kw_defaults = {name: param.default
                              for name, param in parameters.items()
                              if param.default is not inspect.Parameter.empty}

    if 'kwargs' in self._func_kw_names:
        self._has_kwargs_arg = True
        self._func_kw_names.remove('kwargs')
        self._func_kw_defaults.pop('kwargs')
    else:
        self._has_kwargs_arg = False

    if any([param.kind == inspect.Parameter.VAR_KEYWORD for name, param in parameters.items()]):
        self._has_star_star_arg = True
    else:
        self._has_star_star_arg = False

    functools.update_wrapper(self, func)

kwargs property

kwargs

The effective keyword arguments for the current instance, with inheritance and overrides applied.

Dict

Dict(*kwarg_dicts)

Bases: MutableMapping

A dictionary-like wrapper for managing and merging multiple keyword argument dictionaries.

This class allows combining several dictionaries into a single mapping. It enables key access, mutation, and deletion across all source dictionaries. It also supports extracting keys or key groups by prefix, optionally removing them in the process. Keys are resolved in insertion order.

Used to support decorated functions where kwargs are passed and consumed progressively.

Parameters:

  • *kwargs (dict) –

    One or more dictionaries to be combined and managed.

Source code in simple/utils.py
203
204
205
206
207
208
209
210
211
212
213
214
215
def __init__(self, *kwarg_dicts):
    self.dicts = [d for d in kwarg_dicts if
                  (isinstance(d, (dict, self.__class__)) and not isinstance(d, DefaultKwargs.DontUpdateDict))]
    self.combined = {}
    for d in kwarg_dicts:
        if isinstance(d, DefaultKwargs.DontUpdateDict):
            self.combined.update(d.dict)
        elif isinstance(d, (dict, self.__class__)):
            self.combined.update(d)
        elif d is None:
            pass
        else:
            raise TypeError(f'Items must be dictionaries or None (got {type(d)})')

combined instance-attribute

combined = {}

dicts instance-attribute

dicts = [d for d in kwarg_dicts if isinstance(d, (dict, __class__)) and not isinstance(d, DontUpdateDict)]

copy

copy()
Source code in simple/utils.py
240
241
def copy(self):
    return self.__class__(self.combined.copy())

get_many

get_many(keys=None, prefix=None, remove_prefix=True, **default_kwargs)

Created a new dictionary containing the matching keys.

This will not alter the original dictionary and its associated subdictionaries.

Parameters:

  • keys

    Keys to be extracted. This can be either a comma or space seperated string, a list of strings, or a callable where the named arguments will be used as keys. Missing keys are ignored. Default values can be defined using the default_kwargs argument.

  • prefix

    Any keyword with this prefix will be extracted. A "_" will be added to the end of the prefix if not already present. Can be a comma or space seperated string, or a list of strings.

  • remove_prefix

    If True the prefix part of the keyword is removed from the key in the returned dictionary.

  • **default_kwargs

    Forms the basis of the returned dictionary. Extracted values will overwrite these values.

Returns:

  • dict

    A dictionary containing the extracted items.

Source code in simple/utils.py
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
def get_many(self, keys=None, prefix=None, remove_prefix=True, **default_kwargs):
    """
    Created a new dictionary containing the matching keys.

    This will not alter the original dictionary and its associated subdictionaries.

    Args:
        keys (): Keys to be extracted. This can be either a comma or space seperated string, a list of
            strings, or a callable where the named arguments will be used as keys.
            Missing keys are ignored. Default values can be defined using the *default_kwargs* argument.
        prefix (): Any keyword with this prefix will be extracted. A ``"_"`` will be added to the end of the
            prefix if not already present. Can be a comma or space seperated string, or a list of strings.
        remove_prefix ():  If ``True`` the prefix part of the keyword is removed from the key in the returned
            dictionary.
        **default_kwargs (): Forms the basis of the returned dictionary. Extracted values will overwrite these
            values.

    Returns:
        dict: A dictionary containing the extracted items.
    """
    return self._get_many(keys, prefix, remove_prefix, default_kwargs, False)

pop_many

pop_many(keys=None, prefix=None, remove_prefix=True, **default_kwargs)

Created a new dictionary containing the matching keys.

This will remove the corresponding item from the original dictionary and its subdictionaries.

Parameters:

  • keys

    Keys to be extracted. This can be either a comma or space seperated string, a list of strings, or a callable where the named arguments will be used as keys. Missing keys are ignored. Default values can be defined using the default_kwargs argument.

  • prefix

    Any keyword with this prefix will be extracted. A "_" will be added to the end of the prefix if not already present. Can be a comma or space seperated string, or a list of strings.

  • remove_prefix

    If True the prefix part of the keyword is removed from the key in the returned dictionary.

  • **default_kwargs

    Forms the basis of the returned dictionary. Extracted values will overwrite these values.

Returns:

  • dict

    A dictionary containing the extracted items.

Source code in simple/utils.py
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
def pop_many(self, keys=None, prefix=None, remove_prefix=True, **default_kwargs):
    """
    Created a new dictionary containing the matching keys.

    This will remove the corresponding item from the original dictionary and its subdictionaries.

    Args:
        keys (): Keys to be extracted. This can be either a comma or space seperated string, a list of
            strings, or a callable where the named arguments will be used as keys.
            Missing keys are ignored. Default values can be defined using the *default_kwargs* argument.
        prefix (): Any keyword with this prefix will be extracted. A ``"_"`` will be added to the end of the
            prefix if not already present. Can be a comma or space seperated string, or a list of strings.
        remove_prefix ():  If ``True`` the prefix part of the keyword is removed from the key in the returned
            dictionary.
        **default_kwargs (): Forms the basis of the returned dictionary. Extracted values will overwrite these
            values.

    Returns:
        dict: A dictionary containing the extracted items.
    """
    return self._get_many(keys, prefix, remove_prefix, default_kwargs, True)

DontUpdateDict

DontUpdateDict(dict)
Source code in simple/utils.py
185
186
187
def __init__(self, dict):
    # Just to avoid the overhead of updating dicts without references.
    self.dict = dict

dict instance-attribute

dict = dict

Shortcut

Shortcut(name, parent, kwargs)

A callabe shortcut with its own additional set of default keyword arguments.

Source code in simple/utils.py
145
146
147
148
def __init__(self, name, parent, kwargs):
    self._name = name
    self._parent = parent
    self._kwargs = kwargs

kwargs property

kwargs

The effective keyword arguments for the current instance, with inheritance and overrides applied.

clear_kwargs

clear_kwargs()

Removes all items from the default kwargs of this shortcut.

Source code in simple/utils.py
178
179
180
181
182
def clear_kwargs(self):
    """
    Removes all items from the default kwargs of this shortcut.
    """
    self._kwargs.clear()

remove_kwargs

remove_kwargs(*args)

Remove items from the default kwargs of this shortcut.

Source code in simple/utils.py
171
172
173
174
175
176
def remove_kwargs(self, *args):
    """
    Remove items from the default kwargs of this shortcut.
    """
    for arg in args:
        self._kwargs.pop(arg, None)

update_kwargs

update_kwargs(**kwargs)

Update the default kwargs of this shortcut.

Source code in simple/utils.py
165
166
167
168
169
def update_kwargs(self, **kwargs):
    """
    Update the default kwargs of this shortcut.
    """
    self._kwargs.update(kwargs)

add_shortcut

add_shortcut(name, **kwargs)

Defines a named variant of this function with modified defaults. Accessible via attribute syntax.

Source code in simple/utils.py
443
444
445
446
447
def add_shortcut(self, name,  **kwargs):
    """
    Defines a named variant of this function with modified defaults. Accessible via attribute syntax.
    """
    self._shortcuts[name] = kwargs

clear_kwargs

clear_kwargs()

Removes all items from the default kwargs of this function.

Note: This will not delete default kwargs defined in the signature.

Source code in simple/utils.py
482
483
484
485
486
487
488
def clear_kwargs(self):
    """
    Removes all items from the default kwargs of this function.

    Note: This will not delete default kwargs defined in the signature.
    """
    self._kwargs.clear()

remove_kwargs

remove_kwargs(*args)

Remove items from the default kwargs of this function.

Note: This will not delete default kwargs defined in the signature.

Source code in simple/utils.py
473
474
475
476
477
478
479
480
def remove_kwargs(self, *args):
    """
    Remove items from the default kwargs of this function.

    Note: This will not delete default kwargs defined in the signature.
    """
    for arg in args:
        self._kwargs.pop(arg, None)

update_kwargs

update_kwargs(d=None, **kwargs)

Update the default kwargs of this function.

Source code in simple/utils.py
464
465
466
467
468
469
470
471
def update_kwargs(self, d=None, **kwargs):
    """
    Update the default kwargs of this function.
    """
    if d is not None:
        self._kwargs.update(d)
    if kwargs:
        self._kwargs.update(kwargs)

simple.utils.Element

Bases: str

RE class-attribute instance-attribute

RE = '([a-zA-Z]{1,2})([*_:][a-zA-Z0-9_]*)?'

latex

latex(dollar=True)

Returns a latex representation of the string e.g. Pd -> \(\mathrm{Pd}\)

Parameters:

  • dollar (bool, default: True ) –

    Whether to include the bracketing $ signs.

Source code in simple/utils.py
703
704
705
706
707
708
709
710
711
712
713
714
715
def latex(self, dollar=True):
    """
    Returns a latex representation of the string e.g. Pd -> $\mathrm{Pd}$

    Args:
        dollar (bool): Whether to include the bracketing ``$`` signs.

    """
    string = fr"\mathrm{{{self.symbol}{self.suffix}}}"
    if dollar:
        return f"${string}$"
    else:
        return string

without_suffix

without_suffix()

Return a new element string without the suffix.

Source code in simple/utils.py
717
718
719
720
721
def without_suffix(self):
    """
    Return a new element string without the suffix.
    """
    return self._new_(self.symbol, '')

simple.utils.EndlessList

Bases: list

A subclass of list that where the index will never go out of bounds. If a requested index is out of bounds, it will cycle around to the start of the list.

Examples:

>>> ls = simple.plot.Endlesslist(["a", "b", "c"])
>>> ls[3]
"a"

simple.utils.EvalArg

EvalArg(number, string)
Source code in simple/utils.py
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
def __init__(self, number, string):
    if number is not None:
        if '.' in number:
            v = float(number.strip())
        else:
            v = int(number)
        self.value = v
        self.is_attr = False
        self.is_kwarg = False
    else:
        string = string.strip()
        if string[0] == '.':
            is_attr = True
            string = string[1:].strip()
        else:
            is_attr = False
        if string[0] == '{' and string[-1] == '}':
            is_kwarg = True
            string = string[1:-1].strip()
        else:
            is_kwarg = False

        self.value = string
        self.is_attr = is_attr
        self.is_kwarg = is_kwarg
        if self.is_attr is False and self.is_kwarg is False:
            if string == 'True':
                self.value = True
            elif string == 'False':
                self.value = False
            elif string == 'None':
                self.value = None

NoAttr class-attribute instance-attribute

NoAttr = object()

is_attr instance-attribute

is_attr = False

is_kwarg instance-attribute

is_kwarg = False

value instance-attribute

value = v

NoAttributeError

Bases: AttributeError

simple.utils.Isotope

Isotope(string, without_suffix=False)

Bases: str

A subclass of string representing an isotope using the format <element symbol>-<mass number><suffix> e.g. Pd-105.

The order of the element symbol and mass number in string is not important, but they must proceed the suffix. The element symbol and mass number can optionally be seperated by -. The case of the element symbol is not considered.

Parameters:

  • string (str) –

    A string element symbol and a mass number.

  • without_suffix (bool, default: False ) –

    If True the suffix part of the string is ignored.

Attributes:

  • element (str) –

    The element symbol of the isotope

  • mass (str) –

    The mass number of the isotope

  • suffix (str) –

    The suffix of the isotope

Raises:

  • ValueError

    If string does not represent a valid isotope.

Source code in simple/utils.py
763
764
765
def __init__(self, string, without_suffix=False):
    # Never called. Just for the docstring.
    super(Isotope, self).__init__()

RE class-attribute instance-attribute

RE = '((([a-zA-Z]{1,2})[-]?([0-9]{1,3}))|(([0-9]{1,3})[-]?([a-zA-Z]{1,2})))([*_: ].*)?'

latex

latex(dollar=True)

Returns a latex representation of the string e.g. Pd-105 -> \({}^{105}\mathrm{Pd}\)

Parameters:

  • dollar (bool, default: True ) –

    Whether to include the bracketing $ signs.

Source code in simple/utils.py
776
777
778
779
780
781
782
783
784
785
786
787
788
def latex(self, dollar=True):
    """
    Returns a latex representation of the string e.g. Pd-105 -> ${}^{105}\mathrm{Pd}$

    Args:
        dollar (bool): Whether to include the bracketing ``$`` signs.

    """
    string = fr"{{}}^{{{self.mass}}}\mathrm{{{self.element}}}"
    if dollar:
        return f"${string}$"
    else:
        return string

without_suffix

without_suffix()

Return a new isotope string without the suffix.

Source code in simple/utils.py
790
791
792
793
794
def without_suffix(self):
    """
    Return a new isotope string without the suffix.
    """
    return self._new_(self.mass, self.element.without_suffix())

simple.utils.MaskEval

MaskEval()
Source code in simple/utils.py
1266
1267
def __init__(self):
    self.ab_evalstrings = []

ab_evalstrings instance-attribute

ab_evalstrings = []

add_ab_evaluator

add_ab_evaluator(opstr, operator)
Source code in simple/utils.py
1269
1270
def add_ab_evaluator(self, opstr, operator):
    self.ab_evalstrings.append((opstr, f'{REATTR}{opstr}{REATTR}', operator))

eval

eval(item, mask, shape, **mask_kwargs)
Source code in simple/utils.py
1315
1316
1317
def eval(self, item, mask, shape, **mask_kwargs):
    evaluate = self.parse_mask(mask)
    return evaluate(item, shape, mask_kwargs)

parse_mask

parse_mask(mask)
Source code in simple/utils.py
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
def parse_mask(self, mask):
    eval = MaskEvaluator()

    if type(mask) is not str:
        eval.add(None, mask)
        return eval

    if "&" in mask and "|" in mask:
        raise ValueError('Mask string cannot contain & and |')
    elif "|" in mask:
        evalstrings = mask.split('|')
        eval._and = False
    else:
        evalstrings = mask.split('&')

    for evalstr in evalstrings:
        evalstr = evalstr.strip()
        if len(evalstr) == 0:
            pass

        elif (m := re.fullmatch(REINDEX, evalstr)):
            start, stop, step, index = m.groups()
            if index is not None:
                eval.add(None, int(index))
            else:
                eval.add(None, slice(int(start) if start.strip() else None,
                                                    int(stop) if stop.strip() else None,
                                                    int(step) if step.strip() else None))
        else:
            for opstr, regex, opfunc in self.ab_evalstrings:
                # The first check significantly speeds up the evaluation
                if (opstr in evalstr) and (m := re.fullmatch(regex, evalstr.strip())):
                    a_number, a_string, b_number, b_string = m.groups()
                    eval.add(opfunc, EvalArg(a_number, a_string), EvalArg(b_number, b_string))
                    break
            else:
                if (m := re.fullmatch(REATTR, evalstr.strip())):
                    a_number, a_string = m.groups()
                    eval.add(None, EvalArg(a_number, a_string))
                else:
                    raise ValueError(f'Unable to parse condition "{evalstr}"')
    return eval

simple.utils.MaskEvaluator

MaskEvaluator()

Bases: BoolEvaluator

Source code in simple/utils.py
1144
1145
1146
def __init__(self):
    self._evaluators = []
    self._and = True

simple.utils.NamedDict

NamedDict(*args, **kwargs)

Bases: dict

A subclass of a normal dict where item in the dictionary can also be accessed as attributes.

Examples:

>>> nd = simple.utils.NamedDict({'a': 1, 'b': 2, 'c': 3})
>>> nd.a
1
Source code in simple/utils.py
102
103
def __init__(self, *args, **kwargs):
    self.update(*args, **kwargs)

setdefault

setdefault(key, default_value)
Source code in simple/utils.py
118
119
120
121
122
def setdefault(self, key, default_value):
    if key not in self:
        self[key] = default_value

    return self[key]

update

update(*args, **kwargs)
Source code in simple/utils.py
114
115
116
def update(self, *args, **kwargs):
    for k, v in dict(*args, **kwargs).items():
        self[k] = v

simple.utils.Ratio

Bases: str

A subclass of string representing a ratio of two isotopes using the format <numer>/<denom> e.g. Pd-108/Pd-105.

Parameters:

  • string (str) –

    A string consisting of two isotope seperated by a /.

  • without_suffix (bool) –

    If True the suffix part of the numerator and denominator`isotopes is ignored.

Attributes:

  • numer (str) –

    The numerator isotope

  • mass (str) –

    The denominator isotope

Raises:

  • ValueError

    If string does not represent a isotope ratio.

latex

latex(dollar=True)

Returns a latex representation of the string e.g. Pd-108/Pd-105 -> \({}^{108}\mathrm{Pd}/{}^{105}\mathrm{Pd}\)

Parameters:

  • dollar (bool, default: True ) –

    Whether to include the bracketing $ signs.

Source code in simple/utils.py
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
def latex(self, dollar=True):
    """
    Returns a latex representation of the string e.g. Pd-108/Pd-105 -> ${}^{108}\mathrm{Pd}/{}^{105}\mathrm{Pd}$

    Args:
        dollar (bool): Whether to include the bracketing ``$`` signs.
    """
    if type(self.numer) is str:
        numer = self.numer
    else:
        numer = self.numer.latex(dollar=dollar)
    if type(self.denom) is str:
        denom = self.denom
    else:
        denom = self.denom.latex(dollar=dollar)

    return f"{numer}/{denom}"

without_suffix

without_suffix()

Return a new isotope string without the suffix.

Source code in simple/utils.py
845
846
847
848
849
def without_suffix(self):
    """
    Return a new isotope string without the suffix.
    """
    return self._new_(self.numer.without_suffix(), self.denom.without_suffix())

simple.utils.add_shortcut

add_shortcut(name, **shortcut_kwargs)

Decorator that creates a named shortcut for a given DefaultKwargs instance.

Source code in simple/utils.py
503
504
505
506
507
508
509
510
511
512
513
514
def add_shortcut(name, **shortcut_kwargs):
    """
    Decorator that creates a named shortcut for a given `DefaultKwargs` instance.
    """
    def inner(func):
        if not isinstance(func, DefaultKwargs):
            raise TypeError("First call `set_default_kwargs` on function before adding shortcuts")
        else:
            func.add_shortcut(name, **shortcut_kwargs)

        return func
    return inner

simple.utils.asarray

asarray(values, dtype=None, saving=False)

Convert data to a numpy array.

If data is a string or a sequence of strings and saving=False, either a single string or a tuple of string will be returned. If saving is True the values will be converted to an array with a byte dtype. This ensures values are compatible with the hdf5 library.

Arrays with a bytes dtype will automatically be converted to the str dtype. If saving is False then this values will be converted to either a string or a tuple of strings (see above).

Parameters:

  • values

    An values like object.

  • dtype

    The data type of the returned values.

  • saving

    Should be True is the data is to be saved in a hdf5 file.

Source code in simple/utils.py
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
def asarray(values, dtype=None, saving=False):
    """
    Convert ``data`` to a numpy array.

    If ``data`` is a string or a sequence of strings and ``saving=False``, either a single string or a tuple
    of string will be returned. If ``saving`` is ``True`` the values will be converted to an array with a byte dtype.
    This ensures values are compatible with the hdf5 library.

    Arrays with a ``bytes`` dtype will automatically be converted to the ``str`` dtype. If ``saving`` is ``False`` then
    this values will be converted to either a string or a tuple of strings (see above).

    Args:
        values (): An values like object.
        dtype (): The data type of the returned values.
        saving (): Should be ``True`` is the data is to be saved in a hdf5 file.

    """
    values = np.asarray(values, dtype=dtype)

    if values.dtype.type is np.bytes_:
        values = values.astype(np.str_)

    if not saving and values.dtype.type is np.str_:
        values = values.tolist()
        if type(values) is list:
            values = tuple(values)

    if saving and values.dtype.type is np.str_:
        values = values.astype(np.bytes_)

    return values

simple.utils.aselement

aselement(string, without_suffix=False, allow_invalid=False)

Returns a Element representing an element symbol.

The returned element format is the capitalised element symbol followed by the suffix, if present. E.g. Pd-104* where * is the suffix.

The case of the element symbol is not considered.

Parameters:

  • string (str) –

    A string containing an element symbol.

  • without_suffix

    If True the suffix part of the string is ignored.

  • allow_invalid

    If False, and string cannot be parsed into an element string, an exception is raised. If True then string.strip() is returned instead.

Examples:

>>> ele = simple.asisotope("pd"); ele
"Pd"
Source code in simple/utils.py
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
def aselement(string, without_suffix=False, allow_invalid=False):
    """
    Returns a [``Element``][simple.utils.Element] representing an element symbol.

    The returned element format is the capitalised element
    symbol followed by the suffix, if present. E.g. ``Pd-104*`` where
    ``*`` is the suffix.

    The case of the element symbol is not considered.

    Args:
        string (str): A string containing an element symbol.
        without_suffix (): If ``True`` the suffix part of the string is ignored.
        allow_invalid ():  If ``False``, and ``string`` cannot be parsed into an element string, an exception is
            raised. If ``True`` then ``string.strip()`` is returned instead.

    Examples:
        >>> ele = simple.asisotope("pd"); ele
        "Pd"


    """
    if type(string) is Element:
        if without_suffix:
            return string.without_suffix()
        else:
            return string
    elif isinstance(string, str):
        string = string.strip()
    else:
        raise TypeError(f'``string`` must a str not {type(string)}')

    try:
        return Element(string, without_suffix=without_suffix)
    except ValueError:
        if allow_invalid:
            return string
        else:
            raise

simple.utils.aselements

aselements(strings, without_suffix=False, allow_invalid=False)

Returns a tuple of Element strings where each string represents an element symbol.

Parameters:

  • strings

    Can either be a string with element symbol seperated by a , or a sequence of strings.

  • without_suffix

    If True the suffix part of each isotope string is ignored.

  • allow_invalid

    If False, and a string cannot be parsed into an isotope string, an exception is raised. If True then string.strip() is returned instead.

Examples:

>>> simple.asisotopes('ru, pd, cd')
('Ru', 'Pd', 'Cd')
>>> simple.asisotopes(['ru', 'pd', 'cd'])
('Ru', 'Pd', 'Cd')
Source code in simple/utils.py
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
def aselements(strings, without_suffix=False, allow_invalid=False):
    """
    Returns a tuple of [``Element``][simple.utils.Element] strings where each string represents an element symbol.

    Args:
        strings (): Can either be a string with element symbol seperated by a ``,`` or a sequence of strings.
        without_suffix (): If ``True`` the suffix part of each isotope string is ignored.
        allow_invalid ():  If ``False``, and a string cannot be parsed into an isotope string, an exception is
            raised. If ``True`` then ``string.strip()`` is returned instead.

    Examples:
        >>> simple.asisotopes('ru, pd, cd')
        ('Ru', 'Pd', 'Cd')

        >>> simple.asisotopes(['ru', 'pd', 'cd'])
        ('Ru', 'Pd', 'Cd')
    """
    if type(strings) is str:
        strings = [s for s in strings.split(',')]

    return tuple(aselement(string, without_suffix=without_suffix, allow_invalid=allow_invalid) for string in strings)

simple.utils.asisolist

asisolist(isolist, without_suffix=False, allow_invalid=False)

Return a dictionary consisting of an isotope key mapped to a tuple of isotopes that should make up the key isotope.

If isolist is list or tuple of keys then each key will be mapped only to itself.

Parameters:

  • isolist

    Either a dictionary mapping a single isotope to a list of isotopes or a sequence of isotopes that will be mapped to themselfs.

  • without_suffix

    If True the suffix part of each isotope string is ignored.

  • allow_invalid

    If True invalid isotopes string are allowed. If False they will instead raise an exception.

Source code in simple/utils.py
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
def asisolist(isolist, without_suffix=False, allow_invalid=False):
    """
    Return a dictionary consisting of an isotope key mapped to a tuple of isotopes that should make up the
    key isotope.

    If ``isolist`` is list or tuple of keys then each key will be mapped only to itself.

    Args:
        isolist (): Either a dictionary mapping a single isotope to a list of isotopes or a sequence of isotopes that
            will be mapped to themselfs.
        without_suffix (): If ``True`` the suffix part of each isotope string is ignored.
        allow_invalid (): If ``True`` invalid isotopes string are allowed. If ``False`` they will instead raise
            an exception.
    """
    if type(isolist) is not dict:
        isolist = asisotopes(isolist, without_suffix=without_suffix, allow_invalid=allow_invalid)
        return {iso: (iso,) for iso in isolist}
    else:
        return {asisotope(k, without_suffix=without_suffix, allow_invalid=allow_invalid):
                asisotopes(v, without_suffix=without_suffix, allow_invalid=allow_invalid)
                for k,v in isolist.items()}

simple.utils.asisotope

asisotope(string, without_suffix=False, allow_invalid=False)

Returns a Isotope representing an isotope.

The returned isotope format is the capitalised element symbol followed by a dash followed by the mass number followed by the suffix, if present. E.g. Pd-104* where * is the suffix.

The order of the element symbol and mass number in string is not important, but they must proceed the suffix. The element symbol and mass number may be seperated by -. The case of the element symbol is not considered.

Parameters:

  • string (str) –

    A string element symbol and a mass number.

  • without_suffix

    If True the suffix part of the isotope string is ignored.

  • allow_invalid

    If False, and string cannot be parsed into an isotope string, an exception is raised. If True then string.strip() is returned instead.

Examples:

>>> iso = simple.asisotope("104pd"); iso # pd104, 104-Pd etc are also valid
"Pd-104"
>>> iso.symbol, iso.mass
"Pd", "104"
Source code in simple/utils.py
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
def asisotope(string, without_suffix=False, allow_invalid=False):
    """
    Returns a [``Isotope``][simple.utils.Isotope] representing an isotope.

    The returned isotope format is the capitalised element
    symbol followed by a dash followed by the mass number followed by the suffix, if present. E.g. ``Pd-104*`` where
    ``*`` is the suffix.

    The order of the element symbol and mass number in ``string`` is not important, but they must proceed the suffix.
    The element symbol and mass number may be seperated by ``-``. The case of the element symbol is not
    considered.

    Args:
        string (str): A string element symbol and a mass number.
        without_suffix (): If ``True`` the suffix part of the isotope string is ignored.
        allow_invalid ():  If ``False``, and ``string`` cannot be parsed into an isotope string, an exception is
            raised. If ``True`` then ``string.strip()`` is returned instead.

    Examples:
        >>> iso = simple.asisotope("104pd"); iso # pd104, 104-Pd etc are also valid
        "Pd-104"
        >>> iso.symbol, iso.mass
        "Pd", "104"

    """
    if type(string) is Isotope:
        if without_suffix:
            return string.without_suffix()
        else:
            return string
    elif isinstance(string, str):
        string = string.strip()
    else:
        raise TypeError(f'``string`` must a str not {type(string)}')

    try:
        return Isotope(string, without_suffix=without_suffix)
    except ValueError:
        if allow_invalid:
            return string
        else:
            raise

simple.utils.asisotopes

asisotopes(strings, without_suffix=False, allow_invalid=False)

Returns a tuple of Isotope strings where each string represents an isotope.

Parameters:

  • strings

    Can either be a string with isotopes seperated by a , or a sequence of strings.

  • without_suffix

    If True the suffix part of each isotope string is ignored.

  • allow_invalid

    If False, and a string cannot be parsed into an isotope string, an exception is raised. If True then string.strip() is returned instead.

Examples:

>>> simple.asisotopes('104pd, pd105, 106-Pd')
('Pd-104', 'Pd-105, 106-Pd')
>>> simple.asisotopes(['104pd', 'pd105', '106-Pd'])
('Pd-104', 'Pd-105, 106-Pd')
Source code in simple/utils.py
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
def asisotopes(strings, without_suffix=False, allow_invalid=False):
    """
    Returns a tuple of [``Isotope``][simple.utils.Isotope] strings where each string represents an isotope.

    Args:
        strings (): Can either be a string with isotopes seperated by a ``,`` or a sequence of strings.
        without_suffix (): If ``True`` the suffix part of each isotope string is ignored.
        allow_invalid ():  If ``False``, and a string cannot be parsed into an isotope string, an exception is
            raised. If ``True`` then ``string.strip()`` is returned instead.

    Examples:
        >>> simple.asisotopes('104pd, pd105, 106-Pd')
        ('Pd-104', 'Pd-105, 106-Pd')

        >>> simple.asisotopes(['104pd', 'pd105', '106-Pd'])
        ('Pd-104', 'Pd-105, 106-Pd')
    """
    if type(strings) is str:
        strings = [s for s in strings.split(',')]

    return tuple(asisotope(string, without_suffix=without_suffix, allow_invalid=allow_invalid) for string in strings)

simple.utils.askeyarray

askeyarray(values, keys, dtype=None)

Returns a numpy array where the columns can be accessed by the column key.

Parameters:

  • values

    An array consisting of 2 dimensions where first dimension is the row and the second dimension is the column.

  • keys

    The keys for each column in values. Must be the same length as the second dimension of values. of array.

  • dtype

    The values type of the returned array. All columns will have the same dtype.

Notes If values has less then 2 dimensions then it is assumed to represent a single row of values.

It is not possible to save this type of array in hdf5 files if they have more than a few hundred columns.

Examples:

>>> a = simple.askeyarray([[1,2,3],[4,5,6]], ['Pd-104','Pd-105','Pd-106']); a
array([(1, 2, 3), (4, 5, 6)],
      dtype=[('Pd-104', '<i8'), ('Pd-105', '<i8'), ('Pd-106', '<i8')])
>>> a['Pd-104']
array([1, 4])
Source code in simple/utils.py
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
def askeyarray(values, keys, dtype=None):
    """
    Returns a numpy array where the columns can be accessed by the column key.

    Args:
        values (): An array consisting of 2 dimensions where first dimension is the row and the second
            dimension is the column.
        keys (): The keys for each column in ``values``. Must be the same length as the second dimension of ``values``.
            of ``array``.
        dtype (): The values type of the returned array. All columns will have the same dtype.

    **Notes**
    If ``values`` has less then 2 dimensions then it is assumed to represent a single row of values.

    It is not possible to save this type of array in hdf5 files if they have more than a few hundred columns.

    Examples:
        >>> a = simple.askeyarray([[1,2,3],[4,5,6]], ['Pd-104','Pd-105','Pd-106']); a
        array([(1, 2, 3), (4, 5, 6)],
              dtype=[('Pd-104', '<i8'), ('Pd-105', '<i8'), ('Pd-106', '<i8')])
        >>> a['Pd-104']
        array([1, 4])

    """
    if type(keys) is str:
        keys = [k.strip() for k in keys.split(',')]

    a = np.asarray(values, dtype=dtype)
    dtype = [(k, a.dtype) for k in keys]

    if a.ndim < 2:
        a = a.reshape((-1, a.size))
    elif a.ndim > 2:
        raise ValueError('``values`` cannot have more than 2 dimensions')

    if a.shape[1] != len(keys):
        raise ValueError(
            f'item (r:{a.shape[0]}, c:{a.shape[1]}) must have same number of columns as there are keys ({len(keys)})')

    return np.array([tuple(r) for r in a], dtype=dtype)

simple.utils.asratio

asratio(string, without_suffix=False, allow_invalid=False)

Returns a Ratio string representing the ratio of two isotopes.

The format of the returned string is the numerator followed by a / followed by the normiso. The numerator and normiso string be parsed by asisotope together with the given without_suffix and allow_invalid arguments passed to this function.

Parameters:

  • string (str) –

    A string contaning two strings seperated by a single /.

  • without_suffix (bool, default: False ) –

    If True the suffix part of the numerator and normiso string is ignored.

  • allow_invalid (bool, default: False ) –

    Whether the numerator and normiso has to be a valid isotope string.

If the returned string is an isotope string it will have the following attributes and methods.

Attributes:

  • numer (str) –

    The numerator string

  • denom (str) –

    The normiso string

Functions:

  • latex

    Returns a latex formatted version of the isotope.

  • without_suffix

    Returns a ratio string omitting the numerator and normiso suffix.

Source code in simple/utils.py
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
def asratio(string, without_suffix=False, allow_invalid=False):
    """
    Returns a [``Ratio``][simple.utils.Isotope] string representing the ratio of two isotopes.

    The format of the returned string is the numerator followed by
    a ``/`` followed by the normiso. The numerator and normiso string be parsed by ``asisotope`` together with
    the given ``without_suffix`` and ``allow_invalid`` arguments passed to this function.

    Args:
        string (str): A string contaning two strings seperated by a single ``/``.
        without_suffix (bool): If ``True`` the suffix part of the numerator and normiso string is ignored.
        allow_invalid (bool): Whether the numerator and normiso has to be a valid isotope string.

    If the returned string is an isotope string it will have the following attributes and methods.

    Attributes:
        numer (str): The numerator string
        denom (str): The normiso string

    Methods:
        latex(string): Returns a latex formatted version of the isotope.
        without_suffix(): Returns a ratio string omitting the numerator and normiso suffix.

    """
    if type(string) is Ratio:
        return string
    elif isinstance(string, str):
        string = string.strip()
    else:
        raise TypeError(f'``string`` must a str not {type(string)}')

    try:
        return Ratio(string, without_suffix=without_suffix)
    except ValueError:
        if allow_invalid:
            return string
        else:
            raise

simple.utils.asratios

asratios(strings, without_suffix=False, allow_invalid=False)

Returns a tuple of Ratio strings where each string represents the ratio of two isotopes.

Parameters:

  • strings

    Can either be a string with isotope ratios seperated by a , or a sequence of strings.

  • without_suffix

    If True the suffix part of each isotope string is ignored.

  • allow_invalid

    If False, and a string cannot be parsed into an isotope string, an exception is raised. If True then string.strip() is returned instead.

Source code in simple/utils.py
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
def asratios(strings, without_suffix=False, allow_invalid=False):
    """
    Returns a tuple of [``Ratio``][simple.utils.Isotope] strings where each string represents the ratio of two isotopes.

    Args:
        strings (): Can either be a string with isotope ratios seperated by a ``,`` or a sequence of strings.
        without_suffix (): If ``True`` the suffix part of each isotope string is ignored.
        allow_invalid ():  If ``False``, and a string cannot be parsed into an isotope string, an exception is
            raised. If ``True`` then ``string.strip()`` is returned instead.
    """
    if type(strings) is str:
        strings = [s.strip() for s in strings.split(',')]

    return tuple(asratio(string, without_suffix=without_suffix, allow_invalid=allow_invalid) for string in strings)

simple.utils.deprecation_warning

deprecation_warning(message)

Used to signal that a function is deprecated. Will raise a warning with message when the function is used.

Source code in simple/utils.py
516
517
518
519
520
521
522
523
524
525
526
def deprecation_warning(message):
    """
    Used to signal that a function is deprecated. Will raise a warning with *message* when the function is used.
    """
    def inner(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            logger.warning(f'DeprecationWarning: {message}')
            return func(*args, **kwargs)
        return wrapper
    return inner

simple.utils.get_isotopes_of_element

get_isotopes_of_element(isotopes, element, isotopes_without_suffix=False)

Returns a tuple of all isotopes in a sequence that contain the given element symbol.

Note The strings in isotopes will be passed through asisotopes before the evaluation and therefore do not have to be correcly formatted. Invalid isotope string are allowed but will be ignored by the evaluation.

Parameters:

  • isotopes

    An iterable of strings representing isotopes.

  • element (str) –

    The element symbol.

  • isotopes_without_suffix (bool, default: False ) –

    If True suffixes will be removed from the isotopes in isotopes before the evaluation takes place.

Examples:

>>> simple.utils.get_isotopes_of_element(["Ru-101", "Pd-102", "Rh-103", "Pd-104"], "Pd")
>>> ("Pd-102", "Pd-104")
Source code in simple/utils.py
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
def get_isotopes_of_element(isotopes, element, isotopes_without_suffix=False):
    """
    Returns a tuple of all isotopes in a sequence that contain the given element symbol.

    **Note** The strings in ``isotopes`` will be passed through [asisotopes][simple.asisotopes] before
    the evaluation and therefore do not have to be correcly formatted. Invalid isotope string are allowed
    but will be ignored by the evaluation.

    Args:
        isotopes (): An iterable of strings representing isotopes.
        element (str): The element symbol.
        isotopes_without_suffix (bool): If ``True`` suffixes will be removed from the isotopes in ``isotopes``
            before the evaluation takes place.

    Examples:
        >>> simple.utils.get_isotopes_of_element(["Ru-101", "Pd-102", "Rh-103", "Pd-104"], "Pd")
        >>> ("Pd-102", "Pd-104")
    """
    isotopes = asisolist(isotopes, without_suffix=isotopes_without_suffix, allow_invalid=True)
    element = aselement(element)
    return tuple(iso for iso in isotopes if (type(iso) is Isotope and iso.element == element))

simple.utils.get_last_attr

get_last_attr(item, attrname, default=OptionalArg)
Source code in simple/utils.py
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
def get_last_attr(item, attrname, default=OptionalArg):
    # Return the final attribute in the chain ``attrname`` starting from ``item``
    attrnames = parse_attrname(attrname).split('.')
    attr = item
    for i, name in enumerate(attrnames):
        try:
            if isinstance(attr, dict):
                attr = attr[name]
            else:
                attr = getattr(attr, name)
        except (AttributeError, KeyError):
            if default is not OptionalArg:
                # Log message?
                return default
            else:
                raise AttributeError(f'Item {item} has no attribute {".".join(attrnames[:i+1])}')
    return attr

simple.utils.load_defaults

load_defaults(filename)

Loads default arguments for functions from a YAML formatted file.

To use a set of default values, unpack the arguments in the function call (See example).

You can still arguments and keyword arguments as normal as long as they are not included in the default dictionary.

Returns:

  • A named dictionary mapping the prefixes given in the yaml file to another dictionary mapping the arguments

  • to the specified values.

Examples:

The file default.yaml is expected to look like this:

somefunction:
    arg: value
    listarg:
        - first thing in list
        - second thing in list

anotherfunction:
    arg: value

It can be used like this

>>> defaults = simple.load_defaults('defaults.yaml')
>>> somefunction(**defaults['somefunction']) # Unpack arguments into function call
Source code in simple/utils.py
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
def load_defaults(filename: str):
    """
    Loads default arguments for functions from a YAML formatted file.

    To use a set of default values, unpack the arguments in the function call (See example).

    You can still arguments and keyword arguments as normal as long as they are not included in the default dictionary.

    Returns:
        A named dictionary mapping the prefixes given in the yaml file to another dictionary mapping the arguments
        to the specified values.

    Examples:
        The file ``default.yaml`` is expected to look like this:
        ```
        somefunction:
            arg: value
            listarg:
                - first thing in list
                - second thing in list

        anotherfunction:
            arg: value
        ```

        It can be used like this
        >>> defaults = simple.load_defaults('defaults.yaml')
        >>> somefunction(**defaults['somefunction']) # Unpack arguments into function call


    """
    return dict(yaml.safe_load(open(filename, 'r').read()))

simple.utils.models_where

models_where(models, where, **where_kwargs)
Source code in simple/utils.py
1333
1334
1335
1336
1337
1338
1339
1340
def models_where(models, where, **where_kwargs):
    eval = simple_eval.parse_where(where)
    out = []
    for model in models:
        if eval.eval(model, where_kwargs):
            out.append(model)

    return out

simple.utils.parse_attrname

parse_attrname(attrname)
Source code in simple/utils.py
50
51
52
53
54
def parse_attrname(attrname):
    if attrname is None:
        return None
    else:
        return '.'.join([aa for a in attrname.split('.') if (aa := a.strip()) != ''])

simple.utils.select_isolist

select_isolist(isolist, data, *, without_suffix=False)

Creates a subselection of data containing only the isotopes in isolist.

If multiple input isotopes are given for an output isotope in isolist the values of the input isotopes will be added together. Any isotopes missing from data will be given a value of 0.

When using this function to account for radioactive decay you want the unit of data to be in moles.

Parameters:

  • isolist

    Either a list of isotopes to be selected or a dictionary consisting of the final isotope mapped to a list of isotopes to be added together for this isotope.

  • data

    A key array from which the subselection will be made.

  • without_suffix

    If True the suffix will be removed from all isotope strings in isolist.

Returns:

  • A new key array containing only the isotopes in isolist.

Source code in simple/utils.py
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
def select_isolist(isolist, data, *, without_suffix=False):
    """
    Creates a subselection of ``data`` containing only the isotopes in ``isolist``.

    If multiple input isotopes are given for an output isotope in ``isolist`` the values of the input isotopes will
    be added together. Any isotopes missing from ``data`` will be given a value of 0.

    When using this function to account for radioactive decay you want the unit of ``data`` to be in moles.

    Args:
        isolist (): Either a list of isotopes to be selected or a dictionary consisting of the
             final isotope mapped to a list of isotopes to be added together for this isotope.
        data (): A key array from which the subselection will be made.
        without_suffix (): If ``True`` the suffix will be removed from all isotope strings in ``isolist``.


    Returns:
        A new key array containing only the isotopes in ``isolist``.

    """
    isolist = asisolist(isolist, without_suffix=without_suffix)
    data = np.asarray(data)

    new_data = []
    missing_isotopes = []

    if data.dtype.names is None:
        raise ValueError('``data`` must be a key arrray')

    for mainiso, inciso in isolist.items():
        value = np.zeros(data.shape)

        for iso in inciso:
            if iso in data.dtype.names:
                value += data[iso]
            else:
                missing_isotopes.append(iso)

        new_data.append(value)

    result = askeyarray(np.array(new_data).transpose(), isolist.keys())

    if len(missing_isotopes) != 0:
        logger.warning(f'Missing isotopes set to 0: {", ".join(missing_isotopes)}')

    return result

simple.utils.set_default_kwargs

set_default_kwargs(*, inherits_=None, **default_kwargs)

Decorator sets the default keyword arguments for the function. It wraps the function so that the default kwargs are always passed to the function.

The kwargs can be accessed from <func>.kwargs. To update the dictionary use the function update__kwargs attached to the return function.

Source code in simple/utils.py
491
492
493
494
495
496
497
498
499
500
501
def set_default_kwargs(*, inherits_ = None, **default_kwargs):
    """
    Decorator sets the default keyword arguments for the function. It wraps the function so that the
    default kwargs are always passed to the function.

    The kwargs can be accessed from ``<func>.kwargs``. To update the dictionary use the function
    ``update__kwargs`` attached to the return function.
    """
    def decorator(func):
        return DefaultKwargs(func, default_kwargs, inherits_)
    return decorator

simple.utils.set_logging_level

set_logging_level(level)

Set the level of messages to be displayed.

Options are: DEBUG, INFO, WARNING, ERROR.

Source code in simple/utils.py
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
def set_logging_level(level):
    """
    Set the level of messages to be displayed.

    Options are: DEBUG, INFO, WARNING, ERROR.
    """
    if level.upper() == 'DEBUG':
        level = logging.DEBUG
    elif level.upper() == 'INFO':
        level = logging.INFO
    elif level.upper() == 'WARNING':
        level = logging.WARNING
    elif level.upper() == 'ERROR':
        level = logging.ERROR

    SimpleLogger.setLevel(level)