Contents

Why?

This guide is for ICM users who want to learn how to create or understand software written in ICM’s full-featured scripting language. This guide is not a comprehensive reference of the full capabilities of ICM and its scripting language but it is designed to provide a good overview of the topics important to someone with some general programming experience who wishes to take advantage of ICM’s programmability.

Usage

Interpreter Considerations

When creating non-interactive scripts in the ICM scripting language, you must use the correct program loader directive. This must reflect a path to your ICM executable. It may be reasonable if the program is to run autonomously to call ICM without the overhead of a graphical user interface. This is done with icmng. The following is a typical useful first line for ICM script files:

#!/usr/local/bin/icm/icmng -s
Note
In the lab, our path is /pro/icm/icm/ or if you’re trying to emphasize stability try /pro/icm/icms/.

The -s option stands for "silent" because it suppresses all of the start up messages.

Version

To find out what version of ICM you are using you can use the following features:

icm/def> show version
 Info> icm version
3.7-3a/Lin-iX86-64bit/g4.4/Release/HardwareGL/Gui263940/WebKit/Video/FlexLM
[Jan 18 2013 17:10], path="/pro/icm/icm/icm" UNIX BCDZEHPQRXUVM
icm/def> Version()
3.7-3a/Lin-iX86-64bit/g4.4/Release/HardwareGL/Gui263940/WebKit/Video/FlexLM/icm
UNIX BCDZEHPQRXUVM

If you’re in an environment where the ICM is updated frequently, it is often useful to consider the date in the version string as this is when this ICM executable was actually compiled.

Interpreter Initialization

When ICM starts, it loads a lot of files. These include system-wide and user-specific start-up files, as well as special files in the ICM home directory (like _macro and icm.*).

Moreover, ICM is controlled by preferences. Users may have non-default preferences that are stored and loaded from their home directory. Adding to the complexity is the fact that specific paths and the exact list of loaded files depends on (1) the ICM command line options, (2) the version of ICM, and (3) the operation system.

On Linux, the following locations can contain special user-specific functionality.

~/.icm
~/.molsoft
~/.browser
~/.browerpro
~/.qt           <= This is non-trivial.

Interactive

When using ICM’s command line interactively, you will see icm/def>. This prompt is defined with the s_icmPrompt variable. It can be set with things like %t> which will show the elapsed time since this shell was started, like this:

icm/def> s_icmPrompt = "%t>"
00:00:27>

What does def in icm/def> mean? Not really sure.

When an object is specified by itself in the shell, the interpreter evaluates and prints object’s value (like the Python interpreter). This can be handy for debugging.

Style

Literal Interpretation

The ICM scripting language usually does not need to decorate variables to indicate that is what they are (like Bash and Perl do with $). It adds defined variables to the correct name space and evaluates them when the name is encountered. However, ICM also allows for explicit variable evaluation. For example:

icm/def> a ="b" ; s="a"
icm/def> print s $s
a b

Don’t get too carried away with this or you’ll get an error like:

Warning> [1] $expressions will not be evaluated in substituted $variables

Formatting

Semi-colons can be used to separate commands (since version 3.6-1). They are not required to terminate a statement. Note that the interpreter’s history mechanism will store multiple statements separated by semi-colons as multiple history entries. This differs from other interpreted shells such as Bash or Python. It probably is wise to avoid using semi-colons.

Space is an important delimiter. If you do something like this:

x="*"
print x     # Returns "*"
print x x   # Returns "* *"
print xx    # Error if variable xx is not defined!
print x+x   # Returns "**"

Although space is important here, it’s not as important as it might seem. For example:

print x   x   # Returns "* *"

In general you can put as many spaces as you like as a delimiter.

Comments

At the beginning of a line, a # character causes the interpreter to ignore the following comment. In the middle of a line the # causes the interpreter to ignore subsequent text allowing for comments like:

x="*" # A star is born.

Interestingly in ICM’s shell history a comment is not even recorded, so making notes to yourself in interactive sessions for history recall probably won’t work.

Naming Conventions

ICM commands tend to use all lower case letters (example delete). ICM’s built in functions tend to capitalize the first letter of the function (example Nof()). ICM macros tend to use what Perl people call "camel case" where the name is descriptively several concatenated words with each capitalized (example MakePdbFromStereo).

Types

ICM’s scripting language has many types here are some of the important ones with their official abbreviations:

Table 1. ICM Types

i

integer

r

real

s

string

l

logical

I

iarray

R

rarray

S

sarray

M

matrix

seq

sequence

prf

profile

ali

alignment

m

map

g

graphics object (grob)

vs

selections of internal coordinates (for example torsion angles)

T

table

T.I

table array of integers

T.R

table array of reals

T.S

table array of strings

There are types involving selections that can get quite complex: os_::object selection ms_::molecule selection rs_::residue selection as_::any kind of selection (object/molecule/residue/atom); in some cases means only atom selection aselection:: generic type of os_, ms_, rs_ and as_

There are some other types which do not necessarily have official abbreviations: vselection, command, macro

There may be more.

Assignment

For simple object usage, things are simple:

Assign an integer: a=1

Assign a real: b=1.

Assign a character (string) type: c="a string"

ICM’s scripting language is pretty strongly typed for a scripting language. In Perl or Python a variable can contain one type and then be reassigned to a different type implicitly casting it to that new type. ICM does not force you to explicitly declare a variable’s type but it does require that you be consistent with it as the following example shows:

icm> a=3
icm> a="*"
 Error> [696] wrong assignment or name conflict
icm> delete a
icm> a="*"
icm> print a
*

It is possible to convert variables in place if there is sufficient information. For example:

icm/def> aninteger=1
icm/def> aninteger=2.1
 Error> [696] wrong assignment or name conflict
icm/def> afloat=2.1
icm/def> afloat=1
icm/def> print afloat
1.

In this case it was possible to make the 1 into a 1.0 but ICM didn’t want to make the 2.1 into a 2 automatically.

If you want to see all of the integers that are defined, you can use show integer. This works for string, real, map, and others.

Casting

If you need to explicitly work with a variable’s type you can use one of the many functions provided for this:

Integer()

Converts function argument to an integer type.

Tointeger()

A (redundant?) function similar to Integer()?

Iarray()

Converts an array of non-integers to an array of integers if possible.

Real()

Converts function argument to a real type.

Toreal()

A (redundant?) function similar to Real()?

Rarray()

Converts to a real array if possible.

String()

Converts function argument to a string type.

Tostring()

A (redundant?) function similar to String()?

Sarray()

Converts to a string array if possible.

Type()

Returns the type of an object.

Table()

A very versatile function that produces various table objects.

Grob()

A versatile function to generate graphics objects.

Shell Variables

One property of ICM that is somewhat idiosyncratic is that many functions, in addition to possibly handling input and output, also set internal ICM shell variables which can be used in subsequent operations. This is a bit like the Perl variable $_ which is magically set after certain functions run, allowing subsequent functions to assume some preparatory action (or as the Perl people say, "underline is understood to be underlying certain undertakings").

In ICM the following automatic variables are sometimes set by functions and can be used by subsequent operations.

i_out , i_2out , r_out , l_out , s_out , R_out , as_out

Apparently you can modify these variables with some caveats. You can not delete them. Also functions will overwrite anything you put in there if given a chance.

Strings

Strings can be defined in a few ways. You can use quotes pretty intuitively:

x='a string'
y="also a string"
z="ICM's strings"

You can also use a Python style triple quote style like this:

icm/def> Gmaj="""3
?> 0
?> 0
?> 0
?> 2
?> 3"""
icm/def> print Gmaj
3
0
0
0
2
3

ICM has escape sequences for several common symbols like \", \t, \n.

Strings can be treated like arrays to an extent. You can do something like this similar to Python slices:

icm/def> t='abc'
icm/def> print t[2]
b
icm/def> print t[1:2]
ab

Strings can be formatted the C way with the printf command. To just format the string without printing it per se, see sprintf. To send formatted string output to a file, see fprintf (also see the IO section below).

Functions To Work With Strings

+

Concatenates two strings. Sometimes, as with the print command, if strings are arguments, they get concatenated automatically. Best not to rely or base a programming style on that.

Field()

Extracts a particular field (as in a text database) from a line of text. The line of text must have the fields delimited in some sensible way. Here’s an example with comma separated values: If iron= "Fe,[Ar] 3d6 4s2,26" then Field(iron,3,',') is "26". Without the third (separator argument) the assumption is whitespace.

Index()

Returns position of specified substring ("cd") in specified string ("abcdefg"). Index('abcdefg', 'cd') returns 3. With an optional third argument last, the position of the last occurrence is returned. If the optional third argument is a positive number it starts the search after that position. If it’s a negative number it searches the end. For example, to check for the presence of a file name extension, Index(fn,'gz',-2). This will return a positive number if it ends in gz.

Length()

Returns how long a string is. This is useful for iterating over each character and tasks like that. Many things in ICM use Nof() but not strings.

Match()

Returns anything in the first (string) argument that matches the second argument’s regular expression. For example, this finds a valid looking (but possibly out of range) IP address: Match('192.168.33.132', '\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}')

Replace()

Replace occurrences of the second argument in the first with the third. For example Replace('grace','g','rat') produces "ratrace". Fancier things are possible too such as Replace('Miss Smith', {'Miss','Mrs.'}, 'Ms.'). Regular expressions can be used too.

Split()

To cut a string into parts. This returns a string array. For example: Split('alice&bob&eve','&')[2] returns "bob". To just get a string array of all the letters, make the character to split upon empty ("").

Tolower()

Converts strings to lower case.

Tostring()

Converts other types to a string. Tostring( (1==1) )+"=true" produces "yes=true".

Toupper()

Converts strings to upper case.

Trim()

Removes trailing whitespace from a string. 'x' + Trim(' abcde ' ) + 'x' returns x abcdex. Note that only the end is trimmed. However, with a second argument, all both ends are trimmed. If the second argument is a string, only the letters in the second argument are preserved from the string. So Trim("$45US",'0123456789') produces 45.

Type()

For strings, returns the string "string". Helpful to make sure you really are working with a string.

Arrays

Arrays are ordered containers of consistently typed objects.

a={1,2,3}
Nof(a) # Function to return "Number of"
  3
delete variable a 2 # deletes second value of the array `a`.
Nof(a)
  2

The command list is used to "display" variables, i.e. not for anything related to an itemized list in the Python or Lisp sense.

icm/def> list wireStyle
  wireStyle        = "chemistry"

With no arguments, it will give you a list of defined variables.

ICM has a type of array that points to other types of objects. This is a "pointer array", the parray type. This allows you to store heterogeneous typed objects in one object.

Tables

The first thing to clarify is that there exist "predefined icm-shell tables" (described here) which are not "user-defined ICM tables" (described here). Just be aware that sometimes tables are referred to (the "Tables" link on the molsoft.com/man page) and they don’t mean the kind you’ll typically use. That said, many ICM features like to generate a table object that the user can use.

Tables are not simply two dimensional arrays. Tables are a first class data type that contain and organize arrays of other types. A table is a specific data structure that may have zero or more columns (arrays with data) and zero or more headers (which also may be arrays).

You can check to see if an ICB file contains a table and what that table’s name is with this.

icm/def> list binary "myfile.icb"
Binary file version: 16
   1 msLigandModelAll               table             13599724 0
icm/def> read binary list name="msLigandModelAll" "myfile.icb"
 Info> 1 objects from myfile.icb kept in msLigandModelAll

The most basic way to create a table is to use group table. This takes at least one argument which comes next and must be the name of the table.

icm/def> group table an_empty_new_user_table
 Info> table  an_empty_new_user_table  (0 headers, 0 arrays) has been created
  Headers:
  Arrays :
icm/def> Type(an_empty_new_user_table)
table

To verify that you have created a table or to find your table after forgetting what you named it, use list tables. You’ll notice that there are many built in tables in ICM.

The next thing to do is to add "columns" to the table. This can be with the group table command.

icm/def> group table t {10,20,30} "tens" {1,2,3} "ones"
 Info> table  t  (0 headers, 2 arrays) has been created
  Headers:
  Arrays : t.tens t.ones
icm/def> t
#>T t
#>-tens--------ones-------
   10          1
   20          2
   30          3
icm/def> Type(t)
table
icm/def> Type(t.ones)
iarray

Columns can also be added with the add column command. This also can create empty tables. Here’s an example of both.

icm/def> add column abc
 Info> 0 columns '' added to table 'abc'
icm/def> add column abc {'A' 'B' 'C'} name="column_name_here"
 Info> 1 column  'column_name_here' added to table 'abc'
icm/def> show abc
#>T abc
#>-column_name_here
   A
   B
   C

If working with tables interactively, you can invoke the system’s text editor and make arbitrary edits to tables with the following command: edit t

group table t {1 2 3} "a" {4. 5. 7.} "b"
delete t.a == 2       # the second entry
show t
delete t[2]           # the second entry
show t
delete t              # the whole thing
group table t {1 2 3} "a" {4. 5. 7.} "b"
delete t.a > 1          # 2nd and 3rd

You can also change the value of individual cells with a command like:

icm/def> t.a[1] = 0

Accessing Table Elements

To access individual table components, you can use t.a[1] if you know and can explicitly name the cell. However to iterate over all the cells can be a bit trickier.

Here’s the problem:

icm/def> Ufields=Name(atable)
icm/def> Ufields[3]
atable.SM
icm/def> Type(Ufields[3])
string
icm/def> Type(atable.SM)
sarray

This means that to iterate over the table’s values, you need to do something like this:

Ufields=Name(atable)
for i=1,Nof(Ufields)
  s_col = Ufields[i]
  for j=1,Nof( $s_col )
     $s_col [j]   # row=i, col=j
  endfor
endfor

Here is another recommended way:

Ufields=Field(Name(atable) 2 '.' )
for i=1,Nof(atable)
  row = Collection( atable[i] )
  for j=1,Nof( Ufields )
     row[ Ufields [j] ]
  endfor
endfor

Hash/Collection Objects

FAQ: What’s a "map"? A hash like C+\+? An electron density map? It looks like there is a type collection. There is documentation on this and on my ICM they seem to work, but there are also references which make it seem like "collections" are being renamed into "hash" objects. However ER says that "collection" is official and correct and "hash" was some intermediate version.

To use them do something like the following:

icm/def> myattribs="{'width':'100','height':'200'}"
icm/def> c=Collection(myattribs  )
icm/def> c
{
 "height": "200",
 "width": "100"
}
icm/def> c['width']
100

FAQ: How do you iterate over the keys in a collection? Answer:

c = Collection( "aaa","bbb" )
S_keys = Name( col )
for i=1,Nof(S_keys)
    print c[ S_keys[i] ]
endfor

How to tell if an object is a key in a collection (Basically obj.has_key(key) in Python.)?

if ( Index(Name(P_myCollection),'PossibleKey')) then
    print "Yes, that's a key."
endif

Apparently Collection(web) is a facility for parsing CGI input but I am still trying to figure out how. Eugene says, "It checks REQUEST_METHOD first, then either parses QUERY_STRING for GET or stdin for POST."

To use collections as function parameters, you need to name the parameter something like: P_MyCollection.

Clearing State

When scripting with ICM it is sometimes desirable to reset the values of all the objects (variables, molecular objects, tables, etc) without having to exit or restart the session. The delete all command will remove everything you created except for protected items (set property delete off/on). This is extremely useful in processing multiple ICB files, a task that ICM scripting frequently performs.

Operators

Logical Operators

&

Logical and - "yes & no" equals "no", "yes & yes" equals "yes"

|

Logical or - "no | no" equals "no", other combinations equal "yes"

!

Logical not - "no | !yes" equals "no"

Assignment

+= Assign result of right hand operator and left hand value to left hand operator. a+=3 is the same as a=a+3. The same is true for subtraction -=, multiplication *=, and division /=.

There is also an in place assignment operator can assign an array concatenation:

t.Name//="new_item"

This usage may not be robust and you may be asking for an Error 4127 when you try to append a column to a table. Needs some more experimentation perhaps.

"//" This is a concatenation operator. This makes a rarray (an array of reals): b=3.0//4.1//5.2

The copy operator is used for molecular objects. Normal assignment is fine for normal variables, but molecular objects should, apparently, be created explicitly with the copy command.

Comparison

The logical operator for testing if something is the same as something else is ==. To test if it’s not the same use !=. Also inequality operators work as expected: <, >, <=, >=.

That all sounds reasonable, but Andrey points out that there can be odd exceptions. For example, in a fresh ICM session the following behavior is observed:

icm/def> if (Nof(Obj(a_*.) ) == 0) print "there is no object"
icm/def> if (Nof(Obj(a_*.) ) != 0) print "there is at least one object"
icm/def>

Arithmetic Expressions

http://www.molsoft.com/man/arithmetics.html All the normal algebraic operators work, +, -, *, /. Note that, like Python, the result of dividing two integers is an integer. This can produce unexpected results:

icm> 3.0/4 - 3/4
0.75

This happens because 3/4 (both integers) equals 0 which is 0.75 with the decimal (non integer) part discarded.

Arrays do not handle simple arithmetic operators like you might expect. If + concatenated two arrays, what would / do? Instead, when arithmetic operators are used on arrays, a new array is formed by the operation being performed on each value of the array (in order). This allows for things like this:

icm> a={1,2,3}; b={2,3,4}
icm> b-a
#>I
  1
  1
  1

Note that if you want array concatenation, you can do that with the // operator.

Pattern Matching

*

matches any string including an empty string (e.g. *see* )

?

matches any single character (e.g. ???ee M)

[string]

matches any one of the enclosed characters. Two characters separated by dash represent a range of characters. Examples: [A-Z], [a-Z], [a-z], [0-9] (e.g. [A-Z] see [A-Z])

[ !string]

negation. matches any but the enclosed characters (e.g. I see [!K])

{}

single-character multiplication, character{m,n} (e.g. I?\{3,6\}M - repeat any character, ?, from 3 to 6 times)

There are also operators for pattern matching, ~ and !~. Here’s an example:

icm> somesa.Name={"white","knight","fights","windmills"}
icm> print (somesa.Name ~ "*gh*") // (somesa.Name !~ "*gh*")
knight fights white windmills

Regular expressions Regular expressions can be used with the functions Match(), Replace(), Index(), Split()

Actually, ICM has several different kinds of regular expressions in different functions and selections. for instance, the Replace function recognizes two different sorts of expressions and its behavior is controlled by the regexp keyword. Sometimes we have to create multiple escape sequences to make selections work, e.g.

Index(S_ Replace(s_ "([[*(+.+)?]])" "\\\\\\1" regexp) regexp all)

Control Structures

Branching

if

Conditional statements aren’t too confusing but there are (at least) a couple of modes to consider. First is a single line type of thing like:

if (Random() < 0.5) print "Tails"

The more complete construction is something like this:

if (Random() < 0.5) then
    print "H"
elseif (Random() == .5) then
    print "Lands on edge!"
else
    print "T"
endif

You can omit the elseif and/or else clauses as necessary.

An important consideration when using if statements is the nature of the conditional expression. This must evaluate to a "logical" type which can take the value yes or no, both reserved words for signifying this.

icm/def> A= yes
icm/def> A
  yes
icm/def> if ( A ) print "OK"
OK

Iteration

The normal way to iterate is to iterate over a range using a for statement.

icm> for i=1,5
icm>    print i
1
icm> endfor
2
3
4
5

I don’t know if there are direct ways to iterate over the items in an array, but there are definitely techniques to achieve that effect which are commonly used:

somestuff={'alpha','bravo','charlie','delta'}
for i=1,Nof(somestuff)
    print somestuff[i]
endfor

While

It seems that ICM does not have a do-while type of control structure. The conditional expression of a while statement is always evaluated before any of the body is executed. Under ideal circumstances, that looks like:

while ( Random() < 5.0/6.0 )
   print "Still playing Russian roulette!"
endwhile

To get a do-while effect the commonly used idiom looks like this:

#!/pro/icm/icms/icmng64 -R

total= 0
while (yes)
    read sarray keep separator="\n" limit=100 "bigtextfile" name="btf"
    print "Nof(btf):"+Nof(btf)
    for i=1,Nof(btf)
        total+= Tointeger( btf[i] )
    endfor
    if (l_out) break
endwhile
print "Total:"+total
quit

The output looks like:

Nof(btf):100
Nof(btf):100
Nof(btf):100
Nof(btf):100
Nof(btf):8
Nof(btf):0
Total:6744368

Note the use of the break key word. The limit flag is used to work in batches of 100 until there are not 100 left and when the remaining are processed.

In this case the while is really being used as a backwards goto. ICM has a goto which can jump forward to a label formatted like LABEL:, but it can’t jump backwards. So the while is used.

FAQ: Is there some kind of iteration using {} expansion somehow?

Modular Code - Function Definition

The most basic way to organize functionality into different containers is to use the call command to import ICM commands contained in other files. This is about like source in Unix shell languages.

Here’s an example:

$ echo "a='Sample'" > example.icm
$ echo "print a"  >> example.icm
$ echo "quit"  >> example.icm
$ icm -s -e 'call "example.icm"'
Sample

Note that this hangs for some reason. The quit command may not have obvious behavior in these circumstances.

FAQ: What’s the difference between:

exit [s_message]

Quits to an interactive ICM shell.

break

Exits from loops and macros.

return

is for macros and functions.

quit

Quits script completely. Useful for CGI programs (i.e. not shell spawned).

Although ICM has a read command which will load a particular file, there is no shell-style source command per se. Apparently, the entire concept is inadvisable anyway because when ICM is updated, not all external files will necessarily be compatible with the old version.

FAQ: Is there an eval kind of command? Answer: There is. Basically the $ is an eval.

icm/def> mystatement='show s_pdbDir'
icm/def> $mystatement
ftp://ftp.rcsb.org/pub/pdb/data/structures/divided/pdb/

Also consider the following example:

s = "print 'hello'"
 set property command s
# or
 set property command s auto  # for autoexec status

I don’t really know what that does.

Commands

Commands are provided by ICM and can not be created or modified by normal users. They take options and arguments (not necessarily in that order). They can not be nested like functions.

FAQ: Is there an exit code equivalent? Answer: Yes. Look at the result of Error() after running a command.

Functions

ICM has numerous built in functions. Stylistically, these start with a capital letter and contain parameters (if any) in parentheses. In addition to taking parameters, functions can return values and can be used in assignments and nested expressions.

Functions can also be created by users using the following technique:

#!/pro/icm/icm/icmng -s
function UserDefinedFunction s_argument1
    myarg='Passed by reference?'
    return "The function returns:" + s_argument1
endfunction

myarg='Passed by value?'
print UserDefinedFunction(myarg)
print myarg
quit

This returns:

The function returns:Passed by value?
Passed by value?

Here is an example using an arbitrary number of arguments:

#!/pro/icm/icm/icmng -s
function Switcher s_argument1 s_argument2
    return s_argument2 + s_argument1
endfunction

a= 'Chicken'
b= 'Egg'
print Switcher(a,b)  # Outputs "EggChicken"
quit

Note that function (and macro) parameters must have a special naming scheme. There must be a prefix of s_ for string types, r_ for real types, i_ for integer types, etc. The complete list of how this works is here. However, note that advanced complex types that aren’t listed, for example collections, should use the P_ prefix. The point to all this is that if the parameters are all of different types, then the argument order of the function call can be arbitrary.

FAQ: Is there a way to make a "module" or "package" that can keep name spaces clear? Answer: Use ‘call` to import user defined functions. Functions’ variables are local to them.

Note
When investigating ICM functions it is wise to read the manual entry on macros too since they are only subtly different and much of the explanation for functions was taken as given in the documentation for macros.

Macros

Macros tend to have names capitalized like this: sortSeqByLength. Of course there are exceptions (homodel, cool, nice, morph2tz, set_icmff). The show command can help figure out what’s going on with macros.

It can do things like show the contents of a macro. You can also see all the defined macros. For example, show macro shows the whole list of them with their parameters.

It is rumored that, like commands, macros can not be used in direct nested expressions. Functions can like: a( b(x) )

If you need to get rid of some macro that you created, you can do it with:

delete macro anIllAdvisedMacro
Note
I don’t know if it’s a smart thing to do, but you can delete the ICM preconfigured macros from your session (they come back when you rerun ICM). This might be handy if ICM has taken some name you want to use or otherwise keep free.

Also be aware of the set key command which can bind keys to a particular action. For example:

set key "F1" "set plane 1\n"

Of course this is already done and if you try to do it again, it will say `key [F1] is already bound.

Also show key to see what keys are already defined.

Note
I actually couldn’t get key binding to work at all. Maybe it’s a GUI only thing.

FAQ: How does one delete or modify key bindings?

Alias

You can do show alias to reveal all of the currently defined aliases. You can also do list alias if you like to have options.

alias <name> <action> delete alias <name>

Generally it is not recommended to use aliases in scripts.

FAQ: What’s the difference between an alias and a macro?

Input/Output

Environment

You can query the state of environment variables from ICM with the Getenv function. It works like this:

user= Getenv("USER")
if (user=="guest") then
   print "You're a guest"
endif

If you’re unsure if an environment variable exists, you can look at something like:

Existenv("USER")

And if you need to set your own environment variables Putenv().

Option And Argument Parsing

ICM has its own way of doing options that is not exactly like normal Unix scripts. While not great for consistency with other shell scripting arrangements, it is however designed to have less redundant features and is often much less complicated to implement.

I do not think that ICM scripts have a built in way to handle short style options. Handling long options, however, is quite simple.

To use options with ICM scripts you can pass the options to a running ICM session with the -a option when running ICM (the ICM executable does have some short style options, they’re just not available for easy parsing in your scripts).

Note
The options you can pass to the ICM executable can be found here.

Here’s the simplest demonstration of argument handling:

$ /pro/icm/icms/icmng64 -R -a x=3 y=simple
icm/def> Getarg("x")
3
icm/def> Getarg("y")
simple

Note: You can have arguments like -x=3 if you want, but you have to use "-x" in the Getarg() function. Probably best to just avoid that potential confusion and leave the dashed options for the icm executable.

This makes a more complex example clearer:

#!/pro/icm/icms/icmng64 -R
HELP= "Usage: $P [help] [a=<alpha>] [b=<beta>] [c] <arguments>"

if Getarg(help) quit HELP

# Pull out the options that the program expects.
alpha= Getarg("a","ADEFAULTVALUE",delete)
beta=  Getarg("b","",delete)
gamma= Getarg("c","",delete)
scriptarguments= Getarg(list,delete)

if ( Nof(scriptarguments) < 1 ) quit " Error> At least one argument is required.\n" HELP

errorAction  = "quit"

if(scriptarguments) then
  print "a=", alpha
  print "b=", beta
  print "c=", gamma
  print scriptarguments
  quit
endif

Reading From Files

The most basic way to read data from a file in ICM is to create a string array with each line of the file being an item in the array. This looks something like:

read sarray comment name="mystory" "mystory.txt"

The keyword comment causes lines that begin with the # character to be skipped.

When reading some kind of data, it is probably best to employ one of ICM’s fancier file reading methods. Here’s a comparison of the two methods. First the file is loaded as text into a string array. You can see that the file has a header row that isn’t part of the data per se.

icm/def> read sarray name="aa" "mydata.csv"
 Info> 21 elements of sarray 'aa' loaded from mydata.csv
icm/def> aa[1:4]
#>S string_array
NAME,SHORT,CODE
Alanine,Ala,A
Arginine,Arg,R
Asparagine,Asn,N

Next the data is loaded into a table object. This allows its header to be correctly understood and used and the fields correctly parsed.

icm/def> read table separator="," header name="aa" "mydata.csv"
 Info> table 'aa' ([3] columns) has been created
icm/def> aa[1:4]
#>T aa
#>-NAME--------SHORT-------CODE-------
   Alanine     Ala         A
   Arginine    Arg         R
   Asparagine  Asn         N
   "Aspartic acid" Asp         D

If you’re reading something astronomically huge you may not wish to have it all load into an ICM object. You may not have enough memory.

FAQ: How does one read data from files? Need to include CSV into tables (read table), text into Sarrays (read sarray), etc. Also need to show how to do something with each line of text as it comes in and not store it in a list. (see limit=n_records)

