8

我正在考虑将 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)。

4

7 回答 7

9

我开发了Forpy允许您在 Fortran(嵌入)中使用 Python 的库。它使用 Fortran C 互操作性来调用 Python C API 函数。

虽然我同意扩展(在 Python 中使用 Fortran)通常更可取,但嵌入有其用途:

  • 现有的大型 Fortran 代码可能需要大量重构才能从 Python 中使用——这里嵌入可以节省开发时间
  • 用 Python 实现替换现有代码的一部分
  • 临时嵌入 Python 以试验给定的 Fortran 代码:例如测试替代算法或提取中间结果

除了嵌入,Forpy还支持扩展 Python。你可以完全用ForpyFortran 编写 Python 扩展模块。现有工具的一个优点f2py是您可以使用 Python 数据类型(例如,编写一个将 Python 列表作为参数的函数或返回 Python 字典的函数)。

使用现有的(可能是遗留的)Fortran 代码通常非常具有挑战性,我认为开发人员应该拥有可用于嵌入和扩展 Python 的工具。

于 2018-06-03T15:48:35.313 回答
8

1. 不要这样做

我知道您想在 Fortan 程序中添加 Python 代码,而不是使用带有 Fortran 扩展的 Python 程序。我的第一条建议是不要这样做。Fortran 在数组算术方面比 Python 快,但 Python 比 Fortran 更容易编写,使用 OOP 技术扩展 Python 代码更容易,而且 Python 可能可以访问对您很重要的库。您提到在 Fortran 中有一个超级优化的主循环;Fortran 非常适合超级优化的内部循环。在使用 Numpy 的 Python 程序中传递 Fortran 数组的逻辑比在 Fortran 中正确处理 Python 对象所必须做的要简单得多。

当我从头开始一个科学计算项目时,我总是先用 Python 编写代码,找出性能瓶颈,然后将其转换为 Fortran。能够针对经过验证的 Python 代码测试更快的 Fortran 代码可以更轻松地显示代码是否正常工作。

由于您有现有代码,使用 Fortran 制作的模块扩展 Python 代码将需要重构,但此过程应该很简单。将初始化代码与主循环分开,将循环分解为逻辑片段,将这些例程中的每一个包装在一个 Python 函数中,然后您的主 Python 代码可以调用 Fortran 子例程,并根据需要将这些子例程与 Python 函数交错。在这个过程中,您可以在 Fortran 的主循环中保留许多优化。F2PY 是一个相当标准的工具,因此找到可以帮助您解决任何问题的人并不难。

2.系统调用

如果您绝对必须让 Fortran 代码调用 Python 代码,而不是相反,最简单的方法是让 Fortran 代码将一些数据写入磁盘,然后使用 aSYSTEM或运行 Python 代码EXECUTE_COMMAND_LINE。如果你使用EXECUTE_COMMAND_LINE,您可以让 Python 代码将其结果输出到 stdout,而 Fortran 代码可以将其作为字符数据读取;如果您有很多输出(例如,一个大矩阵),那么 Python 代码输出一个 Fortran 代码然后读取的文件会更有意义。磁盘读/写开销可能会因此变得非常重要。此外,您必须编写 Fortran 代码来输出数据,Python 代码来读取它,Python 代码来再次输出它,以及 Fortran 代码来重新输入数据。这段代码应该很容易编写和测试,但是在编辑代码时保持这四个部分同步可能会让人头疼。

(在这个 Stack Overflow 问题中尝试了这种方法)

3. 在 Fortran 中将 Python 嵌入到 C 中

我不知道将内存中的 Python 对象直接传递给 Fortran。但是,Fortran 代码可以调用 C 代码,而 C 代码可以嵌入 Python。(请参阅关于扩展和嵌入的 Python 教程。)一般来说,扩展 Python(就像我在第 1 点中推荐的那样)比将其嵌入到 C/C++ 中更可取。(请参阅扩展与嵌入:只有一个正确的决定。)让它工作将是一场噩梦,因为 Python 和 Fortran 之间的任何通信问题都可能发生在 Python 和 C 之间,或者 C 和 Fortran 之间。我不知道是否有人真的在 Fortran 中将 Python 嵌入到 C 中,因此很难获得帮助。

于 2013-06-13T19:00:15.823 回答
6

