Example #1
0
    def get_names(type='public_objects',enabled_only=0,selection="",_self=cmd):
        '''
DESCRIPTION

    "get_names" returns a list of object and/or selection names.

PYMOL API

    cmd.get_names( [string: "objects"|"selections"|"all"|"public_objects"|"public_selections"] )

NOTES

    The default behavior is to return only object names.

SEE ALSO

    get_type, count_atoms, count_states
        '''
        selection = selector.process(selection)
        # this needs to be replaced with a flag & masking scheme...
        if type=='objects':
            mode = 1
        elif type=='selections':
            mode = 2
        elif type=='all':
            mode = 0
        elif type=='public':
            mode = 3
        elif type=='public_objects':
            mode = 4
        elif type=='public_selections':
            mode = 5
        elif type=='public_nongroup_objects':
            mode = 6
        elif type=='public_group_objects':
            mode = 7
        elif type=='nongroup_objects':
            mode = 8
        elif type=='group_objects':
            mode = 9
        else:
            raise pymol.CmdException("unknown type: '{}'".format(type))
        with _self.lockcm:
            r = _cmd.get_names(_self._COb,int(mode),int(enabled_only),str(selection))
        return r
Example #2
0
def color_by_area(sele, mode="molecular", state=0, palette='rainbow', _self=cmd):
    """
DESCRIPTION

    Colors molecule by surface area

ARGUMENTS

    sele = str: atom selection

    mode = str: "molecular" {default} or "solvent"
    """
    asa = 1 if mode=="solvent" else 0

    tmpObj = _self.get_unused_name("_tmp")
    tmpSel = _self.get_unused_name("_sel")
    orgSel = _self.get_unused_name("_org")

    orgN = _self.select(orgSel, sele, 0)
    _self.create(tmpObj, "byobj ?%s & ! solvent" % (orgSel), zoom=0)
    tmpN = _self.select(tmpSel, '?%s in ?%s' % (tmpObj, orgSel), 0)

    try:
        if orgN != tmpN:
            raise pymol.CmdException('color_by_area failed')

        _self.set("dot_solvent", asa, tmpObj)
        _self.set("dot_density", 3, tmpObj)

        l = []
        _self.get_area(tmpSel, load_b=1)
        _self.spectrum("b", palette, tmpSel)
        _self.iterate(tmpSel, "l_a(color)", space={'l_a': l.append})
        _self.alter(orgSel, "color=l_n()", space={'l_n': getattr(iter(l), _next_method_name)})

        _self.recolor(orgSel)
    finally:
        _self.delete(tmpSel)
        _self.delete(tmpObj)
        _self.delete(orgSel)
Example #3
0
    def alignto(target='',
                method="cealign",
                selection='',
                quiet=1,
                _self=cmd,
                **kwargs):
        """
DESCRIPTION

        "alignto" aligns all other loaded objects to the target
        using the specified alignment algorithm.

USAGE

        alignto target [, method [, quiet ]]

NOTES

        Available alignment methods are "align", "super" and "cealign".

EXAMPLE

        # fetch some calmodulins
        fetch 1cll 1sra 1ggz 1k95, async=0

        # align them to 1cll using cealign
        alignto 1cll, method=cealign
        alignto 1cll, object=all_to_1cll

SEE ALSO

        extra_fit, align, super, cealign, fit, rms, rms_cur, intra_fit
                """
        if not selection:
            names = cmd.get_names("public_objects", 1)
            if not names:
                raise pymol.CmdException('no public objects')
            selection = '%' + ' %'.join(names)
        return extra_fit(selection, target, method, 0, quiet, _self, **kwargs)
Example #4
0
    def system(command, async_=0, _self=cmd, **kwargs):
        '''
DESCRIPTION

    "system" executes a command in a subshell under Unix or Windows.

USAGE

    system command 

PYMOL API

    cmd.system(string command,int async=0)

NOTES

    async can only be specified from the Python level (not the command language)

    if async is 0 (default), then the result code from "system" is returned in r

    if async is 1, then the command is run in a separate thread whose object is
    returned

SEE ALSO

    ls, cd, pwd
        '''
        async_ = int(kwargs.pop('async', async_))

        if kwargs:
            raise pymol.CmdException('unknown argument: ' + ', '.join(kwargs))

        r = None
        if async_:
            r = threading.Thread(target=_cmd.system,args=(str(command),1))
            r.start()
        else:
            r = _cmd.system(_self._COb,str(command),0)
        return r # special meaning
Example #5
0
    def get_gltf(filename, quiet=1, *, _self=cmd):
        '''
DESCRIPTION

    "get_gltf" saves a gltf file representing the content
    currently displayed.

PYMOL API

    cmd.get_gltf()

        '''
        import shutil
        exe = shutil.which('collada2gltf') or shutil.which('COLLADA2GLTF-bin')
        if exe is None:
            raise pymol.CmdException('could not find collada2gltf')

        # https://github.com/schrodinger/pymol-open-source/issues/107
        _self.set('collada_geometry_mode', 1, quiet=quiet)

        r = _self.get_collada()

        # write collada file
        with open(filename, 'w') as handle:
            handle.write(r)

        import subprocess

        result = subprocess.call([exe, '-i', filename, '-o', filename])
        # convert collada file to gltf by using collada2gltf binary

        if not quiet:
            if result == 0:
                print(' Save: wrote "' + filename + '".')
            else:
                print(' Save-Error: no file written')

        return result
Example #6
0
    def cd(dir="~",complain=1,quiet=1):
        '''
DESCRIPTION

    "cd" changes the current working directory.

USAGE

    cd <path>

SEE ALSO

    pwd, ls, system
        '''
        dir = exp_path(dir)
        try:
            os.chdir(dir)  # raises on error
            if not quiet:
                print(" cd: now in %s" % getcwdu())
        except BaseException as e:
            if complain:
                raise pymol.CmdException(str(e))
        return DEFAULT_SUCCESS
Example #7
0
def fab(input,name=None,mode='peptide',resi=1,chain='',segi='',state=-1,
        dir=1,hydro=-1,ss=0,async_=-1,quiet=1,_self=cmd, **kwargs):
    '''
DESCRIPTION

    Build a peptide

ARGUMENTS

    input = str: sequence in one-letter code

    name = str: name of object to create {default: }

    ss = int: Secondary structure 1=alpha helix, 2=antiparallel beta, 3=parallel beta, 4=flat

EXAMPLE

    fab ACDEFGH
    fab ACDEFGH, helix, ss=1
    '''
    async_ = int(kwargs.pop('async', async_))

    if kwargs:
        raise pymol.CmdException('unknown argument: ' + ', '.join(kwargs))

    if async_ < 1:
        r = _fab(input,name,mode,resi,chain,segi,
                 state,dir,hydro,ss,quiet,_self)
    else:
        fab_thread = threading.Thread(target=_fab, args=(input,name,mode,
                                                         resi,chain,
                                                         segi,state,dir,
                                                         hydro,ss,quiet,_self))
        fab_thread.setDaemon(1)
        fab_thread.start()
        r = DEFAULT_SUCCESS
    return r
def attach_fragment(selection, fragment, hydrogen, anchor, *, _self=cmd):
    '''
ARGUMENTS

    selection = str: Name of a single-atom selection. If no such named
    selection exists, then create a new object with name `fragment`.

    fragment = str: fragment name to load from fragment library

    hydrogen = int: atom ID in fragment to fuse

    anchor = int: (unused)
    '''
    remove_hydrogens = _self.get_setting_boolean("auto_remove_hydrogens")

    if selection not in _self.get_names("selections"):
        if fragment in _self.get_names("objects"):
            raise pymol.CmdException("an object with that name already exists")

        _self.fragment(fragment)

        if remove_hydrogens:
            _self.remove(f"(hydro and {fragment})")
    else:
        fragment_label = _self.get_unused_name(_prefix + "_attach_fragment")
        _self.fragment(fragment, fragment_label, origin=0)

        try:
            _self.fuse(f"{fragment_label} and id {hydrogen}", f"({selection})",
                       1)

            if remove_hydrogens:
                _self.remove("(hydro and pkmol)")
            elif _self.count_atoms('hydro and (neighbor pk2)'):
                _self.h_fill()
        finally:
            _self.delete(fragment_label)
def dumpexcel(selection='all',
              props='chain segi resi resn name b q',
              state=STATELESS):
    '''
DESCRIPTION

    Open a new Excel spreadsheet with atom property data from the given
    selection.
    '''
    try:
        import xlwings
    except ImportError:
        raise pymol.CmdException('Requires xlwings (conda install xlwings)')

    try:
        sheet = xlwings.Book().sheets[0]
    except IndexError:
        sheet = None

    if not isinstance(props, (list, tuple)):
        props = props.split()

    table = [props]

    kwargs = {
        'expression': '_append((' + ','.join(props) + '))',
        'space': {
            '_append': table.append
        },
    }

    if state == STATELESS:
        pymol.cmd.iterate(selection, **kwargs)
    else:
        pymol.cmd.iterate_state(state, selection, **kwargs)

    xlwings.view(table, sheet)
Example #10
0
    def get_gltf(filename, quiet=1, _self=cmd):
        '''
DESCRIPTION

    "get_gltf" saves a gltf file representing the content
    currently displayed.

PYMOL API

    cmd.get_gltf()

        '''

        from distutils.spawn import find_executable
        exe = find_executable('collada2gltf')
        if exe is None:
            raise pymol.CmdException('could not find collada2gltf')

        COLLADA_VERSION = 2
        r = _self.get_collada(COLLADA_VERSION)

        # write collada file
        with open(filename, 'w') as handle:
            handle.write(r)

        import subprocess

        result = subprocess.call([exe, '-i', filename, '-o', filename])
                # convert collada file to gltf by using collada2gltf binary

        if not quiet:
            if result == 0:
                print(' Save: wrote "' + filename + '".')
            else:
                print(' Save-Error: no file written')

        return result
Example #11
0
def get_stereo_labels_schrodinger(molstr):
    '''R/S labels with SCHRODINGER backend
    '''
    if 'SCHRODINGER' not in os.environ:
        raise pymol.CmdException('SCHRODINGER environment variable not set')

    import subprocess
    import tempfile

    schrun = os.path.join(os.environ['SCHRODINGER'], 'run')
    script = os.path.join(os.path.dirname(__file__), 'schrodinger-helper.py')
    filename = tempfile.mktemp('.mol')
    args = [schrun, script, filename]

    env = dict(os.environ)
    env.pop('PYTHONEXECUTABLE', '')  # messes up on Mac

    with open(filename, 'w') as handle:
        handle.write(molstr)

    try:
        p = subprocess.Popen(
            args,
            env=env,
            universal_newlines=True,
            stdin=subprocess.PIPE,  # Windows fix
            stderr=subprocess.PIPE,  # Windows fix
            stdout=subprocess.PIPE)
        stdout, stderr = p.communicate()
    finally:
        os.unlink(filename)

    if stderr:
        print(stderr)

    return stdout.splitlines()
Example #12
0
    def ramp_new(name,
                 map_name,
                 range=[-1.0, 0.0, 1.0],
                 color=['red', [1.0, 1.0, 1.0], 'blue'],
                 state=1,
                 selection='',
                 beyond=2.0,
                 within=6.0,
                 sigma=2.0,
                 zero=1,
                 quiet=1,
                 _self=cmd):
        '''
DESCRIPTION

    "ramp_new" creates a color ramp based on a map potential value or
    based on proximity to a molecular object.
    
USAGE

    ramp_new name, map_name [, range [, color [, state [, selection [,
        beyond [, within [, sigma [, zero ]]]]]]]]

ARGUMENTS

    name = string: name of the ramp object

    map_name = string: name of the map (for potential) or molecular
    object (for proximity)
    
    range = list: values corresponding to slots in the ramp

    color = list: colors corresponding to slots in the ramp

    state = integer: state identifier

    selection = selection: for automatic ranging
    
    beyond = number: with automatic ranging, are we excluding
    values beyond a certain distance from the selection?

    within = number: with automatic ranging, are we only including
    valuess within a certain distance from the selection?

    sigma = number: with automatic ranging, how many standard
    deviations from the mean do we go?

    zero = integer: with automatic ranging, do we force the central
    value to be zero?

EXAMPLES

    ramp_new e_pot_color, e_pot_map, [-10,0,10], [red,white,blue]

NOTES

    Color ramps are extremely powerful but complicated to use.

    In the simplest case, they can be used to color representations
    based on the potential values found in a map object at the
    corresponding positions in space.

    In another simple case, representations can be colored based on
    proximity to a target.  Note that since ramp targets must
    themselves be real objects (not merely selections), the "create"
    command may be needed in order to generate an appropriate target.
    
    In more complicated cases, they can be used to color
    representations on one object based atoms found in another.

    Ramps can operate recursively.  In other words, the output color
    from one ramp can be used as the input color for another.  For
    example, you could color by map potential within a certain
    distance of the target object, beyond which, a uniform color is applied.
    
    
PYMOL API

    def ramp_new(string name, string map_name, list range, list color,
                 int state, string selection, float beyond, float
                 within, float sigma, int zero, int quiet)

SEE ALSO

    ramp_update, load, color, create, slice, gradient
    
    '''
        r = DEFAULT_ERROR
        safe_color = str(color).strip()
        if (safe_color[0:1] == "["):  # looks like a list
            color = safe_alpha_list_eval(str(safe_color))
        else:  # looks like a literal
            color = str(color)
        new_color = []
        # preprocess selection
        if selection != '':
            selection = selector.process(selection)
        # coerce range
        try:
            if isinstance(range, str):
                range = safe_list_eval(range)
            range = list(map(float, range))
        except:
            raise pymol.CmdException('invalid range')
        if is_list(color):
            for a in color:
                if not is_list(a):
                    new_color.append(list(_self.get_color_tuple(
                        a, 4)))  # incl negative RGB special colors
                else:
                    new_color.append(a)
        elif is_string(color):
            new_color = ramp_spectrum_dict[ramp_spectrum_sc.auto_err(
                str(color), 'ramp color spectrum')]
        else:
            new_color = int(color)
        try:
            _self.lock(_self)
            r = _cmd.ramp_new(_self._COb, str(name), str(map_name), range,
                              new_color,
                              int(state) - 1, str(selection), float(beyond),
                              float(within), float(sigma), int(zero),
                              int(quiet))
            _self._invalidate_color_sc(_self)
        finally:
            _self.unlock(r, _self)
        if _self._raising(r, _self): raise pymol.CmdException
        return r
