Connecting to COCO from MATLAB

Discusses use of COCO, the process simulation and modelling software suite from AmsterCHEM, downloadable from www.cocosimulator.org

Moderator: jasper

Re: Connecting to COCO from MATLAB

Postby cocofan » 12 April 2020, 17:32

Thanks @jasper for your suggestion regarding Python. It sounds pretty convenient and I'll look into it. As for Cofestand, the clock time is 7-8 s on average for each call (not just the first one), although the cputime shows only ~2 s. I disabled the antivirus and didn't get any noticeable changes. I'm running on a Windows platform and Intel CPU i5-6200U @ 2.3 GHz 2 Cores 4 logical processors and 8 GB of RAM.
Code: Select all
Exepath=["C:\\Progra~1\\COCO\\cofestand64"];
t=cputime;
tic
[STATUS]=system([Exepath " compressors.fsd"],RETURN_OUTPUT);
toc
t_run=cputime-t
cocofan
 
Posts: 11
Joined: 24 March 2020, 02:36

Re: Connecting to COCO from MATLAB

Postby jasper » 13 April 2020, 08:10

Strange - for me the turn-around time of a call is slightly below a second. But this is still very long compared to solving a compressor of course.

Let me know if you succeed with the COM binding via python - I have never looked into that.
User avatar
jasper
 
Posts: 935
Joined: 24 October 2012, 15:33
Location: Spain

Re: Connecting to COCO from MATLAB

Postby cocofan » 13 April 2020, 14:52

