nglviewer / ngl

WebGL protein viewer
http://nglviewer.org/ngl/
MIT License
667 stars 170 forks source link

Display of neighbouring symmetric units around AU #826

Open DAlvGar opened 3 years ago

DAlvGar commented 3 years ago

Hi all,

I'd like to display the symmetry mates around my assymetric unit. Using the "unitcell" assembly option when the AU is in the corner is not very useful (eg pdbid 1UYD). Using the "supercell" makes the scene too crowded. Is there a way I could display all the mates around my AU like you would in pymol? https://pymolwiki.org/index.php/Symexp

Any tips on how I could implement this behaviour are very welcome!

Thank you!

Daniel

DAlvGar commented 3 years ago

And a related question... how can i select a residue in the symmetry mate? :) I found that the pickingProxy contains some instanceData that could be useful but I couldn't find anything in the selection language regarding the instance.

fredludlow commented 3 years ago

AFAIK, symmetry copies of atoms map to the same underlying atoms (done to improve performance), so selecting just symmetry atoms isn't possible, and building eg. a surface representing a pocket at a symmetry boundary wouldn't be possible. So, depending on what your intended use is there are a couple of options:

1) Write a function which takes a cutoff and makes a new structure component (possibly including the central molecule). You'd need to have a convention for adding symmetry copies as new models, chains, or something else so that you could select them independently... The main disadvantage is probably performance (you're multiplying number of atoms)

1 b) (Do the above in-place, which is awkward as the Structure class isn't really intended to be mutable, though you can get away with it if you're careful)

2) Add another assembly type. You could start with the code in structure-utils.ts and add a 'NEIGHBOUR' type which implements a fixed cutoff. I've only thought about this for a few minutes so there might be a nicer way but maybe start with supercell, figure out the transformed bounding boxes of each instance and cull any which don't border your original 'AU' instance, then if need be do a full atom-distance cutoff to further cull neighbours.

DAlvGar commented 3 years ago

Thanks a lot for the tips @fredludlow! I'll investigate first the 2nd option, interactivity with the symmetry atoms is secondary for now.

DAlvGar commented 3 years ago

Hi @fredludlow , I might need some help with the last part "if need be do a full atom-distance cutoff to further cull neighbours".

I managed to create a new Assembly based on the SUPERCELL one. My idea was to create one assembly part for each potential transform (-1,0,0; 0,-1,0; etc) with an associated chainList for each, indicating what chains to keep. To decide what chains to keep i need to apply the transform and check close atoms with the original AU structure. What is the best way to do that? StructureView doesn't take any transform and the function to compute close atoms is based on the original coordinates as well. Any tips on how to proceed? Thanks a lot.

DAlvGar commented 3 years ago

Hi @fredludlow do you have any tips on how I could implement this in a performant way? Thanks!

fredludlow commented 3 years ago

HI @DAlvGar,

Are you doing this within NGL? If so, you've got a transformation matrix for each part of the assembly, plus access to the original structure coordinates. So the first thing I'd try (just to check that the basic process is correct and transforms are being applied in the correct direction etc.):

Then there's a bunch of optimizations you could consider depending on the performance of the simple method, I've not thought these through completely so it's entirely possible I've made a stupid mistake

  1. Break out of the inner loop early (i.e. once you've found a single close-enough atom there's no need to keep checking), obvious quick win
  2. If you've got the -1 and +1 neighbours for a given transform then if original chain A contacts B in unit '-1' then chain B in the original will contact chain A in unit '+1' - this would halve your run time
  3. You could discard distant neighbours quickly by considering the bounding box. Each structure has a Box3 instance, you could make a copy of this and apply the matrix to it: Box3.applyMatrix4 (which will generate a larger, axis-aligned box, then check whether it intersects the original box using Box3.intersectsBox), you're rotating a box and then finding the axis-aligned box that the rotated box will fit into, hence it being bigger
  4. Similar to 3. above but use Box3getBoundingSphere(), transform the center point by your matrix and then check whether distance < (2*r + threshold). It's not obvious to me whether this or 3. would cull neighbours more effectively
  5. You could do some fancier intersection check for non-axis-aligned boxes - this would be more complex to code and I don't think there's code for this in NGL currently (see here and also googling for non-axis aligned collision detection and/or separating axis theorem) - I'd check the run time of the simpler axis-aligned case first before delving in here

(Or another really neat way that I haven't thought of, @arose, @garyo or others might have some better ideas!)

DAlvGar commented 3 years ago

Thanks @fredludlow !!! i'll try all of these excellent tips. Thanks for your time!