diff --git a/src/Science.hpp b/src/Science.hpp
index 569b9a6c6c494ddbb583efef4e924702b6e43237..4110a58d0829a8b2d315efbdda1ae0fa66156d8b 100644
--- a/src/Science.hpp
+++ b/src/Science.hpp
@@ -130,9 +130,126 @@ void R_invt(TArrayn::DTArray & R, TArrayn::DTArray & u,
          TArrayn::DTArray & temp2, TArrayn::Grad * gradient_op,
          const string * grid_type, bool v_exist);
 
+
+inline double nleos_inline(double T, double S){
+   // Returns the density (kg/m^3) 
+   // MacDougall et. al. 2003  (JAOT 20)
+   //
+   // This EOS is a rational function taken at pressure = 0
+   //
+   // Constants for Numerator
+   
+    // First find any regions of negative salinity and set them to zero for this operation.
+    
+    if (S < 0) { 
+        S = 0;
+    };
+
+    const double N0 = 9.99843699e+02; 
+    const double N1 = 7.35212840e+00;
+    const double N2 = -5.45928211e-02;
+    const double N3 = 3.98476704e-04;
+    const double N4 = 2.96938239e+00;
+    const double N5 = -7.23268813e-03;
+    const double N6 = 2.12382341e-03;
+    const double N7 = 1.04004591e-02;
+    const double N8 = 1.03970529e-07;
+    const double N9 = 5.18761880e-06;
+    const double N10 = -3.24041825e-08;
+    const double N11 = -1.23869360e-11;
+
+    const int p = 0; //Someday we could include pressure effects, and whoever wanted to do that would change p to an input variable from the model
+
+    double numerator = N0 + T*(N1 + T*(N2 + N3*T)) + S*(N4 + N5*T + N6*S) + p*(N7 + N8*T*T + N9*S + p*(N10 + N11*T*T));
+
+    // Constants for denominator 
+    const double D0 = 1.00000000e+00;
+    const double D1 = 7.28606739e-03;
+    const double D2 = -4.60835542e-05;
+    const double D3 = 3.68390573e-07;
+    const double D4 = 1.80809186e-10;
+    const double D5 = 2.14691708e-03;
+    const double D6 = -9.27062484e-06;
+    const double D7 = -1.78343643e-10;
+    const double D8 = 4.76534122e-06;
+    const double D9 = 1.63410736e-09;
+    const double D10 = 5.30848875e-06;
+    const double D11 = -3.03175128e-16;
+    const double D12 = -1.27934137e-17;
+    
+    double denominator = D0 + T*(D1 + T*(D2 + T*(D3 + D4*T))) + S*(D5 + T*(D6 + D7*T*T) + sqrt(S)*(D8 + D9*T*T)) + p*(D10 + p*T*(D11*T*T + D12*p));
+
+   return numerator/denominator;
+}
+BZ_DECLARE_FUNCTION2(nleos_inline)
+
+
+
+void nleos(TArrayn::DTArray & rho, TArrayn::DTArray & T,
+         TArrayn::DTArray & S);
+
+inline double compute_alpha(double T0, double S0){
+    // Computes the thermal expansion coefficient at S0 and T0 
+    // Derivative is a finite difference for simplicity
+    // Does not divide by rho_0, that is done in lineos()
+    const double dT = 1e-08;
+    double TpdT = T0 + dT;
+  
+    double alpha = (nleos_inline(TpdT,S0) - nleos_inline(T0,S0))/dT;
+
+    return alpha;
+}
+BZ_DECLARE_FUNCTION(compute_alpha)
+
+inline double compute_beta(double T0, double S0){
+    // Computes the haline contraction coefficient at S0 and T0 
+    // Derivative is a finite difference for simplicity
+    // Does not divide by rho_0, that is done in lineos()
+    const double dS = 1e-08;
+    double SpdS = S0 + dS;
+
+    double beta = (nleos_inline(T0,SpdS) - nleos_inline(T0,S0))/dS;
+
+    return beta;
+}
+BZ_DECLARE_FUNCTION(compute_beta)
+ 
+inline double compute_rho0(double T0, double S0){
+    // Computes the reference density at S0 and T0 
+    double rho_0 = nleos_inline(T0,S0);
+
+    return rho_0;
+}
+BZ_DECLARE_FUNCTION(compute_rho0)
+void lineos(TArrayn::DTArray & rho, TArrayn::DTArray & T,
+         TArrayn::DTArray & S, const double & T0, const double & S0);
+
+void quadeos(TArrayn::DTArray & rho, TArrayn::DTArray & T);
+
+
+void eos(const string eos_type, TArrayn::DTArray & rho, TArrayn::DTArray & T, TArrayn::DTArray & S, double T0 = -10, double S0 = -2);
+
+
+
+/*
+inline double fresh_quad(double T){
+   // Returns the density (kg/m^3) for water using simple quadratic fit to 
+   // MacDougall et. al. 2003  (JAOT 20)
+   // Constants are determined via a quadratic fit preserving the Temperature of maximum density
+   const double rho_max = 9.999744074665388e+02; // Density of freshwater at Tmd (deg C)
+   //0 pressure relative to sea surface pressure.
+   const double Tmd = 3.973973973973974; // Temperature of maximum density for freshwater at the surface
+   const double C = -0.007641729398834; // Fitting Constant
+
+   return rho_max + C*(T - Tmd)*(T - Tmd);
+}
+BZ_DECLARE_FUNCTION(fresh_quad)
+*/
+
+
 // Equation of state for seawater, polynomial fit from
 // Brydon, Sun, Bleck (1999) (JGR)
