Python Example - Getting Started

From SysCAD Documentation
Jump to navigation Jump to search

Navigation: User Guide ➔ COM Automation ➔ Python Automation ➔ Example - Basic Usage

Python Setup Python Examples
Installation &
Troubleshooting
Python
Utilities
Basic Usage & Scenarios Constrained FEM
(numpy|scipy|matplotlib)
Optimisation
(COM | scipy tools)
Model Testing
Framework
Dynamic
with GUI
Dynamic
External DLL
Programmatic
Model Generation
Importing data
to SysCAD

Basic Usage: Opening, running, setting and getting tags

This basic example opens up the Demo Gold steady state ProBal project, looks at the amount of gold in the product, changes the feed composition tag, runs the project, then looks at the new value of gold in product.

## Example Automation Using python.
import sys
import time
try:
  import win32com.client as wc    ## PyWin32 COM Client
except:
  print ("Win32 python extensions not found")
  print ("Install pywin32:  https://pypi.org/project/pywin32/")
  sys.exit(1)

ProgID = "SysCADSimulator93.Application"
SysCAD = wc.DispatchEx(ProgID)  ## Fire up SysCAD
build = SysCAD.VersionNumber(2) ## You can now query the build number

ScdDir = r'C:\SysCAD%d' % build
print ("Using Build", build, "Install Directory is ", ScdDir)

ScdPrj = r'\Examples\25 Gold\Demo Gold Project.spf\Project.spj'
#### If you use \ in file and directory strings use a raw string or escape \'s 
#### e.g. Prj = SysCAD.OpenProject('C:\\SysCADProjectFiles\\Sample.spf\\Project.spj')
####    Prj = SysCAD.OpenProject('C:/SysCADProjectFiles/Sample.spf/Project.spj')  ## also works

Prj = SysCAD.OpenProject(ScdDir+ScdPrj)  ## Open project 

Tags = Prj.Tags         
#### The tag naming was changed in the distributed examples for 138
goldFlow = "P_007.Qo.QM.Au(s) (oz/d)"
print ("Gold in product", end=" ")
print (Tags.TagValue(goldFlow))  ## Get a tag value

Solver = Prj.Solver
ProBal = Solver.ProBal

ProBal.Start()                        ## Start ProBal
while True:                           ## Wait until solved 
   print ("Solving...")                 
   time.sleep(.5)
   if ProBal.IsStopped:
       break
print ("Feed ppm in gold ore", Tags.TagValue("FEED_ppm_SET.TC[1].User.Value"))   ## Print another tag
print ("Changing value to 45 ppm")
Tags.SetTagValue("FEED_ppm_SET.TC[1].User.Value", 45)  ## Change tag value
ProBal.Start()          ## and re-solve
while True:
   print ("Solving...")
   time.sleep(.5)
   if ProBal.IsStopped:
       break
print ("Gold in product", end = " ")
print (Tags.TagValue(goldFlow))   ## Print the same tag
 
SysCAD.CloseProject(False)
##
time.sleep(5)
del(Solver)
del(Tags)
del(Prj)
del(SysCAD)

The output will be something like:

Using Build 138 Install Directory is  C:\SysCAD138
Gold in product 57.6095546633
Solving...
Feed ppm in gold ore 40.0
Changing value to 45 ppm
Solving...
Gold in product 64.8079746073

See Notes for general information and troubleshooting on using SysCAD COM with Python.

When using COM you create a number of COM objects representing various features of SysCAD: a Project object (Prj), a Tags object, a Solver object and so on. Keeping track of these (and cleaning up afterwards) is a mess, and we can simplify this by encapsulating all the functionality in a single python class which we will use for subsequent examples. We assume all the necessary packages are installed: if you get errors when importing a particular package such as numpy, you will need to install it to continue.

Encapsulating COM functionality in a python class

It is a good idea to create a python class which encapsulates all the functionality needed in once place. Once you have the class, you can reuse it. The syscadif.py module in the code in the link encapsulates basic SysCAD COM functionality in a python class SysCADCom, and we will use it in subsequent examples. You need to save this file either in the working directory (where the python script is located), or somewhere on your PYTHONPATH

SysCAD COM Interface Class

Some methods

Method Description
sc = SysCADCom() Create a SysCADCom class instance.
OpenProject(file) Open a SysCAD project
CloseProject() Close the current project
Close(): Close SysCAD and clean up COM objects
getTag(str) Get the value of tag str
setTag(str, val) Get the value of tag str to val
getTags(tagLis) Get the value of tags in list or iterable
setTags(tagLis, vals) Set the value of tags in list to items in vals
run() Run a Probal model
runScenarios(Vals, TagsIn, TagsOut) Run scenarios setting a single or mutiple tags


We also implement __getitem__ and __setitem__ methods for the class, so we can use shorthand notation similar to PGM for getting and setting tags:

flow = sc["P_007.Qo.QM.Au(s) (oz/d)"] ## or just flow = sc.getTag("P_007.Qo.QM.Au(s) (oz/d)") ## 
sc["$Solver.Cmd.CompleteReset"] = 1  ## equivalent to sc.setTag("$Solver.Cmd.CompleteReset", 1)

Not all COM methods are implemented directly via the class but you still have access to these via the member attributes:

sc.SysCAD.CreateProject(cfg, prj)     ## Create a new project


Using the SysCADCom class, the example in the first section now becomes

## Example Automation Using python.
import syscadif  ## SysCAD COM interface class

sc = syscadif.SysCADCom()
ScdDir = r'C:\SysCAD%d' % sc.build
print ("Using Build", sc.build, "Install Directory is ", ScdDir)
ScdPrj = r'\Examples\25 Gold\Demo Gold Project.spf\Project.spj'
sc.OpenProject(ScdDir+ScdPrj)  ## Open project 
goldFlowTag = "P_007.Qo.QM.Au(s) (oz/d)"

sc.run()
print ("Feed ppm in gold ore", sc["FEED_ppm_SET.TC[1].User.Value"])   ## Print another tag
print ("Gold in product", sc[goldFlowTag])
input ("Hit any key to change a tag, model reset and rerun")
sc["$Solver.Cmd.CompleteReset"] = 1
print ("Changing value to 45 ppm")
sc["FEED_ppm_SET.TC[1].User.Value"] = 45  ## Change tag value
sc.run()          ## and re-solve
print ("Gold in product", sc[goldFlowTag])
input ("Hit any key to finish")
 
sc.CloseProject()
sc.Close()

Further Usage: Scenarios, plots, etc.

Running a series of scenarios

Extending on the first example, we can easily make plots:

Comexample1.png

The SysCADCom class has a useful function to run a series of scenarios and collect the values of any number of tags

import syscadif
import matplotlib.pyplot as plt

sc = syscadif.SysCADCom()
ScdDir = r'C:\SysCAD%d' % sc.build
ScdPrj = r'\Examples\25 Gold\Demo Gold Project.spf\Project.spj'
 
sc.OpenProject(ScdDir+ScdPrj)  ## Open project 

X = list(range(40, 85, 5))  ## for Python3 since since range() is now a generator and we need to reuse X
Y = sc.RunScenarios(X, "FEED_ppm_SET.TC[1].User.Value", ["P_007.Qo.QM.Au(s) (oz/d)", "P_011.Qo.QM.Au(s) (oz/d)"])
Z = [y[0] for y in Y]  # flatten
Z1 = [y[1] for y in Y]
plt.plot(X, Z, label="Production")
plt.plot(X, Z, "ro")
plt.plot(X, Z1, label = "Recycle in P_11")
plt.xlabel("Ore Gold Concentration (ppm)")
plt.ylabel("Gold Production (oz/day)")
plt.legend(loc=2)
plt.show()
 
sc.CloseProject()
sc.Close()

Sensitivity Analysis Example

Here is the Sensitivity Analysis example done with python

import syscadif
import numpy as np
import matplotlib.pyplot as plt
 
 
sc = syscadif.SysCADCom()
ScdPrj = r'C:\SysCAD139\Examples\65 Smelting\Copper Flash Furnace Example.spf\Project.spj'
sc.OpenProject(ScdPrj)  ## Open project 

VarTags = {"Cu_Concentrate.QmReqd (t/h)": (62, 100, 2), 
           "Controllers.Cfg.[2].Spt":     (1240, 1320, 4),
           ## add further variables as needed Tagstr: (vmin, vmax, npts)
           }
SensTags = ["P_005.Qm (t/h)", "$Solver.Convergence.Iterations"]

V  = tuple(np.linspace(*v) for v in VarTags.values())
mgrid  = np.meshgrid(*V)

@np.vectorize
def f(*U):
    '''Funtion to set primary variables, run SysCAD, and return sensitivity variable'''
    for u, t in zip(U, VarTags.keys()):
        sc[t] = u
    sc.run()
    return tuple(sc[t] for t in SensTags)
 
## Now evaluate the SysCAD model over a n-dimensional grid (n is the number of VarTags)
## This returns a tuple of n-dimensional arrays, each being the values of the sensitivity variables at the gridpoints defined by meshgrid.
Z = f(*mgrid)  

X, Y = mgrid
fig = plt.figure()
ax = fig.add_subplot(1,2,1, projection='3d')
ax.plot_wireframe(X, Y, Z[0])
ax.set_xlabel("Primary Var")
ax.set_ylabel("Secondary Var")
ax.set_zlabel("Sensitivity Var")
ax = fig.add_subplot(1,2,2, projection='3d') 
ax.plot_wireframe(X, Y, Z[1])
ax.set_xlabel("Concentrate Flow (tph)")
ax.set_ylabel("Furnace Temperature (C)")
ax.set_zlabel("Iterations")
 
