diff --git a/anastruct/cython/basic.py b/anastruct/cython/basic.py index f9c639e..30b4f2d 100644 --- a/anastruct/cython/basic.py +++ b/anastruct/cython/basic.py @@ -18,20 +18,20 @@ def converge(lhs: float, rhs: float) -> float: return (rhs / lhs - 1) / div + 1 -def angle_x_axis(delta_x: float, delta_z: float) -> float: +def angle_x_axis(delta_x: float, delta_y: float) -> float: """Determine the angle of the element with the global x-axis Args: delta_x (float): Element length in the x-direction - delta_z (float): Element length in the z-direction + delta_y (float): Element length in the y-direction Returns: float: Angle of the element with the global x-axis """ - # dot product v_x = [1, 0] ; v = [delta_x, delta_z] - # dot product = 1 * delta_x + 0 * delta_z -> delta_x - ai = math.acos(delta_x / math.sqrt(delta_x**2 + delta_z**2)) - if delta_z < 0: + # dot product v_x = [1, 0] ; v = [delta_x, delta_y] + # dot product = 1 * delta_x + 0 * delta_y -> delta_x + ai = math.acos(delta_x / math.sqrt(delta_x**2 + delta_y**2)) + if delta_y < 0: ai = 2 * math.pi - ai return ai diff --git a/anastruct/cython/cbasic.pyx b/anastruct/cython/cbasic.pyx index af01625..5dbb860 100644 --- a/anastruct/cython/cbasic.pyx +++ b/anastruct/cython/cbasic.pyx @@ -16,12 +16,12 @@ cpdef converge(double lhs, double rhs): div = max(lhs, rhs) / min(lhs, rhs) * 2.0 return (abs(rhs) / abs(lhs) - 1.0) / div + 1.0 -cpdef angle_x_axis(double delta_x, double delta_z): +cpdef angle_x_axis(double delta_x, double delta_y): cdef double ai - # dot product v_x = [1, 0] ; v = [delta_x, delta_z] - # dot product = 1 * delta_x + 0 * delta_z -> delta_x - ai = acos(delta_x / sqrt(delta_x**2 + delta_z**2)) - if delta_z < 0: + # dot product v_x = [1, 0] ; v = [delta_x, delta_y] + # dot product = 1 * delta_x + 0 * delta_y -> delta_x + ai = acos(delta_x / sqrt(delta_x**2 + delta_y**2)) + if delta_y < 0: ai = 2 * pi - ai return ai \ No newline at end of file diff --git a/anastruct/fem/elements.py b/anastruct/fem/elements.py index 85df404..98105a9 100644 --- a/anastruct/fem/elements.py +++ b/anastruct/fem/elements.py @@ -284,11 +284,11 @@ def __add__(self, other: Element) -> Element: el.node_map[self.node_id1] = el.node_1 + other.node_1 el.node_map[self.node_id2] = el.node_2 + other.node_2 el.node_map[self.node_id1].ux = el.node_1.ux + other.node_1.ux - el.node_map[self.node_id1].uz = el.node_1.uz + other.node_1.uz - el.node_map[self.node_id1].phi_y = el.node_1.phi_y + other.node_1.phi_y + el.node_map[self.node_id1].uy = el.node_1.uy + other.node_1.uy + el.node_map[self.node_id1].phi_z = el.node_1.phi_z + other.node_1.phi_z el.node_map[self.node_id2].ux = el.node_2.ux + other.node_2.ux - el.node_map[self.node_id2].uz = el.node_2.uz + other.node_2.uz - el.node_map[self.node_id2].phi_y = el.node_2.phi_y + other.node_2.phi_y + el.node_map[self.node_id2].uy = el.node_2.uy + other.node_2.uy + el.node_map[self.node_id2].phi_z = el.node_2.phi_z + other.node_2.phi_z return el diff --git a/anastruct/fem/examples/ex_4.py b/anastruct/fem/examples/ex_4.py index b1a73dd..196b04b 100644 --- a/anastruct/fem/examples/ex_4.py +++ b/anastruct/fem/examples/ex_4.py @@ -3,7 +3,7 @@ ss = SystemElements() ss.add_element(location=[[0, 0], [5, 0]], EA=5e9, EI=8000, spring={2: 0}) ss.add_element(location=[[5, 0], [5, 5]], EA=5e9, EI=4000) -ss.moment_load(Ty=10, node_id=3) +ss.moment_load(Tz=10, node_id=3) ss.add_support_hinged(node_id=1) ss.add_support_hinged(node_id=3) diff --git a/anastruct/fem/examples/ex_5.py b/anastruct/fem/examples/ex_5.py index f4dd232..7687697 100644 --- a/anastruct/fem/examples/ex_5.py +++ b/anastruct/fem/examples/ex_5.py @@ -3,7 +3,7 @@ ss = SystemElements() ss.add_element(location=[[0, 0], [5, 0]], EA=5e9, EI=8000) ss.add_element(location=[[5, 0], [5, 5]], EA=5e9, EI=4000) -ss.moment_load(Ty=10, node_id=3) +ss.moment_load(Tz=10, node_id=3) ss.add_support_hinged(node_id=1) ss.add_support_hinged(node_id=3) diff --git a/anastruct/fem/examples/water_acc.ipynb b/anastruct/fem/examples/water_acc.ipynb index 58e85e9..8c3eda5 100644 --- a/anastruct/fem/examples/water_acc.ipynb +++ b/anastruct/fem/examples/water_acc.ipynb @@ -196,7 +196,7 @@ " deflection = np.zeros(len(ss.node_map))\n", "\n", " # Height of the nodes\n", - " y = np.array(ss.nodes_range(\"y\"))\n", + " y = np.array(ss.nodes_range(\"y_neg\"))\n", "\n", " # An array with point loads.\n", " # cubic meters * weight water\n", @@ -210,7 +210,7 @@ " point_load = force_water[k - 1]\n", "\n", " if point_load > 0:\n", - " ss.point_load(k, Fx=0, Fz=-point_load)\n", + " ss.point_load(k, Fx=0, Fy=-point_load)\n", " cubics += point_load / q_water\n", "\n", " return cubics" @@ -397,12 +397,14 @@ "source": [ "plt.figure(figsize=(10, 6))\n", "\n", - "plt.plot(ss.nodes_range(\"x\")[:-2], ss.nodes_range(\"y\")[:-2])\n", + "plt.plot(ss.nodes_range(\"x\")[:-2], ss.nodes_range(\"y_neg\")[:-2])\n", "plt.plot(\n", " ss.nodes_range(\"x\")[:-2],\n", " [\n", " a + b\n", - " for a, b in zip(ss.nodes_range(\"y\")[:-2], ss.get_node_result_range(\"uy\")[:-2])\n", + " for a, b in zip(\n", + " ss.nodes_range(\"y_neg\")[:-2], ss.get_node_result_range(\"uy\")[:-2]\n", + " )\n", " ],\n", ")\n", "\n", diff --git a/anastruct/fem/node.py b/anastruct/fem/node.py index 556f370..39e8334 100644 --- a/anastruct/fem/node.py +++ b/anastruct/fem/node.py @@ -13,11 +13,11 @@ def __init__( self, id: int, # pylint: disable=redefined-builtin Fx: float = 0.0, - Fz: float = 0.0, - Ty: float = 0.0, + Fy: float = 0.0, + Tz: float = 0.0, ux: float = 0.0, - uz: float = 0.0, - phi_y: float = 0.0, + uy: float = 0.0, + phi_z: float = 0.0, vertex: Vertex = Vertex(0, 0), hinge: bool = False, ): @@ -26,35 +26,35 @@ def __init__( Args: id (int): ID of the node Fx (float, optional): Value of Fx force. Defaults to 0.0. - Fz (float, optional): Value of Fz force. Defaults to 0.0. - Ty (float, optional): Value of Ty moment. Defaults to 0.0. + Fy (float, optional): Value of Fy force. Defaults to 0.0. + Tz (float, optional): Value of Tz moment. Defaults to 0.0. ux (float, optional): Value of ux displacement. Defaults to 0.0. - uz (float, optional): Value of uz displacement. Defaults to 0.0. - phi_y (float, optional): Value of phi_y rotation. Defaults to 0.0. + uy (float, optional): Value of uy displacement. Defaults to 0.0. + phi_z (float, optional): Value of phi_z rotation. Defaults to 0.0. vertex (Vertex, optional): Point object coordinate. Defaults to Vertex(0, 0). hinge (bool, optional): Is this node a hinge. Defaults to False. """ self.id = id # forces self.Fx = Fx - self.Fz = Fz - self.Ty = Ty + self.Fy = Fy + self.Tz = Tz # displacements self.ux = ux - self.uz = uz - self.phi_y = phi_y + self.uy = uy + self.phi_z = phi_z self.vertex = vertex self.hinge = hinge self.elements: Dict[int, Element] = {} @property - def Fy(self) -> float: - """Fy is the vertical force, and the negative of Fz + def Fy_neg(self) -> float: + """Fy is the vertical force, and the negative of Fy Returns: - float: negative of Fz + float: negative of Fy """ - return -self.Fz + return -self.Fy def __str__(self) -> str: """String representation of the node @@ -64,12 +64,12 @@ def __str__(self) -> str: """ if self.vertex: return ( - f"[id = {self.id}, Fx = {self.Fx}, Fz = {self.Fz}, Ty = {self.Ty}, ux = {self.ux}, " - f"uz = {self.uz}, phi_y = {self.phi_y}, x = {self.vertex.x}, y = {self.vertex.y}]" + f"[id = {self.id}, Fx = {self.Fx}, Fy = {self.Fy}, Tz = {self.Tz}, ux = {self.ux}, " + f"uy = {self.uy}, phi_z = {self.phi_z}, x = {self.vertex.x}, y = {self.vertex.y}]" ) return ( - f"[id = {self.id}, Fx = {self.Fx}, Fz = {self.Fz}, Ty = {self.Ty}, ux = {self.ux}, " - f"uz = {self.uz}, phi_y = {self.phi_y}]" + f"[id = {self.id}, Fx = {self.Fx}, Fy = {self.Fy}, Tz = {self.Tz}, ux = {self.ux}, " + f"uy = {self.uy}, phi_z = {self.phi_z}]" ) def __add__(self, other: Node) -> Node: @@ -85,17 +85,17 @@ def __add__(self, other: Node) -> Node: self.id == other.id ), "Cannot add nodes as the ID's don't match. The nodes positions don't match." Fx = self.Fx + other.Fx - Fz = self.Fz + other.Fz - Ty = self.Ty + other.Ty + Fy = self.Fy + other.Fy + Tz = self.Tz + other.Tz return Node( id=self.id, Fx=Fx, - Fz=Fz, - Ty=Ty, + Fy=Fy, + Tz=Tz, ux=self.ux, - uz=self.uz, - phi_y=self.phi_y, + uy=self.uy, + phi_z=self.phi_z, vertex=self.vertex, hinge=self.hinge, ) @@ -113,24 +113,24 @@ def __sub__(self, other: Node) -> Node: self.id == other.id ), "Cannot subtract nodes as the ID's don't match. The nodes positions don't match." Fx = self.Fx - other.Fx - Fz = self.Fz - other.Fz - Ty = self.Ty - other.Ty + Fy = self.Fy - other.Fy + Tz = self.Tz - other.Tz return Node( self.id, Fx, - Fz, - Ty, + Fy, + Tz, self.ux, - self.uz, - self.phi_y, + self.uy, + self.phi_z, self.vertex, hinge=self.hinge, ) def reset(self) -> None: """Reset the node to zero forces and displacements""" - self.Fx = self.Fz = self.Ty = self.ux = self.uz = self.phi_y = 0 + self.Fx = self.Fy = self.Tz = self.ux = self.uy = self.phi_z = 0 self.hinge = False def add_results(self, other: Node) -> None: @@ -142,15 +142,15 @@ def add_results(self, other: Node) -> None: assert ( self.id == other.id ), "Cannot add nodes as the ID's don't match. The nodes positions don't match." - assert self.phi_y is not None + assert self.phi_z is not None assert self.ux is not None - assert self.uz is not None - assert other.phi_y is not None + assert self.uy is not None + assert other.phi_z is not None assert other.ux is not None - assert other.uz is not None + assert other.uy is not None self.Fx = self.Fx + other.Fx - self.Fz = self.Fz + other.Fz - self.Ty = self.Ty + other.Ty + self.Fy = self.Fy + other.Fy + self.Tz = self.Tz + other.Tz self.ux = self.ux + other.ux - self.uz = self.uz + other.uz - self.phi_y = self.phi_y + other.phi_y + self.uy = self.uy + other.uy + self.phi_z = self.phi_z + other.phi_z diff --git a/anastruct/fem/plotter/element.py b/anastruct/fem/plotter/element.py index 9e167cf..80e37da 100644 --- a/anastruct/fem/plotter/element.py +++ b/anastruct/fem/plotter/element.py @@ -21,14 +21,14 @@ def plot_values_deflection( Tuple[np.ndarray, np.ndarray]: x and y values """ ux1 = element.node_1.ux * factor - uz1 = -element.node_1.uz * factor + uy1 = -element.node_1.uy * factor ux2 = element.node_2.ux * factor - uz2 = -element.node_2.uz * factor + uy2 = -element.node_2.uy * factor x1 = element.vertex_1.x + ux1 - y1 = -element.vertex_1.z + uz1 + y1 = element.vertex_1.y + uy1 x2 = element.vertex_2.x + ux2 - y2 = -element.vertex_2.z + uz2 + y2 = element.vertex_2.y + uy2 if element.type == "general" and not linear: assert element.deflection is not None @@ -61,17 +61,17 @@ def plot_values_bending_moment( """ # Determine forces for horizontal element.angle = 0 - T_left = element.node_1.Ty - T_right = -element.node_2.Ty + T_left = element.node_1.Tz + T_right = -element.node_2.Tz sin = math.sin(-element.angle) cos = math.cos(-element.angle) # apply angle ai x1 = element.vertex_1.x + T_left * sin * factor - y1 = -element.vertex_1.z + T_left * cos * factor + y1 = element.vertex_1.y + T_left * cos * factor x2 = element.vertex_2.x + T_right * sin * factor - y2 = -element.vertex_2.z + T_right * cos * factor + y2 = element.vertex_2.y + T_right * cos * factor interpolate = np.linspace(0, 1, n) dx = x2 - x1 @@ -94,9 +94,9 @@ def plot_values_bending_moment( y_val += cos * q_part * factor x_val = np.append(x_val, element.vertex_2.x) - y_val = np.append(y_val, -element.vertex_2.z) + y_val = np.append(y_val, element.vertex_2.y) x_val = np.insert(x_val, 0, element.vertex_1.x) - y_val = np.insert(y_val, 0, -element.vertex_1.z) + y_val = np.insert(y_val, 0, element.vertex_1.y) return x_val, y_val @@ -126,9 +126,9 @@ def plot_values_axial_force( # apply angle ai x1 = element.vertex_1.x + N1 * cos * factor - y1 = -element.vertex_1.z + N1 * sin * factor + y1 = element.vertex_1.y + N1 * sin * factor x2 = element.vertex_2.x + N2 * cos * factor - y2 = -element.vertex_2.z + N2 * sin * factor + y2 = element.vertex_2.y + N2 * sin * factor interpolate = np.linspace(0, 1, n) dx = x2 - x1 @@ -147,9 +147,9 @@ def plot_values_axial_force( y_val += sin * qn_part * factor x_val = np.append(x_val, element.vertex_2.x) - y_val = np.append(y_val, -element.vertex_2.z) + y_val = np.append(y_val, element.vertex_2.y) x_val = np.insert(x_val, 0, element.vertex_1.x) - y_val = np.insert(y_val, 0, -element.vertex_1.z) + y_val = np.insert(y_val, 0, element.vertex_1.y) return x_val, y_val @@ -167,9 +167,9 @@ def plot_values_shear_force( Tuple[np.ndarray, np.ndarray]: x and y values """ x1 = element.vertex_1.x - y1 = -element.vertex_1.z + y1 = element.vertex_1.y x2 = element.vertex_2.x - y2 = -element.vertex_2.z + y2 = element.vertex_2.y assert element.shear_force is not None n = len(element.shear_force) @@ -189,9 +189,9 @@ def plot_values_shear_force( y_val += cos * element.shear_force * factor x_val = np.append(x_val, element.vertex_2.x) - y_val = np.append(y_val, -element.vertex_2.z) + y_val = np.append(y_val, element.vertex_2.y) x_val = np.insert(x_val, 0, element.vertex_1.x) - y_val = np.insert(y_val, 0, -element.vertex_1.z) + y_val = np.insert(y_val, 0, element.vertex_1.y) return x_val, y_val @@ -206,5 +206,5 @@ def plot_values_element(element: "Element") -> Tuple[np.ndarray, np.ndarray]: Tuple[np.ndarray, np.ndarray]: x and y values """ x_val = np.array([element.vertex_1.x, element.vertex_2.x]) - y_val = np.array([-element.vertex_1.z, -element.vertex_2.z]) + y_val = np.array([element.vertex_1.y, element.vertex_2.y]) return x_val, y_val diff --git a/anastruct/fem/plotter/mpl.py b/anastruct/fem/plotter/mpl.py index c2276b3..136db6e 100644 --- a/anastruct/fem/plotter/mpl.py +++ b/anastruct/fem/plotter/mpl.py @@ -81,7 +81,7 @@ def __fixed_support_patch(self, max_val: float, axes_i: int = 0) -> None: width = height = PATCH_SIZE * max_val for node in self.system.supports_fixed: support_patch = mpatches.Rectangle( - (node.vertex.x - width * 0.5, -node.vertex.z - width * 0.5), + (node.vertex.x - width * 0.5, node.vertex.y - width * 0.5), width, height, color="r", @@ -117,7 +117,7 @@ def __rotational_support_patch(self, max_val: float, axes_i: int = 0) -> None: width = height = PATCH_SIZE * max_val for node in self.system.supports_rotational: support_patch = mpatches.Rectangle( - (node.vertex.x - width * 0.5, -node.vertex.z - width * 0.5), + (node.vertex.x - width * 0.5, node.vertex.y - width * 0.5), width, height, color="r", @@ -182,13 +182,13 @@ def __roll_support_patch(self, max_val: float, axes_i: int = 0) -> None: zorder=9, ) self.axes[axes_i].add_patch(support_patch_regpoly) - y = -node.vertex.z - 2 * radius + y = node.vertex.y - 2 * radius self.axes[axes_i].plot( [node.vertex.x - radius, node.vertex.x + radius], [y, y], color="r" ) if not rotate: rect_patch_rect = mpatches.Rectangle( - (node.vertex.x - radius / 2, -node.vertex.z - radius / 2), + (node.vertex.x - radius / 2, node.vertex.y - radius / 2), radius, radius, color="r", @@ -210,7 +210,7 @@ def __roll_support_patch(self, max_val: float, axes_i: int = 0) -> None: ) if not rotate: rect_patch_rect = mpatches.Rectangle( - (node.vertex.x - radius / 2, -node.vertex.z - radius / 2), + (node.vertex.x - radius / 2, node.vertex.y - radius / 2), radius, radius, color="r", @@ -229,7 +229,7 @@ def __rotating_spring_support_patch(self, max_val: float, axes_i: int = 0) -> No """ radius = PATCH_SIZE * max_val - for node, _ in self.system.supports_spring_y: + for node, _ in self.system.supports_spring_z: r = np.arange(0, radius, 0.001) theta = 25 * np.pi * r / (0.2 * max_val) x = np.cos(theta) * r + node.vertex.x @@ -258,7 +258,7 @@ def __spring_support_patch(self, max_val: float, axes_i: int = 0) -> None: right = 0.5 * h dh = 0.2 * h - for node, _ in self.system.supports_spring_z: + for node, _ in self.system.supports_spring_y: yval = np.arange(0, -9, -1) * dh + node.vertex.y xval = ( np.array([0, 0, left, right, left, right, left, 0, 0]) + node.vertex.x @@ -268,7 +268,7 @@ def __spring_support_patch(self, max_val: float, axes_i: int = 0) -> None: # Triangle support_patch = mpatches.RegularPolygon( - (node.vertex.x, -node.vertex.z - h * 2.6), + (node.vertex.x, node.vertex.y - h * 2.6), numVertices=3, radius=h * 0.9, color="r", @@ -286,7 +286,7 @@ def __spring_support_patch(self, max_val: float, axes_i: int = 0) -> None: # Triangle support_patch = mpatches.RegularPolygon( - (node.vertex.x + h * 1.7, -node.vertex.z - h), + (node.vertex.x + h * 1.7, node.vertex.y - h), numVertices=3, radius=h * 0.9, color="r", @@ -336,7 +336,7 @@ def __plot_patch( direction (float): 1 or -1, depending on the direction of the load el_angle (float): angle of the element """ - # - value, because the positive z of the system is opposite of positive y of the plotter + # - value, because the positive y of the system is opposite of positive y of the plotter xn1 = x1 + np.sin(ai) * h1 * direction yn1 = y1 + np.cos(ai) * h1 * direction xn2 = x2 + np.sin(ai) * h2 * direction @@ -446,13 +446,13 @@ def __plot_patch( @staticmethod def __arrow_patch_values( - Fx: float, Fz: float, node: "Node", h: float + Fx: float, Fy: float, node: "Node", h: float ) -> Tuple[float, float, float, float, float]: """Determines the values for the point load arrow patch. Args: Fx (float): Point load magnitude in x direction - Fz (float): Point load magnitude in z direction + Fy (float): Point load magnitude in y direction node (Node): Node upon which load is applied h (float): Scale variable @@ -460,9 +460,9 @@ def __arrow_patch_values( Tuple[float, float, float, float, float]: x, y, len_x, len_y, F (for matplotlib plotter) """ - F = (Fx**2 + Fz**2) ** 0.5 + F = (Fx**2 + Fy**2) ** 0.5 len_x = Fx / F * h - len_y = -Fz / F * h + len_y = -Fy / F * h x = node.vertex.x - len_x * 1.2 y = node.vertex.y - len_y * 1.2 @@ -480,11 +480,11 @@ def __point_load_patch( """ for k in self.system.loads_point: - Fx, Fz = self.system.loads_point[k] - F = (Fx**2 + Fz**2) ** 0.5 + Fx, Fy = self.system.loads_point[k] + F = (Fx**2 + Fy**2) ** 0.5 node = self.system.node_map[k] h = 0.1 * max_plot_range * F / self.max_system_point_load - x, y, len_x, len_y, F = self.__arrow_patch_values(Fx, Fz, node, h) + x, y, len_x, len_y, F = self.__arrow_patch_values(Fx, Fy, node, h) self.axes[axes_i].arrow( x, @@ -513,7 +513,7 @@ def __moment_load_patch(self, max_val: float, axes_i: int = 0) -> None: if v > 0: self.axes[axes_i].plot( node.vertex.x, - -node.vertex.z, + node.vertex.y, marker=r"$\circlearrowleft$", ms=25, color="orange", @@ -521,14 +521,14 @@ def __moment_load_patch(self, max_val: float, axes_i: int = 0) -> None: else: self.axes[axes_i].plot( node.vertex.x, - -node.vertex.z, + node.vertex.y, marker=r"$\circlearrowright$", ms=25, color="orange", ) self.axes[axes_i].text( node.vertex.x + h * 0.2, - -node.vertex.z + h * 0.2, + node.vertex.y + h * 0.2, f"T={v}", color="k", fontsize=9, @@ -834,7 +834,7 @@ def axial_force( point.displace_polar( alpha=el.angle + 0.5 * np.pi, radius=0.5 * el.N_1 * factor, - inverse_z_axis=True, + inverse_y_axis=True, ) if verbosity == 0: @@ -851,7 +851,7 @@ def axial_force( point.displace_polar( alpha=el.angle + 0.5 * np.pi, radius=0.5 * el.N_1 * factor, - inverse_z_axis=True, + inverse_y_axis=True, ) if verbosity == 0: @@ -906,8 +906,8 @@ def bending_moment( max_moment = max( map( lambda el: max( - abs(el.node_1.Ty), - abs(el.node_2.Ty), + abs(el.node_1.Tz), + abs(el.node_2.Tz), abs(((el.all_qp_load[0] + el.all_qp_load[1]) / 16) * el.l**2), ) if el.type == "general" @@ -920,8 +920,8 @@ def bending_moment( # determine the axis values for el in self.system.element_map.values(): if ( - math.isclose(el.node_1.Ty, 0, rel_tol=1e-5, abs_tol=1e-9) - and math.isclose(el.node_2.Ty, 0, rel_tol=1e-5, abs_tol=1e-9) + math.isclose(el.node_1.Tz, 0, rel_tol=1e-5, abs_tol=1e-9) + and math.isclose(el.node_2.Tz, 0, rel_tol=1e-5, abs_tol=1e-9) and math.isclose(el.all_qp_load[0], 0, rel_tol=1e-5, abs_tol=1e-9) and math.isclose(el.all_qp_load[1], 0, rel_tol=1e-5, abs_tol=1e-9) ): @@ -931,8 +931,8 @@ def bending_moment( node_results = verbosity == 0 self.plot_result( axis_values, - abs(el.node_1.Ty), - abs(el.node_2.Ty), + abs(el.node_1.Tz), + abs(el.node_2.Tz), node_results=node_results, axes_i=axes_i, ) @@ -997,8 +997,8 @@ def shear_force( for el in self.system.element_map.values(): if ( - math.isclose(el.node_1.Ty, 0, rel_tol=1e-5, abs_tol=1e-9) - and math.isclose(el.node_2.Ty, 0, rel_tol=1e-5, abs_tol=1e-9) + math.isclose(el.node_1.Tz, 0, rel_tol=1e-5, abs_tol=1e-9) + and math.isclose(el.node_2.Tz, 0, rel_tol=1e-5, abs_tol=1e-9) and math.isclose(el.all_qp_load[0], 0, rel_tol=1e-5, abs_tol=1e-9) and math.isclose(el.all_qp_load[1], 0, rel_tol=1e-5, abs_tol=1e-9) ): @@ -1059,7 +1059,7 @@ def reaction_force( h = 0.2 * self.max_val_structure max_force = max( map( - lambda node: max(abs(node.Fx), abs(node.Fz)), + lambda node: max(abs(node.Fx), abs(node.Fy)), self.system.reaction_forces.values(), ) ) @@ -1096,10 +1096,10 @@ def reaction_force( zorder=10, ) - if not math.isclose(node.Fz, 0, rel_tol=1e-5, abs_tol=1e-9): - # z direction - scale = abs(node.Fz) / max_force * h - sol = self.__arrow_patch_values(0, node.Fz, node, scale) + if not math.isclose(node.Fy, 0, rel_tol=1e-5, abs_tol=1e-9): + # y direction + scale = abs(node.Fy) / max_force * h + sol = self.__arrow_patch_values(0, node.Fy, node, scale) x = sol[0] y = sol[1] len_x = sol[2] @@ -1121,26 +1121,26 @@ def reaction_force( self.axes[axes_i].text( x, y, - f"R={round(node.Fz, 2)}", + f"R={round(node.Fy, 2)}", color="k", fontsize=9, zorder=10, ) - if not math.isclose(node.Ty, 0, rel_tol=1e-5, abs_tol=1e-9): + if not math.isclose(node.Tz, 0, rel_tol=1e-5, abs_tol=1e-9): # '$...$': render the strings using mathtext - if node.Ty > 0: + if node.Tz > 0: self.axes[axes_i].plot( node.vertex.x, - -node.vertex.z, + node.vertex.y, marker=r"$\circlearrowleft$", ms=25, color="orange", ) - if node.Ty < 0: + if node.Tz < 0: self.axes[axes_i].plot( node.vertex.x, - -node.vertex.z, + node.vertex.y, marker=r"$\circlearrowright$", ms=25, color="orange", @@ -1149,8 +1149,8 @@ def reaction_force( if verbosity == 0: self.axes[axes_i].text( node.vertex.x + h * 0.2, - -node.vertex.z + h * 0.2, - f"T={round(node.Ty, 2)}", + node.vertex.y + h * 0.2, + f"T={round(node.Tz, 2)}", color="k", fontsize=9, zorder=10, @@ -1197,7 +1197,7 @@ def displacements( # pylint: disable=arguments-renamed map( lambda el: max( abs(el.node_1.ux), - abs(el.node_1.uz), + abs(el.node_1.uy), el.max_deflection or 0, ) if el.type == "general" diff --git a/anastruct/fem/plotter/values.py b/anastruct/fem/plotter/values.py index b94f848..dfa4106 100644 --- a/anastruct/fem/plotter/values.py +++ b/anastruct/fem/plotter/values.py @@ -80,7 +80,7 @@ def displacements( max_displacement = max( map( lambda el: max( - abs(el.node_1.ux), abs(el.node_1.uz), el.max_deflection or 0 + abs(el.node_1.ux), abs(el.node_1.uy), el.max_deflection or 0 ) if el.type == "general" else 0, @@ -110,8 +110,8 @@ def bending_moment(self, factor: Optional[float]) -> Tuple[np.ndarray, np.ndarra max_moment = max( map( lambda el: max( - abs(el.node_1.Ty), - abs(el.node_2.Ty), + abs(el.node_1.Tz), + abs(el.node_2.Tz), abs(((el.all_qp_load[0] + el.all_qp_load[1]) / 16) * el.l**2), ), self.system.element_map.values(), diff --git a/anastruct/fem/postprocess.py b/anastruct/fem/postprocess.py index 433ba31..504d586 100644 --- a/anastruct/fem/postprocess.py +++ b/anastruct/fem/postprocess.py @@ -36,25 +36,25 @@ def node_results_system(self) -> None: self.system.node_map[k].reset() if k in self.system.loads_moment: - self.system.node_map[k].Ty += self.system.loads_moment[k] + self.system.node_map[k].Tz += self.system.loads_moment[k] if k in self.system.loads_point: - Fx, Fz = self.system.loads_point[k] + Fx, Fy = self.system.loads_point[k] self.system.node_map[k].Fx += Fx - self.system.node_map[k].Fz += Fz + self.system.node_map[k].Fy += Fy for vi in v: node = vi.node_map[k] self.system.node_map[k] -= node assert node.ux is not None - assert node.uz is not None - assert node.phi_y is not None + assert node.uy is not None + assert node.phi_z is not None # The displacements are not summarized. Should be assigned only once self.system.node_map[k].ux = -node.ux - self.system.node_map[k].uz = -node.uz - self.system.node_map[k].phi_y = -node.phi_y + self.system.node_map[k].uy = -node.uy + self.system.node_map[k].phi_z = -node.phi_z def reaction_forces(self) -> None: """Determines the reaction forces on the system level. @@ -71,21 +71,21 @@ def reaction_forces(self) -> None: supports.append(node.id) for node, _ in self.system.supports_spring_x: supports.append(node.id) - for node, _ in self.system.supports_spring_z: - supports.append(node.id) for node, _ in self.system.supports_spring_y: supports.append(node.id) + for node, _ in self.system.supports_spring_z: + supports.append(node.id) for node_id in supports: node = self.system.node_map[node_id] node = copy.copy(node) self.system.reaction_forces[node_id] = node node.Fx *= -1 - node.Fz *= -1 - node.Ty *= -1 + node.Fy *= -1 + node.Tz *= -1 node.ux = 0.0 - node.uz = 0.0 - node.phi_y = 0.0 + node.uy = 0.0 + node.phi_z = 0.0 def element_results(self) -> None: """Determines the element results for all elements in the system on element level.""" @@ -115,14 +115,14 @@ def node_results(self, element: "Element") -> None: id=element.node_id1, Fx=element.element_force_vector[0] + element.element_primary_force_vector[0], - Fz=element.element_force_vector[1] + Fy=element.element_force_vector[1] + element.element_primary_force_vector[1], - Ty=element.element_force_vector[2] + element.element_primary_force_vector[2] + Tz=element.element_force_vector[2] + element.element_primary_force_vector[2] if not hinge1 else 0, ux=element.element_displacement_vector[0], - uz=element.element_displacement_vector[1], - phi_y=element.element_displacement_vector[2] if not hinge1 else 0, + uy=element.element_displacement_vector[1], + phi_z=element.element_displacement_vector[2] if not hinge1 else 0, hinge=hinge1, ) @@ -130,14 +130,14 @@ def node_results(self, element: "Element") -> None: id=element.node_id2, Fx=element.element_force_vector[3] + element.element_primary_force_vector[3], - Fz=element.element_force_vector[4] + Fy=element.element_force_vector[4] + element.element_primary_force_vector[4], - Ty=element.element_force_vector[5] + element.element_primary_force_vector[5] + Tz=element.element_force_vector[5] + element.element_primary_force_vector[5] if not hinge2 else 0, ux=element.element_displacement_vector[3], - uz=element.element_displacement_vector[4], - phi_y=element.element_displacement_vector[5] if not hinge2 else 0, + uy=element.element_displacement_vector[4], + phi_z=element.element_displacement_vector[5] if not hinge2 else 0, hinge=hinge2, ) @@ -150,13 +150,13 @@ def node_results(self, element: "Element") -> None: c = np.cos(angle) s = np.sin(angle) Fx = node.Fx - Fz = node.Fz + Fy = node.Fy ux = node.ux - uz = node.uz - node.Fz = c * Fz + s * Fx - node.Fx = -(c * Fx + s * Fz) - node.ux = c * ux + s * uz - node.uz = c * uz + s * ux + uy = node.uy + node.Fy = c * Fy + s * Fx + node.Fx = -(c * Fx + s * Fy) + node.ux = c * ux + s * uy + node.uy = c * uy + s * ux @staticmethod def determine_axial_force(element: "Element", con: int) -> None: @@ -166,10 +166,10 @@ def determine_axial_force(element: "Element", con: int) -> None: element (Element): Element for which to determine axial force con (int): Number of points to determine axial force """ - N_1 = (math.sin(element.angle) * element.node_1.Fz) + -( + N_1 = (math.sin(element.angle) * element.node_1.Fy) + -( math.cos(element.angle) * element.node_1.Fx ) - N_2 = -(math.sin(element.angle) * element.node_2.Fz) + ( + N_2 = -(math.sin(element.angle) * element.node_2.Fy) + ( math.cos(element.angle) * element.node_2.Fx ) @@ -197,11 +197,11 @@ def determine_bending_moment(element: "Element", con: int) -> None: element (Element): Element for which to determine bending moment con (int): """ - dT = -(element.node_2.Ty + element.node_1.Ty) # T2 - (-T1) + dT = -(element.node_2.Tz + element.node_1.Tz) # T2 - (-T1) iteration_factor = np.linspace(0, 1, con) x = iteration_factor * element.l - m_val = element.node_1.Ty + iteration_factor * dT + m_val = element.node_1.Tz + iteration_factor * dT if element.all_qp_load: qi = element.all_qp_load[0] q = element.all_qp_load[1] diff --git a/anastruct/fem/system.py b/anastruct/fem/system.py index 98788a2..87b2255 100644 --- a/anastruct/fem/system.py +++ b/anastruct/fem/system.py @@ -80,6 +80,7 @@ def __init__( EI: float = 5e3, load_factor: float = 1.0, mesh: int = 50, + invert_y_loads: bool = True, ): """Create a new structure @@ -89,6 +90,8 @@ def __init__( EI (float, optional): Bending stiffness. Defaults to 5e3. load_factor (float, optional): Load factor by which to multiply all loads. Defaults to 1.0. mesh (int, optional): Number of mesh elements, only used for plotting. Defaults to 50. + invert_y_loads (bool, optional): Whether to invert the y-direction of the loads, such that a positive + Fy load will be in the direction of gravity. Defaults to True. """ # init object self.post_processor = post_sl(self) @@ -99,7 +102,8 @@ def __init__( self.EA = EA self.EI = EI self.figsize = figsize - self.orientation_cs = -1 # needed for the loads directions + # whether to invert the y-direction of the loads + self.orientation_cs = -1 if invert_y_loads else 1 # structure system self.element_map: Dict[ @@ -125,8 +129,8 @@ def __init__( self.internal_hinges: List[Node] = [] self.supports_roll: List[Node] = [] self.supports_spring_x: List[Tuple[Node, bool]] = [] - self.supports_spring_z: List[Tuple[Node, bool]] = [] self.supports_spring_y: List[Tuple[Node, bool]] = [] + self.supports_spring_z: List[Tuple[Node, bool]] = [] self.supports_roll_direction: List[Literal[1, 2]] = [] self.inclined_roll: Dict[ int, float @@ -946,11 +950,11 @@ def solve( index_node_1 = (el.node_1.id - 1) * 3 index_node_2 = (el.node_2.id - 1) * 3 - # node 1 ux, uz, phi + # node 1 ux, uy, phi el.element_displacement_vector[:3] = self.system_displacement_vector[ index_node_1 : index_node_1 + 3 ] - # node 2 ux, uz, phi + # node 2 ux, uy, phi el.element_displacement_vector[3:] = self.system_displacement_vector[ index_node_2 : index_node_2 + 3 ] @@ -1133,7 +1137,7 @@ def add_support_spring( Args: node_id (Union[Sequence[int], int]): Represents the nodes ID translation (Union[Sequence[AxisNumber], AxisNumber]): Represents the prevented translation or rotation. - 1 = translation in x, 2 = translation in z, 3 = rotation in y + 1 = translation in x, 2 = translation in y, 3 = rotation about z k (Union[Sequence[float], float]): Stiffness of the spring roll (Union[Sequence[bool], bool], optional): If set to True, only the translation of the spring is controlled. Defaults to False. @@ -1166,9 +1170,14 @@ def add_support_spring( if translation_ == 1: self.supports_spring_x.append((self.node_map[id_], roll_)) elif translation_ == 2: + self.supports_spring_y.append((self.node_map[id_], roll_)) + elif translation_ == 3: self.supports_spring_z.append((self.node_map[id_], roll_)) else: - self.supports_spring_y.append((self.node_map[id_], roll_)) + raise FEMException( + "Invalid translation", + f"Translation should be 1, 2 or 3, but is {translation_}", + ) def q_load( self, @@ -1273,6 +1282,7 @@ def point_load( Fx: Union[float, Sequence[float]] = 0.0, Fy: Union[float, Sequence[float]] = 0.0, rotation: Union[float, Sequence[float]] = 0.0, + Fz: Optional[Union[float, Sequence[float]]] = None, ) -> None: """Apply a point load to a node. @@ -1286,6 +1296,8 @@ def point_load( Raises: FEMException: Point loads may not be placed at the location of inclined roller supports """ + if isinstance(Fy, (int, float)) and Fy == 0.0 and Fz is not None: + Fy = Fz # for backwards compatibility with old y/z axes behaviour n = len(node_id) if isinstance(node_id, Sequence) else 1 node_id = arg_to_list(node_id, n) Fx = arg_to_list(Fx, n) @@ -1313,21 +1325,26 @@ def point_load( ) def moment_load( - self, node_id: Union[int, Sequence[int]], Ty: Union[float, Sequence[float]] + self, + node_id: Union[int, Sequence[int]], + Tz: Union[float, Sequence[float]] = 0.0, + Ty: Optional[Union[float, Sequence[float]]] = None, ) -> None: """Apply a moment load to a node. Args: node_id (Union[int, Sequence[int]]): The node ID to which to apply the load - Ty (Union[float, Sequence[float]]): Moment load (about the global Y direction) to apply + Tz (Union[float, Sequence[float]]): Moment load (about the global Y direction) to apply """ + if isinstance(Tz, (int, float)) and Tz == 0.0 and Ty is not None: + Tz = Ty # for backwards compatibility with old y/z axes behaviour n = len(node_id) if isinstance(node_id, Sequence) else 1 node_id = arg_to_list(node_id, n) - Ty = arg_to_list(Ty, n) + Tz = arg_to_list(Tz, n) for i, node_idi in enumerate(node_id): id_ = _negative_index_to_id(node_idi, self.node_map.keys()) - self.loads_moment[id_] = Ty[i] * self.load_factor + self.loads_moment[id_] = Tz[i] * self.load_factor def show_structure( self, @@ -1549,9 +1566,9 @@ def get_node_results_system( Returns: Union[ List[Dict[str, Union[int, float]]], Dict[str, Union[int, float]] ]: If node_id == 0, returns a list containing tuples with the results: - [(id, Fx, Fy, Ty, ux, uy, phi_y), (id, Fx, Fy...), () .. ] + [(id, Fx, Fy, Tz, ux, uy, phi_z), (id, Fx, Fy...), () .. ] If node_id > 0, returns a dict with the results: - {"id": id, "Fx": Fx, "Fy": Fy, "Ty": Ty, "ux": ux, "uy": uy, "phi_y": phi_y} + {"id": id, "Fx": Fx, "Fy": Fy, "Tz": Tz, "ux": ux, "uy": uy, "phi_z": phi_z} """ result_list = [] if node_id != 0: @@ -1560,22 +1577,22 @@ def get_node_results_system( return { "id": node.id, "Fx": node.Fx, - "Fy": node.Fy, - "Ty": node.Ty, + "Fy": node.Fy_neg, + "Tz": node.Tz, "ux": node.ux, - "uy": -node.uz, - "phi_y": node.phi_y, + "uy": -node.uy, + "phi_z": node.phi_z, } for node in self.node_map.values(): result_list.append( { "id": node.id, "Fx": node.Fx, - "Fy": node.Fy, - "Ty": node.Ty, + "Fy": node.Fy_neg, + "Tz": node.Tz, "ux": node.ux, - "uy": -node.uz, - "phi_y": node.phi_y, + "uy": -node.uy, + "phi_z": node.phi_z, } ) return result_list @@ -1591,8 +1608,8 @@ def get_node_displacements( Returns: Union[List[Dict[str, Any]], Dict[str, Any]]: If node_id == 0, returns a list containing - tuples with the results: [(id, ux, uy, phi_y), (id, ux, uy, phi_y), ... (id, ux, uy, phi_y) ] - If node_id > 0, returns a dict with the results: {"id": id, "ux": ux, "uy": uy, "phi_y": phi_y} + tuples with the results: [(id, ux, uy, phi_z), (id, ux, uy, phi_z), ... (id, ux, uy, phi_z) ] + If node_id > 0, returns a dict with the results: {"id": id, "ux": ux, "uy": uy, "phi_z": phi_z} """ result_list = [] if node_id != 0: @@ -1601,16 +1618,16 @@ def get_node_displacements( return { "id": node.id, "ux": -node.ux, - "uy": node.uz, # - * - = + - "phi_y": node.phi_y, + "uy": node.uy, # - * - = + + "phi_z": node.phi_z, } for node in self.node_map.values(): result_list.append( { "id": node.id, "ux": -node.ux, - "uy": node.uz, # - * - = + - "phi_y": node.phi_y, + "uy": node.uy, # - * - = + + "phi_z": node.phi_z, } ) return result_list @@ -1781,11 +1798,11 @@ def minmax_array(array: np.ndarray) -> Union[float, Tuple[float]]: ] raise NotImplementedError("unit must be 'shear', 'moment', or 'axial'") - def get_node_result_range(self, unit: Literal["ux", "uy", "phi_y"]) -> List[float]: + def get_node_result_range(self, unit: Literal["ux", "uy", "phi_z"]) -> List[float]: """Get the node results. Returns a list with the node results for a certain unit. Args: - unit (str): "uy", "ux", or "phi_y" + unit (str): "uy", "ux", or "phi_z" Raises: NotImplementedError: If the unit is not implemented. @@ -1794,11 +1811,11 @@ def get_node_result_range(self, unit: Literal["ux", "uy", "phi_y"]) -> List[floa List[float]: List with the node results for a certain unit. """ if unit == "uy": - return [node.uz for node in self.node_map.values()] # - * - = + + return [node.uy for node in self.node_map.values()] # - * - = + if unit == "ux": return [-node.ux for node in self.node_map.values()] - if unit == "phi_y": - return [node.phi_y for node in self.node_map.values()] + if unit == "phi_z": + return [node.phi_z for node in self.node_map.values()] raise NotImplementedError def find_node_id(self, vertex: Union[Vertex, Sequence[float]]) -> Optional[int]: @@ -1829,22 +1846,22 @@ def find_node_id(self, vertex: Union[Vertex, Sequence[float]]) -> Optional[int]: def nodes_range( self, dimension: "Dimension" ) -> List[Union[float, Tuple[float, float], None]]: - """Retrieve a list with coordinates x or z (y). + """Retrieve a list with coordinates x or y. Args: - dimension (str): "both", 'x', 'y' or 'z' + dimension (str): "both", 'x' or 'y' Returns: - List[Union[float, Tuple[float, float], None]]: List with coordinates x or z (y) + List[Union[float, Tuple[float, float], None]]: List with coordinates x or y """ return list( map( lambda x: x.vertex.x if dimension == "x" - else x.vertex.z - if dimension == "z" else x.vertex.y if dimension == "y" + else x.vertex.y_neg + if dimension == "y_neg" else (x.vertex.x, x.vertex.y) if dimension == "both" else None, @@ -1869,7 +1886,7 @@ def nearest_node( np.argmin( np.sqrt( (np.array(self.nodes_range("x")) - val[0]) ** 2 - + (np.array(self.nodes_range("y")) - val[1]) ** 2 + + (np.array(self.nodes_range("y_neg")) - val[1]) ** 2 ) ) ) diff --git a/anastruct/fem/system_components/assembly.py b/anastruct/fem/system_components/assembly.py index f75c41a..0153cec 100644 --- a/anastruct/fem/system_components/assembly.py +++ b/anastruct/fem/system_components/assembly.py @@ -19,7 +19,7 @@ def set_force_vector( Args: system (SystemElements): System to which the force vector is applied force_list (List[Tuple[int, AxisNumber, float]]): List of tuples containing - the node id, direction (1 = x, 2 = z, 3 = y), and force magnitude + the node id, direction (1 = x, 2 = y, 3 = z), and force magnitude Returns: np.ndarray: System force vector with the applied forces on the nodes @@ -52,8 +52,8 @@ def apply_moment_load(system: "SystemElements") -> None: Args: system (SystemElements): System to which the moment load is applied """ - for node_id, Ty in system.loads_moment.items(): - set_force_vector(system, [(node_id, 3, Ty)]) + for node_id, Tz in system.loads_moment.items(): + set_force_vector(system, [(node_id, 3, Tz)]) def apply_point_load(system: "SystemElements") -> None: @@ -63,13 +63,13 @@ def apply_point_load(system: "SystemElements") -> None: system (SystemElements): System to which the point load is applied """ for node_id in system.loads_point: - Fx, Fz = system.loads_point[node_id] + Fx, Fy = system.loads_point[node_id] # system force vector. set_force_vector( system, [ (node_id, 1, Fx), - (node_id, 2, Fz), + (node_id, 2, Fy), ], ) @@ -107,15 +107,15 @@ def apply_perpendicular_q_load(system: "SystemElements") -> None: rleft_x = rleft * math.sin(element.a1) rright_x = rright * math.sin(element.a2) - rleft_z = rleft * math.cos(element.a1) - rright_z = rright * math.cos(element.a2) + rleft_y = rleft * math.cos(element.a1) + rright_y = rright * math.cos(element.a2) if element.type == "truss": left_moment = 0 right_moment = 0 primary_force = np.array( - [rleft_x, rleft_z, left_moment, rright_x, rright_z, right_moment] + [rleft_x, rleft_y, left_moment, rright_x, rright_y, right_moment] ) element.element_primary_force_vector -= primary_force @@ -149,19 +149,19 @@ def apply_parallel_qn_load(system: "SystemElements") -> None: rleft_x = -rleft * math.cos(element.a1) rright_x = -rright * math.cos(element.a2) - rleft_z = rleft * math.sin(element.a1) - rright_z = rright * math.sin(element.a2) + rleft_y = rleft * math.sin(element.a1) + rright_y = rright * math.sin(element.a2) element.element_primary_force_vector[0] -= rleft_x - element.element_primary_force_vector[1] -= rleft_z + element.element_primary_force_vector[1] -= rleft_y element.element_primary_force_vector[3] -= rright_x - element.element_primary_force_vector[4] -= rright_z + element.element_primary_force_vector[4] -= rright_y set_force_vector( system, [ - (element.node_1.id, 2, rleft_z), - (element.node_2.id, 2, rright_z), + (element.node_1.id, 2, rleft_y), + (element.node_2.id, 2, rright_y), (element.node_1.id, 1, rleft_x), (element.node_2.id, 1, rright_x), ], @@ -206,14 +206,14 @@ def assemble_system_matrix( # system matrix [K] # # [fx 1] [K | \ node 1 starts at row 1 - # |fz 1] | K | / - # |Ty 1] | K | / + # |Fy 1] | K | / + # |Tz 1] | K | / # |fx 2] | K | \ node 2 starts at row 4 - # |fz 2] | K | / - # |Ty 2] | K | / + # |Fy 2] | K | / + # |Tz 2] | K | / # |fx 3] | K | \ node 3 starts at row 7 - # |fz 3] | K | / - # [Ty 3] [ K] / + # |Fy 3] | K | / + # [Tz 3] [ K] / # # n n n # o o o @@ -249,7 +249,7 @@ def set_displacement_vector( Args: system (SystemElements): System to which the displacement vector is applied nodes_list (List[Tuple[int, AxisNumber]]): List of tuples containing - the node id and the direction (1 = x, 2 = z, 3 = y) + the node id and the direction (1 = x, 2 = y, 3 = z) Raises: IndexError: This often occurs if you set supports before the all the elements are @@ -341,11 +341,11 @@ def process_supports(system: "SystemElements") -> None: if not roll: set_displacement_vector(system, [(node.id, 2)]) - for node, roll in system.supports_spring_z: + for node, roll in system.supports_spring_y: if not roll: set_displacement_vector(system, [(node.id, 1)]) - for node, roll in system.supports_spring_y: + for node, roll in system.supports_spring_z: if not roll: set_displacement_vector(system, [(node.id, 1), (node.id, 2)]) diff --git a/anastruct/fem/system_components/solver.py b/anastruct/fem/system_components/solver.py index d712d0f..04b8ce6 100644 --- a/anastruct/fem/system_components/solver.py +++ b/anastruct/fem/system_components/solver.py @@ -39,12 +39,12 @@ def stiffness_adaptation( for node_no, mp in v.items(): if node_no == 1: - # Fast Ty + # Fast Tz m_e = ( el.element_force_vector[2] + el.element_primary_force_vector[2] ) else: - # Fast Ty + # Fast Tz m_e = ( el.element_force_vector[5] + el.element_primary_force_vector[5] ) diff --git a/anastruct/fem/system_components/util.py b/anastruct/fem/system_components/util.py index 7ff0810..ccfd8c4 100644 --- a/anastruct/fem/system_components/util.py +++ b/anastruct/fem/system_components/util.py @@ -249,15 +249,15 @@ def force_elements_orientation( """ # determine the angle of the element with the global x-axis delta_x = point_2.x - point_1.x - delta_z = point_2.z - point_1.z # minus sign to work with an opposite z-axis - angle = -angle_x_axis(delta_x, delta_z) + delta_y = point_2.y - point_1.y # minus sign to work with an opposite y-axis + angle = angle_x_axis(delta_x, delta_y) if delta_x < 0: # switch points point_1, point_2 = point_2, point_1 node_id1, node_id2 = node_id2, node_id1 - angle = -angle_x_axis(-delta_x, -delta_z) + angle = angle_x_axis(-delta_x, -delta_y) if spring is not None: assert isinstance( diff --git a/anastruct/fem/util/load.py b/anastruct/fem/util/load.py index a5ee555..64c8ac8 100644 --- a/anastruct/fem/util/load.py +++ b/anastruct/fem/util/load.py @@ -71,16 +71,16 @@ def point_load( } def moment_load( - self, node_id: Union[int, Sequence[int]], Ty: Union[float, Sequence[float]] + self, node_id: Union[int, Sequence[int]], Tz: Union[float, Sequence[float]] ) -> None: """ Apply a moment on a node. :param node_id: (int/ list) Nodes ID. - :param Ty: (flt/ list) Moments acting on the node. + :param Tz: (flt/ list) Moments acting on the node. """ self.c += 1 - self.spec[f"moment_load-{self.c}"] = {"node_id": node_id, "Ty": Ty} + self.spec[f"moment_load-{self.c}"] = {"node_id": node_id, "Tz": Tz} def dead_load( self, element_id: Union[int, Sequence[int]], g: Union[float, Sequence[float]] diff --git a/anastruct/types.py b/anastruct/types.py index d2413de..22c9d90 100644 --- a/anastruct/types.py +++ b/anastruct/types.py @@ -6,7 +6,7 @@ from anastruct.vertex import Vertex AxisNumber = Literal[1, 2, 3] -Dimension = Literal["x", "y", "z", "both"] +Dimension = Literal["x", "y", "y_neg", "both"] ElementType = Literal["general", "truss"] LoadDirection = Literal["element", "x", "y", "parallel", "perpendicular", "angle"] MpType = Dict[Literal[1, 2], float] diff --git a/anastruct/vertex.py b/anastruct/vertex.py index bae2c0c..458985b 100644 --- a/anastruct/vertex.py +++ b/anastruct/vertex.py @@ -63,11 +63,11 @@ def y(self) -> float: return float(self.coordinates[1]) @property - def z(self) -> float: - """Z coordinate (equals negative of Y coordinate) + def y_neg(self) -> float: + """Y negative coordinate (equals negative of Y coordinate) Returns: - float: Z coordinate + float: Y_neg coordinate """ return float(self.coordinates[1] * -1) @@ -88,16 +88,16 @@ def unit(self) -> Vertex: return (1 / self.modulus()) * self def displace_polar( - self, alpha: float, radius: float, inverse_z_axis: bool = False + self, alpha: float, radius: float, inverse_y_axis: bool = False ) -> None: """Displace the Vertex by a polar coordinate Args: alpha (float): Angle in radians radius (float): Radius - inverse_z_axis (bool, optional): Return a Z coordinate (negative of Y coordinate)?. Defaults to False. + inverse_y_axis (bool, optional): Return a negative Y coordinate. Defaults to False. """ - if inverse_z_axis: + if inverse_y_axis: self.coordinates[0] += math.cos(alpha) * radius self.coordinates[1] -= math.sin(alpha) * radius else: diff --git a/tests/test_e2e.py b/tests/test_e2e.py index 0b1eea0..8bdd280 100644 --- a/tests/test_e2e.py +++ b/tests/test_e2e.py @@ -102,7 +102,7 @@ def it_results_in_correct_solution(): def it_results_in_correct_node_3_displacements(): assert system.get_node_displacements(3)["ux"] == approx(0.0575402011335) assert system.get_node_displacements(3)["uy"] == approx(-0.0128738197933) - assert system.get_node_displacements(3)["phi_y"] == approx(0.0021605118130) + assert system.get_node_displacements(3)["phi_z"] == approx(0.0021605118130) def context_example_4(): @pspec_context("Example 4") @@ -112,7 +112,7 @@ def describe(): system = SystemElements() system.add_element(location=[[0, 0], [5, 0]], EA=5e9, EI=8000, spring={2: 0}) system.add_element(location=[[5, 0], [5, 5]], EA=5e9, EI=4000) - system.moment_load(Ty=10, node_id=3) + system.moment_load(Tz=10, node_id=3) system.add_support_hinged(node_id=1) system.add_support_hinged(node_id=3) @@ -134,7 +134,7 @@ def describe(): system = SystemElements() system.add_element(location=[[0, 0], [5, 0]], EA=5e9, EI=8000) system.add_element(location=[[5, 0], [5, -5]], EA=5e9, EI=4000) - system.moment_load(Ty=10, node_id=3) + system.moment_load(Tz=10, node_id=3) system.add_support_hinged(node_id=1) system.add_support_hinged(node_id=3) @@ -163,7 +163,7 @@ def describe(): system.solve() def it_results_in_correct_in_correct_moment_at_support(): - assert system.get_node_results_system(1)["Ty"] == approx(-61.25, rel=1e-3) + assert system.get_node_results_system(1)["Tz"] == approx(-61.25, rel=1e-3) def context_example_7_rotational_spring(): @pspec_context("Example 7: Test the rotational springs") @@ -212,10 +212,10 @@ def describe(): def it_results_in_correct_moment_loads(SS_12): SS_12.solve() - assert SS_12.get_node_results_system(1)["Ty"] == approx(6.66667) - assert SS_12.get_node_results_system(2)["Ty"] == approx(0) - assert SS_12.get_node_results_system(3)["Ty"] == approx(0) - assert SS_12.get_node_results_system(4)["Ty"] == approx(-6.66667) + assert SS_12.get_node_results_system(1)["Tz"] == approx(6.66667) + assert SS_12.get_node_results_system(2)["Tz"] == approx(0) + assert SS_12.get_node_results_system(3)["Tz"] == approx(0) + assert SS_12.get_node_results_system(4)["Tz"] == approx(-6.66667) def context_example_13(): @pspec_context("Example 13: X-axis loads, with system nodes having 0 Fx loads") @@ -365,7 +365,7 @@ def describe(): def it_results_in_same_forces(): assert u1["Fx"] == approx(u2["Fx"]) assert u1["Fy"] == approx(u2["Fy"]) - assert u1["Ty"] == approx(u2["Ty"]) + assert u1["Tz"] == approx(u2["Tz"]) def context_inclined_roller_reactions(): @pspec_context("Inclined roller forces are calculated correctly") @@ -453,8 +453,8 @@ def describe(): def it_results_in_correct_reactions(): assert system.get_node_results_system(1)["Fx"] == approx(-12.5) assert system.get_node_results_system(1)["Fy"] == approx(-10) - assert system.get_node_results_system(1)["Ty"] == approx(0) - assert system.get_node_results_system(3)["Ty"] == approx(0) + assert system.get_node_results_system(1)["Tz"] == approx(0) + assert system.get_node_results_system(3)["Tz"] == approx(0) def context_multiple_elements_spacing(): @pspec_context( @@ -506,10 +506,10 @@ def describe(): def it_results_in_correct_displacements(): assert system.get_node_results_system(2)["ux"] == approx(0.00833333) assert system.get_node_results_system(2)["uy"] == approx(0) - assert system.get_node_results_system(2)["phi_y"] == approx(0) + assert system.get_node_results_system(2)["phi_z"] == approx(0) def it_results_in_correct_reaction(): - assert system.get_node_results_system(2)["Ty"] == approx(166.6667) + assert system.get_node_results_system(2)["Tz"] == approx(166.6667) def context_rotational_support(): @pspec_context("Test addition of rotation-only supports") @@ -527,10 +527,10 @@ def describe(): def it_results_in_correct_displacements(): assert system.get_node_results_system(2)["ux"] == approx(0.00666667) assert system.get_node_results_system(2)["uy"] == approx(0.00833333) - assert system.get_node_results_system(2)["phi_y"] == approx(0) + assert system.get_node_results_system(2)["phi_z"] == approx(0) def it_results_in_correct_reaction(): - assert system.get_node_results_system(2)["Ty"] == approx(-166.6667) + assert system.get_node_results_system(2)["Tz"] == approx(-166.6667) def context_load_cases(): @pspec_context("Test a trivial load case") @@ -566,7 +566,7 @@ def describe(): system.q_load(q=(0.1, 1), element_id=2, direction="element") system.point_load(node_id=1, Fx=15) system.point_load(node_id=2, Fy=-5) - system.moment_load(node_id=3, Ty=-7) + system.moment_load(node_id=3, Tz=-7) system.solve() def it_has_correct_trapezoidal_load(): @@ -578,8 +578,8 @@ def it_results_in_correct_reactions(): assert system.get_node_results_system(1)["Fx"] == approx(15) assert system.get_node_results_system(2)["Fy"] == approx(-4.45) assert system.get_node_results_system(2)["Fx"] == approx(-0.825) - assert system.get_node_results_system(1)["Ty"] == approx(0) - assert system.get_node_results_system(2)["Ty"] == approx(-5.8625) + assert system.get_node_results_system(1)["Tz"] == approx(0) + assert system.get_node_results_system(2)["Tz"] == approx(-5.8625) def context_internal_hinge_symmetry(): @pspec_context("Test bug fix for asymmetric results with internal hinges") @@ -924,7 +924,7 @@ def describe(): def it_results_in_correct_reactions(): assert system.get_node_results_system(1)["Fy"] == approx(3 * w * l / 8) assert system.get_node_results_system(2)["Fy"] == approx(5 * w * l / 8) - assert system.get_node_results_system(2)["Ty"] == approx(-w * (l**2) / 8) + assert system.get_node_results_system(2)["Tz"] == approx(-w * (l**2) / 8) def it_results_in_correct_deflections(): assert system.get_element_results(1)["wmax"] == approx( @@ -955,7 +955,7 @@ def describe(): def it_results_in_correct_reactions(): assert system.get_node_results_system(1)["Fy"] == approx(5 * p / 16) assert system.get_node_results_system(4)["Fy"] == approx(11 * p / 16) - assert system.get_node_results_system(4)["Ty"] == approx(-3 * p * l / 16) + assert system.get_node_results_system(4)["Tz"] == approx(-3 * p * l / 16) def it_results_in_correct_deflections(): assert system.get_node_results_system(2)["uy"] == approx( @@ -981,8 +981,8 @@ def describe(): def it_results_in_correct_reactions(): assert system.get_node_results_system(1)["Fy"] == approx(w * l / 2) assert system.get_node_results_system(2)["Fy"] == approx(w * l / 2) - assert system.get_node_results_system(1)["Ty"] == approx(w * l**2 / 12) - assert system.get_node_results_system(2)["Ty"] == approx(-w * l**2 / 12) + assert system.get_node_results_system(1)["Tz"] == approx(w * l**2 / 12) + assert system.get_node_results_system(2)["Tz"] == approx(-w * l**2 / 12) def it_results_in_correct_deflections(): assert system.get_element_results(1)["wmax"] == approx( diff --git a/tests/test_plotter.py b/tests/test_plotter.py index 6a72793..84bfc7f 100644 --- a/tests/test_plotter.py +++ b/tests/test_plotter.py @@ -1,5 +1,7 @@ import unittest + import numpy as np + from anastruct.fem import system as se @@ -56,7 +58,7 @@ def test_example_4(self): system = se.SystemElements() system.add_element(location=[[0, 0], [5, 0]], EA=5e9, EI=8000, hinge=2) system.add_element(location=[[5, 0], [5, 5]], EA=5e9, EI=4000) - system.moment_load(Ty=10, node_id=3) + system.moment_load(Tz=10, node_id=3) system.add_support_hinged(node_id=1) system.add_support_hinged(node_id=3) system.show_structure(show=self.show) @@ -70,7 +72,7 @@ def test_example_5(self): system = se.SystemElements() system.add_element(location=[[0, 0], [5, 0]], EA=5e9, EI=8000) system.add_element(location=[[5, 0], [5, 5]], EA=5e9, EI=4000) - system.moment_load(Ty=10, node_id=3) + system.moment_load(Tz=10, node_id=3) system.add_support_hinged(node_id=1) system.add_support_hinged(node_id=3) system.solve() diff --git a/tests/test_sectionbase.py b/tests/test_sectionbase.py index 53e8f0d..afc112d 100644 --- a/tests/test_sectionbase.py +++ b/tests/test_sectionbase.py @@ -1,6 +1,7 @@ import unittest -from anastruct.sectionbase import section_base + from anastruct.fem import system +from anastruct.sectionbase import section_base class SimpleUnitTest(unittest.TestCase): @@ -97,7 +98,7 @@ def test_sectionbase_steel_section_self_weight_reaction(self): ss.add_support_hinged(1) ss.add_support_hinged(2) ss.solve() - self.assertAlmostEqual(-4224.24, ss.reaction_forces[1].Fz) + self.assertAlmostEqual(-4224.24, ss.reaction_forces[1].Fy) def test_rectangle_section_deflection(self): ss = system.SystemElements() @@ -125,7 +126,7 @@ def test_rectangle_section_self_weight_reaction(self): ss.add_support_hinged(1) ss.add_support_hinged(2) ss.solve() - self.assertAlmostEqual(-100, ss.reaction_forces[1].Fz) + self.assertAlmostEqual(-100, ss.reaction_forces[1].Fy) def test_circle_section_self_weight_reaction(self): ss = system.SystemElements() @@ -133,7 +134,7 @@ def test_circle_section_self_weight_reaction(self): ss.add_support_hinged(1) ss.add_support_hinged(2) ss.solve() - self.assertAlmostEqual(-314.15926535, ss.reaction_forces[1].Fz) + self.assertAlmostEqual(-314.15926535, ss.reaction_forces[1].Fy) def test_show_available_sections(self): sections = section_base.available_sections