Pass functions to objects

Hi everyone,

I’m new to Julia and writing a particle simulation. Each System of Particles should have a set of functions that update the forces acting on the system/particles. These functions should be user definable. I don’t know how to solve this problem properly. Currently I’m using a tuple that contains the functions that should be called:

mutable struct System
    ps::Vector{Particle}
    forces::Tuple
end

and then calling them like:

function updateForces!(s::System)
    for force in s.forces
        force(s)
    end
end

This however is slow compared to calling the functions directly. @benchmark reports 350ns with 0 allocations vs 430ns with 0 allocations when using 2 custom functions for s.forces.

Is there a better way to do this?

Thank you,
Nico

1 Like

Tuple is an abstract type. Make all fields in System concrete. For instance

mutable struct System{P, F}
    ps::Vector{P}
    forces::Tuple{F, F}
end
1 Like

Looping over functulns tend to be slow because the compiler often cannot devirtualize the call. People usually use GitHub - yuyichao/FunctionWrappers.jl to work around that (although I have heard it is slower than needed due to some julia issue).

1 Like

Probably

?

1 Like

Note that this overhead you are observing will be probably irrelevant relative to the cost of computing the functions themselves (it is probably a case of premature optimization).

Take a look at the packages of JuliaMolSim and at CellListMap.jl, probably there is useful stuff there to you.

@ncxst some ideas about that are available here: GitHub - m3g/2021_FortranCon: Simulation codes shown at the FortranCon 2021

1 Like

I don’t fully understand the {} syntax jet, but as far as I understand it specifies types. Does forces::Tuple{F, F} mean that the tuple forces contains 2 objects of type F?
When I try to implement this, I get errors as forces contains different functions, e.g. harmonic! and f_d!:

julia> System([Particle([0.,0.]) for i in 1:3], (harmonic!, f_d!))
ERROR: MethodError: no method matching System(::Vector{Particle}, ::Tuple{typeof(harmonic!), typeof(f_d!)})
Closest candidates are:
  System(::Vector{P}, ::Tuple{F, F}) where {P, F} at REPL[5]:2

Can your code be adapted to accommodate for this?

https://docs.julialang.org/en/v1/manual/types/#Parametric-Types

1 Like
mutable struct System{P, F1, F2}
    ps::Vector{P}
    forces::Tuple{F1, F2}
end

Aren’t you fighting multiple dispatch a bit here by trying to privately define a set of functions for a struct in a classic object oriented design approach?

What’s preventing you from having the system struct separate to a list of methods that can operate on system structs? You could even define them in a separate module?

Maybe I’m missing the point…

Now it allows for 2 types of forces, how can I make it accept any number of forces?

Perhaps OP has different functions per instance.

I want to have multiple systems where each of them has a different list of forces that act on the system.
How would I do such a thing with multiple dispatch?

Ok - difficult to discern the need - are you trying to allow users to create their own functions for a particular set of forces? If so, are the System structs not actually structs that inherit from the System Abstract struct?

Then you can have any number of methods to act on a concrete system struct and add them to a collection to do as you first posted.

I think you want to decouple the particles from the system, ie separate the data from the different operations that can act on it.

1 Like

Yes

Do you mean, that there should be specific structs that inherit from an abstract struct and specify the functions/forces that should act on the special struct?

How would the system then know which particles are in it and how to apply the forces on the particles.

Yes, basically. I’m on my phone but that sounds like that pattern you’re describing: you differentiate the action by the properties of the struct.

This is multiple dispatch if inverted from your original design i.e. they are not all actually “system” structs; they are particular types of system struct

What differentiates the different particles? Can that be encoded in the type(s), for example as type parameters, or must each particle instance get an arbitrary list of functions?

If you can design it as the former, you definitely should leverage dispatch instead of the current design.

1 Like

So the user would define a custom “system” e.g. MySystem and a custom function updateForces!(s::MySystem) that calls the forces-function the user wants.
Then multiple dispatch chooses the appropriate updateForces!.
Do I understand this correctly?

I see your point, however the same kind of particles can be trapped in different kinds of traps e.g harmonic or sinusoidal, so there has to be the possibility to have different forces acting in different systems with the same type of particles in it.