1#A* -------------------------------------------------------------------
2#B* This file contains source code for the PyMOL computer program
3#C* Copyright (c) Schrodinger, LLC.
4#D* -------------------------------------------------------------------
5#E* It is unlawful to modify or remove this copyright notice.
6#F* -------------------------------------------------------------------
7#G* Please see the accompanying LICENSE file for further information.
8#H* -------------------------------------------------------------------
9#I* Additional authors of this source file include:
10#-*
11#-*
12#-*
13#Z* -------------------------------------------------------------------
14
15from __future__ import print_function
16
17from .constants import CURRENT_STATE, ALL_STATES
18
19if True:
20
21    import time
22    from . import selector
23    import pymol
24    cmd = __import__("sys").modules["pymol.cmd"]
25    from .cmd import _cmd,lock,unlock,Shortcut, \
26          _feedback,fb_module,fb_mask,is_list, \
27          DEFAULT_ERROR, DEFAULT_SUCCESS, _raising, is_ok, is_error
28
29    def auto_measure(_self=cmd):
30        lst = _self.get_names("selections")
31        if "pk1" in lst:
32            if "pk2" in lst:
33                if "pk3" in lst:
34                    if "pk4" in lst:
35                        _self.dihedral()
36                    else:
37                        _self.angle()
38                else:
39                    _self.distance()
40        _self.unpick()
41
42    def get_volume_field(objName, state=1, copy=1, _self=cmd):
43        '''
44DESCRIPTION
45
46    EXPERIMENTAL AND SUBJECT TO CHANGE - DO NOT USE
47    API only. Get the raw data of a map or volume object.
48
49ARGUMENTS
50
51    objName = str: object name
52
53    state = int: state index {default: 1}
54
55    copy = 0/1: {default: 1} WARNING: only use copy=0 if you know what you're
56    doing. copy=0 will return a numpy array which is a wrapper of the internal
57    memory. If the internal memory gets freed or reallocated, this wrapper
58    will become invalid.
59        '''
60        with _self.lockcm:
61            r = _self._cmd.get_volume_field(_self._COb, objName, int(state) - 1, int(copy))
62        return r
63
64    def get_volume_histogram(objName, bins=64, range=None, _self=cmd):
65        '''
66DESCRIPTION
67
68    API ONLY.  Get min, max, mean, stdev and histogram of a map or volume
69    object as a list of length bins + 4.
70        '''
71        with _self.lockcm:
72            r = _self._cmd.get_volume_histogram(_self._COb,objName,
73                    int(bins), range or (0., 0.))
74        return r
75
76    def get_unused_name(prefix="tmp",alwaysnumber=1,_self=cmd):
77        with _self.lockcm:
78            r = _cmd.get_unused_name(_self._COb, prefix, alwaysnumber)
79        return r
80
81    def get_modal_draw(_self=cmd,quiet=1):
82        with _self.lockcm:
83            r = _cmd.get_modal_draw(_self._COb)
84        return r
85
86    def get_drag_object_name(_self=cmd):
87        with _self.lockcm:
88            r = _cmd.get_drag_object_name(_self._COb)
89        return r
90
91    def get_object_matrix(object,state=1, incl_ttt=1, _self=cmd):
92        '''
93DESCRIPTION
94
95    "get_object_matrix" is an unsupported command that may have
96    something to do with querying the transformation matrices
97    associated with an object
98        '''
99        object = str(object)
100        with _self.lockcm:
101            r = _cmd.get_object_matrix(_self._COb,str(object), int(state)-1, int(incl_ttt))
102        return r
103
104    def get_object_ttt(object, quiet=1, _self=cmd):
105        '''
106DESCRIPTION
107
108    "get_object_ttt" is an unsupported command
109        '''
110        quiet = int(quiet)
111        with _self.lockcm:
112            r = _cmd.get_object_ttt(_self._COb, str(object), -1, quiet)
113        if not quiet:
114            if r is None:
115                print('TTT is None')
116            else:
117                for i in range(4):
118                    if i == 3:
119                        print('TTT ---------------------------+---------')
120                    print('TTT %8.2f %8.2f %8.2f | %8.2f' % tuple(r[i * 4:i * 4 + 4]))
121        return r
122
123    def get_object_settings(object, state=ALL_STATES, quiet=1, _self=cmd):
124        '''
125DESCRIPTION
126
127    "get_object_settings" is an unsupported command
128        '''
129        with _self.lockcm:
130            r = _cmd.get_object_settings(_self._COb, str(object), int(state) - 1)
131        return r
132
133    def get_object_list(selection="(all)", quiet=1, _self=cmd):
134        '''
135
136DESCRIPTION
137
138    "get_object_list" is an unsupported command that may have
139    something to do with querying the objects covered by a selection.
140    '''
141        selection = selector.process(selection)
142        with _self.lockcm:
143            r = _cmd.get_object_list(_self._COb,str(selection))
144            if not quiet:
145                if(is_list(r)):
146                    print(" get_object_list: ",str(r))
147        return r
148
149    def get_symmetry(selection="(all)",state=CURRENT_STATE,quiet=1,_self=cmd):
150        '''
151DESCRIPTION
152
153    "get_symmetry" can be used to obtain the crystal
154    and spacegroup parameters for a molecule or map.
155
156USAGE
157
158    get_symmetry object-name-or-selection
159
160PYMOL API
161
162    cmd.get_symmetry(string selection, int state, int quiet)
163
164
165        '''
166        selection = selector.process(selection)
167        with _self.lockcm:
168            r = _cmd.get_symmetry(_self._COb,str(selection),int(state) - 1)
169        if not quiet:
170            if r:
171                        print(" get_symmetry: A     = %7.3f B    = %7.3f C     = %7.3f"%tuple(r[0:3]))
172                        print(" get_symmetry: Alpha = %7.3f Beta = %7.3f Gamma = %7.3f"%tuple(r[3:6]))
173                        print(" get_symmetry: SpaceGroup = %s"%r[6])
174            else:
175                        print(" get_symmetry: No symmetry defined.")
176        return r
177
178    def get_title(object, state, quiet=1, _self=cmd):
179        '''
180DESCRIPTION
181
182    "get_title" retrieves a text string to the state of a particular
183    object which will be displayed when the state is active.
184
185USAGE
186
187    set_title object, state
188
189PYMOL API
190
191    cmd.set_title(string object, int state, string text)
192
193    '''
194        with _self.lockcm:
195            r = _cmd.get_title(_self._COb,str(object),int(state)-1)
196        if not quiet:
197                if r is not None:
198                    print(" get_title: %s"%r)
199        return r
200
201
202    def angle(name=None, selection1="(pk1)", selection2="(pk2)",
203	      selection3="(pk3)", mode=None, label=1, reset=0,
204	      zoom=0, state=ALL_STATES, quiet=1, _self=cmd,
205              state1=-3, state2=-3, state3=-3):
206
207        '''
208DESCRIPTION
209
210    "angle" shows the angle formed between any three atoms.
211
212USAGE
213
214    angle [ name [, selection1 [, selection2 [, selection3 ]]]]
215
216NOTES
217
218    "angle" alone will show the angle angle formed by selections (pk1),
219    (pk2), (pk3) which can be set using the "PkAt" mouse action
220    (typically, Ctrl-middle-click)
221
222PYMOL API
223
224    cmd.angle(string name, string selection1, string selection2,
225              string selection3)
226
227SEE ALSO
228
229    distance, dihedral
230    '''
231
232        r = DEFAULT_SUCCESS
233        if selection1=="(pk1)":
234            if "pk1" not in _self.get_names('selections'):
235                if _feedback(fb_module.cmd,fb_mask.errors,_self):
236                    print("cmd-Error: The 'pk1' selection is undefined.")
237                r = DEFAULT_ERROR
238        if selection2=="(pk2)":
239            if "pk2" not in _self.get_names('selections'):
240                if _feedback(fb_module.cmd,fb_mask.errors,_self):
241                    print("cmd-Error: The 'pk2' selection is undefined.")
242                r = DEFAULT_ERROR
243        if selection3=="(pk3)":
244            if "pk3" not in _self.get_names('selections'):
245                if _feedback(fb_module.cmd,fb_mask.errors,_self):
246                    print("cmd-Error: The 'pk3' selection is undefined.")
247                r = DEFAULT_ERROR
248        if is_ok(r):
249            r = DEFAULT_ERROR
250
251            # if unlabeled, then get next name in series
252
253            if name is not None:
254                nam=name
255            else:
256                try:
257                    _self.lock(_self)
258                    cnt = _self.get_setting_int("dist_counter") + 1
259                    r = _self.set("dist_counter", cnt)
260                    nam = "angle%02.0f" % cnt
261                finally:
262                    _self.unlock(r,_self)
263
264            # defaults
265            if mode is None:
266                mode = 0
267            # preprocess selections
268            selection1 = selector.process(selection1)
269            selection2 = selector.process(selection2)
270            selection3 = selector.process(selection3)
271            # now do the deed
272            try:
273                _self.lock(_self)
274                if selection2!="same":
275                    selection2 = "("+selection2+")"
276                if selection3!="same":
277                    selection3 = "("+selection3+")"
278                r = _cmd.angle(_self._COb,str(nam),"("+str(selection1)+")",
279                               str(selection2),
280                               str(selection3),
281                               int(mode),int(label),int(reset),
282                               int(zoom),int(quiet),int(state)-1,
283                               int(state1)-1, int(state2)-1, int(state3)-1)
284            finally:
285                _self.unlock(r,_self)
286        if _raising(r,_self): raise pymol.CmdException
287        return r
288
289    def dihedral(name=None, selection1="(pk1)", selection2="(pk2)",
290		 selection3="(pk3)", selection4="(pk4)", mode=None,
291		 label=1, reset=0, zoom=0, state=ALL_STATES, quiet=1, _self=cmd):
292        '''
293DESCRIPTION
294
295    "dihedral" shows dihedral angles formed between any four atoms.
296
297USAGE
298
299    dihedral [ name [, selection1 [, selection2 [, selection3 [, selection4 ]]]]]
300
301
302NOTES
303
304    "dihedral" alone will show the dihedral angle formed by selections
305    (pk1), (pk2), (pk3), and (pk4), which can be set using the "PkAt"
306    mouse action (typically, Ctrl-middle-click)
307
308PYMOL API
309
310    cmd.dihedral(string name, string selection1, string selection2,
311                 string selection3, string selection4)
312
313SEE ALSO
314
315    distance, angle
316    '''
317        r = DEFAULT_SUCCESS
318        if selection1=="(pk1)":
319            if "pk1" not in _self.get_names('selections'):
320                if _feedback(fb_module.cmd,fb_mask.errors,_self):
321                    print("cmd-Error: The 'pk1' selection is undefined.")
322                r = DEFAULT_ERROR
323        if selection2=="(pk2)":
324            if "pk2" not in _self.get_names('selections'):
325                if _feedback(fb_module.cmd,fb_mask.errors,_self):
326                    print("cmd-Error: The 'pk2' selection is undefined.")
327                r = DEFAULT_ERROR
328        if selection3=="(pk3)":
329            if "pk3" not in _self.get_names('selections'):
330                if _feedback(fb_module.cmd,fb_mask.errors,_self):
331                    print("cmd-Error: The 'pk3' selection is undefined.")
332                r = DEFAULT_ERROR
333        if selection3=="(pk4)":
334            if "pk4" not in _self.get_names('selections'):
335                if _feedback(fb_module.cmd,fb_mask.errors,_self):
336                    print("cmd-Error: The 'pk4' selection is undefined.")
337                r = DEFAULT_ERROR
338        if is_ok(r):
339            r = DEFAULT_ERROR
340            # if unlabeled, then get next name in series
341
342            if name is not None:
343                nam=name
344            else:
345                try:
346                    _self.lock(_self)
347                    cnt = _self.get_setting_int("dist_counter") + 1
348                    r = _self.set("dist_counter", cnt)
349                    nam = "dihedral%02.0f" % cnt
350                finally:
351                    _self.unlock(r,_self)
352
353            # defaults
354            if mode is None:
355                mode = 0
356            # preprocess selections
357            selection1 = selector.process(selection1)
358            selection2 = selector.process(selection2)
359            selection3 = selector.process(selection3)
360            selection4 = selector.process(selection4)
361            # now do the deed
362            try:
363                _self.lock(_self)
364                if selection2!="same":
365                    selection2 = "("+selection2+")"
366                if selection3!="same":
367                    selection3 = "("+selection3+")"
368                if selection4!="same":
369                    selection4 = "("+selection4+")"
370                r = _cmd.dihedral(_self._COb,str(nam),"("+str(selection1)+")",
371                                  str(selection2),
372                                  str(selection3),
373                                  str(selection4),
374                                  int(mode),int(label),
375                                  int(reset),int(zoom),
376                                  int(quiet),int(state)-1)
377            finally:
378                _self.unlock(r,_self)
379        if _raising(r,_self): raise pymol.CmdException
380        return r
381
382    def distance(name=None, selection1="(pk1)", selection2="(pk2)",
383		 cutoff=None, mode=None, zoom=0, width=None, length=None,
384                 gap=None, label=1, quiet=1, reset=0, state=ALL_STATES,
385                 state1=-3, state2=-3, _self=cmd):
386
387        '''
388DESCRIPTION
389
390    "distance" creates a new distance object between two selections.
391
392USAGE
393
394    distance [name [, selection1 [, selection2 [, cutoff [, mode ]]]]]
395
396ARGUMENTS
397
398    name = string: name of the distance object to create
399
400    selection1 = string: first atom selection
401
402    selection2 = string: second atom selection
403
404    cutoff = float: longest distance to show
405
406    mode = 0: all interatomic distances
407
408    mode = 1: only bond distances
409
410    mode = 2: only show polar contact distances
411
412    mode = 3: like mode=0, but use distance_exclusion setting
413
414    mode = 4: distance between centroids (does not support
415              dynamic_measures; new in PyMOL 1.8.2)
416
417    mode = 5: pi-pi and pi-cation interactions
418
419    mode = 6: pi-pi interactions
420
421    mode = 7: pi-cation interactions
422
423    mode = 8: like mode=3, but cutoff is the ratio between
424              distance and sum of VDW radii
425
426    state = int: object state to create the measurement object in
427    and to get coordinates from {default: 0 (all states)}
428
429    state1, state2 = int: overrule 'state' argument to measure distances
430    between different states {default: use state}
431
432EXAMPLES
433
434    distance mydist, 14/CA, 29/CA
435
436    distance hbonds, all, all, 3.2, mode=2
437
438NOTES
439
440    The distance wizard makes measuring distances easier than using
441    the "dist" command for real-time operations.
442
443    "dist" alone will show distances between selections (pk1) and (pk1),
444    which can be set using the PkAt mouse action (usually CTRL-middle-click).
445
446PYMOL API
447
448    cmd.distance(string name, string selection1, string selection2,
449                 string cutoff, string mode )
450
451    '''
452        # handle unnamed distance
453        r = DEFAULT_SUCCESS
454        if name is not None:
455            if len(name):
456                if name[0]=='(' or ' ' in name or '/' in name: # we're one argument off...
457                    if cutoff is not None:
458                        mode = cutoff
459                    if selection2!="(pk2)":
460                        cutoff = selection2
461                    if selection1!="(pk1)":
462                        selection2 = selection1
463                    selection1=name
464                    name = None
465
466        if selection1=="(pk1)":
467            if "pk1" not in _self.get_names('selections'):
468                if _feedback(fb_module.cmd,fb_mask.errors,_self):
469                    print("cmd-Error: The 'pk1' selection is undefined.")
470                r = DEFAULT_ERROR
471        if selection2=="(pk2)":
472            if "pk2" not in _self.get_names('selections'):
473                if _feedback(fb_module.cmd,fb_mask.errors,_self):
474                    print("cmd-Error: The 'pk2' selection is undefined.")
475                r = DEFAULT_ERROR
476        if is_ok(r):
477            r = DEFAULT_ERROR
478
479            # if unlabeled, then get next name in series
480
481            if name is not None:
482                nam=name
483            else:
484                try:
485                    _self.lock(_self)
486                    cnt = _self.get_setting_int("dist_counter") + 1
487                    r = _self.set("dist_counter", cnt)
488                    nam = "dist%02.0f" % cnt
489                finally:
490                    _self.unlock(r,_self)
491
492            # defaults
493            if mode is None:
494                mode = 0
495            if cutoff is None:
496                cutoff = -1.0
497            # preprocess selections
498            selection1 = selector.process(selection1)
499            selection2 = selector.process(selection2)
500            # now do the deed
501            try:
502                _self.lock(_self)
503                if selection2!="same":
504                    selection2 = "("+selection2+")"
505                r = _cmd.dist(_self._COb,str(nam),"("+str(selection1)+")",
506                              str(selection2),int(mode),float(cutoff),
507                              int(label),int(quiet),int(reset),
508                              int(state)-1,int(zoom),
509                              int(state1)-1, int(state2)-1)
510                if width is not None:
511                    _self.set("dash_width",width,nam)
512                if length is not None:
513                    _self.set("dash_length",length,nam)
514                if gap is not None:
515                    _self.set("dash_gap",gap,nam)
516            finally:
517                _self.unlock(r,_self)
518        if (r<0.0) and (not quiet):
519            # a negative value is an warning signal from PyMOL...
520            r = DEFAULT_ERROR
521        if _raising(r,_self): raise pymol.CmdException
522        return r
523
524    # LEGACY support for cmd.dist
525    dist = distance
526
527    def pi_interactions(name="",
528                        selection1="all",
529                        selection2="same",
530                        state=ALL_STATES,
531                        state1=-3,
532                        state2=-3,
533                        quiet=1,
534                        reset=0,
535                        _self=cmd):
536        '''
537DESCRIPTION
538
539    Find pi-pi and pi-cation interactions.
540
541    Identical to cmd.distance(..., mode=5, label=0)
542
543SEE ALSO
544
545    distance
546        '''
547        raise pymol.IncentiveOnlyException()
548
549    def get_povray(_self=cmd):
550        '''
551DESCRIPTION
552
553    "get_povray" returns a tuple corresponding to strings for a PovRay
554    input file.
555
556PYMOL API
557
558    cmd.get_povray()
559
560        '''
561        with _self.lockcm:
562            r = _cmd.get_povray(_self._COb)
563        return r
564
565    def get_idtf(quiet=1, _self=cmd):
566        '''
567DESCRIPTION
568
569    "get_idft" is under development, but should eventually return an
570    idtf file containing multiple objects and scenes.
571
572PYMOL API
573
574    cmd.idtf()
575
576        '''
577        with _self.lockcm:
578            r = _cmd.get_idtf(_self._COb)
579
580        if not quiet:
581            fov = _self.get_setting_float("field_of_view")
582            dist = _self.get_view()[11]
583            print(" 3Daac=%3.1f, 3Droll=0, 3Dc2c=0 0 1, 3Droo=%1.2f, 3Dcoo=0 0 %1.2f" % (fov, -dist, dist))
584
585        return r
586
587    def get_mtl_obj(_self=cmd):
588        '''
589DESCRIPTION
590
591    NOTE: this is an incomplete and unsupported feature.
592
593    "get_mtl_obj" returns a tuple containing mtl and obj input files
594    for use with Maya.
595
596PYMOL API
597
598    cmd.get_obj_mtl()
599
600        '''
601        with _self.lockcm:
602            r = _cmd.get_mtl_obj(_self._COb)
603        return r
604
605    def get_version(quiet=1,_self=cmd):
606        '''
607DESCRIPTION
608
609    "get_version" returns a tuple of length six containing text,
610    floating point, and integer representations of the current PyMOL
611    version number, build date as unix timestamp, GIT SHA and SVN
612    code revision so far available.
613
614PYMOL API
615
616    cmd.get_version(int quiet)
617
618        '''
619        # get_version doesn't need the _COb and doesn't require a lock
620        r = _cmd.get_version()
621        if True:
622            quiet = int(quiet)
623            if quiet < 1 and _feedback(fb_module.cmd, fb_mask.results, _self):
624                import re
625                p = pymol.get_version_message(r)
626                print(re.sub(r'^', ' ', p, re.M))
627                if quiet < 0:
628                    if r[3]:
629                        print(' build date:', time.strftime('%c %Z', time.localtime(r[3])))
630                    if r[4]:
631                        print(' git sha:', r[4])
632        return r
633
634    def get_vrml(version=2,_self=cmd):
635        '''
636DESCRIPTION
637
638    "get_vrml" returns a VRML2 string representing the content
639    currently displayed.
640
641PYMOL API
642
643    cmd.get_vrml()
644
645        '''
646        with _self.lockcm:
647            r = _cmd.get_vrml(_self._COb,int(version))
648        return r
649
650    def get_collada(version=2, _self=cmd):
651        '''
652DESCRIPTION
653
654    "get_collada" returns a COLLADA string representing the content
655    currently displayed.
656
657PYMOL API
658
659    cmd.get_collada()
660
661        '''
662        with _self.lockcm:
663            r = _cmd.get_collada(_self._COb,int(version))
664        return r
665
666    def get_gltf(filename, quiet=1, _self=cmd):
667        '''
668DESCRIPTION
669
670    "get_gltf" saves a gltf file representing the content
671    currently displayed.
672
673PYMOL API
674
675    cmd.get_gltf()
676
677        '''
678
679        from distutils.spawn import find_executable
680        exe = find_executable('collada2gltf')
681        if exe is None:
682            raise pymol.CmdException('could not find collada2gltf')
683
684        COLLADA_VERSION = 2
685        r = _self.get_collada(COLLADA_VERSION)
686
687        # write collada file
688        with open(filename, 'w') as handle:
689            handle.write(r)
690
691        import subprocess
692
693        result = subprocess.call([exe, '-i', filename, '-o', filename])
694                # convert collada file to gltf by using collada2gltf binary
695
696        if not quiet:
697            if result == 0:
698                print(' Save: wrote "' + filename + '".')
699            else:
700                print(' Save-Error: no file written')
701
702        return result
703
704    def count_states(selection="(all)", quiet=1, _self=cmd):
705        '''
706DESCRIPTION
707
708    "count_states" returns the number of states in the selection.
709
710USAGE
711
712    count_states
713
714PYMOL API
715
716    cmd.count_states(string selection)
717
718SEE ALSO
719
720    frame
721    '''
722        # preprocess selection
723        selection = selector.process(selection)
724        #
725        with _self.lockcm:
726            r = _cmd.count_states(_self._COb,selection)
727        if not quiet:
728                print(" cmd.count_states: %d states."%r)
729        return r
730
731    def get_movie_length(quiet=1, images=-1, _self=cmd):
732        '''
733DESCRIPTION
734
735    "get_movie_length" returns the number of frames explicitly defined
736    in the movie, not including molecular states.
737
738PYMOL API
739
740    cmd.count_frames()
741
742SEE ALSO
743
744    frame, count_states, count_frames
745    '''
746        with _self.lockcm:
747            r = _cmd.get_movie_length(_self._COb)
748            if r<0:
749                if images==0:
750                    r = 0
751                elif images<0:
752                    r = -r
753            if images == 1:
754                if r>0:
755                    r = 0
756            if r>=0 and not quiet:
757                print(" cmd.get_movie_length: %d frames"%r)
758        return r
759
760    def count_frames(quiet=1, _self=cmd):
761        '''
762DESCRIPTION
763
764    "count_frames" returns the number of frames defined for the PyMOL
765    movie.
766
767USAGE
768
769    count_frames
770
771PYMOL API
772
773    cmd.count_frames()
774
775SEE ALSO
776
777    frame, count_states
778    '''
779        with _self.lockcm:
780            r = _cmd.count_frames(_self._COb)
781            if not quiet: print(" cmd.count_frames: %d frames"%r)
782        return r
783
784    def overlap(selection1, selection2, state1=1, state2=1, adjust=0.0, quiet=1, _self=cmd):
785        '''
786DESCRIPTION
787
788    "overlap" is an unsupported command that sums up
789    [(VDWi + VDWj) - distance_ij]/2 between pairs of
790    selected atoms.
791
792PYMOL API
793
794    cmd.overlap(string selection1, string selection2 [, int state1=1, int state2=1, float adjust=0.0])
795
796NOTES
797
798    It does not compute the volume overlap,
799    selections with more atoms will have a larger
800    sum.
801
802    '''
803
804        # preprocess selections
805        selection1 = selector.process(selection1)
806        selection2 = selector.process(selection2)
807        #
808        with _self.lockcm:
809            r = _cmd.overlap(_self._COb,str(selection1),str(selection2),
810                                  int(state1)-1,int(state2)-1,
811                                  float(adjust))
812            if not quiet: print(" cmd.overlap: %5.3f Angstroms."%r)
813        return r
814
815    def get_movie_locked(_self=cmd):
816        with _self.lockcm:
817            r = _cmd.get_movie_locked(_self._COb)
818        return r
819
820    def get_object_color_index(name,_self=cmd):
821        name = str(name)
822        with _self.lockcm:
823            r = _cmd.get_object_color_index(_self._COb,name)
824        return r
825
826    def get_color_tuple(name,mode=0,_self=cmd):
827        '''Get the RGB color tuple for a color (range 0.0 to 1.0)
828        :param name: color name or index
829        :param mode: don't use (mode 4 returns negative R for special colors)
830        '''
831        name=str(name)
832        mod = int(mode)
833        if mode in (1, 2):
834            print(' Warning: use get_color_indices instead of get_color_tuple(mode={})'.format(mode))
835        elif mode == 3:
836            print(' Warning: use get_color_index instead of get_color_tuple(mode=3)')
837        with _self.lockcm:
838            r = _cmd.get_color(_self._COb,name,mode)
839            if r is None:
840                if _feedback(fb_module.cmd,fb_mask.errors,_self):
841                    print("cmd-Error: Unknown color '%s'."%name)
842        return r
843
844    def get_color_indices(all=0,_self=cmd):
845        with _self.lockcm:
846            if all:
847                r = _cmd.get_color(_self._COb,'',2)
848            else:
849                r = _cmd.get_color(_self._COb,'',1)
850        return r
851
852    def get_color_index(color,_self=cmd):
853        with _self.lockcm:
854            r = _cmd.get_color(_self._COb,str(color),3)
855        return r
856
857    def get_renderer(quiet=1, _self=cmd):
858        '''
859DESCRIPTION
860
861    Prints OpenGL renderer information.
862        '''
863        with _self.lockcm:
864            r = _cmd.get_renderer(_self._COb)
865
866        if not int(quiet):
867            print(" OpenGL graphics engine:")
868            print("  GL_VENDOR:   ", r[0])
869            print("  GL_RENDERER: ", r[1])
870            print("  GL_VERSION:  ", r[2])
871
872        return r
873
874    def get_phipsi(selection="(name CA)",state=CURRENT_STATE,_self=cmd):
875        # preprocess selections
876        selection = selector.process(selection)
877        #
878        with _self.lockcm:
879            r = _cmd.get_phipsi(_self._COb,"("+str(selection)+")",int(state)-1)
880        return r
881
882    def get_atom_coords(selection, state=ALL_STATES,quiet=1,_self=cmd):
883        '''
884DESCRIPTION
885
886    "get_atom_coords" returns the 3D coordinates of a single atom.
887
888    '''
889        # low performance way to get coords for a single atom
890        selection = selector.process(selection)
891        with _self.lockcm:
892            r = _cmd.get_atom_coords(_self._COb,str(selection),int(state)-1,int(quiet))
893        if not quiet:
894            for a in r:
895                print(" cmd.get_atom_coords: [%8.3f,%8.3f,%8.3f]"%(a))
896        return r
897
898    def get_coords(selection='all', state=1, quiet=1, _self=cmd):
899        '''
900DESCRIPTION
901
902    API only. Get selection coordinates as numpy array.
903
904ARGUMENTS
905
906    selection = str: atom selection {default: all}
907
908    state = int: state index or all states if state=0 {default: 1}
909        '''
910        selection = selector.process(selection)
911        with _self.lockcm:
912            r = _cmd.get_coords(_self._COb, selection, int(state) - 1)
913            return r
914
915    def get_coordset(name, state=1, copy=1, quiet=1, _self=cmd):
916        '''
917DESCRIPTION
918
919    API only. Get object coordinates as numpy array.
920
921ARGUMENTS
922
923    selection = str: atom selection {default: all}
924
925    state = int: state index {default: 1}
926
927    copy = 0/1: {default: 1} WARNING: only use copy=0 if you know what you're
928    doing. copy=0 will return a numpy array which is a wrapper of the internal
929    coordinate set memory. If the internal memory gets freed or reallocated,
930    this wrapper will become invalid.
931        '''
932        with _self.lockcm:
933            r = _cmd.get_coordset(_self._COb, name, int(state) - 1, int(copy))
934            return r
935
936
937    def get_position(quiet=1, _self=cmd):
938        '''
939DESCRIPTION
940
941    "get_position" returns the 3D coordinates of the center of the
942    viewer window.
943
944    '''
945
946        with _self.lockcm:
947            r = _cmd.get_position(_self._COb)
948        if not quiet:
949            print(" cmd.get_position: [%8.3f,%8.3f,%8.3f]"%(r[0],r[1],r[2]))
950        return r
951
952    def get_distance(atom1="pk1",atom2="pk2",state=CURRENT_STATE,quiet=1,_self=cmd):
953        '''
954DESCRIPTION
955
956    "get_distance" returns the distance between two atoms.  By default, the
957    coordinates used are from the current state, however an alternate
958    state identifier can be provided.
959
960USAGE
961
962    get_distance atom1, atom2, [,state ]
963
964EXAMPLES
965
966    get_distance 4/n,4/c
967    get_distance 4/n,4/c,state=4
968
969PYMOL API
970
971    cmd.get_distance(atom1="pk1",atom2="pk2",state=-1)
972
973        '''
974        # preprocess selections
975        atom1 = selector.process(atom1)
976        atom2 = selector.process(atom2)
977        #
978        with _self.lockcm:
979            r = _cmd.get_distance(_self._COb,str(atom1),str(atom2),int(state)-1)
980        if not quiet:
981            print(" cmd.get_distance: %5.3f Angstroms."%r)
982        return r
983
984    def get_angle(atom1="pk1",atom2="pk2",atom3="pk3",state=CURRENT_STATE,quiet=1,_self=cmd):
985        '''
986DESCRIPTION
987
988    "get_angle" returns the angle between three atoms.  By default, the
989    coordinates used are from the current state, however an alternate
990    state identifier can be provided.
991
992USAGE
993
994    get_angle atom1, atom2, atom3, [,state ]
995
996EXAMPLES
997
998    get_angle 4/n,4/c,4/ca
999    get_angle 4/n,4/c,4/ca,state=4
1000
1001PYMOL API
1002
1003    cmd.get_angle(atom1="pk1",atom2="pk2",atom3="pk3",state=-1)
1004
1005        '''
1006        # preprocess selections
1007        atom1 = selector.process(atom1)
1008        atom2 = selector.process(atom2)
1009        atom3 = selector.process(atom3)
1010        #
1011        with _self.lockcm:
1012            r = _cmd.get_angle(_self._COb,str(atom1),str(atom2),str(atom3),int(state)-1)
1013        if not quiet:
1014            print(" cmd.get_angle: %5.3f degrees."%r)
1015        return r
1016
1017    def get_dihedral(atom1="pk1",atom2="pk2",atom3="pk3",atom4="pk4",state=CURRENT_STATE,quiet=1,_self=cmd):
1018        '''
1019DESCRIPTION
1020
1021    "get_dihedral" returns the dihedral angle between four atoms.  By
1022    default, the coordinates used are from the current state, however
1023    an alternate state identifier can be provided.
1024
1025    By convention, positive dihedral angles are right-handed
1026    (looking down the atom2-atom3 axis).
1027
1028USAGE
1029
1030    get_dihedral atom1, atom2, atom3, atom4 [,state ]
1031
1032EXAMPLES
1033
1034    get_dihedral 4/n,4/c,4/ca,4/cb
1035    get_dihedral 4/n,4/c,4/ca,4/cb,state=4
1036
1037PYMOL API
1038
1039    cmd.get_dihedral(atom1,atom2,atom3,atom4,state=-1)
1040
1041        '''
1042        # preprocess selections
1043        atom1 = selector.process(atom1)
1044        atom2 = selector.process(atom2)
1045        atom3 = selector.process(atom3)
1046        atom4 = selector.process(atom4)
1047        #
1048        with _self.lockcm:
1049            r = _cmd.get_dihe(_self._COb,str(atom1),str(atom2),str(atom3),str(atom4),int(state)-1)
1050        if not quiet:
1051            print(" cmd.get_dihedral: %5.3f degrees."%r)
1052        return r
1053
1054    def get_model(selection="(all)",state=1,ref='',ref_state=0,_self=cmd):
1055        '''
1056DESCRIPTION
1057
1058    "get_model" returns a ChemPy "Indexed" format model from a selection.
1059
1060PYMOL API
1061
1062    cmd.get_model(string selection [,int state] )
1063
1064        '''
1065        # preprocess selection
1066        selection = selector.process(selection)
1067        #
1068        with _self.lockcm:
1069            r = _cmd.get_model(_self._COb,"("+str(selection)+")",int(state)-1,str(ref),int(ref_state)-1)
1070        return r
1071
1072    def get_bonds(selection="(all)", state=CURRENT_STATE, _self=cmd):
1073        '''
1074DESCRIPTION
1075
1076    Get a list of (atm1, atm2, order) tuples for bonds with coordinates in
1077    the given state (same logic as cmd.get_model()).
1078
1079    WARNING: atm1/atm2 are 0-based indices! They enumerate the atoms in the
1080    selection and do not necessarily correspond to the "index" atom property.
1081
1082    To get a atm1/atm2 to index mapping, you can do:
1083    >>> stored.indices = []
1084    >>> cmd.iterate_state(state, selection, "stored.indices.append(index)")
1085
1086SEE ALSO
1087
1088    cmd.get_model().bond
1089        '''
1090        with _self.lockcm:
1091            return _cmd.get_bonds(_self._COb, selection, int(state) - 1)
1092
1093    def get_area(selection="(all)",state=1,load_b=0,quiet=1,_self=cmd):
1094        '''
1095DESCRIPTION
1096
1097    Get the surface area of an selection. Depends on the "dot_solvent"
1098    setting. With "dot_solvent=off" (default) it calculates the solvent
1099    excluded surface area, else the surface accessible surface.
1100
1101USAGE
1102
1103    get_area [ selection [, state [, load_b ]]]
1104
1105ARGUMENTS
1106
1107    load_b = bool: store per-atom surface area in b-factors {default: 0}
1108
1109SEE ALSO
1110
1111    "dot_solvent" setting, "dots" representation (show dots)
1112        '''
1113        # preprocess selection
1114        selection = selector.process(selection)
1115        #
1116        with _self.lockcm:
1117            r = _cmd.get_area(_self._COb,"("+str(selection)+")",int(state)-1,int(load_b))
1118        if not quiet:
1119            print(" cmd.get_area: %5.3f Angstroms^2."%r)
1120        return r
1121
1122    def get_chains(selection="(all)",state=ALL_STATES,quiet=1,_self=cmd):
1123        '''
1124DESCRIPTION
1125
1126    Print the list of chain identifiers in the given selection.
1127
1128USAGE
1129
1130    get_chains [ selection [, state ]]
1131
1132ARGUMENTS
1133
1134    selection = str: atom selection {default: all}
1135
1136    state = int: CURRENTLY IGNORED
1137        '''
1138        # preprocess selection
1139        selection = selector.process(selection)
1140        #
1141        with _self.lockcm:
1142            r = _cmd.get_chains(_self._COb,"("+str(selection)+")",int(state)-1)
1143        if r is None:
1144            return []
1145        if not quiet:
1146            print(" cmd.get_chains: ",str(r))
1147        return r
1148
1149
1150    def get_names(type='public_objects',enabled_only=0,selection="",_self=cmd):
1151        '''
1152DESCRIPTION
1153
1154    "get_names" returns a list of object and/or selection names.
1155
1156PYMOL API
1157
1158    cmd.get_names( [string: "objects"|"selections"|"all"|"public_objects"|"public_selections"] )
1159
1160NOTES
1161
1162    The default behavior is to return only object names.
1163
1164SEE ALSO
1165
1166    get_type, count_atoms, count_states
1167        '''
1168        selection = selector.process(selection)
1169        # this needs to be replaced with a flag & masking scheme...
1170        if type=='objects':
1171            mode = 1
1172        elif type=='selections':
1173            mode = 2
1174        elif type=='all':
1175            mode = 0
1176        elif type=='public':
1177            mode = 3
1178        elif type=='public_objects':
1179            mode = 4
1180        elif type=='public_selections':
1181            mode = 5
1182        elif type=='public_nongroup_objects':
1183            mode = 6
1184        elif type=='public_group_objects':
1185            mode = 7
1186        elif type=='nongroup_objects':
1187            mode = 8
1188        elif type=='group_objects':
1189            mode = 9
1190        else:
1191            raise pymol.CmdException("unknown type: '{}'".format(type))
1192        with _self.lockcm:
1193            r = _cmd.get_names(_self._COb,int(mode),int(enabled_only),str(selection))
1194        return r
1195
1196    def get_legal_name(name,_self=cmd):
1197        with _self.lockcm:
1198            r = _cmd.get_legal_name(_self._COb,str(name))
1199        return r
1200
1201    def get_type(name,quiet=1,_self=cmd):
1202        '''
1203DESCRIPTION
1204
1205    "get_type" returns a string describing the named object or
1206     selection or the string "nonexistent" if the name in unknown.
1207
1208PYMOL API
1209
1210    cmd.get_type(string object-name)
1211
1212NOTES
1213
1214    Possible return values are
1215
1216    "object:molecule"
1217    "object:map"
1218    "object:mesh"
1219    "object:slice"
1220    "object:surface"
1221    "object:measurement"
1222    "object:cgo"
1223    "object:group"
1224    "object:volume"
1225    "selection"
1226
1227SEE ALSO
1228
1229    get_names
1230        '''
1231        with _self.lockcm:
1232            r = _cmd.get_type(_self._COb,str(name))
1233        if not quiet:
1234            print(r)
1235        return r
1236
1237    def id_atom(selection,mode=0,quiet=1,_self=cmd):
1238        '''
1239DESCRIPTION
1240
1241    "id_atom" returns the original source id of a single atom, or
1242    raises and exception if the atom does not exist or if the selection
1243    corresponds to multiple atoms.
1244
1245PYMOL API
1246
1247    list = cmd.id_atom(string selection)
1248        '''
1249        r = DEFAULT_ERROR
1250        selection = str(selection)
1251        l = identify(selection, mode)
1252        ll = len(l)
1253        if not ll:
1254            if _feedback(fb_module.cmd,fb_mask.errors,_self):
1255                print("cmd-Error: atom %s not found by id_atom." % selection)
1256            if _self._raising(_self=_self): raise pymol.CmdException
1257        elif ll>1:
1258            if _feedback(fb_module.cmd,fb_mask.errors,_self):
1259                print("cmd-Error: multiple atoms %s found by id_atom." % selection)
1260            if _self._raising(_self=_self): raise pymol.CmdException
1261        else:
1262            r = l[0]
1263            if not quiet:
1264                if mode:
1265                    print(" cmd.id_atom: (%s and id %d)"%(r[0],r[1]))
1266                else:
1267                    print(" cmd.id_atom: (id %d)"%r)
1268        if _raising(r,_self): raise pymol.CmdException
1269        return r
1270
1271    def identify(selection="(all)",mode=0,quiet=1,_self=cmd):
1272        '''
1273DESCRIPTION
1274
1275    "identify" returns a list of atom IDs corresponding to the ID code
1276    of atoms in the selection.
1277
1278PYMOL API
1279
1280    list = cmd.identify(string selection="(all)",int mode=0)
1281
1282NOTES
1283
1284    mode 0: only return a list of identifiers (default)
1285    mode 1: return a list of tuples of the object name and the identifier
1286
1287        '''
1288        # preprocess selection
1289        selection = selector.process(selection)
1290        #
1291        with _self.lockcm:
1292            r = _cmd.identify(_self._COb,"("+str(selection)+")",int(mode)) # 0 = default mode
1293        if is_list(r):
1294            if len(r):
1295                if not quiet:
1296                    if mode:
1297                        for a in r:
1298                            print(" cmd.identify: (%s and id %d)"%(a[0],a[1]))
1299                    else:
1300                        for a in r:
1301                            print(" cmd.identify: (id %d)"%a)
1302        return r
1303
1304    def index(selection="(all)",quiet=1,_self=cmd):
1305        '''
1306DESCRIPTION
1307
1308    "index" returns a list of tuples corresponding to the
1309    object name and index of the atoms in the selection.
1310
1311PYMOL API
1312
1313    list = cmd.index(string selection="(all)")
1314
1315NOTE
1316
1317  Atom indices are fragile and will change as atoms are added
1318  or deleted.  Whenever possible, use integral atom identifiers
1319  instead of indices.
1320
1321        '''
1322        # preprocess selection
1323        selection = selector.process(selection)
1324        #
1325        with _self.lockcm:
1326            r = _cmd.index(_self._COb,"("+str(selection)+")",0) # 0 = default mode
1327        if not quiet:
1328            if is_list(r):
1329                if len(r):
1330                    for a in r:
1331                        print(" cmd.index: (%s`%d)"%(a[0],a[1]))
1332        return r
1333
1334    def find_pairs(selection1,selection2,state1=1,state2=1,cutoff=3.5,mode=0,angle=45,_self=cmd):
1335        '''
1336DESCRIPTION
1337
1338    API only function. Returns a list of atom pairs. Atoms are represented as
1339    (model,index) tuples.
1340
1341    Can be restricted to hydrogen-bonding-like contacts. WARNING: Only checks
1342    atom orientation, not atom type (so would hydrogen bond between carbons for
1343    example), so make sure to provide appropriate atom selections.
1344
1345ARGUMENTS
1346
1347    selection1, selection2 = string: atom selections
1348
1349    state1, state2 = integer: state-index (only positive values allowed) {default: 1}
1350
1351    cutoff = float: distance cutoff {default: 3.5}
1352
1353    mode = integer: if mode=1, do coarse hydrogen bonding assessment {default: 0}
1354
1355    angle = float: hydrogen bond angle cutoff, only if mode=1 {default: 45.0}
1356
1357NOTE
1358
1359    Although this does a similar job like "distance", it uses a completely
1360    different routine and the "mode" argument has different meanings!
1361        '''
1362        # preprocess selection
1363        selection1 = selector.process(selection1)
1364        selection2 = selector.process(selection2)
1365        #
1366        with _self.lockcm:
1367            r = _cmd.find_pairs(_self._COb,"("+str(selection1)+")",
1368                                      "("+str(selection2)+")",
1369                                      int(state1)-1,int(state2)-1,
1370                                      int(mode),float(cutoff),float(angle))
1371        return r
1372
1373    def get_extent(selection="(all)",state=ALL_STATES,quiet=1,_self=cmd):
1374        '''
1375DESCRIPTION
1376
1377    "get_extent" returns the minimum and maximum XYZ coordinates of a
1378    selection as an array:
1379     [ [ min-X , min-Y , min-Z ],[ max-X, max-Y , max-Z ]]
1380
1381PYMOL API
1382
1383    cmd.get_extent(string selection="(all)", state=0 )
1384
1385        '''
1386        # preprocess selection
1387        selection = selector.process(selection)
1388        #
1389        with _self.lockcm:
1390            r = _cmd.get_min_max(_self._COb,str(selection),int(state)-1)
1391        if not quiet:
1392            print(" cmd.extent: min: [%8.3f,%8.3f,%8.3f]"%(r[0][0],r[0][1],r[0][2]))
1393            print(" cmd.extent: max: [%8.3f,%8.3f,%8.3f]"%(r[1][0],r[1][1],r[1][2]))
1394        return r
1395
1396    def phi_psi(selection="(byres pk1)", quiet=1, _self=cmd):
1397        '''
1398DESCRIPTION
1399
1400    "phi_psi" return the phi and psi angles for a protein atom
1401    selection.
1402
1403USAGE
1404        '''
1405
1406        r = cmd.get_phipsi(selection)
1407        if not quiet and isinstance(r, dict):
1408            for a in sorted(r):
1409                    _self.iterate("(%s`%d)"%a,"print(' %-9s "+
1410                                ("( %6.1f, %6.1f )"%r[a])+
1411                                "'%(resn+'-'+resi+':'))")
1412        return r
1413
1414
1415    def count_atoms(selection="(all)",quiet=1,state=ALL_STATES,domain='',_self=cmd):
1416        '''
1417DESCRIPTION
1418
1419    "count_atoms" returns a count of atoms in a selection.
1420
1421USAGE
1422
1423    count_atoms [ selection [, quiet [, state ]]]
1424        '''
1425        r = DEFAULT_ERROR
1426        # preprocess selection
1427        selection = selector.process(selection)
1428        #
1429        try:
1430            _self.lock(_self)
1431            r = _cmd.select(_self._COb,"_count_tmp","("+str(selection)+")",1,int(state)-1,str(domain))
1432            _cmd.delete(_self._COb,"_count_tmp")
1433        finally:
1434            _self.unlock(r,_self)
1435        if not quiet: print(" count_atoms: %d atoms"%r)
1436        if _raising(r,_self): raise pymol.CmdException
1437        return r
1438
1439    def count_discrete(selection, quiet=1, _self=cmd):
1440        '''
1441DESCRIPTION
1442
1443    Count the number of discrete objects in selection.
1444
1445USAGE
1446
1447    count_discrete selection
1448        '''
1449        with _self.lockcm:
1450            r = _cmd.count_discrete(_self._COb, str(selection))
1451            if not int(quiet):
1452                print(' count_discrete: %d' % r)
1453            return r
1454
1455    def get_names_of_type(type,public=1,_self=cmd):
1456        """
1457DESCRIPTION
1458
1459    "get_names_of_type" will return a list of names for the given type.
1460
1461        """
1462        obj_type = "public_objects" if public==1 else "objects"
1463        types = []
1464        mix = []
1465        obj = None
1466        try:
1467            obj = _self.get_names(obj_type)
1468        except:
1469            pass
1470
1471        if obj:
1472            try:
1473                types = list(map(_self.get_type,obj))
1474                mix = list(zip(obj,types))
1475            except:
1476                pass
1477        lst = []
1478        for a in mix:
1479            if a[1]==type:
1480                lst.append(a[0])
1481        return lst
1482
1483
1484    def get_raw_alignment(name='',active_only=0,_self=cmd):
1485        '''
1486DESCRIPTION
1487
1488    "get_raw_alignment" returns a list of lists of (object,index)
1489    tuples containing the raw per-atom alignment relationships
1490
1491PYMOL API
1492
1493    cmd.get_raw_alignment(name)
1494
1495        '''
1496        with _self.lockcm:
1497            r = _cmd.get_raw_alignment(_self._COb,str(name),int(active_only))
1498        return r
1499
1500    def get_object_state(name, _self=cmd):
1501        '''
1502DESCRIPTION
1503
1504    Returns the effective object state.
1505        '''
1506        states = _self.count_states('%' + name)
1507        if states < 2 and _self.get_setting_boolean('static_singletons'):
1508            return 1
1509        if _self.get_setting_int('all_states', name):
1510            return 0
1511        state = _self.get_setting_int('state', name)
1512        if state > states:
1513            raise pymol.CmdException('Invalid state %d for object %s' % (state, name))
1514        return state
1515
1516    def get_selection_state(selection, _self=cmd):
1517        '''
1518DESCRIPTION
1519
1520    Returns the effective object state for all objects in given selection.
1521    Raises exception if objects are in different states.
1522        '''
1523        state_set = set(map(_self.get_object_state,
1524            _self.get_object_list('(' + selection + ')')))
1525        if len(state_set) != 1:
1526            if len(state_set) == 0:
1527                return 1
1528            raise pymol.CmdException('Selection spans multiple object states')
1529        return state_set.pop()
1530
1531    def centerofmass(selection='(all)', state=CURRENT_STATE, quiet=1, _self=cmd):
1532        '''
1533DESCRIPTION
1534
1535    Calculates the center of mass. Considers atom mass and occupancy.
1536
1537ARGUMENTS
1538
1539    selection = string: atom selection {default: all}
1540
1541    state = integer: object state, -1 for current state, 0 for all states
1542    {default: -1}
1543
1544NOTES
1545
1546    If occupancy is 0.0 for an atom, set it to 1.0 for the calculation
1547    (assume it was loaded from a file without occupancy information).
1548
1549SEE ALSO
1550
1551    get_extent
1552        '''
1553        from chempy import cpv
1554        state, quiet = int(state), int(quiet)
1555
1556        if state < 0:
1557            state = _self.get_selection_state(selection)
1558        if state == 0:
1559            states = list(range(1, _self.count_states(selection)+1))
1560        else:
1561            states = [state]
1562
1563        com = cpv.get_null()
1564        totmass = 0.0
1565
1566        for state in states:
1567            model = _self.get_model(selection, state)
1568            for a in model.atom:
1569                m = a.get_mass() * (a.q or 1.0)
1570                com = cpv.add(com, cpv.scale(a.coord, m))
1571                totmass += m
1572
1573        if not totmass:
1574            raise pymol.CmdException('mass is zero')
1575
1576        com = cpv.scale(com, 1./totmass)
1577        if not quiet:
1578            print(' Center of Mass: [%8.3f,%8.3f,%8.3f]' % tuple(com))
1579        return com
1580
1581    def cif_get_array(name, key, dtype="s", quiet=1, _self=cmd):
1582        '''
1583DESCRIPTION
1584
1585    EXPERIMENTAL AND SUBJECT TO CHANGE!
1586
1587ARGUMENTS
1588
1589    name = string: object name
1590
1591    key = CIF data item name in lower case
1592
1593    dtype = str: "s" (str), "i" (int) or "f" (float)
1594        '''
1595        with _self.lockcm:
1596            r = _cmd.cif_get_array(_self._COb, name, key, dtype)
1597        if r and not int(quiet):
1598            n = len(r)
1599            r_print = r if n < 10 else (r[:9] + ['... (%d more items)' % (n - 9)])
1600            print(" %s:" % (key), ', '.join(map(str, r_print)))
1601        return r
1602
1603    def get_assembly_ids(name, quiet=1, _self=cmd):
1604        '''
1605DESCRIPTION
1606
1607    EXPERIMENTAL AND SUBJECT TO CHANGE!
1608    Get the list of assembly ids for an object loaded from mmCIF.
1609        '''
1610        r = cif_get_array(name, "_pdbx_struct_assembly.id", _self=_self)
1611        if r and not int(quiet):
1612            print(" Assembly IDs: %s" % ', '.join(r))
1613        return r
1614