diff --git a/openmc/data/function.py b/openmc/data/function.py index e9ecf82c58..0515a57c0c 100644 --- a/openmc/data/function.py +++ b/openmc/data/function.py @@ -397,6 +397,67 @@ class Polynomial(np.polynomial.Polynomial, Function1D): return cls(dataset.value) +class Combination(EqualityMixin): + """Combination of multiple functions with a user-defined operator + + This class allows you to create a callable object which represents the + combination of other callable objects by way of a series of user-defined + operators connecting each of the callable objects. + + Parameters + ---------- + functions : Iterable of Callable + Functions to combine according to operations + operations : Iterable of numpy.ufunc + Operations to perform between functions; note that the standard order + of operations will not be followed, but can be simulated by + combinations of Combination objects. The operations parameter must have + a length one less than the number of functions. + + + Attributes + ---------- + functions : Iterable of Callable + Functions to combine according to operations + operations : Iterable of numpy.ufunc + Operations to perform between functions; note that the standard order + of operations will not be followed, but can be simulated by + combinations of Combination objects. The operations parameter must have + a length one less than the number of functions. + + """ + + def __init__(self, functions, operations): + self.functions = functions + self.operations = operations + + def __call__(self, x): + ans = self.functions[0](x) + for i, operation in enumerate(self.operations): + ans = operation(ans, self.functions[i + 1](x)) + return ans + + @property + def functions(self): + return self._functions + + @functions.setter + def functions(self, functions): + cv.check_type('functions', functions, Iterable, Callable) + self._functions = functions + + @property + def operations(self): + return self._operations + + @operations.setter + def operations(self, operations): + cv.check_type('operations', operations, Iterable, np.ufunc) + length = len(self.functions) - 1 + cv.check_length('operations', operations, length, length_max=length) + self._operations = operations + + class Sum(EqualityMixin): """Sum of multiple functions. @@ -432,6 +493,63 @@ class Sum(EqualityMixin): self._functions = functions +class Regions1D(EqualityMixin): + """Piecewise composition of multiple functions. + + This class allows you to create a callable object which is composed + of multiple other callable objects, each applying to a specific interval + + Parameters + ---------- + functions : Iterable of Callable + Functions which are to be combined in a piecewise fashion + breakpoints : Iterable of float + The values of the dependent variable that define the domain of + each function. The *i*th and *(i+1)*th values are the limits of the + domain of the *i*th function. Values must be monotonically increasing. + + Attributes + ---------- + functions : Iterable of Callable + Functions which are to be combined in a piecewise fashion + breakpoints : Iterable of float + The breakpoints between each function + + """ + + def __init__(self, functions, breakpoints): + self.functions = functions + self.breakpoints = breakpoints + + def __call__(self, x): + i = np.searchsorted(self.breakpoints, x) + if isinstance(x, Iterable): + ans = np.empty_like(x) + for j in range(len(i)): + ans[j] = self.functions[i[j]](x[j]) + return ans + else: + return self.functions[i](x) + + @property + def functions(self): + return self._functions + + @property + def breakpoints(self): + return self._breakpoints + + @functions.setter + def functions(self, functions): + cv.check_type('functions', functions, Iterable, Callable) + self._functions = functions + + @breakpoints.setter + def breakpoints(self, breakpoints): + cv.check_iterable_type('breakpoints', breakpoints, Real) + self._breakpoints = breakpoints + + class ResonancesWithBackground(EqualityMixin): """Cross section in resolved resonance region. diff --git a/openmc/data/library.py b/openmc/data/library.py index 0485af0641..9f6d931467 100644 --- a/openmc/data/library.py +++ b/openmc/data/library.py @@ -22,6 +22,21 @@ class DataLibrary(EqualityMixin): def __init__(self): self.libraries = [] + def get_by_material(self, value): + """Return the library dictionary containing a given material. + + Returns + ------- + library : dict or None + Dictionary summarizing cross section data from a single file; + the dictionary has keys 'path', 'type', and 'materials'. + + """ + for library in self.libraries: + if value in library['materials']: + return library + return None + def register_file(self, filename): """Register a file with the data library. @@ -78,3 +93,38 @@ class DataLibrary(EqualityMixin): tree = ET.ElementTree(root) tree.write(path, xml_declaration=True, encoding='utf-8', method='xml') + + @classmethod + def from_xml(cls, path): + """Read cross section data library from an XML file. + + Parameters + ---------- + path : str + Path to XML file to read. + + Returns + ------- + data : openmc.data.DataLibrary + Data library object initialized from the provided XML + + """ + + data = cls() + + tree = ET.parse(path) + root = tree.getroot() + if root.find('directory') is not None: + directory = root.find('directory').text + else: + directory = os.path.dirname(path) + + for lib_element in root.findall('library'): + filename = os.path.join(directory, lib_element.attrib['path']) + filetype = lib_element.attrib['type'] + materials = lib_element.attrib['materials'].split() + library = {'path': filename, 'type': filetype, + 'materials': materials} + data.libraries.append(library) + + return data