/**
* @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