Skip to content

Commit

Permalink
Wind Jacobian calculations (#856)
Browse files Browse the repository at this point in the history
This adds tests and code to perform Wind-based Jacobian calculations in
ARTS3.

There is a design difference from ARTS2. You must pass [du_df, dv_df,
dw_df] to the propagation matrix agenda so that it deals with the output
of the agenda being in units of dK_d{u,v,w}.
  • Loading branch information
riclarsson authored Oct 30, 2024
2 parents 9402278 + 6917ce3 commit 5fbc477
Show file tree
Hide file tree
Showing 17 changed files with 600 additions and 166 deletions.
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ add_library(artsworkspace STATIC
m_covmat.cc
m_disort.cc
m_disort_settings.cc
m_frequency_grid.cc
m_fwd.cc
m_predefined_absorption_models.cc
m_xsec_fit.cc
Expand Down
14 changes: 5 additions & 9 deletions src/core/path/atm_path.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,7 @@ ArrayOfAscendingGrid &path_freq_resize(ArrayOfAscendingGrid &path_freq,
void forward_path_freq(AscendingGrid &path_freq,
const AscendingGrid &main_freq,
const PropagationPathPoint &rad_path,
const AtmPoint &atm_path,
const Numeric along_path_atm_speed) {
const AtmPoint &atm_path) {
auto dot_prod = [&]() {
const auto &[u, v, w] = atm_path.wind;
const auto &[za, aa] = rad_path.los;
Expand All @@ -64,7 +63,7 @@ void forward_path_freq(AscendingGrid &path_freq,
};

const Numeric fac =
1.0 - (along_path_atm_speed + dot_prod()) / Constant::speed_of_light;
1.0 - dot_prod() / Constant::speed_of_light;

ARTS_USER_ERROR_IF(
fac < 0 or nonstd::isnan(fac), "Bad frequency scaling factor: {}", fac)
Expand All @@ -78,15 +77,13 @@ void forward_path_freq(AscendingGrid &path_freq,
void forward_path_freq(ArrayOfAscendingGrid &path_freq,
const AscendingGrid &main_freq,
const ArrayOfPropagationPathPoint &rad_path,
const ArrayOfAtmPoint &atm_path,
const Numeric along_path_atm_speed) {
const ArrayOfAtmPoint &atm_path) {
if (arts_omp_in_parallel()) {
for (Size ip = 0; ip < atm_path.size(); ip++) {
forward_path_freq(path_freq[ip],
main_freq,
rad_path[ip],
atm_path[ip],
along_path_atm_speed);
atm_path[ip]);
}
} else {
String error{};
Expand All @@ -96,8 +93,7 @@ void forward_path_freq(ArrayOfAscendingGrid &path_freq,
forward_path_freq(path_freq[ip],
main_freq,
rad_path[ip],
atm_path[ip],
along_path_atm_speed);
atm_path[ip]);
} catch (const std::exception &e) {
#pragma omp critical
error = var_string(e.what(), "\n");
Expand Down
8 changes: 2 additions & 6 deletions src/core/path/atm_path.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,28 +48,24 @@ ArrayOfAscendingGrid &path_freq_resize(ArrayOfAscendingGrid &ppvar_f,
* @param main_freq Original frequency grid
* @param rad_path path point of the radiation
* @param atm_path path point of the atmosphere
* @param along_path_atm_speed Speed of the measurement along the path
*/
void forward_path_freq(AscendingGrid &path_freq,
const AscendingGrid &main_freq,
const PropagationPathPoint &rad_path,
const AtmPoint &atm_path,
const Numeric along_path_atm_speed);
const AtmPoint &atm_path);

/** Set frequency grid along the atmospheric path
*
* @param[out] ppvar_f As WSV
* @param[in] f_grid As WSV
* @param[in] rad_path As WSV
* @param[in] ppvar_atm As WSV
* @param[in] rte_alonglos_v As WSV
* @return ArrayOfVector& As ppvar_f WSV
*/
void forward_path_freq(ArrayOfAscendingGrid &ppvar_f,
const AscendingGrid &f_grid,
const ArrayOfPropagationPathPoint &rad_path,
const ArrayOfAtmPoint &ppvar_atm,
const Numeric rte_alonglos_v);
const ArrayOfAtmPoint &ppvar_atm);

/** Extracts a 1D atmospheric "path" from a 3D atmospheric field
*
Expand Down
44 changes: 23 additions & 21 deletions src/m_abs.cc
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "atm.h"
#include "check_input.h"
#include "debug.h"
#include "enumsAtmKey.h"
#include "file.h"
#include "hitran_species.h"
#include "jacobian.h"
Expand All @@ -33,6 +34,7 @@
#include "nlte.h"
#include "optproperties.h"
#include "path_point.h"
#include "sorted_grid.h"
#include "species.h"
#include "species_tags.h"

Expand All @@ -42,8 +44,7 @@
#include "nc_io.h"
#endif

void mirror_los(Vector& los_mirrored,
const ConstVectorView& los) {
void mirror_los(Vector& los_mirrored, const ConstVectorView& los) {
los_mirrored.resize(2);
los_mirrored[0] = 180 - los[0];
los_mirrored[1] = los[1] + 180;
Expand Down Expand Up @@ -72,10 +73,10 @@ Numeric dotprod_with_los(const ConstVectorView& los,
return f * (cos(za_f) * cos(za_p) + sin(za_f) * sin(za_p) * cos(aa_f - aa_p));
}

inline constexpr Numeric ELECTRON_CHARGE = -Constant::elementary_charge;
inline constexpr Numeric ELECTRON_MASS = Constant::electron_mass;
inline constexpr Numeric PI = Constant::pi;
inline constexpr Numeric SPEED_OF_LIGHT = Constant::speed_of_light;
inline constexpr Numeric ELECTRON_CHARGE = -Constant::elementary_charge;
inline constexpr Numeric ELECTRON_MASS = Constant::electron_mass;
inline constexpr Numeric PI = Constant::pi;
inline constexpr Numeric SPEED_OF_LIGHT = Constant::speed_of_light;
inline constexpr Numeric VACUUM_PERMITTIVITY = Constant::vacuum_permittivity;

/* Workspace method: Doxygen documentation will be auto-generated */
Expand All @@ -94,7 +95,8 @@ void absorption_speciesSet( // WS Output:
// Call this function.
absorption_species[i] = ArrayOfSpeciesTag(names[i]);
}
} ARTS_METHOD_ERROR_CATCH
}
ARTS_METHOD_ERROR_CATCH

/* Workspace method: Doxygen documentation will be auto-generated */
void absorption_speciesDefineAllInScenario( // WS Output:
Expand Down Expand Up @@ -155,11 +157,10 @@ void AbsInputFromAtmFields( // WS Output:
const Tensor3& t_field,
const Tensor4& vmr_field) {
// First, make sure that we really have a 1D atmosphere:
ARTS_USER_ERROR_IF(
1 != 3, "Atmospheric dimension must be 1D, but 3 is 3")
ARTS_USER_ERROR_IF(1 != 3, "Atmospheric dimension must be 1D, but 3 is 3")

abs_p = p_grid;
abs_t = t_field(joker, 0, 0);
abs_p = p_grid;
abs_t = t_field(joker, 0, 0);
abs_vmrs = vmr_field(joker, joker, 0, 0);
}

Expand Down Expand Up @@ -278,8 +279,8 @@ void propagation_matrixAddFaraday(
}

for (Index iv = 0; iv < frequency_grid.nelem(); iv++) {
const Numeric f2 = frequency_grid[iv] * frequency_grid[iv];
const Numeric r = ne * c1 / f2;
const Numeric f2 = frequency_grid[iv] * frequency_grid[iv];
const Numeric r = ne * c1 / f2;
propagation_matrix[iv].U() += r;

for (Size i = 0; i < 3; i++) {
Expand Down Expand Up @@ -334,7 +335,7 @@ void propagation_matrixAddParticles(
"passed a consistency check (scat_data_checked=1).")

const Index ns = TotalNumberOfElements(scat_data);
Index np = 0;
Index np = 0;
for (Size sp = 0; sp < absorption_species.size(); sp++) {
if (absorption_species[sp].Particles()) {
np++;
Expand Down Expand Up @@ -370,7 +371,7 @@ void propagation_matrixAddParticles(
Vector T_array;
if (jac_temperature.first) {
T_array.resize(2);
T_array = atm_point.temperature;
T_array = atm_point.temperature;
T_array[1] += dT;
} else {
T_array.resize(1);
Expand All @@ -397,7 +398,7 @@ void propagation_matrixAddParticles(

// loop over the scat_data and link them with correct vmr_field entry according
// to the position of the particle type entries in absorption_species.
Index sp = 0;
Index sp = 0;
Index i_se_flat = 0;
for (Size i_ss = 0; i_ss < scat_data.size(); i_ss++) {
for (Size i_se = 0; i_se < scat_data[i_ss].size(); i_se++) {
Expand Down Expand Up @@ -454,10 +455,10 @@ void propagation_matrixAddParticles(
const auto iq = jac_temperature.second->target_pos;

if (use_abs_as_ext) {
tmp(joker, joker, 0) = abs_vec_Nse[i_ss][i_se](joker, 1, 0, joker);
tmp(joker, joker, 0) = abs_vec_Nse[i_ss][i_se](joker, 1, 0, joker);
tmp(joker, joker, 0) -= abs_vec_Nse[i_ss][i_se](joker, 0, 0, joker);
} else {
tmp = ext_mat_Nse[i_ss][i_se](joker, 1, 0, joker, joker);
tmp = ext_mat_Nse[i_ss][i_se](joker, 1, 0, joker, joker);
tmp -= ext_mat_Nse[i_ss][i_se](joker, 0, 0, joker, joker);
}

Expand Down Expand Up @@ -559,12 +560,13 @@ void propagation_matrix_agendaAuto( // Workspace reference:
}

//propagation_matrixAddFaraday
if (std::ranges::any_of(absorption_species, [](auto& spec) {
return spec.FreeElectrons();
})) {
if (std::ranges::any_of(absorption_species,
[](auto& spec) { return spec.FreeElectrons(); })) {
agenda.add("propagation_matrixAddFaraday");
}

agenda.add("propagation_matrix_jacobianWindFix");

// Extra check (should really never ever fail when species exist)
propagation_matrix_agenda = std::move(agenda).finalize();
}
150 changes: 150 additions & 0 deletions src/m_frequency_grid.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
#include <atm.h>
#include <jacobian.h>
#include <path_point.h>
#include <rtepack.h>

#include "arts_constants.h"

void frequency_gridWindShift(AscendingGrid& frequency_grid,
Vector3& frequency_grid_wind_shift_jacobian,
const AtmPoint& atmospheric_point,
const PropagationPathPoint& ray_path_point) {
constexpr Numeric c = Constant::speed_of_light;

const auto& [u, v, w] = atmospheric_point.wind;
const auto [za, aa] = path::mirror(ray_path_point.los);

const Numeric u2v2 = u * u + v * v;
const Numeric w2 = w * w;
const Numeric f2 = u2v2 + w2;
const Numeric f = std::sqrt(f2);
const Numeric za_f = f == w ? 0.0 : std::acos(w / f);
const Numeric aa_f = std::atan2(u, v);
const Numeric za_p = Conversion::deg2rad(za);
const Numeric aa_p = Conversion::deg2rad(aa);
const Numeric scl = f2 * std::sqrt(f2 - w2);
const Numeric czaf = std::cos(za_f);
const Numeric szaf = std::sin(za_f);
const Numeric czap = std::cos(za_p);
const Numeric szap = std::sin(za_p);
const Numeric caa = std::cos(aa_f - aa_p);
const Numeric saa = std::sin(aa_p - aa_f);
const Numeric dp = czaf * czap + szaf * szap * caa;
const Numeric fac = 1.0 - (f * dp) / c;

ARTS_USER_ERROR_IF(fac <= 0,
R"(Negative frequency scaling factor: {}
atmospheric_point.wind: {:B,}
ray_path_point.los: {:B,}
)",
fac,
atmospheric_point.wind,
ray_path_point.los);

//! Zero shift if nan
if (std::isnan(fac)) {
frequency_grid_wind_shift_jacobian = {0, 0, 0};
return;
}

// shift the frequency grid
std::transform(frequency_grid.begin(),
frequency_grid.end(),
frequency_grid.unsafe_begin(),
[fac](const Numeric& f) { return fac * f; });

{
const Numeric df_du = (f == 0) ? 1.0 : u / f;
const Numeric dczaf_du = (f2 == 0) ? 0.0 : (-w * df_du / f2);
const Numeric dszaf_du = (f2 == w2) ? 0.0 : (w2 * df_du / scl);
const Numeric dcaa_du = (u2v2 == 0) ? 0.0 : (v * saa / u2v2);
const Numeric ddp_du =
czap * dczaf_du + szap * caa * dszaf_du + szap * szaf * dcaa_du;
frequency_grid_wind_shift_jacobian[0] = -(dp * df_du + f * ddp_du) / c;
}

{
const Numeric df_dv = (f == 0) ? 1.0 : v / f;
const Numeric dczaf_dv = (f2 == 0) ? 0.0 : (-w * df_dv / f2);
const Numeric dszaf_dv = (f2 == w2) ? 0.0 : (w2 * df_dv / scl);
const Numeric dcaa_dv = (u2v2 == 0) ? 0.0 : (-u * saa / u2v2);
const Numeric ddp_dv =
czap * dczaf_dv + szap * caa * dszaf_dv + szap * szaf * dcaa_dv;
frequency_grid_wind_shift_jacobian[1] = -(dp * df_dv + f * ddp_dv) / c;
}

{
const Numeric df_dw = (f == 0) ? 1.0 : w / f;
const Numeric dczaf_dw = (f2 == 0) ? 0.0 : (-w * df_dw / f2 + 1.0 / f);
const Numeric dszaf_dw = (scl == 0) ? 0.0 : ((w2 * df_dw - f * w) / scl);
const Numeric ddp_dw = czap * dczaf_dw + szap * caa * dszaf_dw;
frequency_grid_wind_shift_jacobian[2] = -(dp * df_dw + f * ddp_dw) / c;
}

frequency_grid_wind_shift_jacobian /= fac;
}

void propagation_matrix_jacobianWindFix(
PropmatMatrix& propagation_matrix_jacobian,
StokvecMatrix& source_vector_nonlte_jacobian,
const AscendingGrid& frequency_grid,
const JacobianTargets& jacobian_targets,
const Vector3& frequency_grid_wind_shift_jacobian) {
using enum AtmKey;

const auto& atm = jacobian_targets.atm();

const auto [df_du, df_dv, df_dw] = frequency_grid_wind_shift_jacobian;

if (auto ptr = std::ranges::find_if(
atm, Cmp::eq(wind_u), &Jacobian::AtmTarget::type);
ptr != atm.end()) {
const Index i = ptr->target_pos;

std::transform(frequency_grid.begin(),
frequency_grid.end(),
propagation_matrix_jacobian[i].begin(),
propagation_matrix_jacobian[i].begin(),
[df_du](const Numeric f, const Propmat& x) { return x * f * df_du; });
std::transform(frequency_grid.begin(),
frequency_grid.end(),
source_vector_nonlte_jacobian[i].begin(),
source_vector_nonlte_jacobian[i].begin(),
[df_du](const Numeric f, const Stokvec& x) { return x * f * df_du; });
}

if (auto ptr = std::ranges::find_if(
atm, Cmp::eq(wind_v), &Jacobian::AtmTarget::type);
ptr != atm.end()) {
const Index i = ptr->target_pos;

std::transform(frequency_grid.begin(),
frequency_grid.end(),
propagation_matrix_jacobian[i].begin(),
propagation_matrix_jacobian[i].begin(),
[df_dv](const Numeric f, const Propmat& x) { return x * f * df_dv; });
std::transform(frequency_grid.begin(),
frequency_grid.end(),
source_vector_nonlte_jacobian[i].begin(),
source_vector_nonlte_jacobian[i].begin(),
[df_dv](const Numeric f, const Stokvec& x) { return x * f * df_dv; });
}

if (auto ptr = std::ranges::find_if(
atm, Cmp::eq(wind_w), &Jacobian::AtmTarget::type);
ptr != atm.end()) {
const Index i = ptr->target_pos;

std::transform(frequency_grid.begin(),
frequency_grid.end(),
propagation_matrix_jacobian[i].begin(),
propagation_matrix_jacobian[i].begin(),
[df_dw](const Numeric f, const Propmat& x) { return x * f * df_dw; });
std::transform(frequency_grid.begin(),
frequency_grid.end(),
source_vector_nonlte_jacobian[i].begin(),
source_vector_nonlte_jacobian[i].begin(),
[df_dw](const Numeric f, const Stokvec& x) { return x * f * df_dw; });
}
}
Loading

0 comments on commit 5fbc477

Please sign in to comment.