-
+//
 inline double eqn_of_state(double T, double S){
    // Returns the density anomaly (kg/m^3) for water at the given
    // temperature T (degrees celsius) and salinity S (PSU?)
diff --git a/src/Science/eos.cpp b/src/Science/eos.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..77010f023921bc0cc23f1a8e76a4bace88aae211
--- /dev/null
+++ b/src/Science/eos.cpp
@@ -0,0 +1,84 @@
+#include "../Science.hpp"
+#include "math.h"
+#include "../Par_util.hpp"
+#include "stdio.h"
+#include "../Split_reader.hpp"
+#include "../T_util.hpp"
+#include "../Parformer.hpp"
+#include "../Sorter.hpp"
+#include <numeric>
+#include <stdlib.h>
+
+using blitz::Array;
+using blitz::cos;
+using namespace TArrayn;
+using namespace NSIntegrator;
+using namespace Transformer;
+
+
+void eos(const string eos_type, TArrayn::DTArray & rho, TArrayn::DTArray & T, TArrayn::DTArray & S, double T0, double S0) {
+    bool is_T0_valid = false;
+    bool is_S0_valid = false;
+
+    if (T0 >= -2) {
+        is_T0_valid = true;
+    }
+    if (S0 >= 0) {
+        is_S0_valid = true;
+    }
+
+    if (eos_type == "QUADEOS") {
+        
+        //For Debugging purposes
+        /*
+        if (master()) {
+          	    fprintf(stdout,"You picked the QUADEOS option.\n");
+                }
+                MPI_Finalize(); exit(0);
+        */
+        //Call quadeos
+        quadeos(rho,T);
+
+    }
+    else if (eos_type == "LINEOS") {
+       if (is_T0_valid & is_S0_valid) {
+
+        //For Debugging purposes
+        /*
+           if (master()) {
+          	    fprintf(stdout,"You picked the LINEOS option.\n");
+                }
+                MPI_Finalize(); exit(0);
+        */
+           //Call lineos
+        lineos(rho,T,S,T0,S0);
+         }
+       else {
+           if (master()) {
+          	fprintf(stderr,"Invalid option for background temperature or salinity. Exiting.\n");
+           }
+           MPI_Finalize(); exit(0);
+       }
+    }
+    else if (eos_type == "NLEOS") {
+
+        //For Debugging purposes
+        /*
+        if (master()) {
+          	    fprintf(stdout,"You picked the NLEOS option.\n");
+                }
+                MPI_Finalize(); exit(0);
+        */
+        //Call nleos
+        nleos(rho,T,S);
+
+    }
+    else {
+       	    if (master()) {
+			fprintf(stderr,"Invalid option received for eos type. Exiting.\n");
+			}
+                MPI_Finalize(); exit(0);
+    }
+}
+
+ 
diff --git a/src/Science/lineos.cpp b/src/Science/lineos.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..fe65663a4eef4f0a39a35e98d6118e3327854fb1
--- /dev/null
+++ b/src/Science/lineos.cpp
@@ -0,0 +1,26 @@
+#include "../Science.hpp"
+#include "math.h"
+#include "../Par_util.hpp"
+#include "stdio.h"
+#include "../Split_reader.hpp"
+#include "../T_util.hpp"
+#include "../Parformer.hpp"
+#include "../Sorter.hpp"
+#include <numeric>
+
+using blitz::Array;
+using blitz::cos;
+using namespace TArrayn;
+using namespace NSIntegrator;
+using namespace Transformer;
+
+void lineos(TArrayn::DTArray & rho, TArrayn::DTArray & T, TArrayn::DTArray & S,
+        const double & T0, const double & S0) { 
+
+    double alpha = compute_alpha(T0,S0);
+    double beta = compute_beta(T0,S0);
+    double rho_0 = compute_rho0(T0,S0);
+
+    rho = rho_0*(1 + alpha/rho_0*(T - T0) + beta/rho_0*(S - S0));
+
+}
diff --git a/src/Science/nleos.cpp b/src/Science/nleos.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e270b9e571fbe956767c7afaa6b3e78af9a644ce
--- /dev/null
+++ b/src/Science/nleos.cpp
@@ -0,0 +1,63 @@
+#include "../Science.hpp"
+#include "math.h"
+#include "../Par_util.hpp"
+#include "stdio.h"
+#include "../Split_reader.hpp"
+#include "../T_util.hpp"
+#include "../Parformer.hpp"
+#include "../Sorter.hpp"
+#include <numeric>
+
+using blitz::Array;
+using blitz::cos;
+using namespace TArrayn;
+using namespace NSIntegrator;
+using namespace Transformer;
+
+
+void nleos(TArrayn::DTArray & rho, TArrayn::DTArray & T, TArrayn::DTArray & S) {
+   // Returns the density (kg/m^3) 
+   // MacDougall et. al. 2003  (JAOT 20)
+   //
+   // This EOS is a rational function taken at pressure = 0
+   //
+   
+    // First find any regions of negative salinity and set them to zero for this operation.
+   
+
+    //Numerator
+    const double N0 = 9.99843699e+02; 
+    const double N1 = 7.35212840e+00;
+    const double N2 = -5.45928211e-02;
+    const double N3 = 3.98476704e-04;
+    const double N4 = 2.96938239e+00;
+    const double N5 = -7.23268813e-03;
+    const double N6 = 2.12382341e-03;
+    const double N7 = 1.04004591e-02;
+    const double N8 = 1.03970529e-07;
+    const double N9 = 5.18761880e-06;
+    const double N10 = -3.24041825e-08;
+    const double N11 = -1.23869360e-11;
+
+    const int p = 0; //Someday we could include pressure effects, and whoever wanted to do that would change p to an input variable from the model
+
+    rho = N0 + T*(N1 + T*(N2 + N3*T)) + S*(N4 + N5*T + N6*S) + p*(N7 + N8*T*T + N9*S + p*(N10 + N11*T*T));
+
+    // Constants for denominator 
+    const double D0 = 1.00000000e+00;
+    const double D1 = 7.28606739e-03;
+    const double D2 = -4.60835542e-05;
+    const double D3 = 3.68390573e-07;
+    const double D4 = 1.80809186e-10;
+    const double D5 = 2.14691708e-03;
+    const double D6 = -9.27062484e-06;
+    const double D7 = -1.78343643e-10;
+    const double D8 = 4.76534122e-06;
+    const double D9 = 1.63410736e-09;
+    const double D10 = 5.30848875e-06;
+    const double D11 = -3.03175128e-16;
+    const double D12 = -1.27934137e-17;
+    
+    rho = rho/(D0 + T*(D1 + T*(D2 + T*(D3 + D4*T))) + S*(D5 + T*(D6 + D7*T*T) + sqrt(max(S,0))*(D8 + D9*T*T)) + p*(D10 + p*T*(D11*T*T + D12*p)));
+}
+
diff --git a/src/Science/quadeos.cpp b/src/Science/quadeos.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e5fb8e17b295fbaa8dc4695c7b353edd1cdce647
--- /dev/null
+++ b/src/Science/quadeos.cpp
@@ -0,0 +1,30 @@
+#include "../Science.hpp"
+#include "math.h"
+#include "../Par_util.hpp"
+#include "stdio.h"
+#include "../Split_reader.hpp"
+#include "../T_util.hpp"
+#include "../Parformer.hpp"
+#include "../Sorter.hpp"
+#include <numeric>
+
+using blitz::Array;
+using blitz::cos;
+using namespace TArrayn;
+using namespace NSIntegrator;
+using namespace Transformer;
+
+
+void quadeos(TArrayn::DTArray & rho, TArrayn::DTArray & T) {
+
+   // Returns the density (kg/m^3) for water using simple quadratic fit to 
+   // MacDougall et. al. 2003  (JAOT 20)
+   // Constants are determined via a quadratic fit preserving the Temperature of maximum density
+   const double rho_max = 9.999744074665388e+02; // Density of freshwater at Tmd (deg C)
+   //0 pressure relative to sea surface pressure.
+   const double Tmd = 3.973973973973974; // Temperature of maximum density for freshwater at the surface
+   const double C = -0.007641729398834; // Fitting Constant
+
+   rho = rho_max + C*(T - Tmd)*(T - Tmd);
+
+}
diff --git a/src/cases/brydon_test/brydon_test.cpp b/src/cases/brydon_test/brydon_test.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..fae64e442dca599650b504ddd155402e800e91ad
--- /dev/null
+++ b/src/cases/brydon_test/brydon_test.cpp
@@ -0,0 +1,650 @@
+/* cases/salt_temp/salt_temp.cpp */
+/* Script for the formation of a gravity current with:
+ * zero initial velocity
+ * salt or temperature stratification (not density)
+ * and with or without topography */
+
+/* ------------------ Top matter --------------------- */
+
+// Required headers
+#include "../../BaseCase.hpp"      // Support file containing default implementations of several functions
+#include "../../Options.hpp"       // config-file parser
+#include <random/normal.h>         // Blitz random number generator
+
+using namespace ranlib;
+
+// Tensor variables for indexing
+blitz::firstIndex ii;
+blitz::secondIndex jj;
+blitz::thirdIndex kk;
+
+/* ------------------ Define parameters --------------------- */
+
+// Grid scales
+double Lx, Ly, Lz;              // Grid lengths (m)
+int    Nx, Ny, Nz;              // Number of points in x, y, z
+double MinX, MinY, MinZ;        // Minimum x/y/z points (m)
+// Mapped grid?
+bool mapped;
+// Grid types
+DIMTYPE intype_x, intype_y, intype_z;
+string grid_type[3];
+
+// Physical parameters
+double g, rot_f, rho_0;         // gravity accel (m/s^2), Coriolis frequency (s^-1), reference density (kg/m^3)
+double visco;                   // viscosity (m^2/s)
+double mu;                      // dynamic viscosity (kg/(m·s))
+double kappa_T;                 // diffusivity of temperature (m^2/s)
+double kappa_S;                 // diffusivity of salt (m^2/s)
+// helpful constants
+const int Num_tracers = 2;      // number of tracers (temperature, salt and dyes)
+const int TEMP = 0;             // index for temperature
+const int SALT = 1;             // index for salt
+
+// Problem parameters
+double delta_T;                 // temperature difference between different layers
+double delta_S;                 // salinity difference between different layers
+double T_0;                     // background temperature
+double S_0;                     // background salinity
+double delta_x;                 // horizontal transition length (m)
+double Lmix;                    // Width of mixed region (m)
+
+// Topography parameters
+double hill_height;             // height of hill (m)
+double hill_centre;             // position of hill peak (m)
+double hill_width;              // width of hill (m)
+
+// Temporal parameters
+double final_time;              // Final time (s)
+double plot_interval;           // Time between field writes (s)
+double dt_max;                  // maximum time step (s)
+
+// Restarting options
+bool restarting;                // are you restarting?
+double initial_time;            // initial start time of simulation
+int restart_sequence;           // output number to restart from
+
+// Dump parameters
+bool restart_from_dump;         // restarting from dump?
+double compute_time;            // requested computation time
+double avg_write_time;          // average time to write all output fields at one output
+double real_start_time;         // real (clock) time when simulation begins
+double compute_start_time;      // real (clock) time when computation begins (after initialization)
+
+// other options
+double perturb;                 // Initial velocity perturbation
+bool compute_enstrophy;         // Compute enstrophy?
+bool compute_dissipation;       // Compute dissipation?
+bool compute_BPE;               // Compute background potential energy?
+bool compute_internal_to_BPE;   // Compute BPE gained from internal energy?
+bool compute_stresses_top;      // Compute top surface stresses?
+bool compute_stresses_bottom;   // Compute bottom surface stresses?
+bool write_pressure;            // Write the pressure field?
+int iter = 0;                   // Iteration counter
+
+// Maximum squared buoyancy frequency
+double N2_max;
+
+/* ------------------ Adjust the class --------------------- */
+
+class userControl : public BaseCase {
+    public:
+        // Grid and topography arrays
+        DTArray *zgrid;                 // Full grid fields
+        Array<double,1> xx, yy, zz;     // 1D grid vectors
+        Array<double,1> topo;           // topography vector
+        DTArray *Hprime;                // derivative of topography vector
+
+        // Arrays and operators for derivatives
+        Grad * gradient_op;
+        DTArray *temp1, *dxdydz;
+
+        // Timing variables (for outputs and measuring time steps)
+        int plot_number;        // plot output number
+        double next_plot;       // time of next output write
+        double comp_duration;   // clock time since computation began
+        double clock_time;      // current clock time
+
+        // Size of domain
+        double length_x() const { return Lx; }
+        double length_y() const { return Ly; }
+        double length_z() const { return Lz; }
+
+        // Resolution in x, y, and z
+        int size_x() const { return Nx; }
+        int size_y() const { return Ny; }
+        int size_z() const { return Nz; }
+
+        // Set expansions (FREE_SLIP, NO_SLIP (in vertical) or PERIODIC)
+        DIMTYPE type_x() const { return intype_x; }
+        DIMTYPE type_y() const { return intype_y; }
+        DIMTYPE type_z() const { return intype_z; }
+
+        // Record the gradient-taking object
+        void set_grad(Grad * in_grad) { gradient_op = in_grad; }
+
+        // Coriolis parameter, viscosity, and diffusivities
+        double get_rot_f() const { return rot_f; }
+        double get_visco() const { return visco; }
+        double get_diffusivity(int t_num) const {
+            switch (t_num) {
+                case TEMP:
+                    return kappa_T;
+                case SALT:
+                    return kappa_S;
+                default:
+                    if (master()) fprintf(stderr,"Invalid tracer number!\n");
+                    MPI_Finalize(); exit(1);
+            }
+        }
+
+        // Temporal parameters
+        double init_time() const { return initial_time; }
+        int get_restart_sequence() const { return restart_sequence; }
+        double get_dt_max() const { return dt_max; }
+        double get_next_plot() { return next_plot; }
+
+        // Number of tracers (the first is density)
+        int numtracers() const { return Num_tracers; }
+
+        // Create mapped grid
+        bool is_mapped() const { return mapped; }
+        void do_mapping(DTArray & xg, DTArray & yg, DTArray & zg) {
+            zgrid = alloc_array(Nx,Ny,Nz);
+
+            // over-write zz to be between -1 and 1
+            // (zz defined in automatic_grid below)
+            zz = -cos(ii*M_PI/(Nz-1));      // Chebyshev in vertical
+
+            // Define topography
+            topo = hill_height*exp(-pow((xx(ii)-hill_centre)/hill_width,2));
+
+            // make full grids
+            xg = xx(ii) + 0*jj + 0*kk;
+            yg = yy(jj) + 0*ii + 0*kk;
+            zg = MinZ + 0.5*Lz*(1+zz(kk)) + 0.5*(1-zz(kk))*topo(ii);
+            *zgrid = zg;
+
+            // Write the arrays and matlab readers
+            write_array(xg,"xgrid");
+            write_reader(xg,"xgrid",false);
+            if (Ny > 1) {
+                write_array(yg,"ygrid");
+                write_reader(yg,"ygrid",false);
+            }
+            write_array(zg,"zgrid");
+            write_reader(zg,"zgrid",false);
+        }
+
+        /* Initialize velocities */
+        void init_vels(DTArray & u, DTArray & v, DTArray & w) {
+            if (master()) fprintf(stdout,"Initializing velocities\n");
+            // if restarting
+            if (restarting and !restart_from_dump) {
+                init_vels_restart(u, v, w);
+            } else if (restarting and restart_from_dump) {
+                init_vels_dump(u, v, w);
+            } else{
+                // else have a near motionless field
+                u = 0;
+                v = 0;
+                w = 0;
+                // Add a random perturbation to trigger any 3D instabilities
+                int myrank;
+                MPI_Comm_rank(MPI_COMM_WORLD,&myrank);
+                Normal<double> rnd(0,1);
+                for (int i = u.lbound(firstDim); i <= u.ubound(firstDim); i++) {
+                    rnd.seed(i);
+                    for (int j = u.lbound(secondDim); j <= u.ubound(secondDim); j++) {
+                        for (int k = u.lbound(thirdDim); k <= u.ubound(thirdDim); k++) {
+                            u(i,j,k) += perturb*rnd.random();
+                            w(i,j,k) += perturb*rnd.random();
+                            if (Ny > 1 || rot_f != 0)
+                                v(i,j,k) += perturb*rnd.random();
+                        }
+                    }
+                }
+                // Write the arrays
+                write_array(u,"u",plot_number);
+                write_array(w,"w",plot_number);
+                if (Ny > 1 || rot_f != 0) {
+                    write_array(v,"v",plot_number);
+                }
+            }
+        }
+
+        /* Initialize the tracers (density and dyes) */
+        void init_tracers(vector<DTArray *> & tracers) {
+            if (master()) fprintf(stdout,"Initializing tracers\n");
+            DTArray & temp = *tracers[TEMP];
+            DTArray & salt = *tracers[SALT];
+            
+            if (restarting and !restart_from_dump) {
+                init_tracer_restart("t",temp);
+                init_tracer_restart("s",salt);
+            } else if (restarting and restart_from_dump) {
+                init_tracer_dump("t",temp);
+                init_tracer_dump("s",salt);
+            } else {
+                // temperature configuration
+                temp = T_0 + delta_T*0.5*(1.0-tanh((xx(ii)-Lmix)/delta_x)) + 0*jj + 0*kk;
+                // salt configuration
+                salt = S_0 + delta_S*0.5*(1.0-tanh((xx(ii)-Lmix)/delta_x)) + 0*jj + 0*kk;
+                // Important! if mapped, and t or s depends on z
+                // then (*zgrid)(ii,jj,kk), must be used instead of zz(kk)
+                // Write the array
+                write_array(temp,"t",plot_number);
+                write_array(salt,"s",plot_number);
+            }
+        }
+
+        /* Forcing in the momentum equations */
+        void forcing(double t, const DTArray & u, DTArray & u_f,
+                const DTArray & v, DTArray & v_f, const DTArray & w, DTArray & w_f,
+                vector<DTArray *> & tracers, vector<DTArray *> & tracers_f) {
+            u_f = +rot_f*v;
+            v_f = -rot_f*u;
+            w_f = -g*(eqn_of_state(*tracers[TEMP],*tracers[SALT]))/rho_0;
+            *tracers_f[TEMP] = 0;
+            *tracers_f[SALT] = 0;
+        }
+
+        /* Basic analysis: compute secondary variables, and save fields and diagnostics */
+        void analysis(double time, DTArray & u, DTArray & v, DTArray & w,
+                vector<DTArray *> & tracers, DTArray & pressure) {
+            // Set-up
+            if ( iter == 0 ) {
+                if ( compute_enstrophy or compute_dissipation or
+                        compute_stresses_top or compute_stresses_bottom ) {
+                    temp1 = alloc_array(Nx,Ny,Nz);
+                }
+                if ( compute_stresses_top or compute_stresses_bottom ) {
+                    // initialize the vector of the bottom slope (Hprime)
+                    Hprime = alloc_array(Nx,Ny,1);
+                    if (is_mapped()) {
+                        bottom_slope(*Hprime, *zgrid, *temp1, gradient_op, grid_type, Nx, Ny, Nz);
+                    } else {
+                        topo = 0*ii;
+                        *Hprime = 0*ii + 0*jj;
+                    }
+                }
+                // Determine last plot if restarting from the dump file
+                if (restart_from_dump) {
+                    next_plot = (restart_sequence+1)*plot_interval;
+                }
+                // initialize the size of each voxel
+                dxdydz = alloc_array(Nx,Ny,Nz);
+                *dxdydz = (*get_quad_x())(ii)*(*get_quad_y())(jj)*(*get_quad_z())(kk);
+                if (is_mapped()) {
+                    *dxdydz = (*dxdydz)*(Lz-topo(ii))/Lz;
+                }
+            }
+            // update clocks
+            if (master()) {
+                clock_time = MPI_Wtime();
+                comp_duration = clock_time - compute_start_time;
+            }
+
+            /* Calculate and write out useful information */
+
+            // Energy (PE assumes density is density anomaly)
+            double ke_x = 0, ke_y = 0, ke_z = 0;
+            if ( Nx > 1 ) {
+                ke_x = pssum(sum(0.5*rho_0*(u*u)*(*dxdydz)));
+            }
+            if (Ny > 1 || rot_f != 0) {
+                ke_y = pssum(sum(0.5*rho_0*(v*v)*(*dxdydz)));
+            }
+            if ( Nz > 1 ) {
+                ke_z = pssum(sum(0.5*rho_0*(w*w)*(*dxdydz)));
+            }
+            double pe_tot;
+            if (is_mapped()) {
+                pe_tot = pssum(sum(eqn_of_state(*tracers[TEMP],*tracers[SALT])*g*((*zgrid)(ii,jj,kk) - MinZ)*(*dxdydz)));
+            } else {
+                pe_tot = pssum(sum(eqn_of_state(*tracers[TEMP],*tracers[SALT])*g*(zz(kk) - MinZ)*(*dxdydz)));
+            }
+            double BPE_tot = 0;
+            if (compute_BPE) {
+                // full energy budget for salt/heat stratifcations is incomplete
+                //compute_Background_PE(BPE_tot, eqn_of_state(*tracers[TEMP],*tracers[SALT]), *dxdydz, Nx, Ny, Nz, Lx, Ly, Lz,
+                //        g, rho_0, iter, true, is_mapped(), topo);
+            }
+            // Conversion from internal energy to background potential energy
+            double phi_i = 0;
+            if (compute_internal_to_BPE) {
+                // this is not finished for temperature/salt stratified cases
+                //compute_BPE_from_internal(phi_i, *tracers[RHO], kappa_rho, rho_0, g, Nz);
+            }
+            // viscous dissipation
+            double diss_tot = 0;
+            double max_diss = 0;
+            if (compute_dissipation) {
+                dissipation(*temp1, u, v, w, gradient_op, grid_type, Nx, Ny, Nz, mu);
+                max_diss = psmax(max(*temp1));
+                diss_tot = pssum(sum((*temp1)*(*dxdydz)));
+            }
+            // Vorticity / Enstrophy
+            double max_vort_x = 0, enst_x_tot = 0;
+            double max_vort_y = 0, enst_y_tot = 0;
+            double max_vort_z = 0, enst_z_tot = 0;
+            if (compute_enstrophy) {
+                // x-vorticity
+                if (Ny > 1 and Nz > 1) {
+                    compute_vort_x(*temp1, v, w, gradient_op, grid_type);
+                    max_vort_x = psmax(max(abs(*temp1)));
+                    enst_x_tot = pssum(sum(0.5*pow(*temp1,2)*(*dxdydz)));
+                }
+                // y-vorticity
+                if (Nx > 1 and Nz > 1) {
+                    compute_vort_y(*temp1, u, w, gradient_op, grid_type);
+                    max_vort_y = psmax(max(abs(*temp1)));
+                    enst_y_tot = pssum(sum(0.5*pow(*temp1,2)*(*dxdydz)));
+                }
+                // z-vorticity
+                if (Nx > 1 and Ny > 1) {
+                    compute_vort_z(*temp1, u, v, gradient_op, grid_type);
+                    max_vort_z = psmax(max(abs(*temp1)));
+                    enst_z_tot = pssum(sum(0.5*pow(*temp1,2)*(*dxdydz)));
+                }
+            }
+            // max of fields
+            double max_u = psmax(max(abs(u)));
+            double max_v = psmax(max(abs(v)));
+            double max_w = psmax(max(abs(w)));
+            double max_vel = psmax(max(sqrt(u*u + v*v + w*w)));
+            double max_temp = psmax(max(*tracers[TEMP]));
+            double min_temp = psmin(min(*tracers[TEMP]));
+            double max_salt = psmax(max(*tracers[SALT]));
+            double min_salt = psmin(min(*tracers[SALT]));
+            double max_rho =  psmax(max(eqn_of_state(*tracers[TEMP],*tracers[SALT])));
+            double min_rho =  psmin(min(eqn_of_state(*tracers[TEMP],*tracers[SALT])));
+            // total mass (tracers[RHO] is non-dimensional density)
+            double mass = pssum(sum(eqn_of_state(*tracers[TEMP],*tracers[SALT])*(*dxdydz)));
+
+            if (master()) {
+                // add diagnostics to buffers
+                string header, line;
+                add_diagnostic("Iter", iter,            header, line);
+                add_diagnostic("Clock_time", comp_duration, header, line);
+                add_diagnostic("Time", time,            header, line);
+                add_diagnostic("Max_vel", max_vel,      header, line);
+                add_diagnostic("Max_temperature", max_temp, header, line);
+                add_diagnostic("Min_temperature", min_temp, header, line);
+                add_diagnostic("Max_salinity", max_salt,    header, line);
+                add_diagnostic("Min_salinity", min_salt,    header, line);
+                add_diagnostic("Max_density", max_rho,  header, line);
+                add_diagnostic("Min_density", min_rho,  header, line);
+                add_diagnostic("Mass", mass,            header, line);
+                add_diagnostic("PE_tot", pe_tot,        header, line);
+                if (compute_BPE) {
+                    add_diagnostic("BPE_tot", BPE_tot,  header, line);
+                }
+                if (compute_internal_to_BPE) {
+                    add_diagnostic("BPE_from_int", phi_i,   header, line);
+                }
+                if (compute_dissipation) {
+                    add_diagnostic("Max_diss", max_diss,    header, line);
+                    add_diagnostic("Diss_tot", diss_tot,    header, line);
+                }
+                if (Nx > 1) {
+                    add_diagnostic("Max_u", max_u,  header, line);
+                    add_diagnostic("KE_x", ke_x,    header, line);
+                }
+                if (Ny > 1 || rot_f != 0) {
+                    add_diagnostic("Max_v", max_v,  header, line);
+                    add_diagnostic("KE_y", ke_y,    header, line);
+                }
+                if (Nz > 1) {
+                    add_diagnostic("Max_w", max_w,  header, line);
+                    add_diagnostic("KE_z", ke_z,    header, line);
+                }
+                if (Ny > 1 && Nz > 1 && compute_enstrophy) {
+                    add_diagnostic("Enst_x_tot", enst_x_tot, header, line);
+                    add_diagnostic("Max_vort_x", max_vort_x, header, line);
+                }
+                if (Nx > 1 && Nz > 1 && compute_enstrophy) {
+                    add_diagnostic("Enst_y_tot", enst_y_tot, header, line);
+                    add_diagnostic("Max_vort_y", max_vort_y, header, line);
+                }
+                if (Nx > 1 && Ny > 1 && compute_enstrophy) {
+                    add_diagnostic("Enst_z_tot", enst_z_tot, header, line);
+                    add_diagnostic("Max_vort_z", max_vort_z, header, line);
+                }
+
+                // Write to file
+                if (!(restarting and iter==0))
+                    write_diagnostics(header, line, iter, restarting);
+                // and to the log file
+                fprintf(stdout,"[%d] (%.4g) %.4f: "
+                        "%.4g %.4g %.4g %.4g\n",
+                        iter,comp_duration,time,
+                        max_u,max_v,max_w,max_rho);
+            }
+
+            // Top Surface Stresses
+            if ( compute_stresses_top ) {
+                stresses_top(u, v, w, *Hprime, *temp1, gradient_op, grid_type, mu, time, iter, restarting);
+            }
+            // Bottom Surface Stresses
+            if ( compute_stresses_bottom ) {
+                stresses_bottom(u, v, w, *Hprime, *temp1, gradient_op, grid_type, mu, time, iter, restarting);
+            }
+
+            /* Write to disk if at correct time */
+            if ((time - next_plot) > -1e-6) {
+                plot_number++;
+                comp_duration = MPI_Wtime(); // time just before write (for dump)
+                // Write the arrays
+                write_array(u,"u",plot_number);
+                write_array(w,"w",plot_number);
+                if (Ny > 1 || rot_f != 0)
+                    write_array(v,"v",plot_number);
+                write_array(*tracers[TEMP],"t",plot_number);
+                write_array(*tracers[SALT],"s",plot_number);
+                if (write_pressure)
+                    write_array(pressure,"p",plot_number);
+                // update next plot time
+                next_plot = next_plot + plot_interval;
+
+                // Find average time to write (for dump)
+                clock_time = MPI_Wtime(); // time just after write
+                avg_write_time = (avg_write_time*(plot_number-restart_sequence-1) 
+                        + (clock_time - comp_duration))/(plot_number-restart_sequence);
+                // Print information about plot outputs
+                write_plot_times(time, clock_time, comp_duration, avg_write_time, plot_number, restarting);
+            }
+
+            // see if close to end of compute time and dump
+            check_and_dump(clock_time, real_start_time, compute_time, time, avg_write_time,
+                    plot_number, iter, u, v, w, tracers);
+            // Change dump log file if successfully reached final time
+            successful_dump(plot_number, final_time, plot_interval);
+            // increase counter
+            iter++;
+        }
+
+        // User specified variables to dump
+        void write_variables(DTArray & u,DTArray & v, DTArray & w,
+                vector<DTArray *> & tracers) {
+            write_array(u,"u.dump");
+            write_array(v,"v.dump");
+            write_array(w,"w.dump");
+            write_array(*tracers[TEMP],"t.dump");
+            write_array(*tracers[SALT],"s.dump");
+        }
+
+        // Constructor: Initialize local variables
+        userControl():
+            xx(split_range(Nx)), yy(Ny), zz(Nz),
+            topo(split_range(Nx)), gradient_op(0),
+            plot_number(restart_sequence),
+            next_plot(initial_time + plot_interval)
+    {   compute_quadweights(
+            size_x(),   size_y(),   size_z(),
+            length_x(), length_y(), length_z(),
+            type_x(),   type_y(),   type_z());
+        // Create one-dimensional arrays for the coordinates
+        automatic_grid(MinX, MinY, MinZ, &xx, &yy, &zz);
+    }
+};
+
+/* The ''main'' routine */
+int main(int argc, char ** argv) {
+    /* Initialize MPI.  This is required even for single-processor runs,
+       since the inner routines assume some degree of parallelization,
+       even if it is trivial. */
+    MPI_Init(&argc, &argv);
+
+    real_start_time = MPI_Wtime();     // start of simulation (for dump)
+    /* ------------------ Define parameters from spins.conf --------------------- */
+    options_init();
+
+    option_category("Grid Options");
+    add_option("Lx",&Lx,"Length of tank");
+    add_option("Ly",&Ly,1.0,"Width of tank");
+    add_option("Lz",&Lz,"Height of tank");
+    add_option("Nx",&Nx,"Number of points in X");
+    add_option("Ny",&Ny,1,"Number of points in Y");
+    add_option("Nz",&Nz,"Number of points in Z");
+    add_option("min_x",&MinX,0.0,"Minimum X-value");
+    add_option("min_y",&MinY,0.0,"Minimum Y-value");
+    add_option("min_z",&MinZ,0.0,"Minimum Z-value");
+
+    option_category("Grid expansion options");
+    string xgrid_type, ygrid_type, zgrid_type;
+    add_option("type_x",&xgrid_type,
+            "Grid type in X.  Valid values are:\n"
+            "   FOURIER: Periodic\n"
+            "   FREE_SLIP: Cosine expansion\n"
+            "   NO_SLIP: Chebyhsev expansion");
+    add_option("type_y",&ygrid_type,"FOURIER","Grid type in Y");
+    add_option("type_z",&zgrid_type,"Grid type in Z");
+    add_option("mapped_grid",&mapped,true,"Is the grid mapped?");
+
+    option_category("Topography parameters");
+    add_option("hill_height",&hill_height,"Height of hill");
+    add_option("hill_centre",&hill_centre,"location of hill peak");
+    add_option("hill_width",&hill_width,"Width of hill");
+
+    option_category("Physical parameters");
+    add_option("g",&g,9.81,"Gravitational acceleration");
+    add_option("rot_f",&rot_f,0.0,"Coriolis parameter");
+    add_option("rho_0",&rho_0,1000.0,"Reference density");
+    add_option("visco",&visco,"Viscosity");
+    add_option("kappa_T",&kappa_T,"Diffusivity of heat");
+    add_option("kappa_S",&kappa_S,"Diffusivity of salt");
+
+    option_category("Problem parameters");
+    add_option("delta_T",&delta_T,"Temperature difference");
+    add_option("delta_S",&delta_S,"Salinity difference");
+    add_option("T_0",&T_0,"Background temperature");
+    add_option("S_0",&S_0,"Background salinity");
+    add_option("delta_x",&delta_x,"Horizontal transition half-width");
+    add_option("Lmix",&Lmix,"Width of collapse region");
+
+    option_category("Temporal options");
+    add_option("final_time",&final_time,"Final time");
+    add_option("plot_interval",&plot_interval,"Time between writes");
+    add_option("dt_max",&dt_max,0.0,"Maximum time step. Zero value results in the default");
+
+    option_category("Restart options");
+    add_option("restart",&restarting,false,"Restart from prior output time.");
+    add_option("restart_time",&initial_time,0.0,"Time to restart from");
+    add_option("restart_sequence",&restart_sequence,-1,"Sequence number to restart from");
+
+    option_category("Dumping options");
+    add_option("restart_from_dump",&restart_from_dump,false,"If restart from dump");
+    add_option("compute_time",&compute_time,-1.0,"Time permitted for computation");
+
+    option_category("Other options");
+    add_option("perturb",&perturb,"Initial perturbation in velocity");
+    add_option("compute_enstrophy",&compute_enstrophy,true,"Calculate enstrophy?");
+    add_option("compute_dissipation",&compute_dissipation,true,"Calculate dissipation?");
+    add_option("compute_BPE",&compute_BPE,true,"Calculate BPE?");
+    add_option("compute_internal_to_BPE",&compute_internal_to_BPE,true,
+            "Calculate BPE gained from internal energy?");
+    add_option("compute_stresses_top",&compute_stresses_top,false,"Calculate top surfaces stresses?");
+    add_option("compute_stresses_bottom",&compute_stresses_bottom,false,"Calculate bottom surfaces stresses?");
+    add_option("write_pressure",&write_pressure,false,"Write the pressure field?");
+
+    option_category("Filter options");
+    add_option("f_cutoff",&f_cutoff,0.6,"Filter cut-off frequency");
+    add_option("f_order",&f_order,2.0,"Filter order");
+    add_option("f_strength",&f_strength,20.0,"Filter strength");
+
+    // Parse the options from the command line and config file
+    options_parse(argc,argv);
+
+    /* ------------------ Adjust and check parameters --------------------- */
+    /* Now, make sense of the options received.  Many of these
+     * can be directly used, but the ones of string-type need further procesing. */
+
+    // adjust temporal values when restarting from dump
+    if (restart_from_dump) {
+        adjust_for_dump(restarting, initial_time, restart_sequence,
+                final_time, compute_time, avg_write_time, Num_tracers, Nx, Ny, Nz);
+    }
+
+    // check restart sequence
+    check_restart_sequence(restarting, restart_sequence, initial_time, plot_interval);
+
+    // parse expansion types
+    parse_boundary_conditions(xgrid_type, ygrid_type, zgrid_type, intype_x, intype_y, intype_z);
+    // vector of expansion types
+    grid_type[0] = xgrid_type;
+    grid_type[1] = ygrid_type;
+    grid_type[2] = zgrid_type;
+
+    // adjust Ly for 2D
+    if (Ny==1 and Ly!=1.0) {
+        Ly = 1.0;
+        if (master())
+            fprintf(stdout,"Simulation is 2 dimensional, "
+                    "Ly has been changed to 1.0 for normalization.\n");
+    }
+
+    /* ------------------ Derived parameters --------------------- */
+
+    // Dynamic viscosity
+    mu = visco*rho_0;
+    // Maximum buoyancy frequency (squared) if the initial stratification was stable
+    //N2_max = g*delta_rho/(2*delta_x);
+    // Maximum time step
+    if (dt_max == 0.0) {
+        // if dt_max not given in spins.conf, use this
+        dt_max = 0.1;
+    }
+
+    /* ------------------ Initialize --------------------- */
+
+    // Create an instance of the above class
+    userControl mycode;
+    // Create a flow-evolver that takes its settings from the above class
+    FluidEvolve<userControl> do_stuff(&mycode);
+    // Initialize
+    do_stuff.initialize();
+
+    /* ------------------ Print some parameters --------------------- */
+
+    compute_start_time = MPI_Wtime(); // beginning of simulation (after reading in data)
+    double startup_time = compute_start_time - real_start_time;
+
+    if (master()) {
+        fprintf(stdout,"Dam break problem\n");
+        fprintf(stdout,"Using a %f x %f x %f grid of %d x %d x %d points\n",Lx,Ly,Lz,Nx,Ny,Nz);
+        fprintf(stdout,"g = %f, rot_f = %f, rho_0 = %f\n",g,rot_f,rho_0);
+        fprintf(stdout,"Time between plots: %g s\n",plot_interval);
+        fprintf(stdout,"Initial velocity perturbation: %g\n",perturb);
+        fprintf(stdout,"Filter cutoff = %f, order = %f, strength = %f\n",f_cutoff,f_order,f_strength);
+        fprintf(stdout,"Approx. max buoyancy frequency squared: %g\n",N2_max);
+        fprintf(stdout,"Max time step: %g\n",dt_max);
+        fprintf(stdout,"Start-up time: %.6g s.\n",startup_time);
+    }
+
+    /* ------------------ Run --------------------- */
+    // Run until the end of time
+    do_stuff.do_run(final_time);
+    MPI_Finalize();
+    return 0;
+}
diff --git a/src/cases/brydon_test/spins.conf b/src/cases/brydon_test/spins.conf
new file mode 100644
index 0000000000000000000000000000000000000000..fe5c278245ccd17011b34d7b7cd6cd4dbdcef1ff
--- /dev/null
+++ b/src/cases/brydon_test/spins.conf
@@ -0,0 +1,67 @@
+## Salt-temperature configuration file
+
+# Spatial Parameters
+Lx = 1.0
+Ly = 1.0
+Lz = 0.1
+Nx = 1024
+Ny = 1
+Nz = 128
+min_x = 0
+min_y = 0
+min_z = 0
+
+# Expansion types
+type_x = FREE_SLIP
+type_y = FREE_SLIP
+type_z = FREE_SLIP
+mapped_grid = false
+
+# Physical Parameters
+g = 9.81
+rot_f = 0.0e-3
+rho_0 = 1000.0
+visco = 2e-6
+kappa_T = 2e-6
+kappa_S = 1e-7
+
+# Problem Parameters (delta_rho is percentage)
+delta_T = 25.0
+delta_S = 5.0
+T_0 = 15.0
+S_0 = 0.0
+delta_x = 0.02
+Lmix = 0.1
+
+# Problem topography parameters
+hill_height = 0.1
+hill_centre = 0.5
+hill_width = 0.05
+
+# Temporal Parameters
+final_time = 100
+plot_interval = 1
+#dt_max = 0.0
+
+# Restart Options
+restart = false
+restart_time = 0.0
+restart_sequence = 0
+restart_from_dump = false
+compute_time = -1
+
+# Perturbation Parameter
+perturb = 1e-3
+
+# Filter Parameters
+f_cutoff = 0.6
+f_order = 2.0
+f_strength = 20.0
+
+# secondary diagnostics
+#compute_enstrophy = true
+#compute_dissipation = true
+compute_BPE = false
+compute_internal_to_BPE = false
+#compute_stresses_top = false
+#compute_stresses_bottom = false
diff --git a/src/cases/jmd_test/jmd_test.cpp b/src/cases/jmd_test/jmd_test.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..99ecd95bf751660bb07d77df3c9ab730f76e8a7b
--- /dev/null
+++ b/src/cases/jmd_test/jmd_test.cpp
@@ -0,0 +1,650 @@
+/* cases/salt_temp/salt_temp.cpp */
+/* Script for the formation of a gravity current with:
+ * zero initial velocity
+ * salt or temperature stratification (not density)
+ * and with or without topography */
+
+/* ------------------ Top matter --------------------- */
+
+// Required headers
+#include "../../BaseCase.hpp"      // Support file containing default implementations of several functions
+#include "../../Options.hpp"       // config-file parser
+#include <random/normal.h>         // Blitz random number generator
+
+using namespace ranlib;
+
+// Tensor variables for indexing
+blitz::firstIndex ii;
+blitz::secondIndex jj;
+blitz::thirdIndex kk;
+
+/* ------------------ Define parameters --------------------- */
+
+// Grid scales
+double Lx, Ly, Lz;              // Grid lengths (m)
+int    Nx, Ny, Nz;              // Number of points in x, y, z
+double MinX, MinY, MinZ;        // Minimum x/y/z points (m)
+// Mapped grid?
+bool mapped;
+// Grid types
+DIMTYPE intype_x, intype_y, intype_z;
+string grid_type[3];
+
+// Physical parameters
+double g, rot_f, rho_0;         // gravity accel (m/s^2), Coriolis frequency (s^-1), reference density (kg/m^3)
+double visco;                   // viscosity (m^2/s)
+double mu;                      // dynamic viscosity (kg/(m·s))
+double kappa_T;                 // diffusivity of temperature (m^2/s)
+double kappa_S;                 // diffusivity of salt (m^2/s)
+// helpful constants
+const int Num_tracers = 2;      // number of tracers (temperature, salt and dyes)
+const int TEMP = 0;             // index for temperature
+const int SALT = 1;             // index for salt
+
+// Problem parameters
+double delta_T;                 // temperature difference between different layers
+double delta_S;                 // salinity difference between different layers
+double T_0;                     // background temperature
+double S_0;                     // background salinity
+double delta_x;                 // horizontal transition length (m)
+double Lmix;                    // Width of mixed region (m)
+
+// Topography parameters
+double hill_height;             // height of hill (m)
+double hill_centre;             // position of hill peak (m)
+double hill_width;              // width of hill (m)
+
+// Temporal parameters
+double final_time;              // Final time (s)
+double plot_interval;           // Time between field writes (s)
+double dt_max;                  // maximum time step (s)
+
+// Restarting options
+bool restarting;                // are you restarting?
+double initial_time;            // initial start time of simulation
+int restart_sequence;           // output number to restart from
+
+// Dump parameters
+bool restart_from_dump;         // restarting from dump?
+double compute_time;            // requested computation time
+double avg_write_time;          // average time to write all output fields at one output
+double real_start_time;         // real (clock) time when simulation begins
+double compute_start_time;      // real (clock) time when computation begins (after initialization)
+
+// other options
+double perturb;                 // Initial velocity perturbation
+bool compute_enstrophy;         // Compute enstrophy?
+bool compute_dissipation;       // Compute dissipation?
+bool compute_BPE;               // Compute background potential energy?
+bool compute_internal_to_BPE;   // Compute BPE gained from internal energy?
+bool compute_stresses_top;      // Compute top surface stresses?
+bool compute_stresses_bottom;   // Compute bottom surface stresses?
+bool write_pressure;            // Write the pressure field?
+int iter = 0;                   // Iteration counter
+
+// Maximum squared buoyancy frequency
+double N2_max;
+
+/* ------------------ Adjust the class --------------------- */
+
+class userControl : public BaseCase {
+    public:
+        // Grid and topography arrays
+        DTArray *zgrid;                 // Full grid fields
+        Array<double,1> xx, yy, zz;     // 1D grid vectors
+        Array<double,1> topo;           // topography vector
+        DTArray *Hprime;                // derivative of topography vector
+
+        // Arrays and operators for derivatives
+        Grad * gradient_op;
+        DTArray *temp1, *dxdydz;
+
+        // Timing variables (for outputs and measuring time steps)
+        int plot_number;        // plot output number
+        double next_plot;       // time of next output write
+        double comp_duration;   // clock time since computation began
+        double clock_time;      // current clock time
+
+        // Size of domain
+        double length_x() const { return Lx; }
+        double length_y() const { return Ly; }
+        double length_z() const { return Lz; }
+
+        // Resolution in x, y, and z
+        int size_x() const { return Nx; }
+        int size_y() const { return Ny; }
+        int size_z() const { return Nz; }
+
+        // Set expansions (FREE_SLIP, NO_SLIP (in vertical) or PERIODIC)
+        DIMTYPE type_x() const { return intype_x; }
+        DIMTYPE type_y() const { return intype_y; }
+        DIMTYPE type_z() const { return intype_z; }
+
+        // Record the gradient-taking object
+        void set_grad(Grad * in_grad) { gradient_op = in_grad; }
+
+        // Coriolis parameter, viscosity, and diffusivities
+        double get_rot_f() const { return rot_f; }
+        double get_visco() const { return visco; }
+        double get_diffusivity(int t_num) const {
+            switch (t_num) {
+                case TEMP:
+                    return kappa_T;
+                case SALT:
+                    return kappa_S;
+                default:
+                    if (master()) fprintf(stderr,"Invalid tracer number!\n");
+                    MPI_Finalize(); exit(1);
+            }
+        }
+
+        // Temporal parameters
+        double init_time() const { return initial_time; }
+        int get_restart_sequence() const { return restart_sequence; }
+        double get_dt_max() const { return dt_max; }
+        double get_next_plot() { return next_plot; }
+
+        // Number of tracers (the first is density)
+        int numtracers() const { return Num_tracers; }
+
+        // Create mapped grid
+        bool is_mapped() const { return mapped; }
+        void do_mapping(DTArray & xg, DTArray & yg, DTArray & zg) {
+            zgrid = alloc_array(Nx,Ny,Nz);
+
+            // over-write zz to be between -1 and 1
+            // (zz defined in automatic_grid below)
+            zz = -cos(ii*M_PI/(Nz-1));      // Chebyshev in vertical
+
+            // Define topography
+            topo = hill_height*exp(-pow((xx(ii)-hill_centre)/hill_width,2));
+
+            // make full grids
+            xg = xx(ii) + 0*jj + 0*kk;
+            yg = yy(jj) + 0*ii + 0*kk;
+            zg = MinZ + 0.5*Lz*(1+zz(kk)) + 0.5*(1-zz(kk))*topo(ii);
+            *zgrid = zg;
+
+            // Write the arrays and matlab readers
+            write_array(xg,"xgrid");
+            write_reader(xg,"xgrid",false);
+            if (Ny > 1) {
+                write_array(yg,"ygrid");
+                write_reader(yg,"ygrid",false);
+            }
+            write_array(zg,"zgrid");
+            write_reader(zg,"zgrid",false);
+        }
+
+        /* Initialize velocities */
+        void init_vels(DTArray & u, DTArray & v, DTArray & w) {
+            if (master()) fprintf(stdout,"Initializing velocities\n");
+            // if restarting
+            if (restarting and !restart_from_dump) {
+                init_vels_restart(u, v, w);
+            } else if (restarting and restart_from_dump) {
+                init_vels_dump(u, v, w);
+            } else{
+                // else have a near motionless field
+                u = 0;
+                v = 0;
+                w = 0;
+                // Add a random perturbation to trigger any 3D instabilities
+                int myrank;
+                MPI_Comm_rank(MPI_COMM_WORLD,&myrank);
+                Normal<double> rnd(0,1);
+                for (int i = u.lbound(firstDim); i <= u.ubound(firstDim); i++) {
+                    rnd.seed(i);
+                    for (int j = u.lbound(secondDim); j <= u.ubound(secondDim); j++) {
+                        for (int k = u.lbound(thirdDim); k <= u.ubound(thirdDim); k++) {
+                            u(i,j,k) += perturb*rnd.random();
+                            w(i,j,k) += perturb*rnd.random();
+                            if (Ny > 1 || rot_f != 0)
+                                v(i,j,k) += perturb*rnd.random();
+                        }
+                    }
+                }
+                // Write the arrays
+                write_array(u,"u",plot_number);
+                write_array(w,"w",plot_number);
+                if (Ny > 1 || rot_f != 0) {
+                    write_array(v,"v",plot_number);
+                }
+            }
+        }
+
+        /* Initialize the tracers (density and dyes) */
+        void init_tracers(vector<DTArray *> & tracers) {
+            if (master()) fprintf(stdout,"Initializing tracers\n");
+            DTArray & temp = *tracers[TEMP];
+            DTArray & salt = *tracers[SALT];
+            
+            if (restarting and !restart_from_dump) {
+                init_tracer_restart("t",temp);
+                init_tracer_restart("s",salt);
+            } else if (restarting and restart_from_dump) {
+                init_tracer_dump("t",temp);
+                init_tracer_dump("s",salt);
+            } else {
+                // temperature configuration
+                temp = T_0 + delta_T*0.5*(1.0-tanh((xx(ii)-Lmix)/delta_x)) + 0*jj + 0*kk;
+                // salt configuration
+                salt = S_0 + delta_S*0.5*(1.0-tanh((xx(ii)-Lmix)/delta_x)) + 0*jj + 0*kk;
+                // Important! if mapped, and t or s depends on z
+                // then (*zgrid)(ii,jj,kk), must be used instead of zz(kk)
+                // Write the array
+                write_array(temp,"t",plot_number);
+                write_array(salt,"s",plot_number);
+            }
+        }
+
+        /* Forcing in the momentum equations */
+        void forcing(double t, const DTArray & u, DTArray & u_f,
+                const DTArray & v, DTArray & v_f, const DTArray & w, DTArray & w_f,
+                vector<DTArray *> & tracers, vector<DTArray *> & tracers_f) {
+            u_f = +rot_f*v;
+            v_f = -rot_f*u;
+            w_f = -g*(nleos(*tracers[TEMP],*tracers[SALT]) - rho_0)/rho_0;
+            *tracers_f[TEMP] = 0;
+            *tracers_f[SALT] = 0;
+        }
+
+        /* Basic analysis: compute secondary variables, and save fields and diagnostics */
+        void analysis(double time, DTArray & u, DTArray & v, DTArray & w,
+                vector<DTArray *> & tracers, DTArray & pressure) {
+            // Set-up
+            if ( iter == 0 ) {
+                if ( compute_enstrophy or compute_dissipation or
+                        compute_stresses_top or compute_stresses_bottom ) {
+                    temp1 = alloc_array(Nx,Ny,Nz);
+                }
+                if ( compute_stresses_top or compute_stresses_bottom ) {
+                    // initialize the vector of the bottom slope (Hprime)
+                    Hprime = alloc_array(Nx,Ny,1);
+                    if (is_mapped()) {
+                        bottom_slope(*Hprime, *zgrid, *temp1, gradient_op, grid_type, Nx, Ny, Nz);
+                    } else {
+                        topo = 0*ii;
+                        *Hprime = 0*ii + 0*jj;
+                    }
+                }
+                // Determine last plot if restarting from the dump file
+                if (restart_from_dump) {
+                    next_plot = (restart_sequence+1)*plot_interval;
+                }
+                // initialize the size of each voxel
+                dxdydz = alloc_array(Nx,Ny,Nz);
+                *dxdydz = (*get_quad_x())(ii)*(*get_quad_y())(jj)*(*get_quad_z())(kk);
+                if (is_mapped()) {
+                    *dxdydz = (*dxdydz)*(Lz-topo(ii))/Lz;
+                }
+            }
+            // update clocks
+            if (master()) {
+                clock_time = MPI_Wtime();
+                comp_duration = clock_time - compute_start_time;
+            }
+
+            /* Calculate and write out useful information */
+
+            // Energy (PE assumes density is density anomaly)
+            double ke_x = 0, ke_y = 0, ke_z = 0;
+            if ( Nx > 1 ) {
+                ke_x = pssum(sum(0.5*rho_0*(u*u)*(*dxdydz)));
+            }
+            if (Ny > 1 || rot_f != 0) {
+                ke_y = pssum(sum(0.5*rho_0*(v*v)*(*dxdydz)));
+            }
+            if ( Nz > 1 ) {
+                ke_z = pssum(sum(0.5*rho_0*(w*w)*(*dxdydz)));
+            }
+            double pe_tot;
+            if (is_mapped()) {
+                pe_tot = pssum(sum(eqn_of_state(*tracers[TEMP],*tracers[SALT])*g*((*zgrid)(ii,jj,kk) - MinZ)*(*dxdydz)));
+            } else {
+                pe_tot = pssum(sum(eqn_of_state(*tracers[TEMP],*tracers[SALT])*g*(zz(kk) - MinZ)*(*dxdydz)));
+            }
+            double BPE_tot = 0;
+            if (compute_BPE) {
+                // full energy budget for salt/heat stratifcations is incomplete
+                //compute_Background_PE(BPE_tot, eqn_of_state(*tracers[TEMP],*tracers[SALT]), *dxdydz, Nx, Ny, Nz, Lx, Ly, Lz,
+                //        g, rho_0, iter, true, is_mapped(), topo);
+            }
+            // Conversion from internal energy to background potential energy
+            double phi_i = 0;
+            if (compute_internal_to_BPE) {
+                // this is not finished for temperature/salt stratified cases
+                //compute_BPE_from_internal(phi_i, *tracers[RHO], kappa_rho, rho_0, g, Nz);
+            }
+            // viscous dissipation
+            double diss_tot = 0;
+            double max_diss = 0;
+            if (compute_dissipation) {
+                dissipation(*temp1, u, v, w, gradient_op, grid_type, Nx, Ny, Nz, mu);
+                max_diss = psmax(max(*temp1));
+                diss_tot = pssum(sum((*temp1)*(*dxdydz)));
+            }
+            // Vorticity / Enstrophy
+            double max_vort_x = 0, enst_x_tot = 0;
+            double max_vort_y = 0, enst_y_tot = 0;
+            double max_vort_z = 0, enst_z_tot = 0;
+            if (compute_enstrophy) {
+                // x-vorticity
+                if (Ny > 1 and Nz > 1) {
+                    compute_vort_x(*temp1, v, w, gradient_op, grid_type);
+                    max_vort_x = psmax(max(abs(*temp1)));
+                    enst_x_tot = pssum(sum(0.5*pow(*temp1,2)*(*dxdydz)));
+                }
+                // y-vorticity
+                if (Nx > 1 and Nz > 1) {
+                    compute_vort_y(*temp1, u, w, gradient_op, grid_type);
+                    max_vort_y = psmax(max(abs(*temp1)));
+                    enst_y_tot = pssum(sum(0.5*pow(*temp1,2)*(*dxdydz)));
+                }
+                // z-vorticity
+                if (Nx > 1 and Ny > 1) {
+                    compute_vort_z(*temp1, u, v, gradient_op, grid_type);
+                    max_vort_z = psmax(max(abs(*temp1)));
+                    enst_z_tot = pssum(sum(0.5*pow(*temp1,2)*(*dxdydz)));
+                }
+            }
+            // max of fields
+            double max_u = psmax(max(abs(u)));
+            double max_v = psmax(max(abs(v)));
+            double max_w = psmax(max(abs(w)));
+            double max_vel = psmax(max(sqrt(u*u + v*v + w*w)));
+            double max_temp = psmax(max(*tracers[TEMP]));
+            double min_temp = psmin(min(*tracers[TEMP]));
+            double max_salt = psmax(max(*tracers[SALT]));
+            double min_salt = psmin(min(*tracers[SALT]));
+            double max_rho =  psmax(max(eqn_of_state(*tracers[TEMP],*tracers[SALT])));
+            double min_rho =  psmin(min(eqn_of_state(*tracers[TEMP],*tracers[SALT])));
+            // total mass (tracers[RHO] is non-dimensional density)
+            double mass = pssum(sum(eqn_of_state(*tracers[TEMP],*tracers[SALT])*(*dxdydz)));
+
+            if (master()) {
+                // add diagnostics to buffers
+                string header, line;
+                add_diagnostic("Iter", iter,            header, line);
+                add_diagnostic("Clock_time", comp_duration, header, line);
+                add_diagnostic("Time", time,            header, line);
+                add_diagnostic("Max_vel", max_vel,      header, line);
+                add_diagnostic("Max_temperature", max_temp, header, line);
+                add_diagnostic("Min_temperature", min_temp, header, line);
+                add_diagnostic("Max_salinity", max_salt,    header, line);
+                add_diagnostic("Min_salinity", min_salt,    header, line);
+                add_diagnostic("Max_density", max_rho,  header, line);
+                add_diagnostic("Min_density", min_rho,  header, line);
+                add_diagnostic("Mass", mass,            header, line);
+                add_diagnostic("PE_tot", pe_tot,        header, line);
+                if (compute_BPE) {
+                    add_diagnostic("BPE_tot", BPE_tot,  header, line);
+                }
+                if (compute_internal_to_BPE) {
+                    add_diagnostic("BPE_from_int", phi_i,   header, line);
+                }
+                if (compute_dissipation) {
+                    add_diagnostic("Max_diss", max_diss,    header, line);
+                    add_diagnostic("Diss_tot", diss_tot,    header, line);
+                }
+                if (Nx > 1) {
+                    add_diagnostic("Max_u", max_u,  header, line);
+                    add_diagnostic("KE_x", ke_x,    header, line);
+                }
+                if (Ny > 1 || rot_f != 0) {
+                    add_diagnostic("Max_v", max_v,  header, line);
+                    add_diagnostic("KE_y", ke_y,    header, line);
+                }
+                if (Nz > 1) {
+                    add_diagnostic("Max_w", max_w,  header, line);
+                    add_diagnostic("KE_z", ke_z,    header, line);
+                }
+                if (Ny > 1 && Nz > 1 && compute_enstrophy) {
+                    add_diagnostic("Enst_x_tot", enst_x_tot, header, line);
+                    add_diagnostic("Max_vort_x", max_vort_x, header, line);
+                }
+                if (Nx > 1 && Nz > 1 && compute_enstrophy) {
+                    add_diagnostic("Enst_y_tot", enst_y_tot, header, line);
+                    add_diagnostic("Max_vort_y", max_vort_y, header, line);
+                }
+                if (Nx > 1 && Ny > 1 && compute_enstrophy) {
+                    add_diagnostic("Enst_z_tot", enst_z_tot, header, line);
+                    add_diagnostic("Max_vort_z", max_vort_z, header, line);
+                }
+
+                // Write to file
+                if (!(restarting and iter==0))
+                    write_diagnostics(header, line, iter, restarting);
+                // and to the log file
+                fprintf(stdout,"[%d] (%.4g) %.4f: "
+                        "%.4g %.4g %.4g %.4g\n",
+                        iter,comp_duration,time,
+                        max_u,max_v,max_w,max_rho);
+            }
+
+            // Top Surface Stresses
+            if ( compute_stresses_top ) {
+                stresses_top(u, v, w, *Hprime, *temp1, gradient_op, grid_type, mu, time, iter, restarting);
+            }
+            // Bottom Surface Stresses
+            if ( compute_stresses_bottom ) {
+                stresses_bottom(u, v, w, *Hprime, *temp1, gradient_op, grid_type, mu, time, iter, restarting);
+            }
+
+            /* Write to disk if at correct time */
+            if ((time - next_plot) > -1e-6) {
+                plot_number++;
+                comp_duration = MPI_Wtime(); // time just before write (for dump)
+                // Write the arrays
+                write_array(u,"u",plot_number);
+                write_array(w,"w",plot_number);
+                if (Ny > 1 || rot_f != 0)
+                    write_array(v,"v",plot_number);
+                write_array(*tracers[TEMP],"t",plot_number);
+                write_array(*tracers[SALT],"s",plot_number);
+                if (write_pressure)
+                    write_array(pressure,"p",plot_number);
+                // update next plot time
+                next_plot = next_plot + plot_interval;
+
+                // Find average time to write (for dump)
+                clock_time = MPI_Wtime(); // time just after write
+                avg_write_time = (avg_write_time*(plot_number-restart_sequence-1) 
+                        + (clock_time - comp_duration))/(plot_number-restart_sequence);
+                // Print information about plot outputs
+                write_plot_times(time, clock_time, comp_duration, avg_write_time, plot_number, restarting);
+            }
+
+            // see if close to end of compute time and dump
+            check_and_dump(clock_time, real_start_time, compute_time, time, avg_write_time,
+                    plot_number, iter, u, v, w, tracers);
+            // Change dump log file if successfully reached final time
+            successful_dump(plot_number, final_time, plot_interval);
+            // increase counter
+            iter++;
+        }
+
+        // User specified variables to dump
+        void write_variables(DTArray & u,DTArray & v, DTArray & w,
+                vector<DTArray *> & tracers) {
+            write_array(u,"u.dump");
+            write_array(v,"v.dump");
+            write_array(w,"w.dump");
+            write_array(*tracers[TEMP],"t.dump");
+            write_array(*tracers[SALT],"s.dump");
+        }
+
+        // Constructor: Initialize local variables
+        userControl():
+            xx(split_range(Nx)), yy(Ny), zz(Nz),
+            topo(split_range(Nx)), gradient_op(0),
+            plot_number(restart_sequence),
+            next_plot(initial_time + plot_interval)
+    {   compute_quadweights(
+            size_x(),   size_y(),   size_z(),
+            length_x(), length_y(), length_z(),
+            type_x(),   type_y(),   type_z());
+        // Create one-dimensional arrays for the coordinates
+        automatic_grid(MinX, MinY, MinZ, &xx, &yy, &zz);
+    }
+};
+
+/* The ''main'' routine */
+int main(int argc, char ** argv) {
+    /* Initialize MPI.  This is required even for single-processor runs,
+       since the inner routines assume some degree of parallelization,
+       even if it is trivial. */
+    MPI_Init(&argc, &argv);
+
+    real_start_time = MPI_Wtime();     // start of simulation (for dump)
+    /* ------------------ Define parameters from spins.conf --------------------- */
+    options_init();
+
+    option_category("Grid Options");
+    add_option("Lx",&Lx,"Length of tank");
+    add_option("Ly",&Ly,1.0,"Width of tank");
+    add_option("Lz",&Lz,"Height of tank");
+    add_option("Nx",&Nx,"Number of points in X");
+    add_option("Ny",&Ny,1,"Number of points in Y");
+    add_option("Nz",&Nz,"Number of points in Z");
+    add_option("min_x",&MinX,0.0,"Minimum X-value");
+    add_option("min_y",&MinY,0.0,"Minimum Y-value");
+    add_option("min_z",&MinZ,0.0,"Minimum Z-value");
+
+    option_category("Grid expansion options");
+    string xgrid_type, ygrid_type, zgrid_type;
+    add_option("type_x",&xgrid_type,
+            "Grid type in X.  Valid values are:\n"
+            "   FOURIER: Periodic\n"
+            "   FREE_SLIP: Cosine expansion\n"
+            "   NO_SLIP: Chebyhsev expansion");
+    add_option("type_y",&ygrid_type,"FOURIER","Grid type in Y");
+    add_option("type_z",&zgrid_type,"Grid type in Z");
+    add_option("mapped_grid",&mapped,true,"Is the grid mapped?");
+
+    option_category("Topography parameters");
+    add_option("hill_height",&hill_height,"Height of hill");
+    add_option("hill_centre",&hill_centre,"location of hill peak");
+    add_option("hill_width",&hill_width,"Width of hill");
+
+    option_category("Physical parameters");
+    add_option("g",&g,9.81,"Gravitational acceleration");
+    add_option("rot_f",&rot_f,0.0,"Coriolis parameter");
+    add_option("rho_0",&rho_0,1000.0,"Reference density");
+    add_option("visco",&visco,"Viscosity");
+    add_option("kappa_T",&kappa_T,"Diffusivity of heat");
+    add_option("kappa_S",&kappa_S,"Diffusivity of salt");
+
+    option_category("Problem parameters");
+    add_option("delta_T",&delta_T,"Temperature difference");
+    add_option("delta_S",&delta_S,"Salinity difference");
+    add_option("T_0",&T_0,"Background temperature");
+    add_option("S_0",&S_0,"Background salinity");
+    add_option("delta_x",&delta_x,"Horizontal transition half-width");
+    add_option("Lmix",&Lmix,"Width of collapse region");
+
+    option_category("Temporal options");
+    add_option("final_time",&final_time,"Final time");
+    add_option("plot_interval",&plot_interval,"Time between writes");
+    add_option("dt_max",&dt_max,0.0,"Maximum time step. Zero value results in the default");
+
+    option_category("Restart options");
+    add_option("restart",&restarting,false,"Restart from prior output time.");
+    add_option("restart_time",&initial_time,0.0,"Time to restart from");
+    add_option("restart_sequence",&restart_sequence,-1,"Sequence number to restart from");
+
+    option_category("Dumping options");
+    add_option("restart_from_dump",&restart_from_dump,false,"If restart from dump");
+    add_option("compute_time",&compute_time,-1.0,"Time permitted for computation");
+
+    option_category("Other options");
+    add_option("perturb",&perturb,"Initial perturbation in velocity");
+    add_option("compute_enstrophy",&compute_enstrophy,true,"Calculate enstrophy?");
+    add_option("compute_dissipation",&compute_dissipation,true,"Calculate dissipation?");
+    add_option("compute_BPE",&compute_BPE,true,"Calculate BPE?");
+    add_option("compute_internal_to_BPE",&compute_internal_to_BPE,true,
+            "Calculate BPE gained from internal energy?");
+    add_option("compute_stresses_top",&compute_stresses_top,false,"Calculate top surfaces stresses?");
+    add_option("compute_stresses_bottom",&compute_stresses_bottom,false,"Calculate bottom surfaces stresses?");
+    add_option("write_pressure",&write_pressure,false,"Write the pressure field?");
+
+    option_category("Filter options");
+    add_option("f_cutoff",&f_cutoff,0.6,"Filter cut-off frequency");
+    add_option("f_order",&f_order,2.0,"Filter order");
+    add_option("f_strength",&f_strength,20.0,"Filter strength");
+
+    // Parse the options from the command line and config file
+    options_parse(argc,argv);
+
+    /* ------------------ Adjust and check parameters --------------------- */
+    /* Now, make sense of the options received.  Many of these
+     * can be directly used, but the ones of string-type need further procesing. */
+
+    // adjust temporal values when restarting from dump
+    if (restart_from_dump) {
+        adjust_for_dump(restarting, initial_time, restart_sequence,
+                final_time, compute_time, avg_write_time, Num_tracers, Nx, Ny, Nz);
+    }
+
+    // check restart sequence
+    check_restart_sequence(restarting, restart_sequence, initial_time, plot_interval);
+
+    // parse expansion types
+    parse_boundary_conditions(xgrid_type, ygrid_type, zgrid_type, intype_x, intype_y, intype_z);
+    // vector of expansion types
+    grid_type[0] = xgrid_type;
+    grid_type[1] = ygrid_type;
+    grid_type[2] = zgrid_type;
+
+    // adjust Ly for 2D
+    if (Ny==1 and Ly!=1.0) {
+        Ly = 1.0;
+        if (master())
+            fprintf(stdout,"Simulation is 2 dimensional, "
+                    "Ly has been changed to 1.0 for normalization.\n");
+    }
+
+    /* ------------------ Derived parameters --------------------- */
+
+    // Dynamic viscosity
+    mu = visco*rho_0;
+    // Maximum buoyancy frequency (squared) if the initial stratification was stable
+    //N2_max = g*delta_rho/(2*delta_x);
+    // Maximum time step
+    if (dt_max == 0.0) {
+        // if dt_max not given in spins.conf, use this
+        dt_max = 0.1;
+    }
+
+    /* ------------------ Initialize --------------------- */
+
+    // Create an instance of the above class
+    userControl mycode;
+    // Create a flow-evolver that takes its settings from the above class
+    FluidEvolve<userControl> do_stuff(&mycode);
+    // Initialize
+    do_stuff.initialize();
+
+    /* ------------------ Print some parameters --------------------- */
+
+    compute_start_time = MPI_Wtime(); // beginning of simulation (after reading in data)
+    double startup_time = compute_start_time - real_start_time;
+
+    if (master()) {
+        fprintf(stdout,"Dam break problem\n");
+        fprintf(stdout,"Using a %f x %f x %f grid of %d x %d x %d points\n",Lx,Ly,Lz,Nx,Ny,Nz);
+        fprintf(stdout,"g = %f, rot_f = %f, rho_0 = %f\n",g,rot_f,rho_0);
+        fprintf(stdout,"Time between plots: %g s\n",plot_interval);
+        fprintf(stdout,"Initial velocity perturbation: %g\n",perturb);
+        fprintf(stdout,"Filter cutoff = %f, order = %f, strength = %f\n",f_cutoff,f_order,f_strength);
+        fprintf(stdout,"Approx. max buoyancy frequency squared: %g\n",N2_max);
+        fprintf(stdout,"Max time step: %g\n",dt_max);
+        fprintf(stdout,"Start-up time: %.6g s.\n",startup_time);
+    }
+
+    /* ------------------ Run --------------------- */
+    // Run until the end of time
+    do_stuff.do_run(final_time);
+    MPI_Finalize();
+    return 0;
+}
diff --git a/src/cases/jmd_test/spins.conf b/src/cases/jmd_test/spins.conf
new file mode 100644
index 0000000000000000000000000000000000000000..fe5c278245ccd17011b34d7b7cd6cd4dbdcef1ff
--- /dev/null
+++ b/src/cases/jmd_test/spins.conf
@@ -0,0 +1,67 @@
+## Salt-temperature configuration file
+
+# Spatial Parameters
+Lx = 1.0
+Ly = 1.0
+Lz = 0.1
+Nx = 1024
+Ny = 1
+Nz = 128
+min_x = 0
+min_y = 0
+min_z = 0
+
+# Expansion types
+type_x = FREE_SLIP
+type_y = FREE_SLIP
+type_z = FREE_SLIP
+mapped_grid = false
+
+# Physical Parameters
+g = 9.81
+rot_f = 0.0e-3
+rho_0 = 1000.0
+visco = 2e-6
+kappa_T = 2e-6
+kappa_S = 1e-7
+
+# Problem Parameters (delta_rho is percentage)
+delta_T = 25.0
+delta_S = 5.0
+T_0 = 15.0
+S_0 = 0.0
+delta_x = 0.02
+Lmix = 0.1
+
+# Problem topography parameters
+hill_height = 0.1
+hill_centre = 0.5
+hill_width = 0.05
+
+# Temporal Parameters
+final_time = 100
+plot_interval = 1
+#dt_max = 0.0
+
+# Restart Options
+restart = false
+restart_time = 0.0
+restart_sequence = 0
+restart_from_dump = false
+compute_time = -1
+
+# Perturbation Parameter
+perturb = 1e-3
+
+# Filter Parameters
+f_cutoff = 0.6
+f_order = 2.0
+f_strength = 20.0
+
+# secondary diagnostics
+#compute_enstrophy = true
+#compute_dissipation = true
+compute_BPE = false
+compute_internal_to_BPE = false
+#compute_stresses_top = false
+#compute_stresses_bottom = false
diff --git a/src/cases/lineos_test/lineos_test.cpp b/src/cases/lineos_test/lineos_test.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..80d7075250928ade6e372f1362341d58995da3ea
--- /dev/null
+++ b/src/cases/lineos_test/lineos_test.cpp
@@ -0,0 +1,655 @@
+/* cases/salt_temp/salt_temp.cpp */
+/* Script for the formation of a gravity current with:
+ * zero initial velocity
+ * salt or temperature stratification (not density)
+ * and with or without topography */
+
+/* ------------------ Top matter --------------------- */
+
+// Required headers
+#include "../../BaseCase.hpp"      // Support file containing default implementations of several functions
+#include "../../Options.hpp"       // config-file parser
+#include <random/normal.h>         // Blitz random number generator
+
+using namespace ranlib;
+
+// Tensor variables for indexing
+blitz::firstIndex ii;
+blitz::secondIndex jj;
+blitz::thirdIndex kk;
+
+/* ------------------ Define parameters --------------------- */
+
+// Grid scales
+double Lx, Ly, Lz;              // Grid lengths (m)
+int    Nx, Ny, Nz;              // Number of points in x, y, z
+double MinX, MinY, MinZ;        // Minimum x/y/z points (m)
+// Mapped grid?
+bool mapped;
+// Grid types
+DIMTYPE intype_x, intype_y, intype_z;
+string grid_type[3];
+
+// Physical parameters
+double g, rot_f, rho_0;         // gravity accel (m/s^2), Coriolis frequency (s^-1), reference density (kg/m^3)
+double visco;                   // viscosity (m^2/s)
+double mu;                      // dynamic viscosity (kg/(m·s))
+double kappa_T;                 // diffusivity of temperature (m^2/s)
+double kappa_S;                 // diffusivity of salt (m^2/s)
+// helpful constants
+const int Num_tracers = 2;      // number of tracers (temperature, salt and dyes)
+const int TEMP = 0;             // index for temperature
+const int SALT = 1;             // index for salt
+
+// Problem parameters
+double delta_T;                 // temperature difference between different layers
+double delta_S;                 // salinity difference between different layers
+double T_0;                     // background temperature
+double S_0;                     // background salinity
+double delta_x;                 // horizontal transition length (m)
+double Lmix;                    // Width of mixed region (m)
+
+// Topography parameters
+double hill_height;             // height of hill (m)
+double hill_centre;             // position of hill peak (m)
+double hill_width;              // width of hill (m)
+
+// Temporal parameters
+double final_time;              // Final time (s)
+double plot_interval;           // Time between field writes (s)
+double dt_max;                  // maximum time step (s)
+
+// Restarting options
+bool restarting;                // are you restarting?
+double initial_time;            // initial start time of simulation
+int restart_sequence;           // output number to restart from
+
+// Dump parameters
+bool restart_from_dump;         // restarting from dump?
+double compute_time;            // requested computation time
+double avg_write_time;          // average time to write all output fields at one output
+double real_start_time;         // real (clock) time when simulation begins
+double compute_start_time;      // real (clock) time when computation begins (after initialization)
+
+// other options
+double perturb;                 // Initial velocity perturbation
+bool compute_enstrophy;         // Compute enstrophy?
+bool compute_dissipation;       // Compute dissipation?
+bool compute_BPE;               // Compute background potential energy?
+bool compute_internal_to_BPE;   // Compute BPE gained from internal energy?
+bool compute_stresses_top;      // Compute top surface stresses?
+bool compute_stresses_bottom;   // Compute bottom surface stresses?
+bool write_pressure;            // Write the pressure field?
+int iter = 0;                   // Iteration counter
+
+// Maximum squared buoyancy frequency
+double N2_max;
+
+/* ------------------ Adjust the class --------------------- */
+
+class userControl : public BaseCase {
+    public:
+        // Grid and topography arrays
+        DTArray *zgrid;                 // Full grid fields
+        Array<double,1> xx, yy, zz;     // 1D grid vectors
+        Array<double,1> topo;           // topography vector
+        DTArray *Hprime;                // derivative of topography vector
+
+        // Arrays and operators for derivatives
+        Grad * gradient_op;
+        DTArray *temp1, *dxdydz;
+
+        // Timing variables (for outputs and measuring time steps)
+        int plot_number;        // plot output number
+        double next_plot;       // time of next output write
+        double comp_duration;   // clock time since computation began
+        double clock_time;      // current clock time
+
+        // Size of domain
+        double length_x() const { return Lx; }
+        double length_y() const { return Ly; }
+        double length_z() const { return Lz; }
+
+        // Resolution in x, y, and z
+        int size_x() const { return Nx; }
+        int size_y() const { return Ny; }
+        int size_z() const { return Nz; }
+
+        // Set expansions (FREE_SLIP, NO_SLIP (in vertical) or PERIODIC)
+        DIMTYPE type_x() const { return intype_x; }
+        DIMTYPE type_y() const { return intype_y; }
+        DIMTYPE type_z() const { return intype_z; }
+
+        // Record the gradient-taking object
+        void set_grad(Grad * in_grad) { gradient_op = in_grad; }
+
+        // Coriolis parameter, viscosity, and diffusivities
+        double get_rot_f() const { return rot_f; }
+        double get_visco() const { return visco; }
+        double get_diffusivity(int t_num) const {
+            switch (t_num) {
+                case TEMP:
+                    return kappa_T;
+                case SALT:
+                    return kappa_S;
+                default:
+                    if (master()) fprintf(stderr,"Invalid tracer number!\n");
+                    MPI_Finalize(); exit(1);
+            }
+        }
+
+        // Temporal parameters
+        double init_time() const { return initial_time; }
+        int get_restart_sequence() const { return restart_sequence; }
+        double get_dt_max() const { return dt_max; }
+        double get_next_plot() { return next_plot; }
+
+        // Number of tracers (the first is density)
+        int numtracers() const { return Num_tracers; }
+
+        // Create mapped grid
+        bool is_mapped() const { return mapped; }
+        void do_mapping(DTArray & xg, DTArray & yg, DTArray & zg) {
+            zgrid = alloc_array(Nx,Ny,Nz);
+
+            // over-write zz to be between -1 and 1
+            // (zz defined in automatic_grid below)
+            zz = -cos(ii*M_PI/(Nz-1));      // Chebyshev in vertical
+
+            // Define topography
+            topo = hill_height*exp(-pow((xx(ii)-hill_centre)/hill_width,2));
+
+            // make full grids
+            xg = xx(ii) + 0*jj + 0*kk;
+            yg = yy(jj) + 0*ii + 0*kk;
+            zg = MinZ + 0.5*Lz*(1+zz(kk)) + 0.5*(1-zz(kk))*topo(ii);
+            *zgrid = zg;
+
+            // Write the arrays and matlab readers
+            write_array(xg,"xgrid");
+            write_reader(xg,"xgrid",false);
+            if (Ny > 1) {
+                write_array(yg,"ygrid");
+                write_reader(yg,"ygrid",false);
+            }
+            write_array(zg,"zgrid");
+            write_reader(zg,"zgrid",false);
+        }
+
+        /* Initialize velocities */
+        void init_vels(DTArray & u, DTArray & v, DTArray & w) {
+            if (master()) fprintf(stdout,"Initializing velocities\n");
+            // if restarting
+            if (restarting and !restart_from_dump) {
+                init_vels_restart(u, v, w);
+            } else if (restarting and restart_from_dump) {
+                init_vels_dump(u, v, w);
+            } else{
+                // else have a near motionless field
+                u = 0;
+                v = 0;
+                w = 0;
+                // Add a random perturbation to trigger any 3D instabilities
+                int myrank;
+                MPI_Comm_rank(MPI_COMM_WORLD,&myrank);
+                Normal<double> rnd(0,1);
+                for (int i = u.lbound(firstDim); i <= u.ubound(firstDim); i++) {
+                    rnd.seed(i);
+                    for (int j = u.lbound(secondDim); j <= u.ubound(secondDim); j++) {
+                        for (int k = u.lbound(thirdDim); k <= u.ubound(thirdDim); k++) {
+                            u(i,j,k) += perturb*rnd.random();
+                            w(i,j,k) += perturb*rnd.random();
+                            if (Ny > 1 || rot_f != 0)
+                                v(i,j,k) += perturb*rnd.random();
+                        }
+                    }
+                }
+                // Write the arrays
+                write_array(u,"u",plot_number);
+                write_array(w,"w",plot_number);
+                if (Ny > 1 || rot_f != 0) {
+                    write_array(v,"v",plot_number);
+                }
+            }
+        }
+
+        /* Initialize the tracers (density and dyes) */
+        void init_tracers(vector<DTArray *> & tracers) {
+            if (master()) fprintf(stdout,"Initializing tracers\n");
+            DTArray & temp = *tracers[TEMP];
+            DTArray & salt = *tracers[SALT];
+            
+            if (restarting and !restart_from_dump) {
+                init_tracer_restart("t",temp);
+                init_tracer_restart("s",salt);
+            } else if (restarting and restart_from_dump) {
+                init_tracer_dump("t",temp);
+                init_tracer_dump("s",salt);
+            } else {
+                // temperature configuration
+                temp = T_0 + delta_T*0.5*(1.0-tanh((xx(ii)-Lmix)/delta_x)) + 0*jj + 0*kk;
+                // salt configuration
+                salt = S_0 + delta_S*0.5*(1.0-tanh((xx(ii)-Lmix)/delta_x)) + 0*jj + 0*kk;
+                // Important! if mapped, and t or s depends on z
+                // then (*zgrid)(ii,jj,kk), must be used instead of zz(kk)
+                // Write the array
+                write_array(temp,"t",plot_number);
+                write_array(salt,"s",plot_number);
+            }
+        }
+
+        /* Forcing in the momentum equations */
+        void forcing(double t, const DTArray & u, DTArray & u_f,
+                const DTArray & v, DTArray & v_f, const DTArray & w, DTArray & w_f,
+                vector<DTArray *> & tracers, vector<DTArray *> & tracers_f) {
+            u_f = +rot_f*v;
+            v_f = -rot_f*u;
+
+            //Do Eos stuff 
+            lineos(*temp1,*tracers[TEMP],*tracers[SALT],T_0,S_0);
+            rho_0 = compute_rho0(T_0,S_0);
+            
+            w_f = -g/rho_0*(*temp1-rho_0);
+            *tracers_f[TEMP] = 0;
+            *tracers_f[SALT] = 0;
+        }
+
+        /* Basic analysis: compute secondary variables, and save fields and diagnostics */
+        void analysis(double time, DTArray & u, DTArray & v, DTArray & w,
+                vector<DTArray *> & tracers, DTArray & pressure) {
+            // Set-up
+            if ( iter == 0 ) {
+                if ( compute_enstrophy or compute_dissipation or
+                        compute_stresses_top or compute_stresses_bottom ) {
+                    temp1 = alloc_array(Nx,Ny,Nz);
+                }
+                if ( compute_stresses_top or compute_stresses_bottom ) {
+                    // initialize the vector of the bottom slope (Hprime)
+                    Hprime = alloc_array(Nx,Ny,1);
+                    if (is_mapped()) {
+                        bottom_slope(*Hprime, *zgrid, *temp1, gradient_op, grid_type, Nx, Ny, Nz);
+                    } else {
+                        topo = 0*ii;
+                        *Hprime = 0*ii + 0*jj;
+                    }
+                }
+                // Determine last plot if restarting from the dump file
+                if (restart_from_dump) {
+                    next_plot = (restart_sequence+1)*plot_interval;
+                }
+                // initialize the size of each voxel
+                dxdydz = alloc_array(Nx,Ny,Nz);
+                *dxdydz = (*get_quad_x())(ii)*(*get_quad_y())(jj)*(*get_quad_z())(kk);
+                if (is_mapped()) {
+                    *dxdydz = (*dxdydz)*(Lz-topo(ii))/Lz;
+                }
+            }
+            // update clocks
+            if (master()) {
+                clock_time = MPI_Wtime();
+                comp_duration = clock_time - compute_start_time;
+            }
+
+            /* Calculate and write out useful information */
+
+            // Energy (PE assumes density is density anomaly)
+            double ke_x = 0, ke_y = 0, ke_z = 0;
+            if ( Nx > 1 ) {
+                ke_x = pssum(sum(0.5*rho_0*(u*u)*(*dxdydz)));
+            }
+            if (Ny > 1 || rot_f != 0) {
+                ke_y = pssum(sum(0.5*rho_0*(v*v)*(*dxdydz)));
+            }
+            if ( Nz > 1 ) {
+                ke_z = pssum(sum(0.5*rho_0*(w*w)*(*dxdydz)));
+            }
+            double pe_tot;
+            if (is_mapped()) {
+                pe_tot = pssum(sum(eqn_of_state(*tracers[TEMP],*tracers[SALT])*g*((*zgrid)(ii,jj,kk) - MinZ)*(*dxdydz)));
+            } else {
+                pe_tot = pssum(sum(eqn_of_state(*tracers[TEMP],*tracers[SALT])*g*(zz(kk) - MinZ)*(*dxdydz)));
+            }
+            double BPE_tot = 0;
+            if (compute_BPE) {
+                // full energy budget for salt/heat stratifcations is incomplete
+                //compute_Background_PE(BPE_tot, eqn_of_state(*tracers[TEMP],*tracers[SALT]), *dxdydz, Nx, Ny, Nz, Lx, Ly, Lz,
+                //        g, rho_0, iter, true, is_mapped(), topo);
+            }
+            // Conversion from internal energy to background potential energy
+            double phi_i = 0;
+            if (compute_internal_to_BPE) {
+                // this is not finished for temperature/salt stratified cases
+                //compute_BPE_from_internal(phi_i, *tracers[RHO], kappa_rho, rho_0, g, Nz);
+            }
+            // viscous dissipation
+            double diss_tot = 0;
+            double max_diss = 0;
+            if (compute_dissipation) {
+                dissipation(*temp1, u, v, w, gradient_op, grid_type, Nx, Ny, Nz, mu);
+                max_diss = psmax(max(*temp1));
+                diss_tot = pssum(sum((*temp1)*(*dxdydz)));
+            }
+            // Vorticity / Enstrophy
+            double max_vort_x = 0, enst_x_tot = 0;
+            double max_vort_y = 0, enst_y_tot = 0;
+            double max_vort_z = 0, enst_z_tot = 0;
+            if (compute_enstrophy) {
+                // x-vorticity
+                if (Ny > 1 and Nz > 1) {
+                    compute_vort_x(*temp1, v, w, gradient_op, grid_type);
+                    max_vort_x = psmax(max(abs(*temp1)));
+                    enst_x_tot = pssum(sum(0.5*pow(*temp1,2)*(*dxdydz)));
+                }
+                // y-vorticity
+                if (Nx > 1 and Nz > 1) {
+                    compute_vort_y(*temp1, u, w, gradient_op, grid_type);
+                    max_vort_y = psmax(max(abs(*temp1)));
+                    enst_y_tot = pssum(sum(0.5*pow(*temp1,2)*(*dxdydz)));
+                }
+                // z-vorticity
+                if (Nx > 1 and Ny > 1) {
+                    compute_vort_z(*temp1, u, v, gradient_op, grid_type);
+                    max_vort_z = psmax(max(abs(*temp1)));
+                    enst_z_tot = pssum(sum(0.5*pow(*temp1,2)*(*dxdydz)));
+                }
+            }
+            // max of fields
+            double max_u = psmax(max(abs(u)));
+            double max_v = psmax(max(abs(v)));
+            double max_w = psmax(max(abs(w)));
+            double max_vel = psmax(max(sqrt(u*u + v*v + w*w)));
+            double max_temp = psmax(max(*tracers[TEMP]));
+            double min_temp = psmin(min(*tracers[TEMP]));
+            double max_salt = psmax(max(*tracers[SALT]));
+            double min_salt = psmin(min(*tracers[SALT]));
+            double max_rho =  psmax(max(eqn_of_state(*tracers[TEMP],*tracers[SALT])));
+            double min_rho =  psmin(min(eqn_of_state(*tracers[TEMP],*tracers[SALT])));
+            // total mass (tracers[RHO] is non-dimensional density)
+            double mass = pssum(sum(eqn_of_state(*tracers[TEMP],*tracers[SALT])*(*dxdydz)));
+
+            if (master()) {
+                // add diagnostics to buffers
+                string header, line;
+                add_diagnostic("Iter", iter,            header, line);
+                add_diagnostic("Clock_time", comp_duration, header, line);
+                add_diagnostic("Time", time,            header, line);
+                add_diagnostic("Max_vel", max_vel,      header, line);
+                add_diagnostic("Max_temperature", max_temp, header, line);
+                add_diagnostic("Min_temperature", min_temp, header, line);
+                add_diagnostic("Max_salinity", max_salt,    header, line);
+                add_diagnostic("Min_salinity", min_salt,    header, line);
+                add_diagnostic("Max_density", max_rho,  header, line);
+                add_diagnostic("Min_density", min_rho,  header, line);
+                add_diagnostic("Mass", mass,            header, line);
+                add_diagnostic("PE_tot", pe_tot,        header, line);
+                if (compute_BPE) {
+                    add_diagnostic("BPE_tot", BPE_tot,  header, line);
+                }
+                if (compute_internal_to_BPE) {
+                    add_diagnostic("BPE_from_int", phi_i,   header, line);
+                }
+                if (compute_dissipation) {
+                    add_diagnostic("Max_diss", max_diss,    header, line);
+                    add_diagnostic("Diss_tot", diss_tot,    header, line);
+                }
+                if (Nx > 1) {
+                    add_diagnostic("Max_u", max_u,  header, line);
+                    add_diagnostic("KE_x", ke_x,    header, line);
+                }
+                if (Ny > 1 || rot_f != 0) {
+                    add_diagnostic("Max_v", max_v,  header, line);
+                    add_diagnostic("KE_y", ke_y,    header, line);
+                }
+                if (Nz > 1) {
+                    add_diagnostic("Max_w", max_w,  header, line);
+                    add_diagnostic("KE_z", ke_z,    header, line);
+                }
+                if (Ny > 1 && Nz > 1 && compute_enstrophy) {
+                    add_diagnostic("Enst_x_tot", enst_x_tot, header, line);
+                    add_diagnostic("Max_vort_x", max_vort_x, header, line);
+                }
+                if (Nx > 1 && Nz > 1 && compute_enstrophy) {
+                    add_diagnostic("Enst_y_tot", enst_y_tot, header, line);
+                    add_diagnostic("Max_vort_y", max_vort_y, header, line);
+                }
+                if (Nx > 1 && Ny > 1 && compute_enstrophy) {
+                    add_diagnostic("Enst_z_tot", enst_z_tot, header, line);
+                    add_diagnostic("Max_vort_z", max_vort_z, header, line);
+                }
+
+                // Write to file
+                if (!(restarting and iter==0))
+                    write_diagnostics(header, line, iter, restarting);
+                // and to the log file
+                fprintf(stdout,"[%d] (%.4g) %.4f: "
+                        "%.4g %.4g %.4g %.4g\n",
+                        iter,comp_duration,time,
+                        max_u,max_v,max_w,max_rho);
+            }
+
+            // Top Surface Stresses
+            if ( compute_stresses_top ) {
+                stresses_top(u, v, w, *Hprime, *temp1, gradient_op, grid_type, mu, time, iter, restarting);
+            }
+            // Bottom Surface Stresses
+            if ( compute_stresses_bottom ) {
+                stresses_bottom(u, v, w, *Hprime, *temp1, gradient_op, grid_type, mu, time, iter, restarting);
+            }
+
+            /* Write to disk if at correct time */
+            if ((time - next_plot) > -1e-6) {
+                plot_number++;
+                comp_duration = MPI_Wtime(); // time just before write (for dump)
+                // Write the arrays
+                write_array(u,"u",plot_number);
+                write_array(w,"w",plot_number);
+                if (Ny > 1 || rot_f != 0)
+                    write_array(v,"v",plot_number);
+                write_array(*tracers[TEMP],"t",plot_number);
+                write_array(*tracers[SALT],"s",plot_number);
+                if (write_pressure)
+                    write_array(pressure,"p",plot_number);
+                // update next plot time
+                next_plot = next_plot + plot_interval;
+
+                // Find average time to write (for dump)
+                clock_time = MPI_Wtime(); // time just after write
+                avg_write_time = (avg_write_time*(plot_number-restart_sequence-1) 
+                        + (clock_time - comp_duration))/(plot_number-restart_sequence);
+                // Print information about plot outputs
+                write_plot_times(time, clock_time, comp_duration, avg_write_time, plot_number, restarting);
+            }
+
+            // see if close to end of compute time and dump
+            check_and_dump(clock_time, real_start_time, compute_time, time, avg_write_time,
+                    plot_number, iter, u, v, w, tracers);
+            // Change dump log file if successfully reached final time
+            successful_dump(plot_number, final_time, plot_interval);
+            // increase counter
+            iter++;
+        }
+
+        // User specified variables to dump
+        void write_variables(DTArray & u,DTArray & v, DTArray & w,
+                vector<DTArray *> & tracers) {
+            write_array(u,"u.dump");
+            write_array(v,"v.dump");
+            write_array(w,"w.dump");
+            write_array(*tracers[TEMP],"t.dump");
+            write_array(*tracers[SALT],"s.dump");
+        }
+
+        // Constructor: Initialize local variables
+        userControl():
+            xx(split_range(Nx)), yy(Ny), zz(Nz),
+            topo(split_range(Nx)), gradient_op(0),
+            plot_number(restart_sequence),
+            next_plot(initial_time + plot_interval)
+    {   compute_quadweights(
+            size_x(),   size_y(),   size_z(),
+            length_x(), length_y(), length_z(),
+            type_x(),   type_y(),   type_z());
+        // Create one-dimensional arrays for the coordinates
+        automatic_grid(MinX, MinY, MinZ, &xx, &yy, &zz);
+    }
+};
+
+/* The ''main'' routine */
+int main(int argc, char ** argv) {
+    /* Initialize MPI.  This is required even for single-processor runs,
+       since the inner routines assume some degree of parallelization,
+       even if it is trivial. */
+    MPI_Init(&argc, &argv);
+
+    real_start_time = MPI_Wtime();     // start of simulation (for dump)
+    /* ------------------ Define parameters from spins.conf --------------------- */
+    options_init();
+
+    option_category("Grid Options");
+    add_option("Lx",&Lx,"Length of tank");
+    add_option("Ly",&Ly,1.0,"Width of tank");
+    add_option("Lz",&Lz,"Height of tank");
+    add_option("Nx",&Nx,"Number of points in X");
+    add_option("Ny",&Ny,1,"Number of points in Y");
+    add_option("Nz",&Nz,"Number of points in Z");
+    add_option("min_x",&MinX,0.0,"Minimum X-value");
+    add_option("min_y",&MinY,0.0,"Minimum Y-value");
+    add_option("min_z",&MinZ,0.0,"Minimum Z-value");
+
+    option_category("Grid expansion options");
+    string xgrid_type, ygrid_type, zgrid_type;
+    add_option("type_x",&xgrid_type,
+            "Grid type in X.  Valid values are:\n"
+            "   FOURIER: Periodic\n"
+            "   FREE_SLIP: Cosine expansion\n"
+            "   NO_SLIP: Chebyhsev expansion");
+    add_option("type_y",&ygrid_type,"FOURIER","Grid type in Y");
+    add_option("type_z",&zgrid_type,"Grid type in Z");
+    add_option("mapped_grid",&mapped,true,"Is the grid mapped?");
+
+    option_category("Topography parameters");
+    add_option("hill_height",&hill_height,"Height of hill");
+    add_option("hill_centre",&hill_centre,"location of hill peak");
+    add_option("hill_width",&hill_width,"Width of hill");
+
+    option_category("Physical parameters");
+    add_option("g",&g,9.81,"Gravitational acceleration");
+    add_option("rot_f",&rot_f,0.0,"Coriolis parameter");
+    add_option("rho_0",&rho_0,1000.0,"Reference density");
+    add_option("visco",&visco,"Viscosity");
+    add_option("kappa_T",&kappa_T,"Diffusivity of heat");
+    add_option("kappa_S",&kappa_S,"Diffusivity of salt");
+
+    option_category("Problem parameters");
+    add_option("delta_T",&delta_T,"Temperature difference");
+    add_option("delta_S",&delta_S,"Salinity difference");
+    add_option("T_0",&T_0,"Background temperature");
+    add_option("S_0",&S_0,"Background salinity");
+    add_option("delta_x",&delta_x,"Horizontal transition half-width");
+    add_option("Lmix",&Lmix,"Width of collapse region");
+
+    option_category("Temporal options");
+    add_option("final_time",&final_time,"Final time");
+    add_option("plot_interval",&plot_interval,"Time between writes");
+    add_option("dt_max",&dt_max,0.0,"Maximum time step. Zero value results in the default");
+
+    option_category("Restart options");
+    add_option("restart",&restarting,false,"Restart from prior output time.");
+    add_option("restart_time",&initial_time,0.0,"Time to restart from");
+    add_option("restart_sequence",&restart_sequence,-1,"Sequence number to restart from");
+
+    option_category("Dumping options");
+    add_option("restart_from_dump",&restart_from_dump,false,"If restart from dump");
+    add_option("compute_time",&compute_time,-1.0,"Time permitted for computation");
+
+    option_category("Other options");
+    add_option("perturb",&perturb,"Initial perturbation in velocity");
+    add_option("compute_enstrophy",&compute_enstrophy,true,"Calculate enstrophy?");
+    add_option("compute_dissipation",&compute_dissipation,true,"Calculate dissipation?");
+    add_option("compute_BPE",&compute_BPE,true,"Calculate BPE?");
+    add_option("compute_internal_to_BPE",&compute_internal_to_BPE,true,
+            "Calculate BPE gained from internal energy?");
+    add_option("compute_stresses_top",&compute_stresses_top,false,"Calculate top surfaces stresses?");
+    add_option("compute_stresses_bottom",&compute_stresses_bottom,false,"Calculate bottom surfaces stresses?");
+    add_option("write_pressure",&write_pressure,false,"Write the pressure field?");
+
+    option_category("Filter options");
+    add_option("f_cutoff",&f_cutoff,0.6,"Filter cut-off frequency");
+    add_option("f_order",&f_order,2.0,"Filter order");
+    add_option("f_strength",&f_strength,20.0,"Filter strength");
+
+    // Parse the options from the command line and config file
+    options_parse(argc,argv);
+
+    /* ------------------ Adjust and check parameters --------------------- */
+    /* Now, make sense of the options received.  Many of these
+     * can be directly used, but the ones of string-type need further procesing. */
+
+    // adjust temporal values when restarting from dump
+    if (restart_from_dump) {
+        adjust_for_dump(restarting, initial_time, restart_sequence,
+                final_time, compute_time, avg_write_time, Num_tracers, Nx, Ny, Nz);
+    }
+
+    // check restart sequence
+    check_restart_sequence(restarting, restart_sequence, initial_time, plot_interval);
+
+    // parse expansion types
+    parse_boundary_conditions(xgrid_type, ygrid_type, zgrid_type, intype_x, intype_y, intype_z);
+    // vector of expansion types
+    grid_type[0] = xgrid_type;
+    grid_type[1] = ygrid_type;
+    grid_type[2] = zgrid_type;
+
+    // adjust Ly for 2D
+    if (Ny==1 and Ly!=1.0) {
+        Ly = 1.0;
+        if (master())
+            fprintf(stdout,"Simulation is 2 dimensional, "
+                    "Ly has been changed to 1.0 for normalization.\n");
+    }
+
+    /* ------------------ Derived parameters --------------------- */
+
+    // Dynamic viscosity
+    mu = visco*rho_0;
+    // Maximum buoyancy frequency (squared) if the initial stratification was stable
+    //N2_max = g*delta_rho/(2*delta_x);
+    // Maximum time step
+    if (dt_max == 0.0) {
+        // if dt_max not given in spins.conf, use this
+        dt_max = 0.1;
+    }
+
+    /* ------------------ Initialize --------------------- */
+
+    // Create an instance of the above class
+    userControl mycode;
+    // Create a flow-evolver that takes its settings from the above class
+    FluidEvolve<userControl> do_stuff(&mycode);
+    // Initialize
+    do_stuff.initialize();
+
+    /* ------------------ Print some parameters --------------------- */
+
+    compute_start_time = MPI_Wtime(); // beginning of simulation (after reading in data)
+    double startup_time = compute_start_time - real_start_time;
+
+    if (master()) {
+        fprintf(stdout,"Dam break problem\n");
+        fprintf(stdout,"Using a %f x %f x %f grid of %d x %d x %d points\n",Lx,Ly,Lz,Nx,Ny,Nz);
+        fprintf(stdout,"g = %f, rot_f = %f, rho_0 = %f\n",g,rot_f,rho_0);
+        fprintf(stdout,"Time between plots: %g s\n",plot_interval);
+        fprintf(stdout,"Initial velocity perturbation: %g\n",perturb);
+        fprintf(stdout,"Filter cutoff = %f, order = %f, strength = %f\n",f_cutoff,f_order,f_strength);
+        fprintf(stdout,"Approx. max buoyancy frequency squared: %g\n",N2_max);
+        fprintf(stdout,"Max time step: %g\n",dt_max);
+        fprintf(stdout,"Start-up time: %.6g s.\n",startup_time);
+    }
+
+    /* ------------------ Run --------------------- */
+    // Run until the end of time
+    do_stuff.do_run(final_time);
+    MPI_Finalize();
+    return 0;
+}
diff --git a/src/cases/lineos_test/spins.conf b/src/cases/lineos_test/spins.conf
new file mode 100644
index 0000000000000000000000000000000000000000..fe5c278245ccd17011b34d7b7cd6cd4dbdcef1ff
--- /dev/null
+++ b/src/cases/lineos_test/spins.conf
@@ -0,0 +1,67 @@
+## Salt-temperature configuration file
+
+# Spatial Parameters
+Lx = 1.0
+Ly = 1.0
+Lz = 0.1
+Nx = 1024
+Ny = 1
+Nz = 128
+min_x = 0
+min_y = 0
+min_z = 0
+
+# Expansion types
+type_x = FREE_SLIP
+type_y = FREE_SLIP
+type_z = FREE_SLIP
+mapped_grid = false
+
+# Physical Parameters
+g = 9.81
+rot_f = 0.0e-3
+rho_0 = 1000.0
+visco = 2e-6
+kappa_T = 2e-6
+kappa_S = 1e-7
+
+# Problem Parameters (delta_rho is percentage)
+delta_T = 25.0
+delta_S = 5.0
+T_0 = 15.0
+S_0 = 0.0
+delta_x = 0.02
+Lmix = 0.1
+
+# Problem topography parameters
+hill_height = 0.1
+hill_centre = 0.5
+hill_width = 0.05
+
+# Temporal Parameters
+final_time = 100
+plot_interval = 1
+#dt_max = 0.0
+
+# Restart Options
+restart = false
+restart_time = 0.0
+restart_sequence = 0
+restart_from_dump = false
+compute_time = -1
+
+# Perturbation Parameter
+perturb = 1e-3
+
+# Filter Parameters
+f_cutoff = 0.6
+f_order = 2.0
+f_strength = 20.0
+
+# secondary diagnostics
+#compute_enstrophy = true
+#compute_dissipation = true
+compute_BPE = false
+compute_internal_to_BPE = false
+#compute_stresses_top = false
+#compute_stresses_bottom = false
diff --git a/src/cases/nleos_test/nleos_test.cpp b/src/cases/nleos_test/nleos_test.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6bea267ca530f3bd3d07ede918adeeb533714faf
--- /dev/null
+++ b/src/cases/nleos_test/nleos_test.cpp
@@ -0,0 +1,655 @@
+/* cases/salt_temp/salt_temp.cpp */
+/* Script for the formation of a gravity current with:
+ * zero initial velocity
+ * salt or temperature stratification (not density)
+ * and with or without topography */
+
+/* ------------------ Top matter --------------------- */
+
+// Required headers
+#include "../../BaseCase.hpp"      // Support file containing default implementations of several functions
+#include "../../Options.hpp"       // config-file parser
+#include <random/normal.h>         // Blitz random number generator
+
+using namespace ranlib;
+
+// Tensor variables for indexing
+blitz::firstIndex ii;
+blitz::secondIndex jj;
+blitz::thirdIndex kk;
+
+/* ------------------ Define parameters --------------------- */
+
+// Grid scales
+double Lx, Ly, Lz;              // Grid lengths (m)
+int    Nx, Ny, Nz;              // Number of points in x, y, z
+double MinX, MinY, MinZ;        // Minimum x/y/z points (m)
+// Mapped grid?
+bool mapped;
+// Grid types
+DIMTYPE intype_x, intype_y, intype_z;
+string grid_type[3];
+
+// Physical parameters
+double g, rot_f, rho_0;         // gravity accel (m/s^2), Coriolis frequency (s^-1), reference density (kg/m^3)
+double visco;                   // viscosity (m^2/s)
+double mu;                      // dynamic viscosity (kg/(m·s))
+double kappa_T;                 // diffusivity of temperature (m^2/s)
+double kappa_S;                 // diffusivity of salt (m^2/s)
+// helpful constants
+const int Num_tracers = 2;      // number of tracers (temperature, salt and dyes)
+const int TEMP = 0;             // index for temperature
+const int SALT = 1;             // index for salt
+
+// Problem parameters
+double delta_T;                 // temperature difference between different layers
+double delta_S;                 // salinity difference between different layers
+double T_0;                     // background temperature
+double S_0;                     // background salinity
+double delta_x;                 // horizontal transition length (m)
+double Lmix;                    // Width of mixed region (m)
+
+// Topography parameters
+double hill_height;             // height of hill (m)
+double hill_centre;             // position of hill peak (m)
+double hill_width;              // width of hill (m)
+
+// Temporal parameters
+double final_time;              // Final time (s)
+double plot_interval;           // Time between field writes (s)
+double dt_max;                  // maximum time step (s)
+
+// Restarting options
+bool restarting;                // are you restarting?
+double initial_time;            // initial start time of simulation
+int restart_sequence;           // output number to restart from
+
+// Dump parameters
+bool restart_from_dump;         // restarting from dump?
+double compute_time;            // requested computation time
+double avg_write_time;          // average time to write all output fields at one output
+double real_start_time;         // real (clock) time when simulation begins
+double compute_start_time;      // real (clock) time when computation begins (after initialization)
+
+// other options
+double perturb;                 // Initial velocity perturbation
+bool compute_enstrophy;         // Compute enstrophy?
+bool compute_dissipation;       // Compute dissipation?
+bool compute_BPE;               // Compute background potential energy?
+bool compute_internal_to_BPE;   // Compute BPE gained from internal energy?
+bool compute_stresses_top;      // Compute top surface stresses?
+bool compute_stresses_bottom;   // Compute bottom surface stresses?
+bool write_pressure;            // Write the pressure field?
+int iter = 0;                   // Iteration counter
+
+// Maximum squared buoyancy frequency
+double N2_max;
+
+/* ------------------ Adjust the class --------------------- */
+
+class userControl : public BaseCase {
+    public:
+        // Grid and topography arrays
+        DTArray *zgrid;                 // Full grid fields
+        Array<double,1> xx, yy, zz;     // 1D grid vectors
+        Array<double,1> topo;           // topography vector
+        DTArray *Hprime;                // derivative of topography vector
+
+        // Arrays and operators for derivatives
+        Grad * gradient_op;
+        DTArray *temp1, *dxdydz;
+
+        // Timing variables (for outputs and measuring time steps)
+        int plot_number;        // plot output number
+        double next_plot;       // time of next output write
+        double comp_duration;   // clock time since computation began
+        double clock_time;      // current clock time
+
+        // Size of domain
+        double length_x() const { return Lx; }
+        double length_y() const { return Ly; }
+        double length_z() const { return Lz; }
+
+        // Resolution in x, y, and z
+        int size_x() const { return Nx; }
+        int size_y() const { return Ny; }
+        int size_z() const { return Nz; }
+
+        // Set expansions (FREE_SLIP, NO_SLIP (in vertical) or PERIODIC)
+        DIMTYPE type_x() const { return intype_x; }
+        DIMTYPE type_y() const { return intype_y; }
+        DIMTYPE type_z() const { return intype_z; }
+
+        // Record the gradient-taking object
+        void set_grad(Grad * in_grad) { gradient_op = in_grad; }
+
+        // Coriolis parameter, viscosity, and diffusivities
+        double get_rot_f() const { return rot_f; }
+        double get_visco() const { return visco; }
+        double get_diffusivity(int t_num) const {
+            switch (t_num) {
+                case TEMP:
+                    return kappa_T;
+                case SALT:
+                    return kappa_S;
+                default:
+                    if (master()) fprintf(stderr,"Invalid tracer number!\n");
+                    MPI_Finalize(); exit(1);
+            }
+        }
+
+        // Temporal parameters
+        double init_time() const { return initial_time; }
+        int get_restart_sequence() const { return restart_sequence; }
+        double get_dt_max() const { return dt_max; }
+        double get_next_plot() { return next_plot; }
+
+        // Number of tracers (the first is density)
+        int numtracers() const { return Num_tracers; }
+
+        // Create mapped grid
+        bool is_mapped() const { return mapped; }
+        void do_mapping(DTArray & xg, DTArray & yg, DTArray & zg) {
+            zgrid = alloc_array(Nx,Ny,Nz);
+
+            // over-write zz to be between -1 and 1
+            // (zz defined in automatic_grid below)
+            zz = -cos(ii*M_PI/(Nz-1));      // Chebyshev in vertical
+
+            // Define topography
+            topo = hill_height*exp(-pow((xx(ii)-hill_centre)/hill_width,2));
+
+            // make full grids
+            xg = xx(ii) + 0*jj + 0*kk;
+            yg = yy(jj) + 0*ii + 0*kk;
+            zg = MinZ + 0.5*Lz*(1+zz(kk)) + 0.5*(1-zz(kk))*topo(ii);
+            *zgrid = zg;
+
+            // Write the arrays and matlab readers
+            write_array(xg,"xgrid");
+            write_reader(xg,"xgrid",false);
+            if (Ny > 1) {
+                write_array(yg,"ygrid");
+                write_reader(yg,"ygrid",false);
+            }
+            write_array(zg,"zgrid");
+            write_reader(zg,"zgrid",false);
+        }
+
+        /* Initialize velocities */
+        void init_vels(DTArray & u, DTArray & v, DTArray & w) {
+            if (master()) fprintf(stdout,"Initializing velocities\n");
+            // if restarting
+            if (restarting and !restart_from_dump) {
+                init_vels_restart(u, v, w);
+            } else if (restarting and restart_from_dump) {
+                init_vels_dump(u, v, w);
+            } else{
+                // else have a near motionless field
+                u = 0;
+                v = 0;
+                w = 0;
+                // Add a random perturbation to trigger any 3D instabilities
+                int myrank;
+                MPI_Comm_rank(MPI_COMM_WORLD,&myrank);
+                Normal<double> rnd(0,1);
+                for (int i = u.lbound(firstDim); i <= u.ubound(firstDim); i++) {
+                    rnd.seed(i);
+                    for (int j = u.lbound(secondDim); j <= u.ubound(secondDim); j++) {
+                        for (int k = u.lbound(thirdDim); k <= u.ubound(thirdDim); k++) {
+                            u(i,j,k) += perturb*rnd.random();
+                            w(i,j,k) += perturb*rnd.random();
+                            if (Ny > 1 || rot_f != 0)
+                                v(i,j,k) += perturb*rnd.random();
+                        }
+                    }
+                }
+                // Write the arrays
+                write_array(u,"u",plot_number);
+                write_array(w,"w",plot_number);
+                if (Ny > 1 || rot_f != 0) {
+                    write_array(v,"v",plot_number);
+                }
+            }
+        }
+
+        /* Initialize the tracers (density and dyes) */
+        void init_tracers(vector<DTArray *> & tracers) {
+            if (master()) fprintf(stdout,"Initializing tracers\n");
+            DTArray & temp = *tracers[TEMP];
+            DTArray & salt = *tracers[SALT];
+            
+            if (restarting and !restart_from_dump) {
+                init_tracer_restart("t",temp);
+                init_tracer_restart("s",salt);
+            } else if (restarting and restart_from_dump) {
+                init_tracer_dump("t",temp);
+                init_tracer_dump("s",salt);
+            } else {
+                // temperature configuration
+                temp = T_0 + delta_T*0.5*(1.0-tanh((xx(ii)-Lmix)/delta_x)) + 0*jj + 0*kk;
+                // salt configuration
+                salt = S_0 + delta_S*0.5*(1.0-tanh((xx(ii)-Lmix)/delta_x)) + 0*jj + 0*kk;
+                // Important! if mapped, and t or s depends on z
+                // then (*zgrid)(ii,jj,kk), must be used instead of zz(kk)
+                // Write the array
+                write_array(temp,"t",plot_number);
+                write_array(salt,"s",plot_number);
+            }
+        }
+
+        /* Forcing in the momentum equations */
+        void forcing(double t, const DTArray & u, DTArray & u_f,
+                const DTArray & v, DTArray & v_f, const DTArray & w, DTArray & w_f,
+                vector<DTArray *> & tracers, vector<DTArray *> & tracers_f) {
+            u_f = +rot_f*v;
+            v_f = -rot_f*u;
+
+            //Do Eos stuff 
+            nleos(*temp1,*tracers[TEMP],*tracers[SALT]);
+            rho_0 = compute_rho0(T_0,S_0);
+            
+            w_f = -g/rho_0*(*temp1-rho_0);
+            *tracers_f[TEMP] = 0;
+            *tracers_f[SALT] = 0;
+        }
+
+        /* Basic analysis: compute secondary variables, and save fields and diagnostics */
+        void analysis(double time, DTArray & u, DTArray & v, DTArray & w,
+                vector<DTArray *> & tracers, DTArray & pressure) {
+            // Set-up
+            if ( iter == 0 ) {
+                if ( compute_enstrophy or compute_dissipation or
+                        compute_stresses_top or compute_stresses_bottom ) {
+                    temp1 = alloc_array(Nx,Ny,Nz);
+                }
+                if ( compute_stresses_top or compute_stresses_bottom ) {
+                    // initialize the vector of the bottom slope (Hprime)
+                    Hprime = alloc_array(Nx,Ny,1);
+                    if (is_mapped()) {
+                        bottom_slope(*Hprime, *zgrid, *temp1, gradient_op, grid_type, Nx, Ny, Nz);
+                    } else {
+                        topo = 0*ii;
+                        *Hprime = 0*ii + 0*jj;
+                    }
+                }
+                // Determine last plot if restarting from the dump file
+                if (restart_from_dump) {
+                    next_plot = (restart_sequence+1)*plot_interval;
+                }
+                // initialize the size of each voxel
+                dxdydz = alloc_array(Nx,Ny,Nz);
+                *dxdydz = (*get_quad_x())(ii)*(*get_quad_y())(jj)*(*get_quad_z())(kk);
+                if (is_mapped()) {
+                    *dxdydz = (*dxdydz)*(Lz-topo(ii))/Lz;
+                }
+            }
+            // update clocks
+            if (master()) {
+                clock_time = MPI_Wtime();
+                comp_duration = clock_time - compute_start_time;
+            }
+
+            /* Calculate and write out useful information */
+
+            // Energy (PE assumes density is density anomaly)
+            double ke_x = 0, ke_y = 0, ke_z = 0;
+            if ( Nx > 1 ) {
+                ke_x = pssum(sum(0.5*rho_0*(u*u)*(*dxdydz)));
+            }
+            if (Ny > 1 || rot_f != 0) {
+                ke_y = pssum(sum(0.5*rho_0*(v*v)*(*dxdydz)));
+            }
+            if ( Nz > 1 ) {
+                ke_z = pssum(sum(0.5*rho_0*(w*w)*(*dxdydz)));
+            }
+            double pe_tot;
+            if (is_mapped()) {
+                pe_tot = pssum(sum(eqn_of_state(*tracers[TEMP],*tracers[SALT])*g*((*zgrid)(ii,jj,kk) - MinZ)*(*dxdydz)));
+            } else {
+                pe_tot = pssum(sum(eqn_of_state(*tracers[TEMP],*tracers[SALT])*g*(zz(kk) - MinZ)*(*dxdydz)));
+            }
+            double BPE_tot = 0;
+            if (compute_BPE) {
+                // full energy budget for salt/heat stratifcations is incomplete
+                //compute_Background_PE(BPE_tot, eqn_of_state(*tracers[TEMP],*tracers[SALT]), *dxdydz, Nx, Ny, Nz, Lx, Ly, Lz,
+                //        g, rho_0, iter, true, is_mapped(), topo);
+            }
+            // Conversion from internal energy to background potential energy
+            double phi_i = 0;
+            if (compute_internal_to_BPE) {
+                // this is not finished for temperature/salt stratified cases
+                //compute_BPE_from_internal(phi_i, *tracers[RHO], kappa_rho, rho_0, g, Nz);
+            }
+            // viscous dissipation
+            double diss_tot = 0;
+            double max_diss = 0;
+            if (compute_dissipation) {
+                dissipation(*temp1, u, v, w, gradient_op, grid_type, Nx, Ny, Nz, mu);
+                max_diss = psmax(max(*temp1));
+                diss_tot = pssum(sum((*temp1)*(*dxdydz)));
+            }
+            // Vorticity / Enstrophy
+            double max_vort_x = 0, enst_x_tot = 0;
+            double max_vort_y = 0, enst_y_tot = 0;
+            double max_vort_z = 0, enst_z_tot = 0;
+            if (compute_enstrophy) {
+                // x-vorticity
+                if (Ny > 1 and Nz > 1) {
+                    compute_vort_x(*temp1, v, w, gradient_op, grid_type);
+                    max_vort_x = psmax(max(abs(*temp1)));
+                    enst_x_tot = pssum(sum(0.5*pow(*temp1,2)*(*dxdydz)));
+                }
+                // y-vorticity
+                if (Nx > 1 and Nz > 1) {
+                    compute_vort_y(*temp1, u, w, gradient_op, grid_type);
+                    max_vort_y = psmax(max(abs(*temp1)));
+                    enst_y_tot = pssum(sum(0.5*pow(*temp1,2)*(*dxdydz)));
+                }
+                // z-vorticity
+                if (Nx > 1 and Ny > 1) {
+                    compute_vort_z(*temp1, u, v, gradient_op, grid_type);
+                    max_vort_z = psmax(max(abs(*temp1)));
+                    enst_z_tot = pssum(sum(0.5*pow(*temp1,2)*(*dxdydz)));
+                }
+            }
+            // max of fields
+            double max_u = psmax(max(abs(u)));
+            double max_v = psmax(max(abs(v)));
+            double max_w = psmax(max(abs(w)));
+            double max_vel = psmax(max(sqrt(u*u + v*v + w*w)));
+            double max_temp = psmax(max(*tracers[TEMP]));
+            double min_temp = psmin(min(*tracers[TEMP]));
+            double max_salt = psmax(max(*tracers[SALT]));
+            double min_salt = psmin(min(*tracers[SALT]));
+            double max_rho =  psmax(max(eqn_of_state(*tracers[TEMP],*tracers[SALT])));
+            double min_rho =  psmin(min(eqn_of_state(*tracers[TEMP],*tracers[SALT])));
+            // total mass (tracers[RHO] is non-dimensional density)
+            double mass = pssum(sum(eqn_of_state(*tracers[TEMP],*tracers[SALT])*(*dxdydz)));
+
+            if (master()) {
+                // add diagnostics to buffers
+                string header, line;
+                add_diagnostic("Iter", iter,            header, line);
+                add_diagnostic("Clock_time", comp_duration, header, line);
+                add_diagnostic("Time", time,            header, line);
+                add_diagnostic("Max_vel", max_vel,      header, line);
+                add_diagnostic("Max_temperature", max_temp, header, line);
+                add_diagnostic("Min_temperature", min_temp, header, line);
+                add_diagnostic("Max_salinity", max_salt,    header, line);
+                add_diagnostic("Min_salinity", min_salt,    header, line);
+                add_diagnostic("Max_density", max_rho,  header, line);
+                add_diagnostic("Min_density", min_rho,  header, line);
+                add_diagnostic("Mass", mass,            header, line);
+                add_diagnostic("PE_tot", pe_tot,        header, line);
+                if (compute_BPE) {
+                    add_diagnostic("BPE_tot", BPE_tot,  header, line);
+                }
+                if (compute_internal_to_BPE) {
+                    add_diagnostic("BPE_from_int", phi_i,   header, line);
+                }
+                if (compute_dissipation) {
+                    add_diagnostic("Max_diss", max_diss,    header, line);
+                    add_diagnostic("Diss_tot", diss_tot,    header, line);
+                }
+                if (Nx > 1) {
+                    add_diagnostic("Max_u", max_u,  header, line);
+                    add_diagnostic("KE_x", ke_x,    header, line);
+                }
+                if (Ny > 1 || rot_f != 0) {
+                    add_diagnostic("Max_v", max_v,  header, line);
+                    add_diagnostic("KE_y", ke_y,    header, line);
+                }
+                if (Nz > 1) {
+                    add_diagnostic("Max_w", max_w,  header, line);
+                    add_diagnostic("KE_z", ke_z,    header, line);
+                }
+                if (Ny > 1 && Nz > 1 && compute_enstrophy) {
+                    add_diagnostic("Enst_x_tot", enst_x_tot, header, line);
+                    add_diagnostic("Max_vort_x", max_vort_x, header, line);
+                }
+                if (Nx > 1 && Nz > 1 && compute_enstrophy) {
+                    add_diagnostic("Enst_y_tot", enst_y_tot, header, line);
+                    add_diagnostic("Max_vort_y", max_vort_y, header, line);
+                }
+                if (Nx > 1 && Ny > 1 && compute_enstrophy) {
+                    add_diagnostic("Enst_z_tot", enst_z_tot, header, line);
+                    add_diagnostic("Max_vort_z", max_vort_z, header, line);
+                }
+
+                // Write to file
+                if (!(restarting and iter==0))
+                    write_diagnostics(header, line, iter, restarting);
+                // and to the log file
+                fprintf(stdout,"[%d] (%.4g) %.4f: "
+                        "%.4g %.4g %.4g %.4g\n",
+                        iter,comp_duration,time,
+                        max_u,max_v,max_w,max_rho);
+            }
+
+            // Top Surface Stresses
+            if ( compute_stresses_top ) {
+                stresses_top(u, v, w, *Hprime, *temp1, gradient_op, grid_type, mu, time, iter, restarting);
+            }
+            // Bottom Surface Stresses
+            if ( compute_stresses_bottom ) {
+                stresses_bottom(u, v, w, *Hprime, *temp1, gradient_op, grid_type, mu, time, iter, restarting);
+            }
+
+            /* Write to disk if at correct time */
+            if ((time - next_plot) > -1e-6) {
+                plot_number++;
+                comp_duration = MPI_Wtime(); // time just before write (for dump)
+                // Write the arrays
+                write_array(u,"u",plot_number);
+                write_array(w,"w",plot_number);
+                if (Ny > 1 || rot_f != 0)
+                    write_array(v,"v",plot_number);
+                write_array(*tracers[TEMP],"t",plot_number);
+                write_array(*tracers[SALT],"s",plot_number);
+                if (write_pressure)
+                    write_array(pressure,"p",plot_number);
+                // update next plot time
+                next_plot = next_plot + plot_interval;
+
+                // Find average time to write (for dump)
+                clock_time = MPI_Wtime(); // time just after write
+                avg_write_time = (avg_write_time*(plot_number-restart_sequence-1) 
+                        + (clock_time - comp_duration))/(plot_number-restart_sequence);
+                // Print information about plot outputs
+                write_plot_times(time, clock_time, comp_duration, avg_write_time, plot_number, restarting);
+            }
+
+            // see if close to end of compute time and dump
+            check_and_dump(clock_time, real_start_time, compute_time, time, avg_write_time,
+                    plot_number, iter, u, v, w, tracers);
+            // Change dump log file if successfully reached final time
+            successful_dump(plot_number, final_time, plot_interval);
+            // increase counter
+            iter++;
+        }
+
+        // User specified variables to dump
+        void write_variables(DTArray & u,DTArray & v, DTArray & w,
+                vector<DTArray *> & tracers) {
+            write_array(u,"u.dump");
+            write_array(v,"v.dump");
+            write_array(w,"w.dump");
+            write_array(*tracers[TEMP],"t.dump");
+            write_array(*tracers[SALT],"s.dump");
+        }
+
+        // Constructor: Initialize local variables
+        userControl():
+            xx(split_range(Nx)), yy(Ny), zz(Nz),
+            topo(split_range(Nx)), gradient_op(0),
+            plot_number(restart_sequence),
+            next_plot(initial_time + plot_interval)
+    {   compute_quadweights(
+            size_x(),   size_y(),   size_z(),
+            length_x(), length_y(), length_z(),
+            type_x(),   type_y(),   type_z());
+        // Create one-dimensional arrays for the coordinates
+        automatic_grid(MinX, MinY, MinZ, &xx, &yy, &zz);
+    }
+};
+
+/* The ''main'' routine */
+int main(int argc, char ** argv) {
+    /* Initialize MPI.  This is required even for single-processor runs,
+       since the inner routines assume some degree of parallelization,
+       even if it is trivial. */
+    MPI_Init(&argc, &argv);
+
+    real_start_time = MPI_Wtime();     // start of simulation (for dump)
+    /* ------------------ Define parameters from spins.conf --------------------- */
+    options_init();
+
+    option_category("Grid Options");
+    add_option("Lx",&Lx,"Length of tank");
+    add_option("Ly",&Ly,1.0,"Width of tank");
+    add_option("Lz",&Lz,"Height of tank");
+    add_option("Nx",&Nx,"Number of points in X");
+    add_option("Ny",&Ny,1,"Number of points in Y");
+    add_option("Nz",&Nz,"Number of points in Z");
+    add_option("min_x",&MinX,0.0,"Minimum X-value");
+    add_option("min_y",&MinY,0.0,"Minimum Y-value");
+    add_option("min_z",&MinZ,0.0,"Minimum Z-value");
+
+    option_category("Grid expansion options");
+    string xgrid_type, ygrid_type, zgrid_type;
+    add_option("type_x",&xgrid_type,
+            "Grid type in X.  Valid values are:\n"
+            "   FOURIER: Periodic\n"
+            "   FREE_SLIP: Cosine expansion\n"
+            "   NO_SLIP: Chebyhsev expansion");
+    add_option("type_y",&ygrid_type,"FOURIER","Grid type in Y");
+    add_option("type_z",&zgrid_type,"Grid type in Z");
+    add_option("mapped_grid",&mapped,true,"Is the grid mapped?");
+
+    option_category("Topography parameters");
+    add_option("hill_height",&hill_height,"Height of hill");
+    add_option("hill_centre",&hill_centre,"location of hill peak");
+    add_option("hill_width",&hill_width,"Width of hill");
+
+    option_category("Physical parameters");
+    add_option("g",&g,9.81,"Gravitational acceleration");
+    add_option("rot_f",&rot_f,0.0,"Coriolis parameter");
+    add_option("rho_0",&rho_0,1000.0,"Reference density");
+    add_option("visco",&visco,"Viscosity");
+    add_option("kappa_T",&kappa_T,"Diffusivity of heat");
+    add_option("kappa_S",&kappa_S,"Diffusivity of salt");
+
+    option_category("Problem parameters");
+    add_option("delta_T",&delta_T,"Temperature difference");
+    add_option("delta_S",&delta_S,"Salinity difference");
+    add_option("T_0",&T_0,"Background temperature");
+    add_option("S_0",&S_0,"Background salinity");
+    add_option("delta_x",&delta_x,"Horizontal transition half-width");
+    add_option("Lmix",&Lmix,"Width of collapse region");
+
+    option_category("Temporal options");
+    add_option("final_time",&final_time,"Final time");
+    add_option("plot_interval",&plot_interval,"Time between writes");
+    add_option("dt_max",&dt_max,0.0,"Maximum time step. Zero value results in the default");
+
+    option_category("Restart options");
+    add_option("restart",&restarting,false,"Restart from prior output time.");
+    add_option("restart_time",&initial_time,0.0,"Time to restart from");
+    add_option("restart_sequence",&restart_sequence,-1,"Sequence number to restart from");
+
+    option_category("Dumping options");
+    add_option("restart_from_dump",&restart_from_dump,false,"If restart from dump");
+    add_option("compute_time",&compute_time,-1.0,"Time permitted for computation");
+
+    option_category("Other options");
+    add_option("perturb",&perturb,"Initial perturbation in velocity");
+    add_option("compute_enstrophy",&compute_enstrophy,true,"Calculate enstrophy?");
+    add_option("compute_dissipation",&compute_dissipation,true,"Calculate dissipation?");
+    add_option("compute_BPE",&compute_BPE,true,"Calculate BPE?");
+    add_option("compute_internal_to_BPE",&compute_internal_to_BPE,true,
+            "Calculate BPE gained from internal energy?");
+    add_option("compute_stresses_top",&compute_stresses_top,false,"Calculate top surfaces stresses?");
+    add_option("compute_stresses_bottom",&compute_stresses_bottom,false,"Calculate bottom surfaces stresses?");
+    add_option("write_pressure",&write_pressure,false,"Write the pressure field?");
+
+    option_category("Filter options");
+    add_option("f_cutoff",&f_cutoff,0.6,"Filter cut-off frequency");
+    add_option("f_order",&f_order,2.0,"Filter order");
+    add_option("f_strength",&f_strength,20.0,"Filter strength");
+
+    // Parse the options from the command line and config file
+    options_parse(argc,argv);
+
+    /* ------------------ Adjust and check parameters --------------------- */
+    /* Now, make sense of the options received.  Many of these
+     * can be directly used, but the ones of string-type need further procesing. */
+
+    // adjust temporal values when restarting from dump
+    if (restart_from_dump) {
+        adjust_for_dump(restarting, initial_time, restart_sequence,
+                final_time, compute_time, avg_write_time, Num_tracers, Nx, Ny, Nz);
+    }
+
+    // check restart sequence
+    check_restart_sequence(restarting, restart_sequence, initial_time, plot_interval);
+
+    // parse expansion types
+    parse_boundary_conditions(xgrid_type, ygrid_type, zgrid_type, intype_x, intype_y, intype_z);
+    // vector of expansion types
+    grid_type[0] = xgrid_type;
+    grid_type[1] = ygrid_type;
+    grid_type[2] = zgrid_type;
+
+    // adjust Ly for 2D
+    if (Ny==1 and Ly!=1.0) {
+        Ly = 1.0;
+        if (master())
+            fprintf(stdout,"Simulation is 2 dimensional, "
+                    "Ly has been changed to 1.0 for normalization.\n");
+    }
+
+    /* ------------------ Derived parameters --------------------- */
+
+    // Dynamic viscosity
+    mu = visco*rho_0;
+    // Maximum buoyancy frequency (squared) if the initial stratification was stable
+    //N2_max = g*delta_rho/(2*delta_x);
+    // Maximum time step
+    if (dt_max == 0.0) {
+        // if dt_max not given in spins.conf, use this
+        dt_max = 0.1;
+    }
+
+    /* ------------------ Initialize --------------------- */
+
+    // Create an instance of the above class
+    userControl mycode;
+    // Create a flow-evolver that takes its settings from the above class
+    FluidEvolve<userControl> do_stuff(&mycode);
+    // Initialize
+    do_stuff.initialize();
+
+    /* ------------------ Print some parameters --------------------- */
+
+    compute_start_time = MPI_Wtime(); // beginning of simulation (after reading in data)
+    double startup_time = compute_start_time - real_start_time;
+
+    if (master()) {
+        fprintf(stdout,"Dam break problem\n");
+        fprintf(stdout,"Using a %f x %f x %f grid of %d x %d x %d points\n",Lx,Ly,Lz,Nx,Ny,Nz);
+        fprintf(stdout,"g = %f, rot_f = %f, rho_0 = %f\n",g,rot_f,rho_0);
+        fprintf(stdout,"Time between plots: %g s\n",plot_interval);
+        fprintf(stdout,"Initial velocity perturbation: %g\n",perturb);
+        fprintf(stdout,"Filter cutoff = %f, order = %f, strength = %f\n",f_cutoff,f_order,f_strength);
+        fprintf(stdout,"Approx. max buoyancy frequency squared: %g\n",N2_max);
+        fprintf(stdout,"Max time step: %g\n",dt_max);
+        fprintf(stdout,"Start-up time: %.6g s.\n",startup_time);
+    }
+
+    /* ------------------ Run --------------------- */
+    // Run until the end of time
+    do_stuff.do_run(final_time);
+    MPI_Finalize();
+    return 0;
+}
diff --git a/src/cases/nleos_test/spins.conf b/src/cases/nleos_test/spins.conf
new file mode 100644
index 0000000000000000000000000000000000000000..fe5c278245ccd17011b34d7b7cd6cd4dbdcef1ff
--- /dev/null
+++ b/src/cases/nleos_test/spins.conf
@@ -0,0 +1,67 @@
+## Salt-temperature configuration file
+
+# Spatial Parameters
+Lx = 1.0
+Ly = 1.0
+Lz = 0.1
+Nx = 1024
+Ny = 1
+Nz = 128
+min_x = 0
+min_y = 0
+min_z = 0
+
+# Expansion types
+type_x = FREE_SLIP
+type_y = FREE_SLIP
+type_z = FREE_SLIP
+mapped_grid = false
+
+# Physical Parameters
+g = 9.81
+rot_f = 0.0e-3
+rho_0 = 1000.0
+visco = 2e-6
+kappa_T = 2e-6
+kappa_S = 1e-7
+
+# Problem Parameters (delta_rho is percentage)
+delta_T = 25.0
+delta_S = 5.0
+T_0 = 15.0
+S_0 = 0.0
+delta_x = 0.02
+Lmix = 0.1
+
+# Problem topography parameters
+hill_height = 0.1
+hill_centre = 0.5
+hill_width = 0.05
+
+# Temporal Parameters
+final_time = 100
+plot_interval = 1
+#dt_max = 0.0
+
+# Restart Options
+restart = false
+restart_time = 0.0
+restart_sequence = 0
+restart_from_dump = false
+compute_time = -1
+
+# Perturbation Parameter
+perturb = 1e-3
+
+# Filter Parameters
+f_cutoff = 0.6
+f_order = 2.0
+f_strength = 20.0
+
+# secondary diagnostics
+#compute_enstrophy = true
+#compute_dissipation = true
+compute_BPE = false
+compute_internal_to_BPE = false
+#compute_stresses_top = false
+#compute_stresses_bottom = false
diff --git a/src/cases/quadeos_test/nleos_test.cpp b/src/cases/quadeos_test/nleos_test.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6bea267ca530f3bd3d07ede918adeeb533714faf
--- /dev/null
+++ b/src/cases/quadeos_test/nleos_test.cpp
@@ -0,0 +1,655 @@
+/* cases/salt_temp/salt_temp.cpp */
+/* Script for the formation of a gravity current with:
+ * zero initial velocity
+ * salt or temperature stratification (not density)
+ * and with or without topography */
+
+/* ------------------ Top matter --------------------- */
+
+// Required headers
+#include "../../BaseCase.hpp"      // Support file containing default implementations of several functions
+#include "../../Options.hpp"       // config-file parser
+#include <random/normal.h>         // Blitz random number generator
+
+using namespace ranlib;
+
+// Tensor variables for indexing
+blitz::firstIndex ii;
+blitz::secondIndex jj;
+blitz::thirdIndex kk;
+
+/* ------------------ Define parameters --------------------- */
+
+// Grid scales
+double Lx, Ly, Lz;              // Grid lengths (m)
+int    Nx, Ny, Nz;              // Number of points in x, y, z
+double MinX, MinY, MinZ;        // Minimum x/y/z points (m)
+// Mapped grid?
+bool mapped;
+// Grid types
+DIMTYPE intype_x, intype_y, intype_z;
+string grid_type[3];
+
+// Physical parameters
+double g, rot_f, rho_0;         // gravity accel (m/s^2), Coriolis frequency (s^-1), reference density (kg/m^3)
+double visco;                   // viscosity (m^2/s)
+double mu;                      // dynamic viscosity (kg/(m·s))
+double kappa_T;                 // diffusivity of temperature (m^2/s)
+double kappa_S;                 // diffusivity of salt (m^2/s)
+// helpful constants
+const int Num_tracers = 2;      // number of tracers (temperature, salt and dyes)
+const int TEMP = 0;             // index for temperature
+const int SALT = 1;             // index for salt
+
+// Problem parameters
+double delta_T;                 // temperature difference between different layers
+double delta_S;                 // salinity difference between different layers
+double T_0;                     // background temperature
+double S_0;                     // background salinity
+double delta_x;                 // horizontal transition length (m)
+double Lmix;                    // Width of mixed region (m)
+
+// Topography parameters
+double hill_height;             // height of hill (m)
+double hill_centre;             // position of hill peak (m)
+double hill_width;              // width of hill (m)
+
+// Temporal parameters
+double final_time;              // Final time (s)
+double plot_interval;           // Time between field writes (s)
+double dt_max;                  // maximum time step (s)
+
+// Restarting options
+bool restarting;                // are you restarting?
+double initial_time;            // initial start time of simulation
+int restart_sequence;           // output number to restart from
+
+// Dump parameters
+bool restart_from_dump;         // restarting from dump?
+double compute_time;            // requested computation time
+double avg_write_time;          // average time to write all output fields at one output
+double real_start_time;         // real (clock) time when simulation begins
+double compute_start_time;      // real (clock) time when computation begins (after initialization)
+
+// other options
+double perturb;                 // Initial velocity perturbation
+bool compute_enstrophy;         // Compute enstrophy?
+bool compute_dissipation;       // Compute dissipation?
+bool compute_BPE;               // Compute background potential energy?
+bool compute_internal_to_BPE;   // Compute BPE gained from internal energy?
+bool compute_stresses_top;      // Compute top surface stresses?
+bool compute_stresses_bottom;   // Compute bottom surface stresses?
+bool write_pressure;            // Write the pressure field?
+int iter = 0;                   // Iteration counter
+
+// Maximum squared buoyancy frequency
+double N2_max;
+
+/* ------------------ Adjust the class --------------------- */
+
+class userControl : public BaseCase {
+    public:
+        // Grid and topography arrays
+        DTArray *zgrid;                 // Full grid fields
+        Array<double,1> xx, yy, zz;     // 1D grid vectors
+        Array<double,1> topo;           // topography vector
+        DTArray *Hprime;                // derivative of topography vector
+
+        // Arrays and operators for derivatives
+        Grad * gradient_op;
+        DTArray *temp1, *dxdydz;
+
+        // Timing variables (for outputs and measuring time steps)
+        int plot_number;        // plot output number
+        double next_plot;       // time of next output write
+        double comp_duration;   // clock time since computation began
+        double clock_time;      // current clock time
+
+        // Size of domain
+        double length_x() const { return Lx; }
+        double length_y() const { return Ly; }
+        double length_z() const { return Lz; }
+
+        // Resolution in x, y, and z
+        int size_x() const { return Nx; }
+        int size_y() const { return Ny; }
+        int size_z() const { return Nz; }
+
+        // Set expansions (FREE_SLIP, NO_SLIP (in vertical) or PERIODIC)
+        DIMTYPE type_x() const { return intype_x; }
+        DIMTYPE type_y() const { return intype_y; }
+        DIMTYPE type_z() const { return intype_z; }
+
+        // Record the gradient-taking object
+        void set_grad(Grad * in_grad) { gradient_op = in_grad; }
+
+        // Coriolis parameter, viscosity, and diffusivities
+        double get_rot_f() const { return rot_f; }
+        double get_visco() const { return visco; }
+        double get_diffusivity(int t_num) const {
+            switch (t_num) {
+                case TEMP:
+                    return kappa_T;
+                case SALT:
+                    return kappa_S;
+                default:
+                    if (master()) fprintf(stderr,"Invalid tracer number!\n");
+                    MPI_Finalize(); exit(1);
+            }
+        }
+
+        // Temporal parameters
+        double init_time() const { return initial_time; }
+        int get_restart_sequence() const { return restart_sequence; }
+        double get_dt_max() const { return dt_max; }
+        double get_next_plot() { return next_plot; }
+
+        // Number of tracers (the first is density)
+        int numtracers() const { return Num_tracers; }
+
+        // Create mapped grid
+        bool is_mapped() const { return mapped; }
+        void do_mapping(DTArray & xg, DTArray & yg, DTArray & zg) {
+            zgrid = alloc_array(Nx,Ny,Nz);
+
+            // over-write zz to be between -1 and 1
+            // (zz defined in automatic_grid below)
+            zz = -cos(ii*M_PI/(Nz-1));      // Chebyshev in vertical
+
+            // Define topography
+            topo = hill_height*exp(-pow((xx(ii)-hill_centre)/hill_width,2));
+
+            // make full grids
+            xg = xx(ii) + 0*jj + 0*kk;
+            yg = yy(jj) + 0*ii + 0*kk;
+            zg = MinZ + 0.5*Lz*(1+zz(kk)) + 0.5*(1-zz(kk))*topo(ii);
+            *zgrid = zg;
+
+            // Write the arrays and matlab readers
+            write_array(xg,"xgrid");
+            write_reader(xg,"xgrid",false);
+            if (Ny > 1) {
+                write_array(yg,"ygrid");
+                write_reader(yg,"ygrid",false);
+            }
+            write_array(zg,"zgrid");
+            write_reader(zg,"zgrid",false);
+        }
+
+        /* Initialize velocities */
+        void init_vels(DTArray & u, DTArray & v, DTArray & w) {
+            if (master()) fprintf(stdout,"Initializing velocities\n");
+            // if restarting
+            if (restarting and !restart_from_dump) {
+                init_vels_restart(u, v, w);
+            } else if (restarting and restart_from_dump) {
+                init_vels_dump(u, v, w);
+            } else{
+                // else have a near motionless field
+                u = 0;
+                v = 0;
+                w = 0;
+                // Add a random perturbation to trigger any 3D instabilities
+                int myrank;
+                MPI_Comm_rank(MPI_COMM_WORLD,&myrank);
+                Normal<double> rnd(0,1);
+                for (int i = u.lbound(firstDim); i <= u.ubound(firstDim); i++) {
+                    rnd.seed(i);
+                    for (int j = u.lbound(secondDim); j <= u.ubound(secondDim); j++) {
+                        for (int k = u.lbound(thirdDim); k <= u.ubound(thirdDim); k++) {
+                            u(i,j,k) += perturb*rnd.random();
+                            w(i,j,k) += perturb*rnd.random();
+                            if (Ny > 1 || rot_f != 0)
+                                v(i,j,k) += perturb*rnd.random();
+                        }
+                    }
+                }
+                // Write the arrays
+                write_array(u,"u",plot_number);
+                write_array(w,"w",plot_number);
+                if (Ny > 1 || rot_f != 0) {
+                    write_array(v,"v",plot_number);
+                }
+            }
+        }
+
+        /* Initialize the tracers (density and dyes) */
+        void init_tracers(vector<DTArray *> & tracers) {
+            if (master()) fprintf(stdout,"Initializing tracers\n");
+            DTArray & temp = *tracers[TEMP];
+            DTArray & salt = *tracers[SALT];
+            
+            if (restarting and !restart_from_dump) {
+                init_tracer_restart("t",temp);
+                init_tracer_restart("s",salt);
+            } else if (restarting and restart_from_dump) {
+                init_tracer_dump("t",temp);
+                init_tracer_dump("s",salt);
+            } else {
+                // temperature configuration
+                temp = T_0 + delta_T*0.5*(1.0-tanh((xx(ii)-Lmix)/delta_x)) + 0*jj + 0*kk;
+                // salt configuration
+                salt = S_0 + delta_S*0.5*(1.0-tanh((xx(ii)-Lmix)/delta_x)) + 0*jj + 0*kk;
+                // Important! if mapped, and t or s depends on z
+                // then (*zgrid)(ii,jj,kk), must be used instead of zz(kk)
+                // Write the array
+                write_array(temp,"t",plot_number);
+                write_array(salt,"s",plot_number);
+            }
+        }
+
+        /* Forcing in the momentum equations */
+        void forcing(double t, const DTArray & u, DTArray & u_f,
+                const DTArray & v, DTArray & v_f, const DTArray & w, DTArray & w_f,
+                vector<DTArray *> & tracers, vector<DTArray *> & tracers_f) {
+            u_f = +rot_f*v;
+            v_f = -rot_f*u;
+
+            //Do Eos stuff 
+            nleos(*temp1,*tracers[TEMP],*tracers[SALT]);
+            rho_0 = compute_rho0(T_0,S_0);
+            
+            w_f = -g/rho_0*(*temp1-rho_0);
+            *tracers_f[TEMP] = 0;
+            *tracers_f[SALT] = 0;
+        }
+
+        /* Basic analysis: compute secondary variables, and save fields and diagnostics */
+        void analysis(double time, DTArray & u, DTArray & v, DTArray & w,
+                vector<DTArray *> & tracers, DTArray & pressure) {
+            // Set-up
+            if ( iter == 0 ) {
+                if ( compute_enstrophy or compute_dissipation or
+                        compute_stresses_top or compute_stresses_bottom ) {
+                    temp1 = alloc_array(Nx,Ny,Nz);
+                }
+                if ( compute_stresses_top or compute_stresses_bottom ) {
+                    // initialize the vector of the bottom slope (Hprime)
+                    Hprime = alloc_array(Nx,Ny,1);
+                    if (is_mapped()) {
+                        bottom_slope(*Hprime, *zgrid, *temp1, gradient_op, grid_type, Nx, Ny, Nz);
+                    } else {
+                        topo = 0*ii;
+                        *Hprime = 0*ii + 0*jj;
+                    }
+                }
+                // Determine last plot if restarting from the dump file
+                if (restart_from_dump) {
+                    next_plot = (restart_sequence+1)*plot_interval;
+                }
+                // initialize the size of each voxel
+                dxdydz = alloc_array(Nx,Ny,Nz);
+                *dxdydz = (*get_quad_x())(ii)*(*get_quad_y())(jj)*(*get_quad_z())(kk);
+                if (is_mapped()) {
+                    *dxdydz = (*dxdydz)*(Lz-topo(ii))/Lz;
+                }
+            }
+            // update clocks
+            if (master()) {
+                clock_time = MPI_Wtime();
+                comp_duration = clock_time - compute_start_time;
+            }
+
+            /* Calculate and write out useful information */
+
+            // Energy (PE assumes density is density anomaly)
+            double ke_x = 0, ke_y = 0, ke_z = 0;
+            if ( Nx > 1 ) {
+                ke_x = pssum(sum(0.5*rho_0*(u*u)*(*dxdydz)));
+            }
+            if (Ny > 1 || rot_f != 0) {
+                ke_y = pssum(sum(0.5*rho_0*(v*v)*(*dxdydz)));
+            }
+            if ( Nz > 1 ) {
+                ke_z = pssum(sum(0.5*rho_0*(w*w)*(*dxdydz)));
+            }
+            double pe_tot;
+            if (is_mapped()) {
+                pe_tot = pssum(sum(eqn_of_state(*tracers[TEMP],*tracers[SALT])*g*((*zgrid)(ii,jj,kk) - MinZ)*(*dxdydz)));
+            } else {
+                pe_tot = pssum(sum(eqn_of_state(*tracers[TEMP],*tracers[SALT])*g*(zz(kk) - MinZ)*(*dxdydz)));
+            }
+            double BPE_tot = 0;
+            if (compute_BPE) {
+                // full energy budget for salt/heat stratifcations is incomplete
+                //compute_Background_PE(BPE_tot, eqn_of_state(*tracers[TEMP],*tracers[SALT]), *dxdydz, Nx, Ny, Nz, Lx, Ly, Lz,
+                //        g, rho_0, iter, true, is_mapped(), topo);
+            }
+            // Conversion from internal energy to background potential energy
+            double phi_i = 0;
+            if (compute_internal_to_BPE) {
+                // this is not finished for temperature/salt stratified cases
+                //compute_BPE_from_internal(phi_i, *tracers[RHO], kappa_rho, rho_0, g, Nz);
+            }
+            // viscous dissipation
+            double diss_tot = 0;
+            double max_diss = 0;
+            if (compute_dissipation) {
+                dissipation(*temp1, u, v, w, gradient_op, grid_type, Nx, Ny, Nz, mu);
+                max_diss = psmax(max(*temp1));
+                diss_tot = pssum(sum((*temp1)*(*dxdydz)));
+            }
+            // Vorticity / Enstrophy
+            double max_vort_x = 0, enst_x_tot = 0;
+            double max_vort_y = 0, enst_y_tot = 0;
+            double max_vort_z = 0, enst_z_tot = 0;
+            if (compute_enstrophy) {
+                // x-vorticity
+                if (Ny > 1 and Nz > 1) {
+                    compute_vort_x(*temp1, v, w, gradient_op, grid_type);
+                    max_vort_x = psmax(max(abs(*temp1)));
+                    enst_x_tot = pssum(sum(0.5*pow(*temp1,2)*(*dxdydz)));
+                }
+                // y-vorticity
+                if (Nx > 1 and Nz > 1) {
+                    compute_vort_y(*temp1, u, w, gradient_op, grid_type);
+                    max_vort_y = psmax(max(abs(*temp1)));
+                    enst_y_tot = pssum(sum(0.5*pow(*temp1,2)*(*dxdydz)));
+                }
+                // z-vorticity
+                if (Nx > 1 and Ny > 1) {
+                    compute_vort_z(*temp1, u, v, gradient_op, grid_type);
+                    max_vort_z = psmax(max(abs(*temp1)));
+                    enst_z_tot = pssum(sum(0.5*pow(*temp1,2)*(*dxdydz)));
+                }
+            }
+            // max of fields
+            double max_u = psmax(max(abs(u)));
+            double max_v = psmax(max(abs(v)));
+            double max_w = psmax(max(abs(w)));
+            double max_vel = psmax(max(sqrt(u*u + v*v + w*w)));
+            double max_temp = psmax(max(*tracers[TEMP]));
+            double min_temp = psmin(min(*tracers[TEMP]));
+            double max_salt = psmax(max(*tracers[SALT]));
+            double min_salt = psmin(min(*tracers[SALT]));
+            double max_rho =  psmax(max(eqn_of_state(*tracers[TEMP],*tracers[SALT])));
+            double min_rho =  psmin(min(eqn_of_state(*tracers[TEMP],*tracers[SALT])));
+            // total mass (tracers[RHO] is non-dimensional density)
+            double mass = pssum(sum(eqn_of_state(*tracers[TEMP],*tracers[SALT])*(*dxdydz)));
+
+            if (master()) {
+                // add diagnostics to buffers
+                string header, line;
+                add_diagnostic("Iter", iter,            header, line);
+                add_diagnostic("Clock_time", comp_duration, header, line);
+                add_diagnostic("Time", time,            header, line);
+                add_diagnostic("Max_vel", max_vel,      header, line);
+                add_diagnostic("Max_temperature", max_temp, header, line);
+                add_diagnostic("Min_temperature", min_temp, header, line);
+                add_diagnostic("Max_salinity", max_salt,    header, line);
+                add_diagnostic("Min_salinity", min_salt,    header, line);
+                add_diagnostic("Max_density", max_rho,  header, line);
+                add_diagnostic("Min_density", min_rho,  header, line);
+                add_diagnostic("Mass", mass,            header, line);
+                add_diagnostic("PE_tot", pe_tot,        header, line);
+                if (compute_BPE) {
+                    add_diagnostic("BPE_tot", BPE_tot,  header, line);
+                }
+                if (compute_internal_to_BPE) {
+                    add_diagnostic("BPE_from_int", phi_i,   header, line);
+                }
+                if (compute_dissipation) {
+                    add_diagnostic("Max_diss", max_diss,    header, line);
+                    add_diagnostic("Diss_tot", diss_tot,    header, line);
+                }
+                if (Nx > 1) {
+                    add_diagnostic("Max_u", max_u,  header, line);
+                    add_diagnostic("KE_x", ke_x,    header, line);
+                }
+                if (Ny > 1 || rot_f != 0) {
+                    add_diagnostic("Max_v", max_v,  header, line);
+                    add_diagnostic("KE_y", ke_y,    header, line);
+                }
+                if (Nz > 1) {
+                    add_diagnostic("Max_w", max_w,  header, line);
+                    add_diagnostic("KE_z", ke_z,    header, line);
+                }
+                if (Ny > 1 && Nz > 1 && compute_enstrophy) {
+                    add_diagnostic("Enst_x_tot", enst_x_tot, header, line);
+                    add_diagnostic("Max_vort_x", max_vort_x, header, line);
+                }
+                if (Nx > 1 && Nz > 1 && compute_enstrophy) {
+                    add_diagnostic("Enst_y_tot", enst_y_tot, header, line);
+                    add_diagnostic("Max_vort_y", max_vort_y, header, line);
+                }
+                if (Nx > 1 && Ny > 1 && compute_enstrophy) {
+                    add_diagnostic("Enst_z_tot", enst_z_tot, header, line);
+                    add_diagnostic("Max_vort_z", max_vort_z, header, line);
+                }
+
+                // Write to file
+                if (!(restarting and iter==0))
+                    write_diagnostics(header, line, iter, restarting);
+                // and to the log file
+                fprintf(stdout,"[%d] (%.4g) %.4f: "
+                        "%.4g %.4g %.4g %.4g\n",
+                        iter,comp_duration,time,
+                        max_u,max_v,max_w,max_rho);
+            }
+
+            // Top Surface Stresses
+            if ( compute_stresses_top ) {
+                stresses_top(u, v, w, *Hprime, *temp1, gradient_op, grid_type, mu, time, iter, restarting);
+            }
+            // Bottom Surface Stresses
+            if ( compute_stresses_bottom ) {
+                stresses_bottom(u, v, w, *Hprime, *temp1, gradient_op, grid_type, mu, time, iter, restarting);
+            }
+
+            /* Write to disk if at correct time */
+            if ((time - next_plot) > -1e-6) {
+                plot_number++;
+                comp_duration = MPI_Wtime(); // time just before write (for dump)
+                // Write the arrays
+                write_array(u,"u",plot_number);
+                write_array(w,"w",plot_number);
+                if (Ny > 1 || rot_f != 0)
+                    write_array(v,"v",plot_number);
+                write_array(*tracers[TEMP],"t",plot_number);
+                write_array(*tracers[SALT],"s",plot_number);
+                if (write_pressure)
+                    write_array(pressure,"p",plot_number);
+                // update next plot time
+                next_plot = next_plot + plot_interval;
+
+                // Find average time to write (for dump)
+                clock_time = MPI_Wtime(); // time just after write
+                avg_write_time = (avg_write_time*(plot_number-restart_sequence-1) 
+                        + (clock_time - comp_duration))/(plot_number-restart_sequence);
+                // Print information about plot outputs
+                write_plot_times(time, clock_time, comp_duration, avg_write_time, plot_number, restarting);
+            }
+
+            // see if close to end of compute time and dump
+            check_and_dump(clock_time, real_start_time, compute_time, time, avg_write_time,
+                    plot_number, iter, u, v, w, tracers);
+            // Change dump log file if successfully reached final time
+            successful_dump(plot_number, final_time, plot_interval);
+            // increase counter
+            iter++;
+        }
+
+        // User specified variables to dump
+        void write_variables(DTArray & u,DTArray & v, DTArray & w,
+                vector<DTArray *> & tracers) {
+            write_array(u,"u.dump");
+            write_array(v,"v.dump");
+            write_array(w,"w.dump");
+            write_array(*tracers[TEMP],"t.dump");
+            write_array(*tracers[SALT],"s.dump");
+        }
+
+        // Constructor: Initialize local variables
+        userControl():
+            xx(split_range(Nx)), yy(Ny), zz(Nz),
+            topo(split_range(Nx)), gradient_op(0),
+            plot_number(restart_sequence),
+            next_plot(initial_time + plot_interval)
+    {   compute_quadweights(
+            size_x(),   size_y(),   size_z(),
+            length_x(), length_y(), length_z(),
+            type_x(),   type_y(),   type_z());
+        // Create one-dimensional arrays for the coordinates
+        automatic_grid(MinX, MinY, MinZ, &xx, &yy, &zz);
+    }
+};
+
+/* The ''main'' routine */
+int main(int argc, char ** argv) {
+    /* Initialize MPI.  This is required even for single-processor runs,
+       since the inner routines assume some degree of parallelization,
+       even if it is trivial. */
+    MPI_Init(&argc, &argv);
+
+    real_start_time = MPI_Wtime();     // start of simulation (for dump)
+    /* ------------------ Define parameters from spins.conf --------------------- */
+    options_init();
+
+    option_category("Grid Options");
+    add_option("Lx",&Lx,"Length of tank");
+    add_option("Ly",&Ly,1.0,"Width of tank");
+    add_option("Lz",&Lz,"Height of tank");
+    add_option("Nx",&Nx,"Number of points in X");
+    add_option("Ny",&Ny,1,"Number of points in Y");
+    add_option("Nz",&Nz,"Number of points in Z");
+    add_option("min_x",&MinX,0.0,"Minimum X-value");
+    add_option("min_y",&MinY,0.0,"Minimum Y-value");
+    add_option("min_z",&MinZ,0.0,"Minimum Z-value");
+
+    option_category("Grid expansion options");
+    string xgrid_type, ygrid_type, zgrid_type;
+    add_option("type_x",&xgrid_type,
+            "Grid type in X.  Valid values are:\n"
+            "   FOURIER: Periodic\n"
+            "   FREE_SLIP: Cosine expansion\n"
+            "   NO_SLIP: Chebyhsev expansion");
+    add_option("type_y",&ygrid_type,"FOURIER","Grid type in Y");
+    add_option("type_z",&zgrid_type,"Grid type in Z");
+    add_option("mapped_grid",&mapped,true,"Is the grid mapped?");
+
+    option_category("Topography parameters");
+    add_option("hill_height",&hill_height,"Height of hill");
+    add_option("hill_centre",&hill_centre,"location of hill peak");
+    add_option("hill_width",&hill_width,"Width of hill");
+
+    option_category("Physical parameters");
+    add_option("g",&g,9.81,"Gravitational acceleration");
+    add_option("rot_f",&rot_f,0.0,"Coriolis parameter");
+    add_option("rho_0",&rho_0,1000.0,"Reference density");
+    add_option("visco",&visco,"Viscosity");
+    add_option("kappa_T",&kappa_T,"Diffusivity of heat");
+    add_option("kappa_S",&kappa_S,"Diffusivity of salt");
+
+    option_category("Problem parameters");
+    add_option("delta_T",&delta_T,"Temperature difference");
+    add_option("delta_S",&delta_S,"Salinity difference");
+    add_option("T_0",&T_0,"Background temperature");
+    add_option("S_0",&S_0,"Background salinity");
+    add_option("delta_x",&delta_x,"Horizontal transition half-width");
+    add_option("Lmix",&Lmix,"Width of collapse region");
+
+    option_category("Temporal options");
+    add_option("final_time",&final_time,"Final time");
+    add_option("plot_interval",&plot_interval,"Time between writes");
+    add_option("dt_max",&dt_max,0.0,"Maximum time step. Zero value results in the default");
+
+    option_category("Restart options");
+    add_option("restart",&restarting,false,"Restart from prior output time.");
+    add_option("restart_time",&initial_time,0.0,"Time to restart from");
+    add_option("restart_sequence",&restart_sequence,-1,"Sequence number to restart from");
+
+    option_category("Dumping options");
+    add_option("restart_from_dump",&restart_from_dump,false,"If restart from dump");
+    add_option("compute_time",&compute_time,-1.0,"Time permitted for computation");
+
+    option_category("Other options");
+    add_option("perturb",&perturb,"Initial perturbation in velocity");
+    add_option("compute_enstrophy",&compute_enstrophy,true,"Calculate enstrophy?");
+    add_option("compute_dissipation",&compute_dissipation,true,"Calculate dissipation?");
+    add_option("compute_BPE",&compute_BPE,true,"Calculate BPE?");
+    add_option("compute_internal_to_BPE",&compute_internal_to_BPE,true,
+            "Calculate BPE gained from internal energy?");
+    add_option("compute_stresses_top",&compute_stresses_top,false,"Calculate top surfaces stresses?");
+    add_option("compute_stresses_bottom",&compute_stresses_bottom,false,"Calculate bottom surfaces stresses?");
+    add_option("write_pressure",&write_pressure,false,"Write the pressure field?");
+
+    option_category("Filter options");
+    add_option("f_cutoff",&f_cutoff,0.6,"Filter cut-off frequency");
+    add_option("f_order",&f_order,2.0,"Filter order");
+    add_option("f_strength",&f_strength,20.0,"Filter strength");
+
+    // Parse the options from the command line and config file
+    options_parse(argc,argv);
+
+    /* ------------------ Adjust and check parameters --------------------- */
+    /* Now, make sense of the options received.  Many of these
+     * can be directly used, but the ones of string-type need further procesing. */
+
+    // adjust temporal values when restarting from dump
+    if (restart_from_dump) {
+        adjust_for_dump(restarting, initial_time, restart_sequence,
+                final_time, compute_time, avg_write_time, Num_tracers, Nx, Ny, Nz);
+    }
+
+    // check restart sequence
+    check_restart_sequence(restarting, restart_sequence, initial_time, plot_interval);
+
+    // parse expansion types
+    parse_boundary_conditions(xgrid_type, ygrid_type, zgrid_type, intype_x, intype_y, intype_z);
+    // vector of expansion types
+    grid_type[0] = xgrid_type;
+    grid_type[1] = ygrid_type;
+    grid_type[2] = zgrid_type;
+
+    // adjust Ly for 2D
+    if (Ny==1 and Ly!=1.0) {
+        Ly = 1.0;
+        if (master())
+            fprintf(stdout,"Simulation is 2 dimensional, "
+                    "Ly has been changed to 1.0 for normalization.\n");
+    }
+
+    /* ------------------ Derived parameters --------------------- */
+
+    // Dynamic viscosity
+    mu = visco*rho_0;
+    // Maximum buoyancy frequency (squared) if the initial stratification was stable
+    //N2_max = g*delta_rho/(2*delta_x);
+    // Maximum time step
+    if (dt_max == 0.0) {
+        // if dt_max not given in spins.conf, use this
+        dt_max = 0.1;
+    }
+
+    /* ------------------ Initialize --------------------- */
+
+    // Create an instance of the above class
+    userControl mycode;
+    // Create a flow-evolver that takes its settings from the above class
+    FluidEvolve<userControl> do_stuff(&mycode);
+    // Initialize
+    do_stuff.initialize();
+
+    /* ------------------ Print some parameters --------------------- */
+
+    compute_start_time = MPI_Wtime(); // beginning of simulation (after reading in data)
+    double startup_time = compute_start_time - real_start_time;
+
+    if (master()) {
+        fprintf(stdout,"Dam break problem\n");
+        fprintf(stdout,"Using a %f x %f x %f grid of %d x %d x %d points\n",Lx,Ly,Lz,Nx,Ny,Nz);
+        fprintf(stdout,"g = %f, rot_f = %f, rho_0 = %f\n",g,rot_f,rho_0);
+        fprintf(stdout,"Time between plots: %g s\n",plot_interval);
+        fprintf(stdout,"Initial velocity perturbation: %g\n",perturb);
+        fprintf(stdout,"Filter cutoff = %f, order = %f, strength = %f\n",f_cutoff,f_order,f_strength);
+        fprintf(stdout,"Approx. max buoyancy frequency squared: %g\n",N2_max);
+        fprintf(stdout,"Max time step: %g\n",dt_max);
+        fprintf(stdout,"Start-up time: %.6g s.\n",startup_time);
+    }
+
+    /* ------------------ Run --------------------- */
+    // Run until the end of time
+    do_stuff.do_run(final_time);
+    MPI_Finalize();
+    return 0;
+}
diff --git a/src/cases/quadeos_test/quadeos_test.cpp b/src/cases/quadeos_test/quadeos_test.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d70c550917b18f3268b1f123eea62110d01f636a
--- /dev/null
+++ b/src/cases/quadeos_test/quadeos_test.cpp
@@ -0,0 +1,656 @@
+/* cases/salt_temp/salt_temp.cpp */
+/* Script for the formation of a gravity current with:
+ * zero initial velocity
+ * salt or temperature stratification (not density)
+ * and with or without topography */
+
+/* ------------------ Top matter --------------------- */
+
+// Required headers
+#include "../../BaseCase.hpp"      // Support file containing default implementations of several functions
+#include "../../Options.hpp"       // config-file parser
+#include <random/normal.h>         // Blitz random number generator
+
+using namespace ranlib;
+
+// Tensor variables for indexing
+blitz::firstIndex ii;
+blitz::secondIndex jj;
+blitz::thirdIndex kk;
+
+/* ------------------ Define parameters --------------------- */
+
+// Grid scales
+double Lx, Ly, Lz;              // Grid lengths (m)
+int    Nx, Ny, Nz;              // Number of points in x, y, z
+double MinX, MinY, MinZ;        // Minimum x/y/z points (m)
+// Mapped grid?
+bool mapped;
+// Grid types
+DIMTYPE intype_x, intype_y, intype_z;
+string grid_type[3];
+
+// Physical parameters
+double g, rot_f, rho_0;         // gravity accel (m/s^2), Coriolis frequency (s^-1), reference density (kg/m^3)
+double visco;                   // viscosity (m^2/s)
+double mu;                      // dynamic viscosity (kg/(m·s))
+double kappa_T;                 // diffusivity of temperature (m^2/s)
+double kappa_S;                 // diffusivity of salt (m^2/s)
+// helpful constants
+const int Num_tracers = 2;      // number of tracers (temperature, salt and dyes)
+const int TEMP = 0;             // index for temperature
+const int SALT = 1;             // index for salt
+
+// Problem parameters
+double delta_T;                 // temperature difference between different layers
+double delta_S;                 // salinity difference between different layers
+double T_0;                     // background temperature
+double S_0;                     // background salinity
+double delta_x;                 // horizontal transition length (m)
+double Lmix;                    // Width of mixed region (m)
+
+// Topography parameters
+double hill_height;             // height of hill (m)
+double hill_centre;             // position of hill peak (m)
+double hill_width;              // width of hill (m)
+
+// Temporal parameters
+double final_time;              // Final time (s)
+double plot_interval;           // Time between field writes (s)
+double dt_max;                  // maximum time step (s)
+
+// Restarting options
+bool restarting;                // are you restarting?
+double initial_time;            // initial start time of simulation
+int restart_sequence;           // output number to restart from
+
+// Dump parameters
+bool restart_from_dump;         // restarting from dump?
+double compute_time;            // requested computation time
+double avg_write_time;          // average time to write all output fields at one output
+double real_start_time;         // real (clock) time when simulation begins
+double compute_start_time;      // real (clock) time when computation begins (after initialization)
+
+// other options
+double perturb;                 // Initial velocity perturbation
+bool compute_enstrophy;         // Compute enstrophy?
+bool compute_dissipation;       // Compute dissipation?
+bool compute_BPE;               // Compute background potential energy?
+bool compute_internal_to_BPE;   // Compute BPE gained from internal energy?
+bool compute_stresses_top;      // Compute top surface stresses?
+bool compute_stresses_bottom;   // Compute bottom surface stresses?
+bool write_pressure;            // Write the pressure field?
+int iter = 0;                   // Iteration counter
+
+// Maximum squared buoyancy frequency
+double N2_max;
+
+/* ------------------ Adjust the class --------------------- */
+
+class userControl : public BaseCase {
+    public:
+        // Grid and topography arrays
+        DTArray *zgrid;                 // Full grid fields
+        Array<double,1> xx, yy, zz;     // 1D grid vectors
+        Array<double,1> topo;           // topography vector
+        DTArray *Hprime;                // derivative of topography vector
+
+        // Arrays and operators for derivatives
+        Grad * gradient_op;
+        DTArray *temp1, *dxdydz;
+
+        // Timing variables (for outputs and measuring time steps)
+        int plot_number;        // plot output number
+        double next_plot;       // time of next output write
+        double comp_duration;   // clock time since computation began
+        double clock_time;      // current clock time
+
+        // Size of domain
+        double length_x() const { return Lx; }
+        double length_y() const { return Ly; }
+        double length_z() const { return Lz; }
+
+        // Resolution in x, y, and z
+        int size_x() const { return Nx; }
+        int size_y() const { return Ny; }
+        int size_z() const { return Nz; }
+
+        // Set expansions (FREE_SLIP, NO_SLIP (in vertical) or PERIODIC)
+        DIMTYPE type_x() const { return intype_x; }
+        DIMTYPE type_y() const { return intype_y; }
+        DIMTYPE type_z() const { return intype_z; }
+
+        // Record the gradient-taking object
+        void set_grad(Grad * in_grad) { gradient_op = in_grad; }
+
+        // Coriolis parameter, viscosity, and diffusivities
+        double get_rot_f() const { return rot_f; }
+        double get_visco() const { return visco; }
+        double get_diffusivity(int t_num) const {
+            switch (t_num) {
+                case TEMP:
+                    return kappa_T;
+                case SALT:
+                    return kappa_S;
+                default:
+                    if (master()) fprintf(stderr,"Invalid tracer number!\n");
+                    MPI_Finalize(); exit(1);
+            }
+        }
+
+        // Temporal parameters
+        double init_time() const { return initial_time; }
+        int get_restart_sequence() const { return restart_sequence; }
+        double get_dt_max() const { return dt_max; }
+        double get_next_plot() { return next_plot; }
+
+        // Number of tracers (the first is density)
+        int numtracers() const { return Num_tracers; }
+
+        // Create mapped grid
+        bool is_mapped() const { return mapped; }
+        void do_mapping(DTArray & xg, DTArray & yg, DTArray & zg) {
+            zgrid = alloc_array(Nx,Ny,Nz);
+
+            // over-write zz to be between -1 and 1
+            // (zz defined in automatic_grid below)
+            zz = -cos(ii*M_PI/(Nz-1));      // Chebyshev in vertical
+
+            // Define topography
+            topo = hill_height*exp(-pow((xx(ii)-hill_centre)/hill_width,2));
+
+            // make full grids
+            xg = xx(ii) + 0*jj + 0*kk;
+            yg = yy(jj) + 0*ii + 0*kk;
+            zg = MinZ + 0.5*Lz*(1+zz(kk)) + 0.5*(1-zz(kk))*topo(ii);
+            *zgrid = zg;
+
+            // Write the arrays and matlab readers
+            write_array(xg,"xgrid");
+            write_reader(xg,"xgrid",false);
+            if (Ny > 1) {
+                write_array(yg,"ygrid");
+                write_reader(yg,"ygrid",false);
+            }
+            write_array(zg,"zgrid");
+            write_reader(zg,"zgrid",false);
+        }
+
+        /* Initialize velocities */
+        void init_vels(DTArray & u, DTArray & v, DTArray & w) {
+            if (master()) fprintf(stdout,"Initializing velocities\n");
+            // if restarting
+            if (restarting and !restart_from_dump) {
+                init_vels_restart(u, v, w);
+            } else if (restarting and restart_from_dump) {
+                init_vels_dump(u, v, w);
+            } else{
+                // else have a near motionless field
+                u = 0;
+                v = 0;
+                w = 0;
+                // Add a random perturbation to trigger any 3D instabilities
+                int myrank;
+                MPI_Comm_rank(MPI_COMM_WORLD,&myrank);
+                Normal<double> rnd(0,1);
+                for (int i = u.lbound(firstDim); i <= u.ubound(firstDim); i++) {
+                    rnd.seed(i);
+                    for (int j = u.lbound(secondDim); j <= u.ubound(secondDim); j++) {
+                        for (int k = u.lbound(thirdDim); k <= u.ubound(thirdDim); k++) {
+                            u(i,j,k) += perturb*rnd.random();
+                            w(i,j,k) += perturb*rnd.random();
+                            if (Ny > 1 || rot_f != 0)
+                                v(i,j,k) += perturb*rnd.random();
+                        }
+                    }
+                }
+                // Write the arrays
+                write_array(u,"u",plot_number);
+                write_array(w,"w",plot_number);
+                if (Ny > 1 || rot_f != 0) {
+                    write_array(v,"v",plot_number);
+                }
+            }
+        }
+
+        /* Initialize the tracers (density and dyes) */
+        void init_tracers(vector<DTArray *> & tracers) {
+            if (master()) fprintf(stdout,"Initializing tracers\n");
+            DTArray & temp = *tracers[TEMP];
+            DTArray & salt = *tracers[SALT];
+            
+            if (restarting and !restart_from_dump) {
+                init_tracer_restart("t",temp);
+                init_tracer_restart("s",salt);
+            } else if (restarting and restart_from_dump) {
+                init_tracer_dump("t",temp);
+                init_tracer_dump("s",salt);
+            } else {
+                // temperature configuration
+                temp = T_0 + delta_T*0.5*(1.0-tanh((xx(ii)-Lmix)/delta_x)) + 0*jj + 0*kk;
+                // salt configuration
+                salt = S_0 + delta_S*0.5*(1.0-tanh((xx(ii)-Lmix)/delta_x)) + 0*jj + 0*kk;
+                // Important! if mapped, and t or s depends on z
+                // then (*zgrid)(ii,jj,kk), must be used instead of zz(kk)
+                // Write the array
+                write_array(temp,"t",plot_number);
+                write_array(salt,"s",plot_number);
+            }
+        }
+
+        /* Forcing in the momentum equations */
+        void forcing(double t, const DTArray & u, DTArray & u_f,
+                const DTArray & v, DTArray & v_f, const DTArray & w, DTArray & w_f,
+                vector<DTArray *> & tracers, vector<DTArray *> & tracers_f) {
+            u_f = +rot_f*v;
+            v_f = -rot_f*u;
+
+            //Do Eos stuff 
+            quadeos(*temp1,*tracers[TEMP]);
+            //The line below is patchwork, but is enough for a test case
+            rho_0 = compute_rho0(3.98,0);
+            
+            w_f = -g/rho_0*(*temp1-rho_0);
+            *tracers_f[TEMP] = 0;
+            *tracers_f[SALT] = 0;
+        }
+
+        /* Basic analysis: compute secondary variables, and save fields and diagnostics */
+        void analysis(double time, DTArray & u, DTArray & v, DTArray & w,
+                vector<DTArray *> & tracers, DTArray & pressure) {
+            // Set-up
+            if ( iter == 0 ) {
+                if ( compute_enstrophy or compute_dissipation or
+                        compute_stresses_top or compute_stresses_bottom ) {
+                    temp1 = alloc_array(Nx,Ny,Nz);
+                }
+                if ( compute_stresses_top or compute_stresses_bottom ) {
+                    // initialize the vector of the bottom slope (Hprime)
+                    Hprime = alloc_array(Nx,Ny,1);
+                    if (is_mapped()) {
+                        bottom_slope(*Hprime, *zgrid, *temp1, gradient_op, grid_type, Nx, Ny, Nz);
+                    } else {
+                        topo = 0*ii;
+                        *Hprime = 0*ii + 0*jj;
+                    }
+                }
+                // Determine last plot if restarting from the dump file
+                if (restart_from_dump) {
+                    next_plot = (restart_sequence+1)*plot_interval;
+                }
+                // initialize the size of each voxel
+                dxdydz = alloc_array(Nx,Ny,Nz);
+                *dxdydz = (*get_quad_x())(ii)*(*get_quad_y())(jj)*(*get_quad_z())(kk);
+                if (is_mapped()) {
+                    *dxdydz = (*dxdydz)*(Lz-topo(ii))/Lz;
+                }
+            }
+            // update clocks
+            if (master()) {
+                clock_time = MPI_Wtime();
+                comp_duration = clock_time - compute_start_time;
+            }
+
+            /* Calculate and write out useful information */
+
+            // Energy (PE assumes density is density anomaly)
+            double ke_x = 0, ke_y = 0, ke_z = 0;
+            if ( Nx > 1 ) {
+                ke_x = pssum(sum(0.5*rho_0*(u*u)*(*dxdydz)));
+            }
+            if (Ny > 1 || rot_f != 0) {
+                ke_y = pssum(sum(0.5*rho_0*(v*v)*(*dxdydz)));
+            }
+            if ( Nz > 1 ) {
+                ke_z = pssum(sum(0.5*rho_0*(w*w)*(*dxdydz)));
+            }
+            double pe_tot;
+            if (is_mapped()) {
+                pe_tot = pssum(sum(eqn_of_state(*tracers[TEMP],*tracers[SALT])*g*((*zgrid)(ii,jj,kk) - MinZ)*(*dxdydz)));
+            } else {
+                pe_tot = pssum(sum(eqn_of_state(*tracers[TEMP],*tracers[SALT])*g*(zz(kk) - MinZ)*(*dxdydz)));
+            }
+            double BPE_tot = 0;
+            if (compute_BPE) {
+                // full energy budget for salt/heat stratifcations is incomplete
+                //compute_Background_PE(BPE_tot, eqn_of_state(*tracers[TEMP],*tracers[SALT]), *dxdydz, Nx, Ny, Nz, Lx, Ly, Lz,
+                //        g, rho_0, iter, true, is_mapped(), topo);
+            }
+            // Conversion from internal energy to background potential energy
+            double phi_i = 0;
+            if (compute_internal_to_BPE) {
+                // this is not finished for temperature/salt stratified cases
+                //compute_BPE_from_internal(phi_i, *tracers[RHO], kappa_rho, rho_0, g, Nz);
+            }
+            // viscous dissipation
+            double diss_tot = 0;
+            double max_diss = 0;
+            if (compute_dissipation) {
+                dissipation(*temp1, u, v, w, gradient_op, grid_type, Nx, Ny, Nz, mu);
+                max_diss = psmax(max(*temp1));
+                diss_tot = pssum(sum((*temp1)*(*dxdydz)));
+            }
+            // Vorticity / Enstrophy
+            double max_vort_x = 0, enst_x_tot = 0;
+            double max_vort_y = 0, enst_y_tot = 0;
+            double max_vort_z = 0, enst_z_tot = 0;
+            if (compute_enstrophy) {
+                // x-vorticity
+                if (Ny > 1 and Nz > 1) {
+                    compute_vort_x(*temp1, v, w, gradient_op, grid_type);
+                    max_vort_x = psmax(max(abs(*temp1)));
+                    enst_x_tot = pssum(sum(0.5*pow(*temp1,2)*(*dxdydz)));
+                }
+                // y-vorticity
+                if (Nx > 1 and Nz > 1) {
+                    compute_vort_y(*temp1, u, w, gradient_op, grid_type);
+                    max_vort_y = psmax(max(abs(*temp1)));
+                    enst_y_tot = pssum(sum(0.5*pow(*temp1,2)*(*dxdydz)));
+                }
+                // z-vorticity
+                if (Nx > 1 and Ny > 1) {
+                    compute_vort_z(*temp1, u, v, gradient_op, grid_type);
+                    max_vort_z = psmax(max(abs(*temp1)));
+                    enst_z_tot = pssum(sum(0.5*pow(*temp1,2)*(*dxdydz)));
+                }
+            }
+            // max of fields
+            double max_u = psmax(max(abs(u)));
+            double max_v = psmax(max(abs(v)));
+            double max_w = psmax(max(abs(w)));
+            double max_vel = psmax(max(sqrt(u*u + v*v + w*w)));
+            double max_temp = psmax(max(*tracers[TEMP]));
+            double min_temp = psmin(min(*tracers[TEMP]));
+            double max_salt = psmax(max(*tracers[SALT]));
+            double min_salt = psmin(min(*tracers[SALT]));
+            double max_rho =  psmax(max(eqn_of_state(*tracers[TEMP],*tracers[SALT])));
+            double min_rho =  psmin(min(eqn_of_state(*tracers[TEMP],*tracers[SALT])));
+            // total mass (tracers[RHO] is non-dimensional density)
+            double mass = pssum(sum(eqn_of_state(*tracers[TEMP],*tracers[SALT])*(*dxdydz)));
+
+            if (master()) {
+                // add diagnostics to buffers
+                string header, line;
+                add_diagnostic("Iter", iter,            header, line);
+                add_diagnostic("Clock_time", comp_duration, header, line);
+                add_diagnostic("Time", time,            header, line);
+                add_diagnostic("Max_vel", max_vel,      header, line);
+                add_diagnostic("Max_temperature", max_temp, header, line);
+                add_diagnostic("Min_temperature", min_temp, header, line);
+                add_diagnostic("Max_salinity", max_salt,    header, line);
+                add_diagnostic("Min_salinity", min_salt,    header, line);
+                add_diagnostic("Max_density", max_rho,  header, line);
+                add_diagnostic("Min_density", min_rho,  header, line);
+                add_diagnostic("Mass", mass,            header, line);
+                add_diagnostic("PE_tot", pe_tot,        header, line);
+                if (compute_BPE) {
+                    add_diagnostic("BPE_tot", BPE_tot,  header, line);
+                }
+                if (compute_internal_to_BPE) {
+                    add_diagnostic("BPE_from_int", phi_i,   header, line);
+                }
+                if (compute_dissipation) {
+                    add_diagnostic("Max_diss", max_diss,    header, line);
+                    add_diagnostic("Diss_tot", diss_tot,    header, line);
+                }
+                if (Nx > 1) {
+                    add_diagnostic("Max_u", max_u,  header, line);
+                    add_diagnostic("KE_x", ke_x,    header, line);
+                }
+                if (Ny > 1 || rot_f != 0) {
+                    add_diagnostic("Max_v", max_v,  header, line);
+                    add_diagnostic("KE_y", ke_y,    header, line);
+                }
+                if (Nz > 1) {
+                    add_diagnostic("Max_w", max_w,  header, line);
+                    add_diagnostic("KE_z", ke_z,    header, line);
+                }
+                if (Ny > 1 && Nz > 1 && compute_enstrophy) {
+                    add_diagnostic("Enst_x_tot", enst_x_tot, header, line);
+                    add_diagnostic("Max_vort_x", max_vort_x, header, line);
+                }
+                if (Nx > 1 && Nz > 1 && compute_enstrophy) {
+                    add_diagnostic("Enst_y_tot", enst_y_tot, header, line);
+                    add_diagnostic("Max_vort_y", max_vort_y, header, line);
+                }
+                if (Nx > 1 && Ny > 1 && compute_enstrophy) {
+                    add_diagnostic("Enst_z_tot", enst_z_tot, header, line);
+                    add_diagnostic("Max_vort_z", max_vort_z, header, line);
+                }
+
+                // Write to file
+                if (!(restarting and iter==0))
+                    write_diagnostics(header, line, iter, restarting);
+                // and to the log file
+                fprintf(stdout,"[%d] (%.4g) %.4f: "
+                        "%.4g %.4g %.4g %.4g\n",
+                        iter,comp_duration,time,
+                        max_u,max_v,max_w,max_rho);
+            }
+
+            // Top Surface Stresses
+            if ( compute_stresses_top ) {
+                stresses_top(u, v, w, *Hprime, *temp1, gradient_op, grid_type, mu, time, iter, restarting);
+            }
+            // Bottom Surface Stresses
+            if ( compute_stresses_bottom ) {
+                stresses_bottom(u, v, w, *Hprime, *temp1, gradient_op, grid_type, mu, time, iter, restarting);
+            }
+
+            /* Write to disk if at correct time */
+            if ((time - next_plot) > -1e-6) {
+                plot_number++;
+                comp_duration = MPI_Wtime(); // time just before write (for dump)
+                // Write the arrays
+                write_array(u,"u",plot_number);
+                write_array(w,"w",plot_number);
+                if (Ny > 1 || rot_f != 0)
+                    write_array(v,"v",plot_number);
+                write_array(*tracers[TEMP],"t",plot_number);
+                write_array(*tracers[SALT],"s",plot_number);
+                if (write_pressure)
+                    write_array(pressure,"p",plot_number);
+                // update next plot time
+                next_plot = next_plot + plot_interval;
+
+                // Find average time to write (for dump)
+                clock_time = MPI_Wtime(); // time just after write
+                avg_write_time = (avg_write_time*(plot_number-restart_sequence-1) 
+                        + (clock_time - comp_duration))/(plot_number-restart_sequence);
+                // Print information about plot outputs
+                write_plot_times(time, clock_time, comp_duration, avg_write_time, plot_number, restarting);
+            }
+
+            // see if close to end of compute time and dump
+            check_and_dump(clock_time, real_start_time, compute_time, time, avg_write_time,
+                    plot_number, iter, u, v, w, tracers);
+            // Change dump log file if successfully reached final time
+            successful_dump(plot_number, final_time, plot_interval);
+            // increase counter
+            iter++;
+        }
+
+        // User specified variables to dump
+        void write_variables(DTArray & u,DTArray & v, DTArray & w,
+                vector<DTArray *> & tracers) {
+            write_array(u,"u.dump");
+            write_array(v,"v.dump");
+            write_array(w,"w.dump");
+            write_array(*tracers[TEMP],"t.dump");
+            write_array(*tracers[SALT],"s.dump");
+        }
+
+        // Constructor: Initialize local variables
+        userControl():
+            xx(split_range(Nx)), yy(Ny), zz(Nz),
+            topo(split_range(Nx)), gradient_op(0),
+            plot_number(restart_sequence),
+            next_plot(initial_time + plot_interval)
+    {   compute_quadweights(
+            size_x(),   size_y(),   size_z(),
+            length_x(), length_y(), length_z(),
+            type_x(),   type_y(),   type_z());
+        // Create one-dimensional arrays for the coordinates
+        automatic_grid(MinX, MinY, MinZ, &xx, &yy, &zz);
+    }
+};
+
+/* The ''main'' routine */
+int main(int argc, char ** argv) {
+    /* Initialize MPI.  This is required even for single-processor runs,
+       since the inner routines assume some degree of parallelization,
+       even if it is trivial. */
+    MPI_Init(&argc, &argv);
+
+    real_start_time = MPI_Wtime();     // start of simulation (for dump)
+    /* ------------------ Define parameters from spins.conf --------------------- */
+    options_init();
+
+    option_category("Grid Options");
+    add_option("Lx",&Lx,"Length of tank");
+    add_option("Ly",&Ly,1.0,"Width of tank");
+    add_option("Lz",&Lz,"Height of tank");
+    add_option("Nx",&Nx,"Number of points in X");
+    add_option("Ny",&Ny,1,"Number of points in Y");
+    add_option("Nz",&Nz,"Number of points in Z");
+    add_option("min_x",&MinX,0.0,"Minimum X-value");
+    add_option("min_y",&MinY,0.0,"Minimum Y-value");
+    add_option("min_z",&MinZ,0.0,"Minimum Z-value");
+
+    option_category("Grid expansion options");
+    string xgrid_type, ygrid_type, zgrid_type;
+    add_option("type_x",&xgrid_type,
+            "Grid type in X.  Valid values are:\n"
+            "   FOURIER: Periodic\n"
+            "   FREE_SLIP: Cosine expansion\n"
+            "   NO_SLIP: Chebyhsev expansion");
+    add_option("type_y",&ygrid_type,"FOURIER","Grid type in Y");
+    add_option("type_z",&zgrid_type,"Grid type in Z");
+    add_option("mapped_grid",&mapped,true,"Is the grid mapped?");
+
+    option_category("Topography parameters");
+    add_option("hill_height",&hill_height,"Height of hill");
+    add_option("hill_centre",&hill_centre,"location of hill peak");
+    add_option("hill_width",&hill_width,"Width of hill");
+
+    option_category("Physical parameters");
+    add_option("g",&g,9.81,"Gravitational acceleration");
+    add_option("rot_f",&rot_f,0.0,"Coriolis parameter");
+    add_option("rho_0",&rho_0,1000.0,"Reference density");
+    add_option("visco",&visco,"Viscosity");
+    add_option("kappa_T",&kappa_T,"Diffusivity of heat");
+    add_option("kappa_S",&kappa_S,"Diffusivity of salt");
+
+    option_category("Problem parameters");
+    add_option("delta_T",&delta_T,"Temperature difference");
+    add_option("delta_S",&delta_S,"Salinity difference");
+    add_option("T_0",&T_0,"Background temperature");
+    add_option("S_0",&S_0,"Background salinity");
+    add_option("delta_x",&delta_x,"Horizontal transition half-width");
+    add_option("Lmix",&Lmix,"Width of collapse region");
+
+    option_category("Temporal options");
+    add_option("final_time",&final_time,"Final time");
+    add_option("plot_interval",&plot_interval,"Time between writes");
+    add_option("dt_max",&dt_max,0.0,"Maximum time step. Zero value results in the default");
+
+    option_category("Restart options");
+    add_option("restart",&restarting,false,"Restart from prior output time.");
+    add_option("restart_time",&initial_time,0.0,"Time to restart from");
+    add_option("restart_sequence",&restart_sequence,-1,"Sequence number to restart from");
+
+    option_category("Dumping options");
+    add_option("restart_from_dump",&restart_from_dump,false,"If restart from dump");
+    add_option("compute_time",&compute_time,-1.0,"Time permitted for computation");
+
+    option_category("Other options");
+    add_option("perturb",&perturb,"Initial perturbation in velocity");
+    add_option("compute_enstrophy",&compute_enstrophy,true,"Calculate enstrophy?");
+    add_option("compute_dissipation",&compute_dissipation,true,"Calculate dissipation?");
+    add_option("compute_BPE",&compute_BPE,true,"Calculate BPE?");
+    add_option("compute_internal_to_BPE",&compute_internal_to_BPE,true,
+            "Calculate BPE gained from internal energy?");
+    add_option("compute_stresses_top",&compute_stresses_top,false,"Calculate top surfaces stresses?");
+    add_option("compute_stresses_bottom",&compute_stresses_bottom,false,"Calculate bottom surfaces stresses?");
+    add_option("write_pressure",&write_pressure,false,"Write the pressure field?");
+
+    option_category("Filter options");
+    add_option("f_cutoff",&f_cutoff,0.6,"Filter cut-off frequency");
+    add_option("f_order",&f_order,2.0,"Filter order");
+    add_option("f_strength",&f_strength,20.0,"Filter strength");
+
+    // Parse the options from the command line and config file
+    options_parse(argc,argv);
+
+    /* ------------------ Adjust and check parameters --------------------- */
+    /* Now, make sense of the options received.  Many of these
+     * can be directly used, but the ones of string-type need further procesing. */
+
+    // adjust temporal values when restarting from dump
+    if (restart_from_dump) {
+        adjust_for_dump(restarting, initial_time, restart_sequence,
+                final_time, compute_time, avg_write_time, Num_tracers, Nx, Ny, Nz);
+    }
+
+    // check restart sequence
+    check_restart_sequence(restarting, restart_sequence, initial_time, plot_interval);
+
+    // parse expansion types
+    parse_boundary_conditions(xgrid_type, ygrid_type, zgrid_type, intype_x, intype_y, intype_z);
+    // vector of expansion types
+    grid_type[0] = xgrid_type;
+    grid_type[1] = ygrid_type;
+    grid_type[2] = zgrid_type;
+
+    // adjust Ly for 2D
+    if (Ny==1 and Ly!=1.0) {
+        Ly = 1.0;
+        if (master())
+            fprintf(stdout,"Simulation is 2 dimensional, "
+                    "Ly has been changed to 1.0 for normalization.\n");
+    }
+
+    /* ------------------ Derived parameters --------------------- */
+
+    // Dynamic viscosity
+    mu = visco*rho_0;
+    // Maximum buoyancy frequency (squared) if the initial stratification was stable
+    //N2_max = g*delta_rho/(2*delta_x);
+    // Maximum time step
+    if (dt_max == 0.0) {
+        // if dt_max not given in spins.conf, use this
+        dt_max = 0.1;
+    }
+
+    /* ------------------ Initialize --------------------- */
+
+    // Create an instance of the above class
+    userControl mycode;
+    // Create a flow-evolver that takes its settings from the above class
+    FluidEvolve<userControl> do_stuff(&mycode);
+    // Initialize
+    do_stuff.initialize();
+
+    /* ------------------ Print some parameters --------------------- */
+
+    compute_start_time = MPI_Wtime(); // beginning of simulation (after reading in data)
+    double startup_time = compute_start_time - real_start_time;
+
+    if (master()) {
+        fprintf(stdout,"Dam break problem\n");
+        fprintf(stdout,"Using a %f x %f x %f grid of %d x %d x %d points\n",Lx,Ly,Lz,Nx,Ny,Nz);
+        fprintf(stdout,"g = %f, rot_f = %f, rho_0 = %f\n",g,rot_f,rho_0);
+        fprintf(stdout,"Time between plots: %g s\n",plot_interval);
+        fprintf(stdout,"Initial velocity perturbation: %g\n",perturb);
+        fprintf(stdout,"Filter cutoff = %f, order = %f, strength = %f\n",f_cutoff,f_order,f_strength);
+        fprintf(stdout,"Approx. max buoyancy frequency squared: %g\n",N2_max);
+        fprintf(stdout,"Max time step: %g\n",dt_max);
+        fprintf(stdout,"Start-up time: %.6g s.\n",startup_time);
+    }
+
+    /* ------------------ Run --------------------- */
+    // Run until the end of time
+    do_stuff.do_run(final_time);
+    MPI_Finalize();
+    return 0;
+}
diff --git a/src/cases/quadeos_test/spins.conf b/src/cases/quadeos_test/spins.conf
new file mode 100644
index 0000000000000000000000000000000000000000..fe5c278245ccd17011b34d7b7cd6cd4dbdcef1ff
--- /dev/null
+++ b/src/cases/quadeos_test/spins.conf
@@ -0,0 +1,67 @@
+## Salt-temperature configuration file
+
+# Spatial Parameters
+Lx = 1.0
+Ly = 1.0
+Lz = 0.1
+Nx = 1024
+Ny = 1
+Nz = 128
+min_x = 0
+min_y = 0
+min_z = 0
+
+# Expansion types
+type_x = FREE_SLIP
+type_y = FREE_SLIP
+type_z = FREE_SLIP
+mapped_grid = false
+
+# Physical Parameters
+g = 9.81
+rot_f = 0.0e-3
+rho_0 = 1000.0
+visco = 2e-6
+kappa_T = 2e-6
+kappa_S = 1e-7
+
+# Problem Parameters (delta_rho is percentage)
+delta_T = 25.0
+delta_S = 5.0
+T_0 = 15.0
+S_0 = 0.0
+delta_x = 0.02
+Lmix = 0.1
+
+# Problem topography parameters
+hill_height = 0.1
+hill_centre = 0.5
+hill_width = 0.05
+
+# Temporal Parameters
+final_time = 100
+plot_interval = 1
+#dt_max = 0.0
+
+# Restart Options
+restart = false
+restart_time = 0.0
+restart_sequence = 0
+restart_from_dump = false
+compute_time = -1
+
+# Perturbation Parameter
+perturb = 1e-3
+
+# Filter Parameters
+f_cutoff = 0.6
+f_order = 2.0
+f_strength = 20.0
+
+# secondary diagnostics
+#compute_enstrophy = true
+#compute_dissipation = true
+compute_BPE = false
+compute_internal_to_BPE = false
+#compute_stresses_top = false
+#compute_stresses_bottom = false
diff --git a/src/cases/salt_temp/salt_temp.cpp b/src/cases/salt_temp/salt_temp.cpp
index f48746d395f72b0060c1af7c8d125715e39e2f89..99ecd95bf751660bb07d77df3c9ab730f76e8a7b 100644
--- a/src/cases/salt_temp/salt_temp.cpp
+++ b/src/cases/salt_temp/salt_temp.cpp
@@ -244,7 +244,7 @@ class userControl : public BaseCase {
                 vector<DTArray *> & tracers, vector<DTArray *> & tracers_f) {
             u_f = +rot_f*v;
             v_f = -rot_f*u;
-            w_f = -g*eqn_of_state(*tracers[TEMP],*tracers[SALT]) / rho_0;
+            w_f = -g*(nleos(*tracers[TEMP],*tracers[SALT]) - rho_0)/rho_0;
             *tracers_f[TEMP] = 0;
             *tracers_f[SALT] = 0;
         }