示例#1
0
    def test_geertsma_kernel_seabed():
        grid = EclGrid.createRectangular(dims=(1, 1, 1), dV=(50, 50, 50))
        with TestAreaContext("Subsidence"):
            p1 = [1]
            create_restart(grid, "TEST", p1)
            create_init(grid, "TEST")

            init = EclFile("TEST.INIT")
            restart_file = EclFile("TEST.UNRST")

            restart_view1 = restart_file.restartView(
                sim_time=datetime.date(2000, 1, 1))

            subsidence = EclSubsidence(grid, init)
            subsidence.add_survey_PRESSURE("S1", restart_view1)

            youngs_modulus = 5E8
            poisson_ratio = 0.3
            seabed = 300
            above = 100
            topres = 2000
            receiver = (1000, 1000, topres - seabed - above)

            dz = subsidence.evalGeertsma("S1", None, receiver, youngs_modulus,
                                         poisson_ratio, seabed)
            np.testing.assert_almost_equal(dz, 5.819790154474284e-08)
示例#2
0
文件: ots.py 项目: oddvarlia/semeio
    def __init__(
        self,
        eclbase,
        mapaxes,
        seabed,
        youngs,
        poisson,
        rfactor,
        convention,
        above,
        velocity_model,
    ):
        """
        The OTS class manages the information required to calculate
        overburden timeshift.

        The constructor will look for the Eclipse files INIT, EGRID
        and UNRST based on the input case, if some of the files are
        missing an exception will be raised. It will then instantiate
        a EclSubsidence object will be used to manage the rest of the
        overburden timeshift calculations.
        """
        case = os.path.splitext(eclbase)[0]
        self._init_file = EclFile("%s.INIT" % case)
        self._rst_file = EclFile("%s.UNRST" % case)
        self._grid = EclGrid("%s.EGRID" % case, apply_mapaxes=mapaxes)

        self.subsidence = EclSubsidence(self._grid, self._init_file)

        self._seabed = seabed
        self._youngs_modulus = youngs * 1e9
        self._poisson_ratio = poisson
        self._r_factor = rfactor
        self._convention = convention

        self._surface = res_surface = OTSResSurface(grid=self._grid,
                                                    above=above)
        if velocity_model is not None:
            self._surface = OTSVelSurface(res_surface=res_surface,
                                          vcube=velocity_model)

        self._restart_views = {}
示例#3
0
    def test_geertsma_rporv_kernel_2_source_points_2_vintages(self):
        grid = EclGrid.createRectangular(dims=(2, 1, 1), dV=(100, 100, 100))

        with TestAreaContext("Subsidence"):
            p1 = [1, 10]
            p2 = [10, 20]
            create_restart(grid,
                           "TEST",
                           p1,
                           p2,
                           rporv1=[10**5, 10**5],
                           rporv2=[9 * 10**4, 9 * 10**4])
            create_init(grid, "TEST")

            init = EclFile("TEST.INIT")
            restart_file = EclFile("TEST.UNRST")

            restart_view1 = restart_file.restartView(
                sim_time=datetime.date(2000, 1, 1))
            restart_view2 = restart_file.restartView(
                sim_time=datetime.date(2010, 1, 1))

            subsidence = EclSubsidence(grid, init)
            subsidence.add_survey_PRESSURE("S1", restart_view1)
            subsidence.add_survey_PRESSURE("S2", restart_view2)

            youngs_modulus = 5E8
            poisson_ratio = 0.3
            seabed = 0
            receiver = (1000, 1000, 0)

            dz1 = subsidence.eval_geertsma_rporv("S1", None, receiver,
                                                 youngs_modulus, poisson_ratio,
                                                 seabed)
            dz2 = subsidence.eval_geertsma_rporv("S2", None, receiver,
                                                 youngs_modulus, poisson_ratio,
                                                 seabed)
            dz = subsidence.eval_geertsma_rporv("S1", "S2", receiver,
                                                youngs_modulus, poisson_ratio,
                                                seabed)

            np.testing.assert_almost_equal(dz, dz1 - dz2)
            self.assertTrue(dz > 0)
