陳飆松+李云鵬+陸旭澤
摘要: 基于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