SUBROUTINE runchn(chlen, chnpar, chburn, filenm, chvals, qrand, & iear, ifluxar, ibflxar, iigroup, iichanb, & iichane, irespon, iesavar, ieffar, nfpar, & parasf, parval) IMPLICIT NONE INTEGER chlen, chnpar, nfpar INTEGER chburn INTEGER iear, ifluxar, ibflxar, iigroup, iichanb, iichane INTEGER irespon, iesavar, ieffar DOUBLE PRECISION chvals(chnpar) DOUBLE PRECISION parasf(nfpar) DOUBLE PRECISION parval(nfpar, 2) LOGICAL qrand CHARACTER filenm*(*) c Routine to run an MCMC chain starting at the current parameter values c Arguments : c chlen i i: The length of the chain to run c chnpar i i: The number of parameters (+ 1 for the statistic) c chburn i i: The burn-in length for the chain c filenm c i: Output filename c chvals d w: Workspace array for chain values c qrand l i: If true then use randomized starting point c iear i i: pointer to upper energy for ith energy range c ifluxar i i: pointer to model in energy space c ibflxar i i: pointer to background model in energy space c iigroup i i: pointer to number of response groups for ith energy c iichanb i i: pointer to start bin for ith response group c iichane i i: pointer to end bin for ith response group c irespon i i: pointer to response matrix elements c iesavar i i: pointer to save array for model components c ieffar i i: pointer to effective areas c nfpar i i: number of fit parameters c parasf d w: work array for saved parameter values c parval d w: work array for parameter values INCLUDE '../../inc/xspec.inc' REAL random, oldstat, ftstat INTEGER ieftprm, ievalue, ieigenv INTEGER ipar, iv, istat, neigen, impar, i, lun INTEGER ierr, j LOGICAL qincnw, qdone CHARACTER wrtstr*2048 DOUBLE PRECISION fgpval REAL xgfstt INTEGER fgnfpr, dgdpnt, fgneig, fgnvpr, lenact LOGICAL fgqfrz EXTERNAL fgpval, xgfstt, fgnfpr, dgdpnt, fgneig, fgqfrz EXTERNAL fgnvpr, lenact c test inputs IF ( chlen .LE. 0 ) & CALL xwrite('RUNCHN: Invalid chain length', 10) IF ( chnpar .LE. 0 ) & CALL xwrite('RUNCHN: Invalid number of parameters', 10) IF ( chburn .LT. 0 ) & CALL xwrite('RUNCHN: Invalid burn-in length', 10) IF ( chnpar-1 .NE. fgnvpr() ) & CALL xwrite('RUNCHN: Unexpected number of parameters', 10) IF ( lenact(filenm) .EQ. 0 ) & CALL xwrite('RUNCHN: No filename given', 10) c save the original parameter values nfpar = fgnfpr() DO ipar = 1, nfpar parasf(ipar) = fgpval(ipar, 'v') ENDDO c get the eigenfunctions and values to use to generate new sets of parameters neigen = fgneig() ieftprm = -1 CALL udmget( neigen, 4, ieftprm, istat ) IF( istat .NE. 0 ) CALL xwrite( & ' WARNING: Memory allocation for EFTPRM failed in RUNCHN.', 2) ievalue = -1 CALL udmget( neigen, 7, ievalue, istat ) IF( istat .NE. 0 ) CALL xwrite( & ' WARNING: Memory allocation for EVALUE failed in RUNCHN.', 2) ieigenv = -1 CALL udmget( neigen**2, 7, ieigenv, istat ) IF( istat .NE. 0 ) CALL xwrite( & ' WARNING: Memory allocation for EIGENV failed in RUNCHN.', 2) CALL fgeftp(neigen, MEMI(ieftprm), istat) CALL fgevec(neigen, MEMD(ieigenv), istat) CALL fgeval(neigen, MEMD(ievalue), istat) IF( istat .NE. 0 ) CALL xwrite( & ' WARNING: Failed to load eigenvectors in RUNCHN.', 2) c set the first point in the chain to the current parameter values c or a randomized starting point IF ( qrand ) THEN c triple the eigenvalues so we get a 3 sigma randomization DO i = 1, neigen MEMD(ievalue+i-1) = MEMD(ievalue+i-1)*3 ENDDO c generate new parameters till we get a valid set qdone = .FALSE. DO WHILE ( .NOT.qdone ) CALL simprm(neigen, MEMI(ieftprm), MEMD(ieigenv), & MEMD(ievalue), nfpar, parasf, parval(1,1)) qdone = .TRUE. DO ipar = 1, nfpar IF ( parval(ipar,1) .LT. fgpval(ipar,'l') .OR. & parval(ipar,1) .GT. fgpval(ipar,'h') ) THEN qdone = .FALSE. ENDIF ENDDO ENDDO c set the parameters just generated DO ipar = 1, nfpar CALL fppval(ipar, parval(ipar,1), 'v', istat) ENDDO CALL evlmod(memr(iear), .FALSE., memr(iesavar), memr(ifluxar), & memr(ibflxar), memr(ieffar)) CALL fldmod(memr(ifluxar), memr(ibflxar), memi(iigroup), & memi(iichanb), memi(iichane), memr(irespon), & memr(dgdpnt('mod')), memr(ieffar)) CALL calstat(memr(dgdpnt('mod')), memr(dgdpnt('src')), & memr(dgdpnt('csv')), memr(dgdpnt('cor')), & memr(dgdpnt('bkg')), memr(dgdpnt('cbv')), & memr(dgdpnt('reg')), memr(dgdpnt('brg')), & memr(dgdpnt('are')), memr(dgdpnt('bre')), & ftstat) chvals(chnpar) = ftstat c reset the eigenvalues DO i = 1, neigen MEMD(ievalue+i-1) = MEMD(ievalue+i-1)/3 ENDDO ENDIF c Set the first elements of the chain. If the randomization was in use this c will be offset from the fit else it will be the result of the last fit before c running the chain command. iv = 0 DO ipar = 1, nfpar IF ( .NOT. fgqfrz(ipar) ) THEN iv = iv + 1 chvals(iv) = fgpval(ipar, 'v') ENDIF ENDDO c If the randomization was not in use we need to set the statistic value IF ( .NOT.qrand ) chvals(chnpar) = xgfstt() c Open the output file and write the first row wrtstr = 'Writing chain to '//filenm(:lenact(filenm)) CALL xwrite(wrtstr, 25) CALL getlun(lun) CALL openwr(lun, filenm, 'new', ' ', ' ', 0, 1, ierr) IF ( ierr .NE. 0 ) THEN wrtstr = 'RUNCHN: Failed to open '// & filenm(:lenact(filenm)) CALL xwrite(wrtstr, 10) WRITE(wrtstr,'(a,i4)') ' iostat = ', ierr CALL xwrite(wrtstr, 10) CALL frelun(lun) RETURN ENDIF WRITE(wrtstr, *) (chvals(j),j=1,chnpar) WRITE(lun, '(a)') wrtstr(:lenact(wrtstr)) c loop over the chain including the burn-in. We accumulate the chain c through the burn-in then reset the start point so the burn-in data c is overwritten and the chain ends up with length chlen. DO i = 2, chlen+chburn c generate the proposed new set of parameters - use a multivariate Gaussian c based on the eigenfunctions of likelihood space at the last calculated c best fit. This assumes that the chain is being run after a best fit c procedure with the same data and model. DO ipar = 1, nfpar parval(ipar,1) = fgpval(ipar, 'v') ENDDO c generate new parameters till we get a valid set qdone = .FALSE. DO WHILE ( .NOT.qdone ) CALL simprm(neigen, MEMI(ieftprm), MEMD(ieigenv), & MEMD(ievalue), nfpar, parval(1,1), parval(1,2)) qdone = .TRUE. DO ipar = 1, nfpar IF ( parval(ipar,2) .LT. fgpval(ipar,'l') .OR. & parval(ipar,2) .GT. fgpval(ipar,'h') ) THEN qdone = .FALSE. ENDIF ENDDO ENDDO c set the parameters just generated DO ipar = 1, nfpar CALL fppval(ipar, parval(ipar,2), 'v', istat) ENDDO c calculate the likelihood for these new parameters CALL evlmod(memr(iear), .FALSE., memr(iesavar), memr(ifluxar), & memr(ibflxar), memr(ieffar)) CALL fldmod(memr(ifluxar), memr(ibflxar), memi(iigroup), & memi(iichanb), memi(iichane), memr(irespon), & memr(dgdpnt('mod')), memr(ieffar)) CALL calstat(memr(dgdpnt('mod')), memr(dgdpnt('src')), & memr(dgdpnt('csv')), memr(dgdpnt('cor')), & memr(dgdpnt('bkg')), memr(dgdpnt('cbv')), & memr(dgdpnt('reg')), memr(dgdpnt('brg')), & memr(dgdpnt('are')), memr(dgdpnt('bre')), & ftstat) c decide whether to include these proposed new parameters oldstat = chvals(chnpar) qincnw = .FALSE. IF ( ftstat .LE. oldstat ) THEN qincnw = .TRUE. ELSE CALL ranlux(random, 1) IF ( random .LT. exp(0.5*(oldstat-ftstat)) ) & qincnw = .TRUE. ENDIF c if we include the new parameters then save in the chain IF ( qincnw ) THEN iv = 0 DO ipar = 1, nfpar IF ( .NOT. fgqfrz(ipar) ) THEN iv = iv + 1 chvals(iv) = fgpval(ipar, 'v') ENDIF ENDDO chvals(chnpar) = ftstat c if we don't include the new parameters then c reset the parameters to the old values ELSE DO ipar = 1, nfpar CALL fppval(ipar, parval(ipar,1), 'v', istat) ENDDO ENDIF c if this is the end of the burn-in then reset to the start of the file IF ( i .EQ. chburn+1 ) REWIND(lun) c Write the current chain values to the file WRITE(wrtstr, *) (chvals(j),j=1,chnpar) WRITE(lun, '(a)') wrtstr(:lenact(wrtstr)) c end loop over the chain ENDDO c Close the output file CLOSE(lun) CALL frelun(lun) c free up memory from the temporary parameters CALL udmfre( ieftprm, 4, istat ) IF( istat .NE. 0 ) CALL xwrite( & ' WARNING: Memory deallocation of EFTPRM failed in RUNCHN.', 2) CALL udmfre( ievalue, 7, istat ) IF( istat .NE. 0 ) CALL xwrite( & ' WARNING: Memory deallocation of EVALUE failed in RUNCHN.', 2) CALL udmfre( ieigenv, 7, istat ) IF( istat .NE. 0 ) CALL xwrite( & ' WARNING: Memory deallocation of EIGENV failed in RUNCHN.', 2) c reset to the original parameters and recalculate model DO i = 1, nfpar CALL fppval(i, parasf(i), 'v', istat) ENDDO CALL evlmod(memr(iear), .FALSE., memr(iesavar), memr(ifluxar), & memr(ibflxar), memr(ieffar)) CALL fldmod(memr(ifluxar), memr(ibflxar), memi(iigroup), & memi(iichanb), memi(iichane), memr(irespon), & memr(dgdpnt('mod')), memr(ieffar)) CALL calstat(memr(dgdpnt('mod')), memr(dgdpnt('src')), & memr(dgdpnt('csv')), memr(dgdpnt('cor')), & memr(dgdpnt('bkg')), memr(dgdpnt('cbv')), & memr(dgdpnt('reg')), memr(dgdpnt('brg')), & memr(dgdpnt('are')), memr(dgdpnt('bre')), & ftstat) RETURN END