|
| 1 | +using Parameters |
| 2 | +using Statistics |
| 3 | + |
| 4 | +""" |
| 5 | +Object for storing useful debugging information about potentially problematic species |
| 6 | +tol is the tolerance analyzed at, G is the Gibbs free energy, dy is |
| 7 | +the flux to that species and ratio is the ratio between dy and the flux scale |
| 8 | +index is the index of the species |
| 9 | +""" |
| 10 | +@with_kw struct DebugSpecies |
| 11 | + name::String |
| 12 | + G::Float64 = 0.0 |
| 13 | + ratio::Float64 = 0.0 |
| 14 | + dy::Float64 = 0.0 |
| 15 | + tol::Float64 = 0.0 |
| 16 | + index::Int64 = 0 |
| 17 | +end |
| 18 | + |
| 19 | +""" |
| 20 | +Object for storing useful debugging information about potentially problematic reactions |
| 21 | +tol is the tolerance analyzed at, kf and krev are the forward and reverse rate coefficients |
| 22 | +rt is the rate of the reaction, ratio is the ratio between rt and the rate scale |
| 23 | +T and P are the temperature and pressure and index is the index of the reaction |
| 24 | +""" |
| 25 | +@with_kw struct DebugReaction |
| 26 | + reactants::Array{DebugSpecies,1} |
| 27 | + products::Array{DebugSpecies,1} |
| 28 | + rxnstring::String |
| 29 | + tol::Float64 = 0.0 |
| 30 | + kf::Float64 = 0.0 |
| 31 | + krev::Float64 = 0.0 |
| 32 | + rt::Float64 = 0.0 |
| 33 | + ratio::Float64 = 0.0 |
| 34 | + T::Float64 = 0.0 |
| 35 | + P::Float64 = 0.0 |
| 36 | + index::Int64 = 0 |
| 37 | +end |
| 38 | + |
| 39 | +struct DebugMech |
| 40 | + spcs::Array{DebugSpecies,1} |
| 41 | + rxns::Array{DebugReaction,1} |
| 42 | +end |
| 43 | + |
| 44 | +function getdebugreaction(rxn::ElementaryReaction;tol=0.0,kf=0.0,krev=0.0,rt=0.0,ratio=0.0,T=0.0,P=0.0,index=0) |
| 45 | + return DebugReaction([getdebugspecies(spc,T) for spc in rxn.reactants], |
| 46 | + [getdebugspecies(spc,T) for spc in rxn.products], |
| 47 | + getrxnstr(rxn),tol,kf,krev,rt,ratio,T,P,index) |
| 48 | +end |
| 49 | + |
| 50 | +export getdebugreaction |
| 51 | + |
| 52 | +function getdebugspecies(spc::Species,T::Float64;dy=0.0,ratio=0.0,tol=0.0,index=0) |
| 53 | + G = getGibbs(spc.thermo,T) |
| 54 | + return DebugSpecies(spc.name,G,ratio,dy,tol,index) |
| 55 | +end |
| 56 | + |
| 57 | +export getdebugspecies |
| 58 | + |
| 59 | +""" |
| 60 | +This function prints a report analyzing rates and dydt to determine reactions |
| 61 | +and thermochemistry worth looking at |
| 62 | +sim should be a simulation object created from a solution object corresponding to a crash |
| 63 | +tol is the ratio relative to the median absolute rate or dn_i/dt value above which reactions |
| 64 | +and species will be reported |
| 65 | +""" |
| 66 | +function analyzecrash(sim::Simulation;tol=1e6) |
| 67 | + rxns = Array{DebugReaction,1}() |
| 68 | + spcs = Array{DebugSpecies,1}() |
| 69 | + t = sim.sol.t[end] |
| 70 | + T = getT(sim,t) |
| 71 | + rts = rates(sim,t) |
| 72 | + rmedian = median(abs.([rt for rt in rts if !isnan(rt) && rt != 0.0])) |
| 73 | + dydt = zeros(length(sim.sol.u[end])) |
| 74 | + dydtreactor!(dydt,sim.sol.u[end],0.0,sim.domain,sim.interfaces;p=sim.p) |
| 75 | + dymedian = median(abs.([dy for dy in dydt if !isnan(dy) && dy != 0.0])) |
| 76 | + y = sim.sol(t) |
| 77 | + p = sim.domain.p |
| 78 | + ns,cs,T,P,V,C,N,mu,kfs,krevs,Hs,Us,Gs,diffs,Cvave,cpdivR = calcthermo(sim.domain,y,t,p) |
| 79 | + for (i,rt) in enumerate(rts) |
| 80 | + if isnan(rt) |
| 81 | + push!(rxns,getdebugreaction(sim.reactions[i];tol=tol,kf=kfs[i],krev=krevs[i],rt=rt,T=T,ratio=NaN,index=i)) |
| 82 | + elseif abs(rt/rmedian) > tol |
| 83 | + ratio = abs(rt/rmedian) |
| 84 | + push!(rxns,getdebugreaction(sim.reactions[i];tol=tol,kf=kfs[i],krev=krev[i],rt=rt,T=T,ratio=ratio,index=i)) |
| 85 | + end |
| 86 | + end |
| 87 | + for (i,dy) in enumerate(dydt) |
| 88 | + if i > length(sim.species) |
| 89 | + continue |
| 90 | + end |
| 91 | + if isnan(dy) |
| 92 | + push!(spcs,getdebugspecies(sim.species[i],T;dy=dy,ratio=NaN,tol=tol,index=i)) |
| 93 | + elseif abs(dy/dymedian) > tol |
| 94 | + ratio = abs(dy/dymedian) |
| 95 | + push!(spcs,getdebugspecies(sim.species[i],T;dy=dy,ratio=ratio,tol=tol,index=i)) |
| 96 | + end |
| 97 | + end |
| 98 | + return DebugMech(spcs,rxns) |
| 99 | +end |
| 100 | + |
| 101 | +""" |
| 102 | +This function prints a report analyzing rates and dydt to determine reactions |
| 103 | +and thermochemistry worth looking at |
| 104 | +sim should be a simulation object created from a solution object corresponding to a crash |
| 105 | +tol is the ratio relative to the median absolute rate or dn_i/dt value above which reactions |
| 106 | +and species will be reported |
| 107 | +""" |
| 108 | +function analyzecrash(sim::SystemSimulation;tol=1e6) |
| 109 | + rxns = Array{DebugReaction,1}() |
| 110 | + spcs = Array{DebugSpecies,1}() |
| 111 | + t = sim.sol.t[end] |
| 112 | + rts = rates(sim,t) |
| 113 | + rmedian = median(abs.([rt for rt in rts if !isnan(rt) && rt != 0.0])) |
| 114 | + dydt = zeros(length(sim.sol.u[end])) |
| 115 | + dydtreactor!(dydt,sim.sol.u[end],0.0,tuple([s.domain for s in sim.sims]),sim.interfaces;p=sim.p) |
| 116 | + dymedian = median(abs.([dy for dy in dydt if !isnan(dy) && dy != 0.0])) |
| 117 | + y = sim.sol(t) |
| 118 | + p = sim.domain.p |
| 119 | + ns,cs,T,P,V,C,N,mu,kfs,krevs,Hs,Us,Gs,diffs,Cvave,cpdivR = calcthermo(sim.domain,y,t,p) |
| 120 | + for (i,rt) in enumerate(rts) |
| 121 | + if isnan(rt) |
| 122 | + push!(rxns,getdebugreaction(sim.reactions[i];tol=tol,kf=kfs[i],krev=krevs[i],rt=rt,ratio=NaN,index=i)) |
| 123 | + elseif abs(rt/rmedian) > tol |
| 124 | + ratio = abs(rt/rmedian) |
| 125 | + push!(rxns,getdebugreaction(sim.reactions[i];tol=tol,kf=kfs[i],krev=krev[i],rt=rt,ratio=ratio,index=i)) |
| 126 | + end |
| 127 | + end |
| 128 | + for (i,dy) in enumerate(dydt) |
| 129 | + if i > length(sim.species) |
| 130 | + continue |
| 131 | + end |
| 132 | + if isnan(dy) |
| 133 | + push!(spcs,getdebugspecies(sim.species[i];dy=dy,ratio=NaN,tol=tol,index=i)) |
| 134 | + elseif abs(dy/dymedian) > tol |
| 135 | + ratio = abs(dy/dymedian) |
| 136 | + push!(spcs,getdebugspecies(sim.species[i];dy=dy,ratio=ratio,tol=tol,index=i)) |
| 137 | + end |
| 138 | + end |
| 139 | + return DebugMech(spcs,rxns) |
| 140 | +end |
| 141 | +export analyzecrash |
| 142 | + |
| 143 | +function getdebugmechstring(mech::DebugMech) |
| 144 | + s = "Crash Analysis Report\n" |
| 145 | + for rxn in mech.rxns |
| 146 | + s *= getdebugrxnstring(rxn) |
| 147 | + end |
| 148 | + for spc in mech.spcs |
| 149 | + s *= getdebugspeciesstring(spc) |
| 150 | + end |
| 151 | + return s |
| 152 | +end |
| 153 | + |
| 154 | +export getdebugmechstring |
| 155 | + |
| 156 | +function getdebugrxnstring(rxn) |
| 157 | + s = "" |
| 158 | + rt = rxn.rt |
| 159 | + tol = rxn.tol |
| 160 | + ratio = rxn.ratio |
| 161 | + kf = rxn.kf |
| 162 | + krev = rxn.krev |
| 163 | + index = rxn.index |
| 164 | + if isnan(rt) |
| 165 | + s *= "NaN for Reaction:\n" |
| 166 | + elseif ratio > tol |
| 167 | + s *= "rt/rmedian > $tol, rt=$rt, rt/rmedian=$ratio \nReaction:\n" |
| 168 | + end |
| 169 | + s *= "Index: $index\n" |
| 170 | + s *= rxn.rxnstring * "\n" |
| 171 | + if length(rxn.reactants) == 1 |
| 172 | + kunitsf = "s^-1" |
| 173 | + elseif length(rxn.reactants) == 2 |
| 174 | + kunitsf = "m^3/(mol*s)" |
| 175 | + elseif length(rxn.reactants) == 3 |
| 176 | + kunitsf = "m^6/(mol^2*s)" |
| 177 | + else |
| 178 | + error("cannot accomodate reactants>3") |
| 179 | + end |
| 180 | + if length(rxn.products) == 1 |
| 181 | + kunitsr = "s^-1" |
| 182 | + elseif length(rxn.products) == 2 |
| 183 | + kunitsr = "m^3/(mol*s)" |
| 184 | + elseif length(rxn.products) == 3 |
| 185 | + kunitsr = "m^6/(mol^2*s)" |
| 186 | + else |
| 187 | + error("cannot accomodate products>3") |
| 188 | + end |
| 189 | + s *= "kf: $kf $kunitsf\n" |
| 190 | + s *= "krev: $krev $kunitsr\n" |
| 191 | + s *= "Reactants: \n" |
| 192 | + for reactant in rxn.reactants |
| 193 | + s *= getdebugspeciesstring(reactant) |
| 194 | + end |
| 195 | + s *= "Products: \n" |
| 196 | + for product in rxn.products |
| 197 | + s *= getdebugspeciesstring(product) |
| 198 | + end |
| 199 | + return s |
| 200 | +end |
| 201 | + |
| 202 | +export getdebugreactionstring |
| 203 | + |
| 204 | +function getdebugspeciesstring(spc) |
| 205 | + s = "" |
| 206 | + dy = spc.dy |
| 207 | + ratio = spc.ratio |
| 208 | + tol = spc.tol |
| 209 | + index = spc.index |
| 210 | + if isnan(dy) |
| 211 | + s *= "NaN for Species net flux:\n" |
| 212 | + elseif ratio> tol |
| 213 | + s *= "dydt/dydtmedian > $tol, dydt=$dy, dydt/dydtmedian=$ratio \nSpecies:\n" |
| 214 | + end |
| 215 | + if index != 0 |
| 216 | + s *= "Index: $index\n" |
| 217 | + end |
| 218 | + G = spc.G/4184.0 |
| 219 | + name = spc.name |
| 220 | + s *= "\t$name G(T): $G kcal/mol\n" |
| 221 | + return s |
| 222 | +end |
| 223 | + |
| 224 | +export getdebugspeciesstring |
| 225 | + |
| 226 | +function printcrashanalysis(mech::DebugMech) |
| 227 | + println(getdebugmechstring(mech)) |
| 228 | +end |
| 229 | + |
| 230 | +function printcrashanalysis(sim::Simulation;tol=1e6) |
| 231 | + printcrashanalysis(analyzecrash(sim;tol=tol)) |
| 232 | +end |
| 233 | + |
| 234 | +export printcrashanalysis |
| 235 | + |
| 236 | +""" |
| 237 | +This calculates the collision limit of H + H -> H2 as an upper bound |
| 238 | +""" |
| 239 | +function calccollisionlimit(T) |
| 240 | + mu = 0.00050397 |
| 241 | + sigma = 2.05e-10 |
| 242 | + epsilon = 1205.6 |
| 243 | + Tr = T*kB*Na/epsilon |
| 244 | + collintegral = 1.16145*Tr^(-0.14874)+0.52487*exp(-0.7732 * Tr)+2.16178*exp(-2.437887*Tr) |
| 245 | + kcoll = sqrt(8.0*pi*kB*T*Na/mu)*sigma^2*collintegral*Na |
| 246 | + return kcoll |
| 247 | +end |
| 248 | + |
| 249 | +export calccollisionlimit |
| 250 | + |
| 251 | +""" |
| 252 | +Compares bimolecular and trimolecular reactions with the collision limit for H+H->H2 |
| 253 | +to check whether they are physical, a report with only a title and kcoll means no violators |
| 254 | +""" |
| 255 | +function analyzecolllimit(phase,Tmin,Tmax,Pmin,Pmax) |
| 256 | + println("Collision Limit Report (Comparisons are done with collision limit for H+H->H2)") |
| 257 | + println("kcoll=$kcoll") |
| 258 | + for T in [Tmin,Tmax] |
| 259 | + for P in [Pmin,Pmax] |
| 260 | + kcoll = calccollisionlimit(T) |
| 261 | + cpdivR,hdivRT,sdivR = calcHSCpdless(phase.vecthermo,T) |
| 262 | + Gs = (hdivRT.-sdivR)*(R*T) |
| 263 | + if phase.diffusionlimited |
| 264 | + diffs = getfield.(phase.species,:diffusion)(T=T,mu=0.0,P=P) |
| 265 | + else |
| 266 | + diffs = Array{Float64,1}() |
| 267 | + end |
| 268 | + kfs,krevs = getkfkrevs(phase,T,P,P/(R*T),1.0,Gs,diffs,V=1.0/C) |
| 269 | + for (i,rxn) in enumerate(phase.reactions) |
| 270 | + boo = false |
| 271 | + if len(rxn.reactants) > 2 |
| 272 | + if kfs[i] > kcoll |
| 273 | + if !boo |
| 274 | + println(getrxnstr(rxn)) |
| 275 | + boo = true |
| 276 | + end |
| 277 | + kf = kfs[i] |
| 278 | + ratio = kfs[i]/kcoll |
| 279 | + println("Violated in forward direction kf=$kf ratio=$ratio T=$T P=$P") |
| 280 | + end |
| 281 | + end |
| 282 | + if len(rxn.products) > 2 |
| 283 | + if krevs[i] > kcoll |
| 284 | + if !boo |
| 285 | + println(getrxnstr(rxn)) |
| 286 | + boo = true |
| 287 | + end |
| 288 | + krev = krevs[i] |
| 289 | + ratio = krevs[i]/kcoll |
| 290 | + println("Violated in forward direction krev=$krev ratio=$ratio T=$T P=$P") |
| 291 | + end |
| 292 | + end |
| 293 | + end |
| 294 | + end |
| 295 | + end |
| 296 | +end |
| 297 | + |
| 298 | +export analyzecolllimit |
0 commit comments