def besttype(x):
    """Convert string x to the most useful type, i.e. int, float or unicode string.

    If x is a quoted string (single or double quotes) then the quotes
    are stripped and the enclosed string returned.

    .. Note::

       Strings will be returned as Unicode strings (using :func:`to_unicode`).

    .. versionchanged:: 0.7.0
       removed `encoding keyword argument
    """
    x = to_unicode(x)  # make unicode as soon as possible
    try:
        x = x.strip()
    except AttributeError:
        pass
    m = re.match(r"""['"](?P<value>.*)["']$""", x)
    if m is None:
        # not a quoted string, try different types
        for converter in int, float, to_unicode:   # try them in increasing order of lenience
            try:
                return converter(x)
            except ValueError:
                pass
    else:
        # quoted string
        x = to_unicode(m.group('value'))
    return x

def besttype ( x ) : x = to_unicode ( x ) try : x = x . strip ( ) except AttributeError : pass m = re . match ( r , x ) if m is None : for converter in int , float , to_unicode : try : return converter ( x ) except ValueError : pass else : x = to_unicode ( m . group ( 'value' ) ) return x

Convert string x to the most useful type i . e . int float or unicode string .


def to_int64(a):
    """Return view of the recarray with all int32 cast to int64."""
    # build new dtype and replace i4 --> i8
    def promote_i4(typestr):
        if typestr[1:] == 'i4':
            typestr = typestr[0]+'i8'
        return typestr

    dtype = [(name, promote_i4(typestr)) for name,typestr in a.dtype.descr]
    return a.astype(dtype)

def to_int64 ( a ) : def promote_i4 ( typestr ) : if typestr [ 1 : ] == 'i4' : typestr = typestr [ 0 ] + 'i8' return typestr dtype = [ ( name , promote_i4 ( typestr ) ) for name , typestr in a . dtype . descr ] return a . astype ( dtype )

Return view of the recarray with all int32 cast to int64 .


def irecarray_to_py(a):
    """Slow conversion of a recarray into a list of records with python types.

    Get the field names from :attr:`a.dtype.names`.

    :Returns: iterator so that one can handle big input arrays
    """
    pytypes = [pyify(typestr) for name,typestr in a.dtype.descr]
    def convert_record(r):
        return tuple([converter(value) for converter, value in zip(pytypes,r)])
    return (convert_record(r) for r in a)

def irecarray_to_py ( a ) : pytypes = [ pyify ( typestr ) for name , typestr in a . dtype . descr ] def convert_record ( r ) : return tuple ( [ converter ( value ) for converter , value in zip ( pytypes , r ) ] ) return ( convert_record ( r ) for r in a )

Slow conversion of a recarray into a list of records with python types .


def col(self, c):
        """Parse colour specification"""
        m = self.COLOUR.search(c)
        if not m:
            self.logger.fatal("Cannot parse colour specification %r.", c)
            raise ParseError("XPM reader: Cannot parse colour specification {0!r}.".format(c))
        value = m.group('value')
        color = m.group('symbol')
        self.logger.debug("%s: %s %s\n", c.strip(), color, value)
        return color, value


def transform_args(self, *args, **kwargs):
        """Transform arguments and return them as a list suitable for Popen."""
        options = []
        for option,value in kwargs.items():
            if not option.startswith('-'):
                # heuristic for turning key=val pairs into options
                # (fails for commands such as 'find' -- then just use args)
                if len(option) == 1:
                    option = '-' + option         # POSIX style
                else:
                    option = '--' + option        # GNU option
            if value is True:
                options.append(option)
                continue
            elif value is False:
                raise ValueError('A False value is ambiguous for option {0!r}'.format(option))

            if option[:2] == '--':
                options.append(option + '=' + str(value))    # GNU option
            else:
                options.extend((option, str(value)))         # POSIX style
        return options + list(args)

Transform arguments and return them as a list suitable for Popen .


def help(self, long=False):
        """Print help; same as using ``?`` in ``ipython``. long=True also gives call signature."""
        print("\ncommand: {0!s}\n\n".format(self.command_name))
        print(self.__doc__)
        if long:
            print("\ncall method: command():\n")
            print(self.__call__.__doc__)

def
help
(
self
,
long
=
False
)
:
print
(
"\ncommand: {0!s}\n\n"
.
format
(
self
.
command_name
)
)
print
(
self
.
__doc__
)
if
long
:
print
(
"\ncall method: command():\n"
)
print
(
self
.
__call__
.
__doc__
)

Print help ; same as using ? in ipython . long = True also gives call signature .


def _combineargs(self, *args, **kwargs):
        """Add switches as 'options' with value True to the options dict."""
        d = {arg: True for arg in args}   # switches are kwargs with value True
        d.update(kwargs)
        return d

def _combineargs ( self , * args , ** kwargs ) : d = { arg : True for arg in args } d . update ( kwargs ) return d

Add switches as options with value True to the options dict .


def _build_arg_list(self, **kwargs):
        """Build list of arguments from the dict; keys must be valid  gromacs flags."""
        arglist = []
        for flag, value in kwargs.items():
            # XXX: check flag against allowed values
            flag = str(flag)
            if flag.startswith('_'):
                flag = flag[1:]                 # python-illegal keywords are '_'-quoted
            if not flag.startswith('-'):
                flag = '-' + flag               # now flag is guaranteed to start with '-'
            if value is True:
                arglist.append(flag)            # simple command line flag
            elif value is False:
                if flag.startswith('-no'):
                    # negate a negated flag ('noX=False' --> X=True --> -X ... but who uses that?)
                    arglist.append('-' + flag[3:])
                else:
                    arglist.append('-no' + flag[1:])  # gromacs switches booleans by prefixing 'no'
            elif value is None:
                pass                            # ignore flag = None
            else:
                try:
                    arglist.extend([flag] + value) # option with value list
                except TypeError:
                    arglist.extend([flag, value])  # option with single value
        return list(map(str, arglist))

def _build_arg_list ( self , ** kwargs ) : arglist = [ ] for flag , value in kwargs . items ( ) : flag = str ( flag ) if flag . startswith ( '_' ) : flag = flag [ 1 : ] if not flag . startswith ( '-' ) : flag = '-' + flag if value is True : arglist . append ( flag ) elif value is False : if flag . startswith ( '-no' ) : arglist . append ( '-' + flag [ 3 : ] ) else : arglist . append ( '-no' + flag [ 1 : ] ) elif value is None : pass else : try : arglist . extend ( [ flag ] + value ) except TypeError : arglist . extend ( [ flag , value ] ) return list ( map ( str , arglist ) )

Build list of arguments from the dict ; keys must be valid gromacs flags .


def transform_args(self,*args,**kwargs):
        """Combine arguments and turn them into gromacs tool arguments."""
        newargs = self._combineargs(*args, **kwargs)
        return self._build_arg_list(**newargs)

def transform_args ( self , * args , ** kwargs ) : newargs = self . _combineargs ( * args , ** kwargs ) return self . _build_arg_list ( ** newargs )

Combine arguments and turn them into gromacs tool arguments .


def _get_gmx_docs(self):
        """Extract standard gromacs doc

        Extract by running the program and chopping the header to keep from
        'DESCRIPTION' onwards.
        """
        if self._doc_cache is not None:
            return self._doc_cache

        try:
            logging.disable(logging.CRITICAL)
            rc, header, docs = self.run('h', stdout=PIPE, stderr=PIPE, use_input=False)
        except:
            logging.critical("Invoking command {0} failed when determining its doc string. Proceed with caution".format(self.command_name))
            self._doc_cache = "(No Gromacs documentation available)"
            return self._doc_cache
        finally:
            # ALWAYS restore logging...
            logging.disable(logging.NOTSET)

        # The header is on STDOUT and is ignored. The docs are read from STDERR in GMX 4.
        m = re.match(self.doc_pattern, docs, re.DOTALL)

        if m is None:
            # In GMX 5, the opposite is true (Grrr)
            m = re.match(self.doc_pattern, header, re.DOTALL)
            if m is None:
                self._doc_cache = "(No Gromacs documentation available)"
                return self._doc_cache

        self._doc_cache = m.group('DOCS')
        return self._doc_cache


def autoconvert(s):
    """Convert input to a numerical type if possible.

    1. A non-string object is returned as it is
    2. Try conversion to int, float, str.
    """
    if type(s) is not str:
        return s
    for converter in int, float, str:   # try them in increasing order of lenience
        try:
            s = [converter(i) for i in s.split()]
            if len(s) == 1:
                return s[0]
            else:
                return numpy.array(s)
        except (ValueError, AttributeError):
            pass
    raise ValueError("Failed to autoconvert {0!r}".format(s))

def
autoconvert
(
s
)
:
if
type
(
s
)
is
not
str
:
return
s
for
converter
in
int
,
float
,
str
:
try
:
s
=
[
converter
(
i
)
for
i
in
s
.
split
(
)
]
if
len
(
s
)
==
1
:
return
s
[
0
]
else
:
return
numpy
.
array
(
s
)
except
(
ValueError
,
AttributeError
)
:
pass
raise
ValueError
(
"Failed to autoconvert {0!r}"
.
format
(
s
)
)

Convert input to a numerical type if possible .


def isstream(obj):
    """Detect if `obj` is a stream.

    We consider anything a stream that has the methods

    - ``close()``

    and either set of the following

    - ``read()``, ``readline()``, ``readlines()``
    - ``write()``, ``writeline()``, ``writelines()``

    :Arguments:
      *obj*
          stream or str

    :Returns:
      *bool*, ``True`` if `obj` is a stream, ``False`` otherwise

    .. SeeAlso::
       :mod:`io`

    .. versionadded:: 0.7.1
    """
    signature_methods = ("close",)
    alternative_methods = (
        ("read", "readline", "readlines"),
        ("write", "writeline", "writelines"))

    # Must have ALL the signature methods
    for m in signature_methods:
        if not hasmethod(obj, m):
            return False
    # Must have at least one complete set of alternative_methods
    alternative_results = [
        numpy.all([hasmethod(obj, m) for m in alternatives])
        for alternatives in alternative_methods]
    return numpy.any(alternative_results)

def isstream ( obj ) : signature_methods = ( "close" , ) alternative_methods = ( ( "read" , "readline" , "readlines" ) , ( "write" , "writeline" , "writelines" ) ) for m in signature_methods : if not hasmethod ( obj , m ) : return False alternative_results = [ numpy . all ( [ hasmethod ( obj , m ) for m in alternatives ] ) for alternatives in alternative_methods ] return numpy . any ( alternative_results )


def convert_aa_code(x):
    """Converts between 3-letter and 1-letter amino acid codes."""
    if len(x) == 1:
        return amino_acid_codes[x.upper()]
    elif len(x) == 3:
        return inverse_aa_codes[x.upper()]
    else:
        raise ValueError("Can only convert 1-letter or 3-letter amino acid codes, "
                         "not %r" % x)

Converts between 3 - letter and 1 - letter amino acid codes .


def in_dir(directory, create=True):
    """Context manager to execute a code block in a directory.

    * The directory is created if it does not exist (unless
      create=False is set)
    * At the end or after an exception code always returns to
      the directory that was the current directory before entering
      the block.
    """
    startdir = os.getcwd()
    try:
        try:
            os.chdir(directory)
            logger.debug("Working in {directory!r}...".format(**vars()))
        except OSError as err:
            if create and err.errno == errno.ENOENT:
                os.makedirs(directory)
                os.chdir(directory)
                logger.info("Working in {directory!r} (newly created)...".format(**vars()))
            else:
                logger.exception("Failed to start working in {directory!r}.".format(**vars()))
                raise
        yield os.getcwd()
    finally:
        os.chdir(startdir)

