Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
try to fix initializer order
[simgrid.git] / src / plugins / battery.cpp
1 /* Copyright (c) 2023. The SimGrid Team. All rights reserved.          */
2
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>
12 #include <xbt/log.h>
13
14 #include "src/kernel/resource/CpuImpl.hpp"
15 #include "src/simgrid/module.hpp"
16
17 SIMGRID_REGISTER_PLUGIN(battery, "Battery management", nullptr)
18 /** @defgroup plugin_battery plugin_battery Plugin Battery
19
20   @beginrst
21
22 This is the battery plugin
23
24   @endrst
25  */
26 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(Battery, kernel, "Logging specific to the battery plugin");
27
28 namespace simgrid::plugins {
29
30 /* BatteryModel */
31
32 BatteryModel::BatteryModel() : Model("BatteryModel") {}
33
34 void BatteryModel::add_battery(BatteryPtr b)
35 {
36   batteries_.push_back(b);
37 }
38
39 void BatteryModel::update_actions_state(double now, double delta)
40 {
41   for (auto battery : batteries_)
42     battery->update();
43 }
44
45 double BatteryModel::next_occurring_event(double now)
46 {
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;
51   }
52   return time_delta;
53 }
54
55 /* Event */
56
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)
59 {
60 }
61
62 std::shared_ptr<Battery::Event> Battery::Event::init(double state_of_charge, Flow flow, std::function<void()> callback,
63                                                      bool repeat)
64 {
65   return std::make_shared<Battery::Event>(state_of_charge, flow, callback, repeat);
66 }
67
68 /* Battery */
69
70 std::shared_ptr<BatteryModel> Battery::battery_model_;
71
72 void Battery::init_plugin()
73 {
74   auto model = std::make_shared<BatteryModel>();
75   simgrid::s4u::Engine::get_instance()->add_model(model);
76   Battery::battery_model_ = model;
77 }
78
79 void Battery::update()
80 {
81   kernel::actor::simcall_answered([this] {
82     double now          = s4u::Engine::get_clock();
83     double time_delta_s = now - last_updated_;
84
85     // Nothing to update
86     if (time_delta_s <= 0)
87       return;
88
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_) {
95       if (load > 0)
96         provided_power_w += load;
97       else
98         consumed_power_w += -load;
99     }
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;
102
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_) /
111                   energy_budget_j_)) /
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);
115
116     // Updating battery
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_);
122     last_updated_    = now;
123
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) {
127         event->callback_();
128         if (event->repeat_)
129           event->time_delta_ = -1;
130         else
131           to_delete.push_back(event);
132       }
133     }
134     for (auto event : to_delete)
135       delete_event(event);
136   });
137 }
138
139 double Battery::next_occurring_event()
140 {
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_) {
146     if (load > 0)
147       provided_power_w += load;
148     else
149       consumed_power_w += -load;
150   }
151
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)) {
160       continue;
161     }
162     // Evaluate time until event happen
163     else {
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
166        */
167       event->time_delta_ = (3600 * event->state_of_charge_ * initial_capacity_wh_ *
168                                 (1 - (energy_provided_j_ + energy_consumed_j_) / energy_budget_j_) -
169                             energy_stored_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_;
175     }
176   }
177   return time_delta;
178 }
179
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)
182     : name_(name)
183     , charge_efficiency_(charge_efficiency)
184     , discharge_efficiency_(discharge_efficiency)
185     , initial_capacity_wh_(initial_capacity_wh)
186     , cycles_(cycles)
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)
191 {
192   xbt_assert(state_of_charge >= 0 and state_of_charge <= 1, " : state of charge should be in [0, 1] (provided: %f)",
193              state_of_charge);
194   xbt_assert(charge_efficiency > 0 and charge_efficiency <= 1, " : charge efficiency should be in [0,1] (provided: %f)",
195              charge_efficiency);
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);
202 }
203
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)
206 {
207   static bool plugin_inited = false;
208   if (not plugin_inited) {
209     init_plugin();
210     plugin_inited = true;
211   }
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);
215   return battery;
216 }
217
218 void Battery::set_load(const std::string& name, double power_w)
219 {
220   named_loads_[name] = power_w;
221 }
222
223 void Battery::connect_host(s4u::Host* h, bool active)
224 {
225   host_loads_[h] = active;
226 }
227
228 double Battery::get_state_of_charge()
229 {
230   return energy_stored_j_ / (3600 * capacity_wh_);
231 }
232
233 double Battery::get_state_of_health()
234 {
235   return 1 - ((energy_provided_j_ + energy_consumed_j_) / energy_budget_j_);
236 }
237
238 double Battery::get_capacity()
239 {
240   return capacity_wh_;
241 }
242
243 double Battery::get_energy_provided()
244 {
245   return energy_provided_j_;
246 }
247
248 double Battery::get_energy_consumed()
249 {
250   return energy_consumed_j_;
251 }
252
253 double Battery::get_energy_stored(std::string unit)
254 {
255   if (unit == "J")
256     return energy_stored_j_;
257   else if (unit == "Wh")
258     return energy_stored_j_ / 3600;
259   else
260     xbt_die("Invalid unit. Valid units are J (default) or Wh.");
261 }
262
263 std::shared_ptr<Battery::Event> Battery::create_event(double state_of_charge, Flow flow, std::function<void()> callback,
264                                                       bool repeat)
265 {
266   auto event = Event::init(state_of_charge, flow, callback, repeat);
267   events_.push_back(event);
268   return event;
269 }
270
271 std::vector<std::shared_ptr<Battery::Event>> Battery::get_events()
272 {
273   return events_;
274 }
275
276 void Battery::delete_event(std::shared_ptr<Event> event)
277 {
278   events_.erase(
279       std::remove_if(events_.begin(), events_.end(), [&event](std::shared_ptr<Event> e) { return event == e; }),
280       events_.end());
281 }
282 } // namespace simgrid::plugins