def test_plot_two_pool_with_internal_flux(self): # get the components ready # - initial age distributions tss = 1 x, y = 4, 4 s = (x, y) age_dist_0 = TsTpMassField(2 * np.ones(s), tss) x1, y1 = 4, 4 s1 = (x1, y1) age_dist_1 = TsTpMassField(2 * np.zeros(s1), tss) # the code has to initialize pool_0 regarding the size of pool_1 initial_plains = CompatibleTsTpMassFieldsPerPool( [age_dist_0, age_dist_1]) # - deathrate fields # internal idr = dict() idr[(0, 1)] = TsTpDeathRateField( np.zeros(s), tss ) #one connection to second pool (well mixed=no preference for any age) idr[(0, 1)][1, 1] = 0.5 idr[(0, 1)][2, 2] = 0.5 idr[(0, 1)][3, 3] = 0.5 # external edr = dict() ei = dict() ei[0] = 3 ts = TimeStep(0, initial_plains, idr, edr, ei) TimeStepPlotter(ts).plot_pdfs()
def test_gains(self): tss = 0.1 x, y = 3, 3 s = (x, y) val = 1 arr = np.zeros(s) arr[2, 2] = val f_0 = TsTpMassField(arr, tss) mult = 2 f_1 = TsTpMassField(mult * arr, tss) # only pipes can contribute to a pool pipe_contents = TsTpMassFieldsPerPipe({(0, 1): f_0, (2, 1): f_1}) computed_gains = pipe_contents.gains # only pool one gains receivers = computed_gains.keys() self.assertEqual(list(computed_gains.keys()), [1]) # the result is one dimensional self.assertTrue(isinstance(computed_gains[1], TsMassField)) # the length of the result has increased by one # representing our policy to pinpoint the moment when # material becomes older excatly between the moments of # leaving one pool and reaching the next # material aging in the pipeline self.assertTrue(computed_gains[1].shape, f_0.shape + (1, )) ref = np.zeros(x + 1) # the contributions are added up correctly # and appear one step shifted in Ts ref[3] = val + mult * val self.assertTrue((computed_gains[1].arr == ref).all())
def setUp(self): self.x, self.y = 6, 6 self.s = (self.x, self.y) self.arr = np.zeros(self.s) self.arr[5, 5] = 10 self.arr[5, 4] = 20 self.tss = 0.1 ## the time step size self.spad = TsTpMassField(self.arr, self.tss)
def setUp(self): self.tss = 0.1 arr0 = np.zeros((3, 3)) ref0 = arr0 arr1 = np.zeros((3, 3)) ref1 = arr1 f0 = TsTpMassField(arr0, self.tss) f1 = TsTpMassField(arr1, self.tss) self.fields = CompatibleTsTpMassFieldsPerPool([f0, f1])
def test_external_losses(self): computed_losses = self.fields.external_losses( self.external_death_rate_fields) ref = TsTpMassFieldsPerPool() ref[0] = TsTpMassField(self.loss_factor * self.age_dist_0.arr, self.tss) ref[1] = TsTpMassField(self.loss_factor * self.age_dist_1.arr, self.tss) for k, v in ref.items(): f = computed_losses[k] self.assertTrue((v.arr == f.arr).all())
def test_plot_three_pool(self): # get the components ready # - initial age distributions tss = 1 x, y = 5, 4 s = (x, y) age_dist_0 = TsTpMassField(np.zeros(s), tss) age_dist_0[3, 3] = 1 age_dist_0[3, 2] = 2 age_dist_0[2, 2] = 4 x1, y1 = 5, 4 s1 = (x1, y1) age_dist_1 = TsTpMassField(np.zeros(s1), tss) age_dist_1[3, 3] = 0.5 age_dist_1[3, 2] = 0.5 age_dist_1[1, 1] = 0.5 x2, y2 = 5, 4 s2 = (x2, y2) age_dist_2 = TsTpMassField(np.zeros(s2), tss) age_dist_2[3, 0] = 2 age_dist_2[1, 0] = 4 age_dist_2[0, 0] = 6 # the code has to initialize pool_0 regarding the size of pool_1 initial_plains = CompatibleTsTpMassFieldsPerPool( [age_dist_0, age_dist_1, age_dist_2]) # - deathrate fields # internal idr = dict() idr[(0, 1)] = TsTpDeathRateField( 1 / 8 * np.ones(s), tss ) #one connection to second pool (well mixed=no preference for any age) idr[(0, 2)] = TsTpDeathRateField( 1 / 8 * np.ones(s), tss ) #one connection to second pool (well mixed=no preference for any age) # external edr = dict() edr[0] = TsTpDeathRateField( 1 / 4 * np.ones(s), tss) # well mixed outflux regardless of age from pool 1 edr[1] = TsTpDeathRateField(1 / 8 * np.ones(s1), tss) # also from pool 2 # - external input numbers ei = dict() ei[0] = 3 ei[1] = 4 ts = TimeStep(0, initial_plains, idr, edr, ei) ts.plot_pdfs()
def test_plot_single_pool_with_outflux(self): # get the components ready # - initial age distributions tss = 1 x, y = 2, 2 s = (x, y) age_dist_0 = TsTpMassField(3 * np.ones(s), tss) #homogeneous age distribution # the output from pool_1 has a bigger age_span than pool_0 can encompass # the code has to initialize pool_0 regarding the size of pool_1 initial_plains = CompatibleTsTpMassFieldsPerPool([age_dist_0]) # no internal deathrate fields because there is no other pool # to transfer material to idr = dict() # external deathrate # to check the effect we only remove water that has been # of one particular age bin. edr = dict() edr[0] = TsTpDeathRateField(np.zeros(s), tss) edr[0][1, 1] = 0.5 # - external input numbers ei = dict() ei[0] = 2 ts = TimeStep(0, initial_plains, idr, edr, ei) TimeStepPlotter(ts).plot_pdfs() refarr = np.zeros((x + 1, y + 1)) refarr[2, 2] = 0.5
def test_plot_single_pool_with_outflux(self): # get the components ready # - initial age distributions tss=1 x,y=5,4 s=(x,y) age_dist_0=TsTpMassField(np.zeros(s),tss) age_dist_0[3,3]=1 age_dist_0[3,2]=2 age_dist_0[2,2]=3 # the output from pool_1 has a bigger age_span than pool_0 can encompass # the code has to initialize pool_0 regarding the size of pool_1 initial_plains=CompatibleTsTpMassFieldsPerPool([age_dist_0]) # - deathrate fields idr=dict() # external edr=dict() edr[0]=TsTpDeathRateField(np.zeros(s),tss) edr[0][3,2]=0.5 edr[0][3,1]=0.4 edr[0][3,0]=0.3 # - external input numbers ei=dict() ei[0]=3 tsp=TimeStepPlotter(TimeStep(0,initial_plains,idr,edr,ei)) tsp.plot_pdfs()
def test_init(self): tss = 0.1 with self.assertRaises(Exception) as cm: #empty list initial_fields = CompatibleTsTpMassFieldsPerPool([]) # check a one pool example age_dist_0 = TsTpMassField(np.zeros((4, 3)), tss) age_dist_0[2, 2] = 100 initial_fields = CompatibleTsTpMassFieldsPerPool([age_dist_0]) # check a one 3 pool example initial_fields = CompatibleTsTpMassFieldsPerPool([ TsTpMassField(np.zeros((3, 3)), tss), TsTpMassField(np.zeros((2, 1)), tss), TsTpMassField(np.zeros((4, 2)), tss) ]) self.assertEqual(len(initial_fields), 3) # check that all pools can now receive sytem ages up to 4*tss althoug they were smaller at the beginnign tss = 0.1 initial_fields = CompatibleTsTpMassFieldsPerPool([ TsTpMassField(np.zeros((3, 3)), tss), TsTpMassField(np.zeros((2, 1)), tss), TsTpMassField(np.zeros((4, 2)), tss) ]) for field in initial_fields.values(): self.assertTrue(field.number_of_Ts_entries == 4)
def test_internal_losses(self): internal_death_rate_funcs = dict() internal_death_rate_funcs[(0, 1)] = self.func internal_death_rate_funcs[(1, 0)] = self.func fields = self.fields t = self.time internal_death_rate_fields = { pipe_key: f(fields[pipe_key[0]], t) for pipe_key, f in internal_death_rate_funcs.items() } #print(fields) computed_losses = fields.internal_losses(internal_death_rate_fields) ref = TsTpMassFieldsPerPipe() ref[(0, 1)] = TsTpMassField(self.loss_factor * self.age_dist_0.arr, self.tss) ref[(1, 0)] = TsTpMassField(self.loss_factor * self.age_dist_1.arr, self.tss) for key, v in ref.items(): f = computed_losses[key] self.assertTrue((v.arr == f.arr).all())
def setUp(self): self.tss = 0.1 x, y = 3, 3 s = (x, y) arr = np.zeros(s) arr[2, 2] = 2 arr2 = 2 * arr #copy for references in the tests self.arr = arr self.arr2 = arr2 self.age_dist_0 = TsTpMassField(arr, self.tss) self.age_dist_1 = TsTpMassField(arr2, self.tss) size_diff = 5 age_dist_2 = TsTpMassField(np.zeros((x + size_diff, y + size_diff)), self.tss) self.fields = TsTpMassFieldsPerPool({ 0: self.age_dist_0, 1: self.age_dist_1 }) self.time = 10 self.loss_factor = 0.3 def constant_well_mixed_death_rate(age_dist, t): # these functions must be able to define a TsTpDeathRateField eta # of the same size as the age distribution it gets # for all the system ages and pool ages present in age_dist it must # be able to compute the deathrate return (TsTpDeathRateField( self.loss_factor * np.ones(age_dist.arr.shape), age_dist.tss)) self.func = constant_well_mixed_death_rate external_death_rate_funcs = dict() external_death_rate_funcs[0] = self.func external_death_rate_funcs[1] = self.func fields = self.fields t = self.time external_death_rate_fields = { pool_key: f(fields[pool_key], t) for pool_key, f in external_death_rate_funcs.items() } self.external_death_rate_fields = external_death_rate_fields
def test_receive(self): x, y = 5, 2 valf = 1 field = TsTpMassField(np.zeros((x, y)), self.tss) # although not relevant for this test the shifted # field would not have entries in [0,:],and [:,0] therefor we choose field[1, 1] = valf # note that the gains method has already shifted the TsMassField # by tss so that the gains are the same size as the (also shifted) # receiver in Ts direction gain = TsMassField(5 * np.zeros(x), self.tss) # Since the gains have also been collected from other pools # there system age is at least tss # size in Ts direction so gains[0] must stay 0 valg = 2 gain[1] = valg field.receive(gain) ref = np.zeros((x, y)) ref[1, 1] = valf #as before ref[1, 0] = valg #gains incorporated with pool age 0 self.assertTrue((field.arr == ref).all())
def test_init(self): f_0 = TsTpMassField(np.zeros((3, 3)), 0.1) with self.assertRaises(Exception) as cm: TsTpMassFieldsPerPipe( {1: f_0}) #only tuples of integer are allowed as indices with self.assertRaises(Exception) as cm: TsTpMassFieldsPerPipe({1: 2}) #only TsTpMassFields allowed as values with self.assertRaises(Exception) as cm: TsTpMassFieldsPerPipe({ (1, 1): 2 }) # sender and receiver have to be different
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
class TestTsTpMassField(unittest.TestCase): def setUp(self): self.x, self.y = 6, 6 self.s = (self.x, self.y) self.arr = np.zeros(self.s) self.arr[5, 5] = 10 self.arr[5, 4] = 20 self.tss = 0.1 ## the time step size self.spad = TsTpMassField(self.arr, self.tss) def test_loss(self): eta_dist = TsTpDeathRateField(np.ones(self.s) * 0.5, self.tss) l = self.spad.loss(eta_dist) self.assertEqual(l[5, 5], 5) self.assertEqual(l[5, 4], 10) def test_sum_over_all_pool_ages(self): res = self.spad.sum_over_all_pool_ages() self.assertTrue(isinstance(res, TsMassField)) ref = np.zeros(self.x) ref[5] = 30 #10+20 self.assertTrue((res.arr == ref).all()) def test_shift(self): spad = self.spad spad.shift() ref = np.zeros((self.x + 1, self.y + 1)) ref[6, 6] = 10 ref[6, 5] = 20 #print("\n##########shift",spad.arr) self.assertTrue((spad.arr == ref).all()) def test_resize(self): spad = self.spad spad.resize(10) ref = np.zeros((10, self.y)) ref[5, 5] = 10 ref[5, 4] = 20 self.assertTrue((spad.arr == ref).all()) def test_receive_external(self): spad = self.spad spad.receive_external(5) ref = np.zeros((self.x, self.y)) ref[5, 5] = 10 ref[5, 4] = 20 ref[0, 0] = 5 self.assertTrue((spad.arr == ref).all()) def test_receive(self): x, y = 5, 2 valf = 1 field = TsTpMassField(np.zeros((x, y)), self.tss) # although not relevant for this test the shifted # field would not have entries in [0,:],and [:,0] therefor we choose field[1, 1] = valf # note that the gains method has already shifted the TsMassField # by tss so that the gains are the same size as the (also shifted) # receiver in Ts direction gain = TsMassField(5 * np.zeros(x), self.tss) # Since the gains have also been collected from other pools # there system age is at least tss # size in Ts direction so gains[0] must stay 0 valg = 2 gain[1] = valg field.receive(gain) ref = np.zeros((x, y)) ref[1, 1] = valf #as before ref[1, 0] = valg #gains incorporated with pool age 0 self.assertTrue((field.arr == ref).all())
def test_list_comprehension(self): ############################################################ # get the components ready # - initial age distributions tss = 1 x, y = 9, 9 s = (x, y) age_dist_0 = TsTpMassField(np.zeros(s), tss) age_dist_0[2, 2] = 100 x1, y1 = 4, 4 s = (x1, y1) age_dist_1 = TsTpMassField(np.zeros(s), tss) age_dist_1[3, 3] = 100 # the output from pool_1 has a bigger age_span than pool_0 can encompass # the code has to initialize pool_0 regarding the size of pool_1 initial_plains = CompatibleTsTpMassFieldsPerPool( [age_dist_0, age_dist_1]) ############################################################ # - deathrate functions loss_factor = 0.1 external_death_rate_funcs = dict() internal_death_rate_funcs = dict() def constant_well_mixed_death_rate(age_dist, t): # these functions must be able to define a field eta # of the same size as the age distribution it gets # for all the ages present in age_dist it must # be able to compute the deathrate return (TsTpDeathRateField( loss_factor * np.ones(age_dist.arr.shape), age_dist.tss)) external_death_rate_funcs[0] = constant_well_mixed_death_rate external_death_rate_funcs[1] = constant_well_mixed_death_rate internal_death_rate_funcs[(0, 1)] = constant_well_mixed_death_rate # - input functions def zero_input(t): return (0) def const_input(t): return (5) external_input_funcs = dict() external_input_funcs[0] = const_input external_input_funcs[1] = zero_input drf = lambda t: 0.2 ############################################################ # initialize the Iterator it = TimeStepIterator(initial_plains, external_input_funcs, internal_death_rate_funcs, external_death_rate_funcs, t0=5, number_of_steps=3) ############################################################ ############################################################ ############################################################ # start testing # extract the complete information steps = [ts for ts in it] # or only the part one is interested in rectangles_for_first_pool = [ts.rectangles[0] for ts in it] #print("\n#####################################\nrectangles[0]",rectangles_for_first_pool) # or some parts tuples = [(ts.time, ts.rectangles[0].total_content) for ts in it] x = [t[0] for t in tuples] y = [t[1] for t in tuples] fig = plt.figure() ax = fig.add_subplot(1, 1, 1) ax.plot(x, y, "x") fig.savefig("plot.pdf") plt.close(fig.number)