|
1 | | -import sys |
2 | | -import numpy |
3 | | -from matplotlib import pyplot |
4 | | -from amuse.lab import * |
5 | | -from amuse.plot import plot |
6 | | -from amuse.ext.sph_to_star import convert_SPH_to_stellar_model |
7 | | -from amuse.examples.prepare_figure import * |
8 | | -from amuse.examples.distinct_colours import get_distinct |
| 1 | +""" |
| 2 | +Example to merge two stars using SPH |
| 3 | +""" |
| 4 | +import argparse |
| 5 | +import numpy as np |
| 6 | +import matplotlib.pyplot as plt |
| 7 | + |
| 8 | +from amuse.units import units, constants, nbody_system |
| 9 | +from amuse.datamodel import Particle, ParticlesSuperset |
| 10 | +from amuse.community.evtwin import Evtwin |
| 11 | +from amuse.community.gadget2 import Gadget2 |
| 12 | +from amuse.ext.star_to_sph import convert_stellar_model_to_SPH |
| 13 | + |
9 | 14 |
|
10 | 15 | def return_evolved_star_hydro(mass, time, Nsph): |
11 | | - star = Particle(mass=mass) |
12 | | - stellar = EVtwin() |
| 16 | + star = Particle(mass=mass) |
| 17 | + stellar = Evtwin() |
13 | 18 | star = stellar.particles.add_particle(star) |
14 | 19 | stellar.evolve_model(time) |
15 | 20 | Nsph = Nsph * int(mass.value_in(units.MSun)) |
16 | 21 | star_in_sph = convert_stellar_model_to_SPH(star, Nsph).gas_particles |
17 | 22 | stellar.stop() |
18 | 23 | return star_in_sph |
19 | 24 |
|
20 | | -def merge_two_stars_sph(Mprim, Msec, t_coll, Nsph, opening_angle): |
21 | | - primary_in_sph = return_evolved_star_hydro(Mprim, t_coll, |
22 | | - int(Nsph*Mprim/(1.|units.MSun))) |
23 | | -# primary_in_sph = relax_sph_realization(primary_in_sph) |
24 | | - secondary_in_sph = return_evolved_star_hydro(Msec, t_coll, |
25 | | - int(Nsph*Msec/(1.|units.MSun))) |
26 | | -# secondary_in_sph = relax_sph_realization(secondary_in_sph) |
27 | | - R = primary_in_sph.x.max() + secondary_in_sph.x.max() |
28 | | - M = primary_in_sph.mass.sum() + secondary_in_sph.mass.sum() |
29 | | - secondary_in_sph.x += 0.8*R |
30 | | - secondary_in_sph.y += 0.6*R |
31 | | - secondary_in_sph.vx -= (constants.G*M/R).sqrt() |
32 | | - |
33 | | - converter=nbody_system.nbody_to_si(Mprim, 1.0|units.AU) |
| 25 | + |
| 26 | +def merge_two_stars_sph( |
| 27 | + mass_primary, |
| 28 | + mass_secondary, |
| 29 | + time_collision, |
| 30 | + Nsph, |
| 31 | + opening_angle, |
| 32 | +): |
| 33 | + primary_in_sph = return_evolved_star_hydro( |
| 34 | + mass_primary, time_collision, int(Nsph*mass_primary/(1. | units.MSun)) |
| 35 | + ) |
| 36 | + # primary_in_sph = relax_sph_realization(primary_in_sph) |
| 37 | + secondary_in_sph = return_evolved_star_hydro( |
| 38 | + mass_secondary, |
| 39 | + time_collision, |
| 40 | + int(Nsph*mass_secondary/(1. | units.MSun)) |
| 41 | + ) |
| 42 | + # secondary_in_sph = relax_sph_realization(secondary_in_sph) |
| 43 | + radius = primary_in_sph.x.max() + secondary_in_sph.x.max() |
| 44 | + total_mass = primary_in_sph.mass.sum() + secondary_in_sph.mass.sum() |
| 45 | + secondary_in_sph.x += 0.8*radius |
| 46 | + secondary_in_sph.y += 0.6*radius |
| 47 | + secondary_in_sph.vx -= (constants.G*total_mass/radius).sqrt() |
| 48 | + |
| 49 | + converter = nbody_system.nbody_to_si(mass_primary, 1.0 | units.au) |
34 | 50 | hydro = Gadget2(converter, number_of_workers=4) |
35 | | -# hydro = Fi(converter) |
36 | 51 | hydro.parameters.opening_angle = opening_angle |
37 | 52 |
|
38 | | - """ |
39 | | - print "Opening criterion:", hydro.parameters.opening_angle |
40 | | - print "Opening criterion:", hydro.get_gdgop() |
41 | | - print "Opening criterion:", hydro.parameters.opening_angle |
42 | | - print "Opening criterion:", hydro.get_gdgop() |
43 | | - """ |
44 | | - |
| 53 | + # print("Opening criterion:", hydro.parameters.opening_angle) |
| 54 | + # print("Opening criterion:", hydro.get_gdgop()) |
| 55 | + # print("Opening criterion:", hydro.parameters.opening_angle) |
| 56 | + # print("Opening criterion:", hydro.get_gdgop()) |
| 57 | + |
45 | 58 | hydro.gas_particles.add_particles(primary_in_sph) |
46 | 59 | hydro.gas_particles.add_particles(secondary_in_sph) |
47 | | - hydro.evolve_model(2.0|units.hour) |
| 60 | + hydro.evolve_model(2.0 | units.hour) |
48 | 61 | hydro.gas_particles.new_channel_to(primary_in_sph).copy() |
49 | 62 | hydro.gas_particles.new_channel_to(secondary_in_sph).copy() |
50 | 63 | hydro.stop() |
51 | 64 | return primary_in_sph, secondary_in_sph |
52 | 65 |
|
53 | | -def relax_sph_realization(sph_star): |
54 | 66 |
|
| 67 | +def relax_sph_realization(sph_star): |
55 | 68 | dynamical_timescale = sph_star.dynamical_timescale() |
56 | | - converter = nbody_system.nbody_to_si(dynamical_timescale, 1|units.RSun) |
| 69 | + converter = nbody_system.nbody_to_si(dynamical_timescale, 1 | units.RSun) |
57 | 70 | hydro = Gadget2(converter, number_of_workers=2) |
58 | 71 | hydro.gas_particles.add_particles(sph_star) |
59 | 72 |
|
60 | | - to_hydro = sph_star.new_channel_to(hydro.gas_particles) |
| 73 | + # to_hydro = sph_star.new_channel_to(hydro.gas_particles) |
61 | 74 | to_framework = hydro.gas_particles.new_channel_to(sph_star) |
62 | 75 |
|
63 | 76 | ts_factor = 2.5 |
64 | 77 | t_end = ts_factor * sph_star.dynamical_timescale(mass_fraction=0.9) |
65 | 78 | n_steps = ts_factor * 100 |
66 | | - velocity_damp_factor = 1.0 - (ts_factor*2*numpy.pi)/n_steps |
| 79 | + velocity_damp_factor = 1.0 - (ts_factor*2*np.pi)/n_steps |
67 | 80 | dt = t_end/float(n_steps) |
68 | | - time = 0|units.day |
| 81 | + time = 0 | units.day |
69 | 82 | while time < t_end: |
70 | 83 | time += dt |
71 | 84 | hydro.evolve_model(time) |
72 | | - hydro.gas_particles.velocity = velocity_damp_factor * hydro.gas_particles.velocity |
| 85 | + hydro.gas_particles.velocity = ( |
| 86 | + velocity_damp_factor |
| 87 | + * hydro.gas_particles.velocity |
| 88 | + ) |
73 | 89 | to_framework.copy() |
74 | 90 | hydro.stop() |
75 | 91 | return sph_star |
76 | 92 |
|
77 | | -def merge_and_plot_distribution(Mprim, Msec, t_coll, Nsph, opening_angle, c, label): |
78 | | - p, s = merge_two_stars_sph(Mprim, Msec, t_coll, Nsph, opening_angle) |
| 93 | + |
| 94 | +def merge_and_plot_distribution( |
| 95 | + mass_primary, |
| 96 | + mass_secondary, |
| 97 | + time_collision, |
| 98 | + Nsph, |
| 99 | + opening_angle, |
| 100 | + c=None, |
| 101 | + label=None |
| 102 | +): |
| 103 | + p, s = merge_two_stars_sph( |
| 104 | + mass_primary, |
| 105 | + mass_secondary, |
| 106 | + time_collision, |
| 107 | + Nsph, |
| 108 | + opening_angle, |
| 109 | + ) |
79 | 110 | merger = ParticlesSuperset([p, s]) |
80 | 111 | com = merger.center_of_mass() |
81 | | - merger.r = ((merger.x-com[0])**2 + (merger.y-com[1])**2 + (merger.z-com[2])**2).sqrt() |
| 112 | + merger.r = ( |
| 113 | + (merger.x-com[0])**2 |
| 114 | + + (merger.y-com[1])**2 |
| 115 | + + (merger.z-com[2])**2 |
| 116 | + ).sqrt() |
82 | 117 | merger = merger.sorted_by_attributes("r") |
83 | 118 | n = [] |
84 | 119 | m = 0 |
85 | | - mi = 1.0*(Mprim+Msec).value_in(units.MSun)/len(merger) |
| 120 | + mi = 1.0*(mass_primary+mass_secondary).value_in(units.MSun)/len(merger) |
86 | 121 | for i in range(len(merger.r)): |
87 | 122 | m += mi |
88 | 123 | n.append(m) |
89 | | - pyplot.plot(merger.r.value_in(units.RSun), n, c=c, label=label) |
90 | | - |
91 | | -def new_option_parser(): |
92 | | - from amuse.units.optparse import OptionParser |
93 | | - result = OptionParser() |
94 | | - result.add_option("-M", unit=units.MSun, |
95 | | - dest="Mprim", type="float",default = 10|units.MSun, |
96 | | - help="Mass of the primary star [%default] MSun") |
97 | | - result.add_option("-m", unit=units.MSun, |
98 | | - dest="Msec", type="float",default = 1|units.MSun, |
99 | | - help="Mass of the secondary star [%default] MSun") |
100 | | - result.add_option("-N", |
101 | | - dest="Nsph", type="int",default = 100, |
102 | | - help="Number of sph particles per MSun [%default]") |
103 | | - result.add_option("-t", unit=units.Myr, |
104 | | - dest="t_coll", type="float", default = 0.01|units.Myr, |
105 | | - help="end time of the simulation [%default] Myr") |
106 | | - return result |
107 | | - |
108 | | -if __name__ in ('__main__', '__plot__'): |
109 | | - o, arguments = new_option_parser().parse_args() |
110 | | - |
111 | | - colors = get_distinct(4) |
112 | | - figure = pyplot.figure(figsize=(16, 12)) |
113 | | - pyplot.xlabel("R [R$_\odot$]") |
114 | | - pyplot.ylabel("$M_{<r}$") |
115 | | - ax = pyplot.gca() |
116 | | - ax.minorticks_on() # switch on the minor ticks |
| 124 | + plt.plot(merger.r.value_in(units.RSun), n, c=c, label=label) |
| 125 | + |
| 126 | + |
| 127 | +def new_argument_parser(): |
| 128 | + parser = argparse.ArgumentParser( |
| 129 | + formatter_class=argparse.ArgumentDefaultsHelpFormatter |
| 130 | + ) |
| 131 | + parser.add_argument( |
| 132 | + "-M", "--mass_primary", type=units.MSun, default=10 | units.MSun, |
| 133 | + help="Mass of the primary star" |
| 134 | + ) |
| 135 | + parser.add_argument( |
| 136 | + "-m", "--mass_secondary", type=units.MSun, default=1 | units.MSun, |
| 137 | + help="Mass of the secondary star" |
| 138 | + ) |
| 139 | + # specifying more numbers will result in more simulations |
| 140 | + parser.add_argument( |
| 141 | + "-N", "--number_of_sph_particles", type=int, nargs="+", |
| 142 | + default=[10, 100, 1000, 10000], |
| 143 | + help="number of sph particles" |
| 144 | + ) |
| 145 | + parser.add_argument( |
| 146 | + "-t", |
| 147 | + "--time_collision", |
| 148 | + type=units.Myr, |
| 149 | + default=0.01 | units.Myr, |
| 150 | + help="end time of the simulation" |
| 151 | + ) |
| 152 | + return parser |
| 153 | + |
| 154 | + |
| 155 | +def merge_two_stars_sph_convergence( |
| 156 | + mass_primary=10 | units.MSun, |
| 157 | + mass_secondary=1 | units.MSun, |
| 158 | + time_collision=0.01 | units.Myr, |
| 159 | + number_of_sph_particles=None, |
| 160 | +): |
| 161 | + if number_of_sph_particles is None: |
| 162 | + number_of_sph_particles = [10, 100, 1000, 10000] |
| 163 | + plt.figure(figsize=(8, 6)) |
| 164 | + plt.xlabel(r"R [R$_\odot$]") |
| 165 | + plt.ylabel("$M_{<r}$") |
| 166 | + ax = plt.gca() |
| 167 | + ax.minorticks_on() # switch on the minor ticks |
117 | 168 | ax.locator_params(nbins=3) |
118 | | - merge_and_plot_distribution(o.Mprim, o.Msec, o.t_coll, 10, 0.5, colors[0], "$N_{sph} = 10/M_\odot$") |
119 | | - merge_and_plot_distribution(o.Mprim, o.Msec, o.t_coll, 100, 0.5, colors[1], "$N_{sph} = 100/M_\odot$") |
120 | | - merge_and_plot_distribution(o.Mprim, o.Msec, o.t_coll, 1000, 0.5, colors[2], "$N_{sph} = 10^3/M_\odot$") |
121 | | - merge_and_plot_distribution(o.Mprim, o.Msec, o.t_coll, 10000, 0.5, colors[3], "$N_{sph} = 10^4/M_\odot$") |
122 | | -# pyplot.show() |
123 | | -# pyplot.semilogx() |
124 | | - pyplot.xlim(0, 20) |
125 | | - pyplot.ylim(0, 12) |
126 | | - pyplot.legend(loc=4, fontsize=24) |
127 | | - pyplot.savefig("stellar_merger_convergence") |
| 169 | + for n in number_of_sph_particles: |
| 170 | + merge_and_plot_distribution( |
| 171 | + mass_primary, mass_secondary, time_collision, n, 0.5, |
| 172 | + label=r"$N_{sph} = " + f"{n}" + r"/M_\odot$" |
| 173 | + ) |
| 174 | + plt.xlim(0, 20) |
| 175 | + plt.ylim(0, 12) |
| 176 | + plt.legend(loc=4, fontsize=24) |
| 177 | + plt.savefig("stellar_merger_convergence.pdf") |
| 178 | + |
| 179 | + |
| 180 | +def main(): |
| 181 | + args = new_argument_parser().parse_args() |
| 182 | + merge_two_stars_sph_convergence(**args.__dict__) |
| 183 | + |
| 184 | + |
| 185 | +if __name__ == "__main__": |
| 186 | + main() |
0 commit comments