1 /* simulate_radiances.cpp - provides a function taking inactive arguments that returns also Jacobian matrices
2 
3   Copyright (C) 2012-2014 The University of Reading
4 
5   Copying and distribution of this file, with or without modification,
6   are permitted in any medium without royalty provided the copyright
7   notice and this notice are preserved.  This file is offered as-is,
8   without any warranty.
9 */
10 
11 #include "adept.h"
12 #include "simulate_radiances.h"
13 
14 using adept::aReal;
15 using adept::Real;
16 
17 // Simulate a single radiance (W sr-1 m-3) given the wavelength (m),
18 // emissivity profile, surface temperature (K) and temperature profile
19 // (K), where the profile data are located at n points with spacing
20 // 1000 m. This function uses active arguments. It is accessible only
21 // from within this file; the public interface is the
22 // simulate_radiance function.
23 static
24 aReal
simulate_radiance_private(int n,Real wavelength,const Real * emissivity,const aReal & surface_temperature,const aReal * temperature)25 simulate_radiance_private(int n,
26 			  Real wavelength,
27 			  const Real* emissivity,
28 			  const aReal& surface_temperature,
29 			  const aReal* temperature)
30 {
31   static const Real BOLTZMANN_CONSTANT = 1.380648813e-23;
32   static const Real SPEED_OF_LIGHT = 299792458.0;
33 
34   int i;
35   aReal bt = surface_temperature; // Brightness temperature in K
36   // Loop up through the atmosphere working out the contribution from
37   // each layer
38   for (i = 0; i < n; i++) {
39     bt = bt*(1.0-emissivity[i]) + emissivity[i]*temperature[i];
40   }
41   // Convert from brightness temperature to radiance using
42   // Rayleigh-Jeans approximation
43   return 2.0*SPEED_OF_LIGHT*BOLTZMANN_CONSTANT*bt
44     /(wavelength*wavelength*wavelength*wavelength);
45 }
46 
47 // Simulate two radiances (W sr-1 m-3) given the surface temperature
48 // (K) and temperature profile (K), where the profile data are located
49 // at n points with spacing 1000 m. This function uses inactive
50 // arguments.
51 void
simulate_radiances(int n,Real surface_temperature,const Real * temperature,Real radiance[2],Real dradiance_dsurface_temperature[2],Real * dradiance_dtemperature)52 simulate_radiances(int n, // Size of temperature array
53 		   // Input variables:
54 		   Real surface_temperature,
55 		   const Real* temperature,
56 		   // Output variables:
57 		   Real radiance[2],
58 		   // Output Jacobians:
59 		   Real dradiance_dsurface_temperature[2],
60 		   Real* dradiance_dtemperature)
61 {
62   // First temporarily deactivate any existing Adept stack used by the
63   // calling function
64   adept::Stack* caller_stack = adept::active_stack();
65   if (caller_stack != 0) {
66     caller_stack->deactivate();
67   }
68 
69   // Within the scope of these curly brackets, another Adept stack
70   // will be used
71   {
72     // Ficticious oxygen channels around 60 GHz: wavelength in m
73     static const Real wavelength[2] = {0.006, 0.0061};
74     // Mass absorption coefficient of oxygen in m2 kg-1
75     static const Real mass_abs_coefft[2] = {3.0e-5, 3.0e-3};
76     // Layer thickness in m
77     static const Real dz = 1000.0;
78 
79     // Density of oxygen in kg m-3
80     std::vector<Real> density_oxygen(n);
81     // Emissivity at a particular microwave wavelength
82     std::vector<Real> emissivity(n);
83 
84     // Start a new stack
85     adept::Stack s;
86 
87     // Create local active variables: surface temperature, temperature
88     // and radiance
89     aReal st = surface_temperature;
90     std::vector<aReal> t(n);
91     aReal r[2];
92 
93     // Initialize the oxygen density and temperature
94     for (int i = 0; i < n; i++) {
95       Real altitude = i*dz;
96       // Oxygen density uses an assumed volume mixing ratio with air
97       // of 21%, molecular mass of 16 (compared to 29 for air), a
98       // surface air density of 1.2 kg m-3 and an atmospheric scale
99       // height of 8000 m
100       density_oxygen[i] = 1.2*0.21*(16.0/29.0)*exp(-altitude/8000.0);
101       t[i] = temperature[i];
102     }
103 
104     // Start recording derivative information
105     s.new_recording();
106 
107     // Loop through the two channels
108     for (int ichan = 0; ichan < 2; ichan++) {
109       // Compute the emissivity profile
110       for (int i = 0; i < n; i++) {
111 	emissivity[i] = 1.0-exp(-density_oxygen[i]*mass_abs_coefft[ichan]*dz);
112       }
113       // Simulate the radiance
114       r[ichan] = simulate_radiance_private(n, wavelength[ichan],
115 					   &emissivity[0], st, &t[0]);
116       // Copy the aReal variable to the Real variable
117       radiance[ichan] = r[ichan].value();
118     }
119 
120     // Declare independent (x) and dependent (y) variables for
121     // Jacobian matrix
122     s.independent(st);
123     s.independent(&t[0], n);
124     s.dependent(r, 2);
125 
126     // Compute Jacobian matrix
127     std::vector<Real> jacobian((n+1)*2);
128     s.jacobian(&jacobian[0]);
129 
130     // Copy elements of Jacobian matrix into the calling arrays
131     for (int ichan = 0; ichan < 2; ichan++) {
132       dradiance_dsurface_temperature[ichan] = jacobian[ichan];
133       for (int i = 0; i < n; i++) {
134 	dradiance_dtemperature[i*2+ichan] = jacobian[2+i*2+ichan];
135       }
136     }
137 
138     // At the following curly bracket, the local Adept stack will be
139     // destructed
140   }
141 
142   // Reactivate the Adept stack of the calling function
143   if (caller_stack != 0) {
144     caller_stack->activate();
145   }
146 }
147