henry2004y / Batsrus.jl

BATSRUS/SWMF Data Processor
https://henry2004y.github.io/Batsrus.jl/dev/
MIT License
5 stars 2 forks source link

`getdata2d` relies on matplotlib #49

Closed henry2004y closed 2 months ago

henry2004y commented 3 months ago

https://github.com/henry2004y/Batsrus.jl/blob/a540abcb258cf878522074e071fd57f69f4b8f09/src/plot/utility.jl#L11

Currently getdata2d depends on matplotlib to do interpolations. In the future we really need to figure out how to do it native Julia. Related to #14.

henry2004y commented 3 months ago

This is also related to a newly wanted feature of extracting variables at a fixed spatial location from slices, presumably in an unstructured mesh.

henry2004y commented 2 months ago

I found one solution using NaturalNeighbours.jl. It is about 10 times slower than Matplotlibs's triangular interpolation on a small dataset, but at least we have something in native Julia now.

DanielVandH commented 2 months ago

I found one solution using NaturalNeighbours.jl. It is about 10 times slower than Matplotlibs's triangular interpolation on a small dataset, but at least we have something in native Julia now.

You'll probably get a faster speed if you replace https://github.com/henry2004y/Batsrus.jl/blob/2ae4d98052efc27f5c8951ae9038b2d4ddf55632/src/plot/utility.jl#L130 to instead be itp(x, y), where x and y are the vectors you're evaluating at, similar to what is done in the README https://github.com/DanielVandH/NaturalNeighbours.jl. You'll be able to hook into the multithreading that way.

henry2004y commented 2 months ago

I found one solution using NaturalNeighbours.jl. It is about 10 times slower than Matplotlibs's triangular interpolation on a small dataset, but at least we have something in native Julia now.

You'll probably get a faster speed if you replace

https://github.com/henry2004y/Batsrus.jl/blob/2ae4d98052efc27f5c8951ae9038b2d4ddf55632/src/plot/utility.jl#L130

to instead be itp(x, y), where x and y are the vectors you're evaluating at, similar to what is done in the README https://github.com/DanielVandH/NaturalNeighbours.jl. You'll be able to hook into the multithreading that way.

Thank you for your tips! I tried and it is indeed a bit faster, with 4 thread giving a speedup of ~ 2. However, the current way I did it requires extra array allocations:

function interpolate2d_generalized_coords2(X::T, Y::T, W::T,
   plotrange::Vector{<:AbstractFloat}, plotinterval::Real) where
   {T<:AbstractVector}
   xi = range(plotrange[1], stop=plotrange[2], step=plotinterval)
   yi = range(plotrange[3], stop=plotrange[4], step=plotinterval)
   itp = interpolate(X, Y, W)
   #Wi = [itp(x, y; method=Triangle()) for y in yi, x in xi]::Matrix{eltype(W)}
   Xi, Yi = Batsrus.meshgrid(xi, yi)
   x_ = vec(Xi)
   y_ = vec(Yi)
   Wi = itp(x_, y_; method=Triangle())

   xi, yi, Wi
end

which is something I may wish to avoid (say using LazyGrids.jl, but I haven't tried yet). However, even with multithreading it is still a bit slower than the Matplotlib triangulation interpolation implementation. Do you know the reason @DanielVandH ?

Here is a minimal working example for you to test:

using NaturalNeighbours
using StableRNGs
using PyPlot

function meshgrid(x, y)
   X = [x for _ in y, x in x]
   Y = [y for y in y, _ in x]

   X, Y
end

## The data 
rng = StableRNG(123)
f = (x, y) -> sin(x * y) - cos(x - y) * exp(-(x - y)^2)
x = vec([(i - 1) / 9 for i in (1, 3, 4, 5, 8, 9, 10), j in (1, 2, 3, 5, 6, 7, 9, 10)])
y = vec([(j - 1) / 9 for i in (1, 3, 4, 5, 8, 9, 10), j in (1, 2, 3, 5, 6, 7, 9, 10)])
z = f.(x, y)

## The interpolant and grid 
itp = interpolate(x, y, z; derivatives=true)
xg = LinRange(0, 1, 100)
yg = LinRange(0, 1, 100)
_x = vec([x for x in xg, _ in yg])
_y = vec([y for _ in xg, y in yg])

## Evaluate some interpolants 
triangle_vals = itp(_x, _y; method=Triangle())
println("NaturalNeighbours.jl:")
@time itp(_x, _y; method=Triangle());

triang = matplotlib.tri.Triangulation(x, y)
# Perform linear interpolation on the triangle mesh
interpolator = matplotlib.tri.LinearTriInterpolator(triang, z)
Xi, Yi = meshgrid(xg, yg)
Wi = interpolator(Xi, Yi)
println("Matplotlib:")
@time Wi = interpolator(Xi, Yi);
println("")

What I get is

NaturalNeighbours.jl:
  0.010370 seconds (37.41 k allocations: 1.056 MiB)
Matplotlib:
  0.000652 seconds (40 allocations: 80.031 KiB)

I will post an issue in your repo~

DanielVandH commented 2 months ago

which is something I may wish to avoid (say using LazyGrids.jl, but I haven't tried yet)

Yeah the need for vec is annoying. I could probably fix this at some point so that matrices can also be provided if I found the time.