/
IntegrateJ.m
219 lines (193 loc) · 7.18 KB
/
IntegrateJ.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
function [J,wl_out,QY_out,CS_out,LF_out] = IntegrateJ(CSin,QYin,LFin,T,P,wl_bounds,plotem)
% function J = IntegrateJ(Cross,QYield,LFlux,T,P,wl_bounds,plotem)
% Explicitly calculates a photolysis frequency by integrating the product of
% cross section, quantum yield and actinic flux over wavelength.
% Based on an ancient file from JD Crounse and F Paulot.
%
% INPUTS
% CSin: absorption cross-section (cm^2) and corresponding wavelength (nm).
% QYin: quantum yield (unitless) and corresponding wavelength (nm).
% LFin: light flux (quanta/cm^2/s/nm) and corresponding wavelength (nm).
% T: temperature (in K). Can be scalar or 1-D array.
% P: pressure (in mbar). Same size as T.
% wl_bounds: an optional 2-element vector of min and max cutoff wavelengths.
% plotem: optional flag to plot CS, QY, LF and their product. 0 or 1.
%
% For each of the first three inputs, there are several options for input format.
% (1) string: name of text file where 1st column is wavelength and 2nd column is CS/QY/PF.
% (2) array: 2-column numeric array of wavelength and CS/QY/PF.
% (3) function: a handle for a function with CS/QY/PF as 1st output and wavelength as 2nd.
% Function must have inputs of T and P (in that order) AND
% be vectorized such that it returns a matrix with one column for each T input.
% (4) scalar: assumed constant at all wavelengths (QUANTUM YIELD ONLY)
%
% OUTPUTS
% J: photolysis frequency, /s
% wl_out: wavelength grid for integration, nm
% QY_out: gridded quantum yield
% CS_out: gridded cross section, cm^2
% LF_out: gridded light flux, quanta/cm^2/s/nm
%
% 20120620 WGM Creation.
% 20120727 GMW Modified to work with T and P inputs as arrays.
% Also changed integration over wavelength to operate with
% coarsest wavelength resolution of given data.
% 20151024 GMW Changed name from "Photolysis" to "IntegrateJ."
% Also modified commenting and error messages to match UWCM style.
% Also identified a major error in final integration and fixed it.
% Also added better error handlers at the end.
% 20160215 GMW Added option for LFlux, QYield and Cross to be specified as matrices.
% Cleaned up comments, made some tweaks to conditional criteria.
% Fixed behavior of wl_bounds input (this should just be limits).
% Modified CS/QY convolution from linear interpolation (bad) to an
% integral-based convolution as done in TUV (better).
% 20160302 GMW Modified for vectorized CS/QY functions.
% 20160425 GMW Added plotting option.
% 20190222 GMW Added *_out outputs.
%% DEAL WITH INPUTS
nT = length(T);
if length(P)~=length(T)
error('IntegrateJ: P and T inputs must be same length.')
end
if nargin<6 || isempty(wl_bounds), wl_bounds = [0 100000]; end
if nargin<7, plotem=0; end
%% LIGHT FLUX
if ischar(LFin) %text file
lf = dlmread(LFin);
wl_lf = lf(:,1);
LF = lf(:,2);
elseif isnumeric(LFin) %2-column array
wl_lf = LFin(:,1);
LF = LFin(:,2);
else %function
[LF,wl_lf] = feval(LFin);
end
LF(LF<0) = 0;
%% CROSS SECTION
if ischar(CSin) %text file
cs = dlmread(CSin);
wl_cs = cs(:,1);
CS = cs(:,2);
elseif isnumeric(CSin) %2-column array
wl_cs = CSin(:,1);
CS = CSin(:,2);
else %function
[CS,wl_cs] = feval(CSin,T,P);
end
%% QUANTUM YIELD
if ischar(QYin) %text file
qy = dlmread(QYin);
wl_qy = qy(:,1);
QY = qy(:,2);
elseif isnumeric(QYin) && isscalar(QYin) %scalar
wl_qy = wl_lf;
QY = QYin*ones(size(wl_qy));
elseif isnumeric(QYin) %2-column array
wl_qy = QYin(:,1);
QY = QYin(:,2);
else %function
[QY,wl_qy] = feval(QYin,T,P);
end
%% TRUNCATE AND INTERPOLATE
% get common wavelenth grid
wl_out = wl_lf;
iwl = wl_out>=wl_bounds(1) & wl_out<=wl_bounds(2);
wl_out = wl_out(iwl);
LF_out = LF(iwl);
% define start and stop points for QY/CS smearing
% assumes LF wavelengths are mid-points
% also assumes equal spacing on ends
dwl = diff(wl_out);
dwl(end+1) = dwl(end);
dwl2 = dwl./2;
wllim = [wl_out(1) - dwl2(1); wl_out + dwl2];
dwl = diff(wllim);
% convolute Cross, QY to output grid
% integration/smearing mimics that done in TUV file "numer.f interp2"
N = length(wl_out);
CS_out=zeros(N,1);
QY_out=zeros(N,1);
for i=1:N
wll = wllim(i);
wlu = wllim(i+1);
if wlu<min(wl_cs) || wll>max(wl_cs) %skip if outside CS window
else, CS_out(i) = smear(wl_cs,CS,wll,wlu); %smear function defined below
end
if wlu<min(wl_qy) || wll>max(wl_qy) %skip if outside QY window
else, QY_out(i) = smear(wl_qy,QY,wll,wlu);
end
end
% OLD METHOD, REPLACED 20160219 GMW
% wlmin = max([min(wl_bounds),min(wl_qy),min(wl_lf),min(wl_cs)]);
% wlmax = min([max(wl_bounds),max(wl_qy),max(wl_lf),max(wl_cs)]);
% dwl = max([diff(wl_qy);diff(wl_lf);diff(wl_cs)]); %determine wavelength spacing by worst resolution
% wlavg = [wlmin:dwl:wlmax]';
% wlavg = wl_out;
% CS_out = interp1(wl_cs,CS,wlavg);
% QY_out = interp1(wl_qy,QY,wlavg);
% LF_out = interp1(wl_lf,LF,wlavg);
%Expand if running for multiple T/P inputs
if nT>1
if size(LF_out,2)==1
LF_out = repmat(LF_out,1,nT);
end
if size(CS_out,2)==1
CS_out = repmat(CS_out,1,nT);
end
if size(QY_out,2)==1
QY_out = repmat(QY_out,1,nT);
end
if size(dwl,2)==1
dwl = repmat(dwl,1,nT);
end
end
%% EVALUATE J
J = trapz(wl_out,QY_out.*CS_out.*LF_out)';
if any(J < 0)
error(['Negative J Value Calculated. '...
'Check photolysis files. QYield:' char(QYin) ' Cross: ' char(CSin)])
elseif any(isnan(J))
error(['NaN J Value Calculated. '...
'Check photolysis files. QYield:' char(QYin) ' Cross: ' char(CSin)])
end
%% OPTIONAL PLOTS
if plotem
figure
s2=subplot(221);
plot(wl_cs,CS,'b+-',wl_out,CS_out,'ro')
ylabel('Cross Section (cm^2)')
legend('Input','Convolved')
s3=subplot(222);
plot(wl_qy,QY,'b+-',wl_out,QY_out,'ro')
ylabel('Quantum Yield')
s1=subplot(223);
plot(wl_out,LF_out,'k*-')
ylabel('Light Flux (#/s/cm^2/nm)')
xlabel('wavelength (nm)')
s4=subplot(224);
plot(wl_out,QY_out.*CS_out.*LF_out.*dwl,'k*-')
ylabel('LF*QY*CS*dw (/s)')
xlabel('wavelength (nm)')
linkaxes([s1 s2 s3 s4],'x')
end
%% SUB-FUNCTION FOR INTEGRAL CONVOLUTION
% function yg = smear(x,y,xgl,xgu)
% Convolutes a cross section or quantum yield to another wavelength resolution
% by trapezoidal integration over wavelength and then re-destributing evenly over the output grid.
% stolen/borrowed directly from the TUV routine numer.f/interp2
function yg = smear(x,y,xgl,xgu)
area = 0;
for k=1:length(x)-1
if x(k+1)<xgl || x(k)>xgu %outside window
continue
end
a1 = max(x(k),xgl);
a2 = min(x(k+1),xgu);
slope = (y(k+1) - y(k))/(x(k+1) - x(k));
b1 = y(k) + slope*(a1 - x(k));
b2 = y(k) + slope*(a2 - x(k));
area = area + (a2 - a1)*(b2 + b1)/2;
end
yg = area./(xgu-xgl);
end
end