OpenSeesPyAssistant 1.1
OpenSeesPy for everyone
AnalysisAndPostProcessing.py
Go to the documentation of this file.
1"""Module with pre-made analysis and postprocessing functions. \n
2Carmine Schipani, 2021
3"""
4
5from openseespy.opensees import *
6import matplotlib.pyplot as plt
7import numpy as np
8import os
9import openseespy.postprocessing.Get_Rendering as opsplt
11from OpenSeesPyAssistant.Units import *
14
15
16class Analysis():
17 """Class dedicated to the analysis of the OpenSeesPy model. The Gravity method should be run first to perform the Load-control analysis (apply the vertical load). If no vertical load, this method can be omitted. \n
18 Then only one of the Displacement-control (Pushover or LoadingProtocol) or Load-control (LateralForce) analysis can ran. \n
19 After the analysis reach convergence in the last step, for the postprocessing, the DeformedShape method can be used to see the final deformed shape and the animation of the entire loading protocol;
20 the FiberResponse method can be used to see the animation of the same fiber section recorded during the analysis (strain and/or stress).
21 """
22 def __init__(self, data_dir: str, name_ODB: str, algo = "KrylovNewton", test_type = "NormDispIncr", test_opt = 0, max_iter = MAX_ITER, tol = TOL, allow_smaller_step = False):
23 """
24 The constructor of the class.
25
26 @param data_dir (str): Directory in which the results from the analysis will be stored. Use the recorders (from OpenSeesPy) or the Record method from MemberModel.
27 @param name_ODB (str): Name for the folder in which the data for the animations and the fibers are stored.
28 @param algo (str, optional): Type of alghoritm chosen for the analysis. It detemines how to construct a SolutionAlgorithm object, which determines the sequence of steps taken to solve the non-linear equation.
29 For more information on the available types, see the OpenSeesPy documentation. Defaults to "KrylovNewton".
30 @param test_type (str, optional): Type of test chosen for the analysis. It determines how to construct a ConvergenceTest object.
31 Certain SolutionAlgorithm objects require a ConvergenceTest object to determine if convergence has been achieved at the end of an iteration step.
32 For more information on the available types, see the OpenSeesPy documentation. Defaults to "NormDispIncr".
33 @param test_opt (int, optional): Print-flag from 0 to 5 used to receive more info during the iteration
34 (for example: 0 print nothing and 2 print information on norms and number of iterations at end of successful test).
35 For more information, see the OpenSeesPy documentation. Defaults to 0.
36 @param max_iter (float, optional): Maximal number of iterations to check. Defaults to MAX_ITER (from Constants Module).
37 @param tol (float, optional): Tolerance criteria used to check for convergence. Defaults to TOL (from Constants Module).
38 @param allow_smaller_step (bool, optional): Allow smaller steps in the displacement-control analysis. Defaults to False.
39
40 @exception NegativeValue: The argument max_iter should be positive.
41 @exception NegativeValue: The argument tol should be positive.
42 """
43 if max_iter < 0: raise NegativeValue()
44 if tol < 0: raise NegativeValue()
45 if not os.path.exists(data_dir):
46 print("Folder {} not found in this directory; creating one".format(data_dir))
47 os.makedirs(data_dir)
48
49 self.data_dirdata_dir = data_dir
50 self.name_ODBname_ODB = name_ODB
51 self.algoalgo = algo
52 self.test_typetest_type = test_type
53 self.toltol = tol
54 self.test_opttest_opt = test_opt
55 self.max_itermax_iter = max_iter
56 self.allow_smaller_stepallow_smaller_step = allow_smaller_step
57 self.load_caseload_case = "None"
58
59
60 def Gravity(self, loaded_nodes: list, Fy: list, timeSeries_ID: int, pattern_ID: int, n_step = 10, timeSeries_type = "Linear", pattern_type = "Plain",
61 constraints_type = "Plain", numberer_type = "RCM", system_type = "BandGeneral", analysis_type = "Static", show_plot = False):
62 """
63 Method to perform the gravity analyisis with vertical loadings (load-control).
64 It can be used before calling the Pushover or LoadingProtocol methods that perform the actual anlysis. If no vertical loadings present, this method can be avoided.
65
66 @param loaded_nodes (list): List of nodes that are loaded by the the forces in Fy. The first node will be recorded (thus usually should be in the roof).
67 @param Fy (list): List of vertical loadings (negative is toward the ground, thus compression; see global coordinate system).
68 @param timeSeries_ID (int): ID of the timeseries.
69 @param pattern_ID (int): ID of the pattern.
70 @param n_step (int, optional): Number of steps used to during the analysis to reach the objective state (with 100% vertical loadings imposed). Defaults to 10.
71 @param timeSeries_type (str, optional): Type of timeseries chosen.
72 For more information, see the OpenSeesPy documentation. Defaults to "Linear".
73 @param pattern_type (str, optional): Type of pattern chosen.
74 For more information, see the OpenSeesPy documentation. Defaults to "Plain".
75 @param constraints_type (str, optional): Type of contraints chosen. It detemines how the constraint equations are enforced in the analysis.
76 For more information, see the OpenSeesPy documentation. Defaults to "Plain".
77 @param numberer_type (str, optional): Type of numberer chosen. It determines the mapping between equation numbers and degrees-of-freedom.
78 For more information, see the OpenSeesPy documentation. Defaults to "RCM".
79 @param system_type (str, optional): Type of system of equations chosen. It determines how to construct the LinearSOE and LinearSolver objects to store and solve the system of equations in the analysis.
80 For more information, see the OpenSeesPy documentation. Defaults to "BandGeneral".
81 @param analysis_type (str, optional): Type of analysis chosen. It determines how to construct the Analysis object, which defines what type of analysis is to be performed.
82 For more information, see the OpenSeesPy documentation. Defaults to "Static".
83 @param show_plot (bool, optional): Option to show the 'vertical displacement vs. vertical loading' curve after the analysis. Defaults to False.
84
85 @exception WrongDimension: The dimension of the loaded_nodes and Fy arguments needs to be the same.
86 @exception NegativeValue: The ID of timeSeries_ID needs to be a positive integer.
87 @exception NegativeValue: The ID of pattern_ID needs to be a positive integer.
88 """
89 if len(loaded_nodes) != len(Fy): raise WrongDimension()
90 if timeSeries_ID < 1: raise NegativeValue()
91 if pattern_ID < 1: raise NegativeValue()
92
93 # for mass defined: opsplt.createODB(self.name_ODB, "Gravity", Nmodes = nEigen);
94 # for tracking gravity with ODB: opsplt.createODB(self.name_ODB, "Gravity");
95
96 # Create load pattern
97 timeSeries(timeSeries_type, timeSeries_ID)
98 pattern(pattern_type, timeSeries_ID, pattern_ID)
99 for ii, node_ID in enumerate(loaded_nodes):
100 load(node_ID, 0.0, Fy[ii], 0.0) # load(IDNode, Fx, Fy, Mz)
101 DGravity = 1.0/n_step # load increment
102
103 # Set up analysis options
104 constraints(constraints_type) # how it handles boundary conditions
105 numberer(numberer_type) # renumber dof's to minimize band-width (optimization)
106 system(system_type) # how to store and solve the system of equations in the analysis
107 # For static model, BandGeneral, for transient and/or big model, UmfPack
108 integrator("LoadControl", DGravity) # LoadControl and DisplacementControl only with static model, linear TimeSeries w/ factor of 1
109 # Newmark used for transient model
110 algorithm("Newton") # placeholder
111 analysis(analysis_type) # define type of analysis: static for pushover
112
113 # Analysis
114 dataG = np.zeros((n_step+1,2))
115 print("")
116 print("Gravity analysis starts")
117 for iteration in range(n_step):
118 convergence = self.__LoadCtrlLoop__LoadCtrlLoop(DGravity, iteration,
119 self.algoalgo, self.test_typetest_type, self.toltol, self.test_opttest_opt, self.max_itermax_iter)
120 if convergence != 0: break
121 dataG[iteration+1,0] = nodeDisp(loaded_nodes[0], 2)/mm_unit
122 dataG[iteration+1,1] = getLoadFactor(pattern_ID)*Fy[0]/kN_unit
123
124 if show_plot:
125 plt.plot(dataG[:,0], dataG[:,1])
126 plt.xlabel('Vertical Displacement [mm]')
127 plt.ylabel('Vertical Load [kN]')
128 plt.title('Gravity curve')
129 plt.show()
130
131 loadConst("-time", 0.0)
132
133 print("")
134 print("Gravity complete")
135
136
137 def LateralForce(self, loaded_nodes: list, Fx: list, timeSeries_ID: int, pattern_ID: int, n_step = 1000, fiber_ID_analysed = -1, fiber_section = 1,
138 timeSeries_type = "Linear", pattern_type = "Plain", constraints_type = "Plain", numberer_type = "RCM", system_type = "BandGeneral", analysis_type = "Static",
139 show_plot = True, block = False):
140 """
141 Method to perform the lateral force analyisis with lateral loading (load-control).
142 If this method is called, the LoadingProtocol and Pushover methods should be avoided.
143
144 @param loaded_nodes (list): List of nodes that are loaded by the the forces in Fx. The first node will be recorded (thus usually should be in the roof).
145 @param Fx (list): List of horizontal loadings (negative is toward left; see global coordinate system).
146 @param timeSeries_ID (int): ID of the timeseries.
147 @param pattern_ID (int): ID of the pattern.
148 @param n_step (int, optional): Number of steps used to during the analysis to reach the objective state (with 100% horizontal loadings imposed). Defaults to 1000.
149 @param fiber_ID_analysed (int, optional): The ID of the analysed fiber. If fibers are present in the model and the user wants to save ODB data
150 (to use in the post-processing with for example FiberResponse), assign to this argument the ID of the fiber chosen.
151 -1 will ignore the storage of data for fibers. Defaults to -1.
152 @param fiber_section (int, optional): The section number, i.e. the Gauss integratio number.
153 If the fiber_ID_analysed is equal to -1, this argument is not used. Defaults to 1.
154 @param timeSeries_type (str, optional): Type of timeseries chosen.
155 For more information, see the OpenSeesPy documentation. Defaults to "Linear".
156 @param pattern_type (str, optional): Type of pattern chosen.
157 For more information, see the OpenSeesPy documentation. Defaults to "Plain".
158 @param constraints_type (str, optional): Type of contraints chosen. It detemines how the constraint equations are enforced in the analysis.
159 For more information, see the OpenSeesPy documentation. Defaults to "Plain".
160 @param numberer_type (str, optional): Type of numberer chosen. It determines the mapping between equation numbers and degrees-of-freedom.
161 For more information, see the OpenSeesPy documentation. Defaults to "RCM".
162 @param system_type (str, optional): Type of system of equations chosen. It determines how to construct the LinearSOE and LinearSolver objects to store and solve the system of equations in the analysis.
163 For more information, see the OpenSeesPy documentation. Defaults to "BandGeneral".
164 @param analysis_type (str, optional): Type of analysis chosen. It determines how to construct the Analysis object, which defines what type of analysis is to be performed.
165 For more information, see the OpenSeesPy documentation. Defaults to "Static".
166 @param show_plot (bool, optional): Option to show the 'Horizontal displacement vs. Horizontal loading' curve after the analysis. Defaults to True.
167 @param block (bool, optional): Option to wait the user command 'plt.show()' (avoiding the stop of the program everytime that a plot should pop up). Defaults to False.
168
169 @exception WrongDimension: The dimension of the loaded_nodes and Fx arguments needs to be the same.
170 @exception NegativeValue: The ID of timeSeries_ID needs to be a positive integer.
171 @exception NegativeValue: The ID of pattern_ID needs to be a positive integer.
172 @exception NegativeValue: The ID of fiber_ID_analysed needs to be a positive integer.
173 """
174 if len(loaded_nodes) != len(Fx): raise WrongDimension()
175 if timeSeries_ID < 1: raise NegativeValue()
176 if pattern_ID < 1: raise NegativeValue()
177 if fiber_ID_analysed != -1 and fiber_ID_analysed < 1: raise NegativeValue()
178
179 # for mass defined: opsplt.createODB(self.name_ODB, "LateralForce", Nmodes = nEigen);
180 opsplt.createODB(self.name_ODBname_ODB, "LateralForce");
181 if fiber_ID_analysed != -1: opsplt.saveFiberData2D(self.name_ODBname_ODB, "LateralForce", fiber_ID_analysed, fiber_section)
182
183 # Create load pattern
184 timeSeries(timeSeries_type, timeSeries_ID)
185 pattern(pattern_type, timeSeries_ID, pattern_ID)
186 for ii, node_ID in enumerate(loaded_nodes):
187 load(node_ID, Fx[ii], 0.0, 0.0) # load(IDNode, Fx, Fy, Mz)
188 force = 1.0/n_step # load increment
189
190 # Set up analysis options
191 constraints(constraints_type) # how it handles boundary conditions
192 numberer(numberer_type) # renumber dof's to minimize band-width (optimization)
193 system(system_type) # how to store and solve the system of equations in the analysis
194 # For static model, BandGeneral, for transient and/or big model, UmfPack
195 integrator("LoadControl", force)# LoadControl and DisplacementControl only with static model, linear TimeSeries w/ factor of 1
196 # Newmark used for transient model
197 algorithm("Newton") # placeholder
198 analysis(analysis_type) # define type of analysis: static for pushover
199
200 # Analysis
201 dataLF = np.zeros((n_step+1,2))
202 print("")
203 print("Lateral Force analysis starts")
204 for iteration in range(n_step):
205 convergence = self.__LoadCtrlLoop__LoadCtrlLoop(force, iteration,
206 self.algoalgo, self.test_typetest_type, self.toltol, self.test_opttest_opt, self.max_itermax_iter)
207 if convergence != 0: break
208 dataLF[iteration+1,0] = nodeDisp(loaded_nodes[0], 1)/mm_unit
209 dataLF[iteration+1,1] = getLoadFactor(pattern_ID)*Fx[0]/kN_unit
210
211 if show_plot:
212 plt.plot(dataLF[:,0], dataLF[:,1])
213 plt.xlabel('Lateral Displacement [mm]')
214 plt.ylabel('Lateral Load [kN]')
215 plt.title('Lateral force curve')
216 if block:
217 plt.show()
218
219 loadConst("-time", 0.0)
220
221 print("")
222 print("Lateral force complete")
223 self.load_caseload_case = "LateralForce"
224
225 wipe()
226
227
228 def Pushover(self, CtrlNode: int, Dmax, Dincr, timeSeries_ID: int, pattern_ID: int, Fx = 1*kN_unit, ele_fiber_ID_analysed = -1, fiber_section = 1,
229 timeSeries_type = "Linear", pattern_type = "Plain", constraints_type = "Plain", numberer_type = "RCM", system_type = "UmfPack", analysis_type = "Static",
230 show_plot = True, block = False):
231 """
232 Method to perform a pushover analysis (displacement-control). If this method is called, the LoadingProtocol and LateralForce methods should be avoided.
233
234 @param CtrlNode (int): The node that will be used to impose the displacement Dmax of the pushover analysis.
235 If the show_plot option is True, the curve displayed follows this node.
236 @param Dmax (float): The imposed displacement.
237 @param Dincr (float): The incremental displacement to reach Dmax. To converge, it should be small enough (1000 times smaller of Dmax).
238 @param timeSeries_ID (int): ID of the timeseries.
239 @param pattern_ID (int): ID of the pattern.
240 @param Fx (float, optional): The force imposed at the control node CtrlNode. It is used for convergence reasons and it can be arbitrarly small.
241 Defaults to 1*kN_unit.
242 @param ele_fiber_ID_analysed (int, optional): The ID of the analysed element with fibers. If fibers are present in the model and the user wants to save ODB data
243 (to use in the post-processing with for example FiberResponse), assign to this argument the ID of the element with fibers chosen.
244 -1 will ignore the storage of data for fibers. Defaults to -1.
245 @param fiber_section (int, optional): The section number, i.e. the Gauss integratio number.
246 If the fiber_ID_analysed is equal to -1, this argument is not used. Defaults to 1.
247 @param timeSeries_type (str, optional): Type of timeseries chosen.
248 For more information, see the OpenSeesPy documentation. Defaults to "Linear".
249 @param pattern_type (str, optional): Type of pattern chosen.
250 For more information, see the OpenSeesPy documentation. Defaults to "Plain".
251 @param constraints_type (str, optional): Type of contraints chosen. It detemines how the constraint equations are enforced in the analysis.
252 For more information, see the OpenSeesPy documentation. Defaults to "Plain".
253 @param numberer_type (str, optional): Type of numberer chosen. It determines the mapping between equation numbers and degrees-of-freedom.
254 For more information, see the OpenSeesPy documentation. Defaults to "RCM".
255 @param system_type (str, optional): Type of system of equations chosen. It determines how to construct the LinearSOE and LinearSolver objects to store and solve the system of equations in the analysis.
256 For more information, see the OpenSeesPy documentation. Defaults to "UmfPack".
257 @param analysis_type (str, optional): Type of analysis chosen. It determines how to construct the Analysis object, which defines what type of analysis is to be performed.
258 For more information, see the OpenSeesPy documentation. Defaults to "Static".
259 @param show_plot (bool, optional): Option to show the 'lateral displacement vs. lateral loading' curve after the analysis. Defaults to True.
260 @param block (bool, optional): Option to wait the user command 'plt.show()' (avoiding the stop of the program everytime that a plot should pop up). Defaults to False.
261
262 @exception NegativeValue: The ID of CtrlNode needs to be a positive integer.
263 @exception NegativeValue: The ID of timeSeries_ID needs to be a positive integer.
264 @exception NegativeValue: The ID of pattern_ID needs to be a positive integer.
265 @exception NegativeValue: The ID of ele_fiber_ID_analysed needs to be a positive integer if is different from -1.
266 """
267 if CtrlNode < 1: raise NegativeValue()
268 if timeSeries_ID < 1: raise NegativeValue()
269 if pattern_ID < 1: raise NegativeValue()
270 if ele_fiber_ID_analysed != -1 and ele_fiber_ID_analysed < 1: raise NegativeValue()
271
272 # for mass defined: opsplt.createODB(self.name_ODB, "Pushover", Nmodes = nEigen);
273 opsplt.createODB(self.name_ODBname_ODB, "Pushover");
274 if ele_fiber_ID_analysed != -1: opsplt.saveFiberData2D(self.name_ODBname_ODB, "Pushover", ele_fiber_ID_analysed, fiber_section)
275
276 # Create load pattern
277 timeSeries(timeSeries_type, timeSeries_ID)
278 pattern(pattern_type, timeSeries_ID, pattern_ID)
279 load(CtrlNode, Fx, 0.0, 0.0) # load(IDNode, Fx, Fy, Mz)
280 Nsteps = int(abs(Dmax/Dincr)) # number of pushover analysis steps
281
282 # Set up analysis options
283 constraints(constraints_type) # how it handles boundary conditions
284 numberer(numberer_type) # renumber dof's to minimize band-width (optimization)
285 system(system_type) # how to store and solve the system of equations in the analysis
286 # For static model, BandGeneral, for transient and/or big model, UmfPack
287 integrator("LoadControl", 1) # placeholder
288 algorithm("Newton") # placeholder
289 analysis(analysis_type) # define type of analysis: static for pushover
290
291 # Analysis
292 dataPO = np.zeros((Nsteps+1,2))
293 next_step = 0
294 print("")
295 print("Pushover analysis starts")
296 for iteration in range(Nsteps):
297 next_step = ProgressingPercentage(Nsteps, iteration, next_step)
298 convergence = self.__LatDispCtrlLoop__LatDispCtrlLoop(CtrlNode, Dincr, iteration,
299 self.algoalgo, self.test_typetest_type, self.toltol, self.test_opttest_opt, self.max_itermax_iter, self.allow_smaller_stepallow_smaller_step)
300 if convergence != 0: break
301 dataPO[iteration+1,0] = nodeDisp(CtrlNode, 1)/mm_unit
302 dataPO[iteration+1,1] = getLoadFactor(pattern_ID)*Fx/kN_unit
303
304 if show_plot:
305 plt.plot(dataPO[:,0], dataPO[:,1])
306 plt.xlabel('Horizontal Displacement [mm]')
307 plt.ylabel('Horizontal Load [kN]')
308 plt.title('Pushover curve')
309 if block:
310 plt.show()
311
312 print("")
313 print("Pushover complete")
314 self.load_caseload_case = "Pushover"
315
316 wipe()
317
318
319 def LoadingProtocol(self, CtrlNode: int, discr_LP: np.ndarray, timeSeries_ID: int, pattern_ID: int, Fx = 1*kN_unit, ele_fiber_ID_analysed = -1, fiber_section = 1,
320 timeSeries_type = "Linear", pattern_type = "Plain", constraints_type = "Plain", numberer_type = "RCM", system_type = "UmfPack", analysis_type = "Static",
321 show_plot = True, block = False):
322 """
323 Method to perform a loading protocol analysis (displacement-control). If this method is called, the Pushover and LateralForce methods should be avoided.
324
325 @param CtrlNode (int): The node that will be used to impose the displacement from the discr_LP to perform the analysis.
326 @param discr_LP (np.ndarray): The loading protocol array (1 dimension) discretised. It needs to be filled with imposed displacement, not SDR.
327 Use the functions DiscretizeLoadProtocol and DiscretizeLinearly in FunctionalFeatures module to help create and/or discretise one.
328 @param timeSeries_ID (int): ID of the timeseries.
329 @param pattern_ID (int): ID of the pattern.
330 @param Fx (float, optional): The force imposed at the control node CtrlNode. It is used for convergence reasons and it can be arbitrarly small.
331 Defaults to 1*kN_unit.
332 @param ele_fiber_ID_analysed (int, optional): The ID of the analysed element with fibers. If fibers are present in the model and the user wants to save ODB data
333 (to use in the post-processing with for example FiberResponse), assign to this argument the ID of the element with fibers chosen.
334 -1 will ignore the storage of data for fibers. Defaults to -1.
335 @param fiber_section (int, optional): The section number, i.e. the Gauss integratio number.
336 If the fiber_ID_analysed is equal to -1, this argument is not used. Defaults to 1.
337 @param timeSeries_type (str, optional): Type of timeseries chosen.
338 For more information, see the OpenSeesPy documentation. Defaults to "Linear".
339 @param pattern_type (str, optional): Type of pattern chosen.
340 For more information, see the OpenSeesPy documentation. Defaults to "Plain".
341 @param constraints_type (str, optional): Type of contraints chosen. It detemines how the constraint equations are enforced in the analysis.
342 For more information, see the OpenSeesPy documentation. Defaults to "Plain".
343 @param numberer_type (str, optional): Type of numberer chosen. It determines the mapping between equation numbers and degrees-of-freedom.
344 For more information, see the OpenSeesPy documentation. Defaults to "RCM".
345 @param system_type (str, optional): Type of system of equations chosen. It determines how to construct the LinearSOE and LinearSolver objects to store and solve the system of equations in the analysis.
346 For more information, see the OpenSeesPy documentation. Defaults to "UmfPack".
347 @param analysis_type (str, optional): Type of analysis chosen. It determines how to construct the Analysis object, which defines what type of analysis is to be performed.
348 For more information, see the OpenSeesPy documentation. Defaults to "Static".
349 @param show_plot (bool, optional): Option to show the 'lateral displacement vs. lateral loading' curve after the analysis. Defaults to True.
350 @param block (bool, optional): Option to wait the user command 'plt.show()' (avoiding the stop of the program everytime that a plot should pop up). Defaults to False.
351
352 @exception NegativeValue: The ID of CtrlNode needs to be a positive integer.
353 @exception NegativeValue: The ID of timeSeries_ID needs to be a positive integer.
354 @exception NegativeValue: The ID of pattern_ID needs to be a positive integer.
355 @exception NegativeValue: The ID of fiber_ID_analysed needs to be a positive integer if is different from -1.
356 """
357 if CtrlNode < 1: raise NegativeValue()
358 if timeSeries_ID < 1: raise NegativeValue()
359 if pattern_ID < 1: raise NegativeValue()
360 if ele_fiber_ID_analysed != -1 and ele_fiber_ID_analysed < 1: raise NegativeValue()
361
362 # for mass defined: opsplt.createODB(self.name_ODB, "LoadingProtocol", Nmodes = nEigen);
363 opsplt.createODB(self.name_ODBname_ODB, "LoadingProtocol");
364 if ele_fiber_ID_analysed != -1: opsplt.saveFiberData2D(self.name_ODBname_ODB, "LoadingProtocol", ele_fiber_ID_analysed, fiber_section)
365
366 # Create load pattern
367 timeSeries(timeSeries_type, timeSeries_ID)
368 pattern(pattern_type, timeSeries_ID, pattern_ID)
369 load(CtrlNode, Fx, 0.0, 0.0) # load(IDNode, Fx, Fy, Mz)
370 dU_prev = 0
371 Nsteps = np.size(discr_LP) # number of pushover analysis steps
372
373 # Set up analysis options
374 constraints(constraints_type) # how it handles boundary conditions
375 numberer(numberer_type) # renumber dof's to minimize band-width (optimization)
376 system(system_type) # how to store and solve the system of equations in the analysis
377 # For static model, BandGeneral, for transient and/or big model, UmfPack
378 integrator("LoadControl", 1) # placeholder
379 algorithm("Newton") # placeholder
380 analysis(analysis_type) # define type of analysis: static for LoadingProtocol
381
382 # Analysis
383 dataLP = np.zeros((Nsteps+1,2))
384 next_step = 0
385 print("")
386 print("Loading Protocol analysis starts")
387 for iteration in range(Nsteps):
388 # Compute displacement usinf the given loading protocol (discretized)
389 dU_next = discr_LP[iteration]
390 dU = dU_next - dU_prev
391 dU_prev = dU_next
392
393 next_step = ProgressingPercentage(Nsteps, iteration, next_step)
394 convergence = self.__LatDispCtrlLoop__LatDispCtrlLoop(CtrlNode, dU, iteration,
395 self.algoalgo, self.test_typetest_type, self.toltol, self.test_opttest_opt, self.max_itermax_iter, self.allow_smaller_stepallow_smaller_step)
396 if convergence != 0: break
397 dataLP[iteration+1,0] = nodeDisp(CtrlNode, 1)/mm_unit
398 dataLP[iteration+1,1] = getLoadFactor(pattern_ID)*Fx/kN_unit
399
400 if show_plot:
401 plt.plot(dataLP[:,0], dataLP[:,1])
402 plt.xlabel('Horizontal Displacement [mm]')
403 plt.ylabel('Horizontal Load [kN]')
404 plt.title('Loading Protocol curve')
405 if block:
406 plt.show()
407
408 print("")
409 print("Loading Protocol complete")
410 self.load_caseload_case = "LoadingProtocol"
411
412 wipe()
413
414
415 def __LoadCtrlLoop(self, force, iteration: int, algo = "KrylovNewton", test_type = "NormDispIncr", tol = TOL, test_opt = 0, max_iter = MAX_ITER):
416 """
417 PRIVATE METHOD. It is used perform one load increment 'force' load-control analysis step using 'algo' and 'test_type' as algorithm and test.
418 The integrator is LoadControl. If convergence issues are encountered, the method performa a convergence analysis trying different ways to converge.
419
420 @param force (dougle): The load increment performed.
421 @param iteration (int): The current iteration.
422 @param algo (str, optional): Type of alghoritm chosen for the analysis. It detemines how to construct a SolutionAlgorithm object, which determines the sequence of steps taken to solve the non-linear equation.
423 For more information on the available types, see the OpenSeesPy documentation. Defaults to "KrylovNewton".
424 @param test_type (str, optional): Type of test chosen for the analysis. It determines how to construct a ConvergenceTest object.
425 Certain SolutionAlgorithm objects require a ConvergenceTest object to determine if convergence has been achieved at the end of an iteration step.
426 For more information on the available types, see the OpenSeesPy documentation. Defaults to "NormDispIncr".
427 @param tol (float, optional): Tolerance criteria used to check for convergence. Defaults to TOL (from Constants Module).
428 @param test_opt (int, optional): Print-flag from 0 to 5 used to receive more info during the iteration
429 (for example: 0 print nothing and 2 print information on norms and number of iterations at end of successful test).
430 For more information, see the OpenSeesPy documentation. Defaults to 0.
431 @param max_iter (float, optional): Maximal number of iterations to check. Defaults to MAX_ITER (from Constants Module).
432
433 @exception NegativeValue: iteration needs to be a positive integer.
434 @exception NegativeValue: tol needs to be positive.
435 @exception NegativeValue: max_iter needs to be positive.
436
437 @returns int: 0 if the interation converged.
438 """
439 if iteration < 0: raise NegativeValue()
440 if tol < 0: raise NegativeValue()
441 if max_iter < 0: raise NegativeValue()
442
443 # Default analysis
444 integrator("LoadControl", force) # LoadControl and DisplacementControl only with static model, linear TimeSeries w/ factor of 1
445 # Newmark used for transient model
446 test(test_type, tol, max_iter, test_opt) # type of convergence criteria with tolerance, max iterations;
447 # Normally use EnergyIncr, if conv issues, try NormDispIncr; optional: test_opt = 2 for debugging
448 algorithm(algo) # use Newton's solution algorithm: updates tangent stiffness at every iteration
449 convergence = analyze(1) # this will return zero if no convergence problems were encountered
450
451 # Convergence analysis
452 if convergence != 0:
453 print("---------------------------------------------------------------------------------------")
454 print("Vertical Load-control analysis failed at iteration {}".format(iteration))
455 print("Convergence analysis starts")
456 print("---------------------------------------------------------------------------------------")
457
458 if convergence != 0:
459 print("Try 1")
460 convergence = _ConvergenceTest("KrylovNewton", "NormDispIncr", tol, max_iter, test_opt)
461
462 if convergence != 0:
463 print("Try 2")
464 convergence = _ConvergenceTest("KrylovNewton", "NormDispIncr", tol*10, max_iter*10, test_opt)
465
466 if convergence != 0:
467 print("Try 3")
468 convergence = _ConvergenceTest("KrylovNewton", "EnergyIncr", tol, max_iter, test_opt)
469
470 if convergence != 0:
471 print("Try 4")
472 convergence = _ConvergenceTest("KrylovNewton", "EnergyIncr", tol*10, max_iter*10, test_opt)
473
474 if convergence != 0:
475 print("Try 5")
476 convergence = _ConvergenceTest("Newton", "NormDispIncr", tol, max_iter, test_opt)
477
478 if convergence != 0:
479 print("Try 6")
480 convergence = _ConvergenceTest("Newton", "NormDispIncr", tol*10, max_iter*10, test_opt)
481
482 if convergence != 0:
483 print("Try 7")
484 convergence = _ConvergenceTest("Newton", "EnergyIncr", tol, max_iter, test_opt)
485
486 if convergence != 0:
487 print("Try 8")
488 convergence = _ConvergenceTest("Newton", "EnergyIncr", tol*10, max_iter*10, test_opt)
489
490 if convergence != 0:
491 print("Try 9")
492 convergence = _ConvergenceTest("ModifiedNewton", "NormDispIncr", tol, max_iter, test_opt)
493
494 if convergence != 0:
495 print("Try 10")
496 convergence = _ConvergenceTest("ModifiedNewton", "NormDispIncr", tol*10, max_iter*10, test_opt)
497
498 if convergence != 0:
499 print("Try 11")
500 convergence = _ConvergenceTest("ModifiedNewton", "EnergyIncr", tol, max_iter, test_opt)
501
502 if convergence != 0:
503 print("Try 12")
504 convergence = _ConvergenceTest("ModifiedNewton", "EnergyIncr", tol*10, max_iter*10, test_opt)
505
506 if convergence != 0:
507 print("")
508 print("#############################################################################")
509 print("NO CONVERGENCE! Load-control analysis stops at iteration {} ".format(iteration))
510 print("#############################################################################")
511 print("")
512 else:
513 print("Convergence reached, convergence analysis ends.")
514
515 return convergence
516
517
518 def __LatDispCtrlLoop(self, CtrlNode, dU, iteration, algo = "KrylovNewton", test_type = "NormDispIncr", tol = TOL, test_opt = 0, max_iter = MAX_ITER, allow_smaller_step = False):
519 """
520 PRIVATE METHOD. It is used perform one imposed displacement increment 'dU' displacement-control analysis step using 'algo' and 'test_type' as algorithm and test.
521 The integrator is DisplacementControl. If convergence issues are encountered, the method performa a convergence analysis trying different ways to converge.
522
523 @param CtrlNode ([type]): [description]
524 @param dU (float): The imposed displacement increment for the current iteration
525 @param iteration (int): The current iteration.
526 @param algo (str, optional): Type of alghoritm chosen for the analysis. It detemines how to construct a SolutionAlgorithm object, which determines the sequence of steps taken to solve the non-linear equation.
527 For more information on the available types, see the OpenSeesPy documentation. Defaults to "KrylovNewton".
528 @param test_type (str, optional): Type of test chosen for the analysis. It determines how to construct a ConvergenceTest object.
529 Certain SolutionAlgorithm objects require a ConvergenceTest object to determine if convergence has been achieved at the end of an iteration step.
530 For more information on the available types, see the OpenSeesPy documentation. Defaults to "NormDispIncr".
531 @param tol (float, optional): Tolerance criteria used to check for convergence. Defaults to TOL (from Constants Module).
532 @param test_opt (int, optional): Print-flag from 0 to 5 used to receive more info during the iteration
533 (for example: 0 print nothing and 2 print information on norms and number of iterations at end of successful test).
534 For more information, see the OpenSeesPy documentation. Defaults to 0.
535 @param max_iter (float, optional): Maximal number of iterations to check. Defaults to MAX_ITER (from Constants Module).
536 @param allow_smaller_step (bool, optional): Allow smaller steps in the displacement-control analysis. Defaults to False.
537
538 @exception NegativeValue: iteration needs to be a positive integer.
539 @exception NegativeValue: tol needs to be positive.
540 @exception NegativeValue: max_iter needs to be positive.
541
542 @returns int: 0 if the interation converged.
543 """
544 if iteration < 0: raise NegativeValue()
545 if tol < 0: raise NegativeValue()
546 if max_iter < 0: raise NegativeValue()
547
548 # Default analysis
549 CtrlDOF = 1
550 integrator("DisplacementControl", CtrlNode, CtrlDOF, dU, 1, dU, dU) # use displacement-controlled analysis
551 test(test_type, tol, max_iter, test_opt) # type of convergence criteria with tolerance, max iterations;
552 # Normally use EnergyIncr, if conv issues, try NormDispIncr; optional: test_opt = 2 for debugging
553 algorithm(algo) # use Newton's solution algorithm: updates tangent stiffness at every iteration
554 convergence = analyze(1) # this will return zero if no convergence problems were encountered
555
556 # Convergence analysis
557 if convergence != 0:
558 print("---------------------------------------------------------------------------------------")
559 print("Lateral Displacement-control analysis failed at iteration {} and control node lateral displacement = {} mm.".format(iteration, nodeDisp(CtrlNode, CtrlDOF)/mm_unit))
560 print("Convergence analysis starts")
561 print("---------------------------------------------------------------------------------------")
562
563 if convergence != 0:
564 print("Try 1")
565 convergence = _ConvergenceTest("KrylovNewton", "NormDispIncr", tol, max_iter, test_opt)
566
567 if convergence != 0:
568 print("Try 2")
569 convergence = _ConvergenceTest("KrylovNewton", "NormDispIncr", tol*10, max_iter*10, test_opt)
570
571 if convergence != 0:
572 print("Try 3")
573 convergence = _ConvergenceTest("KrylovNewton", "EnergyIncr", tol, max_iter, test_opt)
574
575 if convergence != 0:
576 print("Try 4")
577 convergence = _ConvergenceTest("KrylovNewton", "EnergyIncr", tol*10, max_iter*10, test_opt)
578
579 if convergence != 0:
580 print("Try 5")
581 convergence = _ConvergenceTest("Newton", "NormDispIncr", tol, max_iter, test_opt)
582
583 if convergence != 0:
584 print("Try 6")
585 convergence = _ConvergenceTest("Newton", "NormDispIncr", tol*10, max_iter*10, test_opt)
586
587 if convergence != 0:
588 print("Try 7")
589 convergence = _ConvergenceTest("Newton", "EnergyIncr", tol, max_iter, test_opt)
590
591 if convergence != 0:
592 print("Try 8")
593 convergence = _ConvergenceTest("Newton", "EnergyIncr", tol*10, max_iter*10, test_opt)
594
595 if convergence != 0:
596 print("Try 9")
597 convergence = _ConvergenceTest("ModifiedNewton", "NormDispIncr", tol, max_iter, test_opt)
598
599 if convergence != 0:
600 print("Try 10")
601 convergence = _ConvergenceTest("ModifiedNewton", "NormDispIncr", tol*10, max_iter*10, test_opt)
602
603 if convergence != 0:
604 print("Try 11")
605 convergence = _ConvergenceTest("ModifiedNewton", "EnergyIncr", tol, max_iter, test_opt)
606
607 if convergence != 0:
608 print("Try 12")
609 convergence = _ConvergenceTest("ModifiedNewton", "EnergyIncr", tol*10, max_iter*10, test_opt)
610
611 if convergence != 0 and allow_smaller_step:
612 print("Use smaller steps")
613 print("10 times more intergator iteration, min_dU = dU/10")
614 integrator("DisplacementControl", CtrlNode, CtrlDOF, dU, 10, dU/10, dU)
615
616 if convergence != 0:
617 print("Try 13")
618 convergence = _ConvergenceTest("KrylovNewton", "NormDispIncr", tol, max_iter, test_opt)
619
620 if convergence != 0:
621 print("Try 14")
622 convergence = _ConvergenceTest("KrylovNewton", "EnergyIncr", tol, max_iter, test_opt)
623
624 if convergence != 0:
625 print("Try 15")
626 convergence = _ConvergenceTest("Newton", "NormDispIncr", tol, max_iter, test_opt)
627
628 if convergence != 0:
629 print("Try 16")
630 convergence = _ConvergenceTest("Newton", "EnergyIncr", tol, max_iter, test_opt)
631
632 if convergence != 0:
633 print("Try 17")
634 convergence = _ConvergenceTest("ModifiedNewton", "NormDispIncr", tol, max_iter, test_opt)
635
636 if convergence != 0:
637 print("Try 19")
638 convergence = _ConvergenceTest("ModifiedNewton", "EnergyIncr", tol, max_iter, test_opt)
639
640 if convergence != 0:
641 print("")
642 print("#############################################################################")
643 print("NO CONVERGENCE! Lateral Displacement-control analysis stops at iteration {} and control node lateral displacement = {} mm.".format(iteration, nodeDisp(CtrlNode, CtrlDOF)/mm_unit))
644 print("#############################################################################")
645 print("")
646 else:
647 print("Convergence reached, convergence analysis ends.")
648
649 return convergence
650
651
652 def DeformedShape(self, scale = 1, animate = False, dt = 0.01):
653 """
654 Method that shows the final deformed shape of the model. It can also show the animation that shows how the model behaved during the analysis.
655
656 @param scale (int, optional): The scaling factor to magnify the deformation. The value should be adjusted for each model. Defaults to 1.
657 @param animate (bool, optional): Option to show the animation of the model during the analysis. Defaults to False.
658 @param dt (float, optional): The time step between every iteration. Defaults to 0.01.
659
660 @exception NameError: The methods for the analysis were not called.
661 """
662 if self.load_caseload_case == "None": raise NameError("The analysis is not complete.")
663
664 # Display deformed shape, the scaling factor needs to be adjusted for each model
665 opsplt.plot_deformedshape(Model = self.name_ODBname_ODB, LoadCase=self.load_caseload_case, scale = scale)
666 if animate:
667 opsplt.animate_deformedshape(Model = self.name_ODBname_ODB, LoadCase=self.load_caseload_case, dt = dt, scale = scale)
668
669
670 def FiberResponse(self, ele_fiber_ID_analysed: int, fiber_section = 1, animate_stress = False, animate_strain = False, fps = 25):
671 """
672 Method that shows the final stress response of the fiber section chosen.
673 It can also show the animation that shows how the fiber section behaved during the analysis. The fiber ID and section needs to be recorded during the analysis,
674 thus if the method LateralForce, Pushover or LoadingProtocol was used, the same fiber ID and section need to be used.
675
676 @param ele_fiber_ID_analysed (int): The ID of the analysed fiber. If fibers are present in the model and the user wants to save ODB data
677 (to use in the post-processing with for example FiberResponse), assign to this argument the ID of the fiber chosen.
678 -1 will ignore the storage of data for fibers.
679 @param fiber_section (int, optional): The section number, i.e. the Gauss integratio number.
680 If the fiber_ID_analysed is equal to -1, this argument is not used. Defaults to 1.
681 @param animate_stress (bool, optional): Option to show the animation of the fiber stress during the analysis. Defaults to False.
682 @param animate_strain (bool, optional): Option to show the animation of the fiber strain during the analysis. Defaults to False.
683 @param fps (int, optional): Number of frame per seconds for the animations. Defaults to 25.
684 @exception NameError: The methods for the analysis were not called.
685 """
686 if self.load_caseload_case == "None": raise NameError("The analysis is not complete.")
687
688 opsplt.plot_fiberResponse2D(self.name_ODBname_ODB, self.load_caseload_case, ele_fiber_ID_analysed, fiber_section, InputType = 'stress')
689 if animate_stress:
690 ani1 = opsplt.animate_fiberResponse2D(self.name_ODBname_ODB, self.load_caseload_case, ele_fiber_ID_analysed, fiber_section, InputType = 'stress', fps = fps)
691 if animate_strain:
692 ani1 = opsplt.animate_fiberResponse2D(self.name_ODBname_ODB, self.load_caseload_case, ele_fiber_ID_analysed, fiber_section, InputType = 'strain', fps = fps)
693
694
695def _ConvergenceTest(algo_type: str, test_type: str, tol, max_iter, test_opt = 0):
696 """
697 PRIVATE FUNCTION. It is used during the convergence analysis to test different ways to reach convergence.
698
699 @param algo_type (str): Type of alghoritm chosen for the analysis. It detemines how to construct a SolutionAlgorithm object, which determines the sequence of steps taken to solve the non-linear equation.
700 For more information on the available types, see the OpenSeesPy documentation.
701 @param test_type (str): Type of test chosen for the analysis. It determines how to construct a ConvergenceTest object.
702 Certain SolutionAlgorithm objects require a ConvergenceTest object to determine if convergence has been achieved at the end of an iteration step.
703 For more information on the available types, see the OpenSeesPy documentation.
704 @param tol (float): Tolerance criteria used to check for convergence.
705 @param max_iter (float): Maximal number of iterations to check.
706 @param test_opt (int, optional): Print-flag from 0 to 5 used to receive more info during the iteration
707 (for example: 0 print nothing and 2 print information on norms and number of iterations at end of successful test).
708 For more information, see the OpenSeesPy documentation. Defaults to 0.
709
710 @returns int: 0 if the interation converged.
711 """
712 print("algorithm: {}".format(algo_type))
713 print("test: {}, tol = {}, max iter = {}".format(test_type, tol, max_iter))
714 test(test_type, tol, max_iter, test_opt)
715 algorithm(algo_type)
716
717 return analyze(1)
718
719
720
Class dedicated to the analysis of the OpenSeesPy model.
def LateralForce(self, list loaded_nodes, list Fx, int timeSeries_ID, int pattern_ID, n_step=1000, fiber_ID_analysed=-1, fiber_section=1, timeSeries_type="Linear", pattern_type="Plain", constraints_type="Plain", numberer_type="RCM", system_type="BandGeneral", analysis_type="Static", show_plot=True, block=False)
Method to perform the lateral force analyisis with lateral loading (load-control).
def LoadingProtocol(self, int CtrlNode, np.ndarray discr_LP, int timeSeries_ID, int pattern_ID, Fx=1 *kN_unit, ele_fiber_ID_analysed=-1, fiber_section=1, timeSeries_type="Linear", pattern_type="Plain", constraints_type="Plain", numberer_type="RCM", system_type="UmfPack", analysis_type="Static", show_plot=True, block=False)
Method to perform a loading protocol analysis (displacement-control).
def __LoadCtrlLoop(self, force, int iteration, algo="KrylovNewton", test_type="NormDispIncr", tol=TOL, test_opt=0, max_iter=MAX_ITER)
def Pushover(self, int CtrlNode, Dmax, Dincr, int timeSeries_ID, int pattern_ID, Fx=1 *kN_unit, ele_fiber_ID_analysed=-1, fiber_section=1, timeSeries_type="Linear", pattern_type="Plain", constraints_type="Plain", numberer_type="RCM", system_type="UmfPack", analysis_type="Static", show_plot=True, block=False)
Method to perform a pushover analysis (displacement-control).
def __init__(self, str data_dir, str name_ODB, algo="KrylovNewton", test_type="NormDispIncr", test_opt=0, max_iter=MAX_ITER, tol=TOL, allow_smaller_step=False)
The constructor of the class.
def DeformedShape(self, scale=1, animate=False, dt=0.01)
Method that shows the final deformed shape of the model.
def Gravity(self, list loaded_nodes, list Fy, int timeSeries_ID, int pattern_ID, n_step=10, timeSeries_type="Linear", pattern_type="Plain", constraints_type="Plain", numberer_type="RCM", system_type="BandGeneral", analysis_type="Static", show_plot=False)
Method to perform the gravity analyisis with vertical loadings (load-control).
def FiberResponse(self, int ele_fiber_ID_analysed, fiber_section=1, animate_stress=False, animate_strain=False, fps=25)
Method that shows the final stress response of the fiber section chosen.
def __LatDispCtrlLoop(self, CtrlNode, dU, iteration, algo="KrylovNewton", test_type="NormDispIncr", tol=TOL, test_opt=0, max_iter=MAX_ITER, allow_smaller_step=False)
def ProgressingPercentage(max_iter, int i, int next_step, step=10)
Function that shows the progressing percentage of an iterative process.