Index: issm/trunk-jpl/src/m/model/MatlabProcessPatch.m
===================================================================
--- issm/trunk-jpl/src/m/model/MatlabProcessPatch.m	(revision 12732)
+++ issm/trunk-jpl/src/m/model/MatlabProcessPatch.m	(revision 12733)
@@ -5,5 +5,5 @@
 %      Result=ProcessPatch(Result);
 
-%return if there is no fiel Patch
+%return if there is no field Patch
 if (~isfield(structure,'Patch')),
 	return;
Index: issm/trunk-jpl/src/m/model/MatlabProcessPatch.py
===================================================================
--- issm/trunk-jpl/src/m/model/MatlabProcessPatch.py	(revision 12733)
+++ issm/trunk-jpl/src/m/model/MatlabProcessPatch.py	(revision 12733)
@@ -0,0 +1,20 @@
+"""
+PROCESSPATCH - create a structure from a patch
+ 
+    Usage:
+       Result=ProcessPatch(Result);
+"""
+
+def MatlabProcessPatch(structure):
+
+	#loop over steps
+	for structurei in structure.itervalues():
+
+		#return if there is no field Patch
+		if not 'Patch' in structurei:
+			continue
+
+		raise SystemError("MatlabProcessPatch not implemented in Python.")
+
+	return structure
+
Index: issm/trunk-jpl/src/m/model/loadresultsfromcluster.py
===================================================================
--- issm/trunk-jpl/src/m/model/loadresultsfromcluster.py	(revision 12733)
+++ issm/trunk-jpl/src/m/model/loadresultsfromcluster.py	(revision 12733)
@@ -0,0 +1,62 @@
+"""
+LOADRESULTSFROMCLUSTER - load results of solution sequence from cluster
+ 
+    Usage:
+       md=loadresultsfromcluster(md,runtimename);
+"""
+
+import os
+import platform
+import socket
+from MatlabFuncs import *
+
+def loadresultsfromcluster(md,runtimename=False):
+
+	#retrieve cluster, to be able to call its methods
+	cluster=md.cluster
+
+	if runtimename:
+		md.private.runtimename=runtimename
+	end
+
+	#Download outputs from the cluster
+	filelist=[md.miscellaneous.name+'.outlog',md.miscellaneous.name+'.errlog']
+	if md.qmu.isdakota:
+		filelist.append(md.miscellaneous.name+'.qmu.err')
+		filelist.append(md.miscellaneous.name+'.qmu.out')
+		if 'tabular_graphics_data' in md.qmu.params:
+			if md.qmu.params['tabular_graphics_data']:
+				filelist.append('dakota_tabular.dat')
+		filelist.append(md.miscellaneous.name+'.outbin')
+	Download(cluster,md.private.runtimename,filelist)
+
+	#If we are here, no errors in the solution sequence, call loadresultsfromdisk.
+	md=loadresultsfromdisk(md,md.miscellaneous.name+'.outbin')
+
+	#erase the log and output files
+	if md.qmu.isdakota:
+		os.remove(os.path.join('qmu'+str(os.getpid()),md.miscellaneous.name+'.outlog'))
+		os.remove(os.path.join('qmu'+str(os.getpid()),md.miscellaneous.name+'.errlog'))
+	else:
+		os.remove(md.miscellaneous.name+'.outlog')
+		os.remove(md.miscellaneous.name+'.errlog')
+		os.remove(md.miscellaneous.name+'.outbin')
+		if not 'Windows' in platform.system():
+			os.remove(md.private.runtimename+'.tar.gz')
+
+	#erase input file if run was carried out on same platform.
+	hostname=socket.gethostname().lower().split('.')[0]
+	if strcmpi(hostname,cluster.name):
+		if md.qmu.isdakota:
+			os.remove(os.path.join('qmu'+str(os.getpid()),md.miscellaneous.name+'.bin'))
+			os.remove(os.path.join('qmu'+str(os.getpid()),md.miscellaneous.name+'.queue'))
+		else:
+			os.remove(md.miscellaneous.name+'.bin')
+			os.remove(md.miscellaneous.name+'.petsc')
+			if not 'Windows' in platform.system():
+				os.remove(md.miscellaneous.name+'.queue')
+			else:
+				os.remove(md.miscellaneous.name+'.bat')
+
+	return md
+
Index: issm/trunk-jpl/src/m/model/loadresultsfromdisk.py
===================================================================
--- issm/trunk-jpl/src/m/model/loadresultsfromdisk.py	(revision 12733)
+++ issm/trunk-jpl/src/m/model/loadresultsfromdisk.py	(revision 12733)
@@ -0,0 +1,61 @@
+"""
+LOADRESULTSFROMDISK - load results of solution sequence from disk file "filename"            
+ 
+    Usage:
+       md=loadresultsfromdisk(md=False,filename=False);
+"""
+
+import os
+
+def loadresultsfromdisk(md,filename):
+
+	#check number of inputs/outputs
+	if not md or not filename:
+		raise ValueError("loadresultsfromdisk: error message.")
+
+	if not md.qmu.isdakota:
+
+		#Check that file exists
+		if not os.path.exists(filename):
+			raise ValueError("binary file '%s' not found." % filename)
+
+		#initialize md.results if not a structure yet
+		if not isinstance(md.results,dict):
+			md.results={}
+
+		#load results onto model
+		structure=parseresultsfromdisk(filename,~md.settings.io_gather)
+		if not len(structure):
+			raise RuntimeError("No result found in binary file '%s'. Check for solution crash." % filename)
+		end
+		md.results[structure[1]['SolutionType']]=structure;
+
+		#recover solution_type from results
+		md.private.solution=structure[1]['SolutionType']
+
+		#read log files onto fields
+		if os.path.exists(md.miscellaneous.name+'.errlog'):
+			with open(md.miscellaneous.name+'.errlog','r') as f:
+				md.results[structure[1]['SolutionType']]['errlog']=[line[:-1] for line in f]
+		else:
+			md.results[structure[1]['SolutionType']]['errlog']=[]
+
+		if os.path.exists(md.miscellaneous.name+'.outlog'):
+			with open(md.miscellaneous.name+'.outlog','r') as f:
+				md.results[structure[1]['SolutionType']]['outlog']=[line[:-1] for line in f]
+		else:
+			md.results[structure[1]['SolutionType']]['outlog']=[]
+
+		if len(md.results[structure[1]['SolutionType']]['errlog']):
+			print ("loadresultsfromcluster info message: error during solution. Check your errlog and outlog model fields.")
+
+	#post processes qmu results if necessary
+	else:
+
+		if not isinstance(md.private.solution,str):
+			md.private.solution=EnumToString(md.private.solution)
+		md=postqmu(md)
+		os.chdir('..')
+
+	return md
+
Index: issm/trunk-jpl/src/m/model/marshall.py
===================================================================
--- issm/trunk-jpl/src/m/model/marshall.py	(revision 12732)
+++ issm/trunk-jpl/src/m/model/marshall.py	(revision 12733)
@@ -16,8 +16,7 @@
 	#open file for binary writing
 	try:
