def __call__(self, days=7): self.c.vegetation.LAI = 7 self.c.vegetation.height = 10 self.c.vegetation.CanopyClosure = 0.1 l = self.c.layers[0] self.c.surfacewater.depth = 0.05 l.soil.Ksat = 0.02 l.volume = 100 self.c.canopy.volume = self.c.vegetation.LAI * self.c.vegetation.CanopyCapacityPerLAI self.c.set_uptakestress(cmf.VolumeStress(10, 0)) Vc0 = self.c.canopy.volume Vl0 = self.c.layers[0].volume Vs0 = self.c.surfacewater.volume solver = cmf.CVodeIntegrator(self.p, 1e-9) vol = [] flux = [] resistance = [] mpot = [] self.et.refresh(cmf.Time()) for t in solver.run(cmf.Time(), cmf.day * days, cmf.min * 6): #print(f'{t}: {self.c.surfacewater.depth:0.3f}m') vol.append( (self.c.canopy.volume / Vc0, self.c.surfacewater.volume / Vs0, self.c.layers[0].volume / Vl0)) flux.append(( self.c.canopy.flux_to(self.c.evaporation, t), self.c.surfacewater.flux_to(self.c.evaporation, t), self.c.layers[0].flux_to(self.c.evaporation, t), self.c.layers[0].flux_to(self.c.transpiration, t), self.c.surfacewater.flux_to(self.c.layers[0], t), )) resistance.append((self.et.RAA, self.et.RAC, self.et.RAS, self.et.RSC, self.et.RSS)) mpot.append(self.c.surface_water_coverage()) return vol, flux, resistance, mpot
def mean_concentration_for_period(phosphorus, water, i, s, time0, time1): conc = [ value[i] for key, value in phosphorus[s.Name + '_simulated_mcg_per_m3_mx+mp'].items() if time0 <= cmf.Time(datetime.strptime(key, '%d.%m.%Y %H:%M')) < time1 ] flux = [ value[i] for key, value in water['simulated_flux_l_per_m2_per_day'].items() if time0 <= cmf.Time(datetime.strptime(key, '%d.%m.%Y %H:%M')) < time1 ] return sum([(a * b) for a, b in zip(conc, flux)]) / sum(flux)
def format_phosphorus_results(approach, phosphorus_results, water_results): """ TEXT :param phosphorus_results: :param water_results: :param approach: :return: """ # save depth of lysimeters and number of corresponding soil layers: depth, layers = depth_and_layers(approach) # begin of the irrigation experiment begin = cmf.Time(12, 6, 2018, 10, 1) simulation_results = { 'dp_simulated_mcg_per_m3_mx+mp': [], 'pp_simulated_mcg_per_m3_mx+mp': [], 'dp_simulated_state_per_m2_mx+mp': [], 'pp_simulated_state_per_m2_mx+mp': [] } # simulation_results = {'dip_simulated_mcg_per_m3_mx+mp': [], 'dop_simulated_mcg_per_m3_mx+mp': [], # 'pp_simulated_mcg_per_m3_mx+mp': [], 'dip_simulated_state_per_m2_mx+mp': [], # 'dop_simulated_state_per_m2_mx+mp': [], 'pp_simulated_state_per_m2_mx+mp': []} for index, row in approach.evaluation_df.iterrows(): i = layers[depth.index(row['depth [m]'])] time0, time1 = timespan(begin, row) for s in approach.project.solutes: # Concentration over time: this is not simply the mean of all time steps, since it needs to be weighted by # the water amount, similar to total_concentration_mcg_per_m3() in input_and_output.py mcg_per_m3_mean = mean_concentration_for_period( phosphorus_results, water_results, i, s, time0, time1) simulation_results[s.Name + '_simulated_mcg_per_m3_mx+mp'].append( mcg_per_m3_mean) # since the state is always per m2 for a whole day, it needs to be transformed to the minute sum_for_period = sum([ value[i] / (24 * 60) for key, value in # value[i] is the flux in the i-th soil layer phosphorus_results[s.Name + '_simulated_state_per_m2_mx+mp'].items() if time0 <= cmf.Time(datetime.strptime( key, '%d.%m.%Y %H:%M')) < time1 ]) # maybe for testing: concentration * water_amount should be the same simulation_results[s.Name + '_simulated_state_per_m2_mx+mp'].append( sum_for_period) return simulation_results
def test_timeseries_from_scalar(self): ts = cmf.timeseries.from_scalar(1.0) self.assertEqual(len(ts), 1) self.assertEqual(ts[0], 1.0) with self.assertRaises(IndexError): _ = ts[1] self.assertEqual(ts[cmf.Time(1, 1, 2018)], 1.0)
def solve(self, cmf_project, tolerance): """Solves the model""" # Create solver, set time and set up results solver = cmf.CVodeIntegrator(cmf_project, tolerance) solver.t = cmf.Time(1, 1, 2017) self.config_outputs(cmf_project) # Save initial conditions to results self.gather_results(cmf_project, solver.t) # Set timer start_time = datetime.now() step = 0 last = start_time # Run solver and save results at each time step for t in solver.run( solver.t, solver.t + timedelta(hours=self.solver_settings['analysis_length']), timedelta(hours=float(self.solver_settings['time_step']))): self.gather_results(cmf_project, t) last = self.print_solver_time(t, start_time, last, step) step += 1 self.solved = True return True
def infiltration_per_time(p, si_con, W0=None): c = p[0] l = c.layers[0] if W0 is not None: si_con.W0 = W0 t = cmf.Time() solver = cmf.RKFIntegrator(p, 1e-9) solver.t = t l.volume = 0.0 c.surfacewater.volume = 0.0 I = c.get_rainfall(t) sw_vol = [c.surfacewater.volume] l_vol = [l.volume] q_inf = [c.surfacewater.flux_to(l, t) / I] inf_ex = [c.surfacewater.waterbalance(t) / I] t_list = [t / cmf.day + 1] for t in solver.run(solver.t, solver.t + cmf.day * 2, cmf.min * 10): sw_vol.append(c.surfacewater.volume / l.get_capacity()) l_vol.append(l.volume / l.get_capacity()) q_inf.append(c.surfacewater.flux_to(l, t) / I) inf_ex.append(c.surfacewater.waterbalance(t) / I) t_list.append(t / cmf.h) return t_list, sw_vol, l_vol, q_inf, inf_ex
def __call__(self, test): for stress in [cmf.VolumeStress(15, 5), cmf.ContentStress(), cmf.SuctionStress()]: self.layer.wetness = 0.31 # roughly pF=2.5 self.cell.set_uptakestress(stress) solver = cmf.HeunIntegrator(self.project) solver.t = cmf.Time(1, 6, 2019) for _ in solver.run(solver.t, solver.t + cmf.day * 10, cmf.day): test.assertGreater(self.layer.wetness, 0.1)
def create_time_series(analysis_length, time_step=1.0): # Start date is the 1st of January 2017 at 00:00 start = cmf.Time(1, 1, 2017, 0, 0) step = cmf.h * time_step # Create time series return cmf.timeseries(begin=start, step=step, count=analysis_length)
def test_constant_flux(self): p, w, o = get_project() cmf.ConstantFlux(w, o, 1.0, 1.0, cmf.day) t0 = cmf.Time() def assert_flux(vol, flux): w.volume = vol self.assertAlmostEqual(w.flux_to(o, t0), flux) for vol, flux in [(3, 1), (2, 1), (1.5, 0.5), (1.0, 0.0), (0.0, 0.0)]: assert_flux(vol, flux)
def format_water_results(approach, water_results): """ TEXT :param water_results: :param approach: :return: """ # save depth of lysimeters and number of corresponding soil layers: depth, layers = depth_and_layers(approach) # begin of the irrigation experiment begin = cmf.Time(12, 6, 2018, 10, 1) simulation_results = { 'simulated_flux_l_per_m2_per_day': [], 'amount_simulated_l_per_m2': [] } for index, row in approach.evaluation_df.iterrows(): # i is the index from simulation results: depth.index chooses the index of depth (0,1,2) according to depth of # lysimeter; layers[depth.index] uses this index to find the right index of water_results (7,18,28) i = layers[depth.index(row['depth [m]'])] time0, time1 = timespan(begin, row) # mean flux in l per m2 per sec over time period t0:t1: mean_for_period = statistics.mean([ value[i] for key, value in # value[i] is the flux in the i-th soil layer water_results['simulated_flux_l_per_m2_per_day'].items() if time0 <= cmf.Time(datetime.strptime(key, '%d.%m.%Y %H:%M')) < time1 ]) simulation_results['simulated_flux_l_per_m2_per_day'].append( mean_for_period) # L over time period t0:t1: amount_for_period = (mean_for_period * int(row['duration [min]'])) / (24 * 60) simulation_results['amount_simulated_l_per_m2'].append( amount_for_period) return simulation_results
def __init__(self, water_params, spotpy_soil_params=True, irrigation=1, profile=1, flow_approach=3, mode='water', file_name='results/LHS_FF3_P1'): self.project = None self.mode = mode self.flow_approach = flow_approach self.irrigation = irrigation self.profile = profile self.spotpy_soil_params = spotpy_soil_params if mode == 'water': self.water_params = self.create_water_parameters( vgm_via_spotpy=self.spotpy_soil_params) else: self.water_params = water_params if mode == 'phosphorus': self.phosphorus_params = self.create_phosphorus_parameters() else: self.phosphorus_params = None self.begin = cmf.Time(12, 6, 2018, 0, 00) # starting time of solute_results self.dt = cmf.min # time steps (cmf.sec, cmf.min, cmf.h, cmf.day, cmf.week, cmf.month, cmf.year) self.evaluation_df = iao.evaluation_water_df( source=Path('input/MIT_Evaluation.csv'), irrigation=self.irrigation, profile=self.profile) self.error_file = Path(file_name + '_errors.csv') if self.mode == 'phosphorus': iao.write_error_file(spotpy=self.phosphorus_params, name=self.error_file) self.evaluation_df = iao.evaluation_phosphorus_df( self.evaluation_df) self.tracer = 'dp pp' # self.tracer = 'dip dop pp' else: iao.write_error_file(spotpy=self.water_params, name=self.error_file) self.tracer = '' self.sim_stop = max( self.evaluation_df['time [min]'] ) # time when last measurement was taken (min after start)
def get_runoff(v, beta=1, residual=0): """ Calculates runoff from W1 to W2 when volume of W1 is v with beta and residual """ cmf.PowerLawConnection(W1, W2, Q0=1, V0=1, beta=beta, residual=residual) W1.volume = v return W1.flux_to(W2, cmf.Time())
def test_constant_state_flux(self): p, w, o = get_project() o.is_source = True cmf.ConstantStateFlux(w, o, 1.0, cmf.day) t0 = cmf.Time() def assert_flux(vol, flux): w.volume = vol self.assertAlmostEqual(w.flux_to(o, t0), flux) for vol, flux in [(2, 1), (1, 0), (1.5, 0.5), (0.0, -1.0)]: assert_flux(vol, flux)
def test_balance_flux(self): p, w, o = get_project() i = p.NewNeumannBoundary('input', w) o.is_source = True cmf.WaterBalanceFlux(w, o) t0 = cmf.Time() def assert_flux(influx, outflux): i.flux = influx self.assertAlmostEqual(w.flux_to(o, t0), outflux) for flux in np.arange(0, 10, 0.1): assert_flux(flux, flux)
def test_timeseries_pickle(self): begin = cmf.Time(1, 1, 2012) step = cmf.h ots = cmf.timeseries.from_sequence(begin, step, range(100)) pickled_ts = pickle.dumps(ots) nts = pickle.loads(pickled_ts) self.assertEqual(nts.begin, ots.begin) self.assertEqual(nts.step, ots.step) self.assertEqual(len(nts), len(ots)) for o, n in zip(ots, nts): self.assertEqual(o, n) for t in nts.iter_time(): self.assertEqual(ots[t], nts[t])
def storage_runoff(p, si_con, W0=None): c = p[0] l = c.layers[0] if W0 is not None: si_con.W0 = W0 t = cmf.Time() def get_inf(W): l.volume = W * l.get_capacity() I = c.get_rainfall(t) q_inf = c.surfacewater.flux_to(l, t) / I inf_ex = c.surfacewater.waterbalance(t) / I return q_inf, inf_ex wetness = np.linspace(0, 1, 1001) q_inf, inf_ex = np.transpose([get_inf(W) for W in wetness]) return wetness, q_inf, inf_ex
def test_solver_run(self): for st in solver_types: p, stores, X = get_project(False) solver = st(p) # Test all run parameters for t in solver.run(cmf.Time(), cmf.day, cmf.h, reset=False, max_errors=2): pass self.assertEqual(solver.t, cmf.day) # Test for few parameters in run for t in solver.run(step=cmf.h): pass self.assertEqual(solver.t, cmf.day + 100 * cmf.h)
def plot_Vt(self, label=None): """ Plots an integration of the Volume over 1 week starting with a volume of 2 m³ in W1 and an empty W2 """ self.w1.volume = 2.0 self.w2.volume = 0.0 label = label or self.con.to_string() solver = cmf.CVodeIntegrator(self.p, 1e-9) V0 = self.w1.volume t, vol = zip(*[(0, V0)] + [(t / cmf.day, self.w1.volume) for t in solver.run(cmf.Time(), cmf.week, cmf.h)]) plt.plot(t, vol, label=label) plt.xlabel(r'$t\ in\ days$') plt.ylabel(r'$V\ in\ m^3$') plt.yticks([0, 1, 2], ['0', '$V_0$', '$V(t_0)$']) plt.grid(True)
def connector_matrix(states, compression_factor=1): """ Returns a matrix that shows the connectivity between the given states :param states: A sequence of states to create the matrix :param size: Large matrices can compressed with a factor. :return: A symmetric 2d matrix with 1 for connected states and 0 for unconnected states. Compressed matrices contain larger numbers for the count of connection in the compressed field """ l = len(states) size = (l // compression_factor, l // compression_factor) jac = numpy.zeros(size, dtype=int) posdict = {a.node_id: i for i, a in enumerate(states)} for i, a in enumerate(states): for f, t in a.fluxes(cmf.Time()): j = posdict.get(t.node_id) if j: jac[i * size[0] // l, j * size[1] // l] += 1 return jac
def _set_time(self,dt_min,duration): """ Sets the timing of the model (temporal resolution and duration) Parameters ---------- dt_min : float Time step [min]. duration : int Duration of the simulation [m]. Returns ------- None. """ self.tstart = cmf.Time(1, 7, 2019) self.dt = cmf.min * dt_min self.dt_min = dt_min self.duration = cmf.min*duration
def solve_project(cmf_project: cmf.project, solver_settings: dict, outputs: dict) -> dict: """Solves the model""" logger.info('Initializing solving of CMF project') # Create solver, set time and set up results solver = cmf.CVodeIntegrator(cmf_project, solver_settings['tolerance']) solver.t = cmf.Time(solver_settings['start_time']['day'], solver_settings['start_time']['month'], solver_settings['start_time']['year']) logger.debug(f'Solver start time: {solver.t}') results = config_outputs(cmf_project, outputs) # Save initial conditions to results gather_results(cmf_project, results, solver.t) analysis_length = get_analysis_length(solver_settings['analysis_length']) time_step = get_time_step(solver_settings['time_step']) number_of_steps = analysis_length.total_seconds( ) / time_step.total_seconds() # Run solver and save results at each time step widgets = [ ' [', progressbar.Timer(), '] ', progressbar.Bar(), ' [', progressbar.AdaptiveETA(), ' ]' ] bar = progressbar.ProgressBar(max_value=number_of_steps, widgets=widgets) for index, time_ in enumerate( solver.run(solver.t, solver.t + analysis_length, time_step)): gather_results(cmf_project, results, time_) bar.update(index) logger.info('Solved CMF project!') return results
def run(self, nsteps, dt, startvol): """ Parameters ---------- nsteps: int Number of steps dt: cmf.Time Timestep length Returns ------- """ solver = cmf.ImplicitEuler(self.project) for s in self.storages: s.volume = 0.0 self.storages[-1].volume = startvol return [ self.outlet(t) for t in solver.run(cmf.Time(), dt * nsteps, dt) ]
def setUp(self): self.t1 = cmf.Time(1, 1, 2018, 12, 13, 59) self.t2 = cmf.Time(2, 1, 2018, 12, 13, 59)
def simple1D(): from matplotlib import pyplot as plt import cmf from datetime import datetime, timedelta project = cmf.project() # Add one cell at position (0,0,0), Area=1000m2 cell = project.NewCell(0, 0, 0, 1000, with_surfacewater=True) # Create a retention curve r_curve = cmf.VanGenuchtenMualem(Ksat=1, phi=0.5, alpha=0.01, n=2.0) # Add ten layers of 10cm thickness for i in range(10): depth = (i + 1) * 0.1 cell.add_layer(depth, r_curve) # Connect layers with Richards perc. # this can be shorten as cell.install_connection(cmf.Richards) # Create solver solver = cmf.CVodeIntegrator(project, 1e-6) solver.t = cmf.Time(1, 1, 2011) # Create groundwater boundary (uncomment to use it) # Create the boundary condition gw = project.NewOutlet('groundwater', x=0, y=0, z=-1.1) # Set the potential gw.potential = -2 # Connect the lowest layer to the groundwater using Richards percolation gw_flux=cmf.Richards(cell.layers[-1],gw) # Set inital conditions # Set all layers to a potential of -2 m cell.saturated_depth = 2. # 100 mm water in the surface water storage cell.surfacewater.depth = 0.1 # The run time loop, run for 72 hours # Save potential and soil moisture for each layer potential = [cell.layers.potential] moisture = [cell.layers.theta] for t in solver.run(solver.t, solver.t + timedelta(days=7), timedelta(hours=1)): potential.append(cell.layers.potential) moisture.append(cell.layers.theta) """ # Plot results plt.subplot(211) plt.plot(moisture) plt.ylabel(r'Soil moisture $\theta [m^3/m^3]$') plt.xlabel(r'$time [h]$') plt.grid() plt.subplot(212) plt.plot(potential) plt.ylabel(r'Water head $\Psi_{tot} [m]$') plt.xlabel(r'$time [h]$') plt.grid() plt.show() """ print(cell.vegetation)
# Use matrix infiltration as connection between surface water and first layer c.install_connection(cmf.MatrixInfiltration) # Create a snow storage and use a simple Temperature index model as a connection between snow and surfacewater c.install_connection(cmf.SimpleTindexSnowMelt) # Use Penman-Monteith for ET c.install_connection(cmf.ShuttleworthWallace) c.vegetation.stomatal_resistance = 200 # Make an outlet (Groundwater as a boundary condition) groundwater = p.NewOutlet('outlet', 0, 0, -4.4) # Connect last layer with the groundwater, using Richards equation cmf.Richards(c.layers[-1], groundwater) # load meteorological data load_meteo(p) # Make solver solver = cmf.CVodeIntegrator(p, 1e-9) solver.t = cmf.Time(1, 11, 1980) def run(until=cmf.year, dt=cmf.day): """Runs a the model, and saves the outflow""" # Create a timeseries for the outflow outflow = cmf.timeseries(solver.t, dt) # Create a list to save the layer wetness wetness = [] # Get the end time until = until if until > solver.t else solver.t + until # The run time loop. Iterates over the outer timestep of the model # Internally, the model may take shorter timesteps try:
def Q(V): self.w1.volume = V self.w2.volume = 0.5 return self.w1.flux_to(self.w2, cmf.Time())
def __init__( self, water_params=None, phosphorus_params=None, spotpy_soil_params=False, irrigation=1, profile=1, fast_component=3, tracer='', begin=cmf.Time(1, 1, 2019, 0, 00), cell=(0, 0, 0, 1000, True), # IMPORTANT: now the area is 1000 m2 surface_runoff=True, **kwargs): """ Creates the basic structure of the model. :param water_params: spotpy (or determined) parameter for water :param phosphorus_params: spotpy (or determined) parameters for phosphorus :param irrigation: for which irrigation the model is created (important only for setting phosphorus conc) :param profile: the profile for which the model is created (important for soil layers) :param fast_component: either None, MacroporeFastFlow, or BypassFastFlow :param tracer: one or more possible tracer that is/are transported via water, adsorbed and filtered :param begin: cmf.Time object, starting point of solute_results :param cell: tuple describing the simulated cell (x, y, z, area, with surfacewater=true) :param kwargs: further parameters possible """ self.begin = begin self.dt = cmf.min rain = iao.irrigation_experiment(self.begin, self.dt, startup=10 * 60, fade_out=2 * 60) self.duration = len(rain) self.tend = self.begin + self.dt * self.duration super().__init__(tracer) self.c = self.NewCell(*cell) soil = iao.real_soil_from_csv( soil_file=Path('input/MIT' + str(profile) + '_soil.csv')) self.layer_boundary, self.layer_type = self.create_layers( soil, water_params, based_on_spotpy=spotpy_soil_params) self.mx_infiltration, self.mx_percolation = self.connect_matrix() self.gw = self.create_groundwater() if fast_component == 1: self.flow_approach = None elif fast_component == 2: self.flow_approach = BypassFastFlow(self, water_params.ksat_mp) elif fast_component == 3: self.flow_approach = MacroporeFastFlow( self, porefraction_mp=water_params.porefraction_mp, ksat_mp=water_params.ksat_mp, density_mp=water_params.density_mp, k_shape=water_params.k_shape) self.c.surfacewater.puddledepth = water_params.puddle_depth self.c.saturated_depth = water_params.saturated_depth if surface_runoff: self.surface_runoff = self.create_surface_runoff() else: self.surface_runoff = False self.rain_station = self.create_rainfall_station(rain) if phosphorus_params: # self.dip, self.dop, self.pp = self.solutes self.dp, self.pp = self.solutes for s in self.solutes: for layer in self.c.layers: layer.Solute(s).set_abs_errtol(10) self.matrix_filter(phosphorus_params) self.rainstation_concentration(irrigation, profile) self.layer_concentration(phosphorus_params) # self.layer_decay(phosphorus_params) if type(self.flow_approach) == BypassFastFlow: self.bypass_filter(phosphorus_params) elif type(self.flow_approach) == MacroporeFastFlow: self.macropore_filter(phosphorus_params)
def test_timeseries_basic(self): ts = cmf.timeseries(cmf.Time(), cmf.h) self.assertEqual(len(ts), 0) ts.add(1.0) self.assertEqual(len(ts), 1) self.assertEqual(ts.end, cmf.h)
cmf.connect_cells_with_flux(p,cmf.DarcyKinematic) cmf.connect_cells_with_flux(p,cmf.KinematicSurfaceRunoff) # Make an outlet (ditch, 30cm deep) outlet=p.NewOutlet('outlet',-celllength, 0, -.3) # Connect outlet to soil p[0].connect_soil_with_node(outlet,cmf.DarcyKinematic,10.0,5.0) # Connect outlet to surfacewater (overbank flow) cmf.KinematicSurfaceRunoff(p[0].surfacewater, outlet, 10.0, 5.0) load_meteo(p) # Make solver solver=cmf.CVodeIntegrator(p,1e-6) solver.t=cmf.Time(1,1,1990) def run(until,dt=cmf.day): outflow = cmf.timeseries(solver.t,dt) for t in solver.run(solver.t,solver.t+until,dt): outflow.add(outlet(t)) print("%20s - %6.1f l/s" % (t,outlet(t)*1e3)) return outflow if __name__=='__main__': tstart = time.time() outflow=run(cmf.year,cmf.day) print('{:g} s, {} rhs evaluations'.format(time.time()-tstart, solver.get_rhsevals())) if pylab: cmf.draw.plot_timeseries(outflow)
p = cmf.project() # Create cell with 1000m2 and surface water storage c = p.NewCell(0, 0, 1, 1000, True) # Set puddle depth to 2mm c.surfacewater.puddledepth = 0.002 # Add a thick layer, low conductivity. Use Green-Ampt-Infiltration c.add_layer(0.1, cmf.VanGenuchtenMualem(Ksat=0.1)) c.install_connection(cmf.GreenAmptInfiltration) # Create a Neumann Boundary condition connected to W1 In = cmf.NeumannBoundary.create(c.surfacewater) # Create a timeseries with daily alternating values. In.flux = 5 # Create a solver solver = cmf.CVodeIntegrator(p, 1e-8) # Calculate results Vsoil, Vsurf, = transpose([ (c.layers[0].volume, c.surfacewater.volume) for t in solver.run(cmf.Time(1, 1, 2012), cmf.Time(2, 1, 2012), cmf.min) ]) # Present results plt.figure() plt.plot(Vsurf, label='Surface') plt.plot(Vsoil, label='Soil') plt.ylabel('Water content in mm') plt.legend(loc=0) plt.show()