Skip to content

Commit

Permalink
Bug fix and code cleanup
Browse files Browse the repository at this point in the history
- Fix bug with propagation of selected parameter from settings to view (useValidState in view)
- Minor code cleanup for readability
  • Loading branch information
jorgenherje committed Sep 20, 2023
1 parent b847edc commit 2ab2fdd
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 88 deletions.
60 changes: 42 additions & 18 deletions frontend/src/modules/SimulationTimeSeriesMatrix/settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { Label } from "@lib/components/Label";
import { RadioGroup } from "@lib/components/RadioGroup";
import { SmartNodeSelectorSelection, TreeDataNode } from "@lib/components/SmartNodeSelector";
import { VectorSelector } from "@lib/components/VectorSelector";
import { useValidState } from "@lib/hooks/useValidState";
import { resolveClassNames } from "@lib/utils/resolveClassNames";
import { createVectorSelectorDataFromVectors } from "@lib/utils/vectorSelectorUtils";

Expand Down Expand Up @@ -45,11 +46,11 @@ export function settings({ moduleContext, workbenchSession }: ModuleFCProps<Stat
const [groupBy, setGroupBy] = moduleContext.useStoreState("groupBy");
const [colorRealizationsByParameter, setColorRealizationsByParameter] =
moduleContext.useStoreState("colorRealizationsByParameter");
const [parameterIdent, setParameterIdent] = moduleContext.useStoreState("parameterIdent");
const [visualizationMode, setVisualizationMode] = moduleContext.useStoreState("visualizationMode");
const [showHistorical, setShowHistorical] = moduleContext.useStoreState("showHistorical");
const [showObservations, setShowObservations] = moduleContext.useStoreState("showObservations");
const [statisticsSelection, setStatisticsSelection] = moduleContext.useStoreState("statisticsSelection");
const setParameterIdent = moduleContext.useSetStoreValue("parameterIdent");
const setVectorSpecifications = moduleContext.useSetStoreValue("vectorSpecifications");

// States
Expand All @@ -70,28 +71,33 @@ export function settings({ moduleContext, workbenchSession }: ModuleFCProps<Stat
const selectedVectorNamesHasHistorical = ensembleVectorListsHelper.hasAnyHistoricalVector(selectedVectorNames);
const currentVectorSelectorData = createVectorSelectorDataFromVectors(vectorsUnion);

// Get union of continuous parameters for selected ensembles
const continuousParametersUnion: ParameterIdent[] = [];
// Get union of continuous and non-constant parameters for selected ensembles
const continuousAndNonConstantParametersUnion: ParameterIdent[] = [];
for (const ensembleIdent of selectedEnsembleIdents) {
const ensemble = ensembleSet.findEnsemble(ensembleIdent);
if (ensemble === null) continue;

for (const parameter of ensemble.getParameters().getParameterIdents(ParameterType.CONTINUOUS)) {
if (continuousParametersUnion.some((param) => param.equals(parameter))) continue;

// TODO: Check if parameter is constant?
if (continuousAndNonConstantParametersUnion.some((param) => param.equals(parameter))) continue;
if (ensemble.getParameters().getParameter(parameter).isConstant) continue;

continuousParametersUnion.push(parameter);
continuousAndNonConstantParametersUnion.push(parameter);
}
}

// Only update if all vector lists are retrieved before updating vectorSelectorData has changed
// Use valid state for selected parameter ident
const [selectedParameterIdentStr, setSelectedParameterIdentStr] = useValidState<string | null>(null, [
continuousAndNonConstantParametersUnion,
(item: ParameterIdent) => item.toString(),
]);

// Await update of vectorSelectorData until all vector lists are retrieved
const hasVectorListQueriesErrorOrLoading = vectorListQueries.some((query) => query.isLoading || query.isError);
if (!hasVectorListQueriesErrorOrLoading && !isEqual(currentVectorSelectorData, vectorSelectorData)) {
setVectorSelectorData(currentVectorSelectorData);
}

// Handle update of ensemble set
if (!isEqual(ensembleSet, previousEnsembleSet)) {
const newSelectedEnsembleIdents = selectedEnsembleIdents.filter(
(ensemble) => ensembleSet.findEnsemble(ensemble) !== null
Expand Down Expand Up @@ -120,20 +126,36 @@ export function settings({ moduleContext, workbenchSession }: ModuleFCProps<Stat
});
}
}

setVectorSpecifications(newVectorSpecifications);
},
[selectedEnsembleIdents, selectedVectorNames, ensembleVectorListsHelper.numberOfQueriesWithData()]
);

