/** * @file tests/multithreading/simple/models.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-2022 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 . */ #ifndef TESTS_MULTITHREADING_SIMPLE_MODELS_HPP #define TESTS_MULTITHREADING_SIMPLE_MODELS_HPP #include #include #include #include namespace artis { namespace tests { namespace multithreading { namespace simple { void delay() { for (unsigned int i = 0; i < 1000; ++i) { std::vector v; for (unsigned int j = 1000; j > 0; --j) { v.push_back(j); } std::sort(v.begin(), v.end()); } } struct State { enum values { STOP, UP, MAX, DOWN }; }; struct Vehicle { unsigned int index; double v_max; double acceleration; State::values state; artis::common::DoubleTime::type next_time; }; struct GeneratorParameters { double v_max; double mean; double stddev; unsigned long seed; }; class Generator : public artis::pdevs::Dynamics { public: struct outputs { enum values { OUT }; }; Generator(const std::string &name, const artis::pdevs::Context &context) : artis::pdevs::Dynamics( name, context), _v_max(context.parameters().v_max), _distribution(context.parameters().mean, context.parameters().stddev), _v_max_distribution(0.5, 1.), _port_distribution(0, 7) { _generator.seed(context.parameters().seed); output_ports({{outputs::OUT, "out_1"}}); output_ports({{outputs::OUT + 1, "out_2"}}); output_ports({{outputs::OUT + 2, "out_3"}}); output_ports({{outputs::OUT + 3, "out_4"}}); output_ports({{outputs::OUT + 4, "out_5"}}); output_ports({{outputs::OUT + 5, "out_6"}}); output_ports({{outputs::OUT + 6, "out_7"}}); output_ports({{outputs::OUT + 7, "out_8"}}); } ~Generator() override = default; void dint(const artis::common::DoubleTime::type &t) override { // std::cout << get_full_name() << " at " << t << " - dint" << std::endl; _last_time = t; _sigma = _distribution(_generator); _sigma = _sigma <= 0 ? 0.1 : _sigma; _next_v_max = _v_max * _v_max_distribution(_generator); _next_port = _port_distribution(_generator); ++_index; } void start(const artis::common::DoubleTime::type &t) override { // std::cout << get_full_name() << " at " << t << " - start" << std::endl; _last_time = t; _sigma = _distribution(_generator); _sigma = _sigma <= 0 ? 0.1 : _sigma; _next_v_max = _v_max * _v_max_distribution(_generator); _next_port = _port_distribution(_generator); _index = 1; } artis::common::DoubleTime::type ta(const artis::common::DoubleTime::type & /* t */) const override { return _sigma; } artis::common::event::Bag lambda(const artis::common::DoubleTime::type &t) const override { artis::common::event::Bag bag; // std::cout << get_full_name() << " at " << t << " - lambda" << std::endl; if (t > 0) { Vehicle vehicle = {_index, _next_v_max, 0.5, State::STOP, t}; bag.push_back( artis::common::event::ExternalEvent( outputs::OUT + _next_port, vehicle)); } return bag; } common::DoubleTime::type lookahead(const common::DoubleTime::type & /* t */) const override { return _last_time + _sigma; } private: // parameters double _v_max; // state artis::common::DoubleTime::type _sigma; artis::common::DoubleTime::type _last_time; std::default_random_engine _generator; std::normal_distribution _distribution; std::uniform_real_distribution _v_max_distribution; std::uniform_int_distribution _port_distribution; double _next_v_max; int _next_port; unsigned int _index; }; class Counter : public artis::pdevs::Dynamics { public: struct inputs { enum values { IN }; }; struct vars { enum values { COUNTER }; }; Counter(const std::string &name, const artis::pdevs::Context &context) : artis::pdevs::Dynamics(name, context) { input_port({inputs::IN, "in"}); observable({vars::COUNTER, "counter"}); } ~Counter() override = default; void dext(const artis::common::DoubleTime::type & /* t */, const artis::common::DoubleTime::type & /* e */, const artis::common::event::Bag &bag) override { // std::cout << get_full_name() << " at " << t << " - dext" << std::endl; _counter += bag.size(); } void start(const artis::common::DoubleTime::type & /* t */) override { // std::cout << get_full_name() << " at " << t << " - start" << std::endl; _counter = 0; } artis::common::DoubleTime::type ta(const artis::common::DoubleTime::type & /* t */) const override { return artis::common::DoubleTime::infinity; } artis::common::event::Value observe(const artis::common::DoubleTime::type & /* t */, unsigned int index) const override { if (index == vars::COUNTER) { return _counter; } else { return artis::common::event::Value(); } } common::DoubleTime::type lookahead(const common::DoubleTime::type & /* t */) const override { return common::DoubleTime::infinity; } private: unsigned int _counter; }; class Link : public artis::pdevs::Dynamics { public : struct inputs { enum values { IN }; }; struct outputs { enum values { OUT }; }; Link(const std::string &name, const artis::pdevs::Context &context) : artis::pdevs::Dynamics(name, context) { input_port({inputs::IN, "in"}); output_port({outputs::OUT, "out"}); } ~ Link() override = default; void dint(const artis::common::DoubleTime::type &t) override { // delay(); // std::cout << get_full_name() << " at " << t << " - dint" << std::endl; auto it = _vehicles.begin(); while (it != _vehicles.end()) { if (it->next_time == t and it->state == State::STOP) { _vehicles.erase(it); it = _vehicles.begin(); } else { ++it; } } for (auto &vehicle: _vehicles) { if (vehicle.next_time == t) { switch (vehicle.state) { case State::UP: { double duration = vehicle.v_max / vehicle.acceleration; double acceleration_distance = 0.5 * vehicle.acceleration * duration * duration; vehicle.state = State::MAX; vehicle.next_time = t + (_length - 2 * acceleration_distance) / vehicle.v_max; break; } case State::MAX: { vehicle.state = State::DOWN; vehicle.next_time = t + vehicle.v_max / vehicle.acceleration; break; } case State::DOWN: { vehicle.state = State::STOP; vehicle.next_time = t; break; } case State::STOP: { assert(false); break; } } } } update_sigma(t); } void dext(const artis::common::DoubleTime::type &t, const artis::common::DoubleTime::type & /* e */, const artis::common::event::Bag &bag) override { // std::cout << get_full_name() << " at " << t << " - dext" << std::endl; std::for_each(bag.begin(), bag.end(), [this, t](const common::event::ExternalEvent &event) { if (event.on_port(inputs::IN)) { Vehicle vehicle; event.data()(vehicle); vehicle.next_time = t + vehicle.v_max / vehicle.acceleration; vehicle.state = State::UP; _vehicles.push_back(vehicle); } }); update_sigma(t); } artis::common::DoubleTime::type ta(const artis::common::DoubleTime::type & /* t */) const override { return _sigma; } artis::common::event::Bag lambda(const artis::common::DoubleTime::type &t) const override { artis::common::event::Bag bag; // std::cout << get_full_name() << " at " << t << " - lambda" << std::endl; for (auto vehicle: _vehicles) { if (vehicle.next_time == t and vehicle.state == State::STOP) { bag.push_back( artis::common::event::ExternalEvent( outputs::OUT, vehicle)); } } return bag; } void start(const artis::common::DoubleTime::type & /* t */) override { // std::cout << get_full_name() << " at " << t << " - start" << std::endl; _sigma = artis::common::DoubleTime::infinity; } common::DoubleTime::type lookahead(const common::DoubleTime::type &t) const override { double eot = artis::common::DoubleTime::infinity; for (auto vehicle: _vehicles) { double eot_i = artis::common::DoubleTime::infinity; if (vehicle.next_time == t and vehicle.state == State::STOP) { eot_i = t; } else if (vehicle.state == State::DOWN) { eot_i = vehicle.next_time; } else if (vehicle.state == State::MAX) { eot_i = vehicle.next_time + vehicle.v_max / vehicle.acceleration; } else if (vehicle.state == State::UP) { double duration = vehicle.v_max / vehicle.acceleration; double acceleration_distance = 0.5 * vehicle.acceleration * duration * duration; eot_i = vehicle.next_time + (_length - 2 * acceleration_distance) / vehicle.v_max + vehicle.v_max / vehicle.acceleration; } if (eot_i < eot) { eot = eot_i; } } return eot; } private: void update_sigma(const artis::common::DoubleTime::type &t) { if (_vehicles.empty()) { _sigma = artis::common::DoubleTime::infinity; } else { _sigma = std::min_element(_vehicles.begin(), _vehicles.end(), [](const Vehicle &e1, const Vehicle &e2) { return e1.next_time < e2.next_time; })->next_time - t; } } const double _length = 500; std::deque _vehicles; artis::common::DoubleTime::type _sigma; }; } } } } // namespace artis tests multithreading simple #endif