Parcourir la source

New formalism: QSS

Eric Ramat il y a 4 ans
Parent
commit
8c20ed803c

+ 2 - 1
.gitignore

@@ -1,2 +1,3 @@
 *~
-build/
+build/
+cmake-build-debug/

+ 1 - 1
src/artis-star/common/ExternalEvent.hpp

@@ -72,7 +72,7 @@ namespace artis {
 
             const Value& data() const { return _data; }
 
-            unsigned int get_port_index() const { return _port_index; }
+            unsigned int port_index() const { return _port_index; }
 
             void data(const Value& data) { _data = data; }
 

+ 8 - 1
src/artis-star/common/Model.hpp

@@ -125,6 +125,8 @@ namespace artis {
                 return _in_port_map.find(port_index)->second;
             }
 
+            size_t get_in_port_number() const { return _in_port_map.size(); }
+
             std::string get_out_port_name(unsigned int port_index) const
             {
                 assert(exist_out_port(port_index));
@@ -132,6 +134,8 @@ namespace artis {
                 return _out_port_map.find(port_index)->second;
             }
 
+            size_t get_out_port_number() const { return _out_port_map.size(); }
+
             const std::string& get_name() const { return _name; }
 
             Model<Time>* get_parent() const { return _parent; }
@@ -186,7 +190,10 @@ namespace artis {
                 return std::string();
             }
 
-            std::string path() const { return (_parent != nullptr ? _parent->path() : "") + ":" + get_name(); }
+            std::string path() const
+            {
+                return (_parent != nullptr ? _parent->path() : "") + ":" + get_name();
+            }
 
             void set_parent(Model<Time>* parent) { _parent = parent; }
 

+ 1 - 1
src/artis-star/common/observer/CMakeLists.txt

@@ -3,7 +3,7 @@ INCLUDE_DIRECTORIES(
 
 LINK_DIRECTORIES()
 
-SET(COMMON_OBSERVER_HPP Observer.hpp Output.hpp View.hpp)
+SET(COMMON_OBSERVER_HPP Iterator.hpp Observer.hpp Output.hpp View.hpp)
 
 INSTALL(FILES ${COMMON_OBSERVER_HPP} DESTINATION
         ${ARTIS_INCLUDE_DIRS}/common/observer)

+ 91 - 0
src/artis-star/common/observer/Iterator.hpp

@@ -0,0 +1,91 @@
+/**
+ * @file artis-star/common/observer/View.hpp
+ * @author The ARTIS Development Team
+ * See the AUTHORS or Authors.txt file
+ */
+
+/*
+ * ARTIS - the multimodeling and simulation environment
+ * This file is a part of the ARTIS environment
+ *
+ * Copyright (C) 2013-2019 ULCO http://www.univ-littoral.fr
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef ARTIS_COMMON_OBSERVER_ITERATOR_HPP
+#define ARTIS_COMMON_OBSERVER_ITERATOR_HPP
+
+#include <artis-star/common/observer/View.hpp>
+
+namespace artis {
+    namespace observer {
+
+        template<typename Time>
+        class Iterator {
+        public:
+            Iterator(const typename View<Time>::Values& view)
+                    :_view(view), _iterator(view.begin()) { }
+
+            virtual ~Iterator() = default;
+
+            bool has_next() const { return _iterator != _view.end(); }
+
+            virtual void operator++() { _iterator++; }
+
+            virtual const std::pair<double, common::Value>& operator*() const { return *_iterator; }
+
+        private:
+            const typename View<Time>::Values& _view;
+            typename View<Time>::Values::const_iterator _iterator;
+        };
+
+        template<typename Time>
+        class DiscreteTimeIterator : public Iterator<Time> {
+        public:
+            DiscreteTimeIterator(const typename View<Time>::Values& view,
+                    const typename Time::type& start, const typename Time::type& step)
+                    :Iterator<Time>(view), _step(step), _time(start)
+            {
+                _last_value = &Iterator<Time>::operator*();
+                while (Iterator<Time>::operator*().first < start
+                        and Iterator<Time>::has_next()) {
+                    Iterator<Time>::operator++();
+                    _last_value = &Iterator<Time>::operator*();
+                }
+            }
+
+            virtual ~DiscreteTimeIterator() = default;
+
+            void operator++() override
+            {
+                _time += _step;
+                while (Iterator<Time>::operator*().first < _time and Iterator<Time>::has_next()) {
+                    Iterator<Time>::operator++();
+                    _last_value = &Iterator<Time>::operator*();
+                }
+            }
+
+            const std::pair<double, common::Value>& operator*() const override { return *_last_value; }
+
+        private:
+            typename Time::type _step;
+            typename Time::type _time;
+            const std::pair<double, common::Value>* _last_value;
+        };
+
+    }
+}
+
+#endif

+ 4 - 2
src/artis-star/common/observer/View.hpp

@@ -103,7 +103,8 @@ namespace artis {
                 return t;
             }
 
-            double get(double t, const std::string& selector_name, const std::string& variable_name) const
+            double
+            get(double t, const std::string& selector_name, const std::string& variable_name) const
             {
                 SelectorValues::const_iterator it = _values.find(selector_name);
 
@@ -129,7 +130,8 @@ namespace artis {
                 return 0;
             }
 
-            const Values& get(const std::string& selector_name, const std::string& variable_name) const
+            const Values&
+            get(const std::string& selector_name, const std::string& variable_name) const
             {
                 SelectorValues::const_iterator it = _values.find(selector_name);
 

+ 14 - 8
src/artis-star/kernel/dtss/GraphManager.hpp

@@ -84,7 +84,7 @@ namespace artis {
                 for (auto& ymsg : bag) {
                     typename common::Links<Time>::Result result_model =
                             _link_list.find(ymsg.get_model(),
-                                    ymsg.get_port_index());
+                                    ymsg.port_index());
 
                     for (typename common::Links<Time>::const_iterator it =
                             result_model.first; it != result_model.second; ++it) {
@@ -95,9 +95,11 @@ namespace artis {
                             ymessages.push_back(
                                     common::ExternalEvent<Time>(
                                             it->second, ymsg.data()));
-                            dynamic_cast < common::Coordinator<Time>* >(
-                                    common::GraphManager<Time>::_coordinator->get_parent())
-                                    ->dispatch_events(ymessages, t);
+                            if (common::GraphManager<Time>::_coordinator->get_parent()) {
+                                dynamic_cast < common::Coordinator<Time>* >(
+                                        common::GraphManager<Time>::_coordinator->get_parent())
+                                        ->dispatch_events(ymessages, t);
+                            }
                         } else { // event on input port of internal model
                             it->second.get_model()->post_event(
                                     t, common::ExternalEvent<Time>(
@@ -110,10 +112,14 @@ namespace artis {
             void post_event(typename Time::type t, const common::ExternalEvent<Time>& event)
             {
                 typename common::Links<Time>::Result result =
-                        _link_list.find(common::GraphManager<Time>::_coordinator, event.get_port_index());
-
-                for (typename common::Links<Time>::const_iterator it_r = result.first; it_r != result.second; ++it_r) {
-                    it_r->second.get_model()->post_event(t, common::ExternalEvent<Time>(it_r->second, event.data()));
+                        _link_list.find(common::GraphManager<Time>::_coordinator,
+                                event.port_index());
+
+                for (typename common::Links<Time>::const_iterator it_r = result.first;
+                     it_r != result.second;
+                     ++it_r) {
+                    it_r->second.get_model()->post_event(t,
+                            common::ExternalEvent<Time>(it_r->second, event.data()));
                 }
             }
 

+ 1 - 0
src/artis-star/kernel/pdevs/CMakeLists.txt

@@ -13,3 +13,4 @@ INSTALL(FILES ${PDEVS_HPP} DESTINATION ${ARTIS_INCLUDE_DIRS}/kernel/pdevs)
 
 ADD_SUBDIRECTORY(multithreading)
 ADD_SUBDIRECTORY(mpi)
+ADD_SUBDIRECTORY(qss)

+ 4 - 0
src/artis-star/kernel/pdevs/Dynamics.hpp

@@ -80,6 +80,8 @@ namespace artis {
                 _simulator->add_in_port(p);
             }
 
+            size_t input_port_number() const { return _simulator->get_in_port_number(); }
+
             void input_ports(std::initializer_list<common::Port> list)
             {
                 for (typename std::initializer_list<common::Port>::iterator it = list.begin(); it != list.end(); ++it) {
@@ -111,6 +113,8 @@ namespace artis {
                 _simulator->add_out_port(p);
             }
 
+            size_t output_port_number() const { return _simulator->get_out_port_number(); }
+
             void output_ports(std::initializer_list<common::Port> list)
             {
                 for (typename std::initializer_list<common::Port>::iterator it =

+ 6 - 4
src/artis-star/kernel/pdevs/GraphManager.hpp

@@ -87,7 +87,7 @@ namespace artis {
             {
                 for (auto& ymsg : bag) {
                     typename common::Links<Time>::Result result_model = _link_list.find(ymsg.get_model(),
-                            ymsg.get_port_index());
+                            ymsg.port_index());
 
                     for (typename common::Links<Time>::const_iterator it = result_model.first;
                          it != result_model.second; ++it) {
@@ -108,8 +108,10 @@ namespace artis {
 
                 ymessages.push_back(common::ExternalEvent<Time>(node, content));
 
-                dynamic_cast < common::Coordinator<Time>* >(common::GraphManager<Time>::_coordinator->get_parent())
-                        ->dispatch_events(ymessages, t);
+                if (common::GraphManager<Time>::_coordinator->get_parent()) {
+                    dynamic_cast < common::Coordinator<Time>* >(common::GraphManager<Time>::_coordinator->get_parent())
+                            ->dispatch_events(ymessages, t);
+                }
             }
 
             bool exist_link(common::Model<Time>* src_model, unsigned int src_port_index,
@@ -121,7 +123,7 @@ namespace artis {
             void post_event(typename Time::type t, const common::ExternalEvent<Time>& event)
             {
                 typename common::Links<Time>::Result result =
-                        _link_list.find(common::GraphManager<Time>::_coordinator, event.get_port_index());
+                        _link_list.find(common::GraphManager<Time>::_coordinator, event.port_index());
 
                 for (typename common::Links<Time>::const_iterator it_r = result.first; it_r != result.second; ++it_r) {
                     it_r->second.get_model()->post_event(t, common::ExternalEvent<Time>(it_r->second, event.data()));

+ 11 - 0
src/artis-star/kernel/pdevs/qss/CMakeLists.txt

@@ -0,0 +1,11 @@
+INCLUDE_DIRECTORIES(
+        ${ARTIS_BINARY_DIR}/src
+        ${ARTIS_SOURCE_DIR}/src
+        ${Boost_INCLUDE_DIRS})
+
+LINK_DIRECTORIES(
+        ${Boost_LIBRARY_DIRS})
+
+SET(PDEVS_QSS_HPP Data.hpp Derivative.hpp GraphManager.hpp Integrator.hpp Quantifier.hpp)
+
+INSTALL(FILES ${PDEVS_QSS_HPP} DESTINATION ${ARTIS_INCLUDE_DIRS}/kernel/pdevs/qss)

+ 52 - 0
src/artis-star/kernel/pdevs/qss/Data.hpp

@@ -0,0 +1,52 @@
+/**
+ * @file kernel/pdevs/qss/Data.hpp
+ * @author The ARTIS Development Team
+ * See the AUTHORS or Authors.txt file
+ */
+
+/*
+ * ARTIS - the multimodeling and simulation environment
+ * This file is a part of the ARTIS environment
+ *
+ * Copyright (C) 2013-2019 ULCO http://www.univ-littoral.fr
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef QSS_DATA
+#define QSS_DATA
+
+namespace artis {
+    namespace pdevs {
+        namespace qss {
+
+            struct IntegratorData {
+                double value;
+            };
+
+            struct DerivativeData {
+                double x_dot;
+            };
+
+            struct QuantifierData {
+                double up;
+                double down;
+            };
+
+
+        }
+    }
+}
+
+#endif

+ 176 - 0
src/artis-star/kernel/pdevs/qss/Derivative.hpp

@@ -0,0 +1,176 @@
+/**
+ * @file kernel/pdevs/qss/Derivative.hpp
+ * @author The ARTIS Development Team
+ * See the AUTHORS or Authors.txt file
+ */
+
+/*
+ * ARTIS - the multimodeling and simulation environment
+ * This file is a part of the ARTIS environment
+ *
+ * Copyright (C) 2013-2019 ULCO http://www.univ-littoral.fr
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef QSS_DERIVATIVE
+#define QSS_DERIVATIVE
+
+#include <artis-star/kernel/pdevs/Dynamics.hpp>
+#include <artis-star/kernel/pdevs/qss/Data.hpp>
+
+namespace artis {
+    namespace pdevs {
+        namespace qss {
+
+            template<class Time, class Dyn, class Parameters = common::NoParameters>
+            class Derivative : public artis::pdevs::Dynamics<Time, Dyn, Parameters> {
+            public:
+                enum inputs {
+                    IN = 1
+                };
+
+                enum outputs {
+                    OUT = 1
+                };
+
+                Derivative(const std::string& name, const Context<Time, Dyn, Parameters>& context)
+                        :
+                        artis::pdevs::Dynamics<Time, Dyn, Parameters>(name, context)
+                {
+                    this->input_port({IN, "in"});
+                    this->output_port({OUT, "out"});
+                }
+
+                virtual ~Derivative() { }
+
+                virtual double compute() const = 0;
+
+                virtual void dconf(typename Time::type t, typename Time::type e,
+                        const common::Bag<Time>& bag)
+                {
+                    dint(t);
+                    dext(t, e, bag);
+                }
+
+                virtual void dint(typename Time::type /* time */)
+                {
+                    if (_state == RESPONSE) {
+                        _last_output = _output_value;
+                    }
+                    _state = WAIT;
+                }
+
+                virtual void dext(typename Time::type t, typename Time::type e,
+                        const common::Bag<Time>& bag)
+                {
+                    std::for_each(bag.begin(), bag.end(),
+                            [this, t, e](const common::ExternalEvent<Time>& event) {
+                                IntegratorData data;
+
+                                event.data()(data);
+                                _inputs[event.port_index()] = data.value;
+                                switch (_state) {
+                                case INIT:
+                                    if (_inputs.size() == _input_number) {
+                                        _output_value = compute();
+                                        _state = RESPONSE;
+                                    }
+                                    break;
+                                case WAIT:
+                                case RESPONSE:
+                                    double value = compute();
+
+                                    if (value != _last_output) {
+                                        _output_value = value;
+                                        _state = RESPONSE;
+                                    } else {
+                                        _state = WAIT;
+                                    }
+                                }
+                            });
+                }
+
+                virtual typename Time::type
+
+                start(typename Time::type /* time */)
+                {
+                    _input_number = this->input_port_number();
+                    if (_input_number == 0) {
+                        _output_value = compute();
+                        _state = RESPONSE;
+                        return 0;
+                    } else {
+                        _state = INIT;
+                        return Time::infinity;
+                    }
+                }
+
+                virtual typename Time::type ta(typename Time::type /* time */)
+                {
+                    switch (_state) {
+                    case INIT:
+                        return Time::infinity;
+                    case WAIT:
+                        return Time::infinity;
+                    case RESPONSE:
+                        return 0;
+                    }
+                    return Time::infinity;
+                }
+
+                virtual common::Bag<Time> lambda(typename Time::type /* time */) const
+                {
+                    common::Bag<Time> msgs;
+
+                    switch (_state) {
+                    case INIT:
+                        break;
+                    case WAIT:
+                        break;
+                    case RESPONSE:
+                        const DerivativeData data = {_output_value};
+
+                        msgs.push_back(common::ExternalEvent<Time>(OUT, data));
+                    }
+                    return msgs;
+                }
+
+                virtual common::Value observe(const typename Time::type& /* t */,
+                        unsigned int /* index */) const
+                {
+                    return common::Value();
+                }
+
+                double input(unsigned int index) const {
+                    return _inputs.at(index);
+                }
+
+            private:
+                typedef enum {
+                    INIT, WAIT, RESPONSE
+                } State;
+
+                State _state;
+                unsigned int _input_number;
+                std::map<unsigned int, double> _inputs;
+                double _output_value;
+                double _last_output;
+            };
+
+        }
+    }
+}
+
+#endif

+ 105 - 0
src/artis-star/kernel/pdevs/qss/GraphManager.hpp

@@ -0,0 +1,105 @@
+/**
+ * @file kernel/pdevs/qss/GraphManager.hpp
+ * @author The ARTIS Development Team
+ * See the AUTHORS or Authors.txt file
+ */
+
+/*
+ * ARTIS - the multimodeling and simulation environment
+ * This file is a part of the ARTIS environment
+ *
+ * Copyright (C) 2013-2019 ULCO http://www.univ-littoral.fr
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef QSS_GRAPH_MANAGER
+#define QSS_GRAPH_MANAGER
+
+#include <artis-star/kernel/pdevs/GraphManager.hpp>
+#include <artis-star/kernel/pdevs/qss/Integrator.hpp>
+#include <artis-star/kernel/pdevs/qss/Quantifier.hpp>
+
+namespace artis {
+    namespace pdevs {
+        namespace qss {
+
+            template <class DerivativeParameters>
+            struct QSSParameters {
+                IntegratorParameters integrator;
+                QuantifierParameters quantifier;
+                DerivativeParameters derivative;
+            };
+
+            template<class Time,
+                    class Derivative,
+                    class DerivativeParameters = artis::common::NoParameters>
+            class GraphManager :
+                    public artis::pdevs::GraphManager<Time, QSSParameters<DerivativeParameters>, artis::common::NoParameters> {
+            public:
+                enum submodels {
+                    S_Derivative, S_Integrator, S_Quantifier
+                };
+
+                enum outputs {
+                    OUT
+                };
+
+                GraphManager(common::Coordinator<Time>* coordinator,
+                        const QSSParameters<DerivativeParameters>& parameters,
+                        const artis::common::NoParameters& graph_parameters)
+                        :
+                        artis::pdevs::GraphManager<Time, QSSParameters<DerivativeParameters>, artis::common::NoParameters>(
+                                coordinator,
+                                parameters,
+                                graph_parameters
+                        ),
+                        _derivative("d", parameters.derivative),
+                        _integrator("i", parameters.integrator),
+                        _quantifier("q", parameters.quantifier)
+                {
+                    this->add_child(S_Derivative, &_derivative);
+                    this->add_child(S_Integrator, &_integrator);
+                    this->add_child(S_Quantifier, &_quantifier);
+
+                    coordinator->output_port({OUT, "out"});
+
+                    this->out({&_derivative, Derivative::OUT})
+                            >> this->in({&_integrator, Integrator<Time>::X_DOT});
+                    this->out({&_integrator, Integrator<Time>::OUT})
+                            >> this->in({&_quantifier, Quantifier<Time>::IN});
+                    this->out({&_quantifier, Quantifier<Time>::OUT})
+                            >> this->in({&_integrator, Integrator<Time>::QUANTA});
+                    this->out({&_integrator, Integrator<Time>::OUT})
+                            >> this->out({coordinator, OUT});
+                    this->out({&_integrator, Integrator<Time>::OUT})
+                            >> this->in({&_derivative, Derivative::IN});
+                }
+
+                ~GraphManager() override = default;
+
+                artis::pdevs::Simulator<Time, Derivative, DerivativeParameters>* derivative()
+                { return &_derivative; }
+
+            private:
+                artis::pdevs::Simulator<Time, Derivative, DerivativeParameters> _derivative;
+                artis::pdevs::Simulator<Time, artis::pdevs::qss::Integrator<Time>, artis::pdevs::qss::IntegratorParameters> _integrator;
+                artis::pdevs::Simulator<Time, artis::pdevs::qss::Quantifier<Time>, artis::pdevs::qss::QuantifierParameters> _quantifier;
+            };
+
+        }
+    }
+}
+
+#endif

+ 277 - 0
src/artis-star/kernel/pdevs/qss/Integrator.hpp

@@ -0,0 +1,277 @@
+/**
+ * @file kernel/pdevs/qss/Integrator.hpp
+ * @author The ARTIS Development Team
+ * See the AUTHORS or Authors.txt file
+ */
+
+/*
+ * ARTIS - the multimodeling and simulation environment
+ * This file is a part of the ARTIS environment
+ *
+ * Copyright (C) 2013-2019 ULCO http://www.univ-littoral.fr
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef QSS_INTEGRATOR
+#define QSS_INTEGRATOR
+
+#include <artis-star/kernel/pdevs/Dynamics.hpp>
+#include <artis-star/kernel/pdevs/qss/Data.hpp>
+
+namespace artis {
+    namespace pdevs {
+        namespace qss {
+
+            struct IntegratorParameters {
+                double x_0;
+            };
+
+            template<class Time>
+            class Integrator
+                    : public artis::pdevs::Dynamics<Time, Integrator<Time>, IntegratorParameters> {
+            public:
+                enum inputs {
+                    QUANTA = 1, X_DOT
+                };
+
+                enum outputs {
+                    OUT = 1
+                };
+
+                typedef enum vars {
+                    VALUE
+                } Observable;
+
+                Integrator(const std::string& name,
+                        const Context<Time, Integrator<Time>, IntegratorParameters>& context)
+                        :
+                        artis::pdevs::Dynamics<Time, Integrator<Time>, IntegratorParameters>(name,
+                                context)
+                {
+                    this->input_ports({{QUANTA, "quanta"},
+                                       {X_DOT,  "x_dot"}});
+                    this->output_port({OUT, "out"});
+                    this->observable({VALUE, "value"});
+
+                    _init_value = context.parameters().x_0;
+                }
+
+                virtual ~Integrator() { }
+
+                virtual void dconf(typename Time::type t, typename Time::type e,
+                        const common::Bag<Time>& bag)
+                {
+                    dint(t);
+                    dext(t, e, bag);
+                }
+
+                virtual void dint(typename Time::type time)
+                {
+                    switch (_state) {
+                    case RUNNING: {
+                        record_t record;
+                        double last_derivative_value = _archive.back().x_dot;
+
+                        _last_output_value = _expected_value;
+                        _last_output_date = time;
+                        _archive.clear();
+                        record.date = time;
+                        record.x_dot = last_derivative_value;
+                        _archive.push_back(record);
+                        _current_value = _expected_value;
+                        _state = WAIT_FOR_QUANTA;
+                        break;
+                    }
+                    case INIT: {
+                        _state = WAIT_FOR_BOTH;
+                        _last_output_value = _current_value;
+                        _last_output_date = time;
+                        break;
+                    }
+                    default:
+                        assert(false);
+                    }
+                }
+
+                virtual void dext(typename Time::type t, typename Time::type e,
+                        const common::Bag<Time>& bag)
+                {
+                    std::for_each(bag.begin(), bag.end(),
+                            [this, t, e](const common::ExternalEvent<Time>& event) {
+                                if (event.on_port(QUANTA)) {
+                                    QuantifierData data;
+
+                                    event.data()(data);
+                                    _up_threshold = data.up;
+                                    _down_threshold = data.down;
+                                    if (_state == WAIT_FOR_QUANTA) {
+                                        _state = RUNNING;
+                                    }
+                                    if (_state == WAIT_FOR_BOTH) {
+                                        _state = WAIT_FOR_X_DOT;
+                                    }
+                                }
+                                if (event.on_port(X_DOT)) {
+                                    DerivativeData data;
+                                    record_t record;
+
+                                    event.data()(data);
+                                    record.date = t;
+                                    record.x_dot = data.x_dot;
+                                    _archive.push_back(record);
+                                    if (_state == WAIT_FOR_X_DOT) {
+                                        _state = RUNNING;
+                                    }
+                                    if (_state == WAIT_FOR_BOTH) {
+                                        _state = WAIT_FOR_QUANTA;
+                                    }
+                                }
+                            });
+                    if (_state == RUNNING) {
+                        _current_value = current_value(t);
+                        _expected_value = expected_value(t);
+                    }
+                }
+
+                virtual typename Time::type start(typename Time::type /* time */)
+                {
+                    _current_value = _init_value;
+                    _state = INIT;
+                    return 0;
+                }
+
+                virtual typename Time::type ta(typename Time::type /* time */)
+                {
+                    double current_derivative;
+
+                    switch (_state) {
+                    case RUNNING:
+
+                        assert(_archive.size() > 0);
+
+                        current_derivative = _archive.back().x_dot;
+                        if (current_derivative == 0) {
+                            return Time::infinity;
+                        }
+                        if (current_derivative > 0) {
+
+                            assert(_up_threshold - _current_value >= 0);
+
+                            return (_up_threshold - _current_value) / current_derivative;
+                        } else {
+
+                            assert(_down_threshold - _current_value <= 0);
+
+                            return (_down_threshold - _current_value) / current_derivative;
+                        }
+                    default:
+                        return Time::infinity;
+                    }
+                }
+
+                virtual common::Bag<Time> lambda(typename Time::type /* time */) const
+                {
+                    common::Bag<Time> msgs;
+
+                    switch (_state) {
+                    case RUNNING: {
+                        const IntegratorData data = {_expected_value};
+
+                        msgs.push_back(common::ExternalEvent<Time>(OUT, data));
+                        break;
+                    }
+                    case INIT: {
+                        const IntegratorData data = {_current_value};
+
+                        msgs.push_back(common::ExternalEvent<Time>(OUT, data));
+                        break;
+                    }
+                    default:
+                        break;
+                    }
+                    return msgs;
+                }
+
+                virtual common::Value observe(const typename Time::type& /* t */,
+                        unsigned int index) const
+                {
+                    switch (index) {
+                    case VALUE:
+                        return (double) (_current_value);
+                    default:
+                        return common::Value();
+                    }
+                }
+
+            private:
+                double current_value(const typename Time::type& time) const
+                {
+                    double val = _last_output_value;
+
+                    if (_archive.size() > 0) {
+                        for (size_t i = 0; i < _archive.size() - 1; i++) {
+                            val +=
+                                    (_archive[i + 1].date - _archive[i].date) * _archive[i].x_dot;
+                        }
+                        val += (time - _archive.back().date) * _archive.back().x_dot;
+                    }
+                    return val;
+                }
+
+                double expected_value(const typename Time::type& /* time */) const
+                {
+                    double current_derivative = _archive.back().x_dot;
+
+                    if (current_derivative == 0) {
+                        return _current_value;
+                    } else if (current_derivative > 0) {
+                        return _up_threshold;
+                    }
+                    return _down_threshold;
+                }
+
+                typedef enum {
+                    INIT,
+                    WAIT_FOR_QUANTA,
+                    WAIT_FOR_X_DOT,
+                    WAIT_FOR_BOTH,
+                    RUNNING
+                } State;
+
+                struct record_t {
+                    double x_dot;
+                    typename Time::type date;
+                };
+
+                State _state;
+
+                typename Time::type _last_output_date;
+
+                double _up_threshold;
+                double _down_threshold;
+
+                double _last_output_value;
+                double _init_value;
+                double _current_value;
+                double _expected_value;
+
+                std::deque<record_t> _archive;
+            };
+
+        }
+    }
+}
+
+#endif

+ 365 - 0
src/artis-star/kernel/pdevs/qss/Quantifier.hpp

@@ -0,0 +1,365 @@
+/**
+ * @file kernel/pdevs/qss/Quantifier.hpp
+ * @author The ARTIS Development Team
+ * See the AUTHORS or Authors.txt file
+ */
+
+/*
+ * ARTIS - the multimodeling and simulation environment
+ * This file is a part of the ARTIS environment
+ *
+ * Copyright (C) 2013-2019 ULCO http://www.univ-littoral.fr
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef QSS_QUANTIFIER
+#define QSS_QUANTIFIER
+
+#include <artis-star/kernel/pdevs/Dynamics.hpp>
+#include <artis-star/kernel/pdevs/qss/Data.hpp>
+
+#include <cmath>
+
+namespace artis {
+    namespace pdevs {
+        namespace qss {
+
+            struct QuantifierParameters {
+                bool allow_offsets;
+                bool zero_init_offset;
+                double quantum;
+                unsigned int archive_length;
+            };
+
+            template<class Time>
+            class Quantifier
+                    : public artis::pdevs::Dynamics<Time, Quantifier<Time>, QuantifierParameters> {
+            public:
+                enum inputs {
+                    IN = 1
+                };
+
+                enum outputs {
+                    OUT = 1
+                };
+
+                Quantifier(const std::string& name,
+                        const Context<Time, Quantifier<Time>, QuantifierParameters>& context)
+                        :
+                        artis::pdevs::Dynamics<Time, Quantifier<Time>, QuantifierParameters>(name,
+                                context)
+                {
+                    this->input_port({IN, "in"});
+                    this->output_port({OUT, "out"});
+                    this->observables({{UP,    "up"},
+                                       {DOWN,  "down"},
+                                       {VALUE, "value"}});
+
+                    _adaptive = context.parameters().allow_offsets;
+                    _adaptive_state = _adaptive ? POSSIBLE : IMPOSSIBLE;
+                    _zero_init_offset = context.parameters().zero_init_offset;
+                    _step_size = context.parameters().quantum;
+
+                    assert(_step_size > 0);
+
+                    _past_length = context.parameters().archive_length;
+
+                    assert(_past_length > 2);
+                }
+
+                virtual ~Quantifier() { }
+
+                virtual void dconf(typename Time::type t, typename Time::type e,
+                        const common::Bag<Time>& bag)
+                {
+                    dint(t);
+                    dext(t, e, bag);
+                }
+
+                virtual void dint(typename Time::type /* t */)
+                {
+                    switch (_state) {
+                    case INIT:
+                        break;
+                    case IDLE:
+                        break;
+                    case RESPONSE:
+                        _state = IDLE;
+                        break;
+                    }
+                }
+
+                virtual void dext(typename Time::type t, typename Time::type e,
+                        const common::Bag<Time>& bag)
+                {
+                    std::for_each(bag.begin(), bag.end(),
+                            [this, t, e](const common::ExternalEvent<Time>& event) {
+                                IntegratorData data;
+                                double shifting_factor;
+                                double value;
+                                int cnt;
+
+                                event.data()(data);
+                                value = data.value;
+                                if (_state == INIT) {
+                                    init_step_number_and_offset(value);
+                                    update_thresholds();
+                                    _state = RESPONSE;
+                                } else {
+                                    cnt = 0;
+                                    while (value >= _up_threshold or value <= _down_threshold) {
+                                        cnt++;
+                                        if (value >= _up_threshold) {
+                                            _step_number++;
+                                        } else {
+                                            _step_number--;
+                                        }
+                                        switch (_adaptive_state) {
+                                        case IMPOSSIBLE:
+                                            update_thresholds();
+                                            break;
+                                        case POSSIBLE:
+                                            if (value >= _up_threshold) {
+                                                store_change(_step_size, t);
+                                            } else {
+                                                store_change(-_step_size, t);
+                                            }
+                                            shifting_factor = shift_quanta();
+
+                                            assert(shifting_factor >= 0
+                                                    and shifting_factor <= 1);
+
+                                            if (shifting_factor != 0 and shifting_factor != 1) {
+                                                if (value >= _up_threshold) {
+                                                    update_thresholds(shifting_factor,
+                                                            DIRECTION_DOWN);
+                                                } else {
+                                                    update_thresholds(shifting_factor,
+                                                            DIRECTION_UP);
+                                                }
+                                                _adaptive_state = DONE;
+                                            } else {
+                                                update_thresholds();
+                                            }
+                                            break;
+                                        case DONE:
+                                            init_step_number_and_offset(value);
+                                            _adaptive_state = POSSIBLE;
+                                            update_thresholds();
+                                            break;
+                                        }
+                                    }
+                                }
+                            });
+                    _state = RESPONSE;
+                }
+
+                virtual typename Time::type start(typename Time::type /* time */)
+                {
+                    _offset = 0;
+                    _state = INIT;
+                    return Time::infinity;
+                }
+
+                virtual typename Time::type ta(typename Time::type /* time */)
+                {
+                    switch (_state) {
+                    case INIT:
+                    case IDLE:
+                        return Time::infinity;
+                    case RESPONSE:
+                        return 0.0;
+                    }
+                    return Time::infinity;
+                }
+
+                virtual common::Bag<Time> lambda(typename Time::type /* time */) const
+                {
+                    common::Bag<Time> msgs;
+                    const QuantifierData data = {_up_threshold, _down_threshold};
+
+                    msgs.push_back(common::ExternalEvent<Time>(OUT, data));
+                    return msgs;
+                }
+
+                virtual common::Value observe(const typename Time::type& /* t */,
+                        unsigned int index) const
+                {
+                    switch (index) {
+                    case UP:
+                        return (double) _up_threshold;
+                    case DOWN:
+                        return (double) _down_threshold;
+                    case VALUE:
+                        return (double) (_up_threshold - _down_threshold);
+                    default:
+                        return common::Value();
+                    }
+                }
+
+            private:
+                typedef enum {
+                    DIRECTION_UP, DIRECTION_DOWN
+                } Direction;
+
+                void init_step_number_and_offset(double value)
+                {
+                    _step_number = static_cast<long int>(std::floor(value / _step_size));
+                    if (_zero_init_offset) {
+                        _offset = 0;
+                    } else {
+                        _offset = value - static_cast<double>(_step_number) * _step_size;
+                    }
+                }
+
+                bool monotonous(unsigned int range)
+                {
+                    if ((range + 1) > _archive.size()) {
+                        return false;
+                    }
+                    for (size_t i = 0; i < range; i++) {
+                        if (_archive[i].value * _archive[i + 1].value < 0) {
+                            return false;
+                        }
+                    }
+                    return true;
+                }
+
+                bool oscillating(unsigned int range)
+                {
+                    if ((range + 1) > _archive.size()) {
+                        return false;
+                    }
+                    for (size_t i = _archive.size() - range; i < _archive.size() - 1; i++) {
+                        if (_archive[i].value * _archive[i + 1].value > 0) {
+                            return false;
+                        }
+                    }
+                    return true;
+                }
+
+                double shift_quanta()
+                {
+                    double factor = 0;
+
+                    if (oscillating(_past_length - 1) and
+                            _archive.back().date - _archive.front().date != 0) {
+                        double acc;
+                        double local_estim;
+                        int cnt;
+
+                        acc = 0;
+                        cnt = 0;
+                        for (size_t i = 0; i < _archive.size() - 2; ++i) {
+                            if (0 != (_archive[i + 2].date - _archive[i].date)) {
+                                if ((_archive.back().value * _archive[i + 1].value) > 0) {
+                                    local_estim =
+                                            1 - (_archive[i + 1].date - _archive[i].date) /
+                                                    (_archive[i + 2].date - _archive[i].date);
+                                } else {
+                                    local_estim = (_archive[i + 1].date - _archive[i].date) /
+                                            (_archive[i + 2].date - _archive[i].date);
+                                }
+                                acc += local_estim;
+                                cnt++;
+                            }
+                        }
+                        acc = acc / cnt;
+                        factor = acc;
+                        _archive.resize(0);
+                    }
+                    return factor;
+                }
+
+                void store_change(double val, const typename Time::type& time)
+                {
+                    record_t record;
+
+                    record.date = time;
+                    record.value = val;
+                    _archive.push_back(record);
+                    while (_archive.size() > _past_length) {
+                        _archive.pop_front();
+                    }
+                }
+
+                void update_thresholds()
+                {
+                    auto step_number = static_cast<double>(_step_number);
+
+                    _up_threshold = _offset + _step_size * (step_number + 1);
+                    _down_threshold = _offset + _step_size * (step_number - 1);
+                }
+
+                void update_thresholds(double factor)
+                {
+                    auto step_number = static_cast<double>(_step_number);
+
+                    _up_threshold = _offset + _step_size * (step_number + (1 - factor));
+                    _down_threshold = _offset + _step_size * (step_number - (1 - factor));
+                }
+
+                void update_thresholds(double factor, Direction d)
+                {
+                    auto step_number = static_cast<double>(_step_number);
+
+                    if (d == DIRECTION_UP) {
+                        _up_threshold = _offset + _step_size * (step_number + (1 - factor));
+                        _down_threshold = _offset + _step_size * (step_number - 1);
+                    } else {
+                        _up_threshold = _offset + _step_size * (step_number + 1);
+                        _down_threshold = _offset + _step_size * (step_number - (1 - factor));
+                    }
+                }
+
+                typedef enum vars {
+                    UP, DOWN, VALUE
+                } Observable;
+
+                typedef enum {
+                    INIT, IDLE, RESPONSE
+                } State;
+
+                typedef enum {
+                    IMPOSSIBLE, POSSIBLE, DONE
+                } AdaptiveState;
+
+                struct record_t {
+                    double value;
+                    typename Time::type date;
+                };
+
+                State _state;
+                AdaptiveState _adaptive_state;
+                bool _adaptive;
+                bool _zero_init_offset;
+
+                double _offset;
+                double _step_size;
+
+                long int _step_number;
+                double _up_threshold;
+                double _down_threshold;
+
+                std::deque<record_t> _archive;
+
+                unsigned int _past_length;
+            };
+
+        }
+    }
+}
+
+#endif

+ 5 - 3
src/artis-star/kernel/sss/GraphManager.hpp

@@ -87,9 +87,11 @@ namespace artis {
                                     common::ExternalEvent<Time>(
                                             it->second,
                                             ymsg.data()));
-                            dynamic_cast < common::Coordinator<
-                                    Time>* >(_coordinator->get_parent())
-                                    ->dispatch_events(ymessages, t);
+                            if (_coordinator->get_parent()) {
+                                dynamic_cast < common::Coordinator<
+                                        Time>* >(_coordinator->get_parent())
+                                        ->dispatch_events(ymessages, t);
+                            }
                         } else { // event on input port of internal model
                             it->second.get_model()->post_event(
                                     t, common::ExternalEvent<Time>(