FAQ: Maybe talk about stream strategy somewhere. Is a stream a type?

Reading From User

The Ask() function can allow a script to pause and ask for user input.

icm/def> n = Ask("Enter a number", 5)
  Enter a number : 5
icm/def> n
  5

In this case the default value is "5" and it is prompted. The behavior above is from just hitting enter at the prompt. Note that a colon is added so hopefully that is indeed the formatting you want.

FAQ: When using string types, you can use the keyword simple after the default value specification (no additional comma) - what does this do?

Also there is the pause command. Normally you supply an argument specifying how many seconds to pause, however, if you don’t, it will wait until the user hits a key.

Printing

Generally the contents of objects can be output by using the print command.

There is also a show command in ICM with 1000s of uses. There is also a list command with some print-like abilities.

ICM provides normal printf mechanisms for outputting explicitly formatted text. There is printf which outputs to stdout and the special variable s_out. There is sprintf which only saves the output in the special variable s_out.

Writing To Files

There is fprint which works as follows for outputting directly to files:

fprintf append "overheat.log" "%s %.2f\n" "CPU1Temp", 42.4

FAQ: What will an .icb file be? Can a "session" be saved from a script? Any other odd cases? An entire ICM session can be saved from a script with the following approach:

$ icm -s
icm/def> a
 Error> [1073]  a: unknown word
icm/def> a = Pi
icm/def> a
  3.14159
icm/def> write binary all "session.icb" delete
icm/def> quit
$ icm -s
icm/def> a
 Error> [1073]  a: unknown word
icm/def> read binary "session.icb"
icm/def> a
  3.14159

FAQ: Tables to CSV? See the write command which can do this.

Here’s an example of a table being written to a comma separated value file:

group table t {1 2 3} "a" {"one","two","three"} "b"
write t header separator="," "t.csv"
unix cat t.csv
a,b
1,one
2,two
3,three
write t separator="," "t.csv"  # without header
unix cat t.csv
1,one
2,two
3,three

This can be recalled with the following:

read table separator="," header name="t" "t.csv"

ICM Web Technologies

Let’s say that you have an interest in creating a web service and you think that it might be a good idea to use ICM for some or all of that project. It’s fairly obvious why you wouldn’t use ICM as there are many good ways to make web services. Here are some of the compelling reasons that you might want to consider using ICM.

  • Your project involves chemistry, biology, genetics, proteins, etc.

  • Other parts of your project use ICM.

  • Your project involves chemical or genetic databases.

  • Your project involves protein docking.

  • You would like to have molecules depicted graphically.

  • You really like ICM.

Running ICM As A CGI Program

If you want the web server to not just deliver the source code of an ICM program, but rather execute it and deliver the results, you must configure that in the web server configuration. There are known places where this is done globally, but when you want to do it in some arbitrary directory, you can use a .htaccess file which allows you to make custom local configuration changes. To have files that end in ".cgi", ".py", or ".icm" execute, create a .htaccess file in the directory where these programs will be served which looks like this:

.htaccess
AddHandler cgi-script .cgi .icm .py
Options +ExecCGI
SetEnv LM_LICENSE_FILE @lic.your-domain.com
Note
To minimize problems, it is good when developing ICM scripts to make sure that they always run with a LM_LICENSE_FILE variable set. This applies to CGI programs. Including it here ensures that it is done. Make sure to use the correct server or location for your ICM license.

CGI Execution Of ICM

The first line of your CGI script should contain a line that looks something like one of these:

#!/yourpathtoICM/icm -w/yourpathtoICM
#!/yourpathtoICM/icm -s
#!/yourpathtoICM/icmng -s

There are different reasons to use the different flags and executables. I’m including them here to show what has worked in various situations. Experiment.

It looks like -w, for example will "webify" things a bit. One of the features of this mode is that error messages are somewhat decorated by HTML. This is true even when run from the console. For example, I got this HTML fragment as an error:

<hr><h3>Error: [1073]  enfor: unknown word</h3><hr>

Don’t forget to make your script "executable" with something like chmod a+x myscript.icm. A good strategy is to develop programs that can run on the command line or with a web interface.

Libraries

It cat be frustrating to have your procedure work fine in an interactive command line session and then fail spectacularly when put into a program. Specifically it wills seem like valid commands are missing. This can happen if macro libraries, which are loaded automatically during interactive sessions, fail to load.

The cure is to simply include something like this into your scripts:

call "/pro/icm/icm/_macro"
read library

Generating HTML

Some people don’t mind mixing up HTML and proper programming but I find that it makes syntax highlighting terrible and it opens a lot of possibilities for errors trying to match opening and closing tags. Matching tags is the kind of thing a computer should be doing and when writing real programs, you can let the program do this. Here is my function to do this translated to ICM from my Python version.

html_tagger.icm
#!/pro/icm/icm/icmng -s
#html_tagger.icm - Chris X Edwards

#No HTML in my programs! This function functionalizes HTML tags.
#Example: tag('a','click here', "{'href':'http://www.xed.ch'}")
#Produces: <a href="http://www.xed.ch">click here</a>
# Param1= name of tag (table, img, body, etc)
# Param2= contents of tag <tag>This text</tag>
# Param3= dictionary of attributes {'alt':'[bullet]','height':'100'}

function Tag s_tag s_content s_attlist
    s_tagstring= "<"+s_tag
    if (Type(s_content) == 'unknown') then
        s_content=''
    endif
    if (Type(s_attlist) != 'unknown') then
        attC= Collection(s_attlist)
        S_keys= Name( attC )
        for i=1,Nof(S_keys)
           s_att += ' '+S_keys[i]+'="'+attC[ S_keys[i] ]+'"'
        endfor
        s_tagstring += s_att
    endif
    if (Length(s_content) > 0) then
        s_tagstring += ">\n" + s_content + "\n</" + s_tag + ">\n"
    else
        s_tagstring += "/>\n"
    endif
    return s_tagstring
endfunction

s_title= Tag('head', Tag('title', "A Test"))
s_photo= Tag('img','',"{'src':'./photo.jpg','width':'100','height':'200'}")
s_par= Tag('p', "No html here. Just sensible code.")
s_hr= Tag('hr')
s_text= Tag('body', s_photo + s_par + s_hr,"{'bgcolor':'#cecece'}" )
print Tag('html', s_title + s_text)
quit

Running this will produce:

<html>
<head>
<title>
A Test
</title>

</head>
<body bgcolor="#cecece">
<img height="200" src="./photo.jpg" width="100"/>
<p>
No html here. Just sensible code.
</p>
<hr/>

</body>

</html>
Note
The quoting on the attribute dictionary is a bit strange in ICM but basically you need to use a JSON-like string that is itself entirely quoted.

HTML Documents In ICM

As described in this web page, ICM has the ability to have HTML documents in working memory and to display them in a type of browser (GUI, I presume). This is started with something like:

read html "a.html"  # reads and sets the property mask
# Create a new HTML document.
s = "<html><h1>TITLE</h1></html>"
set property html s

CGI Input

CGI Variable Handling

ICM has very convenient facilities to handle CGI input. The trick is that the Collection() function does a lot of fancy things with CGI query strings. It even handles POST methods for loading files that are uploaded from a browser. Here is a succinct example:

exampleCGIform.html
<html>
<form action="m.cgi" enctype="multipart/form-data" method="post">
<p>
Type some text (if you like):<br>
<input type="text" name="textline" size="30">
</p>
<p>
Please specify a file, or a set of files:<br>
<input type="file" name="datafile" size="40">
</p>
<div>
<input type="submit" value="Send">
</div>
</form>
</html>
exampleCGIscript.cgi
#!/yourpathtoICM/icm -w/yourpathtoICM

printf "Content-type: text/html;\n\n"

cc = Collection(web)
cc["param1"]  # get parameter
cc["datafile","body"]  # blob with file content
read binary input=cc["datafile","body"]  # read from blob
quit

Here is a function that processes a POST method for receiving and using binary files. This allows the uploaded file to never need to reside on the web server. This is only a part of such a program, but it clearly shows the important bits for handling POST binary file uploads.

function Processpost
    # Note "web" is an option keyword here, not a mysterious variable.
    c= Collection(web)
    webvars= Name(c)
    if (  Index(Name(c),'datafile')  ) then
        filebytes= Length(c['datafile','data'])
        if ( filebytes == 0 ) then
            return Makehtml( br+Tag('b', 'You did not specify a file!') )
        endif
    endif
    read binary input=c['datafile','data']
    fullreport= c['full'] # From radio button
    myresis= Name(a_./*)
    if ( c['full'] == '1' ) then
        msg= 'Full Report:' + br
        msg+= Sum(myresis,' + ')
    else
        msg= 'Summary Report:' + br
        msg+= Tostring(Nof(myresis)) + ' residues (and other junk).'
    endif
    return Makehtml( msg )
endfunction

Integrated Form And Handling

It is common to create an HTML document that contains a form and a CGI program that handles the data filled into the form. There is no reason not to use the flexibility of the form handling program to also generate the form. The huge advantage of this is that the form and the handling of the form elements will be in the same place. If, for example, you need to add or remove a field, you only need to edit one file. If you write the program sensibly, you might be able to only edit one thing for such a change. Here is an example of a CGI program that handles CGI data, or, if none is given, prompts for it with a form. This program requires the html_tagger function mentioned above.

Integrated Form And Handling
#!/yourpathtoICM/icm -s
# cgiform.cgi - A template for a complete CGI system in ICM.
# This version provides the framework for generating a form and, if
# parameters are supplied, processing the results of the form.
call 'html_tagger.icm'
CONTYPE= 'Content-type: text/html\n\n'

# Find name of this script from the web form's perspective.
fullscriptpath= File(last)
scriptparts= Split(fullscriptpath,'/')
thelast= Nof(scriptparts)
SCRIPT= './'+ scriptparts[thelast]

function Makeform
    formlabel= Tag('h4','This Is A Sample Form:')
    ff= Tag('p',Tag('input','Peptide?',"{'type':'checkbox','name':'pep','value':'1'}") )
    ff+= Tag('p',Tag('input','Protein?',"{'type':'checkbox','name':'pro','value':'1'}") )
    ff+= Tag('p',Tag('input','Cis',"{'type':'radio','name':'rb1','value':'C','checked':'1'}") )
    ff+= Tag('p',Tag('input','Trans',"{'type':'radio','name':'rb1','value':'T'}") )
    ff+= Tag('p',Tag('input','',"{'type':'submit'}") )
    f= Tag('form',ff, "{'action':'"+SCRIPT+"', 'method':'GET', 'enctype':'multipart/form-data'}")
    return formlabel + f
endfunction

function Processform
    c= Collection(web)
    s_formdata= String()
    S_keys= Name( c )
    for i=1,Nof(S_keys)
        s_formdata+= Tag('li',S_keys[i]+' : '+c[ S_keys[i] ])
    endfor
    return Tag('ul',s_formdata)
endfunction

function Makehtml s_contents
    s_title= Tag('head', Tag('title', 'A CGI Test'))
    s_body= Tag('body', s_contents,"{'bgcolor':'#cecece'}" )
    return Tag('html', s_title + s_body)
endfunction

# =============== Main =================
if ( Existenv('REQUEST_METHOD') ) then
    REQUEST_METHOD= Getenv('REQUEST_METHOD')  #  GET or POST - `web` #parser needs this
endif

message= Makeform()
if (REQUEST_METHOD == 'GET') then
    if ( Existenv('QUERY_STRING') & Length(Getenv('QUERY_STRING')) > 0 ) then
        message+= Processform() + 'Processed form!'
    endif
else
    message+= 'POST HTTP method not supported yet!'
endif

print CONTYPE + Makehtml(message)
quit

Outputting Non-Text Data

Sometimes you don’t want to send an HTML document to the requesting client. Sometimes you want to send a different kind of data. For example, you may be generating a plot or graphic on the fly based on CGI input and you need to return an image data stream. This can be tricky in ICM but the following technique handles it with no problems:

function ImgTest
    CONTYPE= "Content-type: image/png\n\n"
    write blob Blob(CONTYPE) "/dev/stdout"
    myPath= '/var/www/fs/users/myname/myapp/'
    s_iName="test.png"
    fullFileName= myPath + s_iName
    read blob name="tempimg" fullFileName
    write blob tempimg "/dev/stdout"
    # Make sure you make this function = something to prevent return's output.
    return ''
endfunction

This technique can be useful in a program that normally receives CGI input and returns an HTML page. But the program can also be set up to handle specially marked requests (http://url?getanimage=true) to return graphics and other content. Then the main HTML output can contain embedded links to the program effectively calling it again to furnish the rest of the content.

Sanitizing Input

If you are writing web applications, it is common for them to accept inputs to provide the custom functionality the user requires. Unfortunately bad people live on the internets and they do bad things. To prevent unintended use of your scripts, it is a good idea to "sanitize" the data you collect from the wild internet. This means that if you do not expect certain characters to be in your input, you should disallow them. Here is my basic sanitizing function.

Basic Sanitizing Function
function InputOK s_suspect
    good= 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-.'
    cleaned= Trim(s_suspect, good)
    if (Length(s_suspect) == Length(cleaned)) then
        return 1
    else
        return 0
    endif
endfunction

teststring= "This: #is not@ ok!"
if (InputOK(teststring)) then
    print "Ok."
else
    print "Error: Invalid characters in input."
endif

ICM Tables To HTML

One nice trick that ICM can do to assist web development is to convert ICM tables into very nice HTML tables. The basic way to do that is the following.

ICM to HTML Table Example
# Create a simple table.
add column t {1 2 3}  Chemical({"CCC","CCO","CCN"})

# Use the sort keyword to make the columns sortable.
# Chemicals will be exported as HTML5 canvases.
show html t sort

The sort keyword adds some magical stuff to the output so that the table is "sortable". More specifically, it comes with some Javascript code (or references to it) that will resort the table on the client’s browser. This code is based on Stuart Langridge’s "sortable" code. The Molsoft team have a version that lives here.

One problem with this code is that it generates a complete HTML document instead of an HTML fragment. This means that it would, for example, be less convenient to include two sortable tables from ICM tables in a single HTML document.

One way to solve this problem is to parse the output of this show html functionality. This is possible with the following function:

function HtmlTableSplit( s_tab l_sort )
   l_commands = l_info = no
    show html $s_tab output = "sss" !l_sort? : sort
    c = Collection( )
    c["head"] =  Match( sss, "(?n)<head>(.*)</head>" 1 )
    c["table"] =   Match( sss, "(?n)<body[^>]*>(.*)</body>" 1 )
    c["onload"] =  Match( sss, "<body.*?onload=(.*?)>" 1 )
   return c
endfunction

# .. use:
c = HtmlTableSplit( "t", yes )
# Then you need to add   c["head"] to the head section, the onload call
# inside the <body ... > definition and the table itself inside the body.

Another way around this limitation is to make your own tables explicitly. This may not be as convenient but sometimes if you want something done right, you have to do it yourself. Here are some functions that can produce simple HTML tables from an ICM table. Note that these assume the HTML tagger function discussed previously.

# Create an HTML header row from an ICM table.
function UIcmtab2htmltab_header T_the_table
    Ufields=Field(Name(T_the_table) 2 '.' )
    Uhttabhead= ''
    for Ui=1,Nof(Ufields)
        Uhttabhead //= Tag('th',Ufields[Ui])
    endfor
    Uhttabhead= Tag('tr',Uhttabhead)
    return Uhttabhead
endfunction

# Pull out table's cell data and make into correct HTML tr and td.
function UIcmtab2htmltab_cells T_the_table
    Uhttab= ''
    Ufields=Field(Name(T_the_table) 2 '.' )
    for Ui=1,Nof(T_the_table)
        Uhtrow= ''
        Urow = Collection( T_the_table[Ui] )
        for Uj=1,Nof( Ufields )
            Ucell= String( Urow[ Ufields [Uj] ])
            Uhtrow //= Tag('td',Ucell)
        endfor
        Uhttab //= Tag('tr',Uhtrow)
    endfor
    return Uhttab
endfunction

HTML5 Canvas

Pretty much all sane browsers today support the very useful HTML5 canvas tag which allows for composing arbitrary graphics using JavaScript. This has obvious implications for domain specific technical diagrams like molecular diagrams.

The Molsoft function man page has this tantalizing and extremely brief mention of the topic under the String() function

String ( X_chem, html ) - return HTML5 canvas and rendering JavaScript

I don’t know exactly what that means. What is an X_chem? Is it different from X_chemarray?

Here is a function which will create the HTML5 canvas tag. Note that if you have multiple ones of these they will all be in the same place. The way I fixed that was to do something like this:

Ucell= Replace(Ucell,"chem_1","chem_"+String(Ui))

Do this in the loop where Ui is the number of the molecule you’re on. Basically make them all different if you want it to render to different canvases.

Note that you need to specify the size of the canvas too, it’s not optional.

# Create an HTML5 <canvas> tag graphical representation of a SMILES string.
function USmiles2canvas s_smiles
    Ucanw= 200
    Ucanh= 200
    Ucan= String( Chemical(s_smiles) Ucanw Ucanh html)
    return Ucan
endfunction

I also found that the executable I used did matter. Some versions of ICM may not have this feature compiled in. Check the error message for String(x y). Look for:

String( <X_chem> i_w i_h html ) => <s_html5_canvas>

If that is part of the error message for String, then you can be pretty sure it should work.

SVG

It is also possible to export chemical as svg.

write image Chemical("CCCO") "xxx.svg"

Note that this requires that you be running ICM in graphical mode (-g).

ActiveICM

A discussion of web technologies featuring ICM would not be complete without discussing ActiveICM. This is a browser plugin which can allow full 3-d molecular models to be rendered and manipulated in a browser window. The down side to this is that the plug in can be tricky to get working. Even though it costs no money, the downloading and configuration can limit spontaneity. However, if the investment is made, one is empowered by a truly excellent way to view molecules from a web browser. ActiveICM is vastly better than the popular Java alternatives and as a bonus you don’t need to have Java, the security nightmare, installed.

ActiveICM Client Browser Plugin

To get ActiveICM working on my Ubuntu installation, I had to add some things.

sudo apt-get install libxt-dev libxt6
sudo apt-get install libxext-dev libxext6
sudo apt-get install libgtkgl2.0-1 libgtkgl2.0-dev
sudo apt-get install libgtkglext1-dev libgtkglext1

You’ll need the plugin which can be downloaded from the Molsoft website.

For Linux it comes as a gzipped tar file. Do something like this to unpack:

tar -xvzf activeicm-1.1-7.x64.tgz

Change to the activeicmplugin directory that is created and run the installer with:

sh activeicm-plugin-installer

For me on Ubuntu 12.04.2 LTS, I got the following error:

ERROR: Your glibc library is older than 2.3.
       Please update your glibc library.

Fortunately I found this similar problem and the solution seemed to work. So basically comment out the if statement around the "valid-glibc" check so that it always echoes "valid-glibc". Once the installer runs cleanly, it should work. For me it works on chromium-browser and firefox.

To test, try the following sites:

Creating ActiveICM Content

<html> <body>

<OBJECT ID="ActiveIcmCtl" WIDTH="100%" HEIGHT="100%" type="application/x-molsoft-icb">
    <param name="projectFile" value="YOURICMSTUFFHERE.icb">

<!-- Good Idea to provide the link to get ActiveICM or help if it's not working. -->
    Click
    <a href="http://www.molsoft.com/getbrowser.cgi?product=activeicm&act=list"
                                                                     target="_blank">
        here
    </a> to download ActiveICM plug-in
</OBJECT>

</body></html>

To make something that can be viewed you need to provide an ICM binary file (YOURICMSTUFFHERE.icb shown above). To make one of these, I think you pretty much just have to do something like:

build smiles name="mefloquine" "OC(C1CCCCN1)C1=CC(=NC2=C1C=CC=C2C(F)(F)F)C(F)(F)F"
write binary mefloquine "./mefloquine.icb"

However, this didn’t quite work for me. I think maybe the object never got "displayed" and it could be displayed without a graphical session or a virtual X server session. This is not an esoteric issue since if you are writing web software to create custom molecular objects to view, they will probably be run on a system with no graphics.

Examples Of ICM Web Applications

The team at Molsoft has written some nice applications in JavaScript that shows this off. These might be useful references:

Reserved Words

The online reference for ICM functions is here.

There are a lot of reserved words that you should avoid. Sometimes there is an alias (ux), a command (unix), and a function (Unix()). With such a prodigious collection of reserved words, it might be a good idea to prefix your variables with something distinctive.

Aliases

There are many aliases that are predefined so it’s a good idea not to use the same identifier for your stuff. For example, q is an alias for the quit command. To see all the aliases use list alias.

Table 2. Some Default Aliases

ali

alignment

at

atom

cd

set directory "$1"

cn

drestraint

cp

copy

csv

table separator=","

ds

display

dsb

(complicated)

dsbb

(complicated)

dssp

assign sstructure

echo

print

ey

energy

hi

history 20

gd

gradient

la

label

lb

library

ls

list

lsdock

list macro "dock"

lsar

list iarray rarray sarray

mc

montecarlo

ml

molecule

mt

matrix

mv

move

prf

profile

protect

set property delete off

unprotect

set property delete on

re

residue

rex

read object "/data/icm/xpdb/$1"

getpdblist

(complicated)

GT

(complicated)

rm

delete

rs

vrestraint

sar

sarray

seq

sequence

shdb

show database

ssbond

disulfide bond

ty

types

tz

tether

unds

undisplay

ux

unix

va

variable

gapOpen

gapFunction[1]

gapExtension

gapFunction[2]

shininess

GRAPHICS.light[1]

QUOTE

"\""

MAX

Max(Max( $1 ))

MIN

Min(Min( $1 ))

SUM

Sum(Sum( $1 ))

BS

build string "$0"

COLRELIA

color ribbon $1 Smooth( $1 , -Score( $1 ) , 6. )

DIFF

a_/A & Replace(Replace( $1 ,"[- ]","%"),"[!.#~+^]"," ")

FRAG

(complicated)

NORM

($1 - Mean($1))/Rmsd($1)

PSEQID

(complicated)

SEQID

(complicated)

SEQIDo

(Max(25.,290.15/Power($1,0.562))+5.)

remark

set comment a_ Sum(Namex(a_)) + "$0"

rinx

read index s_inxDir + "$1"

rsbase

read sequence SBASE.ID=="$1"

rswiss

read sequence SWISS.ID=="$1"

loadVtConf

(complicated)

ssbase

show SBASE.ID=="$1"

sswiss

show SWISS.ID=="$1"

pf

ds skin Sphere(a_3//* 7) & a_1,2 a_1,2

WIN

(View()[35:36])

ICM_LITE

(Index(Version(),"mini")!=0)

testall

call s_icmhome+"test/all"

HDR_PROP_OFF

dummy=""

sys

unix

FR_FIRST

-1

FR_PREV

-2

FR_NEXT

-3

FR_LAST

-4

ST_UNDEF

0

ST_TWEEN

1

ST_ROTATE

2

ST_ROCK

3

ST_STILL

4

sys

unix

GROUPTAB

group table append $s_projName header

dockIdentSites

icmPocketFinder $1 6. yes yes

COLRELIA

color ribbon $1 Smooth( $1 , -Score( $1 ) , 10.)

searchSWS

readUniprot

Commands

The following key words are built in commands in ICM:

Reserved Words Which Are Options To Commands

accessibility all amber angle antialias area aselection atom auto auxiliary axis background ball bar base bfactor binary bold bond born boundary box bw catalog cavity cell chain charge chemical chiral cistrans cmyk column command comment comp_matrix conf cpk csd dash database directory distance disulfide dot drestraint energy error evolution exact exclude factor field filename filter flat font foreground formal format frame full gamess gcg genome gif gradient graphic grid grob hbond header heavy html hydrogen iarray icmdb idb identity image input integer intensity italic iupac join jpeg json kernel last lection left library limit local logical map margin material matrix memory merit mmcif model mol mol2 moldb molsar movie msf mute name new none nosort number object off on only oracle origin output page parray pattern pca pdb pharmacophore pipe pir plane pmf png postscript potential pov preference preview problem profile project property pseudo query rainbow rarray reaction real reflection refresh regression residue resolution restore rgb ribbon right ring salt sarray segment selftether separator sequence session similarity simple size skin slide sln solid solution sql sstructure stack static stdin stdout stereo stick string surface svariable symmetry system table targa term tether texture topology torsion transparent type underline unix unknown user variable version view virtual vrestraint vselection water wavefront weight window wire xplor xstick

More Like A System Variable Than A Command?

Functions

Math Functions

The following functions are related to mathematical computation.

Numbers
Geometry and Trigonometry

Programming Functions

The following functions are useful for performing software related tasks.

Clock
Program Management

Chemistry

I am not a chemist or even someone who really should ever be using ICM but sometimes I need to do simple things with it involving its actual real purpose, chemistry. Here is a tiny example collection of simple chemistry related things that ICM can do.

SMILES to SDF

Need an SDF file to work with but you don’t have one? Often if you know what the molecule is you can hunt down it’s SMILES string on Wikipedia and do this.

icm/C4H10O3> ethylene_oxide="C1CO1"
icm/C4H10O3> String(Chemical(ethylene_oxide))
C2H4O
  MOLSOFT 01031707042D

  3  3  0  0  0  0  0  0  0  0999 V2000
    0.0000    0.0000    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
    1.2124    0.7000    0.0000 C   0  0  0  0  0  0  0  0  0  0  0  0
    1.2124   -0.7000    0.0000 O   0  0  0  0  0  0  0  0  0  0  0  0
  1  2  1  0  0  0  0
  3  1  1  0  0  0  0
  2  3  1  0  0  0  0
M  END

$$$$

Create 3D Molecular Model

To take a SMILES string and make a 3D model use the build command.

build smiles ethylene_oxide

You can give it a name with name="eto" (mind the quotes on the name). If you don’t name it, it will get a chemical name (like "H2O").

If you need to build a protein or a peptide chain, that is extremely easy to do. Going with the easiest, you can just load a model from PDB.

read pdb "3hag"

I’m not sure if this converts from bad PDB representation to proper ICM version. You can also just build the molecule yourself from a peptide sequence. Here’s Lixisenatide, a drug for treating diabetes made from something resembling Gila monster venom.

build string name="gomesin3" "XC1RRLC2YKQRC2VTYC1RGR"
randomize v_//!V,omg 180.0  # create random starting conformation

Selections

Selections can be tricky. You can select atoms, residues, basically anything conceivable. Here’s the full documentation on selections. Generally selections look like this.

prefix _ [ object(s) . ] molecule(s) / residue(s) / atom(s) or variable(s)

Here I load a PDB structure and then delete all the atoms that aren’t part of a particular alpha helix I’m interested in.

read pdb "1ove"
delete a_1ove./!252:262

Double clicking a workspace object will select atoms in an object. And double clicking empty space in the hierarchical workspace list will unselect everything.

Select something with a box using right button and then erase everything but that with this.

delete !as_graph

Select all? It’s something like this, but probably not exactly.

select a_*//*

Analysis

How to extract the DrugLikeness property from the Predict function. Shown here starting with a SMILES string.

Predict(Chemical('Oc1ccc(O)cc1C(C)(C)C')).DrugLikeness
-0.887574
Predict(Chemical('Cc1c(OC)c(C)cnc1CS(=O)c2nc3ccc(OC)cc3n2')).DrugLikeness
0.88296

Negative are not drug like, positive are.

Superimpose

Often objects wander around in space and you’d really like them to be pointed the same way in the same place. Here’s what the Expert technique is.

superimpose a_1.1 a_2.1 align minimize

Display

These commands do a good job of putting the stuff on the screen.

center
center static

Here is a way to get rid of the cartoons ribbon display that usually is popular.

GRAPHICS.ribbonWorm=yes
ribbonStyle=5

Thanks

Thanks to Andrey I. for contributions and review of this guide. None of the mistakes in this have anything to do with him! Same for The Expert.

Questions

  • How do I load a table from an icb file, ex. /pro/bin/molScreenOutputMerge.icm output. See ~/X/P/molscreen/folic_acid.icb

  • How do I show all objects that are loaded? Don’t know, but have a look at variable t when loading an icb file.