import copy
from openmdao.util.log import NullLogger
[docs]class DomainObj(object):
"""
A :class:`DomainObj` represents a (possibly multi-zoned) mesh and
data related to that mesh.
"""
def __init__(self):
self.reference_state = None
# Zones are kept in an explicit list to retain the order
# that they were added.
self.zones = []
@property
[docs] def shape(self):
""" List of coordinate index limits for each zone. """
return [zone.shape for zone in self.zones]
@property
[docs] def extent(self):
""" List of coordinate ranges for each zone. """
return [zone.extent for zone in self.zones]
[docs] def add_domain(self, other, prefix='', make_copy=False):
"""
Add zones from `other` to self, retaining names where possible.
other: :class:`DomainObj`
Source for new zone data.
prefix: string
String prepended to zone names.
make_copy: bool
If True, then a deep copy of each zone is made rather than just
referring to a shared instance.
"""
for zone in other.zones:
name = other.zone_name(zone)
if hasattr(self, name):
name = ''
self.add_zone(name, zone, prefix, make_copy)
[docs] def add_zone(self, name, zone, prefix='', make_copy=False):
"""
Add a :class:`Zone`. Returns the added zone.
name: string
Name for the zone. If None or blank, then a default of the form
``zone_N`` is used.
zone: :class:`Zone`
Zone to be added.
prefix: string
String prepended to the zone name.
make_copy: bool
If True, then a deep copy of `zone` is made rather than just
referring to a shared instance.
"""
if not name:
name = 'zone_%d' % (len(self.zones) + 1)
name = prefix+name
if hasattr(self, name):
raise ValueError('name %r is already bound' % name)
if make_copy:
zone = copy.deepcopy(zone)
setattr(self, name, zone)
self.zones.append(zone)
return zone
[docs] def remove_zone(self, zone):
"""
Remove a zone. Returns the removed zone.
zone: string or :class:`Zone`
Zone to be removed.
"""
if isinstance(zone, basestring):
name = zone
zone = getattr(self, zone)
else:
name = self.zone_name(zone)
delattr(self, name)
self.zones.remove(zone)
return zone
[docs] def rename_zone(self, name, zone):
""" Rename a zone.
name: string
New name for the zone.
zone: :class:`Zone`
Zone to be renamed.
"""
if hasattr(self, name):
raise ValueError('name %r is already bound' % name)
current = self.zone_name(zone)
delattr(self, current)
setattr(self, name, zone)
[docs] def zone_name(self, zone):
"""
Return name that a zone is bound to.
zone: :class:`Zone`
Zone whose name is to be returned.
"""
for name, value in self.__dict__.items():
if value is zone:
return name
raise ValueError('cannot find zone!')
[docs] def copy(self):
""" Returns a deep copy of self. """
return copy.deepcopy(self)
[docs] def deallocate(self):
""" Deallocate resources. """
for zone in self.zones:
delattr(self, self.zone_name(zone))
self.zones = []
[docs] def is_equivalent(self, other, logger=None, tolerance=0.):
"""
Test if self and `other` are equivalent.
other: :class:`DomainObj`
The domain to check against.
logger: Logger or None
Used to log debug messages that will indicate what, if anything,
is not equivalent.
tolerance: float
The maximum relative difference in array values to be considered
equivalent.
"""
logger = logger or NullLogger()
if not isinstance(other, DomainObj):
logger.debug('other is not a DomainObj object.')
return False
if len(self.zones) != len(other.zones):
logger.debug('zone count mismatch.')
return False
for zone in self.zones:
name = self.zone_name(zone)
try:
other_zone = getattr(other, name)
except AttributeError:
logger.debug('other is missing zone %r.', name)
return False
if not zone.is_equivalent(other_zone, logger, tolerance):
logger.debug('zone %r equivalence failed.', name)
return False
return True
[docs] def extract(self, zone_args):
"""
Construct a new :class:`DomainObj` from grid and flow data extracted
from the specified regions of each zone. Existing zone names are used
for the new domain's zones.
zone_args: sequence
Sequence of argument tuples to be used to extract data from each
zone. If an argument tuple is empty or ``None`` then that zone
is skipped.
"""
domain = DomainObj()
for i, args in enumerate(zone_args):
if args:
zone = self.zones[i]
name = self.zone_name(zone)
domain.add_zone(name, zone.extract(*args))
if self.reference_state is not None:
domain.reference_state = self.reference_state.copy()
return domain
[docs] def extend(self, zone_args):
"""
Construct a new :class:`DomainObj` from zones extended according to
`zone_args`. Existing zone names are used for the new domain's zones.
zone_args: sequence
Sequence of argument tuples to be used to extend each zone.
If an argument tuple is empty or ``None``, then that zone
is skipped.
"""
domain = DomainObj()
for i, args in enumerate(zone_args):
if args:
zone = self.zones[i]
name = self.zone_name(zone)
domain.add_zone(name, zone.extend(*args))
return domain
[docs] def make_cartesian(self, axis='z'):
"""
Convert to Cartesian coordinate system.
axis: string
Specifies which is the cylinder axis ('z' or 'x').
"""
for zone in self.zones:
zone.make_cartesian(axis)
[docs] def make_cylindrical(self, axis='z'):
"""
Convert to cylindrical coordinate system.
axis: string
Specifies which is the cylinder axis ('z' or 'x').
"""
for zone in self.zones:
zone.make_cylindrical(axis)
[docs] def make_left_handed(self):
""" Convert to left-handed coordinate system. """
for zone in self.zones:
zone.make_left_handed()
[docs] def make_right_handed(self):
""" Convert to right-handed coordinate system. """
for zone in self.zones:
zone.make_right_handed()
[docs] def translate(self, delta_x, delta_y, delta_z):
"""
Translate coordinates.
delta_x, delta_y, delta_z: float
Amount of translation along the corresponding axis.
"""
for zone in self.zones:
zone.translate(delta_x, delta_y, delta_z)
[docs] def rotate_about_x(self, deg):
"""
Rotate about the X axis.
deg: float (degrees)
Amount of rotation.
"""
for zone in self.zones:
zone.rotate_about_x(deg)
[docs] def rotate_about_y(self, deg):
"""
Rotate about the Y axis.
deg: float (degrees)
Amount of rotation.
"""
for zone in self.zones:
zone.rotate_about_y(deg)
[docs] def rotate_about_z(self, deg):
"""
Rotate about the Z axis.
deg: float (degrees)
Amount of rotation.
"""
for zone in self.zones:
zone.rotate_about_z(deg)
[docs] def promote(self):
""" Promote from N-dimensional to N+1 dimensional index space. """
for zone in self.zones:
zone.promote()
[docs] def demote(self):
""" Demote from N-dimensional to N-1 dimensional index space. """
for zone in self.zones:
zone.demote()