From 51dff86a19dafc103b37eff818216637d2a624f6 Mon Sep 17 00:00:00 2001 From: Connor Ferster Date: Mon, 19 Jan 2026 09:40:48 -0800 Subject: [PATCH 1/4] feat: add full load distribution --- src/loadbearing_wall/wall_model.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/loadbearing_wall/wall_model.py b/src/loadbearing_wall/wall_model.py index a7ed536..1ed7ab0 100644 --- a/src/loadbearing_wall/wall_model.py +++ b/src/loadbearing_wall/wall_model.py @@ -13,6 +13,7 @@ class LinearWallModel(BaseModel): length: float vertical_spread_angle: float = 0.0 minimum_point_spread: float = 0.5 + distribute_loads_full_length: bool = False distributed_loads: dict = Field(default={}) point_loads: dict = Field(default={}) gravity_dir: str = "z" @@ -43,6 +44,8 @@ class LinearWallModel(BaseModel): your context and the length unit system you are using. In other words, all applied point loads will be converted to distributed loads spread over this distance unless a vertical spread angle is applied. + 'distribute_loads_full_length': If True, will override both 'vertical_spread_angle' and + 'minimum_point_spread' and will distribute all loads evenly across the whole wall panel. 'distributed_loads': A dictionary of loads. Can be set directly or using the .add_dist_load() methods 'point_loads': A dictionary of loads. Can be set directly or using the .add_point_load() @@ -189,6 +192,21 @@ def spread_loads(self) -> None: x1: projected_load[3], } ) + elif self.distribute_loads_full_length: + w0 = dist_load[w0] + w1 = dist_load[w1] + x0 = dist_load[x0] + x1 = dist_load[x1] + total_load = (w0 + w1) / 2 * (x1 - x0) + w_full = total_load / self.length + proj[load_dir][load_case].append( + { + w0: w_full, + w1: w_full, + x0: 0.0, + x1: self.length + } + ) else: proj[load_dir][load_case].append(dist_load) @@ -222,6 +240,17 @@ def spread_loads(self) -> None: x1: projected_load[3], } ) + elif self.distribute_loads_full_length: + total_load = point_load[p] + w_full = total_load / self.length + proj[load_dir][load_case].append( + { + w0: w_full, + w1: w_full, + x0: 0.0, + x1: self.length + } + ) else: projected_load = geom.apply_minimum_width( point_load[p], From b0cbba4134b062de57ce402095e44d7d129f5363 Mon Sep 17 00:00:00 2001 From: Connor Ferster Date: Mon, 19 Jan 2026 10:43:55 -0800 Subject: [PATCH 2/4] TESTS: ADD TEST --- tests/test_wall_model.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/test_wall_model.py b/tests/test_wall_model.py index fff64f3..4037ca0 100644 --- a/tests/test_wall_model.py +++ b/tests/test_wall_model.py @@ -90,6 +90,20 @@ def test_45_spread(WM1): }, ] +def test_full_load_dist(WM0): + WM0.distribute_loads_full_length = True + rxn = WM0.get_reactions(flattened=False) + assert rxn['Fz']['D'] == [ + { + 'dir': 'Fz', + 'case': 'D', + 'w1': -40, + 'w2': -40, + 'x1': 0.0, + 'x2': 4.0 + } + ] + def test_serialization(WM0): serialized_dict = WM0.dump_dict() From c6242596656b0fc13e87ae95f5b6550d0c8d0046 Mon Sep 17 00:00:00 2001 From: Connor Ferster Date: Mon, 19 Jan 2026 13:41:11 -0800 Subject: [PATCH 3/4] chore: black --- src/loadbearing_wall/wall_model.py | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/loadbearing_wall/wall_model.py b/src/loadbearing_wall/wall_model.py index 1ed7ab0..6881e4f 100644 --- a/src/loadbearing_wall/wall_model.py +++ b/src/loadbearing_wall/wall_model.py @@ -200,12 +200,7 @@ def spread_loads(self) -> None: total_load = (w0 + w1) / 2 * (x1 - x0) w_full = total_load / self.length proj[load_dir][load_case].append( - { - w0: w_full, - w1: w_full, - x0: 0.0, - x1: self.length - } + {w0: w_full, w1: w_full, x0: 0.0, x1: self.length} ) else: proj[load_dir][load_case].append(dist_load) @@ -244,12 +239,7 @@ def spread_loads(self) -> None: total_load = point_load[p] w_full = total_load / self.length proj[load_dir][load_case].append( - { - w0: w_full, - w1: w_full, - x0: 0.0, - x1: self.length - } + {w0: w_full, w1: w_full, x0: 0.0, x1: self.length} ) else: projected_load = geom.apply_minimum_width( From cafb6a9c734e509990a97d3b73635c9b3b8abdbd Mon Sep 17 00:00:00 2001 From: Connor Ferster Date: Mon, 19 Jan 2026 13:47:39 -0800 Subject: [PATCH 4/4] fix: overwritten variable name --- src/loadbearing_wall/wall_model.py | 10 +++++----- tests/test_wall_model.py | 26 ++++++++++++++++++++++++-- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/loadbearing_wall/wall_model.py b/src/loadbearing_wall/wall_model.py index 6881e4f..b47d103 100644 --- a/src/loadbearing_wall/wall_model.py +++ b/src/loadbearing_wall/wall_model.py @@ -193,11 +193,11 @@ def spread_loads(self) -> None: } ) elif self.distribute_loads_full_length: - w0 = dist_load[w0] - w1 = dist_load[w1] - x0 = dist_load[x0] - x1 = dist_load[x1] - total_load = (w0 + w1) / 2 * (x1 - x0) + w0_value = dist_load[w0] + w1_value = dist_load[w1] + x0_value = dist_load[x0] + x1_value = dist_load[x1] + total_load = (w0_value + w1_value) / 2 * (x1_value - x0_value) w_full = total_load / self.length proj[load_dir][load_case].append( {w0: w_full, w1: w_full, x0: 0.0, x1: self.length} diff --git a/tests/test_wall_model.py b/tests/test_wall_model.py index 4037ca0..52e9711 100644 --- a/tests/test_wall_model.py +++ b/tests/test_wall_model.py @@ -97,8 +97,30 @@ def test_full_load_dist(WM0): { 'dir': 'Fz', 'case': 'D', - 'w1': -40, - 'w2': -40, + 'w1': -30, + 'w2': -30, + 'x1': 0.0, + 'x2': 4.0 + } + ] + WM0.distribute_loads_full_length = True + rxn = WM0.get_reactions(flattened=False) + assert rxn['Fz']['L'] == [ + { + 'dir': 'Fz', + 'case': 'L', + 'w1': -32.5, + 'w2': -32.5, + 'x1': 0.0, + 'x2': 4.0 + } + ] + assert rxn['Fx']['W'] == [ + { + 'dir': 'Fx', + 'case': 'W', + 'w1': -500.0, + 'w2': -500.0, 'x1': 0.0, 'x2': 4.0 }