Skip to content

Commit

Permalink
Merge pull request #241 from JWock82/main
Browse files Browse the repository at this point in the history
v1.0.0 Release
  • Loading branch information
JWock82 authored Feb 1, 2025
2 parents 906b432 + e28de7f commit c15828e
Show file tree
Hide file tree
Showing 80 changed files with 3,430 additions and 508 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ jobs:
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- name: Test with unittest
- name: Test with pytest
run: |
xvfb-run -a coverage run --source=PyNite -m unittest discover
xvfb-run -a coverage run --source=Pynite -m pytest
- name: Convert coverage to XML
run: |
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ PyNiteFEA.egg-info/*
build/*
dist/*
Derivations/*.ipynb_checkpoints
PyNite Report.pdf
Pynite Report.pdf
*.fbp
gui.py
wxTableBases.py
4 changes: 2 additions & 2 deletions Archived/MITC4.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ def B_gamma(self, r, s):
Returns the [B] matrix for shear.
This is provided for reference only and is not actually used by
PyNite. This is the theoretical solution, but it is known to
Pynite. This is the theoretical solution, but it is known to
produce spurious shear forces. It is prone to a phenomenon called
shear locking. Instead of this matrix, the MITC4 [B] matrix is used,
which eliminates shear-locking and can be used for thick and thin
Expand Down Expand Up @@ -355,7 +355,7 @@ def k_b(self):
# combined bending plus membrane stiffness matrix. Some of those terms
# relate to translational stiffness. It seems more rational to only
# look at the terms relating to rotational stiffness. That will be
# PyNite's approach.
# Pynite's approach.
k_rz = min(abs(k[1, 1]), abs(k[2, 2]), abs(k[4, 4]), abs(k[5, 5]),
abs(k[7, 7]), abs(k[8, 8]), abs(k[10, 10]), abs(k[11, 11])
)/1000
Expand Down
14 changes: 11 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
If you would like to contribute to PyNite, please review the following guidelines.
If you would like to contribute to Pynite, please review the following guidelines.

## Coding Guidelines:
1. Over-commenting is better than under commenting. Try to provide a comment for nearly every line of code.
2. Keep it organized. Large methods (functions/subroutines) can often be broken into smaller pieces. Classes and inheritance can also reduce unecessary repetition. Place things where they make sense, and provide method and variable names that are descriptive.
3. Keep it clean. Try to follow the PEP8 style guide as much as possible. There are some conventions in there that don't work well for finite element analysis, where we use capitalization to distinguish between local and global element terms, but otherwise please stick to the guide. I also dislike the limitation on line widths of 79 characters. I end up breaking a lot of lines if I do that. I'm using 100 characters. In some instances I use more (such as for matrix rows that really need to be displayed on one line to make sense). It's recognized that PyNite already has room for improvement in this regard, but going forward PEP8 is the goal.
3. Keep it clean. Try to follow the PEP8 style guide as much as possible. There are some conventions in there that don't work well for finite element analysis, where we use capitalization to distinguish between local and global element terms, but otherwise please stick to the guide. I also dislike the limitation on line widths of 79 characters. I end up breaking a lot of lines if I do that. I'm using 100 characters. In some instances I use more (such as for matrix rows that really need to be displayed on one line to make sense). It's recognized that Pynite already has room for improvement in this regard, but going forward PEP8 is the goal.

## Keys to Getting Your Contributions Integrated Quickly:
1. First and foremost, **KEEP IT SIMPLE**. Every complex problem is nothing more than a series of small simple problems building on each other. Try to break your code into small self-explanatory pieces. Consider issuing larger features as a series of smaller features that add incremental functionality that builds on prior functionality, rather than overhauling the program radically all at once. The addition of CI to PyNite has helped check new code for unexpected issues, but I still like to review this stuff before accepting it. Pull-requests get approved much faster if they are easy for me to follow.
1. First and foremost, **KEEP IT SIMPLE**. Every complex problem is nothing more than a series of small simple problems building on each other. Try to break your code into small self-explanatory pieces. Consider issuing larger features as a series of smaller features that add incremental functionality that builds on prior functionality, rather than overhauling the program radically all at once. The addition of CI to Pynite has helped check new code for unexpected issues, but I still like to review this stuff before accepting it. Pull-requests get approved much faster if they are easy for me to follow.
2. Provide features that most users would find useful.
3. Test and debug your code. Please make sure it's working properly before submitting a pull request.
4. Provide an example problem `.py` file with your code.
5. Provide all pieces necessary in your pull request.
6. Follow the coding guidelines above. This will make review go much faster.
7. Pull requests for any active repository `Projects` will usually be given first priorty.

## Testing

Run tests using `pytest`.

```bash
pytest
```
20 changes: 10 additions & 10 deletions Derivations/DMKQ Quad Element.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"# General Procedure\n",
"The stiffness matrix [K] for an MITC4 element is defined using Equation 5.27 of Reference 1 (all references are listed at the bottom of this notebook):\n",
"# **General Procedure**\n",
"The stiffness matrix [K] for a DKMQ element is defined as:\n",
"\n",
"$[k] = \\int_A [B]^T[C][B] dA$\n",
"$[k] = \\int_A [B]^T[H][B] dA$\n",
"\n",
"This integral will be extremely complex, so integration over the area (A) of the element will be carried out numerically using a procedure called Gaussian Quadrature as outlined in Reference 3 Section 10.4. The greatest difficulty in deriving this element is in coming up with the [B] matrix.\n",
"\n",
Expand All @@ -33,7 +33,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"# Step 1a: Define the Bilinear Shape Functions\n",
"## **Step 1a: Define the Bilinear Shape Functions**\n",
"To obtain the [$B_b$] matrix for bending, we'll need to define what are known as shape functions and their partial derivatives. Their purpose will become clear below. The shape functions are given in Figure 5 of Reference 1:\n",
"\n",
"<div>\n",
Expand All @@ -43,7 +43,7 @@
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": 10,
"metadata": {},
"outputs": [],
"source": [
Expand All @@ -66,7 +66,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"# Step 1b: Calculate the Partial Derivatives of the Shape Functions"
"## **Step 1b: Calculate the Partial Derivatives of the Shape Functions**"
]
},
{
Expand Down Expand Up @@ -216,13 +216,13 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"# Step 1c: Define the Quadratic Shape Functions and Their Partial Derivatives\n",
"## **Step 1c: Define the Quadratic Shape Functions and Their Partial Derivatives**\n",
"Again, from Figure 5 of Reference 1, we obtain interpolation functions are given in Figure 5 of Reference 1:"
]
},
{
"cell_type": "code",
"execution_count": 9,
"execution_count": 4,
"metadata": {},
"outputs": [
{
Expand Down Expand Up @@ -375,7 +375,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"# Step 2: Relate the (x, y) Local Coordinate System to the ($\\xi$, $\\eta$) Natural Coordinate System\n",
"## **Step 2: Relate the (x, y) Local Coordinate System to the ($\\xi$, $\\eta$) Natural Coordinate System**\n",
"DKMQ elements use 2 coordinate systems. The natural coordinate system has its origin at the plate's centroid where $\\xi$ = 0 and $\\eta$ = 0, and goes from -1 to 1 along each axis. For example ($\\xi$, $\\eta$) = (-1, -1) is the bottom left corner of the plate. ($\\xi$, $\\eta$) = (1, 0) is the center of the right edge of the plate. Using the ($\\xi$, $\\eta$) system greatly simplifies integration over the volume of the plate. The interpolation functions are used to relate (or map) the real world (x, y) system to the natural ($\\xi$, $\\eta$) system:\n",
"\n",
"$x = \\sum \\limits _{i=1} ^{4} {N_ix_i}$ &emsp; &emsp; $y = \\sum \\limits _{i=1} ^{4} {N_iy_i}$\n",
Expand All @@ -387,7 +387,7 @@
},
{
"cell_type": "code",
"execution_count": 5,
"execution_count": 11,
"metadata": {},
"outputs": [
{
Expand Down
2 changes: 1 addition & 1 deletion Derivations/README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# About the Derivations
These derivations show how many of the equations used in PyNite were derived. They are Jupyter Notebooks. To view them properly, you'll need to have either Jupyter Notebook or Jupyter Lab installed. This is a free library for scientific computing available here: https://jupyter.org/. You'll also need to have Sympy installed. Sympy is a symbolic math library for Python that was used to symbolically evaluate many of these equations. All outputs have been cleared in these derivations to minimize the amount of space the math formatting takes up in the PyNite repository. Once you open a derivation in Jupyter you'll need to run all cells to view the output.
These derivations show how many of the equations used in Pynite were derived. They are Jupyter Notebooks. To view them properly, you'll need to have either Jupyter Notebook or Jupyter Lab installed. This is a free library for scientific computing available here: https://jupyter.org/. You'll also need to have Sympy installed. Sympy is a symbolic math library for Python that was used to symbolically evaluate many of these equations. All outputs have been cleared in these derivations to minimize the amount of space the math formatting takes up in the Pynite repository. Once you open a derivation in Jupyter you'll need to run all cells to view the output.
10 changes: 5 additions & 5 deletions Examples/Beam on Elastic Foundation.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"""

# Create a new model for the beam
from PyNite import FEModel3D
from Pynite import FEModel3D
boef = FEModel3D()

# Assign the length of the beam
Expand Down Expand Up @@ -50,7 +50,7 @@
boef.add_member('M1', 'N1', 'N' + str(num_nodes), 'Steel', 'W8x35')

# In the next few lines no load case or load combination is being specified. When this is the case,
# PyNite internally creates a default load case ('Case 1') and a default load combination
# Pynite internally creates a default load case ('Case 1') and a default load combination
# ('Combo 1'). This can be handy for quick calculations that don't need the added complexity of
# using load combinations.

Expand All @@ -61,13 +61,13 @@
mid_node = 'N' + str(int(num_nodes/2))
boef.add_node_load(mid_node, 'FY', -40)

# Analyze the model. PyNite's standard solver is most appropriate or this model since there are
# Analyze the model. Pynite's standard solver is most appropriate or this model since there are
# non-linear features (compression-only springs) but no large axial forces that would cause P-Delta
# effects.
boef.analyze(log=True, check_statics=True)

# Render the mdoel with the deformed shape using PyNite's buit-in renderer
from PyNite.Rendering import Renderer
# Render the mdoel with the deformed shape using Pynite's buit-in renderer
from Pynite.Rendering import Renderer
renderer = Renderer(boef)
renderer.annotation_size = 1.5
renderer.deformed_shape = True
Expand Down
8 changes: 4 additions & 4 deletions Examples/Braced Frame - Spring Supported.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Example of a basic 2D tension-only braced frame with gravity and lateral
# loads. Units used for the model in this example are inches and kips.

# Import `FEModel3D` from `PyNite`
from PyNite import FEModel3D
# Import `FEModel3D` from `Pynite`
from Pynite import FEModel3D

# Create a new finite element model
braced_frame = FEModel3D()
Expand Down Expand Up @@ -84,7 +84,7 @@
# be in the Y-direction, due to the fact that the spring only has stiffness in
# that direction. We fix the node so that it's not free to spin or translate
# in the other directions however. If we didn't the node would be unstable and
# the model would crash. PyNite is unforgiving in this regard. Every degree of
# the model would crash. Pynite is unforgiving in this regard. Every degree of
# freedom (3 translations and 3 rotations) at every node must be stabilized so
# it's not free to move infinitely.
braced_frame.def_support('N1s', support_DX=True, support_DY=True, support_DZ=True, support_RX=True, support_RY=True, support_RZ=True)
Expand Down Expand Up @@ -133,7 +133,7 @@

# Display the deformed shape of the structure magnified 50 times with the text
# height 5 model units (inches) high.
from PyNite.Rendering import Renderer
from Pynite.Rendering import Renderer
rndr = Renderer(braced_frame)
rndr.annotation_size = 5
rndr.deformed_shape = True
Expand Down
6 changes: 3 additions & 3 deletions Examples/Braced Frame - Tension Only.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Example of a basic 2D tension-only braced frame with gravity and lateral
# loads. Units used for the model in this example are inches and kips

# Import `FEModel3D` from `PyNite`
from PyNite import FEModel3D
# Import `FEModel3D` from `Pynite`
from Pynite import FEModel3D

# Create a new finite element model
braced_frame = FEModel3D()
Expand Down Expand Up @@ -110,7 +110,7 @@

# Display the deformed shape of the structure magnified 50 times with the text
# height 5 model units (inches) high
from PyNite.Visualization import Renderer
from Pynite.Visualization import Renderer
rndr = Renderer(braced_frame)
rndr.annotation_size = 5
rndr.deformed_shape = True
Expand Down
6 changes: 3 additions & 3 deletions Examples/Circular Bin with Conical Hopper.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from math import pi
from PyNite.FEModel3D import FEModel3D
from PyNite.Mesh import FrustrumMesh, CylinderMesh
from Pynite.FEModel3D import FEModel3D
from Pynite.Mesh import FrustrumMesh, CylinderMesh

t = 0.25/12
E = 29000*1000*12**2
Expand Down Expand Up @@ -69,7 +69,7 @@
model.analyze()

# Render the model. Labels and loads will be turned off to speed up interaction.
from PyNite.Visualization import Renderer
from Pynite.Visualization import Renderer
rndr = Renderer(model)
rndr.annotation_size = 0.1
rndr.render_loads = False
Expand Down
6 changes: 3 additions & 3 deletions Examples/Moment Frame - Lateral Load.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Example of a basic 2D moment frame with gravity and lateral loads
# Units used for the model in this example are inches and kips

# Import `FEModel3D` from `PyNite`
from PyNite import FEModel3D
# Import `FEModel3D` from `Pynite`
from Pynite import FEModel3D

# Create a new finite element model
MomentFrame = FEModel3D()
Expand Down Expand Up @@ -71,7 +71,7 @@
# MomentFrame.analyze_linear(log=True)

# Display the deformed shape of the structure magnified 50 times with the text height 5 model units (inches) high
from PyNite.Visualization import Renderer
from Pynite.Visualization import Renderer
rndr = Renderer(MomentFrame)
rndr.annotation_size = 5
rndr.deformed_shape = True
Expand Down
4 changes: 2 additions & 2 deletions Examples/P-Delta Analysis.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from PyNite import FEModel3D
from Pynite import FEModel3D
from math import tan

# Create the cantilever model
Expand Down Expand Up @@ -39,7 +39,7 @@
# Perform 2nd order analysis
cantilever.analyze_PDelta()

from PyNite.Visualization import Renderer
from Pynite.Visualization import Renderer
renderer = Renderer(cantilever)
renderer.annotation_size = 0.5
renderer.window_width = 750
Expand Down
14 changes: 7 additions & 7 deletions Examples/Rectangular Plate Bending - Qauds.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# This is an example of quadrilateral elements used to model a rectangular wall under uniform load.
# This example demonstrates some of the nuances of using quadrilaterals in PyNite. PyNite's
# This example demonstrates some of the nuances of using quadrilaterals in Pynite. Pynite's
# quadrilaterals are based on an MITC4 formulation, which produces very accurate results for thick
# and thin plates for the most part. One problem however is that stress results at quadrilateral
# corners can be difficult to determine. This example highlights the issue and shows how to work
# around it.

# PyNite has another element which is a rectangular plate bending element. Usually for this type of
# Pynite has another element which is a rectangular plate bending element. Usually for this type of
# problem that element is better suited, as long as the wall does not have significant transverse
# shear deformations, and the elements are not skewed. See the "Rectangular Tank Wall -
# Hydrostatic Loads" example using that element.
Expand All @@ -17,7 +17,7 @@
load = 250 # psf

# Create a finite element model
from PyNite import FEModel3D
from Pynite import FEModel3D
model = FEModel3D()

# Define concrete in the model
Expand All @@ -27,7 +27,7 @@
# Add the mesh to the model
model.add_rectangle_mesh('MSH1', mesh_size, width, height, t, 'Concrete', 1, 1, [0, 0, 0], 'XY', element_type='Quad')

# PyNite automatically generates each mesh when it analyzes. Sometimes it's convenient to generate
# Pynite automatically generates each mesh when it analyzes. Sometimes it's convenient to generate
# the nodes and elements in the mesh in advance so that we can manipulate the mesh prior to
# analysis.
model.meshes['MSH1'].generate()
Expand All @@ -54,7 +54,7 @@
# +-----------------------+

# Set up a renderer for the wall. The quad mesh will be set to show 'Mx' results.
from PyNite.Rendering import Renderer
from Pynite.Rendering import Renderer
renderer = Renderer(model)
renderer.annotation_size = mesh_size/6
renderer.deformed_shape = False
Expand All @@ -70,7 +70,7 @@
# An unsmoothed plot would essentially show quad center stresses at the quad element corners.

# Here are the expected results from Timoshenko's "Theory of Plates and Shells" Table 35, p. 202.
# Note that the deflection values for the PyNite solution are slightly larger, due to transverse
# Note that the deflection values for the Pynite solution are slightly larger, due to transverse
# shear deformations being accounted for.
D = E*t**3/(12*(1-nu**2))
print('Solution from Timoshenko Table 35 for b/a = 2.0:')
Expand All @@ -81,7 +81,7 @@
print('Expected My at Top & Bottom:', -0.0571*load*width**2)

# It should be noted that even the smoothed Mx contours are off by nearly 30% from the theoretical
# solution at the wall boundaries. Because there are no adjacent quads at the boundaries, PyNite
# solution at the wall boundaries. Because there are no adjacent quads at the boundaries, Pynite
# cannot smooth the results there, and center stresses are being reported at the boundaries
# instead of corner stresses. So what's really going on here?

Expand Down
8 changes: 4 additions & 4 deletions Examples/Rectangular Tank Wall - Hydrostatic Loads.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# This example demonstrates how to analyze a tank wall for hydrostatic loads.

# Import a few libraries from PyNite that we'll need
from PyNite.FEModel3D import FEModel3D
from PyNite.Mesh import RectangleMesh
# Import a few libraries from Pynite that we'll need
from Pynite.FEModel3D import FEModel3D
from Pynite.Mesh import RectangleMesh

# Create a finite element model
model = FEModel3D()
Expand Down Expand Up @@ -66,7 +66,7 @@
model.analyze(log=True, check_statics=True)

# Render the model and plot the `Mx` moments.
from PyNite.Rendering import Renderer
from Pynite.Rendering import Renderer
renderer = Renderer(model)
renderer.annotation_size = 0.2
renderer.render_loads = True
Expand Down
Loading

0 comments on commit c15828e

Please sign in to comment.