Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
remove unused battery parameters
[simgrid.git] / src / plugins / battery.cpp
1 /* Copyright (c) 2023. The SimGrid Team. All rights reserved.          */
2
3 /* This program is free software; you can redistribute it and/or modify it
4  * under the terms of the license (GNU LGPL) which comes with this package. */
5 #include <simgrid/Exception.hpp>
6 #include <simgrid/plugins/battery.hpp>
7 #include <simgrid/plugins/energy.h>
8 #include <simgrid/s4u/Engine.hpp>
9 #include <simgrid/s4u/Host.hpp>
10 #include <simgrid/simix.hpp>
11 #include <xbt/asserts.h>
12 #include <xbt/log.h>
13
14 #include "src/kernel/resource/CpuImpl.hpp"
15 #include "src/simgrid/module.hpp"
16
17 SIMGRID_REGISTER_PLUGIN(battery, "Battery management", nullptr)
18 /** @defgroup plugin_battery plugin_battery Plugin Battery
19
20   @beginrst
21
22 This is the battery plugin, enabling management of batteries.
23
24 With this plugin you can:
25
26 - create Batteries
27 - associate positive or negative load to Batteries
28 - connect Hosts to Batteries
29 - create Events triggered whenever a Battery reach a specific state of charge
30
31 The natural depletion of batteries over time is not taken into account.
32
33 A battery starts with an energy budget :math:`E` such as:
34
35 .. math::
36
37   E = C \times D \times N \times 2
38
39 Where :math:`C` is the initial capacity, :math:`D` is the depth of discharge
40 and :math:`N` is the number of cycles of the battery.
41
42 The SoH represents the consumption of this energy budget during the lifetime of the battery.
43 Use the battery reduces its SoH and its capacity in consequence.
44 When the SoH reaches 0, the battery becomes unusable.
45
46 Plotting the output of the example "battery-degradation" highlights the linear decrease of the SoH due to a continuous
47 use of the battery and the decreasing cycle duration as its capacity reduces:
48
49 .. image:: /img/battery_degradation.svg
50    :align: center
51
52   @endrst
53  */
54 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(Battery, kernel, "Logging specific to the battery plugin");
55
56 namespace simgrid::plugins {
57
58 /* BatteryModel */
59
60 BatteryModel::BatteryModel() : Model("BatteryModel") {}
61
62 void BatteryModel::add_battery(BatteryPtr b)
63 {
64   batteries_.push_back(b);
65 }
66
67 void BatteryModel::update_actions_state(double now, double delta)
68 {
69   for (auto battery : batteries_)
70     battery->update();
71 }
72
73 double BatteryModel::next_occurring_event(double now)
74 {
75   double time_delta = -1;
76   for (auto battery : batteries_) {
77     double time_delta_battery = battery->next_occurring_event();
78     time_delta                = time_delta == -1 or time_delta_battery < time_delta ? time_delta_battery : time_delta;
79   }
80   return time_delta;
81 }
82
83 /* Event */
84
85 Battery::Event::Event(double state_of_charge, Flow flow, std::function<void()> callback, bool repeat)
86     : state_of_charge_(state_of_charge), flow_(flow), callback_(callback), repeat_(repeat)
87 {
88 }
89
90 std::shared_ptr<Battery::Event> Battery::Event::init(double state_of_charge, Flow flow, std::function<void()> callback,
91                                                      bool repeat)
92 {
93   return std::make_shared<Battery::Event>(state_of_charge, flow, callback, repeat);
94 }
95
96 /* Battery */
97
98 std::shared_ptr<BatteryModel> Battery::battery_model_;
99
100 void Battery::init_plugin()
101 {
102   auto model = std::make_shared<BatteryModel>();
103   simgrid::s4u::Engine::get_instance()->add_model(model);
104   Battery::battery_model_ = model;
105 }
106
107 void Battery::update()
108 {
109   kernel::actor::simcall_answered([this] {
110     double now          = s4u::Engine::get_clock();
111     double time_delta_s = now - last_updated_;
112
113     // Nothing to update
114     if (time_delta_s <= 0)
115       return;
116
117     // Calculate energy provided / consumed during this step
118     double provided_power_w = 0;
119     double consumed_power_w = 0;
120     for (auto const& [host, active] : host_loads_)
121       provided_power_w += active ? sg_host_get_current_consumption(host) : 0;
122     for (auto const& [name, load] : named_loads_) {
123       if (load > 0)
124         provided_power_w += load;
125       else
126         consumed_power_w += -load;
127     }
128     double energy_lost_delta_j   = provided_power_w / discharge_efficiency_ * time_delta_s;
129     double energy_gained_delta_j = consumed_power_w * charge_efficiency_ * time_delta_s;
130
131     // Check that the provided/consumed energy is valid
132     energy_lost_delta_j = std::min(energy_lost_delta_j, energy_stored_j_ + energy_gained_delta_j);
133     /* Charging deteriorate the capacity, but the capacity is used to evaluate the maximum charge so
134        we need to evaluate the theorethical new capacity in the worst case when we fully charge the battery */
135     double new_tmp_capacity_wh =
136         (initial_capacity_wh_ *
137          (1 - (energy_provided_j_ + energy_lost_delta_j * discharge_efficiency_ + energy_consumed_j_ -
138                (energy_stored_j_ + energy_lost_delta_j) / charge_efficiency_) /
139                   energy_budget_j_)) /
140         (1 + 3600 * initial_capacity_wh_ / (charge_efficiency_ * energy_budget_j_));
141     energy_gained_delta_j =
142         std::min(energy_gained_delta_j, (3600 * new_tmp_capacity_wh) - energy_stored_j_ + energy_lost_delta_j);
143
144     // Updating battery
145     energy_provided_j_ += energy_lost_delta_j * discharge_efficiency_;
146     energy_consumed_j_ += energy_gained_delta_j / charge_efficiency_;
147     capacity_wh_ = initial_capacity_wh_ * (1 - (energy_provided_j_ + energy_consumed_j_) / energy_budget_j_);
148     energy_stored_j_ += energy_gained_delta_j - energy_lost_delta_j;
149     energy_stored_j_ = std::min(energy_stored_j_, 3600 * capacity_wh_);
150     last_updated_    = now;
151
152     std::vector<std::shared_ptr<Event>> to_delete = {};
153     for (auto event : events_) {
154       if (abs(event->time_delta_ - time_delta_s) < 0.000000001) {
155         event->callback_();
156         if (event->repeat_)
157           event->time_delta_ = -1;
158         else
159           to_delete.push_back(event);
160       }
161     }
162     for (auto event : to_delete)
163       delete_event(event);
164   });
165 }
166
167 double Battery::next_occurring_event()
168 {
169   double provided_power_w = 0;
170   double consumed_power_w = 0;
171   for (auto const& [host, active] : host_loads_)
172     provided_power_w += active ? sg_host_get_current_consumption(host) : 0;
173   for (auto const& [name, load] : named_loads_) {
174     if (load > 0)
175       provided_power_w += load;
176     else
177       consumed_power_w += -load;
178   }
179
180   double time_delta = -1;
181   for (auto& event : events_) {
182     double lost_power_w   = provided_power_w / discharge_efficiency_;
183     double gained_power_w = consumed_power_w * charge_efficiency_;
184     // Event cannot happen
185     if ((lost_power_w == gained_power_w) or (event->state_of_charge_ == energy_stored_j_ / (3600 * capacity_wh_)) or
186         (lost_power_w > gained_power_w and event->flow_ == Flow::CHARGE) or
187         (lost_power_w < gained_power_w and event->flow_ == Flow::DISCHARGE)) {
188       continue;
189     }
190     // Evaluate time until event happen
191     else {
192       /* The time to reach a state of charge depends on the capacity, but charging and discharging deteriorate the
193        * capacity, so we need to evaluate the time considering a capacity that also depends on time
194        */
195       event->time_delta_ = (3600 * event->state_of_charge_ * initial_capacity_wh_ *
196                                 (1 - (energy_provided_j_ + energy_consumed_j_) / energy_budget_j_) -
197                             energy_stored_j_) /
198                            (gained_power_w - lost_power_w +
199                             3600 * event->state_of_charge_ * initial_capacity_wh_ *
200                                 (consumed_power_w + provided_power_w) / energy_budget_j_);
201       if ((time_delta == -1 or event->time_delta_ < time_delta) and abs(event->time_delta_) > 0.000000001)
202         time_delta = event->time_delta_;
203     }
204   }
205   return time_delta;
206 }
207
208 Battery::Battery(const std::string& name, double state_of_charge, double charge_efficiency, double discharge_efficiency,
209                  double initial_capacity_wh, int cycles, double depth_of_discharge)
210     : name_(name)
211     , charge_efficiency_(charge_efficiency)
212     , discharge_efficiency_(discharge_efficiency)
213     , initial_capacity_wh_(initial_capacity_wh)
214     , energy_budget_j_(initial_capacity_wh * depth_of_discharge * 3600 * cycles * 2)
215     , capacity_wh_(initial_capacity_wh)
216     , energy_stored_j_(state_of_charge * 3600 * initial_capacity_wh)
217 {
218   xbt_assert(state_of_charge >= 0 and state_of_charge <= 1, " : state of charge should be in [0, 1] (provided: %f)",
219              state_of_charge);
220   xbt_assert(charge_efficiency > 0 and charge_efficiency <= 1, " : charge efficiency should be in [0,1] (provided: %f)",
221              charge_efficiency);
222   xbt_assert(discharge_efficiency > 0 and discharge_efficiency <= 1,
223              " : discharge efficiency should be in [0,1] (provided: %f)", discharge_efficiency);
224   xbt_assert(initial_capacity_wh > 0, " : initial capacity should be > 0 (provided: %f)", initial_capacity_wh);
225   xbt_assert(cycles > 0, " : cycles should be > 0 (provided: %d)", cycles);
226   xbt_assert(depth_of_discharge > 0 and depth_of_discharge <= 1,
227              " : depth of discharge should be in ]0, 1] (provided: %f)", depth_of_discharge);
228 }
229
230 /** @ingroup plugin_battery
231  *  @param name The name of the Battery.
232  *  @param state_of_charge The initial state of charge of the Battery [0,1].
233  *  @param charge_efficiency The charge efficiency of the Battery [0,1].
234  *  @param discharge_efficiency The discharge efficiency of the Battery [0,1].
235  *  @param initial_capacity_wh The initial capacity of the Battery in Wh (>0).
236  *  @param cycles The number of charge-discharge cycles until complete depletion of the Battery capacity.
237  *  @param depth_of_discharge The depth of discharge of the Battery.
238  *  @return A BatteryPtr pointing to the new Battery.
239  */
240 BatteryPtr Battery::init(const std::string& name, double state_of_charge, double charge_efficiency,
241                          double discharge_efficiency, double initial_capacity_wh, int cycles, double depth_of_discharge)
242 {
243   static bool plugin_inited = false;
244   if (not plugin_inited) {
245     init_plugin();
246     plugin_inited = true;
247   }
248   auto battery = BatteryPtr(new Battery(name, state_of_charge, charge_efficiency, discharge_efficiency,
249                                         initial_capacity_wh, cycles, depth_of_discharge));
250   battery_model_->add_battery(battery);
251   return battery;
252 }
253
254 /** @ingroup plugin_battery
255  *  @param name The name of the load
256  *  @param power_w Power of the load in W. A positive value discharges the Battery while a negative value charges it.
257  */
258 void Battery::set_load(const std::string& name, double power_w)
259 {
260   named_loads_[name] = power_w;
261 }
262
263 /** @ingroup plugin_battery
264  *  @param h The Host to connect.
265  *  @param active Status of the connected Host (default true).
266  *  @brief Connect a Host to the Battery with the status active. As long as the status is true the Host takes its energy
267  from the Battery. To modify this status connect again the same Host with a different status.
268     @warning Do NOT connect the same Host to multiple Batteries with the status true at the same time.
269     In this case all Batteries would have the full consumption from this Host.
270  */
271 void Battery::connect_host(s4u::Host* host, bool active)
272 {
273   host_loads_[host] = active;
274 }
275
276 /** @ingroup plugin_battery
277  *  @return The state of charge of the battery.
278  */
279 double Battery::get_state_of_charge()
280 {
281   return energy_stored_j_ / (3600 * capacity_wh_);
282 }
283
284 /** @ingroup plugin_battery
285  *  @return The state of health of the Battery.
286  */
287 double Battery::get_state_of_health()
288 {
289   return 1 - ((energy_provided_j_ + energy_consumed_j_) / energy_budget_j_);
290 }
291
292 /** @ingroup plugin_battery
293  *  @return The current capacity of the Battery.
294  */
295 double Battery::get_capacity()
296 {
297   return capacity_wh_;
298 }
299
300 /** @ingroup plugin_battery
301  *  @return The energy provided by the Battery.
302  *  @note It is the energy provided from an external point of view, after application of the discharge efficiency.
303           It means that the Battery lost more energy than it has provided.
304  */
305 double Battery::get_energy_provided()
306 {
307   return energy_provided_j_;
308 }
309
310 /** @ingroup plugin_battery
311  *  @return The energy consumed by the Battery.
312  *  @note It is the energy consumed from an external point of view, before application of the charge efficiency.
313           It means that the Battery consumed more energy than is has absorbed.
314  */
315 double Battery::get_energy_consumed()
316 {
317   return energy_consumed_j_;
318 }
319
320 /** @ingroup plugin_battery
321  *  @param Unit Valid units are J (default) and Wh.
322  *  @return Energy stored in the Battery.
323  */
324 double Battery::get_energy_stored(std::string unit)
325 {
326   if (unit == "J")
327     return energy_stored_j_;
328   else if (unit == "Wh")
329     return energy_stored_j_ / 3600;
330   else
331     xbt_die("Invalid unit. Valid units are J (default) or Wh.");
332 }
333
334 /** @ingroup plugin_battery
335  *  @brief Create a new Event.
336  *  @param state_of_charge The state of charge at which the Event will happen.
337  *  @param flow The flow in which the Event will happen, either when the Battery is charging or discharging.
338  *  @param callback The callable to trigger when the Event happen.
339  *  @param repeat If the Event is a recurrent Event or a single Event.
340  *  @return A shared pointer of the new Event.
341  */
342 std::shared_ptr<Battery::Event> Battery::create_event(double state_of_charge, Flow flow, std::function<void()> callback,
343                                                       bool repeat)
344 {
345   auto event = Event::init(state_of_charge, flow, callback, repeat);
346   events_.push_back(event);
347   return event;
348 }
349
350 /** @ingroup plugin_battery
351  *  @return A vector containing the Events associated to the Battery.
352  */
353 std::vector<std::shared_ptr<Battery::Event>> Battery::get_events()
354 {
355   return events_;
356 }
357
358 /** @ingroup plugin_battery
359  *  @brief Remove an Event from the Battery.
360  */
361 void Battery::delete_event(std::shared_ptr<Event> event)
362 {
363   events_.erase(
364       std::remove_if(events_.begin(), events_.end(), [&event](std::shared_ptr<Event> e) { return event == e; }),
365       events_.end());
366 }
367 } // namespace simgrid::plugins