在Fortran中调用Python教程
前言
Python是机器学习领域不断增长的通用语言。拥有一些非常棒的工具包,比如scikit-learn,tensorflow和pytorch。气候模式通常是使用Fortran实现的。那么我们应该将基于Python的机器学习迁移到Fortran模型中吗?数据科学领域可能会利用HTTP API(比如Flask)封装机器学习方法,但是HTTP在紧密耦合的系统(比如气候模式)中效率太低。因此,可以选择直接从Fortran中调用Python,直接通过RAM传递气候模式的状态,而不是通过高延迟的通信层,比如HTTP。
1、实现方法
有很多方法可以实现通过Python调用Fortran,但是从Fortran调用Python的方法却很少。从Fortran调用Python,可以看作是将Python代码嵌入到Fortran,但是Python的设计并不是像嵌入式语言Lua。可以通过以下三种方法实现从Fortran调用Python:
Python的C语言API。这是最常用的方式,但需要实现大量的C封装代码。
基于Cython。Cython用于从Python中调用C语言,但也可以实现从C调用Python。
基于CFFI。CFFI提供了非常方便的方法可以嵌入Python代码。
无论选择哪种方法,用户都需要将可执行Fortran文件链接到系统Python库,比如通过添加-lpython3.6到Fortran模式的Makefile文件。
2、实例演示
下面通过Hello World示例演示如何通过Fortran调用Python。Fortran代码保存在test.f90文件,如下:
! test.f90
program call_pythonuse, intrinsic :: iso_c_bindingimplicit noneinterfacesubroutine hello_world() bind (c)end subroutine hello_worldend interfacecall hello_world()end program call_python
首先导入Fortran 2003内部定义的和C语言类型互通的模块iso_c_binding。但使用CFFI时,我们不需要写任何C代码,CFFI会生成C类型的打包接口。下一行则定义了一个C函数hello_world接口,这可以在C语言中实现,但是这里我们使用Python和CFFI。最后,调用hello_world。
为了使用hello_world,我们需要构建CFFI标注,并保存在builder.py中,此代码用于创建可以链接Fortran程序的动态库:
import cffi
ffibuilder = cffi.FFI()header = """
extern void hello_world(void);
"""module = """
from my_plugin import ffi
import numpy as np@ffi.def_extern()
def hello_world():print("Hello World!")
"""with open("plugin.h", "w") as f:f.write(header)ffibuilder.embedding_api(header)
ffibuilder.set_source("my_plugin", r'''#include "plugin.h"
''')ffibuilder.embedding_init_code(module)
ffibuilder.compile(target="libplugin.dylib", verbose=True)
首先,我们导入cffi包,并且声明了外部函数接口(FFI)对象。这看起来似乎比较奇怪,这只是CFFI实现这种目的的方式。下一步,header字符串中包含了需要调用的函数接口的定义。module字符串中包含了真正需要执行的Python程序。装饰器@ffi.def_extern用于标记hello_world函数。my_plugin用于获取ffi对象。def_extern装饰器用于处理C类型,指针等。然后,ffibuilder.embedding_api(header)定义了API,embedding_init_code定义了Python代码。
看起来比较奇怪的是在字符串中定义Python代码,但CFFI需要以这种方式将Python代码构建为共享库对象。ffibuilder.set_source来设置源代码信息。
然后执行以下语句创建共享库libplugin.dylib:
python builder.py
然后使用下列命令编译Fortran程序:
gfortran -o test -L./ -lplugin test.f90
以上是在Mac OSX上创建的共享库,如果在Linux上,共享库应该以.so结尾。如果一切没有问题,那么就可以执行文件了:
3、常见的一些问题
(1)必须将所有Python代码写入header字符串吗?
不需要这样。你可以直接在不同的Python模块中定义Python代码(比如my_module.py),然后在module字符串的开头导入即可。比如,builder.py可以改为如下形式:
...module = """
from my_plugin import ffi
import my_module@ffi.def_extern()
def hello_world():my_module.some_function()"""
...
这将在Python中使用可导入的形式使用Python程序。在添加到Fortran中之前,你也可以通过python -c "import my_module"测试一下。如果失败了,你可能需要将包含my_module模块的路径添加到Python的sys.path变量中。
(2)如何传递Fortran数组给Python
下面是一个示例,将代码定义在一个模块文件中,比如my_module.py:
# my_module.py# Create the dictionary mapping ctypes to np dtypes.
ctype2dtype = {}# Integer types
for prefix in ('int', 'uint'):for log_bytes in range(4):ctype = '%s%d_t' % (prefix, 8 * (2**log_bytes))dtype = '%s%d' % (prefix[0], 2**log_bytes)# print( ctype )# print( dtype )ctype2dtype[ctype] = np.dtype(dtype)# Floating point types
ctype2dtype['float'] = np.dtype('f4')
ctype2dtype['double'] = np.dtype('f8')def asarray(ffi, ptr, shape, **kwargs):length = np.prod(shape)# Get the canonical C type of the elements of ptr as a string.T = ffi.getctype(ffi.typeof(ptr).item)# print( T )# print( ffi.sizeof( T ) )if T not in ctype2dtype:raise RuntimeError("Cannot create an array for element type: %s" % T)a = np.frombuffer(ffi.buffer(ptr, length * ffi.sizeof(T)), ctype2dtype[T])\.reshape(shape, **kwargs)return a
asarray函数使用CFFI的ffi对象转换指针ptr为给定形状的numpy数组。可以使用如下形式在builder.py中的module字符串中调用:
module = """
import my_module@ffi.def_extern()
def add_one(a_ptr)a = my_module.asarray(a)a[:] += 1
"""
add_one也可以定义在my_module.py中。最后,我们需要定义与函数相关的头文件信息,并且添加到builder.py的header字符串中:
header = """
extern void add_one (double *);
"""
最后,在Fortran中以如下形式调用:
program call_pythonuse, intrinsic :: iso_c_bindingimplicit noneinterfacesubroutine add_one(x_c, n) bind (c)use iso_c_bindinginteger(c_int) :: nreal(c_double) :: x_c(n)end subroutine add_oneend interfacereal(c_double) :: x(10)print *, xcall add_one(x, size(x))print *, xend program call_python
这一部分,我们介绍了如何在Fortran中嵌入Python代码块,以及如何传递数组给Fortran或从Fortran传递数组给Python。然后,有些方面还是不太方便。
(3)必须要在三个不同的区域定义python函数签名吗
任何要传递给Fortran的Python函数,都必须要要在三个区域进行定义。
首先,必须在header.h中进行C头文件声明
然后,执行函数必须要在builder.py的module字符串中,或一个外部模块中
最后,Fortran代码中必须包含定义子程序的interface块(接口块)
这对于改变Python函数来说就显得有些麻烦。比如,我们写了一个Python函数:
defcompute_precipitation(T,Q):...
必须要在所有区域进行声明。如果我们想添加一个垂直涡度W作为输入参数,我们必须要修改builder.py以及调用Fortran的程序。显而易见,对于大的工程来说,这就变得极为麻烦。
对于一般通信而言,采用了一小部分fortran/python代码封装器。主要依赖于一个外部python模块:
# module.py
import impSTATE = {}def get(key, c_ptr, shape, ffi):"""Copy the numpy array stored in STATE[key] to a pointer"""# wrap pointer in numpy arrayfortran_arr = asarray(ffi, c_ptr, shape)# update the numpy array in placefortran_arr[:] = STATE[key]def set(key, c_ptr, shape, ffi):"""Call python"""STATE[key] = asarray(ffi, c_ptr, shape).copy()def call_function(module_name, function_name):# import the python moduleimport importlibmod = importlib.import_module(module_name)# the function we want to callfun = getattr(mod, function_name)# call the function# this function can edit STATE inplacefun(STATE)
全局变量STATE是一个包含了函数需要的所有数据的Python字典。get和set函数的功能主要就是将Fortran数组传递给STATA或者从STATE中取出Fortran数组。如果这些函数使用了Fortran/CFFI封装器,那么可以使用如下方式从Fortran中调用Python函数cumulus.compute_precipitation(state_dict):
call set("Q", q)
call set("T", temperature)
call set("ahother_arg", 1)call call_function("cumulus", "compute_precipitation")call get("prec", prec)
如果需要传递更多的数据给compute_precipitation,那么需要添加更多的call set命令,这可能会改变Python函数的执行。我们就不需要改变builder.py中的任何代码。
4、结论
上面描述了如何传递Fortran数据给Python函数,然后再获取计算输出。为了解决频繁更改接口的问题,我们将fortran数据放到了Python模块的字典中。通过调用给定的名称来获取数据,并且将计算结果也存储到相同的字段中,然后,Fortran代码通过索引字典中正确的关键词来获取结果。Cython中使用了类似的架构,但CFFI更为方便。最重要的是,从C语言中调用Cython需要导入Python.h头文件,还要运行Py_initialize和init_my_cython_module函数。然而,CFFI会在后台完成这些操作。
相关文章:
在Fortran中调用Python教程
前言Python是机器学习领域不断增长的通用语言。拥有一些非常棒的工具包,比如scikit-learn,tensorflow和pytorch。气候模式通常是使用Fortran实现的。那么我们应该将基于Python的机器学习迁移到Fortran模型中吗?数据科学领域可能会利用HTTP AP…...

