Show code cell content
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import fsolve
plt.style.use('fivethirtyeight')
from matplotlib import rc
plt.rc('text', usetex=True)
plt.rc('font', family='sans')
Show code cell source
from IPython.lib.display import YouTubeVideo
YouTubeVideo('fcx9jR0O0rY')
Show code cell source
YouTubeVideo('QcENiGydyF0')
Nonlinear pendulum solution#
The first solution to the pendulum was to linearize the equation of motion using a Taylor series expansion. As you look at engineering systems, this is always the best first estimate for a problem. Linear solutions are much easier to solve and usually its good enough to make a plan.
If you have a linear solution, but want the nonlinear solution then you can approach this in two ways
use a different integration or equation e.g. work-energy
numerically integrate the equations of motion
Using work-energy for the pendulum#
The motion of the pendulum must obey the work-energy. Add potential and kinetic energy you have
\(T_0 + V_0 = T(t) + V(t)\)
The kinetic energy is \(T = 1/2mv^2 = 1/2mL^2\dot{\theta}^2\)
the potential energy is \(V = mgh = mgL(1 - \cos\theta)\)
initial kinetic energy (if starting from rest) is \(T_0=0\)
initial potential energy is \(V_0 = mgL(1 - \cos\theta_0)\)
So you can solve for \(\dot{\theta} = f(\theta)\)
\(\dot{\theta} = \pm \sqrt{\frac{2g}{L}(\cos\theta - \cos\theta_0)}\)
Note: This is the same solution derived in the video by using integration by parts. \(\frac{d\dot{\theta}}{dt} = -\frac{g}{L}\sin\theta \rightarrow \frac{d\dot{\theta}}{d\theta}\frac{d\theta}{dt} = -\frac{g}{L}\sin\theta\). The work-energy formula is a shortcut to integrating an equation of motion by parts.
Below, you can see the angular velocity as a function of angle for a L=0.5 m pendulum released at \(\theta_0 = 90^o = \pi/2~rad\).
Show code cell source
L = 0.5
theta = np.linspace(-np.pi/2, np.pi/2, 200)
dtheta = np.sqrt(2*9.81/L*(np.cos(theta) - np.cos(np.pi/2)))
plt.plot(theta*180/np.pi, dtheta*180/np.pi, 'b-')
plt.plot(theta*180/np.pi, -dtheta*180/np.pi, 'b-')
# plt.axis('equal')
plt.xlabel(r'$\theta$ (deg)')
plt.ylabel(r'$\dot{\theta}$ (deg/s)');
Error in callback <function _draw_all_if_interactive at 0x7f75584b7dc0> (for post_execute), with arguments args (),kwargs {}:
---------------------------------------------------------------------------
FileNotFoundError Traceback (most recent call last)
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/texmanager.py:250, in TexManager._run_checked_subprocess(cls, command, tex, cwd)
249 try:
--> 250 report = subprocess.check_output(
251 command, cwd=cwd if cwd is not None else cls._texcache,
252 stderr=subprocess.STDOUT)
253 except FileNotFoundError as exc:
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/subprocess.py:424, in check_output(timeout, *popenargs, **kwargs)
422 kwargs['input'] = empty
--> 424 return run(*popenargs, stdout=PIPE, timeout=timeout, check=True,
425 **kwargs).stdout
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/subprocess.py:505, in run(input, capture_output, timeout, check, *popenargs, **kwargs)
503 kwargs['stderr'] = PIPE
--> 505 with Popen(*popenargs, **kwargs) as process:
506 try:
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/subprocess.py:951, in Popen.__init__(self, args, bufsize, executable, stdin, stdout, stderr, preexec_fn, close_fds, shell, cwd, env, universal_newlines, startupinfo, creationflags, restore_signals, start_new_session, pass_fds, user, group, extra_groups, encoding, errors, text, umask)
948 self.stderr = io.TextIOWrapper(self.stderr,
949 encoding=encoding, errors=errors)
--> 951 self._execute_child(args, executable, preexec_fn, close_fds,
952 pass_fds, cwd, env,
953 startupinfo, creationflags, shell,
954 p2cread, p2cwrite,
955 c2pread, c2pwrite,
956 errread, errwrite,
957 restore_signals,
958 gid, gids, uid, umask,
959 start_new_session)
960 except:
961 # Cleanup if the child failed starting.
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/subprocess.py:1837, in Popen._execute_child(self, args, executable, preexec_fn, close_fds, pass_fds, cwd, env, startupinfo, creationflags, shell, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite, restore_signals, gid, gids, uid, umask, start_new_session)
1836 err_msg = os.strerror(errno_num)
-> 1837 raise child_exception_type(errno_num, err_msg, err_filename)
1838 raise child_exception_type(err_msg)
FileNotFoundError: [Errno 2] No such file or directory: 'latex'
The above exception was the direct cause of the following exception:
RuntimeError Traceback (most recent call last)
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/pyplot.py:197, in _draw_all_if_interactive()
195 def _draw_all_if_interactive() -> None:
196 if matplotlib.is_interactive():
--> 197 draw_all()
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/_pylab_helpers.py:132, in Gcf.draw_all(cls, force)
130 for manager in cls.get_all_fig_managers():
131 if force or manager.canvas.figure.stale:
--> 132 manager.canvas.draw_idle()
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/backend_bases.py:1893, in FigureCanvasBase.draw_idle(self, *args, **kwargs)
1891 if not self._is_idle_drawing:
1892 with self._idle_draw_cntx():
-> 1893 self.draw(*args, **kwargs)
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/backends/backend_agg.py:388, in FigureCanvasAgg.draw(self)
385 # Acquire a lock on the shared font cache.
386 with (self.toolbar._wait_cursor_for_draw_cm() if self.toolbar
387 else nullcontext()):
--> 388 self.figure.draw(self.renderer)
389 # A GUI class may be need to update a window using this draw, so
390 # don't forget to call the superclass.
391 super().draw()
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/artist.py:95, in _finalize_rasterization.<locals>.draw_wrapper(artist, renderer, *args, **kwargs)
93 @wraps(draw)
94 def draw_wrapper(artist, renderer, *args, **kwargs):
---> 95 result = draw(artist, renderer, *args, **kwargs)
96 if renderer._rasterizing:
97 renderer.stop_rasterizing()
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/artist.py:72, in allow_rasterization.<locals>.draw_wrapper(artist, renderer)
69 if artist.get_agg_filter() is not None:
70 renderer.start_filter()
---> 72 return draw(artist, renderer)
73 finally:
74 if artist.get_agg_filter() is not None:
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/figure.py:3154, in Figure.draw(self, renderer)
3151 # ValueError can occur when resizing a window.
3153 self.patch.draw(renderer)
-> 3154 mimage._draw_list_compositing_images(
3155 renderer, self, artists, self.suppressComposite)
3157 for sfig in self.subfigs:
3158 sfig.draw(renderer)
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/image.py:132, in _draw_list_compositing_images(renderer, parent, artists, suppress_composite)
130 if not_composite or not has_images:
131 for a in artists:
--> 132 a.draw(renderer)
133 else:
134 # Composite any adjacent images together
135 image_group = []
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/artist.py:72, in allow_rasterization.<locals>.draw_wrapper(artist, renderer)
69 if artist.get_agg_filter() is not None:
70 renderer.start_filter()
---> 72 return draw(artist, renderer)
73 finally:
74 if artist.get_agg_filter() is not None:
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/axes/_base.py:3070, in _AxesBase.draw(self, renderer)
3067 if artists_rasterized:
3068 _draw_rasterized(self.figure, artists_rasterized, renderer)
-> 3070 mimage._draw_list_compositing_images(
3071 renderer, self, artists, self.figure.suppressComposite)
3073 renderer.close_group('axes')
3074 self.stale = False
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/image.py:132, in _draw_list_compositing_images(renderer, parent, artists, suppress_composite)
130 if not_composite or not has_images:
131 for a in artists:
--> 132 a.draw(renderer)
133 else:
134 # Composite any adjacent images together
135 image_group = []
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/artist.py:72, in allow_rasterization.<locals>.draw_wrapper(artist, renderer)
69 if artist.get_agg_filter() is not None:
70 renderer.start_filter()
---> 72 return draw(artist, renderer)
73 finally:
74 if artist.get_agg_filter() is not None:
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/axis.py:1388, in Axis.draw(self, renderer, *args, **kwargs)
1385 renderer.open_group(__name__, gid=self.get_gid())
1387 ticks_to_draw = self._update_ticks()
-> 1388 tlb1, tlb2 = self._get_ticklabel_bboxes(ticks_to_draw, renderer)
1390 for tick in ticks_to_draw:
1391 tick.draw(renderer)
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/axis.py:1315, in Axis._get_ticklabel_bboxes(self, ticks, renderer)
1313 if renderer is None:
1314 renderer = self.figure._get_renderer()
-> 1315 return ([tick.label1.get_window_extent(renderer)
1316 for tick in ticks if tick.label1.get_visible()],
1317 [tick.label2.get_window_extent(renderer)
1318 for tick in ticks if tick.label2.get_visible()])
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/axis.py:1315, in <listcomp>(.0)
1313 if renderer is None:
1314 renderer = self.figure._get_renderer()
-> 1315 return ([tick.label1.get_window_extent(renderer)
1316 for tick in ticks if tick.label1.get_visible()],
1317 [tick.label2.get_window_extent(renderer)
1318 for tick in ticks if tick.label2.get_visible()])
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/text.py:956, in Text.get_window_extent(self, renderer, dpi)
951 raise RuntimeError(
952 "Cannot get window extent of text w/o renderer. You likely "
953 "want to call 'figure.draw_without_rendering()' first.")
955 with cbook._setattr_cm(self.figure, dpi=dpi):
--> 956 bbox, info, descent = self._get_layout(self._renderer)
957 x, y = self.get_unitless_position()
958 x, y = self.get_transform().transform((x, y))
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/text.py:373, in Text._get_layout(self, renderer)
370 ys = []
372 # Full vertical extent of font, including ascenders and descenders:
--> 373 _, lp_h, lp_d = _get_text_metrics_with_cache(
374 renderer, "lp", self._fontproperties,
375 ismath="TeX" if self.get_usetex() else False, dpi=self.figure.dpi)
376 min_dy = (lp_h - lp_d) * self._linespacing
378 for i, line in enumerate(lines):
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/text.py:69, in _get_text_metrics_with_cache(renderer, text, fontprop, ismath, dpi)
66 """Call ``renderer.get_text_width_height_descent``, caching the results."""
67 # Cached based on a copy of fontprop so that later in-place mutations of
68 # the passed-in argument do not mess up the cache.
---> 69 return _get_text_metrics_with_cache_impl(
70 weakref.ref(renderer), text, fontprop.copy(), ismath, dpi)
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/text.py:77, in _get_text_metrics_with_cache_impl(renderer_ref, text, fontprop, ismath, dpi)
73 @functools.lru_cache(4096)
74 def _get_text_metrics_with_cache_impl(
75 renderer_ref, text, fontprop, ismath, dpi):
76 # dpi is unused, but participates in cache invalidation (via the renderer).
---> 77 return renderer_ref().get_text_width_height_descent(text, fontprop, ismath)
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/backends/backend_agg.py:213, in RendererAgg.get_text_width_height_descent(self, s, prop, ismath)
211 _api.check_in_list(["TeX", True, False], ismath=ismath)
212 if ismath == "TeX":
--> 213 return super().get_text_width_height_descent(s, prop, ismath)
215 if ismath:
216 ox, oy, width, height, descent, font_image = \
217 self.mathtext_parser.parse(s, self.dpi, prop)
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/backend_bases.py:652, in RendererBase.get_text_width_height_descent(self, s, prop, ismath)
648 fontsize = prop.get_size_in_points()
650 if ismath == 'TeX':
651 # todo: handle properties
--> 652 return self.get_texmanager().get_text_width_height_descent(
653 s, fontsize, renderer=self)
655 dpi = self.points_to_pixels(72)
656 if ismath:
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/texmanager.py:363, in TexManager.get_text_width_height_descent(cls, tex, fontsize, renderer)
361 if tex.strip() == '':
362 return 0, 0, 0
--> 363 dvifile = cls.make_dvi(tex, fontsize)
364 dpi_fraction = renderer.points_to_pixels(1.) if renderer else 1
365 with dviread.Dvi(dvifile, 72 * dpi_fraction) as dvi:
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/texmanager.py:295, in TexManager.make_dvi(cls, tex, fontsize)
293 with TemporaryDirectory(dir=cwd) as tmpdir:
294 tmppath = Path(tmpdir)
--> 295 cls._run_checked_subprocess(
296 ["latex", "-interaction=nonstopmode", "--halt-on-error",
297 f"--output-directory={tmppath.name}",
298 f"{texfile.name}"], tex, cwd=cwd)
299 (tmppath / Path(dvifile).name).replace(dvifile)
300 return dvifile
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/texmanager.py:254, in TexManager._run_checked_subprocess(cls, command, tex, cwd)
250 report = subprocess.check_output(
251 command, cwd=cwd if cwd is not None else cls._texcache,
252 stderr=subprocess.STDOUT)
253 except FileNotFoundError as exc:
--> 254 raise RuntimeError(
255 f'Failed to process string with tex because {command[0]} '
256 'could not be found') from exc
257 except subprocess.CalledProcessError as exc:
258 raise RuntimeError(
259 '{prog} was not able to process the following string:\n'
260 '{tex!r}\n\n'
(...)
267 exc=exc.output.decode('utf-8', 'backslashreplace'))
268 ) from None
RuntimeError: Failed to process string with tex because latex could not be found
---------------------------------------------------------------------------
FileNotFoundError Traceback (most recent call last)
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/texmanager.py:250, in TexManager._run_checked_subprocess(cls, command, tex, cwd)
249 try:
--> 250 report = subprocess.check_output(
251 command, cwd=cwd if cwd is not None else cls._texcache,
252 stderr=subprocess.STDOUT)
253 except FileNotFoundError as exc:
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/subprocess.py:424, in check_output(timeout, *popenargs, **kwargs)
422 kwargs['input'] = empty
--> 424 return run(*popenargs, stdout=PIPE, timeout=timeout, check=True,
425 **kwargs).stdout
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/subprocess.py:505, in run(input, capture_output, timeout, check, *popenargs, **kwargs)
503 kwargs['stderr'] = PIPE
--> 505 with Popen(*popenargs, **kwargs) as process:
506 try:
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/subprocess.py:951, in Popen.__init__(self, args, bufsize, executable, stdin, stdout, stderr, preexec_fn, close_fds, shell, cwd, env, universal_newlines, startupinfo, creationflags, restore_signals, start_new_session, pass_fds, user, group, extra_groups, encoding, errors, text, umask)
948 self.stderr = io.TextIOWrapper(self.stderr,
949 encoding=encoding, errors=errors)
--> 951 self._execute_child(args, executable, preexec_fn, close_fds,
952 pass_fds, cwd, env,
953 startupinfo, creationflags, shell,
954 p2cread, p2cwrite,
955 c2pread, c2pwrite,
956 errread, errwrite,
957 restore_signals,
958 gid, gids, uid, umask,
959 start_new_session)
960 except:
961 # Cleanup if the child failed starting.
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/subprocess.py:1837, in Popen._execute_child(self, args, executable, preexec_fn, close_fds, pass_fds, cwd, env, startupinfo, creationflags, shell, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite, restore_signals, gid, gids, uid, umask, start_new_session)
1836 err_msg = os.strerror(errno_num)
-> 1837 raise child_exception_type(errno_num, err_msg, err_filename)
1838 raise child_exception_type(err_msg)
FileNotFoundError: [Errno 2] No such file or directory: 'latex'
The above exception was the direct cause of the following exception:
RuntimeError Traceback (most recent call last)
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/IPython/core/formatters.py:340, in BaseFormatter.__call__(self, obj)
338 pass
339 else:
--> 340 return printer(obj)
341 # Finally look for special method names
342 method = get_real_method(obj, self.print_method)
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/IPython/core/pylabtools.py:152, in print_figure(fig, fmt, bbox_inches, base64, **kwargs)
149 from matplotlib.backend_bases import FigureCanvasBase
150 FigureCanvasBase(fig)
--> 152 fig.canvas.print_figure(bytes_io, **kw)
153 data = bytes_io.getvalue()
154 if fmt == 'svg':
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/backend_bases.py:2164, in FigureCanvasBase.print_figure(self, filename, dpi, facecolor, edgecolor, orientation, format, bbox_inches, pad_inches, bbox_extra_artists, backend, **kwargs)
2161 # we do this instead of `self.figure.draw_without_rendering`
2162 # so that we can inject the orientation
2163 with getattr(renderer, "_draw_disabled", nullcontext)():
-> 2164 self.figure.draw(renderer)
2165 if bbox_inches:
2166 if bbox_inches == "tight":
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/artist.py:95, in _finalize_rasterization.<locals>.draw_wrapper(artist, renderer, *args, **kwargs)
93 @wraps(draw)
94 def draw_wrapper(artist, renderer, *args, **kwargs):
---> 95 result = draw(artist, renderer, *args, **kwargs)
96 if renderer._rasterizing:
97 renderer.stop_rasterizing()
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/artist.py:72, in allow_rasterization.<locals>.draw_wrapper(artist, renderer)
69 if artist.get_agg_filter() is not None:
70 renderer.start_filter()
---> 72 return draw(artist, renderer)
73 finally:
74 if artist.get_agg_filter() is not None:
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/figure.py:3154, in Figure.draw(self, renderer)
3151 # ValueError can occur when resizing a window.
3153 self.patch.draw(renderer)
-> 3154 mimage._draw_list_compositing_images(
3155 renderer, self, artists, self.suppressComposite)
3157 for sfig in self.subfigs:
3158 sfig.draw(renderer)
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/image.py:132, in _draw_list_compositing_images(renderer, parent, artists, suppress_composite)
130 if not_composite or not has_images:
131 for a in artists:
--> 132 a.draw(renderer)
133 else:
134 # Composite any adjacent images together
135 image_group = []
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/artist.py:72, in allow_rasterization.<locals>.draw_wrapper(artist, renderer)
69 if artist.get_agg_filter() is not None:
70 renderer.start_filter()
---> 72 return draw(artist, renderer)
73 finally:
74 if artist.get_agg_filter() is not None:
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/axes/_base.py:3070, in _AxesBase.draw(self, renderer)
3067 if artists_rasterized:
3068 _draw_rasterized(self.figure, artists_rasterized, renderer)
-> 3070 mimage._draw_list_compositing_images(
3071 renderer, self, artists, self.figure.suppressComposite)
3073 renderer.close_group('axes')
3074 self.stale = False
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/image.py:132, in _draw_list_compositing_images(renderer, parent, artists, suppress_composite)
130 if not_composite or not has_images:
131 for a in artists:
--> 132 a.draw(renderer)
133 else:
134 # Composite any adjacent images together
135 image_group = []
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/artist.py:72, in allow_rasterization.<locals>.draw_wrapper(artist, renderer)
69 if artist.get_agg_filter() is not None:
70 renderer.start_filter()
---> 72 return draw(artist, renderer)
73 finally:
74 if artist.get_agg_filter() is not None:
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/axis.py:1388, in Axis.draw(self, renderer, *args, **kwargs)
1385 renderer.open_group(__name__, gid=self.get_gid())
1387 ticks_to_draw = self._update_ticks()
-> 1388 tlb1, tlb2 = self._get_ticklabel_bboxes(ticks_to_draw, renderer)
1390 for tick in ticks_to_draw:
1391 tick.draw(renderer)
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/axis.py:1315, in Axis._get_ticklabel_bboxes(self, ticks, renderer)
1313 if renderer is None:
1314 renderer = self.figure._get_renderer()
-> 1315 return ([tick.label1.get_window_extent(renderer)
1316 for tick in ticks if tick.label1.get_visible()],
1317 [tick.label2.get_window_extent(renderer)
1318 for tick in ticks if tick.label2.get_visible()])
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/axis.py:1315, in <listcomp>(.0)
1313 if renderer is None:
1314 renderer = self.figure._get_renderer()
-> 1315 return ([tick.label1.get_window_extent(renderer)
1316 for tick in ticks if tick.label1.get_visible()],
1317 [tick.label2.get_window_extent(renderer)
1318 for tick in ticks if tick.label2.get_visible()])
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/text.py:956, in Text.get_window_extent(self, renderer, dpi)
951 raise RuntimeError(
952 "Cannot get window extent of text w/o renderer. You likely "
953 "want to call 'figure.draw_without_rendering()' first.")
955 with cbook._setattr_cm(self.figure, dpi=dpi):
--> 956 bbox, info, descent = self._get_layout(self._renderer)
957 x, y = self.get_unitless_position()
958 x, y = self.get_transform().transform((x, y))
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/text.py:373, in Text._get_layout(self, renderer)
370 ys = []
372 # Full vertical extent of font, including ascenders and descenders:
--> 373 _, lp_h, lp_d = _get_text_metrics_with_cache(
374 renderer, "lp", self._fontproperties,
375 ismath="TeX" if self.get_usetex() else False, dpi=self.figure.dpi)
376 min_dy = (lp_h - lp_d) * self._linespacing
378 for i, line in enumerate(lines):
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/text.py:69, in _get_text_metrics_with_cache(renderer, text, fontprop, ismath, dpi)
66 """Call ``renderer.get_text_width_height_descent``, caching the results."""
67 # Cached based on a copy of fontprop so that later in-place mutations of
68 # the passed-in argument do not mess up the cache.
---> 69 return _get_text_metrics_with_cache_impl(
70 weakref.ref(renderer), text, fontprop.copy(), ismath, dpi)
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/text.py:77, in _get_text_metrics_with_cache_impl(renderer_ref, text, fontprop, ismath, dpi)
73 @functools.lru_cache(4096)
74 def _get_text_metrics_with_cache_impl(
75 renderer_ref, text, fontprop, ismath, dpi):
76 # dpi is unused, but participates in cache invalidation (via the renderer).
---> 77 return renderer_ref().get_text_width_height_descent(text, fontprop, ismath)
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/backends/backend_agg.py:213, in RendererAgg.get_text_width_height_descent(self, s, prop, ismath)
211 _api.check_in_list(["TeX", True, False], ismath=ismath)
212 if ismath == "TeX":
--> 213 return super().get_text_width_height_descent(s, prop, ismath)
215 if ismath:
216 ox, oy, width, height, descent, font_image = \
217 self.mathtext_parser.parse(s, self.dpi, prop)
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/backend_bases.py:652, in RendererBase.get_text_width_height_descent(self, s, prop, ismath)
648 fontsize = prop.get_size_in_points()
650 if ismath == 'TeX':
651 # todo: handle properties
--> 652 return self.get_texmanager().get_text_width_height_descent(
653 s, fontsize, renderer=self)
655 dpi = self.points_to_pixels(72)
656 if ismath:
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/texmanager.py:363, in TexManager.get_text_width_height_descent(cls, tex, fontsize, renderer)
361 if tex.strip() == '':
362 return 0, 0, 0
--> 363 dvifile = cls.make_dvi(tex, fontsize)
364 dpi_fraction = renderer.points_to_pixels(1.) if renderer else 1
365 with dviread.Dvi(dvifile, 72 * dpi_fraction) as dvi:
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/texmanager.py:295, in TexManager.make_dvi(cls, tex, fontsize)
293 with TemporaryDirectory(dir=cwd) as tmpdir:
294 tmppath = Path(tmpdir)
--> 295 cls._run_checked_subprocess(
296 ["latex", "-interaction=nonstopmode", "--halt-on-error",
297 f"--output-directory={tmppath.name}",
298 f"{texfile.name}"], tex, cwd=cwd)
299 (tmppath / Path(dvifile).name).replace(dvifile)
300 return dvifile
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/texmanager.py:254, in TexManager._run_checked_subprocess(cls, command, tex, cwd)
250 report = subprocess.check_output(
251 command, cwd=cwd if cwd is not None else cls._texcache,
252 stderr=subprocess.STDOUT)
253 except FileNotFoundError as exc:
--> 254 raise RuntimeError(
255 f'Failed to process string with tex because {command[0]} '
256 'could not be found') from exc
257 except subprocess.CalledProcessError as exc:
258 raise RuntimeError(
259 '{prog} was not able to process the following string:\n'
260 '{tex!r}\n\n'
(...)
267 exc=exc.output.decode('utf-8', 'backslashreplace'))
268 ) from None
RuntimeError: Failed to process string with tex because latex could not be found
<Figure size 640x480 with 1 Axes>
Phase plot of the simple pendulum#
This type of plot is called a phase plot. You can use it to compare different initial conditions and stability. Extending this phase plot to account for initial angles, \(\theta_0 = (18^o...180^o)\), you see the concentric circles move outward (from light to dark) with higher speeds.
Show code cell source
opacity = 0
for theta0 in np.linspace(np.pi/10, np.pi, 6):
theta = np.linspace(-theta0, theta0, 100)
dtheta = np.sqrt(2*9.81/L*(np.cos(theta) - np.cos(theta0)))
plt.plot(theta*180/np.pi, dtheta*180/np.pi, '-', color = (0, 0, 1, 0.2+opacity))
plt.plot(theta*180/np.pi, -dtheta*180/np.pi, '-', color = (0, 0, 1, 0.2+opacity))
opacity += 0.1
plt.title('Pendulum speed vs angle for\n'+
r'$\theta_0=(18^o...180^o)$')
plt.xlabel(r'$\theta$ (deg)')
plt.ylabel(r'$\dot{\theta}$ (deg/s)');
Error in callback <function _draw_all_if_interactive at 0x7f75584b7dc0> (for post_execute), with arguments args (),kwargs {}:
---------------------------------------------------------------------------
FileNotFoundError Traceback (most recent call last)
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/texmanager.py:250, in TexManager._run_checked_subprocess(cls, command, tex, cwd)
249 try:
--> 250 report = subprocess.check_output(
251 command, cwd=cwd if cwd is not None else cls._texcache,
252 stderr=subprocess.STDOUT)
253 except FileNotFoundError as exc:
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/subprocess.py:424, in check_output(timeout, *popenargs, **kwargs)
422 kwargs['input'] = empty
--> 424 return run(*popenargs, stdout=PIPE, timeout=timeout, check=True,
425 **kwargs).stdout
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/subprocess.py:505, in run(input, capture_output, timeout, check, *popenargs, **kwargs)
503 kwargs['stderr'] = PIPE
--> 505 with Popen(*popenargs, **kwargs) as process:
506 try:
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/subprocess.py:951, in Popen.__init__(self, args, bufsize, executable, stdin, stdout, stderr, preexec_fn, close_fds, shell, cwd, env, universal_newlines, startupinfo, creationflags, restore_signals, start_new_session, pass_fds, user, group, extra_groups, encoding, errors, text, umask)
948 self.stderr = io.TextIOWrapper(self.stderr,
949 encoding=encoding, errors=errors)
--> 951 self._execute_child(args, executable, preexec_fn, close_fds,
952 pass_fds, cwd, env,
953 startupinfo, creationflags, shell,
954 p2cread, p2cwrite,
955 c2pread, c2pwrite,
956 errread, errwrite,
957 restore_signals,
958 gid, gids, uid, umask,
959 start_new_session)
960 except:
961 # Cleanup if the child failed starting.
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/subprocess.py:1837, in Popen._execute_child(self, args, executable, preexec_fn, close_fds, pass_fds, cwd, env, startupinfo, creationflags, shell, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite, restore_signals, gid, gids, uid, umask, start_new_session)
1836 err_msg = os.strerror(errno_num)
-> 1837 raise child_exception_type(errno_num, err_msg, err_filename)
1838 raise child_exception_type(err_msg)
FileNotFoundError: [Errno 2] No such file or directory: 'latex'
The above exception was the direct cause of the following exception:
RuntimeError Traceback (most recent call last)
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/pyplot.py:197, in _draw_all_if_interactive()
195 def _draw_all_if_interactive() -> None:
196 if matplotlib.is_interactive():
--> 197 draw_all()
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/_pylab_helpers.py:132, in Gcf.draw_all(cls, force)
130 for manager in cls.get_all_fig_managers():
131 if force or manager.canvas.figure.stale:
--> 132 manager.canvas.draw_idle()
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/backend_bases.py:1893, in FigureCanvasBase.draw_idle(self, *args, **kwargs)
1891 if not self._is_idle_drawing:
1892 with self._idle_draw_cntx():
-> 1893 self.draw(*args, **kwargs)
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/backends/backend_agg.py:388, in FigureCanvasAgg.draw(self)
385 # Acquire a lock on the shared font cache.
386 with (self.toolbar._wait_cursor_for_draw_cm() if self.toolbar
387 else nullcontext()):
--> 388 self.figure.draw(self.renderer)
389 # A GUI class may be need to update a window using this draw, so
390 # don't forget to call the superclass.
391 super().draw()
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/artist.py:95, in _finalize_rasterization.<locals>.draw_wrapper(artist, renderer, *args, **kwargs)
93 @wraps(draw)
94 def draw_wrapper(artist, renderer, *args, **kwargs):
---> 95 result = draw(artist, renderer, *args, **kwargs)
96 if renderer._rasterizing:
97 renderer.stop_rasterizing()
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/artist.py:72, in allow_rasterization.<locals>.draw_wrapper(artist, renderer)
69 if artist.get_agg_filter() is not None:
70 renderer.start_filter()
---> 72 return draw(artist, renderer)
73 finally:
74 if artist.get_agg_filter() is not None:
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/figure.py:3154, in Figure.draw(self, renderer)
3151 # ValueError can occur when resizing a window.
3153 self.patch.draw(renderer)
-> 3154 mimage._draw_list_compositing_images(
3155 renderer, self, artists, self.suppressComposite)
3157 for sfig in self.subfigs:
3158 sfig.draw(renderer)
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/image.py:132, in _draw_list_compositing_images(renderer, parent, artists, suppress_composite)
130 if not_composite or not has_images:
131 for a in artists:
--> 132 a.draw(renderer)
133 else:
134 # Composite any adjacent images together
135 image_group = []
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/artist.py:72, in allow_rasterization.<locals>.draw_wrapper(artist, renderer)
69 if artist.get_agg_filter() is not None:
70 renderer.start_filter()
---> 72 return draw(artist, renderer)
73 finally:
74 if artist.get_agg_filter() is not None:
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/axes/_base.py:3034, in _AxesBase.draw(self, renderer)
3031 for spine in self.spines.values():
3032 artists.remove(spine)
-> 3034 self._update_title_position(renderer)
3036 if not self.axison:
3037 for _axis in self._axis_map.values():
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/axes/_base.py:2978, in _AxesBase._update_title_position(self, renderer)
2976 top = max(top, bb.ymax)
2977 if title.get_text():
-> 2978 ax.yaxis.get_tightbbox(renderer) # update offsetText
2979 if ax.yaxis.offsetText.get_text():
2980 bb = ax.yaxis.offsetText.get_tightbbox(renderer)
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/axis.py:1336, in Axis.get_tightbbox(self, renderer, for_layout_only)
1333 renderer = self.figure._get_renderer()
1334 ticks_to_draw = self._update_ticks()
-> 1336 self._update_label_position(renderer)
1338 # go back to just this axis's tick labels
1339 tlb1, tlb2 = self._get_ticklabel_bboxes(ticks_to_draw, renderer)
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/axis.py:2609, in YAxis._update_label_position(self, renderer)
2605 return
2607 # get bounding boxes for this axis and any siblings
2608 # that have been set by `fig.align_ylabels()`
-> 2609 bboxes, bboxes2 = self._get_tick_boxes_siblings(renderer=renderer)
2610 x, y = self.label.get_position()
2611 if self.label_position == 'left':
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/axis.py:2161, in Axis._get_tick_boxes_siblings(self, renderer)
2159 axis = ax._axis_map[name]
2160 ticks_to_draw = axis._update_ticks()
-> 2161 tlb, tlb2 = axis._get_ticklabel_bboxes(ticks_to_draw, renderer)
2162 bboxes.extend(tlb)
2163 bboxes2.extend(tlb2)
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/axis.py:1315, in Axis._get_ticklabel_bboxes(self, ticks, renderer)
1313 if renderer is None:
1314 renderer = self.figure._get_renderer()
-> 1315 return ([tick.label1.get_window_extent(renderer)
1316 for tick in ticks if tick.label1.get_visible()],
1317 [tick.label2.get_window_extent(renderer)
1318 for tick in ticks if tick.label2.get_visible()])
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/axis.py:1315, in <listcomp>(.0)
1313 if renderer is None:
1314 renderer = self.figure._get_renderer()
-> 1315 return ([tick.label1.get_window_extent(renderer)
1316 for tick in ticks if tick.label1.get_visible()],
1317 [tick.label2.get_window_extent(renderer)
1318 for tick in ticks if tick.label2.get_visible()])
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/text.py:956, in Text.get_window_extent(self, renderer, dpi)
951 raise RuntimeError(
952 "Cannot get window extent of text w/o renderer. You likely "
953 "want to call 'figure.draw_without_rendering()' first.")
955 with cbook._setattr_cm(self.figure, dpi=dpi):
--> 956 bbox, info, descent = self._get_layout(self._renderer)
957 x, y = self.get_unitless_position()
958 x, y = self.get_transform().transform((x, y))
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/text.py:373, in Text._get_layout(self, renderer)
370 ys = []
372 # Full vertical extent of font, including ascenders and descenders:
--> 373 _, lp_h, lp_d = _get_text_metrics_with_cache(
374 renderer, "lp", self._fontproperties,
375 ismath="TeX" if self.get_usetex() else False, dpi=self.figure.dpi)
376 min_dy = (lp_h - lp_d) * self._linespacing
378 for i, line in enumerate(lines):
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/text.py:69, in _get_text_metrics_with_cache(renderer, text, fontprop, ismath, dpi)
66 """Call ``renderer.get_text_width_height_descent``, caching the results."""
67 # Cached based on a copy of fontprop so that later in-place mutations of
68 # the passed-in argument do not mess up the cache.
---> 69 return _get_text_metrics_with_cache_impl(
70 weakref.ref(renderer), text, fontprop.copy(), ismath, dpi)
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/text.py:77, in _get_text_metrics_with_cache_impl(renderer_ref, text, fontprop, ismath, dpi)
73 @functools.lru_cache(4096)
74 def _get_text_metrics_with_cache_impl(
75 renderer_ref, text, fontprop, ismath, dpi):
76 # dpi is unused, but participates in cache invalidation (via the renderer).
---> 77 return renderer_ref().get_text_width_height_descent(text, fontprop, ismath)
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/backends/backend_agg.py:213, in RendererAgg.get_text_width_height_descent(self, s, prop, ismath)
211 _api.check_in_list(["TeX", True, False], ismath=ismath)
212 if ismath == "TeX":
--> 213 return super().get_text_width_height_descent(s, prop, ismath)
215 if ismath:
216 ox, oy, width, height, descent, font_image = \
217 self.mathtext_parser.parse(s, self.dpi, prop)
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/backend_bases.py:652, in RendererBase.get_text_width_height_descent(self, s, prop, ismath)
648 fontsize = prop.get_size_in_points()
650 if ismath == 'TeX':
651 # todo: handle properties
--> 652 return self.get_texmanager().get_text_width_height_descent(
653 s, fontsize, renderer=self)
655 dpi = self.points_to_pixels(72)
656 if ismath:
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/texmanager.py:363, in TexManager.get_text_width_height_descent(cls, tex, fontsize, renderer)
361 if tex.strip() == '':
362 return 0, 0, 0
--> 363 dvifile = cls.make_dvi(tex, fontsize)
364 dpi_fraction = renderer.points_to_pixels(1.) if renderer else 1
365 with dviread.Dvi(dvifile, 72 * dpi_fraction) as dvi:
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/texmanager.py:295, in TexManager.make_dvi(cls, tex, fontsize)
293 with TemporaryDirectory(dir=cwd) as tmpdir:
294 tmppath = Path(tmpdir)
--> 295 cls._run_checked_subprocess(
296 ["latex", "-interaction=nonstopmode", "--halt-on-error",
297 f"--output-directory={tmppath.name}",
298 f"{texfile.name}"], tex, cwd=cwd)
299 (tmppath / Path(dvifile).name).replace(dvifile)
300 return dvifile
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/texmanager.py:254, in TexManager._run_checked_subprocess(cls, command, tex, cwd)
250 report = subprocess.check_output(
251 command, cwd=cwd if cwd is not None else cls._texcache,
252 stderr=subprocess.STDOUT)
253 except FileNotFoundError as exc:
--> 254 raise RuntimeError(
255 f'Failed to process string with tex because {command[0]} '
256 'could not be found') from exc
257 except subprocess.CalledProcessError as exc:
258 raise RuntimeError(
259 '{prog} was not able to process the following string:\n'
260 '{tex!r}\n\n'
(...)
267 exc=exc.output.decode('utf-8', 'backslashreplace'))
268 ) from None
RuntimeError: Failed to process string with tex because latex could not be found
---------------------------------------------------------------------------
FileNotFoundError Traceback (most recent call last)
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/texmanager.py:250, in TexManager._run_checked_subprocess(cls, command, tex, cwd)
249 try:
--> 250 report = subprocess.check_output(
251 command, cwd=cwd if cwd is not None else cls._texcache,
252 stderr=subprocess.STDOUT)
253 except FileNotFoundError as exc:
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/subprocess.py:424, in check_output(timeout, *popenargs, **kwargs)
422 kwargs['input'] = empty
--> 424 return run(*popenargs, stdout=PIPE, timeout=timeout, check=True,
425 **kwargs).stdout
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/subprocess.py:505, in run(input, capture_output, timeout, check, *popenargs, **kwargs)
503 kwargs['stderr'] = PIPE
--> 505 with Popen(*popenargs, **kwargs) as process:
506 try:
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/subprocess.py:951, in Popen.__init__(self, args, bufsize, executable, stdin, stdout, stderr, preexec_fn, close_fds, shell, cwd, env, universal_newlines, startupinfo, creationflags, restore_signals, start_new_session, pass_fds, user, group, extra_groups, encoding, errors, text, umask)
948 self.stderr = io.TextIOWrapper(self.stderr,
949 encoding=encoding, errors=errors)
--> 951 self._execute_child(args, executable, preexec_fn, close_fds,
952 pass_fds, cwd, env,
953 startupinfo, creationflags, shell,
954 p2cread, p2cwrite,
955 c2pread, c2pwrite,
956 errread, errwrite,
957 restore_signals,
958 gid, gids, uid, umask,
959 start_new_session)
960 except:
961 # Cleanup if the child failed starting.
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/subprocess.py:1837, in Popen._execute_child(self, args, executable, preexec_fn, close_fds, pass_fds, cwd, env, startupinfo, creationflags, shell, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite, restore_signals, gid, gids, uid, umask, start_new_session)
1836 err_msg = os.strerror(errno_num)
-> 1837 raise child_exception_type(errno_num, err_msg, err_filename)
1838 raise child_exception_type(err_msg)
FileNotFoundError: [Errno 2] No such file or directory: 'latex'
The above exception was the direct cause of the following exception:
RuntimeError Traceback (most recent call last)
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/IPython/core/formatters.py:340, in BaseFormatter.__call__(self, obj)
338 pass
339 else:
--> 340 return printer(obj)
341 # Finally look for special method names
342 method = get_real_method(obj, self.print_method)
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/IPython/core/pylabtools.py:152, in print_figure(fig, fmt, bbox_inches, base64, **kwargs)
149 from matplotlib.backend_bases import FigureCanvasBase
150 FigureCanvasBase(fig)
--> 152 fig.canvas.print_figure(bytes_io, **kw)
153 data = bytes_io.getvalue()
154 if fmt == 'svg':
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/backend_bases.py:2164, in FigureCanvasBase.print_figure(self, filename, dpi, facecolor, edgecolor, orientation, format, bbox_inches, pad_inches, bbox_extra_artists, backend, **kwargs)
2161 # we do this instead of `self.figure.draw_without_rendering`
2162 # so that we can inject the orientation
2163 with getattr(renderer, "_draw_disabled", nullcontext)():
-> 2164 self.figure.draw(renderer)
2165 if bbox_inches:
2166 if bbox_inches == "tight":
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/artist.py:95, in _finalize_rasterization.<locals>.draw_wrapper(artist, renderer, *args, **kwargs)
93 @wraps(draw)
94 def draw_wrapper(artist, renderer, *args, **kwargs):
---> 95 result = draw(artist, renderer, *args, **kwargs)
96 if renderer._rasterizing:
97 renderer.stop_rasterizing()
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/artist.py:72, in allow_rasterization.<locals>.draw_wrapper(artist, renderer)
69 if artist.get_agg_filter() is not None:
70 renderer.start_filter()
---> 72 return draw(artist, renderer)
73 finally:
74 if artist.get_agg_filter() is not None:
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/figure.py:3154, in Figure.draw(self, renderer)
3151 # ValueError can occur when resizing a window.
3153 self.patch.draw(renderer)
-> 3154 mimage._draw_list_compositing_images(
3155 renderer, self, artists, self.suppressComposite)
3157 for sfig in self.subfigs:
3158 sfig.draw(renderer)
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/image.py:132, in _draw_list_compositing_images(renderer, parent, artists, suppress_composite)
130 if not_composite or not has_images:
131 for a in artists:
--> 132 a.draw(renderer)
133 else:
134 # Composite any adjacent images together
135 image_group = []
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/artist.py:72, in allow_rasterization.<locals>.draw_wrapper(artist, renderer)
69 if artist.get_agg_filter() is not None:
70 renderer.start_filter()
---> 72 return draw(artist, renderer)
73 finally:
74 if artist.get_agg_filter() is not None:
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/axes/_base.py:3034, in _AxesBase.draw(self, renderer)
3031 for spine in self.spines.values():
3032 artists.remove(spine)
-> 3034 self._update_title_position(renderer)
3036 if not self.axison:
3037 for _axis in self._axis_map.values():
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/axes/_base.py:2978, in _AxesBase._update_title_position(self, renderer)
2976 top = max(top, bb.ymax)
2977 if title.get_text():
-> 2978 ax.yaxis.get_tightbbox(renderer) # update offsetText
2979 if ax.yaxis.offsetText.get_text():
2980 bb = ax.yaxis.offsetText.get_tightbbox(renderer)
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/axis.py:1336, in Axis.get_tightbbox(self, renderer, for_layout_only)
1333 renderer = self.figure._get_renderer()
1334 ticks_to_draw = self._update_ticks()
-> 1336 self._update_label_position(renderer)
1338 # go back to just this axis's tick labels
1339 tlb1, tlb2 = self._get_ticklabel_bboxes(ticks_to_draw, renderer)
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/axis.py:2609, in YAxis._update_label_position(self, renderer)
2605 return
2607 # get bounding boxes for this axis and any siblings
2608 # that have been set by `fig.align_ylabels()`
-> 2609 bboxes, bboxes2 = self._get_tick_boxes_siblings(renderer=renderer)
2610 x, y = self.label.get_position()
2611 if self.label_position == 'left':
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/axis.py:2161, in Axis._get_tick_boxes_siblings(self, renderer)
2159 axis = ax._axis_map[name]
2160 ticks_to_draw = axis._update_ticks()
-> 2161 tlb, tlb2 = axis._get_ticklabel_bboxes(ticks_to_draw, renderer)
2162 bboxes.extend(tlb)
2163 bboxes2.extend(tlb2)
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/axis.py:1315, in Axis._get_ticklabel_bboxes(self, ticks, renderer)
1313 if renderer is None:
1314 renderer = self.figure._get_renderer()
-> 1315 return ([tick.label1.get_window_extent(renderer)
1316 for tick in ticks if tick.label1.get_visible()],
1317 [tick.label2.get_window_extent(renderer)
1318 for tick in ticks if tick.label2.get_visible()])
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/axis.py:1315, in <listcomp>(.0)
1313 if renderer is None:
1314 renderer = self.figure._get_renderer()
-> 1315 return ([tick.label1.get_window_extent(renderer)
1316 for tick in ticks if tick.label1.get_visible()],
1317 [tick.label2.get_window_extent(renderer)
1318 for tick in ticks if tick.label2.get_visible()])
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/text.py:956, in Text.get_window_extent(self, renderer, dpi)
951 raise RuntimeError(
952 "Cannot get window extent of text w/o renderer. You likely "
953 "want to call 'figure.draw_without_rendering()' first.")
955 with cbook._setattr_cm(self.figure, dpi=dpi):
--> 956 bbox, info, descent = self._get_layout(self._renderer)
957 x, y = self.get_unitless_position()
958 x, y = self.get_transform().transform((x, y))
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/text.py:373, in Text._get_layout(self, renderer)
370 ys = []
372 # Full vertical extent of font, including ascenders and descenders:
--> 373 _, lp_h, lp_d = _get_text_metrics_with_cache(
374 renderer, "lp", self._fontproperties,
375 ismath="TeX" if self.get_usetex() else False, dpi=self.figure.dpi)
376 min_dy = (lp_h - lp_d) * self._linespacing
378 for i, line in enumerate(lines):
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/text.py:69, in _get_text_metrics_with_cache(renderer, text, fontprop, ismath, dpi)
66 """Call ``renderer.get_text_width_height_descent``, caching the results."""
67 # Cached based on a copy of fontprop so that later in-place mutations of
68 # the passed-in argument do not mess up the cache.
---> 69 return _get_text_metrics_with_cache_impl(
70 weakref.ref(renderer), text, fontprop.copy(), ismath, dpi)
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/text.py:77, in _get_text_metrics_with_cache_impl(renderer_ref, text, fontprop, ismath, dpi)
73 @functools.lru_cache(4096)
74 def _get_text_metrics_with_cache_impl(
75 renderer_ref, text, fontprop, ismath, dpi):
76 # dpi is unused, but participates in cache invalidation (via the renderer).
---> 77 return renderer_ref().get_text_width_height_descent(text, fontprop, ismath)
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/backends/backend_agg.py:213, in RendererAgg.get_text_width_height_descent(self, s, prop, ismath)
211 _api.check_in_list(["TeX", True, False], ismath=ismath)
212 if ismath == "TeX":
--> 213 return super().get_text_width_height_descent(s, prop, ismath)
215 if ismath:
216 ox, oy, width, height, descent, font_image = \
217 self.mathtext_parser.parse(s, self.dpi, prop)
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/backend_bases.py:652, in RendererBase.get_text_width_height_descent(self, s, prop, ismath)
648 fontsize = prop.get_size_in_points()
650 if ismath == 'TeX':
651 # todo: handle properties
--> 652 return self.get_texmanager().get_text_width_height_descent(
653 s, fontsize, renderer=self)
655 dpi = self.points_to_pixels(72)
656 if ismath:
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/texmanager.py:363, in TexManager.get_text_width_height_descent(cls, tex, fontsize, renderer)
361 if tex.strip() == '':
362 return 0, 0, 0
--> 363 dvifile = cls.make_dvi(tex, fontsize)
364 dpi_fraction = renderer.points_to_pixels(1.) if renderer else 1
365 with dviread.Dvi(dvifile, 72 * dpi_fraction) as dvi:
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/texmanager.py:295, in TexManager.make_dvi(cls, tex, fontsize)
293 with TemporaryDirectory(dir=cwd) as tmpdir:
294 tmppath = Path(tmpdir)
--> 295 cls._run_checked_subprocess(
296 ["latex", "-interaction=nonstopmode", "--halt-on-error",
297 f"--output-directory={tmppath.name}",
298 f"{texfile.name}"], tex, cwd=cwd)
299 (tmppath / Path(dvifile).name).replace(dvifile)
300 return dvifile
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/texmanager.py:254, in TexManager._run_checked_subprocess(cls, command, tex, cwd)
250 report = subprocess.check_output(
251 command, cwd=cwd if cwd is not None else cls._texcache,
252 stderr=subprocess.STDOUT)
253 except FileNotFoundError as exc:
--> 254 raise RuntimeError(
255 f'Failed to process string with tex because {command[0]} '
256 'could not be found') from exc
257 except subprocess.CalledProcessError as exc:
258 raise RuntimeError(
259 '{prog} was not able to process the following string:\n'
260 '{tex!r}\n\n'
(...)
267 exc=exc.output.decode('utf-8', 'backslashreplace'))
268 ) from None
RuntimeError: Failed to process string with tex because latex could not be found
<Figure size 640x480 with 1 Axes>
Numerical integration#
The phase plot can be used for a number of engineering analyses. It tells you the maximum and minimum speeds and displacements for the pendulum, how fast the system is moving at given location, etc. It does not tell you what \(\theta(t)\) is. If you have a nonlinear equation of motion and you need the solution for \(\theta(t)\), you have to use a numerical integration.
Numerical integration only works on first order differential equations. The idea behind the integration is that if you know the current value and you know how quickly it changes, then you can estimate the next value
\(next~value = current~value + \frac{d~value}{dt}*\Delta t\)
Here, you will separate the second order differential equation,
\(\ddot{\theta} = -\frac{g}{L}\sin\theta\)
into 2 first-order differential equations
\(\frac{d\theta}{dt} = \dot{\theta}\)
\(\frac{d\dot{\theta}}{dt} = -\frac{g}{L}\sin\theta\)
Now, you have 2 differential equations and one state varible, \(\mathbf{x} = [\theta,~\dot{\theta}]\).
\(\frac{dx_1}{dt} = x_2\)
\(\frac{d x_2}{dt} = -\frac{g}{L}\sin x_1\)
g = 9.81
L = 0.5
def pendulum(t, x):
'''pendulum equations of motion for theta and dtheta/dt
arguments
---------
t: current time
x: current state variable [theta, dtheta/dt]
outputs
-------
dx: current derivative of state variable [dtheta/dt, ddtheta/ddt]'''
dx = np.zeros(len(x))
dx[0] = x[1]
dx[1] = -g/L*np.sin(x[0])
return dx
The SciPy function solve_ivp
can now be used to integrate pendulum
and plot the solutions below. You can use a for-loop
to generate multiple solutions on one graph to compare results. Below, the solutions are plotted for \(\theta_0 = (18^o...180^o)\).
Show code cell source
from scipy.integrate import solve_ivp
for theta0 in np.linspace(np.pi/10, np.pi, 6):
sol = solve_ivp(pendulum, [0, 4], [theta0, 0], t_eval=np.linspace(0, 4))
plt.plot(sol.t, sol.y[0]*180/np.pi, label = r'$\theta_0$={:1.0f}$^o$'.format(theta0*180/np.pi))
plt.legend(loc = 'lower right')
<matplotlib.legend.Legend at 0x7f755573bf70>
Error in callback <function _draw_all_if_interactive at 0x7f75584b7dc0> (for post_execute), with arguments args (),kwargs {}:
---------------------------------------------------------------------------
FileNotFoundError Traceback (most recent call last)
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/texmanager.py:250, in TexManager._run_checked_subprocess(cls, command, tex, cwd)
249 try:
--> 250 report = subprocess.check_output(
251 command, cwd=cwd if cwd is not None else cls._texcache,
252 stderr=subprocess.STDOUT)
253 except FileNotFoundError as exc:
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/subprocess.py:424, in check_output(timeout, *popenargs, **kwargs)
422 kwargs['input'] = empty
--> 424 return run(*popenargs, stdout=PIPE, timeout=timeout, check=True,
425 **kwargs).stdout
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/subprocess.py:505, in run(input, capture_output, timeout, check, *popenargs, **kwargs)
503 kwargs['stderr'] = PIPE
--> 505 with Popen(*popenargs, **kwargs) as process:
506 try:
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/subprocess.py:951, in Popen.__init__(self, args, bufsize, executable, stdin, stdout, stderr, preexec_fn, close_fds, shell, cwd, env, universal_newlines, startupinfo, creationflags, restore_signals, start_new_session, pass_fds, user, group, extra_groups, encoding, errors, text, umask)
948 self.stderr = io.TextIOWrapper(self.stderr,
949 encoding=encoding, errors=errors)
--> 951 self._execute_child(args, executable, preexec_fn, close_fds,
952 pass_fds, cwd, env,
953 startupinfo, creationflags, shell,
954 p2cread, p2cwrite,
955 c2pread, c2pwrite,
956 errread, errwrite,
957 restore_signals,
958 gid, gids, uid, umask,
959 start_new_session)
960 except:
961 # Cleanup if the child failed starting.
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/subprocess.py:1837, in Popen._execute_child(self, args, executable, preexec_fn, close_fds, pass_fds, cwd, env, startupinfo, creationflags, shell, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite, restore_signals, gid, gids, uid, umask, start_new_session)
1836 err_msg = os.strerror(errno_num)
-> 1837 raise child_exception_type(errno_num, err_msg, err_filename)
1838 raise child_exception_type(err_msg)
FileNotFoundError: [Errno 2] No such file or directory: 'latex'
The above exception was the direct cause of the following exception:
RuntimeError Traceback (most recent call last)
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/pyplot.py:197, in _draw_all_if_interactive()
195 def _draw_all_if_interactive() -> None:
196 if matplotlib.is_interactive():
--> 197 draw_all()
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/_pylab_helpers.py:132, in Gcf.draw_all(cls, force)
130 for manager in cls.get_all_fig_managers():
131 if force or manager.canvas.figure.stale:
--> 132 manager.canvas.draw_idle()
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/backend_bases.py:1893, in FigureCanvasBase.draw_idle(self, *args, **kwargs)
1891 if not self._is_idle_drawing:
1892 with self._idle_draw_cntx():
-> 1893 self.draw(*args, **kwargs)
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/backends/backend_agg.py:388, in FigureCanvasAgg.draw(self)
385 # Acquire a lock on the shared font cache.
386 with (self.toolbar._wait_cursor_for_draw_cm() if self.toolbar
387 else nullcontext()):
--> 388 self.figure.draw(self.renderer)
389 # A GUI class may be need to update a window using this draw, so
390 # don't forget to call the superclass.
391 super().draw()
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/artist.py:95, in _finalize_rasterization.<locals>.draw_wrapper(artist, renderer, *args, **kwargs)
93 @wraps(draw)
94 def draw_wrapper(artist, renderer, *args, **kwargs):
---> 95 result = draw(artist, renderer, *args, **kwargs)
96 if renderer._rasterizing:
97 renderer.stop_rasterizing()
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/artist.py:72, in allow_rasterization.<locals>.draw_wrapper(artist, renderer)
69 if artist.get_agg_filter() is not None:
70 renderer.start_filter()
---> 72 return draw(artist, renderer)
73 finally:
74 if artist.get_agg_filter() is not None:
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/figure.py:3154, in Figure.draw(self, renderer)
3151 # ValueError can occur when resizing a window.
3153 self.patch.draw(renderer)
-> 3154 mimage._draw_list_compositing_images(
3155 renderer, self, artists, self.suppressComposite)
3157 for sfig in self.subfigs:
3158 sfig.draw(renderer)
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/image.py:132, in _draw_list_compositing_images(renderer, parent, artists, suppress_composite)
130 if not_composite or not has_images:
131 for a in artists:
--> 132 a.draw(renderer)
133 else:
134 # Composite any adjacent images together
135 image_group = []
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/artist.py:72, in allow_rasterization.<locals>.draw_wrapper(artist, renderer)
69 if artist.get_agg_filter() is not None:
70 renderer.start_filter()
---> 72 return draw(artist, renderer)
73 finally:
74 if artist.get_agg_filter() is not None:
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/axes/_base.py:3070, in _AxesBase.draw(self, renderer)
3067 if artists_rasterized:
3068 _draw_rasterized(self.figure, artists_rasterized, renderer)
-> 3070 mimage._draw_list_compositing_images(
3071 renderer, self, artists, self.figure.suppressComposite)
3073 renderer.close_group('axes')
3074 self.stale = False
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/image.py:132, in _draw_list_compositing_images(renderer, parent, artists, suppress_composite)
130 if not_composite or not has_images:
131 for a in artists:
--> 132 a.draw(renderer)
133 else:
134 # Composite any adjacent images together
135 image_group = []
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/artist.py:72, in allow_rasterization.<locals>.draw_wrapper(artist, renderer)
69 if artist.get_agg_filter() is not None:
70 renderer.start_filter()
---> 72 return draw(artist, renderer)
73 finally:
74 if artist.get_agg_filter() is not None:
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/axis.py:1388, in Axis.draw(self, renderer, *args, **kwargs)
1385 renderer.open_group(__name__, gid=self.get_gid())
1387 ticks_to_draw = self._update_ticks()
-> 1388 tlb1, tlb2 = self._get_ticklabel_bboxes(ticks_to_draw, renderer)
1390 for tick in ticks_to_draw:
1391 tick.draw(renderer)
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/axis.py:1315, in Axis._get_ticklabel_bboxes(self, ticks, renderer)
1313 if renderer is None:
1314 renderer = self.figure._get_renderer()
-> 1315 return ([tick.label1.get_window_extent(renderer)
1316 for tick in ticks if tick.label1.get_visible()],
1317 [tick.label2.get_window_extent(renderer)
1318 for tick in ticks if tick.label2.get_visible()])
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/axis.py:1315, in <listcomp>(.0)
1313 if renderer is None:
1314 renderer = self.figure._get_renderer()
-> 1315 return ([tick.label1.get_window_extent(renderer)
1316 for tick in ticks if tick.label1.get_visible()],
1317 [tick.label2.get_window_extent(renderer)
1318 for tick in ticks if tick.label2.get_visible()])
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/text.py:956, in Text.get_window_extent(self, renderer, dpi)
951 raise RuntimeError(
952 "Cannot get window extent of text w/o renderer. You likely "
953 "want to call 'figure.draw_without_rendering()' first.")
955 with cbook._setattr_cm(self.figure, dpi=dpi):
--> 956 bbox, info, descent = self._get_layout(self._renderer)
957 x, y = self.get_unitless_position()
958 x, y = self.get_transform().transform((x, y))
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/text.py:373, in Text._get_layout(self, renderer)
370 ys = []
372 # Full vertical extent of font, including ascenders and descenders:
--> 373 _, lp_h, lp_d = _get_text_metrics_with_cache(
374 renderer, "lp", self._fontproperties,
375 ismath="TeX" if self.get_usetex() else False, dpi=self.figure.dpi)
376 min_dy = (lp_h - lp_d) * self._linespacing
378 for i, line in enumerate(lines):
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/text.py:69, in _get_text_metrics_with_cache(renderer, text, fontprop, ismath, dpi)
66 """Call ``renderer.get_text_width_height_descent``, caching the results."""
67 # Cached based on a copy of fontprop so that later in-place mutations of
68 # the passed-in argument do not mess up the cache.
---> 69 return _get_text_metrics_with_cache_impl(
70 weakref.ref(renderer), text, fontprop.copy(), ismath, dpi)
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/text.py:77, in _get_text_metrics_with_cache_impl(renderer_ref, text, fontprop, ismath, dpi)
73 @functools.lru_cache(4096)
74 def _get_text_metrics_with_cache_impl(
75 renderer_ref, text, fontprop, ismath, dpi):
76 # dpi is unused, but participates in cache invalidation (via the renderer).
---> 77 return renderer_ref().get_text_width_height_descent(text, fontprop, ismath)
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/backends/backend_agg.py:213, in RendererAgg.get_text_width_height_descent(self, s, prop, ismath)
211 _api.check_in_list(["TeX", True, False], ismath=ismath)
212 if ismath == "TeX":
--> 213 return super().get_text_width_height_descent(s, prop, ismath)
215 if ismath:
216 ox, oy, width, height, descent, font_image = \
217 self.mathtext_parser.parse(s, self.dpi, prop)
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/backend_bases.py:652, in RendererBase.get_text_width_height_descent(self, s, prop, ismath)
648 fontsize = prop.get_size_in_points()
650 if ismath == 'TeX':
651 # todo: handle properties
--> 652 return self.get_texmanager().get_text_width_height_descent(
653 s, fontsize, renderer=self)
655 dpi = self.points_to_pixels(72)
656 if ismath:
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/texmanager.py:363, in TexManager.get_text_width_height_descent(cls, tex, fontsize, renderer)
361 if tex.strip() == '':
362 return 0, 0, 0
--> 363 dvifile = cls.make_dvi(tex, fontsize)
364 dpi_fraction = renderer.points_to_pixels(1.) if renderer else 1
365 with dviread.Dvi(dvifile, 72 * dpi_fraction) as dvi:
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/texmanager.py:295, in TexManager.make_dvi(cls, tex, fontsize)
293 with TemporaryDirectory(dir=cwd) as tmpdir:
294 tmppath = Path(tmpdir)
--> 295 cls._run_checked_subprocess(
296 ["latex", "-interaction=nonstopmode", "--halt-on-error",
297 f"--output-directory={tmppath.name}",
298 f"{texfile.name}"], tex, cwd=cwd)
299 (tmppath / Path(dvifile).name).replace(dvifile)
300 return dvifile
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/texmanager.py:254, in TexManager._run_checked_subprocess(cls, command, tex, cwd)
250 report = subprocess.check_output(
251 command, cwd=cwd if cwd is not None else cls._texcache,
252 stderr=subprocess.STDOUT)
253 except FileNotFoundError as exc:
--> 254 raise RuntimeError(
255 f'Failed to process string with tex because {command[0]} '
256 'could not be found') from exc
257 except subprocess.CalledProcessError as exc:
258 raise RuntimeError(
259 '{prog} was not able to process the following string:\n'
260 '{tex!r}\n\n'
(...)
267 exc=exc.output.decode('utf-8', 'backslashreplace'))
268 ) from None
RuntimeError: Failed to process string with tex because latex could not be found
---------------------------------------------------------------------------
FileNotFoundError Traceback (most recent call last)
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/texmanager.py:250, in TexManager._run_checked_subprocess(cls, command, tex, cwd)
249 try:
--> 250 report = subprocess.check_output(
251 command, cwd=cwd if cwd is not None else cls._texcache,
252 stderr=subprocess.STDOUT)
253 except FileNotFoundError as exc:
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/subprocess.py:424, in check_output(timeout, *popenargs, **kwargs)
422 kwargs['input'] = empty
--> 424 return run(*popenargs, stdout=PIPE, timeout=timeout, check=True,
425 **kwargs).stdout
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/subprocess.py:505, in run(input, capture_output, timeout, check, *popenargs, **kwargs)
503 kwargs['stderr'] = PIPE
--> 505 with Popen(*popenargs, **kwargs) as process:
506 try:
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/subprocess.py:951, in Popen.__init__(self, args, bufsize, executable, stdin, stdout, stderr, preexec_fn, close_fds, shell, cwd, env, universal_newlines, startupinfo, creationflags, restore_signals, start_new_session, pass_fds, user, group, extra_groups, encoding, errors, text, umask)
948 self.stderr = io.TextIOWrapper(self.stderr,
949 encoding=encoding, errors=errors)
--> 951 self._execute_child(args, executable, preexec_fn, close_fds,
952 pass_fds, cwd, env,
953 startupinfo, creationflags, shell,
954 p2cread, p2cwrite,
955 c2pread, c2pwrite,
956 errread, errwrite,
957 restore_signals,
958 gid, gids, uid, umask,
959 start_new_session)
960 except:
961 # Cleanup if the child failed starting.
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/subprocess.py:1837, in Popen._execute_child(self, args, executable, preexec_fn, close_fds, pass_fds, cwd, env, startupinfo, creationflags, shell, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite, restore_signals, gid, gids, uid, umask, start_new_session)
1836 err_msg = os.strerror(errno_num)
-> 1837 raise child_exception_type(errno_num, err_msg, err_filename)
1838 raise child_exception_type(err_msg)
FileNotFoundError: [Errno 2] No such file or directory: 'latex'
The above exception was the direct cause of the following exception:
RuntimeError Traceback (most recent call last)
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/IPython/core/formatters.py:340, in BaseFormatter.__call__(self, obj)
338 pass
339 else:
--> 340 return printer(obj)
341 # Finally look for special method names
342 method = get_real_method(obj, self.print_method)
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/IPython/core/pylabtools.py:152, in print_figure(fig, fmt, bbox_inches, base64, **kwargs)
149 from matplotlib.backend_bases import FigureCanvasBase
150 FigureCanvasBase(fig)
--> 152 fig.canvas.print_figure(bytes_io, **kw)
153 data = bytes_io.getvalue()
154 if fmt == 'svg':
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/backend_bases.py:2164, in FigureCanvasBase.print_figure(self, filename, dpi, facecolor, edgecolor, orientation, format, bbox_inches, pad_inches, bbox_extra_artists, backend, **kwargs)
2161 # we do this instead of `self.figure.draw_without_rendering`
2162 # so that we can inject the orientation
2163 with getattr(renderer, "_draw_disabled", nullcontext)():
-> 2164 self.figure.draw(renderer)
2165 if bbox_inches:
2166 if bbox_inches == "tight":
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/artist.py:95, in _finalize_rasterization.<locals>.draw_wrapper(artist, renderer, *args, **kwargs)
93 @wraps(draw)
94 def draw_wrapper(artist, renderer, *args, **kwargs):
---> 95 result = draw(artist, renderer, *args, **kwargs)
96 if renderer._rasterizing:
97 renderer.stop_rasterizing()
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/artist.py:72, in allow_rasterization.<locals>.draw_wrapper(artist, renderer)
69 if artist.get_agg_filter() is not None:
70 renderer.start_filter()
---> 72 return draw(artist, renderer)
73 finally:
74 if artist.get_agg_filter() is not None:
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/figure.py:3154, in Figure.draw(self, renderer)
3151 # ValueError can occur when resizing a window.
3153 self.patch.draw(renderer)
-> 3154 mimage._draw_list_compositing_images(
3155 renderer, self, artists, self.suppressComposite)
3157 for sfig in self.subfigs:
3158 sfig.draw(renderer)
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/image.py:132, in _draw_list_compositing_images(renderer, parent, artists, suppress_composite)
130 if not_composite or not has_images:
131 for a in artists:
--> 132 a.draw(renderer)
133 else:
134 # Composite any adjacent images together
135 image_group = []
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/artist.py:72, in allow_rasterization.<locals>.draw_wrapper(artist, renderer)
69 if artist.get_agg_filter() is not None:
70 renderer.start_filter()
---> 72 return draw(artist, renderer)
73 finally:
74 if artist.get_agg_filter() is not None:
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/axes/_base.py:3070, in _AxesBase.draw(self, renderer)
3067 if artists_rasterized:
3068 _draw_rasterized(self.figure, artists_rasterized, renderer)
-> 3070 mimage._draw_list_compositing_images(
3071 renderer, self, artists, self.figure.suppressComposite)
3073 renderer.close_group('axes')
3074 self.stale = False
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/image.py:132, in _draw_list_compositing_images(renderer, parent, artists, suppress_composite)
130 if not_composite or not has_images:
131 for a in artists:
--> 132 a.draw(renderer)
133 else:
134 # Composite any adjacent images together
135 image_group = []
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/artist.py:72, in allow_rasterization.<locals>.draw_wrapper(artist, renderer)
69 if artist.get_agg_filter() is not None:
70 renderer.start_filter()
---> 72 return draw(artist, renderer)
73 finally:
74 if artist.get_agg_filter() is not None:
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/axis.py:1388, in Axis.draw(self, renderer, *args, **kwargs)
1385 renderer.open_group(__name__, gid=self.get_gid())
1387 ticks_to_draw = self._update_ticks()
-> 1388 tlb1, tlb2 = self._get_ticklabel_bboxes(ticks_to_draw, renderer)
1390 for tick in ticks_to_draw:
1391 tick.draw(renderer)
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/axis.py:1315, in Axis._get_ticklabel_bboxes(self, ticks, renderer)
1313 if renderer is None:
1314 renderer = self.figure._get_renderer()
-> 1315 return ([tick.label1.get_window_extent(renderer)
1316 for tick in ticks if tick.label1.get_visible()],
1317 [tick.label2.get_window_extent(renderer)
1318 for tick in ticks if tick.label2.get_visible()])
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/axis.py:1315, in <listcomp>(.0)
1313 if renderer is None:
1314 renderer = self.figure._get_renderer()
-> 1315 return ([tick.label1.get_window_extent(renderer)
1316 for tick in ticks if tick.label1.get_visible()],
1317 [tick.label2.get_window_extent(renderer)
1318 for tick in ticks if tick.label2.get_visible()])
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/text.py:956, in Text.get_window_extent(self, renderer, dpi)
951 raise RuntimeError(
952 "Cannot get window extent of text w/o renderer. You likely "
953 "want to call 'figure.draw_without_rendering()' first.")
955 with cbook._setattr_cm(self.figure, dpi=dpi):
--> 956 bbox, info, descent = self._get_layout(self._renderer)
957 x, y = self.get_unitless_position()
958 x, y = self.get_transform().transform((x, y))
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/text.py:373, in Text._get_layout(self, renderer)
370 ys = []
372 # Full vertical extent of font, including ascenders and descenders:
--> 373 _, lp_h, lp_d = _get_text_metrics_with_cache(
374 renderer, "lp", self._fontproperties,
375 ismath="TeX" if self.get_usetex() else False, dpi=self.figure.dpi)
376 min_dy = (lp_h - lp_d) * self._linespacing
378 for i, line in enumerate(lines):
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/text.py:69, in _get_text_metrics_with_cache(renderer, text, fontprop, ismath, dpi)
66 """Call ``renderer.get_text_width_height_descent``, caching the results."""
67 # Cached based on a copy of fontprop so that later in-place mutations of
68 # the passed-in argument do not mess up the cache.
---> 69 return _get_text_metrics_with_cache_impl(
70 weakref.ref(renderer), text, fontprop.copy(), ismath, dpi)
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/text.py:77, in _get_text_metrics_with_cache_impl(renderer_ref, text, fontprop, ismath, dpi)
73 @functools.lru_cache(4096)
74 def _get_text_metrics_with_cache_impl(
75 renderer_ref, text, fontprop, ismath, dpi):
76 # dpi is unused, but participates in cache invalidation (via the renderer).
---> 77 return renderer_ref().get_text_width_height_descent(text, fontprop, ismath)
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/backends/backend_agg.py:213, in RendererAgg.get_text_width_height_descent(self, s, prop, ismath)
211 _api.check_in_list(["TeX", True, False], ismath=ismath)
212 if ismath == "TeX":
--> 213 return super().get_text_width_height_descent(s, prop, ismath)
215 if ismath:
216 ox, oy, width, height, descent, font_image = \
217 self.mathtext_parser.parse(s, self.dpi, prop)
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/backend_bases.py:652, in RendererBase.get_text_width_height_descent(self, s, prop, ismath)
648 fontsize = prop.get_size_in_points()
650 if ismath == 'TeX':
651 # todo: handle properties
--> 652 return self.get_texmanager().get_text_width_height_descent(
653 s, fontsize, renderer=self)
655 dpi = self.points_to_pixels(72)
656 if ismath:
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/texmanager.py:363, in TexManager.get_text_width_height_descent(cls, tex, fontsize, renderer)
361 if tex.strip() == '':
362 return 0, 0, 0
--> 363 dvifile = cls.make_dvi(tex, fontsize)
364 dpi_fraction = renderer.points_to_pixels(1.) if renderer else 1
365 with dviread.Dvi(dvifile, 72 * dpi_fraction) as dvi:
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/texmanager.py:295, in TexManager.make_dvi(cls, tex, fontsize)
293 with TemporaryDirectory(dir=cwd) as tmpdir:
294 tmppath = Path(tmpdir)
--> 295 cls._run_checked_subprocess(
296 ["latex", "-interaction=nonstopmode", "--halt-on-error",
297 f"--output-directory={tmppath.name}",
298 f"{texfile.name}"], tex, cwd=cwd)
299 (tmppath / Path(dvifile).name).replace(dvifile)
300 return dvifile
File /opt/hostedtoolcache/Python/3.9.19/x64/lib/python3.9/site-packages/matplotlib/texmanager.py:254, in TexManager._run_checked_subprocess(cls, command, tex, cwd)
250 report = subprocess.check_output(
251 command, cwd=cwd if cwd is not None else cls._texcache,
252 stderr=subprocess.STDOUT)
253 except FileNotFoundError as exc:
--> 254 raise RuntimeError(
255 f'Failed to process string with tex because {command[0]} '
256 'could not be found') from exc
257 except subprocess.CalledProcessError as exc:
258 raise RuntimeError(
259 '{prog} was not able to process the following string:\n'
260 '{tex!r}\n\n'
(...)
267 exc=exc.output.decode('utf-8', 'backslashreplace'))
268 ) from None
RuntimeError: Failed to process string with tex because latex could not be found
<Figure size 640x480 with 1 Axes>
Notice in the graphs how the time period changes depending upon initial angle, \(\theta_0\). You may have experienced this first hand if you have played on a swingset. Initially, you need to pump your legs quickly to get started, but then as you swing higher, you spend more time between pumps.
Note: What happens at the \(\theta_0 = 180^o\)? Why is it constant?
Wrapping up#
In this notebook, you used two techniques to solve the nonlinear pendulum equation of motion
work-energy formula which is equivalent to integrating by parts
numerical integration using
solve_ivp
You can use either of these techniques for any engineering system. Similarly, you can always linearize an equation of motion so you don’t need to use nonlinear solutions. Just keep in mind, a linearized solution will introduce error in your model results.