Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Merge branch 'plugins-energy-battery-interaction' into 'master'
[simgrid.git] / src / plugins / battery.cpp
index 21d1ceb..3a870f9 100644 (file)
  * under the terms of the license (GNU LGPL) which comes with this package. */
 #include <simgrid/Exception.hpp>
 #include <simgrid/plugins/battery.hpp>
-#include <simgrid/s4u/Actor.hpp>
+#include <simgrid/plugins/energy.h>
 #include <simgrid/s4u/Engine.hpp>
 #include <simgrid/s4u/Host.hpp>
-#include <simgrid/s4u/VirtualMachine.hpp>
 #include <simgrid/simix.hpp>
+#include <xbt/asserts.h>
+#include <xbt/log.h>
 
 #include "src/kernel/resource/CpuImpl.hpp"
 #include "src/simgrid/module.hpp"
 
-#include <boost/algorithm/string/classification.hpp>
-#include <boost/algorithm/string/split.hpp>
-
-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.
-
-The natural depletion of batteries over time is not taken into account.
-
-State of Health (SoH)
-.....................
-
-A battery starts with an energy budget :math:`E` such as:
+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:
 
 .. math::
 
-  E = C \times U \times D \times N \times 2
+  E_{lost} = {E_{provided} \over \eta_{discharge}}
 
-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.
+  E_{gained} = E_{consumed} \times \eta_{charge}
 
-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.
+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.
 
-.. warning::
+All the energies are positive, but loads connected to a Battery may be positive or negative, as explained in the next
+section.
 
-  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.
+Use the battery reduces its State of Health :math:`SoH` and its capacity :math:`C` linearly in consequence:
 
-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:
+.. math::
 
-.. image:: /img/battery_degradation.svg
-   :align: center
+  SoH = 1 - {E_{lost} + E_{gained} \over E_{budget}}
 
-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
-
-   <host id="Host" speed="100.0Mf" core="1">
-       <prop id="battery_active" value="1" />
-       <prop id="battery_capacity" value="10" />
-       <prop id="battery_cycles" value="200" />
-       <prop id="battery_state_of_charge" value="0.8" />
-   </host>
-
-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)
+  C = C_{initial} \times SoH
 
-  @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<simgrid::s4u::Host, Battery> 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<simgrid::s4u::Host, Battery> 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; });
-}
-
-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; });
-}
+With:
 
-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; });
-}
-
-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; });
-}
+.. math::
 
-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; });
-}
+  E_{budget} = C_{initial} \times N \times 2
 
-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; });
-}
+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:
 
-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; });
-}
+.. image:: /img/battery_degradation.svg
+   :align: center
 
-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; });
-}
+The natural depletion of batteries over time is not taken into account.
 
-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; });
-}
+Loads & Hosts
+..............
 
-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; });
-}
+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_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; });
-}
+Handlers
+......
 
-void Battery::set_eval_cost(bool eval)
-{
-  simgrid::kernel::actor::simcall_answered([this, eval] { eval_cost_ = eval; });
-}
+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_investment_cost(double c)
-{
-  xbt_assert(c >= 0, " : investment cost should be >= 0 (provided: %f)", c);
-  simgrid::kernel::actor::simcall_answered([this, c] { investment_cost_per_wh_ = c; });
-}
-
-void Battery::set_static_maintenance_cost(double c)
-{
-  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; });
-}
+  @endrst
+ */
+XBT_LOG_NEW_DEFAULT_SUBCATEGORY(Battery, kernel, "Logging specific to the battery plugin");
 
-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; });
-}
+namespace simgrid::plugins {
 
-bool Battery::is_charging()
-{
-  update();
-  return power_w_ < 0;
-}
+/* BatteryModel */
 
-bool Battery::is_active() const
-{
-  return active_;
-}
+BatteryModel::BatteryModel() : Model("BatteryModel") {}
 
-double Battery::get_power()
+void BatteryModel::add_battery(BatteryPtr b)
 {
-  update();
-  return power_w_;
+  batteries_.push_back(b);
 }
 
-double Battery::get_state_of_charge()
+void BatteryModel::update_actions_state(double now, double delta)
 {
-  update();
-  return state_of_charge_;
+  for (auto battery : batteries_)
+    battery->update();
 }
 
-double Battery::get_state_of_charge_min() const
+double BatteryModel::next_occurring_event(double now)
 {
-  return state_of_charge_min_;
+  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;
 }
 
-double Battery::get_state_of_charge_max() const
-{
-  return state_of_charge_max_;
-}
+/* Handler */
 
-double Battery::get_state_of_health()
+Battery::Handler::Handler(double state_of_charge, Flow flow,  bool repeat, std::function<void()> callback)
+    : state_of_charge_(state_of_charge), flow_(flow), callback_(callback), repeat_(repeat)
 {
-  update();
-  return 1 - (energy_exchanged_j_ / energy_budget_j_);
 }
 
-double Battery::get_capacity()
+std::shared_ptr<Battery::Handler> Battery::Handler::init(double state_of_charge, Flow flow, bool repeat,
+                                                         std::function<void()> callback)
 {
-  update();
-  return capacity_wh_;
+  return std::make_shared<Battery::Handler>(state_of_charge, flow, repeat, callback);
 }
 
-double Battery::get_cumulative_cost()
-{
-  update();
-  return cumulative_cost_;
-}
+/* Battery */
 
-double Battery::get_next_event_date()
-{
-  update();
-  return next_event_;
-}
+std::shared_ptr<BatteryModel> Battery::battery_model_;
 
-void Battery::init_battery_params()
+void Battery::init_plugin()
 {
-  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()));
-  }
-  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(); });
+  auto model = std::make_shared<BatteryModel>();
+  simgrid::s4u::Engine::get_instance()->add_model(model);
+  Battery::battery_model_ = model;
 }
 
 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 || not is_active())
