コード例 #1
0
 def test2(self):
     print "Test basic particle attributes and scale_to_standard - SI units"
     convert_nbody = nbody_system.nbody_to_si(1 | units.MSun, 1 | units.parsec)
     particles = Particles(2)
     particles.position = [[-1, 0, 0], [1,0,0]] | units.parsec
     particles.velocity = [[-1, 0, 0], [1,0,0]] | units.parsec / units.Myr
     particles.mass = 0.5 | units.MSun 
     
     self.assertAlmostRelativeEquals(particles.total_mass(), 1.0 | units.MSun)
     self.assertAlmostRelativeEquals(particles.kinetic_energy(), 1.0 * (0.5 | units.MSun) * (1 |units.parsec / units.Myr) **2 )
     self.assertAlmostRelativeEquals(particles.potential_energy(), -constants.G *  (0.5 | units.MSun) ** 2  / ([2,0,0] | units.parsec).length() )
     self.assertAlmostRelativeEquals(particles.virial_radius(), 4.0 | units.parsec)
     
     particles.scale_to_standard(convert_nbody)
     self.assertAlmostRelativeEquals(particles.total_mass(), convert_nbody.to_si(1.0 | nbody_system.mass))
     self.assertAlmostRelativeEquals(particles.kinetic_energy(), convert_nbody.to_si(0.25 | nbody_system.energy))
     self.assertAlmostRelativeEquals(particles.potential_energy().as_quantity_in(units.J), convert_nbody.to_si(-0.5 | nbody_system.energy).as_quantity_in(units.J), 12)
     self.assertAlmostRelativeEquals(particles.virial_radius(), convert_nbody.to_si(1.0 | nbody_system.length))
     
     particles.scale_to_standard(convert_nbody, virial_ratio=1) # unbound
     self.assertAlmostRelativeEquals(particles.kinetic_energy(), 0.5 * constants.G * (1 | units.MSun**2 / units.parsec), 13)
     self.assertAlmostRelativeEquals(particles.potential_energy(), -0.5 * constants.G * (1 | units.MSun**2 / units.parsec))
     particles.scale_to_standard(convert_nbody, virial_ratio=0) # velocities zeroed
     self.assertAlmostRelativeEquals(particles.kinetic_energy(), 0 | units.J)
     self.assertAlmostRelativeEquals(particles.potential_energy(), -0.5 * constants.G * (1 | units.MSun**2 / units.parsec))
コード例 #2
0
    def test2(self):
        print(
            "Test basic particle attributes and scale_to_standard - SI units")
        convert_nbody = nbody_system.nbody_to_si(1 | units.MSun,
                                                 1 | units.parsec)
        particles = Particles(2)
        particles.position = [[-1, 0, 0], [1, 0, 0]] | units.parsec
        particles.velocity = [[-1, 0, 0], [1, 0, 0]] | units.parsec / units.Myr
        particles.mass = 0.5 | units.MSun

        self.assertAlmostRelativeEquals(particles.total_mass(),
                                        1.0 | units.MSun)
        self.assertAlmostRelativeEquals(
            particles.kinetic_energy(),
            1.0 * (0.5 | units.MSun) * (1 | units.parsec / units.Myr)**2)
        self.assertAlmostRelativeEquals(
            particles.potential_energy(), -constants.G *
            (0.5 | units.MSun)**2 / ([2, 0, 0] | units.parsec).length())
        self.assertAlmostRelativeEquals(particles.virial_radius(),
                                        4.0 | units.parsec)

        particles.scale_to_standard(convert_nbody)
        self.assertAlmostRelativeEquals(
            particles.total_mass(),
            convert_nbody.to_si(1.0 | nbody_system.mass))
        self.assertAlmostRelativeEquals(
            particles.kinetic_energy(),
            convert_nbody.to_si(0.25 | nbody_system.energy))
        self.assertAlmostRelativeEquals(
            particles.potential_energy().as_quantity_in(units.J),
            convert_nbody.to_si(-0.5 | nbody_system.energy).as_quantity_in(
                units.J), 12)
        self.assertAlmostRelativeEquals(
            particles.virial_radius(),
            convert_nbody.to_si(1.0 | nbody_system.length))

        particles.scale_to_standard(convert_nbody, virial_ratio=1)  # unbound
        self.assertAlmostRelativeEquals(
            particles.kinetic_energy(),
            0.5 * constants.G * (1 | units.MSun**2 / units.parsec), 13)
        self.assertAlmostRelativeEquals(
            particles.potential_energy(),
            -0.5 * constants.G * (1 | units.MSun**2 / units.parsec))
        particles.scale_to_standard(convert_nbody,
                                    virial_ratio=0)  # velocities zeroed
        self.assertAlmostRelativeEquals(particles.kinetic_energy(),
                                        0 | units.J)
        self.assertAlmostRelativeEquals(
            particles.potential_energy(),
            -0.5 * constants.G * (1 | units.MSun**2 / units.parsec))
