"""
Definition of geometrical transformations.
"""
# Standard library modules.
import os
import math
# Third party modules.
# Local modules.
from pypenelopetools.pengeom.base import GeometryBase, LINE_SEPARATOR
# Globals and constants variables.
[docs]class Rotation(GeometryBase):
"""
Represents a rotation using 3 Euler angles (YZY).
Args:
omega_deg (float): Rotation around the z-axis (deg).
theta_deg (float): Rotation around the y-axis (deg).
phi_deg (float): Rotation around the new z-axis (deg).
"""
def __init__(self, omega_deg=0.0, theta_deg=0.0, phi_deg=0.0):
self.omega_deg = omega_deg
self.theta_deg = theta_deg
self.phi_deg = phi_deg
def __repr__(self):
return "<Rotation(omega={0:g} deg, theta={1:g} deg, phi={2:g} deg)>" \
.format(self.omega_deg, self.theta_deg, self.phi_deg)
def __str__(self):
return '(omega={0:g} deg, theta={1:g} deg, phi={2:g} deg)' \
.format(self.omega_deg, self.theta_deg, self.phi_deg)
def _read(self, fileobj, material_lookup, surface_lookup, module_lookup):
line = self._peek_next_line(fileobj)
while line != LINE_SEPARATOR:
keyword, value, termination = self._parse_expline(line)
if termination.startswith('RAD'):
value = math.degrees(value)
if keyword == 'OMEGA=':
self.omega_deg = value
elif keyword == 'THETA=':
self.theta_deg = value
elif keyword == 'PHI=':
self.phi_deg = value
line = self._read_next_line(fileobj)
def _write(self, fileobj, index_lookup):
line = self._create_expline('OMEGA=', self.omega_deg, ' DEG (DEFAULT=0.0)')
fileobj.write(line + os.linesep)
line = self._create_expline('THETA=', self.theta_deg, ' DEG (DEFAULT=0.0)')
fileobj.write(line + os.linesep)
line = self._create_expline('PHI=', self.phi_deg, ' DEG (DEFAULT=0.0)')
fileobj.write(line + os.linesep)
@property
def omega_deg(self):
"""float: Rotation around the z-axis (deg).
The value must be between 0 and 360.
"""
return self._omega
@omega_deg.setter
def omega_deg(self, angle):
if angle < 0 or angle > 360.0:
raise ValueError("Angle ({0}) must be between [0,360].".format(angle))
self._omega = angle
@property
def theta_deg(self):
"""float: Rotation around the y-axis (deg).
The value must be between 0 and 360.
"""
return self._theta
@theta_deg.setter
def theta_deg(self, angle):
if angle < 0 or angle > 360:
raise ValueError("Angle ({0}) must be between [0,360].".format(angle))
self._theta = angle
@property
def phi_deg(self):
"""float: Rotation around the new z-axis (deg).
The new z-axis refer to the axis after the omega and theta rotation were
applied on the original coordinate system.
The value must be between 0 and 360.
"""
return self._phi
@phi_deg.setter
def phi_deg(self, angle):
if angle < 0 or angle > 360:
raise ValueError("Angle ({0}) must be between [0,360].".format(angle))
self._phi = angle
[docs]class Shift(GeometryBase):
"""
Represents a translation in space.
Args:
x_cm (float): Translation along the x direction (cm).
y_cm (float): Translation along the y direction (cm).
z_cm (float): Translation along the z direction (cm).
"""
def __init__(self, x_cm=0.0, y_cm=0.0, z_cm=0.0):
self.x_cm = x_cm
self.y_cm = y_cm
self.z_cm = z_cm
def __repr__(self):
return "<Shift(x={0:g} cm, y={1:g} cm, z={2:g} cm)>" \
.format(self.x_cm, self.y_cm, self.z_cm)
def __str__(self):
return "(x={0:g} cm, y={1:g} cm, z={2:g} cm)" \
.format(self.x_cm, self.y_cm, self.z_cm)
def _read(self, fileobj, material_lookup, surface_lookup, module_lookup):
line = self._peek_next_line(fileobj)
while line != LINE_SEPARATOR:
keyword, value, _ = self._parse_expline(line)
if keyword == 'X-SHIFT=':
self.x_cm = value
elif keyword == 'Y-SHIFT=':
self.y_cm = value
elif keyword == 'Z-SHIFT=':
self.z_cm = value
line = self._read_next_line(fileobj)
def _write(self, fileobj, index_lookup):
line = self._create_expline('X-SHIFT=', self.x_cm, ' (DEFAULT=0.0)')
fileobj.write(line + os.linesep)
line = self._create_expline('Y-SHIFT=', self.y_cm, ' (DEFAULT=0.0)')
fileobj.write(line + os.linesep)
line = self._create_expline('Z-SHIFT=', self.z_cm, ' (DEFAULT=0.0)')
fileobj.write(line + os.linesep)
@property
def x_cm(self):
"""float: Translation along the x direction (cm)."""
return self._x
@x_cm.setter
def x_cm(self, shift):
self._x = shift
@property
def y_cm(self):
"""float: Translation along the y direction (cm)."""
return self._y
@y_cm.setter
def y_cm(self, shift):
self._y = shift
@property
def z_cm(self):
"""float: Translation along the z direction (cm)."""
return self._z
@z_cm.setter
def z_cm(self, shift):
self._z = shift
[docs]class Scale(GeometryBase):
"""
Represents scaling.
Args:
x (float): Scaling along the x direction.
y (float): Scaling along the y direction.
z (float): Scaling along the z direction.
"""
def __init__(self, x=1.0, y=1.0, z=1.0):
self.x = x
self.y = y
self.z = z
def __repr__(self):
return "<Shift(x={0:g}, y={1:g}, z={2:g})>" \
.format(self.x, self.y, self.z)
def __str__(self):
return "(x={0:g}, y={1:g}, z={2:g})".format(self.x, self.y, self.z)
def _read(self, fileobj, material_lookup, surface_lookup, module_lookup):
line = self._peek_next_line(fileobj)
while line != LINE_SEPARATOR:
keyword, value, _ = self._parse_expline(line)
if keyword == 'X-SCALE=':
self.x = value
elif keyword == 'Y-SCALE=':
self.y = value
elif keyword == 'Z-SCALE=':
self.z = value
line = self._read_next_line(fileobj)
def _write(self, fileobj, index_lookup):
line = self._create_expline('X-SCALE=', self.x, ' (DEFAULT=1.0)')
fileobj.write(line + os.linesep)
line = self._create_expline('Y-SCALE=', self.y, ' (DEFAULT=1.0)')
fileobj.write(line + os.linesep)
line = self._create_expline('Z-SCALE=', self.z, ' (DEFAULT=1.0)')
fileobj.write(line + os.linesep)
@property
def x(self):
"""float: Scaling along the x direction.
The value cannot be 0.
"""
return self._x
@x.setter
def x(self, scale):
if scale == 0.0:
raise ValueError("X scale cannot be equal to 0.")
self._x = scale
@property
def y(self):
"""float: Scaling along the y direction.
The value cannot be 0.
"""
return self._y
@y.setter
def y(self, scale):
if scale == 0.0:
raise ValueError("Y scale cannot be equal to 0.")
self._y = scale
@property
def z(self):
"""float: Scaling along the z direction.
The value cannot be 0.
"""
return self._z
@z.setter
def z(self, scale):
if scale == 0.0:
raise ValueError("Z scale cannot be equal to 0.")
self._z = scale