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 of Points or Circles 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

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

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

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

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

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

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

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
show(s, camera_pos = (5,-5,0), lookat = (0,0,0), geometry_scale=0.3)
../_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))
Properties of point0 (Point)
PropertyValueUnitRemarksExplained
applied_force(277.613,
0.000,
140.209 )
[kN,
kN,
kN]
parent axisApplied 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 axisApplied force and moment on this point
force311.011[kN]total force magnitude as applied on the point

Properties of circle (Circle)
PropertyValueUnitRemarksExplained
axis(0.000,
0.000,
1.000 )
[m,
m,
m]
parent axis systemDirection of the sheave axis
global_axis(0.000,
0.000,
1.000 )
[m,
m,
m]
Global axis direction

../_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.