+  kernel::actor::simcall_answered([this] {
+    double now          = s4u::Engine::get_clock();
+    double time_delta_s = now - last_updated_;
+
+    // Nothing to update
+    if (time_delta_s <= 0)
       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;
+
+    // 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, load] : named_loads_) {
+      if (load > 0)
+        provided_power_w += load;
+      else
+        consumed_power_w += -load;
     }
-    if (event) {
-      power_w_    = 0;
-      next_event_ = -1;
+    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_;
+    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;
+
+    std::vector<std::shared_ptr<Handler>> to_delete = {};
+    for (auto handler : handlers_) {
+      if (abs(handler->time_delta_ - time_delta_s) < 0.000000001) {
+        handler->callback_();
+        if (handler->repeat_)
+          handler->time_delta_ = -1;
+        else
+          to_delete.push_back(handler);
+      }
     }
-    last_updated_ = now;
+    for (auto handler : to_delete)
+      delete_handler(handler);
   });
 }
 
-Battery::Battery(simgrid::s4u::Host* host) : host_(host)
-{
-  init_battery_params();
-}
-
-Battery::~Battery() = default;
-} // namespace simgrid::plugin
-
-using simgrid::plugin::Battery;
-
-/* **************************** events  callback *************************** */
-
-static void on_creation(simgrid::s4u::Host& host)
+double Battery::next_occurring_handler()
 {
-  if (dynamic_cast<simgrid::s4u::VirtualMachine*>(&host)) // Ignore virtual machines
-    return;
-  host.extension_set(new Battery(&host));
-}
-
-/* **************************** Public interface *************************** */
+  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, load] : named_loads_) {
+    if (load > 0)
+      provided_power_w += load;
+    else
+      consumed_power_w += -load;
+  }
 
-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.");
+  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_;
+    // Handler cannot happen
+    if ((lost_power_w == gained_power_w) or (handler->state_of_charge_ == energy_stored_j_ / (3600 * capacity_wh_)) or
+        (lost_power_w > gained_power_w and handler->flow_ == Flow::CHARGE) or
+        (lost_power_w < gained_power_w and handler->flow_ == Flow::DISCHARGE)) {
+      continue;
+    }
+    // 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_;
+    }
+  }
+  return time_delta;
+}
+
+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 non-negative (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);
 }
 
 /** @ingroup plugin_battery
- *  @brief Enable battery plugin.
+ *  @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 delivered by the Battery in W (<= 0).
+ *  @param nominal_discharge_power_w The maximum power absorbed 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.
  */
-void sg_battery_plugin_init()
-{
-  if (Battery::EXTENSION_ID.valid())
-    return;
-  Battery::EXTENSION_ID = simgrid::s4u::Host::extension_create<Battery>();
-  simgrid::s4u::Host::on_creation_cb(&on_creation);
+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
- *  @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 power_w Power of the load in W. A positive value discharges the Battery while a negative value charges it.
  */
-void sg_battery_set_state(const_sg_host_t host, bool state)
+void Battery::set_load(const std::string& name, double power_w)
 {
-  ensure_plugin_inited();
-  host->extension<Battery>()->set_state(state);
+  named_loads_[name] = power_w;
 }
 
 /** @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<Battery>()->set_power(power);
+  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<Battery>()->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<Battery>()->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<Battery>()->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<Battery>()->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<Battery>()->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<Battery>()->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 repeat 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::Handler> Battery::schedule_handler(double state_of_charge, Flow flow, bool repeat, std::function<void()> callback)
 {
-  ensure_plugin_inited();
-  return host->extension<Battery>()->get_capacity();
+  auto handler = Handler::init(state_of_charge, flow, repeat, 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<std::shared_ptr<Battery::Handler>> Battery::get_handlers()
 {
-  ensure_plugin_inited();
-  return host->extension<Battery>()->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> handler)
 {
-  ensure_plugin_inited();
-  return host->extension<Battery>()->get_next_event_date();
+  handlers_.erase(std::remove_if(handlers_.begin(), handlers_.end(),
+                                 [&handler](std::shared_ptr<Handler> e) { return handler == e; }),
+                  handlers_.end());
 }
+} // namespace simgrid::plugins
\ No newline at end of file