def initialize(self, grid_size, report_interval, run_duration, output_interval, disturbance_rate, weathering_rate, uplift_interval, plot_interval, friction_coef, fault_x, **kwds): """Initialize the grain hill model.""" self.disturbance_rate = disturbance_rate self.weathering_rate = weathering_rate self.uplift_interval = uplift_interval self.plot_interval = plot_interval self.friction_coef = friction_coef # Call base class init super(GrainFacetSimulator, self).initialize(grid_size=grid_size, report_interval=report_interval, grid_orientation='vertical', grid_shape='rect', show_plots=True, cts_type='oriented_hex', run_duration=run_duration, output_interval=output_interval, plot_every_transition=False) ns = self.grid.at_node['node_state'] self.uplifter = LatticeNormalFault(fault_x_intercept=fault_x, grid=self.grid, node_state=ns)
def initializer(self, grid_size, report_interval, run_duration, output_interval, settling_rate, disturbance_rate, weathering_rate, uplift_interval, plot_interval, friction_coef, fault_x, rock_state_for_uplift, opt_rock_collapse, show_plots, initial_state_grid, opt_track_grains, prop_data, prop_reset_value, callback_fn, **kwds): """Initialize the grain hill model.""" self.settling_rate = settling_rate self.disturbance_rate = disturbance_rate self.weathering_rate = weathering_rate self.uplift_interval = uplift_interval self.plot_interval = plot_interval self.friction_coef = friction_coef self.rock_state = rock_state_for_uplift # 7 (resting sed) or 8 (rock) self.opt_track_grains = opt_track_grains self.callback_fn = callback_fn if opt_rock_collapse: self.collapse_rate = self.settling_rate else: self.collapse_rate = 0.0 # Call base class init super(GrainFacet, self).initialize(grid_size=grid_size, report_interval=report_interval, grid_orientation='vertical', grid_shape='rect', show_plots=show_plots, cts_type='oriented_hex', run_duration=run_duration, output_interval=output_interval, initial_state_grid=initial_state_grid, prop_data=prop_data, prop_reset_value=prop_reset_value, **kwds) # Close top and right edges so as to avoid boundary bug issue for edge in (self.grid.nodes_at_right_edge, self.grid.nodes_at_top_edge): self.grid.status_at_node[edge] = CLOSED_BOUNDARY # Set some things related to property-swapping and/or callback fn # if the user wants to track grain motion. #if opt_track_grains: # propid = self.ca.propid #else: # propid = None self.uplifter = LatticeNormalFault( fault_x_intercept=fault_x, grid=self.grid, node_state=self.grid.at_node['node_state'], propid=self.ca.propid, prop_data=self.ca.prop_data, prop_reset_value=self.ca.prop_reset_value) self.initialize_timing(output_interval, plot_interval, uplift_interval, report_interval)
def initialize(self, grid_size, report_interval, run_duration, output_interval, disturbance_rate, weathering_rate, dissolution_rate, uplift_interval, plot_interval, friction_coef, fault_x, cell_width, grav_accel, plot_file_name=None, **kwds): """Initialize the grain hill model.""" self.disturbance_rate = disturbance_rate self.weathering_rate = weathering_rate self.dissolution_rate = dissolution_rate self.uplift_interval = uplift_interval self.plot_interval = plot_interval self.friction_coef = friction_coef self.settling_rate = calculate_settling_rate(cell_width, grav_accel) # Call base class init super(GrainFacetSimulator, self).initialize(grid_size=grid_size, report_interval=report_interval, grid_orientation='vertical', grid_shape='rect', show_plots=True, cts_type='oriented_hex', run_duration=run_duration, output_interval=output_interval, plot_every_transition=False) # Close top and right edges so as to avoid boundary bug issue for edge in (self.grid.nodes_at_right_edge, self.grid.nodes_at_top_edge): self.grid.status_at_node[edge] = CLOSED_BOUNDARY ns = self.grid.at_node['node_state'] self.uplifter = LatticeNormalFault(fault_x_intercept=fault_x, grid=self.grid, node_state=ns) self.plot_file_name = plot_file_name if plot_file_name is not None: self.plot_number = 0 self.plot_to_file()
def initializer(self, grid_size, report_interval, run_duration, output_interval, settling_rate, disturbance_rate, weathering_rate, uplift_interval, plot_interval, friction_coef, fault_x, rock_state_for_uplift, opt_rock_collapse, show_plots, initial_state_grid, opt_track_grains, prop_data, prop_reset_value, callback_fn, **kwds): """Initialize the grain hill model.""" self.settling_rate = settling_rate self.disturbance_rate = disturbance_rate self.weathering_rate = weathering_rate self.uplift_interval = uplift_interval self.plot_interval = plot_interval self.friction_coef = friction_coef self.rock_state = rock_state_for_uplift # 7 (resting sed) or 8 (rock) self.opt_track_grains = opt_track_grains self.callback_fn = callback_fn if opt_rock_collapse: self.collapse_rate = self.settling_rate else: self.collapse_rate = 0.0 # Call base class init super(GrainFacet, self).initialize(grid_size=grid_size, report_interval=report_interval, grid_orientation='vertical', grid_shape='rect', show_plots=show_plots, cts_type='oriented_hex', run_duration=run_duration, output_interval=output_interval, initial_state_grid=initial_state_grid, prop_data=prop_data, prop_reset_value=prop_reset_value, **kwds) # Close top and right edges so as to avoid boundary bug issue for edge in (self.grid.nodes_at_right_edge, self.grid.nodes_at_top_edge): self.grid.status_at_node[edge] = CLOSED_BOUNDARY # Set some things related to property-swapping and/or callback fn # if the user wants to track grain motion. #if opt_track_grains: # propid = self.ca.propid #else: # propid = None self.uplifter = LatticeNormalFault(fault_x_intercept=fault_x, grid=self.grid, node_state=self.grid.at_node['node_state'], propid=self.ca.propid, prop_data=self.ca.prop_data, prop_reset_value=self.ca.prop_reset_value) self.initialize_timing(output_interval, plot_interval, uplift_interval, report_interval)
def test_links_to_update(): """Test that update list includes lower 2 rows and fault-crossing links""" # Create a 6x6 test grid hg = HexModelGrid(6, 6, shape="rect", orientation="vert") lnf = LatticeNormalFault(grid=hg, fault_x_intercept=-0.1) assert_array_equal( lnf.links_to_update, [ 8, 9, 11, 12, 13, 14, 15, 16, 18, 19, 20, 21, 22, 24, 25, 26, 27, 28, 29, 30, 31, 32, 35, 36, 37, 38, 40, 41, 43, 46, 51, 54, 56, 60, 62, 68, 70, 73, 76, 77, 80, ], )
def test_links_to_update(): """Test that update list includes lower 2 rows and fault-crossing links""" # Create a 6x6 test grid hg = HexModelGrid((6, 6), node_layout="rect", orientation="vertical") lnf = LatticeNormalFault(grid=hg, fault_x_intercept=-0.1) assert_array_equal( lnf.links_to_update, [ 6, 7, 9, 10, 11, 12, 13, 14, 16, 17, 18, 19, 20, 22, 23, 24, 25, 26, 27, 28, 29, 30, 33, 34, 35, 36, 38, 39, 41, 44, 49, 52, 54, 58, 60, 66, 68, 71, 74, 75, 78, ], )
def initialize(self, grid_size, report_interval, run_duration, output_interval, disturbance_rate, weathering_rate, dissolution_rate, uplift_interval, baselevel_rise_interval, plot_interval, friction_coef, fault_x, cell_width, grav_accel, plot_file_name=None, **kwds): """Initialize the grain hill model.""" self.disturbance_rate = disturbance_rate self.weathering_rate = weathering_rate self.dissolution_rate = dissolution_rate self.uplift_interval = uplift_interval self.baselevel_rise_interval = baselevel_rise_interval self.plot_interval = plot_interval self.friction_coef = friction_coef self.settling_rate = calculate_settling_rate(cell_width, grav_accel) # Call base class init super(GrainFacetSimulator, self).initialize(grid_size=grid_size, report_interval=report_interval, grid_orientation='vertical', grid_shape='rect', show_plots=True, cts_type='oriented_hex', run_duration=run_duration, output_interval=output_interval, plot_every_transition=False, closed_boundaries=(True, True, False, False)) ns = self.grid.at_node['node_state'] self.uplifter = LatticeNormalFault(fault_x_intercept=fault_x, grid=self.grid, node_state=ns) self.plot_file_name = plot_file_name if plot_file_name is not None: self.plot_number = 0 self.plot_to_file()
class GrainFacet(CTSModel): """ Model hillslope evolution with block uplift. """ def __init__(self, grid_size, report_interval=1.0e8, run_duration=1.0, output_interval=1.0e99, settling_rate=2.2e8, disturbance_rate=1.0, weathering_rate=1.0, uplift_interval=1.0, plot_interval=1.0e99, friction_coef=0.3, fault_x=1.0, rock_state_for_uplift=7, opt_rock_collapse=False, show_plots=True, initial_state_grid=None, opt_track_grains=False, prop_data=None, prop_reset_value=None, callback_fn=None, **kwds): """Call the initialize() method.""" self.initializer(grid_size, report_interval, run_duration, output_interval, settling_rate, disturbance_rate, weathering_rate, uplift_interval, plot_interval, friction_coef, fault_x, rock_state_for_uplift, opt_rock_collapse, show_plots, initial_state_grid, opt_track_grains, prop_data, prop_reset_value, callback_fn, **kwds) def initializer(self, grid_size, report_interval, run_duration, output_interval, settling_rate, disturbance_rate, weathering_rate, uplift_interval, plot_interval, friction_coef, fault_x, rock_state_for_uplift, opt_rock_collapse, show_plots, initial_state_grid, opt_track_grains, prop_data, prop_reset_value, callback_fn, **kwds): """Initialize the grain hill model.""" self.settling_rate = settling_rate self.disturbance_rate = disturbance_rate self.weathering_rate = weathering_rate self.uplift_interval = uplift_interval self.plot_interval = plot_interval self.friction_coef = friction_coef self.rock_state = rock_state_for_uplift # 7 (resting sed) or 8 (rock) self.opt_track_grains = opt_track_grains self.callback_fn = callback_fn if opt_rock_collapse: self.collapse_rate = self.settling_rate else: self.collapse_rate = 0.0 # Call base class init super(GrainFacet, self).initialize(grid_size=grid_size, report_interval=report_interval, grid_orientation='vertical', grid_shape='rect', show_plots=show_plots, cts_type='oriented_hex', run_duration=run_duration, output_interval=output_interval, initial_state_grid=initial_state_grid, prop_data=prop_data, prop_reset_value=prop_reset_value, **kwds) # Set some things related to property-swapping and/or callback fn # if the user wants to track grain motion. #if opt_track_grains: # propid = self.ca.propid #else: # propid = None self.uplifter = LatticeNormalFault( fault_x_intercept=fault_x, grid=self.grid, node_state=self.grid.at_node['node_state'], propid=self.ca.propid, prop_data=self.ca.prop_data, prop_reset_value=self.ca.prop_reset_value) self.initialize_timing(output_interval, plot_interval, uplift_interval, report_interval) def initialize_timing(self, output_interval, plot_interval, uplift_interval, report_interval): """Set up variables related to timing of uplift, output, reporting""" self.current_time = 0.0 # Next time for output to file self.next_output = output_interval # Next time for a plot if self._show_plots: self.next_plot = plot_interval else: self.next_plot = self.run_duration + 1 # Next time for a progress report to user self.next_report = report_interval # Next time to add baselevel adjustment self.next_uplift = uplift_interval # Iteration numbers, for output files self.output_iteration = 1 def node_state_dictionary(self): """ Create and return dict of node states. Overrides base-class method. Here, we simply call on a function in the lattice_grain module. """ return lattice_grain_node_states() def transition_list(self): """ Make and return list of Transition object. """ xn_list = lattice_grain_transition_list(g=self.settling_rate, f=self.friction_coef, motion=self.settling_rate, swap=self.opt_track_grains, callback=self.callback_fn) xn_list = self.add_weathering_and_disturbance_transitions( xn_list, self.disturbance_rate, self.weathering_rate, collapse_rate=self.collapse_rate) return xn_list def add_weathering_and_disturbance_transitions(self, xn_list, d=0.0, w=0.0, collapse_rate=0.0, swap=False, callback=None): """ Add transition rules representing weathering and/or grain disturbance to the list, and return the list. Parameters ---------- xn_list : list of Transition objects List of objects that encode information about the link-state transitions. Normally should first be initialized with lattice-grain transition rules, then passed to this function to add rules for weathering and disturbance. d : float (optional) Rate of transition (1/time) from fluid / resting grain pair to mobile-grain / fluid pair, representing grain disturbance. w : float (optional) Rate of transition (1/time) from fluid / rock pair to fluid / resting-grain pair, representing weathering. Returns ------- xn_list : list of Transition objects Modified transition list. """ # Disturbance rule if d > 0.0: xn_list.append( Transition((7, 0, 0), (0, 1, 0), d, 'disturbance', swap, callback)) xn_list.append( Transition((7, 0, 1), (0, 2, 1), d, 'disturbance', swap, callback)) xn_list.append( Transition((7, 0, 2), (0, 3, 2), d, 'disturbance', swap, callback)) xn_list.append( Transition((0, 7, 0), (4, 0, 0), d, 'disturbance', swap, callback)) xn_list.append( Transition((0, 7, 1), (5, 0, 1), d, 'disturbance', swap, callback)) xn_list.append( Transition((0, 7, 2), (6, 0, 2), d, 'disturbance', swap, callback)) # Weathering rule if w > 0.0: xn_list.append(Transition((8, 0, 0), (7, 0, 0), w, 'weathering')) xn_list.append(Transition((8, 0, 1), (7, 0, 1), w, 'weathering')) xn_list.append(Transition((8, 0, 2), (7, 0, 2), w, 'weathering')) xn_list.append(Transition((0, 8, 0), (0, 7, 0), w, 'weathering')) xn_list.append(Transition((0, 8, 1), (0, 7, 1), w, 'weathering')) xn_list.append(Transition((0, 8, 2), (0, 7, 2), w, 'weathering')) # "Vertical rock collapse" rule: a rock particle overlying air # will collapse, transitioning to a downward-moving grain if collapse_rate > 0.0: xn_list.append( Transition((0, 8, 0), (4, 0, 0), collapse_rate, 'rock collapse', swap, callback)) if _DEBUG: print() print('setup_transition_list(): list has ' + str(len(xn_list)) + ' transitions:') for t in xn_list: print(' From state ' + str(t.from_state) + ' to state ' + str(t.to_state) + ' at rate ' + str(t.rate) + ' called ' + str(t.name)) return xn_list def initialize_node_state_grid(self): """Set up initial node states. Examples -------- >>> gh = GrainFacet((5, 7)) >>> gh.grid.at_node['node_state'] array([8, 7, 7, 8, 7, 7, 7, 0, 7, 7, 0, 7, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) """ # For shorthand, get a reference to the node-state grid nsg = self.grid.at_node['node_state'] # Fill the bottom two rows with grains right_side_x = 0.866025403784 * (self.grid.number_of_node_columns - 1) for i in range(self.grid.number_of_nodes): if self.grid.node_y[i] < 2.0: if (self.grid.node_x[i] > 0.0 and self.grid.node_x[i] < right_side_x): nsg[i] = 7 # Place "wall" particles in the lower-left and lower-right corners if self.grid.number_of_node_columns % 2 == 0: bottom_right = self.grid.number_of_node_columns - 1 else: bottom_right = self.grid.number_of_node_columns // 2 nsg[0] = 8 # bottom left nsg[bottom_right] = 8 return nsg def run(self, to=None): """Run the model.""" if to is None: run_to = self.run_duration else: run_to = to while self.current_time < run_to: # Figure out what time to run to this iteration next_pause = min(self.next_output, self.next_plot) next_pause = min(next_pause, self.next_uplift) next_pause = min(next_pause, run_to) # Once in a while, print out simulation and real time to let the user # know that the sim is running ok current_real_time = time.time() if current_real_time >= self.next_report: print('Current sim time' + str(self.current_time) + '(' + \ str(100 * self.current_time / self.run_duration) + '%)') self.next_report = current_real_time + self.report_interval # Run until next pause self.ca.run(next_pause, self.ca.node_state) self.current_time = next_pause # Handle output to file if self.current_time >= self.next_output: self.write_output(self.grid, 'grain_hill_model', self.output_iteration) self.output_iteration += 1 self.next_output += self.output_interval # Handle plotting on display if self._show_plots and self.current_time >= self.next_plot: self.ca_plotter.update_plot() axis('off') self.next_plot += self.plot_interval # Handle uplift if self.current_time >= self.next_uplift: self.uplifter.do_offset(ca=self.ca, current_time=self.current_time, rock_state=self.rock_state) self.next_uplift += self.uplift_interval def get_profile_and_soil_thickness(self, grid, data): """Calculate and return profiles of elevation and soil thickness. Examples -------- >>> from landlab import HexModelGrid >>> hg = HexModelGrid(4, 5, shape='rect', orientation='vert') >>> ns = hg.add_zeros('node', 'node_state', dtype=int) >>> ns[[0, 3, 1, 6, 4, 9, 2]] = 8 >>> ns[[8, 13, 11, 16, 14]] = 7 >>> gh = GrainHill((3, 7)) # grid size arbitrary here >>> (elev, thickness) = gh.get_profile_and_soil_thickness(hg, ns) >>> elev array([0. , 2.5, 3. , 2.5, 0. ]) >>> thickness array([0., 2., 2., 1., 0.]) """ nc = grid.number_of_node_columns elev = zeros(nc) soil = zeros(nc) for col in range(nc): states = data[grid.nodes[:, col]] (rows_with_rock_or_sed, ) = where(states > 0) if len(rows_with_rock_or_sed) == 0: elev[col] = 0.0 else: elev[col] = amax(rows_with_rock_or_sed) + 0.5 * (col % 2) soil[col] = count_nonzero(logical_and(states > 0, states < 8)) return elev, soil
def initialize(self, grid_size, report_interval, run_duration, output_interval, disturbance_rate, weathering_rate, dissolution_rate, uplift_interval, baselevel_rise_interval, plot_interval, friction_coef, fault_x, cell_width, grav_accel, init_state_grid=None, save_plots=False, plot_filename=None, plot_filetype='.png', seed=0, **kwds): """Initialize the grain hill model.""" self.disturbance_rate = disturbance_rate self.weathering_rate = weathering_rate self.dissolution_rate = dissolution_rate self.uplift_interval = uplift_interval self.baselevel_rise_interval = baselevel_rise_interval self.plot_interval = plot_interval self.friction_coef = friction_coef self.settling_rate = calculate_settling_rate(cell_width, grav_accel) # Call base class init super(GrainFacetSimulator, self).initialize(grid_size=grid_size, report_interval=report_interval, grid_orientation='vertical', grid_shape='rect', show_plots=False, cts_type='oriented_hex', run_duration=run_duration, output_interval=output_interval, plot_every_transition=False, initial_state_grid=init_state_grid, closed_boundaries=(True, True, False, False), seed=seed) ns = self.grid.at_node['node_state'] self.uplifter = LatticeNormalFault(fault_x_intercept=fault_x, grid=self.grid, node_state=ns) # initialize plotting if plot_interval <= run_duration: import matplotlib.pyplot as plt plt.ion() plt.figure(1) self.save_plots = save_plots if save_plots: self.plot_filename = plot_filename self.plot_filetype = plot_filetype nplots = (self.run_duration / self.plot_interval) + 1 self.ndigits = int(np.floor(np.log10(nplots))) + 1 this_filename = (plot_filename + '0'.zfill(self.ndigits) + plot_filetype) print(this_filename) else: this_filename = None plot_hill(self.grid, this_filename) # Work out the next times to plot and output self.next_output = self.output_interval self.next_plot = self.plot_interval self.plot_iteration = 1 # Next time for a progress report to user self.next_report = self.report_interval # And baselevel adjustment self.next_uplift = self.uplift_interval if self.baselevel_rise_interval > 0: self.next_baselevel = self.baselevel_rise_interval self.baselevel_row = 1 else: self.next_baselevel = self.run_duration + 1 self.current_time = 0.0
class GrainFacetSimulator(CTSModel): """ Model facet-slope evolution with 60-degree normal-fault slip. """ def __init__(self, grid_size, report_interval=1.0e8, run_duration=1.0, output_interval=1.0e99, disturbance_rate=0.0, weathering_rate=0.0, dissolution_rate=0.0, uplift_interval=1.0, baselevel_rise_interval=0, plot_interval=1.0e99, friction_coef=0.3, fault_x=1.0, cell_width=1.0, grav_accel=9.8, init_state_grid=None, save_plots=False, plot_filename=None, plot_filetype='.png', seed=0, **kwds): """Call the initialize() method.""" self.initialize(grid_size, report_interval, run_duration, output_interval, disturbance_rate, weathering_rate, dissolution_rate, uplift_interval, baselevel_rise_interval, plot_interval, friction_coef, fault_x,cell_width, grav_accel, init_state_grid, save_plots, plot_filename, plot_filetype, seed, **kwds) def initialize(self, grid_size, report_interval, run_duration, output_interval, disturbance_rate, weathering_rate, dissolution_rate, uplift_interval, baselevel_rise_interval, plot_interval, friction_coef, fault_x, cell_width, grav_accel, init_state_grid=None, save_plots=False, plot_filename=None, plot_filetype='.png', seed=0, **kwds): """Initialize the grain hill model.""" self.disturbance_rate = disturbance_rate self.weathering_rate = weathering_rate self.dissolution_rate = dissolution_rate self.uplift_interval = uplift_interval self.baselevel_rise_interval = baselevel_rise_interval self.plot_interval = plot_interval self.friction_coef = friction_coef self.settling_rate = calculate_settling_rate(cell_width, grav_accel) # Call base class init super(GrainFacetSimulator, self).initialize(grid_size=grid_size, report_interval=report_interval, grid_orientation='vertical', grid_shape='rect', show_plots=False, cts_type='oriented_hex', run_duration=run_duration, output_interval=output_interval, plot_every_transition=False, initial_state_grid=init_state_grid, closed_boundaries=(True, True, False, False), seed=seed) ns = self.grid.at_node['node_state'] self.uplifter = LatticeNormalFault(fault_x_intercept=fault_x, grid=self.grid, node_state=ns) # initialize plotting if plot_interval <= run_duration: import matplotlib.pyplot as plt plt.ion() plt.figure(1) self.save_plots = save_plots if save_plots: self.plot_filename = plot_filename self.plot_filetype = plot_filetype nplots = (self.run_duration / self.plot_interval) + 1 self.ndigits = int(np.floor(np.log10(nplots))) + 1 this_filename = (plot_filename + '0'.zfill(self.ndigits) + plot_filetype) print(this_filename) else: this_filename = None plot_hill(self.grid, this_filename) # Work out the next times to plot and output self.next_output = self.output_interval self.next_plot = self.plot_interval self.plot_iteration = 1 # Next time for a progress report to user self.next_report = self.report_interval # And baselevel adjustment self.next_uplift = self.uplift_interval if self.baselevel_rise_interval > 0: self.next_baselevel = self.baselevel_rise_interval self.baselevel_row = 1 else: self.next_baselevel = self.run_duration + 1 self.current_time = 0.0 def node_state_dictionary(self): """ Create and return dict of node states. Overrides base-class method. Here, we simply call on a function in the lattice_grain module. """ return lattice_grain_node_states() def transition_list(self): """ Make and return list of Transition object. """ xn_list = lattice_grain_transition_list(g=self.settling_rate, f=self.friction_coef, motion=self.settling_rate) xn_list = self.add_weathering_and_disturbance_transitions(xn_list, self.disturbance_rate, self.weathering_rate, self.dissolution_rate) return xn_list def add_weathering_and_disturbance_transitions(self, xn_list, d=0.0, w=0.0, diss=0.0): """ Add transition rules representing weathering and/or grain disturbance to the list, and return the list. Parameters ---------- xn_list : list of Transition objects List of objects that encode information about the link-state transitions. Normally should first be initialized with lattice-grain transition rules, then passed to this function to add rules for weathering and disturbance. d : float (optional, default=0.0) Rate of transition (1/time) from fluid / resting grain pair to mobile-grain / fluid pair, representing grain disturbance. w : float (optional, default=0.0) Rate of transition (1/time) from fluid / rock pair to fluid / resting-grain pair, representing weathering. diss : float (optional, default=0.0) Dissolution: rate of transition from fluid / rock pair to fluid / fluid pair. Returns ------- xn_list : list of Transition objects Modified transition list. """ # Disturbance rule if d > 0.0: xn_list.append( Transition((7,0,0), (0,1,0), d, 'disturbance') ) xn_list.append( Transition((7,0,1), (0,2,1), d, 'disturbance') ) xn_list.append( Transition((7,0,2), (0,3,2), d, 'disturbance') ) xn_list.append( Transition((0,7,0), (4,0,0), d, 'disturbance') ) xn_list.append( Transition((0,7,1), (5,0,1), d, 'disturbance') ) xn_list.append( Transition((0,7,2), (6,0,2), d, 'disturbance') ) # Weathering rule if w > 0.0: xn_list.append( Transition((8,0,0), (7,0,0), w, 'weathering') ) xn_list.append( Transition((8,0,1), (7,0,1), w, 'weathering') ) xn_list.append( Transition((8,0,2), (7,0,2), w, 'weathering') ) xn_list.append( Transition((0,8,0), (0,7,0), w, 'weathering') ) xn_list.append( Transition((0,8,1), (0,7,1), w, 'weathering') ) xn_list.append( Transition((0,8,2), (0,7,2), w, 'weathering') ) # Dissolution rule if diss > 0.0: xn_list.append( Transition((8,0,0), (0,0,0), diss, 'dissolution') ) xn_list.append( Transition((8,0,1), (0,0,1), diss, 'dissolution') ) xn_list.append( Transition((8,0,2), (0,0,2), diss, 'dissolution') ) xn_list.append( Transition((0,8,0), (0,0,0), diss, 'dissolution') ) xn_list.append( Transition((0,8,1), (0,0,1), diss, 'dissolution') ) xn_list.append( Transition((0,8,2), (0,0,2), diss, 'dissolution') ) if _DEBUG: print('') print('setup_transition_list(): list has ' + str(len(xn_list)) + ' transitions:') for t in xn_list: print(' From state ' + str(t.from_state) + ' to state ' + str(t.to_state) + ' at rate ' + str(t.rate) + 'called' + str(t.name)) return xn_list def initialize_node_state_grid(self): """Set up initial node states. Examples -------- >>> from grainhill import GrainHill >>> gh = GrainHill((5, 7)) >>> gh.grid.at_node['node_state'][:20] array([8, 7, 7, 8, 7, 7, 7, 0, 7, 7, 0, 7, 7, 7, 0, 0, 0, 0, 0, 0]) """ # For shorthand, get a reference to the node-state grid nsg = self.grid.at_node['node_state'] # Fill the bottom two rows with grains right_side_x = 0.866025403784 * (self.grid.number_of_node_columns - 1) for i in range(self.grid.number_of_nodes): if self.grid.node_y[i] < 2.0: if (self.grid.node_x[i] > 0.0 and self.grid.node_x[i] < right_side_x): nsg[i] = 8 # Place "wall" particles in the lower-left and lower-right corners if self.grid.number_of_node_columns % 2 == 0: bottom_right = self.grid.number_of_node_columns - 1 else: bottom_right = self.grid.number_of_node_columns // 2 nsg[0] = 8 # bottom left nsg[bottom_right] = 8 return nsg def update_until(self, run_to_time): """Advance up to a specified time.""" while self.current_time < run_to_time: # Figure out what time to run to this iteration next_pause = min(self.next_output, self.next_plot) next_pause = min(next_pause, self.next_uplift) next_pause = min(next_pause, self.next_baselevel) next_pause = min(next_pause, run_to_time) # Once in a while, print out simulation and real time to let the user # know that the sim is running ok current_real_time = time.time() if current_real_time >= self.next_report: print('Current sim time ' + str(self.current_time) + ' (' + \ str(100 * self.current_time / self.run_duration) + '%)') self.next_report = current_real_time + self.report_interval # Run the model forward in time until the next output step print('Running to...' + str(next_pause)) self.ca.run(next_pause, self.ca.node_state) self.current_time = next_pause # Handle output to file if self.current_time >= self.next_output: self.next_output += self.output_interval # Handle plotting on display if self.current_time >= self.next_plot: if self.save_plots: this_filename = (self.plot_filename + str(self.plot_iteration).zfill(self.ndigits) + self.plot_filetype) else: this_filename = None plot_hill(self.grid, this_filename) self.plot_iteration += 1 self.next_plot += self.plot_interval # Handle fault slip if self.current_time >= self.next_uplift: self.uplifter.do_offset(ca=self.ca, current_time=self.current_time, rock_state=8) # for i in range(self.grid.number_of_links): # if self.grid.status_at_link[i] == 4 and self.ca.next_trn_id[i] != -1: # print((i, self.ca.next_trn_id[i])) # print((self.grid.x_of_node[self.grid.node_at_link_tail[i]])) # print((self.grid.y_of_node[self.grid.node_at_link_tail[i]])) # print((self.grid.x_of_node[self.grid.node_at_link_head[i]])) # print((self.grid.y_of_node[self.grid.node_at_link_head[i]])) self.next_uplift += self.uplift_interval # Handle baselevel rise if self.current_time >= self.next_baselevel: self.raise_baselevel(self.baselevel_row) self.baselevel_row += 1 self.next_baselevel += self.baselevel_rise_interval def run(self, to=None): """Run the model.""" if to is None: to = self.run_duration self.update_until(to) def raise_baselevel(self, baselevel_row): """Raise baselevel on left by closing a node on the left boundary. Parameters ---------- baselevel_row : int Row number of the next left-boundary node to be closed Examples -------- >>> params = { 'grid_size' : (10,5), 'baselevel_rise_interval' : 1.0 } >>> params['run_duration'] = 10.0 >>> params['uplift_interval'] = 11.0 >>> gfs = GrainFacetSimulator(**params) >>> gfs.run() Current sim time 0.0 (0.0%) Running to...1.0 Running to...2.0 Running to...3.0 Running to...4.0 Running to...5.0 Running to...6.0 Running to...7.0 Running to...8.0 Running to...9.0 Running to...10.0 >>> gfs.ca.node_state[:50:5] array([8, 8, 8, 8, 8, 8, 8, 8, 8, 8]) """ baselevel_node = self.grid.number_of_node_columns * baselevel_row if baselevel_node < self.grid.number_of_nodes: self.ca.node_state[baselevel_node] = 8 self.ca.bnd_lnk[self.grid.links_at_node[baselevel_node]] = True self.grid.status_at_node[baselevel_node] = self.grid.BC_NODE_IS_CLOSED def nodes_in_column(self, col, num_rows, num_cols): """Return array of node IDs in given column. Examples -------- >>> gfs = GrainFacetSimulator((3, 5)) >>> gfs.nodes_in_column(1, 3, 5) array([ 3, 8, 13]) >>> gfs.nodes_in_column(4, 3, 5) array([ 2, 7, 12]) >>> gfs = GrainFacetSimulator((3, 6)) >>> gfs.nodes_in_column(3, 3, 6) array([ 4, 10, 16]) >>> gfs.nodes_in_column(4, 3, 6) array([ 2, 8, 14]) """ base_node = (col // 2) + (col % 2) * ((num_cols + 1) // 2) num_nodes = num_rows * num_cols return np.arange(base_node, num_nodes, num_cols) def get_profile_and_soil_thickness(self): """Calculate and return the topographic profile and the regolith thickness.""" nr = self.ca.grid.number_of_node_rows nc = self.ca.grid.number_of_node_columns data = self.ca.node_state elev = np.zeros(nc) soil = np.zeros(nc) for c in range(nc): e = (c%2)/2.0 s = 0 r = 0 while r<nr and data[c*nr+r]!=0: e+=1 if data[c*nr+r]==7: s+=1 r+=1 elev[c] = e soil[c] = s return elev, soil def report_info_for_debug(self, current_time): """Print out various bits of data, for testing and debugging.""" print('\n Current time: ' + str(current_time)) print('Node state:') print(self.ca.node_state) for lnk in range(self.grid.number_of_links): if self.grid.status_at_link[lnk] == 0: print((lnk, self.grid.node_at_link_tail[lnk], self.grid.node_at_link_head[lnk], self.ca.node_state[self.grid.node_at_link_tail[lnk]], self.ca.node_state[self.grid.node_at_link_head[lnk]], self.ca.link_state[lnk],self.ca.next_update[lnk], self.ca.next_trn_id[lnk])) print('PQ:') print(self.ca.priority_queue._queue) def plot_to_file(self): """Plot profile of hill to file.""" fname = self.plot_file_name + str(self.plot_number).zfill(4) + '.png' plot_hill(self.ca.grid, filename=fname) self.plot_number += 1
class GrainFacet(CTSModel): """ Model hillslope evolution with block uplift. """ def __init__(self, grid_size, report_interval=1.0e8, run_duration=1.0, output_interval=1.0e99, settling_rate=2.2e8, disturbance_rate=1.0, weathering_rate=1.0, uplift_interval=1.0, plot_interval=1.0e99, friction_coef=0.3, fault_x=1.0, rock_state_for_uplift=7, opt_rock_collapse=False, show_plots=True, initial_state_grid=None, opt_track_grains=False, prop_data=None, prop_reset_value=None, callback_fn=None, **kwds): """Call the initialize() method.""" self.initializer(grid_size, report_interval, run_duration, output_interval, settling_rate, disturbance_rate, weathering_rate, uplift_interval, plot_interval, friction_coef, fault_x, rock_state_for_uplift, opt_rock_collapse, show_plots, initial_state_grid, opt_track_grains, prop_data, prop_reset_value, callback_fn, **kwds) def initializer(self, grid_size, report_interval, run_duration, output_interval, settling_rate, disturbance_rate, weathering_rate, uplift_interval, plot_interval, friction_coef, fault_x, rock_state_for_uplift, opt_rock_collapse, show_plots, initial_state_grid, opt_track_grains, prop_data, prop_reset_value, callback_fn, **kwds): """Initialize the grain hill model.""" self.settling_rate = settling_rate self.disturbance_rate = disturbance_rate self.weathering_rate = weathering_rate self.uplift_interval = uplift_interval self.plot_interval = plot_interval self.friction_coef = friction_coef self.rock_state = rock_state_for_uplift # 7 (resting sed) or 8 (rock) self.opt_track_grains = opt_track_grains self.callback_fn = callback_fn if opt_rock_collapse: self.collapse_rate = self.settling_rate else: self.collapse_rate = 0.0 # Call base class init super(GrainFacet, self).initialize(grid_size=grid_size, report_interval=report_interval, grid_orientation='vertical', grid_shape='rect', show_plots=show_plots, cts_type='oriented_hex', run_duration=run_duration, output_interval=output_interval, initial_state_grid=initial_state_grid, prop_data=prop_data, prop_reset_value=prop_reset_value, **kwds) # Close top and right edges so as to avoid boundary bug issue for edge in (self.grid.nodes_at_right_edge, self.grid.nodes_at_top_edge): self.grid.status_at_node[edge] = CLOSED_BOUNDARY # Set some things related to property-swapping and/or callback fn # if the user wants to track grain motion. #if opt_track_grains: # propid = self.ca.propid #else: # propid = None self.uplifter = LatticeNormalFault(fault_x_intercept=fault_x, grid=self.grid, node_state=self.grid.at_node['node_state'], propid=self.ca.propid, prop_data=self.ca.prop_data, prop_reset_value=self.ca.prop_reset_value) self.initialize_timing(output_interval, plot_interval, uplift_interval, report_interval) def initialize_timing(self, output_interval, plot_interval, uplift_interval, report_interval): """Set up variables related to timing of uplift, output, reporting""" self.current_time = 0.0 # Next time for output to file self.next_output = output_interval # Next time for a plot if self._show_plots: self.next_plot = plot_interval else: self.next_plot = self.run_duration + 1 # Next time for a progress report to user self.next_report = report_interval # Next time to add baselevel adjustment self.next_uplift = uplift_interval # Iteration numbers, for output files self.output_iteration = 1 def node_state_dictionary(self): """ Create and return dict of node states. Overrides base-class method. Here, we simply call on a function in the lattice_grain module. """ return lattice_grain_node_states() def transition_list(self): """ Make and return list of Transition object. """ xn_list = lattice_grain_transition_list(g=self.settling_rate, f=self.friction_coef, motion=self.settling_rate, swap=self.opt_track_grains, callback=self.callback_fn) xn_list = self.add_weathering_and_disturbance_transitions(xn_list, self.disturbance_rate, self.weathering_rate, collapse_rate=self.collapse_rate) return xn_list def add_weathering_and_disturbance_transitions(self, xn_list, d=0.0, w=0.0, collapse_rate=0.0, swap=False, callback=None): """ Add transition rules representing weathering and/or grain disturbance to the list, and return the list. Parameters ---------- xn_list : list of Transition objects List of objects that encode information about the link-state transitions. Normally should first be initialized with lattice-grain transition rules, then passed to this function to add rules for weathering and disturbance. d : float (optional) Rate of transition (1/time) from fluid / resting grain pair to mobile-grain / fluid pair, representing grain disturbance. w : float (optional) Rate of transition (1/time) from fluid / rock pair to fluid / resting-grain pair, representing weathering. Returns ------- xn_list : list of Transition objects Modified transition list. """ # Disturbance rule if d > 0.0: xn_list.append( Transition((7,0,0), (0,1,0), d, 'disturbance', swap, callback) ) xn_list.append( Transition((7,0,1), (0,2,1), d, 'disturbance', swap, callback) ) xn_list.append( Transition((7,0,2), (0,3,2), d, 'disturbance', swap, callback) ) xn_list.append( Transition((0,7,0), (4,0,0), d, 'disturbance', swap, callback) ) xn_list.append( Transition((0,7,1), (5,0,1), d, 'disturbance', swap, callback) ) xn_list.append( Transition((0,7,2), (6,0,2), d, 'disturbance', swap, callback) ) # Weathering rule if w > 0.0: xn_list.append( Transition((8,0,0), (7,0,0), w, 'weathering') ) xn_list.append( Transition((8,0,1), (7,0,1), w, 'weathering') ) xn_list.append( Transition((8,0,2), (7,0,2), w, 'weathering') ) xn_list.append( Transition((0,8,0), (0,7,0), w, 'weathering') ) xn_list.append( Transition((0,8,1), (0,7,1), w, 'weathering') ) xn_list.append( Transition((0,8,2), (0,7,2), w, 'weathering') ) # "Vertical rock collapse" rule: a rock particle overlying air # will collapse, transitioning to a downward-moving grain if collapse_rate > 0.0: xn_list.append( Transition((0,8,0), (4,0,0), collapse_rate, 'rock collapse', swap, callback)) if _DEBUG: print() print('setup_transition_list(): list has ' + str(len(xn_list)) + ' transitions:') for t in xn_list: print(' From state ' + str(t.from_state) + ' to state ' + str(t.to_state) + ' at rate ' + str(t.rate) + ' called ' + str(t.name)) return xn_list def initialize_node_state_grid(self): """Set up initial node states. Examples -------- >>> gh = GrainFacet((5, 7)) >>> gh.grid.at_node['node_state'] array([8, 7, 7, 8, 7, 7, 7, 0, 7, 7, 0, 7, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) """ # For shorthand, get a reference to the node-state grid nsg = self.grid.at_node['node_state'] # Fill the bottom two rows with grains right_side_x = 0.866025403784 * (self.grid.number_of_node_columns - 1) for i in range(self.grid.number_of_nodes): if self.grid.node_y[i] < 2.0: if (self.grid.node_x[i] > 0.0 and self.grid.node_x[i] < right_side_x): nsg[i] = 7 # Place "wall" particles in the lower-left and lower-right corners if self.grid.number_of_node_columns % 2 == 0: bottom_right = self.grid.number_of_node_columns - 1 else: bottom_right = self.grid.number_of_node_columns // 2 nsg[0] = 8 # bottom left nsg[bottom_right] = 8 return nsg def run(self, to=None): """Run the model.""" if to is None: run_to = self.run_duration else: run_to = to while self.current_time < run_to: # Figure out what time to run to this iteration next_pause = min(self.next_output, self.next_plot) next_pause = min(next_pause, self.next_uplift) next_pause = min(next_pause, run_to) # Once in a while, print out simulation and real time to let the user # know that the sim is running ok current_real_time = time.time() if current_real_time >= self.next_report: print('Current sim time' + str(self.current_time) + '(' + \ str(100 * self.current_time / self.run_duration) + '%)') self.next_report = current_real_time + self.report_interval # Run until next pause self.ca.run(next_pause, self.ca.node_state) self.current_time = next_pause # Handle output to file if self.current_time >= self.next_output: self.write_output(self.grid, 'grain_hill_model', self.output_iteration) self.output_iteration += 1 self.next_output += self.output_interval # Handle plotting on display if self._show_plots and self.current_time >= self.next_plot: self.ca_plotter.update_plot() axis('off') self.next_plot += self.plot_interval # Handle uplift if self.current_time >= self.next_uplift: self.uplifter.do_offset( ca=self.ca, current_time=self.current_time, rock_state=self.rock_state) self.next_uplift += self.uplift_interval def get_profile_and_soil_thickness(self, grid, data): """Calculate and return profiles of elevation and soil thickness. Examples -------- >>> from landlab import HexModelGrid >>> hg = HexModelGrid(4, 5, shape='rect', orientation='vert') >>> ns = hg.add_zeros('node', 'node_state', dtype=int) >>> ns[[0, 3, 1, 6, 4, 9, 2]] = 8 >>> ns[[8, 13, 11, 16, 14]] = 7 >>> gh = GrainHill((3, 7)) # grid size arbitrary here >>> (elev, thickness) = gh.get_profile_and_soil_thickness(hg, ns) >>> elev array([0. , 2.5, 3. , 2.5, 0. ]) >>> thickness array([0., 2., 2., 1., 0.]) """ nc = grid.number_of_node_columns elev = zeros(nc) soil = zeros(nc) for col in range(nc): states = data[grid.nodes[:, col]] (rows_with_rock_or_sed, ) = where(states > 0) if len(rows_with_rock_or_sed) == 0: elev[col] = 0.0 else: elev[col] = amax(rows_with_rock_or_sed) + 0.5 * (col % 2) soil[col] = count_nonzero(logical_and(states > 0, states < 8)) return elev, soil
class GrainFacetSimulator(CTSModel): """ Model facet-slope evolution with 60-degree normal-fault slip. """ def __init__(self, grid_size, report_interval=1.0e8, run_duration=1.0, output_interval=1.0e99, disturbance_rate=1.0e-6, weathering_rate=1.0e-6, uplift_interval=1.0, plot_interval=1.0e99, friction_coef=0.3, fault_x=1.0, **kwds): """Call the initialize() method.""" self.initialize(grid_size, report_interval, run_duration, output_interval, disturbance_rate, weathering_rate, uplift_interval, plot_interval, friction_coef, fault_x, **kwds) def initialize(self, grid_size, report_interval, run_duration, output_interval, disturbance_rate, weathering_rate, uplift_interval, plot_interval, friction_coef, fault_x, **kwds): """Initialize the grain hill model.""" self.disturbance_rate = disturbance_rate self.weathering_rate = weathering_rate self.uplift_interval = uplift_interval self.plot_interval = plot_interval self.friction_coef = friction_coef # Call base class init super(GrainFacetSimulator, self).initialize(grid_size=grid_size, report_interval=report_interval, grid_orientation='vertical', grid_shape='rect', show_plots=True, cts_type='oriented_hex', run_duration=run_duration, output_interval=output_interval, plot_every_transition=False) ns = self.grid.at_node['node_state'] self.uplifter = LatticeNormalFault(fault_x_intercept=fault_x, grid=self.grid, node_state=ns) def node_state_dictionary(self): """ Create and return dict of node states. Overrides base-class method. Here, we simply call on a function in the lattice_grain module. """ return lattice_grain_node_states() def transition_list(self): """ Make and return list of Transition object. """ xn_list = lattice_grain_transition_list(g=1.0, f=self.friction_coef) xn_list = self.add_weathering_and_disturbance_transitions( xn_list, self.disturbance_rate, self.weathering_rate) return xn_list def add_weathering_and_disturbance_transitions(self, xn_list, d=0.0, w=0.0): """ Add transition rules representing weathering and/or grain disturbance to the list, and return the list. Parameters ---------- xn_list : list of Transition objects List of objects that encode information about the link-state transitions. Normally should first be initialized with lattice-grain transition rules, then passed to this function to add rules for weathering and disturbance. d : float (optional) Rate of transition (1/time) from fluid / resting grain pair to mobile-grain / fluid pair, representing grain disturbance. w : float (optional) Rate of transition (1/time) from fluid / rock pair to fluid / resting-grain pair, representing weathering. Returns ------- xn_list : list of Transition objects Modified transition list. """ # Disturbance rule xn_list.append(Transition((7, 0, 0), (0, 1, 0), d, 'disturbance')) xn_list.append(Transition((7, 0, 1), (0, 2, 1), d, 'disturbance')) xn_list.append(Transition((7, 0, 2), (0, 3, 2), d, 'disturbance')) xn_list.append(Transition((0, 7, 0), (4, 0, 0), d, 'disturbance')) xn_list.append(Transition((0, 7, 1), (5, 0, 1), d, 'disturbance')) xn_list.append(Transition((0, 7, 2), (6, 0, 2), d, 'disturbance')) # Weathering rule xn_list.append(Transition((8, 0, 0), (7, 0, 0), w, 'weathering')) xn_list.append(Transition((8, 0, 1), (7, 0, 1), w, 'weathering')) xn_list.append(Transition((8, 0, 2), (7, 0, 2), w, 'weathering')) xn_list.append(Transition((0, 8, 0), (0, 7, 0), w, 'weathering')) xn_list.append(Transition((0, 8, 1), (0, 7, 1), w, 'weathering')) xn_list.append(Transition((0, 8, 2), (0, 7, 2), w, 'weathering')) if _DEBUG: print print 'setup_transition_list(): list has', len( xn_list), 'transitions:' for t in xn_list: print ' From state', t.from_state, 'to state', t.to_state, 'at rate', t.rate, 'called', t.name return xn_list def initialize_node_state_grid(self): """Set up initial node states. Examples -------- >>> gh = GrainHill((5, 7)) >>> gh.grid.at_node['node_state'] array([8, 7, 7, 8, 7, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) """ # For shorthand, get a reference to the node-state grid nsg = self.grid.at_node['node_state'] # Fill the bottom two rows with grains right_side_x = 0.866025403784 * (self.grid.number_of_node_columns - 1) for i in range(self.grid.number_of_nodes): if self.grid.node_y[i] < 1.0: if (self.grid.node_x[i] > 0.0 and self.grid.node_x[i] < right_side_x): nsg[i] = 7 # Place "wall" particles in the lower-left and lower-right corners if self.grid.number_of_node_columns % 2 == 0: bottom_right = self.grid.number_of_node_columns - 1 else: bottom_right = self.grid.number_of_node_columns // 2 nsg[0] = 8 # bottom left nsg[bottom_right] = 8 return nsg def run(self): """Run the model.""" # Work out the next times to plot and output next_output = self.output_interval next_plot = self.plot_interval # Next time for a progress report to user next_report = self.report_interval # And baselevel adjustment next_uplift = self.uplift_interval current_time = 0.0 while current_time < self.run_duration: # Figure out what time to run to this iteration next_pause = min(next_output, next_plot) next_pause = min(next_pause, next_uplift) next_pause = min(next_pause, self.run_duration) # Once in a while, print out simulation and real time to let the user # know that the sim is running ok current_real_time = time.time() if current_real_time >= next_report: print('Current sim time' + str(current_time) + '(' + \ str(100 * current_time / self.run_duration) + '%)') next_report = current_real_time + self.report_interval # Run the model forward in time until the next output step print('Running to...' + str(next_pause)) self.ca.run(next_pause, self.ca.node_state) #, #plot_each_transition=plot_every_transition, plotter=ca_plotter) current_time = next_pause # Handle output to file if current_time >= next_output: #write_output(hmg, filenm, output_iteration) #output_iteration += 1 next_output += self.output_interval # Handle plotting on display if current_time >= next_plot: #node_state_grid[hmg.number_of_node_rows-1] = 8 self.ca_plotter.update_plot() axis('off') next_plot += self.plot_interval # Handle fault slip if current_time >= next_uplift: self.uplifter.do_offset(rock_state=8) self.ca.update_link_states_and_transitions(current_time) next_uplift += self.uplift_interval def nodes_in_column(self, col, num_rows, num_cols): """Return array of node IDs in given column. Examples -------- >>> gfs = GrainFacetSimulator((3, 5)) >>> gfs.nodes_in_column(1, 3, 5) array([ 3, 8, 13]) >>> gfs.nodes_in_column(4, 3, 5) array([ 2, 7, 12]) >>> gfs = GrainFacetSimulator((3, 6)) >>> gfs.nodes_in_column(3, 3, 6) array([ 4, 10, 16]) >>> gfs.nodes_in_column(4, 3, 6) array([ 2, 8, 14]) """ base_node = (col // 2) + (col % 2) * ((num_cols + 1) // 2) num_nodes = num_rows * num_cols return np.arange(base_node, num_nodes, num_cols) def get_profile_and_soil_thickness(self): """Calculate and return the topographic profile and the regolith thickness.""" nr = self.ca.grid.number_of_node_rows nc = self.ca.grid.number_of_node_columns data = self.ca.node_state elev = np.zeros(nc) soil = np.zeros(nc) for c in range(nc): e = (c % 2) / 2.0 s = 0 r = 0 while r < nr and data[c * nr + r] != 0: e += 1 if data[c * nr + r] == 7: s += 1 r += 1 elev[c] = e soil[c] = s return elev, soil
class GrainFacetSimulator(CTSModel): """ Model facet-slope evolution with 60-degree normal-fault slip. """ def __init__(self, grid_size, report_interval=1.0e8, run_duration=1.0, output_interval=1.0e99, disturbance_rate=0.0, weathering_rate=0.0, dissolution_rate=0.0, uplift_interval=1.0, baselevel_rise_interval=0, plot_interval=1.0e99, friction_coef=0.3, fault_x=1.0, cell_width=1.0, grav_accel=9.8, plot_file_name=None, **kwds): """Call the initialize() method.""" self.initialize(grid_size, report_interval, run_duration, output_interval, disturbance_rate, weathering_rate, dissolution_rate, uplift_interval, baselevel_rise_interval, plot_interval, friction_coef, fault_x,cell_width, grav_accel, plot_file_name, **kwds) def initialize(self, grid_size, report_interval, run_duration, output_interval, disturbance_rate, weathering_rate, dissolution_rate, uplift_interval, baselevel_rise_interval, plot_interval, friction_coef, fault_x, cell_width, grav_accel, plot_file_name=None, **kwds): """Initialize the grain hill model.""" self.disturbance_rate = disturbance_rate self.weathering_rate = weathering_rate self.dissolution_rate = dissolution_rate self.uplift_interval = uplift_interval self.baselevel_rise_interval = baselevel_rise_interval self.plot_interval = plot_interval self.friction_coef = friction_coef self.settling_rate = calculate_settling_rate(cell_width, grav_accel) # Call base class init super(GrainFacetSimulator, self).initialize(grid_size=grid_size, report_interval=report_interval, grid_orientation='vertical', grid_shape='rect', show_plots=True, cts_type='oriented_hex', run_duration=run_duration, output_interval=output_interval, plot_every_transition=False, closed_boundaries=(True, True, False, False)) ns = self.grid.at_node['node_state'] self.uplifter = LatticeNormalFault(fault_x_intercept=fault_x, grid=self.grid, node_state=ns) self.plot_file_name = plot_file_name if plot_file_name is not None: self.plot_number = 0 self.plot_to_file() def node_state_dictionary(self): """ Create and return dict of node states. Overrides base-class method. Here, we simply call on a function in the lattice_grain module. """ return lattice_grain_node_states() def transition_list(self): """ Make and return list of Transition object. """ xn_list = lattice_grain_transition_list(g=self.settling_rate, f=self.friction_coef, motion=self.settling_rate) xn_list = self.add_weathering_and_disturbance_transitions(xn_list, self.disturbance_rate, self.weathering_rate, self.dissolution_rate) return xn_list def add_weathering_and_disturbance_transitions(self, xn_list, d=0.0, w=0.0, diss=0.0): """ Add transition rules representing weathering and/or grain disturbance to the list, and return the list. Parameters ---------- xn_list : list of Transition objects List of objects that encode information about the link-state transitions. Normally should first be initialized with lattice-grain transition rules, then passed to this function to add rules for weathering and disturbance. d : float (optional, default=0.0) Rate of transition (1/time) from fluid / resting grain pair to mobile-grain / fluid pair, representing grain disturbance. w : float (optional, default=0.0) Rate of transition (1/time) from fluid / rock pair to fluid / resting-grain pair, representing weathering. diss : float (optional, default=0.0) Dissolution: rate of transition from fluid / rock pair to fluid / fluid pair. Returns ------- xn_list : list of Transition objects Modified transition list. """ # Disturbance rule if d > 0.0: xn_list.append( Transition((7,0,0), (0,1,0), d, 'disturbance') ) xn_list.append( Transition((7,0,1), (0,2,1), d, 'disturbance') ) xn_list.append( Transition((7,0,2), (0,3,2), d, 'disturbance') ) xn_list.append( Transition((0,7,0), (4,0,0), d, 'disturbance') ) xn_list.append( Transition((0,7,1), (5,0,1), d, 'disturbance') ) xn_list.append( Transition((0,7,2), (6,0,2), d, 'disturbance') ) # Weathering rule if w > 0.0: xn_list.append( Transition((8,0,0), (7,0,0), w, 'weathering') ) xn_list.append( Transition((8,0,1), (7,0,1), w, 'weathering') ) xn_list.append( Transition((8,0,2), (7,0,2), w, 'weathering') ) xn_list.append( Transition((0,8,0), (0,7,0), w, 'weathering') ) xn_list.append( Transition((0,8,1), (0,7,1), w, 'weathering') ) xn_list.append( Transition((0,8,2), (0,7,2), w, 'weathering') ) # Dissolution rule if diss > 0.0: xn_list.append( Transition((8,0,0), (0,0,0), diss, 'dissolution') ) xn_list.append( Transition((8,0,1), (0,0,1), diss, 'dissolution') ) xn_list.append( Transition((8,0,2), (0,0,2), diss, 'dissolution') ) xn_list.append( Transition((0,8,0), (0,0,0), diss, 'dissolution') ) xn_list.append( Transition((0,8,1), (0,0,1), diss, 'dissolution') ) xn_list.append( Transition((0,8,2), (0,0,2), diss, 'dissolution') ) if _DEBUG: print('') print('setup_transition_list(): list has ' + str(len(xn_list)) + ' transitions:') for t in xn_list: print(' From state ' + str(t.from_state) + ' to state ' + str(t.to_state) + ' at rate ' + str(t.rate) + 'called' + str(t.name)) return xn_list def initialize_node_state_grid(self): """Set up initial node states. Examples -------- >>> from grainhill import GrainHill >>> gh = GrainHill((5, 7)) >>> gh.grid.at_node['node_state'][:20] array([8, 7, 7, 8, 7, 7, 7, 0, 7, 7, 0, 7, 7, 7, 0, 0, 0, 0, 0, 0]) """ # For shorthand, get a reference to the node-state grid nsg = self.grid.at_node['node_state'] # Fill the bottom two rows with grains right_side_x = 0.866025403784 * (self.grid.number_of_node_columns - 1) for i in range(self.grid.number_of_nodes): if self.grid.node_y[i] < 2.0: if (self.grid.node_x[i] > 0.0 and self.grid.node_x[i] < right_side_x): nsg[i] = 8 # Place "wall" particles in the lower-left and lower-right corners if self.grid.number_of_node_columns % 2 == 0: bottom_right = self.grid.number_of_node_columns - 1 else: bottom_right = self.grid.number_of_node_columns // 2 nsg[0] = 8 # bottom left nsg[bottom_right] = 8 return nsg def run(self): """Run the model.""" # Work out the next times to plot and output next_output = self.output_interval next_plot = self.plot_interval # Next time for a progress report to user next_report = self.report_interval # And baselevel adjustment next_uplift = self.uplift_interval if self.baselevel_rise_interval > 0: next_baselevel = self.baselevel_rise_interval baselevel_row = 1 else: next_baselevel = self.run_duration + 1 current_time = 0.0 while current_time < self.run_duration: # Figure out what time to run to this iteration next_pause = min(next_output, next_plot) next_pause = min(next_pause, next_uplift) next_pause = min(next_pause, next_baselevel) next_pause = min(next_pause, self.run_duration) # Once in a while, print out simulation and real time to let the user # know that the sim is running ok current_real_time = time.time() if current_real_time >= next_report: print('Current sim time' + str(current_time) + '(' + \ str(100 * current_time / self.run_duration) + '%)') next_report = current_real_time + self.report_interval # Run the model forward in time until the next output step print('Running to...' + str(next_pause)) self.ca.run(next_pause, self.ca.node_state) current_time = next_pause # Handle output to file if current_time >= next_output: next_output += self.output_interval # Handle plotting on display if current_time >= next_plot: self.ca_plotter.update_plot() if self.plot_file_name is not None: self.plot_to_file() next_plot += self.plot_interval # Handle fault slip if current_time >= next_uplift: self.uplifter.do_offset(ca=self.ca, current_time=current_time, rock_state=8) for i in range(self.grid.number_of_links): if self.grid.status_at_link[i] == 4 and self.ca.next_trn_id[i] != -1: print i next_uplift += self.uplift_interval # Handle baselevel rise if current_time >= next_baselevel: self.raise_baselevel(baselevel_row) baselevel_row += 1 next_baselevel += self.baselevel_rise_interval def raise_baselevel(self, baselevel_row): """Raise baselevel on left by closing a node on the left boundary. Parameters ---------- baselevel_row : int Row number of the next left-boundary node to be closed Examples -------- >>> params = { 'grid_size' : (10,5), 'baselevel_rise_interval' : 1.0 } >>> params['run_duration'] = 10.0 >>> params['uplift_interval'] = 11.0 >>> gfs = GrainFacetSimulator(**params) >>> gfs.run() Current sim time0.0(0.0%) Running to...1.0 Running to...2.0 Running to...3.0 Running to...4.0 Running to...5.0 Running to...6.0 Running to...7.0 Running to...8.0 Running to...9.0 Running to...10.0 >>> gfs.ca.node_state[:50:5] array([8, 8, 8, 8, 8, 8, 8, 8, 8, 8]) """ baselevel_node = self.grid.number_of_node_columns * baselevel_row if baselevel_node < self.grid.number_of_nodes: self.ca.node_state[baselevel_node] = 8 self.ca.bnd_lnk[self.grid.links_at_node[baselevel_node]] = True self.grid.status_at_node[baselevel_node] = CLOSED_BOUNDARY def nodes_in_column(self, col, num_rows, num_cols): """Return array of node IDs in given column. Examples -------- >>> gfs = GrainFacetSimulator((3, 5)) >>> gfs.nodes_in_column(1, 3, 5) array([ 3, 8, 13]) >>> gfs.nodes_in_column(4, 3, 5) array([ 2, 7, 12]) >>> gfs = GrainFacetSimulator((3, 6)) >>> gfs.nodes_in_column(3, 3, 6) array([ 4, 10, 16]) >>> gfs.nodes_in_column(4, 3, 6) array([ 2, 8, 14]) """ base_node = (col // 2) + (col % 2) * ((num_cols + 1) // 2) num_nodes = num_rows * num_cols return np.arange(base_node, num_nodes, num_cols) def get_profile_and_soil_thickness(self): """Calculate and return the topographic profile and the regolith thickness.""" nr = self.ca.grid.number_of_node_rows nc = self.ca.grid.number_of_node_columns data = self.ca.node_state elev = np.zeros(nc) soil = np.zeros(nc) for c in range(nc): e = (c%2)/2.0 s = 0 r = 0 while r<nr and data[c*nr+r]!=0: e+=1 if data[c*nr+r]==7: s+=1 r+=1 elev[c] = e soil[c] = s return elev, soil def report_info_for_debug(self, current_time): """Print out various bits of data, for testing and debugging.""" print('\n Current time: ' + str(current_time)) print('Node state:') print(self.ca.node_state) for lnk in range(self.grid.number_of_links): if self.grid.status_at_link[lnk] == 0: print((lnk, self.grid.node_at_link_tail[lnk], self.grid.node_at_link_head[lnk], self.ca.node_state[self.grid.node_at_link_tail[lnk]], self.ca.node_state[self.grid.node_at_link_head[lnk]], self.ca.link_state[lnk],self.ca.next_update[lnk], self.ca.next_trn_id[lnk])) print('PQ:') print(self.ca.priority_queue._queue) def plot_to_file(self): """Plot profile of hill to file.""" fname = self.plot_file_name + str(self.plot_number).zfill(4) + '.png' plot_hill(self.ca.grid, filename=fname) self.plot_number += 1
class GrainFacetSimulator(CTSModel): """ Model facet-slope evolution with 60-degree normal-fault slip. """ def __init__(self, grid_size, report_interval=1.0e8, run_duration=1.0, output_interval=1.0e99, disturbance_rate=0.0, weathering_rate=0.0, dissolution_rate=0.0, uplift_interval=1.0, plot_interval=1.0e99, friction_coef=0.3, fault_x=1.0, cell_width=1.0, grav_accel=9.8, plot_file_name=None, **kwds): """Call the initialize() method.""" self.initialize(grid_size, report_interval, run_duration, output_interval, disturbance_rate, weathering_rate, dissolution_rate, uplift_interval, plot_interval, friction_coef, fault_x, cell_width, grav_accel, plot_file_name, **kwds) def initialize(self, grid_size, report_interval, run_duration, output_interval, disturbance_rate, weathering_rate, dissolution_rate, uplift_interval, plot_interval, friction_coef, fault_x, cell_width, grav_accel, plot_file_name=None, **kwds): """Initialize the grain hill model.""" self.disturbance_rate = disturbance_rate self.weathering_rate = weathering_rate self.dissolution_rate = dissolution_rate self.uplift_interval = uplift_interval self.plot_interval = plot_interval self.friction_coef = friction_coef self.settling_rate = calculate_settling_rate(cell_width, grav_accel) # Call base class init super(GrainFacetSimulator, self).initialize(grid_size=grid_size, report_interval=report_interval, grid_orientation='vertical', grid_shape='rect', show_plots=True, cts_type='oriented_hex', run_duration=run_duration, output_interval=output_interval, plot_every_transition=False) # Close top and right edges so as to avoid boundary bug issue for edge in (self.grid.nodes_at_right_edge, self.grid.nodes_at_top_edge): self.grid.status_at_node[edge] = CLOSED_BOUNDARY ns = self.grid.at_node['node_state'] self.uplifter = LatticeNormalFault(fault_x_intercept=fault_x, grid=self.grid, node_state=ns) self.plot_file_name = plot_file_name if plot_file_name is not None: self.plot_number = 0 self.plot_to_file() def node_state_dictionary(self): """ Create and return dict of node states. Overrides base-class method. Here, we simply call on a function in the lattice_grain module. """ return lattice_grain_node_states() def transition_list(self): """ Make and return list of Transition object. """ xn_list = lattice_grain_transition_list(g=self.settling_rate, f=self.friction_coef, motion=self.settling_rate) xn_list = self.add_weathering_and_disturbance_transitions( xn_list, self.disturbance_rate, self.weathering_rate, self.dissolution_rate) return xn_list def add_weathering_and_disturbance_transitions(self, xn_list, d=0.0, w=0.0, diss=0.0): """ Add transition rules representing weathering and/or grain disturbance to the list, and return the list. Parameters ---------- xn_list : list of Transition objects List of objects that encode information about the link-state transitions. Normally should first be initialized with lattice-grain transition rules, then passed to this function to add rules for weathering and disturbance. d : float (optional, default=0.0) Rate of transition (1/time) from fluid / resting grain pair to mobile-grain / fluid pair, representing grain disturbance. w : float (optional, default=0.0) Rate of transition (1/time) from fluid / rock pair to fluid / resting-grain pair, representing weathering. diss : float (optional, default=0.0) Dissolution: rate of transition from fluid / rock pair to fluid / fluid pair. Returns ------- xn_list : list of Transition objects Modified transition list. """ # Disturbance rule if d > 0.0: xn_list.append(Transition((7, 0, 0), (0, 1, 0), d, 'disturbance')) xn_list.append(Transition((7, 0, 1), (0, 2, 1), d, 'disturbance')) xn_list.append(Transition((7, 0, 2), (0, 3, 2), d, 'disturbance')) xn_list.append(Transition((0, 7, 0), (4, 0, 0), d, 'disturbance')) xn_list.append(Transition((0, 7, 1), (5, 0, 1), d, 'disturbance')) xn_list.append(Transition((0, 7, 2), (6, 0, 2), d, 'disturbance')) # Weathering rule if w > 0.0: xn_list.append(Transition((8, 0, 0), (7, 0, 0), w, 'weathering')) xn_list.append(Transition((8, 0, 1), (7, 0, 1), w, 'weathering')) xn_list.append(Transition((8, 0, 2), (7, 0, 2), w, 'weathering')) xn_list.append(Transition((0, 8, 0), (0, 7, 0), w, 'weathering')) xn_list.append(Transition((0, 8, 1), (0, 7, 1), w, 'weathering')) xn_list.append(Transition((0, 8, 2), (0, 7, 2), w, 'weathering')) # Dissolution rule if diss > 0.0: xn_list.append( Transition((8, 0, 0), (0, 0, 0), diss, 'dissolution')) xn_list.append( Transition((8, 0, 1), (0, 0, 1), diss, 'dissolution')) xn_list.append( Transition((8, 0, 2), (0, 0, 2), diss, 'dissolution')) xn_list.append( Transition((0, 8, 0), (0, 0, 0), diss, 'dissolution')) xn_list.append( Transition((0, 8, 1), (0, 0, 1), diss, 'dissolution')) xn_list.append( Transition((0, 8, 2), (0, 0, 2), diss, 'dissolution')) if _DEBUG: print('') print('setup_transition_list(): list has ' + str(len(xn_list)) + ' transitions:') for t in xn_list: print(' From state ' + str(t.from_state) + ' to state ' + str(t.to_state) + ' at rate ' + str(t.rate) + 'called' + str(t.name)) return xn_list def initialize_node_state_grid(self): """Set up initial node states. Examples -------- >>> from grainhill import GrainHill >>> gh = GrainHill((5, 7)) >>> gh.grid.at_node['node_state'] array([8, 7, 7, 8, 7, 7, 7, 0, 7, 7, 0, 7, 7, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) """ # For shorthand, get a reference to the node-state grid nsg = self.grid.at_node['node_state'] # Fill the bottom two rows with grains right_side_x = 0.866025403784 * (self.grid.number_of_node_columns - 1) for i in range(self.grid.number_of_nodes): if self.grid.node_y[i] < 2.0: if (self.grid.node_x[i] > 0.0 and self.grid.node_x[i] < right_side_x): nsg[i] = 8 # Place "wall" particles in the lower-left and lower-right corners if self.grid.number_of_node_columns % 2 == 0: bottom_right = self.grid.number_of_node_columns - 1 else: bottom_right = self.grid.number_of_node_columns // 2 nsg[0] = 8 # bottom left nsg[bottom_right] = 8 return nsg def run(self): """Run the model.""" # Work out the next times to plot and output next_output = self.output_interval next_plot = self.plot_interval # Next time for a progress report to user next_report = self.report_interval # And baselevel adjustment next_uplift = self.uplift_interval current_time = 0.0 while current_time < self.run_duration: # Figure out what time to run to this iteration next_pause = min(next_output, next_plot) next_pause = min(next_pause, next_uplift) next_pause = min(next_pause, self.run_duration) # Once in a while, print out simulation and real time to let the user # know that the sim is running ok current_real_time = time.time() if current_real_time >= next_report: print('Current sim time' + str(current_time) + '(' + \ str(100 * current_time / self.run_duration) + '%)') next_report = current_real_time + self.report_interval # Run the model forward in time until the next output step print('Running to...' + str(next_pause)) self.ca.run(next_pause, self.ca.node_state) current_time = next_pause # Handle output to file if current_time >= next_output: next_output += self.output_interval # Handle plotting on display if current_time >= next_plot: self.ca_plotter.update_plot() if self.plot_file_name is not None: self.plot_to_file() next_plot += self.plot_interval # Handle fault slip if current_time >= next_uplift: self.uplifter.do_offset(ca=self.ca, current_time=current_time, rock_state=8) next_uplift += self.uplift_interval def nodes_in_column(self, col, num_rows, num_cols): """Return array of node IDs in given column. Examples -------- >>> gfs = GrainFacetSimulator((3, 5)) >>> gfs.nodes_in_column(1, 3, 5) array([ 3, 8, 13]) >>> gfs.nodes_in_column(4, 3, 5) array([ 2, 7, 12]) >>> gfs = GrainFacetSimulator((3, 6)) >>> gfs.nodes_in_column(3, 3, 6) array([ 4, 10, 16]) >>> gfs.nodes_in_column(4, 3, 6) array([ 2, 8, 14]) """ base_node = (col // 2) + (col % 2) * ((num_cols + 1) // 2) num_nodes = num_rows * num_cols return np.arange(base_node, num_nodes, num_cols) def get_profile_and_soil_thickness(self): """Calculate and return the topographic profile and the regolith thickness.""" nr = self.ca.grid.number_of_node_rows nc = self.ca.grid.number_of_node_columns data = self.ca.node_state elev = np.zeros(nc) soil = np.zeros(nc) for c in range(nc): e = (c % 2) / 2.0 s = 0 r = 0 while r < nr and data[c * nr + r] != 0: e += 1 if data[c * nr + r] == 7: s += 1 r += 1 elev[c] = e soil[c] = s return elev, soil def report_info_for_debug(self, current_time): """Print out various bits of data, for testing and debugging.""" print('\n Current time: ' + str(current_time)) print('Node state:') print(self.ca.node_state) for lnk in range(self.grid.number_of_links): if self.grid.status_at_link[lnk] == 0: print((lnk, self.grid.node_at_link_tail[lnk], self.grid.node_at_link_head[lnk], self.ca.node_state[self.grid.node_at_link_tail[lnk]], self.ca.node_state[self.grid.node_at_link_head[lnk]], self.ca.link_state[lnk], self.ca.next_update[lnk], self.ca.next_trn_id[lnk])) print('PQ:') print(self.ca.priority_queue._queue) def plot_to_file(self): """Plot profile of hill to file.""" fname = self.plot_file_name + str(self.plot_number).zfill(4) + '.png' plot_hill(self.ca.grid, filename=fname) self.plot_number += 1