-
Notifications
You must be signed in to change notification settings - Fork 3
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
Constrained pendulum problem #36
base: master
Are you sure you want to change the base?
Conversation
For use in half-explicit methods and for data assimilation.
% Ascher, Uri. "On symmetric schemes and differential-algebraic equations." | ||
% SIAM journal on scientific and statistical computing 10.5 (1989): 937-949. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this the right source? I don't see anything on a pendulum.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hairer, E., Roche, M., Lubich, C. (1989). Description of differential-algebraic problems. In: The Numerical Solution of Differential-Algebraic Systems by Runge-Kutta Methods. Lecture Notes in Mathematics, vol 1409. Springer, Berlin, Heidelberg. https://doi.org/10.1007/BFb0093948
That is the pendulum problem source.
The Ascher book has a nice formulation as a Hessenberg indeex 2 DAE( linear algebraic variables) we use.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah the citation is wrong.
%Gravity defines the magnitude of acceleration towards the ground | ||
Gravity %MATLAB ONLY: (1,1) {mustBeNonnegative} | ||
%Mass defines the mass of the pendulum | ||
Mass %MATLAB ONLY: (1,1) {mustBeNonnegative} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sharper validation: {mustBeReal, mustBeFinite, mustBePositive}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea
@@ -0,0 +1,35 @@ | |||
classdef ConstrainedPendulumProblem < otp.Problem |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps just PendulumDAEProblem
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This class ought to override internalIndex2label
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree with both
%CONSTRAINEDPENDULUM PROBLEM This is a Hessenberg Index-2 DAE problem posed in terms of three constraints | ||
% | ||
|
||
properties |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These should have SetAccess = private
or protected
initialconstraints = otp.constrainedpendulum.constraints([], obj.Y0, g, m, l, 0); | ||
E0 = initialconstraints(3); | ||
|
||
obj.RHS = otp.RHS(@(t, y) otp.constrainedpendulum.f(t, y, g, m, l, E0)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This RHS will lead to confusion as a call to solve
will not enforce constraints, right? Ideally, RHS
should be in a form that a built-in solver can integrate, but we can let that slide for an index-2 DAE. I suggest the default RHS should use a mass matrix and an f
that operates on the full state (diff and alg). Then there can be a RHSDifferential
and RHSAlgebraic
to get the individual pieces.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also it looks like 'Vectorized', 'on'
can be added to RHS
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with this
@@ -0,0 +1,27 @@ | |||
function dc = constraintsjacobian(~, state, g, m, ~, ~) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What Jacobian is this exactly? For the DAE
y' = f(y, z)
0 = g(y)
is it g_y or f_z*g_y
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in this formulation. y' = fhat(y) - ((g_y)^T)*z. T is transpose.
so g_y * f_z = g_y*((g_y)^T)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this jacobian is actually just g_y
Long term, I would like to have a problem class for constrained mechanical systems (Solving ODEs II pg. 464), and the pendulum would simply be a preset. This would also unify the handling of reduced index forms. |
I agree longterm. Right now we need this problem for something. We might also add a constrained shallow water (2D) |
|
||
y0 = [sqrt(2)/2; sqrt(2)/2; 0; 0]; | ||
|
||
obj = [email protected](tspan, y0, params); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will the solve
functionality be disabled for this problem?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is an interesting question. In theory we should be able to add our own small method to OTP to handle these cases
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. Here's one idea: Add a HighIndexDAE
property to https://github.com/ComputationalScienceLaboratory/ODE-Test-Problems/blob/master/src/%2Botp/%2Butils/Solver.m which simply throws an error message that high-index DAEs are not supported by matlab solvers. Eventually when we have a constrained mechanical system problem, we will support RHS reduced to index-1 which can rely on built-in solvers.
@@ -0,0 +1,27 @@ | |||
function dc = constraintsjacobian(~, state, g, m, ~, ~) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this to be the standard for otp when getting the Jacobian for Half-Explicit methods? Or is this an internal functionality of the problem?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if the problem is posed in terms of invariants, this should be standard. I might rename it to invariantJacobian
@@ -0,0 +1,19 @@ | |||
function dstate = fdifferential(~, state, g, m, ~, ~) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The convention for functions is camel case now: https://github.com/ComputationalScienceLaboratory/ODE-Test-Problems/blob/master/docs/CONTRIBUTING.md#functions
|
||
% The right hand size in terms of x, y, x', y', and three control parameters z | ||
obj.RHS = otp.RHS(@(t, y) otp.pendulumdae.f(t, y, g, m, l, E0), ... | ||
'Mass', @(t, y) otp.pendulumdae.mass(t, y, g, m, l, E0)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the mass matrix is constant, it can be a matrix instead of a function handle. See
'Mass', otp.zlakinetics.mass([], [], k, K, klA, Ks, pCO2, H), ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was just copying the pendulum problem, which had @(t,y). I will change
Unless I am missing something, I think I have addressed all the issues. |
Y0Differential | ||
Z0Algebraic |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not opposed to having these, but if we do include this, I think all DAE problems should have these for consistency. Also, I think Y0Algebraic
would be better than Z0Algebraic
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't really differentiate the two. Algebraic implies there is no direct differential equation thus it is a variable obtained from differentiation of the constraint. I think it should stay.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this naming is pretty clear. It's mostly motivated by established naming conventions. For partitioned problems, we use <property><partition name>
. E.g.
RHSStiff and RHSNonstiff
jacobianDifferential and jacobianAlgebraic
Y0Position and Y0Velocity
This is nice for autocompletion as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My bad I misinterpreted what was being proposed. I thought you wanted Y0Differential -> Y0Algebraic
not Z0Algebraic -> Y0Algebraic
. I agree on the convention you proposed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking of changing it to that already, glad to see we all think alike
% Generate the constituent RHS for the differential part | ||
obj.RHSDifferential = otp.RHS(@(t, y) otp.pendulumdae.fDifferential(t, y, g, m, l, E0), ... | ||
'JacobianVectorProduct', ... | ||
@(t, y, v) otp.pendulumdae.jacobianDifferentialVectorProduct(t, y, g, m, l, E0, v), ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
jacobianVectorProductDifferential
is the naming convention https://github.com/ComputationalScienceLaboratory/ODE-Test-Problems/blob/master/docs/CONTRIBUTING.md#functions-for-rhs
'Vectorized', 'on'); | ||
|
||
% Generate the constituent RHS for the algebraic part | ||
obj.RHSAlgebraic = otp.RHS(@(t, y) otp.pendulumdae.invariants(t, y, g, m, l, E0), ... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The terms "algebraic" and "invariant" are used inconsistently. I think "algebraic" is the more standard DAE term, so I say we should name the functions
fAlgebraic
jacobianAlgebraic
...
Added some final pedantic comments |
src/+otp/+utils/+solvers/dae34.m
Outdated
reltol = odeget(options, 'RelTol', 1e-3); | ||
abstol = odeget(options, 'AbsTol', 1e-6); | ||
J = odeget(options, 'Jacobian', @(t, y) jacapprox(f, t, y)); | ||
M = odeget(options, 'Mass', speye(numel(y0))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should determine if the mass matrix needs to be sparse from the Jacobian. Ideally, we could call eye(n, 'like', J)
but this is not supported in Octave.
h = tend - tc; | ||
end | ||
|
||
stages = zeros(numel(yc), stagenum); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to reallocate every step
src/+otp/+utils/+solvers/dae34.m
Outdated
|
||
np = inf; | ||
|
||
ntol = 1e-6; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be based on AbsTol
and RelTol
src/+otp/+utils/+solvers/dae34.m
Outdated
nmaxits = 5; | ||
its = 0; | ||
while norm(np) > ntol || its < nmaxits | ||
Mc = M(staget, yc + stagedy + gh*newtonk0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the mass matrix is state dependent, a Newton iteration would need dM/dy. We should parse the MStateDependence
property to handle this better
src/+otp/+utils/+solvers/dae34.m
Outdated
Mc = M(staget, yc + stagedy + gh*newtonk0); | ||
g = Mc*newtonk0 - f(staget, yc + stagedy + gh*newtonk0); | ||
H = Mc - gh*J(staget, yc + stagedy + gh*newtonk0); | ||
[np, ~] = gmres(H, g); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A direct solver would be more standard. Unfortunately, Octave does not support decomposition
, so lu
is probably best.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I found that the matrix is often very poorly scaled, so a direct solve was throwing tons of warnings.
src/+otp/+utils/+solvers/dae34.m
Outdated
while norm(np) > ntol || its < nmaxits | ||
Mc = M(staget, yc + stagedy + gh*newtonk0); | ||
g = Mc*newtonk0 - f(staget, yc + stagedy + gh*newtonk0); | ||
H = Mc - gh*J(staget, yc + stagedy + gh*newtonk0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can upgrade Newton iterations to algorithm on pg 119 of Solving ODEs II. This will evaluate the Jacobian only as needed.
src/+otp/+utils/+solvers/dae34.m
Outdated
|
||
Mc = M(tc + h, yc); | ||
|
||
err = rms((Mc*(yc-yhat))./sc); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rms
not supported in Octave
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure why error is multiplied by Mc
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can do manual rms. Error is multiplied by Mc, as one way to do error is to simply look at the differential variables and ignore the algebraic.
|
||
end | ||
|
||
function J = jacapprox(f, t, y) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'll need something a bit more robust or not support finite differences at all.
…lgorithm. The starting algorithm for FIRK can actually just use the previous stages. This speeds up computation and just works. For SDIRK this does not work, just for some Lobatto methods
src/+otp/+utils/+solvers/dae43.m
Outdated
reltol = odeget(options, 'RelTol', 1e-3); | ||
abstol = odeget(options, 'AbsTol', 1e-6); | ||
J = odeget(options, 'Jacobian', []); | ||
M = odeget(options, 'Mass', speye(numel(y0))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Default value should be []
src/+otp/+utils/+solvers/dae43.m
Outdated
|
||
% Compute initial step size | ||
if isempty(h) | ||
sc = abstol + reltol*abs(y0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RelTol
could be a vector. Should use .*
src/+otp/+utils/+solvers/dae43.m
Outdated
step = 1; | ||
|
||
Mc = M(tspan(1), y0); | ||
Mfull = zeros(n*stagenum, n*stagenum, 'like', Mc); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately, 'like'
option is not supported in Octave
src/+otp/+utils/+solvers/dae43.m
Outdated
|
||
end | ||
|
||
function J = jacapprox(f, t, y) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems like a useful enough to warrant it's own publicly-accessible function. We can also look into more sophisticated and robust approaches.
M = @(t, y) eye(numel(y0)); | ||
end | ||
elseif ~isa(M, 'function_handle') | ||
M = @(t, y) M; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We also need to support mass matrices that are only a function of time, e.g., M(t)
All resolved. |
Should I remove the other DAE solvers (SDIRK and ESDIRK)? |
For use in half-explicit methods and for data assimilation.