[go: up one dir, main page]

File: spline.f

package info (click to toggle)
ifeffit 2%3A1.2.11d-9.1
  • links: PTS
  • area: contrib
  • in suites: jessie, jessie-kfreebsd
  • size: 12,444 kB
  • ctags: 6,492
  • sloc: fortran: 35,441; ansic: 8,454; makefile: 4,815; python: 3,274; perl: 3,146; sh: 2,721; ada: 1,003; tcl: 95
file content (369 lines) | stat: -rw-r--r-- 14,353 bytes parent folder | download | duplicates (7)
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
       subroutine spline(energy, xmu, mxmu, xkstd, chistd, mstd,
     $      e0_in, rbkgin, r1stin, toler, nknots, 
     $      qmin, qmax, xkw, dk1, dk2, winnam, 
     $      stfind, fnorm, enor1, enor2, pre1, pre2, estep,
     $      lclmp1, xclmp1, lclmp2, xclmp2, nclmp,
     $      cnorm, fixstd, usestd, varye0, spstep,
     $      dofit, bkg, mxk, xk, chi)
c
c//////////////////////////////////////////////////////////////////////
c Copyright (c) 1997--2000 Matthew Newville, The University of Chicago
c Copyright (c) 1992--1996 Matthew Newville, University of Washington
c
c Permission to use and redistribute the source code or binary forms of
c this software and its documentation, with or without modification is
c hereby granted provided that the above notice of copyright, these
c terms of use, and the disclaimer of warranty below appear in the
c source code and documentation, and that none of the names of The
c University of Chicago, The University of Washington, or the authors
c appear in advertising or endorsement of works derived from this
c software without specific prior written permission from all parties.
c
c THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
c EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
c MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
c IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
c CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
c TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
c SOFTWARE OR THE USE OR OTHER DEALINGS IN THIS SOFTWARE.
c//////////////////////////////////////////////////////////////////////
c
c purpose: automated xafs background spline (the autobk algorithm)
c
c arguments:
c      energy   array of energy values                  [in]
c      xmu      array of xmu values                     [in]
c      mxmu     maximum array length of energy/xmu      [in]
c      xkstd    array of k values for standard          [in]
c      chistd   array of chi values for standard        [in]
c      mstd     maximum array length of xkstd/chistd    [in]
c      fixstd   l=flag for fixing amp of standard       [in]
c      usestd   l=flag for using standard at all        [in]
c      rbkgin   rbkg value (nyquist value)              [in/out]
c      toler    fitting tolerance                       [in/out]
c      nknots   number of knots in spline               [in/out]
c      qmin     low-k value for fft window              [in/out]
c      qmax     high-k value for fft window             [in/out]
c      xkw      k-weight for fft                        [in]
c      dk1      low-k value for fft window              [in]
c      dk2      low-k value for fft window              [in]
c      winnam   fft window type                         [in]
c      varye0   l=flag for varying e0 in fit            [in]
c      e0_in    energy origin                           [in/out]
c      stfind   l=flag for redefining estep             [in]
c      fnorm    l=flag for functional normalization     [in]
c      enor1    low-energy  range for post-step         [in]
c      enor2    high-energy range for post-step         [in]
c      pre1     low-energy  range for pre-edge          [in]
c      pre2     high-energy range for pre-edge          [in]
c      estep    edge-step, aka delta_xmu                [in/out]
c      dofit    l=flag for doing fit at all             [in]
c      bkg      array of mu0 values   (mxmu points)     [out]
c      mxk      maximum array length of xk/chi          [out]
c      xk       array of k values                       [out]
c      chi      array of chi values                     [out]
c
c notes:
c   1. for use in ifeffit: uses ifeffit include files
c   2. based on/related to suroutine autnls from autobk
c   3. not well tested with standard chi(k) data
c
c see documentation and code comments for more details
c
c requires:  include files  consts.h, spline.h, fft.h
c            splfun, lmdif1, cffti, qintrp, lintrp, polyft,
c            fitfft, window, sumsqr, nofx, echo, chrdmp
c
c
       implicit none
       double precision  energy(*), xmu(*), bkg(*), xkstd(*), chistd(*)
       double precision  xk(*), chi(*), enor1, enor2, e0_in, estep
       double precision  rbkgin, toler,qmin,qmax
       double precision  xkw,dk1,dk2, pre1, pre2, cnorm(*)
       integer mxmu, mxk, mstd, nknots, nx1
       logical fnorm, fixstd, usestd, varye0, stfind, dofit
       logical           lclmp1, lclmp2
       double precision  xclmp1, xclmp2
c
       include 'consts.h'
       include 'spline.h'
       include 'fft.h'
c
       integer lenwrk, loop, lminfo, iup, ilo, nnorm , iterp
       integer iwork(mtknot), nemin, nemax, nofx, nrfit, nclmp
       integer i, nr, iend,  ntest, ipos, nqmax, j
       parameter(lenwrk = 2*maxpts*mtknot + 20*mtknot + 2*maxpts   )
       character*80  messg, winnam*32
       double precision   work(lenwrk), fvect(2*maxpts), varys(mtknot)
       double precision   esplin(mtknot), bscoef(mtknot), thiq(maxpts)
       double precision   small, toldef, spstep, r1stin
       double precision   sumsqr, qmn, qmx, qknot, slope, offset
       double precision  e0tst, qmntst, qmxtst,  xtmp, qtmp, e0t, tmp
       parameter (small  = 1.d-10, toldef = 1.d-5)
       external splfun, nofx, sumsqr
       save
c
       nnorm  = 3
       if (toler .le. zero) toler = toldef
       spstep = min(20.d0, max(1.d-6, spstep))
       loop   = 0

       if (.not.wftset) then
          call cffti(maxfft, wfftc)
          wftset = .true.
       end if

       rbkg   = rbkgin
       r1st   = rbkg + r1stin
       if (rbkg .le. small) rbkg = one
       nrbkg  = 2 * int(1d-2 + (rbkg /rgrid))+ 2
       nr1st  = 2 * int(1d-2 + (r1st /rgrid))+ 2
       if (qmax .le. small) qmax = 0
c
c initialize bkgdat common block
       splqw = xkw
       nsplin = nknots
       nautbk = 0
       thefix = fixstd
       theory = usestd
       eevary = varye0 .and. theory
cc       print*, ' spline:  eevary, = ', eevary, theory
       step   = estep
       e0     = e0_in
       emin   = e0 + qmin**2 / etok
       emax   = e0 + qmax**2 / etok
       e0t    = e0
       de0    = 0
       thessq = one
       thebkg = one
       nxmu   = mxmu
       do 10 i = 1, maxpts
          endat(i)  = zero
          xmudat(i) = zero
          spldat(i) = zero
          chie(i)   = zero
          splfit(i) = zero
          splwin(i) = zero
 10    continue
       do 20 i = 1, nxmu
          endat(i)  = energy(i)
          xmudat(i) = xmu(i)
          spldat(i) = xmu(i)
 20    continue

c do xafsft window
c put standard chi(k) on an absolute q-grid, and do fft
       if (mstd.gt.0) then
          nqmax = int( xkstd(mstd) / qgrid)
          ipos  = 1
          do 30 i = 1, nqmax
             qtmp = qgrid * (i-1)
             call qintrp(xkstd, chistd, mstd, qtmp, ipos, thiq(i))
 30       continue
       end if
c
 100   continue
       lminfo = 0
       loop   = loop + 1
c
c----start fitting:
       do 120 i = 1, nxmu
          bkg(i) = spldat(i)
 120   continue
c
       clamp(1)  = lclmp1
       clamp(2)  = lclmp2
       sclamp(1) = xclmp1
       sclamp(2) = xclmp2
       nclamp    = nclmp
