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#-* Filipe Maia (slicing code)
11#-*
12#-*
13#Z* -------------------------------------------------------------------
14
15from __future__ import print_function, absolute_import
16
17if True:
18
19    import pymol
20    from . import selector
21    import traceback
22    cmd = __import__("sys").modules["pymol.cmd"]
23    import re
24    import gzip
25    import os
26    from .cmd import _cmd, Shortcut, is_list, is_string, \
27          safe_list_eval, safe_alpha_list_eval, \
28          DEFAULT_ERROR, DEFAULT_SUCCESS, is_ok, is_error, \
29          is_tuple
30    import tempfile
31    import sys
32
33    from chempy import fragments
34
35    map_type_dict = {
36        'vdw' : 0,
37        'coulomb' : 1,
38        'gaussian' : 2, # gaussian summation
39        'coulomb_neutral' : 3,
40        'coulomb_local' : 4,
41        'gaussian_max' : 5, # gaussian maximum contributor
42        }
43
44    map_type_sc = Shortcut(map_type_dict.keys())
45
46    ramp_spectrum_dict = {
47        "traditional" : 1,
48        "sludge" : 2,
49        "ocean" : 3,
50        "hot" : 4,
51        "grayable" : 5,
52        "rainbow" : 6,
53        "afmhot" : 7,
54        "grayscale" : 8,
55        "object" : [[-1.0,-1.0,-1.0]]
56        }
57
58    ramp_spectrum_sc = Shortcut(ramp_spectrum_dict.keys())
59
60    group_action_dict = {
61        "add" : 1,
62        "remove" : 2,
63        "open" : 3,
64        "close" : 4,
65        "toggle" : 5,
66        "auto" : 6,
67        "ungroup" : 7,
68        "empty" : 8,
69        "purge" : 9,
70        "excise" : 10,
71        }
72
73    group_action_sc =  Shortcut(group_action_dict.keys())
74
75    # must correspond to the driver's constants
76    reflection_format_dict = {
77        "cns" : 1,
78        "mtz" : 2,
79        "cif" : 3
80        }
81
82    def group(name, members="", action='auto', quiet=1,_self=cmd):
83        '''
84
85DESCRIPTION
86
87    "group" creates or updates a group object: a container for
88    organizing objects into a hierarchy.
89
90USAGE
91
92    group name [, members [, action ]]
93
94ARGUMENTS
95
96    name = string: name of the group
97
98    members = string: space-separated list of objects to include in
99              the group
100
101    action = add, remove, open, close, toggle, auto, empty,
102             purge, excise
103
104ACTIONS
105
106    add:     add members to group
107    remove:  remove members from group (members will be ungrouped)
108    empty:   remove all members from group
109    purge:   remove all members from group and delete them
110    excise:  remove all members from group and delete group
111    open:    expand group display in object menu panel
112    close:   collapse group display in object menu panel
113    toggle:  toggle group display in object menu panel
114    auto:    add or toggle
115    ungroup: DEPRECATED, use ungroup command
116
117EXAMPLE
118
119    group kinases, 1oky 1pkg 1t46 1uwh 1z5m
120    group kinases, open
121    group kinases, close
122
123NOTES
124
125    Group objects can typically be used as arguments to commands.  In
126    such cases, the command should be applied to all members of the
127    group.  If the group is used as a selection, then all atoms in all
128    objects in the group should be included in the selection.
129
130    When a group objects is open, objects can be added or removed from
131    the group by right-clicking and dragging in the control panel.
132
133SEE ALSO
134
135    ungroup, order, "group_auto_mode" setting
136
137'''
138        action = group_action_dict[group_action_sc.auto_err(str(action),'group action')]
139        if name=='all': name='*'
140        if action == 6:  # auto
141            if len(members):
142                action = 1  # add
143            elif '*' in name or name in _self.get_names():
144                action = 5  # toggle
145            else:
146                action = 1  # add
147        elif action == 7:
148            print('action=ungroup is deprecated, use the "ungroup" command')
149        with _self.lockcm:
150            return _cmd.group(_self._COb,str(name),str(members),int(action),int(quiet))
151
152    def ungroup(members, quiet=1, _self=cmd):
153        '''
154
155DESCRIPTION
156
157    "ungroup" removes an object from a group object, returning it to
158    the top level.
159
160USAGE
161
162    ungroup name
163
164
165SEE ALSO
166
167    group
168
169    '''
170        with _self.lockcm:
171            return _cmd.group(_self._COb, "", str(members), 7, int(quiet))
172
173    def map_generate(name, reflection_file, amplitudes, phases, weights="None",
174                     reso_low=50.0, reso_high=1.0,quiet=1,zoom=1,_self=cmd):
175        '''
176
177DESCRIPTION
178
179    "map_generate" generates a map object from a PDB object or selection and
180    reflection data.
181
182    Experimental use with caution.
183
184USAGE
185
186    map_generate name, reflection_file, amplitudes, phases, weights [,
187        reso_low [, reso_high ]]
188
189ARGUMENTS
190
191    name = string: name of the map object to create or modify
192
193    reflection_file = string: name of reflection file on disk; if None, then
194                      PyMOL attempts to download the CIF file from the PDB.
195                      Default = None; attempt to download from PDB.
196
197    amplitudes = string: fully qualified apmlitudes column name.  Properly
198                 qualified names are: project/crysta/column.  For example,
199                 KINASE/cryastl1/FWT.
200
201    phases = string:  fully qualified phases column name.  Properly
202             qualified names are: project/crystal/column.  For example,
203             KINASE/crystal1/DELPHWT.
204
205    weights = string: fully qualified phases column name.  Properly
206              qualified names are: project/crystal/column.  For example,
207              KINASE/crystal1/FOM.
208
209    reso_low = float : minimum resolution; if set to equal max_reso, then
210               reso_low and reso_high will be read from the reflection file.
211
212    reso_high = float : maximum resolution; if set to equal min_reso then
213               reso_low and reso_high will be read from the reflection file.
214
215NOTES
216
217    This function can be used to synthesize x-ray maps from the reflection data.
218    Supported reflection file formats are "mtz".  Other formats coming soon.
219
220    New in PyMOL v1.4 for Mac and Linux.
221
222    '''
223        quiet = int(quiet)
224        r = DEFAULT_ERROR
225        try:
226            _self.lock(_self)
227            if not os.path.isfile(reflection_file):
228                print(" MapGenerate-Error: Could not find file '%s'.\n Please check the filename and try again." % reflection_file)
229                raise pymol.CmdException
230
231            # TODO: work for CIF, MTZ, and CNS
232            from . import headering
233            mtzFile = headering.MTZHeader(reflection_file)
234
235            # FORMAT: crystal/dataset/column
236            _, datasetName, ampColName = ('//' + amplitudes).rsplit('/', 2)
237
238            # if datasetName is empty, take any dataset that has ampColName
239            for dataset in list(mtzFile.datasets.values()):
240                if (not datasetName or dataset["name"] == datasetName) and \
241                        ampColName in dataset["cols"]:
242                    break
243            else:
244                raise pymol.CmdException("no dataset found")
245
246            cellX, cellY, cellZ = dataset['x'], dataset['y'], dataset['z']
247            cellAlpha, cellBeta, cellGamma = dataset['alpha'], dataset['beta'], dataset['gamma']
248            if reso_low==reso_high:
249                reso_low, reso_high = mtzFile.reso_min, mtzFile.reso_max
250            space_group = mtzFile.space_group
251
252            phases = phases.rsplit('/', 1)[-1]
253            if not phases:
254                raise pymol.CmdException("phase name missing")
255
256            if weights and weights!="None":
257                weights = weights.rsplit('/', 1)[-1]
258                if not weights:
259                    raise pymol.CmdException("Improperly formatted weights name")
260
261            tempFile = tempfile.NamedTemporaryFile(delete=False)
262            tempFileName = tempFile.name
263            tempFile.close()
264
265            r = _cmd.map_generate(_self._COb,str(name),str(reflection_file),str(tempFileName),
266                              str(ampColName),str(phases),
267                              str(weights),float(reso_low),float(reso_high),str(space_group),
268                              float(cellX), float(cellY), float(cellZ),
269                              float(cellAlpha), float(cellBeta), float(cellGamma),
270                              int(quiet),int(zoom))
271            if r is not None:
272                if not quiet:
273                    print("Loading map '%s'" % (name))
274                r = _self.load(r, name, format="ccp4", finish=1)
275            else:
276                print(' Error: Map generation failed')
277
278            os.remove(tempFileName)
279
280        except ImportError:
281            print(" MapGenerate-Error: Cannot import headering module.  Cannot read MTZ file or make map.")
282        finally:
283            _self.unlock(r,_self)
284        if _self._raising(r,_self): raise pymol.CmdException
285
286        return name
287
288    def map_new(name, type='gaussian', grid=None, selection="(all)",
289                buffer=None, box=None, state=0, quiet=1, zoom=0,
290                normalize=-1, clamp=[1.0,-1.0], resolution=0.0, _self=cmd):
291
292        '''
293
294DESCRIPTION
295
296    "map_new" creates a map object using one of the built-in map
297    generation routines.  This command not yet fully supported.
298
299USAGE
300
301    map_new name [, type [, grid [, selection [, buffer [, box [, state ]]]]]]
302
303ARGUMENTS
304
305    name = string: name of the map object to create or modify
306
307    type = vdw, gaussian, gaussian_max, coulomb, coulomb_neutral, coulomb_local
308
309    grid = float: grid spacing
310
311    selection = string: atoms about which to generate the map
312
313    buffer = float: cutoff
314
315    state > 0: use the indicated state
316
317    state = 0: use all states independently with independent extents
318
319    state = -1: use current global state
320
321    state = -2: use effective object state(s)
322
323    state = -3: use all states in one map
324
325    state = -4: use all states independent states by with a unified extent
326
327NOTES
328
329    This command can be used to create low-resolution surfaces of
330    protein structures.
331
332    '''
333        # preprocess selection
334        r = DEFAULT_ERROR
335        selection = selector.process(selection)
336        if box is not None: # box should be [[x1,y1,z1],[x2,y2,z2]]
337            if _self.is_string(box):
338                box = safe_list_eval(box)
339            box = (float(box[0][0]),
340                   float(box[0][1]),
341                   float(box[0][2]),
342                   float(box[1][0]),
343                   float(box[1][1]),
344                   float(box[1][2]))
345            box_flag = 1
346        else:
347            box = (0.0,0.0,0.0,1.0,1.0,1.0)
348            box_flag = 0
349        if grid is None:
350            grid = _self.get_setting_float('gaussian_resolution')/3.0
351        if buffer is None:
352            buffer = -1.0
353        grid = float(grid) # for now, uniform xyz; later (x,y,z)
354
355        if not is_list(clamp):
356            clamp = safe_list_eval(str(clamp))
357        if len(clamp)<2:
358            clamp = [1.0,-1.0]
359        type = map_type_dict[map_type_sc.auto_err(str(type),'map type')]
360        try:
361            _self.lock(_self)
362            r = _cmd.map_new(_self._COb,str(name),int(type),grid,str(selection),
363                             float(buffer),box,int(state)-1,
364                             int(box_flag),int(quiet),int(zoom),int(normalize),
365                             float(clamp[0]),float(clamp[1]),float(resolution))
366        finally:
367            _self.unlock(r,_self)
368        if _self._raising(r,_self): raise pymol.CmdException
369        return r
370
371    def ramp_new(name, map_name, range=[-1.0,0.0,1.0],
372                 color=['red',[1.0,1.0,1.0],'blue'], state=1,
373                 selection='', beyond=2.0, within=6.0, sigma=2.0,
374                 zero=1, quiet=1, _self=cmd):
375
376        '''
377DESCRIPTION
378
379    "ramp_new" creates a color ramp based on a map potential value or
380    based on proximity to a molecular object.
381
382USAGE
383
384    ramp_new name, map_name [, range [, color [, state [, selection [,
385        beyond [, within [, sigma [, zero ]]]]]]]]
386
387ARGUMENTS
388
389    name = string: name of the ramp object
390
391    map_name = string: name of the map (for potential) or molecular
392    object (for proximity)
393
394    range = list: values corresponding to slots in the ramp
395
396    color = list: colors corresponding to slots in the ramp
397
398    state = integer: state identifier
399
400    selection = selection: for automatic ranging
401
402    beyond = number: with automatic ranging, are we excluding
403    values beyond a certain distance from the selection?
404
405    within = number: with automatic ranging, are we only including
406    valuess within a certain distance from the selection?
407
408    sigma = number: with automatic ranging, how many standard
409    deviations from the mean do we go?
410
411    zero = integer: with automatic ranging, do we force the central
412    value to be zero?
413
414EXAMPLES
415
416    ramp_new e_pot_color, e_pot_map, [-10,0,10], [red,white,blue]
417
418NOTES
419
420    Color ramps are extremely powerful but complicated to use.
421
422    In the simplest case, they can be used to color representations
423    based on the potential values found in a map object at the
424    corresponding positions in space.
425
426    In another simple case, representations can be colored based on
427    proximity to a target.  Note that since ramp targets must
428    themselves be real objects (not merely selections), the "create"
429    command may be needed in order to generate an appropriate target.
430
431    In more complicated cases, they can be used to color
432    representations on one object based atoms found in another.
433
434    Ramps can operate recursively.  In other words, the output color
435    from one ramp can be used as the input color for another.  For
436    example, you could color by map potential within a certain
437    distance of the target object, beyond which, a uniform color is applied.
438
439
440PYMOL API
441
442    def ramp_new(string name, string map_name, list range, list color,
443                 int state, string selection, float beyond, float
444                 within, float sigma, int zero, int quiet)
445
446SEE ALSO
447
448    ramp_update, load, color, create, slice, gradient
449
450    '''
451        r = DEFAULT_ERROR
452        safe_color = str(color).strip()
453        if(safe_color[0:1]=="["): # looks like a list
454            color = safe_alpha_list_eval(str(safe_color))
455        else: # looks like a literal
456            color = str(color)
457        new_color = []
458        # preprocess selection
459        if selection!='':
460            selection = selector.process(selection)
461        # coerce range
462        try:
463            if isinstance(range, str):
464                range = safe_list_eval(range)
465            range = list(map(float, range))
466        except:
467            raise pymol.CmdException('invalid range')
468        if is_list(color):
469            for a in color:
470                if not is_list(a):
471                    new_color.append(list(_self.get_color_tuple(a,4))) # incl negative RGB special colors
472                else:
473                    new_color.append(a)
474        elif is_string(color):
475            new_color = ramp_spectrum_dict[ramp_spectrum_sc.auto_err(str(color),'ramp color spectrum')]
476        else:
477            new_color=int(color)
478        try:
479            _self.lock(_self)
480            r = _cmd.ramp_new(_self._COb,str(name),str(map_name),range,new_color,
481                                    int(state)-1,str(selection),float(beyond),float(within),
482                                    float(sigma),int(zero),int(quiet))
483            _self._invalidate_color_sc(_self)
484        finally:
485            _self.unlock(r,_self)
486        if _self._raising(r,_self): raise pymol.CmdException
487        return r
488
489    def ramp_update(name, range=[], color=[], quiet=1, _self=cmd):
490        '''
491DESCRIPTION
492
493    "ramp_update" updates range and/or color of a color ramp.
494
495USAGE
496
497    ramp_update name [, range [, color ]]
498
499EXAMPLES
500
501    ramp_new    e_pot_color, e_pot_map, [-10,0,10], [red,white,blue]
502    ramp_update e_pot_color, range=[-15,0,15]
503    ramp_update e_pot_color, color=[green,white,orange]
504
505SEE ALSO
506
507    ramp_new
508        '''
509        return _self.ramp_new(name, '', range, color, quiet=quiet)
510
511    def isomesh(name, map, level=1.0, selection='', buffer=0.0,
512                state=1, carve=None, source_state=0, quiet=1, _self=cmd):
513        '''
514DESCRIPTION
515
516    "isomesh" creates a mesh isosurface object from a map object.
517
518USAGE
519
520    isomesh name, map, level [, selection [, buffer [, state [, carve ]]]]
521
522ARGUMENTS
523
524    name = the name for the new mesh isosurface object.
525
526    map = the name of the map object to use for computing the mesh.
527
528    level = the contour level.
529
530    selection = an atom selection about which to display the mesh with
531        an additional "buffer" (if provided).
532
533    state = the state into which the object should be loaded (default=1)
534        (set state=0 to append new mesh as a new state)
535
536    carve = a radius about each atom in the selection for which to
537        include density. If "carve" is not provided, then the whole
538        brick is displayed.
539
540NOTES
541
542    If the mesh object already exists, then the new mesh will be
543    appended onto the object as a new state (unless you indicate a state).
544
545    state > 0: specific state
546    state = 0: all states
547    state = -1: current state
548
549    source_state > 0: specific state
550    source_state = 0: include all states starting with 0
551    source_state = -1: current state
552    source_state = -2: last state in map
553
554SEE ALSO
555
556    isodot, load
557'''
558        # preprocess selection
559        selection = selector.process(selection)
560        #
561        if carve is None:
562            carve=0.0
563        with _self.lockcm:
564            return _cmd.isomesh(_self._COb,str(name),str(map),
565                             selection,float(buffer),
566                             float(level),0,int(state)-1,float(carve),
567                             int(source_state)-1,int(quiet),
568                             float(level))
569
570    def volume(name, map, ramp='', selection='', buffer=0.0,
571                state=1, carve=None, source_state=0, quiet=1, _self=cmd):
572        '''
573DESCRIPTION
574
575    "volume" creates a volume object from a map object.
576
577USAGE
578
579    volume name, map [, ramp [, selection [, buffer [, state [, carve ]]]]]
580
581ARGUMENTS
582
583    name = the name for the new volume object.
584
585    map = the name of the map object to use for computing the volume.
586
587    ramp = str: named color ramp {default: }
588
589    selection = an atom selection about which to display the mesh with
590        an additional "buffer" (if provided).
591
592    carve = a radius about each atom in the selection for which to
593        include density. If "carve" is not provided, then the whole
594        brick is displayed.
595
596NOTES
597
598    If the volume object already exists, then the new volume will
599    overwrite the existing object.
600
601EXAMPLE
602
603    fetch 1oky, async=0
604    fetch 1oky, type=2fofc, async=0
605    volume 1okyVol, 1oky_2fofc
606
607SEE ALSO
608
609    map_new, isosurface, isomesh, volume_color, volume_ramp_new
610
611'''
612        r = DEFAULT_ERROR
613
614        # preprocess selection
615        selection = selector.process(selection)
616
617        if carve is None:
618            carve=0.0
619
620        try:
621            # legacy
622            level = float(ramp)
623            ramp = ''
624        except (ValueError, TypeError):
625            level = 0.0
626
627        with _self.lockcm:
628            r = _cmd.volume(_self._COb,str(name),str(map),
629                            selection,float(buffer),
630                            float(level),int(state)-1,float(carve),
631                            int(source_state)-1,int(quiet))
632
633        if ramp:
634            _self.volume_color(name, ramp)
635
636        return r
637
638
639    def set_raw_alignment(name, raw, guide='', state=1, quiet=1, _self=cmd):
640        '''
641DESCRIPTION
642
643    API only. Create an alignment object from lists of indices.
644
645ARGUMENTS
646
647    name = str: alignment object name
648
649    raw = list: list (columns) of lists with (model, index) tuples
650
651    guide = str: name of guide object
652
653    state = int: object state
654
655EXAMPLE
656
657    cmd.align('1t46', '1oky', object='aln')
658    raw = cmd.get_raw_alignment('aln')
659    cmd.delete('aln')
660    cmd.set_raw_alignment('alnnew', raw)
661
662SEE ALSO
663
664    cmd.get_raw_alignment
665        '''
666        with _self.lockcm:
667            return _cmd.set_raw_alignment(name, raw, guide,
668                    int(state) - 1, int(quiet), _self._COb)
669
670
671    def slice_new(name, map, state=1, source_state=0, _self=cmd):
672        '''
673DESCRIPTION
674
675    "slice_map" creates a slice object from a map object.
676
677USAGE
678
679    slice_map name, map, [opacity, [resolution, [state, [source_state]]]]
680
681ARGUMENTS
682
683    name = the name for the new slice object.
684
685    map = the name of the map object to use for computing the slice.
686
687    opacity = opacity of the new slice (default=1)
688
689    resolution = the number of pixels per sampling (default=5)
690
691    state = the state into which the object should be loaded (default=1)
692        (set state=0 to append new mesh as a new state)
693
694    source_state = the state of the map from which the object should be loaded (default=0)
695
696SEE ALSO
697
698    isomesh, isodot, load
699'''
700        r = DEFAULT_ERROR
701        try:
702            _self.lock(_self)
703            r = _cmd.slice_new(_self._COb,str(name),str(map),int(state)-1,int(source_state)-1)
704        finally:
705            _self.unlock(r,_self)
706        if _self._raising(r,_self): raise pymol.CmdException
707        return r
708
709
710    def isosurface(name, map, level=1.0, selection='', buffer=0.0, state=1,
711                   carve=None, source_state=0, side=1, mode=3, quiet=1,
712                   _self=cmd):
713        '''
714DESCRIPTION
715
716    "isosurface" creates a new surface object from a map object.
717
718USAGE
719
720    isosurface name, map, level [, selection [, buffer [, state [, carve ]]]]
721
722ARGUMENTS
723
724    name = the name for the new mesh isosurface object.
725
726    map = the name of the map object to use for computing the mesh.
727
728    level = the contour level.
729
730    selection = an atom selection about which to display the mesh with
731        an additional "buffer" (if provided).
732
733    state = the state into which the object should be loaded (default=1)
734        (set state=0 to append new surface as a new state)
735
736    carve = a radius about each atom in the selection for which to
737        include density. If "carve= not provided, then the whole
738        brick is displayed.
739
740NOTES
741
742    If the surface object already exists, then the new surface will be
743    appended onto the object as a new state (unless you indicate a state).
744
745SEE ALSO
746
747    isodot, isomesh, load
748        '''
749        # preprocess selection
750        selection = selector.process(selection)
751      #
752        if carve is None:
753            carve=0.0
754        with _self.lockcm:
755            return _cmd.isosurface(_self._COb,str(name),str(map),
756                                      selection,float(buffer),
757                                      float(level),int(mode),int(state)-1,float(carve),
758                                      int(source_state)-1,int(side),int(quiet))
759
760
761    def isodot(name,map,level=1.0,selection='',buffer=0.0,state=0,
762                  carve=None,source_state=0,quiet=1,_self=cmd):
763        '''
764DESCRIPTION
765
766    "isodot" creates a dot isosurface object from a map object.
767
768USAGE
769
770    isodot name, map [, level [, selection [, buffer [, state
771        [, carve [, source_state [, quiet ]]]]]]]
772
773ARGUMENTS
774
775    map = the name of the map object to use.
776
777    level = the contour level.
778
779    selection = an atom selection about which to display the mesh with
780        an additional "buffer" (if provided).
781
782NOTES
783
784    If the dot isosurface object already exists, then the new dots will
785    be appended onto the object as a new state.
786
787SEE ALSO
788
789    load, isomesh
790        '''
791        # preprocess selections
792        selection = selector.process(selection)
793        #
794        if carve is None:
795            carve=0.0
796        with _self.lockcm:
797            return _cmd.isomesh(_self._COb,str(name),str(map),
798                             selection,float(buffer),
799                             float(level),1,int(state)-1,
800                             float(carve),int(source_state)-1,int(quiet),
801                             float(level))
802
803
804    def isolevel(name,level=1.0,state=0,query=0,quiet=1,_self=cmd):
805        '''
806DESCRIPTION
807
808    "isolevel" changes the contour level of a isodot, isosurface, or isomesh object.
809
810USAGE
811
812    isolevel name, level, state
813
814        '''
815        r = DEFAULT_ERROR
816        try:
817            _self.lock(_self)
818            r = _cmd.isolevel(_self._COb,str(name),float(level),int(state)-1,int(query),int(quiet))
819        finally:
820            _self.unlock(r,_self)
821        if not int(query):
822            if _self._raising(r,_self): raise pymol.CmdException
823        return r
824
825    def gradient(name, map, minimum=1.0, maximum=-1.0,
826                 selection='', buffer=0.0, state=0,
827                 carve=None, source_state=0, quiet=1, _self=cmd):
828        '''
829DESCRIPTION
830
831    "gradient" creates a gradient object from a map object.
832
833USAGE
834
835    gradient name, map [, minimum [, maximum [, selection [, buffer [, state
836        [, carve [, source_state [, quiet ]]]]]]]]
837
838ARGUMENTS
839
840    map = the name of the map object to use.
841
842    minimum, maximum = minimum and maximum levels (default: full map range)
843
844    selection = an atom selection about which to display the mesh with
845        an additional "buffer" (if provided).
846
847SEE ALSO
848
849    load, isomesh
850        '''
851        # preprocess selections
852        selection = selector.process(selection)
853        #
854        if carve is None:
855            carve=0.0
856        with _self.lockcm:
857            return _cmd.isomesh(_self._COb,str(name),str(map),
858                             selection,float(buffer),
859                             float(minimum),3,int(state)-1,
860                             float(carve),int(source_state)-1,int(quiet),
861                             float(maximum))
862
863    def copy(target,source,zoom=-1,_self=cmd):
864        '''
865DESCRIPTION
866
867    "copy" creates a new object that is an identical copy of an
868    existing object.
869
870USAGE
871
872    copy target, source [, zoom ]
873
874NOTES
875
876    Currently, this command only works for molecular objects.
877
878SEE ALSO
879
880    create
881        '''
882        r = DEFAULT_ERROR
883        try:
884            _self.lock(_self)
885            r = _cmd.copy(_self._COb,str(source),str(target),int(zoom))
886        finally:
887            _self.unlock(r,_self)
888        if _self._raising(r,_self): raise pymol.CmdException
889        return r
890
891    def symexp(prefix, object, selection, cutoff, segi=0, quiet=1,_self=cmd):
892        '''
893DESCRIPTION
894
895    "symexp" creates all symmetry-related objects for the specified
896    object that occur within a cutoff about an atom selection.
897
898USAGE
899
900    symexp prefix, object, selection, cutoff
901
902NOTES
903
904    The newly objects are labeled using the prefix provided along with
905    their crystallographic symmetry operation and translation.
906
907SEE ALSO
908
909    load
910        '''
911        r = DEFAULT_ERROR
912        # preprocess selection
913        selection=selector.process(selection)
914        #
915        try:
916            _self.lock(_self)
917            r = _cmd.symexp(_self._COb,str(prefix),str(object),
918                            "("+str(selection)+")",float(cutoff),
919                            int(segi),int(quiet))
920        finally:
921            _self.unlock(r,_self)
922        if _self._raising(r,_self): raise pymol.CmdException
923        return r
924
925    def fragment(name, object=None, origin=1, zoom=0, quiet=1, _self=cmd):
926        '''
927DESCRIPTION
928
929    "fragment" retrieves a 3D structure from the fragment library,
930    which is currently pretty meager (just amino acids).
931
932USAGE
933
934    fragment name
935
936    '''
937        r = DEFAULT_ERROR
938        try:
939            if object is None:
940                object=name
941            model = fragments.get(str(name))
942            la = len(model.atom)
943            if la and int(origin):
944                position = _self.get_position()
945                for c in range(0,3):
946                    mean_c = sum([a.coord[c] for a in model.atom]) / la
947                    mean_c = position[c] - mean_c
948                    for a in model.atom:
949                        a.coord[c] += mean_c
950            r = _self.load_model(model,str(object),quiet=quiet,zoom=zoom, _self=_self)
951        except IOError:
952            raise pymol.CmdException("unable to load fragment '%s'." % name)
953        if _self._raising(r,_self): raise pymol.CmdException
954        return r
955
956    def create(name, selection, source_state=0,
957               target_state=0, discrete=0, zoom=-1, quiet=1,
958               singletons=0, extract=None, copy_properties=False, _self=cmd):
959        '''
960DESCRIPTION
961
962    "create" creates a new molecule object from a selection.  It can
963    also be used to create states in an existing object.
964
965USAGE
966
967    create name, selection [,source_state [,target_state ] ]
968
969ARGUMENTS
970
971    name = string: name of object to create or modify
972
973    selection = string: atoms to include in the new object
974
975    source_state = integer: {default: 0 -- copy all states}
976
977    target_state = integer: -1 appends after last state {default: 0}
978
979PYMOL API
980
981    cmd.create(string name, string selection, int state,
982               int target_state, int discrete)
983
984NOTES
985
986    If the source and target states are zero (default), then all
987    states will be copied.  Otherwise, only the indicated states will
988    be copied.
989
990SEE ALSO
991
992    load, copy, extract
993        '''
994        r = DEFAULT_ERROR
995        target_state = int(target_state)
996        if target_state == -1:
997            target_state = _self.count_states('?' + name) + 1
998        if copy_properties:
999            print(' Warning: properties are not supported in Open-Source PyMOL')
1000        # preprocess selection
1001        selection = selector.process(selection)
1002
1003        # TODO is this too much convenience? 'extract' should be a simple boolean
1004        if extract in (None, 0, '0', ''):
1005            extract = ''
1006        elif extract in (1, '1'):
1007            extract = selection
1008        else:
1009            print(' Warning: non-boolean extract values are deprecated!')
1010            extract = selector.process(extract)
1011
1012        if extract:
1013            extract_sele = _self.get_unused_name('_extract')
1014            _self.select(extract_sele, extract, 0)
1015
1016        try:
1017            _self.lock(_self)
1018            if not name:
1019                name = _self.get_unused_name("obj")
1020            r = _cmd.create(_self._COb,str(name),"("+str(selection)+")",
1021                            int(source_state)-1,int(target_state)-1,
1022                            int(discrete),int(zoom),int(quiet),int(singletons), int(copy_properties))
1023        finally:
1024            _self.unlock(r,_self)
1025
1026        if extract:
1027            if not is_error(r):
1028                _self.remove(extract_sele)
1029            _self.delete(extract_sele)
1030
1031        if _self._raising(r,_self): raise pymol.CmdException
1032        return r
1033
1034    def extract(name, selection, *arg,**kw):
1035        '''
1036DESCRIPTION
1037
1038    "extract" is simply a shorthand way calling the "create" command
1039    with the extract argument activated, so that atoms in the new
1040    object are removed from the source object.
1041
1042USAGE
1043
1044    extract name, selection [, source_state [, target_state ]]
1045
1046SEE ALSO
1047
1048    create
1049
1050    '''
1051
1052        kw['extract'] = 1
1053        return create(name, selection, *arg, **kw)
1054
1055    pseudoatom_mode_dict = {
1056        "unit" : 0, # radius 0.5
1057        "extent" : 1,
1058        "rms" : 2,
1059#        "ellipse" : 2,  for anisotropic b-factors?
1060        }
1061
1062    pseudoatom_mode_sc =  Shortcut(pseudoatom_mode_dict.keys())
1063
1064    unquote_re = re.compile(r"r?('.*'|\".*\")$")
1065
1066    def unquote(s):
1067        if sys.version_info[0] > 2 and isinstance(s, bytes):
1068            s = s.decode('utf-8', 'replace')
1069
1070        s = str(s)
1071        if unquote_re.match(s):
1072            try:
1073                return cmd.safe_eval(s)
1074            except SyntaxError:
1075                print(" Warning: unquote failed for", repr(s))
1076        return s
1077
1078    def pseudoatom(object='', selection='', name='PS1', resn='PSD', resi='1', chain='P',
1079                   segi='PSDO', elem='PS', vdw=-1.0, hetatm=1, b=0.0, q=0.0, color='',
1080                   label='', pos=None, state=0, mode='rms', quiet=1,_self=cmd):
1081        '''
1082
1083DESCRIPTION
1084
1085    "pseudoatom" adds a pseudoatom to a molecular object, and will
1086    creating the molecular object if it does not yet exist.
1087
1088USAGE
1089
1090    pseudoatom object [, selection [, name [, resn [, resi [, chain
1091        [, segi [, elem [, vdw [, hetatm [, b [, q [, color [, label
1092        [, pos [, state [, mode [, quiet ]]]]]]]]]]]]]]]]]
1093
1094NOTES
1095
1096    "pseudoatom" can be used for a wide variety of random tasks where
1097    on must place an atom or a label in 3D space.
1098
1099    '''
1100
1101        r = DEFAULT_ERROR
1102        # preprocess selection
1103        if len(color):
1104            color = _self.get_color_index(str(color))
1105        else:
1106            color = -1 # default
1107        selection = selector.process(selection)
1108        mode = pseudoatom_mode_dict[pseudoatom_mode_sc.auto_err(str(mode),'pseudoatom mode')]
1109
1110        (name,resn,resi,chain,segi,elem,label) = list(map(unquote,(name,resn,resi,chain,segi,elem,label)))
1111        #
1112        try:
1113            _self.lock(_self)
1114            if pos is not None:
1115                if not (is_list(pos) or is_tuple(pos)):
1116                    pos = safe_list_eval(pos)
1117                pos = (float(pos[0]), # tuple-ize
1118                       float(pos[1]),
1119                       float(pos[2]))
1120            if len(selection.split())>1:
1121                selection = "("+str(selection)+")"
1122            r = _cmd.pseudoatom(_self._COb,str(object), str(selection),
1123                                str(name), str(resn), str(resi), str(chain),
1124                                str(segi), str(elem), float(vdw), int(hetatm),
1125                                float(b), float(q), str(label), pos, int(color),
1126                                int(state)-1, int(mode), int(quiet))
1127        finally:
1128            _self.unlock(r,_self)
1129        if _self._raising(r,_self): raise pymol.CmdException
1130        return r
1131
1132    def join_states(name, selection='all', mode=2, zoom=0, quiet=1, _self=cmd):
1133        '''
1134DESCRIPTION
1135
1136    The reverse of split_states. Create a multi-state object from a
1137    selection which spans multiple objects.
1138
1139ARGUMENTS
1140
1141    name = string: name of object to create or modify
1142
1143    selection = string: atoms to include in the new object
1144
1145    mode = int: how to match states {default: 2}
1146      0: Create discrete object, input objects can be (totally) different
1147      1: Assume identical topology (same number of atoms and matching atom
1148         identifiers) in all input objects
1149      2: Assume matching atom identifiers in all input objects, but also
1150         check for missing atoms and only include atoms that are present
1151         in all input objects
1152      3: match atoms by sequence alignment, slowest but most robust option
1153
1154EXAMPLE
1155
1156    fragment ala
1157    fragment his
1158    join_states multi, (ala|his), mode=0
1159        '''
1160
1161        mode, quiet = int(mode), int(quiet)
1162
1163        if mode == 3:
1164            aln_name = _self.get_unused_name('_join_states_aln')
1165
1166        sele_name = _self.get_unused_name('_join_states_sele1')
1167        msel_name = _self.get_unused_name('_join_states_sele2')
1168
1169        _self.select(sele_name, selection, 0)
1170        models = _self.get_object_list('?' + sele_name)
1171
1172        for i, model in enumerate(models):
1173            _self.select(msel_name, '?%s & ?%s' % (sele_name, model), 0)
1174            if mode == 2 and i > 0:
1175                _self.remove('?%s & ! ((alt A+) & (?%s in ?%s))' % (name, name, msel_name))
1176                _self.create(name, '?%s in ?%s' % (msel_name, name), 0, -1, 0, 0, quiet)
1177            elif mode == 3 and i > 0:
1178                _self.align(msel_name, name, cycles=0, transform=0, object=aln_name)
1179                _self.select(msel_name, '?%s & ?%s' % (sele_name, aln_name), 0)
1180                _self.remove('?%s and not ?%s' % (name, aln_name))
1181                _self.delete(aln_name)
1182
1183                n = _self.count_states('?' + name)
1184                for j in range(_self.count_states('?' + model)):
1185                    _self.create(name, name, 1, -1, 0, 0, quiet)
1186                    _self.update(name, '?' + msel_name, n + j + 1, 1, 0, quiet)
1187            else:
1188                _self.create(name, msel_name, 0, -1, mode == 0, 0, quiet)
1189
1190        _self.delete(sele_name)
1191        _self.delete(msel_name)
1192        _self.rebuild('?' + name)
1193
1194        if int(zoom):
1195            _self.zoom(name, state=0)
1196