diff --git a/make_deps.sh b/make_deps.sh index 5c8da679b73ad9407fc7e76aa1bd96a713aac737..c1c03a82873de41bb88ddbe4081adbac53b24e59 100755 --- a/make_deps.sh +++ b/make_deps.sh @@ -70,12 +70,12 @@ if [ ! "$BUILD_BLITZ" = "yes" ]; then else echo "Building Blitz++" # Download the Blitz tarball if necessary - if [ ! -e "blitz_2010.tgz" ]; then - mv redist_libs/blitz_2010.tgz ./ + if [ ! -e "blitz_1.0.2.tar.gz" ]; then + mv redist_libs/blitz_1.0.2.tar.gz ./ fi - (tar -xzvf blitz_2010.tgz > /dev/null) || (echo "Untar of Blitz FAILED"; exit 1); - pushd blitz - (./configure --prefix="$CWD" --disable-fortran "${BLITZ_OPTIONS}" > /dev/null) && \ + (tar -xzvf blitz_1.0.2.tar.gz > /dev/null) || (echo "Untar of Blitz FAILED"; exit 1); + pushd blitz-1.0.2 + (autoreconf -vif && ./configure --prefix="$CWD" --disable-fortran "${BLITZ_OPTIONS}" > /dev/null) && \ (make lib > /dev/null) && \ pushd blitz && (make install > /dev/null) && popd && \ pushd lib && (make install > /dev/null) && popd && \ @@ -92,14 +92,14 @@ if [ ! "$BUILD_FFTW" = "yes" ]; then else echo "Building FFTW" # Download FFTW if necessary - if [ ! -e "fftw-3.3.3.tar.gz" ]; then - mv redist_libs/fftw-3.3.3.tar.gz ./ + if [ ! -e "fftw-3.3.9.tar.gz" ]; then + mv redist_libs/fftw-3.3.9.tar.gz ./ fi - (tar -xzvf fftw-3.3.3.tar.gz > /dev/null) + (tar -xzvf fftw-3.3.9.tar.gz > /dev/null) if [ 0 -ne $? ]; then echo "Untar of FFTW FAILED"; exit 1 fi - pushd fftw-3.3.3 + pushd fftw-3.3.9 # The "${FFTW_OPTIONS[@]}" syntax expands FFTW_OPTIONS as an array variable; # this allows for multi-word arguments like 'CFLAGS="-O3 --fast-math"' to # work properly as a single argument from configure's perspective. diff --git a/matlab/spins_QSP_csv.m b/matlab/spins_QSP_csv.m new file mode 100644 index 0000000000000000000000000000000000000000..d544e59fa3bae91a8c6c2480384a0ae9f6cfae0b --- /dev/null +++ b/matlab/spins_QSP_csv.m @@ -0,0 +1,19 @@ +function [T1_max, T1_min, S1_max, S1_min, pdfCount] = spins_QSP_csv(filename) + %% Reads in the csv file and interprets it in the correct way for the user. + %% + %% Input: + %% Filename (string): name of csv file. + %% + %% Output: + %% T1_max (float): upper limit of the maximum-valued bin of T1 tracer + %% T1_min (float): lower limit of the minimum-valued bin of T1 tracer + %% S1_max (float): upper limit of the maximum-valued bin of S1 tracer + %% S1_min (float): lower limit of the minimum-valued bin of S1 tracer + A = importdata(filename); + T1_max = A(1, 1); + T1_min = A(1, 2); + S1_max = A(1, 3); + S1_min = A(1, 4); + pdfCount = A(2:end, :); + pdfCount = pdfCount / sum(sum(pdfCount)); +end diff --git a/src/ESolver.cpp b/src/ESolver.cpp index 656e4ea7e9db368bedddd3f518036739a538939f..4eab40950d4ee123d7365c986312ec4469d1aa1d 100644 --- a/src/ESolver.cpp +++ b/src/ESolver.cpp @@ -1,6 +1,5 @@ #include "TArray.hpp" #include "blitz/array.h" -#include "blitz/tinyvec-et.h" #include "ESolver.hpp" #include "gmres_1d_solver.hpp" #include "gmres_2d_solver.hpp" diff --git a/src/Parformer.cpp b/src/Parformer.cpp index 331a2922a7814e9f108461a76bb33d7c5558f6b5..383bde710afa35632db5c47b0a31495e5543eba2 100644 --- a/src/Parformer.cpp +++ b/src/Parformer.cpp @@ -2,7 +2,6 @@ #include "Parformer.hpp" #include "Par_util.hpp" -#include <blitz/tinyvec-et.h> #include <stdio.h> #include <iostream> diff --git a/src/Science.hpp b/src/Science.hpp index 569b9a6c6c494ddbb583efef4e924702b6e43237..cd5c38f754ead297aca9c7e09f6a338d2d164096 100644 --- a/src/Science.hpp +++ b/src/Science.hpp @@ -13,12 +13,12 @@ extern Array<double,1> _quadw_x, _quadw_y, _quadw_z; // Marek's Overturning Diagnostic -blitz::Array<double,3> overturning_2d(blitz::Array<double,3> const & rho, +blitz::Array<double,3> overturning_2d(blitz::Array<double,3> const & rho, blitz::Array<double,1> const & zgrid, TArrayn::Dimension reduce = TArrayn::thirdDim ); // Read in a 2D file and interpret it as a 2D slice of a 3D array, for // initialization with read-in-data from a program like MATLAB -void read_2d_slice(blitz::Array<double,3> & fillme, const char * filename, +void read_2d_slice(blitz::Array<double,3> & fillme, const char * filename, int Nx, int Ny); void read_2d_restart(blitz::Array<double,3>& fillme, const char* filename, @@ -40,7 +40,7 @@ void compute_baroclinic_vort_x(TArrayn::DTArray & barovortx, TArrayn::DTArray & TArrayn::Grad * gradient_op, const string * grid_type); void compute_baroclinic_vort_y(TArrayn::DTArray & barovorty, TArrayn::DTArray & T, TArrayn::Grad * gradient_op, const string * grid_type); -void compute_baroclinic_vort(TArrayn::DTArray & barovort, TArrayn::DTArray & temp, +void compute_baroclinic_vort(TArrayn::DTArray & barovort, TArrayn::DTArray & temp, TArrayn::DTArray & T, TArrayn::Grad * gradient_op, const string * grid_type, bool v_exist); // Enstrophy density @@ -63,7 +63,7 @@ void compute_BPE_from_internal(double & phi_i, TArrayn::DTArray & rho, double kappa_rho, double rho_0, double g, int Nz, bool dimensional_rho = false); // Quadrature weights -void compute_quadweights(int szx, int szy, int szz, +void compute_quadweights(int szx, int szy, int szz, double Lx, double Ly, double Lz, NSIntegrator::DIMTYPE DIM_X, NSIntegrator::DIMTYPE DIM_Y, NSIntegrator::DIMTYPE DIM_Z); @@ -130,9 +130,165 @@ 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) +*/ + +// lambda2, second eigenvalue of S^2+Omega^2 +void compute_lambda2(TArrayn::DTArray & lambda2, TArrayn::DTArray & u, + TArrayn::DTArray & v, TArrayn::DTArray & w, TArrayn::DTArray & temp1, + TArrayn::DTArray & temp2, TArrayn::Grad * gradient_op, + const string * grid_type, TArrayn::DTArray & A11, TArrayn::DTArray & A12, + TArrayn::DTArray & A13, TArrayn::DTArray & A22, TArrayn::DTArray & A23, + TArrayn::DTArray & A33); + +/* Creates and outputs data for plotting a bivariate histogram with NS*NT bins + * between tracers S1 and T1 to "filename.csv" (don't include file extension). + */ +struct QSPOptions { + int NS; + int NT; + string filename; + double S1_max; + double S1_min; + double T1_max; + double T1_min; + string T1_name; + string S1_name; +}; + +struct QSPData { + TArrayn::DTArray *u; + TArrayn::DTArray *v; + TArrayn::DTArray *w; + TArrayn::DTArray *temp; + TArrayn::DTArray *rho; + TArrayn::DTArray *salinity; + TArrayn::DTArray *custom_T1; + TArrayn::DTArray *custom_S1; + TArrayn::DTArray *xgrid; + TArrayn::DTArray *ygrid; + TArrayn::DTArray *zgrid; + int Nx; + int Ny; + int Nz; + int plotnum; + bool mapped; +}; + +void QSPCount(QSPOptions qsp_options, QSPData qsp_data); + // 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?) @@ -163,7 +319,7 @@ inline double eqn_of_state_dT(double T){ // Constants are from table 4 of the above paper, at pressure 0 // (This is appropriate since this is currently an incompressible // model) - + // Kept the same names for these constants as in eqn_of_state const double c2 = 5.10768e-2; // T term, constant after derivative const double c4 = -7.40849e-3; // T^2 term, 2T after derivative diff --git a/src/Science/QSPCount.cpp b/src/Science/QSPCount.cpp new file mode 100644 index 0000000000000000000000000000000000000000..25fdae08abca743c8d0c48e222fd7722e134f65a --- /dev/null +++ b/src/Science/QSPCount.cpp @@ -0,0 +1,458 @@ +#include "../Science.hpp" +#include "boost/lexical_cast.hpp" +#include <algorithm> +#include <blitz/array.h> +#include <cmath> +#include <cstdlib> +#include <fstream> +#include <ios> +#include <iostream> +#include <limits> +#include <mpi.h> +#include <string> +#include <vector> + +using namespace Transformer; +using namespace blitz; + +class QSPVector { +private: + double *data; + int length; + int rows; + int cols; + +public: + explicit QSPVector(int length) + : data((double *)calloc(length, sizeof(double))), length(length), rows(0), + cols(0) { + if (!data) { + std::cout << "Error! Data could not be initialized.\n"; + } + } + QSPVector(int Nx, int Ny) + : data((double *)calloc(Nx * Ny, sizeof(double))), length(Nx * Ny), + rows(Nx), cols(Ny) { + if (!data) { + std::cout << "Error! Data could not be initialized.\n"; + } + } + double *raw() const { return data; } + int Length() const { return length; } + int Rows() const { return rows; } + int Cols() const { return cols; } + double operator[](int i) const { + assert(i >= 0 && i < length); + return data[i]; + } + double operator()(int row, int col) const { + assert(row >= 0 && row < rows); + assert(col >= 0 && col < cols); + return data[(row * cols) + col]; + } + double &operator()(int row, int col) { + assert(row >= 0 && row < rows); + assert(col >= 0 && col < cols); + return data[(row * cols) + col]; + } + ~QSPVector() { free(data); } +}; + +enum QSPType { + QSP_u, + QSP_v, + QSP_w, + QSP_ke, + QSP_temp, + QSP_rho, + QSP_salinity, + QSP_custom, +}; + +void QSP_write(int local_rank, const QSPVector &local_hist, + const QSPOptions &qsp_options, const QSPData &qsp_data) { + if (local_rank == 0) { + QSPVector glob_hist(qsp_options.NS, qsp_options.NT); + MPI_Reduce(local_hist.raw(), glob_hist.raw(), // send and receive buffers + qsp_options.NS * qsp_options.NT, // Count + MPI_DOUBLE, // datatype + MPI_SUM, 0, // Reduction operator and root process # + MPI_COMM_WORLD); // Communicator + string filename = qsp_options.filename + "." + + boost::lexical_cast<string>(qsp_data.plotnum) + ".csv"; + std::fstream outfile; + outfile.open(filename.c_str(), std::ios_base::out); + if (outfile.is_open()) { + outfile << qsp_options.T1_max << ',' << qsp_options.T1_min << ',' + << qsp_options.S1_max << ',' << qsp_options.S1_min; + for (int i = 4; i < qsp_options.NT; i++) { + outfile << ',' << 0; + } + outfile << std::endl; + for (int ii = 0; ii < qsp_options.NS; ii++) { + outfile << glob_hist(ii, 0); + for (int jj = 1; jj < qsp_options.NT; jj++) { + outfile << ',' << glob_hist(ii, jj); + } + outfile << std::endl; + } + } + } else { + MPI_Reduce(local_hist.raw(), NULL, // send and receive buffers + qsp_options.NS * qsp_options.NT, // count + MPI_DOUBLE, // datatype + MPI_SUM, 0, // Reduction operator and root process + MPI_COMM_WORLD); // Communicator + } +} + +QSPType QSPConvert(const std::string &name) { + QSPType converted_type; + if (name.compare("u") == 0) { + converted_type = QSP_u; + } else if (name.compare("v") == 0) { + converted_type = QSP_v; + } else if (name.compare("w") == 0) { + converted_type = QSP_w; + } else if (name.compare("ke") == 0) { + converted_type = QSP_ke; + } else if (name.compare("temp") == 0) { + converted_type = QSP_temp; + } else if (name.compare("rho") == 0) { + converted_type = QSP_rho; + } else if (name.compare("salinity") == 0) { + converted_type = QSP_salinity; + } else if (name.compare("custom") == 0) { + converted_type = QSP_custom; + } + return converted_type; +} + +TArrayn::DTArray *QSPPtr(const QSPData &qsp_data, const QSPType &type) { + TArrayn::DTArray *ptr = NULL; + + switch (type) { + case QSP_u: + ptr = qsp_data.u; + break; + case QSP_v: + ptr = qsp_data.v; + break; + case QSP_w: + ptr = qsp_data.w; + break; + case QSP_ke: // This is an odd case. + break; + case QSP_rho: + ptr = qsp_data.rho; + break; + case QSP_temp: + ptr = qsp_data.temp; + break; + case QSP_salinity: + ptr = qsp_data.salinity; + break; + case QSP_custom: // Make sure to set the pointer yourself. Can't do it here. + break; + } + + return ptr; +} + +// compute the minimum and maximum values for either tracer, then substitute +// any default values of +- infinity with the global min/max values instead. +void QSPMaxMin(const QSPType &T1_type, const QSPType &S1_type, + TArrayn::DTArray *T1_ptr, TArrayn::DTArray *S1_ptr, + QSPOptions &qsp_options, const QSPData &qsp_data, int i_low, + int j_low, int k_low, int i_high, int j_high, int k_high) { + double double_max = std::numeric_limits<double>::max(); + + // If One of the variables is K.E or rho, we need to hand-roll the max / min + // since, in general, the index of max(u) is the same as the index of max(v) + if (T1_type == QSP_ke || T1_type == QSP_rho || S1_type == QSP_ke || + S1_type == QSP_rho) { + double ke_max = -double_max, ke_min = double_max; + double rho_max = -double_max, rho_min = double_max; + // Main hand-rolled loop + for (int i = i_low; i <= i_high; i++) { + for (int j = j_low; j <= j_high; j++) { + for (int k = k_low; k <= k_high; k++) { + double tmp; + if (T1_type == QSP_ke || S1_type == QSP_ke) { + double ke_current = 0; + tmp = (*qsp_data.u)(i, j, k); + ke_current += tmp * tmp; + if (qsp_data.Ny > 1) { + tmp = (*qsp_data.v)(i, j, k); + ke_current += tmp * tmp; + } + tmp = (*qsp_data.w)(i, j, k); + ke_current += tmp * tmp; + ke_current = 0.5 * ke_current; + ke_max = ke_current > ke_max ? ke_current : ke_max; + ke_min = ke_current < ke_min ? ke_current : ke_min; + } + if (T1_type == QSP_rho || S1_type == QSP_rho) { + double rho_current; + if (qsp_data.rho) { + rho_current = (*qsp_data.rho)(i, j, k); + } else if (qsp_data.temp && !qsp_data.salinity) { + rho_current = eqn_of_state_t((*qsp_data.temp)(i, j, k)); + } else if (qsp_data.temp && qsp_data.salinity) { + rho_current = eqn_of_state((*qsp_data.temp)(i, j, k), + (*qsp_data.salinity)(i, j, k)); + } else { + rho_current = 0; + } + rho_max = rho_current > rho_max ? rho_current : rho_max; + rho_min = rho_current < rho_min ? rho_current : rho_min; + } + } + } + } + double glob_ke_max, glob_ke_min, glob_rho_max, glob_rho_min; + MPI_Allreduce(&ke_max, &glob_ke_max, 1, MPI_DOUBLE, MPI_MAX, + MPI_COMM_WORLD); + MPI_Allreduce(&ke_min, &glob_ke_min, 1, MPI_DOUBLE, MPI_MIN, + MPI_COMM_WORLD); + MPI_Allreduce(&rho_max, &glob_rho_max, 1, MPI_DOUBLE, MPI_MAX, + MPI_COMM_WORLD); + MPI_Allreduce(&rho_min, &glob_rho_min, 1, MPI_DOUBLE, MPI_MIN, + MPI_COMM_WORLD); + switch (T1_type) { + case QSP_ke: + qsp_options.T1_max = + qsp_options.T1_max == double_max ? glob_ke_max : qsp_options.T1_max; + qsp_options.T1_min = + qsp_options.T1_min == -double_max ? glob_ke_min : qsp_options.T1_min; + break; + case QSP_rho: + qsp_options.T1_max = + qsp_options.T1_max == double_max ? glob_rho_max : qsp_options.T1_max; + qsp_options.T1_min = + qsp_options.T1_min == -double_max ? glob_rho_min : qsp_options.T1_min; + break; + default: + qsp_options.T1_max = qsp_options.T1_max == double_max + ? psmax(max(*T1_ptr)) + : qsp_options.T1_max; + qsp_options.T1_min = qsp_options.T1_min == -double_max + ? psmin(min(*T1_ptr)) + : qsp_options.T1_min; + break; + } + switch (S1_type) { + case QSP_ke: + qsp_options.S1_max = + qsp_options.S1_max == double_max ? glob_ke_max : qsp_options.S1_max; + qsp_options.S1_min = + qsp_options.S1_min == -double_max ? glob_ke_min : qsp_options.S1_min; + break; + case QSP_rho: + qsp_options.S1_max = + qsp_options.S1_max == double_max ? glob_rho_max : qsp_options.S1_max; + qsp_options.S1_min = + qsp_options.S1_min == -double_max ? glob_rho_min : qsp_options.S1_min; + break; + default: + qsp_options.S1_max = qsp_options.S1_max == double_max + ? psmax(max(*S1_ptr)) + : qsp_options.S1_max; + qsp_options.S1_min = qsp_options.S1_min == -double_max + ? psmin(min(*S1_ptr)) + : qsp_options.S1_min; + break; + } + } else { // !(cond1 || cond2) == !cond1 && !cond2 + qsp_options.S1_max = psmax(max(*S1_ptr)); + qsp_options.S1_min = psmin(min(*S1_ptr)); + qsp_options.T1_max = psmax(max(*T1_ptr)); + qsp_options.T1_min = psmin(min(*T1_ptr)); + } +} + +void QSPCount(QSPOptions qsp_options, QSPData qsp_data) { + + int local_rank; + MPI_Comm_rank(MPI_COMM_WORLD, &local_rank); + + // Find out what + QSPType S1_type = QSPConvert(qsp_options.S1_name); + QSPType T1_type = QSPConvert(qsp_options.T1_name); + TArrayn::DTArray *S1_ptr = QSPPtr(qsp_data, S1_type); + TArrayn::DTArray *T1_ptr = QSPPtr(qsp_data, T1_type); + + if (T1_type == QSP_custom) { + T1_ptr = qsp_data.custom_T1; + } + + if (S1_type == QSP_custom) { + S1_ptr = qsp_data.custom_S1; + } + + if ((!S1_ptr && S1_type != QSP_ke) || (!T1_ptr && T1_type != QSP_ke)) { + std::cout << "Not enough data was provided for the requested tracer. " + "Aborting...\n"; + return; + } + + int i_low, j_low, k_low, i_high, j_high, k_high; + + { + TArrayn::DTArray *temp_ptr; + if (S1_ptr) { // If S1 is not ke + temp_ptr = S1_ptr; + } else { // If S1 is ke we know u must exist + temp_ptr = qsp_data.u; + } + i_low = temp_ptr->lbound(blitz::firstDim); + j_low = temp_ptr->lbound(blitz::secondDim); + k_low = temp_ptr->lbound(blitz::thirdDim); + i_high = temp_ptr->ubound(blitz::firstDim); + j_high = temp_ptr->ubound(blitz::secondDim); + k_high = temp_ptr->ubound(blitz::thirdDim); + } + + double double_max = std::numeric_limits<double>::max(); + if (qsp_options.T1_max == double_max || qsp_options.S1_max == double_max || + qsp_options.T1_min == -double_max || qsp_options.S1_min == -double_max) { + QSPMaxMin(T1_type, S1_type, T1_ptr, S1_ptr, qsp_options, qsp_data, i_low, + j_low, k_low, i_high, j_high, k_high); + } + + double hS = + (qsp_options.S1_max - qsp_options.S1_min) / (double)qsp_options.NS; + double hT = + (qsp_options.T1_max - qsp_options.T1_min) / (double)qsp_options.NT; + double hS_inv = 1 / hS; + double hT_inv = 1 / hT; + + QSPVector local_hist(qsp_options.NS, qsp_options.NT); + QSPVector global_z_max(qsp_data.Nx, qsp_data.Ny); + QSPVector global_z_min(qsp_data.Nx, qsp_data.Ny); + // Find the range of Lz values per 2D-slice + if (qsp_data.mapped) { + QSPVector local_z_max(qsp_data.Nx, qsp_data.Ny); + QSPVector local_z_min(qsp_data.Nx, qsp_data.Ny); + // We are slicing as if we are doing zgrid[i, j, :] + for (int ii = i_low; ii <= i_high; ii++) { + for (int jj = j_low; jj <= j_high; jj++) { + // min is set to the highest possible value so it always gets changed + double tmp_z_min = std::numeric_limits<double>::max(); + // max is set to the lowest possible value so it always gets changed + double tmp_z_max = -tmp_z_min; + double tmp; + for (int kk = k_low; kk <= k_high; kk++) { + tmp = (*qsp_data.zgrid)(ii, jj, kk); + if (tmp > tmp_z_max) { + tmp_z_max = tmp; + } else if (tmp < tmp_z_min) { + tmp_z_min = tmp; + } + } + local_z_max(ii, jj) = tmp_z_max; + local_z_min(ii, jj) = tmp_z_min; + } + } + MPI_Allreduce(local_z_min.raw(), global_z_min.raw(), + qsp_data.Nx * qsp_data.Ny, MPI_DOUBLE, MPI_MIN, + MPI_COMM_WORLD); + MPI_Allreduce(local_z_max.raw(), global_z_max.raw(), + qsp_data.Nx * qsp_data.Ny, MPI_DOUBLE, MPI_MAX, + MPI_COMM_WORLD); + } + + // Main loop for QSP + double Tval, Sval, tmp; + for (int i = i_low; i <= i_high; i++) { + for (int j = j_low; j <= j_high; j++) { + for (int k = k_low; k <= k_high; k++) { + + switch (T1_type) { + case QSP_ke: + Tval = 0; + tmp = (*qsp_data.u)(i, j, k); + Tval += tmp * tmp; + tmp = (*qsp_data.w)(i, j, k); + Tval += tmp * tmp; + if (qsp_data.Ny > 1) { + tmp = (*qsp_data.v)(i, j, k); + Tval += tmp * tmp; + } + Tval = 0.5 * Tval; + break; + case QSP_rho: + tmp = (*T1_ptr)(i, j, k); + Tval = eqn_of_state_t(tmp); + break; + default: + Tval = (*T1_ptr)(i, j, k); + break; + } + + switch (S1_type) { + case QSP_ke: + Sval = 0; + tmp = (*qsp_data.u)(i, j, k); + Sval += tmp * tmp; + tmp = (*qsp_data.w)(i, j, k); + Sval += tmp * tmp; + if (qsp_data.Ny > 1) { + tmp = (*qsp_data.v)(i, j, k); + Sval += tmp * tmp; + } + Sval = 0.5 * Sval; + break; + case QSP_rho: + tmp = (*S1_ptr)(i, j, k); + Sval = eqn_of_state_t(tmp); + break; + default: + Sval = (*S1_ptr)(i, j, k); + break; + } + + int idxS = floor((Sval - qsp_options.S1_min) * hS_inv); + int idxT = floor((Tval - qsp_options.T1_min) * hT_inv); + idxS = std::max(std::min(idxS, qsp_options.NS - 1), 0); + idxT = std::max(std::min(idxT, qsp_options.NT - 1), 0); + + double volume_weight; + if (qsp_data.mapped) { + // Calculate the Lz range + double Lzmax_now = global_z_max(i, j); + double Lzmin_now = global_z_min(i, j); + + // Calculate the arc length + double arc, z_high, z_low, cos_high, cos_low; + if (k > 0 && k < qsp_data.Nz - 1) { + z_high = (double)(k + 1); + z_low = (double)(k - 1); + } else if (k == 0) { + z_high = (double)(k + 1); + z_low = (double)(k); + } else if (k == qsp_data.Nz - 1) { + z_high = (double)(k); + z_low = (double)(k - 1); + } else { // Failure + std::cout << "k was out of bounds somehow. Failing...\n"; + return; + } + cos_high = std::cos(M_PI * z_high / (double)qsp_data.Nz); + cos_low = std::cos(M_PI * z_low / (double)qsp_data.Nz); + arc = 0.5 * (cos_low - cos_high); + + // Calculate the volume weight + volume_weight = arc * (Lzmax_now - Lzmin_now); + } else { + volume_weight = 1.0; + } + + local_hist(idxS, idxT) += volume_weight; + } + } + } + + MPI_Barrier(MPI_COMM_WORLD); // Wait for everyone to finish + QSP_write(local_rank, local_hist, qsp_options, qsp_data); +} diff --git a/src/Science/compute_lambda2.cpp b/src/Science/compute_lambda2.cpp new file mode 100644 index 0000000000000000000000000000000000000000..abd08d62d940c41c535bfe9a72b3e48cc179ff42 --- /dev/null +++ b/src/Science/compute_lambda2.cpp @@ -0,0 +1,161 @@ +#include "../Science.hpp" +#include <blitz/array.h> +#include <string> +#include <math.h> + +using namespace Transformer; +using namespace blitz; + +// Calculates the second largest eigenvalue of S^2+Omega^2 +// S and Omega are the symmetric and antisymmetric components of u_i,j, respectively +// Note: S^2+Omega^2 = 0.5*(u_{i,k}u_{k,j}+u_{k,i}u_{j,k}) +// This is used to find coherent structures correlated with eddies in turbulent flow +void compute_lambda2(TArrayn::DTArray & lambda2, TArrayn::DTArray & u, + TArrayn::DTArray & v, TArrayn::DTArray & w, TArrayn::DTArray & temp1, + TArrayn::DTArray & temp2, TArrayn::Grad * gradient_op, + const string * grid_type, TArrayn::DTArray & A11, TArrayn::DTArray & A12, + TArrayn::DTArray & A13, TArrayn::DTArray & A22, TArrayn::DTArray & A23, + TArrayn::DTArray & A33) { + // Set-up + S_EXP expan[3]; + assert(gradient_op); + TArrayn::DTArray * A_ref; //This will hold references to the components + TArrayn::DTArray * vel_mm; //This will hold the velocities + TArrayn::DTArray * vel_nn; //This will hold the velocities + std::string vel_labs[3] = {"u","v","w"}; + + int ctr = 0; + // Construct S^2+Omega^2 + for (int mm=0; mm<3; mm++){ + for (int nn=mm; nn<3; nn++){ + // Choose which component will be constructed + if (ctr==0){A_ref = &A11;} + else if (ctr==1){A_ref = &A12;} + else if (ctr==2){A_ref = &A13;} + else if (ctr==3){A_ref = &A22;} + else if (ctr==4){A_ref = &A23;} + else if (ctr==5){A_ref = &A33;} + + // A switch for the velocities + // Useful for computing the u_{i,k}u_{j,k} terms + + if (mm==0){vel_mm = &u;} + else if (mm==1){vel_mm = &v;} + else if(mm==2){vel_mm = &w;} + if (nn==0){vel_nn = &u;} + else if (nn==1){vel_nn = &v;} + else if (nn==2){vel_nn = &w;} + + // u_{i,k}u_{k,j} + // Get u_{i,x} + find_expansion(grid_type, expan, vel_labs[mm]); + gradient_op->setup_array(vel_mm,expan[0],expan[1],expan[2]); + gradient_op->get_dx(&temp1,false); + // Get u_{x,j} + find_expansion(grid_type, expan, "u"); + gradient_op->setup_array(&u,expan[0],expan[1],expan[2]); + if (nn==0) { gradient_op->get_dx(&temp2,false); } + else if (nn==1) { gradient_op->get_dy(&temp2,false); } + else if (nn==2) { gradient_op->get_dz(&temp2,false); } + *A_ref = temp1 * temp2; + // Get u_{i,y} + find_expansion(grid_type, expan, vel_labs[mm]); + gradient_op->setup_array(vel_mm,expan[0],expan[1],expan[2]); + gradient_op->get_dy(&temp1,false); + // Get u_{y,j} + find_expansion(grid_type, expan, "v"); + gradient_op->setup_array(&v,expan[0],expan[1],expan[2]); + if (nn==0) { gradient_op->get_dx(&temp2,false); } + else if (nn==1) { gradient_op->get_dy(&temp2,false); } + else if (nn==2) { gradient_op->get_dz(&temp2,false); } + *A_ref = temp1 * temp2; + // Get u_{i,z} + find_expansion(grid_type, expan, vel_labs[mm]); + gradient_op->setup_array(vel_mm,expan[0],expan[1],expan[2]); + gradient_op->get_dz(&temp1,false); + // Get u_{z,j} + find_expansion(grid_type, expan, "w"); + gradient_op->setup_array(&w,expan[0],expan[1],expan[2]); + if (nn==0) { gradient_op->get_dx(&temp2,false); } + else if (nn==1) { gradient_op->get_dy(&temp2,false); } + else if (nn==2) { gradient_op->get_dz(&temp2,false); } + *A_ref = temp1 * temp2; + + + // u_{k,i}u_{j,k} + // Get u_{x,i} + find_expansion(grid_type, expan, "u"); + gradient_op->setup_array(&u,expan[0],expan[1],expan[2]); + if (mm==0) { gradient_op->get_dx(&temp1,false); } + else if (mm==1) { gradient_op->get_dy(&temp1,false); } + else if (mm==2) { gradient_op->get_dz(&temp1,false); } + // Get u_{j,x} + find_expansion(grid_type, expan, vel_labs[nn]); + gradient_op->setup_array(vel_nn,expan[0],expan[1],expan[2]); + gradient_op->get_dx(&temp2,false); + *A_ref = temp1 * temp2; + // Get u_{y,i} + find_expansion(grid_type, expan, "v"); + gradient_op->setup_array(&v,expan[0],expan[1],expan[2]); + if (mm==0) { gradient_op->get_dx(&temp1,false); } + else if (mm==1) { gradient_op->get_dy(&temp1,false); } + else if (mm==2) { gradient_op->get_dz(&temp1,false); } + // Get u_{j,y} + find_expansion(grid_type, expan, vel_labs[nn]); + gradient_op->setup_array(vel_nn,expan[0],expan[1],expan[2]); + gradient_op->get_dy(&temp2,false); + *A_ref = temp1 * temp2; + // Get u_{z,i} + find_expansion(grid_type, expan, "w"); + gradient_op->setup_array(&w,expan[0],expan[1],expan[2]); + if (mm==0) { gradient_op->get_dx(&temp1,false); } + else if (mm==1) { gradient_op->get_dy(&temp1,false); } + else if (mm==2) { gradient_op->get_dz(&temp1,false); } + // Get u_{j,z} + find_expansion(grid_type, expan, vel_labs[nn]); + gradient_op->setup_array(vel_nn,expan[0],expan[1],expan[2]); + gradient_op->get_dz(&temp2,false); + *A_ref = temp1 * temp2; + + *A_ref = (*A_ref)*0.5; + ctr++; + } + } + + // Now for the eigenvalue algorithm from: + // https://en.wikipedia.org/wiki/Eigenvalue_algorithm#3%C3%973_matrices + + // In the above article, we transform A into B=(1/p)*(A-qI) + // Then, lambda2 = q + 2*p*cos(arccos(det(B)/2)/3 + k*pi/3), k=0,1,2 + // Define q + temp1 = (A11+A22+A33)/3.0; + // Define p + temp2 = pow2(A11-temp1) + pow2(A22-temp1) + pow2(A33-temp1) +2.0*(pow2(A12)+pow2(A13)+pow2(A23)); + temp2 = sqrt(temp2/6.0); + + // Transform A + A11 = (A11-temp1)/temp2; + A22 = (A22-temp1)/temp2; + A33 = (A33-temp1)/temp2; + A12 = A12/temp2; + A13 = A13/temp2; + A23 = A23/temp2; + + // Calculate the determinant, using lambda2 as a dummy array + lambda2 = 0.5*(A11*(A22*A33-A23*A23)-A12*(A12*A33-A13*A23)+A13*(A12*A23-A13*A22)); + + // Since det(B)/2 feeds into acos, make sure it's between -1 and 1 + for (int i = lambda2.lbound(blitz::firstDim); i <= lambda2.ubound(blitz::firstDim); i++) { // outer loop over slowest-varying dimension + for (int k = lambda2.lbound(blitz::thirdDim); k <= lambda2.ubound(blitz::thirdDim); k++) { // middle loop over next-slowest varying dimension + for (int j = lambda2.lbound(blitz::secondDim); j <= lambda2.ubound(blitz::secondDim); j++) { // inner loop over fastest varying dimeion + if (isnan(lambda2(i,j,k))) {lambda2(i,j,k) = 0;} + else if (lambda2(i,j,k) < -1.0) {lambda2(i,j,k) = -1.0;} + else if (lambda2(i,j,k) > 1.0) {lambda2(i,j,k) = 1.0;} + // first conditional works as a safety check because the det is NaN iff temp2=0; in this case the 2nd eig is temp1 + }}} + + // Now calculate the actual lamba2. + lambda2 = temp1+2.0*temp2*cos(acos(lambda2)/3.0+4.0*M_PI/3.0); + + +} 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/Sorter.cpp b/src/Sorter.cpp index 27836927fd8cf6304b09a56fe88c988b0ad1108e..21b008b84e61bc5e0bab79f1f086418f81056639 100644 --- a/src/Sorter.cpp +++ b/src/Sorter.cpp @@ -10,7 +10,6 @@ #include <vector> #include <algorithm> #include <stdlib.h> -#include <blitz/tinyvec-et.h> #include "Sorter.hpp" diff --git a/src/Splits.cpp b/src/Splits.cpp index 1e366bee7578ab2e7cd6f5f8fa87ff0abf8240b7..afb5162e76e7404e68b999170432d4e713c305e2 100644 --- a/src/Splits.cpp +++ b/src/Splits.cpp @@ -2,7 +2,6 @@ #include <vector> #include <blitz/array.h> -#include <blitz/tinyvec-et.h> #include <stdio.h> #include <iostream> #include <complex> @@ -121,7 +120,7 @@ src_temp(0), dst_temp(0), issue_warning(true) { rec_types.resize(num_proc); // make sure our vector can hold everything for (int i = 0; i < num_proc; i++) { - // We'll have to use the MPI_Type_struct syntax, so build arrays: + /*// We'll have to use the MPI_Type_struct syntax, so build arrays: MPI_Datatype types[2] = {vec_type,MPI_UB}; int counts[2]; counts[0] = extent_from[i]; counts[1] = 1; // Now, set displacements to set the "end" of the datatype a full @@ -129,8 +128,23 @@ src_temp(0), dst_temp(0), issue_warning(true) { MPI_Aint displs[2] = {0,sizeof(T)*sizes[untouched_dim]*sizes[from_dim]}; // And make the type - MPI_Type_struct(2,counts,displs,types,&rec_types[i]); + MPI_Type_struct(2,counts,displs,types,&rec_types[i]);*/ + + // This code formerly used the MPI_UB marker to define the upper bound + // of the derived datatype. This was deprecated as part of the MPI-2 + // standard, and it was removed in MPI-3. The replacement is to use + // MPI_Type_create_resized to create a resized type directly. + + // First, create the multi-vector containing the data: + MPI_Datatype multivec; + MPI_Type_contiguous(extent_from[i],vec_type,&multivec); + + MPI_Type_create_resized(multivec, 0, // base type, lower bound + sizeof(T)*sizes[untouched_dim]*sizes[from_dim], // extent + &rec_types[i]); // new type + MPI_Type_commit(&rec_types[i]); + MPI_Type_free(&multivec); /* Impelementation note: one optimization possible here is to collapse this typelist. This impelementation makes no assumptions about how many part-rows are received per processor, but our default splitting diff --git a/src/TArray.cpp b/src/TArray.cpp index 5137e9246470d394ba6f4945bc1e9d2906848bb3..d10b09d13d04ee926293b0810251afb4115d04ff 100644 --- a/src/TArray.cpp +++ b/src/TArray.cpp @@ -1,6 +1,5 @@ #include "TArray.hpp" #include "Plan_holder.hpp" -#include <blitz/tinyvec-et.h> #include <assert.h> #include <fftw3.h> // FFTW /* Implementation of TArray transform functions for the relevant cases. diff --git a/src/T_util.cpp b/src/T_util.cpp index 7503f132e5c428f1f71124a334c87f4fa61a8e67..7913ee76aafaddfba21dc5224e217ab9f81b0ab0 100644 --- a/src/T_util.cpp +++ b/src/T_util.cpp @@ -1,7 +1,6 @@ #include "T_util.hpp" #include "TArray.hpp" #include <blitz/array.h> -#include <blitz/tinyvec-et.h> #include "Par_util.hpp" #include <complex> #include <string> diff --git a/src/VERSION b/src/VERSION index 2944c9e8063acc3ac0de00f2350a9e70fc6318e9..81e4488aef22f8c969bdd8aada2b50890c93a412 100644 --- a/src/VERSION +++ b/src/VERSION @@ -1,3 +1,3 @@ MAJOR_VERSION=2 MINOR_VERSION=1 -PATCH_VERSION=6 +PATCH_VERSION=9 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/derivatives/derivatives.cpp b/src/cases/derivatives/derivatives.cpp index ab383f08ef2f6d100e50a985837abb1b193969a9..3ed017656a9a31b9b916fc460af83303bc6ec5eb 100644 --- a/src/cases/derivatives/derivatives.cpp +++ b/src/cases/derivatives/derivatives.cpp @@ -8,6 +8,11 @@ #include "../../Options.hpp" // config-file parser #include "../../Science.hpp" // Science content +// Defines limits of intrinsic types. Used as default values for +// T1_max, T1_min, S1_max and/orS1_min +#include <cassert> +#include <limits> + // Tensor variables for indexing blitz::firstIndex ii; blitz::secondIndex jj; @@ -29,6 +34,13 @@ const int x_ind = 0; const int y_ind = 1; const int z_ind = 2; +// QSP variables +double T1_max, S1_max, T1_min, S1_min; +string T1_name, S1_name; +int NT, NS; +string QSP_filename; +bool use_salinity; + // physical parameters double visco; // viscosity (m^2/s) double rho_0; // reference density (kg/m^3) @@ -38,7 +50,7 @@ double g; // acceleration due to gravity (m/s^2) int plotnum; // Derivative options -string deriv_filenames; // file name to take derivative of +string deriv_filenames; // file name to take derivative of int start_sequence; // output number to start taking derivatives at int final_sequence; // output number to stop taking derivatives at int step_sequence; // step between outputs to take derivatives @@ -52,7 +64,9 @@ bool do_enst_stretch; // Do enstrophy production term from vortex bool do_Q; // Do second invariant of grad(u,v,w)? bool do_R; // Do third invariant/det of grad(u,v,w)? bool do_Q_and_R; // Do the second and third invariants? +bool do_lambda2; // Do lambda2/Hussain's Lambda? bool v_exist; // Does the v field exist? +bool do_hist; // Create QSP data? /* ------------------ Adjust the class --------------------- */ @@ -63,6 +77,8 @@ class userControl : public BaseCase { DTArray deriv_var; // array for derivative DTArray *temp1, *temp2, *temp3; // arrays for vortex stretching / enstrophy production DTArray *temp4; // array for storing temporary array for baroclinic vorticity + DTArray *A11, *A12, *A13, *A22, *A23, *A33; //Arrays for components of S^2+Omega^2 + DTArray *xgrid, *ygrid, *zgrid; // Arrays for storing grid data /* Size of domain */ double length_x() const { return Lx; } @@ -85,7 +101,7 @@ class userControl : public BaseCase { /* Set other things */ double get_visco() const { return visco; } int get_restart_sequence() const { return plotnum; } - + /* Read grid (if mapped) */ bool is_mapped() const { return mapped; } void do_mapping(DTArray & xg, DTArray & yg, DTArray & zg) { @@ -114,17 +130,43 @@ class userControl : public BaseCase { //string prev_deriv, base_field; vector<string> fields; // vector of fields to take derivatives split(deriv_filenames.c_str(), ' ', fields); // populate that vector - if ( do_vort_stretch or do_enst_stretch or do_Q or do_R or do_Q_and_R ) { + if ( do_vort_stretch or do_enst_stretch or do_Q or do_R or + do_Q_and_R or do_lambda2 or do_hist ) { + temp1 = alloc_array(Nx,Ny,Nz); temp2 = alloc_array(Nx,Ny,Nz); - if ( do_enst_stretch ) - temp3 = alloc_array(Nx,Ny,Nz); + + // If user asks to grab the grids, we allocate arrays to store + // them in memory + if (mapped) { + xgrid = alloc_array(Nx, Ny, Nz); + if (Ny > 1) { + ygrid = alloc_array(Nx, Ny, Nz); + } + zgrid = alloc_array(Nx, Ny, Nz); + } else { + // If user doesn't want to grab grids, we make sure not to + // allocate arrays for them and to set the pointers to NULL. + xgrid = NULL; + ygrid = NULL; + zgrid = NULL; + } + + if ( do_enst_stretch or do_hist ) temp3 = alloc_array(Nx,Ny,Nz); } - - if ( do_barvor ) { + + if ( do_barvor ) { temp4 = alloc_array(Nx,Ny,Nz); } + if ( do_lambda2 ) { + A11 = alloc_array(Nx,Ny,Nz); + A12 = alloc_array(Nx,Ny,Nz); + A13 = alloc_array(Nx,Ny,Nz); + A22 = alloc_array(Nx,Ny,Nz); + A23 = alloc_array(Nx,Ny,Nz); + A33 = alloc_array(Nx,Ny,Nz); + } // Compute derivatives at each requested output for ( plotnum = start_sequence; plotnum <= final_sequence; plotnum = plotnum + step_sequence ) { @@ -144,7 +186,7 @@ class userControl : public BaseCase { // parse for expansion type find_expansion(grid_type, expan, fields[var_num]); - // read the field and setup for derivative + // read the field and setup for derivative if ( fields[var_num] == "v" ) { saved_v = true; init_tracer_restart(fields[var_num],v); @@ -214,7 +256,7 @@ class userControl : public BaseCase { } } } - + // Baroclinic vorticity if ( do_barvor ) { @@ -229,23 +271,23 @@ class userControl : public BaseCase { compute_baroclinic_vort_x(deriv_var, u, gradient_op, grid_type); deriv_var=deriv_var*g/rho_0; write_array(deriv_var,"barx",plotnum); - if (master()) fprintf(stdout,"Completed the write for barx.%d\n",plotnum); + if (master()) fprintf(stdout,"Completed the write for barx.%d\n",plotnum); } compute_baroclinic_vort_y(deriv_var, u, gradient_op, grid_type); deriv_var=deriv_var*g/rho_0; write_array(deriv_var,"bary",plotnum); if (master()) fprintf(stdout,"Completed the write for bary.%d\n",plotnum); - + // Restart u u = 0; } - // read in fields (if not already stored in memory) + // read in fields (if not already stored in memory) if ( do_vor_x or do_vor_y or do_vor_z or - do_enstrophy or do_dissipation or - do_vort_stretch or do_enst_stretch or - do_Q or do_R or do_Q_and_R ) { + do_enstrophy or do_dissipation or + do_vort_stretch or do_enst_stretch or + do_Q or do_R or do_Q_and_R or do_lambda2 or do_hist ) { // u init_tracer_restart("u",u); // v @@ -289,7 +331,7 @@ class userControl : public BaseCase { write_array(deriv_var,"vortz",plotnum); if (master()) fprintf(stdout,"Completed the write for vortz.%d\n",plotnum); - } + } // Calculate Enstrophy if ( do_enstrophy ) { @@ -363,7 +405,7 @@ class userControl : public BaseCase { write_array(deriv_var,"Q",plotnum); if (master()) fprintf(stdout,"Completed the write for Q.%d\n",plotnum); } - + // Calculate R/third invariant of grad(u,v,w) if ( do_R or do_Q_and_R ) { R_invt(deriv_var, u, v, w, *temp1, *temp2, gradient_op, grid_type, v_exist); @@ -372,6 +414,82 @@ class userControl : public BaseCase { write_array(deriv_var,"R",plotnum); if (master()) fprintf(stdout,"Completed the write for R.%d\n",plotnum); } + + // Calculate lambda2/second eigenvalue of S^2+Omega^2 + if ( do_lambda2 ){ + compute_lambda2( deriv_var, u, v, w, *temp1, *temp2, gradient_op, + grid_type, *A11, *A12, *A13, *A22, *A23, *A33); + double max_var = psmax(max(abs(deriv_var))); + if (master()) fprintf(stdout,"Max lam2: %.6g\n",max_var); + write_array(deriv_var,"lam2",plotnum); + if (master()) fprintf(stdout,"Completed the write for lam2.%d\n",plotnum); + + } + + // Compute QSP data. The code promises to not mutate the arrays, + // nor to make deep copies of them + if ( do_hist ){ + + // If user asked to grab the grids, we populate the grids + // with the correct data from disk + if (mapped) { + do_mapping(*xgrid, *ygrid, *zgrid); + } else { + // Make sure that if the user didn't want us to grab the + // grids then we haven't allocated data to store them! + assert(xgrid == NULL); + assert(ygrid == NULL); + assert(zgrid == NULL); + } + + QSPOptions qsp_opts; + qsp_opts.S1_name = S1_name; + qsp_opts.T1_name = T1_name; + qsp_opts.filename = QSP_filename; + qsp_opts.NS = NS; + qsp_opts.NT = NT; + qsp_opts.S1_max = S1_max; + qsp_opts.S1_min = S1_min; + qsp_opts.T1_max = T1_max; + qsp_opts.T1_min = T1_min; + + QSPData qsp_data; + qsp_data.mapped = mapped; + qsp_data.Nx = Nx; + qsp_data.Ny = Ny; + qsp_data.Nz = Nz; + qsp_data.plotnum = plotnum; + qsp_data.u = &u; + qsp_data.v = &v; + qsp_data.w = &w; + qsp_data.xgrid = xgrid; + qsp_data.ygrid = ygrid; + qsp_data.zgrid = zgrid; + + qsp_data.temp = NULL; + qsp_data.rho = NULL; + qsp_data.salinity = NULL; + + if (use_salinity) { + init_tracer_restart("s", *temp1); + qsp_data.salinity = temp1; + } + if (T1_name.compare("temp") == 0 || S1_name.compare("temp") == 0) { + init_tracer_restart("t", *temp2); + qsp_data.temp = temp2; + } + if (T1_name.compare("rho") == 0 || S1_name.compare("rho") == 0) { + init_tracer_restart("rho", *temp3); + qsp_data.rho = temp3; + } + + QSPCount(qsp_opts, qsp_data); + + if (master()) { + fprintf(stdout, "Completed the write for QSP.%d\n", plotnum); + } + } + } } @@ -419,7 +537,7 @@ int main(int argc, char ** argv) { add_option("type_z",&zgrid_type,"Grid type in Z"); option_category("Physical parameters"); - add_option("visco",&visco,0.0,"Viscosity"); + add_option("visco",&visco,1.0,"Viscosity"); add_option("rho_0",&rho_0,1000.0,"Reference Density"); add_option("g",&g,9.81,"Acceleration due to gravity"); @@ -443,6 +561,18 @@ int main(int argc, char ** argv) { add_option("do_Q",&do_Q,false,"Calculate Q?"); add_option("do_R",&do_R,false,"Calculate R?"); add_option("do_Q_and_R",&do_Q_and_R,false,"Calculate Q and R?"); + add_option("do_lambda2",&do_lambda2,false,"Calculate Lambda2?"); + add_option("do_hist",&do_hist,false,"Create QSP Data?"); + add_option("T1",&T1_name,"u", "Name of tracer 1 for QSP. Valid values are rho,u,v,w,temp or ke"); + add_option("S1",&S1_name,"w", "Name of tracer 2 for QSP. Valid values are rho,u,v,w,temp or ke"); + add_option("T1_max",&T1_max,std::numeric_limits<double>::max(), "Maximum explicit bin for T1 in QSP."); + add_option("T1_min",&T1_min,std::numeric_limits<double>::min(), "Minimum explicit bin for T1 in QSP."); + add_option("S1_max",&S1_max,std::numeric_limits<double>::max(), "Maximum explicit bin for S1 in QSP."); + add_option("S1_min",&S1_min,std::numeric_limits<double>::min(), "Minimum explicit bin for S1 in QSP."); + add_option("salinity",&use_salinity, false, "Should salinity be read in from filename s?."); + add_option("QSP_filename",&QSP_filename,"QSP_default", "Filename to save data to. Don't include file extension."); + add_option("NS",&NS,10,"Number of bins for tracer S"); + add_option("NT",&NT,10,"Number of bins for tracer T"); add_option("v_exist",&v_exist,"Does the v field exist?"); // Parse the options from the command line and config file options_parse(argc,argv); @@ -465,6 +595,17 @@ int main(int argc, char ** argv) { fprintf(stdout,"Simulation is 2 dimensional, " "Ly has been changed to 1.0 for normalization.\n"); } + if (visco==1.0){ + if (master()) + fprintf(stdout,"You may have forgotten to specify viscosity, " + "Using default value visco = 1.\n"); + } + + if (rho_0==1000.0){ + if (master()) + fprintf(stdout,"You may have forgotten to specify reference density, " + "Using default value rho_0 = 1000.\n"); + } /* ------------------ Do stuff --------------------- */ userControl mycode; // Create an instantiated object of the above class diff --git a/src/cases/derivatives/spins.conf b/src/cases/derivatives/spins.conf index 352e07a7d058b22f19f66eb1deb0da4c008cb935..3d05b5ba8bed408714ff6da43f738ac245dee918 100644 --- a/src/cases/derivatives/spins.conf +++ b/src/cases/derivatives/spins.conf @@ -40,4 +40,13 @@ do_enst_stretch = false do_barvor = false do_Q = false do_R = false -do_Q_and_R = true +do_Q_and_R = false +do_lambda2 = false + +# QSP Parameters +do_hist = false +T1 = temp +S1 = ke +QSP_filename = QSP +NT = 10 +NS = 10 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/qsp/qsp.cpp b/src/cases/qsp/qsp.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3dca0407e408fa5b77c63b6296929c857f70efbc --- /dev/null +++ b/src/cases/qsp/qsp.cpp @@ -0,0 +1,305 @@ +/* Derivative case file for computing derivatives of existing fields */ +// only able to return first and second derivatives + +/* ------------------ Top matter --------------------- */ + +// Required headers +#include "../../BaseCase.hpp" // contains default class +#include "../../Options.hpp" // config-file parser +#include "../../Science.hpp" // Science content + +#include <cassert> +#include <limits> +#include <string> + +// 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 +// Mapped grid? +bool mapped; +// Grid types +DIMTYPE intype_x, intype_y, intype_z; +string grid_type[3]; +// Expansion types and indices +S_EXP expan[3]; +const int x_ind = 0; +const int y_ind = 1; +const int z_ind = 2; + +// QSP variables +double T1_max, S1_max, T1_min, S1_min; +std::string T1_name, S1_name; +int NT, NS; +std::string QSP_filename; +bool use_salinity, read_rho; +std::string salinity_filename, temp_filename, rho_filename; +std::string custom_T1_filename, custom_S1_filename; +const double double_max = std::numeric_limits<double>::max(); + +// current output number +int plotnum; + +// Derivative options +int start_sequence; // output number to start taking derivatives at +int final_sequence; // output number to stop taking derivatives at +int step_sequence; // step between outputs to take derivatives + // streching/tilting? +bool v_exist; // Does the v field exist? + +/* ------------------ Adjust the class --------------------- */ + +class userControl : public BaseCase { +public: + /* Initialize things */ + DTArray *xgrid, *ygrid, *zgrid; // Arrays for storing grid data + + /* 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; } + + /* Set other things */ + int get_restart_sequence() const { return plotnum; } + + /* Read grid (if mapped) */ + bool is_mapped() const { return mapped; } + void do_mapping(DTArray &xg, DTArray &yg, DTArray &zg) { + init_grid_restart("x", "xgrid", xg); + if (Ny > 1) + init_grid_restart("y", "ygrid", yg); + init_grid_restart("z", "zgrid", zg); + } + + /* Read fields and do derivatives */ + void init_vels(DTArray &u, DTArray &v, DTArray &w) { + // If user asks to grab the grids, we allocate arrays to store + // them in memory + if (mapped) { + xgrid = alloc_array(Nx, Ny, Nz); + if (Ny > 1) { + ygrid = alloc_array(Nx, Ny, Nz); + } + zgrid = alloc_array(Nx, Ny, Nz); + do_mapping(*xgrid, *ygrid, *zgrid); + } else { + // If user doesn't want to grab grids, we make sure not to + // allocate arrays for them and to set the pointers to NULL. + xgrid = NULL; + ygrid = NULL; + zgrid = NULL; + } + QSPOptions qsp_opts; + qsp_opts.S1_name = S1_name; + qsp_opts.T1_name = T1_name; + qsp_opts.filename = QSP_filename; + qsp_opts.NS = NS; + qsp_opts.NT = NT; + qsp_opts.S1_max = S1_max; + qsp_opts.S1_min = S1_min; + qsp_opts.T1_max = T1_max; + qsp_opts.T1_min = T1_min; + + QSPData qsp_data; + qsp_data.mapped = mapped; + qsp_data.Nx = Nx; + qsp_data.Ny = Ny; + qsp_data.Nz = Nz; + qsp_data.xgrid = xgrid; + qsp_data.ygrid = ygrid; + qsp_data.zgrid = zgrid; + + // Compute derivatives at each requested output + for (plotnum = start_sequence; plotnum <= final_sequence; + plotnum = plotnum + step_sequence) { + + // read in fields (if not already stored in memory) + // Compute QSP data. The code promises to not mutate the arrays, + // nor to make deep copies of them + + // u + init_tracer_restart("u", u); + // v + if (v_exist) { + init_tracer_restart("v", v); + } else { + if (master()) + fprintf(stdout, "No v field, setting v=0\n"); + v = 0; + } + // w + init_tracer_restart("w", w); + + qsp_data.plotnum = plotnum; + qsp_data.u = &u; + qsp_data.v = &v; + qsp_data.w = &w; + qsp_data.temp = NULL; + qsp_data.rho = NULL; + qsp_data.salinity = NULL; + qsp_data.custom_T1 = NULL; + qsp_data.custom_S1 = NULL; + + DTArray *arr_salinity, *arr_temperature, *arr_rho, *custom_T1, *custom_S1; + + if (use_salinity) { + arr_salinity = alloc_array(Nx, Ny, Nz); + init_tracer_restart(salinity_filename, *arr_salinity); + qsp_data.salinity = arr_salinity; + } + + if (read_rho) { + arr_rho = alloc_array(Nx, Ny, Nz); + init_tracer_restart(rho_filename, *arr_rho); + qsp_data.rho = arr_rho; + } + + if (T1_name.compare("temp") == 0 || S1_name.compare("temp") == 0) { + arr_temperature = alloc_array(Nx, Ny, Nz); + init_tracer_restart(temp_filename, *arr_temperature); + qsp_data.temp = arr_temperature; + } + + if (T1_name.compare("custom") == 0) { + custom_T1 = alloc_array(Nx, Ny, Nz); + init_tracer_restart(custom_T1_filename, *custom_T1); + qsp_data.custom_T1 = custom_T1; + } + + if (S1_name.compare("custom") == 0) { + custom_S1 = alloc_array(Nx, Ny, Nz); + init_tracer_restart(custom_S1_filename, *custom_S1); + qsp_data.custom_S1 = custom_S1; + } + + QSPCount(qsp_opts, qsp_data); + + if (master()) { + fprintf(stdout, "Completed QSP calculation for plotnum %d\n", plotnum); + } + } + } + + // Constructor: Initialize local variables + userControl() { + compute_quadweights(size_x(), size_y(), size_z(), length_x(), length_y(), + length_z(), type_x(), type_y(), type_z()); + } +}; + +/* 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); + + /* ------------------ 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"); + + option_category("Grid mapping options"); + add_option("mapped_grid", &mapped, false, "Is the grid mapped?"); + + 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"); + + option_category("QSP options"); + add_option("start_sequence", &start_sequence, + "Sequence number to start taking derivatives at"); + add_option("final_sequence", &final_sequence, + "Sequence number to stop taking derivatives at"); + add_option("step_sequence", &step_sequence, 1, + "Step between outputs to take derivatives"); + add_option("salinity_filename", &salinity_filename, + "Base Filename of Salinity data (optional)."); + add_option("temp_filename", &temp_filename, + "Base Filename of temperature data (optional)."); + add_option("T1_filename", &custom_T1_filename, + "Base Filename of custom tracer for T1."); + add_option("S1_filename", &custom_S1_filename, + "Base Filename of custom tracer for S1."); + add_option("T1", &T1_name, "u", + "Name of tracer 1 for QSP. Valid values are rho,u,v,w,temp, " + "salinity or ke"); + add_option("S1", &S1_name, "w", + "Name of tracer 2 for QSP. Valid values are rho,u,v,w,temp, " + "salinity or ke"); + add_option("T1_max", &T1_max, double_max, + "Maximum explicit bin for T1 in QSP."); + add_option("T1_min", &T1_min, -double_max, + "Minimum explicit bin for T1 in QSP."); + add_option("S1_max", &S1_max, double_max, + "Maximum explicit bin for S1 in QSP."); + add_option("S1_min", &S1_min, -double_max, + "Minimum explicit bin for S1 in QSP."); + add_option("salinity", &use_salinity, false, + "Should salinity be read in from a file?."); + add_option("read_rho", &read_rho, false, + "Should rho be read in from a file?."); + add_option("QSP_filename", &QSP_filename, "QSP_default", + "Filename to save data to. Don't include file extension."); + add_option("NS", &NS, 10, "Number of bins for tracer S"); + add_option("NT", &NT, 10, "Number of bins for tracer T"); + add_option("v_exist", &v_exist, "Does the v field exist?"); + // 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 values + can be directly used, but the ones of string-type need further procesing. + */ + + // parse expansion types + parse_boundary_conditions(xgrid_type, ygrid_type, zgrid_type, intype_x, + intype_y, intype_z); + // vector of string types + grid_type[x_ind] = xgrid_type; + grid_type[y_ind] = ygrid_type; + grid_type[z_ind] = 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"); + } + + /* ------------------ Do stuff --------------------- */ + userControl mycode; // Create an instantiated object of the above class + FluidEvolve<userControl> kevin_kh(&mycode); + kevin_kh.initialize(); + MPI_Finalize(); + return 0; +} diff --git a/src/cases/qsp/spins.conf b/src/cases/qsp/spins.conf new file mode 100644 index 0000000000000000000000000000000000000000..dd65a3bd8b7742946d05410f553b805950966b6c --- /dev/null +++ b/src/cases/qsp/spins.conf @@ -0,0 +1,69 @@ +## QSP configuration file + +# Spatial Parameters +Lx = 6.4 +Ly = 1.0 +Lz = 0.3 +Nx = 4096 +Ny = 1 +Nz = 384 + +# Expansion types +type_x = FREE_SLIP +type_y = FOURIER +type_z = NO_SLIP +mapped_grid = false +v_exist = false + +# If salinity is needed (directly or indirectly) set this to true +salinity = false +salinity_filename = s + +# If you already pre-computed rho (the equation of state) and saved it to a +# file, then set this to true. If this is false, and QSP can find the +# temperature data but not rho, it will use the equation of state function from +# Science.hpp, and if it also finds salinity it will add that to the equation +# of state calculation too. +read_rho = false +rho_filename = rho + +# Sequence Options +start_sequence = 0 +final_sequence = 0 +step_sequence = 1 + +# Choose what tracers you want to use. T1 and S1 have no distinguishable +# meaning, just stay consistent with NT and NS. Do note that if you pick ke +# then QSP will compute it in-place. If you already pre-computed ke and have a +# saved file for it, use the custom property explained below instead. +# Tracer options: rho, temp, salinity, u, v, w, ke +# +# Note: If you need to use a custom tracer (i.e. not one of the above), set the +# tracer name to "custom." If you do this however, it is your responsability to +# ensure that: +# a) you provide a valid custom filename too and +# b) all prepocessing is already completed and the data in the file is exactly +# what you want to use +T1 = temp +S1 = ke + +# Tracer base filenames. These are only relevent parameters IFF you set the +# according tracer name to "custom" above. Otherwise, these are ignored. +T1_filename = vorticity +S1_filename = vorticity + +# Set the min/max values of the first/last bin in each dimension respectively. +# These are optional parameters. If you remove these lines from the config QSP +# will find the global min/max values for each timestep and use that instead. +# So if you need fixed values for these make sure to include them! +T1_min = 1 +T1_max = 1 +S1_min = 1 +S1_max = 1 + +# Number of bins for T1 and S1 chosen above. +NT = 10 +NS = 10 + +# Base filename to save results +QSP_filename = QSP 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; } diff --git a/src/gmres.hpp b/src/gmres.hpp index 1499ae3234ef06376ffc76b4d12594da2f042e58..863eed30f69ca4096bff355ad44caa9b266b4a5a 100644 --- a/src/gmres.hpp +++ b/src/gmres.hpp @@ -269,8 +269,8 @@ template <class Controller> class GMRES_Solver { /* Perform the inner iteration */ /* First, allocate vectors for residuals and the Krylov basis */ - blitz::Vector<RType> resid_vec(num_its+1); - blitz::Vector<BType> basis_vec(num_its); // Note, these are base 0 + blitz::Array<RType,1> resid_vec(num_its+1); + blitz::Array<BType,1> basis_vec(num_its); // Note, these are base 0 for (int k = 0; k < num_its; k++) { resid_vec(k) = ops->alloc_resid(); basis_vec(k) = ops->alloc_basis(); @@ -406,7 +406,7 @@ template <class Controller> class GMRES_Solver { return my_it-1; } - void build_x(BType out_x, blitz::Vector<BType> & basis_vec, + void build_x(BType out_x, blitz::Array<BType,1> & basis_vec, blitz::Array<double,1> & rhs_vec, double starting_norm, int my_it) { /* Now, the Krylov basis is in basis_vec, and the proper multiples are in diff --git a/src/multigrid.cpp b/src/multigrid.cpp index 7df67096bd518b3e46bf98a718d016a22691b967..46836679ee105f0b58551c511976a9281e5b9433 100644 --- a/src/multigrid.cpp +++ b/src/multigrid.cpp @@ -3,7 +3,6 @@ #include <blitz/array.h> #include <iostream> -#include <blitz/tinyvec-et.h> #include "umfpack.h" #include "timing.hpp" @@ -1897,14 +1896,14 @@ void MG_Solver::_cycle(CYCLE_TYPE cycle, Array<double,2> & f, Array<double,2> & that the problem is really of size (1+size_x*size_z); this doesn't actually fit in u or f. So, allocate static blitz-vectors for the data, if necessary. */ - static blitz::Vector<double> extra_u(0), extra_f(0); + static blitz::Array<double,1> extra_u(0), extra_f(0); // fprintf(stderr,"CG: Input residual\n"); // cerr << f; if (indefinite_problem) { // fprintf(stderr,"Solving indefinite problem on coarse grid\n"); - if (extra_u.length() != (size_x*size_z+1)) + if (extra_u.length(firstDim) != (size_x*size_z+1)) extra_u.resize(size_x*size_z+1); - if (extra_f.length() != (size_x*size_z+1)) + if (extra_f.length(firstDim) != (size_x*size_z+1)) extra_f.resize(size_x*size_z+1); // Copy the f-problem to extra_f diff --git a/systems/belize3.sh b/systems/belize3.sh new file mode 100644 index 0000000000000000000000000000000000000000..949a94f20210b92d844b8b96a64662c342e8e0c6 --- /dev/null +++ b/systems/belize3.sh @@ -0,0 +1,57 @@ +#!/bin/bash + +# System-specific settings for belize.math.uwaterloo.ca + +CC=gcc +CXX=g++ +LD=mpic++ + +# System-specific compiler flags +SYSTEM_CFLAGS= +SYSTEM_LDFLAGS= +SYSTEM_CXXFLAGS= + +# Compiler flags for debugging +DEBUG_CFLAGS="-g -DBZ_DEBUG" +DEBUG_LDFLAGS= + +# Compiler flags for optimization +OPTIM_CFLAGS="-O3 -ffast-math -march=native" +OPTIM_LDFLAGS=$OPTIM_CFLAGS + +# Compiler flags for extra optimization, such as -ip -ipo on icc +EXTRA_OPTIM_CFLAGS= +EXTRA_OPTIM_LDFLAGS= + +# Library names/locations/flags for MPI-compilation. This will +# probably not be necessary on systems with a working mpicc +# alias +MPICXX=mpic++ +MPI_CFLAGS= +MPI_LIB= +MPI_LIBDIR= +MPI_INCDIR= + +# Library names/locations for LAPACK +LAPACK_LIB="-llapack -lblas" +LAPACK_LIBDIR= +LAPACK_INCDIR= + +# Library locations for blitz; leave blank to use system-installed +# or compiled-with-this-package version +BLITZ_LIBDIR= +BLITZ_INCDIR= + +# Library locations for fftw +FFTW_LIBDIR= +FFTW_INCDIR= + +# Library locations for UMFPACK +UMF_INCDIR= +UMF_LIBDIR= + +# Location/library for BLAS +BLAS_LIB="-lblas" +BLAS_LIBDIR= +BLAS_INCDIR= + diff --git a/systems/hood.sh b/systems/hood.sh index 4cf798bcb78535dd1b8bf54d4b7c5194cbd5ffe2..d6ff86b76cc525171c89bf183f680c089d55f968 100644 --- a/systems/hood.sh +++ b/systems/hood.sh @@ -28,9 +28,9 @@ EXTRA_OPTIM_LDFLAGS=$EXTRA_OPTIM_CFLAGS # alias MPICXX=icpc MPI_CFLAGS= -MPI_LIBDIR="-L${MPI_LIB}" +MPI_LIBDIR="-L${MPI_LIB?MPI_LIB unset}" MPI_LIB="-lmpi" -MPI_INCDIR="-I${MPI_INCLUDE}" +MPI_INCDIR="-I${MPI_INCLUDE?MPI_INCLUDE unset}" # Library names/locations for LAPACK LAPACK_LIB="-lmkl_intel_lp64 -lmkl_core -lmkl_sequential -lpthread" diff --git a/systems/makemake.sh b/systems/makemake.sh index b70c4f248a9f34009b78f2c8f8a52326629a8bd3..359f1407c6edfcf7897f659ba16fbdd1cd5ca38d 100755 --- a/systems/makemake.sh +++ b/systems/makemake.sh @@ -83,3 +83,18 @@ BLAS_INCDIR:=$BLAS_INCDIR EOF +echo "Sanity check - building trivial MPI executable" +rm -f mpitest.o mpitest.x mpitest.cpp +cat > mpitest.cpp << EOF +#include <mpi.h> +int main(int argc, char ** argv) { + MPI_Init(&argc, &argv); + MPI_Finalize(); + return 0; +} +EOF +echo "Executing: $MPICXX $SYSTEM_CFLAGS $SYSTEM_CXXFLAGS $MPI_CFLAGS $MPI_INCDIR -c -o mpitest.o ./mpitest.cpp" +$MPICXX $SYSTEM_CFLAGS $SYSTEM_CXXFLAGS $MPI_CFLAGS $MPI_INCDIR -c -o mpitest.o ./mpitest.cpp || echo "WARNING: MPI compilation failed!" && echo "MPI compilation success" +echo "Executing: $LD $SYSTEM_LDFLAGS $MPI_LIBDIR -o mpitest.x mpitest.o $MPI_LIB -lstdc++" +$LD $SYSTEM_LDFLAGS $MPI_LIBDIR -o mpitest.x mpitest.o $MPI_LIB -lstdc++ || echo "WARNING: MPI linking failed!" && echo "MPI linking success" +rm -f mpitest.o mpitest.x mpitest.cpp