Canoe
Comprehensive Atmosphere N' Ocean Engine
profile_inversion.cpp
Go to the documentation of this file.
1 // C/C++
2 #include <algorithm>
3 #include <iostream>
4 #include <memory>
5 #include <sstream>
6 #include <string>
7 
8 // canoe
9 #include <configure.hpp>
10 #include <impl.hpp>
11 
12 // climath
13 #include <climath/root.h>
14 
15 // athena
16 #include <athena/athena.hpp>
17 #include <athena/coordinates/coordinates.hpp>
18 #include <athena/globals.hpp>
19 #include <athena/hydro/hydro.hpp>
20 #include <athena/mesh/mesh.hpp>
21 #include <athena/stride_iterator.hpp>
22 
23 // application
24 #include <application/application.hpp>
25 #include <application/exceptions.hpp>
26 
27 // snap
29 
30 // utils
31 #include <utils/ndarrays.hpp>
32 #include <utils/vectorize.hpp>
33 
34 // inversion
35 #include "gaussian_process.hpp"
36 #include "profile_inversion.hpp"
37 
39 
40 ProfileInversion::ProfileInversion(MeshBlock *pmb, ParameterInput *pin,
41  std::string name)
42  : Inversion(pmb, pin, name) {
43  Application::Logger app("inversion");
44  app->Log("Initializing ProfileInversion");
45  char buf[80];
46 
47  // species id
48  idx_ =
49  Vectorize<int>(pin->GetString("inversion", name + ".variables").c_str());
50 
51  // read in prior
52  for (auto m : idx_) {
53  if (m == IDN) { // change temperature
54  Xstd_[IDN] = pin->GetReal("inversion", name + ".tem.std");
55  Xlen_[IDN] =
56  pin->GetReal("inversion", name + ".tem.corr.km") * 1.E3; // km -> m
57  app->Log(name + "::temperature std" + std::to_string(Xstd_[IDN]));
58  app->Log(name + "::temperature correlation length" +
59  std::to_string(Xlen_[0]));
60  } else {
61  Xstd_[m] = pin->GetReal("inversion", name + ".qvapor" +
62  std::to_string(m) + ".std.gkg") /
63  1.E3;
64  // km -> m
65  Xlen_[m] = pin->GetReal("inversion", name + ".qvapor" +
66  std::to_string(m) + ".corr.km") *
67  1.E3;
68  snprintf(buf, sizeof(buf),
69  "%s::vapor %d standard deviation = ", name.c_str(), m);
70  app->Log(buf + std::to_string(Xstd_[m]));
71  snprintf(buf, sizeof(buf),
72  "%s::vapor %d correlation length = ", name.c_str(), m);
73  app->Log(buf + std::to_string(Xlen_[m]));
74  }
75  }
76 
77  // power law coefficient
78  chi_ = pin->GetOrAddReal("inversion", name + ".chi", 0.0);
79 
80  // Pressure sample
81  plevel_ =
82  Vectorize<Real>(pin->GetString("inversion", name + ".PrSample").c_str());
83  int ndim = idx_.size() * plevel_.size();
84 
85  app->Log(name + "::number of input dimension = " + std::to_string(ndim));
86  // app->Log(name + "::inversion pressure levels (bars) = ", plevel_);
87 
88  // add boundaries
89  Real pmax = pin->GetReal("inversion", name + ".Pmax");
90  Real pmin = pin->GetReal("inversion", name + ".Pmin");
91  if (pmax < (plevel_.front() + 1.E-6))
92  throw RuntimeError("ProfileInversion",
93  "Pmax < " + std::to_string(plevel_.front()));
94 
95  if (pmin > (plevel_.back() - 1.E-6))
96  throw RuntimeError("ProfileInversion",
97  "Pmin > " + std::to_string(plevel_.back()));
98 
99  plevel_.insert(plevel_.begin(), pmax);
100  plevel_.push_back(pmin);
101  // app->Log(name + "::top boundary = ", pmin);
102  // app->Log(name + "::bottom boundary =", pmax);
103 
104  // bar -> pa
105  for (size_t n = 0; n < plevel_.size(); ++n) plevel_[n] *= 1.E5;
106 
107  // output dimension
108  int nvalue = target_.size();
109  // app->Log("number of output dimension = ", nvalue);
110 
111  // number of walkers
112  int nwalker = pmb->block_size.nx3;
113  // app->Log("walkers per block =", nwalker);
114  // app->Log("total number of walkers = ", pmb->pmy_mesh->mesh_size.nx3);
115  if ((nwalker < 2) && pmb->pmy_mesh->nlim > 0) {
116  throw RuntimeError("ProfileInversion", "nwalker (nx3) must be at least 2");
117  }
118 
119  // initialize mcmc chain
120  InitializeChain(pmb->pmy_mesh->nlim + 1, nwalker, ndim, nvalue);
121 }
122 
124  int nwalker = GetWalkers();
125  int ndim = GetDims();
126  int nsample = samples(); // excluding boundaries
127 
128  Application::Logger app("inversion");
129 
130  // initialize random positions
131  app->Log("Initializing random positions for walkers");
132 
133  unsigned int seed = time(NULL) + Globals::my_rank;
134  NewCArray(init_pos_, nwalker, ndim);
135 
136  Real reference_pressure = pmy_block_->pcoord->GetReferencePressure();
137  for (int n = 0; n < nwalker; ++n) {
138  int ip = 0;
139  for (auto m : idx_) {
140  for (int i = 0; i < nsample; ++i)
141  init_pos_[n][ip * nsample + i] =
142  (1. * rand_r(&seed) / RAND_MAX - 0.5) * Xstd_[m] *
143  pow(reference_pressure / plevel_[i + 1], chi_);
144  ip++;
145  }
146  }
147 }
148 
149 void ProfileInversion::UpdateHydro(Hydro *phydro, ParameterInput *pin) const {
150  // read profile updates from input
151  char buf[80];
152  int nsample = samples(); // excluding boundaries
153  Application::Logger app("inversion");
154  app->Log("UpdateHydro");
155 
156  // prepare inversion model
157  Real **XpSample;
158  NewCArray(XpSample, 1 + NVAPOR, nsample + 2);
159  std::fill(*XpSample, *XpSample + (1 + NVAPOR) * (nsample + 2), 0.);
160 
161  int is = pmy_block_->is, js = pmy_block_->js, ks = pmy_block_->ks;
162  int ie = pmy_block_->ie, ke = pmy_block_->ke;
163 
164  for (auto m : idx_) {
165  if (m == IDN) { // temperature
166  snprintf(buf, sizeof(buf), "%s.tema.K", GetName().c_str());
167  } else { // vapors
168  snprintf(buf, sizeof(buf), "%s.qvapor%da.gkg", GetName().c_str(), m);
169  }
170  std::string str = pin->GetString("problem", buf);
171  std::vector<Real> qa = Vectorize<Real>(str.c_str(), " ,");
172 
173  if (qa.size() != nsample) {
174  throw ValueError("UpdateHydro", buf, qa.size(), nsample);
175  }
176 
177  // g/kg -> kg/kg
178  for (int i = 1; i <= nsample; ++i) {
179  XpSample[m][i] = qa[i - 1] / 1.E3;
180  }
181 
182  for (int k = ks; k <= ke; ++k) {
183  // revise atmospheric profiles
184  UpdateProfiles(phydro, XpSample, k, jl_, ju_);
185 
186  // set the revised profile to js-1
187  for (int n = 0; n < NHYDRO; ++n)
188  for (int i = is; i <= ie; ++i) {
189  phydro->w(n, k, js - 1, i) = phydro->w(n, k, ju_, i);
190  }
191  }
192  }
193 
194  FreeCArray(XpSample);
195 }
196 
197 void ProfileInversion::UpdateProfiles(Hydro *phydro, Real **XpSample, int k,
198  int jl, int ju) const {
199  Application::Logger app("inversion");
200  app->Log("Updating profiles at k = " + std::to_string(k));
201 
202  if (ju - jl != idx_.size()) {
203  throw RuntimeError("UpdateProfiles",
204  "Number of allocations in x2 should be " +
205  std::to_string(idx_.size() + 1));
206  }
207 
208  int is = pmy_block_->is, ie = pmy_block_->ie;
209 
210  int nlayer = ie - is + 1;
211  int nsample = plevel_.size();
212 
213  std::vector<Real> zlev(nsample);
214  Real P0 = pmy_block_->pcoord->GetReferencePressure();
215  Real H0 = pmy_block_->pcoord->GetPressureScaleHeight();
216 
217  for (int i = 0; i < nsample; ++i) {
218  zlev[i] = -H0 * log(plevel_[i] / P0);
219  }
220  // app->Log("sample levels in height = ", zlev);
221 
222  // calculate the covariance matrix of T
223  std::vector<Real> stdAll(nlayer);
224  std::vector<Real> stdSample(nsample);
225  Real **Xp;
226  NewCArray(Xp, 1 + NVAPOR, nlayer);
227 
228  // copy previous jl-1 -> jl .. ju
229  for (int n = 0; n < NHYDRO; ++n)
230  for (int j = jl; j <= ju; ++j)
231  for (int i = is; i <= ie; ++i)
232  phydro->w(n, k, j, i) = phydro->w(n, k, jl - 1, i);
233 
234  auto pthermo = Thermodynamics::GetInstance();
235  auto pmb = pmy_block_;
236  Real Rd = pthermo->GetRd();
237 
238  // calculate perturbed profiles
239  app->Log("Update profiles");
240  auto pcoord = pmb->pcoord;
241  for (size_t n = 0; n < idx_.size(); ++n) {
242  int jn = jl + n;
243  int m = idx_[n];
244 
245  for (int i = is; i <= ie; ++i)
246  stdAll[i - is] = Xstd_[m] * pow(exp(pcoord->x1v(i) / H0), chi_);
247 
248  for (int i = 0; i < nsample; ++i)
249  stdSample[i] = Xstd_[m] * pow(exp(zlev[i] / H0), chi_);
250 
251  gp_predict(SquaredExponential, Xp[m], &pcoord->x1v(is), stdAll.data(),
252  nlayer, XpSample[m], zlev.data(), stdSample.data(), nsample,
253  Xlen_[m]);
254 
255  for (int i = is; i <= ie; ++i) {
256  Real temp = pthermo->GetTemp(pmb, k, jn, i);
257 
258  // do not alter levels lower than zlev[0] or higher than zlev[nsample-1]
259  if (pcoord->x1v(i) < zlev[0] || pcoord->x1v(i) > zlev[nsample - 1])
260  continue;
261 
262  // save perturbed T profile
263  if (m == IDN) {
264  if (temp + Xp[m][i - is] < 0.) {
265  temp = 1.; // min 1K temperature
266  } else {
267  temp += Xp[m][i - is];
268  }
269  phydro->w(IDN, k, jn, i) = phydro->w(IPR, k, jn, i) /
270  (Rd * temp * pthermo->RovRd(pmb, k, jn, i));
271  } else { // save perturbed compositional profiles
272  phydro->w(m, k, jn, i) += Xp[m][i - is];
273  phydro->w(m, k, jn, i) = std::max(phydro->w(m, k, jn, i), 0.);
274  phydro->w(m, k, jn, i) = std::min(phydro->w(m, k, jn, i), 1.);
275  }
276  }
277  }
278 
279  // copy final adjusted profiles to ju
280  // set density to reflect temperature
281  Real *temp1d = new Real[nlayer];
282 
283  for (int i = is; i <= ie; ++i) {
284  // get initial temperature from jl-1
285  temp1d[i - is] = pthermo->GetTemp(pmb, k, jl - 1, i);
286 
287  for (size_t n = 0; n < idx_.size(); ++n) {
288  int jn = jl + n;
289  int m = idx_[n];
290  if (m == IDN) {
291  // override with new temperature
292  temp1d[i - is] = pthermo->GetTemp(pmb, k, jn, i);
293  } else {
294  phydro->w(m, k, ju, i) = phydro->w(m, k, jn, i);
295  }
296  }
297 
298  // reset temperature given new concentration
299  phydro->w(IDN, k, ju, i) =
300  phydro->w(IPR, k, ju, i) /
301  (Rd * temp1d[i - is] * pthermo->RovRd(pmb, k, ju, i));
302  }
303 
304  delete[] temp1d;
305  FreeCArray(Xp);
306 
307  ConvectiveAdjustment(phydro, k, ju);
308 }
309 
310 void ProfileInversion::ConvectiveAdjustment(Hydro *phydro, int k,
311  int ju) const {
312  Application::Logger app("inversion");
313  app->Log("doing convective adjustment");
314 
315  int is = pmy_block_->is, ie = pmy_block_->ie;
316 
317  auto pthermo = Thermodynamics::GetInstance();
318  auto pmb = pmy_block_;
319 
320  auto &&ac = AirParcelHelper::gather_from_primitive(pmb, k, ju, is, ie - 1);
321 
322  for (int i = is + 1; i <= ie; ++i) {
323  auto &air = ac[i - is - 1];
324  air.ToMoleFraction();
325 
326  Real dlnp = log(phydro->w(IPR, k, ju, i) / phydro->w(IPR, k, ju, i - 1));
327  pthermo->Extrapolate(&air, dlnp, Thermodynamics::Method::NeutralStability);
328 
329  air.ToMassFraction();
330 
331  // stability
332  phydro->w(IDN, k, ju, i) = std::min(air.w[IDN], phydro->w(IDN, k, ju, i));
333 
334  // saturation
335  for (int n = 1; n <= NVAPOR; ++n) {
336  Real rh = pthermo->RelativeHumidity(pmb, n, k, ju, i);
337  if (rh > 1.) phydro->w(n, k, ju, i) /= rh;
338  }
339  }
340 }
void InitializeChain(int nstep, int nwalker, int ndim, int nvalue)
Definition: inversion.cpp:71
Eigen::VectorXd target_
Definition: inversion.hpp:84
int GetDims() const
Definition: inversion.hpp:61
Real ** init_pos_
Definition: inversion.hpp:88
int GetWalkers() const
Definition: inversion.hpp:63
MeshBlock const * pmy_block_
pointer to parent MeshBlock
Definition: inversion.hpp:97
std::string GetName() const
Real Xstd_[1+NVAPOR]
std::vector< int > idx_
void UpdateHydro(Hydro *phydro, ParameterInput *pin) const override
void InitializePositions() override
Real Xlen_[1+NVAPOR]
std::vector< Real > plevel_
void ConvectiveAdjustment(Hydro *phydro, int k, int ju) const
ProfileInversion(MeshBlock *pmb, ParameterInput *pin, std::string name)
void UpdateProfiles(Hydro *phydro, Real **XpSample, int k, int jl, int ju) const
size_t samples() const
static Thermodynamics const * GetInstance()
Return a pointer to the one and only instance of Thermodynamics.
double min(double x1, double x2, double x3)
Definition: core.h:16
double max(double x1, double x2, double x3)
Definition: core.h:19
double gp_predict(KernelFunction_t kernel, double *arr2, double const *x2, double const *s2, int n2, double const *arr1, double const *x1, double const *s1, int n1, double len)
double SquaredExponential(double x1, double x2, double l, double s1s2=1.)
AirParcel gather_from_primitive(MeshBlock const *pmb, int k, int j, int i)
Definition: air_parcel.cpp:507
float temp
Definition: fit_ammonia.py:6
void FreeCArray(T **a)
Definition: ndarrays.hpp:13
void NewCArray(T **&a, int n1, int n2)
Definition: ndarrays.hpp:5
Real Rd
Definition: straka.cpp:66