diff --git a/.gitignore b/.gitignore index 0f0dc8e700..9843daf52c 100644 --- a/.gitignore +++ b/.gitignore @@ -85,4 +85,4 @@ cyclus/cycpp.py # Rever rever/ -.vscode +.vscode \ No newline at end of file diff --git a/CHANGELOG.rst b/CHANGELOG.rst index c39efff0a9..75440c14e6 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -34,7 +34,9 @@ Since last release * Allow multiple archetype blocks to facilitate includes (#1874) **Changed:** + * Made the Unit Tests far less verbose by suppressing log output during RunSim (#1927) +* Reworked the facility/instituion/region_cost.cycpp.h files to work with MC (#1931) * Changed Dockerfile to use boost and boost-cpp instead of libboost-devel (#1906) * Changed TradeExecutor to use adjusted preferences from ExchangeContext (#1897) * Ran clang-format on src directory (#1881, #1893) diff --git a/agents/null_region.cc b/agents/null_region.cc index 055f90aefd..da1f5762c4 100644 --- a/agents/null_region.cc +++ b/agents/null_region.cc @@ -6,9 +6,7 @@ NullRegion::NullRegion(cyclus::Context* ctx) : cyclus::Region(ctx) {} NullRegion::~NullRegion() {} -void NullRegion::EnterNotify() { - InitEconParameters(); -} +void NullRegion::EnterNotify() {} extern "C" cyclus::Agent* ConstructNullRegion(cyclus::Context* ctx) { return new NullRegion(ctx); diff --git a/agents/null_region.h b/agents/null_region.h index eec1efb479..49964ef5b2 100644 --- a/agents/null_region.h +++ b/agents/null_region.h @@ -22,9 +22,6 @@ class NullRegion : public cyclus::Region { "institutions but exhibits null behavior. " \ "No parameters are given when using the " \ "null region."} - - private: - #include "toolkit/region_cost.cycpp.h" }; } // namespace cyclus diff --git a/src/context.cc b/src/context.cc index 616e5a5af8..51324d8158 100644 --- a/src/context.cc +++ b/src/context.cc @@ -348,6 +348,17 @@ void Context::UnregisterTimeListener(TimeListener* tl) { ti_->UnregisterTimeListener(tl); } +void Context::UnregisterCommodityConsumer(std::set in_commods, Trader* e){ + return; + for (std::string commod : in_commods){ + commodity_consumers_.at(commod).erase(e); + } + } + +void Context::RegisterCommoditiesTraded(int t, std::set trade_commods){ + commodities_traded_[t].merge(trade_commods); +} + Datum* Context::NewDatum(std::string title) { return rec_->NewDatum(title); } diff --git a/src/context.h b/src/context.h index 0e5b925de4..e30a2d78f7 100644 --- a/src/context.h +++ b/src/context.h @@ -183,6 +183,38 @@ class Context { /// @return the current set of traders registered for resource exchange. inline const std::set& traders() const { return traders_; } + ////////////////////////////////////////////////////////// discrete cyclus functions /////////////////// + + inline void RegisterCommodityConsumer(std::string in_commod, Trader* e){commodity_consumers_[in_commod].insert(e);} + + void UnregisterCommodityConsumer(std::set in_commods, Trader* e); + + inline const std::map>& consumers() const {return commodity_consumers_;} + + + void RegisterCommoditiesTraded(int t, std::set trade_commods); + + inline std::set& CommoditiesTraded(int t) {return commodities_traded_.at(t);} + + inline void RegisterRequesters(int time, Trader* e) {request_queue_[time].insert(e); + } //conditions to register an event is up to archetype dev + + inline void EventComplete(int t) {request_queue_.erase(t);} // fit this so it purges any 0 entries as well as the most current completed event ! + + inline const std::set& EventRequesters(int t) const {return request_queue_.at(t); } + + inline const std::map>& EventTimeline() const { return request_queue_; } + + //// currently unused + + inline void Populate(int t) {if (pop_sched_.count(t)>0){request_queue_.at(t) = traders();};} //there are more use cases for this + + inline void SchedPopulate(int next_event) {pop_sched_.insert(next_event); request_queue_[next_event];} // there are more use cases for this + + inline void DeregisterRequesters(int time, Trader* e) {request_queue_.at(time).erase(e);} //conditions to deregister an event is up to archetype dev + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + /// Create a new agent by cloning the named prototype. The returned agent is /// not initialized as a simulation participant. /// @@ -364,16 +396,20 @@ class Context { /// contains archetype specs of all agents for which version have already /// been recorded in the db std::set rec_ver_; - std::map protos_; std::map recipes_; std::map packages_; std::map transport_units_; std::set agent_list_; std::set traders_; + + std::map> request_queue_; + std::map> commodity_consumers_; + std::map> commodities_traded_; + std::set pop_sched_; + std::map n_prototypes_; std::map n_specs_; - SimInfo si_; Timer* ti_; ExchangeSolver* solver_; diff --git a/src/cyclus.h b/src/cyclus.h index dfa37ec0fa..ff6aa58daa 100644 --- a/src/cyclus.h +++ b/src/cyclus.h @@ -79,6 +79,7 @@ extern "C" { #include "toolkit/symbolic_function_factories.h" #include "toolkit/symbolic_functions.h" #include "toolkit/timeseries.h" +#include "toolkit/scheduling_function.h" // Undefines isnan from pyne #ifdef isnan diff --git a/src/facility.cc b/src/facility.cc index bfcad159d8..beb04fbb37 100644 --- a/src/facility.cc +++ b/src/facility.cc @@ -10,10 +10,12 @@ #include "institution.h" #include "logger.h" #include "timer.h" +#include +#include namespace cyclus { -Facility::Facility(Context* ctx) : Trader(this), Agent(ctx) { +Facility::Facility(Context* ctx) : Trader(this), Agent(ctx), schedule_helper_(this) { kind_ = std::string("Facility"); } @@ -24,13 +26,33 @@ void Facility::InitFrom(Facility* m) { } void Facility::Build(Agent* parent) { - Agent::Build(parent); + Agent::Build(parent); + //for agents WITHOUT the need for a checkdecom status, they can easily schedule decom at build (only reactor/separations use this right now) + if (lifetime() >= 0 && CheckDecommissionCondition() == NULL) { + context()->SchedDecom(this, exit_time()); + } + for (auto& requests: GetMatlRequests()) { + if(requests){ + for(auto& request : requests->requests()) { + // //hopefully this is a dry run with no impact on DRE (ie adding porfolios) + std::string commodity = request->commodity(); + context()->RegisterCommodityConsumer(commodity,this); + FillInCommods(commodity); //any repeats should be + } + } + } + std::cout<<"Agent of type" << prototype()<< " has " << GetInCommods().size()<<" in_commods_ size in Trader \n\n\n"; + // FORCEFULLY SET THE COMMOD MAP (not relying on tick/tock event request) + context()-> RegisterCommoditiesTraded(context()->time(), GetInCommods()); + std::cout <<"Context commodity map size called in Facility::Build is "<< (context()->CommoditiesTraded(context()->time())).size()<<" (not empty) \n\n\n\n"; } void Facility::EnterNotify() { Agent::EnterNotify(); context()->RegisterTrader(dynamic_cast(this)); context()->RegisterTimeListener(this); + schedule_helper_.InitialTrade(); + //all agents who have been build should want to immediately trade } std::string Facility::str() { @@ -41,17 +63,34 @@ std::string Facility::str() { } void Facility::Decommission() { - if (!CheckDecommissionCondition()) { + if (!CheckDecommissionCondition()) { //check what happens w NULL throw Error("Cannot decommission " + prototype()); } context()->UnregisterTrader(dynamic_cast(this)); context()->UnregisterTimeListener(this); + context()->UnregisterCommodityConsumer(GetInCommods(),this); + Agent::Decommission(); } bool Facility::CheckDecommissionCondition() { - return true; + return NULL; +} + +void Facility::Tock(){ // archetype developers need to invoke this method in tock + EventRequest(); +} + +void Facility::Tick(){ + SetTraded(false); //archetype developers need to invoke this method in tick +} + +void Facility::EventRequest(){ + schedule_helper_.FixIncSchedule(); //FixIncSchedule schedules like cyclus 1.6v + for(int i: schedule_helper_.EventTime()){ //probably needed in future... + selftimes_.insert(i); + } } Region* Facility::GetParentRegion(int layer) { diff --git a/src/facility.h b/src/facility.h index 2b12657c63..8a108e2d06 100644 --- a/src/facility.h +++ b/src/facility.h @@ -8,6 +8,7 @@ #include "agent.h" #include "time_listener.h" #include "trader.h" +#include "toolkit/scheduling_function.h" namespace cyclus { @@ -100,7 +101,7 @@ class Facility : public TimeListener, public Agent, public Trader { /// facilities over write this method if a condition must be met /// before their destructors can be called - virtual bool CheckDecommissionCondition(); + virtual bool CheckDecommissionCondition(); /// every agent should be able to print a verbose description virtual std::string str(); @@ -127,6 +128,24 @@ class Facility : public TimeListener, public Agent, public Trader { return std::set::Ptr>(); } + virtual void EventRequest(); //maybe an archetype dev will want to replace this + // this is intended to be a default behavior should an archetype developer not invoke their own "Scheduling Function " + //within cycamore + // void EventRequest(){ + //schedule_helper_.(args);; + // or more directly schedule_helper.schedule(time,commods) + //} + + virtual void Tock(); + + virtual void Tick(); + + //return all future events scheduled for some facility (this function is useful for deregistration context()->DeregisterRequesters(--) + // purposes and for archetype developer scheduled facility behavior) + inline const std::set& GetFutureEvents() const { + return selftimes_; + } + /// default implementation for material preferences. virtual void AdjustMatlPrefs(PrefMap::type& prefs) {} @@ -139,7 +158,6 @@ class Facility : public TimeListener, public Agent, public Trader { virtual void GetMatlTrades( const std::vector>& trades, std::vector, Material::Ptr>>& responses) { - std::cout << "in material facility getmatltrades\n"; } /// @brief default implementation for responding to product trades @@ -193,6 +211,11 @@ class Facility : public TimeListener, public Agent, public Trader { /// @brief Returns all parent facilities by traversing up the hierarchy /// @return Vector of all parent facilities, ordered from closest to farthest std::vector GetAllParentFacilities(); + + + private: + std::set selftimes_; + toolkit::SchedulingFunctions schedule_helper_; }; } // namespace cyclus diff --git a/src/institution.cc b/src/institution.cc index d8b03aae78..9b9f0fd2c1 100644 --- a/src/institution.cc +++ b/src/institution.cc @@ -50,7 +50,7 @@ void Institution::Tock() { Agent* a = *it; if (a->lifetime() != -1 && context()->time() >= a->exit_time()) { Facility* fac = dynamic_cast(a); - if (fac == NULL || fac->CheckDecommissionCondition()) { + if (fac == NULL || fac->CheckDecommissionCondition()) { // any facility without overwritten checkdecomcondition expects a NULL, only true will pass || CLOG(LEV_INFO3) << a->prototype() << " has reached the end of its lifetime"; context()->SchedDecom(a); diff --git a/src/resource_exchange.h b/src/resource_exchange.h index 2b73e0675f..41da99d987 100644 --- a/src/resource_exchange.h +++ b/src/resource_exchange.h @@ -67,10 +67,10 @@ template class ResourceExchange { inline ExchangeContext& ex_ctx() { return ex_ctx_; } /// @brief queries traders and collects all requests for bids - void AddAllRequests() { - InitTraders(); - std::for_each(traders_.begin(), - traders_.end(), + void AddAllRequests() { // I think we should add other commodity consumers of type a into the mix within InitRequesters() as well. + InitRequesters(); + std::for_each(requesters_.begin(), + requesters_.end(), std::bind(&cyclus::ResourceExchange::AddRequests_, this, std::placeholders::_1)); @@ -112,6 +112,24 @@ template class ResourceExchange { } } + void InitRequesters() { + auto orig = InitRequestersAdjacent(); //we do not need the whole ass map tbh. + std::set::iterator it; + for (it = orig.begin(); it != orig.end(); ++it) { + requesters_.insert(*it); + } + } + + std::set InitRequestersAdjacent() { + std::set traders; + auto commod_map = sim_ctx_->consumers(); + auto map2 = sim_ctx_->CommoditiesTraded(sim_ctx_->time()); + for (std::string commods : map2){ + traders.merge(commod_map[commods]); + } + return traders; + } + /// @brief queries a given facility agent for void AddRequests_(Trader* t) { std::set::Ptr> rp = QueryRequests(t); @@ -160,7 +178,7 @@ template class ResourceExchange { // determinism of Cyclus overall. This allows all traders' resource // exchange functions are called in a much closer to deterministic order. std::set traders_; - + std::set requesters_; Context* sim_ctx_; ExchangeContext ex_ctx_; }; diff --git a/src/timer.cc b/src/timer.cc index 86ae859de5..542dc366c0 100644 --- a/src/timer.cc +++ b/src/timer.cc @@ -29,14 +29,18 @@ void Timer::RunSim() { ExchangeManager matl_manager(ctx_); ExchangeManager genrsrc_manager(ctx_); + + //the following 3 lines are to add an upper limit to the 3 event maps so that the there is always a final event in "queue" + ctx_->SchedPopulate(dur()+1); + build_queue_[dur()+1]; // find a better home + decom_queue_[dur()+1]; // find a better home + while (time_ < si_.duration) { CLOG(LEV_INFO1) << "Current time: " << time_; - if (want_snapshot_) { want_snapshot_ = false; SimInit::Snapshot(ctx_); } - // run through phases DoBuild(); CLOG(LEV_INFO2) << "Beginning Tick for time: " << time_; @@ -52,9 +56,8 @@ void Timer::RunSim() { #ifdef CYCLUS_WITH_PYTHON EventLoop(); #endif - - time_++; - + time_ = NextEvent(); + if (want_kill_) { break; } @@ -76,6 +79,7 @@ void Timer::RunSim() { void Timer::DoBuild() { // build queued agents std::vector> build_list = build_queue_[time_]; + //if build_list is empty at time, already no one builds i.e. "skips" event for (int i = 0; i < build_list.size(); ++i) { Agent* m = ctx_->CreateAgent(build_list[i].first); Agent* parent = build_list[i].second; @@ -85,7 +89,6 @@ void Timer::DoBuild() { if (parent != NULL) { parent->BuildNotify(m); } else { - CLOG(LEV_DEBUG1) << "Hey! Listen! Built an Agent without a Parent."; } } } @@ -96,6 +99,7 @@ void Timer::DoTick() { } #pragma omp parallel for + //everyone ticks for all events (Cyclus 04/21 notes) for (size_t i = 0; i < cpp_tickers_.size(); ++i) { cpp_tickers_[i]->Tick(); } @@ -103,11 +107,18 @@ void Timer::DoTick() { void Timer::DoResEx(ExchangeManager* matmgr, ExchangeManager* genmgr) { - matmgr->Execute(); - genmgr->Execute(); + auto reg_traders = ctx_->EventRequesters(time_); + std::cout <<"Context commodity map size called in Timer is"<< (ctx_->CommoditiesTraded(time_)).size()<<" (empty) \n\n\n\n"; + std::cout<<"Context discrete-registered traders map size is "<0){ + matmgr->Execute(); + genmgr->Execute(); + } } void Timer::DoTock() { + //everyone tocks for all events (Cyclus 04/21 notes) for (TimeListener* agent : py_tickers_) { agent->Tock(); } @@ -128,6 +139,17 @@ void Timer::DoTock() { } } } + auto reg_traders = ctx_->EventTimeline(); + if(reg_traders[time_].size()>0){ + ctx_->EventComplete(time_); //to dereference some pointers, maybe applied to build/decom maps too + + if(reg_traders.count(reg_traders.lower_bound(time_ +1)->first) == 0){ + //if archetype developer has some ctx_->DeregisterRequester behavior + //this looks for instances that previously registered -upcoming- events + //have been emptied of requesters + ctx_->EventComplete(reg_traders.lower_bound(time_ + 1)->first); + } + } } void Timer::DoDecision() { @@ -190,6 +212,7 @@ void Timer::RecordInventory(Agent* a, std::string name, Material::Ptr m) { void Timer::DoDecom() { // decommission queued agents std::vector decom_list = decom_queue_[time_]; + //if decom_list is empty at time, already no one decommissions i.e. "skips" event for (int i = 0; i < decom_list.size(); ++i) { Agent* m = decom_list[i]; if (m->parent() != NULL) { @@ -220,6 +243,14 @@ void Timer::UnregisterTimeListener(TimeListener* tl) { } } +int Timer::NextEvent(){ + auto reg_traders = ctx_->EventTimeline(); + int t_p = time_ +1; + std::vector event_lists = {decom_queue_.lower_bound(t_p)->first,build_queue_.lower_bound(t_p)->first, + reg_traders.lower_bound(t_p)->first}; + return *std::min_element(event_lists.begin(), event_lists.end()); +} + void Timer::SchedBuild(Agent* parent, std::string proto_name, int t) { if (t <= time_) { throw ValueError("Cannot schedule build for t < [current-time]"); @@ -276,7 +307,6 @@ void Timer::Initialize(Context* ctx, SimInfo si) { if (si.m0 < 1 || si.m0 > 12) { throw ValueError("Invalid month0; must be between 1 and 12 (inclusive)."); } - want_kill_ = false; ctx_ = ctx; time_ = 0; diff --git a/src/timer.h b/src/timer.h index 43eadd323b..cd300fa805 100644 --- a/src/timer.h +++ b/src/timer.h @@ -50,6 +50,8 @@ class Timer { /// Agents should unregister from their Decommission method. void UnregisterTimeListener(TimeListener* tl); + int NextEvent(); + /// Schedules the named prototype to be built for the specified parent at /// timestep t. void SchedBuild(Agent* parent, std::string proto_name, int t); @@ -106,7 +108,6 @@ class Timer { /// The current time, measured in months from when the simulation /// started. int time_; - SimInfo si_; bool want_snapshot_; diff --git a/src/toolkit/facility_cost.cycpp.h b/src/toolkit/facility_cost.cycpp.h deleted file mode 100644 index bd99a6a87e..0000000000 --- a/src/toolkit/facility_cost.cycpp.h +++ /dev/null @@ -1,245 +0,0 @@ -/// This includes the required header to add facility costs to archetypes. -/// One should only need to: -/// - '#include "toolkit/facility_cost.cycpp.h"' in the header of the -/// archetype class (as private) -/// - Add `InitEconParameters()` to `EnterNotify()` in the cc file of the -/// archetype class. - -/// How to add parameters to this file: -/// 1. Add the pragma. A default value MUST be added to ensure backwards -/// compatibility. -/// 2. Edit the unordered_map called "econ_params" -/// i. add the desired parameter to the array {"name", value} -/// ii. the value of the pair should be the variable name exactly -/// 3. Add "std::vector cycpp_shape_ = {0};" to the end of the -/// file with the other ones, reaplcing with the name you put -/// in the econ_params array (again, must match exactly). - -// clang-format off -#pragma cyclus var {"default" : 0.0, \ - "uilabel" : "Capital cost required to build facility", \ - "doc" : "Capital cost required to build facility", \ - "units" : "Unit of Currency" } -double capital_cost; - -#pragma cyclus var { \ - "default": 0.0, \ - "uilabel": "Property Tax Rate as decimal", \ - "range": [0.0, 1.0], \ - "doc": "Property tax rate for this facility as decimal (1% --> 0.01)" \ - } -double property_tax_rate; - -#pragma cyclus var { \ - "default" : 0.0, "uilabel" : "Annual Operations and Maintenance Cost", \ - "doc" : "Annual Operations and Maintenance Cost required to run facility", \ - "units" : "Unit of Currency" } -double annual_operations_and_maintenance; - -#pragma cyclus var { \ - "default": 1.0, \ - "uilabel": "Estimated Useful lifetime of facility for economic purposes in years", \ - "doc": "Estimate on how long the facility will be active for economic purposes", \ - "units": "years" \ - } -double facility_operational_lifetime; - -#pragma cyclus var { \ - "default": 1.0, \ - "uilabel": "Taxable lifetime of facility for economic purposes in years", \ - "doc": "How long the facility will be depreciating their initial investment", \ - "units": "years" \ - } -double facility_depreciation_lifetime; - -// We maybe want this to be more like a line-item not per unit? -#pragma cyclus var { \ - "default": 0.0, \ - "uilabel": "Annual cost of labor", \ - "doc": "Annual cost of labor", \ - "units": "Unit of Currency" \ - } -double annual_labor_cost; - -#pragma cyclus var { \ - "default": -1.0, \ - "uilabel": "Cost in dollars of one unit of production", \ - "doc": "(optional) Hook to bypass LCP calculation and provide a cost in dollars", \ - "units": "Dimensionless" \ - } -double cost_override; - -#pragma cyclus var { \ - "default": 0.0, \ - "uilabel": "Property Insurance Rate as decimal", \ - "range": [0.0, 1.0], \ - "doc": "Property insurance rate for this facility as decimal (1% --> 0.01)" \ - } -double property_insurance_rate; -// clang-format on - -// Must be done in a function so that we can access the user-defined values -std::unordered_map GenerateParamList() const override { - std::unordered_map econ_params{ - {"capital_cost", capital_cost}, - {"property_tax_rate", property_tax_rate}, - {"annual_operations_and_maintenance", annual_operations_and_maintenance}, - {"facility_operational_lifetime", facility_operational_lifetime}, - {"facility_depreciation_lifetime", facility_depreciation_lifetime}, - {"annual_labor_cost", annual_labor_cost}, - {"property_insurance_rate", property_insurance_rate}}; - - return econ_params; -} - -/// @brief Calculates the levelized unit cost of production, accounting for -/// capital depreciation, O&M, labor, property taxes, and input costs. -/// -/// unit_cost = cost_override if cost_override > 0, otherwise unit_cost = -/// production_cost + material_cost -/// -/// Where: -/// - production_cost = levelized_fixed_costs/units_produced_annually + -/// levelized_variable_costs + (initial_investment/units_produced_annually) * -/// [property_tax_insurance_rate + (1/(1-income_tax_rate)) * -/// PMT(facility_lifetime, tax_modified_rate_of_return, 1, 0) - -/// (1/facility_lifetime) * (income_tax_rate/(1-income_tax_rate))] -/// - material_cost = Unit Cost of Material (weighted average of input material -/// unit values) -/// - units_produced_annually = production_capacity * timesteps_per_year -/// - levelized_fixed_costs = annual_operations_and_maintenance -/// - levelized_variable_costs = annual_labor_cost -/// - initial_investment = capital_cost -/// - property_and_insurance_rate = property_tax_rate + property_insurance_rate -/// - tax_modified_rate_of_return = (1-income_tax_rate)*bond_rate*bond_fraction -/// + shareholder_rate*shareholder_fraction -/// - income_tax_rate = Income Tax Rate -/// - facility_lifetime = facility_depreciation_lifetime -/// -/// The model assumes straight-line depreciation over the facility lifetime. -/// -/// @param production_capacity Maximum throughput per timestep -/// @param units_to_produce Number of units produced in the batch -/// @param input_cost (Optional) Total cost of input materials used in the batch -/// @return Estimated levelized cost to produce one unit -double CalculateUnitCost(double production_capacity, double units_to_produce, - double input_cost_per_unit = 0.0) const { - // Check if there's a cost override, and if so, use that - if (cost_override > 0) { - return cost_override + input_cost_per_unit; - } - - // Economic Parameters (required for the try catch block) - double initial_investment; - double facility_lifetime; - double levelized_fixed_costs; - double levelized_variable_costs; - double property_and_insurance_rate; - double tax_modified_rate_of_return; - double income_tax_rate; - double bond_rate; - double bond_fraction; - double shareholder_rate; - double shareholder_fraction; - double property_insurance_rate; - - // Get facility-level parameters - try { - initial_investment = GetEconParameter("capital_cost"); - facility_lifetime = GetEconParameter("facility_depreciation_lifetime"); - property_insurance_rate = GetEconParameter("property_insurance_rate"); - - // Note: Since our fixed and variable costs are the same every timestep, we - // call them levelized here. If that changes in the future, we need to - // change this to reflect actual levelization. Additionally, if more - // fixed and variable costs are added in the future, we need to change this - // to reflect that as well. - levelized_fixed_costs = - GetEconParameter("annual_operations_and_maintenance"); - levelized_variable_costs = GetEconParameter("annual_labor_cost"); - - } catch (const std::exception& e) { - LOG(cyclus::LEV_INFO1, "CalculateUnitCost") - << prototype() - << "failed to get facility financial_data_: " << e.what(); - return kDefaultUnitCost; - } - - // Get institution-level parameters - try { - income_tax_rate = parent()->GetEconParameter("corporate_income_tax_rate"); - bond_rate = parent()->GetEconParameter("bond_holders_rate_of_return"); - bond_fraction = parent()->GetEconParameter("fraction_bond_financing"); - shareholder_rate = - parent()->GetEconParameter("share_holders_rate_of_return"); - shareholder_fraction = - parent()->GetEconParameter("fraction_private_capital"); - } catch (const std::exception& e) { - LOG(cyclus::LEV_INFO1, "CalculateUnitCost") - << prototype() - << "failed to get institution financial_data_: " << e.what(); - return kDefaultUnitCost; - } - - // Get region-level parameters - try { - double property_tax_rate = - parent()->parent()->GetEconParameter("property_tax_rate"); - property_and_insurance_rate = property_tax_rate + property_insurance_rate; - } catch (const std::exception& e) { - LOG(cyclus::LEV_INFO1, "CalculateUnitCost") - << prototype() << "failed to get region financial_data_: " << e.what(); - return kDefaultUnitCost; - } - - // U = throughput * timesteps_per_year - double timesteps_per_year = cyclusYear / context()->dt(); - double units_produced_annually = production_capacity * timesteps_per_year; - - // x = (1-τ)*r_b*f_b + r_s*f_s - tax_modified_rate_of_return = - (1 - income_tax_rate) * bond_rate * bond_fraction + - shareholder_rate * shareholder_fraction; - - // c_j = F_bar/U + V_bar + (I_0/U) * [p + (1/(1-τ)) * PMT(N,x,1,0) - (1/N) * - // (τ/(1-τ))] - double fixed_cost_per_unit = levelized_fixed_costs / units_produced_annually; - double capital_investment_per_unit = - initial_investment / units_produced_annually; - - double depreciation_tax_shield = - income_tax_rate / (1.0 - income_tax_rate) / facility_lifetime; - double capital_recovery_factor = - PMT(facility_lifetime, tax_modified_rate_of_return, 1.0, 0.0) / - (1.0 - income_tax_rate); - double capital_cost_per_unit = - capital_investment_per_unit * - (property_and_insurance_rate + capital_recovery_factor - - depreciation_tax_shield); - double production_cost = - fixed_cost_per_unit + levelized_variable_costs + capital_cost_per_unit; - - // c_u = c_j + c_M = production_cost + input_cost_per_unit - double unit_cost = production_cost + input_cost_per_unit; - - // Protects against divide by zero in pref = 1/unit_cost - return unit_cost != 0 ? unit_cost : kDefaultUnitCost; -} - -double CalculateUnitPrice(double production_capacity, double units_to_produce, - double input_cost_per_unit = 0.0) const { - // Default implementation - return CalculateUnitCost(production_capacity, units_to_produce, - input_cost_per_unit); -} - -// Required for compilation but not added by the cycpp preprocessor. Do not -// remove. Must be one for each variable. -std::vector cycpp_shape_capital_cost = {0}; -std::vector cycpp_shape_property_tax_rate = {0}; -std::vector cycpp_shape_annual_operations_and_maintenance = {0}; -std::vector cycpp_shape_facility_operational_lifetime = {0}; -std::vector cycpp_shape_facility_depreciation_lifetime = {0}; -std::vector cycpp_shape_annual_labor_cost = {0}; -std::vector cycpp_shape_cost_override = {0}; -std::vector cycpp_shape_property_insurance_rate = {0}; \ No newline at end of file diff --git a/src/toolkit/institution_cost.cycpp.h b/src/toolkit/institution_cost.cycpp.h deleted file mode 100644 index e908ac9a8c..0000000000 --- a/src/toolkit/institution_cost.cycpp.h +++ /dev/null @@ -1,96 +0,0 @@ -/// This includes the required header to add institution costs to archetypes. -/// One should only need to: -/// - '#include "toolkit/institution_cost.cycpp.h"' in the header of the -/// archetype class (as private) -/// - Add `InitEconParameters()` to `EnterNotify()` in the cc file of the -/// archetype class. - -/// How to add parameters to this file: -/// 1. Add the pragma. A default value MUST be added to ensure backwards -/// compatibility. -/// 2. Edit the unordered_map called "econ_params" -/// i. add the desired parameter to the array {"name", value} -/// ii. the value of the pair should be the variable name exactly -/// 3. Add "std::vector cycpp_shape_ = {0};" to the end of the -/// file with the other ones, reaplcing with the name you put -/// in the econ_params array (again, must match exactly). - -// clang-format off -#pragma cyclus var { \ - "default": 0.0, \ - "uilabel": "Minimum acceptable rate of return", \ - "range": [0.0, 1.0], \ - "doc": "Minimum acceptable rate of return for the institution", \ - "units": "Dimensionless" \ - } -double minimum_acceptable_return_rate; - -#pragma cyclus var { \ - "default": 0.0, \ - "uilabel": "Corporate Income Tax Rate", \ - "range": [0.0, 1.0], \ - "doc": "Corporate income tax rate as decimal (1% --> 0.01)", \ - "units": "Dimensionless" \ - } -double corporate_income_tax_rate; - -#pragma cyclus var { \ - "default": 0.0, \ - "uilabel": "Bond-holder's Expected Rate of Return", \ - "range": [0.0, 1.0], \ - "doc": "Expected rate of return for bond holders as decimal (1% --> 0.01)", \ - "units": "Dimensionless" \ - } -double bond_holders_rate_of_return; - -#pragma cyclus var { \ - "default": 0.0, \ - "uilabel": "Fraction of Initial Investment from Bonds", \ - "range": [0.0, 1.0], \ - "doc": "Fraction of initial investment financed through bonds as decimal (1% --> 0.01)", \ - "units": "Dimensionless" \ - } -double fraction_bond_financing; - -#pragma cyclus var { \ - "default": 0.0, \ - "uilabel": "Share-holder's Expected Rate of Return", \ - "range": [0.0, 1.0], \ - "doc": "Expected rate of return for share holders as decimal (1% --> 0.01)", \ - "units": "Dimensionless" \ - } -double share_holders_rate_of_return; - -#pragma cyclus var { \ - "default": 0.0, \ - "uilabel": "Fraction of Initial Investment from Private Capital", \ - "range": [0.0, 1.0], \ - "doc": "Fraction of initial investment financed through private capital as decimal (1% --> 0.01)", \ - "units": "Dimensionless" \ - } -double fraction_private_capital; -// clang-format on - -// Must be done in a function so that we can access the user-defined values -std::unordered_map GenerateParamList() const { - std::unordered_map econ_params { - {"minimum_acceptable_return_rate", minimum_acceptable_return_rate}, - {"corporate_income_tax_rate", corporate_income_tax_rate}, - {"bond_holders_rate_of_return", bond_holders_rate_of_return}, - {"fraction_bond_financing", fraction_bond_financing}, - {"share_holders_rate_of_return", share_holders_rate_of_return}, - {"fraction_private_capital", fraction_private_capital} - }; - - return econ_params; -} - - -// Required for compilation but not added by the cycpp preprocessor. Do not -// remove. Must be one for each variable. -std::vector cycpp_shape_minimum_acceptable_return_rate = {0}; -std::vector cycpp_shape_corporate_income_tax_rate = {0}; -std::vector cycpp_shape_bond_holders_rate_of_return = {0}; -std::vector cycpp_shape_fraction_bond_financing = {0}; -std::vector cycpp_shape_share_holders_rate_of_return = {0}; -std::vector cycpp_shape_fraction_private_capital = {0}; \ No newline at end of file diff --git a/src/toolkit/marginal_cost.cycpp.h b/src/toolkit/marginal_cost.cycpp.h new file mode 100644 index 0000000000..2e76a5c100 --- /dev/null +++ b/src/toolkit/marginal_cost.cycpp.h @@ -0,0 +1,69 @@ +/// This includes the required header to add facility costs to archetypes. +/// One should only need to: +/// - '#include "toolkit/marginal_cost.cycpp.h"' in the header of the +/// archetype class (as private) +/// - Add `InitEconParameters()` to `EnterNotify()` in the cc file of the +/// archetype class. + +/// How to add parameters to this file: +/// 1. Add the pragma. A default value MUST be added to ensure backwards +/// compatibility. +/// 2. Edit the unordered_map called "econ_params" +/// i. add the desired parameter to the array {"name", value} +/// ii. the value of the pair should be the variable name exactly +/// 3. Add "std::vector cycpp_shape_ = {0};" to the end of the +/// file with the other ones, reaplcing with the name you put +/// in the econ_params array (again, must match exactly). + +// clang-format off +#pragma cyclus var { \ + "default": 0.0, \ + "uilabel": "Variable Cost Per Unit", \ + "doc": "Variable cost per unit of production (labor, materials, etc.). " \ + "The data from the Cost Basis Report represents a variable cost per " \ + "unit and can be used here.", \ + "units": "Unit of Currency/Unit of Production" \ + } +double variable_cost_per_unit; +// clang-format on + +// Must be done in a function so that we can access the user-defined values +std::unordered_map GenerateParamList() const override { + std::unordered_map econ_params{ + {"variable_cost_per_unit", variable_cost_per_unit}}; + + return econ_params; +} + +/// @brief Returns the sum of the variable cost per unit and the material cost +/// per unit if available, and the default unit cost of kDefaultUnitCost if not. +/// +/// @param material_cost_per_unit Per-unit cost of input materials +/// @return Sum of variable cost per unit and material cost per unit, or +/// kDefaultUnitCost if not available +double CalcMarginalCost(double material_cost_per_unit) const { + double variable_cost_per_unit; + + try { + variable_cost_per_unit = GetEconParameter("variable_cost_per_unit"); + } catch (const std::exception& e) { + LOG(cyclus::LEV_INFO1, "ValidateFacilityEconParameters") + << prototype() + << "failed to get variable cost per unit: " << e.what(); + return kDefaultUnitCost; + } + + if (variable_cost_per_unit <= cyclus::eps()) { + LOG(cyclus::LEV_WARN, "CalcMarginalCost") + << prototype() + << "has a very low (" << variable_cost_per_unit << ") variable cost per unit!"; + } + + return variable_cost_per_unit + material_cost_per_unit; +} + + + +// Required for compilation but not added by the cycpp preprocessor. Do not +// remove. Must be one for each variable. +std::vector cycpp_shape_variable_cost_per_unit = {0}; \ No newline at end of file diff --git a/src/toolkit/region_cost.cycpp.h b/src/toolkit/region_cost.cycpp.h deleted file mode 100644 index 151548fdd6..0000000000 --- a/src/toolkit/region_cost.cycpp.h +++ /dev/null @@ -1,39 +0,0 @@ -/// This includes the required header to add regional costs to archetypes. -/// One should only need to: -/// - '#include "toolkit/region_cost.cycpp.h"' in the header of the -/// archetype class (as private) -/// - Add `InitEconParameters()` to `EnterNotify()` in the cc file of the -/// archetype class. - -/// How to add parameters to this file: -/// 1. Add the pragma. A default value MUST be added to ensure backwards -/// compatibility. -/// 2. Edit the unordered_map called "econ_params" -/// i. add the desired parameter to the array {"name", value} -/// ii. the value of the pair should be the variable name exactly -/// 3. Add "std::vector cycpp_shape_ = {0};" to the end of the -/// file with the other ones, reaplcing with the name you put -/// in the econ_params array (again, must match exactly). - -// clang-format off -#pragma cyclus var { \ - "default": 0.0, \ - "uilabel": "Corporate Income Tax Rate as decimal", \ - "range": [0.0, 1.0], \ - "doc": "Income Tax Rate for all facilities belonging to this region as decimal (1% --> 0.01)", \ - "units": "Dimensionless" \ - } -double corporate_income_tax_rate; -// clang-format on - -// Must be done in a function so that we can access the user-defined values -std::unordered_map GenerateParamList() const { - std::unordered_map econ_params{ - {"corporate_income_tax_rate", corporate_income_tax_rate}}; - - return econ_params; -} - -// Required for compilation but not added by the cycpp preprocessor. Do not -// remove. Must be one for each variable. -std::vector cycpp_shape_corporate_income_tax_rate = {0}; \ No newline at end of file diff --git a/src/toolkit/scheduling_function.cc b/src/toolkit/scheduling_function.cc new file mode 100644 index 0000000000..ee72ad97e2 --- /dev/null +++ b/src/toolkit/scheduling_function.cc @@ -0,0 +1,62 @@ +#include "scheduling_function.h" +#include "cyc_limits.h" +#include "facility.h" + +namespace cyclus { +namespace toolkit { + + +SchedulingFunctions::SchedulingFunctions(Facility* fac): + f_(fac) {} + +void SchedulingFunctions::Scheduler(int t, std::set in_commods){ + f_-> context()-> RegisterRequesters(t, f_); + f_-> context()-> RegisterCommoditiesTraded(t, in_commods); + t_.insert(t); +} //archetype developers can also call this method to schedule some time directly with context +// from their own scheduling function/parameters + +void SchedulingFunctions::FixIncSchedule(){ + Scheduler(FacilityTime()+ 1, f_->GetInCommods()); +} + +void SchedulingFunctions::ConstantRequest(int cycle_length, std::set commods){ + Scheduler(FacilityTime() + cycle_length, commods); +} + +void SchedulingFunctions::DemandDrivenRequests(ResBuf res,std::set commods){ + if (res.space() > eps_rsrc()) { + Scheduler(FacilityTime()+1, commods); + } + else { + return; + } +} + +void SchedulingFunctions::PredefinedSchedule(std::set sched){ + //in order to use this function the entire schedule of a facilities requests should be mapped. + //do not invoke parent tock in facilities that use this. Add additional EventSchedule() to EnterNotify() instead. + t_ = sched; +} + +void SchedulingFunctions::clear(){ + t_.clear(); +} + +void SchedulingFunctions::InitialTrade(){ + Scheduler(FacilityTime(),f_->GetInCommods()); + //i changed this from the original +// if(FacilityTime() == 0){ +// Scheduler(1, f_->GetInCommods()); +// } +// else if (FacilityTime() > 0){ //if during sim_time >0 register for sim_time +// Scheduler(FacilityTime(),f_->GetInCommods()); +// } +} + +int SchedulingFunctions::FacilityTime(){ + return f_->context()->time(); +} + +} // namespace toolkit +} // namespace cyclus \ No newline at end of file diff --git a/src/toolkit/scheduling_function.h b/src/toolkit/scheduling_function.h new file mode 100644 index 0000000000..9b1d31e408 --- /dev/null +++ b/src/toolkit/scheduling_function.h @@ -0,0 +1,53 @@ +#ifndef CYCLUS_SRC_TOOLKIT_SCHEDULING_FUNCTION_H_ +#define CYCLUS_SRC_TOOLKIT_SCHEDULING_FUNCTION_H_ + +#include "context.h" +#include "symbolic_functions.h" //I may add a symbolic functions later... +#include "res_buf.h" +#include + +namespace cyclus { + +class Facility; + +namespace toolkit { +//these are a kit of functions to help the archetype developer start their own DRE event scheduling. +//the dev can use as many functions as they want in the GetSchedulingTime() override ... +// because of that fact there is an time_stamp set with an associated commodity pair (which times they want to trade what) getter function +// + +class SchedulingFunctions { + +public: + SchedulingFunctions(Facility* fac); + +// deconstructor .. + +void DemandDrivenRequests(ResBuf res,std::set commods); + +void ConstantRequest(int cycle_length, std::set commods); + +void FixIncSchedule(); + +void clear(); + +void InitialTrade(); + +int FacilityTime(); + +void Scheduler(int t, std::set in_commods); + +void PredefinedSchedule(std::set sched); + +const std::set& EventTime() const {//schedule + return t_; +} + +private: + Facility* f_; + std::set t_; //this may be sketchy with clear(), should i not have a variable and just have sched. func return event times? +}; + +} // namespace toolkit +} // namespace cyclus +#endif \ No newline at end of file diff --git a/src/trader.h b/src/trader.h index ce999ac949..557cd1b0e9 100644 --- a/src/trader.h +++ b/src/trader.h @@ -78,12 +78,27 @@ class Trader { virtual void AcceptProductTrades( const std::vector, Product::Ptr>>& responses) {} - protected: + virtual void EventRequest(){} // can be overridden ? + + bool Traded; //follows DRE to assess whether trader completed trades; + // archetype devs may want to use this to assess event scheduling + + void SetTraded(bool status){Traded = status;} //setter for Traded + + bool ReturnTraded(){return Traded;} //getter for Traded + + void FillInCommods(std::string commod){in_commods_.insert(commod);} + + std::set GetInCommods() {return in_commods_;} + + protected: Agent* manager_; private: /// @warning this function is hidden to prevent an invalid signature that can /// raise difficult to find bugs + std::set in_commods_; + virtual std::set::Ptr> GetMatlBids( const CommodMap::type& commod_requests) { return std::set::Ptr>(); diff --git a/src/trader_management.h b/src/trader_management.h index 97fb22eaab..69a227ad57 100644 --- a/src/trader_management.h +++ b/src/trader_management.h @@ -84,6 +84,7 @@ inline void AcceptTrades( Trader* trader, const std::vector, Material::Ptr>>& responses) { dynamic_cast(trader)->AcceptMatlTrades(responses); + trader->SetTraded(true); //successful trade means Traded status changes } template <> @@ -91,6 +92,7 @@ inline void AcceptTrades( Trader* trader, const std::vector, Product::Ptr>>& responses) { trader->AcceptProductTrades(responses); + trader->SetTraded(true); //successful trade means Traded status changes } } // namespace cyclus