コード例 #3
0
    def test1(self):
        print(
            "Test basic particle attributes and scale_to_standard - nbody units"
        )
        particles = Particles(2)
        particles.position = [[-1, 0, 0], [1, 0, 0]] | nbody_system.length
        particles.velocity = [[-1, 0, 0], [1, 0, 0]
                              ] | nbody_system.length / nbody_system.time
        particles.mass = 0.4 | nbody_system.mass
        self.assertAlmostRelativeEquals(particles.total_mass(),
                                        0.8 | nbody_system.mass)
        self.assertAlmostRelativeEquals(particles.kinetic_energy(),
                                        0.4 | nbody_system.energy)
        self.assertAlmostRelativeEquals(
            particles.potential_energy(G=nbody_system.G),
            -0.08 | nbody_system.energy)
        self.assertAlmostRelativeEquals(particles.virial_radius(),
                                        4.0 | nbody_system.length)
        particles.scale_to_standard()
        self.assertAlmostRelativeEquals(particles.total_mass(),
                                        1.0 | nbody_system.mass)
        self.assertAlmostRelativeEquals(particles.kinetic_energy(),
                                        0.25 | nbody_system.energy)
        self.assertAlmostRelativeEquals(
            particles.potential_energy(G=nbody_system.G),
            -0.5 | nbody_system.energy)
        self.assertAlmostRelativeEquals(particles.virial_radius(),
                                        1.0 | nbody_system.length)

        particles.scale_to_standard(virial_ratio=1)  # unbound
        self.assertAlmostRelativeEquals(particles.kinetic_energy(),
                                        0.5 | nbody_system.energy)
        self.assertAlmostRelativeEquals(
            particles.potential_energy(G=nbody_system.G),
            -0.5 | nbody_system.energy)
        particles.scale_to_standard(virial_ratio=0)  # velocities zeroed
        self.assertAlmostRelativeEquals(particles.kinetic_energy(),
                                        0 | nbody_system.energy)
        self.assertAlmostRelativeEquals(
            particles.potential_energy(G=nbody_system.G),
            -0.5 | nbody_system.energy)
コード例 #4
0
 def test1(self):
     print "Test basic particle attributes and scale_to_standard - nbody units"
     particles = Particles(2)
     particles.position = [[-1, 0, 0], [1,0,0]] | nbody_system.length
     particles.velocity = [[-1, 0, 0], [1,0,0]] | nbody_system.length/nbody_system.time
     particles.mass = 0.4 | nbody_system.mass
     self.assertAlmostRelativeEquals(particles.total_mass(), 0.8 | nbody_system.mass)
     self.assertAlmostRelativeEquals(particles.kinetic_energy(), 0.4 | nbody_system.energy)
     self.assertAlmostRelativeEquals(particles.potential_energy(G=nbody_system.G), -0.08 | nbody_system.energy)
     self.assertAlmostRelativeEquals(particles.virial_radius(), 4.0 | nbody_system.length)
     particles.scale_to_standard()
     self.assertAlmostRelativeEquals(particles.total_mass(), 1.0 | nbody_system.mass)
     self.assertAlmostRelativeEquals(particles.kinetic_energy(), 0.25 | nbody_system.energy)
     self.assertAlmostRelativeEquals(particles.potential_energy(G=nbody_system.G), -0.5 | nbody_system.energy)
     self.assertAlmostRelativeEquals(particles.virial_radius(), 1.0 | nbody_system.length)
     
     particles.scale_to_standard(virial_ratio=1) # unbound
     self.assertAlmostRelativeEquals(particles.kinetic_energy(), 0.5 | nbody_system.energy)
     self.assertAlmostRelativeEquals(particles.potential_energy(G=nbody_system.G), -0.5 | nbody_system.energy)
     particles.scale_to_standard(virial_ratio=0) # velocities zeroed
     self.assertAlmostRelativeEquals(particles.kinetic_energy(), 0 | nbody_system.energy)
     self.assertAlmostRelativeEquals(particles.potential_energy(G=nbody_system.G), -0.5 | nbody_system.energy)
コード例 #5
0
ファイル: test_sticky_spheres.py プロジェクト: Ingwar/amuse
 def test2(self):
     colliders = Particles(2)
     colliders.mass = [5, 5] | units.kg
     colliders.position = [[0.0, 0.0, 0.0], [1.0, 1.0, 1.0]] | units.m
     colliders.velocity = [[0.0, 0.0, 0.0], [2.0, 2.0, 2.0]] | units.m / units.s
     
     merged = StickySpheres(mass_loss=0.2).handle_collision(colliders[0], colliders[1])
     self.assertTrue(isinstance(merged, Particles))
     self.assertEqual(merged.mass, 8 | units.kg)
     self.assertAlmostEqual(merged.position, [0.5, 0.5, 0.5] | units.m)
     self.assertAlmostEqual(merged.velocity, [1.0, 1.0, 1.0] | units.m / units.s)
     copy = colliders.copy()
     copy.move_to_center()
     self.assertAlmostEqual(colliders.kinetic_energy(), merged.as_set().kinetic_energy() / 0.8 + copy.kinetic_energy())
コード例 #6
0
 def test2(self):
     colliders = Particles(2)
     colliders.mass = [5, 5] | units.kg
     colliders.position = [[0.0, 0.0, 0.0], [1.0, 1.0, 1.0]] | units.m
     colliders.velocity = [[0.0, 0.0, 0.0], [2.0, 2.0, 2.0]] | units.m / units.s
     
     merged = StickySpheres(mass_loss=0.2).handle_collision(colliders[0], colliders[1])
     self.assertTrue(isinstance(merged, Particles))
     self.assertEqual(merged.mass, 8 | units.kg)
     self.assertAlmostEqual(merged.position, [0.5, 0.5, 0.5] | units.m)
     self.assertAlmostEqual(merged.velocity, [1.0, 1.0, 1.0] | units.m / units.s)
     copy = colliders.copy()
     copy.move_to_center()
     self.assertAlmostEqual(colliders.kinetic_energy(), merged.as_set().kinetic_energy() / 0.8 + copy.kinetic_energy())
コード例 #7
0
ファイル: test_sticky_spheres.py プロジェクト: Ingwar/amuse
 def test1(self):
     colliders = Particles(2)
     colliders.mass = [5, 2] | units.kg
     colliders.position = [[0.0, 0.0, 0.0], [0.7, 1.4, -0.35]] | units.m
     colliders.velocity = [[0.4, -0.6, 0.0], [0.0, 0.0, -3.0]] | units.m / units.s
     self.assertAlmostEqual(colliders.center_of_mass_velocity().length(), 1.0 | units.m / units.s)
     
     merged = StickySpheres().handle_collision(colliders[0], colliders[1])
     self.assertTrue(isinstance(merged, Particles))
     self.assertEqual(merged.mass, 7 | units.kg)
     self.assertAlmostEqual(merged.position, [0.2, 0.4, -0.1] | units.m)
     self.assertAlmostEqual(merged.velocity, ([2.0, -3.0, -6.0] | units.m / units.s) / 7.0)
     self.assertAlmostEqual(merged.velocity.length(), 1.0 | units.m / units.s)
     copy = colliders.copy()
     copy.move_to_center()
     self.assertAlmostEqual(colliders.kinetic_energy(), merged.as_set().kinetic_energy() + copy.kinetic_energy())
コード例 #8
0
 def test1(self):
     colliders = Particles(2)
     colliders.mass = [5, 2] | units.kg
     colliders.position = [[0.0, 0.0, 0.0], [0.7, 1.4, -0.35]] | units.m
     colliders.velocity = [[0.4, -0.6, 0.0], [0.0, 0.0, -3.0]] | units.m / units.s
     self.assertAlmostEqual(colliders.center_of_mass_velocity().length(), 1.0 | units.m / units.s)
     
     merged = StickySpheres().handle_collision(colliders[0], colliders[1])
     self.assertTrue(isinstance(merged, Particles))
     self.assertEqual(merged.mass, 7 | units.kg)
     self.assertAlmostEqual(merged.position, [0.2, 0.4, -0.1] | units.m)
     self.assertAlmostEqual(merged.velocity, ([2.0, -3.0, -6.0] | units.m / units.s) / 7.0)
     self.assertAlmostEqual(merged.velocity.length(), 1.0 | units.m / units.s)
     copy = colliders.copy()
     copy.move_to_center()
     self.assertAlmostEqual(colliders.kinetic_energy(), merged.as_set().kinetic_energy() + copy.kinetic_energy())
コード例 #9
0
class GravityCodeForTesting(object):
    def __init__(self):
        self.particles = Particles()
        self.model_time = quantities.zero

    def evolve_model(self, t_end):
        self.particles.position += self.particles.velocity * (t_end -
                                                              self.model_time)
        self.model_time = t_end

    @property
    def potential_energy(self):
        G = nbody_system.G if self.particles.x.unit == nbody_system.length else constants.G
        return self.particles.potential_energy(G=G)

    @property
    def kinetic_energy(self):
        return self.particles.kinetic_energy()
コード例 #10
0
ファイル: test_fastkick.py プロジェクト: Ingwar/amuse
class GravityCodeForTesting(object):
    
    def __init__(self):
        self.particles = Particles()
        self.model_time = quantities.zero
    
    def evolve_model(self, t_end):
        self.particles.position += self.particles.velocity * (t_end - self.model_time)
        self.model_time = t_end
    
    @property
    def potential_energy(self):
        G = nbody_system.G if self.particles.x.unit == nbody_system.length else constants.G
        return self.particles.potential_energy(G=G)
    
    @property
    def kinetic_energy(self):
        return self.particles.kinetic_energy()
コード例 #11
0
class ExampleGravityCodeInterface(object):
    
    def __init__(self, softening_mode="shared"):
        self.particles = Particles()
        if softening_mode == "individual":
            self.softening_mode = "individual"
            self._softening_lengths_squared = self._softening_lengths_squared_individual
            self._softening_lengths = self._softening_lengths_individual
        else:
            self.softening_mode = "shared"
            self._softening_lengths_squared = self._softening_lengths_squared_shared
            self._softening_lengths = self._softening_lengths_shared
            epsilon_squared_parameter = parameters.ModuleMethodParameterDefinition(
                "get_epsilon_squared",
                "set_epsilon_squared",
                "epsilon_squared",
                "gravitational softening length squared",
                default_value = 0.0 | nbody_system.length**2,
                must_set_before_get = False
            )
            self.parameters = parameters.new_parameters_instance_with_docs([epsilon_squared_parameter], self)
            self.epsilon_squared = 0.0 | nbody_system.length**2
    
    def _softening_lengths_squared_individual(self):
        return self.particles.radius**2
    def _softening_lengths_squared_shared(self):
        return self.epsilon_squared.as_vector_with_length(len(self.particles))
    
    def _softening_lengths_individual(self):
        return self.particles.radius
    def _softening_lengths_shared(self):
        return self.epsilon_squared.sqrt().as_vector_with_length(len(self.particles))
    
    def initialize_code(self):
        self.model_time = 0 | units.Myr
    
    def get_potential_at_point(self, eps, x ,y, z):
        if isinstance(x, VectorQuantity):
            return -constants.G * (self.particles.mass.reshape((-1,1)) / 
                (self._softening_lengths_squared().reshape((-1,1)) + eps.reshape((1,-1))**2 + (self.particles.x.reshape((-1,1)) - 
                x.reshape((1,-1)))**2 + (self.particles.y.reshape((-1,1)) - y.reshape((1,-1)))**2 + 
                (self.particles.z.reshape((-1,1)) - z.reshape((1,-1)))**2).sqrt()).sum(axis=0)
        return -constants.G * (self.particles.mass / (self._softening_lengths_squared() + eps**2 + (self.particles.x - x)**2 + 
            (self.particles.y - y)**2 + (self.particles.z - z)**2).sqrt()).sum()
    
    def get_gravity_at_point(self, eps, x ,y, z):
        if isinstance(x, VectorQuantity):
            delta_x = x.reshape((1,-1)) - self.particles.x.reshape((-1,1))
            delta_y = y.reshape((1,-1)) - self.particles.y.reshape((-1,1))
            delta_z = z.reshape((1,-1)) - self.particles.z.reshape((-1,1))
            factor = -constants.G * (self.particles.mass.reshape((-1,1)) / 
                (self._softening_lengths_squared().reshape((-1,1)) + eps.reshape((1,-1))**2 + 
                delta_x**2 + delta_y**2 + delta_z**2)**1.5)
            return (factor*delta_x).sum(axis=0), (factor*delta_y).sum(axis=0), (factor*delta_z).sum(axis=0)
        delta_x = self.particles.x - x
        delta_y = self.particles.y - y
        delta_z = self.particles.z - z
        factor = -constants.G * (self.particles.mass / 
            (self._softening_lengths_squared() + eps**2 + delta_x**2 + delta_y**2 + delta_z**2)**1.5)
        return (factor*delta_x).sum(), (factor*delta_y).sum(), (factor*delta_z).sum()
    
    @property
    def potential_energy(self):
        if self.softening_mode == "individual":
            if len(self.particles) < 2:
                return zero * constants.G
            eps_vector = self.particles.radius**2
            sum_of_energies = zero
            for i in range(len(self.particles) - 1):
                dx = self.particles[i].x - self.particles[i+1:].x
                dy = self.particles[i].y - self.particles[i+1:].y
                dz = self.particles[i].z - self.particles[i+1:].z
                dr = ((dx * dx) + (dy * dy) + (dz * dz) + eps_vector[i] + eps_vector[i+1:]).sqrt()
                sum_of_energies -= (self.particles.mass[i] * self.particles.mass[i+1:] / dr).sum()
            return constants.G * sum_of_energies
        return self.particles.potential_energy(smoothing_length_squared=2*self.epsilon_squared)
    
    def before_set_parameter(self):
        pass
        
    def before_get_parameter(self):
        pass
        
    @property
    def kinetic_energy(self):
        return self.particles.kinetic_energy()
    
    def get_epsilon_squared(self):
        return self.epsilon_squared
    
    def set_epsilon_squared(self, epsilon_squared):
        self.epsilon_squared = epsilon_squared
    
    def commit_particles(self):
        self.set_accelerations()
        self.set_next_timestep()
    
    def set_accelerations(self):
        accelerations = self.get_gravity_at_point(self._softening_lengths(), 
            self.particles.x, self.particles.y, self.particles.z)
        self.particles.ax = accelerations[0]
        self.particles.ay = accelerations[1]
        self.particles.az = accelerations[2]
    
    def set_next_timestep(self):
        self.next_timestep = min([0.01 * (self.particles.velocity / 
            self.particles.acceleration).lengths_squared().amin().sqrt(), 1 | units.yr])
    
    def evolve_model(self, t_end):
        while self.model_time < t_end:
            dt = self.next_timestep
            self.particles.position += self.particles.velocity * dt + 0.5 * self.particles.acceleration * dt**2
            old_acceleration = self.particles.acceleration
            self.set_accelerations()
            self.particles.velocity += 0.5 * (old_acceleration + self.particles.acceleration) * dt
            self.model_time += dt
            self.set_next_timestep()