Context manager to execute a code block in a directory .


def unlink_f(path):
    """Unlink path but do not complain if file does not exist."""
    try:
        os.unlink(path)
    except OSError as err:
        if err.errno != errno.ENOENT:
            raise

def unlink_f ( path ) : try : os . unlink ( path ) except OSError as err : if err . errno != errno . ENOENT : raise

Unlink path but do not complain if file does not exist .


def remove_legend(ax=None):
    """Remove legend for axes or gca.

    See http://osdir.com/ml/python.matplotlib.general/2005-07/msg00285.html
    """
    from pylab import gca, draw
    if ax is None:
        ax = gca()
    ax.legend_ = None
    draw()

def remove_legend ( ax = None ) : from pylab import gca , draw if ax is None : ax = gca ( ) ax . legend_ = None draw ( )


def filename(self,filename=None,ext=None,set_default=False,use_my_ext=False):
        """Supply a file name for the class object.

        Typical uses::

           fn = filename()             ---> <default_filename>
           fn = filename('name.ext')   ---> 'name'
           fn = filename(ext='pickle') ---> <default_filename>'.pickle'
           fn = filename('name.inp','pdf') --> 'name.pdf'
           fn = filename('foo.pdf',ext='png',use_my_ext=True) --> 'foo.pdf'

        The returned filename is stripped of the extension
        (``use_my_ext=False``) and if provided, another extension is
        appended. Chooses a default if no filename is given.

        Raises a ``ValueError`` exception if no default file name is known.

        If ``set_default=True`` then the default filename is also set.

        ``use_my_ext=True`` lets the suffix of a provided filename take
        priority over a default ``ext`` tension.

        .. versionchanged:: 0.3.1
           An empty string as *ext* = "" will suppress appending an extension.
        """
        if filename is None:
            if not hasattr(self,'_filename'):
                self._filename = None        # add attribute to class
            if self._filename:
                filename = self._filename
            else:
                raise ValueError("A file name is required because no default file name was defined.")
            my_ext = None
        else:
            filename, my_ext = os.path.splitext(filename)
            if set_default:                  # replaces existing default file name
                self._filename = filename
        if my_ext and use_my_ext:
            ext = my_ext
        if ext is not None:
            if ext.startswith(os.extsep):
                ext = ext[1:]  # strip a dot to avoid annoying mistakes
            if ext != "":
                filename = filename + os.extsep + ext
        return filename

Supply a file name for the class object .


def check_file_exists(self, filename, resolve='exception', force=None):
        """If a file exists then continue with the action specified in ``resolve``.

        ``resolve`` must be one of

        "ignore"
              always return ``False``
        "indicate"
              return ``True`` if it exists
        "warn"
              indicate and issue a :exc:`UserWarning`
        "exception"
              raise :exc:`IOError` if it exists

        Alternatively, set *force* for the following behaviour (which
        ignores *resolve*):

        ``True``
              same as *resolve* = "ignore" (will allow overwriting of files)
        ``False``
              same as *resolve* = "exception" (will prevent overwriting of files)
        ``None``
              ignored, do whatever *resolve* says
        """
        def _warn(x):
            msg = "File {0!r} already exists.".format(x)
            logger.warn(msg)
            warnings.warn(msg)
            return True
        def _raise(x):
            msg = "File {0!r} already exists.".format(x)
            logger.error(msg)
            raise IOError(errno.EEXIST, x, msg)
        solutions = {'ignore': lambda x: False,      # file exists, but we pretend that it doesn't
                     'indicate': lambda x: True,     # yes, file exists
                     'warn': _warn,
                     'warning': _warn,
                     'exception': _raise,
                     'raise': _raise,
                     }

        if force is True:
            resolve = 'ignore'
        elif force is False:
            resolve = 'exception'

        if not os.path.isfile(filename):
            return False
        else:
            return solutions[resolve](filename)

def
check_file_exists
(
self
,
filename
,
resolve
=
'exception'
,
force
=
)
:
def
_warn
(
x
)
:
msg
=
"File {0!r} already exists."
.
format
(
x
)
logger
.
warn
(
msg
)
warnings
.
warn
(
msg
)
return
True
def
_raise
(
x
)
:
msg
=
"File {0!r} already exists."
.
format
(
x
)
logger
.
error
(
msg
)
raise
IOError
(
errno
.
EEXIST
,
x
,
msg
)
solutions
=
{
'ignore'
:
lambda
x
:
False
,
'indicate'
:
lambda
x
:
True
,
'warn'
:
_warn
,
'warning'
:
_warn
,
'exception'
:
_raise
,
'raise'
:
_raise
,
}
if
force
is
True
:
resolve
=
'ignore'
elif
force
is
False
:
resolve
=
'exception'
if
not
os
.
path
.
isfile
(
filename
)
:
return
False
else
:
return
solutions
[
resolve
]
(
filename
)

If a file exists then continue with the action specified in resolve .


def strftime(self, fmt="%d:%H:%M:%S"):
        """Primitive string formatter.

        The only directives understood are the following:
          ============   ==========================
          Directive      meaning
          ============   ==========================
          %d             day as integer
          %H             hour  [00-23]
          %h             hours including days
          %M             minute as integer [00-59]
          %S             second as integer [00-59]
          ============   ==========================
        """
        substitutions = {
            "%d": str(self.days),
            "%H": "{0:02d}".format(self.dhours),
            "%h": str(24*self.days + self.dhours),
            "%M": "{0:02d}".format(self.dminutes),
            "%S": "{0:02d}".format(self.dseconds),
            }
        s = fmt
        for search, replacement in substitutions.items():
            s = s.replace(search, replacement)
        return s

def strftime ( self , fmt = "%d:%H:%M:%S" ) : substitutions = { "%d" : str ( self . days ) , "%H" : "{0:02d}" . format ( self . dhours ) , "%h" : str ( 24 * self . days + self . dhours ) , "%M" : "{0:02d}" . format ( self . dminutes ) , "%S" : "{0:02d}" . format ( self . dseconds ) , } s = fmt for search , replacement in substitutions . items ( ) : s = s . replace ( search , replacement ) return s


def start_logging(logfile="gromacs.log"):
    """Start logging of messages to file and console.

    The default logfile is named ``gromacs.log`` and messages are
    logged with the tag *gromacs*.
    """
    from . import log
    log.create("gromacs", logfile=logfile)
    logging.getLogger("gromacs").info("GromacsWrapper %s STARTED logging to %r",
                                      __version__, logfile)

Start logging of messages to file and console .


def stop_logging():
    """Stop logging to logfile and console."""
    from . import log
    logger = logging.getLogger("gromacs")
    logger.info("GromacsWrapper %s STOPPED logging", get_version())
    log.clear_handlers(logger)

def
stop_logging
(
)
:
from
.
import
log
logger
=
logging
.
getLogger
(
"gromacs"
)
logger
.
info
(
"GromacsWrapper %s STOPPED logging"
,
get_version
(
)
)
log
.
clear_handlers
(
logger
)


def tool_factory(clsname, name, driver, base=GromacsCommand):
    """ Factory for GromacsCommand derived types. """
    clsdict = {
        'command_name': name,
        'driver': driver,
        '__doc__': property(base._get_gmx_docs)
    }
    return type(clsname, (base,), clsdict)

def tool_factory ( clsname , name , driver , base = GromacsCommand ) : clsdict = { 'command_name' : name , 'driver' : driver , '__doc__' : property ( base . _get_gmx_docs ) } return type ( clsname , ( base , ) , clsdict )

Factory for GromacsCommand derived types .


def find_executables(path):
    """ Find executables in a path.

    Searches executables in a directory excluding some know commands
    unusable with GromacsWrapper.

    :param path: dirname to search for
    :return: list of executables
    """
    execs = []
    for exe in os.listdir(path):
        fullexe = os.path.join(path, exe)
        if (os.access(fullexe, os.X_OK) and not os.path.isdir(fullexe) and
             exe not in ['GMXRC', 'GMXRC.bash', 'GMXRC.csh', 'GMXRC.zsh',
                         'demux.pl', 'xplor2gmx.pl']):
            execs.append(exe)
    return execs

def find_executables ( path ) : execs = [ ] for exe in os . listdir ( path ) : fullexe = os . path . join ( path , exe ) if ( os . access ( fullexe , os . X_OK ) and not os . path . isdir ( fullexe ) and exe not in [ 'GMXRC' , 'GMXRC.bash' , 'GMXRC.csh' , 'GMXRC.zsh' , 'demux.pl' , 'xplor2gmx.pl' ] ) : execs . append ( exe ) return execs


def load_v4_tools():
    """ Load Gromacs 4.x tools automatically using some heuristic.

    Tries to load tools (1) in configured tool groups (2) and fails back  to
    automatic detection from ``GMXBIN`` (3) then to a prefilled list.

    Also load any extra tool configured in ``~/.gromacswrapper.cfg``

    :return: dict mapping tool names to GromacsCommand classes
    """
    logger.debug("Loading v4 tools...")

    names = config.get_tool_names()

    if len(names) == 0 and 'GMXBIN' in os.environ:
        names = find_executables(os.environ['GMXBIN'])

    if len(names) == 0 or len(names) > len(V4TOOLS) * 4:
        names = list(V4TOOLS)

    names.extend(config.get_extra_tool_names())

    tools = {}
    for name in names:
        fancy = make_valid_identifier(name)
        tools[fancy] = tool_factory(fancy, name, None)

    if not tools:
        errmsg = "Failed to load v4 tools"
        logger.debug(errmsg)
        raise GromacsToolLoadingError(errmsg)
    logger.debug("Loaded {0} v4 tools successfully!".format(len(tools)))
    return tools

def
load_v4_tools
(
)
:
logger
.
debug
(
"Loading v4 tools..."
)
names
=
config
.
get_tool_names
(
)
if
len
(
names
)
==
0
and
'GMXBIN'
in
os
.
environ
:
names
=
find_executables
(
os
.
environ
[
'GMXBIN'
]
)
if
len
(
names
)
==
0
or
len
(
names
)
>
len
(
V4TOOLS
)
*
4
:
names
=
list
(
V4TOOLS
)
names
.
extend
(
config
.
get_extra_tool_names
(
)
)
tools
=
{
}
for
name
in
names
:
fancy
=
make_valid_identifier
(
name
)
tools
[
fancy
]
=
tool_factory
(
fancy
,
name
,
)
if
not
tools
:
errmsg
=
"Failed to load v4 tools"
logger
.
debug
(
errmsg
)
raise
GromacsToolLoadingError
(
errmsg
)
logger
.
debug
(
"Loaded {0} v4 tools successfully!"
.
format
(
len
(
tools
)
)
)
return
tools

Load Gromacs 4 . x tools automatically using some heuristic .


def merge_ndx(*args):
    """ Takes one or more index files and optionally one structure file and
    returns a path for a new merged index file.

    :param args: index files and zero or one structure file
    :return: path for the new merged index file
    """
    ndxs = []
    struct = None
    for fname in args:
        if fname.endswith('.ndx'):
            ndxs.append(fname)
        else:
            if struct is not None:
                raise ValueError("only one structure file supported")
            struct = fname

    fd, multi_ndx = tempfile.mkstemp(suffix='.ndx', prefix='multi_')
    os.close(fd)
    atexit.register(os.unlink, multi_ndx)

    if struct:
        make_ndx = registry['Make_ndx'](f=struct, n=ndxs, o=multi_ndx)
    else:
        make_ndx = registry['Make_ndx'](n=ndxs, o=multi_ndx)

    _, _, _ = make_ndx(input=['q'], stdout=False, stderr=False)
    return multi_ndx

def
merge_ndx
(
*
args
)
:
ndxs
=
[
]
struct
=
for
fname
in
args
:
if
fname
.
endswith
(
'.ndx'
)
:
ndxs
.
append
(
fname
)
else
:
if
struct
is
not
:
raise
ValueError
(
"only one structure file supported"
)
struct
=
fname
fd
,
multi_ndx
=
tempfile
.
mkstemp
(
suffix
=
'.ndx'
,
prefix
=
'multi_'
)
os
.
close
(
fd
)
atexit
.
register
(
os
.
unlink
,
multi_ndx
)
if
struct
:
make_ndx
=
registry
[
'Make_ndx'
]
(
f
=
struct
,
n
=
ndxs
,
o
=
multi_ndx
)
else
:
make_ndx
=
registry
[
'Make_ndx'
]
(
n
=
ndxs
,
o
=
multi_ndx
)
_
,
_
,
_
=
make_ndx
(
input
=
[
'q'
]
,
stdout
=
False
,
stderr
=
False
)
return
multi_ndx

Takes one or more index files and optionally one structure file and returns a path for a new merged index file .


def break_array(a, threshold=numpy.pi, other=None):
    """Create a array which masks jumps >= threshold.

    Extra points are inserted between two subsequent values whose
    absolute difference differs by more than threshold (default is
    pi).

    Other can be a secondary array which is also masked according to
    *a*.

    Returns (*a_masked*, *other_masked*) (where *other_masked* can be
    ``None``)
    """
    assert len(a.shape) == 1, "Only 1D arrays supported"

    if other is not None and a.shape != other.shape:
        raise ValueError("arrays must be of identical shape")

    # jump occurs after the index in break
    breaks = numpy.where(numpy.abs(numpy.diff(a)) >= threshold)[0]
    # insert a blank after
    breaks += 1

    # is this needed?? -- no, but leave it here as a reminder
    #f2 = numpy.diff(a, 2)
    #up = (f2[breaks - 1] >= 0)  # >0: up, <0: down
    # sort into up and down breaks:
    #breaks_up = breaks[up]
    #breaks_down = breaks[~up]

    # new array b including insertions for all the breaks
    m = len(breaks)
    b = numpy.empty((len(a) + m))
    # calculate new indices for breaks in b, taking previous insertions into account
    b_breaks = breaks + numpy.arange(m)
    mask =  numpy.zeros_like(b, dtype=numpy.bool)
    mask[b_breaks] = True
    b[~mask] = a
    b[mask] = numpy.NAN

    if other is not None:
        c = numpy.empty_like(b)
        c[~mask] = other
        c[mask] = numpy.NAN
        ma_c = numpy.ma.array(c, mask=mask)
    else:
        ma_c = None

    return numpy.ma.array(b, mask=mask), ma_c

def
break_array
(
a
,
threshold
=
numpy
.
pi
,
other
=
)
:
assert
len
(
a
.
shape
)
==
1
,
"Only 1D arrays supported"
if
other
is
not
and
a
.
shape
!=
other
.
shape
:
raise
ValueError
(
"arrays must be of identical shape"
)
breaks
=
numpy
.
where
(
numpy
.
abs
(
numpy
.
diff
(
a
)
)
>=
threshold
)
[
0
]
breaks
+=
1
m
=
len
(
breaks
)
b
=
numpy
.
empty
(
(
len
(
a
)
+
m
)
)
b_breaks
=
breaks
+
numpy
.
arange
(
m
)
mask
=
numpy
.
zeros_like
(
b
,
dtype
=
numpy
.
bool
)
mask
[
b_breaks
]
=
True
b
[
~
mask
]
=
a
b
[
mask
]
=
numpy
.
if
other
is
not
:
c
=
numpy
.
empty_like
(
b
)
c
[
~
mask
]
=
other
c
[
mask
]
=
numpy
.
ma_c
=
numpy
.
ma
.
array
(
c
,
mask
=
mask
)
else
:
ma_c
=
return
numpy
.
ma
.
array
(
b
,
mask
=
mask
)
,
ma_c

Create a array which masks jumps > = threshold .


def ma(self):
        """Represent data as a masked array.

        The array is returned with column-first indexing, i.e. for a data file with
        columns X Y1 Y2 Y3 ... the array a will be a[0] = X, a[1] = Y1, ... .

        inf and nan are filtered via :func:`numpy.isfinite`.
        """
        a = self.array
        return numpy.ma.MaskedArray(a, mask=numpy.logical_not(numpy.isfinite(a)))

def ma ( self ) : a = self . array return numpy . ma . MaskedArray ( a , mask = numpy . logical_not ( numpy . isfinite ( a ) ) )


def _tcorrel(self, nstep=100, **kwargs):
        """Correlation "time" of data.

        The 0-th column of the data is interpreted as a time and the
        decay of the data is computed from the autocorrelation
        function (using FFT).

        .. SeeAlso:: :func:`numkit.timeseries.tcorrel`
        """
        t = self.array[0,::nstep]
        r = gromacs.collections.Collection([numkit.timeseries.tcorrel(t, Y, nstep=1, **kwargs) for Y in self.array[1:,::nstep]])
        return r

def _tcorrel ( self , nstep = 100 , ** kwargs ) : t = self . array [ 0 , : : nstep ] r = gromacs . collections . Collection ( [ numkit . timeseries . tcorrel ( t , Y , nstep = 1 , ** kwargs ) for Y in self . array [ 1 : , : : nstep ] ] ) return r


def set_correlparameters(self, **kwargs):
        """Set and change the parameters for calculations with  correlation functions.

        The parameters persist until explicitly changed.

        :Keywords:
           *nstep*
               only process every *nstep* data point to speed up the FFT; if
               left empty a default is chosen that produces roughly 25,000 data
               points (or whatever is set in *ncorrel*)
           *ncorrel*
               If no *nstep* is supplied, aim at using *ncorrel* data points for
               the FFT; sets :attr:`XVG.ncorrel` [25000]
           *force*
               force recalculating correlation data even if cached values are
               available
           *kwargs*
               see :func:`numkit.timeseries.tcorrel` for other options

        .. SeeAlso: :attr:`XVG.error` for details and references.
        """
        self.ncorrel = kwargs.pop('ncorrel', self.ncorrel) or 25000
        nstep = kwargs.pop('nstep', None)
        if nstep is None:
            # good step size leads to ~25,000 data points
            nstep = len(self.array[0])/float(self.ncorrel)
            nstep = int(numpy.ceil(nstep))  # catch small data sets
        kwargs['nstep'] = nstep
        self.__correlkwargs.update(kwargs)  # only contains legal kw for numkit.timeseries.tcorrel or force
        return self.__correlkwargs

def set_correlparameters ( self , ** kwargs ) : self . ncorrel = kwargs . pop ( 'ncorrel' , self . ncorrel ) or 25000 nstep = kwargs . pop ( 'nstep' , None ) if nstep is None : nstep = len ( self . array [ 0 ] ) / float ( self . ncorrel ) nstep = int ( numpy . ceil ( nstep ) ) kwargs [ 'nstep' ] = nstep self . __correlkwargs . update ( kwargs ) return self . __correlkwargs

Set and change the parameters for calculations with correlation functions .


def parse(self, stride=None):
        """Read and cache the file as a numpy array.

        Store every *stride* line of data; if ``None`` then the class default is used.

        The array is returned with column-first indexing, i.e. for a data file with
        columns X Y1 Y2 Y3 ... the array a will be a[0] = X, a[1] = Y1, ... .
        """
        if stride is None:
            stride = self.stride
        self.corrupted_lineno = []
        irow  = 0  # count rows of data
        # cannot use numpy.loadtxt() because xvg can have two types of 'comment' lines
        with utilities.openany(self.real_filename) as xvg:
            rows = []
            ncol = None
            for lineno,line in enumerate(xvg):
                line = line.strip()
                if len(line) == 0:
                    continue
                if "label" in line and "xaxis" in line:
                        self.xaxis = line.split('"')[-2]
                if "label" in line and "yaxis" in line:
                        self.yaxis = line.split('"')[-2]
                if line.startswith("@ legend"):
                                        if not "legend" in self.metadata: self.metadata["legend"] = []
                                        self.metadata["legend"].append(line.split("legend ")[-1])
                if line.startswith("@ s") and "subtitle" not in line:
                                        name = line.split("legend ")[-1].replace('"','').strip()
                                        self.names.append(name)
                if line.startswith(('#', '@')) :
                                        continue
                if line.startswith('&'):
                    raise NotImplementedError('{0!s}: Multi-data not supported, only simple NXY format.'.format(self.real_filename))
                # parse line as floats
                try:
                    row = [float(el) for el in line.split()]
                except:
                    if self.permissive:
                        self.logger.warn("%s: SKIPPING unparsable line %d: %r",
                                         self.real_filename, lineno+1, line)
                        self.corrupted_lineno.append(lineno+1)
                        continue
                    self.logger.error("%s: Cannot parse line %d: %r",
                                      self.real_filename, lineno+1, line)
                    raise
                # check for same number of columns as in previous step
                if ncol is not None and len(row) != ncol:
                    if self.permissive:
                        self.logger.warn("%s: SKIPPING line %d with wrong number of columns: %r",
                                         self.real_filename, lineno+1, line)
                        self.corrupted_lineno.append(lineno+1)
                        continue
                    errmsg = "{0!s}: Wrong number of columns in line {1:d}: {2!r}".format(self.real_filename, lineno+1, line)
                    self.logger.error(errmsg)
                    raise IOError(errno.ENODATA, errmsg, self.real_filename)
                # finally: a good line
                if irow % stride == 0:
                    ncol = len(row)
                    rows.append(row)
                irow += 1
        try:
            self.__array = numpy.array(rows).transpose()    # cache result
        except:
            self.logger.error("%s: Failed reading XVG file, possibly data corrupted. "
                              "Check the last line of the file...", self.real_filename)
            raise
        finally:
            del rows

Read and cache the file as a numpy array .


def plot(self, **kwargs):
        """Plot xvg file data.

        The first column of the data is always taken as the abscissa
        X. Additional columns are plotted as ordinates Y1, Y2, ...

        In the special case that there is only a single column then this column
        is plotted against the index, i.e. (N, Y).

        :Keywords:
          *columns* : list
               Select the columns of the data to be plotted; the list
               is used as a numpy.array extended slice. The default is
               to use all columns. Columns are selected *after* a transform.
          *transform* : function
               function ``transform(array) -> array`` which transforms
               the original array; must return a 2D numpy array of
               shape [X, Y1, Y2, ...] where X, Y1, ... are column
               vectors.  By default the transformation is the
               identity [``lambda x: x``].
          *maxpoints* : int
               limit the total number of data points; matplotlib has issues processing
               png files with >100,000 points and pdfs take forever to display. Set to
               ``None`` if really all data should be displayed. At the moment we simply
               decimate the data at regular intervals. [10000]
          *method*
               method to decimate the data to *maxpoints*, see :meth:`XVG.decimate`
               for details
          *color*
               single color (used for all plots); sequence of colors
               (will be repeated as necessary); or a matplotlib
               colormap (e.g. "jet", see :mod:`matplotlib.cm`). The
               default is to use the :attr:`XVG.default_color_cycle`.
          *ax*
               plot into given axes or create new one if ``None`` [``None``]
          *kwargs*
               All other keyword arguments are passed on to :func:`matplotlib.pyplot.plot`.

        :Returns:
          *ax*
               axes instance
        """
        columns = kwargs.pop('columns', Ellipsis)         # slice for everything
        maxpoints = kwargs.pop('maxpoints', self.maxpoints_default)
        transform = kwargs.pop('transform', lambda x: x)  # default is identity transformation
        method = kwargs.pop('method', "mean")
        ax = kwargs.pop('ax', None)

        if columns is Ellipsis or columns is None:
            columns = numpy.arange(self.array.shape[0])
        if len(columns) == 0:
            raise MissingDataError("plot() needs at least one column of data")

        if len(self.array.shape) == 1 or self.array.shape[0] == 1:
            # special case: plot against index; plot would do this automatically but
            # we'll just produce our own xdata and pretend that this was X all along
            a = numpy.ravel(self.array)
            X = numpy.arange(len(a))
            a = numpy.vstack((X, a))
            columns = [0] + [c+1 for c in columns]
        else:
            a = self.array

        color = kwargs.pop('color', self.default_color_cycle)
        try:
            cmap = matplotlib.cm.get_cmap(color)
            colors = cmap(matplotlib.colors.Normalize()(numpy.arange(len(columns[1:]), dtype=float)))
        except TypeError:
            colors = cycle(utilities.asiterable(color))

        if ax is None:
            ax = plt.gca()

        # (decimate/smooth o slice o transform)(array)
        a = self.decimate(method, numpy.asarray(transform(a))[columns], maxpoints=maxpoints)

        # now deal with infs, nans etc AFTER all transformations (needed for plotting across inf/nan)
        ma = numpy.ma.MaskedArray(a, mask=numpy.logical_not(numpy.isfinite(a)))

        # finally plot (each column separately to catch empty sets)
        for column, color in zip(range(1,len(columns)), colors):
            if len(ma[column]) == 0:
                warnings.warn("No data to plot for column {column:d}".format(**vars()), category=MissingDataWarning)
            kwargs['color'] = color
            ax.plot(ma[0], ma[column], **kwargs)   # plot all other columns in parallel
        return ax


def topology(struct=None, protein='protein',
             top='system.top',  dirname='top',
             posres="posres.itp",
             ff="oplsaa", water="tip4p",
             **pdb2gmx_args):
    """Build Gromacs topology files from pdb.

    :Keywords:
       *struct*
           input structure (**required**)
       *protein*
           name of the output files
       *top*
           name of the topology file
       *dirname*
           directory in which the new topology will be stored
       *ff*
           force field (string understood by ``pdb2gmx``); default
           "oplsaa"
       *water*
           water model (string), default "tip4p"
       *pdb2gmxargs*
           other arguments for ``pdb2gmx``

    .. note::
       At the moment this function simply runs ``pdb2gmx`` and uses
       the resulting topology file directly. If you want to create
       more complicated topologies and maybe also use additional itp
       files or make a protein itp file then you will have to do this
       manually.
    """

    structure = realpath(struct)

    new_struct = protein + '.pdb'
    if posres is None:
        posres = protein + '_posres.itp'

    pdb2gmx_args.update({'f': structure, 'o': new_struct, 'p': top, 'i': posres,
                         'ff': ff, 'water': water})

    with in_dir(dirname):
        logger.info("[{dirname!s}] Building topology {top!r} from struct = {struct!r}".format(**vars()))
        # perhaps parse output from pdb2gmx 4.5.x to get the names of the chain itp files?
        gromacs.pdb2gmx(**pdb2gmx_args)
    return { \
            'top': realpath(dirname, top), \
            'struct': realpath(dirname, new_struct), \
            'posres' : realpath(dirname, posres) }


def make_main_index(struct, selection='"Protein"', ndx='main.ndx', oldndx=None):
    """Make index file with the special groups.

    This routine adds the group __main__ and the group __environment__
    to the end of the index file. __main__ contains what the user
    defines as the *central* and *most important* parts of the
    system. __environment__ is everything else.

    The template mdp file, for instance, uses these two groups for T-coupling.

    These groups are mainly useful if the default groups "Protein" and "Non-Protein"
    are not appropriate. By using symbolic names such as __main__ one
    can keep scripts more general.

    :Returns:
      *groups* is a list of dictionaries that describe the index groups. See
      :func:`gromacs.cbook.parse_ndxlist` for details.

    :Arguments:
      *struct* : filename
        structure (tpr, pdb, gro)
      *selection* : string
        is a ``make_ndx`` command such as ``"Protein"`` or ``r DRG`` which
        determines what is considered the main group for centering etc. It is
        passed directly to ``make_ndx``.
      *ndx* : string
         name of the final index file
      *oldndx* : string
         name of index file that should be used as a basis; if None
         then the ``make_ndx`` default groups are used.

    This routine is very dumb at the moment; maybe some heuristics will be
    added later as could be other symbolic groups such as __membrane__.
    """

    logger.info("Building the main index file {ndx!r}...".format(**vars()))

    # pass 1: select
    # get a list of groups
    # need the first "" to get make_ndx to spit out the group list.
    _,out,_ = gromacs.make_ndx(f=struct, n=oldndx, o=ndx, stdout=False,
                                      input=("", "q"))
    groups = cbook.parse_ndxlist(out)

    # find the matching groups,
    # there is a nasty bug in GROMACS where make_ndx may have multiple
    # groups, which caused the previous approach to fail big time.
    # this is a work around the make_ndx bug.
    # striping the "" allows compatibility with existing make_ndx selection commands.
    selection = selection.strip("\"")

    selected_groups = [g for g in groups if g['name'].lower() == selection.lower()]

    if len(selected_groups) > 1:
        logging.warn("make_ndx created duplicated groups, performing work around")

    if len(selected_groups) <= 0:
        msg = "no groups found for selection {0}, available groups are {1}".format(selection, groups)
        logging.error(msg)
        raise ValueError(msg)

    # Found at least one matching group, we're OK

    # index of last group
    last = len(groups) - 1
    assert last == groups[-1]['nr']

    group = selected_groups[0]

    # pass 2:
    # 1) last group is __main__
    # 2) __environment__ is everything else (eg SOL, ions, ...)
    _,out,_ = gromacs.make_ndx(f=struct, n=ndx, o=ndx,
                                      stdout=False,
                                             # make copy selected group, this now has index last + 1
                                      input=("{0}".format(group['nr']),
                                             # rename this to __main__
                                             "name {0} __main__".format(last+1),
                                             # make a complement to this group, it get index last + 2
                                             "! \"__main__\"",
                                             # rename this to __environment__
                                             "name {0} __environment__".format(last+2),
                                             # list the groups
                                             "",
                                             # quit
                                             "q"))
    return cbook.parse_ndxlist(out)

Make index file with the special groups .


def get_lipid_vdwradii(outdir=os.path.curdir, libdir=None):
    """Find vdwradii.dat and add special entries for lipids.

    See :data:`gromacs.setup.vdw_lipid_resnames` for lipid
    resnames. Add more if necessary.
    """
    vdwradii_dat = os.path.join(outdir, "vdwradii.dat")

    if libdir is not None:
        filename = os.path.join(libdir, 'vdwradii.dat')  # canonical name
        if not os.path.exists(filename):
            msg = 'No VDW database file found in {filename!r}.'.format(**vars())
            logger.exception(msg)
            raise OSError(msg, errno.ENOENT)
    else:
        try:
            filename = os.path.join(os.environ['GMXLIB'], 'vdwradii.dat')
        except KeyError:
            try:
                filename = os.path.join(os.environ['GMXDATA'], 'top', 'vdwradii.dat')
            except KeyError:
                msg = "Cannot find vdwradii.dat. Set GMXLIB (point to 'top') or GMXDATA ('share/gromacs')."
                logger.exception(msg)
                raise OSError(msg, errno.ENOENT)
    if not os.path.exists(filename):
        msg = "Cannot find {filename!r}; something is wrong with the Gromacs installation.".format(**vars())
        logger.exception(msg, errno.ENOENT)
        raise OSError(msg)

    # make sure to catch 3 and 4 letter resnames
    patterns = vdw_lipid_resnames + list({x[:3] for x in vdw_lipid_resnames})
    # TODO: should do a tempfile...
    with open(vdwradii_dat, 'w') as outfile:
        # write lipid stuff before general
        outfile.write('; Special larger vdw radii for solvating lipid membranes\n')
        for resname in patterns:
            for atom,radius in vdw_lipid_atom_radii.items():
                outfile.write('{resname:4!s} {atom:<5!s} {radius:5.3f}\n'.format(**vars()))
        with open(filename, 'r') as infile:
            for line in infile:
                outfile.write(line)
    logger.debug('Created lipid vdW radii file {vdwradii_dat!r}.'.format(**vars()))
    return realpath(vdwradii_dat)

Find vdwradii . dat and add special entries for lipids .


def solvate(struct='top/protein.pdb', top='top/system.top',
            distance=0.9, boxtype='dodecahedron',
            concentration=0, cation='NA', anion='CL',
            water='tip4p', solvent_name='SOL', with_membrane=False,
            ndx = 'main.ndx', mainselection = '"Protein"',
            dirname='solvate',
            **kwargs):
    """Put protein into box, add water, add counter-ions.

    Currently this really only supports solutes in water. If you need
    to embedd a protein in a membrane then you will require more
    sophisticated approaches.

    However, you *can* supply a protein already inserted in a
    bilayer. In this case you will probably want to set *distance* =
    ``None`` and also enable *with_membrane* = ``True`` (using extra
    big vdw radii for typical lipids).

    .. Note:: The defaults are suitable for solvating a globular
       protein in a fairly tight (increase *distance*!) dodecahedral
       box.

    :Arguments:
      *struct* : filename
          pdb or gro input structure
      *top* : filename
          Gromacs topology
      *distance* : float
          When solvating with water, make the box big enough so that
          at least *distance* nm water are between the solute *struct*
          and the box boundary.
          Set *boxtype*  to ``None`` in order to use a box size in the input
          file (gro or pdb).
      *boxtype* or *bt*: string
          Any of the box types supported by :class:`~gromacs.tools.Editconf`
          (triclinic, cubic, dodecahedron, octahedron). Set the box dimensions
          either with *distance* or the *box* and *angle* keywords.

          If set to ``None`` it will ignore *distance* and use the box
          inside the *struct* file.

          *bt* overrides the value of *boxtype*.
      *box*
          List of three box lengths [A,B,C] that are used by :class:`~gromacs.tools.Editconf`
          in combination with *boxtype* (``bt`` in :program:`editconf`) and *angles*.
          Setting *box* overrides *distance*.
      *angles*
          List of three angles (only necessary for triclinic boxes).
      *concentration* : float
          Concentration of the free ions in mol/l. Note that counter
          ions are added in excess of this concentration.
      *cation* and *anion* : string
          Molecule names of the ions. This depends on the chosen force field.
      *water* : string
          Name of the water model; one of "spc", "spce", "tip3p",
          "tip4p". This should be appropriate for the chosen force
          field. If an alternative solvent is required, simply supply the path to a box
          with solvent molecules (used by :func:`~gromacs.genbox`'s  *cs* argument)
          and also supply the molecule name via *solvent_name*.
      *solvent_name*
          Name of the molecules that make up the solvent (as set in the itp/top).
          Typically needs to be changed when using non-standard/non-water solvents.
          ["SOL"]
      *with_membrane* : bool
           ``True``: use special ``vdwradii.dat`` with 0.1 nm-increased radii on
           lipids. Default is ``False``.
      *ndx* : filename
          How to name the index file that is produced by this function.
      *mainselection* : string
          A string that is fed to :class:`~gromacs.tools.Make_ndx` and
          which should select the solute.
      *dirname* : directory name
          Name of the directory in which all files for the solvation stage are stored.
      *includes*
          List of additional directories to add to the mdp include path
      *kwargs*
          Additional arguments are passed on to
          :class:`~gromacs.tools.Editconf` or are interpreted as parameters to be
          changed in the mdp file.

    """
    sol = solvate_sol(struct=struct, top=top,
                      distance=distance, boxtype=boxtype,
                      water=water, solvent_name=solvent_name,
                      with_membrane=with_membrane,
                      dirname=dirname, **kwargs)

    ion = solvate_ion(struct=sol['struct'], top=top,
                      concentration=concentration, cation=cation, anion=anion,
                      solvent_name=solvent_name, ndx=ndx,
                      mainselection=mainselection, dirname=dirname,
                      **kwargs)
    return ion

