Skip to content

Commit ad72763

Browse files
committed
Tracking plastic forces at member ends
1 parent 7ea3c90 commit ad72763

File tree

2 files changed

+37
-22
lines changed

2 files changed

+37
-22
lines changed

PyNite/FEModel3D.py

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2193,7 +2193,7 @@ def _not_ready_yet_analyze_pushover(self, log=False, check_stability=True, push_
21932193

21942194
# Step through each load combination
21952195
for combo in combo_list:
2196-
2196+
21972197
# Skip the pushover combo
21982198
if combo.name == push_combo:
21992199
continue
@@ -2202,6 +2202,11 @@ def _not_ready_yet_analyze_pushover(self, log=False, check_stability=True, push_
22022202
print('')
22032203
print('- Analyzing load combination ' + combo.name)
22042204

2205+
# Reset nonlinear material member end forces to zero
2206+
for member in self.Members.values():
2207+
member._fxi, member._myi, member._mzi = 0, 0, 0
2208+
member._fxj, member._myj, member._mzj = 0, 0, 0
2209+
22052210
# Get the pushover load step and initialize the load factor
22062211
load_step = list(self.LoadCombos[push_combo].factors.values())[0]
22072212
load_factor = load_step
@@ -2220,13 +2225,14 @@ def _not_ready_yet_analyze_pushover(self, log=False, check_stability=True, push_
22202225
P1_push, P2_push = Analysis._partition(self, self.P(push_combo), D1_indices, D2_indices)
22212226

22222227
# Solve the current load combination without the pushover load applied
2223-
Analysis._PDelta_step(self, combo.name, P1, FER1, D1_indices, D2_indices, D2, log, sparse, check_stability, max_iter, tol, first_step=True)
2228+
Analysis._PDelta_step(self, combo.name, P1, FER1, D1_indices, D2_indices, D2, log, sparse, check_stability, max_iter, first_step=True)
22242229

2225-
# Delete this next line used for debugging
2226-
fx, my, mz = self.Members['M1'].f(combo.name)[0, 0], self.Members['M1'].f(combo.name)[4, 0], self.Members['M1'].f(combo.name)[5, 0]
2227-
dummy = 1
2230+
# Since a P-Delta analysis was just run, we'll need to correct the solution to flag it
2231+
# as 'pushover' instead of 'PDelta'
2232+
self.solution = 'pushover'
22282233

2229-
# Apply the pushover load in steps, summing deformations as we go, until the full pushover load has been analyzed
2234+
# Apply the pushover load in steps, summing deformations as we go, until the full
2235+
# pushover load has been analyzed
22302236
while load_factor <= 1:
22312237

22322238
# Inform the user which pushover load step we're on
@@ -2236,8 +2242,14 @@ def _not_ready_yet_analyze_pushover(self, log=False, check_stability=True, push_
22362242
# Run the next pushover load step
22372243
Analysis._pushover_step(self, combo.name, push_combo, step_num, P1_push, FER1_push, D1_indices, D2_indices, D2, log, sparse, check_stability)
22382244

2239-
# Delete this next line used for debugging
2240-
fx, my, mz = self.Members['M1'].f(combo.name)[0, 0], self.Members['M1'].f(combo.name)[4, 0], self.Members['M1'].f(combo.name)[5, 0]
2245+
# Update nonlinear material member end forces for each member
2246+
for member in self.Members.values():
2247+
member._fxi = member.f(combo.name, push_combo, step_num)[0, 0]
2248+
member._myi = member.f(combo.name, push_combo, step_num)[4, 0]
2249+
member._mzi = member.f(combo.name, push_combo, step_num)[5, 0]
2250+
member._fxj = member.f(combo.name, push_combo, step_num)[6, 0]
2251+
member._myj = member.f(combo.name, push_combo, step_num)[10, 0]
2252+
member._mzj = member.f(combo.name, push_combo, step_num)[11, 0]
22412253

22422254
# Move on to the next load step
22432255
load_factor += load_step

PyNite/Member3D.py

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,14 @@ def __init__(self, name, i_node, j_node, material, model, Iy, Iz, J, A, auxNode=
4949
self.Iz = model.Sections[section_name].Iz
5050
self.J = model.Sections[section_name].J
5151

52+
# Variables used to track nonlinear material member end forces
53+
self._fxi = 0
54+
self._myi = 0
55+
self._mzi= 0
56+
self._fxj = 0
57+
self._myj = 0
58+
self._mzj = 0
59+
5260
self.auxNode = auxNode # Optional auxiliary node used to define the member's local z-axis
5361
self.PtLoads = [] # A list of point loads & moments applied to the element (Direction, P, x, case='Case 1') or (Direction, M, x, case='Case 1')
5462
self.DistLoads = [] # A list of linear distributed loads applied to the element (Direction, w1, w2, x1, x2, case='Case 1')
@@ -59,14 +67,10 @@ def __init__(self, name, i_node, j_node, material, model, Iy, Iz, J, A, auxNode=
5967
self.tension_only = tension_only # Indicates whether the member is tension-only
6068
self.comp_only = comp_only # Indicates whether the member is compression-only
6169

62-
# Members need to track whether they are active or not for any given load combination.
63-
# They may become inactive for a load combination during a tension/compression-only
64-
# analysis. This dictionary will be used when the model is solved.
70+
# Members need to track whether they are active or not for any given load combination. They may become inactive for a load combination during a tension/compression-only analysis. This dictionary will be used when the model is solved.
6571
self.active = {} # Key = load combo name, Value = True or False
6672

67-
# The 'Member3D' object will store results for one load combination at a time. To reduce repetative calculations
68-
# the '__solved_combo' variable will be used to track whether the member needs to be resegmented before running
69-
# calculations for any given load combination.
73+
# The 'Member3D' object will store results for one load combination at a time. To reduce repetative calculations the '__solved_combo' variable will be used to track whether the member needs to be resegmented before running calculations for any given load combination.
7074
self.__solved_combo = None # The current solved load combination
7175

7276
# Members need a link to the model they belong to
@@ -230,9 +234,6 @@ def km(self, combo_name='Combo 1', push_combo='Push', step_num=1):
230234
:return: The plastic reduction matrix for the element
231235
:rtype: array
232236
"""
233-
234-
# Get the total elastic end forces applied to the element
235-
f = self.f(combo_name) - self.fer(combo_name) - self.fer(push_combo)*step_num
236237

237238
# Get the elastic local stiffness matrix
238239
ke = self.k()
@@ -251,9 +252,8 @@ def km(self, combo_name='Combo 1', push_combo='Push', step_num=1):
251252
if self.section is None:
252253
raise Exception('Nonlinear material analysis requires member sections to be defined. A section definition is missing for element ' + self.name + '.')
253254
else:
254-
# TODO: Note that we have assumed that `f` is based on the total elastic load acting on the member at this load step, rather than just the change in load for this load step. This seems appropriate, since G is the gradient to the yield surface, and where we are heading along that surface depends on the total load applied, rather than a part of the total load. Need to verify this works correctly with test cases.
255-
Gi = self.section.G(f[0, 0], f[4, 0], f[5, 0])
256-
Gj = self.section.G(f[6, 0], f[10, 0], f[11, 0])
255+
Gi = self.section.G(self._fxi, self._myi, self._mzi)
256+
Gj = self.section.G(self._fxj, self._myj, self._mzj)
257257

258258
# Expand the gradients for a 12 degree of freedom element
259259
zeros_array = zeros((6, 1))
@@ -427,8 +427,8 @@ def _partition(self, unp_matrix):
427427
return m11, m12, m21, m22
428428

429429
#%%
430-
def f(self, combo_name='Combo 1'):
431-
"""Returns the member's local end force vector for the given load combination.
430+
def f(self, combo_name='Combo 1', push_combo='Push', step_num=1):
431+
"""Returns the member's elastic local end force vector for the given load combination.
432432
433433
:param combo_name: The load combination to get the local end for vector for. Defaults to 'Combo 1'.
434434
:type combo_name: str, optional
@@ -441,6 +441,9 @@ def f(self, combo_name='Combo 1'):
441441
# Back-calculate the axial force on the member from the axial strain
442442
P = (self.d(combo_name)[6, 0] - self.d(combo_name)[0, 0])*self.A*self.E/self.L()
443443
return add(matmul(add(self.k(), self.kg(P)), self.d(combo_name)), self.fer(combo_name))
444+
elif self.model.solution == 'Pushover':
445+
P = self._fxj -self._fxi
446+
return add(matmul(add(self.k(), self.kg(P), self.km(combo_name, push_combo, step_num)), self.d(combo_name)), self.fer(combo_name))
444447
else:
445448
return add(matmul(self.k(), self.d(combo_name)), self.fer(combo_name))
446449

0 commit comments

Comments
 (0)