Example PGM Files
From SysCAD Documentation
Navigation: PGMs
Sample PGM file (General)
;======================================================================================
; This is a sample PGM file. The aim is to set the recycle flowrate
; (from tank3 to tank1) to be half the feed flowrate.
;======================================================================================
;Lines that start with a semicolon (;) are comments only.
;pgm files must end with a $ sign OR Endfile.
;---- Define Variables ------------------------
;Bit is true or false
BIT DefaultSetPoint*
;Double is a real number
;adding * adds the variable to the Access Window
;adding @ marks the variable read only on the access window
;adding ("Dimension","unit") adds engineering unit to the access window
DOUBLE FeedSetPoint*("Qm","kg/s"), ActualFeedRate@("Qm","kg/s")
DOUBLE RecycleFlowRate@, RecycleRatio*
;---Logic--------------------------------
;Adding in an if statement to predefine a default FeedSetPoint and RecycleRatio.
;This will be used when a user calls for a default set point ORwhen there is no feedsetpoint
;or recycleRatio defined. Othertimes, the user is able to make changes to the FeedSetPoint
;and RecycleRatio direct from syscad GC_1 access window or trend window.
if (DefaultSetPoint) or (ActualFeedRate ==0 or RecycleRatio ==0)
FeedSetPoint = 1000 ;kg/s
RecycleRatio = 0.5 ;FeedSetPoint / RecycleFlowRate
DefaultSetPoint = 0
endif
;keyword GetTag gets a value from SysCAD
;keyword SetTag sets a value to SysCAD
SetTag("Source1.Qm_Rqd (kg/s)", FeedSetPoint)
ActualFeedRate= GetTag("pipe1.Qm (kg/s)")
RecycleFlowRate = ActualFeedRate * RecycleRatio
SetTag("Tank3.GM.IOs.[Recycle].Flow (kg/s)", RecycleFlowRate)
$ ;END OF FILE
;The $ sign (or ENDFILE) above marks the end of the file. It must be present.
Any text after the first $ sign will be ignored.
Sample PGM File (File layout when using counters)
;===================================================================================
; This is a sample PGM file.
; The aim is to show how to set up the PGM file when counters are being used.
;===================================================================================
;Lines that start with a semicolon (;) are comments only.
;pgm files must end with a $ sign OR Endfile.
;---- Define Variables ------------------------
long Counter*@
bit ControlOn*
bit Error1*@
Str AssessmentMessage1*@
;--- Logic ------------------------------------
if (OnInitialise)
;Place code here that must execute before the first iteration when solve or run is selected...
;This can be used to set tags that will become unchangeable once SysCAD is solving.
;This would include tags such as solving methods, any list boxes or tick boxes and datum in Dynamic.
;Note that SetDynTag(Tag, value) and GetDynTag(Tag) must be used to do this.
SetDynTag("PID_2.Cfg.[0].On", ControlOn)
elseif (OnTerminate)
;Place code here that must execute after last iteration when stop is selected or solve is complete...
;For example, can have conditional messages to appear at the end of the solve.
Error1 = ;place any solution checking logic here
if (Error1)
AssessmentMessage1 = "Run not successful, do this..." ;message as required...
MsgBox(AssessmentMessage1)
endif
;can leave blank if nothing will go here, but must have this statement to ensure counter counts correctly.
else
;Place code here that must execute every iteration.
;NOTE counter must be placed in this section, otherwise it will have extra counts during
;SysCAD initialise and while SysCAD stops.
Counter = Counter + 1
endif
$ ;END OF FILE
Example PGM with ReactionFinder Classes and Functions
;============================================================================
; Demonstration PGM - Class functions.
; Project Used: Any with a Reaction Block.
; KWA KENWALT - MARCH 2006
;============================================================================
; Description of the ReactionFinder Class function ---
; This class frees the user from the problem of the reaction index changing when a new reaction
; is added to the reaction file before the controlled reaction(s). The "Init" function should be called
; in the "OnInitialise" part of a pgm and this finds the index of the required reaction.
; The "SetExtent" function then sets the extent of the reaction as a fraction of the "Reqdspecie".
; NOTE: This has been updated to run under SysCAD Build 115 Update 12
;=============================================================================
Class ReactionFinder
;--- input variables ------
str ReqdUnit* ;the unit containing the reaction file
str ReqdReaction* ;the actual reaction that will be controlled
str ReqdSpecie* ;the specie that the control is based upon
double ReqdExtent*("Frac","%") ;the required reaction extent
;---- Output variables ------
long ReactionIndex@ ;the actual reaction index number in the unit
long totalNumReactions@ ;total number of reactions in the reaction file
;--- local variables ------
str NumReactions
str ReactionName, RBReact
str temp, ReactionNumber
str errormsg
long Minlength
long i,reactioncomp
Function Init()
if (strlen(ReqdUnit) > 0)
i = 1
ReactionIndex = 0
NumReactions = StrCat(ReqdUnit, ".RB.NoOfReactions")
totalNumReactions = GetDynTag(NumReactions)
while (i <= totalNumReactions)
ReactionName = StrCat3(ReqdUnit, ".RB.R", IntToStr(i), ".Reaction")
RBReact = GetDynStrTag(ReactionName)
Minlength = Min(strlen(RBREact),strlen(ReqdReaction))
if (Minlength > 0)
reactioncomp = StriCmp(Left(RBREact,Minlength), Left(ReqdReaction,Minlength))
if (reactioncomp == 0)
temp = StrCat3(ReqdUnit, ".RB.R", IntToStr(i), ".Extent (%.")
ReactionNumber = StrCat2(temp, ReqdSpecie, ")")
ReactionIndex = i
i = totalNumReactions + 1
endif
endif
i = i + 1
endwhile
if (ReactionIndex == 0)
errormsg = strcat3("The reaction ", ReqdReaction, " not found in ", ReqdUnit)
lognote(errormsg)
endif
endif
return 0
EndFunct
FUNCTION SetExtent()
if (ReactionIndex > 0)
SetDynTag(ReactionNumber,ReqdExtent)
endif
return 0
EndFunct
EndClass
;The following is an example of pgm code using the ReactionFinder class to
;call a number of reactions.
>>C:\SysCAD90\Examples\ReactionFinder.pgm
long NumUnits* ;In this case NumUnits = 1
double OverallRxnExt*, Feedore@("Qm","kg/h")
double tank3Input@("Qm","kg/h")
double tank3extent@, Output@("Qm","kg/h")
long iR
ReactionFinder Reactions[1]
if (Oninitialise)
iR = 0
while (iR < NumUnits)
Reactions[iR].Init()
iR = iR + 1
endwhile
else
Feedore = GetTag("Ore_in.Qi.Na2CO3(s) (kg/h)")
tank3Input = GetTag("Leached2.Qi.Na2CO3(s) (kg/h)")
Output = GetTag("Leached_solution.Qi.Na2CO3(s) (kg/h)")
tank3extent = 100 - (Feedore * (1 - OverallRxnExt/100) / tank3Input) * 100
SetTag("LEACH_CONVERSION.Reactions[0].ReqdExtent (%)",tank3extent)
iR = 0
while (iR < NumUnits)
Reactions[iR].SetExtent()
iR = iR + 1
endwhile
endif
$
Example PGM Function to Check for Error and Pause Simulation
BIT CheckErrors*
Function SanityCheck(BIT Wrong, STR Message)
if (CheckErrors AND Wrong)
logerror(strcat("Ohoh:",Message))
pausesimulation = true
Endif
return 0
EndFunct
Example PGM Class to set specie splits based on user requirements
The following Class Example PGM file - Class_Specie.pgm (given under the table) allows the user to set the specie split in a General Model (GM) based on any of the following requirements:
| Example function call using the SpecieClass Defined in the Class_Specie.pgm | Purpose | Description |
|---|---|---|
Init("Cu", "s") ;call initialisation function
IonSplit("Splitter.GM.IOs.[P_002]", 80) ;call the split function
| Set Mass Fraction Split based on One (1) given Element | Set the split Mass Fraction of all species containing a given element in a user defined phase, i.e. the user can send 80% of all of the Copper in the solid phase to a required stream. |
Init2("Cu", "S", "s") ;call initialisation function
IonSplit("Splitter.GM.IOs.[P_002]", 75) ;call the split function
| Set Mass Fraction Split based on Two (2) given Elements | Set the split Mass Fraction of all species containing two given elements in a user defined phase, i.e. the user can send 75% of all species that contain both Copper and Sulphur in the solid phase to a required stream. This method selects CuS(s), Cu2FeS4(s), etc, but not Cu(s) or NiS(s) - the species must contain BOTH elements. |
IndPhaseSplit("Splitter.GM.IOs.[P_002]", 2, "aq")
| Set Mass Fraction Split based on One (1) given Individual Phase | Set the split of all species in an Individual Phase to a stream, i.e. send 2% of all (aq) species to a stream. |
PhaseSplit("Splitter.GM.IOs.[P_002]", 5, "l")
| Set Mass Fraction Split based on One (1) given Phase | Set the split of all species in a Phase to a stream, i.e. send 5% of all liquid species to a stream. |
DensitySplit("Density_Splitter_001.GM.IOs.[P_HeavySolids]", 3000, "s")
| Set Mass Fraction Split based on Density | Set the split of all species in a Phase to a stream based on density separation, i.e. send 100% of all solids with density > 3000 kg/m3 to a stream. |
PGM FILE: Class_Specie.pgm ;======================================================================================= ;--- SysCAD General Controller - Specie Splitter class --- long SpecieCount ; Find the total number of species in the project SpecieCount = SDB.SpecieCount() Class SpecieClass str STag double TempValue long ReqPhase, Sphase, isp, SplitPhase, NumIonSpecies array IonIndex ;------------------------------------------------------------------------------ ; Initialisation function to set up an array with all of the species in the ; required phase (GPhase) that contain the user defined element (Ion) Function Init(str Ion, str GPhase) IonIndex.SetLen(25) IonIndex.SetAll(0) NumIonSpecies = 0 long Cmp1, SpPhase, ReqdPhase, j ; Find the index of the required phase ReqPhase = SDB.FindPhase(GPhase) isp = 0 j = 0 ; Scroll through all the species in the required phase in the project to find ; ones that contain the required element while (isp < SpecieCount) SpPhase = SDB.SpIPhaseNo(isp) if (SpPhase == ReqPhase) Cmp1 = SDB.SpElemMoles(isp, Ion) if (Cmp1 > 0) IonIndex.SetAt(j,isp) j = j + 1 endif endif isp = isp + 1 endwhile NumIonSpecies = j return 0 EndFunct ;------------------------------------------------------------------------------ ; Initialisation function to set up an array with all of the species in the ; required phase (GPhase) that contain both user defined elements (Ion1 and Ion2) Function Init2(str Ion1, str Ion2, str GPhase) IonIndex.SetLen(20) IonIndex.SetAll(0) NumIonSpecies = 0 long Cmp1, Cmp2, SpPhase, ReqdPhase, j ; Find the index of the required phase ReqdPhase = SDB.FindPhase(GPhase) isp = 0 j = 0 ; Scroll through all the species in the required phase in the project to find ; ones that contain both the required elements while (isp < SpecieCount) SpPhase = SDB.SpIPhaseNo(isp) if (SpPhase == ReqdPhase) Cmp1 = SDB.SpElemMoles(isp, Ion1) Cmp2 = SDB.SpElemMoles(isp, Ion2) if ((Cmp1 > 0) AND (Cmp2 > 0)) IonIndex.SetAt(j,isp) j = j + 1 endif endif isp = isp + 1 endwhile NumIonSpecies = j return 0 EndFunct ;------------------------------------------------------------------------------ ; Function to split a single required specie to a stream at a user defined value Function SpecieSplit(str SplitTag, double ReqValue, str ReqdSpecie) str tempsplit tempsplit = StrCat3(SplitTag, ".Splt.", ReqdSpecie ," (%)") SetDynTag(tempsplit, ReqValue) return 0 EndFunct ;------------------------------------------------------------------------------ ; Function to split a single specie with a required index to a stream at a user defined value Function SpecieIndexSplit(str SplitTag, double ReqValue, long SpecieIndex) str tempsplit, RequiredSpecie RequiredSpecie = SDB.SpSymbol(SpecieIndex) tempsplit = StrCat3(SplitTag, ".Splt.", RequiredSpecie ," (%)") SetDynTag(tempsplit, ReqValue) return 0 EndFunct ;------------------------------------------------------------------------------ ; Function to split all species containing a required element(s) to a stream at a user defined value ; Must have initialised the array first using Init or Init2 Function IonSplit(str SplitUnit, Double SplitValue) str TempSp long js js = 0 while (js < NumIonSpecies) isp = IonIndex.GetAt(js) TempSp = SDB.SpSymbol(isp) SpecieSplit(SplitUnit, SplitValue, TempSp) js = js + 1 endwhile return 0 EndFunct ;------------------------------------------------------------------------------ ; Function to split all species in a required individual phase to a stream at a user defined value Function IndPhaseSplit(str SplitUnit, Double SplitValue, str ReqdIPhase) str TempSp long SpPhase SplitPhase = SDB.FindIPhase(ReqdIPhase) isp = 0 while (isp < SpecieCount) SpPhase = SDB.SpIPhaseNo(isp) if (SpPhase == SplitPhase) TempSp = SDB.SpSymbol(isp) SpecieSplit(SplitUnit, SplitValue, TempSp) endif isp = isp + 1 endwhile return 0 EndFunct ;------------------------------------------------------------------------------ ; Function to split all species in a required phase to a stream at a user defined value Function PhaseSplit(str SplitUnit, Double SplitValue, str ReqdPhase) str TempSp long SpPhase SplitPhase = SDB.FindPhase(ReqdPhase) isp = 0 while (isp < SpecieCount) SpPhase = SDB.SpPhaseNo(isp) if (SpPhase == SplitPhase) TempSp = SDB.SpSymbol(isp) SpecieSplit(SplitUnit, SplitValue, TempSp) endif isp = isp + 1 endwhile return 0 EndFunct ;------------------------------------------------------------------------------ ; Function to split all species in a required phase based on Density to a stream at a user defined value Function DensitySplit(str SplitUnit, Double DensitySplitValue, str ReqdPhase) str TempSp long SpPhase SplitPhase = SDB.FindPhase(ReqdPhase) isp = 0 while (isp < SpecieCount) SpPhase = SDB.SpPhaseNo(isp) if (SpPhase == SplitPhase) TempSp = SDB.SpSymbol(isp) if SDB.SpDensity(isp,298.15,101.325) > DensitySplitValue SpecieSplit(SplitUnit, 100, TempSp) Else SpecieSplit(SplitUnit, 0, TempSp) endif endif isp = isp + 1 endwhile return 0 EndFunct EndClass
One of the advantages of using this class when doing specie splits, is that if the user adds species to the project, this class will automatically include it in the split.
For an example PGM that uses this class, see Example PGM file including the Specie Class.
Example PGM file including the Specie Class
The following is an example of a PGM file that uses the above splitter class:
;======================================================================================= ;--- SysCAD General Controller for a Generic Splitter --- >> Class_Specie.pgm ;--- variable declarations --- TextLabel("Required Phase (s, l or g)") str Phase* TextLabel("Elemental Recoveries") double NiRecovery*("Frac","%") double CuRecovery*("Frac","%") TextLabel() Double LiquidRecovery*("Frac","%") ; Local Variables double prevNi, prevCu, prevLiquid SpecieClass NiSplit, CuSplit if (OnInitialise) ;Initialise the functions in the class for elemental splits NiSplit.Init("Ni", "s") CuSplit.Init("Cu", "s") prevNi = 0 prevCu = 0 prevLiquid = 0 else ; Split based on elements. Only call the function when the required recovery changes. if (NiRecovery != prevNi) NiSplit.IonSplit("Split_Unit.GM.IOs.[P_002]", NiRecovery) prevNi = NiRecovery endif if (CuRecovery != prevCu) CuSplit.IonSplit("Split_Unit.GM.IOs.[P_002]", CuRecovery) prevCu = CuRecovery endif if (LiquidRecovery != prevLiquid) NiSplit.PhaseSplit("Split_Unit.GM.IOs.[P_002]", LiquidRecovery, "l") prevLiquid = LiquidRecovery endif endif $ ; --- end of file ---
Example PGM with Classes and Functions
;============================================================================
; Demonstration PGM - Class functions.
; Project Used: Alumina Digestion project.
; KWA KENWALT - MARCH 2003
;============================================================================
;============================================================================
; DEFINING SUB ROUTINES
;============================================================================
;----------------------------
Class BayerLiquor
;----------------------------
;-----DEFINE Bayer Liquor VARIABLES--------------------------------------------
STR StreamTagName*, MyTag, Liquor, Water
STR Al2O3, Na2C2O4, Na2C5O7, Na2CO3, Na2SO4, NaCl, NaOH, SiO2, Temp
DOUBLE LiquorQm("Qm","kg/s"), WaterQm("Qm","kg/s"), Al2O3Qm("Qm","kg/s")
DOUBLE Na2C2O4Qm("Qm","kg/s"), Na2C5O7Qm("Qm","kg/s"), Na2CO3Qm("Qm","kg/s")
DOUBLE Na2SO4Qm("Qm","kg/s"), NaClQm("Qm","kg/s"), NaOHQm("Qm","kg/s")
DOUBLE SiO2Qm("Qm","kg/s"), TNa, TAl2O3, Density_25("Rho","kg/m^3")@
DOUBLE Density_T("Rho","kg/m^3")@, T("T","dC")
DOUBLE A("Conc","g/L")@, C("Conc","g/L")@, S("Conc","g/L")@, A_C@, C_S@
;----FUNCTION GET VARIOUS MASSES----------------------------------------------
Function Mass()
LiquorQm = GetDynTag(Liquor)
WaterQm = GetDynTag(Water)
Al2O3Qm = GetDynTag(Al2O3)
Na2C2O4Qm = GetDynTag(Na2C2O4)
Na2C5O7Qm = GetDynTag(Na2C5O7)
Na2CO3Qm = GetDynTag(Na2CO3)
Na2SO4Qm = GetDynTag(Na2SO4)
NaClQm = GetDynTag(NaCl)
NaOHQm = GetDynTag(NaOH)
SiO2Qm = GetDynTag(SiO2)
return 0
EndFunct
;----FUNCTION TO INITIALISE Tags----------------------------------------------
Function InitTag()
MyTag = StreamTagName
Liquor = StrCat(MyTag, ".Qo.Liquids (kg/s)")
Water = StrCat(MyTag, ".Qo.H2O(l) (kg/s)")
Al2O3 = StrCat(MyTag, ".Qo.Al2O3(aq) (kg/s)")
Na2C2O4 = StrCat(MyTag, ".Qo.Na2C2O4(aq) (kg/s)")
Na2C5O7 = StrCat(MyTag, ".Qo.Na2C5O7(aq) (kg/s)")
Na2CO3 = StrCat(MyTag, ".Qo.Na2CO3(aq) (kg/s)")
Na2SO4 = StrCat(MyTag, ".Qo.Na2SO4(aq) (kg/s)")
NaCl = StrCat(MyTag, ".Qo.NaCl(aq) (kg/s)")
NaOH = StrCat(MyTag, ".Qo.NaOH(aq) (kg/s)")
SiO2 = StrCat(MyTag, ".Qo.SiO2(aq) (kg/s)")
Temp = StrCat(MyTag, ".T (dC)")
Return 0
EndFunct
;----FUNCTION - DENSITY @ 25 dC------------------------------------------------
Function Density25()
Mass()
TNa = (Na2CO3Qm + (NaOHQm/80 + Na2C2O4Qm/134 + Na2C5O7Qm/218 + NaClQm/116.9
+ Na2SO4Qm / 142.05) * 106 ) * (100 / LiquorQm)
TAl2O3 = Al2O3Qm*100/LiquorQm
Density_25 = (0.982 + (0.01349855 * TNa) + (-0.00024948 * TNa * TNa) +
(0.00000273* TNa * TNa * TNa) + (0.00208035* TAl2O3)+
(0.00004113* TAl2O3 * TAl2O3) + (-0.00000728* TAl2O3 * TAl2O3 * TAl2O3)+
(0.00033367* TNa * TAl2O3))*1000
Return 0
EndFunct
;----FUNCTION - DENSITY @ Temp ------------------------------------------------
Function DensityT()
T = GetDynTag(Temp)
Density_T = Density_25 * (1 - (0.0005021858*0.85*(T-25))-(0.0000011881*0.85*(T-25)*(T-25)))
Return 0
EndFunct
;----FUNCTION - Concentration ------------------------------------------------
Function Conc()
A = Al2O3Qm / LiquorQm * Density_25
C = NaOHQm/80*106 / LiquorQm * Density_25
S = Na2CO3Qm / LiquorQm * Density_25 + C
A_C = A/C
C_S = C/S
Return 0
EndFunct
;----FUNCTION - EXECUTE FUNCTIONS---------------------------------------------
Function Exec()
Density25()
DensityT()
Conc()
Return 0
EndFunct
EndClass
;=================================================
; DEFINING EQUIPMENT IN THE PROJECT
;=================================================
;Example of Defining Classes individually
BayerLiquor P4, P9, P10, P14
;Example of Defining Classes with an Array
BayerLiquor Pipe[3]
;=================================================
; DEFINING LOGIC IN THE PROJECT
;=================================================
if (Oninitialise)
;------Initialise Tag Name(s)---------
P4.InitTag("P_4")
P9.InitTag("P_9")
P10.InitTag("P_10")
P14.InitTag("P_14")
;Example of calling functions from classes defined with an array.
pipe[0].initTag("P_11")
pipe[1].initTag("P_12")
pipe[2].initTag("P_13")
Elseif (OnTerminate)
Else
;-------Executing Functions--------
P4.Exec()
P9.Exec()
P10.Exec()
P14.Exec()
;Example of calling functions from classes defined with an array.
pipe[0].Exec()
pipe[1].Exec()
pipe[2].Exec()
Endif
;=================================================
ENDFILE
;=================================================
