From c985c20e70e51b927083f4d8d2fb920925b6811f Mon Sep 17 00:00:00 2001 From: Brooks Smith Date: Mon, 30 Oct 2023 10:19:39 +1100 Subject: [PATCH 1/4] Remove orphaned nodes in remove_element() --- anastruct/fem/system.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/anastruct/fem/system.py b/anastruct/fem/system.py index fdd455f..013486d 100644 --- a/anastruct/fem/system.py +++ b/anastruct/fem/system.py @@ -477,12 +477,21 @@ def remove_element(self, element_id: int) -> None: element_id (int): ID of the element to remove """ element = self.element_map[element_id] - for node_id in (element.node_id1, element.node_id2): + node_id1 = element.node_id1 + node_id2 = element.node_id2 + + # Remove element object itself from node maps + for node_id in (node_id1, node_id2): self.node_element_map[node_id].remove(element) if len(self.node_element_map[node_id]) == 0: self.node_element_map.pop(node_id) self.node_map[node_id].elements.pop(element_id) + # Check if node is now orphaned, and remove it if so + if len(self.node_map[node_id].elements) == 0: + self._vertices.pop(self.node_map[node_id].vertex) + self.node_map.pop(node_id) + # Remove element_id self.element_map.pop(element_id) if element_id in self.loads_q: self.loads_q.pop(element_id) From a6e33eeaebd288c9bef9a304e3410f923316d02c Mon Sep 17 00:00:00 2001 From: Brooks Smith Date: Mon, 30 Oct 2023 10:39:12 +1100 Subject: [PATCH 2/4] Remove loads & supports on orphaned nodes too and move functionality to helper function in system_components.util --- anastruct/fem/system.py | 5 +-- anastruct/fem/system_components/util.py | 52 +++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/anastruct/fem/system.py b/anastruct/fem/system.py index 013486d..1b4518a 100644 --- a/anastruct/fem/system.py +++ b/anastruct/fem/system.py @@ -486,10 +486,9 @@ def remove_element(self, element_id: int) -> None: if len(self.node_element_map[node_id]) == 0: self.node_element_map.pop(node_id) self.node_map[node_id].elements.pop(element_id) - # Check if node is now orphaned, and remove it if so + # Check if node is now orphaned, and remove it and its loads if so if len(self.node_map[node_id].elements) == 0: - self._vertices.pop(self.node_map[node_id].vertex) - self.node_map.pop(node_id) + system_components.util.remove_node_id(self, node_id) # Remove element_id self.element_map.pop(element_id) diff --git a/anastruct/fem/system_components/util.py b/anastruct/fem/system_components/util.py index 0b1c820..30009aa 100644 --- a/anastruct/fem/system_components/util.py +++ b/anastruct/fem/system_components/util.py @@ -78,6 +78,58 @@ def append_node_id( system.node_map[node_id2] = Node(node_id2, vertex=point_2) +def remove_node_id( + system: "SystemElements", + node_id: int, +) -> None: + """Remove the node id from the system + + Note that this function does NOT check if the node is still used in the system. It is + intended to be used as a helper function to `remove_element()` and others, not to be + used directly by users. + + Args: + system (SystemElements): System in which the node is located + node_id (int): Node id of the node + """ + system._vertices.pop(system.node_map[node_id].vertex) + system.node_map.pop(node_id) + if node_id in system.loads_point: + system.loads_point.pop(node_id) + if node_id in system.loads_moment: + system.loads_moment.pop(node_id) + if node_id in system.supports_fixed: + system.supports_fixed.remove(node_id) + if node_id in system.supports_hinged: + system.supports_hinged.remove(node_id) + if node_id in system.supports_rotational: + system.supports_rotational.remove(node_id) + if node_id in system.supports_roll: + ind = system.supports_roll.index(node_id) + system.supports_roll.remove(node_id) + system.supports_roll_direction.pop(ind) + system.supports_roll_rotate.pop(ind) + if node_id in system.inclined_roll: + system.inclined_roll.pop(node_id) + if node_id in system.supports_spring_x: + system.supports_spring_x.remove(node_id) + if node_id in system.supports_spring_z: + system.supports_spring_z.remove(node_id) + if node_id in system.supports_spring_y: + system.supports_spring_y.remove(node_id) + if node_id in [ + system.supports_spring_args[i][0] + for i in range(len(system.supports_spring_args)) + ]: + ind = [ + system.supports_spring_args[i][0] + for i in range(len(system.supports_spring_args)) + ].index(node_id) + system.supports_spring_args.pop(ind) + if node_id in system.internal_hinges: + system.internal_hinges.remove(node_id) + + def det_vertices( system: "SystemElements", location_list: Union[ From 385db6b279762a4f68f55d583eced21f1223d736 Mon Sep 17 00:00:00 2001 From: Brooks Smith Date: Mon, 30 Oct 2023 10:57:34 +1100 Subject: [PATCH 3/4] Add a test for remove_element() function --- tests/test_e2e.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/test_e2e.py b/tests/test_e2e.py index ae6312a..dc16776 100644 --- a/tests/test_e2e.py +++ b/tests/test_e2e.py @@ -1016,3 +1016,31 @@ def it_results_in_correct_deflections(): assert system.get_node_results_system(1)["uy"] == approx( -w * l**4 / (8 * EI) ) + + def context_remove_element(): + @pspec_context("Removing an element from a propped beam") + def describe(): + pass + + system = SystemElements() + system.add_element([[0, 0], [10, 0]]) + system.add_element([[10, 0], [15, 0]]) + system.add_support_hinged(1) + system.add_support_hinged(2) + system.q_load(q=-1, element_id=1) + system.q_load(q=-1, element_id=2) + system.point_load(node_id=3, Fy=-10) + system.remove_element(2) + system.solve() + + def it_removes_element(): + assert len(system.element_map) == 1 + assert not (2 in system.loads_q) + + def it_removes_orphaned_node(): + assert len(system.node_map) == 2 + assert not (3 in system.loads_point) + + def it_results_in_correct_reactions(): + assert system.get_node_results_system(1)["Fy"] == approx(-5) + assert system.get_node_results_system(2)["Fy"] == approx(-5) From 276c01ae4a3c5b2c15e904f8c2936becb8d82b51 Mon Sep 17 00:00:00 2001 From: Brooks Smith Date: Mon, 30 Oct 2023 11:04:58 +1100 Subject: [PATCH 4/4] Fix node vs node_id indexing --- anastruct/fem/system_components/util.py | 48 ++++++++++++------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/anastruct/fem/system_components/util.py b/anastruct/fem/system_components/util.py index 30009aa..7ff0810 100644 --- a/anastruct/fem/system_components/util.py +++ b/anastruct/fem/system_components/util.py @@ -92,42 +92,40 @@ def remove_node_id( system (SystemElements): System in which the node is located node_id (int): Node id of the node """ + node = system.node_map[node_id] system._vertices.pop(system.node_map[node_id].vertex) system.node_map.pop(node_id) if node_id in system.loads_point: system.loads_point.pop(node_id) if node_id in system.loads_moment: system.loads_moment.pop(node_id) - if node_id in system.supports_fixed: - system.supports_fixed.remove(node_id) - if node_id in system.supports_hinged: - system.supports_hinged.remove(node_id) - if node_id in system.supports_rotational: - system.supports_rotational.remove(node_id) - if node_id in system.supports_roll: - ind = system.supports_roll.index(node_id) - system.supports_roll.remove(node_id) + if node in system.supports_fixed: + system.supports_fixed.remove(node) + if node in system.supports_hinged: + system.supports_hinged.remove(node) + if node in system.supports_rotational: + system.supports_rotational.remove(node) + if node in system.internal_hinges: + system.internal_hinges.remove(node) + if node in system.supports_roll: + ind = system.supports_roll.index(node) + system.supports_roll.remove(node) system.supports_roll_direction.pop(ind) system.supports_roll_rotate.pop(ind) if node_id in system.inclined_roll: system.inclined_roll.pop(node_id) - if node_id in system.supports_spring_x: - system.supports_spring_x.remove(node_id) - if node_id in system.supports_spring_z: - system.supports_spring_z.remove(node_id) - if node_id in system.supports_spring_y: - system.supports_spring_y.remove(node_id) - if node_id in [ - system.supports_spring_args[i][0] - for i in range(len(system.supports_spring_args)) - ]: - ind = [ - system.supports_spring_args[i][0] - for i in range(len(system.supports_spring_args)) - ].index(node_id) + if node in [item[0] for item in system.supports_spring_x]: + ind = [item[0] for item in system.supports_spring_x].index(node) + system.supports_spring_x.pop(ind) + if node in [item[0] for item in system.supports_spring_y]: + ind = [item[0] for item in system.supports_spring_y].index(node) + system.supports_spring_y.pop(ind) + if node in [item[0] for item in system.supports_spring_z]: + ind = [item[0] for item in system.supports_spring_z].index(node) + system.supports_spring_z.pop(ind) + if node_id in [item[0] for item in system.supports_spring_args]: + ind = [item[0] for item in system.supports_spring_args].index(node_id) system.supports_spring_args.pop(ind) - if node_id in system.internal_hinges: - system.internal_hinges.remove(node_id) def det_vertices(