-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathigrf.m
370 lines (319 loc) · 9.99 KB
/
igrf.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
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
function [lmcosi,prepar]=igrf(vrs,yr,yir)
% [lmcosi,prepar]=IGRF(vrs,yr,yir)
%
% Interface to load the International Geomagnetic Reference Field
% and pass it on to other subroutines.
%
% INPUT:
%
% vrs The version number: 10, 11, 12, or 13 [default: 13]
% OR: a string with the demo number
% yr The year of interest (out of 1900:5:2020) [default: 2020]
% OR: the version number if the first argument is a demo string
% yir The year of interest if the first argument is a demo string
%
% OUTPUT:
%
% lmcosi The tradition matrix with the ordered real coefficients
% for the potential - however, in units of nT (nanoTesla)
% prepar The different ordering, for verification purposes only
%
% EXAMPLE:
%
% igrf('demo1') % The radial field, in nanoTesla
% igrf('demo2') % The radial non-dipolar field, in nanoTesla
% igrf('demo3') % The radial field, only contoured, in nanoTesla
% igrf('demo4') % The radial non-dipolar field, only contoured, in nanoTesla
% igrf('demo5') % The radial non-dipolar field, also contoured
% igrf('demo6') % The secular variation
% igrf('demo7') % Power-spectral density figure
% igrf('demo8') % Code and model testing which should produce no output
%
% Only very subtle changes when an IGRF becomes a DGRF
% igrf('demo3',13,2010)
% igrf('demo3',12,2010)
% igrf('demo3',11,2010)
% igrf('demo3',13,2015)
% igrf('demo3',12,2015)
%
% SEE ALSO:
%
% PLM2MAG, IGRF10
%
% Tested on 8.3.0.532 (R2014a)
%
% Last modified by fjsimons-at-alum.mit.edu, 03/17/2020
% Note that the inputs to PLM2XYZ are GEOCENTRIC coordinates...
% Normalize the coefficients such that they can be expanded in the 4pi basis
% lola= guyotphysics(0);
% h=igrf(13,2020);
% Don't be missing the normalization
% h(:,3:4)=h(:,3:4)./repmat(sqrt(2*h(:,1)+1),1,2);
% Don't be missing the radial derivative
% h(:,3:4)=h(:,3:4).*repmat(h(:,1)+1,1,2);
% [r,lon,lat]=plm2xyz(h,lola(2),lola(1))
% So the results match igrf12.f and igrf13.f
% But this calculator is in geodetic coordinates... See the switch inside.
% http://www.geomag.bgs.ac.uk/data_service/models_compass/igrf_calc.html
defval('vrs',13)
if ~isstr(vrs)
defval('yr',2005)
% Make it a single year - perhaps fix later
if prod(size(yr))~=1
error('Only a single year at the time can be requested for now')
end
if vrs==10
% Separate code since it handled a bit differently in the past
% Calling the old routine is awkward but saves special cases in the
% body of the current routine.
lmcosi=igrf10(yr);
else
% Open file
fname=fullfile(getenv('IFILES'),'EARTHMODELS',...
sprintf('IGRF-%2.2i',vrs),sprintf('igrf%2.2icoeffs.txt',vrs));
fid=fopen(fname);
% Read the first three header lines
hdr{1}=fgetl(fid);
hdr{2}=fgetl(fid);
hdr{3}=fgetl(fid);
% Define formats, with reference to IGRF-10
fmt1=['%s %s %s' repmat('%n',1,22+vrs-10) '%s'];
fmt2=['%s' repmat('%n',1,25+vrs-10)];
% The maximum expansion
lmax=[repmat(10,1,20) repmat(13,1,2+vrs-10)];
% Read the third line
d=textscan(fid,fmt1,1);
% Read the rest - supply zeroes where unavailable
e=textscan(fid,fmt2,'emptyvalue',0);
% Close file
fclose(fid);
% Available years
years=[d{4:25+vrs-10}];
% Spherical harmonic degrees
EL=e{2};
% Spherical harmonic orders
EM=e{3};
% Secular variation
SV=e{26+vrs-10};
%%% This applied when the unkowns are ZEROES in the file %%%%%%%%%%
% Now extract the data
[C,iy]=intersect(years,yr);
if isempty(iy)
error(sprintf('\n%s: Musty specify valid model year',upper(mfilename)));
end
% Stick in the non-existing zeros for degree and order zero
prepar=[zeros(1,size(e{iy+3},2)) ; e{iy+3}];
%%% This applied when the unkowns are ZEROES in the file %%%%%%%%%%
% Reordering sequence
[dems,dels,mz,lmcosi,mzi,mzo,bigm,bigl,rinm,ronm,demin]=...
addmon(max(lmax));
% What we have from the file is, effectively
% [bigl(2:end) bigm(2:end)]
% and what we want is lmcosi with the coefficients in the right position
% This is the output
lmcosi(mzo+2*size(lmcosi,1))=prepar;
end
elseif strcmp(vrs,'demo1')
% Now the version is defaulted... everything skips
defval('yr',13)
defval('yir',2020);
h=igrf(yr,yir);
% Plot and print
plotandprint(h,vrs,yr,yir,0,0)
elseif strcmp(vrs,'demo2')
defval('yr',13)
defval('yir',2020);
h=igrf(yr,yir);
% Plot and print
plotandprint(h,vrs,yr,yir,1,0,[-20000:1000:-1000],[1000:1000:20000])
elseif strcmp(vrs,'demo3')
defval('yr',13)
defval('yir',2020);
h=igrf(yr,yir);
% Plot and print
plotandprint(h,vrs,yr,yir,0,1,[-65000:5000:-5000],[5000:5000:65000])
elseif strcmp(vrs,'demo4')
defval('yr',13)
defval('yir',2020);
h=igrf(yr,yir);
% Plot and print
plotandprint(h,vrs,yr,yir,1,1)
elseif strcmp(vrs,'demo5')
defval('yr',13)
defval('yir',2020);
h=igrf(yr,yir);
% Plot and print
plotandprint(h,vrs,yr,yir,1,2,[-20000:2000:-2000],[2000:2000:20000])
elseif strcmp(vrs,'demo6')
defval('yr',13)
for yir=1900:5:2020
h=igrf(yr,yir);
% Plot and print
plotandprint(h,vrs,yr,yir,1,2,[-20000:2000:-2000],[2000:2000:20000])
end
elseif strcmp(vrs,'demo7')
clf
ah=gca;
defval('yr',13)
defval('yir',1920);
defval('mods',sprintf('IGRF-%2.2i (%i)',yr,yir));
defval('units','nT');
% Load the model
h=igrf(yr,yir);
% The degree range of interest
EL=minmax(h(abs(h(:,3))>0,1));
% Plot and print
norma=1;
% Spectral calculation of signal or noise - watch the normalization
[sdl,l,bta,lfit,logy,logpm]=plm2spec(h,norma,2,max(EL)+1);
% The following borrowed from WATTSANDMOORE
fig2print(gcf,'portrait')
% The power spectral density
a=loglog(l,sdl,'o');
hold on
% The loglinear fit
b=loglog(lfit,logy,'k-');
hold off
% Take that fit off here
% delete(b)
% Create labels for future use
xlabs='spherical harmonic degree';
xxlabs='equivalent wavelength (km)';
ylabs=sprintf('%s power spectral density [%s**2]',mods,units);
% Cosmetics
set(a,'MarkerFaceColor','k','MarkerSize',3,'MarkerEdgeColor','k')
xlim(EL+[-0.5 27]);
longticks(gca)
shrink(gca,1.333,1.075)
% This needs to be data-dependent
ylim=[1e1 1e11];
ah.YLim=ylim;
% The reference degrees you want plotted also
nn=[1 13];
% The reference degrees you wanted as well
hold on
for index=1:length(nn)
pn(index)=plot([nn(index) nn(index)],ylim,'k--');
end
hold off
% Labels
ylabel(ylabs)
xlabel(xlabs)
% Extra axis in equivalent wavelengths, needed to know 6371.2 from IGRF
% model specification, but fix label precision
nlt=[2*pi*6371.2/2 10000 3000 1000];
% With or without round, it's always going to be approximate...
[ax,xl,yl]=xtraxis(ah,round(jeans(nlt,0,1)),1000*round(nlt/1000),xxlabs);
longticks(ax)
% Final cosmetics
delete(pn)
ax.XMinorTick='off';
ah.XTick=[1 2 3 4 6 9 13 40];
ah.XGrid='on';
ah.YGrid='on';
ah.XMinorTick='off';
ah.MinorGridLineStyle='none';
% Output to PDF
figdisp('igrf',sprintf('%s-%i-%i',vrs,yr,yir),[],2)
elseif strcmp(vrs,'demo8')
for yr=1900:5:2005
diferm(igrf(10,yr),igrf10(yr))
end
for yr=1900:5:2000
diferm(igrf(11,yr),igrf10(yr))
diferm(igrf(12,yr),igrf10(yr))
diferm(igrf(13,yr),igrf10(yr))
end
for yr=1900:5:2005
diferm(igrf(12,yr),igrf(11,yr))
diferm(igrf(13,yr),igrf(11,yr))
diferm(igrf(13,yr),igrf(12,yr))
end
for yr=1900:5:2010
diferm(igrf(13,yr),igrf(12,yr))
end
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Subfunction to plot and print
function plotandprint(h,vrs,yr,yir,zro,cnt,negcont,poscont)
% Zero out the degree-1 component
defval('zro',0)
% Whether contours are plotted
defval('cnt',0)
% Change Schmidt to full normalization for use in PLOTPLM
% This converts the COEFFICIENTS to be multiplied with Schmidt to the
% COEFFICIENTS to be multiplied with 4pi-normalized harmonics, which
% are Schmidt*sqrt(2l+1), see PLM2XYZ and note the TYPO in Blakely.
h(:,3:4)=h(:,3:4)./repmat(sqrt(2*h(:,1)+1),1,2);
if zro==1
% The nondipole field, as Blakely p170 and eq. (8.20)
h(1:3,3:4)=0;
xlab='non-dipolar radial component (nT)';
else
xlab='radial component (nT)';
end
% Make sure it is the RADIAL component of this at the surface
h(:,3:4)=repmat(h(:,1)+1,1,2).*h(:,3:4);
clf
% This resolution parameter will change the quoted maxima and minima
degres=1;
d=plotplm(h,[],[],4,degres);
kelicol
% The title string
ztit=sprintf('IGRF-%2.2i magnetic field, year %i, degrees %i-%i',yr,yir,...
h(min(find(h(:,3))),1),max(h(~~sum(h(:,3:4),2),1)));
switch cnt
case 0
% Just a color plot
axis image
longticks(gca,2)
t(1)=title(ztit);
movev(t,5)
cb=colorbar('hor');
shrink(cb,2,2)
axes(cb)
longticks(cb,2)
xlabel(xlab)
movev(cb,-.1)
case {1,2}
% A judicious contour plot
if cnt==1
% No overlay
clf
end
% Negative and positive contour intervals
defval('negcont',[-20000:1000:-1000])
defval('poscont',[ 1000:1000:20000])
% Geographic grid
lons=linspace(0,360,size(d,2));
lats=linspace(-90,90,size(d,1));
% Don't forget to flip up down for contouring!
[c,hh]=contour(lons,lats,flipud(d),negcont);
set(hh,'EdgeC','r')
hold on
[c,hh]=contour(lons,lats,flipud(d),poscont);
set(hh,'EdgeC','b')
[c,hh]=contour(lons,lats,flipud(d),[0 0]);
set(hh,'EdgeC','k','LineW',2)
% Finalize
plotcont; axis image; ylim([-90 90])
defval('dlat',45)
set(gca,'ytick',[-90:dlat:90])
set(gca,'xtick',[0:90:360])
if cnt==1
% Otherwise you already had them
deggies(gca)
end
longticks(gca,2)
t(1)=title(ztit);
movev(t,5)
xl=xlabel(sprintf('minimum %i nT ; maximum %i nT ; contour interval %i nT',...
round(min(d(:))),round(max(d(:))),...
unique([diff(negcont) diff(poscont)])));
movev(xl,-10)
end
% Actual printing
fig2print(gcf,'portrait')
figna=figdisp('igrf',sprintf('%s-%i-%i',vrs,yr,yir),[],2);
% Maybe this...
% figna=figdisp([],sprintf('%s-%i-%i',vrs,yr,yir),'-r300',1,'jpeg');