Sure. I'll post the results with Python once I get it to work. I tried calling cofestand from the command prompt (to avoid any overhead from Octave) and again it took the same amount of time. Here's the command line output in case it helps diagnose the issue (it's doing a bunch of stuff before starting to solve). Btw, would it be possible to suppress these outputs (e.g., when cofestand is embedded in another program)?
Code: Select all
Loading C:\Users\Octave\compressors.fsd
added stream type default
Restoring flowsheet from file
message: ChemSep/COPP v8.20 (bcf4e2bfbbc6 2020-4-6 01:56) Title= User=FA Date=2020-04-13 Time=19:10:48
added stream type default
message: ChemSep/COPP v8.20 (bcf4e2bfbbc6 2020-4-6 01:56) Title= User=FA Date=2020-04-13 Time=19:10:48
added stream type default
added stream type default
added stream type default
added stream type default
message: ChemSep/COPP v8.20 (bcf4e2bfbbc6 2020-4-6 01:56) Title= User=FA Date=2020-04-13 Time=19:10:48
message: ChemSep/COPP v8.20 (bcf4e2bfbbc6 2020-4-6 01:56) Title= User=FA Date=2020-04-13 Time=19:10:48
message: ChemSep/COPP v8.20 (bcf4e2bfbbc6 2020-4-6 01:56) Title= User=FA Date=2020-04-13 Time=19:10:48
message: ChemSep/COPP v8.20 (bcf4e2bfbbc6 2020-4-6 01:56) Title= User=FA Date=2020-04-13 Time=19:10:48
Starting solve
Solve: solving unit Compressor_1
Solve: solving unit HeaterCooler_2
Solve: solving unit Compressor_3
Solve finished
Saved compressors.fsd
Saved C:\Users\FA\Dropbox\AUT Teaching\Modeling-Simulation\Simulation97\Software\COCO\COCO_Octave\compressors.fsd
SOLVE: OK
cocofan
 
Posts: 11
Joined: 24 March 2020, 02:36

Re: Connecting to COCO from MATLAB

Postby jasper » 13 April 2020, 19:09

Not sure whether the Python route will work. Pythoncom's idea of early binding is looking up the types and dispIDs in the TLB, and then create a py class that calls DispInvoke. and the latter of course depends on a proper implementation of IDispatch, which is exactly why late binding does not work.

I suppose to get proper early binding (call the interface functions, instead of DispInvoke), one would have to generate some c- or cpp-based python classes. Probably not what you are looking for.
User avatar
jasper
 
Posts: 935
Joined: 24 October 2012, 15:33
Location: Spain

Re: Connecting to COCO from MATLAB

Postby jasper » 14 April 2020, 13:01

So - here's a solution.

As stated in a previous mail, pywin32 does not deal with custom interfaces using proper early binding. But the comtypes package does. However, this has a problem with passing VT_BSTR|VT_BYREF values. In addition, as opposed to pywin32, comtypes does not support creating an object by specifying the file name to coGetObject. So I added two methods to ICOFEDocument: Import, which takes a file name as BSTR, and Solve, which raises an exception in case of solution failure. So first - update COFE to 3.3.0.16 (using CUP)

Next, you will need the comtypes package. In Matlab, type

Code: Select all

pyenv



to figure out which python Matlab is running (in my system there is a whole nest of pythons, and Matlab runs 3.8, in contrast to the documentation https://www.mathworks.com/help/matlab/matlab_external/system-and-configuration-requirements.html that states 3.8 is not supported).

For the corresponding python, run

Code: Select all
pip install comtypes


Then save PLUM.py:

Code: Select all
import comtypes.client #requires pip install comtypes
import comtypes.gen

#PLUM Python Link Used by Matlab
#Jasper, 2020

#tell comtypes to load type libs
cofeTlb=('{0D1006C7-6086-4838-89FC-FBDCC0E98780}',1,0) #COFE type lib
cofeTypes=comtypes.client.GetModule(cofeTlb)
coTlb=('{4A5E2E81-C093-11D4-9F1B-0010A4D198C2}',1,1) #CAPE-OPEN v1.1 type lib
coTypes=comtypes.client.GetModule(coTlb)
#print(cofeTypes.__file__)

#variables
doc=None
sourceName=""
targetName=""
sourceParName=""
targetParName=""

#set up the problem
def setup(fileName,sourceUnitName,sourceParameterName,targetUnitName,targetParameterName):
    global doc,sourceName,targetName,sourceParName,targetParName
    #get and load the doc
    doc=comtypes.client.CreateObject('COCO_COFE.Document',interface=cofeTypes.ICOFEDocument)
    doc.Import(fileName) #'Import' requires COFE 3.3.0.16 or newer
    #save the names
    sourceName=sourceUnitName
    targetName=targetUnitName
    sourceParName=sourceParameterName
    targetParName=targetParameterName

def eval(parVal):
    global doc,sourceName,targetName,sourceParName,targetParName
    #when COFE is running in multi-theaded mode, unit operation interfaces cannot be saved across a call to Solve.
    doc.GetUnit(sourceName).QueryInterface(coTypes.ICapeUtilities).Parameters.QueryInterface(coTypes.ICapeCollection).Item(sourceParName).QueryInterface(coTypes.ICapeParameter).value=float(parVal)
    doc.Solve()
    return doc.GetUnit(targetName).QueryInterface(coTypes.ICapeUtilities).Parameters.QueryInterface(coTypes.ICapeCollection).Item(targetParName).QueryInterface(coTypes.ICapeParameter).value


From Matlab

Code: Select all
>> py.PLUM.setup('compressors.fsd','Compressor_1','Pressure ratio','Compressor_1','Energy demand')
>> py.PLUM.eval(4)


Let me know the performance of this one? The disadvantage of this setup compared to COFEStand is that you do not see the solver output (warnings, iteration history, ...)

For this particularly small flowsheet, the solution is probably considerably quicker if you turn off multi-threading from the COFE options dialog.
User avatar
jasper
 
Posts: 935
Joined: 24 October 2012, 15:33
Location: Spain

Re: Connecting to COCO from MATLAB

Postby jasper » 25 April 2020, 15:31

Is this solution working for you?
User avatar
jasper
 
Posts: 935
Joined: 24 October 2012, 15:33
Location: Spain

Re: Connecting to COCO from MATLAB

Postby cocofan » 08 May 2020, 20:45

Hi Jasper, thanks a lot for posting your solution & sorry for the delayed response! I was going to update the post after trying out your workaround, but unfortunately haven't had a chance to do so yet. I'll let you know as soon as I can test it.
cocofan
 
Posts: 11
Joined: 24 March 2020, 02:36

Re: Connecting to COCO from MATLAB

Postby jasper » 22 May 2020, 09:16

note that the recent COFE update contains some additional fixes.
User avatar
jasper
 
Posts: 935
Joined: 24 October 2012, 15:33
Location: Spain

Re: Connecting to COCO from MATLAB

Postby Laurence » 23 June 2020, 12:08

Jasper has been helping me with connecting to COCO from python via the COM interface.
Below is some example python code implementing some common methods (get/set stream & unit parameters).

Code: Select all
import comtypes.client
import comtypes.gen
from comtypes import COMError
from comtypes.automation import VARIANT
import array
import os

cofeTlb=('{0D1006C7-6086-4838-89FC-FBDCC0E98780}',1,0)
cofeTypes=comtypes.client.GetModule(cofeTlb)
coTlb=('{4A5E2E81-C093-11D4-9F1B-0010A4D198C2}',1,1)
coTypes=comtypes.client.GetModule(coTlb)

# load the file
filename = 'Flowsheet1.fsd'
filename=os.path.join(os.getcwd(),filename)
doc=comtypes.client.CreateObject('COCO_COFE.Document',interface=cofeTypes.ICOFEDocument)
doc.Import(filename)

# print out all of the unit parameters
count = doc.GetUnit('Column_1').QueryInterface(coTypes.ICapeUtilities). \
    Parameters.QueryInterface(coTypes.ICapeCollection).Count()
for i in range(1, count+1):
    print(doc.GetUnit('Column_1').QueryInterface(coTypes.ICapeUtilities).Parameters. \
    QueryInterface(coTypes.ICapeCollection).Item(i).QueryInterface(coTypes.ICapeParameter). \
    QueryInterface(coTypes.ICapeIdentification).ComponentName)

# set inlet stream flowrates (also example of error handling)
try:
   doc.GetStream('1').QueryInterface(coTypes.ICapeThermoMaterial).SetOverallProp("flow", "mole", array.array('d', [300, 400, 500]))
except COMError:
   print("Error:", self.doc.GetStream('1').QueryInterface(coTypes.ECapeRoot).name)

# set some unit parameters
doc.GetUnit('Column_1').QueryInterface(coTypes.ICapeUtilities).Parameters. \
    QueryInterface(coTypes.ICapeCollection).Item("Number of stages").QueryInterface(coTypes.ICapeParameter).value = float(20)


doc.GetUnit('Column_1').QueryInterface(coTypes.ICapeUtilities).Parameters.QueryInterface(coTypes.ICapeCollection).Item(
            "Reflux ratio").QueryInterface(coTypes.ICapeParameter).value = float(5)

doc.GetUnit('Column_1').QueryInterface(coTypes.ICapeUtilities).Parameters.QueryInterface(coTypes.ICapeCollection).\
            Item("Reboil ratio").QueryInterface(coTypes.ICapeParameter).value = float(5)


doc.Solve()
# get an output parameter
print(f"""distillate flowrates: {doc.GetStream('2').QueryInterface(coTypes.ICapeThermoMaterial).GetOverallProp("flow", "mole")}""")
Attachments
Flowsheet1.fsd
COCO file
(27.22 KiB) Downloaded 23 times
Laurence
 
Posts: 1
Joined: 02 June 2020, 18:47

Previous

Return to COCO

Who is online

Users browsing this forum: No registered users and 4 guests

cron