Skip to content

Commit 85bcccd

Browse files
committed
Wrapped up projectile motion post.
1 parent 0d061b9 commit 85bcccd

File tree

11 files changed

+76
-49
lines changed

11 files changed

+76
-49
lines changed

_freeze/posts/projectileMotion/projectileMotionWithDrag/execute-results/html.json

Lines changed: 2 additions & 2 deletions
Large diffs are not rendered by default.
Loading
Loading
Loading
Loading
Loading
Loading
Binary file not shown.

posts/projectileMotion/projectile.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,8 @@ class dragProjectile:
111111
'''
112112
def __init__(self, vLaunchMag, vLaunchDir, height, mass, dragCoef):
113113
model = dragEOM(vLaunchMag, vLaunchDir, height, mass, dragCoef)
114-
tMax = 2 * model.idealTof
115-
tVals = np.linspace(0,tMax,100)
114+
tMax = 10 * model.idealTof
115+
tVals = np.linspace(0,tMax,1000)
116116
u0 = model.u0
117117
sol = solve_ivp(model, t_span=[0,tMax], y0 = u0, t_eval=tVals, events=model.splash, dense_output=True)
118118
self.tof = sol.t_events[0][0]
@@ -143,10 +143,10 @@ def makeTrajectoryPlot(vi,theta,h,m,c):
143143
x, y = pos(tt)
144144
maxY = np.max((np.max(y),np.max(rCan.y)))
145145
maxX = 1.1* np.max((np.max(x),np.max(rCan.x)))
146-
fig, ax = plt.subplots()
146+
fig, ax = plt.subplots(figsize=(8,4))
147147

148-
ax.plot(x,y,'r',label='Ideal')
149-
ax.plot(rCan.x,rCan.y,'g',label='With drag')
148+
ax.plot(x,y,'r',label='Vacuum')
149+
ax.plot(rCan.x,rCan.y,'g',label='Air')
150150
ax.axis('equal')
151151
ax.set_xlabel('x (m)')
152152
ax.set_ylabel('y (m)')
@@ -157,7 +157,8 @@ def makeTrajectoryPlot(vi,theta,h,m,c):
157157
ax.add_patch(Rectangle((-30,-20),30,y[0]+20, color='slategrey'))
158158
fig.suptitle(pltTitle)
159159
ax.legend()
160-
plt.figtext(0.8,0.01, txt,family=['DejaVu Sans','FontAwesome'],fontsize=10)
160+
fig.subplots_adjust(right=0.9,bottom=0.25)
161+
plt.figtext(0.6,0.01, txt,family=['DejaVu Sans','FontAwesome'],fontsize=10)
161162
plt.show()
162163

163164
def plotCannonCurves(vi,theta,h,m,c):
@@ -184,7 +185,7 @@ def rep(comp):
184185

185186
pltTitle = f'Cannon fired at {float(theta):.0f} deg'
186187

187-
fig, axs = plt.subplots(3,2, sharex=True, sharey='row',figsize=(5,7))
188+
fig, axs = plt.subplots(3,2, sharex=True, sharey='row',figsize=(8,10))
188189
axs[0,0].plot(timeI,x,label='vacuum')
189190
axs[0,0].plot(rCan.t,rCan.x,label='air')
190191
axs[0,0].set_ylabel(r'position $(m)$')
@@ -208,6 +209,6 @@ def rep(comp):
208209
for i in range(3):
209210
for j in range(2):
210211
axs[i,j].legend()
211-
plt.figtext(0.8,0.01, txt,family=['DejaVu Sans','FontAwesome'],fontsize=10)
212+
plt.figtext(0.6,0.01, txt,family=['DejaVu Sans','FontAwesome'],fontsize=10)
212213
plt.show()
213214

posts/projectileMotion/projectileMotionWithDrag.qmd

Lines changed: 65 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,19 @@ execute:
1313
messages: false
1414
warning: false
1515
jupyter: python3
16-
draft: true
1716
---
1817

19-
I've stalled out a bit on my heat equation modeling project because I've been thinking about ways to make it better. Also, I've been busy with the real world. So I thought I'd adapt something that I've used in class and post it here. In the standard treatment, we often make approximations that may or may not be valid, and never test these approximations. In fact, that's why we physicists often get made fun of, because we are talking about spherical cows. I like to get my students to think beyond spherical cows and give them tools to examine the limits of these approximations.
18+
:::: {layout="[75, 25]"}
2019

21-
[![ ](spherical-cow.png){width=25% fig-align="center"}](https://www.sphericalcowblog.com/spherical-cows)
20+
::: {#first-column}
21+
I've stalled out a bit on my heat equation modeling project because I've been thinking about ways to make it better. Also, I've been busy with the real world. So I thought I'd adapt something that I've used in class and post it here. Plus, I can't let my students have *_all_* the fun, right? In the standard treatment, we often make approximations that may or may not be valid, and never test these approximations. In fact, that's why we physicists often get made fun of, because we are talking about spherical cows. I like to get my students to think beyond spherical cows and give them tools to examine the limits of these approximations. So I'm going to build the models, and then play around a bit.
22+
:::
23+
24+
::: {#second-column}
25+
[![ ](spherical-cow.png){width=15% fig-align="center"}](https://www.sphericalcowblog.com/spherical-cows)
26+
:::
27+
28+
::::
2229

2330
## A typical Physics 1 projectile motion problem
2431
I've given this problem (or variations of it) in my physics class for years:
@@ -276,7 +283,7 @@ class dragProjectile:
276283
## Comparing motion with/without air resistance
277284
Now that we have these tools built, we can start to visualize this motion. I'll do this using some functions in my `projectile.py` file to make graphs. I will also include the following numerical values:
278285

279-
- $H = 100$ m
286+
- $H = 200$ m
280287
- $v_i = 100$ m/s
281288
- $\theta = 30$ deg
282289
- $m = 1$ kg
@@ -287,7 +294,7 @@ import numpy as np
287294
import matplotlib.pyplot as plt
288295
from projectile import *
289296
290-
H = 100
297+
H = 200
291298
vi = 100
292299
theta = 30
293300
m = 1
@@ -303,7 +310,7 @@ I will visualize this motion in two ways. First, I'll plot each quantity of the
303310
plotCannonCurves(vi,theta,H,m,c)
304311
```
305312

306-
Now I will plot the trajectory (y-coordinate vs x-coordinate).
313+
Now I will plot the trajectory (y-coordinate vs x-coordinate). Note that I have added boxes to this plot indicating the cliff location and the water level to help guide the eye. The plot has also been set up with an equal aspect ratio so that the horizontal and vertical distances are the same. The green curve should more closely match the golf ball tracer than the red parabolic path does.
307314

308315
```{python}
309316
#| fig-align: "center"
@@ -312,19 +319,21 @@ Now I will plot the trajectory (y-coordinate vs x-coordinate).
312319
makeTrajectoryPlot(vi,theta,H,m,c)
313320
```
314321

315-
### When should we consider air resistance?
316-
Since we are usually most concerned with where the projectile will land (have to hit the pirates storming the fort...) let's design a metric for quantifying the difference between the models with/without air resistance. Since I used $D$ to denote the range, I will write $D_1$ to denote the range for model 1 (no air resistance) and $D_2$ to denote the range for model 2 (with air resistance). Therefore the quantity below is the fractional difference between the targeting location of the models.
322+
### Where to go from here
323+
Usually, I'll give my students some tasks like finding the maximum range of the projectile, and comparing the model results. I thought that I would do something a little different as it will allow me to compare the model predictions. I will do this using the fractional difference between the two predictions for both the range and the time of flight. Since I used $D$ to denote the range, I will write $D_1$ to denote the range for model 1 (no air resistance) and $D_2$ to denote the range for model 2 (with air resistance). Therefore the quantity below is the fractional difference between the targeting location of the models.
317324

318325
$$
319326
\delta_D = \frac{\left|D_1 - D_2\right|}{D_1}
320327
$$
321328

322-
I would also like to calculate a similar quantity related to the time-of-flight
329+
I will also define a similar quantity related to the time-of-flight.
323330

324331
$$
325-
\delta_T = \frac{\left|T_1 - T_2\right|}{T_1}
332+
\delta_T = \frac{\left|T_2 - T_1\right|}{T_2}
326333
$$
327334

335+
Note that I'm using $D_1$ in the denominator of $\delta_D$ and $T_2$ in the denominator of $\delta_T$. This is because these are the larger values for each motion.
336+
328337
I will calculate these quantities for a range of launch velocities, masses, and launch angles keeping the cliff height $(H=100 \,\text{m})$ and drag coefficient $(c=0.003 \,\text{kg/m})$ the same as it was before.
329338

330339
```{python}
@@ -333,9 +342,9 @@ I will calculate these quantities for a range of launch velocities, masses, and
333342
import matplotlib.colors as mc
334343
import matplotlib.cm as cm
335344
336-
launchVelocities = np.linspace(0,1000,20)
337-
launchAngles = np.arange(0,89,5)
338-
masses = np.linspace(0.1,100,20)
345+
launchVelocities = np.linspace(0,200,21)
346+
launchAngles = np.linspace(0,85,18)
347+
masses = np.logspace(-2,2,19)
339348
nv = len(launchVelocities)
340349
na = len(launchAngles)
341350
nm = len(masses)
@@ -353,7 +362,7 @@ for i in range(nv):
353362
D2 = m2.maxX
354363
T1 = m1.tof
355364
T2 = m2.tof
356-
deltaT[i,j,k] = np.abs(T1 - T2)/T1
365+
deltaT[i,j,k] = np.abs(T1 - T2)/T2
357366
if D1<1 and D2 <1:
358367
# For small ranges (less than 1 meter from the cliff), just set the
359368
# fractional difference equal to zero
@@ -362,8 +371,10 @@ for i in range(nv):
362371
deltaD[i,j,k] = np.abs(D1 - D2)/D1
363372
364373
# Set up properties of the color bar
365-
cnorm = mc.Normalize(vmin=0,vmax=1)
366-
cbar = cm.ScalarMappable(norm=cnorm)
374+
cnormD = mc.Normalize(vmin=0,vmax=deltaD.max())
375+
cbarD = cm.ScalarMappable(norm=cnormD)
376+
cnormT = mc.Normalize(vmin=0,vmax=deltaT.max())
377+
cbarT = cm.ScalarMappable(norm=cnormT)
367378
368379
# Arrange the calculations into multiple grids for making a heatmap/contour plot
369380
X1, Y1 = np.meshgrid(launchVelocities, launchAngles)
@@ -379,7 +390,7 @@ Z3 = deltaD[nv//2, :, :].transpose()
379390
ZZ3 = deltaT[nv//2, :, :].transpose()
380391
```
381392

382-
Now, let's examine the range:
393+
Now, let's examine the model agreement for the range:
383394
```{python}
384395
#| code-fold: true
385396
#| fig-align: "center"
@@ -395,49 +406,64 @@ ax1[1].contourf(X2,Y2,Z2,100)
395406
ax1[1].set_title(f'Launch angle = {launchAngles[na//2]:.0f} deg',size=10)
396407
ax1[1].set_xlabel('Launch velocity (m/s)')
397408
ax1[1].set_ylabel('Mass (kg)')
409+
ax1[1].set_yscale('log')
398410
ax1[2].contourf(X3,Y3,Z3,100)
399411
ax1[2].set_title(f'Launch velocity = {launchVelocities[nv//2]:.0f} m/s',size=10)
400412
ax1[2].set_xlabel('Launch angle (deg)')
401413
ax1[2].set_ylabel('Mass (kg)')
402-
fig1.subplots_adjust(bottom=0.3,wspace=0.45,right=0.9)
414+
ax1[2].set_yscale('log')
415+
fig1.subplots_adjust(bottom=0.3,wspace=0.5,right=0.9)
403416
cbar_ax = fig1.add_axes([0.2, 0.15, 0.65, 0.02])
404-
fig1.colorbar(cbar, cax=cbar_ax,label=r'$\delta_D$',orientation='horizontal')
417+
fig1.colorbar(cbarD, cax=cbar_ax,label=r'$\delta_D$',orientation='horizontal')
405418
ghLogo = u"\uf09b"
406419
liLogo = u"\uf08c"
407420
txt = f"{ghLogo} datawolf04 {liLogo} steven-wolf-253b6625a"
408421
plt.figtext(0.6,0.01, txt,family=['DejaVu Sans','FontAwesome'],fontsize=10)
422+
plt.savefig('rangeCalcFracDiff.png')
409423
plt.show()
410424
```
411425

412-
Areas where the color is yellow shows a high level of disagreement between motion in vacuum vs. motion in air. Areas where the color is blue/violet shows a low level of disagreement between the models. Since air resistance is dependent on speed, we would expect that at low speeds, air resistance is less important. And this is what we see in these plots--note the deep blue color in the first 2 panels along the y axis (where the launch velocity is small). This should make sense. Also we note that with large masses, air resistance is less important as well.
413-
414-
Finally, examine the Time of Flight:
415426

427+
Finally, I'll examine the model agreement for the time-of-flight:
416428
```{python}
417429
#| code-fold: true
418430
#| fig-align: "center"
419431
#| width: 90%
420432
421-
fig2, ax2 = plt.subplots(1,3,figsize=(7,4))
422-
fig2.suptitle("Comparing predicted TOF with/without air resistance")
423-
CS = ax2[0].contourf(X1,Y1,ZZ1,100)
424-
ax2[0].set_title(f'Mass = {masses[nm//2]:.2f} kg',size=10)
425-
ax2[0].set_xlabel('Launch velocity (m/s)')
426-
ax2[0].set_ylabel('Launch angle (deg)')
427-
ax2[1].contourf(X2,Y2,ZZ2,100)
428-
ax2[1].set_title(f'Launch angle = {launchAngles[na//2]:.0f} deg',size=10)
429-
ax2[1].set_xlabel('Launch velocity (m/s)')
430-
ax2[1].set_ylabel('Mass (kg)')
431-
ax2[2].contourf(X3,Y3,ZZ3,100)
432-
ax2[2].set_title(f'Launch velocity = {launchVelocities[nv//2]:.0f} m/s',size=10)
433-
ax2[2].set_xlabel('Launch angle (deg)')
434-
ax2[2].set_ylabel('Mass (kg)')
435-
fig2.subplots_adjust(bottom=0.3,wspace=0.45,right=0.9)
436-
cbar_ax = fig2.add_axes([0.2, 0.15, 0.65, 0.02])
437-
fig2.colorbar(cbar, cax=cbar_ax,label=r'$\delta_D$',orientation='horizontal')
433+
fig1, ax1 = plt.subplots(1,3,figsize=(7,4))
434+
fig1.suptitle("Comparing predicted time of flight with/without air resistance")
435+
CS = ax1[0].contourf(X1,Y1,ZZ1,100)
436+
ax1[0].set_title(f'Mass = {masses[nm//2]:.2f} kg',size=10)
437+
ax1[0].set_xlabel('Launch velocity (m/s)')
438+
ax1[0].set_ylabel('Launch angle (deg)')
439+
ax1[1].contourf(X2,Y2,ZZ2,100)
440+
ax1[1].set_title(f'Launch angle = {launchAngles[na//2]:.0f} deg',size=10)
441+
ax1[1].set_xlabel('Launch velocity (m/s)')
442+
ax1[1].set_ylabel('Mass (kg)')
443+
ax1[1].set_yscale('log')
444+
ax1[2].contourf(X3,Y3,ZZ3,100)
445+
ax1[2].set_title(f'Launch velocity = {launchVelocities[nv//2]:.0f} m/s',size=10)
446+
ax1[2].set_xlabel('Launch angle (deg)')
447+
ax1[2].set_ylabel('Mass (kg)')
448+
ax1[2].set_yscale('log')
449+
fig1.subplots_adjust(bottom=0.3,wspace=0.5,right=0.9)
450+
cbar_ax = fig1.add_axes([0.2, 0.15, 0.65, 0.02])
451+
fig1.colorbar(cbarT, cax=cbar_ax,label=r'$\delta_T$',orientation='horizontal')
438452
ghLogo = u"\uf09b"
439453
liLogo = u"\uf08c"
440454
txt = f"{ghLogo} datawolf04 {liLogo} steven-wolf-253b6625a"
441455
plt.figtext(0.6,0.01, txt,family=['DejaVu Sans','FontAwesome'],fontsize=10)
442456
plt.show()
443457
```
458+
459+
Note that the color scales for these plots have different ranges. In particular the max $\delta_T$ value is larger than the max $\delta_D$ value. Given the initial height, it would seem that the time of flight is much more sensitive to air resistance. In general, areas on the plots above where the color is yellow shows a high level of disagreement between motion in vacuum vs. motion in air. Areas where the color is blue/violet shows a low level of disagreement between the models. Since air resistance is dependent on speed, we would expect that at low speeds, air resistance is less important. Given the fact that gravity/weight is the other force of interest, objects with a larger mass are also less sensitive to air resistance.
460+
461+
There is an interesting "trough" in the time of flight graphic. You can see that the minimum $\delta_T$ values aren't along one of the axes as they are for $\delta_T$. This is because for very slow, or nearly vertical launch trajectories, the range isn't going to be very different as it just doesn't move very fast in the x-direction. But the time is quite different, and you can start to see the reason for this when you look at the $v_y$ vs $t$ graph. For the graphs below, you can see that the projectile turns around $(v_y=0)$ sooner when air resistance is considered. It's fun to think about and play around with.
462+
463+
```{python}
464+
#| fig-align: "center"
465+
#| width: 75%
466+
467+
makeTrajectoryPlot(50,60,H,1,c)
468+
plotCannonCurves(50,60,H,1,c)
469+
```

0 commit comments

Comments
 (0)