is the Newton drag term
that is applicable for large Reynolds numbers (turbulent), and
\(\rho_\mathrm{air}\) is the density of air, \(C\) is the drag
coefficient, \(A\) is the cross-sectional area of the projectile.
Note
Gravity only affects the vertical (\(y\)) component of velocity,
but drag affects both, since it is proportional to \({\bf v}\).
We’ll consider a baseball. Then we can take:
\(C = 0.3\)
\(A = \pi (d/2)^2\) with the diameter of a bassball, \(d = 0.074~\mathrm{m}\)
\(m = 0.145~\mathrm{kg}\)
\(\rho_\mathrm{air} = 1.2~\mathrm{kg~m^{-3}}\)
and we take the gravitational acceleration to be \(g = -9.81~\mathrm{m~s^{-2}}\),
We’ll imagine throwing the baseball from some height, \(y_0\), above the
ground (\(y = 0\)) at an angle \(\theta\) from the horizontal with velocity magnitude \(v_0\).
and we want to integrate until the ball hits the ground,
\(y(t_\mathrm{max}) = 0\).
Your task
Solve this system using our 2nd-order Runge-Kutta method.
You should structure your program the same way we did for our
2nd-order Runge-Kutta Orbit code in class. The main differences will be
to the righthand side function and the stopping criterion.
Run the case with and without drag (you can set \(C = 0\) for no
drag), and plot the results together on the same plot.
Tip
If you have 2 output files, file1.txt and file2.txt, you
can plot both of them together in gnuplot as:
plot 'file1.txt' using 1:2 title "no drag", 'file2.txt' using 1:2 title "drag"
This will plot column 2 vs. column 1 from each file as separate
lines and label them in the plot.
#include<iostream>#include<vector>#include<cmath>#include<numbers>#include<format>constdoubled=0.074;constdoubleA=std::numbers::pi*d*d;constdoublem=0.145;constdoublerho_air=1.2;constdoubleg=-9.81;structOrbitState{doublet;doublex;doubley;doublevx;doublevy;};OrbitStaterhs(constOrbitState&state,doubleC){OrbitStatedodt{};// dx/dt = vx; dy/dt = vydodt.x=state.vx;dodt.y=state.vy;// d(vx)/dt = - 1/2 C rho_air A |v| vx / m// d(vy)/dt = - 1/2 C rho_air A |v| vy / m - |g|doublev=std::sqrt(state.vx*state.vx+state.vy*state.vy);dodt.vx=-0.5*C*rho_air*A*v*state.vx/m;dodt.vy=-0.5*C*rho_air*A*v*state.vy/m-std::abs(g);returndodt;}OrbitStateupdate_state(constOrbitState&state,constdoubledt,constOrbitState&state_derivs){OrbitStatestate_new{};state_new.t=state.t+dt;state_new.x=state.x+dt*state_derivs.x;state_new.y=state.y+dt*state_derivs.y;state_new.vx=state.vx+dt*state_derivs.vx;state_new.vy=state.vy+dt*state_derivs.vy;returnstate_new;}voidwrite_history(conststd::vector<OrbitState>&history){for(constauto&o:history){std::cout<<std::format("{:11.8f} {:11.8f} {:11.8f} {:11.8f} {:11.8f}\n",o.t,o.x,o.y,o.vx,o.vy);}}std::vector<OrbitState>integrate(constOrbitState&state0,constdoubleC,constdoubledt_in){// how the history of the orbitstd::vector<OrbitState>orbit_history{};orbit_history.push_back(state0);doubledt=dt_in;OrbitStatestate=state0;// integration loopwhile(state.y>0.0){// get the derivativesautostate_derivs=rhs(state,C);// get the derivatives at the midpoint in timeautostate_star=update_state(state,0.5*dt,state_derivs);state_derivs=rhs(state_star,C);// update the statestate=update_state(state,dt,state_derivs);orbit_history.push_back(state);}returnorbit_history;}intmain(){// set initial conditionsOrbitStatestate0{};doubleC{};std::cout<<"# Enter the drag coefficient C: ";std::cin>>C;std::cout<<std::endl;// initial velocity and angledoublev0=10.0;doubletheta=std::numbers::pi/4.0;// initial heightdoubley0=10.0;state0.t=0.0;state0.x=0.0;state0.y=y0;state0.vx=v0*std::cos(theta);state0.vy=v0*std::sin(theta);doubledt=0.05;autoorbit_history=integrate(state0,C,dt);write_history(orbit_history);}
Notice that our while loop is:
while(state.y>0.0){...}
This will keep looping until the projectile hits the ground.
This prompts the user for the drag coefficient when run. You can run
both cases as: