Skip to content

Commit

Permalink
Fix for add_mesh + made stability check optional
Browse files Browse the repository at this point in the history
  • Loading branch information
JWock82 committed Feb 4, 2022
1 parent 31ad425 commit 3e8152e
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 29 deletions.
8 changes: 5 additions & 3 deletions Examples/Circular Bin with Conical Hopper.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,12 @@

# Add the two meshes to the model
model.add_mesh(hopper_mesh)

import cProfile
# cProfile.run('model.add_mesh(shell_mesh)', sort='cumtime')
model.add_mesh(shell_mesh)

# The two meshes have overlapping duplicate nodes that need to be merged
import cProfile
# cProfile.run('model.merge_duplicate_nodes()', sort='cumtime')
model.merge_duplicate_nodes()

Expand All @@ -60,8 +62,8 @@
model.add_load_combo('1.4F', {'Hydrostatic': 1.4})

# Analyze the model
# cProfile.run('model.analyze()', sort='cumtime')
model.analyze()
cProfile.run('model.analyze()', sort='cumtime')
# model.analyze()

# Render the model. Labels and loads will be turned off to speed up interaction.
render_model(model, 0.1, render_loads=True, color_map='dz', combo_name='1.4F', labels=False)
76 changes: 51 additions & 25 deletions PyNite/FEModel3D.py
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,7 @@ def add_mesh(self, mesh, rename=True):
self.Nodes[node.Name] = node
else:
# Get the next available node name and rename the node
new_name = 'N' + str(len(self.Nodes))
new_name = 'N' + str(len(self.Nodes + 1))
node.Name = new_name
self.Nodes[new_name] = node

Expand All @@ -422,7 +422,7 @@ def add_mesh(self, mesh, rename=True):
self.Plates[element.Name] = element
else:
# Get the next available element name and rename the element
new_name = 'P' + str(len(self.Plates))
new_name = 'P' + str(len(self.Plates + 1))
element.Name = new_name
self.Plates[new_name] = element

Expand All @@ -434,7 +434,7 @@ def add_mesh(self, mesh, rename=True):
self.Quads[element.Name] = element
else:
# Get the next available element name and rename the element
new_name = 'Q' + str(len(self.Quads))
new_name = 'Q' + str(len(self.Quads + 1))
element.Name = new_name
self.Quads[new_name] = element

Expand Down Expand Up @@ -1136,7 +1136,7 @@ def _aux_list(self):
# Return the indices and the known displacements
return D1_indices, D2_indices, D2

def K(self, combo_name='Combo 1', log=False, sparse=True):
def K(self, combo_name='Combo 1', log=False, check_stability=True, sparse=True):
"""
Returns the model's global stiffness matrix.
Expand Down Expand Up @@ -1447,9 +1447,10 @@ def K(self, combo_name='Combo 1', log=False, sparse=True):
K = coo_matrix((data, (row, col)), shape=(len(self.Nodes)*6, len(self.Nodes)*6))

# Check that there are no nodal instabilities
if log: print('- Checking nodal stability')
if sparse: self._check_stability(K.tocsr())
else: self._check_stability(K)
if check_stability:
if log: print('- Checking nodal stability')
if sparse: self._check_stability(K.tocsr())
else: self._check_stability(K)

# Return the global stiffness matrix
return K
Expand Down Expand Up @@ -1721,7 +1722,7 @@ def _partition(self, unp_matrix, D1_indices, D2_indices):
m22 = unp_matrix[D2_indices, :][:, D2_indices]
return m11, m12, m21, m22

def analyze(self, log=False, check_statics=False, max_iter=30, sparse=True):
def analyze(self, log=False, check_stability=True, check_statics=False, max_iter=30, sparse=True):
'''
Performs first-order static analysis.
Expand Down Expand Up @@ -1795,9 +1796,9 @@ def analyze(self, log=False, check_statics=False, max_iter=30, sparse=True):

# Get the partitioned global stiffness matrix K11, K12, K21, K22
if sparse == True:
K11, K12, K21, K22 = self._partition(self.K(combo.name, log, sparse).tolil(), D1_indices, D2_indices)
K11, K12, K21, K22 = self._partition(self.K(combo.name, log, check_stability, sparse).tolil(), D1_indices, D2_indices)
else:
K11, K12, K21, K22 = self._partition(self.K(combo.name, log, sparse), D1_indices, D2_indices)
K11, K12, K21, K22 = self._partition(self.K(combo.name, log, check_stability, sparse), D1_indices, D2_indices)

# Get the partitioned global fixed end reaction vector
FER1, FER2 = self._partition(self.FER(combo.name), D1_indices, D2_indices)
Expand Down Expand Up @@ -1976,7 +1977,7 @@ def analyze(self, log=False, check_statics=False, max_iter=30, sparse=True):
if check_statics == True:
self._check_statics()

def analyze_PDelta(self, log=False, max_iter=30, tol=0.01, sparse=True):
def analyze_PDelta(self, log=False, check_stability=True, max_iter=30, tol=0.01, sparse=True):
'''
Performs second order (P-Delta) analysis.
Expand Down Expand Up @@ -2063,9 +2064,9 @@ def analyze_PDelta(self, log=False, max_iter=30, tol=0.01, sparse=True):
if iter_count_PD == 1:

if sparse == True:
K11, K12, K21, K22 = self._partition(self.K(combo.name, log, sparse).tolil(), D1_indices, D2_indices) # Initial stiffness matrix
K11, K12, K21, K22 = self._partition(self.K(combo.name, log, check_stability, sparse).tolil(), D1_indices, D2_indices) # Initial stiffness matrix
else:
K11, K12, K21, K22 = self._partition(self.K(combo.name, log, sparse), D1_indices, D2_indices) # Initial stiffness matrix
K11, K12, K21, K22 = self._partition(self.K(combo.name, log, check_stability, sparse), D1_indices, D2_indices) # Initial stiffness matrix

# Check that the structure is stable
if log: print('- Checking stability')
Expand All @@ -2082,7 +2083,7 @@ def analyze_PDelta(self, log=False, max_iter=30, tol=0.01, sparse=True):
# Calculate the partitioned global stiffness matrices
if sparse == True:

K11, K12, K21, K22 = self._partition(self.K(combo.name, log, sparse).tolil(), D1_indices, D2_indices) # Initial stiffness matrix
K11, K12, K21, K22 = self._partition(self.K(combo.name, log, check_stability, sparse).tolil(), D1_indices, D2_indices) # Initial stiffness matrix
Kg11, Kg12, Kg21, Kg22 = self._partition(self.Kg(combo.name, log, sparse), D1_indices, D2_indices) # Geometric stiffness matrix

# The stiffness matrices are currently `lil` format which is great for
Expand All @@ -2096,7 +2097,7 @@ def analyze_PDelta(self, log=False, max_iter=30, tol=0.01, sparse=True):

else:

K11, K12, K21, K22 = self._partition(self.K(combo.name, log, sparse), D1_indices, D2_indices) # Initial stiffness matrix
K11, K12, K21, K22 = self._partition(self.K(combo.name, log, check_stability, sparse), D1_indices, D2_indices) # Initial stiffness matrix
Kg11, Kg12, Kg21, Kg22 = self._partition(self.Kg(combo.name, log, sparse), D1_indices, D2_indices) # Geometric stiffness matrix

K11 = K11 + Kg11
Expand Down Expand Up @@ -2781,24 +2782,49 @@ def rename(self):
"""

