Skip to content

Commit 25891d9

Browse files
author
Robert DeJaco
committed
Add porous media calculations and example
1 parent 32f490c commit 25891d9

File tree

10 files changed

+256
-1
lines changed

10 files changed

+256
-1
lines changed

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# diffusivity_estimation
2-
Estimation of Diffusion Coefficients
2+
Estimation of Diffusion Coefficients for gases and porous media
33

44
Diffusion coefficients are often estimated in scientific and engineering.
55
There can be many options and parameters involved in these estimations.
@@ -8,3 +8,8 @@ Having an open-source software repository for estimation of diffusion coefficien
88
streamline reproduction and sensitivity analysis.
99

1010
So far, this repository focuses on estimation of diffusivities in porous media.
11+
12+
13+
Binary Diffusivities approximate range (Deen p. 11)
14+
===================================================
15+
* Gases from 1E-5 to 1E-4
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
void_fraction, ,0.35
2+
d_pore, ,5e-10
3+
molecular_weight, ,None
4+
tortuosity, ,2.0
5+
R, ,8.314
6+
molecular_weight_i, ,
7+
,CH4,16.043
8+
,H2,2.016
9+
sigma_i, ,
10+
,CH4,3.758
11+
,H2,2.827
12+
epsilon_i, ,
13+
,CH4,148.6
14+
,H2,59.7
15+
component_i, ,
16+
,CH4,
17+
,H2,
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
CH4,H2,effective_macropore_i [m^2/s],1.015327e-10
2+
CH4,H2,knudsen_i [m^2/s],5.803363e-10
3+
CH4,H2,molecular_ij [m^2/s],2.220814e-11
4+
H2,CH4,effective_macropore_i [m^2/s],2.862856e-10
5+
H2,CH4,knudsen_i [m^2/s],1.637108e-09
6+
H2,CH4,molecular_ij [m^2/s],2.220814e-11
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
"""Calculate diffusion coefficients for methane (CH4)/H2 in porous media"""
2+
3+
4+
def main():
5+
from porous_media.parameters import FluidMixture
6+
void_fraction, d_pore, tortuosity = 0.35, 0.5e-9, 2.
7+
names = ['CH4', 'H2'] # must match what in parameter file!
8+
molecular_weights = [12.011+1.008*4, 1.008*2]
9+
from raw_data.read_data import get_LJ_params, read_csv
10+
LJ_data = read_csv('../../raw_data/LJparams.csv')
11+
sigma, epsilon = get_LJ_params(names, LJ_data)
12+
cls = FluidMixture(
13+
names, molecular_weights, sigma, epsilon, void_fraction, d_pore, tortuosity
14+
)
15+
cls.write_params('input.csv')
16+
cls.write_calculations('output.csv', 300., 101325.)
17+
18+
19+
if __name__ == '__main__':
20+
main()

porous_media/__init__.py

Whitespace-only changes.

porous_media/parameters.py

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
import math
2+
3+
4+
class PureFluid:
5+
"""Only one component is moving through porous media"""
6+
def __init__(self, void_fraction, d_pore, tortuosity, molecular_weight=None):
7+
"""
8+
9+
:param molecular_weight: molecular weight [kg/mol] for each component
10+
:param void_fraction: void fraction of porous media
11+
:param d_pore: nominal pore diameter [meters]
12+
:param tortuosity: tortuosity of porous media
13+
"""
14+
self.void_fraction = void_fraction
15+
self.d_pore = d_pore
16+
self.molecular_weight = molecular_weight
17+
self.tortuosity = tortuosity
18+
self.R = 8.314 # J/mol/K [=] m**3*Pa/mol/K
19+
20+
def knudsen(self, T):
21+
"""Knudsen diffusivity of component i; derived from kinetic theory of gases
22+
23+
Units:
24+
R = m**3*Pa/mol/K [=] m*m*kg/mol/s/s/K
25+
26+
sqrt(RT/MW) [=] sqrt(m*m/s/s)
27+
28+
29+
:param i: component name
30+
:return: Knudsen diffusivity m^2/s
31+
"""
32+
return self.void_fraction*self.d_pore/self.tortuosity/3.*math.sqrt(8*self.R*T/math.pi/self.molecular_weight)
33+
34+
def write_params(self, file_name):
35+
with open(file_name, 'w') as f:
36+
for key, val in self.__dict__.items():
37+
if isinstance(val, float):
38+
f.write('{},{},{}\n'.format(key, ' ', val))
39+
elif isinstance(val, dict):
40+
f.write('{},{},{}\n'.format(key, ' ', ' '))
41+
for key2, val2 in val.items():
42+
if isinstance(key2, tuple):
43+
key2 = ' '.join(key2)
44+
f.write('{},{},{}\n'.format(' ', key2, val2))
45+
elif isinstance(val, list):
46+
f.write('{},{},{}\n'.format(key, ' ', ' '))
47+
for val2 in val:
48+
f.write('{},{},{}\n'.format(' ', val2, ' '))
49+
elif isinstance(val, str) or val is None:
50+
f.write('{},{},{}\n'.format(key, ' ', val))
51+
else:
52+
raise Exception('Val type not found for {}:{}'.format(key, val))
53+
54+
55+
class FluidMixture(PureFluid):
56+
"""Multiple components flowing through porous media"""
57+
def __init__(self, components, molecular_weight, sigma, epsilon_molecular, *args):
58+
"""
59+
60+
:param components:
61+
:type components: list
62+
:param molecular_weight:
63+
:type molecular_weight: list
64+
:param sigma: collision diameter in Angstrom, get from Poling et al "The Properties of gases and liquids"
65+
:type sigma: list
66+
:param epsilon_molecular: molecular epsilon in K,get from Poling et al "The Properties of gases and liquids"
67+
:type epsilon_molecular: list
68+
:param args: args to pass to super
69+
"""
70+
PureFluid.__init__(self, *args)
71+
self.molecular_weight_i = {
72+
key: val for key, val in zip(components, molecular_weight)
73+
}
74+
self.sigma_i = {
75+
key: val for key, val in zip(components, sigma)
76+
}
77+
self.epsilon_i = {
78+
key: val for key, val in zip(components, epsilon_molecular)
79+
}
80+
self.component_i = components
81+
82+
def knudsen_i(self, i, j, temperature, pressure):
83+
"""
84+
85+
:param i: component name
86+
:return: Knudsen diffusivity for component *i*
87+
"""
88+
self.molecular_weight = self.molecular_weight_i[i]
89+
return self.knudsen(temperature)
90+
91+
def sigma_ij_rule(self, i, j):
92+
"""Lennard-Jones combining rule for sigma"""
93+
return (self.sigma_i[i] + self.sigma_i[j]) / 2.
94+
95+
def epsilon_ij_rule(self, i, j):
96+
"""Lennard-Jones combining rule for epsilon"""
97+
return math.sqrt(self.epsilon_i[i] * self.epsilon_i[j])
98+
99+
def omega_ij(self, w):
100+
"""Bird 1960 p. 746
101+
102+
:return: Temperature-dependent collision integral (dimensionless)
103+
"""
104+
105+
A1, B1, A2, B2 = 1.06036, 0.15610, 0.19300, 0.47635
106+
A3, B3, A4, B4 = 1.03587, 1.52996, 1.76474, 3.89411
107+
value = (
108+
A1 / pow(w, B1)
109+
+ A2 / math.exp(B2 * w)
110+
+ A3 / math.exp(B3 * w)
111+
+ A4 / math.exp(B4 * w)
112+
)
113+
assert 0.5 < value < 2.7, 'Value predicted of %2.3f is not reasonable' % value
114+
return value
115+
116+
def molecular_ij(self, i, j, T, P):
117+
"""Molecular diffusivites estimated by Chapman-Enskog
118+
119+
:param i: sorbate i name
120+
:param j: sorbate j name
121+
:param T: temperature in K
122+
:param P: pressure in atm
123+
:return: binary diffusion coefficient (m^2/s)
124+
"""
125+
126+
# convert molecular weights to g/mol to apply formula
127+
M_i = self.molecular_weight_i[i]*1000.
128+
M_j = self.molecular_weight_i[j]*1000.
129+
130+
return 1.858e-3 * math.sqrt(T*T*T * (1. / M_i + 1. / M_j)) / (
131+
P * self.sigma_ij_rule(i, j) * self.sigma_ij_rule(i, j) *
132+
self.omega_ij(T / self.epsilon_ij_rule(i, j))
133+
)/100./100.
134+
135+
def effective_macropore_i(self, i, j, temperature, pressure):
136+
"""
137+
138+
:param temperature: temperature in K
139+
:param pressure: pressure in Pa
140+
:param i: component i name
141+
:param j: other_component name
142+
:return: effective macropore diffusivity m^2/s
143+
"""
144+
P_atm = pressure/101325.
145+
return self.void_fraction/self.tortuosity/(
146+
1. / self.molecular_ij(i, j, temperature, P_atm) + 1. / self.knudsen_i(i, j, temperature, pressure)
147+
)
148+
149+
def write_calculations(self, output_file, temperature, pressure):
150+
with open(output_file, 'w') as f:
151+
for i in self.component_i:
152+
for j in self.component_i:
153+
if i == j:
154+
continue
155+
for attr in [
156+
'effective_macropore_i',
157+
'knudsen_i',
158+
'molecular_ij'
159+
]:
160+
func = getattr(self, attr)
161+
f.write('%s,%s,%s [m^2/s],%e\n' % (i, j, attr, func(i, j, temperature, pressure)))

