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 N \times 2
39 Where :math:`C` is the initial capacity and :math:`N` is the number of cycles of the battery.
41 The SoH represents the consumption of this energy budget during the lifetime of the battery.
42 Use the battery reduces its SoH and its capacity in consequence.
43 When the SoH reaches 0, the battery becomes unusable.
45 Plotting the output of the example "battery-degradation" highlights the linear decrease of the SoH due to a continuous
46 use of the battery and the decreasing cycle duration as its capacity reduces:
48 .. image:: /img/battery_degradation.svg
53 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(Battery, kernel, "Logging specific to the battery plugin");
55 namespace simgrid::plugins {
59 BatteryModel::BatteryModel() : Model("BatteryModel") {}
61 void BatteryModel::add_battery(BatteryPtr b)
63 batteries_.push_back(b);
66 void BatteryModel::update_actions_state(double now, double delta)
68 for (auto battery : batteries_)
72 double BatteryModel::next_occurring_event(double now)
74 double time_delta = -1;
75 for (auto battery : batteries_) {
76 double time_delta_battery = battery->next_occurring_event();
77 time_delta = time_delta == -1 or time_delta_battery < time_delta ? time_delta_battery : time_delta;
84 Battery::Event::Event(double state_of_charge, Flow flow, std::function<void()> callback, bool repeat)
85 : state_of_charge_(state_of_charge), flow_(flow), callback_(callback), repeat_(repeat)
89 std::shared_ptr<Battery::Event> Battery::Event::init(double state_of_charge, Flow flow, std::function<void()> callback,
92 return std::make_shared<Battery::Event>(state_of_charge, flow, callback, repeat);
97 std::shared_ptr<BatteryModel> Battery::battery_model_;
99 void Battery::init_plugin()
101 auto model = std::make_shared<BatteryModel>();
102 simgrid::s4u::Engine::get_instance()->add_model(model);
103 Battery::battery_model_ = model;
106 void Battery::update()
108 kernel::actor::simcall_answered([this] {
109 double now = s4u::Engine::get_clock();
110 double time_delta_s = now - last_updated_;
113 if (time_delta_s <= 0)
116 // Calculate energy provided / consumed during this step
117 double provided_power_w = 0;
118 double consumed_power_w = 0;
119 for (auto const& [host, active] : host_loads_)
120 provided_power_w += active ? sg_host_get_current_consumption(host) : 0;
121 for (auto const& [name, load] : named_loads_) {
123 provided_power_w += load;
125 consumed_power_w += -load;
127 double energy_lost_delta_j = provided_power_w / discharge_efficiency_ * time_delta_s;
128 double energy_gained_delta_j = consumed_power_w * charge_efficiency_ * time_delta_s;
130 // Check that the provided/consumed energy is valid
131 energy_lost_delta_j = std::min(energy_lost_delta_j, energy_stored_j_ + energy_gained_delta_j);
132 /* Charging deteriorate the capacity, but the capacity is used to evaluate the maximum charge so
133 we need to evaluate the theorethical new capacity in the worst case when we fully charge the battery */
134 double new_tmp_capacity_wh =
135 (initial_capacity_wh_ *
136 (1 - (energy_provided_j_ + energy_lost_delta_j * discharge_efficiency_ + energy_consumed_j_ -
137 (energy_stored_j_ + energy_lost_delta_j) / charge_efficiency_) /
139 (1 + 3600 * initial_capacity_wh_ / (charge_efficiency_ * energy_budget_j_));
140 energy_gained_delta_j =
141 std::min(energy_gained_delta_j, (3600 * new_tmp_capacity_wh) - energy_stored_j_ + energy_lost_delta_j);
144 energy_provided_j_ += energy_lost_delta_j * discharge_efficiency_;
145 energy_consumed_j_ += energy_gained_delta_j / charge_efficiency_;
146 capacity_wh_ = initial_capacity_wh_ * (1 - (energy_provided_j_ + energy_consumed_j_) / energy_budget_j_);
147 energy_stored_j_ += energy_gained_delta_j - energy_lost_delta_j;
148 energy_stored_j_ = std::min(energy_stored_j_, 3600 * capacity_wh_);
151 std::vector<std::shared_ptr<Event>> to_delete = {};
152 for (auto event : events_) {
153 if (abs(event->time_delta_ - time_delta_s) < 0.000000001) {
156 event->time_delta_ = -1;
158 to_delete.push_back(event);
161 for (auto event : to_delete)
166 double Battery::next_occurring_event()
168 double provided_power_w = 0;
169 double consumed_power_w = 0;
170 for (auto const& [host, active] : host_loads_)
171 provided_power_w += active ? sg_host_get_current_consumption(host) : 0;
172 for (auto const& [name, load] : named_loads_) {
174 provided_power_w += load;
176 consumed_power_w += -load;
179 double time_delta = -1;
180 for (auto& event : events_) {
181 double lost_power_w = provided_power_w / discharge_efficiency_;
182 double gained_power_w = consumed_power_w * charge_efficiency_;
183 // Event cannot happen
184 if ((lost_power_w == gained_power_w) or (event->state_of_charge_ == energy_stored_j_ / (3600 * capacity_wh_)) or
185 (lost_power_w > gained_power_w and event->flow_ == Flow::CHARGE) or
186 (lost_power_w < gained_power_w and event->flow_ == Flow::DISCHARGE)) {
189 // Evaluate time until event happen
191 /* The time to reach a state of charge depends on the capacity, but charging and discharging deteriorate the
192 * capacity, so we need to evaluate the time considering a capacity that also depends on time
194 event->time_delta_ = (3600 * event->state_of_charge_ * initial_capacity_wh_ *
195 (1 - (energy_provided_j_ + energy_consumed_j_) / energy_budget_j_) -
197 (gained_power_w - lost_power_w +
198 3600 * event->state_of_charge_ * initial_capacity_wh_ *
199 (consumed_power_w + provided_power_w) / energy_budget_j_);
200 if ((time_delta == -1 or event->time_delta_ < time_delta) and abs(event->time_delta_) > 0.000000001)
201 time_delta = event->time_delta_;
207 Battery::Battery(const std::string& name, double state_of_charge, double charge_efficiency, double discharge_efficiency,
208 double initial_capacity_wh, int cycles)
210 , charge_efficiency_(charge_efficiency)
211 , discharge_efficiency_(discharge_efficiency)
212 , initial_capacity_wh_(initial_capacity_wh)
213 , energy_budget_j_(initial_capacity_wh * 3600 * cycles * 2)
214 , capacity_wh_(initial_capacity_wh)
215 , energy_stored_j_(state_of_charge * 3600 * initial_capacity_wh)
217 xbt_assert(state_of_charge >= 0 and state_of_charge <= 1, " : state of charge should be in [0, 1] (provided: %f)",
219 xbt_assert(charge_efficiency > 0 and charge_efficiency <= 1, " : charge efficiency should be in [0,1] (provided: %f)",
221 xbt_assert(discharge_efficiency > 0 and discharge_efficiency <= 1,
222 " : discharge efficiency should be in [0,1] (provided: %f)", discharge_efficiency);
223 xbt_assert(initial_capacity_wh > 0, " : initial capacity should be > 0 (provided: %f)", initial_capacity_wh);
224 xbt_assert(cycles > 0, " : cycles should be > 0 (provided: %d)", cycles);
227 /** @ingroup plugin_battery
228 * @param name The name of the Battery.
229 * @param state_of_charge The initial state of charge of the Battery [0,1].
230 * @param charge_efficiency The charge efficiency of the Battery [0,1].
231 * @param discharge_efficiency The discharge efficiency of the Battery [0,1].
232 * @param initial_capacity_wh The initial capacity of the Battery in Wh (>0).
233 * @param cycles The number of charge-discharge cycles until complete depletion of the Battery capacity.
234 * @return A BatteryPtr pointing to the new Battery.
236 BatteryPtr Battery::init(const std::string& name, double state_of_charge, double charge_efficiency,
237 double discharge_efficiency, double initial_capacity_wh, int cycles)
239 static bool plugin_inited = false;
240 if (not plugin_inited) {
242 plugin_inited = true;
244 auto battery = BatteryPtr(new Battery(name, state_of_charge, charge_efficiency, discharge_efficiency,
245 initial_capacity_wh, cycles));
246 battery_model_->add_battery(battery);
250 /** @ingroup plugin_battery
251 * @param name The name of the load
252 * @param power_w Power of the load in W. A positive value discharges the Battery while a negative value charges it.
254 void Battery::set_load(const std::string& name, double power_w)
256 named_loads_[name] = power_w;
259 /** @ingroup plugin_battery
260 * @param h The Host to connect.
261 * @param active Status of the connected Host (default true).
262 * @brief Connect a Host to the Battery with the status active. As long as the status is true the Host takes its energy
263 from the Battery. To modify this status connect again the same Host with a different status.
264 @warning Do NOT connect the same Host to multiple Batteries with the status true at the same time.
265 In this case all Batteries would have the full consumption from this Host.
267 void Battery::connect_host(s4u::Host* host, bool active)
269 host_loads_[host] = active;
272 /** @ingroup plugin_battery
273 * @return The state of charge of the battery.
275 double Battery::get_state_of_charge()
277 return energy_stored_j_ / (3600 * capacity_wh_);
280 /** @ingroup plugin_battery
281 * @return The state of health of the Battery.
283 double Battery::get_state_of_health()
285 return 1 - ((energy_provided_j_ + energy_consumed_j_) / energy_budget_j_);
288 /** @ingroup plugin_battery
289 * @return The current capacity of the Battery.
291 double Battery::get_capacity()
296 /** @ingroup plugin_battery
297 * @return The energy provided by the Battery.
298 * @note It is the energy provided from an external point of view, after application of the discharge efficiency.
299 It means that the Battery lost more energy than it has provided.
301 double Battery::get_energy_provided()
303 return energy_provided_j_;
306 /** @ingroup plugin_battery
307 * @return The energy consumed by the Battery.
308 * @note It is the energy consumed from an external point of view, before application of the charge efficiency.
309 It means that the Battery consumed more energy than is has absorbed.
311 double Battery::get_energy_consumed()
313 return energy_consumed_j_;
316 /** @ingroup plugin_battery
317 * @param Unit Valid units are J (default) and Wh.
318 * @return Energy stored in the Battery.
320 double Battery::get_energy_stored(std::string unit)
323 return energy_stored_j_;
324 else if (unit == "Wh")
325 return energy_stored_j_ / 3600;
327 xbt_die("Invalid unit. Valid units are J (default) or Wh.");
330 /** @ingroup plugin_battery
331 * @brief Create a new Event.
332 * @param state_of_charge The state of charge at which the Event will happen.
333 * @param flow The flow in which the Event will happen, either when the Battery is charging or discharging.
334 * @param callback The callable to trigger when the Event happen.
335 * @param repeat If the Event is a recurrent Event or a single Event.
336 * @return A shared pointer of the new Event.
338 std::shared_ptr<Battery::Event> Battery::create_event(double state_of_charge, Flow flow, std::function<void()> callback,
341 auto event = Event::init(state_of_charge, flow, callback, repeat);
342 events_.push_back(event);
346 /** @ingroup plugin_battery
347 * @return A vector containing the Events associated to the Battery.
349 std::vector<std::shared_ptr<Battery::Event>> Battery::get_events()
354 /** @ingroup plugin_battery
355 * @brief Remove an Event from the Battery.
357 void Battery::delete_event(std::shared_ptr<Event> event)
360 std::remove_if(events_.begin(), events_.end(), [&event](std::shared_ptr<Event> e) { return event == e; }),
363 } // namespace simgrid::plugins