将python嵌入fortran 90
Posted
技术标签:
【中文标题】将python嵌入fortran 90【英文标题】:Embed python into fortran 90 【发布时间】:2013-06-09 03:37:20 【问题描述】:我正在寻找将 python 嵌入到 fortran90 中的选项,以便将 python 功能添加到我现有的 fortran90 代码中。我知道可以通过使用 numpy 中的 f2py 使用 fortran90 扩展 python 来完成相反的操作。但是,我想在 fortran 中保留我的超级优化主循环并添加 python 来执行一些额外的任务/评估进一步的开发,然后才能在 fortran 中执行它,并且还可以简化代码维护。我正在寻找以下问题的答案:
1) 是否有一个已经存在的库,我可以从中将 python 嵌入到 fortran 中? (我知道 f2py 并且它以相反的方式进行) 2) 我们如何处理从 fortran 到 python 和返回的数据传输? 3)我们如何实现回调功能? (让我稍微描述一下这个场景......我在 Fortran 中有我的 main_fortran 程序,它在 python 中调用 Func1_Python 模块。现在,从这个 Func1_Python 中,我想调用另一个函数......比如说在 fortran 中的 Func2_Fortran) 4)在性能方面将python的解释器嵌入到fortran中会有什么影响......比如加载时间,运行时间,发送数据(双精度的大数组)等等。
非常感谢您的帮助!!
Edit1:我想通过添加更多关于我正在做的工作的信息来确定讨论的方向。我喜欢科学计算的东西。所以,我会在双精度的巨大数组/矩阵上做很多工作,并进行浮点运算。因此,除了 fortran 之外,几乎没有其他选择可以真正为我完成这项工作。我想在我的代码中包含 python 的原因是我可以在必要时使用 NumPy 进行一些基本计算,并以最小的努力扩展代码的功能。例如,我可以使用几个可用的库来链接 python 和其他一些包(比如使用 PyFoam 库的 OpenFoam)。
【问题讨论】:
如果你打算使用 sympy,check this 除非您正在执行字符串操作,否则 Fortran 可能比 Python 更快。 【参考方案1】:我开发了库Forpy
,允许您在 Fortran(嵌入)中使用 Python。
它使用 Fortran C 互操作性来调用 Python C API 函数。
虽然我同意扩展(在 Python 中使用 Fortran)通常更可取,但嵌入也有其用途:
现有的大型 Fortran 代码可能需要大量重构才能 它们可以从 Python 中使用 - 这里嵌入可以节省开发时间 用 Python 实现替换现有代码的一部分 临时嵌入 Python 以试验给定的 Fortran 代码: 例如测试替代算法或提取中间结果除了嵌入,Forpy
还支持扩展 Python。
使用Forpy
,您可以完全在 Fortran 中编写 Python 扩展模块。
f2py
等现有工具的一个优势是您可以使用 Python 数据类型
(例如,编写一个将 Python 列表作为参数的函数或一个返回 Python dict 的函数)。
使用现有的(可能是遗留的)Fortran 代码通常非常具有挑战性,我 认为开发人员应该拥有可用于嵌入和扩展 Python 的工具。
【讨论】:
如果您必须能够从 fortran 访问 python 并且无法从 python 调用 fortran,那么 这就是答案。 Forpy 太棒了。【参考方案2】:1。不要这样做
我知道您希望在 Fortan 程序中添加 Python 代码,而不是使用带有 Fortran 扩展的 Python 程序。我的第一条建议是不要这样做。 Fortran 在数组算术方面比 Python 快,但 Python 比 Fortran 更容易编写,使用 OOP 技术扩展 Python 代码更容易,而且 Python 可能可以访问对您很重要的库。您提到在 Fortran 中有一个超级优化的主循环; Fortran 非常适合超级优化的 inner 循环。在使用 Numpy 的 Python 程序中传递 Fortran 数组的逻辑比在 Fortran 中正确处理 Python 对象所必须做的要简单得多。
当我从头开始一个科学计算项目时,我总是先用 Python 编写代码,找出性能瓶颈,然后将其转换为 Fortran。能够针对经过验证的 Python 代码测试更快的 Fortran 代码可以更轻松地显示代码是否正常工作。
由于您有现有代码,使用 Fortran 制作的模块扩展 Python 代码需要重构,但此过程应该很简单。将初始化代码与主循环分开,将循环分解成逻辑片段,将这些例程中的每一个包装在一个 Python 函数中,然后您的主 Python 代码可以调用 Fortran 子例程,并酌情将这些子例程与 Python 函数交错。在这个过程中,您可以在 Fortran 的主循环中保留许多优化。 F2PY 是一个相当标准的工具,因此找到可以帮助您解决任何问题的人并不难。
2。系统调用
如果您绝对必须让 Fortran 代码调用 Python 代码,而不是相反,最简单的方法是让 Fortran 代码将一些数据写入磁盘,然后使用 SYSTEM
运行 Python 代码或EXECUTE_COMMAND_LINE
。如果你使用EXECUTE_COMMAND_LINE
,你可以让Python代码把它的结果输出到stdout,Fortran代码可以把它作为字符数据读取;如果你有很多输出(例如,一个大矩阵),那么 Python 代码输出一个 Fortran 代码然后读取的文件会更有意义。磁盘读/写开销可能会因此变得非常重要。此外,您必须编写 Fortran 代码来输出数据,Python 代码来读取它,Python 代码来再次输出它,以及 Fortran 代码来重新输入数据。这段代码应该很容易编写和测试,但是在编辑代码时保持这四个部分同步可能会让人头疼。
(这种方法在this Stack Overflow question中尝试过)
3。在 Fortran 中将 Python 嵌入到 C 中
据我所知,无法将内存中的 Python 对象直接传递给 Fortran。但是,Fortran 代码可以调用 C 代码,而 C 代码可以嵌入 Python。 (参见Python tutorial on extending and embedding。)一般来说,扩展Python(就像我在第1点中推荐的那样)比将它嵌入到C/C++中更可取。 (请参阅Extending Vs. Embedding: There is Only One Correct Decision。)让它工作将是一场噩梦,因为 Python 和 Fortran 之间的任何通信问题都可能发生在 Python 和 C 之间,或者 C 和 Fortran 之间。我不知道是否有人真的在 Fortran 中将 Python 嵌入到 C 中,因此很难获得帮助。
【讨论】:
您好亚历克斯,感谢您非常全面的回答。我还在查看 Fortran-> C -> Python 选项....我将在几天内发布使用 fortran 扩展 python 与将 python 嵌入 fortran 的放大结果。 有大量的 C/C++ 程序嵌入了 pzthon 解释器。当然 Lua 或 Guile 更容易嵌入,但社区中的人更多。使用适当的 C/Fortran 互操作性,无论是嵌入 C 还是 Fortran,应该没有太大区别。 虽然我知道 Alex 的心是正确的,但他可能会遗漏第 1 点和第 2 点。Greenfield Fortran 编程很少见,当人们询问在 Fortran 中嵌入语言时,这是因为核心代码出于务实的原因(例如质量保证、速度、用另一种语言从头开始编写的资源很少、开发人员的核心能力),必须留在 Fortran 中。没有人将一种语言嵌入作为一种随意的练习。同样,触发 system() 调用可能不安全且不可移植,因此有充分的理由不这样做。【参考方案3】:如果您要在 Fortran 中嵌入 Python,则必须通过 Fortran 的 C 接口进行;这就是 ISO_C_BINDING 的用途。我会警告不要嵌入 Python,不是因为这样做的技术难度,而是因为 Python(语言或社区)似乎坚决反对将 Python 用作从属语言。普遍的观点是,你的代码当前使用的任何非 Python 语言都应该分解为库并用于扩展 Python,而不是相反。因此,您会看到(如这里)更多的回复,试图让您相信您真的不想做您实际上想做的事情,而不是实际的技术援助。
这不是煽动或社论或做出道德判断;这是一个简单的事实陈述。如果您尝试嵌入 Python,您将无法从 Python 社区获得帮助。
如果您需要的是超出 Fortran 语言本身支持的功能(例如文件系统操作)并且您并不特别需要 Python 并且您想要一种比C,你可能想看看嵌入 Lua。与 Python 不同,Lua 专门用于嵌入,因此您可能面临的社会和技术阻力要小得多。
integrate Fortran and Lua 有很多项目,迄今为止我见过的最完整的一个是Aotus。作者反应灵敏,集成过程简单。
诚然,这并没有回答最初的问题(如何在 Fortran 90 应用程序中嵌入 Python 解释器),但公平地说,其他回答也没有。这些天来,我使用 Python 作为我选择的可移植通用语言,并且在扩展我们的主要产品(用 Fortran 编写)时我真的更愿意坚持使用它。由于上述原因,我放弃了嵌入 Python 的尝试,转而嵌入 Lua;出于社会原因,我觉得 Lua 是一个更好的技术选择。这不是我的第一选择,但它是可行的,至少在我的情况下。
如果我冒犯了任何人,我深表歉意;我不是想挑衅,只是在研究这个特定主题时讲述我的经验。
【讨论】:
感谢您的回答。我同意你所说的。 Python 不是为了将其嵌入到另一种语言中而设计的,因此很少/不支持这样做。然而,从科学计算的角度来看,Python 非常受关注,尤其是因为像 NumPy 和 SciPy 这样的计算包比它所具有的所有其他优势。通过在嵌入和扩展之间进行某种混合,我确实找到了一种两全其美的方法。你可以看看我上面的回答。 Python 是我工作环境(工程分析)中通用计算和低性能数值的选择。在这里,软件由主要语言为 Fortran 的主题专家(通常是核、化学和机械工程师)编写,而不是由计算机科学家编写。出于可靠的技术和组织原因,将我们的主要编程语言从 Fortran 切换到 Python 是不可能的。我宁愿避免引入第三种语言,但 Lua 是满足我们需求的更好选择。 YMMV。【参考方案4】:我已经尝试了几种解决问题的方法,并且找到了一种可能是最佳的解决方法。我将简要列出方法和结果。
1)通过系统调用嵌入:每次我们想从fortran访问python时,我们使用系统调用来执行一些python脚本并在它们之间交换数据。这种方法的速度受到磁盘读取、写入的限制(在这个代码缓存级别优化的时代,去磁盘是一种致命的罪过)。此外,我们每次要执行脚本时都需要初始化解释器,这是相当大的开销。运行 300 个时间步长的简单 Runge Kutta 4 阶方法的执行时间高达 59 秒。
2) 通过 C 从 Fortran 到 Python:我们使用 ISO_C 绑定在 Fortran 和 C 之间进行通信;我们将 Python 解释器嵌入到 C 中。我得到了它的一部分工作,但同时我找到了更好的方法并放弃了这个想法。不过,为了完整起见,我仍然想对此进行评估。
3) 使用 f2py 将 Fortran 子例程导入 Python(扩展): 在这里,我们从 Fortran 中取出主循环并用 Python 对其进行编码(这种方法称为 Extending Python with Fortran);我们使用 f2py (http://cens.ioc.ee/projects/f2py2e/usersguide/) 将所有 Fortran 子例程导入 Python。我们可以灵活地在任何科学应用程序中拥有最重要的数据,即 Python 中的最外层循环(通常是时间循环),以便我们可以将其与其他应用程序耦合。但是,我们也有一个缺点,即必须在 Fortran 和 Python 之间交换可能超出需要的数据。相同的 Runge Kutta 4 阶方法示例的执行时间为 0.372 秒。
4) 通过扩展模拟嵌入: 到目前为止,我们已经看到了 Embedding(主循环留在 fortran 中,我们根据需要调用 python)和 Extending(主循环留在 python 中,我们根据需要调用 fortran)两种纯方法。还有另一种方法,我发现这是最优化的。将部分主循环转移到 Python 中会导致开销,这可能不是一直都需要的。为了摆脱这种开销,我们可以将主循环保留在 Fortran 中,将其转换为子程序而不做任何更改,在 Python 中有一个伪主循环,它只调用 Fortran 中的主循环,程序就好像它是我们的一样执行原封不动的 Fortran 程序。必要时,我们可以使用回调函数将所需数据返回 python,执行脚本并再次返回 fortran。在这种方法中,Runge Kutta 4th Order 方法耗时 0.083 秒。我分析了代码,发现python解释器的初始化和加载用了0.075秒,程序只用了0.008秒(其中包括300个python回调函数)。原始 fortran 代码耗时 0.007 秒。因此,使用这种方法,我们可以获得几乎与 Fortran 类似的性能和类似 Python 的灵活性。
【讨论】:
这种方法(第 4 种)对您有何影响?您有可以分享的回购示例吗?【参考方案5】:使用f2py
有一个非常简单的方法可以做到这一点。编写您的 python 方法并将其作为输入添加到您的 Fortran 子例程中。在 cf2py
钩子和类型声明中将其声明为 EXTERNAL
以及它的返回值类型,例如REAL*8
。然后,您的 Fortran 代码将有一个指向存储 python 方法的地址的指针。它会像糖蜜一样慢,但对于测试算法,它可能很有用。我经常这样做(我将很多古老的意大利面条 Fortran 移植到 python 模块......)这也是在传统 fortran 中使用优化的 Scipy 调用之类的好方法
【讨论】:
【参考方案6】:我刚刚使用cffi
成功地将 Python 嵌入到我们内部的 ~500 KLOC Fortran 程序中。一个重要的方面是不要触及现有的代码。该程序是用 Fortran 95 编写的。我使用 iso_c_binding
模块编写了一个精简的 2003 包装器,它简单地从各个模块导入数据,获取指向这些数据的 C 指针和/或将 Fortran 类型包装到 C 结构中,将所有内容放入单个类型/结构并发送到 C 函数。这个 C 函数恰好是一个用 cffi
包装的 Python 函数。它将 C 结构解包为更用户友好的 Python 对象,将 Fortran 数组包装为 Numpy 数组(无复制),然后根据用户配置放入交互式 Python 控制台或运行 Python 脚本。除了单个头文件外,无需编写 C 代码。显然有相当多的开销,但此功能旨在实现可扩展性,而不是性能。
我建议不要使用 f2py。它维护不善,严重限制了 Fortran 代码的公共接口。
【讨论】:
【参考方案7】:我为此编写了一个库forcallpy,使用嵌入 Python 表达式解释函数的 C 层,并专门处理 Fortran 和 Python 之间传递的参数以使脚本调用尽可能简单(使用嵌入式 numpy 直接映射ndarrays 中的 Fortran 数组,在 C/Python 端使用参数名称来知道它们的类型)。
您可以在 readthedocs 的文档中查看一些示例。
洛朗。
【讨论】:
以上是关于将python嵌入fortran 90的主要内容,如果未能解决你的问题,请参考以下文章
将 mpi 消息从 c++ 代码发送到 fortran 90 代码
是否有可能将“include”转移到fortran的模块中?
[fortran90][原创]安装fortran后的hello world