Skip to content

Commit

Permalink
Interventions charts (#4199)
Browse files Browse the repository at this point in the history
Co-authored-by: Cole Blanchard <[email protected]>
Co-authored-by: Cole Blanchard <[email protected]>
  • Loading branch information
3 people authored Jul 22, 2024
1 parent 2d95b29 commit a2f98cd
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@
<ul class="flex flex-column gap-2">
<li v-for="(interventions, appliedTo) in groupedOutputParameters" :key="appliedTo">
<h5 class="pb-2">{{ appliedTo }}</h5>
<!-- CHARTS HERE-->
<vega-chart :are-embed-actions-visible="false" :visualization-spec="preparedCharts[appliedTo]" />
<ul>
<li class="pb-2" v-for="intervention in interventions" :key="intervention.name">
<h6 class="pb-1">{{ intervention.name }}</h6>
Expand Down Expand Up @@ -124,7 +124,7 @@ import { WorkflowNode } from '@/types/workflow';
import TeraSliderPanel from '@/components/widgets/tera-slider-panel.vue';
import { computed, onMounted, ref, watch } from 'vue';
import TeraColumnarPanel from '@/components/widgets/tera-columnar-panel.vue';
import { cloneDeep, groupBy, isEmpty } from 'lodash';
import _, { cloneDeep, groupBy, isEmpty } from 'lodash';
import Button from 'primevue/button';
import TeraInput from '@/components/widgets/tera-input.vue';
import { getInterventionPoliciesForModel, getModel } from '@/services/model';
Expand All @@ -146,6 +146,8 @@ import EmptySeed from '@/assets/images/lottie-empty-seed.json';
import { Vue3Lottie } from 'vue3-lottie';
import { sortDatesDesc } from '@/utils/date';
import { blankIntervention } from '@/components/workflow/ops/optimize-ciemss/optimize-ciemss-operation';
import { createInterventionChart } from '@/services/charts';
import VegaChart from '@/components/widgets/VegaChart.vue';
import TeraInterventionCard from './tera-intervention-card.vue';
import { InterventionPolicyOperation, InterventionPolicyState } from './tera-intervention-policy-operation';
import TeraInterventionPolicyCard from './tera-intervention-policy-card.vue';
Expand Down Expand Up @@ -205,6 +207,19 @@ const stateOptions = computed(() => {
const groupedOutputParameters = computed(() => groupBy(selectedPolicy.value?.interventions, 'appliedTo'));
const preparedCharts = computed(() =>
_.mapValues(groupedOutputParameters.value, (interventions) => {
const flattenedData = interventions.flatMap((intervention) =>
intervention.staticInterventions.map((staticIntervention) => ({
name: intervention.name,
value: staticIntervention.value,
time: staticIntervention.timestep
}))
);
return createInterventionChart(flattenedData);
})
);
const initialize = async () => {
const state = props.node.state;
const modelId = props.node.inputs[0].value?.[0];
Expand Down Expand Up @@ -291,7 +306,9 @@ const onAddIntervention = () => {
const onDeleteIntervention = (index: number) => {
// Create a new array excluding the intervention at the specified index
const updatedInterventions = knobs.value.transientInterventionPolicy.interventions.filter((_, i) => i !== index);
const updatedInterventions = knobs.value.transientInterventionPolicy.interventions.filter(
(_intervention, i) => i !== index
);
// Reassign the updated interventions array back to the transientInterventionPolicy
// This ensures that we're not modifying the original array in place and Vue's reactivity system detects the change
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ export interface OptimizeCiemssOperationState extends BaseState {
inProgressPostForecastId: string;
postForecastRunId: string;
optimizationRunId: string;
optimizedInterventionPolicy: InterventionPolicy | null;
optimizeErrorMessage: { name: string; value: string; traceback: string };
simulateErrorMessage: { name: string; value: string; traceback: string };
}
Expand Down Expand Up @@ -158,6 +159,7 @@ export const OptimizeCiemssOperation: Operation = {
preForecastRunId: '',
postForecastRunId: '',
optimizationRunId: '',
optimizedInterventionPolicy: null,
optimizeErrorMessage: { name: '', value: '', traceback: '' },
simulateErrorMessage: { name: '', value: '', traceback: '' }
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@
</template>
<script setup lang="ts">
import _, { cloneDeep, Dictionary, groupBy } from 'lodash';
import _, { cloneDeep, Dictionary } from 'lodash';
import { computed, onMounted, ref, watch } from 'vue';
import Button from 'primevue/button';
import Dropdown from 'primevue/dropdown';
Expand Down Expand Up @@ -597,8 +597,8 @@ const setInterventionPolicyGroups = (interventionPolicy: InterventionPolicy) =>
const newIntervention = _.cloneDeep(blankInterventionPolicyGroup);
newIntervention.intervention = intervention;
newIntervention.isActive = !isNotActive;
newIntervention.startTimeGuess = intervention.staticInterventions[0].timestep;
newIntervention.initialGuessValue = intervention.staticInterventions[0].value;
newIntervention.startTimeGuess = intervention.staticInterventions[0]?.timestep;
newIntervention.initialGuessValue = intervention.staticInterventions[0]?.value;
state.interventionPolicyGroups.push(newIntervention);
});
}
Expand Down Expand Up @@ -755,15 +755,36 @@ const setOutputValues = async () => {
const preProcessedInterventionsData = computed<Dictionary<{ name: string; value: number; time: number }[]>>(() => {
const state = _.cloneDeep(props.node.state);
const data = state.interventionPolicyGroups.flatMap((ele) =>
ele.intervention.staticInterventions.map((intervention) => ({
name: ele.intervention.appliedTo,
value: intervention.value,
time: intervention.timestep
// Combine before and after interventions
const combinedInterventions = [
...state.interventionPolicyGroups.flatMap((group) =>
group.intervention.staticInterventions.map((intervention) => ({
appliedTo: group.intervention.appliedTo,
name: group.intervention.name,
value: intervention.value,
time: intervention.timestep
}))
),
...(state.optimizedInterventionPolicy?.interventions.flatMap((intervention) =>
intervention.staticInterventions.map((staticIntervention) => ({
appliedTo: intervention.appliedTo,
name: intervention.name,
value: staticIntervention.value,
time: staticIntervention.timestep
}))
) || [])
];
// Group by appliedTo and map to exclude 'appliedTo' from final objects
const groupedAndMapped = _.mapValues(_.groupBy(combinedInterventions, 'appliedTo'), (interventions) =>
interventions.map(({ name, value, time }) => ({
name,
value,
time
}))
);
return groupBy(data, 'name');
return groupedAndMapped;
});
onMounted(async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ watch(
state.optimizationRunId = optId;
state.inProgressPreForecastId = preForecastId;
state.inProgressPostForecastId = postForecastId;
state.optimizedInterventionPolicy = newInterventionResponse;
emit('update-state', state);
}
},
Expand Down
49 changes: 47 additions & 2 deletions packages/client/hmi-client/src/services/charts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -400,13 +400,31 @@ export function createOptimizeChart(
}

function createInterventionChartMarkers(data: { name: string; value: number; time: number }[]) {
return data.map((ele) => ({
data: [{}], // Dummy data to ensure the layer is rendered
const markers = data.map((ele) => ({
data: { values: data },
mark: { type: 'rule', strokeDash: [4, 4], color: 'black' },
encoding: {
x: { datum: ele.time }
}
}));

const labelsSpec = {
data: { values: data },
mark: {
type: 'text',
align: 'left',
angle: 90,
dx: 5,
dy: -10
},
encoding: {
x: { field: 'time', type: 'quantitative' },
y: { field: 'value', type: 'quantitative' },
text: { field: 'name', type: 'nominal' }
}
};

return [...markers, labelsSpec];
}

function createStatisticLayer(
Expand Down Expand Up @@ -577,3 +595,30 @@ export const createOptimizeForecastChart = (

return spec;
};

export const createInterventionChart = (interventionsData: { name: string; value: number; time: number }[]) => {
const spec: any = {
$schema: VEGALITE_SCHEMA,
width: 400,
autosize: {
type: 'fit'
},
layer: []
};
if (interventionsData && interventionsData.length > 0) {
// markers
createInterventionChartMarkers(interventionsData).forEach((marker) => {
spec.layer.push(marker);
});
// chart
spec.layer.push({
data: { values: interventionsData },
mark: 'point',
encoding: {
x: { field: 'time', type: 'quantitative' },
y: { field: 'value', type: 'quantitative' }
}
});
}
return spec;
};

0 comments on commit a2f98cd

Please sign in to comment.