OMJulia.jl icon indicating copy to clipboard operation
OMJulia.jl copied to clipboard

Multi-threading with OMJulia

Open Wardi0 opened this issue 2 years ago • 8 comments

Hi there! I am using OMJulia in VSCode and am trying to optimise the parameters of dynamic Modelica models.

I was wondering if the multi-threading of simulations is possible with OMJulia? I have tried creating multiple OMCSession objects and running parallel simulations across them but I get a "TaskFailedException" as soon as multiple simulations try to run at once. I am quite new to both OMJulia and multi-threading so I'm not sure if this is a limitation of OMJulia or a mistake on my end.

Wardi0 avatar Dec 05 '22 15:12 Wardi0

@JKRT can you please comment on that?

casella avatar Dec 05 '22 17:12 casella

Hi there! I am using OMJulia in VSCode and am trying to optimise the parameters of dynamic Modelica models.

I was wondering if the multi-threading of simulations is possible with OMJulia? I have tried creating multiple OMCSession objects and running parallel simulations across them but I get a "TaskFailedException" as soon as multiple simulations try to run at once. I am quite new to both OMJulia and multi-threading so I'm not sure if this is a limitation of OMJulia or a mistake on my end.

Hello @Wardi0 thanks for raising the issue, could you provide a minimal example showcasing the issue, in principle, I believe it should work

JKRT avatar Dec 06 '22 09:12 JKRT

Hi JKRT, thank you for helping with this issue! This is my code:

# Create an OMCSession for each thread, add to a vector and load the model on each
sessions = []
for thread in 1:Threads.nthreads()
    push!(sessions, OMJulia.OMCSession())
    sessions[thread].ModelicaSystem("package.mo","FusionPhD.ModelicaFluidModels.RankineCycles.SimpleCycleESSDemo2")
end

# Define the valid domain of the decision variables [gain, time constant]
upper = x -> [2000e-5,300.0]
lower = x -> [1e-5,1.0]
domain = Fresa.Domain(lower,upper)

# Objective function (minimise deviation integral)
function controller_error(x, sessions)
    thread_id = Threads.threadid() 
    dev = Inf64 # Worst-case objective value
    feasibility = 1.0 # Assume infeasible
    try        
        sessions[thread_id].setParameters(["power_PI.k=$(x[1])", "power_PI.T=$(x[2])"]) # Set design parameters
        println("Starting simulation on thread "*string(Int(thread_id)))
        sessions[thread_id].simulate() # Simulate model with design parameters
        dev = sessions[thread_id].getSolutions("controller_error")[1][end] # Extract objective value from simulation results  
        feasibility = 0.0 # Assume feasible if simulation runs with no errors
        println("Simulation on thread "*string(Int(thread_id))*"\nObjective="*string(dev)*" | Feasibility="*string(feasibility))    
    catch TaskFailedException
        println("Caught TaskFailedException on thread "*string(Int(thread_id)))
        return (dev, feasibility)
    end
    return (dev, feasibility)
end

p0 = [Fresa.createpoint([350e-5,50.0], controller_error, sessions)] # Initial solution population
best, pop = Fresa.solve(controller_error, p0, domain; parameters=sessions, multithreading = true, ngen = 5) # Run Fresa optimiser

Based on my print statements, OMJulia is indeed running in parallel. It is just that the multi-threading raises these TaskFailedExceptions (seemingly at random to me); all the simulations ran fine with no errors on a single thread.

Wardi0 avatar Dec 06 '22 16:12 Wardi0

Perhaps you need to set a unique working directory for each OMCSession otherwise simulation will not work since it tries to delete and create files at the same location.

adeas31 avatar Dec 07 '22 10:12 adeas31

@adeas31 OMJulia creates a new temp directory for each OMCSession() by default and the simulations are done in separate working directory

arun3688 avatar Dec 07 '22 10:12 arun3688

Hi JKRT, thank you for helping with this issue! This is my code:

