def test_unit_vector(): """ Test unit vector function """ # Assert vector have length 1 for v in [UNIT_X, SOME_VECTOR]: v = m.unit_vector(v) assert np.linalg.norm(v) == 1 # Assert list/numpy arrays produce same output v_from_list = m.unit_vector(list(SOME_VECTOR)) v_from_np = m.unit_vector(np.array(SOME_VECTOR)) assert np.testing.assert_array_equal(v_from_list, v_from_np) is None
def _accel_vector(plot, ps): """ Add a vector indicating the direction of (gravitational) acceleration Args: :plot: plot :ps: plot settings Returns: :plot: modified plot object """ frame = ps['frame'] accel_direction = frame.accel_direction accel_magnitude = np.linalg.norm(accel_direction) orig = 3 * GlobalSystem.UnitVectors.Z vector = unit_vector(accel_direction) plot.scatter(*orig, color=COLOR_BC, linewidth=ps['set_linewidth']) plot.quiver(*orig, *vector, length=1, color=COLOR_BC, linewidth=ps['set_vectorwidth']) plot.text(*orig, f"{accel_magnitude:.2e} m/s²", color=COLOR_BC, fontsize=ps['set_fontsize']) return plot
def get_local_system_from_up(x_elem, up): """ Return the y- and z-axis of a Cartesian coordinate system (local element coordinate system) for a given x-axis and a defined "up-direction" Args: :x_elem: x-axis (of the element) :up: up-direction Returns: :y_axis: y-axis :z_axis: z-axis * Values are returned as a tuple """ if abs(1 - abs(np.dot(x_elem, up))) <= 1e-10: logger.error("up-direction and local x-axis are parallel") raise ValueError("up-direction and local x-axis are parallel") z_elem = unit_vector(vector_rejection(up, x_elem)) y_elem = unit_vector(np.cross(z_elem, x_elem)) return (y_elem, z_elem)
def __init__(self, p1, p2, up): """ Beam finite element with 6 dof per node TODO """ # ===== Geometry ===== # Node points self.p1 = p1 self.p2 = p2 # Vectors of the local coordinate system self.x_elem = unit_vector(self.p2.coord - self.p1.coord) self.y_elem, self.z_elem = get_local_system_from_up(self.x_elem, up) # Additional geometric properties self.mid_point = (self.p1.coord + self.p2.coord) / 2 self.mid_xsi = (self.p1.rel_coord + self.p2.rel_coord) / 2 self.length = np.linalg.norm(self.p2.coord - self.p1.coord) # ===== Material and cross section properties ===== self.properties = defaultdict(lambda: None) # ===== Load vector in the global system ===== self.load_vector_glob = np.zeros((12, 1)) self.mass_matrix_glob = np.zeros((12, 12)) # self.stiffness_matrix_glob = np.zeros((12, 1)) # ===== Transformation matrix ===== lx = direction_cosine(self.x_elem, G.X) ly = direction_cosine(self.y_elem, G.X) lz = direction_cosine(self.z_elem, G.X) mx = direction_cosine(self.x_elem, G.Y) my = direction_cosine(self.y_elem, G.Y) mz = direction_cosine(self.z_elem, G.Y) nx = direction_cosine(self.x_elem, G.Z) ny = direction_cosine(self.y_elem, G.Z) nz = direction_cosine(self.z_elem, G.Z) T3 = np.array([[lx, mx, nx], [ly, my, ny], [lz, mz, nz]]) self.T = np.zeros((12, 12)) self.T[0:3, 0:3] = self.T[3:6, 3:6] = self.T[6:9, 6:9] = self.T[9:12, 9:12] = T3
def __init__(self, parent_beamline, xsi1, xsi2, uid1, uid2, up=None): """ Beam finite element with 6 dof per node Args: :parent_beamline: parent beamline object :xsi1: relative position of first node :xsi2: relative position of second node :uid1: UID of node 1 :uid2: UID of node 2 """ # Book keeping self.element_num = parent_beamline.parent_frame.counter.elements parent_beamline.parent_frame.finder.elements.update( self.element_num, self) parent_beamline.parent_frame.counter.increment_element_count() logger.debug(f"Adding new element no. '{self.element_num}'...") # Reference to parent beamline self.parent_beamline = parent_beamline # Element geometry node1_num = parent_beamline.parent_frame.counter.get_last_node_num( ) - 1 node2_num = parent_beamline.parent_frame.counter.get_last_node_num() self.node1 = Node(self, uid1, xsi1, node1_num, elem_loc=1) self.node2 = Node(self, uid2, xsi2, node2_num, elem_loc=2) # Vectors of the local coordinate system self.x_elem = unit_vector(self.node2.coord - self.node1.coord) self.y_elem = None self.z_elem = None self.up = up # Element properties and loads self.properties = defaultdict(lambda: None) self.distributed_loads = defaultdict(int) self.concentrated_loads = defaultdict(int) self.point_properties = defaultdict(int) self.distributed_loads_in_loc_system = False self.concentrated_loads_in_loc_system = False
def add_freestream_vector(axes_2d, axes_3d, state, tip_pos=np.array([0, 0, 0]), vector_len=3): """ Add a free stream vector Args: :axes_2d: 2D axes object (matplotlib) :axes_3d: 3D axes object (matplotlib) :state: State model """ free_stream_vel = vector_len * unit_vector( state.free_stream_velocity_vector) orig = tip_pos - free_stream_vel axes_3d.quiver(*orig, *free_stream_vel, color=C.BLACK, linewidth=PS.LINEWIDTH_c) axes_3d.text(*orig, f"{state.aero['airspeed']:.1f} m/s")