-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathmbeDynFormulationBase.m
334 lines (285 loc) · 14.5 KB
/
mbeDynFormulationBase.m
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
classdef (Abstract) mbeDynFormulationBase < handle
% Virtual base for dynamic solvers. Offerts the service of dyn
% simulations, displaying plots of their results, etc.
% (portions based on old code 'MBS_solve')
% -----------------------------------------------------------------------------
% This file is part of MBDE-MATLAB. See: https://github.com/MBDS/mbde-matlab
%
% MBDE-MATLAB is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% MBDE-MATLAB is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with MBDE-MATLAB. If not, see <http://www.gnu.org/licenses/>.
% -----------------------------------------------------------------------------
methods(Abstract,Access=public)
% Abstract method for all dynamic solvers to calculate
% instantaneous accelerations (both dep & indep accelerations may
% be returned, depending on the method)
%
% [qpp,zpp] = solve_for_accelerations(me,model,q,qp,context)
%
% - context: Simulation context struct. Contents are: Current
% time,dt,...
% - Returned (qp, q) will normally be the same than passed as
% input, except in "projection" methods.
[qpp,zpp, q, qp] = solve_for_accelerations(me,model,q,qp,context);
end
properties(Access=public)
% Whether to show messages, animations, etc.
logging = mbeLoggingParams();
% The integrator scheme for batch_dyn_solve()
% From enumeration above
integrator_type = mbeIntegratorTypes.intTrapezoidal;
% Integration policy:
% - "1" (default) -> numerical integration of **independent coords**, then solve
% pos, vel problems at each timestep (even if the formulation
% produces dependent accelerations, they are ignored).
% - "0" -> numerical integration of **dependent coords**, even if
% the formulation produces independent accelerations, the
% dependent accelerations are computed via a linear "acceleration
% problem".
%
% **Warning:** Disable indep coords integration only in dynamical methods that
% have stabilization (e.g. penalizers, baumbarte)
integrate_indep_coords = 1;
tol_trapezoidal = 1e-10; % dynamic tolerance (trapezoidal integrator)
iter_max_trapezoidal = 100; % max iters for integration (trapezoidal integrator)
end
% --- public API ----
methods(Access=public)
function [states,perf_stats] = batch_dyn_solve(me,model,t_ini,t_end,dt)
% Executes a batch simulation of a given multibody system with the
% integrator defined in the property "integrator_type".
% Accelerations are obtained from the
% specific method implemented in the derived class.
% - model: The MB system parameters
% - t_ini, t_end, dt: Initial, final and step time.
% - states: A struct with Nx1 (N=nTimeSteps) vectors : q,qp,qpp, t
% - perf_stats: A struct with energy & residues info
%
% See also: plot_perf_stats()
% Initial conditions
q_approx = model.q_init_approx;
assert(length(q_approx)==model.dep_coords_count);
zp_init = model.zp_init; % Was: thetap = MBS.thetap; % Initial DOF velocity
nTimeSteps = ceil((t_end-t_ini)/dt);
% get indep/dep coord indices:
iidxs = model.get_indep_indxs();
% Integration context (aux vars,...)
context = struct();
context.model = model;
context.iidxs = iidxs;
context.dt = dt;
context.current_run_time = t_ini;
% initial position, velocity and acceleration problems
q = mbeKinematicsSolver.pos_problem(model,q_approx);
qp = mbeKinematicsSolver.vel_problem(model,q, zp_init);
[qpp,~,q,qp] = me.solve_for_accelerations(model,q,qp, context);
% TODO: Detect the optional residual output vars, don't waste
% time computing them if not present.
do_calc_perf_indicators = 1;
% Output matrix:
states = struct();
states.t = zeros(nTimeSteps,1);
states.q = zeros(nTimeSteps,model.dep_coords_count);
states.qp = zeros(nTimeSteps,model.dep_coords_count);
states.qpp = zeros(nTimeSteps,model.dep_coords_count);
% Verification variables
perf_stats = struct();
if (do_calc_perf_indicators)
perf_stats.T = zeros (1, nTimeSteps);
perf_stats.Vg = zeros (1, nTimeSteps);
perf_stats.E_mec = zeros (1, nTimeSteps);
% Constraints fulfilment
perf_stats.resid_pos = zeros(1, nTimeSteps);
perf_stats.resid_vel = zeros(1, nTimeSteps);
perf_stats.resid_acc = zeros(1, nTimeSteps);
end
% For logging progress:
logstep_last = 0;
logstep_nSteps = 10;
logstep_incr = nTimeSteps/logstep_nSteps;
% For debugging animations:
liveanim_last = 0;
if (me.logging.show_mech_anim_fps>0)
liveanim_fig = figure();
liveanim_incr = (1.0/me.logging.show_mech_anim_fps)/dt;
else
liveanim_incr = Inf; % Disabled
end
% MBS loop
for i = 1:nTimeSteps,
% Set iteration:
context.current_run_time = t_ini + i*dt;
if (i>=logstep_last+logstep_incr || i==nTimeSteps)
pcdone = 100.0 * i / nTimeSteps;
me.logging.log_level(1, sprintf('batch_dyn_solve: %.02f%% done. iter %u/%u, simul_time=%.03fs/%.03fs',...
pcdone, ...
i,nTimeSteps, ...
context.current_run_time,t_end ));
logstep_last = i;
end
% Perform numerical integration:
% ----------------------------------
switch me.integrator_type
case mbeIntegratorTypes.intEuler
[q,qp,qpp] = me.ni_step_euler(q,qp,qpp, context);
case mbeIntegratorTypes.intTrapezoidal
[q,qp,qpp] = me.ni_step_trapezoidal(q,qp,qpp, context);
case mbeIntegratorTypes.intI3AL
[q,qp,qpp] = me.ni_step_I3AL(q,qp,qpp, context);
otherwise
error('Unknown integrator scheme!');
end
% Store dynamical results:
states.t(i) = context.current_run_time;
states.q(i,:) = q';
states.qp(i,:) = qp';
states.qpp(i,:) = qpp';
% Store performance data:
if (do_calc_perf_indicators)
perf_stats.T(i) = 0.5*qp'*model.M*qp;
perf_stats.Vg(i) = model.eval_potential_energy(q);
perf_stats.E_mec(i) = perf_stats.T(i)+perf_stats.Vg(i);
phi_q = model.jacob_phi_q(q);
perf_stats.resid_pos(i) = norm(model.phi(q));
perf_stats.resid_vel(i) = norm(phi_q*qp);
perf_stats.resid_acc(i) = norm(model.jacob_phiqp_times_qp(q,qp)+phi_q*qpp);
end
% Debug animations: GT, BadModel, Estim
if (i>=liveanim_last+liveanim_incr)
clf; hold on;
% Plot mechanism state:
model.plot_model_skeleton(q, 'b', 1); % the last 1=fit plot axes
% Title:
title('Mechanism skeleton visualization');
drawnow;
liveanim_last=i;
end
end % end for each timestep
end % batch_dyn_solve
function [q_next,qp_next,qpp_next] = integrate_one_timestep(me,model,current_time, dt, q,qp,qpp )
% perform one single integration timestep.
% Integration context (aux vars,...)
context = struct();
context.model = model;
context.iidxs = model.get_indep_indxs();
context.dt = dt;
context.current_run_time = current_time;
% Perform numerical integration:
% ----------------------------------
switch me.integrator_type
case mbeIntegratorTypes.intEuler
[q_next,qp_next,qpp_next] = me.ni_step_euler(q,qp,qpp, context);
case mbeIntegratorTypes.intTrapezoidal
[q_next,qp_next,qpp_next] = me.ni_step_trapezoidal(q,qp,qpp, context);
case mbeIntegratorTypes.intI3AL
[q_next,qp_next,qpp_next] = me.ni_step_I3AL(q,qp,qpp, context);
otherwise
error('Unknown integrator scheme!');
end
end % integrate_one_timestep()
end % end of public methods
methods(Static,Access=public)
function [] = plot_perf_stats(s)
% Shows the results from batch_dyn_solve: Conservation of
% energy and constraints fulfilment
% [] = plot_perf_stats(s)
% - s: perf_stats from batch_dyn_solve()
% (Old code: MBS_plots)
long = length(s.E_mec);
figure
clf
hold on
grid on
plot (1:long,s.E_mec, 'r')
plot (1:long,s.T, 'g')
plot (1:long,s.Vg, 'b')
EnCon = abs(100 - abs(max(s.E_mec) - min(s.E_mec))*100/s.E_mec(1));
str = sprintf('Conservation of energy: %.3f %%\n',EnCon);
Box = uicontrol('style','text');
set(Box,'String',str)
set(Box,'Position',[80,-20,200,35])
legend ('Mechanical', 'Kinetic ', 'Potential')
title('Conservation of energy')
hold off
figure
clf
hold on
grid on
plot (1:long, s.resid_pos, 'r')
plot (1:long, s.resid_vel, 'b')
plot (1:long, s.resid_acc, 'g')
legend ({'Position', 'Velocity', 'Acceleration'})
title('Constraints fulfilment')
hold off
end % plot_perf_stats
end % end of public (static) methods
methods(Access=protected)
function [q_next,qp_next,qpp_next] = ni_step_I3AL(me, q,qp,qpp, context)
% formulation-specific formula:
[qpp_next,zpp_next,q_next, qp_next] = solve_for_accelerations(me, context.model,q,qp, context);
end
function [q_next,qp_next,qpp_next] = ni_step_trapezoidal(me, q,qp,qpp, context)
% NUM. INTEGRATION IMPLEMENTATION: Trapezoidal rule
dt = context.dt;
if (me.integrate_indep_coords)
iidxs=context.iidxs;
z = q(iidxs);
zp = qp(iidxs);
zpp = qpp(iidxs);
zpp_next=zpp;
end
err = 1; ite = 0;
% Initial values (will be refined in the first loop below):
q_next = q; qpp_next = qpp;
while err>me.tol_trapezoidal && ite < me.iter_max_trapezoidal
q_old = q_next;
if (me.integrate_indep_coords)
z_next = z + zp*dt + dt^2/4*(zpp+zpp_next);
zp_next = zp + dt/2*(zpp+zpp_next);
q_next(iidxs) = z_next;
q_next = mbeKinematicsSolver.pos_problem( context.model,q_next); %ini_pos(q_next,PARAMS);
qp_next = mbeKinematicsSolver.vel_problem( context.model,q_next,zp_next); % Solve for dep. vels
else
q_next = q + qp*dt + dt^2/4*(qpp+qpp_next);
qp_next = qp + dt/2*(qpp+qpp_next);
end
% New acceleration vector: invoke the
% formulation-specific formula:
[qpp_next,zpp_next,q_next, qp_next] = solve_for_accelerations(me, context.model,q_next,qp_next, context);
% next iter:
ite=ite+1;
err = norm (q_old-q_next);
end
end % ni_step_trapezoidal
function [q_next,qp_next,qpp_next] = ni_step_euler(me, q,qp,qpp, context)
% NUM. INTEGRATION IMPLEMENTATION: Explicit Euler rule
dt = context.dt;
if (me.integrate_indep_coords)
iidxs=context.iidxs;
z = q(iidxs);
zp = qp(iidxs);
zpp = qpp(iidxs);
z_next = z + zp * dt;
zp_next = zp + zpp * dt;
q_next = q; % Init value
q_next(iidxs) = z_next;
q_next = mbeKinematicsSolver.pos_problem( context.model,q_next); %ini_pos(q_next,PARAMS);
qp_next = mbeKinematicsSolver.vel_problem( context.model,q_next,zp_next); % Solve for dep. vels
else
q_next = q + qp * dt;
qp_next = qp + qpp * dt;
end
% New acceleration vector: invoke the formulation-specific formula:
[qpp_next,~,q_next, qp_next] = solve_for_accelerations(me, context.model,q_next,qp_next,context);
end % ni_step_euler
end
end