def make_planets(central_particle, masses, radii, density=3 | units.g / units.cm**3, phi=None, theta=None, eccentricity=0.0, kepler=None, rng=None): volumes = masses / density planet_radii = (3.0 * volumes / (4.0 * numpy.pi))**(1.0 / 3.0) n = len(masses) planet_particles = Particles(n) planet_particles.semimajor_axis = radii if eccentricity is None: eccentricity = numpy.abs(rng.normal(-0.00001, 0.00001, n)) planet_particles.eccentricity = eccentricity planet_particles.mass = masses planet_particles.radius = planet_radii if phi is None: phi = numpy.radians(rng.uniform(0.0, 90.0, 1)[0]) #rotate under x if theta is None: theta0 = numpy.radians((rng.normal(-90.0, 90.0, 1)[0])) #rotate under y theta0 = 0 theta_inclination = numpy.radians(rng.normal(0, 1.0, n)) theta_inclination[0] = 0 theta = theta0 + theta_inclination #psi = numpy.radians(rng.uniform(0, 180, 1))[0] #0 # numpy.radians(90) # numpy.radians(rng.uniform(0, 180, 1))[0] psi = numpy.radians(rng.uniform(0.0, 180.0, 1))[ 0] #0 # numpy.radians(90) # numpy.radians(rng.uniform(0, 180, 1))[0] com_particle = central_particle.copy() for x, t in zip(iter(planet_particles), theta): pos, vel = posvel_from_orbital_elements(com_particle.mass + x.mass, x.semimajor_axis, x.eccentricity, kepler, rng) pos, vel = rotate(pos, vel, 0, 0, psi) # theta and phi in radians pos, vel = rotate(pos, vel, 0, t, 0) # theta and phi in radians pos, vel = rotate(pos, vel, phi, 0, 0) # theta and phi in radians x.position = pos + com_particle.position x.velocity = vel + com_particle.velocity if False: two_body = Particles(particles=[com_particle, x]) print "dp:", (com_particle.position - two_body.center_of_mass()).as_quantity_in(units.AU) com_particle.mass = two_body.mass.sum() com_particle.position = two_body.center_of_mass() com_particle.velocity = two_body.center_of_mass_velocity() #planet_particles.position += central_particle.position #planet_particles.velocity += central_particle.velocity return planet_particles
def test8(self): print( "Testing adding and removing particles from stellar evolution code..." ) instance = BSE() instance.initialize_code() stars = Particles(6) stars.mass = [1.0, 1.0, 1.0, 0.2, 0.2, 0.2] | units.MSun binaries = Particles(3) binaries.eccentricity = 0.0 for i in range(3): binaries[i].child1 = stars[i] binaries[i].child2 = stars[i + 3] orbital_period = 200.0 | units.day semi_major_axis = instance.orbital_period_to_semi_major_axis( orbital_period, binaries.child1.as_set().mass, binaries.child2.as_set().mass) binaries.semi_major_axis = semi_major_axis instance.commit_parameters() self.assertEqual(len(instance.particles), 0) self.assertEqual(len(instance.binaries), 0) # before creation instance.particles.add_particles(stars) instance.binaries.add_particles(binaries[:-1]) instance.commit_particles() instance.evolve_model(1.0 | units.Myr) self.assertEqual(len(instance.binaries), 2) # before remove self.assertAlmostEqual(instance.binaries.age, 1.0 | units.Myr) instance.binaries.remove_particle(binaries[0]) self.assertEqual(len(instance.binaries), 1) instance.evolve_model(2.0 | units.Myr) self.assertAlmostEqual(instance.binaries[0].age, 2.0 | units.Myr) instance.binaries.add_particles(binaries[::2]) self.assertEqual(len(instance.binaries), 3) # it's back... self.assertAlmostEqual(instance.binaries[0].age, 2.0 | units.Myr) self.assertAlmostEqual(instance.binaries[1].age, 0.0 | units.Myr) self.assertAlmostEqual(instance.binaries[2].age, 0.0 | units.Myr) # ... and rejuvenated. instance.evolve_model( 3.0 | units.Myr ) # The young stars keep their age offset from the old star self.assertAlmostEqual(instance.binaries.age, [3.0, 1.0, 1.0] | units.Myr) instance.evolve_model(4.0 | units.Myr) self.assertAlmostEqual(instance.binaries.age, [4.0, 2.0, 2.0] | units.Myr) instance.stop()
def test7(self): print "Test evolve_model optional arguments: end_time and keep_synchronous" instance = BSE() instance.commit_parameters() stars = Particles(6) stars.mass = [1.0, 2.0, 3.0, 0.1, 0.2, 0.3] | units.MSun binaries = Particles(3) binaries.eccentricity = 0.0 for i in range(3): binaries[i].child1 = stars[i] binaries[i].child2 = stars[i + 3] orbital_period = 200.0 | units.day semi_major_axis = instance.orbital_period_to_semi_major_axis( orbital_period, binaries.child1.as_set().mass, binaries.child2.as_set().mass) binaries.semi_major_axis = semi_major_axis instance.particles.add_particles(stars) instance.binaries.add_particles(binaries) self.assertAlmostEqual(instance.binaries.age, [0.0, 0.0, 0.0] | units.yr) self.assertAlmostEqual(instance.binaries.time_step, [550.1565, 58.2081, 18.8768] | units.Myr, 3) print "evolve_model without arguments: use shared timestep = min(particles.time_step)" instance.evolve_model() self.assertAlmostEqual(instance.binaries.age, [18.8768, 18.8768, 18.8768] | units.Myr, 3) self.assertAlmostEqual(instance.binaries.time_step, [550.1565, 58.2081, 18.8768] | units.Myr, 3) self.assertAlmostEqual(instance.model_time, 18.8768 | units.Myr, 3) print "evolve_model with end_time: take timesteps, until end_time is reached exactly" instance.evolve_model(100 | units.Myr) self.assertAlmostEqual(instance.binaries.age, [100.0, 100.0, 100.0] | units.Myr, 3) self.assertAlmostEqual(instance.binaries.time_step, [550.1565, 58.2081, 18.8768] | units.Myr, 3) self.assertAlmostEqual(instance.model_time, 100.0 | units.Myr, 3) print "evolve_model with keep_synchronous: use non-shared timestep, particle ages will typically diverge" instance.evolve_model(keep_synchronous=False) self.assertAlmostEqual(instance.binaries.age, (100 | units.Myr) + ([550.1565, 58.2081, 18.8768] | units.Myr), 3) self.assertAlmostEqual(instance.binaries.time_step, [550.1565, 58.2081, 18.8768] | units.Myr, 3) self.assertAlmostEqual(instance.model_time, 100.0 | units.Myr, 3) # Unchanged! instance.stop()
def get_bodies_in_orbit(m0, m_ffp, m_bp, a_bp, e_bp, phi_bp, lan_bp, b_ffp, r_inf): #Bodies bodies = Particles() ##Get BP in orbit #Binary star_planet = new_binary_from_orbital_elements(m0, m_bp, a_bp, e_bp, true_anomaly=phi_bp, inclination = 28, longitude_of_the_ascending_node = lan_bp) #Planet attributes star_planet.eccentricity = e_bp star_planet.semimajoraxis = a_bp #Center on the star star_planet.position -= star_planet[0].position star_planet.velocity -= star_planet[0].velocity cm_p = star_planet.center_of_mass() cm_v = star_planet.center_of_mass_velocity() ##Get FFP in orbit #Particle set m0_ffp = Particles(2) #Zeros and parabolic velocity zero_p = 0.0 | nbody_system.length zero_v = 0.0 | nbody_system.speed parabolic_velocity = get_parabolic_velocity(m0, m_ffp, b_ffp, r_inf, m_bp, a_bp, phi_bp) #Central star m0_ffp[0].mass = m0 m0_ffp[0].position = (zero_p,zero_p,zero_p) m0_ffp[0].velocity = (zero_v,zero_v,zero_v) #Free-floating planet m0_ffp[1].mass = m_ffp m0_ffp[1].position = (-r_inf-cm_p[0],-b_ffp-cm_p[1],zero_p) m0_ffp[1].velocity = (parabolic_velocity,zero_v,zero_v) #Orbital Elements star_planet_as_one = Particles(1) star_planet_as_one.mass = m0 + m_bp star_planet_as_one.position = cm_p star_planet_as_one.velocity = cm_v binary = [star_planet_as_one[0], m0_ffp[1]] m1, m2, sma, e = my_orbital_elements_from_binary(binary) #For the star it sets the initial values of semimajoraxis and eccentricity of the ffp around star+bp m0_ffp.eccentricity = e m0_ffp.semimajoraxis = sma #Order: star, ffp, bp bodies.add_particle(m0_ffp[0]) bodies.add_particle(m0_ffp[1]) bodies.add_particle(star_planet[1]) return bodies
def test8(self): print "Testing adding and removing particles from stellar evolution code..." instance = MOBSE() instance.initialize_code() stars = Particles(6) stars.mass = [1.0,1.0, 1.0, 0.2, 0.2, 0.2] | units.MSun binaries = Particles(3) binaries.eccentricity = 0.0 for i in range(3): binaries[i].child1 = stars[i] binaries[i].child2 = stars[i+3] orbital_period = 200.0 | units.day semi_major_axis = instance.orbital_period_to_semi_major_axis( orbital_period, binaries.child1.as_set().mass , binaries.child2.as_set().mass ) binaries.semi_major_axis = semi_major_axis instance.commit_parameters() self.assertEquals(len(instance.particles), 0) self.assertEquals(len(instance.binaries), 0) # before creation instance.particles.add_particles(stars) instance.binaries.add_particles(binaries[:-1]) instance.commit_particles() instance.evolve_model(1.0 | units.Myr) self.assertEquals(len(instance.binaries), 2) # before remove self.assertAlmostEqual(instance.binaries.age, 1.0 | units.Myr) instance.binaries.remove_particle(binaries[0]) self.assertEquals(len(instance.binaries), 1) instance.evolve_model(2.0 | units.Myr) self.assertAlmostEqual(instance.binaries[0].age, 2.0 | units.Myr) instance.binaries.add_particles(binaries[::2]) self.assertEquals(len(instance.binaries), 3) # it's back... self.assertAlmostEqual(instance.binaries[0].age, 2.0 | units.Myr) self.assertAlmostEqual(instance.binaries[1].age, 0.0 | units.Myr) self.assertAlmostEqual(instance.binaries[2].age, 0.0 | units.Myr) # ... and rejuvenated. instance.evolve_model(3.0 | units.Myr) # The young stars keep their age offset from the old star self.assertAlmostEqual(instance.binaries.age, [3.0, 1.0, 1.0] | units.Myr) instance.evolve_model(4.0 | units.Myr) self.assertAlmostEqual(instance.binaries.age, [4.0, 2.0, 2.0] | units.Myr) instance.stop()
def test7(self): print "Test evolve_model optional arguments: end_time and keep_synchronous" instance = MOBSE() instance.commit_parameters() stars = Particles(6) stars.mass = [1.0,2.0,3.0, 0.1, 0.2, 0.3] | units.MSun binaries = Particles(3) binaries.eccentricity = 0.0 for i in range(3): binaries[i].child1 = stars[i] binaries[i].child2 = stars[i+3] orbital_period = 200.0 | units.day semi_major_axis = instance.orbital_period_to_semi_major_axis( orbital_period, binaries.child1.as_set().mass , binaries.child2.as_set().mass ) binaries.semi_major_axis = semi_major_axis instance.particles.add_particles(stars) instance.binaries.add_particles(binaries) self.assertAlmostEqual(instance.binaries.age, [0.0, 0.0, 0.0] | units.yr) self.assertAlmostEqual(instance.binaries.time_step, [550.1565, 58.2081, 18.8768] | units.Myr, 3) print "evolve_model without arguments: use shared timestep = min(particles.time_step)" instance.evolve_model() self.assertAlmostEqual(instance.binaries.age, [18.8768, 18.8768, 18.8768] | units.Myr, 3) self.assertAlmostEqual(instance.binaries.time_step, [550.1565, 58.2081, 18.8768] | units.Myr, 3) self.assertAlmostEqual(instance.model_time, 18.8768 | units.Myr, 3) print "evolve_model with end_time: take timesteps, until end_time is reached exactly" instance.evolve_model(100 | units.Myr) self.assertAlmostEqual(instance.binaries.age, [100.0, 100.0, 100.0] | units.Myr, 3) self.assertAlmostEqual(instance.binaries.time_step, [550.1565, 58.2081, 18.8785] | units.Myr, 3) self.assertAlmostEqual(instance.model_time, 100.0 | units.Myr, 3) print "evolve_model with keep_synchronous: use non-shared timestep, particle ages will typically diverge" instance.evolve_model(keep_synchronous = False) self.assertAlmostEqual(instance.binaries.age, (100 | units.Myr) + ([550.1565, 58.2081, 18.8785] | units.Myr), 3) self.assertAlmostEqual(instance.binaries.time_step, [550.1565, 58.2081, 18.8785] | units.Myr, 3) self.assertAlmostEqual(instance.model_time, 100.0 | units.Myr, 3) # Unchanged! instance.stop()
def make_planets(central_particle, masses, radii, density = 3 | units.g/units.cm**3, phi=None, theta=None, eccentricity = 0.0, kepler = None, rng = None): volumes = masses / density planet_radii = (3.0 * volumes / (4.0 * numpy.pi))**(1.0/3.0) n = len(masses) planet_particles = Particles(n) planet_particles.semimajor_axis = radii if eccentricity is None: eccentricity = numpy.abs(rng.normal(-0.00001,0.00001,n)) planet_particles.eccentricity = eccentricity planet_particles.mass = masses planet_particles.radius = planet_radii if phi is None: phi = numpy.radians(rng.uniform(0.0, 90.0, 1)[0])#rotate under x if theta is None: theta0 = numpy.radians((rng.normal(-90.0,90.0,1)[0]))#rotate under y theta0 = 0 theta_inclination = numpy.radians(rng.normal(0, 1.0, n )) theta_inclination[0] = 0 theta = theta0 + theta_inclination #psi = numpy.radians(rng.uniform(0, 180, 1))[0] #0 # numpy.radians(90) # numpy.radians(rng.uniform(0, 180, 1))[0] psi = numpy.radians(rng.uniform(0.0, 180.0, 1))[0] #0 # numpy.radians(90) # numpy.radians(rng.uniform(0, 180, 1))[0] com_particle = central_particle.copy() for x, t in zip(iter(planet_particles), theta): pos,vel = posvel_from_orbital_elements(com_particle.mass + x.mass, x.semimajor_axis, x.eccentricity, kepler, rng) pos,vel = rotate(pos, vel, 0, 0, psi) # theta and phi in radians pos,vel = rotate(pos, vel, 0, t, 0) # theta and phi in radians pos,vel = rotate(pos, vel, phi, 0, 0) # theta and phi in radians x.position = pos + com_particle.position x.velocity = vel + com_particle.velocity if False: two_body = Particles(particles=[com_particle, x]) print "dp:", (com_particle.position - two_body.center_of_mass()).as_quantity_in(units.AU) com_particle.mass = two_body.mass.sum() com_particle.position = two_body.center_of_mass() com_particle.velocity = two_body.center_of_mass_velocity() #planet_particles.position += central_particle.position #planet_particles.velocity += central_particle.velocity return planet_particles
def test9(self): print "Testing BSE states" instance = BSE() stars = Particles(2) stars.mass = [1.0, 0.2] | units.MSun binaries = Particles(1) orbital_period = 200.0 | units.day semi_major_axis = instance.orbital_period_to_semi_major_axis( orbital_period, stars[0].mass, stars[1].mass) binaries.semi_major_axis = semi_major_axis binaries.eccentricity = 0.0 binaries[0].child1 = stars[0] binaries[0].child2 = stars[1] print "First do everything manually:", self.assertEquals(instance.get_name_of_current_state(), 'UNINITIALIZED') instance.initialize_code() self.assertEquals(instance.get_name_of_current_state(), 'INITIALIZED') instance.commit_parameters() self.assertEquals(instance.get_name_of_current_state(), 'RUN') instance.cleanup_code() self.assertEquals(instance.get_name_of_current_state(), 'END') instance.stop() print "ok" print "initialize_code(), commit_parameters(), " \ "and cleanup_code() should be called automatically:", instance = BSE() self.assertEquals(instance.get_name_of_current_state(), 'UNINITIALIZED') instance.parameters.reimers_mass_loss_coefficient = 0.5 self.assertEquals(instance.get_name_of_current_state(), 'INITIALIZED') instance.particles.add_particles(stars) instance.binaries.add_particles(binaries) self.assertEquals(instance.get_name_of_current_state(), 'RUN') instance.stop() self.assertEquals(instance.get_name_of_current_state(), 'STOPPED') print "ok"
def test9(self): print "Testing MOBSE states" instance = MOBSE() stars = Particles(2) stars.mass = [1.0, 0.2] | units.MSun binaries = Particles(1) orbital_period = 200.0 | units.day semi_major_axis = instance.orbital_period_to_semi_major_axis(orbital_period, stars[0].mass , stars[1].mass) binaries.semi_major_axis = semi_major_axis binaries.eccentricity = 0.0 binaries[0].child1 = stars[0] binaries[0].child2 = stars[1] print "First do everything manually:", self.assertEquals(instance.get_name_of_current_state(), 'UNINITIALIZED') instance.initialize_code() self.assertEquals(instance.get_name_of_current_state(), 'INITIALIZED') instance.commit_parameters() self.assertEquals(instance.get_name_of_current_state(), 'RUN') instance.cleanup_code() self.assertEquals(instance.get_name_of_current_state(), 'END') instance.stop() print "ok" print "initialize_code(), commit_parameters(), " \ "and cleanup_code() should be called automatically:", instance = MOBSE() self.assertEquals(instance.get_name_of_current_state(), 'UNINITIALIZED') instance.parameters.reimers_mass_loss_coefficient = 0.5 self.assertEquals(instance.get_name_of_current_state(), 'INITIALIZED') instance.particles.add_particles(stars) instance.binaries.add_particles(binaries) self.assertEquals(instance.get_name_of_current_state(), 'RUN') instance.stop() self.assertEquals(instance.get_name_of_current_state(), 'STOPPED') print "ok"
def get_ffp_in_orbit(m0, m_ffp, b, r_inf, parabolic_velocity): m0_and_ffp_in_orbit = Particles(2) zero_p = 0.0 | nbody_system.length zero_v = 0.0 | nbody_system.speed #Central star m0_and_ffp_in_orbit[0].mass = m0 m0_and_ffp_in_orbit[0].position = (zero_p,zero_p,zero_p) m0_and_ffp_in_orbit[0].velocity = (zero_v,zero_v,zero_v) #Free-floating planet m0_and_ffp_in_orbit[1].mass = m_ffp m0_and_ffp_in_orbit[1].position = (-r_inf,-b,zero_p) m0_and_ffp_in_orbit[1].velocity = (parabolic_velocity,zero_v,zero_v) m1, m2, sma, e, ta, i, lan, ap = orbital_elements_from_binary(m0_and_ffp_in_orbit) #For the star it sets the initial values of semimajoraxis and eccentricity of the ffp m0_and_ffp_in_orbit.eccentricity = e m0_and_ffp_in_orbit.semimajoraxis = sma return m0_and_ffp_in_orbit
def new_binary_distribution( primary_mass, secondary_mass=None, binaries=None, min_mass=0.08 | units.MSun, ): """ Takes primary masses, and returns a set of stars and a set of binaries formed by these stars. Secondary masses are given by a uniform random mass ratio with the primary masses. If optional secondary masses are given, these are used instead. binaries is an optional particleset used for the binary pairs, with given positions and velocities. Other parameters are ignored. """ N = len(primary_mass) if binaries is None: binaries = Particles(N) # Should give some position/velocity as well? elif len(binaries) != N: print("binaries must be None or have the same lenght as primary_mass") return -1 if secondary_mass is None: # Now, we need to specify the mass ratio in the binaries. # A flat distribution seems to be OK. mass_ratio = uniform(N) # This gives us the secondaries' masses secondary_mass = mass_ratio * primary_mass # secondaries are min_mass at least secondary_mass = numpy.maximum(secondary_mass, min_mass) elif len(secondary_mass) != N: print("Number of secondaries is unequal to number of primaries!") return -1 else: # Make sure primary_mass is the larger of the two, and secondary_mass # the smaller. pm = primary_mass.maximum(secondary_mass) sm = primary_mass.minimum(secondary_mass) primary_mass = pm secondary_mass = sm del(pm, sm) # Now, we need to calculate the semi-major axes for the binaries. Since the # observed quantity is orbital periods, we start from there. mean_log_orbital_period = 5 # 10log of the period in days, (Duchene&Kraus) sigma_log_orbital_period = 2.3 orbital_period = numpy.random.lognormal( size=N, mean=numpy.log(10) * mean_log_orbital_period, sigma=numpy.log(10) * sigma_log_orbital_period, ) | units.day # We need the masses to calculate the corresponding semi-major axes. semi_major_axis = orbital_period_to_semi_major_axis( orbital_period, primary_mass, secondary_mass, ) # Eccentricity: square root of random value eccentricity = numpy.sqrt(random(N)) # Other orbital elements at random inclination = pi * random(N) | units.rad true_anomaly = 2 * pi * random(N) | units.rad longitude_of_the_ascending_node = 2 * pi * random(N) | units.rad argument_of_periapsis = 2 * pi * random(N) | units.rad primaries, secondaries = generate_binaries( primary_mass, secondary_mass, semi_major_axis, eccentricity=eccentricity, inclination=inclination, true_anomaly=true_anomaly, longitude_of_the_ascending_node=longitude_of_the_ascending_node, argument_of_periapsis=argument_of_periapsis, G=constants.G, ) stars = Particles() primaries.position += binaries.position secondaries.position += binaries.position primaries.velocity += binaries.velocity secondaries.velocity += binaries.velocity primaries = stars.add_particles(primaries) secondaries = stars.add_particles(secondaries) binaries.eccentricity = eccentricity binaries.semi_major_axis = semi_major_axis for i in range(len(primaries)): binaries[i].child1 = primaries[i] binaries[i].child2 = secondaries[i] # Probably needed binaries[i].mass = primaries[i].mass + secondaries[i].mass return stars, binaries
def get_bodies_in_orbit(m0, m_ffp, m_bp, a_bp, e_bp, phi_bp, inc_bp, lan_bp, b_ffp, r_inf): #Bodies bodies = Particles() ##Get BP in orbit #Binary star_planet = new_binary_from_orbital_elements(m0, m_bp, a_bp, e_bp, true_anomaly=phi_bp, inclination = inc_bp, longitude_of_the_ascending_node = lan_bp) #Planet attributes star_planet.eccentricity = e_bp star_planet.semimajoraxis = a_bp #Center on the star star_planet.position -= star_planet[0].position star_planet.velocity -= star_planet[0].velocity cm_p = star_planet.center_of_mass() cm_v = star_planet.center_of_mass_velocity() ##Get FFP in orbit #Particle set m0_ffp = Particles(2) #Zeros and parabolic velocity zero_p = 0.0 | nbody_system.length zero_v = 0.0 | nbody_system.speed parabolic_velocity = get_parabolic_velocity(m0, m_bp, b_ffp, r_inf) #Central star m0_ffp[0].mass = m0 m0_ffp[0].position = (zero_p,zero_p,zero_p) m0_ffp[0].velocity = (zero_v,zero_v,zero_v) #Free-floating planet m0_ffp[1].mass = m_ffp m0_ffp[1].position = (-r_inf+cm_p[0], b_ffp+cm_p[1], cm_p[2]) m0_ffp[1].velocity = (parabolic_velocity+cm_v[0], cm_v[1], cm_v[2]) #To find the orbital period of the BP G = (1.0 | nbody_system.length**3 * nbody_system.time**-2 * nbody_system.mass**-1) orbital_period_bp = 2*math.pi*((a_bp**3)/(G*m0)).sqrt() #To find the distance and time to periastron kep = Kepler() kep.initialize_code() star_planet_as_one = Particles(1) star_planet_as_one.mass = m0 + m_bp star_planet_as_one.position = cm_p star_planet_as_one.velocity = cm_v kepler_bodies = Particles() kepler_bodies.add_particle(star_planet_as_one[0]) kepler_bodies.add_particle(m0_ffp[1]) kep.initialize_from_particles(kepler_bodies) kep.advance_to_periastron() time_pericenter = kep.get_time() kep.stop() binary = [star_planet_as_one[0], m0_ffp[1]] sma, e, inclination, long_asc_node, arg_per = my_orbital_elements_from_binary(binary) m0_ffp.eccentricity = e m0_ffp.semimajoraxis = sma #Adding bodies. Order: star, ffp, bp bodies.add_particle(m0_ffp[0]) bodies.add_particle(m0_ffp[1]) bodies.add_particle(star_planet[1]) return bodies, time_pericenter, orbital_period_bp