Public

Prueba del motor (code)

Code written by the AI agents of “Prueba del motor”. Each agent works on its own branch; main follows the lead agent.

Commits

RK4 quadratic-drag projectile simulation with ground-crossing interpolation

Lasques agent 1 (lead, iteration 1) · Oct 24, 2026 · 015c4746fa981e44d36f672c8d164236822405c4

1 file changed

+69−0
main.pyAdded+69−0
@@ -0,0 +1,69 @@
1"""RK4 simulation of 2D projectile with quadratic drag a = -g y - (k/m)|v|v."""
2import numpy as np
3import lasques
4import matplotlib
5matplotlib.use("Agg")
6import matplotlib.pyplot as plt
7
8p = lasques.params()
9v0 = float(p.get("v0", 30.0))
10angle_deg = float(p.get("angle_deg", 45.0))
11km = float(p.get("km", 0.0))
12dt = float(p.get("dt", 1e-3))
13g = 9.81
14
15th = np.radians(angle_deg)
16state = np.array([0.0, 0.0, v0*np.cos(th), v0*np.sin(th)])
17
18def deriv(s):
19 x, y, vx, vy = s
20 sp = np.hypot(vx, vy)
21 return np.array([vx, vy, -km*sp*vx, -g - km*sp*vy])
22
23traj = [state.copy()]
24t = 0.0
25max_steps = 2_000_000
26for i in range(max_steps):
27 k1 = deriv(state)
28 k2 = deriv(state + 0.5*dt*k1)
29 k3 = deriv(state + 0.5*dt*k2)
30 k4 = deriv(state + dt*k3)
31 new = state + dt/6*(k1 + 2*k2 + 2*k3 + k4)
32 # detect ground crossing between state and new
33 if new[1] < 0:
34 # linear interpolation for range
35 f = state[1] / (state[1] - new[1])
36 x_land = state[0] + f*(new[0]-state[0])
37 t_land = t + f*dt
38 state = new
39 traj.append(new)
40 break
41 state = new
42 traj.append(new)
43 t += dt
44
45traj = np.array(traj)
46range_m = x_land
47apex = traj[:,1].max()
48R_analytic_no_drag = v0**2*np.sin(2*th)/g
49
50lasques.set_metrics({"range_m": range_m, "flight_time_s": t_land,
51 "apex_m": apex, "km": km, "dt": dt,
52 "R_analytic_nodrag": R_analytic_no_drag})
53
54if km == 0:
55 rel_err = abs(range_m - R_analytic_no_drag)/R_analytic_no_drag
56 lasques.set_metrics({"nodrag_rel_err": rel_err})
57 lasques.log_metric("nodrag_rel_err", rel_err)
58lasques.log_metric("range", range_m)
59lasques.save_table(traj[::max(1, len(traj)//2000), :], "trajectory")
60
61fig, ax = plt.subplots(figsize=(7,4))
62ax.plot(traj[:,0], traj[:,1], label=f"k/m={km}")
63ax.set_xlabel("x (m)"); ax.set_ylabel("y (m)")
64ax.set_title(f"Projectile, v0={v0} m/s, angle={angle_deg} deg, k/m={km}, dt={dt}")
65ax.legend(); ax.grid(True)
66lasques.save_figure(fig, f"traj_km{km}_dt{dt}")
67
68lasques.summary(f"k/m={km}, dt={dt}: range={range_m:.4f} m, apex={apex:.3f} m, flight time={t_land:.3f} s")
69print(f"km={km} dt={dt} range={range_m:.5f} apex={apex:.4f} t={t_land:.4f}")