Esempio n. 1
0
def totalQ(Ta, Td, Pa, Pd, ya, yd, iso_Ta, iso_Td,cp, vf, ms, qa, wcvf_a, iso_df):
  """ Returns [0] Q_{thermal} computed from eq. 1 in 10.1039/c4ee02636e (kJ/mol)
              [1] Qs, sensible heat (kJ/mol)
              [2] Qd, enthalpy of desorption (kJ/mol, defined as positive)
              [3] WCv, mass CO2 per m3 of bed (kg/m3)
              [4] pur, final CO2 purity (-)

  """
  CO2_KGMOL = 0.044 # kg/mol
  # extract or compute the adsorption conditions if not already stored
  if not qa:
    logging.debug("Ta = {:.3e}, Pa = {:.3e}, ya = {:.3e}, yd = {:.3e}".format(Ta,Pa,ya,yd))
    wcvf_a['CO_2'] = wcvf(ya, Ta, Pa, vf, ms)
    wcvf_a['N_2'] = wcvf(1-ya, Ta, Pa, vf, ms)
    # gas uptake @ adsorption
    qa['CO_2'], qa['N_2'] = pyiast.iast(
                                      np.array([ya, 1-ya]) * Pa,
                                      [iso_Ta['CO_2'], iso_Ta['N_2']],
                                      verboseflag=False,
                                      warningoff=True, # no complain about extrapolation
                                     )
    logging.debug("qa['CO_2']: {:.3e}, qa['N_2']: {:.3e}".format(qa['CO_2'], qa['N_2']))
  # Compute desorption conditions
  wcvf_d = {}
  wcvf_d['CO_2'] = wcvf(yd, Td, Pd, vf, ms)
  wcvf_d['N_2'] = wcvf(1-yd, Td, Pd, vf, ms)
  qd, qds = {}, {}
  # gas uptake @ desorption
  qd['CO_2'], qd['N_2'] = pyiast.iast(
                                    np.array([yd, 1-yd]) * Pd,
                                    [iso_Td['CO_2'], iso_Td['N_2']],
                                    verboseflag=False,
                                    warningoff=True, # no complain about extrapolation
                                    adsorbed_mole_fraction_guess=[0.99999999, 0.00000001]
                                   )
  logging.debug("qd['CO_2']: {:.3e}, qd['N_2']: {:.3e}".format(qd['CO_2'], qd['N_2']))
  wc, wct = {}, {}
  for m in ['CO_2', 'N_2']:
    wc[m] = qa[m] - qd[m] # working capacity in the adsorbent (mol_component/kg_ADS)
    wct[m] = wc[m] + wcvf_a[m] - wcvf_d[m] # working capacity total = adsorbent + void
  WCv = wct['CO_2'] * ms * CO2_KGMOL # kgCO2/m3
  logging.debug("wct['CO_2'] = {:.3e} => WCv = {:.3e}".format(wct['CO_2'], WCv))
  if WCv < 0:
    logging.debug("NEGATIVE wct['CO_2']")
    return np.nan, np.nan, np.nan, WCv, np.nan
  else:
    # Compute volumetric working capacity, and final purity
    pur = wct['CO_2'] / (wct['CO_2'] + wct['N_2'])
    logging.debug("WCv = {:.3e}, pur = {:.3e}".format(WCv, pur))
    # Compute Q_{thermal} (Qt) = sensible_heat (Qs) + desorption_enthalpy (Qd)
    Qs = cp * ms * (Td - Ta) / WCv # J/kgCO2
    Qd = 0
    for m in ['CO_2', 'N_2']:
      # Get the enthalpy from the input data, at the closest conditions to desorption (assuming constant HoA with loading)
      idx_closest_Pd = (iso_df[m]['pressure(Pa)']-Pd).abs().argsort()[0]
      h = - iso_df[m].loc[idx_closest_Pd]['HoA(kJ/mol)'] # positive number, we have to provide this enthalpy
      Qd += h * 1e3 * wc[m] * ms / WCv # J/kgCO2
    Qt = Qs + Qd  # J/kgCO2
    logging.debug("Qs = {:.3e}, Qd = {:.3e}, Qt = {:.3e}".format(Qs, Qd, Qt))
    return Qt, Qs, Qd, WCv, pur
