1"""Module with useful functions (discretise curves, ID conventions, etc) \n
7import matplotlib.pyplot
as plt
8import openseespy.postprocessing.internal_plotting_functions
as ipltf
16 Function that shows the progressing percentage of an iterative process.
18 @param max_iter (int): Maximal number of interations
19 @param i (int): Current iteration
20 @param next_step (int): Next step of the percentage (set to 0 for the first iteration and then use the return parameter)
21 @param step (int, optional): Size of the step (should be a fraction of 100). Defaults to 10.
23 @returns int: The updated next step
25 if i*100.0/(max_iter-1) >= next_step:
26 print(
"The progression is {}%".format(next_step))
27 return next_step + step
32def DiscretizeLoadProtocol(SDR_LP: np.ndarray, nr_cycles_LP: np.ndarray, discr_first_cycle: int, plot =
False, block =
False, show_original_peaks =
True):
34 Discretized a cyclic load protocol keeping a similar discretisation step throughout the different cycles and keeping in the output the extremes (peaks).
36 @param SDR_LP (np.ndarray): Array (1 dimension) that stores the peaks of the cycles.
37 They needs to be only the positive peaks, beacuse this function will use them as the extreme
for each cycle.
38 @param nr_cycles_LP (np.ndarray): Array (1 dimension) that stores the number of cycles
for every extreme declared
in 'SDR_LP' and its countepart negative.
39 They need to be positive integers.
40 @param discr_first_cycle (int): The number of points
from peak to peak (counting the two peaks)
in the first cycle. It should be odd.
41 @param plot (bool, optional): Option to show the plot of the discretized (
and also the original peaks). Defaults to
False.
42 @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.
43 @param show_original_peaks (bool, optional): Option to show the original peaks to check
if the discretized curve
is correct.
44 The argument plot need to be
True. Defaults to
True.
46 @exception WrongDimension: SDR_LP
and nr_cycles_LP need to be of same length.
47 @exception NegativeValue: SDR_LP needs to have only positive integers.
48 @exception NegativeValue: nr_cycles_LP needs to have only positive integers.
49 @exception NegativeValue: discr_first_cycle needs to be a positive integer.
51 @returns np.array: Array (1 dimension) that stores the new discretized load protocol curve.
53 if np.size(SDR_LP) != np.size(nr_cycles_LP): raise WrongDimension()
54 if any(col < 0 for col in SDR_LP): raise NegativeValue()
55 if any(col < 0 for col in nr_cycles_LP): raise NegativeValue()
56 if discr_first_cycle < 0: raise NegativeValue()
58 if discr_first_cycle % 2 == 0:
59 discr_first_cycle = discr_first_cycle + 1
60 discr_factor = discr_first_cycle / (SDR_LP[0]*2)
61 discretized_LP = [0.0]
64 for i
in range(np.size(SDR_LP)):
65 discr_i = math.ceil(discr_factor*SDR_LP[i]*2)-1;
68 length_tmp = int((discr_i+1)/2)
69 tmp_up = np.linspace(0.0, SDR_LP[i], length_tmp)
70 tmp_down = np.linspace(SDR_LP[i], 0.0, length_tmp)
71 for j
in range(int(nr_cycles_LP[i])):
72 discretized_LP = np.append(discretized_LP, tmp_up[1:length_tmp])
73 discretized_LP = np.append(discretized_LP, tmp_down[1:length_tmp])
74 discretized_LP = np.append(discretized_LP, -tmp_up[1:length_tmp])
75 discretized_LP = np.append(discretized_LP, -tmp_down[1:length_tmp])
77 x_val.append(length_tmp-1+skip_x)
78 skip_x = (length_tmp-1)*(4*(nr_cycles_LP[i]-1)+3)+x_val[-1]
82 fig, ax = plt.subplots()
83 ax.plot(discretized_LP,
'-r', label=
'Discretised LP')
85 ax.set(xlabel=
'Step number [-]', ylabel=
'Unit of the loading protocol',
86 title=
'Discretized loading protocol')
89 if show_original_peaks:
90 ax.plot(x_val, SDR_LP,
'ob', label=
'Original LP')
99def DiscretizeLinearly(LP: np.ndarray, discr: int, plot =
False, block =
False, show_original_LP =
True):
101 This function discretize the curve 'LP' given adding the number of point given by 'discr' between every point (linearly).
103 @param LP (np.ndarray): Array (1 dimension) that stores the curve that needs to be discretized
104 @param discr (int): The number of points to add between every two points of 'LP' (linearly)
105 @param plot (bool, optional): Option to show the plot of the discretized (and also the original LP). Defaults to False.
106 @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.
107 @param show_original_LP (bool, optional): Option to show the original LP to check if the discretized curve is correct. Defaults to True.
109 @returns np.ndarray: Array (1 dimension) that stores the new discretized load protocol.
115 length = 1 + (np.size(LP)-1) * (discr+1)
116 discr_LP = np.zeros(length)
129 xnew = np.linspace(x[0], x[1], discr+2)
130 ynew = np.interp(xnew[1:], x, y)
133 index = np.array(np.arange(discr+1)+1+iter)
134 discr_LP[index] = ynew
138 iter = iter + discr + 1
141 fig, ax = plt.subplots()
142 ax.plot(discr_LP,
'-r', label=
'Discretised LP')
144 ax.set(xlabel=
'Step number [-]', ylabel=
'Unit of the loading protocol',
145 title=
'Discretized loading protocol')
149 x_val = np.arange(0, np.size(discr_LP), discr+1)
150 ax.plot(x_val, LP,
'ob', label=
'Original LP')
161 Function used to construct the ID of the nodes in the grid (first nodes that define the geometry of the model).
162 The conventional grid node ID is xy, with x = the pier position 'pier_axis'; y = the floor position 'floor_axis'.
164 @param pier_axis (int): The pier (or x) postion of the node.
165 @param floor_axis (int): The floor (or y) position of the node.
166 @param max_pier (int, optional): Maximal pier position of the model (used to identify the number of digits).
167 Defaults to -1, e.g. taken equal of pier_axis.
168 @param max_floor (int, optional): Maximal floor position of the model (used to identify the number of digits).
169 Defaults to -1, e.g. taken equal of floor_axis.
171 @exception NameError: Work In Progress: only 9 floors/bays.
172 @exception NegativeValue: The argument pier_axis needs to be a positive integer.
173 @exception NegativeValue: The argument floor_axis needs to be a positive integer.
174 @exception NegativeValue: The argument max_pier needs to be a positive integer
if different
from -1.
175 @exception NegativeValue: The argument max_floor needs to be a positive integer
if different
from -1.
176 @exception WrongArgument: The argument max_pier need to be equal
or bigger to pier_axis
177 @exception WrongArgument: The argument max_floor need to be equal
or bigger to floor_axis
179 @returns int: The grid node ID
183 if pier_axis > 9
or floor_axis > 9
or max_pier > 9
or max_floor > 9:
raise NameError(
"WIP: maximal 9 floors or bays")
184 max_pier = pier_axis
if max_pier == -1
else max_pier
185 max_floor = floor_axis
if max_floor == -1
else max_floor
187 if pier_axis < 0:
raise NegativeValue()
188 if floor_axis < 0:
raise NegativeValue()
189 if max_pier < 0:
raise NegativeValue()
190 if max_floor < 0:
raise NegativeValue()
191 if max_pier < pier_axis:
raise WrongArgument()
192 if max_floor < floor_axis:
raise WrongArgument()
194 max_x_digits = int(math.log10(max_pier))+1
195 max_y_digits = int(math.log10(max_floor))+1
198 return pier_axis*10**max_y_digits + floor_axis
203 Function used to construct IDs for elements and offgrid nodes.
204 It appends to a positive integer number 'prefix' a number of zeros 'n_zeros_between' and at last another positive integer 'suffix'.
205 The conventional element ID is xy(a)x'y'(a') with xya = the node ID in pier x, floor y and offgrid parameter a (optional);
206 x'y'a
' = the node ID in pier x', floor y
' and offgrid parameter a' (optional).
207 For more information on x
and y, see GridIDConvention;
for more information on a, see OffsetNodeIDConvention.
209 @param prefix (int): Prefix of the new ID. For a vertical element it should be the left node ID;
210 for an horizontal one it should be the bottom node.
211 @param suffix (int): Suffix of the new ID. For a vertical element it should be the right node ID;
212 for an horizontal one it should be the top node.
213 @param n_zeros_between (int, optional): Number of zeros to add between the two nodes. Defaults to 0.
215 @exception NegativeValue: The argument prefix needs to be a positive integer.
216 @exception NegativeValue: The argument suffix needs to be a positive integer.
217 @exception NegativeValue: The argument n_zeros_between needs to be a positive integer.
219 @returns int: The combined ID
225 if prefix < 0:
raise NegativeValue()
226 if suffix < 0:
raise NegativeValue()
227 if n_zeros_between < 0:
raise NegativeValue()
229 return int(str(prefix*10**n_zeros_between) + str(suffix))
234 Function used to add node on top of existing ones in the extremes of memebers with springs.
236 @param node_ID (int): Node that we refer to.
237 @param orientation (str): Orientation of the memeber. Can be 'vertical' or 'horizontal'.
238 @param position_i_or_j (str): Position at the start 'i' (left or bottom)
239 or at the end
'j' (right
or top) of
'node_ID' in the member.
241 @exception NegativeValue: The argument node_ID needs to be a positive integer.
242 @exception WrongArgument: The argument position_i_or_j needs to be
'i' or 'j'
243 @exception WrongArgument: The argument orientation needs to be
'vertical' or 'horizontal'
245 @returns int: The combined ID
254 if node_ID < 1:
raise NegativeValue()
255 if position_i_or_j !=
"i" and position_i_or_j !=
"j":
raise WrongArgument()
257 if orientation ==
"vertical":
258 if position_i_or_j ==
"i":
262 elif orientation ==
"horizontal":
263 if position_i_or_j ==
"i":
267 else:
raise WrongArgument()
272 Function that finds the orientation of the vector with direction 'jNode_ID''iNode_ID'.
273 If the the nodes are on top of each other, the function returns 'zero_length'.
275 @param iNode_ID (int): Node i.
276 @param jNode_ID (int): Node j.
278 @exception NegativeValue: The argument iNode_ID needs to be a positive integer.
279 @exception NegativeValue: The argument jNode_ID needs to be a positive integer.
281 @returns str: The orientation of the vector.
283 if iNode_ID < 1:
raise NegativeValue()
284 if jNode_ID < 1:
raise NegativeValue()
286 iNode = np.array(nodeCoord(iNode_ID))
287 jNode = np.array(nodeCoord(jNode_ID))
288 if abs(iNode[0]-jNode[0]) + abs(iNode[1]-jNode[1]) == 0:
290 elif abs(iNode[0]-jNode[0]) < abs(iNode[1]-jNode[1]):
296def plot_member(element_array: list, member_name =
"Member name not defined", show_element_ID =
True, show_node_ID =
True):
298 Function that plots a set of elements. It can be used to check the correctness of a part of the model or of a member.
299 If the entire model need to be plotted, use instead 'plot_model("nodes", "elements")' from openseespy.postprocessing.Get_Rendering. \n
300 Inspired by plot_model written by Anurag Upadhyay and Christian Slotboom.
302 @param element_array (list): An array (list of lists of one dimensions
and length = 3) that store the element
and nodes IDs.
303 An element
is stored
in one list
with 3 entries: the element ID, node i ID
and node j ID.
304 @param member_name (str, optional): The name of what
is plotted. Defaults to
"Member name not defined".
305 @param show_element_ID (bool, optional): Option to show the element IDs. Defaults to
True.
306 @param show_node_ID (bool, optional): Option to show the nodes IDs. Defaults to
True.
308 @exception WrongDimension: element_array needs to be non-empty.
309 @exception WrongDimension: The number of entries
in the lists inside the argument element_array need to be 3.
311 @returns matplotlib.axes._subplots.AxesSubplo: The figure
's wrappr, useful to customise the plot (change axes label, etc).
313 if len(element_array) == 0: raise WrongArgument()
314 if len(element_array[0]) != 3: raise WrongDimension()
316 node_style = {'color':'black', 'marker':'o', 'facecolor':'black','linewidth':0.}
317 node_text_style = {'fontsize':8, 'fontweight':'regular', 'color':'green'}
326 ax = fig.add_subplot(1,1,1)
328 for ele
in element_array:
334 iNode = np.array(nodeCoord(Nodes[0]))
335 jNode = np.array(nodeCoord(Nodes[1]))
336 ipltf._plotBeam2D(iNode, jNode, ax, show_e_ID, eleTag,
"solid")
337 ax.scatter(*iNode, **node_style)
338 ax.scatter(*jNode, **node_style)
340 if abs(sum(iNode - jNode)) > 1e-6:
342 __plt_node(Nodes[0], track_node, iNode, ax, node_text_style)
343 __plt_node(Nodes[1], track_node, jNode, ax, node_text_style, h_align=
'right', v_align=
'bottom')
346 __plt_node(Nodes[0], track_node, iNode, ax, node_text_style, h_align=
'right')
347 __plt_node(Nodes[1], track_node, jNode, ax, node_text_style)
349 print(
"Too many nodes in this elemnet (see shell elements)")
351 ax.set_xlabel(
'x [{}]'.format(length_unit))
352 ax.set_ylabel(
'y [{}]'.format(length_unit))
353 plt.title(
"Visualisation of: {}".format(member_name))
358def plot_nodes(nodes_array: list, name =
"Not defined", show_node_ID =
True):
360 Function that plots a set of nodes. It can be used to check the correctness of the model's geometry.
361 If the entire model need to be plotted, use instead 'plot_model("nodes", "elements")' from openseespy.postprocessing.Get_Rendering.
363 @param nodes_array (list): List of 1 dimension with the IDs of the nodes to be displayed.
364 @param name (str, optional): Name that describe what the plot will show. Defaults to "Not defined".
365 @param show_node_ID (bool, optional): Option to show the node IDs. Defaults to True.
367 @exception WrongArgument: nodes_array needs to be non-empty.
369 @returns (matplotlib.axes._subplots.AxesSubplot): The figure's wrapper, useful to customise the plot (change axes label, etc).
371 if len(nodes_array) == 0:
raise WrongArgument()
373 node_style = {
'color':
'black',
'marker':
'o',
'facecolor':
'black',
'linewidth':0.}
374 node_text_style = {
'fontsize':8,
'fontweight':
'regular',
'color':
'green'}
378 ax = fig.add_subplot(1,1,1)
380 for node_ID
in nodes_array:
381 node_xy = np.array(nodeCoord(node_ID))
382 ax.scatter(*node_xy, **node_style)
384 __plt_node(node_ID, track_node, node_xy, ax, node_text_style)
386 ax.set_xlabel(
'x [{}]'.format(length_unit))
387 ax.set_ylabel(
'y [{}]'.format(length_unit))
388 plt.title(
"Visualisation of: {}".format(name))
393def __plt_node(nodeID: int, track_node: dict, NodeXY, ax, node_text_style, x_off = 0, y_off = 0, h_align =
'left', v_align=
'top'):
395 PRIVATE FUNCTION. Used to plot the nodes in a controlled way (no repetition, position of the IDs, text style).
397 @param nodeID (int): The ID of the node.
398 @param track_node (dict): A dictionary used to avoid plotting a node multiple times.
399 @param NodeXY (list): List of dimension 1, length 2 with the position of the node.
400 @param ax (matplotlib.axes._subplots.AxesSubplot): The figure's wrappr.
401 @param node_text_style (dict): Dictionary for the text style.
402 @param x_off (int, optional): Offset in x direction. Defaults to 0.
403 @param y_off (int, optional): Offset in y direction. Defaults to 0.
404 @param h_align (str, optional): Horizontal alignment ('left' or 'right'). Defaults to 'left'.
405 @param v_align (str, optional): Vertical alignment ('center', 'top' or 'bottom'). Defaults to 'top'.
407 if not nodeID
in track_node:
408 track_node[nodeID] =
True
409 ax.text(NodeXY[0]+x_off, NodeXY[1]+y_off, nodeID,**node_text_style, horizontalalignment=h_align, verticalalignment=v_align)
413 """Class that manage the ID generation.
414 USE ONLY IF EVERY NODE IS DEFINED BY THE USER (because the OpenSeesPyAssistant modules use the convention defined in the functions above).
417 """The class constructor.
426 Method that generate a unique node ID.
428 @returns int: The node ID.
435 Method that generate a unique element ID.
437 @returns int: The element ID.
444 Method that generate a unique material ID.
446 @returns int: The material ID.
453 Method that generate a unique fiber ID.
455 @returns int: The fiber ID.
Class that manage the ID generation.
def GenerateIDFiber(self)
Method that generate a unique fiber ID.
def GenerateIDNode(self)
Method that generate a unique node ID.
def GenerateIDMat(self)
Method that generate a unique material ID.
def __init__(self)
The class constructor.
def GenerateIDElement(self)
Method that generate a unique element ID.
def ProgressingPercentage(max_iter, int i, int next_step, step=10)
Function that shows the progressing percentage of an iterative process.
def OffsetNodeIDConvention(int node_ID, str orientation, str position_i_or_j)
Function used to add node on top of existing ones in the extremes of memebers with springs.
def NodesOrientation(int iNode_ID, int jNode_ID)
Function that finds the orientation of the vector with direction 'jNode_ID''iNode_ID'.
def IDConvention(int prefix, int suffix, n_zeros_between=0)
Function used to construct IDs for elements and offgrid nodes.
def DiscretizeLoadProtocol(np.ndarray SDR_LP, np.ndarray nr_cycles_LP, int discr_first_cycle, plot=False, block=False, show_original_peaks=True)
Discretized a cyclic load protocol keeping a similar discretisation step throughout the different cyc...
def DiscretizeLinearly(np.ndarray LP, int discr, plot=False, block=False, show_original_LP=True)
This function discretize the curve 'LP' given adding the number of point given by 'discr' between eve...
def GridIDConvention(int pier_axis, int floor_axis, max_pier=-1, max_floor=-1)
Function used to construct the ID of the nodes in the grid (first nodes that define the geometry of t...
def plot_member(list element_array, member_name="Member name not defined", show_element_ID=True, show_node_ID=True)
Function that plots a set of elements.
def plot_nodes(list nodes_array, name="Not defined", show_node_ID=True)
Function that plots a set of nodes.