Advertisement
Guest User

Untitled

a guest
May 20th, 2019
77
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 7.72 KB | None | 0 0
  1. #!/usr/bin/env python3
  2. # -*- coding: utf-8 -*-
  3. """
  4. Spinner movement equations solved numerically
  5.  
  6.  
  7. @author: lbo
  8. """
  9.  
  10. import matplotlib.pyplot as plt
  11. from mpl_toolkits.mplot3d import Axes3D # noqa: F401 unused import
  12. import numpy as np
  13.  
  14. from collections import namedtuple
  15. import itertools
  16. import math
  17.  
  18. # MoveParams specifies the vertical angle, its first and second derivative,
  19. # and the angle on the horizontal plane of a spinner.
  20. MoveParams = namedtuple('MoveParams', ['x', 'v', 'a', 'phi'])
  21.  
  22. Spins = namedtuple('Spins', ['lv', 'ls', 'lorth'])
  23. StdSpins = namedtuple('StdSpins', ['l1', 'l2', 'l3'])
  24.  
  25. class Calculator:
  26. _initial = MoveParams(0,0,0,0)
  27. _lmbda = 0.0
  28. _nu = 0.0
  29.  
  30. _last = MoveParams(0,0,0,0)
  31.  
  32. _step = 1e-3
  33.  
  34. def __init__(self, initial, nu, lmbda, step=1e-3):
  35. """Construct a calculator given a set of initial conditions."""
  36. self._step = step
  37. assert initial.x != 0, "θ must not be equal to zero in initial conditions"
  38. # Set initial conditions and calculate initial acceleration.
  39. self._initial = initial._replace(a=Calculator.acceleration(initial.x, nu, lmbda))
  40. self._lmbda = lmbda
  41. self._nu = nu
  42. self.reset()
  43.  
  44. def reset(self):
  45. self._last = self._initial
  46.  
  47. # The calculation methods are static and thus do not change the state
  48. # of a Calculator instance.
  49. #
  50. # ν is not v! It's the greek letter nu. Replace with "nu" after it having
  51. # caused the second bug.
  52. def acceleration(θ, ν, λ):
  53. """The equation for d^2θ/dt^2."""
  54. first = -(λ - math.cos(θ))*(1 - λ * math.cos(θ)) / (math.sin(θ)**3)
  55. second = ν * math.sin(θ)
  56. return first + second
  57.  
  58. def next_x(last, step):
  59. return last.x + last.v * step + last.a * step * step * 1/2
  60.  
  61. def next_v(last, a_next, step):
  62. return last.v + (a_next + last.a)/2 * step
  63.  
  64. def incremental_phi(current, last, λ, step):
  65. """Returns the sum term of phi for the current step."""
  66. first = (λ - math.cos(current.x))/(math.sin(current.x)**2)
  67. second = (λ - math.cos(last.x))/(math.sin(last.x)**2)
  68. return 1/2 * step * (first+second)
  69.  
  70. def update(self):
  71. """Run one iteration."""
  72. last = self._last
  73. x_next = Calculator.next_x(last, self._step)
  74. a_next = Calculator.acceleration(x_next, self._nu, self._lmbda)
  75. v_next = Calculator.next_v(last, a_next, self._step)
  76.  
  77. next = MoveParams(x_next, v_next, a_next, 0)
  78. # Calculation of phi requires knowing both the current and last value
  79. # of θ.
  80. phi_next = last.phi + Calculator.incremental_phi(next, last, self._lmbda, self._step,)
  81. next = next._replace(phi=phi_next)
  82. self._last = next
  83. return last
  84.  
  85. def __iter__(self):
  86. return self
  87.  
  88. def __next__(self):
  89. return self.next()
  90.  
  91. def next(self):
  92. return self.update()
  93.  
  94. def spins(self, state=None):
  95. """Calculate angular momentum in local (e3/e3'/e_orth) base."""
  96. state = self._last if not state else state
  97. lv = (self._lmbda - math.cos(state.x))/math.sin(state.x)**2
  98. ls = (1 - self._lmbda * math.cos(state.x))/math.sin(state.x)**2
  99. lorth = state.v
  100. return Spins(lv, ls, lorth)
  101.  
  102. def orth_spins(self, state=None):
  103. """Calculate angular momentum in e1/e2/e3 base. Returns a (l1, l2, l3) tuple."""
  104. state = self._last if not state else state
  105. (lv, ls, lorth) = self.spins(state=state)
  106. # Project local angular momentum vectors onto standard unit vectors e1-e3.
  107. l1 = ls * math.cos(state.phi) * math.sin(state.x) - lorth * math.sin(state.phi)
  108. l2 = ls * math.sin(state.phi) * math.sin(state.x) + lorth * math.cos(state.phi)
  109. l3 = lv + ls * math.cos(state.x)
  110. return StdSpins(l1, l2, l3)
  111.  
  112.  
  113.  
  114. def run(initial, ν, λ, steps=2e3, step=1e-3):
  115. """Run Calculator with given parameters, and return a tuple with (t, theta, phi, lv, ls, lorth) arrays."""
  116. calc = Calculator(initial, ν, λ, step=step)
  117. zeros = lambda: np.zeros((steps))
  118. (t, theta, phi) = (zeros(), zeros(), zeros())
  119. (lv, ls, lorth) = (zeros(), zeros(), zeros())
  120. (l1, l2, l3) = (zeros(), zeros(), zeros())
  121. for (i, p) in enumerate(itertools.islice(calc, int(steps))):
  122. t[i] = i*step
  123. theta[i] = p.x
  124. phi[i] = p.phi
  125. (lv[i], ls[i], lorth[i]) = calc.spins(state=p)
  126. (l1[i], l2[i], l3[i]) = calc.orth_spins(state=p)
  127. return (t, theta, phi, Spins(lv, ls, lorth), StdSpins(l1, l2, l3))
  128.  
  129. def t_plot(t, theta, phi):
  130. fig = plt.figure(dpi=100)
  131. thaxes = fig.add_subplot(111)
  132. thaxes.set_ylabel("θ")
  133. thaxes.xaxis.set_label_text("t / s")
  134. phiaxes = fig.add_subplot(111, sharex=thaxes)
  135. phiaxes.yaxis.tick_right()
  136. phiaxes.yaxis.set_label_position("right")
  137. phiaxes.yaxis.set_label_text("ɸ")
  138.  
  139. thaxes.plot(t, theta)
  140. phiaxes.plot(t, phi)
  141. plt.show()
  142.  
  143. def polar_axis_plot(theta, phi, axes=None, **kwargs):
  144. """Plot a bird's perspective of a spinner."""
  145. # Transform theta into polar argument r.
  146. r = np.sin(theta)
  147.  
  148. if not axes:
  149. fig = plt.figure(dpi=100)
  150. axes = fig.add_subplot(111, projection='polar')
  151. axes.plot(phi, r, **kwargs)
  152.  
  153. def plot_assignment_parameters(steps=5000, step=1e-2):
  154. # 3 b(i)
  155. fig = plt.figure(figsize=(4 * 22/2.54, 76/2.54))
  156.  
  157. theta = 0.4
  158. nu = 0.05
  159. initial = MoveParams(theta, 0, 0, 0)
  160. for i, lmbda in enumerate([math.cos(theta), 1.1*math.cos(theta), 1.4*math.cos(theta)]):
  161. (t, th, ph, (lv, ls, lorth), (l1, l2, l3)) = run(initial, nu, lmbda, steps=steps, step=step)
  162.  
  163. axes = fig.add_subplot(4,4,1 + 4 * i, projection='polar')
  164. axes.set_ybound(0, 2)
  165. axes.set_title('Axis movement with λ = {:.2f}'.format(lmbda))
  166. polar_axis_plot(th, ph, axes=axes, label='λ = {:.2f}'.format(lmbda))
  167.  
  168. # 3 (c)
  169. spinaxes = fig.add_subplot(4,4,2 + 4 * i)
  170. spinaxes.set_title('Angular momentum with λ = {:.2f}'.format(lmbda))
  171. spinaxes.plot(t, lv, label='l_v')
  172. spinaxes.plot(t, ls, label='l_s')
  173. spinaxes.plot(t, lorth, label='l_orth')
  174. spinaxes.legend()
  175.  
  176. # 3 (d)
  177. orthspinaxes = fig.add_subplot(4,4,3 + 4 * i)
  178. orthspinaxes.set_title('Angular momentum in standard base with λ = {:.2f}'.format(lmbda))
  179. orthspinaxes.plot(t, l1, label='l1')
  180. orthspinaxes.plot(t, l2, label='l2')
  181. orthspinaxes.plot(t, l3, label='l3')
  182. orthspinaxes.legend()
  183.  
  184. # 3 (e)
  185. xyaxes = fig.add_subplot(4,4,4 + 4 * i)
  186. xyaxes.set_title('Angular momentum direction with λ = {:.2f}'.format(lmbda))
  187. xyaxes.plot(l1, l2, label='l')
  188. xyaxes.legend()
  189.  
  190. # 3 b(ii)
  191. axes = fig.add_subplot(4,4,13, projection='polar')
  192. axes.set_title('Axis movement (ii)')
  193. lmbda = 1.25
  194. theta = 0.664
  195. nu = 0.05
  196. initial = MoveParams(theta, 0, 0, 0)
  197. (t, th, ph, (lv, ls, lorth), (l1, l2, l3)) = run(initial, nu, lmbda, steps=steps, step=step)
  198. polar_axis_plot(th, ph, axes=axes, label='λ = {:.2f}'.format(lmbda))
  199.  
  200. # 3 (c)
  201. spinaxes = fig.add_subplot(4,4,14)
  202. spinaxes.set_title('Spins with λ = {:.2f}'.format(lmbda))
  203. spinaxes.plot(t, lv, label='l_v')
  204. spinaxes.plot(t, ls, label='l_s')
  205. spinaxes.plot(t, lorth, label='l_orth')
  206. spinaxes.legend()
  207.  
  208. # 3 (d)
  209. orthspinaxes = fig.add_subplot(4,4,15)
  210. orthspinaxes.set_title('Spins in standard base with λ = {:.2f}'.format(lmbda))
  211. orthspinaxes.plot(t, l1, label='l1')
  212. orthspinaxes.plot(t, l2, label='l2')
  213. orthspinaxes.plot(t, l3, label='l3')
  214. orthspinaxes.legend()
  215.  
  216. # 3 (e)
  217. xyaxes = fig.add_subplot(4,4,16)
  218. xyaxes.set_title('Angular momentum direction with λ = {:.2f}'.format(lmbda))
  219. xyaxes.plot(l1, l2, label='l')
  220. xyaxes.legend()
  221.  
  222. fig.legend(())
  223. fig.savefig('plot.pdf')
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement