Skip to content

Commit ba89d6c

Browse files
Merge pull request #1203 from chrisjonesBSU/remove-rigid
Remove rigid body data structure from `Compound`
2 parents aabbab6 + a4c41c5 commit ba89d6c

File tree

5 files changed

+22
-945
lines changed

5 files changed

+22
-945
lines changed

mbuild/compound.py

Lines changed: 3 additions & 269 deletions
Original file line numberDiff line numberDiff line change
@@ -127,17 +127,10 @@ class Compound(object):
127127
compound is the root of the containment hierarchy.
128128
referrers : set
129129
Other compounds that reference this part with labels.
130-
rigid_id : int, default=None
131-
The ID of the rigid body that this Compound belongs to. Only Particles
132-
(the bottom of the containment hierarchy) can have integer values for
133-
`rigid_id`. Compounds containing rigid particles will always have
134-
`rigid_id == None`. See also `contains_rigid`.
135130
boundingbox : mb.Box
136131
The bounds (xmin, xmax, ymin, ymax, zmin, zmax) of particles in Compound
137132
center
138-
contains_rigid
139133
mass
140-
max_rigid_id
141134
n_particles
142135
n_bonds
143136
root
@@ -183,10 +176,6 @@ def __init__(
183176

184177
self.port_particle = port_particle
185178

186-
self._rigid_id = None
187-
self._contains_rigid = False
188-
self._check_if_contains_rigid_bodies = False
189-
190179
self.element = element
191180
if mass and float(mass) < 0.0:
192181
raise ValueError("Cannot set a Compound mass value less than zero")
@@ -583,230 +572,6 @@ def charge(self, value):
583572
"not at the bottom of the containment hierarchy."
584573
)
585574

