簡體   English   中英

兩個獨立的Python引擎之間的通信

[英]Communication between two separate Python engines

問題陳述如下:

我正在與Abaqus合作,這是一個分析機械問題的程序。 它基本上是一個獨立的Python解釋器,有自己的對象等。在這個程序中,我運行一個python腳本來設置我的分析(所以這個腳本可以被修改)。 它還包含一種在接收外部信號時必須執行的方法。 這些信號來自我在自己的Python引擎中運行的主腳本。

現在,我有以下工作流程:當Abaqus腳本必須執行特定功能時,主腳本將布爾值設置為True,並將此布爾值選取到文件中。 Abaqus腳本定期檢查此文件以查看布爾值是否已設置為true。 如果是這樣,它會進行分析並對輸出進行pickle,以便主腳本可以讀取此輸出並對其進行操作。

我正在尋找一種更有效的方式來通知其他進程開始分析,因為有很多不必要的檢查正確知道。 通過泡菜進行數據交換對我來說不是問題,但更有效的解決方案當然是受歡迎的。

搜索結果總是給我帶有子進程等的解決方案,這是針對在同一個解釋器中啟動的兩個進程。 我也看過ZeroMQ,因為這應該可以達到這樣的目的,但是我覺得這樣做太過分了,想要在python中找到解決方案。 兩個解釋器都運行python 2.7(雖然版本不同)

編輯:

像@MattP一樣,我將添加我的理解陳述:

背景

我相信您正在運行名為abaqus的產品。 abaqus產品包括一個鏈接的python解釋器,你可以通過某種方式訪問​​(可能通過在命令行上運行abaqus python foo.py )。

您還可以在同一台計算機上安裝單獨的python。 您正在開發代碼,可能包括numpy / scipy,以在該python安裝上運行。

這兩個安裝是不同的:它們具有不同的二進制解釋器,不同的庫,不同的安裝路徑等。但它們存在於同一物理主機上。

您的目標是啟用由您編寫的“普通python”程序與在“Abaqus python”環境中運行的一個或多個腳本進行通信,以便這些腳本可以在Abaqus系統內執行工作並返回結果。

這是基於套接字的解決方案。 有兩個部分, abqlistener.pyabqclient.py 這種方法的優點是它使用明確定義的機制來“等待工作”。 沒有文件輪詢等。它是一個“硬”API。 您可以從同一台機器上的進程,運行相同版本的python,或從不同的機器,或從不同版本的python,或從ruby或C或perl甚至COBOL連接到偵聽器進程。 它允許您在系統中放置一個真正的“氣隙”,這樣您就可以用最小的耦合來開發這兩個部件。

服務器部分是abqlistener 目的是您將部分代碼復制到Abaqus腳本中。 然后,abq進程將成為服務器,偵聽特定端口號上的連接,並作為響應進行工作。 是否發送回復。 等等。

我不確定您是否需要為每項工作進行設置工作。 如果是這樣,那就必須是連接的一部分。 這只會啟動ABQ,監聽端口(永遠),並處理請求。 任何特定於工作的設置都必須是工作流程的一部分。 (也許發送參數字符串,或配置文件的名稱,或其他。)

客戶端部分是abqclient 這可以移動到模塊中,或者只是復制/粘貼到現有的非ABQ程序代碼中。 基本上,您打開與正確的主機:端口組合的連接,並且您正在與服務器通信。 發送一些數據,獲取一些數據等。

這些東西大多是從在線示例代碼中刪除的。 如果你開始挖掘任何東西,它應該看起來真的很熟悉。

這是abqlistener.py:

# The below usage example is completely bogus. I don't have abaqus, so
# I'm just running python2.7 abqlistener.py [options]
usage = """
abacus python abqlistener.py [--host 127.0.0.1 | --host mypc.example.com ] \\
        [ --port 2525 ]

Sets up a socket listener on the host interface specified (default: all
interfaces), on the given port number (default: 2525). When a connection
is made to the socket, begins processing data.
"""



import argparse

parser = argparse.ArgumentParser(description='Abacus listener',
    add_help=True,
    usage=usage)

parser.add_argument('-H', '--host', metavar='INTERFACE', default='',
                    help='Interface IP address or name, or (default: empty string)')
parser.add_argument('-P', '--port', metavar='PORTNUM', type=int, default=2525,
                    help='port number of listener (default: 2525)')

args = parser.parse_args()

import SocketServer
import json

class AbqRequestHandler(SocketServer.BaseRequestHandler):
    """Request handler for our socket server.

    This class is instantiated whenever a new connection is made, and
    must override `handle(self)` in order to handle communicating with
    the client.
    """

    def do_work(self, data):
        "Do some work here. Call abaqus, whatever."
        print "DO_WORK: Doing work with data!"
        print data
        return { 'desc': 'low-precision natural constants','pi': 3, 'e': 3 }

    def handle(self):
        # Allow the client to send a 1kb message (file path?)
        self.data = self.request.recv(1024).strip()
        print "SERVER: {} wrote:".format(self.client_address[0])
        print self.data
        result = self.do_work(self.data)
        self.response = json.dumps(result)
        print "SERVER: response to {}:".format(self.client_address[0])
        print self.response
        self.request.sendall(self.response)


