-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy pathslave.hpp
351 lines (314 loc) · 13.1 KB
/
slave.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
/**
* \file
* Slave interface.
*
* \copyright
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
#ifndef COSIM_SLAVE_HPP
#define COSIM_SLAVE_HPP
#include <cosim/model_description.hpp>
#include <cosim/serialization.hpp>
#include <cosim/time.hpp>
#include <boost/container/vector.hpp>
#include <gsl/span>
#include <optional>
#include <string>
namespace cosim
{
/**
* An interface for classes that represent co-simulation slaves.
*
* The function call sequence is as follows:
*
* 1. `setup()`:
* Configure the slave and enter initialisation mode.
* 2. `set_<type>_variables()`, `get_<type>_variables()`:
* Variable initialisation. The functions may be called multiple times
* in any order.
* 3. `start_simulation()`:
* End initialisation mode, start simulation.
* 4. `do_step()`, `get_<type>_variables()`, `set_<type>_variables()`:
* Simulation. The functions may be called multiple times, in this
* order.
* 5. `end_simulation()`:
* End simulation.
*
* Any method may throw an exception. In almost all cases, the slave will
* be considered "broken" after this happens, and no further method calls
* will be made. The sole exception to this rule is that the
* `set_<type>_variables()` methods may throw `nonfatal_bad_value` to
* indicate that one or more variables were out of the allowed range, but
* that the slave has either ignored or accepted these values and is able
* to proceed.
*/
class slave
{
public:
virtual ~slave() = default;
/// Returns a model description.
virtual cosim::model_description model_description() const = 0;
/**
* Instructs the slave to perform pre-simulation setup and enter
* initialisation mode.
*
* This function is called when the slave has been added to an execution.
* The arguments `startTime` and `stopTime` represent the time interval
* inside which the slave's model equations are required to be valid.
* (In other words, it is guaranteed that do_step() will never be called
* with a time point outside this interval.)
*
* \param [in] startTime
* The earliest possible time point for the simulation.
* \param [in] stopTime
* The latest possible time point for the simulation. May be
* left unspecified if there is no defined stop time.
* \param [in] relativeTolerance
* If specified, this contains the relative tolerance of the step
* size controller. The slave may then use this for error control
* in its internal integrator.
*/
virtual void setup(
time_point startTime,
std::optional<time_point> stopTime,
std::optional<double> relativeTolerance) = 0;
/**
* Informs the slave that the initialisation stage ends and the
* simulation begins.
*/
virtual void start_simulation() = 0;
/// Informs the slave that the simulation run has ended.
virtual void end_simulation() = 0;
/**
* Performs model calculations for the time step which starts at
* the time point `currentT` and has a duration of `deltaT`.
*
* If this is not the first time step, it can be assumed that the previous
* time step ended at `currentT`. It can also be assumed that `currentT`
* is greater than or equal to the start time, and `currentT+deltaT` is
* less than or equal to the stop time, specified in the setup() call.
*
* \returns
* Whether the step completed successfully or not.
* (Non-recoverable problems must be signaled with an exception.)
*
* \note
* Currently, retrying a failed time step is not supported, but this is
* planned for a future version.
*/
virtual step_result do_step(time_point currentT, duration deltaT) = 0;
/**
* Retrieves the values of real variables.
*
* On return, the `values` array will be filled with the values of the
* variables specified in `variables`, in the same order.
*
* \pre `variables.size() == values.size()`
*/
virtual void get_real_variables(
gsl::span<const value_reference> variables,
gsl::span<double> values) const = 0;
/**
* Retrieves the values of integer variables.
*
* On return, the `values` array will be filled with the values of the
* variables specified in `variables`, in the same order.
*
* \pre `variables.size() == values.size()`
*/
virtual void get_integer_variables(
gsl::span<const value_reference> variables,
gsl::span<int> values) const = 0;
/**
* Retrieves the values of boolean variables.
*
* On return, the `values` array will be filled with the values of the
* variables specified in `variables`, in the same order.
*
* \pre `variables.size() == values.size()`
*/
virtual void get_boolean_variables(
gsl::span<const value_reference> variables,
gsl::span<bool> values) const = 0;
/**
* Retrieves the values of string variables.
*
* On return, the `values` array will be filled with the values of the
* variables specified in `variables`, in the same order.
*
* \pre `variables.size() == values.size()`
*/
virtual void get_string_variables(
gsl::span<const value_reference> variables,
gsl::span<std::string> values) const = 0;
/**
* Sets the values of real variables.
*
* This will set the value of each variable specified in the `variables`
* array to the value given in the corresponding element of `values`.
*
* The function may throw `nonfatal_bad_value` to indicate that one or
* more values were out of range or invalid, but that these values have
* been accepted or ignored so the simulation can proceed.
*
* \pre `variables.size() == values.size()`
*/
virtual void set_real_variables(
gsl::span<const value_reference> variables,
gsl::span<const double> values) = 0;
/**
* Sets the values of integer variables.
*
* This will set the value of each variable specified in the `variables`
* array to the value given in the corresponding element of `values`.
*
* The function may throw `nonfatal_bad_value` to indicate that one or
* more values were out of range or invalid, but that these values have
* been accepted or ignored so the simulation can proceed.
*
* \pre `variables.size() == values.size()`
*/
virtual void set_integer_variables(
gsl::span<const value_reference> variables,
gsl::span<const int> values) = 0;
/**
* Sets the values of boolean variables.
*
* This will set the value of each variable specified in the `variables`
* array to the value given in the corresponding element of `values`.
*
* The function may throw `nonfatal_bad_value` to indicate that one or
* more values were out of range or invalid, but that these values have
* been accepted or ignored so the simulation can proceed.
*
* \pre `variables.size() == values.size()`
*/
virtual void set_boolean_variables(
gsl::span<const value_reference> variables,
gsl::span<const bool> values) = 0;
/**
* Sets the values of string variables.
*
* This will set the value of each variable specified in the `variables`
* array to the value given in the corresponding element of `values`.
*
* The function may throw `nonfatal_bad_value` to indicate that one or
* more values were out of range or invalid, but that these values have
* been accepted or ignored so the simulation can proceed.
*
* \pre `variables.size() == values.size()`
*/
virtual void set_string_variables(
gsl::span<const value_reference> variables,
gsl::span<const std::string> values) = 0;
/// Result type for `get_variables()`.
struct variable_values
{
/// Real variable values.
std::vector<double> real;
/// Integer variable values.
std::vector<int> integer;
/// Boolean variable values.
boost::container::vector<bool> boolean;
/// String variable values.
std::vector<std::string> string;
};
void get_variables(
variable_values* values,
gsl::span<const value_reference> real_variables,
gsl::span<const value_reference> integer_variables,
gsl::span<const value_reference> boolean_variables,
gsl::span<const value_reference> string_variables) const
{
if (static_cast<size_t>(values->real.size()) != static_cast<size_t>(real_variables.size()))
values->real.resize(real_variables.size());
if (static_cast<size_t>(values->integer.size()) != static_cast<size_t>(integer_variables.size()))
values->integer.resize(integer_variables.size());
if (static_cast<size_t>(values->boolean.size()) != static_cast<size_t>(boolean_variables.size()))
values->boolean.resize(boolean_variables.size());
if (static_cast<size_t>(values->string.size()) != static_cast<size_t>(string_variables.size()))
values->string.resize(string_variables.size());
get_real_variables(real_variables, values->real);
get_integer_variables(integer_variables, values->integer);
get_boolean_variables(boolean_variables, values->boolean);
get_string_variables(string_variables, values->string);
}
void set_variables(
gsl::span<const value_reference> real_variables,
gsl::span<const double> real_values,
gsl::span<const value_reference> integer_variables,
gsl::span<const int> integer_values,
gsl::span<const value_reference> boolean_variables,
gsl::span<const bool> boolean_values,
gsl::span<const value_reference> string_variables,
gsl::span<const std::string> string_values)
{
set_real_variables(real_variables, real_values);
set_integer_variables(integer_variables, integer_values);
set_boolean_variables(boolean_variables, boolean_values);
set_string_variables(string_variables, string_values);
}
/// A type used for references to saved states (see `save_state()`).
using state_index = int;
/**
* Saves the current state.
*
* This will create and store a copy of the slave's current internal
* state, so that it can be restored at a later time. The copy is stored
* internally in the slave, and must be referred to by the returned
* `state_index`. The index is only valid for this particular slave.
*
* The function may be called at any point after `setup()` has been called.
*/
virtual state_index save_state() = 0;
/**
* Saves the current state, overwriting a previously-saved state.
*
* This function does the same as `save_state()`, except that it
* overwrites a state which has previously been stored by that function.
* The old index thereafter refers to the newly-saved state.
*/
virtual void save_state(state_index stateIndex) = 0;
/**
* Restores a previously-saved state.
*
* This restores the slave to a state which has previously been saved
* using `save_state()`.
*
* Note that the saved state is supposed to be the *complete and exact*
* state of the slave at the moment `save_state()` was called. For example,
* if the state was saved while the slave was in initialisation mode
* (between `setup()` and `start_simulation()`), then it will be restored
* in that mode, and `start_simulation()` must be called before the
* simulation can start. Similarly, if it is saved at logical time `t`,
* then the first `do_step()` call after restoration must start at `t`.
*/
virtual void restore_state(state_index stateIndex) = 0;
/**
* Frees all resources (e.g. memory) associated with a saved state.
*
* After this, the state may no longer be restored with `restore_state()`,
* nor may it be overwritten with `save_state(state_index)`. The
* implementation is free to reuse the same `state_index` at a later point.
*/
virtual void release_state(state_index stateIndex) = 0;
/**
* Exports a saved state.
*
* This returns a previously-saved state in a generic format so it can be
* serialized, e.g. to write it to disk and use it in a later simulation.
*/
virtual serialization::node export_state(state_index stateIndex) const = 0;
/**
* Imports an exported state.
*
* The imported state is added to the slave's internal list of saved
* states. Use `restore_state()` to restore it again. The state must have
* been saved by a slave of the same or a compatible type.
*/
virtual state_index import_state(const serialization::node& exportedState) = 0;
};
} // namespace cosim
#endif // header guard