Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/loadbearing_wall/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""
A package that models a generic load-bearing wall for the purpose of determining
A package that models a generic load-bearing wall for the purpose of determining
reactions and load-transfer.

The wall model is parameterizable and can represent any material
Expand All @@ -8,4 +8,4 @@
__version__ = "0.1.0"

from loadbearing_wall.wall_model import LinearWallModel
from loadbearing_wall import *
from loadbearing_wall import *
14 changes: 10 additions & 4 deletions src/loadbearing_wall/geom_ops.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import math
from typing import Optional


def apply_spread_angle(
wall_height: float,
wall_length: float,
Expand All @@ -12,7 +13,7 @@ def apply_spread_angle(
) -> dict:
"""
Returns a dictionary representing the load described by
w0, w1, x0, x1. If only w0 and x0 are provided, the
w0, w1, x0, x1. If only w0 and x0 are provided, the
load is assumed to be a point load.

The total spread cannot be longer than the wall length.
Expand All @@ -37,14 +38,19 @@ def apply_spread_angle(
projected_w1 = w1 * ratio
else:
projected_w1 = w0 * ratio
return (round_to_close_integer(projected_w0), round_to_close_integer(projected_w1), round_to_close_integer(projected_x0), round_to_close_integer(projected_x1))
return (
round_to_close_integer(projected_w0),
round_to_close_integer(projected_w1),
round_to_close_integer(projected_x0),
round_to_close_integer(projected_x1),
)


def round_to_close_integer(x: float, eps = 1e-7) -> float | int:
def round_to_close_integer(x: float, eps=1e-7) -> float | int:
"""
Rounds to the nearest int if it is REALLY close
"""
if abs(abs(round(x)) - abs(x)) < eps:
return round(x)
else:
return x
return x
91 changes: 53 additions & 38 deletions src/loadbearing_wall/linear_reactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from load_distribution import Singularity, singularities_to_polygon
from .geom_ops import round_to_close_integer as rtci


@dataclass
class LinearReaction:
w0: float
Expand All @@ -12,7 +13,7 @@ class LinearReaction:

def point_in_reaction(self, x: float):
return self.x0 <= x <= self.x1

def points_enclose_reaction(self, xa: float, xb: float) -> bool:
"""
Returns True if xa <= self.x0 <= self.x1 <= xb
Expand All @@ -26,12 +27,14 @@ def extract_reaction(self, xa: float, xb: float) -> "LinearReaction":
"""
m = (self.w1 - self.w0) / (self.x1 - self.x0)
y0 = self.w0
if not any([
self.point_in_reaction(xa),
self.point_in_reaction(xb),
self.points_enclose_reaction(xa, xb),
]):
return LinearReaction(0.0, 0.0, self.x0, self.x1)
if not any(
[
self.point_in_reaction(xa),
self.point_in_reaction(xb),
self.points_enclose_reaction(xa, xb),
]
):
return LinearReaction(0.0, 0.0, self.x0, self.x1)
if not self.point_in_reaction(xa):
xi = self.x0
yi = self.w0
Expand All @@ -54,6 +57,7 @@ class LinearReactionString:
"""
A class to manage a collection of LinearReactions
"""

linear_reactions: dict[str, dict[str, list[LinearReaction]]]
magnitude_start_key: str
magnitude_end_key: str
Expand All @@ -62,7 +66,7 @@ class LinearReactionString:

@classmethod
def from_projected_loads(
cls,
cls,
projected_loads: dict[str, dict[str, list[dict]]],
magnitude_start_key: str,
magnitude_end_key: str,
Expand All @@ -86,11 +90,14 @@ def from_projected_loads(
applied_load.get(x1),
)

linear_reaction_components[load_dir][load_case].append(linear_reaction)
linear_reaction_components[load_dir][load_case].append(
linear_reaction
)
return cls(linear_reaction_components, w0, w1, x0, x1)


def extract_reaction_string(self, xa: float, xb: float, case: str, dir: str) -> Optional[list[LinearReaction]]:
def extract_reaction_string(
self, xa: float, xb: float, case: str, dir: str
) -> Optional[list[LinearReaction]]:
"""
Returns a LinearReactionString representing the linear reactions that
exist between 'xa' and 'xb' extracted from self.
Expand All @@ -111,14 +118,10 @@ def extract_reaction_string(self, xa: float, xb: float, case: str, dir: str) ->
self.location_start_key,
self.location_end_key,
)



