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 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)
104 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(battery, kernel, "Logging specific to the battery plugin");
106 namespace simgrid::plugin {
109 simgrid::s4u::Host* host_ = nullptr;
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;
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;
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;
136 void init_battery_params();
137 void init_cost_params();
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);
158 static simgrid::xbt::Extension<simgrid::s4u::Host, Battery> EXTENSION_ID;
160 explicit Battery(simgrid::s4u::Host* host);
163 void set_state(bool state);
164 void set_power(const double power);
166 bool is_active() const;
168 double get_state_of_charge();
169 double get_state_of_charge_min();
170 double get_state_of_charge_max();
171 double get_state_of_health();
172 double get_capacity();
173 double get_cumulative_cost();
174 double get_next_event_date();
177 simgrid::xbt::Extension<simgrid::s4u::Host, Battery> Battery::EXTENSION_ID;
179 void Battery::set_power(const double p)
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 "
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] {
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
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_);
203 if (soh_shutdown <= 0)
204 next_event_ = simgrid::s4u::Engine::get_clock() + soc_shutdown;
206 next_event_ = simgrid::s4u::Engine::get_clock() + std::min(soc_shutdown, soh_shutdown);
210 void Battery::set_state(bool state)
213 simgrid::kernel::actor::simcall_answered([this, state] { active_ = state; });
216 void Battery::set_state_of_charge(double soc)
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; });
222 void Battery::set_state_of_charge_min(double soc)
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; });
229 void Battery::set_state_of_charge_max(double soc)
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; });
236 void Battery::set_initial_capacity(double c)
238 xbt_assert(c > 0, " : capacity should be > 0 (provided: %f)", c);
239 simgrid::kernel::actor::simcall_answered([this, c] { initial_capacity_wh_ = c; });
242 void Battery::set_capacity(double c)
244 xbt_assert(c > 0, " : capacity should be > 0 (provided: %f)", c);
245 simgrid::kernel::actor::simcall_answered([this, c] { capacity_wh_ = c; });
248 void Battery::set_cycles(int c)
250 xbt_assert(c > 0, " : cycles should be > 0 (provided: %d)", c);
251 simgrid::kernel::actor::simcall_answered([this, c] { cycles_ = c; });
254 void Battery::set_depth_of_discharge(double d)
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; });
260 void Battery::set_usable_capacity(double c)
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; });
266 void Battery::set_charge_efficiency(double e)
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; });
272 void Battery::set_discharge_efficiency(double e)
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; });
278 void Battery::set_eval_cost(bool eval)
280 simgrid::kernel::actor::simcall_answered([this, eval] { eval_cost_ = eval; });
283 void Battery::set_investment_cost(double c)
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; });
289 void Battery::set_static_maintenance_cost(double c)
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; });
295 void Battery::set_variable_maintenance_cost(double c)
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; });
301 bool Battery::is_charging()
307 bool Battery::is_active() const
312 double Battery::get_power()
318 double Battery::get_state_of_charge()
321 return state_of_charge_;
324 double Battery::get_state_of_charge_min()
326 return state_of_charge_min_;
329 double Battery::get_state_of_charge_max()
331 return state_of_charge_max_;
334 double Battery::get_state_of_health()
337 return 1 - (energy_exchanged_j_ / energy_budget_j_);
340 double Battery::get_capacity()
346 double Battery::get_cumulative_cost()
349 return cumulative_cost_;
352 double Battery::get_next_event_date()
358 void Battery::init_battery_params()
360 const char* prop_chars;
361 prop_chars = host_->get_property("battery_capacity");
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()));
366 prop_chars = host_->get_property("battery_usable_capacity");
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");
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");
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);
379 prop_chars = host_->get_property("battery_active");
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");
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");
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");
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");
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");
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");
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");
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");
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");
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");
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(); });
421 void Battery::update()
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())
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);
437 double usage_cost_per_wh =
438 (investment_cost_per_wh_ / (depth_of_discharge_ * cycles_ * 2) + variable_maintenance_cost_per_wh_);
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;
453 Battery::Battery(simgrid::s4u::Host* host) : host_(host)
455 init_battery_params();
458 Battery::~Battery() = default;
459 } // namespace simgrid::plugin
461 using simgrid::plugin::Battery;
463 /* **************************** events callback *************************** */
465 static void on_creation(simgrid::s4u::Host& host)
467 if (dynamic_cast<simgrid::s4u::VirtualMachine*>(&host)) // Ignore virtual machines
469 host.extension_set(new Battery(&host));
472 /* **************************** Public interface *************************** */
474 static void ensure_plugin_inited()
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.");
481 /** @ingroup plugin_battery
482 * @brief Enable battery plugin.
484 void sg_battery_plugin_init()
486 if (Battery::EXTENSION_ID.valid())
488 Battery::EXTENSION_ID = simgrid::s4u::Host::extension_create<Battery>();
489 simgrid::s4u::Host::on_creation_cb(&on_creation);
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.
497 void sg_battery_set_state(const_sg_host_t host, bool state)
499 ensure_plugin_inited();
500 host->extension<Battery>()->set_state(state);
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.
509 void sg_battery_set_power(const_sg_host_t host, double power)
511 ensure_plugin_inited();
512 host->extension<Battery>()->set_power(power);
515 /** @ingroup plugin_battery
516 * @brief Return true if the battery is active.
518 bool sg_battery_is_active(const_sg_host_t host)
520 ensure_plugin_inited();
521 return host->extension<Battery>()->is_active();
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.
529 double sg_battery_get_power(const_sg_host_t host)
531 ensure_plugin_inited();
532 return host->extension<Battery>()->get_power();
535 /** @ingroup plugin_battery
536 * @brief Return the state of charge of the battery.
538 double sg_battery_get_state_of_charge(const_sg_host_t host)
540 ensure_plugin_inited();
541 return host->extension<Battery>()->get_state_of_charge();
544 /** @ingroup plugin_battery
545 * @brief Return the minimal state of charge of the battery.
547 double sg_battery_get_state_of_charge_min(const_sg_host_t host)
549 ensure_plugin_inited();
550 return host->extension<Battery>()->get_state_of_charge_min();
553 /** @ingroup plugin_battery
554 * @brief Return the maximal state of charge of the battery.
556 double sg_battery_get_state_of_charge_max(const_sg_host_t host)
558 ensure_plugin_inited();
559 return host->extension<Battery>()->get_state_of_charge_max();
562 /** @ingroup plugin_battery
563 * @brief Return the state of health of the battery.
565 double sg_battery_get_state_of_health(const_sg_host_t host)
567 ensure_plugin_inited();
568 return host->extension<Battery>()->get_state_of_health();
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.
575 double sg_battery_get_capacity(const_sg_host_t host)
577 ensure_plugin_inited();
578 return host->extension<Battery>()->get_capacity();
581 /** @ingroup plugin_battery
582 * @brief Return the cumulative cost of the battery.
584 double sg_battery_get_cumulative_cost(const_sg_host_t host)
586 ensure_plugin_inited();
587 return host->extension<Battery>()->get_cumulative_cost();
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.
594 double sg_battery_get_next_event_date(const_sg_host_t host)
596 ensure_plugin_inited();
597 return host->extension<Battery>()->get_next_event_date();