Skip to content

Commit

Permalink
Allow comparing tables with missing identifier columns
Browse files Browse the repository at this point in the history
- Allow comparing tables with unequal number of columns, they must have at least one common column.
- For common columns, the values has to be equal.

- Minor typing fix in back-end, no functional adjustments needed
  • Loading branch information
jorgenherje committed Feb 7, 2025
1 parent b5b49f4 commit e8d4259
Show file tree
Hide file tree
Showing 13 changed files with 141 additions and 117 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ async def _get_volume_df_per_fluid_selection_and_categorized_result_names_async(
Calculation of volume names and properties, and creation of the results is handled outside this function.
"""
# Check for empty identifier selections
# Check for empty identifier selection lists
has_empty_identifier_selection = any(
not identifier_with_values.values for identifier_with_values in identifiers_with_values
)
Expand Down Expand Up @@ -285,10 +285,10 @@ async def _get_volume_df_per_fluid_selection_and_categorized_result_names_async(
)

# Get volume table per fluid selection - requested volumes and volumes needed for properties
volume_df_per_fluid_selection: dict[
FluidSelection, pl.DataFrame
] = await self._create_volume_df_per_fluid_selection(
table_name, all_volume_names, fluid_zones, realizations, identifiers_with_values, accumulate_fluid_zones
volume_df_per_fluid_selection: dict[FluidSelection, pl.DataFrame] = (
await self._create_volume_df_per_fluid_selection(
table_name, all_volume_names, fluid_zones, realizations, identifiers_with_values, accumulate_fluid_zones
)
)

# If accumulate_fluid_zones is True, exclude BO and BG from valid properties
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from enum import StrEnum
from dataclasses import dataclass
from typing import Dict, List, Union


# NOTE:
Expand Down Expand Up @@ -88,57 +87,57 @@ class CategorizedResultNames:
Class to hold categorized result names
Attributes:
- volume_names: List[str] - Basic volume names among result names
- calculated_volume_names: List[str] - Calculated volume names among result names (STOIIP_TOTAL, GIIP_TOTAL)
- property_names: List[str] - Property names among result names
- volume_names: list[str] - Basic volume names among result names
- calculated_volume_names: list[str] - Calculated volume names among result names (STOIIP_TOTAL, GIIP_TOTAL)
- property_names: list[str] - Property names among result names
"""

volume_names: List[str]
calculated_volume_names: List[str]
property_names: List[str]
volume_names: list[str]
calculated_volume_names: list[str]
property_names: list[str]


@dataclass
class InplaceVolumetricsIdentifierWithValues:
"""
Unique values for an identifier column in an inplace volumetrics table
Unique values for an identifier column in an inplace volumetric table
NOTE: Ideally all values should be strings, but it is possible that some values are integers - especially for REGION
"""

identifier: InplaceVolumetricsIdentifier
values: List[Union[str, int]] # List of values: str or int
values: list[str | int] # list of values: str or int


@dataclass
class InplaceVolumetricsTableDefinition:
"""Definition of a volumetric table"""

table_name: str
identifiers_with_values: List[InplaceVolumetricsIdentifierWithValues]
result_names: List[str]
fluid_zones: List[FluidZone]
identifiers_with_values: list[InplaceVolumetricsIdentifierWithValues]
result_names: list[str]
fluid_zones: list[FluidZone]


@dataclass
class RepeatedTableColumnData:
"""Definition of a column with repeated column data"""

column_name: str
unique_values: List[str | int] # ["Valysar", "Therys", "Volon"]
indices: List[int] # [0, 1, 1, 1, 2, 2, 2]. Length = number of rows in the table
unique_values: list[str | int] # ["Valysar", "Therys", "Volon"]
indices: list[int] # [0, 1, 1, 1, 2, 2, 2]. Length = number of rows in the table


@dataclass
class TableColumnData:
column_name: str
values: List[float] # Column values Length = number of rows in the table
values: list[float] # Column values Length = number of rows in the table


@dataclass
class TableColumnStatisticalData:
column_name: str
statistic_values: Dict[Statistic, List[float]] # Statistics values Length = number of rows in the table
statistic_values: dict[Statistic, list[float]] # Statistics values Length = number of rows in the table


@dataclass
Expand All @@ -148,10 +147,10 @@ class InplaceVolumetricTableData:
Contains data for a single fluid zone, e.g. Oil, Gas, Water, or sum of fluid zones
"""

# fluid_zones: List[FluidZone] # Oil, Gas, Water or "Oil + Gas", etc.
# fluid_zones: list[FluidZone] # Oil, Gas, Water or "Oil + Gas", etc.
fluid_selection_name: str # Oil, Gas, Water or "Oil + Gas", etc.
selector_columns: List[RepeatedTableColumnData] # Index columns and realizations
result_columns: List[TableColumnData]
selector_columns: list[RepeatedTableColumnData] # Index columns and realizations
result_columns: list[TableColumnData]


@dataclass
Expand All @@ -163,8 +162,8 @@ class InplaceStatisticalVolumetricTableData:
"""

fluid_selection_name: str # Oil, Gas, Water or "Oil + Gas", etc.
selector_columns: List[RepeatedTableColumnData] # Index columns and realizations
result_column_statistics: List[TableColumnStatisticalData]
selector_columns: list[RepeatedTableColumnData] # Index columns and realizations
result_column_statistics: list[TableColumnStatisticalData]


@dataclass
Expand All @@ -175,7 +174,7 @@ class InplaceVolumetricTableDataPerFluidSelection:
Fluid selection can be single fluid zones, e.g. Oil, Gas, Water, or sum of fluid zones - Oil + Gas + Water
"""

table_data_per_fluid_selection: List[InplaceVolumetricTableData]
table_data_per_fluid_selection: list[InplaceVolumetricTableData]


@dataclass
Expand All @@ -186,4 +185,4 @@ class InplaceStatisticalVolumetricTableDataPerFluidSelection:
Fluid selection can be single fluid zones, e.g. Oil, Gas, Water, or sum of fluid zones - Oil + Gas + Water
"""

table_data_per_fluid_selection: List[InplaceStatisticalVolumetricTableData]
table_data_per_fluid_selection: list[InplaceStatisticalVolumetricTableData]
15 changes: 13 additions & 2 deletions frontend/src/lib/utils/fixupUserSelection.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
export enum FixupSelection {
SELECT_NONE = "SELECT_NONE",
SELECT_ALL = "SELECT_ALL",
SELECT_FIRST = "SELECT_FIRST",
}

export function fixupUserSelection<TSelection>(
userSelection: TSelection[],
validOptions: TSelection[],
selectAll: boolean = false
fixupSelection: FixupSelection = FixupSelection.SELECT_FIRST
): TSelection[] {
const newSelections = userSelection.filter((selection) => validOptions.includes(selection));
if (newSelections.length === 0 && validOptions.length > 0) {
if (selectAll) {
if (fixupSelection === FixupSelection.SELECT_ALL) {
return validOptions;
}
if (fixupSelection === FixupSelection.SELECT_NONE) {
return [];
}

// Fall back to selecting the first valid option
newSelections.push(validOptions[0]);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { FluidZone_api, InplaceVolumetricResultName_api, InplaceVolumetricsIdentifierWithValues_api } from "@api";
import { EnsembleSetAtom } from "@framework/GlobalAtoms";
import { fixupRegularEnsembleIdents } from "@framework/utils/ensembleUiHelpers";
import { fixupUserSelection } from "@lib/utils/fixupUserSelection";
import { FixupSelection, fixupUserSelection } from "@lib/utils/fixupUserSelection";
import { fixupUserSelectedIdentifierValues } from "@modules/_shared/InplaceVolumetrics/fixupUserSelectedIdentifierValues";
import { RealSelector, SelectorColumn, SourceAndTableIdentifierUnion } from "@modules/_shared/InplaceVolumetrics/types";
import {
Expand Down Expand Up @@ -121,7 +121,11 @@ export const selectedFluidZonesAtom = atom<FluidZone_api[]>((get) => {
return tableDefinitionsAccessor.getFluidZonesIntersection();
}

return fixupUserSelection(userSelectedFluidZones, tableDefinitionsAccessor.getFluidZonesIntersection(), true);
return fixupUserSelection(
userSelectedFluidZones,
tableDefinitionsAccessor.getFluidZonesIntersection(),
FixupSelection.SELECT_ALL
);
});

export const selectedResultNameAtom = atom<InplaceVolumetricResultName_api | null>((get) => {
Expand Down Expand Up @@ -174,7 +178,7 @@ export const selectedSelectorColumnAtom = atom<SelectorColumn | null>((get) => {

const possibleSelectorColumns = [
RealSelector.REAL,
...tableDefinitionsAccessor.getIdentifiersWithIntersectionValues().map((ident) => ident.identifier),
...tableDefinitionsAccessor.getCommonIdentifiersWithValues().map((ident) => ident.identifier),
];
if (!userSelectedSelectorColumn) {
return possibleSelectorColumns[0];
Expand All @@ -192,13 +196,12 @@ export const selectedIdentifiersValuesAtom = atom<InplaceVolumetricsIdentifierWi
const userSelectedIdentifierValues = get(userSelectedIdentifiersValuesAtom);
const tableDefinitionsAccessor = get(tableDefinitionsAccessorAtom);

const uniqueIdentifierValues = tableDefinitionsAccessor.getIdentifiersWithIntersectionValues();
const selectAllOnFixup = true;
const uniqueIdentifierValues = tableDefinitionsAccessor.getCommonIdentifiersWithValues();

const fixedUpIdentifierValues: InplaceVolumetricsIdentifierWithValues_api[] = fixupUserSelectedIdentifierValues(
userSelectedIdentifierValues,
uniqueIdentifierValues,
selectAllOnFixup
FixupSelection.SELECT_ALL
);

return fixedUpIdentifierValues;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,12 @@ export function Settings(props: ModuleSettingsProps<Interfaces>): React.ReactNod
.map((name) => ({ label: name, value: name, hoverText: createHoverTextForVolume(name) }));

// Create selector options
const identifiersIntersection = tableDefinitionsAccessor.getIdentifiersWithIntersectionValues().map((ident) => {
const commonIdentifiers = tableDefinitionsAccessor.getCommonIdentifiersWithValues().map((ident) => {
return ident.identifier;
});
const selectorOptions: DropdownOption<SelectorColumn>[] = [
{ label: RealSelector.REAL, value: RealSelector.REAL },
...identifiersIntersection.map((name) => ({ label: name, value: name })),
...commonIdentifiers.map((name) => ({ label: name, value: name })),
];

const subplotOptions = makeSubplotByOptions(tableDefinitionsAccessor, selectedTableNames);
Expand Down Expand Up @@ -163,7 +163,7 @@ export function Settings(props: ModuleSettingsProps<Interfaces>): React.ReactNod
isPending={tableDefinitionsQueryResult.isLoading}
availableFluidZones={tableDefinitionsAccessor.getFluidZonesIntersection()}
availableTableNames={tableDefinitionsAccessor.getTableNamesIntersection()}
availableIdentifiersWithValues={tableDefinitionsAccessor.getIdentifiersWithIntersectionValues()}
availableIdentifiersWithValues={tableDefinitionsAccessor.getCommonIdentifiersWithValues()}
selectedEnsembleIdents={selectedEnsembleIdents}
selectedFluidZones={selectedFluidZones}
selectedIdentifiersValues={selectedIdentifiersValues}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export function makeSubplotByOptions(
},
];

for (const identifier of tableDefinitionsAccessor.getIdentifiersWithIntersectionValues()) {
for (const identifier of tableDefinitionsAccessor.getCommonIdentifiersWithValues()) {
options.push({
value: identifier.identifier,
label: identifier.identifier,
Expand Down Expand Up @@ -98,7 +98,7 @@ export function makeColorByOptions(
});
}

for (const identifier of tableDefinitionsAccessor.getIdentifiersWithIntersectionValues()) {
for (const identifier of tableDefinitionsAccessor.getCommonIdentifiersWithValues()) {
if (selectedSubplotBy !== identifier.identifier) {
options.push({
value: identifier.identifier,
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/modules/InplaceVolumetricsPlot/view/view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export function View(props: ModuleViewProps<Interfaces>): React.ReactNode {
return "Failed to load volumetric table data";
}
if (!areSelectedTablesComparable) {
return "Selected volumetric tables are not comparable";
return "Selected volumetric tables are not comparable due to mismatching fluid zones, result names or identifier columns";
}

return null;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { FluidZone_api, InplaceVolumetricResultName_api, InplaceVolumetricsIdentifierWithValues_api } from "@api";
import { EnsembleSetAtom } from "@framework/GlobalAtoms";
import { fixupRegularEnsembleIdents } from "@framework/utils/ensembleUiHelpers";
import { fixupUserSelection } from "@lib/utils/fixupUserSelection";
import { FixupSelection, fixupUserSelection } from "@lib/utils/fixupUserSelection";
import { fixupUserSelectedIdentifierValues } from "@modules/_shared/InplaceVolumetrics/fixupUserSelectedIdentifierValues";
import { SourceAndTableIdentifierUnion, SourceIdentifier } from "@modules/_shared/InplaceVolumetrics/types";
import {
Expand Down Expand Up @@ -111,7 +111,11 @@ export const selectedFluidZonesAtom = atom<FluidZone_api[]>((get) => {
return tableDefinitionsAccessor.getFluidZonesIntersection();
}

return fixupUserSelection(userSelectedFluidZones, tableDefinitionsAccessor.getFluidZonesIntersection(), true);
return fixupUserSelection(
userSelectedFluidZones,
tableDefinitionsAccessor.getFluidZonesIntersection(),
FixupSelection.SELECT_ALL
);
});

export const selectedResultNamesAtom = atom<InplaceVolumetricResultName_api[]>((get) => {
Expand All @@ -136,28 +140,27 @@ export const selectedAccumulationOptionsAtom = atom<
SourceAndTableIdentifierUnion,
SourceIdentifier.ENSEMBLE | SourceIdentifier.TABLE_NAME
>[] = [SourceIdentifier.FLUID_ZONE];
for (const identifier of tableDefinitionsAccessor.getIdentifiersWithIntersectionValues()) {
for (const identifier of tableDefinitionsAccessor.getCommonIdentifiersWithValues()) {
availableUniqueAccumulationOptions.push(identifier.identifier);
}

if (!userSelectedAccumulation || userSelectedAccumulation.length === 0) {
return [];
}

return fixupUserSelection(userSelectedAccumulation, availableUniqueAccumulationOptions);
return fixupUserSelection(userSelectedAccumulation, availableUniqueAccumulationOptions, FixupSelection.SELECT_NONE);
});

export const selectedIdentifiersValuesAtom = atom<InplaceVolumetricsIdentifierWithValues_api[]>((get) => {
const userSelectedIdentifierValues = get(userSelectedIdentifiersValuesAtom);
const tableDefinitionsAccessor = get(tableDefinitionsAccessorAtom);

const uniqueIdentifierValues = tableDefinitionsAccessor.getIdentifiersWithIntersectionValues();
const selectAllOnFixup = true;
const uniqueIdentifierValues = tableDefinitionsAccessor.getCommonIdentifiersWithValues();

const fixedUpIdentifierValues: InplaceVolumetricsIdentifierWithValues_api[] = fixupUserSelectedIdentifierValues(
userSelectedIdentifierValues,
uniqueIdentifierValues,
selectAllOnFixup
FixupSelection.SELECT_ALL
);

return fixedUpIdentifierValues;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ export function Settings(props: ModuleSettingsProps<Interfaces>): React.ReactNod
const accumulateOptions: TagOption<
Omit<SourceAndTableIdentifierUnion, SourceIdentifier.ENSEMBLE | SourceIdentifier.TABLE_NAME>
>[] = [{ label: "FLUID ZONE", value: SourceIdentifier.FLUID_ZONE }];
for (const identifier of tableDefinitionsAccessor.getIdentifiersWithIntersectionValues()) {
for (const identifier of tableDefinitionsAccessor.getCommonIdentifiersWithValues()) {
accumulateOptions.push({ label: identifier.identifier, value: identifier.identifier });
}

Expand Down Expand Up @@ -162,7 +162,7 @@ export function Settings(props: ModuleSettingsProps<Interfaces>): React.ReactNod
isPending={tableDefinitionsQueryResult.isLoading}
availableFluidZones={tableDefinitionsAccessor.getFluidZonesIntersection()}
availableTableNames={tableDefinitionsAccessor.getTableNamesIntersection()}
availableIdentifiersWithValues={tableDefinitionsAccessor.getIdentifiersWithIntersectionValues()}
availableIdentifiersWithValues={tableDefinitionsAccessor.getCommonIdentifiersWithValues()}
selectedEnsembleIdents={selectedEnsembleIdents}
selectedFluidZones={selectedFluidZones}
selectedIdentifiersValues={selectedIdentifiersValues}
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/modules/InplaceVolumetricsTable/view/view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export function View(props: ModuleViewProps<Interfaces>): React.ReactNode {
return "Failed to load volumetric table data";
}
if (!areSelectedTablesComparable) {
return "Selected volumetric tables are not comparable";
return "Selected volumetric tables are not comparable due to mismatching fluid zones, result names or identifier columns";
}

return null;
Expand Down
Loading

0 comments on commit e8d4259

Please sign in to comment.