Skip to content. | Skip to navigation

Personal tools
You are here: Home Documents DSblock

The DSblock model interface

Version 4.0a

Incomplete Draft

Martin Otter
Pieter Mosterman

January 12, 1998


     Nov. 19., 1996: Initial version.
Nov. 27., 1996: Mathematical description forms, example for hierarchical DSblock with separatly compiled components.
Jan. 12., 1998: Re-designed and adapted to Modelica version 1.0. Implemented in C++ and tested with Dymola.

Note: this document is best printed on a laser printer by using a font size of 10pt.

1. Introduction

The DSblock model interface definition is a neutral, low-level description for complex dynamic systems. It is developed since 1990 from Martin Otter and is used for the mathematical description of parameterized, event-driven, explizit ordinary differential equations or differential algebraic equations in form of input-output "blocks". Upto version 3.3, the DSblock definition consists of upto 11 Fortran77 subroutines with defined interfaces.

Below, a draft of DSblock version 4.0 is given, which is a major redefinition. Most importantly, the underlying target language, i.e., Fortran, is changed. The new target language is C++. Interfaces for Java and Fortran90 will be added in the future. The reason for this change is that Fortran77 is too limited in scope and that C++ leads to a considerably simplified interface, due to the better available datastructures. Other changes include:

  • The new DSblock definition is closely related to the Modelica language. Especially, all attributes of a Modelica model can be stored and the DSblock datastructure is a direct map of the Modelica datastructures. Of course, it will still be possible and useful to generate DSblock code for other model description forms.

  • DSblock models can be built up hierarchically, i.e., subcomponents of a DSblock model may consist of other, precompiled DSblock models. This allows e.g., to generate a subcomponent from a Modelica model, compile it and use this precompiled DSblock as submodel in another Modelica model.

  • DSblock models are no longer be limited to input/output blocks. Every non-causal component, like a Resistor or a Capacitor, can be described.

  • There are now two interfaces. One interface describes the model and is e.g. used to connect a DSblock with other DSblocks to built up hierarchical structures. Every such model contains a description of the model which is close to the Modelica description.
    The second interface is used for the communication with the integrators. Depending on the integrator, several interfaces are provided (presently for hybrid differential equations in space form and for hybrid differential algebraic equation systems).

The DSblock interface version 4.0 is realized in form of C++ classes. The interface definition and the software is free, and can be used without any restrictions. Below, pointers to the software are given. In order to get a quick feeling how models are defined, see the first two examples. The whole available software can also be downloaded as zip-file Simple electrical circuit as a Modelica model.
    circuit.cpp   "" as a DSblock model. It is demonstrated how physical models can be built up hierarchically. Modelica model to demonstrate the hybrid language elements of Modelica.
    hybrid.cpp "" in form of a DSblock model. It is demonstrated how hybrid proporties are described.

    dsmodel.h Header-file containing classes to implement Modelica models in an easy way.
    dsblock.h Header-file containing classes to interface a DSblock model to ODE and DAE integrators.
    dsblock.cpp   Source-file of all DSblock classes of "dsmodel.h" and "dsblock.h"".;

In the following sections, examples for the new DSblock definition are given in C++. All models are first given as a Modelica model before the corresponding DSblock description is discussed. At the end, a more detailed overall description is given.

2. DSblock Interface to Modelica Models

Several utility classes are provided to build up a DSblock model which originates from a Modelica model. The classes are structured in such a way that a Modelica compiler can generate C++ DSblock code easily. At hand of three examples, the most important features are explained.

2.1 Simple DSblock: Van der Pol equation

In the most simpliest case, a DSblock is a "flat" respresentation of a corresponding Modelica model. E.g. the Van der Pol equation, a second order ODE, can be described by the following Modelica model:

   model VanDerPol
      parameter Real lambda=0.5;
                Real y;
      der(der(y)) + lambda*(1-y^2)*der(y) + y = 0
   end VanDerPol;

This model can be easily solved for its highest derivative. A corresponding C++ DSblock has the following structure:

   #include "dsmodel.h"
   using namespace dsblock;

   class VanDerPol : public Model {
       // Variables of model
          Real lambda;
          Real y, der_y, der_der_y;

       // Functions of DSblock
          VanDerPol();                         // initialize DSblock
          void equation(DynamicSystem& sys);   // sorted equations

   Model& dsblock::newModel() {   // make model available
      Model *model = new VanDerPol;
      return *model;

Class VanDerPol is a subclass of the predefined class Model in namespace dsblock, in which the common properties of Modelica model classes are defined. For every type of Modelica class, a corresponding DSblock class is provided. In particular, the following mappings are used:

    Modelica class DSblock C++ class
    class class ModelicaClass {...}
    connector class Connector : public ModelicaClass {...}
    record class Record      : public ModelicaClass {...}
    package class Package    : public ModelicaClass {...}
    model class Model      : public ModelicaClass {...}
    block class Block       : public Model {...}
    function class Function : public Block {...}
    - class Variable   : public ModelicaClass {...}
    Real class Real         : public Variable {...}
    Integer class Integer     : public Variable {...}
    Boolean class Boolean    : public Variable {...}
    - class Crossing    : public ModelicaClass {...}

The variables of a DSblock are declared in a similiar way as the corresponding Modelica model by statements:

    Real lambda;
    Real y, der_y, der_der_y;

Furthermore the constructor of the class (here: ": VanDerPol()") and the virtual function equations have to be provided. The former function is used to initialize the DSblock and to provide all the attributes of a variable. The latter function is used to provided the sorted equations of the model. For the VanDerPol class the constructor can be defined in the following way:

   VanDerPol::VanDerPol() {
     // register all components
        insert(lambda   ,"lambda");
        insert(y        ,"y");
        insert(der_y    ,"der_y");
     // define other component attributes

Utility function insert is a member of class ModelicaClass and is used to register all components together with the component name as character string. When a model is hierarchically structured, all components together form a tree and the insert function is used to introduce new nodes in this tree. All other classes have class-dependent member functions to define all Modelica attributes. E.g. the attributes of a Real variable could be set in the following way:

   Real var;

Additionally, proporties of variables can also be defined, especially


defines that variable der_y is the derivative of variable y. Finally, the sorted model equations have to be provided in function equation which is a member of class Model:

   void VanDerPol::equation(DynamicSystem& sys) {
     // Equations of Van-Der-Pol system
        der_der_y._ = -lambda._ * (1 - y._*y._) * der_y._ - y._;

The actual value of a variable is stored in the public member variable "_". This means e.g. that "y._" is the value of Modelica variable y. All the other attributes of a Modelica variable are stored in the private field and can be inquired via utility functions (e.g., the full name of the variable). An object of class DynamicSystem is provided in the interface of member function equation. This object contains utility functions needed by the model, e.g., the actual time instant can be inquired or the task to be carried out. This will be discussed in more detail below.

The equations have to be solved for the highest derivatives or for the residues of implicit algebraic equations containing these derivatives as unknowns, or a mixture of the two. In the latter two cases, only implicit solvers can be used, whereas for the form above explicit integration methods can be utilitzed as well. Rewritting the Van der Pol system into residue form results in:

   VanDerPol::VanDerPol() {
     // register all components

     // define other component attributes
        resvdp.residue(der_der_y);   // residue used to compute der_der_y

   void VanDerPol::equation(DynamicSystem& sys) {
     // Equations of Van-Der-Pol system
        resvdp = -lambda._ * (1 - y._*y._) * der_y._ - y._ - der_der_y._;

The only differences to the previous formulation is that an additional Real variable resvdp is introduced with property = residue, and that this residue is computed instead of the highest derivative. The property of a residue variable contains a reference to another variable. Informally this means, that a residue is primarily used to compute the refered variable in the solver. To be more specific, this information is used for the ordering of the equations in the solver. In the solver, the unknown variables are ordered according to the order in which the corresponding constructors are called. Then, the equations are ordered in the same way utilizing the variable refered to in the property attribute of the residue. Of course, this is just the initial ordering of the systems of equations; the solver may changed this ordering afterwards.

2.2 Hierarchical DSblock: A simple electrical circuit

In principal, every model could be built up with the described scheme. However, one of the disadvantages is that for a hierarchical Modelica model all the attribute definitions (e.g. the instance names) are repeated all the time. At hand of an example from Modelicas language description it is show, in which way hierarchical models can be build up without unnecessarily repeating information. The complete Modelica model is given in The most important parts are:

   model Circuit "Simple circuit as demonstration example"
     Resistor  R1 (R= 10);
     Resistor  R2 (R=100);
     Capacitor C  (C=0.01);
     Inductor  L  (L=0.1);
     VsourceAC AC;
     Ground    G;
     connect  (AC.p, R1.p); 
     connect  (R1.n, C.p);
     connect  (C.n, AC.n);
     connect  (R1.p, R2.p);
     connect  (R2.n, L.p);
     connect  (L.n,  C.n);
     connect  (AC.n, G.p);
   end  Circuit;

   model  Resistor              // component used in Circuit
     extends  TwoPin;
     parameter  Resistance R;
     v = R*p.i;
   end Resistor;

   partial model  TwoPin        // superclass of Resistor
     Pin     p "Positive pin";
     Pin     n "Negative pin";
     Voltage v "Voltage drop between the two pins";
     v = p.v - n.v;
   end  TwoPin;

   connector Pin               // connector used in TwoPin
     Voltage      v  "Potential at the pin";
     flow  Current i  "Current flowing into the pin";
   end  Pin;

   // Type definitions
      type  Voltage = Real(quantity="Voltage", unit="V");
      type  Current = Real(quantity="Current", unit="A");

In the corresponding DSblock, the same hierarchy as in the Modelica model is used for the datastructures. To enhance efficiency, this is usually not done for the model equations. By symbolic processing of the models all the equations are "flattened" and sorted together, i.e., the hierarchy of the original model is lost. The complete C++ DSblock model is given in circuit.cpp. The most important parts are:

   class Circuit : public Model { public:
     Resistor  R1;
     Resistor  R2;
     Capacitor C;
     Inductor  L;
     VsourceAC AC;
     Ground    G;

     void equation(DynamicSystem& sys);

   Circuit::Circuit() {
     // register all components 
        insert(C ,"C" );
        insert(L ,"L" );
        insert(G ,"G" );

     // define other component attributes
        C.C.parameter (0.01);
        L.L.parameter (0.1);
        description("Simple circuit as demonstration example");

     // define alias and constant variables
        R1.p.v.alias(AC.v);    // R1.p.v = AC.v

   class Resistor : public TwoPin { public:
     Resistance R;
     Resistor() {

   class TwoPin : public Model { public:
     Pin     p;
     Pin     n;
     Voltage v;

     TwoPin() {
       // register all components 

       // define other component attributes
          p.description("Positive pin");
          n.description("Negative pin");
          v.description("Voltage drop between the two pins");

   class Pin : public Connector { public:
     Voltage v;
     Current i;

     Pin() {
       // register all components 

       // define other component attributes
          v.description("Potential at the pin");
          i.description("Current flowing into the pin").flow();

   class Voltage : public Real { public: Voltage() {
     quantity("Voltage"); unit("V");

   class Current : public Real { public: Current() {
     quantity("Current"); unit("A");

The hierarchical ordering of the C++ classes is done exactly as for the Modelica model. When connecting components together, a lot of equations of the form "a = b" are generated, because connector variables in different components are identical. It would be a vaste of space if the time series of the result is stored for both variables. Therefore, in the initialization part of the model it is e.g. defined:

    R1.p.v.alias(AC.v);   // R1.p.v = AC.v

which means that only the results for AV.v are stored and the information that R1.p.v is identical to AV.v. Additional the utility member function v2.negAlias(v1) is provided to state that v2 = -v1.
As already noted, only on the highest level the flattened model equations are provided:

   void Circuit::equation(DynamicSystem& sys) {
     // Equations of simple circuit
        int equationStructure[5] = {0,  // ComputeCrossings
                                    0,  // ComputeOutputs
                                    1,  // ComputeDerivatives
                                    2,  // ComputeAll
                                    0}; // ComputeJacobian
        int stopLabel = equationStructure[sys.demand()];
        double Qw101;
        double time = sys.time();

     // Compute only the minimum needed part of the equations
        if ( computeLabel >= stopLabel ) return;
        switch ( ++computeLabel ) {
          case 1:
            Qw101     = sin(2.0*AC.PI._*AC.f._*time);
            AC.p.v._  = AC.VA._*Qw101;
            R1.p.i._  = (AC.p.v._ - C.v._)/R1.R._;
            AC.p.i._  = - (R1.p.i._ + L.p.i._);
            AC.v._    = AC.p.v._;
            R1.v._    = AC.p.v._ - C.v._;
            R2.n.v._  = AC.p.v._ - R2.R._*L.p.i._;
            R2.v._    = AC.p.v._ - R2.n.v._;
            C.der_v._ = R1.p.i._ / C.C._;
            L.der_i._ = R2.n.v._ / L.L._;
            if ( stopLabel <= 1 ) break;

          case 2:
            G.p.i._   = R1.p.i._ + L.p.i._ + AC.p.i._;
            if ( stopLabel <= 2 ) break;
        computeLabel = stopLabel;

Via member function demand() the variables to be computed can be inquired in order that only the minimum amount of equations are evaluated during simulation. In this case this saves not much. But for more realistic models, this enhances efficiency considerably. The public variable computeLabel is set to zero from the calling environment, whenever one of the input variables has been changed. Together with the scheme above this allows the desired computation of the minimum amount of evaluation at the current time instant.

2.3 Hybrid DSblock

Modelica has several powerful features for hybrid systems. Most of the hybrid language elements are mapped quite directly on the corresponding C++ DSblock. This is demonstrated at hand of a Modelica model containing most of the hybrid features:

   model hybrid "Test hybrid features of Modelica and DSblock"
      // Declarations
         parameter Real    g=9.81;
         parameter Real    hStart = 1;
                   Real    h, v, a;
                   Real    u;
         output    Real    y1;
         output    Real    y2;
                   Boolean yL(start=false);
                   Boolean trigger;

      // Test event operator and crossing functions
         u  = time - 2;
         y1 = if event(u >  1 ) then  1  else
              if event(u < -1 ) then -1  else u;

      // Test when operator and crossing functions
         when y1 > 0.5 do
            yL := true;
         end when;

      // Test when, initial and new operator on Real (jumping ball)
         der(h) = v;
         der(v) = a;
         a      = -g;

         when Initial(time) do
           new(h) := hStart;
         end when;
         when h < 0 do
           new(v) := -0.9*v;
         end when;
      // Test when, sample and new  (generate triangle signal)
         der(y2) = 1.0;
         when Sample(0.0, 0.5) do
            newy2) := 0.0; 
         end when;
      // Test new operator on Boolean
         when Sample(0.0, 0.5) do
            new(trigger) := not trigger;
        end when;   
   end hybrid;

The event operator signals a state event. A when clause is only evaluated when the corresponding expression becomes true. The new operator is used to characterize a new value of the variable, after the corresponding event is processed. The mapping of these language elements into C++ DSblock code is summarized in the following table under the assumption that sys is the instance of class DynamicSystem provided as single argument to function equations:

    Modelica DSblock C++ code
    time sys.time()
    initial sys.initial()
    terminal sys.terminal()
    sample(v1,v2) sys.sample(v1,v2)
    if event (u > 1) then    ... Crossing z;
    z = u - 1;
    if ( sys.sign(z) > 0 ) { ...
    when bvar do
    end when;
    Boolean bvar;
    if ( sys.edge(bvar) ) { ... }
    Control c (enable=bvar); Control c;
    sys.enable(c, bvar);

The operators time, initial, terminal, sample are directly mapped into appropriate C++ operators. An expression, such as "u > 1", has to be first transformed into a zero crossing function z which has to be declared as an instance of the predefined class Crossing. Afterwards, the sign of the crossing function can be inquired and used whenever the Modelica event operator signals a state event. The Crossing class is realized with an overloaded "=" operator:

   void operator= (double v) {if (sys->Event) sys->zSignUpdate(*this, v);
                              zPos = v + Eps;
                              zNeg = v - Eps;}

which computes the correct sign of the crossing function at event instants, stores this sign in the crossing function datastructure and keeps it constant during continuous integration.
The expression of a when clause has to be assigned to a Boolean variable. Afterwards, with operator edge it can be tested when this Boolean becomes true, i.e., the when semantic applies.
Finally, a block component can be enabled or disabled by just calling function enable on the desired component. This function enables or disables automatically all sub-components and variables which are contained in the block, even if the block has a deep hierarchy of components.

Using the mentioned hybrid language construct, the previous Modelica model can be transformed into C++ DSblock code in the following way:

   class Hybrid : public Model { public:
     // variables of model
        Real    g;
        Real    hStart;
        Real    h, v, a, der_h, der_v, new_h, new_v;
        Real    u;
        Real    y1;
        Real    y2, der_y2, new_y2;
        Boolean yL;
        Boolean trigger, new_trigger;

     // auxiliary variables for crossing functions and when clauses
        Crossing z1, z2, z3, z4;
        Boolean  b1, b2, b3, b4, b5;

     void equation(DynamicSystem& sys);
   Hybrid::Hybrid() {
     // register all components
        insert(g          ,"g");
        insert(hStart     ,"hStart");
        insert(h          ,"h");
        insert(v          ,"v");
        insert(a          ,"a");
        insert(der_h      ,"der_h");
        insert(der_v      ,"der_v");
        insert(new_h      ,"new_h");
        insert(new_v      ,"new_v");
        insert(u          ,"u");
        insert(y1         ,"y1");
        insert(y2         ,"y2");
        insert(der_y2     ,"der_y2");
        insert(new_y2     ,"new_y2");
        insert(yL         ,"yL");
        insert(trigger    ,"trigger");

     // define other component attributes
        z1.description("u - 1");
        z2.description("u + 1");
        z3.description("y1 - 0.5");
        b1.description("y - 0.5");
        b4.description("sample(0.0, 0.5)");
        b5.description("sample(0.0, 0.5)");

        description("Test hybrid features of Modelica and DSblock");

   void Hybrid::equation(DynamicSystem& sys) {
     // Equations of simple circuit
        double time = sys.time();

     // Compute only the minimum needed part of the equations
        if ( computeLabel > 0 ) return;

     // Test event operator and crossing functions
        u  = time - 2.0;
        z1 = u._ - 1.0;
        z2 = u._ + 1.0;

        if ( sys.sign(z1) > 0 ) {
           y1 = 1.0;
        } else if ( sys.sign(z2) < 0 ) {
           y1 = -1.0;
        } else {
           y1 = u._;

     // Test when operator and crossing functions
        z3 = y1._ - 0.5;
        b1 = sys.sign(z3) > 0.0;
        if ( sys.edge(b1) ) {
           yL = True;

     // Test when, initial and new operator on Real (jumping ball)
        a     = -g._;
        der_h =  v._;
        der_v =  a._;
        b2    = sys.initial();

        if ( sys.edge(b2) ) {
           new_h = hStart._;

        z4 = h._;
        b3 = sys.sign(z4) < 0;
        if ( sys.edge(b3) ) {
           new_v = -0.9*v._;
     // Test when, sample and new (generate triangle signal)
        der_y2 = 1.0;
        b4 = sys.sample(0.0, 0.5);
        if ( sys.edge(b4) ) {
           new_y2 = 0.0; 
     // Test new operator on Boolean
        b5 = sys.sample(0.0, 0.5);
        if ( sys.edge(b5) ) {
           new_trigger = !trigger._;
        computeLabel = 1;

3. DSblock Interface to Integrator

The DSblock provides a layer between a representation of the model close to the Modelica model and between a representation which is close to the requirements of a numerical integrator. For the latter case, several C++ classes are provided, depending on the underlying mathematical description form of the integrator. Presently, two description forms - hybrid ODEs and hybrid DAEs - are supported. These classes are subclasses of class FullDynamicSystem which in turn is a subclass of class DynamicSystem which is used for the input argument of the equation function.

3.1 Hybrid ODEs (class HybridODE)

A hybrid ODE (Ordinary Differential Equation in state space form) is described by the following equations:

   (1a) Derivative Equations :  dx/dt := f(x, u, p, t, m)
   (1b) Output Equations     :      y := f(x, u, p, t, m)
   (1c) Crossing Functions   :      z := g(x, u, p, t, m)
   (1d) Update Functions     :  [m,x] := h(x, u, p, t, m, xpost)

where the variables have the following meaning:

    t time, the independent (real) variable.
    x(t) (real) state variables.
    u(t) known (real) functions of time.
    p known (constant) parameters of type Real, Integer, Boolean.
    m discrete variables of any type (real, integer, string, ...) defining the current mode.
    y(t) (real) output variables.
    z(t) (real) crossing variables where the zero crossings trigger state events.

The derivative equations (1a) and the output equations (1b) are used for continuous integration. During integration, the discrete variables m are not changed. The crossing functions (1c) are also evaluated during continuous integration. If one of the signals z crosses zero, the integration is halted and an event occurs. The special case of a time event, "z = t - te", is also included. For efficiency reasons, time events are usually treated in a special way, since the time instant of such an event is known in advance. At every event instant, the update functions (1e) are used to determine new values of the discrete variables and of new initial values for the states x. The change of discrete variables may characterize a new structure of a DAE where elements of the state vector x are disabled. In other words, the number of state variables may change at event instants by disabling the appropriate part of the equations. For clarity of the equations, this is not explicitly shown by an additional index in (1).

In (Mosterman and Biswas 1996) it is shown that for the update functions h it is necessary to distinguish the usage of x in equations and assignment statements from the usage in Boolean expressions, such as "if x > 0 then ...". Changes of discrete variables may cause jumps in state variables, and Boolean expressions may have to utilize the values immediately after the jump occurred. In (1) these state variables are supplied as additional function argument xpost, where the index "post" stands for "posterior" value.

The update functions are used in the following iteration procedure to determine new consistent initial conditions:

     xnew := x
        [mnew,xnew]:= h(x, u, p, t, m, xnew)
        if mnew == m then break
           m := mnew
           [mnew,xnew]:= h(x, u, p, t, m, xnew)  
        until mnew == m 
        x := xnew      
     end loop

If, for example, several ideal Coulomb friction elements are used in a model, it is not possible to decide in advance the mode of each friction element (whether it is stuck or it slides) when the relative velocity is zero. An iteration is needed to determine the modes which are compatible with the forces and torques acting on the mechanical device. According to (Mosterman and Biswas 1996) an inner loop is needed in order not to converge to a physically wrong solution. The equations of the inner loop are always evaluated with the state vector x of the last consistent mode m. The inner loop is iterated until the next consistent mode m is found. The start of an integration is treated as an event instant, i.e., an iteration also takes place at the initial time. Note, that the feature with the posterior values is not yet implemented in the DSblock C++ classes described below.

The DSblock C++ interface for the integrators is a straightforward map of the discussed functions. In particular, the following member functions are provided in class HybridODE (all the details can be found in the header file dsblock.h and its implementation dsblock.cpp):

   class HybridODE : public FullDynamicSystem {
       // Evaluation functions
          void setArg         (double x   [], double u[], double time);
          void getArg         (double x   []);  // get arguments after event

          void derivatives    (double xDot[]);  // compute derivatives (1a)
          void outputs        (double y[]);     // compute output variables (1b)
          void crossings      (double z[]);     // compute crossing variables (1c)
          void performInitial ();               // perform initial event (1d)
          void performEvent   ();               // perform event (1d)
          void performTerminal();               // perform terminal event (1d)

       // Inquiry functions
          double nextTimeEvent(); // return next time event
          Bool   dimChanged();    // true, if number of dimensions changed
          int    nxMax();         // maximum number of states
          int    nx   ();         // actual number of state
          int    nu   ();         // number of Real input variables
          int    ny   ();         // number of Real Output variables
          int    nzMax();         // maximum number of crossing variables
          int    nz   ();         // actual number of crossing variables

The setArg function is used to copy the input arguments from the vectors provided by the integrator into the model datastructure discussed in the previous chapter. This function has to be called, before a calculation is performed. Afterwards, different computations can be done, according to the situation at hand, e.g., computation of derivatives or of crossing functions (this is often done at different time instants). Depending on the implementation of the model, the complete model is evaluated once and the evaluation calls such as outputs copy the result back without newly evaluating the model equations or alternativly, only the minimum needed part of the model is computed, depending on the required action.

The update functions (1d) are split into three functions, since at the initial and terminal time of the integration some different actions have to be performed. After an event is processed, modified state variables can be inquired via the standard computational procedures (such as derivatives).

Furthermore, a lot of inquiry functions are provided, especially to inquire the number of dimensions. For present Modelica models it is possible to determine the maximum dimensions of all vectors even for variable structure models, because all components have to be provided and during simulation sub-components can be disabled but no new sub-components can be added dynamically. Both the maximum dimensions, as well as the dimensions of the actual configuration can be inquired at event instants.

3.2 Hybrid DAEs (class HybridDAE)


4. Discussion


This work was supported in part by SiE (Simulation in Europe), the European COSY program and the "Schwerpunktprogramm KONDISK der Deutschen Forschungsgemeinschaft".

Document Actions