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 892f2ca commit 1333d17
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 126 deletions.
86 changes: 53 additions & 33 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 @@ -59,51 +60,52 @@ export function settings({ moduleContext, workbenchSession }: ModuleFCProps<Stat
const [vectorSelectorData, setVectorSelectorData] = React.useState<TreeDataNode[]>([]);
const [prevVisualizationMode, setPrevVisualizationMode] = React.useState<VisualizationMode>(visualizationMode);

if (prevVisualizationMode !== visualizationMode) {
if (visualizationMode !== prevVisualizationMode) {
setPrevVisualizationMode(visualizationMode);
}

if (!isEqual(ensembleSet, previousEnsembleSet)) {
const newSelectedEnsembleIdents = selectedEnsembleIdents.filter(
(ensemble) => ensembleSet.findEnsemble(ensemble) !== null
);
const validatedEnsembleIdents = fixupEnsembleIdents(newSelectedEnsembleIdents, ensembleSet) ?? [];
if (!isEqual(selectedEnsembleIdents, validatedEnsembleIdents)) {
setSelectedEnsembleIdents(validatedEnsembleIdents);
}

setPreviousEnsembleSet(ensembleSet);
}

// Queries
const vectorListQueries = useVectorListQueries(selectedEnsembleIdents);
const ensembleVectorListsHelper = new EnsembleVectorListsHelper(selectedEnsembleIdents, vectorListQueries);
const vectorsUnion = ensembleVectorListsHelper.vectorsUnion();
const selectedVectorNamesHasHistorical = ensembleVectorListsHelper.hasAnyHistoricalVector(selectedVectorNames);
const currentVectorSelectorData = createVectorSelectorDataFromVectors(vectorsUnion);
const currentVectorSelectorData = createVectorSelectorDataFromVectors(ensembleVectorListsHelper.vectorsUnion());

// Get union of continuous parameters for selected ensembles
const continuousParametersUnion: ParameterIdent[] = [];
// Get union of continuous and non-constant parameters for selected ensembles and set valid parameter ident str
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);
}
}
const [selectedParameterIdentStr, setSelectedParameterIdentStr] = useValidState<string | null>(null, [
continuousAndNonConstantParametersUnion,
(item: ParameterIdent) => item.toString(),
]);

// Only update if all vector lists are retrieved before updating vectorSelectorData has changed
// 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);
}

if (!isEqual(ensembleSet, previousEnsembleSet)) {
const newSelectedEnsembleIdents = selectedEnsembleIdents.filter(
(ensemble) => ensembleSet.findEnsemble(ensemble) !== null
);
const validatedEnsembleIdents = fixupEnsembleIdents(newSelectedEnsembleIdents, ensembleSet) ?? [];
if (!isEqual(selectedEnsembleIdents, validatedEnsembleIdents)) {
setSelectedEnsembleIdents(validatedEnsembleIdents);
}

setPreviousEnsembleSet(ensembleSet);
}