示例#4
0
    def test_geertsma_kernel_2_source_points_2_vintages():
        grid = EclGrid.createRectangular(dims=(2, 1, 1), dV=(100, 100, 100))

        with TestAreaContext("Subsidence"):
            p1 = [1, 10]
            p2 = [10, 20]
            create_restart(grid, "TEST", p1, p2)
            create_init(grid, "TEST")

            init = EclFile("TEST.INIT")
            restart_file = EclFile("TEST.UNRST")

            restart_view1 = restart_file.restartView(
                sim_time=datetime.date(2000, 1, 1))
            restart_view2 = restart_file.restartView(
                sim_time=datetime.date(2010, 1, 1))

            subsidence = EclSubsidence(grid, init)
            subsidence.add_survey_PRESSURE("S1", restart_view1)
            subsidence.add_survey_PRESSURE("S2", restart_view2)

            youngs_modulus = 5E8
            poisson_ratio = 0.3
            seabed = 0
            receiver = (1000, 1000, 0)

            dz1 = subsidence.evalGeertsma("S1", None, receiver, youngs_modulus,
                                          poisson_ratio, seabed)
            np.testing.assert_almost_equal(dz1, 8.65322541521704e-07)

            dz2 = subsidence.evalGeertsma("S2", None, receiver, youngs_modulus,
                                          poisson_ratio, seabed)
            np.testing.assert_almost_equal(dz2, 2.275556615015282e-06)

            np.testing.assert_almost_equal(dz1 - dz2, -1.4102340734935779e-06)

            dz = subsidence.evalGeertsma("S1", "S2", receiver, youngs_modulus,
                                         poisson_ratio, seabed)
            np.testing.assert_almost_equal(dz, dz1 - dz2)