def iast_loading(partial_pressures, i):
    """
    Calculate loading of component i according to IAST
    
    partial_pressures: Array, partial pressures of each component
    i: component in the mixture
    """
    component_loadings = pyiast.iast(partial_pressures, isotherms)
    return component_loadings[i]
def iast_loading(partial_pressures, i):
    """
    Calculate loading of component i according to IAST
    
    partial_pressures: Array, partial pressures of each component
    i: component in the mixture
    """
    component_loadings = pyiast.iast(partial_pressures, isotherms)
    return component_loadings[i]
Esempio n. 4
0
    def equations(p):
        x = np.array(p)

        ads = list(
            pyiast.iast(p, PureIsotherms, verboseflag=False, warningoff=True))

        return ([
            phi * x_i / RT + (1. - phi) * rho * ads_i - Ntot_i
            for (x_i, ads_i, Ntot_i) in zip(x, ads, Ntot)
        ])
Esempio n. 5
0
                                              fill_value=df_ch3ch3["Loading(mmol/g)"].max())
pyiast.plot_isotherm(ch3ch3_isotherm)


# ## Perform IAST at same mixture conditions as binary GCMC simulations

# In[9]:

n_mixtures = df_mixture.shape[0]
iast_component_loadings = np.zeros((2, n_mixtures))  # store component loadings here

for i in range(n_mixtures):
    y_ethane = df_mixture['y_ethane'].iloc[i]
    partial_pressures = 65.0 * np.array([y_ethane, 1.0 - y_ethane])
    iast_component_loadings[:, i] = pyiast.iast(partial_pressures, 
                          [ch3ch3_isotherm, ch4_isotherm], 
                          verboseflag=False)


# ## Compare pyIAST predictions to binary GCMC

# In the following plot, the points are the dual component GCMC simulation loadings at the respective ethane mole fraction in the gas phase. The lines are the result of the IAST calculation. The IAST calculations match the binary GCMC simulations very well.

# In[10]:

fig = plt.figure()

plt.scatter(df_mixture['y_ethane'], df_mixture['EthaneLoading(mmol/g)'], 
            color=color_key["ethane"], label='Ethane', s=50)
plt.scatter(df_mixture['y_ethane'], df_mixture['MethaneLoading(mmol/g)'], 
            color=color_key["methane"], label='Methane', s=50)
    pressure_key="Pressure(bar)",
    fill_value=df_ch3ch3["Loading(mmol/g)"].max())
pyiast.plot_isotherm(ch3ch3_isotherm)

# ## Perform IAST at same mixture conditions as binary GCMC simulations

# In[9]:

n_mixtures = df_mixture.shape[0]
iast_component_loadings = np.zeros(
    (2, n_mixtures))  # store component loadings here

for i in range(n_mixtures):
    y_ethane = df_mixture['y_ethane'].iloc[i]
    partial_pressures = 65.0 * np.array([y_ethane, 1.0 - y_ethane])
    iast_component_loadings[:, i] = pyiast.iast(
        partial_pressures, [ch3ch3_isotherm, ch4_isotherm], verboseflag=False)

# ## Compare pyIAST predictions to binary GCMC

# In the following plot, the points are the dual component GCMC simulation loadings at the respective ethane mole fraction in the gas phase. The lines are the result of the IAST calculation. The IAST calculations match the binary GCMC simulations very well.

# In[10]:

fig = plt.figure()

plt.scatter(
    df_mixture['y_ethane'],
    df_mixture['EthaneLoading(mmol/g)'],
    color=color_key["ethane"],
    label='Ethane',
    s=50)
