Python Example - Adding Visualisation with Plots
Navigation: User Guide ➔ COM Automation ➔ Python Automation ➔ Python Example - Adding Visualisation with Plots
| Python Setup | Python Examples | Python Script | Optimisation | Visulisation | Python GUI | Tags and Data | |||
|---|---|---|---|---|---|---|---|---|---|
| Installation & Troubleshooting | List of Examples | Simple Script (pywin32) | SysCAD COM Python Class | Automated Model Testing | Constrained FEM (numpy|matplotlib) | Optimisation (numpy|scipy) | Adding Plots (numpy|matplotlib) | Dynamic with GUI | Accessing Data (sqlite3|pandas) |
Python has lots of powerful libraries for data manipulation, plotting, and analysis. These are some examples of what can be done
Adding visualisation with Plots
As we run scenarios, it results are best presented using plots. Here we will discuss how to add visualisation to some example projects already set up to run scenarios.
Gold Example Project
The SysCADCom class has a useful function to run a series of scenarios and collect the values of any number of tags.
The project was run across a range of feed rates. From the resulting plot, it is evident that the current setup is not suitable for higher flow rates. To accommodate these higher flow rates, a larger tank volume is required.
You may consider re-running the scenario with an increased tank volume in the CSTR configuration and reviewing the updated results.
Washer Example
Here is the Washer Example Project showing the NaOH concentration profiles.
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
sc = syscadif.SysCADCom()
ScdDir = r'C:\SysCAD%d' % sc.build
ScdPrj = r'\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.OpenProject(ScdDir + ScdPrj)
sc.run()
t0()
sc.CloseProject()
sc.Close()
|
Sensitivity Analysis Example
Here is the Sensitivity Analysis example done with python.
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. (See left images below.)
import syscadif
import numpy as np
import matplotlib.pyplot as plt
sc = syscadif.SysCADCom()
ScdDir = r'C:\SysCAD%d' % sc.build
ScdPrj = r'\Examples\65 Smelting\Copper Flash Furnace Example.spf\Project.spj'
sc.OpenProject(ScdDir+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()
|
|
|
If we just want to create a 2D plot like the original example (see right image above)
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")
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.
Plotting PSD Data
Plotting Data in Log Scale
In this next example, we’ve copied seven columns of data. Column 1 represents the size fractions. Columns 2 to 4 contain the particle size distributions (PSD) for the Feed, Overflow (OF), and Underflow (UF) streams. Columns 5 to 7 present the Fe₂O₃ PSD for the Feed, OF, and UF respectively.
Sub Plots
Size distribution data can vary widely, and when dealing with multiple ore types and process streams, there are many ways to visualize it. In this example, we have three columns of data—each representing size measurements for an ore species across three streams. The dataset contains 60 rows in total, with 20 data points per stream. We can visualize this data either by stream or by ore species.
Plotting in 3D
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.
Plotting Thermophysical Data
We can generate thermophysical data using Species Property reports when viewing species data: here we select all the different phases of a particular species and plot the specific heat, entropy, enthalpy and Gibbs free energy. The data is put in the clipboard, and it is easy to access the data directly from the clipboard and plot it:
This script demonstrates accessing the clipboard, using numpy to read blocks of text data into arrays, and then plotting this, along with labels and legends.
In particular the Gibbs function curves for the phases intersect at the melting and boiling points. At low temperatures the solid (cr) phase has the lowest free energy, and as the temperature increases, the liquid and then vapour phases have lower free energy.
Plotting Species Flows
As well as the usual plots we can create bar or pie charts. The Qo tab on any stream has the option to copy the species to the clipboard, so we can create a chart showing the species flows.
- This shows using structured arrays in numpy, which is useful when we have mixed data types, in this case string data for the species, and numerical data for the flows.
- The notation v[v>0] is showing use of an index array.
- v>0 is an array of flags indicating which elements of the array v are non-zero.
>>> v>0
array([ True, True, True, True, True, True, True, True, False,
True, False, True, False, False, False, False, False, False,
False, False, False, False, False, False, False, False, False,
False, False, False, False, False, False, True, False, False,
False, False, False, False, False, False, False])
- When we use an array as an index, it returns just the elements for which the index array are True, i.e. the non-zero elements of the original array.
>>> v[v>0]
array([3.75694246e+02, 1.76991503e+02, 2.72699554e+01, 1.28469206e+01,
6.32469251e-01, 2.60232738e+00, 5.87385513e-01, 1.45618792e+01,
5.32841733e-02, 2.42947148e+01, 4.20754018e-02])
- Similarly res['species'] is an array of strings having the species names, while res['species'][v>0] is an array of names of species with non-zero flow.
- We plot this on a logarithmic scale to show relative magnitudes of the flows of the species.




