diff --git a/.gitignore b/.gitignore index 1419bab..4ff3b13 100644 --- a/.gitignore +++ b/.gitignore @@ -77,5 +77,8 @@ Manifest.toml notes/* # My terrible, newbie attempt at version control (yes, shouldve jsut learned git) backups/* -# Directory for holding the code for various bhmie implementations -.bhmielibs/* \ No newline at end of file +# Directory for caching test data +*/.cache/* +# For now, performance will be tracked in a benchmarks folder +*benchmarks +*benchmarks/* \ No newline at end of file diff --git a/Project.toml b/Project.toml index e5788f5..38e9942 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,9 @@ +name = "nanoconc" +uuid = "9a947172-b1ea-4b16-84a6-f3d50752424d" +authors = ["Cian Hughes "] +version = "0.1.0" + [deps] -AirspeedVelocity = "1c8270ee-6884-45cc-9545-60fa71ec23e4" -BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b" DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" Debugger = "31a5f54b-26ea-5ae9-a837-f05ce5417438" diff --git a/anchors.jl b/anchors.jl new file mode 100644 index 0000000..307200f --- /dev/null +++ b/anchors.jl @@ -0,0 +1,7 @@ +module Anchors + +const ROOT_DIR = @__DIR__ +const SRC_DIR = joinpath(ROOT_DIR, "src/") +const TEST_DIR = joinpath(ROOT_DIR, "test/") + +end \ No newline at end of file diff --git a/test/Project.toml b/test/Project.toml new file mode 100644 index 0000000..306ba5b --- /dev/null +++ b/test/Project.toml @@ -0,0 +1,5 @@ +[deps] +AirspeedVelocity = "1c8270ee-6884-45cc-9545-60fa71ec23e4" +BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" +PropCheck = "ca382230-33be-11e9-0059-d981d03070e4" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/test/benchmarks.jl b/test/benchmarks.jl new file mode 100644 index 0000000..8f9c222 --- /dev/null +++ b/test/benchmarks.jl @@ -0,0 +1,83 @@ +module Benchmarks + +include("../anchors.jl") +include("ffi_wraps.jl") + +import .Anchors.ROOT_DIR +import .FFIWraps: bhmie_c, bhmie_fortran, bhmie_fortran77 +using BenchmarkTools + +include("$ROOT_DIR/src/miemfp.jl") + +function bench_vs_ffi() + # Fixed testing values + nang = UInt32(2) # Example number of angles + + c_result = @benchmark bhmie_c(x, cxref, nang, cxs1, cxs2) setup=( + x = rand(Float32); + cxref = rand(ComplexF32); + nang = UInt32($nang); + cxs1 = rand(ComplexF32, $nang); + cxs2 = rand(ComplexF32, $nang); + ) + + f_result = @benchmark bhmie_fortran(x, cxref, nang, cxs1, cxs2) setup=( + x = rand(Float32); + cxref = rand(ComplexF32); + nang = Int32($nang); + cxs1 = rand(ComplexF32, $nang); + cxs2 = rand(ComplexF32, $nang); + ) + + f77_result = @benchmark bhmie_fortran77(x, cxref, nang, cxs1, cxs2) setup=( + x = rand(Float32); + cxref = rand(ComplexF32); + nang = Int32($nang); + cxs1 = rand(ComplexF32, $nang); + cxs2 = rand(ComplexF32, $nang); + ) + + j_result = @benchmark miemfp.bhmie(Float64(x), ComplexF64(cxref), nang) setup=( + x = rand(Float32); + cxref = rand(ComplexF32); + nang = UInt32($nang); + ) + + println("\nC Implementation") + display(c_result) + println("\nFortran Implementation") + display(f_result) + println("\nFortran 77 Implementation") + display(f77_result) + println("\nJulia Implementation") + display(j_result) + + return c_result, f_result, f77_result, j_result +end + +end + +if abspath(PROGRAM_FILE) == @__FILE__ + result = Benchmarks.bench_vs_ffi() + + include("../anchors.jl") + import .Anchors.ROOT_DIR + using Pkg + + current_package_version = Pkg.TOML.parsefile("$ROOT_DIR/Project.toml")["version"] + + function display_to_file(io, x) + show(IOContext(io, :limit => false), "text/plain", x) + end + + open("$ROOT_DIR/benchmarks/$current_package_version.txt", "w") do io + println(io, "C Implementation") + display_to_file(io, result[1]) + println(io, "\n\nFortran Implementation") + display_to_file(io, result[2]) + println(io, "\n\nFortran 77 Implementation") + display_to_file(io, result[3]) + println(io, "\n\nJulia Implementation") + display_to_file(io, result[4]) + end +end \ No newline at end of file diff --git a/scripts/build_other_impls.sh b/test/build_ffi.sh similarity index 84% rename from scripts/build_other_impls.sh rename to test/build_ffi.sh index c51adbc..0060c69 100755 --- a/scripts/build_other_impls.sh +++ b/test/build_ffi.sh @@ -10,9 +10,12 @@ base_url="http://scatterlib.wikidot.com/local--files/codes/" # Next, let's create a list of the codebases we want to pull codebases=("bhmie-f.zip" "bhmie-c.zip") -# Then, let's create a directory to pull the codebases to -bhmie_dir=".bhmielibs" -mkdir -p $bhmie_dir +# if bhmie_dir containers bhmie-c/bhmie.so, bhmie-f/bhmie.so, and bhmie-f/bhmie_f77.so then we can skip the build +bhmie_dir=$1 +if [ -f $bhmie_dir/bhmie-c/bhmie.so ] && [ -f $bhmie_dir/bhmie-f/bhmie.so ] && [ -f $bhmie_dir/bhmie-f/bhmie_f77.so ]; then + echo "bhmie-c/bhmie.so, bhmie-f/bhmie.so, and bhmie-f/bhmie_f77.so already exist. Skipping build." + exit 0 +fi # Now, let's pull the codebases for codebase in ${codebases[@]}; do diff --git a/bhmielibs_test.jl b/test/ffi_wraps.jl similarity index 58% rename from bhmielibs_test.jl rename to test/ffi_wraps.jl index e69a2ae..c405630 100644 --- a/bhmielibs_test.jl +++ b/test/ffi_wraps.jl @@ -1,10 +1,21 @@ -run(`scripts/build_other_impls.sh`) +module FFIWraps -# using Debugger -using BenchmarkTools +include("../anchors.jl") -include("src/miemfp.jl") -# using miemfp +if !@isdefined TEST_DIR + include("../anchors.jl") + import .Anchors: TEST_DIR +end + +BHMIELIBS_DIR = joinpath(TEST_DIR, ".cache/bhmie-libs/") + +function __init__() + build_script = joinpath(TEST_DIR, "build_ffi.sh") + + mkpath(BHMIELIBS_DIR) + + run(`$build_script $BHMIELIBS_DIR`) +end function bhmie_c(x::Float32, cxref::ComplexF32, nang::UInt32, cxs1::Vector{ComplexF32}, cxs2::Vector{ComplexF32}) # Pre-allocate memory for the output variables @@ -17,7 +28,7 @@ function bhmie_c(x::Float32, cxref::ComplexF32, nang::UInt32, cxs1::Vector{Compl # For example, if they need to be of size `nang`, you should verify or resize them accordingly # Call the C function - ccall((:bhmie, ".bhmielibs/bhmie-c/bhmie.so"), Cvoid, + ccall((:bhmie, "$BHMIELIBS_DIR/bhmie-c/bhmie.so"), Cvoid, (Float32, ComplexF32, UInt32, Vector{ComplexF32}, Vector{ComplexF32}, Ref{Float32}, Ref{Float32}, Ref{Float32}, Ref{Float32}), x, cxref, nang, cxs1, cxs2, qext, qsca, qback, gsca) @@ -33,7 +44,7 @@ function bhmie_fortran(x::Float32, refrel::ComplexF32, nang::Int32, s1::Vector{C gsca = Ref{Float32}(0.0) # Call the Fortran subroutine - ccall((:bhmie_, ".bhmielibs/bhmie-f/bhmie.so"), Cvoid, + ccall((:bhmie_, "$BHMIELIBS_DIR/bhmie-f/bhmie.so"), Cvoid, (Ref{Float32}, Ref{ComplexF32}, Ref{Int32}, Ptr{ComplexF32}, Ptr{ComplexF32}, Ref{Float32}, Ref{Float32}, Ref{Float32}, Ref{Float32}), x, refrel, nang, s1, s2, qext, qsca, qback, gsca) @@ -49,7 +60,7 @@ function bhmie_fortran77(x::Float32, refrel::ComplexF32, nang::Int32, s1::Vector gsca = Ref{Float32}(0.0) # Call the Fortran subroutine - ccall((:bhmie_, ".bhmielibs/bhmie-f/bhmie.so"), Cvoid, + ccall((:bhmie_, "$BHMIELIBS_DIR/bhmie-f/bhmie.so"), Cvoid, (Ref{Float32}, Ref{ComplexF32}, Ref{Int32}, Ptr{ComplexF32}, Ptr{ComplexF32}, Ref{Float32}, Ref{Float32}, Ref{Float32}, Ref{Float32}), x, refrel, nang, s1, s2, qext, qsca, qback, gsca) @@ -57,44 +68,4 @@ function bhmie_fortran77(x::Float32, refrel::ComplexF32, nang::Int32, s1::Vector return qext[], qsca[], qback[], gsca[] end -# Fixed testing values -nang = UInt32(2) # Example number of angles - -c_result = @benchmark bhmie_c(x, cxref, nang, cxs1, cxs2) setup=( - x = rand(Float32); - cxref = rand(ComplexF32); - nang = UInt32($nang); - cxs1 = rand(ComplexF32, $nang); - cxs2 = rand(ComplexF32, $nang); -) - -f_result = @benchmark bhmie_fortran(x, cxref, nang, cxs1, cxs2) setup=( - x = rand(Float32); - cxref = rand(ComplexF32); - nang = Int32($nang); - cxs1 = rand(ComplexF32, $nang); - cxs2 = rand(ComplexF32, $nang); -) - -f77_result = @benchmark bhmie_fortran77(x, cxref, nang, cxs1, cxs2) setup=( - x = rand(Float32); - cxref = rand(ComplexF32); - nang = Int32($nang); - cxs1 = rand(ComplexF32, $nang); - cxs2 = rand(ComplexF32, $nang); -) - -j_result = @benchmark miemfp.bhmie(Float64(x), ComplexF64(cxref), nang) setup=( - x = rand(Float32); - cxref = rand(ComplexF32); - nang = UInt32($nang); -) - -println("\nC Implementation") -display(c_result) -println("\nFortran Implementation") -display(f_result) -println("\nFortran 77 Implementation") -display(f77_result) -println("\nJulia Implementation") -display(j_result) +end \ No newline at end of file diff --git a/test/miemfp_tests.jl b/test/miemfp_tests.jl index ef3447b..040410f 100644 --- a/test/miemfp_tests.jl +++ b/test/miemfp_tests.jl @@ -1,31 +1,99 @@ using Test +using PropCheck + +include("../anchors.jl") + +import .Anchors: TEST_DIR, SRC_DIR if !@isdefined TestUtils - include("testutils.jl") + include(joinpath(TEST_DIR, "testutils.jl")) end -if !@isdefined nanoconc - include("../src/nanoconc.jl") +if !@isdefined miemfp + include(joinpath(SRC_DIR, "miemfp.jl")) +end +if !@isdefined FFIWraps + include(joinpath(TEST_DIR, "ffi_wraps.jl")) end -@testset "miemfp" begin +# function julia_vs_c(args::Tuple{Float64, Float64, Float64, UInt32, Vector{Float64}, Vector{Float64}, Vector{Float64}, Vector{Float64}}) +# x, cxref_re, cxref_im, nang, cxs1_re, cxs1_im, cxs2_re, cxs2_im = args +function julia_vs_c(x, cxref_re, cxref_im, nang, cxs1_re, cxs1_im, cxs2_re, cxs2_im) + cxref, cxs1, cxs2 = ComplexF64(cxref_re, cxref_im), ComplexF64.(cxs1_re, cxs1_im), ComplexF64.(cxs2_re, cxs2_im) + x_c, cxref_c, nang_c, cxs1_c, cxs2_c = Float32(x), ComplexF32(cxref), UInt32(nang), ComplexF32.(cxs1), ComplexF32.(cxs2) + return isapprox( + miemfp.bhmie(x, cxref, nang), + FFIWraps.bhmie_c(x_c, cxref_c, nang_c, cxs1_c, cxs2_c), + rtol=0.1, + ) +end + +# function julia_vs_fortran(args::Tuple{Float64, Float64, Float64, UInt32, Vector{Float64}, Vector{Float64}, Vector{Float64}, Vector{Float64}}) +# x, cxref_re, cxref_im, nang, cxs1_re, cxs1_im, cxs2_re, cxs2_im = args +function julia_vs_fortran(x, cxref_re, cxref_im, nang, cxs1_re, cxs1_im, cxs2_re, cxs2_im) + cxref, cxs1, cxs2 = ComplexF64(cxref_re, cxref_im), ComplexF64.(cxs1_re, cxs1_im), ComplexF64.(cxs2_re, cxs2_im) + x_f, cxref_f, nang_f, cxs1_f, cxs2_f = Float32(x), ComplexF32(cxref), Int32(nang), ComplexF32.(cxs1), ComplexF32.(cxs2) + b = miemfp.bhmie(x, cxref, nang) + f = FFIWraps.bhmie_fortran(x_f, cxref_f, nang_f, cxs1_f, cxs2_f) + # open("bhmie_julia_vs_fortran.txt", "a") do io + # println(io, "julia: ", b) + # println(io, "fortran: ", f) + # end + # return is_approx(b, f) + return isapprox( + miemfp.bhmie(x, cxref, nang), + FFIWraps.bhmie_fortran(x_f, cxref_f, nang_f, cxs1_f, cxs2_f), + rtol=0.1, + ) +end + +# function julia_vs_fortran77(args::Tuple{Float64, Float64, Float64, UInt32, Vector{Float64}, Vector{Float64}, Vector{Float64}, Vector{Float64}}) +# x, cxref_re, cxref_im, nang, cxs1_re, cxs1_im, cxs2_re, cxs2_im = args +function julia_vs_fortran77(x, cxref_re, cxref_im, nang, cxs1_re, cxs1_im, cxs2_re, cxs2_im) + cxref, cxs1, cxs2 = ComplexF64(cxref_re, cxref_im), ComplexF64.(cxs1_re, cxs1_im), ComplexF64.(cxs2_re, cxs2_im) + x_f, cxref_f, nang_f, cxs1_f, cxs2_f = Float32(x), ComplexF32(cxref), Int32(nang), ComplexF32.(cxs1), ComplexF32.(cxs2) + return isapprox( + miemfp.bhmie(x, cxref, nang), + FFIWraps.bhmie_fortran77(x_f, cxref_f, nang_f, cxs1_f, cxs2_f), + rtol=0.1, + ) +end + +f64_gen = PropCheck.itype(Float64) +UInt32_gen = PropCheck.itype(UInt32) +f64_vec_gen = PropCheck.vector(isample(1:100), f64_gen) +bhmie_gen = PropCheck.interleave( + f64_gen, + f64_gen, + f64_gen, + UInt32_gen, + f64_vec_gen, + f64_vec_gen, + f64_vec_gen, + f64_vec_gen, +) + +@testset "bhmie" begin @testset "miemfp.bhmie" begin - TestUtils.test_from_serialized( - nanoconc.miemfp.bhmie, - "test/data/Main.nanoconc.miemfp.bhmie.ser" - ) - end - - @testset "miemfp.mfp" begin - TestUtils.test_from_serialized( - nanoconc.miemfp.mfp, - "test/data/Main.nanoconc.miemfp.mfp.ser" - ) - end - - @testset "miemfp.qbare" begin - TestUtils.test_from_serialized( - nanoconc.miemfp.qbare, - "test/data/Main.nanoconc.miemfp.qbare.ser" - ) + c_check = PropCheck.check(julia_vs_c, bhmie_gen) + c_result = c_check == true + if !c_result + println("Fail vs C, PropCheck:") + display(c_check) + end + @test c_result + f_check = PropCheck.check(julia_vs_fortran, bhmie_gen) + f_result = f_check == true + if !f_result + println("Fail vs Fortran, PropCheck:") + display(f_check) + end + @test f_result + f77_check = PropCheck.check(julia_vs_fortran77, bhmie_gen) + f77_result = f77_check == true + if !f77_result + println("Fail vs Fortran77, PropCheck:") + display(f77_check) + end + @test f77_result end end \ No newline at end of file diff --git a/test/runtests.jl b/test/runtests.jl index 62221ee..52ccef4 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -3,6 +3,9 @@ using Test include("testutils.jl") include("../src/nanoconc.jl") -include("nanoconc_tests.jl") +# include("nanoconc_tests.jl") include("miemfp_tests.jl") -include("quantumcalc_tests.jl") \ No newline at end of file +# include("quantumcalc_tests.jl") +include("benchmarks.jl") + +Benchmarks.bench_vs_ffi() \ No newline at end of file