React.useEffect(
function propagateParameterIdentToView() {
if (selectedParameterIdentStr === null) {
setParameterIdent(null);
return;
}

try {
const newParameterIdent = ParameterIdent.fromString(selectedParameterIdentStr);
continuousAndNonConstantParametersUnion.some((parameter) => parameter.equals(newParameterIdent))
? setParameterIdent(newParameterIdent)
: setParameterIdent(null);
} catch {
setParameterIdent(null);
}
},
[selectedParameterIdentStr]
);

function handleGroupByChange(event: React.ChangeEvent<HTMLInputElement>) {
setGroupBy(event.target.value as GroupBy);
}

function handleColorByParameterChange(parameterIdentStr: string) {
const parameterIdent = ParameterIdent.fromString(parameterIdentStr);
const newParameterIdent = continuousParametersUnion.find((elm) => elm.equals(parameterIdent)) ?? null;
setParameterIdent(newParameterIdent);
setSelectedParameterIdentStr(parameterIdentStr);
}

function handleEnsembleSelectChange(ensembleIdentArr: EnsembleIdent[]) {
Expand Down Expand Up @@ -263,7 +285,6 @@ export function settings({ moduleContext, workbenchSession }: ModuleFCProps<Stat
>
<VectorSelector
data={vectorSelectorData}
selectedTags={selectedVectorNames}
placeholder="Add new vector..."
maxNumSelectedNodes={50}
numSecondsUntilSuggestionsAreShown={0.5}
Expand All @@ -273,9 +294,9 @@ export function settings({ moduleContext, workbenchSession }: ModuleFCProps<Stat
</ApiStatesWrapper>
</div>
</CollapsibleGroup>
<CollapsibleGroup expanded={true} title="Color selection">
<CollapsibleGroup expanded={true} title="Color by parameter">
<Checkbox
label="Color realizations by continuous parameter"
label="Enable"
checked={colorRealizationsByParameter}
disabled={visualizationMode !== VisualizationMode.INDIVIDUAL_REALIZATIONS}
onChange={(event) => {
Expand All @@ -291,10 +312,13 @@ export function settings({ moduleContext, workbenchSession }: ModuleFCProps<Stat
)}
>
<Dropdown
options={continuousParametersUnion.map((elm) => {
return { value: elm.toString(), label: elm.toString() };
options={continuousAndNonConstantParametersUnion.map((elm) => {
return {
value: elm.toString(),
label: elm.groupName ? `${elm.groupName}:${elm.name}` : elm.name,
};
})}
value={parameterIdent?.toString() ?? undefined}
value={selectedParameterIdentStr?.toString() ?? undefined}
onChange={handleColorByParameterChange}
/>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import {
VectorStatisticData_api,
} from "@api";

import { type } from "os";

import { FanchartData, FreeLineData, LowHighData, MinMaxData } from "./fanchartPlotting";
import { createFanchartTraces } from "./fanchartPlotting";
import { LineData, StatisticsData, createStatisticsTraces } from "./statisticsPlotting";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { VectorHistoricalData_api, VectorRealizationData_api, VectorStatisticData_api } from "@api";
import { ColorScale } from "@lib/utils/ColorScale";
// import { ColorScale } from "@lib/utils/ColorScale";
import { ColorSet } from "@lib/utils/ColorSet";

import { PlotMarker } from "plotly.js";
Expand Down Expand Up @@ -58,7 +58,7 @@ export class SubplotBuilder {
private _width = 0;
private _height = 0;

private _defaultScatterType = "scatter";
private _scatterType: "scatter" | "scattergl";

private _parameterColorScaleHelper: ContinuousParameterColorScaleHelper | null = null;
private _parameterFallbackColor = "#808080";
Expand All @@ -69,7 +69,8 @@ export class SubplotBuilder {
colorSet: ColorSet,
width: number,
height: number,
parameterColorScaleHelper?: ContinuousParameterColorScaleHelper
parameterColorScaleHelper?: ContinuousParameterColorScaleHelper,
scatterType: "scatter" | "scattergl" = "scatter"
) {
this._selectedVectorSpecifications = selectedVectorSpecifications;
this._width = width;
Expand Down Expand Up @@ -97,6 +98,7 @@ export class SubplotBuilder {
: this._uniqueEnsembleNames.length;

this._parameterColorScaleHelper = parameterColorScaleHelper ?? null;
this._scatterType = scatterType;

// TODO:
// - Handle keep uirevision?
Expand Down Expand Up @@ -253,8 +255,6 @@ export class SubplotBuilder {
): void {
if (this._parameterColorScaleHelper === null) return;

this._defaultScatterType = "scattergl";

// Only allow selected vectors
const selectedVectorsRealizationData = vectorsRealizationData.filter((vec) =>
this._selectedVectorSpecifications.some(
Expand Down Expand Up @@ -298,6 +298,7 @@ export class SubplotBuilder {
hoverTemplate: hoverTemplate,
showLegend: addLegendForTraces,
yaxis: `y${subplotIndex + 1}`,
type: this._scatterType,
});
this._plotData.push(vectorRealizationTrace);
this._hasRealizationsTracesColoredByParameter = true;
Expand All @@ -309,8 +310,6 @@ export class SubplotBuilder {
vectorsRealizationData: { vectorSpecification: VectorSpec; data: VectorRealizationData_api[] }[],
useIncreasedBrightness: boolean
): void {
this._defaultScatterType = "scattergl";

// Only allow selected vectors
const selectedVectorsRealizationData = vectorsRealizationData.filter((vec) =>
this._selectedVectorSpecifications.some(
Expand Down Expand Up @@ -349,6 +348,7 @@ export class SubplotBuilder {
hoverTemplate: hoverTemplate,
showLegend: addLegendForTraces,
yaxis: `y${subplotIndex + 1}`,
type: this._scatterType,
});

this._plotData.push(...vectorRealizationTraces);
Expand Down Expand Up @@ -380,6 +380,7 @@ export class SubplotBuilder {
hexColor: color,
legendGroup: legendGroup,
yaxis: `y${subplotIndex + 1}`,
type: this._scatterType,
});

this._plotData.push(...vectorFanchartTraces);
Expand Down Expand Up @@ -414,6 +415,7 @@ export class SubplotBuilder {
legendGroup: legendGroup,
yaxis: `y${subplotIndex + 1}`,
lineWidth: lineWidth,
type: this._scatterType,
});

this._plotData.push(...vectorStatisticsTraces);
Expand Down Expand Up @@ -443,6 +445,7 @@ export class SubplotBuilder {
vectorHistoricalData: elm.data,
color: this._historyVectorColor,
yaxis: `y${subplotIndex + 1}`,
type: this._scatterType,
});
this._plotData.push(vectorHistoryTrace);
});
Expand Down
99 changes: 38 additions & 61 deletions frontend/src/modules/SimulationTimeSeriesMatrix/view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,10 @@ import React from "react";
import Plot from "react-plotly.js";

import { Ensemble } from "@framework/Ensemble";
import { EnsembleIdent } from "@framework/EnsembleIdent";
import { ModuleFCProps } from "@framework/Module";
import { useEnsembleSet } from "@framework/WorkbenchSession";
import { useElementSize } from "@lib/hooks/useElementSize";
import { ColorScaleGradientType } from "@lib/utils/ColorScale";
// Note: Have for debug render count info
import { isDevMode } from "@lib/utils/devMode";

import { useHistoricalVectorDataQueries, useStatisticalVectorDataQueries, useVectorDataQueries } from "./queryHooks";
import { GroupBy, State, VisualizationMode } from "./state";
Expand All @@ -21,12 +18,6 @@ import {
} from "./utils/vectorSpecificationsAndQueriesUtils";

export const view = ({ moduleContext, workbenchSession, workbenchSettings }: ModuleFCProps<State>) => {
// Leave this in until we get a feeling for React18/Plotly
const renderCount = React.useRef(0);
React.useEffect(function incrementRenderCount() {
renderCount.current = renderCount.current + 1;
});

const wrapperDivRef = React.useRef<HTMLDivElement>(null);
const wrapperDivSize = useElementSize(wrapperDivRef);

Expand All @@ -49,29 +40,6 @@ export const view = ({ moduleContext, workbenchSession, workbenchSettings }: Mod
gradientType: ColorScaleGradientType.Diverging,
});

// Get range for color scale
const uniqueEnsembleIdents: EnsembleIdent[] = [];
vectorSpecifications?.forEach((specification) => {
if (uniqueEnsembleIdents.some((ensembleIdent) => ensembleIdent.equals(specification.ensembleIdent))) return;

uniqueEnsembleIdents.push(specification.ensembleIdent);
});

// Get ensemble objects from ensemble set
const selectedEnsembles: Ensemble[] = [];
for (const ensembleIdent of uniqueEnsembleIdents) {
const ensemble = ensembleSet.findEnsemble(ensembleIdent);
if (ensemble === null) continue;

selectedEnsembles.push(ensemble);
}

// Create parameter color scale helper
const doColorByParameter = colorRealizationsByParameter && parameterIdent !== null && selectedEnsembles.length > 0;
const parameterColorScaleHelper = doColorByParameter
? new ContinuousParameterColorScaleHelper(parameterIdent, selectedEnsembles, parameterColorScale)
: null;

// Queries
const vectorDataQueries = useVectorDataQueries(
vectorSpecifications,
Expand All @@ -96,6 +64,9 @@ export const view = ({ moduleContext, workbenchSession, workbenchSettings }: Mod
);

// Map vector specifications and queries with data
// TODO:
// - Add loading state if 1 or more queries are loading?
// - Can check for equal length of useQueries arrays and the loadedVectorSpecificationsAndData arrays?
const loadedVectorSpecificationsAndRealizationData = vectorSpecifications
? createLoadedVectorSpecificationAndDataArray(vectorSpecifications, vectorDataQueries)
: [];
Expand All @@ -109,40 +80,52 @@ export const view = ({ moduleContext, workbenchSession, workbenchSettings }: Mod
)
: [];

// TODO:
// - Add loading state if 1 or more queries are loading?
// - Can check for equal length of useQueries arrays and the loadedVectorSpecificationsAndData arrays?
// Retrieve selected ensembles from vector specifications
const selectedEnsembles: Ensemble[] = [];
vectorSpecifications?.forEach((vectorSpecification) => {
if (selectedEnsembles.some((ensemble) => ensemble.getIdent().equals(vectorSpecification.ensembleIdent))) {
return;
}

// Iterate over unique ensemble names and assign color from color palette
if (vectorSpecifications) {
const uniqueEnsembleNames: string[] = [];
vectorSpecifications.forEach((vectorSpec) => {
const ensembleName = vectorSpec.ensembleIdent.getEnsembleName();
if (!uniqueEnsembleNames.includes(ensembleName)) {
uniqueEnsembleNames.push(vectorSpec.ensembleIdent.getEnsembleName());
}
});
}
const ensemble = ensembleSet.findEnsemble(vectorSpecification.ensembleIdent);
if (ensemble === null) return;

selectedEnsembles.push(ensemble);
});

// Plot builder
// NOTE: useRef?
// Create parameter color scale helper
const doColorByParameter =
colorRealizationsByParameter &&
parameterIdent !== null &&
selectedEnsembles.some((ensemble) => ensemble.getParameters().findParameter(parameterIdent));
const parameterColorScaleHelper = doColorByParameter
? new ContinuousParameterColorScaleHelper(parameterIdent, selectedEnsembles, parameterColorScale)
: null;

// Create Plot Builder
const subplotOwner = groupBy === GroupBy.TIME_SERIES ? SubplotOwner.VECTOR : SubplotOwner.ENSEMBLE;
const scatterType =
visualizationMode === VisualizationMode.INDIVIDUAL_REALIZATIONS ||
visualizationMode === VisualizationMode.STATISTICS_AND_REALIZATIONS
? "scattergl"
: "scatter";
const subplotBuilder = new SubplotBuilder(
subplotOwner,
vectorSpecifications ?? [],
colorSet,
wrapperDivSize.width,
wrapperDivSize.height,
parameterColorScaleHelper ?? undefined
parameterColorScaleHelper ?? undefined,
scatterType
);

if (visualizationMode === VisualizationMode.INDIVIDUAL_REALIZATIONS) {
if (doColorByParameter) {
subplotBuilder.addRealizationTracesColoredByParameter(loadedVectorSpecificationsAndRealizationData);
} else {
const useIncreasedBrightness = false;
subplotBuilder.addRealizationsTraces(loadedVectorSpecificationsAndRealizationData, useIncreasedBrightness);
}
// Add traces based on visualization mode
if (doColorByParameter && visualizationMode === VisualizationMode.INDIVIDUAL_REALIZATIONS) {
subplotBuilder.addRealizationTracesColoredByParameter(loadedVectorSpecificationsAndRealizationData);
}
if (!doColorByParameter && visualizationMode === VisualizationMode.INDIVIDUAL_REALIZATIONS) {
const useIncreasedBrightness = false;
subplotBuilder.addRealizationsTraces(loadedVectorSpecificationsAndRealizationData, useIncreasedBrightness);
}
if (visualizationMode === VisualizationMode.STATISTICAL_FANCHART) {
const selectedVectorsFanchartStatisticData = filterVectorSpecificationAndFanchartStatisticsDataArray(
Expand Down Expand Up @@ -194,12 +177,6 @@ export const view = ({ moduleContext, workbenchSession, workbenchSettings }: Mod
onHover={handleHover}
onUnhover={handleUnHover}
/>
{isDevMode() && (
<>
<div className="absolute top-10 left-5 italic text-pink-400">(rc={renderCount.current})</div>
<div className="absolute top-10 left-20 italic text-pink-400"> Traces: {plotData.length}</div>
</>
)}
</div>
);
};

0 comments on commit 2ab2fdd

Please sign in to comment.