两个独立的Python引擎之间的通信

时间:2017-04-10 23:14:19

标签: python python-2.7 abaqus

问题陈述如下:

我正在与Abaqus合作,这是一个分析机械问题的程序。它基本上是一个独立的Python解释器,有自己的对象等。在这个程序中,我运行一个python脚本来设置我的分析(所以这个脚本可以被修改)。它还包含一种在接收外部信号时必须执行的方法。这些信号来自我在自己的Python引擎中运行的主脚本。

目前,我有以下工作流程: 当Abaqus脚本必须执行特定功能时,主脚本将布尔值设置为True,并将此布尔值选取到文件中。 Abaqus脚本定期检查此文件以查看布尔值是否已设置为true。如果是这样,它会进行分析并对输出进行pickle,以便主脚本可以读取此输出并对其进行操作。

我正在寻找一种更有效的方式来通知其他进程开始分析,因为正确知道有很多不必要的检查。通过泡菜进行数据交换对我来说不是问题,但更有效的解决方案当然是受欢迎的。

搜索结果总是给我带有子进程等的解决方案,这是针对在同一个解释器中启动的两个进程。我也看过ZeroMQ,因为这应该可以达到这样的目的,但是我觉得这样做太过分了,想要在python中找到解决方案。两个解释器都运行python 2.7(虽然版本不同)

3 个答案:

答案 0 :(得分:4)

修改

与@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库。

答案 1 :(得分:0)

要清楚,我的理解是您通过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启动所有分析,则不需要触发器文件。例如:

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

完成后,main.py可以读取输出文件并继续。如果数据文件是由分析本身生成的(例如.dat.odb文件),则这很简单。 OTH,如果输出文件是由当前abq.py中的某些代码生成的,那么您可以将其包含在main.py中。

如果没有提供足够的控制,您可以向monitorManager对象(在导入abaqus模块时自动创建)中添加回调函数,而不是waitForCompletion方法: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并获取数据,然后根据需要进行任何操作,而根本不需要外部文件。

答案 2 :(得分:0)

我同意答案,除了一些小的语法问题。

在处理程序中定义实例变量是否定的。更不用说它们没有在任何 init ()方法中定义。子类TCPServer并在TCPServer中定义您的实例变量。 init ()。其他一切都会起作用。