Example #13
0
    def map_generate(name,
                     reflection_file,
                     amplitudes,
                     phases,
                     weights="None",
                     reso_low=50.0,
                     reso_high=1.0,
                     quiet=1,
                     zoom=1,
                     _self=cmd):
        '''

DESCRIPTION

    "map_generate" generates a map object from a PDB object or selection and
    reflection data.

    Experimental use with caution.
    
USAGE

    map_generate name, reflection_file, amplitudes, phases, weights [,
        reso_low [, reso_high ]]

ARGUMENTS

    name = string: name of the map object to create or modify
	
    reflection_file = string: name of reflection file on disk; if None, then
                      PyMOL attempts to download the CIF file from the PDB.
                      Default = None; attempt to download from PDB.

    amplitudes = string: fully qualified apmlitudes column name.  Properly 
                 qualified names are: project/crysta/column.  For example,
                 KINASE/cryastl1/FWT.

    phases = string:  fully qualified phases column name.  Properly 
             qualified names are: project/crystal/column.  For example,
             KINASE/crystal1/DELPHWT.

    weights = string: fully qualified phases column name.  Properly 
              qualified names are: project/crystal/column.  For example,
              KINASE/crystal1/FOM.

    reso_low = float : minimum resolution; if set to equal max_reso, then
               reso_low and reso_high will be read from the reflection file.

    reso_high = float : maximum resolution; if set to equal min_reso then
               reso_low and reso_high will be read from the reflection file.
    
NOTES

    This function can be used to synthesize x-ray maps from the reflection data.
    Supported reflection file formats are "mtz".  Other formats coming soon.

    New in PyMOL v1.4 for Mac and Linux.
    
    '''
        import subprocess
        exe = 'mtz2ccp4_px'

        quiet = int(quiet)
        r = DEFAULT_ERROR
        try:
            _self.lock(_self)
            if not os.path.isfile(reflection_file):
                print(
                    " MapGenerate-Error: Could not find file '%s'.\n Please check the filename and try again."
                    % reflection_file)
                raise pymol.CmdException

            # TODO: work for CIF, MTZ, and CNS
            from . import headering
            mtzFile = headering.MTZHeader(reflection_file)

            # FORMAT: crystal/dataset/column
            _, datasetName, ampColName = ('//' + amplitudes).rsplit('/', 2)

            # if datasetName is empty, take any dataset that has ampColName
            for dataset in list(mtzFile.datasets.values()):
                if (not datasetName or dataset["name"] == datasetName) and \
                        ampColName in dataset["cols"]:
                    break
            else:
                raise pymol.CmdException("no dataset found")

            cellX, cellY, cellZ = dataset['x'], dataset['y'], dataset['z']
            cellAlpha, cellBeta, cellGamma = dataset['alpha'], dataset[
                'beta'], dataset['gamma']
            if reso_low == reso_high:
                reso_low, reso_high = mtzFile.reso_min, mtzFile.reso_max
            space_group = mtzFile.space_group

            phases = phases.rsplit('/', 1)[-1]
            if not phases:
                raise pymol.CmdException("phase name missing")

            if weights and weights != "None":
                weights = weights.rsplit('/', 1)[-1]
                if not weights:
                    raise pymol.CmdException(
                        "Improperly formatted weights name")
            else:
                weights = ''

            if not quiet:
                print('Info: Generating map from columns', ampColName, phases,
                      weights)
                print('Info: Resolution limits low=%.2f high=%.2f' %
                      (reso_low, reso_high))

            tempFile = tempfile.NamedTemporaryFile(delete=False)
            tempFileName = tempFile.name
            tempFile.close()

            r = _cmd.map_generate(_self._COb, str(name), str(reflection_file),
                                  str(tempFileName), str(ampColName),
                                  str(phases), str(weights), float(reso_low),
                                  float(reso_high), str(space_group),
                                  float(cellX), float(cellY), float(cellZ),
                                  float(cellAlpha), float(cellBeta),
                                  float(cellGamma), int(quiet), int(zoom))

            # With NO_MMLIBS the C function call returns None.
            # Try standalone executable instead.
            if r is None:
                args = [
                    str(arg) for arg in [
                        exe, space_group, cellX, cellY, cellZ, cellAlpha,
                        cellBeta, cellGamma, reso_high, reso_low,
                        reflection_file, ampColName, phases, weights,
                        tempFileName
                    ]
                ]

                subprocess.check_call(args)
                r = tempFileName

            if r != None:
                if not quiet:
                    print("Loading map '%s'" % (name))
                r = _self.load(r, name, format="ccp4", finish=1)
            else:
                print(' Error: Map generation failed')

            os.remove(tempFileName)

        except OSError as e:
            raise pymol.CmdException('cannot run %s: %s' % (exe, e))
        except subprocess.CalledProcessError as e:
            raise pymol.CmdException('%s failed: %s' % (exe, e))
        except ImportError:
            print(
                " MapGenerate-Error: Cannot import headering module.  Cannot read MTZ file or make map."
            )
        finally:
            _self.unlock(r, _self)
        if _self._raising(r, _self): raise pymol.CmdException

        return name
Example #14
0
    def save(filename,
             selection='(all)',
             state=-1,
             format='',
             ref='',
             ref_state=-1,
             quiet=1,
             partial=0,
             *,
             _self=cmd):
        '''
DESCRIPTION

    "save" writes content to a file.
    
USAGE

    save filename [, selection [, state [, format ]]]

ARGUMENTS

    filename = string: file path to be written

    selection = string: atoms to save {default: (all)}

    state = integer: state to save {default: -1 (current state)}
    
PYMOL API

    cmd.save(string file, string selection, int state, string format)

NOTES

    The file format is automatically chosen if the extesion is one of
    the supported output formats: pdb, pqr, mol, sdf, pkl, pkla, mmd, out,
    dat, mmod, cif, pov, png, pse, psw, aln, fasta, obj, mtl, wrl, dae, idtf,
    or mol2.

    If the file format is not recognized, then a PDB file is written
    by default.

    For molecular files and where applicable and supported:
    
    * if state = -1 (default), then only the current state is written.

    * if state = 0, then a multi-state output file is written.
    
SEE ALSO

    load, get_model
        '''
        quiet = int(quiet)

        # preprocess selection
        selection = selector.process(selection)
        #
        r = DEFAULT_ERROR

        # analyze filename
        from pymol.importing import filename_to_format, _eval_func
        _, _, format_guessed, zipped = filename_to_format(filename)
        filename = _self.exp_path(filename)

        # file format
        if not format:
            if not format_guessed:
                raise pymol.CmdException('Unrecognized file format')
            format = format_guessed

        # PyMOL session
        if format in (
                'pse',
                'psw',
        ):
            _self.set(
                "session_file",
                # always use unix-like path separators
                filename.replace("\\", "/"),
                quiet=1)
            if not quiet:
                print(" Save: Please wait -- writing session file...")

        func_type4 = {
            'mmod': io.mmd.toFile,
            'pkl': io.pkl.toFile,  # binary pickle
            'pkla': lambda model, filename: io.pkl.toFile(
                model, filename, bin=0),  # ascii pickle
        }

        contents = None

        if format in savefunctions:
            # generic forwarding to format specific save functions
            func = savefunctions[format]
            func = _eval_func(func)
            kw_all = {
                'filename': filename,
                'selection': selection,
                'name': selection,  # alt (get_ccp4str)
                'state': state,
                'format': format,
                'ref': ref,
                'ref_state': ref_state,
                'quiet': quiet,
                'partial': partial,
                '_self': _self,
            }

            import inspect
            sig = inspect.signature(func, follow_wrapped=False)
            kw = {}

            for n, param in sig.parameters.items():
                if param.kind == inspect.Parameter.VAR_KEYWORD:
                    kw = kw_all
                    break

                if param.kind == inspect.Parameter.VAR_POSITIONAL:
                    print('FIXME: savefunctions[%s]: *args' % (format))
                elif param.kind == inspect.Parameter.POSITIONAL_ONLY:
                    raise Exception('positional-only arguments not supported')
                elif n in kw_all:
                    kw[n] = kw_all[n]

            contents = func(**kw)

            if 'filename' in sig.parameters:
                # assume function wrote directly to file and returned a status
                return contents

        elif format in func_type4:
            func_type4[format](_self.get_model(selection, state, ref,
                                               ref_state), filename)
            r = DEFAULT_SUCCESS
        else:
            raise pymol.CmdException('File format not supported for export')

        # function returned sequence of strings or bytes
        if isinstance(contents, (tuple, list)) and contents:
            contents = contents[0][:0].join(contents)

        if cmd.is_string(contents):
            if not isinstance(contents, bytes):
                contents = contents.encode()

            if zipped == 'gz':
                import gzip
                fopen = gzip.open
            else:
                fopen = open
                if zipped == 'bz2':
                    import bz2
                    contents = bz2.compress(contents)

            with fopen(filename, 'wb') as handle:
                handle.write(contents)
            r = DEFAULT_SUCCESS

        if _self._raising(r): raise QuietException

        if not quiet:
            if r == DEFAULT_SUCCESS:
                print(' Save: wrote "' + filename + '".')
            else:
                print(' Save-Error: no file written')

        return r
