Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

situational awareness state population #5551

Merged
merged 34 commits into from
Nov 21, 2024
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
86aff14
scenario template storage and layout
Nov 15, 2024
f2c81bd
rank and node sep
Nov 18, 2024
0974d8b
Merge branch 'main' into scenario-template-storage-and-layout
blanchco Nov 18, 2024
fac5327
Merge branch 'main' into scenario-template-storage-and-layout
blanchco Nov 18, 2024
e3efef4
Merge branch 'main' into scenario-template-storage-and-layout
blanchco Nov 18, 2024
4a1ce56
situational awareness state population
Nov 19, 2024
0edba14
async
Nov 19, 2024
333222f
-commented code
Nov 19, 2024
cad3d1f
Merge branch 'main' into scenario-template-storage-and-layout
blanchco Nov 19, 2024
8802ac6
Merge branch 'scenario-template-storage-and-layout' into situational-…
blanchco Nov 19, 2024
00196be
yohanns suggestions
Nov 19, 2024
dca1893
merge
Nov 19, 2024
a706ca2
merge
Nov 20, 2024
2e38573
changes
Nov 20, 2024
3b934a7
comments
Nov 20, 2024
edb9eb3
updateNode
Nov 20, 2024
41e5d0f
lodash
Nov 20, 2024
c6f122a
enter press
Nov 20, 2024
e690326
init focus input
Nov 20, 2024
5842f9e
Merge branch 'main' into situational-awareness-cleanup
blanchco Nov 20, 2024
5d89a27
comment
Nov 20, 2024
4ddf577
nexttick
Nov 20, 2024
db187dc
Merge branch 'main' into situational-awareness-cleanup
blanchco Nov 20, 2024
0260b48
-intervention settings
Nov 20, 2024
4d891ef
Merge branch 'main' into situational-awareness-cleanup
blanchco Nov 20, 2024
680e571
-error distribution
Nov 20, 2024
1709ade
cahrt variables
Nov 20, 2024
d076821
-comment
Nov 20, 2024
afd0061
spelling
Nov 20, 2024
c6d1950
Merge branch 'main' into situational-awareness-cleanup
blanchco Nov 20, 2024
4e30ede
set output port if needed
Nov 20, 2024
cc0398d
blank canvas svg and updateNode ugrade
Nov 20, 2024
61f8c63
Merge branch 'main' into situational-awareness-cleanup
blanchco Nov 21, 2024
9a0b5ab
Merge branch 'main' into situational-awareness-cleanup
blanchco Nov 21, 2024
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
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,8 @@ main > section {
}

