# Class

Functions Sub Routines Classes
Defining a Function Predefined Functions Tag Functions Mathematical Functions String Functions Defining a Subroutine Trigger Subroutines Defining a Class

Related Links: User Defined Class and Functions

# What is "Class" and When should it be used

• A Class will allow a user to define a group of variables and functions/subroutines. The class definition is then used to make any number of "instances" or "objects" based on the class definition (or "class template").
• For example, a user may want to extend the functionality of the Tank and turn it into a complex type of tank (we have used "CSTR" in our example). To do this, the user will need to define functions to calculate the additional user variables.
• If the code is written without the use of class, the user will need to write the same code each time a CSTR tank is used. This can lead to repeating the same code a number of times depending on the number of CSTR tanks in the project. It is also difficult to maintain because any change in logic needs to be made in all repeated code.
• If the code is written in the form of a Class, the user can define all the CSTR Variables and Functions required as a "template" (class definition), then instruct SysCAD to use this template to create as many "instances" or "objects" of type CSTR, then use these to perform the CSTR calculations (n times) without the need to repeat the code.
• To aid the understanding of "Writing a Class" and to explain how to use the class function, a demonstration example project is distributed with the SysCAD install. Please see project Gold Project - CSTR pgm file. We will use this file to explain the use of Class in this document.

# How to Define a Class (Template of Variable and Functions)

## Syntax

 Class className

;Variable declaration(s)

;Sub(s) and Function(s)

EndClass


Defining a class:

• The class declaration must begin with the Class keyword and end with the EndClass keyword. See Class Declaration.
• A globally unique class type name must be provided className in above (and CSTR in example below). This is later used as a "data type" when declaring instances of classes of this class type.
• Variables can be declared within the class, their scope is limited to the class, and they are available to the instance of the class.
• When declaring variables in a class, the normal variable declaration rules will apply:
The user can define units;
Append * to make the variable visible and writeable in the access window; and/or
Append @ to make the variable visible in the Access window, but read only.
• Variables of type class can be declared within the class. These may be SysCAD PGM classes (eg Matrix Class) or user defined classes.
• Any number of functions (of type Sub, Function and StrFunction) can be declared within the class and they are available to the instance of the class. The sequence of functions and variables within a class can be mixed and arranged as required.
• Class definitions cannot be nested. (i.e. classes cannot be defined within the definition of a class)
• Various PGM language commands are unavailable within a class definition.

Special functions available within class functions and subs:

• ClassName() : returns a string of the class type name. In example below, this will return "CSTR".
• ClassTag() : returns a string of the class instance variable name (tag). This is a particularly useful function in when the class instance variable name matches a tag in the model. For example, we can use "UnitTag = ClassTag()" within an "Init Sub for the class. In example below, ClassTag() will return "Tank_A", "Tank_B" and "TankC".

Other functionality available for Classes:

• ForEachClass Macro Function: used to call subroutines for all instances of the class. See ForEachClass Function for more details.
• ExcludeClass Used to block class from use in ForEachClass. Commonly used to block instance at index 0 in an array of classes.

## Example PGM File

• A very condensed extract of the CSTR Class PGM file is presented below to explain the structure and use of the Class definition:
Class CSTR

;... Various Variable Definition.....
String     UnitTag{tag}*
Checkbox   ClassTag_is_UnitTag*<<1>>
Real       TankVolume*("Vol","m^3")<<300>>, CuLeachExtent*("Frac", "%")
;... Various Variable Definition.....

Sub Init()
If ClassTag_is_UnitTag
UnitTag = ClassTag()
Endif
[concatenate(UnitTag, ".ResTime.Volume (m^3)")] = TankVolume
EndSub

Sub CalculateCSTR()
;......Various Calculation Code .....
EndSub

Sub Exec()
;......Various Calculation Code .....
CalculateCSTR()
;......Various Calculation Code .....
[concatenate(UnitTag, ".RB.R3.Extent (%)")] = CuLeachExtent
EndSub

EndClass

1. The name of the Class (or template) is called CSTR.
2. All the variables that are associated with the CSTR are defined. (The full PGM file is available in the Gold distributed example project)
3. String tag "UnitTag" is defined here -
• User can either enter the actual Unit Model Tag name from the access window or
• define the class instance using the same name as the unit tag name, by ticking the "ClassTag_is_UnitTag" checkbox.
• the subroutine "Init" can also be used to initailise the class name if required.
4. Nested functions are allowed within a class, for example Sub "CalculateCSTR" is called inside the Sub "Exec"
5. In summary, the Class(template) "CSTR" contains a number of calculations and a number of Set Value Commands.

