def test_error_bips_setup(): """ The goal of this test is to check a BIP input with incompatible dimensions. The BIPs compose a symmetric matrix of size (n_species, n_species). This test checks for the case where the k BIPs matrix is not symmetric. """ db = reaktoro.Database(str(get_test_data_dir() / 'hydrocarbons.xml')) editor_bips = reaktoro.ChemicalEditor(db) gaseous_species = ["C1(g)", "C4(g)", "C10(g)"] def calculate_bips(T): one = 1.0 # Wrong BIP matrix k = [ [one, one, one], [one, one, one], [-one, one, one], ] bips = reaktoro.BinaryInteractionParams(k) return bips eos_params_bips = reaktoro.CubicEOSParams( model=reaktoro.CubicEOSModel.PengRobinson, phase_identification_method=reaktoro.PhaseIdentificationMethod. GibbsEnergyAndEquationOfStateMethod, binary_interaction_values=calculate_bips) with pytest.raises(RuntimeError, match='k is not symmetric'): editor_bips.addGaseousPhase(gaseous_species).setChemicalModelCubicEOS( eos_params_bips)
def _add_hydrocarbons_to_database(db): hydrocarbons_db = reaktoro.Database( str(get_test_data_dir() / 'hydrocarbons.xml')) element_names_in_db = set(e.name() for e in db.elements()) for element in hydrocarbons_db.elements(): if element.name() not in element_names_in_db: db.addElement(element) for species in hydrocarbons_db.gaseousSpecies(): db.addGaseousSpecies(species) for species in hydrocarbons_db.liquidSpecies(): db.addLiquidSpecies(species)
def _add_lumped_oil_to_database(db): lumped_oil_db = reaktoro.Database( str(get_test_data_dir() / 'lumped_oil.xml')) element_names_in_db = set(e.name() for e in db.elements()) for element in lumped_oil_db.elements(): if element.name() not in element_names_in_db: db.addElement(element) for species in lumped_oil_db.gaseousSpecies(): db.addGaseousSpecies(species) for species in lumped_oil_db.liquidSpecies(): db.addLiquidSpecies(species)
def test_chemicaltransport_step(num_regression): nx, ny, nz = 5, 5, 5 # number of cells along x, y, and z nsteps = 10 # number of time steps velocity = fire.Constant([1.0e-2, 0.0, 0.0]) # the velocity (in units of m/s) diffusion = fire.Constant( 1.0e-4) # the diffusion coefficient (in units of m2/s) dt = 1.0 # the time step (in units of s) T = 60.0 + 273.15 # the temperature (in units of K) P = 100 * 1e5 # the pressure (in units of Pa) mesh = fire.UnitCubeMesh(nx, ny, nz) V = fire.FunctionSpace(mesh, "CG", 1) # Initialise the database database = rkt.Database("supcrt98.xml") # Initialise the chemical editor editor = rkt.ChemicalEditor(database) editor.addAqueousPhase( "H2O(l) H+ OH- Na+ Cl- Ca++ Mg++ HCO3- CO2(aq) CO3--") editor.addMineralPhase("Quartz") editor.addMineralPhase("Calcite") editor.addMineralPhase("Dolomite") # Initialise the chemical system system = rkt.ChemicalSystem(editor) # Define the initial condition of the reactive transport modeling problem problem_ic = rkt.EquilibriumProblem(system) problem_ic.setTemperature(T) problem_ic.setPressure(P) problem_ic.add("H2O", 1.0, "kg") problem_ic.add("NaCl", 0.7, "mol") problem_ic.add("CaCO3", 10, "mol") problem_ic.add("SiO2", 10, "mol") # Define the boundary condition of the reactive transport modeling problem problem_bc = rkt.EquilibriumProblem(system) problem_bc.setTemperature(T) problem_bc.setPressure(P) problem_bc.add("H2O", 1.0, "kg") problem_bc.add("NaCl", 0.90, "mol") problem_bc.add("MgCl2", 0.05, "mol") problem_bc.add("CaCl2", 0.01, "mol") problem_bc.add("CO2", 0.75, "mol") # Calculate the equilibrium states for the initial and boundary conditions state_ic = rkt.equilibrate(problem_ic) state_bc = rkt.equilibrate(problem_bc) # Scale the volumes of the phases in the initial condition such that their sum is 1 m3 state_ic.scalePhaseVolume("Aqueous", 0.1, "m3") state_ic.scalePhaseVolume("Quartz", 0.882, "m3") state_ic.scalePhaseVolume("Calcite", 0.018, "m3") # Scale the volume of the boundary equilibrium state to 1 m3 state_bc.scaleVolume(1.0) # Initialise the chemical field field = rok.ChemicalField(system, V) field.fill(state_ic) # Initialize the chemical transport solver transport = rok.ChemicalTransportSolver(field) transport.addBoundaryCondition(state_bc, 1) # 1 means left side transport.setVelocity([velocity]) transport.setDiffusion([diffusion]) species_names_for_output = [ "Ca++", "Mg++", "Calcite", "Dolomite", "CO2(aq)", "HCO3-", "Cl-", "H2O(l)", ] element_names_for_output = ["H", "O", "C", "Ca", "Mg", "Na", "Cl"] species_amount_functions = [ fire.Function(V, name=name) for name in species_names_for_output ] element_amount_functions = [ fire.Function(V, name=name) for name in element_names_for_output ] step = 0 species_data = [] element_data = [] while step <= nsteps: transport.step(field, dt) # For each selected species, output its molar amounts for f in species_amount_functions: f.assign(field.speciesAmount(f.name())) species_data.append(f.dat.data) # For each selected species, output its molar amounts for f in element_amount_functions: f.assign(field.elementAmountInPhase(f.name(), "Aqueous")) element_data.append(f.dat.data) step += 1 species_data = { "n({})(step={})".format(name, i): u for i, (name, u) in enumerate(zip(species_names_for_output, species_data)) } element_data = { "b({})(step={})".format(name, i): u for i, (name, u) in enumerate(zip(element_names_for_output, element_data)) } data = {**species_data, **element_data} num_regression.check(data)
def test_ternary_c1_c4_c10_bips_setup(): """ This is a ternary example from Whitson monograph. Retrieved from Problem 18 in Appendix B. This test compares the results defining BIPs as 'zero' matrix with the result without BIPs setup. Such results should be the same. .. reference: Whitson, C. H., & Brulé, M. R. (2000). Phase behavior (Vol. 20). Richardson, TX: Henry L. Doherty Memorial Fund of AIME, Society of Petroleum Engineers. """ temperature = 280 # degF pressure = 500 # psi composition = np.array([0.5, 0.42, 0.08]) # molar fractions db = reaktoro.Database(str(get_test_data_dir() / 'hydrocarbons.xml')) editor = reaktoro.ChemicalEditor(db) editor_bips = reaktoro.ChemicalEditor(db) gaseous_species = ["C1(g)", "C4(g)", "C10(g)"] oil_species = ["C1(liq)", "C4(liq)", "C10(liq)"] eos_params = reaktoro.CubicEOSParams( model=reaktoro.CubicEOSModel.PengRobinson, phase_identification_method=reaktoro.PhaseIdentificationMethod. GibbsEnergyAndEquationOfStateMethod, ) editor.addGaseousPhase(gaseous_species).setChemicalModelCubicEOS( eos_params) editor.addLiquidPhase(oil_species).setChemicalModelCubicEOS(eos_params) def calculate_bips(T): zero = 0.0 k = [[zero, zero, zero], [zero, zero, zero], [zero, zero, zero]] bips = reaktoro.BinaryInteractionParams(k) return bips eos_params_bips = reaktoro.CubicEOSParams( model=reaktoro.CubicEOSModel.PengRobinson, phase_identification_method=reaktoro.PhaseIdentificationMethod. GibbsEnergyAndEquationOfStateMethod, binary_interaction_values=calculate_bips) editor_bips.addGaseousPhase(gaseous_species).setChemicalModelCubicEOS( eos_params_bips) editor_bips.addLiquidPhase(oil_species).setChemicalModelCubicEOS( eos_params_bips) system = reaktoro.ChemicalSystem(editor) system_bips = reaktoro.ChemicalSystem(editor_bips) problem = reaktoro.EquilibriumProblem(system) problem_bips = reaktoro.EquilibriumProblem(system_bips) problem.setTemperature(temperature, 'degF') problem.setPressure(pressure, 'psi') problem.setElementAmount('C1', composition[0]) problem.setElementAmount('C4', composition[1]) problem.setElementAmount('C10', composition[2]) problem_bips.setTemperature(temperature, 'degF') problem_bips.setPressure(pressure, 'psi') problem_bips.setElementAmount('C1', composition[0]) problem_bips.setElementAmount('C4', composition[1]) problem_bips.setElementAmount('C10', composition[2]) # Custom initial guess below. This is necessary for hydrocarbon mixtures. state = reaktoro.ChemicalState(system) state.setSpeciesAmounts(0.001) # start will all having 0.001 moles state.setSpeciesAmount( "C1(g)", composition[0]) # overwrite amount of C1(g) (same below) state.setSpeciesAmount("C4(g)", composition[1]) state.setSpeciesAmount("C10(g)", composition[2]) state_bips = reaktoro.ChemicalState(system_bips) state_bips.setSpeciesAmounts(0.001) state_bips.setSpeciesAmount("C1(g)", composition[0]) state_bips.setSpeciesAmount("C4(g)", composition[1]) state_bips.setSpeciesAmount("C10(g)", composition[2]) options = reaktoro.EquilibriumOptions() options.hessian = reaktoro.GibbsHessian.Exact solver = reaktoro.EquilibriumSolver(system) solver.setOptions(options) result = solver.solve(state, problem) solver_bips = reaktoro.EquilibriumSolver(system_bips) solver_bips.setOptions(options) result_bips = solver_bips.solve(state_bips, problem_bips) assert result.optimum.succeeded assert result_bips.optimum.succeeded # Compare results with and without setting BIPs molar_base = state.phaseAmount('Gaseous') + state.phaseAmount('Liquid') gas_phase_molar_fraction = state.phaseAmount('Gaseous') / molar_base liquid_phase_molar_fraction = state.phaseAmount('Liquid') / molar_base phase_fractions = np.array( [gas_phase_molar_fraction, liquid_phase_molar_fraction]) molar_base_bips = state_bips.phaseAmount( 'Gaseous') + state_bips.phaseAmount('Liquid') gas_phase_molar_fraction_bips = state_bips.phaseAmount( 'Gaseous') / molar_base_bips liquid_phase_molar_fraction_bips = state_bips.phaseAmount( 'Liquid') / molar_base_bips phase_fractions_bips = np.array( [gas_phase_molar_fraction_bips, liquid_phase_molar_fraction_bips]) assert phase_fractions == pytest.approx(phase_fractions_bips)
def test_equilibrium_CH4_CO2_pedersen(): """ This case is gathered from Pedersen book [1]. It is a C1-CO2 mixture using SRK as Cubic EOS. In P = 20 bar and T = -42 degC, it results in a LV equilibrium. This test compare the results from the book with Reaktoro's outcomes. The main goal is to check if BIPs setup is working properly. The case is given in Table 6.3 in the Chapter 6 of [1]. The BIP value is given in the Chapter 4, Table 4.2. .. reference: [1] Pedersen, K. S., Christensen, P. L., & Shaikh, J. A. (2014). Phase behavior of petroleum reservoir fluids. CRC press. """ db = reaktoro.Database("supcrt98.xml") editor = reaktoro.ChemicalEditor(db) def calculate_bips(T): k_00 = k_11 = 0.0 k_01 = k_10 = 0.12 k = [ [k_00, k_01], [k_10, k_11], ] bips = reaktoro.BinaryInteractionParams(k) return bips eos_params = reaktoro.CubicEOSParams( model=reaktoro.CubicEOSModel.SoaveRedlichKwong, phase_identification_method=reaktoro.PhaseIdentificationMethod. GibbsEnergyAndEquationOfStateMethod, binary_interaction_values=calculate_bips) editor.addGaseousPhase(["CH4(g)", "CO2(g)"]).setChemicalModelCubicEOS(eos_params) editor.addLiquidPhase(["CH4(liq)", "CO2(liq)"]).setChemicalModelCubicEOS(eos_params) system = reaktoro.ChemicalSystem(editor) problem = reaktoro.EquilibriumProblem(system) temperature = -42.0 # degC pressure = 20.0 # bar problem.setTemperature(temperature, "degC") problem.setPressure(pressure, "bar") problem.add("CH4(g)", 0.4, "mol") problem.add("CO2(g)", 0.6, "mol") state = reaktoro.ChemicalState(system) state.setSpeciesAmounts(0.001) # start will all having 0.001 moles state.setSpeciesAmount("CH4(g)", 0.4) # overwrite amount of C1(g) (same below) state.setSpeciesAmount("CO2(g)", 0.6) solver = reaktoro.EquilibriumSolver(problem.system()) options = reaktoro.EquilibriumOptions() options.hessian = reaktoro.GibbsHessian.Exact solver.setOptions(options) result = solver.solve(state, problem) assert result.optimum.succeeded # Compare phases molar fractions molar_base = state.phaseAmount('Gaseous') + state.phaseAmount('Liquid') gas_phase_molar_fraction = state.phaseAmount('Gaseous') / molar_base liquid_phase_molar_fraction = state.phaseAmount('Liquid') / molar_base phase_fractions = np.array( [liquid_phase_molar_fraction, gas_phase_molar_fraction]) F_expected = np.array([0.197, 0.803]) assert phase_fractions == pytest.approx(F_expected, rel=2e-2) ch4_gas_fraction = state.speciesAmount('CH4(g)') / phase_fractions[1] co2_gas_fraction = state.speciesAmount('CO2(g)') / phase_fractions[1] equilibrium_gas_composition = np.array( [ch4_gas_fraction, co2_gas_fraction]) expected_gas_composition = np.array([0.488, 0.512]) assert equilibrium_gas_composition == pytest.approx( expected_gas_composition, rel=1e-2 # 1% rel error ) ch4_liq_fraction = state.speciesAmount('CH4(liq)') / phase_fractions[0] co2_liq_fraction = state.speciesAmount('CO2(liq)') / phase_fractions[0] equilibrium_liq_composition = np.array( [ch4_liq_fraction, co2_liq_fraction]) expected_liq_composition = np.array([0.042, 0.958]) assert equilibrium_liq_composition == pytest.approx( expected_liq_composition, rel=1e-2 # 1% rel error )
def test_ternary_c1_c4_c10_mixture(P, T, F_expected, x_expected, y_expected): """ This is a ternary example from Whitson monograph. Retrieved from Problem 18 in Appendix B. .. reference: Whitson, C. H., & Brulé, M. R. (2000). Phase behavior (Vol. 20). Richardson, TX: Henry L. Doherty Memorial Fund of AIME, Society of Petroleum Engineers. """ temperature = T # degF pressure = P # psi composition = np.array([0.5, 0.42, 0.08]) # molar fractions db = reaktoro.Database('supcrt07.xml') _add_hydrocarbons_to_database(db) editor = reaktoro.ChemicalEditor(db) gaseous_species = ["C1(g)", "C4(g)", "C10(g)"] oil_species = ["C1(liq)", "C4(liq)", "C10(liq)"] eos_params = reaktoro.CubicEOSParams( model=reaktoro.CubicEOSModel.PengRobinson, phase_identification_method=reaktoro.PhaseIdentificationMethod. GibbsEnergyAndEquationOfStateMethod, ) editor.addGaseousPhase(gaseous_species).setChemicalModelCubicEOS( eos_params) editor.addLiquidPhase(oil_species).setChemicalModelCubicEOS(eos_params) system = reaktoro.ChemicalSystem(editor) problem = reaktoro.EquilibriumProblem(system) problem.setTemperature(temperature, 'degF') problem.setPressure(pressure, 'psi') problem.setElementAmount('C1', composition[0]) problem.setElementAmount('C4', composition[1]) problem.setElementAmount('C10', composition[2]) # Custom initial guess below. This is necessary for hydrocarbon mixtures. state = reaktoro.ChemicalState(system) state.setSpeciesAmounts(0.001) # start will all having 0.001 moles state.setSpeciesAmount( "C1(g)", composition[0]) # overwrite amount of C1(g) (same below) state.setSpeciesAmount("C4(g)", composition[1]) state.setSpeciesAmount("C10(g)", composition[2]) options = reaktoro.EquilibriumOptions() options.hessian = reaktoro.GibbsHessian.Exact solver = reaktoro.EquilibriumSolver(system) solver.setOptions(options) result = solver.solve(state, problem) assert result.optimum.succeeded molar_base = state.phaseAmount('Gaseous') + state.phaseAmount('Liquid') gas_phase_molar_fraction = state.phaseAmount('Gaseous') / molar_base liquid_molar_fraction = state.phaseAmount('Liquid') / molar_base phase_fractions = np.array( [gas_phase_molar_fraction, liquid_molar_fraction]) assert phase_fractions == pytest.approx(F_expected, rel=5e-3) c1_gas_fraction = state.speciesAmount('C1(g)') / phase_fractions[0] c4_gas_fraction = state.speciesAmount('C4(g)') / phase_fractions[0] c10_gas_fraction = state.speciesAmount('C10(g)') / phase_fractions[0] equilibrium_gas_composition = np.array( [c1_gas_fraction, c4_gas_fraction, c10_gas_fraction]) assert equilibrium_gas_composition == pytest.approx(y_expected, rel=2e-2) c1_liq_fraction = state.speciesAmount('C1(liq)') / phase_fractions[1] c4_liq_fraction = state.speciesAmount('C4(liq)') / phase_fractions[1] c10_liq_fraction = state.speciesAmount('C10(liq)') / phase_fractions[1] equilibrium_liq_composition = np.array( [c1_liq_fraction, c4_liq_fraction, c10_liq_fraction]) assert equilibrium_liq_composition == pytest.approx(x_expected, rel=2e-2)
D = fire.Constant(1.0e-9) # the diffusion coefficient (in units of m2/s) v = fire.Constant([1.0 / week, 0.0]) # the fluid pore velocity (in units of m/s) dt = 30 * minute # the time step (in units of s) T = 60.0 + 273.15 # the temperature (in units of K) P = 100 * 1e5 # the pressure (in units of Pa) # Initialise the mesh # mesh = fire.UnitIntervalMesh(nx) # mesh = fire.UnitCubeMesh(nx, ny, nz) mesh = fire.UnitSquareMesh(nx, ny, quadrilateral=True) V = fire.FunctionSpace(mesh, "CG", 1) # Initialise the database database = rkt.Database("supcrt98.xml") # Initialise the chemical editor editor = rkt.ChemicalEditor(database) editor.addAqueousPhase("H2O(l) H+ OH- Na+ Cl- Ca++ Mg++ HCO3- CO2(aq) CO3--") editor.addMineralPhase("Quartz") editor.addMineralPhase("Calcite") editor.addMineralPhase("Dolomite") # Initialise the chemical system system = rkt.ChemicalSystem(editor) # Define the initial condition of the reactive transport modeling problem problem_ic = rkt.EquilibriumProblem(system) problem_ic.setTemperature(T) problem_ic.setPressure(P)
def test_convergence_with_liquid_phase(num_regression): temperature = 343.15 pressure = 1323.9767e5 db = reaktoro.Database('supcrt07.xml') _add_lumped_oil_to_database(db) editor = reaktoro.ChemicalEditor(db) aqueous_species = [ "H2O(l)", "CO2(aq)", "HCO3-", "CO3--", "H2S(aq)", "HS-", "SO4--", "Ca++", "Na+", "Cl-", "Fe++", "Ba++", "Sr++", ] gaseous_species = ["CO2(g)", "H2S(g)", "CH4(g)"] oil_species = ["H2S(liq)", "CO2(liq)", "LumpedOil(liq)"] minerals = [ "Anhydrite", "Barite", "Calcite", "Celestite", "Siderite", "Pyrrhotite" ] editor.addAqueousPhase(aqueous_species) editor.addGaseousPhase(gaseous_species) editor.addLiquidPhase(oil_species) for mineral in minerals: editor.addMineralPhase(mineral) system = reaktoro.ChemicalSystem(editor) problem = reaktoro.EquilibriumProblem(system) problem.setTemperature(temperature) problem.setPressure(pressure) state = reaktoro.ChemicalState(system) state.setTemperature(temperature) state.setPressure(pressure) # AQUEOUS state.setSpeciesAmount("H2O(l)", 1047530951.9776191) state.setSpeciesAmount("CO2(aq)", 20774.560623553392) state.setSpeciesAmount("HCO3-", 282912.10935910186) state.setSpeciesAmount("CO3--", 1483.0702563458142) state.setSpeciesAmount("H2S(aq)", 12166.740676127629) state.setSpeciesAmount("HS-", 41503.521359359736) state.setSpeciesAmount("SO4--", 0.00000000000000000) state.setSpeciesAmount("Ca++", 182556.98986975398) state.setSpeciesAmount("Na+", 8919454.7940101717) state.setSpeciesAmount("Cl-", 8955756.8340308685) state.setSpeciesAmount("Fe++", 0.00000000000000000) state.setSpeciesAmount("Ba++", 13319.520269138628) state.setSpeciesAmount("Sr++", 20875.710568363389) # MINERAL state.setSpeciesAmount("Calcite", 999131754.50533485) state.setSpeciesAmount("Anhydrite", 0.00000000000000000) state.setSpeciesAmount("Barite", 0.00000000000000000) state.setSpeciesAmount("Celestite", 0.00000000000000000) state.setSpeciesAmount("Siderite", 6732617546.7550077) state.setSpeciesAmount("Pyrrhotite", 0.00000000000000000) # GASEOUS state.setSpeciesAmount("CO2(g)", 574511.37111462699) state.setSpeciesAmount("H2S(g)", 13303.310614909715) state.setSpeciesAmount("CH4(g)", 700402204.48213911) # LIQUID state.setSpeciesAmount("CO2(liq)", 821887.77968393208) state.setSpeciesAmount("H2S(liq)", 240596.16100715694) state.setSpeciesAmount("LumpedOil(liq)", 12676647027.415014) problem.addState(state) solver = reaktoro.EquilibriumSolver(system) state = reaktoro.ChemicalState(system) converged = solver.solve(state, problem).optimum.succeeded n = state.speciesAmounts() b = state.elementAmounts() output = {} output["Element amounts [mol]"] = np.asarray(b) output["Species amounts [mol]"] = n num_regression.check(output) assert converged