class PI(np.ndarray): """ A class representing a point of intersection (PI) of an alignment. Parameters ---------- x, y, z : float The x, y, and z coordinates. radius : float The radius of the horizontal curve. Use zero if a curve does not exist. """ # Custom properties x = propy.index_property(0) y = propy.index_property(1) z = propy.index_property(2) def __new__(cls, x, y, z=0, radius=0): obj = np.array([x, y, z], dtype='float').view(cls) obj.radius = radius return obj def __array_finalize__(self, obj): if obj is None: return self.radius = getattr(obj, 'radius', 0) __repr__ = propy.repr_method('x', 'y', 'z', 'radius')
class SurveyPoint(np.ndarray): """ A class representing a survey point. Parameters ---------- x, y, z : float The x, y, and z coordinates. """ # Custom properties x = propy.index_property(0) y = propy.index_property(1) z = propy.index_property(2) def __new__(cls, x, y, z, **kwargs): obj = np.array([x, y, z], dtype='float').view(cls) obj.meta = dict(**kwargs) return obj def __array_finalize__(self, obj): if obj is None: return self.meta = getattr(obj, 'meta', {}) __repr__ = propy.repr_method('x', 'y', 'z', 'meta')
class ElementLoad(np.ndarray): """ A class representing an element load. Parameters ---------- element : str The name of the element to which the loads are applied. fx, fy, fz : float The global forces applied to the element. mx, my, mz : float The global moments applied to the element. ix, : float The distance from the i node at where the loads are applied. dx : float The distance from the ix position toward the j node over which the loads are applied. """ # Custom properties element = propy.str_property('element') fx = propy.index_property(0) fy = propy.index_property(1) fz = propy.index_property(2) mx = propy.index_property(3) my = propy.index_property(4) mz = propy.index_property(5) _element_ref = propy.weakref_property('_element_ref') def __new__(cls, element, fx=0, fy=0, fz=0, mx=0, my=0, mz=0, ix=0, dx=-1): obj = np.array([fx, fy, fz, mx, my, mz], dtype='float').view(cls) obj.element = element obj.ix = ix obj.dx = dx return obj def __array_finalize__(self, obj): if obj is None: return self.element = getattr(obj, 'element', '') self.ix = getattr(obj, 'ix', 0) self.dx = getattr(obj, 'dx', 0) self._element_ref = None def __repr__(self): s = [ 'element={!r}'.format(self.element), 'forces={!r}'.format( (self.fx, self.fy, self.fz)), 'moments={!r}'.format( (self.mx, self.my, self.mz)), 'ix={!r}'.format(self.ix), 'dx={!r}'.format(self.dx) ] return '{}({})'.format(type(self).__name__, ', '.join(s)) def forces(self): """Returns the force vector.""" return self[:3] def moments(self): """Returns the moment vector.""" return self[3:6] def get_element(self): """Gets the referenced element.""" if self._element_ref is None: raise ValueError('Element has not been set.') return self._element_ref def set_element(self, edict): """ Sets the element reference. Parameters ---------- edict : dict A dictionary mapping node names to node objects. """ self._element_ref = edict[self.element] def local_reactions(self, di=(0, 0, 0), dj=(0, 0, 0)): """ Returns the local end reactions for the element. Parameters ---------- di, dj : array The deflections at the i and j ends of the element. """ di, dj = np.asarray(di), np.asarray(dj) e = self.get_element() xi, xj = e.get_nodes() dx, dy, dz = (xj - xi) + (dj - di) fx, fy, fz = self.forces() mx, my, mz = self.moments() r = local_reactions(fx, fy, fz, mx, my, mz, dx, dy, dz, e.roll, self.ix, self.dx, e.imx_free, e.imy_free, e.imz_free, e.jmx_free, e.jmy_free, e.jmz_free) return r def global_reactions(self, di=(0, 0, 0), dj=(0, 0, 0)): """ Returns the global end reactions for the element. Parameters ---------- di, dj : array The deflections at the i and j ends of the element. """ di, dj = np.asarray(di), np.asarray(dj) e = self.get_element() t = e.transformation_matrix(di, dj) q = self.local_reactions(di, dj) return t.T.dot(q)
class NodeLoad(np.ndarray): """ A class representing a load applied to a node. Parameters ---------- node : str The name of the node to which the load will be applied. fx, fy, fz : float The applied global node forces. mx, my, mz : float The applied global moments. dx, dy, dz : float The applied node deflections. rx, ry, rz : float The applied node rotations. """ # Custom properties node = propy.str_property('node') _node_ref = propy.weakref_property('_node_ref') fx = propy.index_property(0) fy = propy.index_property(1) fz = propy.index_property(2) mx = propy.index_property(3) my = propy.index_property(4) mz = propy.index_property(5) dx = propy.index_property(6) dy = propy.index_property(7) dz = propy.index_property(8) rx = propy.index_property(9) ry = propy.index_property(10) rz = propy.index_property(11) def __new__(cls, node, fx=0, fy=0, fz=0, mx=0, my=0, mz=0, dx=0, dy=0, dz=0, rx=0, ry=0, rz=0): obj = np.array([fx, fy, fz, mx, my, mz, dx, dy, dz, rx, ry, rz], dtype='float').view(cls) obj.node = node return obj def __array_finalize__(self, obj): if obj is None: return self.node = getattr(obj, 'node', '') self._node_ref = None def __repr__(self): s = [ 'node={!r}'.format(self.node), 'forces={!r}'.format( (self.fx, self.fy, self.fz)), 'moments={!r}'.format( (self.mx, self.my, self.mz)), 'defl={!r}'.format( (self.dx, self.dy, self.dz)), 'rot={!r}'.format( (self.rx, self.ry, self.rz)) ] return '{}({})'.format(type(self).__name__, ', '.join(s)) def forces(self): """Returns the applied force and moment matrix.""" return self[:6] def deflections(self): """Returns the applied deflection and rotation matrix.""" return self[6:] def get_node(self): """Gets the referenced node.""" if self._node_ref is None: raise ValueError('Node has not been set.') return self._node_ref def set_node(self, ndict): """ Sets the node reference. Parameters ---------- ndict : dict A dictionary mapping node names to node objects. """ self._node_ref = ndict[self.node]
class Node(np.ndarray): """ A class representing a structural node. Parameters ---------- name : str A unique name for the node. x, y, z : float The x, y, and z coordinates of the node. symmetry : {None, 'x', 'y', 'xy'} The symmetry of the node. fx_free, fy_free, fz_free : bool The force fixities of the node in the x, y, and z directions. mx_free, my_free, mz_free : bool The moment fixities of the node about the x, y, and z axes. """ SYMMETRIES = (None, 'x', 'y', 'xy') # Custom properties name = propy.str_property('name') x = propy.index_property(0) y = propy.index_property(1) z = propy.index_property(2) symmetry = propy.enum_property('symmetry', set(SYMMETRIES)) fx_free = propy.bool_property('fx_free') fy_free = propy.bool_property('fy_free') fz_free = propy.bool_property('fz_free') mx_free = propy.bool_property('mx_free') my_free = propy.bool_property('my_free') mz_free = propy.bool_property('mz_free') def __new__(cls, name, x=0, y=0, z=0, symmetry=None, fx_free=True, fy_free=True, fz_free=True, mx_free=True, my_free=True, mz_free=True): obj = np.array([x, y, z], dtype='float').view(cls) obj.name = name obj.symmetry = symmetry obj.fx_free = fx_free obj.fy_free = fy_free obj.fz_free = fz_free obj.mx_free = mx_free obj.my_free = my_free obj.mz_free = mz_free return obj def __array_finalize__(self, obj): if obj is None: return self.name = getattr(obj, 'name', '') self.symmetry = getattr(obj, 'symmetry', None) self.fx_free = getattr(obj, 'fx_free', True) self.fy_free = getattr(obj, 'fy_free', True) self.fz_free = getattr(obj, 'fz_free', True) self.mx_free = getattr(obj, 'mx_free', True) self.my_free = getattr(obj, 'my_free', True) self.mz_free = getattr(obj, 'mz_free', True) __repr__ = propy.repr_method('name', 'x', 'y', 'z', 'symmetry', 'fx_free', 'fy_free', 'fz_free', 'mx_free', 'my_free', 'mz_free') def __str__(self): return self.name def copy(self): """Returns a copy of the node.""" return copy.copy(self) def f_fixed(self): """Sets the node force reactions to fixed.""" self.fx_free = self.fy_free = self.fz_free = False return self def m_fixed(self): """Sets the node moment reactions to fixed.""" self.mx_free = self.my_free = self.mz_free = False return self def fixed(self): """Sets the node force and moment reactions to fixed.""" return self.f_fixed().m_fixed() def fixities(self): """Returns the force and moment fixities for the node.""" return [ self.fx_free, self.fy_free, self.fz_free, self.mx_free, self.my_free, self.mz_free ] def sym_nodes(self): """Returns the symmetric nodes for the node.""" def primary(): n = self.copy() n.name = '{}_p'.format(self.name) return n def x_sym(): n = self.copy() n.name = '{}_x'.format(self.name) n[1] *= -1 return n def y_sym(): n = self.copy() n.name = '{}_y'.format(self.name) n[0] *= -1 return n def xy_sym(): n = self.copy() n.name = '{}_xy'.format(self.name) n[:2] *= -1 return n if self.symmetry is None: return primary(), elif self.symmetry == 'x': return primary(), x_sym() elif self.symmetry == 'y': return primary(), y_sym() elif self.symmetry == 'xy': return primary(), x_sym(), y_sym(), xy_sym()
class SurveyStake(np.ndarray): """ A class representing a survey stake. This method should be initialized using the :meth:`SurveyStake.init_xy` or :meth:`SurveyStake.init_station` class methods. """ TYPES = ('xy', 'station') # Custom properties x = propy.index_property(0) y = propy.index_property(1) z = propy.index_property(2) lock_z = propy.bool_property('lock_z') _type = propy.enum_property('_type', TYPES) def __new__(cls, x, y, z, station, offset, height, rotation, lock_z, _type, _init=False, **kwargs): if not _init: raise ValueError( 'SurveyStake should be initialized using the ' '`SurveyStake.init_xy` or `SurveyStake.init_station` methods ' 'in lieu of the standard initializer.') obj = np.array([x, y, z], dtype='float').view(cls) obj.station = station obj.offset = offset obj.height = height obj.rotation = rotation obj.lock_z = lock_z obj._type = _type obj.meta = dict(**kwargs) return obj def __array_finalize__(self, obj): if obj is None: return self.station = getattr(obj, 'station', 0) self.offset = getattr(obj, 'offset', 0) self.height = getattr(obj, 'height', 0) self.rotation = getattr(obj, 'rotation', 0) self.lock_z = getattr(obj, 'lock_z', False) self._type = getattr(obj, '_type', 'xy') self.meta = getattr(obj, 'meta', {}) __repr__ = propy.repr_method('_type', 'x', 'y', 'z', 'station', 'offset', 'lock_z', 'meta') @classmethod def init_xy(cls, x, y, z=0, height=0, rotation=0, lock_z=False, **kwargs): """ Initializes a survey stake based on an (x, y) global coordinate. Parameters ---------- x, y, z : float The x, y, and z coordinates. height : float The height of the point above z. rotation : float The rotation of the point about its base point. lock_z : float If False, the alignment will be snapped to the TIN (if applicable) during certain updates. Otherwise, the z coordinate will remain fixed. """ return cls(x=x, y=y, z=z, station=0, offset=0, height=height, rotation=rotation, lock_z=lock_z, _type='xy', _init=True, **kwargs) @classmethod def init_station(cls, station, offset=0, z=0, height=0, rotation=0, lock_z=False, **kwargs): """ Initializes a survey stake based on a survey station and offset. Parameters ---------- station : float The alignment survey station. offset : float The offset from the alignment. z : float The z coordinate. height : float The height of the point above z. rotation : float The rotation of the point about its base point. lock_z : float If False, the alignment will be snapped to the TIN (if applicable) during certain updates. Otherwise, the z coordinate will remain fixed. """ return cls(x=0, y=0, z=z, height=height, rotation=rotation, station=station, offset=offset, lock_z=lock_z, _type='station', _init=True, **kwargs)