AlignmentRepresentationPresetProvider.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. /*
  2. * Copyright (c) 2021 RCSB PDB and contributors, licensed under MIT, See LICENSE file for more info.
  3. * @author Joan Segura Mora <joan.segura@rcsb.org>
  4. */
  5. import {
  6. StructureRepresentationPresetProvider
  7. } from "molstar/lib/mol-plugin-state/builder/structure/representation-preset";
  8. import {PluginContext} from "molstar/lib/mol-plugin/context";
  9. import {PluginStateObject} from "molstar/lib/mol-plugin-state/objects";
  10. import {StateObjectRef} from "molstar/lib/mol-state";
  11. import {Structure, StructureElement, StructureProperties as SP, Unit} from "molstar/lib/mol-model/structure";
  12. import {MolScriptBuilder as MS} from "molstar/lib/mol-script/language/builder";
  13. import reprBuilder = StructureRepresentationPresetProvider.reprBuilder;
  14. import uniqid from "uniqid";
  15. import {PLDDTConfidenceColorThemeProvider} from "molstar/lib/extensions/model-archive/quality-assessment/color/plddt";
  16. import {ColorTheme} from "molstar/lib/mol-theme/color";
  17. import {createSelectionExpressions} from "@rcsb/rcsb-molstar/build/src/viewer/helpers/selection";
  18. import {ParamDefinition as PD} from "molstar/lib/mol-util/param-definition";
  19. import {Loci} from "molstar/lib/mol-model/loci";
  20. import {alignAndSuperpose} from "molstar/lib/mol-model/structure/structure/util/superposition";
  21. import {Mat4} from "molstar/lib/mol-math/linear-algebra";
  22. import {SymmetryOperator} from "molstar/lib/mol-math/geometry/symmetry-operator";
  23. import {StateTransforms} from "molstar/lib/mol-plugin-state/transforms";
  24. import {TagDelimiter} from "@rcsb/rcsb-saguaro-app";
  25. let refData: Structure|undefined = undefined;
  26. let refId: {entryId:string;entityId:string;}|undefined = undefined;
  27. export const AlignmentRepresentationPresetProvider = StructureRepresentationPresetProvider<{pdb?:{entryId:string;entityId:string;};},any>({
  28. id: 'alignment-to-reference',
  29. display: {
  30. name: 'Alignemnt to Reference'
  31. },
  32. isApplicable: (structureRef: PluginStateObject.Molecule.Structure, plugin: PluginContext): boolean => true,
  33. params: (structureRef: PluginStateObject.Molecule.Structure | undefined, plugin: PluginContext) => ({
  34. pdb: PD.Value<{entryId:string;entityId:string;}|undefined>(undefined)
  35. }),
  36. apply: async (structureRef: StateObjectRef<PluginStateObject.Molecule.Structure>, params: {pdb?:{entryId:string;entityId:string;};}, plugin: PluginContext) => {
  37. const structureCell = StateObjectRef.resolveAndCheck(plugin.state.data, structureRef);
  38. if(!structureCell) return;
  39. const structure = structureCell.obj!.data;
  40. if(plugin.managers.structure.hierarchy.current.structures.length == 1){
  41. refId = params.pdb
  42. }
  43. if(refId && params.pdb){
  44. await structuralAlignment(plugin, refId, params.pdb, structure);
  45. }
  46. const entryId = params.pdb?.entryId!;
  47. const entityId = params.pdb?.entityId!;
  48. const l = StructureElement.Location.create(structure);
  49. let alignedAsymId;
  50. let alignedOperatorName;
  51. let alignedType;
  52. for(const unit of structure.units) {
  53. StructureElement.Location.set(l, structure, unit, unit.elements[0]);
  54. const alignedEntityId = SP.chain.label_entity_id(l);
  55. if(alignedEntityId == params.pdb?.entityId){
  56. alignedAsymId = SP.chain.label_asym_id(l);
  57. alignedOperatorName = SP.unit.operator_name(l);
  58. alignedType = SP.entity.type(l);
  59. const alignedOperators = SP.unit.pdbx_struct_oper_list_ids(l);
  60. if(alignedType != "polymer")
  61. return;
  62. const comp = await plugin.builders.structure.tryCreateComponentFromExpression(
  63. structureCell,
  64. MS.struct.generator.atomGroups({
  65. 'chain-test': MS.core.logic.and([
  66. MS.core.rel.eq([MS.ammp('label_asym_id'), alignedAsymId]),
  67. MS.core.rel.eq([MS.acp('operatorName'), alignedOperatorName])
  68. ])
  69. }),
  70. uniqid(`${entryId}${TagDelimiter.entity}${entityId}${TagDelimiter.instance}${alignedAsymId}${TagDelimiter.entity}${alignedOperators.join(",")}`),
  71. {
  72. label: `${entryId}${TagDelimiter.entity}${entityId}${TagDelimiter.instance}${alignedAsymId}${TagDelimiter.assembly}${alignedOperators.join(",")}${TagDelimiter.assembly}${alignedType}`
  73. }
  74. );
  75. //TODO This needs to be called after tryCreateComponentFromExpression
  76. const {update, builder} = reprBuilder(plugin, {
  77. ignoreHydrogens: true,
  78. ignoreLight: false,
  79. quality: "auto"
  80. });
  81. builder.buildRepresentation(update, comp, {
  82. color: PLDDTConfidenceColorThemeProvider.isApplicable({ structure }) ? PLDDTConfidenceColorThemeProvider.name as ColorTheme.BuiltIn : "chain-id",
  83. type: "cartoon"
  84. });
  85. await update.commit({ revertOnError: false });
  86. break;
  87. }
  88. }
  89. const expressions = []
  90. const asymObserved: {[key:string]:boolean} = {};
  91. for(const unit of structure.units){
  92. StructureElement.Location.set(l, structure, unit, unit.elements[0]);
  93. const asymId = SP.chain.label_asym_id(l);
  94. const operatorName = SP.unit.operator_name(l);
  95. if(asymId == alignedAsymId && operatorName == alignedOperatorName)
  96. continue;
  97. if(asymObserved[`${asymId}${TagDelimiter.assembly}${operatorName}`])
  98. continue;
  99. asymObserved[`${asymId}${TagDelimiter.assembly}${operatorName}`] = true;
  100. const type = SP.entity.type(l);
  101. if (type == "polymer") {
  102. expressions.push(MS.core.logic.and([
  103. MS.core.rel.eq([MS.ammp('label_asym_id'), asymId]),
  104. MS.core.rel.eq([MS.acp('operatorName'), operatorName])
  105. ]))
  106. }
  107. }
  108. const comp = await plugin.builders.structure.tryCreateComponentFromExpression(
  109. structureCell,
  110. MS.struct.generator.atomGroups({
  111. 'chain-test': MS.core.logic.or(expressions)
  112. }),
  113. uniqid(`${entryId}${TagDelimiter.entity}${entityId}${TagDelimiter.assembly}${alignedType}`),
  114. {
  115. label: `${entryId}${TagDelimiter.entity}${entityId}${TagDelimiter.assembly}${alignedType}`
  116. }
  117. );
  118. const {update, builder} = reprBuilder(plugin, {
  119. ignoreHydrogens: true,
  120. ignoreLight: false,
  121. quality: "auto"
  122. });
  123. builder.buildRepresentation(update, comp, {
  124. color: PLDDTConfidenceColorThemeProvider.isApplicable({ structure }) ? PLDDTConfidenceColorThemeProvider.name as ColorTheme.BuiltIn : "chain-id",
  125. type: "cartoon"
  126. });
  127. await update.commit({ revertOnError: false });
  128. for(const expression of createSelectionExpressions(entryId)){
  129. if(expression.tag == "polymer")
  130. continue;
  131. const comp = await plugin.builders.structure.tryCreateComponentFromExpression(
  132. structureCell,
  133. expression.expression,
  134. uniqid(`${entryId}${TagDelimiter.entity}${entityId}_${expression.tag}`),
  135. {
  136. label: `${entryId}${TagDelimiter.entity}${entityId}-${expression.tag}`
  137. });
  138. //TODO This needs to be called after tryCreateComponentFromExpression
  139. const { update, builder } = reprBuilder(plugin, {
  140. ignoreHydrogens: true,
  141. ignoreLight: false,
  142. quality: "auto"
  143. });
  144. builder.buildRepresentation(update, comp, {
  145. type: expression.type
  146. },expression.tag == "water" ? {
  147. initialState:{
  148. isHidden:true
  149. }
  150. } : undefined);
  151. await update.commit({ revertOnError: false });
  152. }
  153. }
  154. });
  155. async function structuralAlignment(plugin: PluginContext, ref:{entryId:string;entityId:string;}, pdb:{entryId:string;entityId:string;}, structure: Structure): Promise<void> {
  156. if(ref.entryId == pdb.entryId){
  157. refData = structure;
  158. }else{
  159. const pdbData: Structure = structure;
  160. const pdbUnit:Unit|undefined = findFirstEntityUnit(pdbData,pdb.entityId);
  161. const refUnit:Unit|undefined = refData ? findFirstEntityUnit(refData, ref.entityId) : undefined;
  162. if(pdbData && pdbUnit && refData && refUnit){
  163. const refLoci: Loci = Structure.toStructureElementLoci(Structure.create([refUnit]));
  164. const pdbLoci: Loci = Structure.toStructureElementLoci(Structure.create([pdbUnit]));
  165. if(StructureElement.Loci.is(refLoci) && StructureElement.Loci.is(pdbLoci)) {
  166. const pivot = plugin.managers.structure.hierarchy.findStructure(refLoci.structure);
  167. const coordinateSystem = pivot?.transform?.cell.obj?.data.coordinateSystem;
  168. const transforms = alignAndSuperpose([refLoci, pdbLoci]);
  169. const { bTransform } = transforms[0];
  170. await transform(plugin, plugin.helpers.substructureParent.get(pdbData)!, bTransform, coordinateSystem);
  171. }
  172. }
  173. }
  174. }
  175. function findFirstEntityUnit(structure: Structure, entityId: string): Unit|undefined {
  176. const l = StructureElement.Location.create(structure);
  177. for(const unit of structure.units) {
  178. StructureElement.Location.set(l, structure, unit, unit.elements[0]);
  179. const unitEntityId = SP.chain.label_entity_id(l);
  180. if (unitEntityId == entityId) {
  181. return unit;
  182. }
  183. }
  184. }
  185. const SuperpositionTag = 'SuperpositionTransform';
  186. async function transform(plugin:PluginContext, s: StateObjectRef<PluginStateObject.Molecule.Structure>, matrix: Mat4, coordinateSystem?: SymmetryOperator): Promise<void>{
  187. const r = StateObjectRef.resolveAndCheck(plugin.state.data, s);
  188. if (!r) return;
  189. const o = plugin.state.data.selectQ(q => q.byRef(r.transform.ref).subtree().withTransformer(StateTransforms.Model.TransformStructureConformation))[0];
  190. const transform = coordinateSystem && !Mat4.isIdentity(coordinateSystem.matrix)
  191. ? Mat4.mul(Mat4(), coordinateSystem.matrix, matrix)
  192. : matrix;
  193. const params = {
  194. transform: {
  195. name: 'matrix' as const,
  196. params: { data: transform, transpose: false }
  197. }
  198. };
  199. const b = o
  200. ? plugin.state.data.build().to(o).update(params)
  201. : plugin.state.data.build().to(s)
  202. .insert(StateTransforms.Model.TransformStructureConformation, params, { tags: SuperpositionTag });
  203. await plugin.runTask(plugin.state.data.updateTree(b));
  204. }