Skip to content

Commit 3470060

Browse files
committed
[Echem] Add FickDiffusionInternalInterface
for diffusion between two domains. Adds a non-reactive internal interface modeling Fickian diffusive mass transfer across a stagnant film of finite thickness between two finite volume domains.
1 parent 8a6ac49 commit 3470060

3 files changed

Lines changed: 229 additions & 106 deletions

File tree

src/Interface.jl

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ struct ReactiveInternalInterfaceConstantTPhi{J,N,B,B2,B3,C,C2,Q<:AbstractReactio
8989
forwardability::Array{Bool,1}
9090
end
9191
function ReactiveInternalInterfaceConstantTPhi(domain1,domain2,reactions,T,A,phi=0.0)
92-
@assert domain1.T == domain2.T
92+
@assert domain1.T == domain2.T
9393
reactions = upgradekinetics(reactions,domain1,domain2)
9494
rxnarray = getinterfacereactioninds(domain1,domain2,reactions)
9595
M,Nrp1,Nrp2 = getstoichmatrix(domain1,domain2,reactions)
@@ -301,7 +301,7 @@ export ConstantReservoirDiffusion
301301
kLAkHCondensationEvaporationWithReservoir adds evaporation and condensation to
302302
(1) a liquid phase domain with a constant composition vapor resevoir, where number of moles, P, and T need to be specified, or
303303
(2) a gas phase domain with a constant composition liquid resevoir, where number of moles, V, and T need to be specified.
304-
kLA and kH are used to model cond/evap.
304+
kLA and kH are used to model cond/evap.
305305
kLA is liquid volumetric mass transfer coefficient with unit 1/s , and kH is Henry's law constant defined as gas phase partial pressure of solute over liquid phase concentration of solute.
306306
"""
307307

@@ -389,7 +389,7 @@ end
389389
export VolumetricFlowRateInlet
390390

391391
"""
392-
VolumeMaintainingOutlet is designed for gas phase domain such that the flow rate of this outlet will adjust to maintain the volume of the
392+
VolumeMaintainingOutlet is designed for gas phase domain such that the flow rate of this outlet will adjust to maintain the volume of the
393393
domain to be constant. This is particularly useful to simulate any vapor-liquid phase system where the gas phase outlet
394394
is determined by the amount of evaporation.
395395
"""
@@ -451,6 +451,60 @@ function getinterfaceignoremasstransferspcinds(domaingas, domainliq, ignoremasst
451451
return indices
452452
end
453453

454+
"""
455+
FickDiffusionInternalInterface models Fickian diffusive mass transfer across a stagnant
456+
film of finite thickness between two finite volume domains. Unlike
457+
ConstantReservoirDiffusion, both sides carry their own state, so species
458+
accumulate/deplete over time and mass is conserved across the interface.
459+
460+
The molar flux into domain1 (and equal-and-opposite out of domain2) for species i is
461+
J_i = A * D_i * (c2_i - c1_i) / layer_thickness [mol/s]
462+
where c1, c2 are the per-species concentrations of domain1, domain2, A is the
463+
interfacial area, layer_thickness is the film spacing, and D_i is the per-species
464+
diffusivity across the film. This is basic Fick's law: the flux is driven by the
465+
concentration difference and vanishes when c1 == c2 (no partition-coefficient term,
466+
so it is intended for diffusion within a single phase / between finite volumes).
467+
"""
468+
struct FickDiffusionInternalInterface{D1,D2} <: AbstractInternalInterface
469+
domain1::D1
470+
domain2::D2
471+
A::Float64
472+
layer_thickness::Float64
473+
diffusivity::Array{Float64,1}
474+
parameterindexes::Array{Int64,1}
475+
domaininds::Array{Int64,1}
476+
p::Array{Float64,1}
477+
end
478+
479+
function FickDiffusionInternalInterface(domain1, domain2, A, layer_thickness;
480+
diffusivity=nothing)
481+
@assert getfield.(domain1.phase.species, :name) == getfield.(domain2.phase.species, :name) "FickDiffusionInternalInterface requires both domains to share the same ordered species list"
482+
nspc = length(domain1.phase.species)
483+
D = diffusivity === nothing ? convert(Array{Float64,1}, domain1.diffusivity) : convert(Array{Float64,1}, diffusivity)
484+
@assert length(D) == nspc "diffusivity must have one entry per species ($nspc); got $(length(D)). If domain1.diffusivity is empty, pass diffusivity explicitly."
485+
return FickDiffusionInternalInterface(domain1, domain2, Float64(A), Float64(layer_thickness),
486+
D, [1, nspc], [0, 0], ones(nspc)), ones(nspc)
487+
end
488+
export FickDiffusionInternalInterface
489+
490+
@inline function fickdiffusionflux(fd::FickDiffusionInternalInterface, cstot)
491+
@views @inbounds c1 = cstot[fd.domain1.indexes[1]:fd.domain1.indexes[2]]
492+
@views @inbounds c2 = cstot[fd.domain2.indexes[1]:fd.domain2.indexes[2]]
493+
@fastmath return fd.A .* fd.diffusivity .* (c2 .- c1) ./ fd.layer_thickness
494+
end
495+
496+
function evaluate(fd::FickDiffusionInternalInterface, dydt, cstot, p::W) where {W<:SciMLBase.NullParameters}
497+
flux = fickdiffusionflux(fd, cstot)
498+
@views @inbounds @fastmath dydt[fd.domain1.indexes[1]:fd.domain1.indexes[2]] .+= flux
499+
@views @inbounds @fastmath dydt[fd.domain2.indexes[1]:fd.domain2.indexes[2]] .-= flux
500+
end
501+
502+
function evaluate(fd::FickDiffusionInternalInterface, dydt, cstot, p)
503+
flux = fickdiffusionflux(fd, cstot) .* p[fd.parameterindexes[1]:fd.parameterindexes[2]]
504+
@views @inbounds @fastmath dydt[fd.domain1.indexes[1]:fd.domain1.indexes[2]] .+= flux
505+
@views @inbounds @fastmath dydt[fd.domain2.indexes[1]:fd.domain2.indexes[2]] .-= flux
506+
end
507+
454508
struct FragmentBasedReactiveFilmGrowthInterfaceConstantT{D1,D2,Q<:AbstractReaction,M1} <: AbstractReactiveInternalInterface
455509
domainfilm::D1
456510
domain2::D2
@@ -567,4 +621,4 @@ function evaluate(ri::FragmentBasedReactiveFilmGrowthInterfaceConstantT, dydt, V
567621
end
568622
end
569623

570-
export evaluate
624+
export evaluate

0 commit comments

Comments
 (0)