diff --git a/sample_inputs/input.yml b/sample_inputs/input.yml index cf044dd..bcc9241 100644 --- a/sample_inputs/input.yml +++ b/sample_inputs/input.yml @@ -828,6 +828,7 @@ epidemiological_parameters: # --------------------------------------------------------------- # 15. Mosquito Parameters (default is location based) # --------------------------------------------------------------- +#TODO: Check this implementation in Mosquito class mosquito_parameters: mosquito_config: mode: "location_based" # or "grid_based" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ea606bc..89bf2eb 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -5,6 +5,7 @@ find_package(sol2 CONFIG REQUIRED) find_package(Lua REQUIRED) find_package(spdlog REQUIRED) find_package(date CONFIG REQUIRED) +find_package(CLI11 CONFIG REQUIRED) include_directories( ${PROJECT_SOURCE_DIR}/src @@ -34,6 +35,7 @@ target_link_libraries(MalaSimCore PUBLIC sol2 spdlog::spdlog date::date date::date-tz + CLI11::CLI11 ) set_property(TARGET MalaSimCore PROPERTY CXX_STANDARD 20) diff --git a/src/Configuration/Config.cpp b/src/Configuration/Config.cpp index 77f66bd..d373483 100644 --- a/src/Configuration/Config.cpp +++ b/src/Configuration/Config.cpp @@ -13,58 +13,73 @@ int inline get_pipe_count(const std::string &str) { return pipe_count; } -void Config::load(const std::string &filename) { +bool Config::load(const std::string &filename) { config_file_path_ = filename; - YAML::Node config = YAML::LoadFile(filename); + try { + YAML::Node config = YAML::LoadFile(filename); - config_data_.model_settings = config["model_settings"].as(); - config_data_.simulation_timeframe = - config["simulation_timeframe"].as(); + std::cout << "Configuration file loaded successfully" << std::endl; - config_data_.transmission_settings = - config["transmission_settings"].as(); + config_data_.model_settings = config["model_settings"].as(); + config_data_.simulation_timeframe = + config["simulation_timeframe"].as(); - config_data_.population_demographic = - config["population_demographic"].as(); + config_data_.transmission_settings = + config["transmission_settings"].as(); - config_data_.spatial_settings = - config["spatial_settings"].as(); + config_data_.population_demographic = + config["population_demographic"].as(); - config_data_.seasonality_settings = - config["seasonality_settings"].as(); + config_data_.spatial_settings = + config["spatial_settings"].as(); - config_data_.movement_settings = - config["movement_settings"].as(); + config_data_.seasonality_settings = + config["seasonality_settings"].as(); - config_data_.parasite_parameters = - config["parasite_parameters"].as(); + config_data_.movement_settings = + config["movement_settings"].as(); - config_data_.immune_system_parameters = - config["immune_system_parameters"].as(); + config_data_.parasite_parameters = + config["parasite_parameters"].as(); - config_data_.genotype_parameters = - config["genotype_parameters"].as(); + config_data_.immune_system_parameters = + config["immune_system_parameters"].as(); - config_data_.drug_parameters = - config["drug_parameters"].as(); + config_data_.genotype_parameters = + config["genotype_parameters"].as(); - config_data_.therapy_parameters = - config["therapy_parameters"].as(); + config_data_.drug_parameters = + config["drug_parameters"].as(); - config_data_.strategy_parameters = - config["strategy_parameters"].as(); + config_data_.therapy_parameters = + config["therapy_parameters"].as(); - config_data_.epidemiological_parameters = - config["epidemiological_parameters"].as(); + config_data_.strategy_parameters = + config["strategy_parameters"].as(); - config_data_.mosquito_parameters = - config["mosquito_parameters"].as(); + config_data_.epidemiological_parameters = + config["epidemiological_parameters"].as(); - config_data_.population_events = - config["population_events"].as(); + config_data_.mosquito_parameters = + config["mosquito_parameters"].as(); - // Validate all cross field validations - validate_all_cross_field_validations(); + config_data_.population_events = + config["population_events"].as(); + + std::cout << "Configuration file parsed successfully" << std::endl; + + // Validate all cross field validations + validate_all_cross_field_validations(); + + std::cout << "Configuration file validated successfully" << std::endl; + + return true; + } + catch (YAML::BadFile) { + std::cerr << "Error: File not found" << std::endl; + return false; + } + return false; } void Config::validate_all_cross_field_validations() { diff --git a/src/Configuration/Config.h b/src/Configuration/Config.h index 23f71bc..8df89a0 100644 --- a/src/Configuration/Config.h +++ b/src/Configuration/Config.h @@ -19,7 +19,7 @@ class Config { Config &operator=(Config &&) = delete; // Load configuration from a YAML file - void load(const std::string &filename); + bool load(const std::string &filename); // Reload configuration (useful for dynamic updates) void reload(); diff --git a/src/Configuration/README.md b/src/Configuration/README.md index bab675b..bff4103 100644 --- a/src/Configuration/README.md +++ b/src/Configuration/README.md @@ -1 +1,47 @@ # Core Config + +The Configuration module is responsible for loading, validating, and managing the configuration settings for the simulation. It reads configuration data from a YAML file and provides access to various configuration parameters through getter methods. + +## Files + +- `Config.h` and `Config.cpp`: Defines the `Config` class, which handles loading and validating the configuration file. +- `ConfigData.h`: Defines the `ConfigData` struct, which holds all the configuration parameters. +- `ConfigData.cpp`: Implements the methods for the `ConfigData` struct. + +## Classes + +### Config + +The `Config` class is responsible for: + +- Loading configuration data from a YAML file. +- Validating the configuration data. +- Providing access to the configuration parameters. + +#### Methods + +- `bool load(const std::string &filename)`: Loads the configuration from the specified YAML file. +- `void reload()`: Reloads the configuration file. +- `void validate_all_cross_field_validations()`: Validates all cross-field validations. +- Various getter methods to access specific configuration parameters. + +### ConfigData + +The `ConfigData` struct holds all the configuration parameters. It includes the following fields: + +- `ModelSettings model_settings`: Configuration for the model settings. +- `TransmissionSettings transmission_settings`: Configuration for transmission settings. +- `PopulationDemographic population_demographic`: Configuration for population demographics. +- `SimulationTimeframe simulation_timeframe`: Configuration for the simulation timeframe. +- `SpatialSettings spatial_settings`: Configuration for spatial settings. +- `SeasonalitySettings seasonality_settings`: Configuration for seasonality settings. +- `MovementSettings movement_settings`: Configuration for movement settings. +- `ParasiteParameters parasite_parameters`: Configuration for parasite parameters. +- `ImmuneSystemParameters immune_system_parameters`: Configuration for immune system parameters. +- `GenotypeParameters genotype_parameters`: Configuration for genotype parameters. +- `DrugParameters drug_parameters`: Configuration for drug parameters. +- `TherapyParameters therapy_parameters`: Configuration for therapy parameters. +- `StrategyParameters strategy_parameters`: Configuration for strategy parameters. +- `EpidemiologicalParameters epidemiological_parameters`: Configuration for epidemiological parameters. +- `MosquitoParameters mosquito_parameters`: Configuration for mosquito parameters. +- `PopulationEvents population_events`: Configuration for population events. diff --git a/src/Configuration/SimulationTimeframe.h b/src/Configuration/SimulationTimeframe.h index 9e9013d..070b5e6 100644 --- a/src/Configuration/SimulationTimeframe.h +++ b/src/Configuration/SimulationTimeframe.h @@ -4,6 +4,7 @@ #include #include "Utils/YamlFile.h" +#include "Utils/Time.h" // Class to hold the simulation timeframe data class SimulationTimeframe { @@ -53,11 +54,20 @@ class SimulationTimeframe { start_collect_data_day_ = value; } + void set_total_time(int total_time) { + total_time_ = total_time; + } + + [[nodiscard]] int get_total_time() const { + return total_time_; + } + private: date::year_month_day starting_date_; date::year_month_day start_of_comparison_period_; date::year_month_day ending_date_; int start_collect_data_day_; + int total_time_; }; // Specialization of convert for the SimulationTimeframe class @@ -93,6 +103,7 @@ struct convert { rhs.set_ending_date(node["ending_date"].as()); rhs.set_start_collect_data_day(node["start_collect_data_day"].as()); + rhs.set_total_time(utils::Time::instance().get_day_count(rhs.get_starting_date(), rhs.get_ending_date())); return true; } }; diff --git a/src/Core/Scheduler/README.md b/src/Core/Scheduler/README.md index 1b7f3e7..97f6ebb 100644 --- a/src/Core/Scheduler/README.md +++ b/src/Core/Scheduler/README.md @@ -1 +1,37 @@ # Core Scheduler + +The Scheduler module is responsible for managing the simulation's time progression and scheduling events. It interacts with the `Model` class to execute daily, monthly, and yearly updates. + +## Files + +- `Scheduler.h` and `Scheduler.cpp`: Defines the `Scheduler` class, which handles the simulation's time management and event scheduling. + +## Classes + +### Scheduler + +The `Scheduler` class is responsible for: + +- Managing the simulation's current time and total available time. +- Interacting with the `Model` class to perform updates. +- Handling the simulation's calendar date. +- Providing methods to start, run, and stop the simulation. + +#### Methods + +- `Scheduler(Model *model = nullptr)`: Constructor that initializes the scheduler with an optional `Model` instance. +- `~Scheduler()`: Destructor that clears all events. +- `void extend_total_time(int new_total_time)`: Extends the total available time for the simulation. +- `void clear_all_events()`: Clears all scheduled events. +- `void initialize(const date::year_month_day &starting_date, const int &total_time)`: Initializes the scheduler with a starting date and total time. +- `void run()`: Runs the simulation. +- `void begin_time_step() const`: Begins a time step in the simulation. +- `void end_time_step() const`: Ends a time step in the simulation. +- `bool can_stop() const`: Checks if the simulation can stop. +- `int current_day_in_year() const`: Returns the current day in the year. +- `int current_month_in_year() const`: Returns the current month in the year. +- `bool is_today_last_day_of_month() const`: Checks if today is the last day of the month. +- `bool is_today_first_day_of_month() const`: Checks if today is the first day of the month. +- `bool is_today_first_day_of_year() const`: Checks if today is the first day of the year. +- `bool is_today_last_day_of_year() const`: Checks if today is the last day of the year. +- `void daily_update() const`: Performs daily updates in the simulation. diff --git a/src/Core/Scheduler/Scheduler.cpp b/src/Core/Scheduler/Scheduler.cpp new file mode 100644 index 0000000..1a123c1 --- /dev/null +++ b/src/Core/Scheduler/Scheduler.cpp @@ -0,0 +1,111 @@ +#include "Scheduler.h" + +#include + +Scheduler::Scheduler(Model* model) + : current_time_(-1), total_available_time_(-1), model_(model), is_force_stop_(false) {} + +Scheduler::~Scheduler() { + clear_all_events(); +} + +void Scheduler::extend_total_time(int new_total_time) { + if (total_available_time_ < new_total_time) { + // for (auto i = total_available_time_; i <= new_total_time; i++) { + // individual_events_list_.push_back(EventPtrVector()); + // population_events_list_.push_back(EventPtrVector()); + // } + } + total_available_time_ = new_total_time; +} + +void Scheduler::clear_all_events() { + // clear_all_events(individual_events_list_); + // clear_all_events(population_events_list_); +} + +void Scheduler::initialize(const date::year_month_day& starting_date, const int& total_time) { + set_total_available_time(total_time + 720); // Prevent scheduling at the end of simulation + set_current_time(0); + calendar_date = date::sys_days(starting_date); +} + +// Methods not declared in the header have been removed + +void Scheduler::run() { + current_time_ = 0; + for (current_time_ = 0; !can_stop(); current_time_++) { + if (current_time_ % model_->get_config()->get_model_settings().get_days_between_stdout_output() == 0) { + spdlog::info("Day: {}", current_time_); + } + begin_time_step(); + daily_update(); + end_time_step(); + calendar_date += date::days{1}; + } +} + +void Scheduler::begin_time_step() const { + if (model_ != nullptr) { + model_->begin_time_step(); + } +} + +void Scheduler::daily_update() const { + if (model_ != nullptr) { + model_->daily_update(); + + if (is_today_first_day_of_month()) { + model_->monthly_update(); + } + + if (is_today_first_day_of_year()) { + model_->yearly_update(); + } + + // Executing population-related events + // execute_events_list(population_events_list_[current_time_]); + + // Executing individual-related events + // execute_events_list(individual_events_list_[current_time_]); + } +} + +void Scheduler::end_time_step() const { + if (model_ != nullptr) { + model_->end_time_step(); + } +} + +bool Scheduler::can_stop() const { + return current_time_ > model_->get_config()->get_simulation_timeframe().get_total_time(); +} + +int Scheduler::current_day_in_year() const { + return utils::Time::instance().day_of_year(calendar_date); +} + +int Scheduler::current_month_in_year() const { + return utils::Time::instance().month_of_year(calendar_date); +} + +bool Scheduler::is_today_last_day_of_year() const { + date::year_month_day ymd{calendar_date}; + return ymd.month() == date::month{12} && ymd.day() == date::day{31}; +} + +bool Scheduler::is_today_first_day_of_month() const { + date::year_month_day ymd{calendar_date}; + return ymd.day() == date::day{1}; +} + +bool Scheduler::is_today_first_day_of_year() const { + date::year_month_day ymd{calendar_date}; + return ymd.month() == date::month{1} && ymd.day() == date::day{1}; +} + +bool Scheduler::is_today_last_day_of_month() const { + const auto next_date = calendar_date + date::days{1}; + date::year_month_day ymd{next_date}; + return ymd.day() == date::day{1}; +} diff --git a/src/Core/Scheduler/Scheduler.h b/src/Core/Scheduler/Scheduler.h new file mode 100644 index 0000000..fcc9102 --- /dev/null +++ b/src/Core/Scheduler/Scheduler.h @@ -0,0 +1,84 @@ +#ifndef SCHEDULER_H +#define SCHEDULER_H + +#include +#include "date/date.h" +#include "Simulation/Model.h" // Assuming Model is defined in a separate header file + +class Scheduler { +public: + // Disable copy and assignment + Scheduler(const Scheduler&) = delete; + Scheduler& operator=(const Scheduler&) = delete; + Scheduler(Scheduler&&) = delete; + Scheduler& operator=(Scheduler&&) = delete; + + int current_time_; + int total_available_time_; + Model* model_; + bool is_force_stop_; + + date::sys_days calendar_date; + + explicit Scheduler(Model *model = nullptr); + + virtual ~Scheduler(); + + // Getter and Setter for current_time + [[nodiscard]] int current_time() const { return current_time_; } + void set_current_time(int time) { current_time_ = time; } + + // Getter and Setter for total_available_time + [[nodiscard]] int total_available_time() const { return total_available_time_; } + void set_total_available_time(int total_time) { total_available_time_ = total_time; } + + // Getter and Setter for model + [[nodiscard]] Model* model() const { return model_; } + void set_model(Model* model) { model_ = model; } + + // Getter and Setter for is_force_stop + [[nodiscard]] bool is_force_stop() const { return is_force_stop_; } + void set_is_force_stop(bool force_stop) { is_force_stop_ = force_stop; } + + void extend_total_time(int new_total_time); + + void clear_all_events(); + +// void clear_all_events(EventPtrVector2 &events_list) const; +// +// virtual void schedule_individual_event(Event *event); +// +// virtual void schedule_population_event(Event *event); +// +// virtual void schedule_event(EventPtrVector &time_events, Event *event); +// +// virtual void cancel(Event *event); +// +// void execute_events_list(EventPtrVector &events_list) const; + + void initialize(const date::year_month_day &starting_date, const int &total_time); + + void run(); + + void begin_time_step() const; + + void end_time_step() const; + + [[nodiscard]] bool can_stop() const; + + [[nodiscard]] int current_day_in_year() const; + + [[nodiscard]] int current_month_in_year() const; + + [[nodiscard]] bool is_today_last_day_of_month() const; + + [[nodiscard]] bool is_today_first_day_of_month() const; + + [[nodiscard]] bool is_today_first_day_of_year() const; + + [[nodiscard]] bool is_today_last_day_of_year() const; + + void daily_update() const; +}; + +#endif /* SCHEDULER_H */ diff --git a/src/Simulation/Model.cpp b/src/Simulation/Model.cpp index d80a106..5fb1143 100644 --- a/src/Simulation/Model.cpp +++ b/src/Simulation/Model.cpp @@ -1,30 +1,31 @@ -#include "Model.h" - #include #include - +#include "Model.h" +#include #include "Configuration/Config.h" // Assuming Config is defined here +#include "Utils/Cli.h" // Private constructor: creates the Config instance Model::Model() : config_(std::make_unique()), - config_file_path_("config.yml"), + scheduler_(std::make_unique(this)), config_file_path_("input.yml"), is_initialized_(false) { - // Constructor does not load the configuration file - // Initialization is deferred to the Initialize() method } -void Model::initialize() { +bool Model::initialize() { if (!config_file_path_.empty()) { + config_file_path_ = utils::Cli::get_instance().get_input_file(); // Load the configuration file - config_->load(config_file_path_); - - std::cout << "Model initialized with configuration file: " - << config_file_path_ << "\n"; - is_initialized_ = true; - } else { - throw std::invalid_argument("Configuration file path must be provided."); + if(config_->load(config_file_path_)) { + std::cout << "Model initialized with configuration file: " + << config_file_path_ << "\n"; + is_initialized_ = true; + } + else { + spdlog::error("Failed to load configuration file: " + config_file_path_); + } } + return is_initialized_; } void Model::run() const { @@ -32,7 +33,7 @@ void Model::run() const { throw std::runtime_error( "Model is not initialized. Call Initialize() first."); } - // Simulation run code + scheduler_->run(); } void Model::finalize() { @@ -43,3 +44,24 @@ void Model::finalize() { config_.reset(); // Automatically handled by unique_ptr, but explicitly // showing intent } + +void Model::begin_time_step() { + // spdlog::info("\t\t\tBegin time step"); +}/**/ + +void Model::end_time_step() { + // spdlog::info("\t\t\tEnd time step"); +} + +void Model::daily_update() { + // spdlog::info("\tDaily update"); +} + +void Model::monthly_update() { + // spdlog::info("\t\tMonthly update"); +} + +void Model::yearly_update() { + // spdlog::info("\tYearly update"); +} + diff --git a/src/Simulation/Model.h b/src/Simulation/Model.h index bc12954..dad49f5 100644 --- a/src/Simulation/Model.h +++ b/src/Simulation/Model.h @@ -6,6 +6,9 @@ // Forward declaration class Config; +class Random; +class Cli; +class Scheduler; class Model { public: @@ -16,7 +19,7 @@ class Model { } // Initialize the model - void initialize(); + bool initialize(); // Run the simulation void run() const; @@ -33,6 +36,15 @@ class Model { return config_.get(); } + // Access scheduler in a controlled manner + [[nodiscard]] const Scheduler* get_scheduler() const { + if (!scheduler_) { + throw std::runtime_error( + "Model not initialized. Call Initialize() first."); + } + return scheduler_.get(); + } + // Prevent copying and moving Model(const Model &) = delete; Model(Model &&) = delete; @@ -45,12 +57,21 @@ class Model { ~Model() = default; // Configuration managed by a smart pointer - std::unique_ptr config_; + std::shared_ptr config_; + + std::shared_ptr scheduler_; // Configuration file path with default value std::string config_file_path_; bool is_initialized_; +public: + void begin_time_step(); + void end_time_step(); + void daily_update(); + void monthly_update(); + void yearly_update(); + }; #endif // MODEL_H diff --git a/src/Simulation/main.cpp b/src/Simulation/main.cpp index 680cac8..a1d5e30 100644 --- a/src/Simulation/main.cpp +++ b/src/Simulation/main.cpp @@ -1,10 +1,19 @@ +#include + +#include "Utils/Cli.h" +#include "Model.h" #include "Utils/Logger.h" #include "Configuration/Config.h" -int main() { +int main(int argc, char** argv) { Logger::Instance().Initialize(spdlog::level::info); - spdlog::get("default_logger")->info("Hello from MalaSim!"); - Config *config = new Config(); - config->load("../../sample_inputs/input.yml"); + utils::Cli::get_instance().parse(argc, argv); + if(Model::instance().initialize()) { + Model::instance().run(); + Model::instance().finalize(); + } + else { + spdlog::get("default_logger")->error("Model initialization failed."); + } return 0; } diff --git a/src/Utils/Cli.h b/src/Utils/Cli.h new file mode 100644 index 0000000..93229a0 --- /dev/null +++ b/src/Utils/Cli.h @@ -0,0 +1,60 @@ +#include +#include + +namespace utils{ +class Cli { +public: + // Static method to get the single instance of the class + static Cli& get_instance() { + static Cli instance; // Guaranteed to be destroyed and instantiated on first use. + return instance; + } + + // Delete copy constructor and assignment operator + Cli(const Cli&) = delete; + Cli& operator=(const Cli&) = delete; + + // Parse function + void parse(int argc, char** argv) { + try { + app.parse(argc, argv); + } catch (const CLI::ParseError &e) { + std::exit(app.exit(e)); + } + } + + // Accessors for parameters + std::string get_input_file() const { return input_file; } + int get_cluster_job_number() const { return cluster_job_number; } + std::string get_reporter() const { return reporter; } + std::string get_output_path() const { return output_path; } + int get_verbosity() const { return verbosity; } + bool is_help_requested() const { return help; } + +private: + // Private constructor for Singleton + Cli() { + app.description("Individual-based simulation for malaria."); + + // Command options with required() to make them compulsory + app.add_option("-i,--input,--config", input_file, "The config file (YAML format) (Required). \nEx: MaSim -i input.yml")->required(); + app.add_option("-j,--job", cluster_job_number, "Cluster job number (Optional). \nEx: MaSim -j 1"); + app.add_option("-r,--reporter", reporter, "Reporter Type (Optional). \nEx: MaSim -r MonthlyReporter"); + app.add_option("-o,--output", output_path, "Path for output files, default is current directory (Optional). \nEx: MaSim -o out"); + + // Global options + app.add_option("--v", verbosity, "Sets the current verbosity of the logging, default zero"); + } + + CLI::App app{"Individual-based simulation for malaria"}; + + // Parameters + std::string input_file; + int cluster_job_number; + std::string reporter; + std::string output_path; + int verbosity = 0; + bool help = false; +}; +} // namespace utils + diff --git a/src/Utils/Time.h b/src/Utils/Time.h new file mode 100644 index 0000000..0811631 --- /dev/null +++ b/src/Utils/Time.h @@ -0,0 +1,139 @@ +#ifndef TIME_H +#define TIME_H + +#include +#include +#include + +namespace utils { + + class Time { + public: + // Delete copy and move constructors to ensure single instance + Time(const Time&) = delete; + Time& operator=(const Time&) = delete; + Time(Time&&) = delete; + Time& operator=(Time&&) = delete; + + // Access the singleton instance + static Time& instance() { + static Time instance; + return instance; + } + + // Get the current time in milliseconds + long long current_time_millis() const { + return std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()).count(); + } + + // Get the current time in seconds + long long current_time_seconds() const { + return std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()).count(); + } + + // Get the current time in minutes + long long current_time_minutes() const { + return std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()).count(); + } + + // Get the current time in hours + long long current_time_hours() const { + return std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()).count(); + } + + // Get the current time in days + long long current_time_days() const { + return std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()).count(); + } + + // Get the current time in weeks + long long current_time_weeks() const { + return current_time_days() / 7; + } + + // Get the current time in months + long long current_time_months() const { + return current_time_days() / 30; + } + + // Get the current time in years + long long current_time_years() const { + return current_time_days() / 365; + } + + // Get the current date + date::year_month_day current_date() const { + return date::year_month_day(date::floor( + std::chrono::system_clock::now())); + } + + int get_day_count(const date::year_month_day& start_date, const date::year_month_day& end_date) const { + return (date::sys_days{end_date} - date::sys_days{start_date}).count(); + } + + // Helper functions (merged from TimeHelpers) + + int number_of_days(const int &y1, const unsigned int &m1, const unsigned int &d1, + const int &y2, const unsigned int &m2, const unsigned int &d2) const { + using namespace date; + return (sys_days{year{y2}/month{m2}/day{d2}} - sys_days{year{y1}/month{m1}/day{d1}}).count(); + } + + int number_of_days(const date::sys_days &first, const date::sys_days &last) const { + return (last - first).count(); + } + + template + T convert_to(const std::string &input) const { + T result{}; + std::stringstream ss(input); + date::from_stream(ss, "%Y/%m/%d", result); + return result; + } + + int number_of_days_to_next_year(const date::sys_days &today) const { + const date::sys_days next_year{date::year_month_day{today} + date::years{1}}; + return number_of_days(today, next_year); + } + + int get_simulation_time_birthday(const int &days_to_next_birthday, const int &age, + const date::sys_days &starting_day) const { + const auto calendar_birthday = date::floor( + starting_day + date::days{days_to_next_birthday + 1} - date::years{age + 1}); + return number_of_days(starting_day, calendar_birthday); + } + + int day_of_year(const int &y, const unsigned &m, const unsigned &d) const { + using namespace date; + if (m < 1 || m > 12 || d < 1 || d > 31) return 0; + return (sys_days{year{y}/month{m}/day{d}} - sys_days{year{y}/jan/0}).count(); + } + + int day_of_year(const date::sys_days &day) const { + date::year_month_day ymd{day}; + return number_of_days(date::sys_days{ymd.year()/1/0}, day); + } + + int month_of_year(const date::sys_days &day) const { + date::year_month_day ymd{day}; + return static_cast(ymd.month()); + } + + private: + // Private constructor to prevent instantiation + Time() = default; + }; + + // Overload for printing date::sys_days + inline std::ostream &operator<<(std::ostream &stream, const date::sys_days &o_days) { + stream << date::year_month_day{o_days}; + return stream; + } +} + +#endif //TIME_H diff --git a/vcpkg.json b/vcpkg.json index 654ea46..0528997 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -7,6 +7,7 @@ "sol2", "spdlog", "yaml-cpp", - "date" + "date", + "cli11" ] }