# Rename each node in the model
for id, node in enumerate(self.Nodes.values()):
node.Name = 'N' + str(id)
temp = self.Nodes.copy()
id = 1
for old_key, node in temp.items():
new_key = 'N' + str(id)
self.Nodes[new_key] = self.Nodes.pop(old_key)
self.Nodes[new_key].Name = new_key
id += 1

# Rename each spring in the model
for id, spring in enumerate(self.Springs.values()):
spring.Name = 'S' + str(id)
temp = self.Springs.copy()
id = 1
for old_key, spring in temp.items():
new_key = 'S' + str(id)
self.Springs[new_key] = self.Springs.pop(old_key)
self.Springs[new_key].Name = new_key
id += 1

# Rename each member in the model
for id, member in enumerate(self.Members.values()):
member.Name = 'M' + str(id)
temp = self.Members.copy()
id = 1
for old_key, member in temp.items():
new_key = 'M' + str(id)
self.Members[new_key] = self.Members.pop(old_key)
self.Members[new_key].Name = new_key
id += 1

# Rename each plate in the model
for id, plate in enumerate(self.Plates.values()):
plate.Name = 'P' + str(id)
temp = self.Plates.copy()
id = 1
for old_key, plate in temp.items():
new_key = 'P' + str(id)
self.Plates[new_key] = self.Plates.pop(old_key)
self.Plates[new_key].Name = new_key
id += 1

# Rename each quad in the model
for id, quad in enumerate(self.Quads.values()):
quad.Name = 'Q' + str(id)
temp = self.Quads.copy()
id = 1
for old_key, quad in temp.items():
new_key = 'Q' + str(id)
self.Quads[new_key] = self.Quads.pop(old_key)
self.Quads[new_key].Name = new_key
id += 1

def orphaned_nodes(self):
"""
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ PyNite depends on the following packages:
* sympy: Only needed if you want to view the derivations used to build PyNite.

# What's New?
v0.0.63
* Fixed the `add_mesh` method. It was not working properly after version 0.0.62.
* Made stability checks optional. Stability checks add significant solve time. If you are confident your model is stable, you can skip the stability check by toggling `check_stability` to `False` in your call to your analysis command.

v0.0.62
* PyNite now checks for nodal instabilities when analyzing a model. If nodal instabilities are found, PyNite will output the unstable nodes and directions to the console, and will throw an exception.
* Added a method called `rename` to the `FEModel3D` class for quickly renaming all the nodes and elements in the model in sequential order.
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

setuptools.setup(
name="PyNiteFEA",
version="0.0.62",
version="0.0.63",
author="D. Craig Brinck, PE, SE",
author_email="Building.Code@outlook.com",
description="A simple elastic 3D structural finite element library for Python.",
Expand Down

0 comments on commit 3e8152e

Please sign in to comment.