pycba-tools 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pycba_tools/__init__.py +41 -0
- pycba_tools/combos.py +31 -0
- pycba_tools/envelope.py +86 -0
- pycba_tools/model.py +806 -0
- pycba_tools/reports.py +699 -0
- pycba_tools/serialize.py +176 -0
- pycba_tools-0.1.0.dist-info/METADATA +266 -0
- pycba_tools-0.1.0.dist-info/RECORD +10 -0
- pycba_tools-0.1.0.dist-info/WHEEL +4 -0
- pycba_tools-0.1.0.dist-info/licenses/LICENSE +201 -0
pycba_tools/__init__.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""
|
|
2
|
+
pycba-tools: Serialization and bulk results extraction for pycba continuous beam models, interchangeable with pynite-tools.
|
|
3
|
+
|
|
4
|
+
Modules included:
|
|
5
|
+
|
|
6
|
+
- model: CBAModel, a pycba beam with named load cases, load combinations and
|
|
7
|
+
Pynite-shaped result views
|
|
8
|
+
- reports: Functions for bulk exporting structured analysis results
|
|
9
|
+
- serialize: Functions for serializing/deserializing CBAModel objects
|
|
10
|
+
- combos: Functions for bulk importing load combinations
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
__version__ = "0.1.0"
|
|
14
|
+
|
|
15
|
+
from .model import CBAModel, LoadCombo
|
|
16
|
+
|
|
17
|
+
from .reports import (
|
|
18
|
+
extract_node_reactions,
|
|
19
|
+
extract_node_displacements,
|
|
20
|
+
extract_member_arrays,
|
|
21
|
+
extract_member_envelopes,
|
|
22
|
+
extract_member_actions_by_location,
|
|
23
|
+
extract_span_envelopes,
|
|
24
|
+
extract_load_combinations,
|
|
25
|
+
merge_result_trees,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
from .serialize import (
|
|
29
|
+
to_json,
|
|
30
|
+
from_json,
|
|
31
|
+
dump,
|
|
32
|
+
dumps,
|
|
33
|
+
dump_dict,
|
|
34
|
+
load,
|
|
35
|
+
loads,
|
|
36
|
+
load_dict,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
from .combos import (
|
|
40
|
+
model_add_combos,
|
|
41
|
+
)
|
pycba_tools/combos.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from copy import deepcopy
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
from .model import CBAModel
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def model_add_combos(
|
|
8
|
+
load_combos: list[dict[str, dict[str, float]]], model: CBAModel, as_copy: bool = False
|
|
9
|
+
) -> Optional[CBAModel]:
|
|
10
|
+
"""
|
|
11
|
+
Bulk adds a list of load combination dictionaries into the 'model' all at once.
|
|
12
|
+
|
|
13
|
+
'load_combos': a list of load combination elements, each in the following format:
|
|
14
|
+
{
|
|
15
|
+
"name": "load_combo_name",
|
|
16
|
+
"factors": {
|
|
17
|
+
"case_1_name": 1.0,
|
|
18
|
+
"case_2_name": 1.0,
|
|
19
|
+
...
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
'model': A pycba_tools.CBAModel
|
|
23
|
+
'as_copy': If True, will return a copy of the CBAModel with the load combos applied.
|
|
24
|
+
If False, returns None and modifies the CBAModel in place. Default is False.
|
|
25
|
+
"""
|
|
26
|
+
if as_copy:
|
|
27
|
+
model = deepcopy(model)
|
|
28
|
+
for load_combo in load_combos:
|
|
29
|
+
model.add_load_combo(**load_combo)
|
|
30
|
+
if as_copy:
|
|
31
|
+
return model
|
pycba_tools/envelope.py
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
from copy import copy
|
|
2
|
+
from typing import Hashable
|
|
3
|
+
|
|
4
|
+
PYMAX = max
|
|
5
|
+
PYMIN = min
|
|
6
|
+
|
|
7
|
+
def envelope_tree(tree: dict | list, levels: list[Hashable | None], leaf: Hashable | None, agg_func: callable, with_trace: bool = True) -> dict:
|
|
8
|
+
"""
|
|
9
|
+
Envelopes the tree at the leaf node with 'agg_func'.
|
|
10
|
+
"""
|
|
11
|
+
env_acc = {}
|
|
12
|
+
# If we are at the last branch...
|
|
13
|
+
if not levels:
|
|
14
|
+
if isinstance(tree, list):
|
|
15
|
+
tree = {idx: leaf for idx, leaf in enumerate(tree)}
|
|
16
|
+
leaf_acc = {}
|
|
17
|
+
for k, leaves in tree.items():
|
|
18
|
+
if leaf is not None:
|
|
19
|
+
leaf_acc.update({k: leaves[leaf]})
|
|
20
|
+
else:
|
|
21
|
+
leaf_acc = tree
|
|
22
|
+
# ...create a dict of the enveloped value and the key
|
|
23
|
+
# that it belongs to and return it.
|
|
24
|
+
env_values = list(leaf_acc.values())
|
|
25
|
+
env_value = agg_func(env_values)
|
|
26
|
+
env_keys = list(leaf_acc.keys())
|
|
27
|
+
try:
|
|
28
|
+
env_key = env_keys[env_values.index(env_value)]
|
|
29
|
+
except ValueError: # The value was transformed, likely due to abs()
|
|
30
|
+
env_key = env_keys[env_values.index(-1 * env_value)]
|
|
31
|
+
env_acc.update({"key": env_key, "value": env_value})
|
|
32
|
+
if with_trace:
|
|
33
|
+
return env_acc
|
|
34
|
+
else:
|
|
35
|
+
return env_value
|
|
36
|
+
else:
|
|
37
|
+
# Otherwise, pop the next level and dive into the tree on that branch
|
|
38
|
+
level = levels[0]
|
|
39
|
+
if level is not None:
|
|
40
|
+
try:
|
|
41
|
+
tree[level]
|
|
42
|
+
except KeyError:
|
|
43
|
+
raise KeyError(f"Key '{level}' does not exist at this level. Available keys: {list(tree.keys())}")
|
|
44
|
+
env_acc.update({level: envelope_tree(tree[level], levels[1:], leaf, agg_func, with_trace)})
|
|
45
|
+
return env_acc
|
|
46
|
+
else:
|
|
47
|
+
# If None, then walk all branches of this node of the tree
|
|
48
|
+
if isinstance(tree, list):
|
|
49
|
+
tree = {idx: leaf for idx, leaf in enumerate(tree)}
|
|
50
|
+
for k, v in tree.items():
|
|
51
|
+
env_acc.update({k: envelope_tree(v, levels[1:], leaf, agg_func, with_trace)})
|
|
52
|
+
return env_acc
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def absmax(x: list[float | int]) -> float | int:
|
|
56
|
+
"""
|
|
57
|
+
Returns the absolute maximum value in 'x'.
|
|
58
|
+
"""
|
|
59
|
+
abs_acc = [abs(y) for y in x]
|
|
60
|
+
return max(abs_acc)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def absmin(x: list[float | int]) -> float | int:
|
|
64
|
+
"""
|
|
65
|
+
Returns the absolute minimum value in 'x'.
|
|
66
|
+
"""
|
|
67
|
+
abs_acc = [abs(y) for y in x]
|
|
68
|
+
return min(abs_acc)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def max(x: list[float | int | None]) -> float | int | None:
|
|
72
|
+
"""
|
|
73
|
+
Returns the max value in 'x' while ignoring "None".
|
|
74
|
+
If all values in 'x' are None, then None is returned.
|
|
75
|
+
"""
|
|
76
|
+
return PYMAX([y for y in x if y is not None])
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def min(x: list[float | int | None]) -> float | int | None:
|
|
80
|
+
"""
|
|
81
|
+
Returns the max value in 'x' while ignoring "None".
|
|
82
|
+
If all values in 'x' are None, then None is returned.
|
|
83
|
+
"""
|
|
84
|
+
return PYMIN([y for y in x if y is not None])
|
|
85
|
+
|
|
86
|
+
|