def integrate(self, to_time=None): if self.__initialized is False: self.initialize() self.__initialized = True print(to_time, self.t_end) # Allocate dense output npts = int(np.floor((self.t_end - self.t_start) / self.h) + 1) # Initial state x = np.concatenate((self.particles.positions, self.particles.velocities)) # Vector of times sol_time = np.linspace(self.t_start, self.t_start + self.h * (npts - 1), npts) energy_init = self.calculate_energy() # Compute second step dxdt0 = ODE.ode_n_body_first_order(x, self.CONST_G, self.particles.masses) x = x + dxdt0 * self.h # Launch integration count = 2 for t in sol_time[count:]: dxdt = ODE.ode_n_body_first_order(x, self.CONST_G, self.particles.masses) # Advance step x += 0.5 * self.h * (3 * dxdt - dxdt0) # Update dxdt0 = dxdt self.particles.positions = x[0:self.particles.N * 3] self.particles.velocities = x[self.particles.N * 3:] self._t = t self.store_state() energy = self.calculate_energy() print(('t = %f, E/E0 = %g' % (self.t, np.abs(energy - energy_init) / energy_init))) count += 1 self.buf.close() return 0
def __initial_time_step(y0, dy0, G, masses, nbodies): p = 15 ########### ESTIMATE INITIAL STEP SIZE # Compute scaling # sc = abs(y0)*epsb # Evaluate function f0 = ODE.ode_n_body_second_order(y0, G, masses) d0 = max(abs(y0)) d1 = max(abs(f0)) if (d0 < 1e-5) or (d1 < 1e-5): dt0 = 1e-6 else: dt0 = 0.01 * (d0 / d1) # Perform one Euler step y1 = y0 + dt0 * dy0 dy1 = dy0 + dt0 * f0 # Call function f1 = ODE.ode_n_body_second_order(y1, G, masses) d2 = max(abs((f1 - f0))) / dt0 if max(d1, d2) <= 1e-15: dt1 = max([1e-6, dt0 * 1e-3]) else: dt1 = (0.01 / max([d1, d2]))**(1.0 / (p + 1)) dt = min([100 * dt0, dt1]) return dt
def integrate_numpy(self, to_time=None): if to_time is not None: self.t_end = to_time # Allocate dense output npts = int(np.floor((self.t_end - self.t_start) / self.h) + 1) # Initial state x = np.concatenate( (self._particles.positions, self._particles.velocities)) # Vector of times sol_time = np.linspace(self.t_start, self.t_start + self.h * (npts - 1), npts) energy_init = self.calculate_energy() # Launch integration count = 1 for t in sol_time[count:]: # Evaluate coefficients k1 = ODE.ode_n_body_first_order(x, self.CONST_G, self._particles.masses) k2 = ODE.ode_n_body_first_order(x + 0.5 * self.h * k1, self.CONST_G, self._particles.masses) k3 = ODE.ode_n_body_first_order(x + 0.5 * self.h * k2, self.CONST_G, self._particles.masses) k4 = ODE.ode_n_body_first_order(x + self.h * k3, self.CONST_G, self._particles.masses) # Advance the state x += (self.h * (k1 + 2 * k2 + 2 * k3 + k4) / 6.0) # Store step self.particles.positions = x[0:self._particles.N * 3] self.particles.velocities = x[self._particles.N * 3:] self._t = t self.store_state() energy = self.calculate_energy() print(('t = %f, E/E0 = %g' % (self.t, np.abs(energy - energy_init) / energy_init))) count += 1 self.buf.close() return 0
def integrate_numpy(self, to_time): # Some parameters epsb = self.tol # recommended 1e-9 fac = 0.25 # For fixed step integration, choose exponent = 0 # exponent = 0; exponent = 1. / 7 y0 = self.particles.positions dy0 = self.particles.velocities # Dimension of the system dim = len(y0) # Tolerance Predictor-Corrector tolpc = 1e-18 # Return Radau spacing [hs, nh] = self.__radau_spacing() ddy0 = ODE.ode_n_body_second_order(y0, self.CONST_G, self._particles.masses) # Initial time step self.h = self.__initial_time_step(y0, dy0, self.CONST_G, self._particles.masses, self.particles.N) # Initialize bs0 = np.zeros((nh - 1, dim)) bs = np.zeros((nh - 1, dim)) g = np.zeros((nh - 1, dim)) self._t = self.t_start E = np.zeros((nh - 1, dim)) ddys = np.zeros((nh, dim)) r = self.__compute_rs() c = self.__compute_cs() integrate = True if to_time is not None: self.t_end = to_time imode = 0 energy_init = self.calculate_energy() while integrate: # if self.t + self.h > self.t_end: # self.h = self.t_end - self.t # integrate = False # Advance one step and return: y, dy, ddy, self._t, dt, g, bs, E, bs0, istat, imode = self.gaus_radau15_step( y0, dy0, ddy0, ddys, self.h, self.t, self.t_end, nh, hs, bs0, bs, E, g, r, c, self.tol, exponent, fac, imode, self.CONST_G, self._particles.masses, self.particles.N) # Detect end of integration: if istat == 2: integrate = False # Update step y0 = y dy0 = dy ddy0 = ddy self.particles.positions = y self.particles.velocities = dy self.store_state() energy = self.calculate_energy() # print('t = %f, E/E0 = %g' % (self.t, np.abs(energy-energy_init)/energy_init)) self.buf.close() return 0
def gaus_radau15_step(self, y0, dy0, ddy0, ddys, dt, t, tf, nh, hs, bs0, bs, E, g, r, c, tol, exponent, fac, imode, G, masses, nbodies): istat = 0 while (True): # Variable number of iterations in PC for ipc in range(0, 12): ddys = ddys * 0 # Advance along the Radau sequence for ih in range(0, nh): # Estimate position and velocity with bs0 and current h y = self.__approx_pos(y0, dy0, ddy0, hs[ih], bs, dt) dy = self.__approx_vel(dy0, ddy0, hs[ih], bs, dt) # Evaluate force function and store ddys[ih, :] = ODE.ode_n_body_second_order( y, self.CONST_G, self._particles.masses) g = self.__compute_gs(g, r, ddys, ih) bs = self.__compute_bs_from_gs(bs, g, ih, c) # Estimate convergence of PC db6 = bs[-1, :] - bs0[-1, :] if (max(abs(db6)) / max(abs(ddys[-1, :])) < 1e-16): break bs0 = bs # Advance the solution: y = self.__approx_pos(y0, dy0, ddy0, 1., bs, dt) dy = self.__approx_vel(dy0, ddy0, 1., bs, dt) ddy = ODE.ode_n_body_second_order(y, self.CONST_G, self.particles.masses) # Estimate relative error estim_b6 = max(abs(bs[-1, :])) / max(abs(ddy)) err = (estim_b6 / tol)**(exponent) # Step-size required for next step: dtreq = dt / err # Accept the step if (err <= 1): # Report accepted step: istat = 1 # Advance time: t = t + dt # Update b coefficients: bs0 = bs # Refine predictor-corrector coefficients for next pass: [bs, E] = self.__refine_bs(bs, dtreq / dt, E, imode) # Normal refine mode: imode = 1 # Check if tf was reached: if (t >= tf): istat = 2 # Step size for next iteration: if (dtreq / dt > 1.0 / fac): dt = dt / fac elif (dtreq < 1e-12): dt = dt * fac else: dt = dtreq # Correct overshooting: if (t + dt > tf): dt = tf - t # Return if the step was accepted: if (istat > 0): break return y, dy, ddy, t, dt, g, bs, E, bs0, istat, imode