Esempio n. 1
0
 def setUp(self):
     """
     This sets up the test case.
     """
     def f(t,y):
         eps = 1.e-6
         my = 1./eps
         yd_0 = y[1]
         yd_1 = my*((1.-y[0]**2)*y[1]-y[0])
         
         return N.array([yd_0,yd_1])
     
     def jac(t,y):
         eps = 1.e-6
         my = 1./eps
         J = N.zeros([2,2])
         
         J[0,0]=0.
         J[0,1]=1.
         J[1,0]=my*(-2.*y[0]*y[1]-1.)
         J[1,1]=my*(1.-y[0]**2)
         
         return J
     
     def jac_sparse(t,y):
         eps = 1.e-6
         my = 1./eps
         J = N.zeros([2,2])
         
         J[0,0]=0.
         J[0,1]=1.
         J[1,0]=my*(-2.*y[0]*y[1]-1.)
         J[1,1]=my*(1.-y[0]**2)
         
         return sp.csc_matrix(J)
     
     #Define an Assimulo problem
     y0 = [2.0,-0.6] #Initial conditions
     
     exp_mod = Explicit_Problem(f,y0)
     exp_mod_t0 = Explicit_Problem(f,y0,1.0)
     exp_mod_sp = Explicit_Problem(f,y0)
     
     exp_mod.jac = jac
     exp_mod_sp.jac = jac_sparse
     self.mod = exp_mod
         
     #Define an explicit solver
     self.sim = Radau5ODE(exp_mod) #Create a Radau5 solve
     self.sim_t0 = Radau5ODE(exp_mod_t0)
     self.sim_sp = Radau5ODE(exp_mod_sp)
     
     #Sets the parameters
     self.sim.atol = 1e-4 #Default 1e-6
     self.sim.rtol = 1e-4 #Default 1e-6
     self.sim.inith = 1.e-4 #Initial step-size
     self.sim.usejac = False
Esempio n. 2
0
    def test_usejac_csc_matrix(self):
        """
        This tests the functionality of the property usejac.
        """
        f = lambda t, x: N.array([x[1], -9.82])  #Defines the rhs
        jac = lambda t, x: sp.csc_matrix(N.array([[0., 1.], [0., 0.]])
                                         )  #Defines the jacobian

        exp_mod = Explicit_Problem(f, [1.0, 0.0])
        exp_mod.jac = jac

        exp_sim = CVode(exp_mod)
        exp_sim.discr = 'BDF'
        exp_sim.iter = 'Newton'
        exp_sim.simulate(5., 100)

        assert exp_sim.statistics["nfcnjacs"] == 0
        nose.tools.assert_almost_equal(exp_sim.y_sol[-1][0], -121.75000143, 4)

        exp_sim.reset()
        exp_sim.usejac = False
        exp_sim.simulate(5., 100)

        nose.tools.assert_almost_equal(exp_sim.y_sol[-1][0], -121.75000143, 4)
        assert exp_sim.statistics["nfcnjacs"] > 0
Esempio n. 3
0
    def setUp(self):
        #Define the rhs
        def f(t, y):
            eps = 1.e-6
            my = 1. / eps
            yd_0 = y[1]
            yd_1 = my * ((1. - y[0]**2) * y[1] - y[0])

            return N.array([yd_0, yd_1])

        #Define the jacobian
        def jac(t, y):
            eps = 1.e-6
            my = 1. / eps
            j = N.array(
                [[0.0, 1.0],
                 [my * ((-2.0 * y[0]) * y[1] - 1.0), my * (1.0 - y[0]**2)]])
            return j

        def jac_sparse(t, y):
            eps = 1.e-6
            my = 1. / eps
            J = N.zeros([2, 2])

            J[0, 0] = 0.
            J[0, 1] = 1.
            J[1, 0] = my * (-2. * y[0] * y[1] - 1.)
            J[1, 1] = my * (1. - y[0]**2)

            return sp.csc_matrix(J)

        y0 = [2.0, -0.6]  #Initial conditions

        #Define an Assimulo problem
        exp_mod = Explicit_Problem(f, y0, name='Van der Pol (explicit)')
        exp_mod_sp = Explicit_Problem(f, y0, name='Van der Pol (explicit)')
        exp_mod.jac = jac
        exp_mod_sp.jac = jac_sparse
        self.mod = exp_mod
        self.mod_sp = exp_mod_sp
Esempio n. 4
0
def run_example(with_plots=True):
    global t,y
    
    #Defines the rhs
    def f(t,y):
        yd_0 = y[1]
        yd_1 = -9.82
        #print y, yd_0, yd_1
        return N.array([yd_0,yd_1])
    
    #Defines the jacobian
    def jac(t,y):
        j = N.array([[0,1.],[0,0]])
        return j
    
    #Defines an Assimulo explicit problem
    y0 = [1.0,0.0] #Initial conditions

    exp_mod = Explicit_Problem(f,y0)
    
    exp_mod.jac = jac #Sets the jacobian
    exp_mod.name = 'Example using Jacobian'

    
    exp_sim = CVode(exp_mod) #Create a CVode solver
    
    #Set the parameters
    exp_sim.iter = 'Newton' #Default 'FixedPoint'
    exp_sim.discr = 'BDF' #Default 'Adams'
    exp_sim.atol = 1e-5 #Default 1e-6
    exp_sim.rtol = 1e-5 #Default 1e-6
    
    #Simulate
    t, y = exp_sim.simulate(5, 1000) #Simulate 5 seconds with 1000 communication points
    
    #Basic tests
    nose.tools.assert_almost_equal(y[-1][0],-121.75000000,4)
    nose.tools.assert_almost_equal(y[-1][1],-49.100000000)
        
    #Plot
    if with_plots:
        P.plot(t,y,linestyle="dashed",marker="o") #Plot the solution
        P.show()
