-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathDLSolvers.jl
262 lines (229 loc) · 7.95 KB
/
DLSolvers.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
# Dynamic loading of ODE-Solvers for ODEInterface
using Libdl
"""
macro for importing (un-)load functions.
"""
macro import_dynamicload()
:(
using ODEInterface: loadODESolvers, unloadODESolvers
)
end
"""
Type describing a dynamically loaded method.
"""
struct MethodDLinfo
generic_name :: AbstractString # input for trytoloadmethod
methodname_found :: AbstractString # variant of generic_name found
method_ptr :: Ptr{Cvoid} # pointer to method/code
error # error if there was one
end
"""
Type describing the "dynamic parts" of a solver.
"""
struct SolverDLinfo
libname :: AbstractString # the name of the dynamic library
libfilepath :: AbstractString # path to lib, result of find_library
libhandle :: Ptr{Cvoid} # handle of lib, result of dlopen
# an immutable Dict is missing ...
methods :: Tuple # ... Tuple of MethodDLinfo
error # error if there was one
end
"""
global Dict saving informations of all loaded solvers.
"""
global const dlSolversInfo = Dict{AbstractString,SolverDLinfo}()
"""
function trytoloadlib(name::AbstractString,extrapaths::Vector)
tries to (dynamically) load the given shared library given by name.
if `ame="name"` then all the following variants will be tried:
`"name"`, `"NAME"`, `"Name"`
returns `(ptr,filepath)`
"""
function trytoloadlib(name::AbstractString,extrapaths::Vector)
ptr = C_NULL
trylist = [name, uppercase(name), uppercasefirst(name)]
filepath = Libdl.find_library(trylist, extrapaths)
if isempty(filepath)
throw(ErrorException(
"Cannot find one of $trylist in libpaths or in $extrapaths"))
end
ptr = Libdl.dlopen(filepath)
return (ptr,filepath)
end
"""
function trytoloadmethod(libhandle::Ptr{Cvoid},
method_name::AbstractString) -> (ptr,namefound)
tries to find the given method by name in a dynamically loaded library.
if `method_name="name"` then the following variants will be tried:
`"name"`, `"NAME"`, `"Name"`,
`"name_"`, `"NAME_"`, `"Name_"`,
`"_name"`, `"_NAME"`, `"_Name"`,
`"_name_"`, `"_NAME_"`, `"_Name_"`
"""
function trytoloadmethod(libhandle::Ptr{Cvoid},method_name::AbstractString)
namefound = ""
name_vars = ( method_name, uppercase(method_name),
uppercasefirst(method_name) )
trylist = tuple(
name_vars...,
map(x->string(x,"_"),name_vars)...,
map(x->string("_",x),name_vars)...)
ptr = C_NULL
for name in trylist
ptr = Libdl.dlsym_e(libhandle,name)
if (ptr != C_NULL)
namefound = name
break
end
end
ptr == C_NULL &&
throw(ErrorException("Cannot find one of the methods $trylist"))
return (ptr,namefound)
end
"""
attemt to get the path of this module.
Returns path or nothing. May throw exceptions.
"""
function guess_path_of_module()
path_to_module = nothing
if path_to_module == nothing
path_sep = ""
if isdefined(Base, :path_separator)
path_sep = Base.path_separator
end
if isdefined(Base, :Filesystem) &&
isdefined(Base.Filesystem, :path_separator)
path_sep = Base.Filesystem.path_separator
end
path_to_module = Base.find_package("ODEInterface")
if path_to_module ≠ nothing
if endswith(path_to_module, ".jl")
path_to_module = path_to_module[1:end-3]
end
if endswith(path_to_module, "ODEInterface")
path_to_module = path_to_module[1:end-12]
end
if !endswith(path_to_module, path_sep)
path_to_module = string(path_to_module, path_sep)
end
end
end
return path_to_module
end
"""
function loadODESolvers(extrapaths::Vector=AbstractString[],
loadlibnames::Tuple=() )
tries to (dynamically) load the solvers.
additional locations/paths to look at can be given as argument.
If the 1st argument is an empty Vector, then the method tries to
find the path of the ODEInterface module and (if successfull)
uses this path as `extrapaths`.
The 2nd argument is a `Tuple` with libnames of solvers to load.
If it is an empty tuple, then all known solvers will be tried.
If an solver is already successfully loaded, then it will *not* be
loaded again.
returns `Dict` with informations about the loaded solvers (and errors).
If a solver cannot be found (or needed methods inside a dynmic library
cannot be found) then the errors are not propagated to the caller. The
errors and expections are saved in the returned `Dict`. Why? Using this
way, it is possible to see with one call (and try to load all solvers)
which solvers are found.
You can simply `dump` the values of the output dict to get a human-readable
form of the result or call `help_solversupport()`.
for k in keys(res); dump(res[k]); end
ODEInterface.help_solversupport()
"""
function loadODESolvers(extrapaths::Vector=AbstractString[],
loadlibnames::Tuple=() )
if isempty(extrapaths)
try
path_to_module = guess_path_of_module()
if path_to_module ≠ nothing
extrapaths = [ path_to_module ]
end
catch e
# At least we tried
end
end
for solver in solverInfo
for variant in solver.variants
libname = variant.libname
if isempty(loadlibnames) || libname ∈ loadlibnames
if !haskey(dlSolversInfo,libname) ||
nothing ≠ dlSolversInfo[libname].error
libhandle = C_NULL; filepath =""; err = nothing
mArray = Vector{MethodDLinfo}()
try
(libhandle,filepath) = trytoloadlib(libname,extrapaths)
for generic_name in variant.methods
methodsname_found = ""; method_ptr = C_NULL; merr = nothing
try
(method_ptr, methodsname_found) =
trytoloadmethod(libhandle, generic_name)
catch e
merr = e
end
push!(mArray,MethodDLinfo(
generic_name, methodsname_found, method_ptr, merr))
end
catch e
err = e
end
dlSolversInfo[libname] = SolverDLinfo(
libname, filepath, libhandle, tuple(mArray...), err)
end
end
end
end
return copy(dlSolversInfo)
end
"""
function unloadODESolvers()
unload all (loaded) solvers.
"""
function unloadODESolvers()
all_keys = collect( keys(dlSolversInfo) )
for key in all_keys
libhandle = dlSolversInfo[key].libhandle
libhandle ≠ C_NULL && Libdl.dlclose(libhandle)
delete!(dlSolversInfo,key)
end
return nothing
end
"""
return all method-pointers for a solver.
tries to return all `method_ptr`s for all methods of a solver.
This method checks if the `method_ptr`s are existent and different
from `C_NULL`. If not then this method tries to load the
`dlname` ODE-Solver with the `loadODESolvers` method and checks again.
If even after this the `method_ptr`s are not found a exception is thrown.
see `loadODESolvers`.
"""
function getAllMethodPtrs(dlname::AbstractString)
load_tried = false
ret = Vector{Ptr{Cvoid}}()
while true
try
empty!(ret)
@assert dlSolversInfo[dlname].error == nothing
for method in dlSolversInfo[dlname].methods
@assert method.method_ptr ≠ C_NULL
push!(ret,method.method_ptr)
end
break
catch e
if load_tried
throw(SolverODEnotLoaded(string(
"Cannot find method(s) for $(dlname)! ",
"I've tried to loadODESolvers(), but it didn't work. ",
"Please check ODEInterface.help_solversupport() and ",
"call loadODESolvers and check also this output. ",
"For further information see also ODEInterface.help_install.")))
else
loadODESolvers([],(dlname,)); load_tried = true
end
end
end
return ret
end
# vim:syn=julia:cc=79:fdm=indent: