model_data.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of ACADO Toolkit.
3  *
4  * ACADO Toolkit -- A Toolkit for Automatic Control and Dynamic Optimization.
5  * Copyright (C) 2008-2014 by Boris Houska, Hans Joachim Ferreau,
6  * Milan Vukov, Rien Quirynen, KU Leuven.
7  * Developed within the Optimization in Engineering Center (OPTEC)
8  * under supervision of Moritz Diehl. All rights reserved.
9  *
10  * ACADO Toolkit is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 3 of the License, or (at your option) any later version.
14  *
15  * ACADO Toolkit is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with ACADO Toolkit; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23  *
24  */
25 
26 
33 #include <acado/ocp/model_data.hpp>
34 
35 
37 
38 
39 
40 //
41 // PUBLIC MEMBER FUNCTIONS:
42 //
43 
45 
46  NX1 = 0;
47  NX2 = 0;
48  NX3 = 0;
49  NDX = 0;
50  NDX3 = 0;
51  NXA = 0;
52  NXA3 = 0;
53  NU = 0;
54  NP = 0;
55  N = 0;
56  NOD = 0;
59  delay = 1;
60 }
61 
62 
63 returnValue ModelData::setDimensions( uint _NX1, uint _NX2, uint _NX3, uint _NDX, uint _NDX3, uint _NXA, uint _NXA3, uint _NU, uint _NOD, uint _NP )
64 {
65  NX1 = _NX1;
66  NX2 = _NX2;
67  NX3 = _NX3;
68  NDX = _NDX;
69  NDX3 = _NDX3;
70  NXA = _NXA;
71  NXA3 = _NXA3;
72  NU = _NU;
73  NP = _NP;
74  NOD = _NOD;
76  return SUCCESSFUL_RETURN;
77 }
78 
79 
80 uint ModelData::addOutput( const OutputFcn& outputEquation_, const Grid& grid ){
81 
82  if( rhs_name.empty() && outputNames.size() == 0 ) {
84  outputEquation_.getExpression( next );
85  outputExpressions.push_back( next );
86  dim_outputs.push_back( next.getDim() );
87 
88  if( NDX == 0 ) NDX = outputEquation_.getNDX();
89  if( NU == 0 ) NU = outputEquation_.getNU();
90  if( NP == 0 ) NP = outputEquation_.getNP();
91  if( NOD == 0 ) NOD = outputEquation_.getNOD();
92 
93  outputGrids.push_back( grid );
94 
95  uint numOuts = (int) ceil((double)grid.getNumIntervals());
96  num_meas.push_back( numOuts );
97  }
98  else {
100  }
101 
102  return dim_outputs.size();
103 }
104 
105 
106 uint ModelData::addOutput( const std::string& output, const std::string& diffs_output, const uint dim, const Grid& grid ){
107 
109  outputNames.push_back( output );
110  diffs_outputNames.push_back( diffs_output );
111  dim_outputs.push_back( dim );
112 
113  outputGrids.push_back( grid );
114 
115  uint numOuts = (int) ceil((double)grid.getNumIntervals());
116  num_meas.push_back( numOuts );
117  }
118  else {
119  return ACADOERROR( RET_INVALID_OPTION );
120  }
121 
122  return dim_outputs.size();
123 }
124 
125 
126 uint ModelData::addOutput( const std::string& output, const std::string& diffs_output, const uint dim,
127  const Grid& grid, const std::string& colInd, const std::string& rowPtr ){
128 
129 
130  DVector colIndV, rowPtrV;
131 
132  colIndV.read( colInd.c_str() );
133  rowPtrV.read( rowPtr.c_str() );
134 
135  if( rowPtrV.getDim() != (dim+1) ) {
136  return ACADOERROR( RET_INVALID_OPTION );
137  }
138  colInd_outputs.push_back( colIndV );
139  rowPtr_outputs.push_back( rowPtrV );
140 
141  return addOutput( output, diffs_output, dim, grid );
142 }
143 
144 
146 
147  if( outputExpressions.size() == 0 && outputNames.size() == 0 ) return BT_FALSE;
148  return BT_TRUE;
149 }
150 
151 
153 
154  _numSteps = numSteps;
155  return SUCCESSFUL_RETURN;
156 }
157 
158 
160 
161  numSteps = _numSteps;
162  return SUCCESSFUL_RETURN;
163 }
164 
165 
166 returnValue ModelData::getOutputExpressions( std::vector<Expression>& outputExpressions_ ) const {
167 
168  outputExpressions_ = outputExpressions;
169  return SUCCESSFUL_RETURN;
170 }
171 
172 
173 std::vector<DMatrix> ModelData::getOutputDependencies( ) const {
174  std::vector<DMatrix> outputDependencies;
175  if( hasCompressedStorage() ) {
176  for( uint i = 0; i < outputNames.size(); i++ ) {
177  DVector colIndV = colInd_outputs[i];
178  DVector rowPtrV = rowPtr_outputs[i];
179 
180  DMatrix dependencyMat = zeros<double>( dim_outputs[i],getNX()+NXA+NU+NDX );
181  int index = 1;
182  for( uint j = 0; j < dim_outputs[i]; j++ ) {
183  uint upper = (uint)rowPtrV(j+1);
184  for( uint k = (uint)rowPtrV(j); k < upper; k++ ) {
185  dependencyMat(j,(uint)colIndV(k-1)-1) = index++;
186  }
187  }
188 
189  outputDependencies.push_back( dependencyMat );
190  }
191  }
192  return outputDependencies;
193 }
194 
195 
196 returnValue ModelData::getOutputGrids( std::vector<Grid>& outputGrids_ ) const {
197 
198  outputGrids_ = outputGrids;
199  return SUCCESSFUL_RETURN;
200 }
201 
202 
204 
206  return SUCCESSFUL_RETURN;
207 }
208 
209 
211 
212  _delay = delay;
213  _parms = parms;
214 
215  return SUCCESSFUL_RETURN;
216 }
217 
218 
220  M1_ = M1;
221  A1_ = A1;
222  B1_ = B1;
223 
224  return SUCCESSFUL_RETURN;
225 }
226 
227 
229  M3_ = M3;
230  A3_ = A3;
231  rhs_ = rhs3;
232 
233  return SUCCESSFUL_RETURN;
234 }
235 
236 
238  C_ = C;
239  feedb_ = feedb;
240 
241  return SUCCESSFUL_RETURN;
242 }
243 
244 
246  M3_ = M3;
247  A3_ = A3;
248 
249  return SUCCESSFUL_RETURN;
250 }
251 
252 
254 {
255  if( rhs_name.empty() && NX2 == 0 ) {
257  Expression rhs;
259 
261  NX2 = rhs.getDim() - NXA;
262  if( NDX == 0 ) NDX = _f.getNDX();
263 // if( _f.getNDX() > 0 && _f.getNDX() != (int)NX2 ) { // TODO: this test returns an error for well-defined models when using a linear input subsystem!
264 // std::cout << "nonlinear model of size " << NX2 << " depends on " << _f.getNDX() << " differential state derivatives!" << std::endl;
265 // return ACADOERROR( RET_INVALID_OPTION );
266 // }
267  if( NU == 0 ) NU = _f.getNU();
268  if( NP == 0 ) NP = _f.getNP();
269  if( NOD == 0 ) NOD = _f.getNOD();
270 
272  }
273  else {
274  return ACADOERROR( RET_INVALID_OPTION );
275  }
276  return SUCCESSFUL_RETURN;
277 }
278 
279 
280 returnValue ModelData::setNARXmodel( const uint _delay, const DMatrix& _parms ) {
281 
282  if( rhs_name.empty() && NX2 == 0 && NX3 == 0 ) {
283  NX2 = _parms.getNumRows();
284  delay = _delay;
285  uint numParms = 1;
286  uint n = delay*getNX();
287  if( delay >= 1 ) {
288  numParms = numParms + n;
289  }
290  for( uint i = 1; i < delay; i++ ) {
291  numParms = numParms + (n+1)*(uint)pow((double)n,(int)i)/2;
292  }
293  if( _parms.getNumCols() == numParms ) {
294  parms = _parms;
295  }
296  else {
298  }
299 
301  }
302  else {
303  return ACADOERROR( RET_INVALID_OPTION );
304  }
305  return SUCCESSFUL_RETURN;
306 }
307 
308 
309 returnValue ModelData::setLinearInput( const DMatrix& M1_, const DMatrix& A1_, const DMatrix& B1_ )
310 {
311  M1 = M1_;
312  A1 = A1_;
313  B1 = B1_;
314  NX1 = A1.getNumCols();
315  NU = B1.getNumCols();
317 
318  return SUCCESSFUL_RETURN;
319 }
320 
321 
322 returnValue ModelData::setLinearOutput( const DMatrix& M3_, const DMatrix& A3_, const OutputFcn& rhs3_ )
323 {
324  M3 = M3_;
325  A3 = A3_;
326  NX3 = A3.getNumCols();
327 
328  rhs3 = rhs3_;
329  if( NDX == 0 ) NDX = rhs3_.getNDX();
330  if( NU == 0 ) NU = rhs3_.getNU();
331  if( NP == 0 ) NP = rhs3_.getNP();
332  if( NOD == 0 ) NOD = rhs3_.getNOD();
333 
335 
336  return SUCCESSFUL_RETURN;
337 }
338 
339 
341 {
342  C = C_;
343  feedb = feedb_;
344  if( feedb_.getNDX() > 0 || feedb_.getNU() > 0 || feedb_.getNP() > 0 || feedb_.getNOD() > 0 ) return RET_NOT_IMPLEMENTED_YET;
345 
347 
348  return SUCCESSFUL_RETURN;
349 }
350 
351 
352 returnValue ModelData::setLinearOutput( const DMatrix& M3_, const DMatrix& A3_, const std::string& rhs3_, const std::string& diffs3_ )
353 {
354  if( !export_rhs ) {
355  M3 = M3_;
356  A3 = A3_;
357  NX3 = A3.getNumCols();
358 
359  rhs3_name = std::string(rhs3_);
360  diffs3_name = std::string(diffs3_);
361  }
362  else {
363  return ACADOERROR( RET_INVALID_OPTION );
364  }
365 
366  return SUCCESSFUL_RETURN;
367 }
368 
369 
370 returnValue ModelData::setModel( const std::string& fileName, const std::string& _rhs_ODE, const std::string& _diffs_ODE )
371 {
373  {
374  externModel = fileName;
375  rhs_name = _rhs_ODE;
376  diffs_name = _diffs_ODE;
377 
379  }
380  else
381  {
382  return ACADOERROR( RET_INVALID_OPTION );
383  }
384 
385  return SUCCESSFUL_RETURN;
386 }
387 
388 
390 {
391  integrationGrid_ = integrationGrid;
392  return SUCCESSFUL_RETURN;
393 }
394 
395 
396 returnValue ModelData::setIntegrationGrid( const Grid& _ocpGrid, const uint _numSteps
397  )
398 {
399  uint i;
400  N = _ocpGrid.getNumIntervals();
401  BooleanType equidistantControl = _ocpGrid.isEquidistant();
402  double T = _ocpGrid.getLastTime() - _ocpGrid.getFirstTime();
403  double h = T/((double)_numSteps);
404  DVector stepsVector( N );
405 
407  {
408  for( i = 0; i < stepsVector.getDim(); i++ )
409  {
410  stepsVector(i) = (int) acadoRound((_ocpGrid.getTime(i+1)-_ocpGrid.getTime(i))/h);
411  }
412 
413  if( equidistantControl )
414  {
415  // Setup fixed integrator grid for equidistant control grid
416  integrationGrid = Grid( 0.0, ((double) T)/((double) N), (int) ceil((double)_numSteps/((double) N) - 10.0*EPS) + 1 );
417  }
418  else
419  {
420  // Setup for non equidistant control grid
421  // NOTE: This grid defines only one integration step because the control
422  // grid is non equidistant.
423  integrationGrid = Grid( 0.0, h, 2 );
424  numSteps = stepsVector;
425  }
426  }
427  return SUCCESSFUL_RETURN;
428 }
429 
430 
432 {
433  integrationGrid = Grid();
434 
435  return SUCCESSFUL_RETURN;
436 }
437 
438 
440 {
441  return numSteps.isEmpty();
442 }
443 
444 
446 
447  if( outputExpressions.size() == 0 ) return BT_FALSE;
448  return BT_TRUE;
449 }
450 
451 
453 
454  if( differentialEquation.getDim() == 0 ) return BT_FALSE;
455  return BT_TRUE;
456 }
457 
458 
460 
461  return model_dimensions_set;
462 }
463 
464 
466 
467  return export_rhs;
468 }
469 
470 
472 
473  if( colInd_outputs.size() == 0 ) return BT_FALSE;
474  return BT_TRUE;
475 }
476 
477 
479 {
480  if( parms.isEmpty() ) {
481  return NX1+NX2+NX3;
482  }
483  else {
484  return delay*(NX1+NX2)+NX3; // IMPORTANT for NARX models where the state space is increased because of the delay
485  }
486 }
487 
488 
490 {
491  return NX1;
492 }
493 
494 
496 {
497  return NX2;
498 }
499 
500 
502 {
503  return NX3;
504 }
505 
506 
508 {
509  if( NDX > 0 ) {
510  return getNX();
511  }
512  return NDX;
513 }
514 
515 
517 {
518  if( NDX3 > 0 ) {
519  return NX1+NX2;
520  }
521  return NDX3;
522 }
523 
524 
526 {
527  return NXA;
528 }
529 
530 
532 {
533  if( NXA3 > 0 ) {
534  return NXA;
535  }
536  return NXA3;
537 }
538 
539 
541 {
542  return NU;
543 }
544 
545 
547 {
548  return NP;
549 }
550 
552 {
553  return NOD;
554 }
555 
556 
558 {
559  return N;
560 }
561 
562 
564 {
565  N = N_;
566  return SUCCESSFUL_RETURN;
567 }
568 
569 
571 {
572  NU = NU_;
573  return SUCCESSFUL_RETURN;
574 }
575 
576 
578 {
579  NP = NP_;
580  return SUCCESSFUL_RETURN;
581 }
582 
583 
585 {
586  NOD = NOD_;
587  return SUCCESSFUL_RETURN;
588 }
589 
590 
592 {
593  DVector nOutV( (uint)dim_outputs.size() );
594  for( uint i = 0; i < dim_outputs.size(); i++ ) {
595  nOutV(i) = dim_outputs[i];
596  }
597  return nOutV;
598 }
599 
600 
602 {
603  return (uint)outputGrids.size();
604 }
605 
606 
607 returnValue ModelData::getDimOutputs( std::vector<uint>& dims ) const
608 {
609  dims = dim_outputs;
610  return SUCCESSFUL_RETURN;
611 }
612 
613 
615 {
616  DVector nMeasV( (uint)num_meas.size() );
617  for( uint i = 0; i < num_meas.size(); i++ ) {
618  nMeasV(i) = num_meas[i];
619  }
620  return nMeasV;
621 }
622 
623 
624 const std::string ModelData::getFileNameModel() const {
625  return externModel;
626 }
627 
628 
629 const std::string ModelData::getNameRhs() const {
630  return rhs_name;
631 }
632 
633 
634 const std::string ModelData::getNameDiffsRhs() const {
635  return diffs_name;
636 }
637 
638 
639 const std::string ModelData::getNameOutput() const {
640  return rhs3_name;
641 }
642 
643 
644 const std::string ModelData::getNameDiffsOutput() const {
645  return diffs3_name;
646 }
647 
648 
649 returnValue ModelData::getNameOutputs( std::vector<std::string>& names ) const {
650  names = outputNames;
651  return SUCCESSFUL_RETURN;
652 }
653 
654 
655 returnValue ModelData::getNameDiffsOutputs( std::vector<std::string>& names ) const {
656  names = diffs_outputNames;
657  return SUCCESSFUL_RETURN;
658 }
659 
660 
661 
662 // PROTECTED:
663 
664 
665 
666 
668 
669 // end of file.
std::vector< DVector > colInd_outputs
Definition: model_data.hpp:466
bool isEmpty() const
Definition: matrix.hpp:193
DVector numSteps
Definition: model_data.hpp:458
returnValue setIntegrationGrid(const Grid &_ocpGrid, const uint _numSteps)
Definition: model_data.cpp:396
int getNOD() const
Definition: function.cpp:257
USING_NAMESPACE_ACADO typedef TaylorVariable< Interval > T
DifferentialEquation differentialEquation
Definition: model_data.hpp:455
Allows to setup and evaluate output functions based on SymbolicExpressions.
Definition: output_fcn.hpp:55
returnValue setNP(const uint NP_)
Definition: model_data.cpp:577
double getTime(uint pointIdx) const
returnValue getNonlinearFeedback(DMatrix &C_, OutputFcn &feedb_) const
Definition: model_data.cpp:237
OutputFcn feedb
Definition: model_data.hpp:483
int getNumDynamicEquations() const
std::vector< DMatrix > getOutputDependencies() const
Definition: model_data.cpp:173
double getFirstTime() const
DVector getNumMeas() const
Definition: model_data.cpp:614
BooleanType modelDimensionsSet() const
Definition: model_data.cpp:459
DVector getDimOutputs() const
Definition: model_data.cpp:591
returnValue getOutputExpressions(std::vector< Expression > &outputExpressions_) const
Definition: model_data.cpp:166
BEGIN_NAMESPACE_ACADO const double EPS
std::vector< uint > dim_outputs
Definition: model_data.hpp:462
int getNDX() const
Definition: function.cpp:217
BooleanType isEquidistant() const
BooleanType hasEquidistantControlGrid() const
Definition: model_data.cpp:439
returnValue setNonlinearFeedback(const DMatrix &C_, const OutputFcn &feedb_)
Definition: model_data.cpp:340
DMatrix A1
Definition: model_data.hpp:475
Allows to pass back messages to the calling function.
std::string externModel
Definition: model_data.hpp:450
IntermediateState pow(const Expression &arg1, const Expression &arg2)
std::vector< uint > num_meas
Definition: model_data.hpp:463
returnValue setNU(const uint NU_)
Definition: model_data.cpp:570
returnValue getModel(DifferentialEquation &_f) const
Definition: model_data.cpp:203
int getNU() const
Definition: function.cpp:222
DMatrix parms
Definition: model_data.hpp:487
BEGIN_NAMESPACE_ACADO typedef unsigned int uint
Definition: acado_types.hpp:42
Allows to conveniently handle (one-dimensional) grids consisting of time points.
Definition: grid.hpp:58
BooleanType isEmpty() const
std::string diffs_name
Definition: model_data.hpp:452
returnValue setLinearInput(const DMatrix &M1_, const DMatrix &A1_, const DMatrix &B1_)
Definition: model_data.cpp:309
DMatrix M1
Definition: model_data.hpp:474
#define CLOSE_NAMESPACE_ACADO
bool isEmpty() const
Definition: vector.hpp:176
int getNXA() const
Definition: function.cpp:212
returnValue getExpression(Expression &expression) const
Definition: function.cpp:139
returnValue getLinearOutput(DMatrix &M3_, DMatrix &A3_, OutputFcn &rhs_) const
Definition: model_data.cpp:228
uint getNXA3() const
Definition: model_data.cpp:531
const std::string getFileNameModel() const
Definition: model_data.cpp:624
uint getNX1() const
Definition: model_data.cpp:489
std::vector< DVector > rowPtr_outputs
Definition: model_data.hpp:467
DMatrix A3
Definition: model_data.hpp:479
returnValue setNumSteps(const DVector &_numSteps)
Definition: model_data.cpp:159
std::string rhs_name
Definition: model_data.hpp:451
Base class for all variables within the symbolic expressions family.
Definition: expression.hpp:56
Grid integrationGrid
Definition: model_data.hpp:457
returnValue getNameDiffsOutputs(std::vector< std::string > &names) const
Definition: model_data.cpp:655
const std::string getNameRhs() const
Definition: model_data.cpp:629
uint addOutput(const OutputFcn &outputEquation_, const Grid &measurements)
Definition: model_data.cpp:80
const std::string getNameDiffsOutput() const
Definition: model_data.cpp:644
const std::string getNameDiffsRhs() const
Definition: model_data.cpp:634
std::vector< Expression > outputExpressions
Definition: model_data.hpp:460
int getNP() const
Definition: function.cpp:233
uint getN() const
Definition: model_data.cpp:557
returnValue setLinearOutput(const DMatrix &M3_, const DMatrix &A3_, const OutputFcn &rhs_)
Definition: model_data.cpp:322
DMatrix M3
Definition: model_data.hpp:478
uint getNDX() const
Definition: model_data.cpp:507
returnValue setModel(const DifferentialEquation &_f)
Definition: model_data.cpp:253
returnValue getIntegrationGrid(Grid &integrationGrid_) const
Definition: model_data.cpp:389
DMatrix C
Definition: model_data.hpp:482
unsigned getDim() const
Definition: vector.hpp:172
returnValue setNOD(const uint NOD_)
Definition: model_data.cpp:584
int acadoRound(double x)
int getDim() const
uint getNU() const
Definition: model_data.cpp:540
returnValue getOutputGrids(std::vector< Grid > &outputGrids_) const
Definition: model_data.cpp:196
std::string diffs3_name
Definition: model_data.hpp:454
BooleanType model_dimensions_set
Definition: model_data.hpp:449
std::vector< Grid > outputGrids
Definition: model_data.hpp:461
BooleanType hasOutputs() const
Definition: model_data.cpp:145
void rhs(const real_t *x, real_t *f)
returnValue setNARXmodel(const uint _delay, const DMatrix &_parms)
Definition: model_data.cpp:280
returnValue clearIntegrationGrid()
Definition: model_data.cpp:431
BooleanType hasCompressedStorage() const
Definition: model_data.cpp:471
unsigned getNumRows() const
Definition: matrix.hpp:185
OutputFcn rhs3
Definition: model_data.hpp:480
#define BT_TRUE
Definition: acado_types.hpp:47
uint getNXA() const
Definition: model_data.cpp:525
uint getNumOutputs() const
Definition: model_data.cpp:601
std::string rhs3_name
Definition: model_data.hpp:453
unsigned getNumCols() const
Definition: matrix.hpp:189
uint getNumIntervals() const
returnValue getNumSteps(DVector &_numSteps) const
Definition: model_data.cpp:152
BooleanType hasDifferentialEquation() const
Definition: model_data.cpp:452
returnValue getNARXmodel(uint &_delay, DMatrix &_parms) const
Definition: model_data.cpp:210
returnValue getLinearInput(DMatrix &M1_, DMatrix &A1_, DMatrix &B1_) const
Definition: model_data.cpp:219
std::vector< std::string > outputNames
Definition: model_data.hpp:464
BooleanType export_rhs
Definition: model_data.hpp:448
double getLastTime() const
Expression next(const Expression &arg)
uint getNX() const
Definition: model_data.cpp:478
#define BEGIN_NAMESPACE_ACADO
returnValue setDimensions(uint _NX1, uint _NX2, uint _NX3, uint _NDX, uint _NDX3, uint _NXA, uint _NXA3, uint _NU, uint _NOD, uint _NP)
Definition: model_data.cpp:63
BooleanType hasOutputFunctions() const
Definition: model_data.cpp:445
#define BT_FALSE
Definition: acado_types.hpp:49
USING_NAMESPACE_ACADO void output(const char *name, const Expression &e)
std::vector< std::string > diffs_outputNames
Definition: model_data.hpp:465
uint getNDX3() const
Definition: model_data.cpp:516
const std::string getNameOutput() const
Definition: model_data.cpp:639
returnValue getNameOutputs(std::vector< std::string > &names) const
Definition: model_data.cpp:649
uint getNOD() const
Definition: model_data.cpp:551
uint getNX3() const
Definition: model_data.cpp:501
uint getNP() const
Definition: model_data.cpp:546
DMatrix B1
Definition: model_data.hpp:476
BooleanType exportRhs() const
Definition: model_data.cpp:465
returnValue setN(const uint N_)
Definition: model_data.cpp:563
uint getNX2() const
Definition: model_data.cpp:495
#define ACADOERROR(retval)
Allows to setup and evaluate differential equations (ODEs and DAEs) based on SymbolicExpressions.
virtual returnValue read(std::istream &stream)
Definition: vector.cpp:251
uint getDim() const


acado
Author(s): Milan Vukov, Rien Quirynen
autogenerated on Mon Jun 10 2019 12:34:54