Esempio n. 7
0
def run(test=False):
    # Set to False if you **do not** want to recalculate pure gas adsorption isotherms
    is_simulate_loadings = False
    loadings_file = 'loadings.dat'

    # Option to draw the isotherms: it is either **'ToFile'** or **'ToScreen'** (case insensitive).
    # Any other value will be interpreted as no graphics
    graphing = 'none'

    # Gas names as they will be referred through simulations
    gas_names = ['ch4', 'co2']

    # Corresponding mole fractions of gases that will allow us to calculate their partial pressures through the Dalton's law
    mol_frac = [0.5, 0.5]

    # Calibrated previously functional forms of chemical potentials of gases for GCMC simulations as a functions of pressure
    chem_pots = [
        lambda x: 2.4153 * numpy.log(x) - 36.722,
        lambda x: 2.40 * numpy.log(x) - 40.701
    ]

    # Root directory for some data (For PySIMM examples it is )
    data_dir = osp.join('..', '09_cassandra_simulations', 'gcmc')

    # Setup of adsorbate model
    gases = []
    for gn in gas_names:
        gases.append(system.read_lammps(osp.join(data_dir, gn + '.lmps')))
        gases[-1].forcefield = 'trappe/amber'

    # Setup of adsorbent model
    frame = system.read_lammps('pim.lmps')
    frame.forcefield = 'trappe/amber'

    # Constant for loadings calculations
    molec2mmols_g = 1e+3 / frame.mass

    # Setup of the GCMC simulations
    css = cassandra.Cassandra(frame)
    sim_settings = css.read_input('run_props.inp')

    # This function in given context will calculate the loading from short GCMC simulations
    def calculate_isotherm_point(gas_name, press):
        run_fldr = osp.join(gas_name, str(press))
        idx = gas_names.index(gas_name)
        # sim_settings.update({'Run_Name':  'gcmc'})
        css.add_gcmc(species=gases[idx],
                     is_new=True,
                     chem_pot=chem_pots[idx](press),
                     out_folder=run_fldr,
                     props_file='gcmc.inp',
                     **sim_settings)
        css.run()
        full_prp = css.run_queue[0].get_prp()
        return molec2mmols_g * numpy.average(
            full_prp[3][int(len(2 * full_prp[3]) / 3):])

    # This function in given context will load the pre-calculated loading value from previously done GCMC simulations
    def load_isotherm_point(gas_name, press):
        with open(loadings_file, 'r') as pntr:
            stream = pntr.read()
            tmp = stream.split('\n' + gas_name)[1]
            idx = re.search('[a-zA-Z]|\Z', tmp)
            value = re.findall('\n{:}\s+\d+\.\d+'.format(press),
                               tmp[:idx.start()])[0]
            return float(re.split('\s+', value)[-1])

    # Calculation of adsorption isotherms for pure CH4 and CO2 gases for further usage in IAST simulations.
    # This is the **MOST TIME CONSUMING STEP** in this example, if you want to skip it switch the key is_simulated to False
    # The IAST will be done using PyIAST package, thus isotherms are wrapped into the corresponding object
    gas_press = [0.1, 1, 5, 10, 25, 50]
    lk = 'Loading(mmol/g)'
    pk = 'Pressure(bar)'
    isotherms = []
    loadings = dict.fromkeys(gas_names)
    for gn in gas_names:
        loadings[gn] = []
        for p in gas_press:
            if is_simulate_loadings:
                data = calculate_isotherm_point(gn, p)
            else:
                data = load_isotherm_point(gn, p)
            loadings[gn].append(data)
        isotherms.append(
            pyiast.ModelIsotherm(pandas.DataFrame(zip(gas_press, loadings[gn]),
                                                  columns=[pk, lk]),
                                 loading_key=lk,
                                 pressure_key=pk,
                                 model='BET',
                                 optimization_method='Powell'))

    # The PyIAST run for calculating of mixed adsorption isotherm
    # Initial guesses of adsorbed mole fractions do span broad range of values, because PyIAST might not find
    #  solution at certain values of mole fractions and through an exception
    guesses = [[a, 1 - a] for a in numpy.linspace(0.01, 0.99, 50)]
    for in_g in guesses:
        mix_loadings = []
        try:
            for p in gas_press:
                mix_loadings.append(
                    list(
                        pyiast.iast(p * numpy.array(mol_frac),
                                    isotherms,
                                    verboseflag=False,
                                    adsorbed_mole_fraction_guess=in_g)))
            mix_loadings = numpy.array(mix_loadings)
            break
        except:
            print('Initial guess {:} had failed to converge'.format(in_g))
            continue

    mix_loadings = numpy.sum(mix_loadings, axis=1)
    mix_isotherm = pyiast.ModelIsotherm(pandas.DataFrame(zip(
        gas_press, mix_loadings),
                                                         columns=[pk, lk]),
                                        loading_key=lk,
                                        pressure_key=pk,
                                        model='BET',
                                        optimization_method='L-BFGS-B')

    # Output: Graphing of constructed isotherms
    def _plot_isotherms(ax, loc_gp, loc_isoth, loc_mix_load, loc_mix_isoth):
        rng = numpy.linspace(min(loc_gp), max(loc_gp), 100)
        ax.plot(loc_gp,
                loadings[gas_names[0]],
                'og',
                lw=2.5,
                label='{:} loadings'.format(gas_names[0].upper()))
        ax.plot(rng, [loc_isoth[0].loading(t) for t in rng],
                '--g',
                lw=2,
                label='BET fit of {:} loadings'.format(gas_names[0].upper()))
        ax.plot(loc_gp,
                loadings[gas_names[1]],
                'or',
                lw=2.5,
                label='{:} loadings'.format(gas_names[1].upper()))
        ax.plot(rng, [loc_isoth[1].loading(t) for t in rng],
                '--r',
                lw=2,
                label='BET fit of {:} loadings'.format(gas_names[1].upper()))
        ax.plot(loc_gp,
                loc_mix_load,
                'ob',
                lw=2.5,
                label='1-to-1 mixture loadings')
        ax.plot(rng, [loc_mix_isoth.loading(t) for t in rng],
                '--b',
                lw=2,
                label='BET fit of 1-to-1 mixture loadings')
        ax.set_xlabel('Gas pressure [bar]', fontsize=20)
        ax.set_ylabel('Loading [mmol / g]', fontsize=20)
        ax.tick_params(axis='both', labelsize=16)
        ax.grid(True)
        ax.legend(fontsize=16)
        mplp.tight_layout()

    if graphing.lower() == 'tofile':
        fig, axs = mplp.subplots(1, 1, figsize=(10, 5))
        _plot_isotherms(axs, gas_press, isotherms, mix_loadings, mix_isotherm)
        mplp.savefig('pim1_mix_adsorption.png', dpi=192)
    elif graphing.lower() == 'toscreen':
        mplp.figure()
        axs = mplp.gca()
        _plot_isotherms(axs, gas_press, isotherms, mix_loadings, mix_isotherm)
        mplp.show()

    with open('iast_loadings.dat', 'w') as pntr:
        pntr.write('{:}\t\t{:}\n'.format(pk, lk))
        pntr.write('{:}-{:} 1-to-1\n'.format(gas_names[0], gas_names[1]))
        for p, ml in zip(gas_press, mix_loadings):
            pntr.write('{:}\t\t{:}\n'.format(p, ml))
Esempio n. 8
0
                             pressure_key=pk,
                             model='BET',
                             optimization_method='Powell'))

# The PyIAST run for calculating of mixed adsorption isotherm
# Initial guesses of adsorbed mole fractions do span broad range of values, because PyIAST might not find
#  solution at certain values of mole fractions and through an exception
guesses = [[a, 1 - a] for a in numpy.linspace(0.01, 0.99, 50)]
for in_g in guesses:
    mix_loadings = []
    try:
        for p in gas_press:
            mix_loadings.append(
                list(
                    pyiast.iast(p * numpy.array(mol_frac),
                                isotherms,
                                verboseflag=False,
                                adsorbed_mole_fraction_guess=in_g)))
        mix_loadings = numpy.array(mix_loadings)
        break
    except:
        print('Initial guess {:} had failed to converge'.format(in_g))
        continue

mix_loadings = numpy.sum(mix_loadings, axis=1)
mix_isotherm = pyiast.ModelIsotherm(pandas.DataFrame(zip(
    gas_press, mix_loadings),
                                                     columns=[pk, lk]),
                                    loading_key=lk,
                                    pressure_key=pk,
                                    model='BET',
                                    optimization_method='L-BFGS-B')