<commit_before>import copy
import os
import itertools
from xml.etree import ElementTree as ET

import numpy as np

from openmc import Mesh, Filter, Trigger, Nuclide
from openmc.summary import Summary
from openmc.clean_xml import *
from openmc.checkvalue import *


# "Static" variable for auto-generated Tally IDs
AUTO_TALLY_ID = 10000


def reset_auto_tally_id():
    global AUTO_TALLY_ID
    AUTO_TALLY_ID = 10000


class Tally(object):

    def __init__(self, tally_id=None, name=''):

        # Initialize Tally class attributes
        self.id = tally_id
        self.name = name
        self._filters = []
        self._nuclides = []
        self._scores = []
        self._estimator = None
        self._triggers = []

        self._num_score_bins = 0
        self._num_realizations = 0
        self._with_summary = False

        self._sum = None
        self._sum_sq = None
        self._mean = None
        self._std_dev = None


    def __deepcopy__(self, memo):

        existing = memo.get(id(self))

        # If this is the first time we have tried to copy this object, create a copy
        if existing is None:

            clone = type(self).__new__(type(self))
            clone.id = self.id
            clone.name = self.name
            clone.estimator = self.estimator
            clone.num_score_bins = self.num_score_bins
            clone.num_realizations = self.num_realizations
            clone._sum = copy.deepcopy(self.sum, memo)
            clone._sum_sq = copy.deepcopy(self.sum_sq, memo)
            clone._mean = copy.deepcopy(self.mean, memo)
            clone._std_dev = copy.deepcopy(self.std_dev, memo)

            clone.filters = []
            for filter in self.filters:
              clone.add_filter(copy.deepcopy(filter, memo))

            clone.nuclides = []
            for nuclide in self.nuclides:
              clone.add_nuclide(copy.deepcopy(nuclide, memo))

            clone.scores = []
            for score in self.scores:
              clone.add_score(score)

            clone.triggers = []
            for trigger in self.triggers:
              clone.add_trigger(trigger)

            memo[id(self)] = clone

            return clone

        # If this object has been copied before, return the first copy made
        else:
            return existing


    def __eq__(self, tally2):

        # Check all filters
        if len(self.filters) != len(tally2.filters):
            return False

        for filter in self.filters:
            if not filter in tally2.filters:
                return False

        # Check all nuclides
        if len(self.nuclides) != len(tally2.nuclides):
            return False

        for nuclide in self.nuclides:
            if not nuclide in tally2.nuclides:
                return False

        # Check all scores
        if len(self.scores) != len(tally2.scores):
            return False

        for score in self.scores:
            if not score in tally2.scores:
                return False

        if self.estimator != tally2.estimator:
            return False

        return True


    def __hash__(self):
        hashable = []

        for filter in self.filters:
            hashable.append((filter.type, tuple(filter.bins)))

        for nuclide in self.nuclides:
            hashable.append(nuclide.name)

        for score in self.scores:
            hashable.append(score)

        hashable.append(self.estimator)
        hashable.append(self.name)

        return hash(tuple(hashable))


    def __add__(self, other):

        # FIXME: Error checking: must check that results has been
        # set and that # bins is the same

        new_tally = Tally()
        new_tally._mean = self._mean + other._mean
        new_tally._std_dev = np.sqrt(self.std_dev**2 + other.std_dev**2)


    @property
    def id(self):
        return self._id


    @property
    def name(self):
        return self._name


    @property
    def filters(self):
        return self._filters<commit_msg>Added initial Tally arithmetic addition operation<commit_after>import copy
import os
import itertools
from xml.etree import ElementTree as ET

import numpy as np

from openmc import Mesh, Filter, Trigger, Nuclide
from openmc.summary import Summary
from openmc.clean_xml import *
from openmc.checkvalue import *


# "Static" variable for auto-generated Tally IDs
AUTO_TALLY_ID = 10000


def reset_auto_tally_id():
    global AUTO_TALLY_ID
    AUTO_TALLY_ID = 10000