raw_data/LJparams.csv

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
"Substance","Name","sigma [angstrom]","epsilon [K]"
2+
Ar,"Argon",3.542,93.3
3+
He,"Helium",255.1,10.2
4+
Kr,"Krypton",3.655,178.9
5+
CH4,"Methane",3.758,148.6
6+
H2,"Hydrogen",2.827,59.7
7+
H2O,"Water",2.641,809.1
8+
N2,"Nitrogen",3.798,71.4
9+
CO2,"Carbon Dioxide",3.971,195.2
10+
O2,"Oxygen",3.467,106.7
11+
C2H6,"Ethane",4.443,215.7
12+
C2H4,"Ethylene",4.163,224.7
13+
C3H8,"Propane",5.118,237.1
14+
C3H6,"Propylene",4.678,298.9
15+
nC4H10,"Normal Butane",4.687,531.4
16+
iC4H10,"Iso Butane",5.278,330.1

raw_data/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
LJparams
2+
========
3+
* found from Cussler "Diffusion", 3rd edition; parameters found from viscosities

raw_data/__init__.py

Whitespace-only changes.

raw_data/read_data.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
def read_csv(f_name):
2+
with open(f_name) as f:
3+
header = list(map(eval, next(f).rstrip('\n').split(',')))
4+
data = {key: [] for key in header}
5+
for line in f:
6+
for key, val in zip(header, line.rstrip('\n').split(',')):
7+
try:
8+
data[key].append(float(val))
9+
except ValueError:
10+
data[key].append(val)
11+
return data
12+
13+
14+
def get_LJ_params(components, LJ_data):
15+
"""Get LJ params from csv file
16+
17+
:param components:
18+
:return: LJ params sigma and epsilon
19+
"""
20+
sigma = []
21+
epsilon = []
22+
for key in components:
23+
assert key in LJ_data['Substance'], 'No parameters found for {} in LJparams.csv'.format(key)
24+
index = LJ_data['Substance'].index(key)
25+
sigma.append(LJ_data['sigma [angstrom]'][index])
26+
epsilon.append(LJ_data['epsilon [K]'][index])
27+
return sigma, epsilon

0 commit comments

Comments
 (0)