def consolidate_reactions(
self,
flatten: bool,
dir_key: str = "dir",
case_key: str = "case"
):
self, flatten: bool, dir_key: str = "dir", case_key: str = "case"
):
"""
Collects distributed loads from the top of a wall run and
converts them into a LinearReactionString which can sum and
Expand Down Expand Up @@ -156,23 +159,25 @@ def consolidate_reactions(
singularity_functions = []
for lr in linear_reactions:
if lr.w1 is None and lr.x1 is None:
point_load = {w0: lr.w0, x0: lr.x0, dir_key: load_dir, case_key: load_case}
flattened_reaction_components.append(
point_load
)
reaction_components[load_dir][load_case].append(
point_load
)
point_load = {
w0: lr.w0,
x0: lr.x0,
dir_key: load_dir,
case_key: load_case,
}
flattened_reaction_components.append(point_load)
reaction_components[load_dir][load_case].append(point_load)
else:
m = (lr.w1 - lr.w0) / (lr.x1 - lr.x0)
y0 = lr.w0
singularity_function = Singularity(x0=lr.x0, y0=y0, x1=lr.x1, m=m, precision=6)
singularity_function = Singularity(
x0=lr.x0, y0=y0, x1=lr.x1, m=m, precision=6
)
singularity_functions.append(singularity_function)
if not singularity_functions: continue
if not singularity_functions:
continue
linear_reactions = singularity_xy_to_distributed_loads(
singularities_to_polygon(
singularity_functions, xy=True
),
singularities_to_polygon(singularity_functions, xy=True),
magnitude_start_key=w0,
magnitude_end_key=w1,
location_start_key=x0,
Expand All @@ -189,9 +194,11 @@ def consolidate_reactions(
if flatten:
return flattened_reaction_components
return reaction_components


def filter_repeated_y_values(xy_vals: list[list[float], list[float]]) -> list[list[float, float]]:


def filter_repeated_y_values(
xy_vals: list[list[float], list[float]],
) -> list[list[float, float]]:
"""
Returns xy_vals but with any "repeating" data points removed and
returns a list of coordinates, list[list[float, float]]
Expand All @@ -211,7 +218,7 @@ def filter_repeated_y_values(xy_vals: list[list[float], list[float]]) -> list[li
filtered.append([x, y])
prev_y = y
return filtered


def singularity_xy_to_distributed_loads(
xy_vals: list[list[float], list[float]],
Expand All @@ -225,7 +232,7 @@ def singularity_xy_to_distributed_loads(
dir_key: str = "dir",
) -> list[dict]:
"""
Returns dicts representing distributed
Returns dicts representing distributed
"""
w0 = magnitude_start_key
w1 = magnitude_end_key
Expand All @@ -234,16 +241,24 @@ def singularity_xy_to_distributed_loads(
dist_loads = []
prev_x = None
for idx, (x, y) in enumerate(zip(*xy_vals)):
if idx == 0: continue
if idx == 0:
continue
if prev_x is None:
prev_x = x
prev_y = y
elif x - prev_x > 1e-3:
dist_load = {w0: float(rtci(prev_y)), w1: float(rtci(y)), x0: float(rtci(prev_x)), x1: float(rtci(x)), case_key: case, dir_key: dir}
dist_load = {
w0: float(rtci(prev_y)),
w1: float(rtci(y)),
x0: float(rtci(prev_x)),
x1: float(rtci(x)),
case_key: case,
dir_key: dir,
}
dist_loads.append(dist_load)
prev_x = x
prev_y = y
else:
prev_x = x
prev_y = y
return dist_loads
return dist_loads
17 changes: 5 additions & 12 deletions src/loadbearing_wall/point_reactions.py
Original file line number Diff line number Diff line change
@@ -1,34 +1,27 @@
from dataclasses import dataclass


@dataclass
class PointReactionCollection:
_reaction_components: dict[str, dict[str, list[float]]]


def extract_reactions(self, case: str, dir: str) -> float:
"""
Returns a float representing the total sum of loads for the
given 'dir' (direction) and 'case' (load case).
"""
return sum(self._reaction_components.get(case, {}).get(dir, []))


@classmethod
def from_point_loads(cls, point_loads: dict[str, dict[str, str | float]]):
"""

"""
""" """
reaction_components = {}
for load_case in point_loads.items():
reaction_components.setdefault(load_case, {})
for load_dir, pt_loads in load_case.items():
reaction_components[load_case].setdefault(load_dir, [])
for point_load in pt_loads:
reaction_components[load_dir][load_case].append(point_load['magnitude'])
reaction_components[load_dir][load_case].append(
point_load["magnitude"]
)
return cls(reaction_components)






Loading
Loading