def gb_coarse_fine_cell_mapping( gb: pp.GridBucket, gb_ref: pp.GridBucket, tol=1e-8 ) -> None: """ Wrapper for coarse_fine_cell_mapping to construct mapping for grids in GridBucket. Adds a node_prop to each grid in gb. The key is 'coarse_fine_cell_mapping', and is the mapping generated by 'coarse_fine_cell_mapping(...)'. Note: No node prop is added to the reference grids in gb_ref. Parameters ---------- gb : pp.GridBucket Coarse grid bucket gb_ref : pp.GridBucket Refined grid bucket tol : float, Optional Tolerance for point_in_poly* -methods """ grids = gb.get_grids() grids_ref = gb_ref.get_grids() assert len(grids) == len( grids_ref ), "Weakly check that GridBuckets refer to same domains" assert np.allclose( np.append(*gb.bounding_box()), np.append(*gb_ref.bounding_box()) ), "Weakly check that GridBuckets refer to same domains" # This method assumes a consistent node ordering between grids. # At least assign one. gb.assign_node_ordering(overwrite_existing=False) gb_ref.assign_node_ordering(overwrite_existing=False) # Add node prop on the coarse grid to map from coarse to fine cells. gb.add_node_props(keys="coarse_fine_cell_mapping") for i in np.arange(len(grids)): g, g_ref = grids[i], grids_ref[i] node_num = gb.node_props(g, "node_number") node_num_ref = gb_ref.node_props(g_ref, "node_number") assert node_num == node_num_ref, "Weakly check that grids refer to same domain." # Compute the mapping for this grid-pair, # and assign the result to the node of the coarse gb mapping = coarse_fine_cell_mapping(g, g_ref, point_in_poly_tol=tol) gb.set_node_prop(g, key="coarse_fine_cell_mapping", val=mapping)
def gb_coarse_fine_cell_mapping( gb: pp.GridBucket, gb_ref: pp.GridBucket, tol=1e-8 ): """ Wrapper for coarse_fine_cell_mapping to construct mapping for grids in GridBucket. Parameters ---------- gb : pp.GridBucket Coarse grid bucket gb_ref : pp.GridBucket Refined grid bucket tol : float, Optional Tolerance for point_in_poly* -methods Returns ------- mapping : list of tuples with entries (pp.GridBucket, pp.GridBucket, sps.csc_matrix) The first entry is the coarse grid. The second entry is the refined grid. The third entry is the mapping from coarse to fine cells """ grids = gb.get_grids() grids_ref = gb_ref.get_grids() assert len(grids) == len(grids_ref), "Weakly check that GridBuckets refer to same domains" assert np.array_equal(np.append(*gb.bounding_box()), np.append(*gb_ref.bounding_box())), \ "Weakly check that GridBuckets refer to same domains" # This method assumes a consistent node ordering between grids. At least assign one. gb.assign_node_ordering(overwrite_existing=False) gb_ref.assign_node_ordering(overwrite_existing=False) n_grids = len(grids) # mappings = [None]*n_grids mappings = {'gb': gb, 'gb_ref': gb_ref} for i in np.arange(n_grids): g, g_ref = grids[i], grids_ref[i] node_num, node_num_ref = gb._nodes[g]['node_number'], gb_ref._nodes[g_ref]['node_number'] assert node_num == node_num_ref, "Weakly check that grids refer to same domain." mapping = coarse_fine_cell_mapping(g, g_ref, tol=tol) mappings[(g, g_ref)] = {'node_number': node_num, 'data': gb.node_props(g), 'data_ref': gb_ref.node_props(g_ref)} return mappings
def set_grid(self, gb: pp.GridBucket): """ Set a new grid """ self.gb = gb self.Nd = gb.dim_max() self.n_frac = gb.get_grids(lambda _g: _g.dim == self.Nd - 1).size self.gb.add_node_props(keys="name") # Add 'name' as node prop to all grids.
def nd_sides_shearzone_injection_cell( params: FlowParameters, gb: pp.GridBucket, reset_frac_tags: bool = True, ) -> None: """ Tag the Nd cells surrounding a shear zone injection point Parameters ---------- params : FlowParameters parameters that contain "source_scalar_borehole_shearzone" (with "shearzone", and "borehole") and "length_scale". gb : pp.GridBucket grid bucket reset_frac_tags : bool [Default: True] if set to False, keep injection tag in the shear zone. """ # Shorthand shearzone = params.source_scalar_borehole_shearzone.get("shearzone") # First, tag the fracture cell, and get the tag shearzone_injection_cell(params, gb) fracture = gb.get_grids(lambda g: gb.node_props(g, "name") == shearzone)[0] tags = fracture.tags["well_cells"] # Second, map the cell to the Nd grid nd_grid: pp.Grid = gb.grids_of_dimension(gb.dim_max())[0] data_edge = gb.edge_props((fracture, nd_grid)) mg: pp.MortarGrid = data_edge["mortar_grid"] slave_to_master_face = mg.mortar_to_master_int() * mg.slave_to_mortar_int() face_to_cell = nd_grid.cell_faces.T slave_to_master_cell = face_to_cell * slave_to_master_face nd_tags = np.abs(slave_to_master_cell) * tags # Set tags on the nd-grid nd_grid.tags["well_cells"] = nd_tags ndd = gb.node_props(nd_grid) pp.set_state(ndd, {"well": tags}) if reset_frac_tags: # reset tags on the fracture zeros = np.zeros(fracture.num_cells) fracture.tags["well_cells"] = zeros d = gb.node_props(fracture) pp.set_state(d, {"well": zeros})
def center_of_shearzone_injection_cell( params: FlowParameters, gb: pp.GridBucket ) -> None: """ Tag the center cell of the given shear zone with 1 (injection) Parameters ---------- params : FlowParameters gb : pp.GridBucket """ # Shorthand shearzone = params.source_scalar_borehole_shearzone.get("shearzone") # Get the grid to inject to frac: pp.Grid = gb.get_grids(lambda g: gb.node_props(g, "name") == shearzone)[0] centers: np.ndarray = frac.cell_centers pts = np.atleast_2d(np.mean(centers, axis=1)).T # Tag injection grid with 1 in the injection cell _tag_injection_cell(gb, frac, pts, params.length_scale)
def shearzone_injection_cell(params: FlowParameters, gb: pp.GridBucket) -> None: """ Tag the borehole - shearzone intersection cell with 1 (injection) Parameters ---------- params : FlowParameters gb : pp.GridBucket """ # Shorthand shearzone = params.source_scalar_borehole_shearzone.get("shearzone") # Get intersection point pts = shearzone_borehole_intersection(params) # Get the grid to inject to injection_grid = gb.get_grids(lambda g: gb.node_props(g, "name") == shearzone)[0] assert ( injection_grid.dim == gb.dim_max() - 1 ), "Injection grid should be a Nd-1 fracture" # Tag injection grid with 1 in the injection cell _tag_injection_cell(gb, injection_grid, pts, params.length_scale)
def grid_error( gb: pp.GridBucket, gb_ref: pp.GridBucket, variable: List[str], variable_dof: List[int], ) -> dict: """ Compute grid errors a grid bucket and refined reference grid bucket Assumes that the coarse grid bucket has a node property 'coarse_fine_cell_mapping' assigned on each grid, which maps from coarse to fine cells according to the method 'coarse_fine_cell_mapping(...)'. Parameters ---------- gb, gb_ref : pp.GridBucket Coarse and fine grid buckets, respectively variable : List[str] which variables to compute error over variable_dof : List[int] Degrees of freedom for each variable in the list 'variable'. Returns ------- errors : dict Dictionary with top level keys as node_number, within which for each variable, the error is reported. """ if not isinstance(variable, list): variable = [variable] if not isinstance(variable_dof, list): variable_dof = [variable_dof] assert len(variable) == len(variable_dof), ( "Each variable must have associated " "with it a number of degrees of freedom.") n_variables = len(variable) errors = {} grids = gb.get_grids() grids_ref = gb_ref.get_grids() n_grids = len(grids) for i in np.arange(n_grids): g, g_ref = grids[i], grids_ref[i] mapping = gb.node_props(g, "coarse_fine_cell_mapping") # Get states data = gb.node_props(g) data_ref = gb_ref.node_props(g_ref) states = data[pp.STATE] states_ref = data_ref[pp.STATE] node_number = data["node_number"] # Initialize errors errors[node_number] = {} for var_idx in range(0, n_variables): var = variable[var_idx] var_dof = variable_dof[var_idx] # Check if the variable exists on both # the grid and reference grid state_keys = set(states.keys()) state_ref_keys = set(states_ref.keys()) check_keys = state_keys.intersection(state_ref_keys) if var not in check_keys: logger.info(f"{var} not present on grid number " f"{node_number} of dim {g.dim}.") continue # Compute errors relative to the reference grid # TODO: Should the solution be divided by # g.cell_volumes or similar? # TODO: If scaling is used, consider that # - or use the export-ready variables, # 'u_exp', 'p_exp', etc. sol = (states[var].reshape((var_dof, -1), order="F").T) # (num_cells x var_dof) mapped_sol: np.ndarray = mapping.dot( sol) # (num_cells x variable_dof) sol_ref = (states_ref[var].reshape( (var_dof, -1), order="F").T) # (num_cells x var_dof) # axis=0 gives component-wise norm. absolute_error = np.linalg.norm(mapped_sol - sol_ref, axis=0) norm_ref = np.linalg.norm(sol_ref, axis=0) if np.any(norm_ref < 1e-10): logger.info(f"Relative error not reportable. " f"Norm of reference solution is {norm_ref}. " f"Reporting absolute error") error = absolute_error is_relative = False else: error = absolute_error / norm_ref is_relative = True errors[node_number][var] = { "error": error, "is_relative": is_relative, } return errors
def gb_refinement( gb: pp.GridBucket, gb_ref: pp.GridBucket, tol: float = 1e-8, mode: str = "nested" ): """Wrapper for coarse_fine_cell_mapping to construct mapping for grids in GridBucket. Adds a node_prop to each grid in gb. The key is 'coarse_fine_cell_mapping', and is the mapping generated by 'coarse_fine_cell_mapping(...)'. Currently, only nested refinement is supported; more general cases are also possible. Note: No node prop is added to the reference grids in gb_ref. Parameters ---------- gb : pp.GridBucket Coarse grid bucket gb_ref : pp.GridBucket Refined grid bucket tol : float, Optional Tolerance for point_in_poly* -methods mode : str, Optional Refinement mode. Defaults to 'nested', corresponds to refinement by splitting. Acknowledgement: The code was contributed by Haakon Ervik. """ grids = gb.get_grids() grids_ref = gb_ref.get_grids() assert len(grids) == len( grids_ref ), "Weakly check that GridBuckets refer to same domains" assert np.allclose( np.append(*gb.bounding_box()), np.append(*gb_ref.bounding_box()) ), "Weakly check that GridBuckets refer to same domains" # This method assumes a consistent node ordering between grids. At least assign one. gb.assign_node_ordering(overwrite_existing=False) gb_ref.assign_node_ordering(overwrite_existing=False) # Add node prop on the coarse grid to map from coarse to fine cells. gb.add_node_props(keys="coarse_fine_cell_mapping") for i in np.arange(len(grids)): g, g_ref = grids[i], grids_ref[i] node_num, node_num_ref = ( gb._nodes[g]["node_number"], gb_ref._nodes[g_ref]["node_number"], ) assert node_num == node_num_ref, "Weakly check that grids refer to same domain." # Compute the mapping for this grid-pair, # and assign the result to the node of the coarse gb if mode == "nested": mapping = structured_refinement(g, g_ref, point_in_poly_tol=tol) else: raise NotImplementedError("Unknown refinement mode") gb.set_node_prop(grid=g, key="coarse_fine_cell_mapping", val=mapping)