PageRenderTime 70ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/src/m_abs.cc

https://github.com/atmtools/arts
C++ | 1902 lines | 1354 code | 225 blank | 323 comment | 277 complexity | 6d599174d3ba8b60ac5ab14261adff6b MD5 | raw file
Possible License(s): GPL-2.0, GPL-3.0, LGPL-2.1

Large files files are truncated, but you can click here to view the full file

  1. /* Copyright (C) 2000-2012
  2. Stefan Buehler <sbuehler@ltu.se>
  3. Patrick Eriksson <patrick.eriksson@chalmers.se>
  4. Axel von Engeln <engeln@uni-bremen.de>
  5. Thomas Kuhn <tkuhn@uni-bremen.de>
  6. This program is free software; you can redistribute it and/or modify it
  7. under the terms of the GNU General Public License as published by the
  8. Free Software Foundation; either version 2, or (at your option) any
  9. later version.
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with this program; if not, write to the Free Software
  16. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
  17. USA. */
  18. //
  19. /**
  20. \file m_abs.cc
  21. Stuff related to the calculation of absorption coefficients.
  22. \author Stefan Buehler
  23. \date 2001-03-12
  24. */
  25. #include <algorithm>
  26. #include <cmath>
  27. #include "absorption.h"
  28. #include "array.h"
  29. #include "arts.h"
  30. #include "auto_md.h"
  31. #include "check_input.h"
  32. #include "legacy_continua.h"
  33. #include "file.h"
  34. #include "global_data.h"
  35. #include "jacobian.h"
  36. #include "m_xml.h"
  37. #include "math_funcs.h"
  38. #include "matpackI.h"
  39. #include "messages.h"
  40. #include "montecarlo.h"
  41. #include "optproperties.h"
  42. #include "parameters.h"
  43. #include "physics_funcs.h"
  44. #include "rte.h"
  45. #include "xml_io.h"
  46. #ifdef ENABLE_NETCDF
  47. #include <netcdf.h>
  48. #include "nc_io.h"
  49. #endif
  50. extern const Numeric ELECTRON_CHARGE;
  51. extern const Numeric ELECTRON_MASS;
  52. extern const Numeric PI;
  53. extern const Numeric SPEED_OF_LIGHT;
  54. extern const Numeric VACUUM_PERMITTIVITY;
  55. /* Workspace method: Doxygen documentation will be auto-generated */
  56. void AbsInputFromRteScalars( // WS Output:
  57. Vector& abs_p,
  58. Vector& abs_t,
  59. Matrix& abs_vmrs,
  60. // WS Input:
  61. const Numeric& rtp_pressure,
  62. const Numeric& rtp_temperature,
  63. const Vector& rtp_vmr,
  64. const Verbosity&) {
  65. // Prepare abs_p:
  66. abs_p.resize(1);
  67. abs_p = rtp_pressure;
  68. // Prepare abs_t:
  69. abs_t.resize(1);
  70. abs_t = rtp_temperature;
  71. // Prepare abs_vmrs:
  72. abs_vmrs.resize(rtp_vmr.nelem(), 1);
  73. abs_vmrs = rtp_vmr;
  74. }
  75. /* Workspace method: Doxygen documentation will be auto-generated */
  76. void abs_lines_per_speciesCreateFromLines( // WS Output:
  77. ArrayOfArrayOfAbsorptionLines& abs_lines_per_species,
  78. // WS Input:
  79. const ArrayOfAbsorptionLines& abs_lines,
  80. const ArrayOfArrayOfSpeciesTag& tgs,
  81. const Verbosity&) {
  82. // Size is set but inner size will now change from the original definition of species tags...
  83. abs_lines_per_species.resize(tgs.nelem());
  84. // The inner arrays need to be emptied, because they may contain lines
  85. // from a previous calculation
  86. for (auto &tg : abs_lines_per_species)
  87. tg.resize(0);
  88. // Take copies because we have to support frequency ranges, so might have to delete
  89. for (AbsorptionLines lines: abs_lines) {
  90. // Skip empty lines
  91. if (lines.NumLines() == 0) continue;
  92. // Loop all the tags
  93. for (Index i=0; i<tgs.nelem() and lines.NumLines(); i++) {
  94. for (auto& this_tag: tgs[i]) {
  95. // Test species first, we might leave as we leave
  96. if (this_tag.Species() not_eq lines.Species()) break;
  97. // Test isotopologue, we have to hit the end of the list for no isotopologue or the exact value
  98. if (this_tag.Isotopologue() not_eq SpeciesDataOfBand(lines).Isotopologue().nelem() and
  99. this_tag.Isotopologue() not_eq lines.Isotopologue())
  100. continue;
  101. // If there is a frequency range, we have to check so that only selected lines are included
  102. if (this_tag.Lf() >= 0 or this_tag.Uf() >= 0) {
  103. const Numeric low = (this_tag.Lf() >= 0) ? this_tag.Lf() : std::numeric_limits<Numeric>::lowest();
  104. const Numeric upp = (this_tag.Uf() >= 0) ? this_tag.Uf() : std::numeric_limits<Numeric>::max();
  105. // Fill up a copy of the line record to match with the wished frequency criteria
  106. AbsorptionLines these_lines = createEmptyCopy(lines);
  107. for (Index k=lines.NumLines()-1; k>=0; k--)
  108. if (low <= lines.F0(k) and upp >= lines.F0(k))
  109. these_lines.AppendSingleLine(lines.PopLine(k));
  110. // Append these lines after sorting them if there are any of them
  111. if (these_lines.NumLines()) {
  112. these_lines.ReverseLines();
  113. abs_lines_per_species[i].push_back(these_lines);
  114. }
  115. // If this means we have deleted all lines, then we leave
  116. if (lines.NumLines() == 0)
  117. goto leave_inner_loop;
  118. }
  119. else {
  120. abs_lines_per_species[i].push_back(lines);
  121. goto leave_inner_loop;
  122. }
  123. }
  124. }
  125. leave_inner_loop: {}
  126. }
  127. }
  128. /* Workspace method: Doxygen documentation will be auto-generated */
  129. void abs_speciesDefineAllInScenario( // WS Output:
  130. ArrayOfArrayOfSpeciesTag& tgs,
  131. Index& propmat_clearsky_agenda_checked,
  132. Index& abs_xsec_agenda_checked,
  133. // Control Parameters:
  134. const String& basename,
  135. const Verbosity& verbosity) {
  136. CREATE_OUT2;
  137. // Invalidate agenda check flags
  138. propmat_clearsky_agenda_checked = false;
  139. abs_xsec_agenda_checked = false;
  140. // Species lookup data:
  141. using global_data::species_data;
  142. // We want to make lists of included and excluded species:
  143. ArrayOfString included(0), excluded(0);
  144. tgs.resize(0);
  145. for (Index i = 0; i < species_data.nelem(); ++i) {
  146. const String specname = species_data[i].Name();
  147. String filename = basename;
  148. if (basename.length() && basename[basename.length() - 1] != '/')
  149. filename += ".";
  150. filename += specname;
  151. try {
  152. find_xml_file(filename, verbosity);
  153. // Add to included list:
  154. included.push_back(specname);
  155. // Convert name of species to a SpeciesTag object:
  156. SpeciesTag this_tag(specname);
  157. // Create Array of SpeciesTags with length 1
  158. // (our tag group has only one tag):
  159. ArrayOfSpeciesTag this_group(1);
  160. this_group[0] = this_tag;
  161. // Add this tag group to tgs:
  162. tgs.push_back(this_group);
  163. } catch (const std::runtime_error& e) {
  164. // The file for the species could not be found.
  165. excluded.push_back(specname);
  166. }
  167. }
  168. // Some nice output:
  169. out2 << " Included Species (" << included.nelem() << "):\n";
  170. for (Index i = 0; i < included.nelem(); ++i)
  171. out2 << " " << included[i] << "\n";
  172. out2 << " Excluded Species (" << excluded.nelem() << "):\n";
  173. for (Index i = 0; i < excluded.nelem(); ++i)
  174. out2 << " " << excluded[i] << "\n";
  175. }
  176. /* Workspace method: Doxygen documentation will be auto-generated */
  177. void abs_speciesDefineAll( // WS Output:
  178. ArrayOfArrayOfSpeciesTag& abs_species,
  179. Index& propmat_clearsky_agenda_checked,
  180. Index& abs_xsec_agenda_checked,
  181. // Control Parameters:
  182. const Verbosity& verbosity) {
  183. // Species lookup data:
  184. using global_data::species_data;
  185. // We want to make lists of all species
  186. ArrayOfString specs(0);
  187. for (auto& spec: species_data) {
  188. specs.push_back(spec.Name());
  189. }
  190. // Set the values
  191. abs_speciesSet(abs_species, abs_xsec_agenda_checked, propmat_clearsky_agenda_checked, specs, verbosity);
  192. }
  193. /* Workspace method: Doxygen documentation will be auto-generated */
  194. void AbsInputFromAtmFields( // WS Output:
  195. Vector& abs_p,
  196. Vector& abs_t,
  197. Matrix& abs_vmrs,
  198. // WS Input:
  199. const Index& atmosphere_dim,
  200. const Vector& p_grid,
  201. const Tensor3& t_field,
  202. const Tensor4& vmr_field,
  203. const Verbosity&) {
  204. // First, make sure that we really have a 1D atmosphere:
  205. if (1 != atmosphere_dim) {
  206. ostringstream os;
  207. os << "Atmospheric dimension must be 1D, but atmosphere_dim is "
  208. << atmosphere_dim << ".";
  209. throw runtime_error(os.str());
  210. }
  211. abs_p = p_grid;
  212. abs_t = t_field(joker, 0, 0);
  213. abs_vmrs = vmr_field(joker, joker, 0, 0);
  214. }
  215. /* Workspace method: Doxygen documentation will be auto-generated */
  216. void abs_coefCalcFromXsec( // WS Output:
  217. Matrix& abs_coef,
  218. Matrix& src_coef,
  219. ArrayOfMatrix& dabs_coef_dx,
  220. ArrayOfMatrix& dsrc_coef_dx,
  221. ArrayOfMatrix& abs_coef_per_species,
  222. ArrayOfMatrix& src_coef_per_species,
  223. // WS Input:
  224. const ArrayOfMatrix& abs_xsec_per_species,
  225. const ArrayOfMatrix& src_xsec_per_species,
  226. const ArrayOfArrayOfMatrix& dabs_xsec_per_species_dx,
  227. const ArrayOfArrayOfMatrix& dsrc_xsec_per_species_dx,
  228. const ArrayOfArrayOfSpeciesTag& abs_species,
  229. const ArrayOfRetrievalQuantity& jacobian_quantities,
  230. const Matrix& abs_vmrs,
  231. const Vector& abs_p,
  232. const Vector& abs_t,
  233. const Verbosity& verbosity) {
  234. CREATE_OUT3;
  235. // Check that abs_vmrs and abs_xsec_per_species really have compatible
  236. // dimensions. In abs_vmrs there should be one row for each tg:
  237. if (abs_vmrs.nrows() != abs_xsec_per_species.nelem()) {
  238. ostringstream os;
  239. os << "Variable abs_vmrs must have compatible dimension to abs_xsec_per_species.\n"
  240. << "abs_vmrs.nrows() = " << abs_vmrs.nrows() << "\n"
  241. << "abs_xsec_per_species.nelem() = " << abs_xsec_per_species.nelem();
  242. throw runtime_error(os.str());
  243. }
  244. // Check that number of altitudes are compatible. We only check the
  245. // first element, this is possilble because within arts all elements
  246. // are on the same altitude grid.
  247. if (abs_vmrs.ncols() != abs_xsec_per_species[0].ncols()) {
  248. ostringstream os;
  249. os << "Variable abs_vmrs must have same numbers of altitudes as abs_xsec_per_species.\n"
  250. << "abs_vmrs.ncols() = " << abs_vmrs.ncols() << "\n"
  251. << "abs_xsec_per_species[0].ncols() = "
  252. << abs_xsec_per_species[0].ncols();
  253. throw runtime_error(os.str());
  254. }
  255. // Check dimensions of abs_p and abs_t:
  256. chk_size("abs_p", abs_p, abs_vmrs.ncols());
  257. chk_size("abs_t", abs_t, abs_vmrs.ncols());
  258. // Will these calculations deal with nlte?
  259. const bool do_src =
  260. (src_xsec_per_species.nelem() == abs_xsec_per_species.nelem())
  261. ? not src_xsec_per_species[0].empty() ? true : false
  262. : false;
  263. const Index src_rows = do_src ? src_xsec_per_species[0].nrows() : 0;
  264. // Initialize abs_coef and abs_coef_per_species. The array dimension of abs_coef_per_species
  265. // is the same as that of abs_xsec_per_species. The dimension of abs_coef should
  266. // be equal to one of the abs_xsec_per_species enries.
  267. abs_coef.resize(abs_xsec_per_species[0].nrows(),
  268. abs_xsec_per_species[0].ncols());
  269. abs_coef = 0;
  270. src_coef.resize(src_rows, src_xsec_per_species[0].ncols());
  271. src_coef = 0;
  272. const ArrayOfIndex jacobian_quantities_position =
  273. equivalent_propmattype_indexes(jacobian_quantities);
  274. dabs_coef_dx.resize(jacobian_quantities_position.nelem());
  275. dsrc_coef_dx.resize(do_src ? jacobian_quantities_position.nelem() : 0);
  276. for (Index ii = 0; ii < jacobian_quantities_position.nelem(); ii++) {
  277. if (jacobian_quantities[jacobian_quantities_position[ii]] not_eq
  278. JacPropMatType::NotPropagationMatrixType) {
  279. dabs_coef_dx[ii].resize(abs_xsec_per_species[0].nrows(),
  280. abs_xsec_per_species[0].ncols());
  281. dabs_coef_dx[ii] = 0.0;
  282. if (do_src) {
  283. dsrc_coef_dx[ii].resize(src_rows, src_xsec_per_species[0].ncols());
  284. dsrc_coef_dx[ii] = 0.0;
  285. }
  286. }
  287. }
  288. abs_coef_per_species.resize(abs_xsec_per_species.nelem());
  289. src_coef_per_species.resize(src_xsec_per_species.nelem());
  290. out3
  291. << " Computing abs_coef and abs_coef_per_species from abs_xsec_per_species.\n";
  292. // Loop through all tag groups
  293. for (Index i = 0; i < abs_xsec_per_species.nelem(); ++i) {
  294. out3 << " Tag group " << i << "\n";
  295. // Make this element of abs_xsec_per_species the right size:
  296. abs_coef_per_species[i].resize(abs_xsec_per_species[i].nrows(),
  297. abs_xsec_per_species[i].ncols());
  298. abs_coef_per_species[i] = 0; // Initialize all elements to 0.
  299. if (do_src) {
  300. src_coef_per_species[i].resize(src_rows, src_xsec_per_species[i].ncols());
  301. src_coef_per_species[i] = 0; // Initialize all elements to 0.
  302. }
  303. // Loop through all altitudes
  304. for (Index j = 0; j < abs_xsec_per_species[i].ncols(); j++) {
  305. // Calculate total number density from pressure and temperature.
  306. const Numeric n = number_density(abs_p[j], abs_t[j]);
  307. const Numeric dn_dT = dnumber_density_dt(abs_p[j], abs_t[j]);
  308. // Wasted calculations when Jacobians are not calculated...
  309. // Though this is called seldom enough that it this fine? value is -1/t*n
  310. // Loop through all frequencies
  311. for (Index k = 0; k < abs_xsec_per_species[i].nrows(); k++) {
  312. abs_coef_per_species[i](k, j) =
  313. abs_xsec_per_species[i](k, j) * n * abs_vmrs(i, j);
  314. if (do_src)
  315. src_coef_per_species[i](k, j) =
  316. src_xsec_per_species[i](k, j) * n * abs_vmrs(i, j);
  317. for (Index iq = 0; iq < jacobian_quantities_position.nelem(); iq++) {
  318. if (jacobian_quantities[jacobian_quantities_position[iq]] ==
  319. JacPropMatType::Temperature) {
  320. dabs_coef_dx[iq](k, j) +=
  321. (dabs_xsec_per_species_dx[i][iq](k, j) * n +
  322. abs_xsec_per_species[i](k, j) * dn_dT) *
  323. abs_vmrs(i, j);
  324. if (do_src)
  325. dsrc_coef_dx[iq](k, j) +=
  326. (dsrc_xsec_per_species_dx[i][iq](k, j) * n +
  327. src_xsec_per_species[i](k, j) * dn_dT) *
  328. abs_vmrs(i, j);
  329. } else if (jacobian_quantities[jacobian_quantities_position[iq]] ==
  330. JacPropMatType::VMR) {
  331. bool seco = false, main = false;
  332. for (const auto& s : abs_species[i]) {
  333. if (species_match(
  334. jacobian_quantities[jacobian_quantities_position[iq]],
  335. s.BathSpecies()) or
  336. s.Type() not_eq SpeciesTag::TYPE_CIA)
  337. seco = true;
  338. if (species_iso_match(
  339. jacobian_quantities[jacobian_quantities_position[iq]],
  340. s.Species(),
  341. s.Isotopologue()))
  342. main = true;
  343. }
  344. if (main and seco) {
  345. dabs_coef_dx[iq](k, j) +=
  346. (dabs_xsec_per_species_dx[i][iq](k, j) * abs_vmrs(i, j) +
  347. abs_xsec_per_species[i](k, j)) *
  348. n;
  349. if (do_src)
  350. dsrc_coef_dx[iq](k, j) +=
  351. (dsrc_xsec_per_species_dx[i][iq](k, j) * abs_vmrs(i, j) +
  352. src_xsec_per_species[i](k, j)) *
  353. n;
  354. } else if (main) {
  355. dabs_coef_dx[iq](k, j) += abs_xsec_per_species[i](k, j) * n;
  356. if (do_src)
  357. dsrc_coef_dx[iq](k, j) += src_xsec_per_species[i](k, j) * n;
  358. } else if (seco) {
  359. dabs_coef_dx[iq](k, j) +=
  360. dabs_xsec_per_species_dx[i][iq](k, j) * abs_vmrs(i, j) * n;
  361. if (do_src)
  362. dsrc_coef_dx[iq](k, j) +=
  363. dsrc_xsec_per_species_dx[i][iq](k, j) * abs_vmrs(i, j) * n;
  364. }
  365. } else if (jacobian_quantities
  366. [jacobian_quantities_position[iq]] not_eq
  367. JacPropMatType::NotPropagationMatrixType) {
  368. dabs_coef_dx[iq](k, j) +=
  369. dabs_xsec_per_species_dx[i][iq](k, j) * n * abs_vmrs(i, j);
  370. if (do_src)
  371. dsrc_coef_dx[iq](k, j) +=
  372. dsrc_xsec_per_species_dx[i][iq](k, j) * n * abs_vmrs(i, j);
  373. }
  374. }
  375. }
  376. }
  377. // Add up to the total absorption:
  378. abs_coef += abs_coef_per_species[i]; // In Matpack you can use the +=
  379. // operator to do elementwise addition.
  380. if (do_src) src_coef += src_coef_per_species[i];
  381. }
  382. }
  383. /* Workspace method: Doxygen documentation will be auto-generated */
  384. void abs_xsec_per_speciesInit( // WS Output:
  385. ArrayOfMatrix& abs_xsec_per_species,
  386. ArrayOfMatrix& src_xsec_per_species,
  387. ArrayOfArrayOfMatrix& dabs_xsec_per_species_dx,
  388. ArrayOfArrayOfMatrix& dsrc_xsec_per_species_dx,
  389. // WS Input:
  390. const ArrayOfArrayOfSpeciesTag& tgs,
  391. const ArrayOfRetrievalQuantity& jacobian_quantities,
  392. const ArrayOfIndex& abs_species_active,
  393. const Vector& f_grid,
  394. const Vector& abs_p,
  395. const Index& abs_xsec_agenda_checked,
  396. const Index& nlte_do,
  397. const Verbosity& verbosity) {
  398. CREATE_OUT3;
  399. if (!abs_xsec_agenda_checked)
  400. throw runtime_error(
  401. "You must call *abs_xsec_agenda_checkedCalc* before calling this method.");
  402. // We need to check that abs_species_active doesn't have more elements than
  403. // abs_species (abs_xsec_agenda_checkedCalc doesn't know abs_species_active.
  404. // Usually we come here through an agenda call, where abs_species_active has
  405. // been properly created somewhere internally. But we might get here by
  406. // direct call, and then need to be safe!).
  407. if (tgs.nelem() < abs_species_active.nelem()) {
  408. ostringstream os;
  409. os << "abs_species_active (n=" << abs_species_active.nelem()
  410. << ") not allowed to have more elements than abs_species (n="
  411. << tgs.nelem() << ")!\n";
  412. throw runtime_error(os.str());
  413. }
  414. // Initialize abs_xsec_per_species. The array dimension of abs_xsec_per_species
  415. // is the same as that of abs_lines_per_species.
  416. abs_xsec_per_species.resize(tgs.nelem());
  417. src_xsec_per_species.resize(tgs.nelem());
  418. const bool do_jac = supports_propmat_clearsky(
  419. jacobian_quantities); //minus one is a flag for a non-species...
  420. const ArrayOfIndex jacobian_quantities_position =
  421. equivalent_propmattype_indexes(jacobian_quantities);
  422. dabs_xsec_per_species_dx.resize(do_jac ? tgs.nelem() : 0);
  423. dsrc_xsec_per_species_dx.resize(do_jac ? tgs.nelem() : 0);
  424. // Loop abs_xsec_per_species and make each matrix the right size,
  425. // initializing to zero.
  426. // But skip inactive species, loop only over the active ones.
  427. for (Index ii = 0; ii < abs_species_active.nelem(); ++ii) {
  428. const Index i = abs_species_active[ii];
  429. // Check that abs_species_active index is not higher than the number
  430. // of species
  431. if (i >= tgs.nelem()) {
  432. ostringstream os;
  433. os << "*abs_species_active* contains an invalid species index.\n"
  434. << "Species index must be between 0 and " << tgs.nelem() - 1;
  435. throw std::runtime_error(os.str());
  436. }
  437. // Make this element of abs_xsec_per_species the right size:
  438. abs_xsec_per_species[i].resize(f_grid.nelem(), abs_p.nelem());
  439. abs_xsec_per_species[i] = 0; // Matpack can set all elements like this.
  440. if (nlte_do) {
  441. src_xsec_per_species[i].resize(f_grid.nelem(), abs_p.nelem());
  442. src_xsec_per_species[i] = 0;
  443. } else {
  444. src_xsec_per_species[i].resize(0, 0);
  445. }
  446. if (do_jac) {
  447. dabs_xsec_per_species_dx[ii] =
  448. ArrayOfMatrix(jacobian_quantities_position.nelem(),
  449. Matrix(f_grid.nelem(), abs_p.nelem(), 0.0));
  450. if (nlte_do)
  451. dsrc_xsec_per_species_dx[ii] =
  452. ArrayOfMatrix(jacobian_quantities_position.nelem(),
  453. Matrix(f_grid.nelem(), abs_p.nelem(), 0.0));
  454. }
  455. }
  456. ostringstream os;
  457. os << " Initialized abs_xsec_per_species.\n"
  458. << " Number of frequencies : " << f_grid.nelem() << "\n"
  459. << " Number of pressure levels : " << abs_p.nelem() << "\n";
  460. out3 << os.str();
  461. }
  462. /* Workspace method: Doxygen documentation will be auto-generated */
  463. void abs_xsec_per_speciesAddConts( // WS Output:
  464. ArrayOfMatrix& abs_xsec_per_species,
  465. ArrayOfArrayOfMatrix& dabs_xsec_per_species_dx,
  466. // WS Input:
  467. const ArrayOfArrayOfSpeciesTag& tgs,
  468. const ArrayOfRetrievalQuantity& jacobian_quantities,
  469. const ArrayOfIndex& abs_species_active,
  470. const Vector& f_grid,
  471. const Vector& abs_p,
  472. const Vector& abs_t,
  473. const Matrix& abs_vmrs,
  474. const ArrayOfString& abs_cont_names,
  475. const ArrayOfVector& abs_cont_parameters,
  476. const ArrayOfString& abs_cont_models,
  477. const Verbosity& verbosity) {
  478. CREATE_OUT3;
  479. // Needed for some continua, and set here from abs_vmrs:
  480. Vector abs_h2o, abs_n2, abs_o2;
  481. // Check that all paramters that should have the number of tag
  482. // groups as a dimension are consistent.
  483. {
  484. const Index n_tgs = tgs.nelem();
  485. const Index n_xsec = abs_xsec_per_species.nelem();
  486. const Index n_vmrs = abs_vmrs.nrows();
  487. if (n_tgs != n_xsec || n_tgs != n_vmrs) {
  488. ostringstream os;
  489. os << "The following variables must all have the same dimension:\n"
  490. << "tgs: " << tgs.nelem() << "\n"
  491. << "abs_xsec_per_species: " << abs_xsec_per_species.nelem() << "\n"
  492. << "abs_vmrs.nrows(): " << abs_vmrs.nrows();
  493. throw runtime_error(os.str());
  494. }
  495. }
  496. // Jacobian overhead START
  497. /* NOTE: if any of the functions inside continuum tags could
  498. be made to give partial derivatives, then that would
  499. speed things up. Also be aware that line specific
  500. parameters cannot be retrieved while using these
  501. models. */
  502. const bool do_jac = supports_continuum(
  503. jacobian_quantities); // Throws runtime error if line parameters are wanted since we cannot know if the line is in the Continuum...
  504. const bool do_freq_jac = do_frequency_jacobian(jacobian_quantities);
  505. const bool do_temp_jac = do_temperature_jacobian(jacobian_quantities);
  506. Vector dfreq, dabs_t;
  507. const Numeric df = frequency_perturbation(jacobian_quantities);
  508. const Numeric dt = temperature_perturbation(jacobian_quantities);
  509. const ArrayOfIndex jacobian_quantities_position =
  510. equivalent_propmattype_indexes(jacobian_quantities);
  511. if (do_freq_jac) {
  512. dfreq.resize(f_grid.nelem());
  513. for (Index iv = 0; iv < f_grid.nelem(); iv++) dfreq[iv] = f_grid[iv] + df;
  514. }
  515. if (do_temp_jac) {
  516. dabs_t.resize(abs_t.nelem());
  517. for (Index it = 0; it < abs_t.nelem(); it++) dabs_t[it] = abs_t[it] + dt;
  518. }
  519. Matrix jacs_df, jacs_dt, normal;
  520. if (do_jac) {
  521. if (do_freq_jac) jacs_df.resize(f_grid.nelem(), abs_p.nelem());
  522. if (do_temp_jac) jacs_dt.resize(f_grid.nelem(), abs_p.nelem());
  523. normal.resize(f_grid.nelem(), abs_p.nelem());
  524. }
  525. // Jacobian overhead END
  526. // Check, that dimensions of abs_cont_names and
  527. // abs_cont_parameters are consistent...
  528. if (abs_cont_names.nelem() != abs_cont_parameters.nelem()) {
  529. ostringstream os;
  530. for (Index i = 0; i < abs_cont_names.nelem(); ++i)
  531. os << "abs_xsec_per_speciesAddConts: " << i
  532. << " name : " << abs_cont_names[i] << "\n";
  533. for (Index i = 0; i < abs_cont_parameters.nelem(); ++i)
  534. os << "abs_xsec_per_speciesAddConts: " << i
  535. << " param: " << abs_cont_parameters[i] << "\n";
  536. for (Index i = 0; i < abs_cont_models.nelem(); ++i)
  537. os << "abs_xsec_per_speciesAddConts: " << i
  538. << " option: " << abs_cont_models[i] << "\n";
  539. os << "The following variables must have the same dimension:\n"
  540. << "abs_cont_names: " << abs_cont_names.nelem() << "\n"
  541. << "abs_cont_parameters: " << abs_cont_parameters.nelem();
  542. throw runtime_error(os.str());
  543. }
  544. // Check that abs_p, abs_t, and abs_vmrs have the same
  545. // dimension. This could be a user error, so we throw a
  546. // runtime_error.
  547. if (abs_t.nelem() != abs_p.nelem()) {
  548. ostringstream os;
  549. os << "Variable abs_t must have the same dimension as abs_p.\n"
  550. << "abs_t.nelem() = " << abs_t.nelem() << '\n'
  551. << "abs_p.nelem() = " << abs_p.nelem();
  552. throw runtime_error(os.str());
  553. }
  554. if (abs_vmrs.ncols() != abs_p.nelem()) {
  555. ostringstream os;
  556. os << "Variable dimension abs_vmrs.ncols() must\n"
  557. << "be the same as abs_p.nelem().\n"
  558. << "abs_vmrs.ncols() = " << abs_vmrs.ncols() << '\n'
  559. << "abs_p.nelem() = " << abs_p.nelem();
  560. throw runtime_error(os.str());
  561. }
  562. // We set abs_h2o, abs_n2, and abs_o2 later, because we only want to
  563. // do it if the parameters are really needed.
  564. out3 << " Calculating continuum spectra.\n";
  565. // Loop tag groups:
  566. for (Index ii = 0; ii < abs_species_active.nelem(); ++ii) {
  567. const Index i = abs_species_active[ii];
  568. using global_data::species_data;
  569. // Go through the tags in the current tag group to see if they
  570. // are continuum tags:
  571. for (Index s = 0; s < tgs[i].nelem(); ++s) {
  572. // Continuum tags in the sense that we talk about here
  573. // (including complete absorption models) are marked by a special type.
  574. if (tgs[i][s].Type() == SpeciesTag::TYPE_PREDEF) {
  575. // We have identified a continuum tag!
  576. // Get only the continuum name. The full tag name is something like:
  577. // H2O-HITRAN96Self-*-*. We want only the `H2O-HITRAN96Self' part:
  578. const String name = species_data[tgs[i][s].Species()].Name() + "-" +
  579. species_data[tgs[i][s].Species()]
  580. .Isotopologue()[tgs[i][s].Isotopologue()]
  581. .Name();
  582. if (name == "O2-MPM2020") continue;
  583. // Check, if we have parameters for this model. For
  584. // this, the model name must be listed in
  585. // abs_cont_names.
  586. const Index n =
  587. find(abs_cont_names.begin(), abs_cont_names.end(), name) -
  588. abs_cont_names.begin();
  589. // n==abs_cont_names.nelem() indicates that
  590. // the name was not found.
  591. if (n == abs_cont_names.nelem()) {
  592. ostringstream os;
  593. os << "Cannot find model " << name << " in abs_cont_names.";
  594. throw runtime_error(os.str());
  595. }
  596. // Ok, the tag specifies a valid continuum model and
  597. // we have continuum parameters.
  598. if (out3.sufficient_priority()) {
  599. ostringstream os;
  600. os << " Adding " << name << " to tag group " << i << ".\n";
  601. out3 << os.str();
  602. }
  603. // find the options for this continuum tag from the input array
  604. // of options. The actual field of the array is n:
  605. const String ContOption = abs_cont_models[n];
  606. // Set abs_h2o, abs_n2, and abs_o2 from the first matching species.
  607. set_vmr_from_first_species(abs_h2o, "H2O", tgs, abs_vmrs);
  608. set_vmr_from_first_species(abs_n2, "N2", tgs, abs_vmrs);
  609. set_vmr_from_first_species(abs_o2, "O2", tgs, abs_vmrs);
  610. // Add the continuum for this tag. The parameters in
  611. // this call should be clear. The vmr is in
  612. // abs_vmrs(i,Range(joker)). The other vmr variables,
  613. // abs_h2o, abs_n2, and abs_o2 contains the real vmr of H2O,
  614. // N2, nad O2, which are needed as additional information for
  615. // certain continua:
  616. // abs_h2o for
  617. // O2-PWR88, O2-PWR93, O2-PWR98,
  618. // O2-MPM85, O2-MPM87, O2-MPM89, O2-MPM92, O2-MPM93,
  619. // O2-TRE05,
  620. // O2-SelfContStandardType, O2-SelfContMPM93, O2-SelfContPWR93,
  621. // N2-SelfContMPM93, N2-DryContATM01,
  622. // N2-CIArotCKDMT252, N2-CIAfunCKDMT252
  623. // abs_n2 for
  624. // H2O-SelfContCKD24, H2O-ForeignContCKD24,
  625. // O2-v0v0CKDMT100,
  626. // CO2-ForeignContPWR93, CO2-ForeignContHo66
  627. // abs_o2 for
  628. // N2-CIArotCKDMT252, N2-CIAfunCKDMT252
  629. if (!do_jac)
  630. xsec_continuum_tag(abs_xsec_per_species[i],
  631. name,
  632. abs_cont_parameters[n],
  633. abs_cont_models[n],
  634. f_grid,
  635. abs_p,
  636. abs_t,
  637. abs_n2,
  638. abs_h2o,
  639. abs_o2,
  640. abs_vmrs(i, Range(joker)),
  641. verbosity);
  642. else // The Jacobian block
  643. {
  644. // Needs a reseted block here...
  645. for (Index iv = 0; iv < f_grid.nelem(); iv++) {
  646. for (Index ip = 0; ip < abs_p.nelem(); ip++) {
  647. if (do_freq_jac) jacs_df(iv, ip) = 0.0;
  648. if (do_temp_jac) jacs_dt(iv, ip) = 0.0;
  649. normal(iv, ip) = 0.0;
  650. }
  651. }
  652. // Normal calculations
  653. xsec_continuum_tag(normal,
  654. name,
  655. abs_cont_parameters[n],
  656. abs_cont_models[n],
  657. f_grid,
  658. abs_p,
  659. abs_t,
  660. abs_n2,
  661. abs_h2o,
  662. abs_o2,
  663. abs_vmrs(i, Range(joker)),
  664. verbosity);
  665. // Frequency calculations
  666. if (do_freq_jac)
  667. xsec_continuum_tag(jacs_df,
  668. name,
  669. abs_cont_parameters[n],
  670. abs_cont_models[n],
  671. dfreq,
  672. abs_p,
  673. abs_t,
  674. abs_n2,
  675. abs_h2o,
  676. abs_o2,
  677. abs_vmrs(i, Range(joker)),
  678. verbosity);
  679. //Temperature calculations
  680. if (do_temp_jac)
  681. xsec_continuum_tag(jacs_dt,
  682. name,
  683. abs_cont_parameters[n],
  684. abs_cont_models[n],
  685. f_grid,
  686. abs_p,
  687. dabs_t,
  688. abs_n2,
  689. abs_h2o,
  690. abs_o2,
  691. abs_vmrs(i, Range(joker)),
  692. verbosity);
  693. for (Index iv = 0; iv < f_grid.nelem(); iv++) {
  694. for (Index ip = 0; ip < abs_p.nelem(); ip++) {
  695. abs_xsec_per_species[i](iv, ip) += normal(iv, ip);
  696. for (Index iq = 0; iq < jacobian_quantities_position.nelem();
  697. iq++) {
  698. if (is_frequency_parameter(
  699. jacobian_quantities[jacobian_quantities_position[iq]]))
  700. dabs_xsec_per_species_dx[i][iq](iv, ip) +=
  701. (jacs_df(iv, ip) - normal(iv, ip)) * (1. / df);
  702. else if (jacobian_quantities
  703. [jacobian_quantities_position[iq]] ==
  704. JacPropMatType::Temperature)
  705. dabs_xsec_per_species_dx[i][iq](iv, ip) +=
  706. (jacs_dt(iv, ip) - normal(iv, ip)) * (1. / dt);
  707. }
  708. }
  709. }
  710. }
  711. // Calling this function with a row of Matrix abs_vmrs
  712. // is possible because it uses Views.
  713. }
  714. }
  715. }
  716. }
  717. //======================================================================
  718. // Methods related to continua
  719. //======================================================================
  720. /* Workspace method: Doxygen documentation will be auto-generated */
  721. void abs_cont_descriptionInit( // WS Output:
  722. ArrayOfString& abs_cont_names,
  723. ArrayOfString& abs_cont_options,
  724. ArrayOfVector& abs_cont_parameters,
  725. const Verbosity& verbosity) {
  726. CREATE_OUT2;
  727. abs_cont_names.resize(0);
  728. abs_cont_options.resize(0);
  729. abs_cont_parameters.resize(0);
  730. out2 << " Initialized abs_cont_names \n"
  731. " abs_cont_models\n"
  732. " abs_cont_parameters.\n";
  733. }
  734. /* Workspace method: Doxygen documentation will be auto-generated */
  735. void abs_cont_descriptionAppend( // WS Output:
  736. ArrayOfString& abs_cont_names,
  737. ArrayOfString& abs_cont_models,
  738. ArrayOfVector& abs_cont_parameters,
  739. // Control Parameters:
  740. const String& tagname,
  741. const String& model,
  742. const Vector& userparameters,
  743. const Verbosity&) {
  744. // First we have to check that name matches a continuum species tag.
  745. check_continuum_model(tagname);
  746. //cout << " + tagname: " << tagname << "\n";
  747. //cout << " + model: " << model << "\n";
  748. //cout << " + parameters: " << userparameters << "\n";
  749. // Add name and parameters to the apropriate variables:
  750. abs_cont_names.push_back(tagname);
  751. abs_cont_models.push_back(model);
  752. abs_cont_parameters.push_back(userparameters);
  753. }
  754. /* Workspace method: Doxygen documentation will be auto-generated */
  755. void nlte_sourceFromTemperatureAndSrcCoefPerSpecies( // WS Output:
  756. ArrayOfStokesVector& nlte_source,
  757. ArrayOfStokesVector& dnlte_dx_source,
  758. ArrayOfStokesVector& nlte_dsource_dx,
  759. // WS Input:
  760. const ArrayOfMatrix& src_coef_per_species,
  761. const ArrayOfMatrix& dsrc_coef_dx,
  762. const ArrayOfRetrievalQuantity& jacobian_quantities,
  763. const Vector& f_grid,
  764. const Numeric& rtp_temperature,
  765. const Verbosity&) {
  766. // nlte_source has format
  767. // [ abs_species, f_grid, stokes_dim ].
  768. // src_coef_per_species has format ArrayOfMatrix (over species),
  769. // where for each species the matrix has format [f_grid, abs_p].
  770. Index n_species = src_coef_per_species.nelem(); // # species
  771. if (not n_species) {
  772. ostringstream os;
  773. os << "Must have at least one species.";
  774. throw runtime_error(os.str());
  775. }
  776. Index n_f = src_coef_per_species[0].nrows(); // # frequencies
  777. // # pressures must be 1:
  778. if (1 not_eq src_coef_per_species[0].ncols()) {
  779. ostringstream os;
  780. os << "Must have exactly one pressure.";
  781. throw runtime_error(os.str());
  782. }
  783. // Check species dimension of propmat_clearsky
  784. if (nlte_source.nelem() not_eq n_species) {
  785. ostringstream os;
  786. os << "Species dimension of propmat_clearsky does not\n"
  787. << "match src_coef_per_species.";
  788. throw std::runtime_error(os.str());
  789. }
  790. // Check frequency dimension of propmat_clearsky
  791. if (nlte_source[0].NumberOfFrequencies() not_eq n_f) {
  792. ostringstream os;
  793. os << "Frequency dimension of propmat_clearsky does not\n"
  794. << "match abs_coef_per_species.";
  795. throw runtime_error(os.str());
  796. }
  797. const ArrayOfIndex jacobian_quantities_position =
  798. equivalent_propmattype_indexes(jacobian_quantities);
  799. Vector B(n_f);
  800. for (Index iv = 0; iv < n_f; iv++)
  801. B[iv] = planck(f_grid[iv], rtp_temperature);
  802. StokesVector sv(n_f, nlte_source[0].StokesDimensions());
  803. for (Index si = 0; si < n_species; ++si) {
  804. sv.Kjj() = src_coef_per_species[si](joker, 0);
  805. sv *= B;
  806. nlte_source[si].Kjj() += sv.Kjj();
  807. }
  808. // Jacobian
  809. for (Index ii = 0; ii < jacobian_quantities_position.nelem(); ii++) {
  810. if (jacobian_quantities[jacobian_quantities_position[ii]] ==
  811. JacPropMatType::Temperature) {
  812. Vector dB(n_f);
  813. for (Index iv = 0; iv < n_f; iv++)
  814. dB[iv] = dplanck_dt(f_grid[iv], rtp_temperature);
  815. for (Index si = 0; si < n_species; ++si) {
  816. sv.Kjj() = src_coef_per_species[si](joker, 0);
  817. sv *= dB;
  818. nlte_dsource_dx[ii].Kjj() += sv.Kjj();
  819. }
  820. sv.Kjj() = dsrc_coef_dx[ii](joker, 0);
  821. sv *= B;
  822. dnlte_dx_source[ii].Kjj() += sv.Kjj();
  823. } else if (is_frequency_parameter(
  824. jacobian_quantities[jacobian_quantities_position[ii]])) {
  825. Vector dB(n_f);
  826. for (Index iv = 0; iv < n_f; iv++)
  827. dB[iv] = dplanck_df(f_grid[iv], rtp_temperature);
  828. for (Index si = 0; si < n_species; ++si) {
  829. sv.Kjj() = src_coef_per_species[si](joker, 0);
  830. sv *= dB;
  831. nlte_dsource_dx[ii].Kjj() += sv.Kjj();
  832. }
  833. sv.Kjj() = dsrc_coef_dx[ii](joker, 0);
  834. sv *= B;
  835. dnlte_dx_source[ii].Kjj() += sv.Kjj();
  836. } else if (jacobian_quantities[jacobian_quantities_position[ii]] not_eq
  837. JacPropMatType::NotPropagationMatrixType) {
  838. sv.Kjj() = dsrc_coef_dx[ii](joker, 0);
  839. sv *= B;
  840. dnlte_dx_source[ii].Kjj() += sv.Kjj();
  841. } else { /* All is fine! */
  842. }
  843. }
  844. }
  845. /* Workspace method: Doxygen documentation will be auto-generated */
  846. void propmat_clearskyAddFromAbsCoefPerSpecies( // WS Output:
  847. ArrayOfPropagationMatrix& propmat_clearsky,
  848. ArrayOfPropagationMatrix& dpropmat_clearsky_dx,
  849. // WS Input:
  850. const ArrayOfMatrix& abs_coef_per_species,
  851. const ArrayOfMatrix& dabs_coef_dx,
  852. const Verbosity&) {
  853. // propmat_clearsky has format
  854. // [ abs_species, f_grid, stokes_dim, stokes_dim ].
  855. // abs_coef_per_species has format ArrayOfMatrix (over species),
  856. // where for each species the matrix has format [f_grid, abs_p].
  857. Index n_species = abs_coef_per_species.nelem(); // # species
  858. if (0 == n_species) {
  859. ostringstream os;
  860. os << "Must have at least one species.";
  861. throw runtime_error(os.str());
  862. }
  863. Index n_f = abs_coef_per_species[0].nrows(); // # frequencies
  864. // # pressures must be 1:
  865. if (1 not_eq abs_coef_per_species[0].ncols()) {
  866. ostringstream os;
  867. os << "Must have exactly one pressure.";
  868. throw runtime_error(os.str());
  869. }
  870. // Check species dimension of propmat_clearsky
  871. if (propmat_clearsky.nelem() not_eq n_species) {
  872. ostringstream os;
  873. os << "Species dimension of propmat_clearsky does not\n"
  874. << "match abs_coef_per_species.";
  875. throw runtime_error(os.str());
  876. }
  877. // Check frequency dimension of propmat_clearsky
  878. if (propmat_clearsky[0].NumberOfFrequencies() not_eq n_f) {
  879. ostringstream os;
  880. os << "Frequency dimension of propmat_clearsky does not\n"
  881. << "match abs_coef_per_species.";
  882. throw runtime_error(os.str());
  883. }
  884. // Loop species and stokes dimensions, and add to propmat_clearsky:
  885. for (Index si = 0; si < n_species; ++si)
  886. propmat_clearsky[si].Kjj() += abs_coef_per_species[si](joker, 0);
  887. for (Index iqn = 0; iqn < dabs_coef_dx.nelem(); iqn++) {
  888. if (dabs_coef_dx[iqn].nrows() == n_f) {
  889. if (dabs_coef_dx[iqn].ncols() == 1) {
  890. dpropmat_clearsky_dx[iqn].Kjj() += dabs_coef_dx[iqn](joker, 0);
  891. } else
  892. throw std::runtime_error("Must have exactly one pressure.");
  893. }
  894. }
  895. }
  896. /* Workspace method: Doxygen documentation will be auto-generated */
  897. void propmat_clearskyInit( //WS Output
  898. ArrayOfPropagationMatrix& propmat_clearsky,
  899. ArrayOfStokesVector& nlte_source,
  900. ArrayOfPropagationMatrix& dpropmat_clearsky_dx,
  901. ArrayOfStokesVector& dnlte_dx_source,
  902. ArrayOfStokesVector& nlte_dsource_dx,
  903. //WS Input
  904. const ArrayOfArrayOfSpeciesTag& abs_species,
  905. const ArrayOfRetrievalQuantity& jacobian_quantities,
  906. const Vector& f_grid,
  907. const Index& stokes_dim,
  908. const Index& propmat_clearsky_agenda_checked,
  909. const Index& nlte_do,
  910. const Verbosity&) {
  911. if (!propmat_clearsky_agenda_checked)
  912. throw runtime_error(
  913. "You must call *propmat_clearsky_agenda_checkedCalc* before calling this method.");
  914. Index nf = f_grid.nelem();
  915. if (not abs_species.nelem())
  916. throw std::runtime_error("abs_species.nelem() = 0");
  917. if (not nf) throw runtime_error("nf = 0");
  918. if (not stokes_dim) throw runtime_error("stokes_dim = 0");
  919. const Index nq = equivalent_propmattype_indexes(jacobian_quantities).nelem();
  920. propmat_clearsky = ArrayOfPropagationMatrix(
  921. abs_species.nelem(), PropagationMatrix(nf, stokes_dim));
  922. dpropmat_clearsky_dx =
  923. ArrayOfPropagationMatrix(nq, PropagationMatrix(nf, stokes_dim));
  924. nlte_source = nlte_do ? ArrayOfStokesVector(abs_species.nelem(),
  925. StokesVector(nf, stokes_dim))
  926. : ArrayOfStokesVector(0);
  927. dnlte_dx_source = nlte_do
  928. ? ArrayOfStokesVector(nq, StokesVector(nf, stokes_dim))
  929. : ArrayOfStokesVector(0);
  930. nlte_dsource_dx = nlte_do
  931. ? ArrayOfStokesVector(nq, StokesVector(nf, stokes_dim))
  932. : ArrayOfStokesVector(0);
  933. }
  934. /* Workspace method: Doxygen documentation will be auto-generated */
  935. void propmat_clearskyAddFaraday(
  936. ArrayOfPropagationMatrix& propmat_clearsky,
  937. ArrayOfPropagationMatrix& dpropmat_clearsky_dx,
  938. const Index& stokes_dim,
  939. const Index& atmosphere_dim,
  940. const Vector& f_grid,
  941. const ArrayOfArrayOfSpeciesTag& abs_species,
  942. const ArrayOfRetrievalQuantity& jacobian_quantities,
  943. const Vector& rtp_vmr,
  944. const Vector& rtp_los,
  945. const Vector& rtp_mag,
  946. const Verbosity&) {
  947. // All the physical constants joined into one static constant:
  948. // (abs as e defined as negative)
  949. static const Numeric FRconst =
  950. abs(ELECTRON_CHARGE * ELECTRON_CHARGE * ELECTRON_CHARGE /
  951. (8 * PI * PI * SPEED_OF_LIGHT * VACUUM_PERMITTIVITY * ELECTRON_MASS *
  952. ELECTRON_MASS));
  953. if (stokes_dim < 3)
  954. throw runtime_error(
  955. "To include Faraday rotation, stokes_dim >= 3 is required.");
  956. if (atmosphere_dim == 1 && rtp_los.nelem() < 1) {
  957. ostringstream os;
  958. os << "For applying propmat_clearskyAddFaraday, los needs to be specified\n"
  959. << "(at least zenith angle component for atmosphere_dim==1),\n"
  960. << "but it is not.\n";
  961. throw runtime_error(os.str());
  962. } else if (atmosphere_dim > 1 && rtp_los.nelem() < 2) {
  963. ostringstream os;
  964. os << "For applying propmat_clearskyAddFaraday, los needs to be specified\n"
  965. << "(both zenith and azimuth angle components for atmosphere_dim>1),\n"
  966. << "but it is not.\n";
  967. throw runtime_error(os.str());
  968. }
  969. const bool do_jac = supports_faraday(jacobian_quantities);
  970. const bool do_magn_jac = do_magnetic_jacobian(jacobian_quantities);
  971. const Numeric dmag = magnetic_field_perturbation(jacobian_quantities);
  972. const ArrayOfIndex jacobian_quantities_position =
  973. equivalent_propmattype_indexes(jacobian_quantities);
  974. Index ife = -1;
  975. for (Index sp = 0; sp < abs_species.nelem() && ife < 0; sp++) {
  976. if (abs_species[sp][0].Type() == SpeciesTag::TYPE_FREE_ELECTRONS) {
  977. ife = sp;
  978. }
  979. }
  980. if (ife < 0) {
  981. throw runtime_error(
  982. "Free electrons not found in *abs_species* and "
  983. "Faraday rotation can not be calculated.");
  984. } else {
  985. const Numeric ne = rtp_vmr[ife];
  986. if (ne != 0 && (rtp_mag[0] != 0 || rtp_mag[1] != 0 || rtp_mag[2] != 0)) {
  987. // Include remaining terms, beside /f^2
  988. const Numeric c1 =
  989. 2 * FRconst * ne *
  990. dotprod_with_los(
  991. rtp_los, rtp_mag[0], rtp_mag[1], rtp_mag[2], atmosphere_dim);
  992. Numeric dc1_u = 0.0, dc1_v = 0.0, dc1_w = 0.0;
  993. if (do_magn_jac) {
  994. dc1_u = (2 * FRconst * ne *
  995. dotprod_with_los(rtp_los,
  996. rtp_mag[0] + dmag,
  997. rtp_mag[1],
  998. rtp_mag[2],
  999. atmosphere_dim) -
  1000. c1) /
  1001. dmag;
  1002. dc1_v = (2 * FRconst * ne *
  1003. dotprod_with_los(rtp_los,
  1004. rtp_mag[0],
  1005. rtp_mag[1] + dmag,
  1006. rtp_mag[2],
  1007. atmosphere_dim) -
  1008. c1) /
  1009. dmag;
  1010. dc1_w = (2 * FRconst * ne *
  1011. dotprod_with_los(rtp_los,
  1012. rtp_mag[0],
  1013. rtp_mag[1],
  1014. rtp_mag[2] + dmag,
  1015. atmosphere_dim) -
  1016. c1) /
  1017. dmag;
  1018. }
  1019. if (not do_jac) {
  1020. for (Index iv = 0; iv < f_grid.nelem(); iv++) {
  1021. const Numeric r = c1 / (f_grid[iv] * f_grid[iv]);
  1022. propmat_clearsky[ife].SetFaraday(r, iv);
  1023. }
  1024. } else {
  1025. for (Index iv = 0; iv < f_grid.nelem(); iv++) {
  1026. const Numeric f2 = f_grid[iv] * f_grid[iv];
  1027. const Numeric r = c1 / f2;
  1028. propmat_clearsky[ife].SetFaraday(r, iv);
  1029. // The Jacobian loop
  1030. for (Index iq = 0; iq < jacobian_quantities_position.nelem(); iq++) {
  1031. if (is_frequency_parameter(
  1032. jacobian_quantities[jacobian_quantities_position[iq]]))
  1033. dpropmat_clearsky_dx[iq].AddFaraday(-2.0 * r / f_grid[iv], iv);
  1034. else if (jacobian_quantities[jacobian_quantities_position[iq]] ==
  1035. JacPropMatType::MagneticU)
  1036. dpropmat_clearsky_dx[iq].AddFaraday(dc1_u / f2, iv);
  1037. else if (jacobian_quantities[jacobian_quantities_position[iq]] ==
  1038. JacPropMatType::MagneticV)
  1039. dpropmat_clearsky_dx[iq].AddFaraday(dc1_v / f2, iv);
  1040. else if (jacobian_quantities[jacobian_quantities_position[iq]] ==
  1041. JacPropMatType::MagneticW)
  1042. dpropmat_clearsky_dx[iq].AddFaraday(dc1_w / f2, iv);
  1043. else if (jacobian_quantities[jacobian_quantities_position[iq]] ==
  1044. JacPropMatType::Electrons)
  1045. dpropmat_clearsky_dx[iq].AddFaraday(r / ne, iv);
  1046. }
  1047. }
  1048. }
  1049. }
  1050. }
  1051. }
  1052. /* Workspace method: Doxygen documentation will be auto-generated */
  1053. void propmat_clearskyAddParticles(
  1054. // WS Output:
  1055. ArrayOfPropagationMatrix& propmat_clearsky,
  1056. ArrayOfPropagationMatrix& dpropmat_clearsky_dx,
  1057. // WS Input:
  1058. const Index& stokes_dim,
  1059. const Index& atmosphere_dim,
  1060. const Vector& f_grid,
  1061. const ArrayOfArrayOfSpeciesTag& abs_species,
  1062. const ArrayOfRetrievalQuantity& jacobian_quantities,
  1063. const Vector& rtp_vmr,
  1064. const Vector& rtp_los,
  1065. const Numeric& rtp_temperature,
  1066. const ArrayOfArrayOfSingleScatteringData& scat_data,
  1067. const Index& scat_data_checked,
  1068. const Index& use_abs_as_ext,
  1069. // Verbosity object:
  1070. const Verbosity& verbosity) {
  1071. CREATE_OUT1;
  1072. // (i)yCalc only checks scat_data_checked if cloudbox is on. It is off here,
  1073. // though, i.e. we need to check it here explicitly. (Also, cloudboxOff sets
  1074. // scat_data_checked=0 as it does not check it and as we ususally don't need
  1075. // scat_data for clearsky cases, hence don't want to check them by
  1076. // scat_data_checkedCalc in that case. This approach seems to be the more
  1077. // handy compared to cloudboxOff setting scat_data_checked=1 without checking
  1078. // it assuming we won't use it anyways.)
  1079. if (scat_data_checked != 1)
  1080. throw runtime_error(
  1081. "The scat_data must be flagged to have "
  1082. "passed a consistency check (scat_data_checked=1).");
  1083. const Index ns = TotalNumberOfElements(scat_data);
  1084. Index np = 0;
  1085. for (Index sp = 0; sp < abs_species.nelem(); sp++) {
  1086. if (abs_species[sp][0].Type() == SpeciesTag::TYPE_PARTICLES) {
  1087. np++;
  1088. }
  1089. }
  1090. if (np == 0) {
  1091. ostringstream os;
  1092. os << "For applying propmat_clearskyAddParticles, *abs_species* needs to"
  1093. << "contain species 'particles', but it does not.\n";
  1094. throw runtime_error(os.str());
  1095. }
  1096. if (ns != np) {
  1097. ostringstream os;
  1098. os << "Number of 'particles' entries in abs_species and of elements in\n"
  1099. << "*scat_data* needs to be identical. But you have " << np
  1100. << " 'particles' entries\n"
  1101. << "and " << ns << " *scat_data* elements.\n";
  1102. throw runtime_error(os.str());
  1103. }
  1104. if (atmosphere_dim == 1 && rtp_los.nelem() < 1) {
  1105. ostringstream os;
  1106. os << "For applying *propmat_clearskyAddParticles*, *rtp_los* needs to be specified\n"
  1107. << "(at least zenith angle component for atmosphere_dim==1),\n"
  1108. << "but it is not.\n";
  1109. throw runtime_error(os.str());
  1110. } else if (atmosphere_dim > 1 && rtp_los.nelem() < 2) {
  1111. ostringstream os;
  1112. os << "For applying *propmat_clearskyAddParticles*, *rtp_los* needs to be specified\n"
  1113. << "(both zenith and azimuth angle components for atmosphere_dim>1),\n"
  1114. << "but it is not.\n";
  1115. throw runtime_error(os.str());
  1116. }
  1117. // Use for rescaling vmr of particulates
  1118. Numeric rtp_vmr_sum = 0.0;
  1119. // Tests and setup partial derivatives
  1120. const bool do_jac = supports_particles(jacobian_quantities);
  1121. const bool do_jac_temperature = do_temperature_jacobian(jacobian_quantities);
  1122. const bool do_jac_frequencies = do_frequency_jacobian(jacobian_quantities);
  1123. const ArrayOfIndex jacobian_quantities_position =
  1124. equivalent_propmattype_indexes(jacobian_quantities);
  1125. const Numeric dT = temperature_perturbation(jacobian_quantities);
  1126. const Index na = abs_species.nelem();
  1127. Vector rtp_los_back;
  1128. mirror_los(rtp_los_back, rtp_los, atmosphere_dim);
  1129. // 170918 JM: along with transition to use of new-type (aka
  1130. // pre-f_grid-interpolated) scat_data, freq perturbation switched off. Typical
  1131. // clear-sky freq perturbations yield insignificant effects in particle
  1132. // properties. Hence, this feature is neglected here.
  1133. if (do_jac_frequencies) {
  1134. out1 << "WARNING:\n"
  1135. << "Frequency perturbation not available for absorbing particles.\n";
  1136. }
  1137. // creating temporary output containers
  1138. ArrayOfArrayOfTensor5 ext_mat_Nse;
  1139. ArrayOfArrayOfTensor4 abs_vec_Nse;
  1140. ArrayOfArrayOfIndex ptypes_Nse;
  1141. Matrix t_ok;
  1142. // preparing input in format needed
  1143. Vector T_array;
  1144. if (do_jac_temperature) {
  1145. T_array.resize(2);
  1146. T_array = rtp_temperature;
  1147. T_array[1] += dT;
  1148. } else {
  1149. T_array.resize(1);
  1150. T_array = rtp_temperature;
  1151. }
  1152. Matrix dir_array(1, 2);
  1153. dir_array(0, joker) = rtp_los_back;
  1154. // ext/abs per scat element for all freqs at once
  1155. opt_prop_NScatElems(ext_mat_Nse,
  1156. abs_vec_Nse,
  1157. ptypes_Nse,
  1158. t_ok,
  1159. scat_data,
  1160. stokes_dim,
  1161. T_array,
  1162. dir_array,
  1163. -1);
  1164. const Index nf = abs_vec_Nse[0][0].nbooks();
  1165. Tensor3 tmp(nf, stokes_dim, stokes_dim);
  1166. // loop over the scat_data and link them with correct vmr_field entry according
  1167. // to the position of the particle type entries in abs_species.
  1168. Index sp = 0;
  1169. Index i_se_flat = 0;
  1170. for (Inde

Large files files are truncated, but you can click here to view the full file