|
| 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))) |
0 commit comments