From de7fba757a249fcbc6dc15e36a6bc66e3498c5aa Mon Sep 17 00:00:00 2001 From: Hans Kallekleiv <16436291+HansKallekleiv@users.noreply.github.com> Date: Tue, 12 Sep 2023 18:24:30 +0200 Subject: [PATCH 01/20] Show multiple sensitivities in TimeSeriesSensitivities module --- .../modules/Sensitivity/sensitivityChart.tsx | 29 ++- frontend/src/modules/Sensitivity/view.tsx | 50 ++++-- .../loadModule.tsx | 3 +- .../queryHooks.tsx | 31 +++- .../settings.tsx | 164 +++++++++-------- .../simulationTimeSeriesChart/chart.tsx | 2 + .../simulationTimeSeriesChart/traces.ts | 77 +++++++- .../SimulationTimeSeriesSensitivity/state.ts | 4 +- .../SimulationTimeSeriesSensitivity/view.tsx | 165 +++++++++--------- 9 files changed, 324 insertions(+), 201 deletions(-) diff --git a/frontend/src/modules/Sensitivity/sensitivityChart.tsx b/frontend/src/modules/Sensitivity/sensitivityChart.tsx index 47d733a7a..a3f2eb5f1 100644 --- a/frontend/src/modules/Sensitivity/sensitivityChart.tsx +++ b/frontend/src/modules/Sensitivity/sensitivityChart.tsx @@ -6,8 +6,13 @@ import { Layout, PlotData, PlotMouseEvent } from "plotly.js"; import { SensitivityResponse, SensitivityResponseDataset } from "./sensitivityResponseCalculator"; import { SelectedSensitivity } from "./state"; +export type SensitivityColors = { + sensitivityName: string; + color: string; +}; export type sensitivityChartProps = { sensitivityResponseDataset: SensitivityResponseDataset; + sensitivityColors: SensitivityColors[]; showLabels: boolean; hideZeroY: boolean; showRealizationPoints: boolean; @@ -102,16 +107,8 @@ const lowTrace = ( sensitivityResponses: SensitivityResponse[], showLabels: boolean, selectedBar: SelectedBar | null, - barColor = "e53935" + colors: string[] ): Partial => { - // const label = showLabels - // ? sensitivityResponses.map( - // (s) => - // `${numFormat(s.lowCaseReferenceDifference)} ${numFormat(s.lowCaseAverage)}
Case: ${ - // s.lowCaseName - // }` - // ) - // : []; const label = showLabels ? sensitivityResponses.map((s) => computeLowLabel(s)) : []; return { @@ -128,7 +125,7 @@ const lowTrace = ( name: TraceGroup.LOW, orientation: "h", marker: { - color: barColor, + color: colors, line: { width: 3, color: sensitivityResponses.map((s, idx) => @@ -146,7 +143,7 @@ const highTrace = ( sensitivityResponses: SensitivityResponse[], showLabels: boolean, selectedBar: SelectedBar | null, - barColor = "#00897b" + colors: string[] ): Partial => { // const label = showLabels // ? sensitivityResponses.map( @@ -172,7 +169,7 @@ const highTrace = ( name: TraceGroup.HIGH, orientation: "h", marker: { - color: barColor, + color: colors, line: { width: 3, color: sensitivityResponses.map((s, idx) => @@ -201,9 +198,11 @@ const sensitivityChart: React.FC = (props) => { (s) => s.lowCaseReferenceDifference !== 0 || s.highCaseReferenceDifference !== 0 ); } - - traces.push(lowTrace(filteredSensitivityResponses, showLabels, selectedBar)); - traces.push(highTrace(filteredSensitivityResponses, showLabels, selectedBar)); + const colors: string[] = filteredSensitivityResponses.map( + (s) => props.sensitivityColors.find((sc) => sc.sensitivityName === s.sensitivityName)?.color ?? "black" + ); + traces.push(lowTrace(filteredSensitivityResponses, showLabels, selectedBar, colors)); + traces.push(highTrace(filteredSensitivityResponses, showLabels, selectedBar, colors)); // if (showRealizationPoints) { // TODO: Add realization points diff --git a/frontend/src/modules/Sensitivity/view.tsx b/frontend/src/modules/Sensitivity/view.tsx index 1dd06118c..74ae61f27 100644 --- a/frontend/src/modules/Sensitivity/view.tsx +++ b/frontend/src/modules/Sensitivity/view.tsx @@ -7,12 +7,21 @@ import { useEnsembleSet } from "@framework/WorkbenchSession"; import { AdjustmentsHorizontalIcon, ChartBarIcon, TableCellsIcon } from "@heroicons/react/20/solid"; import { useElementSize } from "@lib/hooks/useElementSize"; -import SensitivityChart from "./sensitivityChart"; -import { EnsembleScalarResponse, SensitivityResponseCalculator } from "./sensitivityResponseCalculator"; +import SensitivityChart, { SensitivityColors } from "./sensitivityChart"; +import { + EnsembleScalarResponse, + SensitivityResponseCalculator, + SensitivityResponseDataset, +} from "./sensitivityResponseCalculator"; import SensitivityTable from "./sensitivityTable"; import { PlotType, State } from "./state"; -export const view = ({ moduleContext, workbenchSession, workbenchServices }: ModuleFCProps) => { +export const view = ({ + moduleContext, + workbenchSession, + workbenchSettings, + workbenchServices, +}: ModuleFCProps) => { // Leave this in until we get a feeling for React18/Plotly const renderCount = React.useRef(0); React.useEffect(function incrementRenderCount() { @@ -78,24 +87,26 @@ export const view = ({ moduleContext, workbenchSession, workbenchServices }: Mod return unsubscribeFunc; }, [responseChannel, ensembleSet]); - // Memoize the computation of sensitivity responses. Should we use useMemo? const sensitivities = channelEnsemble?.getSensitivities(); - const computedSensitivityResponseDataset = React.useMemo(() => { - if (sensitivities && channelResponseData) { - // How to handle errors? - try { - const sensitivityResponseCalculator = new SensitivityResponseCalculator( - sensitivities, - channelResponseData - ); - return sensitivityResponseCalculator.computeSensitivitiesForResponse(); - } catch (e) { - console.warn(e); - return null; - } + const colorSet = workbenchSettings.useColorSet(); + const sensitivityColors: SensitivityColors[] = + sensitivities + ?.getSensitivityNames() + .map((sensitivityName, index) => + index === 0 + ? { sensitivityName, color: colorSet.getFirstColor() } + : { sensitivityName, color: colorSet.getNextColor() } + ) ?? []; + let computedSensitivityResponseDataset: SensitivityResponseDataset | null = null; + if (sensitivities && channelResponseData) { + // How to handle errors? + try { + const sensitivityResponseCalculator = new SensitivityResponseCalculator(sensitivities, channelResponseData); + computedSensitivityResponseDataset = sensitivityResponseCalculator.computeSensitivitiesForResponse(); + } catch (e) { + console.warn(e); } - return null; - }, [sensitivities, channelResponseData]); + } let errMessage = ""; if (!computedSensitivityResponseDataset) { @@ -176,6 +187,7 @@ export const view = ({ moduleContext, workbenchSession, workbenchServices }: Mod {computedSensitivityResponseDataset && plotType === PlotType.TORNADO && ( ("SimulationTimeSeriesSensitivity", defaultState); diff --git a/frontend/src/modules/SimulationTimeSeriesSensitivity/queryHooks.tsx b/frontend/src/modules/SimulationTimeSeriesSensitivity/queryHooks.tsx index c15a151f3..2b57f476e 100644 --- a/frontend/src/modules/SimulationTimeSeriesSensitivity/queryHooks.tsx +++ b/frontend/src/modules/SimulationTimeSeriesSensitivity/queryHooks.tsx @@ -1,4 +1,9 @@ -import { Frequency_api, VectorDescription_api, VectorStatisticSensitivityData_api } from "@api"; +import { + Frequency_api, + VectorDescription_api, + VectorHistoricalData_api, + VectorStatisticSensitivityData_api, +} from "@api"; import { VectorRealizationData_api } from "@api"; import { apiService } from "@framework/ApiService"; import { UseQueryResult, useQuery } from "@tanstack/react-query"; @@ -6,7 +11,7 @@ import { UseQueryResult, useQuery } from "@tanstack/react-query"; const STALE_TIME = 60 * 1000; const CACHE_TIME = 60 * 1000; -export function useVectorsQuery( +export function useVectorListQuery( caseUuid: string | undefined, ensembleName: string | undefined ): UseQueryResult> { @@ -72,3 +77,25 @@ export function useStatisticalVectorSensitivityDataQuery( enabled: allowEnable && caseUuid && ensembleName && vectorName && resampleFrequency ? true : false, }); } + +export function useHistoricalVectorDataQuery( + caseUuid: string | undefined, + ensembleName: string | undefined, + vectorName: string | undefined, + resampleFrequency: Frequency_api | null, + allowEnable: boolean +): UseQueryResult { + return useQuery({ + queryKey: ["getHistoricalVectorData", caseUuid, ensembleName, vectorName, resampleFrequency], + queryFn: () => + apiService.timeseries.getHistoricalVectorData( + caseUuid ?? "", + ensembleName ?? "", + vectorName ?? "", + resampleFrequency ?? Frequency_api.MONTHLY + ), + staleTime: STALE_TIME, + cacheTime: CACHE_TIME, + enabled: allowEnable && caseUuid && ensembleName && vectorName && resampleFrequency ? true : false, + }); +} diff --git a/frontend/src/modules/SimulationTimeSeriesSensitivity/settings.tsx b/frontend/src/modules/SimulationTimeSeriesSensitivity/settings.tsx index 729073884..6271905c2 100644 --- a/frontend/src/modules/SimulationTimeSeriesSensitivity/settings.tsx +++ b/frontend/src/modules/SimulationTimeSeriesSensitivity/settings.tsx @@ -10,11 +10,15 @@ import { fixupEnsembleIdent, maybeAssignFirstSyncedEnsemble } from "@framework/u import { ApiStateWrapper } from "@lib/components/ApiStateWrapper"; import { Checkbox } from "@lib/components/Checkbox"; import { CircularProgress } from "@lib/components/CircularProgress"; +import { CollapsibleGroup } from "@lib/components/CollapsibleGroup"; import { Dropdown, DropdownOption } from "@lib/components/Dropdown"; import { Label } from "@lib/components/Label"; import { Select, SelectOption } from "@lib/components/Select"; +import { SmartNodeSelectorSelection, TreeDataNode } from "@lib/components/SmartNodeSelector"; +import { VectorSelector } from "@lib/components/VectorSelector"; +import { createVectorSelectorDataFromVectors } from "@lib/utils/vectorSelectorUtils"; -import { useVectorsQuery } from "./queryHooks"; +import { useVectorListQuery } from "./queryHooks"; import { State } from "./state"; //----------------------------------------------------------------------------------------------------------- @@ -25,12 +29,12 @@ export function settings({ moduleContext, workbenchSession, workbenchServices }: const ensembleSet = useEnsembleSet(workbenchSession); const [selectedEnsembleIdent, setSelectedEnsembleIdent] = React.useState(null); - const [selectedVectorName, setSelectedVectorName] = React.useState(""); - const [selectedSensitivity, setSelectedSensitivity] = moduleContext.useStoreState("selectedSensitivity"); + const [selectedVectorName, setSelectedVectorName] = React.useState(null); + const [selectedSensitivities, setSelectedSensitivities] = moduleContext.useStoreState("selectedSensitivities"); const [resampleFrequency, setResamplingFrequency] = moduleContext.useStoreState("resamplingFrequency"); const [showStatistics, setShowStatistics] = moduleContext.useStoreState("showStatistics"); const [showRealizations, setShowRealizations] = moduleContext.useStoreState("showRealizations"); - + const [showHistorical, setShowHistorical] = moduleContext.useStoreState("showHistorical"); const syncedSettingKeys = moduleContext.useSyncedSettingKeys(); const syncHelper = new SyncSettingsHelper(syncedSettingKeys, workbenchServices); const syncedValueEnsembles = syncHelper.useValue(SyncSettingKey.ENSEMBLE, "global.syncValue.ensembles"); @@ -39,35 +43,46 @@ export function settings({ moduleContext, workbenchSession, workbenchServices }: const candidateEnsembleIdent = maybeAssignFirstSyncedEnsemble(selectedEnsembleIdent, syncedValueEnsembles); const computedEnsembleIdent = fixupEnsembleIdent(candidateEnsembleIdent, ensembleSet); - const vectorsQuery = useVectorsQuery( + const vectorsListQuery = useVectorListQuery( computedEnsembleIdent?.getCaseUuid(), computedEnsembleIdent?.getEnsembleName() ); + const vectorNames = vectorsListQuery.data?.map((vec) => vec.name) ?? []; + const vectorSelectorData: TreeDataNode[] = createVectorSelectorDataFromVectors(vectorNames); + let candidateVectorName = selectedVectorName; if (syncedValueSummaryVector?.vectorName) { console.debug(`${myInstanceIdStr} -- syncing timeSeries to ${syncedValueSummaryVector.vectorName}`); candidateVectorName = syncedValueSummaryVector.vectorName; } - const computedVectorName = fixupVectorName(candidateVectorName, vectorsQuery.data); + const computedVectorName = fixupVectorName(candidateVectorName, vectorNames); - if (computedEnsembleIdent && !computedEnsembleIdent.equals(selectedEnsembleIdent)) { - setSelectedEnsembleIdent(computedEnsembleIdent); - } if (computedVectorName && computedVectorName !== selectedVectorName) { setSelectedVectorName(computedVectorName); } - const computedEnsemble = computedEnsembleIdent ? ensembleSet.findEnsemble(computedEnsembleIdent) : null; - const sensitivities = computedEnsemble?.getSensitivities(); + const hasHistorical = + vectorsListQuery.data?.some((vec) => vec.name === computedVectorName && vec.has_historical) ?? false; - React.useEffect(() => { - const sensitivityNames = computedEnsemble?.getSensitivities()?.getSensitivityNames(); - if (sensitivityNames && sensitivityNames.length > 0) { - if (!selectedSensitivity || !sensitivityNames.includes(selectedSensitivity)) { - setSelectedSensitivity(sensitivityNames[0]); + if (computedEnsembleIdent && !computedEnsembleIdent.equals(selectedEnsembleIdent)) { + setSelectedEnsembleIdent(computedEnsembleIdent); + } + + const computedEnsemble = computedEnsembleIdent ? ensembleSet.findEnsemble(computedEnsembleIdent) : null; + const sensitivityNames = computedEnsemble?.getSensitivities()?.getSensitivityNames() ?? []; + React.useEffect( + function setInitialSensitivities() { + if (!selectedSensitivities && sensitivityNames.length > 0) { + setSelectedSensitivities(sensitivityNames); } - } - }, [computedEnsemble]); + }, + [selectedEnsembleIdent] + ); + const sensitivityOptions: SelectOption[] = + sensitivityNames.map((name) => ({ + value: name, + label: name, + })) ?? []; React.useEffect( function propagateVectorSpecToView() { @@ -75,6 +90,7 @@ export function settings({ moduleContext, workbenchSession, workbenchServices }: moduleContext.getStateStore().setValue("vectorSpec", { ensembleIdent: computedEnsembleIdent, vectorName: computedVectorName, + hasHistorical, }); } else { moduleContext.getStateStore().setValue("vectorSpec", null); @@ -91,15 +107,6 @@ export function settings({ moduleContext, workbenchSession, workbenchServices }: } } - function handleVectorSelectionChange(selectedVecNames: string[]) { - console.debug("handleVectorSelectionChange()"); - const newName = selectedVecNames[0] ?? ""; - setSelectedVectorName(newName); - if (newName) { - syncHelper.publishValue(SyncSettingKey.TIME_SERIES, "global.syncValue.timeSeries", { vectorName: newName }); - } - } - function handleFrequencySelectionChange(newFreqStr: string) { console.debug(`handleFrequencySelectionChange() newFreqStr=${newFreqStr}`); let newFreq: Frequency_api | null = null; @@ -109,68 +116,73 @@ export function settings({ moduleContext, workbenchSession, workbenchServices }: console.debug(`handleFrequencySelectionChange() newFreqStr=${newFreqStr} newFreq=${newFreq}`); setResamplingFrequency(newFreq); } - if (!sensitivities?.getSensitivityArr()) { - return
This is not a sensitivity ensemble
; + function handleVectorSelectChange(selection: SmartNodeSelectorSelection) { + setSelectedVectorName(selection.selectedTags[0]); + } + function handleShowHistorical(event: React.ChangeEvent) { + setShowHistorical(event.target.checked); } - return ( <> - + } + errorComponent={"Could not load the vectors for selected ensembles"} > - - - setShowStatistics(e.target.checked)} - /> - setShowRealizations(e.target.checked)} - /> + ); } @@ -178,16 +190,16 @@ export function settings({ moduleContext, workbenchSession, workbenchServices }: //----------------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------------- -function fixupVectorName(currVectorName: string, vectorDescriptionsArr: VectorDescription_api[] | undefined): string { - if (!vectorDescriptionsArr || vectorDescriptionsArr.length === 0) { +function fixupVectorName(currVectorName: string | null, availableVectorNames: string[] | undefined): string { + if (!availableVectorNames || availableVectorNames.length === 0) { return ""; } - if (vectorDescriptionsArr.find((item) => item.name === currVectorName)) { + if (availableVectorNames.find((name) => name === currVectorName) && currVectorName) { return currVectorName; } - return vectorDescriptionsArr[0].name; + return availableVectorNames[0]; } function makeVectorOptionItems(vectorDescriptionsArr: VectorDescription_api[] | undefined): SelectOption[] { diff --git a/frontend/src/modules/SimulationTimeSeriesSensitivity/simulationTimeSeriesChart/chart.tsx b/frontend/src/modules/SimulationTimeSeriesSensitivity/simulationTimeSeriesChart/chart.tsx index 072fb6d6d..9afc28097 100644 --- a/frontend/src/modules/SimulationTimeSeriesSensitivity/simulationTimeSeriesChart/chart.tsx +++ b/frontend/src/modules/SimulationTimeSeriesSensitivity/simulationTimeSeriesChart/chart.tsx @@ -15,6 +15,7 @@ export type TimeSeriesChartProps = { traceDataArr: TimeSeriesPlotlyTrace[]; activeTimestampUtcMs?: number; hoveredTimestampUtcMs?: number; + uirevision?: string; onHover?: (hoverData: HoverInfo | null) => void; onClick?: (timestampUtcMs: number) => void; height?: number | 100; @@ -74,6 +75,7 @@ export const TimeSeriesChart: React.FC = (props) => { legend: { orientation: "h", yanchor: "bottom", y: 1.02, xanchor: "right", x: 1 }, margin: { t: 0, b: 100, r: 0 }, shapes: [], + uirevision: props.uirevision, }; if (props.activeTimestampUtcMs !== undefined) { diff --git a/frontend/src/modules/SimulationTimeSeriesSensitivity/simulationTimeSeriesChart/traces.ts b/frontend/src/modules/SimulationTimeSeriesSensitivity/simulationTimeSeriesChart/traces.ts index 79485833d..90229ebb1 100644 --- a/frontend/src/modules/SimulationTimeSeriesSensitivity/simulationTimeSeriesChart/traces.ts +++ b/frontend/src/modules/SimulationTimeSeriesSensitivity/simulationTimeSeriesChart/traces.ts @@ -1,5 +1,8 @@ -import { VectorRealizationData_api } from "@api"; +import { VectorRealizationData_api, VectorStatisticSensitivityData_api } from "@api"; +import { StatisticFunction_api } from "@api"; +import { Sensitivity } from "@framework/EnsembleSensitivities"; +import { StringIterator } from "lodash"; import { PlotData } from "plotly.js"; export interface TimeSeriesPlotlyTrace extends Partial { @@ -7,18 +10,59 @@ export interface TimeSeriesPlotlyTrace extends Partial { legendrank?: number; } +export function createStatisticalLineTraces( + sensitivityData: VectorStatisticSensitivityData_api[], + color: string +): TimeSeriesPlotlyTrace[] { + const traces: TimeSeriesPlotlyTrace[] = []; + sensitivityData.forEach((aCase, index) => { + const meanObj = aCase.value_objects.find((obj) => obj.statistic_function === StatisticFunction_api.MEAN); + if (meanObj) { + traces.push( + createLineTrace({ + timestampsMsUtc: aCase.timestamps_utc_ms, + values: meanObj.values, + name: `${aCase.sensitivity_name}`, + legendGroup: `${aCase.sensitivity_name}`, + lineShape: "linear", + lineDash: "dash", + showLegend: index === 0, + lineColor: color, + lineWidth: 3, + hoverTemplate: `Sensitivity:${aCase.sensitivity_name}
Case: ${aCase.sensitivity_case}
Value: %{y}
Date: %{x}`, + }) + ); + } + }); + return traces; +} + export function createRealizationLineTraces( realizationData: VectorRealizationData_api[], + sensitivity: Sensitivity, + color: string, highlightedRealization?: number | undefined ): TimeSeriesPlotlyTrace[] { const traces: TimeSeriesPlotlyTrace[] = []; let highlightedTrace: TimeSeriesPlotlyTrace | null = null; realizationData.forEach((vec) => { - const curveColor = highlightedRealization === vec.realization ? "red" : "grey"; + const curveColor = highlightedRealization === vec.realization ? "red" : color; const lineWidth = highlightedRealization === vec.realization ? 2 : 1; const lineShape = highlightedRealization === vec.realization ? "spline" : "linear"; const isHighlighted = vec.realization === highlightedRealization ? true : false; - const trace = createSingleRealizationLineTrace(vec, curveColor, lineWidth, lineShape); + + const trace = createLineTrace({ + timestampsMsUtc: vec.timestamps_utc_ms, + values: vec.values, + name: `real-${vec.realization}`, + lineShape: lineShape, + lineDash: "solid", + showLegend: false, + lineColor: curveColor, + lineWidth: lineWidth, + hoverTemplate: `Sensitivity:${sensitivity.name}
Value: %{y}
Date: %{x}`, + }); + if (isHighlighted) { highlightedTrace = trace; } else { @@ -30,6 +74,31 @@ export function createRealizationLineTraces( } return traces; } +export type LineTraceData = { + timestampsMsUtc: number[]; + values: number[]; + name: string; + hoverTemplate?: string; + legendGroup?: string; + lineShape: "linear" | "spline"; + lineDash: "dash" | "dot" | "dashdot" | "solid"; + showLegend: boolean; + lineColor: string; + lineWidth: number; +}; +export function createLineTrace(data: LineTraceData): TimeSeriesPlotlyTrace { + return { + x: data.timestampsMsUtc, + y: data.values, + name: data.name, + showlegend: data.showLegend, + hovertemplate: data.hoverTemplate, + legendgroup: data.legendGroup, + type: "scatter", + mode: "lines", + line: { color: data.lineColor, width: data.lineWidth, dash: data.lineDash, shape: data.lineShape }, + }; +} function createSingleRealizationLineTrace( vec: VectorRealizationData_api, @@ -62,7 +131,7 @@ export function createSensitivityStatisticsTrace( x: timestampsMsUtc, y: values, name: name, - legendrank: -1, + showlegend: false, type: "scatter", mode: "lines", line: { color: lineColor || "lightblue", width: 4, dash: lineDash, shape: lineShape }, diff --git a/frontend/src/modules/SimulationTimeSeriesSensitivity/state.ts b/frontend/src/modules/SimulationTimeSeriesSensitivity/state.ts index 8a97034c9..79256418a 100644 --- a/frontend/src/modules/SimulationTimeSeriesSensitivity/state.ts +++ b/frontend/src/modules/SimulationTimeSeriesSensitivity/state.ts @@ -4,13 +4,15 @@ import { EnsembleIdent } from "@framework/EnsembleIdent"; export interface VectorSpec { ensembleIdent: EnsembleIdent; vectorName: string; + hasHistorical: boolean; } export interface State { vectorSpec: VectorSpec | null; resamplingFrequency: Frequency_api | null; - selectedSensitivity: string | null; + selectedSensitivities: string[] | null; showStatistics: boolean; showRealizations: boolean; realizationsToInclude: number[] | null; + showHistorical: boolean; } diff --git a/frontend/src/modules/SimulationTimeSeriesSensitivity/view.tsx b/frontend/src/modules/SimulationTimeSeriesSensitivity/view.tsx index a4e965a38..3f2a46d1f 100644 --- a/frontend/src/modules/SimulationTimeSeriesSensitivity/view.tsx +++ b/frontend/src/modules/SimulationTimeSeriesSensitivity/view.tsx @@ -1,7 +1,12 @@ import React from "react"; -import { StatisticFunction_api, VectorRealizationData_api, VectorStatisticSensitivityData_api } from "@api"; -import { BroadcastChannelMeta, BroadcastChannelData } from "@framework/Broadcaster"; +import { + StatisticFunction_api, + VectorHistoricalData_api, + VectorRealizationData_api, + VectorStatisticSensitivityData_api, +} from "@api"; +import { BroadcastChannelData, BroadcastChannelMeta } from "@framework/Broadcaster"; import { Ensemble } from "@framework/Ensemble"; import { ModuleFCProps } from "@framework/Module"; import { useSubscribedValue } from "@framework/WorkbenchServices"; @@ -11,13 +16,27 @@ import { useElementSize } from "@lib/hooks/useElementSize"; import { indexOf } from "lodash"; import { BroadcastChannelNames } from "./channelDefs"; -import { useStatisticalVectorSensitivityDataQuery, useVectorDataQuery } from "./queryHooks"; +import { + useHistoricalVectorDataQuery, + useStatisticalVectorSensitivityDataQuery, + useVectorDataQuery, +} from "./queryHooks"; import { HoverInfo, TimeSeriesChart } from "./simulationTimeSeriesChart/chart"; -import { TimeSeriesPlotlyTrace } from "./simulationTimeSeriesChart/traces"; -import { createRealizationLineTraces, createSensitivityStatisticsTrace } from "./simulationTimeSeriesChart/traces"; +import { TimeSeriesPlotlyTrace, createStatisticalLineTraces } from "./simulationTimeSeriesChart/traces"; +import { + LineTraceData, + createLineTrace, + createRealizationLineTraces, + createSensitivityStatisticsTrace, +} from "./simulationTimeSeriesChart/traces"; import { State } from "./state"; -export const view = ({ moduleContext, workbenchSession, workbenchServices }: ModuleFCProps) => { +export const view = ({ + moduleContext, + workbenchSession, + workbenchSettings, + workbenchServices, +}: ModuleFCProps) => { // Leave this in until we get a feeling for React18/Plotly const renderCount = React.useRef(0); React.useEffect(function incrementRenderCount() { @@ -30,7 +49,7 @@ export const view = ({ moduleContext, workbenchSession, workbenchServices }: Mod const resampleFrequency = moduleContext.useStoreValue("resamplingFrequency"); const showStatistics = moduleContext.useStoreValue("showStatistics"); const showRealizations = moduleContext.useStoreValue("showRealizations"); - const selectedSensitivity = moduleContext.useStoreValue("selectedSensitivity"); + const selectedSensitivities = moduleContext.useStoreValue("selectedSensitivities"); const [activeTimestampUtcMs, setActiveTimestampUtcMs] = React.useState(null); const subscribedHoverTimestampUtcMs = useSubscribedValue("global.hoverTimestamp", workbenchServices); @@ -50,6 +69,13 @@ export const view = ({ moduleContext, workbenchSession, workbenchServices }: Mod resampleFrequency, showStatistics ); + const historicalQuery = useHistoricalVectorDataQuery( + vectorSpec?.ensembleIdent.getCaseUuid(), + vectorSpec?.ensembleIdent.getEnsembleName(), + vectorSpec?.vectorName, + resampleFrequency, + vectorSpec?.hasHistorical || false + ); const ensembleSet = workbenchSession.getEnsembleSet(); const ensemble = vectorSpec ? ensembleSet.findEnsemble(vectorSpec.ensembleIdent) : null; @@ -82,20 +108,56 @@ export const view = ({ moduleContext, workbenchSession, workbenchServices }: Mod }, [ensemble, vectorSpec, realizationsQuery.data, activeTimestampUtcMs, moduleContext] ); + const colorSet = workbenchSettings.useColorSet(); - const traceDataArr = React.useMemo(() => { - if (!ensemble || !selectedSensitivity) { - return []; + const allSensitivityNamesInEnsemble = ensemble?.getSensitivities()?.getSensitivityNames().sort() ?? []; + const traceDataArr: TimeSeriesPlotlyTrace[] = []; + if (ensemble && selectedSensitivities && selectedSensitivities.length > 0) { + // Loop through all available sensitivities to get correct color + allSensitivityNamesInEnsemble.forEach((sensitivityName, index) => { + const color = index === 0 ? colorSet.getFirstColor() : colorSet.getNextColor(); + + // Work on selected sensitivities only + if (selectedSensitivities.includes(sensitivityName)) { + // Add statistics traces + if (showStatistics && statisticsQuery.data) { + const matchingCases: VectorStatisticSensitivityData_api[] = statisticsQuery.data.filter( + (stat) => stat.sensitivity_name === sensitivityName + ); + const traces = createStatisticalLineTraces(matchingCases, color); + traceDataArr.push(...traces); + } + + // Add realization traces + const sensitivity = ensemble.getSensitivities()?.getSensitivityByName(sensitivityName); + if (showRealizations && realizationsQuery.data && sensitivity) { + for (const sensCase of sensitivity.cases) { + const realsToInclude = sensCase.realizations; + const realizationData: VectorRealizationData_api[] = realizationsQuery.data.filter((vec) => + realsToInclude.includes(vec.realization) + ); + const traces = createRealizationLineTraces(realizationData, sensitivity, color); + traceDataArr.push(...traces); + } + } + } + }); + // Add history + if (historicalQuery?.data) { + traceDataArr.push( + createLineTrace({ + timestampsMsUtc: historicalQuery.data.timestamps_utc_ms, + values: historicalQuery.data.values, + name: "history", + lineShape: "linear", + lineDash: "solid", + showLegend: true, + lineColor: "black", + lineWidth: 2, + }) + ); } - return buildTraceDataArr( - ensemble, - selectedSensitivity, - showStatistics, - showRealizations, - statisticsQuery.data, - realizationsQuery.data - ); - }, [ensemble, selectedSensitivity, showStatistics, showRealizations, statisticsQuery.data, realizationsQuery.data]); + } function handleHoverInChart(hoverInfo: HoverInfo | null) { if (hoverInfo) { @@ -126,6 +188,7 @@ export const view = ({ moduleContext, workbenchSession, workbenchServices }: Mod
); }; - -function buildTraceDataArr( - ensemble: Ensemble, - sensitivityName: string, - showStatistics: boolean, - showRealizations: boolean, - perSensitivityStatisticData?: VectorStatisticSensitivityData_api[], - perRealizationData?: VectorRealizationData_api[] -): TimeSeriesPlotlyTrace[] { - const sensitivity = ensemble.getSensitivities()?.getSensitivityByName(sensitivityName); - if (!sensitivity) { - return []; - } - - const traceDataArr: TimeSeriesPlotlyTrace[] = []; - - if (perSensitivityStatisticData) { - const refCase = perSensitivityStatisticData.find((stat) => stat.sensitivity_name === "rms_seed"); - const meanObj = refCase?.value_objects.find((obj) => obj.statistic_function === StatisticFunction_api.MEAN); - if (refCase && meanObj) { - traceDataArr.push( - createSensitivityStatisticsTrace( - refCase.timestamps_utc_ms, - meanObj.values, - `reference ${refCase.sensitivity_name}`, - "linear", - "solid", - "black" - ) - ); - } - } - - if (showStatistics && perSensitivityStatisticData) { - const matchingCases = perSensitivityStatisticData.filter((stat) => stat.sensitivity_name === sensitivityName); - for (const aCase of matchingCases) { - const meanObj = aCase.value_objects.find((obj) => obj.statistic_function === StatisticFunction_api.MEAN); - if (meanObj) { - traceDataArr.push( - createSensitivityStatisticsTrace( - aCase.timestamps_utc_ms, - meanObj.values, - aCase.sensitivity_case, - "linear", - "dash" - ) - ); - } - } - } - - if (showRealizations && perRealizationData) { - for (const sensCase of sensitivity.cases) { - const realsToInclude = sensCase.realizations; - const realizationData: VectorRealizationData_api[] = perRealizationData.filter((vec) => - realsToInclude.includes(vec.realization) - ); - const traces = createRealizationLineTraces(realizationData); - traceDataArr.push(...traces); - } - } - - return traceDataArr; -} From 5f29463363eaad6d1484fb3ae25f00c14acb2644 Mon Sep 17 00:00:00 2001 From: Hans Kallekleiv <16436291+HansKallekleiv@users.noreply.github.com> Date: Tue, 12 Sep 2023 22:02:23 +0200 Subject: [PATCH 02/20] Rename module and template --- .../registerModule.ts | 2 +- .../settings.tsx | 12 +----- .../simulationTimeSeriesChart/traces.ts | 39 ------------------- .../SimulationTimeSeriesSensitivity/view.tsx | 15 +------ .../loadModule.tsx | 2 +- .../{Sensitivity => TornadoChart}/preview.tsx | 0 .../queryHooks.tsx | 0 .../registerModule.tsx | 6 +-- .../sensitivityChart.tsx | 0 .../sensitivityResponseCalculator.ts | 0 .../sensitivityTable.tsx | 0 .../settings.tsx | 0 .../{Sensitivity => TornadoChart}/state.ts | 0 .../{Sensitivity => TornadoChart}/view.tsx | 0 frontend/src/modules/registerAllModules.ts | 2 +- .../{oyvindsplugin.ts => oatTimeSeries.ts} | 8 ++-- .../src/templates/registerAllTemplates.ts | 2 +- 17 files changed, 14 insertions(+), 74 deletions(-) rename frontend/src/modules/{Sensitivity => TornadoChart}/loadModule.tsx (81%) rename frontend/src/modules/{Sensitivity => TornadoChart}/preview.tsx (100%) rename frontend/src/modules/{Sensitivity => TornadoChart}/queryHooks.tsx (100%) rename frontend/src/modules/{Sensitivity => TornadoChart}/registerModule.tsx (79%) rename frontend/src/modules/{Sensitivity => TornadoChart}/sensitivityChart.tsx (100%) rename frontend/src/modules/{Sensitivity => TornadoChart}/sensitivityResponseCalculator.ts (100%) rename frontend/src/modules/{Sensitivity => TornadoChart}/sensitivityTable.tsx (100%) rename frontend/src/modules/{Sensitivity => TornadoChart}/settings.tsx (100%) rename frontend/src/modules/{Sensitivity => TornadoChart}/state.ts (100%) rename frontend/src/modules/{Sensitivity => TornadoChart}/view.tsx (100%) rename frontend/src/templates/{oyvindsplugin.ts => oatTimeSeries.ts} (84%) diff --git a/frontend/src/modules/SimulationTimeSeriesSensitivity/registerModule.ts b/frontend/src/modules/SimulationTimeSeriesSensitivity/registerModule.ts index 49a1f23a9..2e81afcda 100644 --- a/frontend/src/modules/SimulationTimeSeriesSensitivity/registerModule.ts +++ b/frontend/src/modules/SimulationTimeSeriesSensitivity/registerModule.ts @@ -6,7 +6,7 @@ import { State } from "./state"; ModuleRegistry.registerModule({ moduleName: "SimulationTimeSeriesSensitivity", - defaultTitle: "Simulation time series sensitivity", + defaultTitle: "Simulation time series per sensitivity", syncableSettingKeys: [SyncSettingKey.ENSEMBLE, SyncSettingKey.TIME_SERIES], broadcastChannelsDef, }); diff --git a/frontend/src/modules/SimulationTimeSeriesSensitivity/settings.tsx b/frontend/src/modules/SimulationTimeSeriesSensitivity/settings.tsx index 6271905c2..9ffe28811 100644 --- a/frontend/src/modules/SimulationTimeSeriesSensitivity/settings.tsx +++ b/frontend/src/modules/SimulationTimeSeriesSensitivity/settings.tsx @@ -1,6 +1,6 @@ import React from "react"; -import { Frequency_api, VectorDescription_api } from "@api"; +import { Frequency_api } from "@api"; import { EnsembleIdent } from "@framework/EnsembleIdent"; import { ModuleFCProps } from "@framework/Module"; import { SyncSettingKey, SyncSettingsHelper } from "@framework/SyncSettings"; @@ -202,16 +202,6 @@ function fixupVectorName(currVectorName: string | null, availableVectorNames: st return availableVectorNames[0]; } -function makeVectorOptionItems(vectorDescriptionsArr: VectorDescription_api[] | undefined): SelectOption[] { - const itemArr: SelectOption[] = []; - if (vectorDescriptionsArr) { - for (const vec of vectorDescriptionsArr) { - itemArr.push({ value: vec.name, label: vec.descriptive_name }); - } - } - return itemArr; -} - function makeFrequencyOptionItems(): DropdownOption[] { const itemArr: DropdownOption[] = [ { value: Frequency_api.DAILY, label: "Daily" }, diff --git a/frontend/src/modules/SimulationTimeSeriesSensitivity/simulationTimeSeriesChart/traces.ts b/frontend/src/modules/SimulationTimeSeriesSensitivity/simulationTimeSeriesChart/traces.ts index 90229ebb1..9e0f74a5a 100644 --- a/frontend/src/modules/SimulationTimeSeriesSensitivity/simulationTimeSeriesChart/traces.ts +++ b/frontend/src/modules/SimulationTimeSeriesSensitivity/simulationTimeSeriesChart/traces.ts @@ -2,7 +2,6 @@ import { VectorRealizationData_api, VectorStatisticSensitivityData_api } from "@ import { StatisticFunction_api } from "@api"; import { Sensitivity } from "@framework/EnsembleSensitivities"; -import { StringIterator } from "lodash"; import { PlotData } from "plotly.js"; export interface TimeSeriesPlotlyTrace extends Partial { @@ -99,41 +98,3 @@ export function createLineTrace(data: LineTraceData): TimeSeriesPlotlyTrace { line: { color: data.lineColor, width: data.lineWidth, dash: data.lineDash, shape: data.lineShape }, }; } - -function createSingleRealizationLineTrace( - vec: VectorRealizationData_api, - curveColor: string, - lineWidth: number, - lineShape: "linear" | "spline" -): TimeSeriesPlotlyTrace { - return { - x: vec.timestamps_utc_ms, - y: vec.values, - name: `real-${vec.realization}`, - realizationNumber: vec.realization, - // legendrank: vec.realization, - showlegend: false, - type: "scatter", - mode: "lines", - line: { color: curveColor, width: lineWidth, shape: lineShape }, - }; -} - -export function createSensitivityStatisticsTrace( - timestampsMsUtc: number[], - values: number[], - name: string, - lineShape: "linear" | "spline", - lineDash: "dash" | "dot" | "dashdot" | "solid", - lineColor?: string | null -): TimeSeriesPlotlyTrace { - return { - x: timestampsMsUtc, - y: values, - name: name, - showlegend: false, - type: "scatter", - mode: "lines", - line: { color: lineColor || "lightblue", width: 4, dash: lineDash, shape: lineShape }, - }; -} diff --git a/frontend/src/modules/SimulationTimeSeriesSensitivity/view.tsx b/frontend/src/modules/SimulationTimeSeriesSensitivity/view.tsx index 3f2a46d1f..c039c7323 100644 --- a/frontend/src/modules/SimulationTimeSeriesSensitivity/view.tsx +++ b/frontend/src/modules/SimulationTimeSeriesSensitivity/view.tsx @@ -1,13 +1,7 @@ import React from "react"; -import { - StatisticFunction_api, - VectorHistoricalData_api, - VectorRealizationData_api, - VectorStatisticSensitivityData_api, -} from "@api"; +import { VectorRealizationData_api, VectorStatisticSensitivityData_api } from "@api"; import { BroadcastChannelData, BroadcastChannelMeta } from "@framework/Broadcaster"; -import { Ensemble } from "@framework/Ensemble"; import { ModuleFCProps } from "@framework/Module"; import { useSubscribedValue } from "@framework/WorkbenchServices"; import { timestampUtcMsToCompactIsoString } from "@framework/utils/timestampUtils"; @@ -23,12 +17,7 @@ import { } from "./queryHooks"; import { HoverInfo, TimeSeriesChart } from "./simulationTimeSeriesChart/chart"; import { TimeSeriesPlotlyTrace, createStatisticalLineTraces } from "./simulationTimeSeriesChart/traces"; -import { - LineTraceData, - createLineTrace, - createRealizationLineTraces, - createSensitivityStatisticsTrace, -} from "./simulationTimeSeriesChart/traces"; +import { createLineTrace, createRealizationLineTraces } from "./simulationTimeSeriesChart/traces"; import { State } from "./state"; export const view = ({ diff --git a/frontend/src/modules/Sensitivity/loadModule.tsx b/frontend/src/modules/TornadoChart/loadModule.tsx similarity index 81% rename from frontend/src/modules/Sensitivity/loadModule.tsx rename to frontend/src/modules/TornadoChart/loadModule.tsx index bbd363c28..3b73df84a 100644 --- a/frontend/src/modules/Sensitivity/loadModule.tsx +++ b/frontend/src/modules/TornadoChart/loadModule.tsx @@ -10,7 +10,7 @@ const defaultState: State = { responseChannelName: null, }; -const module = ModuleRegistry.initModule("Sensitivity", defaultState); +const module = ModuleRegistry.initModule("TornadoChart", defaultState); module.viewFC = view; module.settingsFC = settings; diff --git a/frontend/src/modules/Sensitivity/preview.tsx b/frontend/src/modules/TornadoChart/preview.tsx similarity index 100% rename from frontend/src/modules/Sensitivity/preview.tsx rename to frontend/src/modules/TornadoChart/preview.tsx diff --git a/frontend/src/modules/Sensitivity/queryHooks.tsx b/frontend/src/modules/TornadoChart/queryHooks.tsx similarity index 100% rename from frontend/src/modules/Sensitivity/queryHooks.tsx rename to frontend/src/modules/TornadoChart/queryHooks.tsx diff --git a/frontend/src/modules/Sensitivity/registerModule.tsx b/frontend/src/modules/TornadoChart/registerModule.tsx similarity index 79% rename from frontend/src/modules/Sensitivity/registerModule.tsx rename to frontend/src/modules/TornadoChart/registerModule.tsx index bc5fb4795..477d7a1c6 100644 --- a/frontend/src/modules/Sensitivity/registerModule.tsx +++ b/frontend/src/modules/TornadoChart/registerModule.tsx @@ -5,8 +5,8 @@ import { preview } from "./preview"; import { State } from "./state"; ModuleRegistry.registerModule({ - moduleName: "Sensitivity", - defaultTitle: "Sensitivity", + moduleName: "TornadoChart", + defaultTitle: "Tornado Chart", syncableSettingKeys: [SyncSettingKey.ENSEMBLE, SyncSettingKey.TIME_SERIES], - preview + preview, }); diff --git a/frontend/src/modules/Sensitivity/sensitivityChart.tsx b/frontend/src/modules/TornadoChart/sensitivityChart.tsx similarity index 100% rename from frontend/src/modules/Sensitivity/sensitivityChart.tsx rename to frontend/src/modules/TornadoChart/sensitivityChart.tsx diff --git a/frontend/src/modules/Sensitivity/sensitivityResponseCalculator.ts b/frontend/src/modules/TornadoChart/sensitivityResponseCalculator.ts similarity index 100% rename from frontend/src/modules/Sensitivity/sensitivityResponseCalculator.ts rename to frontend/src/modules/TornadoChart/sensitivityResponseCalculator.ts diff --git a/frontend/src/modules/Sensitivity/sensitivityTable.tsx b/frontend/src/modules/TornadoChart/sensitivityTable.tsx similarity index 100% rename from frontend/src/modules/Sensitivity/sensitivityTable.tsx rename to frontend/src/modules/TornadoChart/sensitivityTable.tsx diff --git a/frontend/src/modules/Sensitivity/settings.tsx b/frontend/src/modules/TornadoChart/settings.tsx similarity index 100% rename from frontend/src/modules/Sensitivity/settings.tsx rename to frontend/src/modules/TornadoChart/settings.tsx diff --git a/frontend/src/modules/Sensitivity/state.ts b/frontend/src/modules/TornadoChart/state.ts similarity index 100% rename from frontend/src/modules/Sensitivity/state.ts rename to frontend/src/modules/TornadoChart/state.ts diff --git a/frontend/src/modules/Sensitivity/view.tsx b/frontend/src/modules/TornadoChart/view.tsx similarity index 100% rename from frontend/src/modules/Sensitivity/view.tsx rename to frontend/src/modules/TornadoChart/view.tsx diff --git a/frontend/src/modules/registerAllModules.ts b/frontend/src/modules/registerAllModules.ts index 407540a7f..b1e7016a6 100644 --- a/frontend/src/modules/registerAllModules.ts +++ b/frontend/src/modules/registerAllModules.ts @@ -8,12 +8,12 @@ import "./Grid3DIntersection/registerModule"; import "./InplaceVolumetrics/registerModule"; import "./Map/registerModule"; import "./Pvt/registerModule"; -import "./Sensitivity/registerModule"; import "./SimulationTimeSeries/registerModule"; import "./SimulationTimeSeriesMatrix/registerModule"; import "./SimulationTimeSeriesSensitivity/registerModule"; import "./TimeSeriesParameterDistribution/registerModule"; import "./TopographicMap/registerModule"; +import "./TornadoChart/registerModule"; import "./WellCompletion/registerModule"; if (isDevMode()) { diff --git a/frontend/src/templates/oyvindsplugin.ts b/frontend/src/templates/oatTimeSeries.ts similarity index 84% rename from frontend/src/templates/oyvindsplugin.ts rename to frontend/src/templates/oatTimeSeries.ts index 2ea1d8ee5..353c498ac 100644 --- a/frontend/src/templates/oyvindsplugin.ts +++ b/frontend/src/templates/oatTimeSeries.ts @@ -6,7 +6,7 @@ import { BroadcastChannelNames } from "@modules/SimulationTimeSeries/channelDefs const template: Template = { description: - "Combination of simulation time series, sensitivity and distribution plot. All are synced to the same ensemble. Data is shared for realizations.", + "Dashboard for one-at-a-Time (OAT) sensitivity analysis of time series. Includes a time series chart, a tornado chart for the time series response per sensitivity for a given date, and a distribution chart.", moduleInstances: [ { instanceRef: "MainTimeSeriesSensitivityInstance", @@ -20,8 +20,8 @@ const template: Template = { syncedSettings: [SyncSettingKey.ENSEMBLE], }, { - instanceRef: "MySensitivityInstance", - moduleName: "Sensitivity", + instanceRef: "TorandoChartInstance", + moduleName: "TornadoChart", layout: { relHeight: 0.5, relWidth: 0.5, @@ -62,4 +62,4 @@ const template: Template = { ], }; -TemplateRegistry.registerTemplate("Øyvind's plugin", template); +TemplateRegistry.registerTemplate("Sensitivity Analysis of Time Series", template); diff --git a/frontend/src/templates/registerAllTemplates.ts b/frontend/src/templates/registerAllTemplates.ts index 8385ac6ef..03335c905 100644 --- a/frontend/src/templates/registerAllTemplates.ts +++ b/frontend/src/templates/registerAllTemplates.ts @@ -1 +1 @@ -import "./oyvindsplugin"; +import "./oatTimeSeries"; From 702d74fedab7eccb208ef9fe68d3ebcf555a04a8 Mon Sep 17 00:00:00 2001 From: Hans Kallekleiv <16436291+HansKallekleiv@users.noreply.github.com> Date: Wed, 13 Sep 2023 11:15:13 +0200 Subject: [PATCH 03/20] Show/hide historical --- frontend/src/modules/SimulationTimeSeriesSensitivity/view.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/modules/SimulationTimeSeriesSensitivity/view.tsx b/frontend/src/modules/SimulationTimeSeriesSensitivity/view.tsx index c039c7323..24316b57d 100644 --- a/frontend/src/modules/SimulationTimeSeriesSensitivity/view.tsx +++ b/frontend/src/modules/SimulationTimeSeriesSensitivity/view.tsx @@ -39,7 +39,7 @@ export const view = ({ const showStatistics = moduleContext.useStoreValue("showStatistics"); const showRealizations = moduleContext.useStoreValue("showRealizations"); const selectedSensitivities = moduleContext.useStoreValue("selectedSensitivities"); - + const showHistorical = moduleContext.useStoreValue("showHistorical"); const [activeTimestampUtcMs, setActiveTimestampUtcMs] = React.useState(null); const subscribedHoverTimestampUtcMs = useSubscribedValue("global.hoverTimestamp", workbenchServices); @@ -132,7 +132,7 @@ export const view = ({ } }); // Add history - if (historicalQuery?.data) { + if (historicalQuery?.data && showHistorical) { traceDataArr.push( createLineTrace({ timestampsMsUtc: historicalQuery.data.timestamps_utc_ms, From 789e92083e7c72c93d85340fd68d594255271b84 Mon Sep 17 00:00:00 2001 From: Hans Kallekleiv <16436291+HansKallekleiv@users.noreply.github.com> Date: Wed, 13 Sep 2023 11:15:23 +0200 Subject: [PATCH 04/20] Hide legend in tornado chart --- frontend/src/modules/TornadoChart/sensitivityChart.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/src/modules/TornadoChart/sensitivityChart.tsx b/frontend/src/modules/TornadoChart/sensitivityChart.tsx index a3f2eb5f1..cea599db0 100644 --- a/frontend/src/modules/TornadoChart/sensitivityChart.tsx +++ b/frontend/src/modules/TornadoChart/sensitivityChart.tsx @@ -123,6 +123,7 @@ const lowTrace = ( textposition: "auto", insidetextanchor: "middle", name: TraceGroup.LOW, + showlegend: false, orientation: "h", marker: { color: colors, @@ -167,6 +168,7 @@ const highTrace = ( insidetextanchor: "middle", type: "bar", name: TraceGroup.HIGH, + showlegend: false, orientation: "h", marker: { color: colors, From 6efd8a82e3df3ffe62ddd4ed038ebb7ffeb81f91 Mon Sep 17 00:00:00 2001 From: Hans Kallekleiv <16436291+HansKallekleiv@users.noreply.github.com> Date: Thu, 14 Sep 2023 12:40:48 +0200 Subject: [PATCH 05/20] Set last timeStamp as initially selected --- .../src/modules/SimulationTimeSeriesSensitivity/view.tsx | 6 ++++++ frontend/src/modules/TornadoChart/settings.tsx | 2 +- frontend/src/modules/TornadoChart/view.tsx | 4 ++-- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/frontend/src/modules/SimulationTimeSeriesSensitivity/view.tsx b/frontend/src/modules/SimulationTimeSeriesSensitivity/view.tsx index 24316b57d..c9f713c04 100644 --- a/frontend/src/modules/SimulationTimeSeriesSensitivity/view.tsx +++ b/frontend/src/modules/SimulationTimeSeriesSensitivity/view.tsx @@ -68,6 +68,12 @@ export const view = ({ const ensembleSet = workbenchSession.getEnsembleSet(); const ensemble = vectorSpec ? ensembleSet.findEnsemble(vectorSpec.ensembleIdent) : null; + // Set the active timestamp to the last timestamp in the data if it is not already set + const lastTimestampUtcMs = statisticsQuery.data?.at(0)?.timestamps_utc_ms.slice(-1)[0] ?? null; + if (lastTimestampUtcMs !== null && activeTimestampUtcMs === null) { + setActiveTimestampUtcMs(lastTimestampUtcMs); + } + // Broadcast the data to the realization data channel React.useEffect( function broadcast() { diff --git a/frontend/src/modules/TornadoChart/settings.tsx b/frontend/src/modules/TornadoChart/settings.tsx index 07015498e..507badea9 100644 --- a/frontend/src/modules/TornadoChart/settings.tsx +++ b/frontend/src/modules/TornadoChart/settings.tsx @@ -12,7 +12,7 @@ export function settings({ moduleContext, workbenchServices, initialSettings }: return ( <> -