示例#1
0
def run_demo(with_plots=True):

    curr_dir = os.path.dirname(os.path.abspath(__file__));
        
    jmu_name  = compile_jmu("Orbital", curr_dir+"/files/Hohmann.mop")
    comp_opts = {"normalize_minimum_time_problems": False} # Disable minimum time normalization
    fmux_name = compile_fmux("HohmannTransfer", curr_dir+"/files/Hohmann.mop",
                             compiler_options=comp_opts)

    # Optimization
    model = CasadiModel(fmux_name)
    opts = model.optimize_options(algorithm="CasadiPseudoSpectralAlg")
    opts["n_cp"] = 40                                      # Number of collocation points
    opts["n_e"] = 2                                        # Number of phases
    opts["free_phases"] = True                             # The phase boundary is allowed to be changed in time
    opts["phase_options"] = ["t1"]                         # The phase boundary is connected to variable t1
    opts["link_options"] = [(1,"vy","dy1"),(1,"vx","dx1")] # Allow for discontinuities between phase 1 and 2 for vy and vx.
                                                           # The discontinuities are connected by dy1 and dx1
    # Optimize
    res_opt = model.optimize(algorithm="CasadiPseudoSpectralAlg", options=opts)

    # Get results
    dx1,dy1,dx2,dy2 = res_opt.final("dx1"), res_opt.final("dy1"), res_opt.final("dx2"), res_opt.final("dy2")
    r1,r2,my        = res_opt.final("rStart"), res_opt.final("rFinal"), res_opt.final("my")
    tf,t1           = res_opt.final("time"), res_opt.final("t1")
    
    # Verify solution using theoretical results
    # Assert dv1 
    assert N.abs( N.sqrt(dx1**2+dy1**2) - N.sqrt(my/r1)*(N.sqrt(2*r2/(r1+r2))-1) ) < 1e-1
    #Assert dv2 
    assert N.abs( N.sqrt(dx2**2+dy2**2) - N.sqrt(my/r2)*(1-N.sqrt(2*r1/(r1+r2))) ) < 1e-1
    #Assert transfer time 
    assert N.abs( tf-t1 - N.pi*((r1+r2)**3/(8*my))**0.5 ) < 1e-1

    # Verify solution by simulation
    model = JMUModel(jmu_name)
    
    # Retrieve the options
    opts = model.simulate_options()
    
    opts["ncp"] = 100
    opts["solver"] = "IDA"
    opts["initialize"] = False
    
    # Simulation of Phase 1
    res = model.simulate(final_time=t1,options=opts)

    x_phase_1,y_phase_1 = res["x"], res["y"]
    
    # Simulation of Phase 2
    model.set("vx", dx1 + res.final("vx"))
    model.set("vy", dy1 + res.final("vy"))

    res = model.simulate(start_time=t1,final_time=tf,options=opts)

    x_phase_2,y_phase_2 = res["x"], res["y"]

    # Simulation of Phase 3 (verify that we are still in orbit)
    model.set("vx", dx2 + res.final("vx"))
    model.set("vy", dy2 + res.final("vy"))

    res = model.simulate(start_time=tf, final_time=tf*2, options=opts)

    x_phase_3,y_phase_3 = res["x"], res["y"]
    
    if with_plots:
        # Plot Earth
        r = 1.0
        xE = r*N.cos(N.linspace(0,2*N.pi,200))
        yE = r*N.sin(N.linspace(0,2*N.pi,200))
        plt.plot(xE,yE,label="Earth")

        # Plot Orbits
        r = res.final("rStart")
        xS = r*N.cos(N.linspace(0,2*N.pi,200))
        yS = r*N.sin(N.linspace(0,2*N.pi,200))
        plt.plot(xS,yS,label="Low Orbit")

        r = res.final("rFinal")
        xSE = r*N.cos(N.linspace(0,2*N.pi,200))
        ySE = r*N.sin(N.linspace(0,2*N.pi,200))
        plt.plot(xSE,ySE,label="High Orbit")
        
        # Plot Satellite trajectory
        x_sim=N.hstack((N.hstack((x_phase_1,x_phase_2)),x_phase_3))
        y_sim=N.hstack((N.hstack((y_phase_1,y_phase_2)),y_phase_3))

        plt.plot(x_sim,y_sim,"-",label="Satellite")
        
        # Plot Rocket Burns
        plt.arrow(x_phase_1[-1],y_phase_1[-1],0.5*dx1,0.5*dy1, width=0.01,label="dv1")
        plt.arrow(x_phase_2[-1],y_phase_2[-1],0.5*dx2,0.5*dy2, width=0.01,label="dv2")
        
        plt.legend()
        plt.show()
