def start_age_moments_from_empty_spinup( srm, t_max, parameter_dict, func_set, max_order ): times = np.linspace(0, t_max, 101) spin_up_mr = SmoothModelRun( srm, parameter_dict=parameter_dict, start_values=np.zeros(srm.nr_pools), times=times, func_set=func_set ) m0 = start_age_moments_from_zero_initial_content(srm, max_order) moment_vector_list = [ spin_up_mr.age_moment_vector(order, m0[0:order, :])[-1, :] for order in range(1, max_order+1) ] # stack the moment vectors line wise # first line first moment (columns are pools) # second line second moment (columns are pools) moment_arr = np.stack(moment_vector_list, 0) sol_t_max = spin_up_mr.solve()[-1, :] return moment_arr, sol_t_max
def panel_one(limited_srm,bm, par_dict_v1, control_start_values, times, func_dict): start_values=control_start_values[:-1] limited_smr = SmoothModelRun(limited_srm, par_dict_v1, start_values, times, func_dict) bmr=BastinModelRun( bm, par_dict_v1, control_start_values, times, func_dict) soln_uncontrolled = limited_smr.solve() soln_controlled = bmr.solve() fig=plt.figure(figsize=(10,10)) #fig1.title('Total carbon'+title_suffs[version]) ax1=fig.add_subplot(2,1,1) ax1.tick_params(axis='both', which='major', labelsize=12) ax1.plot(times, soln_uncontrolled[:,0],color='blue' ,label='Atmosphere') ax1.plot(times, soln_uncontrolled[:,1],color='green',label='Terrestrial Biosphere') ax1.plot(times, soln_uncontrolled[:,2],color='red' ,label='Surface ocean') ax1.set_ylabel('Carbon stocks (Pg C)', fontsize=15) ax1.legend(loc=2) ax1.set_title("(a)") ax1.set_xlim(1900, 2500) ax2=fig.add_subplot(2,1,2) ax2.tick_params(axis='both', which='major', labelsize=12) ax2.plot(times, soln_controlled[:,0],color='blue' ,label='Atmosphere') ax2.plot(times, soln_controlled[:,1],color='green',label='Terrestrial Biosphere') ax2.plot(times, soln_controlled[:,2],color='red' ,label='Surface ocean') ax2.set_ylabel('Carbon stocks (Pg C)', fontsize=15) ax2.set_xlabel('Time (yr)', fontsize=15) ax2.set_ylim(ax1.get_ylim()) ax2.set_xlim(1900, 2500) ax2.set_title("(b)") #limited_soln_uncontrolled fig.savefig(my_func_name()+'.pdf', bbox_inches='tight')
def setUp(self): x, y, t = symbols("x y t") state_vector = Matrix([x, y]) B = Matrix([[-1, 1.5], [0.5, -2]]) u = Matrix(2, 1, [9, 1]) srm = SmoothReservoirModel.from_B_u(state_vector, t, B, u) start_values = np.array([10, 40]) self.start_values = start_values self.t_0 = 0 self.t_max = 10 self.ntmo = 10 self.fac = 2 self.times = np.linspace(self.t_0, self.t_max, self.ntmo + 1) self.smr = SmoothModelRun(srm, {}, start_values, self.times) alpha = 0.5 self.decay_rate = 1.0 self.start_values_14C = alpha * self.start_values def Fa_func(t): return alpha self.Fa_func = Fa_func self.smr_14C = SmoothModelRun_14C(self.smr, self.start_values_14C, self.Fa_func, self.decay_rate)
def test_phi_cache_vals(self): k_0_val = 1 k_1_val = 2 x0_0 = np.float(0.5) x0_1 = np.float(1.5) x_0, x_1, k_0, k_1, t, u = symbols("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 = 2000 # the old way relies on the interpolation and goes wild for # small nt... times = np.linspace(t_0, t_max, nt) parameter_dict = {k_0: k_0_val, k_1: k_1_val, u: 1} func_dict = {} # make it a column vector for later use start_x = np.array([x0_0, x0_1]) # create the model run smr = SmoothModelRun(model=srm, parameter_dict=parameter_dict, start_values=start_x, times=times, func_set=func_dict) nr_pools = srm.nr_pools # smr.initialize_state_transition_operator_cache_2b(size=3) cache = smr._compute_state_transition_operator_cache(size=2) 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)] smrl = smr.linearize_old() for ind, phi in enumerate(cache.values): tmin = cache.keys[ind] tmax = cache.keys[ind + 1] for x in bvs: with self.subTest(): phi_x_old = smrl._state_transition_operator_for_linear_systems( tmax, tmin, x) # noqa: E501 phi_x_mat = np.matmul(phi, x).reshape(nr_pools, ) self.assertTrue( np.allclose(phi_x_old, phi_x_mat, rtol=1e-2))
def setUp(self): x, y, t, k = symbols("x y t k") u_1 = Function('u_1')(x, t) state_vector = Matrix([x, y]) B = Matrix([[-1, 1.5], [k, -2]]) u = Matrix(2, 1, [u_1, 1]) self.srm = SmoothReservoirModel.from_B_u(state_vector, t, B, u) start_values = np.array([10, 40]) t_0 = 0 t_max = 10 times = np.linspace(t_0, t_max, 11) disc_times = [5] parameter_dicts = [{k: 1}, {k: 0.5}] func_dicts = [{u_1: lambda x_14C, t: 9}, {u_1: lambda x_14C, t: 3}] pwc_mr = PWCModelRun(self.srm, parameter_dicts, start_values, times, disc_times, func_dicts) self.alpha = 0.5 start_values_14C = start_values * self.alpha def Fa_func(t): return self.alpha decay_rate = 1.0 self.pwc_mr_14C = PWCModelRun_14C(pwc_mr, start_values_14C, Fa_func, decay_rate) timess = [ np.linspace(t_0, disc_times[0], 6), np.linspace(disc_times[0], t_max, 6) ] smrs_14C = [] tmp_start_values = start_values tmp_start_values_14C = start_values_14C for i in range(len(disc_times) + 1): smr = SmoothModelRun(self.srm, parameter_dict=parameter_dicts[i], start_values=tmp_start_values, times=timess[i], func_set=func_dicts[i]) tmp_start_values = smr.solve()[-1] smrs_14C.append( SmoothModelRun_14C(smr, tmp_start_values_14C, Fa_func, decay_rate)) tmp_start_values_14C = smrs_14C[i].solve()[-1] self.smrs_14C = smrs_14C
def __init__( self, qpm, start_values_quant, times_quant, ): self.qpm = qpm p = qpm.parameterization if len(start_values_quant) != len(p.state_var_units): raise Exception('size inconsistency') times_num = np.array([to_number(tv, p.time_unit) for tv in times_quant]) start_values_num = np.array( [to_number(sv, p.state_var_units[i]) for i, sv in enumerate(start_values_quant)] ) self.smr = SmoothModelRun( qpm.srm, p.par_dict, start_values_num, times_num, p.func_dict )
def setUp(self): C_1, C_2, k = symbols('C_1 C_2 k') B = Matrix([[-2, 0], [k, -2]]) u = Matrix(2, 1, [1, 1]) state_vector = Matrix(2, 1, [C_1, C_2]) time_symbol = Symbol('t') srm = SmoothReservoirModel.from_B_u(state_vector, time_symbol, B, u) parameter_dict = {k: 1} start_values = np.array([1.0 / 3.0, 4.0 / 9.0]) times = np.linspace(0, 5, 6) smr = SmoothModelRun(srm, parameter_dict, start_values, times) # smr.initialize_state_transition_operator_cache(lru_maxsize=None) self.alpha = 0.5 # self.alpha = ALPHA_14C start_values_14C = smr.start_values * self.alpha def Fa_func_14C(t): return self.alpha self.smr_14C = SmoothModelRun_14C( smr, start_values_14C, Fa_func_14C, 1.0 # or use DECAY_RATE_14C_DAILY )
def setUp(self): x, y, t = symbols("x y t") state_vector = Matrix([x, y]) B = Matrix([[-1, 1.5], [0.5, -2]]) u = Matrix(2, 1, [9, 1]) srm = SmoothReservoirModel.from_B_u(state_vector, t, B, u) start_values = np.array([10, 40]) self.start_values = start_values self.t_0 = 0 self.t_max = 10 self.ntmo = 10 self.fac = 2 self.times = np.linspace(self.t_0, self.t_max, self.ntmo + 1) self.smr = SmoothModelRun(srm, {}, start_values, self.times)
class QuantityModelRun: def __init__( self, qpm, start_values_quant, times_quant, ): self.qpm = qpm p = qpm.parameterization if len(start_values_quant) != len(p.state_var_units): raise Exception('size inconsistency') times_num = np.array( [to_number(tv, p.time_unit) for tv in times_quant]) start_values_num = np.array([ to_number(sv, p.state_var_units[i]) for i, sv in enumerate(start_values_quant) ]) self.smr = SmoothModelRun(qpm.srm, p.par_dict, start_values_num, times_num, p.func_dict) def solve(self): # compute the solution with respect to the state_var_units given in # the model parameterization sol_num = self.smr.solve() # the result is correct since it comes with unit # and can be converted in any other unit. sol_quant = sol_num * self.qpm.parameterization.state_var_units return sol_quant
def setUp(self): x, t = symbols('x t') B = Matrix(1, 1, [-1]) u = Matrix(1, 1, [1]) state_vector = Matrix(1, 1, [x]) srm = SmoothReservoirModel.from_B_u(state_vector, t, B, u) parameter_dict = {} start_values = np.array([1]) times = np.arange(0, 6, 1) smr = SmoothModelRun(srm, parameter_dict, start_values, times) self.alpha = 1 start_values_14C = start_values * self.alpha decay_rate = 1.0 def Fa_func(t): return self.alpha self.smr_14C = SmoothModelRun_14C(smr, start_values_14C, Fa_func, decay_rate) dmr_from_pwc = DMR.from_SmoothModelRun(smr) fake_net_Us = DMR.from_SmoothModelRun(self.smr_14C).net_Us # we cannot infer net_Us_14C coming from data, hence we use the # net_Us from the 14C model coming from the Smooth 14C model # this is of no use in pracitical situations tough since once # we have smr, we can use smr_14C immediately instead of going # through DMRs self.dmr_from_pwc_14C = DiscreteModelRun_14C(dmr_from_pwc, start_values_14C, fake_net_Us, decay_rate)
def matrix_simul_from_symbolic( # for this model we also want the accumulated respiration pa, X0, npp_in, times): symbol_names = mvs.get_BibInfo().sym_dict.keys() for name in symbol_names: var(name) srm = mvs.get_SmoothReservoirModel() # we create a parameterdict for the fixed values # and extend it by the parameters provided parDict = { clay: 0.2028, silt: 0.2808, lig_wood: 0.4, f_wood2CWD: 1, f_metlit2mic: 0.45, NPP: npp_in } model_params = {Symbol(k): v for k, v in pa._asdict().items()} parDict.update(model_params) nsv1 = {Symbol(k): v for k, v in X0._asdict().items()} start_values = np.array([nsv1[k] for k in mvs.get_StateVariableTuple()]) smr = SmoothModelRun(srm, parameter_dict=parDict, start_values=start_values, times=times, func_set={}) sol = smr.solve() RESP_vec = smr.acc_gross_external_output_vector() RESP = np.sum(RESP_vec, axis=1) # in order to attach it to the solution we # have to have equal length. (in timesteps) # since the accumulated respiration vector # does not return the zeros for the t=0 # but the solution contains the start values # we add the zero at the first time step RESP_w0 = np.concatenate([np.array([0]), RESP]).reshape(sol.shape[0], 1) # have to add zeros at the start ( result = np.concatenate([sol, RESP_w0], axis=1) return result
def test_start_value_format(self): # create ReservoirModel C_1, C_2, C_3 = symbols('C_1 C_2 C_3') state_vector = Matrix(3, 1, [C_1, C_2, C_3]) t = symbols('t') B = Matrix([[-2, 0, 1], [2, -2, 0], [0, 2, -2]]) u = Matrix(3, 1, [1, 0, 0]) srm = SmoothReservoirModel.from_B_u(state_vector, t, B, u) # create ModelRun ss = (-B**(-1) * u) # start_values = np.array(ss).astype(np.float64).reshape((3,)) start_values = np.array(ss).astype(np.float64) times = np.linspace(1919, 2009, 901) parameter_dict = {} smr = SmoothModelRun(srm, parameter_dict, start_values, times) smr.initialize_state_transition_operator_cache(lru_maxsize=None) DMR.from_SmoothModelRun(smr)
def test_net_vs_gross_for_different_time_steps(self): times_fine = np.linspace(self.t_0, self.t_max, self.fac * self.ntmo + 1) times_extra_fine = np.linspace(self.t_0, self.t_max, self.fac**2 * self.ntmo + 1) smr = self.smr smr_fine = SmoothModelRun(smr.model, smr.parameter_dict, smr.start_values, times_fine, smr.func_set) smr_extra_fine = SmoothModelRun(smr.model, smr.parameter_dict, smr.start_values, times_extra_fine, smr.func_set) # We build a discrete model where we use the gross fluxes # as arguments for BOTH (net and gross) fluxes. # This simulates the real world scenario. # Since the net fluxes are different from the gross fluxes # the discrete model assumes wrong net fluxes. # The correct values would be given by smr_fine. # For a smaller step size the gross fluxes would be essentially # the same (interpolating original ones) but the difference # to the net fluxes would be smaller, since the latter approach # the gross fluxes in the limit of small time steps. # So the bigger the time step the bigger the error in the # net fluxes and hence the reconstruction of the discrete Bs. xs_fine, net_Us_fine, net_Fs_fine, net_Rs_fine \ = self.smr.fake_net_discretized_output(times_fine) xs_fine, gross_Us_fine, gross_Fs_fine, gross_Rs_fine \ = self.smr.fake_gross_discretized_output(times_fine) dmr_wrong_fine = DMRWGF.reconstruct_from_fluxes_and_solution( times_fine, xs_fine, # net_Us=gross_Us_fine, net_Fs=gross_Fs_fine, net_Rs=gross_Rs_fine, gross_Us=gross_Us_fine, gross_Fs=gross_Fs_fine, gross_Rs=gross_Rs_fine) plot_stocks_and_fluxes( [self.smr, smr_fine, smr_extra_fine, dmr_wrong_fine], 'stocks_and_fluxes.pdf', labels=['mr_normal', 'mr_fine', 'mr_extra_fine', 'dmr_wrong_fine'])
def numeric_model_run_1( npsrm: NumericParameterizedSmoothReservoirModel, start_values_num: NumericStartValueArray, times_num: NumericSimulationTimes, ) -> SmoothModelRun: return SmoothModelRun( npsrm.srm, npsrm.parameterization.par_dict, start_values_num, times_num, npsrm.parameterization.func_dict, )
def test_state_transition_operator_1d(self): # one-dimensional case C = Symbol('C') state_vector = [C] time_symbol = Symbol('t') # are inputs really ignored in the computation of Phi? input_fluxes = {0: 1} output_fluxes = {0: C} internal_fluxes = {} srm = SmoothReservoirModel(state_vector, time_symbol, input_fluxes, output_fluxes, internal_fluxes) start_values = np.array([5]) times = np.linspace(0, 1, 11) smr = SmoothModelRun(srm, {}, start_values, times) x = np.array([1]) Phix = smr._state_transition_operator(1, 0, x) self.assertEqual(Phix.shape, (1, )) self.assertTrue(abs(Phix - np.exp(-1)) < 1e-03)
def setUp(self): x, y, t, k = symbols("x y t k") u_1 = Function('u_1')(x, t) state_vector = Matrix([x, y]) B = Matrix([[-1, 1.5], [k/(t+1), -2]]) u = Matrix(2, 1, [u_1, 1]) self.srm = SmoothReservoirModel.from_B_u( state_vector, t, B, u ) start_values = np.array([10, 40]) t_0 = 0 t_max = 10 times = np.linspace(t_0, t_max, 11) disc_times = [5] parameter_dicts = [{k: 1}, {k: 0.5*t}] func_dicts = [{u_1: lambda x, t: 9}, {u_1: lambda x, t: 3*t}] self.pwc_mr = PWCModelRun( self.srm, parameter_dicts, start_values, times, disc_times, func_dicts ) timess = [ np.linspace(t_0, disc_times[0], 6), np.linspace(disc_times[0], t_max, 6) ] smrs = [] tmp_start_values = start_values for i in range(len(disc_times)+1): smrs.append( SmoothModelRun( self.srm, parameter_dict=parameter_dicts[i], start_values=tmp_start_values, times=timess[i], func_set=func_dicts[i] ) ) tmp_start_values = smrs[i].solve()[-1] self.smrs = smrs
def test_phi_2d_linear(self): C_0, C_1 = symbols('C_0 C_1') state_vector = [C_0, C_1] time_symbol = Symbol('t') input_fluxes = {} output_fluxes = {0: C_0, 1: C_1} internal_fluxes = {} srm = SmoothReservoirModel(state_vector, time_symbol, input_fluxes, output_fluxes, internal_fluxes) start_values = np.array([1, 2]) t_0 = 0 t_max = 4 nt = 200 times = np.linspace(t_0, t_max, nt) smr = SmoothModelRun(srm, {}, start_values, times) smr.initialize_state_transition_operator_cache(lru_maxsize=None, size=2) nr_pools = srm.nr_pools 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)] for s in np.linspace(t_0, t_max, 5): for t in np.linspace(s, t_max, 5): phi_ref = np.eye(2) * np.exp(-(t - s)) # test the matrix valued results with self.subTest(): self.assertTrue( np.allclose(smr.Phi(t, s), phi_ref, rtol=1e-2)) # test the vectored valued results for x in bvs: for phi_x in [ smr._state_transition_operator(t, s, x), smr._state_transition_operator_for_linear_systems( t, s, x) ]: with self.subTest(): self.assertTrue( np.allclose(phi_x, np.matmul(phi_ref, x).reshape(nr_pools, ), rtol=1e-2))
def setUp(self): x, y, t, k = symbols("x y t k") u_1 = Function('u_1')(x, t) state_vector = Matrix([x, y]) B = Matrix([[-1, 1.5], [k / (t + 1), -2]]) u = Matrix(2, 1, [u_1, 1]) self.srm = SmoothReservoirModel.from_B_u(state_vector, t, B, u) start_values = np.array([10, 40]) t_0 = 0 t_max = 10 times = np.linspace(t_0, t_max, 11) parameter_dict = {k: 1} func_dict = {u_1: lambda x, t: 9} self.smr = SmoothModelRun(self.srm, parameter_dict, start_values, times, func_dict)
def test_state_transition_operator_2d(self): # two-dimensional case C_0, C_1 = symbols('C_0 C_1') state_vector = [C_0, C_1] time_symbol = Symbol('t') input_fluxes = {} output_fluxes = {0: C_0, 1: C_1} internal_fluxes = {} srm = SmoothReservoirModel(state_vector, time_symbol, input_fluxes, output_fluxes, internal_fluxes) start_values = np.array([5, 3]) times = np.linspace(0, 1, 11) smr = SmoothModelRun(srm, {}, start_values, times) x = np.array([1, 3]) Phix = smr._state_transition_operator(1, 0, x) self.assertEqual(Phix.shape, (2, )) # test t < t_0 with self.assertRaises(Exception): Phix = smr._state_transition_operator(0, 1, x) # test if operator works correctly also late in time C = Symbol('C') state_vector = [C] time_symbol = Symbol('t') # are inputs really ignored in the computation of Phi? input_fluxes = {0: 1} output_fluxes = {0: C} internal_fluxes = {} srm = SmoothReservoirModel(state_vector, time_symbol, input_fluxes, output_fluxes, internal_fluxes) start_values = np.array([5]) times = np.linspace(0, 100, 101) smr = SmoothModelRun(srm, {}, start_values, times) x = np.array([1]) Phix = smr._state_transition_operator(91, 89, x) self.assertTrue(abs(Phix - np.exp(-2)) < 1e-03)
def test_cache_hash(self): # test semi-symbolic semi-numerical SmoothReservoirModel C_0, C_1, C_2 = symbols('C_0 C_1 C_2') t = Symbol('t') u_0_expr = Function('u_0')(C_0, C_1, t) u_2_expr = Function('u_2')(t) X = Matrix([C_0, C_1, C_2]) t_min, t_max = 0, 10 u_data_0 = np.array([[t_min, 0.1], [t_max, 0.2]]) u_data_2 = np.array([[t_min, 0.4], [t_max, 0.5]]) symbolic_input_fluxes = {0: u_0_expr, 2: u_2_expr} u_0_interp = interp1d(u_data_0[:, 0], u_data_0[:, 1]) def u0_func(C_0_val, C_1_val, t_val): return C_0_val * 0 + C_1_val * 0 + u_0_interp(t_val) u_1_interp = interp1d(u_data_2[:, 0], u_data_2[:, 1]) def u2_func(t_val): return u_1_interp(t_val) func_set = {u_0_expr: u0_func, u_2_expr: u2_func} output_fluxes = {} internal_fluxes = {(0, 1): 5 * C_0, (1, 0): 4 * C_1**2} srm = SmoothReservoirModel(X, t, symbolic_input_fluxes, output_fluxes, internal_fluxes) start_values = np.array([1, 2, 3]) times = np.linspace(t_min, t_max, 11) smr = SmoothModelRun(srm, parameter_dict={}, start_values=start_values, times=times, func_set=func_set) smr.solve() # To be able to check if a stored state_transition_operator cache # is applicable to the SmoothModelRun object it is supposed to speed up smr.myhash()
#limited_srm_90 = SmoothReservoirModel( # state_vector, time_symbol, net_input_fluxes, net_output_fluxes, # lim_inf_90 #) #half_saturation_bm_300=BastinModel( # limited_srm_300,half_saturation_utz_exp,z #) cubic_bm = BastinModel(limited_srm, cubic_utz_exp, z) #deceleration_bm_300=BastinModel( # limited_srm_300,deceleration_utz_exp,z #) # create a dictionary of model runs all_mrs = { "unlimited_smr": SmoothModelRun(unlimited_srm, par_dict, start_values, times, func_dict), "limited_smr": SmoothModelRun(limited_srm, par_dict, start_values, times, func_dict), # "limited_90_smr": # SmoothModelRun( # limited_srm_90, par_dict, start_values , times, func_dict), # "limited_300_controlled_half_saturation_10_20": # BastinModelRun( # half_saturation_bm_300, par_dict_half_saturation_10, # start_values=np.array(list(start_values)+[20]), # times=times, func_dict=func_dict), "limited_controlled_cubic_fast": BastinModelRun(cubic_bm, par_dict_cubic_fast, start_values=np.array( list(start_values) + [par_dict_cubic_fast[z_max]]),
# + [markdown] hide_input=false hide_output=false run_control={"marked": false} # We use the `SmoothReservoirModel`, the parameter dictionary, and the customly defined functions to create a `SmoothModelRun`. First for version 1, then for version 2. We also initialize a cache to dramatically speed up computations at almost no cost. # + hide_input=false hide_output=false run_control={"marked": false} # the system starts in equilibrium start_values = np.array([A_eq, T_eq, S_eq]) # possibly nonlinear effects as a parameter dictionary par_dict_v1 = {alpha: 0.2, beta: 10.0} # nonlinear par_dict_v2 = {alpha: 1.0, beta: 1.0} # linear par_dicts = [par_dict_v1, par_dict_v2] # create the nonlinear model run and the according cache smrs = [] for version in versions: smr = SmoothModelRun(srm, par_dicts[version], start_values, times, func_set) smr.initialize_state_transition_operator_cache(lru_maxsize=5000, size=100) smrs.append(smr) # - # Now we plot the carbon stocks of the two model versions. # + hide_input=false hide_output=false run_control={"marked": false} for version in versions: soln = smrs[version].solve() # plot the solution plt.figure(figsize=(10, 7)) plt.title('Total carbon' + title_suffs[version]) plt.plot(times, soln[:, 0], color='blue', label='Atmosphere')
# # Create a numerical instance of the model # We now use the set of parameter values described in the original publication, and define the initial conditions for the state variables, so we can run a forward simulation. # We use here the class SmoothReservoirModel from the CompartmentalSystem package to load all the ingridients for the model and then be able to compute the solution. # Replace symbolic model by numerical values par_dict = {mu_b: 4.38, epsilon: 0.39, K_s: 53954.83, V_s: 59.13, F_NPP: 345.0} start_values = np.array([11000, 50]) times = np.linspace(0, 100, 1000) # Create smooth reservoir model (a class instance) srm = SmoothReservoirModel.from_B_u(x, time_symbol, B, u) # create the nonlinear model run (also a class instance, now from SmoothModelRun) smrs = [] smr = SmoothModelRun(srm, par_dict, start_values, times) soln = smr.solve() # + # plot the solution plt.figure(figsize=(10, 7)) plt.title('Total carbon') plt.plot(times, soln[:, 0], color='blue', label='C_s: carbon substrate') plt.plot(times, soln[:, 1], color='green', label='C_b: microbial biomass') #plt.plot(times, soln.sum(1), color = 'black', label = 'Total') plt.legend(loc=2) plt.xlabel('Time (yr)') plt.ylabel('Mass (gC/m2)')
class TestModelRun(InDirTest): def setUp(self): x, y, t = symbols("x y t") state_vector = Matrix([x, y]) B = Matrix([[-1, 1.5], [0.5, -2]]) u = Matrix(2, 1, [9, 1]) srm = SmoothReservoirModel.from_B_u(state_vector, t, B, u) start_values = np.array([10, 40]) self.start_values = start_values self.t_0 = 0 self.t_max = 10 self.ntmo = 10 self.fac = 2 self.times = np.linspace(self.t_0, self.t_max, self.ntmo + 1) self.smr = SmoothModelRun(srm, {}, start_values, self.times) # @unittest.skip def test_DiscreteModelRunWithGrossFluxes_from_SmoothModelRun(self): dmr = DMRWGF.from_SmoothModelRun(self.smr) meths = [ "solve", "acc_gross_external_input_vector", "acc_net_external_input_vector", "acc_gross_external_output_vector", "acc_net_external_output_vector", "acc_gross_internal_flux_matrix", "acc_net_internal_flux_matrix" ] for meth in meths: with self.subTest(): self.assertTrue( np.allclose( getattr(self.smr, meth)(), getattr(dmr, meth)())) def test_net_vs_gross_for_different_time_steps(self): times_fine = np.linspace(self.t_0, self.t_max, self.fac * self.ntmo + 1) times_extra_fine = np.linspace(self.t_0, self.t_max, self.fac**2 * self.ntmo + 1) smr = self.smr smr_fine = SmoothModelRun(smr.model, smr.parameter_dict, smr.start_values, times_fine, smr.func_set) smr_extra_fine = SmoothModelRun(smr.model, smr.parameter_dict, smr.start_values, times_extra_fine, smr.func_set) # We build a discrete model where we use the gross fluxes # as arguments for BOTH (net and gross) fluxes. # This simulates the real world scenario. # Since the net fluxes are different from the gross fluxes # the discrete model assumes wrong net fluxes. # The correct values would be given by smr_fine. # For a smaller step size the gross fluxes would be essentially # the same (interpolating original ones) but the difference # to the net fluxes would be smaller, since the latter approach # the gross fluxes in the limit of small time steps. # So the bigger the time step the bigger the error in the # net fluxes and hence the reconstruction of the discrete Bs. xs_fine, net_Us_fine, net_Fs_fine, net_Rs_fine \ = self.smr.fake_net_discretized_output(times_fine) xs_fine, gross_Us_fine, gross_Fs_fine, gross_Rs_fine \ = self.smr.fake_gross_discretized_output(times_fine) dmr_wrong_fine = DMRWGF.reconstruct_from_fluxes_and_solution( times_fine, xs_fine, # net_Us=gross_Us_fine, net_Fs=gross_Fs_fine, net_Rs=gross_Rs_fine, gross_Us=gross_Us_fine, gross_Fs=gross_Fs_fine, gross_Rs=gross_Rs_fine) plot_stocks_and_fluxes( [self.smr, smr_fine, smr_extra_fine, dmr_wrong_fine], 'stocks_and_fluxes.pdf', labels=['mr_normal', 'mr_fine', 'mr_extra_fine', 'dmr_wrong_fine']) # @unittest.skip def test_DiscreteModelRunFromFakeData(self): times = self.smr.times xs, net_Us, net_Fs, net_Rs \ = self.smr.fake_net_discretized_output(times) xs, gross_Us, gross_Fs, gross_Rs \ = self.smr.fake_gross_discretized_output(times) dmr = DMRWGF.reconstruct_from_fluxes_and_solution( times, xs, # net_Us, net_Fs, net_Rs, gross_Us, gross_Fs, gross_Rs) meths = [ "solve", "acc_gross_external_input_vector", "acc_net_external_input_vector", "acc_gross_external_output_vector", "acc_net_external_output_vector", "acc_gross_internal_flux_matrix", "acc_net_internal_flux_matrix" ] for meth in meths: with self.subTest(): self.assertTrue( np.allclose( getattr(self.smr, meth)(), getattr(dmr, meth)())) # @unittest.skip def test_PWCModelRunFD(self): times = self.smr.times xs, gross_Us, gross_Fs, gross_Rs \ = self.smr.fake_gross_discretized_output(times) pwc_mr_fd = PWCModelRunFD.from_gross_fluxes(self.smr.model.time_symbol, times, self.smr.start_values, gross_Us, gross_Fs, gross_Rs) meths = [ "solve", "acc_gross_external_input_vector", "acc_net_external_input_vector", "acc_gross_external_output_vector", "acc_net_external_output_vector", "acc_gross_internal_flux_matrix", "acc_net_internal_flux_matrix" ] for meth in meths: with self.subTest(): ref = getattr(self.smr, meth)() res = getattr(pwc_mr_fd, meth)() self.assertTrue( np.allclose(ref, res, rtol=3e-02) # For this linear constant model # the error should actually be zero # and is only due to numerical inaccuracy. ) plot_stocks_and_fluxes([self.smr, pwc_mr_fd], 'stocks_and_fluxes.pdf')
def test_mean_age_distribution_for_BW(self): # create the model var("t, k_01,k_10,k_0o,k_1o") var("C_0,C_1") state_variables = [C_0, C_1] # order is important inputs = { #0:sin(t)+2,#input to pool 0 #1:cos(t)+2 #input to pool 1 0: sympify(2), #input to pool 0 1: sympify(0) #input to pool 1 } outputs = { 0: k_0o * C_0**3, #output from pool 0 1: k_1o * C_1**3 #output from pool 0 } internal_fluxes = { (0, 1): k_01 * C_0 * C_1**2, #flux from pool0 to pool 1 (1, 0): k_10 * C_0 * C_1 #flux from pool1 to pool 0 } time_symbol = t mod = SmoothReservoirModel(state_variables, time_symbol, inputs, outputs, internal_fluxes) #set the time step size tss = .1 #create a Model run self.params = {k_01: 1 / 100, k_10: 1 / 100, k_0o: 1 / 2, k_1o: 1 / 2} start_values = [1, 2] times = np.arange(100) * tss # time grid forward mr = SmoothModelRun(mod, self.params, start_values, times) # now create initial age distributions # since we start with system age 0 we start with very # small fields indeed # pool 0 x, y = 1, 1 s = (x, y) age_dist_0 = TsTpMassField(np.zeros(s), tss) age_dist_0[0, 0] = start_values[0] # pool 1 x1, y1 = 1, 1 s = (x1, y1) age_dist_1 = TsTpMassField(np.zeros(s), tss) age_dist_1[0, 0] = start_values[1] # initialize the combination (this would adjust for different system ages) initial_plains = CompatibleTsTpMassFieldsPerPool( [age_dist_0, age_dist_1]) # we now build the deathrate functions # note that the factories depend # on the solution funtions # produce the output deathrate functions def external_death_rate_maker(sender, func, solfs): def wrapper(field, t): tss = field.tss loss = quad(func, t, t + tss)[0] stock = solfs[sender](t) relative_loss = loss / stock #print("stock:=",stock) #print("loss:=",loss) #print("ext_relative_loss:=",relative_loss) dr = TsTpDeathRateField(relative_loss * np.ones(field.shape), tss) return (dr) return (wrapper) external_death_rate_functions = dict() solfs = mr.sol_funcs() for sender, func in mr.output_flux_funcs().items(): external_death_rate_functions[sender] = external_death_rate_maker( sender, func, solfs) # produce the internal deathrate functions def internal_death_rate_maker(key, func, solfs): def wrapper(field, t): sender = key[0] tss = field.tss loss = quad(func, t, t + tss)[0] stock = solfs[sender](t) relative_loss = loss / stock #print("int_relative_loss:=",relative_loss) dr = TsTpDeathRateField(relative_loss * np.ones(field.shape), tss) return (dr) return (wrapper) internal_death_rate_functions = dict() for key, func in mr.internal_flux_funcs().items(): internal_death_rate_functions[key] = internal_death_rate_maker( key, func, solfs) # produce the external inputs def external_input_maker(receiver, func): def wrapper(t): return (quad(func, t, t + tss)[0]) return (wrapper) external_inputs = dict() for receiver, func in mr.external_input_flux_funcs().items(): external_inputs[receiver] = external_input_maker(receiver, func) start = times[0] age_dist_hist = TsTpMassFieldsPerPoolPerTimeStep.compute_from( initial_plains, external_inputs, internal_death_rate_functions, external_death_rate_functions, start, len(times) - 1) #age_dist_hist.single_pool_cartoon(0,"pool_0") fig = plt.figure() age_dist_hist.matrix_plot("plot_total_contents", fig) fig.savefig("total_content.pdf") # fig = plt.figure() #age_dist_hist.matrix_plot3d("plot_system_age_distributions_with_bins",fig) age_dist_hist.matrix_plot3d( "plot_system_age_distributions_as_surfaces", fig) fig.savefig("system_age_distribution.pdf") fig = plt.figure() mr.plot_sols(fig) fig.savefig("mr_total_content.pdf") #compare
{} # func dict ) # define a dictionary to connect the symbols with the according functions func_set = {xi: xi_func_numerical, G: u_func_numerical} # the system starts in equilibrium start_values = np.array([x_1e, x_2e, x_3e, x_4e, x_5e]) # start_values = np.zeros(5) # parameter dictionary par_dict = {G_eq: G_emanuel} # create the nonlinear model run and the according cache smrs = [] smr = SmoothModelRun(srm, par_dict, start_values, times, func_set) smr.initialize_state_transition_operator_cache(lru_maxsize=5000, size=100) smrs.append(smr) soln = smr.solve() cstocks = pd.DataFrame(soln, columns=pool_names) stocks = cstocks.join(pd.DataFrame({"Time": times})) stocks.to_csv("stocks.csv", index=False) GPP = pd.DataFrame(smr.external_input_vector, columns=pool_names) Re = pd.DataFrame(smr.external_output_vector, columns=pool_names) GPP.to_csv("GPP.csv", index=False) Re.to_csv("Re.csv", index=False) ##### load linear autonomous pool model in steady state #####
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")
def start_age_distributions_from_empty_spinup( srm, t_max, parameter_dict, func_set ): """ Finite age spin up from empty pools Creates a SmoothModelRun object with empty pools at :math:`t=0`, runs it until :math:`t=t_{max}` an returns the age distribution at :math:`t_{max}` Args: srm (SmoothReservoirModel) : The (symbolic) model t_max (float): The end of the spinup (which starts at t=0 and runs until t=t_max) parameter_dict (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 Returns: (a_dist_at_end_of_spinup, sol_t_max) (tuple): a_dist_at_end_of_spinup is a vector valued function of age. a_dist_at_end_of_spinup(a)[i] reflects the mass of age :math:`a` in pool i. sol_t_max is a one dimensional vector representing the pool contents at the end of the spinup. This is returned since it is very likely needed as start vector in the simulation for which the start distributions has been computed. """ a_dist_at_start_of_spinup =\ start_age_distributions_from_zero_initial_content(srm) # unfortunately the number of time steps has some influence on accuracy # although the ode solver guarantees a minimum it gets better if you # force it to make smaller steps... # times=[0,t_max] times = np.linspace(0, t_max, 101) spin_up_mr = SmoothModelRun( srm, parameter_dict=parameter_dict, start_values=np.zeros(srm.nr_pools), times=times, func_set=func_set ) # p_sv(a,t) returns the mass of age a at time t p_sv = spin_up_mr.pool_age_densities_single_value( a_dist_at_start_of_spinup ) sol_t_max = spin_up_mr.solve()[-1, :] def a_dist_at_end_of_spinup(age): return p_sv(age, t_max) return a_dist_at_end_of_spinup, sol_t_max
def test_phi_2d_non_linear(self): k_0_val = 1 k_1_val = 2 x0_0 = np.float(0.5) x0_1 = np.float(1.5) x_0, x_1, k_0, k_1, t, u = symbols("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 = 20000 times = np.linspace(t_0, t_max, nt) parameter_dict = {k_0: k_0_val, k_1: k_1_val, u: 1} func_dict = {} # make it a column vector for later use start_x = np.array([x0_0, x0_1]) # create the model run smr = SmoothModelRun( model=srm, parameter_dict=parameter_dict, start_values=start_x, times=times, func_set=func_dict, ) smr.initialize_state_transition_operator_cache(lru_maxsize=None, size=4) nr_pools = srm.nr_pools 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)] smrl = smr.linearize_old() for s in np.linspace(t_0, t_max, 5): for t in np.linspace(s, t_max, 5): for x in bvs: with self.subTest(): self.assertTrue( np.allclose( smr._state_transition_operator(t, s, x), smrl. _state_transition_operator_for_linear_systems( t, s, x), # noqa: E501 rtol=1.5 * 1e-2)) for t in np.linspace(t_0, t_max, 6): for phi_mat in [smr.Phi(t, t_0), smrl.Phi(t, t_0)]: for x in bvs: with self.subTest(): self.assertTrue( np.allclose(np.matmul(phi_mat, x).reshape(nr_pools, ), smr._state_transition_operator( t, t_0, x), rtol=1.5 * 1e-2))
def test_save_and_load_state_transition_operator_cache(self): # two-dimensional C_0, C_1 = symbols('C_0 C_1') state_vector = Matrix([C_0, C_1]) time_symbol = Symbol('t') input_fluxes = {0: 1, 1: 2} output_fluxes = {0: C_0, 1: C_1} internal_fluxes = {} srm = SmoothReservoirModel(state_vector, time_symbol, input_fluxes, output_fluxes, internal_fluxes) start_values = np.array([5, 3]) times = np.linspace(0, 1, 6) smr = SmoothModelRun(srm, {}, start_values, times) # ages = np.linspace(-1,1,3) # negative ages will be cut off automatically # start_age_densities = lambda a: np.exp(-a)*start_values smr.initialize_state_transition_operator_cache(lru_maxsize=None, size=100) ca = smr._state_transition_operator_cache filename = 'sto.cache' smr.save_state_transition_operator_cache(filename) smr.load_state_transition_operator_cache(filename) self.assertTrue(np.all(ca == smr._state_transition_operator_cache)) # now we change the model run and make sure that the # saved cache is recognized as invalid. start_values_2 = np.array([6, 3]) smr = SmoothModelRun(srm, {}, start_values_2, times) with self.assertRaises(Exception): smr.load_state_transition_operator_cache(filename)