Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Well Log Viewer Module #702

Open
wants to merge 69 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
4e14df5
wip
rubenthoms Aug 12, 2024
f59fa77
First working version
rubenthoms Aug 13, 2024
dc39996
Improvements and bug fixes
rubenthoms Aug 13, 2024
2cd1e83
Documentation
rubenthoms Aug 13, 2024
dcdf563
Merge branch 'main' into drag-list-components
rubenthoms Aug 13, 2024
28cf492
Improvements to UX
rubenthoms Aug 14, 2024
1561c01
Minor changes to styling
rubenthoms Aug 14, 2024
5307a20
Change of `onItemMove` to `onItemMoved`to emphasize the moving is done
rubenthoms Aug 14, 2024
65a57a8
Added optional callback for allowing movement
rubenthoms Aug 14, 2024
d246368
Minor documentation fix
rubenthoms Aug 14, 2024
ce667a6
WIP: Created bare-bones implementation of wrapper component for Subsu…
Anders2303 Aug 14, 2024
2222487
Merge branch 'drag-list-components' into well-log-viewer
Anders2303 Aug 14, 2024
c8d53c5
Adjustments and new menu utility components
rubenthoms Aug 19, 2024
e3e1bef
Merge branch 'drag-list-components' into well-log-viewer
Anders2303 Aug 20, 2024
9de4ba5
Added global sync for MD-hover marker + fixed secondary scale curve u…
Anders2303 Aug 21, 2024
707a475
Implemented global syncronization for Vertical Scale and Intersection
Anders2303 Aug 23, 2024
f353e58
Merge branch 'main' into well-log-viewer
Anders2303 Aug 23, 2024
3af6684
Migrated to WellLogViewer to new init flow
Anders2303 Aug 23, 2024
97622a2
Added a (somewhat crude) way to handle composite plot types
Anders2303 Aug 23, 2024
8f148ee
Adjustments
rubenthoms Aug 27, 2024
548e20c
Merge remote-tracking branch 'equinor/main' into drag-list-components
rubenthoms Aug 27, 2024
4cbc970
Made curve colors remain static between renders
Anders2303 Aug 28, 2024
4bad88f
Moved EsvIntersection and VectorSelect to _shared
Anders2303 Aug 28, 2024
fa41d0a
Minor fixes, removal of debug code, scrollbars always slightly visible
rubenthoms Aug 28, 2024
890d665
Multiple bug fixes and code improvements
rubenthoms Aug 28, 2024
b1f5a29
Merge branch 'main' into well-log-viewer
Anders2303 Aug 29, 2024
98a2d21
Created a shared utility component for readouts. Made Intersection an…
Anders2303 Aug 29, 2024
5b5cb8f
Resolved compiler warnings
Anders2303 Aug 29, 2024
9887cf4
Made floatingbox attempt to move away from the mouse
Anders2303 Aug 29, 2024
7246873
Made box-flipping optional. Added some doc-comments
Anders2303 Aug 30, 2024
97f92f1
Made "Viewport" be a global framework type
Anders2303 Aug 30, 2024
faf27ef
fixed ts-errors
Anders2303 Aug 30, 2024
ad236f2
Made curve colortable be based on the active color theme
Anders2303 Aug 30, 2024
07d2ba7
Merge branch 'drag-list-components' into well-log-viewer
Anders2303 Aug 30, 2024
aa7a594
Merge branch 'general-readout-box-comp' into well-log-viewer
Anders2303 Aug 30, 2024
7fba538
Renamed components to make their purpose more clear
Anders2303 Sep 2, 2024
b6ad4ec
Updated track config flow to be curve-type first
Anders2303 Sep 3, 2024
0187016
Added placeholder prop to dropdown
Anders2303 Sep 3, 2024
183739e
WIP: Add option to separate dropdown options within flat groups
Anders2303 Sep 3, 2024
5ae84d9
Fix keyboard navigation with separator items in Dropdown
rubenthoms Sep 3, 2024
bc0674d
Added readout-box to well log viewer
Anders2303 Sep 4, 2024
123277b
Changed readout box placement to grid. Fixed item alignments
Anders2303 Sep 4, 2024
20c4d87
Fix endless scrolling issue by adding keys to item components
rubenthoms Sep 4, 2024
b6bc0e9
Merge remote-tracking branch 'origin/well-log-viewer' into well-log-v…
rubenthoms Sep 4, 2024
3412f8d
Stopping propagation of mousedown event
rubenthoms Sep 4, 2024
27333ea
Merge branch 'drag-list-components' into well-log-viewer
rubenthoms Sep 4, 2024
11323d4
Cleaned up callbacks, imports and destructuring
Anders2303 Sep 4, 2024
626636e
Add support for explicit realization list when calculating surface st…
sigurdp Aug 30, 2024
918d66e
Fix multi select initial anchor issue (#700)
rubenthoms Sep 2, 2024
1165e6b
Added "onUnloadInstance" option to module registration
Anders2303 Sep 5, 2024
5311b27
Added an atom utility to support per-instance localStorage persistenc…
Anders2303 Sep 5, 2024
55b5958
Adde persistence-example to MyModule2
Anders2303 Sep 5, 2024
2af7f90
Made track config persists between reloads
Anders2303 Sep 5, 2024
1a9b109
Made it possible to export and import tracks config
Anders2303 Sep 6, 2024
3931859
Fixed readout box showing every config change
Anders2303 Sep 6, 2024
7c4143a
Made the well-log curve endpoints be a little less error prone
Anders2303 Sep 9, 2024
3013c53
Made view handle errors and missing data more gracefully
Anders2303 Sep 9, 2024
850fbdc
Fixed module title and some other smaller things
Anders2303 Sep 9, 2024
f932be2
Fixed linter error in curve header convert
Anders2303 Sep 10, 2024
6164df9
Created unit tests for utilities + some minor fixes to related code
Anders2303 Sep 11, 2024
dd6374f
Renamed "LogTracks" to "TemplateTrackSettings"
Anders2303 Sep 11, 2024
248e2ea
Made it possible to select and view well-picks in the well-log viewer
Anders2303 Sep 12, 2024
9f1b841
Added option to pad log data with empty rows
Anders2303 Sep 12, 2024
9abc3ef
Added unit tests for well-pick transform and padded data rows
Anders2303 Sep 13, 2024
2f19d41
Merge remote-tracking branch 'jorgenherje/main' into well-log-viewer
rubenthoms Oct 9, 2024
4a9ff38
Adjustments and fixes after merge
rubenthoms Oct 9, 2024
6311139
Removal of unused code
rubenthoms Oct 9, 2024
f24b587
Fixed cornercase where curve names *sometimes* is dupliucated across …
Anders2303 Oct 10, 2024
f283567
Resolved linter error
Anders2303 Oct 10, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions backend_py/primary/primary/routers/well/converters.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,9 @@ def convert_wellbore_perforation_to_schema(
def convert_wellbore_log_curve_header_to_schema(
wellbore_log_curve_header: WellboreLogCurveHeader,
) -> schemas.WellboreLogCurveHeader:
if wellbore_log_curve_header.log_name is None:
raise AttributeError("Missing log name is not allowed")

return schemas.WellboreLogCurveHeader(
logName=wellbore_log_curve_header.log_name,
curveName=wellbore_log_curve_header.curve_name,
Expand All @@ -131,6 +134,9 @@ def convert_wellbore_log_curve_data_to_schema(
wellbore_log_curve_data: WellboreLogCurveData,
) -> schemas.WellboreLogCurveData:
return schemas.WellboreLogCurveData(
name=wellbore_log_curve_data.name,
unit=wellbore_log_curve_data.unit,
curveUnitDesc=wellbore_log_curve_data.curve_unit_desc,
indexMin=wellbore_log_curve_data.index_min,
indexMax=wellbore_log_curve_data.index_max,
minCurveValue=wellbore_log_curve_data.min_curve_value,
Expand Down
2 changes: 2 additions & 0 deletions backend_py/primary/primary/routers/well/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,8 @@ async def get_wellbore_log_curve_headers(
return [
converters.convert_wellbore_log_curve_header_to_schema(wellbore_log_curve_header)
for wellbore_log_curve_header in wellbore_log_curve_headers
# Missing log name implies garbage data, so we simply drop them
if wellbore_log_curve_header.log_name is not None
]


Expand Down
13 changes: 8 additions & 5 deletions backend_py/primary/primary/routers/well/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,16 +103,19 @@ class WellborePerforation(BaseModel):
class WellboreLogCurveHeader(BaseModel):
logName: str
curveName: str
curveUnit: str
curveUnit: str | None


class WellboreLogCurveData(BaseModel):
name: str
indexMin: float
indexMax: float
minCurveValue: float
maxCurveValue: float
dataPoints: list[list[float | None]]
curveAlias: str
curveDescription: str
curveAlias: str | None
curveDescription: str | None
indexUnit: str
noDataValue: float
noDataValue: float | None
unit: str
curveUnitDesc: str | None
dataPoints: list[list[float | None]]
28 changes: 22 additions & 6 deletions backend_py/primary/primary/services/ssdl_access/types.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from typing import Any
from pydantic import BaseModel


Expand Down Expand Up @@ -34,18 +35,33 @@ class WellborePerforation(BaseModel):


class WellboreLogCurveHeader(BaseModel):
log_name: str
log_name: str | None
curve_name: str
curve_unit: str
curve_unit: str | None

# Defining a hash-function to facilitate usage in Sets
def __hash__(self) -> int:
# No globally unique field, but curve-name should be unique (per wellbore)
return hash(self.curve_name + (self.log_name or "N/A"))

def __eq__(self, other: Any) -> bool:
if not isinstance(other, WellboreLogCurveHeader):
# delegate to the other item in the comparison
return NotImplemented

return (self.curve_name, self.log_name) == (other.curve_name, other.log_name)


class WellboreLogCurveData(BaseModel):
name: str
index_min: float
index_max: float
min_curve_value: float
max_curve_value: float
DataPoints: list[list[float | None]]
curve_alias: str
curve_description: str
curve_alias: str | None
curve_description: str | None
index_unit: str
no_data_value: float
no_data_value: float | None
unit: str
curve_unit_desc: str | None
DataPoints: list[list[float | None]]
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,12 @@ async def get_log_curve_headers_for_wellbore(self, wellbore_uuid: str) -> List[t
endpoint = f"WellLog/{wellbore_uuid}"
ssdl_data = await fetch_from_ssdl(access_token=self._ssdl_token, endpoint=endpoint, params=None)
try:
result = [types.WellboreLogCurveHeader.model_validate(log_curve) for log_curve in ssdl_data]
# This endpoint is a bit weird, and MIGHT return duplicates which, as far as I can tell, are the exact same. Using a set to drop duplicates. See data model for comparator
result_set = {types.WellboreLogCurveHeader.model_validate(log_curve) for log_curve in ssdl_data}

except ValidationError as error:
raise InvalidDataError(f"Invalid log curve headers for wellbore {wellbore_uuid}", Service.SSDL) from error
return result
return list(result_set)

async def get_log_curve_headers_for_field(self, field_uuid: str) -> List[types.WellboreLogCurveHeader]:
endpoint = f"WellLog/field/{field_uuid}"
Expand Down
31 changes: 31 additions & 0 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"@webviz/group-tree-plot": "^1.1.14",
"@webviz/subsurface-viewer": "^0.25.2",
"@webviz/well-completions-plot": "^1.4.1",
"@webviz/well-log-viewer": "^1.12.7",
"animate.css": "^4.1.1",
"axios": "^1.6.5",
"culori": "^3.2.0",
Expand Down
11 changes: 7 additions & 4 deletions frontend/src/api/models/WellboreLogCurveData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,17 @@
/* tslint:disable */
/* eslint-disable */
export type WellboreLogCurveData = {
name: string;
indexMin: number;
indexMax: number;
minCurveValue: number;
maxCurveValue: number;
dataPoints: Array<Array<(number | null)>>;
curveAlias: string;
curveDescription: string;
curveAlias: (string | null);
curveDescription: (string | null);
indexUnit: string;
noDataValue: number;
noDataValue: (number | null);
unit: string;
curveUnitDesc: (string | null);
dataPoints: Array<Array<(number | null)>>;
};

2 changes: 1 addition & 1 deletion frontend/src/api/models/WellboreLogCurveHeader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
export type WellboreLogCurveHeader = {
logName: string;
curveName: string;
curveUnit: string;
curveUnit: (string | null);
};

4 changes: 4 additions & 0 deletions frontend/src/framework/AtomStoreMaster.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { WritableAtom, createStore } from "jotai";

import { CurrentModuleInstanceIdAtom } from "./GlobalAtoms";

export type AtomStore = ReturnType<typeof createStore>;

export class AtomStoreMaster {
Expand All @@ -16,6 +18,8 @@ export class AtomStoreMaster {

makeAtomStoreForModuleInstance(moduleInstanceId: string) {
const atomStore = createStore();
// Make the module's own id available within each module's store
atomStore.set(CurrentModuleInstanceIdAtom, moduleInstanceId);
this._atomStores.set(moduleInstanceId, atomStore);

const atomStates = Array.from(this._atomStates.entries());
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/framework/GlobalAtoms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ import { RealizationFilterSet } from "./RealizationFilterSet";
import { EnsembleRealizationFilterFunction } from "./WorkbenchSession";
import { atomWithCompare } from "./utils/atomUtils";

/** A module's instance-id. Available in the jotai-store of each module, otherwise null */
// ? Should this one be moved to `AtomStoreMaster.ts`?
export const CurrentModuleInstanceIdAtom = atom<string | null>(null);

export const EnsembleSetAtom = atomWithCompare<EnsembleSet>(new EnsembleSet([]), isEqual);

/**
Expand Down
9 changes: 9 additions & 0 deletions frontend/src/framework/Module.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import { WorkbenchServices } from "./WorkbenchServices";
import { WorkbenchSession } from "./WorkbenchSession";
import { WorkbenchSettings } from "./WorkbenchSettings";

export type OnInstanceUnloadFunc = (instanceId: string) => void;

export enum ModuleCategory {
MAIN = "main",
SUB = "sub",
Expand Down Expand Up @@ -105,6 +107,7 @@ export interface ModuleOptions {
description?: string;
channelDefinitions?: ChannelDefinition[];
channelReceiverDefinitions?: ChannelReceiverDefinition[];
onUnloadInstanceFunc?: OnInstanceUnloadFunc;
}

export class Module<TInterfaceTypes extends ModuleInterfaceTypes> {
Expand All @@ -127,6 +130,7 @@ export class Module<TInterfaceTypes extends ModuleInterfaceTypes> {
private _workbench: Workbench | null = null;
private _syncableSettingKeys: SyncSettingKey[];
private _drawPreviewFunc: DrawPreviewFunc | null;
private _onUnloadInstanceFunc: OnInstanceUnloadFunc | null;
private _description: string | null;
private _channelDefinitions: ChannelDefinition[] | null;
private _channelReceiverDefinitions: ChannelReceiverDefinition[] | null;
Expand All @@ -143,6 +147,7 @@ export class Module<TInterfaceTypes extends ModuleInterfaceTypes> {
this.settingsFC = () => <div>Not defined</div>;
this._syncableSettingKeys = options.syncableSettingKeys ?? [];
this._drawPreviewFunc = options.drawPreviewFunc ?? null;
this._onUnloadInstanceFunc = options.onUnloadInstanceFunc ?? null;
this._description = options.description ?? null;
this._channelDefinitions = options.channelDefinitions ?? null;
this._channelReceiverDefinitions = options.channelReceiverDefinitions ?? null;
Expand Down Expand Up @@ -242,6 +247,10 @@ export class Module<TInterfaceTypes extends ModuleInterfaceTypes> {
return instance;
}

onInstanceUnload(instanceId: string) {
this._onUnloadInstanceFunc?.(instanceId);
}

private setImportState(state: ImportState): void {
this._importState = state;
this._moduleInstances.forEach((instance) => {
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/framework/ModuleInstance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,10 @@ export class ModuleInstance<TInterfaceTypes extends ModuleInterfaceTypes> {
getInitialSettings(): InitialSettings | null {
return this._initialSettings;
}

unload() {
this._module.onInstanceUnload(this._id);
}
}

export function useModuleInstanceTopicValue<T extends ModuleInstanceTopic>(
Expand Down
11 changes: 10 additions & 1 deletion frontend/src/framework/ModuleRegistry.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import { ChannelDefinition, ChannelReceiverDefinition } from "./DataChannelTypes";
import { InterfaceEffects, Module, ModuleCategory, ModuleDevState, ModuleInterfaceTypes } from "./Module";
import {
InterfaceEffects,
Module,
ModuleCategory,
ModuleDevState,
ModuleInterfaceTypes,
OnInstanceUnloadFunc,
} from "./Module";
import { ModuleDataTagId } from "./ModuleDataTags";
import { DrawPreviewFunc } from "./Preview";
import { SyncSettingKey } from "./SyncSettings";
Expand All @@ -17,6 +24,7 @@ export type RegisterModuleOptions = {
channelReceiverDefinitions?: ChannelReceiverDefinition[];
preview?: DrawPreviewFunc;
description?: string;
onUnloadInstance?: OnInstanceUnloadFunc;
};

export class ModuleNotFoundError extends Error {
Expand Down Expand Up @@ -49,6 +57,7 @@ export class ModuleRegistry {
channelDefinitions: options.channelDefinitions,
channelReceiverDefinitions: options.channelReceiverDefinitions,
drawPreviewFunc: options.preview,
onUnloadInstanceFunc: options.onUnloadInstance,
description: options.description,
});
this._registeredModules[options.moduleName] = module;
Expand Down
8 changes: 6 additions & 2 deletions frontend/src/framework/Workbench.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,12 @@ export class Workbench {
}

removeModuleInstance(moduleInstanceId: string): void {
const manager = this.getModuleInstance(moduleInstanceId)?.getChannelManager();
if (manager) {
const moduleInstance = this.getModuleInstance(moduleInstanceId);

if (moduleInstance) {
const manager = moduleInstance.getChannelManager();

moduleInstance.unload();
manager.unregisterAllChannels();
manager.unregisterAllReceivers();
}
Expand Down
14 changes: 14 additions & 0 deletions frontend/src/framework/utils/arrays.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* Util method to do an immutable item move in an array
* @param array The array to move items in
* @param from The index of the first item being moved
* @param to The index the item(s) should be moved to
* @param moveAmt The amount of items (from the start-index) that should be moved
* @returns A copy of the original array, with it's items moved accordingly
*/
export function arrayMove<t>(array: t[], from: number, to: number, moveAmt = 1): t[] {
const newArrray = [...array];
const movedItems = newArrray.splice(from, moveAmt);

return newArrray.toSpliced(to, 0, ...movedItems);
}
Loading
Loading