miniWeather_serial.f90 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652
  1. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  2. !! miniWeather
  3. !! Author: Matt Norman <normanmr@ornl.gov> , Oak Ridge National Laboratory
  4. !! This code simulates dry, stratified, compressible, non-hydrostatic fluid flows
  5. !! For documentation, please see the attached documentation in the "documentation" folder
  6. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  7. program miniweather
  8. use nvtx
  9. !implicit none
  10. !Declare the precision for the real constants (at least 15 digits of accuracy = double precision)
  11. integer , parameter :: rp = selected_real_kind(15)
  12. !Define some physical constants to use throughout the simulation
  13. real(rp), parameter :: pi = 3.14159265358979323846264338327_rp !Pi
  14. real(rp), parameter :: grav = 9.8_rp !Gravitational acceleration (m / s^2)
  15. real(rp), parameter :: cp = 1004._rp !Specific heat of dry air at constant pressure
  16. real(rp), parameter :: rd = 287._rp !Dry air constant for equation of state (P=rho*rd*T)
  17. real(rp), parameter :: p0 = 1.e5_rp !Standard pressure at the surface in Pascals
  18. real(rp), parameter :: C0 = 27.5629410929725921310572974482_rp !Constant to translate potential temperature into pressure (P=C0*(rho*theta)**gamma)
  19. real(rp), parameter :: gamma = 1.40027894002789400278940027894_rp !gamma=cp/Rd
  20. !Define domain and stability-related constants
  21. real(rp), parameter :: xlen = 2.e4_rp !Length of the domain in the x-direction (meters)
  22. real(rp), parameter :: zlen = 1.e4_rp !Length of the domain in the z-direction (meters)
  23. real(rp), parameter :: hv_beta = 0.25_rp !How strong to diffuse the solution: hv_beta \in [0:1]
  24. real(rp), parameter :: cfl = 1.50_rp !"Courant, Friedrichs, Lewy" number (for numerical stability)
  25. real(rp), parameter :: max_speed = 450 !Assumed maximum wave speed during the simulation (speed of sound + speed of wind) (meter / sec)
  26. integer , parameter :: hs = 2 !"Halo" size: number of cells needed for a full "stencil" of information for reconstruction
  27. integer , parameter :: sten_size = 4 !Size of the stencil used for interpolation
  28. !Parameters for indexing and flags
  29. integer , parameter :: NUM_VARS = 4 !Number of fluid state variables
  30. integer , parameter :: ID_DENS = 1 !index for density ("rho")
  31. integer , parameter :: ID_UMOM = 2 !index for momentum in the x-direction ("rho * u")
  32. integer , parameter :: ID_WMOM = 3 !index for momentum in the z-direction ("rho * w")
  33. integer , parameter :: ID_RHOT = 4 !index for density * potential temperature ("rho * theta")
  34. integer , parameter :: DIR_X = 1 !Integer constant to express that this operation is in the x-direction
  35. integer , parameter :: DIR_Z = 2 !Integer constant to express that this operation is in the z-direction
  36. !Gauss-Legendre quadrature points and weights on the domain [0:1]
  37. integer , parameter :: nqpoints = 3
  38. real(rp) :: qpoints (nqpoints) = (/ 0.112701665379258311482073460022E0_rp , 0.500000000000000000000000000000E0_rp , 0.887298334620741688517926539980E0_rp /)
  39. real(rp) :: qweights(nqpoints) = (/ 0.277777777777777777777777777779E0_rp , 0.444444444444444444444444444444E0_rp , 0.277777777777777777777777777779E0_rp /)
  40. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  41. !! Variables that are initialized but remain static over the course of the simulation
  42. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  43. real(rp) :: sim_time !total simulation time in seconds
  44. real(rp) :: output_freq !frequency to perform output in seconds
  45. real(rp) :: dt !Model time step (seconds)
  46. integer :: nx, nz !Number of local grid cells in the x- and z- dimensions
  47. real(rp) :: dx, dz !Grid space length in x- and z-dimension (meters)
  48. integer :: nx_glob, nz_glob !Number of total grid cells in the x- and z- dimensions
  49. integer :: i_beg, k_beg !beginning index in the x- and z-directions
  50. integer :: nranks, myrank !my rank id
  51. integer :: left_rank, right_rank
  52. real(rp), allocatable :: hy_dens_cell (:) !hydrostatic density (vert cell avgs). Dimensions: (1-hs:nz+hs)
  53. real(rp), allocatable :: hy_dens_theta_cell(:) !hydrostatic rho*t (vert cell avgs). Dimensions: (1-hs:nz+hs)
  54. real(rp), allocatable :: hy_dens_int (:) !hydrostatic density (vert cell interf). Dimensions: (1:nz+1)
  55. real(rp), allocatable :: hy_dens_theta_int (:) !hydrostatic rho*t (vert cell interf). Dimensions: (1:nz+1)
  56. real(rp), allocatable :: hy_pressure_int (:) !hydrostatic press (vert cell interf). Dimensions: (1:nz+1)
  57. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  58. !! Variables that are dynamics over the course of the simulation
  59. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  60. real(rp) :: etime !Elapsed model time
  61. real(rp) :: output_counter !Helps determine when it's time to do output
  62. !Runtime variable arrays
  63. real(rp), allocatable :: state (:,:,:) !Fluid state. Dimensions: (1-hs:nx+hs,1-hs:nz+hs,NUM_VARS)
  64. real(rp), allocatable :: state_tmp (:,:,:) !Fluid state. Dimensions: (1-hs:nx+hs,1-hs:nz+hs,NUM_VARS)
  65. real(rp), allocatable :: flux (:,:,:) !Cell interface fluxes. Dimensions: (nx+1,nz+1,NUM_VARS)
  66. real(rp), allocatable :: tend (:,:,:) !Fluid state tendencies. Dimensions: (nx,nz,NUM_VARS)
  67. integer(8) :: t1, t2, rate !For CPU Timings
  68. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  69. !! THE MAIN PROGRAM STARTS HERE
  70. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  71. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  72. !! BEGIN USER-CONFIGURABLE PARAMETERS
  73. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  74. !The x-direction length is twice as long as the z-direction length
  75. !So, you'll want to have nx_glob be twice as large as nz_glob
  76. nx_glob = 400 !Number of total cells in the x-dirction
  77. nz_glob = 200 !Number of total cells in the z-dirction
  78. sim_time = 10 !How many seconds to run the simulation
  79. output_freq = 100 !How frequently to output data to file (in seconds)
  80. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  81. !! END USER-CONFIGURABLE PARAMETERS
  82. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  83. !Allocate arrays, initialize the grid and the data
  84. call nvtxStartRange("Total")
  85. call init()
  86. !Output the initial state
  87. !call output(state,etime)
  88. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  89. !! MAIN TIME STEP LOOP
  90. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  91. call system_clock(t1)
  92. call nvtxStartRange("while")
  93. do while (etime < sim_time)
  94. !If the time step leads to exceeding the simulation time, shorten it for the last step
  95. if (etime + dt > sim_time) dt = sim_time - etime
  96. !Perform a single time step
  97. call nvtxStartRange("perform_timestep")
  98. call perform_timestep(state,state_tmp,flux,tend,dt)
  99. call nvtxEndRange
  100. !Inform the user
  101. write(*,*) 'Elapsed Time: ', etime , ' / ' , sim_time
  102. !Update the elapsed time and output counter
  103. etime = etime + dt
  104. output_counter = output_counter + dt
  105. !If it's time for output, reset the counter, and do output
  106. if (output_counter >= output_freq) then
  107. output_counter = output_counter - output_freq
  108. !call output(state,etime)
  109. endif
  110. enddo
  111. call nvtxEndRange
  112. call system_clock(t2,rate)
  113. write(*,*) "CPU Time: ",dble(t2-t1)/dble(rate)
  114. !Deallocate
  115. call finalize()
  116. call nvtxEndRange
  117. contains
  118. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  119. !! HELPER SUBROUTINES AND FUNCTIONS GO HERE
  120. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  121. !Performs a single dimensionally split time step using a simple low-storate three-stage Runge-Kutta time integrator
  122. !The dimensional splitting is a second-order-accurate alternating Strang splitting in which the
  123. !order of directions is alternated each time step.
  124. !The Runge-Kutta method used here is defined as follows:
  125. ! q* = q[n] + dt/3 * rhs(q[n])
  126. ! q** = q[n] + dt/2 * rhs(q* )
  127. ! q[n+1] = q[n] + dt/1 * rhs(q** )
  128. subroutine perform_timestep(state,state_tmp,flux,tend,dt)
  129. implicit none
  130. real(rp), intent(inout) :: state (1-hs:nx+hs,1-hs:nz+hs,NUM_VARS)
  131. real(rp), intent( out) :: state_tmp(1-hs:nx+hs,1-hs:nz+hs,NUM_VARS)
  132. real(rp), intent( out) :: flux (nx+1,nz+1,NUM_VARS)
  133. real(rp), intent( out) :: tend (nx,nz,NUM_VARS)
  134. real(rp), intent(in ) :: dt
  135. logical, save :: direction_switch = .true.
  136. if (direction_switch) then
  137. !x-direction first
  138. call semi_discrete_step( state , state , state_tmp , dt / 3 , DIR_X , flux , tend )
  139. call semi_discrete_step( state , state_tmp , state_tmp , dt / 2 , DIR_X , flux , tend )
  140. call semi_discrete_step( state , state_tmp , state , dt / 1 , DIR_X , flux , tend )
  141. !z-direction second
  142. call semi_discrete_step( state , state , state_tmp , dt / 3 , DIR_Z , flux , tend )
  143. call semi_discrete_step( state , state_tmp , state_tmp , dt / 2 , DIR_Z , flux , tend )
  144. call semi_discrete_step( state , state_tmp , state , dt / 1 , DIR_Z , flux , tend )
  145. else
  146. !z-direction second
  147. call semi_discrete_step( state , state , state_tmp , dt / 3 , DIR_Z , flux , tend )
  148. call semi_discrete_step( state , state_tmp , state_tmp , dt / 2 , DIR_Z , flux , tend )
  149. call semi_discrete_step( state , state_tmp , state , dt / 1 , DIR_Z , flux , tend )
  150. !x-direction first
  151. call semi_discrete_step( state , state , state_tmp , dt / 3 , DIR_X , flux , tend )
  152. call semi_discrete_step( state , state_tmp , state_tmp , dt / 2 , DIR_X , flux , tend )
  153. call semi_discrete_step( state , state_tmp , state , dt / 1 , DIR_X , flux , tend )
  154. endif
  155. direction_switch = .not. direction_switch
  156. end subroutine perform_timestep
  157. !Perform a single semi-discretized step in time with the form:
  158. !state_out = state_init + dt * rhs(state_forcing)
  159. !Meaning the step starts from state_init, computes the rhs using state_forcing, and stores the result in state_out
  160. subroutine semi_discrete_step( state_init , state_forcing , state_out , dt , dir , flux , tend )
  161. implicit none
  162. real(rp), intent(in ) :: state_init (1-hs:nx+hs,1-hs:nz+hs,NUM_VARS)
  163. real(rp), intent(inout) :: state_forcing(1-hs:nx+hs,1-hs:nz+hs,NUM_VARS)
  164. real(rp), intent( out) :: state_out (1-hs:nx+hs,1-hs:nz+hs,NUM_VARS)
  165. real(rp), intent( out) :: flux (nx+1,nz+1,NUM_VARS)
  166. real(rp), intent( out) :: tend (nx,nz,NUM_VARS)
  167. real(rp), intent(in ) :: dt
  168. integer , intent(in ) :: dir
  169. integer :: i,k,ll
  170. if (dir == DIR_X) then
  171. !Set the halo values in the x-direction
  172. call set_halo_values_x(state_forcing)
  173. !Compute the time tendencies for the fluid state in the x-direction
  174. call compute_tendencies_x(state_forcing,flux,tend)
  175. elseif (dir == DIR_Z) then
  176. !Set the halo values in the z-direction
  177. call set_halo_values_z(state_forcing)
  178. !Compute the time tendencies for the fluid state in the z-direction
  179. call compute_tendencies_z(state_forcing,flux,tend)
  180. endif
  181. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  182. !! TODO: THREAD ME
  183. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  184. !Apply the tendencies to the fluid state
  185. do ll = 1 , NUM_VARS
  186. do k = 1 , nz
  187. do i = 1 , nx
  188. state_out(i,k,ll) = state_init(i,k,ll) + dt * tend(i,k,ll)
  189. enddo
  190. enddo
  191. enddo
  192. end subroutine semi_discrete_step
  193. !Compute the time tendencies of the fluid state using forcing in the x-direction
  194. !First, compute the flux vector at each cell interface in the x-direction (including hyperviscosity)
  195. !Then, compute the tendencies using those fluxes
  196. subroutine compute_tendencies_x(state,flux,tend)
  197. implicit none
  198. real(rp), intent(in ) :: state(1-hs:nx+hs,1-hs:nz+hs,NUM_VARS)
  199. real(rp), intent( out) :: flux (nx+1,nz+1,NUM_VARS)
  200. real(rp), intent( out) :: tend (nx,nz,NUM_VARS)
  201. integer :: i,k,ll,s
  202. real(rp) :: r,u,w,t,p, stencil(4), d3_vals(NUM_VARS), vals(NUM_VARS), hv_coef
  203. !Compute the hyperviscosity coeficient
  204. hv_coef = -hv_beta * dx / (16*dt)
  205. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  206. !! TODO: THREAD ME
  207. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  208. !Compute fluxes in the x-direction for each cell
  209. do k = 1 , nz
  210. do i = 1 , nx+1
  211. !Use fourth-order interpolation from four cell averages to compute the value at the interface in question
  212. do ll = 1 , NUM_VARS
  213. do s = 1 , sten_size
  214. stencil(s) = state(i-hs-1+s,k,ll)
  215. enddo
  216. !Fourth-order-accurate interpolation of the state
  217. vals(ll) = -stencil(1)/12 + 7*stencil(2)/12 + 7*stencil(3)/12 - stencil(4)/12
  218. !First-order-accurate interpolation of the third spatial derivative of the state (for artificial viscosity)
  219. d3_vals(ll) = -stencil(1) + 3*stencil(2) - 3*stencil(3) + stencil(4)
  220. enddo
  221. !Compute density, u-wind, w-wind, potential temperature, and pressure (r,u,w,t,p respectively)
  222. r = vals(ID_DENS) + hy_dens_cell(k)
  223. u = vals(ID_UMOM) / r
  224. w = vals(ID_WMOM) / r
  225. t = ( vals(ID_RHOT) + hy_dens_theta_cell(k) ) / r
  226. p = C0*(r*t)**gamma
  227. !Compute the flux vector
  228. flux(i,k,ID_DENS) = r*u - hv_coef*d3_vals(ID_DENS)
  229. flux(i,k,ID_UMOM) = r*u*u+p - hv_coef*d3_vals(ID_UMOM)
  230. flux(i,k,ID_WMOM) = r*u*w - hv_coef*d3_vals(ID_WMOM)
  231. flux(i,k,ID_RHOT) = r*u*t - hv_coef*d3_vals(ID_RHOT)
  232. enddo
  233. enddo
  234. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  235. !! TODO: THREAD ME
  236. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  237. !Use the fluxes to compute tendencies for each cell
  238. do ll = 1 , NUM_VARS
  239. do k = 1 , nz
  240. do i = 1 , nx
  241. tend(i,k,ll) = -( flux(i+1,k,ll) - flux(i,k,ll) ) / dx
  242. enddo
  243. enddo
  244. enddo
  245. end subroutine compute_tendencies_x
  246. !Compute the time tendencies of the fluid state using forcing in the z-direction
  247. !First, compute the flux vector at each cell interface in the z-direction (including hyperviscosity)
  248. !Then, compute the tendencies using those fluxes
  249. subroutine compute_tendencies_z(state,flux,tend)
  250. implicit none
  251. real(rp), intent(in ) :: state(1-hs:nx+hs,1-hs:nz+hs,NUM_VARS)
  252. real(rp), intent( out) :: flux (nx+1,nz+1,NUM_VARS)
  253. real(rp), intent( out) :: tend (nx,nz,NUM_VARS)
  254. integer :: i,k,ll,s
  255. real(rp) :: r,u,w,t,p, stencil(4), d3_vals(NUM_VARS), vals(NUM_VARS), hv_coef
  256. !Compute the hyperviscosity coeficient
  257. hv_coef = -hv_beta * dz / (16*dt)
  258. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  259. !! TODO: THREAD ME
  260. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  261. !Compute fluxes in the x-direction for each cell
  262. do k = 1 , nz+1
  263. do i = 1 , nx
  264. !Use fourth-order interpolation from four cell averages to compute the value at the interface in question
  265. do ll = 1 , NUM_VARS
  266. do s = 1 , sten_size
  267. stencil(s) = state(i,k-hs-1+s,ll)
  268. enddo
  269. !Fourth-order-accurate interpolation of the state
  270. vals(ll) = -stencil(1)/12 + 7*stencil(2)/12 + 7*stencil(3)/12 - stencil(4)/12
  271. !First-order-accurate interpolation of the third spatial derivative of the state
  272. d3_vals(ll) = -stencil(1) + 3*stencil(2) - 3*stencil(3) + stencil(4)
  273. enddo
  274. !Compute density, u-wind, w-wind, potential temperature, and pressure (r,u,w,t,p respectively)
  275. r = vals(ID_DENS) + hy_dens_int(k)
  276. u = vals(ID_UMOM) / r
  277. w = vals(ID_WMOM) / r
  278. t = ( vals(ID_RHOT) + hy_dens_theta_int(k) ) / r
  279. p = C0*(r*t)**gamma - hy_pressure_int(k)
  280. !Enforce vertical boundary condition and exact mass conservation
  281. if (k == 1 .or. k == nz+1) then
  282. w = 0
  283. d3_vals(ID_DENS) = 0
  284. endif
  285. !Compute the flux vector with hyperviscosity
  286. flux(i,k,ID_DENS) = r*w - hv_coef*d3_vals(ID_DENS)
  287. flux(i,k,ID_UMOM) = r*w*u - hv_coef*d3_vals(ID_UMOM)
  288. flux(i,k,ID_WMOM) = r*w*w+p - hv_coef*d3_vals(ID_WMOM)
  289. flux(i,k,ID_RHOT) = r*w*t - hv_coef*d3_vals(ID_RHOT)
  290. enddo
  291. enddo
  292. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  293. !! TODO: THREAD ME
  294. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  295. !Use the fluxes to compute tendencies for each cell
  296. do ll = 1 , NUM_VARS
  297. do k = 1 , nz
  298. do i = 1 , nx
  299. tend(i,k,ll) = -( flux(i,k+1,ll) - flux(i,k,ll) ) / dz
  300. if (ll == ID_WMOM) then
  301. tend(i,k,ID_WMOM) = tend(i,k,ID_WMOM) - state(i,k,ID_DENS)*grav
  302. endif
  303. enddo
  304. enddo
  305. enddo
  306. end subroutine compute_tendencies_z
  307. !Set halo values in the x-direction.
  308. subroutine set_halo_values_x(state)
  309. implicit none
  310. real(rp), intent(inout) :: state(1-hs:nx+hs,1-hs:nz+hs,NUM_VARS)
  311. integer :: k, ll
  312. real(rp) :: z
  313. do ll = 1 , NUM_VARS
  314. do k = 1 , nz
  315. state(-1 ,k,ll) = state(nx-1,k,ll)
  316. state(0 ,k,ll) = state(nx ,k,ll)
  317. state(nx+1,k,ll) = state(1 ,k,ll)
  318. state(nx+2,k,ll) = state(2 ,k,ll)
  319. enddo
  320. enddo
  321. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  322. if (myrank == 0) then
  323. do k = 1 , nz
  324. z = (k_beg-1 + k-0.5_rp)*dz
  325. if (abs(z-3*zlen/4) <= zlen/16) then
  326. state(-1:0,k,ID_UMOM) = (state(-1:0,k,ID_DENS)+hy_dens_cell(k)) * 50._rp
  327. state(-1:0,k,ID_RHOT) = (state(-1:0,k,ID_DENS)+hy_dens_cell(k)) * 298._rp - hy_dens_theta_cell(k)
  328. endif
  329. enddo
  330. endif
  331. end subroutine set_halo_values_x
  332. !Set halo values in the z-direction.
  333. !decomposition in the vertical direction
  334. subroutine set_halo_values_z(state)
  335. implicit none
  336. real(rp), intent(inout) :: state(1-hs:nx+hs,1-hs:nz+hs,NUM_VARS)
  337. integer :: i, ll
  338. real(rp), parameter :: mnt_width = xlen/8
  339. real(rp) :: x, xloc, mnt_deriv
  340. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  341. !! TODO: THREAD ME
  342. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  343. do ll = 1 , NUM_VARS
  344. do i = 1-hs,nx+hs
  345. if (ll == ID_WMOM) then
  346. state(i,-1 ,ll) = 0
  347. state(i,0 ,ll) = 0
  348. state(i,nz+1,ll) = 0
  349. state(i,nz+2,ll) = 0
  350. else
  351. state(i,-1 ,ll) = state(i,1 ,ll)
  352. state(i,0 ,ll) = state(i,1 ,ll)
  353. state(i,nz+1,ll) = state(i,nz,ll)
  354. state(i,nz+2,ll) = state(i,nz,ll)
  355. endif
  356. enddo
  357. enddo
  358. end subroutine set_halo_values_z
  359. !Initialize some grid settings, allocate data, and initialize the full grid and data
  360. subroutine init()
  361. implicit none
  362. integer :: i, k, ii, kk, ll, ierr
  363. real(rp) :: x, z, r, u, w, t, hr, ht
  364. !Set the cell grid size
  365. dx = xlen / nx_glob
  366. dz = zlen / nz_glob
  367. nranks = 1
  368. myrank = 0
  369. i_beg = 1
  370. nx = nx_glob
  371. left_rank = 0
  372. right_rank = 0
  373. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  374. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  375. !! YOU DON'T NEED TO ALTER ANYTHING BELOW THIS POINT IN THE CODE
  376. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  377. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  378. k_beg = 1
  379. nz = nz_glob
  380. !Allocate the model data
  381. allocate(state (1-hs:nx+hs,1-hs:nz+hs,NUM_VARS))
  382. allocate(state_tmp (1-hs:nx+hs,1-hs:nz+hs,NUM_VARS))
  383. allocate(flux (nx+1,nz+1,NUM_VARS))
  384. allocate(tend (nx,nz,NUM_VARS))
  385. allocate(hy_dens_cell (1-hs:nz+hs))
  386. allocate(hy_dens_theta_cell(1-hs:nz+hs))
  387. allocate(hy_dens_int (nz+1))
  388. allocate(hy_dens_theta_int (nz+1))
  389. allocate(hy_pressure_int (nz+1))
  390. !Define the maximum stable time step based on an assumed maximum wind speed
  391. dt = min(dx,dz) / max_speed * cfl
  392. !Set initial elapsed model time and output_counter to zero
  393. etime = 0
  394. output_counter = 0
  395. !Display some grid information
  396. write(*,*) 'nx_glob, nz_glob:', nx_glob, nz_glob
  397. write(*,*) 'dx,dz: ',dx,dz
  398. write(*,*) 'dt: ',dt
  399. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  400. !! Initialize the cell-averaged fluid state via Gauss-Legendre quadrature
  401. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  402. state = 0
  403. do k = 1-hs , nz+hs
  404. do i = 1-hs , nx+hs
  405. !Initialize the state to zero
  406. !Use Gauss-Legendre quadrature to initialize a hydrostatic balance + temperature perturbation
  407. do kk = 1 , nqpoints
  408. do ii = 1 , nqpoints
  409. !Compute the x,z location within the global domain based on cell and quadrature index
  410. x = (i_beg-1 + i-0.5_rp) * dx + (qpoints(ii)-0.5_rp)*dx
  411. z = (k_beg-1 + k-0.5_rp) * dz + (qpoints(kk)-0.5_rp)*dz
  412. !Set the fluid state based on the user's specification
  413. call injection (x,z,r,u,w,t,hr,ht)
  414. !Store into the fluid state array
  415. state(i,k,ID_DENS) = state(i,k,ID_DENS) + r * qweights(ii)*qweights(kk)
  416. state(i,k,ID_UMOM) = state(i,k,ID_UMOM) + (r+hr)*u * qweights(ii)*qweights(kk)
  417. state(i,k,ID_WMOM) = state(i,k,ID_WMOM) + (r+hr)*w * qweights(ii)*qweights(kk)
  418. state(i,k,ID_RHOT) = state(i,k,ID_RHOT) + ( (r+hr)*(t+ht) - hr*ht ) * qweights(ii)*qweights(kk)
  419. enddo
  420. enddo
  421. do ll = 1 , NUM_VARS
  422. state_tmp(i,k,ll) = state(i,k,ll)
  423. enddo
  424. enddo
  425. enddo
  426. !Compute the hydrostatic background state over vertical cell averages
  427. hy_dens_cell = 0
  428. hy_dens_theta_cell = 0
  429. do k = 1-hs , nz+hs
  430. do kk = 1 , nqpoints
  431. z = (k_beg-1 + k-0.5_rp) * dz + (qpoints(kk)-0.5_rp)*dz
  432. !Set the fluid state based on the user's specification
  433. call injection (0._rp,z,r,u,w,t,hr,ht)
  434. hy_dens_cell(k) = hy_dens_cell(k) + hr * qweights(kk)
  435. hy_dens_theta_cell(k) = hy_dens_theta_cell(k) + hr*ht * qweights(kk)
  436. enddo
  437. enddo
  438. !Compute the hydrostatic background state at vertical cell interfaces
  439. do k = 1 , nz+1
  440. z = (k_beg-1 + k-1) * dz
  441. call injection (0._rp,z,r,u,w,t,hr,ht)
  442. hy_dens_int (k) = hr
  443. hy_dens_theta_int(k) = hr*ht
  444. hy_pressure_int (k) = C0*(hr*ht)**gamma
  445. enddo
  446. end subroutine init
  447. !This test case is initially balanced but injects fast, cold air from the left boundary near the model top
  448. subroutine injection(x,z,r,u,w,t,hr,ht)
  449. implicit none
  450. real(rp), intent(in ) :: x, z !x- and z- location of the point being sampled
  451. real(rp), intent( out) :: r, u, w, t !Density, uwind, wwind, and potential temperature
  452. real(rp), intent( out) :: hr, ht !Hydrostatic density and potential temperature
  453. call hydro_const_theta(z,hr,ht)
  454. r = 0
  455. t = 0
  456. u = 0
  457. w = 0
  458. end subroutine injection
  459. !Establish hydrstatic balance using constant potential temperature (thermally neutral atmosphere)
  460. subroutine hydro_const_theta(z,r,t)
  461. implicit none
  462. real(rp), intent(in ) :: z !x- and z- location of the point being sampled
  463. real(rp), intent( out) :: r, t !Density and potential temperature at this point
  464. real(rp), parameter :: theta0 = 300._rp !Background potential temperature
  465. real(rp), parameter :: exner0 = 1._rp !Surface-level Exner pressure
  466. real(rp) :: p,exner,rt
  467. !Establish hydrostatic balance first using Exner pressure
  468. t = theta0 !Potential Temperature at z
  469. exner = exner0 - grav * z / (cp * theta0) !Exner pressure at z
  470. p = p0 * exner**(cp/rd) !Pressure at z
  471. rt = (p / c0)**(1._rp / gamma) !rho*theta at z
  472. r = rt / t !Density at z
  473. end subroutine hydro_const_theta
  474. !Deallocate and call Finalize
  475. subroutine finalize()
  476. implicit none
  477. integer :: ierr
  478. deallocate(state )
  479. deallocate(state_tmp )
  480. deallocate(flux )
  481. deallocate(tend )
  482. deallocate(hy_dens_cell )
  483. deallocate(hy_dens_theta_cell)
  484. deallocate(hy_dens_int )
  485. deallocate(hy_dens_theta_int )
  486. deallocate(hy_pressure_int )
  487. end subroutine finalize
  488. !Output the fluid state (state) to a NetCDF file at a given elapsed model time (etime)
  489. !The file I/O uses parallel-netcdf, the only external library required for this mini-app.
  490. !If it's too cumbersome, you can comment the I/O out, but you'll miss out on some potentially cool graphics
  491. subroutine output(state,etime)
  492. implicit none
  493. real(rp), intent(in) :: state(1-hs:nx+hs,1-hs:nz+hs,NUM_VARS)
  494. real(rp), intent(in) :: etime
  495. integer :: ncid, t_dimid, x_dimid, z_dimid, dens_varid, uwnd_varid, wwnd_varid, theta_varid, t_varid
  496. integer :: i,k, __LINE__
  497. integer, save :: num_out = 0
  498. integer :: len, st1(1),ct1(1),st3(3),ct3(3)
  499. !Temporary arrays to hold density, u-wind, w-wind, and potential temperature (theta)
  500. real(rp), allocatable :: dens(:,:), uwnd(:,:), wwnd(:,:), theta(:,:)
  501. real(rp) :: etimearr(1)
  502. !Inform the user
  503. write(*,*) '*** OUTPUT ***'
  504. !Allocate some (big) temp arrays
  505. allocate(dens (nx,nz))
  506. allocate(uwnd (nx,nz))
  507. allocate(wwnd (nx,nz))
  508. allocate(theta(nx,nz))
  509. !If the elapsed time is zero, create the file. Otherwise, open the file
  510. if (etime == 0) then
  511. !Create the file
  512. call ncwrap( nf90_create('reference.nc' , nf90_clobber , ncid ) , __LINE__ )
  513. !Create the dimensions
  514. len=nf90_unlimited; call ncwrap( nf90_def_dim( ncid , 't' , len , t_dimid ) , __LINE__ )
  515. len=nx_glob ; call ncwrap( nf90_def_dim( ncid , 'x' , len , x_dimid ) , __LINE__ )
  516. len=nz_glob ; call ncwrap( nf90_def_dim( ncid , 'z' , len , z_dimid ) , __LINE__ )
  517. !Create the variables
  518. call ncwrap( nf_def_var( ncid , 't' , NF90_DOUBLE , 1 , (/ t_dimid /) , t_varid ) , __LINE__ )
  519. call ncwrap( nf_def_var( ncid , 'dens' , nf90_double , 3 , (/ x_dimid , z_dimid , t_dimid /) , dens_varid ) , __LINE__ )
  520. call ncwrap( nf_def_var( ncid , 'uwnd' , nf90_double , 3 , (/ x_dimid , z_dimid , t_dimid /) , uwnd_varid ) , __LINE__ )
  521. call ncwrap( nf_def_var( ncid , 'wwnd' , nf90_double , 3 , (/ x_dimid , z_dimid , t_dimid /) , wwnd_varid ) , __LINE__ )
  522. call ncwrap( nf_def_var( ncid , 'theta' , nf90_double , 3 , (/ x_dimid , z_dimid , t_dimid /) , theta_varid ) , __LINE__ )
  523. !End "define" mode
  524. call ncwrap( nf90_enddef( ncid ) , __LINE__ )
  525. else
  526. !Open the file
  527. call ncwrap( nf90_open( 'reference.nc' , nf90_write , ncid ) , __LINE__ )
  528. !Get the variable IDs
  529. call ncwrap( nf90_inq_varid( ncid , 'dens' , dens_varid ) , __LINE__ )
  530. call ncwrap( nf90_inq_varid( ncid , 'uwnd' , uwnd_varid ) , __LINE__ )
  531. call ncwrap( nf90_inq_varid( ncid , 'wwnd' , wwnd_varid ) , __LINE__ )
  532. call ncwrap( nf90_inq_varid( ncid , 'theta' , theta_varid ) , __LINE__ )
  533. call ncwrap( nf90_inq_varid( ncid , 't' , t_varid ) , __LINE__ )
  534. endif
  535. !Store perturbed values in the temp arrays for output
  536. do k = 1 , nz
  537. do i = 1 , nx
  538. dens (i,k) = state(i,k,ID_DENS)
  539. uwnd (i,k) = state(i,k,ID_UMOM) / ( hy_dens_cell(k) + state(i,k,ID_DENS) )
  540. wwnd (i,k) = state(i,k,ID_WMOM) / ( hy_dens_cell(k) + state(i,k,ID_DENS) )
  541. theta(i,k) = ( state(i,k,ID_RHOT) + hy_dens_theta_cell(k) ) / ( hy_dens_cell(k) + state(i,k,ID_DENS) ) - hy_dens_theta_cell(k) / hy_dens_cell(k)
  542. enddo
  543. enddo
  544. !Write the grid data to file with all the processes writing collectively
  545. st3=(/i_beg,k_beg,num_out+1/); ct3=(/nx,nz,1/); call ncwrap( nf_put_vara_double( ncid , dens_varid , st3 , ct3 , dens ) , __LINE__ )
  546. st3=(/i_beg,k_beg,num_out+1/); ct3=(/nx,nz,1/); call ncwrap( nf_put_vara_double( ncid , uwnd_varid , st3 , ct3 , uwnd ) , __LINE__ )
  547. st3=(/i_beg,k_beg,num_out+1/); ct3=(/nx,nz,1/); call ncwrap( nf_put_vara_double( ncid , wwnd_varid , st3 , ct3 , wwnd ) , __LINE__ )
  548. st3=(/i_beg,k_beg,num_out+1/); ct3=(/nx,nz,1/); call ncwrap( nf_put_vara_double( ncid , theta_varid , st3 , ct3 , theta ) , __LINE__ )
  549. !Only the master process needs to write the elapsed time
  550. !write elapsed time to file
  551. st1=(/num_out+1/); ct1=(/1/); etimearr(1) = etime; call ncwrap( nf_put_vara_double( ncid , t_varid , st1 , ct1 , etimearr ) , __LINE__ )
  552. !Close the file
  553. call ncwrap( nf90_close(ncid) , __LINE__ )
  554. !Increment the number of outputs
  555. num_out = num_out + 1
  556. !Deallocate the temp arrays
  557. deallocate(dens )
  558. deallocate(uwnd )
  559. deallocate(wwnd )
  560. deallocate(theta)
  561. end subroutine output
  562. !Error reporting routine for the NetCDF I/O
  563. subroutine ncwrap( ierr , line )
  564. implicit none
  565. integer, intent(in) :: ierr
  566. integer, intent(in) :: line
  567. if (ierr /= nf90_noerr) then
  568. write(*,*) 'NetCDF Error at line: ', line
  569. write(*,*) nf90_strerror(ierr)
  570. stop
  571. endif
  572. end subroutine ncwrap
  573. end program miniweather