Example #15
0
    def multisave(filename,
                  pattern="all",
                  state=-1,
                  append=0,
                  format='',
                  quiet=1,
                  *,
                  _self=cmd):
        '''
DESCRIPTION

    "multisave" will save a multi-entry PDB file.

    Every object in the given selection (pattern) will have a HEADER and a
    CRYST (if symmetry is defined) record, and is terminated with END.
    Loading such a multi-entry PDB file into PyMOL will load each entry
    as a separate object.

    This behavior is different to the "save" command, where a multi-object
    selection is written "flat" to a PDB file, without HEADER or CRYST
    records.

ARGUMENTS

    filename = string: file path to be written

    pattern = str: atom selection (before 1.8.4: object name pattern)

    state = int: object state (-1=current, 0=all) {default: -1}

    append = 0/1: append to existing file {default: 0}

    format = str: file format {default: guess from extension, or 'pdb'}
    '''
        from pymol.importing import filename_to_format
        _, _, format_guessed, zipped = filename_to_format(filename)

        if zipped:
            raise pymol.CmdException(zipped + ' not supported with multisave')

        if not format:
            format = format_guessed or 'pdb'

        if format == 'pmo':
            raise pymol.CmdException('pmo format not supported anymore')

        if format not in ('pdb', 'cif'):
            raise pymol.CmdException(format +
                                     ' format not supported with multisave')

        s = _self.get_str(format, pattern, state, '', -1, 1, quiet)

        if s is None:
            raise QuietException

        filename = _self.exp_path(filename)

        with open(filename, 'a' if int(append) else 'w') as handle:
            handle.write(s)

        return DEFAULT_SUCCESS
Example #16
0
    def png(filename,
            width=0,
            height=0,
            dpi=-1.0,
            ray=0,
            quiet=1,
            prior=0,
            format=0,
            *,
            _self=cmd):
        '''
DESCRIPTION

    "png" saves a PNG format image file of the current display.

USAGE

    png filename [, width [, height [, dpi [, ray]]]]

ARGUMENTS

    filename = string: file path to be written
    
    width = integer or string: width in pixels (without units), inches (in)
    or centimeters (cm). If unit suffix is given, dpi argument is required
    as well. If only one of width or height is given, the aspect ratio of
    the viewport is preserved. {default: 0 (current)}

    height = integer or string: height (see width) {default: 0 (current)}

    dpi = float: dots-per-inch {default -1.0 (unspecified)}

    ray = 0 or 1: should ray be run first {default: 0 (no)}

EXAMPLES

    png image.png
    png image.png, dpi=300
    png image.png, 10cm, dpi=300, ray=1

NOTES

    PNG is the only image format supported by PyMOL.

SEE ALSO

    mpng, save
    
PYMOL API

    cmd.png(string filename, int width, int height, float dpi,
            int ray, int quiet)
        '''
        ray = int(ray)

        PRIOR_TRY = -1
        PRIOR_NO = 0
        PRIOR_YES = 1

        prior = int(prior)
        assert prior in (PRIOR_TRY, PRIOR_YES, PRIOR_NO)

        FORMAT_GUESS = -1
        FORMAT_PNG = 0
        FORMAT_PPM = 1

        if format == 'png':
            format = FORMAT_PNG

        assert format in (FORMAT_PNG, FORMAT_PPM, FORMAT_GUESS)

        if format == FORMAT_GUESS:
            if filename and filename.endswith(".ppm"):
                format = FORMAT_PPM
            else:
                format = FORMAT_PNG

        if filename and not filename.startswith('\x01'):
            if format == FORMAT_PNG and not filename.endswith(".png"):
                filename += ".png"

            filename = cmd.exp_path(filename)

        dpi = float(dpi)
        if dpi < 0:
            dpi = _self.get_setting_float('image_dots_per_inch')

        width = _unit2px(width, dpi)
        height = _unit2px(height, dpi)

        def func():
            with _self.lockcm:
                return _cmd.png(_self._COb, filename, int(width), int(height),
                                dpi, ray, int(quiet), prior, format)

        if prior:
            # fetch the prior image, without doing any work (fast-path / non-GLUT thread-safe)
            r = func()
            if r:
                return r

            if prior != PRIOR_TRY:
                raise pymol.CmdException("no prior image available")

            print("no prior image available, fall back to rendering")
            prior = PRIOR_NO

        if ray:
            return func()

        return _self._call_with_opengl_context(func)
Example #17
0
 def _get_mtl_obj(format, _self):
     # TODO mtl not implemented, always returns empty string
     if format == 'mtl':
         raise pymol.CmdException('.MTL export not implemented')
     i = {'mtl': 0, 'obj': 1}.get(format)
     return _self.get_mtl_obj()[i]
Example #18
0
def clean(selection,
          present='',
          state=-1,
          fix='',
          restrain='',
          method='mmff',
          async_=0,
          save_undo=1,
          message=None,
          _self=cmd_module,
          **kwargs):
    '''
DESCRIPTION

    Note: This operation is limited to 999 atoms.

    Run energy minimization on the given selection, using an MMFF94
    force field.

ARGUMENTS

    selection = str: atom selection to minimize

    present = str: selection of fixed atoms to restrain the minimization

    state = int: object state {default: -1 (current)}

    fix = UNUSED

    restraing = UNUSED

    method = UNUSED

    async = 0/1: run in separate thread {default: 0}

    save_undo = UNUSED

    message = Message to display during async minimization

EXAMPLE

    # minimize ligand in binding pocket
    clean organic, all within 8 of organic
    '''
    if int(state) == 0:
        raise pymol.CmdException('cleaning all states not supported')

    async_ = int(kwargs.pop('async', async_))

    if kwargs:
        raise pymol.CmdException('unknown argument: ' + ', '.join(kwargs))

    args = (selection, present, state, fix, restrain, method, save_undo,
            message, _self)

    if not async_:
        return _clean(*args)
    else:
        try:
            t = threading.Thread(target=_clean, args=args)
            t.setDaemon(1)
            t.start()
        except:
            traceback.print_exc()
        return 0
    def focal_blur(aperture=2.0,
                   samples=10,
                   ray=0,
                   filename='',
                   quiet=1,
                   _self=cmd):
        '''
DESCRIPTION

    Creates fancy figures by introducing a focal blur to the image.
    The object at the origin will be in focus.

USAGE

    focal_blur [ aperture [, samples [, ray [, filename ]]]]

ARGUMENTS

    aperture = float: aperture angle in degrees {default: 2.0}

    samples = int: number of images for averaging {default: 10}

    ray = 0/1: {default: 0}

    filename = str: write image to file {default: temporary}

AUTHORS

    Jarl Underhaug, Jason Vertrees and Thomas Holder

EXAMPLES

    focal_blur 3.0, 50
        '''
        from tempfile import mkdtemp
        from shutil import rmtree
        from math import sin, cos, pi, sqrt

        try:
            import Image
        except ImportError:
            try:
                from PIL import Image
            except ImportError:
                raise pymol.CmdException(
                    "Python Image Library (PIL) not available")

        aperture, samples = float(aperture), int(samples)
        ray, quiet = int(ray), int(quiet)
        avg = None
        handle_list = []

        # make sure we have a still image
        _self.mstop()
        _self.unset('rock')
        _self.unset('text')

        # we need at least two samples
        if samples < 2:
            raise pymol.CmdException("need samples > 1")

        # Get the orientation of the camera and the light
        view = _self.get_view()
        lights_names = [
            'light', 'light2', 'light3', 'light4', 'light5', 'light6',
            'light7', 'light8', 'light9'
        ]
        lights = [
            _self.get_setting_tuple(lights_names[i])[1]
            for i in range(_self.get_setting_int('light_count') - 1)
        ]

        # Create a temporary directory
        tmpdir = mkdtemp()

        try:
            # Rotate the camera and the light in order to create the blur
            for frame in range(samples):
                # Populate angles as Fermat's spiral
                theta = frame * pi * 110.0 / 144.0
                radius = 0.5 * aperture * sqrt(frame / float(samples - 1))
                x = cos(theta) * radius
                y = sin(theta) * radius
                xr = x / 180.0 * pi
                yr = y / 180.0 * pi

                # Rotate the camera
                _self.turn('x', x)
                _self.turn('y', y)

                # Rotate the light
                for light, light_name in zip(lights, lights_names):
                    ly = light[1] * cos(xr) - light[2] * sin(xr)
                    lz = light[2] * cos(xr) + light[1] * sin(xr)
                    lx = light[0] * cos(yr) + lz * sin(yr)
                    lz = lz * cos(yr) - lx * sin(yr)
                    _self.set(light_name, [lx, ly, lz])

                # Save the image to temporary directory
                curFile = "%s/frame-%04d.png" % (tmpdir, frame)
                _self.png(curFile, ray=ray, quiet=1)

                if not quiet:
                    print(" Created frame %i/%i (%0.0f%%)" %
                          (frame + 1, samples, 100 * (frame + 1) / samples))

                # Create the average/blured image
                handle = open(curFile, "rb")
                handle_list.append(handle)
                img = Image.open(handle)
                avg = Image.blend(avg, img, 1.0 / (frame + 1)) if avg else img
                del img

                # Return the camera and the light to the original orientation
                _self.set_view(view)
                for light, light_name in zip(lights, lights_names):
                    _self.set(light_name, light)

            if not filename:
                filename = '%s/avg.png' % (tmpdir)

            # Load the blured image
            avg.save(filename)
            _self.load(filename)

        finally:
            del avg

            # needed for Windows
            for handle in handle_list:
                handle.close()

            # Delete the temporary files
            rmtree(tmpdir)
Example #20
0
    def extra_fit(selection='(all)',
                  reference='',
                  method='align',
                  zoom=1,
                  quiet=0,
                  *,
                  _self=cmd,
                  **kwargs):
        '''
DESCRIPTION

    Like "intra_fit", but for multiple objects instead of
    multiple states.

ARGUMENTS

    selection = string: atom selection of multiple objects {default: all}

    reference = string: reference object name {default: first object in selection}

    method = string: alignment method (command that takes "mobile" and "target"
    arguments, like "align", "super", "cealign" {default: align}

    ... extra arguments are passed to "method"

SEE ALSO

    align, super, cealign, intra_fit, util.mass_align
            '''
        zoom, quiet = int(zoom), int(quiet)
        sele_name = _self.get_unused_name('_')
        _self.select(sele_name, selection, 0)
        models = _self.get_object_list(sele_name)

        if not reference:
            reference = models[0]
            models = models[1:]
        elif reference in models:
            models.remove(reference)
        else:
            _self.select(sele_name, reference, merge=1)

        if _self.is_string(method):
            if method in _self.keyword:
                method = _self.keyword[method][0]
            else:
                raise pymol.CmdException(method, 'Unknown method')

        for model in models:
            x = method(mobile='?%s & ?%s' % (sele_name, model),
                       target='?%s & ?%s' % (sele_name, reference),
                       **kwargs)
            if not quiet:
                if _self.is_sequence(x):
                    print('%-20s RMSD = %8.3f (%d atoms)' %
                          (model, x[0], x[1]))
                elif isinstance(x, float):
                    print('%-20s RMSD = %8.3f' % (model, x))
                elif isinstance(x, dict) and 'RMSD' in x:
                    natoms = x.get('alignment_length', 0)
                    suffix = (' (%s atoms)' % natoms) if natoms else ''
                    print('%-20s RMSD = %8.3f' % (model, x['RMSD']) + suffix)
                else:
                    print('%-20s' % (model, ))

        if zoom:
            _self.zoom(sele_name)
        _self.delete(sele_name)
    def callout(name,
                label,
                pos='',
                screen='auto',
                state=-1,
                color='front',
                quiet=1,
                _wiz=0,
                _self=cmd):
        '''
DESCRIPTION

    Create a new screen-stabilized callout object.

ARGUMENTS

    name = str: object name

    label = str: label text

    pos = str or list: anchor in model space as 3-float coord list or atom
    selection. If empty, don't draw an arrow. {default: }

    screen = str or list: position on screen as 2-float list between [-1,-1]
    (lower left) and [1,1] (upper right) or "auto" for smart placement.
    {default: auto}
        '''
        state, quiet = int(state), int(quiet)
        sele = ''

        if state == -1:
            state = _self.get_setting_int('state')

        if isinstance(pos, basestring):
            if ',' in pos:
                pos = _self.safe_list_eval(pos)
            else:
                sele, pos = pos, None

        if isinstance(screen, basestring):
            if screen == 'auto':
                screen = (0.0, 0.0)
            else:
                screen = _self.safe_list_eval(screen)

        if len(screen) == 2:
            screen += (0.0, )
        elif len(screen) != 3:
            raise pymol.CmdException('invalid screen argument')

        if isinstance(screen[0], int):
            relative_mode = 2  # pixels
        else:
            relative_mode = 1  # [-1.0 .. +1.0]

        if not name:
            name = _self.get_unused_name('callout', 1)

        _self.pseudoatom(name,
                         sele,
                         'CALL',
                         'CAL',
                         str(_self.count_atoms('?' + name) + 1),
                         'C',
                         'CALL',
                         'PS',
                         0.5,
                         color=color,
                         label=label,
                         pos=pos,
                         state=state,
                         quiet=quiet)

        expr = ('('
                's["label_relative_mode"],'
                's["label_connector"],'
                's["label_connector_color"],'
                's["label_color"],'
                ') = (%d, %s, "default", "default",)' % (
                    relative_mode,
                    bool(pos or sele),
                ))

        expr_state = ('(' 's["label_screen_point"],' ') = (%s,)' % (screen, ))

        _self.alter("last " + name, expr)
        _self.alter_state(state, "last " + name, expr_state)

        if int(_wiz):
            _self.wizard('labeledit', 'last ' + name)
