基于SiPESC平台的Python扩展模块开发

2015-01-13 19:07陈飙松李云鹏陆旭泽
计算机辅助工程 2014年6期

陈飙松+李云鹏+陆旭泽

摘要: 基于Python的可扩展性,针对SiPESC平台对于动态解释型脚本语言环境的需求,设计并开发可动态集成SiPESC平台上系统管理、算法调用、分析计算等功能的Python扩展模块.讨论扩展模块开发过程中的用户需求、程序设计以及相关技术.SiPESC.OPT与Python第三方库的集成优化算例表明,该扩展模块能顺利驱动SiPESC平台上复杂工作流程,具有较强的实用性和灵活性.

关键词: Python; SiPESC; 脚本语言; 扩展模块

中图分类号: TB115.1文献标志码: B

0引言

随着计算机技术的飞速发展,科学研究与科学计算之间的关系日益密切,人们对于科学计算工具的需求已不再局限于处理数值问题.各种格式的大型数据集的处理、新算法的实现、数据库与互联网等计算系统的搭建等问题都需要新工具解决.[1]Python以其优越的跨平台性和强大的科学计算功能,正获得越来越多科研人员的青睐.不少商业CAE软件也提供对Python脚本语言的支持,例如Abaqus二次开发环境提供的脚本接口就是基于Python语言进行定制开发的.

目前,SiPESC平台已经实现对JavaScript脚本语言的支持,用户可以通过JavaScript语言调用平台上所有的功能插件完成各种任务.为进一步丰富SiPESC平台的脚本语言环境,弥补JavaScript语言在模块开放性等方面的不足,平台开发小组引进Python作为新的脚本支持引擎.在SiPESC平台上Python具有以下优势:

1)执行相应操作所需的代码少,代码编写简单.[2]

2)通过判断、循环和数据存储与处理等语句,易于实现人工智能控制和自动化处理过程.

3)对优化问题易于实现参数化分析,且代码结构合理易读,便于修改.

4)可以编写独立的模块,使用Python,C,C++和Fortran等语言扩展SiPESC平台的功能.

5)提供优秀的异常抛出和处理机制,缩短调试脚本的时间.

1Python语言及其扩展方法

1.1Python语言概述

Python是一门优雅而健壮的编程语言,继承传统编译语言的强大性和通用性,同时也借鉴简单脚本和解释语言的易用性.[3]其高效率的高层数据结构和简单易学的面向对象编程特点,在很多平台上都得到很好的应用,成为在这些平台上进行快速应用程序开发的主流脚本语言.[4]Python能够在Linux,Windows,Mac OS,FreeBSD和Solaris等主流操作系统中运行,其代码几乎无须进行任何修改就能在这些操作系统中执行,具有很强的跨平台性.自从1989年Guido van Rossum始创Python以来,经过十几年的发展,Python已经包含一系列完善、简易的标准库,足以完成文本处理、算法实现、科学计算、文件管理、界面设计和网络通信等常见任务,同时其还提供一套丰富的API,能够帮助程序员轻松地使用其他编程语言编写扩展模块.这种可扩展性让程序员能灵活附加或定制工具,大大缩短开发周期,并使代码的性能得到显著提升.[3]

1.2Python的通用扩展方法

早期的编程语言并不具备可扩展性,程序员只能使用现有的功能而无法向该语言增加新的功能.Python作为最早一批提供可扩展性能的语言,在支持使用纯Python语言编写扩展的同时,也支持C,C++和Java等主流编程语言的扩展.抽象的动态加载机制让扩展与解释器之间的交互方式与Python的内置模块完全一致,并且不区分编写扩展所使用的语言.

Python扩展模块主要通过Python的C语言应用程序编程接口Python/C API实现,其创建过程主要分以下几个步骤[57]:

1)包含Python头文件.通过Python.h文件导入到Python API并完成必要的预处理定义.

2)编写具体实现并封装.使用其他编程语言实现具体的功能函数和类,并将其封装成能让Python解释器识别的形式.

3)编写初始化函数.初始化函数主要完成模块对象创建和导入工作.

4)编译和链接.可以通过Python自身提供的distutils包或g++和gcc等编译器完成编译和链接.

除以上的扩展方法外,也可以通过第三方的封装工具,如Boost.Python,SWIG,Pyrex和Psyco等编写Python的扩展模块.

