DEFINITION MODULE SysModDSBase;
(*******************************************************************
Module SysModDSBase (ISIS_Version_1.2)
Copyright (c) 1998-2006 by Andreas Fischlin, Dimitrios Gyalistras
and ETH Zurich.
Purpose Support the declaration of entire data structures
involved in the modeling of systems, e.g. declaration
of an entire output vector.
Remarks The Data Structure Definition Language (DSDL) has
the following EBNF:
DataStructure ::= ["STRUCT" "="] vectorSpace;
vectorSpace ::= "S" ":" identifier vector { "*" vector }.
vector ::= "<" element { "," element } ">".
element ::= identifier | integer | integerRange | vectorSpace.
identifier ::= letter { letter | digit | "_" }.
integer ::= "0"|"1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9".
integerRange ::= integer ".." integer.
See also companion module SysModBase
This module belongs to ISIS
Integrative Systems Implementation Software.
Programming
o Design
Andreas Fischlin 09/05/1998
Dimitrios Gyalistras 09/05/1998
o Implementation
Andreas Fischlin 09/05/1998
ETH Zurich
Systems Ecology
CHN E 35.1
Universitaetstrasse 16
8092 Zurich
SWITZERLAND
URLs:
<mailto:RAMSES@env.ethz.ch>
<http://www.sysecol.ethz.ch>
<http://www.sysecol.ethz.ch/SimSoftware/RAMSES>
Last revision of definition: 28/05/2002 AF
*******************************************************************)
FROM SysModBase IMPORT System, MObjType, MObjVar;
(* Error constants: sysModDSBaseOffSet = DMLanguage.userBase + 350 .. 380-1 *)
CONST
undefOrd = -1; (* undefined ordination number used to denote
undefined indices (see procedures using formal
argument ordTuple). E.g. element 1,1 out of a
matrix could be referenced by the tuple [1,1],
which contains all defined ordination numbers
(indices). However, the tuple [undefOrd,undefOrd]
references no specific element, thus this module
interprets it to denote the entire structure, i.e.
the entire matrix *)
endOfOrdTuple = -2; (* used as index value to mark end of
indices array (see procedures using
formal argument ordTuple); is stronger
than undefOrd and functions like shortening
the declared dimension of the tuple. *)
(*********************************************)
(*##### Declaring Object Structures #####*)
(*********************************************)
PROCEDURE DeclMObjStruct(s : System;
mObjStructQualId : ARRAY OF CHAR;
objStructType : MObjType;
structDef : ARRAY OF CHAR;
sepDimWithUL : BOOLEAN);
(*
Declare a data structure as a structured model object.
E.g. use this procedure to declare an output vector.
mObjStructQualId serves as an identifier for the entire
data structure and allows you to reference it as a single
entity. objStructType is the type of the structure, e.g.
type stateVar allows you to declare a data structure as a
structure of state variables. structDef defines the data
structure according to DSDL, the Data Structure Definition
Language (syntax given above).
Example 1
---------
Ex. use of a state vector of dimension 3:
VAR
x: ARRAY [1..3] OF StateVar;
Its structure is: "S: x<1,2,3>"
Thus declare it as follows:
DeclMObjStruct(s,"m.x",stateVar,"S: x<1,2,3>",FALSE);
Above call will declare 3 state variables for you, using for
them the identifiers: "m.x1", "m.x2", and "m.x3".
Alternatively you could have declared this structure also in
this way:
DeclMObjStruct(s,"m.x",stateVar,"S: x<1..3>",FALSE);
which is most useful for long vectors or large matrices.
Note, you may use the underline character "_" in identifiers.
E.g.
DeclMObjStruct(s,"m.x",stateVar,"S: x_<1..3>",FALSE);
would declare state variables with these identifiers: "m.x_1",
"m.x_2", and "m.x_3".
Once the structure has been declared, you need to bind the
elements of the actual array of state variables, i.e. x, to
the declared model objects. There are two techniques
by which to accomplish this:
(i) program the loop yourself:
VAR index: ARRAY [0..0] OF INTEGER; i: INTEGER;
mObjQualIdent: IDENT;
FOR i := 1 TO 3 DO
index[0] := i-1;
GetEleIdent(s,"m.x",index,mObjQualIdent);
BindMObjVar(s,mObjQualIdent,x[i]);
END(*FOR*);
(ii) ask SysModBase to do the loop for you:
VAR index: ARRAY [0..0] OF INTEGER; i: INTEGER;
mObjQualIdent: IDENT;
GetFrstMObjStructEle(s,"m.x",mObjQualIdent,index);
WHILE (mObjQualIdent[0]<>0C) DO
BindMObjVar(s,mObjQualIdent,x[index[0]+1]);
GetNextMObjStructEle(s,"m.x",mObjQualIdent,index);
END(*WHILE*);
The looping with the 2nd technique can be used successfully
even if you pass unknownSystem as the actual parameter for s
in case the model object structure is of type outVar. This
allows for any subsystem which wants to use the data
structure as an input, to learn about the construction of
identifiers and possibly of the associated tuple of
ordination numbers, i.e. ordTuple, of the output data
structure without having to import any types or other
objects from the outputting subsystem. In such cases
mObjStructQualId, e.g. "m.x", is then already sufficient to
learn about the structure of the structured model object.
See also the routines involved in "Linking of Subsystems and
Submodels for data exchange" as provided by the companion
module SysModBase.
Example 2
---------
Parameter sepDimWithUL governs the use of the character underline,
i.e. "_", as separator between parts of an identifier which results
from multidimensional structures.
For example a 3 by 3 Markov matrix could be declared as this:
VAR
m: ARRAY [1..3],[1..3] OF Parameter;
It defines a 2 dimensional structure:
"S: m <1..3> * <1..3>"
By calling
DeclMObjStruct(s,"model.MarkovMatrix", modParam, "S: m <1..3> * <1..3>",TRUE);
results in declaration of parameters with the tabulated identifiers
and indices tuples:
Identifier Indices
model.m1_1 0,0
model.m1_2 0,1
model.m1_3 0,2
model.m2_1 1,0
model.m2_2 1,1
model.m2_3 1,2
model.m3_1 2,0
model.m3_2 2,1
model.m3_3 2,2
REMARK: Due to the limitation of Modula-2 to pass open array
parameters always as arrays which start from index 0, an index
in the tuples containing the ordination numbers for the elements in
the data structure always start from 0. Consequently, if you
declare this array
VAR ind: ARRAY [1..2] OF INTEGER;
you can bind all of the matrix' elements by this code:
(*(i) program the loop yourself:*)
FOR i := 1 TO 3 DO ind[1] := i-1;
FOR j := 1 TO 3 DO ind[2] := j-1;
GetEleIdent(s,"model.MarkovMatrix",ind,mObjQualIdent);
BindMObjVar(s,mObjQualIdent,m[i,j]);
END(*FOR*);
END(*FOR*);
or like this:
(*(ii) ask SysModBase to do the loop for you:*)
GetFrstMObjStructEle(s,"model.MarkovMatrix",mObjQualIdent,ind);
WHILE (mObjQualIdent[0]<>0C) DO
BindMObjVar(s,mObjQualIdent,m[ind[1]+1,ind[2]+1]);
GetNextMObjStructEle(s,"model.MarkovMatrix",mObjQualIdent,ind);
END(*WHILE*);
Rember also, by calling
DeclMObjStruct(s,"model.MarkovMatrix", modParam, "S: m_ <1..3> * <1..3>",FALSE);
you can alternatively get:
Identifier Indices
model.m_11 0,0
model.m_12 0,1
model.m_13 0,2
model.m_21 1,0
model.m_22 1,1
model.m_23 1,2
model.m_31 2,0
model.m_32 2,1
model.m_33 2,2
Example 3
---------
Example featuring a more complex data structure:
CONST alpha = 1; beta = 2;
VAR
out: ARRAY [alpha..beta] OF RECORD
theta: OutVar;
y: ARRAY [1..3] OF ARRAY [1..2] OF OutVar;
END;
Its structure can be defined like this:
"S: out * * <1..2>>"
Calling
DeclMObjStruct(s,"mo.outStruct",outVar,
"S: out_ * *<1..2>>",FALSE);
results in declaration of model objects with tabulated identifiers
and indices tuples:
Identifier Indices
mo.out_alpha_theta 0,0
mo.out_alpha_y11 0,1,0,0
mo.out_alpha_y12 0,1,0,1
mo.out_alpha_y21 0,1,1,0
mo.out_alpha_y22 0,1,1,1
mo.out_alpha_y31 0,1,2,0
mo.out_alpha_y32 0,1,2,1
mo.out_beta_theta 1,0
mo.out_beta_y11 1,1,0,0
mo.out_beta_y12 1,1,0,1
mo.out_beta_y21 1,1,1,0
mo.out_beta_y22 1,1,1,1
mo.out_beta_y31 1,1,2,0
mo.out_beta_y32 1,1,2,1
Do the binding like this:
CONST theta = 0; y = 1;
VAR ordTuple: ARRAY [0..3] OF INTEGER; i,j,k,l: INTEGER;
mObjQualIdent: IDENT;
(i) programing the loops yourself:
FOR i := alpha TO beta DO ordTuple[0] := i-alpha;
FOR j := theta TO y DO ordTuple[1] := j-theta;
IF j=theta THEN ordTuple[2] := endOfOrdTuple;
GetEleIdent(s,"mo.outStruct",ordTuple,mObjQualIdent);
BindMObjVar(s,mObjQualIdent,out[i].theta);
ELSE
FOR k := 1 TO 3 DO ordTuple[2] := k-1;
FOR l := 1 TO 2 DO ordTuple[3] := l-1;
GetEleIdent(s,"mo.outStruct",ordTuple,mObjQualIdent);
BindMObjVar(s,mObjQualIdent,out[i].y[k,l]);
END(*FOR*);
END(*FOR*);
END(*IF*);
END(*FOR*);
END(*FOR*);
Note, you can temporarily "reduce" the dimension of
ordTuple by assigning after the lest element in use the
reserved value endOfOrdTuple; see above assignment:
ordTuple[2] := endOfOrdTuple;
(ii) Let SysModDSBase construct the looping:
GetFrstMObjStructEle(s,"mo.outStruct",mObjQualIdent,ordTuple);
WHILE (mObjQualIdent[0]<>0C) DO
i := ordTuple[0]+alpha; j := ordTuple[1]+theta;
IF j=theta THEN
BindMObjVar(s,mObjQualIdent,out[i].theta);
ELSE (* j = y *)
k := ordTuple[2]+1; l := ordTuple[3]+1;
BindMObjVar(s,mObjQualIdent,out[i].y[k,l]);
END(*IF*);
GetNextMObjStructEle(s,"mo.outStruct",mObjQualIdent,ordTuple);
END(*WHILE*);
*)
PROCEDURE MObjStructExists(mObjStructQualId : ARRAY OF CHAR): BOOLEAN;
PROCEDURE UndeclMObjStruct(s : System;
mObjStructQualId : ARRAY OF CHAR);
PROCEDURE UndeclAllMObjStructs( s : System );
(**********************************************)
(*##### Traversing Object Structures #####*)
(**********************************************)
(*
Following four procedures work also if you pass unknownSystem for
parameter s in case the structure is of type outVar.
*)
PROCEDURE GetFrstMObjStructEle(s : System;
mObjStructQualId : ARRAY OF CHAR;
VAR mObjQualIdent : ARRAY OF CHAR;
VAR ordTuple : ARRAY OF INTEGER);
PROCEDURE GetNextMObjStructEle(s : System;
mObjStructQualId : ARRAY OF CHAR;
VAR mObjQualIdent : ARRAY OF CHAR;
VAR ordTuple : ARRAY OF INTEGER);
PROCEDURE GetEleIdent (s : System;
mObjStructQualId : ARRAY OF CHAR;
ordTuple : ARRAY OF INTEGER;
VAR mObjQualIdent : ARRAY OF CHAR);
(*
Returns in mObjQualIdent the identifier which denotes an element as
given by the ordTuple within the structured model object
mObjStructQualId. E.g. calling
Model.p
ordTuple[0] := 0; ordTuple[1] := 0; ordTuple[2] := 0; ordTuple[3] := 0;
GetSubStructIdent(s,"Model.complexPar",ordTuple,mObjQualIdent);
for a structure defined as:
"S: p <0,1> * * <0..2> >"
returns in mObjQualIdent the identifier "Model.p0_m0_0". This is
because ordTuple defines as the ordination number for the first
dimension the first matrix, plus the 2nd and 3rd dimensions refer to
the dimensions of the actual matrix m (see also GetSubStructIdent).
For instance the following code lets you learn about any identifier,
i.e. qualifEleIdent, of all elements in the structured model object
"Model.complexPar":
FOR i:= 0 TO 1 DO ordTuple[0] := i;
ordTuple[1] := j;
FOR k:= 0 TO 2 DO ordTuple[2] := k;
FOR l:= 0 TO 2 DO ordTuple[3] := l;
GetEleIdent(s,"Model.complexPar",ordTuple,qualifEleIdent);
(* referring to parameter p[i].m[k,l] *)
END(*FOR*);
END(*FOR*);
END(*FOR*);
based on these declarations:
VAR
p: ARRAY [0..1] OF RECORD
m: ARRAY [0..2] OF ARRAY [0..2] OF Parameter
END;
VAR i: [0..1]; CONST j = 0; VAR k,l: [0..2];
ordTuple: ARRAY [0..3] OF INTEGER;
The actual identifiers (given you called DeclMObjStruct
with parameter sepDimWithUL = TRUE) can be referenced via
these ordination tuples:
ordTuple Identifier ordTuple Identifier
0,0,0,0 Model.p0_m0_0 1,0,0,0 Model.p1_m0_0
0,0,0,1 Model.p0_m0_1 1,0,0,1 Model.p1_m0_1
0,0,0,2 Model.p0_m0_2 1,0,0,2 Model.p1_m0_2
0,0,1,0 Model.p0_m1_0 1,0,1,0 Model.p1_m1_0
0,0,1,1 Model.p0_m1_1 1,0,1,1 Model.p1_m1_1
0,0,1,2 Model.p0_m1_2 1,0,1,2 Model.p1_m1_2
0,0,2,0 Model.p0_m2_0 1,0,2,0 Model.p1_m2_0
0,0,2,1 Model.p0_m2_1 1,0,2,1 Model.p1_m2_1
0,0,2,2 Model.p0_m2_2 1,0,2,2 Model.p1_m2_2
*)
PROCEDURE BindMObjVar (s : System;
mObjQualIdent : ARRAY OF CHAR;
VAR obj : MObjVar);
(*
Bind the model object variable obj (of type MObjVar = REAL)
to the model object with identifier mObjQualIdent. The
latter has typically been declared by means of routine
DeclMObjStruct (see above), which can't associate, i.e.
bind, a particular real variable with the model object.
Note this contrasts with the behavior of routine DeclMObj
from module SysModBase, which binds the model object obj
during the declaration. Since this is not possible with a
structured data type, you need to call BindMObjVar for
every element in your data structure which functions as a
model object. Typically you traverse through your data
structure by means of a loop similar to one of these
techniques:
(i) programmed yourself
FOR i:= 0 TO 1 DO T[0] := i;
T[1] := j;
FOR k:= 0 TO 2 DO T[2] := k;
FOR l:= 0 TO 2 DO T[3] := l;
GetEleIdent(s,"Model.complexPar",T,qualifEleIdent);
BindMObjVar(s,qualifEleIdent,p[i].m[k,l]);
END(*FOR*);
END(*FOR*);
END(*FOR*);
or
(ii) let SysModDSBase construct the looping
GetFrstMObjStructEle(s,"Model.complexPar",qualifEleIdent,T);
WHILE (qualifEleIdent[0]<>0C) DO i := T[0]; k := T[2]; l := T[3];
BindMObjVar(s,qualifEleIdent,p[i].m[k,l]);
GetNextMObjStructEle(s,"Model.complexPar",qualifEleIdent,T);
END(*WHILE*);
and bind all involved varibles to the corrsponding model
object declaration.
Above examples were based on these declarations:
VAR
p: ARRAY [0..1] OF RECORD m: ARRAY [0..2] OF ARRAY [0..2] OF Parameter END;
VAR i: [0..1]; CONST j = 0; VAR k,l: [0..2];
T: ARRAY [0..3] OF INTEGER;
*)
PROCEDURE GetSubStructIdent (s : System;
mObjStructQualId : ARRAY OF CHAR;
ordTuple : ARRAY OF INTEGER;
VAR subStructQualId : ARRAY OF CHAR);
(*
Returns in subStructQualId the partial identifier which names the
sub structure as given by the ordTuple with the structured model
object mObjStructQualId. E.g. calling (T ~ ordTuple)
T[0] := 0; T[1] := 0;
T[2] := undefOrd; T[3] := undefOrd;
GetSubStructIdent(s,"Model.complexPar",T,subStructQualId);
for a structure defined as:
"S: p <0,1> * * <0..2> >"
returns in subStructQualId the identifier "Model.p0_m". This
is because T (ordTuple) defines only the ordination number for
the first two dimension2; both other dimensions involved in
the actual matrix m, are given as undefined only (confer
GetEleIdent). The first dimension designating which of the
two matrices are to be referenced, points at the first one.
On the other hand, calling
T[0] := 1; T[1] := 0;
T[2] := undefOrd; T[3] := undefOrd;
GetSubStructIdent(s,"Model.complexPar",T,subStructQualId);
returns in subStructQualId the identifier "Model.p1_m".
If you wish to learn about a substructure which requires
only a few indices in ordTuple to specify it, you can temporarily
reduce the length of the ordTuple array by using the reserved
index value "endOfOrdTuple". E.g. with above structure
T[0] := undefOrd; T[1] := undefOrd;
T[2] := endOfOrdTuple;
GetSubStructIdent(s,"Model.complexPar",T,subStructQualId);
returns the main identifier of the complex parameter, i.e.
subStructQualId = "Model.p".
Substructures are of course only defined up to that level of
recursion in the structure which are defined for all
preceeding parts (left-side of structure) for any given
substructure, in other words: "a branch cannot hang in the
air!". This implies that leaving a dimension undefined on
which another substructure depends, will make it impossible
for GetSubStructIdent to return a unique identifier. E.g.
calling
T[0] := 0; T[1] := undefOrd; T[2] := 0;
T[3] := undefOrd; T[4] := undefOrd;
GetSubStructIdent(s,"M.bigStructure",T,subStructQualId);
GetSubStructIdent(s,"M.bigStructure",ordTuple,subStructQualId);
for a structure defined as:
"S: a < S: b < S: c < S: m <1,2>*<1,2> > >, beta >"
returns in subStructQualId not "M.m" but only the empty
string, since T[1] = undefOrd, and consequently T[2] = 0 has
to be ignored, i.e. the substructure is not defined at all. Only
this
T[0] := 0; T[1] := 0; T[2] := 0;
T[3] := undefOrd; T[4] := undefOrd;
GetSubStructIdent(s,"M.bigStructure",T,subStructQualId);
returns the result "M.m".
See also comments on procedures GetSubStructIdents and GetEleIdent.
Hint: Use routine GetSubStructIdents if you want to learn
about the identifiers of all substructures at once.
*)
PROCEDURE GetSubStructIdents (s : System;
mObjStructQualId : ARRAY OF CHAR;
VAR subStructQualIds : ARRAY OF CHAR;
VAR n : INTEGER);
(*
Returns in subStructQualIds all possible partial identifiers
which name all sub structures of the structured model object
mObjStructQualId. The n individual identifiers are separated
with delimiter "|". This routine serves as a function for
substructures with a purpose similar to that of the
procedures GetFrstMObjStructEle and GetNextMObjStructEle for
elements, i.e. to learn about all identifiers defined by
the structured model object mObjStructQualId. Ex.:
Calling
GetSubStructIdents(s,"M.bigStructure",subStructQualId,n);
for a structure defined as:
"S: a < S: b < S: c < S: m <1,2>*<1,2> > >, beta >"
returns in subStructQualIds:
M.m|M.c|M.b|M.a and n = 4
since the structure defines these identifiers
Identifier (elements) T #
-----------------------------------------------
M.m1_1 0,0,0,0,0
M.m1_2 0,0,0,0,1
M.m2_1 0,0,0,1,0 5
M.m2_2 0,0,0,1,1
M.abeta 1 •
Identifier (substructures) T #
-----------------------------------------------
M.m 0, 0, 0,-1,-1
M.c 0, 0,-1 • 4
M.b 0,-1 •
M.a -1 •
• stands for an element with endOfOrdTuple, i.e. -2, in
T: ARRAY [0..4] OF INTEGER;
See also comments on procedures GetSubStructIdent and GetEleIdent.
*)
(*************************************)
(*##### Conversion Routines #####*)
(*************************************)
PROCEDURE OrdTupleToStr(ordTuple: ARRAY OF INTEGER; sep: ARRAY OF CHAR;
VAR str: ARRAY OF CHAR);
(*
str is constructed up to HIGH(ordTuple) or only up to encounter of
an element which is equal to endOfOrdTuple.
*)
PROCEDURE StrToOrdTuple(ordTupleStr: ARRAY OF CHAR; sepCh: CHAR;
VAR ordTuple: ARRAY OF INTEGER);
(*
IMPLEMENTATION RESTRICTION: sepCh must be a single character, thus can revert
conversion of effect by OrdTupleToStr only if sep contained a single character.
*)
END SysModDSBase.