Search
Worked problem: The string problem

Ignore the following drawing code.

from IPython.display import SVG
from collections import namedtuple

V = namedtuple("V", ["x", "y"])

p0 = V(10, 10)
p1 = V(50, 80)
p2 = V(150, 100)
p3 = V(290, 10)

def sub(x):
    return f'<tspan baseline-shift="sub" font-size="10">{x}</tspan>'

SVG(f'''
<svg viewBox="0 0 300 150" xmlns="http://www.w3.org/2000/svg">
  <style>
    ellipse 
    text 
    .string 
    .tstring 
    .bar 
  </style>

  <line x1="{p0.x}" y1="{p0.y}" x2="{p1.x}" y2="{p1.y}" class="string"/>
  <line x1="{p1.x}" y1="{p1.y}" x2="{p2.x}" y2="{p2.y}" class="string"/>
  <line x1="{p2.x}" y1="{p2.y}" x2="{p3.x}" y2="{p3.y}" class="string"/>

  <g>
    <line x1="{p0.x}" y1="{p0.y}" x2="{p3.x}" y2="{p3.y}" class="bar"/>
    <text x="{(p0.x+p3.x)/2}" y="{p0.y + 10}">L</text>
  </g>
  <g>
    <ellipse cx="{p1.x}" cy="{p1.y}" rx="20" ry="20"/>
    <text x="{p1.x}" y="{p1.y}">W{sub(12)}</text>
  </g>
  <g>
    <ellipse cx="{p2.x}" cy="{p2.y}" rx="20" ry="20"/>
    <text x="{p2.x}" y="{p2.y}">W{sub(23)}</text>
  </g>
  <text x="{(p0.x+p1.x)/2 + 5}" y="{(p0.y+p1.y)/2 - 5}" class="tstring">1</text>
  <text x="{(p1.x+p2.x)/2 + 5}" y="{(p1.y+p2.y)/2 - 8}" class="tstring">2</text>
  <text x="{(p2.x+p3.x)/2 - 5}" y="{(p2.y+p3.y)/2 - 5}" class="tstring">3</text>
</svg>
''')

We'll define $T_n$ to be the tension, $L_n$ to be the length, and $\theta_n$ to be the angle from horizantal for each string labeled $n$.

First, we assume the horizontal lengths add up to L:

$$ L_1 \cos \theta_1 + L_2 \cos \theta_2 + L_3 \cos \theta_3 = L \tag{1} $$

Then, we can assume that the vertical lengths cancel:

$$ L_1 \sin \theta_1 + L_2 \sin \theta_2 - L_3 \sin \theta_3 = 0 \tag{2} $$

(We are explicilty defining theta to be the positive angle from the $x$ axis)

We can also use trigometric identites:

$$ \begin{align} \sin^2 \theta_1 + \cos^2 \theta_1 &=& 1 \tag{3} \\ \sin^2 \theta_2 + \cos^2 \theta_2 &=& 1 \tag{4} \\ \sin^2 \theta_3 + \cos^2 \theta_3 &=& 1 \tag{5} \end{align} $$

Finally, we can use physics to get 4 force equations, two for each weight.

$$ \begin{align} T_1 \sin \theta_1 - T_2 \sin \theta_2 - W_{12} &=& 0 \tag{6} \\ T_1 \cos \theta_1 - T_2 \cos \theta_2 &=& 0 \tag{7} \\ T_2 \sin \theta_2 + T_3 \sin \theta_3 - W_{23} &=& 0 \tag{8} \\ T_2 \cos \theta_2 - T_3 \cos \theta_3 &=& 0 \tag{9} \\ \end{align} $$

Now, we have our unknowns:

$$ \mathbf{x} = \left( \begin{matrix} \sin{\theta_1} \\ \sin{\theta_2} \\ \sin{\theta_3} \\ \cos{\theta_1} \\ \cos{\theta_2} \\ \cos{\theta_3} \\ T_1 \\ T_2 \\ T_3 \\ \end{matrix} \right) $$

But, we have the problem that our solution is non-linear:

$$ f(\mathbf{x}) = \left( \begin{matrix} 3 x_5 + 4 x_4 + 4 x_5 - 8 \\ 3 x_0 + 4 x_1 + 4 x_2 \\ x_6 x_0 - x_7 x_1 - 10 \\ x_6 x_3 - x_7 x_4 \\ x_7 x_1 + x_8 x_2 - 20 \\ x_7 x_4 + x_8 x_6 \\ x_0^2 + x_3^2 - 1 \\ x_1^2 + x_4^2 - 1 \\ x_2^2 + x_5^2 - 1 \\ \end{matrix} \right) = 0 $$

Unlike the book, I'm using 0 based indexing. Let's use SymPy to give us some symbolic manipulation abilities:

import numpy as np
import sympy as s
s.init_printing()
x = s.Matrix(s.symbols('x:10'))
f = s.Matrix([
    3*x[3] + 4*x[4] + 4*x[5] - 8,
    3*x[0] + 4*x[1] - 4*x[2],
    x[6]*x[0] - x[7]*x[1] - 10,
    x[6]*x[3] - x[7]*x[4],
    x[7]*x[1] + x[8]*x[2] - 20,
    x[7]*x[4] - x[8]*x[5],
    x[0]**2 + x[3]**2 - 1,
    x[1]**2 + x[4]**2 - 1,
    x[2]**2 + x[5]**2 - 1,
])
f
df = s.Matrix([f.diff(x[i]).T for i in range(9)]).T
df
x_arr = np.array([0.5 , 0.5 , 0.5 , 0.5 , 0.5 , 0.5 , 1. , 1. , 1.])
eps = 1e-3

for it in range(15):
    
    # Compute f and its derivative
    y = f.evalf(subs={a:b for a,b in zip(x, x_arr)})
    M = df.evalf(subs={a:b for a,b in zip(x, x_arr)})

    # Convert these SymPy contraptions into Numpy
    y = np.asarray(y).astype(np.float64).flatten()
    M = np.asarray(M).astype(np.float64)

    # Solve for Δx
    Δx = np.linalg.solve(M, -y)
    
    # Compute new x
    x_arr += Δx
    
    errX = abs(Δx)
    errX[x_arr != 0] /= abs(x_arr[x_arr != 0]) # Relitave error only if x is not 0
    errF = abs(y)
    
    if np.all(errX <= eps) and np.all(errF <= eps):
        break
        
print('Number of iterations = ', it + 1)
print('Final Solution:')
for i in range(9):
    print(f'x_arr[{i}] = {x_arr[i]}')
g_df