Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Merge branch 'master' of https://framagit.org/simgrid/simgrid
[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
87 act as a load and charge. A positive value indicates that the battery act as a generator and discharge
88 - ``battery_state_of_charge``: Set the initial state of charge of the battery (default=0)
89 - ``battery_state_of_charge_min``: Set the minimal state of charge of the battery (default=0.2)
90 - ``battery_state_of_charge_max``: Set the maximal state of charge of the battery (default=0.8)
91 - ``battery_capacity``: Set the initial capacity of the battery in Wh (default=0)
92 - ``battery_usable_capacity``: Set the ratio of usable capacity of the battery (default=0.8)
93 - ``battery_depth_of_discharge``: Set the depth of discharge of the battery (default=0.9)
94 - ``battery_charge_efficiency``: Set the charge efficiency of the battery (default=1)
95 - ``battery_discharge_efficiency``: Set the charge efficiency of the battery (default=1)
96 - ``battery_cycles``: Set the number of cycle of the battery (default=1)
97 - ``battery_depth_of_discharge``: Set the number of cycle of the battery (default=1)
98 - ``battery_eval_cost``: Evaluate the cost of the battery during the simulation if set to 1 (defaulf=0)
99 - ``battery_investment_cost``: Set the investment cost of the battery (default=0)
100 - ``battery_static_maintenance_cost``: Set the static maintenance cost of the battery (default=0)
101 - ``battery_variable_maintenance_cost``: Set the variable maintenance cost of the battery (default=0)
102
103   @endrst
104  */
105 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(battery, kernel, "Logging specific to the battery plugin");
106
107 namespace simgrid::plugin {
108 class Battery {
109 private:
110   simgrid::s4u::Host* host_ = nullptr;
111
112   double state_of_charge_min_  = 0.2;
113   double state_of_charge_max_  = 0.8;
114   double charge_efficiency_    = 1;
115   double discharge_efficiency_ = 1;
116   double initial_capacity_wh_  = 0;
117   double cycles_ = 1; // total complete cycles (charge + discharge) the battery can do before complete depletion.
118   double depth_of_discharge_ = 0.9;
119   double usable_capacity_    = 0.8;
120   double energy_budget_j_    = 0;
121
122   bool active_               = false;
123   double power_w_            = 0; // NEGATIVE when charging (consumes power) POSITIVE when discharging (generates power)
124   double state_of_charge_    = 0;
125   double capacity_wh_        = 0;
126   double next_event_         = -1;
127   double energy_exchanged_j_ = 0;
128   double last_updated_       = 0;
129
130   // Calculation of costs from Bei Li thesis (link :https://tel.archives-ouvertes.fr/tel-02077668/document) (chapter 4)
131   bool eval_cost_                                = false;
132   double cumulative_cost_                        = 0;
133   double investment_cost_per_wh_                 = 0;
134   double static_maintenance_cost_per_wh_times_h_ = 0;
135   double variable_maintenance_cost_per_wh_       = 0;
136
137   void init_battery_params();
138   void init_cost_params();
139   void update();
140
141   void set_state_of_charge(double soc);
142   void set_state_of_charge_min(double soc);
143   void set_state_of_charge_max(double soc);
144   void set_capacity(double c);
145   void set_initial_capacity(double c);
146   void set_cycles(int c);
147   void set_depth_of_discharge(double d);
148   void set_usable_capacity(double c);
149   void set_charge_efficiency(double e);
150   void set_discharge_efficiency(double e);
151   void set_eval_cost(bool eval);
152   void set_investment_cost(double c);
153   void set_static_maintenance_cost(double c);
154   void set_variable_maintenance_cost(double c);
155
156   bool is_charging();
157
158 public:
159   static simgrid::xbt::Extension<simgrid::s4u::Host, Battery> EXTENSION_ID;
160
161   explicit Battery(simgrid::s4u::Host* host);
162   ~Battery();
163
164   void set_state(bool state);
165   void set_power(const double power);
166
167   bool is_active() const;
168   double get_power();
169   double get_state_of_charge();
170   double get_state_of_charge_min();
171   double get_state_of_charge_max();
172   double get_state_of_health();
173   double get_capacity();
174   double get_cumulative_cost();
175   double get_next_event_date();
176 };
177
178 simgrid::xbt::Extension<simgrid::s4u::Host, Battery> Battery::EXTENSION_ID;
179
180 void Battery::set_power(const double p)
181 {
182   update();
183   xbt_assert(p == 0 or (p > 0 and state_of_charge_ > state_of_charge_min_) or
184                  (p < 0 and state_of_charge_ < state_of_charge_max_),
185              "Incoherent power and state of charge. A battery cannot charge(discharge) past its maximal(minimal) state "
186              "of charge.");
187   xbt_assert(p == 0 or energy_exchanged_j_ < energy_budget_j_, "Cannot set power of a fully used battery.");
188   simgrid::kernel::actor::simcall_answered([this, p] {
189     power_w_ = p;
190     if (power_w_ == 0) {
191       next_event_ = -1;
192       return;
193     }
194     double soc_shutdown;
195     double soh_shutdown;
196     if (power_w_ > 0) {
197       soc_shutdown = capacity_wh_ * 3600 * (state_of_charge_ - state_of_charge_min_) / (power_w_ * charge_efficiency_);
198       soh_shutdown = (energy_budget_j_ - energy_exchanged_j_) / (power_w_ * charge_efficiency_);
199     } else { // power_w_ < 0
200       soc_shutdown =
201           capacity_wh_ * 3600 * (state_of_charge_max_ - state_of_charge_) / (abs(power_w_) / discharge_efficiency_);
202       soh_shutdown = (energy_budget_j_ - energy_exchanged_j_) / (abs(power_w_) / discharge_efficiency_);
203     }
204     if (soh_shutdown <= 0)
205       next_event_ = simgrid::s4u::Engine::get_clock() + soc_shutdown;
206     else
207       next_event_ = simgrid::s4u::Engine::get_clock() + std::min(soc_shutdown, soh_shutdown);
208   });
209 }
210
211 void Battery::set_state(bool state)
212 {
213   update();
214   simgrid::kernel::actor::simcall_answered([this, state] { active_ = state; });
215 }
216
217 void Battery::set_state_of_charge(double soc)
218 {
219   xbt_assert(soc > 0 and soc <= 1, " : state of charge should be in ]0,1] (provided: %f)", soc);
220   simgrid::kernel::actor::simcall_answered([this, soc] { state_of_charge_ = soc; });
221 }
222
223 void Battery::set_state_of_charge_min(double soc)
224 {
225   xbt_assert(soc > 0 and soc <= 1 and soc < state_of_charge_max_,
226              " : state of charge min should be in ]0,1] and below state of charge max (provided: %f)", soc);
227   simgrid::kernel::actor::simcall_answered([this, soc] { state_of_charge_min_ = soc; });
228 }
229
230 void Battery::set_state_of_charge_max(double soc)
231 {
232   xbt_assert(soc > 0 and soc <= 1 and soc > state_of_charge_min_,
233              " : state of charge max should be in ]0,1] and above state of charge min (provided: %f)", soc);
234   simgrid::kernel::actor::simcall_answered([this, soc] { state_of_charge_max_ = soc; });
235 }
236
237 void Battery::set_initial_capacity(double c)
238 {
239   xbt_assert(c > 0, " : capacity should be > 0 (provided: %f)", c);
240   simgrid::kernel::actor::simcall_answered([this, c] { initial_capacity_wh_ = c; });
241 }
242
243 void Battery::set_capacity(double c)
244 {
245   xbt_assert(c > 0, " : capacity should be > 0 (provided: %f)", c);
246   simgrid::kernel::actor::simcall_answered([this, c] { capacity_wh_ = c; });
247 }
248
249 void Battery::set_cycles(int c)
250 {
251   xbt_assert(c > 0, " : cycles should be > 0 (provided: %d)", c);
252   simgrid::kernel::actor::simcall_answered([this, c] { cycles_ = c; });
253 }
254
255 void Battery::set_depth_of_discharge(double d)
256 {
257   xbt_assert(d > 0 and d <= 1, " : depth of discharge should be in ]0, 1] (provided: %f)", d);
258   simgrid::kernel::actor::simcall_answered([this, d] { depth_of_discharge_ = d; });
259 }
260
261 void Battery::set_usable_capacity(double c)
262 {
263   xbt_assert(c > 0 and c <= 1, " : usable capacity should be in ]0, 1] (provided: %f)", c);
264   simgrid::kernel::actor::simcall_answered([this, c] { usable_capacity_ = c; });
265 }
266
267 void Battery::set_charge_efficiency(double e)
268 {
269   xbt_assert(e > 0 and e <= 1, " : charge efficiency should be in [0,1] (provided: %f)", e);
270   simgrid::kernel::actor::simcall_answered([this, e] { charge_efficiency_ = e; });
271 }
272
273 void Battery::set_discharge_efficiency(double e)
274 {
275   xbt_assert(e > 0 and e <= 1, " : discharge efficiency should be in [0,1] (provided: %f)", e);
276   simgrid::kernel::actor::simcall_answered([this, e] { discharge_efficiency_ = e; });
277 }
278
279 void Battery::set_eval_cost(bool eval)
280 {
281   simgrid::kernel::actor::simcall_answered([this, eval] { eval_cost_ = eval; });
282 }
283
284 void Battery::set_investment_cost(double c)
285 {
286   xbt_assert(c >= 0, " : investment cost should be >= 0 (provided: %f)", c);
287   simgrid::kernel::actor::simcall_answered([this, c] { investment_cost_per_wh_ = c; });
288 }
289
290 void Battery::set_static_maintenance_cost(double c)
291 {
292   xbt_assert(c >= 0, " : static maintenance cost should be >= 0 (provided: %f)", c);
293   simgrid::kernel::actor::simcall_answered([this, c] { static_maintenance_cost_per_wh_times_h_ = c; });
294 }
295
296 void Battery::set_variable_maintenance_cost(double c)
297 {
298   xbt_assert(c >= 0, " : variable maintenance cost should be >= 0 (provided: %f)", c);
299   simgrid::kernel::actor::simcall_answered([this, c] { variable_maintenance_cost_per_wh_ = c; });
300 }
301
302 bool Battery::is_charging()
303 {
304   update();
305   return power_w_ < 0;
306 }
307
308 bool Battery::is_active() const
309 {
310   return active_;
311 }
312
313 double Battery::get_power()
314 {
315   update();
316   return power_w_;
317 }
318
319 double Battery::get_state_of_charge()
320 {
321   update();
322   return state_of_charge_;
323 }
324
325 double Battery::get_state_of_charge_min()
326 {
327   return state_of_charge_min_;
328 }
329
330 double Battery::get_state_of_charge_max()
331 {
332   return state_of_charge_max_;
333 }
334
335 double Battery::get_state_of_health()
336 {
337   update();
338   return 1 - (energy_exchanged_j_ / energy_budget_j_);
339 }
340
341 double Battery::get_capacity()
342 {
343   update();
344   return capacity_wh_;
345 }
346
347 double Battery::get_cumulative_cost()
348 {
349   update();
350   return cumulative_cost_;
351 }
352
353 double Battery::get_next_event_date()
354 {
355   update();
356   return next_event_;
357 }
358
359 void Battery::init_battery_params()
360 {
361   const char* prop_chars;
362   prop_chars = host_->get_property("battery_capacity");
363   if (prop_chars) {
364     set_capacity(xbt_str_parse_double(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str()));
365     set_initial_capacity(xbt_str_parse_double(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str()));
366   }
367   prop_chars = host_->get_property("battery_usable_capacity");
368   if (prop_chars)
369     set_usable_capacity(xbt_str_parse_double(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str()));
370   prop_chars = host_->get_property("battery_depth_of_discharge");
371   if (prop_chars)
372     set_depth_of_discharge(
373         xbt_str_parse_double(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str()));
374   prop_chars = host_->get_property("battery_cycles");
375   if (prop_chars)
376     set_cycles(xbt_str_parse_int(prop_chars, ("cannot parse int: " + std::string(prop_chars)).c_str()));
377   simgrid::kernel::actor::simcall_answered([this] {
378     energy_budget_j_ = (initial_capacity_wh_ * usable_capacity_ * depth_of_discharge_ * 3600 * cycles_ * 2);
379   });
380   prop_chars = host_->get_property("battery_active");
381   if (prop_chars)
382     set_state(xbt_str_parse_int(prop_chars, ("cannot parse int: " + std::string(prop_chars)).c_str()));
383   prop_chars = host_->get_property("battery_state_of_charge_min");
384   if (prop_chars)
385     set_state_of_charge_min(
386         xbt_str_parse_double(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str()));
387   prop_chars = host_->get_property("battery_state_of_charge_max");
388   if (prop_chars)
389     set_state_of_charge_max(
390         xbt_str_parse_double(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str()));
391   prop_chars = host_->get_property("battery_charge_efficiency");
392   if (prop_chars)
393     set_charge_efficiency(
394         xbt_str_parse_double(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str()));
395   prop_chars = host_->get_property("battery_discharge_efficiency");
396   if (prop_chars)
397     set_discharge_efficiency(
398         xbt_str_parse_double(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str()));
399   prop_chars = host_->get_property("battery_state_of_charge");
400   if (prop_chars)
401     set_state_of_charge(xbt_str_parse_double(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str()));
402   prop_chars = host_->get_property("battery_eval_cost");
403   if (prop_chars)
404     set_eval_cost(xbt_str_parse_int(prop_chars, ("cannot parse int: " + std::string(prop_chars)).c_str()));
405   prop_chars = host_->get_property("battery_investment_cost");
406   if (prop_chars)
407     set_investment_cost(xbt_str_parse_int(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str()));
408   prop_chars = host_->get_property("battery_static_maintenance_cost");
409   if (prop_chars)
410     set_static_maintenance_cost(
411         xbt_str_parse_int(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str()));
412   prop_chars = host_->get_property("battery_variable_maintenance_cost");
413   if (prop_chars)
414     set_variable_maintenance_cost(
415         xbt_str_parse_int(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str()));
416   prop_chars = host_->get_property("battery_power");
417   if (prop_chars)
418     set_power(xbt_str_parse_double(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str()));
419   simgrid::kernel::actor::simcall_answered([this] { last_updated_ = simgrid::s4u::Engine::get_clock(); });
420 }
421
422 void Battery::update()
423 {
424   simgrid::kernel::actor::simcall_answered([this] {
425     double now             = simgrid::s4u::Engine::get_clock();
426     double time_delta_real = now - last_updated_;
427     if (time_delta_real <= 0 or not is_active())
428       return;
429     double time_delta_until_event = next_event_ - last_updated_;
430     bool event                    = next_event_ != -1 and time_delta_until_event <= time_delta_real;
431     double power_real_w           = power_w_ < 0 ? power_w_ * charge_efficiency_ : power_w_ / discharge_efficiency_;
432     state_of_charge_ -= power_real_w * (event ? time_delta_until_event : time_delta_real) / (3600 * capacity_wh_);
433     energy_exchanged_j_ += (event ? time_delta_until_event : time_delta_real) * abs(power_real_w);
434     capacity_wh_ = initial_capacity_wh_ * usable_capacity_ * (1 - (energy_exchanged_j_ / energy_budget_j_)) +
435                    initial_capacity_wh_ * (1 - usable_capacity_);
436     capacity_wh_ = std::max(capacity_wh_, 0.0);
437     if (eval_cost_) {
438       double usage_cost_per_wh =
439           (investment_cost_per_wh_ / (depth_of_discharge_ * cycles_ * 2) + variable_maintenance_cost_per_wh_);
440       double usage_cost =
441           usage_cost_per_wh * abs(power_real_w) * (event ? time_delta_until_event : time_delta_real) / 3600;
442       double static_maintenance_cost =
443           static_maintenance_cost_per_wh_times_h_ * initial_capacity_wh_ * time_delta_real / 3600;
444       cumulative_cost_ += usage_cost + static_maintenance_cost;
445     }
446     if (event) {
447       power_w_    = 0;
448       next_event_ = -1;
449     }
450     last_updated_ = now;
451   });
452 }
453
454 Battery::Battery(simgrid::s4u::Host* host) : host_(host)
455 {
456   init_battery_params();
457 }
458
459 Battery::~Battery() = default;
460 } // namespace simgrid::plugin
461
462 using simgrid::plugin::Battery;
463
464 /* **************************** events  callback *************************** */
465
466 static void on_creation(simgrid::s4u::Host& host)
467 {
468   if (dynamic_cast<simgrid::s4u::VirtualMachine*>(&host)) // Ignore virtual machines
469     return;
470   host.extension_set(new Battery(&host));
471 }
472
473 /* **************************** Public interface *************************** */
474
475 static void ensure_plugin_inited()
476 {
477   if (not Battery::EXTENSION_ID.valid())
478     throw simgrid::xbt::InitializationError("The Battery plugin is not active. Please call sg_battery_plugin_init() "
479                                             "before calling any function related to that plugin.");
480 }
481
482 /** @ingroup plugin_battery
483  *  @brief Enable battery plugin.
484  */
485 void sg_battery_plugin_init()
486 {
487   if (Battery::EXTENSION_ID.valid())
488     return;
489   Battery::EXTENSION_ID = simgrid::s4u::Host::extension_create<Battery>();
490   simgrid::s4u::Host::on_creation_cb(&on_creation);
491 }
492
493 /** @ingroup plugin_battery
494  *  @param state The state to set.
495  *  @brief Set the state of the battery.
496  * A battery set to inactive (false) will neither update its state of charge nor its state of health.
497  */
498 void sg_battery_set_state(const_sg_host_t host, bool state)
499 {
500   ensure_plugin_inited();
501   host->extension<Battery>()->set_state(state);
502 }
503
504 /** @ingroup plugin_battery
505  *  @param power The power to set in W.
506  *  @brief Set the power of the battery.
507  *  @note A negative value makes the battery act as a load and charge.
508  * A positive value makes the battery act as a generator and discharge.
509  */
510 void sg_battery_set_power(const_sg_host_t host, double power)
511 {
512   ensure_plugin_inited();
513   host->extension<Battery>()->set_power(power);
514 }
515
516 /** @ingroup plugin_battery
517  *  @brief Return true if the battery is active.
518  */
519 bool sg_battery_is_active(const_sg_host_t host)
520 {
521   ensure_plugin_inited();
522   return host->extension<Battery>()->is_active();
523 }
524
525 /** @ingroup plugin_battery
526  *  @brief Return the power of the battery in W.
527  *  @note A negative value indicates that the battery act as a load and charge.
528  * A positive value indicates that the battery act as a generator and discharge.
529  */
530 double sg_battery_get_power(const_sg_host_t host)
531 {
532   ensure_plugin_inited();
533   return host->extension<Battery>()->get_power();
534 }
535
536 /** @ingroup plugin_battery
537  *  @brief Return the state of charge of the battery.
538  */
539 double sg_battery_get_state_of_charge(const_sg_host_t host)
540 {
541   ensure_plugin_inited();
542   return host->extension<Battery>()->get_state_of_charge();
543 }
544
545 /** @ingroup plugin_battery
546  *  @brief Return the minimal state of charge of the battery.
547  */
548 double sg_battery_get_state_of_charge_min(const_sg_host_t host)
549 {
550   ensure_plugin_inited();
551   return host->extension<Battery>()->get_state_of_charge_min();
552 }
553
554 /** @ingroup plugin_battery
555  *  @brief Return the maximal state of charge of the battery.
556  */
557 double sg_battery_get_state_of_charge_max(const_sg_host_t host)
558 {
559   ensure_plugin_inited();
560   return host->extension<Battery>()->get_state_of_charge_max();
561 }
562
563 /** @ingroup plugin_battery
564  *  @brief Return the state of health of the battery.
565  */
566 double sg_battery_get_state_of_health(const_sg_host_t host)
567 {
568   ensure_plugin_inited();
569   return host->extension<Battery>()->get_state_of_health();
570 }
571
572 /** @ingroup plugin_battery
573  *  @brief Return the capacity of the battery in Wh.
574  *  @note capacity is reduced by the state of health of the battery.
575  */
576 double sg_battery_get_capacity(const_sg_host_t host)
577 {
578   ensure_plugin_inited();
579   return host->extension<Battery>()->get_capacity();
580 }
581
582 /** @ingroup plugin_battery
583  *  @brief Return the cumulative cost of the battery.
584  */
585 double sg_battery_get_cumulative_cost(const_sg_host_t host)
586 {
587   ensure_plugin_inited();
588   return host->extension<Battery>()->get_cumulative_cost();
589 }
590
591 /** @ingroup plugin_battery
592  *  @brief Return the date of the next event, i.e., when the battery will be empty or full.
593  *  @note If power is null then return -1.
594  */
595 double sg_battery_get_next_event_date(const_sg_host_t host)
596 {
597   ensure_plugin_inited();
598   return host->extension<Battery>()->get_next_event_date();
599 }