def solvate ( struct = 'top/protein.pdb' , top = 'top/system.top' , distance = 0.9 , boxtype = 'dodecahedron' , concentration = 0 , cation = 'NA' , anion = 'CL' , water = 'tip4p' , solvent_name = 'SOL' , with_membrane = False , ndx = 'main.ndx' , mainselection = '"Protein"' , dirname = 'solvate' , ** kwargs ) : sol = solvate_sol ( struct = struct , top = top , distance = distance , boxtype = boxtype , water = water , solvent_name = solvent_name , with_membrane = with_membrane , dirname = dirname , ** kwargs ) ion = solvate_ion ( struct = sol [ 'struct' ] , top = top , concentration = concentration , cation = cation , anion = anion , solvent_name = solvent_name , ndx = ndx , mainselection = mainselection , dirname = dirname , ** kwargs ) return ion

Put protein into box add water add counter - ions .


def energy_minimize(dirname='em', mdp=config.templates['em.mdp'],
                    struct='solvate/ionized.gro', top='top/system.top',
                    output='em.pdb', deffnm="em",
                    mdrunner=None, mdrun_args=None,
                    **kwargs):
    """Energy minimize the system.

    This sets up the system (creates run input files) and also runs
    ``mdrun_d``. Thus it can take a while.

    Additional itp files should be in the same directory as the top file.

    Many of the keyword arguments below already have sensible values.

    :Keywords:
       *dirname*
          set up under directory dirname [em]
       *struct*
          input structure (gro, pdb, ...) [solvate/ionized.gro]
       *output*
          output structure (will be put under dirname) [em.pdb]
       *deffnm*
          default name for mdrun-related files [em]
       *top*
          topology file [top/system.top]
       *mdp*
          mdp file (or use the template) [templates/em.mdp]
       *includes*
          additional directories to search for itp files
       *mdrunner*
          :class:`gromacs.run.MDrunner` instance; by default we
          just try :func:`gromacs.mdrun_d` and :func:`gromacs.mdrun` but a
          MDrunner instance gives the user the ability to run mpi jobs
          etc. [None]
       *mdrun_args*
          arguments for *mdrunner* (as a dict), e.g. ``{'nt': 2}``;
          empty by default

          .. versionaddedd:: 0.7.0

       *kwargs*
          remaining key/value pairs that should be changed in the
          template mdp file, eg ``nstxtcout=250, nstfout=250``.

    .. note:: If :func:`~gromacs.mdrun_d` is not found, the function
              falls back to :func:`~gromacs.mdrun` instead.
    """

    structure = realpath(struct)
    topology = realpath(top)
    mdp_template = config.get_template(mdp)
    deffnm = deffnm.strip()

    mdrun_args = {} if mdrun_args is None else mdrun_args

    # write the processed topology to the default output
    kwargs.setdefault('pp', 'processed.top')

    # filter some kwargs that might come through when feeding output
    # from previous stages such as solvate(); necessary because *all*
    # **kwargs must be *either* substitutions in the mdp file *or* valid
    # command line parameters for ``grompp``.
    kwargs.pop('ndx', None)
    # mainselection is not used but only passed through; right now we
    # set it to the default that is being used in all argument lists
    # but that is not pretty. TODO.
    mainselection = kwargs.pop('mainselection', '"Protein"')
    # only interesting when passed from solvate()
    qtot = kwargs.pop('qtot', 0)

    # mdp is now the *output* MDP that will be generated from mdp_template
    mdp = deffnm+'.mdp'
    tpr = deffnm+'.tpr'

    logger.info("[{dirname!s}] Energy minimization of struct={struct!r}, top={top!r}, mdp={mdp!r} ...".format(**vars()))

    cbook.add_mdp_includes(topology, kwargs)

    if qtot != 0:
        # At the moment this is purely user-reported and really only here because
        # it might get fed into the function when using the keyword-expansion pipeline
        # usage paradigm.
        wmsg = "Total charge was reported as qtot = {qtot:g} <> 0; probably a problem.".format(**vars())
        logger.warn(wmsg)
        warnings.warn(wmsg, category=BadParameterWarning)

    with in_dir(dirname):
        unprocessed = cbook.edit_mdp(mdp_template, new_mdp=mdp, **kwargs)
        check_mdpargs(unprocessed)
        gromacs.grompp(f=mdp, o=tpr, c=structure, r=structure, p=topology, **unprocessed)
        mdrun_args.update(v=True, stepout=10, deffnm=deffnm, c=output)
        if mdrunner is None:
            mdrun = run.get_double_or_single_prec_mdrun()
            mdrun(**mdrun_args)
        else:
            if type(mdrunner) is type:
                # class
                # user wants full control and provides simulation.MDrunner **class**
                # NO CHECKING --- in principle user can supply any callback they like
                mdrun = mdrunner(**mdrun_args)
                mdrun.run()
            else:
                # anything with a run() method that takes mdrun arguments...
                try:
                    mdrunner.run(mdrunargs=mdrun_args)
                except AttributeError:
                    logger.error("mdrunner: Provide a gromacs.run.MDrunner class or instance or a callback with a run() method")
                    raise TypeError("mdrunner: Provide a gromacs.run.MDrunner class or instance or a callback with a run() method")

        # em.gro --> gives 'Bad box in file em.gro' warning --- why??
        # --> use em.pdb instead.
        if not os.path.exists(output):
            errmsg = "Energy minimized system NOT produced."
            logger.error(errmsg)
            raise GromacsError(errmsg)
        final_struct = realpath(output)

    logger.info("[{dirname!s}] energy minimized structure {final_struct!r}".format(**vars()))
    return {'struct': final_struct,
            'top': topology,
            'mainselection': mainselection,
            }


def em_schedule(**kwargs):
    """Run multiple energy minimizations one after each other.

    :Keywords:
      *integrators*
           list of integrators (from 'l-bfgs', 'cg', 'steep')
           [['bfgs', 'steep']]
      *nsteps*
           list of maximum number of steps; one for each integrator in
           in the *integrators* list [[100,1000]]
      *kwargs*
           mostly passed to :func:`gromacs.setup.energy_minimize`

    :Returns: dictionary with paths to final structure ('struct') and
              other files

    :Example:
       Conduct three minimizations:
         1. low memory Broyden-Goldfarb-Fletcher-Shannon (BFGS) for 30 steps
         2. steepest descent for 200 steps
         3. finish with BFGS for another 30 steps
       We also do a multi-processor minimization when possible (i.e. for steep
       (and conjugate gradient) by using a :class:`gromacs.run.MDrunner` class
       for a :program:`mdrun` executable compiled for OpenMP in 64 bit (see
       :mod:`gromacs.run` for details)::

          import gromacs.run
          gromacs.setup.em_schedule(struct='solvate/ionized.gro',
                    mdrunner=gromacs.run.MDrunnerOpenMP64,
                    integrators=['l-bfgs', 'steep', 'l-bfgs'],
                    nsteps=[50,200, 50])

    .. Note:: You might have to prepare the mdp file carefully because at the
              moment one can only modify the *nsteps* parameter on a
              per-minimizer basis.
    """

    mdrunner = kwargs.pop('mdrunner', None)
    integrators = kwargs.pop('integrators', ['l-bfgs', 'steep'])
    kwargs.pop('integrator', None)  # clean input; we set intgerator from integrators
    nsteps = kwargs.pop('nsteps', [100, 1000])

    outputs = ['em{0:03d}_{1!s}.pdb'.format(i, integrator) for i,integrator in enumerate(integrators)]
    outputs[-1] = kwargs.pop('output', 'em.pdb')

    files = {'struct': kwargs.pop('struct', None)}  # fake output from energy_minimize()

    for i, integrator in enumerate(integrators):
        struct = files['struct']
        logger.info("[em %d] energy minimize with %s for maximum %d steps", i, integrator, nsteps[i])
        kwargs.update({'struct':struct, 'output':outputs[i],
                       'integrator':integrator, 'nsteps': nsteps[i]})
        if not integrator == 'l-bfgs':
            kwargs['mdrunner'] = mdrunner
        else:
            kwargs['mdrunner'] = None
            logger.warning("[em %d]  Not using mdrunner for L-BFGS because it cannot "
                           "do parallel runs.", i)

        files = energy_minimize(**kwargs)

    return files

Run multiple energy minimizations one after each other .


def MD_restrained(dirname='MD_POSRES', **kwargs):
    """Set up MD with position restraints.

    Additional itp files should be in the same directory as the top file.

    Many of the keyword arguments below already have sensible values. Note that
    setting *mainselection* = ``None`` will disable many of the automated
    choices and is often recommended when using your own mdp file.

    :Keywords:
       *dirname*
          set up under directory dirname [MD_POSRES]
       *struct*
          input structure (gro, pdb, ...) [em/em.pdb]
       *top*
          topology file [top/system.top]
       *mdp*
          mdp file (or use the template) [templates/md.mdp]
       *ndx*
          index file (supply when using a custom mdp)
       *includes*
          additional directories to search for itp files
       *mainselection*
          :program:`make_ndx` selection to select main group ["Protein"]
          (If ``None`` then no canonical index file is generated and
          it is the user's responsibility to set *tc_grps*,
          *tau_t*, and *ref_t* as keyword arguments, or provide the mdp template
          with all parameter pre-set in *mdp* and probably also your own *ndx*
          index file.)
       *deffnm*
          default filename for Gromacs run [md]
       *runtime*
          total length of the simulation in ps [1000]
       *dt*
          integration time step in ps [0.002]
       *qscript*
          script to submit to the queuing system; by default
          uses the template :data:`gromacs.config.qscript_template`, which can
          be manually set to another template from :data:`gromacs.config.templates`;
          can also be a list of template names.
       *qname*
          name to be used for the job in the queuing system [PR_GMX]
       *mdrun_opts*
          option flags for the :program:`mdrun` command in the queuing system
          scripts such as "-stepout 100". [""]
       *kwargs*
          remaining key/value pairs that should be changed in the template mdp
          file, eg ``nstxtcout=250, nstfout=250`` or command line options for
          ``grompp` such as ``maxwarn=1``.

          In particular one can also set **define** and activate
          whichever position restraints have been coded into the itp
          and top file. For instance one could have

             *define* = "-DPOSRES_MainChain -DPOSRES_LIGAND"

          if these preprocessor constructs exist. Note that there
          **must not be any space between "-D" and the value.**

          By default *define* is set to "-DPOSRES".

    :Returns: a dict that can be fed into :func:`gromacs.setup.MD`
              (but check, just in case, especially if you want to
              change the ``define`` parameter in the mdp file)

    .. Note:: The output frequency is drastically reduced for position
              restraint runs by default. Set the corresponding ``nst*``
              variables if you require more output. The `pressure coupling`_
              option *refcoord_scaling* is set to "com" by default (but can
              be changed via *kwargs*) and the pressure coupling
              algorithm itself is set to *Pcoupl* = "Berendsen" to
              run a stable simulation.

    .. _`pressure coupling`: http://manual.gromacs.org/online/mdp_opt.html#pc
    """

    logger.info("[{dirname!s}] Setting up MD with position restraints...".format(**vars()))
    kwargs.setdefault('struct', 'em/em.pdb')
    kwargs.setdefault('qname', 'PR_GMX')
    kwargs.setdefault('define', '-DPOSRES')
    # reduce size of output files
    kwargs.setdefault('nstxout', '50000')   # trr pos
    kwargs.setdefault('nstvout', '50000')   # trr veloc
    kwargs.setdefault('nstfout', '0')       # trr forces
    kwargs.setdefault('nstlog', '500')      # log file
    kwargs.setdefault('nstenergy', '2500')  # edr energy
    kwargs.setdefault('nstxtcout', '5000')  # xtc pos
    # try to get good pressure equilibration
    kwargs.setdefault('refcoord_scaling', 'com')
    kwargs.setdefault('Pcoupl', "Berendsen")

    new_kwargs =  _setup_MD(dirname, **kwargs)

    # clean up output kwargs
    new_kwargs.pop('define', None)          # but make sure that -DPOSRES does not stay...
    new_kwargs.pop('refcoord_scaling', None)
    new_kwargs.pop('Pcoupl', None)
    return new_kwargs