plt.show()
sc.Close()

We could run this as a set of scenarios, but for fun we use a numpy meshgrid to create the points at which we want to evaluate the model. We also create a vectorized function which is used to evaluate the model at each of the points. We use the resulting 2D array to plot a 3D graphic.


CuSensitivity.png

Even easier if we just want to create a 2D plot like the original example

for Y, v in zip(Z[0], V[1]):
    plt.plot(V[0], Y, label = ("%6.1f" % v))
plt.legend()
plt.title("Sensitivity Analysis")
plt.ylabel("Sensitive Variable")
plt.xlabel("Primary Variable")

CuSensitivity1.png


This example can easily be extended to more primary tags and sensitivity variables. Just add more items to the dictionary of primary tags (VarTags), and list of sensitivity tags. The code creates a meshgrid of the ranges, and call the vectorized function f on the meshgrid. This returns a tuple of the sensitivity variables as numpy arrays, which can be used for plotting or further analysis. This may involve a lot of evaluations of SysCAD!

If we had five primary variables, each to be evaluated at four points and returning 10 sensitivity variables, we would require [math]\displaystyle{ 4^5 = 1024 }[/math] evaluations of the flowsheet, and the vectorized function call would return 10240 data points.

Washer Example

Here is the Washer Example Project showing the NaOH concentration profiles.

WasherExample.png

Python Code      
import numpy as np
import matplotlib.pyplot as plt

import sys
sys.path.append(r"C:\SysCAD139\BaseFilesUser\Scripts")   ## syscadif.py is saved in a subdirectory of BaseFilesUser
import syscadif

scprj = r"C:\SysCAD139\Examples\03 UnitModels\Counter Current Washer Example.spf\Project.spj"

WashTags  = ["CCW_CONTROL.CCW{}_Online",
             "WASHER_CONTROL.CCD00{}_Online",
             "WASHER_CONTROL.CCD10{}_Online",
             "TH_CONTROL.TH{}_Online"]
OutTags = ["CCW_CONTROL.UF{}_NaOHConc (g/L)",
           "WASHER_CONTROL.UF00{}_NaOHConc (g/L)",
           "WASHER_CONTROL.UF10{}_NaOHConc (g/L)", 
           "TH_CONTROL.UF{}_NaOHConc (g/L)"]

def setStages(ion):
    '''Set all the on/off states for washers'''
    for i in range(2, 7):
        for tg in WashTags:
            sc[tg.format(i)] = (i<ion)

def getData():
    return np.array([sc[tg.format(i)] for tg in OutTags for i in range(1, 7)])
    
def t0():
    plt.ion()
    fig, axs = plt.subplots(1, 4, sharey=True, tight_layout=True)

    for i in range(2, 7):
        setStages(i)
        sc.run()
        dd = getData().reshape((4, 6))
        for ax, dat in zip(axs, dd):
            ax.plot(dat, label=f"{i} Stages")
            ax.plot(dat, "ko")   
    for ax in axs: ax.legend()
    for ax, ss in zip(axs, ["CC Washer", "Washer (Bypass to OF)", "Washer (Bypass to UF)", "Thickener"]):
        ax.set_title(ss)
    axs[0].set_ylabel("NaOH Conc (gpl)")
    plt.suptitle("NaOH Profiles")
    plt.savefig("fig4.png")
    plt.show()

sc = syscadif.SysCADCom()
sc.OpenProject(scprj)
sc.run()

t0()

sc.CloseProject()
sc.Close()

More 3D plots

This example pulls particle size distributions data for a series of pipes in the Distributed Precipitation and Classification Example without ever running SysCAD. It then makes a 3d plot showing the variation in PSD curve along the tank row.

import syscadif  ## SysCAD COM interface class
import numpy as np
import matplotlib.pyplot as plt


sc = syscadif.SysCADCom()
ScdDir = r'C:\SysCAD%d' % sc.build
ScdPrj = r'\Examples\10 Alumina\PSD Precipitation & Classification Example.spf\Project.spj'
 
sc.OpenProject(ScdDir+ScdPrj)  ## Open project 
pipelist = ["P_115_", "P_116_"]+["P_0%02d" % i for i in range(4, 24, 2)]+["P_121"]
sizetags = [".Qo.Sz.I%d.FP.Al[OH]3(s) (%%)" % i for i in range(28)]
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
X, Y = np.meshgrid(np.arange(len(pipelist)),
                  np.arange(len(sizetags)))
Z = np.array([ [sc[pipe+s] for pipe in pipelist] for s in sizetags])
ax.plot_wireframe(X, Y, Z)
plt.show()

Comexample.png