From: Adrien Gougeon Date: Wed, 28 Jun 2023 15:32:58 +0000 (+0200) Subject: Complete revamp of battery plugin X-Git-Tag: v3.35~113^2~8 X-Git-Url: http://bilbo.iut-bm.univ-fcomte.fr/pub/gitweb/simgrid.git/commitdiff_plain/e6f111b95343b45e14c154574210ef9a448369f5 Complete revamp of battery plugin --- diff --git a/MANIFEST.in b/MANIFEST.in index 3896bde51f..85b0de18a2 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1807,7 +1807,6 @@ include examples/README.rst include examples/c/CMakeLists.txt include examples/cpp/CMakeLists.txt include examples/platforms/CMakeLists.txt -include examples/platforms/battery_platform.xml include examples/platforms/bypassRoute.xml include examples/platforms/bypassZoneRoute.xml include examples/platforms/cloud.xml diff --git a/docs/source/img/battery_degradation.svg b/docs/source/img/battery_degradation.svg index 630744ba83..a887272992 100644 --- a/docs/source/img/battery_degradation.svg +++ b/docs/source/img/battery_degradation.svg @@ -6,7 +6,7 @@ - 2023-03-30T14:42:09.493393 + 2023-06-28T16:42:29.585725 image/svg+xml @@ -40,13 +40,13 @@ z - + - + - + - + - + - + - + - + - + - + - + - + + + + + + + + + + + + + + + + + + + + @@ -376,14 +425,14 @@ z - - + + - - - + + + - - - - - - - - - - - - - - + - + - + - + - + - - - - + - + - + - + + - + - + @@ -635,497 +637,702 @@ z - + +" clip-path="url(#p2cce2f2c75)" style="fill: none; stroke: #dd8452; stroke-width: 1.5; stroke-linecap: round"/> @@ -1307,7 +1514,7 @@ L 992.865937 116.787969 - + diff --git a/examples/cpp/battery-degradation/s4u-battery-degradation.cpp b/examples/cpp/battery-degradation/s4u-battery-degradation.cpp index 3a56ff01fe..829efeff02 100644 --- a/examples/cpp/battery-degradation/s4u-battery-degradation.cpp +++ b/examples/cpp/battery-degradation/s4u-battery-degradation.cpp @@ -5,20 +5,39 @@ #include "simgrid/plugins/battery.hpp" #include "simgrid/s4u.hpp" +#include +#include XBT_LOG_NEW_DEFAULT_CATEGORY(battery_degradation, "Messages specific for this s4u example"); static void manager() { - const auto* battery = simgrid::s4u::this_actor::get_host(); - double power = 100; - while (sg_battery_get_state_of_health(battery) > 0) { - XBT_INFO("%f,%f,SoC", simgrid::s4u::Engine::get_clock(), sg_battery_get_state_of_charge(battery)); - XBT_INFO("%f,%f,SoH", simgrid::s4u::Engine::get_clock(), sg_battery_get_state_of_health(battery)); - sg_battery_set_power(battery, power); - simgrid::s4u::this_actor::sleep_until(sg_battery_get_next_event_date(battery)); - power *= -1; - } + auto battery = simgrid::plugins::Battery::init("Battery", 0.8, 0.9, 0.9, 10, 100, 0.9); + + battery->set_load("load", 100); + + auto event1 = battery->create_event( + 0.2, simgrid::plugins::Battery::DISCHARGE, + [battery]() { + XBT_INFO("%f,%f,SoC", simgrid::s4u::Engine::get_clock(), battery->get_state_of_charge()); + XBT_INFO("%f,%f,SoH", simgrid::s4u::Engine::get_clock(), battery->get_state_of_health()); + battery->set_load("load", -100); + }, + true); + + std::shared_ptr event2; + event2 = battery->create_event( + 0.8, simgrid::plugins::Battery::CHARGE, + [battery, event1, event2]() { + XBT_INFO("%f,%f,SoC", simgrid::s4u::Engine::get_clock(), battery->get_state_of_charge()); + XBT_INFO("%f,%f,SoH", simgrid::s4u::Engine::get_clock(), battery->get_state_of_health()); + if (battery->get_state_of_health() < 0.1) { + battery->delete_event(event1); + battery->delete_event(event2); + } + battery->set_load("load", 100); + }, + true); } int main(int argc, char* argv[]) @@ -26,9 +45,8 @@ int main(int argc, char* argv[]) simgrid::s4u::Engine e(&argc, argv); e.load_platform(argv[1]); - sg_battery_plugin_init(); + simgrid::s4u::Actor::create("manager", e.host_by_name("MyHost1"), manager); - simgrid::s4u::Actor::create("manager", e.get_all_hosts()[0], manager); e.run(); return 0; } diff --git a/examples/cpp/battery-degradation/s4u-battery-degradation.tesh b/examples/cpp/battery-degradation/s4u-battery-degradation.tesh index 876bd7a923..5af472879c 100644 --- a/examples/cpp/battery-degradation/s4u-battery-degradation.tesh +++ b/examples/cpp/battery-degradation/s4u-battery-degradation.tesh @@ -2,4 +2,4 @@ ! output ignore -$ ${bindir:=.}/s4u-battery-degradation ${platfdir}/battery_platform.xml +$ ${bindir:=.}/s4u-battery-degradation ${platfdir}/energy_platform.xml diff --git a/examples/cpp/battery-energy/s4u-battery-energy.cpp b/examples/cpp/battery-energy/s4u-battery-energy.cpp index 558cbc263f..09645ca754 100644 --- a/examples/cpp/battery-energy/s4u-battery-energy.cpp +++ b/examples/cpp/battery-energy/s4u-battery-energy.cpp @@ -6,68 +6,57 @@ #include "simgrid/plugins/battery.hpp" #include "simgrid/plugins/energy.h" #include "simgrid/s4u.hpp" - -#include +#include +#include XBT_LOG_NEW_DEFAULT_CATEGORY(battery_energy, "Messages specific for this s4u example"); static void manager() { - const auto* battery = simgrid::s4u::Engine::get_instance()->host_by_name("battery"); - auto* host1 = simgrid::s4u::Engine::get_instance()->host_by_name("host1"); - auto* host2 = simgrid::s4u::Engine::get_instance()->host_by_name("host2"); - - XBT_INFO("Initial Battery: SoC: %f SoH: %f Capacity (Total): %fWh Capacity (Usable): %fWh P: %fW", - sg_battery_get_state_of_charge(battery), sg_battery_get_state_of_health(battery), - sg_battery_get_capacity(battery), - sg_battery_get_capacity(battery) * - (sg_battery_get_state_of_charge_max(battery) - sg_battery_get_state_of_charge_min(battery)), - sg_battery_get_power(battery)); - - // Start execs on each host - simgrid::s4u::ExecPtr exec1 = simgrid::s4u::Exec::init(); - exec1->set_flops_amount(1e10); - exec1->set_host(host1); - exec1->start(); - simgrid::s4u::ExecPtr exec2 = simgrid::s4u::Exec::init(); - exec2->set_flops_amount(1e10); - exec2->set_host(host2); - exec2->start(); - // Set power generation from the battery - double total_power_w = - sg_host_get_wattmax_at(host1, host1->get_pstate()) + sg_host_get_wattmax_at(host2, host2->get_pstate()); - sg_battery_set_power(battery, total_power_w); - XBT_INFO("Battery power set to: %fW (host1) + %fW (host2)", sg_host_get_wattmax_at(host1, host1->get_pstate()), - sg_host_get_wattmax_at(host2, host2->get_pstate())); - double end = sg_battery_get_next_event_date(battery); - XBT_INFO("The battery will be depleted at: %f", end); - XBT_INFO("Exec1 will be finished in: %f", exec1->get_remaining() / host1->get_speed()); - XBT_INFO("Exec2 will be finished in: %f", exec2->get_remaining() / host2->get_speed()); - simgrid::s4u::this_actor::sleep_until(end); - - // Battery depleted - XBT_INFO("Battery depleted: SoC: %f SoH: %f P: %fW", sg_battery_get_state_of_charge(battery), - sg_battery_get_state_of_health(battery), sg_battery_get_power(battery)); - double energy_battery = sg_host_get_consumed_energy(host1) + sg_host_get_consumed_energy(host2); - XBT_INFO("Pursuing with power from the grid until both execs are finished"); - simgrid::s4u::this_actor::sleep_for( - std::max(exec1->get_remaining() / host1->get_speed(), exec2->get_remaining() / host2->get_speed())); - - // Execs finished - double energy_grid = sg_host_get_consumed_energy(host1) + sg_host_get_consumed_energy(host2) - energy_battery; - XBT_INFO("Energy consumed: Battery: %fJ (%fWh) Grid: %fJ (%fWh)", energy_battery, energy_battery / 3600, energy_grid, - energy_grid / 3600); + auto battery = simgrid::plugins::Battery::init("Battery", 0.8, 0.9, 0.9, 10, 1000, 0.9); + + auto* host1 = simgrid::s4u::Engine::get_instance()->host_by_name("MyHost1"); + auto* host2 = simgrid::s4u::Engine::get_instance()->host_by_name("MyHost2"); + auto* host3 = simgrid::s4u::Engine::get_instance()->host_by_name("MyHost3"); + + battery->create_event(0.2, simgrid::plugins::Battery::DISCHARGE, [battery, &host1, &host2, &host3]() { + XBT_INFO("Event -> Battery low: SoC: %f SoH: %f Energy stored: %fJ Energy provided: %fJ Energy consumed %fJ", + battery->get_state_of_charge(), battery->get_state_of_health(), battery->get_energy_stored(), + battery->get_energy_provided(), battery->get_energy_consumed()); + XBT_INFO("Disconnecting hosts %s and %s", host1->get_cname(), host2->get_cname()); + battery->connect_host(host1, false); + battery->connect_host(host2, false); + XBT_INFO("Energy consumed this far by: %s: %fJ, %s: %fJ, %s: %fJ", host1->get_cname(), + sg_host_get_consumed_energy(host1), host2->get_cname(), sg_host_get_consumed_energy(host2), + host3->get_cname(), sg_host_get_consumed_energy(host3)); + }); + + XBT_INFO("Battery state: SoC: %f SoH: %f Energy stored: %fJ Energy provided: %fJ Energy consumed %fJ", + battery->get_state_of_charge(), battery->get_state_of_health(), battery->get_energy_stored(), + battery->get_energy_provided(), battery->get_energy_consumed()); + XBT_INFO("Connecting hosts %s and %s to the battery", host1->get_cname(), host2->get_cname()); + battery->connect_host(host1); + battery->connect_host(host2); + + double flops = 1e9; + XBT_INFO("Host %s will now execute %f flops", host1->get_cname(), flops); + host1->execute(flops); + + simgrid::s4u::this_actor::sleep_until(200); + XBT_INFO("Battery state: SoC: %f SoH: %f Energy stored: %fJ Energy provided: %fJ Energy consumed %fJ", + battery->get_state_of_charge(), battery->get_state_of_health(), battery->get_energy_stored(), + battery->get_energy_provided(), battery->get_energy_consumed()); } int main(int argc, char* argv[]) { simgrid::s4u::Engine e(&argc, argv); + // if you plan to use Battery::connect_host you have to init the energy plugin at start. + sg_host_energy_plugin_init(); e.load_platform(argv[1]); - sg_host_energy_plugin_init(); - sg_battery_plugin_init(); + simgrid::s4u::Actor::create("manager", e.host_by_name("MyHost1"), manager); - simgrid::s4u::Actor::create("manager", e.host_by_name("host1"), manager); e.run(); return 0; } diff --git a/examples/cpp/battery-energy/s4u-battery-energy.tesh b/examples/cpp/battery-energy/s4u-battery-energy.tesh index e0ecf5533a..43094f468a 100644 --- a/examples/cpp/battery-energy/s4u-battery-energy.tesh +++ b/examples/cpp/battery-energy/s4u-battery-energy.tesh @@ -1,15 +1,14 @@ #!/usr/bin/env tesh -$ ${bindir:=.}/s4u-battery-energy ${platfdir}/battery_platform.xml -> [host1:manager:(1) 0.000000] [battery_energy/INFO] Initial Battery: SoC: 0.800000 SoH: 1.000000 Capacity (Total): 10.000000Wh Capacity (Usable): 6.000000Wh P: 0.000000W -> [host1:manager:(1) 0.000000] [battery_energy/INFO] Battery power set to: 200.000000W (host1) + 170.000000W (host2) -> [host1:manager:(1) 0.000000] [battery_energy/INFO] The battery will be depleted at: 58.378378 -> [host1:manager:(1) 0.000000] [battery_energy/INFO] Exec1 will be finished in: 100.000000 -> [host1:manager:(1) 0.000000] [battery_energy/INFO] Exec2 will be finished in: 200.000000 -> [host1:manager:(1) 58.378378] [battery_energy/INFO] Battery depleted: SoC: 0.200000 SoH: 0.995833 P: 0.000000W -> [host1:manager:(1) 58.378378] [battery_energy/INFO] Pursuing with power from the grid until both execs are finished -> [host1:manager:(1) 200.000000] [battery_energy/INFO] Energy consumed: Battery: 21600.000000J (6.000000Wh) Grid: 42400.000000J (11.777778Wh) -> [200.000000] [host_energy/INFO] Total energy consumption: 64000.000000 Joules (used hosts: 64000.000000 Joules; unused/idle hosts: 0.000000) -> [200.000000] [host_energy/INFO] Energy consumption of host battery: 0.000000 Joules -> [200.000000] [host_energy/INFO] Energy consumption of host host1: 30000.000000 Joules -> [200.000000] [host_energy/INFO] Energy consumption of host host2: 34000.000000 Joules +$ ${bindir:=.}/s4u-battery-energy ${platfdir}/energy_platform.xml +> [MyHost1:manager:(1) 0.000000] [battery_energy/INFO] Battery state: SoC: 0.800000 SoH: 1.000000 Energy stored: 28800.000000J Energy provided: 0.000000J Energy consumed 0.000000J +> [MyHost1:manager:(1) 0.000000] [battery_energy/INFO] Connecting hosts MyHost1 and MyHost2 to the battery +> [MyHost1:manager:(1) 0.000000] [battery_energy/INFO] Host MyHost1 will now execute 1000000000.000000 flops +> [96.209721] [battery_energy/INFO] Event -> Battery low: SoC: 0.200000 SoH: 0.999700 Energy stored: 7197.839784J Energy provided: 19441.944194J Energy consumed 0.000000J +> [96.209721] [battery_energy/INFO] Disconnecting hosts MyHost1 and MyHost2 +> [96.209721] [battery_energy/INFO] Energy consumed this far by: MyHost1: 9820.972097J, MyHost2: 9620.972097J, MyHost3: 9620.972097J +> [MyHost1:manager:(1) 200.000000] [battery_energy/INFO] Battery state: SoC: 0.200000 SoH: 0.999700 Energy stored: 7197.839784J Energy provided: 19441.944194J Energy consumed 0.000000J +> [200.000000] [host_energy/INFO] Total energy consumption: 60200.000000 Joules (used hosts: 20200.000000 Joules; unused/idle hosts: 40000.000000) +> [200.000000] [host_energy/INFO] Energy consumption of host MyHost1: 20200.000000 Joules +> [200.000000] [host_energy/INFO] Energy consumption of host MyHost2: 20000.000000 Joules +> [200.000000] [host_energy/INFO] Energy consumption of host MyHost3: 20000.000000 Joules diff --git a/examples/cpp/battery-simple/s4u-battery-simple.cpp b/examples/cpp/battery-simple/s4u-battery-simple.cpp index 03e9a5705c..a4993702dd 100644 --- a/examples/cpp/battery-simple/s4u-battery-simple.cpp +++ b/examples/cpp/battery-simple/s4u-battery-simple.cpp @@ -5,37 +5,40 @@ #include "simgrid/plugins/battery.hpp" #include "simgrid/s4u.hpp" +#include XBT_LOG_NEW_DEFAULT_CATEGORY(battery_simple, "Messages specific for this s4u example"); -static void manager() -{ - const auto* battery = simgrid::s4u::Engine::get_instance()->host_by_name("battery"); - double consumption_w = 200; - - XBT_INFO("Initial Battery: SoC: %f SoH: %f Capacity (Total): %fWh Capacity (Usable): %fWh P: %f", - sg_battery_get_state_of_charge(battery), sg_battery_get_state_of_health(battery), - sg_battery_get_capacity(battery), - sg_battery_get_capacity(battery) * - (sg_battery_get_state_of_charge_max(battery) - sg_battery_get_state_of_charge_min(battery)), - sg_battery_get_power(battery)); - double start = simgrid::s4u::Engine::get_clock(); - sg_battery_set_power(battery, consumption_w); - XBT_INFO("Battery power set to: %fW", consumption_w); - double end = sg_battery_get_next_event_date(battery); - XBT_INFO("The battery will be depleted at: %f", end); - simgrid::s4u::this_actor::sleep_until(end); - XBT_INFO("Energy consumed : %fJ (%fWh)", (end - start) * consumption_w, (end - start) * consumption_w / 3600); -} - int main(int argc, char* argv[]) { simgrid::s4u::Engine e(&argc, argv); e.load_platform(argv[1]); - sg_battery_plugin_init(); + auto battery = simgrid::plugins::Battery::init("Battery", 0.8, 0.9, 0.9, 10, 1000, 0.9); + + XBT_INFO("Initial state: SoC: %f SoH: %f Energy stored: %fJ Energy provided: %fJ Energy consumed %fJ", + battery->get_state_of_charge(), battery->get_state_of_health(), battery->get_energy_stored(), + battery->get_energy_provided(), battery->get_energy_consumed()); + + double load_w = 100; + battery->set_load("load", load_w); + XBT_INFO("Set load to %fW", load_w); + + battery->create_event(0.2, simgrid::plugins::Battery::DISCHARGE, [battery, &load_w]() { + XBT_INFO("Discharged state: SoC: %f SoH: %f Energy stored: %fJ Energy provided: %fJ Energy consumed %fJ", + battery->get_state_of_charge(), battery->get_state_of_health(), battery->get_energy_stored(), + battery->get_energy_provided(), battery->get_energy_consumed()); + battery->set_load("load", -load_w); + XBT_INFO("Set load to %fW", -load_w); + }); + + battery->create_event(0.8, simgrid::plugins::Battery::CHARGE, [battery]() { + XBT_INFO("Charged state: SoC: %f SoH: %f Energy stored: %fJ Energy provided: %fJ Energy consumed %fJ", + battery->get_state_of_charge(), battery->get_state_of_health(), battery->get_energy_stored(), + battery->get_energy_provided(), battery->get_energy_consumed()); + XBT_INFO("Set load to %fW", 0.0); + }); - simgrid::s4u::Actor::create("manager", e.host_by_name("host1"), manager); e.run(); return 0; -} +} \ No newline at end of file diff --git a/examples/cpp/battery-simple/s4u-battery-simple.tesh b/examples/cpp/battery-simple/s4u-battery-simple.tesh index b2691454e9..8870651c41 100644 --- a/examples/cpp/battery-simple/s4u-battery-simple.tesh +++ b/examples/cpp/battery-simple/s4u-battery-simple.tesh @@ -1,7 +1,9 @@ #!/usr/bin/env tesh -$ ${bindir:=.}/s4u-battery-simple ${platfdir}/battery_platform.xml -> [host1:manager:(1) 0.000000] [battery_simple/INFO] Initial Battery: SoC: 0.800000 SoH: 1.000000 Capacity (Total): 10.000000Wh Capacity (Usable): 6.000000Wh P: 0.000000 -> [host1:manager:(1) 0.000000] [battery_simple/INFO] Battery power set to: 200.000000W -> [host1:manager:(1) 0.000000] [battery_simple/INFO] The battery will be depleted at: 108.000000 -> [host1:manager:(1) 108.000000] [battery_simple/INFO] Energy consumed : 21600.000000J (6.000000Wh) +$ ${bindir:=.}/s4u-battery-simple ${platfdir}/energy_platform.xml +> [0.000000] [battery_simple/INFO] Initial state: SoC: 0.800000 SoH: 1.000000 Energy stored: 28800.000000J Energy provided: 0.000000J Energy consumed 0.000000J +> [0.000000] [battery_simple/INFO] Set load to 100.000000W +> [194.419442] [battery_simple/INFO] Discharged state: SoC: 0.200000 SoH: 0.999700 Energy stored: 7197.839784J Energy provided: 19441.944194J Energy consumed 0.000000J +> [194.419442] [battery_simple/INFO] Set load to -100.000000W +> [434.229010] [battery_simple/INFO] Charged state: SoC: 0.800000 SoH: 0.999330 Energy stored: 28780.700933J Energy provided: 19441.944194J Energy consumed 23980.956832J +> [434.229010] [battery_simple/INFO] Set load to 0.000000W \ No newline at end of file diff --git a/examples/platforms/battery_platform.xml b/examples/platforms/battery_platform.xml deleted file mode 100644 index 210b4cb1c4..0000000000 --- a/examples/platforms/battery_platform.xml +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/include/simgrid/plugins/battery.hpp b/include/simgrid/plugins/battery.hpp index d6d7e2c036..87746d393c 100644 --- a/include/simgrid/plugins/battery.hpp +++ b/include/simgrid/plugins/battery.hpp @@ -2,30 +2,108 @@ /* This program is free software; you can redistribute it and/or modify it * under the terms of the license (GNU LGPL) which comes with this package. */ -#ifndef SIMGRID_PLUGINS_BATTERY_H_ -#define SIMGRID_PLUGINS_BATTERY_H_ -#include -#include -#include +#ifndef SIMGRID_PLUGINS_BATTERY_HPP_ +#define SIMGRID_PLUGINS_BATTERY_HPP_ -SG_BEGIN_DECL +#include +#include +#include +#include -XBT_PUBLIC void sg_battery_plugin_init(); +namespace simgrid::plugins { -XBT_PUBLIC void sg_battery_set_state(const_sg_host_t host, bool state); -XBT_PUBLIC void sg_battery_set_power(const_sg_host_t host, double power); +class Battery; +using BatteryPtr = boost::intrusive_ptr; +XBT_PUBLIC void intrusive_ptr_release(Battery* o); +XBT_PUBLIC void intrusive_ptr_add_ref(Battery* o); -XBT_PUBLIC bool sg_battery_is_active(const_sg_host_t host); -XBT_PUBLIC double sg_battery_get_power(const_sg_host_t host); -XBT_PUBLIC double sg_battery_get_state_of_charge(const_sg_host_t host); -XBT_PUBLIC double sg_battery_get_state_of_charge_min(const_sg_host_t host); -XBT_PUBLIC double sg_battery_get_state_of_charge_max(const_sg_host_t host); -XBT_PUBLIC double sg_battery_get_state_of_health(const_sg_host_t host); -XBT_PUBLIC double sg_battery_get_capacity(const_sg_host_t host); -XBT_PUBLIC double sg_battery_get_cumulative_cost(const_sg_host_t host); -XBT_PUBLIC double sg_battery_get_next_event_date(const_sg_host_t host); +class BatteryModel : public kernel::resource::Model { + std::vector batteries_; -SG_END_DECL +public: + explicit BatteryModel(); + void add_battery(BatteryPtr b); + void update_actions_state(double now, double delta) override; + double next_occurring_event(double now) override; +}; + +class Battery { + + friend BatteryModel; + +public: + enum Flow { CHARGE, DISCHARGE }; + + class Event { + public: + double state_of_charge_; + Flow flow_; + double time_delta_ = -1; + std::function callback_; + bool repeat_; + + Event(double state_of_charge, Flow flow, std::function callback, bool repeat); + static std::shared_ptr init(double state_of_charge, Flow flow, std::function callback, bool repeat); + }; + +private: + static std::shared_ptr battery_model_; + + std::string name_; + double charge_efficiency_; + double discharge_efficiency_; + double initial_capacity_wh_; + int cycles_; // total complete cycles (charge + discharge) the battery can do before complete depletion. + double depth_of_discharge_; + double energy_budget_j_; + + // std::map host_loads_ = {}; + std::map host_loads_ = {}; + std::map named_loads_ = {}; + std::vector> events_; + + double capacity_wh_ = 0; + double energy_stored_j_ = 0; + double energy_provided_j_ = 0; + double energy_consumed_j_ = 0; + double last_updated_ = 0; + + explicit Battery(const std::string& name, double state_of_charge, double charge_efficiency, + double discharge_efficiency, double initial_capacity_wh, int cycles, double depth_of_discharge); + static void init_plugin(); + void update(); + double next_occurring_event(); + + std::atomic_int_fast32_t refcount_{0}; +#ifndef DOXYGEN + friend void intrusive_ptr_release(Battery* o) + { + if (o->refcount_.fetch_sub(1, std::memory_order_release) == 1) { + std::atomic_thread_fence(std::memory_order_acquire); + delete o; + } + } + friend void intrusive_ptr_add_ref(Battery* o) { o->refcount_.fetch_add(1, std::memory_order_relaxed); } +#endif + +public: + static BatteryPtr init(const std::string& name, double state_of_charge, double charge_efficiency, + double discharge_efficiency, double initial_capacity_wh, int cycles, + double depth_of_discharge); + void set_load(const std::string& name, double power_w); + void connect_host(s4u::Host* host, bool active = true); + double get_state_of_charge(); + double get_state_of_health(); + double get_capacity(); + double get_energy_provided(); + double get_energy_consumed(); + double get_energy_stored(std::string unit = "J"); + std::shared_ptr create_event(double state_of_charge, Flow flow, std::function callback, + bool repeat = false); + std::vector> get_events(); + void delete_event(std::shared_ptr event); +}; +} // namespace simgrid::plugins #endif \ No newline at end of file diff --git a/src/plugins/battery.cpp b/src/plugins/battery.cpp index 21d1ceb3c5..c8f602dea9 100644 --- a/src/plugins/battery.cpp +++ b/src/plugins/battery.cpp @@ -4,599 +4,279 @@ * under the terms of the license (GNU LGPL) which comes with this package. */ #include #include -#include +#include #include #include -#include #include +#include +#include #include "src/kernel/resource/CpuImpl.hpp" #include "src/simgrid/module.hpp" -#include -#include - -SIMGRID_REGISTER_PLUGIN(battery, "Battery management", &sg_battery_plugin_init) - +SIMGRID_REGISTER_PLUGIN(battery, "Battery management", nullptr) /** @defgroup plugin_battery plugin_battery Plugin Battery @beginrst -This is the battery plugin, enabling management of batteries on hosts. -To activate this plugin, first call :cpp:func:`sg_battery_plugin_init()`. - -We consider a constant energy exchange model. - -Properties of batteries such as State of Charge and State of Health are lazily updated, ie., when reading values and -when the power is modified. - -State of Charge (SoC) -..................... - -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 -reaches its maximal SoC. Conversely, if the power of a battery is set to a positive value then the battery will act as a -generator and deplete over time until it reaches its minimal SoC. When reaching either its maximal or minimal SoC it -will set its power to 0. - -The natural depletion of batteries over time is not taken into account. - -State of Health (SoH) -..................... - -A battery starts with an energy budget :math:`E` such as: - -.. math:: - - E = C \times U \times D \times N \times 2 - -Where :math:`C` is the initial capacity, :math:`U` is the ratio of usable capacity, :math:`D` is the depth of discharge -and :math:`N` is the number of cycles of the battery. - -The SoH represents the consumption of this energy budget during the lifetime of the battery. -Use the battery reduces its SoH and its capacity in consequence. -When the SoH reaches 0, the battery becomes unusable. - -.. warning:: - - Due to the decrease of the battery capacity with the SoH, a large usable capacity leads to very tiny battery capacity - when reaching low SoH. This may results in charge and discharge cycles too short to be evaluated by the simulator. To - avoid this situation you should not try to reach a SoH of 0 with a usable capacity set to 1. - -Plotting the output of the example "battery-degradation" highlights the linear decrease of the SoH due to a continuous -use of the battery and the decreasing cycle duration as its capacity reduces: - -.. image:: /img/battery_degradation.svg - :align: center - -Batteries properties -.................... - -Properties of the batteries are defined as properties of hosts in the platform XML file. - -Here is an example of XML declaration: - -.. code-block:: xml - - - - - - - - -The different properties are: - -- ``battery_active``: Set the battery as active if set to 1 (default=0) -- ``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 -- ``battery_state_of_charge``: Set the initial state of charge of the battery (default=0) -- ``battery_state_of_charge_min``: Set the minimal state of charge of the battery (default=0.2) -- ``battery_state_of_charge_max``: Set the maximal state of charge of the battery (default=0.8) -- ``battery_capacity``: Set the initial capacity of the battery in Wh (default=0) -- ``battery_usable_capacity``: Set the ratio of usable capacity of the battery (default=0.8) -- ``battery_depth_of_discharge``: Set the depth of discharge of the battery (default=0.9) -- ``battery_charge_efficiency``: Set the charge efficiency of the battery (default=1) -- ``battery_discharge_efficiency``: Set the charge efficiency of the battery (default=1) -- ``battery_cycles``: Set the number of cycle of the battery (default=1) -- ``battery_depth_of_discharge``: Set the number of cycle of the battery (default=1) -- ``battery_eval_cost``: Evaluate the cost of the battery during the simulation if set to 1 (defaulf=0) -- ``battery_investment_cost``: Set the investment cost of the battery (default=0) -- ``battery_static_maintenance_cost``: Set the static maintenance cost of the battery (default=0) -- ``battery_variable_maintenance_cost``: Set the variable maintenance cost of the battery (default=0) +This is the battery plugin @endrst */ -XBT_LOG_NEW_DEFAULT_SUBCATEGORY(battery, kernel, "Logging specific to the battery plugin"); - -namespace simgrid::plugin { -class Battery { -private: - simgrid::s4u::Host* host_ = nullptr; - - double state_of_charge_min_ = 0.2; - double state_of_charge_max_ = 0.8; - double charge_efficiency_ = 1; - double discharge_efficiency_ = 1; - double initial_capacity_wh_ = 0; - double cycles_ = 1; // total complete cycles (charge + discharge) the battery can do before complete depletion. - double depth_of_discharge_ = 0.9; - double usable_capacity_ = 0.8; - double energy_budget_j_ = 0; - - bool active_ = false; - double power_w_ = 0; // NEGATIVE when charging (consumes power) POSITIVE when discharging (generates power) - double state_of_charge_ = 0; - double capacity_wh_ = 0; - double next_event_ = -1; - double energy_exchanged_j_ = 0; - double last_updated_ = 0; - - // Calculation of costs from Bei Li thesis (link :https://tel.archives-ouvertes.fr/tel-02077668/document) (chapter 4) - bool eval_cost_ = false; - double cumulative_cost_ = 0; - double investment_cost_per_wh_ = 0; - double static_maintenance_cost_per_wh_times_h_ = 0; - double variable_maintenance_cost_per_wh_ = 0; - - void init_battery_params(); - void init_cost_params(); - void update(); - - void set_state_of_charge(double soc); - void set_state_of_charge_min(double soc); - void set_state_of_charge_max(double soc); - void set_capacity(double c); - void set_initial_capacity(double c); - void set_cycles(int c); - void set_depth_of_discharge(double d); - void set_usable_capacity(double c); - void set_charge_efficiency(double e); - void set_discharge_efficiency(double e); - void set_eval_cost(bool eval); - void set_investment_cost(double c); - void set_static_maintenance_cost(double c); - void set_variable_maintenance_cost(double c); - - bool is_charging(); - -public: - static simgrid::xbt::Extension EXTENSION_ID; - - explicit Battery(simgrid::s4u::Host* host); - ~Battery(); - - void set_state(bool state); - void set_power(const double power); - - bool is_active() const; - double get_power(); - double get_state_of_charge(); - double get_state_of_charge_min() const; - double get_state_of_charge_max() const; - double get_state_of_health(); - double get_capacity(); - double get_cumulative_cost(); - double get_next_event_date(); -}; - -simgrid::xbt::Extension Battery::EXTENSION_ID; - -void Battery::set_power(const double p) -{ - update(); - xbt_assert(p == 0 or (p > 0 and state_of_charge_ > state_of_charge_min_) or - (p < 0 and state_of_charge_ < state_of_charge_max_), - "Incoherent power and state of charge. A battery cannot charge(discharge) past its maximal(minimal) state " - "of charge."); - xbt_assert(p == 0 or energy_exchanged_j_ < energy_budget_j_, "Cannot set power of a fully used battery."); - simgrid::kernel::actor::simcall_answered([this, p] { - power_w_ = p; - if (power_w_ == 0) { - next_event_ = -1; - return; - } - double soc_shutdown; - double soh_shutdown; - if (power_w_ > 0) { - soc_shutdown = capacity_wh_ * 3600 * (state_of_charge_ - state_of_charge_min_) / (power_w_ * charge_efficiency_); - soh_shutdown = (energy_budget_j_ - energy_exchanged_j_) / (power_w_ * charge_efficiency_); - } else { // power_w_ < 0 - soc_shutdown = - capacity_wh_ * 3600 * (state_of_charge_max_ - state_of_charge_) / (abs(power_w_) / discharge_efficiency_); - soh_shutdown = (energy_budget_j_ - energy_exchanged_j_) / (abs(power_w_) / discharge_efficiency_); - } - if (soh_shutdown <= 0) - next_event_ = simgrid::s4u::Engine::get_clock() + soc_shutdown; - else - next_event_ = simgrid::s4u::Engine::get_clock() + std::min(soc_shutdown, soh_shutdown); - }); -} - -void Battery::set_state(bool state) -{ - update(); - simgrid::kernel::actor::simcall_answered([this, state] { active_ = state; }); -} - -void Battery::set_state_of_charge(double soc) -{ - xbt_assert(soc > 0 and soc <= 1, " : state of charge should be in ]0,1] (provided: %f)", soc); - simgrid::kernel::actor::simcall_answered([this, soc] { state_of_charge_ = soc; }); -} - -void Battery::set_state_of_charge_min(double soc) -{ - xbt_assert(soc > 0 and soc <= 1 and soc < state_of_charge_max_, - " : state of charge min should be in ]0,1] and below state of charge max (provided: %f)", soc); - simgrid::kernel::actor::simcall_answered([this, soc] { state_of_charge_min_ = soc; }); -} - -void Battery::set_state_of_charge_max(double soc) -{ - xbt_assert(soc > 0 and soc <= 1 and soc > state_of_charge_min_, - " : state of charge max should be in ]0,1] and above state of charge min (provided: %f)", soc); - simgrid::kernel::actor::simcall_answered([this, soc] { state_of_charge_max_ = soc; }); -} - -void Battery::set_initial_capacity(double c) -{ - xbt_assert(c > 0, " : capacity should be > 0 (provided: %f)", c); - simgrid::kernel::actor::simcall_answered([this, c] { initial_capacity_wh_ = c; }); -} - -void Battery::set_capacity(double c) -{ - xbt_assert(c > 0, " : capacity should be > 0 (provided: %f)", c); - simgrid::kernel::actor::simcall_answered([this, c] { capacity_wh_ = c; }); -} - -void Battery::set_cycles(int c) -{ - xbt_assert(c > 0, " : cycles should be > 0 (provided: %d)", c); - simgrid::kernel::actor::simcall_answered([this, c] { cycles_ = c; }); -} - -void Battery::set_depth_of_discharge(double d) -{ - xbt_assert(d > 0 and d <= 1, " : depth of discharge should be in ]0, 1] (provided: %f)", d); - simgrid::kernel::actor::simcall_answered([this, d] { depth_of_discharge_ = d; }); -} - -void Battery::set_usable_capacity(double c) -{ - xbt_assert(c > 0 and c <= 1, " : usable capacity should be in ]0, 1] (provided: %f)", c); - simgrid::kernel::actor::simcall_answered([this, c] { usable_capacity_ = c; }); -} - -void Battery::set_charge_efficiency(double e) -{ - xbt_assert(e > 0 and e <= 1, " : charge efficiency should be in [0,1] (provided: %f)", e); - simgrid::kernel::actor::simcall_answered([this, e] { charge_efficiency_ = e; }); -} - -void Battery::set_discharge_efficiency(double e) -{ - xbt_assert(e > 0 and e <= 1, " : discharge efficiency should be in [0,1] (provided: %f)", e); - simgrid::kernel::actor::simcall_answered([this, e] { discharge_efficiency_ = e; }); -} +XBT_LOG_NEW_DEFAULT_SUBCATEGORY(Battery, kernel, "Logging specific to the battery plugin"); -void Battery::set_eval_cost(bool eval) -{ - simgrid::kernel::actor::simcall_answered([this, eval] { eval_cost_ = eval; }); -} - -void Battery::set_investment_cost(double c) -{ - xbt_assert(c >= 0, " : investment cost should be >= 0 (provided: %f)", c); - simgrid::kernel::actor::simcall_answered([this, c] { investment_cost_per_wh_ = c; }); -} - -void Battery::set_static_maintenance_cost(double c) -{ - xbt_assert(c >= 0, " : static maintenance cost should be >= 0 (provided: %f)", c); - simgrid::kernel::actor::simcall_answered([this, c] { static_maintenance_cost_per_wh_times_h_ = c; }); -} +namespace simgrid::plugins { -void Battery::set_variable_maintenance_cost(double c) -{ - xbt_assert(c >= 0, " : variable maintenance cost should be >= 0 (provided: %f)", c); - simgrid::kernel::actor::simcall_answered([this, c] { variable_maintenance_cost_per_wh_ = c; }); -} - -bool Battery::is_charging() -{ - update(); - return power_w_ < 0; -} +/* BatteryModel */ -bool Battery::is_active() const -{ - return active_; -} +BatteryModel::BatteryModel() : Model("BatteryModel") {} -double Battery::get_power() +void BatteryModel::add_battery(BatteryPtr b) { - update(); - return power_w_; + batteries_.push_back(b); } -double Battery::get_state_of_charge() +void BatteryModel::update_actions_state(double now, double delta) { - update(); - return state_of_charge_; + for (auto battery : batteries_) + battery->update(); } -double Battery::get_state_of_charge_min() const +double BatteryModel::next_occurring_event(double now) { - return state_of_charge_min_; + double time_delta = -1; + for (auto battery : batteries_) { + double time_delta_battery = battery->next_occurring_event(); + time_delta = time_delta == -1 or time_delta_battery < time_delta ? time_delta_battery : time_delta; + } + return time_delta; } -double Battery::get_state_of_charge_max() const -{ - return state_of_charge_max_; -} +/* Event */ -double Battery::get_state_of_health() +Battery::Event::Event(double state_of_charge, Flow flow, std::function callback, bool repeat) + : state_of_charge_(state_of_charge), flow_(flow), callback_(callback), repeat_(repeat) { - update(); - return 1 - (energy_exchanged_j_ / energy_budget_j_); } -double Battery::get_capacity() +std::shared_ptr Battery::Event::init(double state_of_charge, Flow flow, std::function callback, + bool repeat) { - update(); - return capacity_wh_; + return std::make_shared(state_of_charge, flow, callback, repeat); } -double Battery::get_cumulative_cost() -{ - update(); - return cumulative_cost_; -} +/* Battery */ -double Battery::get_next_event_date() -{ - update(); - return next_event_; -} +std::shared_ptr Battery::battery_model_; -void Battery::init_battery_params() +void Battery::init_plugin() { - const char* prop_chars; - prop_chars = host_->get_property("battery_capacity"); - if (prop_chars) { - set_capacity(xbt_str_parse_double(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str())); - set_initial_capacity(xbt_str_parse_double(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str())); - } - prop_chars = host_->get_property("battery_usable_capacity"); - if (prop_chars) - set_usable_capacity(xbt_str_parse_double(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str())); - prop_chars = host_->get_property("battery_depth_of_discharge"); - if (prop_chars) - set_depth_of_discharge( - xbt_str_parse_double(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str())); - prop_chars = host_->get_property("battery_cycles"); - if (prop_chars) - set_cycles(xbt_str_parse_int(prop_chars, ("cannot parse int: " + std::string(prop_chars)).c_str())); - simgrid::kernel::actor::simcall_answered([this] { - energy_budget_j_ = (initial_capacity_wh_ * usable_capacity_ * depth_of_discharge_ * 3600 * cycles_ * 2); - }); - prop_chars = host_->get_property("battery_active"); - if (prop_chars) - set_state(xbt_str_parse_int(prop_chars, ("cannot parse int: " + std::string(prop_chars)).c_str())); - prop_chars = host_->get_property("battery_state_of_charge_min"); - if (prop_chars) - set_state_of_charge_min( - xbt_str_parse_double(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str())); - prop_chars = host_->get_property("battery_state_of_charge_max"); - if (prop_chars) - set_state_of_charge_max( - xbt_str_parse_double(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str())); - prop_chars = host_->get_property("battery_charge_efficiency"); - if (prop_chars) - set_charge_efficiency( - xbt_str_parse_double(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str())); - prop_chars = host_->get_property("battery_discharge_efficiency"); - if (prop_chars) - set_discharge_efficiency( - xbt_str_parse_double(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str())); - prop_chars = host_->get_property("battery_state_of_charge"); - if (prop_chars) - set_state_of_charge(xbt_str_parse_double(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str())); - prop_chars = host_->get_property("battery_eval_cost"); - if (prop_chars) - set_eval_cost(xbt_str_parse_int(prop_chars, ("cannot parse int: " + std::string(prop_chars)).c_str())); - prop_chars = host_->get_property("battery_investment_cost"); - if (prop_chars) - set_investment_cost(xbt_str_parse_int(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str())); - prop_chars = host_->get_property("battery_static_maintenance_cost"); - if (prop_chars) - set_static_maintenance_cost( - xbt_str_parse_int(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str())); - prop_chars = host_->get_property("battery_variable_maintenance_cost"); - if (prop_chars) - set_variable_maintenance_cost( - xbt_str_parse_int(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str())); - prop_chars = host_->get_property("battery_power"); - if (prop_chars) - set_power(xbt_str_parse_double(prop_chars, ("cannot parse double: " + std::string(prop_chars)).c_str())); - simgrid::kernel::actor::simcall_answered([this] { last_updated_ = simgrid::s4u::Engine::get_clock(); }); + auto model = std::make_shared(); + simgrid::s4u::Engine::get_instance()->add_model(model); + Battery::battery_model_ = model; } void Battery::update() { - simgrid::kernel::actor::simcall_answered([this] { - double now = simgrid::s4u::Engine::get_clock(); - double time_delta_real = now - last_updated_; - if (time_delta_real <= 0 || not is_active()) + kernel::actor::simcall_answered([this] { + double now = s4u::Engine::get_clock(); + double time_delta_s = now - last_updated_; + + // Nothing to update + if (time_delta_s <= 0) return; - double time_delta_until_event = next_event_ - last_updated_; - bool event = next_event_ != -1 and time_delta_until_event <= time_delta_real; - double power_real_w = power_w_ < 0 ? power_w_ * charge_efficiency_ : power_w_ / discharge_efficiency_; - state_of_charge_ -= power_real_w * (event ? time_delta_until_event : time_delta_real) / (3600 * capacity_wh_); - energy_exchanged_j_ += (event ? time_delta_until_event : time_delta_real) * abs(power_real_w); - capacity_wh_ = initial_capacity_wh_ * usable_capacity_ * (1 - (energy_exchanged_j_ / energy_budget_j_)) + - initial_capacity_wh_ * (1 - usable_capacity_); - capacity_wh_ = std::max(capacity_wh_, 0.0); - if (eval_cost_) { - double usage_cost_per_wh = - (investment_cost_per_wh_ / (depth_of_discharge_ * cycles_ * 2) + variable_maintenance_cost_per_wh_); - double usage_cost = - usage_cost_per_wh * abs(power_real_w) * (event ? time_delta_until_event : time_delta_real) / 3600; - double static_maintenance_cost = - static_maintenance_cost_per_wh_times_h_ * initial_capacity_wh_ * time_delta_real / 3600; - cumulative_cost_ += usage_cost + static_maintenance_cost; + + // Calculate energy provided / consumed during this step + double provided_power_w = 0; + double consumed_power_w = 0; + for (auto const& [host, active] : host_loads_) + provided_power_w += active ? sg_host_get_current_consumption(host) : 0; + for (auto const& [name, load] : named_loads_) { + if (load > 0) + provided_power_w += load; + else + consumed_power_w += -load; } - if (event) { - power_w_ = 0; - next_event_ = -1; + double energy_lost_delta_j = provided_power_w / discharge_efficiency_ * time_delta_s; + double energy_gained_delta_j = consumed_power_w * charge_efficiency_ * time_delta_s; + + // Check that the provided/consumed energy is valid + energy_lost_delta_j = std::min(energy_lost_delta_j, energy_stored_j_ + energy_gained_delta_j); + /* Charging deteriorate the capacity, but the capacity is used to evaluate the maximum charge so + we need to evaluate the theorethical new capacity in the worst case when we fully charge the battery */ + double new_tmp_capacity_wh = + (initial_capacity_wh_ * + (1 - (energy_provided_j_ + energy_lost_delta_j * discharge_efficiency_ + energy_consumed_j_ - + (energy_stored_j_ + energy_lost_delta_j) / charge_efficiency_) / + energy_budget_j_)) / + (1 + 3600 * initial_capacity_wh_ / (charge_efficiency_ * energy_budget_j_)); + energy_gained_delta_j = + std::min(energy_gained_delta_j, (3600 * new_tmp_capacity_wh) - energy_stored_j_ + energy_lost_delta_j); + + // Updating battery + energy_provided_j_ += energy_lost_delta_j * discharge_efficiency_; + energy_consumed_j_ += energy_gained_delta_j / charge_efficiency_; + capacity_wh_ = initial_capacity_wh_ * (1 - (energy_provided_j_ + energy_consumed_j_) / energy_budget_j_); + energy_stored_j_ += energy_gained_delta_j - energy_lost_delta_j; + energy_stored_j_ = std::min(energy_stored_j_, 3600 * capacity_wh_); + last_updated_ = now; + + std::vector> to_delete = {}; + for (auto event : events_) { + if (abs(event->time_delta_ - time_delta_s) < 0.000000001) { + event->callback_(); + if (event->repeat_) + event->time_delta_ = -1; + else + to_delete.push_back(event); + } } - last_updated_ = now; + for (auto event : to_delete) + delete_event(event); }); } -Battery::Battery(simgrid::s4u::Host* host) : host_(host) -{ - init_battery_params(); -} - -Battery::~Battery() = default; -} // namespace simgrid::plugin - -using simgrid::plugin::Battery; - -/* **************************** events callback *************************** */ - -static void on_creation(simgrid::s4u::Host& host) -{ - if (dynamic_cast(&host)) // Ignore virtual machines - return; - host.extension_set(new Battery(&host)); -} - -/* **************************** Public interface *************************** */ - -static void ensure_plugin_inited() +double Battery::next_occurring_event() { - if (not Battery::EXTENSION_ID.valid()) - throw simgrid::xbt::InitializationError("The Battery plugin is not active. Please call sg_battery_plugin_init() " - "before calling any function related to that plugin."); -} + double provided_power_w = 0; + double consumed_power_w = 0; + for (auto const& [host, active] : host_loads_) + provided_power_w += active ? sg_host_get_current_consumption(host) : 0; + for (auto const& [name, load] : named_loads_) { + if (load > 0) + provided_power_w += load; + else + consumed_power_w += -load; + } -/** @ingroup plugin_battery - * @brief Enable battery plugin. - */ -void sg_battery_plugin_init() -{ - if (Battery::EXTENSION_ID.valid()) - return; - Battery::EXTENSION_ID = simgrid::s4u::Host::extension_create(); - simgrid::s4u::Host::on_creation_cb(&on_creation); + double time_delta = -1; + for (auto& event : events_) { + double lost_power_w = provided_power_w / discharge_efficiency_; + double gained_power_w = consumed_power_w * charge_efficiency_; + // Event cannot happen + if ((lost_power_w == gained_power_w) or (event->state_of_charge_ == energy_stored_j_ / (3600 * capacity_wh_)) or + (lost_power_w > gained_power_w and event->flow_ == Flow::CHARGE) or + (lost_power_w < gained_power_w and event->flow_ == Flow::DISCHARGE)) { + continue; + } + // Evaluate time until event happen + else { + /* The time to reach a state of charge depends on the capacity, but charging and discharging deteriorate the + * capacity, so we need to evaluate the time considering a capacity that also depends on time + */ + event->time_delta_ = (3600 * event->state_of_charge_ * initial_capacity_wh_ * + (1 - (energy_provided_j_ + energy_consumed_j_) / energy_budget_j_) - + energy_stored_j_) / + (gained_power_w - lost_power_w + + 3600 * event->state_of_charge_ * initial_capacity_wh_ * + (consumed_power_w + provided_power_w) / energy_budget_j_); + if ((time_delta == -1 or event->time_delta_ < time_delta) and abs(event->time_delta_) > 0.000000001) + time_delta = event->time_delta_; + } + } + return time_delta; +} + +Battery::Battery(const std::string& name, double state_of_charge, double charge_efficiency, double discharge_efficiency, + double initial_capacity_wh, int cycles, double depth_of_discharge) + : name_(name) + , energy_stored_j_(state_of_charge * 3600 * initial_capacity_wh) + , charge_efficiency_(charge_efficiency) + , discharge_efficiency_(discharge_efficiency) + , initial_capacity_wh_(initial_capacity_wh) + , capacity_wh_(initial_capacity_wh) + , cycles_(cycles) + , depth_of_discharge_(depth_of_discharge) + , energy_budget_j_(initial_capacity_wh * depth_of_discharge * 3600 * cycles * 2) +{ + xbt_assert(state_of_charge >= 0 and state_of_charge <= 1, " : state of charge should be in [0, 1] (provided: %f)", + state_of_charge); + xbt_assert(charge_efficiency > 0 and charge_efficiency <= 1, " : charge efficiency should be in [0,1] (provided: %f)", + charge_efficiency); + xbt_assert(discharge_efficiency > 0 and discharge_efficiency <= 1, + " : discharge efficiency should be in [0,1] (provided: %f)", discharge_efficiency); + xbt_assert(initial_capacity_wh > 0, " : initial capacity should be > 0 (provided: %f)", initial_capacity_wh); + xbt_assert(cycles > 0, " : cycles should be > 0 (provided: %d)", cycles); + xbt_assert(depth_of_discharge > 0 and depth_of_discharge <= 1, + " : depth of discharge should be in ]0, 1] (provided: %f)", depth_of_discharge); +} + +BatteryPtr Battery::init(const std::string& name, double state_of_charge, double charge_efficiency, + double discharge_efficiency, double initial_capacity_wh, int cycles, double depth_of_discharge) +{ + static bool plugin_inited = false; + if (not plugin_inited) { + init_plugin(); + plugin_inited = true; + } + auto battery = BatteryPtr(new Battery(name, state_of_charge, charge_efficiency, discharge_efficiency, + initial_capacity_wh, cycles, depth_of_discharge)); + battery_model_->add_battery(battery); + return battery; } -/** @ingroup plugin_battery - * @param state The state to set. - * @brief Set the state of the battery. - * A battery set to inactive (false) will neither update its state of charge nor its state of health. - */ -void sg_battery_set_state(const_sg_host_t host, bool state) +void Battery::set_load(const std::string& name, double power_w) { - ensure_plugin_inited(); - host->extension()->set_state(state); + named_loads_[name] = power_w; } -/** @ingroup plugin_battery - * @param power The power to set in W. - * @brief Set the power of the battery. - * @note A negative value makes the battery act as a load and charge. - * A positive value makes the battery act as a generator and discharge. - */ -void sg_battery_set_power(const_sg_host_t host, double power) +void Battery::connect_host(s4u::Host* h, bool active) { - ensure_plugin_inited(); - host->extension()->set_power(power); + host_loads_[h] = active; } -/** @ingroup plugin_battery - * @brief Return true if the battery is active. - */ -bool sg_battery_is_active(const_sg_host_t host) +double Battery::get_state_of_charge() { - ensure_plugin_inited(); - return host->extension()->is_active(); + return energy_stored_j_ / (3600 * capacity_wh_); } -/** @ingroup plugin_battery - * @brief Return the power of the battery in W. - * @note 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. - */ -double sg_battery_get_power(const_sg_host_t host) +double Battery::get_state_of_health() { - ensure_plugin_inited(); - return host->extension()->get_power(); + return 1 - ((energy_provided_j_ + energy_consumed_j_) / energy_budget_j_); } -/** @ingroup plugin_battery - * @brief Return the state of charge of the battery. - */ -double sg_battery_get_state_of_charge(const_sg_host_t host) +double Battery::get_capacity() { - ensure_plugin_inited(); - return host->extension()->get_state_of_charge(); + return capacity_wh_; } -/** @ingroup plugin_battery - * @brief Return the minimal state of charge of the battery. - */ -double sg_battery_get_state_of_charge_min(const_sg_host_t host) +double Battery::get_energy_provided() { - ensure_plugin_inited(); - return host->extension()->get_state_of_charge_min(); + return energy_provided_j_; } -/** @ingroup plugin_battery - * @brief Return the maximal state of charge of the battery. - */ -double sg_battery_get_state_of_charge_max(const_sg_host_t host) +double Battery::get_energy_consumed() { - ensure_plugin_inited(); - return host->extension()->get_state_of_charge_max(); + return energy_consumed_j_; } -/** @ingroup plugin_battery - * @brief Return the state of health of the battery. - */ -double sg_battery_get_state_of_health(const_sg_host_t host) +double Battery::get_energy_stored(std::string unit) { - ensure_plugin_inited(); - return host->extension()->get_state_of_health(); + if (unit == "J") + return energy_stored_j_; + else if (unit == "Wh") + return energy_stored_j_ / 3600; + else + xbt_die("Invalid unit. Valid units are J (default) or Wh."); } -/** @ingroup plugin_battery - * @brief Return the capacity of the battery in Wh. - * @note capacity is reduced by the state of health of the battery. - */ -double sg_battery_get_capacity(const_sg_host_t host) +std::shared_ptr Battery::create_event(double state_of_charge, Flow flow, std::function callback, + bool repeat) { - ensure_plugin_inited(); - return host->extension()->get_capacity(); + auto event = Event::init(state_of_charge, flow, callback, repeat); + events_.push_back(event); + return event; } -/** @ingroup plugin_battery - * @brief Return the cumulative cost of the battery. - */ -double sg_battery_get_cumulative_cost(const_sg_host_t host) +std::vector> Battery::get_events() { - ensure_plugin_inited(); - return host->extension()->get_cumulative_cost(); + return events_; } -/** @ingroup plugin_battery - * @brief Return the date of the next event, i.e., when the battery will be empty or full. - * @note If power is null then return -1. - */ -double sg_battery_get_next_event_date(const_sg_host_t host) +void Battery::delete_event(std::shared_ptr event) { - ensure_plugin_inited(); - return host->extension()->get_next_event_date(); + events_.erase( + std::remove_if(events_.begin(), events_.end(), [&event](std::shared_ptr e) { return event == e; }), + events_.end()); } +} // namespace simgrid::plugins \ No newline at end of file