コード例 #1
0
def compute_fixedpoint_numerically(srm, t0, x0, parameter_dict, func_set):
    B_sym = srm.compartmental_matrix
    u_sym = srm.external_inputs

    t = srm.time_symbol

    tup = tuple(srm.state_vector) + (t,)
    u_func_non_lin = numerical_function_from_expression(
        u_sym,
        tup,
        parameter_dict,
        func_set
    )
    B_func_non_lin = numerical_function_from_expression(
        B_sym,
        tup,
        parameter_dict,
        func_set
    )

    # get functions of x1,...,xn by partly applying to t0
    B0_func = func_subs(t, Function("B")(*tup), B_func_non_lin, t0)
    u0_func = func_subs(t, Function("u")(*tup), u_func_non_lin, t0)

    # build the kind of function that scipy.optimize.root expects
    # it has to have a single vector like argument
    def ex_func(x):
        tup = tuple(x)
        return B0_func(*tup) @ x + u0_func(*tup).reshape(x.shape)
    res = root(fun=ex_func, jac=False, x0=x0)
    assert(res.success)
    # chose a method that does not use the jacobian
#    res = root(fun=ex_func,method='krylov',x0=x0,tol=1e-5)
#    pe('res',locals())
    return res.x
コード例 #2
0
    def test_numerical_function_from_expression(self):
        C_0, C_1, C_2 = symbols('C_0 C_1 C_2')
        t = Symbol('t')
        u_0_sym = Function('u_0')
        u_2_sym = Function('u_2')

        u_0_expr = u_0_sym(C_0, C_1, t)
        u_2_expr = u_2_sym(t)

        X = Matrix([C_0, C_1, C_2])
        t_min, t_max = 0, 10
        symbolic_input_fluxes = {0: u_0_expr, 2: u_2_expr}

        def u0_func(C_0_val, C_1_val, t_val):
            return C_0_val + C_1_val + t_val

        def u2_func(t_val):
            return t_val

        parameter_dict = {}
        func_set = {u_0_expr: u0_func, u_2_expr: u2_func}

        tup = (C_0, C_1) + (t, )
        u_0_func = numerical_function_from_expression(u_0_expr, tup,
                                                      parameter_dict, func_set)
        self.assertEqual(u_0_func(1, 2, 3), 1 + 2 + 3)

        tup = (t, )
        u_2_func = numerical_function_from_expression(u_2_expr, tup,
                                                      parameter_dict, func_set)
        self.assertEqual(u_2_func(2), 2)

        # wrong tup: C_1 is not necessary but it does not hurt
        # this behavior is convinient to make everything a variable of
        # ALL statevariables and time
        tup = (
            C_1,
            t,
        )
        u_2_func = numerical_function_from_expression(u_2_expr, tup,
                                                      parameter_dict, func_set)
        # the superflous first argument just does not have any influence
        self.assertEqual(u_2_func(1002103413131, 2), 2)

        # wrong tup: C_0 is missing but necessary
        # this is a real error
        tup = (C_1, t)
        with self.assertRaises(Exception) as e:
            u_0_func = numerical_function_from_expression(
                u_0_expr, tup, parameter_dict, func_set)
コード例 #3
0
ファイル: createModel.py プロジェクト: MPIBGC-TEE/bgc_md2
def numfunc(expr):
    return hr.numerical_function_from_expression(
    expr=expr,
    tup=(mvs.get_TimeSymbol(), *mvs.get_StateVariableTuple()),
    parameter_dict=par_dict,
    func_set=func_dict
)
コード例 #4
0
    def _flux_funcs(self, expr_dict):
        bm = self.bm
        srm = bm.srm
        sol_funcs = self.sol_funcs()
        flux_funcs = {}
        tup = tuple(bm.state_vector) + (bm.time_symbol, )

        for key, expr in expr_dict.items():
            if isinstance(expr, Number):
                # if expr is a number like 5.1 lambdify does not create a vectorized function
                # so the output is always a number and not an array with identical exprs which is a problem in plots
                def expr_func(arg_arr):
                    return expr * np.ones_like(arg_arr)

                flux_funcs[key] = expr_func

            else:
                ol = numerical_function_from_expression(
                    expr, tup, self.par_dict, self.func_dict)
                flux_funcs[key] = f_of_t_maker(sol_funcs, ol)

        return flux_funcs
コード例 #5
0
ファイル: ageComputation.py プロジェクト: MPIBGC-TEE/bgc_md2
alpha = 1
rho = 0.65
f_i = 1
#x_a = start_year*exp(0.0305*time_symbol)/(start_year+exp(0.0305*time_symbol)-1)+284
x_a = start_year * exp(0.0305 * (time_symbol - start_year)) / (
    start_year + exp(0.0305 * (time_symbol - start_year)) - 1) + 284
T_s = T_s0 + sigma / log(2) * log(x_a / 285)
Gamma = 42.7 + 1.68 * (T_s - 25) + 0.012 * (T_s - 25)**2
beta = 3 * rho * x_a * Gamma / ((rho * x_a - Gamma) * (rho * x_a + 2 * Gamma))
s_i = f_i * alpha * s_0 * (1 + 2.5 * beta * log(x_a / 285))

gpp_industrial = G_emanuel * s_i

u_func_numerical = numerical_function_from_expression(
    gpp_industrial,  # sympy expression
    (time_symbol, ),  # tuple of symbols to be replaced by numerical values
    {},  # parameter dict
    {}  # func dict
)

xi_b = 2
xi_t = xi_b**(0.1 * T_s - 1.5)

xi_func_numerical = numerical_function_from_expression(
    xi_t,  # sympy expression
    (time_symbol, ),  # tuple of symbols to be replaced by numerical values
    {},  # parameter dict
    {}  # func dict
)

# define a dictionary to connect the symbols with the according functions
func_set = {xi: xi_func_numerical, G: u_func_numerical}
コード例 #6
0
def test_stateTransitionOperator_by_different_methods():
    # The state transition operator Phi can be used to reproduce the solution
    k_0_val = 1
    k_1_val = 2
    x0_0 = np.float(0.5)
    x0_1 = np.float(1.5)
    delta_t = np.float(1. / 4.)
    #
    var(["x_0", "x_1", "k_0", "k_1", "t", "u"])
    #
    inputs = {0: u, 1: u * t}
    outputs = {0: k_0 * x_0**2, 1: k_1 * x_1}
    internal_fluxes = {}
    svec = Matrix([x_0, x_1])
    srm = SmoothReservoirModel(state_vector=svec,
                               time_symbol=t,
                               input_fluxes=inputs,
                               output_fluxes=outputs,
                               internal_fluxes=internal_fluxes)
    t_0 = 0
    t_max = 4
    nt = 5
    times = np.linspace(t_0, t_max, nt)
    double_times = np.linspace(t_0, t_max, 2 * (nt - 1) + 1)
    quad_times = np.linspace(t_0, t_max, 4 * (nt - 1) + 1)
    parameter_dict = {k_0: k_0_val, k_1: k_1_val, u: 1}
    func_dict = {}
    start_x = np.array([x0_0, x0_1])  #make it a column vector for later use
    #create the model run
    smr = SmoothModelRun(model=srm,
                         parameter_dict=parameter_dict,
                         start_values=start_x,
                         times=times,
                         func_set=func_dict)
    smr.build_state_transition_operator_cache(size=4)
    nr_pools = smr.nr_pools

    # to be able to compare the results we have to compute them for a
    # set of n linear independent vectors
    def baseVector(i):
        e_i = np.zeros((nr_pools, 1))
        e_i[i] = 1
        return e_i

    bvs = [baseVector(i) for i in range(nr_pools)]
    #pe('Phi_skew(2,1,bvs[0])',locals())
    #raise

    test_times = np.linspace(t_0, t_max, 11)

    # We now rebuild the solution by means of phi and plot it along with the original solution
    original_sol, sol_func = smr.solve()

    u_sym = srm.external_inputs
    u_num = numerical_function_from_expression(u_sym, (t, ), parameter_dict,
                                               {})

    def vectorlist2array(l):
        return np.stack([vec.flatten() for vec in l], 1)


