diff --git a/src/js/Workflow.ts b/src/js/Workflow.ts index 855d23b3..70cbc578 100644 --- a/src/js/Workflow.ts +++ b/src/js/Workflow.ts @@ -347,7 +347,17 @@ class Workflow extends InMemoryEntity implements W } else { const vcRelax = this.getStandataRelaxationSubworkflow(); if (vcRelax) { - this.addSubworkflow(new Subworkflow(vcRelax), true); + const application = structuredClone(this.subworkflowInstances[0].application); + this.addSubworkflow( + new Subworkflow({ + ...vcRelax, + application, + units: vcRelax.units.map((unit) => + unit.type === UnitType.execution ? { ...unit, application } : unit, + ), + }), + true, + ); } } } @@ -370,15 +380,12 @@ class Workflow extends InMemoryEntity implements W return undefined; } - const executionUnit = subworkflow.units.find((unit) => unit.type === UnitType.execution); - if (!executionUnit) { + const hasExecutionUnit = subworkflow.units.some((unit) => unit.type === UnitType.execution); + if (!hasExecutionUnit) { throw new Error("Relaxation subworkflow is missing an execution unit"); } - return { - ...subworkflow, - application: executionUnit.application, - }; + return subworkflow; } private getRelaxationSubworkflow() { diff --git a/src/js/workflows/default.ts b/src/js/workflows/default.ts index 7b739599..0b01235a 100644 --- a/src/js/workflows/default.ts +++ b/src/js/workflows/default.ts @@ -8,9 +8,9 @@ const defaultWorkflowConfig: WorkflowSchema = { _id: "c6e9dbbee8929de01f4e76ee", application: { name: "espresso", - shortName: "espresso", - summary: "Quantum Espresso", - build: "6.3", + shortName: "qe", + summary: "Quantum ESPRESSO", + build: "GNU", version: "6.3", }, model: { diff --git a/tests/js/Workflow.test.ts b/tests/js/Workflow.test.ts index cb540c44..fdc6a39c 100644 --- a/tests/js/Workflow.test.ts +++ b/tests/js/Workflow.test.ts @@ -22,10 +22,12 @@ import type { WorkflowRenderContext } from "src/js/Workflow"; import { Subworkflow, UnitFactory, Workflow } from "../../src/js"; import { UnitType } from "../../src/js/enums"; +import type { ExecutionUnit } from "../../src/js/units"; import { repairWorkflow } from "../../src/js/utils/repair"; import type { WorkflowEntity } from "../../src/js/Workflow"; import type { WorkflowSchema } from "../../src/js/workflows/types"; import workflowHashes from "../fixtures/workflow_hashes.json"; +import { assertNotNull } from "./assertNotNull"; function invalidExecutionUnit(flowchartId: string) { return { @@ -195,6 +197,86 @@ describe("Workflow", () => { }); }); + it("inherits the existing subworkflow's application version and build", () => { + const config = structuredClone(Workflow.defaultConfig); + Object.assign(config.subworkflows[0].application, { build: "Intel", version: "7.5" }); + + const workflow = new Workflow(config); + workflow.toggleRelaxation(); + + const rehydrated = new Workflow(structuredClone(workflow.toJSON())); + const relaxation = assertNotNull( + rehydrated.subworkflowInstances.find( + (subworkflow) => subworkflow.systemName === "espresso-variable-cell-relaxation", + ), + ); + + expect(relaxation.application).to.include({ build: "Intel", version: "7.5" }); + + const executionUnit = assertNotNull( + relaxation.unitsInstances.find((unit) => unit.type === UnitType.execution), + ) as ExecutionUnit; + + expect(executionUnit.application).to.include({ build: "Intel", version: "7.5" }); + expect(executionUnit.executable?.name).to.equal("pw.x"); + expect(executionUnit.flavor?.name).to.equal("pw_vc-relax"); + }); + + it("inherits a version and build the registry actually offers", () => { + const offered = (application: { name: string; version: string; build: string }) => + new ApplicationRegistry() + .getApplications() + .some( + (candidate) => + candidate.name === application.name && + candidate.version === application.version && + candidate.build === application.build, + ); + + const workflow = new Workflow(structuredClone(Workflow.defaultConfig)); + workflow.toggleRelaxation(); + + const rehydrated = new Workflow(structuredClone(workflow.toJSON())); + const relaxation = assertNotNull( + rehydrated.subworkflowInstances.find( + (subworkflow) => subworkflow.systemName === "espresso-variable-cell-relaxation", + ), + ); + + const executionUnit = assertNotNull( + relaxation.unitsInstances.find((unit) => unit.type === UnitType.execution), + ) as ExecutionUnit; + + expect(offered(relaxation.application), "subworkflow application").to.equal(true); + expect(offered(executionUnit.application), "unit application").to.equal(true); + }); + + it("does not reset the model to the application default when inheriting", () => { + const config = structuredClone(Workflow.defaultConfig); + Object.assign(config.subworkflows[0].application, { + name: "vasp", + shortName: "vasp", + summary: "Vienna Ab-initio Simulation Package", + build: "GNU", + version: "5.4.4", + }); + + const workflow = new Workflow(config); + workflow.toggleRelaxation(); + + const rehydrated = new Workflow(structuredClone(workflow.toJSON())); + const relaxation = assertNotNull( + rehydrated.subworkflowInstances.find( + (subworkflow) => subworkflow.systemName === "vasp-variable-cell-relaxation", + ), + ); + + expect(relaxation.model.method).to.include({ + type: "pseudopotential", + subtype: "paw", + }); + }); + it("removes the added relaxation subworkflow on the next toggle", () => { const workflow = new Workflow(structuredClone(Workflow.defaultConfig)); const initialSubworkflowCount = workflow.subworkflowInstances.length;