Skip to content

Commit

Permalink
Merge pull request #42 from adtzlr/simplify-plots
Browse files Browse the repository at this point in the history
Plots: Remove `config`-argument (always show deformed configuration except for `inc=0`)
  • Loading branch information
adtzlr committed May 3, 2023
2 parents 288900f + a10ce60 commit 944e291
Show file tree
Hide file tree
Showing 33 changed files with 268 additions and 338 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@ All notable changes to this project will be documented in this file. The format

## [Unreleased]

### Changed
- Change default `force_scale=1.0` (from `0.5`) in `Model.plot_model()` and `Model.plot_movie()`.
- Set `title="UNDEFORMED"` if increment zero is passed to `Model.plot_model(inc=0)`.
- If `inc < 0` in `Model.plot_model()`, evaluate the increment to `inc = len(Model.Results.R) - inc`.

### Removed
- Remove the `config`-argument in `Model.plot_model()` (plot the deformed configuration if `inc > 0`, show the undeformed configuration with `lpf=1` if `inc==0`).
- Don't support `Model.plot_model(force_scale=None)`, must be `float`.

## [1.0.4] - 2023-05-03

### Changed
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<p align="center">Truss Solver for Python.</p>
</p>

[![PyPI version shields.io](https://img.shields.io/pypi/v/trusspy.svg)](https://pypi.python.org/pypi/trusspy/) [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/adtzlr/trusspy/main?labpath=docs%2Fexamples%2Fe101_nb_interactive.ipynb) [![Documentation Status](https://readthedocs.org/projects/trusspy/badge/?version=latest)](https://trusspy.readthedocs.io/en/latest/?badge=latest) [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) ![Made with love in Graz (Austria)](https://img.shields.io/badge/Made%20with%20%E2%9D%A4%EF%B8%8F%20in-Graz%20(Austria)-0c674a) [![codecov](https://codecov.io/gh/adtzlr/trusspy/branch/main/graph/badge.svg?token=2Z0ZKOUKPW)](https://codecov.io/gh/adtzlr/trusspy) [![DOI](https://zenodo.org/badge/145743574.svg)](https://zenodo.org/badge/latestdoi/145743574) ![Codestyle black](https://img.shields.io/badge/code%20style-black-black)
[![PyPI version shields.io](https://img.shields.io/pypi/v/trusspy.svg)](https://pypi.python.org/pypi/trusspy/) [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/adtzlr/trusspy/main?labpath=docs%2Fexamples%2Fe101_nb_interactive.ipynb) <a target="_blank" href="https://colab.research.google.com/github/adtzlr/trusspy/blob/main/docs/examples/e101_nb_interactive.ipynb"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab"/></a> [![Documentation Status](https://readthedocs.org/projects/trusspy/badge/?version=latest)](https://trusspy.readthedocs.io/en/latest/?badge=latest) [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) ![Made with love in Graz (Austria)](https://img.shields.io/badge/Made%20with%20%E2%9D%A4%EF%B8%8F%20in-Graz%20(Austria)-0c674a) [![codecov](https://codecov.io/gh/adtzlr/trusspy/branch/main/graph/badge.svg?token=2Z0ZKOUKPW)](https://codecov.io/gh/adtzlr/trusspy) [![DOI](https://zenodo.org/badge/145743574.svg)](https://zenodo.org/badge/latestdoi/145743574) ![Codestyle black](https://img.shields.io/badge/code%20style-black-black)

**TrussPy** is a 3D **Truss**-Solver written in **Py**-thon which is capable of material and geometric nonlinearities. It uses an object-oriented approach to structure the code in meaningful classes, attributes and methods. TrussPy contains both multistep functionality (multiple loadcase analysis with sequenced external forces) and an adaptive method to control incremental stepwidths. Input files may be written in Excel or directly in Python. A simple post-processing inside TrussPy is directly available via Matplotlib. Model Plots whether in undeformed or deformed configuration with optional contour plots on element forces are easy to show. They may also be generated for a series of increments and saved as a GIF Movie. Last but not least History (a.k.a. x-y) Plots for a series of increments or Path Plots along a given node path may be generated for nodal properties (displacements, forces) or global quantities like the Load-Proportionality-Factor (LPF).

Expand Down
65 changes: 44 additions & 21 deletions docs/examples/e101_nb_interactive.ipynb

Large diffs are not rendered by default.

27 changes: 15 additions & 12 deletions docs/examples/e102.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ The model may be generated with the following code and is shown in the undeforme
ME.add_element( 2 ,conn=(2,3), gprop=[1] )
E = 1 # elastic modulus
ME.assign_material( 'all', [E])
ME.assign_material("all", [E])
with M.Boundaries as MB:
MB.add_bound_U( 1, (0,0,0) )
Expand All @@ -45,13 +45,13 @@ The calculation of the deformation process is started by calling the `build()` a
M.build()
M.run()
M.plot_model(config=['undeformed'],inc=0)
M.plot_model(config=['undeformed','deformed'],
view='xz',
contour='force',
lim_scale=(-1,3,-2.5,1.5)
)
fig, ax = M.plot_history(nodes=[2,2],X='Displacement Z',Y='LPF')
M.plot_model(inc=0)
M.plot_model(
view="xz",
contour="force",
lim_scale=(-1,3,-2.5,1.5),
)
fig, ax = M.plot_history(nodes=[2,2], X="Displacement Z", Y="LPF")
.. figure:: data_e102/model_deformed.*
:scale: 100%
Expand All @@ -69,14 +69,17 @@ Let's re-run the model with a nonlinear material (plasticity with isotropic hard
K = 0.1 # plastic modulus
Sy = 0.1 # initial yield stress
ME.assign_mtype( 'all', 1 )
ME.assign_material( 'all', [E, K, Sy])
ME.assign_mtype("all", 1 )
ME.assign_material("all", [E, K, Sy])"
M.build()
M.run()
fig, ax = M.plot_history(nodes=[2,2],X='Displacement Z',Y='LPF',fig=fig,ax=ax)
ax.legend(['Node 2: linear elastic','Node 2: elastic-plastic (isotropic hardening)'])
fig, ax = M.plot_history(nodes=[2,2], X="Displacement Z", Y="LPF", fig=fig, ax=ax)
ax.legend([
"Node 2: linear elastic",
"Node 2: elastic-plastic (isotropic hardening)",
])
.. figure:: data_e102/history_node2_DispZ-LPF.*
Expand Down
88 changes: 44 additions & 44 deletions docs/examples/e103.rst
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ The model may be generated with the following code and is shown in the undeforme
ME.add_element( 2 ,conn=(2,3), gprop=[1] )
E = 1 # elastic modulus
ME.assign_material( 'all', [E])
ME.assign_material("all", [E])
with M1.Boundaries as MB:
MB.add_bound_U( 1, (0,0,0) )
Expand All @@ -56,16 +56,16 @@ The calculation of the deformation process is started by calling the `build()` a
M1.build()
M1.run()
fig, ax = M1.plot_model(config=['undeformed'],inc=0)
fig, ax = M1.plot_model(config=['undeformed','deformed'],
view='xz',
contour='force',
force_scale=2,
inc=20
)
fig, ax = M1.plot_model(inc=0)
fig, ax = M1.plot_model(
view="xz",
contour="force",
force_scale=2,
inc=20
)
fig1, ax1 = M1.plot_history(nodes=[2,2],X='Displacement X',Y='Displacement Z')
fig2, ax2 = M1.plot_history(nodes=[2,2],X='Displacement Z',Y='LPF')
fig1, ax1 = M1.plot_history(nodes=[2,2], X="Displacement X", Y="Displacement Z")
fig2, ax2 = M1.plot_history(nodes=[2,2], X="Displacement Z", Y="LPF")
.. figure:: data_e103/model1_deformed.*
:scale: 100%
Expand All @@ -89,52 +89,52 @@ Let's re-run the model with a geometric imperfection at node 2 (misalignment `dx
.. code:: python
M2 = tp.Model(logfile=False)
with M2.Nodes as MN:
MN.add_node( 1, coord=( 0, 0, 0))
MN.add_node( 2, coord=( 1.1, 0, 3))
MN.add_node( 3, coord=( 2, 0, 0))
MN.add_node(1, coord=(0, 0, 0))
MN.add_node(2, coord=(1.1, 0, 3))
MN.add_node(3, coord=(2, 0, 0))
with M2.Elements as ME:
ME.add_element( 1, conn=(1,2), gprop=[1] )
ME.add_element( 2 ,conn=(2,3), gprop=[1] )
E = 1 # elastic modulus
ME.assign_material( 'all', [E])
ME.add_element(1, conn=(1, 2), gprop=[1])
ME.add_element(2, conn=(2, 3), gprop=[1])
E = 1 # elastic modulus
ME.assign_material("all", [E])
with M2.Boundaries as MB:
MB.add_bound_U( 1, (0,0,0) )
MB.add_bound_U( 2, (1,0,1) )
MB.add_bound_U( 3, (0,0,0) )
MB.add_bound_U(1, (0, 0, 0))
MB.add_bound_U(2, (1, 0, 1))
MB.add_bound_U(3, (0, 0, 0))
with M2.ExtForces as MF:
MF.add_force( 2, ( 0, 0,-1) )
MF.add_force(2, (0, 0, -1))
M2.Settings.incs = 150
M2.Settings.du = 0.01
M2.Settings.dlpf = 0.01
M2.Settings.xlimit = (2,10)
M2.Settings.xlimit = (2, 10)
M2.Settings.dlpf
M2.Settings.stepcontrol = True
M2.Settings.maxfac = 10
M2.build()
M2.run()
fig, ax = M2.plot_model(config=['undeformed'],lim_scale=(-1,4,-1,4),inc=0)
fig, ax = M2.plot_model(config=['undeformed','deformed'],
view='xz',
contour='force',
lim_scale=(-1,4,-1,4),
force_scale=10,
inc=40
)
fig2, ax2 = M2.plot_history(nodes=[2,2],X='Displacement Z',Y='LPF',fig=fig2,ax=ax2)
ax2.legend(['Node 2: basic model (nDOF=1)','Node 2: imperfection (nDOF=2)'])
fig1, ax1 = M2.plot_history(nodes=[2,2],X='Displacement X',Y='Displacement Z',fig=fig1,ax=ax1)
ax1.legend(['Node 2: basic model (nDOF=1)','Node 2: imperfection (nDOF=2)'])
fig, ax = M2.plot_model(lim_scale=(-1, 4, -1, 4), inc=0)
fig, ax = M2.plot_model(
view="xz", contour="force", lim_scale=(-1, 4, -1, 4), force_scale=10, inc=40
)
fig2, ax2 = M2.plot_history(
nodes=[2, 2], X="Displacement Z", Y="LPF", fig=fig2, ax=ax2
)
ax2.legend(["Node 2: basic model (nDOF=1)", "Node 2: imperfection (nDOF=2)"])
fig1, ax1 = M2.plot_history(
nodes=[2, 2], X="Displacement X", Y="Displacement Z", fig=fig1, ax=ax1
)
ax1.legend(["Node 2: basic model (nDOF=1)", "Node 2: imperfection (nDOF=2)"])
.. figure:: data_e103/model2_deformed.*
:scale: 100%
Expand Down
55 changes: 27 additions & 28 deletions docs/examples/eNTA-A.rst
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ Now we create Nodes with coordinate triples and Elements with a list of node con
ME.add_element( 5, conn=(2,5), gprop=[1 ] )
ME.add_element( 6, conn=(4,5), gprop=[1 ] )
ME.assign_etype( 'all', element_type )
ME.assign_mtype( 'all', material_type )
ME.assign_material( 'all', [youngs_modulus] )
ME.assign_etype("all", element_type)
ME.assign_mtype("all", material_type)
ME.assign_material("all", [youngs_modulus])
Beside Nodes and Elements we have to define Mechanical (U) Boundaries and External Forces. If a node does not contain Boundaries or External Forces the corresponding entries are added automatically by TrussPy.
Expand Down Expand Up @@ -241,36 +241,35 @@ Model Plot and Node History

To visualize the deformed state of the model for increment 40 some model plots are generated. First the undeformed configuration is generated for different views.

.. code:: python
.. code:: python
# undeformed views
fig, ax = M.plot_model(
view="3d", # 'xy', 'yz', 'xz'
contour="force",
lim_scale=(-3, 2, 0, 5, -1, 4), # 3d
# lim_scale=1.4, # plane-view
force_scale=5.0, # 2
inc=0,
)
fig.savefig("model_undeformed_inc0_3d.pdf")
fig.savefig("model_undeformed_inc0_3d.png")
# undeformed views
fig, ax = M.plot_model(config=['undeformed'],
view='3d', #'xy', 'yz', 'xz'
contour='force',
lim_scale=(-3,2,0,5,-1,4), #3d
#lim_scale=1.4, #plane-view
force_scale=5.0, #2
inc=0)
fig.savefig('model_undeformed_inc0_3d.pdf')
fig.savefig('model_undeformed_inc0_3d.png')
For the deformed model the figures are generated with the following code:

.. code:: python
fig, ax = M.plot_model(config=['deformed'],
view='xz',
contour='force',
lim_scale=1.3,
force_scale=500.0,
inc=pinc)
fig, ax = M.plot_model(config=['deformed'],
view='3d',
contour='force',
lim_scale=(-3,2,0,5,-2,3),
force_scale=500.0,
inc=40)
fig, ax = M.plot_model(
view="xz", contour="force", lim_scale=1.3, force_scale=500.0, inc=40
)
fig, ax = M.plot_model(
view="3d",
contour="force",
lim_scale=(-3, 2, 0, 5, -2, 3),
force_scale=500.0,
inc=40,
)
.. figure:: data_eNTA-A/model_contour-force_inc40_3d.png
:scale: 100%
Expand Down
33 changes: 18 additions & 15 deletions docs/usage/getting_started.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ Getting Started
The following basic example is written in interactive mode to show how trusspy works. *For mid-sized models it may be more convenient to use a Spreadsheet (Excel) - input file*. All model parameters except allowed incremental quantities are assumed with default values to enable a clean tutorial for the model creation process. We will consider a model with two nodes and one truss. Although this configuration does not include any geometric nonlinear effects it is the most basic example to start with. The left end of the truss (Node 1) is fixed whereas the right end displacement is free in direction x (Node 2). An external force acts on the right end of the truss in direction x. To sum up, this model contains two nodes, one element and one degree of freedom (DOF).


**News**: It is now possible to run TrussPy online without any installation. This section is now available as an `Interactive Online Notebook`_.
It is also possible to run TrussPy online without any installation. This section is available as an Interactive Online Notebook.
[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/adtzlr/trusspy/main?labpath=docs%2Fexamples%2Fe101_nb_interactive.ipynb) <a target="_blank" href="https://colab.research.google.com/github/adtzlr/trusspy/blob/main/docs/examples/e101_nb_interactive.ipynb"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab"/></a>


.. figure:: images/getting_started-1.png
Expand Down Expand Up @@ -80,13 +81,14 @@ When the job has finished we may post-process the deformed model and plot the fo
.. code:: python
# show results
M.plot_model(config=['deformed'],
view='xz',
contour='force',
lim_scale=(-0.5,3.5,-2,2),
force_scale=1.0,
inc=-1)
M.plot_history(nodes=[2,2], X='Displacement X', Y='Force X')
M.plot_model(
view="xz",
contour="force",
lim_scale=(-0.5,3.5,-2,2),
force_scale=1.0,
inc=-1,
)
M.plot_history(nodes=[2,2], X="Displacement X", Y="Force X")
M.plot_show()
Expand All @@ -109,12 +111,13 @@ It could also be helpful to show the animated deformation process within a simpl
.. code:: python
# show results
M.plot_movie(config=['deformed'],
view='xz',
contour='force',
lim_scale=(-0.5,3.5,-2,2),
force_scale=1.0,
cbar_limits=[-1,1])
M.plot_movie(
view="xz",
contour="force",
lim_scale=(-0.5,3.5,-2,2),
force_scale=1.0,
cbar_limits=[-1,1],
)
.. figure:: images/getting_started-4.gif
:width: 75%
Expand All @@ -125,4 +128,4 @@ It could also be helpful to show the animated deformation process within a simpl

*Important Note:* A **LOT** of assumptions are made to run this model without specifying barely any parameter. Most important ones are incremental displacement values, incremental LPF value and the amount of increments to be solved. These critical parameters are responsible if the model solution will converge or not!

.. _`Interactive Online Notebook`: https://mybinder.org/v2/gh/adtzlr/trusspy/main?labpath=docs%2Fexamples%2Fe101_nb_interactive.ipynb
.. _`Interactive Online Notebook`: https://mybinder.org/v2/gh/adtzlr/trusspy/main?labpath=docs%2Fexamples%2Fe101_nb_interactive.ipynb
2 changes: 1 addition & 1 deletion src/trusspy/__about__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "1.0.4"
__version__ = "2.0.0"
1 change: 0 additions & 1 deletion src/trusspy/elements/element_definition.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ def truss(
umat,
analysis,
):

NA, NE = nodes
XA, XE = Xnodes
UA, UE = Unodes
Expand Down
1 change: 0 additions & 1 deletion src/trusspy/handlers/handler_boundary.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ class BoundaryHandler:
"Handler for Boundary Conditions"

def __init__(self):

# Node-based displacement boundary condition
self.Unodes = None

Expand Down
2 changes: 0 additions & 2 deletions src/trusspy/handlers/handler_extforce.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ def __exit__(self, H_type, H_value, H_traceback):
pass

def add_force(self, F, *args, **kwargs):

if "ExternalForce" not in str(type(F)):
F = ExternalForce(F, *args, **kwargs)

Expand All @@ -41,7 +40,6 @@ def del_force(self, label):
self.forces = np.delete(self.forces, idx, axis=0)

def add_forces(self, FF):

for F in FF:
self.add_force(F)

Expand Down
1 change: 0 additions & 1 deletion src/trusspy/handlers/handler_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ def __exit__(self, H_type, H_value, H_traceback):
pass

def add_node(self, N, *args, **kwargs):

# raw node
if "Node" not in str(type(N)):
N = Node(N, *args, **kwargs)
Expand Down
2 changes: 0 additions & 2 deletions src/trusspy/materials/material_definition.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@


def umat_el(e, de, mat_prop, state_v):

E = mat_prop[0]

s = E * e
Expand All @@ -20,7 +19,6 @@ def umat_el(e, de, mat_prop, state_v):


def umat_elplast_iso(e, de, mat_prop, state_v):

E = mat_prop[0]
K = mat_prop[1]
Sy = mat_prop[2]
Expand Down
Loading

0 comments on commit 944e291

Please sign in to comment.