2扩展模块的设计

传统的Python扩展模块封装方法为静态封装,即每项扩展中增加一个新的功能,都需修改源代码,并重新编译.这种封装方法工作量大,缺乏灵活性,开发效率低,也无法匹配SiPESC平台的开放性.本文采用动态封装的方法,充分利用SiPESC平台软件体系的动态性,实现对任意插件的自动封装而无须修改源代码.这种自动封装技术也是SiPESC平台脚本语言环境的体系结构(见图1)中的重要环节.

图 1SiPESC.LAB软件体系结构

Fig.1Software architecture of SiPESC.LAB

2.1扩展模块的系统分析

2.1.1用户需求分析

不同的用户对SiPESC平台和Python脚本语言的了解程度不同,其使用Python扩展模块的目的也不尽相同.可以将Python扩展模块的用户分成以下2类.

1)普通用户,指对Python脚本语言了解很少甚至从未接触过Python,仅想通过脚本调用SiPESC平台的插件进行一般计算分析或其他基本操作的用户.对于该类用户,要尽量使扩展模块中调用SiPESC平台功能插件所要使用的脚本代码规范化,让他们能通过使用手册中的介绍或示例很容易地编写脚本、调用插件.为此,Python扩展模块的设计要结构清晰,层次分明,不容易造成混淆,让用户一目了然.同时,扩展模块中的类名、函数名要尽量与原有JavaScript脚本引擎一致,保证那些JavaScript用户也能很快适应新的脚本引擎.endprint

2)高级用户,指非常熟悉Python语言并能熟练使用Python的一些第三方扩展库的用户.他们不仅想通过脚本使用平台的功能,同时还想利用Python的第三方库进行一些高级应用,实现多模块的集成.为了能让平台的功能与Python的第三方库更好地结合,需要在设计扩展模块时尽量使用Python的内建数据类型,同时让模块里的方法更加通用,完善对常用第三方库中数据类型的支持.

2.1.2任务分析

基于SiPESC平台的Python扩展模块的价值在于为Python脚本语言与SiPESC集成平台之间搭建一座桥梁,在极大扩展Python功能的同时也方便平台中脚本语言的使用.任何一台安装SiPESC集成平台和Python环境的用户计算机,都可以在不启动平台的情况下,使用几行简单的脚本代码调用平台的各个功能,而高级用户更是能结合平台的计算分析功能以及Python第三方库的其他功能,搭建一个功能强大的集成环境.

通过独立的脚本代码即可调用SiPESC的各功能插件,需要让扩展模块完全脱离平台的GUI界面,而通过链接SiPESC平台中插件管理相关的动态库实现两者的交互.平台插件与Python第三方库的结合使用,需要规范扩展模块的数据类型,尽可能使用Python的内建类型,提高数据类型在各扩展中的兼容性.同时, 针对SiPESC平台的软件结构体系,需要扩展模块必须具有一定的动态性,即对于已有或未来开发的插件都能在不修改代码、不重新编译的情况下被扩展模块动态地加载使用.

2.1.3接口分析

为实现Python脚本与SiPESC平台的交互,需要分析SiPESC平台插件管理机制,寻找关键的接口,并实现在扩展模块中封装.这样,Python解释器就能通过这些接口访问SiPESC平台中的各功能插件,完成相应的工作.SiPESC平台采用统一的接口管理模式[8],其插件管理器类图见图插件管理器类用于管理系统中所有的插件,包括加载、卸载和查询等操作.平台启动时会首先调用initialize方法初始化插件管理器,创建全局的插件管理器对象.可以使用getManager方法获得相应的插件管理器对象,并通过注册、加载和启用扩展等方法动态地配置所需的平台环境.平台上具体的功能通过各个扩展对象实现,由功能扩展管理器类创建并管理.

功能扩展管理器类实现对各种插件中的功能扩展的管理和查询等功能.SiPESC平台中功能扩展管理器类图见图3.

getManager方法用于获得一个全局功能扩展管理器对象,由这个全局对象管理平台上的各种功能,实现插件的调用与功能扩展对象的创建等.createExtension方法用于创建一个指定名称的功能扩展对象.createExtensionCaller方法则只用于内部使用,无须暴露给用户.其他的方法均提供相应的查询功能,方便用户获取扩展对象的具体信息.