.content {
max-height: 65vh;
padding: 0 2rem;
max-height: 80vh;
padding: 0 var(--gap-8);
overflow-y: auto;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ export interface CalibrationOperationStateCiemss extends BaseState {
endTime: number;
stepSize: number;
learningRate: number;

// This is a state variable to indicate which model variables are selected for calibration, this is filled in from the workflow template generator
templateSelectedModelVariables: string[];
blanchco marked this conversation as resolved.
Show resolved Hide resolved
}

export const CalibrationOperationCiemss: Operation = {
Expand Down Expand Up @@ -68,7 +71,8 @@ export const CalibrationOperationCiemss: Operation = {
numSamples: 100,
endTime: 100,
stepSize: 1,
learningRate: 0.03
learningRate: 0.03,
templateSelectedModelVariables: []
};
return init;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -818,9 +818,18 @@ const initDefaultChartSettings = (state: CalibrationOperationStateCiemss) => {
// Initialize default selected chart settings when chart settings are not set yet. Return if chart settings are already set.
if (Array.isArray(state.chartSettings)) return;
const defaultSelectedParam = modelParameters.value.filter((p) => !!p.distribution).map((p) => p.id);
const mappedModelVariables = mapping.value
.filter((c) => ['state', 'observable'].includes(modelPartTypesMap.value[c.modelVariable]))
.map((c) => c.modelVariable);
const mappedModelVariablesSet = new Set(
mapping.value
.filter((c) => ['state', 'observable'].includes(modelPartTypesMap.value[c.modelVariable]))
.map((c) => c.modelVariable)
);

props.node.state.templateSelectedModelVariables.forEach((variable) => {
mappedModelVariablesSet.add(variable);
});

const mappedModelVariables = Array.from(mappedModelVariablesSet);

state.chartSettings = updateChartSettingsBySelectedVariables([], ChartSettingType.VARIABLE, mappedModelVariables);
state.chartSettings = updateChartSettingsBySelectedVariables(
state.chartSettings,
Expand Down Expand Up @@ -886,6 +895,8 @@ const runCalibrate = async () => {
state.inProgressForecastId = '';
state.inProgressPreForecastId = '';
initDefaultChartSettings(state);
// we want to make this an empty array after the first click of run, these are generated from the workflow template
state.templateSelectedModelVariables = [];
emit('update-state', state);
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,11 @@ export abstract class BaseScenario {
this.workflowName = '';
}

abstract createWorkflow(): Workflow;
abstract createWorkflow(): Promise<Workflow>;

isValid(): boolean {
return true;
}

setWorkflowName(name: string) {
this.workflowName = name;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export class BlankCanvasScenario extends BaseScenario {
};
}

createWorkflow() {
async createWorkflow() {
const wf = new workflowService.WorkflowWrapper();
wf.setWorkflowName(this.workflowName);
wf.setWorkflowScenario(this.toJSON());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { operation as ModelConfigOp } from '@/components/workflow/ops/model-conf
import { operation as CalibrateCiemssOp } from '@/components/workflow/ops/calibrate-ciemss/mod';
import { operation as DatasetOp } from '@/components/workflow/ops/dataset/mod';
import { OperatorNodeSize } from '@/services/workflow';
import { getModelConfigurationById } from '@/services/model-configurations';
import _ from 'lodash';

export class SituationalAwarenessScenario extends BaseScenario {
public static templateId = 'situational-awareness';
Expand All @@ -19,6 +21,10 @@ export class SituationalAwarenessScenario extends BaseScenario {

calibrateSpec: { ids: string[] };

historicalInterventionSpec: { id: string };

futureInterventionSpec: { id: string };

constructor() {
super();
this.workflowName = 'Situational Awareness';
Expand All @@ -34,6 +40,12 @@ export class SituationalAwarenessScenario extends BaseScenario {
this.calibrateSpec = {
ids: []
};
this.historicalInterventionSpec = {
id: ''
};
this.futureInterventionSpec = {
id: ''
};
}

setModelSpec(id: string) {
Expand All @@ -50,6 +62,14 @@ export class SituationalAwarenessScenario extends BaseScenario {
this.modelConfigSpec.id = id;
}

setHistoricalInterventionSpec(id: string) {
this.historicalInterventionSpec.id = id;
}

setFutureInterventionSpec(id: string) {
this.futureInterventionSpec.id = id;
}

setCalibrateSpec(ids: string[]) {
this.calibrateSpec.ids = ids;
}
Expand All @@ -65,18 +85,32 @@ export class SituationalAwarenessScenario extends BaseScenario {
};
}

createWorkflow() {
isValid(): boolean {
return (
this.modelSpec.id !== '' &&
this.datasetSpec.id !== '' &&
this.modelConfigSpec.id !== '' &&
!_.isEmpty(this.calibrateSpec.ids)
);
}

async createWorkflow() {
const wf = new workflowService.WorkflowWrapper();
wf.setWorkflowName(this.workflowName);
wf.setWorkflowScenario(this.toJSON());

// Add nodes
// Model

const modelNode = wf.addNode(
ModelOp,
{ x: 0, y: 0 },
{
size: OperatorNodeSize.medium
size: OperatorNodeSize.medium,
state: {
modelId: this.modelSpec.id
},
outputValue: this.modelSpec.id
}
);

Expand All @@ -85,16 +119,25 @@ export class SituationalAwarenessScenario extends BaseScenario {
DatasetOp,
{ x: 0, y: 0 },
{
size: OperatorNodeSize.medium
size: OperatorNodeSize.medium,
state: {
datasetId: this.datasetSpec.id
},
outputValue: this.datasetSpec.id
}
);

// Model Configuration
const modelConfig = await getModelConfigurationById(this.modelConfigSpec.id);
const modelConfigNode = wf.addNode(
ModelConfigOp,
{ x: 0, y: 0 },
{
size: OperatorNodeSize.medium
size: OperatorNodeSize.medium,
state: {
transientModelConfig: modelConfig
},
outputValue: this.modelConfigSpec.id
}
);

Expand All @@ -103,7 +146,10 @@ export class SituationalAwarenessScenario extends BaseScenario {
CalibrateCiemssOp,
{ x: 0, y: 0 },
{
size: OperatorNodeSize.medium
size: OperatorNodeSize.medium,
state: {
templateSelectedModelVariables: this.calibrateSpec.ids
}
}
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@
</div>
<div>
<h6>Examples</h6>
<ul>
<li>Some example</li>
<ul class="pl-5">
<li>Anticipate the arrival of new variants.</li>
<li>Evaluate the potential impact of growing vaccine hesitancy and declining NPIs.</li>
</ul>
</div>

Expand Down Expand Up @@ -41,11 +42,26 @@
@update:model-value="scenario.setDatasetSpec($event)"
/>

<label>Select an intervention policy (historical)</label>
<Dropdown placeholder="Optional" :disabled="isEmpty(interventionPolicies) || isFetchingModelInformation" />
<!-- TODO: adding intervention policies -->
<!-- <label>Select an intervention policy (historical)</label>
<Dropdown
:model-value="scenario.historicalInterventionSpec.id"
placeholder="Optional"
:options="interventionPolicies"
option-label="name"
option-value="id"
@update:model-value="scenario.setHistoricalInterventionSpec($event)"
:disabled="isEmpty(interventionPolicies) || isFetchingModelInformation" />

<label>Select an intervention policy (known future)</label>
<Dropdown placeholder="Optional" :disabled="isEmpty(interventionPolicies) || isFetchingModelInformation" />
<Dropdown
:model-value="scenario.futureInterventionSpec.id"
placeholder="Optional"
:options="interventionPolicies"
option-label="name"
option-value="id"
@update:model-value="scenario.setFutureInterventionSpec($event)"
:disabled="isEmpty(interventionPolicies) || isFetchingModelInformation" /> -->

<label>Select configuration representing best and generous estimates of the initial conditions</label>
<Dropdown
Expand All @@ -71,6 +87,7 @@
@update:model-value="scenario.setCalibrateSpec($event)"
filter
/>
<img :src="calibrate" alt="Calibrate chart" />
</div>
</div>
</template>
Expand All @@ -84,6 +101,7 @@ import { getInterventionPoliciesForModel, getModel, getModelConfigurationsForMod
import { isEmpty } from 'lodash';
import TeraInputText from '@/components/widgets/tera-input-text.vue';
import MultiSelect from 'primevue/multiselect';
import calibrate from '@/assets/svg/template-images/calibration-thumbnail.svg';
import { SituationalAwarenessScenario } from './situational-awareness-scenario';

const isFetchingModelInformation = ref(false);
Expand All @@ -108,6 +126,11 @@ watch(
modelConfigurations.value = await getModelConfigurationsForModel(modelId);
interventionPolicies.value = await getInterventionPoliciesForModel(modelId);

// Set the first model configuration as the default
if (!isEmpty(modelConfigurations.value)) {
props.scenario.setModelConfigSpec(modelConfigurations.value[0].id!);
}

const modelOptions: any[] = model.model.states;

model.semantics?.ode.observables?.forEach((o) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,20 @@
</template>
<template #default>
<div class="grid">
<aside class="col-2">
<label>Select a template</label>
<aside class="flex flex-column col-2">
<label class="p-text-secondary pb-2">Select a template</label>
<div v-for="scenario in scenarios" :key="scenario.id" class="flex align-items-center py-1">
<RadioButton :inputId="scenario.id" :value="scenario.id" v-model="selectedTemplateId" />
<label class="pl-2" :for="scenario.id">{{ scenario.displayName }}</label>
</div>
</aside>
<main class="col-10 flex flex-column gap-3">
<main class="col-10 flex flex-column gap-3 p-3">
<component v-if="getScenario()" :is="getScenario().component" :scenario="getScenario().instance" />
</main>
</div>
</template>
<template #footer>
<Button label="Create" size="large" @click="saveWorkflow" />
<Button label="Create" size="large" @click="saveWorkflow" :disabled="!getScenario().instance.isValid()" />
<Button label="Close" class="p-button-secondary" size="large" outlined @click="emit('close-modal')" />
</template>
</tera-modal>
Expand Down Expand Up @@ -69,7 +69,7 @@ const selectedTemplateId = ref<any>(scenarios.value[0].id);

const saveWorkflow = async () => {
const scenario = getScenario();
const wf = scenario.instance.createWorkflow();
const wf = await scenario.instance.createWorkflow();
const response = await createWorkflow(wf);

const projectId = useProjects().activeProject.value?.id;
Expand Down
15 changes: 12 additions & 3 deletions packages/client/hmi-client/src/services/workflow.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Component } from 'vue';
import { v4 as uuidv4 } from 'uuid';
import _ from 'lodash';
import _, { isArray } from 'lodash';
import API from '@/api/api';
import { logger } from '@/utils/logger';
import { EventEmitter } from '@/utils/emitter';
Expand Down Expand Up @@ -210,7 +210,7 @@ export class WorkflowWrapper {
}
}

addNode(op: Operation, pos: Position, options: { size?: OperatorNodeSize; state?: any }) {
addNode(op: Operation, pos: Position, options: { size?: OperatorNodeSize; state?: any; outputValue?: string }) {
const nodeSize: Size = getOperatorNodeSize(options.size ?? OperatorNodeSize.medium);

const node: WorkflowNode<any> = {
Expand Down Expand Up @@ -250,10 +250,19 @@ export class WorkflowWrapper {
width: nodeSize.width,
height: nodeSize.height
};
if (op.initState && _.isEmpty(node.state)) {
if (op.initState) {
node.state = op.initState();
Object.assign(node.state, options.state);
}
this.wf.nodes.push(node);

if (options.outputValue) {
node.status = OperatorStatus.SUCCESS;
const outputPort = node.outputs[0];
outputPort.value = isArray(options.outputValue) ? options.outputValue : [options.outputValue];
node.active = outputPort.id;
this.selectOutput(node, outputPort.id);
}
mwdchang marked this conversation as resolved.
Show resolved Hide resolved
return node;
}

Expand Down