def MD(dirname='MD', **kwargs):
    """Set up equilibrium MD.

    Additional itp files should be in the same directory as the top file.

    Many of the keyword arguments below already have sensible values. Note that
    setting *mainselection* = ``None`` will disable many of the automated
    choices and is often recommended when using your own mdp file.

    :Keywords:
       *dirname*
          set up under directory dirname [MD]
       *struct*
          input structure (gro, pdb, ...) [MD_POSRES/md_posres.pdb]
       *top*
          topology file [top/system.top]
       *mdp*
          mdp file (or use the template) [templates/md.mdp]
       *ndx*
          index file (supply when using a custom mdp)
       *includes*
          additional directories to search for itp files
       *mainselection*
          ``make_ndx`` selection to select main group ["Protein"]
          (If ``None`` then no canonical index file is generated and
          it is the user's responsibility to set *tc_grps*,
          *tau_t*, and *ref_t* as keyword arguments, or provide the mdp template
          with all parameter pre-set in *mdp* and probably also your own *ndx*
          index file.)
       *deffnm*
          default filename for Gromacs run [md]
       *runtime*
          total length of the simulation in ps [1000]
       *dt*
          integration time step in ps [0.002]
       *qscript*
          script to submit to the queuing system; by default
          uses the template :data:`gromacs.config.qscript_template`, which can
          be manually set to another template from :data:`gromacs.config.templates`;
          can also be a list of template names.
       *qname*
          name to be used for the job in the queuing system [MD_GMX]
       *mdrun_opts*
          option flags for the :program:`mdrun` command in the queuing system
          scripts such as "-stepout 100 -dgdl". [""]
       *kwargs*
          remaining key/value pairs that should be changed in the template mdp
          file, e.g. ``nstxtcout=250, nstfout=250`` or command line options for
          :program`grompp` such as ``maxwarn=1``.

    :Returns: a dict that can be fed into :func:`gromacs.setup.MD`
              (but check, just in case, especially if you want to
              change the *define* parameter in the mdp file)
    """

    logger.info("[{dirname!s}] Setting up MD...".format(**vars()))
    kwargs.setdefault('struct', 'MD_POSRES/md.gro')
    kwargs.setdefault('qname', 'MD_GMX')
    return _setup_MD(dirname, **kwargs)

def
MD
(
dirname
=
'MD'
,
**
kwargs
)
:
logger
.
info
(
"[{dirname!s}] Setting up MD..."
.
format
(
**
vars
(
)
)
)
kwargs
.
setdefault
(
'struct'
,
'MD_POSRES/md.gro'
)
kwargs
.
setdefault
(
'qname'
,
'MD_GMX'
)
return
_setup_MD
(
dirname
,
**
kwargs
)


def generate_submit_scripts(templates, prefix=None, deffnm='md', jobname='MD', budget=None,
                            mdrun_opts=None, walltime=1.0, jobarray_string=None, startdir=None,
                            npme=None, **kwargs):
    """Write scripts for queuing systems.

    This sets up queuing system run scripts with a simple search and replace in
    templates. See :func:`gromacs.cbook.edit_txt` for details. Shell scripts
    are made executable.

    :Arguments:
      *templates*
          Template file or list of template files. The "files" can also be names
          or symbolic names for templates in the templates directory. See
          :mod:`gromacs.config` for details and rules for writing templates.
      *prefix*
          Prefix for the final run script filename; by default the filename will be
          the same as the template. [None]
      *dirname*
          Directory in which to place the submit scripts. [.]
      *deffnm*
          Default filename prefix for :program:`mdrun` ``-deffnm`` [md]
      *jobname*
          Name of the job in the queuing system. [MD]
      *budget*
          Which budget to book the runtime on [None]
      *startdir*
          Explicit path on the remote system (for run scripts that need to `cd`
          into this directory at the beginning of execution) [None]
      *mdrun_opts*
          String of additional options for :program:`mdrun`.
      *walltime*
          Maximum runtime of the job in hours. [1]
      *npme*
          number of PME nodes
      *jobarray_string*
          Multi-line string that is spliced in for job array functionality
          (see :func:`gromacs.qsub.generate_submit_array`; do not use manually)
      *kwargs*
          all other kwargs are ignored

    :Returns: list of generated run scripts
    """
    if not jobname[0].isalpha():
        jobname = 'MD_'+jobname
        wmsg = "To make the jobname legal it must start with a letter: changed to {0!r}".format(jobname)
        logger.warn(wmsg)
        warnings.warn(wmsg, category=AutoCorrectionWarning)
    if prefix is None:
        prefix = ""
    if mdrun_opts is not None:
        mdrun_opts = '"'+str(mdrun_opts)+'"'  # TODO: could test if quotes already present

    dirname = kwargs.pop('dirname', os.path.curdir)

    wt = Timedelta(hours=walltime)
    walltime = wt.strftime("%h:%M:%S")
    wall_hours = wt.ashours

    def write_script(template):
        submitscript = os.path.join(dirname, prefix + os.path.basename(template))
        logger.info("Setting up queuing system script {submitscript!r}...".format(**vars()))
        # These substitution rules are documented for the user in the module doc string
        qsystem = detect_queuing_system(template)
        if qsystem is not None and (qsystem.name == 'Slurm'):
            cbook.edit_txt(template,
                           [('^ *DEFFNM=','(?<==)(.*)', deffnm),
                            ('^#.*(-J)', '((?<=-J\s))\s*\w+', jobname),
                            ('^#.*(-A|account_no)', '((?<=-A\s)|(?<=account_no\s))\s*\w+', budget),
                            ('^#.*(-t)', '(?<=-t\s)(\d+:\d+:\d+)', walltime),
                            ('^ *WALL_HOURS=', '(?<==)(.*)', wall_hours),
                            ('^ *STARTDIR=', '(?<==)(.*)', startdir),
                            ('^ *NPME=', '(?<==)(.*)', npme),
                            ('^ *MDRUN_OPTS=', '(?<==)("")', mdrun_opts),  # only replace literal ""
                            ('^# JOB_ARRAY_PLACEHOLDER', '^.*$', jobarray_string),
                            ],
                           newname=submitscript)
            ext = os.path.splitext(submitscript)[1]
        else:
            cbook.edit_txt(template,
                           [('^ *DEFFNM=','(?<==)(.*)', deffnm),
                            ('^#.*(-N|job_name)', '((?<=-N\s)|(?<=job_name\s))\s*\w+', jobname),
                            ('^#.*(-A|account_no)', '((?<=-A\s)|(?<=account_no\s))\s*\w+', budget),
                            ('^#.*(-l walltime|wall_clock_limit)', '(?<==)(\d+:\d+:\d+)', walltime),
                            ('^ *WALL_HOURS=', '(?<==)(.*)', wall_hours),
                            ('^ *STARTDIR=', '(?<==)(.*)', startdir),
                            ('^ *NPME=', '(?<==)(.*)', npme),
                            ('^ *MDRUN_OPTS=', '(?<==)("")', mdrun_opts),  # only replace literal ""
                            ('^# JOB_ARRAY_PLACEHOLDER', '^.*$', jobarray_string),
                           ],
                           newname=submitscript)
            ext = os.path.splitext(submitscript)[1]
        if ext in ('.sh', '.csh', '.bash'):
            os.chmod(submitscript, 0o755)
        return submitscript

    return [write_script(template) for template in config.get_templates(templates)]


def generate_submit_array(templates, directories, **kwargs):
    """Generate a array job.

    For each ``work_dir`` in *directories*, the array job will
     1. cd into ``work_dir``
     2. run the job as detailed in the template
    It will use all the queuing system directives found in the
    template. If more complicated set ups are required, then this
    function cannot be used.

    :Arguments:
       *templates*
          Basic template for a single job; the job array logic is spliced into
          the position of the line ::
              # JOB_ARRAY_PLACEHOLDER
          The appropriate commands for common queuing systems (Sun Gridengine, PBS)
          are hard coded here. The queuing system is detected from the suffix of
          the template.
       *directories*
          List of directories under *dirname*. One task is set up for each
          directory.
       *dirname*
          The array script will be placed in this directory. The *directories*
          **must** be located under *dirname*.
       *kwargs*
          See :func:`gromacs.setup.generate_submit_script` for details.
    """
    dirname = kwargs.setdefault('dirname', os.path.curdir)
    reldirs = [relpath(p, start=dirname) for p in asiterable(directories)]
    missing = [p for p in (os.path.join(dirname, subdir) for subdir in reldirs)
               if not os.path.exists(p)]
    if len(missing) > 0:
        logger.debug("template=%(template)r: dirname=%(dirname)r reldirs=%(reldirs)r", vars())
        logger.error("Some directories are not accessible from the array script: "
                     "%(missing)r", vars())
    def write_script(template):
        qsystem = detect_queuing_system(template)
        if qsystem is None or not qsystem.has_arrays():
            logger.warning("Not known how to make a job array for %(template)r; skipping...", vars())
            return None
        kwargs['jobarray_string'] = qsystem.array(reldirs)
        return generate_submit_scripts(template, **kwargs)[0]   # returns list of length 1

    # must use config.get_templates() because we need to access the file for detecting
    return [write_script(template) for template in config.get_templates(templates)]


def isMine(self, scriptname):
        """Primitive queuing system detection; only looks at suffix at the moment."""
        suffix = os.path.splitext(scriptname)[1].lower()
        if suffix.startswith('.'):
            suffix = suffix[1:]
        return self.suffix == suffix

def isMine ( self , scriptname ) : suffix = os . path . splitext ( scriptname ) [ 1 ] . lower ( ) if suffix . startswith ( '.' ) : suffix = suffix [ 1 : ] return self . suffix == suffix

Primitive queuing system detection ; only looks at suffix at the moment .


