Index: /issm/trunk-jpl/externalpackages/gmsh/install-4-mac-python-static.sh
===================================================================
--- /issm/trunk-jpl/externalpackages/gmsh/install-4-mac-python-static.sh	(revision 25761)
+++ /issm/trunk-jpl/externalpackages/gmsh/install-4-mac-python-static.sh	(revision 25762)
@@ -34,5 +34,5 @@
 
 # Copy customized source and config files to 'src' directory
-cp configs/4/mac/static/CMakeLists.txt src
+cp configs/${VER}/mac/static/CMakeLists.txt src
 
 # Configure
Index: /issm/trunk-jpl/jenkins/pine_island-mac-binaries-python
===================================================================
--- /issm/trunk-jpl/jenkins/pine_island-mac-binaries-python	(revision 25761)
+++ /issm/trunk-jpl/jenkins/pine_island-mac-binaries-python	(revision 25762)
@@ -46,5 +46,4 @@
 	boost		install-1.7-mac-static.sh
 	dakota		install-6.2-mac-static.sh
-	curl		install-7.67-static.sh
 	netcdf		install-4.7-mac-parallel-static.sh
 	proj		install-6.2-static.sh
@@ -75,5 +74,5 @@
 # 		compilation
 #
-NUMCPUS_INSTALL=4
+NUMCPUS_INSTALL=8
 
 # Number of CPUs used in the nightly runs
Index: /issm/trunk-jpl/scripts/py_to_pyc.sh
===================================================================
--- /issm/trunk-jpl/scripts/py_to_pyc.sh	(revision 25762)
+++ /issm/trunk-jpl/scripts/py_to_pyc.sh	(revision 25762)
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+################################################################################
+# Compiles all Python source files in a directory (recursively).
+#
+# Runs quietly if there are no errors. Otherwise, prints errors to console.
+################################################################################
+COMPILE_LOG='./py_to_pyc.log'
+TARGET='.'
+
+if [ "$#" -gt 0 ]; then
+	TARGET=$1
+fi
+
+echo "Compiling Python source files"
+python -m compileall -q ${TARGET} > ${COMPILE_LOG}
+
+if [ -s ${COMPILE_LOG} ]; then
+	echo "Error(s) occured while compiling Python scripts!"
+	echo "--------------- start: ${COMPILE_LOG} ---------------"
+	cat ${COMPILE_LOG}
+	echo "---------------- end: ${COMPILE_LOG} ----------------"
+	exit 1
+fi
Index: /issm/trunk-jpl/src/m/classes/clusters/local.py
===================================================================
--- /issm/trunk-jpl/src/m/classes/clusters/local.py	(revision 25761)
+++ /issm/trunk-jpl/src/m/classes/clusters/local.py	(revision 25762)
@@ -48,5 +48,5 @@
     #}}}
 
-    def checkconsistency: #{{{
+    def checkconsistency(self, md, solution, analyses): #{{{
         if cluster.np < 1:
             md.checkmessage('number of processors should be at least 1')
Index: /issm/trunk-jpl/src/m/classes/spatiallinearbasalforcings.py
===================================================================
--- /issm/trunk-jpl/src/m/classes/spatiallinearbasalforcings.py	(revision 25761)
+++ /issm/trunk-jpl/src/m/classes/spatiallinearbasalforcings.py	(revision 25762)
@@ -16,5 +16,5 @@
 
     def __init__(self, *args): #{{{
-        nargs = len(args):
+        nargs = len(args)
         if nargs == 0:
             self.groundedice_melting_rate   = np.nan
@@ -80,5 +80,5 @@
             raise Exception('not implemented yet!')
             md = checkfield(md, 'fieldname', 'basalforcings.groundedice_melting_rate', 'NaN', 1, 'Inf', 1, 'size', [md.mesh.numberofvertices])
-            md = checkfield(md, x'fieldname', 'basalforcings.deepwater_melting_rate', '>=', 0, 'numel', 1)
+            md = checkfield(md, 'fieldname', 'basalforcings.deepwater_melting_rate', '>=', 0, 'numel', 1)
             md = checkfield(md, 'fieldname', 'basalforcings.deepwater_elevation', '<', 'basalforcings.upperwater_elevation', 'numel', 1)
             md = checkfield(md, 'fieldname', 'basalforcings.upperwater_elevation', '<=', 0, 'numel', 1)
