1 
2 /* Siconos is a program dedicated to modeling, simulation and control
3  * of non smooth dynamical systems.
4  *
5  * Copyright 2021 INRIA.
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 
20 #include "LsodarOSI.hpp"
21 #include "SiconosAlgebraProd.hpp"
22 #include "EventDriven.hpp"
23 #include "LagrangianLinearTIDS.hpp"
24 #include "BlockVector.hpp"
25 #include "NonSmoothDynamicalSystem.hpp"
26 #include "Topology.hpp"
27 #include "LagrangianRheonomousR.hpp"
28 #include "LagrangianScleronomousR.hpp"
29 #include "NewtonImpactNSL.hpp"
30 #include "MultipleImpactNSL.hpp"
31 #include "NewtonImpactFrictionNSL.hpp"
32 #include "FirstOrderNonLinearDS.hpp"
33 #include "ExtraAdditionalTerms.hpp"
34 #include "OneStepNSProblem.hpp"
35 #include "TypeName.hpp"
36 #include <odepack.h>
37 
38 using namespace RELATION;
39 
40 // #define DEBUG_NOCOLOR
41 // #define DEBUG_STDOUT
42 // #define DEBUG_MESSAGES
43 #include "siconos_debug.h"
44 
45 int LsodarOSI::count_NST = 0;
46 int LsodarOSI::count_NFE = 0;
47 
48 // ===== Out of class objects and functions =====
49 
50 // global object and wrapping functions -> required for function plug-in and call in fortran routine.
51 SP::LsodarOSI global_object;
52 
53 // This first function must have the same signature as argument F (arg 1) in DLSODAR (see opkdmain.f in Numerics)
54 extern "C" void LsodarOSI_f_wrapper(integer* sizeOfX, doublereal* time, doublereal* x, doublereal* xdot);
LsodarOSI_f_wrapper(integer * sizeOfX,doublereal * time,doublereal * x,doublereal * xdot)55 extern "C" void LsodarOSI_f_wrapper(integer* sizeOfX, doublereal* time, doublereal* x, doublereal* xdot)
56 {
57   return global_object->f(sizeOfX, time, x, xdot);
58 }
59 
60 // Function to wrap g: same signature as argument G (arg 18) in DLSODAR (see opkdmain.f in Numerics)
61 extern "C" void LsodarOSI_g_wrapper(integer* nEq, doublereal* time, doublereal* x, integer* ng, doublereal* gOut);
LsodarOSI_g_wrapper(integer * nEq,doublereal * time,doublereal * x,integer * ng,doublereal * gOut)62 extern "C" void LsodarOSI_g_wrapper(integer* nEq, doublereal* time, doublereal* x, integer* ng, doublereal* gOut)
63 {
64   return global_object->g(nEq, time, x, ng, gOut);
65 }
66 
67 // Function to wrap jacobianf: same signature as argument JAC (arg 16) in DLSODAR (see opkdmain.f in Numerics)
68 extern "C" void LsodarOSI_jacobianf_wrapper(integer* sizeOfX, doublereal* time, doublereal* x, integer* ml, integer* mu,  doublereal* jacob, integer* nrowpd);
LsodarOSI_jacobianf_wrapper(integer * sizeOfX,doublereal * time,doublereal * x,integer * ml,integer * mu,doublereal * jacob,integer * nrowpd)69 extern "C" void LsodarOSI_jacobianf_wrapper(integer* sizeOfX, doublereal* time, doublereal* x, integer* ml, integer* mu,  doublereal* jacob, integer* nrowpd)
70 {
71   return global_object->jacobianfx(sizeOfX, time, x, ml, mu, jacob, nrowpd);
72 }
73 
LsodarOSI()74 LsodarOSI::LsodarOSI():
75   OneStepIntegrator(OSI::LSODAROSI)
76 {
77 
78   _sizeTol =1;
79   _itol=1;
80   _intData.resize(9);
81   for(int i = 0; i < 9; i++) _intData[i] = 0;
82   _sizeMem = 2;
83   _steps=1;
84 
85   // Set levels. This may depend on the nonsmooth law and will be updated during initializeWorkVectorsForInteraction(...) call.
86   _levelMinForOutput=0;
87   _levelMaxForOutput=2;
88   _levelMinForInput=1;
89   _levelMaxForInput=2;
90 
91   rtol.reset(new doublereal[_sizeTol]) ;  // rtol, relative tolerance
92   atol.reset(new doublereal[_sizeTol]) ;  // atol, absolute tolerance
93   // Set atol and rtol values ...
94   rtol[0] = RTOL_DEFAULT ; // rtol
95   atol[0] = ATOL_DEFAULT ;  // atol
96 }
97 
setTol(integer newItol,SA::doublereal newRtol,SA::doublereal newAtol)98 void LsodarOSI::setTol(integer newItol, SA::doublereal newRtol, SA::doublereal newAtol)
99 {
100   //            The input parameters ITOL, RTOL, and ATOL determine
101   //         the error control performed by the solver.  The solver will
102   //         control the vector E = (E(i)) of estimated local errors
103   //         in y, according to an inequality of the form
104   //                     max-norm of ( E(i)/EWT(i) )   .le.   1,
105   //         where EWT = (EWT(i)) is a vector of positive error weights.
106   //         The values of RTOL and ATOL should all be non-negative.
107   //         The following table gives the types (scalar/array) of
108   //         RTOL and ATOL, and the corresponding form of EWT(i).
109   //
110   //            ITOL    RTOL       ATOL          EWT(i)
111   //             1     scalar     scalar     RTOL*ABS(Y(i)) + ATOL
112   //             2     scalar     array      RTOL*ABS(Y(i)) + ATOL(i)
113   //             3     array      scalar     RTOL(i)*ABS(Y(i)) + ATOL
114   //             4     array      array      RTOL(i)*ABS(Y(i)) + ATOL(i)
115 
116   _itol = newItol; // itol
117   rtol = newRtol;
118   atol = newAtol;
119 }
120 
setMinMaxStepSizes(doublereal minStep,doublereal maxStep)121 void LsodarOSI::setMinMaxStepSizes(doublereal minStep, doublereal maxStep)
122 {
123   _intData[5] = 1; // set IOPT = 1
124   rwork[5] = minStep;
125   rwork[6] = maxStep;
126 }
127 
setMaxNstep(integer maxNumberSteps)128 void LsodarOSI::setMaxNstep(integer maxNumberSteps)
129 {
130   _intData[5] = 1; // set IOPT = 1
131   iwork[5] = maxNumberSteps;
132 }
133 
setTol(integer newItol,doublereal newRtol,doublereal newAtol)134 void LsodarOSI::setTol(integer newItol, doublereal newRtol, doublereal newAtol)
135 {
136   _itol = newItol; // itol
137   rtol[0] = newRtol; // rtol
138   atol[0] = newRtol;  // atol
139 }
140 
setMaxOrder(integer maxorderNonStiff,integer maxorderStiff)141 void LsodarOSI::setMaxOrder(integer maxorderNonStiff, integer maxorderStiff)
142 {
143   _intData[5] = 1; // set IOPT = 1
144   iwork[7] = maxorderNonStiff;
145   iwork[8] = maxorderStiff;
146 }
147 
updateData()148 void LsodarOSI::updateData()
149 {
150   // Used to update some data (iwork ...) when _intData is modified.
151   // Warning: it only checks sizes and possibly reallocate memory, but no values are set.
152 
153   iwork.reset(new integer[_intData[7]]);
154   for(int i = 0; i < _intData[7]; i++) iwork[i] = 0;
155 
156   // This is for documentation purposes only
157   // Set the flag to generate extra printing at method switches.
158   //iwork[4] = 0;
159   // Set the maximal number of steps for one call
160   //iwork[5] = 0;
161   // set  the maximum number of messages printed (per problem)
162   //iwork[6] = 0;
163   // Set the maximum order to be allowed for the nonstiff (Adams) method
164   //iwork[7] = 0;
165   // Set   the maximum order to be allowed for the stiff  (BDF) method.
166   //iwork[8] = 0;
167 
168   rwork.reset(new doublereal[_intData[6]]);
169   for(int i = 0; i < _intData[6]; i++) rwork[i] = 0.0;
170 
171   jroot.reset(new integer[_intData[1]]);
172   for(int i = 0; i < _intData[1]; i++) jroot[i] = 0;
173 
174 }
175 
fillXWork(integer * sizeOfX,doublereal * x)176 void LsodarOSI::fillXWork(integer* sizeOfX, doublereal* x)
177 {
178   assert((unsigned int)(*sizeOfX) == _xWork->size() && "LsodarOSI::fillXWork xWork and sizeOfX have different sizes");
179   (*_xWork) = x;
180 }
181 
computeRhs(double t)182 void LsodarOSI::computeRhs(double t)
183 {
184   DEBUG_BEGIN("LsodarOSI::computeRhs(double t, DynamicalSystemsGraph& DSG0)\n")
185   DynamicalSystemsGraph::VIterator dsi, dsend;
186   for(std::tie(dsi, dsend) = _dynamicalSystemsGraph->vertices(); dsi != dsend; ++dsi)
187   {
188     if(!checkOSI(dsi)) continue;
189     SP::DynamicalSystem ds = _dynamicalSystemsGraph->bundle(*dsi);
190     // compute standard rhs stored in the dynamical system
191     ds->computeRhs(t);
192     DEBUG_EXPR(ds->getRhs().display(););
193     /* This next line is a good protection  */
194     assert(_dynamicalSystemsGraph->properties(*dsi).workVectors);
195     VectorOfVectors& workVectors = *_dynamicalSystemsGraph->properties(*dsi).workVectors;
196     Type::Siconos dsType = Type::value(*ds);
197     if(dsType == Type::LagrangianLinearTIDS || dsType == Type::LagrangianDS)
198     {
199       SP::LagrangianDS lds = std::static_pointer_cast<LagrangianDS> (ds);
200       SiconosVector &free=*workVectors[LsodarOSI::FREE];
201       // we assume that inverseMass and forces are updated after call of ds->computeRhs(t);
202       free = *lds->forces();
203       if(lds->inverseMass())
204         lds->inverseMass()->Solve(free);
205       DEBUG_EXPR(free.display(););
206     }
207     if(_extraAdditionalTerms)
208     {
209       DynamicalSystemsGraph::VDescriptor dsgVD = _dynamicalSystemsGraph->descriptor(ds);
210       _extraAdditionalTerms->addSmoothTerms(*_dynamicalSystemsGraph, dsgVD, t, ds->getRhs());
211     }
212   }
213   DEBUG_END("LsodarOSI::computeRhs(double t, DynamicalSystemsGraph& DSG0)\n")
214 
215 }
216 
computeJacobianRhs(double t,DynamicalSystemsGraph & DSG0)217 void LsodarOSI::computeJacobianRhs(double t, DynamicalSystemsGraph& DSG0)
218 {
219 
220   DynamicalSystemsGraph::VIterator dsi, dsend;
221   for(std::tie(dsi, dsend) = _dynamicalSystemsGraph->vertices(); dsi != dsend; ++dsi)
222   {
223     if(!checkOSI(dsi)) continue;
224     SP::DynamicalSystem ds = _dynamicalSystemsGraph->bundle(*dsi);
225     ds->computeJacobianRhsx(t);
226     if(_extraAdditionalTerms)
227     {
228       DynamicalSystemsGraph::VDescriptor dsgVD = DSG0.descriptor(ds);
229       _extraAdditionalTerms->addJacobianRhsContribution(DSG0, dsgVD, t, *(ds->jacobianRhsx()));
230     }
231   }
232 }
233 
f(integer * sizeOfX,doublereal * time,doublereal * x,doublereal * xdot)234 void LsodarOSI::f(integer* sizeOfX, doublereal* time, doublereal* x, doublereal* xdot)
235 {
236   std::static_pointer_cast<EventDriven>(_simulation)->computef(*this, sizeOfX, time, x, xdot);
237 }
238 
g(integer * nEq,doublereal * time,doublereal * x,integer * ng,doublereal * gOut)239 void LsodarOSI::g(integer* nEq, doublereal*  time, doublereal* x, integer* ng, doublereal* gOut)
240 {
241   std::static_pointer_cast<EventDriven>(_simulation)->computeg(shared_from_this(), nEq, time, x, ng, gOut);
242 }
243 
jacobianfx(integer * sizeOfX,doublereal * time,doublereal * x,integer * ml,integer * mu,doublereal * jacob,integer * nrowpd)244 void LsodarOSI::jacobianfx(integer* sizeOfX, doublereal* time, doublereal* x, integer* ml, integer* mu,  doublereal* jacob, integer* nrowpd)
245 {
246   std::static_pointer_cast<EventDriven>(_simulation)->computeJacobianfx(*this, sizeOfX, time, x, jacob);
247 }
248 
249 
initializeWorkVectorsForDS(double t,SP::DynamicalSystem ds)250 void LsodarOSI::initializeWorkVectorsForDS(double t, SP::DynamicalSystem ds)
251 {
252   DEBUG_BEGIN("LsodarOSI::initializeWorkVectorsForDS( double t, SP::DynamicalSystem ds)\n");
253   // Get work buffers from the graph
254   VectorOfVectors& ds_work_vectors = *_initializeDSWorkVectors(ds);
255 
256   Type::Siconos dsType = Type::value(*ds);
257 
258   ds->initRhs(t); // This will create p[2] and other required vectors/buffers
259 
260   if(dsType == Type::LagrangianDS || dsType == Type::LagrangianLinearTIDS)
261   {
262     LagrangianDS& lds = *std::static_pointer_cast<LagrangianDS>(ds);
263     // TODO FP: use buffer in graph for xWork?
264     if(!_xWork)
265       _xWork.reset(new BlockVector());
266     _xWork->insertPtr(lds.q());
267     _xWork->insertPtr(lds.velocity());
268     ds_work_vectors.resize(LsodarOSI::WORK_LENGTH);
269     ds_work_vectors[LsodarOSI::FREE].reset(new SiconosVector(lds.dimension()));
270   }
271   else
272   {
273     if(!_xWork)
274       _xWork.reset(new BlockVector());
275     _xWork->insertPtr(ds->x());
276   }
277   ds->swapInMemory();
278 
279   // Update necessary data
280 
281   // 1 - Neq; x vector size.
282   _intData[0] = _xWork->size();
283   // 5 - lrw, size of rwork
284   _intData[6] = 22 + _intData[0] * std::max(16, (int)_intData[0] + 9) + 3 * _intData[1];
285   // 6 - liw, size of iwork
286   _intData[7] = 20 + _intData[0];
287 
288   // memory allocation for doublereal*, according to _intData values
289   updateData();
290 
291   _xtmp.reset(new SiconosVector(_xWork->size()));
292 
293   computeRhs(t);
294 
295   DEBUG_END("LsodarOSI::initializeWorkVectorsForDS( double t, SP::DynamicalSystem ds)\n");
296 }
297 
initializeWorkVectorsForInteraction(Interaction & inter,InteractionProperties & interProp,DynamicalSystemsGraph & DSG)298 void LsodarOSI::initializeWorkVectorsForInteraction(Interaction &inter,
299     InteractionProperties& interProp,
300     DynamicalSystemsGraph & DSG)
301 {
302   SP::DynamicalSystem ds1= interProp.source;
303   SP::DynamicalSystem ds2= interProp.target;
304   assert(ds1);
305   assert(ds2);
306 
307 
308   VectorOfBlockVectors& DSlink = inter.linkToDSVariables();
309   if(!interProp.workVectors)
310   {
311     interProp.workVectors.reset(new VectorOfVectors);
312     interProp.workVectors->resize(LsodarOSI::WORK_INTERACTION_LENGTH);
313   }
314 
315   if(!interProp.workBlockVectors)
316   {
317     interProp.workBlockVectors.reset(new VectorOfBlockVectors);
318     interProp.workBlockVectors->resize(LsodarOSI::BLOCK_WORK_LENGTH);
319   }
320 
321   VectorOfVectors& inter_work = *interProp.workVectors;
322   VectorOfBlockVectors& inter_work_block = *interProp.workBlockVectors;
323 
324   Relation &relation =  *inter.relation();
325   RELATION::TYPES relationType = relation.getType();
326 
327   inter_work[LsodarOSI::OSNSP_RHS].reset(new SiconosVector(inter.dimension()));
328 
329   NonSmoothLaw & nslaw = *inter.nonSmoothLaw();
330 
331   Type::Siconos nslType = Type::value(nslaw);
332 
333   if(nslType == Type::NewtonImpactNSL || nslType == Type::MultipleImpactNSL)
334   {
335     _levelMinForOutput = 0;
336     _levelMaxForOutput = 2 ;
337     _levelMinForInput = 1;
338     _levelMaxForInput = 2;
339   }
340   else if(nslType ==  Type::NewtonImpactFrictionNSL)
341   {
342     _levelMinForOutput = 0;
343     _levelMaxForOutput = 4;
344     _levelMinForInput = 1;
345     _levelMaxForInput = 2;
346     THROW_EXCEPTION("LsodarOSI::initializeWorkVectorsForInteraction  not yet implemented for nonsmooth law of type NewtonImpactFrictionNSL");
347   }
348   else
349     THROW_EXCEPTION("LsodarOSI::initializeWorkVectorsForInteraction not yet implemented  for nonsmooth of type");
350 
351   // Check if interations levels (i.e. y and lambda sizes) are compliant with the current osi.
352   _check_and_update_interaction_levels(inter);
353   // Initialize/allocate memory buffers in interaction.
354   inter.initializeMemory(_steps);
355 
356   /* allocate and set work vectors for the osi */
357 
358   if(!(checkOSI(DSG.descriptor(ds1)) && checkOSI(DSG.descriptor(ds2))))
359   {
360     THROW_EXCEPTION("LsodarOSI::initializeWorkVectorsForInteraction. The implementation is not correct for two different OSI for one interaction");
361   }
362 
363 
364 
365   VectorOfVectors &workVds1 = *DSG.properties(DSG.descriptor(ds1)).workVectors;
366   if(relationType == Lagrangian)
367   {
368     LagrangianDS& lds = *std::static_pointer_cast<LagrangianDS> (ds1);
369     inter_work_block[LsodarOSI::xfree].reset(new BlockVector());
370     inter_work_block[LsodarOSI::xfree]->insertPtr(workVds1[LsodarOSI::FREE]);
371     DSlink[LagrangianR::p2].reset(new BlockVector());
372     DSlink[LagrangianR::p2]->insertPtr(lds.p(2));
373     DSlink[LagrangianR::q2].reset(new BlockVector());
374     DSlink[LagrangianR::q2]->insertPtr(lds.acceleration());
375   }
376   // else if (relationType == NewtonEuler)
377   // {
378   //   inter_work_block[::xfree].reset(new BlockVector());
379   //   inter_work_block[::xfree]->insertPtr(workVds1[LsodarOSI::FREE]);
380   // }
381 
382 
383 
384   if(ds1 != ds2)
385   {
386     VectorOfVectors &workVds2 = *DSG.properties(DSG.descriptor(ds2)).workVectors;
387     if(relationType == Lagrangian)
388     {
389       LagrangianDS& lds = *std::static_pointer_cast<LagrangianDS> (ds2);
390       inter_work_block[LsodarOSI::xfree]->insertPtr(workVds2[LsodarOSI::FREE]);
391       DSlink[LagrangianR::p2]->insertPtr(lds.p(2));
392       DSlink[LagrangianR::q2]->insertPtr(lds.acceleration());
393     }
394     // else if (relationType == NewtonEuler)
395     // {
396     //   inter_work_block[NewtonEulerR::xfree]->insertPtr(workVds2[LsodarOSI::FREE]);
397     // }
398   }
399 }
400 
initialize()401 void LsodarOSI::initialize()
402 {
403   DEBUG_BEGIN("LsodarOSI::initialize()\n");
404   OneStepIntegrator::initialize();
405   //std::string type;
406   // initialize xWork with x values of the dynamical systems present in the set.
407 
408   // DynamicalSystemsGraph::VIterator dsi, dsend;
409   // for(std::tie(dsi, dsend) = _dynamicalSystemsGraph->vertices(); dsi != dsend; ++dsi)
410   //   {
411   //     if(!checkOSI(dsi)) continue;
412   //     SP::DynamicalSystem ds = _dynamicalSystemsGraph->bundle(*dsi);
413   //     initializeWorkVectorsForDS(m, m.t0(),ds);
414   //     //ds->resetToInitialState();
415   //     //ds->swapInMemory();
416   //   }
417 
418   // SP::InteractionsGraph indexSet0 = m.nonSmoothDynamicalSystem()->topology()->indexSet0();
419   // InteractionsGraph::VIterator ui, uiend;
420   // for (std::tie(ui, uiend) = indexSet0->vertices(); ui != uiend; ++ui)
421   //   {
422   //     Interaction& inter = *indexSet0->bundle(*ui);
423   //     initializeWorkVectorsForInteraction(m.t0(), inter, indexSet0->properties(*ui), *_dynamicalSystemsGraph);
424   //   }
425 
426 
427   //   Integer parameters for LSODAROSI are saved in vector intParam.
428   //   The link with variable names in opkdmain.f is indicated in comments
429 
430   // 2 - Ng, number of constraints:
431   _intData[1] = std::static_pointer_cast<EventDriven>(_simulation)->computeSizeOfg();
432   // 3 - Itol, itask, iopt
433   _intData[2] = _itol; // itol, 1 if ATOL is a scalar, else 2 (ATOL array)
434   _intData[3] = 1; // itask, an index specifying the task to be performed. 1: normal computation.
435   _intData[5] = 0; // iopt: 0 if no optional input else 1.
436 
437   // 4 - Istate
438   _intData[4] = 1; // istate, an index used for input and output to specify the state of the calculation.
439   // On input:
440   //                 1: first call for the problem (initializations will be done).
441   //                 2: means this is not the first call, and the calculation is to continue normally, with no change in any input
442   //                    parameters except possibly TOUT and ITASK.
443   //                 3:  means this is not the first call, and the calculation is to continue normally, but with
444   //                     a change in input parameters other than TOUT and ITASK.
445   // On output:
446   //                 1: means nothing was done; TOUT = t and ISTATE = 1 on input.
447   //                 2: means the integration was performed successfully, and no roots were found.
448   //                 3: means the integration was successful, and one or more roots were found before satisfying the stop condition specified by ITASK. See JROOT.
449   //                 <0: error. See table below, in integrate function output message.
450 
451 
452 
453   // 7 - JT, Jacobian type indicator
454   _intData[8] = 2;   // jt, Jacobian type indicator.
455   //           1 means a user-supplied full (NEQ by NEQ) Jacobian.
456   //           2 means an internally generated (difference quotient) full Jacobian (using NEQ extra calls to f per df/dx value).
457   //           4 means a user-supplied banded Jacobian.
458   //           5 means an internally generated banded Jacobian (using ML+MU+1 extra calls to f per df/dx evaluation).
459 
460   // set the optional input flags of LSODAROSI to 0
461   // LSODAROSI will take the default values
462 
463 
464   // === Error handling in LSODAROSI===
465 
466   //   parameters: itol, rtol, atol.
467   //   Control vector E = (E(i)) of estimated local errors in y:
468   //   max-norm of ( E(i)/EWT(i) )< 1
469   //   EWT = (EWT(i)) vector of positive error weights.
470   //   The values of RTOL and ATOL should all be non-negative.
471   //
472   //  ITOL    RTOL       ATOL          EWT(i)
473   //   1     scalar     scalar     RTOL*ABS(Y(i)) + ATOL
474   //   2     scalar     array      RTOL*ABS(Y(i)) + ATOL(i)
475   //   3     array      scalar     RTOL(i)*ABS(Y(i)) + ATOL
476   //   4     array      array      RTOL(i)*ABS(Y(i)) + ATOL(i)
477   DEBUG_END("LsodarOSI::initialize()\n");
478 }
479 
integrate(double & tinit,double & tend,double & tout,int & istate)480 void LsodarOSI::integrate(double& tinit, double& tend, double& tout, int& istate)
481 {
482 
483   DEBUG_BEGIN("LsodarOSI::integrate(double& tinit, double& tend, double& tout, int& istate) with \n");
484   DEBUG_PRINTF("tinit = %f, tend= %f, tout = %f, istate = %i\n", tinit, tend,  tout, istate);
485 
486   // For details on DLSODAR parameters, see opkdmain.f in externals/odepack
487   doublereal tend_DR = tend  ;       // next point where output is desired (different from t!)
488   doublereal tinit_DR = tinit;       // current (starting) time
489 
490   // === Pointers to function ===
491   //  --> definition and initialisation thanks to wrapper:
492   global_object = std::static_pointer_cast<LsodarOSI>(shared_from_this()); // Warning: global object must be initialized to current one before pointers to function initialisation.
493 
494 #ifdef HAS_FORTRAN
495   // function to compute the righ-hand side of xdot = f(x,t) + Tu
496   fpointer pointerToF = LsodarOSI_f_wrapper;
497 
498   // function to compute the Jacobian/x of the rhs.
499   jacopointer pointerToJacobianF = LsodarOSI_jacobianf_wrapper; // function to compute the Jacobian/x of the rhs.
500 
501   // function to compute the constraints
502   gpointer pointerToG;
503   pointerToG = LsodarOSI_g_wrapper; // function to compute the constraints
504 #endif
505 
506   // === LSODAR CALL ===
507   DEBUG_EXPR(_xWork->display(););
508   *_xtmp = *_xWork;
509   if(istate == 3)
510   {
511     istate = 1; // restart TEMPORARY
512   }
513 
514   _intData[4] = istate;
515 
516 #ifdef HAS_FORTRAN
517   // call LSODAR to integrate dynamical equation
518   CNAME(dlsodar)(pointerToF,
519                  &(_intData[0]),
520                  _xtmp->getArray(),
521                  &tinit_DR,
522                  &tend_DR,
523                  &(_intData[2]),
524                  rtol.get(),
525                  atol.get(),
526                  &(_intData[3]),
527                  &(_intData[4]),
528                  &(_intData[5]),
529                  rwork.get(),
530                  &(_intData[6]),
531                  iwork.get(),
532                  &(_intData[7]),
533                  pointerToJacobianF,
534                  &(_intData[8]),
535                  pointerToG, &
536                  (_intData[1]),
537                  jroot.get());
538 #else
539   THROW_EXCEPTION("LsodarOSI, Fortran Language is not enabled in siconos kernel. Compile with fortran if you need Lsodar");
540 #endif
541   // jroot: jroot[i] = 1 if g(i) has a root at t, else jroot[i] = 0.
542 
543   // === Post ===
544   if(_intData[4] < 0)  // if istate < 0 => LSODAROSI failed
545   {
546     std::cout << "LSodar::integrate(...) failed - Istate = " << _intData[4] <<std::endl;
547     std::cout << " -1 means excess work done on this call (perhaps wrong JT, or so small tolerance (ATOL and RTOL), or small maximum number of steps for one call (MXSTEP)). You should increase ATOL or RTOL or increase the MXSTEP" <<std::endl;
548     std::cout << " -2 means excess accuracy requested (tolerances too small)." <<std::endl;
549     std::cout << " -3 means illegal input detected (see printed message)." <<std::endl;
550     std::cout << " -4 means repeated error test failures (check all inputs)." <<std::endl;
551     std::cout << " -5 means repeated convergence failures (perhaps bad Jacobian supplied or wrong choice of JT or tolerances)." <<std::endl;
552     std::cout << " -6 means error weight became zero during problem. (Solution component i vanished, and ATOL or ATOL(i) = 0.)" <<std::endl;
553     std::cout << " -7 means work space insufficient to finish (see messages)." <<std::endl;
554     THROW_EXCEPTION("LsodarOSI, integration failed");
555   }
556 
557   *_xWork = *_xtmp;
558   istate = _intData[4];
559   tout  = tinit_DR; // real ouput time
560   tend  = tend_DR; // necessary for next start of DLSODAR
561   DEBUG_PRINTF("tout = %g, tinit = %g, tend = %g ", tout, tinit, tend);
562   DEBUG_EXPR(_xtmp->display(););
563   DEBUG_EXPR(_xWork->display(););
564   if(istate == 3)
565   {
566     //      std:: std::cout << "ok\n";
567     assert(true);
568   }
569   // Update counters
570   count_NST = iwork[10];
571   count_NFE = iwork[11];
572   //  tinit = tinit_DR;
573   DEBUG_END("LsodarOSI::integrate(double& tinit, double& tend, double& tout, int& istate)\n");
574 }
575 
576 
updateState(const unsigned int level)577 void LsodarOSI::updateState(const unsigned int level)
578 {
579   // Compute all required (ie time-dependent) data for the DS of the OSI.
580   DynamicalSystemsGraph::VIterator dsi, dsend;
581   if(level == 1)  // ie impact case: compute velocity
582   {
583     for(std::tie(dsi, dsend) = _dynamicalSystemsGraph->vertices(); dsi != dsend; ++dsi)
584     {
585       if(!checkOSI(dsi)) continue;
586       SP::LagrangianDS lds = std::static_pointer_cast<LagrangianDS>(_dynamicalSystemsGraph->bundle(*dsi));
587       lds->computePostImpactVelocity();
588     }
589   }
590   else if(level == 2)
591   {
592     double time = _simulation->nextTime();
593     for(std::tie(dsi, dsend) = _dynamicalSystemsGraph->vertices(); dsi != dsend; ++dsi)
594     {
595       if(!checkOSI(dsi)) continue;
596       {
597         SP::DynamicalSystem ds = _dynamicalSystemsGraph->bundle(*dsi);
598         ds->update(time);
599       }
600     }
601   }
602   else THROW_EXCEPTION("LsodarOSI::updateState(index), index is out of range. Index = " + std::to_string(level));
603 }
604 
605 struct LsodarOSI::_NSLEffectOnFreeOutput : public SiconosVisitor
606 {
607   using SiconosVisitor::visit;
608 
609   OneStepNSProblem * _osnsp;
610   SP::Interaction _inter;
611   InteractionProperties& _interProp;
_NSLEffectOnFreeOutputLsodarOSI::_NSLEffectOnFreeOutput612   _NSLEffectOnFreeOutput(OneStepNSProblem *p, SP::Interaction inter, InteractionProperties& interProp) :
613     _osnsp(p), _inter(inter), _interProp(interProp) {};
614 
visitLsodarOSI::_NSLEffectOnFreeOutput615   void visit(const NewtonImpactNSL& nslaw)
616   {
617     double e;
618     e = nslaw.e();
619     Index subCoord(4);
620     subCoord[0] = 0;
621     subCoord[1] = _inter->nonSmoothLaw()->size();
622     subCoord[2] = 0;
623     subCoord[3] = subCoord[1];
624     SiconosVector & osnsp_rhs = *(*_interProp.workVectors)[LsodarOSI::OSNSP_RHS];
625     subscal(e, *_inter->yOld(_osnsp->inputOutputLevel()), osnsp_rhs, subCoord, false); // q = q + e * q
626   }
627 
628   // visit function added by Son (9/11/2010)
visitLsodarOSI::_NSLEffectOnFreeOutput629   void visit(const MultipleImpactNSL& nslaw)
630   {
631     ;
632   }
633   // note : no NewtonImpactFrictionNSL
634 };
635 
636 
computeFreeOutput(InteractionsGraph::VDescriptor & vertex_inter,OneStepNSProblem * osnsp)637 void LsodarOSI::computeFreeOutput(InteractionsGraph::VDescriptor& vertex_inter, OneStepNSProblem* osnsp)
638 {
639   SP::OneStepNSProblems  allOSNS  = _simulation->oneStepNSProblems();
640   SP::InteractionsGraph indexSet = osnsp->simulation()->indexSet(osnsp->indexSetLevel());
641   SP::Interaction inter = indexSet->bundle(vertex_inter);
642   VectorOfBlockVectors& DSlink = inter->linkToDSVariables();
643   VectorOfBlockVectors& inter_work_block = *indexSet->properties(vertex_inter).workBlockVectors;
644 
645 // Get relation and non smooth law types
646   RELATION::TYPES relationType = inter->relation()->getType();
647   RELATION::SUBTYPES relationSubType = inter->relation()->getSubType();
648   unsigned int sizeY = inter->nonSmoothLaw()->size();
649 
650   unsigned int relativePosition = 0;
651   SP::Interaction mainInteraction = inter;
652   Index coord(8);
653   coord[0] = relativePosition;
654   coord[1] = relativePosition + sizeY;
655   coord[2] = 0;
656   coord[4] = 0;
657   coord[6] = 0;
658   coord[7] = sizeY;
659   SP::SiconosMatrix  C;
660   //   SP::SiconosMatrix  D;
661   //   SP::SiconosMatrix  F;
662   SiconosVector& osnsp_rhs = *(*indexSet->properties(vertex_inter).workVectors)[LsodarOSI::OSNSP_RHS];
663 
664   SP::BlockVector Xfree;
665 
666 
667   // All of these values should be stored in the node corrseponding to the Interactionwhen a LsodarOSI scheme is used.
668 
669   /* V.A. 10/10/2010
670    * Following the type of OSNS  we need to retrieve the velocity or the acceleration
671    * This tricks is not very nice but for the moment the OSNS do not known if
672    * it is in accelaration of not
673    */
674 
675   //SP::OneStepNSProblems  allOSNS  = _simulation->oneStepNSProblems();
676   if(((*allOSNS)[SICONOS_OSNSP_ED_SMOOTH_ACC]).get() == osnsp)
677   {
678     if(relationType == Lagrangian)
679     {
680       Xfree = inter_work_block[LsodarOSI::xfree];
681       DEBUG_EXPR(Xfree->display(););
682     }
683     // else if  (relationType == NewtonEuler)
684     // {
685     //   Xfree = inter->data(::FREE);
686     // }
687     assert(Xfree);
688     //        std::cout << "Computeqblock Xfree (Gamma)========" << std::endl;
689     //       Xfree->display();
690   }
691   else  if(((*allOSNS)[SICONOS_OSNSP_ED_IMPACT]).get() == osnsp)
692   {
693     Xfree = DSlink[LagrangianR::q1];
694     //        std::cout << "Computeqblock Xfree (Velocity)========" << std::endl;
695     //       Xfree->display();
696 
697   }
698   else
699     THROW_EXCEPTION(" computeqBlock for Event Event-driven is wrong ");
700 
701   if(relationType == Lagrangian)
702   {
703     C = mainInteraction->relation()->C();
704     if(C)
705     {
706       assert(Xfree);
707 
708       coord[3] = C->size(1);
709       coord[5] = C->size(1);
710 
711       subprod(*C, *Xfree, osnsp_rhs, coord, true);
712     }
713 
714     SP::SiconosMatrix ID(new SimpleMatrix(sizeY, sizeY));
715     ID->eye();
716 
717     Index xcoord(8);
718     xcoord[0] = 0;
719     xcoord[1] = sizeY;
720     xcoord[2] = 0;
721     xcoord[3] = sizeY;
722     xcoord[4] = 0;
723     xcoord[5] = sizeY;
724     xcoord[6] = 0;
725     xcoord[7] = sizeY;
726     // For the relation of type LagrangianRheonomousR
727     if(relationSubType == RheonomousR)
728     {
729       if(((*allOSNS)[SICONOS_OSNSP_ED_SMOOTH_ACC]).get() == osnsp)
730       {
731         THROW_EXCEPTION("LsodarOSI::computeFreeOutput not yet implemented for LCP at acceleration level with LagrangianRheonomousR");
732       }
733       else if(((*allOSNS)[SICONOS_OSNSP_TS_VELOCITY]).get() == osnsp)
734       {
735         std::static_pointer_cast<LagrangianRheonomousR>(inter->relation())->computehDot(simulation()->getTkp1(), *DSlink[LagrangianR::q0], *DSlink[LagrangianR::z]);
736         subprod(*ID, *(std::static_pointer_cast<LagrangianRheonomousR>(inter->relation())->hDot()), osnsp_rhs, xcoord, false); // y += hDot
737       }
738       else
739         THROW_EXCEPTION("LsodarOSI::computeFreeOutput not implemented for SICONOS_OSNSP ");
740     }
741     // For the relation of type LagrangianScleronomousR
742     if(relationSubType == ScleronomousR)
743     {
744       if(((*allOSNS)[SICONOS_OSNSP_ED_SMOOTH_ACC]).get() == osnsp)
745       {
746         std::static_pointer_cast<LagrangianScleronomousR>(inter->relation())->computedotjacqhXqdot(simulation()->getTkp1(), *inter, DSlink);
747         subprod(*ID, *(std::static_pointer_cast<LagrangianScleronomousR>(inter->relation())->dotjacqhXqdot()), osnsp_rhs, xcoord, false); // y += NonLinearPart
748       }
749     }
750   }
751   else
752     THROW_EXCEPTION("LsodarOSI::computeFreeOutput not yet implemented for Relation of type " + std::to_string(relationType));
753   if(((*allOSNS)[SICONOS_OSNSP_ED_IMPACT]).get() == osnsp)
754   {
755     if(inter->relation()->getType() == Lagrangian || inter->relation()->getType() == NewtonEuler)
756     {
757       SP::SiconosVisitor nslEffectOnFreeOutput(new _NSLEffectOnFreeOutput(osnsp, inter, indexSet->properties(vertex_inter)));
758       inter->nonSmoothLaw()->accept(*nslEffectOnFreeOutput);
759     }
760   }
761 
762 }
display()763 void LsodarOSI::display()
764 {
765   OneStepIntegrator::display();
766   std::cout << " --- > LsodarOSI specific values: " <<std::endl;
767   std::cout << "Number of equations: " << _intData[0] <<std::endl;
768   std::cout << "Number of constraints: " << _intData[1] <<std::endl;
769   std::cout << "itol, itask, istate, iopt, lrw, liw, jt: (for details on what are these variables see opkdmain.f)" <<std::endl;
770   std::cout << _intData[2] << ", " << _intData[3] << ", " << _intData[4] << ", " << _intData[5] << ", " << _intData[6]  << ", " << _intData[7]  << ", " << _intData[8] <<std::endl;
771   std::cout << "====================================" <<std::endl;
772 }
773