mrfhelper 1.2__tar.gz
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.
- mrfhelper-1.2/MRFHelper/BuildingGeometry.py +42 -0
- mrfhelper-1.2/MRFHelper/ConnectionAndBoundary.py +78 -0
- mrfhelper-1.2/MRFHelper/LoadAndMaterial.py +282 -0
- mrfhelper-1.2/MRFHelper/MRFhelper.py +138 -0
- mrfhelper-1.2/MRFHelper/StructuralComponents.py +187 -0
- mrfhelper-1.2/MRFHelper/UserCommand.py +103 -0
- mrfhelper-1.2/MRFHelper/WriteInfo.py +183 -0
- mrfhelper-1.2/MRFHelper/WriteScript.py +1737 -0
- mrfhelper-1.2/MRFHelper/__init__.py +5 -0
- mrfhelper-1.2/MRFHelper/func.py +115 -0
- mrfhelper-1.2/PKG-INFO +8 -0
- mrfhelper-1.2/README.md +18 -0
- mrfhelper-1.2/mrfhelper.egg-info/PKG-INFO +8 -0
- mrfhelper-1.2/mrfhelper.egg-info/SOURCES.txt +16 -0
- mrfhelper-1.2/mrfhelper.egg-info/dependency_links.txt +1 -0
- mrfhelper-1.2/mrfhelper.egg-info/top_level.txt +1 -0
- mrfhelper-1.2/setup.cfg +4 -0
- mrfhelper-1.2/setup.py +14 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
from . import func
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class BuildingGeometry:
|
|
5
|
+
|
|
6
|
+
def __init__(self):
|
|
7
|
+
"""Step-1:
|
|
8
|
+
Building geometry parameters
|
|
9
|
+
|
|
10
|
+
Properties:
|
|
11
|
+
* story_height (tuple): Story_height
|
|
12
|
+
* bay_length (tuple): Bay_length
|
|
13
|
+
* plan_dimensions (tuple): (main direction, secondary direction)
|
|
14
|
+
* MF_number (int): Number of moment frame
|
|
15
|
+
* exterior_column_tributary_area (tuple): (x, y), area = x * y
|
|
16
|
+
* interior_column_tributary_area (tuple): (x, y), area = x * y
|
|
17
|
+
"""
|
|
18
|
+
self.story_height = None
|
|
19
|
+
self.bay_length = None
|
|
20
|
+
self.plane_dimensions = None
|
|
21
|
+
self.exterior_column_tributary_area = None
|
|
22
|
+
self.interior_column_tributary_area = None
|
|
23
|
+
self.MF_number = None
|
|
24
|
+
|
|
25
|
+
def _check(self):
|
|
26
|
+
func.check_list(self.story_height, min_length=1, max_length=98, name='story')
|
|
27
|
+
func.check_list(self.bay_length, min_length=1, max_length=98, name='story')
|
|
28
|
+
func.check_tuple(self.plane_dimensions, 2, name='plan_dimensions')
|
|
29
|
+
func.check_int(self.MF_number, [1, 99], name='MF_number')
|
|
30
|
+
func.check_tuple(self.exterior_column_tributary_area, 2, name='exterior_column_tributary_area')
|
|
31
|
+
func.check_tuple(self.interior_column_tributary_area, 2, name='exterior_column_tributary_area')
|
|
32
|
+
|
|
33
|
+
def _finish(self):
|
|
34
|
+
"""Complete the definition of building geometry parameters"""
|
|
35
|
+
self._check()
|
|
36
|
+
self.N = len(self.story_height)
|
|
37
|
+
self.bays = len(self.bay_length)
|
|
38
|
+
self.axis = self.bays + 1
|
|
39
|
+
self.building_height = sum(self.story_height)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import TYPE_CHECKING
|
|
3
|
+
if TYPE_CHECKING:
|
|
4
|
+
from .MRFhelper import Frame
|
|
5
|
+
from . import func
|
|
6
|
+
from typing import Literal
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ConnectionAndBoundary:
|
|
10
|
+
|
|
11
|
+
def __init__(self, frame: Frame) -> None:
|
|
12
|
+
"""Step-4:
|
|
13
|
+
Connection and boundary condition
|
|
14
|
+
"""
|
|
15
|
+
self.frame = frame
|
|
16
|
+
self.base_support = 'Fixed'
|
|
17
|
+
self.beam_column_connection = 'Full'
|
|
18
|
+
self.RBS_paras = (0.625, 0.75, 0.25)
|
|
19
|
+
self.panel_zone_deformation = True
|
|
20
|
+
self.soil_constraint = []
|
|
21
|
+
self.rigid_disphragm = True
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def set_base_support(self, type: Literal['Fixed', 'Pinned']='Fixed'):
|
|
25
|
+
"""Defined column base support
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
type (str, optional):
|
|
29
|
+
* "Fixed": Full constrained conncetion
|
|
30
|
+
* "Pinned", Pinned connection
|
|
31
|
+
"""
|
|
32
|
+
func.check_string(type, ['Fixed', 'Pinned'], 'type')
|
|
33
|
+
self.base_support = type
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def set_beam_column_connection(self, type: Literal['Full', 'RBS', 'Hinged']='Full', a=0.625, b=0.75, c=0.25):
|
|
37
|
+
"""Define beam to column connection
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
type (str, optional):
|
|
41
|
+
* "Full" - Full restrained
|
|
42
|
+
* "RBS" - Reduced beam section
|
|
43
|
+
* "Hinged" - Hinged connection
|
|
44
|
+
|
|
45
|
+
a, b, c: RBS parameters, default to 0.625, 0.75, and 0.25, respectively
|
|
46
|
+
"""
|
|
47
|
+
func.check_int_float(a, [0, 10], name='a')
|
|
48
|
+
func.check_int_float(b, [0, 10], name='b')
|
|
49
|
+
func.check_int_float(c, [0, 1], name='c')
|
|
50
|
+
func.check_string(type, ['Full', 'RBS', 'Hinged'], 'type')
|
|
51
|
+
self.beam_column_connection = type
|
|
52
|
+
self.RBS_paras = (a, b, c)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def set_panel_zone_deformation(self, type: Literal[True, False]=True):
|
|
56
|
+
"""Define whether the panel zone deformation is consdiered
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
type (bool, optional):
|
|
60
|
+
* True - Use a parrallelogram model
|
|
61
|
+
* False - Use a cruciform model
|
|
62
|
+
"""
|
|
63
|
+
func.check_boolean(type, name='type')
|
|
64
|
+
self.panel_zone_deformation = type
|
|
65
|
+
|
|
66
|
+
def set_soil_constraint(self, floor: int):
|
|
67
|
+
"""Set soil constraint at specified floor
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
floor (int): floor number with soil constraint
|
|
71
|
+
"""
|
|
72
|
+
func.check_int(floor, [1, self.frame.N + 1])
|
|
73
|
+
if floor not in self.soil_constraint:
|
|
74
|
+
self.soil_constraint.append(floor)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _finished(self):
|
|
78
|
+
pass
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import TYPE_CHECKING
|
|
3
|
+
if TYPE_CHECKING:
|
|
4
|
+
from .MRFhelper import Frame
|
|
5
|
+
import numpy as np
|
|
6
|
+
from . import func
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class LoadAndMaterial:
|
|
10
|
+
g = 9800
|
|
11
|
+
|
|
12
|
+
def __init__(self, frame: Frame) -> None:
|
|
13
|
+
self.N = frame.N
|
|
14
|
+
self.bays = frame.bays
|
|
15
|
+
self.axis = frame.axis
|
|
16
|
+
self.dead_load = dict()
|
|
17
|
+
self.live_load = dict()
|
|
18
|
+
self.cladding_load = dict()
|
|
19
|
+
self.E = 206000 # Necessary
|
|
20
|
+
self.fy_beam = None # Necessary
|
|
21
|
+
self.fy_column = None # Necessary
|
|
22
|
+
self.miu = 0.3 # Unnecessary
|
|
23
|
+
self.cc_weight = {'Dead': 1.05, 'Live': 0.25, 'Cladding': 1.05} # Combination coefficients of seismic weight, necessary
|
|
24
|
+
self.cc_mass = {'Dead': 1, 'Live': 0, 'Cladding': 1} # Combination coefficients of seismic mass, necessary
|
|
25
|
+
for floor in range(2, frame.N + 2):
|
|
26
|
+
self.dead_load[floor] = 1e-9
|
|
27
|
+
self.live_load[floor] = 1e-9
|
|
28
|
+
self.cladding_load[floor - 1] = 1e-9
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def set_dead_load(self, floor: list[int], load_per_area: list[int | float]):
|
|
32
|
+
"""Step-3:
|
|
33
|
+
Set dead load for each floor
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
floor (list[int]): Numbers of floor
|
|
37
|
+
load_per_area (list[int | float]): Values of dead load (surface load)
|
|
38
|
+
"""
|
|
39
|
+
func.check_list(floor, length=self.N, name='floor')
|
|
40
|
+
for val in floor:
|
|
41
|
+
func.check_int(val, [2, self.N + 1], name='floor')
|
|
42
|
+
func.check_list(load_per_area, length=self.N, name='load_per_area')
|
|
43
|
+
for _, (a, b) in enumerate(zip(floor, load_per_area)):
|
|
44
|
+
self.dead_load[a] = b
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def set_live_load(self, floor: list[int], load_per_area: list[int | float]):
|
|
48
|
+
"""Set live load for each floor
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
floor (list[int]): Numbers of floor
|
|
52
|
+
load_per_area (list[int | float]): Values of live load (surface load)
|
|
53
|
+
"""
|
|
54
|
+
func.check_list(floor, length=self.N, name='floor')
|
|
55
|
+
for val in floor:
|
|
56
|
+
func.check_int(val, [2, self.N + 1], name='floor')
|
|
57
|
+
func.check_list(load_per_area, length=self.N, name='load')
|
|
58
|
+
for _, (a, b) in enumerate(zip(floor, load_per_area)):
|
|
59
|
+
self.live_load[a] = b
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def set_cladding_load(self, story: list[int], load_per_area: list[int | float]):
|
|
63
|
+
"""Set cladding load for each story
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
story (list[int]): Numbers of story
|
|
67
|
+
load_per_area (list[int | float]): Values of cladding load (surface load)
|
|
68
|
+
"""
|
|
69
|
+
func.check_list(story, length=self.N, name='story')
|
|
70
|
+
for val in story:
|
|
71
|
+
func.check_int(val, [1, self.N], name='story')
|
|
72
|
+
func.check_list(load_per_area, length=self.N, name='load')
|
|
73
|
+
for _, (a, b) in enumerate(zip(story, load_per_area)):
|
|
74
|
+
self.cladding_load[a] = b
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def set_material(self, E: int | float, fy_beam: int | float, fy_column: int | float, miu: float=0.3):
|
|
78
|
+
"""Set material properties for all steel components
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
E (int | float): Youngs modulus
|
|
82
|
+
fy_beam (int | float): Nominal yield strength of beams
|
|
83
|
+
fy_column (int | float): Nominal yield strength of column
|
|
84
|
+
miu (float, optional): possion ratio
|
|
85
|
+
"""
|
|
86
|
+
func.check_int_float(E, name='E')
|
|
87
|
+
func.check_int_float(fy_beam, name='fy_beam')
|
|
88
|
+
func.check_int_float(fy_column, name='fy_column')
|
|
89
|
+
func.check_int_float(miu, [0, 0.5], name='miu')
|
|
90
|
+
self.E = E
|
|
91
|
+
self.fy_beam = fy_beam
|
|
92
|
+
self.fy_column = fy_column
|
|
93
|
+
self.miu = miu
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def set_weight_combination_coefficients(self, coefficients: dict):
|
|
97
|
+
"""Set seicmic weight combination coefficients
|
|
98
|
+
Note: Load type should be within ['Dead', 'Live', 'Cladding']
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
coefficients (dict): {load type: combination coefficient}
|
|
102
|
+
"""
|
|
103
|
+
func.check_dict(coefficients, name='coefficients')
|
|
104
|
+
for key, val in coefficients.items():
|
|
105
|
+
if not key in ['Dead', 'Live', 'Cladding']:
|
|
106
|
+
raise ValueError("Load type should be within ['Dead', 'Live', 'Cladding']")
|
|
107
|
+
func.check_int_float(val, name='coefficient')
|
|
108
|
+
self.cc_weight[key] = val
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def set_mass_combination_coefficients(self, coefficients: dict):
|
|
112
|
+
"""Set seicmic mass combination coefficients
|
|
113
|
+
Note: Load type should be within ['Dead', 'Live', 'Cladding']
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
coefficients (dict): {load type: combination coefficient}
|
|
117
|
+
"""
|
|
118
|
+
func.check_dict(coefficients, name='coefficients')
|
|
119
|
+
for key, val in coefficients.items():
|
|
120
|
+
if not key in ['Dead', 'Live', 'Cladding']:
|
|
121
|
+
raise ValueError("Load type should be within ['Dead', 'Live', 'Cladding']")
|
|
122
|
+
func.check_int_float(val, name='coefficient')
|
|
123
|
+
self.cc_mass[key] = val
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _finished(self):
|
|
127
|
+
if self.E is None:
|
|
128
|
+
raise ValueError("Young's modulus has not been difined")
|
|
129
|
+
if self.fy_beam is None:
|
|
130
|
+
raise ValueError("Nominal yield strength of beams has not been difined")
|
|
131
|
+
if self.fy_column is None:
|
|
132
|
+
raise ValueError("Nominal yield strength of columns has not been difined")
|
|
133
|
+
if not self.cc_weight:
|
|
134
|
+
raise ValueError("Combination coefficients of seismic weight have not been difined")
|
|
135
|
+
if not self.cc_mass:
|
|
136
|
+
raise ValueError("Combination coefficients of seismic mass have not been difined")
|
|
137
|
+
floor_set = set([2 + i for i in range(self.N)])
|
|
138
|
+
floor_defined_dead = set(self.dead_load.keys())
|
|
139
|
+
floor_defined_live = set(self.live_load.keys())
|
|
140
|
+
if floor_set == floor_defined_dead:
|
|
141
|
+
return
|
|
142
|
+
if floor_set - floor_defined_dead:
|
|
143
|
+
raise ValueError(f'Dead load for floor {floor_set - floor_defined_dead} have not been defined')
|
|
144
|
+
if floor_defined_dead - floor_set:
|
|
145
|
+
raise ValueError(f'Floor {floor_defined_dead - floor_set} are not existed')
|
|
146
|
+
if floor_set - floor_defined_live:
|
|
147
|
+
raise ValueError(f'Dead load for floor {floor_set - floor_defined_live} have not been defined')
|
|
148
|
+
if floor_defined_live - floor_set:
|
|
149
|
+
raise ValueError(f'Floor {floor_defined_live - floor_set} are not existed')
|
|
150
|
+
self._calculate_load()
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def _calculate_load(self, frame: Frame):
|
|
154
|
+
"""Calculate load and mass
|
|
155
|
+
* F_node (dict): Beam-column joint force ({floor: force})
|
|
156
|
+
* F_grav (dect): Leaning column joint force ({floor: force})
|
|
157
|
+
* mass_node (dict): Beam-column joint mass ({floor: mass})
|
|
158
|
+
* mass_grav (dect): Leaning column joint mass ({floor: mass})
|
|
159
|
+
"""
|
|
160
|
+
plane_dimensions = frame.BuildingGeometry.plane_dimensions
|
|
161
|
+
N_MF = frame.BuildingGeometry.MF_number
|
|
162
|
+
area_ex = frame.BuildingGeometry.exterior_column_tributary_area
|
|
163
|
+
area_in = frame.BuildingGeometry.interior_column_tributary_area
|
|
164
|
+
h_list = frame.BuildingGeometry.story_height
|
|
165
|
+
# dead load
|
|
166
|
+
DL_node = dict()
|
|
167
|
+
DL_grav = dict()
|
|
168
|
+
S_3D = plane_dimensions[0] * plane_dimensions[1] # area of 3D building plane
|
|
169
|
+
for floor, DL_value in self.dead_load.items():
|
|
170
|
+
DL_3D = S_3D * DL_value # Dead load (N) for each floor of the 3D building
|
|
171
|
+
DL = DL_3D / N_MF # Dead load (N) for each floor of the 2D considered frame
|
|
172
|
+
r_rest = 1
|
|
173
|
+
DL_floor = []
|
|
174
|
+
for id_axis in range(self.axis):
|
|
175
|
+
if id_axis == 0 or id_axis == self.axis - 1:
|
|
176
|
+
r = area_ex[0] * area_ex[1] / (S_3D / N_MF) # area ratio for external columns
|
|
177
|
+
else:
|
|
178
|
+
r = area_in[0] * area_in[1] / (S_3D / N_MF) # area ratio for internal columns
|
|
179
|
+
r_rest -= r
|
|
180
|
+
DL_floor.append(DL * r)
|
|
181
|
+
DL_grav[floor] = DL * r_rest
|
|
182
|
+
DL_node[floor] = DL_floor
|
|
183
|
+
# live load (same as the dead load)
|
|
184
|
+
LL_node = dict()
|
|
185
|
+
LL_grav = dict()
|
|
186
|
+
S_3D = plane_dimensions[0] * plane_dimensions[1] # area of 3D building plane
|
|
187
|
+
for floor, LL_value in self.live_load.items():
|
|
188
|
+
LL_3D = S_3D * LL_value # Dead load (N) for each floor of the 3D building
|
|
189
|
+
LL = LL_3D / N_MF # Dead load (N) for each floor of the 2D considered frame
|
|
190
|
+
r_rest = 1
|
|
191
|
+
LL_floor = []
|
|
192
|
+
for id_axis in range(self.axis):
|
|
193
|
+
if id_axis == 0 or id_axis == self.axis - 1:
|
|
194
|
+
r = area_ex[0] * area_ex[1] / (S_3D / N_MF) # area ratio for external columns
|
|
195
|
+
else:
|
|
196
|
+
r = area_in[0] * area_in[1] / (S_3D / N_MF) # area ratio for internal columns
|
|
197
|
+
r_rest -= r
|
|
198
|
+
LL_floor.append(LL * r)
|
|
199
|
+
LL_grav[floor] = LL * r_rest
|
|
200
|
+
LL_node[floor] = LL_floor
|
|
201
|
+
# cladding load
|
|
202
|
+
CL_node = dict()
|
|
203
|
+
CL_grav = dict()
|
|
204
|
+
for story, CL_value in self.cladding_load.items():
|
|
205
|
+
floor = story + 1
|
|
206
|
+
story_b, story_t = floor - 1, floor # story number
|
|
207
|
+
CL_floor = []
|
|
208
|
+
h = h_list[story_b-1]/2 if floor==self.N+1 else (h_list[story_b-1] + h_list[story_t-1])/2 # tributary heigth of each floor
|
|
209
|
+
S_rest = plane_dimensions[0] * h
|
|
210
|
+
for id_axis in range(self.axis):
|
|
211
|
+
if id_axis == 0 or id_axis == self.axis - 1: # if external node
|
|
212
|
+
if floor == self.N + 1: # if top floor
|
|
213
|
+
S_node = area_ex[0] * h_list[story_b-1] / 2 # Node tributary facade area
|
|
214
|
+
else:
|
|
215
|
+
S_node = area_ex[0] * (h_list[story_b-1] + h_list[story_t-1]) / 2 # Node tributary facade area
|
|
216
|
+
else:
|
|
217
|
+
if floor == self.N + 1: # if top floor
|
|
218
|
+
S_node = area_in[0] * h_list[story_b-1] / 2 # Node tributary facade area
|
|
219
|
+
else:
|
|
220
|
+
S_node = area_in[0] * (h_list[story_b-1] + h_list[story_t-1]) / 2 # Node tributary facade area
|
|
221
|
+
S_rest -= S_node
|
|
222
|
+
CL_floor.append(S_node * CL_value)
|
|
223
|
+
CL_node[floor] = CL_floor
|
|
224
|
+
CL_grav[floor] = S_rest * CL_value + plane_dimensions[1] * h * CL_value # Consider the Y-direction cladding load
|
|
225
|
+
# CL_grav[floor] = S_rest * CL_value # TODO Did not consider the Y-direction cladding load
|
|
226
|
+
# node force
|
|
227
|
+
F_node = dict()
|
|
228
|
+
F_grav = dict()
|
|
229
|
+
mass_node = dict()
|
|
230
|
+
mass_grav = dict()
|
|
231
|
+
for floor in range(2, 2 + self.N):
|
|
232
|
+
DL_floor = np.array(DL_node[floor])
|
|
233
|
+
LL_floor = np.array(LL_node[floor])
|
|
234
|
+
CL_floor = np.array(CL_node[floor])
|
|
235
|
+
F_floor = DL_floor * self.cc_weight['Dead'] + LL_floor * self.cc_weight['Live'] + CL_floor + self.cc_weight['Cladding']
|
|
236
|
+
mass_floor = (DL_floor * self.cc_mass['Dead'] + LL_floor * self.cc_mass['Live'] + CL_floor + self.cc_mass['Cladding']) / self.g
|
|
237
|
+
F_floor = list(F_floor)
|
|
238
|
+
mass_floor = list(mass_floor)
|
|
239
|
+
F_node[floor] = F_floor
|
|
240
|
+
mass_node[floor] = mass_floor
|
|
241
|
+
F_grav[floor] = DL_grav[floor] * self.cc_weight['Dead'] + LL_grav[floor] * self.cc_weight['Live'] + CL_grav[floor] * self.cc_weight['Cladding']
|
|
242
|
+
mass_grav[floor] = (DL_grav[floor] * self.cc_mass['Dead'] + LL_grav[floor] * self.cc_mass['Live'] + CL_grav[floor] * self.cc_mass['Cladding']) / self.g
|
|
243
|
+
# TODO Beam and column component masses have not been considered, but FM2D also did not consider.
|
|
244
|
+
self.F_node, self.F_grav, self.mass_node, self.mass_grav = F_node, F_grav, mass_node, mass_grav
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def _calculate_PPy(self, frame: Frame, PPy_scale: float=1.25):
|
|
248
|
+
"""Calculate the axial compression ratio of columns
|
|
249
|
+
* PPy (dict): Compression ratio ({story: ratio})
|
|
250
|
+
* PPy_scale (float): Scale factor of compression ratio to consider the overturning effect,
|
|
251
|
+
defaults to 1.25
|
|
252
|
+
|
|
253
|
+
Args:
|
|
254
|
+
PPy_scale (float, optional): Scale factor of compression ratio, defaults to 1.25.
|
|
255
|
+
"""
|
|
256
|
+
props_col = frame.StructuralComponents.column_properties
|
|
257
|
+
P_col = dict() # Axial forces of columns
|
|
258
|
+
P_temp = np.zeros(self.axis)
|
|
259
|
+
for story in list(1+i for i in range(self.N))[::-1]:
|
|
260
|
+
floor = story + 1
|
|
261
|
+
P_temp += np.array(self.F_node[floor])
|
|
262
|
+
P_col[story] = P_temp.tolist()
|
|
263
|
+
PPy = dict() # Column axial compression ratio
|
|
264
|
+
for story in range(1, self.N + 1):
|
|
265
|
+
prop_floor = props_col[story] # column properties for each story
|
|
266
|
+
PPy_story_b, PPy_story_t = [], []
|
|
267
|
+
for axis in range(1, self.axis + 1):
|
|
268
|
+
id_axis = axis - 1
|
|
269
|
+
A = prop_floor[id_axis][5] # Cross section aera of column
|
|
270
|
+
P = P_col[story][id_axis] # Axial force of column
|
|
271
|
+
PPy_story_b.append(P / (A * self.fy_column)) # Axial compression ratio of columns on the story
|
|
272
|
+
if story not in frame.StructuralComponents.column_splice:
|
|
273
|
+
PPy_story_t.append(P / (A * self.fy_column)) # Axial compression ratio of columns on the story
|
|
274
|
+
else:
|
|
275
|
+
A_t = props_col[story+1][id_axis][5] # Cross section aera of column
|
|
276
|
+
PPy_story_t.append(P / (A_t * self.fy_column)) # Axial compression ratio of columns on the story
|
|
277
|
+
PPy[f'{story}b'] = PPy_story_b
|
|
278
|
+
PPy[f'{story}t'] = PPy_story_t
|
|
279
|
+
self.PPy, self.PPy_scale = PPy, PPy_scale
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from .BuildingGeometry import BuildingGeometry
|
|
4
|
+
from .StructuralComponents import StructuralComponents
|
|
5
|
+
from .ConnectionAndBoundary import ConnectionAndBoundary
|
|
6
|
+
from .LoadAndMaterial import LoadAndMaterial
|
|
7
|
+
from .UserCommand import UserCommand
|
|
8
|
+
from . import WriteInfo
|
|
9
|
+
from . import WriteScript
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
VERSION = '2.0'
|
|
13
|
+
|
|
14
|
+
class Frame:
|
|
15
|
+
version = VERSION
|
|
16
|
+
|
|
17
|
+
def __init__(self, frame_name: str, notes: str=None):
|
|
18
|
+
"""Use this class to define structural parameters of steel moment resisting frame (MRF)
|
|
19
|
+
and write tcl script for analysis using OpenSees. Make sure all the units are [N, mm, t].
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
frame_name (str): Give a name for the considered frame
|
|
23
|
+
notes (str, optional): Optional notes
|
|
24
|
+
"""
|
|
25
|
+
if not frame_name.isidentifier():
|
|
26
|
+
raise ValueError(f'Illegal frame name: {frame_name}')
|
|
27
|
+
self.frame_name = frame_name
|
|
28
|
+
self.notes = notes
|
|
29
|
+
self.BuildingGeometry = BuildingGeometry()
|
|
30
|
+
|
|
31
|
+
def step1_finished(self):
|
|
32
|
+
"""Complete define building geometry"""
|
|
33
|
+
self.BuildingGeometry._finish()
|
|
34
|
+
self.N = self.BuildingGeometry.N # number of stories
|
|
35
|
+
self.bays = self.BuildingGeometry.bays # number of bays
|
|
36
|
+
self.axis = self.BuildingGeometry.axis # number of axes
|
|
37
|
+
self.StructuralComponents = StructuralComponents(self)
|
|
38
|
+
|
|
39
|
+
def step2_finished(self):
|
|
40
|
+
"""Complete define structural components"""
|
|
41
|
+
self.StructuralComponents._finish()
|
|
42
|
+
self.LoadAndMaterial = LoadAndMaterial(self)
|
|
43
|
+
|
|
44
|
+
def step3_finished(self):
|
|
45
|
+
"""Complete define load and material"""
|
|
46
|
+
self.LoadAndMaterial._finished()
|
|
47
|
+
self.ConnectionAndBoundary = ConnectionAndBoundary(self)
|
|
48
|
+
|
|
49
|
+
def step4_finished(self):
|
|
50
|
+
"""Complete define connection and boundary"""
|
|
51
|
+
self.ConnectionAndBoundary._finished()
|
|
52
|
+
self.recorders = {
|
|
53
|
+
'Reactions': True,
|
|
54
|
+
'Drift': True,
|
|
55
|
+
'FloorAccel': True,
|
|
56
|
+
'FloorVel': True,
|
|
57
|
+
'FloorDisp': True,
|
|
58
|
+
'ColumnForce': True,
|
|
59
|
+
'ColumnHinge': True,
|
|
60
|
+
'BeamHinge': True,
|
|
61
|
+
'PanelZone': True
|
|
62
|
+
}
|
|
63
|
+
self.UserComment = UserCommand()
|
|
64
|
+
|
|
65
|
+
def all_steps_finished(self):
|
|
66
|
+
"""Calculate simulation parameters and write them into tcl script"""
|
|
67
|
+
self.StructuralComponents._get_section_properties(self)
|
|
68
|
+
self.StructuralComponents._get_panel_zone_thickness()
|
|
69
|
+
self.LoadAndMaterial._calculate_load(self)
|
|
70
|
+
self.LoadAndMaterial._calculate_PPy(self)
|
|
71
|
+
self.dict_info = WriteInfo.write_info_to_dict(self)
|
|
72
|
+
|
|
73
|
+
def generate_tcl_script(self, dir_):
|
|
74
|
+
"""Write tcl script"""
|
|
75
|
+
self.output_path = Path(dir_)
|
|
76
|
+
if not self.output_path.exists():
|
|
77
|
+
Path.mkdir(self.output_path)
|
|
78
|
+
self.builiding_info = WriteInfo.write_info_to_tcl(self)
|
|
79
|
+
WriteScript.WriteScript(self)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def from_json(file: str | Path) -> Frame:
|
|
83
|
+
"""Get an available model from json file
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
file (str | Path): file path
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
Frame: Object of `Frame`
|
|
90
|
+
"""
|
|
91
|
+
file = Path(file)
|
|
92
|
+
if not file.exists():
|
|
93
|
+
raise FileExistsError(f'file not found')
|
|
94
|
+
with open(file, 'r') as f:
|
|
95
|
+
dict_info = json.load(f)
|
|
96
|
+
frame = Frame(dict_info['name'])
|
|
97
|
+
# Step 1
|
|
98
|
+
frame.BuildingGeometry.story_height = dict_info['BuildingGeometry']['story_height']
|
|
99
|
+
frame.BuildingGeometry.bay_length = dict_info['BuildingGeometry']['bay_length']
|
|
100
|
+
frame.BuildingGeometry.plane_dimensions = tuple(dict_info['BuildingGeometry']['plane_dimensions'])
|
|
101
|
+
frame.BuildingGeometry.MF_number = dict_info['BuildingGeometry']['MF_number']
|
|
102
|
+
frame.BuildingGeometry.exterior_column_tributary_area = tuple(dict_info['BuildingGeometry']['exterior_column_tributary_area'])
|
|
103
|
+
frame.BuildingGeometry.interior_column_tributary_area = tuple(dict_info['BuildingGeometry']['interior_column_tributary_area'])
|
|
104
|
+
frame.step1_finished()
|
|
105
|
+
# Step 2
|
|
106
|
+
for FF, sections in dict_info['StructuralComponents']['beams'].items():
|
|
107
|
+
frame.StructuralComponents.set_beams(int(FF), sections)
|
|
108
|
+
for SS, sections in dict_info['StructuralComponents']['columns'].items():
|
|
109
|
+
frame.StructuralComponents.set_columns(int(SS), sections)
|
|
110
|
+
for FF, thickness in dict_info['StructuralComponents']['set_doubler_plate'].items():
|
|
111
|
+
frame.StructuralComponents.set_doubler_plate(int(FF), thickness)
|
|
112
|
+
frame.StructuralComponents.set_column_splice(*dict_info['StructuralComponents']['column_splice'])
|
|
113
|
+
frame.StructuralComponents.set_beam_splice(*dict_info['StructuralComponents']['beam_splice'])
|
|
114
|
+
if dict_info['StructuralComponents']['RBS_length']:
|
|
115
|
+
frame.StructuralComponents.set_RBS_length(dict_info['StructuralComponents']['RBS_length'])
|
|
116
|
+
frame.step2_finished()
|
|
117
|
+
# Step 3
|
|
118
|
+
FF_ls = list(int(i) for i in dict_info['LoadAndMaterial']['dead_load'].keys())
|
|
119
|
+
SS_ls = list(int(i) for i in dict_info['LoadAndMaterial']['clading_load'].keys())
|
|
120
|
+
frame.LoadAndMaterial.set_dead_load(FF_ls, list(dict_info['LoadAndMaterial']['dead_load'].values()))
|
|
121
|
+
frame.LoadAndMaterial.set_live_load(FF_ls, list(dict_info['LoadAndMaterial']['live_load'].values()))
|
|
122
|
+
frame.LoadAndMaterial.set_cladding_load(SS_ls, list(dict_info['LoadAndMaterial']['clading_load'].values()))
|
|
123
|
+
frame.LoadAndMaterial.set_weight_combination_coefficients(dict_info['LoadAndMaterial']['weight_combination_coefficients'])
|
|
124
|
+
frame.LoadAndMaterial.set_mass_combination_coefficients(dict_info['LoadAndMaterial']['mass_combination_coefficients'])
|
|
125
|
+
material = dict_info['LoadAndMaterial']['material']
|
|
126
|
+
frame.LoadAndMaterial.set_material(material['E'], material["fy_beam"], material["fy_column"], material["miu"])
|
|
127
|
+
frame.step3_finished()
|
|
128
|
+
# Step 4
|
|
129
|
+
frame.ConnectionAndBoundary.set_base_support(dict_info['ConnectionAndBoundary']['base_support'])
|
|
130
|
+
frame.ConnectionAndBoundary.set_beam_column_connection(dict_info['ConnectionAndBoundary']['beam_column_connection'])
|
|
131
|
+
frame.ConnectionAndBoundary.set_panel_zone_deformation(dict_info['ConnectionAndBoundary']['panel_zone_deformation'])
|
|
132
|
+
if dict_info['ConnectionAndBoundary']['soil_constraint']:
|
|
133
|
+
frame.ConnectionAndBoundary.set_soil_constraint(dict_info['ConnectionAndBoundary']['soil_constraint'])
|
|
134
|
+
frame.step4_finished()
|
|
135
|
+
# finish
|
|
136
|
+
frame.all_steps_finished()
|
|
137
|
+
frame.dict_info['References'] = dict_info['References']
|
|
138
|
+
return frame
|