コード例 #12
0
ファイル: test_bridge.py プロジェクト: vdhelm/amuse
class ExampleGravityCodeInterface(object):
    def __init__(self, softening_mode="shared"):
        self.particles = Particles()
        if softening_mode == "individual":
            self.softening_mode = "individual"
            self._softening_lengths_squared = self._softening_lengths_squared_individual
            self._softening_lengths = self._softening_lengths_individual
        else:
            self.softening_mode = "shared"
            self._softening_lengths_squared = self._softening_lengths_squared_shared
            self._softening_lengths = self._softening_lengths_shared
            epsilon_squared_parameter = parameters.ModuleMethodParameterDefinition(
                "get_epsilon_squared",
                "set_epsilon_squared",
                "epsilon_squared",
                "gravitational softening length squared",
                default_value=0.0 | nbody_system.length ** 2,
                must_set_before_get=False,
            )
            self.parameters = parameters.new_parameters_instance_with_docs([epsilon_squared_parameter], self)
            self.epsilon_squared = 0.0 | nbody_system.length ** 2

    def _softening_lengths_squared_individual(self):
        return self.particles.radius ** 2

    def _softening_lengths_squared_shared(self):
        return self.epsilon_squared.as_vector_with_length(len(self.particles))

    def _softening_lengths_individual(self):
        return self.particles.radius

    def _softening_lengths_shared(self):
        return self.epsilon_squared.sqrt().as_vector_with_length(len(self.particles))

    def initialize_code(self):
        self.model_time = 0 | units.Myr

    def get_potential_at_point(self, eps, x, y, z):
        if isinstance(x, VectorQuantity):
            return -constants.G * (
                self.particles.mass.reshape((-1, 1))
                / (
                    self._softening_lengths_squared().reshape((-1, 1))
                    + eps.reshape((1, -1)) ** 2
                    + (self.particles.x.reshape((-1, 1)) - x.reshape((1, -1))) ** 2
                    + (self.particles.y.reshape((-1, 1)) - y.reshape((1, -1))) ** 2
                    + (self.particles.z.reshape((-1, 1)) - z.reshape((1, -1))) ** 2
                ).sqrt()
            ).sum(axis=0)
        return (
            -constants.G
            * (
                self.particles.mass
                / (
                    self._softening_lengths_squared()
                    + eps ** 2
                    + (self.particles.x - x) ** 2
                    + (self.particles.y - y) ** 2
                    + (self.particles.z - z) ** 2
                ).sqrt()
            ).sum()
        )

    def get_gravity_at_point(self, eps, x, y, z):
        if isinstance(x, VectorQuantity):
            delta_x = x.reshape((1, -1)) - self.particles.x.reshape((-1, 1))
            delta_y = y.reshape((1, -1)) - self.particles.y.reshape((-1, 1))
            delta_z = z.reshape((1, -1)) - self.particles.z.reshape((-1, 1))
            factor = -constants.G * (
                self.particles.mass.reshape((-1, 1))
                / (
                    self._softening_lengths_squared().reshape((-1, 1))
                    + eps.reshape((1, -1)) ** 2
                    + delta_x ** 2
                    + delta_y ** 2
                    + delta_z ** 2
                )
                ** 1.5
            )
            return (factor * delta_x).sum(axis=0), (factor * delta_y).sum(axis=0), (factor * delta_z).sum(axis=0)
        delta_x = self.particles.x - x
        delta_y = self.particles.y - y
        delta_z = self.particles.z - z
        factor = -constants.G * (
            self.particles.mass
            / (self._softening_lengths_squared() + eps ** 2 + delta_x ** 2 + delta_y ** 2 + delta_z ** 2) ** 1.5
        )
        return (factor * delta_x).sum(), (factor * delta_y).sum(), (factor * delta_z).sum()

    @property
    def potential_energy(self):
        if self.softening_mode == "individual":
            if len(self.particles) < 2:
                return zero * constants.G
            eps_vector = self.particles.radius ** 2
            sum_of_energies = zero
            for i in range(len(self.particles) - 1):
                dx = self.particles[i].x - self.particles[i + 1 :].x
                dy = self.particles[i].y - self.particles[i + 1 :].y
                dz = self.particles[i].z - self.particles[i + 1 :].z
                dr = ((dx * dx) + (dy * dy) + (dz * dz) + eps_vector[i] + eps_vector[i + 1 :]).sqrt()
                sum_of_energies -= (self.particles.mass[i] * self.particles.mass[i + 1 :] / dr).sum()
            return constants.G * sum_of_energies
        return self.particles.potential_energy(smoothing_length_squared=2 * self.epsilon_squared)

    def before_set_parameter(self):
        pass

    def before_get_parameter(self):
        pass

    @property
    def kinetic_energy(self):
        return self.particles.kinetic_energy()

    def get_epsilon_squared(self):
        return self.epsilon_squared

    def set_epsilon_squared(self, epsilon_squared):
        self.epsilon_squared = epsilon_squared

    def commit_particles(self):
        self.set_accelerations()
        self.set_next_timestep()

    def set_accelerations(self):
        accelerations = self.get_gravity_at_point(
            self._softening_lengths(), self.particles.x, self.particles.y, self.particles.z
        )
        self.particles.ax = accelerations[0]
        self.particles.ay = accelerations[1]
        self.particles.az = accelerations[2]

    def set_next_timestep(self):
        self.next_timestep = min(
            [
                0.01 * (self.particles.velocity / self.particles.acceleration).lengths_squared().amin().sqrt(),
                1 | units.yr,
            ]
        )

    def evolve_model(self, t_end):
        while self.model_time < t_end:
            dt = self.next_timestep
            self.particles.position += self.particles.velocity * dt + 0.5 * self.particles.acceleration * dt ** 2
            old_acceleration = self.particles.acceleration
            self.set_accelerations()
            self.particles.velocity += 0.5 * (old_acceleration + self.particles.acceleration) * dt
            self.model_time += dt
            self.set_next_timestep()