04-PS人像磨皮方法
1.高斯模糊磨皮 这种方法的原理就是建立一个将原图高斯模糊后图层, 然后用蒙版加画笔或者历史画笔工具将需要磨皮的地方涂抹出来, 通过图层透明度, 画笔流量等参数来控制磨皮程度 1.新建图层(命名为了高斯模糊磨皮), 混合模式设置为正常, 然后选择高斯模糊, 模糊数值设置到看…...

nginx反向代理+负载均衡上传webshell重难点+apache漏洞
nginx反向代理 nginx 负载均衡 负载均衡的策略 1、轮询:nginx默认就是轮询其权重都默认为1,服务器处理请求的顺序:ABABABABAB… upstream mysvr { server 192.168.137.131; server 192.168.137.136; }2、weight:跟据配置…...
transition组件的使用
<template><button click"flag !flag">切换</button><transition name"fade"><div v-if"flag" class"box"></div></transition> </template><script setup lang"ts"&g…...

多行文本在块元素中垂直居中
单行文本垂直居中对齐 在块元素中,让单行文本居中,可以使用line-height等于块元素的高,即可让该单行文本垂直居中对齐。 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><me…...
在 WebAssembly 中使用 C/C++ 和 libbpf 编写 eBPF 程序
作者:于桐,郑昱笙 eBPF(extended Berkeley Packet Filter)是一种高性能的内核虚拟机,可以运行在内核空间中,用来收集系统和网络信息。随着计算机技术的不断发展,eBPF 的功能日益强大,…...
leveldb源码解析六——compact
compact分为manual_compaction、minor_compaction、major_compaction,统一由MaybeScheduleCompaction触发: void DBImpl::MaybeScheduleCompaction() {mutex_.AssertHeld();if (background_compaction_scheduled_) {// Already scheduled} else if (shu…...

数据结构(二):单向链表、双向链表
数据结构(二)一、什么是链表1.数组的缺点2.链表的优点3.链表的缺点4.链表和数组的区别二、封装单向链表1. append方法:向尾部插入节点2. toString方法:链表元素转字符串3. insert方法:在任意位置插入数据4.get获取某个…...

COCO物体检测评测方法简介
本文从ap计算到map计算,最后到coco[0.5:0.95:0.05] map的计算,一步一步拆解物体检测指标map的计算方式。 一、ap计算方法 一个数据集有多个类别,对于该数据库有5个gt,算法检测出来10个bbox,对于人这个类别来说检测有…...

记一次上环境获取资源失败的案例
代码结构以及资源位置 测试代码 RestController RequestMapping("/json") public class JsonController {GetMapping("/user/1")public String queryUserInfo() throws Exception {// 如果使用全路径, 必须使用/开头String path JsonController.class.ge…...

实战超详细MySQL8离线安装
在RedHat中,RPM Bundle 方式安装MySQL8。建议一定要用 RPM Bndle 版本安装,包全。官网下载:https://dev.mysql.com/downloads/mysql/1.卸载mariadb,会与MySQL安装冲突。rpm -qa | grep mariadb 查看有无mariadb如果有࿰…...
依赖倒置原则|SOLID as a rock
文章目录 意图动机:违反依赖倒置原则解决方案:C++中依赖倒置原则的例子依赖倒置原则的优点1、可复用性2、可维护性在C++中用好DIP的标准总结本文是关于 SOLID as Rock 设计原则系列的五部分中的 最后一部分。 SOLID 设计原则侧重于开发 易于维护、可重用和可扩展的软件。 在…...

Webpack的知识要点
在前端开发中,一般情况下都使用 npm 和 webpack。 npm是一个非常流行的包管理工具,帮助开发者管理项目中使用的依赖库和工具。它可以方便地为项目安装第三方库,并在项目开发过程中进行版本控制。 webpack是一个模块打包工具ÿ…...

handler解析(2) -Handler源码解析
目录 基础了解: 相关概念解释 整体流程图: 源码解析 Looper 总结: sendMessage 总结: ThreadLocal 基础了解: Handler是一套 Android 消息传递机制,主要用于线程间通信。实际上handler其实就是主线程在起了一…...
【算法】kmp
KMP算法 名称由来 是由发明这个算法的三个科学家的名称首字母组成 作用 用于字符串的匹配问题 举例说明 字符串 aabaabaaf 模式串 aabaaf 传统匹配方法 第一步 aabaabaaf aabaaf 此时,b和f不一致,则把模式串从头和文本串的第二个字符开始比 第…...
git 常用命令之 git checkout
大家好,我是 17。 git checkout 是 git 中最重要最常用的命令之一,本文为大家详细解说一下。 恢复工作区 checkout 的用途之一是恢复工作区。 git checkout . checkout . 表示恢复工作区的所有更改,未跟踪的文件不会有变化。 恢复工作区的所有文件风…...

一些常见错误
500状态码: 代表服务器业务代码出错, 也就是执行controller里面的某个方法的过程中报错, 此时在IDEA的控制台中会显示具体的错误信息, 所以需要去看IDEA控制台的报错404状态码: 找不到资源找不到静态资源 检查请求地址是否拼写错误 检查静态资源的位置是否正确 如果以上都没有问…...

[单片机框架][调试功能] 回溯案发现场
程序莫名死机跑飞,不知道问题,那么下面教你回溯错误源 回溯案发现场一、修改HardFault_Handler1. xx.s 在启动文件,找到HardFault_Handler。并修改。2. 定义HardFault_Handler_C函数。(主要是打印信息并存储Flash)3. 根…...
MySQL主从同步-(二)搭建从机服务器
在docker中创建并启动MySQL从服务器:**端口3307docker run -d \-p 3307:3306 \-v /atguigu/mysql/slave1/conf:/etc/mysql/conf.d \-v /atguigu/mysql/slave1/data:/var/lib/mysql \-e MYSQL_ROOT_PASSWORD123456 \--name atguigu-mysql-slave1 \mysql:8.0.3创建MyS…...

Linux系列 备份与分享文档
作者简介:一名在校云计算网络运维学生、每天分享网络运维的学习经验、和学习笔记。 座右铭:低头赶路,敬事如仪 个人主页:网络豆的主页 目录 前言 一.备份与分享文档 1.使用压缩和解压缩工具 (1&…...
浏览器访问 AWS ECS 上部署的 Docker 容器(监听 80 端口)
✅ 一、ECS 服务配置 Dockerfile 确保监听 80 端口 EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]或 EXPOSE 80 CMD ["python3", "-m", "http.server", "80"]任务定义(Task Definition&…...

边缘计算医疗风险自查APP开发方案
核心目标:在便携设备(智能手表/家用检测仪)部署轻量化疾病预测模型,实现低延迟、隐私安全的实时健康风险评估。 一、技术架构设计 #mermaid-svg-iuNaeeLK2YoFKfao {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg…...

c#开发AI模型对话
AI模型 前面已经介绍了一般AI模型本地部署,直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型,但是目前国内可能使用不多,至少实践例子很少看见。开发训练模型就不介绍了&am…...
MySQL账号权限管理指南:安全创建账户与精细授权技巧
在MySQL数据库管理中,合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号? 最小权限原则…...
SQL慢可能是触发了ring buffer
简介 最近在进行 postgresql 性能排查的时候,发现 PG 在某一个时间并行执行的 SQL 变得特别慢。最后通过监控监观察到并行发起得时间 buffers_alloc 就急速上升,且低水位伴随在整个慢 SQL,一直是 buferIO 的等待事件,此时也没有其他会话的争抢。SQL 虽然不是高效 SQL ,但…...

MFC 抛体运动模拟:常见问题解决与界面美化
在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...
C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...
NPOI Excel用OLE对象的形式插入文件附件以及插入图片
static void Main(string[] args) {XlsWithObjData();Console.WriteLine("输出完成"); }static void XlsWithObjData() {// 创建工作簿和单元格,只有HSSFWorkbook,XSSFWorkbook不可以HSSFWorkbook workbook new HSSFWorkbook();HSSFSheet sheet (HSSFSheet)workboo…...
深度剖析 DeepSeek 开源模型部署与应用:策略、权衡与未来走向
在人工智能技术呈指数级发展的当下,大模型已然成为推动各行业变革的核心驱动力。DeepSeek 开源模型以其卓越的性能和灵活的开源特性,吸引了众多企业与开发者的目光。如何高效且合理地部署与运用 DeepSeek 模型,成为释放其巨大潜力的关键所在&…...

LeetCode - 148. 排序链表
目录 题目 思路 基本情况检查 复杂度分析 执行示例 读者可能出的错误 正确的写法 题目 148. 排序链表 - 力扣(LeetCode) 思路 链表归并排序采用"分治"的策略,主要分为三个步骤: 分割:将链表从中间…...