def anumb_to_atom(self, anumb):
        '''Returns the atom object corresponding to an atom number'''

        assert isinstance(anumb, int), "anumb must be integer"

        if not self._anumb_to_atom:   # empty dictionary

            if self.atoms:
                for atom in self.atoms:
                    self._anumb_to_atom[atom.number] = atom
                return self._anumb_to_atom[anumb]
            else:
                self.logger("no atoms in the molecule")
                return False

        else:
            if anumb in self._anumb_to_atom:
                return self._anumb_to_atom[anumb]
            else:
                self.logger("no such atom number ({0:d}) in the molecule".format(anumb))
                return False


def total_regular_pixels_from_mask(mask):
    """Compute the total number of unmasked regular pixels in a masks."""

    total_regular_pixels = 0

    for y in range(mask.shape[0]):
        for x in range(mask.shape[1]):
            if not mask[y, x]:
                total_regular_pixels += 1

    return total_regular_pixels

def total_regular_pixels_from_mask ( mask ) : total_regular_pixels = 0 for y in range ( mask . shape [ 0 ] ) : for x in range ( mask . shape [ 1 ] ) : if not mask [ y , x ] : total_regular_pixels += 1 return total_regular_pixels

Compute the total number of unmasked regular pixels in a masks .


def mask_circular_annular_from_shape_pixel_scale_and_radii(shape, pixel_scale, inner_radius_arcsec, outer_radius_arcsec,
                                                           centre=(0.0, 0.0)):
    """Compute an annular masks from an input inner and outer masks radius and regular shape."""

    mask = np.full(shape, True)

    centres_arcsec = mask_centres_from_shape_pixel_scale_and_centre(shape=mask.shape, pixel_scale=pixel_scale, centre=centre)

    for y in range(mask.shape[0]):
        for x in range(mask.shape[1]):

            y_arcsec = (y - centres_arcsec[0]) * pixel_scale
            x_arcsec = (x - centres_arcsec[1]) * pixel_scale

            r_arcsec = np.sqrt(x_arcsec ** 2 + y_arcsec ** 2)

            if outer_radius_arcsec >= r_arcsec >= inner_radius_arcsec:
                mask[y, x] = False

    return mask

Compute an annular masks from an input inner and outer masks radius and regular shape .


def mask_blurring_from_mask_and_psf_shape(mask, psf_shape):
    """Compute a blurring masks from an input masks and psf shape.

    The blurring masks corresponds to all pixels which are outside of the masks but will have a fraction of their \
    light blur into the masked region due to PSF convolution."""

    blurring_mask = np.full(mask.shape, True)

    for y in range(mask.shape[0]):
        for x in range(mask.shape[1]):
            if not mask[y, x]:
                for y1 in range((-psf_shape[0] + 1) // 2, (psf_shape[0] + 1) // 2):
                    for x1 in range((-psf_shape[1] + 1) // 2, (psf_shape[1] + 1) // 2):
                        if 0 <= x + x1 <= mask.shape[1] - 1 and 0 <= y + y1 <= mask.shape[0] - 1:
                            if mask[y + y1, x + x1]:
                                blurring_mask[y + y1, x + x1] = False
                        else:
                            raise exc.MaskException(
                                "setup_blurring_mask extends beyond the sub_grid_size of the masks - pad the "
                                "datas array before masking")

    return blurring_mask

Compute a blurring masks from an input masks and psf shape .


def edge_pixels_from_mask(mask):
    """Compute a 1D array listing all edge pixel indexes in the masks. An edge pixel is a pixel which is not fully \
    surrounding by False masks values i.e. it is on an edge."""

    edge_pixel_total = total_edge_pixels_from_mask(mask)

    edge_pixels = np.zeros(edge_pixel_total)
    edge_index = 0
    regular_index = 0

    for y in range(mask.shape[0]):
        for x in range(mask.shape[1]):
            if not mask[y, x]:
                if mask[y + 1, x] or mask[y - 1, x] or mask[y, x + 1] or mask[y, x - 1] or \
                        mask[y + 1, x + 1] or mask[y + 1, x - 1] or mask[y - 1, x + 1] or mask[y - 1, x - 1]:
                    edge_pixels[edge_index] = regular_index
                    edge_index += 1

                regular_index += 1

    return edge_pixels

def edge_pixels_from_mask ( mask ) : edge_pixel_total = total_edge_pixels_from_mask ( mask ) edge_pixels = np . zeros ( edge_pixel_total ) edge_index = 0 regular_index = 0 for y in range ( mask . shape [ 0 ] ) : for x in range ( mask . shape [ 1 ] ) : if not mask [ y , x ] : if mask [ y + 1 , x ] or mask [ y - 1 , x ] or mask [ y , x + 1 ] or mask [ y , x - 1 ] or mask [ y + 1 , x + 1 ] or mask [ y + 1 , x - 1 ] or mask [ y - 1 , x + 1 ] or mask [ y - 1 , x - 1 ] : edge_pixels [ edge_index ] = regular_index edge_index += 1 regular_index += 1 return edge_pixels

Compute a 1D array listing all edge pixel indexes in the masks . An edge pixel is a pixel which is not fully \ surrounding by False masks values i . e . it is on an edge .


def bin_up_mask_2d(mask_2d, bin_up_factor):
    """Bin up an array to coarser resolution, by binning up groups of pixels and using their sum value to determine \
     the value of the new pixel.

    If an array of shape (8,8) is input and the bin up size is 2, this would return a new array of size (4,4) where \
    every pixel was the sum of each collection of 2x2 pixels on the (8,8) array.

    If binning up the array leads to an edge being cut (e.g. a (9,9) array binned up by 2), an array is first \
    extracted around the centre of that array.

    Parameters
    ----------
    mask_2d : ndarray
        The 2D array that is resized.
    new_shape : (int, int)
        The (y,x) new pixel dimension of the trimmed array.
    origin : (int, int)
        The oigin of the resized array, e.g. the central pixel around which the array is extracted.

    Returns
    -------
    ndarray
        The resized 2D array from the input 2D array.

    Examples
    --------
    array_2d = np.ones((5,5))
    resize_array = resize_array_2d(array_2d=array_2d, new_shape=(2,2), origin=(2, 2))
    """

    padded_array_2d = array_util.pad_2d_array_for_binning_up_with_bin_up_factor(
        array_2d=mask_2d, bin_up_factor=bin_up_factor, pad_value=True)

    binned_array_2d = np.zeros(shape=(padded_array_2d.shape[0] // bin_up_factor,
                                      padded_array_2d.shape[1] // bin_up_factor))

    for y in range(binned_array_2d.shape[0]):
        for x in range(binned_array_2d.shape[1]):
            value = True
            for y1 in range(bin_up_factor):
                for x1 in range(bin_up_factor):
                    padded_y = y*bin_up_factor + y1
                    padded_x = x*bin_up_factor + x1
                    if padded_array_2d[padded_y, padded_x] == False:
                        value = False

            binned_array_2d[y,x] = value

    return binned_array_2d

Bin up an array to coarser resolution by binning up groups of pixels and using their sum value to determine \ the value of the new pixel .


def setup_figure(figsize, as_subplot):
    """Setup a figure for plotting an image.

    Parameters
    -----------
    figsize : (int, int)
        The size of the figure in (rows, columns).
    as_subplot : bool
        If the figure is a subplot, the setup_figure function is omitted to ensure that each subplot does not create a \
        new figure and so that it can be output using the *output_subplot_array* function.
    """
    if not as_subplot:
        fig = plt.figure(figsize=figsize)
        return fig

def setup_figure ( figsize , as_subplot ) : if not as_subplot : fig = plt . figure ( figsize = figsize ) return fig


def output_figure(array, as_subplot, output_path, output_filename, output_format):
    """Output the figure, either as an image on the screen or to the hard-disk as a .png or .fits file.

    Parameters
    -----------
    array : ndarray
        The 2D array of image to be output, required for outputting the image as a fits file.
    as_subplot : bool
        Whether the figure is part of subplot, in which case the figure is not output so that the entire subplot can \
        be output instead using the *output_subplot_array* function.
    output_path : str
        The path on the hard-disk where the figure is output.
    output_filename : str
        The filename of the figure that is output.
    output_format : str
        The format the figue is output:
        'show' - display on computer screen.
        'png' - output to hard-disk as a png.
        'fits' - output to hard-disk as a fits file.'
    """
    if not as_subplot:

        if output_format is 'show':
            plt.show()
        elif output_format is 'png':
            plt.savefig(output_path + output_filename + '.png', bbox_inches='tight')
        elif output_format is 'fits':
            array_util.numpy_array_2d_to_fits(array_2d=array, file_path=output_path + output_filename + '.fits',
                                              overwrite=True)

def output_figure ( array , as_subplot , output_path , output_filename , output_format ) : if not as_subplot : if output_format is 'show' : plt . show ( ) elif output_format is 'png' : plt . savefig ( output_path + output_filename + '.png' , bbox_inches = 'tight' ) elif output_format is 'fits' : array_util . numpy_array_2d_to_fits ( array_2d = array , file_path = output_path + output_filename + '.fits' , overwrite = True )

Output the figure either as an image on the screen or to the hard - disk as a . png or . fits file .


def output_subplot_array(output_path, output_filename, output_format):
    """Output a figure which consists of a set of subplot,, either as an image on the screen or to the hard-disk as a \
    .png file.

    Parameters
    -----------
    output_path : str
        The path on the hard-disk where the figure is output.
    output_filename : str
        The filename of the figure that is output.
    output_format : str
        The format the figue is output:
        'show' - display on computer screen.
        'png' - output to hard-disk as a png.
    """
    if output_format is 'show':
        plt.show()
    elif output_format is 'png':
        plt.savefig(output_path + output_filename + '.png', bbox_inches='tight')
    elif output_format is 'fits':
        raise exc.PlottingException('You cannot output a subplots with format .fits')

Output a figure which consists of a set of subplot either as an image on the screen or to the hard - disk as a \ . png file .


def image_psf_shape_tag_from_image_psf_shape(image_psf_shape):
    """Generate an image psf shape tag, to customize phase names based on size of the image PSF that the original PSF \
    is trimmed to for faster run times.

    This changes the phase name 'phase_name' as follows:

    image_psf_shape = 1 -> phase_name
    image_psf_shape = 2 -> phase_name_image_psf_shape_2
    image_psf_shape = 2 -> phase_name_image_psf_shape_2
    """
    if image_psf_shape is None:
        return ''
    else:
        y = str(image_psf_shape[0])
        x = str(image_psf_shape[1])
        return ('_image_psf_' + y + 'x' + x)

Generate an image psf shape tag to customize phase names based on size of the image PSF that the original PSF \ is trimmed to for faster run times .


def inversion_psf_shape_tag_from_inversion_psf_shape(inversion_psf_shape):
    """Generate an inversion psf shape tag, to customize phase names based on size of the inversion PSF that the \
    original PSF is trimmed to for faster run times.

    This changes the phase name 'phase_name' as follows:

    inversion_psf_shape = 1 -> phase_name
    inversion_psf_shape = 2 -> phase_name_inversion_psf_shape_2
    inversion_psf_shape = 2 -> phase_name_inversion_psf_shape_2
    """
    if inversion_psf_shape is None:
        return ''
    else:
        y = str(inversion_psf_shape[0])
        x = str(inversion_psf_shape[1])
        return ('_inv_psf_' + y + 'x' + x)

Generate an inversion psf shape tag to customize phase names based on size of the inversion PSF that the \ original PSF is trimmed to for faster run times .


def bulge_disk_tag_from_align_bulge_disks(align_bulge_disk_centre, align_bulge_disk_axis_ratio, align_bulge_disk_phi):
    """Generate a tag for the alignment of the geometry of the bulge and disk of a bulge-disk system, to customize \
    phase names based on the bulge-disk model. This adds together the bulge_disk tags generated in the 3 functions
    above
    """
    align_bulge_disk_centre_tag = align_bulge_disk_centre_tag_from_align_bulge_disk_centre(
        align_bulge_disk_centre=align_bulge_disk_centre)
    align_bulge_disk_axis_ratio_tag = align_bulge_disk_axis_ratio_tag_from_align_bulge_disk_axis_ratio(
        align_bulge_disk_axis_ratio=align_bulge_disk_axis_ratio)
    align_bulge_disk_phi_tag = align_bulge_disk_phi_tag_from_align_bulge_disk_phi(
        align_bulge_disk_phi=align_bulge_disk_phi)

    return align_bulge_disk_centre_tag + align_bulge_disk_axis_ratio_tag + align_bulge_disk_phi_tag

Generate a tag for the alignment of the geometry of the bulge and disk of a bulge - disk system to customize \ phase names based on the bulge - disk model . This adds together the bulge_disk tags generated in the 3 functions above


def compute_deflections_at_next_plane(plane_index, total_planes):
    """This function determines whether the tracer should compute the deflections at the next plane.

    This is True if there is another plane after this plane, else it is False..

    Parameters
    -----------
    plane_index : int
        The index of the plane we are deciding if we should compute its deflections.
    total_planes : int
        The total number of planes."""

    if plane_index < total_planes - 1:
        return True
    elif plane_index == total_planes - 1:
        return False
    else:
        raise exc.RayTracingException('A galaxy was not correctly allocated its previous / next redshifts')

This function determines whether the tracer should compute the deflections at the next plane .


def scaled_deflection_stack_from_plane_and_scaling_factor(plane, scaling_factor):
    """Given a plane and scaling factor, compute a set of scaled deflections.

    Parameters
    -----------
    plane : plane.Plane
        The plane whose deflection stack is scaled.
    scaling_factor : float
        The factor the deflection angles are scaled by, which is typically the scaling factor between redshifts for \
        multi-plane lensing.
    """

    def scale(grid):
        return np.multiply(scaling_factor, grid)

    if plane.deflection_stack is not None:
        return plane.deflection_stack.apply_function(scale)
    else:
        return None

Given a plane and scaling factor compute a set of scaled deflections .


def grid_stack_from_deflection_stack(grid_stack, deflection_stack):
    """For a deflection stack, comput a new grid stack but subtracting the deflections"""

    if deflection_stack is not None:
        def minus(grid, deflections):
            return grid - deflections

        return grid_stack.map_function(minus, deflection_stack)

def grid_stack_from_deflection_stack ( grid_stack , deflection_stack ) : if deflection_stack is not None : def minus ( grid , deflections ) : return grid - deflections return grid_stack . map_function ( minus , deflection_stack )

For a deflection stack comput a new grid stack but subtracting the deflections


def constant_regularization_matrix_from_pixel_neighbors(coefficients, pixel_neighbors, pixel_neighbors_size):
    """From the pixel-neighbors, setup the regularization matrix using the constant regularization scheme.

    Parameters
    ----------
    coefficients : tuple
        The regularization coefficients which controls the degree of smoothing of the inversion reconstruction.
    pixel_neighbors : ndarray
        An array of length (total_pixels) which provides the index of all neighbors of every pixel in \
        the Voronoi grid (entries of -1 correspond to no neighbor).
    pixel_neighbors_size : ndarrayy
        An array of length (total_pixels) which gives the number of neighbors of every pixel in the \
        Voronoi grid.
    """

    pixels = len(pixel_neighbors)

    regularization_matrix = np.zeros(shape=(pixels, pixels))

    regularization_coefficient = coefficients[0] ** 2.0

    for i in range(pixels):
        regularization_matrix[i, i] += 1e-8
        for j in range(pixel_neighbors_size[i]):
            neighbor_index = pixel_neighbors[i, j]
            regularization_matrix[i, i] += regularization_coefficient
            regularization_matrix[i, neighbor_index] -= regularization_coefficient

    return regularization_matrix

From the pixel - neighbors setup the regularization matrix using the constant regularization scheme .


def weighted_regularization_matrix_from_pixel_neighbors(regularization_weights, pixel_neighbors,
                                                        pixel_neighbors_size):
    """From the pixel-neighbors, setup the regularization matrix using the weighted regularization scheme.

    Parameters
    ----------
    regularization_weights : ndarray
        The regularization_ weight of each pixel, which governs how much smoothing is applied to that individual pixel.
    pixel_neighbors : ndarray
        An array of length (total_pixels) which provides the index of all neighbors of every pixel in \
        the Voronoi grid (entries of -1 correspond to no neighbor).
    pixel_neighbors_size : ndarrayy
        An array of length (total_pixels) which gives the number of neighbors of every pixel in the \
        Voronoi grid.
    """

    pixels = len(regularization_weights)

    regularization_matrix = np.zeros(shape=(pixels, pixels))

    regularization_weight = regularization_weights ** 2.0

    for i in range(pixels):
        for j in range(pixel_neighbors_size[i]):
            neighbor_index = pixel_neighbors[i, j]
            regularization_matrix[i, i] += regularization_weight[neighbor_index]
            regularization_matrix[neighbor_index, neighbor_index] += regularization_weight[neighbor_index]
            regularization_matrix[i, neighbor_index] -= regularization_weight[neighbor_index]
            regularization_matrix[neighbor_index, i] -= regularization_weight[neighbor_index]

    return regularization_matrix

From the pixel - neighbors setup the regularization matrix using the weighted regularization scheme .


def plot_figure(array, as_subplot, units, kpc_per_arcsec, figsize, aspect, cmap, norm, norm_min, norm_max,
                linthresh, linscale, xticks_manual, yticks_manual):
    """Open a matplotlib figure and plot the array of data on it.

    Parameters
    -----------
    array : data.array.scaled_array.ScaledArray
        The 2D array of data which is plotted.
    as_subplot : bool
        Whether the array is plotted as part of a subplot, in which case the grid figure is not opened / closed.
    units : str
        The units of the y / x axis of the plots, in arc-seconds ('arcsec') or kiloparsecs ('kpc').
    kpc_per_arcsec : float or None
        The conversion factor between arc-seconds and kiloparsecs, required to plot the units in kpc.
    figsize : (int, int)
        The size of the figure in (rows, columns).
    aspect : str
        The aspect ratio of the array, specifically whether it is forced to be square ('equal') or adapts its size to \
        the figure size ('auto').
    cmap : str
        The colormap the array is plotted using, which may be chosen from the standard matplotlib colormaps.
    norm : str
        The normalization of the colormap used to plot the image, specifically whether it is linear ('linear'), log \
        ('log') or a symmetric log normalization ('symmetric_log').
    norm_min : float or None
        The minimum array value the colormap map spans (all values below this value are plotted the same color).
    norm_max : float or None
        The maximum array value the colormap map spans (all values above this value are plotted the same color).
    linthresh : float
        For the 'symmetric_log' colormap normalization ,this specifies the range of values within which the colormap \
        is linear.
    linscale : float
        For the 'symmetric_log' colormap normalization, this allowws the linear range set by linthresh to be stretched \
        relative to the logarithmic range.
    xticks_manual :  [] or None
        If input, the xticks do not use the array's default xticks but instead overwrite them as these values.
    yticks_manual :  [] or None
        If input, the yticks do not use the array's default yticks but instead overwrite them as these values.
    """

    fig = plotter_util.setup_figure(figsize=figsize, as_subplot=as_subplot)

    norm_min, norm_max = get_normalization_min_max(array=array, norm_min=norm_min, norm_max=norm_max)
    norm_scale = get_normalization_scale(norm=norm, norm_min=norm_min, norm_max=norm_max,
                                         linthresh=linthresh, linscale=linscale)

    extent = get_extent(array=array, units=units, kpc_per_arcsec=kpc_per_arcsec,
                        xticks_manual=xticks_manual, yticks_manual=yticks_manual)

    plt.imshow(array, aspect=aspect, cmap=cmap, norm=norm_scale, extent=extent)
    return fig

def plot_figure ( array , as_subplot , units , kpc_per_arcsec , figsize , aspect , cmap , norm , norm_min , norm_max , linthresh , linscale , xticks_manual , yticks_manual ) : fig = plotter_util . setup_figure ( figsize = figsize , as_subplot = as_subplot ) norm_min , norm_max = get_normalization_min_max ( array = array , norm_min = norm_min , norm_max = norm_max ) norm_scale = get_normalization_scale ( norm = norm , norm_min = norm_min , norm_max = norm_max , linthresh = linthresh , linscale = linscale ) extent = get_extent ( array = array , units = units , kpc_per_arcsec = kpc_per_arcsec , xticks_manual = xticks_manual , yticks_manual = yticks_manual ) plt . imshow ( array , aspect = aspect , cmap = cmap , norm = norm_scale , extent = extent ) return fig

Open a matplotlib figure and plot the array of data on it .


def get_normalization_min_max(array, norm_min, norm_max):
    """Get the minimum and maximum of the normalization of the array, which sets the lower and upper limits of the \
    colormap.

    If norm_min / norm_max are not supplied, the minimum / maximum values of the array of data are used.

    Parameters
    -----------
    array : data.array.scaled_array.ScaledArray
        The 2D array of data which is plotted.
    norm_min : float or None
        The minimum array value the colormap map spans (all values below this value are plotted the same color).
    norm_max : float or None
        The maximum array value the colormap map spans (all values above this value are plotted the same color).
    """
    if norm_min is None:
        norm_min = array.min()
    if norm_max is None:
        norm_max = array.max()

    return norm_min, norm_max

def get_normalization_min_max ( array , norm_min , norm_max ) : if norm_min is None : norm_min = array . min ( ) if norm_max is None : norm_max = array . max ( ) return norm_min , norm_max

Get the minimum and maximum of the normalization of the array which sets the lower and upper limits of the \ colormap .


def set_colorbar(cb_ticksize, cb_fraction, cb_pad, cb_tick_values, cb_tick_labels):
    """Setup the colorbar of the figure, specifically its ticksize and the size is appears relative to the figure.

    Parameters
    -----------
    cb_ticksize : int
        The size of the tick labels on the colorbar.
    cb_fraction : float
        The fraction of the figure that the colorbar takes up, which resizes the colorbar relative to the figure.
    cb_pad : float
        Pads the color bar in the figure, which resizes the colorbar relative to the figure.
    cb_tick_values : [float]
        Manually specified values of where the colorbar tick labels appear on the colorbar.
    cb_tick_labels : [float]
        Manually specified labels of the color bar tick labels, which appear where specified by cb_tick_values.
    """

    if cb_tick_values is None and cb_tick_labels is None:
        cb = plt.colorbar(fraction=cb_fraction, pad=cb_pad)
    elif cb_tick_values is not None and cb_tick_labels is not None:
        cb = plt.colorbar(fraction=cb_fraction, pad=cb_pad, ticks=cb_tick_values)
        cb.ax.set_yticklabels(cb_tick_labels)
    else:
        raise exc.PlottingException('Only 1 entry of cb_tick_values or cb_tick_labels was input. You must either supply'
                                    'both the values and labels, or neither.')

    cb.ax.tick_params(labelsize=cb_ticksize)

Setup the colorbar of the figure specifically its ticksize and the size is appears relative to the figure .