-
-
Notifications
You must be signed in to change notification settings - Fork 196
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
Newmark beta method #2187
base: master
Are you sure you want to change the base?
Newmark beta method #2187
Conversation
@@ -495,6 +495,7 @@ alg_order(alg::ERKN4) = 4 | |||
alg_order(alg::ERKN5) = 5 | |||
alg_order(alg::ERKN7) = 7 | |||
alg_order(alg::RKN4) = 4 | |||
alg_order(alg::NewmarkBeta) = 1 # or at least 2, depending on the parameters. |
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 coefficients are part of the algorithm, can you calculate the order here?
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.
similar to FBDF, you can just set this to 1 I think
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.
Thanks for taking a look Hendrik! Yes, I could compute them here. I will fix such stuff after I get a pendulum example working. I just opened the PR to discuss the progress and possibly better design choices for the new class of solvers.
The last remaining problem is a bit tricky. For a given DynamicalODEFunction Newmark needs to solve for a "future accelleration" by solving the problem The first issue I run into is passing down the analytical Jacobian: using OrdinaryDiffEq
function f1_harmonic(dv, v, u, p, t)
dv .= -u
end
function f2_harmonic(du, v, u, p, t)
du .= v
end
harmonic_jac_f1(J, v, u, p, t) = J[1,1] = -1.0
ff_harmonic = DynamicalODEFunction(f1_harmonic, f2_harmonic; jac = harmonic_jac_f1)
DiffEqBase.has_jac(ff_harmonic) # false
DiffEqBase.has_jac(ff_harmonic.f1) # false
ff_f1 = ODEFunction{false}(ODEFunction{false}(f1_harmonic); jac=harmonic_jac_f1!)
ff_harmonic = DynamicalODEFunction(ff_f1, f2_harmonic; jac = harmonic_jac_f1)
DiffEqBase.has_jac(ff_harmonic) # false
DiffEqBase.has_jac(ff_harmonic.f1) # false The second problem is getting the AD on track. My current strategy is to pass a helper type |
I think I can call this now a first prototype. I am not very sure why it manages to converge to the correct solution, but I assume the residual is just wrong up to scaling. Another problem is that the Jacobian is per construction wrong. The derivative of However, this seems to break a few assumptions taken in the code. Hence I could need some feedback on how to proceed before putting time into a design which we do not want in the library. I already think that Furthermore I cannot figure out how to integrate the W-transformation here. Any pointers? |
@@ -507,6 +507,7 @@ nlsolve_f(f, alg::DAEAlgorithm) = f | |||
function nlsolve_f(integrator::ODEIntegrator) | |||
nlsolve_f(integrator.f, unwrap_alg(integrator, true)) | |||
end | |||
nlsolve_f(f::DynamicalODEFunction, alg::NewmarkBeta) = f.f1 # FIXME |
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 having a very hard time figuring out whether this seems obviously fine, or an incredibly bad idea.
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.
Yea, as you pointed out on Slack we should switch to SecondOrderODEFunction
IIUC.
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.
Okay the reason why I did this was that there is not SecondOrderODEFunction
. Can we easily infer from the DynamicalODEFunction
that the second function is only f2(du,u,v,p,t) = 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.
oh, that's weird. we have a SecondOrderODEProblem
but no SecondOrderODEFunction
.
struct ArrayPartitionNLSolveHelper{T} | ||
γ₁::T | ||
γ₂::T | ||
end | ||
|
||
function Base.:*(γ::ArrayPartitionNLSolveHelper{T1}, z::ArrayPartition{T2, <: Tuple{<:Any, <:Any}}) where {T1, T2} | ||
ArrayPartition(γ.γ₁*z.x[1], γ.γ₂*z.x[2]) | ||
end | ||
|
||
function Base.:*(γ::ArrayPartitionNLSolveHelper{T1}, z::Vector{T2}) where {T1, T2} | ||
ArrayPartition(γ.γ₁*z, γ.γ₂*z) | ||
end | ||
|
||
Base.:*(γ::ArrayPartitionNLSolveHelper{T}, scalar::T) where T = scalar*γ | ||
|
||
function Base.:*(scalar::T, γ::ArrayPartitionNLSolveHelper{T}) where T | ||
ArrayPartitionNLSolveHelper(scalar*γ.γ₁, scalar*γ.γ₂) | ||
end | ||
|
||
Base.inv(γ::ArrayPartitionNLSolveHelper) = 1/(γ.γ₁+γ.γ₂) |
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 really weird algebra to me. Why does inv(::ArrayPartitionNLSolveHelper)
give a scalar? also having the multiplication with Vector
seems very sketchy. Probably should be AbstractVector
? but It also feels somewhat weird that that method returns an ArrayPartition
?...
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.
Because I am not sure how to integrate a pair of weights with the W transformation. This is basically a hotfix to make the example work at all.
If you take another look at the perform step function you will see what I am trying to achive. Basically the nonlinear solver interface assumes that
@@ -303,7 +303,7 @@ end | |||
# page 54. | |||
γdt = isdae ? α * invγdt : γ * dt | |||
|
|||
!(W_γdt ≈ γdt) && (rmul!(dz, 2 / (1 + γdt / W_γdt))) | |||
# !(W_γdt ≈ γdt) && (rmul!(dz, 2 / (1 + γdt / W_γdt))) |
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.
why is this random line commented out?
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 could not get it to work with Newmark, because here we have a comparison between a scalar W_γdt
and a pair γdt
.
function _compute_rhs!(tmp, ztmp, ustep, γ::ArrayPartitionNLSolveHelper, α, tstep, k, | ||
invγdt, method::MethodType, p, dt, f, z) | ||
mass_matrix = f.mass_matrix | ||
ustep = tmp + γ * z |
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.
why isn't this compute_ustep!
?
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 need to double check what exactly lead to this change, but I think we do not need this anymore.
mutable struct NLSolver{algType, iip, uType, gamType, tmpType, tmp2Type, tType, | ||
C <: AbstractNLSolverCache} <: AbstractNLSolver{algType, iip} | ||
z::uType | ||
tmp::uType # DIRK and multistep methods only use tmp | ||
tmp2::tmpType # for GLM if neccssary | ||
tmp::tmpType # DIRK and multistep methods only use tmp | ||
tmp2::tmp2Type # for GLM if neccssary |
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 you're going to do this, I think it's probably worth refactoring to use innertmp
and outertmp
for consistency (and then we can unify the handling by setting the appropriate one to 0 for the method being used.
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 should go into a different PR.
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.
agreed
integrator.f(cache.fsalfirst, integrator.uprev, integrator.p, integrator.t) | ||
integrator.stats.nf += 1 | ||
integrator.fsalfirst = cache.fsalfirst | ||
integrator.fsallast = cache.fsalfirst |
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.
typo?
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 you ask like this, then I think I am understanding something wrong here. From looking at other FSAL methods I thought that we can just put the same memory into both locations.
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.
never mind on this one then. I haven't read through other ones in a while, so this looked like a copy paste error, but if other methods do it also, then I think this is fine.
upred_full = ArrayPartition( | ||
duprev + dt*(1.0 - γ)*dduprev, | ||
uprev + dt*dt*(0.5 - β)*dduprev + dt*duprev | ||
) |
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 it will allocate a bunch, right?
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.
Indeed. As also pointed out below by you during review we might be able to use nlsolver.tmp
to get rid of this.
# Note: innertmp = nlsolve.tmp | ||
nlsolver.γ = ArrayPartitionNLSolveHelper(γ, β * dt) # = γ̂ | ||
# nlsolver.γ = ArrayPartitionNLSolveHelper(0.0, β * dt) | ||
nlsolver.tmp .= upred_full # TODO check f tmp is potentially modified and if not elimiate the allocation of upred_full |
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 believe nlsolver.tmp
isn't modified, but I'm not 100% sure.
# dt⋅f(innertmp + γ̂⋅z, p, t + c⋅dt) + outertmp = z | ||
# So we rewrite the problem | ||
# u(tₙ₊₁)'' - f₁(ũ(tₙ₊₁) + u(tₙ₊₁)'' β Δtₙ², ũ(tₙ₊₁)' + u(tₙ₊₁)'' γ Δtₙ,t) = 0 | ||
# z = Δtₙ u(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.
I think we generally put the Δtₙ
as part of γ
rather than z
. Is there a reason not to do so?
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.
Note that the
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.
oh. that's awful.
Prototype to resolve #411 .
Checklist
contributor guidelines, in particular the SciML Style Guide and
COLPRAC.