586-
@property
587-
def rigid_id(self):
588-
"""Get the rigid_id of the Compound."""
589-
return self._rigid_id
590-
591-
@rigid_id.setter
592-
def rigid_id(self, value):
593-
if self._contains_only_ports():
594-
self._rigid_id = value
595-
for ancestor in self.ancestors():
596-
ancestor._check_if_contains_rigid_bodies = True
597-
else:
598-
raise AttributeError(
599-
"rigid_id is immutable for Compounds that are "
600-
"not at the bottom of the containment hierarchy."
601-
)
602-
603-
@property
604-
def contains_rigid(self):
605-
"""Return True if the Compound contains rigid bodies.
606-
607-
If the Compound contains any particle with a rigid_id != None
608-
then contains_rigid will return True. If the Compound has no
609-
children (i.e. the Compound resides at the bottom of the containment
610-
hierarchy) then contains_rigid will return False.
611-
612-
Returns
613-
-------
614-
bool,
615-
True if the Compound contains any particle with a rigid_id != None
616-
617-
Notes
618-
-----
619-
The private variable '_check_if_contains_rigid_bodies' is used to help
620-
cache the status of 'contains_rigid'.
621-
If '_check_if_contains_rigid_bodies' is False, then the rigid body
622-
containment of the Compound has not changed, and the particle tree is
623-
not traversed, boosting performance.
624-
"""
625-
if self._check_if_contains_rigid_bodies:
626-
self._check_if_contains_rigid_bodies = False
627-
if any(p.rigid_id is not None for p in self._particles()):
628-
self._contains_rigid = True
629-
else:
630-
self._contains_rigid = False
631-
return self._contains_rigid
632-
633-
@property
634-
def max_rigid_id(self):
635-
"""Return the maximum rigid body ID contained in the Compound.
636-
637-
This is usually used by compound.root to determine the maximum
638-
rigid_id in the containment hierarchy.
639-
640-
Returns
641-
-------
642-
int or None
643-
The maximum rigid body ID contained in the Compound. If no
644-
rigid body IDs are found, None is returned
645-
"""
646-
try:
647-
return max(
648-
[p.rigid_id for p in self.particles() if p.rigid_id is not None]
649-
)
650-
except ValueError:
651-
return
652-
653-
def rigid_particles(self, rigid_id=None):
654-
"""Generate all particles in rigid bodies.
655-
656-
If a rigid_id is specified, then this function will only yield particles
657-
with a matching rigid_id.
658-
659-
Parameters
660-
----------
661-
rigid_id : int, optional
662-
Include only particles with this rigid body ID
663-
664-
Yields
665-
------
666-
mb.Compound
667-
The next particle with a rigid_id that is not None, or the next
668-
particle with a matching rigid_id if specified
669-
"""
670-
for particle in self.particles():
671-
if rigid_id is not None:
672-
if particle.rigid_id == rigid_id:
673-
yield particle
674-
else:
675-
if particle.rigid_id is not None:
676-
yield particle
677-
678-
def label_rigid_bodies(self, discrete_bodies=None, rigid_particles=None):
679-
"""Designate which Compounds should be treated as rigid bodies.
680-
681-
If no arguments are provided, this function will treat the compound
682-
as a single rigid body by providing all particles in `self` with the
683-
same rigid_id. If `discrete_bodies` is not None, each instance of
684-
a Compound with a name found in `discrete_bodies` will be treated as a
685-
unique rigid body. If `rigid_particles` is not None, only Particles
686-
(Compounds at the bottom of the containment hierarchy) matching this
687-
name will be considered part of the rigid body.
688-
689-
Parameters
690-
----------
691-
discrete_bodies : str or list of str, optional, default=None
692-
Name(s) of Compound instances to be treated as unique rigid bodies.
693-
Compound instances matching this (these) name(s) will be provided
694-
with unique rigid_ids
695-
rigid_particles : str or list of str, optional, default=None
696-
Name(s) of Compound instances at the bottom of the containment
697-
hierarchy (Particles) to be included in rigid bodies. Only Particles
698-
matching this (these) name(s) will have their rigid_ids altered to
699-
match the rigid body number.
700-
701-
Examples
702-
--------
703-
Creating a rigid benzene
704-
705-
>>> import mbuild as mb
706-
>>> from mbuild.utils.io import get_fn
707-
>>> benzene = mb.load(get_fn('benzene.mol2'))
708-
>>> benzene.label_rigid_bodies()
709-
710-
Creating a semi-rigid benzene, where only the carbons are treated as
711-
a rigid body
712-
713-
>>> import mbuild as mb
714-
>>> from mbuild.utils.io import get_fn
715-
>>> benzene = mb.load(get_fn('benzene.mol2'))
716-
>>> benzene.label_rigid_bodies(rigid_particles='C')
717-
718-
Create a box of rigid benzenes, where each benzene has a unique rigid
719-
body ID.
720-
721-
>>> import mbuild as mb
722-
>>> from mbuild.utils.io import get_fn
723-
>>> benzene = mb.load(get_fn('benzene.mol2'))
724-
>>> benzene.name = 'Benzene'
725-
>>> filled = mb.fill_box(benzene,
726-
... n_compounds=10,
727-
... box=[0, 0, 0, 4, 4, 4])
728-
>>> filled.label_rigid_bodies(distinct_bodies='Benzene')
729-
730-
Create a box of semi-rigid benzenes, where each benzene has a unique
731-
rigid body ID and only the carbon portion is treated as rigid.
732-
733-
>>> import mbuild as mb
734-
>>> from mbuild.utils.io import get_fn
735-
>>> benzene = mb.load(get_fn('benzene.mol2'))
736-
>>> benzene.name = 'Benzene'
737-
>>> filled = mb.fill_box(benzene,
738-
... n_compounds=10,
739-
... box=[0, 0, 0, 4, 4, 4])
740-
>>> filled.label_rigid_bodies(distinct_bodies='Benzene',
741-
... rigid_particles='C')
742-
"""
743-
if discrete_bodies is not None:
744-
if isinstance(discrete_bodies, str):
745-
discrete_bodies = [discrete_bodies]
746-
if rigid_particles is not None:
747-
if isinstance(rigid_particles, str):
748-
rigid_particles = [rigid_particles]
749-
750-
if self.root.max_rigid_id is not None:
751-
rigid_id = self.root.max_rigid_id + 1
752-
warn(
753-
f"{rigid_id} rigid bodies already exist. Incrementing 'rigid_id'"
754-
f"starting from {rigid_id}."
755-
)
756-
else:
757-
rigid_id = 0
758-
759-
for successor in self.successors():
760-
if discrete_bodies and successor.name not in discrete_bodies:
761-
continue
762-
for particle in successor.particles():
763-
if rigid_particles and particle.name not in rigid_particles:
764-
continue
765-
particle.rigid_id = rigid_id
766-
if discrete_bodies:
767-
rigid_id += 1
768-
769-
def unlabel_rigid_bodies(self):
770-
"""Remove all rigid body labels from the Compound."""
771-
self._check_if_contains_rigid_bodies = True
772-
for child in self.children:
773-
child._check_if_contains_rigid_bodies = True
774-
for particle in self.particles():
775-
particle.rigid_id = None
776-
777-
def _increment_rigid_ids(self, increment):
778-
"""Increment the rigid_id of all rigid Particles in a Compound.
779-
780-
Adds `increment` to the rigid_id of all Particles in `self` that
781-
already have an integer rigid_id.
782-
"""
783-
for particle in self.particles():
784-
if particle.rigid_id is not None:
785-
particle.rigid_id += increment
786-
787-
def _reorder_rigid_ids(self):
788-
"""Reorder rigid body IDs ensuring consecutiveness.
789-
790-
Primarily used internally to ensure consecutive rigid_ids following
791-
removal of a Compound.
792-
"""
793-
max_rigid = self.max_rigid_id
794-
unique_rigid_ids = sorted(
795-
set([p.rigid_id for p in self.rigid_particles()])
796-
)
797-
n_unique_rigid = len(unique_rigid_ids)
798-
if max_rigid and n_unique_rigid != max_rigid + 1:
799-
missing_rigid_id = (
800-
unique_rigid_ids[-1] * (unique_rigid_ids[-1] + 1)
801-
) / 2 - sum(unique_rigid_ids)
802-
for successor in self.successors():
803-
if successor.rigid_id is not None:
804-
if successor.rigid_id > missing_rigid_id:
805-
successor.rigid_id -= 1
806-
if self.rigid_id:
807-
if self.rigid_id > missing_rigid_id:
808-
self.rigid_id -= 1
809-
810575
def add(
811576
self,
812577
new_child,
@@ -815,7 +580,6 @@ def add(
815580
replace=False,
816581
inherit_periodicity=None,
817582
inherit_box=False,
818-
reset_rigid_ids=True,
819583
check_box_size=True,
820584
):
821585
"""Add a part to the Compound.
@@ -840,11 +604,6 @@ def add(
840604
Compound being added
841605
inherit_box: bool, optional, default=False
842606
Replace the box of self with the box of the Compound being added
843-
reset_rigid_ids : bool, optional, default=True
844-
If the Compound to be added contains rigid bodies, reset the
845-
rigid_ids such that values remain distinct from rigid_ids
846-
already present in `self`. Can be set to False if attempting
847-
to add Compounds to an existing rigid body.
848607
check_box_size : bool, optional, default=True
849608
Checks and warns if compound box is smaller than its bounding box after adding new_child.
850609
"""
@@ -894,15 +653,10 @@ def add(
894653
self.add(
895654
child,
896655
label=label_list[i],
897-
reset_rigid_ids=reset_rigid_ids,
898656
check_box_size=False,
899657
)
900658
else:
901-
self.add(
902-
child,
903-
reset_rigid_ids=reset_rigid_ids,
904-
check_box_size=False,
905-
)
659+
self.add(child, check_box_size=False)
906660

907661
return
908662

@@ -919,13 +673,6 @@ def add(
919673
)
920674
self._mass = 0
921675

922-
if new_child.contains_rigid or new_child.rigid_id is not None:
923-
if self.contains_rigid and reset_rigid_ids:
924-
new_child._increment_rigid_ids(increment=self.max_rigid_id + 1)
925-
self._check_if_contains_rigid_bodies = True
926-
if self.rigid_id is not None:
927-
self.rigid_id = None
928-
929676
# Create children and labels on the first add operation
930677
if self.children is None:
931678
self.children = list()
@@ -1067,7 +814,7 @@ def _check_if_empty(child):
1067814
for particle in particles_to_remove:
1068815
_check_if_empty(particle)
1069816

1070-
# Fix rigid_ids and remove obj from bondgraph
817+
# Remove obj from bondgraph
1071818
for removed_part in to_remove:
1072819
self._remove(removed_part)
1073820

@@ -1077,11 +824,6 @@ def _check_if_empty(child):
1077824
removed_part.parent.children.remove(removed_part)
1078825
self._remove_references(removed_part)
1079826

1080-
# Check and reorder rigid id
1081-
for _ in particles_to_remove:
1082-
if self.contains_rigid:
1083-
self.root._reorder_rigid_ids()
1084-
1085827
# Remove ghost ports
1086828
self._prune_ghost_ports()
1087829

@@ -1148,10 +890,7 @@ def _prune_ghost_ports(self):
1148890
self._remove_references(port)
1149891

1150892
def _remove(self, removed_part):
1151-
"""Worker for remove(). Fixes rigid IDs and removes bonds."""
1152-
if removed_part.rigid_id is not None:
1153-
for ancestor in removed_part.ancestors():
1154-
ancestor._check_if_contains_rigid_bodies = True
893+
"""Worker for remove(). Removes bonds."""
1155894
if self.root.bond_graph.has_node(removed_part):
1156895
for neighbor in nx.neighbors(
1157896
self.root.bond_graph.copy(), removed_part
@@ -3600,12 +3339,7 @@ def _clone(self, clone_of=None, root_container=None):
36003339
newone._pos = deepcopy(self._pos)
36013340
newone.port_particle = deepcopy(self.port_particle)
36023341
newone._box = deepcopy(self._box)
3603-
newone._check_if_contains_rigid_bodies = deepcopy(
3604-
self._check_if_contains_rigid_bodies
3605-
)
36063342
newone._periodicity = deepcopy(self._periodicity)
3607-
newone._contains_rigid = deepcopy(self._contains_rigid)
3608-
newone._rigid_id = deepcopy(self._rigid_id)
36093343
newone._charge = deepcopy(self._charge)
36103344
newone._mass = deepcopy(self._mass)
36113345
if hasattr(self, "index"):

0 commit comments

Comments
 (0)