def initial_state_poro_mech(self): print("\n\n") print("-------- Poro-mechanical initialization -------------") print("\n\n") model_params = self._standard_parameters() # model_params["initial_mechanics_state"] = self.initial_mechanics_state # The injection lasts in total 6 hours. Also do half an hour of relaxation after this model_params["end_time"] = 1000 * pp.YEAR model_params["time_step"] = model_params["end_time"] / 5 model_params["num_loadsteps"] = 1 model_params["export_folder"] = "model_initialization" model_params["export_file_name"] = "model_init" poro_model = BiotMechanicsModel(model_params) pp.run_time_dependent_model(poro_model, { "max_iterations": 500, "nl_convergence_tol": 1e-14 }) poro_model.store_contact_state("reference") poro_model.store_contact_state("previous") logger.info(f"\n\n Model initialization completed\n\n") self.model = poro_model
def _solve(self, setup): pp.run_time_dependent_model(setup, {"convergence_tol": 1e-6}) gb = setup.gb nd = setup._Nd g2 = gb.grids_of_dimension(nd)[0] g1 = gb.grids_of_dimension(nd - 1)[0] d_m = gb.edge_props((g1, g2)) d_1 = gb.node_props(g1) mg = d_m["mortar_grid"] u_mortar = d_m[pp.STATE][setup.mortar_displacement_variable] contact_force = d_1[pp.STATE][setup.contact_traction_variable] fracture_pressure = d_1[pp.STATE][setup.scalar_variable] displacement_jump_global_coord = ( mg.mortar_to_secondary_avg(nd=nd) * mg.sign_of_mortar_sides(nd=nd) * u_mortar ) projection = d_1["tangential_normal_projection"] project_to_local = projection.project_tangential_normal(int(mg.num_cells / 2)) u_mortar_local = project_to_local * displacement_jump_global_coord u_mortar_local_decomposed = u_mortar_local.reshape((nd, -1), order="F") contact_force = contact_force.reshape((nd, -1), order="F") return u_mortar_local_decomposed, contact_force, fracture_pressure
def _solve(self, setup): if hasattr(setup, "end_time"): pp.run_time_dependent_model(setup, {"convergence_tol": 1e-10}) else: pp.run_stationary_model(setup, {"convergence_tol": 1e-10}) gb = setup.gb nd = gb.dim_max() g2 = gb.grids_of_dimension(2)[0] g1 = gb.grids_of_dimension(1)[0] d_m = gb.edge_props((g1, g2)) d_1 = gb.node_props(g1) mg = d_m["mortar_grid"] u_mortar = d_m[pp.STATE][setup.mortar_displacement_variable] contact_force = d_1[pp.STATE][setup.contact_traction_variable] displacement_jump_global_coord = (mg.mortar_to_slave_avg(nd=nd) * mg.sign_of_mortar_sides(nd=nd) * u_mortar) projection = d_m["tangential_normal_projection"] project_to_local = projection.project_tangential_normal( int(mg.num_cells / 2)) u_mortar_local = project_to_local * displacement_jump_global_coord u_mortar_local_decomposed = u_mortar_local.reshape((2, -1), order="F") contact_force = contact_force.reshape((2, -1), order="F") return u_mortar_local_decomposed, contact_force
def _solve(self, setup): pp.run_time_dependent_model(setup, {}) gb = setup.gb g = gb.grids_of_dimension(setup._Nd)[0] d = gb.node_props(g) u = d[pp.STATE][setup.displacement_variable] p = d[pp.STATE][setup.scalar_variable] return u, p
def test_grid_error_structured_grid(self): """ Set up and solve a simple 3d-problem with 2 fractures. Verify decreasing error. # TODO: Modify FlowISC so that it modifies permeability on setup. """ time_step = pp.HOUR params = FlowParameters( head="TestGridError/test_grid_error_structured_grid", time_step=time_step, end_time=time_step * 4, fluid_type=pp.UnitFluid, shearzone_names=["f1"], mesh_args={}, source_scalar_borehole_shearzone=None, well_cells=nd_injection_cell_center, injection_rate=1, frac_permeability=1, intact_permeability=1, ) n_ref = 2 gb_list = [ structured_grid_1_frac_horizontal_with_refinements(ref) for ref in range(n_ref + 1) ] for gb in gb_list: setup = FlowISC(params) setup.gb = gb pp.run_time_dependent_model(setup, {}) # --- Compute errors --- gb_ref = gb_list[-1] errors = [] for i in range(0, n_ref): gb_i = gb_list[i] gb_coarse_fine_cell_mapping(gb=gb_i, gb_ref=gb_ref) _error = grid_error( gb=gb_i, gb_ref=gb_ref, variable=["p_exp"], variable_dof=[1], ) errors.append(_error) logger.info(errors)
def test_run_flow_term_by_filter(): """ This test intends to test results of running only the flow terms of the biot equation (on all subdomains) -- Key features: * 2 intersecting fractures * Full stress tensor and mechanical gravity terms included * hydrostatic scalar BC's * Initialization phase AND Injection phase (shortened) """ # 1. Prepare parameters stress = gts.stress_tensor() # # We set up hydrostatic stress # hydrostatic = np.mean(np.diag(stress)) * np.ones(stress.shape[0]) # stress = np.diag(hydrostatic) shearzones = ["S1_2", "S3_1"] gravity = True # False # No gravity effects params = { "stress": stress, "shearzone_names": shearzones, "_gravity_bc": gravity, "_gravity_src": gravity, } # Storage folder this_method_name = test_run_flow_term_by_filter.__name__ now_as_YYMMDD = pendulum.now().format("YYMMDD") _folder_root = f"{this_method_name}/{now_as_YYMMDD}/test_1" # # 2. Setup and run test params = test_util.prepare_params(path_head=_folder_root, params=params, setup_loggers=True) setup = BiotReduceToFlow(params=params) nl_params = {} # Default Newton Iteration parameters pp.run_time_dependent_model(setup, params=nl_params) # Stimulation phase logger.info( f"Starting stimulation phase at time: {pendulum.now().to_atom_string()}" ) setup.prepare_main_run() logger.info("Setup complete. Starting time-dependent simulation") pp.run_time_dependent_model(setup=setup, params=params) return setup
def test_realistic_only_fracture_zone(self): # See changelist: reset changes for only fracture zone setup of 2020/06/25. # _sz = 2, bounding_box=None -> ~53k 3d, ~6k 2d, 80 1d cells _sz = 3.5 params = BiotParameters( # Base parameters length_scale=0.05, scalar_scale=1e6, head="60k-5frac/medium-sized-intact-grid/t_end-10min", time_step=pp.MINUTE, end_time=10 * pp.MINUTE, rock=GrimselGranodiorite(), # Geometry parameters mesh_args={ "mesh_size_frac": 0.9 * _sz, "mesh_size_min": 0.2 * _sz, "mesh_size_bound": 3 * _sz, }, bounding_box={ "xmin": -1 - 50, "ymin": 80 - 50, "zmin": -4 - 50, "xmax": 86 + 50, "ymax": 151 + 50, "zmax": 41 + 50, }, shearzone_names=["S1_1", "S1_2", "S1_3", "S3_1", "S3_2"], # Mechanical parameters stress=stress_tensor(), newton_options={ "max_iterations": 30, "nl_convergence_tol": 1e-10, "nl_divergence_tol": 1e5, }, # Flow parameters source_scalar_borehole_shearzone={ "shearzone": "S1_2", "borehole": "INJ1", }, well_cells=nd_and_shearzone_injection_cell, injection_rate=(1 / 6) / 3, # = 10 l/min - Divide by 3 due to 3 injection cells. frac_permeability=[2.3e-15, 4.5e-17, 2.3e-17, 6.4e-14, 1e-16], intact_permeability=2e-20, ) setup = NeverFailtBiotCM(params) newton_params = params.newton_options pp.run_time_dependent_model(setup, newton_params)
def gts_biot_model(setup, params): """ Setup for time-dependent model run at Grimsel Test Site Usually called by run_abstract_model if this method is supplied as the argument 'run_model'. """ # Initialization phase pp.run_time_dependent_model(setup=setup, params=params) logger.info(f"Initial simulation complete. Exporting solution. " f"Time: {pendulum.now().to_atom_string()}") # Stimulation phase logger.info( f"Starting stimulation phase at time: {pendulum.now().to_atom_string()}" ) setup.prepare_main_run() logger.info("Setup complete. Starting time-dependent simulation") pp.run_time_dependent_model(setup=setup, params=params)
def test_realistic_setup(self): """ For a 50 000 cell setup, test Contact mechanics Biot model on 5 shear zones. Parameters are close to the ISC setup. Model is run for 10 minutes, with time steps of 1 minute. Injection to a shear zone. """ # _sz=6 => ~50k cells, # _sz=5.5 => ~66k cells _sz = 6 params = BiotParameters( # Base parameters length_scale=12.8, scalar_scale=1e10, head="2frac/20l_min/inj-frac-center/dilation", time_step=pp.MINUTE, end_time=7 * pp.MINUTE, rock=GrimselGranodiorite(), # Geometry parameters shearzone_names=["S1_2", "S3_1"], mesh_args={ "mesh_size_frac": _sz, # 0.3 * _sz "mesh_size_min": 0.2 * _sz, "mesh_size_bound": 3 * _sz, # 3.2 * _sz }, # Mechanical parameters stress=stress_tensor(), dilation_angle=(np.pi / 180) * 5, # 5 degrees dilation angle. newton_options={ "max_iterations": 40, "nl_convergence_tol": 1e-10, "nl_divergence_tol": 1e5, }, # Flow parameters source_scalar_borehole_shearzone={ "shearzone": "S1_2", "borehole": "INJ1", }, well_cells=center_of_shearzone_injection_cell, injection_rate=(10 / 60) * 2, # (10/60)*2 = 20l per 60s = 20 l/min frac_transmissivity=[1e-9, 3.7e-7], ) setup = NeverFailtBiotCM(params) newton_params = params.newton_options pp.run_time_dependent_model(setup, newton_params)
def test_run_biot_term_by_term(test_name: str): # TODO: THIS METHOD IS NOT FINISHED SET UP (maybe remove it) """ This test intends to investigate various properties of the biot equation by discretizing only certain terms. Additional simplifications: * hydrostatic mechanical stress * no mechanical gravity term """ # 1. Prepare parameters stress = gts.stress_tensor() # We set up hydrostatic stress hydrostatic = np.mean(np.diag(stress)) * np.ones(stress.shape[0]) stress = np.diag(hydrostatic) no_shearzones = None gravity = False # No gravity effects params = { "stress": stress, "shearzone_names": no_shearzones, "_gravity_bc": gravity, "_gravity_src": gravity, } # Storage folder this_method_name = test_run_biot_term_by_term.__name__ now_as_YYMMDD = pendulum.now().format("YYMMDD") _folder_root = f"{this_method_name}/{now_as_YYMMDD}/{test_name}" # # 2. Setup and run test params = test_util.prepare_params(path_head=_folder_root, params=params, setup_loggers=True) setup = BiotReduceToMechanics(params=params) nl_params = {} # Default Newton Iteration parameters pp.run_time_dependent_model(setup, params=nl_params) return setup
def test_run_mechanics_term_by_filter(): """ This test intends to replicate the part of the results of 'test_decomposition_of_stress' by only discretizing the mechanics term. """ # 1. Prepare parameters stress = gts.stress_tensor() # We set up hydrostatic stress hydrostatic = np.mean(np.diag(stress)) * np.ones(stress.shape[0]) stress = np.diag(hydrostatic) no_shearzones = None gravity = False # No gravity effects params = { "stress": stress, "shearzone_names": no_shearzones, "_gravity_bc": gravity, "_gravity_src": gravity, } # Storage folder this_method_name = test_run_mechanics_term_by_filter.__name__ now_as_YYMMDD = pendulum.now().format("YYMMDD") _folder_root = f"{this_method_name}/{now_as_YYMMDD}/test_1" # # 2. Setup and run test params = test_util.prepare_params(path_head=_folder_root, params=params, setup_loggers=True) setup = BiotReduceToMechanics(params=params) nl_params = {} # Default Newton Iteration parameters pp.run_time_dependent_model(setup, params=nl_params) return setup
def simulate_march_29(self, params=None): if params is None: params = {} poro_model = self.model # Switch on the sources poro_model.activate_sources() time_step = 15 * pp.MINUTE poro_model.time = 0 poro_model.set_time_step(time_step) poro_model.init_time_step = time_step poro_model.end_time = 10.5 * pp.HOUR export_folder = "march_29" poro_model.export_folder = export_folder poro_model.export_file_name = "stimulation" poro_model.prev_export_time = 0 poro_model.set_export() if not os.path.exists(export_folder): os.mkdir(export_folder) pickle.dump(poro_model.gb, open(export_folder + "/grid_bucket.grid", "wb")) pp.run_time_dependent_model( poro_model, { "prepare_simulation": False, "max_iterations": 500, "nl_convergence_tol": 1e-14, }, )
def _helper_run_flowisc_optimized_grid(params: FlowParameters, optimize_method="Netgen"): """ Run FlowISC on optimized meshes""" _gb, network = create_grid(**params.dict( include={ "mesh_args", "length_scale", "bounding_box", "shearzone_names", "folder_name", })) logger.info(f"gb cells non-optimized: {_gb.num_cells()}") # Optimize the mesh in_file = params.folder_name / "gmsh_frac_file.geo" out_file = params.folder_name / "gmsh_frac_file-optimized.msh" optimize_mesh( in_file=in_file, out_file=out_file, method=optimize_method, force=True, dim_tags=[3], ) gb: pp.GridBucket = pp.fracture_importer.dfm_from_gmsh(str(out_file), dim=3) logger.info(f"gb cells optimized: {gb.num_cells()}") assert _gb.num_cells() < gb.num_cells() # Run FlowISC setup = FlowISC(params) setup.gb = gb pp.run_time_dependent_model(setup, {}) assert setup.neg_ind.size == 0
def test_low_k_1_frac(self): """ Run FlowISC on a mesh with 1 fracture""" _sz = 4 # _sz = 2 intact_k = 1e-13 frac_k = 1e-9 time_step = pp.MINUTE params = FlowParameters( # head=f"test_low_k_1_frac/sz_{_sz}/kf_{intact_k}_ki_{frac_k}/1min", head="TestFlowISC/delete-me", time_step=time_step, end_time=time_step * 4, shearzone_names=["S1_2"], bounding_box=None, mesh_args={ "mesh_size_frac": _sz, "mesh_size_min": _sz, # 0.2 * _sz, "mesh_size_bound": _sz, # 3 * _sz, }, well_cells=shearzone_injection_cell, injection_rate=1 / 6, frac_permeability=frac_k, intact_permeability=intact_k, ) setup = FlowISC(params) _gb, network = create_grid(**params.dict( include={ "mesh_args", "length_scale", "bounding_box", "shearzone_names", "folder_name", })) pp.run_time_dependent_model(setup, {}) assert setup.neg_ind.size == 0
def test_run_simulation(self, setup): setup.prepare_simulation() pp.run_time_dependent_model(setup, {})
def test_run_simulation(self, biot_params): setup = ISCBiotContactMechanics(biot_params) setup.prepare_simulation() pp.run_time_dependent_model(setup, {})
"folder_name": folder_name, "nx": 10, "prepare_umfpack": False, } if reference: params["file_name"] = "tensile_stable_propagation_reference" if not os.path.exists(folder_name): os.makedirs(folder_name) for i, dt in enumerate(time_steps): params["time_step"] = dt for j, nx in enumerate(n_cells): logger.info("\nSolving for dt {} and nx {}.".format(dt, nx)) params["nx"] = nx m = Example2Model(params) pp.run_time_dependent_model(m, params) fracture_sizes[(dt, nx)] = m.fracture_sizes export_times[(dt, nx)] = m.export_times m._export_pvd() plot = False if reference: data = read_pickle("exII/fracture_sizes") fracture_sizes.update(data["fracture_sizes"]) time_steps = np.union1d(data["time_steps"], time_steps) export_times = data["export_times"].update(export_times) n_cells = np.union1d(data["n_cells"], n_cells) data = { "fracture_sizes": fracture_sizes, "time_steps": time_steps,
def _run_flow_models_helper( sz: float, incompressible: bool, head: str, shearzone_names: Optional[List[str]], time_step: float = None, ) -> None: """ Helper method for the test_flow setups sz is related to mesh_args incompressible turns on or off compressibility (and time stepping) - If compressible, you should also set time_step head is the path head. Usually, method name shearzone_names is a list of names given to fractures n_frac is the number of fractures to be constructed """ fluid = pp.UnitFluid(11) if incompressible: # Consider an incompressible problem fluid.COMPRESSIBILITY = 0 time_step = 1 end_time = 1 else: time_step = time_step end_time = time_step * 4 params = FlowParameters( head=f"TestFlow/{head}", fluid=fluid, time_step=time_step, end_time=end_time, shearzone_names=shearzone_names, mesh_args={ "mesh_size_frac": sz, "mesh_size_min": sz, "mesh_size_bound": sz * 4, }, source_scalar_borehole_shearzone=None, well_cells=nd_injection_cell_center, injection_rate=1, frac_permeability=1, intact_permeability=1, bounding_box={ "xmin": 0, "ymin": 0, "zmin": 0, "xmax": 1, "ymax": 1, "zmax": 1 }, ) setup = FlowISC(params) network = network_n_fractures(params.n_frac) gb = network.mesh( mesh_args=params.mesh_args, file_name=str(params.folder_name / "gmsh_frac_file"), ) setup.gb = gb pp.run_time_dependent_model(setup, {}) assert setup.neg_ind.size == 0
def test_compare_run_mech_and_run_mech_by_filter_term(): """ This test runs pure mechanics by running the test 'test_mechanics_class_methods.test_decomposition_of_stress()' for the hydrostatic case. Then, it runs the test 'test_run_mechanics_term_by_filter()'. The goal is to compare the output of these two methods and ensure they are the same. """ # 1. Prepare parameters stress = gts.stress_tensor() # We set up hydrostatic stress hydrostatic = np.mean(np.diag(stress)) * np.ones(stress.shape[0]) stress = np.diag(hydrostatic) no_shearzones = None gravity = False # No gravity effects params = { "stress": stress, "shearzone_names": no_shearzones, "_gravity_bc": gravity, "_gravity_src": gravity, } # Storage folder this_method_name = test_compare_run_mech_and_run_mech_by_filter_term.__name__ now_as_YYMMDD = pendulum.now().format("YYMMDD") _folder_root = f"{this_method_name}/{now_as_YYMMDD}" # 1. --- Setup ContactMechanicsISC --- setup_mech = test_util.prepare_setup( model=gts.ContactMechanicsISC, path_head=f"{_folder_root}/test_mech", params=params, prepare_simulation=False, setup_loggers=True, ) setup_mech.create_grid() # 2. --- Setup BiotReduceToMechanics --- params2 = test_util.prepare_params( path_head=f"{_folder_root}/test_biot_reduce_to_mech", params=params, setup_loggers=False, ) setup_biot = BiotReduceToMechanics(params=params2) # Recreate the same mesh as for the above setup path_to_gb_msh = f"{setup_mech.viz_folder_name}/gmsh_frac_file.msh" gb2 = pp.fracture_importer.dfm_from_gmsh(path_to_gb_msh, dim=3, network=setup_mech._network) setup_biot.set_grid(gb2) # 3. --- Run simulations --- nl_params = {} # Run ContactMechanicsISC pp.run_stationary_model(setup_mech, nl_params) # Run BiotReduceToMechanics pp.run_time_dependent_model(setup_biot, nl_params) # --- Compare results --- def get_u(_setup): gb = _setup.gb g = gb.grids_of_dimension(3)[0] d = gb.node_props(g) u = d["state"]["u"].reshape((3, -1), order="F") return u u_mech = get_u(setup_mech) u_biot = get_u(setup_biot) assert np.isclose( np.sum(np.abs(u_mech - u_biot)), 0.0), ("Running mechanics or biot (only discretize mechanics " "term should return same result.") return setup_mech, setup_biot
def run(params): logger.info("\n\n" + params["file_name"]) m = Model(params) pp.run_time_dependent_model(m, params) m.export_pvd()
fn + "tangential_displacement_jumps_" + setup.file_name + ".txt", setup.u_jumps_tangential[ind:], ) np.savetxt( fn + "normal_displacement_jumps_" + setup.file_name + ".txt", setup.u_jumps_normal[ind:], ) np.savetxt(fn + "time_steps_" + setup.file_name + ".txt", t) np.savetxt(fn + "iterations_" + setup.file_name + ".txt", iterations) if __name__ == "__main__": # Define mesh sizes for grid generation. mesh_size = 0.014 mesh_args = { "mesh_size_frac": mesh_size, "mesh_size_min": 0.2 * mesh_size, "mesh_size_bound": 3 * mesh_size, } params = { "folder_name": "results", "convergence_tol": 2e-7, "max_iterations": 20, "file_name": "thm", } setup = ExampleModel(params, mesh_args) pp.run_time_dependent_model(setup, params) setup.export_pvd() write_displacement_jumps_to_file(setup)
def test_flow_model_regular_vs_optimized_mesh(self): """ Investigate if optimizing the mesh can give different results on a problem with known issues in the solution We expect regular mesh generation to produce a mesh of poor enough quality for the solution to be unphysical (here: we get 6 cells with negative pressure values). However, if we use the Netgen mesh optimizer, the find that the negative cells are eradicated from the solution. """ # Create a regular, non-optimized mesh _sz = 10 time_step = pp.MINUTE params = FlowParameters( head="TestOptimizeMesh/test_optimize_mesh", time_step=time_step, end_time=time_step * 4, shearzone_names=None, mesh_args={ "mesh_size_frac": _sz, "mesh_size_min": 0.2 * _sz, "mesh_size_bound": 3 * _sz, }, source_scalar_borehole_shearzone=None, well_cells=nd_injection_cell_center, injection_rate=1 / 6, frac_permeability=0, intact_permeability=1e-13, ) setup = FlowISC(params) pp.run_time_dependent_model(setup, {}) assert setup.neg_ind.size == 6 # --- Re-run test with optimized mesh --- # Optimize the mesh in_file = params.folder_name / "gmsh_frac_file.geo" out_file = params.folder_name / "optimized/gmsh_frac_file.msh" optimize_mesh( in_file=in_file, out_file=out_file, method="Netgen", ) gb: pp.GridBucket = pp.fracture_importer.dfm_from_gmsh(str(out_file), dim=3) # Check that the grid was indeed optimized assert setup.gb.num_cells() < gb.num_cells() # Set up a new model params.folder_name = params.folder_name / "optimized" setup_opt = FlowISC(params) # Set the optimized grid bucket to the flow model setup_opt.gb = gb # Run the model pp.run_time_dependent_model(setup_opt, {}) assert setup_opt.neg_ind.size == 0