/openmdao.lib/src/openmdao/lib/datatypes/domain/zone.py

https://github.com/thearn/OpenMDAO-Framework
Python | 278 lines | 271 code | 6 blank | 1 comment | 0 complexity | 8c25fa87d7a877c8ac58ebc75e3d6f9e MD5 | raw file
  1. import copy
  2. from openmdao.lib.datatypes.domain.flow import FlowSolution
  3. from openmdao.lib.datatypes.domain.grid import GridCoordinates
  4. CARTESIAN = 'Cartesian'
  5. CYLINDRICAL = 'Cylindrical'
  6. _COORD_SYSTEMS = (CARTESIAN, CYLINDRICAL)
  7. class Zone(object):
  8. """ One zone in a possibly multi-zone :class:`DomainObj`. """
  9. def __init__(self):
  10. self.grid_coordinates = GridCoordinates()
  11. self.flow_solution = FlowSolution()
  12. self.reference_state = None
  13. self._coordinate_system = CARTESIAN
  14. self.right_handed = True
  15. self.symmetry = None
  16. self.symmetry_axis = None
  17. self.symmetry_instances = 1
  18. @property
  19. def shape(self):
  20. """ Coordinate index limits, not including 'ghost/rind' planes. """
  21. return self.grid_coordinates.shape
  22. @property
  23. def extent(self):
  24. """ Coordinate ranges, not including 'ghost/rind' planes. """
  25. return self.grid_coordinates.extent
  26. def _get_coord_sys(self):
  27. return self._coordinate_system
  28. def _set_coord_sys(self, sys):
  29. if sys in _COORD_SYSTEMS:
  30. self._coordinate_system = sys
  31. else:
  32. raise ValueError('invalid coordinate system %r' % sys)
  33. coordinate_system = property(_get_coord_sys, _set_coord_sys,
  34. doc='Coordinate system in use.')
  35. def copy(self):
  36. """ Returns a deep copy of self. """
  37. return copy.deepcopy(self)
  38. def is_equivalent(self, other, logger, tolerance=0.):
  39. """
  40. Test if self and `other` are equivalent.
  41. other: :class:`Zone`
  42. Zone to check against.
  43. logger: :class:`Logger` or None
  44. Used to log debug messages that will indicate what if anything is
  45. not equivalent.
  46. tolerance: float
  47. The maximum relative difference in array values to be considered
  48. equivalent.
  49. """
  50. if not isinstance(other, Zone):
  51. logger.debug('other is not a Zone object.')
  52. return False
  53. if self.coordinate_system != other.coordinate_system:
  54. logger.debug('coordinate_systems are not equal.')
  55. return False
  56. if self.right_handed != other.right_handed:
  57. logger.debug('handedness is not equal.')
  58. return False
  59. if self.symmetry != other.symmetry:
  60. logger.debug('symmetry is not equal.')
  61. return False
  62. if self.symmetry_axis != other.symmetry_axis:
  63. logger.debug('symmetry_axis is not equal.')
  64. return False
  65. if self.symmetry_instances != other.symmetry_instances:
  66. logger.debug('symmetry_instances is not equal.')
  67. return False
  68. if not self.grid_coordinates.is_equivalent(other.grid_coordinates,
  69. logger, tolerance):
  70. return False
  71. if not self.flow_solution.is_equivalent(other.flow_solution, logger,
  72. tolerance):
  73. return False
  74. return True
  75. def extract(self, imin, imax, jmin=None, jmax=None, kmin=None, kmax=None,
  76. grid_ghosts=None, flow_ghosts=None):
  77. """
  78. Construct a new :class:`Zone` from grid and flow data extracted
  79. from the specified region. Symmetry data is copied.
  80. imin, imax, jmin, jmax, kmin, kmax: int
  81. Specifies the region to extract neglecting ghost/rind planes.
  82. Negative values are relative to the size in that dimension,
  83. so -1 refers to the last element. For 2D zones omit kmin and kmax.
  84. For 1D zones omit jmin, jmax, kmin, and kmax.
  85. grid_ghosts: int[]
  86. The number of ghost/rind planes for the new zone's grid.
  87. If ``None`` the grid's existing specification is used.
  88. flow_ghosts: int[]
  89. The number of ghost/rind planes for the new zone's flow solution.
  90. If ``None`` the flow's existing specification is used.
  91. """
  92. zone = Zone()
  93. zone.grid_coordinates = \
  94. self.grid_coordinates.extract(imin, imax, jmin, jmax, kmin, kmax,
  95. grid_ghosts)
  96. zone.flow_solution = \
  97. self.flow_solution.extract(imin, imax, jmin, jmax, kmin, kmax,
  98. flow_ghosts)
  99. if self.reference_state is not None:
  100. zone.reference_state = self.reference_state.copy()
  101. zone.coordinate_system = self.coordinate_system
  102. zone.right_handed = self.right_handed
  103. zone.symmetry = self.symmetry
  104. zone.symmetry_axis = self.symmetry_axis
  105. zone.symmetry_instances = self.symmetry_instances
  106. return zone
  107. def extend(self, axis, delta, grid_points, flow_points, normal=None):
  108. """
  109. Construct a new :class:`Zone` by linearly extending the grid and
  110. replicating the flow. Symmetry data is copied.
  111. axis: 'i', 'j', or 'k'
  112. Index axis to extend.
  113. delta: float.
  114. Fractional amount to move for each point. Multiplies the 'edge'
  115. delta in the `axis` direction or the appropriate component of
  116. `normal`. A negative value adds points before the current
  117. zero-index of `axis`.
  118. grid_points: int >= 0
  119. Number of points to add in `axis` dimension.
  120. flow_points: int >= 0
  121. Number of points to add in `axis` dimension.
  122. normal: float[]
  123. For cases where only a single point exists in the `axis` direction,
  124. this specifies the direction to move. If not specified, an
  125. axis-aligned direction is selected based on minimum grid extent.
  126. """
  127. zone = Zone()
  128. if grid_points > 0:
  129. zone.grid_coordinates = \
  130. self.grid_coordinates.extend(axis, delta, grid_points, normal)
  131. else:
  132. zone.grid_coordinates = self.grid_coordinates.copy()
  133. if flow_points > 0:
  134. zone.flow_solution = \
  135. self.flow_solution.extend(axis, delta, flow_points)
  136. else:
  137. zone.flow_solution = self.flow_solution.copy()
  138. if self.reference_state is not None:
  139. zone.reference_state = self.reference_state.copy()
  140. zone.coordinate_system = self.coordinate_system
  141. zone.right_handed = self.right_handed
  142. zone.symmetry = self.symmetry
  143. zone.symmetry_axis = self.symmetry_axis
  144. zone.symmetry_instances = self.symmetry_instances
  145. return zone
  146. def make_cartesian(self, axis='z'):
  147. """
  148. Convert to Cartesian coordinate system.
  149. axis: string
  150. Specifies which is the cylinder axis ('z' or 'x').
  151. """
  152. if self.coordinate_system != CARTESIAN:
  153. self.flow_solution.make_cartesian(self.grid_coordinates, axis)
  154. self.grid_coordinates.make_cartesian(axis)
  155. self.coordinate_system = CARTESIAN
  156. def make_cylindrical(self, axis='z'):
  157. """
  158. Convert to cylindrical coordinate system.
  159. axis: string
  160. Specifies which is the cylinder axis ('z' or 'x').
  161. """
  162. if self.coordinate_system != CYLINDRICAL:
  163. self.grid_coordinates.make_cylindrical(axis)
  164. self.flow_solution.make_cylindrical(self.grid_coordinates, axis)
  165. self.coordinate_system = CYLINDRICAL
  166. def make_left_handed(self):
  167. """ Convert to left-handed coordinate system. """
  168. if self.right_handed:
  169. self.grid_coordinates.flip_z()
  170. self.flow_solution.flip_z()
  171. self.right_handed = False
  172. def make_right_handed(self):
  173. """ Convert to right-handed coordinate system. """
  174. if not self.right_handed:
  175. self.grid_coordinates.flip_z()
  176. self.flow_solution.flip_z()
  177. self.right_handed = True
  178. def translate(self, delta_x, delta_y, delta_z):
  179. """
  180. Translate coordinates.
  181. delta_x, delta_y, delta_z: float
  182. Amount of translation along the corresponding axis.
  183. """
  184. if self.coordinate_system == CARTESIAN:
  185. self.grid_coordinates.translate(delta_x, delta_y, delta_z)
  186. else:
  187. raise RuntimeError('Zone not in cartesian coordinates')
  188. def rotate_about_x(self, deg):
  189. """
  190. Rotate about the X axis.
  191. deg: float (degrees)
  192. Amount of rotation.
  193. """
  194. if self.coordinate_system == CARTESIAN:
  195. self.grid_coordinates.rotate_about_x(deg)
  196. self.flow_solution.rotate_about_x(deg)
  197. else:
  198. raise RuntimeError('Zone not in cartesian coordinates')
  199. def rotate_about_y(self, deg):
  200. """
  201. Rotate about the Y axis.
  202. deg: float (degrees)
  203. Amount of rotation.
  204. """
  205. if self.coordinate_system == CARTESIAN:
  206. self.grid_coordinates.rotate_about_y(deg)
  207. self.flow_solution.rotate_about_y(deg)
  208. else:
  209. raise RuntimeError('Zone not in cartesian coordinates')
  210. def rotate_about_z(self, deg):
  211. """
  212. Rotate about the Z axis.
  213. deg: float (degrees)
  214. Amount of rotation.
  215. """
  216. if self.coordinate_system == CARTESIAN:
  217. self.grid_coordinates.rotate_about_z(deg)
  218. self.flow_solution.rotate_about_z(deg)
  219. else:
  220. raise RuntimeError('Zone not in cartesian coordinates')
  221. def promote(self):
  222. """ Promote from N-dimensional to N+1 dimensional index space. """
  223. self.grid_coordinates.promote()
  224. self.flow_solution.promote()
  225. def demote(self):
  226. """ Demote from N-dimensional to N-1 dimensional index space. """
  227. self.grid_coordinates.demote()
  228. self.flow_solution.demote()