Интеграция Fortran и Engee¶
В этом проекте рассматривается вопрос интеграции унаследованного кода Fortran и Engee.
Рассматриваются два сценария:
- Интеграция в среду технических расчетов
- Интеграция в среду моделирования
Допустим, требуется интегрировать такой код:
subroutine vect_add(a,b,res,len)
integer len
integer i
real, intent(inout) :: res(len)
real a(len)
real b(len)
do concurrent (i = 1:len)
res(i) = a(i) + b(i)
end do
return
end subroutine vect_add
Данная подпрограмма складывает два вектора. Наша задача - использовать эту подпрограмму в своих расчетах.
Подготовка к работе¶
Прежде чем начать работу, отметим следующее: Нам потребуется модифицировать код подпрограммы, что бы она могла корректно привязаться к имени C или C++:
subroutine vect_add(a,b,res,len) bind ( C, name="vect_add" )
Что бы корректно использовать нашу подпрограмму, она должна быть скомпилирована в разделяемую библиотеку. Для этого будем использовать компилятор gfortran
run(`gfortran vect_add.f90 -shared -fPIC -o vect_add.so`)
Убедимся в том, что мы можем работать с скомпилированной процедурой, выполнив следующую команду:
run(`objdump -x vect_add.so`)
Данная команда выводит символы в бинарном файле, и мы видим что символ vect_add существует в нашей библиотеке:
00000000000010f9 g F .text 00000000000000e1 vect_add
Интеграция в среду технических расчетов¶
Для корректной интеграции мы будем использовать Dynamic Linker. Это позволит нам загружать и выгружать нашу разделяемую библиотеку по мере необходимости.
using Pkg; Pkg.add("Libdl")
using Libdl
Загрузим нашу библиотеку:
lib = Libdl.dlopen("./vect_add.so");
foo = Libdl.dlsym(lib, :vect_add);
Создадим тестовые данные и вызовем нашу подпрограмму с помощью функции ccall.
Особенностью работы ccall с фортраном является то, что все параметры подпрограмм фортрана передаются по ссылке.
В Julia для этого есть специальный тип Ref
.
Vect_in = Vector{Float32}([1.0,2.0,3.0,4.0,5.0])
Vect_out = copy(Vect_in);
ccall(foo,Nothing,(Ref{Float32},Ref{Float32},Ref{Float32},Ref{Int}), Vect_in, Vect_in, Vect_out, 5)
println("Результат работы кода Fortran : $Vect_out")
После того как мы закончили работать с библиотекой, ее надо выгрузить:
Libdl.dlclose(lib)
Интеграция в среду моделирования¶
Для интеграции скомпилированной библиотеки в среду моделирования будем использовать блок C Function.
Как и для любой библиотеки для работы с библиотечными функциями требуется заголовочный файл. Как несложно заметить, у нас его нет. Однако gfortran позволяет сгенерировать его автоматически. Для этого перекомпилируем наш код, дополнительно указав ключ -fc-prototypes
и записав вывод в файл vect_add.h
res = read(`gfortran vect_add.f90 -shared -o libvect_add.so -fPIC -fc-prototypes`,String);
write("vect_add.h",res);
Откроем модель fort_integration
:
demoroot = @__DIR__
demo = joinpath("$demoroot","fortran_caller.engee");
mdl = engee.load(demo)
Дополнительно настроим пути до заголовочных файлов и библиотек для блока C Function:
engee.set_param!("fortran_caller/Fortran_is_Here","IncludeDirectories"=>demoroot)
engee.set_param!("fortran_caller/Fortran_is_Here","LibraryDirectories"=>demoroot)
Вызов нашей процедуры содержится в блоке C Function, в секции output code. Так как на ее вход подается указатель, то мы должны передавать адреса входных сигналов.
Дополнительно надо добавить сгенерированный заголовочный файл и библиотеку в информацию о сборке:
Запустим симуляцию и посмотрим результаты:
engee.run(mdl);
println("Входной вектор: $(simout["fortran_caller/V_in"].value[1])")
println("Результаты работа Fortran: $(simout["fortran_caller/res"].value[1])")
Выводы¶
Интеграция кода на Fortran выполняется первичной компиляции этого кода в разделяемую библиотеку и вызова этой библиотеки из среды моделирования или интерактивного скрипта. Такой подход позволяет использовать предыдущие наработки без затрат на переписывание кода на языке Julia