root/branches/newmole/source/atom_feii.cpp

Revision 2582, 91.9 kB (checked in by rporter, 2 weeks ago)

branches/newmole - bring in trunk r2541:2581

  • Property svn:eol-style set to native
Line 
1/* This file is part of Cloudy and is copyright (C)1978-2008 by Gary J. Ferland and
2 * others.  For conditions of distribution and use see copyright notice in license.txt */
3/*FeII_Colden maintain H2 column densities within X */
4/*FeIILevelPops  main FeII routine, called by CoolIron to evaluate iron cooling */
5/*FeIICreate read in needed data from file
6 * convert form of FeII data, as read in from file within routine FeIICreate
7 * into physical form.  called by atmdat_readin  */
8/*FeIIPunPop - punch level populations */
9/*AssertFeIIDep called by assert FeII depart coef command */
10/*FeIIPrint print FeII information */
11/*FeIICollRatesBoltzmann evaluate collision strenths,
12 * both interpolating on r-mat and creating g-bar
13 * find Boltzmann factors, evaluate collisional rate coefficients */
14/*FeIIPrint print output from large FeII atom, called by prtzone */
15/*FeIISumBand sum up large FeII emission over certain bands, called in lineset4 */
16/*FeII_RT_TauInc called once per zone in RT_tau_inc to increment large FeII atom line optical depths */
17/*FeII_RT_tau_reset reset optical depths for large FeII atom, called by update after each iteration */
18/*FeIIPoint called by ContCreatePointers to create pointers for lines in large FeII atom */
19/*FeIIAccel called by rt_line_driving to compute radiative acceleration due to FeII lines */
20/*FeIIAddLines save accumulated FeII intensities called by lineset4 */
21/*FeIIPunchLines punch FeII lines at end of calculation, if punch verner set, called by punch_do*/
22/*FeIIPunchOpticalDepth punch FeII line optical depth at end of calculation, called by punch_do*/
23/*FeIIPunchLevels punch FeII levels and energies */
24/*FeII_LineZero zero out storage for large FeII atom, called by tauout */
25/*FeIIIntenZero zero out intensity of FeII atom */
26/*FeIIZero initialize some variables, called by zero one time before commands parsed */
27/*FeIIReset reset some variables, called by zero */
28/*FeIIPunData punch line data */ 
29/*FeIIPunDepart punch some departure coef for large atom,
30 * set with punch FeII departure command*/
31/*FeIIPun1Depart send the departure coef for physical level nPUN to unit ioPUN */
32/*FeIIContCreate create FeII continuum bins to add lines into ncell cells between wavelengths lambda low and high,
33 * returns number of cells used */
34/*FeIIBandsCreate returns number of FeII bands */
35/*FeII_RT_Out do outward rates for FeII, called by RT_diffuse */
36/*FeII_OTS do ots rates for FeII, called by RT_OTS */
37/*FeII_RT_Make called by RT_line_all, does large FeII atom radiative transfer */
38/*FeIILyaPump find rate of Lya excitation of the FeII atom */
39/*FeIIOvrLap handle overlapping FeII lines */
40/*ParseAtomFeII parse the atom FeII command */
41/*FeIIPunchLineStuff include FeII lines in punched optical depths, etc, called from PunchLineStuff */
42#include "cddefines.h"
43#include "cddrive.h"
44#include "thermal.h"
45#include "physconst.h"
46#include "doppvel.h"
47#include "taulines.h"
48#include "dense.h"
49#include "rfield.h"
50#include "radius.h"
51#include "lines_service.h"
52#include "ipoint.h"
53#include "thirdparty.h"
54#include "hydrogenic.h"
55#include "lines.h"
56#include "rt.h"
57#include "trace.h"
58#include "punch.h"
59#include "phycon.h"
60#include "atomfeii.h"
61#include "iso.h"
62#include "pressure.h"
63
64/* FeIIOvrLap handle overlapping FeII lines */
65STATIC void FeIIOvrLap(void);
66
67/* add FeII lines into ncell cells between wavelengths lambda low and high */
68STATIC void FeIIContCreate(double xLamLow , double xLamHigh , long int ncell );
69
70/* read in the FeII Bands file, and sets nFeIIBands, the number of bands,
71 * if argument is "" then use default file with bands,
72 * if filename within "" then use it instead,
73 * return value is 0 if success, 1 if failed */
74STATIC int FeIIBandsCreate( const char chFile[] );
75
76/* this will be the smallest collision strength we will permit with the gbar
77 * collision strengths, or for the data that have zeroes */
78/* >>chng 99 jul 24, this was 1e-9 in the old fortran version and in baldwin et al. 96,
79 * and has been changed several times, and affects results.  this is the smallest
80 * non-zero collision strength in the r-matrix calculations */
81realnum CS2SMALL = (realnum)1e-5;
82/* routines used only within this file */
83
84/*FeIICollRatesBoltzmann evaluate collision strenths,
85 * both interpolating on r-mat and creating g-bar
86 * find Boltzmann factors, evaluate collisional rate coefficients */
87STATIC void FeIICollRatesBoltzmann(void);
88/* find rate of Lya excitation of the FeII atom */
89STATIC void FeIILyaPump(void);
90
91/*extern realnum Fe2LevN[NFE2LEVN][NFE2LEVN][NTA];*/
92/*extern realnum Fe2LevN[ipHi][ipLo].t[NTA];*/
93/*realnum ***Fe2LevN;*/
94/* >>chng 06 mar 01, boost to global namespace */
95/*transition **Fe2LevN;*/
96/* flag for the collision strength */
97int **ncs1;
98
99/* all following variables have file scope
100#define NFEIILINES      68635 */
101
102/* this is size of nnPradDat array */
103#define NPRADDAT 159
104
105/* band wavelength, lower and upper bounds, in vacuum Angstroms */
106/* FeII_Bands[n][3], where n is the number of bands in fe2Bands.dat
107 * these bands are defined in fe2Bands.dat and read in at startup
108 * of calculation */
109realnum **FeII_Bands;
110
111/* continuum wavelengths, lower and upper bounds, in vacuum Angstroms,
112 * third is integrated intensity */
113/* FeII_Cont[n][3], where n is the number of cells needed
114 * these bands are defined in FeIIContCreate */
115realnum **FeII_Cont;
116
117/* this is the number of bands read in from FeII_bands.ini */
118long int nFeIIBands;
119
120/* number of bands in continuum array */
121long int nFeIIConBins;
122
123/* the dim of this vector this needs one extra since there are 20 numbers per line,
124 * highest not ever used for anything */
125/*long int nnPradDat[NPRADDAT+1];*/
126static long int *nnPradDat;
127
128/* malloced in feiidata */
129/* realnum sPradDat[NPRADDAT][NPRADDAT][8];*/
130/* realnum sPradDat[ipHi][ipLo].t[8];*/
131static realnum ***sPradDat;
132
133/* array used to integrate FeII line intensities over model,
134 * Fe2SavN[upper][lower],
135 *static realnum Fe2SavN[NFE2LEVN][NFE2LEVN];*/
136static double **Fe2SavN;
137
138/* save effective transition rates */
139static double **Fe2A;
140
141/* induced transition rates */
142static double **Fe2LPump, **Fe2CPump;
143
144/* energies read in from fe2energies.dat data file */
145realnum *Fe2Energies;
146
147/* collision rates */
148static realnum **Fe2Coll;
149
150/* Fe2DepCoef[NFE2LEVN];
151realnum cli[NFEIILINES],
152  cfe[NFEIILINES];*/
153static double 
154        /* departure coefficients */
155        *Fe2DepCoef ,
156        /* level populations */
157        *Fe2LevelPop ,
158        /* column densities */
159        *Fe2ColDen ,
160        /* this will become array of Boltzmann factors */
161        *FeIIBoltzmann;
162        /*FeIIBoltzmann[NFE2LEVN] ,*/
163
164static double EnerLyaProf1,
165  EnerLyaProf4,
166  PhotOccNumLyaCenter;
167static double 
168                /* the yVector - will become level populations after matrix inversion */
169                *yVector,
170          /* this is used to call matrix routines */
171          /*xMatrix[NFE2LEVN][NFE2LEVN] ,*/
172          **xMatrix ,
173          /* this will become the very large 1-D array that
174           * is passed to the matrix inversion routine*/
175          *amat;
176
177
178/*FeII_Colden maintain H2 column densities within X */
179void FeII_Colden( const char *chLabel )
180{
181        long int n;
182
183        DEBUG_ENTRY( "FeII_Colden()" );
184
185        /* >>chng 05 nov 29, FeII always on, always want to evaluate this */
186        /* nothing to do if no FeII
187        if( !FeII. lgFeIION )
188                return;*/
189
190        if( strcmp(chLabel,"ZERO") == 0 )
191        {
192                /* zero out column density */
193                for( n=0; n < FeII.nFeIILevel; ++n )
194                {
195                        /* space for the rotation quantum number */
196                        Fe2ColDen[n] = 0.;
197                }
198        }
199
200        else if( strcmp(chLabel,"ADD ") == 0 )
201        {
202                /*  add together column densities */
203                for( n=0; n < FeII.nFeIILevel; ++n )
204                {
205                        /* state-specific FeII column density */
206                        Fe2ColDen[n] += Fe2LevelPop[n]*radius.drad_x_fillfac;
207                }
208        }
209
210        /* check for the print option, which we can't do, else  we have a problem */
211        else if( strcmp(chLabel,"PRIN") != 0 )
212        {
213                fprintf( ioQQQ, " FeII_Colden does not understand the label %s\n",
214                  chLabel );
215                cdEXIT(EXIT_FAILURE);
216        }
217
218        return;
219}
220
221/*
222 *=====================================================================
223 */
224/* FeIICreate read in FeII data from files on disk.  called by atmdat_readin
225 * but only if FeII. lgFeIION is true, set with atom FeII verner command */
226void FeIICreate(void)
227{
228        FILE *ioDATA;
229
230        char chLine[FILENAME_PATH_LENGTH_2];
231
232        long int i,
233          ipHi ,
234          ipLo,
235          lo,
236          ihi,
237          k,
238          m1,
239          m2,
240          m3;
241
242        DEBUG_ENTRY( "FeIICreate()" );
243
244        if( lgFeIIMalloc )
245        {
246                /* we have already been called one time, just bail out */
247
248                return;
249        }
250
251        /* now set flag so never done again - this is set false when initi
252         * when this is true it is no longer possible to change the number of levels
253         * in the model atom with the atom FeII levels command */
254        lgFeIIMalloc = true;
255
256        /* remember how many levels this was, so that in future calculations
257         * we can reset the atom to full value */
258        FeII.nFeIILevelAlloc = FeII.nFeIILevel;
259
260        /* set up array to save FeII transition probabilities */
261        Fe2A = (double **)MALLOC(sizeof(double *)*(unsigned long)FeII.nFeIILevelAlloc );
262
263        /* second dimension, lower level, for line save array */
264        for( ipHi=0; ipHi < FeII.nFeIILevelAlloc; ++ipHi )
265        {
266                Fe2A[ipHi]=(double*)MALLOC(sizeof(double )*(unsigned long)FeII.nFeIILevelAlloc);
267        }
268
269        /* set up array to save FeII pumping rates */
270        Fe2CPump = (double **)MALLOC(sizeof(double *)*(unsigned long)FeII.nFeIILevelAlloc );
271
272        /* set up array to save FeII pumping rates */
273        Fe2LPump = (double **)MALLOC(sizeof(double *)*(unsigned long)FeII.nFeIILevelAlloc );
274
275        /* second dimension, lower level, for line save array */
276        for( ipHi=0; ipHi < FeII.nFeIILevelAlloc; ++ipHi )
277        {
278                Fe2CPump[ipHi]=(double*)MALLOC(sizeof(double )*(unsigned long)FeII.nFeIILevelAlloc);
279
280                Fe2LPump[ipHi]=(double*)MALLOC(sizeof(double )*(unsigned long)FeII.nFeIILevelAlloc);
281        }
282
283        /* set up array to save FeII collision rates */
284        Fe2Energies = (realnum *)MALLOC(sizeof(realnum)*(unsigned long)FeII.nFeIILevelAlloc );
285
286        /* set up array to save FeII collision rates */
287        Fe2Coll = (realnum **)MALLOC(sizeof(realnum *)*(unsigned long)FeII.nFeIILevelAlloc );
288
289        /* second dimension, lower level, for line save array */
290        for( ipHi=0; ipHi < FeII.nFeIILevelAlloc; ++ipHi )
291        {
292                Fe2Coll[ipHi]=(realnum*)MALLOC(sizeof(realnum )*(unsigned long)FeII.nFeIILevelAlloc);
293        }
294
295        /* MALLOC space for the 2D matrix array */
296        xMatrix = (double **)MALLOC(sizeof(double *)*(unsigned long)FeII.nFeIILevelAlloc );
297
298        /* now do the second dimension */
299        for( i=0; i<FeII.nFeIILevelAlloc; ++i )
300        {
301                xMatrix[i] = (double *)MALLOC(sizeof(double)*(unsigned long)FeII.nFeIILevelAlloc );
302        }
303        /* MALLOC space for the  1-yVector array */
304        amat=(double*)MALLOC( (sizeof(double)*(unsigned long)(FeII.nFeIILevelAlloc*FeII.nFeIILevelAlloc) ));
305
306        /* MALLOC space for the  1-yVector array */
307        yVector=(double*)MALLOC( (sizeof(double)*(unsigned long)(FeII.nFeIILevelAlloc) ));
308
309        /* set up array to save FeII line intensities */
310        Fe2SavN = (double **)MALLOC(sizeof(double *)*(unsigned long)FeII.nFeIILevelAlloc );
311
312        /* second dimension, lower level, for line save array */
313        for( ipHi=1; ipHi < FeII.nFeIILevelAlloc; ++ipHi )
314        {
315                Fe2SavN[ipHi]=(double*)MALLOC(sizeof(double )*(unsigned long)ipHi);
316        }
317
318        /* now MALLOC space for energy level table*/
319        nnPradDat = (long*)MALLOC( (NPRADDAT+1)*sizeof(long) );
320
321        /*Fe2DepCoef[NFE2LEVN];*/
322        Fe2DepCoef = (double*)MALLOC( (unsigned long)FeII.nFeIILevelAlloc*sizeof(double) );
323
324        /*Fe2LevelPop[NFE2LEVN];*/
325        Fe2LevelPop = (double*)MALLOC( (unsigned long)FeII.nFeIILevelAlloc*sizeof(double) );
326
327        /*Fe2ColDen[NFE2LEVN];*/
328        Fe2ColDen = (double*)MALLOC( (unsigned long)FeII.nFeIILevelAlloc*sizeof(double) );
329
330        /*FeIIBoltzmann[NFE2LEVN];*/
331        FeIIBoltzmann = (double*)MALLOC( (unsigned long)FeII.nFeIILevelAlloc*sizeof(double) );
332
333
334        /* MALLOC the realnum sPradDat[NPRADDAT][NPRADDAT][8] array */
335        /* MALLOC the realnum sPradDat[ipHi][ipLo].t[8] array */
336        sPradDat = ((realnum ***)MALLOC(NPRADDAT*sizeof(realnum **)));
337
338        /* >>chng 00 dec 06, changed lower limit of loop to 1, Tru64 alpha's will not allocate 0 bytes!, PvH */
339        sPradDat[0] = NULL;
340        for(ipHi=1; ipHi < NPRADDAT; ipHi++)
341        {
342                /* >>chng 00 dec 06, changed sizeof(realnum) to sizeof(realnum*), PvH */
343                sPradDat[ipHi] = (realnum **)MALLOC((unsigned long)ipHi*sizeof(realnum *));
344
345                /* now make space for the second dimension */
346                for( ipLo=0; ipLo< ipHi; ipLo++ )
347                {
348                        sPradDat[ipHi][ipLo] = (realnum *)MALLOC(8*sizeof(realnum ));
349                }
350        }
351
352        /* now set junk to make sure reset before used */
353        for(ipHi=0; ipHi < NPRADDAT; ipHi++)
354        {
355                for( ipLo=0; ipLo< ipHi; ipLo++ )
356                {
357                        for( k=0; k<8; ++k )
358                        {
359                                sPradDat[ipHi][ipLo][k] = -FLT_MAX;
360                        }
361                }
362        }
363
364        /* now create the main emission line array and a helper array for the cs flag  */
365        Fe2LevN=(transition**)MALLOC(sizeof(transition *)*(unsigned long)FeII.nFeIILevelAlloc);
366        ncs1=(int**)MALLOC(sizeof(int *)*(unsigned long)FeII.nFeIILevelAlloc);
367
368        for( ipHi=1; ipHi < FeII.nFeIILevelAlloc; ++ipHi )
369        {
370                Fe2LevN[ipHi]=(transition*)MALLOC(sizeof(transition)*(unsigned long)ipHi);
371
372                ncs1[ipHi]=(int*)MALLOC(sizeof(int)*(unsigned long)ipHi);
373        }
374
375#if     0
376        /* now that its created, set to junk */
377        for( ipLo=0; ipLo < (FeII.nFeIILevelAlloc - 1); ipLo++ )
378        {
379                for( ipHi=ipLo + 1; ipHi < FeII.nFeIILevelAlloc; ipHi++ )
380                {
381                        TransitionJunk( &Fe2LevN[ipHi][ipLo] );
382                }
383        }
384#endif
385
386        /* now assign state and Emis pointers */
387        Fe2LevN[1][0].Lo = AddState2Stack();
388        /* now that its created, set to junk */
389        for( ipHi=1; ipHi < FeII.nFeIILevelAlloc; ipHi++ )
390        {
391                /* add this upper level */
392                Fe2LevN[ipHi][0].Hi = AddState2Stack();
393                for( ipLo=0; ipLo < ipHi; ipLo++ )
394                {
395                        if( ipLo == 0 )
396                        {
397                                Fe2LevN[ipHi][ipLo].Lo = Fe2LevN[1][0].Lo;
398                        }
399                        else
400                        {
401                                /* lower state is same as a previous upper state. */
402                                Fe2LevN[ipHi][ipLo].Lo = Fe2LevN[ipLo][0].Hi;
403                        }
404
405                        Fe2LevN[ipHi][ipLo].Hi = Fe2LevN[ipHi][0].Hi;
406                        Fe2LevN[ipHi][ipLo].Emis = AddLine2Stack( true );
407                }
408        }
409
410        if( trace.lgTrace )
411        {
412                fprintf( ioQQQ," FeIICreate opening fe2nn.dat:");
413        }
414
415        ioDATA = open_data( "fe2nn.dat", "r" );
416
417        ASSERT( ioDATA !=NULL );
418        /* read in the fe2nn.dat file - this gives the Zheng and Pradhan number of level
419         * for every cloudy level.  So nnPradDat[1] is the index in the cloudy level
420         * counting for level 1 of Zheng & Pradan
421         * note that the order of some levels is different, the nnPradDat file goes
422         * 21 23 22 - also that many levels are missing, the file goes 95 99 94 93 116
423         */
424        for( i=0; i < 8; i++ )
425        {
426                if( read_whole_line( chLine , (int)sizeof(chLine) , ioDATA ) == NULL )
427                {
428                        fprintf( ioQQQ, " fe2nn.dat error reading data\n" );
429                        cdEXIT(EXIT_FAILURE);
430                }
431
432                /* get these integers from fe2nn.dat */
433                k = 20*i;
434                /* NPRADDAT is size of nnPradDat array, 159+1, make sure we do not exceed it */
435                ASSERT( k+19 < NPRADDAT+1 );
436                sscanf( chLine ,
437                        "%4ld%4ld%4ld%4ld%4ld%4ld%4ld%4ld%4ld%4ld%4ld%4ld%4ld%4ld%4ld%4ld%4ld%4ld%4ld%4ld",
438                        &nnPradDat[k+0], &nnPradDat[k+1],  &nnPradDat[k+2], &nnPradDat[k+3], &nnPradDat[k+4],
439                        &nnPradDat[k+5], &nnPradDat[k+6],  &nnPradDat[k+7], &nnPradDat[k+8], &nnPradDat[k+9],
440                        &nnPradDat[k+10],&nnPradDat[k+11], &nnPradDat[k+12],&nnPradDat[k+13],&nnPradDat[k+14],
441                        &nnPradDat[k+15],&nnPradDat[k+16], &nnPradDat[k+17],&nnPradDat[k+18],&nnPradDat[k+19]
442                        );
443#               if !defined(NDEBUG)
444                for( m1=0; m1<20; ++m1 )
445                {
446                        ASSERT( nnPradDat[k+m1] >= 0 && nnPradDat[k+m1] <= NFE2LEVN );
447                }
448#               endif
449        }
450        fclose(ioDATA);
451
452        /* now get energies */
453        if( trace.lgTrace )
454        {
455                fprintf( ioQQQ," FeIICreate opening fe2energies.dat:");
456        }
457
458        ioDATA = open_data( "fe2energies.dat", "r" );
459
460        /* file now open, read the data */
461        for( ipHi=0; ipHi < FeII.nFeIILevelAlloc; ipHi++ )
462        {
463                /* keep reading until have non-comment line, one that does not
464                 * start with # */
465                chLine[0] = '#';
466                while( chLine[0] == '#' )
467                {
468                        if( read_whole_line( chLine , (int)sizeof(chLine) , ioDATA ) == NULL )
469                        {
470                                fprintf( ioQQQ, " fe2energies.dat error reading data\n" );
471                                cdEXIT(EXIT_FAILURE);
472                        }
473                }
474
475                /* first and last number on this line */
476                double help;
477                sscanf( chLine, "%lf", &help );
478                Fe2Energies[ipHi] = (realnum)help;
479        }
480        fclose(ioDATA);
481
482        /* transition probabilities */
483
484        if( trace.lgTrace )
485                fprintf( ioQQQ," FeIICreate opening fe2rad.dat:");
486
487        ioDATA = open_data( "fe2rad.dat", "r" );
488
489        /* get the first line, this is a version number */
490        if( read_whole_line( chLine , (int)sizeof(chLine) , ioDATA ) == NULL )
491        {
492                fprintf( ioQQQ, " fe2rad.dat error reading data\n" );
493                cdEXIT(EXIT_FAILURE);
494        }
495        /* scan off three integers */
496        sscanf( chLine ,"%ld%ld%ld",&lo, &ihi, &m1);
497        const int nYrFe2Rad=8, nMoFe2Rad=8, nDyFe2Rad=24;
498        if( lo!=nYrFe2Rad || ihi!=nMoFe2Rad || m1!=nDyFe2Rad )
499        {
500                fprintf( ioQQQ, "DISASTER fe2rad.dat has the wrong magic numbers, expected "
501                        "%2i %2i %2i and got %2li %2li %2li\n" ,
502                        nYrFe2Rad, nMoFe2Rad, nDyFe2Rad,
503                        lo, ihi, m1);
504                cdEXIT(EXIT_FAILURE);
505        }
506
507        /* file now open, read the data */
508        for( ipHi=1; ipHi < FeII.nFeIILevelAlloc; ipHi++ )
509        {
510                for( ipLo=0; ipLo < ipHi; ipLo++ )
511                {
512                        /* following double since used in sscanf */
513                        double save[2];
514                        /* keep reading until have non-comment line, one that does not
515                         * start with # */
516                        chLine[0] = '#';
517                        while( chLine[0] == '#' )
518                        {
519                                if( read_whole_line( chLine , (int)sizeof(chLine) , ioDATA ) == NULL )
520                                {
521                                        fprintf( ioQQQ, " fe2nn.dat error reading data\n" );
522                                        cdEXIT(EXIT_FAILURE);
523                                }
524                        }
525
526                        /* first and last number on this line */
527                        sscanf( chLine ,
528                                "%ld%ld%ld%ld%lf%lf%ld",
529                                &lo, &ihi, &m1, &m2 ,
530                                &save[0], &save[1] , &m3);
531                        /* the indices ihi and lo within this array were
532                         * read in from the line above */
533                        Fe2LevN[ihi-1][lo-1].Lo->g = (realnum)m1;
534                        Fe2LevN[ihi-1][lo-1].Hi->g = (realnum)m2;
535                        Fe2LevN[ihi-1][lo-1].Emis->Aul = (realnum)save[0];
536                        /*>>chng 06 apr 10, option to use table of energies */
537#                       define  USE_OLD true
538                        if( USE_OLD )
539                                Fe2LevN[ihi-1][lo-1].EnergyWN = (realnum)save[1];
540                        else
541                                Fe2LevN[ihi-1][lo-1].EnergyWN = Fe2Energies[ihi-1]-Fe2Energies[lo-1];
542
543                        /* Aul == -1 indicates intercombination line with no real Aul */
544                        if( fp_equal( Fe2LevN[ihi-1][lo-1].Emis->Aul , (realnum)-1.0f ) )
545                        {
546                                /* these are made-up intercombination lines, set gf to 1e-5 */
547                                Fe2LevN[ihi-1][lo-1].Emis->gf = 1e-5f;
548                               
549                                /* get corresponding A */
550                                Fe2LevN[ihi-1][lo-1].Emis->Aul = Fe2LevN[ihi-1][lo-1].Emis->gf*(realnum)TRANS_PROB_CONST*
551                                        POW2(Fe2LevN[ihi-1][lo-1].EnergyWN)*Fe2LevN[ihi-1][lo-1].Lo->g/Fe2LevN[ihi-1][lo-1].Hi->g;
552                        }
553
554                        /* the last column of fe2rad.dat, and is 1, 2, or 3. 
555                         * 1 signifies that transition is permitted,
556                         * 2 is semi-forbidden
557                         * 3 forbidden, within lowest 63 levels are forbidden, first permitted
558                         * transition is from 64 */
559                        ncs1[ihi-1][lo-1] = (int)m3;
560                }
561        }
562        fclose(ioDATA);
563
564        /* now read collision data in fe2col.dat
565         * These are from the following sources
566         >>refer        fe2     cs      Zhang, H.L., & Pradhan, A.K., 1995, A&A 293, 953
567         >>refer        fe2     cs      Bautista, M., (private communication),
568         >>refer        fe2     cs      Mewe, R., 1972, A&AS 20, 215 (the g-bar approximation)
569         */
570
571        if( trace.lgTrace )
572        {
573                fprintf( ioQQQ," FeIICreate opening fe2col.dat:");
574        }
575
576        ioDATA = open_data( "fe2col.dat", "r" );
577
578        ASSERT( ioDATA !=NULL);
579        for( ipHi=1; ipHi<NPRADDAT; ++ipHi )
580        {
581                for( ipLo=0; ipLo<ipHi; ++ipLo )
582                {
583                        /* double since used in sscanf */
584                        double save[8];
585                        if( read_whole_line( chLine , (int)sizeof(chLine) , ioDATA ) == NULL )
586                        {
587                                fprintf( ioQQQ, " fe2col.dat error reading data\n" );
588                                cdEXIT(EXIT_FAILURE);
589                        }
590                        sscanf( chLine ,
591                                "%ld%ld%lf%lf%lf%lf%lf%lf%lf%lf",
592                                &m1, &m2,
593                                &save[0], &save[1] , &save[2],&save[3], &save[4] , &save[5],
594                                &save[6], &save[7]
595                                );
596                        for( k=0; k<8; ++k )
597                        {
598                                /* the max is here because there are some zeroes in the data file.
599                                 * this is unphysical but is part of their distribution.  As a result
600                                 * don't let the cs fall below 0.01 */
601                                sPradDat[m2-1][m1-1][k] = max(CS2SMALL , (realnum)save[k] );
602                        }
603                }
604        }
605
606        fclose( ioDATA );
607
608        /*generate needed opacity data for the large FeII atom */
609
610        /* this routine is called only one time when cloudy is init
611         * for the very first time.  It converts the FeII data stored
612         * in the large FeII arrays into the array storage needed by cloudy
613         * for its line optical depth arrays
614         */
615
616        /* convert large FeII line arrays into standard heavy el ar */
617        for( ipLo=0; ipLo < (FeII.nFeIILevelAlloc - 1); ipLo++ )
618        {
619                for( ipHi=ipLo + 1; ipHi < FeII.nFeIILevelAlloc; ipHi++ )
620                {
621                        /* pull information out of existing data arrays */
622
623                        ASSERT( Fe2LevN[ipHi][ipLo].EnergyWN > 0. );
624                        ASSERT( Fe2LevN[ipHi][ipLo].Emis->Aul> 0. );
625
626                        /* now put into standard internal line format
627                        Fe2LevN[ipHi][ipLo].WLAng = (realnum)(1.e8/Fe2LevN[ipHi][ipLo].EnergyWN); */
628                        /* >>chng 03 oct 28, above neglected index of refraction of air -
629                         * convert to below */
630                        Fe2LevN[ipHi][ipLo].WLAng =
631                                (realnum)(1.0e8/
632                        Fe2LevN[ipHi][ipLo].EnergyWN/
633                        RefIndex( Fe2LevN[ipHi][ipLo].EnergyWN ));
634
635                        /* generate gf from A */
636                        Fe2LevN[ipHi][ipLo].Emis->gf =
637                                (realnum)(Fe2LevN[ipHi][ipLo].Emis->Aul*Fe2LevN[ipHi][ipLo].Hi->g/
638                                TRANS_PROB_CONST/POW2(Fe2LevN[ipHi][ipLo].EnergyWN));
639
640                        Fe2LevN[ipHi][ipLo].Hi->IonStg = 2;
641                        Fe2LevN[ipHi][ipLo].Hi->nelem = 26;
642                        /* which redistribution function?? 
643                         * For resonance line use K2 (-1),
644                         * for subordinate lines use CRD with wings */
645                        /* >>chng 01 mar 09, all had been 1, inc redis with wings */
646                        /* to reset this, so that the code works as it did pre 01 mar 29,
647                         * use command
648                         * atom FeII redistribution resonance pdr
649                         * atom FeII redistribution subordinate pdr */
650                        if( ipLo == 0 )
651                        {
652                                Fe2LevN[ipHi][ipLo].Emis->iRedisFun = FeII.ipRedisFcnResonance;
653                        }
654                        else
655                        {
656                                /* >>chng 01 feb 27, had been -1, crd with core only,
657                                 * change to crd with wings as per discussion with Ivan Hubeny */
658                                Fe2LevN[ipHi][ipLo].Emis->iRedisFun = FeII.ipRedisFcnSubordinate;
659                        }
660                        Fe2LevN[ipHi][ipLo].Emis->phots = 0.;
661                        Fe2LevN[ipHi][ipLo].Emis->ots = 0.;
662                        Fe2LevN[ipHi][ipLo].Emis->FracInwd = 1.;
663                }
664        }
665
666        /* finally get FeII bands, this sets  */
667        if( FeIIBandsCreate("") )
668        {
669                fprintf( ioQQQ," failed to open FeII bands file \n");
670                cdEXIT(EXIT_FAILURE);
671        }
672
673        /* now establish the FeII continuum, these are set to
674         * 1000, 7000, and 1000 in FeIIZero in this file, and
675         * are reset with the atom FeII continuum command */
676        FeIIContCreate( FeII.fe2con_wl1 , FeII.fe2con_wl2 , FeII.nfe2con );
677
678        /* this must be true */
679        ASSERT( FeII.nFeIILevelAlloc == FeII.nFeIILevel );
680
681        return;
682}
683
684/*
685 *=====================================================================
686 */
687/***********************************************************************
688 *** version of FeIILevelPops.f with overlap in procces 05.28.97 ooooooo
689 ******change in common block *te* sqrte 05.28.97
690 *******texc is fixed 03.28.97
691 *********this version has work on overlap
692 *********this version has # of zones (ZoneCnt) 07.03.97
693 *********taux - optical depth depends on iter correction 03.03.97
694 *********calculate cooling (Fe2_large_cool) and output fecool from Cloudy 01.29.97god
695 *********and cooling derivative (ddT_Fe2_large_cool)
696 ************ this program for 371 level iron model 12/14/1995
697 ************ this program for 371 level iron model 1/11/1996
698 ************ this program for 371 level iron model 3/21/1996
699 ************ this program without La 3/27/1996
700 ************ this program for 371 level iron model 4/9/1996
701 ************ includes:FeIICollRatesBoltzmann;cooling;overlapping in lines */
702void FeIILevelPops( void )
703{
704        long int  i,
705                ipHi ,
706                ipLo ,
707                n;
708        /* used in test for non-positive level populations */
709        bool lgPopNeg;
710
711        double 
712          EnergyWN,
713          pop ,
714          sum;
715
716        int32 info;
717        int32 ipiv[NFE2LEVN];
718
719        DEBUG_ENTRY( "FeIILevelPops()" );
720
721        if( trace.lgTrace )
722        {
723                fprintf( ioQQQ,"   FeIILevelPops fe2 pops called\n");
724        }
725
726        /* FeII.lgSimulate was set true with simulate flag on atom FeII command,
727         * for bebugging without actually calling the routine */
728        if( FeII.lgSimulate )
729        {
730
731                return;
732        }
733
734        /* zero out some arrays */
735        for( n=0; n<FeII.nFeIILevel; ++n)
736        {
737                for( ipLo=0; ipLo<FeII.nFeIILevel; ++ipLo )
738                {
739                        Fe2CPump[ipLo][n] = 0.;
740                        Fe2LPump[ipLo][n] = 0.;
741                        Fe2A[ipLo][n] = 0.;
742                        xMatrix[ipLo][n] = 0.;
743                }
744        }
745
746        /* make the g-bar collision strengths and do linear interpolation on r-matrix data.
747         * this also sets Boltzmann factors for all levels,
748         * sets values of FeColl used below, but only if temp has changed */
749        FeIICollRatesBoltzmann();
750
751        /* pumping and spontantous decays */
752        for( n=0; n<FeII.nFeIILevel; ++n)
753        {
754                for( ipHi=n+1; ipHi<FeII.nFeIILevel; ++ipHi )
755                {
756                        /* continuum pumping rate from n to upper ipHi */
757                        Fe2CPump[n][ipHi] = Fe2LevN[ipHi][n].Emis->pump;
758
759                        /* continuum pumping rate from ipHi to lower n */
760                        Fe2CPump[ipHi][n] = Fe2LevN[ipHi][n].Emis->pump*
761                                Fe2LevN[ipHi][n].Hi->g/Fe2LevN[ipHi][n].Lo->g;
762
763                        /* spontaneous decays */
764                        Fe2A[ipHi][n] = Fe2LevN[ipHi][n].Emis->Aul*(Fe2LevN[ipHi][n].Emis->Pesc + Fe2LevN[ipHi][n].Emis->Pelec_esc +
765                          Fe2LevN[ipHi][n].Emis->Pdest);
766                }
767        }
768
769        /* now do pumping of atom by Lya */
770        FeIILyaPump();
771
772        /* **************************************************************************
773         *
774         * final rates into matrix
775         *
776         ***************************************************************************/
777
778        /* fill in xMatrix with matrix elements */
779        for( n=0; n<FeII.nFeIILevel; ++n)
780        {
781                /* all processes leaving level n going down*/
782                for( ipLo=0; ipLo<n; ++ipLo )
783                {
784                        xMatrix[n][n] = xMatrix[n][n] + Fe2CPump[n][ipLo] + Fe2LPump[n][ipLo]+ Fe2A[n][ipLo] +
785                                Fe2Coll[n][ipLo]*dense.eden;
786                }
787                /* all processes leaving level n going up*/
788                for( ipHi=n+1; ipHi<FeII.nFeIILevel; ++ipHi )
789                {
790                        xMatrix[n][n] = xMatrix[n][n] + Fe2CPump[n][ipHi] + Fe2LPump[n][ipHi] + Fe2Coll[n][ipHi]*dense.eden;
791                }
792                /* all processes entering level n from below*/
793                for( ipLo=0; ipLo<n; ++ipLo )
794                {
795                        xMatrix[ipLo][n] = xMatrix[ipLo][n] - Fe2CPump[ipLo][n] - Fe2LPump[ipLo][n] - Fe2Coll[ipLo][n]*dense.eden;
796                }
797                /* all processes entering level n from above*/
798                for( ipHi=n+1; ipHi<FeII.nFeIILevel; ++ipHi )
799                {
800                        xMatrix[ipHi][n] = xMatrix[ipHi][n] - Fe2CPump[ipHi][n] - Fe2LPump[ipHi][n] - Fe2Coll[ipHi][n]*dense.eden -
801                                Fe2A[ipHi][n];
802                }
803
804                /* the top row of the matrix is the sum of level populations */
805                xMatrix[n][0] = 1.0;
806        }
807
808        {
809                /* option to print out entire matrix */
810                enum {DEBUG_LOC=false};
811                if( DEBUG_LOC )
812                {
813                        /* print the matrices */
814                        for( n=0; n<FeII.nFeIILevel; ++n)
815                        {
816                                /*fprintf(ioQQQ,"\n");*/
817                                /* now print the matrix*/
818                                for( ipLo=0; ipLo<FeII.nFeIILevel; ++ipLo )
819                                {
820                                        fprintf(ioQQQ," %.1e", xMatrix[n][ipLo]);
821                                }
822                                fprintf(ioQQQ,"\n");
823                        }
824                }
825        }
826
827        /* define the Y Vector.  The oth element is the sum of all level populations
828         * adding up to the total population.  The remaining elements are the level
829         * balance equations adding up to zero */
830        yVector[0] = 1.0;
831        for( n=1; n < FeII.nFeIILevel; n++ )
832        {
833                yVector[n] = 0.0;
834        }
835
836        /* create the 1-yVector array that will save vector,
837         * this is the macro trick */
838#       ifdef AMAT
839#               undef AMAT
840#       endif
841#       define AMAT(I_,J_)      (*(amat+(I_)*FeII.nFeIILevel+(J_)))
842
843        /* copy current contents of xMatrix array over to special amat array*/
844        for( ipHi=0; ipHi < FeII.nFeIILevel; ipHi++ )
845        {
846                for( i=0; i < FeII.nFeIILevel; i++ )
847                {
848                        AMAT(i,ipHi) = xMatrix[i][ipHi];
849                }
850        }
851
852        info = 0;
853
854        /* do the linear algebra to find the level populations */
855        getrf_wrapper(FeII.nFeIILevel, FeII.nFeIILevel, amat, FeII.nFeIILevel, ipiv, &info);
856        getrs_wrapper('N', FeII.nFeIILevel, 1, amat, FeII.nFeIILevel, ipiv, yVector, FeII.nFeIILevel, &info);
857
858        if( info != 0 )
859        {
860                fprintf( ioQQQ, "DISASTER FeIILevelPops: dgetrs finds singular or ill-conditioned matrix\n" );
861                cdEXIT(EXIT_FAILURE);
862        }
863
864        /* yVector now contains the level populations */
865
866        /* this better be false after this loop - if not then non-positive level pops */
867        lgPopNeg = false;
868        /* copy all level pops over to Fe2LevelPop */
869        for( ipLo=0; ipLo < FeII.nFeIILevel; ipLo++ )
870        {
871                if(yVector[ipLo] < 0. )
872                {
873                        lgPopNeg = true;
874                        fprintf(ioQQQ,"PROBLEM FeIILevelPops finds non-positive level population, level is %ld pop is %g\n",
875                                ipLo , yVector[ipLo] );
876                }
877                /* this is now correct level population, cm^-3 */
878                Fe2LevelPop[ipLo] = yVector[ipLo] * dense.xIonDense[ipIRON][1];
879        }
880        if( lgPopNeg )
881        {
882                /* this is here to use the lgPopNeg value for something, if not here then
883                 * lint and some compilers will note that var is never used */
884                fprintf(ioQQQ , "PROBLEM FeIILevelPops exits with negative level populations.\n");
885        }
886
887        /* >>chng 06 jun 24, make sure remainder of populations up through max
888         * limit are zero - this makes safe the case where the number
889         * of levels actually computed has been trimmed down from largest
890         * possible number of levels, for instance, in cool gas */
891        for( ipLo=FeII.nFeIILevel; ipLo < FeII.nFeIILevelAlloc; ++ipLo )
892        {
893                Fe2LevelPop[ipLo] = 0.;
894        }
895
896        /* now set line opacities, intensities, and level populations
897         * >>chng 06 jun ipLo should go up to FeII.nFeIILevel-1 since this
898         * is the largest lower level with non-zero population */
899        for( ipLo=0; ipLo < (FeII.nFeIILevel - 1); ipLo++ )
900        {
901                /* >>chng 06 jun 24, upper level should go to limit
902                 * of all that were allocated */
903                /*for( ipHi=ipLo+1; ipHi < FeII.nFeIILevel; ipHi++ )*/
904                for( ipHi=ipLo+1; ipHi < FeII.nFeIILevelAlloc; ipHi++ )
905                {
906                        /* >>chng 06 jun 24, in all of these the product
907                         * yVector[ipHi]*dense.xIonDense[ipIRON][1] has been replaced
908                         * with Fe2LevelPop[ipLo] - should always have been this way,
909                         * and saves a mult */
910                        Fe2LevN[ipHi][ipLo].Emis->PopOpc = (Fe2LevelPop[ipLo] -
911                                Fe2LevelPop[ipHi]*Fe2LevN[ipHi][ipLo].Lo->g/F