Index: /issm/trunk-jpl/src/m/classes/pairoptions.py
===================================================================
--- /issm/trunk-jpl/src/m/classes/pairoptions.py	(revision 12659)
+++ /issm/trunk-jpl/src/m/classes/pairoptions.py	(revision 12660)
@@ -1,38 +1,216 @@
-class pairoptions:
-	#properties
-	def __init__(self,*args):
-		# {{{ Properties
-		if len(args)%2==1:
-			raise RuntimeError('pairoption error message: an even number of options is required')
-
-		#create a pairoption object
-		if len(args)==0:
-			self.list=[]
-		else:
-			self.list=[]
-			for i in range(int(round(len(args)/2))):
-				if isinstance(args[2*i],str):
-					self.list.append([args[2*i],args[2*i+1]])
+"""
+PAIROPTIONS class definition
+ 
+    Usage:
+       pairoptions=pairoptions();
+       pairoptions=pairoptions('module',true,'solver',false);
+"""
+
+from WriteData import *
+
+class pairoptions(object):
+	def __init__(self,*arg):
+		self.functionname = ''
+		self.list         = {}
+
+#		%get calling function name
+#		a=dbstack;
+#		if length(a)>1,
+#			self.functionname=a(2).file(1:end-2);
+#		else
+#			self.functionname='';
+#		end
+
+		#initialize list
+		if not len(arg):
+			pass    #Do nothing,
+		else:
+			self.buildlist(*arg)
+	# }}}
+
+	def buildlist(self,*arg):    # {{{
+		"""BUILDLIST - build list of objects from input"""
+
+		#check length of input
+		if len(arg) % 2:
+			raise TypeError('error: an even number of options is required') 
+		numoptions = len(arg)/2
+
+		#go through arg and build list of objects
+		for i in xrange(numoptions):
+			if isinstance(arg[2*i],str):
+				self.list[arg[2*i]] = arg[2*i+1];
+			else:
+				#option is not a string, ignore it
+				print "WARNING: option number %d '%s' is not a string and will be ignored." % (i+1,type(arg[2*i]))
+	# }}}
+
+	def addfield(self,field,value):    # {{{
+		"""ADDFIELD - add a field to an options list"""
+		if isinstance(field,str):
+			if field in self.list:
+				print "WARNING: field '%s' with value=%s exists and will be overwritten with value=%s." % (field,str(self.list[field]),str(value))
+			self.list[field] = value
+	# }}}
+
+	def addfielddefault(self,field,value):    # {{{
+		"""ADDFIELDDEFAULT - add a field to an options list if it does not exist"""
+		if isinstance(field,str):
+			if not field in self.list:
+				self.list[field] = value
+	# }}}
+
+	def AssignObjectFields(self,obj2):    # {{{
+		"""ASSIGNOBJECTFIELDS - assign object fields from options"""
+		for item in self.list.iteritems():
+			if item[0] in dir(obj2):
+				setattr(obj2,item[0],item[1])
+			else:
+				print "WARNING: field '%s' is not a property of '%s'." % (item[0],type(obj2))
+		return obj2
+	# }}}
+
+	def changefieldvalue(self,field,newvalue):    # {{{
+		"""CHANGEOPTIONVALUE - change the value of an option in an option list"""
+
+		self.list[field]=newvalue;
+	# }}}
+
+#	function obj = deleteduplicates(obj,warn) % {{{
+#	%DELETEDUPLICATES - delete duplicates in an option list
+#
+#		%track the first occurance of each option
+#		[dummy lines]=unique(obj.list(:,1),'first');
+#		clear dummy
+#
+#		%warn user if requested
+#		if warn,
+#			numoptions=size(obj.list,1);
+#			for i=1:numoptions,
+#				if ~ismember(i,lines),
+#					disp(['WARNING: option ' obj.list{i,1} ' appeared more than once. Only its first occurence will be kept'])
+#				end
+#			end
+#		end
+#
+#		%remove duplicates from the options list
+#		obj.list=obj.list(lines,:);
+#	end % }}}
+
+	def __repr__(self):    # {{{
+		s="   functionname: %s\n" % self.functionname
+		if self.list:
+			s+="   list: (%ix%i)\n\n" % (len(self.list),2)
+			for item in self.list.iteritems():
+				if   isinstance(item[1],str):
+					s+="     field: %-10s value: '%s'\n" % (item[0],item[1])
+				elif isinstance(item[1],(bool,int,long,float)):
+					s+="     field: %-10s value: %g\n" % (item[0],item[1])
 				else:
-					#option is not a string, ignore it
-					print("%s%i%s"%('buildlist info: option number ',i,' is not a string, it will be ignored'))
-					continue
-
-		#}}}
-	def __repr__(obj):
-		# {{{ Display
-		if not obj.list:
-			string='   list: empty'
-		else:
-			string="   list: (%i)"%(len(obj.list))
-			for i in range(len(obj.list)):
-				if isinstance(obj.list[i][1],str):
-					string2="     field: %-10s value: '%s'"%(obj.list[i][0],obj.list[i][1])
-				elif isinstance(obj.list[i][1],float):
-					string2="     field: %-10s value: %g"%(obj.list[i][0],obj.list[i][1])
-				elif isinstance(obj.list[i][1],int):
-					string2="     field: %-10s value: %i"%(obj.list[i][0],obj.list[i][1])
-				else:
-					string2="     field: %-10s value: (%i)"%(len(obj.list[i][1]))
-				string="%s\n%s"%(string,string2)
-		return string
+					s+="     field: %-10s value: %s\n" % (item[0],type(item[1]))
+		else:
+			s+="   list: empty\n"
+		return s
+	# }}}
+
+	def exist(self,field):    # {{{
+		"""EXIST - check if the option exist"""
+
+		#some argument checking: 
+		if field == None or field == '':
+			raise ValueError('exist error message: bad usage');
+		if not isinstance(field,str):
+			raise TypeError("exist error message: field '%s' should be a string." % str(field));
+
+		#Recover option
+		if field in self.list:
+			return True
+		else:
+			return False
+	# }}}
+
+#	function num = fieldoccurences(obj,field), % {{{
+#	%FIELDOCCURENCES - get number of occurence of a field
+#
+#		%check input 
+#		if ~ischar(field),
+#			error('fieldoccurences error message: field should be a string');
+#		end
+#
+#		%get number of occurence
+#		num=sum(strcmpi(field,obj.list(:,1)));
+#	end % }}}
+
+	def getfieldvalue(self,field,default=None):    # {{{
+		"""
+		GETOPTION - get the value of an option
+	
+		Usage:
+		   value=options.getfieldvalue(field,default)
+	 
+		Find an option value from a field. A default option
+		can be given in input if the field does not exist
+	 
+		Examples:
+		   value=options.getfieldvalue(options,'caxis')
+		   value=options.getfieldvalue(options,'caxis',[0 2])
+		"""
+
+		#some argument checking: 
+		if field == None or field == '':
+			raise ValueError('getfieldvalue error message: bad usage');
+		if not isinstance(field,str):
+			raise TypeError("getfieldvalue error message: field '%s' should be a string." % str(field));
+
+		#Recover option
+		if field in self.list:
+			value=self.list[field]
+		else:
+			if not default == None:
+				value=default
+			else:
+				raise KeyError("error message: field '%s' has not been provided by user (and no default value has been specified)." % field)
+
+		return value
+	# }}}
+
+	def removefield(self,field,warn):    # {{{
+		"""
+		REMOVEFIELD - delete a field in an option list
+	 
+		Usage:
+		   obj=removefield(self,field,warn)
+	 
+		if warn==1 display an info message to warn user that
+		some of his options have been removed.
+		"""
+
+		#check if field exist
+		if field in self.list:
+
+			#remove duplicates from the options list
+			del self.list[field]
+
+			#warn user if requested
+			if warn:
+				print "removefield info: option '%s' has been removed from the list of options." % field
+	# }}}
+
+	def marshall(self,fid,firstindex):    # {{{
+
+		for i,item in enumerate(self.list.iteritems()):
+			name  = item[0]
+			value = item[1]
+
+			#Write option name
+			WriteData(fid,'enum',(firstindex-1)+2*i+1,'data',name,'format','String')
+
+			#Write option value
+			if   isinstance(value,str):
+				WriteData(fid,'enum',(firstindex-1)+2*i+2,'data',value,'format','String')
+			elif isinstance(value,(bool,int,long,float)):
+				WriteData(fid,'enum',(firstindex-1)+2*i+2,'data',value,'format','Double')
+			else:
+				raise TypeError("Cannot marshall option '%s': format not supported yet." % name)
+	# }}}
+
Index: /issm/trunk-jpl/src/m/model/WriteData.m
===================================================================
--- /issm/trunk-jpl/src/m/model/WriteData.m	(revision 12659)
+++ /issm/trunk-jpl/src/m/model/WriteData.m	(revision 12660)
@@ -35,5 +35,5 @@
 %Step 2: write the data itself.
 if     strcmpi(format,'Boolean'),% {{{
-	if(numel(data)~=1), error(['field ' field ' cannot be marshalled as it has more than one element!']); end
+	if(numel(data)~=1), error(['field ' EnumToString(enum) ' cannot be marshalled as it has more than one element!']); end
 
 	%first write length of record
@@ -59,5 +59,5 @@
 	% }}}
 elseif strcmpi(format,'Double'), % {{{
-	if(numel(data)~=1), error(['field ' field ' cannot be marshalled as it has more than one element!']); end
+	if(numel(data)~=1), error(['field ' EnumToString(enum) ' cannot be marshalled as it has more than one element!']); end
 
 	%first write length of record
Index: /issm/trunk-jpl/src/m/model/WriteData.py
===================================================================
--- /issm/trunk-jpl/src/m/model/WriteData.py	(revision 12660)
+++ /issm/trunk-jpl/src/m/model/WriteData.py	(revision 12660)
@@ -0,0 +1,280 @@
+def WriteData(fid,*args):
+	"""
+	WRITEDATA - write model field in binary file
+ 
+    Usage:
+       WriteData(fid,*args)
+	"""
+
+	import numpy
+	import math
+	import struct
+
+	#process options
+	options=pairoptions(args)
+
+	#Get data properties
+	if options.exist('object'):
+		#This is an object field, construct enum and data
+		obj       = options.getfieldvalue('object')
+		fieldname = options.getfieldvalue('fieldname')
+		classname = type(obj)
+
+		enum      = BuildEnum(classname+'_'+fieldname)
+		data      = getattr(obj,fieldname)
+	else:
+		#No processing required
+		data = options.getfieldvalue('data')
+		enum = options.getfieldvalue('enum')
+	format  = options.getfieldvalue('format')
+	mattype = options.getfieldvalue('mattype',0)    #only required for matrices
+
+	#Process sparse matrices
+#	if issparse(data),
+#		data=full(data);
+#	end
+
+	#Step 1: write the enum to identify this record uniquely
+	fid.write(struct.pack('i',enum)) 
+
+	#Step 2: write the data itself.
+	if   strcmpi(format,'Boolean'):    # {{{
+		if len(data) !=1:
+			raise ValueError('field %s cannot be marshalled as it has more than one element!' % EnumToString(enum))
+
+		#first write length of record
+		fid.write(struct.pack('i',4+4))  #1 bool (disguised as an int)+code
+
+		#write data code: 
+		fid.write(struct.pack('i',FormatToCode(format))) 
+
+		#now write integer
+		fid.write(struct.pack('i',data))  #send an int, not easy to send a bool
+		# }}}
+
+	elif strcmpi(format,'Integer'):    # {{{
+		if len(data) !=1:
+			raise ValueError('field %s cannot be marshalled as it has more than one element!' % EnumToString(enum))
+
+		#first write length of record
+		fid.write(struct.pack('i',4+4))  #1 integer + code
+
+		#write data code: 
+		fid.write(struct.pack('i',FormatToCode(format))) 
+
+		#now write integer
+		fid.write(struct.pack('i',data)) 
+		# }}}
+
+	elif strcmpi(format,'Double'):    # {{{
+		if len(data) !=1:
+			raise ValueError('field %s cannot be marshalled as it has more than one element!' % EnumToString(enum))
+
+		#first write length of record
+		fid.write(struct.pack('i',8+4))  #1 double+code
+
+		#write data code: 
+		fid.write(struct.pack('i',FormatToCode(format))) 
+
+		#now write double
+		fid.write(struct.pack('d',data)) 
+		# }}}
+
+	elif strcmpi(format,'String'):    # {{{
+		#first write length of record
+		fid.write(struct.pack('i',len(data)+4+4))  #string + string size + code
+
+		#write data code: 
+		fid.write(struct.pack('i',FormatToCode(format))) 
+
+		#now write string
+		fid.write(struct.pack('i',len(data))) 
+		fid.write(struct.pack('%ds' % len(data),data)) 
+		# }}}
+
+	elif strcmpi(format,'BooleanMat'):    # {{{
+
+		#Get size
+		s=data.shape
+		#if matrix = NaN, then do not write anything
+		if s[0]==1 and s[1]==1 and math.isnan(data[0][0]):
+			s[0]=0
+			s[1]=0
+
+		#first write length of record
+		fid.write(struct.pack('i',4+4+8*s[0]*s[1]+4+4))    #2 integers (32 bits) + the double matrix + code + matrix type
+
+		#write data code and matrix type: 
+		fid.write(struct.pack('i',FormatToCode(format))) 
+		fid.write(struct.pack('i',mattype))
+
+		#now write matrix
+		fid.write(struct.pack('i',s[0])) 
+		fid.write(struct.pack('i',s[1])) 
+		for i in xrange(s[0]):
+			for j in xrange(s[1]):
+				fid.write(struct.pack('i',data[i][j]))    #get to the "c" convention, hence the transpose
+		# }}}
+
+	elif strcmpi(format,'IntMat'):    # {{{
+
+		#Get size
+		s=data.shape
+		#if matrix = NaN, then do not write anything
+		if s[0]==1 and s[1]==1 and math.isnan(data[0][0]):
+			s[0]=0
+			s[1]=0
+
+		#first write length of record
+		fid.write(struct.pack('i',4+4+8*s[0]*s[1]+4+4))    #2 integers (32 bits) + the double matrix + code + matrix type
+
+		#write data code and matrix type: 
+		fid.write(struct.pack('i',FormatToCode(format))) 
+		fid.write(struct.pack('i',mattype))
+
+		#now write matrix
+		fid.write(struct.pack('i',s[0])) 
+		fid.write(struct.pack('i',s[1])) 
+		for i in xrange(s[0]):
+			for j in xrange(s[1]):
+				fid.write(struct.pack('i',data[i][j]))    #get to the "c" convention, hence the transpose
+		# }}}
+
+	elif strcmpi(format,'DoubleMat'):    # {{{
+
+		#Get size
+		s=data.shape
+		#if matrix = NaN, then do not write anything
+		if s[0]==1 and s[1]==1 and math.isnan(data[0][0]):
+			s[0]=0
+			s[1]=0
+
+		#first write length of record
+		fid.write(struct.pack('i',4+4+8*s[0]*s[1]+4+4))    #2 integers (32 bits) + the double matrix + code + matrix type
+
+		#write data code and matrix type: 
+		fid.write(struct.pack('i',FormatToCode(format))) 
+		fid.write(struct.pack('i',mattype))
+
+		#now write matrix
+		fid.write(struct.pack('i',s[0])) 
+		fid.write(struct.pack('i',s[1])) 
+		for i in xrange(s[0]):
+			for j in xrange(s[1]):
+				fid.write(struct.pack('d',data[i][j]))    #get to the "c" convention, hence the transpose
+		# }}}
+
+	elif strcmpi(format,'MatArray'):    # {{{
+
+		#first get length of record
+		recordlength=4+4    #number of records + code
+		for matrix in data:
+			s=matrix.shape
+			recordlength+=4*2+s[0]*s[1]*8    #row and col of matrix + matrix of doubles
+
+		#write length of record
+		fid.write(struct.pack('i',recordlength)) 
+
+		#write data code: 
+		fid.write(struct.pack('i',FormatToCode(format))) 
+
+		#write data, first number of records
+		fid.write(struct.pack('i',len(data))) 
+
+		#write each matrix: 
+		for matrix in data:
+			s=matrix.shape
+			fid.write(struct.pack('i',s[0])) 
+			fid.write(struct.pack('i',s[1])) 
+			for i in xrange(s[0]):
+				for j in xrange(s[1]):
+					fid.write(struct.pack('d',matrix[i][j]))
+		# }}}
+
+	elif strcmpi(format,'StringArray'):    # {{{
+
+		#first get length of record
+		recordlength=4+4    #for length of array + code
+		for string in data:
+			recordlength+=4+len(string)    #for each string
+
+		#write length of record
+		fid.write(struct.pack('i',recordlength)) 
+
+		#write data code: 
+		fid.write(struct.pack('i',FormatToCode(format))) 
+
+		#now write length of string array
+		fid.write(struct.pack('i',len(data))) 
+
+		#now write the strings
+		for string in data:
+			fid.write(struct.pack('i',len(string))) 
+			fid.write(struct.pack('%ds' % len(string),string)) 
+		# }}}
+
+	else:    # {{{
+		raise TypeError('WriteData error message: data type: %d not supported yet! (%s)' % (format,EnumToString(enum)))
+	# }}}
+
+def BuildEnum(string): # {{{
+	"""
+	BUILDENUM - build enum out of string
+ 
+    Usage:
+       enum=BuildEnum(string)
+	"""
+
+	if '_' in string:
+		substrs=string.split('_')
+		string=''
+		for str in substrs:
+			string+=str[0].upper()+str[1:]
+	else:
+		#take first letter of string and make it uppercase: 
+		string=str[0].upper()+str[1:]
+
+	#Get Enum
+	exec('enum='+string+'Enum()',globals())
+
+	return enum
+# }}}
+
+def FormatToCode(format): # {{{
+	"""
+	This routine takes the format string, and hardcodes it into an integer, which 
+	is passed along the record, in order to identify the nature of the dataset being 
+	sent.
+	"""
+
+	if   strcmpi(format,'Boolean'):
+		code=1
+	elif strcmpi(format,'Integer'):
+		code=2
+	elif strcmpi(format,'Double'):
+		code=3
+	elif strcmpi(format,'String'):
+		code=4
+	elif strcmpi(format,'BooleanMat'):
+		code=5
+	elif strcmpi(format,'IntMat'):
+		code=6
+	elif strcmpi(format,'DoubleMat'):
+		code=7
+	elif strcmpi(format,'MatArray'):
+		code=8
+	elif strcmpi(format,'StringArray'):
+		code=9
+	else:
+		raise InputError('FormatToCode error message: data type not supported yet!')
+
+	return code
+# }}}
+
+def strcmpi(str1,str2):
+
+	if str1.lower() == str2.lower():
+		return True
+	else:
+		return False
+