The full PGM code used in the Gold distributed example project is shown here:

 ;Calculates leach and adsorption PageLabel(Options) TextLabel(,"Options:") real TankVolume*("Vol", "m^3")<<300>> real CarbonConc*("Conc", "g/L") real Oxygen_Utilisation*<<0.5>>("Frac", "Frac") TextLabel(,"Leaching Rate Constants") real kAuLeach* real kCuLeach* TextLabel(,"Adsorption Rate Constants") real kAuAdsorb* real kCuAdsorb* ;real MWNaAuCN2, MWNaCuCN2, MWAu, MWCu ; This class is used to carry out the calculations for each tank Class CSTR TextLabel(," ------------------") Checkbox ClassTag_is_UnitTag*<<1>> Checkbox SetOxygen*<<1>> ;NOTE that O2 is added via makeup blocks, this code assumes each tank has been set up with MU1 using oxygen source String UnitTag{Tag}* String UnitTypeName@ real AuSolidsIn@{Comment("ppm")}, CuSolidsIn@{Comment("ppm")} real AuSolidsOut@("Frac", "ppm"), CuSolidsOut@("Frac", "ppm") real AuSolutionOut@("Conc", "mg/L"), CuSolutionOut@("Conc", "mg/L") real QSolids@("Qm", "t/h") real MassSolidsInTank@("M", "t") real O2_Required@("Qm", "kg/h") TextLabel() real AuCarbonIn@{Comment("ppm")}, CuCarbonIn@{Comment("ppm")} real AuCarbonOut@("Frac", "ppm"), CuCarbonOut@("Frac", "ppm") real QCarbon@("Qm", "t/h") real MassCarbonInTank@("M", "t") real Carbon, AuLeachExtent, CuLeachExtent, AuAdsorbExtent, CuAdsorbExtent ; This subroutine is only called once at start-up. Sub Init() If ClassTag_is_UnitTag UnitTag = ClassTag() Endif UnitTypeName = ClassName() [concatenate(UnitTag, ".ResTime.Volume (m^3)")] = TankVolume EndSub ; subroutine to calculate the leaching and Adsorption of gold and copper based on the user defined rate constants. Sub CalculateCSTR() ;Gold and Copper Leaching (first order, no cyanide or oxygen dependance) ;----------------------------------------------------------- AuSolidsOut = (QSolids*AuSolidsIn) / (QSolids + KAuLeach*MassSolidsInTank + 1e-20);added small value to prevent division by 0 CuSolidsOut = (QSolids*CuSolidsIn) / (QSolids + KCuLeach*MassSolidsInTank + 1e-20);added small value to prevent division by 0 ;Gold and Copper Adsorption onto carbon (film diffusion, no isotherm) ;----------------------------------------------------------- If QCarbon > 0 AuCarbonOut = ( QCarbon*AuCarbonIn + KAuAdsorb*AuSolutionOut*MassCarbonInTank ) / QCarbon CuCarbonOut = ( QCarbon*CuCarbonIn + KCuAdsorb*CuSolutionOut*MassCarbonInTank ) / QCarbon Endif AuLeachExtent = (AuSolidsIn - AuSolidsOut)/(AuSolidsIn + 1e-20)*100;added small value to prevent division by 0 CuLeachExtent = (CuSolidsIn - CuSolidsOut)/(CuSolidsIn + 1e-20)*100;added small value to prevent division by 0 [concatenate(UnitTag, ".RB.R7.CuonCarbon_ppm.Required")] = CuCarbonOut EndSub ; Executable subroutine - run every step for the CSTR tanks. Sub Exec() ; First measure all the flows into and out of the tank and calculate values in the tank. QSolids = [concatenate(UnitTag, ".QFeed.QM:IPh.Solids (t/h)")] QCarbon = [concatenate(UnitTag, ".QFeed.QM:IPh.c (t/h)")] MassSolidsInTank = TankVolume * [concatenate(UnitTag, ".QProd.Rho (t/m^3)")]*([concatenate(UnitTag, ".QProd.SQm (t/h)")] / ([concatenate(UnitTag, ".QProd.Qm (t/h)")] + 1e-20));added small value to prevent division by 0 MassCarbonInTank = iif(QCarbon > 0.01, TankVolume*CarbonConc/1000, 0) AuSolidsIn = [concatenate(UnitTag, ".QFeed.Calc.GoldinOre_ppm")] CuSolidsIn = [concatenate(UnitTag, ".QFeed.Calc.CuinOre_ppm")] Carbon = [concatenate(UnitTag, ".QFeed.QM:IPh.c (t/h)")] AuCarbonIn = [concatenate(UnitTag, ".QFeed.Calc.GoldonCarbon_ppm")] CuCarbonIn = [concatenate(UnitTag, ".QFeed.Calc.CuonCarbon_ppm")] AuSolutionOut = [concatenate(UnitTag, ".QProd.ElC:LPh.Au (mg/L)")] CuSolutionOut = [concatenate(UnitTag, ".QProd.ElC:LPh.Cu (mg/L)")] ; Now call the subroutine to calculate the theoretical leach and adsorption values. CalculateCSTR() ; Set the leach extents for gold and copper in each tank (adsorption reaction is controlled via PID) [concatenate(UnitTag, ".RB.R2.Extent (%)")] = AuLeachExtent [concatenate(UnitTag, ".RB.R3.Extent (%)")] = CuLeachExtent if SetOxygen O2_Required = ( [concatenate(UnitTag, ".QFeed.QMl:IPh.Au(s) (kmol/h)")] / 4 * MW(O2) * AuLeachExtent/100 + [concatenate(UnitTag, ".QFeed.QMl:IPh.Cu(s) (kmol/h)")] / 4 * MW(O2) * CuLeachExtent/100 ) / Oxygen_Utilisation ;- [concatenate(UnitTag, ".QFeed.QM.O2(aq) (kg/h)")] [concatenate(UnitTag, ".MU1.Reqd.MakeUp.Qm (kg/h)")] = O2_Required Endif EndSub EndClass ;----------------------------------------------------------- PageLabel("A,B,C") CSTR Tank_A, Tank_B, TankC PageLabel("D,E,F") CSTR TankD, TankE, TankF Sub InitialiseSolution() TankC.UnitTag = "Tank_C" TankD.UnitTag = "Tank_D" TankE.UnitTag = "Tank_E" TankF.UnitTag = "Tank_F" ForEachClass(CSTR, Init) EndSub ForEachClass(CSTR, Exec) $END OF FILE  # How to use the Class (Perform actual Calculations using the Template) Once the Class has been defined (template complete), the user can include this template in pgm files to perform calculations. ## Defining Class Instances This is best shown using an example. In our sample file, the following lines represent the actual representation of the class:  For Build 139.30599 or later CSTR Tank_A, Tank_B, TankC Sub InitialiseSolution() TankC.UnitTag = "Tank_C" ForEachClass(CSTR, Init) EndSub ForEachClass(CSTR, Exec)  For earlier builds of SysCAD CSTR Tank_A, Tank_B, TankC Sub InitialiseSolution() Tank_A.Init() Tank_B.Init() TankC.UnitTag = "Tank_C" TankC.Init() EndSub Tank_A.Exec() Tank_B.Exec() TankC.Exec()  1. Three CSTR instances are defined here, Tank_A, Tank_B & TankC. 2. When the PGM file is evaluated during the SysCAD initialisation step, each instance of the Class is assigned a UnitTag Name. In the Gold Example Project, TANK_A, TANK_B and TANK_C are actual process unit tags • If we defined the instances of the CSTR class with the same name (such as Tank_A and Tank_B), then we can simply tick the ClassTag_is_UnitTag check box. • If we want to assign a different class name to the unit name (such as TankC), we can either initiailise the tag in the PGM code (TankC.UnitTag = "Tank_C"), or enter it manually via the access window. 3. When the project is solved, at each iteration, the Exec() function is called for each of the CSTR instances. 4. When a Class instance has been defined, SysCAD will create variables and functions for that instance. For example: For CSTR Tank_A, the following variable and functions can be accessed: • String1 = Tank_A.ClassTag() - returns the class instances tag name (in this case, "Tank_A" is returned as a text string variable) • String2 = Tank_A.ClassName() - returns the class instance type name (in this case, "CSTR" is returned as a text string variable) • Tank_A.UnitTag - This is an example of the class variable. • Tank_A.Exec() - This is an example of the class function. ## Defining Class Instances in an array Class instance can also be defined in an array format, for example an equivalent of the above code can be written as:  For Build 139.30599 or later CSTR Tank[4] Sub InitialiseSolution() Tank[1].UnitTag = "Tank_A" Tank[2].UnitTag = "Tank_B" Tank[3].UnitTag = "Tank_C" ForEachClass(Tank, Init) EndSub ForEachClass(Tank, Exec)  For earlier builds of SysCAD CSTR Tank[4] ExcludeWatch Tank[0] Sub InitialiseSolution() Tank[1].UnitTag = "Tank_A" Tank[2].UnitTag = "Tank_B" Tank[3].UnitTag = "Tank_C" Tank[1].Init() Tank[2].Init() Tank[3].Init() EndSub Tank[1].Exec() Tank[2].Exec() Tank[3].Exec()  Four CSTR instances are defined here, Tank 0, Tank 1, Tank 2 & Tank 3. Note: The array always starts with 0, therefore, we have intentionally defined 4 instances here and we will use Tanks 1 to 3. The ExcludeWatch Keyword instructs SysCAD not to display Tank 0. In our PGM code we will skip Tank 0 and start with Tank 1. Tank[1].UnitTag - This is an example of the class variable. Tank[1].Exec() - This is an example of the class function. With an array of instances of a class we can efficiently loop through these for the Exec function for each iteration. This makes it easier to change the number of tanks. The code above becomes: After Build 139.30094 Before Build 139.30094 const long TankCount = 4 CSTR Tank[TankCount] ;TankCount must be const long ExcludeWatch Tank[0] Sub InitialiseSolution() Tank[1].UnitTag = "Tank_A" Tank[2].UnitTag = "Tank_B" Tank[3].UnitTag = "Tank_C" ForEachClass(Tank, Init) EndSub ForEachClass(Tank,Exec)  Notes: • This will also call Exec() for Tank[0]. Please use the old notation if this must be avoided. • NOTE: If the "init" subroutine requires an input parameter, such as Init("tagname"), then ForEachClass(Tank,Init) cannot be used. long i const long TankCount = 4 CSTR Tank[4] ExcludeWatch Tank[0] Sub InitialiseSolution() Tank[1].UnitTag = "Tank_A" Tank[2].UnitTag = "Tank_B" Tank[3].UnitTag = "Tank_C" i = 1 While (i<TankCount) Tank[i].init() i = i + 1 EndWhile EndSub i = 1 While (i<TankCount) Tank[i].Exec() i = i + 1 EndWhile  ## ForEachClass Function The macro function ForEachClass provides an efficient method to loop through all instances of a class and call a Sub within the class. ForEachClass can be used in a number of ways: Function Syntax Description and Example ForEachClass(ClassName, SubroutineName) For Example:ForEachClass(TankClass, Exec) • Available for Build 139.30094 and later. • Used to loop through ALL instances of the defined Class (class type). • All instances include those in global scope class arrays and standalone instances. ForEachClass({ClassNameList}, SubroutineName) For Example:ForEachClass({TankClass,ThickenerClass,WasherClass}, Exec()) • Available for Build 139.31388 and later. • Similar to above, but used to loop through ALL instances of all the defined classes in the list. ForEachClass({ClassInstanceList}, SubroutineName) For Example:ForEachClass({T1,T5,FeedTank,TankLine.MidTank,TankLine.EndTank},UpdateResults). • Available for Build 139.31388 and later. • Used to loop through the named instances in the specified class instances. ForEachClass(ClassArray, SubroutineName) For Example:ForEachClass(Tanks, Init) • Available for Build 139.30094 and later. • Used to loop through all instances in the specified Array of classes. In all cases the specified Sub is called for each class instance. From Build 139.31388 the Sub can have any number of parameters, however string parameters are not supported. Where there are multiple class names (class types) are used, they must all have the same sub with the same parameters. NOTES: • The Class instances or Array must be in the global scope and declared before ForEachClass is called. • ForEachClass is not a true function, it is a convenient Macro that generates a sequence of code at PGM load time. This is why the location of ForEachClass is important relative to declaration of class instances (because it only uses the classes already declared when encountering ForEachClass during load). • Use ExcludeClass to block classes from being used with the ForEachClass function. For example for an Array of classes use "ExcludeClass Tanks[0]" alongside "ExcludeWatch Tank[0]" to skip the first class instance in the array with any use of ForEachClass. Available for Build 139.31388 and later. • A class instance within a class is excluded. Some Examples on how to use the function: The following code... Is equivalent to... CSTR Tank10, Tank20 CSTR Tank[3] ;Example 1: ;Call Exec for all instances of CSTR Class ForEachClass(CSTR,Exec) ;Example 2: ;Call Exec for all instances in Tank[] Class Array only ForEachClass(Tank,Exec) CSTR Tank30  • Note that Tank30.Exec() is not called since Tank30 it is declared after ForEachClass(CSTR,Exec). CSTR Tank10, Tank20 CSTR Tank[3] ;Example 1: ;Call Exec for all instances of CSTR Class Tank10.Exec() Tank20.Exec() long i i = 0 While (i<3) Tank[i].Exec() i = i + 1 EndWhile ;Example 2: ;Call Exec for all instances in Tank[] Class Array i = 0 While (i<3) Tank[i].Exec() i = i + 1 EndWhile CSTR Tank30  # Sharing Classes between Projects The recommended method of setting up a class that will be used in a number of pgms, or projects is as follows: 1. The CLASS definition is defined in a separate file; for example: General_Classes_and_functions.pgm • NOTE that this file must NOT contain the$ sign or EndFile token at the end of the file.