# Create an OMCSession for each thread, add to a vector and load the model on each
sessions = []
for thread in 1:Threads.nthreads()
    push!(sessions, OMJulia.OMCSession())
    sessions[thread].ModelicaSystem("package.mo","FusionPhD.ModelicaFluidModels.RankineCycles.SimpleCycleESSDemo2")
end

# Define the valid domain of the decision variables [gain, time constant]
upper = x -> [2000e-5,300.0]
lower = x -> [1e-5,1.0]
domain = Fresa.Domain(lower,upper)

# Objective function (minimise deviation integral)
function controller_error(x, sessions)
    thread_id = Threads.threadid() 
    dev = Inf64 # Worst-case objective value
    feasibility = 1.0 # Assume infeasible
    try        
        sessions[thread_id].setParameters(["power_PI.k=$(x[1])", "power_PI.T=$(x[2])"]) # Set design parameters
        println("Starting simulation on thread "*string(Int(thread_id)))
        sessions[thread_id].simulate() # Simulate model with design parameters
        dev = sessions[thread_id].getSolutions("controller_error")[1][end] # Extract objective value from simulation results  
        feasibility = 0.0 # Assume feasible if simulation runs with no errors
        println("Simulation on thread "*string(Int(thread_id))*"\nObjective="*string(dev)*" | Feasibility="*string(feasibility))    
    catch TaskFailedException
        println("Caught TaskFailedException on thread "*string(Int(thread_id)))
        return (dev, feasibility)
    end
    return (dev, feasibility)
end

p0 = [Fresa.createpoint([350e-5,50.0], controller_error, sessions)] # Initial solution population
best, pop = Fresa.solve(controller_error, p0, domain; parameters=sessions, multithreading = true, ngen = 5) # Run Fresa optimiser

Based on my print statements, OMJulia is indeed running in parallel. It is just that the multi-threading raises these TaskFailedExceptions (seemingly at random to me); all the simulations ran fine with no errors on a single thread.

@Wardi0 What version of OMJulia, and what platform are you on? Furthermore, would it be possible to get model, or if that is not possible that you try the same example with some other model that can be shared?

JKRT avatar Dec 08 '22 09:12 JKRT

@JKRT Ah, I was not up-to-date. I was using OMJulia v0.1.0 on VSCode for Windows 10. I have created a repository with the code that produced the errors in that version if you are still interested (https://github.com/Wardi0/OMJulia_multithreading)

Having updated to OMJulia v0.2.1 though, I can no longer start an OMCSession. OMJulia seems to have made 2 files in the current directory, stderr.log and stdout.log, that I cannot edit without admin priveleges (university laptop with admin restrictions....), so trying to create an OMCSession throws:

IOError: open("stderr.log", 769, 420): permission denied (EACCES)

Is there any way around this?

Wardi0 avatar Dec 09 '22 16:12 Wardi0

@Wardi0 start your julia repl as administrator and it should work

arun3688 avatar Dec 09 '22 17:12 arun3688

@arun3688 My university does not give us admin rights on laptops :(

Wardi0 avatar Dec 13 '22 15:12 Wardi0

@Wardi0 One workaround to try what Arun suggested is to create a virtual machine and install everything you need in that image; in that way, you can be administrator. Still, of course, there are performance penalties if you do, but at least it can help to pinpoint the issue

JKRT avatar Dec 13 '22 15:12 JKRT

Hi @JKRT, just wanted to update this thread. I am now using a Linux machine and have got multi-threading working with OMJulia so I think this issue can be closed.

I still cannot run it on Windows due to the IOError: open("stderr.log", 769, 420): permission denied (EACCES) error I mentioned above; my department's system admin was able to reproduce the error even with administrator priveleges. I can create a new issue for this though.

As an aside, is there a way to suppress the printed terminal output from OMJulia.OMCSession() and OMJulia.simulate? With multiple threads, the simulation output from OpenModelica makes the results quite hard to parse.

Wardi0 avatar Mar 01 '23 17:03 Wardi0