移植旧的fortran程序以使用python + numpy

时间:2014-05-09 22:11:29

标签: python numpy fortran fortran90 f2py

我应该用这个庞大的Fortran 77程序进行研究(我最近将它移植到Fortran 90表面)。它是一个非常古老的软件,用于使用有限元方法进行建模。

  • 这是一个怪物。它大约有240,000行。
  • 自从它在Fortran 77中开始使用以来,它使用了一些真正的脏黑客进行动态内存分配;基本上它使用C标准库中的函数,使用C和Fortran进行混合编程。我还没有完全掌握分配的工作原理。该程序可以由用户轻松扩展,用户通常需要分配一些全局可访问的数组供以后使用。这是通过一个内存地址数组来完成的,它指向动态可分配数组的起始地址。当然,在开始真正编程之前,地址数组的哪个元素指向哪些信息都取决于用户必须学习的约定。有两个地址数组,一个用于整数,另一个用于浮点。
  • 肮脏的黑客,我的意思是不一致的。例如,GNU编译器的优化算法中的更新导致程序以随机​​内存泄漏退出。
  • 该计划远非优雅。全局变量名称通常较短(3-4个字符)且含糊不清。当然,通过使用包括所有程序开关和上述阵列的公共块来完成在例程中传递数据。
  • 该程序的使用大致类似于交互式shell,尽管是一个愚蠢的程序。首先,程序本身读取输入文件,然后根据选择,将用户放入伪shell中,用户必须键入4个字符宽的命令,然后输入参数。然后解析器解析命令,并使用参数调用相应的子例程。你会猜到这个伪解析器中存在一个循环结构(相反的是goto bonanza),它以比21世纪更复杂的方式包装子程序行为。
  • 输入文件的格式相同(命令,然后参数),因为它是相同的解析器。但是语法并不是真的一致(我的意思是它缺乏控制结构,有些命令会导致有限状态机做与其他命令相矛盾的行为;它缺乏明确的语法),不时导致最终用户发现陷阱。用户必须通过经验了解这些陷阱;我没有在程序的任何文档中看到它们。这是python可以轻松避免的问题,甚至不需要实现解析器。

我想做什么:

  • 程序的端口部分为python,即与数值计算无关的部分。这包括
    • 在python中使用OOP方法清理和抽象API,
    • 提供有意义的变量名称,
    • 将动态分配迁移到numpy或Fortran 90并丢失C部分,
    • 将非数字执行迁移到python,并使用f2py包装数字对象,因此性能没有任何损失。我是否告诉该程序在当前状态下快速该死的?希望将对数字子程序和I / O的调用移植到python不会减慢到不切实际的水平(或者它会吗?)。
    • 使用python的交互式shell作为伪shell的替代品。这样,最终用户就不会有任何不一致之处。上述命令将简单地由python中定义的函数替换。这将允许用户实际访问数据。此外,用户将能够扩展程序而无需深入。

我想知道:

  • f2py是否合适并且可以完成包装大量子程序和公共块的任务而不会产生任何混淆?我只在网上看到f2py的单文件示例;我知道numpy使用它来包装LAPACK和东西,但我需要保证f2py是一个足以完成此任务的工具。
  • 对于我应该遵循的一般策略是否有任何建议,或者我应该避免陷阱。
  • 怎么能&我应该在这个python包装的Fortran 90环境中实现一个系统,这样我就可以在fortran例程中修改(分配和分配)全局可访问的数组和变量。这应该优选地省略地址数组并且我应该优选地能够将口头表示注入命名空间。这些变量最好在python和fortran中都可以访问。

注意:

  • 我可能一直在要求太多,超出了可能范围的界限。在这种情况下,请原谅我,因为我是这方面编程的初学者;并且不要犹豫纠正我。
  • "程序"我一直在谈论的是开源,但它是商业的,许可证不允许其分发,所以我决定不提它的名字。但是,您可以从第二句和我给出的描述中推断出它。

2 个答案:

答案 0 :(得分:2)

我正在做一些令人沮丧的事情。我们不是通过C进行动态内存分配,而是使用整数索引(也在全局范围内)的单个全局数组,但除此之外它们大致相同。奇怪的,不一致的输入文件和所有。

我建议不要试图重写大部分程序,无论是在python还是其他任何东西。这是耗时,令人不愉快的,而且在很大程度上是不必要的。作为替代方案,获取F77代码库,以确定它是否足够干净,您是否愿意信任它,然后编写接口例程。