2. For shared use in a single project - The class definition file can be stored in the project\controls folder, then inserted into other PGM files using include file syntax >>filename.
• If the class definition file is stored in the same folder as the PGM file, then use >>General_Classes_and_functions.pgm to insert the file as needed.
3. For shared use across multiple projects - The include file can be saved in a common shared folder, then inserted into other PGM files using include file syntax >>filename, the file name needs to be the relative or full path name.
• For example: >>d:\Users\SysCAD Common Files\General_Classes_and_functions.pgm (Also See example below)

## Example 1. Class to calculate the Agitator Power required in a Vessel

The user wants to calculate the required agitator power in a number of vessels in a project. This functionality will be used in a number of different projects, and hence the file with the class will be stored on the network where it can be accessed by a number of users and projects.

A formula that may be used to calculate the agitator power for turbulent flow is:

$\displaystyle{ \mathbf{\mathit{P = P_{0} N^{3} D^{5} Density}} }$

where:
P - Agitation Power;
P0 - the dimensionless power number, which is a function of impeller geometry;
N - agitator rotational speed;
D - Diameter of the impeller;
Density - Density of the material in the vessel.

The class is saved in a file called AgitatorPower.pgm and is saved on the network at n:\Users\SysCAD Common Files\

 The Class file is as follows: The main pgm file may have the following structure: Class AgitatorPower TextLabel() String UnitName{Tag}* real Po*<<1>> real N<<0.3>>{i, comment("Rotation per second")} real D<<1>>{i, ("L", "m"), comment("ImpellerDiameter")} string DensityTag@{ Tag} real Density@("Rho", "kg/m^3") real Power@("Pwr", "kW") Sub Init() DensityTag = Concatenate(UnitName, ".QProd.SLRho (kg/m^3)") EndSub Sub Exec() Density = [DensityTag] Power = Po * N^3 * D^5 * Density EndSub EndClass  User needs to initialise the unit name, either enter the name directly into the access window or add in some lines in the PGM file (eg: in the "Sub initialiseSolution()" section). PageLabel(Agitators) >>n:\Users\SysCAD Common Files\AgitatorPower.pgm AgitatorPower Tank1, Tank2, StorageTank1 Sub InitialiseSolution() ForEachClass(AgitatorPower, init) EndSub ForEachClass(AgitatorPower, Exec) \$ ; --- end of file --- ; ------------------------------------------------------------------------------ ; the ForEachClass function is only available in Build 139.30094 or later. ; For older versions of SysCAD, use the following lines: Sub InitialiseSolution() Tank1.Init() Tank2.Init() StorageTank1.Init() EndSub Tank1.Exec() Tank2.Exec() StorageTank1.Exec() 

And the access window of the controller will show the following fields:

Notes:

1. The user may follow the same rules for individual functions that may be useful in a number of pgms or in separate projects.

# Example User Defined Classes

1. Gold Project - demonstrates the use of Classes in the CSTR pgm file. This is used to calculate the gold and copper leaching and adsorption values in a number of individual tanks.
2. See Example Class Files for some example uses of user specified class definitions.