Esempio n. 5
0
def run_example(with_plots=True):
    global t, y

    #Defines the rhs
    def f(t, y):
        yd_0 = y[1]
        yd_1 = -9.82
        #print y, yd_0, yd_1
        return N.array([yd_0, yd_1])

    #Defines the jacobian
    def jac(t, y):
        j = N.array([[0, 1.], [0, 0]])
        return j

    #Defines an Assimulo explicit problem
    y0 = [1.0, 0.0]  #Initial conditions

    exp_mod = Explicit_Problem(f, y0)

    exp_mod.jac = jac  #Sets the jacobian
    exp_mod.name = 'Example using Jacobian'

    exp_sim = CVode(exp_mod)  #Create a CVode solver

    #Set the parameters
    exp_sim.iter = 'Newton'  #Default 'FixedPoint'
    exp_sim.discr = 'BDF'  #Default 'Adams'
    exp_sim.atol = 1e-5  #Default 1e-6
    exp_sim.rtol = 1e-5  #Default 1e-6

    #Simulate
    t, y = exp_sim.simulate(
        5, 1000)  #Simulate 5 seconds with 1000 communication points

    #Basic tests
    nose.tools.assert_almost_equal(y[-1][0], -121.75000000, 4)
    nose.tools.assert_almost_equal(y[-1][1], -49.100000000)

    #Plot
    if with_plots:
        P.plot(t, y, linestyle="dashed", marker="o")  #Plot the solution
        P.show()
Esempio n. 6
0
 def setUp(self):
     """
     This sets up the test case.
     """
     def f(t,y):
         eps = 1.e-6
         my = 1./eps
         yd_0 = y[1]
         yd_1 = my*((1.-y[0]**2)*y[1]-y[0])
         
         return N.array([yd_0,yd_1])
     
     def jac(t,y):
         eps = 1.e-6
         my = 1./eps
         J = N.zeros([2,2])
         
         J[0,0]=0.
         J[0,1]=1.
         J[1,0]=my*(-2.*y[0]*y[1]-1.)
         J[1,1]=my*(1.-y[0]**2)
         
         return J
     
     #Define an Assimulo problem
     y0 = [2.0,-0.6] #Initial conditions
     
     exp_mod = Explicit_Problem(f,y0)
     exp_mod_t0 = Explicit_Problem(f,y0,1.0)
     
     exp_mod.jac = jac
     self.mod = exp_mod
         
     #Define an explicit solver
     self.sim = Radau5ODE(exp_mod) #Create a Radau5 solve
     self.sim_t0 = Radau5ODE(exp_mod_t0)
     
     #Sets the parameters
     self.sim.atol = 1e-4 #Default 1e-6
     self.sim.rtol = 1e-4 #Default 1e-6
     self.sim.inith = 1.e-4 #Initial step-size
     self.sim.usejac = False
Esempio n. 7
0
    def test_usejac(self):
        """
        This tests the functionality of the property usejac.
        """
        f = lambda t,x: N.array([x[1], -9.82])       #Defines the rhs
        jac = lambda t,x: N.array([[0.,1.],[0.,0.]]) #Defines the jacobian
        
        exp_mod = Explicit_Problem(f, [1.0,0.0])
        exp_mod.jac = jac
        
        exp_sim = CVode(exp_mod)
        exp_sim.discr='BDF'
        exp_sim.iter='Newton'
        exp_sim.simulate(5.,100)
        
        assert exp_sim.statistics["nfevalsLS"] == 0
        nose.tools.assert_almost_equal(exp_sim.y_sol[-1][0], -121.75000143, 4)
        
        exp_sim.reset()
        exp_sim.usejac=False
        exp_sim.simulate(5.,100)

        nose.tools.assert_almost_equal(exp_sim.y_sol[-1][0], -121.75000143, 4)
        assert exp_sim.statistics["nfevalsLS"] > 0
Esempio n. 8
0
def run_example(with_plots=True):
    r"""
    Example for the use of the implicit Euler method to solve
    Van der Pol's equation
    
    .. math::
       
        \dot y_1 &= y_2 \\
        \dot y_2 &= \mu ((1.-y_1^2) y_2-y_1)

    with :math:`\mu=\frac{1}{5} 10^3`.

    on return:
    
       - :dfn:`exp_mod`    problem instance
    
       - :dfn:`exp_sim`    solver instance

    """
    eps = 5.e-3
    my = 1./eps
    
    #Define the rhs
    def f(t,y):
        yd_0 = y[1]
        yd_1 = my*((1.-y[0]**2)*y[1]-y[0])
        
        return N.array([yd_0,yd_1])
    
    #Define the Jacobian 
    def jac(t,y):
        jd_00 = 0.0
        jd_01 = 1.0
        jd_10 = -1.0*my-2*y[0]*y[1]*my
        jd_11 = my*(1.-y[0]**2)
        
        return N.array([[jd_00,jd_01],[jd_10,jd_11]])
    
    y0 = [2.0,-0.6] #Initial conditions
    
    #Define an Assimulo problem
    exp_mod = Explicit_Problem(f,y0, 
                          name = "ImplicitEuler: Van der Pol's equation (as explicit problem) ")
    exp_mod.jac = jac
    
    #Define an explicit solver
    exp_sim = ImplicitEuler(exp_mod) #Create a ImplicitEuler solver
    
    #Sets the parameters
    exp_sim.h = 1e-4 #Stepsize
    exp_sim.usejac = True #If the user defined jacobian should be used or not
    
    #Simulate
    t, y = exp_sim.simulate(2.0) #Simulate 2 seconds
    
    #Plot
    if with_plots:
        import pylab as P
        P.plot(t,y[:,0], marker='o')
        P.title(exp_mod.name)
        P.ylabel("State: $y_1$")
        P.xlabel("Time")
        P.show()

    #Basic test
    x1 = y[:,0]
    assert N.abs(x1[-1]-1.8601438) < 1e-1 #For test purpose
    
    return exp_mod, exp_sim
