def _solve(self, setup): 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 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 test_decomposition_of_stress(setup='normal_shear'): """ Test the solutions acquired when decomposing stress to purely compressive and purely rotational components. --- Setup --- A: 1. Get the stress tensor and split in diagonal and off-diagonal components. B: 1. Get the stress tensor and split in hydrostatic and deviatoric components. -- 2. Acquire solutions for each split tensor and the full tensor separately (3 cases). 3. Compare solutions quantitatively (linearity of solution) and qualitatively (Paraview) Parameters ---------- setup : str : {'normal_shear', 'hydrostatic'} Type of setup. 'normal_shear' simply separates the normal and shear components 'hydrostatic' separates hydrostatic and deviatoric stresses """ # Import stress tensor stress = gts.isc_modelling.stress_tensor() if setup == 'normal_shear': # Get normal and shear stresses normal_stress = np.diag(np.diag(stress).copy()) shear_stress = stress - normal_stress fname_n = 'normal_stress' fname_s = 'shear_stress' elif setup == 'hydrostatic': # Get hydrostatic and deviatoric stresses hydrostatic = np.mean(np.diag(stress)) * np.ones(stress.shape[0]) normal_stress = np.diag(hydrostatic) shear_stress = stress - normal_stress fname_n = 'hydrostatic_stress' fname_s = 'deviatoric_stress' else: raise ValueError(f"Did not recognise input setup={setup}") # 1. Full stress tensor this_method_name = test_decomposition_of_stress.__name__ now_as_YYMMDD = pendulum.now().format("YYMMDD") _folder_root = f"{this_method_name}/{now_as_YYMMDD}/no_gravity/{setup}" gravity = False no_shearzones = None params = { "shearzone_names": no_shearzones, "stress": stress, "_gravity_bc": gravity, "_gravity_src": gravity, } setup = test_util.prepare_setup( model=gts.ContactMechanicsISC, path_head=f"{_folder_root}", params=params, prepare_simulation=False, setup_loggers=True, ) setup.create_grid() # -- Safely copy the grid generated above -- # Ensures the implicit in-place variable changes across grid_buckets (gb.copy() is insufficient) # Get the grid_bucket .msh path path_to_gb_msh = f"{setup.viz_folder_name}/gmsh_frac_file.msh" # Re-create the grid buckets gb_n = pp.fracture_importer.dfm_from_gmsh(path_to_gb_msh, dim=3, network=setup._network) gb_s = pp.fracture_importer.dfm_from_gmsh(path_to_gb_msh, dim=3, network=setup._network) # 1. Pure normal stress / hydrostatic stress params_n = params.update({'stress': normal_stress}) setup_n = test_util.prepare_setup( model=gts.ContactMechanicsISC, path_head=f"{_folder_root}/{fname_n}", params=params_n, prepare_simulation=False, setup_loggers=False, ) setup_n.set_grid(gb_n) # 2. Pure shear stress / deviatoric stress params_s = params.update({"stress": shear_stress}) setup_s = test_util.prepare_setup( model=gts.ContactMechanicsISC, path_head=f"{_folder_root}/{fname_s}", params=params_s, prepare_simulation=False, setup_loggers=False, ) setup_s.set_grid(gb_s) # --- Run simulations --- params_nl = {} # Use default Newton solver parameters # 1. Full stress tensor pp.run_stationary_model(setup, params_nl) # 2. Pure normal stress pp.run_stationary_model(setup_n, params_nl) # 3. Pure shear stress pp.run_stationary_model(setup_s, params_nl) # --- 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 return get_u(setup), get_u(setup_n), get_u(setup_s), [ setup, setup_n, setup_s ]
"folder_name": folder_name, "inclination": np.pi / 2, "poisson": 0.3, "boundary_traction": 1e-4, "simplex": simplex, "n_cells": [nx, nx, 4], "prepare_umfpack": False, } if not os.path.exists(folder_name): os.makedirs(folder_name) for i, nu in enumerate(poisson_ratios): params["poisson"] = nu for j, h in enumerate(mesh_sizes): params["mesh_args"] = { "mesh_size_frac": h, "mesh_size_bound": 3 * h, } m = SneddonSIFTest(params) # Also compute mode II SIFs: m._is_tensile = False pp.run_stationary_model(m, params) all_errors[i, j] = m.compute_sifs_and_errors() data = { "all_errors": all_errors, "poisson_ratios": poisson_ratios, "mesh_sizes": mesh_sizes, } write_pickle(data, folder_name + "/all_errors")
def convergence_study(): """ Perform a convergence study of a given problem setup. """ # 1. Step: Create n grids by uniform refinement. # 2. Step: for grid i in list of n grids: # 2. a. Step: Set up the mechanics model. # 2. b. Step: Solve the mechanics problem. # 2. c. Step: Keep the grid (with solution data) # 3. Step: Let the finest grid be the reference solution. # 4. Step: For every other grid: # 4. a. Step: Map the solution to the fine grid, and compute error. # 5. Step: Compute order of convergence, etc. # ----------------- # --- ARGUMENTS --- # ----------------- viz_folder_name = Path( os.path.abspath(__file__)).parent / "results/mech_convergence_2test" if not os.path.exists(viz_folder_name): os.makedirs(viz_folder_name, exist_ok=True) shearzone_names = ["S1_1", "S1_2", "S1_3", "S3_1", "S3_2"] mesh_size = 10 mesh_args = { # A very coarse grid "mesh_size_frac": mesh_size, "mesh_size_min": mesh_size, "mesh_size_bound": mesh_size, } bounding_box = { "xmin": -6, "xmax": 80, "ymin": 55, "ymax": 150, "zmin": 0, "zmax": 50, } # 1. Step: Create n grids by uniform refinement. gb_list = create_isc_domain( viz_folder_name=viz_folder_name, shearzone_names=shearzone_names, bounding_box=bounding_box, mesh_args=mesh_args, n_refinements=1, ) scales = { 'scalar_scale': 1, 'length_scale': 1, } solver = 'direct' # --------------------------- # --- PHYSICAL PARAMETERS --- # --------------------------- stress = stress_tensor() # ---------------------- # --- SET UP LOGGING --- # ---------------------- print(viz_folder_name / "results.log") logger = __setup_logging(viz_folder_name) logger.info( f"Preparing setup for mechanics convergence study on {pendulum.now().to_atom_string()}" ) logger.info(f"Reporting on {len(gb_list)} grid buckets.") logger.info(f"Visualization folder path: \n {viz_folder_name}") logger.info(f"Mesh arguments for coarsest grid: \n {mesh_args}") logger.info(f"Bounding box: \n {bounding_box}") logger.info(f"Variable scaling: \n {scales}") logger.info(f"Solver type: {solver}") logger.info(f"Stress tensor: \n {stress}") # ----------------------- # --- SETUP AND SOLVE --- # ----------------------- newton_options = { # Parameters for Newton solver. "max_iterations": 10, "convergence_tol": 1e-10, "divergence_tol": 1e5, } logger.info(f"Options for Newton solver: \n {newton_options}") from GTS.isc_modelling.mechanics import ContactMechanicsISCWithGrid for gb in gb_list: setup = ContactMechanicsISCWithGrid( viz_folder_name, 'main_run', 'linux', mesh_args, bounding_box, shearzone_names, scales, stress, solver, gb, ) logger.info("Setup complete. Starting simulation") pp.run_stationary_model(setup, params=newton_options) logger.info("Simulation complete. Exporting solution.") return gb_list