From 8e4422ae1fb24355b4298bdf57e5a92ff5094b65 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 10 May 2016 10:36:43 -0500 Subject: [PATCH 1/7] Add HexLattice.show_indices staticmethod and clarify universes description --- openmc/lattice.py | 125 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 118 insertions(+), 7 deletions(-) diff --git a/openmc/lattice.py b/openmc/lattice.py index f78ec8e909..af6c14a6a9 100644 --- a/openmc/lattice.py +++ b/openmc/lattice.py @@ -30,8 +30,8 @@ class Lattice(object): Unique identifier for the lattice name : str Name of the lattice - pitch : float - Pitch of the lattice in cm + pitch : Iterable of float + Pitch of the lattice in each direction in cm outer : openmc.Universe A universe to fill all space outside the lattice universes : Iterable of Iterable of openmc.Universe @@ -259,8 +259,9 @@ class RectLattice(Lattice): lower_left : Iterable of float The coordinates of the lower-left corner of the lattice. If the lattice is two-dimensional, only the x- and y-coordinates are specified. - pitch : float - Pitch of the lattice in cm + pitch : Iterable of float + Pitch of the lattice in the x, y, and (if applicable) z directions in + cm. outer : openmc.Universe A universe to fill all space outside the lattice universes : Iterable of Iterable of openmc.Universe @@ -512,13 +513,19 @@ class HexLattice(Lattice): center : Iterable of float Coordinates of the center of the lattice. If the lattice does not have axial sections then only the x- and y-coordinates are specified - pitch : float - Pitch of the lattice in cm + pitch : Iterable of float + Pitch of the lattice in cm. The first item in the iterable specifies the + pitch in the radial direction and, if the lattice is 3D, the second item + in the iterable specifies the pitch in the axial direction. outer : openmc.Universe A universe to fill all space outside the lattice universes : Iterable of Iterable of openmc.Universe A two- or three-dimensional list/array of universes filling each element - of the lattice + of the lattice. Each sub-list corresponds to one ring of universes and + should be ordered from outermost ring to innermost ring. The universes + within each sub-list are ordered from the "top" and proceed in a + clockwise fashion. The :meth:`HexLattice.show_indices` method can be + used to help figure out indices for this property. """ @@ -882,3 +889,107 @@ class HexLattice(Lattice): # Join the rows together and return the string. universe_ids = '\n'.join(rows) return universe_ids + + @staticmethod + def show_indices(num_rings): + """Return a diagram of the hexagonal lattice layout with indices. + + This method can be used to show the proper indices to be used when + setting the :attr:`HexLattice.universes` property. For example, running + this method with num_rings=3 will return the following diagram:: + + (0, 0) + (0,11) (0, 1) + (0,10) (1, 0) (0, 2) + (1, 5) (1, 1) + (0, 9) (2, 0) (0, 3) + (1, 4) (1, 2) + (0, 8) (1, 3) (0, 4) + (0, 7) (0, 5) + (0, 6) + + Parameters + ---------- + num_rings : int + Number of rings in the hexagonal lattice + + Returns + ------- + str + Diagram of the hexagonal lattice showing indices + + """ + + # Find the largest string and count the number of digits so we can + # properly pad the output string later + largest_index = 6*(num_rings - 1) + n_digits_index = len(str(largest_index)) + n_digits_ring = len(str(num_rings - 1)) + str_form = '({{:{}}},{{:{}}})'.format(n_digits_ring, n_digits_index) + pad = ' '*(n_digits_index + n_digits_ring + 3) + + # Initialize the list for each row. + rows = [[] for i in range(1 + 4 * (num_rings-1))] + middle = 2 * (num_rings - 1) + + # Start with the degenerate first ring. + rows[middle] = [str_form.format(num_rings - 1, 0)] + + # Add universes one ring at a time. + for r in range(1, num_rings): + # r_prime increments down while r increments up. + r_prime = num_rings - 1 - r + theta = 0 + y = middle + 2*r + + for i in range(r): + # Climb down the top-right. + rows[y].append(str_form.format(r_prime, theta)) + y -= 1 + theta += 1 + + for i in range(r): + # Climb down the right. + rows[y].append(str_form.format(r_prime, theta)) + y -= 2 + theta += 1 + + for i in range(r): + # Climb down the bottom-right. + rows[y].append(str_form.format(r_prime, theta)) + y -= 1 + theta += 1 + + for i in range(r): + # Climb up the bottom-left. + rows[y].insert(0, str_form.format(r_prime, theta)) + y += 1 + theta += 1 + + for i in range(r): + # Climb up the left. + rows[y].insert(0, str_form.format(r_prime, theta)) + y += 2 + theta += 1 + + for i in range(r): + # Climb up the top-left. + rows[y].insert(0, str_form.format(r_prime, theta)) + y += 1 + theta += 1 + + # Flip the rows and join each row into a single string. + rows = [pad.join(x) for x in rows[::-1]] + + # Pad the beginning of the rows so they line up properly. + for y in range(num_rings - 1): + rows[y] = (num_rings - 1 - y)*pad + rows[y] + rows[-1 - y] = (num_rings - 1 - y)*pad + rows[-1 - y] + + for y in range(num_rings % 2, num_rings, 2): + rows[middle + y] = pad + rows[middle + y] + if y != 0: + rows[middle - y] = pad + rows[middle - y] + + # Join the rows together and return the string. + return '\n'.join(rows) From 17c96c497c33fd19309d4ec1f7dee52d7bd83b3a Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 10 May 2016 10:37:07 -0500 Subject: [PATCH 2/7] Add xyz default for openmc.stats.Point constructor --- openmc/stats/multivariate.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openmc/stats/multivariate.py b/openmc/stats/multivariate.py index 4ce34a0712..e4eadd7aa4 100644 --- a/openmc/stats/multivariate.py +++ b/openmc/stats/multivariate.py @@ -328,8 +328,8 @@ class Point(Spatial): Parameters ---------- - xyz : Iterable of float - Cartesian coordinates of location + xyz : Iterable of float, optional + Cartesian coordinates of location. Defaults to (0., 0., 0.). Attributes ---------- @@ -338,7 +338,7 @@ class Point(Spatial): """ - def __init__(self, xyz): + def __init__(self, xyz=(0., 0., 0.)): super(Point, self).__init__() self.xyz = xyz From 8ff23e9f9808a0efbf990be4bb2355dd111f2220 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 10 May 2016 10:37:40 -0500 Subject: [PATCH 3/7] Clarify documentation regarding cell rotation --- docs/source/usersguide/input.rst | 14 ++++++++++++++ openmc/cell.py | 21 +++++++++++++++++---- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/docs/source/usersguide/input.rst b/docs/source/usersguide/input.rst index 775407d70b..8493ac4805 100644 --- a/docs/source/usersguide/input.rst +++ b/docs/source/usersguide/input.rst @@ -1033,6 +1033,20 @@ Each ```` element can have the following attributes or sub-elements: + The rotation applied is an intrinsic rotation whose Tait-Bryan angles are + given as those specified about the x, y, and z axes respectively. That is to + say, if the angles are :math:`(\phi, \theta, \psi)`, then the rotation + matrix applied is :math:`R_z(\psi) R_y(\theta) R_x(\phi)` or + + .. math:: + + \left [ \begin{array}{ccc} \cos\theta \cos\psi & -\cos\theta \sin\psi + + \sin\phi \sin\theta \cos\psi & \sin\phi \sin\psi + \cos\phi \sin\theta + \cos\psi \\ \cos\theta \sin\psi & \cos\phi \cos\psi + \sin\phi \sin\theta + \sin\psi & -\sin\phi \cos\psi + \cos\phi \sin\theta \sin\psi \\ + -\sin\theta & \sin\phi \cos\theta & \cos\phi \cos\theta \end{array} + \right ] + *Default*: None :translation: diff --git a/openmc/cell.py b/openmc/cell.py index 37828d8fce..806f3f32ff 100644 --- a/openmc/cell.py +++ b/openmc/cell.py @@ -23,7 +23,7 @@ def reset_auto_cell_id(): class Cell(object): - """A region of space defined as the intersection of half-space created by + r"""A region of space defined as the intersection of half-space created by quadric surfaces. Parameters @@ -48,11 +48,24 @@ class Cell(object): Indicates what the region of space is filled with region : openmc.Region Region of space that is assigned to the cell. - rotation : numpy.ndarray + rotation : Iterable of float If the cell is filled with a universe, this array specifies the angles in degrees about the x, y, and z axes that the filled universe should be - rotated. - translation : numpy.ndarray + rotated. The rotation applied is an intrinsic rotation with specified + Tait-Bryan angles. That is to say, if the angles are :math:`(\phi, + \theta, \psi)`, then the rotation matrix applied is :math:`R_z(\psi) + R_y(\theta) R_x(\phi)` or + + .. math:: + + \left [ \begin{array}{ccc} \cos\theta \cos\psi & -\cos\theta \sin\psi + + \sin\phi \sin\theta \cos\psi & \sin\phi \sin\psi + \cos\phi + \sin\theta \cos\psi \\ \cos\theta \sin\psi & \cos\phi \cos\psi + + \sin\phi \sin\theta \sin\psi & -\sin\phi \cos\psi + \cos\phi + \sin\theta \sin\psi \\ -\sin\theta & \sin\phi \cos\theta & \cos\phi + \cos\theta \end{array} \right ] + + translation : Iterable of float If the cell is filled with a universe, this array specifies a vector that is used to translate (shift) the universe. offsets : ndarray From 962d592d2e262206dcde01207100e14cf26a91b0 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 10 May 2016 11:21:48 -0500 Subject: [PATCH 4/7] Restrict the Cell.rotation property to cells filled with a Universe --- openmc/cell.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/openmc/cell.py b/openmc/cell.py index 806f3f32ff..8ddae63716 100644 --- a/openmc/cell.py +++ b/openmc/cell.py @@ -243,6 +243,10 @@ class Cell(object): @rotation.setter def rotation(self, rotation): + if not isinstance(self.fill, openmc.Universe): + raise RuntimeError('Cell rotation can only be applied if the cell ' + 'is filled with a Universe') + cv.check_type('cell rotation', rotation, Iterable, Real) cv.check_length('cell rotation', rotation, 3) self._rotation = rotation From 10f498177e5528001b9e4b8a3c93b4ad0c5533d3 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 10 May 2016 11:55:38 -0500 Subject: [PATCH 5/7] Allow rotation to be set without fill assigned when reading summary file --- openmc/summary.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openmc/summary.py b/openmc/summary.py index 34c51bc51e..c9aa08f152 100644 --- a/openmc/summary.py +++ b/openmc/summary.py @@ -267,7 +267,7 @@ class Summary(object): rotation = \ self._f['geometry/cells'][key]['rotation'][...] rotation = np.asarray(rotation, dtype=np.int) - cell.rotation = rotation + cell._rotation = rotation # Store Cell fill information for after Universe/Lattice creation self._cell_fills[index] = (fill_type, fill) From 32eb58774df061c2198814ab99be6db7058288f6 Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Tue, 10 May 2016 22:08:10 -0500 Subject: [PATCH 6/7] Add make_hexagon_region() function --- docs/source/pythonapi/index.rst | 10 ++++++++ openmc/surface.py | 45 ++++++++++++++++++++++++++++++++- 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/docs/source/pythonapi/index.rst b/docs/source/pythonapi/index.rst index 1631976e67..89d1b0508b 100644 --- a/docs/source/pythonapi/index.rst +++ b/docs/source/pythonapi/index.rst @@ -122,6 +122,16 @@ Many of the above classes are derived from several abstract classes: openmc.Region openmc.Lattice +One function is also available to create a hexagonal region defined by the +intersection of six surface half-spaces. + +.. autosummary:: + :toctree: generated + :nosignatures: + :template: myfunction.rst + + openmc.make_hexagon_region + Constructing Tallies -------------------- diff --git a/openmc/surface.py b/openmc/surface.py index 37e7c2ffdb..84028c1afa 100644 --- a/openmc/surface.py +++ b/openmc/surface.py @@ -2,11 +2,12 @@ from abc import ABCMeta from numbers import Real, Integral from xml.etree import ElementTree as ET import sys +from math import sqrt import numpy as np from openmc.checkvalue import check_type, check_value, check_greater_than -from openmc.region import Region +from openmc.region import Region, Intersection if sys.version_info[0] >= 3: basestring = str @@ -1503,3 +1504,45 @@ class Halfspace(Region): def __str__(self): return '-' + str(self.surface.id) if self.side == '-' \ else str(self.surface.id) + + +def make_hexagon_region(edge_length=1., orientation='y'): + """Create a hexagon region from six surface planes. + + Parameters + ---------- + edge_length : float + Length of a side of the hexagon in cm + orientation : {'x', 'y'} + An 'x' orientation means that two sides of the hexagon are parallel to + the x-axis and a 'y' orientation means that two sides of the hexagon are + parallel to the y-axis. + + Returns + ------- + openmc.Region + The inside of a hexagonal prism + + """ + + l = edge_length + + if orientation == 'x': + right = XPlane(x0=sqrt(3.)/2.*l) + left = XPlane(x0=-sqrt(3.)/2.*l) + c = sqrt(3.)/3. + ur = Plane(A=c, B=1., D=l) # y = -x/sqrt(3) + a + ul = Plane(A=-c, B=1., D=l) # y = x/sqrt(3) + a + lr = Plane(A=-c, B=1., D=-l) # y = x/sqrt(3) - a + ll = Plane(A=c, B=1., D=-l) # y = -x/sqrt(3) - a + return Intersection(-right, +left, -ur, -ul, +lr, +ll) + + elif orientation == 'y': + top = YPlane(y0=sqrt(3.)/2.*l) + bottom = YPlane(y0=-sqrt(3.)/2.*l) + c = sqrt(3.) + ur = Plane(A=c, B=1., D=c*l) # y = -sqrt(3)*(x - a) + lr = Plane(A=-c, B=1., D=-c*l) # y = sqrt(3)*(x + a) + ll = Plane(A=c, B=1., D=-c*l) # y = -sqrt(3)*(x + a) + ul = Plane(A=-c, B=1., D=c*l) # y = sqrt(3)*(x + a) + return Intersection(-top, +bottom, -ur, +lr, +ll, -ul) From 0cabfec5e7279d6a449b1a71da063d710ce200ef Mon Sep 17 00:00:00 2001 From: Paul Romano Date: Wed, 11 May 2016 09:46:58 -0500 Subject: [PATCH 7/7] A little error checking on Element.name --- openmc/element.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/openmc/element.py b/openmc/element.py index 39564add4a..66371aba9a 100644 --- a/openmc/element.py +++ b/openmc/element.py @@ -1,7 +1,7 @@ import sys import openmc -from openmc.checkvalue import check_type +from openmc.checkvalue import check_type, check_length from openmc.data import natural_abundance if sys.version_info[0] >= 3: @@ -99,7 +99,8 @@ class Element(object): @name.setter def name(self, name): - check_type('name', name, basestring) + check_type('element name', name, basestring) + check_length('element name', name, 1, 2) self._name = name @scattering.setter