Exemplo n.º 1
0
    def test_mechanism_simple_api_methods(self):
        try:
            xml = abspath(join('tests', 'test_mechanisms', 'h2-burke.xml'))
            sol = Solution(xml)
            m = ChemicalMechanismSpec.from_solution(sol)
            self.assertEqual(m.n_species, sol.n_species, 'ChemicalMechanismSpec.n_species vs ct.Solution.n_species')
            self.assertEqual(m.n_reactions, sol.n_reactions,
                             'ChemicalMechanismSpec.n_reactions vs ct.Solution.n_reactions')
            for i in range(m.n_species):
                self.assertEqual(m.species_names[i], sol.species_names[i],
                                 'ChemicalMechanismSpec.species_name[i] vs ct.Solution.species_name[i]')
            for n in m.species_names:
                self.assertEqual(m.species_index(n), sol.species_index(n),
                                 'ChemicalMechanismSpec.species_index(name) vs ct.Solution.species_index(name)')

            for i, n in enumerate(m.species_names):
                self.assertEqual(m.species_index(n), i, 'species names and indices are consistent, index vs i')
                self.assertEqual(n, m.species_names[i], 'species names and indices are consistent, name vs n')
                self.assertEqual(m.molecular_weight(i), m.molecular_weight(n),
                                 'ChemicalMechanismSpec molecular_weight(name) vs molecular_weight(idx)')
        except:
            self.assertTrue(False)

        try:
            xml = abspath(join('tests', 'test_mechanisms', 'h2-burke.xml'))
            m = ChemicalMechanismSpec.from_solution(Solution(xml))
            m.molecular_weight(list())
            self.assertTrue(False)
        except:
            self.assertTrue(True)
Exemplo n.º 2
0
def test_equilibrium():
    mechanism = 'gri30.cti'

    # define states
    initial_gas = Solution(mechanism)
    initial_gas.TPX = 300, 101325, {'H2': 1}

    working_gas = Solution(mechanism)
    working_gas.TPX = 300, 101325 * 2, {'H2': 1}

    velocity_guess = 7

    # errors from hand calculations
    good_errors = [
        -18.375000000,  # enthalpy
        101322.993718076,  # pressure
    ]

    test_errors = calculate_error.equilibrium(
        working_gas=working_gas,
        initial_state_gas=initial_gas,
        initial_velocity_guess=velocity_guess)

    for test, good in zip(test_errors, good_errors):
        assert abs(test - good) / good < 1e-7
Exemplo n.º 3
0
def test_reflected_shock_frozen():
    mechanism = 'gri30.cti'

    # define states
    initial_gas = Solution(mechanism)
    initial_gas.TPX = 300, 101325, {'H2': 1}

    working_gas = Solution(mechanism)
    working_gas.TPX = 300, 101325 * 2, {'H2': 1}

    velocity_guess = 7

    # errors from hand calculations
    good_errors = [
        -73.5,  # enthalpy
        101316.97487230592  # pressure
    ]

    test_errors = calculate_error.reflected_shock_frozen(
        working_gas=working_gas,
        post_shock_gas=initial_gas,
        shock_speed=velocity_guess)

    for test, good in zip(test_errors, good_errors):
        assert abs(test - good) / good < 1e-7
Exemplo n.º 4
0
    def test_no_convergence():
        # ensure the proper warning is generated when solution doesn't converge
        mechanism = 'gri30.cti'

        initial_gas = Solution(mechanism)
        initial_gas.TPX = 300, 101325, {'H2': 1}

        working_gas = Solution(mechanism)
        working_gas.TPX = 300, 101325 * 2, {'H2': 1}

        with pytest.warns(Warning, match='No convergence within 1 iterations'):
            sd.Detonation.cj_state(working_gas, initial_gas, 1e-50, 1e-50, 1.5,
                                   1)