cc       print*,' clamps: ', clamp, sclamp, nclamp
c
c  initialize fvect and work arrays for lmdif1

       nrfit = nrbkg
       if (theory)   nrfit = nr1st
       if (clamp(1)) nrfit = nrfit + nclamp
       if (clamp(2)) nrfit = nrfit + nclamp
       do 210 i =1, nrfit
          fvect(i) = zero
 210   continue
       do 220 i =1, lenwrk
          work(i) = zero
 220   continue
       do 240 i =1, mtknot
          iwork(i)  = 0
          varys(i)  = zero
          esplin(i) = zero
          bscoef(i) = zero
 240   continue
c
c  e0-shift:  e0, emin, and emax
       if (abs(de0).gt.1d-3) then
          e0t  = e0t  + de0
          emin = emin + de0
          emax = emax + de0
       end if
c
       if (emax.le.emin) emax = energy(nxmu)
       nemin = nofx(emin,energy,nxmu)
       nemax = nofx(emax,energy,nxmu)
       emin  = max(e0t, energy(nemin))
       emax  = max(emin, energy(nemax))
c
c  evaluate energies for the spline variables:
c  - emin and emax are not relative to the edge
c  - energy contains the input energy values, not relative to the edge.
       qmin   = sqrt(etok* abs(emin - e0t) )
       qmax   = sqrt(etok* (emax - e0t) )
c
c calculate number of independent points in r-space
       if (nsplin.le.1)
     $      nsplin = 2 * int(rbkg * (qmax - qmin)/ pi)  +  1
       nsplin = min(mtknot-5, max(5,nsplin))
       nknots = nsplin
cc       print*, ' SPLINE ', rbkg, nsplin

c  initialize energy values through which the first guess for the
c  spline must go (evenly spaced in q), and get the initial value
c  for the spline value at this point. we'll also get the initial
c  guesses for the variables (the b-spline coefficients) from this
c  initial spline.
       do 300 i = 1, nsplin
          qtmp      = qmin + (i-1)*(qmax-qmin)/(nsplin - 1)
          xtmp      = e0t + ( qtmp**2 / etok )
          j         = nofx(xtmp,energy,nxmu)
          esplin(i) = energy(j)
          iup       = min(nxmu, j + 5)
          ilo       = max(1,    j - 5)
          bscoef(i) = (2*spldat(j)+ spldat(iup)+spldat(ilo))/4
300    continue
       esplin(nsplin) = one + esplin(nsplin)
c the first and last korder knots in the b-spline are nearly degenerate at
c the endpoints. spstep sets the spacing between these points.  the
c default is one -- this may help eliminate "spikes" at the endpoints.
c since each knot represents a place where a derivative can break,
c  having all four of these at one place allows a complete break from
c at this point. by moving a few of the knots just off the ends, the
c spline is a little bit stiffer at the endpoints.
cc       print*, ' spline : ', spstep, korder
       do 310 i = 1, korder
           eknot(i)        = esplin(1)      - spstep * (korder-i-1)
           eknot(nsplin+i) = esplin(nsplin) + spstep * i
310    continue
       qmn       = sqrt( etok* abs(esplin(1) - e0t) )
       if (e0.lt.esplin(1))  qmn = zero
       qmx = sqrt( etok* (esplin(nsplin) - e0t) )
       do 320 i = korder+1, nsplin
           qknot    = (i-korder)*(qmx - qmn)/(nsplin-korder+1)
           eknot(i) = esplin(1) + qknot**2/etok
320    continue
c
c    determine the knots for the spline:
c    knots are points at which the spline has extra freedom.
       if ( (korder.lt.3).or.(nsplin.lt.korder) ) then
          call warn(2,
     $         ' spline error: not enough data to create spline.')
          return
       end if