#    def lists_dict2array_dict(d):
#        return {key:vectorlist2array(val) for key,val in d.items()}
#

    def continiuous_integral_values(integrator, times):
        start = time.time()
        res = vectorlist2array([
            integrator(
                lambda tau: smr._state_transition_operator(t, tau, u_num(tau)),
                t_0, t) for t in times
        ])
        stop = time.time()
        exec_time = stop - start
        #pe('exec_time',locals())
        return (times, res, exec_time)

    def discrete_integral_values(integrator, times):
        start = time.time()
        res = vectorlist2array([
            integrator(
                lambda tau: smr._state_transition_operator(t, tau, u_num(tau)),
                taus=+times[0:i + 1]) for i, t in enumerate(times)
        ])
        stop = time.time()
        exec_time = stop - start
        #pe('exec_time',locals())
        return (times, res, exec_time)

    ## reconstruct the solution with Phi and the integrand
    # x_t=Phi(t,t0)*x_0+int_t0^t Phi(tau,t0)*u(tau) dtau
    # x_t=a(t)+b(t)
    et = bvs[0] + bvs[1]
    phi_arrays = {
        'skew':
        (times,
         vectorlist2array([
             smr._state_transition_operator(t, t_0,
                                            et).reshape(srm.nr_pools, 1)
             for t in times
         ]))
    }

    a_arrays = {
        'skew':
        (times,
         vectorlist2array([
             smr._state_transition_operator(t, t_0,
                                            start_x).reshape(srm.nr_pools, 1)
             for t in times
         ])),
        'trapez1':
        (times,
         vectorlist2array([
             smr._state_transition_operator(t, t_0,
                                            start_x).reshape(srm.nr_pools, 1)
             for t in times
         ])),
        'trapez2':
        (double_times,
         vectorlist2array([
             smr._state_transition_operator(t, t_0,
                                            start_x).reshape(srm.nr_pools, 1)
             for t in double_times
         ])),
        'trapez4':
        (quad_times,
         vectorlist2array([
             smr._state_transition_operator(t, t_0,
                                            start_x).reshape(srm.nr_pools, 1)
             for t in quad_times
         ]))
    }
    nested_boundary_tuples = [(0, t) for t in reversed(times)]

    b_arrays_trapez = {}
    b_arrays = {
        'skew':
        continiuous_integral_values(array_integration_by_ode, times),
        'trapez1':
        discrete_integral_values(array_integration_by_values, times),
        'trapez2':
        discrete_integral_values(array_integration_by_values, double_times),
        'trapez4':
        discrete_integral_values(array_integration_by_values, quad_times)
    }

    b_arrays_quad = {
        'skew': continiuous_integral_values(array_quad_result, times)
    }

    x_arrays = {
        key: (a_arrays[key][0], a_arrays[key][1] + b_arrays[key][1])
        for key in a_arrays.keys()
    }
    #x_arrays['trapez']=(times,a_arrays['skew'][1]+b_arrays['trapez'][1])

    styleDict = OrderedDict({
        'skew': ('green', 6),
        'trapez1': ('black', 4),
        'trapez2': ('blue', 4),
        'trapez4': ('brown', 2)
    })

    def plot_comparison(axl, axr, d):
        for key in styleDict.keys():
            if key in d.keys():
                val = d[key]
                if len(val) == 3:
                    time = "{:7.1e}".format(val[2])
                else:
                    time = ""
                axl.plot(val[0],
                         val[1][0, :],
                         '+',
                         color=styleDict[key][0],
                         markersize=styleDict[key][1],
                         label=key + "[0]" + time)
                axr.plot(val[0],
                         val[1][1, :],
                         'x',
                         color=styleDict[key][0],
                         markersize=styleDict[key][1],
                         label=key + "[1]" + time)

    fig = plt.figure(figsize=(17, 27))
    rpn = 5
    cpn = 2
    r = 1
    axl = fig.add_subplot(rpn, cpn, r)
    plt.title("""phi components, nonlinear part of the system (x[0]) """)
    axr = fig.add_subplot(rpn, cpn, r + 1)
    plt.title("""phi components, linear part of the system (x[1]) """)
    plot_comparison(axl, axr, phi_arrays)
    axl.legend()

    r += cpn
    axl = fig.add_subplot(rpn, cpn, r)
    plt.title('''
    original solution and reconstruction via phi, 
    imprecise for trapez_rule and wrong for the old method
    ''')
    axr = fig.add_subplot(rpn, cpn, r + 1)
    axl.plot(times,
             original_sol[:, 0],
             'o',
             color='blue',
             label="original_sol[:,0]")
    axr.plot(times,
             original_sol[:, 1],
             'o',
             color='blue',
             label="original_sol[:,1]")

    plot_comparison(axl, axr, x_arrays)
    axl.legend()
    axr.legend()

    r += cpn
    axl = fig.add_subplot(rpn, cpn, r)
    plt.title('phi(t,ti-0) x0 ')
    axr = fig.add_subplot(rpn, cpn, r + 1)
    ax = fig.add_subplot(rpn, cpn, r)
    plot_comparison(axl, axr, a_arrays)
    axl.legend()
    axr.legend()

    r += cpn
    axl = fig.add_subplot(rpn, cpn, r)
    plt.title('\int_{t0}^t phi(tau,t) u(tau) d tau')
    axr = fig.add_subplot(rpn, cpn, r + 1)
    plot_comparison(axl, axr, b_arrays)
    axl.legend()
    axr.legend()

    #r+=cpn
    r += cpn
    axl = fig.add_subplot(rpn, cpn, r)
    plt.title('\int_{t0}^t phi(tau,t) u(tau) d tau by quad')
    axr = fig.add_subplot(rpn, cpn, r + 1)
    plot_comparison(axl, axr, b_arrays_quad)
    axl.legend()
    axr.legend()

    fig.savefig("solutions.pdf")
コード例 #7
0
def lapm_for_steady_state(srm, t0, parameter_dict, func_set, x0=None):
    """
    If a fixedpoint of the frozen system can be found, create a linear
    autonomous model as an equivalent for the frozen (generally nonlinear)
    system there.

    The function performs the following steps:

    #.  Substitute symbols and symbolic functions with the parameters and
        numeric functions.

    #.  Transform the general nonlinear non-autonomous system
        into a nonlinear autonomous system by freezing it
        at time :math:`t=t_0`:

    #.  Compute :math:`u_0(x)=u(t_0,x_0)` and :math:`B_0(x)=B(t_0,x_0)`

    #.  Look for an equilibrium :math:`x_{fix}` of the frozen system
        such that :math:`0=B_0(x_{fix})+u_0(x_{fix})`.
        If the frozen system is linear the we can compute
        the fixed point explicitly : :math:`x_{fix}=B_0^{-1}u_0`.
        In general the frozen system will be nonlinear and we will have to
        look for the fixed point numerically.

    #.  Create a linear autonomous pool model.
        that can be investigated with the
        package `LAPM <https://github.com/MPIBGC-TEE/LAPM>`_

        This is a special case of the general
        linearization of a nonlinear model along a trajectory,
        which in case of a fixed point is constant.
        At the fixed point the age distribution and solution of the
        nonlinear system are identical to those of a linear one.

    Args:
        srm (SmoothReservoirModel): The (symbolic) model
        par_set (dict):
            The parameter set that transforms the symbolic model into a
            numeric one.
            The keys are the sympy symbols, the values are the values used
            for the simulation.
        func_set (dict):
            The keys are the symbolic
            sympy expressions for external functions
            the values are the numeric functions to be used in the simulation.
        t0 (float): The time where the non-autonomous system is frozen.

        x0 (numpy.ndarray):
            An initial guess to start the fixed point iteration.
            If the frozen model is linear it will be ignored and
            can therefore be omitted.

    Returns:
        (lapm, x_fix)  (tuple):
        lapm is an instance of
        (:class:`LAPM.linear_autonomous_pool_model.LinearAutonomousPoolModel`)
        representing the linearization with
        respect to the (constant) fixed point trajectory. It yields the
        same age distribution as the frozen possibly nonlinear system at
        the fixed point.
        :math:`x_{fix}` is a one dimensional vector representing the
        equilibrium.
        This is returned since it is very likely needed as start vector in the
        simulation for which the start distributions has been computed.
        (The computed distribution assumes the system to be in this state.)
    """
    B_sym = srm.compartmental_matrix
    u_sym = srm.external_inputs
    if srm.is_linear:
        if srm.is_state_dependent(u_sym):
            # in this case we can in principle transform to
            # a linear Model with constant
            # input and new B
            # compute the jacobian of u
            sv = Matrix(srm.state_vector)
            M = jacobian(u_sym, sv)
            u_sym = u_sym - M*sv
            B_sym = B_sym + M

        t = srm.time_symbol
        tup = (t,)
        u_func = numerical_function_from_expression(
            u_sym,
            tup,
            parameter_dict,
            func_set
        )
        B_func = numerical_function_from_expression(
            B_sym,
            tup,
            parameter_dict,
            func_set
        )
        B0 = B_func(t0)
        u0 = u_func(t0)
        try:
            x_fix = (-inv(B0) @ u0).reshape(srm.nr_pools)
