|
@@ -0,0 +1,649 @@
|
|
|
+//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
+// miniWeather
|
|
|
+// Author: Matt Norman <normanmr@ornl.gov> , Oak Ridge National Laboratory
|
|
|
+// This code simulates dry, stratified, compressible, non-hydrostatic fluid flows
|
|
|
+// For documentation, please see the attached documentation in the "documentation" folder
|
|
|
+//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
+
|
|
|
+/*
|
|
|
+** Copyright (c) 2018, National Center for Computational Sciences, Oak Ridge National Laboratory. All rights reserved.
|
|
|
+**
|
|
|
+** Portions Copyright (c) 2020, NVIDIA Corporation. All rights reserved.
|
|
|
+*/
|
|
|
+
|
|
|
+#include <stdlib.h>
|
|
|
+#include <math.h>
|
|
|
+#include <stdio.h>
|
|
|
+#include <nvtx3/nvToolsExt.h>
|
|
|
+
|
|
|
+const double pi = 3.14159265358979323846264338327; //Pi
|
|
|
+const double grav = 9.8; //Gravitational acceleration (m / s^2)
|
|
|
+const double cp = 1004.; //Specific heat of dry air at constant pressure
|
|
|
+const double rd = 287.; //Dry air constant for equation of state (P=rho*rd*T)
|
|
|
+const double p0 = 1.e5; //Standard pressure at the surface in Pascals
|
|
|
+const double C0 = 27.5629410929725921310572974482; //Constant to translate potential temperature into pressure (P=C0*(rho*theta)**gamma)
|
|
|
+const double gamm = 1.40027894002789400278940027894; //gamma=cp/Rd , have to call this gamm because "gamma" is taken (I hate C so much)
|
|
|
+//Define domain and stability-related constants
|
|
|
+const double xlen = 2.e4; //Length of the domain in the x-direction (meters)
|
|
|
+const double zlen = 1.e4; //Length of the domain in the z-direction (meters)
|
|
|
+const double hv_beta = 0.25; //How strong to diffuse the solution: hv_beta \in [0:1]
|
|
|
+const double cfl = 1.50; //"Courant, Friedrichs, Lewy" number (for numerical stability)
|
|
|
+const double max_speed = 450; //Assumed maximum wave speed during the simulation (speed of sound + speed of wind) (meter / sec)
|
|
|
+const int hs = 2; //"Halo" size: number of cells needed for a full "stencil" of information for reconstruction
|
|
|
+const int sten_size = 4; //Size of the stencil used for interpolation
|
|
|
+
|
|
|
+//Parameters for indexing and flags
|
|
|
+const int NUM_VARS = 4; //Number of fluid state variables
|
|
|
+const int ID_DENS = 0; //index for density ("rho")
|
|
|
+const int ID_UMOM = 1; //index for momentum in the x-direction ("rho * u")
|
|
|
+const int ID_WMOM = 2; //index for momentum in the z-direction ("rho * w")
|
|
|
+const int ID_RHOT = 3; //index for density * potential temperature ("rho * theta")
|
|
|
+const int DIR_X = 1; //Integer constant to express that this operation is in the x-direction
|
|
|
+const int DIR_Z = 2; //Integer constant to express that this operation is in the z-direction
|
|
|
+
|
|
|
+const int nqpoints = 3;
|
|
|
+double qpoints[] = {0.112701665379258311482073460022E0, 0.500000000000000000000000000000E0, 0.887298334620741688517926539980E0};
|
|
|
+double qweights[] = {0.277777777777777777777777777779E0, 0.444444444444444444444444444444E0, 0.277777777777777777777777777779E0};
|
|
|
+
|
|
|
+///////////////////////////////////////////////////////////////////////////////////////
|
|
|
+// Variables that are initialized but remain static over the course of the simulation
|
|
|
+///////////////////////////////////////////////////////////////////////////////////////
|
|
|
+double sim_time; //total simulation time in seconds
|
|
|
+double output_freq; //frequency to perform output in seconds
|
|
|
+double dt; //Model time step (seconds)
|
|
|
+int nx, nz; //Number of local grid cells in the x- and z- dimensions
|
|
|
+double dx, dz; //Grid space length in x- and z-dimension (meters)
|
|
|
+int nx_glob, nz_glob; //Number of total grid cells in the x- and z- dimensions
|
|
|
+int i_beg, k_beg; //beginning index in the x- and z-directions
|
|
|
+int nranks, myrank; //my rank id
|
|
|
+int left_rank, right_rank; //Rank IDs that exist to my left and right in the global domain
|
|
|
+double *hy_dens_cell; //hydrostatic density (vert cell avgs). Dimensions: (1-hs:nz+hs)
|
|
|
+double *hy_dens_theta_cell; //hydrostatic rho*t (vert cell avgs). Dimensions: (1-hs:nz+hs)
|
|
|
+double *hy_dens_int; //hydrostatic density (vert cell interf). Dimensions: (1:nz+1)
|
|
|
+double *hy_dens_theta_int; //hydrostatic rho*t (vert cell interf). Dimensions: (1:nz+1)
|
|
|
+double *hy_pressure_int; //hydrostatic press (vert cell interf). Dimensions: (1:nz+1)
|
|
|
+
|
|
|
+///////////////////////////////////////////////////////////////////////////////////////
|
|
|
+// Variables that are dynamics over the course of the simulation
|
|
|
+///////////////////////////////////////////////////////////////////////////////////////
|
|
|
+double etime; //Elapsed model time
|
|
|
+double output_counter; //Helps determine when it's time to do output
|
|
|
+//Runtime variable arrays
|
|
|
+double *state; //Fluid state. Dimensions: (1-hs:nx+hs,1-hs:nz+hs,NUM_VARS)
|
|
|
+double *state_tmp; //Fluid state. Dimensions: (1-hs:nx+hs,1-hs:nz+hs,NUM_VARS)
|
|
|
+double *flux; //Cell interface fluxes. Dimensions: (nx+1,nz+1,NUM_VARS)
|
|
|
+double *tend; //Fluid state tendencies. Dimensions: (nx,nz,NUM_VARS)
|
|
|
+int num_out = 0; //The number of outputs performed so far
|
|
|
+int direction_switch = 1;
|
|
|
+
|
|
|
+//How is this not in the standard?!
|
|
|
+double dmin(double a, double b)
|
|
|
+{
|
|
|
+ if (a < b)
|
|
|
+ {
|
|
|
+ return a;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ return b;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+//Declaring the functions defined after "main"
|
|
|
+void init();
|
|
|
+void finalize();
|
|
|
+void injection(double x, double z, double &r, double &u, double &w, double &t, double &hr, double &ht);
|
|
|
+void hydro_const_theta(double z, double &r, double &t);
|
|
|
+void output(double *state, double etime);
|
|
|
+void ncwrap(int ierr, int line);
|
|
|
+void perform_timestep(double *state, double *state_tmp, double *flux, double *tend, double dt);
|
|
|
+void semi_discrete_step(double *state_init, double *state_forcing, double *state_out, double dt, int dir, double *flux, double *tend);
|
|
|
+void compute_tendencies_x(double *state, double *flux, double *tend);
|
|
|
+void compute_tendencies_z(double *state, double *flux, double *tend);
|
|
|
+void set_halo_values_x(double *state);
|
|
|
+void set_halo_values_z(double *state);
|
|
|
+
|
|
|
+///////////////////////////////////////////////////////////////////////////////////////
|
|
|
+// THE MAIN PROGRAM STARTS HERE
|
|
|
+///////////////////////////////////////////////////////////////////////////////////////
|
|
|
+int main(int argc, char **argv)
|
|
|
+{
|
|
|
+ ///////////////////////////////////////////////////////////////////////////////////////
|
|
|
+ // BEGIN USER-CONFIGURABLE PARAMETERS
|
|
|
+ ///////////////////////////////////////////////////////////////////////////////////////
|
|
|
+ //The x-direction length is twice as long as the z-direction length
|
|
|
+ //So, you'll want to have nx_glob be twice as large as nz_glob
|
|
|
+ nx_glob = 40; //Number of total cells in the x-dirction
|
|
|
+ nz_glob = 20; //Number of total cells in the z-dirction
|
|
|
+ sim_time = 1000; //How many seconds to run the simulation
|
|
|
+ output_freq = 100; //How frequently to output data to file (in seconds)
|
|
|
+ ///////////////////////////////////////////////////////////////////////////////////////
|
|
|
+ // END USER-CONFIGURABLE PARAMETERS
|
|
|
+ ///////////////////////////////////////////////////////////////////////////////////////
|
|
|
+
|
|
|
+ if (argc == 4)
|
|
|
+ {
|
|
|
+ printf("The arguments supplied are %s %s %s\n", argv[1], argv[2], argv[3]);
|
|
|
+ nx_glob = atoi(argv[1]);
|
|
|
+ nz_glob = atoi(argv[2]);
|
|
|
+ sim_time = atoi(argv[3]);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ printf("Using default values ...\n");
|
|
|
+ }
|
|
|
+
|
|
|
+ nvtxRangePushA("Total");
|
|
|
+ init();
|
|
|
+
|
|
|
+#pragma acc data copyin(state_tmp[(nz + 2 * hs) * (nx + 2 * hs) * NUM_VARS], hy_dens_cell[nz + 2 * hs], hy_dens_theta_cell[nz + 2 * hs], hy_dens_int[nz + 1], hy_dens_theta_int[nz + 1], hy_pressure_int[nz + 1]) \
|
|
|
+ create(flux[(nz + 1) * (nx + 1) * NUM_VARS], tend[nz * nx * NUM_VARS]) \
|
|
|
+ copy(state [0:(nz + 2 * hs) * (nx + 2 * hs) * NUM_VARS])
|
|
|
+ {
|
|
|
+ //Output the initial state
|
|
|
+ //output(state, etime);
|
|
|
+
|
|
|
+ ////////////////////////////////////////////////////
|
|
|
+ // MAIN TIME STEP LOOP
|
|
|
+ ////////////////////////////////////////////////////
|
|
|
+
|
|
|
+ nvtxRangePushA("while");
|
|
|
+ while (etime < sim_time)
|
|
|
+ {
|
|
|
+ //If the time step leads to exceeding the simulation time, shorten it for the last step
|
|
|
+ if (etime + dt > sim_time)
|
|
|
+ {
|
|
|
+ dt = sim_time - etime;
|
|
|
+ }
|
|
|
+
|
|
|
+ //Perform a single time step
|
|
|
+ nvtxRangePushA("perform_timestep");
|
|
|
+ perform_timestep(state, state_tmp, flux, tend, dt);
|
|
|
+ nvtxRangePop();
|
|
|
+
|
|
|
+ //Inform the user
|
|
|
+
|
|
|
+ printf("Elapsed Time: %lf / %lf\n", etime, sim_time);
|
|
|
+
|
|
|
+ //Update the elapsed time and output counter
|
|
|
+ etime = etime + dt;
|
|
|
+ output_counter = output_counter + dt;
|
|
|
+ //If it's time for output, reset the counter, and do output
|
|
|
+
|
|
|
+ if (output_counter >= output_freq)
|
|
|
+ {
|
|
|
+ output_counter = output_counter - output_freq;
|
|
|
+#pragma acc update host(state[(nz + 2 * hs) * (nx + 2 * hs) * NUM_VARS])
|
|
|
+ //output(state, etime);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ nvtxRangePop();
|
|
|
+ }
|
|
|
+ finalize();
|
|
|
+ nvtxRangePop();
|
|
|
+}
|
|
|
+
|
|
|
+//Performs a single dimensionally split time step using a simple low-storate three-stage Runge-Kutta time integrator
|
|
|
+//The dimensional splitting is a second-order-accurate alternating Strang splitting in which the
|
|
|
+//order of directions is alternated each time step.
|
|
|
+//The Runge-Kutta method used here is defined as follows:
|
|
|
+// q* = q[n] + dt/3 * rhs(q[n])
|
|
|
+// q** = q[n] + dt/2 * rhs(q* )
|
|
|
+// q[n+1] = q[n] + dt/1 * rhs(q** )
|
|
|
+void perform_timestep(double *state, double *state_tmp, double *flux, double *tend, double dt)
|
|
|
+{
|
|
|
+ if (direction_switch)
|
|
|
+ {
|
|
|
+ //x-direction first
|
|
|
+ semi_discrete_step(state, state, state_tmp, dt / 3, DIR_X, flux, tend);
|
|
|
+ semi_discrete_step(state, state_tmp, state_tmp, dt / 2, DIR_X, flux, tend);
|
|
|
+ semi_discrete_step(state, state_tmp, state, dt / 1, DIR_X, flux, tend);
|
|
|
+ //z-direction second
|
|
|
+ semi_discrete_step(state, state, state_tmp, dt / 3, DIR_Z, flux, tend);
|
|
|
+ semi_discrete_step(state, state_tmp, state_tmp, dt / 2, DIR_Z, flux, tend);
|
|
|
+ semi_discrete_step(state, state_tmp, state, dt / 1, DIR_Z, flux, tend);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ //z-direction second
|
|
|
+ semi_discrete_step(state, state, state_tmp, dt / 3, DIR_Z, flux, tend);
|
|
|
+ semi_discrete_step(state, state_tmp, state_tmp, dt / 2, DIR_Z, flux, tend);
|
|
|
+ semi_discrete_step(state, state_tmp, state, dt / 1, DIR_Z, flux, tend);
|
|
|
+ //x-direction first
|
|
|
+ semi_discrete_step(state, state, state_tmp, dt / 3, DIR_X, flux, tend);
|
|
|
+ semi_discrete_step(state, state_tmp, state_tmp, dt / 2, DIR_X, flux, tend);
|
|
|
+ semi_discrete_step(state, state_tmp, state, dt / 1, DIR_X, flux, tend);
|
|
|
+ }
|
|
|
+ if (direction_switch)
|
|
|
+ {
|
|
|
+ direction_switch = 0;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ direction_switch = 1;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+//Perform a single semi-discretized step in time with the form:
|
|
|
+//state_out = state_init + dt * rhs(state_forcing)
|
|
|
+//Meaning the step starts from state_init, computes the rhs using state_forcing, and stores the result in state_out
|
|
|
+void semi_discrete_step(double *state_init, double *state_forcing, double *state_out, double dt, int dir, double *flux, double *tend)
|
|
|
+{
|
|
|
+ int i, k, ll, inds, indt;
|
|
|
+ if (dir == DIR_X)
|
|
|
+ {
|
|
|
+ //Set the halo values in the x-direction
|
|
|
+ set_halo_values_x(state_forcing);
|
|
|
+ //Compute the time tendencies for the fluid state in the x-direction
|
|
|
+ compute_tendencies_x(state_forcing, flux, tend);
|
|
|
+ }
|
|
|
+ else if (dir == DIR_Z)
|
|
|
+ {
|
|
|
+ //Set the halo values in the z-direction
|
|
|
+ set_halo_values_z(state_forcing);
|
|
|
+ //Compute the time tendencies for the fluid state in the z-direction
|
|
|
+ compute_tendencies_z(state_forcing, flux, tend);
|
|
|
+ }
|
|
|
+
|
|
|
+/////////////////////////////////////////////////
|
|
|
+// TODO: THREAD ME
|
|
|
+/////////////////////////////////////////////////
|
|
|
+//Apply the tendencies to the fluid state
|
|
|
+#pragma acc parallel loop collapse(3) private(inds, indt) default(present)
|
|
|
+ for (ll = 0; ll < NUM_VARS; ll++)
|
|
|
+ {
|
|
|
+ for (k = 0; k < nz; k++)
|
|
|
+ {
|
|
|
+ for (i = 0; i < nx; i++)
|
|
|
+ {
|
|
|
+ inds = ll * (nz + 2 * hs) * (nx + 2 * hs) + (k + hs) * (nx + 2 * hs) + i + hs;
|
|
|
+ indt = ll * nz * nx + k * nx + i;
|
|
|
+ state_out[inds] = state_init[inds] + dt * tend[indt];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+//Compute the time tendencies of the fluid state using forcing in the x-direction
|
|
|
+
|
|
|
+//First, compute the flux vector at each cell interface in the x-direction (including hyperviscosity)
|
|
|
+//Then, compute the tendencies using those fluxes
|
|
|
+void compute_tendencies_x(double *state, double *flux, double *tend)
|
|
|
+{
|
|
|
+ int i, k, ll, s, inds, indf1, indf2, indt;
|
|
|
+ double r, u, w, t, p, stencil[4], d3_vals[NUM_VARS], vals[NUM_VARS], hv_coef;
|
|
|
+ //Compute the hyperviscosity coeficient
|
|
|
+ hv_coef = -hv_beta * dx / (16 * dt);
|
|
|
+ /////////////////////////////////////////////////
|
|
|
+ // TODO: THREAD ME
|
|
|
+ /////////////////////////////////////////////////
|
|
|
+ //Compute fluxes in the x-direction for each cell
|
|
|
+#pragma acc parallel loop collapse(2) private(ll, s, inds, stencil, vals, d3_vals, r, u, w, t, p) default(present)
|
|
|
+ for (k = 0; k < nz; k++)
|
|
|
+ {
|
|
|
+ for (i = 0; i < nx + 1; i++)
|
|
|
+ {
|
|
|
+ //Use fourth-order interpolation from four cell averages to compute the value at the interface in question
|
|
|
+ for (ll = 0; ll < NUM_VARS; ll++)
|
|
|
+ {
|
|
|
+ for (s = 0; s < sten_size; s++)
|
|
|
+ {
|
|
|
+ inds = ll * (nz + 2 * hs) * (nx + 2 * hs) + (k + hs) * (nx + 2 * hs) + i + s;
|
|
|
+ stencil[s] = state[inds];
|
|
|
+ }
|
|
|
+ //Fourth-order-accurate interpolation of the state
|
|
|
+ vals[ll] = -stencil[0] / 12 + 7 * stencil[1] / 12 + 7 * stencil[2] / 12 - stencil[3] / 12;
|
|
|
+ //First-order-accurate interpolation of the third spatial derivative of the state (for artificial viscosity)
|
|
|
+ d3_vals[ll] = -stencil[0] + 3 * stencil[1] - 3 * stencil[2] + stencil[3];
|
|
|
+ }
|
|
|
+
|
|
|
+ //Compute density, u-wind, w-wind, potential temperature, and pressure (r,u,w,t,p respectively)
|
|
|
+ r = vals[ID_DENS] + hy_dens_cell[k + hs];
|
|
|
+ u = vals[ID_UMOM] / r;
|
|
|
+ w = vals[ID_WMOM] / r;
|
|
|
+ t = (vals[ID_RHOT] + hy_dens_theta_cell[k + hs]) / r;
|
|
|
+ p = C0 * pow((r * t), gamm);
|
|
|
+
|
|
|
+ //Compute the flux vector
|
|
|
+ flux[ID_DENS * (nz + 1) * (nx + 1) + k * (nx + 1) + i] = r * u - hv_coef * d3_vals[ID_DENS];
|
|
|
+ flux[ID_UMOM * (nz + 1) * (nx + 1) + k * (nx + 1) + i] = r * u * u + p - hv_coef * d3_vals[ID_UMOM];
|
|
|
+ flux[ID_WMOM * (nz + 1) * (nx + 1) + k * (nx + 1) + i] = r * u * w - hv_coef * d3_vals[ID_WMOM];
|
|
|
+ flux[ID_RHOT * (nz + 1) * (nx + 1) + k * (nx + 1) + i] = r * u * t - hv_coef * d3_vals[ID_RHOT];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+/////////////////////////////////////////////////
|
|
|
+// TODO: THREAD ME
|
|
|
+/////////////////////////////////////////////////
|
|
|
+//Use the fluxes to compute tendencies for each cell
|
|
|
+#pragma acc parallel loop collapse(3) private(indt, indf1, indf2) default(present)
|
|
|
+ for (ll = 0; ll < NUM_VARS; ll++)
|
|
|
+ {
|
|
|
+ for (k = 0; k < nz; k++)
|
|
|
+ {
|
|
|
+ for (i = 0; i < nx; i++)
|
|
|
+ {
|
|
|
+ indt = ll * nz * nx + k * nx + i;
|
|
|
+ indf1 = ll * (nz + 1) * (nx + 1) + k * (nx + 1) + i;
|
|
|
+ indf2 = ll * (nz + 1) * (nx + 1) + k * (nx + 1) + i + 1;
|
|
|
+ tend[indt] = -(flux[indf2] - flux[indf1]) / dx;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+//Compute the time tendencies of the fluid state using forcing in the z-direction
|
|
|
+
|
|
|
+//First, compute the flux vector at each cell interface in the z-direction (including hyperviscosity)
|
|
|
+//Then, compute the tendencies using those fluxes
|
|
|
+void compute_tendencies_z(double *state, double *flux, double *tend)
|
|
|
+{
|
|
|
+ int i, k, ll, s, inds, indf1, indf2, indt;
|
|
|
+ double r, u, w, t, p, stencil[4], d3_vals[NUM_VARS], vals[NUM_VARS], hv_coef;
|
|
|
+ //Compute the hyperviscosity coeficient
|
|
|
+ hv_coef = -hv_beta * dx / (16 * dt);
|
|
|
+/////////////////////////////////////////////////
|
|
|
+// TODO: THREAD ME
|
|
|
+/////////////////////////////////////////////////
|
|
|
+//Compute fluxes in the x-direction for each cell
|
|
|
+#pragma acc parallel loop collapse(2) private(ll, s, inds, stencil, vals, d3_vals, r, u, w, t, p) default(present)
|
|
|
+ for (k = 0; k < nz + 1; k++)
|
|
|
+ {
|
|
|
+ for (i = 0; i < nx; i++)
|
|
|
+ {
|
|
|
+ //Use fourth-order interpolation from four cell averages to compute the value at the interface in question
|
|
|
+ for (ll = 0; ll < NUM_VARS; ll++)
|
|
|
+ {
|
|
|
+ for (s = 0; s < sten_size; s++)
|
|
|
+ {
|
|
|
+ inds = ll * (nz + 2 * hs) * (nx + 2 * hs) + (k + s) * (nx + 2 * hs) + i + hs;
|
|
|
+ stencil[s] = state[inds];
|
|
|
+ }
|
|
|
+ //Fourth-order-accurate interpolation of the state
|
|
|
+ vals[ll] = -stencil[0] / 12 + 7 * stencil[1] / 12 + 7 * stencil[2] / 12 - stencil[3] / 12;
|
|
|
+ //First-order-accurate interpolation of the third spatial derivative of the state
|
|
|
+ d3_vals[ll] = -stencil[0] + 3 * stencil[1] - 3 * stencil[2] + stencil[3];
|
|
|
+ }
|
|
|
+
|
|
|
+ //Compute density, u-wind, w-wind, potential temperature, and pressure (r,u,w,t,p respectively)
|
|
|
+ r = vals[ID_DENS] + hy_dens_int[k];
|
|
|
+ u = vals[ID_UMOM] / r;
|
|
|
+ w = vals[ID_WMOM] / r;
|
|
|
+ t = (vals[ID_RHOT] + hy_dens_theta_int[k]) / r;
|
|
|
+ p = C0 * pow((r * t), gamm) - hy_pressure_int[k];
|
|
|
+
|
|
|
+ //Compute the flux vector with hyperviscosity
|
|
|
+ flux[ID_DENS * (nz + 1) * (nx + 1) + k * (nx + 1) + i] = r * w - hv_coef * d3_vals[ID_DENS];
|
|
|
+ flux[ID_UMOM * (nz + 1) * (nx + 1) + k * (nx + 1) + i] = r * w * u - hv_coef * d3_vals[ID_UMOM];
|
|
|
+ flux[ID_WMOM * (nz + 1) * (nx + 1) + k * (nx + 1) + i] = r * w * w + p - hv_coef * d3_vals[ID_WMOM];
|
|
|
+ flux[ID_RHOT * (nz + 1) * (nx + 1) + k * (nx + 1) + i] = r * w * t - hv_coef * d3_vals[ID_RHOT];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+/////////////////////////////////////////////////
|
|
|
+// TODO: THREAD ME
|
|
|
+/////////////////////////////////////////////////
|
|
|
+//Use the fluxes to compute tendencies for each cell
|
|
|
+#pragma acc parallel loop collapse(3) private(indt, indf1, indf2) default(present)
|
|
|
+ for (ll = 0; ll < NUM_VARS; ll++)
|
|
|
+ {
|
|
|
+ for (k = 0; k < nz; k++)
|
|
|
+ {
|
|
|
+ for (i = 0; i < nx; i++)
|
|
|
+ {
|
|
|
+ indt = ll * nz * nx + k * nx + i;
|
|
|
+ indf1 = ll * (nz + 1) * (nx + 1) + (k) * (nx + 1) + i;
|
|
|
+ indf2 = ll * (nz + 1) * (nx + 1) + (k + 1) * (nx + 1) + i;
|
|
|
+ tend[indt] = -(flux[indf2] - flux[indf1]) / dz;
|
|
|
+ if (ll == ID_WMOM)
|
|
|
+ {
|
|
|
+ inds = ID_DENS * (nz + 2 * hs) * (nx + 2 * hs) + (k + hs) * (nx + 2 * hs) + i + hs;
|
|
|
+ tend[indt] = tend[indt] - state[inds] * grav;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void set_halo_values_x(double *state)
|
|
|
+{
|
|
|
+ int k, ll, ind_r, ind_u, ind_t, i;
|
|
|
+ double z;
|
|
|
+
|
|
|
+#pragma acc parallel loop collapse(2) default(present)
|
|
|
+ for (ll = 0; ll < NUM_VARS; ll++)
|
|
|
+ {
|
|
|
+ for (k = 0; k < nz; k++)
|
|
|
+ {
|
|
|
+ state[ll * (nz + 2 * hs) * (nx + 2 * hs) + (k + hs) * (nx + 2 * hs) + 0] = state[ll * (nz + 2 * hs) * (nx + 2 * hs) + (k + hs) * (nx + 2 * hs) + nx + hs - 2];
|
|
|
+ state[ll * (nz + 2 * hs) * (nx + 2 * hs) + (k + hs) * (nx + 2 * hs) + 1] = state[ll * (nz + 2 * hs) * (nx + 2 * hs) + (k + hs) * (nx + 2 * hs) + nx + hs - 1];
|
|
|
+ state[ll * (nz + 2 * hs) * (nx + 2 * hs) + (k + hs) * (nx + 2 * hs) + nx + hs] = state[ll * (nz + 2 * hs) * (nx + 2 * hs) + (k + hs) * (nx + 2 * hs) + hs];
|
|
|
+ state[ll * (nz + 2 * hs) * (nx + 2 * hs) + (k + hs) * (nx + 2 * hs) + nx + hs + 1] = state[ll * (nz + 2 * hs) * (nx + 2 * hs) + (k + hs) * (nx + 2 * hs) + hs + 1];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ////////////////////////////////////////////////////
|
|
|
+
|
|
|
+ if (myrank == 0)
|
|
|
+ {
|
|
|
+ for (k = 0; k < nz; k++)
|
|
|
+ {
|
|
|
+ for (i = 0; i < hs; i++)
|
|
|
+ {
|
|
|
+ z = (k_beg + k + 0.5) * dz;
|
|
|
+ if (abs(z - 3 * zlen / 4) <= zlen / 16)
|
|
|
+ {
|
|
|
+ ind_r = ID_DENS * (nz + 2 * hs) * (nx + 2 * hs) + (k + hs) * (nx + 2 * hs) + i;
|
|
|
+ ind_u = ID_UMOM * (nz + 2 * hs) * (nx + 2 * hs) + (k + hs) * (nx + 2 * hs) + i;
|
|
|
+ ind_t = ID_RHOT * (nz + 2 * hs) * (nx + 2 * hs) + (k + hs) * (nx + 2 * hs) + i;
|
|
|
+ state[ind_u] = (state[ind_r] + hy_dens_cell[k + hs]) * 50.;
|
|
|
+ state[ind_t] = (state[ind_r] + hy_dens_cell[k + hs]) * 298. - hy_dens_theta_cell[k + hs];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+//Set this task's halo values in the z-direction.
|
|
|
+//decomposition in the vertical direction.
|
|
|
+void set_halo_values_z(double *state)
|
|
|
+{
|
|
|
+ int i, ll;
|
|
|
+ const double mnt_width = xlen / 8;
|
|
|
+ double x, xloc, mnt_deriv;
|
|
|
+/////////////////////////////////////////////////
|
|
|
+// TODO: THREAD ME
|
|
|
+/////////////////////////////////////////////////
|
|
|
+#pragma acc parallel loop private(x, xloc, mnt_deriv) default(present)
|
|
|
+ for (ll = 0; ll < NUM_VARS; ll++)
|
|
|
+ {
|
|
|
+ for (i = 0; i < nx + 2 * hs; i++)
|
|
|
+ {
|
|
|
+ if (ll == ID_WMOM)
|
|
|
+ {
|
|
|
+ state[ll * (nz + 2 * hs) * (nx + 2 * hs) + (0) * (nx + 2 * hs) + i] = 0.;
|
|
|
+ state[ll * (nz + 2 * hs) * (nx + 2 * hs) + (1) * (nx + 2 * hs) + i] = 0.;
|
|
|
+ state[ll * (nz + 2 * hs) * (nx + 2 * hs) + (nz + hs) * (nx + 2 * hs) + i] = 0.;
|
|
|
+ state[ll * (nz + 2 * hs) * (nx + 2 * hs) + (nz + hs + 1) * (nx + 2 * hs) + i] = 0.;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ state[ll * (nz + 2 * hs) * (nx + 2 * hs) + (0) * (nx + 2 * hs) + i] = state[ll * (nz + 2 * hs) * (nx + 2 * hs) + (hs) * (nx + 2 * hs) + i];
|
|
|
+ state[ll * (nz + 2 * hs) * (nx + 2 * hs) + (1) * (nx + 2 * hs) + i] = state[ll * (nz + 2 * hs) * (nx + 2 * hs) + (hs) * (nx + 2 * hs) + i];
|
|
|
+ state[ll * (nz + 2 * hs) * (nx + 2 * hs) + (nz + hs) * (nx + 2 * hs) + i] = state[ll * (nz + 2 * hs) * (nx + 2 * hs) + (nz + hs - 1) * (nx + 2 * hs) + i];
|
|
|
+ state[ll * (nz + 2 * hs) * (nx + 2 * hs) + (nz + hs + 1) * (nx + 2 * hs) + i] = state[ll * (nz + 2 * hs) * (nx + 2 * hs) + (nz + hs - 1) * (nx + 2 * hs) + i];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void init()
|
|
|
+{
|
|
|
+ int i, k, ii, kk, ll, inds, i_end;
|
|
|
+ double x, z, r, u, w, t, hr, ht, nper;
|
|
|
+
|
|
|
+ //Set the cell grid size
|
|
|
+ dx = xlen / nx_glob;
|
|
|
+ dz = zlen / nz_glob;
|
|
|
+
|
|
|
+ nranks = 1;
|
|
|
+ myrank = 0;
|
|
|
+
|
|
|
+ // For simpler version, replace i_beg = 0, nx = nx_glob, left_rank = 0, right_rank = 0;
|
|
|
+
|
|
|
+ nper = ((double)nx_glob) / nranks;
|
|
|
+ i_beg = round(nper * (myrank));
|
|
|
+ i_end = round(nper * ((myrank) + 1)) - 1;
|
|
|
+ nx = i_end - i_beg + 1;
|
|
|
+ left_rank = myrank - 1;
|
|
|
+ if (left_rank == -1)
|
|
|
+ left_rank = nranks - 1;
|
|
|
+ right_rank = myrank + 1;
|
|
|
+ if (right_rank == nranks)
|
|
|
+ right_rank = 0;
|
|
|
+
|
|
|
+ ////////////////////////////////////////////////////////////////////////////////
|
|
|
+ ////////////////////////////////////////////////////////////////////////////////
|
|
|
+ // YOU DON'T NEED TO ALTER ANYTHING BELOW THIS POINT IN THE CODE
|
|
|
+ ////////////////////////////////////////////////////////////////////////////////
|
|
|
+ ////////////////////////////////////////////////////////////////////////////////
|
|
|
+
|
|
|
+ k_beg = 0;
|
|
|
+ nz = nz_glob;
|
|
|
+
|
|
|
+ //Allocate the model data
|
|
|
+ state = (double *)malloc((nx + 2 * hs) * (nz + 2 * hs) * NUM_VARS * sizeof(double));
|
|
|
+ state_tmp = (double *)malloc((nx + 2 * hs) * (nz + 2 * hs) * NUM_VARS * sizeof(double));
|
|
|
+ flux = (double *)malloc((nx + 1) * (nz + 1) * NUM_VARS * sizeof(double));
|
|
|
+ tend = (double *)malloc(nx * nz * NUM_VARS * sizeof(double));
|
|
|
+ hy_dens_cell = (double *)malloc((nz + 2 * hs) * sizeof(double));
|
|
|
+ hy_dens_theta_cell = (double *)malloc((nz + 2 * hs) * sizeof(double));
|
|
|
+ hy_dens_int = (double *)malloc((nz + 1) * sizeof(double));
|
|
|
+ hy_dens_theta_int = (double *)malloc((nz + 1) * sizeof(double));
|
|
|
+ hy_pressure_int = (double *)malloc((nz + 1) * sizeof(double));
|
|
|
+
|
|
|
+ //Define the maximum stable time step based on an assumed maximum wind speed
|
|
|
+ dt = dmin(dx, dz) / max_speed * cfl;
|
|
|
+ //Set initial elapsed model time and output_counter to zero
|
|
|
+ etime = 0.;
|
|
|
+ output_counter = 0.;
|
|
|
+
|
|
|
+ // Display grid information
|
|
|
+
|
|
|
+ printf("nx_glob, nz_glob: %d %d\n", nx_glob, nz_glob);
|
|
|
+ printf("dx,dz: %lf %lf\n", dx, dz);
|
|
|
+ printf("dt: %lf\n", dt);
|
|
|
+
|
|
|
+ //////////////////////////////////////////////////////////////////////////
|
|
|
+ // Initialize the cell-averaged fluid state via Gauss-Legendre quadrature
|
|
|
+ //////////////////////////////////////////////////////////////////////////
|
|
|
+ for (k = 0; k < nz + 2 * hs; k++)
|
|
|
+ {
|
|
|
+ for (i = 0; i < nx + 2 * hs; i++)
|
|
|
+ {
|
|
|
+ //Initialize the state to zero
|
|
|
+ for (ll = 0; ll < NUM_VARS; ll++)
|
|
|
+ {
|
|
|
+ inds = ll * (nz + 2 * hs) * (nx + 2 * hs) + k * (nx + 2 * hs) + i;
|
|
|
+ state[inds] = 0.;
|
|
|
+ }
|
|
|
+ //Use Gauss-Legendre quadrature to initialize a hydrostatic balance + temperature perturbation
|
|
|
+ for (kk = 0; kk < nqpoints; kk++)
|
|
|
+ {
|
|
|
+ for (ii = 0; ii < nqpoints; ii++)
|
|
|
+ {
|
|
|
+ //Compute the x,z location within the global domain based on cell and quadrature index
|
|
|
+ x = (i_beg + i - hs + 0.5) * dx + (qpoints[ii] - 0.5) * dx;
|
|
|
+ z = (k_beg + k - hs + 0.5) * dz + (qpoints[kk] - 0.5) * dz;
|
|
|
+
|
|
|
+ //Set the fluid state based on the user's specification (default is injection in this example)
|
|
|
+ injection(x, z, r, u, w, t, hr, ht);
|
|
|
+
|
|
|
+ //Store into the fluid state array
|
|
|
+ inds = ID_DENS * (nz + 2 * hs) * (nx + 2 * hs) + k * (nx + 2 * hs) + i;
|
|
|
+ state[inds] = state[inds] + r * qweights[ii] * qweights[kk];
|
|
|
+ inds = ID_UMOM * (nz + 2 * hs) * (nx + 2 * hs) + k * (nx + 2 * hs) + i;
|
|
|
+ state[inds] = state[inds] + (r + hr) * u * qweights[ii] * qweights[kk];
|
|
|
+ inds = ID_WMOM * (nz + 2 * hs) * (nx + 2 * hs) + k * (nx + 2 * hs) + i;
|
|
|
+ state[inds] = state[inds] + (r + hr) * w * qweights[ii] * qweights[kk];
|
|
|
+ inds = ID_RHOT * (nz + 2 * hs) * (nx + 2 * hs) + k * (nx + 2 * hs) + i;
|
|
|
+ state[inds] = state[inds] + ((r + hr) * (t + ht) - hr * ht) * qweights[ii] * qweights[kk];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ for (ll = 0; ll < NUM_VARS; ll++)
|
|
|
+ {
|
|
|
+ inds = ll * (nz + 2 * hs) * (nx + 2 * hs) + k * (nx + 2 * hs) + i;
|
|
|
+ state_tmp[inds] = state[inds];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ //Compute the hydrostatic background state over vertical cell averages
|
|
|
+ for (k = 0; k < nz + 2 * hs; k++)
|
|
|
+ {
|
|
|
+ hy_dens_cell[k] = 0.;
|
|
|
+ hy_dens_theta_cell[k] = 0.;
|
|
|
+ for (kk = 0; kk < nqpoints; kk++)
|
|
|
+ {
|
|
|
+ z = (k_beg + k - hs + 0.5) * dz;
|
|
|
+
|
|
|
+ //Set the fluid state based on the user's specification (default is injection in this example)
|
|
|
+ injection(0., z, r, u, w, t, hr, ht);
|
|
|
+
|
|
|
+ hy_dens_cell[k] = hy_dens_cell[k] + hr * qweights[kk];
|
|
|
+ hy_dens_theta_cell[k] = hy_dens_theta_cell[k] + hr * ht * qweights[kk];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ //Compute the hydrostatic background state at vertical cell interfaces
|
|
|
+ for (k = 0; k < nz + 1; k++)
|
|
|
+ {
|
|
|
+ z = (k_beg + k) * dz;
|
|
|
+
|
|
|
+ //Set the fluid state based on the user's specification (default is injection in this example)
|
|
|
+ injection(0., z, r, u, w, t, hr, ht);
|
|
|
+
|
|
|
+ hy_dens_int[k] = hr;
|
|
|
+ hy_dens_theta_int[k] = hr * ht;
|
|
|
+ hy_pressure_int[k] = C0 * pow((hr * ht), gamm);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+//This test case is initially balanced but injects fast, cold air from the left boundary near the model top
|
|
|
+//x and z are input coordinates at which to sample
|
|
|
+//r,u,w,t are output density, u-wind, w-wind, and potential temperature at that location
|
|
|
+//hr and ht are output background hydrostatic density and potential temperature at that location
|
|
|
+void injection(double x, double z, double &r, double &u, double &w, double &t, double &hr, double &ht)
|
|
|
+{
|
|
|
+ hydro_const_theta(z, hr, ht);
|
|
|
+ r = 0.;
|
|
|
+ t = 0.;
|
|
|
+ u = 0.;
|
|
|
+ w = 0.;
|
|
|
+}
|
|
|
+
|
|
|
+//Establish hydrstatic balance using constant potential temperature (thermally neutral atmosphere)
|
|
|
+//z is the input coordinate
|
|
|
+//r and t are the output background hydrostatic density and potential temperature
|
|
|
+void hydro_const_theta(double z, double &r, double &t)
|
|
|
+{
|
|
|
+ const double theta0 = 300.; //Background potential temperature
|
|
|
+ const double exner0 = 1.; //Surface-level Exner pressure
|
|
|
+ double p, exner, rt;
|
|
|
+ //Establish hydrostatic balance first using Exner pressure
|
|
|
+ t = theta0; //Potential Temperature at z
|
|
|
+ exner = exner0 - grav * z / (cp * theta0); //Exner pressure at z
|
|
|
+ p = p0 * pow(exner, (cp / rd)); //Pressure at z
|
|
|
+ rt = pow((p / C0), (1. / gamm)); //rho*theta at z
|
|
|
+ r = rt / t; //Density at z
|
|
|
+}
|
|
|
+
|
|
|
+void finalize()
|
|
|
+{
|
|
|
+ free(state);
|
|
|
+ free(state_tmp);
|
|
|
+ free(flux);
|
|
|
+ free(tend);
|
|
|
+ free(hy_dens_cell);
|
|
|
+ free(hy_dens_theta_cell);
|
|
|
+ free(hy_dens_int);
|
|
|
+ free(hy_dens_theta_int);
|
|
|
+ free(hy_pressure_int);
|
|
|
+}
|