我现在有一个庞大,丑陋的F77代码库,位于界面后面。该程序需要输入作为文本文件,因此接口的大部分工作是生成该文本文件。除此之外,遗留代码被简化为单个网关例程,该例程采用一些参数(包括识别文本文件的方法)并返回答案。如果使用Fortran 2003的iso_c_binding,则可以以C理解的格式公开接口,此时您可以将其链接到您想要的任何内容。

就现代代码(主要是优化例程)而言,遗留代码库是C接口背后的单个子例程。这比尝试进一步修改旧代码要好得多,也可能是针对您的案例的有效策略。

答案 1 :(得分:2)

有关如何使用多个fortran文件see this post生成f2py接口库的示例。

f2py可能适合您的任务,但有一些陷阱可能会导致一些问题。关于f2py的一些陷阱列在here并总结如下:

  • 关于您的特定问题,您可能会遇到可分配数组的问题,因为f2py是针对Fortran77编写的,并且不支持许多Fortran90 +功能(例如可分配数组)。
  • 我还遇到了未记录的最大阵列大小(arround 400 x 200 x 20 x 20)的问题。如果我使用更大的数组,那么f2py将无法生成python库。特别是在finitie元素代码中传递的大矩阵可能对于接口来说太大了。因此,您无权访问程序的Python部分。
  • 对你有利的是,f2py应该没有COMMON Block等问题,因为它是专为Fortran77编写的。
  • 将数据通过接口传递给Fortran例程后,如果你做得对,应该没有(或只有极小的)减速。关键是尽量减少每次运行程序的Python部分的计算。这包括数据数组的操作(移位,旋转,复制等),但传递它们(因为接口是通过引用传递)。

作为替代方案,您应该看一下Cython(另请参阅Link above和其中的链接working example)。我认为从长远来看,这可能对你有所帮助。

实施建议

这个建议是我如何将我的经验与做过类似的事情相结合(参见下面的背景)。它应该主要取决于你如何连接Python和Fortran代码(f2py,Cython,...)。

当然,你应该非常小心,不要改变行为,因此可能改变程序的结果。因此产生一些测试及其相应的参考信息。输出文件和测试文档,包括重现这些结果所需的所有步骤,按键,命令等应该是您的第一步。

在你的情况下,我会尽量改变Fortran程序的可能性。我会尝试楔入"伪壳"来自Fortran代码,例如使其成为自己的模块,并构建该模块的接口。就像你可以使用所有原始的Fortran代码以及来自同行的修改,错误修正和更新,即使将来也是如此。关键是不要将代码远离原始/主流,因为在科学界通常不是每个人都会同意源代码的重大更改并相应地更新其工作流程或源代码。因此,您的同行未来的工作可能不会在您的版本中进行,而是在原始源代码中进行,您自己有责任将这些更改合并到您的版本中,随着您的更改越少,这将变得越容易。

使用该接口,您可以在python shell上工作,甚至可以为它构建GUI,而无需担心更改原始程序中的任何内容。这降低了引入错误或更改原始结果的风险。因此,您的Shell / GUI将作为原始程序的包装器,以简化工作流程并消除不一致性。所有"情报"和实用程序,如错误和&交叉检查用户输入,帮助页面,教程/ howto等将在Python包装器中实现,它将解析这些输入,将它们转换为Fortran程序的相应命令,发送它们并等待结果。

在简化了程序的使用之后,我会为测试(设置+评估)编写一些自动化来完成您的实用程序套件。就像那样,即使是程序的新手也能够对代码进行更改,而不必担心在不知不觉中改变结果。这应该使您的工具能够使社区受益,从而吸引新用户,从而鼓励社区内的进一步发展。

仅作为最后一步,我将使用C和Fortran90 +方法替换代码部分以简化代码。这是对代码库的广泛更改,需要进行大量测试以确保在更改之前和之后检查并验证每个可能的命令组合。

这种方法也有一个好处,你可以使你的界面/ GUI开源(你必须检查你的程序的许可证),只要它可以从Fortran程序的源代码中分离出来。当您使用一些简单的构建skript加载接口时,必须提供或安装/生成源文件的Fortran-Python接口,如本帖的第一个链接所示。

对于内部数据的操作,我会编写一个单独的包装程序,它只处理数据接口。这应该在Cython中完成,以使您能够使用可分配的数组等。因为这个接口可以使用" pass-by-reference"您应该能够使用Python(numpy)工具的完整集合来操作数组和数据。

背景

我使用我们的直升机旋翼动力学研究代码做了类似的事情。这也是用Fortran77编写的一个非常古老的大型程序(例如goto bonanza)。代码的新增和修改通常在Fortran90 / 2003中完成。

使用此代码的一部分(几个子程序和模块文件)我生成了一个python库,将我们的GUI(Python和Qt)连接到Fortran程序;主要用于Fortran二进制输出文件的后处理。