Isis 3 Programmer Reference
Anisotropic2.cpp
1
6/* SPDX-License-Identifier: CC0-1.0 */
7
8#include <cmath>
9#include "Anisotropic2.h"
10#include "AtmosModel.h"
11#include "Constants.h"
12#include "Pvl.h"
13#include "PvlGroup.h"
14#include "IException.h"
15#include "IString.h"
16
17using std::min;
18using std::max;
19
20namespace Isis {
27 Anisotropic2::Anisotropic2(Pvl &pvl, PhotoModel &pmodel) : AtmosModel(pvl, pmodel) {
28 }
29
30
59 void Anisotropic2::AtmosModelAlgorithm(double phase, double incidence, double emission) {
60 double xx;
61 double munot, mu;
62 double emunot, emu;
63 double munotp, mup;
64 double gmunot, gmu;
65 double hpsq1;
66 double f1munot, f2munot, f3munot;
67 double f1mmunot, f2mmunot, f3mmunot;
68 double maxval;
69 double f1mu, f2mu, f3mu;
70 double f1mmu, f2mmu, f3mmu;
71 double sum;
72 double prod;
73 double cosazss;
74 double xystuff;
75 double xmunot_0, ymunot_0;
76 double xmu_0, ymu_0;
77 double cxx, cyy;
78 double xmunot_1, ymunot_1;
79 double xmu_1, ymu_1;
80
81 if(p_atmosBha == 0.0) {
82 p_atmosBha = 1.0e-6;
83 }
84
85 if(p_atmosTau == 0.0) {
86 p_pstd = 0.0;
87 p_trans = 1.0;
88 p_trans0 = 1.0;
89 p_sbar = 0.0;
90 p_transs = 1.0;
91 return;
92 }
93
94 if(p_atmosWha == 1.0) {
95 QString msg = "Anisotropic conservative case not implemented yet - WHA parameter cannot be set to 1.0.";
96 msg += "This will cause negative planetary curvature to occur.";
97 throw IException(IException::User, msg, _FILEINFO_);
98 }
99
100 if(TauOrWhaChanged()) {
101 // preparation includes exponential integrals e sub 2 through 5
102 p_wha2 = 0.5 * p_atmosWha;
103 p_wham = 1.0 - p_atmosWha;
104 p_e1 = AtmosModel::En(1, p_atmosTau);
105 p_e1_2 = AtmosModel::En(1, 2.0 * p_atmosTau);
106 p_e2 = AtmosModel::En(2, p_atmosTau);
107 p_e3 = AtmosModel::En(3, p_atmosTau);
108 p_e4 = AtmosModel::En(4, p_atmosTau);
109 p_e5 = AtmosModel::En(5, p_atmosTau);
110
111 // chandra's gmn functions require fm and fn at mu=-1
112 xx = -p_atmosTau;
113 if(xx < -69.0) {
114 p_em = 0.0;
115 }
116 else if(xx > 69.0) {
117 p_em = 1.0e30;
118 }
119 else {
120 p_em = exp(xx);
121 }
122
123 p_f1m = log(2.0) - p_em * p_e1 + p_e1_2;
124 p_f2m = -1.0 * (p_f1m + p_em * p_e2 - 1.0);
125 p_f3m = -1.0 * (p_f2m + p_em * p_e3 - 0.5);
126 p_f4m = -1.0 * (p_f3m + p_em * p_e4 - (1.0 / 3.0));
127 p_g12 = (p_atmosTau * p_e1 * p_e2 + p_f1m + p_f2m) * 0.5;
128 p_g13 = (p_atmosTau * p_e1 * p_e3 + p_f1m + p_f3m) * (1.0 / 3.0);
129 p_g14 = (p_atmosTau * p_e1 * p_e4 + p_f1m + p_f4m) * 0.25;
130 p_g32 = (p_atmosTau * p_e3 * p_e2 + p_f3m + p_f2m) * 0.25;
131 p_g33 = (p_atmosTau * p_e3 * p_e3 + p_f3m + p_f3m) * 0.2;
132 p_g34 = (p_atmosTau * p_e3 * p_e4 + p_f3m * p_f4m) * (1.0 / 6.0);
133
134 // chandra's g'mn functions require g'11 and f at mu=+1
135 xx = p_atmosTau;
136 if(xx < -69.0) {
137 p_e = 0.0;
138 }
139 else if(xx > 69.0) {
140 p_e = 1.0e30;
141 }
142 else {
143 p_e = exp(xx);
144 }
145
146 p_f1 = Eulgam() + log(p_atmosTau) + p_e * p_e1;
147 p_f2 = p_f1 + p_e * p_e2 - 1.0;
148 p_f3 = p_f2 + p_e * p_e3 - 0.5;
149 p_f4 = p_f3 + p_e * p_e4 - (1.0 / 3.0);
150 p_g11p = AtmosModel::G11Prime(p_atmosTau);
151 p_g12p = (p_atmosTau * (p_e1 - p_g11p) + p_em * (p_f1 + p_f2)) * 0.25;
152 p_g13p = (p_atmosTau * (0.5 * p_e1 - p_g12p) + p_em * (p_f1 + p_f3)) * 0.2;
153 p_g14p = (p_atmosTau * ((1.0 / 3.0) * p_e1 - p_g13p) + p_em * (p_f1 + p_f4)) * (1.0 / 6.0);
154 p_g32p = (p_atmosTau * (p_e1 - p_g13p) + p_em * (p_f3 + p_f2)) * (1.0 / 6.0);
155 p_g33p = (p_atmosTau * (0.5 * p_e1 - p_g32p) + p_em * (p_f3 + p_f3)) * 0.142857;
156 p_g34p = (p_atmosTau * ((1.0 / 3.0) * p_e1 - p_g33p) + p_em * (p_f3 + p_f4)) * 0.125;
157
158 // first, get the required quantities for the axisymmetric m=0 part
159 // zeroth moments of (uncorrected) x and y times characteristic fn
160 p_x0_0 = p_wha2 * (1.0 + (1.0 / 3.0) * p_atmosBha * p_wham + p_wha2 * (p_g12 + p_atmosBha *
161 p_wham * (p_g14 + p_g32) + pow(p_atmosBha, 2.0) * pow(p_wham, 2.0) * p_g34));
162 p_y0_0 = p_wha2 * (p_e2 + p_atmosBha * p_wham * p_e4 + p_wha2 * (p_g12p
163 + p_atmosBha * p_wham * (p_g14p + p_g32p) +
164 pow(p_atmosBha, 2.0) * pow(p_wham, 2.0) * p_g34p));
165
166 // higher-order correction term for x and y
167 p_delta_0 = (1.0 - (p_x0_0 + p_y0_0) - (1.0 - p_atmosWha * (1.0 + (1.0 / 3.0) * p_atmosBha *
168 p_wham)) / (1.0 - (p_x0_0 - p_y0_0))) / (p_atmosWha * (0.5 - p_e3 + p_atmosBha * p_wham * (0.25 - p_e5)));
169
170 // moments of (corrected) x and y
171 p_alpha0_0 = 1.0 + p_wha2 * (p_g12 + p_atmosBha * p_wham * p_g32) + p_delta_0 * (0.5 - p_e3);
172 p_alpha1_0 = 0.5 + p_wha2 * (p_g13 + p_atmosBha * p_wham * p_g33) + p_delta_0 * ((1.0 / 3.0) - p_e4);
173 p_beta0_0 = p_e2 + p_wha2 * (p_g12p + p_atmosBha * p_wham * p_g32p) + p_delta_0 * (0.5 - p_e3);
174 p_beta1_0 = p_e3 + p_wha2 * (p_g13p + p_atmosBha * p_wham * p_g33p) + p_delta_0 * ((1.0 / 3.0) - p_e4);
175
176 // gamma will be a weighted sum of m=0 x and y functions
177 // but unlike before, call the weights q1 and p1, and we also
178 // need additional weights q0 and p0
179 p_fac = 2.0 - p_atmosWha * p_alpha0_0;
180 p_den = pow(p_fac, 2.0) - pow((p_atmosWha * p_beta0_0), 2.0);
181 p_q0 = p_atmosBha * p_atmosWha * p_wham * (p_fac * p_alpha1_0 - p_atmosWha * p_beta0_0 * p_beta1_0) / p_den;
182 p_p0 = p_atmosBha * p_atmosWha * p_wham * (-p_fac * p_beta1_0 - p_atmosWha * p_beta0_0 * p_alpha1_0) / p_den;
183 p_q02p02 = p_q0 * p_q0 - p_p0 * p_p0;
184 p_q1 = (2.0 * p_wham * p_fac) / p_den;
185 p_p1 = (2.0 * p_wham * p_atmosWha * p_beta0_0) / p_den;
186 p_q12p12 = p_q1 * p_q1 - p_p1 * p_p1;
187
188 // sbar is total diffuse illumination and comes from moments
189 p_sbar = 1.0 - 2.0 * (p_q1 * p_alpha1_0 + p_p1 * p_beta1_0);
190
191 // we're not done yet! still have to calculate the m=1 portion
192 // zeroth moments of (uncorrected) x and y times characteristic fn
193 p_x0_1 = 0.5 * p_wha2 * p_atmosBha * (1.0 - (1.0 / 3.0) + 0.5 * p_wha2 * p_atmosBha *
194 (p_g12 - (p_g14 + p_g32) + p_g34));
195 p_y0_1 = 0.5 * p_wha2 * p_atmosBha * (p_e2 - p_e4 + 0.5 * p_wha2 * p_atmosBha *
196 (p_g12p - (p_g14p + p_g32p) + p_g34p));
197
198 // higher-order correction term for x and y
199 p_delta_1 = (1.0 - (p_x0_1 + p_y0_1) - (1.0 - (1.0 / 3.0) * p_atmosWha * p_atmosBha) /
200 (1.0 - (p_x0_1 - p_y0_1))) / (p_wha2 * p_atmosBha *
201 ((0.5 - 0.25) - (p_e3 - p_e5)));
202
203 // moments of (corrected) x and y are not needed for m=1, so we're done
204 SetOldTau(p_atmosTau);
205 SetOldWha(p_atmosWha);
206 }
207
208 // correct the path lengths for planetary curvature
209 hpsq1 = pow((1.0 + p_atmosHnorm), 2.0) - 1.0;
210
211 if(incidence == 90.0) {
212 munot = 0.0;
213 }
214 else {
215 munot = cos((PI / 180.0) * incidence);
216 }
217
218 maxval = max(1.0e-30, hpsq1 + munot * munot);
219 munotp = p_atmosHnorm / (sqrt(maxval) - munot);
220 munotp = max(munotp, p_atmosTau / 69.0);
221
222 if(emission == 90.0) {
223 mu = 0.0;
224 }
225 else {
226 mu = cos((PI / 180.0) * emission);
227 }
228
229 maxval = max(1.0e-30, hpsq1 + mu * mu);
230 mup = p_atmosHnorm / (sqrt(maxval) - mu);
231 mup = max(mup, p_atmosTau / 69.0);
232
233 // build the x and y functions of mu0 and mu
234 maxval = max(1.0e-30, munotp);
235 xx = -p_atmosTau / maxval;
236 if(xx < -69.0) {
237 emunot = 0.0;
238 }
239 else if(xx > 69.0) {
240 emunot = 1.0e30;
241 }
242 else {
243 emunot = exp(-p_atmosTau / munotp);
244 }
245
246 maxval = max(1.0e-30, mup);
247 xx = -p_atmosTau / maxval;
248 if(xx < -69.0) {
249 emu = 0.0;
250 }
251 else if(xx > 69.0) {
252 emu = 1.0e30;
253 }
254 else {
255 emu = exp(-p_atmosTau / mup);
256 }
257
258 // in the second approximation the x and y include the f1, f3 functions
259 xx = munotp;
260 if(fabs(xx - 1.0) < 1.0e-10) {
261 f1munot = p_f1;
262 f1mmunot = xx * (log(1.0 + 1.0 / xx) - p_e1 * emunot +
263 AtmosModel::En(1, p_atmosTau * (1.0 + 1.0 / xx)));
264 }
265 else if(xx > 0.0) {
266 f1munot = xx * (log(xx / (1.0 - xx)) + p_e1 / emunot +
267 AtmosModel::Ei(p_atmosTau * (1.0 / xx - 1.0)));
268 f1mmunot = xx * (log(1.0 + 1.0 / xx) - p_e1 * emunot +
269 AtmosModel::En(1, p_atmosTau * (1.0 + 1.0 / xx)));
270 }
271 else {
272 QString msg = "Negative length of planetary curvature encountered";
273 throw IException(IException::Unknown, msg, _FILEINFO_);
274 }
275
276 f2munot = munotp * (f1munot + p_e2 / emunot - 1);
277 f2mmunot = -munotp * (f1mmunot + p_e2 * emunot - 1);
278 f3munot = munotp * (f2munot + p_e3 / emunot - 0.5);
279 f3mmunot = -munotp * (f2mmunot + p_e3 * emunot - 0.5);
280 xx = mup;
281
282 if(fabs(xx - 1.0) < 1.0e-10) {
283 f1mu = p_f1;
284 f1mmu = xx * (log(1.0 + 1.0 / xx) - p_e1 * emu + AtmosModel::En(1, p_atmosTau * (1.0 + 1.0 / xx)));
285 }
286 else if(xx > 0.0) {
287 f1mu = xx * (log(xx / (1.0 - xx)) + p_e1 / emu + AtmosModel::Ei(p_atmosTau * (1.0 / xx - 1.0)));
288 f1mmu = xx * (log(1.0 + 1.0 / xx) - p_e1 * emu + AtmosModel::En(1, p_atmosTau * (1.0 + 1.0 / xx)));
289 }
290 else {
291 QString msg = "Negative length of planetary curvature encountered";
292 throw IException(IException::Unknown, msg, _FILEINFO_);
293 }
294
295 f2mu = mup * (f1mu + p_e2 / emu - 1);
296 f2mmu = -mup * (f1mmu + p_e2 * emu - 1);
297 f3mu = mup * (f2mu + p_e3 / emu - 0.5);
298 f3mmu = -mup * (f2mmu + p_e3 * emu - 0.5);
299
300 // first for m=0
301 xmunot_0 = 1.0 + p_wha2 * (f1mmunot + p_atmosBha * p_wham * f3mmunot) + p_delta_0 * munotp * (1.0 - emunot);
302 ymunot_0 = emunot * (1.0 + p_wha2 * (f1munot + p_atmosBha * p_wham * f3munot)) +
303 p_delta_0 * munotp * (1.0 - emunot);
304 xmu_0 = 1.0 + p_wha2 * (f1mmu + p_atmosBha * p_wham * f3mmu) + p_delta_0 * mup * (1.0 - emu);
305 ymu_0 = emu * (1.0 + p_wha2 * (f1mu + p_atmosBha * p_wham * f3mu)) + p_delta_0 * mup * (1.0 - emu);
306
307 // then for m=1
308 xmunot_1 = 1.0 + 0.5 * p_wha2 * p_atmosBha * (f1mmunot - f3mmunot) + p_delta_1 * munotp * (1.0 - emunot);
309 ymunot_1 = emunot * (1.0 + 0.5 * p_wha2 * p_atmosBha *
310 (f1munot - f3munot)) + p_delta_1 * munotp * (1.0 - emunot);
311 xmu_1 = 1.0 + 0.5 * p_wha2 * p_atmosBha * (f1mmu - f3mmu) + p_delta_1 * mup * (1.0 - emu);
312 ymu_1 = emu * (1.0 + 0.5 * p_wha2 * p_atmosBha * (f1mu - f3mu)) + p_delta_1 * mup * (1.0 - emu);
313
314 // gamma1 functions come from x and y with m=0
315 gmunot = p_p1 * xmunot_0 + p_q1 * ymunot_0;
316 gmu = p_p1 * xmu_0 + p_q1 * ymu_0;
317
318 // purely atmos term uses x and y of both orders and is complex
319 sum = munot + mu;
320 prod = munot * mu;
321 cxx = 1.0 - p_q0 * sum + (p_q02p02 - p_atmosBha * p_q12p12) * prod;
322 cyy = 1.0 + p_q0 * sum + (p_q02p02 - p_atmosBha * p_q12p12) * prod;
323
324 if(phase == 90.0) {
325 cosazss = 0.0 - munot * mu;
326 }
327 else {
328 cosazss = cos((PI / 180.0) * phase) - munot * mu;
329 }
330
331 xystuff = cxx * xmunot_0 * xmu_0 - cyy * ymunot_0 *
332 ymu_0 - p_p0 * sum * (xmu_0 * ymunot_0 + ymu_0 * xmunot_0) + cosazss * p_atmosBha * (xmu_1 *
333 xmunot_1 - ymu_1 * ymunot_1);
334 p_pstd = 0.25 * p_atmosWha * munotp / (munotp + mup) * xystuff;
335
336 // xmitted surface term uses gammas from m=0
337 p_trans = gmunot * gmu;
338
339 // finally, never-scattered term is given by pure attenuation
340 p_trans0 = emunot * emu;
341
342 // Calculate the transmission of light that must be subtracted from a
343 // shadow. This includes direct flux and the scattered flux in the
344 // upsun half of the sky downwelling onto the surface, and the usual
345 // transmission upward. NOTE: We need to derive the analytic expression
346 // for the light from half the sky in the Legendre scattering model. Until
347 // we do so, we are setting the shadow transmission to the purely
348 // unscattered part (same as trans0). This will give a result but is
349 // not fully consistent with how the other scattering models are
350 // implemented.
352 }
353}
354
355
356extern "C" Isis::AtmosModel *Anisotropic2Plugin(Isis::Pvl &pvl,
357 Isis::PhotoModel &pmodel) {
358
359 return new Isis::Anisotropic2(pvl, pmodel);
360}
virtual void AtmosModelAlgorithm(double phase, double incidence, double emission)
Anisotropic atmospheric scattering with P1 single-particle phase fn, in the second approximation.
Anisotropic2(Pvl &pvl, PhotoModel &pmodel)
Empty constructor.
Isotropic atmos scattering model.
Definition AtmosModel.h:60
double p_trans
Transmission of surface reflected light through the atmosphere overall.
Definition AtmosModel.h:259
static double En(unsigned int n, double x)
This routine evaluates the generalized exponential integral, En(x).
double p_atmosHnorm
Atmospheric shell thickness normalized to planet radius.
Definition AtmosModel.h:267
double p_sbar
Illumination of the ground by the sky.
Definition AtmosModel.h:262
static double G11Prime(double tau)
Perform Chandra and Van de Hulst's series approximation for the g'11 function needed in second order ...
double p_transs
Transmission of light that must be subtracted from the flat surface model to get the shadow model.
Definition AtmosModel.h:261
static double Ei(double x)
This routine computes the exponential integral, Ei(x).
double p_pstd
Pure atmospheric-scattering term.
Definition AtmosModel.h:258
double p_trans0
Transmission of surface reflected light through the atmosphere with no scatterings in the atmosphere.
Definition AtmosModel.h:260
bool TauOrWhaChanged() const
Checks whether tau or wha have changed.
Isis exception class.
Definition IException.h:91
@ Unknown
A type of error that cannot be classified as any of the other error types.
Definition IException.h:118
@ User
A type of error that could only have occurred due to a mistake on the user's part (e....
Definition IException.h:126
Container for cube-like labels.
Definition Pvl.h:119
This is free and unencumbered software released into the public domain.
Definition Apollo.h:16
const double PI
The mathematical constant PI.
Definition Constants.h:40