Ejemplo n.º 1
0
    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
Ejemplo n.º 2
0
    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
Ejemplo n.º 3
0
    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
Ejemplo n.º 4
0
    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
Ejemplo n.º 7
0
 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)
Ejemplo n.º 8
0
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)
Ejemplo n.º 9
0
    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
Ejemplo n.º 12
0
    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,
            },
        )
Ejemplo n.º 13
0
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
Ejemplo n.º 14
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, {})
Ejemplo n.º 16
0
 def test_run_simulation(self, biot_params):
     setup = ISCBiotContactMechanics(biot_params)
     setup.prepare_simulation()
     pp.run_time_dependent_model(setup, {})
Ejemplo n.º 17
0
        "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,
Ejemplo n.º 18
0
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
Ejemplo n.º 20
0
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)
Ejemplo n.º 22
0
    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