class Tally(object):

    def __init__(self, tally_id=None, name=''):

        # Initialize Tally class attributes
        self.id = tally_id
        self.name = name
        self._filters = []
        self._nuclides = []
        self._scores = []
        self._estimator = None
        self._triggers = []

        self._num_score_bins = 0
        self._num_realizations = 0
        self._with_summary = False

        self._sum = None
        self._sum_sq = None
        self._mean = None
        self._std_dev = None


    def _align_tally_data(self, other):

            self_mean = copy.deepcopy(self.mean)
            self_std_dev = copy.deepcopy(self.std_dev)
            other_mean = copy.deepcopy(other.mean)
            other_std_dev = copy.deepcopy(other.std_dev)

            if self.filters != other.filters:

                # Determine the number of paired combinations of filter bins
                # between the two tallies and repeat arrays along filter axes
                self_num_filter_bins = self.mean.shape[0]
                other_num_filter_bins = other.mean.shape[0]
                num_filter_bins = self_num_filter_bins * other_num_filter_bins
                self_new_bins = max(num_filter_bins - self_num_filter_bins, 1)
                other_new_bins = max(num_filter_bins - other_num_filter_bins, 1)

                # Replicate the data
                self_mean = np.repeat(self_mean, self_new_bins, axis=0)
                other_mean = np.repeat(other_mean, other_new_bins, axis=0)
                self_std_dev = np.repeat(self_std_dev, self_new_bins, axis=0)
                other_std_dev = np.repeat(other_std_dev, other_new_bins, axis=0)

            if self.nuclides != other.nuclides:

                # Determine the number of paired combinations of nuclides
                # between the two tallies and repeat arrays along nuclide axes
                self_num_nuclide_bins = self.mean.shape[1]
                other_num_nuclide_bins = other.mean.shape[1]
                num_nuclide_bins = self_num_nuclide_bins * other_num_nuclide_bins
                self_new_bins = max(num_nuclide_bins - self_num_nuclide_bins, 1)
                other_new_bins = max(num_nuclide_bins - other_num_nuclide_bins, 1)

                # Replicate the data
                self_mean = np.repeat(self_mean, self_new_bins, axis=1)
                other_mean = np.repeat(other_mean, other_new_bins, axis=1)
                self_std_dev = np.repeat(self_std_dev, self_new_bins, axis=1)
                other_std_dev = np.repeat(other_std_dev, other_new_bins, axis=1)

            if self.scores != other.scores:

                # Determine the number of paired combinations of score bins
                # between the two tallies and repeat arrays along score axes
                self_num_score_bins = self.mean.shape[2]
                other_num_score_bins = other.mean.shape[2]
                num_score_bins = self_num_score_bins * other_num_score_bins
                self_new_bins = max(num_score_bins - self_num_score_bins, 1)
                other_new_bins = max(num_score_bins - other_num_score_bins, 1)

                # Replicate the data
                self_mean = np.repeat(self_mean, self_new_bins, axis=2)
                other_mean = np.repeat(other_mean, other_new_bins, axis=2)
                self_std_dev = np.repeat(self_std_dev, self_new_bins, axis=2)
                other_std_dev = np.repeat(other_std_dev, other_new_bins, axis=2)

            data = {}
            data['self'] = {}
            data['other'] = {}
            data['self']['mean'] = self_mean
            data['other']['mean'] = other_mean
            data['self']['std. dev.'] = self_std_dev
            data['other']['std. dev.'] = other_std_dev
            return data


    def __add__(self, other):

        # FIXME: Check that results have been read
        if not (self.mean and other.mean):
            msg = 'Unable to add Tally ID={0} to {1} since they do not ' \
                  'contain any results.'.format(self.id, other.id)
            raise ValueError(msg)

        new_tally = Tally(name='derived')

        if isinstance(other, Tally):

            # FIXME: Need new CrossFilter class
            # FIXME: Need way to cross-product Nuclides
            # FIXME: Need cross-product scores - just mix/match strings?

            data = self._align_tally_data(other)

            new_tally._mean = data['self']['mean'] + data['other']['mean']
            new_tally._std_dev = np.sqrt(data['self']['std. dev.']**2 + \
                                         data['other']['std. dev.']**2)

            if self.estimator == other.estimator:
                new_tally.estimator = self.estimator

            if self.with_summary and other.with_summary:
                new_tally.with_summary = self.with_summary

            if self.num_realizations == other.num_realizations:
                new_tally.num_realizations = self.num_realizations

        elif is_integer(other) or is_float(other):

            new_tally._mean = self._mean + other
            new_tally._std_dev = self._std_dev

            new_tally.estimator = self.estimator
            new_tally.with_summary = self.with_summary
            new_tally.num_realization = self.num_realizations
            new_tally.num_score_bins = self.num_score_bins

            for score in self.scores:
                new_tally.add_score(score)

        else:
            msg = 'Unable to add {0} to Tally ID={1}'.format(other, self.id)
            raise ValueError(msg)

        return new_tally


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

    '''
    def __sub__(self, other):

    def __rsub__(self, other):

    def __mul__(self, other):

    def __rmul__(self, other):

    def __div__(self, other):

    def __rdiv__(self, other):

    def __pow__(self, power):

    def sum(self, axis=None):
    '''


    def __deepcopy__(self, memo):

        existing = memo.get(id(self))

        # If this is the first time we have tried to copy this object, create a copy
        if existing is None:

            clone = type(self).__new__(type(self))
            clone.id = self.id
            clone.name = self.name
            clone.estimator = self.estimator
            clone.num_score_bins = self.num_score_bins
            clone.num_realizations = self.num_realizations
            clone._sum = copy.deepcopy(self.sum, memo)
            clone._sum_sq = copy.deepcopy(self.sum_sq, memo)
            clone._mean = copy.deepcopy(self.mean, memo)
            clone._std_dev = copy.deepcopy(self.std_dev, memo)

            clone.filters = []
            for filter in self.filters:
              clone.add_filter(copy.deepcopy(filter, memo))

            clone.nuclides = []
            for nuclide in self.nuclides:
              clone.add_nuclide(copy.deepcopy(nuclide, memo))

            clone.scores = []
            for score in self.scores:
              clone.add_score(score)

            clone.triggers = []
            for trigger in self.triggers:
              clone.add_trigger(trigger)

            memo[id(self)] = clone

            return clone

        # If this object has been copied before, return the first copy made
        else:
            return existing


    def __eq__(self, tally2):

        # Check all filters
        if len(self.filters) != len(tally2.filters):
            return False

        for filter in self.filters:
            if not filter in tally2.filters:
                return False

        # Check all nuclides
        if len(self.nuclides) != len(tally2.nuclides):
            return False

        for nuclide in self.nuclides:
            if not nuclide in tally2.nuclides:
                return False

        # Check all scores
        if len(self.scores) != len(tally2.scores):
            return False

        for score in self.scores:
            if not score in tally2.scores:
                return False

        if self.estimator != tally2.estimator:
            return False

        return True


    def __hash__(self):
        hashable = []

        for filter in self.filters:
            hashable.append((filter.type, tuple(filter.bins)))

        for nuclide in self.nuclides:
            hashable.append(nuclide.name)

        for score in self.scores:
            hashable.append(score)

        hashable.append(self.estimator)
        hashable.append(self.name)

        return hash(tuple(hashable))


    @property
    def id(self):
        return self._id


    @property
    def name(self):
        return self._name


    @property
    def filters(self):
        return self._filters
{'fraction_non_alphanumeric': 0.08275599543205177, 'fraction_numerical': 0.005481537875904073, 'mean_word_length': 1.8927548998018058, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 4}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2105608', 'n_tokens_mistral': 4059, 'n_tokens_neox': 3752, 'n_words': 932}