Exemplo n.º 5
0
def pymars(model_file,
           conditions,
           error,
           method,
           target_species,
           retained_species=None,
           run_sensitivity_analysis=False,
           epsilon_star=0.1):
    """Driver function for pyMARS to reduce a model.

    Parameters
    ----------
    model_file : str
        Cantera-format model to be reduced (e.g., 'mech.cti').
    conditions : str
        File with list of autoignition initial conditions.
    error : float
        Maximum error % for the reduced model.
    method : {'DRG', 'DRGEP', 'PFA'}
        Skeletal reduction method to use.
    target_species: list of str
        List of target species for reduction.
    retained_species : list of str, optional
        List of non-target species to always retain.
    run_sensitivity_analysis : bool, optional
        Flag to run sensitivity analysis after completing another method.
    epsilon_star : float, optional
        Epsilon^* value used to determine species for sensitivity analysis.

    Returns
    -------
        Converted mechanism file
        Trimmed Solution Object
        Trimmed Mechanism file

    Examples
    --------
    >>> pymars('gri30.cti', conditions_file, 10.0, 'DRGEP', ['CH4', 'O2'], retained_species=['N2'])

    """

    solution_object = Solution(model_file)

    if method == 'DRG':
        results = run_drg(solution_object, conditions, error, target_species,
                          retained_species)
    elif method == 'PFA':
        results = run_pfa(solution_object, conditions, error, target_species,
                          retained_species)
    elif method == 'DRGEP':
        results = run_drgep(solution_object, conditions, error, target_species,
                            retained_species)
    reduced_model, reduced_error = results

    if run_sensitivity_analysis:
        results = run_sa(solution_object, reduced_model, epsilon_star,
                         conditions, error, retained_species)
        reduced_model, reduced_error = results
        sa_file = soln2cti.write(reduced_model)
Exemplo n.º 6
0
def validate_on_mechanism(mech, temperature, pressure, tau, do_rhs, do_jac):
    xml = join(test_mech_directory, mech + '.xml')
    T = temperature
    Tin = T + 1000.
    p = pressure

    r = ChemicalMechanismSpec(xml, 'gas').griffon

    gas = Solution(xml)
    ns = gas.n_species

    y = np.ones(ns)  # equal masses in the reactor
    gas.TPY = T, p, y
    y = np.copy(gas.Y)

    xin = np.ones(ns)  # equal moles in the feed
    gas.TPX = Tin, p, xin
    yin = np.copy(gas.Y)

    state = hstack((T, y[:-1]))

    rhsCN = rhs_cantera(p, T, y, Tin, yin, tau, gas)
    rhsGR = np.empty(ns)
    r.reactor_rhs_isobaric(state, p, Tin, yin, tau, 0, 0, 0, 0, 0, 0, True, rhsGR)

    if do_rhs:
        return max(abs(rhsGR - rhsCN) / (abs(rhsCN) + 1.)) < 1.e-4

    if do_jac:
        jacGR = np.empty(ns * ns)
        r.reactor_jac_isobaric(state, p, Tin, yin, tau, 0, 0, 0, 0, 0, 0, True, 0, 0, rhsGR, jacGR)
        jacGR = jacGR.reshape((ns, ns), order='F')

        dT = 1.e-6
        dY = 1.e-6
        jacFD = np.empty((ns, ns))
        rhsGR1, rhsGR2 = np.empty(ns), np.empty(ns)
        state_m = hstack((T - dT, y[:-1]))
        state_p = hstack((T + dT, y[:-1]))
        r.reactor_rhs_isobaric(state_m, p, Tin, yin, tau, 0, 0, 0, 0, 0, 0, True, rhsGR1)
        r.reactor_rhs_isobaric(state_p, p, Tin, yin, tau, 0, 0, 0, 0, 0, 0, True, rhsGR2)
        jacFD[:, 0] = (- rhsGR1 + rhsGR2) / (2. * dT)

        for i in range(ns - 1):
            y_m1, y_p1 = np.copy(y), np.copy(y)
            y_m1[i] += - dY
            y_m1[-1] -= - dY
            y_p1[i] += dY
            y_p1[-1] -= dY
            state_m = hstack((T, y_m1[:-1]))
            state_p = hstack((T, y_p1[:-1]))
            r.reactor_rhs_isobaric(state_m, p, Tin, yin, tau, 0, 0, 0, 0, 0, 0, True, rhsGR1)
            r.reactor_rhs_isobaric(state_p, p, Tin, yin, tau, 0, 0, 0, 0, 0, 0, True, rhsGR2)
            jacFD[:, 1 + i] = (- rhsGR1 + rhsGR2) / (2. * dY)

        pass_jac = max(abs(jacGR - jacFD) / (abs(jacGR) + 1.)) < 1.e-2

        return pass_jac
