X-Git-Url: http://info.iut-bm.univ-fcomte.fr/pub/gitweb/simgrid.git/blobdiff_plain/f0534a5e2af72c36c12d55f7ea323040e6e9bf36..201605b6315d5474e458c655eb4fb9952151b732:/src/plugins/battery.cpp diff --git a/src/plugins/battery.cpp b/src/plugins/battery.cpp index 65077e921a..c682e178ac 100644 --- a/src/plugins/battery.cpp +++ b/src/plugins/battery.cpp @@ -1,598 +1,474 @@ +/* Copyright (c) 2023. The SimGrid Team. All rights reserved. */ + +/* This program is free software; you can redistribute it and/or modify it + * under the terms of the license (GNU LGPL) which comes with this package. */ #include #include -#include +#include #include -#include -#include #include #include "src/kernel/resource/CpuImpl.hpp" #include "src/simgrid/module.hpp" -#include -#include - -SIMGRID_REGISTER_PLUGIN(battery, "Battery management", &sg_battery_plugin_init) - +SIMGRID_REGISTER_PLUGIN(battery, "Battery management", nullptr) /** @defgroup plugin_battery plugin_battery Plugin Battery @beginrst -This is the battery plugin, enabling management of batteries on hosts. -To activate this plugin, first call :cpp:func:`sg_battery_plugin_init()`. +This is the battery plugin, enabling management of batteries. -We consider a constant energy exchange model. +Batteries +......... -Properties of batteries such as State of Charge and State of Health are lazily updated, ie., when reading values and -when the power is modified. +A battery has an initial State of Charge :math:`SoC`, a nominal charge power, a nominal discharge power, a charge +efficiency :math:`\eta_{charge}`, a discharge efficiency :math:`\eta_{discharge}`, an initial capacity +:math:`C_{initial}` and a number of cycle :math:`N`. -State of Charge (SoC) -..................... +The nominal charge(discharge) power is the maximum power the Battery can consume(provide), before application of the +charge(discharge) efficiency factor. For instance, if a load provides(consumes) 100W to(from) the Battery with a nominal +charge(discharge) power of 50W and a charge(discharge) efficiency of 0.9, the Battery will only gain(provide) 45W. -If the power of a battery is set to a negative value then the battery will act as a load and fill over time until it -reaches its maximal SoC. Conversely, if the power of a battery is set to a positive value then the battery will act as a -generator and deplete over time until it reaches its minimal SoC. When reaching either its maximal or minimal SoC it -will set its power to 0. +We distinguish the energy provided :math:`E_{provided}` / consumed :math:`E_{consumed}` from the energy lost +:math:`E_{lost}` / gained :math:`E_{gained}`. The energy provided / consumed shows the external point of view, and the +energy lost / gained shows the internal point of view: -The natural depletion of batteries over time is not taken into account. +.. math:: + + E_{lost} = {E_{provided} \over \eta_{discharge}} + + E_{gained} = E_{consumed} \times \eta_{charge} -State of Health (SoH) -..................... +For instance, if you apply a load of 100W to a battery for 10s with a discharge efficiency of 0.8, the energy provided +will be equal to 10kJ, and the energy lost will be equal to 12.5kJ. -A battery starts with an energy budget :math:`E` such as: +All the energies are positive, but loads connected to a Battery may be positive or negative, as explained in the next +section. + +Use the battery reduces its State of Health :math:`SoH` and its capacity :math:`C` linearly in consequence: .. math:: - E = C \times U \times D \times N \times 2 + SoH = 1 - {E_{lost} + E_{gained} \over E_{budget}} -Where :math:`C` is the initial capacity, :math:`U` is the ratio of usable capacity, :math:`D` is the depth of discharge -and :math:`N` is the number of cycles of the battery. + C = C_{initial} \times SoH -The SoH represents the consumption of this energy budget during the lifetime of the battery. -Use the battery reduces its SoH and its capacity in consequence. -When the SoH reaches 0, the battery becomes unusable. +With: -.. warning:: +.. math:: - Due to the decrease of the battery capacity with the SoH, a large usable capacity leads to very tiny battery capacity - when reaching low SoH. This may results in charge and discharge cycles too short to be evaluated by the simulator. To - avoid this situation you should not try to reach a SoH of 0 with a usable capacity set to 1. + E_{budget} = C_{initial} \times N \times 2 -Plotting the output of the example "battery-degradation" highlights the linear decrease of the SoH due to a continuous -use of the battery and the decreasing cycle duration as its capacity reduces: +Plotting the output of the example "battery-degradation" highlights the linear decrease of the :math:`SoH` due to a +continuous use of the battery alternating between charge and discharge: .. image:: /img/battery_degradation.svg :align: center -Batteries properties -.................... - -Properties of the batteries are defined as properties of hosts in the platform XML file. - -Here is an example of XML declaration: - -.. code-block:: xml - - - - - - - - -The different properties are: - -- ``battery_active``: Set the battery as active if set to 1 (default=0) -- ``battery_power``: Set the initial power of the battery in W (default=0). A negative value indicates that the battery act as a load and charge. A positive value indicates that the battery act as a generator and discharge -- ``battery_state_of_charge``: Set the initial state of charge of the battery (default=0) -- ``battery_state_of_charge_min``: Set the minimal state of charge of the battery (default=0.2) -- ``battery_state_of_charge_max``: Set the maximal state of charge of the battery (default=0.8) -- ``battery_capacity``: Set the initial capacity of the battery in Wh (default=0) -- ``battery_usable_capacity``: Set the ratio of usable capacity of the battery (default=0.8) -- ``battery_depth_of_discharge``: Set the depth of discharge of the battery (default=0.9) -- ``battery_charge_efficiency``: Set the charge efficiency of the battery (default=1) -- ``battery_discharge_efficiency``: Set the charge efficiency of the battery (default=1) -- ``battery_cycles``: Set the number of cycle of the battery (default=1) -- ``battery_depth_of_discharge``: Set the number of cycle of the battery (default=1) -- ``battery_eval_cost``: Evaluate the cost of the battery during the simulation if set to 1 (defaulf=0) -- ``battery_investment_cost``: Set the investment cost of the battery (default=0) -- ``battery_static_maintenance_cost``: Set the static maintenance cost of the battery (default=0) -- ``battery_variable_maintenance_cost``: Set the variable maintenance cost of the battery (default=0) - - @endrst - */ -XBT_LOG_NEW_DEFAULT_SUBCATEGORY(battery, kernel, "Logging specific to the battery plugin"); - -namespace simgrid::plugin { -class Battery { -private: - simgrid::s4u::Host* host_ = nullptr; - - double state_of_charge_min_ = 0.2; - double state_of_charge_max_ = 0.8; - double charge_efficiency_ = 1; - double discharge_efficiency_ = 1; - double initial_capacity_wh_ = 0; - double cycles_ = 1; // total complete cycles (charge + discharge) the battery can do before complete depletion. - double depth_of_discharge_ = 0.9; - double usable_capacity_ = 0.8; - double energy_budget_j_ = 0; - - bool active_ = false; - double power_w_ = 0; // NEGATIVE when charging (consumes power) POSITIVE when discharging (generates power) - double state_of_charge_ = 0; - double capacity_wh_ = 0; - double next_event_ = -1; - double energy_exchanged_j_ = 0; - double last_updated_ = 0; - - // Calculation of costs from Bei Li thesis (link :https://tel.archives-ouvertes.fr/tel-02077668/document) (chapter 4) - bool eval_cost_ = false; - double cumulative_cost_ = 0; - double investment_cost_per_wh_ = 0; - double static_maintenance_cost_per_wh_times_h_ = 0; - double variable_maintenance_cost_per_wh_ = 0; - - void init_battery_params(); - void init_cost_params(); - void update(); - - void set_state_of_charge(double soc); - void set_state_of_charge_min(double soc); - void set_state_of_charge_max(double soc); - void set_capacity(double c); - void set_initial_capacity(double c); - void set_cycles(int c); - void set_depth_of_discharge(double d); - void set_usable_capacity(double c); - void set_charge_efficiency(double e); - void set_discharge_efficiency(double e); - void set_eval_cost(bool eval); - void set_investment_cost(double c); - void set_static_maintenance_cost(double c); - void set_variable_maintenance_cost(double c); - - bool is_charging(); - -public: - static simgrid::xbt::Extension EXTENSION_ID; - - explicit Battery(simgrid::s4u::Host* host); - ~Battery(); - - void set_state(bool state); - void set_power(const double power); - - bool is_active() const; - double get_power(); - double get_state_of_charge(); - double get_state_of_charge_min() const; - double get_state_of_charge_max() const; - double get_state_of_health(); - double get_capacity(); - double get_cumulative_cost(); - double get_next_event_date(); -}; - -simgrid::xbt::Extension Battery::EXTENSION_ID; - -void Battery::set_power(const double p) -{ - update(); - xbt_assert(p == 0 or (p > 0 and state_of_charge_ > state_of_charge_min_) or - (p < 0 and state_of_charge_ < state_of_charge_max_), - "Incoherent power and state of charge. A battery cannot charge(discharge) past its maximal(minimal) state " - "of charge."); - xbt_assert(p == 0 or energy_exchanged_j_ < energy_budget_j_, "Cannot set power of a fully used battery."); - simgrid::kernel::actor::simcall_answered([this, p] { - power_w_ = p; - if (power_w_ == 0) { - next_event_ = -1; - return; - } - double soc_shutdown; - double soh_shutdown; - if (power_w_ > 0) { - soc_shutdown = capacity_wh_ * 3600 * (state_of_charge_ - state_of_charge_min_) / (power_w_ * charge_efficiency_); - soh_shutdown = (energy_budget_j_ - energy_exchanged_j_) / (power_w_ * charge_efficiency_); - } else { // power_w_ < 0 - soc_shutdown = - capacity_wh_ * 3600 * (state_of_charge_max_ - state_of_charge_) / (abs(power_w_) / discharge_efficiency_); - soh_shutdown = (energy_budget_j_ - energy_exchanged_j_) / (abs(power_w_) / discharge_efficiency_); - } - if (soh_shutdown <= 0) - next_event_ = simgrid::s4u::Engine::get_clock() + soc_shutdown; - else - next_event_ = simgrid::s4u::Engine::get_clock() + std::min(soc_shutdown, soh_shutdown); - }); -} - -void Battery::set_state(bool state) -{ - update(); - simgrid::kernel::actor::simcall_answered([this, state] { active_ = state; }); -} +The natural depletion of batteries over time is not taken into account. -void Battery::set_state_of_charge(double soc) -{ - xbt_assert(soc > 0 and soc <= 1, " : state of charge should be in ]0,1] (provided: %f)", soc); - simgrid::kernel::actor::simcall_answered([this, soc] { state_of_charge_ = soc; }); -} +Loads & Hosts +............. -void Battery::set_state_of_charge_min(double soc) -{ - xbt_assert(soc > 0 and soc <= 1 and soc < state_of_charge_max_, - " : state of charge min should be in ]0,1] and below state of charge max (provided: %f)", soc); - simgrid::kernel::actor::simcall_answered([this, soc] { state_of_charge_min_ = soc; }); -} +You can add named loads to a battery. Those loads may be positive and consume energy from the battery, or negative and +provide energy to the battery. You can also connect hosts to a battery. Theses hosts will consume their energy from the +battery until the battery is empty or until the connection between the hosts and the battery is set inactive. -void Battery::set_state_of_charge_max(double soc) -{ - xbt_assert(soc > 0 and soc <= 1 and soc > state_of_charge_min_, - " : state of charge max should be in ]0,1] and above state of charge min (provided: %f)", soc); - simgrid::kernel::actor::simcall_answered([this, soc] { state_of_charge_max_ = soc; }); -} +Handlers +........ -void Battery::set_initial_capacity(double c) -{ - xbt_assert(c > 0, " : capacity should be > 0 (provided: %f)", c); - simgrid::kernel::actor::simcall_answered([this, c] { initial_capacity_wh_ = c; }); -} +You can schedule handlers that will happen at specific SoC of the battery and trigger a callback. +Theses handlers may be recurrent, for instance you may want to always set all loads to zero and deactivate all hosts +connections when the battery reaches 20% SoC. -void Battery::set_capacity(double c) -{ - xbt_assert(c > 0, " : capacity should be > 0 (provided: %f)", c); - simgrid::kernel::actor::simcall_answered([this, c] { capacity_wh_ = c; }); -} +Connector +......... -void Battery::set_cycles(int c) -{ - xbt_assert(c > 0, " : cycles should be > 0 (provided: %d)", c); - simgrid::kernel::actor::simcall_answered([this, c] { cycles_ = c; }); -} +A Battery can act as a connector to connect Solar Panels direcly to loads. Such Battery is created without any +parameter, cannot store energy and has a transfer efficiency of 100%. -void Battery::set_depth_of_discharge(double d) -{ - xbt_assert(d > 0 and d <= 1, " : depth of discharge should be in ]0, 1] (provided: %f)", d); - simgrid::kernel::actor::simcall_answered([this, d] { depth_of_discharge_ = d; }); -} + @endrst + */ +XBT_LOG_NEW_DEFAULT_SUBCATEGORY(Battery, kernel, "Logging specific to the battery plugin"); -void Battery::set_usable_capacity(double c) -{ - xbt_assert(c > 0 and c <= 1, " : usable capacity should be in ]0, 1] (provided: %f)", c); - simgrid::kernel::actor::simcall_answered([this, c] { usable_capacity_ = c; }); -} +namespace simgrid::plugins { -void Battery::set_charge_efficiency(double e) -{ - xbt_assert(e > 0 and e <= 1, " : charge efficiency should be in [0,1] (provided: %f)", e); - simgrid::kernel::actor::simcall_answered([this, e] { charge_efficiency_ = e; }); -} +/* BatteryModel */ -void Battery::set_discharge_efficiency(double e) -{ - xbt_assert(e > 0 and e <= 1, " : discharge efficiency should be in [0,1] (provided: %f)", e); - simgrid::kernel::actor::simcall_answered([this, e] { discharge_efficiency_ = e; }); -} +BatteryModel::BatteryModel() : Model("BatteryModel") {} -void Battery::set_eval_cost(bool eval) +void BatteryModel::add_battery(BatteryPtr b) { - simgrid::kernel::actor::simcall_answered([this, eval] { eval_cost_ = eval; }); + batteries_.push_back(b); } -void Battery::set_investment_cost(double c) +void BatteryModel::update_actions_state(double now, double delta) { - xbt_assert(c >= 0, " : investment cost should be >= 0 (provided: %f)", c); - simgrid::kernel::actor::simcall_answered([this, c] { investment_cost_per_wh_ = c; }); + for (auto battery : batteries_) + battery->update(); } -void Battery::set_static_maintenance_cost(double c) +double BatteryModel::next_occurring_event(double now) { - xbt_assert(c >= 0, " : static maintenance cost should be >= 0 (provided: %f)", c); - simgrid::kernel::actor::simcall_answered([this, c] { static_maintenance_cost_per_wh_times_h_ = c; }); + static bool init = false; + if (!init) { + init = true; + return 0; + } + double time_delta = -1; + for (auto battery : batteries_) { + double time_delta_battery = battery->next_occurring_handler(); + time_delta = time_delta == -1 or time_delta_battery < time_delta ? time_delta_battery : time_delta; + } + return time_delta; } -void Battery::set_variable_maintenance_cost(double c) -{ - xbt_assert(c >= 0, " : variable maintenance cost should be >= 0 (provided: %f)", c); - simgrid::kernel::actor::simcall_answered([this, c] { variable_maintenance_cost_per_wh_ = c; }); -} +/* Handler */ -bool Battery::is_charging() +Battery::Handler::Handler(double state_of_charge, Flow flow, Persistancy p, std::function callback) + : state_of_charge_(state_of_charge), flow_(flow), callback_(callback), persistancy_(p) { - update(); - return power_w_ < 0; } -bool Battery::is_active() const +std::shared_ptr Battery::Handler::init(double state_of_charge, Flow flow, Persistancy p, + std::function callback) { - return active_; + return std::make_shared(state_of_charge, flow, p, callback); } -double Battery::get_power() -{ - update(); - return power_w_; -} +/* Battery */ -double Battery::get_state_of_charge() -{ - update(); - return state_of_charge_; -} +std::shared_ptr Battery::battery_model_; -double Battery::get_state_of_charge_min() const +void Battery::init_plugin() { - return state_of_charge_min_; + auto model = std::make_shared(); + simgrid::s4u::Engine::get_instance()->add_model(model); + Battery::battery_model_ = model; } -double Battery::get_state_of_charge_max() const +void Battery::update() { - return state_of_charge_max_; -} + kernel::actor::simcall_answered([this] { + double now = s4u::Engine::get_clock(); + double time_delta_s = now - last_updated_; -double Battery::get_state_of_health() -{ - update(); - return 1 - (energy_exchanged_j_ / energy_budget_j_); -} + // Nothing to update + if (time_delta_s <= 0) + return; -double Battery::get_capacity() -{ - update(); - return capacity_wh_; -} + // Calculate energy provided / consumed during this step + double provided_power_w = 0; + double consumed_power_w = 0; + for (auto const& [host, active] : host_loads_) + provided_power_w += active ? sg_host_get_current_consumption(host) : 0; + for (auto const& [name, pair] : named_loads_) { + if (not pair.first) + continue; + if (pair.second > 0) + provided_power_w += pair.second; + else + consumed_power_w += -pair.second; + } -double Battery::get_cumulative_cost() -{ - update(); - return cumulative_cost_; -} + provided_power_w = std::min(provided_power_w, nominal_discharge_power_w_ * discharge_efficiency_); + consumed_power_w = std::min(consumed_power_w, -nominal_charge_power_w_); + + double energy_lost_delta_j = provided_power_w / discharge_efficiency_ * time_delta_s; + double energy_gained_delta_j = consumed_power_w * charge_efficiency_ * time_delta_s; + + // Check that the provided/consumed energy is valid + energy_lost_delta_j = std::min(energy_lost_delta_j, energy_stored_j_ + energy_gained_delta_j); + /* Charging deteriorate the capacity, but the capacity is used to evaluate the maximum charge so + we need to evaluate the theorethical new capacity in the worst case when we fully charge the battery */ + double new_tmp_capacity_wh = + (initial_capacity_wh_ * + (1 - (energy_provided_j_ + energy_lost_delta_j * discharge_efficiency_ + energy_consumed_j_ - + (energy_stored_j_ + energy_lost_delta_j) / charge_efficiency_) / + energy_budget_j_)) / + (1 + 3600 * initial_capacity_wh_ / (charge_efficiency_ * energy_budget_j_)); + energy_gained_delta_j = + std::min(energy_gained_delta_j, (3600 * new_tmp_capacity_wh) - energy_stored_j_ + energy_lost_delta_j); + + // Updating battery + energy_provided_j_ += energy_lost_delta_j * discharge_efficiency_; + energy_consumed_j_ += energy_gained_delta_j / charge_efficiency_; + + // This battery is a simple connector, we only update energy provided and consumed + if (energy_budget_j_ == 0) { + energy_consumed_j_ = energy_provided_j_; + last_updated_ = now; + return; + } -double Battery::get_next_event_date() -{ - update(); - return next_event_; + capacity_wh_ = + initial_capacity_wh_ * + (1 - (energy_provided_j_ / discharge_efficiency_ + energy_consumed_j_ * charge_efficiency_) / energy_budget_j_); + energy_stored_j_ += energy_gained_delta_j - energy_lost_delta_j; + energy_stored_j_ = std::min(energy_stored_j_, 3600 * capacity_wh_); + last_updated_ = now; + + auto handlers_2 = handlers_; + for (auto handler : handlers_2) { + if (abs(handler->time_delta_ - time_delta_s) < 0.000000001) { + handler->callback_(); + if (handler->persistancy_ == Handler::Persistancy::PERSISTANT) + handler->time_delta_ = -1; + else + delete_handler(handler); + } + } + }); } -void Battery::init_battery_params() +double Battery::next_occurring_handler() { - const char* prop_chars; - prop_chars = host_->get_property("battery_capacity"); - if (prop_chars) { - set_capacity(xbt_str_parse_double(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str())); - set_initial_capacity(xbt_str_parse_double(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str())); + double provided_power_w = 0; + double consumed_power_w = 0; + for (auto const& [host, active] : host_loads_) + provided_power_w += active ? sg_host_get_current_consumption(host) : 0; + for (auto const& [name, pair] : named_loads_) { + if (not pair.first) + continue; + if (pair.second > 0) + provided_power_w += pair.second; + else + consumed_power_w += -pair.second; } - prop_chars = host_->get_property("battery_usable_capacity"); - if (prop_chars) - set_usable_capacity(xbt_str_parse_double(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str())); - prop_chars = host_->get_property("battery_depth_of_discharge"); - if (prop_chars) - set_depth_of_discharge( - xbt_str_parse_double(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str())); - prop_chars = host_->get_property("battery_cycles"); - if (prop_chars) - set_cycles(xbt_str_parse_int(prop_chars, ("cannot parse int: " + std::string(prop_chars)).c_str())); - simgrid::kernel::actor::simcall_answered([this] { - energy_budget_j_ = (initial_capacity_wh_ * usable_capacity_ * depth_of_discharge_ * 3600 * cycles_ * 2); - }); - prop_chars = host_->get_property("battery_active"); - if (prop_chars) - set_state(xbt_str_parse_int(prop_chars, ("cannot parse int: " + std::string(prop_chars)).c_str())); - prop_chars = host_->get_property("battery_state_of_charge_min"); - if (prop_chars) - set_state_of_charge_min( - xbt_str_parse_double(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str())); - prop_chars = host_->get_property("battery_state_of_charge_max"); - if (prop_chars) - set_state_of_charge_max( - xbt_str_parse_double(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str())); - prop_chars = host_->get_property("battery_charge_efficiency"); - if (prop_chars) - set_charge_efficiency( - xbt_str_parse_double(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str())); - prop_chars = host_->get_property("battery_discharge_efficiency"); - if (prop_chars) - set_discharge_efficiency( - xbt_str_parse_double(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str())); - prop_chars = host_->get_property("battery_state_of_charge"); - if (prop_chars) - set_state_of_charge(xbt_str_parse_double(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str())); - prop_chars = host_->get_property("battery_eval_cost"); - if (prop_chars) - set_eval_cost(xbt_str_parse_int(prop_chars, ("cannot parse int: " + std::string(prop_chars)).c_str())); - prop_chars = host_->get_property("battery_investment_cost"); - if (prop_chars) - set_investment_cost(xbt_str_parse_int(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str())); - prop_chars = host_->get_property("battery_static_maintenance_cost"); - if (prop_chars) - set_static_maintenance_cost( - xbt_str_parse_int(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str())); - prop_chars = host_->get_property("battery_variable_maintenance_cost"); - if (prop_chars) - set_variable_maintenance_cost( - xbt_str_parse_int(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str())); - prop_chars = host_->get_property("battery_power"); - if (prop_chars) - set_power(xbt_str_parse_double(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str())); - simgrid::kernel::actor::simcall_answered([this] { last_updated_ = simgrid::s4u::Engine::get_clock(); }); -} -void Battery::update() -{ - simgrid::kernel::actor::simcall_answered([this] { - double now = simgrid::s4u::Engine::get_clock(); - double time_delta_real = now - last_updated_; - if (time_delta_real <= 0 or not is_active()) - return; - double time_delta_until_event = next_event_ - last_updated_; - bool event = next_event_ != -1 and time_delta_until_event <= time_delta_real; - double power_real_w = power_w_ < 0 ? power_w_ * charge_efficiency_ : power_w_ / discharge_efficiency_; - state_of_charge_ -= power_real_w * (event ? time_delta_until_event : time_delta_real) / (3600 * capacity_wh_); - energy_exchanged_j_ += (event ? time_delta_until_event : time_delta_real) * abs(power_real_w); - capacity_wh_ = initial_capacity_wh_ * usable_capacity_ * (1 - (energy_exchanged_j_ / energy_budget_j_)) + - initial_capacity_wh_ * (1 - usable_capacity_); - capacity_wh_ = std::max(capacity_wh_, 0.0); - if (eval_cost_) { - double usage_cost_per_wh = - (investment_cost_per_wh_ / (depth_of_discharge_ * cycles_ * 2) + variable_maintenance_cost_per_wh_); - double usage_cost = - usage_cost_per_wh * abs(power_real_w) * (event ? time_delta_until_event : time_delta_real) / 3600; - double static_maintenance_cost = - static_maintenance_cost_per_wh_times_h_ * initial_capacity_wh_ * time_delta_real / 3600; - cumulative_cost_ += usage_cost + static_maintenance_cost; + provided_power_w = std::min(provided_power_w, nominal_discharge_power_w_ * discharge_efficiency_); + consumed_power_w = std::min(consumed_power_w, -nominal_charge_power_w_); + + double time_delta = -1; + for (auto& handler : handlers_) { + double lost_power_w = provided_power_w / discharge_efficiency_; + double gained_power_w = consumed_power_w * charge_efficiency_; + if ((lost_power_w == gained_power_w) or (handler->state_of_charge_ == get_state_of_charge()) or + (lost_power_w > gained_power_w and + (handler->flow_ == Flow::CHARGE or handler->state_of_charge_ > get_state_of_charge())) or + (lost_power_w < gained_power_w and + (handler->flow_ == Flow::DISCHARGE or handler->state_of_charge_ < get_state_of_charge()))) { + continue; } - if (event) { - power_w_ = 0; - next_event_ = -1; + // Evaluate time until handler happen + else { + /* The time to reach a state of charge depends on the capacity, but charging and discharging deteriorate the + * capacity, so we need to evaluate the time considering a capacity that also depends on time + */ + handler->time_delta_ = + (3600 * handler->state_of_charge_ * initial_capacity_wh_ * + (1 - (energy_provided_j_ / discharge_efficiency_ + energy_consumed_j_ * charge_efficiency_) / + energy_budget_j_) - + energy_stored_j_) / + (gained_power_w - lost_power_w + + 3600 * handler->state_of_charge_ * initial_capacity_wh_ * (gained_power_w + lost_power_w) / + energy_budget_j_); + if ((time_delta == -1 or handler->time_delta_ < time_delta) and abs(handler->time_delta_) > 0.000000001) + time_delta = handler->time_delta_; } - last_updated_ = now; - }); -} - -Battery::Battery(simgrid::s4u::Host* host) : host_(host) -{ - init_battery_params(); + } + return time_delta; +} + +Battery::Battery() {} + +Battery::Battery(const std::string& name, double state_of_charge, double nominal_charge_power_w, + double nominal_discharge_power_w, double charge_efficiency, double discharge_efficiency, + double initial_capacity_wh, int cycles) + : name_(name) + , nominal_charge_power_w_(nominal_charge_power_w) + , nominal_discharge_power_w_(nominal_discharge_power_w) + , charge_efficiency_(charge_efficiency) + , discharge_efficiency_(discharge_efficiency) + , initial_capacity_wh_(initial_capacity_wh) + , energy_budget_j_(initial_capacity_wh * 3600 * cycles * 2) + , capacity_wh_(initial_capacity_wh) + , energy_stored_j_(state_of_charge * 3600 * initial_capacity_wh) +{ + xbt_assert(nominal_charge_power_w <= 0, " : nominal charge power must be <= 0 (provided: %f)", + nominal_charge_power_w); + xbt_assert(nominal_discharge_power_w >= 0, " : nominal discharge power must be non-negative (provided: %f)", + nominal_discharge_power_w); + xbt_assert(state_of_charge >= 0 and state_of_charge <= 1, " : state of charge should be in [0, 1] (provided: %f)", + state_of_charge); + xbt_assert(charge_efficiency > 0 and charge_efficiency <= 1, " : charge efficiency should be in [0,1] (provided: %f)", + charge_efficiency); + xbt_assert(discharge_efficiency > 0 and discharge_efficiency <= 1, + " : discharge efficiency should be in [0,1] (provided: %f)", discharge_efficiency); + xbt_assert(initial_capacity_wh > 0, " : initial capacity should be > 0 (provided: %f)", initial_capacity_wh); + xbt_assert(cycles > 0, " : cycles should be > 0 (provided: %d)", cycles); } -Battery::~Battery() = default; -} // namespace simgrid::plugin - -using simgrid::plugin::Battery; - -/* **************************** events callback *************************** */ - -static void on_creation(simgrid::s4u::Host& host) +/** @ingroup plugin_battery + * @brief Init a Battery with this constructor makes it only usable as a connector. + * A connector has no capacity and only delivers as much power as it receives + with a transfer efficiency of 100%. + * @return A BatteryPtr pointing to the new Battery. + */ +BatteryPtr Battery::init() { - if (dynamic_cast(&host)) // Ignore virtual machines - return; - host.extension_set(new Battery(&host)); + static bool plugin_inited = false; + if (not plugin_inited) { + init_plugin(); + plugin_inited = true; + } + auto battery = BatteryPtr(new Battery()); + battery_model_->add_battery(battery); + return battery; } -/* **************************** Public interface *************************** */ - -static void ensure_plugin_inited() -{ - if (not Battery::EXTENSION_ID.valid()) - throw simgrid::xbt::InitializationError("The Battery plugin is not active. Please call sg_battery_plugin_init() " - "before calling any function related to that plugin."); +/** @ingroup plugin_battery + * @param name The name of the Battery. + * @param state_of_charge The initial state of charge of the Battery [0,1]. + * @param nominal_charge_power_w The maximum power absorbed by the Battery in W (<= 0). + * @param nominal_discharge_power_w The maximum power delivered by the Battery in W (>= 0). + * @param charge_efficiency The charge efficiency of the Battery [0,1]. + * @param discharge_efficiency The discharge efficiency of the Battery [0,1]. + * @param initial_capacity_wh The initial capacity of the Battery in Wh (>0). + * @param cycles The number of charge-discharge cycles until complete depletion of the Battery capacity. + * @return A BatteryPtr pointing to the new Battery. + */ +BatteryPtr Battery::init(const std::string& name, double state_of_charge, double nominal_charge_power_w, + double nominal_discharge_power_w, double charge_efficiency, double discharge_efficiency, + double initial_capacity_wh, int cycles) +{ + static bool plugin_inited = false; + if (not plugin_inited) { + init_plugin(); + plugin_inited = true; + } + auto battery = BatteryPtr(new Battery(name, state_of_charge, nominal_charge_power_w, nominal_discharge_power_w, + charge_efficiency, discharge_efficiency, initial_capacity_wh, cycles)); + battery_model_->add_battery(battery); + return battery; } /** @ingroup plugin_battery - * @brief Enable battery plugin. + * @param name The name of the load + * @param power_w Power of the load in W. A positive value discharges the Battery while a negative value charges it. */ -void sg_battery_plugin_init() +void Battery::set_load(const std::string& name, double power_w) { - if (Battery::EXTENSION_ID.valid()) - return; - Battery::EXTENSION_ID = simgrid::s4u::Host::extension_create(); - simgrid::s4u::Host::on_creation_cb(&on_creation); + kernel::actor::simcall_answered([this, &name, &power_w] { + if (named_loads_.find(name) == named_loads_.end()) + named_loads_[name] = std::make_pair(true, power_w); + else + named_loads_[name].second = power_w; + }); } /** @ingroup plugin_battery - * @param state The state to set. - * @brief Set the state of the battery. - * A battery set to inactive (false) will neither update its state of charge nor its state of health. + * @param name The name of the load + * @param active Status of the load. If false then the load is ignored by the Battery. */ -void sg_battery_set_state(const_sg_host_t host, bool state) +void Battery::set_load(const std::string& name, bool active) { - ensure_plugin_inited(); - host->extension()->set_state(state); + kernel::actor::simcall_answered([this, &name, &active] { named_loads_[name].first = active; }); } /** @ingroup plugin_battery - * @param power The power to set in W. - * @brief Set the power of the battery. - * @note A negative value makes the battery act as a load and charge. - * A positive value makes the battery act as a generator and discharge. + * @param host The Host to connect. + * @param active Status of the connected Host (default true). + * @brief Connect a Host to the Battery with the status active. As long as the status is true the Host takes its energy + from the Battery. To modify this status connect again the same Host with a different status. + @warning Do NOT connect the same Host to multiple Batteries with the status true at the same time. + In this case all Batteries would have the full consumption from this Host. */ -void sg_battery_set_power(const_sg_host_t host, double power) +void Battery::connect_host(s4u::Host* host, bool active) { - ensure_plugin_inited(); - host->extension()->set_power(power); + kernel::actor::simcall_answered([this, &host, &active] { host_loads_[host] = active; }); } /** @ingroup plugin_battery - * @brief Return true if the battery is active. + * @return The state of charge of the battery. */ -bool sg_battery_is_active(const_sg_host_t host) +double Battery::get_state_of_charge() { - ensure_plugin_inited(); - return host->extension()->is_active(); + return energy_stored_j_ / (3600 * capacity_wh_); } /** @ingroup plugin_battery - * @brief Return the power of the battery in W. - * @note A negative value indicates that the battery act as a load and charge. - * A positive value indicates that the battery act as a generator and discharge. + * @return The state of health of the Battery. */ -double sg_battery_get_power(const_sg_host_t host) +double Battery::get_state_of_health() { - ensure_plugin_inited(); - return host->extension()->get_power(); + return 1 - + ((energy_provided_j_ / discharge_efficiency_ + energy_consumed_j_ * charge_efficiency_) / energy_budget_j_); } /** @ingroup plugin_battery - * @brief Return the state of charge of the battery. + * @return The current capacity of the Battery. */ -double sg_battery_get_state_of_charge(const_sg_host_t host) +double Battery::get_capacity() { - ensure_plugin_inited(); - return host->extension()->get_state_of_charge(); + return capacity_wh_; } /** @ingroup plugin_battery - * @brief Return the minimal state of charge of the battery. + * @return The energy provided by the Battery. + * @note It is the energy provided from an external point of view, after application of the discharge efficiency. + It means that the Battery lost more energy than it has provided. */ -double sg_battery_get_state_of_charge_min(const_sg_host_t host) +double Battery::get_energy_provided() { - ensure_plugin_inited(); - return host->extension()->get_state_of_charge_min(); + return energy_provided_j_; } /** @ingroup plugin_battery - * @brief Return the maximal state of charge of the battery. + * @return The energy consumed by the Battery. + * @note It is the energy consumed from an external point of view, before application of the charge efficiency. + It means that the Battery consumed more energy than is has absorbed. */ -double sg_battery_get_state_of_charge_max(const_sg_host_t host) +double Battery::get_energy_consumed() { - ensure_plugin_inited(); - return host->extension()->get_state_of_charge_max(); + return energy_consumed_j_; } /** @ingroup plugin_battery - * @brief Return the state of health of the battery. + * @param unit Valid units are J (default) and Wh. + * @return Energy stored in the Battery. */ -double sg_battery_get_state_of_health(const_sg_host_t host) +double Battery::get_energy_stored(std::string unit) { - ensure_plugin_inited(); - return host->extension()->get_state_of_health(); + if (unit == "J") + return energy_stored_j_; + else if (unit == "Wh") + return energy_stored_j_ / 3600; + else + xbt_die("Invalid unit. Valid units are J (default) or Wh."); } /** @ingroup plugin_battery - * @brief Return the capacity of the battery in Wh. - * @note capacity is reduced by the state of health of the battery. + * @brief Schedule a new Handler. + * @param state_of_charge The state of charge at which the Handler will happen. + * @param flow The flow in which the Handler will happen, either when the Battery is charging or discharging. + * @param callback The callable to trigger when the Handler happen. + * @param p If the Handler is recurrent or unique. + * @return A shared pointer of the new Handler. */ -double sg_battery_get_capacity(const_sg_host_t host) +std::shared_ptr Battery::schedule_handler(double state_of_charge, Flow flow, Handler::Persistancy p, + std::function callback) { - ensure_plugin_inited(); - return host->extension()->get_capacity(); + auto handler = Handler::init(state_of_charge, flow, p, callback); + handlers_.push_back(handler); + return handler; } /** @ingroup plugin_battery - * @brief Return the cumulative cost of the battery. + * @return A vector containing the Handlers associated to the Battery. */ -double sg_battery_get_cumulative_cost(const_sg_host_t host) +std::vector> Battery::get_handlers() { - ensure_plugin_inited(); - return host->extension()->get_cumulative_cost(); + return handlers_; } /** @ingroup plugin_battery - * @brief Return the date of the next event, i.e., when the battery will be empty or full. - * @note If power is null then return -1. + * @brief Remove an Handler from the Battery. */ -double sg_battery_get_next_event_date(const_sg_host_t host) +void Battery::delete_handler(std::shared_ptr handler) { - ensure_plugin_inited(); - return host->extension()->get_next_event_date(); + handlers_.erase(std::remove_if(handlers_.begin(), handlers_.end(), + [&handler](std::shared_ptr e) { return handler == e; }), + handlers_.end()); } +} // namespace simgrid::plugins \ No newline at end of file