Browse Source

improve handling of coarse grained models

Alexander Rose 4 years ago
parent
commit
3831bd9941

+ 1 - 1
src/mol-model-props/computed/interactions.ts

@@ -28,7 +28,7 @@ export const InteractionsProvider: CustomStructureProperty.Provider<Interactions
     type: 'local',
     defaultParams: InteractionsParams,
     getParams: (data: Structure) => InteractionsParams,
-    isApplicable: (data: Structure) => true,
+    isApplicable: (data: Structure) => !data.isCoarseGrained,
     obtain: async (ctx: CustomProperty.Context, data: Structure, props: Partial<InteractionsProps>) => {
         const p = { ...PD.getDefaultValues(InteractionsParams), ...props };
         return { value: await computeInteractions(ctx, data, p) };

+ 1 - 1
src/mol-model-props/computed/representations/interactions.ts

@@ -46,7 +46,7 @@ export const InteractionsRepresentationProvider = StructureRepresentationProvide
     defaultValues: PD.getDefaultValues(InteractionsParams),
     defaultColorTheme: { name: 'interaction-type' },
     defaultSizeTheme: { name: 'uniform' },
-    isApplicable: (structure: Structure) => structure.elementCount > 0,
+    isApplicable: (structure: Structure) => structure.elementCount > 0 && InteractionsProvider.isApplicable(structure),
     ensureCustomProperties: {
         attach: (ctx: CustomProperty.Context, structure: Structure) => InteractionsProvider.attach(ctx, structure, void 0, true),
         detach: (data) => InteractionsProvider.ref(data, false)

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

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2017-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2017-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author David Sehnal <david.sehnal@gmail.com>
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
@@ -29,6 +29,7 @@ import { CustomModelProperty } from '../../../mol-model-props/common/custom-mode
 import { Trajectory, ArrayTrajectory } from '../trajectory';
 import { Unit } from '../structure';
 import { SortedArray } from '../../../mol-data/int/sorted-array';
+import { PolymerType } from './types';
 
 /**
  * Interface to the "source data" of the molecule.
@@ -224,6 +225,39 @@ export namespace Model {
         }
     };
 
+    const CoarseGrainedProp = '__CoarseGrained__';
+    /**
+     * Has typical coarse grained atom names (BB, SC1) or less than twice as many
+     * atoms as polymer residues (C-alpha only models).
+     */
+    export function isCoarseGrained(model: Model): boolean {
+        if (model._staticPropertyData[CoarseGrainedProp] !== undefined) return model._staticPropertyData[CoarseGrainedProp];
+
+        let polymerResidueCount = 0;
+        const { polymerType } = model.atomicHierarchy.derived.residue;
+        for (let i = 0; i < polymerType.length; ++i) {
+            if (polymerType[i] !== PolymerType.NA) polymerResidueCount += 1;
+        }
+
+        // check for coarse grained atom names
+        let hasBB = false, hasSC1 = false;
+        const { label_atom_id, _rowCount: atomCount } = model.atomicHierarchy.atoms;
+        for (let i = 0; i < atomCount; ++i) {
+            const atomName = label_atom_id.value(i);
+            if (!hasBB && atomName === 'BB') hasBB = true;
+            if (!hasSC1 && atomName === 'SC1') hasSC1 = true;
+            if (hasBB && hasSC1) break;
+        }
+
+        const coarseGrained = (hasBB && hasSC1) || (
+            polymerResidueCount && atomCount
+                ? atomCount / polymerResidueCount < 2
+                : false
+        );
+        model._staticPropertyData[CoarseGrainedProp] = coarseGrained;
+        return coarseGrained;
+    }
+
     //
 
     export function hasCarbohydrate(model: Model): boolean {

+ 5 - 7
src/mol-model/structure/structure/structure.ts

@@ -158,13 +158,11 @@ class Structure {
     }
 
     /**
-     * Coarse-grained structure, defined as containing less than
-     * twice as many elements as polymer residues
+     * True if any model the structure is based on is coarse grained.
+     * @see Model.isCoarseGrained
      */
     get isCoarseGrained() {
-        const ec = this.elementCount;
-        const prc = this.polymerResidueCount;
-        return prc && ec ? ec / prc < 2 : false;
+        return this.models.some(m => Model.isCoarseGrained(m));
     }
 
     get isEmpty() {
@@ -299,7 +297,7 @@ class Structure {
 
     /** Contains only atomic units */
     get isAtomic() {
-        for (const u of this.units) if (Unit.isAtomic(u)) return false;
+        for (const u of this.units) if (!Unit.isAtomic(u)) return false;
         return true;
     }
 
@@ -311,7 +309,7 @@ class Structure {
 
     /** Contains only coarse units */
     get isCoarse() {
-        for (const u of this.units) if (Unit.isCoarse(u)) return false;
+        for (const u of this.units) if (!Unit.isCoarse(u)) return false;
         return true;
     }
 

+ 1 - 1
src/mol-model/structure/structure/unit/bonds/inter-compute.ts

@@ -176,7 +176,7 @@ export interface InterBondComputationProps extends BondComputationProps {
 function findBonds(structure: Structure, props: InterBondComputationProps) {
     const builder = new InterUnitGraph.Builder<number, StructureElement.UnitIndex, InterUnitEdgeProps>();
 
-    if (props.noCompute) {
+    if (props.noCompute || structure.isCoarseGrained) {
         // TODO add function that only adds bonds defined in structConn and avoids using
         //      structure.lookup and unit.lookup (expensive for large structure and not
         //      needed for archival files or files with an MD topology)

+ 2 - 1
src/mol-model/structure/structure/unit/bonds/intra-compute.ts

@@ -19,6 +19,7 @@ import { StructConn } from '../../../../../mol-model-formats/structure/property/
 import { Vec3 } from '../../../../../mol-math/linear-algebra';
 import { ElementIndex } from '../../../model/indexing';
 import { equalEps } from '../../../../../mol-math/linear-algebra/3d/common';
+import { Model } from '../../../model/model';
 
 function getGraph(atomA: StructureElement.UnitIndex[], atomB: StructureElement.UnitIndex[], _order: number[], _flags: number[], atomCount: number, canRemap: boolean): IntraUnitBonds {
     const builder = new IntAdjacencyGraph.EdgeBuilder(atomCount, atomA, atomB);
@@ -233,7 +234,7 @@ function findBonds(unit: Unit.Atomic, props: BondComputationProps): IntraUnitBon
 
 function computeIntraUnitBonds(unit: Unit.Atomic, props?: Partial<BondComputationProps>) {
     const p = { ...DefaultBondComputationProps, ...props };
-    if (p.noCompute) {
+    if (p.noCompute || Model.isCoarseGrained(unit.model)) {
         // TODO add function that only adds bonds defined in structConn of chemCompBond
         //      and avoid using unit.lookup
         return IntraUnitBonds.Empty;

+ 16 - 7
src/mol-plugin-state/builder/structure/representation-preset.ts

@@ -22,6 +22,7 @@ import { ChainIdColorThemeProvider } from '../../../mol-theme/color/chain-id';
 import { OperatorNameColorThemeProvider } from '../../../mol-theme/color/operator-name';
 import { IndexPairBonds } from '../../../mol-model-formats/structure/property/bonds/index-pair';
 import { StructConn } from '../../../mol-model-formats/structure/property/bonds/struct_conn';
+import { StructureRepresentationRegistry } from '../../../mol-repr/structure/registry';
 
 export interface StructureRepresentationPresetProvider<P = any, S extends _Result = _Result> extends PresetProvider<PluginStateObject.Molecule.Structure, P, S> { }
 export function StructureRepresentationPresetProvider<P, S extends _Result>(repr: StructureRepresentationPresetProvider<P, S>) { return repr; }
@@ -230,7 +231,7 @@ const coarseSurface = StructureRepresentationPresetProvider({
     id: 'preset-structure-representation-coarse-surface',
     display: {
         name: 'Coarse Surface', group: BuiltInPresetGroupName,
-        description: 'Shows polymers as coarse Gaussian Surface.'
+        description: 'Shows polymers and lipids as coarse Gaussian Surface.'
     },
     params: () => CommonParams,
     async apply(ref, params, plugin) {
@@ -238,7 +239,8 @@ const coarseSurface = StructureRepresentationPresetProvider({
         if (!structureCell) return {};
 
         const components = {
-            polymer: await presetStaticComponent(plugin, structureCell, 'polymer')
+            polymer: await presetStaticComponent(plugin, structureCell, 'polymer'),
+            lipid: await presetStaticComponent(plugin, structureCell, 'lipid'),
         };
 
         const structure = structureCell.obj!.data;
@@ -246,7 +248,7 @@ const coarseSurface = StructureRepresentationPresetProvider({
         const gaussianProps = Object.create(null);
         if (size === Structure.Size.Gigantic) {
             Object.assign(gaussianProps, {
-                traceOnly: true,
+                traceOnly: !structure.isCoarseGrained,
                 radiusOffset: 2,
                 smoothness: 1,
                 visuals: ['structure-gaussian-surface-mesh']
@@ -266,7 +268,8 @@ const coarseSurface = StructureRepresentationPresetProvider({
         const { update, builder, typeParams, symmetryColor } = reprBuilder(plugin, params, structure);
 
         const representations = {
-            polymer: builder.buildRepresentation(update, components.polymer, { type: 'gaussian-surface', typeParams: { ...typeParams, ...gaussianProps }, color: symmetryColor }, { tag: 'polymer' })
+            polymer: builder.buildRepresentation(update, components.polymer, { type: 'gaussian-surface', typeParams: { ...typeParams, ...gaussianProps }, color: symmetryColor }, { tag: 'polymer' }),
+            lipid: builder.buildRepresentation(update, components.lipid, { type: 'gaussian-surface', typeParams: { ...typeParams, ...gaussianProps }, color: symmetryColor }, { tag: 'lipid' })
         };
 
         await update.commit({ revertOnError: true });
@@ -337,9 +340,15 @@ const atomicDetail = StructureRepresentationPresetProvider({
         const m = structure.models[0];
         const bondsGiven = !!IndexPairBonds.Provider.get(m) || StructConn.isExhaustive(m);
 
-        const atomicType = lowResidueElementRatio && !bondsGiven
-            ? 'spacefill' : highElementCount
-                ? 'line' : 'ball-and-stick';
+        let atomicType: StructureRepresentationRegistry.BuiltIn = 'ball-and-stick';
+        if (structure.isCoarseGrained) {
+            // TODO make configurable?
+            atomicType = structure.elementCount > 1_000_000 ? 'point' : 'spacefill';
+        } else if (lowResidueElementRatio && !bondsGiven) {
+            atomicType = 'spacefill';
+        } else if (highElementCount) {
+            atomicType = 'line';
+        }
         const showCarbohydrateSymbol = params.showCarbohydrateSymbol && !highElementCount && !lowResidueElementRatio;
 
         if (showCarbohydrateSymbol) {

+ 2 - 2
src/mol-plugin/behavior/dynamic/selection/structure-focus-representation.ts

@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2019-2020 mol* contributors, licensed under MIT, See LICENSE file for more info.
+ * Copyright (c) 2019-2021 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
  * @author David Sehnal <david.sehnal@gmail.com>
  * @author Alexander Rose <alexander.rose@weirdbyte.de>
@@ -107,7 +107,7 @@ class StructureFocusRepresentationBehavior extends PluginBehavior.WithSubscriber
                 .apply(StateTransforms.Representation.StructureRepresentation3D, this.getReprParams(this.params.surroundingsParams), { tags: StructureFocusRepresentationTags.SurrRepr }).ref;
         }
 
-        if (components.indexOf('interactions') >= 0 && !refs[StructureFocusRepresentationTags.SurrNciRepr]) {
+        if (components.indexOf('interactions') >= 0 && !refs[StructureFocusRepresentationTags.SurrNciRepr] && cell.obj && InteractionsRepresentationProvider.isApplicable(cell.obj?.data)) {
             refs[StructureFocusRepresentationTags.SurrNciRepr] = builder
                 .to(refs[StructureFocusRepresentationTags.SurrSel]!)
                 .apply(StateTransforms.Representation.StructureRepresentation3D, this.params.nciParams, { tags: StructureFocusRepresentationTags.SurrNciRepr }).ref;

+ 5 - 1
src/mol-repr/structure/representation/spacefill.ts

@@ -21,7 +21,11 @@ export const SpacefillParams = {
 };
 export type SpacefillParams = typeof SpacefillParams
 export function getSpacefillParams(ctx: ThemeRegistryContext, structure: Structure) {
-    return PD.clone(SpacefillParams);
+    const params = PD.clone(SpacefillParams);
+    if (structure.isCoarseGrained) {
+        params.sizeFactor.defaultValue = 2;
+    }
+    return params;
 }
 
 export type SpacefillRepresentation = StructureRepresentation<SpacefillParams>

+ 1 - 2
src/mol-repr/structure/visual/element-point.ts

@@ -18,8 +18,7 @@ import { Sphere3D } from '../../../mol-math/geometry';
 
 export const ElementPointParams = {
     ...UnitsPointsParams,
-    // sizeFactor: PD.Numeric(1.0, { min: 0, max: 10, step: 0.01 }),
-    pointSizeAttenuation: PD.Boolean(false),
+    pointSizeAttenuation: PD.Boolean(true),
     ignoreHydrogens: PD.Boolean(false),
     traceOnly: PD.Boolean(false),
 };