Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Random sink frequency #565

Merged
merged 4 commits into from
Dec 1, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ cycamore Change Log
**Added:**

* GitHub workflows for building/testing on a PR and push to `main` (#549, #564)
* Add functionality for random behavior on the size of a sink (#550)
* Add functionality for random behavior on the size (#550) and frequency (#565) of a sink
* GitHub workflow to check that the CHANGELOG has been updated (#562)

**Changed:**
Expand Down
54 changes: 47 additions & 7 deletions src/sink.cc
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,20 @@ void Sink::EnterNotify() {
/// Create first requestAmt. Only used in testing, as a simulation will
/// overwrite this on Tick()
SetRequestAmt();
SetNextBuyTime();

if (random_size_type != "None") {
LOG(cyclus::LEV_INFO4, "SnkFac") << "Sink " << this->id()
<< " is using random behavior "
<< random_size_type
<< " for determining request size.";
}
if (random_frequency_type != "None") {
LOG(cyclus::LEV_INFO4, "SnkFac") << "Sink " << this->id()
<< " is using random behavior "
<< random_frequency_type
<< " for determining request frequency.";
}
RecordPosition();
}

Expand Down Expand Up @@ -170,21 +183,32 @@ void Sink::AcceptGenRsrcTrades(
void Sink::Tick() {
using std::string;
using std::vector;
LOG(cyclus::LEV_INFO3, "SnkFac") << prototype() << " is ticking {";
LOG(cyclus::LEV_INFO3, "SnkFac") << "Sink " << this->id() << " is ticking {";

SetRequestAmt();
if (nextBuyTime == -1) {
SetRequestAmt();
}
else if (nextBuyTime == context()->time()) {
SetRequestAmt();
SetNextBuyTime();

LOG(cyclus::LEV_INFO3, "SnkFac") << prototype() << " has default request amount " << requestAmt;
LOG(cyclus::LEV_INFO4, "SnkFac") << "Sink " << this->id()
<< " has reached buying time. The next buy time will be time step " << nextBuyTime;
}
else {
requestAmt = 0;
}

// inform the simulation about what the sink facility will be requesting
if (requestAmt > cyclus::eps()) {
LOG(cyclus::LEV_INFO4, "SnkFac") << prototype()
<< " has request amount " << requestAmt
<< " kg of " << in_commods[0] << ".";
LOG(cyclus::LEV_INFO4, "SnkFac") << "Sink " << this->id()
<< " has request amount " << requestAmt
<< " kg of " << in_commods[0] << ".";
for (vector<string>::iterator commod = in_commods.begin();
commod != in_commods.end();
commod++) {
LOG(cyclus::LEV_INFO4, "SnkFac") << " will request " << requestAmt
LOG(cyclus::LEV_INFO4, "SnkFac") << "Sink " << this->id()
<< " will request " << requestAmt
<< " kg of " << *commod << ".";
cyclus::toolkit::RecordTimeSeries<double>("demand"+*commod, this,
requestAmt);
Expand Down Expand Up @@ -242,6 +266,22 @@ void Sink::SetRequestAmt() {
return;
}

void Sink::SetNextBuyTime() {
if (random_frequency_type == "None") {
nextBuyTime = -1;
}
else if (random_frequency_type == "UniformInt") {
nextBuyTime = context()->time() + context()->random_uniform_int(random_frequency_min, random_frequency_max);
}
else if (random_frequency_type == "NormalInt") {
nextBuyTime = context()->time() + context()->random_normal_int(random_frequency_mean, random_frequency_stddev, random_frequency_min, random_frequency_max);
}
else {
nextBuyTime = -1;
}
return;
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
extern "C" cyclus::Agent* ConstructSink(cyclus::Context* ctx) {
return new Sink(ctx);
Expand Down
59 changes: 58 additions & 1 deletion src/sink.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ class Sink
/// @brief SinkFacilities update request amount using random behavior
virtual void SetRequestAmt();

/// @brief SinkFacilities update request time using random behavior
virtual void SetNextBuyTime();

/// add a commodity to the set of input commodities
/// @param name the commodity name
inline void AddCommodity(std::string name) { in_commods.push_back(name); }
Expand Down Expand Up @@ -111,6 +114,7 @@ class Sink

private:
double requestAmt;
int nextBuyTime;
/// all facilities must have at least one input commodity
#pragma cyclus var {"tooltip": "input commodities", \
"doc": "commodities that the sink facility accepts", \
Expand Down Expand Up @@ -195,8 +199,61 @@ class Sink
"space to use as the standard deviation. Default 0.1"}
double random_size_stddev;


// random status (frequencing/timing of request)
#pragma cyclus var {"default": "None", \
"tooltip": "type of random behavior when setting the " \
"timing of the request", \
"uitype": "combobox", \
"uilabel": "Random Timing", \
"categorical": ["None", "UniformInt", "NormalInt"], \
"doc": "type of random behavior to use. Default None, " \
"other options are, 'UniformInt', and 'NormalInt'. " \
"When using 'UniformInt', also set "\
"'random_frequency_min' and 'random_frequency_max'. " \
"For 'NormalInt', set 'random_frequency_mean' and " \
"'random_fequency_stddev', min and max values are " \
"optional. "}
std::string random_frequency_type;

// random frequency mean
#pragma cyclus var {"default": 1, \
"tooltip": "mean of the random frequency", \
"uilabel": "Random Frequency Mean", \
"uitype": "range", \
"range": [0.0, 1e299], \
"doc": "When a normal distribution is used to determine the " \
"frequency of the request, this is the mean. Default 1"}
double random_frequency_mean;

// random frequency std dev
#pragma cyclus var {"default": 1, \
"tooltip": "std dev of the random frequency", \
"uilabel": "Random Frequency Std Dev", \
"uitype": "range", \
"range": [0.0, 1e299], \
"doc": "When a normal distribution is used to determine the " \
"frequency of the request, this is the standard deviation. Default 1"}
double random_frequency_stddev;

// random frequency lower bound
#pragma cyclus var {"default": 1, \
"tooltip": "lower bound of the random frequency", \
"uilabel": "Random Frequency Lower Bound", \
"uitype": "range", \
"range": [1, 1e299], \
"doc": "When a random distribution is used to determine the " \
"frequency of the request, this is the lower bound. Default 1"}
int random_frequency_min;

// random frequency upper bound
#pragma cyclus var {"default": 1e299, \
"tooltip": "upper bound of the random frequency", \
"uilabel": "Random Frequency Upper Bound", \
"uitype": "range", \
"range": [1, 1e299], \
"doc": "When a random distribution is used to determine the " \
"frequency of the request, this is the upper bound. Default 1e299"}
int random_frequency_max;

#pragma cyclus var { \
"default": 0.0, \
Expand Down
145 changes: 142 additions & 3 deletions src/sink_tests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -342,7 +342,7 @@ TEST_F(SinkTest, PositionInitialize2) {

}

TEST_F(SinkTest, RandomUniform) {
TEST_F(SinkTest, RandomUniformSize) {
using cyclus::QueryResult;
using cyclus::Cond;

Expand All @@ -364,7 +364,7 @@ TEST_F(SinkTest, RandomUniform) {
EXPECT_NEAR(qr.GetVal<double>("Quantity"), 9.41273, 0.0001);
}

TEST_F(SinkTest, RandomNormal) {
TEST_F(SinkTest, RandomNormalSize) {
using cyclus::QueryResult;
using cyclus::Cond;

Expand All @@ -386,7 +386,7 @@ TEST_F(SinkTest, RandomNormal) {
EXPECT_NEAR(qr.GetVal<double>("Quantity"), 9.60929, 0.0001);
}

TEST_F(SinkTest, RandomNormalWithMeanSttdev) {
TEST_F(SinkTest, RandomNormalSizeWithMeanSttdev) {
using cyclus::QueryResult;
using cyclus::Cond;

Expand All @@ -410,6 +410,145 @@ TEST_F(SinkTest, RandomNormalWithMeanSttdev) {
EXPECT_NEAR(qr.GetVal<double>("Quantity"), 1.52979, 0.0001);
}

TEST_F(SinkTest, RandomUniformFreq) {
using cyclus::QueryResult;
using cyclus::Cond;

std::string config =
" <in_commods>"
" <val>commods_1</val>"
" </in_commods>"
" <capacity>10</capacity>"
" <random_frequency_type>UniformInt</random_frequency_type> "
" <random_frequency_min>2</random_frequency_min> "
" <random_frequency_max>4</random_frequency_max> ";

int simdur = 3;
cyclus::MockSim sim(cyclus::AgentSpec
(":cycamore:Sink"), config, simdur);
sim.AddSource("commods_1").capacity(10).Finalize();
int id = sim.Run();

QueryResult qr = sim.db().Query("Transactions", NULL);
EXPECT_EQ(qr.rows.size(), 1);
int trans_time = qr.GetVal<int>("Time", 0);
nuclearkatie marked this conversation as resolved.
Show resolved Hide resolved
EXPECT_EQ(trans_time, 2);
}

TEST_F(SinkTest, RandomNormalFreq) {
using cyclus::QueryResult;
using cyclus::Cond;

std::string config =
" <in_commods>"
" <val>commods_1</val>"
" </in_commods>"
" <capacity>10</capacity>"
" <random_frequency_type>NormalInt</random_frequency_type> ";

int simdur = 3;
cyclus::MockSim sim(cyclus::AgentSpec
(":cycamore:Sink"), config, simdur);
sim.AddSource("commods_1").capacity(10).Finalize();
int id = sim.Run();

QueryResult qr = sim.db().Query("Transactions", NULL);
EXPECT_EQ(qr.rows.size(), 1);
int trans_time = qr.GetVal<int>("Time", 0);
EXPECT_EQ(trans_time, 2);
}

TEST_F(SinkTest, RandomNormalFreqWithMeanSttdev) {
using cyclus::QueryResult;
using cyclus::Cond;

std::string config =
" <in_commods>"
" <val>commods_1</val>"
" </in_commods>"
" <capacity>10</capacity>"
" <random_frequency_type>NormalInt</random_frequency_type> "
" <random_frequency_mean>2</random_frequency_mean> "
" <random_frequency_stddev>0.2</random_frequency_stddev> ";

int simdur = 3;
cyclus::MockSim sim(cyclus::AgentSpec
(":cycamore:Sink"), config, simdur);
sim.AddSource("commods_1").capacity(10).Finalize();
int id = sim.Run();

QueryResult qr = sim.db().Query("Transactions", NULL);
EXPECT_EQ(qr.rows.size(), 1);
int trans_time = qr.GetVal<int>("Time", 0);
EXPECT_EQ(trans_time, 2);
}

// Make sure that multiple random
TEST_F(SinkTest, RandomNormalFreqMultipleCycles) {
using cyclus::QueryResult;
using cyclus::Cond;

std::string config =
" <in_commods>"
" <val>commods_1</val>"
" </in_commods>"
" <capacity>10</capacity>"
" <random_frequency_type>NormalInt</random_frequency_type> "
" <random_frequency_mean>4</random_frequency_mean> "
" <random_frequency_stddev>1</random_frequency_stddev> ";

int simdur = 12;
cyclus::MockSim sim(cyclus::AgentSpec
(":cycamore:Sink"), config, simdur);
sim.AddSource("commods_1").capacity(10).Finalize();
int id = sim.Run();

QueryResult qr = sim.db().Query("Transactions", NULL);
EXPECT_EQ(3, qr.rows.size());
// check multiple cycles execute at the expected time
// Buy times should occur at timestep 5, 7, and 10
int first_trans_time = qr.GetVal<int>("Time", 0);
EXPECT_EQ(5, first_trans_time);
int second_trans_time = qr.GetVal<int>("Time", 1);
EXPECT_EQ(7, second_trans_time);
int third_trans_time = qr.GetVal<int>("Time", 2);
EXPECT_EQ(10, third_trans_time);
}

// Check that randomness can be implemented in both size of request and
// request frequency at the same time
TEST_F(SinkTest, RandomNormalSizeUniformFreq) {
using cyclus::QueryResult;
using cyclus::Cond;

std::string config =
" <in_commods>"
" <val>commods_1</val>"
" </in_commods>"
" <capacity>10</capacity>"
" <random_size_type>NormalReal</random_size_type>"
" <random_size_mean>0.8</random_size_mean>"
" <random_size_stddev>0.2</random_size_stddev>"
" <random_frequency_type>UniformInt</random_frequency_type> "
" <random_frequency_min>2</random_frequency_min> "
" <random_frequency_max>4</random_frequency_max> ";

int simdur = 6;
cyclus::MockSim sim(cyclus::AgentSpec
(":cycamore:Sink"), config, simdur);
sim.AddSource("commods_1").capacity(20).Finalize();
int id = sim.Run();

QueryResult tqr = sim.db().Query("Transactions", NULL);
EXPECT_EQ(2, tqr.rows.size());
// check multiple cycles execute at the expected time
int trans_time = tqr.GetVal<int>("Time", 0);
EXPECT_EQ(3, trans_time);
int res_id = tqr.GetVal<int>("ResourceId", 0);
QueryResult rqr = sim.db().Query("Resources", NULL);
double quantity = rqr.GetVal<double>("Quantity", 0);
EXPECT_NEAR(6.54143, quantity, 0.00001);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
cyclus::Agent* SinkConstructor(cyclus::Context* ctx) {
return new cycamore::Sink(ctx);
Expand Down