Exemplo n.º 7
0
    def test_good_input():
        # compare against SDToolbox results
        mechanism = 'gri30.cti'

        initial_gas = Solution(mechanism)
        initial_gas.TPX = 300, 101325, {'H2': 1}

        working_gas = Solution(mechanism)
        working_gas.TPX = 300, 101325 * 2, {'H2': 1}

        cj_calcs = sd.Detonation.cj_state(
            working_gas,
            initial_gas,
            1e-5,
            1e-5,
            1.5
        )

        good_temp = 355.77590742266216
        good_press = 180244.9690980063
        good_species = {'H': 2.8407416566652653e-30, 'H2': 1.0}

        test_temp = cj_calcs[0].T
        check_temp = abs(test_temp - good_temp) / good_temp < 1e-7

        test_press = cj_calcs[0].P
        check_press = abs(test_press - good_press) / good_press < 1e-7

        good_velocity = 1700.3611387277992
        test_velocity = cj_calcs[1]
        check_velocity = abs(test_velocity - good_velocity) / good_velocity \
            < 1e-7

        checks = [check_temp, check_press, check_velocity]

        # make sure the species in each solution are the same
        test_species = cj_calcs[0].mole_fraction_dict()
        for species in good_species:
            checks.append(
                good_species[species] - test_species[species] < 1e-7
            )

        assert all(checks)
Exemplo n.º 8
0
    def test_mechanism_serialization(self):
        xml = abspath(join('tests', 'test_mechanisms', 'h2-burke.xml'))
        sol = Solution(xml)
        m1 = ChemicalMechanismSpec.from_solution(sol)

        m1_pickle = pickle.dumps(m1)
        m2 = pickle.loads(m1_pickle)

        self.assertTrue(m1.mech_data['species'] == m2.mech_data['species'])
        self.assertTrue(m1.mech_data['reactions'] == m2.mech_data['reactions'])
Exemplo n.º 9
0
 def test_create_valid_mechanism_spec_from_solution(self):
     try:
         xml = abspath(join('tests', 'test_mechanisms', 'h2-burke.xml'))
         sol = Solution(xml)
         m = ChemicalMechanismSpec.from_solution(sol)
         g = m.griffon
         x = m.mech_xml_path
         n = m.group_name
         s = m.gas
         self.assertTrue(True)
     except:
         self.assertTrue(False)
