Cables and Circles#
Cables can be used to model slings, grommets, wires and lines. When cables run over a round surface such as a prong, shackle, sheave or trunnion then circle nodes can be used to accurately model the geometry.
Cable
nodes have a connections
property specifying the nodes that the cable is attached to.
The
connections
property can be set to sequence ofPoints
orCircles
nodes.The
diameter
property of a cable specifies the diamter in [m]The
reversed
property can reverse the direction in which a cable runs over a circle
Circle
nodes have a location, an orientation and a radius.
The location is specified by the parent ofthe circle, which is a
Point
.The orientation is the direction of the central axis of the circle. This is expressed in the axis system of the parent of the parent.
The radius is specified by the property
radius
which is the radius in [m].
All these can be controlled via the GUI. However in the remainder of this section we will illustrate them using the python API.
from DAVE import *
from DAVE.jupyter import *
The history saving thread hit an unexpected error (DatabaseError('database disk image is malformed')).History will not be written to the database.
Loading DAVEcore from: c:\python\venvs\build2\Lib\site-packages\DAVEcore.cp311-win_amd64.pyd
DAVEcore version = 2.4113 from c:\python\venvs\build2\Lib\site-packages\DAVEcore.cp311-win_amd64.pyd
Temporary files are stored in C:\Users\MS12H\AppData\Local\Temp\tmpmqgpvw_t - will be deleted when DAVE exits
s = Scene()
# Two endpoints to connect the cable to
s.new_point('point A', position=(0,0,0))
s.new_point('point B', position=(10,0,0))
# A point in the middle with a circle
s.new_point('midPoint', position=(5,0,3))
s.new_circle('circle',
parent = 'midPoint', # connect to midpoint, this determines the position
axis = (0,1,0), # circle axis into the screen
radius = 1) #
# the new_cable command use endA, endB and 'sheaves' instead of 'connections'
s.new_cable('cable',
endA = 'point A',
endB = 'point B',
sheaves = 'circle',
diameter = 0.5)
show(s, camera_pos = (5,-2,2), lookat = 'y', scale=5 , width = 1200, height = 600)
![../_images/4d6e13d1a838e7d292a5db63211447141abe31bd72382c3d00511f84c16f5f94.png](../_images/4d6e13d1a838e7d292a5db63211447141abe31bd72382c3d00511f84c16f5f94.png)
Direction#
The direction in which the cable runs over the circle is determined by the direction of the cable and the direction of the axis of the circle.
In this case the cable runs from point A (on the left) towards point B (on the right). The axis of the circle is (0,1,0) meaning it poins into the screen. Using the right-hand-rule determines the direction in which the cable runs over the circle.
If we reverse the direction of the cable such that is runs from right to left then we get the following:
s['cable'].connections = ('point B','circle','point A')
show(s, camera_pos = (5,-2,2), lookat = 'y', scale=5 , width = 1200, height = 600)
![../_images/289cb8955adaa89fce7a8ea7613dcb639e9485bfa19aad5e0800a31575d85998.png](../_images/289cb8955adaa89fce7a8ea7613dcb639e9485bfa19aad5e0800a31575d85998.png)
Reversing the direction#
Reversing the direction in which a cable runs over a circle is sometimes needed. For example when the same cable contacts the same circle multiple times (grommets!). In those situations the reversed
property of the Cable
can be used. This is a boolean specifying that the cable should run over the circle in opposite direction:
s['cable'].connections = ('point B','circle','point A') # Keep the cable from right to left
# reverse the connection over the middle connection
s['cable'].reversed = (False, True, False)
show(s, camera_pos = (5,-2,2), lookat = 'y', scale=5 , width = 1200, height = 600)
![../_images/89ffdd12a384339b0d52f56a9f41163662333b98e7767dd9d4fdd3dc79ec6e61.png](../_images/89ffdd12a384339b0d52f56a9f41163662333b98e7767dd9d4fdd3dc79ec6e61.png)
The reversed property has to be specified for all connections at the same time. The following is not possible:
try:
s['cable'].reversed[1] = False
except Exception as E:
print(E)
'tuple' object does not support item assignment
A work-around to do this would be:
temp = list(s['cable'].reversed)
temp[1] = True
s['cable'].reversed = temp
the same hold for connections
.
Cable running over a point / circle with zero diameter#
There is a subtle difference between a point and a circle with a diameter of zero:
If a cable runs over a
Point
, then its centerline runs through that point.If a cable runs over a
Circle
, then its skin runs over the circumference of the circle.
Consider the following example:
s = Scene()
left = s.new_point('left',position = (-5,0,5))
right = s.new_point('right', position = (5,0,5))
bottom_left = s.new_point('BL', position = (-5,0,0))
bottom_right = s.new_point('BR', position = (5,0,0))
circle_with_zero_diamter = s.new_circle('circle',parent=right, axis=(0,1,0), radius = 0)
s.new_cable('cable',endA = bottom_left, endB= bottom_right, sheaves = [left, circle_with_zero_diamter], diameter = 0.5)
show(s,lookat='y', camera_pos = (0,-10,4), scale=5)
![../_images/c8728571443130e30bebbca1a63ba2639da394aa3431f64d036fc5b5eaee0f57.png](../_images/c8728571443130e30bebbca1a63ba2639da394aa3431f64d036fc5b5eaee0f57.png)
Left: cable running over a Point
, right: cable running over a zero-diameter Circle
Making loops (grommets)#
A loop (grommet) can be made by giving a cable the same Circle
as start and end.
s = Scene()
s.new_point('Point1', position = (-5,0,-2))
s.new_circle('Circle1', parent = 'Point1', axis = (0,1,0), radius = 1)
s.new_point('Point2', position = (5,0,2))
s.new_circle('Circle2', parent = 'Point2', axis = (0,1,0), radius = 1)
s.new_cable("grommet", endA="Circle1", endB = "Circle1", sheaves = ["Circle2"], EA = 100)
show(s,lookat='y', camera_pos = (0,-10,0), scale=5)
![../_images/050dac8ffdf26d883c58cad8dd38d1f9687741c41a9b330d96c0f669f648bda6.png](../_images/050dac8ffdf26d883c58cad8dd38d1f9687741c41a9b330d96c0f669f648bda6.png)
See also: Grommet and loop length calculation example
3D effects and sideloads#
DAVE is 3D. This means it has to account for the circle not being in-line with the cable. Consider:
s = Scene()
# Two endpoints to connect the cable to
s.new_point('point A', position=(0,0,0))
s.new_point('point B', position=(10,0,0))
# A point in the middle with a circle
s.new_point('midPoint', position=(5,0,3))
s.new_circle('circle',
parent = 'midPoint', # connect to midpoint, this determines the position
axis = (0,1,0), # circle axis into the screen
radius = 1) #
# the new_cable command use endA, endB and 'sheaves' instead of 'connections'
s.new_cable('cable',
endA = 'point A',
endB = 'point B',
sheaves = 'circle',
diameter = 0.5)
s['point A'].y = -5
s['point B'].y = 3
s['cable'].diameter = 0.01
show(s, lookat = (5,0,2), camera_pos = (15,-5,20))
![../_images/019216b3ea3f9674365339eafb1a0d9bd670b1a3defa8617ec78efbbd0200ffe.png](../_images/019216b3ea3f9674365339eafb1a0d9bd670b1a3defa8617ec78efbbd0200ffe.png)
The general model for determining where the cable leaves the circle is based on the assumption that the cable remains on the circle as long as there is a contact force. This is equivalent to assuming that the circle is a sheave with rims that restrain the cable from running off the sheave sideways.
show(s, lookat = 'y', camera_pos = (5,-5,2), scale=5)
![../_images/ceb805f7e6be4adc5a516d0f68225994909167c65947aea77a89cf1435760763.png](../_images/ceb805f7e6be4adc5a516d0f68225994909167c65947aea77a89cf1435760763.png)
This (sheave) model is used till the sidelead becomes more than 45 degrees (which is not realistic to start with). When the sidelead exceeds 45 degrees the contact point is slowly moved towards the point “halfway” the circle. Note that for the situation where the point is on the axis of the circle the point where the cable leaves the circumference of the circle is undefined (yellow).
s = Scene()
s.new_point('origin')
c = s.new_circle('circle',parent='origin', axis = (1,0,1), radius=1)
for lz in range(15):
p = s.new_point(f'point{lz}', position = (5,0,lz-5))
cab = s.new_cable(f'cable{lz}', endA = p, endB= p, sheaves=[c])
if lz==10:
cab.color = (254,215,0)
else:
cab.color = (25*lz, 0,100-10*lz)
show(s, camera_pos = (22,-16,0), lookat = (0,0,0), geometry_scale=0.3)
![../_images/346199a2f7279b9a176e01185b07ba00f7e3e765795865f6fad67f38e6418ad1.png](../_images/346199a2f7279b9a176e01185b07ba00f7e3e765795865f6fad67f38e6418ad1.png)
show(s, camera_pos = (5,-5,0), lookat = (0,0,0), geometry_scale=0.3)
![../_images/fe2e4774ef2a153ec937d2721a455fd9901c2454ac7f14b63deada7c950cec78.png](../_images/fe2e4774ef2a153ec937d2721a455fd9901c2454ac7f14b63deada7c950cec78.png)
Getting the value of the side-load#
The loads on the circle can be obtained from the point that is it connected to.
s = Scene()
s.new_point('point0')
c = s.new_circle('circle',parent='point0', axis = (0,0,2), radius=1)
p = s.new_point('point', position = (10,0,5))
cab = s.new_cable(f'cable', endA = p, endB= p, sheaves=[c], length = 10, EA = 100)
s.solve_statics()
report(s['point0'],'*force*')
report(c,'*axis*')
show(s, camera_pos = (-2, -30, 10), lookat = (5,0,2))
Property | Value | Unit | Remarks | Explained |
---|---|---|---|---|
applied_force | (277.613, 0.000, 140.209 ) | [kN, kN, kN] | parent axis | Applied force |
applied_force_and_moment_global | (277.613, 0.000, 140.209, 0.000, -14.021, 0.000 ) | [kN, kN, kN, kNm, kNm, kNm] | Global axis | Applied force and moment on this point |
force | 311.011 | [kN] | total force magnitude as applied on the point |
Property | Value | Unit | Remarks | Explained |
---|---|---|---|---|
axis | (0.000, 0.000, 1.000 ) | [m, m, m] | parent axis system | Direction of the sheave axis |
global_axis | (0.000, 0.000, 1.000 ) | [m, m, m] | Global axis direction |
![../_images/3559d556f0c2ddeb8a18cafcbb87a4d71c8429ab0a9b10ad33fcae65f64cdef3.png](../_images/3559d556f0c2ddeb8a18cafcbb87a4d71c8429ab0a9b10ad33fcae65f64cdef3.png)
In general the magnitude of the sideload can be calcualted from the dot-product of the applied load and the direction of the axis. The axis
property of a circle is normalized by default:
import numpy as np
sideload = np.dot(s['circle'].axis, s['point0'].applied_force)
print(f'Sideload on the sheave is {sideload:.2f} kN')
Sideload on the sheave is 140.21 kN
Which, in this case, is equal to the z-component of the force applied on the point.
If an angle is needed then this can be calculated from the inverse sinus:
angle = np.arcsin(sideload/s['point0'].force)
print(f"That's an angle of {np.degrees(angle):.3f} degrees")
That's an angle of 26.796 degrees
Note that the above only works if the circle is the only node that applies force on the point.