Skip to content

Commit

Permalink
Synchronized XY data series (#225)
Browse files Browse the repository at this point in the history
* #224 Added new method in time_series_observer for extracting time-synced real series.

* #224 Implicit copy?
  • Loading branch information
eidekrist authored Mar 21, 2019
1 parent 7bbd092 commit 1fce1a6
Show file tree
Hide file tree
Showing 7 changed files with 215 additions and 4 deletions.
27 changes: 27 additions & 0 deletions include/cse.h
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,33 @@ int64_t cse_observer_slave_get_real_samples(
cse_step_number steps[],
cse_time_point times[]);

/**
* Retrieves two time-synchronized series of observed values for two real variables.
*
* \param [in] observer the observer
* \param [in] slave1 index of the first slave
* \param [in] variableIndex1 the first variable index
* \param [in] slave2 index of the second slave
* \param [in] variableIndex2 the second variable index
* \param [in] fromStep the step number to start from
* \param [in] nSamples the number of samples to read
* \param [out] values1 the first series of observed values
* \param [out] values2 the second series of observed values
*
* \returns
* The number of samples actually read, which may be smaller than `nSamples`.
*/
int64_t cse_observer_slave_get_real_synchronized_series(
cse_observer* observer,
cse_slave_index slave1,
cse_variable_index variableIndex1,
cse_slave_index slave2,
cse_variable_index variableIndex2,
cse_step_number fromStep,
size_t nSamples,
double values1[],
double values2[]);

/**
* Sets the values of integer variables for one slave.
*
Expand Down
17 changes: 13 additions & 4 deletions include/cse/observer/time_series_observer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,10 @@ class time_series_observer : public time_series_provider
time_point currentTime) override;

void simulator_step_complete(
simulator_index index,
step_number lastStep,
duration lastStepSize,
time_point currentTime) override;
simulator_index index,
step_number lastStep,
duration lastStepSize,
time_point currentTime) override;

/**
* Start observing a variable.
Expand Down Expand Up @@ -97,6 +97,15 @@ class time_series_observer : public time_series_provider
time_point tEnd,
gsl::span<step_number> steps) override;

std::size_t get_synchronized_real_series(
simulator_index sim1,
variable_index variableIndex1,
simulator_index sim2,
variable_index variableIndex2,
step_number fromStep,
gsl::span<double> values1,
gsl::span<double> values2) override;

~time_series_observer() noexcept override;

private:
Expand Down
23 changes: 23 additions & 0 deletions include/cse/observer/time_series_provider.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,29 @@ class time_series_provider : public observer
time_point tBegin,
time_point tEnd,
gsl::span<step_number> steps) = 0;

/**
* Retrieves two time-synchronized series of observed values for two real variables.
*
* \param [in] sim1 index of the first simulator
* \param [in] variableIndex1 the first variable index
* \param [in] sim2 index of the second simulator
* \param [in] variableIndex2 the second variable index
* \param [in] fromStep the step number to start from
* \param [out] values1 the first series of observed values
* \param [out] values2 the second series of observed values
*
* Returns the number of samples actually read, which may be smaller
* than the sizes of `values1` and `values2`.
*/
virtual std::size_t get_synchronized_real_series(
simulator_index sim1,
variable_index variableIndex1,
simulator_index sim2,
variable_index variableIndex2,
step_number fromStep,
gsl::span<double> values1,
gsl::span<double> values2) = 0;
};


Expand Down
32 changes: 32 additions & 0 deletions src/c/cse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,38 @@ int64_t cse_observer_slave_get_real_samples(
}
}

int64_t cse_observer_slave_get_real_synchronized_series(
cse_observer* observer,
cse_slave_index slave1,
cse_variable_index variableIndex1,
cse_slave_index slave2,
cse_variable_index variableIndex2,
cse_step_number fromStep,
size_t nSamples,
double values1[],
double values2[])
{
try {
std::vector<cse::time_point> timePoints(nSamples);
const auto obs = std::dynamic_pointer_cast<cse::time_series_provider>(observer->cpp_observer);
if (!obs) {
throw std::invalid_argument("Invalid observer! The provided observer must be a time_series_observer.");
}
size_t samplesRead = obs->get_synchronized_real_series(
slave1,
variableIndex1,
slave2,
variableIndex2,
fromStep,
gsl::make_span(values1, nSamples),
gsl::make_span(values2, nSamples));
return samplesRead;
} catch (...) {
handle_current_exception();
return failure;
}
}

int64_t cse_observer_slave_get_integer_samples(
cse_observer* observer,
cse_slave_index slave,
Expand Down
41 changes: 41 additions & 0 deletions src/cpp/observer/time_series_observer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,12 @@ class time_series_observer::single_slave_observer
steps[1] = lastStep;
}

const std::map<step_number, double> get_real_samples_map(variable_index idx)
{
std::lock_guard<std::mutex> lock(lock_);
return realSamples_.at(idx);
}

private:
std::map<variable_index, std::map<step_number, double>> realSamples_;
std::map<variable_index, std::map<step_number, int>> intSamples_;
Expand Down Expand Up @@ -289,6 +295,41 @@ void time_series_observer::get_step_numbers(
slaveObservers_.at(sim)->get_step_numbers(tBegin, tEnd, steps);
}

std::size_t time_series_observer::get_synchronized_real_series(
simulator_index sim1,
variable_index variableIndex1,
simulator_index sim2,
variable_index variableIndex2,
step_number fromStep,
gsl::span<double> values1,
gsl::span<double> values2)
{
CSE_INPUT_CHECK(values1.size() == values2.size());

const auto realSamples1 = slaveObservers_.at(sim1)->get_real_samples_map(variableIndex1);
const auto realSamples2 = slaveObservers_.at(sim2)->get_real_samples_map(variableIndex2);

if (realSamples1.empty() || realSamples2.empty()) {
throw std::out_of_range("Samples for both variables not recorded yet!");
}
auto lastStep1 = realSamples1.rbegin()->first;
auto lastStep2 = realSamples2.rbegin()->first;

size_t samplesRead = 0;
for (auto step = fromStep; step < fromStep + static_cast<step_number>(values1.size()); step++) {
auto sample1 = realSamples1.find(step);
auto sample2 = realSamples2.find(step);
if (sample1 != realSamples1.end() && sample2 != realSamples2.end()) {
values1[samplesRead] = sample1->second;
values2[samplesRead] = sample2->second;
samplesRead++;
} else if (lastStep1 < step || lastStep2 < step) {
break;
}
}
return samplesRead;
}

time_series_observer::~time_series_observer() noexcept = default;

} // namespace cse
1 change: 1 addition & 0 deletions test/cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ set(tests
"trend_buffer_test"
"scenario_manager_test"
"scenario_parser_test"
"synchronized_xy_series_test"
)
set(unittests
"async_slave_unittest"
Expand Down
78 changes: 78 additions & 0 deletions test/cpp/synchronized_xy_series_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#include "mock_slave.hpp"

#include <cse/algorithm.hpp>
#include <cse/async_slave.hpp>
#include <cse/execution.hpp>
#include <cse/log.hpp>
#include <cse/observer/time_series_observer.hpp>

#include <exception>
#include <memory>
#include <stdexcept>

#define REQUIRE(test) \
if (!(test)) throw std::runtime_error("Requirement not satisfied: " #test)

int main()
{
try {
constexpr cse::time_point startTime = cse::to_time_point(0.0);
constexpr cse::time_point midTime = cse::to_time_point(1.0);
constexpr cse::time_point endTime = cse::to_time_point(2.0);
constexpr cse::duration stepSize = cse::to_duration(0.1);

cse::log::set_global_output_level(cse::log::level::debug);

auto algorithm = std::make_shared<cse::fixed_step_algorithm>(stepSize);
auto execution = cse::execution(startTime, algorithm);

auto observer = std::make_shared<cse::time_series_observer>();
execution.add_observer(observer);

double x1 = 0;
auto simIndex1 = execution.add_slave(
cse::make_pseudo_async(std::make_unique<mock_slave>([&x1](double x) { return x + x1++; })), "slave uno");

double x2 = 0;
auto simIndex2 = execution.add_slave(
cse::make_pseudo_async(std::make_unique<mock_slave>([&x2](double x) { return x + x2++; })), "slave dos");

algorithm->set_stepsize_decimation_factor(simIndex2, 2);

auto variableId1 = cse::variable_id{simIndex1, cse::variable_type::real, 0};
auto variableId2 = cse::variable_id{simIndex2, cse::variable_type::real, 0};

observer->start_observing(variableId1);

// Run the simulation
auto simResult = execution.simulate_until(midTime);
REQUIRE(simResult.get());

observer->start_observing(variableId2);

simResult = execution.simulate_until(endTime);
REQUIRE(simResult.get());

const int numSamples = 20;
const cse::variable_index varIndex = 0;
double realValues1[numSamples];
double realValues2[numSamples];

size_t samplesRead = observer->get_synchronized_real_series(simIndex1, varIndex, simIndex2, varIndex, 5, realValues1, realValues2);
REQUIRE(samplesRead == 5);

double expectedReals1[5] = {12.0, 14.0, 16.0, 18.0, 20.0};
double expectedReals2[5] = {6.0, 7.0, 8.0, 9.0, 10.0};

for (size_t i = 0; i < samplesRead; i++) {
REQUIRE(std::fabs(realValues1[i] - expectedReals1[i]) < 1.0e9);
REQUIRE(std::fabs(realValues2[i] - expectedReals2[i]) < 1.0e9);
}

} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << std::endl;
return 1;
}

return 0;
}

0 comments on commit 1fce1a6

Please sign in to comment.