equinor / flownet

FlowNet - Data-Driven Reservoir Predictions
GNU General Public License v3.0
63 stars 29 forks source link

I377 Mitchell's speed up #380

Closed LonnekevB closed 3 years ago

LonnekevB commented 3 years ago

Insert a description of your pull request (PR) here, and check off the boxes below when they are done.


Contributor checklist

LonnekevB commented 3 years ago

I looked through the code and I think it is all mathematically correct what we are doing in the fast Mitchells algorithm. I plotted the flownodes from both algorithms (Norne single layer, 20 additional flow nodes, 100 candidates, just a quick meaningless check). Probably better to plot the actual wells in a different color, to make it a bit more clear, but running out of time for today. image

The fast Mitchell algorithm is now also included in the unit tests and they pass. So additional nodes are placed correctly in the volume of the reservoir at least.

wouterjdb commented 3 years ago

Looking at the images, I'm wondering about the depths. In the "normal" version there are a lot more very deep (shallow in the picture?) nodes. Why?

olelod commented 3 years ago

Looking at the images, I'm wondering about the depths. In the "normal" version there are a lot more very deep (shallow in the picture?) nodes. Why?

I think the difference in z-scale is making it a bit difficult to compare here @wouterjdb. But yes, there are some very shallow nodes in the "normal" version (it's almost like the top layer/FlowNet is missing in the "fast" version).

olelod commented 3 years ago

I also get a feeling that

        in_hull = np.asarray([False] * num_candidates)
        x_candidate = np.zeros(num_candidates)
        y_candidate = np.zeros(num_candidates)
        z_candidate = np.zeros(num_candidates)

        # Repeat while not all random points are inside the convex hull
        while not all(in_hull):
            # Generate a set of random candidates that will be the new
            # flow nodes
            x_candidate_tmp = x_min + np.random.rand(num_candidates) * (x_max - x_min)
            y_candidate_tmp = y_min + np.random.rand(num_candidates) * (y_max - y_min)
            z_candidate_tmp = z_min + np.random.rand(num_candidates) * (z_max - z_min)

            # Update the list of flow node candidates. Only the points that previously
            # were not inside the convex hull are updated.
            np.putmask(x_candidate, np.invert(in_hull), x_candidate_tmp)
            np.putmask(y_candidate, np.invert(in_hull), y_candidate_tmp)
            np.putmask(z_candidate, np.invert(in_hull), z_candidate_tmp)

            candidates = np.vstack([x_candidate, y_candidate, z_candidate]).T

            if concave_hull_bounding_boxes is not None:
                # Test whether all points are inside the bounding boxes of the gridcells of the reservoir volume.
                # This is the always the case when place_nodes_in_volume_reservoir is True.
                in_hull = check_in_hull(
                    concave_hull_bounding_boxes, candidates, in_hull_known=in_hull
                )
            else:
                # Test whether all points are inside the convex hull of the perforations
                if np.all(z == z[0]):
                    in_hull = perforation_hull.find_simplex(candidates[:, (0, 1)]) >= 0
                else:
                    in_hull = perforation_hull.find_simplex(candidates) >= 0

could have been made as a _generate_candidates function. This could be called on the inside or outside of the for-loop like so:

    if fast_option:
         in_hull, x_candidates, y_candidates, z_candidates = _generate_candiadates( some arguments )

    # Generate all new flow nodes
    for i in range(num_points, num_points + num_added_flow_nodes):
        mid = time.time()
        if mid - start > 4:
            start = mid
            print(
                f"\rAdding flow nodes:  {int(((i-num_points)/num_added_flow_nodes)*100)}%",
                end="",
            )
        if not fast_option:
            in_hull, x_candidates, y_candidates, z_candidates = _generate_candiadates( some arguments )

Or are there more differences between mitchells_best_candidate and mitchells_best_candidate_fast that I am not picking up?

LonnekevB commented 3 years ago

Some minor comments.

I tried running for Norne with hundred of nodes to be added and 5,000/50,000 candidates in "Fast" mode. It runs nice 'n fast, but unfortunately I get into the following error:

image

If I run in "Normal" mode I don't get these errors.

I ran it with 100 nodes and 50.000 candidates in fast mode and it runs fine. So this issue seems to be fixed :)

wouterjdb commented 3 years ago

Tested running with fast mode (5000 candidates). It took only a handful of seconds to generate the grid, and it looked like:

image

Seems ok 👍