React.useEffect(
function propagateVectorSpecsToView() {
const newVectorSpecifications: VectorSpec[] = [];
Expand All @@ -120,20 +122,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 +281,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 +290,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 +308,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
Expand Up @@ -3,13 +3,20 @@ import { ContinuousParameter, ParameterIdent, ParameterType } from "@framework/E
import { ColorScale } from "@lib/utils/ColorScale";
import { MinMax } from "@lib/utils/MinMax";

export class ContinuousParameterColorScaleHelper {
export class EnsemblesContinuousParameterColoring {
/**
* Helper class working with coloring according to selected continuous parameter across multiple ensembles
*
* Retrieves min/max parameter value across all ensembles and provides interface for retrieving parameter
* value within the min/max range for specific realization in ensemble.
*/

private _parameterIdent: ParameterIdent;
private _ensembleContinuousParameterSet: { [ensembleName: string]: ContinuousParameter };
private _minMax: MinMax;
private _colorScale: ColorScale;

constructor(parameterIdent: ParameterIdent, selectedEnsembles: Ensemble[], colorScale: ColorScale) {
constructor(selectedEnsembles: Ensemble[], parameterIdent: ParameterIdent, colorScale: ColorScale) {
this._parameterIdent = parameterIdent;
this._ensembleContinuousParameterSet = {};
this._minMax = MinMax.createInvalid();
Expand All @@ -24,7 +31,7 @@ export class ContinuousParameterColorScaleHelper {
}
}

// TODO: Set Range [0,0] if parameterMinMax is invalid?
// Consider: Set Range [0,0] if parameterMinMax is invalid?
this._colorScale = colorScale;
const midValue = this._minMax.min + (this._minMax.max - this._minMax.min) / 2;
this._colorScale.setRangeAndMidPoint(this._minMax.min, this._minMax.max, midValue);
Expand All @@ -34,14 +41,6 @@ export class ContinuousParameterColorScaleHelper {
return this._colorScale;
}

getMinMax(): MinMax {
return this._minMax;
}

getParameterIdent(): ParameterIdent {
return this._parameterIdent;
}

hasEnsembleName(ensembleName: string): boolean {
return ensembleName in this._ensembleContinuousParameterSet;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { VectorHistoricalData_api, VectorRealizationData_api, VectorStatisticData_api } from "@api";
import { ColorScale } from "@lib/utils/ColorScale";
import { ColorSet } from "@lib/utils/ColorSet";

import { PlotMarker } from "plotly.js";
Expand All @@ -14,7 +13,7 @@ import {
createVectorStatisticsTraces,
} from "./PlotlyTraceUtils/createVectorTracesUtils";
import { scaleHexColorLightness } from "./colorUtils";
import { ContinuousParameterColorScaleHelper } from "./parameterColoringUtils";
import { EnsemblesContinuousParameterColoring } from "./ensemblesContinuousParameterColoring";
import { TimeSeriesPlotData } from "./timeSeriesPlotData";

import { VectorSpec } from "../state";
Expand Down Expand Up @@ -58,9 +57,9 @@ export class SubplotBuilder {
private _width = 0;
private _height = 0;

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

private _parameterColorScaleHelper: ContinuousParameterColorScaleHelper | null = null;
private _ensemblesParameterColoring: EnsemblesContinuousParameterColoring | null = null;
private _parameterFallbackColor = "#808080";

constructor(
Expand All @@ -69,7 +68,8 @@ export class SubplotBuilder {
colorSet: ColorSet,
width: number,
height: number,
parameterColorScaleHelper?: ContinuousParameterColorScaleHelper
ensemblesParameterColoring?: EnsemblesContinuousParameterColoring,
scatterType: "scatter" | "scattergl" = "scatter"
) {
this._selectedVectorSpecifications = selectedVectorSpecifications;
this._width = width;
Expand All @@ -96,7 +96,8 @@ export class SubplotBuilder {
? this._uniqueVectorNames.length
: this._uniqueEnsembleNames.length;

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

// TODO:
// - Handle keep uirevision?
Expand Down Expand Up @@ -227,15 +228,15 @@ export class SubplotBuilder {
this._plotData.push(observationLegendTrace);
}

// Add color scale for color by parameter
if (this._hasRealizationsTracesColoredByParameter && this._parameterColorScaleHelper !== null) {
// Add color scale for color by parameter below the legends
if (this._hasRealizationsTracesColoredByParameter && this._ensemblesParameterColoring !== null) {
const colorScaleMarker: Partial<PlotMarker> = {
...this._parameterColorScaleHelper.getColorScale().getAsPlotlyColorScaleMarkerObject(),
...this._ensemblesParameterColoring.getColorScale().getAsPlotlyColorScaleMarkerObject(),
colorbar: {
title: "Parameter range",
titleside: "right",
ticks: "outside",
len: 0.75,
len: 0.75, // Note: If too many legends are added, this len might have to be reduced?
},
};
const parameterColorLegendTrace: Partial<TimeSeriesPlotData> = {
Expand All @@ -251,9 +252,7 @@ export class SubplotBuilder {
addRealizationTracesColoredByParameter(
vectorsRealizationData: { vectorSpecification: VectorSpec; data: VectorRealizationData_api[] }[]
): void {
if (this._parameterColorScaleHelper === null) return;

this._defaultScatterType = "scattergl";
if (this._ensemblesParameterColoring === null) return;

// Only allow selected vectors
const selectedVectorsRealizationData = vectorsRealizationData.filter((vec) =>
Expand All @@ -278,16 +277,16 @@ export class SubplotBuilder {
let parameterColor = this._parameterFallbackColor;
const ensembleName = elm.vectorSpecification.ensembleIdent.getEnsembleName();
if (
this._parameterColorScaleHelper.hasParameterRealizationNumericalValue(
this._ensemblesParameterColoring.hasParameterRealizationNumericalValue(
ensembleName,
realizationData.realization
)
) {
const value = this._parameterColorScaleHelper.getParameterRealizationValue(
const value = this._ensemblesParameterColoring.getParameterRealizationValue(
ensembleName,
realizationData.realization
);
parameterColor = this._parameterColorScaleHelper.getColorScale().getColorForValue(value);
parameterColor = this._ensemblesParameterColoring.getColorScale().getColorForValue(value);
}

const vectorRealizationTrace = createVectorRealizationTrace({
Expand All @@ -298,6 +297,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 +309,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 +347,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 +379,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 +414,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 +444,7 @@ export class SubplotBuilder {
vectorHistoricalData: elm.data,
color: this._historyVectorColor,
yaxis: `y${subplotIndex + 1}`,
type: this._scatterType,
});
this._plotData.push(vectorHistoryTrace);
});
Expand Down
Loading

0 comments on commit 1333d17

Please sign in to comment.