Index: /issm/trunk-jpl/jenkins/javascript/karma/karma.conf.js
===================================================================
--- /issm/trunk-jpl/jenkins/javascript/karma/karma.conf.js	(revision 20831)
+++ /issm/trunk-jpl/jenkins/javascript/karma/karma.conf.js	(revision 20832)
@@ -12,4 +12,5 @@
     // list of files / patterns to load in the browser
     files: [
+      'node_modules/jquery/dist/jquery.min.js',
       'node_modules/mathjs/dist/math.min.js',
       'lib/bin/EnumDefinitions.js',
Index: /issm/trunk-jpl/src/m/classes/mask.js
===================================================================
--- /issm/trunk-jpl/src/m/classes/mask.js	(revision 20831)
+++ /issm/trunk-jpl/src/m/classes/mask.js	(revision 20832)
@@ -56,7 +56,7 @@
 			}
 		} // }}}
-		this.marshall=function(md,fid) { //{{{
-            WriteData(fid,'object',this,'fieldname','groundedice_levelset','format','DoubleMat','mattype',1);
-            WriteData(fid,'object',this,'fieldname','ice_levelset','format','DoubleMat','mattype',1);
+		this.marshall=function(md,prefix,fid) { //{{{
+            WriteData(fid,prefix,'object',this,'fieldname','groundedice_levelset','format','DoubleMat','mattype',1);
+            WriteData(fid,prefix,'object',this,'fieldname','ice_levelset','format','DoubleMat','mattype',1);
 		}//}}}
 		this.fix=function() { //{{{
Index: /issm/trunk-jpl/src/m/classes/mesh3dprisms.js
===================================================================
--- /issm/trunk-jpl/src/m/classes/mesh3dprisms.js	(revision 20832)
+++ /issm/trunk-jpl/src/m/classes/mesh3dprisms.js	(revision 20832)
@@ -0,0 +1,159 @@
+//MESH3DPRISMS class definition
+//
+//   Usage:
+//      mesh=mesh3dprisms();
+
+function mesh3dprisms() {
+		this.setdefaultparameters = function() { // [[[)
+
+			//the connectivity is the averaged number of nodes linked to a
+			//given node through an edge. This connectivity is used to initially
+			//allocate memory to the stiffness matrix. A value of 16 seems to
+			//give a good memory/time ration. This value can be checked in
+			//trunk/test/Miscellaneous/runme.m
+			this.average_vertex_connectivity=25;
+		} // ]]]
+
+		this.checkconsistency = function(md,solution,analyses) { // [[[)
+
+			checkfield(md,'fieldname','mesh.x','NaN',1,'Inf',1,'size',[md.mesh.numberofvertices, 1]);
+			checkfield(md,'fieldname','mesh.y','NaN',1,'Inf',1,'size',[md.mesh.numberofvertices, 1]);
+			checkfield(md,'fieldname','mesh.z','NaN',1,'Inf',1,'size',[md.mesh.numberofvertices, 1]);
+			checkfield(md,'fieldname','mesh.elements','NaN',1,'Inf',1,'>',0,'values',NewArrayFillIncrement(md.mesh.numberofvertices,1,1));
+			checkfield(md,'fieldname','mesh.elements','size',[md.mesh.numberofelements, 6]);
+
+            if(ArrayAnyEqual(ArrayIsMember(NewArrayFillIncrement(md.mesh.numberofvertices,1,1),ArraySort(ArrayUnique(MatrixToList(md.mesh.elements)))),0)){
+				//md = checkmessage(md,'orphan nodes have been found. Check the mesh outline'); @TODO
+                md.checkmessage('orphan nodes have been found. Check the mesh outline');
+			}
+			checkfield(md,'fieldname','mesh.numberoflayers','>=',0);
+			checkfield(md,'fieldname','mesh.numberofelements','>',0);
+			checkfield(md,'fieldname','mesh.numberofvertices','>',0);
+			checkfield(md,'fieldname','mesh.vertexonbase','size',[md.mesh.numberofvertices, 1],'values',[0, 1]);
+			checkfield(md,'fieldname','mesh.vertexonsurface','size',[md.mesh.numberofvertices, 1],'values',[0, 1]);
+			checkfield(md,'fieldname','mesh.z','>=',md.geometry.base-Math.pow(10, -10),'message','\'mesh.z\' lower than bedrock');
+			checkfield(md,'fieldname','mesh.z','<=',md.geometry.surface+Math.pow(10, -10),'message','\'mesh.z\' higher than surface elevation');
+			checkfield(md,'fieldname','mesh.average_vertex_connectivity','>=',24,'message','\'mesh.average_vertex_connectivity\' should be at least 24 in 3d');
+		} // ]]]
+		function disp() { // [[[
+			console.log(sprintf('   3D prism Mesh:')); 
+
+			console.log(sprintf('\n      Elements and vertices of the original 2d mesh:'));
+			fielddisplay(this,'numberofelements2d','number of elements');
+			fielddisplay(this,'numberofvertices2d','number of vertices');
+			fielddisplay(this,'elements2d','vertex indices of the mesh elements');
+			fielddisplay(this,'x2d','vertices x coordinate [m]');
+			fielddisplay(this,'y2d','vertices y coordinate [m]');
+
+            console.log(sprintf('\n      Elements and vertices of the extruded 3d mesh:'));
+			fielddisplay(this,'numberofelements','number of elements');
+			fielddisplay(this,'numberofvertices','number of vertices');
+			fielddisplay(this,'elements','vertex indices of the mesh elements');
+			fielddisplay(this,'x','vertices x coordinate [m]');
+			fielddisplay(this,'y','vertices y coordinate [m]');
+			fielddisplay(this,'z','vertices z coordinate [m]');
+
+			console.log(sprintf('\n      Properties:'));
+			fielddisplay(this,'numberoflayers','number of extrusion layers');
+			fielddisplay(this,'vertexonbase','lower vertices flags list');
+			fielddisplay(this,'vertexonsurface','upper vertices flags list');
+			fielddisplay(this,'uppervertex','upper vertex list (NaN for vertex on the upper surface)');
+			fielddisplay(this,'upperelements','upper element list (NaN for element on the upper layer)');
+			fielddisplay(this,'lowervertex','lower vertex list (NaN for vertex on the lower surface)');
+			fielddisplay(this,'lowerelements','lower element list (NaN for element on the lower layer');
+			fielddisplay(this,'vertexonboundary','vertices on the boundary of the domain flag list');
+
+			fielddisplay(this,'vertexconnectivity','list of vertices connected to vertex_i');
+			fielddisplay(this,'elementconnectivity','list of vertices connected to element_i');
+			fielddisplay(this,'average_vertex_connectivity','average number of vertices connected to one vertex');
+
+			console.log(sprintf('\n      Extracted model:'));
+			fielddisplay(this,'extractedvertices','vertices extracted from the model');
+			fielddisplay(this,'extractedelements','elements extracted from the model');
+
+			console.log(sprintf('\n      Projection:'));
+			fielddisplay(this,'lat','vertices latitude [degrees]');
+			fielddisplay(this,'long','vertices longitude [degrees]');
+			fielddisplay(this,'epsg','EPSG code (ex: 3413 for UPS Greenland, 3031 for UPS Antarctica)');
+		} // ]]]
+		function marshall(md,prefix,fid) { // [[[
+			WriteData(fid,prefix,'enum',DomainTypeEnum(),'data',StringToEnum(['Domain' + this.domaintype()]),'format','Integer');
+			WriteData(fid,prefix,'enum',DomainDimensionEnum(),'data',this.dimension(),'format','Integer');
+			WriteData(fid,prefix,'enum',MeshElementtypeEnum(),'data',StringToEnum(this.elementtype()),'format','Integer');
+			WriteData(fid,prefix,'object',this,'class','mesh','fieldname','x','format','DoubleMat','mattype',1);
+			WriteData(fid,prefix,'object',this,'class','mesh','fieldname','y','format','DoubleMat','mattype',1);
+			WriteData(fid,prefix,'object',this,'class','mesh','fieldname','z','format','DoubleMat','mattype',1);
+			WriteData(fid,prefix,'object',this,'class','mesh','fieldname','elements','format','DoubleMat','mattype',2);
+			WriteData(fid,prefix,'object',this,'class','mesh','fieldname','numberoflayers','format','Integer');
+			WriteData(fid,prefix,'object',this,'class','mesh','fieldname','numberofelements','format','Integer');
+			WriteData(fid,prefix,'object',this,'class','mesh','fieldname','numberofvertices','format','Integer');
+			WriteData(fid,prefix,'object',this,'class','mesh','fieldname','vertexonbase','format','BooleanMat','mattype',1);
+			WriteData(fid,prefix,'object',this,'class','mesh','fieldname','vertexonsurface','format','BooleanMat','mattype',1);
+			WriteData(fid,prefix,'object',this,'class','mesh','fieldname','lowerelements','format','DoubleMat','mattype',2);
+			WriteData(fid,prefix,'object',this,'class','mesh','fieldname','upperelements','format','DoubleMat','mattype',2);
+			WriteData(fid,prefix,'object',this,'class','mesh','fieldname','average_vertex_connectivity','format','Integer');
+			WriteData(fid,prefix,'object',this,'class','mesh','fieldname','elements2d','format','DoubleMat','mattype',3);
+			WriteData(fid,prefix,'object',this,'class','mesh','fieldname','numberofvertices2d','format','Integer');
+			WriteData(fid,prefix,'object',this,'class','mesh','fieldname','numberofelements2d','format','Integer');
+		} // ]]]
+        this.fix=function() { //{{{
+            //Transform objects into Float64Arrays:
+            this.x=FloatFix(this.x,this.numberofvertices); 
+            this.y=FloatFix(this.y,this.numberofvertices); 
+            this.z=FloatFix(this.y,this.numberofvertices); 
+            this.r=FloatFix(this.y,this.numberofvertices); 
+            this.edges=NaNFix(this.edges,NaN);
+            this.vertexonboundary=FloatFix(this.vertexonboundary,this.numberofvertices); 
+            this.segmentmarkers=FloatFix(this.segmentmarkers,this.segments.length);
+            this.extractedvertices=NaNFix(this.extractedvertices,NaN);
+            this.extractedelements=NaNFix(this.extractedelements,NaN);
+            this.lat=NaNFix(this.lat,NaN);
+            this.long=NaNFix(this.long,NaN);
+        }//}}}
+		this.domaintype = function() { // [[[)
+			return '3D';
+		} // ]]]
+		this.dimension = function() { // [[[)
+			return 3;
+		} // ]]]
+		this.elementtype = function() { // [[[)
+			return 'Penta';
+		} // ]]]
+        this.classname = function () { //{{{
+            return "mesh3dprisms";
+        } //}}}
+
+        //properties (SetAccess=public) 
+        this.x                           = NaN;
+        this.y                           = NaN;
+        this.z                           = NaN;
+        this.elements                    = NaN;
+        this.numberoflayers              = 0;
+        this.numberofelements            = 0;
+        this.numberofvertices            = 0;
+
+        this.lat                         = NaN;
+        this.long                        = NaN;
+        this.epsg                        = 0;
+
+        this.vertexonbase                = NaN;
+        this.vertexonsurface             = NaN;
+        this.lowerelements               = NaN;
+        this.lowervertex                 = NaN;
+        this.upperelements               = NaN;
+        this.uppervertex                 = NaN;
+        this.vertexonboundary            = NaN;
+
+        this.vertexconnectivity          = NaN;
+        this.elementconnectivity         = NaN;
+        this.average_vertex_connectivity = 0;
+
+        this.x2d                         = NaN;
+        this.y2d                         = NaN;
+        this.elements2d                  = NaN;
+        this.numberofvertices2d          = 0;
+        this.numberofelements2d          = 0;
+
+        this.extractedvertices           = NaN;
+        this.extractedelements           = NaN;
+}
Index: /issm/trunk-jpl/src/m/extrusion/project3d.js
===================================================================
--- /issm/trunk-jpl/src/m/extrusion/project3d.js	(revision 20832)
+++ /issm/trunk-jpl/src/m/extrusion/project3d.js	(revision 20832)
@@ -0,0 +1,116 @@
+project3d = function() {
+    //PROJECT3D - vertically project a vector from 2d mesh
+    //
+    //   vertically project a vector from 2d mesh (split in noncoll and coll areas) into a 3d mesh.
+    //   This vector can be a node vector of size (md.mesh.numberofvertices2d,N/A) or an 
+    //   element vector of size (md.mesh.numberofelements2d,N/A). 
+    //   arguments: 
+    //      'vector': 2d vector
+    //      'type': 'element' or 'node'. 
+    //   options: 
+    //      'layer' a layer number where vector should keep its values. If not specified, all layers adopt the 
+    //             value of the 2d vector.
+    //      'padding': default to 0 (value adopted by other 3d layers not being projected0
+    //
+    //   Egs:
+    // md.extruded_vector=project3d(md,'vector',vector2d,'type','node','layer',1,'padding',null);
+    // md.extruded_vector=project3d(md,'vector',vector2d,'type','element','padding',0);
+    // md.extruded_vector=project3d(md,'vector',vector2d,'type','node');
+
+    //some regular checks
+    
+    function remove_first_n(start, arglist) { // Using slice() on arguments is discouraged because it prevents optimizations in engines such as V8
+        var args = [];
+
+        for (var i = start; i < arglist.length; i++) {
+            args.push(arglist[i]);
+        }
+
+        return args;
+    }
+
+    if (arguments.length===1 || arguments.length===0) {
+        //help project3d
+        console.error('project3d bad usage');
+    }
+
+    var md = arguments[0];
+
+    if (md.mesh.elementtype() !== 'Penta') {
+        console.error('input model is not 3d');
+    }
+
+    //retrieve parameters from options.
+    options      = new pairoptions(remove_first_n(1, arguments)); // slice to remove md
+    vector2d     = options.getfieldvalue('vector');     //mandatory
+    type         = options.getfieldvalue('type');       //mandatory
+    layer        = options.getfieldvalue('layer',0);    //optional (do all layers default:)
+    paddingvalue = options.getfieldvalue('padding',0);  //0 by default
+
+    if (isNaN(vector2d) || vector2d.length === 1) { // NaN treated as length 1 in MATLAB
+        projected_vector=vector2d;
+    } else if (type.toLowerCase() === 'node') {
+
+        //Initialize 3d vector
+        console.log(vector2d.length + " " + md.mesh.numberofvertices2d);
+        if (vector2d.length===md.mesh.numberofvertices2d) {
+                projected_vector=ones(md.mesh.numberofvertices,  vector2d[0].length).map(function(arr) { return arr.fill(paddingvalue); });
+        } else if (vector2d.length===md.mesh.numberofvertices2d+1) {
+            projected_vector=ones(md.mesh.numberofvertices+1,vector2d[0].length).map(function(arr) { return arr.fill(paddingvalue); });
+            projected_vector[projected_vector.length-1] = projected_vector[projected_vector.length-1].map(function(element, index) { return vector2d[vector2d.length-1][index]; });
+            vector2d.pop(); // Remove last array
+        } else {
+            console.error('vector length not supported')
+        }
+
+            //Fill in
+        if (layer===0) {
+            for (var i = 1; i < md.mesh.numberoflayers; ++i) {
+                var vec_idx = 0;
+
+                for (var j = (i-1)*md.mesh.numberofvertices2d+1, vec_idx = 0; j <= i * md.mesh.numberofvertices2d && vec_idx < vector2d.length; ++j, ++vec_idx) {
+                    projected_vector[j].map(function(element, index) { return vector2d[vec_idx][index]; });
+                }
+
+            }
+        } else {
+            var vec_idx = 0;
+
+            for (var i = (layer-1)*md.mesh.numberofvertices2d+1, vec_idx = 0; i <= layer * md.mesh.numberofvertices2d && vec_idx < vector2d.length; ++i, ++vec_idx) {
+                projected_vector[i] = vector2d[vec_idx];
+            }
+        }
+    } else if (type.toLowerCase() === 'element') {
+            //Initialize 3d vector
+        if (vector2d.length===md.mesh.numberofelements2d) {
+                projected_vector = ones(md.mesh.numberofelements,  vector2d[0].length).map(function(arr) { return arr.fill(paddingvalue); });
+        } else if (vector2d.length===md.mesh.numberofelements2d+1) {
+            projected_vector = ones(md.mesh.numberofelements+1,  vector2d[0].length).map(function(arr) { return arr.fill(paddingvalue); });
+
+            projected_vector[projected_vector.length-1].map(function(element, index) { return vector2d[vector2d.length-1][index]; });
+
+            vector2d.pop();
+        } else {
+            console.error('vector length not supported')
+        }
+
+            //Fill in
+        if (layer===0) {
+            for (var i = 1; i < md.mesh.numberoflayers-1; ++i) {
+                var vec_idx=0;
+                for (var j = (i-1)*md.mesh.numberofelements2d+1, vec_idx = 0; j <= i * md.mesh.numberofelements2d && vec_idx < vector2d.length; ++j, ++vec_idx) {
+                    projected_vector[j].map(function(element, index) { return vector2d[vec_idx][index]; });
+                }
+            }
+        } else {
+            var vec_idx=0;
+            for (var i = (layer-1)*md.mesh.numberofelements2d+1, vec_idx = 0; i <= layer * md.mesh.numberofelements2d && vec_idx < vector2d.length; ++i, ++vec_idx) {
+                projected_vector[i].map(function(element, index) { return vector2d[vec_idx][index]; });
+            }
+        }
+    } else {
+        console.error('project3d error message: unknown projection type');
+    }
+
+    return projected_vector;
+};