如果要在 Fortran 中嵌入 Python,则必须通过 Fortran 的 C 接口进行;这就是 ISO_C_BINDING 的用途。我会警告不要嵌入 Python,不是因为这样做的技术难度,而是因为 Python(语言或社区)似乎坚决反对将 Python 用作从属语言。普遍的观点是,你的代码当前使用的任何非 Python 语言都应该被分解为库并用于扩展 Python,而不是相反。因此,您将看到(如此处)更多的回复,试图让您相信您真的不想做您真正想做的事情,而不是实际的技术援助。

这不是煽动、社论或做出道德判断;这是一个简单的事实陈述。如果您尝试嵌入 Python,您将无法从 Python 社区获得帮助。

如果您需要的功能超出了 Fortran 语言本身支持的功能(例如文件系统操作),并且您并不特别需要 Python ,并且您想要一种比 C 更具表现力的语言,那么您可能需要考虑嵌入 Lua。与 Python 不同,Lua 是专门用于嵌入的,因此您可能面临的社会和技术阻力要小得多。

集成 Fortran 和 Lua的项目有很多,迄今为止我见过的最完整的一个是Aotus。作者反应灵敏,集成过程简单。

诚然,这并没有回答最初的问题(如何在 Fortran 90 应用程序中嵌入 Python 解释器),但公平地说,其他答案也没有。这些天来,我使用 Python 作为我选择的可移植通用语言,并且在扩展我们的主要产品(用 Fortran 编写)时,我真的更愿意坚持使用它。由于上述原因,我放弃了嵌入 Python 的尝试,转而嵌入 Lua;出于社会原因,我觉得 Lua 是一个更好的技术选择。这不是我的第一选择,但它是可行的,至少在我的情况下。

如果我冒犯了任何人,请道歉;我不是想挑衅,只是在研究这个特定主题时讲述我的经验。

于 2013-08-05T00:08:31.670 回答
4

有一种非常简单的方法可以使用f2py. 编写您的 python 方法并将其作为输入添加到您的 Fortran 子例程中。在cf2py钩子和类型声明中将它声明EXTERNAL为它的返回值类型,例如REAL*8. 然后,您的 Fortran 代码将有一个指向存储 python 方法的地址的指针。它会像糖蜜一样慢,但对于测试算法,它可能很有用。我经常这样做(我将很多古老的意​​大利面条 Fortran 移植到 python 模块......)这也是在传统 fortran 中使用优化的 Scipy 调用之类的好方法

于 2014-05-18T19:21:42.767 回答
4

我已经尝试了几种解决问题的方法,并且找到了一种可能的最佳方法。我将简要列出方法和结果。

1)通过系统调用嵌入:每次我们想从fortran访问python时,我们使用系统调用来执行一些python脚本并在它们之间交换数据。这种方法的速度受到磁盘读取、写入的限制(在这个代码缓存级别优化的时代,去磁盘是一种致命的罪过)。此外,我们每次要执行脚本时都需要初始化解释器,这是相当大的开销。一个简单的 Runge Kutta 4 阶方法运行 300 个时间步,执行时间高达 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)通过扩展模仿嵌入:到目前为止,我们已经看到了嵌入(主循环留在fortran中,我们根据需要调用python)和扩展(主循环留在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 的灵活性。

于 2013-06-21T10:08:11.143 回答
2

我刚刚成功地将 Python 嵌入到我们内部的 ~500 KLOC Fortran 程序中,带有cffi. 一个重要的方面是不要触及现有的代码。该程序是用 Fortran 95 编写的。我使用该模块编写了一个瘦 2003 包装器,该iso_c_binding模块简单地从各种模块导入数据,获取指向这些数据的 C 指针和/或将 Fortran 类型包装到 C 结构中,将所有内容放入单个类型/结构中并发送到 C 函数。这个 C 函数恰好是一个 Python 函数,用cffi. 它将 C 结构解包为更用户友好的 Python 对象,将 Fortran 数组包装为 Numpy 数组(不复制),然后根据用户配置放入交互式 Python 控制台或运行 Python 脚本。除了单个头文件外,无需编写 C 代码。显然有相当多的开销,但这个功能是为了可扩展性,而不是性能。

我建议不要使用 f2py。它维护得不好,严重限制了 Fortran 代码的公共接口。

于 2016-04-25T20:16:57.073 回答
1

我为此编写了一个库forcallpy,使用嵌入 Python 表达式解释函数的 C 层,并专门处理在 Fortran 和 Python 之间传递的参数以使脚本调用尽可能容易(使用嵌入式 numpy 直接映射 ndarrays 中的 Fortran 数组,使用参数名称来了解它们在 C/Python 端的类型)。

您可以在 readthedocs 的文档中看到一些示例。

劳伦特。

于 2018-09-03T14:04:55.117 回答