1 /* Siconos is a program dedicated to modeling, simulation and control
2  * of non smooth dynamical systems.
3  *
4  * Copyright 2021 INRIA.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17 */
18 /*! \file Simulation.hpp
19   \brief Global interface for simulation process description.
20 */
21 
22 #ifndef SIMULATION_H
23 #define SIMULATION_H
24 
25 #include "SiconosConst.hpp"
26 #include "SimulationTypeDef.hpp"
27 #include "SiconosFwd.hpp"
28 #include <fstream>
29 #include "NonSmoothDynamicalSystem.hpp"
30 #include "InteractionManager.hpp"
31 
32 /** Description of the simulation process (integrators, time
33     discretisation and so on).
34 
35     !!! This is an abstract class !!!
36 
37     The available simulations are TimeStepping, EventDriven and TimeSteppingD1Minus.
38 */
39 
40 class Simulation : public std::enable_shared_from_this<Simulation>
41 {
42 protected:
43   /** serialization hooks
44   */
45   ACCEPT_SERIALIZATION(Simulation);
46 
47   /** name or id of the Simulation */
48   std::string _name;
49 
50   /** tool to manage all events */
51   SP::EventsManager _eventsManager;
52 
53   /** current starting time for integration */
54   double _tinit;
55 
56   /** current ending time for integration */
57   double _tend;
58 
59   /** real ending time for integration (different from tend in case of
60       stop during integrate, for example when a root is found in
61       an EventDriven strategy)
62   */
63   double _tout;
64 
65   double _T;
66 
67   /** the dynamical systems integrators */
68   SP::OSISet _allOSI;
69 
70   /** the non smooth problems (each problem is identified thanks to
71       its id) */
72   SP::OneStepNSProblems _allNSProblems;
73 
74   /** A pointer to the simulated nonsmooth dynamical system
75    */
76   SP::NonSmoothDynamicalSystem _nsds;
77 
78   /** An interaction manager
79    */
80   SP::InteractionManager _interman;
81 
82   /** _numberOfIndexSets is the number of index sets that we need for
83    * simulation. It corresponds for most of the simulations to levelMaxForOutput + 1.
84    * Nevertheless, some simulations need more sets of indices that the number
85    * of outputs that we considered.
86    */
87   unsigned int _numberOfIndexSets;
88 
89   /** tolerance value used to compute the index sets.
90       Default: equal to 10 x machine double precision (std::numeric_limits<double>::epsilon)
91   */
92   double _tolerance;
93 
94   /** Output setup: if true, display solver stats */
95   bool _printStat;
96 
97   /** _staticLevels : do not recompute levels once they have been
98    * initialized */
99   bool _staticLevels;
100 
101   /** File id for stats outputs.*/
102   std::ofstream statOut;
103 
104   /**
105    * bool, option specifying if a critere of relative convergence is
106    * used. Default value is false.
107    */
108   bool _useRelativeConvergenceCriterion;
109   /**
110    * bool used to remind if the relative convergence held(useful for
111    * the newton-check-convergence). Modified only if
112    * _useRelativeConvergenceCriterion is true.
113    */
114   bool _relativeConvergenceCriterionHeld;
115   /**
116    *double, relative tolerance. Used only if
117    *_useRelativeConvergenceCriterion is true.
118    */
119   double _relativeConvergenceTol;
120 
121   bool _isInitialized;
122 
123   /** current NSDS changelog position */
124   NonSmoothDynamicalSystem::ChangeLogIter _nsdsChangeLogPosition;
125 
126   /** map of not-yet-initialized DS variables for each OSI */
127   std::map< SP::OneStepIntegrator, std::list<SP::DynamicalSystem> >  _OSIDSmap;
128 
129   /** Call the interaction manager one if is registered, otherwise do nothing. */
130   void updateInteractions();
131 
132   /*TS set the ds->q memory, the world (CAD model for example) must be updated.
133     Overload this method to update user model.*/
updateWorldFromDS()134   virtual void updateWorldFromDS()
135   {
136     ;
137   };
138 
139   /** initialize OSI-DS links in the NSDS graph. */
140   void initializeOSIAssociations();
141 
142   /** initialize objects (DSs and Interations) found in the NSDS
143    * Changelog and update the changelog iterator.
144    */
145   void initializeNSDSChangelog();
146 
147   /** initialize index sets for OSIs */
148   void initializeIndexSets();
149 
150 private:
151 
152   /** copy constructor. Private => no copy nor pass-by value.
153    */
154   Simulation(const Simulation&);
155 
156   /* assignment of simulation. Private => no copy nor pass-by value.
157    */
158   Simulation& operator=(const Simulation&);
159 
160 public:
161 
162   /** default constructor, for serialization
163    */
Simulation()164   Simulation() {};
165 
166   /** default constructor
167    *  \param nsds current nonsmooth dynamical system
168    *  \param td the timeDiscretisation for this Simulation
169    */
170   Simulation(SP::NonSmoothDynamicalSystem nsds, SP::TimeDiscretisation td);
171 
172   /** constructor with only a TimeDiscretisation
173    *  \param td the timeDiscretisation for this Simulation
174    */
175   Simulation(SP::TimeDiscretisation td);
176 
177   /** destructor
178    */
179   virtual ~Simulation();
180 
181   /** clear all maps. This function should not exist, but there is a cycle
182    * with the shared_ptr: the OneStepIntegrator and OneStepNSProblem have
183    * both a link to the Simulation, and here we have all the OneStepIntegrator
184    * and OneStepNSProblem in maps. Then the memory is never freed. The clumsy
185    * way to deal with it is to call this function from the Model destructor
186    * to free the maps and then the cycle is broken
187    * \warning do not call this yourself, it is meant to be called from
188    * the desctructor of the Model
189    */
190   void clear();
191 
192   // GETTERS/SETTERS
193 
194   /** get the name of the Simulation
195    *  \return std::string : the name of the Simulation
196    */
name() const197   inline const std::string name() const
198   {
199     return _name;
200   }
201 
202   /** set the name of the Simulation
203       \param newName the new name
204    */
setName(const std::string & newName)205   inline void setName(const std::string& newName)
206   {
207     _name = newName;
208   }
209 
210   /** returns time instant k of the time discretisation  */
211   double getTk() const;
212 
213   /** get time instant k+1 of the time discretisation
214    * \warning: this instant may be different from nextTime(), if for example some
215       non-smooth events or some sensor events are present
216      \return a double. If the simulation is near the end (t_{k+1} > T), it returns NaN.
217    */
218   double getTkp1() const;
219 
220   /** get time instant k+2 of the time discretisation
221    * \warning: this instant may be different from nextTime(), if for example some
222       non-smooth events or some sensor events are present
223    * \return a double. If the simulation is near the end (t_{k+2} > T), it returns NaN.
224    */
225   double getTkp2() const;
226 
227   /** returns current timestep  */
228   double currentTimeStep() const;
229 
230   /** returns a pointer to the EventsManager
231    */
eventsManager() const232   inline SP::EventsManager eventsManager() const
233   {
234     return _eventsManager;
235   };
236 
237   /** get "current time" (ie starting point for current integration,
238       time of currentEvent of eventsManager.)
239       \return a double.
240   */
241   double startingTime() const;
242 
243   /** get "next time" (ie ending point for current integration, time
244       of nextEvent of eventsManager.)
245       \return a double.
246   */
247   double nextTime() const;
248 
249   /** get the current time step size ("next time"-"current time")
250    *  \return a double.
251    */
timeStep() const252   inline double timeStep() const
253   {
254     return (nextTime() - startingTime());
255   };
256 
257   /** true if a future event is to be treated or not (ie if some
258       events remain in the eventsManager).
259   */
260   bool hasNextEvent() const;
261 
262   /** get all the Integrators of the Simulation
263    *  \return an OSISset
264    */
oneStepIntegrators() const265   inline const SP::OSISet oneStepIntegrators() const
266   {
267     return _allOSI;
268   };
269 
270   /** get the number of OSIs in the Simulation (ie the size of allOSI)
271    *  \return an unsigned int
272    */
numberOfOSI() const273   inline size_t numberOfOSI() const
274   {
275     return _allOSI->size();
276   }
277 
278   /** insert an Integrator into the simulation list of integrators
279    *  \param osi the OneStepIntegrator to add
280    */
281   virtual void insertIntegrator(SP::OneStepIntegrator osi);
282 
283   /** associate an OSI with a DS */
284   void associate(SP::OneStepIntegrator osi, SP::DynamicalSystem ds);
285 
286   /** get a pointer to indexSets[i]
287       \param i number of the required index set
288       \return a graph of interactions
289    */
290   SP::InteractionsGraph indexSet(unsigned int i);
291 
292   /** get allNSProblems
293    *  \return a pointer to OneStepNSProblems object (container of
294    *  SP::OneStepNSProblem)
295    */
oneStepNSProblems() const296   inline const SP::OneStepNSProblems oneStepNSProblems() const
297   {
298     return _allNSProblems;
299   };
300 
301   /** get the number of OSNSP in the Simulation (ie the size of allNSProblems)
302    *  \return an unsigned int
303    */
numberOfOSNSProblems() const304   inline size_t numberOfOSNSProblems() const
305   {
306     return _allNSProblems->size();
307   }
308 
309   /** get a OneStep nonsmooth problem of the simulation, identify with its number.
310       \param id number of the required osnspb
311       \return a pointer to OneStepNSProblem
312    */
313   SP::OneStepNSProblem oneStepNSProblem(int id);
314 
315   /** add a OneStepNSProblem in the Simulation
316       \param osns the OneStepNSProblem to insert
317       \param Id its id: default is SICONOS_OSNSP_DEFAULT,
318       at impact level SICONOS_OSNSP_ED_IMPACT, at acceleration level
319       SICONOS_OSNSP_ED_ACCELERATION
320    */
321   virtual void insertNonSmoothProblem(SP::OneStepNSProblem osns, int Id = SICONOS_OSNSP_DEFAULT);
322 
323 
324   /** get the NonSmoothDynamicalSystem
325    *  \return NonSmoothDynamicalSystem
326    */
nonSmoothDynamicalSystem() const327   inline SP::NonSmoothDynamicalSystem nonSmoothDynamicalSystem() const
328   {
329     return _nsds;
330   }
331   /** set the NonSmoothDynamicalSystem of the Simulation
332    *  \param newPtr a pointer on NonSmoothDynamicalSystem
333    */
setNonSmoothDynamicalSystemPtr(SP::NonSmoothDynamicalSystem newPtr)334   void setNonSmoothDynamicalSystemPtr(SP::NonSmoothDynamicalSystem newPtr)
335   {
336     _nsdsChangeLogPosition = _nsds->changeLogBegin();
337     _nsds = newPtr;
338   }
339 
340   /** get tolerance
341    *  \return a double
342    */
tolerance() const343   double tolerance() const
344   {
345     return _tolerance;
346   };
347 
348   /** set the value of offset for q dof vector in dynamical systems
349       (to avoid events accumulation)
350       \param inputVal new tolerance
351    */
setTolerance(double inputVal)352   void setTolerance(double inputVal)
353   {
354     _tolerance = inputVal;
355   };
356 
357   /** set printStat value: if true, print solver stats.
358       \param newVal true to activate stats
359    */
setPrintStat(const bool & newVal)360   inline void setPrintStat(const bool& newVal)
361   {
362     _printStat = newVal;
363   };
364 
365   /** get printStat value
366       \return true if stats are activated
367    */
getPrintStat() const368   inline bool getPrintStat() const
369   {
370     return _printStat;
371   };
372 
373   /** update all index sets of the topology, using current y and
374       lambda values of Interactions.
375    */
376   void updateIndexSets();
377 
378   /** update indexSets[i] of the topology, using current y and lambda
379       values of Interactions.
380       \param level the number of the set to be updated
381    */
382   virtual void updateIndexSet(unsigned int level) = 0;
383 
384   /** Complete initialisation of the Simulation (OneStepIntegrators,
385       OneStepNSProblem, TImediscretisation).
386   */
387   virtual void initialize();
388 
389   /** Initialize a single Interaction for this Simulation, used for dynamic
390    *  topology updates. */
391   virtual void initializeInteraction(double time, SP::Interaction inter);
392 
393   /** Set an object to automatically manage interactions during the simulation
394    * \param manager
395    */
insertInteractionManager(SP::InteractionManager manager)396   void insertInteractionManager(SP::InteractionManager manager)
397     { _interman = manager; }
398 
399   /** computes a one step NS problem
400    *  \param nb the id of the OneStepNSProblem to be computed
401    *  \return information about the solver convergence.
402    */
403   int computeOneStepNSProblem(int nb);
404 
405   /** update input
406    *  \param level lambda order used to compute input
407    * level is set to 0 by default since in all time-stepping schemes we update all the state
408    */
409   virtual void updateInput(unsigned int level=0);
410 
411   /** update state of each dynamical system
412    */
413   virtual void updateState(unsigned int level=0);
414 
415   /** update output
416    *  \param level lambda order used to compute output
417    * level is set to 0 by default since in all time-stepping schemes we update all the state
418    */
419   virtual void updateOutput(unsigned int level=0);
420 
421   /** update output, state, and input
422    *  \param level lambda order used to compute input
423    * level is set to 0 by default since in all time-stepping schemes we update all the state
424    */
update(unsigned int level=0)425   void update(unsigned int level=0)
426     { updateInput(level); updateState(level); updateOutput(level); }
427 
428   /** run the simulation, from t0 to T
429    * with default parameters if any particular settings has been done
430    */
431   virtual void run();
432 
433   /** initialisation for OneStepNSProblem.
434    */
435   virtual void initOSNS() = 0;
436 
437 
438   /** step from current event to next event of EventsManager
439    */
440   virtual void advanceToEvent() = 0;
441 
442 
443   /** clear the NSDS changelog up to current position.  If you have a
444    * particularly dynamic simulation (DS and Interactions created and
445    * destroyed frequently), then it is important to call this
446    * periodically.
447    */
448   void clearNSDSChangeLog();
449 
450 
451   /* Set the option to specify if a relative convergence criterion must
452    be used to stop the Newton iterations.
453    \param use true if relative critarion activated
454   */
setUseRelativeConvergenceCriteron(bool use)455   inline void setUseRelativeConvergenceCriteron(bool use)
456   {
457     _useRelativeConvergenceCriterion = use;
458   };
459   /**
460      \return true if the relative convergence criterion is activated.
461    */
useRelativeConvergenceCriteron()462   inline bool useRelativeConvergenceCriteron()
463   {
464     return _useRelativeConvergenceCriterion;
465   };
466 
467   /** Set the relative convergence tolerance
468       \param v tolerance value
469    */
setRelativeConvergenceTol(double v)470   inline void setRelativeConvergenceTol(double v)
471   {
472     _relativeConvergenceTol = v;
473   };
474 
475   /**
476    \return the relative convergence tolerence.
477    *
478    */
relativeConvergenceTol()479   inline double relativeConvergenceTol()
480   {
481     return _relativeConvergenceTol;
482   };
483 
484   /**
485    \param newVal a new relative convergence criterion
486    *
487    */
setRelativeConvergenceCriterionHeld(bool newVal)488   inline void setRelativeConvergenceCriterionHeld(bool newVal)
489   {
490     _relativeConvergenceCriterionHeld = newVal;
491   };
492   /**
493      \return true if the relative convergence criterion held.
494    *
495    */
relativeConvergenceCriterionHeld()496   inline bool relativeConvergenceCriterionHeld()
497   {
498     return _relativeConvergenceCriterionHeld;
499   };
500 
501   /** return input lambda[level](coor) for all the interactions
502       \param level lambda min order to be computed
503       \param coor the coordinate of interest
504       \return a SP::SiconosVector that contains the concatenated value
505    */
506   SP::SiconosVector lambda(unsigned int level = 0, unsigned int coor=0 );
507 
508   /** return output y[level](coor) for all the interactions
509       \param level y min order to be computed
510       \param coor the coordinate of interest
511       \return a SP::SiconosVector that contains the concatenated value
512    */
513   SP::SiconosVector y(unsigned int level = 0, unsigned int coor=0 );
514 
515   /** call eventsManager processEvents.
516    */
517   void processEvents();
518 
519 
520   /** set staticLevels
521    * \param b decides whether levels should be computed at each iteration
522    */
setStaticLevels(bool b)523   void setStaticLevels(bool b)
524   {
525     _staticLevels = b;
526   }
527 
528   /** This updates the end of the Simulation.
529    * \warning this should be called only from the Model, to synchronise the 2 values
530    * \param T the new final time
531    */
532   void updateT(double T);
533 
computeResiduY()534   virtual bool computeResiduY() { return false; };
535 
computeResiduR()536   virtual bool computeResiduR() { return false; };
537 
538   /** Add a new Interaction between one or a pair of DSs.
539    * \param inter the SP::Interaction to add
540    * \param ds1 the first SP::DynamicalSystem in the Interaction
541    * \param ds2 the second SP::DynamicalSystem in the Interaction, if any
542    */
543   void link(SP::Interaction inter,
544             SP::DynamicalSystem ds1,
545             SP::DynamicalSystem ds2 = SP::DynamicalSystem());
546 
547   /** Remove an Interaction from the simulation.
548    * \param inter the SP::Interaction to remove
549    */
550   void unlink(SP::Interaction inter);
551 
552   /** visitors hook
553    */
554   VIRTUAL_ACCEPT_VISITORS(Simulation);
555 };
556 
557 #endif // SIMULATION_H
558