示例#2
0
def run_demo(with_plots=True):

    curr_dir = os.path.dirname(os.path.abspath(__file__))

    jmu_name = compile_jmu("Orbital", curr_dir + "/files/Hohmann.mop")
    comp_opts = {
        "normalize_minimum_time_problems": False
    }  # Disable minimum time normalization
    fmux_name = compile_fmux("HohmannTransfer",
                             curr_dir + "/files/Hohmann.mop",
                             compiler_options=comp_opts)

    # Optimization
    model = CasadiModel(fmux_name)
    opts = model.optimize_options(algorithm="CasadiPseudoSpectralAlg")
    opts["n_cp"] = 40  # Number of collocation points
    opts["n_e"] = 2  # Number of phases
    opts[
        "free_phases"] = True  # The phase boundary is allowed to be changed in time
    opts["phase_options"] = [
        "t1"
    ]  # The phase boundary is connected to variable t1
    opts["link_options"] = [
        (1, "vy", "dy1"), (1, "vx", "dx1")
    ]  # Allow for discontinuities between phase 1 and 2 for vy and vx.
    # The discontinuities are connected by dy1 and dx1
    # Optimize
    res_opt = model.optimize(algorithm="CasadiPseudoSpectralAlg", options=opts)

    # Get results
    dx1, dy1, dx2, dy2 = res_opt.final("dx1"), res_opt.final(
        "dy1"), res_opt.final("dx2"), res_opt.final("dy2")
    r1, r2, my = res_opt.final("rStart"), res_opt.final(
        "rFinal"), res_opt.final("my")
    tf, t1 = res_opt.final("time"), res_opt.final("t1")

    # Verify solution using theoretical results
    # Assert dv1
    assert N.abs(
        N.sqrt(dx1**2 + dy1**2) - N.sqrt(my / r1) *
        (N.sqrt(2 * r2 / (r1 + r2)) - 1)) < 1e-1
    #Assert dv2
    assert N.abs(
        N.sqrt(dx2**2 + dy2**2) - N.sqrt(my / r2) *
        (1 - N.sqrt(2 * r1 / (r1 + r2)))) < 1e-1
    #Assert transfer time
    assert N.abs(tf - t1 - N.pi * ((r1 + r2)**3 / (8 * my))**0.5) < 1e-1

    # Verify solution by simulation
    model = JMUModel(jmu_name)

    # Retrieve the options
    opts = model.simulate_options()

    opts["ncp"] = 100
    opts["solver"] = "IDA"
    opts["initialize"] = False

    # Simulation of Phase 1
    res = model.simulate(final_time=t1, options=opts)

    x_phase_1, y_phase_1 = res["x"], res["y"]

    # Simulation of Phase 2
    model.set("vx", dx1 + res.final("vx"))
    model.set("vy", dy1 + res.final("vy"))

    res = model.simulate(start_time=t1, final_time=tf, options=opts)

    x_phase_2, y_phase_2 = res["x"], res["y"]

    # Simulation of Phase 3 (verify that we are still in orbit)
    model.set("vx", dx2 + res.final("vx"))
    model.set("vy", dy2 + res.final("vy"))

    res = model.simulate(start_time=tf, final_time=tf * 2, options=opts)

    x_phase_3, y_phase_3 = res["x"], res["y"]

    if with_plots:
        # Plot Earth
        r = 1.0
        xE = r * N.cos(N.linspace(0, 2 * N.pi, 200))
        yE = r * N.sin(N.linspace(0, 2 * N.pi, 200))
        plt.plot(xE, yE, label="Earth")

        # Plot Orbits
        r = res.final("rStart")
        xS = r * N.cos(N.linspace(0, 2 * N.pi, 200))
        yS = r * N.sin(N.linspace(0, 2 * N.pi, 200))
        plt.plot(xS, yS, label="Low Orbit")

        r = res.final("rFinal")
        xSE = r * N.cos(N.linspace(0, 2 * N.pi, 200))
        ySE = r * N.sin(N.linspace(0, 2 * N.pi, 200))
        plt.plot(xSE, ySE, label="High Orbit")

        # Plot Satellite trajectory
        x_sim = N.hstack((N.hstack((x_phase_1, x_phase_2)), x_phase_3))
        y_sim = N.hstack((N.hstack((y_phase_1, y_phase_2)), y_phase_3))

        plt.plot(x_sim, y_sim, "-", label="Satellite")

        # Plot Rocket Burns
        plt.arrow(x_phase_1[-1],
                  y_phase_1[-1],
                  0.5 * dx1,
                  0.5 * dy1,
                  width=0.01,
                  label="dv1")
        plt.arrow(x_phase_2[-1],
                  y_phase_2[-1],
                  0.5 * dx2,
                  0.5 * dy2,
                  width=0.01,
                  label="dv2")

        plt.legend()
        plt.show()