def validate_on_mechanism(mech, temperature, pressure, test_rhs=True, test_jac=True):
    xml = join(test_mech_directory, mech + '.xml')

    T = temperature
    p = pressure

    r = ChemicalMechanismSpec(xml, 'gas').griffon

    gas = Solution(xml)
    ns = gas.n_species

    gas.TPX = T, p, ones(ns)
    y = gas.Y

    state = hstack((T, y[:-1]))

    rhsGR = np.empty(ns)
    r.reactor_rhs_isobaric(state, p, 0., np.ndarray(1), 0, 0, 0, 0, 0, 0, 0, False, rhsGR)

    if test_jac:
        Tin, yin, tau = 0, np.ndarray(1), 0
        rhsTmp = np.empty(ns)
        jacGR = np.empty(ns * ns)
        r.reactor_jac_isobaric(state, p, Tin, yin, tau, 0, 0, 0, 0, 0, 0, False, 0, 0, rhsTmp, jacGR)
        jacGR = jacGR.reshape((ns, ns), order='F')

        dT = 1.e-6
        dY = 1.e-6
        jacFD = np.empty((ns, ns))
        rhsGR1, rhsGR2 = np.empty(ns), np.empty(ns)
        state_m = hstack((T - dT, y[:-1]))
        state_p = hstack((T + dT, y[:-1]))
        r.reactor_rhs_isobaric(state_m, p, Tin, yin, tau, 0, 0, 0, 0, 0, 0, False, rhsGR1)
        r.reactor_rhs_isobaric(state_p, p, Tin, yin, tau, 0, 0, 0, 0, 0, 0, False, rhsGR2)
        jacFD[:, 0] = (- rhsGR1 + rhsGR2) / (2. * dT)

        for i in range(ns - 1):
            y_m1, y_p1 = np.copy(y), np.copy(y)
            y_m1[i] += - dY
            y_m1[-1] -= - dY
            y_p1[i] += dY
            y_p1[-1] -= dY
            state_m = hstack((T, y_m1[:-1]))
            state_p = hstack((T, y_p1[:-1]))
            r.reactor_rhs_isobaric(state_m, p, Tin, yin, tau, 0, 0, 0, 0, 0, 0, False, rhsGR1)
            r.reactor_rhs_isobaric(state_p, p, Tin, yin, tau, 0, 0, 0, 0, 0, 0, False, rhsGR2)
            jacFD[:, 1 + i] = (- rhsGR1 + rhsGR2) / (2. * dY)

        pass_jac = max(abs(jacGR - jacFD) / (abs(jacGR) + 1.)) < 1.e-2

    w = gas.net_production_rates * gas.molecular_weights
    h = gas.standard_enthalpies_RT * gas.T * gas_constant / gas.molecular_weights
    rhsCN = zeros(ns)
    rhsCN[1:] = w[:-1] / gas.density
    rhsCN[0] = - sum(w * h) / gas.density / gas.cp_mass
    pass_rhs = max(abs(rhsGR - rhsCN) / (abs(rhsCN) + 1.)) < 100. * sqrt(np.finfo(float).eps)

    if test_rhs and test_jac:
        return pass_rhs and pass_jac
    if test_rhs:
        return pass_rhs
    if test_jac:
        return pass_jac
Exemplo n.º 11
0
def validate_on_mechanism(mech, temperature, pressure, tau, do_rhs, do_jac):
    xml = join(test_mech_directory, mech + '.xml')
    T = temperature
    Tin = T + 1000.
    p = pressure

    r = ChemicalMechanismSpec(xml, 'gas').griffon

    gas = Solution(xml)
    ns = gas.n_species

    y = np.ones(ns)  # equal masses in the reactor
    gas.TPY = T, p, y
    y = np.copy(gas.Y)
    rho = gas.density_mass

    xin = np.ones(ns)  # equal moles in the feed
    gas.TPX = Tin, p, xin
    yin = np.copy(gas.Y)
    rhoin = gas.density_mass

    state = hstack((rho, T, y[:-1]))

    rhsGRChemOnly = np.zeros(ns + 1)
    r.reactor_rhs_isochoric(state, rhoin, Tin, yin, tau, 0, 0, 0, 0, 0, 0,
                            False, rhsGRChemOnly)
    rhsCN = rhs_cantera(p, T, y, rhoin, Tin, yin, tau, gas, rhsGRChemOnly)
    rhsGR = np.empty(ns + 1)
    r.reactor_rhs_isochoric(state, rhoin, Tin, yin, tau, 0, 0, 0, 0, 0, 0,
                            True, rhsGR)

    if do_rhs:
        return max(abs(rhsGR - rhsCN) /
                   (abs(rhsCN) + 1.)) < 100. * sqrt(np.finfo(float).eps)

    if do_jac:
        jacGR = np.empty((ns + 1) * (ns + 1))
        r.reactor_jac_isochoric(state, rhoin, Tin, yin, tau, 0, 0, 0, 0, 0, 0,
                                True, 0, rhsGR, jacGR)
        jacGR = jacGR.reshape((ns + 1, ns + 1), order='F')

        drho = 1.e-6
        dT = 1.e-6
        dY = 1.e-6
        jacFD = np.empty((ns + 1, ns + 1))
        rhsGR1, rhsGR2 = np.empty(ns + 1), np.empty(ns + 1)

        state_m = hstack((rho - drho, T, y[:-1]))
        state_p = hstack((rho + drho, T, y[:-1]))
        r.reactor_rhs_isochoric(state_m, rhoin, Tin, yin, tau, 0, 0, 0, 0, 0,
                                0, True, rhsGR1)
        r.reactor_rhs_isochoric(state_p, rhoin, Tin, yin, tau, 0, 0, 0, 0, 0,
                                0, True, rhsGR2)
        jacFD[:, 0] = (-rhsGR1 + rhsGR2) / (2. * drho)

        state_m = hstack((rho, T - dT, y[:-1]))
        state_p = hstack((rho, T + dT, y[:-1]))
        r.reactor_rhs_isochoric(state_m, rhoin, Tin, yin, tau, 0, 0, 0, 0, 0,
                                0, True, rhsGR1)
        r.reactor_rhs_isochoric(state_p, rhoin, Tin, yin, tau, 0, 0, 0, 0, 0,
                                0, True, rhsGR2)
        jacFD[:, 1] = (-rhsGR1 + rhsGR2) / (2. * dT)

        for i in range(ns - 1):
            y_m1, y_p1 = np.copy(y), np.copy(y)
            y_m1[i] += -dY
            y_m1[-1] -= -dY
            y_p1[i] += dY
            y_p1[-1] -= dY
            state_m = hstack((rho, T, y_m1[:-1]))
            state_p = hstack((rho, T, y_p1[:-1]))
            r.reactor_rhs_isochoric(state_m, rhoin, Tin, yin, tau, 0, 0, 0, 0,
                                    0, 0, True, rhsGR1)
            r.reactor_rhs_isochoric(state_p, rhoin, Tin, yin, tau, 0, 0, 0, 0,
                                    0, 0, True, rhsGR2)
            jacFD[:, 2 + i] = (-rhsGR1 + rhsGR2) / (2. * dY)

        return max(abs(jacGR - jacFD) / (abs(jacGR) + 1.)) < 1.e-4
