Skip to content

Commit

Permalink
Interpolate log position from md on well logs (equinor#459)
Browse files Browse the repository at this point in the history
* Log path interpolation from Well Path and Log MD
* Used log data as vector
* Handled aliases for MD
  • Loading branch information
shadab-skhan authored Jul 22, 2021
1 parent 9bdf70e commit dfa874e
Show file tree
Hide file tree
Showing 3 changed files with 154 additions and 45 deletions.
2 changes: 1 addition & 1 deletion react/src/demo/example-data/deckgl-map.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
"lineWidthScale": 5,
"logRadius": 6,
"logrunName": "BLOCKING",
"logName": "zonelog",
"logName": "ZONELOG",
"pointRadiusScale": 8,
"outline": true,
"logCurves": true
Expand Down
2 changes: 1 addition & 1 deletion react/src/demo/example-data/volve_logs.json

Large diffs are not rendered by default.

195 changes: 152 additions & 43 deletions react/src/lib/components/DeckGLMap/layers/wells/wellsLayer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@ import { PickInfo } from "deck.gl";
import { subtract, distance, dot } from "mathjs";
import { interpolateRgbBasis } from "d3-interpolate";
import { color } from "d3-color";
import { Feature } from "geojson";

import { Feature, GeometryCollection, LineString, Position } from "geojson";

import { LayerPickInfo, PropertyDataType } from "../utils/layerTools";
import { patchLayerProps } from "../utils/layerTools";
import { interpolateNumberArray } from "d3";
import { Position2D, Position3D } from "@deck.gl/core/utils/positions";

export interface WellsLayerProps<D> extends CompositeLayerProps<D> {
Expand Down Expand Up @@ -127,8 +130,12 @@ export default class WellsLayer extends CompositeLayer<
widthScale: 10,
widthMinPixels: 1,
miterLimit: 100,
getPath: (d: LogCurveDataType): number[] =>
getLogPath(d, this.props.logrunName),
getPath: (d: LogCurveDataType): Position[] =>
getLogPath(
this.props.data as Feature[],
d,
this.props.logrunName
),
getColor: (d: LogCurveDataType): RGBAColor[] =>
getLogColor(d, this.props.logrunName, this.props.logName),
getWidth: (d: LogCurveDataType): number | number[] =>
Expand Down Expand Up @@ -162,6 +169,7 @@ export default class WellsLayer extends CompositeLayer<

const md_property = getMdProperty(info);
const log_property = getLogProperty(
this.props.data as Feature[],
info,
this.props.logrunName,
this.props.logName
Expand All @@ -184,28 +192,117 @@ WellsLayer.defaultProps = defaultProps;

//================= Local help functions. ==================

function isLogRunSelected(d: LogCurveDataType, logrun_name: string): boolean {
return d.header.name.toLowerCase() === logrun_name.toLowerCase();
function transpose(a) {
return Object.keys(a[0]).map(function (c) {
return a.map(function (r) {
return r[c];
});
});
}

function getLogPath(d: LogCurveDataType, logrun_name: string): number[] {
if (isLogRunSelected(d, logrun_name)) {
if (d?.data) {
return d.data[0];
}
}
return [];
function getLogMd(d: LogCurveDataType, logrun_name: string): number[] {
if (!isSelectedLogRun(d, logrun_name)) return [];

const names_md = ["DEPTH", "DEPT", "MD", "TDEP"]; // aliases for MD
const log_id = getLogIndexByNames(d, names_md);
return log_id >= 0 ? transpose(d.data)[log_id] : [];
}

function getLogIDByName(
function getLogValues(
d: LogCurveDataType,
logrun_name: string,
log_name: string
): number {
if (isLogRunSelected(d, logrun_name)) {
return d?.curves?.findIndex(
(item) => item.name.toLowerCase() === log_name.toLowerCase()
): number[] {
if (!isSelectedLogRun(d, logrun_name)) return [];

const log_id = getLogIndexByName(d, log_name);
return log_id >= 0 ? transpose(d.data)[log_id] : [];
}

function getLogInfo(
d: LogCurveDataType,
logrun_name: string,
log_name: string
): { name: string; description: string } | undefined {
if (!isSelectedLogRun(d, logrun_name)) return undefined;

const log_id = getLogIndexByName(d, log_name);
return d.curves[log_id];
}

function getDiscreteLogMetadata(d: LogCurveDataType, log_name: string) {
return d?.metadata_discrete[log_name];
}

function isSelectedLogRun(d: LogCurveDataType, logrun_name: string): boolean {
return d.header.name.toLowerCase() === logrun_name.toLowerCase();
}

function getWellObjectByName(
wells_data: Feature[],
name: string
): Feature | undefined {
return wells_data.find(
(item) => item.properties?.name.toLowerCase() === name?.toLowerCase()
);
}

function getWellCoordinates(well_object: Feature): Position[] {
return (
(well_object.geometry as GeometryCollection)?.geometries.find(
(item) => item.type == "LineString"
) as LineString
)?.coordinates;
}

function getWellMds(well_object: Feature): number[] {
return well_object.properties?.md[0];
}

function getNeighboringMdIndices(mds: number[], md: number): number[] {
const idx = mds.findIndex((x) => x >= md);
return idx === 0 ? [idx, idx + 1] : [idx - 1, idx];
}

function getLogPath(
wells_data: Feature[],
d: LogCurveDataType,
logrun_name: string
): Position[] {
const well_object = getWellObjectByName(wells_data, d.header.well);
if (well_object == undefined) return [];

const well_xy = getWellCoordinates(well_object);
const well_mds = getWellMds(well_object);
if (well_xy.length == 0 || well_mds.length == 0) return [];

const log_xy: Position[] = [];
const log_mds = getLogMd(d, logrun_name);
log_mds.forEach((md) => {
const [l_idx, h_idx] = getNeighboringMdIndices(well_mds, md);
const md_normalized =
(md - well_mds[l_idx]) / (well_mds[h_idx] - well_mds[l_idx]);
const xy = interpolateNumberArray(
well_xy[l_idx],
well_xy[h_idx]
)(md_normalized);
log_xy.push(xy);
});
return log_xy;
}

function getLogIndexByName(d: LogCurveDataType, log_name: string): number {
return d.curves.findIndex(
(item) => item.name.toLowerCase() === log_name.toLowerCase()
);
}

function getLogIndexByNames(d: LogCurveDataType, names: string[]): number {
for (const name of names) {
const index = d.curves.findIndex(
(item) => item.name.toLowerCase() === name.toLowerCase()
);
if (index >= 0) return index;
}
return -1;
}
Expand All @@ -216,25 +313,24 @@ function getLogColor(
logrun_name: string,
log_name: string
): RGBAColor[] {
const log_id = getLogIDByName(d, logrun_name, log_name);
if (!d?.curves?.[log_id]) {
return [];
}
const log_data = getLogValues(d, logrun_name, log_name);
const log_info = getLogInfo(d, logrun_name, log_name);
if (log_data.length == 0 || log_info == undefined) return [];

const log_color: RGBAColor[] = [];
if (d?.curves[log_id]?.description == "continuous") {
const min = Math.min(...d?.data[log_id]);
const max = Math.max(...d?.data[log_id]);
if (log_info.description == "continuous") {
const min = Math.min(...log_data);
const max = Math.max(...log_data);
const max_delta = max - min;
d.data[log_id].forEach((value) => {
log_data.forEach((value) => {
const rgb = color(color_interp((value - min) / max_delta))?.rgb();
if (rgb != undefined) {
log_color.push([rgb.r, rgb.g, rgb.b]);
}
});
} else {
const log_attributes = d.metadata_discrete[log_name]?.objects;
d.data[log_id].forEach((log_value) => {
const log_attributes = getDiscreteLogMetadata(d, log_name)?.objects;
log_data.forEach((log_value) => {
const dl_attrs = Object.entries(log_attributes).find(
([, value]) => value[1] == log_value
)?.[1];
Expand All @@ -251,8 +347,7 @@ function getLogWidth(
logrun_name: string,
log_name: string
): number[] {
const log_id = getLogIDByName(d, logrun_name, log_name);
return d?.data?.[log_id];
return getLogValues(d, logrun_name, log_name);
}

function squared_distance(a, b): number {
Expand Down Expand Up @@ -303,7 +398,7 @@ function getMd(pickInfo): number | null {
return (md0 * c1 + md1 * c0) / dv;
}

function getMdProperty(info): PropertyDataType | null {
function getMdProperty(info: PickInfo<unknown>): PropertyDataType | null {
const md = getMd(info);
if (md != null) {
const prop_name = "MD " + (info.object as Feature)?.properties?.name;
Expand All @@ -313,8 +408,16 @@ function getMdProperty(info): PropertyDataType | null {
}

// Returns segment index of discrete logs
function getDiscLogSegmentIndex(info): number {
const trajectory = (info.object as LogCurveDataType)?.data[0];
function getLogSegmentIndex(
wells_data: Feature[],
info: PickInfo<unknown>,
logrun_name: string
): number {
const trajectory = getLogPath(
wells_data,
info.object as LogCurveDataType,
logrun_name
);

let min_d = Number.MAX_VALUE;
let segment_index = 0;
Expand All @@ -329,24 +432,30 @@ function getDiscLogSegmentIndex(info): number {
}

function getLogProperty(
info,
wells_data: Feature[],
info: PickInfo<unknown>,
logrun_name: string,
log_name: string
): PropertyDataType | null {
const info_object = info.object as LogCurveDataType;
if (!info_object?.data) return null;

const log_id = getLogIDByName(info_object, logrun_name, log_name);
const log = info_object.curves?.[log_id].name;

const data_objects = info_object.metadata_discrete[log]?.objects;

const segment_index = getDiscLogSegmentIndex(info);
let log_value: number | string = info_object.data[log_id][segment_index];
const dl_attrs = Object.entries(data_objects).find(
([, value]) => value[1] == log_value
);
const segment_index = getLogSegmentIndex(wells_data, info, logrun_name);
let log_value: number | string = getLogValues(
info_object,
logrun_name,
log_name
)[segment_index];

let dl_attrs: [string, [RGBAColor, number]] | undefined = undefined;
const dl_metadata = getDiscreteLogMetadata(info_object, log_name)?.objects;
if (dl_metadata) {
dl_attrs = Object.entries(dl_metadata).find(
([, value]) => value[1] == log_value
);
}

const log = getLogInfo(info_object, logrun_name, log_name)?.name;
const prop_name = log + " " + info_object.header.well;
log_value = dl_attrs ? dl_attrs[0] + " (" + log_value + ")" : log_value;

Expand Down

0 comments on commit dfa874e

Please sign in to comment.