energyConservation.py 2.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
  1. """Basic testing to ensure the chosen timestep is sensible and to probe
  2. the symplectic character of the integrator."""
  3. import matplotlib.pyplot as plt
  4. import numpy as np
  5. from scipy.signal import savgol_filter
  6. from analysis import utils
  7. def calculatePotentialEnergy(r_vec, v_vec, mass, soft=0.):
  8. """Calculates the potential energy of a set of masses.
  9. It is an adaptation of the acceleration routines.
  10. """
  11. mask = mass!=0
  12. massMassive = mass[mask]
  13. rMassive_vec = r_vec[mask]
  14. mass_mat = massMassive.reshape(1, -1, 1)
  15. r_ten = rMassive_vec.reshape(1, -1, 3) - r_vec.reshape(-1, 1, 3)
  16. r_mat = np.linalg.norm(r_ten, axis=-1, keepdims=True)
  17. # Avoid self interactions and divisions by 0
  18. mass_mat = np.where(r_mat == 0, np.zeros_like(mass_mat), mass_mat)
  19. r_mat = np.where(r_mat == 0, np.ones_like(r_mat), r_mat)
  20. PE = -mass_mat/(r_mat + soft)
  21. # Add all contributions but do not sum them twice
  22. PE = np.sum(PE, axis=1)[:,0]/2 * mass
  23. energy = PE + 1/2 * np.linalg.norm(v_vec, axis=-1)**2 * mass
  24. return np.sum(energy)
  25. '''PE = np.sum(PE, axis=1)[:,0]
  26. print((mass==0)[:5])
  27. print('PE', PE[:5], (1/2 * np.linalg.norm(v_vec, axis=-1)**2)[:5])
  28. energy = PE + 1/2 * np.linalg.norm(v_vec, axis=-1)**2
  29. return np.sum(energy[mass==0])'''
  30. #calculatePotentialEnergy(np.array([[0,0,0],[1,0,0], [2,0,0]]), np.array([1,1, 0]), soft=0.)
  31. f, ax = plt.subplots(1, 1)
  32. for name, label, factor, color in zip(['dt1E-2', 'dt1E-3', 'dt1E-4'],
  33. [r'$dt=10^{-2}$', r'$dt=10^{-3}$', r'$dt=10^{-4}$'],
  34. [1, 10, 100],
  35. ['cornflowerblue', 'darkorange', '#75d2aa']):
  36. time = []
  37. errors = []
  38. # Use start of the simulation as ground truth
  39. truth = utils.loadData(name, 10*factor)
  40. truthEnergy = calculatePotentialEnergy(truth['r_vec'], truth['v_vec'], truth['mass'], soft=0.1)
  41. for n in np.linspace(10, 1000, 100):
  42. # Load the data
  43. data = utils.loadData(name, int(n*factor))
  44. # Find the deviation
  45. time.append(data['t'])
  46. # Energies per unit mass
  47. energy = calculatePotentialEnergy(data['r_vec'], data['v_vec'], data['mass'], soft=0.1)
  48. errors.append(np.abs(energy - truthEnergy)/np.abs(truthEnergy))
  49. # Plot
  50. errors = savgol_filter(errors, 11, 3) # Smooth
  51. ax.plot(time, errors, color=color, label=label)
  52. # Styling
  53. utils.stylizePlot([ax])
  54. utils.setAxes(ax, x='Time', y='Relative energy error',
  55. xcoords=(.85,-0.08), ycoords=(-0.1,.75))
  56. ax.set_yscale('log')
  57. ax.set_xlim(0, time[-1])
  58. ax.set_ylim(1E-4, None)
  59. ax.axvline(x=3.55, linestyle='--', linewidth=1, color='black')
  60. plt.legend(loc='upper left')
  61. plt.show()