Bladeren bron

Refactored Task to use run/runAsChild calls on the object itself

David Sehnal 7 jaren geleden
bovenliggende
commit
dd475dc679

+ 1 - 2
src/apps/cif2bcif/converter.ts

@@ -9,11 +9,10 @@ import CIF, { Category } from 'mol-io/reader/cif'
 import * as Encoder from 'mol-io/writer/cif'
 import * as fs from 'fs'
 import classify from './field-classifier'
-import { Run } from 'mol-task'
 
 async function getCIF(path: string) {
     const str = fs.readFileSync(path, 'utf8');
-    const parsed = await Run(CIF.parseText(str));
+    const parsed = await CIF.parseText(str).run();
     if (parsed.isError) {
         throw new Error(parsed.toString());
     }

+ 2 - 2
src/apps/combine-mmcif/index.ts

@@ -13,7 +13,7 @@ require('util.promisify').shim();
 const readFile = util.promisify(fs.readFile);
 const writeFile = util.promisify(fs.writeFile);
 
-import { Run, Progress } from 'mol-task'
+import { Progress } from 'mol-task'
 import { Database, Table, DatabaseCollection } from 'mol-data/db'
 import CIF from 'mol-io/reader/cif'
 // import { CCD_Schema } from 'mol-io/reader/cif/schema/ccd'
@@ -78,7 +78,7 @@ export async function getBIRD() {
 async function parseCif(data: string|Uint8Array) {
     const comp = CIF.parse(data);
     console.time('parse cif');
-    const parsed = await Run(comp, p => console.log(Progress.format(p)), 250);
+    const parsed = await comp.run(p => console.log(Progress.format(p)), 250);
     console.timeEnd('parse cif');
     if (parsed.isError) throw parsed;
     return parsed

+ 8 - 9
src/apps/render-test/state.ts

@@ -16,11 +16,10 @@ import Viewer from 'mol-view/viewer'
 import Spacefill, { SpacefillProps } from 'mol-geo/representation/structure/spacefill'
 import Point, { PointProps } from 'mol-geo/representation/structure/point'
 
-import { Run } from 'mol-task'
 import { StructureSymmetry, Structure, Model } from 'mol-model/structure'
 
 // import mcubes from './utils/mcubes'
-import { getModelFromPdbId, getModelFromFile, log, Volume, getVolumeFromEmdId } from './utils'
+import { getModelFromPdbId, getModelFromFile, runTask, Volume, getVolumeFromEmdId } from './utils'
 import { StructureRepresentation } from 'mol-geo/representation/structure';
 import { Color } from 'mol-util/color';
 import Surface, { SurfaceProps } from 'mol-geo/representation/volume/surface';
@@ -110,7 +109,7 @@ export default class State {
         let structure: Structure
         const assemblies = model.symmetry.assemblies
         if (assemblies.length) {
-            structure = await Run(StructureSymmetry.buildAssembly(Structure.ofModel(model), assembly || '1'), log, 500)
+            structure = await runTask(StructureSymmetry.buildAssembly(Structure.ofModel(model), assembly || '1'));
         } else {
             structure = Structure.ofModel(model)
         }
@@ -128,11 +127,11 @@ export default class State {
         if (!structure) return
 
         this.pointRepr = StructureRepresentation(Point)
-        await Run(this.pointRepr.create(structure, this.getPointProps()), log, 500)
+        await runTask(this.pointRepr.create(structure, this.getPointProps()))
         viewer.add(this.pointRepr)
 
         this.spacefillRepr = StructureRepresentation(Spacefill)
-        await Run(this.spacefillRepr.create(structure, this.getSpacefillProps()), log, 500)
+        await runTask(this.spacefillRepr.create(structure, this.getSpacefillProps()))
         viewer.add(this.spacefillRepr)
 
         this.updateVisibility()
@@ -160,13 +159,13 @@ export default class State {
         if (this.surfaceRepr) this.viewer.remove(this.surfaceRepr)
 
         this.surfaceRepr = VolumeRepresentation(Surface)
-        await Run(this.surfaceRepr.create(v.volume, {
+        await runTask(this.surfaceRepr.create(v.volume, {
             isoValue: VolumeIsoValue.relative(v.volume.dataStats, 3.0),
             alpha: 0.5,
             flatShaded: false,
             flipSided: true,
             doubleSided: true
-        }), log, 500)
+        }))
         viewer.add(this.surfaceRepr)
 
         viewer.requestDraw()
@@ -197,8 +196,8 @@ export default class State {
 
     async update () {
         if (!this.spacefillRepr) return
-        await Run(this.spacefillRepr.update(this.getSpacefillProps()), log, 500)
-        await Run(this.pointRepr.update(this.getPointProps()), log, 500)
+        await runTask(this.spacefillRepr.update(this.getSpacefillProps()))
+        await runTask(this.pointRepr.update(this.getPointProps()))
         this.viewer.add(this.spacefillRepr)
         this.viewer.add(this.pointRepr)
         this.viewer.update()

+ 9 - 5
src/apps/render-test/utils/index.ts

@@ -5,7 +5,7 @@
  */
 
 import CIF from 'mol-io/reader/cif'
-import { Run, Progress } from 'mol-task'
+import { Progress, Task } from 'mol-task'
 import { Model } from 'mol-model/structure'
 import { VolumeData, parseDensityServerData } from 'mol-model/volume'
 import { DensityServer_Data_Database } from 'mol-io/reader/cif/schema/density-server';
@@ -15,6 +15,10 @@ export function log(progress: Progress) {
     console.log(`${p.message} ${(p.current/p.max*100).toFixed(2)}%`)
 }
 
+export function runTask<T>(task: Task<T>) {
+    return task.run(log, 500);
+}
+
 export async function downloadCif(url: string, isBinary: boolean) {
     const data = await fetch(url);
     return parseCif(isBinary ? new Uint8Array(await data.arrayBuffer()) : await data.text());
@@ -22,14 +26,14 @@ export async function downloadCif(url: string, isBinary: boolean) {
 
 export async function parseCif(data: string|Uint8Array) {
     const comp = CIF.parse(data)
-    const parsed = await Run(comp, log, 500);
+    const parsed = await comp.run(log, 500);
     if (parsed.isError) throw parsed;
     return parsed.result
 }
 
 export async function getModelFromPdbId(pdbid: string) {
     const cif = await downloadCif(`https://files.rcsb.org/download/${pdbid}.cif`, false)
-    return Run(Model.create({ kind: 'mmCIF', data: CIF.schema.mmCIF(cif.blocks[0]) }))
+    return Model.create({ kind: 'mmCIF', data: CIF.schema.mmCIF(cif.blocks[0]) }).run();
 }
 
 const readFileAsText = (file: File) => {
@@ -46,7 +50,7 @@ const readFileAsText = (file: File) => {
 
 export async function getModelFromFile(file: File) {
     const cif = await parseCif(await readFileAsText(file))
-    return Run(Model.create({ kind: 'mmCIF', data: CIF.schema.mmCIF(cif.blocks[0]) }))
+    return Model.create({ kind: 'mmCIF', data: CIF.schema.mmCIF(cif.blocks[0]) }).run();
 }
 
 export type Volume = { source: DensityServer_Data_Database, volume: VolumeData }
@@ -54,5 +58,5 @@ export type Volume = { source: DensityServer_Data_Database, volume: VolumeData }
 export async function getVolumeFromEmdId(emdid: string): Promise<Volume> {
     const cif = await downloadCif(`https://webchem.ncbr.muni.cz/DensityServer/em/emd-${emdid}/cell?detail=4`, true)
     const data = CIF.schema.densityServer(cif.blocks[1])
-    return { source: data, volume: await Run(parseDensityServerData(data)) }
+    return { source: data, volume: await parseDensityServerData(data).run() }
 }

+ 2 - 3
src/apps/render-test/utils/mcubes.ts

@@ -4,7 +4,6 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import { Run } from 'mol-task'
 import { computeMarchingCubes } from 'mol-geo/util/marching-cubes/algorithm'
 import { Mesh } from 'mol-geo/shape/mesh'
 import { Tensor, Mat4, Vec3 } from 'mol-math/linear-algebra'
@@ -38,11 +37,11 @@ export default async function computeSurface(f: (x: number, y: number, z: number
     const min = Vec3.create(-1.1, -1.1, -1.1), max = Vec3.create(1.1, 1.1, 1.1);
 
     fillField(field, f, min, max);
-    const surface = await Run(computeMarchingCubes({
+    const surface = await computeMarchingCubes({
         scalarField: field,
         isoLevel: 0,
         oldSurface: data ? data.surface : void 0
-    }));
+    }).run();
 
     const translation = Mat4.fromTranslation(Mat4.zero(), min);
     const grid = Vec3.zero();

+ 3 - 4
src/apps/schema-generator/schema-from-mmcif-dic.ts

@@ -14,15 +14,14 @@ import CIF, { Frame } from 'mol-io/reader/cif'
 import { generateSchema } from './util/cif-dic'
 import { generate } from './util/generate'
 import { Filter } from './util/json-schema'
-import { Run } from 'mol-task'
 
 async function runGenerateSchema(name: string, fieldNamesPath?: string, typescript = false, out?: string) {
     await ensureMmcifDicAvailable()
-    const mmcifDic = await Run(CIF.parseText(fs.readFileSync(MMCIF_DIC_PATH, 'utf8')));
+    const mmcifDic = await CIF.parseText(fs.readFileSync(MMCIF_DIC_PATH, 'utf8')).run();
     if (mmcifDic.isError) throw mmcifDic
 
     await ensureIhmDicAvailable()
-    const ihmDic = await Run(CIF.parseText(fs.readFileSync(IHM_DIC_PATH, 'utf8')));
+    const ihmDic = await CIF.parseText(fs.readFileSync(IHM_DIC_PATH, 'utf8')).run();
     if (ihmDic.isError) throw ihmDic
 
     const mmcifDicVersion = CIF.schema.dic(mmcifDic.result.blocks[0]).dictionary.version.value(0)
@@ -44,7 +43,7 @@ async function runGenerateSchema(name: string, fieldNamesPath?: string, typescri
 
 async function getFieldNamesFilter(fieldNamesPath: string): Promise<Filter> {
     const fieldNamesStr = fs.readFileSync(fieldNamesPath, 'utf8')
-    const parsed = await Run(Csv(fieldNamesStr, { noColumnNames: true }));
+    const parsed = await Csv(fieldNamesStr, { noColumnNames: true }).run();
     if (parsed.isError) throw parser.error
     const csvFile = parsed.result;
 

+ 2 - 2
src/apps/structure-info/helpers.ts

@@ -10,7 +10,7 @@ import fetch from 'node-fetch'
 require('util.promisify').shim();
 
 import CIF from 'mol-io/reader/cif'
-import { Run, Progress } from 'mol-task'
+import { Progress } from 'mol-task'
 
 const readFileAsync = util.promisify(fs.readFile);
 
@@ -27,7 +27,7 @@ async function readFile(path: string) {
 
 async function parseCif(data: string|Uint8Array) {
     const comp = CIF.parse(data);
-    const parsed = await Run(comp, p => console.log(Progress.format(p)), 250);
+    const parsed = await comp.run(p => console.log(Progress.format(p)), 250);
     if (parsed.isError) throw parsed;
     return parsed.result;
 }

+ 1 - 2
src/apps/structure-info/model.ts

@@ -16,7 +16,6 @@ import { OrderedSet } from 'mol-data/int';
 import { Table } from 'mol-data/db';
 import { mmCIF_Database } from 'mol-io/reader/cif/schema/mmcif';
 import { openCif, downloadCif } from './helpers';
-import { Run } from 'mol-task';
 
 
 async function downloadFromPdb(pdb: string) {
@@ -118,7 +117,7 @@ export function printIHMModels(model: Model) {
 }
 
 async function run(mmcif: mmCIF_Database) {
-    const models = await Run(Model.create({ kind: 'mmCIF', data: mmcif }));
+    const models = await Model.create({ kind: 'mmCIF', data: mmcif }).run();
     const structure = Structure.ofModel(models[0]);
     printSequence(models[0]);
     printIHMModels(models[0]);

+ 2 - 3
src/apps/structure-info/volume.ts

@@ -12,7 +12,6 @@ import { VolumeData, parseDensityServerData, VolumeIsoValue } from 'mol-model/vo
 import { downloadCif } from './helpers'
 import CIF from 'mol-io/reader/cif'
 import { DensityServer_Data_Database } from 'mol-io/reader/cif/schema/density-server';
-import { Run } from 'mol-task';
 import { Table } from 'mol-data/db';
 import { computeVolumeSurface } from 'mol-geo/representation/volume/surface';
 import { StringBuilder } from 'mol-util';
@@ -25,7 +24,7 @@ type Volume = { source: DensityServer_Data_Database, volume: VolumeData }
 async function getVolume(url: string): Promise<Volume> {
     const cif = await downloadCif(url, true);
     const data = CIF.schema.densityServer(cif.blocks[1]);
-    return { source: data, volume: await Run(parseDensityServerData(data)) };
+    return { source: data, volume: await parseDensityServerData(data).run() };
 }
 
 function print(data: Volume) {
@@ -38,7 +37,7 @@ function print(data: Volume) {
 }
 
 async function doMesh(data: Volume, filename: string) {
-    const mesh = await Run(computeVolumeSurface(data.volume, VolumeIsoValue.relative(data.volume.dataStats, 1.5)));
+    const mesh = await computeVolumeSurface(data.volume, VolumeIsoValue.relative(data.volume.dataStats, 1.5)).run();
     console.log({ vc: mesh.vertexCount, tc: mesh.triangleCount });
 
     // Export the mesh in OBJ format.

+ 8 - 8
src/examples/task.ts

@@ -4,11 +4,11 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import { Task, Run, Progress, Scheduler, now, MultistepTask, chunkedSubtask } from 'mol-task'
+import { Task, Progress, Scheduler, now, MultistepTask, chunkedSubtask } from 'mol-task'
 
 export async function test1() {
     const t = Task.create('test', async () => 1);
-    const r = await Run(t);
+    const r = await t.run();
     console.log(r);
 }
 
@@ -53,9 +53,9 @@ export function testTree() {
         if (ctx.shouldUpdate) await ctx.update('hi! 3');
 
         // ctx.update('Running children...', true);
-        const c1 = ctx.runChild(createTask(250, 1));
-        const c2 = ctx.runChild(createTask(500, 2));
-        const c3 = ctx.runChild(createTask(750, 3));
+        const c1 = createTask(250, 1).runAsChild(ctx);
+        const c2 = createTask(500, 2).runAsChild(ctx);
+        const c3 = createTask(750, 3).runAsChild(ctx);
 
         //await ctx.runChild(abortAfter(350));
 
@@ -87,8 +87,8 @@ export const ms = MultistepTask('ms-task', ['step 1', 'step 2', 'step 3'], async
         const s = await chunkedSubtask(ctx, 25, { i: 0, current: 0, total: 125 }, processChunk, (ctx, s, p) => ctx.update('chunk test ' + p))
         return s.i;
     });
-    
-    await ctx.runChild(child);
+
+    await child.runAsChild(ctx);
     await Scheduler.delay(250);
     await step(1);
     await chunkedSubtask(ctx, 25, { i: 0, current: 0, total: 80 }, processChunk, (ctx, s, p) => ctx.update('chunk test ' + p))
@@ -114,7 +114,7 @@ async function test() {
         //const r = await Run(testTree(), abortingObserver, 250);
         //console.log(r);
 
-        const m = await Run(ms({ i: 10 }), logP);
+        const m = await ms({ i: 10 }).run(logP);
         console.log(m);
     } catch (e) {
         console.error(e);

+ 3 - 3
src/mol-geo/representation/structure/index.ts

@@ -42,7 +42,7 @@ export function StructureRepresentation<P>(reprCtor: () => UnitsRepresentation<P
                     const group = groups[i];
                     const repr = reprCtor()
                     groupReprs.push({ repr, group })
-                    await ctx.runChild(repr.create(group, props), { message: 'Building structure unit representations...', current: i, max: groups.length });
+                    await repr.create(group, props).runAsChild(ctx, { message: 'Building structure unit representations...', current: i, max: groups.length });
                     renderObjects.push(...repr.renderObjects)
                 }
             });
@@ -55,8 +55,8 @@ export function StructureRepresentation<P>(reprCtor: () => UnitsRepresentation<P
                     const groupRepr = groupReprs[i]
                     const { repr, group } = groupRepr
                     const state = { message: 'Updating structure unit representations...', current: i, max: il };
-                    if (!await ctx.runChild(repr.update(props), state)) {
-                        await ctx.runChild(repr.create(group, props), state)
+                    if (!await repr.update(props).runAsChild(ctx, state)) {
+                        await repr.create(group, props).runAsChild(ctx, state)
                     }
                     renderObjects.push(...repr.renderObjects)
                 }

+ 1 - 2
src/mol-geo/representation/structure/spacefill.ts

@@ -83,8 +83,7 @@ export default function Spacefill(): UnitsRepresentation<SpacefillProps> {
 
                 const { detail, colorTheme, alpha, visible, doubleSided } = { ...DefaultSpacefillProps, ...props }
 
-                await ctx.update('Computing spacefill mesh');
-                const mesh = await ctx.runChild(createSpacefillMesh(group.units[0], detail))
+                const mesh = await createSpacefillMesh(group.units[0], detail).runAsChild(ctx, 'Computing spacefill mesh')
                 // console.log(mesh)
 
                 const vertexMap = VertexMap.fromMesh(mesh)

+ 1 - 2
src/mol-geo/representation/volume/index.ts

@@ -29,8 +29,7 @@ export function VolumeRepresentation<P>(reprCtor: () => VolumeElementRepresentat
         create(volumeData: VolumeData, props: P = {} as P) {
             return Task.create('VolumeRepresentation.create', async ctx => {
                 const repr = reprCtor()
-                await ctx.update({ message: 'Building volume representation...', current: 0, max: 1 });
-                await ctx.runChild(repr.create(volumeData, props));
+                await repr.create(volumeData, props).runAsChild(ctx, { message: 'Building volume representation...', current: 0, max: 1 });
                 renderObjects.push(...repr.renderObjects)
             });
         },

+ 3 - 3
src/mol-geo/representation/volume/surface.ts

@@ -20,10 +20,10 @@ export function computeVolumeSurface(volume: VolumeData, isoValue: VolumeIsoValu
     return Task.create<Mesh>('Volume Surface', async ctx => {
         ctx.update({ message: 'Marching cubes...' });
 
-        const mesh = await ctx.runChild(computeMarchingCubes({
+        const mesh = await computeMarchingCubes({
             isoLevel: VolumeIsoValue.toAbsolute(isoValue).absoluteValue,
             scalarField: volume.data
-        }));
+        }).runAsChild(ctx);
 
         const transform = VolumeData.getGridToCartesianTransform(volume);
         ctx.update({ message: 'Transforming mesh...' });
@@ -56,7 +56,7 @@ export default function Surface(): VolumeElementRepresentation<SurfaceProps> {
                 curProps = { ...DefaultSurfaceProps, ...props }
                 const { alpha, visible, flatShaded, flipSided, doubleSided } = curProps
 
-                const mesh = await ctx.runChild(computeVolumeSurface(volume, curProps.isoValue))
+                const mesh = await computeVolumeSurface(volume, curProps.isoValue).runAsChild(ctx)
                 if (!flatShaded) {
                     Mesh.computeNormalsImmediate(mesh)
                 }

+ 3 - 4
src/mol-io/reader/_spec/csv.spec.ts

@@ -5,7 +5,6 @@
  */
 
 import Csv from '../csv/parser'
-import { Run } from 'mol-task';
 
 const csvStringBasic = `StrCol,IntCol,FloatCol
 # comment
@@ -24,7 +23,7 @@ string2\t42\t2.44`
 
 describe('csv reader', () => {
     it('basic', async () => {
-        const parsed = await Run(Csv(csvStringBasic));
+        const parsed = await Csv(csvStringBasic).run();
         if (parsed.isError) return;
         const csvFile = parsed.result;
 
@@ -46,7 +45,7 @@ describe('csv reader', () => {
     });
 
     it('advanced', async () => {
-        const parsed = await Run(Csv(csvStringAdvanced));
+        const parsed = await Csv(csvStringAdvanced).run();
         if (parsed.isError) return;
         const csvFile = parsed.result;
 
@@ -63,7 +62,7 @@ describe('csv reader', () => {
     });
 
     it('tabs', async () => {
-        const parsed = await Run(Csv(tabString, { delimiter: '\t' }));
+        const parsed = await Csv(tabString, { delimiter: '\t' }).run();;
         if (parsed.isError) return;
         const csvFile = parsed.result;
 

+ 2 - 3
src/mol-io/reader/_spec/gro.spec.ts

@@ -6,7 +6,6 @@
  */
 
 import Gro from '../gro/parser'
-import { Run } from 'mol-task';
 
 const groString = `MD of 2 waters, t= 4.2
     6
@@ -27,7 +26,7 @@ const groStringHighPrecision = `Generated by trjconv : 2168 system t=  15.00000
 
 describe('gro reader', () => {
     it('basic', async () => {
-        const parsed = await Run(Gro(groString));
+        const parsed = await Gro(groString).run();
 
         if (parsed.isError) {
             console.log(parsed)
@@ -58,7 +57,7 @@ describe('gro reader', () => {
     });
 
     it('high precision', async () => {
-        const parsed = await Run(Gro(groStringHighPrecision));
+        const parsed = await Gro(groStringHighPrecision).run();
 
         if (parsed.isError) {
             console.log(parsed)

+ 3 - 4
src/mol-io/reader/_spec/mol2.spec.ts

@@ -1,6 +1,5 @@
 
 import Mol2 from '../mol2/parser'
-import { Run } from 'mol-task';
 
 const Mol2String = `@<TRIPOS>MOLECULE
 5816
@@ -247,7 +246,7 @@ GASTEIGER
 
 describe('mol2 reader', () => {
     it('basic', async () => {
-        const parsed =  await Run(Mol2(Mol2String));
+        const parsed =  await Mol2(Mol2String).run();
         if (parsed.isError) {
             throw new Error(parsed.message);
         }
@@ -298,7 +297,7 @@ describe('mol2 reader', () => {
     });
 
     it('multiblocks', async () => {
-        const parsed =  await Run(Mol2(Mol2StringMultiBlocks));
+        const parsed =  await Mol2(Mol2StringMultiBlocks).run();
         if (parsed.isError) {
             throw new Error(parsed.message);
         }
@@ -349,7 +348,7 @@ describe('mol2 reader', () => {
     });
 
     it('minimal', async () => {
-        const parsed =  await Run(Mol2(Mol2StringMinimal));
+        const parsed =  await Mol2(Mol2StringMinimal).run();
         if (parsed.isError) {
             throw new Error(parsed.message);
         }

+ 1 - 1
src/mol-model/structure/structure/symmetry.ts

@@ -26,7 +26,7 @@ namespace StructureSymmetry {
             const assembler = Structure.Builder();
 
             for (const g of assembly.operatorGroups) {
-                const selection = await ctx.runChild(g.selector(structure));
+                const selection = await g.selector(structure).runAsChild(ctx);
                 if (Selection.structureCount(selection) === 0) {
                     continue;
                 }

+ 17 - 10
src/mol-task/execution/observable.ts

@@ -10,13 +10,22 @@ import { Progress } from './progress'
 import { now } from '../util/now'
 import { Scheduler } from '../util/scheduler'
 
-function ExecuteObservable<T>(task: Task<T>, observer: Progress.Observer, updateRateMs = 250) {
+interface ExposedTask<T> extends Task<T> {
+    f: (ctx: RuntimeContext) => Promise<T>,
+    onAbort?: () => void
+}
+
+export function ExecuteObservable<T>(task: Task<T>, observer: Progress.Observer, updateRateMs = 250) {
     const info = ProgressInfo(task, observer, updateRateMs);
     const ctx = new ObservableRuntimeContext(info, info.root);
-    return execute(task, ctx);
+    return execute(task as ExposedTask<T>, ctx);
+}
+
+export function ExecuteObservableChild<T>(ctx: RuntimeContext, task: Task<T>, progress?: string | Partial<RuntimeContext.ProgressUpdate>) {
+    return (ctx as ObservableRuntimeContext).runChild(task, progress);
 }
 
-namespace ExecuteObservable {
+export namespace ExecuteObservable {
     export let PRINT_ERRORS_TO_STD_ERR = false;
 }
 
@@ -77,10 +86,10 @@ function snapshotProgress(info: ProgressInfo): Progress {
     return { root: cloneTree(info.root), canAbort: canAbort(info.root), requestAbort: info.tryAbort };
 }
 
-async function execute<T>(task: Task<T>, ctx: ObservableRuntimeContext) {
+async function execute<T>(task: ExposedTask<T>, ctx: ObservableRuntimeContext) {
     ctx.node.progress.startedTime = now();
     try {
-        const ret = await task.__f(ctx);
+        const ret = await task.f(ctx);
         if (ctx.info.abortToken.abortRequested) {
             abort(ctx.info, ctx.node);
         }
@@ -91,7 +100,7 @@ async function execute<T>(task: Task<T>, ctx: ObservableRuntimeContext) {
             if (ctx.node.children.length > 0) {
                 await new Promise(res => { ctx.onChildrenFinished = res; });
             }
-            if (task.__onAbort) task.__onAbort();
+            if (task.onAbort) task.onAbort();
         }
         if (ExecuteObservable.PRINT_ERRORS_TO_STD_ERR) console.error(e);
         throw e;
@@ -197,7 +206,7 @@ class ObservableRuntimeContext implements RuntimeContext {
         children.push(node);
         const ctx = new ObservableRuntimeContext(this.info, node);
         try {
-            return await execute(task, ctx);
+            return await execute(task as ExposedTask<T>, ctx);
         } catch (e) {
             if (Task.isAbort(e)) {
                 // need to catch the error here because otherwise
@@ -223,6 +232,4 @@ class ObservableRuntimeContext implements RuntimeContext {
         this.node = node;
         this.info = info;
     }
-}
-
-export { ExecuteObservable }
+}

+ 1 - 7
src/mol-task/execution/runtime-context.ts

@@ -4,8 +4,6 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import { Task } from '../task'
-
 interface RuntimeContext {
     readonly shouldUpdate: boolean,
     readonly isSynchronous: boolean,
@@ -15,11 +13,7 @@ interface RuntimeContext {
     //
     // Alternatively, progress can be updated without notifying (and yielding) using update(progress, true).
     // This is useful for nested tasks.
-    update(progress?: string | Partial<RuntimeContext.ProgressUpdate>, dontNotify?: boolean): Promise<void> | void,
-
-    // Run a child task that adds a new node to the progress tree.
-    // Allow to pass the progress so that the progress tree can be kept in a "good state" without having to separately call update.
-    runChild<T>(task: Task<T>, progress?: string | Partial<RuntimeContext.ProgressUpdate>): Promise<T>
+    update(progress?: string | Partial<RuntimeContext.ProgressUpdate>, dontNotify?: boolean): Promise<void> | void
 }
 
 namespace RuntimeContext {

+ 1 - 10
src/mol-task/execution/synchronous.ts

@@ -4,21 +4,12 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import { Task } from '../task'
 import { RuntimeContext } from './runtime-context'
 
 class SynchronousRuntimeContext implements RuntimeContext {
     shouldUpdate = false;
     isSynchronous = true;
-
     update(progress: string | Partial<RuntimeContext.ProgressUpdate>, dontNotify?: boolean): Promise<void> | void { }
-    runChild<T>(task: Task<T>, progress?: string | Partial<RuntimeContext.ProgressUpdate>): Promise<T> { return ExecuteSynchronous(task); }
-}
-
-const SyncRuntimeInstance = new SynchronousRuntimeContext();
-
-function ExecuteSynchronous<T>(task: Task<T>) {
-    return task.__f(SyncRuntimeInstance);
 }
 
-export { ExecuteSynchronous }
+export const SyncRuntimeContext = new SynchronousRuntimeContext();

+ 1 - 12
src/mol-task/index.ts

@@ -6,21 +6,10 @@
 
 import { Task } from './task'
 import { RuntimeContext } from './execution/runtime-context'
-import { ExecuteSynchronous } from './execution/synchronous'
-import { ExecuteObservable } from './execution/observable'
 import { Progress } from './execution/progress'
 import { now } from './util/now'
 import { Scheduler } from './util/scheduler'
 import { MultistepTask } from './util/multistep'
 import { chunkedSubtask } from './util/chunked'
 
-// Run the task without the ability to observe its progress.
-function Run<T>(task: Task<T>): Promise<T>;
-// Run the task with the ability to observe its progress and request cancellation.
-function Run<T>(task: Task<T>, observer: Progress.Observer, updateRateMs?: number): Promise<T>;
-function Run<T>(task: Task<T>, observer?: Progress.Observer, updateRateMs?: number): Promise<T> {
-    if (observer) return ExecuteObservable(task, observer, updateRateMs || 250);
-    return ExecuteSynchronous(task);
-}
-
-export { Task, RuntimeContext, Progress, Run, now, Scheduler, MultistepTask, chunkedSubtask }
+export { Task, RuntimeContext, Progress, now, Scheduler, MultistepTask, chunkedSubtask }

+ 32 - 6
src/mol-task/task.ts

@@ -5,25 +5,51 @@
  */
 
 import { RuntimeContext } from './execution/runtime-context'
+import { Progress } from './execution/progress'
+import { ExecuteObservable, ExecuteObservableChild } from './execution/observable';
+import { SyncRuntimeContext } from 'mol-task/execution/synchronous';
 
 // A "named function wrapper" with built in "computation tree progress tracking".
 // Use Run(t, ?observer, ?updateRate) to execute
 interface Task<T> {
+    // run the task without observation
+    run(): Promise<T>,
+    // run the task with the specified observer, default updateRate is 250ms
+    run(observer: Progress.Observer, updateRateMs?: number): Promise<T>,
+
+    // Run a child task that adds a new node to the progress tree.
+    // Allow to pass the progress so that the progress tree can be kept in a "good state" without having to separately call update.
+    runAsChild(ctx: RuntimeContext, progress?: string | Partial<RuntimeContext.ProgressUpdate>): Promise<T>
+
     readonly id: number,
-    readonly name: string,
-    // Do not call this directly, use Run.
-    readonly __f: (ctx: RuntimeContext) => Promise<T>,
-    // Do not call this directly, use Run.
-    readonly __onAbort: (() => void) | undefined
+    readonly name: string
 }
 
 namespace Task {
+    class Impl<T> implements Task<T> {
+        readonly id: number;
+
+        run(observer?: Progress.Observer, updateRateMs?: number): Promise<T> {
+            if (observer) return ExecuteObservable(this, observer, updateRateMs as number || 250);
+            return this.f(SyncRuntimeContext);
+        }
+
+        runAsChild(ctx: RuntimeContext, progress?: string | Partial<RuntimeContext.ProgressUpdate>): Promise<T> {
+            if (ctx.isSynchronous) return this.f(SyncRuntimeContext);
+            return ExecuteObservableChild(ctx, this, progress as string | Partial<RuntimeContext.ProgressUpdate>);
+        }
+
+        constructor(public name: string, public f: (ctx: RuntimeContext) => Promise<T>, public onAbort?: () => void) {
+            this.id = nextId();
+        }
+    }
+
     export interface Aborted { isAborted: true, reason: string }
     export function isAbort(e: any): e is Aborted { return !!e && !!e.isAborted; }
     export function Aborted(reason: string): Aborted { return { isAborted: true, reason }; }
 
     export function create<T>(name: string, f: (ctx: RuntimeContext) => Promise<T>, onAbort?: () => void): Task<T> {
-        return { id: nextId(), name, __f: f, __onAbort: onAbort };
+        return new Impl(name, f, onAbort);
     }
 
     export function constant<T>(name: string, value: T): Task<T> { return create(name, async ctx => value); }

+ 2 - 3
src/perf-tests/lookup3d.ts

@@ -4,7 +4,6 @@ import CIF from 'mol-io/reader/cif'
 
 import { Structure, Model } from 'mol-model/structure'
 
-import { Run } from 'mol-task';
 import { GridLookup3D } from 'mol-math/geometry';
 // import { sortArray } from 'mol-data/util';
 import { OrderedSet } from 'mol-data/int';
@@ -27,14 +26,14 @@ async function readData(path: string) {
 export async function readCIF(path: string) {
     const input = await readData(path)
     const comp = typeof input === 'string' ? CIF.parseText(input) : CIF.parseBinary(input);
-    const parsed = await Run(comp);
+    const parsed = await comp.run();
     if (parsed.isError) {
         throw parsed;
     }
 
     const data = parsed.result.blocks[0];
     const mmcif = CIF.schema.mmCIF(data);
-    const models = await Run(Model.create({ kind: 'mmCIF', data: mmcif }));
+    const models = await Model.create({ kind: 'mmCIF', data: mmcif }).run();
     const structures = models.map(Structure.ofModel);
 
     return { mmcif, models, structures };

+ 5 - 6
src/perf-tests/structure.ts

@@ -15,7 +15,6 @@ import { Structure, Model, Queries as Q, Element, Selection, StructureSymmetry,
 //import { Segmentation, OrderedSet } from 'mol-data/int'
 
 import to_mmCIF from 'mol-model/structure/export/mmcif'
-import { Run } from 'mol-task';
 import { Vec3 } from 'mol-math/linear-algebra';
 //import { EquivalenceClasses } from 'mol-data/util';
 
@@ -62,7 +61,7 @@ export async function readCIF(path: string) {
 
     console.time('parse');
     const comp = typeof input === 'string' ? CIF.parseText(input) : CIF.parseBinary(input);
-    const parsed = await Run(comp);
+    const parsed = await comp.run();
     console.timeEnd('parse');
     if (parsed.isError) {
         throw parsed;
@@ -74,7 +73,7 @@ export async function readCIF(path: string) {
 
     console.timeEnd('schema')
     console.time('buildModels')
-    const models = await Run(Model.create({ kind: 'mmCIF', data: mmcif }));
+    const models = await Model.create({ kind: 'mmCIF', data: mmcif }).run();
     console.timeEnd('buildModels')
     const structures = models.map(Structure.ofModel);
 
@@ -292,7 +291,7 @@ export namespace PropertyAccess {
 
     export async function testAssembly(id: string, s: Structure) {
         console.time('assembly')
-        const a = await Run(StructureSymmetry.buildAssembly(s, '1'));
+        const a = await StructureSymmetry.buildAssembly(s, '1').run();
         //const auth_comp_id = Q.props.residue.auth_comp_id;
         //const q1 = Query(Q.generators.atoms({ residueTest: l => auth_comp_id(l) === 'ALA' }));
         //const alas = await query(q1, a);
@@ -305,7 +304,7 @@ export namespace PropertyAccess {
 
     export async function testSymmetry(id: string, s: Structure) {
         console.time('symmetry')
-        const a = await Run(StructureSymmetry.buildSymmetryRange(s, Vec3.create(-1, -1, -1), Vec3.create(1, 1, 1)));
+        const a = await StructureSymmetry.buildSymmetryRange(s, Vec3.create(-1, -1, -1), Vec3.create(1, 1, 1)).run();
         //const auth_comp_id = Q.props.residue.auth_comp_id;
         //const q1 = Query(Q.generators.atoms({ residueTest: l => auth_comp_id(l) === 'ALA' }));
         //const alas = await query(q1, a);
@@ -336,7 +335,7 @@ export namespace PropertyAccess {
     // }
 
     function query(q: Query, s: Structure) {
-        return Run((q(s)));
+        return q(s).run();
     }
 
     export async function run() {