Canoe
Comprehensive Atmosphere N' Ocean Engine
straka.cpp
Go to the documentation of this file.
1 /* -------------------------------------------------------------------------------------
2  * SNAP Example Program
3  *
4  * Contributer:
5  * Cheng Li, University of Michigan
6  *
7  * Year: 2021
8  * Contact: chengcli@umich.edu
9  * Reference: Straka et al., 1993
10  * -------------------------------------------------------------------------------------
11  */
12 
13 // @sect3{Include files}
14 
15 // First we include some Athena++ headers files. They contain declarations
16 // of various components of the solver system.
17 // Athena++ is able to run both single precision (float) and double precision
18 // (double) applications. A macro <code>Real</code> is defined to indicate the
19 // precision and it is define in the following header file.
20 #include <athena/athena.hpp>
21 // Then the basic multi-dimension array that stores fluid dynamic data is
22 // declared in the AthenaArray class.
23 #include <athena/athena_arrays.hpp>
24 #include <athena/stride_iterator.hpp>
25 // The model executes according to the parameters specified in an input file.
26 // The input file and the parameters within are managed by the ParameterInput
27 // class.
28 #include <athena/parameter_input.hpp>
29 // Coordinate related information are stored in the Coordinates class.
30 #include <athena/coordinates/coordinates.hpp>
31 // Everying regarding the equation of state is treated by the EquationOfState
32 // class and declared in the following file.
33 #include <athena/eos/eos.hpp>
34 // Evolution of hydrodynamic fields like pressure, density and velocities are
35 // managed by the Hydro class.
36 #include <athena/hydro/hydro.hpp>
37 // Athena++ can alo simulate magnetohydrodynamics (MHD).
38 // However, since this example is a hydro-only application, the MHD file is only
39 // included as a place holder.
40 #include <athena/field/field.hpp>
41 // Dynamic fields are evolving on a mesh and this is the file working with
42 // meshes and the partition of meshes among processors
43 #include <athena/mesh/mesh.hpp>
44 
45 // The following header file contains the definition of the MeshBlock::Impl
46 // class
47 #include <impl.hpp>
48 
49 // Finally, the Thermodynamics class works with thermodynamic aspects of the
50 // problem such as the temperature, potential temperature, condensation of
51 // vapor, etc.
53 
54 // Functions in the math library are protected by a specific namespace because
55 // math functions are usually concise in the names, such as <code>min</code>,
56 // <code>sqr</code>. It is better to protect them in a namespace to avoid
57 // conflicts.
58 #include <climath/core.h>
59 
60 // Now, we get to the real program.
61 // First, we define several global variables specific to this appliciation
62 // For example, here <code>K</code> is the kinematic visocisty, <code>p0</code>
63 // is the surface pressure, <code>cp</code> is the specific heat capacity, and
64 // <code>Rd</code> is the ideal gas constant of dry air. The <a
65 // href="https://en.wikipedia.org/wiki/Prandtl_number">Prandtl number</a> is 1.
66 Real K, p0, cp, Rd;
67 
68 // @sect3{User-defined output variables}
69 
70 // The hydrodynamic solver evolves density, pressure and velocities with time,
71 // meaning that the (potential) temperature is a diagnostic quantity for output
72 // only. This block of code allocates the memory for the outputs of temperature
73 // and potential temperature.
74 void MeshBlock::InitUserMeshBlockData(ParameterInput *pin) {
75  AllocateUserOutputVariables(2);
76  SetUserOutputVariableName(0, "temp");
77  SetUserOutputVariableName(1, "theta");
78 }
79 
80 // Since temperature and potential temperature are only used for output purpose,
81 // they do not need to be calculated every dynamic time step. The subroutine
82 // below loops over all grids and calculates the value of temperature and
83 // potential temperature before the output time step. Particularly, the pointer
84 // to the Thermodynamics class <code>pthermo</code> calculates the temperature
85 // and potential temperature using its own member function
86 // Thermodynamics::GetTemp and PotentialTemp. <code>pthermo</code>, is a member
87 // of a higher level management class MeshBlock. So, you can use it directly
88 // inside a member function of class MeshBlock. In fact, all physics modules are
89 // managed by MeshBlock. As long as you have a pointer to a MeshBlock, you can
90 // access all physics in the simulation.
91 void MeshBlock::UserWorkBeforeOutput(ParameterInput *pin) {
92  auto pthermo = Thermodynamics::GetInstance();
93 
94  // Loop over the grids. <code>js,je,is,ie</code> are members of the MeshBlock
95  // class. They are integer values representing the start index and the end
96  // index of the grids in each dimension.
97  int k = ks;
98  for (int j = js; j <= je; ++j)
99  for (int i = is; i <= ie; ++i) {
100  // <code>user_out_var</code> stores the actual data.
101  // <code>phydro</code> is a pointer to the Hydro class, which has a member
102  // <code>w</code> that stores density, pressure, and velocities at each
103  // grid.
104  user_out_var(0, j, i) = pthermo->GetTemp(this, k, j, i);
105  user_out_var(1, j, i) = pthermo->PotentialTemp(this, p0, k, j, i);
106  }
107 }
108 
109 // @sect3{Forcing function}
110 
111 // The sinking bubble is forced by the gravity and the viscous and thermal
112 // dissipation. The gravitational forcing is taken care of by other parts of the
113 // program and we only need to write a few lines of code to facilitate
114 // dissipation. The first step is to define a function that takes a pointer to
115 // the MeshBlock as an argument such that we can access all physics via this
116 // pointer. The name of this function is not important. It can be anything. But
117 // the order and types of the arguments must be <code>(MeshBlock *, Real const,
118 // Real const, AthenaArray<Real> const&, AthenaArray<Real> const&,
119 // AthenaArray<Real> &)</code>. They are called the signature of the function.
120 void Diffusion(MeshBlock *pmb, Real const time, Real const dt,
121  AthenaArray<Real> const &w, AthenaArray<Real> const &r,
122  AthenaArray<Real> const &bcc, AthenaArray<Real> &u,
123  AthenaArray<Real> &s) {
124  // <code>pcoord</code> is a pointer to the Coordinates class and it is a
125  // member of the MeshBlock class. We use the pointer to the MeshBlock class,
126  // <code>pmb</code> to access <code>pcoord</code> and use its member function
127  // to get the spacing of the grid.
128  Real dx = pmb->pcoord->dx1f(pmb->is);
129  Real dy = pmb->pcoord->dx2f(pmb->js);
130 
131  // Similarly, we use <code>pmb</code> to find the pointer to the
132  // Thermodynamics class, <code>pthermo</code>.
133  auto pthermo = Thermodynamics::GetInstance();
134 
135  // Loop over the grids.
136  int k = pmb->ks;
137  for (int j = pmb->js; j <= pmb->je; ++j)
138  for (int i = pmb->is; i <= pmb->ie; ++i) {
139  // Similar to what we have done in MeshBlock::UserWorkBeforeOutput, we use
140  // the Thermodynamics class to calculate temperature and potential
141  // temperature.
142  Real temp = pthermo->GetTemp(pmb, pmb->ks, j, i);
143  Real theta = pthermo->PotentialTemp(pmb, p0, pmb->ks, j, i);
144 
145  // The thermal diffusion is applied to the potential temperature field,
146  // which is not exactly correct. But this is the setting of the test
147  // program.
148  Real theta_ip1_j = pthermo->PotentialTemp(pmb, p0, k, j + 1, i);
149  Real theta_im1_j = pthermo->PotentialTemp(pmb, p0, k, j - 1, i);
150  Real theta_i_jp1 = pthermo->PotentialTemp(pmb, p0, k, j, i + 1);
151  Real theta_i_jm1 = pthermo->PotentialTemp(pmb, p0, k, j, i - 1);
152 
153  // Add viscous dissipation to the velocities. Now you encounter another
154  // variable called <code>u</code>. <code>u</code> stands for `conserved
155  // variables`, which are density, momentums and total energy (kinetic +
156  // internal). In contrast, <code>w</code> stands for `primitive
157  // variables`, which are density, velocities, and pressure. Their relation
158  // is handled by the EquationOfState class. The `conserved variables` are
159  // meant for internal calculation. Solving for `conserved variables`
160  // guarantees the conservation properties. However, `conserved variables`
161  // are not easy to use for diagnostics. Therefore, another group of
162  // variables called the `primitive variables` are introduced for external
163  // calculations, like calculating transport fluxes, radiative fluxes and
164  // interacting with other physical components of the system. In this case,
165  // the diffusion is calculated by using the `primitive variabes` and the
166  // result is added to the `conserved variables` to ensure conservation
167  // properties.
168  u(IM1, j, i) += dt * K * w(IDN, j, i) / (dx * dy) *
169  (w(IVX, j, i - 1) + w(IVX, j, i + 1) + w(IVX, j - 1, i) +
170  w(IVX, j + 1, i) - 4. * w(IVX, j, i));
171  u(IM2, j, i) += dt * K * w(IDN, j, i) / (dx * dy) *
172  (w(IVY, j, i - 1) + w(IVY, j, i + 1) + w(IVY, j - 1, i) +
173  w(IVY, j + 1, i) - 4. * w(IVY, j, i));
174 
175  // Adding thermal dissipation is similar to adding viscous dissipation.
176  u(IEN, j, i) +=
177  dt * K * w(IDN, j, i) / (dx * dy) * cp * temp / theta *
178  (theta_ip1_j + theta_im1_j + theta_i_jp1 + theta_i_jm1 - 4. * theta);
179  }
180 }
181 
182 // @sect3{Program specific variables}
183 
184 // This is the place where program specific variables are initialized.
185 // Not that the function is a member function of the Mesh class rather than the
186 // MeshBlock class we have been working with. The difference between class Mesh
187 // and class MeshBlock is that class Mesh is an all-encompassing class that
188 // manages multiple MeshBlocks while class MeshBlock manages all physics
189 // modules. During the instantiation of the classes. class Mesh is instantiated
190 // first and then it instantiates all MeshBlocks inside it. Therefore, this
191 // subroutine runs before any MeshBlock.
192 void Mesh::InitUserMeshData(ParameterInput *pin) {
193  // The program specific forcing parameters are set here.
194  // They come from the input file, which is parsed by the ParameterInput class
195  Real gamma = pin->GetReal("hydro", "gamma");
196  K = pin->GetReal("problem", "K");
197  p0 = pin->GetReal("problem", "p0");
198  Rd = pin->GetReal("thermodynamics", "Rd");
199  cp = gamma / (gamma - 1.) * Rd;
200 
201  // This line code enrolls the forcing function we wrote in
202  // section <a href="#Forcingfunction">Forcing function</a>
203  EnrollUserExplicitSourceFunction(Diffusion);
204 }
205 
206 // @sect3{Initial condition}
207 
208 // This is the final part of the program, in which we set the initial condition.
209 // It is called the `problem generator` in a general Athena++ application.
210 void MeshBlock::ProblemGenerator(ParameterInput *pin) {
211  // Get the value of gravity. Positive is upward and negative is downward.
212  // We take the negative to get the absolute value.
213  Real grav = -phydro->hsrc.GetG1();
214  // These lines read the parameter values in the input file, which
215  // are organized into sections. The problem specific parameters are usually
216  // placed under section `problem`.
217  Real Ts = pin->GetReal("problem", "Ts");
218  Real xc = pin->GetReal("problem", "xc");
219  Real xr = pin->GetReal("problem", "xr");
220  Real zc = pin->GetReal("problem", "zc");
221  Real zr = pin->GetReal("problem", "zr");
222  Real dT = pin->GetReal("problem", "dT");
223 
224  // Loop over the grids. The purpose is to set the temperature, pressure and
225  // density fields at each cell-centered grid.
226  for (int j = js; j <= je; ++j) {
227  for (int i = is; i <= ie; ++i) {
228  // Get the Cartesian coordinates in the vertical and horizontal
229  // directions. `x1` is usually the vertical direction and `x2` is the
230  // horizontal direction. The meaning of `x1` and `x2` may change with the
231  // coordinate system. <code>x1v</code> and <code>x2v</code> retrieve the
232  // coordinate at cell centers.
233  Real x1 = pcoord->x1v(i);
234  Real x2 = pcoord->x2v(j);
235  // Distance to the center of a cold air bubble at (xc,zc).
236  Real L = sqrt(sqr((x2 - xc) / xr) + sqr((x1 - zc) / zr));
237  // Adiabatic temperature gradient.
238  Real temp = Ts - grav * x1 / cp;
239  // Once we know the temperature, we can calculate the adiabatic and
240  // hydrostatic pressure as @f$p=p_0(\frac{T}{T_s})^{c_p/R_d}@f$, where @f$p_0@f$
241  // is the surface pressure and @f$T_s@f$ is the surface temperature.
242  phydro->w(IPR, j, i) = p0 * pow(temp / Ts, cp / Rd);
243  if (L <= 1.) temp += dT * (cos(M_PI * L) + 1.) / 2.;
244  // Set density using ideal gas law.
245  phydro->w(IDN, j, i) = phydro->w(IPR, j, i) / (Rd * temp);
246  // Initialize velocities to zero.
247  phydro->w(IVX, j, i) = phydro->w(IVY, j, i) = 0.;
248  }
249  }
250 
251  // We have set all `primitive variables`. The last step is to calculate the
252  // `conserved variables` based on the `primitive variables`. This is done by
253  // calling the member function EquationOfState::PrimitiveToConserved. Note
254  // that <code>pfield->bcc</code> is a placeholder because there is not MHD
255  // involved in this example.
256  peos->PrimitiveToConserved(phydro->w, pfield->bcc, phydro->u, pcoord, is, ie,
257  js, je, ks, ke);
258 }
static Thermodynamics const * GetInstance()
Return a pointer to the one and only instance of Thermodynamics.
double sqr(double x)
Definition: core.h:14
float temp
Definition: fit_ammonia.py:6
Real Rd
Definition: straka.cpp:66
void Diffusion(MeshBlock *pmb, Real const time, Real const dt, AthenaArray< Real > const &w, AthenaArray< Real > const &r, AthenaArray< Real > const &bcc, AthenaArray< Real > &u, AthenaArray< Real > &s)
Definition: straka.cpp:120
Real cp
Definition: straka.cpp:66
Real p0
Definition: straka.cpp:66
Real K
Definition: straka.cpp:66