Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Merge branch 'udpor-phase7' into 'master'
[simgrid.git] / src / plugins / battery.cpp
1 #include <simgrid/Exception.hpp>
2 #include <simgrid/plugins/battery.hpp>
3 #include <simgrid/s4u/Actor.hpp>
4 #include <simgrid/s4u/Engine.hpp>
5 #include <simgrid/s4u/Host.hpp>
6 #include <simgrid/s4u/VirtualMachine.hpp>
7 #include <simgrid/simix.hpp>
8
9 #include "src/kernel/resource/CpuImpl.hpp"
10 #include "src/simgrid/module.hpp"
11
12 #include <boost/algorithm/string/classification.hpp>
13 #include <boost/algorithm/string/split.hpp>
14
15 SIMGRID_REGISTER_PLUGIN(battery, "Battery management", &sg_battery_plugin_init)
16
17 /** @defgroup plugin_battery plugin_battery Plugin Battery
18
19   @beginrst
20
21 This is the battery plugin, enabling management of batteries on hosts.
22 To activate this plugin, first call :cpp:func:`sg_battery_plugin_init()`.
23
24 We consider a constant energy exchange model.
25
26 Properties of batteries such as State of Charge and State of Health are lazily updated, ie., when reading values and
27 when the power is modified.
28
29 State of Charge (SoC)
30 .....................
31
32 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
33 reaches its maximal SoC. Conversely, if the power of a battery is set to a positive value then the battery will act as a
34 generator and deplete over time until it reaches its minimal SoC. When reaching either its maximal or minimal SoC it
35 will set its power to 0.
36
37 The natural depletion of batteries over time is not taken into account.
38
39 State of Health (SoH)
40 .....................
41
42 A battery starts with an energy budget :math:`E` such as:
43
44 .. math::
45
46   E = C \times U \times D \times N \times 2
47
48 Where :math:`C` is the initial capacity, :math:`U` is the ratio of usable capacity, :math:`D` is the depth of discharge
49 and :math:`N` is the number of cycles of the battery.
50
51 The SoH represents the consumption of this energy budget during the lifetime of the battery.
52 Use the battery reduces its SoH and its capacity in consequence.
53 When the SoH reaches 0, the battery becomes unusable.
54
55 .. warning::
56
57   Due to the decrease of the battery capacity with the SoH, a large usable capacity leads to very tiny battery capacity
58   when reaching low SoH. This may results in charge and discharge cycles too short to be evaluated by the simulator. To
59   avoid this situation you should not try to reach a SoH of 0 with a usable capacity set to 1.
60
61 Plotting the output of the example "battery-degradation" highlights the linear decrease of the SoH due to a continuous
62 use of the battery and the decreasing cycle duration as its capacity reduces:
63
64 .. image:: /img/battery_degradation.svg
65    :align: center
66
67 Batteries properties
68 ....................
69
70 Properties of the batteries are defined as properties of hosts in the platform XML file.
71
72 Here is an example of XML declaration:
73
74 .. code-block:: xml
75
76    <host id="Host" speed="100.0Mf" core="1">
77        <prop id="battery_active" value="1" />
78        <prop id="battery_capacity" value="10" />
79        <prop id="battery_cycles" value="200" />
80        <prop id="battery_state_of_charge" value="0.8" />
81    </host>
82
83 The different properties are:
84
85 - ``battery_active``: Set the battery as active if set to 1 (default=0)
86 - ``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
87 - ``battery_state_of_charge``: Set the initial state of charge of the battery (default=0)
88 - ``battery_state_of_charge_min``: Set the minimal state of charge of the battery (default=0.2)
89 - ``battery_state_of_charge_max``: Set the maximal state of charge of the battery (default=0.8)
90 - ``battery_capacity``: Set the initial capacity of the battery in Wh (default=0)
91 - ``battery_usable_capacity``: Set the ratio of usable capacity of the battery (default=0.8)
92 - ``battery_depth_of_discharge``: Set the depth of discharge of the battery (default=0.9)
93 - ``battery_charge_efficiency``: Set the charge efficiency of the battery (default=1)
94 - ``battery_discharge_efficiency``: Set the charge efficiency of the battery (default=1)
95 - ``battery_cycles``: Set the number of cycle of the battery (default=1)
96 - ``battery_depth_of_discharge``: Set the number of cycle of the battery (default=1)
97 - ``battery_eval_cost``: Evaluate the cost of the battery during the simulation if set to 1 (defaulf=0)
98 - ``battery_investment_cost``: Set the investment cost of the battery (default=0)
99 - ``battery_static_maintenance_cost``: Set the static maintenance cost of the battery (default=0)
100 - ``battery_variable_maintenance_cost``: Set the variable maintenance cost of the battery (default=0)
101
102   @endrst
103  */
104 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(battery, kernel, "Logging specific to the battery plugin");
105
106 namespace simgrid::plugin {
107 class Battery {
108 private:
109   simgrid::s4u::Host* host_ = nullptr;
110
111   double state_of_charge_min_  = 0.2;
112   double state_of_charge_max_  = 0.8;
113   double charge_efficiency_    = 1;
114   double discharge_efficiency_ = 1;
115   double initial_capacity_wh_  = 0;
116   double cycles_ = 1; // total complete cycles (charge + discharge) the battery can do before complete depletion.
117   double depth_of_discharge_ = 0.9;
118   double usable_capacity_    = 0.8;
119   double energy_budget_j_    = 0;
120
121   bool active_               = false;
122   double power_w_            = 0; // NEGATIVE when charging (consumes power) POSITIVE when discharging (generates power)
123   double state_of_charge_    = 0;
124   double capacity_wh_        = 0;
125   double next_event_         = -1;
126   double energy_exchanged_j_ = 0;
127   double last_updated_       = 0;
128
129   // Calculation of costs from Bei Li thesis (link :https://tel.archives-ouvertes.fr/tel-02077668/document) (chapter 4)
130   bool eval_cost_                                = false;
131   double cumulative_cost_                        = 0;
132   double investment_cost_per_wh_                 = 0;
133   double static_maintenance_cost_per_wh_times_h_ = 0;
134   double variable_maintenance_cost_per_wh_       = 0;
135
136   void init_battery_params();
137   void init_cost_params();
138   void update();
139
140   void set_state_of_charge(double soc);
141   void set_state_of_charge_min(double soc);
142   void set_state_of_charge_max(double soc);
143   void set_capacity(double c);
144   void set_initial_capacity(double c);
145   void set_cycles(int c);
146   void set_depth_of_discharge(double d);
147   void set_usable_capacity(double c);
148   void set_charge_efficiency(double e);
149   void set_discharge_efficiency(double e);
150   void set_eval_cost(bool eval);
151   void set_investment_cost(double c);
152   void set_static_maintenance_cost(double c);
153   void set_variable_maintenance_cost(double c);
154
155   bool is_charging();
156
157 public:
158   static simgrid::xbt::Extension<simgrid::s4u::Host, Battery> EXTENSION_ID;
159
160   explicit Battery(simgrid::s4u::Host* host);
161   ~Battery();
162
163   void set_state(bool state);
164   void set_power(const double power);
165
166   bool is_active() const;
167   double get_power();
168   double get_state_of_charge();
169   double get_state_of_charge_min() const;
170   double get_state_of_charge_max() const;
171   double get_state_of_health();
172   double get_capacity();
173   double get_cumulative_cost();
174   double get_next_event_date();
175 };
176
177 simgrid::xbt::Extension<simgrid::s4u::Host, Battery> Battery::EXTENSION_ID;
178
179 void Battery::set_power(const double p)
180 {
181   update();
182   xbt_assert(p == 0 or (p > 0 and state_of_charge_ > state_of_charge_min_) or
183                  (p < 0 and state_of_charge_ < state_of_charge_max_),
184              "Incoherent power and state of charge. A battery cannot charge(discharge) past its maximal(minimal) state "
185              "of charge.");
186   xbt_assert(p == 0 or energy_exchanged_j_ < energy_budget_j_, "Cannot set power of a fully used battery.");
187   simgrid::kernel::actor::simcall_answered([this, p] {
188     power_w_ = p;
189     if (power_w_ == 0) {
190       next_event_ = -1;
191       return;
192     }
193     double soc_shutdown;
194     double soh_shutdown;
195     if (power_w_ > 0) {
196       soc_shutdown = capacity_wh_ * 3600 * (state_of_charge_ - state_of_charge_min_) / (power_w_ * charge_efficiency_);
197       soh_shutdown = (energy_budget_j_ - energy_exchanged_j_) / (power_w_ * charge_efficiency_);
198     } else { // power_w_ < 0
199       soc_shutdown =
200           capacity_wh_ * 3600 * (state_of_charge_max_ - state_of_charge_) / (abs(power_w_) / discharge_efficiency_);
201       soh_shutdown = (energy_budget_j_ - energy_exchanged_j_) / (abs(power_w_) / discharge_efficiency_);
202     }
203     if (soh_shutdown <= 0)
204       next_event_ = simgrid::s4u::Engine::get_clock() + soc_shutdown;
205     else
206       next_event_ = simgrid::s4u::Engine::get_clock() + std::min(soc_shutdown, soh_shutdown);
207   });
208 }
209
210 void Battery::set_state(bool state)
211 {
212   update();
213   simgrid::kernel::actor::simcall_answered([this, state] { active_ = state; });
214 }
215
216 void Battery::set_state_of_charge(double soc)
217 {
218   xbt_assert(soc > 0 and soc <= 1, " : state of charge should be in ]0,1] (provided: %f)", soc);
219   simgrid::kernel::actor::simcall_answered([this, soc] { state_of_charge_ = soc; });
220 }
221
222 void Battery::set_state_of_charge_min(double soc)
223 {
224   xbt_assert(soc > 0 and soc <= 1 and soc < state_of_charge_max_,
225              " : state of charge min should be in ]0,1] and below state of charge max (provided: %f)", soc);
226   simgrid::kernel::actor::simcall_answered([this, soc] { state_of_charge_min_ = soc; });
227 }
228
229 void Battery::set_state_of_charge_max(double soc)
230 {
231   xbt_assert(soc > 0 and soc <= 1 and soc > state_of_charge_min_,
232              " : state of charge max should be in ]0,1] and above state of charge min (provided: %f)", soc);
233   simgrid::kernel::actor::simcall_answered([this, soc] { state_of_charge_max_ = soc; });
234 }
235
236 void Battery::set_initial_capacity(double c)
237 {
238   xbt_assert(c > 0, " : capacity should be > 0 (provided: %f)", c);
239   simgrid::kernel::actor::simcall_answered([this, c] { initial_capacity_wh_ = c; });
240 }
241
242 void Battery::set_capacity(double c)
243 {
244   xbt_assert(c > 0, " : capacity should be > 0 (provided: %f)", c);
245   simgrid::kernel::actor::simcall_answered([this, c] { capacity_wh_ = c; });
246 }
247
248 void Battery::set_cycles(int c)
249 {
250   xbt_assert(c > 0, " : cycles should be > 0 (provided: %d)", c);
251   simgrid::kernel::actor::simcall_answered([this, c] { cycles_ = c; });
252 }
253
254 void Battery::set_depth_of_discharge(double d)
255 {
256   xbt_assert(d > 0 and d <= 1, " : depth of discharge should be in ]0, 1] (provided: %f)", d);
257   simgrid::kernel::actor::simcall_answered([this, d] { depth_of_discharge_ = d; });
258 }
259
260 void Battery::set_usable_capacity(double c)
261 {
262   xbt_assert(c > 0 and c <= 1, " : usable capacity should be in ]0, 1] (provided: %f)", c);
263   simgrid::kernel::actor::simcall_answered([this, c] { usable_capacity_ = c; });
264 }
265
266 void Battery::set_charge_efficiency(double e)
267 {
268   xbt_assert(e > 0 and e <= 1, " : charge efficiency should be in [0,1] (provided: %f)", e);
269   simgrid::kernel::actor::simcall_answered([this, e] { charge_efficiency_ = e; });
270 }
271
272 void Battery::set_discharge_efficiency(double e)
273 {
274   xbt_assert(e > 0 and e <= 1, " : discharge efficiency should be in [0,1] (provided: %f)", e);
275   simgrid::kernel::actor::simcall_answered([this, e] { discharge_efficiency_ = e; });
276 }
277
278 void Battery::set_eval_cost(bool eval)
279 {
280   simgrid::kernel::actor::simcall_answered([this, eval] { eval_cost_ = eval; });
281 }
282
283 void Battery::set_investment_cost(double c)
284 {
285   xbt_assert(c >= 0, " : investment cost should be >= 0 (provided: %f)", c);
286   simgrid::kernel::actor::simcall_answered([this, c] { investment_cost_per_wh_ = c; });
287 }
288
289 void Battery::set_static_maintenance_cost(double c)
290 {
291   xbt_assert(c >= 0, " : static maintenance cost should be >= 0 (provided: %f)", c);
292   simgrid::kernel::actor::simcall_answered([this, c] { static_maintenance_cost_per_wh_times_h_ = c; });
293 }
294
295 void Battery::set_variable_maintenance_cost(double c)
296 {
297   xbt_assert(c >= 0, " : variable maintenance cost should be >= 0 (provided: %f)", c);
298   simgrid::kernel::actor::simcall_answered([this, c] { variable_maintenance_cost_per_wh_ = c; });
299 }
300
301 bool Battery::is_charging()
302 {
303   update();
304   return power_w_ < 0;
305 }
306
307 bool Battery::is_active() const
308 {
309   return active_;
310 }
311
312 double Battery::get_power()
313 {
314   update();
315   return power_w_;
316 }
317
318 double Battery::get_state_of_charge()
319 {
320   update();
321   return state_of_charge_;
322 }
323
324 double Battery::get_state_of_charge_min() const
325 {
326   return state_of_charge_min_;
327 }
328
329 double Battery::get_state_of_charge_max() const
330 {
331   return state_of_charge_max_;
332 }
333
334 double Battery::get_state_of_health()
335 {
336   update();
337   return 1 - (energy_exchanged_j_ / energy_budget_j_);
338 }
339
340 double Battery::get_capacity()
341 {
342   update();
343   return capacity_wh_;
344 }
345
346 double Battery::get_cumulative_cost()
347 {
348   update();
349   return cumulative_cost_;
350 }
351
352 double Battery::get_next_event_date()
353 {
354   update();
355   return next_event_;
356 }
357
358 void Battery::init_battery_params()
359 {
360   const char* prop_chars;
361   prop_chars = host_->get_property("battery_capacity");
362   if (prop_chars) {
363     set_capacity(xbt_str_parse_double(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str()));
364     set_initial_capacity(xbt_str_parse_double(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str()));
365   }
366   prop_chars = host_->get_property("battery_usable_capacity");
367   if (prop_chars)
368     set_usable_capacity(xbt_str_parse_double(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str()));
369   prop_chars = host_->get_property("battery_depth_of_discharge");
370   if (prop_chars)
371     set_depth_of_discharge(
372         xbt_str_parse_double(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str()));
373   prop_chars = host_->get_property("battery_cycles");
374   if (prop_chars)
375     set_cycles(xbt_str_parse_int(prop_chars, ("cannot parse int: " + std::string(prop_chars)).c_str()));
376   simgrid::kernel::actor::simcall_answered([this] {
377     energy_budget_j_ = (initial_capacity_wh_ * usable_capacity_ * depth_of_discharge_ * 3600 * cycles_ * 2);
378   });
379   prop_chars = host_->get_property("battery_active");
380   if (prop_chars)
381     set_state(xbt_str_parse_int(prop_chars, ("cannot parse int: " + std::string(prop_chars)).c_str()));
382   prop_chars = host_->get_property("battery_state_of_charge_min");
383   if (prop_chars)
384     set_state_of_charge_min(
385         xbt_str_parse_double(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str()));
386   prop_chars = host_->get_property("battery_state_of_charge_max");
387   if (prop_chars)
388     set_state_of_charge_max(
389         xbt_str_parse_double(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str()));
390   prop_chars = host_->get_property("battery_charge_efficiency");
391   if (prop_chars)
392     set_charge_efficiency(
393         xbt_str_parse_double(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str()));
394   prop_chars = host_->get_property("battery_discharge_efficiency");
395   if (prop_chars)
396     set_discharge_efficiency(
397         xbt_str_parse_double(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str()));
398   prop_chars = host_->get_property("battery_state_of_charge");
399   if (prop_chars)
400     set_state_of_charge(xbt_str_parse_double(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str()));
401   prop_chars = host_->get_property("battery_eval_cost");
402   if (prop_chars)
403     set_eval_cost(xbt_str_parse_int(prop_chars, ("cannot parse int: " + std::string(prop_chars)).c_str()));
404   prop_chars = host_->get_property("battery_investment_cost");
405   if (prop_chars)
406     set_investment_cost(xbt_str_parse_int(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str()));
407   prop_chars = host_->get_property("battery_static_maintenance_cost");
408   if (prop_chars)
409     set_static_maintenance_cost(
410         xbt_str_parse_int(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str()));
411   prop_chars = host_->get_property("battery_variable_maintenance_cost");
412   if (prop_chars)
413     set_variable_maintenance_cost(
414         xbt_str_parse_int(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str()));
415   prop_chars = host_->get_property("battery_power");
416   if (prop_chars)
417     set_power(xbt_str_parse_double(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str()));
418   simgrid::kernel::actor::simcall_answered([this] { last_updated_ = simgrid::s4u::Engine::get_clock(); });
419 }
420
421 void Battery::update()
422 {
423   simgrid::kernel::actor::simcall_answered([this] {
424     double now             = simgrid::s4u::Engine::get_clock();
425     double time_delta_real = now - last_updated_;
426     if (time_delta_real <= 0 or not is_active())
427       return;
428     double time_delta_until_event = next_event_ - last_updated_;
429     bool event                    = next_event_ != -1 and time_delta_until_event <= time_delta_real;
430     double power_real_w           = power_w_ < 0 ? power_w_ * charge_efficiency_ : power_w_ / discharge_efficiency_;
431     state_of_charge_ -= power_real_w * (event ? time_delta_until_event : time_delta_real) / (3600 * capacity_wh_);
432     energy_exchanged_j_ += (event ? time_delta_until_event : time_delta_real) * abs(power_real_w);
433     capacity_wh_ = initial_capacity_wh_ * usable_capacity_ * (1 - (energy_exchanged_j_ / energy_budget_j_)) +
434                    initial_capacity_wh_ * (1 - usable_capacity_);
435     capacity_wh_ = std::max(capacity_wh_, 0.0);
436     if (eval_cost_) {
437       double usage_cost_per_wh =
438           (investment_cost_per_wh_ / (depth_of_discharge_ * cycles_ * 2) + variable_maintenance_cost_per_wh_);
439       double usage_cost =
440           usage_cost_per_wh * abs(power_real_w) * (event ? time_delta_until_event : time_delta_real) / 3600;
441       double static_maintenance_cost =
442           static_maintenance_cost_per_wh_times_h_ * initial_capacity_wh_ * time_delta_real / 3600;
443       cumulative_cost_ += usage_cost + static_maintenance_cost;
444     }
445     if (event) {
446       power_w_    = 0;
447       next_event_ = -1;
448     }
449     last_updated_ = now;
450   });
451 }
452
453 Battery::Battery(simgrid::s4u::Host* host) : host_(host)
454 {
455   init_battery_params();
456 }
457
458 Battery::~Battery() = default;
459 } // namespace simgrid::plugin
460
461 using simgrid::plugin::Battery;
462
463 /* **************************** events  callback *************************** */
464
465 static void on_creation(simgrid::s4u::Host& host)
466 {
467   if (dynamic_cast<simgrid::s4u::VirtualMachine*>(&host)) // Ignore virtual machines
468     return;
469   host.extension_set(new Battery(&host));
470 }
471
472 /* **************************** Public interface *************************** */
473
474 static void ensure_plugin_inited()
475 {
476   if (not Battery::EXTENSION_ID.valid())
477     throw simgrid::xbt::InitializationError("The Battery plugin is not active. Please call sg_battery_plugin_init() "
478                                             "before calling any function related to that plugin.");
479 }
480
481 /** @ingroup plugin_battery
482  *  @brief Enable battery plugin.
483  */
484 void sg_battery_plugin_init()
485 {
486   if (Battery::EXTENSION_ID.valid())
487     return;
488   Battery::EXTENSION_ID = simgrid::s4u::Host::extension_create<Battery>();
489   simgrid::s4u::Host::on_creation_cb(&on_creation);
490 }
491
492 /** @ingroup plugin_battery
493  *  @param state The state to set.
494  *  @brief Set the state of the battery.
495  * A battery set to inactive (false) will neither update its state of charge nor its state of health.
496  */
497 void sg_battery_set_state(const_sg_host_t host, bool state)
498 {
499   ensure_plugin_inited();
500   host->extension<Battery>()->set_state(state);
501 }
502
503 /** @ingroup plugin_battery
504  *  @param power The power to set in W.
505  *  @brief Set the power of the battery.
506  *  @note A negative value makes the battery act as a load and charge.
507  * A positive value makes the battery act as a generator and discharge.
508  */
509 void sg_battery_set_power(const_sg_host_t host, double power)
510 {
511   ensure_plugin_inited();
512   host->extension<Battery>()->set_power(power);
513 }
514
515 /** @ingroup plugin_battery
516  *  @brief Return true if the battery is active.
517  */
518 bool sg_battery_is_active(const_sg_host_t host)
519 {
520   ensure_plugin_inited();
521   return host->extension<Battery>()->is_active();
522 }
523
524 /** @ingroup plugin_battery
525  *  @brief Return the power of the battery in W.
526  *  @note A negative value indicates that the battery act as a load and charge.
527  * A positive value indicates that the battery act as a generator and discharge.
528  */
529 double sg_battery_get_power(const_sg_host_t host)
530 {
531   ensure_plugin_inited();
532   return host->extension<Battery>()->get_power();
533 }
534
535 /** @ingroup plugin_battery
536  *  @brief Return the state of charge of the battery.
537  */
538 double sg_battery_get_state_of_charge(const_sg_host_t host)
539 {
540   ensure_plugin_inited();
541   return host->extension<Battery>()->get_state_of_charge();
542 }
543
544 /** @ingroup plugin_battery
545  *  @brief Return the minimal state of charge of the battery.
546  */
547 double sg_battery_get_state_of_charge_min(const_sg_host_t host)
548 {
549   ensure_plugin_inited();
550   return host->extension<Battery>()->get_state_of_charge_min();
551 }
552
553 /** @ingroup plugin_battery
554  *  @brief Return the maximal state of charge of the battery.
555  */
556 double sg_battery_get_state_of_charge_max(const_sg_host_t host)
557 {
558   ensure_plugin_inited();
559   return host->extension<Battery>()->get_state_of_charge_max();
560 }
561
562 /** @ingroup plugin_battery
563  *  @brief Return the state of health of the battery.
564  */
565 double sg_battery_get_state_of_health(const_sg_host_t host)
566 {
567   ensure_plugin_inited();
568   return host->extension<Battery>()->get_state_of_health();
569 }
570
571 /** @ingroup plugin_battery
572  *  @brief Return the capacity of the battery in Wh.
573  *  @note capacity is reduced by the state of health of the battery.
574  */
575 double sg_battery_get_capacity(const_sg_host_t host)
576 {
577   ensure_plugin_inited();
578   return host->extension<Battery>()->get_capacity();
579 }
580
581 /** @ingroup plugin_battery
582  *  @brief Return the cumulative cost of the battery.
583  */
584 double sg_battery_get_cumulative_cost(const_sg_host_t host)
585 {
586   ensure_plugin_inited();
587   return host->extension<Battery>()->get_cumulative_cost();
588 }
589
590 /** @ingroup plugin_battery
591  *  @brief Return the date of the next event, i.e., when the battery will be empty or full.
592  *  @note If power is null then return -1.
593  */
594 double sg_battery_get_next_event_date(const_sg_host_t host)
595 {
596   ensure_plugin_inited();
597   return host->extension<Battery>()->get_next_event_date();
598 }