Skip to content

Commit 6a3f7cd

Browse files
committed
Support arbitrary spherically symmetric density profiles. Release v0.1.3
1 parent deefe43 commit 6a3f7cd

6 files changed

Lines changed: 150 additions & 9 deletions

File tree

Project.toml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
name = "AstroIC"
22
uuid = "5b3ca070-48c3-41ea-b469-89825dd01fde"
33
authors = ["islent <[email protected]>"]
4-
version = "0.1.2"
4+
version = "0.1.3"
55

66
[deps]
77
AstroIO = "c85a633c-0c3f-44a2-bffe-7f9d0681b3e7"
88
AstroLib = "c7932e45-9af1-51e7-9da9-f004cd3a462b"
99
AstroSimBase = "c6a34d98-f626-4d8d-b450-a3f124eaada6"
1010
BangBang = "198e06fe-97b7-11e9-32a5-e1d131e6ad66"
11+
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
1112
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
1213
Dierckx = "39dd38d3-220a-591b-8e3c-4c3a8c710a94"
1314
Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f"
@@ -29,13 +30,14 @@ AstroIO = "0.1"
2930
AstroLib = "0.4"
3031
AstroSimBase = "0.1"
3132
BangBang = "0.3, 0.4, 0.5"
33+
DataFrames = "1"
3234
Dierckx = "0.5"
3335
Distributions = "0.25"
3436
DocStringExtensions = "0.8, 0.9"
35-
Optim = "1.9.4"
37+
Optim = "1"
3638
PhysicalConstants = "0.2"
3739
PhysicalParticles = "1"
38-
Plots = "1.40.8"
40+
Plots = "1"
3941
PrecompileTools = "1"
4042
QuadGK = "2"
4143
Reexport = "1"

src/AstroIC.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ using PrecompileTools
66
using DocStringExtensions
77
using Reexport
88
using Unitful, UnitfulAstro
9+
using DataFrames
910
using Distributions
1011
using Random
1112
using BangBang
@@ -42,6 +43,7 @@ export
4243
GasCloud,
4344
ExponentialDisc,
4445
Bulge,
46+
SphericalSystem,
4547

4648
solarsystem,
4749

@@ -64,6 +66,7 @@ include("distribution.jl")
6466
include("plummer.jl")
6567
include("disk.jl")
6668
include("bulge.jl")
69+
include("spherical.jl")
6770
include("gascloud.jl")
6871
include("solarsystem.jl")
6972

src/distribution.jl

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -90,21 +90,49 @@ $(TYPEDSIGNATURES)
9090
9191
Sample from the discrete pdf using rejection method
9292
93+
!!! attentions
94+
- Data must be unitless, and have no NaN
95+
- `x` should start from 0
96+
- the maximum of `pdf_array` would be the width of sampling box
97+
"""
98+
function rejection_sampling(x, pdf_array, rMax, NumSamples)
99+
spl = Spline1D(ustrip.(x), ustrip.(pdf_array))
100+
pdf_maximum = ustrip(maximum(pdf_array))
101+
102+
R = eltype(rMax)[]
103+
sizehint!(R, NumSamples)
104+
105+
while length(R) < NumSamples
106+
R_rand = rand() * rMax
107+
if rand() < spl(ustrip(R_rand)) / pdf_maximum
108+
push!(R, R_rand)
109+
end
110+
end
111+
112+
return R
113+
end
114+
115+
"""
116+
$(TYPEDSIGNATURES)
117+
118+
Sample from the discrete pdf using rejection method
119+
93120
!!! attentions
94121
- Data must be unitless, and have no NaN
95122
- `x` should start from 0
96123
- the maximum of `pdf` would be the width of sampling box
97124
"""
98-
function rejection_sampling(x, pdf, rMax, NumSamples)
99-
spl = Spline1D(x, pdf)
100-
pdf_maximum = maximum(pdf)
125+
function rejection_sampling(pdf::Function, rMax, NumSamples)
126+
x = collect(LinRange(rMax/10000, rMax, 10000))
127+
pdf_array = pdf.(x)
128+
pdf_maximum = maximum(pdf_array)
101129

102-
R = eltype(pdf)[]
130+
R = eltype(rMax)[]
103131
sizehint!(R, NumSamples)
104132

105133
while length(R) < NumSamples
106134
R_rand = rand() * rMax
107-
if rand() < spl(R_rand) / pdf_maximum
135+
if rand() < pdf(R_rand) / pdf_maximum
108136
push!(R, R_rand)
109137
end
110138
end

src/spherical.jl

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
"""
2+
$(TYPEDEF)
3+
$(TYPEDFIELDS)
4+
5+
`mass_shell` support two types:
6+
- `Dict` or `DataFrames`: discrete radii `:r` and shell masses `:m`
7+
- `function`: a radial function of shell mass
8+
"""
9+
struct SphericalSystem{I} <: InitialConditionConfig
10+
collection::Collection
11+
NumSamples::I
12+
13+
mass_shell
14+
end
15+
16+
function Base.show(io::IO, config::SphericalSystem)
17+
print(io,
18+
"Config of spherical system Initial Conditions:",
19+
"\n Particle Collection: ", config.collection,
20+
"\n Number of Samples: ", config.NumSamples,
21+
)
22+
end
23+
24+
"""
25+
$(TYPEDSIGNATURES)
26+
27+
"""
28+
function generate(config::SphericalSystem, units = uAstro;
29+
MaxRadius = 0.0u"kpc",
30+
)
31+
uLen = getuLength(units)
32+
uMass = getuMass(units)
33+
uVel = getuVel(units)
34+
NumSamples = config.NumSamples
35+
36+
if iszero(MaxRadius)
37+
error("Keyword `MaxRadius` should not be zero")
38+
end
39+
40+
if config.mass_shell isa Function
41+
R = rejection_sampling(config.mass_shell, MaxRadius, NumSamples)
42+
elseif config.mass_shell isa Dict
43+
if haskey(config.mass_shell, "r") && haskey(config.mass_shell, "m")
44+
R = rejection_sampling(config.mass_shell["r"], config.mass_shell["m"], MaxRadius, NumSamples)
45+
else
46+
error("`mass_shell` of `SphericalSystem` must be `Dict` or `DataFrame` (with fields `:r`, `:m`) or `Function`")
47+
end
48+
elseif config.mass_shell isa DataFrame
49+
if hasproperty(config.mass_shell, :r) && hasproperty(config.mass_shell, :m)
50+
R = rejection_sampling(config.mass_shell.r, config.mass_shell.m, MaxRadius, NumSamples)
51+
else
52+
error("`mass_shell` of `SphericalSystem` must be `Dict` or `DataFrame` (with fields `:r`, `:m`) or `Function`")
53+
end
54+
else
55+
error("`mass_shell` of `SphericalSystem` must be `Dict` or `DataFrame` (with fields `:r`, `:m`) or `Function`")
56+
end
57+
58+
# Compute total mass
59+
x = collect(LinRange(MaxRadius/10000, MaxRadius, 10000))
60+
dx = x[2] - x[1]
61+
if config.mass_shell isa Function
62+
shell_masses = config.mass_shell.(x)
63+
TotalMass = PhysicalParticles.NumericalIntegration.integrate(x, shell_masses) * ustrip(uLen, dx)
64+
elseif config.mass_shell isa Dict || config.mass_shell isa DataFrame
65+
spl = Spline1D(ustrip.(uLen, config.mass_shell["r"]), ustrip.(uMass/uLen, config.mass_shell["m"]))
66+
TotalMass = PhysicalParticles.NumericalIntegration.integrate(x, spl(ustrip.(uLen, x))*uMass/uLen) * ustrip(uLen, dx)
67+
end
68+
69+
pos = rand_pos_3d.(R)
70+
71+
#TODO: vel
72+
vel = [PVector(uVel) for i in 1:NumSamples]
73+
74+
# Packing
75+
particles = StructArray(Star(units, id = i) for i in 1:NumSamples)
76+
assign_particles(particles, :Pos, pos)
77+
assign_particles(particles, :Vel, vel)
78+
79+
Mmean = TotalMass / NumSamples
80+
assign_particles(particles, :Mass, Mmean)
81+
82+
return particles
83+
end

test/runtests.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ using Test
33
using Unitful, UnitfulAstro
44

55
using AstroSimBase
6-
using AstroIO, PhysicalParticles
6+
using PhysicalParticles
7+
using AstroIO
78

89
using Dates
910
using AstroIC
@@ -12,4 +13,5 @@ include("tools.jl")
1213
include("plummer.jl")
1314
include("disk.jl")
1415
include("bulge.jl")
16+
include("spherical.jl")
1517
include("solarsystem.jl")

test/spherical.jl

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# using AstroPlot
2+
3+
@testset "SphericalSystem" begin
4+
r_s = 2.0u"kpc"
5+
MaxRadius = 10*r_s
6+
NumSamples = 1000
7+
8+
r = collect(LinRange(0.001u"kpc", MaxRadius, 1000))
9+
dr = r[2] - r[1]
10+
11+
shell_mass_func = x->1.0e8u"Msun/kpc^3"/(x/r_s)/(1+x/r_s)^2 * 4π * x^2
12+
13+
config = SphericalSystem(STAR, NumSamples, shell_mass_func)
14+
particles = generate(config; MaxRadius)
15+
# plot_makie(particles)
16+
17+
18+
shell_mass = shell_mass_func.(r)
19+
20+
config = SphericalSystem(STAR, NumSamples, Dict("r"=>r, "m"=>shell_mass))
21+
particles = generate(config; MaxRadius)
22+
# plot_makie(particles)
23+
end

0 commit comments

Comments
 (0)