Skip to content
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

Conf sampling #757

Merged
merged 3 commits into from
Jul 21, 2024
Merged

Conf sampling #757

merged 3 commits into from
Jul 21, 2024

Conversation

JintaoWu98
Copy link
Member

  • Updated generate_conformers and deduce_new_conformers to output hypothetical_num_comb.
  • Refined determine_torsion_symmetry to handle carbon rotors with three identical attachments, improving symmetry accuracy.
  • Adjusted all references in the codebase to align with these changes, ensuring consistency.

for angle in torsion_scan:
test_angle = (angle + 60) % 360
if any((test_angle - 5) % 360 <= existing_angle <= (test_angle + 5) % 360 for existing_angle in torsion_scan):
for angle in torsion_scan:

Check notice

Code scanning / CodeQL

Nested loops with same variable Note

Nested for statement uses loop variable 'angle' of enclosing for statement.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JintaoWu98, see the CodeQL comment for this line

Copy link

codecov bot commented Jul 15, 2024

Codecov Report

Attention: Patch coverage is 95.83333% with 1 line in your changes missing coverage. Please review.

Project coverage is 73.72%. Comparing base (0d16383) to head (e87e6c8).

Files Patch % Lines
arc/species/conformers.py 95.83% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #757      +/-   ##
==========================================
+ Coverage   73.69%   73.72%   +0.02%     
==========================================
  Files          99       99              
  Lines       27432    27454      +22     
  Branches     5773     5783      +10     
==========================================
+ Hits        20217    20240      +23     
+ Misses       5765     5764       -1     
  Partials     1450     1450              