Example #22
0
def load_mtz_cctbx(filename,
                   prefix='',
                   amplitudes='',
                   phases='',
                   quiet=1,
                   _self=pymol.cmd):
    '''
DESCRIPTION

    Load maps from an MTZ file, using iotbx (via "cctbx.python").

    Map objects will be named: <prefix>_<amplitudes>_<phases>

ARGUMENTS

    filename = str: path to mtz file

    prefix = str: object name prefix for new map objects

    amplitudes = str: amplitudes column label (optional). If not given,
    load all maps. If given, the 'phases' argument is required as well. 

    phases = str: phases column label (required if and only if
    amplitudes is given as well)
    '''
    import subprocess
    import shutil

    if not prefix:
        prefix = os.path.basename(filename).rpartition('.')[0]

    args = [filename, prefix, amplitudes, phases]

    try:
        # try with "cctbx.python"
        process = subprocess.Popen(['cctbx.python', this_file] + args,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE)
        stdout, stderr = process.communicate()
        if stderr:
            raise pymol.CmdException(stderr)
        outdir = stdout.strip()
        if not isinstance(outdir, str):
            outdir = outdir.decode()
    except OSError:
        try:
            # try inside this Python interpreter
            outdir = mtz2ccp4maps(*args)
        except ImportError:
            raise pymol.CmdException(
                "can't import iotbx and can't run cctbx.python")

    # normalization is done by apply_sigma_scaling()
    normalize = _self.get_setting_int('normalize_ccp4_maps')
    if normalize:
        _self.set('normalize_ccp4_maps', 0)

    for mapfilename in os.listdir(outdir):
        _self.load(os.path.join(outdir, mapfilename), quiet=quiet)

    if normalize:
        _self.set('normalize_ccp4_maps', normalize)

    shutil.rmtree(outdir)
Example #23
0
    def set_key(key, fn=None, arg=(), kw={}, _self=cmd):
        '''
DESCRIPTION

    "set_key" binds a specific python function to a key press.

    New in PyMOL 1.6.1: second argument can also be a string in PyMOL
    command syntax.

USAGE

    set_key key, command

EXAMPLE

    set_key F1, as cartoon, polymer; as sticks, organic

PYMOL API (ONLY)

    cmd.set_key( string key, function fn, tuple arg=(), dict kw={})

PYTHON EXAMPLE

    from pymol import cmd

    def color_blue(object): cmd.color("blue",object)

    cmd.set_key( 'F1' , color_blue, ( "object1" ) )
    // would turn object1 blue when the F1 key is pressed and

    cmd.set_key( 'F2' , color_blue, ( "object2" ) )
    // would turn object2 blue when the F2 key is pressed.

    cmd.set_key( 'CTRL-C' , cmd.zoom )   
    cmd.set_key( 'ALT-A' , cmd.turn, ('x',90) )

KEYS WHICH CAN BE REDEFINED

    F1 to F12
    left, right, pgup, pgdn, home, insert
    CTRL-A to CTRL-Z 
    ALT-0 to ALT-9, ALT-A to ALT-Z

SEE ALSO

    button, alias
        '''
        if fn is None:

            def decorator(func):
                set_key(key, func, arg, kw, _self)
                return func

            return decorator

        if is_string(fn):
            if arg or kw:
                raise ValueError('arg and kw must be empty if fn is string')
        else:
            fn = (fn, arg, kw)

        mod, _, pat = key.rpartition('-')

        if mod not in internal.modifier_keys:
            raise pymol.CmdException("not a valid modifier key: '%s'." % mod)

        if len(pat) > 1:
            if pat[0] != 'F':
                pat = pat.lower()
                key = pat if not mod else (mod + '-' + pat)

            if pat not in internal.special_key_names:
                raise pymol.CmdException("special '%s' key not found." % pat)
        elif not mod:
            raise pymol.CmdException("Can't map regular letters.")
        elif mod == 'SHFT':
            raise pymol.CmdException("Can't map regular letters with SHFT.")

        _self.key_mappings[key] = fn

        return DEFAULT_SUCCESS
Example #24
0
def assign_stereo(selection='all',
                  state=-1,
                  method='',
                  quiet=1,
                  prop='stereo',
                  _self=cmd):
    '''
DESCRIPTION

    Assign "stereo" atom property (R/S stereochemistry).

    Requires either a Schrodinger Suite installation (SCHRODINGER
    environment variable set) or RDKit (rdkit Python module).

USAGE

    assign_stereo [selection [, state [, method ]]]

ARGUMENTS

    selection = str: atom selection {default: all}

    state = int: object state {default: -1 (current)}

    method = schrodinger or rdkit: {default: try both}
    '''
    state, quiet = int(state), int(quiet)

    # method check
    if method and method not in methods:
        raise pymol.CmdException("method '{}' not in {}".format(
            method, list(methods)))

    # alt code check
    alt_codes = set()
    cmd.iterate('({})&!alt ""'.format(selection),
                'alt_codes.add(alt)',
                space={'alt_codes': alt_codes})
    if len(alt_codes) > 1:
        alt_codes = sorted(alt_codes)
        selection = '({}) & alt +{}'.format(selection, alt_codes[0])
        print(' Warning: only using first alt code of: {}'.format(alt_codes))

    molstr = _self.get_str('mol', selection, state)

    if method:
        labels = methods[method](molstr)
    else:
        for method in ('rdkit', 'schrodinger'):
            try:
                labels = methods[method](molstr)
            except BaseException as e:
                print(' Method "{}" not available ({})'.format(
                    method,
                    str(e).strip()))
            else:
                print(' Using method={}'.format(method))
                break
        else:
            raise pymol.CmdException('need SCHRODINGER or rdkit')

    # apply new labels
    _self.alter_state(state,
                      selection,
                      prop + ' = next(label_iter)',
                      space={
                          'label_iter': iter(labels),
                          'next': next
                      })
Example #25
0
def elbow_angle(obj, light='L', heavy='H', limit_l=107, limit_h=113, draw=0):
    """

DESCRIPTION

    Calculates the integer elbow angle of an antibody Fab complex and
    optionally draws a graphical representation of the vectors used to
    determine the angle.

ARGUMENTS

    obj = string: object

    light/heavy = strings: chain ID of light and heavy chains, respectively

    limit_l/limit_h = integers: residue numbers of the last residue in the
    light and heavy chain variable domains, respectively

    draw = boolean: Choose whether or not to draw the angle visualization

REQUIRES: com.py, transformations.py, numpy (see above)


    """

    # store current view
    orig_view = cmd.get_view()

    limit_l = int(limit_l)
    limit_h = int(limit_h)
    draw = int(draw)

    # for temp object names
    tmp_prefix = "tmp_elbow_"

    prefix = tmp_prefix + obj + '_'

    # names
    vl = prefix + 'VL'
    vh = prefix + 'VH'
    cl = prefix + 'CL'
    ch = prefix + 'CH'

    # selections
    vl_sel = 'polymer and %s and chain %s and resi 1-%i' % (obj, light,
                                                            limit_l)
    vh_sel = 'polymer and %s and chain %s and resi 1-%i' % (obj, heavy,
                                                            limit_h)
    cl_sel = 'polymer and %s and chain %s and not resi 1-%i' % (obj, light,
                                                                limit_l)
    ch_sel = 'polymer and %s and chain %s and not resi 1-%i' % (obj, heavy,
                                                                limit_h)
    v_sel = '((' + vl_sel + ') or (' + vh_sel + '))'
    c_sel = '((' + cl_sel + ') or (' + ch_sel + '))'

    # create temp objects
    cmd.create(vl, vl_sel)
    cmd.create(vh, vh_sel)
    cmd.create(cl, cl_sel)
    cmd.create(ch, ch_sel)

    # superimpose vl onto vh, calculate axis and angle
    Rv = calc_super_matrix(vl, vh)
    angle_v, direction_v, point_v = transformations.rotation_from_matrix(Rv)

    # superimpose cl onto ch, calculate axis and angle
    Rc = calc_super_matrix(cl, ch)
    angle_c, direction_c, point_c = transformations.rotation_from_matrix(Rc)

    # delete temporary objects
    cmd.delete(vl)
    cmd.delete(vh)
    cmd.delete(cl)
    cmd.delete(ch)

    # if dot product is positive, angle is acute
    if (numpy.dot(direction_v, direction_c) > 0):
        direction_c = direction_c * -1  # ensure angle is > 90 (need to standardize this)

        # TODO: make both directions point away from the elbow axis.

    elbow = int(
        numpy.degrees(numpy.arccos(numpy.dot(direction_v, direction_c))))
    # while (elbow < 90):
    # elbow = 180 - elbow   # limit to physically reasonable range

    # compare the direction_v and direction_c axes to the vector defined by
    # the C-alpha atoms of limit_l and limit_h of the original fab
    hinge_l_sel = "%s//%s/%s/CA" % (obj, light, limit_l)
    hinge_h_sel = "%s//%s/%s/CA" % (obj, heavy, limit_h)

    try:
        hinge_l = cmd.get_atom_coords(hinge_l_sel)
        hinge_h = cmd.get_atom_coords(hinge_h_sel)
    except pymol.CmdException:
        # Either hinge_l_sel or hinge_h_sel atom did not exist.
        raise pymol.CmdException(
            'Unable to calculate elbow angle. Please check '
            'your limit and chain selections and try again.')
    hinge_vec = numpy.array(hinge_h) - numpy.array(hinge_l)

    test = numpy.dot(hinge_vec, numpy.cross(direction_v, direction_c))
    if (test > 0):
        elbow = 360 - elbow

    print("    Elbow angle: %i degrees" % elbow)

    if (draw == 1):
        # there is probably a more elegant way to do this, but
        # it works so I'm not going to mess with it for now

        pre = obj + '_elbow_'

        # draw hinge vector
        cmd.pseudoatom(pre + "hinge_l", pos=hinge_l)
        cmd.pseudoatom(pre + "hinge_h", pos=hinge_h)
        cmd.distance(pre + "hinge_vec", pre + "hinge_l", pre + "hinge_h")
        cmd.set("dash_gap", 0)

        # draw the variable domain axis
        com_v = COM(v_sel)
        start_v = [a - 10 * b for a, b in zip(com_v, direction_v)]
        end_v = [a + 10 * b for a, b in zip(com_v, direction_v)]
        cmd.pseudoatom(pre + "start_v", pos=start_v)
        cmd.pseudoatom(pre + "end_v", pos=end_v)
        cmd.distance(pre + "v_vec", pre + "start_v", pre + "end_v")

        # draw the constant domain axis
        com_c = COM(c_sel)
        start_c = [a - 10 * b for a, b in zip(com_c, direction_c)]
        end_c = [a + 10 * b for a, b in zip(com_c, direction_c)]
        cmd.pseudoatom(pre + "start_c", pos=start_c)
        cmd.pseudoatom(pre + "end_c", pos=end_c)
        cmd.distance(pre + "c_vec", pre + "start_c", pre + "end_c")

        # customize appearance
        cmd.hide("labels", pre + "hinge_vec")
        cmd.hide("labels", pre + "v_vec")
        cmd.hide("labels", pre + "c_vec")
        cmd.color("green", pre + "hinge_l")
        cmd.color("red", pre + "hinge_h")
        cmd.color("black", pre + "hinge_vec")
        cmd.color("black", pre + "start_v")
        cmd.color("black", pre + "end_v")
        cmd.color("black", pre + "v_vec")
        cmd.color("black", pre + "start_c")
        cmd.color("black", pre + "end_c")
        cmd.color("black", pre + "c_vec")
        # draw spheres
        cmd.show("spheres", pre + "hinge_l or " + pre + "hinge_h")
        cmd.show("spheres", pre + "start_v or " + pre + "start_c")
        cmd.show("spheres", pre + "end_v or " + pre + "end_c")
        cmd.set("sphere_scale", 2)
        cmd.set("dash_gap", 0, pre + "hinge_vec")
        cmd.set("dash_width", 5)
        cmd.set("dash_radius", 0.3)

        # group drawing objects
        cmd.group(pre, pre + "*")

    # restore original view
    cmd.set_view(orig_view)

    return 0
Example #26
0
def attach_amino_acid(selection,amino_acid,center=0,animate=-1,object="",hydro=-1,ss=-1,_self=cmd):
    '''
ARGUMENTS

    selection = str: named selection of single N or C atom

    amino_acid = str: fragment name to load from fragment library

    center = bool: center on new terminus (pk1)

    animate = int: animate centering

    object = str: name of new object (if selection is none)

    hydro = int (-1/0/1): keep hydrogens

    ss = int: Secondary structure 1=alpha helix, 2=antiparallel beta, 3=parallel beta, 4=flat
    '''
