Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add imf function #19

Merged
merged 4 commits into from
Jun 12, 2017
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@ Missing in AstroLib.jl
* `helio`
* `hor2eq`
* `imcontour`
* `imf`
* `planet_coords`
* `qdcb_grid`
* `tdb2tdt`
Expand Down
1 change: 1 addition & 0 deletions docs/src/ref.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ julia> AstroLib.planets["saturn"].mass
[`deredd()`](@ref),
[`flux2mag()`](@ref),
[`gal_uvw()`](@ref),
[`imf()`](@ref),
[`ismeuv()`](@ref),
[`kepler_solver()`](@ref),
[`lsf_rotate()`](@ref),
Expand Down
78 changes: 78 additions & 0 deletions src/imf.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# This file is a part of AstroLib.jl. License is MIT "Expat".

function _imf{T<:AbstractFloat}(mass::AbstractVector{T}, expon::AbstractVector{T},
mass_range::AbstractVector{T})
ne_comp = length(expon)
if length(mass_range) != ne_comp + 1
println("Length of array mass_range is not one more than that of expon")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IDL AstroLib throws an error here, instead of returning a vector of zeros. Why the different behavior?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The option of using exception handling completely slipped my mind. Too much worrying about keeping return-type stable ;-)

return zeros(mass)
end
integ = ones(T, ne_comp)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The initial value of integ doesn't look to be relevant. You can save some nanoseconds by initializing the vector with Vector{T}(ne_comp).

joint = ones(T, ne_comp)
norm = ones(T, ne_comp)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think you need to initialize the vector here, this line can be removed.

for i = 1:ne_comp
if expon[i] != -1
integ[i] = (mass_range[i+1]^(1 + expon[i]) - mass_range[i]^(1 + expon[i]))/
(1 + expon[i])
else
integ[i] = log(mass_range[i+1]/mass_range[i])
end
if i != 1
joint[i] = joint[i-1]*(mass_range[i]^(expon[i-1] - expon[i]))
end
end
norm = (1/sum_kbn(integ.*joint))*joint
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a particular reason why you used sum_kbn instead of the simpler sum? Did you find inaccurate results with it? Anyway, you can save an operation by using joint as numerator:

norm = joint ./ sum(integ .* joint)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought sum_kbn would give a significant boost in accuracy (as I was brought to believe from the docs). But apparently sum_kbn advantage was almost completely nerfed by JuliaLang/julia#4039. So yeah, using sum would be better.

Copy link
Member

@giordano giordano Jun 12, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sum_kbn should be indeed more accurate, for example in the case of elements spanning several orders of magnitude, but if this isn't the case we shouldn't worry too much ;-)

Anyway, I just realized that sum(integ .* joint) is simply the scalar product between the two vectors, so dot(integ, joint) is better. Sorry for not having noticed before.

psi = zeros(mass)
for i = 1:ne_comp
test = find(mass_range[i].< mass.<mass_range[i+1])
if length(test) !=0
psi[test] = norm[i].*(mass[test].^expon[i])
end
end
return psi
end

"""
imf(mass, expon, mass_range) -> psi

### Purpose ###

Compute an N-component power-law logarithmic initial mass function (IMF).

### Explanation ###

The function is normalized so that the total mass distribution equals
one solar mass.

### Arguments ###

* `mass`: mass in units of solar mass, vector.
* `expon`: power law exponent, vector. The number of values in expon equals
the number of different power-law components in the IMF.
* `mass_range`: vector containing the mass upper and lower limits of the
IMF and masses where the IMF exponent changes. The number of values in
mass_range should be one more than in expon. The values in mass_range
should be monotonically increasing and positive.

### Output ###

* `psi`: mass function, number of stars per unit logarithmic mass interval
evaluated for supplied masses.

### Example ###

```julia
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line should probably go below ;-)

Show the number of stars per unit mass interval at 3 Msun for a Salpeter
(expon = -1.35) IMF, with a mass range from 0.1 MSun to 110 Msun.

julia> imf([3], [-1.35], [0.1, 110]) / 3
1-element Array{Float64,1}:
0.0129414
```

### Notes ###

Code of this function is based on IDL Astronomy User's Library.
"""
imf(mass::AbstractVector{<:Real}, expon::AbstractVector{<:Real}, mass_range::AbstractVector{<:Real}) =
_imf(float(mass), float(expon), float(mass_range))
3 changes: 3 additions & 0 deletions src/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ export helio_jd
include("helio_rv.jl")
export helio_rv

include("imf.jl")
export imf

include("ismeuv.jl")
export ismeuv

Expand Down
9 changes: 9 additions & 0 deletions test/utils-tests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,15 @@ end
@test helio_rv.([0.1, 0.9], 0, 1, 0, 100, 0.6, 45) ≈
[-45.64994926111004, 89.7820347358485]

# Test imf
@testset "imf" begin
@test imf([5], [-6.75], [0.9]) == [0]
@test imf([0.1, 0.01], [-0.6, -1], [ 0.007, 1.8, 110] ) ≈
[0.49627714725007616, 1.9757149090208912 ]
@test imf.([[3],[5]], [[-1.35], [-0.6, -1.7]], [[0.1, 100], [0.007, 1.8, 110]]) ≈
[0.038948937298846235, 0.027349915327755464]
end

# Test ismeuv
@testset "ismeuv" begin
@test ismeuv(304, 1e20) ≈ 58.30508020244554
Expand Down