1 /* Copyright (c) 2023. The SimGrid Team. All rights reserved. */
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>
14 #include "src/kernel/resource/CpuImpl.hpp"
15 #include "src/simgrid/module.hpp"
17 SIMGRID_REGISTER_PLUGIN(battery, "Battery management", nullptr)
18 /** @defgroup plugin_battery plugin_battery Plugin Battery
22 This is the battery plugin, enabling management of batteries.
24 With this plugin you can:
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
31 The natural depletion of batteries over time is not taken into account.
33 A battery starts with an energy budget :math:`E` such as:
37 E = C \times D \times N \times 2
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.
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.
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:
49 .. image:: /img/battery_degradation.svg
54 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(Battery, kernel, "Logging specific to the battery plugin");
56 namespace simgrid::plugins {
60 BatteryModel::BatteryModel() : Model("BatteryModel") {}
62 void BatteryModel::add_battery(BatteryPtr b)
64 batteries_.push_back(b);
67 void BatteryModel::update_actions_state(double now, double delta)
69 for (auto battery : batteries_)
73 double BatteryModel::next_occurring_event(double now)
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;
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)
90 std::shared_ptr<Battery::Event> Battery::Event::init(double state_of_charge, Flow flow, std::function<void()> callback,
93 return std::make_shared<Battery::Event>(state_of_charge, flow, callback, repeat);
98 std::shared_ptr<BatteryModel> Battery::battery_model_;
100 void Battery::init_plugin()
102 auto model = std::make_shared<BatteryModel>();
103 simgrid::s4u::Engine::get_instance()->add_model(model);
104 Battery::battery_model_ = model;
107 void Battery::update()
109 kernel::actor::simcall_answered([this] {
110 double now = s4u::Engine::get_clock();
111 double time_delta_s = now - last_updated_;
114 if (time_delta_s <= 0)
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_) {
124 provided_power_w += load;
126 consumed_power_w += -load;
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;
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_) /
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);
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_);
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) {
157 event->time_delta_ = -1;
159 to_delete.push_back(event);
162 for (auto event : to_delete)
167 double Battery::next_occurring_event()
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_) {
175 provided_power_w += load;
177 consumed_power_w += -load;
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)) {
190 // Evaluate time until event happen
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
195 event->time_delta_ = (3600 * event->state_of_charge_ * initial_capacity_wh_ *
196 (1 - (energy_provided_j_ + energy_consumed_j_) / energy_budget_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_;
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)
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)
218 xbt_assert(state_of_charge >= 0 and state_of_charge <= 1, " : state of charge should be in [0, 1] (provided: %f)",
220 xbt_assert(charge_efficiency > 0 and charge_efficiency <= 1, " : charge efficiency should be in [0,1] (provided: %f)",
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);
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.
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)
243 static bool plugin_inited = false;
244 if (not plugin_inited) {
246 plugin_inited = true;
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);
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.
258 void Battery::set_load(const std::string& name, double power_w)
260 named_loads_[name] = power_w;
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.
271 void Battery::connect_host(s4u::Host* host, bool active)
273 host_loads_[host] = active;
276 /** @ingroup plugin_battery
277 * @return The state of charge of the battery.
279 double Battery::get_state_of_charge()
281 return energy_stored_j_ / (3600 * capacity_wh_);
284 /** @ingroup plugin_battery
285 * @return The state of health of the Battery.
287 double Battery::get_state_of_health()
289 return 1 - ((energy_provided_j_ + energy_consumed_j_) / energy_budget_j_);
292 /** @ingroup plugin_battery
293 * @return The current capacity of the Battery.
295 double Battery::get_capacity()
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.
305 double Battery::get_energy_provided()
307 return energy_provided_j_;
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.
315 double Battery::get_energy_consumed()
317 return energy_consumed_j_;
320 /** @ingroup plugin_battery
321 * @param Unit Valid units are J (default) and Wh.
322 * @return Energy stored in the Battery.
324 double Battery::get_energy_stored(std::string unit)
327 return energy_stored_j_;
328 else if (unit == "Wh")
329 return energy_stored_j_ / 3600;
331 xbt_die("Invalid unit. Valid units are J (default) or Wh.");
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.
342 std::shared_ptr<Battery::Event> Battery::create_event(double state_of_charge, Flow flow, std::function<void()> callback,
345 auto event = Event::init(state_of_charge, flow, callback, repeat);
346 events_.push_back(event);
350 /** @ingroup plugin_battery
351 * @return A vector containing the Events associated to the Battery.
353 std::vector<std::shared_ptr<Battery::Event>> Battery::get_events()
358 /** @ingroup plugin_battery
359 * @brief Remove an Event from the Battery.
361 void Battery::delete_event(std::shared_ptr<Event> event)
364 std::remove_if(events_.begin(), events_.end(), [&event](std::shared_ptr<Event> e) { return event == e; }),
367 } // namespace simgrid::plugins