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>
9 #include "src/kernel/resource/CpuImpl.hpp"
10 #include "src/simgrid/module.hpp"
12 #include <boost/algorithm/string/classification.hpp>
13 #include <boost/algorithm/string/split.hpp>
15 SIMGRID_REGISTER_PLUGIN(battery, "Battery management", &sg_battery_plugin_init)
17 /** @defgroup plugin_battery plugin_battery Plugin Battery
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()`.
24 We consider a constant energy exchange model.
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.
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.
37 The natural depletion of batteries over time is not taken into account.
42 A battery starts with an energy budget :math:`E` such as:
46 E = C \times U \times D \times N \times 2
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.
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.
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.
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:
64 .. image:: /img/battery_degradation.svg
70 Properties of the batteries are defined as properties of hosts in the platform XML file.
72 Here is an example of XML declaration:
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" />
83 The different properties are:
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)
105 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(battery, kernel, "Logging specific to the battery plugin");
107 namespace simgrid::plugin {
110 simgrid::s4u::Host* host_ = nullptr;
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;
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;
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;
137 void init_battery_params();
138 void init_cost_params();
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);
159 static simgrid::xbt::Extension<simgrid::s4u::Host, Battery> EXTENSION_ID;
161 explicit Battery(simgrid::s4u::Host* host);
164 void set_state(bool state);
165 void set_power(const double power);
167 bool is_active() const;
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();
178 simgrid::xbt::Extension<simgrid::s4u::Host, Battery> Battery::EXTENSION_ID;
180 void Battery::set_power(const double p)
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 "
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] {
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
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_);
204 if (soh_shutdown <= 0)
205 next_event_ = simgrid::s4u::Engine::get_clock() + soc_shutdown;
207 next_event_ = simgrid::s4u::Engine::get_clock() + std::min(soc_shutdown, soh_shutdown);
211 void Battery::set_state(bool state)
214 simgrid::kernel::actor::simcall_answered([this, state] { active_ = state; });
217 void Battery::set_state_of_charge(double soc)
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; });
223 void Battery::set_state_of_charge_min(double soc)
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; });
230 void Battery::set_state_of_charge_max(double soc)
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; });
237 void Battery::set_initial_capacity(double c)
239 xbt_assert(c > 0, " : capacity should be > 0 (provided: %f)", c);
240 simgrid::kernel::actor::simcall_answered([this, c] { initial_capacity_wh_ = c; });
243 void Battery::set_capacity(double c)
245 xbt_assert(c > 0, " : capacity should be > 0 (provided: %f)", c);
246 simgrid::kernel::actor::simcall_answered([this, c] { capacity_wh_ = c; });
249 void Battery::set_cycles(int c)
251 xbt_assert(c > 0, " : cycles should be > 0 (provided: %d)", c);
252 simgrid::kernel::actor::simcall_answered([this, c] { cycles_ = c; });
255 void Battery::set_depth_of_discharge(double d)
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; });
261 void Battery::set_usable_capacity(double c)
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; });
267 void Battery::set_charge_efficiency(double e)
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; });
273 void Battery::set_discharge_efficiency(double e)
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; });
279 void Battery::set_eval_cost(bool eval)
281 simgrid::kernel::actor::simcall_answered([this, eval] { eval_cost_ = eval; });
284 void Battery::set_investment_cost(double c)
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; });
290 void Battery::set_static_maintenance_cost(double c)
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; });
296 void Battery::set_variable_maintenance_cost(double c)
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; });
302 bool Battery::is_charging()
308 bool Battery::is_active() const
313 double Battery::get_power()
319 double Battery::get_state_of_charge()
322 return state_of_charge_;
325 double Battery::get_state_of_charge_min()
327 return state_of_charge_min_;
330 double Battery::get_state_of_charge_max()
332 return state_of_charge_max_;
335 double Battery::get_state_of_health()
338 return 1 - (energy_exchanged_j_ / energy_budget_j_);
341 double Battery::get_capacity()
347 double Battery::get_cumulative_cost()
350 return cumulative_cost_;
353 double Battery::get_next_event_date()
359 void Battery::init_battery_params()
361 const char* prop_chars;
362 prop_chars = host_->get_property("battery_capacity");
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()));
367 prop_chars = host_->get_property("battery_usable_capacity");
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");
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");
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);
380 prop_chars = host_->get_property("battery_active");
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");
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");
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");
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");
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");
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");
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");
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");
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");
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");
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(); });
422 void Battery::update()
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())
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);
438 double usage_cost_per_wh =
439 (investment_cost_per_wh_ / (depth_of_discharge_ * cycles_ * 2) + variable_maintenance_cost_per_wh_);
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;
454 Battery::Battery(simgrid::s4u::Host* host) : host_(host)
456 init_battery_params();
459 Battery::~Battery() = default;
460 } // namespace simgrid::plugin
462 using simgrid::plugin::Battery;
464 /* **************************** events callback *************************** */
466 static void on_creation(simgrid::s4u::Host& host)
468 if (dynamic_cast<simgrid::s4u::VirtualMachine*>(&host)) // Ignore virtual machines
470 host.extension_set(new Battery(&host));
473 /* **************************** Public interface *************************** */
475 static void ensure_plugin_inited()
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.");
482 /** @ingroup plugin_battery
483 * @brief Enable battery plugin.
485 void sg_battery_plugin_init()
487 if (Battery::EXTENSION_ID.valid())
489 Battery::EXTENSION_ID = simgrid::s4u::Host::extension_create<Battery>();
490 simgrid::s4u::Host::on_creation_cb(&on_creation);
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.
498 void sg_battery_set_state(const_sg_host_t host, bool state)
500 ensure_plugin_inited();
501 host->extension<Battery>()->set_state(state);
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.
510 void sg_battery_set_power(const_sg_host_t host, double power)
512 ensure_plugin_inited();
513 host->extension<Battery>()->set_power(power);
516 /** @ingroup plugin_battery
517 * @brief Return true if the battery is active.
519 bool sg_battery_is_active(const_sg_host_t host)
521 ensure_plugin_inited();
522 return host->extension<Battery>()->is_active();
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.
530 double sg_battery_get_power(const_sg_host_t host)
532 ensure_plugin_inited();
533 return host->extension<Battery>()->get_power();
536 /** @ingroup plugin_battery
537 * @brief Return the state of charge of the battery.
539 double sg_battery_get_state_of_charge(const_sg_host_t host)
541 ensure_plugin_inited();
542 return host->extension<Battery>()->get_state_of_charge();
545 /** @ingroup plugin_battery
546 * @brief Return the minimal state of charge of the battery.
548 double sg_battery_get_state_of_charge_min(const_sg_host_t host)
550 ensure_plugin_inited();
551 return host->extension<Battery>()->get_state_of_charge_min();
554 /** @ingroup plugin_battery
555 * @brief Return the maximal state of charge of the battery.
557 double sg_battery_get_state_of_charge_max(const_sg_host_t host)
559 ensure_plugin_inited();
560 return host->extension<Battery>()->get_state_of_charge_max();
563 /** @ingroup plugin_battery
564 * @brief Return the state of health of the battery.
566 double sg_battery_get_state_of_health(const_sg_host_t host)
568 ensure_plugin_inited();
569 return host->extension<Battery>()->get_state_of_health();
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.
576 double sg_battery_get_capacity(const_sg_host_t host)
578 ensure_plugin_inited();
579 return host->extension<Battery>()->get_capacity();
582 /** @ingroup plugin_battery
583 * @brief Return the cumulative cost of the battery.
585 double sg_battery_get_cumulative_cost(const_sg_host_t host)
587 ensure_plugin_inited();
588 return host->extension<Battery>()->get_cumulative_cost();
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.
595 double sg_battery_get_next_event_date(const_sg_host_t host)
597 ensure_plugin_inited();
598 return host->extension<Battery>()->get_next_event_date();