def run_example(with_plots=True):
    r"""
    Example for the use of RODAS to solve
    Van der Pol's equation
    
    .. math::
       
        \dot y_1 &= y_2 \\
        \dot y_2 &= \mu ((1.-y_1^2) y_2-y_1)

    with :math:`\mu=1 10^6`.

    on return:
    
       - :dfn:`exp_mod`    problem instance
    
       - :dfn:`exp_sim`    solver instance

    """
    
    #Define the rhs
    def f(t,y):
        eps = 1.e-6
        my = 1./eps
        yd_0 = y[1]
        yd_1 = my*((1.-y[0]**2)*y[1]-y[0])
        
        return N.array([yd_0,yd_1])
    
    #Define the jacobian
    def jac(t,y):
        eps = 1.e-6
        my = 1./eps
        j = N.array([[0.0,1.0],[my*((-2.0*y[0])*y[1]-1.0), my*(1.0-y[0]**2)]])
        return j
    
    y0 = [2.0,-0.6] #Initial conditions
    
    #Define an Assimulo problem
    exp_mod = Explicit_Problem(f,y0, name = 'Van der Pol (explicit)')
    exp_mod.jac = jac
    
    #Define an explicit solver
    exp_sim = RodasODE(exp_mod) #Create a Rodas solver
    
    #Sets the parameters
    exp_sim.atol = 1e-4 #Default 1e-6
    exp_sim.rtol = 1e-4 #Default 1e-6
    exp_sim.inith = 1.e-4 #Initial step-size
    exp_sim.usejac = True
    
    #Simulate
    t, y = exp_sim.simulate(2.) #Simulate 2 seconds

    #Plot
    if with_plots:
        P.plot(t,y[:,0], marker='o')
        P.title(exp_mod.name)
        P.ylabel("State: $y_1$")
        P.xlabel("Time")
        P.show()

    
    #Basic test
    x1 = y[:,0]
    assert N.abs(x1[-1]-1.706168035) < 1e-3 #For test purpose
    return exp_mod, exp_sim
Esempio n. 10
0
def run_example(with_plots=True):
    r"""
    Example for demonstrating the use of a user supplied Jacobian
    
    ODE:
    
    .. math::
       
       \dot y_1 &= y_2 \\
       \dot y_2 &= -9.82
       
    
    on return:
    
       - :dfn:`exp_mod`    problem instance
    
       - :dfn:`exp_sim`    solver instance
       
    """

    #Defines the rhs
    def f(t, y):
        yd_0 = y[1]
        yd_1 = -9.82
        return N.array([yd_0, yd_1])

    #Defines the Jacobian
    def jac(t, y):
        j = N.array([[0, 1.], [0, 0]])
        return j

    #Defines an Assimulo explicit problem
    y0 = [1.0, 0.0]  #Initial conditions

    exp_mod = Explicit_Problem(f, y0, name='Example using analytic Jacobian')
    exp_mod.jac = jac  #Sets the Jacobian

    exp_sim = CVode(exp_mod)  #Create a CVode solver

    #Set the parameters
    exp_sim.iter = 'Newton'  #Default 'FixedPoint'
    exp_sim.discr = 'BDF'  #Default 'Adams'
    exp_sim.atol = 1e-5  #Default 1e-6
    exp_sim.rtol = 1e-5  #Default 1e-6

    #Simulate
    t, y = exp_sim.simulate(
        5, 1000)  #Simulate 5 seconds with 1000 communication points

    #Plot
    if with_plots:
        import pylab as P
        P.plot(t, y, linestyle="dashed", marker="o")  #Plot the solution
        P.xlabel('Time')
        P.ylabel('State')
        P.title(exp_mod.name)
        P.show()

    #Basic tests
    nose.tools.assert_almost_equal(y[-1][0], -121.75000000, 4)
    nose.tools.assert_almost_equal(y[-1][1], -49.100000000)

    return exp_mod, exp_sim
def run_example(with_plots=True):
    r"""
    This is the same example from the Sundials package (cvsRoberts_FSA_dns.c)
    Its purpose is to demonstrate the use of parameters in the differential equation.

    This simple example problem for CVode, due to Robertson
    see http://www.dm.uniba.it/~testset/problems/rober.php, 
    is from chemical kinetics, and consists of the system:
    
    .. math:: 
    
       \dot y_1 &= -p_1 y_1 + p_2 y_2 y_3 \\
       \dot y_2 &= p_1 y_1 - p_2 y_2 y_3 - p_3 y_2^2 \\
       \dot y_3 &= p_3  y_ 2^2
       
    
    on return:
    
       - :dfn:`exp_mod`    problem instance
    
       - :dfn:`exp_sim`    solver instance
    
    """
    
    def f(t, y, p):
        
        yd_0 = -p[0]*y[0]+p[1]*y[1]*y[2] 
        yd_1 = p[0]*y[0]-p[1]*y[1]*y[2]-p[2]*y[1]**2 
        yd_2 = p[2]*y[1]**2
        
        return N.array([yd_0,yd_1,yd_2])
        
    def jac(t,y, p):
        J = N.array([[-p[0], p[1]*y[2], p[1]*y[1]],
                     [p[0], -p[1]*y[2]-2*p[2]*y[1], -p[1]*y[1]],
                     [0.0, 2*p[2]*y[1],0.0]])
        return J
        
    def fsens(t, y, s, p):
        J = N.array([[-p[0], p[1]*y[2], p[1]*y[1]],
                     [p[0], -p[1]*y[2]-2*p[2]*y[1], -p[1]*y[1]],
                     [0.0, 2*p[2]*y[1],0.0]])
        P = N.array([[-y[0],y[1]*y[2],0],
                     [y[0], -y[1]*y[2], -y[1]**2],
                     [0,0,y[1]**2]])
        return J.dot(s)+P
    
    #The initial conditions
    y0 = [1.0,0.0,0.0]          #Initial conditions for y
    
    #Create an Assimulo explicit problem
    exp_mod = Explicit_Problem(f,y0, name='Robertson Chemical Kinetics Example')
    exp_mod.rhs_sens = fsens
    exp_mod.jac = jac
    
    #Sets the options to the problem
    exp_mod.p0 = [0.040, 1.0e4, 3.0e7]  #Initial conditions for parameters
    exp_mod.pbar = [0.040, 1.0e4, 3.0e7]

    #Create an Assimulo explicit solver (CVode)
    exp_sim = CVode(exp_mod)
    
    #Sets the solver paramters
    exp_sim.iter = 'Newton'
    exp_sim.discr = 'BDF'
    exp_sim.rtol = 1.e-4
    exp_sim.atol = N.array([1.0e-8, 1.0e-14, 1.0e-6])
    exp_sim.sensmethod = 'SIMULTANEOUS' #Defines the sensitvity method used
    exp_sim.suppress_sens = False       #Dont suppress the sensitivity variables in the error test.
    exp_sim.report_continuously = True

    #Simulate
    t, y = exp_sim.simulate(4,400) #Simulate 4 seconds with 400 communication points
    
    #Basic test
    nose.tools.assert_almost_equal(y[-1][0], 9.05518032e-01, 4)
    nose.tools.assert_almost_equal(y[-1][1], 2.24046805e-05, 4)
    nose.tools.assert_almost_equal(y[-1][2], 9.44595637e-02, 4)
    nose.tools.assert_almost_equal(exp_sim.p_sol[0][-1][0], -1.8761, 2) #Values taken from the example in Sundials
    nose.tools.assert_almost_equal(exp_sim.p_sol[1][-1][0], 2.9614e-06, 8)
    nose.tools.assert_almost_equal(exp_sim.p_sol[2][-1][0], -4.9334e-10, 12)
    
    #Plot
    if with_plots:
        P.plot(t, y)
        P.title(exp_mod.name)
        P.xlabel('Time')
        P.ylabel('State')
        P.show()  
        
    return exp_mod, exp_sim
Esempio n. 12
0
def run_example(with_plots=True):
    r"""
    Example for demonstrating the use of a user supplied Jacobian (sparse).
    Note that this will only work if Assimulo has been configured with
    Sundials + SuperLU. Based on the SUNDIALS example cvRoberts_sps.c
    
    ODE:
    
    .. math::
       
       \dot y_1 &= -0.04y_1 + 1e4 y_2 y_3 \\
       \dot y_2 &= - \dot y_1 - \dot y_3 \\
       \dot y_3 &= 3e7 y_2^2
       
    
    on return:
    
       - :dfn:`exp_mod`    problem instance
    
       - :dfn:`exp_sim`    solver instance
       
    """

    #Defines the rhs
    def f(t, y):
        yd_0 = -0.04 * y[0] + 1e4 * y[1] * y[2]
        yd_2 = 3e7 * y[1] * y[1]
        yd_1 = -yd_0 - yd_2
        return N.array([yd_0, yd_1, yd_2])

    #Defines the Jacobian
    def jac(t, y):

        colptrs = [0, 3, 6, 9]
        rowvals = [0, 1, 2, 0, 1, 2, 0, 1, 2]
        data = [
            -0.04, 0.04, 0.0, 1e4 * y[2], -1e4 * y[2] - 6e7 * y[1], 6e7 * y[1],
            1e4 * y[1], -1e4 * y[1], 0.0
        ]

        J = SP.csc_matrix((data, rowvals, colptrs))
        return J

    #Defines an Assimulo explicit problem
    y0 = [1.0, 0.0, 0.0]  #Initial conditions

    exp_mod = Explicit_Problem(f,
                               y0,
                               name='Example using analytic (sparse) Jacobian')

    exp_mod.jac = jac  #Sets the Jacobian
    exp_mod.jac_nnz = 9

    exp_sim = CVode(exp_mod)  #Create a CVode solver

    #Set the parameters
    exp_sim.iter = 'Newton'  #Default 'FixedPoint'
    exp_sim.discr = 'BDF'  #Default 'Adams'
    exp_sim.atol = [1e-8, 1e-14, 1e-6]  #Default 1e-6
    exp_sim.rtol = 1e-4  #Default 1e-6
    exp_sim.linear_solver = "sparse"

    #Simulate
    t, y = exp_sim.simulate(0.4)  #Simulate 0.4 seconds

    #Basic tests
    nose.tools.assert_almost_equal(y[-1][0], 0.9851, 3)

    #Plot
    if with_plots:
        P.plot(t, y[:, 1], linestyle="dashed", marker="o")  #Plot the solution
        P.xlabel('Time')
        P.ylabel('State')
        P.title(exp_mod.name)
        P.show()

    return exp_mod, exp_sim
Esempio n. 13
0
def run_simulation(filename, start_time, save_output, temp, RH, RO2_indices,
                   H2O, input_dict, simulation_time, batch_step):

    from assimulo.solvers import RodasODE, CVode  #Choose solver accoring to your need.
    from assimulo.problem import Explicit_Problem

    # In this function, we import functions that have been pre-compiled for use in the ODE solver
    # The function that calculates the RHS of the ODE is also defined within this function, such
    # that it can be used by the Assimulo solvers

    # The variables passed to this function are defined as follows:

    #-------------------------------------------------------------------------------------
    # define the ODE function to be called
    def dydt_func(t, y):
        """
        This function defines the right-hand side [RHS] of the ordinary differential equations [ODEs] to be solved
        input:
        • t - time variable [internal to solver]
        • y - array holding concentrations of all compounds in both gas and particulate [molecules/cc]
        output:
        dydt - the dy_dt of each compound in both gas and particulate phase [molecules/cc.sec]
        """

        #pdb.set_trace()
        # Calculate time of day
        time_of_day_seconds = start_time + t

        # make sure the y array is not a list. Assimulo uses lists
        y_asnumpy = numpy.array(y)

        #Calculate the concentration of RO2 species, using an index file created during parsing
        RO2 = numpy.sum(y[RO2_indices])

        #Calculate reaction rate for each equation.
        # Note that H2O will change in parcel mode
        # The time_of_day_seconds is used for photolysis rates - need to change this if want constant values
        rates = evaluate_rates_fortran(RO2, H2O, temp, time_of_day_seconds)
        #pdb.set_trace()
        # Calculate product of all reactants and stochiometry for each reaction [A^a*B^b etc]
        reactants = reactants_fortran(y_asnumpy)
        #pdb.set_trace()
        #Multiply product of reactants with rate coefficient to get reaction rate
        reactants = numpy.multiply(reactants, rates)
        #pdb.set_trace()
        # Now use reaction rates with the loss_gain matri to calculate the final dydt for each compound
        # With the assimulo solvers we need to output numpy arrays
        dydt = loss_gain_fortran(reactants)
        #pdb.set_trace()

        return dydt

    #-------------------------------------------------------------------------------------
    #-------------------------------------------------------------------------------------
    # define jacobian function to be called
    def jacobian(t, y):
        """
        This function defines Jacobian of the ordinary differential equations [ODEs] to be solved
        input:
        • t - time variable [internal to solver]
        • y - array holding concentrations of all compounds in both gas and particulate [molecules/cc]
        output:
        dydt_dydt - the N_compounds x N_compounds matrix of Jacobian values
        """

        # Different solvers might call jacobian at different stages, so we have to redo some calculations here
        # Calculate time of day
        time_of_day_seconds = start_time + t

        # make sure the y array is not a list. Assimulo uses lists
        y_asnumpy = numpy.array(y)

        #Calculate the concentration of RO2 species, using an index file created during parsing
        RO2 = numpy.sum(y[RO2_indices])

        #Calculate reaction rate for each equation.
        # Note that H2O will change in parcel mode
        rates = evaluate_rates_fortran(RO2, H2O, temp, time_of_day_seconds)
        #pdb.set_trace()
        # Now use reaction rates with the loss_gain matrix to calculate the final dydt for each compound
        # With the assimulo solvers we need to output numpy arrays
        dydt_dydt = jacobian_fortran(rates, y_asnumpy)
        #pdb.set_trace()
        return dydt_dydt

    #-------------------------------------------------------------------------------------

    #import static compilation of Fortran functions for use in ODE solver
    print("Importing pre-compiled Fortran modules")
    from rate_coeff_f2py import evaluate_rates as evaluate_rates_fortran
    from reactants_conc_f2py import reactants as reactants_fortran
    from loss_gain_f2py import loss_gain as loss_gain_fortran
    from jacobian_f2py import jacobian as jacobian_fortran

    # 'Unpack' variables from input_dict
    species_dict = input_dict['species_dict']
    species_dict2array = input_dict['species_dict2array']
    species_initial_conc = input_dict['species_initial_conc']
    equations = input_dict['equations']

    #Specify some starting concentrations [ppt]
    Cfactor = 2.55e+10  #ppb-to-molecules/cc

    # Create variables required to initialise ODE
    num_species = len(species_dict.keys())
    y0 = [0] * num_species  #Initial concentrations, set to 0
    t0 = 0.0  #T0

    # Define species concentrations in ppb
    # You have already set this in the front end script, and now we populate the y array with those concentrations
    for specie in species_initial_conc.keys():
        y0[species_dict2array[specie]] = species_initial_conc[
            specie] * Cfactor  #convert from pbb to molcules/cc

    #Set the total_time of the simulation to 0 [havent done anything yet]
    total_time = 0.0

    # Now run through the simulation in batches.
    # I do this to enable testing of coupling processes. Some initial investigations with non-ideality in
    # the condensed phase indicated that even defining a maximum step was not enough for ODE solvers to
    # overshoot a stable region. It also helps with in-simulation debugging. Its up to you if you want to keep this.
    # To not run in batches, just define one batch as your total simulation time. This will reduce any overhead with
    # initialising the solvers
    # Set total simulation time and batch steps in seconds

    # Note also that the current module outputs solver information after each batch step. This can be turned off and the
    # the batch step change for increased speed
    #simulation_time= 3600.0
    #batch_step=100.0
    t_array = []
    time_step = 0
    number_steps = int(
        simulation_time /
        batch_step)  # Just cycling through 3 steps to get to a solution

    # Define a matrix that stores values as outputs from the end of each batch step. Again, you can remove
    # the need to run in batches. You can tell the Assimulo solvers the frequency of outputs.
    y_matrix = numpy.zeros((int(number_steps), len(y0)))

    print("Starting simulation")

    # In the following, we can
    while total_time < simulation_time:

        if total_time == 0.0:
            #Define an Assimulo problem
            #Define an explicit solver
            exp_mod = Explicit_Problem(dydt_func, y0, t0, name=filename)

        else:
            y0 = y_output[
                -1, :]  # Take the output from the last batch as the start of this
            exp_mod = Explicit_Problem(dydt_func, y0, t0, name=filename)

        # Define ODE parameters.
        # Initial steps might be slower than mid-simulation. It varies.
        #exp_mod.jac = dydt_jac
        exp_mod.jac = jacobian
        # Define which ODE solver you want to use
        exp_sim = CVode(exp_mod)
        tol_list = [1.0e-3] * num_species
        exp_sim.atol = tol_list  #Default 1e-6
        exp_sim.rtol = 1e-6  #Default 1e-6
        exp_sim.inith = 1.0e-6  #Initial step-size
        #exp_sim.discr = 'Adams'
        exp_sim.maxh = 100.0
        # Use of a jacobian makes a big differece in simulation time. This is relatively
        # easy to define for a gas phase - not sure for an aerosol phase with composition
        # dependent processes.
        exp_sim.usejac = True  # To be provided as an option in future update.
        #exp_sim.fac1 = 0.05
        #exp_sim.fac2 = 50.0
        exp_sim.report_continuously = True
        exp_sim.maxncf = 1000
        #Sets the parameters
        t_output, y_output = exp_sim.simulate(
            batch_step)  #Simulate 'batch' seconds
        total_time += batch_step
        t_array.append(
            total_time
        )  # Save the output from the end step, of the current batch, to a matrix
        y_matrix[time_step, :] = y_output[-1, :]

        #now save this information into a matrix for later plotting.
        time_step += 1

    # Do you want to save the generated matrix of outputs?
    if save_output:
        numpy.save(filename + '_output', y_matrix)
        df = pd.DataFrame(y_matrix)
        df.to_csv(filename + "_output_matrix.csv")
        w = csv.writer(open(filename + "_output_names.csv", "w"))
        for specie, number in species_dict2array.items():
            w.writerow([specie, number])

    with_plots = True

    #pdb.set_trace()
    #Plot the change in concentration over time for a given specie. For the user to change / remove
    #In a future release I will add this as a seperate module
    if with_plots:

        try:
            plt.plot(t_array,
                     numpy.log10(y_matrix[:, species_dict2array['APINENE']]),
                     marker='o',
                     label="APINENE")
            plt.plot(t_array,
                     numpy.log10(y_matrix[:, species_dict2array['PINONIC']]),
                     marker='o',
                     label="PINONIC")
            plt.title(exp_mod.name)
            plt.legend(loc='upper left')
            plt.ylabel("Concetration log10[molecules/cc]")
            plt.xlabel("Time [seconds] since start of simulation")
            plt.show()
        except:
            print(
                "There is a problem using Matplotlib in your environment. If using this within a docker container, you will need to transfer the data to the host or configure your container to enable graphical displays. More information can be found at http://wiki.ros.org/docker/Tutorials/GUI "
            )
