Rename SymmetryOperator.canonicalName to instanceId (#1571)

This commit is contained in:
David Sehnal
2025-07-19 07:46:43 +02:00
committed by GitHub
parent b9c0347497
commit 572874f4ae
12 changed files with 18 additions and 18 deletions

View File

@@ -33,7 +33,7 @@ Note that since we don't clearly distinguish between a public and private interf
- Avoid calculating rings for coarse-grained structures
- Fix isosurface compute shader normals when transformation matrix is applied to volume
- Symmetry operator naming for spacegroup symmetry - parenthesize multi-character indices (1_111-1 -> 1_(11)1(-1))
- Add canonical operator name (e.g. ASM-1, ASM-X0-1 for assemblies, 1_555, 1_(11)1(-1) for crystals)
- Add `SymmetryOperator.instanceId` that corresponds to a canonical operator name (e.g. ASM-1, ASM-X0-1 for assemblies, 1_555, 1_(11)1(-1) for crystals)
- [Breaking] `PluginContext.initViewer/initContainer/mount` are now async and have been renamed to include `Async` postfix
- Mol2 Reader
- Fix column count parsing

View File

@@ -201,7 +201,7 @@ export class MVSAnnotation {
/** Return value of field `fieldName` assigned to location `loc`, if any */
getValueForLocation(loc: StructureElement.Location, fieldName: string): string | undefined {
const indexedModel = this.getIndexedModel(loc.unit.model, loc.unit.conformation.operator.canonicalName);
const indexedModel = this.getIndexedModel(loc.unit.model, loc.unit.conformation.operator.instanceId);
const iRow = (indexedModel !== null) ? indexedModel[loc.element] : -1;
return this.getValueForRow(iRow, fieldName);
}

View File

@@ -183,7 +183,7 @@ function makeLayers(ctx: ThemeDataContext, props: MultilayerColorThemeProps, col
function getSubstructureGranularity(parent: Structure, substructure: Structure) {
const parentCounts: { [instance: string]: number } = {};
for (const unit of parent.units) {
const instance = unit.conformation.operator.canonicalName;
const instance = unit.conformation.operator.instanceId;
parentCounts[instance] ??= 0;
parentCounts[instance] += unit.elements.length;
}
@@ -191,7 +191,7 @@ function getSubstructureGranularity(parent: Structure, substructure: Structure)
const childCounts: { [instance: string]: number } = {};
const elementsPerInstance: { [instance: string]: { [invariantId: number]: StructureElement.Set } } = {};
for (const unit of substructure.units) {
const instance = unit.conformation.operator.canonicalName;
const instance = unit.conformation.operator.instanceId;
childCounts[instance] ??= 0;
childCounts[instance] += unit.elements.length;
(elementsPerInstance[instance] ??= {})[unit.invariantId] = unit.elements;

View File

@@ -44,7 +44,7 @@ class AtomRangesCache {
}
get(unit: Unit): AtomRanges {
const instanceId = unit.conformation.operator.canonicalName;
const instanceId = unit.conformation.operator.instanceId;
const key = this.hasOperators ? `${unit.model.id}:${instanceId}` : unit.model.id;
return this.cache[key] ??= getAtomRangesForRows(this.rows, unit.model, instanceId, IndicesAndSortings.get(unit.model));
}

View File

@@ -16,7 +16,7 @@ interface SymmetryOperator {
readonly name: string,
/** Canonical operator name, must follow symmetry instance naming rules (https://molstar.org/mol-view-spec-docs/selectors/#instance_id).
* E.g. 1_555, ASM-X0-5 */
readonly canonicalName: string,
readonly instanceId: string,
readonly assembly?: {
/** pointer to `pdbx_struct_assembly.id` or empty string */
@@ -57,7 +57,7 @@ namespace SymmetryOperator {
export const RotationTranslationEpsilon = 0.005;
export type CreateInfo = { assembly?: SymmetryOperator['assembly'], ncsId?: number, hkl?: Vec3, spgrOp?: number, key?: number, canonicalName?: string }
export type CreateInfo = { assembly?: SymmetryOperator['assembly'], ncsId?: number, hkl?: Vec3, spgrOp?: number, key?: number, instanceId?: string }
export function create(name: string, matrix: Mat4, info?: CreateInfo | SymmetryOperator): SymmetryOperator {
let { assembly, ncsId, hkl, spgrOp, key } = info || {};
const _hkl = hkl ? Vec3.clone(hkl) : Vec3();
@@ -66,12 +66,12 @@ namespace SymmetryOperator {
ncsId = ncsId || -1;
const isIdentity = Mat4.isIdentity(matrix);
const suffix = getSuffix(info, isIdentity);
const canonicalName = info?.canonicalName ?? name;
if (isIdentity) return { name, canonicalName, assembly, matrix, inverse: Mat4.identity(), isIdentity: true, hkl: _hkl, spgrOp, ncsId, suffix, key };
const instanceId = info?.instanceId ?? name;
if (isIdentity) return { name, instanceId, assembly, matrix, inverse: Mat4.identity(), isIdentity: true, hkl: _hkl, spgrOp, ncsId, suffix, key };
if (!Mat4.isRotationAndTranslation(matrix, RotationTranslationEpsilon)) {
console.warn(`Symmetry operator (${name}) should be a composition of rotation and translation.`);
}
return { name, canonicalName, assembly, matrix, inverse: Mat4.invert(Mat4(), matrix), isIdentity: false, hkl: _hkl, spgrOp, key, ncsId, suffix };
return { name, instanceId, assembly, matrix, inverse: Mat4.invert(Mat4(), matrix), isIdentity: false, hkl: _hkl, spgrOp, key, ncsId, suffix };
}
function isSymmetryOperator(x: any): x is SymmetryOperator {

View File

@@ -122,7 +122,7 @@ function getAssemblyOperators(matrices: Matrices, operatorNames: string[][], sta
index++;
const opName = `ASM_${index}`; // Kept mostly for compatibility reasons
const canonicalOpName = SymmetryOperator.getAssemblyOperatorName(op);
operators[operators.length] = SymmetryOperator.create(opName, m, { canonicalName: canonicalOpName, assembly: { id: assemblyId, operId: index, operList: op } });
operators[operators.length] = SymmetryOperator.create(opName, m, { instanceId: canonicalOpName, assembly: { id: assemblyId, operId: index, operList: op } });
}
return operators;

View File

@@ -15,7 +15,7 @@ import { Loci } from './loci';
export interface SchemaItem {
/** Corresponds to SymmetryOperator.name, e.g. 1_555, ASM_5 */
operator_name?: string,
/** Corresponds to SymmetryOperator.canonicalName, e.g. 1_555, ASM-X0-5 */
/** Corresponds to SymmetryOperator.instanceId, e.g. 1_555, ASM-X0-5 */
instance_id?: string,
label_entity_id?: string,
label_asym_id?: string,
@@ -72,7 +72,7 @@ function schemaItemToExpression(item: SchemaItem): Expression {
const chainTests: Expression[] = [];
if (isDefined(item.operator_name)) chainTests.push(eq([core.operatorName(), item.operator_name]));
if (isDefined(item.instance_id)) chainTests.push(eq([core.canonicalOperatorName(), item.instance_id]));
if (isDefined(item.instance_id)) chainTests.push(eq([core.instanceId(), item.instance_id]));
if (isDefined(item.label_asym_id)) chainTests.push(eq([macromolecular.label_asym_id(), item.label_asym_id]));
if (isDefined(item.auth_asym_id)) chainTests.push(eq([macromolecular.auth_asym_id(), item.auth_asym_id]));

View File

@@ -173,7 +173,7 @@ const unit = {
multiChain: p(l => Unit.Traits.is(l.unit.traits, Unit.Trait.MultiChain)),
object_primitive: p(l => l.unit.objectPrimitive),
operator_name: p(l => l.unit.conformation.operator.name),
canonical_operator_name: p(l => l.unit.conformation.operator.canonicalName),
instance_id: p(l => l.unit.conformation.operator.instanceId),
operator_key: p(l => l.unit.conformation.operator.key),
model_index: p(l => l.unit.model.modelNum),
model_label: p(l => l.unit.model.label),

View File

@@ -273,7 +273,7 @@ const atomProperty = {
sourceIndex: atomProp(Type.Num, 'Index of the atom/element in the input file.'),
operatorName: atomProp(Type.Str, 'Name of the symmetry operator applied to this element.'),
canonicalOperatorName: atomProp(Type.Str, 'Canonical name of the symmetry operator applied to this element (aka instance_id).'),
instanceId: atomProp(Type.Str, 'Canonical name of the symmetry operator applied to this element.'),
operatorKey: atomProp(Type.Num, 'Key of the symmetry operator applied to this element.'),
modelIndex: atomProp(Type.Num, 'Index of the model in the input file.'),
modelLabel: atomProp(Type.Str, 'Label/header of the model in the input file.')

View File

@@ -305,7 +305,7 @@ const symbols = [
D(MolScript.structureQuery.atomProperty.core.z, atomProp(StructureProperties.atom.z)),
D(MolScript.structureQuery.atomProperty.core.sourceIndex, atomProp(StructureProperties.atom.sourceIndex)),
D(MolScript.structureQuery.atomProperty.core.operatorName, atomProp(StructureProperties.unit.operator_name)),
D(MolScript.structureQuery.atomProperty.core.canonicalOperatorName, atomProp(StructureProperties.unit.canonical_operator_name)),
D(MolScript.structureQuery.atomProperty.core.instanceId, atomProp(StructureProperties.unit.instance_id)),
D(MolScript.structureQuery.atomProperty.core.operatorKey, atomProp(StructureProperties.unit.operator_key)),
D(MolScript.structureQuery.atomProperty.core.modelIndex, atomProp(StructureProperties.unit.model_index)),
D(MolScript.structureQuery.atomProperty.core.modelLabel, atomProp(StructureProperties.unit.model_label)),

View File

@@ -211,7 +211,7 @@ export const SymbolTable = [
Alias(MolScript.structureQuery.atomProperty.core.z, 'atom.z'),
Alias(MolScript.structureQuery.atomProperty.core.sourceIndex, 'atom.src-index'),
Alias(MolScript.structureQuery.atomProperty.core.operatorName, 'atom.op-name'),
Alias(MolScript.structureQuery.atomProperty.core.canonicalOperatorName, 'atom.canonical-op-name'),
Alias(MolScript.structureQuery.atomProperty.core.instanceId, 'atom.instance-id'),
Alias(MolScript.structureQuery.atomProperty.core.operatorKey, 'atom.op-key'),
Alias(MolScript.structureQuery.atomProperty.core.modelIndex, 'atom.model-index'),
Alias(MolScript.structureQuery.atomProperty.core.modelLabel, 'atom.model-label'),

View File

@@ -239,7 +239,7 @@ function _elementLabel(location: StructureElement.Location, granularity: LabelGr
label.push(`<small>${entry}</small>`); // entry
if (granularity !== 'structure') {
label.push(`<small>Model ${location.unit.model.modelNum}</small>`); // model
label.push(`<small>Instance ${location.unit.conformation.operator.canonicalName}</small>`); // instance
label.push(`<small>Instance ${location.unit.conformation.operator.instanceId}</small>`); // instance
}
}