-
Notifications
You must be signed in to change notification settings - Fork 18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draw bonds computed by ASE #535
Conversation
Codecov ReportAttention:
Additional details and impacted files@@ Coverage Diff @@
## master #535 +/- ##
==========================================
+ Coverage 85.88% 87.12% +1.23%
==========================================
Files 27 27
Lines 4618 4643 +25
==========================================
+ Hits 3966 4045 +79
+ Misses 652 598 -54
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
…ab/aiidalab-widgets-base into feature/draw-atomic-bonds
This item is not yet implemented, but I think we can discuss it first during today's meeting and then decide what to do. The rest is ready, please review. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @yakutovicha Thanks for the work. It's important for the large structure. I made a few change requests.
Also two comments:
- better to add a test for the
_compute_bonds
function. - There is a variable called
uuid
, which is theUnique identifier for the representation.
. At first glance, I mixed it with the AiiDA node uuid. I suggest using another name, e.g.style_id
.
aiidalab_widgets_base/viewers.py
Outdated
|
||
radius = radius / 5 | ||
|
||
cutoff = ase.neighborlist.natural_cutoffs(structure, mult=1.09) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also a comment on why you choose 1.09
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above, while testing on a number of systems in some cases I had bonds where I did not 'want' them in in other cases I did not have bonds and I wanted them. This 1.09 was a good compromise. Feel free to change to a value that produces even better results.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done in 6e547fc
aiidalab_widgets_base/viewers.py
Outdated
half_bond_point = v1 + bond_length * Radius[atom1.symbol] / ( | ||
Radius[atom1.symbol] + Radius[atom2.symbol] | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The math here is wrong. You need both the vector
and the length
to get the middle point.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The naming here is confusing, I guess. This is to place the point where the bond changes colour. We find this point using the proportion of the atomic radius of the atoms.
I renamed the variable to intermediate_point
in d4f0a06
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We find this point using the proportion of the atomic radius of the atoms.
No. The correct math is:
# get the bond_vector from the `neighbor_list` too.
normal = bond_vector/bond_length
r1 = Radius[atom1.symbol]
r2 = Radius[atom2.symbol]
intermediate_point = v1 + r1*normal + (bond_length - r1 - r2)/2*normal
# you can use the short term: intermediate_point = v1 + (bond_length + r1 - r2)/2*normal
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand what is wrong with our approach.
pos1
- location of the first atom
pos2
- location of the second atom
bond_vector
is a vector connecting the first atom and the second atom.
r1
- is the vdW radius of the first atom
r2
- is the vdW radius of the first atom
The intermediate point is a percentage of the vector r1 relative its contribution to the sum of two vdW radii.
bond_vector = pos2 - pos1
atom1_contribution = r1 / (r1 + r2)
atom2_contribution = r2 / (r1 + r2)
intermediate_point = pos1 + bond_vector * atom1_contribution
or
intermediate_point = pos2 - bond_vector * atom2_contribution
atom1_contribution + atom2_contribution = r1 / (r1 + r2) + r2 / (r1 + r2) = (r1 + r2) / (r1 + r2) = 1
I'd appreciate it if you could take a moment to double-check our work before mentioning any concerns about the math. Thank you!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the record, we discussed with @superstar54 and @cpignedoli, where we agreed to cut the bond at the middle point between two atoms
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done in 9ee0f99
self._viewer.set_representations(nglview_params, component=0) | ||
self._viewer.add_unitcell() | ||
self._viewer._add_shape(set(bonds), name="bonds") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Even though it is not obvious in the GUI, but you add new shapes for the bonds, and haven't removed the default bond from the nglview itself, thus the two bonds overlap.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That was a hard nut to crash, but I think I managed in 066aa4a. What do you think @cpignedoli?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After setting different radii for different atoms in this commit, the wrong intermediate_point
becomes obvious. Before, all atoms have the same radius in the ball+stick
model, thus intermediate_point
is (only) correct in that case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure we do not remove default bonds: we never use the ngl representation "ball+stik" we always use spacefill (no bonds) and add bond shapes in the case we use the "fake ball+stik"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
v1 + bond_vector * Radius[atom1.symbol] / ( Radius[atom1.symbol] + Radius[atom2.symbol]
bond_vector
goes from atom1
to atom2
.
we will add to atom1 a vector bond_v*r1/(r1+r2)
and to atom2 a vector, in opposite direction (or from intermediate point to atom2), bond_v*r2/(r1+r2)
in total: bond_v*(r1+r2)/(r1+r2) = bond_v
and the two portions of bonds will have length proportional to the size of the spheres
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks a lot @yakutovicha. I only have some tiny requests in terms of code style, @superstar54 did a more thorough test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You already got two great reviews so I am leaving this one alone. :-)
Co-authored-by: Jusong Yu <[email protected]>
@cpignedoli please comment on where all the numbers are coming from. |
Hi @yakutovicha, Thanks for the update! I am still worried about the test on the bond.
It's executed, but the result isn't verified. I would suggest running this |
e03ec73
to
a5fdfb8
Compare
Done in 9986465 |
d780b5a
to
9986465
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My requests are all addressed, you have my approval. Thanks for the nice work! @yakutovicha
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @yakutovicha, Thanks for the update. Since bond
is an important component in the viewer
, and this PR is supposed to handle large systems with > 1000 bonds. So I added a comment on the performance.
for more information, see https://pre-commit.ci
@unkcpz @yakutovicha I moved the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some small requests should be easy to address. @superstar54
aiidalab_widgets_base/viewers.py
Outdated
bonds = [] | ||
if len(structure) <= 1: | ||
return bonds |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bonds = [] | |
if len(structure) <= 1: | |
return bonds | |
if len(structure) <= 1: | |
return [] |
I think this is safe? No other place uses bonds
, and all conditions return list.
aiidalab_widgets_base/viewers.py
Outdated
@@ -554,9 +555,74 @@ def _observe_all_representations(self, change): | |||
if change["new"]: | |||
self._all_representations[-1].viewer_class = self | |||
|
|||
def _compute_bonds(self, structure, radius=1.0, color="element", povray=False): | |||
"""Create an array of bonds for the structure.""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Create an array"? But the return value is a list.
aiidalab_widgets_base/viewers.py
Outdated
v2 = v1 + bond_vectors * 0.5 | ||
|
||
# Choose the correct way for computing the cylinder. | ||
def povray_cylinder(v1, v2, radius, color): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree using function makes it more clean for me. Can you move it out as staticmethod
instead of nesting it here? If it is only used in the class, change the name to _povray_cylinder
. Same for cylinder
-> _cylinder
and move it out.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I changed the inner function into a private helper function.
Hi @unkcpz, thanks for the review. I made the changes as you suggested. Please have another look. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! Looks all good. The pr is waiting for your approve @superstar54 😄
fixes #48
supercedes #377
In nglview if a system is too big bonds will not be computed.
I bypass the "ball+stick" representation defining it as "spacefill" plus shapes that are computed via ase connectivity matrix.
I would not draw also the atom spheres through shapes otherwise it would be a problem to pick atoms with mouse selection
Despite I tested it also with ~1000 atoms it is meant to work on smaller system sizes.
The image below is outdated, just the "ball+stick" representation is needed to produce the image
To Do: