-
Notifications
You must be signed in to change notification settings - Fork 32
Restraints API #1043
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
Merged
Merged
Restraints API #1043
Changes from all commits
Commits
Show all changes
129 commits
Select commit
Hold shift + click to select a range
3e848a6
first pass at abstract classes
IAlibay ef050e5
A start at restraints and forces
IAlibay 420f3e5
Add boresch restraint class
IAlibay 2e2b52e
Fix units
IAlibay 76c5fcf
Fix correction return in kj/mole
IAlibay e9cd918
Add more restraint API bits
IAlibay f1bbd8a
move some things around
IAlibay 4f4d58e
Merge branch 'main' into omm-restraints
IAlibay ac452e9
Some changes
IAlibay 536a76e
Merge branch 'omm-restraints' of github.com:OpenFreeEnergy/openfe int…
IAlibay a19c86c
refactor restraints
IAlibay 20dd1dc
add some angle checks
IAlibay 9ab74a8
only construct with settings
IAlibay 8f2e1e0
Add more checks to utilities
IAlibay 0a480aa
host finding code
IAlibay 7a7be90
fix up weird black wrapping
IAlibay 733f3b3
remove old search file, add more changes to boresch search
IAlibay bbef017
Merge branch 'main' into omm-restraints
IAlibay 96decff
Remove duplicate methods
IAlibay 2d97de8
Apply suggestions from code review
IAlibay 116ba64
Add some more docstring
IAlibay 9ae60da
Add minimized vectors on the collinear checks
IAlibay 3cce308
add host atom finding routine
IAlibay 9171d39
autoformatting
IAlibay 033a1e4
various fixes
IAlibay fe1308e
docstring drive
IAlibay d71b961
Migrate to restraint_utils
IAlibay c914b18
base for restraint settings
IAlibay d24a5a5
Fix up some things
IAlibay 4dd16af
Add restraint settings
IAlibay ec0d01d
Add missing settings imports
IAlibay e19db65
Settings and some tests for them
IAlibay 854d1c6
negative idxs test
IAlibay 9b53cd2
addressing some mypy issues
IAlibay de35e3f
Addressing reviews
IAlibay 7c502c7
Add a todo from last year
IAlibay 786550c
Merge branch 'main' into omm-restraints
IAlibay fc1f316
Update to address review comments
IAlibay 98ff0c8
Last review comment
IAlibay e39cdd3
Various fixes
IAlibay 7906641
Now with DSSP!
IAlibay a52f93b
the big refactor
IAlibay e166c70
fix imports
IAlibay f89a8c8
Fix up secondary structure filtering & add chain selection
IAlibay 2af07c8
Add angle wrapping method
IAlibay aa6f338
Add more tests
IAlibay 3428b39
Add tests for heavy atom getter
IAlibay d2b0a3d
Add extra test
IAlibay 3ef083d
Add some extra tests
IAlibay 7eb8656
Add collinearity tests & better explanation of code
IAlibay e911211
Add tests and fix a few things
IAlibay 0c2f102
Angle variance tests
IAlibay 11aacc8
Some changes
IAlibay dc1d3dd
Try something
IAlibay 04481fa
Fixes nojump issue
IAlibay ca2d5eb
Add atom sorting class
IAlibay 9e76495
Add missing import
IAlibay cb8e928
Merge branch 'omm-restraints' of github.com:OpenFreeEnergy/openfe int…
IAlibay 6b96929
fixes various imports and things
IAlibay a95247e
Oops walked away from my laptop without committing
IAlibay b19ce45
Add the base case for rmsf when there is only one frame
IAlibay e8189d5
fix docs
IAlibay 221cfc8
some settings cleanup
IAlibay d71872d
Fix some tests
IAlibay 318ea20
Merge branch 'main' into omm-restraints
IAlibay 2f2dfa1
Fix up settings typing
IAlibay 1b30231
Some typing improvements
IAlibay 0d3519b
try NDArray typing
IAlibay 01a326e
Some more typing fixes
IAlibay 465f7af
more typing stuff
IAlibay 066702d
Fix up more typing
IAlibay 3b60b62
more typing fixes
IAlibay 7fe0a44
variable length in is_collinear
IAlibay d8ef5a1
this is something we should fix upstream, ignore for now
IAlibay 4aa676e
Fix some typing
IAlibay 4170fde
fix up variable length on list
IAlibay 4110584
try fixing type hinting
IAlibay 4298313
fix rename issue
IAlibay d5f5137
make things into list for typing purposes
IAlibay cee01d4
more type hinting fixes
IAlibay 28393f3
try to get the mixin super to be accepted by mypy
IAlibay a22f0bc
remove type hint in abc to avoid issues
IAlibay 5353dd7
again remove the top level hint for mypy
IAlibay f96da71
fix missing controlling parameter name
IAlibay dacc57a
again remove abc type hint to avoid mypy issues
IAlibay 140f33f
Deal with flat bottom restraint type hint
IAlibay 2dae061
try to avoid Mixin mypy issues
IAlibay 4f1f427
more ignores
IAlibay a73e245
Add another override
IAlibay 3469e52
Merge branch 'main' into omm-restraints
IAlibay bf61539
Merge branch 'main' into omm-restraints
IAlibay a63ebfe
simplify restraint validation and general clean up
jthorton b048a56
Merge branch 'main' into omm-restraints
jthorton 1f028f8
fix type, add harmonic geometry tests
jthorton 6a09412
add flatbottom geometry tests
jthorton 80b1bb3
fix fixture construction, add boresh geometry tests, fix get geometry
jthorton 997ba7f
fix naming, fix heavy atom guest selection, add host and guest tests
jthorton 6945c79
add utils tests using trajectories, don't upload yet
jthorton 02c64e5
add all tests ready for zenodo files
jthorton 1e13f05
use zenodo test data
jthorton 45392ca
Merge branch 'main' into omm-restraints
jthorton 09c03f2
update to use pooch system file
jthorton b34e9a1
Merge branch 'main' into omm-restraints
IAlibay 2b79724
Merge branch 'main' into omm-restraints
hannahbaumann 05f9679
Merge branch 'main' into omm-restraints
IAlibay 6f81295
Update settings.py
IAlibay 3c2c1a7
Merge branch 'main' into omm-restraints
IAlibay e4adb62
Merge branch 'main' into omm-restraints
hannahbaumann ae18dc8
Merge branch 'main' into omm-restraints
hannahbaumann 08de456
fix shortest_path for new networkx API
IAlibay bb5479b
Update settings.py
IAlibay 0edefbe
Update boresch corr test after settings update
hannahbaumann 7c45637
Apply suggestions from code review
IAlibay ce3ce66
Merge branch 'main' into omm-restraints
hannahbaumann 5c80f29
Merge branch 'main' into omm-restraints
hannahbaumann 60dd56e
move to the models vendor
IAlibay dcf3b9c
various mypy fixes
IAlibay 77a5d95
Deal wwith assignment type issues
IAlibay b242693
remove some uncessary type ignores
IAlibay 42e43ba
Just remove typing on base class
IAlibay 2a1a1e0
type of class not class
IAlibay 5433f68
fix models imports
IAlibay dc45bf0
Merge branch 'main' into omm-restraints
IAlibay 9e41472
Add news item
IAlibay 3b4447c
clean up settings
IAlibay 376c1db
Merge branch 'main' into omm-restraints
IAlibay 89a546b
Update openfe/protocols/openmm_rfe/equil_rfe_methods.py
IAlibay 01eb108
isort and black
IAlibay 27447b3
isort + black
IAlibay File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| **Added:** | ||
|
|
||
| * Adds a new internal API for defining alchemical restraints (PR #1043). | ||
|
|
||
| **Changed:** | ||
|
|
||
| * <news item> | ||
|
|
||
| **Deprecated:** | ||
|
|
||
| * <news item> | ||
|
|
||
| **Removed:** | ||
|
|
||
| * <news item> | ||
|
|
||
| **Fixed:** | ||
|
|
||
| * <news item> | ||
|
|
||
| **Security:** | ||
|
|
||
| * <news item> |
Empty file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| from .base import BaseRestraintGeometry, HostGuestRestraintGeometry | ||
| from .boresch import BoreschRestraintGeometry | ||
| from .flatbottom import FlatBottomDistanceGeometry | ||
| from .harmonic import DistanceRestraintGeometry |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,50 @@ | ||
| # This code is part of OpenFE and is licensed under the MIT license. | ||
| # For details, see https://github.com/OpenFreeEnergy/openfe | ||
| """ | ||
| Restraint Geometry classes | ||
|
|
||
| TODO | ||
| ---- | ||
| * Add relevant duecredit entries. | ||
| """ | ||
| import abc | ||
|
|
||
| from pydantic.v1 import BaseModel, validator | ||
|
|
||
|
|
||
| class BaseRestraintGeometry(BaseModel, abc.ABC): | ||
| """ | ||
| A base class for a restraint geometry. | ||
| """ | ||
|
|
||
| class Config: | ||
| arbitrary_types_allowed = True | ||
|
|
||
|
|
||
| class HostGuestRestraintGeometry(BaseRestraintGeometry): | ||
| """ | ||
| An ordered list of guest atoms to restrain. | ||
|
|
||
| Note | ||
| ---- | ||
| The order matters! It will be used to define the underlying | ||
| force. | ||
| """ | ||
|
|
||
| guest_atoms: list[int] | ||
| """ | ||
| An ordered list of host atoms to restrain. | ||
|
|
||
| Note | ||
| ---- | ||
| The order matters! It will be used to define the underlying | ||
| force. | ||
| """ | ||
| host_atoms: list[int] | ||
|
|
||
| @validator("guest_atoms", "host_atoms") | ||
| def positive_idxs(cls, v): | ||
| if v is not None and any([i < 0 for i in v]): | ||
| errmsg = "negative indices passed" | ||
| raise ValueError(errmsg) | ||
| return v |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| from .geometry import BoreschRestraintGeometry, find_boresch_restraint |
291 changes: 291 additions & 0 deletions
291
openfe/protocols/restraint_utils/geometry/boresch/geometry.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,291 @@ | ||
| # This code is part of OpenFE and is licensed under the MIT license. | ||
| # For details, see https://github.com/OpenFreeEnergy/openfe | ||
| """ | ||
| Restraint Geometry classes | ||
|
|
||
| TODO | ||
| ---- | ||
| * Add relevant duecredit entries. | ||
| """ | ||
| from typing import Optional | ||
|
|
||
| import MDAnalysis as mda | ||
| from gufe.vendor.openff.models.types import FloatQuantity | ||
| from MDAnalysis.lib.distances import calc_angles, calc_bonds, calc_dihedrals | ||
| from openfe.protocols.restraint_utils.geometry.base import HostGuestRestraintGeometry | ||
| from openff.units import Quantity, unit | ||
| from rdkit import Chem | ||
|
|
||
| from .guest import find_guest_atom_candidates | ||
| from .host import find_host_anchor, find_host_atom_candidates | ||
|
|
||
|
|
||
| class BoreschRestraintGeometry(HostGuestRestraintGeometry): | ||
| """ | ||
| A class that defines the restraint geometry for a Boresch restraint. | ||
|
|
||
| The restraint is defined by the following: | ||
|
|
||
| H2 G2 | ||
| - - | ||
| - - | ||
| H1 - - H0 -- G0 - - G1 | ||
|
|
||
| Where HX represents the X index of ``host_atoms`` and GX | ||
| the X index of ``guest_atoms``. | ||
| """ | ||
|
|
||
| r_aA0: FloatQuantity["nanometer"] | ||
| """ | ||
| The equilibrium distance between H0 and G0. | ||
| """ | ||
| theta_A0: FloatQuantity["radians"] | ||
| """ | ||
| The equilibrium angle value between H1, H0, and G0. | ||
| """ | ||
| theta_B0: FloatQuantity["radians"] | ||
| """ | ||
| The equilibrium angle value between H0, G0, and G1. | ||
| """ | ||
| phi_A0: FloatQuantity["radians"] | ||
| """ | ||
| The equilibrium dihedral value between H2, H1, H0, and G0. | ||
| """ | ||
| phi_B0: FloatQuantity["radians"] | ||
|
|
||
| """ | ||
| The equilibrium dihedral value between H1, H0, G0, and G1. | ||
| """ | ||
| phi_C0: FloatQuantity["radians"] | ||
|
|
||
| """ | ||
| The equilibrium dihedral value between H0, G0, G1, and G2. | ||
| """ | ||
|
|
||
|
|
||
| def _get_restraint_distances( | ||
| atomgroup: mda.AtomGroup, | ||
| ) -> tuple[Quantity, Quantity, Quantity, Quantity, Quantity, Quantity]: | ||
| """ | ||
| Get the bond, angle, and dihedral distances for an input atomgroup | ||
| defining the six atoms for a Boresch-like restraint. | ||
|
|
||
| The atoms must be in the order of H0, H1, H2, G0, G1, G2. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| atomgroup : mda.AtomGroup | ||
| An AtomGroup defining the restrained atoms in order. | ||
|
|
||
| Returns | ||
| ------- | ||
| bond : openff.units.Quantity | ||
| The H0-G0 bond value. | ||
| angle1 : openff.units.Quantity | ||
| The H1-H0-G0 angle value. | ||
| angle2 : openff.units.Quantity | ||
| The H0-G0-G1 angle value. | ||
| dihed1 : openff.units.Quantity | ||
| The H2-H1-H0-G0 dihedral value. | ||
| dihed2 : openff.units.Quantity | ||
| The H1-H0-G0-G1 dihedral value. | ||
| dihed3 : openff.units.Quantity | ||
| The H0-G0-G1-G2 dihedral value. | ||
| """ | ||
| bond = ( | ||
| calc_bonds( | ||
| atomgroup.atoms[0].position, | ||
| atomgroup.atoms[3].position, | ||
| box=atomgroup.dimensions, | ||
| ) | ||
| * unit.angstroms | ||
| ) | ||
|
|
||
| angles = [] | ||
| for idx_set in [[1, 0, 3], [0, 3, 4]]: | ||
| angle = calc_angles( | ||
| atomgroup.atoms[idx_set[0]].position, | ||
| atomgroup.atoms[idx_set[1]].position, | ||
| atomgroup.atoms[idx_set[2]].position, | ||
| box=atomgroup.dimensions, | ||
| ) | ||
| angles.append(angle * unit.radians) | ||
|
|
||
| dihedrals = [] | ||
| for idx_set in [[2, 1, 0, 3], [1, 0, 3, 4], [0, 3, 4, 5]]: | ||
| dihed = calc_dihedrals( | ||
| atomgroup.atoms[idx_set[0]].position, | ||
| atomgroup.atoms[idx_set[1]].position, | ||
| atomgroup.atoms[idx_set[2]].position, | ||
| atomgroup.atoms[idx_set[3]].position, | ||
| box=atomgroup.dimensions, | ||
| ) | ||
| dihedrals.append(dihed * unit.radians) | ||
|
|
||
| return bond, angles[0], angles[1], dihedrals[0], dihedrals[1], dihedrals[2] | ||
|
|
||
|
|
||
| def find_boresch_restraint( | ||
| universe: mda.Universe, | ||
| guest_rdmol: Chem.Mol, | ||
| guest_idxs: list[int], | ||
| host_idxs: list[int], | ||
| guest_restraint_atoms_idxs: Optional[list[int]] = None, | ||
| host_restraint_atoms_idxs: Optional[list[int]] = None, | ||
| host_selection: str = "all", | ||
| dssp_filter: bool = False, | ||
IAlibay marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| rmsf_cutoff: Quantity = 0.1 * unit.nanometer, | ||
| host_min_distance: Quantity = 1 * unit.nanometer, | ||
| host_max_distance: Quantity = 3 * unit.nanometer, | ||
| angle_force_constant: Quantity = ( | ||
| 83.68 * unit.kilojoule_per_mole / unit.radians**2 | ||
| ), | ||
| temperature: Quantity = 298.15 * unit.kelvin, | ||
| ) -> BoreschRestraintGeometry: | ||
| """ | ||
| Find suitable Boresch-style restraints between a host and guest entity | ||
| based on the approach of Baumann et al. [1] with some modifications. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| universe : mda.Universe | ||
| An MDAnalysis Universe defining the system and its coordinates. | ||
| guest_rdmol : Chem.Mol | ||
| An RDKit Mol for the guest molecule. | ||
| guest_idxs : list[int] | ||
| Indices in the topology for the guest molecule. | ||
| host_idxs : list[int] | ||
| Indices in the topology for the host molecule. | ||
| guest_restraint_atoms_idxs : Optional[list[int]] | ||
| User selected indices of the guest molecule itself (i.e. indexed | ||
| starting a 0 for the guest molecule). This overrides the | ||
| restraint search and a restraint using these indices will | ||
| be returned. Must be defined alongside ``host_restraint_atoms_idxs``. | ||
| host_restraint_atoms_idxs : Optional[list[int]] | ||
| User selected indices of the host molecule itself (i.e. indexed | ||
| starting a 0 for the hosts molecule). This overrides the | ||
| restraint search and a restraint using these indices will | ||
| be returned. Must be defined alongside ``guest_restraint_atoms_idxs``. | ||
| host_selection : str | ||
| An MDAnalysis selection string to sub-select the host atoms. | ||
| dssp_filter : bool | ||
| Whether or not to filter the host atoms by their secondary structure. | ||
| rmsf_cutoff : openff.units.Quantity | ||
| The cutoff value for atom root mean square fluctuation. Atoms with RMSF | ||
| values above this cutoff will be disregarded. | ||
| Must be in units compatible with nanometer. | ||
| host_min_distance : openff.units.Quantity | ||
| The minimum distance between any host atom and the guest G0 atom. | ||
| Must be in units compatible with nanometer. | ||
| host_max_distance : openff.units.Quantity | ||
| The maximum distance between any host atom and the guest G0 atom. | ||
| Must be in units compatible with nanometer. | ||
| angle_force_constant : openff.units.Quantity | ||
| The force constant for the G1-G0-H0 and G0-H0-H1 angles. Must be | ||
| in units compatible with kilojoule / mole / radians ** 2. | ||
| temperature : openff.units.Quantity | ||
| The system temperature in units compatible with Kelvin. | ||
|
|
||
| Returns | ||
| ------- | ||
| BoreschRestraintGeometry | ||
| An object defining the parameters of the Boresch-like restraint. | ||
|
|
||
| References | ||
| ---------- | ||
| [1] Baumann, Hannah M., et al. "Broadening the scope of binding free energy | ||
| calculations using a Separated Topologies approach." (2023). | ||
| """ | ||
| if (guest_restraint_atoms_idxs is not None) and (host_restraint_atoms_idxs is not None): # fmt: skip | ||
| # In this case assume the picked atoms were intentional / | ||
| # representative of the input and go with it | ||
| guest_ag = universe.atoms[guest_idxs] | ||
| guest_atoms = [at.ix for at in guest_ag.atoms[guest_restraint_atoms_idxs]] | ||
| host_ag = universe.atoms[host_idxs] | ||
| host_atoms = [at.ix for at in host_ag.atoms[host_restraint_atoms_idxs]] | ||
|
|
||
| # Set the equilibrium values as those of the final frame | ||
| universe.trajectory[-1] | ||
| atomgroup = universe.atoms[host_atoms + guest_atoms] | ||
| bond, ang1, ang2, dih1, dih2, dih3 = _get_restraint_distances(atomgroup) | ||
|
|
||
| # TODO: add checks to warn if this is a badly picked | ||
IAlibay marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| # set of atoms. | ||
| return BoreschRestraintGeometry( | ||
| host_atoms=host_atoms, | ||
| guest_atoms=guest_atoms, | ||
| r_aA0=bond, | ||
| theta_A0=ang1, | ||
| theta_B0=ang2, | ||
| phi_A0=dih1, | ||
| phi_B0=dih2, | ||
| phi_C0=dih3, | ||
| ) | ||
|
|
||
| if (guest_restraint_atoms_idxs is not None) ^ (host_restraint_atoms_idxs is not None): # fmt: skip | ||
| # This is not an intended outcome, crash out here | ||
| errmsg = ( | ||
| "both ``guest_restraints_atoms_idxs`` and " | ||
| "``host_restraint_atoms_idxs`` " | ||
| "must be set or both must be None. " | ||
| f"Got {guest_restraint_atoms_idxs} and {host_restraint_atoms_idxs}" | ||
| ) | ||
| raise ValueError(errmsg) | ||
|
|
||
| # 1. Fetch the guest anchors | ||
| guest_anchors = find_guest_atom_candidates( | ||
| universe=universe, | ||
| rdmol=guest_rdmol, | ||
| guest_idxs=guest_idxs, | ||
| rmsf_cutoff=rmsf_cutoff, | ||
| ) | ||
|
|
||
| if len(guest_anchors) == 0: | ||
| errmsg = "No suitable ligand atoms found for the restraint." | ||
| raise ValueError(errmsg) | ||
jthorton marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| # 2. We then loop through the guest anchors to find suitable host atoms | ||
| for guest_anchor in guest_anchors: | ||
| # We next fetch the host atom pool | ||
| # Note: return is a set, so need to convert it later on | ||
| host_pool = find_host_atom_candidates( | ||
| universe=universe, | ||
| host_idxs=host_idxs, | ||
| l1_idx=guest_anchor[0], | ||
| host_selection=host_selection, | ||
| dssp_filter=dssp_filter, | ||
| rmsf_cutoff=rmsf_cutoff, | ||
| min_distance=host_min_distance, | ||
| max_distance=host_max_distance, | ||
| ) | ||
|
|
||
| host_anchor = find_host_anchor( | ||
| guest_atoms=universe.atoms[list(guest_anchor)], | ||
| host_atom_pool=universe.atoms[list(host_pool)], | ||
| minimum_distance=0.5 * unit.nanometer, | ||
| angle_force_constant=angle_force_constant, | ||
| temperature=temperature, | ||
| ) | ||
| # continue if it's empty, otherwise stop | ||
| if host_anchor is not None: | ||
| break | ||
|
|
||
| if host_anchor is None: | ||
| errmsg = "No suitable host atoms could be found" | ||
| raise ValueError(errmsg) | ||
|
|
||
| # Set the equilibrium values as those of the final frame | ||
| universe.trajectory[-1] | ||
| atomgroup = universe.atoms[list(host_anchor) + list(guest_anchor)] | ||
| bond, ang1, ang2, dih1, dih2, dih3 = _get_restraint_distances(atomgroup) | ||
|
|
||
| return BoreschRestraintGeometry( | ||
| host_atoms=list(host_anchor), | ||
| guest_atoms=list(guest_anchor), | ||
| r_aA0=bond, | ||
| theta_A0=ang1, | ||
| theta_B0=ang2, | ||
| phi_A0=dih1, | ||
| phi_B0=dih2, | ||
| phi_C0=dih3, | ||
| ) | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.