2.2扩展模块中类的设计

根据对SiPESC平台接口的分析,为实现使用Python脚本调用SiPESC平台上的插件的功能,需要在扩展模块中封装一个MExtensionManager类,向用户提供通过脚本代码对SiPESC平台上的插件进行查询、管理和调用的功能.同时,其能获得所调用的插件中的功能扩展对象,利用创建扩展对象的方法创建相应的功能扩展对象,并可以访问该对象的全部方法和属性.

为更好地控制扩展模块中底层的数据处理和对象创建过程,不使用第三方扩展工具,而是纯粹利用Python/C API实现扩展模块的编写.同时,由于SiPESC平台是基于Qt开发的,为能与SiPESC平台插件管理器中的类型互相兼容,在编写扩展时大量地使用Qt库中的类型.区别于传统的Python扩展模块编写方法,为实现动态封装机制,还需使用JIT(即时编译)技术.[910]

对于扩展模块中的MExtensionManager类,其接口与SiPESC平台上的功能扩展管理器类的接口基本一致,且返回值均为PyObject*类型(构造函数除外).其实扩展模块只是将平台的接口进行二次封装,具体实现时调用的仍是平台上的方法.MExtensionManager类是扩展模块的一个静态类,根据Python扩展的规则,其所有的方法都必须是静态的,而且只能有2个参数,类型也只能是PyObject*.

3扩展模块的实现

3.1扩展模块的运行流程

程序能否正确地实现所采用的理论和方法并正确地运行,流程(框图)设计是先导和关键.根据上文对基于SiPESC平台的Python扩展模块的设计,可以画出该扩展模块在平台上使用时的工作流程图,见图4.全局插件管理器对象的初始化与获取在扩展模块的初始化函数中完成,其他步骤均通过函数的封装实现.流程图中对功能扩展类是否已注册的查询,可以防止重复注册相同的功能扩展类,提高运行效率.

图 4Python扩展模块的运行流程

3.2PyObject与QVariant的类型转换

在具体实现扩展模块中的类时,Python对象与QVariant对象之间的类型转换被反复地使用.前文已经提到过,Python中所有方法的返回值都是PyObject*类型,其参数也必须是PyObject*类型,这些PyObject*指针都指向各自的Python对象.但是,SiPESC平台上所有方法的返回值和参数均为QVariant类型,因此必须实现PyObject与QVariant之间的相互转换.Python提供PyObject与C数据类型对象相互转换的API,而QVariant与C数据类型对象之间的相互转换也很容易实现,这样便能通过2次转换实现PyObject与QVariant的转换.

作为一种动态语言,Python脚本语言中的变量在使用前不需要声明、定义,其类型根据给变量所赋的值自动判断,可以大大地简化从PyObject对象转换成QVariant类型的过程.可以使用Python中判断变量类型的API加上类型转换函数自动完成PyObject到QVariant的转换,而无须知道PyObject在Python中属于哪种类型.将PyObject转换为QVariant的函数原型为endprint

QVariant PyObjToQVariant(PyObject* obj)

PyObject类型的指针指向需要转换的Python对象,可以通过PyObject_TypeCheck接口判断该对象在Python中表示的类型,之后调用相应的Python类型转换API(如PyInt_AS_LONG)将该对象转换为C语言的数据类型,进而转化为QVariant类型.

同理,可以使用QVariant的内置方法将其转换为C数据类型,并调用相应的API(如PyInt_FromLong)完成向PyObject类型的转换.实现这个过程的函数原型为

PyObject* QVarToPyObj(const QVariant& val)

3.3MExtensionManager类的封装

在扩展模块中,只有MExtensionManager类是静态定义的,用于实现SiPESC平台上的功能扩展的加载和调用,其他功能扩展类都通过MExtensionManager类在Python引擎上创建、注册和调用.因此,在扩展模块中只实现MExtensionManager一个类,而该类的结构与SiPESC平台功能扩展管理器类一致,扩展模块中的方法内部调用的其实也是平台上的方法.由SiPESC平台插件管理器的接口可知,该类中大多数的方法都是用来进行查询的,对于这些类方法,实现很简单,只需将从脚本获得的参数转换为QVariant类型,并传给平台调用功能扩展管理器类的相应方法,获得返回值,然后将返回值转换回PyObject*类型并输出.

createExtension方法是MExtensionManager类中最关键的方法,其根据获得的参数创建相应的功能扩展类对象.该方法在被调用时先查询该功能扩展类是否已在Python引擎中注册,若未注册,则先获取功能扩展类的信息对象(MextensionInfo对象),接着根据这个信息创建相应的类(如果该类的父类未在Python引擎中注册,则会先创建父类),然后将创建的类注册给Python,最后通过类的构造函数创建对象并返回.同时,创建的对象将会被存储起来,供脚本调用.

使用createExtension方法创建Python的新类型时,需要提供该类的方法列表,该方法列表为以下形式的结构体:

ml_name为函数名,ml_meth为函数指针,ml_flags指示函数的调用方式,ml_doc则为函数说明.其中,由于动态性的需求,ml_meth通过JIT技术在运行时动态获得,而其他参数可根据对应的MExtensionInfo对象确定.

3.4初始化函数的编写

在完成MExtensionManager类的封装之后,需要编写扩展模块的初始化函数.Python中创建一个模块的API原型为

PyObject* Py_InitModule3(char *name, PyMethodDef *methods, char *doc)

Python根据模块名name和模块层的方法列表methods构造一个新模块并返回.如果参数doc不为空,则此处为模块的一些信息.为向模块中添加自定义类,还需要在模块的初始化函数中使用添加自定义类的API,其原型为

int PyModule_AddObject(PyObject *module, const char *name, PyObject *value)

module为之前创建的模块,name为自定义类的名字,value则是定义类在Python中的类对象的结构体,包含自定义类的全部信息.因为本扩展模块的动态特性,只有MExtensionManager类是在初始化时便添加到模块中,其他类均在使用时根据需求动态地添加,所以创建的模块必须是一个全局的静态变量,这样在程序的每个地方,都能使用这个变量添加类.同时,在初始化函数中还需调用SiPESC平台中MPluginManager类的initialize方法完成平台的初始化.

4应用实例

为验证本文实现的扩展模块在驱动SiPESC平台复杂工作流程中的实用性及其与Python第三方库的良好兼容性,考虑图5所示的GARTEUR飞机模型的修正问题,通过优化6个部位的弹性模量,使模型前9阶振动频率与试验值的方差取最小值.

该算例通过SiPESC.OPT插件控制优化流程,由ANSYS进行频率计算,并通过Python的二维画图库Matplotlib实现迭代历程的输出,见图6.整个优化过程都通过Python的脚本代码驱动,其中部分关键的语句如下:

5结论

基于SiPESC平台的开放性设计并实现相应的Python扩展模块.通过该扩展模块,可以使用脚本语言调用SiPESC中的插件实现数值计算和优化分析等功能,并可集成Python的第三方库,创建复杂工作流程.本文提出动态封装的思想,相对于传统的静态封装方式,提高扩展模块的灵活性,节约开发时间.目前,该扩展模块的主要功能已基本完成,下一步工作将集中在完善动态类型转换机制以及大量复杂实例的测试上.

参考文献:

[1]PREZ F, GRANGER B E, HUNTER J D. Python: an ecosystem for scientific computing[J]. Comput Sci & Eng, 2011, 13(2): 1321.

[2]曹金凤, 王旭春, 孔亮. Python语言在Abaqus中的应用[M]. 北京: 机械工业出版社, 2011: 810.

[3]CHUN W J. Python 核心编程[M]. 宋吉广, 译. 2版. 北京: 人民邮电出版社, 2008: 47.endprint

[4]李勇, 王文强. Python Web开发学习实录[M]. 北京: 清华大学出版社, 2011: 2.

[5]van ROSSUM G. Extending and embedding Python, Release 2.7.7[EB/OL]. (20121029)[20130610]. https://docs.python.org/2.7/extending/index.html.

[6]BEAZLEY D M. Python: essential reference[M]. New Jersey: AddisonWesley, 2009: 591608.

[7]罗霄, 任勇, 山秀明. 基于 Python 的混合语言编程及其实现[J]. 计算机应用与软件, 2004, 21(12): 1718.

LUO Xuiao, REN Yong, SHAN Xiuming. Python based mixedlanguage programming and its implementation[J]. Comput Appl & Software, 2004, 21(12): 1718.

[8]张洪武, 陈飙松, 李云鹏, 等. 面向集成化CAE软件开发的SiPESC研发工作进展[J]. 计算机辅助工程, 2011, 20(2): 3949.

ZHANG Hongwu, CHEN Biaosong, LI Yunpeng, et al. Advancement of design and implementation of SiPESC for development of integrated CAE software systems[J]. Comput Aided Eng, 2011, 20(2): 3949.

[9]SUGANUMA T, OGASAWARA T, TAKEUCHI M, et al. Overview of the IBM Java justintime compiler[J]. IBM sys J, 2000, 39(1): 175193.

[10]闫伟, 谷建华. Java虚拟机即时编译器的一种实现原理[J]. 微处理机, 2007, 28(5): 5860.

YAN Wei, GU Jianhua. One of JIT compiler's implementation in JVM[J]. Microprocessors, 2007, 28(5): 5860.

(编辑武晓英)endprint

[4]李勇, 王文强. Python Web开发学习实录[M]. 北京: 清华大学出版社, 2011: 2.

[5]van ROSSUM G. Extending and embedding Python, Release 2.7.7[EB/OL]. (20121029)[20130610]. https://docs.python.org/2.7/extending/index.html.

[6]BEAZLEY D M. Python: essential reference[M]. New Jersey: AddisonWesley, 2009: 591608.

[7]罗霄, 任勇, 山秀明. 基于 Python 的混合语言编程及其实现[J]. 计算机应用与软件, 2004, 21(12): 1718.

LUO Xuiao, REN Yong, SHAN Xiuming. Python based mixedlanguage programming and its implementation[J]. Comput Appl & Software, 2004, 21(12): 1718.

[8]张洪武, 陈飙松, 李云鹏, 等. 面向集成化CAE软件开发的SiPESC研发工作进展[J]. 计算机辅助工程, 2011, 20(2): 3949.

ZHANG Hongwu, CHEN Biaosong, LI Yunpeng, et al. Advancement of design and implementation of SiPESC for development of integrated CAE software systems[J]. Comput Aided Eng, 2011, 20(2): 3949.

[9]SUGANUMA T, OGASAWARA T, TAKEUCHI M, et al. Overview of the IBM Java justintime compiler[J]. IBM sys J, 2000, 39(1): 175193.

[10]闫伟, 谷建华. Java虚拟机即时编译器的一种实现原理[J]. 微处理机, 2007, 28(5): 5860.

YAN Wei, GU Jianhua. One of JIT compiler's implementation in JVM[J]. Microprocessors, 2007, 28(5): 5860.

(编辑武晓英)endprint

[4]李勇, 王文强. Python Web开发学习实录[M]. 北京: 清华大学出版社, 2011: 2.

[5]van ROSSUM G. Extending and embedding Python, Release 2.7.7[EB/OL]. (20121029)[20130610]. https://docs.python.org/2.7/extending/index.html.

[6]BEAZLEY D M. Python: essential reference[M]. New Jersey: AddisonWesley, 2009: 591608.

[7]罗霄, 任勇, 山秀明. 基于 Python 的混合语言编程及其实现[J]. 计算机应用与软件, 2004, 21(12): 1718.

LUO Xuiao, REN Yong, SHAN Xiuming. Python based mixedlanguage programming and its implementation[J]. Comput Appl & Software, 2004, 21(12): 1718.

[8]张洪武, 陈飙松, 李云鹏, 等. 面向集成化CAE软件开发的SiPESC研发工作进展[J]. 计算机辅助工程, 2011, 20(2): 3949.

ZHANG Hongwu, CHEN Biaosong, LI Yunpeng, et al. Advancement of design and implementation of SiPESC for development of integrated CAE software systems[J]. Comput Aided Eng, 2011, 20(2): 3949.

[9]SUGANUMA T, OGASAWARA T, TAKEUCHI M, et al. Overview of the IBM Java justintime compiler[J]. IBM sys J, 2000, 39(1): 175193.

[10]闫伟, 谷建华. Java虚拟机即时编译器的一种实现原理[J]. 微处理机, 2007, 28(5): 5860.

YAN Wei, GU Jianhua. One of JIT compiler's implementation in JVM[J]. Microprocessors, 2007, 28(5): 5860.

(编辑武晓英)endprint