ForwardEuler_comic_strip.py 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. """Comic strip for illustrating Euler's method for ODEs."""
  2. from pysketcher import *
  3. import numpy as np
  4. xkcd = True
  5. xmin = 0
  6. drawing_tool.set_coordinate_system(xmin=xmin, xmax=4,
  7. ymin=0, ymax=2.5,
  8. axis=True, xkcd=xkcd)
  9. drawing_tool.set_linecolor('blue')
  10. def ForwardEuler(I, a, T, dt):
  11. u = [I]
  12. t = [0]
  13. while t[-1] <= T:
  14. u_new = u[-1] - a*dt*u[-1]
  15. u.append(u_new)
  16. t.append(t[-1] + dt)
  17. return np.array(u), np.array(t)
  18. def make_fig(dt=0.5, heading=''):
  19. I = 2
  20. a = 0.5
  21. T_e = 3
  22. T_FE = 1
  23. t_fine = np.linspace(0, T_e, 101)
  24. u_e = I*np.exp(-a*t_fine)
  25. u, t = ForwardEuler(I, a, T_FE, dt)
  26. # y = slope*(x - x0) + y0
  27. # u_extrapolated = -a*u[-1]*(t - t[-1]) + u[-1]
  28. t_future = t[-1] + 1.5 # let the line be longer than one step
  29. line = Line((t[-1], u[-1]), (t_future, -a*u[-1]*(t_future - t[-1]) + u[-1]))
  30. circles = {
  31. i: Circle((t[i], u[i]), 0.05).set_linecolor('red').set_filled_curves('red')
  32. for i in range(1, len(u))}
  33. # Add next predicted point
  34. t_next = t[-1] + dt
  35. u_next = -a*u[-1]*(t_next - t[-1]) + u[-1]
  36. circles[0] = Circle((t_next, u_next), 0.05).\
  37. set_linecolor('red').set_filled_curves('red')
  38. circles = Composition(circles)
  39. curves = Composition(dict(
  40. exact=Curve(t_fine, u_e).set_linestyle('dashed'),
  41. numerical=Curve(t, u),
  42. extrapolation=line.set_linecolor('red').set_linewidth(3)))
  43. text_exact = Text_wArrow("exact solution", (2.5, 1), (2.5, I*np.exp(-a*2.5)),
  44. alignment='left')
  45. text_predict = Text_wArrow("Here we know the slope:\n$u'=f(u,t)$!\nLet the solution continue\nalong that slope.",
  46. (1.7, 1.7), (t[-1], u[-1]),
  47. alignment='left')
  48. text_next = Text_wArrow("This is the next\npredicted point",
  49. (1, 0.25), (t_next, u_next),
  50. alignment='left')
  51. text_comment = Text(heading, (0.3, 2.05), alignment='left')
  52. fig = Composition(dict(curves=curves,
  53. circles=circles,
  54. exact=text_exact,
  55. predict=text_predict,
  56. next=text_next,
  57. comment=text_comment))
  58. return fig
  59. fig = make_fig(dt=0.5, heading="Differential equations $u'=f(u,t)$\nare hard to solve,\nbut not with programming!")
  60. fig.draw()
  61. drawing_tool.display()
  62. drawing_tool.savefig('tmp1')
  63. drawing_tool.erase()
  64. fig = make_fig(dt=0.24, heading='Just reduce the time step\nto make more accurate\npredictions!')
  65. fig.draw()
  66. drawing_tool.display()
  67. drawing_tool.savefig('tmp2')
  68. import os
  69. comic = 'comic' if xkcd else 'non_comic'
  70. os.system('doconce combine_images pdf -2 tmp1 tmp2 FE_%s_strip' % comic)
  71. os.system('doconce combine_images png -2 tmp1 tmp2 FE_%s_strip' % comic)
  72. input()