示例#3
0
def run_demo(with_plots=True):
    """
    This example is based on the Hicks-Ray Continuously Stirred Tank Reactors 
    (CSTR) system. The system has two states, the concentration and the 
    temperature. The control input to the system is the temperature of the 
    cooling flow in the reactor jacket. The chemical reaction in the reactor is 
    exothermic, and also temperature dependent; high temperature results in high 
    reaction rate.
    
    The problem is solved using the CasADi-based collocation algorithm. The
    steps performed correspond to those demonstrated in
    example pyjmi.examples.cstr, where the same problem is solved using the
    default JMI algorithm. FMI is used for initialization and simulation
    purposes.
    
    The following steps are demonstrated in this example:
    
    1.  How to solve the initialization problem. The initialization model has
        equations specifying that all derivatives should be identically zero,
        which implies that a stationary solution is obtained. Two stationary
        points, corresponding to different inputs, are computed. We call the
        stationary points A and B respectively. Point A corresponds to
        operating conditions where the reactor is cold and the reaction rate is
        low, whereas point B corresponds to a higher temperature where the
        reaction rate is high.
    
    2.  How to generate an initial guess for a direct collocation method by
        means of simulation with a constant input. The trajectories resulting
        from the simulation are used to initialize the variables in the
        transcribed NLP.
       
    3.  An optimal control problem is solved where the objective is to transfer 
        the state of the system from stationary point A to point B. The
        challenge is to ignite the reactor while avoiding uncontrolled
        temperature increase.

    4.  Finally the system is simulated using the optimal control profile. This
        step is important in order to verify that the approximation in the
        transcription step is sufficiently accurate.
    """
    ### 1. Solve the initialization problem
    # Locate the Modelica and Optimica code
    file_path = os.path.join(get_files_path(), "CSTR.mop")

    # Compile the stationary initialization model into a FMU
    init_fmu = compile_fmu("CSTR.CSTR_Init", file_path)

    # Load the FMU
    init_model = load_fmu(init_fmu)

    # Set input for Stationary point A
    Tc_0_A = 250
    init_model.set('Tc', Tc_0_A)

    # Solve the initialization problem using FMI
    init_model.initialize()

    # Store stationary point A
    [c_0_A, T_0_A] = init_model.get(['c', 'T'])

    # Print some data for stationary point A
    print(' *** Stationary point A ***')
    print('Tc = %f' % Tc_0_A)
    print('c = %f' % c_0_A)
    print('T = %f' % T_0_A)

    # Set inputs for Stationary point B
    init_model = load_fmu(init_fmu)
    Tc_0_B = 280
    init_model.set('Tc', Tc_0_B)

    # Solve the initialization problem using FMI
    init_model.initialize()

    # Store stationary point B
    [c_0_B, T_0_B] = init_model.get(['c', 'T'])

    # Print some data for stationary point B
    print(' *** Stationary point B ***')
    print('Tc = %f' % Tc_0_B)
    print('c = %f' % c_0_B)
    print('T = %f' % T_0_B)

    ### 2. Compute initial guess trajectories by means of simulation
    # Compile the optimization initialization model
    init_sim_fmu = compile_fmu("CSTR.CSTR_Init_Optimization", file_path)

    # Load the model
    init_sim_model = load_fmu(init_sim_fmu)

    # Set initial and reference values
    init_sim_model.set('cstr.c_init', c_0_A)
    init_sim_model.set('cstr.T_init', T_0_A)
    init_sim_model.set('c_ref', c_0_B)
    init_sim_model.set('T_ref', T_0_B)
    init_sim_model.set('Tc_ref', Tc_0_B)

    # Simulate with constant input Tc
    init_res = init_sim_model.simulate(start_time=0., final_time=150.)

    # Extract variable profiles
    c_init_sim = init_res['cstr.c']
    T_init_sim = init_res['cstr.T']
    Tc_init_sim = init_res['cstr.Tc']
    t_init_sim = init_res['time']

    # Plot the initial guess trajectories
    if with_plots:
        plt.close(1)
        plt.figure(1)
        plt.hold(True)
        plt.subplot(3, 1, 1)
        plt.plot(t_init_sim, c_init_sim)
        plt.grid()
        plt.ylabel('Concentration')
        plt.title('Initial guess obtained by simulation')

        plt.subplot(3, 1, 2)
        plt.plot(t_init_sim, T_init_sim)
        plt.grid()
        plt.ylabel('Temperature')

        plt.subplot(3, 1, 3)
        plt.plot(t_init_sim, Tc_init_sim)
        plt.grid()
        plt.ylabel('Cooling temperature')
        plt.xlabel('time')
        plt.show()

    ### 3. Solve the optimal control problem
    # Compile model
    fmux = compile_fmux("CSTR.CSTR_Opt2", file_path)

    # Load model
    cstr = CasadiModel(fmux)

    # Set reference values
    cstr.set('Tc_ref', Tc_0_B)
    cstr.set('c_ref', c_0_B)
    cstr.set('T_ref', T_0_B)

    # Set initial values
    cstr.set('cstr.c_init', c_0_A)
    cstr.set('cstr.T_init', T_0_A)

    # Set options
    opt_opts = cstr.optimize_options()
    opt_opts['n_e'] = 100  # Number of elements
    opt_opts['init_traj'] = init_res.result_data

    # Solve the optimal control problem
    res = cstr.optimize(options=opt_opts)

    # Extract variable profiles
    c_res = res['cstr.c']
    T_res = res['cstr.T']
    Tc_res = res['cstr.Tc']
    time_res = res['time']

    c_ref = res['c_ref']
    T_ref = res['T_ref']
    Tc_ref = res['Tc_ref']

    # Verify solution for testing purposes
    try:
        import casadi
    except:
        pass
    else:
        cost = float(res.solver.solver.output(casadi.NLP_SOLVER_F))
        assert (N.abs(cost / 1.e3 - 1.8585429) < 1e-3)

    # Plot the results
    if with_plots:
        plt.close(2)
        plt.figure(2)
        plt.hold(True)
        plt.subplot(3, 1, 1)
        plt.plot(time_res, c_res)
        plt.plot([time_res[0], time_res[-1]], [c_ref, c_ref], '--')
        plt.grid()
        plt.ylabel('Concentration')
        plt.title('Optimized trajectories')

        plt.subplot(312)
        plt.plot(time_res, T_res)
        plt.plot([time_res[0], time_res[-1]], [T_ref, T_ref], '--')
        plt.grid()
        plt.ylabel('Temperature')

        plt.subplot(313)
        plt.plot(time_res, Tc_res)
        plt.plot([time_res[0], time_res[-1]], [Tc_ref, Tc_ref], '--')
        plt.grid()
        plt.ylabel('Cooling temperature')
        plt.xlabel('time')
        plt.show()

    ### 4. Simulate to verify the optimal solution
    # Compile model
    sim_fmu = compile_fmu("CSTR.CSTR", file_path)

    # Load model
    sim_model = load_fmu(sim_fmu)

    # Get optimized input
    (_, opt_input) = res.solver.get_opt_input()

    # Set initial values
    sim_model.set('c_init', c_0_A)
    sim_model.set('T_init', T_0_A)

    # Simulate using optimized input
    res = sim_model.simulate(start_time=0.,
                             final_time=150.,
                             input=('Tc', opt_input))

    # Extract variable profiles
    c_sim = res['c']
    T_sim = res['T']
    Tc_sim = res['Tc']
    time_sim = res['time']

    # Plot the results
    if with_plots:
        plt.close(3)
        plt.figure(3)
        plt.hold(True)
        plt.subplot(3, 1, 1)
        plt.plot(time_res, c_res, '--', lw=5)
        plt.plot(time_sim, c_sim, lw=2)
        plt.legend(('optimized', 'simulated'))
        plt.grid(True)
        plt.ylabel('Concentration')
        plt.title('Verification')

        plt.subplot(3, 1, 2)
        plt.plot(time_res, T_res, '--', lw=5)
        plt.plot(time_sim, T_sim, lw=2)
        plt.grid(True)
        plt.ylabel('Temperature')

        plt.subplot(3, 1, 3)
        plt.plot(time_res, Tc_res, '--', lw=5)
        plt.plot(time_sim, Tc_sim, lw=2)
        plt.grid(True)
        plt.ylabel('Cooling temperature')
        plt.xlabel('time')
        plt.show()
示例#4
0
def run_demo(with_plots=True):
    """
    This example is based on the Hicks-Ray Continuously Stirred Tank Reactors 
    (CSTR) system. The system has two states, the concentration and the 
    temperature. The control input to the system is the temperature of the 
    cooling flow in the reactor jacket. The chemical reaction in the reactor is 
    exothermic, and also temperature dependent; high temperature results in high 
    reaction rate.
    
    The problem is solved using the CasADi-based collocation algorithm. The
    steps performed correspond to those demonstrated in
    example pyjmi.examples.cstr, where the same problem is solved using the
    default JMI algorithm. FMI is used for initialization and simulation
    purposes.
    
    The following steps are demonstrated in this example:
    
    1.  How to solve the initialization problem. The initialization model has
        equations specifying that all derivatives should be identically zero,
        which implies that a stationary solution is obtained. Two stationary
        points, corresponding to different inputs, are computed. We call the
        stationary points A and B respectively. Point A corresponds to
        operating conditions where the reactor is cold and the reaction rate is
        low, whereas point B corresponds to a higher temperature where the
        reaction rate is high.
    
    2.  How to generate an initial guess for a direct collocation method by
        means of simulation with a constant input. The trajectories resulting
        from the simulation are used to initialize the variables in the
        transcribed NLP.
       
    3.  An optimal control problem is solved where the objective is to transfer 
        the state of the system from stationary point A to point B. The
        challenge is to ignite the reactor while avoiding uncontrolled
        temperature increase.

    4.  Finally the system is simulated using the optimal control profile. This
        step is important in order to verify that the approximation in the
        transcription step is sufficiently accurate.
    """
    ### 1. Solve the initialization problem
    # Locate the Modelica and Optimica code
    file_path = os.path.join(get_files_path(), "CSTR.mop")
    
    # Compile the stationary initialization model into a FMU
    init_fmu = compile_fmu("CSTR.CSTR_Init", file_path)
    
    # Load the FMU
    init_model = load_fmu(init_fmu)
    
    # Set input for Stationary point A
    Tc_0_A = 250
    init_model.set('Tc', Tc_0_A)

    # Solve the initialization problem using FMI
    init_model.initialize()

    # Store stationary point A
    [c_0_A, T_0_A] = init_model.get(['c', 'T'])

    # Print some data for stationary point A
    print(' *** Stationary point A ***')
    print('Tc = %f' % Tc_0_A)
    print('c = %f' % c_0_A)
    print('T = %f' % T_0_A)
    
    # Set inputs for Stationary point B
    init_model = load_fmu(init_fmu)
    Tc_0_B = 280
    init_model.set('Tc', Tc_0_B)

    # Solve the initialization problem using FMI
    init_model.initialize()

    # Store stationary point B
    [c_0_B, T_0_B] = init_model.get(['c', 'T'])

    # Print some data for stationary point B
    print(' *** Stationary point B ***')
    print('Tc = %f' % Tc_0_B)
    print('c = %f' % c_0_B)
    print('T = %f' % T_0_B)
    
    ### 2. Compute initial guess trajectories by means of simulation
    # Compile the optimization initialization model
    init_sim_fmu = compile_fmu("CSTR.CSTR_Init_Optimization", file_path)

    # Load the model
    init_sim_model = load_fmu(init_sim_fmu)
    
    # Set initial and reference values
    init_sim_model.set('cstr.c_init', c_0_A)
    init_sim_model.set('cstr.T_init', T_0_A)
    init_sim_model.set('c_ref', c_0_B)
    init_sim_model.set('T_ref', T_0_B)
    init_sim_model.set('Tc_ref', Tc_0_B)
    
    # Simulate with constant input Tc
    init_res = init_sim_model.simulate(start_time=0., final_time=150.)

    # Extract variable profiles
    c_init_sim = init_res['cstr.c']
    T_init_sim = init_res['cstr.T']
    Tc_init_sim = init_res['cstr.Tc']
    t_init_sim = init_res['time']
    
    # Plot the initial guess trajectories
    if with_plots:
        plt.close(1)
        plt.figure(1)
        plt.hold(True)
        plt.subplot(3, 1, 1)
        plt.plot(t_init_sim, c_init_sim)
        plt.grid()
        plt.ylabel('Concentration')
        plt.title('Initial guess obtained by simulation')

        plt.subplot(3, 1, 2)
        plt.plot(t_init_sim, T_init_sim)
        plt.grid()
        plt.ylabel('Temperature')

        plt.subplot(3, 1, 3)
        plt.plot(t_init_sim, Tc_init_sim)
        plt.grid()
        plt.ylabel('Cooling temperature')
        plt.xlabel('time')
        plt.show()
    
    ### 3. Solve the optimal control problem
    # Compile model
    fmux = compile_fmux("CSTR.CSTR_Opt2", file_path)
    
    # Load model
    cstr = CasadiModel(fmux)
    
    # Set reference values
    cstr.set('Tc_ref', Tc_0_B)
    cstr.set('c_ref', c_0_B)
    cstr.set('T_ref', T_0_B)
    
    # Set initial values
    cstr.set('cstr.c_init', c_0_A)
    cstr.set('cstr.T_init', T_0_A)
    
    # Set options
    opt_opts = cstr.optimize_options()
    opt_opts['n_e'] = 100 # Number of elements
    opt_opts['init_traj'] = init_res.result_data
    opt_opts['nominal_traj'] = init_res.result_data
    opt_opts['IPOPT_options']['tol'] = 1e-10
    
    # Solve the optimal control problem
    res = cstr.optimize(options=opt_opts)
    
    # Extract variable profiles
    c_res = res['cstr.c']
    T_res = res['cstr.T']
    Tc_res = res['cstr.Tc']
    time_res = res['time']

    c_ref = res['c_ref']
    T_ref = res['T_ref']
    Tc_ref = res['Tc_ref']
    
    # Verify solution for testing purposes
    try:
        import casadi
    except:
        pass
    else:
        cost = float(res.solver.solver.output(casadi.NLP_SOLVER_F))
        assert(N.abs(cost/1.e3 - 1.8585429) < 1e-3)
    
    # Plot the results
    if with_plots:
        plt.close(2)
        plt.figure(2)
        plt.hold(True)
        plt.subplot(3, 1, 1)
        plt.plot(time_res, c_res)
        plt.plot([time_res[0], time_res[-1]], [c_ref, c_ref], '--')
        plt.grid()
        plt.ylabel('Concentration')
        plt.title('Optimized trajectories')

        plt.subplot(312)
        plt.plot(time_res,T_res)
        plt.plot([time_res[0],time_res[-1]],[T_ref,T_ref],'--')
        plt.grid()
        plt.ylabel('Temperature')

        plt.subplot(313)
        plt.plot(time_res,Tc_res)
        plt.plot([time_res[0],time_res[-1]],[Tc_ref,Tc_ref],'--')
        plt.grid()
        plt.ylabel('Cooling temperature')
        plt.xlabel('time')
        plt.show()

    ### 4. Simulate to verify the optimal solution
    # Compile model
    sim_fmu = compile_fmu("CSTR.CSTR", file_path)

    # Load model
    sim_model = load_fmu(sim_fmu)
    
    # Get optimized input
    (_, opt_input) = res.solver.get_opt_input()
    
    # Set initial values
    sim_model.set('c_init',c_0_A)
    sim_model.set('T_init',T_0_A)

    # Simulate using optimized input
    sim_opts = sim_model.simulate_options()
    sim_opts['CVode_options']['rtol'] = 1e-6
    sim_opts['CVode_options']['atol'] = 1e-8
    res = sim_model.simulate(start_time=0., final_time=150.,
                             input=('Tc', opt_input), options=sim_opts)
    
    # Extract variable profiles
    c_sim=res['c']
    T_sim=res['T']
    Tc_sim=res['Tc']
    time_sim = res['time']

    # Verify results
    N.testing.assert_array_less(abs(c_res[-1] - c_sim[-1])/c_res[-1], 5e-2)
    
    # Plot the results
    if with_plots:
        plt.close(3)
        plt.figure(3)
        plt.hold(True)
        plt.subplot(3, 1, 1)
        plt.plot(time_res, c_res, '--', lw=5)
        plt.plot(time_sim, c_sim, lw=2)
        plt.legend(('optimized', 'simulated'))
        plt.grid(True)
        plt.ylabel('Concentration')
        plt.title('Verification')
        
        plt.subplot(3, 1, 2)
        plt.plot(time_res, T_res, '--', lw=5)
        plt.plot(time_sim, T_sim, lw=2)
        plt.grid(True)
        plt.ylabel('Temperature')
        
        plt.subplot(3, 1, 3)
        plt.plot(time_res, Tc_res, '--', lw=5)
        plt.plot(time_sim, Tc_sim, lw=2)
        plt.grid(True)
        plt.ylabel('Cooling temperature')
        plt.xlabel('time')
        plt.show()