"""
Definition of module.
"""
# Standard library modules.
import os
import enum
# Third party modules.
# Local modules.
from pypenelopetools.pengeom.transformation import Rotation, Shift
from pypenelopetools.pengeom.mixin import DescriptionMixin, ModuleMixin
from pypenelopetools.pengeom.base import GeometryBase, LINE_EXTRA, LINE_SEPARATOR
from pypenelopetools.material import VACUUM
# Globals and constants variables.
[docs]class SidePointer(enum.IntEnum):
"""
Whether the surface is pointing in the positive or negative direction.
"""
POSITIVE = 1
"""Positive direction."""
NEGATIVE = -1
"""Negative direction."""
[docs]class Module(DescriptionMixin, ModuleMixin, GeometryBase):
"""
Definition of a module.
Args:
material (:obj:`Material <pypenelopetools.material.Material>`, optional):
Material associated with this module.
If ``None``, the material is set to
:obj:`VACUUM <pypenelopetools.material.VACUUM>`.
description (str): Description of the module
"""
def __init__(self, material=None, description=''):
if material is None:
material = VACUUM
self.material = material
self.description = description
self._surfaces = {}
self._modules = set()
self._rotation = Rotation()
self._shift = Shift()
def __repr__(self):
return '<Module(description={0}, material={1}, surfaces_count={2:d}, modules_count={3:d}, rotation={4}, shift={5})>' \
.format(self.description, self.material, len(self._surfaces),
len(self._modules), str(self.rotation), str(self.shift))
def _read(self, fileobj, material_lookup, surface_lookup, module_lookup):
line = self._read_next_line(fileobj)
_, _, self.description = self._parse_line(line)
line = self._read_next_line(fileobj)
keyword, material_index, _ = self._parse_line(line)
if keyword != 'MATERIAL':
raise IOError('Expected keyword "MATERIAL" instead of "{0}"'.format(keyword))
material_index = int(material_index)
if material_index not in material_lookup:
raise IOError('No material {0} in lookup table'.format(material_index))
self.material = material_lookup[material_index]
line = self._read_next_line(fileobj)
while line != LINE_EXTRA and line != LINE_SEPARATOR:
keyword, index, termination = self._parse_line(line)
index = int(index)
if keyword == 'SURFACE':
pointer = int(termination[16:18])
surface = surface_lookup[index]
self.add_surface(surface, pointer)
elif keyword == 'MODULE':
submodule = module_lookup[index]
self.add_module(submodule)
else:
raise IOError('Unknown keyword: {0}'.format(keyword))
line = self._read_next_line(fileobj)
if line == LINE_EXTRA:
extra_offset = fileobj.tell()
self.rotation._read(fileobj, material_lookup, surface_lookup, module_lookup)
fileobj.seek(extra_offset)
self.shift._read(fileobj, material_lookup, surface_lookup, module_lookup)
def _write(self, fileobj, index_lookup):
index = index_lookup[self]
text = "{:4d}".format(index)
termination = " " + self.description
line = self._create_line('MODULE', text, termination)
fileobj.write(line + os.linesep)
# Material index
index = index_lookup[self.material]
text = "{:4d}".format(index)
line = self._create_line('MATERIAL', text)
fileobj.write(line + os.linesep)
# Surface pointers
surfaces = sorted((index_lookup[surface], surface, pointer)
for surface, pointer in self._surfaces.items())
for index, surface, pointer in surfaces:
text = "{:4d}".format(index)
termination = ", SIDE POINTER=({:2d})".format(pointer)
line = self._create_line('SURFACE', text, termination)
fileobj.write(line + os.linesep)
# Module indexes
modules = sorted((index_lookup[module], module)
for module in self.get_modules())
for index, module in modules:
text = "{:4d}".format(index)
line = self._create_line('MODULE', text)
fileobj.write(line + os.linesep)
# Separator
fileobj.write(LINE_EXTRA + os.linesep)
# Rotation
self.rotation._write(fileobj, index_lookup)
# Shift
self.shift._write(fileobj, index_lookup)
fileobj.write(LINE_SEPARATOR + os.linesep)
[docs] def add_surface(self, surface, pointer):
"""
Adds a surface.
Args:
surface (:obj:`SurfaceImplicit <pypenelopetools.pengeom.surface.SurfaceImplicit>` or :obj:`SurfaceReduced <pypenelopetools.pengeom.surface.SurfaceReduced>`):
Surface to add.
pointer (:obj:`SidePointer`):
Whether the surface is pointing in the positive or negative
direction.
"""
if isinstance(pointer, int):
if pointer == SidePointer.NEGATIVE:
pointer = SidePointer.NEGATIVE
elif pointer == SidePointer.POSITIVE:
pointer = SidePointer.POSITIVE
if pointer not in SidePointer:
raise ValueError("Pointer ({0}) must be either -1 or 1.".format(pointer))
if surface in self._surfaces:
raise ValueError("Module already contains this surface.")
self._surfaces[surface] = pointer
[docs] def pop_surface(self, surface):
"""
Removes a surface.
Args:
surface (:obj:`SurfaceImplicit <pypenelopetools.pengeom.surface.SurfaceImplicit>` or :obj:`SurfaceReduced <pypenelopetools.pengeom.surface.SurfaceReduced>`):
Surface to remove.
"""
self._surfaces.pop(surface)
[docs] def clear_surfaces(self):
"""
Clear all surfaces.
"""
self._surfaces.clear()
[docs] def get_surface_pointer(self, surface):
"""
Returns the surface pointer for the specified surface.
Args:
surface (:obj:`SurfaceImplicit <pypenelopetools.pengeom.surface.SurfaceImplicit>` or :obj:`SurfaceReduced <pypenelopetools.pengeom.surface.SurfaceReduced>`):
Surface of interest.
Returns:
:obj:`SidePointer`: Side pointer.
"""
return self._surfaces[surface]
[docs] def get_surfaces(self):
"""
Returns:
tuple: All surfaces.
"""
return tuple(self._surfaces.keys())
@property
def rotation(self):
""":obj:`Rotation <pypenelopetools.pengeom.transformation.Rotation>`: Rotation of the module."""
return self._rotation
@property
def shift(self):
""":obj:`Shift <pypenelopetools.pengeom.transformation.Shift>`: Shift/translation of the module."""
return self._shift