Domain conversion

In RMS, domain conversion can be performed through the right-click menu or dedicated jobs. However, in some cases it is convenient to perform domain conversion from a script. This module provides pure Python implementations for converting cubes and surfaces between the time and depth domains.

The functions are based on matching pairs of surfaces in the time and depth domains. Data is interpolated from the source domain to the target domain using average velocity or slowness. This differs from the RMS domain conversion, which uses an interval velocity model.

For domain conversion of cubes, two trace interpolation methods are available:

  • linear: Quick, but not fully amplitude preserving. The degree of amplitude loss depends on the sampling interval and frequency content. This is currently the default method.

  • fft: Amplitude preserving, but computationally more expensive. This method is planned to become the default in a future release.

Examples

Example 1

Convert a cube from the time domain to the depth domain.

import xtgeo
from fmu.tools import DomainConversion

cube = xtgeo.cube_from_file("path/to/time_cube/file.segy")  # assume this is in time domain

depth1 = xtgeo.surface_from_file("path/to/depth/file1.gri")
depth2 = xtgeo.surface_from_file("path/to/depth/file2.gri")
time1 = xtgeo.surface_from_file("path/to/time/file1.gri")
time2 = xtgeo.surface_from_file("path/to/time/file2.gri")

dc = DomainConversion([depth1, depth2], [time1, time2])

# now depth convert cube with fft method
cube_in_depth = dc.depth_convert_cube(cube, zinc=2, method="fft")

cube_in_depth.to_file("path/to/output/cube_in_depth.segy")

Example 2

Convert a cube and a surface from the depth domain to the time domain.

import xtgeo
from fmu.tools import DomainConversion

cube = xtgeo.cube_from_file("path/to/depth_cube/file.segy")  # assume this is in depth domain

depth1 = xtgeo.surface_from_file("path/to/depth/file1.gri")
depth2 = xtgeo.surface_from_file("path/to/depth/file2.gri")
time1 = xtgeo.surface_from_file("path/to/time/file1.gri")
time2 = xtgeo.surface_from_file("path/to/time/file2.gri")


depth_other = xtgeo.surface_from_file("path/to/depth_other.gri")

dlist = [depth1, depth2]
tlist = [time1, time2]


dc = DomainConversion(dlist, tlist)

time_cube = dc.time_convert_cube(cube, tinc=4, method="fft")

time_other_aslist = dc.time_convert_surfaces([depth_other])

Example 3

A larger example using cubes and surfaces in an RMS Python environment.

import xtgeo
from fmu.tools import DomainConversion


HORIZONS = ["TopVolantis", "BaseVolantis"]
AMPL_TIME_CUBE = "seismic--amplitude_far_time--20180101"
AI_TIME_CUBE = "seismic--relai_near_time--20180101"
DEPTH_CATEGORY = "DS_velmod"
TIME_CATEGORY = "TS_time_extracted"
TIME_CATEGORY_OTHER = "TS_interp"

# for depth conversion of cubes
ZINC = 2
MINDEPTH = 1200
MAXDEPTH = 2000
TINC = 3
MINTIME = 1500
MAXTIME = 1900
METHOD = "fft"

CLIP_CATALOG = "testing_dconv"
PRJ = project


def load_input():
    """Load input data, such as cubes and surfaces."""
    amplcube = xtgeo.cube_from_roxar(PRJ, AMPL_TIME_CUBE)
    aicube = xtgeo.cube_from_roxar(PRJ, AI_TIME_CUBE)
    print(f"Loading cubes {AMPL_TIME_CUBE} and {AI_TIME_CUBE}... DONE")

    depthsurfs = []
    timesurfs = []
    for ds in HORIZONS:
        depthsurfs.append(xtgeo.surface_from_roxar(PRJ, ds, DEPTH_CATEGORY))
        timesurfs.append(xtgeo.surface_from_roxar(PRJ, ds, TIME_CATEGORY))
    # read some other time surfaces to be converted
    othertimesurfs = []
    for surf in HORIZONS:
        tsfr = xtgeo.surface_from_roxar(PRJ, surf, TIME_CATEGORY_OTHER)
        othertimesurfs.append(tsfr)

    print("Loading surfaces... DONE")
    return amplcube, aicube, depthsurfs, timesurfs, othertimesurfs


def create_domain_conversion_model(dsurfs, tsurfs):
    """Create a domain model from matching depth and time surfaces."""
    print("Create domain conversion (velocity/slowness) model...")
    dc = DomainConversion(depth_surfaces=dsurfs, time_surfaces=tsurfs)
    print("Create domain conversion model... DONE")
    return dc


def _depth_convert_cube(dc, mycube):
    """Depth convert a cube (generic)."""
    print("Depth convert cube...")
    dcube = dc.depth_convert_cube(mycube, zinc=ZINC, zmin=MINDEPTH, zmax=MAXDEPTH, method=METHOD)
    print("Depth convert cube... DONE")
    return dcube


def _time_convert_cube(dc, dcube):
    """Time convert a cube using the slowness model (generic)."""
    print("Time convert cube...")
    tcube = dc.time_convert_cube(dcube, tinc=TINC, tmin=MINTIME, tmax=MAXTIME, method=METHOD)
    print("Time convert cube... DONE")
    return tcube


def domain_convert_some_cube(dc, tcube, nickname="something"):
    """Convert a cube to depth and back again for demonstration."""
    dcube = _depth_convert_cube(dc, tcube)
    tcube_again = _time_convert_cube(dc, dcube)  # going back again, for demonstration
    dcube.to_roxar(PRJ, nickname + "_depth")
    tcube_again.to_roxar(PRJ, nickname + "_time_again")
    print(f"Save cubes in RMS... ({nickname}...) DONE")


def domain_convert_surfaces(dc, othertimesurfs):
    """Convert surfaces between time and depth domains."""
    print("Depth and time convert surfaces...")
    depthsurfaces = dc.depth_convert_surfaces(othertimesurfs)
    # store on clipboard
    for ds, name in zip(depthsurfaces, HORIZONS):
        ds.to_roxar(PRJ, f"{name}_depth", CLIP_CATALOG, stype="clipboard")
    # surfaces back to time domain
    new_timesurfaces = dc.time_convert_surfaces(depthsurfaces)
    # store new timesurfaces on clipboard
    for ts, name in zip(new_timesurfaces, HORIZONS):
        ts.to_roxar(PRJ, f"{name}_time", CLIP_CATALOG, stype="clipboard")
    print("Depth and time convert surfaces... DONE")


# entry point for script
if __name__ == "__main__":
    ampl, ai, ds, ts, other = load_input()
    dc = create_domain_conversion_model(ds, ts)

    for nickname, cube in zip(["ampl_test", "ai_test"], [ampl, ai]):
        domain_convert_some_cube(dc, cube, nickname)
    domain_convert_surfaces(dc, other)

    print("Done")