Flag Coverage Δ
unittests 73.72% <95.83%> (+0.02%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

for angle in torsion_scan:
test_angle = (angle + 60) % 360
if any((test_angle - 5) % 360 <= existing_angle <= (test_angle + 5) % 360 for existing_angle in torsion_scan):
for angle in torsion_scan:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JintaoWu98, see the CodeQL comment for this line

@@ -298,9 +300,9 @@ def generate_conformers(mol_list: Union[List[Molecule], Molecule],
lowest_confs, new_conformers = list(), list()

if not return_all_conformers:
return lowest_confs
return lowest_confs, hypothetical_num_comb
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to return hypothetical_num_comb for the production code, or just for our internal study?

Comment on lines 257 to 259
new_conformers, symmetries, _ = conformers.deduce_new_conformers(
label='', conformers=confs, torsions=torsions, tops=tops, mol_list=[spc1.mol], plot_path=None,
combination_threshold=10, force_field='MMFF94s', max_combination_iterations=25)

Check failure

Code scanning / CodeQL

Mismatch in multiple assignment Error

Left hand side of assignment contains 3 variables, but right hand side is a tuple of length 2.
Copy link
Member

@alongd alongd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, please see some comments

@@ -341,7 +341,7 @@ def deduce_new_conformers(label, conformers, torsions, tops, mol_list, smeared_s
symmetries = dict()
for torsion, top in zip(torsions, tops):
# identify symmetric torsions so we don't bother considering them in the conformational combinations
symmetry = determine_torsion_symmetry(label, top, mol_list, torsion_angles[tuple(torsion)])
symmetry, torsion_angles[tuple(torsion)] = determine_torsion_symmetry(label, top, mol_list, torsion_angles[tuple(torsion)])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Above we do torsion_angles = get_torsion_angles(label, conformers, torsions)
and here we overwrite torsion_angles[tuple(torsion)]. Can you explain this change?

Copy link
Member Author

@JintaoWu98 JintaoWu98 Jul 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To address this comment and the next one, I define a new function add_torsion_symmetry to update the torsion_angle if needed (currently for rotors like CH3 or CF3).

@@ -995,7 +1014,7 @@ def determine_torsion_symmetry(label, top1, mol_list, torsion_scan):
elif not mol.atoms[top[0] - 1].lone_pairs > 0 and not mol.atoms[top[0] - 1].radical_electrons > 0 \
and all([groups[0].is_isomorphic(group, save_order=True) for group in groups[1:]]):
symmetry *= len(groups)
return symmetry
return symmetry, torsion_scan
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please consider again whether we need to return torsion_scan

Copy link
Member Author

@JintaoWu98 JintaoWu98 Jul 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I define a new function add_torsion_symmetry to resolve this issue.

"""
symmetry = 1
check_tops = [1, 1] # flags for checking top1 and top2
mol = mol_list[0]
top2 = [i + 1 for i in range(len(mol.atoms)) if i + 1 not in top1]
for j, top in enumerate([top1, top2]):
# A quick bypass for methyl rotors which are too common:

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please remove the empty line

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, it's fixed.

"""
symmetry = 1
check_tops = [1, 1] # flags for checking top1 and top2
mol = mol_list[0]
top2 = [i + 1 for i in range(len(mol.atoms)) if i + 1 not in top1]
for j, top in enumerate([top1, top2]):
# A quick bypass for methyl rotors which are too common:

# A quick bypass for carbon rotors which has three equal elements with only one bond that is connected to the carbon atom
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo, should be that have instead of which has
Add to the comment: e.g., CH3 or CF3

Copy link
Member Author

@JintaoWu98 JintaoWu98 Jul 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, it's fixed.

arc/species/conformers.py Show resolved Hide resolved
arc/species/conformers.py Show resolved Hide resolved
new_angles = []
for angle in torsion_scan:
test_angle = (angle + 60) % 360
if any((test_angle - 5) % 360 <= existing_angle <= (test_angle + 5) % 360 for existing_angle in torsion_scan):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

test_angle was already defined with % 360 above, can you explain why we test % 360 again here?

Copy link
Member Author

@JintaoWu98 JintaoWu98 Jul 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If angle in line 945 is 299, test_angle is then 359, thus we need it in line 947 since test_angle+5 will be larger than 360.

Copy link
Member

@alongd alongd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! I added a few more comments

@@ -341,6 +341,7 @@ def deduce_new_conformers(label, conformers, torsions, tops, mol_list, smeared_s
symmetries = dict()
for torsion, top in zip(torsions, tops):
# identify symmetric torsions so we don't bother considering them in the conformational combinations
torsion_angles[tuple(torsion)] = add_torsion_symmetry(top, mol_list, torsion_angles[tuple(torsion)])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe the call to add_torsion_symmetry() should be done within get_torsion_angles(), so we generate it correctly in the first place?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, I moved it.

arc/species/conformers.py Show resolved Hide resolved
arc/species/conformers.py Show resolved Hide resolved
Returns:
torsion_scan (list): The angles corresponding to this torsion from all conformers.
"""

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you remove this extra line?

torsion_scan (list): The angles corresponding to this torsion from all conformers.

Returns:
torsion_scan (list): The angles corresponding to this torsion from all conformers.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be different than the description on line 1012, please describe briefly how torsion_scan is modified

# Check if the new angle, adjusted by ±30 degrees, overlaps with any existing angles
if not any((new_angle - 30) % 360 <= existing_angle <= (new_angle + 30) % 360 for existing_angle in torsion_scan + new_angles):
new_angles.append(new_angle)
break
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps the break should be under the if statement? This way, once the first angle is added, we'll stop the loop and be more efficient, we don't need another one at the same location

@@ -998,6 +1002,46 @@ def determine_torsion_symmetry(label, top1, mol_list, torsion_scan):
return symmetry


def add_torsion_symmetry(top1, mol_list, torsion_scan):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest to rename this function. The current name sounds like we take the torsions and add the symmetry number to the (e.g., symmetry=3)
What about add_missing_symmetric_torsion_values() or something similar?

This can prevent extra symmetries when `symmetry` is multiple times of well number
It will detect and handle carbon rotor that has three equal atoms.

Define `add_torsion_symmetry` for adding symmetry to a torsion scan to account for efficient conformer generation.
`determine_torsion_symmetry` can now handle carbon rotor that has three equal atoms only attached to itself.
Copy link
Member

@alongd alongd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good, thanks!

@alongd alongd merged commit 08e7e93 into main Jul 21, 2024
7 checks passed
@alongd alongd deleted the conf_sampling branch July 21, 2024 13:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants