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
26 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(Battery, kernel, "Logging specific to the battery plugin");
28 namespace simgrid::plugins {
32 BatteryModel::BatteryModel() : Model("BatteryModel") {}
34 void BatteryModel::add_battery(BatteryPtr b)
36 batteries_.push_back(b);
39 void BatteryModel::update_actions_state(double now, double delta)
41 for (auto battery : batteries_)
45 double BatteryModel::next_occurring_event(double now)
47 double time_delta = -1;
48 for (auto battery : batteries_) {
49 double time_delta_battery = battery->next_occurring_event();
50 time_delta = time_delta == -1 or time_delta_battery < time_delta ? time_delta_battery : time_delta;
57 Battery::Event::Event(double state_of_charge, Flow flow, std::function<void()> callback, bool repeat)
58 : state_of_charge_(state_of_charge), flow_(flow), callback_(callback), repeat_(repeat)
62 std::shared_ptr<Battery::Event> Battery::Event::init(double state_of_charge, Flow flow, std::function<void()> callback,
65 return std::make_shared<Battery::Event>(state_of_charge, flow, callback, repeat);
70 std::shared_ptr<BatteryModel> Battery::battery_model_;
72 void Battery::init_plugin()
74 auto model = std::make_shared<BatteryModel>();
75 simgrid::s4u::Engine::get_instance()->add_model(model);
76 Battery::battery_model_ = model;
79 void Battery::update()
81 kernel::actor::simcall_answered([this] {
82 double now = s4u::Engine::get_clock();
83 double time_delta_s = now - last_updated_;
86 if (time_delta_s <= 0)
89 // Calculate energy provided / consumed during this step
90 double provided_power_w = 0;
91 double consumed_power_w = 0;
92 for (auto const& [host, active] : host_loads_)
93 provided_power_w += active ? sg_host_get_current_consumption(host) : 0;
94 for (auto const& [name, load] : named_loads_) {
96 provided_power_w += load;
98 consumed_power_w += -load;
100 double energy_lost_delta_j = provided_power_w / discharge_efficiency_ * time_delta_s;
101 double energy_gained_delta_j = consumed_power_w * charge_efficiency_ * time_delta_s;
103 // Check that the provided/consumed energy is valid
104 energy_lost_delta_j = std::min(energy_lost_delta_j, energy_stored_j_ + energy_gained_delta_j);
105 /* Charging deteriorate the capacity, but the capacity is used to evaluate the maximum charge so
106 we need to evaluate the theorethical new capacity in the worst case when we fully charge the battery */
107 double new_tmp_capacity_wh =
108 (initial_capacity_wh_ *
109 (1 - (energy_provided_j_ + energy_lost_delta_j * discharge_efficiency_ + energy_consumed_j_ -
110 (energy_stored_j_ + energy_lost_delta_j) / charge_efficiency_) /
112 (1 + 3600 * initial_capacity_wh_ / (charge_efficiency_ * energy_budget_j_));
113 energy_gained_delta_j =
114 std::min(energy_gained_delta_j, (3600 * new_tmp_capacity_wh) - energy_stored_j_ + energy_lost_delta_j);
117 energy_provided_j_ += energy_lost_delta_j * discharge_efficiency_;
118 energy_consumed_j_ += energy_gained_delta_j / charge_efficiency_;
119 capacity_wh_ = initial_capacity_wh_ * (1 - (energy_provided_j_ + energy_consumed_j_) / energy_budget_j_);
120 energy_stored_j_ += energy_gained_delta_j - energy_lost_delta_j;
121 energy_stored_j_ = std::min(energy_stored_j_, 3600 * capacity_wh_);
124 std::vector<std::shared_ptr<Event>> to_delete = {};
125 for (auto event : events_) {
126 if (abs(event->time_delta_ - time_delta_s) < 0.000000001) {
129 event->time_delta_ = -1;
131 to_delete.push_back(event);
134 for (auto event : to_delete)
139 double Battery::next_occurring_event()
141 double provided_power_w = 0;
142 double consumed_power_w = 0;
143 for (auto const& [host, active] : host_loads_)
144 provided_power_w += active ? sg_host_get_current_consumption(host) : 0;
145 for (auto const& [name, load] : named_loads_) {
147 provided_power_w += load;
149 consumed_power_w += -load;
152 double time_delta = -1;
153 for (auto& event : events_) {
154 double lost_power_w = provided_power_w / discharge_efficiency_;
155 double gained_power_w = consumed_power_w * charge_efficiency_;
156 // Event cannot happen
157 if ((lost_power_w == gained_power_w) or (event->state_of_charge_ == energy_stored_j_ / (3600 * capacity_wh_)) or
158 (lost_power_w > gained_power_w and event->flow_ == Flow::CHARGE) or
159 (lost_power_w < gained_power_w and event->flow_ == Flow::DISCHARGE)) {
162 // Evaluate time until event happen
164 /* The time to reach a state of charge depends on the capacity, but charging and discharging deteriorate the
165 * capacity, so we need to evaluate the time considering a capacity that also depends on time
167 event->time_delta_ = (3600 * event->state_of_charge_ * initial_capacity_wh_ *
168 (1 - (energy_provided_j_ + energy_consumed_j_) / energy_budget_j_) -
170 (gained_power_w - lost_power_w +
171 3600 * event->state_of_charge_ * initial_capacity_wh_ *
172 (consumed_power_w + provided_power_w) / energy_budget_j_);
173 if ((time_delta == -1 or event->time_delta_ < time_delta) and abs(event->time_delta_) > 0.000000001)
174 time_delta = event->time_delta_;
180 Battery::Battery(const std::string& name, double state_of_charge, double charge_efficiency, double discharge_efficiency,
181 double initial_capacity_wh, int cycles, double depth_of_discharge)
183 , charge_efficiency_(charge_efficiency)
184 , discharge_efficiency_(discharge_efficiency)
185 , initial_capacity_wh_(initial_capacity_wh)
187 , depth_of_discharge_(depth_of_discharge)
188 , energy_budget_j_(initial_capacity_wh * depth_of_discharge * 3600 * cycles * 2)
189 , capacity_wh_(initial_capacity_wh)
190 , energy_stored_j_(state_of_charge * 3600 * initial_capacity_wh)
192 xbt_assert(state_of_charge >= 0 and state_of_charge <= 1, " : state of charge should be in [0, 1] (provided: %f)",
194 xbt_assert(charge_efficiency > 0 and charge_efficiency <= 1, " : charge efficiency should be in [0,1] (provided: %f)",
196 xbt_assert(discharge_efficiency > 0 and discharge_efficiency <= 1,
197 " : discharge efficiency should be in [0,1] (provided: %f)", discharge_efficiency);
198 xbt_assert(initial_capacity_wh > 0, " : initial capacity should be > 0 (provided: %f)", initial_capacity_wh);
199 xbt_assert(cycles > 0, " : cycles should be > 0 (provided: %d)", cycles);
200 xbt_assert(depth_of_discharge > 0 and depth_of_discharge <= 1,
201 " : depth of discharge should be in ]0, 1] (provided: %f)", depth_of_discharge);
204 BatteryPtr Battery::init(const std::string& name, double state_of_charge, double charge_efficiency,
205 double discharge_efficiency, double initial_capacity_wh, int cycles, double depth_of_discharge)
207 static bool plugin_inited = false;
208 if (not plugin_inited) {
210 plugin_inited = true;
212 auto battery = BatteryPtr(new Battery(name, state_of_charge, charge_efficiency, discharge_efficiency,
213 initial_capacity_wh, cycles, depth_of_discharge));
214 battery_model_->add_battery(battery);
218 void Battery::set_load(const std::string& name, double power_w)
220 named_loads_[name] = power_w;
223 void Battery::connect_host(s4u::Host* h, bool active)
225 host_loads_[h] = active;
228 double Battery::get_state_of_charge()
230 return energy_stored_j_ / (3600 * capacity_wh_);
233 double Battery::get_state_of_health()
235 return 1 - ((energy_provided_j_ + energy_consumed_j_) / energy_budget_j_);
238 double Battery::get_capacity()
243 double Battery::get_energy_provided()
245 return energy_provided_j_;
248 double Battery::get_energy_consumed()
250 return energy_consumed_j_;
253 double Battery::get_energy_stored(std::string unit)
256 return energy_stored_j_;
257 else if (unit == "Wh")
258 return energy_stored_j_ / 3600;
260 xbt_die("Invalid unit. Valid units are J (default) or Wh.");
263 std::shared_ptr<Battery::Event> Battery::create_event(double state_of_charge, Flow flow, std::function<void()> callback,
266 auto event = Event::init(state_of_charge, flow, callback, repeat);
267 events_.push_back(event);
271 std::vector<std::shared_ptr<Battery::Event>> Battery::get_events()
276 void Battery::delete_event(std::shared_ptr<Event> event)
279 std::remove_if(events_.begin(), events_.end(), [&event](std::shared_ptr<Event> e) { return event == e; }),
282 } // namespace simgrid::plugins