Skip to content

Commit ede84eb

Browse files
authored
Merge pull request #170 from ReactionMechanismGenerator/multidomainsens
Multidomain and Interfaces Adjoint Sensitivity Analysis
2 parents 1677c4c + 113b50a commit ede84eb

File tree

5 files changed

+143
-19
lines changed

5 files changed

+143
-19
lines changed

src/Domain.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1630,7 +1630,7 @@ end
16301630
@simd for i in domain.indexes[1]:domain.indexes[2]
16311631
@inbounds @fastmath jac[i,i] -= flow/N
16321632
end
1633-
@views @inbounds @fastmath jac[domain.indexes[1]:domain.indexes[2],domain.indexes[3]] .-= flow.*ns.*(-C)
1633+
@views @inbounds @fastmath jac[domain.indexes[1]:domain.indexes[2],domain.indexes[3]] .-= -flow.*ns/(N*V)
16341634
end
16351635
end
16361636
end

src/Interface.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ function evaluate(ri::ReactiveInternalInterfaceConstantTPhi,dydt,domains,T1,T2,p
113113
end
114114

115115
function evaluate(ri::ReactiveInternalInterfaceConstantTPhi,dydt,domains,T1,T2,phi1,phi2,Gs1,Gs2,cstot,p)
116-
if all(p[ri.parameterindexes[1]:ri.parameterindexes[2]] .== ri.kfs)
116+
if p[ri.parameterindexes[1]:ri.parameterindexes[2]] == ri.kfs
117117
kfs = ri.kfs
118118
else
119119
kfs = p[ri.parameterindexes[1]:ri.parameterindexes[2]]

src/Reactor.jl

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ function Reactor(domains::T,y0s::W,tspan::W2,interfaces::Z=Tuple(),ps::X=DiffEqB
6969
domain.rxnarray[i,j] += k-1
7070
end
7171
end
72+
for i in 1:length(domain.constantspeciesinds)
73+
domain.constantspeciesinds[i] += k-1
74+
end
75+
for (thermovar,ind) in domain.thermovariabledict
76+
domain.thermovariabledict[thermovar] += k-1
77+
end
7278
domain.indexes[1] = k
7379
k += Nspcs
7480
domain.indexes[2] = k-1
@@ -241,7 +247,7 @@ export getrates
241247
end
242248

243249
@inline function addreactionratecontributions!(dydt::Q,rarray::Array{W2,2},cs::W,kfs::Z,krevs::Y,V) where {Q,Z,Y,T,W,W2}
244-
@inbounds for i = 1:size(rarray)[2]
250+
@inbounds @simd for i = 1:size(rarray)[2]
245251
if @inbounds rarray[2,i] == 0
246252
@inbounds @fastmath fR = kfs[i]*cs[rarray[1,i]]
247253
elseif @inbounds rarray[3,i] == 0
@@ -326,7 +332,8 @@ export addreactionratecontributionsforwardreverse!
326332
return dydt
327333
end
328334
@inline function dydtreactor!(dydt::RC,y::U,t::Z,domains::Q,interfaces::B;p::RV=DiffEqBase.NullParameters(),sensitivity::Bool=true) where {RC,RV,B,Z,U,Q<:Tuple}
329-
cstot = zeros(typeof(y).parameters[1],length(y))
335+
cstot = similar(y)
336+
cstot .= 0.0
330337
dydt .= 0.0
331338
domain = domains[1]
332339
ns,cs,T,P,V,C,N,mu,kfs,krevs,Hs,Us,Gs,diffs,Cvave,cpdivR,phi = calcthermo(domain,y,t,p)

src/Simulation.jl

Lines changed: 61 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,9 @@ based alternative algorithm is slower, but avoids this concern.
354354
"""
355355
function getadjointsensitivities(bsol::Q,target::String,solver::W;sensalg::W2=InterpolatingAdjoint(autojacvec=ReverseDiffVJP(false)),abstol::Float64=1e-6,reltol::Float64=1e-3,kwargs...) where {Q,W,W2}
356356
@assert target in bsol.names || target in ["T","V","P"]
357+
358+
pethane = 160
359+
357360
if target in ["T","V","P"]
358361
if haskey(bsol.domain.thermovariabledict, target)
359362
ind = bsol.domain.thermovariabledict[target]
@@ -362,14 +365,16 @@ function getadjointsensitivities(bsol::Q,target::String,solver::W;sensalg::W2=In
362365
end
363366
else
364367
ind = findfirst(isequal(target),bsol.names)
365-
sensdomain,sensspcnames,senstooriginspcind,senstooriginrxnind = getsensdomain(bsol.domain,ind)
366-
if :thermovariabledict in fieldnames(typeof(bsol.domain))
367-
yinds = vcat(senstooriginspcind,collect(values(bsol.domain.thermovariabledict)))
368-
else
369-
yinds = vcat(senstooriginspcind)
368+
if isempty(bsol.interfaces)
369+
sensdomain,sensspcnames,senstooriginspcind,senstooriginrxnind = getsensdomain(bsol.domain,ind)
370+
if :thermovariabledict in fieldnames(typeof(bsol.domain))
371+
yinds = vcat(senstooriginspcind,collect(values(bsol.domain.thermovariabledict)))
372+
else
373+
yinds = vcat(senstooriginspcind)
374+
end
375+
pinds = vcat(senstooriginspcind,length(bsol.domain.phase.species).+senstooriginrxnind)
376+
ind = findfirst(isequal(target),sensspcnames)
370377
end
371-
pinds = vcat(senstooriginspcind,length(bsol.domain.phase.species).+senstooriginrxnind)
372-
ind = findfirst(isequal(target),sensspcnames)
373378
end
374379

375380
function sensg(y::X,p::Array{Y,1},t::Z) where {Q,V,X,Y<:Float64,Z}
@@ -399,19 +404,19 @@ function getadjointsensitivities(bsol::Q,target::String,solver::W;sensalg::W2=In
399404

400405
function g(y::X,p::Array{Y,1},t::Z) where {Q,V,X,Y<:Float64,Z}
401406
dy = similar(y,length(y))
402-
return dydtreactor!(dy,y,t,bsol.domain,[],p=p)[ind]
407+
return dydtreactor!(dy,y,t,bsol.domain,bsol.interfaces,p=p)[ind]
403408
end
404409
function g(y::Array{X,1},p::Y,t::Z) where {Q,V,X<:Float64,Y,Z}
405410
dy = similar(p,length(y))
406-
return dydtreactor!(dy,y,t,bsol.domain,[],p=p)[ind]
411+
return dydtreactor!(dy,y,t,bsol.domain,bsol.interfaces,p=p)[ind]
407412
end
408413
function g(y::Array{X,1},p::Array{Y,1},t::Z) where {Q,V,X<:Float64,Y<:Float64,Z}
409414
dy = zeros(length(y))
410-
return dydtreactor!(dy,y,t,bsol.domain,[],p=p)[ind]
415+
return dydtreactor!(dy,y,t,bsol.domain,bsol.interfaces,p=p)[ind]
411416
end
412417
function g(y::Array{X,1},p::Array{Y,1},t::Z) where {Q,V,X<:ForwardDiff.Dual,Y<:ForwardDiff.Dual,Z}
413418
dy = similar(y,length(y))
414-
return dydtreactor!(dy,y,t,bsol.domain,[],p=p)[ind]
419+
return dydtreactor!(dy,y,t,bsol.domain,bsol.interfaces,p=p)[ind]
415420
end
416421

417422
dsensgdu(out, y, p, t) = ForwardDiff.gradient!(out, y -> sensg(y, p, t), y)
@@ -423,23 +428,64 @@ function getadjointsensitivities(bsol::Q,target::String,solver::W;sensalg::W2=In
423428
dgdurevdiff(out, y, p, t) = ReverseDiff.gradient!(out, y -> g(y, p, t), y)
424429
dgdprevdiff(out, y, p, t) = ReverseDiff.gradient!(out, p -> g(y, p, t), p)
425430

426-
pethane = 160
427431
if length(bsol.domain.p)<= pethane
428-
if target in ["T","V","P"]
432+
if target in ["T","V","P"] || !isempty(bsol.interfaces)
429433
du0,dpadj = adjoint_sensitivities(bsol.sol,solver,g,nothing,(dgdu,dgdp);sensealg=sensalg,abstol=abstol,reltol=reltol,kwargs...)
430434
else
431435
du0,dpadj = adjoint_sensitivities(bsol.sol,solver,sensg,nothing,(dsensgdu,dsensgdp);sensealg=sensalg,abstol=abstol,reltol=reltol,kwargs...)
432436
end
433437
else
434-
if target in ["T","V","P"]
438+
if target in ["T","V","P"] || !isempty(bsol.interfaces)
435439
du0,dpadj = adjoint_sensitivities(bsol.sol,solver,g,nothing,(dgdurevdiff,dgdprevdiff);sensealg=sensalg,abstol=abstol,reltol=reltol,kwargs...)
436440
else
437441
du0,dpadj = adjoint_sensitivities(bsol.sol,solver,sensg,nothing,(dsensgdurevdiff,dsensgdprevdiff);sensealg=sensalg,abstol=abstol,reltol=reltol,kwargs...)
438442
end
439443
end
440444
dpadj[length(bsol.domain.phase.species)+1:end] .*= bsol.domain.p[length(bsol.domain.phase.species)+1:end]
441-
if !(target in ["T","V","P"])
445+
if !(target in ["T","V","P"]) && isempty(bsol.interfaces)
442446
dpadj ./= bsol.sol(bsol.sol.t[end])[senstooriginspcind[ind]]
447+
elseif !(target in ["T","V","P"]) && !isempty(bsol.interfaces)
448+
dpadj ./= bsol.sol(bsol.sol.t[end])[ind]
449+
end
450+
return dpadj
451+
end
452+
453+
function getadjointsensitivities(syssim::Q,bsol::W3,target::String,solver::W;sensalg::W2=InterpolatingAdjoint(autojacvec=ReverseDiffVJP(false)),abstol::Float64=1e-6,reltol::Float64=1e-3,kwargs...) where {Q,W,W2,W3}
454+
@assert target in bsol.names || target in ["T","V","P"]
455+
if target in ["T","V","P"]
456+
if haskey(bsol.domain.thermovariabledict, target)
457+
ind = bsol.domain.thermovariabledict[target]
458+
else
459+
throw(error("$(bsol.domain) doesn't have $target in its thermovariables"))
460+
end
461+
else
462+
ind = findfirst(isequal(target),bsol.names)+bsol.domain.indexes[1]-1
463+
end
464+
domains = Tuple([x.domain for x in syssim.sims])
465+
function g(y::X,p::Array{Y,1},t::Z) where {Q,V,X,Y<:Float64,Z}
466+
dy = similar(y,length(y))
467+
return dydtreactor!(dy,y,t,domains,syssim.interfaces,p=p)[ind]
468+
end
469+
function g(y::Array{X,1},p::Y,t::Z) where {Q,V,X<:Float64,Y,Z}
470+
dy = similar(p,length(y))
471+
return dydtreactor!(dy,y,t,domains,syssim.interfaces,p=p)[ind]
472+
end
473+
function g(y::Array{Float64,1},p::Array{Float64,1},t::Z) where {Q,V,Z}
474+
dy = similar(p,length(y))
475+
return dydtreactor!(dy,y,t,domains,syssim.interfaces,p=p)[ind]
476+
end
477+
function g(y::Array{X,1},p::Array{Y,1},t::Z) where {Q,V,X<:ForwardDiff.Dual,Y<:ForwardDiff.Dual,Z}
478+
dy = similar(y,length(y))
479+
return dydtreactor!(dy,y,t,domains,syssim.interfaces,p=p)[ind]
480+
end
481+
dgdu(out, y, p, t) = ForwardDiff.gradient!(out, y -> g(y, p, t), y)
482+
dgdp(out, y, p, t) = ForwardDiff.gradient!(out, p -> g(y, p, t), p)
483+
du0,dpadj = adjoint_sensitivities(syssim.sol,solver,g,nothing,(dgdu,dgdp);sensealg=sensalg,abstol=abstol,reltol=reltol,kwargs...)
484+
for domain in domains
485+
dpadj[domain.parameterindexes[1]+length(domain.phase.species):domain.parameterindexes[2]] .*= syssim.p[domain.parameterindexes[1]+length(domain.phase.species):domain.parameterindexes[2]]
486+
end
487+
if !(target in ["T","V","P"])
488+
dpadj ./= bsol.sol(bsol.sol.t[end])[ind]
443489
end
444490
return dpadj
445491
end

src/TestReactors.jl

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,46 @@ rerr = [isinf(x) ? 0.0 : x for x in rerr]
124124
@test all((abs.(rerr) .> 1e-1).==false)
125125
end;
126126

127+
#Constant T and P Ideal Gas
128+
@testset "Test constant T and P reactor with interfaces simulation" begin
129+
#Define the phase (how species thermodynamic and kinetic properties calculated)
130+
initialconds = Dict(["T"=>1000.0,"P"=>1e5,"H2"=>0.67,"O2"=>0.33]) #Set simulation Initial Temp and Pressure
131+
domain,y0,p = ConstantTPDomain(phase=ig,initialconds=initialconds) #Define the domain (encodes how system thermodynamic properties calculated)
132+
133+
interfaces = [Inlet(domain,Dict{String,Float64}("H2"=>0.67,"O2"=>0.33,"T"=>1000.0,"P"=>1e5),x->0.001),
134+
Outlet(domain,x->0.001)]
135+
136+
react = Reactor(domain,y0,(0.0,150.11094),interfaces;p=p) #Create the reactor object
137+
sol = solve(react.ode,CVODE_BDF(),abstol=1e-20,reltol=1e-12); #solve the ode associated with the reactor
138+
sim = Simulation(sol,domain,interfaces);
139+
140+
#analytic jacobian vs. ForwardDiff jacobian
141+
t = 20.44002454;
142+
y = sol(t)
143+
ja = jacobiany(y,p,t,domain,interfaces,nothing);
144+
j = jacobianyforwarddiff(y,p,t,domain,interfaces,nothing);
145+
@test all((abs.(ja.-j) .> 1e-4.*abs.(j).+1e-16).==false)
146+
147+
jpa = jacobianp(y,p,t,domain,interfaces,nothing);
148+
jp = jacobianpforwarddiff(y,p,t,domain,interfaces,nothing);
149+
@test all((abs.(jpa.-jp) .> 1e-4.*abs.(jp).+1e-16).==false)
150+
151+
#sensitivities
152+
dps = getadjointsensitivities(sim,"H2",CVODE_BDF(linear_solver=:GMRES);sensealg=InterpolatingAdjoint(autojacvec=ReverseDiffVJP(true)),abstol=1e-16,reltol=1e-6)
153+
react2 = Reactor(domain,y0,(0.0,150.11094),interfaces;p=p,forwardsensitivities=true)
154+
sol2 = solve(react2.ode,CVODE_BDF(linear_solver=:GMRES),abstol=1e-21,reltol=1e-7); #solve the ode associated with the reactor
155+
sim2 = Simulation(sol2,domain,interfaces)
156+
157+
x,dp = extract_local_sensitivities(sol2,150.11094);
158+
ind = findfirst(isequal("H2"),sim2.names)
159+
dpvs = [v[ind] for v in dp]
160+
dpvs[length(domain.phase.species)+1:end] .*= domain.p[length(domain.phase.species)+1:end]
161+
dpvs ./= sol2(150.11094)[ind]
162+
rerr = (dpvs .- dps')./dpvs
163+
rerr = [isinf(x) ? 0.0 : x for x in rerr]
164+
@test all((abs.(rerr) .> 1e-1).==false)
165+
end;
166+
127167
#Constant V adiabatic Ideal Gas
128168
#uses superminimal.yml mechanism
129169
@testset "Constant volume adiabatic reactor simulation" begin
@@ -266,6 +306,37 @@ end;
266306

267307

268308

309+
@testset "Multi-domain ConstantV sensitivity analysis" begin
310+
phaseDict = readinput("../src/testing/superminimal.rms")
311+
spcs = phaseDict["phase"]["Species"]
312+
rxns = phaseDict["phase"]["Reactions"]
313+
ig = IdealGas(spcs,rxns,name="phase")
314+
315+
initialcondsV = Dict(["T"=>1000.0,"P"=>10.0e5,"H2"=>0.67,"O2"=>0.33])
316+
domainV,y0V,pV = ConstantVDomain(phase=ig,initialconds=initialcondsV) #Define the domain (encodes how system thermodynamic properties calculated)
317+
318+
reactV = Reactor(domainV,y0V,(0.0,0.037);p=pV) #Create the reactor object
319+
solV = solve(reactV.ode,CVODE_BDF(),abstol=1e-16,reltol=1e-6); #solve the ode associated with the reactor
320+
simV = Simulation(solV,domainV)
321+
322+
initialcondsV1 = Dict(["T"=>1000.0,"P"=>10.0e5,"H2"=>0.67,"O2"=>0.33])
323+
domainV1,y0V1,pV1 = ConstantVDomain(phase=ig,initialconds=initialcondsV1) #Define the domain (encodes how system thermodynamic properties calculated)
324+
initialcondsV2 = Dict(["T"=>1000.0,"P"=>10.0e5,"H2"=>0.67,"O2"=>0.33])
325+
domainV2,y0V2,pV2 = ConstantVDomain(phase=ig,initialconds=initialcondsV2) #Define the domain (encodes how system thermodynamic properties calculated)
326+
327+
react,y0,p = Reactor((domainV1,domainV2),(y0V1,y0V2),(0.0,0.037),[],(pV1,pV2));
328+
sol = solve(react.ode,CVODE_BDF(),abstol=1e-16,reltol=1e-6);
329+
sysim = SystemSimulation(sol,(domainV1,domainV2),[],p);
330+
331+
t = 0.03
332+
@test sol(t)[1:length(spcs)] solV(t)[1:end-2] rtol=1e-5
333+
@test sol(t)[length(spcs)+1:end-4] solV(t)[1:end-2] rtol=1e-5
334+
335+
dpsV = getadjointsensitivities(simV,"H2",CVODE_BDF();sensealg=InterpolatingAdjoint(autojacvec=ReverseDiffVJP(false)),abstol=1e-16,reltol=1e-6)
336+
dps = getadjointsensitivities(sysim,sysim.sims[1],"H2",CVODE_BDF();sensealg=InterpolatingAdjoint(autojacvec=ReverseDiffVJP(false)),abstol=1e-16,reltol=1e-6)
337+
@test dpsV dps rtol=1e-4
338+
end;
339+
269340
@testset "Multi-domain ConstantV and ConstantTP simulation" begin
270341
phaseDict = readinput("../src/testing/superminimal.rms")
271342
spcs = phaseDict["phase"]["Species"]

0 commit comments

Comments
 (0)