-		fid=open(md.miscellaneous.name+'.bin','wb');
+		fid=open(md.miscellaneous.name+'.bin','wb')
 	except IOError as e:
-		print "marshall error message: could not open '%s.bin' file for binary writing." % md.miscellaneous.name
-		raise IOError(e)
+		raise IOError("marshall error message: could not open '%s.bin' file for binary writing." % md.miscellaneous.name)
 
 	#First, write MaximumNumberOfEnum to make sure that the Enums are synchronized
@@ -45,5 +44,4 @@
 		f.close(fid)
 	except IOError as e:
-		print "marshall error message: could not close file '%s.bin'." % md.miscellaneous.name
-		raise IOError(e)
+		raise IOError("marshall error message: could not close file '%s.bin'." % md.miscellaneous.name)
 
Index: issm/trunk-jpl/src/m/model/parseresultsfromdisk.m
===================================================================
--- issm/trunk-jpl/src/m/model/parseresultsfromdisk.m	(revision 12732)
+++ issm/trunk-jpl/src/m/model/parseresultsfromdisk.m	(revision 12733)
@@ -172,5 +172,5 @@
 % }}}
 function result=ReadDataDimensions(fid) % {{{
-%READDATA - read data dimensions, step and time, but not the data itself.
+%READDATADIMENSIONS - read data dimensions, step and time, but not the data itself.
 %
 %   Usage:
Index: issm/trunk-jpl/src/m/model/parseresultsfromdisk.py
===================================================================
--- issm/trunk-jpl/src/m/model/parseresultsfromdisk.py	(revision 12733)
+++ issm/trunk-jpl/src/m/model/parseresultsfromdisk.py	(revision 12733)
@@ -0,0 +1,235 @@
+import struct
+import numpy
+from MatlabFuncs import *
+from MatlabProcessPatch import *
+
+def parseresultsfromdisk(filename,iosplit):
+	"""
+	PARSERESULTSFROMDISK - ...
+	 
+	    Usage:
+	       results=parseresultsfromdisk(filename,iosplit)
+	"""
+
+	if iosplit:
+		results=parseresultsfromdiskiosplit(filename)
+	else:
+		results=parseresultsfromdiskioserial(filename)
+
+	#process patch if necessary
+	results=MatlabProcessPatch(results)
+
+	return results
+
+def parseresultsfromdiskioserial(filename):    # {{{
+	"""
+	PARSERESULTSFROMDISK - ...
+	 
+	    Usage:
+	       results=parseresultsfromdiskioserial(filename)
+	"""
+
+	#Open file
+	try:
+		fid=open(filename,'rb')
+	except IOError as e:
+		raise IOError("loadresultsfromdisk error message: could not open '%s' for binary reading." % filename)
+
+	results={}
+
+	#Read fields until the end of the file.
+	result=ReadData(fid)
+	while result:
+		#Get time and step
+		if not result['step'] in results:
+			results[result['step']]={}
+			results[result['step']]['step']=result['step']
+			results[result['step']]['time']=result['time'] 
+	
+		#Add result
+		if result['step'] in results and \
+		   result['fieldname'] in results[result['step']] and \
+		   not strcmp(result['fieldname'],'SolutionType'):
+			results[result['step']][result['fieldname']]=numpy.concatenate((results[result['step']][result['fieldname']],result['field']),axis=0)
+		else:
+			results[result['step']][result['fieldname']]=result['field']
+
+		#read next result
+		result=ReadData(fid)
+
+	fid.close()
+
+	return results
+	# }}}
+
+def parseresultsfromdiskiosplit(filename):    # {{{
+	"""
+	PARSERESULTSFROMDISKIOSPLIT - ...
+	 
+	    Usage:
+	       results=parseresultsfromdiskiosplit(filename)
+	"""
+
+	#Open file
+	try:
+		fid=open(filename,'rb')
+	except IOError as e:
+		raise IOError("loadresultsfromdisk error message: could not open '%s' for binary reading." % filename)
+
+	results={}
+
+	#if we have done split I/O, ie, we have results that are fragmented across patches, 
+	#do a first pass, and figure out the structure of results
+	result=ReadDataDimensions(fid)
+	while result:
+
+		#Get time and step
+		if not result['step'] in results:
+			results[result['step']]={}
+			results[result['step']]['step']=result['step']
+			results[result['step']]['time']=result['time'] 
+
+		#Add result
+		if strcmpi(result['fieldname'],'Patch'):
+			results[result['step']][result['fieldname']]=[0,result['N']]
+		else:
+			results[result['step']][result['fieldname']]=float('NaN')
+
+		#read next result
+		result=ReadDataDimensions(fid)
+
+	#do a second pass, and figure out the size of the patches
+	fid.seek(0)    #rewind
+	result=ReadDataDimensions(fid)
+	while result:
+
+		#Add result
+		if strcmpi(result['fieldname'],'Patch'):
+			patchdimensions=results[result['step']][result['fieldname']]
+			results[result['step']][result['fieldname']]=[patchdimensions[0]+result['M'],result['N']]
+
+		#read next result
+		result=ReadDataDimensions(fid)
+
+	#allocate patches
+	for result in results.itervalues():
+		if 'Patch' in result:
+			result['Patch']=numpy.zeros(shape=(result['Patch'][0],result['Patch'][1]),dtype=float)
+			result['counter']=0    #use to index into the patch
+
+	#third pass, this time to read the real information
+	fid.seek(0)    #rewind
+	result=ReadData(fid)
+	while result:
+
+		#Get time and step
+		if not result['step'] in results:
+			results[result['step']]={}
+			results[result['step']]['step']=result['step']
+			results[result['step']]['time']=result['time'] 
+
+		#Add result
+		if strcmpi(result['fieldname'],'Patch'):
+			counter=results[result['step']]['counter']
+			counter2=counter+result['field'].shape[0]-1
+			results[result['step']][result['fieldname']][counter:counter2,:]=result['field']
+
+			#increment counter: 
+			results[result['step']]['counter']=counter2+1
+		else:
+			results[result['step']][result['fieldname']]=result['field']
+
+		#read next result
+		result=ReadData(fid)
+
+	#close file
+	fid.close()
+
+	return results
+	# }}}
+
+def ReadData(fid):    # {{{
+	"""
+	READDATA - ...
+	 
+	    Usage:
+	       field=ReadData(fid)
+	"""
+
+	#read field
+	try:
+		length=struct.unpack('i',fid.read(struct.calcsize('i')))[0]
+
+		fieldname=struct.unpack('%ds' % length,fid.read(length))[0][:-1]
+		time=struct.unpack('d',fid.read(struct.calcsize('d')))[0]
+		step=struct.unpack('i',fid.read(struct.calcsize('i')))[0]
+
+		type=struct.unpack('i',fid.read(struct.calcsize('i')))[0]
+		M=struct.unpack('i',fid.read(struct.calcsize('i')))[0]
+		if   type==1:
+			field=numpy.array(struct.unpack('%dd' % M,fid.read(M*struct.calcsize('d'))),dtype=float)
+		elif type==2:
+			field=struct.unpack('%ds' % M,fid.read(M))[0][:-1]
+		elif type==3:
+			N=struct.unpack('i',fid.read(struct.calcsize('i')))[0]
+#			field=transpose(fread(fid,[N M],'double'));
+			field=numpy.zeros(shape=(M,N),dtype=float)
+			for i in xrange(M):
+				field[i,:]=struct.unpack('%dd' % N,fid.read(N*struct.calcsize('d')))
+		else:
+			raise TypeError("cannot read data of type %d" % type)
+
+		result={}
+		result['fieldname']=fieldname
+		result['time']=time
+		result['step']=step
+		result['field']=field
+
+	except struct.error as e:
+		result={}
+
+	return result
+	# }}}
+
+def ReadDataDimensions(fid):    # {{{
+	"""
+	READDATADIMENSIONS - read data dimensions, step and time, but not the data itself.
+	 
+	    Usage:
+	       field=ReadDataDimensions(fid)
+	"""
+
+	#read field
+	try:
+		length=struct.unpack('i',fid.read(struct.calcsize('i')))[0]
+
+		fieldname=struct.unpack('%ds' % length,fid.read(length))[0][:-1]
+		time=struct.unpack('d',fid.read(struct.calcsize('d')))[0]
+		step=struct.unpack('i',fid.read(struct.calcsize('i')))[0]
+
+		type=struct.unpack('i',fid.read(struct.calcsize('i')))[0]
+		M=struct.unpack('i',fid.read(struct.calcsize('i')))[0]
+		N=1    #default
+		if   type==1:
+			fid.seek(M*8,1)
+		elif type==2:
+			fid.seek(M,1)
+		elif type==3:
+			N=struct.unpack('i',fid.read(struct.calcsize('i')))[0]
+			fid.seek(N*M*8,1)
+		else:
+			raise TypeError("cannot read data of type %d" % type)
+
+		result={}
+		result['fieldname']=fieldname
+		result['time']=time
+		result['step']=step
+		result['M']=M
+		result['N']=N
+
+	except struct.error as e:
+		result={}
+
+	return result
+	# }}}
+
Index: issm/trunk-jpl/src/m/utils/OS/ispetsc.py
===================================================================
--- issm/trunk-jpl/src/m/utils/OS/ispetsc.py	(revision 12733)
+++ issm/trunk-jpl/src/m/utils/OS/ispetsc.py	(revision 12733)
@@ -0,0 +1,38 @@
+"""
+%ISPETSC - figure out if PETSC package was compiled with ISSM
+%
+%   Usage:
+%       flag=ispetsc();
+"""
+
+import os
+from issmdir import *
+from MatlabFuncs import *
+
+def ispetsc():
+
+	configfile=os.path.join(issmdir(),'bin','config.h')    #should find it in the install target
+	if not os.path.exists(configfile):
+		raise RuntimeError("File '%s' not found. ISSM has not been configured yet!" % configfile)
+
+	#go through the file, and recover the line we want
+	flag=2
+	try:
+		fid=open(configfile,'r')
+	except IOError as e:
+		print "could not open file: '%s'" % configfile
+		raise IOError(e)
+
+	for tline in fid:
+		if strncmp(tline,'/* #undef _HAVE_PETSC_ */',25):
+			flag=0
+			break
+		if  strncmp(tline,'#define _HAVE_PETSC_',20):
+			flag=1
+			break
+
+	fid.close()
+	if flag==2:
+		raise RuntimeError("could not determine whether PETSC was or was not compiled.")
+
+	return flag
Index: issm/trunk-jpl/src/m/utils/Shell/issmdir.py
===================================================================
--- issm/trunk-jpl/src/m/utils/Shell/issmdir.py	(revision 12733)
+++ issm/trunk-jpl/src/m/utils/Shell/issmdir.py	(revision 12733)
@@ -0,0 +1,25 @@
+"""
+ISSMDIR - Get ISSM_DIR environment variable
+ 
+    Usage:
+       ISSM_DIR=issmdir()
+"""
+
+import platform
+import os
+from MatlabFuncs import *
+
+def issmdir():
+
+	if not 'Windows' in platform.system():
+		ISSM_DIR =os.environ['ISSM_DIR']
+	else:
+		ISSM_DIR =os.environ['ISSM_DIR_WIN']
+		if strcmpi(ISSM_DIR[-1],'/') or strcmpi(ISSM_DIR[-1],'\\'):
+			ISSM_DIR = ISSM_DIR[:-1]    #shave off the last '/'
+
+	if not ISSM_DIR:
+		raise RuntimeError("issmdir error message: 'ISSM_DIR' environment variable is empty! You should define ISSM_DIR in your .cshrc or .bashrc!")
+
+	return ISSM_DIR
+