Exemplo n.º 12
0
def validate_on_mechanism(mech, temperature, pressure, full_Jacobian,
                          isochoric):
    xml = join(test_mech_directory, mech + '.xml')

    r = ChemicalMechanismSpec(xml, 'gas').griffon

    gas = Solution(xml)
    ns = gas.n_species

    T = temperature
    p = pressure

    gas.TPX = T, p, ones(ns)
    y = gas.Y
    rho = gas.density_mass

    if full_Jacobian and isochoric:
        state = hstack((rho, T, y[:-1]))
        rhsGRTemporary = np.empty(ns + 1)
        jac_dense = np.empty((ns + 1) * (ns + 1))
        jac_sparse = np.empty((ns + 1) * (ns + 1))
        r.reactor_jac_isochoric(state, 0, 0, np.ndarray(1), 0, 0, 0, 0, 0, 0,
                                0, False, 0, rhsGRTemporary, jac_dense)
        r.reactor_jac_isochoric(state, 0, 0, np.ndarray(1), 0, 0, 0, 0, 0, 0,
                                0, False, 2, rhsGRTemporary, jac_sparse)
        jac_dense = jac_dense.reshape((ns + 1, ns + 1), order='F')
        jac_sparse = jac_sparse.reshape((ns + 1, ns + 1), order='F')

    elif full_Jacobian and not isochoric:
        state = hstack((T, y[:-1]))
        k = np.empty(ns)
        jac_dense = np.empty(ns * ns)
        jac_sparse = np.empty(ns * ns)
        r.reactor_jac_isobaric(state, p, 0, np.ndarray(1), 0, 0, 0, 0, 0, 0, 0,
                               False, 0, 0, k, jac_dense)
        r.reactor_jac_isobaric(state, p, 0, np.ndarray(1), 0, 0, 0, 0, 0, 0, 0,
                               False, 2, 0, k, jac_sparse)

        jac_dense = jac_dense.reshape((ns, ns), order='F')
        jac_sparse = jac_sparse.reshape((ns, ns), order='F')

    else:
        jac_dense = np.zeros((ns + 1) * (ns + 1))
        jac_sparse = np.zeros((ns + 1) * (ns + 1))
        r.prod_rates_primitive_sensitivities(rho, T, y, 0, jac_dense)
        r.prod_rates_primitive_sensitivities(rho, T, y, 2, jac_sparse)
        jac_dense = jac_dense.reshape((ns + 1, ns + 1), order='F')[:-1, :]
        jac_sparse = jac_sparse.reshape((ns + 1, ns + 1), order='F')[:-1, :]

        jac_fd = np.zeros_like(jac_dense)
        rhsGR1 = np.zeros(ns)
        rhsGR2 = np.zeros(ns)
        dT = 1.e-4
        dr = 1.e-4
        dY = 1.e-4
        r.production_rates(T, rho + dr, y, rhsGR1)
        r.production_rates(T, rho - dr, y, rhsGR2)
        jac_fd[:, 0] = (rhsGR1 - rhsGR2) / dr * 0.5
        r.production_rates(T + dT, rho, y, rhsGR1)
        r.production_rates(T - dT, rho, y, rhsGR2)
        jac_fd[:, 1] = (rhsGR1 - rhsGR2) / dT * 0.5

        for spec_idx in range(ns - 1):
            Yp = np.copy(y)
            Yp[spec_idx] += dY
            Yp[-1] -= dY
            Ym = np.copy(y)
            Ym[spec_idx] -= dY
            Ym[-1] += dY
            r.production_rates(T, rho, Yp, rhsGR1)
            r.production_rates(T, rho, Ym, rhsGR2)
            jac_fd[:, 2 + spec_idx] = (rhsGR1 - rhsGR2) / dY * 0.5

    pass_sparse_vs_dense_jac = np.linalg.norm(
        jac_dense.ravel() - jac_sparse.ravel(), ord=np.Inf) < 1.e-10

    # if not pass_sparse_vs_dense_jac:
    #     print(mech)
    #     diff = abs(jac_dense - jac_sparse) / (abs(jac_dense) + 1.)
    #     nr, nc = jac_dense.shape
    #     print('dense')
    #     for ir in range(nr):
    #         for ic in range(nc):
    #             print(f'{jac_dense[ir, ic]:8.0e}', end=', ')
    #         print('')
    #     print('sparse')
    #     for ir in range(nr):
    #         for ic in range(nc):
    #             print(f'{jac_sparse[ir, ic]:8.0e}', end=', ')
    #         print('')
    #     print('finite difference')
    #     for ir in range(nr):
    #         for ic in range(nc):
    #             print(f'{jac_fd[ir, ic]:8.0e}', end=', ')
    #         print('')
    #     print('diff')
    #     for ir in range(nr):
    #         for ic in range(nc):
    #             print(f'{diff[ir, ic]:8.0e}' if diff[ir, ic] > 1.e-12 else f'{"":8}', end=', ')
    #         print('')
    return pass_sparse_vs_dense_jac