Index: /issm/trunk-jpl/src/m/geometry/AboveGround.py
===================================================================
--- /issm/trunk-jpl/src/m/geometry/AboveGround.py	(revision 25761)
+++ /issm/trunk-jpl/src/m/geometry/AboveGround.py	(revision 25762)
@@ -1,5 +1,5 @@
 import numpy as np
 
-def function AboveGround(lat, long, r, height): #{{{
+def AboveGround(lat, long, r, height): #{{{
     r = r + height
     x = r * np.cos(np.deg2rad(lat)) * np.cos(np.deg2rad(long))
Index: /issm/trunk-jpl/src/m/plot/plot_coastlines.py
===================================================================
--- /issm/trunk-jpl/src/m/plot/plot_coastlines.py	(revision 25761)
+++ /issm/trunk-jpl/src/m/plot/plot_coastlines.py	(revision 25762)
@@ -1,3 +1,3 @@
-import matplotlib.patches.Polygon as mpl.Polygon
+import matplotlib.patches.Polygon as Polygon
 import numpy as np
 
@@ -9914,5 +9914,5 @@
             # Plot
         else:
-            p = mpl.Polygon(np.hstack((x, y)))
+            p = Polygon(np.hstack((x, y)))
             # Plot
             if options.getfieldvalue('coordcent', 'atlantic') == 'pacific':
Index: /issm/trunk-jpl/src/m/solvers/bcgsasmoptions.py
===================================================================
--- /issm/trunk-jpl/src/m/solvers/bcgsasmoptions.py	(revision 25761)
+++ /issm/trunk-jpl/src/m/solvers/bcgsasmoptions.py	(revision 25762)
@@ -3,17 +3,12 @@
 
 
-from pairoptions import pairoptions
-    from collections import OrderedDict
+def bcgsasmoptions(*args):
 
-
-    def bcgsasmoptions(*args):
-
-        #retrieve options provided in *args
-        options = pairoptions(*args)
-        solverOptions = OrderedDict()
-        solverOptions['toolkit'] = 'petsc'
-        solverOptions['mat_type'] = options.getfieldvalue('mat_type', 'mpiaij')
-        solverOptions['ksp_type'] = options.getfieldvalue('ksp_type', 'bcgs')
-        solverOptions['pc_type'] = options.getfieldvalue('pc_type', 'asm')
-        return solverOptions
+    #retrieve options provided in *args
+    options = pairoptions(*args)
+    solverOptions = OrderedDict()
+    solverOptions['toolkit'] = 'petsc'
+    solverOptions['mat_type'] = options.getfieldvalue('mat_type', 'mpiaij')
+    solverOptions['ksp_type'] = options.getfieldvalue('ksp_type', 'bcgs')
+    solverOptions['pc_type'] = options.getfieldvalue('pc_type', 'asm')
     return solverOptions
Index: /issm/trunk-jpl/src/m/solvers/bcgsjacobioptions.py
===================================================================
--- /issm/trunk-jpl/src/m/solvers/bcgsjacobioptions.py	(revision 25761)
+++ /issm/trunk-jpl/src/m/solvers/bcgsjacobioptions.py	(revision 25762)
@@ -3,17 +3,12 @@
 
 
-from pairoptions import pairoptions
-    from collections import OrderedDict
+def bcgsjacobioptions(*args):
 
+    options = pairoptions(*args)
+    solverOptions = OrderedDict()
+    solverOptions['toolkit'] = 'petsc'
+    solverOptions['mat_type'] = options.getfieldvalue('mat_type', 'mpiaij')
+    solverOptions['ksp_type'] = options.getfieldvalue('ksp_type', 'bcgs')
+    solverOptions['pc_type'] = options.getfieldvalue('pc_type', 'jacobi')
 
-    def bcgsjacobioptions(*args):
-
-        options = pairoptions(*args)
-        solverOptions = OrderedDict()
-        solverOptions['toolkit'] = 'petsc'
-        solverOptions['mat_type'] = options.getfieldvalue('mat_type', 'mpiaij')
-        solverOptions['ksp_type'] = options.getfieldvalue('ksp_type', 'bcgs')
-        solverOptions['pc_type'] = options.getfieldvalue('pc_type', 'jacobi')
-
-        return solverOptions
     return solverOptions
Index: /issm/trunk-jpl/src/m/solvers/cgnejacobioptions.py
===================================================================
--- /issm/trunk-jpl/src/m/solvers/cgnejacobioptions.py	(revision 25761)
+++ /issm/trunk-jpl/src/m/solvers/cgnejacobioptions.py	(revision 25762)
@@ -3,18 +3,13 @@
 
 
-from pairoptions import pairoptions
-    from collections import OrderedDict
+def cgnejacobioptions(*args):
 
+    #retrieve options provided in *args
+    options = pairoptions(*args)
+    solverOptions = OrderedDict()
+    solverOptions['toolkit'] = 'petsc'
+    solverOptions['mat_type'] = options.getfieldvalue('mat_type', 'mpiaij')
+    solverOptions['ksp_type'] = options.getfieldvalue('ksp_type', 'cgne')
+    solverOptions['pc_type'] = options.getfieldvalue('pc_type', 'jacobi')
 
-    def cgnejacobioptions(*args):
-
-        #retrieve options provided in *args
-        options = pairoptions(*args)
-        solverOptions = OrderedDict()
-        solverOptions['toolkit'] = 'petsc'
-        solverOptions['mat_type'] = options.getfieldvalue('mat_type', 'mpiaij')
-        solverOptions['ksp_type'] = options.getfieldvalue('ksp_type', 'cgne')
-        solverOptions['pc_type'] = options.getfieldvalue('pc_type', 'jacobi')
-
-        return solverOptions
     return solverOptions
Index: /issm/trunk-jpl/src/m/solvers/conditionnumberoptions.py
===================================================================
--- /issm/trunk-jpl/src/m/solvers/conditionnumberoptions.py	(revision 25761)
+++ /issm/trunk-jpl/src/m/solvers/conditionnumberoptions.py	(revision 25762)
@@ -3,9 +3,10 @@
 
 
-function cn = conditionnumberoptions(*args)
-    #MULTIGRIDOPTIONS - use Multigrid
-    #
-    #   Usage:
-    #      options = mgoptions
+def conditionnumberoptions(*args):
+    """MULTIGRIDOPTIONS - use Multigrid
+
+    Usage:
+        options = mgoptions
+    """
 
     #retrieve options provided in *args
Index: /issm/trunk-jpl/src/m/solvers/mumpsnoneoptions.py
===================================================================
--- /issm/trunk-jpl/src/m/solvers/mumpsnoneoptions.py	(revision 25761)
+++ /issm/trunk-jpl/src/m/solvers/mumpsnoneoptions.py	(revision 25762)
@@ -3,5 +3,5 @@
 
 
-function mumps = mumpsoptions(*args)
+def mumpsoptions(*args):
     #MUMPSOPTIONS - return MUMPS direct solver  petsc options
     #
@@ -16,5 +16,5 @@
     PETSC_MAJOR = IssmConfig('_PETSC_MAJOR_')
     PETSC_MINOR = IssmConfig('_PETSC_MINOR_')
-    if PETSC_MAJOR == 2.,
+    if PETSC_MAJOR == 2:
         mumps.toolkit = 'petsc'
         mumps.mat_type = getfieldvalue(options, 'mat_type', 'aijmumps')
@@ -24,12 +24,12 @@
     end
 
-    if PETSC_MAJOR == 3.,
+    if PETSC_MAJOR == 3:
         mumps.toolkit = 'petsc'
         mumps.mat_type = getfieldvalue(options, 'mat_type', 'mpiaij')
         mumps.ksp_type = getfieldvalue(options, 'ksp_type', 'preonly')
         mumps.pc_type = getfieldvalue(options, 'pc_type', 'lu')
-        if PETSC_MINOR > 8
+        if PETSC_MINOR > 8:
             mumps.pc_factor_mat_solver_type = getfieldvalue(options, 'pc_factor_mat_solver_type', 'mumps')
-        else
+        else:
             mumps.pc_factor_mat_solver_package = getfieldvalue(options, 'pc_factor_mat_solver_package', 'mumps')
         end
Index: /issm/trunk-jpl/test/NightlyRun/GetIds.py
===================================================================
--- /issm/trunk-jpl/test/NightlyRun/GetIds.py	(revision 25761)
+++ /issm/trunk-jpl/test/NightlyRun/GetIds.py	(revision 25762)
@@ -6,23 +6,20 @@
 
 def GetIds(ids_names):
-    """
-     GetIds - output ids from a given array of IDs and test names
+    """GETIDS - Output ids from a given array of IDs and test names
 
-              the test names can be any string or sub - string present
-              in the test's name (first line of corresponding file)
+    The test names can be any string or substring present in the test's name 
+    (first line of corresponding file). Test names are case sensitive.
 
-              test names are case sensitive
-
-         Usage:
-             ids = GetIds(101)
-             ids = GetIds('Dakota')
-             ids = GetIds([101, 102...])
-             ids = GetIds([\'Dakota\', \'Slr\'...])
-             ids = GetIds([[101, 102...], [\'Dakota\', \'Slr\'...]])
+    Usage:
+        ids = GetIds(101)
+        ids = GetIds('Dakota')
+        ids = GetIds([101, 102...])
+        ids = GetIds([\'Dakota\', \'Slr\'...])
+        ids = GetIds([[101, 102...], [\'Dakota\', \'Slr\'...]])
     """
 
     ids = []
 
-    # 1 input, either an id or a test name
+    # Non-list input: either an ID or a test name
     if type(ids_names) == str:
         ids = IdFromString(ids_names)
@@ -32,4 +29,5 @@
         #raise RuntimeError('runme.py: GetIds.py: No tests with names matching "' + ids_names + '" were found. Note that name checking is case sensitive. Test names are in the first line of a given test eg: "Square" would include test101.py: "SquareShelfConstrainedStressSSA2d"')
 
+    # Non-list input: ID
     if type(ids_names) == int:
         ids = [ids_names]
@@ -39,10 +37,10 @@
         #raise RuntimeError('runme.py: GetIds.py: No tests with ids matching "' + ids_names + '" were found. Check that there is a test file named "test' + str(ids_names) + '.py"')
 
-        # many inputs of either ids or test names
+    # many inputs of either ids or test names
     if type(ids_names) == list and len(ids_names) > 0:
         # is everything a string or int?
         if np.array([type(i) == int for i in ids_names]).all():
             ids = ids_names
-        if np.array([type(i) == np.int64 for i in ids_names]).all():
+        elif np.array([type(i) == np.int64 for i in ids_names]).all():
             ids = ids_names
         elif np.array([type(i) == str for i in ids_names]).all():
@@ -51,10 +49,24 @@
                 raise RuntimeError('runme.py: GetIds.py: No tests with names matching "' + ids_names + '" were found. Note that name checking is case sensitive.')
 
-            # many inputs of both ids and test names
-            # ids_names[0] -> ids_names by id
-            # ids_names[1] -> ids_names by test name
+    # many inputs of both ids and test names
+    # ids_names[0] -> ids_names by id
+    # ids_names[1] -> ids_names by test name
+    #
+    # NOTE: ID inclusion/exclusion lists will always hit this condition 
+    #       becasue of the way their respective arguments are gathered at the 
+    #       end of __main__ in the call to function runme.
     if type(ids_names) == list and len(ids_names) == 2:
-        if type(ids_names[0]) == list and len(ids_names[0]) > 0 and type(ids_names[0][0]) == int:
-            ids = np.concatenate([ids, ids_names[0]])
+        if type(ids_names[0]) == list and len(ids_names[0]) > 0:
+            ids_expanded = []
+            for i in ids_names[0]:
+                # Handle case where list element follows MATLAB range syntax
+                if ':' in i:
+                    i_range = i.split(':')
+                    for j in range(int(i_range[0]), int(i_range[1])):
+                        ids_expanded.append(j)
+                else:
+                    ids_expanded.append(int(i))
+            unique_ids = list(set(ids_expanded))
+            ids += unique_ids
         if type(ids_names[1]) == list and len(ids_names[1]) > 0 and type(ids_names[1][0]) == str:
             ids = np.concatenate([ids, np.concatenate([IdFromString(i) for i in ids_names[1]])])
@@ -62,5 +74,5 @@
                 raise RuntimeError('runme.py: GetIds.py: No tests with names matching "' + ids_names + '" were found. Note that name checking is case sensitive.')
 
-            # no recognizable ids or id formats
+    # no recognizable ids or id formats
     if np.size(ids) == 0 and not np.all(np.equal(ids_names, None)):
         raise RuntimeError('runme.py: GetIds.py: include and exclude options (-i/--id; -in/--include_name; -e/--exclude; -en/--exclude_name) options must follow GetIds usage format:\n' + GetIds.__doc__)
Index: /issm/trunk-jpl/test/NightlyRun/runme.py
===================================================================
--- /issm/trunk-jpl/test/NightlyRun/runme.py	(revision 25761)
+++ /issm/trunk-jpl/test/NightlyRun/runme.py	(revision 25762)
@@ -22,14 +22,14 @@
     """RUNME - test deck for ISSM nightly runs
 
-    In a test deck directory (tests/Vertification / NightlyRun for example) the 
-    following command will launch all the existing tests:
-
-        >> ./runme.py
-
-    To run the tests 101 and 102:
-
-        >> ./runme.py -i [101, 102]
-
-    Available options:
+    In a test deck directory (test/NightlyRun for example), the following 
+    command will launch all the existing tests,
+
+        ./runme.py
+
+    To run tests 101 and 102:
+
+        ./runme.py -i [101, 102]
+
+    Options:
         -i/--id             followed by the list of ids or (parts of) test names requested
                             NOTE: runs all tests by default
@@ -144,5 +144,5 @@
     erroredtest_list = []
     for id in test_ids:
-        print(("----------------starting:{}----------------------- ".format(id)))
+        print(("----------------starting:{}-----------------------".format(id)))
         try:
             #Execute test
@@ -218,5 +218,5 @@
                             fid = open(os.path.join(ISSM_DIR, 'nightlylog', 'pythonerror.log'), 'a')
                             fid.write('%s' % message)
-                            fid.write('\n------------------------------------------------------------------    \n')
+                            fid.write('\n------------------------------------------------------------------\n')
                             fid.close()
                             print(('FAILURE difference: N/A test id: {} test name: {} field: {}'.format(id, id_string, fieldname)))
@@ -231,5 +231,5 @@
                 fid = open(os.path.join(ISSM_DIR, 'nightlylog', 'pythonerror.log'), 'a')
                 fid.write('%s' % message)
-                fid.write('\n------------------------------------------------------------------ \n')
+                fid.write('\n------------------------------------------------------------------\n')
                 fid.close()
                 print(('FAILURE difference: N/A test id: {} test name: {} field: {}'.format(id, id_string, 'N/A')))
@@ -238,5 +238,5 @@
                 raise RuntimeError(message)
 
-        print(("----------------    finished:{}----------------------- ".format(id)))
+        print(("----------------finished:{}-----------------------".format(id)))
 
     if errorcount > 0:
@@ -244,9 +244,7 @@
     return
 
-
 if __name__ == '__main__':
     if 'PYTHONSTARTUP' in os.environ:
         PYTHONSTARTUP = os.environ['PYTHONSTARTUP']
-#print 'PYTHONSTARTUP = ', PYTHONSTARTUP
         if os.path.exists(PYTHONSTARTUP):
             try:
@@ -258,7 +256,7 @@
 
         parser = argparse.ArgumentParser(description='RUNME - test deck for ISSM nightly runs')
-        parser.add_argument('-i', '--id', nargs='*', type=int, help='followed by the list of ids requested', default=[])
+        parser.add_argument('-i', '--id', nargs='*', type=str, help='followed by the list of ids requested', default=[])
         parser.add_argument('-in', '--include_name', nargs='*', type=str, help='followed by the list of test names requested', default=[])
-        parser.add_argument('-e', '--exclude', nargs='+', type=int, help='ids to be excluded from the test', default=[])
+        parser.add_argument('-e', '--exclude', nargs='+', type=str, help='ids to be excluded from the test', default=[])
         parser.add_argument('-en', '--exclude_name', nargs='+', type=str, help='test names to be excluded from the test', default=[])
         parser.add_argument('-b', '--benchmark', help='nightly/ismip/eismint/thermal/mesh/...', default='nightly')