#            pe('x_fix',locals())
        except LinAlgError as e:
            print("""
            B_0=B(t_0) is not invertable
            If a singular matrix B_0 occurs, then the system would have traps.
            A steady state could then only occour
            if the components of u0=u(t0) would be zero for the pools connected
            to the trap.
            In this (unlikely) event the startage distribution would
            be ambigous because the fixedpoint x_{fix} is not uniqe
            (The content of the trap is a free parameter and so is its age.)
            """)
            raise e

    else:
        if x0 is None:
            x0 = np.ones(srm.nr_pools)
            warning(
                """
                No initial guess for the fix point iteration given.
                For nonlinear models equilibria can in general only be found
                numerically by an iterative process starting from an initial
                guess x0 which has not been provided.
                Since a nonlinear model can have several equilibria the initial
                guess determines which one will be found by the iteration.
                A good guess might also increase the likelihood to find an
                equilibrium at all.
                In absence of an initial guess we will start with
                numpy.ones(smr.nr_pools) which might not be a good choice.
                We strongly advise to specify the x0 argument."""
            )

        x_fix = compute_fixedpoint_numerically(
            srm,
            t0,
            x0,
            parameter_dict,
            func_set
        )
#        pe('x_fix',locals())

        t = srm.time_symbol
        tup = (t,) + tuple(srm.state_vector)
        u_func_non_lin = numerical_function_from_expression(
            u_sym,
            tup,
            parameter_dict,
            func_set
        )
        B_func_non_lin = numerical_function_from_expression(
            B_sym,
            tup,
            parameter_dict,
            func_set
        )
        B0 = B_func_non_lin(t0, *x_fix)
        u0 = u_func_non_lin(t0, *x_fix)

    B0_m = Matrix(B0)
    u0_m = Matrix(u0)
#    pe('B0',locals())
#    pe('u0',locals())
    lapm = LinearAutonomousPoolModel(u0_m, B0_m, force_numerical=True)
    return lapm, x_fix
コード例 #8
0
    def test_numeric_steady_state(self):
        # two-dimensional nonlinear
        C_0, C_1 = symbols('C_0 C_1')
        state_vector = [C_0, C_1]
        t = Symbol('t')

        input_fluxes = {0: 4, 1: 2}
        output_fluxes = {0: C_0**2, 1: C_1}
        internal_fluxes = {}
        srm = SmoothReservoirModel(state_vector, t, input_fluxes,
                                   output_fluxes, internal_fluxes)
        res = compute_fixedpoint_numerically(srm,
                                             t0=0,
                                             x0=np.array([1, 1]),
                                             parameter_dict={},
                                             func_set={})
        ref = np.array([2, 2])
        self.assertTrue(np.allclose(res, ref))
        self.assertTupleEqual(res.shape, ref.shape)

        # two-dimensional with external functions
        # although linear the code will assume u(C_1,C_2,t) to be
        # nonlinear since it can not check
        C_0, C_1 = symbols('C_0 C_1')
        state_vector = [C_0, C_1]
        t = Symbol('t')

        f_expr = Function('f')(C_0, t)

        def f_func(C_0_val, t_val):
            return C_0_val + t_val

        func_set = {f_expr: f_func}

        input_fluxes = {0: f_expr, 1: 2}
        output_fluxes = {0: 2 * C_0, 1: C_1}
        internal_fluxes = {}
        srm = SmoothReservoirModel(state_vector, t, input_fluxes,
                                   output_fluxes, internal_fluxes)
        res = compute_fixedpoint_numerically(srm,
                                             t0=2,
                                             x0=np.array([4, 4]),
                                             parameter_dict={},
                                             func_set=func_set)
        ref = np.array([2, 2])
        self.assertTrue(np.allclose(res, ref))
        self.assertTupleEqual(res.shape, ref.shape)

        # two-dimensional with coupled with linear external functions
        C_0, C_1 = symbols('C_0 C_1')
        state_vector = [C_0, C_1]
        t = Symbol('t')

        f_expr = Function('f')(t)

        def f_func(t_val):
            return np.sin(t_val)

        func_set = {f_expr: f_func}

        input_fluxes = {0: C_0 * f_expr, 1: 2}
        output_fluxes = {0: C_0, 1: C_1}
        internal_fluxes = {(0, 1): 0.5 * C_0**3}
        srm = SmoothReservoirModel(state_vector, t, input_fluxes,
                                   output_fluxes, internal_fluxes)
        t0 = 2
        res = compute_fixedpoint_numerically(srm,
                                             t0=t0,
                                             x0=np.array([1, 2]),
                                             parameter_dict={},
                                             func_set=func_set)
        # make sure that the righthandside of the ode is zero
        F_sym = srm.F
        F_func = numerical_function_from_expression(F_sym,
                                                    tup=(C_0, C_1, t),
                                                    parameter_dict={},
                                                    func_set=func_set)
        F_res = F_func(*res, t0)
        ref = np.array([0, 0])
        self.assertTrue(np.allclose(F_res, ref))
        self.assertTupleEqual(res.shape, ref.shape)
コード例 #9
0
        #
        # But this would involve 2 inversions.
        # To save one inversion we use (A*B)^-1=B^-1*A^-1
        return np.matmul(np.linalg.inv(Phi_t0_mat(s)), Phi_t0_mat(t))

    if s == t_0:
        return Phi_t0_mat(t)
    # fixme: mm 3/9/2019
    # the following inversion is very expensive and should be cached or avoided
    # to save space in a linear succession state Transition operators from step to step
    # then everything can be reached by a
    return np.matmul(Phi_t0_mat(t), np.linalg.inv(Phi_t0_mat(s)))


tup = (srm.time_symbol, ) + tuple(srm.state_vector)
B_func = numerical_function_from_expression(srm.compartmental_matrix, tup,
                                            parameter_dict, func_dict)


def Phi_direct(t, s):
    """
    For t_0 <s <t we have
    
    compute Phi(t,s) directly
    by integrating 
    d Phi/dt= B(x(tau))  from s to t with the startvalue Phi(s)=UnitMatrix
    It assumes the existence of a solution x 
    """

    check_phi_args(t, s)
    # the next call will cost nothing if s and t are smaller than t_max
    # since the ivp caches the solutions up to t_0 after the first call.
コード例 #10
0
ファイル: createModel.py プロジェクト: MPIBGC-TEE/bgc_md2
# If you look at the result you see that the euler forwar approximation assumes the flux at time t to be constant for the timestep  
expr_disc

# if we assume tat delta_t is 1 day and it counts days 
# it becomes even simpler
expr_disc.subs({delta_t:1})
#Which is the same as if we had 't' replaced in the above formula wiht it

# +
# this expression we turn now into a numeric funciton of it
# although in our example it depends only on t and C_leaf_litter we make it a function of ALL state variables to be able to call it in the same way as u_func and B_func 
argtup=(mvs.get_TimeSymbol(), *mvs.get_StateVariableTuple())

C_leaf_litter_func = hr.numerical_function_from_expression(
    expr=expr_cont,
    tup=argtup, 
    parameter_dict=par_dict,
    func_set=func_dict
)
# call it for testing
C_leaf_litter_func(0,*X_0)


# +
#mass production of output functions
def numfunc(expr):
    return hr.numerical_function_from_expression(
    expr=expr,
    tup=(mvs.get_TimeSymbol(), *mvs.get_StateVariableTuple()),
    parameter_dict=par_dict,
    func_set=func_dict
)
コード例 #11
0
 def phi_num(self, tup):
     bm = self.bm
     u_num = numerical_function_from_expression(bm.u_expr, tup,
                                                self.par_dict,
                                                self.func_dict)
     return u_num