Exemplo n.º 13
0
def validate_on_mechanism(mech,
                          temperature,
                          pressure,
                          test_rhs=True,
                          test_jac=True):
    xml = join(test_mech_directory, mech + '.xml')

    r = ChemicalMechanismSpec(xml, 'gas').griffon

    gas = Solution(xml)
    ns = gas.n_species

    T = temperature
    p = pressure

    gas.TPX = T, p, ones(ns)
    y = gas.Y
    rho = gas.density_mass

    state = hstack((rho, T, y[:-1]))

    rhsGR = np.empty(ns + 1)
    rhsGRTemporary = np.empty(ns + 1)
    jacGR = np.empty((ns + 1) * (ns + 1))
    r.reactor_rhs_isochoric(state, 0, 0, np.ndarray(1), 0, 0, 0, 0, 0, 0, 0,
                            False, rhsGR)
    r.reactor_jac_isochoric(state, 0, 0, np.ndarray(1), 0, 0, 0, 0, 0, 0, 0,
                            False, 0, rhsGRTemporary, jacGR)
    jacGR = jacGR.reshape((ns + 1, ns + 1), order='F')

    def cantera_rhs(rho_arg, T_arg, Y_arg):
        gas.TDY = T_arg, rho_arg, Y_arg
        w = gas.net_production_rates * gas.molecular_weights
        e = gas.standard_int_energies_RT * gas.T * gas_constant / gas.molecular_weights
        cv = gas.cv_mass
        rhs = zeros(ns + 1)
        rhs[0] = 0.
        rhs[1] = -sum(w * e) / (rho_arg * cv)
        rhs[2:] = w[:-1] / rho
        return rhs

    rhsCN = cantera_rhs(rho, T, y)

    if test_rhs:
        pass_rhs = max(abs(rhsGR - rhsCN) /
                       (abs(rhsCN) + 1.)) < 100. * sqrt(np.finfo(float).eps)

    if test_jac:
        jacFD = zeros((ns + 1, ns + 1))
        wm1 = zeros(ns + 1)
        wp1 = zeros(ns + 1)
        drho = 1.e-4
        dT = 1.e-2
        dY = 1.e-6

        state_m = hstack((rho - drho, T, y[:-1]))
        state_p = hstack((rho + drho, T, y[:-1]))
        r.reactor_rhs_isochoric(state_m, 0, 0, np.ndarray(1), 0, 0, 0, 0, 0, 0,
                                0, False, wm1)
        r.reactor_rhs_isochoric(state_p, 0, 0, np.ndarray(1), 0, 0, 0, 0, 0, 0,
                                0, False, wp1)
        jacFD[:, 0] = (-wm1 + wp1) / (2. * drho)

        state_m = hstack((rho, T - dT, y[:-1]))
        state_p = hstack((rho, T + dT, y[:-1]))
        r.reactor_rhs_isochoric(state_m, 0, 0, np.ndarray(1), 0, 0, 0, 0, 0, 0,
                                0, False, wm1)
        r.reactor_rhs_isochoric(state_p, 0, 0, np.ndarray(1), 0, 0, 0, 0, 0, 0,
                                0, False, wp1)
        jacFD[:, 1] = (-wm1 + wp1) / (2. * dT)

        for i in range(ns - 1):
            y_m1, y_p1 = copy(y), copy(y)
            y_m1[i] += -dY
            y_m1[-1] -= -dY
            y_p1[i] += dY
            y_p1[-1] -= dY
            state_m = hstack((rho, T, y_m1[:-1]))
            state_p = hstack((rho, T, y_p1[:-1]))
            r.reactor_rhs_isochoric(state_m, 0, 0, np.ndarray(1), 0, 0, 0, 0,
                                    0, 0, 0, False, wm1)
            r.reactor_rhs_isochoric(state_p, 0, 0, np.ndarray(1), 0, 0, 0, 0,
                                    0, 0, 0, False, wp1)
            jacFD[:, 2 + i] = (-wm1 + wp1) / (2. * dY)

        gas.TDY = T, rho, y
        cv = gas.cv_mass
        cvi = gas.standard_cp_R * gas_constant / gas.molecular_weights
        w = gas.net_production_rates * gas.molecular_weights
        e = gas.standard_int_energies_RT * gas.T * gas_constant / gas.molecular_weights

        gas.TDY = T + dT, rho, y
        wp = gas.net_production_rates * gas.molecular_weights
        cvp = gas.cv_mass

        gas.TDY = T - dT, rho, y
        wm = gas.net_production_rates * gas.molecular_weights
        cvm = gas.cv_mass

        wsensT = (wp - wm) / (2. * dT)
        cvsensT = (cvp - cvm) / (2. * dT)

        jacFD11 = np.copy(jacFD[1, 1])
        jacSemiFD11 = -1. / cv * (1. / rho * (sum(wsensT * e) + sum(cvi * w)) +
                                  cvsensT * rhsGR[1])
        pass_jac = max(abs(jacGR - jacFD) / (abs(jacGR) + 1.)) < 1.e-4

    if test_rhs:
        return pass_rhs
    if test_jac:
        if not pass_jac:
            print(jacGR[1, 1])
            print(jacSemiFD11)
            print(jacFD11)
            print(abs(jacGR - jacFD) / (abs(jacGR) + 1.))
        return pass_jac