Commit 40ebc7f2 authored by Stauff, Nicolas Emile's avatar Stauff, Nicolas Emile
Browse files

Merge branch 'infrastructure' into 'main'

Infrastructure

See merge request nstauff/PyGriffin!1
parents 186ed7e4 304480a1
Loading
Loading
Loading
Loading

.gitignore

0 → 100644
+1 −0
Original line number Diff line number Diff line
**/*__pycache__
 No newline at end of file

CODE_OF_CONDUCT.md

0 → 100644
+76 −0
Original line number Diff line number Diff line
# Contributor Covenant Code of Conduct

## Our Pledge

In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.

## Our Standards

Examples of behavior that contributes to creating a positive environment
include:

* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members

Examples of unacceptable behavior by participants include:

* The use of sexualized language or imagery and unwelcome sexual attention or
  advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
  address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
  professional setting

## Our Responsibilities

Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.

Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.

## Scope

This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at pshriwise@anl.gov or nstauff@anl.gov.
All complaints will be reviewed and investigated and will result in a response
that is deemed necessary and appropriate to the circumstances. The project team
is obligated to maintain confidentiality with regard to the reporter of an
incident. However, note that some project team members may have a legal
obligation to report certain forms of harassment because of their affiliation
(for example, staff and faculty at universities in the United States). Further
details of specific enforcement policies may be posted separately.

Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html

[homepage]: https://www.contributor-covenant.org
+30 −0
Original line number Diff line number Diff line
# PyGriffin

## Prerequisites

  - [NumPy](https://numpy.org/)

## Configuring PyGriffin

There are a number to indicate location of the Griffin executable to PyGriffin.

  1. Pass the path to the Griffin executable to the constructor of the PyGriffin
     class.
  2. Set the `GRIFFIN_EXEC` environment variable in the shell environment
     PyGriffin is called from.
  3. Set the value of `griffin_path` in a .pygriffin.rc configuration file a the
     top of the PyGriffin repository.

These will be preferentially used by PyGriffin in the order they are provided by
the user and the user's compute environment.

## Installing

For now, the best method for installing PyGriffin is to add the top of the
PyGriffin repository to your `PYTHONPATH.

## Testing

Move to the `tests` directory of PyGriffin and run:

```bash
pytest -v
```

pygriffin/__init__.py

0 → 100644
+3 −0
Original line number Diff line number Diff line
__version__ = '0.0.1'

from pygriffin.config import *
+341 −0
Original line number Diff line number Diff line
import copy
from collections.abc import Iterable

import numpy as np

# Provided from OpenMC (commit c8003e0)

def check_type(name, value, expected_type, expected_iter_type=None, *, none_ok=False):
    """Ensure that an object is of an expected type. Optionally, if the object is
    iterable, check that each element is of a particular type.

    Parameters
    ----------
    name : str
        Description of value being checked
    value : object
        Object to check type of
    expected_type : type or Iterable of type
        type to check object against
    expected_iter_type : type or Iterable of type or None, optional
        Expected type of each element in value, assuming it is iterable. If
        None, no check will be performed.
    none_ok : bool, optional
        Whether None is allowed as a value

    """
    if none_ok and value is None:
        return

    if not isinstance(value, expected_type):
        if isinstance(expected_type, Iterable):
            msg = 'Unable to set "{}" to "{}" which is not one of the ' \
                  'following types: "{}"'.format(name, value, ', '.join(
                      [t.__name__ for t in expected_type]))
        else:
            msg = (f'Unable to set "{name}" to "{value}" which is not of type "'
                   f'{expected_type.__name__}"')
        raise TypeError(msg)

    if expected_iter_type:
        if isinstance(value, np.ndarray):
            if not issubclass(value.dtype.type, expected_iter_type):
                msg = (f'Unable to set "{name}" to "{value}" since each item '
                       f'must be of type "{expected_iter_type.__name__}"')
                raise TypeError(msg)
            else:
                return

        for item in value:
            if not isinstance(item, expected_iter_type):
                if isinstance(expected_iter_type, Iterable):
                    msg = 'Unable to set "{}" to "{}" since each item must be ' \
                          'one of the following types: "{}"'.format(
                              name, value, ', '.join([t.__name__ for t in
                                                      expected_iter_type]))
                else:
                    msg = (f'Unable to set "{name}" to "{value}" since each '
                           f'item must be of type "{expected_iter_type.__name__}"')
                raise TypeError(msg)


def check_iterable_type(name, value, expected_type, min_depth=1, max_depth=1):
    """Ensure that an object is an iterable containing an expected type.

    Parameters
    ----------
    name : str
        Description of value being checked
    value : Iterable
        Iterable, possibly of other iterables, that should ultimately contain
        the expected type
    expected_type : type
        type that the iterable should contain
    min_depth : int
        The minimum number of layers of nested iterables there should be before
        reaching the ultimately contained items
    max_depth : int
        The maximum number of layers of nested iterables there should be before
        reaching the ultimately contained items
    """
    # Initialize the tree at the very first item.
    tree = [value]
    index = [0]

    # Traverse the tree.
    while index[0] != len(tree[0]):
        # If we are done with this level of the tree, go to the next branch on
        # the level above this one.
        if index[-1] == len(tree[-1]):
            del index[-1]
            del tree[-1]
            index[-1] += 1
            continue

        # Get a string representation of the current index in case we raise an
        # exception.
        form = '[' + '{:d}, ' * (len(index)-1) + '{:d}]'
        ind_str = form.format(*index)

        # What is the current item we are looking at?
        current_item = tree[-1][index[-1]]

        # If this item is of the expected type, then we've reached the bottom
        # level of this branch.
        if isinstance(current_item, expected_type):
            # Is this deep enough?
            if len(tree) < min_depth:
                msg = (f'Error setting "{name}": The item at {ind_str} does not '
                       f'meet the minimum depth of {min_depth}')
                raise TypeError(msg)

            # This item is okay.  Move on to the next item.
            index[-1] += 1

        # If this item is not of the expected type, then it's either an error or
        # on a deeper level of the tree.
        else:
            if isinstance(current_item, Iterable):
                # The tree goes deeper here, let's explore it.
                tree.append(current_item)
                index.append(0)

                # But first, have we exceeded the max depth?
                if len(tree) > max_depth:
                    msg = (f'Error setting {name}: Found an iterable at '
                           f'{ind_str}, items in that iterable exceed the '
                           f'maximum depth of {max_depth}')
                    raise TypeError(msg)

            else:
                # This item is completely unexpected.
                msg = (f'Error setting {name}: Items must be of type '
                       f'"{expected_type.__name__}", but item at {ind_str} is '
                       f'of type "{type(current_item).__name__}"')
                raise TypeError(msg)


def check_length(name, value, length_min, length_max=None):
    """Ensure that a sized object has length within a given range.

    Parameters
    ----------
    name : str
        Description of value being checked
    value : collections.Sized
        Object to check length of
    length_min : int
        Minimum length of object
    length_max : int or None, optional
        Maximum length of object. If None, it is assumed object must be of
        length length_min.

    """

    if length_max is None:
        if len(value) < length_min:
            msg = (f'Unable to set "{name}" to "{value}" since it must be at '
                   f'least of length "{length_min}"')
            raise ValueError(msg)
    elif not length_min <= len(value) <= length_max:
        if length_min == length_max:
            msg = (f'Unable to set "{name}" to "{value}" since it must be of '
                  f'length "{length_min}"')
        else:
            msg = (f'Unable to set "{name}" to "{value}" since it must have '
                   f'length between "{length_min}" and "{length_max}"')
        raise ValueError(msg)


def check_value(name, value, accepted_values):
    """Ensure that an object's value is contained in a set of acceptable values.

    Parameters
    ----------
    name : str
        Description of value being checked
    value : collections.Iterable
        Object to check
    accepted_values : collections.Container
        Container of acceptable values

    """

    if value not in accepted_values:
        msg = (f'Unable to set "{name}" to "{value}" since it is not in '
               f'"{accepted_values}"')
        raise ValueError(msg)


def check_less_than(name, value, maximum, equality=False):
    """Ensure that an object's value is less than a given value.

    Parameters
    ----------
    name : str
        Description of the value being checked
    value : object
        Object to check
    maximum : object
        Maximum value to check against
    equality : bool, optional
        Whether equality is allowed. Defaults to False.

    """

    if equality:
        if value > maximum:
            msg = (f'Unable to set "{name}" to "{value}" since it is greater '
                   f'than "{maximum}"')
            raise ValueError(msg)
    else:
        if value >= maximum:
            msg = (f'Unable to set "{name}" to "{value}" since it is greater '
                   f'than or equal to "{maximum}"')
            raise ValueError(msg)


def check_greater_than(name, value, minimum, equality=False):
    """Ensure that an object's value is greater than a given value.

    Parameters
    ----------
    name : str
        Description of the value being checked
    value : object
        Object to check
    minimum : object
        Minimum value to check against
    equality : bool, optional
        Whether equality is allowed. Defaults to False.

    """

    if equality:
        if value < minimum:
            msg = (f'Unable to set "{name}" to "{value}" since it is less than '
                   f'"{minimum}"')
            raise ValueError(msg)
    else:
        if value <= minimum:
            msg = (f'Unable to set "{name}" to "{value}" since it is less than '
                   f'or equal to "{minimum}"')
            raise ValueError(msg)


def check_filetype_version(obj, expected_type, expected_version):
    """Check filetype and version of an HDF5 file.

    Parameters
    ----------
    obj : h5py.File
        HDF5 file to check
    expected_type : str
        Expected file type, e.g. 'statepoint'
    expected_version : int
        Expected major version number.

    """
    try:
        this_filetype = obj.attrs['filetype'].decode()
        this_version = obj.attrs['version']

        # Check filetype
        if this_filetype != expected_type:
            raise IOError(f'{obj.filename} is not a {expected_type} file.')

        # Check version
        if this_version[0] != expected_version:
            raise IOError('{} file has a version of {} which is not '
                          'consistent with the version expected by OpenMC, {}'
                          .format(this_filetype,
                                  '.'.join(str(v) for v in this_version),
                                  expected_version))
    except AttributeError:
        raise IOError(f'Could not read {obj.filename} file. This most likely '
                      'means the file was produced by a different version of '
                      'OpenMC than the one you are using.')


class CheckedList(list):
    """A list for which each element is type-checked as it's added

    Parameters
    ----------
    expected_type : type or Iterable of type
        Type(s) which each element should be
    name : str
        Name of data being checked
    items : Iterable, optional
        Items to initialize the list with

    """

    def __init__(self, expected_type, name, items=None):
        super().__init__()
        self.expected_type = expected_type
        self.name = name
        if items is not None:
            for item in items:
                self.append(item)

    def __add__(self, other):
        new_instance = copy.copy(self)
        new_instance += other
        return new_instance

    def __radd__(self, other):
        return self + other

    def __iadd__(self, other):
        check_type('CheckedList add operand', other, Iterable,
                   self.expected_type)
        for item in other:
            self.append(item)
        return self

    def append(self, item):
        """Append item to list

        Parameters
        ----------
        item : object
            Item to append

        """
        check_type(self.name, item, self.expected_type)
        super().append(item)

    def insert(self, index, item):
        """Insert item before index

        Parameters
        ----------
        index : int
            Index in list
        item : object
            Item to insert

        """
        check_type(self.name, item, self.expected_type)
        super().insert(index, item)
Loading