@@ -1153,55 +1153,147 @@ def particles_in_range(
1153
1153
particle_array = np .array (list (self .particles ()))
1154
1154
return particle_array [idxs ]
1155
1155
1156
- def visualize (self , show_ports = False ):
1157
- """Visualize the Compound using nglview.
1156
+ def visualize (self , show_ports = False ,
1157
+ backend = 'py3dmol' , color_scheme = {}): # pragma: no cover
1158
+ """Visualize the Compound using py3dmol (default) or nglview.
1158
1159
1159
1160
Allows for visualization of a Compound within a Jupyter Notebook.
1160
1161
1161
1162
Parameters
1162
1163
----------
1163
1164
show_ports : bool, optional, default=False
1164
1165
Visualize Ports in addition to Particles
1166
+ backend : str, optional, default='py3dmol'
1167
+ Specify the backend package to visualize compounds
1168
+ Currently supported: py3dmol, nglview
1169
+ color_scheme : dict, optional
1170
+ Specify coloring for non-elemental particles
1171
+ keys are strings of the particle names
1172
+ values are strings of the colors
1173
+ i.e. {'_CGBEAD': 'blue'}
1165
1174
1166
1175
"""
1167
- nglview = import_ ( 'nglview' )
1168
- from mdtraj . geometry . sasa import _ATOMIC_RADII
1176
+ viz_pkg = { 'nglview' : self . _visualize_nglview ,
1177
+ 'py3dmol' : self . _visualize_py3dmol }
1169
1178
if run_from_ipython ():
1170
- remove_digits = lambda x : '' .join (i for i in x if not i .isdigit ()
1171
- or i == '_' )
1172
- for particle in self .particles ():
1173
- particle .name = remove_digits (particle .name ).upper ()
1174
- if not particle .name :
1175
- particle .name = 'UNK'
1176
- tmp_dir = tempfile .mkdtemp ()
1177
- self .save (os .path .join (tmp_dir , 'tmp.mol2' ),
1178
- show_ports = show_ports ,
1179
- overwrite = True )
1180
- widget = nglview .show_file (os .path .join (tmp_dir , 'tmp.mol2' ))
1181
- widget .clear ()
1182
- widget .add_ball_and_stick (cylinderOnly = True )
1183
- elements = set ([particle .name for particle in self .particles ()])
1184
- scale = 50.0
1185
- for element in elements :
1186
- try :
1187
- widget .add_ball_and_stick ('_{}' .format (
1188
- element .upper ()), aspect_ratio = _ATOMIC_RADII [element .title ()]** 1.5 * scale )
1189
- except KeyError :
1190
- ids = [str (i ) for i , particle in enumerate (self .particles ())
1191
- if particle .name == element ]
1192
- widget .add_ball_and_stick (
1193
- '@{}' .format (
1194
- ',' .join (ids )),
1195
- aspect_ratio = 0.17 ** 1.5 * scale ,
1196
- color = 'grey' )
1197
- if show_ports :
1198
- widget .add_ball_and_stick ('_VS' ,
1199
- aspect_ratio = 1.0 , color = '#991f00' )
1200
- return widget
1179
+ if backend .lower () in viz_pkg :
1180
+ return viz_pkg [backend .lower ()](show_ports = show_ports ,
1181
+ color_scheme = color_scheme )
1182
+ else :
1183
+ raise RuntimeError ("Unsupported visualization " +
1184
+ "backend ({}). " .format (backend ) +
1185
+ "Currently supported backends include nglview and py3dmol" )
1186
+
1201
1187
else :
1202
1188
raise RuntimeError ('Visualization is only supported in Jupyter '
1203
1189
'Notebooks.' )
1204
1190
1191
+ def _visualize_py3dmol (self , show_ports = False , color_scheme = {}):
1192
+ """Visualize the Compound using py3Dmol.
1193
+
1194
+ Allows for visualization of a Compound within a Jupyter Notebook.
1195
+
1196
+ Parameters
1197
+ ----------
1198
+ show_ports : bool, optional, default=False
1199
+ Visualize Ports in addition to Particles
1200
+ color_scheme : dict, optional
1201
+ Specify coloring for non-elemental particles
1202
+ keys are strings of the particle names
1203
+ values are strings of the colors
1204
+ i.e. {'_CGBEAD': 'blue'}
1205
+
1206
+
1207
+ Returns
1208
+ ------
1209
+ view : py3Dmol.view
1210
+
1211
+ """
1212
+ py3Dmol = import_ ('py3Dmol' )
1213
+ remove_digits = lambda x : '' .join (i for i in x if not i .isdigit ()
1214
+ or i == '_' )
1215
+
1216
+ modified_color_scheme = {}
1217
+ for name , color in color_scheme .items ():
1218
+ # Py3dmol does some element string conversions,
1219
+ # first character is as-is, rest of the characters are lowercase
1220
+ new_name = name [0 ] + name [1 :].lower ()
1221
+ modified_color_scheme [new_name ] = color
1222
+ modified_color_scheme [name ] = color
1223
+
1224
+ for particle in self .particles ():
1225
+ particle .name = remove_digits (particle .name ).upper ()
1226
+ if not particle .name :
1227
+ particle .name = 'UNK'
1228
+ tmp_dir = tempfile .mkdtemp ()
1229
+ self .save (os .path .join (tmp_dir , 'tmp.mol2' ),
1230
+ show_ports = show_ports ,
1231
+ overwrite = True )
1232
+
1233
+ view = py3Dmol .view ()
1234
+ view .addModel (open (os .path .join (tmp_dir , 'tmp.mol2' ), 'r' ).read (),
1235
+ 'mol2' , keepH = True )
1236
+ view .setStyle ({'stick' : {'radius' : 0.2 ,
1237
+ 'color' :'grey' },
1238
+ 'sphere' : {'scale' : 0.3 ,
1239
+ 'colorscheme' :modified_color_scheme }})
1240
+ if show_ports :
1241
+ for p in self .particles (include_ports = True ):
1242
+ if p .port_particle :
1243
+ view .addSphere ({
1244
+ 'center' : {'x' :p .pos [0 ], 'y' :p .pos [1 ], 'z' :p .pos [2 ]},
1245
+ 'radius' :0.4 ,
1246
+ 'color' : '0x991f00' ,
1247
+ 'alpha' : 0.9 })
1248
+ view .zoomTo ()
1249
+ view .show ()
1250
+
1251
+ return view
1252
+
1253
+ def _visualize_nglview (self , show_ports = False , color_scheme = {}):
1254
+ """Visualize the Compound using nglview.
1255
+
1256
+ Allows for visualization of a Compound within a Jupyter Notebook.
1257
+
1258
+ Parameters
1259
+ ----------
1260
+ show_ports : bool, optional, default=False
1261
+ Visualize Ports in addition to Particles
1262
+ """
1263
+ nglview = import_ ('nglview' )
1264
+ from mdtraj .geometry .sasa import _ATOMIC_RADII
1265
+ remove_digits = lambda x : '' .join (i for i in x if not i .isdigit ()
1266
+ or i == '_' )
1267
+ for particle in self .particles ():
1268
+ particle .name = remove_digits (particle .name ).upper ()
1269
+ if not particle .name :
1270
+ particle .name = 'UNK'
1271
+ tmp_dir = tempfile .mkdtemp ()
1272
+ self .save (os .path .join (tmp_dir , 'tmp.mol2' ),
1273
+ show_ports = show_ports ,
1274
+ overwrite = True )
1275
+ widget = nglview .show_file (os .path .join (tmp_dir , 'tmp.mol2' ))
1276
+ widget .clear ()
1277
+ widget .add_ball_and_stick (cylinderOnly = True )
1278
+ elements = set ([particle .name for particle in self .particles ()])
1279
+ scale = 50.0
1280
+ for element in elements :
1281
+ try :
1282
+ widget .add_ball_and_stick ('_{}' .format (
1283
+ element .upper ()), aspect_ratio = _ATOMIC_RADII [element .title ()]** 1.5 * scale )
1284
+ except KeyError :
1285
+ ids = [str (i ) for i , particle in enumerate (self .particles ())
1286
+ if particle .name == element ]
1287
+ widget .add_ball_and_stick (
1288
+ '@{}' .format (
1289
+ ',' .join (ids )),
1290
+ aspect_ratio = 0.17 ** 1.5 * scale ,
1291
+ color = 'grey' )
1292
+ if show_ports :
1293
+ widget .add_ball_and_stick ('_VS' ,
1294
+ aspect_ratio = 1.0 , color = '#991f00' )
1295
+ return widget
1296
+
1205
1297
def update_coordinates (self , filename , update_port_locations = True ):
1206
1298
"""Update the coordinates of this Compound from a file.
1207
1299
@@ -2112,7 +2204,8 @@ def from_parmed(self, structure, coords_only=False):
2112
2204
else :
2113
2205
self .periodicity = np .array ([0. , 0. , 0. ])
2114
2206
2115
- def to_parmed (self , box = None , title = '' , residues = None , show_ports = False ):
2207
+ def to_parmed (self , box = None , title = '' , residues = None , show_ports = False ,
2208
+ infer_residues = False ):
2116
2209
"""Create a ParmEd Structure from a Compound.
2117
2210
2118
2211
Parameters
@@ -2130,6 +2223,8 @@ def to_parmed(self, box=None, title='', residues=None, show_ports=False):
2130
2223
checking against Compound.name.
2131
2224
show_ports : boolean, optional, default=False
2132
2225
Include all port atoms when converting to a `Structure`.
2226
+ infer_residues : bool, optional, default=False
2227
+ Attempt to assign residues based on names of children.
2133
2228
2134
2229
Returns
2135
2230
-------
@@ -2146,6 +2241,9 @@ def to_parmed(self, box=None, title='', residues=None, show_ports=False):
2146
2241
atom_mapping = {} # For creating bonds below
2147
2242
guessed_elements = set ()
2148
2243
2244
+ if not residues and infer_residues :
2245
+ residues = list (set ([child .name for child in self .children ]))
2246
+
2149
2247
if isinstance (residues , string_types ):
2150
2248
residues = [residues ]
2151
2249
if isinstance (residues , (list , set )):
@@ -2288,7 +2386,7 @@ def _iterate_children(self, nodes, edges, names_only=False):
2288
2386
nodes , edges = child ._iterate_children (nodes , edges , names_only = names_only )
2289
2387
return nodes , edges
2290
2388
2291
- def to_intermol (self , molecule_types = None ):
2389
+ def to_intermol (self , molecule_types = None ): # pragma: no cover
2292
2390
"""Create an InterMol system from a Compound.
2293
2391
2294
2392
Parameters
@@ -2341,7 +2439,7 @@ def to_intermol(self, molecule_types=None):
2341
2439
return intermol_system
2342
2440
2343
2441
@staticmethod
2344
- def _add_intermol_molecule_type (intermol_system , parent ):
2442
+ def _add_intermol_molecule_type (intermol_system , parent ): # pragma: no cover
2345
2443
"""Create a molecule type for the parent and add bonds. """
2346
2444
from intermol .moleculetype import MoleculeType
2347
2445
from intermol .forces .bond import Bond as InterMolBond
0 commit comments