Show code cell content
import numpy as np
import matplotlib.pyplot as plt
plt.style.use('fivethirtyeight')
plt.rcParams.update({
"text.usetex": True,
"font.sans-serif": ["Helvetica"]})
Driving forces for moving systems#
In this case study, you want to accelerate a 0.1-kg flywheel with a piston. The desired acceleration of the flywheel is \(\alpha=50~rad/s^2.\) The piston is attached to the link and creates a horizontal driving force. This example demonstrates how we can describe constraints in mathematical forms.
We have two moving bodies:
a flywheel with radius \(r=0.2~m\) and mass \(m=0.1~kg\)
a massless connecting link
Both objects move in a horizontal plane, so their positions have two degrees of freedom to describe position and one degree of freedom that describes orientation.
\(DOF = 3 + 3 = 6\)
Show code cell source
from IPython.display import SVG
SVG(filename = './images/piston-flywheel.svg')
Describing constraints#
There are six degrees of freedom for a flywheel and a link, but there are 6 constraints on the system’s motion:
the flywheel is pinned to the ground in the x-dir
the flywheel is pinned to the ground in the y-dir
the top of the link is pinned to the flywheel
the top of the link is pinned to the flywheel
the bottom of the link slides along the horizontal line
the angle of the flywheel has acceleration, \(\alpha=50~rad/s^2\)
\(system~DOF = 6 - 6 = 0~DOF\)
Note: In general, a pin in a planar system creates 2 constraints on motion.
You should recognize at this point that there are 0 differential equations to solve. Once you have calculated the kinematics based upon the system constraints, you can plug in the values and solve for force of the piston. So, start with the kinematic description of motion.
\(\mathbf{r}_2 =\mathbf{r}_{2/3} + \mathbf{r}_3\)
Show code cell source
SVG(filename = './images/flywheel-constraints.svg')
\(r(\sin\theta\hat{i} - \cos\theta \hat{j}) = -L(\cos\theta_{L}\hat{i}-\sin\theta_L\hat{j}) + d\hat{i}\)
this description creates two independent equations
\(-r\sin\theta = -L\cos\theta_{L}+ d\)
\(r\cos\theta = -L\sin\theta_L\)
The constraint on \(\theta\) says that \(\theta(t)=\frac{\alpha t^2}{2}\). You need to solve for \(\theta_L\) and \(d\) to determine the full state of the system.
\(\theta_L = \sin^{-1}\left(\frac{r}{L}\cos\theta\right)\)
\(d = L\cos\theta_{L}-r\sin\theta\)
Show code cell source
r = 0.2
L = 0.5
theta = np.linspace(0,2*np.pi,100)
thetaL = np.arcsin(r/L*np.cos(theta))
d = L*np.cos(thetaL)-r*np.sin(theta)
f, ax = plt.subplots()
ax.plot(theta*180/np.pi, thetaL*180/np.pi,'b-',label = r'\theta_L')
ax.set_xlabel(r'$\theta$ (degrees)')
ax.set_ylabel(r'$\theta_L$ (degrees)',color = 'blue')
plt.xticks(np.arange(0,360,30))
ax2 = ax.twinx()
ax2.plot(theta*180/np.pi, d, 'g--', label = 'piston d')
ax2.set_ylabel('d (meter)', color = 'green');
plt.show()
---------------------------------------------------------------------------
FileNotFoundError Traceback (most recent call last)
File /opt/hostedtoolcache/Python/3.9.21/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.21/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.21/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.21/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.21/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.21/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.21/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.21/x64/lib/python3.9/site-packages/matplotlib/backend_bases.py:2175, in FigureCanvasBase.print_figure(self, filename, dpi, facecolor, edgecolor, orientation, format, bbox_inches, pad_inches, bbox_extra_artists, backend, **kwargs)
2172 # we do this instead of `self.figure.draw_without_rendering`
2173 # so that we can inject the orientation
2174 with getattr(renderer, "_draw_disabled", nullcontext)():
-> 2175 self.figure.draw(renderer)
2176 if bbox_inches:
2177 if bbox_inches == "tight":
File /opt/hostedtoolcache/Python/3.9.21/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.21/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.21/x64/lib/python3.9/site-packages/matplotlib/figure.py:3162, in Figure.draw(self, renderer)
3159 # ValueError can occur when resizing a window.
3161 self.patch.draw(renderer)
-> 3162 mimage._draw_list_compositing_images(
3163 renderer, self, artists, self.suppressComposite)
3165 renderer.close_group('figure')
3166 finally:
File /opt/hostedtoolcache/Python/3.9.21/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.21/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.21/x64/lib/python3.9/site-packages/matplotlib/axes/_base.py:3137, in _AxesBase.draw(self, renderer)
3134 if artists_rasterized:
3135 _draw_rasterized(self.figure, artists_rasterized, renderer)
-> 3137 mimage._draw_list_compositing_images(
3138 renderer, self, artists, self.figure.suppressComposite)
3140 renderer.close_group('axes')
3141 self.stale = False
File /opt/hostedtoolcache/Python/3.9.21/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.21/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.21/x64/lib/python3.9/site-packages/matplotlib/axis.py:1424, in Axis.draw(self, renderer)
1421 renderer.open_group(__name__, gid=self.get_gid())
1423 ticks_to_draw = self._update_ticks()
-> 1424 tlb1, tlb2 = self._get_ticklabel_bboxes(ticks_to_draw, renderer)
1426 for tick in ticks_to_draw:
1427 tick.draw(renderer)
File /opt/hostedtoolcache/Python/3.9.21/x64/lib/python3.9/site-packages/matplotlib/axis.py:1351, in Axis._get_ticklabel_bboxes(self, ticks, renderer)
1349 if renderer is None:
1350 renderer = self.figure._get_renderer()
-> 1351 return ([tick.label1.get_window_extent(renderer)
1352 for tick in ticks if tick.label1.get_visible()],
1353 [tick.label2.get_window_extent(renderer)
1354 for tick in ticks if tick.label2.get_visible()])
File /opt/hostedtoolcache/Python/3.9.21/x64/lib/python3.9/site-packages/matplotlib/axis.py:1351, in <listcomp>(.0)
1349 if renderer is None:
1350 renderer = self.figure._get_renderer()
-> 1351 return ([tick.label1.get_window_extent(renderer)
1352 for tick in ticks if tick.label1.get_visible()],
1353 [tick.label2.get_window_extent(renderer)
1354 for tick in ticks if tick.label2.get_visible()])
File /opt/hostedtoolcache/Python/3.9.21/x64/lib/python3.9/site-packages/matplotlib/text.py:959, in Text.get_window_extent(self, renderer, dpi)
954 raise RuntimeError(
955 "Cannot get window extent of text w/o renderer. You likely "
956 "want to call 'figure.draw_without_rendering()' first.")
958 with cbook._setattr_cm(self.figure, dpi=dpi):
--> 959 bbox, info, descent = self._get_layout(self._renderer)
960 x, y = self.get_unitless_position()
961 x, y = self.get_transform().transform((x, y))
File /opt/hostedtoolcache/Python/3.9.21/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.21/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.21/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.21/x64/lib/python3.9/site-packages/matplotlib/backends/backend_agg.py:212, in RendererAgg.get_text_width_height_descent(self, s, prop, ismath)
210 _api.check_in_list(["TeX", True, False], ismath=ismath)
211 if ismath == "TeX":
--> 212 return super().get_text_width_height_descent(s, prop, ismath)
214 if ismath:
215 ox, oy, width, height, descent, font_image = \
216 self.mathtext_parser.parse(s, self.dpi, prop)
File /opt/hostedtoolcache/Python/3.9.21/x64/lib/python3.9/site-packages/matplotlib/backend_bases.py:597, in RendererBase.get_text_width_height_descent(self, s, prop, ismath)
593 fontsize = prop.get_size_in_points()
595 if ismath == 'TeX':
596 # todo: handle properties
--> 597 return self.get_texmanager().get_text_width_height_descent(
598 s, fontsize, renderer=self)
600 dpi = self.points_to_pixels(72)
601 if ismath:
File /opt/hostedtoolcache/Python/3.9.21/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.21/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.21/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 2 Axes>
Exercise:#
What is the angular velocity and angular acceleration of the link? \(\dot{\theta}_L\) and \(\ddot{\theta}_L\)
Now, we have solved all of the kinematics we need to solve for the applied piston force. Sometimes, you can look at the Newton-Euler equations for the syatem as a whole, but here we need to separate each component and sum forces and moments.
Flywheel:
\(\sum\mathbf{F}=\mathbf{0}\)
\(\sum M_G = I \alpha\)
Note: If a body is moving, you either have to sum moments about a fixed point or its center of mass. Otherwise, the description of angular acceleration is more involved.
Link:
\(\sum\mathbf{F}=\mathbf{0}\)
\(\sum M = 0\)
Note: when links, cables, or pulleys are assumed to be massless they still obey Newtonian mechanics, but momentum is always zero. So the sum of forces or moments is equal to 0.
The three kinetic equations for the wheel are as such,
\(\sum \mathbf{F}\cdot\hat{i} = F_{2x}+F_{1x} = 0\)
\(\sum \mathbf{F}\cdot\hat{j} = F_{2y}+F_{1y} = 0\)
\(\sum M_{1} = r\hat{e}_r \times (F_{2x}\hat{i}+F_{2y}\hat{j}) = \frac{mr^2}{2}\alpha\)
and the three kinetic equations for the link are as such,
\(\sum \mathbf{F}\cdot\hat{i} = R-F_{2x} = 0\)
\(\sum \mathbf{F}\cdot\hat{j} = -F_{2y} - F_{piston} = 0\)
\(\sum M_{2} = L\hat{b}_1 \times (-F_{piston}\hat{i} + R\hat{j}) = 0\)
The third equation for the link is rearranged to solve for \(R = F\tan\theta_L\). The third equation for the flywheel is rearranged to solve for \(F\) as such
\(rF\cos\theta -rF\tan\theta_L\sin\theta = \frac{mr^2}{2}\alpha\)
finally arriving at
\(F = \frac{mr\alpha}{2}\left(\cos\theta-\tan\theta_L\sin\theta \right)^{-1}\)
Plotted below as a function of flywheel angle, \(\theta\),
Show code cell source
m = 0.1
F = m*r/2*(np.cos(theta)-np.tan(thetaL)*np.sin(theta))**(-1)
plt.plot(theta*180/np.pi, F)
plt.ylim(-0.1,0.1)
plt.xticks(np.arange(0,360,30))
plt.xlabel(r'$\theta$ (degrees)')
plt.ylabel('Piston Force');
Error in callback <function _draw_all_if_interactive at 0x7f04f6dc4280> (for post_execute), with arguments args (),kwargs {}:
---------------------------------------------------------------------------
FileNotFoundError Traceback (most recent call last)
File /opt/hostedtoolcache/Python/3.9.21/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.21/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.21/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.21/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.21/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.21/x64/lib/python3.9/site-packages/matplotlib/pyplot.py:268, in _draw_all_if_interactive()
266 def _draw_all_if_interactive() -> None:
267 if matplotlib.is_interactive():
--> 268 draw_all()
File /opt/hostedtoolcache/Python/3.9.21/x64/lib/python3.9/site-packages/matplotlib/_pylab_helpers.py:131, in Gcf.draw_all(cls, force)
129 for manager in cls.get_all_fig_managers():
130 if force or manager.canvas.figure.stale:
--> 131 manager.canvas.draw_idle()
File /opt/hostedtoolcache/Python/3.9.21/x64/lib/python3.9/site-packages/matplotlib/backend_bases.py:1905, in FigureCanvasBase.draw_idle(self, *args, **kwargs)
1903 if not self._is_idle_drawing:
1904 with self._idle_draw_cntx():
-> 1905 self.draw(*args, **kwargs)
File /opt/hostedtoolcache/Python/3.9.21/x64/lib/python3.9/site-packages/matplotlib/backends/backend_agg.py:387, in FigureCanvasAgg.draw(self)
384 # Acquire a lock on the shared font cache.
385 with (self.toolbar._wait_cursor_for_draw_cm() if self.toolbar
386 else nullcontext()):
--> 387 self.figure.draw(self.renderer)
388 # A GUI class may be need to update a window using this draw, so
389 # don't forget to call the superclass.
390 super().draw()
File /opt/hostedtoolcache/Python/3.9.21/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.21/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.21/x64/lib/python3.9/site-packages/matplotlib/figure.py:3162, in Figure.draw(self, renderer)
3159 # ValueError can occur when resizing a window.
3161 self.patch.draw(renderer)
-> 3162 mimage._draw_list_compositing_images(
3163 renderer, self, artists, self.suppressComposite)
3165 renderer.close_group('figure')
3166 finally:
File /opt/hostedtoolcache/Python/3.9.21/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.21/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.21/x64/lib/python3.9/site-packages/matplotlib/axes/_base.py:3137, in _AxesBase.draw(self, renderer)
3134 if artists_rasterized:
3135 _draw_rasterized(self.figure, artists_rasterized, renderer)
-> 3137 mimage._draw_list_compositing_images(
3138 renderer, self, artists, self.figure.suppressComposite)
3140 renderer.close_group('axes')
3141 self.stale = False
File /opt/hostedtoolcache/Python/3.9.21/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.21/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.21/x64/lib/python3.9/site-packages/matplotlib/axis.py:1424, in Axis.draw(self, renderer)
1421 renderer.open_group(__name__, gid=self.get_gid())
1423 ticks_to_draw = self._update_ticks()
-> 1424 tlb1, tlb2 = self._get_ticklabel_bboxes(ticks_to_draw, renderer)
1426 for tick in ticks_to_draw:
1427 tick.draw(renderer)
File /opt/hostedtoolcache/Python/3.9.21/x64/lib/python3.9/site-packages/matplotlib/axis.py:1351, in Axis._get_ticklabel_bboxes(self, ticks, renderer)
1349 if renderer is None:
1350 renderer = self.figure._get_renderer()
-> 1351 return ([tick.label1.get_window_extent(renderer)
1352 for tick in ticks if tick.label1.get_visible()],
1353 [tick.label2.get_window_extent(renderer)
1354 for tick in ticks if tick.label2.get_visible()])
File /opt/hostedtoolcache/Python/3.9.21/x64/lib/python3.9/site-packages/matplotlib/axis.py:1351, in <listcomp>(.0)
1349 if renderer is None:
1350 renderer = self.figure._get_renderer()
-> 1351 return ([tick.label1.get_window_extent(renderer)
1352 for tick in ticks if tick.label1.get_visible()],
1353 [tick.label2.get_window_extent(renderer)
1354 for tick in ticks if tick.label2.get_visible()])
File /opt/hostedtoolcache/Python/3.9.21/x64/lib/python3.9/site-packages/matplotlib/text.py:959, in Text.get_window_extent(self, renderer, dpi)
954 raise RuntimeError(
955 "Cannot get window extent of text w/o renderer. You likely "
956 "want to call 'figure.draw_without_rendering()' first.")
958 with cbook._setattr_cm(self.figure, dpi=dpi):
--> 959 bbox, info, descent = self._get_layout(self._renderer)
960 x, y = self.get_unitless_position()
961 x, y = self.get_transform().transform((x, y))
File /opt/hostedtoolcache/Python/3.9.21/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.21/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.21/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.21/x64/lib/python3.9/site-packages/matplotlib/backends/backend_agg.py:212, in RendererAgg.get_text_width_height_descent(self, s, prop, ismath)
210 _api.check_in_list(["TeX", True, False], ismath=ismath)
211 if ismath == "TeX":
--> 212 return super().get_text_width_height_descent(s, prop, ismath)
214 if ismath:
215 ox, oy, width, height, descent, font_image = \
216 self.mathtext_parser.parse(s, self.dpi, prop)
File /opt/hostedtoolcache/Python/3.9.21/x64/lib/python3.9/site-packages/matplotlib/backend_bases.py:597, in RendererBase.get_text_width_height_descent(self, s, prop, ismath)
593 fontsize = prop.get_size_in_points()
595 if ismath == 'TeX':
596 # todo: handle properties
--> 597 return self.get_texmanager().get_text_width_height_descent(
598 s, fontsize, renderer=self)
600 dpi = self.points_to_pixels(72)
601 if ismath:
File /opt/hostedtoolcache/Python/3.9.21/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.21/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.21/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.21/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.21/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.21/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.21/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.21/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.21/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.21/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.21/x64/lib/python3.9/site-packages/matplotlib/backend_bases.py:2175, in FigureCanvasBase.print_figure(self, filename, dpi, facecolor, edgecolor, orientation, format, bbox_inches, pad_inches, bbox_extra_artists, backend, **kwargs)
2172 # we do this instead of `self.figure.draw_without_rendering`
2173 # so that we can inject the orientation
2174 with getattr(renderer, "_draw_disabled", nullcontext)():
-> 2175 self.figure.draw(renderer)
2176 if bbox_inches:
2177 if bbox_inches == "tight":
File /opt/hostedtoolcache/Python/3.9.21/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.21/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.21/x64/lib/python3.9/site-packages/matplotlib/figure.py:3162, in Figure.draw(self, renderer)
3159 # ValueError can occur when resizing a window.
3161 self.patch.draw(renderer)
-> 3162 mimage._draw_list_compositing_images(
3163 renderer, self, artists, self.suppressComposite)
3165 renderer.close_group('figure')
3166 finally:
File /opt/hostedtoolcache/Python/3.9.21/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.21/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.21/x64/lib/python3.9/site-packages/matplotlib/axes/_base.py:3137, in _AxesBase.draw(self, renderer)
3134 if artists_rasterized:
3135 _draw_rasterized(self.figure, artists_rasterized, renderer)
-> 3137 mimage._draw_list_compositing_images(
3138 renderer, self, artists, self.figure.suppressComposite)
3140 renderer.close_group('axes')
3141 self.stale = False
File /opt/hostedtoolcache/Python/3.9.21/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.21/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.21/x64/lib/python3.9/site-packages/matplotlib/axis.py:1424, in Axis.draw(self, renderer)
1421 renderer.open_group(__name__, gid=self.get_gid())
1423 ticks_to_draw = self._update_ticks()
-> 1424 tlb1, tlb2 = self._get_ticklabel_bboxes(ticks_to_draw, renderer)
1426 for tick in ticks_to_draw:
1427 tick.draw(renderer)
File /opt/hostedtoolcache/Python/3.9.21/x64/lib/python3.9/site-packages/matplotlib/axis.py:1351, in Axis._get_ticklabel_bboxes(self, ticks, renderer)
1349 if renderer is None:
1350 renderer = self.figure._get_renderer()
-> 1351 return ([tick.label1.get_window_extent(renderer)
1352 for tick in ticks if tick.label1.get_visible()],
1353 [tick.label2.get_window_extent(renderer)
1354 for tick in ticks if tick.label2.get_visible()])
File /opt/hostedtoolcache/Python/3.9.21/x64/lib/python3.9/site-packages/matplotlib/axis.py:1351, in <listcomp>(.0)
1349 if renderer is None:
1350 renderer = self.figure._get_renderer()
-> 1351 return ([tick.label1.get_window_extent(renderer)
1352 for tick in ticks if tick.label1.get_visible()],
1353 [tick.label2.get_window_extent(renderer)
1354 for tick in ticks if tick.label2.get_visible()])
File /opt/hostedtoolcache/Python/3.9.21/x64/lib/python3.9/site-packages/matplotlib/text.py:959, in Text.get_window_extent(self, renderer, dpi)
954 raise RuntimeError(
955 "Cannot get window extent of text w/o renderer. You likely "
956 "want to call 'figure.draw_without_rendering()' first.")
958 with cbook._setattr_cm(self.figure, dpi=dpi):
--> 959 bbox, info, descent = self._get_layout(self._renderer)
960 x, y = self.get_unitless_position()
961 x, y = self.get_transform().transform((x, y))
File /opt/hostedtoolcache/Python/3.9.21/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.21/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.21/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.21/x64/lib/python3.9/site-packages/matplotlib/backends/backend_agg.py:212, in RendererAgg.get_text_width_height_descent(self, s, prop, ismath)
210 _api.check_in_list(["TeX", True, False], ismath=ismath)
211 if ismath == "TeX":
--> 212 return super().get_text_width_height_descent(s, prop, ismath)
214 if ismath:
215 ox, oy, width, height, descent, font_image = \
216 self.mathtext_parser.parse(s, self.dpi, prop)
File /opt/hostedtoolcache/Python/3.9.21/x64/lib/python3.9/site-packages/matplotlib/backend_bases.py:597, in RendererBase.get_text_width_height_descent(self, s, prop, ismath)
593 fontsize = prop.get_size_in_points()
595 if ismath == 'TeX':
596 # todo: handle properties
--> 597 return self.get_texmanager().get_text_width_height_descent(
598 s, fontsize, renderer=self)
600 dpi = self.points_to_pixels(72)
601 if ismath:
File /opt/hostedtoolcache/Python/3.9.21/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.21/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.21/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>