Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import type { ParameterIdent } from "@framework/EnsembleParameters";
import type { InterfaceInitialization } from "@framework/UniDirectionalModuleComponentsInterface";

import {
parameterIdentsAtom,
showLabelsAtom,
useFixedColorRangeAtom,
plotTypeAtom,
correlationSettingsAtom,
selectedParameterIdentsAtom,
} from "./settings/atoms/baseAtoms";
import type { PlotType, CorrelationSettings } from "./typesAndEnums";

Expand All @@ -23,7 +23,7 @@ export type Interfaces = {
};

export const settingsToViewInterfaceInitialization: InterfaceInitialization<SettingsToViewInterface> = {
parameterIdents: (get) => get(parameterIdentsAtom),
parameterIdents: (get) => get(selectedParameterIdentsAtom),
showLabels: (get) => get(showLabelsAtom),
useFixedColorRange: (get) => get(useFixedColorRangeAtom),
plotType: (get) => get(plotTypeAtom),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import { atom } from "jotai";

import type { ParameterIdent } from "@framework/EnsembleParameters";
import type { RegularEnsembleIdent } from "@framework/RegularEnsembleIdent";
import type { KeyKind, ChannelReceiverReturnData } from "@framework/types/dataChannnel";

import { PlotType, type CorrelationSettings } from "../../typesAndEnums";

export const parameterIdentsAtom = atom<ParameterIdent[]>([]);
export const receivedChannelAtom = atom<ChannelReceiverReturnData<KeyKind.REALIZATION[]>[]>([]);
export const selectedParameterIdentsAtom = atom<ParameterIdent[]>([]);
export const showLabelsAtom = atom<boolean>(false);
export const useFixedColorRangeAtom = atom<boolean>(true);
export const plotTypeAtom = atom<PlotType>(PlotType.ParameterResponseMatrix);

export const plotTypeAtom = atom<PlotType>(PlotType.FullTriangularMatrix);
export const regularEnsembleIdentsAtom = atom<RegularEnsembleIdent[]>([]);
export const correlationSettingsAtom = atom<CorrelationSettings>({
threshold: null,
hideIndividualCells: true,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { atom } from "jotai";

import { EnsembleSetAtom } from "@framework/GlobalAtoms";
import { RegularEnsemble } from "@framework/RegularEnsemble";
import { RegularEnsembleIdent } from "@framework/RegularEnsembleIdent";
import { getContinuousAndNonConstantParameterIdentsInEnsembles } from "@modules/_shared/parameterUnions";

import { receivedChannelAtom } from "./baseAtoms";

export const availableParameterIdentsAtom = atom((get) => {
const receivedChannels = get(receivedChannelAtom);
if (!receivedChannels) {
return [];
}

// Filter for channels that have content
const channelsWithContent = receivedChannels.filter((response) => response.channel?.contents);
if (!channelsWithContent.length) {
return [];
}

// Extract ensemble identifiers from channels with content
const ensembleIdentStrings = channelsWithContent
.flatMap((channel) => channel.channel?.contents || [])
.map((content) => content.metaData.ensembleIdentString);

// Get regular ensemble identifiers
const ensembleSet = get(EnsembleSetAtom);
const regularEnsembleIdents = ensembleIdentStrings
.map((id) => ensembleSet.findEnsembleByIdentString(id))
.filter((ensemble) => ensemble instanceof RegularEnsemble)
.map((ensemble) => RegularEnsembleIdent.fromString(ensemble.getIdent().toString()));
return getContinuousAndNonConstantParameterIdentsInEnsembles(ensembleSet, regularEnsembleIdents);
});
Original file line number Diff line number Diff line change
@@ -1,30 +1,29 @@
import React from "react";

import { useAtom } from "jotai";
import { useAtom, useAtomValue, useSetAtom } from "jotai";

import type { ParameterIdent } from "@framework/EnsembleParameters";
import { useApplyInitialSettingsToState } from "@framework/InitialSettings";
import type { ModuleSettingsProps } from "@framework/Module";
import { RegularEnsemble } from "@framework/RegularEnsemble";
import { RegularEnsembleIdent } from "@framework/RegularEnsembleIdent";
import { KeyKind } from "@framework/types/dataChannnel";
import { Checkbox } from "@lib/components/Checkbox";
import { CollapsibleGroup } from "@lib/components/CollapsibleGroup";
import { Label } from "@lib/components/Label";
import { RadioGroup } from "@lib/components/RadioGroup";
import { ParametersSelector } from "@modules/_shared/components/ParameterSelector";
import { getContinuousAndNonConstantParameterIdentsInEnsembles } from "@modules/_shared/parameterUnions";

import type { Interfaces } from "../interfaces";
import { PlotType } from "../typesAndEnums";

import {
correlationSettingsAtom,
parameterIdentsAtom,
plotTypeAtom,
showLabelsAtom,
useFixedColorRangeAtom,
receivedChannelAtom,
selectedParameterIdentsAtom,
} from "./atoms/baseAtoms";
import { availableParameterIdentsAtom } from "./atoms/derivedAtoms";

const plotTypesOptions = [
{
Expand All @@ -41,44 +40,51 @@ const plotTypesOptions = [
},
];

export function Settings({ initialSettings, settingsContext, workbenchSession }: ModuleSettingsProps<Interfaces>) {
const [parameterIdents, setParameterIdents] = useAtom(parameterIdentsAtom);
export function Settings({ initialSettings, settingsContext }: ModuleSettingsProps<Interfaces>) {
const [selectedParameterIdents, setSelectedParameterIdents] = useAtom(selectedParameterIdentsAtom);
const [plotType, setPlotType] = useAtom(plotTypeAtom);
const [showLabels, setShowLabels] = useAtom(showLabelsAtom);
const [useFixedColorRange, setUseFixedColorRange] = useAtom(useFixedColorRangeAtom);
const [correlationSettings, setCorrelationSettings] = useAtom(correlationSettingsAtom);
const setReceivedChannel = useSetAtom(receivedChannelAtom);
const availableParameterIdents = useAtomValue(availableParameterIdentsAtom);

useApplyInitialSettingsToState(initialSettings, "parameterIdents", "array", setParameterIdents);
useApplyInitialSettingsToState(initialSettings, "selectedParameterIdents", "array", setSelectedParameterIdents);
useApplyInitialSettingsToState(initialSettings, "showLabels", "boolean", setShowLabels);
useApplyInitialSettingsToState(initialSettings, "correlationSettings", "object", setCorrelationSettings);
const receiverResponse = settingsContext.useChannelReceiver({

const receiverResponse1 = settingsContext.useChannelReceiver({
receiverIdString: "channelResponse",
expectedKindsOfKeys: [KeyKind.REALIZATION],
});
const receiverResponse2 = settingsContext.useChannelReceiver({
receiverIdString: "channelResponse2",
expectedKindsOfKeys: [KeyKind.REALIZATION],
});
const receiverResponse3 = settingsContext.useChannelReceiver({
receiverIdString: "channelResponse3",
expectedKindsOfKeys: [KeyKind.REALIZATION],
});
const receiverResponses = React.useMemo(
() => [receiverResponse1, receiverResponse2, receiverResponse3],
[receiverResponse1, receiverResponse2, receiverResponse3],
);

const ensembleIdentStringsFromChannels: string[] = React.useMemo(() => {
if (receiverResponse.channel && receiverResponse.channel.contents) {
return receiverResponse.channel.contents.map((content) => content.metaData.ensembleIdentString);
}
return [];
}, [receiverResponse.channel]);

const ensembleSet = workbenchSession.getEnsembleSet();

const regularEnsembleIdentsFromChannels: RegularEnsembleIdent[] = React.useMemo(() => {
return ensembleIdentStringsFromChannels.flatMap((id) => {
const ensemble = ensembleSet.findEnsembleByIdentString(id);
return ensemble instanceof RegularEnsemble ? [RegularEnsembleIdent.fromString(id)] : [];
});
}, [ensembleIdentStringsFromChannels, ensembleSet]);

const allParameterIdents = getContinuousAndNonConstantParameterIdentsInEnsembles(
ensembleSet,
regularEnsembleIdentsFromChannels,
React.useEffect(
() => {
setReceivedChannel(receiverResponses);
}, // We only want to listen to revision number changes, but we need the whole channel response to set it
// eslint-disable-next-line react-hooks/exhaustive-deps
[
receiverResponse1.revisionNumber,
receiverResponse2.revisionNumber,
receiverResponse3.revisionNumber,
setReceivedChannel,
],
);

function handleParametersChanged(parameterIdents: ParameterIdent[]) {
setParameterIdents(parameterIdents);
setSelectedParameterIdents(parameterIdents);
}
function handleShowLabelsChanged(e: React.ChangeEvent<HTMLInputElement>) {
setShowLabels(e.target.checked);
Expand Down Expand Up @@ -168,8 +174,8 @@ export function Settings({ initialSettings, settingsContext, workbenchSession }:
</CollapsibleGroup>
<CollapsibleGroup title="Parameter selection" expanded>
<ParametersSelector
allParameterIdents={allParameterIdents}
selectedParameterIdents={parameterIdents}
allParameterIdents={availableParameterIdents}
selectedParameterIdents={selectedParameterIdents}
onChange={handleParametersChanged}
/>
</CollapsibleGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ export type CorrelationSettings = {
filterColumns: boolean;
filterRows: boolean;
};
export const MAX_NUMBER_OF_PARAMETERS_IN_MATRIX = 500; // To avoid performance issues
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export class ParameterCorrelationMatrixFigure {
showlegend: false,
font: {
family: "Roboto, sans-serif",
size: 12,
size: 10,
color: "#333",
},
});
Expand All @@ -78,11 +78,11 @@ export class ParameterCorrelationMatrixFigure {
};
// Always show response labels
if (this._forceShowYAxisLabels) {
margin.l = 200;
margin.l = 100;
}
if (this._showLabels) {
margin.l = 200;
margin.b = 200;
margin.l = 100;
margin.b = 100;
}
this._figure.updateLayout({
margin: margin,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import {
} from "@modules/_shared/utils/math/correlationMatrix";

import type { Interfaces } from "../interfaces";
import { PlotType, type CorrelationSettings } from "../typesAndEnums";
import { MAX_NUMBER_OF_PARAMETERS_IN_MATRIX, PlotType, type CorrelationSettings } from "../typesAndEnums";

import { ParameterCorrelationMatrixFigure } from "./utils/parameterCorrelationMatrixFigure";
import { createResponseParameterCorrelationMatrix } from "./utils/parameterCorrelationMatrixUtils";
Expand Down Expand Up @@ -93,6 +93,7 @@ export function View({ viewContext, workbenchSession, workbenchSettings }: Modul

statusWriter.setLoading(isPending || receiverResponses.some((r) => r.isPending));
const receiverResponseRevisionNumbers = receiverResponses.map((response) => response.revisionNumber);

const hasParameterIdentsChanged =
parameterIdents.length !== prevParameterIdents.length ||
!parameterIdents.every((ident, index) => ident.equals(prevParameterIdents[index]));
Expand All @@ -118,24 +119,49 @@ export function View({ viewContext, workbenchSession, workbenchSettings }: Modul

startTransition(function makeContent() {
// Content when no data channels are defined

if (receiverResponses.every((response) => !response.channel)) {
setContent(
<ContentWarning>
<span>
Data channel required for use. Add a main module to the workbench and use the data channels
<Input fontSize="small" />
</span>{" "}
Up to 3 modules can be connected.
<span>
<Tag label="Response" />
<Tag label="Response" />
<Tag label="Response" />
</span>
<div className="space-y-3 text-sm">
<p className="font-medium">Data channel required for use.</p>
<p>Add a module supporting data channels to the dashboard and connect it to this module.</p>
<p>
Modules supporting data channels have an <Input fontSize="small" /> icon on their
toolbar.
</p>
<p>Drag from this icon to a response below:</p>
<div className="flex gap-2 flex-wrap">
<Tag label="Response" />
<Tag label="Response" />
<Tag label="Response" />
</div>
</div>
</ContentWarning>,
);
return;
}
if (parameterIdents.length === 0) {
setContent(
<ContentWarning>
<Warning fontSize="large" className="mb-2" />
No parameters selected or available. Please select parameters in the settings pane. If no
parameters are available, ensure that the connected ensembles have continuous and varying
parameters.
</ContentWarning>,
);
return;
}
if (parameterIdents.length > MAX_NUMBER_OF_PARAMETERS_IN_MATRIX) {
setContent(
<ContentWarning>
<Warning fontSize="large" className="mb-2" />
{`Too many parameters selected. Please select ${MAX_NUMBER_OF_PARAMETERS_IN_MATRIX} or fewer parameters to display the correlation
matrix.`}
</ContentWarning>,
);
return;
}

const usedChannels = receiverResponses.filter((response) => response.channel);
const usedChannelsWithoutData = receiverResponses.filter(
(response) => response.channel && response.channel.contents.length === 0,
Expand Down Expand Up @@ -172,6 +198,31 @@ export function View({ viewContext, workbenchSession, workbenchSettings }: Modul
receiveResponsesPerEnsembleIdent.get(ensembleIdentString)?.push(content);
});
});
for (const ensembleIdentString of receiveResponsesPerEnsembleIdent.keys()) {
const ensemble = ensembleSet.findEnsembleByIdentString(ensembleIdentString);
if (!ensemble || ensemble instanceof DeltaEnsemble) {
const ensembleType = !ensemble ? "Invalid" : "Delta";
setContent(
<ContentWarning>
<p>{ensembleType} ensemble detected in the data channel.</p>
<p>Unable to compute parameter correlations.</p>
</ContentWarning>,
);
return;
}
}
// Content when no parameters are selected
if (parameterIdents.length === 0) {
setContent(
<ContentWarning>
<Warning fontSize="large" className="mb-2" />
No parameters selected or available. Please select parameters in the settings pane. If
parameters are selected but not available, ensure that the connected ensembles have continuous
and varying parameters.
</ContentWarning>,
);
return;
}

const numContents = receiveResponsesPerEnsembleIdent.size;

Expand All @@ -189,19 +240,7 @@ export function View({ viewContext, workbenchSession, workbenchSettings }: Modul
showLabels,
useFixedColorRange,
});
for (const ensembleIdentString of receiveResponsesPerEnsembleIdent.keys()) {
const ensemble = ensembleSet.findEnsembleByIdentString(ensembleIdentString);
if (!ensemble || ensemble instanceof DeltaEnsemble) {
const ensembleType = !ensemble ? "Invalid" : "Delta";
setContent(
<ContentWarning>
<p>{ensembleType} ensemble detected in the data channel.</p>
<p>Unable to compute parameter correlations.</p>
</ContentWarning>,
);
return;
}
}

fillParameterCorrelationMatrixFigure(
figure,
parameterIdents,
Expand Down
Loading
Loading