c  the b-spline coefficients will be the variables in the fit,
c  the above estimates for the elements of bscoef are good enough
c  especially when the spline values are smoothed for the initial
c  guesses in the fit, as below.
       varys(1) = (3*bscoef(1) + bscoef(2))/ 4
       varys(nsplin) = (3*bscoef(nsplin) + bscoef(nsplin-1))/ 4
       do 380 i = 2, nsplin-1
          varys(i) =  (bscoef(i-1) + 2*bscoef(i) + bscoef(i+1) )/4
 380   continue
       nautbk = nsplin
c
c set up window function
       call window(winnam,dk1,dk2,qmin,qmax,qgrid,maxpts,splwin)
c  if a theory file is used, do its fft now
c  and add another variable if e0 is to be shifted
       if (theory) then
          if (eevary) then
             nautbk        = nautbk + 1
             varys(nautbk) = de0
          end if
          nr  = nrbkg
          call fitfft(thiq, maxpts, maxfft, wfftc, qgrid,
     $         splwin, splqw, bkg, zero, 1, 0, zero, r1st,
     $         nr, splfit)
          thessq = max(small, sumsqr(splfit(nrbkg),nr1st))
          thebkg = thessq
cc          print*, ' spline.f: thebkg = ', thebkg
cc          print*, '   nrbkg, nr1st   = ', nrbkg, nr1st, nr
          nautbk        = nautbk + 1
          varys(nautbk) = thessq
cc
          if (nr.ne.nr1st)  then
             call warn(3,' spline error: fitfft is broken' )
             return
          end if
       end if
c
       if (eevary) then
          call chrdmp(' spline: fitting background and e0 ... ')
       else
          call chrdmp(' spline: fitting background ... ')
       end if
c
       lminfo = 1
       if (dofit) then
          call lmdif1 (splfun, nrfit, nautbk, varys, fvect,
     $         toler, lminfo, iwork, work, lenwrk)
          call echo(' done.')
          call lm_err(lminfo,toler)
       end if
c
c  if an energy shift was done, the q values may be slightly off, so that
c  the number of independent points changes.  if so, re-do the fit until
c  e0 is stable to 0.5 eV, up to 5 times
       e0tst   = e0 + de0
       qmntst  = sqrt(etok* abs(emin - e0tst ) )
       qmxtst  = sqrt(etok* abs(emax - e0tst ) )
       if (e0tst.le.emin)    qmntst   = zero
       ntest   = 2 * int ( rbkg * abs(qmxtst - qmntst) / pi)  + 1
       ntest   = min(mtknot-5, max(5,ntest))
c
       if ((loop.le.5).and.eevary.and.(ntest.ne.nsplin) ) then
          call echo(' spline warning:  e0 was shifted enough that')
          call warn(1, ' the # of knots in the spline changed'//
     $         ' and the fit should be re-done.')
          go to 100
       end if
c
c  now that we have a good spline, we may want to improve the estimate
c  of the edge-step. so do a parabolic extrapolation of the background
c  spline to the edge energy value, which is probably better than
c  getting the edge step from a linear extrapolation of the xmu data.
c redo function evaluation with the final values for everything.
       call splfun(nrfit, nautbk, varys, fvect, iend)
       if (stfind.and.(.not.fnorm)) then
          call echo(' spline: finding edge_step from bkg(E)')
          call preedg(.false.,.true.,nxmu, endat, bkg, e0,
     $         pre1, pre2, enor1, enor2, nnorm,
     $         step, slope, offset,cnorm)
          estep = step
       end if
c redo function evaluation with the final values for everything.
       call splfun(nrfit, nautbk, varys, fvect, iend)
       if (eevary)  then
          e0    = e0_in + de0
          e0_in = e0
       end if
       do 830 i = 1, nxmu
          bkg(i) = spldat(i)
 830   continue

c set chi array
       call chie2k(endat,chie,nxmu,e0, mxk,xk,chi)
c       print*, 'MM end of spline ', endat(1), endat(2),
c     $      chie(1), chie(2), xk(1), xk(2),
c     $      chi(1), chi(2), nxmu, e0, mxk 
c end  subroutine spline
       return
       end