Esempio n. 14
0
def ode_solv(y, integ_step, rindx, pindx, rstoi, pstoi, nreac, nprod, rrc,
             jac_stoi, njac, jac_den_indx, jac_indx, Cinfl_now, y_arr, y_rind,
             uni_y_rind, y_pind, uni_y_pind, reac_col, prod_col, rstoi_flat,
             pstoi_flat, rr_arr, rr_arr_p, rowvals, colptrs, num_comp, num_sb,
             wall_on, Psat, Cw, act_coeff, kw, jac_wall_indx, seedi, core_diss,
             kelv_fac, kimt, num_asb, jac_part_indx, rindx_aq, pindx_aq,
             rstoi_aq, pstoi_aq, nreac_aq, nprod_aq, jac_stoi_aq, njac_aq,
             jac_den_indx_aq, jac_indx_aq, y_arr_aq, y_rind_aq, uni_y_rind_aq,
             y_pind_aq, uni_y_pind_aq, reac_col_aq, prod_col_aq, rstoi_flat_aq,
             pstoi_flat_aq, rr_arr_aq, rr_arr_p_aq, eqn_num):

    # inputs: -------------------------------------
    # y - initial concentrations (moleucles/cm3)
    # integ_step - the maximum integration time step (s)
    # rindx - index of reactants per equation
    # pindx - index of products per equation
    # rstoi - stoichiometry of reactants
    # pstoi - stoichiometry of products
    # nreac - number of reactants per equation
    # nprod - number of products per equation
    # rrc - reaction rate coefficient
    # jac_stoi - stoichiometries relevant to Jacobian
    # njac - number of Jacobian elements affected per equation
    # jac_den_indx - index of component denominators for Jacobian
    # jac_indx - index of Jacobian to place elements per equation (rows)
    # Cinfl_now - influx of components with constant influx
    #		(molecules/cc/s)
    # y_arr - index for matrix used to arrange concentrations,
    #	enabling calculation of reaction rate coefficients
    # y_rind - index of y relating to reactants for reaction rate
    # 	coefficient equation
    # uni_y_rind - unique index of reactants
    # y_pind - index of y relating to products
    # uni_y_pind - unique index of products
    # reac_col - column indices for sparse matrix of reaction losses
    # prod_col - column indices for sparse matrix of production gains
    # rstoi_flat - 1D array of reactant stoichiometries per equation
    # pstoi_flat - 1D array of product stoichiometries per equation
    # rr_arr - index for reaction rates to allow reactant loss
    # 	calculation
    # rr_arr_p - index for reaction rates to allow reactant loss
    # 	calculation
    # rowvals - row indices of Jacobian elements
    # colptrs - indices of  rowvals corresponding to each column of the
    # 	Jacobian
    # num_comp - number of components
    # num_sb - number of size bins
    # wall_on - flag saying whether to include wall partitioning
    # Psat - pure component saturation vapour pressures (molecules/cc)
    # Cw - effective absorbing mass concentration of wall (molecules/cc)
    # act_coeff - activity coefficient of components
    # kw - mass transfer coefficient to wall (/s)
    # jac_wall_indx - index of inputs to Jacobian by wall partitioning
    # seedi - index of seed material
    # core_diss - dissociation constant of seed material
    # kelv_fac - kelvin factor for particles
    # kimt - mass transfer coefficient for gas-particle partitioning (s)
    # num_asb - number of actual size bins (excluding wall)
    # jac_part_indx - index for sparse Jacobian for particle influence
    # eqn_num - number of gas- and aqueous-phase reactions
    # ---------------------------------------------

    def dydt(t, y):  # define the ODE(s)

        # empty array to hold rate of change per component
        dd = np.zeros((len(y)))

        # gas-phase reactions -------------------------
        # empty array to hold relevant concentrations for
        # reaction rate coefficient calculation
        rrc_y = np.ones((rindx.shape[0] * rindx.shape[1]))
        rrc_y[y_arr] = y[y_rind]
        rrc_y = rrc_y.reshape(rindx.shape[0], rindx.shape[1], order='C')
        # reaction rate (molecules/cc/s)
        rr = rrc[0:rindx.shape[0]] * ((rrc_y**rstoi).prod(axis=1))
        # loss of reactants
        data = rr[rr_arr] * rstoi_flat  # prepare loss values
        # convert to sparse matrix
        loss = SP.csc_matrix((data, y_rind, reac_col))
        # register loss of reactants
        dd[uni_y_rind] -= np.array((loss.sum(axis=1))[uni_y_rind])[:, 0]
        # gain of products
        data = rr[rr_arr_p] * pstoi_flat  # prepare loss values
        # convert to sparse matrix
        loss = SP.csc_matrix((data, y_pind, prod_col))
        # register gain of products
        dd[uni_y_pind] += np.array((loss.sum(axis=1))[uni_y_pind])[:, 0]

        # particle-phase reactions -------------------------
        if (eqn_num[1] > 0):
            # empty array to hold relevant concentrations for
            # reaction rate coefficient calculation
            # tile aqueous-phase reaction rate coefficients
            rr_aq = np.tile(rrc[rindx.shape[0]::], num_asb)
            # prepare aqueous-phase concentrations
            rrc_y = np.ones((rindx_aq.shape[0] * rindx_aq.shape[1]))
            rrc_y[y_arr_aq] = y[y_rind_aq]
            rrc_y = rrc_y.reshape(rindx_aq.shape[0],
                                  rindx_aq.shape[1],
                                  order='C')
            # reaction rate (molecules/cc/s)
            rr = rr_aq * ((rrc_y**rstoi_aq).prod(axis=1))
            # loss of reactants
            data = rr[rr_arr_aq] * rstoi_flat_aq  # prepare loss values
            # convert to sparse matrix
            loss = SP.csc_matrix((data[0, :], y_rind_aq, reac_col_aq))
            # register loss of reactants
            dd[uni_y_rind_aq] -= np.array((loss.sum(axis=1))[uni_y_rind_aq])[:,
                                                                             0]
            # gain of products
            data = rr[rr_arr_p_aq] * pstoi_flat_aq  # prepare loss values
            # convert to sparse matrix
            loss = SP.csc_matrix((data[0, :], y_pind_aq, prod_col_aq))
            # register gain of products
            dd[uni_y_pind_aq] += np.array((loss.sum(axis=1))[uni_y_pind_aq])[:,
                                                                             0]

        # gas-particle partitioning-----------------
        # transform particle phase concentrations into
        # size bins in rows, components in columns
        ymat = (y[num_comp:num_comp * (num_asb + 1)]).reshape(
            num_asb, num_comp)
        # total particle-phase concentration per size bin (molecules/cc (air))
        csum = ((ymat.sum(axis=1) - ymat[:, seedi].sum(axis=1)) + (
            (ymat[:, seedi] * core_diss).sum(axis=1)).reshape(-1)).reshape(
                -1, 1)
        # size bins with contents
        isb = (csum[:, 0] > 0.)

        # container for gas-phase concentrations at particle surface
        Csit = np.zeros((num_asb, num_comp))
        # mole fraction of components at particle surface
        Csit[isb, :] = (ymat[isb, :] / csum[isb, :])
        # gas-phase concentration of components at
        # particle surface (molecules/cc (air))
        Csit[isb, :] = Csit[isb, :] * Psat[isb, :] * kelv_fac[isb] * act_coeff[
            isb, :]
        # partitioning rate (molecules/cc/s)
        dd_all = kimt * (y[0:num_comp].reshape(1, -1) - Csit)
        # gas-phase change
        dd[0:num_comp] -= dd_all.sum(axis=0)
        # particle change
        dd[num_comp:num_comp * (num_asb + 1)] += (dd_all.flatten())

        # gas-wall partitioning ----------------
        # concentration on wall (molecules/cc (air))
        Csit = y[num_comp * num_sb:num_comp * (num_sb + 1)]
        # saturation vapour pressure on wall (molecules/cc (air))
        # note, just using the top rows of Psat and act_coeff
        # as do not need the repetitions over size bins
        if (Cw > 0.):
            Csit = Psat[0, :] * (Csit / Cw) * act_coeff[0, :]
            # rate of transfer (molecules/cc/s)
            dd_all = kw * (y[0:num_comp] - Csit)
            dd[0:num_comp] -= dd_all  # gas-phase change
            dd[num_comp * num_sb:num_comp *
               (num_sb + 1)] += dd_all  # wall change

        return (dd)

    def jac(t, y):  # define the Jacobian
        # elements of sparse Jacobian matrix
        data = np.zeros((94))

        for i in range(rindx.shape[0]):  # gas-phase reaction loop
            # reaction rate (molecules/cc/s)
            rr = rrc[i] * (y[rindx[i, 0:nreac[i]]].prod())
            # prepare Jacobian inputs
            jac_coeff = np.zeros((njac[i, 0]))
            # only fill Jacobian if reaction rate sufficient
            if (rr != 0.):
                jac_coeff = (rr * (jac_stoi[i, 0:njac[i, 0]]) /
                             (y[jac_den_indx[i, 0:njac[i, 0]]]))
            data[jac_indx[i, 0:njac[i, 0]]] += jac_coeff

        n_aqr = nreac_aq.shape[0]  # number of aqueous-phase reactions
        aqi = 0  # aqueous-phase reaction counter
        for i in range(rindx.shape[0],
                       rrc.shape[0]):  # aqueous-phase reaction loop
            # reaction rate (molecules/cc/s)
            rr = rrc[i] * (y[rindx_aq[aqi::n_aqr,
                                      0:nreac_aq[aqi]]].prod(axis=1))
            # spread along affected components
            rr = rr.reshape(-1, 1)
            rr = (np.tile(rr, int(njac_aq[aqi, 0] /
                                  (num_sb - wall_on)))).flatten(order='C')
            # prepare Jacobian inputs
            jac_coeff = np.zeros((njac_aq[aqi, 0]))
            nzi = (rr != 0)
            jac_coeff[nzi] = (
                rr[nzi] * ((jac_stoi_aq[aqi, 0:njac_aq[aqi, 0]])[nzi]) /
                ((y[jac_den_indx_aq[aqi, 0:njac_aq[aqi, 0]]])[nzi]))
            # stack size bins
            jac_coeff = jac_coeff.reshape(int(num_sb - wall_on),
                                          int(njac_aq[aqi, 0] /
                                              (num_sb - wall_on)),
                                          order='C')
            data[jac_indx_aq[aqi::n_aqr,
                             0:(int(njac_aq[aqi, 0] /
                                    (num_sb - wall_on)))]] += jac_coeff
            aqi += 1

        # gas-particle partitioning
        part_eff = np.zeros((56))
        part_eff[0:24:3] = -kimt.sum(axis=0)  # effect of gas on gas

        # transform particle phase concentrations into
        # size bins in rows, components in columns
        ymat = (y[num_comp:num_comp * (num_asb + 1)]).reshape(
            num_asb, num_comp)
        # total particle-phase concentration per size bin (molecules/cc (air))
        csum = ymat.sum(axis=1) - ymat[:, seedi].sum(
            axis=1) + (ymat[:, seedi] * core_diss).sum(axis=1)

        # effect of particle on gas
        for isb in range(int(num_asb)):  # size bin loop
            if csum[isb] > 0:  # if particles present
                # effect of gas on particle
                part_eff[1 + isb:num_comp * (num_asb + 1):num_asb +
                         1] = +kimt[isb, :]
                # start and finish index
                sti = int((num_asb + 1) * num_comp + isb * (num_comp * 2))
                fii = int(sti + (num_comp * 2.))
                # diagonal index
                diag_indxg = sti + np.arange(0, num_comp * 2, 2).astype('int')
                diag_indxp = sti + np.arange(1, num_comp * 2, 2).astype('int')
                # prepare for diagonal (component effect on itself)
                diag = kimt[isb, :] * Psat[0, :] * act_coeff[0, :] * kelv_fac[
                    isb, 0] * (csum[isb] - ymat[isb, :]) / (csum[isb]**2.)
                # implement to part_eff
                part_eff[diag_indxg] = +diag
                part_eff[diag_indxp] = -diag

        data[jac_part_indx] += part_eff

        if (Cw > 0.):
            wall_eff = np.zeros((32))
            wall_eff[0:16:2] = -kw  # effect of gas on gas
            wall_eff[1:16:2] = +kw  # effect of gas on wall
            # effect of wall on gas
            wall_eff[16:32:2] = +kw * (Psat[0, :] * act_coeff[0, :] / Cw)
            # effect of wall on wall
            wall_eff[16 + 1:32:2] = -kw * (Psat[0, :] * act_coeff[0, :] / Cw)
            data[jac_wall_indx] += wall_eff

        # create Jacobian
        j = SP.csc_matrix((data, rowvals, colptrs))
        return (j)

    mod = Explicit_Problem(dydt, y)  # instantiate solver
    mod.jac = jac  # set the Jacobian
    mod_sim = CVode(mod)  # define a solver instance
    # there is a general trend of an inverse relationship
    # between tolerances and computation time
    mod_sim.atol = 0.001
    mod_sim.rtol = 0.0001
    # integration approach (backward differentiation formula)
    mod_sim.discr = 'BDF'
    # call solver
    res_t, res = mod_sim.simulate(integ_step)
    # return concentrations following integration
    return (res, res_t)