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.
@@ -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
@@ -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
+