Although the code is written in C, the object oriented approach can be seen in the design of the data structures of the analytical model interface. The internal representation of a mathematical term is based on a tree structure assembled in an extendible hash table for optimization purposes (Fig. 4.6).
All methods operating on the trees saved into the extendible hash table are just working on a generic unit node similar to a C++ base class.
typedef struct amiNodeGen_t { amiNodeType_t type; /* information about the */ /* underlying node type */ amiResType_t restype; /* information about the resulting */ /* type whether it is a matrix, */ /* vector or scalar */ int counter; /* information how often a node is */ /* referenced by others */ BOOLEAN opt; /* whether node has already been */ /* optimized */ void *pub; /* public pointer just for use of */ /* of external code */ } amiNodeGen_t;Since inheritance is not supported by C a similar mechanism was implemented to support this feature. Every node within a tree inherits the generic node and just differs in the local section of data which is seen in the example below showing a node for bidirectional operators (e.g., +, *, -, /, ...).
typedef struct amiNode2_s { amiNodeGen_t gen; /* the generic node -> necessary */ /* for memory management in C */ union amiNode_u *r; /* node of right child */ union amiNode_u *l; /* node of left child */ } amiNode2_t;The total node structure is now designed using the generic node and all other special nodes to form a union holding the complete information of any node. Since the structures are allocated dynamically during runtime the size of a node is exactly the size of its type and not the largest data structure. All nodes can now be accessed via the union structure and generic information can be selected without knowledge about the underlying node type.
typedef union amiNode_u { amiNodeGen_t gen; /* generic node */ amiNode1_t n1; /* mono-operator */ amiNode2_t n2; /* bi-operator */ amiNodeVal_t val; /* constant value */ amiNodeVar_t var; /* variable */ amiNodeMat_t mat; /* vectors or matrices */ amiNode2_t add; /* add node -> same as n2 */ amiNode2_t mul; /* multiply node -> same as n2 */ amiNode2_t sub; /* subtract node -> same as n2 */ amiNode2_t div; /* divide node -> same as n2 */ amiNode2_t pow; /* power node -> same as n2 */ amiNodeVar_t param; /* parameter variable */ amiNodeAux_t aux; /* auxiliary variable */ amiNodeFunc_t func; /* external functions */ amiNodeMacro_t macro; /* internal functions */ amiNodeTrans_t trans; /* transpose operator */ amiNodeTrig_t trig; /* trigonometric functions */ amiNodeValFT_t valft; /* running variables */ } amiNode_t;The data structure holding the complete model and other important information for a simulation tool looks like
typedef struct amiModel_s { int nDim; /* dimension of the model */ int nQuant; /* number of quantities */ int nTime; /* number of timesteps used */ BOOLEAN moving; /* true if PDE is describing */ /* a moving grid problem */ amiNode_t *solution; /* the solution vector */ amiNode_t *residuum; /* the residual vector */ amiNode_t *jacobian; /* the Jacobian matrix */ amiNode_t *grid; /* the grid criterion for */ /* adaptation purposes */ amiParamList_t *paramList; /* a list of parameter variables */ amiParamList_t *auxList; /* a list of auxiliary variables */ amiParamList_t *debugList; /* a list of variables which */ /* should be debugged during */ /* runtime */ char *name; /* the name of the model */ struct amiModel_s *next; /* the next model */ } amiModel_t;