#    global ___total, ___seg1, ___seg2, ___seg3, ___pass, ___last
#    ___mark0 = ___time()
#    ___mark1 = ___time()
#    ___mark2 = ___time()
#    ___entry = ___time()
    r = DEFAULT_SUCCESS
    ss = int(ss)
    center = int(center)

    if hydro<0:
        hydro = not int(_self.get_setting_boolean("auto_remove_hydrogens"))
    if (selection not in _self.get_names('all')
            if selection == 'pk1' # legacy, calling functions should pass '?pk1'
            else _self.count_atoms(selection) == 0):
        if object == "":
            object = amino_acid
        # create new object 
        if amino_acid in _self.get_names("objects"):
            raise pymol.CmdException("an object with that name already exists")
        r = _self.fragment(amino_acid,object)
        if not hydro:
            _self.remove("(hydro and %s)"%object)
        if _self.count_atoms("((%s) and name C)"%object):
            _self.edit("((%s) and name C)"%object)
        elif _self.count_atoms("((%s) and name N)"%object):
            _self.edit("((%s) and name N)"%object)
    elif _self.select(tmp_connect,"(%s) & elem N,C"%selection) != 1:
        _self.delete(tmp_wild)
        raise pymol.CmdException("invalid connection point: must be one atom, name N or C.")
    elif amino_acid in ["nhh","nme"] and _self.select(tmp_connect,"(%s) & elem C"%selection) != 1:
        _self.delete(tmp_wild)
        raise pymol.CmdException("invalid connection point: must be C for residue '%s'"%(amino_acid))
    elif amino_acid in ["ace"] and _self.select(tmp_connect,"(%s) & elem N"%selection) != 1:
        _self.delete(tmp_wild)
        raise pymol.CmdException("invalid connection point: must be N for residue '%s'"%(amino_acid))
    else:
        if ss<0:
            ss = _self.get_setting_int("secondary_structure")
        if ss:
            if ss==1: # helix
                phi=-57.0
                psi=-47.0
            elif ss==2: # antipara-beta
                phi=-139.0
                psi=135.0
            elif ss==3: # para-beta
                phi=-119.0
                psi=113.0
            else:
                phi=180.0
                psi=180.0
        _self.fragment(amino_acid,tmp_editor, origin=0)
        if _self.count_atoms("elem N",domain=tmp_connect):
            tmp = [ None ]
            _self.iterate(tmp_connect,"tmp[0]=resv", space={ 'tmp' : tmp })
            tmp[0] = str(tmp[0]-1) # counting down
            _self.alter(tmp_editor,"resi=tmp[0]",space={ 'tmp' : tmp})
            _self.set_geometry(tmp_connect, 3, 3) # make nitrogen planar
            _self.fuse("(%s and name C)"%(tmp_editor),tmp_connect,2)
            _self.select(tmp_domain, "byresi (pk1 | pk2)")

            if not hydro:
                _self.remove("(pkmol and hydro)")

            if ((_self.select(tmp1,"?pk1",domain=tmp_domain)==1) and
                (_self.select(tmp2,"?pk2",domain=tmp_domain)==1)):

                if ((_self.select(tmp3,"(name CA,CH3 & nbr. ?pk1)",domain=tmp_domain)==1) and
                    (_self.select(tmp4,"(name CA,CH3 & nbr. ?pk2)",domain=tmp_domain)==1)):
                    _self.set_dihedral(tmp4,tmp2,tmp1,tmp3,180.0) 

                if hydro:
                    _self.h_fix(tmp2) # fix hydrogen position

                if ss:
                    if amino_acid[0:3]!='pro':
                        if ((_self.select(tmp4,
                                          "(!(resn PRO) & name C  & nbr. (name CA & nbr. "+tmp2+"))",
                                          domain=tmp_domain)==1) and
                            (_self.select(tmp3,
                                          "(!(resn PRO) & name CA & nbr. "+tmp2+")",
                                          domain=tmp_domain)==1)):
                            _self.set_dihedral( # PHI
                                tmp4, # C
                                tmp3, # CA 
                                tmp2, # N
                                tmp1, # C
                                phi)

                    if ((_self.select(tmp4,"(name N & nbr. (name CA & nbr. "+tmp1+"))",
                                      domain=tmp_domain)==1) and
                        (_self.select(tmp3,"(name CA & nbr. "+tmp1+")",domain=tmp_domain)==1)):
                        _self.set_dihedral( # PSI (n-1)
                            tmp2, # N
                            tmp1, # C
                            tmp3, # CA
                            tmp4, # N
                            psi)

            sele = ("(name N & (byres nbr. %s) &! (byres %s))"% (tmp_connect,tmp_connect))
            if _self.select(tmp1,sele,domain=tmp_domain):
                _self.edit(tmp1)
                if center:
                    _self.center(tmp1,animate=animate)
        elif _self.count_atoms("elem C",domain=tmp_connect): # forward
            tmp = [ None ]
            _self.iterate(tmp_connect,"tmp[0]=resv", space={ 'tmp' : tmp })
            tmp[0] = str(tmp[0]+1) # counting up
            _self.alter(tmp_editor,"resi=tmp[0]",space={ 'tmp' : tmp})
            _self.set_geometry(tmp_editor + " & name N", 3, 3) # make nitrogen planar
            _self.fuse("(%s and name N)"%tmp_editor,tmp_connect,2)
            _self.select(tmp_domain, "byresi (pk1 | pk2)")

            if not hydro:
                _self.remove("(pkmol and hydro)") 

            if (( _self.select(tmp1,"?pk1",domain=tmp_domain)==1) and
                ( _self.select(tmp2,"?pk2",domain=tmp_domain)==1)):

#                ___mark1 = ___time()
                if ((_self.select(tmp3,"(name CA,CH3 & nbr. ?pk1)",domain=tmp_domain)==1) and
                    (_self.select(tmp4,"(name CA,CH3 & nbr. ?pk2)",domain=tmp_domain)==1)):
                    _self.set_dihedral(tmp4,tmp2,tmp1,tmp3,180.0) 
                if hydro:
                    _self.h_fix("pk1") # fix hydrogen position
                if ss:
                    if hydro and amino_acid[0:3]=='nhh': # fix amide hydrogens
                        if ((_self.select(tmp3,"(name H1 & nbr. "+tmp1+")",domain=tmp_domain)==1) and
                            (_self.select(tmp4,"(name O & nbr. "+tmp2+")",domain=tmp_domain)==1)):
                            _self.set_dihedral(
                                tmp4, # O
                                tmp2, # C
                                tmp1, # N
                                tmp3, # H1
                                180)
                    if amino_acid[0:3]!='pro':
                        if ((_self.select(tmp3,"(name CA & nbr. "+tmp1+")",domain=tmp_domain)==1) and
                            (_self.select(tmp4,"(name C & nbr. (name CA & nbr. "+tmp1+"))",domain=tmp_domain)==1)):
                            _self.set_dihedral( # PHI
                                tmp2, # C
                                tmp1, # N
                                tmp3, # CA 
                                tmp4, # C
                                phi)
                    if ((_self.select(tmp3,"(name CA & nbr. "+tmp2+")",domain=tmp_domain)==1) and
                        (_self.select(tmp4,"(name N & nbr. (name CA & nbr. "+tmp2+"))",domain=tmp_domain)==1)):
                        _self.set_dihedral( # PSI (n-1)
                            tmp4, # N
                            tmp3, # CA
                            tmp2, # C
                            tmp1, # N
                            psi)
#            ___mark2 = ___time()
            sele = ("(name C & (byres nbr. %s) & !(byres %s))"% (tmp_connect,tmp_connect))
            if _self.select(tmp1,sele,domain=tmp_domain):
                _self.edit(tmp1)
                if center:
                    _self.center(tmp1,animate=animate)
            else:
                _self.unpick()
        elif _self.count_atoms("((%s) and elem H)"%selection):
            _self.delete(tmp_wild)
            raise pymol.CmdException("please pick a nitrogen or carbonyl carbon to grow from.")
        else:
            _self.delete(tmp_wild)
            raise pymol.CmdException("unable to attach fragment.")
    _self.delete(tmp_wild)

#    ___exit = ___time()
#    ___seg1 = ___seg1 + ___mark1 - ___entry
#    ___seg2 = ___seg2 + ___mark2 - ___mark1
#    ___seg3 = ___seg3 + ___exit  - ___mark2
#    ___total = ___total + ___exit - ___entry
#    ___pass = ___pass + 1
#    print "%0.3f %0.3f %0.3f / %0.3f + %0.3f + %0.3f = %0.3f vs %0.3f"%(___seg1/___total,___seg2/___total,___seg3/___total,
#                                                          ___seg1/___pass, ___seg2/___pass, ___seg3/___pass,
#                                                          ___total/___pass, (___time()-___last) - (___exit - ___entry))
#    ___last = ___time()

    return r