/
simulation.py
256 lines (231 loc) · 9.52 KB
/
simulation.py
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
from IPython.core.debugger import Tracer; dh = Tracer()
from casadi import *
from assimulo.problem import Explicit_Problem, Implicit_Problem
from assimulo.solvers.sundials import CVode, IDA
from assimulo.solvers import Radau5DAE
from scipy.optimize import fsolve
import numpy as np
import symbolic_processing as sp
import itertools
import time as timing
def simulate(model, init_cond, start_time=0., final_time=1., input=(lambda t: []), ncp=500, blt=True,
elimination_options=sp.EliminationOptions(), expand_to_sx=True, suppress_alg=False,
tol=1e-8, solver="IDA"):
"""
Simulate model from CasADi Interface using CasADi.
init_cond is a dictionary containing initial conditions for all variables.
"""
if blt:
t_0 = timing.time()
blt_model = sp.BLTModel(model, elimination_options)
blt_time = timing.time() - t_0
print("BLT analysis time: %.3f s" % blt_time)
blt_model._model = model
model = blt_model
if elimination_options['closed_form']:
solved_vars = model._solved_vars
solved_expr = model._solved_expr
#~ for (var, expr) in itertools.izip(solved_vars, solved_expr):
#~ print('%s := %s' % (var.getName(), expr))
return model
dh() # This is not a debug statement!
# Extract model variables
model_states = [var for var in model.getVariables(model.DIFFERENTIATED) if not var.isAlias()]
model_derivatives = [var for var in model.getVariables(model.DERIVATIVE) if not var.isAlias()]
model_algs = [var for var in model.getVariables(model.REAL_ALGEBRAIC) if not var.isAlias()]
model_inputs = [var for var in model.getVariables(model.REAL_INPUT) if not var.isAlias()]
states = [var.getVar() for var in model_states]
derivatives = [var.getMyDerivativeVariable().getVar() for var in model_states]
algebraics = [var.getVar() for var in model_algs]
inputs = [var.getVar() for var in model_inputs]
n_x = len(states)
n_y = len(states) + len(algebraics)
n_w = len(algebraics)
n_u = len(inputs)
# Create vectorized model variables
t = model.getTimeVariable()
y = MX.sym("y", n_y)
yd = MX.sym("yd", n_y)
u = MX.sym("u", n_u)
# Extract the residuals and substitute the (x,z) variables for the old variables
scalar_vars = states + algebraics + derivatives + inputs
vector_vars = [y[k] for k in range(n_y)] + [yd[k] for k in range(n_x)] + [u[k] for k in range(n_u)]
[dae] = substitute([model.getDaeResidual()], scalar_vars, vector_vars)
# Fix parameters
if not blt:
# Sort parameters
par_kinds = [model.BOOLEAN_CONSTANT,
model.BOOLEAN_PARAMETER_DEPENDENT,
model.BOOLEAN_PARAMETER_INDEPENDENT,
model.INTEGER_CONSTANT,
model.INTEGER_PARAMETER_DEPENDENT,
model.INTEGER_PARAMETER_INDEPENDENT,
model.REAL_CONSTANT,
model.REAL_PARAMETER_INDEPENDENT,
model.REAL_PARAMETER_DEPENDENT]
pars = reduce(list.__add__, [list(model.getVariables(par_kind)) for
par_kind in par_kinds])
# Get parameter values
model.calculateValuesForDependentParameters()
par_vars = [par.getVar() for par in pars]
par_vals = [model.get_attr(par, "_value") for par in pars]
# Eliminate parameters
[dae] = casadi.substitute([dae], par_vars, par_vals)
# Extract initial conditions
y0 = [init_cond[var.getName()] for var in model_states] + [init_cond[var.getName()] for var in model_algs]
yd0 = [init_cond[var.getName()] for var in model_derivatives] + n_w * [0.]
# Create residual CasADi functions
dae_res = MXFunction([t, y, yd, u], [dae])
dae_res.setOption("name", "complete_dae_residual")
dae_res.init()
###################
#~ import matplotlib.pyplot as plt
#~ h = MX.sym("h")
#~ iter_matrix_expr = dae_res.jac(2)/h + dae_res.jac(1)
#~ iter_matrix = MXFunction([t, y, yd, u, h], [iter_matrix_expr])
#~ iter_matrix.init()
#~ n = 100;
#~ hs = np.logspace(-8, 1, n);
#~ conds = [np.linalg.cond(iter_matrix.call([0, y0, yd0, input(0), hval])[0].toArray()) for hval in hs]
#~ plt.close(1)
#~ plt.figure(1)
#~ plt.loglog(hs, conds, 'b-')
#~ #plt.gca().invert_xaxis()
#~ plt.grid('on')
#~ didx = range(4, 12) + range(30, 33)
#~ aidx = [i for i in range(33) if i not in didx]
#~ didx = range(10)
#~ aidx = []
#~ F = MXFunction([t, y, yd, u], [dae[didx]])
#~ F.init()
#~ G = MXFunction([t, y, yd, u], [dae[aidx]])
#~ G.init()
#~ dFddx = F.jac(2)[:, :n_x]
#~ dFdx = F.jac(1)[:, :n_x]
#~ dFdy = F.jac(1)[:, n_x:]
#~ dGdx = G.jac(1)[:, :n_x]
#~ dGdy = G.jac(1)[:, n_x:]
#~ E_matrix = MXFunction([t, y, yd, u, h], [dFddx])
#~ E_matrix.init()
#~ E_cond = np.linalg.cond(E_matrix.call([0, y0, yd0, input(0), hval])[0].toArray())
#~ iter_matrix_expr = vertcat([horzcat([dFddx + h*dFdx, h*dFdy]), horzcat([dGdx, dGdy])])
#~ iter_matrix = MXFunction([t, y, yd, u, h], [iter_matrix_expr])
#~ iter_matrix.init()
#~ n = 100
#~ hs = np.logspace(-8, 1, n)
#~ conds = [np.linalg.cond(iter_matrix.call([0, y0, yd0, input(0), hval])[0].toArray()) for hval in hs]
#~ plt.loglog(hs, conds, 'b--')
#~ plt.gca().invert_xaxis()
#~ plt.grid('on')
#~ plt.xlabel('$h$')
#~ plt.ylabel('$\kappa$')
#~ plt.show()
#~ dh()
###################
# Expand to SX
if expand_to_sx:
dae_res = SXFunction(dae_res)
dae_res.init()
# Create DAE residual Assimulo function
def dae_residual(t, y, yd):
dae_res.setInput(t, 0)
dae_res.setInput(y, 1)
dae_res.setInput(yd, 2)
dae_res.setInput(input(t), 3)
dae_res.evaluate()
return dae_res.getOutput(0).toArray().reshape(-1)
# Set up simulator
problem = Implicit_Problem(dae_residual, y0, yd0, start_time)
if solver == "IDA":
simulator = IDA(problem)
elif solver == "Radau5DAE":
simulator = Radau5DAE(problem)
else:
raise ValueError("Unknown solver %s" % solver)
simulator.rtol = tol
simulator.atol = 1e-4 * np.array([model.get_attr(var, "nominal") for var in model_states + model_algs])
#~ simulator.atol = tol * np.ones([n_y, 1])
simulator.report_continuously = True
# Log method order
if solver == "IDA":
global order
order = []
def handle_result(solver, t, y, yd):
global order
order.append(solver.get_last_order())
solver.t_sol.extend([t])
solver.y_sol.extend([y])
solver.yd_sol.extend([yd])
problem.handle_result = handle_result
# Suppress algebraic variables
if suppress_alg:
if isinstance(suppress_alg, bool):
simulator.algvar = n_x * [True] + (n_y - n_x) * [False]
else:
simulator.algvar = n_x * [True] + suppress_alg
simulator.suppress_alg = True
# Simulate
t_0 = timing.time()
(t, y, yd) = simulator.simulate(final_time, ncp)
simul_time = timing.time() - t_0
stats = {'time': simul_time, 'steps': simulator.statistics['nsteps']}
if solver == "IDA":
stats['order'] = order
# Generate result for time and inputs
class SimulationResult(dict):
pass
res = SimulationResult()
res.stats = stats
res['time'] = t
if u.numel() > 0:
input_names = [var.getName() for var in model_inputs]
for name in input_names:
res[name] = []
for time in t:
input_val = input(time)
for (name, val) in itertools.izip(input_names, input_val):
res[name].append(val)
# Create results for everything else
if blt:
# Iteration variables
i = 0
for var in model_states:
res[var.getName()] = y[:, i]
res[var.getMyDerivativeVariable().getName()] = yd[:, i]
i += 1
for var in model_algs:
res[var.getName()] = y[:, i]
i += 1
# Create function for computing solved algebraics
for (_, sol_alg) in model._explicit_solved_algebraics:
res[sol_alg.name] = []
alg_sol_f = casadi.MXFunction(model._known_vars + model._explicit_unsolved_vars, model._solved_expr)
alg_sol_f.init()
if expand_to_sx:
alg_sol_f = casadi.SXFunction(alg_sol_f)
alg_sol_f.init()
# Compute solved algebraics
for k in xrange(len(t)):
for (i, var) in enumerate(model._known_vars + model._explicit_unsolved_vars):
alg_sol_f.setInput(res[var.getName()][k], i)
alg_sol_f.evaluate()
for (j, sol_alg) in model._explicit_solved_algebraics:
res[sol_alg.name].append(alg_sol_f.getOutput(j).toScalar())
else:
res_vars = model_states + model_algs
for (i, var) in enumerate(res_vars):
res[var.getName()] = y[:, i]
der_var = var.getMyDerivativeVariable()
if der_var is not None:
res[der_var.getName()] = yd[:, i]
# Add results for all alias variables (only treat time-continuous variables) and convert to array
if blt:
res_model = model._model
else:
res_model = model
for var in res_model.getAllVariables():
if var.getVariability() == var.CONTINUOUS:
res[var.getName()] = np.array(res[var.getModelVariable().getName()])
res["time"] = np.array(res["time"])
res._blt_model = blt_model
return res