示例#5
0
文件: ots.py 项目: oddvarlia/semeio
class OverburdenTimeshift(object):
    def __init__(
        self,
        eclbase,
        mapaxes,
        seabed,
        youngs,
        poisson,
        rfactor,
        convention,
        above,
        velocity_model,
    ):
        """
        The OTS class manages the information required to calculate
        overburden timeshift.

        The constructor will look for the Eclipse files INIT, EGRID
        and UNRST based on the input case, if some of the files are
        missing an exception will be raised. It will then instantiate
        a EclSubsidence object will be used to manage the rest of the
        overburden timeshift calculations.
        """
        case = os.path.splitext(eclbase)[0]
        self._init_file = EclFile("%s.INIT" % case)
        self._rst_file = EclFile("%s.UNRST" % case)
        self._grid = EclGrid("%s.EGRID" % case, apply_mapaxes=mapaxes)

        self.subsidence = EclSubsidence(self._grid, self._init_file)

        self._seabed = seabed
        self._youngs_modulus = youngs * 1e9
        self._poisson_ratio = poisson
        self._r_factor = rfactor
        self._convention = convention

        self._surface = res_surface = OTSResSurface(grid=self._grid,
                                                    above=above)
        if velocity_model is not None:
            self._surface = OTSVelSurface(res_surface=res_surface,
                                          vcube=velocity_model)

        self._restart_views = {}

    def get_horizon(self):
        return self._create_surface()

    def _create_surface(self, z=None):
        """
        Generate irap surface

        :param z: replace z values of surface
        """
        nx = self._surface.nx
        ny = self._surface.ny
        x = self._surface.x
        y = self._surface.y
        if z is None:
            z = self._surface.z

        xstart = np.min(x)
        ystart = np.min(y)

        if nx < 2 or ny < 2:
            raise RuntimeError("Cannot create IRAP surface if nx or ny is <2")

        xinc = (np.max(x) - xstart) / (nx - 1)
        yinc = (np.max(y) - ystart) / (ny - 1)

        surf = Surface(nx=nx,
                       ny=ny,
                       xinc=xinc,
                       yinc=yinc,
                       xstart=xstart,
                       ystart=ystart,
                       angle=0)

        irap_x = np.empty(nx * ny)
        irap_y = np.array(irap_x)

        for i in range(len(surf)):
            irap_x[i], irap_y[i] = surf.getXY(i)

        # Interpolate vel grid to irap grid, should be the same apart from ordering
        z = np.nan_to_num(z)
        ip = CloughTocher2DInterpolator((x, y), z, fill_value=0)
        irap_z = ip(irap_x, irap_y)

        for i in range(len(surf)):
            surf[i] = irap_z[i]

        return surf

    def add_survey(self, name, date):
        """The add_survey() method will register a survey at a specific date.

        The name argument should be a unique string, this will later
        be used when evaluating the elastic strain in the
        overburden. The date should be python datetime.date()
        instance, this date should be present as a report step in the
        restart file - otherwise an exception will be raised.
        """
        restart_view = self._rst_file.restartView(sim_time=date)
        self.subsidence.add_survey_PRESSURE(name, restart_view)

        self._restart_views[name] = restart_view

        return restart_view

    @staticmethod
    def _divide_negative_shift(ts, div_val=5.0):
        for i in range(len(ts)):
            if ts[i] < 0:
                ts[i] /= div_val

    def geertsma_ts_rporv(self, vintage_pairs):
        """
        Calculates TS without using velocity. Fast.
        Velocity is only used to get the surface on the velocity grid.
        Uses change in porevolume from Eclipse (RPORV in .UNRST) as input to
        Geertsma model.

        :param vintage_pairs:
        """
        return self._geertsma_ts_custom(vintage_pairs,
                                        self.subsidence.eval_geertsma_rporv,
                                        "TS_RPORV")

    def geertsma_ts_simple(self, vintage_pairs):
        """
        Calculates TS without using velocity. Fast.
        Velocity is only used to get the surface on the velocity grid.

        :param vintage_pairs:
        """
        return self._geertsma_ts_custom(vintage_pairs,
                                        self.subsidence.evalGeertsma,
                                        "TS_SIMPLE")

    def _geertsma_ts_custom(self, vintage_pairs, subsidence_func, method_name):
        """
        Calculates TS without using velocity. Fast.

        :param vintage_pairs:
        :param subsidence_func: specify subsidence method to be used
        :param method_name: string represeting the subsudence func name
        """

        if len(vintage_pairs) < 1:
            return 0, []

        vintages = self._vintages_name_date(vintage_pairs)
        surface = self._surface
        points_to_calculate = self._get_non_nan_points()
        surf_displacement = {}
        ts_surfaces = []
        for vintage in vintages:
            logging.info("{:%x %X} {}: Calculating vintage {:%Y.%m.%d}".format(
                dt.now(), method_name, vintage.date))

            self.add_survey(vintage.name, vintage.date)
            surf_displacement[vintage.date] = np.zeros(len(surface))

            for point in points_to_calculate:
                r1 = (surface.x[point], surface.y[point], 0)
                r2 = (
                    surface.x[point],
                    surface.y[point],
                    surface.z[point] - self._seabed,
                )
                # subsidence and displacement have opposite sign
                # should have minus on dz1 and dz2 here, more efficient
                # when calculating ts
                dz1 = subsidence_func(
                    base_survey=vintage.name,
                    monitor_survey=None,
                    pos=r1,
                    youngs_modulus=self._youngs_modulus,
                    poisson_ratio=self._poisson_ratio,
                    seabed=self._seabed,
                )
                dz2 = subsidence_func(
                    base_survey=vintage.name,
                    monitor_survey=None,
                    pos=r2,
                    youngs_modulus=self._youngs_modulus,
                    poisson_ratio=self._poisson_ratio,
                    seabed=self._seabed,
                )
                surf_displacement[vintage.date][point] = dz2 - dz1

        for base, monitor in vintage_pairs:
            self._report(method_name, base, monitor, len(points_to_calculate))

            ts = -self._r_factor * (surf_displacement[monitor] -
                                    surf_displacement[base])
            # Observations (even though they are few) indicate that time shifts
            # linked with compaction are smaller than when linked with stretch.
            # This is also reported by e.g. Hatchel and Bourne (2005)
            # Approximatelly R_comp = R_stretch / 5 was chosen
            self._divide_negative_shift(ts, div_val=5.0)

            ts = ts * self._convention

            ts_surfaces.append(self._create_surface(ts))

        return ts_surfaces

    def geertsma_ts(self, vintage_pairs):
        """
        Calculates TS using velocity. Slow.

        :param vintage_pairs:
        """

        if len(vintage_pairs) < 1:
            return 0, []

        surface = self._surface
        points_to_calculate = self._get_non_nan_points()

        ts_surfaces = []
        _, nz = surface.z3d.shape

        for base, monitor in vintage_pairs:
            surf_displacement = np.zeros(len(surface))
            logging.info("{:%x %X} TS: Calculating vintage"
                         " {:%Y.%m.%d} - {:%Y.%m.%d}".format(
                             dt.now(), base, monitor))

            # According to the author, base and monitor are
            # reversed for geertsma_ts
            self.add_survey("base", monitor)
            self.add_survey("monitor", base)

            for point in points_to_calculate:
                for iz in range(nz):
                    rz = surface.z3d[point, iz] - self._seabed
                    if 0 <= rz <= (surface.z[point] - self._seabed):
                        r1 = (surface.x[point], surface.y[point], rz)
                        r2 = (surface.x[point], surface.y[point], rz + 0.1)
                        # subsidence and displacement have opposite sign
                        # should have minus here, more efficient
                        # when calculating ts
                        dz1 = self.subsidence.evalGeertsma(
                            base_survey="base",
                            monitor_survey="monitor",
                            pos=r1,
                            youngs_modulus=self._youngs_modulus,
                            poisson_ratio=self._poisson_ratio,
                            seabed=self._seabed,
                        )
                        dz2 = self.subsidence.evalGeertsma(
                            base_survey="base",
                            monitor_survey="monitor",
                            pos=r2,
                            youngs_modulus=self._youngs_modulus,
                            poisson_ratio=self._poisson_ratio,
                            seabed=self._seabed,
                        )

                        # The vertical strain is equal to dz/z, where dz is
                        # the thickness change within a chosen thickness z.
                        verical_strain = (dz2 - dz1) * 10
                        surf_displacement[point] += verical_strain

            self._report("TS", base, monitor, len(points_to_calculate))

            # subsidence and displacement have opposite sign, thus the minus sign
            ts = -self._r_factor * surf_displacement * surface.dt * 1000
            self._divide_negative_shift(ts)

            ts = ts * self._convention

            ts_surfaces.append(self._create_surface(ts))

        return ts_surfaces

    @staticmethod
    def _vintages_name_date(vintage_pairs):
        vintages = set()
        for pair in vintage_pairs:
            vintages.add(pair[0])
            vintages.add(pair[1])
        vintages_date = list(vintages)
        vintages_name = []
        for i in range(len(vintages_date)):
            vintages_name.append("S{}".format(i))

        Vintage = namedtuple("Vintages", "name date")
        return [
            Vintage(name, date)
            for name, date in zip(vintages_name, vintages_date)
        ]

    def _report(self, func_name, base, monitor, num_points_calculated):
        if self._convention == 1:
            start_date, end_date = monitor, base
        elif self._convention == -1:
            start_date, end_date = base, monitor
        else:
            raise ValueError("Convention must be 1 or -1, was {}".format(
                self._convention))
        logging.debug("{:%x %X} {}: Calculating shift"
                      " {:%Y.%m.%d}-{:%Y.%m.%d} in {} points".format(
                          dt.now(), func_name, start_date, end_date,
                          num_points_calculated))

    def _get_non_nan_points(self):
        points_to_calculate = [
            _id for _id in range(len(self._surface))
            if not np.isnan(self._surface.z[_id])
        ]
        if len(points_to_calculate) == 0:
            logging.error((
                "Overburden timeshift was calculated in 0 points. ",
                "Consider changing --apply_mapaxes value.",
            ))
        return points_to_calculate

    def dpv(self, vintage_pairs):
        """
        Calulates change in pressure multiplied by cell volume
        and sum for all cells in column

        dPV must have equal sign as TS, but opposite from mathematics

        monitor-base

        :param vintage_pairs: list of pairs of vintages
        :return:
        """

        if len(vintage_pairs) < 1:
            return 0, []

        vintages = self._vintages_name_date(vintage_pairs)

        surf = self._surface
        points_to_calculate = self._get_non_nan_points()

        shift_surfaces = []
        pressure_volume = {}

        for vintage in vintages:
            logging.info("{:%x %X} DPV: Calculating vintage"
                         " {:%Y.%m.%d}".format(dt.now(), vintage.date))
            self.add_survey(vintage.name, vintage.date)
            pressure = Ecl3DKW.castFromKW(
                self._restart_views[vintage.name]["PRESSURE"][0], self._grid)
            pressure_volume[vintage.date] = np.zeros(len(surf))

            for point in points_to_calculate:
                r = surf.x[point], surf.y[point], 0
                try:
                    i, j = self._grid.findCellXY(*r)
                    sum_pv = 0
                    for k in range(self._grid.getNZ()):
                        v = self._grid.cell_volume(ijk=(i, j, k))
                        sum_pv += pressure[i, j, k] * v

                    pressure_volume[vintage.date][point] = sum_pv
                except ValueError:
                    pressure_volume[vintage.date][point] = 0

        for base, monitor in vintage_pairs:
            self._report("DPV", base, monitor, len(points_to_calculate))
            dpv = ((pressure_volume[monitor] - pressure_volume[base]) / 1e9 *
                   self._convention)
            shift_surfaces.append(self._create_surface(dpv))

        return shift_surfaces