if __name__ == '__main__':
    print args
    server = SocketServer.TCPServer((args.host, args.port), AbqRequestHandler)
    print "Server starting. Press Ctrl+C to interrupt..."
    server.serve_forever()

這是abqclient.py

usage = """
python2.7 abqclient.py [--host HOST] [--port PORT]

Connect to abqlistener on HOST:PORT, send a message, wait for reply.
"""

import argparse

parser = argparse.ArgumentParser(description='Abacus listener',
    add_help=True,
    usage=usage)

parser.add_argument('-H', '--host', metavar='INTERFACE', default='',
                    help='Interface IP address or name, or (default: empty string)')
parser.add_argument('-P', '--port', metavar='PORTNUM', type=int, default=2525,
                    help='port number of listener (default: 2525)')

args = parser.parse_args()

import json
import socket

message = "I get all the best code from stackoverflow!"

print "CLIENT: Creating socket..."
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

print "CLIENT: Connecting to {}:{}.".format(args.host, args.port)
s.connect((args.host, args.port))

print "CLIENT: Sending message:", message
s.send(message)

print "CLIENT: Waiting for reply..."
data = s.recv(1024)

print "CLIENT: Got response:"
print json.loads(data)

print "CLIENT: Closing socket..."
s.close()

這是他們一起運行時打印的內容:

$ python2.7 abqlistener.py --port 3434 &
[2] 44088
$ Namespace(host='', port=3434)
Server starting. Press Ctrl+C to interrupt...

$ python2.7 abqclient.py --port 3434
CLIENT: Creating socket...
CLIENT: Connecting to :3434.
CLIENT: Sending message: I get all the best code from stackoverflow!
CLIENT: Waiting for reply...
SERVER: 127.0.0.1 wrote:
I get all the best code from stackoverflow!
DO_WORK: Doing work with data!
I get all the best code from stackoverflow!
SERVER: response to 127.0.0.1:
{"pi": 3, "e": 3, "desc": "low-precision natural constants"}
CLIENT: Got response:
{u'pi': 3, u'e': 3, u'desc': u'low-precision natural constants'}
CLIENT: Closing socket...

參考文獻:

argparseSocketServerjsonsocket都是“標准”Python庫。

為了清楚abq.py ,我的理解是你通過Python腳本運行Abaqus / CAE作為一個獨立的進程(讓我們稱之為abq.py ),它檢查,打開和讀取觸發器文件以確定它是否應運行分析。 觸發器文件由第二個Python進程創建(我們稱之為main.py )。 最后, main.py等待讀取abq.py創建的輸出文件。 您需要一種更有效的方式來通知abq.py運行分析,並且您可以使用不同的技術來交換數據。

如您所述,子進程或多處理可能是一種選擇。 但是,我認為更簡單的解決方案是組合兩個腳本,並可選擇使用回調函數來監視解決方案並處理輸出。 我假設不需要將abq.py作為一個單獨的進程持續運行,並且只要合適,所有分析都可以從main.py開始。

main.py可以訪問Abaqus Mdb。 如果它已經構建,您可以使用以下命令打開它:

mdb = openMdb(FileName)

如果main.py啟動所有分析,則main.py觸發器文件。 例如:

if SomeCondition:
    j = mdb.Job(name=MyJobName, model=MyModelName)
    j.submit()
    j.waitForCompletion()

完成后, main.py可以讀取輸出文件並繼續。 如果數據文件是由分析本身生成的(例如.dat.odb文件),這很簡單。 OTH,如果輸出文件是由當前abq.py的某些代碼生成的,那么您可以將其包含在main.py

如果沒有提供足夠的控制,則可以向monitorManager對象添加回調函數(而不是waitForCompletion方法)(在導入abaqus模塊時自動創建: from abaqus import * )。 這允許您監視和響應來自求解器的各種消息,例如COMPLETEDITERATION等。回調函數定義如下:

def onMessage(jobName, messageType, data, userData):
    if messageType == COMPLETED:
        # do stuff
    else:
        # other stuff

然后將其添加到monitorManager並調用作業:

monitorManager.addMessageCallback(jobName=MyJobName,  
    messageType=ANY_MESSAGE_TYPE, callback=onMessage, userData=MyDataObj)
j = mdb.Job(name=MyJobName, model=MyModelName)
j.submit()

這種方法的一個好處是您可以將Python對象作為userData參數傳遞。 這可能是您的輸出文件或其他一些數據容器。 您可能想知道如何在回調函數中處理輸出數據 - 例如,訪問Odb並獲取數據,然后根據需要進行任何操作而根本不需要外部文件。

我同意答案,除了一些小的語法問題。

在處理程序中定義實例變量是否定的。 更不用說它們沒有在任何類型的init ()方法中定義。 子類TCPServer並在TCPServer中定義您的實例變量。 init ()。 其他一切都會起作用。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM