/* ------- begin -------------------------- readConvergence.c --- */ void readConvergence(void) { /* This is a self-contained function to read the convergence matrix, written by RH. */ const char routineName[] = "readConvergence"; char *atmosID; int ncid, ncid_mpi, nx, ny; size_t attr_size; hid_t plist; H5T_class_t type_class; mpi.rh_converged = matrix_int(mpi.nx, mpi.ny); /* --- Open the inputdata file --- */ if (( plist = H5Pcreate(H5P_FILE_ACCESS )) < 0) HERR(routineName); if (( H5Pset_fapl_mpio(plist, mpi.comm, mpi.info) ) < 0) HERR(routineName); if (( ncid = H5Fopen(INPUTDATA_FILE, H5F_ACC_RDWR, plist) ) < 0) HERR(routineName); if (( H5Pclose(plist) ) < 0) HERR(routineName); /* Get ncid of the MPI group */ if (( ncid_mpi = H5Gopen(ncid, "mpi", H5P_DEFAULT) ) < 0) HERR(routineName); /* --- Consistency checks --- */ /* Check that atmosID is the same */ if (( H5LTget_attribute_info(ncid, "/", "atmosID", NULL, &type_class, &attr_size) ) < 0) HERR(routineName); atmosID = (char *) malloc(attr_size + 1); if (( H5LTget_attribute_string(ncid, "/", "atmosID", atmosID) ) < 0) HERR(routineName); if (!strstr(atmosID, atmos.ID)) { sprintf(messageStr, "Indata file was calculated for different atmosphere (%s) than current", atmosID); Error(WARNING, routineName, messageStr); } free(atmosID); /* Check that dimension sizes match */ if (( H5LTget_attribute_int(ncid, "/", "nx", &nx) ) < 0) HERR(routineName); if (nx != mpi.nx) { sprintf(messageStr, "Number of x points mismatch: expected %d, found %d.", mpi.nx, (int)nx); Error(WARNING, routineName, messageStr); } if (( H5LTget_attribute_int(ncid, "/", "ny", &ny) ) < 0) HERR(routineName); if (ny != mpi.ny) { sprintf(messageStr, "Number of y points mismatch: expected %d, found %d.", mpi.ny, (int)ny); Error(WARNING, routineName, messageStr); } /* --- Read variable --- */ if (( H5LTread_dataset_int(ncid_mpi, CONV_NAME, mpi.rh_converged[0]) ) < 0) HERR(routineName); /* --- Close inputdata file --- */ if (( H5Gclose(ncid_mpi) ) < 0) HERR(routineName); if (( H5Fclose(ncid) ) < 0) HERR(routineName); return; }
void initSolution(Atom *atom, Molecule *molecule) { const char routineName[] = "initSolution"; register int k, i, ij, nspect, mu, n, kr, nact; char permission[3]; bool_t result, openJfile; int la, j, niter, Nsr, Nplane, index, status, oflag; double gijk, wla, twohnu3_c2, hc_k, twoc, fourPI, *J, *J20; ActiveSet *as; AtomicLine *line; AtomicContinuum *continuum; XDR xdrs; double cswitch; int to_obs,lamuk,sign,ncoef,ilow,Nlamu,lamu; long int idx, lc; double *lambda,fac,lambda_prv,lambda_gas,lambda_nxt,dl,frac,lag; FILE *fp; getCPU(2, TIME_START, NULL); /* Collisional-radiative switching ? */ if (input.crsw != 0.0) cswitch = input.crsw_ini; else cswitch = 1.0; /* --- Allocate space for angle-averaged mean intensity -- -------- */ if (!input.limit_memory) spectrum.J = matrix_double(spectrum.Nspect, atmos.Nspace); /* --- If we do background polarization we need space for the anisotropy -- -------------- */ if (input.backgr_pol) spectrum.J20 = matrix_double(spectrum.Nspect, atmos.Nspace); /* --- For the PRD angle approximation we need to store J in the gas frame, -------- */ if (input.PRD_angle_dep == PRD_ANGLE_APPROX && atmos.NPRDactive > 0) { spectrum.Jgas = matrix_double(spectrum.Nspect, atmos.Nspace); spectrum.v_los = matrix_double( atmos.Nrays, atmos.Nspace); /* Calculate line of sight velocity */ for (mu = 0; mu < atmos.Nrays; mu++) { for (k = 0; k < atmos.Nspace; k++) { spectrum.v_los[mu][k] = vproject(k, mu); // / vbroad[k]; } } /* precompute prd_rho interpolation coefficients if requested */ if (!input.prdh_limit_mem) { for (nact = 0; nact < atmos.Nactiveatom; nact++) { atom = atmos.activeatoms[nact]; for (kr = 0; kr < atom->Nline; kr++) { line = &atom->line[kr]; if (line->PRD) { Nlamu = 2*atmos.Nrays * line->Nlambda; line->frac = matrix_double(Nlamu, atmos.Nspace); line->id0 = matrix_int(Nlamu, atmos.Nspace); line->id1 = matrix_int(Nlamu, atmos.Nspace); for (la = 0; la < line->Nlambda; la++) { for (mu = 0; mu < atmos.Nrays; mu++) { for (to_obs = 0; to_obs <= 1; to_obs++) { sign = (to_obs) ? 1.0 : -1.0; lamu = 2*(atmos.Nrays*la + mu) + to_obs; for (k = 0; k < atmos.Nspace; k++) { // wavelength in local rest frame lag=line->lambda[la] * (1.+spectrum.v_los[mu][k]*sign/CLIGHT); if (lag <= line->lambda[0]) { // out of the lambda table, constant extrapolation line->frac[lamu][k]=0.0; line->id0[lamu][k]=0; line->id1[lamu][k]=1; } else if (lag >= line->lambda[line->Nlambda-1] ) { // out of the lambda table, constant extrapolation line->frac[lamu][k]=1.0; line->id0[lamu][k]=line->Nlambda-2; line->id1[lamu][k]=line->Nlambda-1; } else { // Locate index of line->lambda of point directly to the left of lag Locate(line->Nlambda,line->lambda,lag,&ilow); line->frac[lamu][k] = (lag-line->lambda[ilow])/ (line->lambda[ilow+1]-line->lambda[ilow]); line->id0[lamu][k]=ilow; line->id1[lamu][k]=ilow+1; } } } } } } } } } /* precompute Jgas interpolation coefficients if requested */ if (!input.prdh_limit_mem) { lambda = spectrum.lambda; /* --- keeps track of where to get indices and interpolation coefficients in spectrum.iprhh and spectrum.cprdh --- */ spectrum.nc= (int *) malloc( 2*atmos.Nrays*spectrum.Nspect*atmos.Nspace * sizeof(int)); for (la = 0; la < spectrum.Nspect; la++) { for (mu = 0; mu < atmos.Nrays; mu++) { for (to_obs = 0; to_obs <= 1; to_obs++) { sign = (to_obs) ? 1.0 : -1.0; for (k = 0; k < atmos.Nspace; k++) { lamuk = la * (atmos.Nrays*2*atmos.Nspace) + mu * (2*atmos.Nspace) + to_obs * (atmos.Nspace) + k ; ncoef=0; // previous, current and next wavelength shifted to gas rest frame fac = (1.+spectrum.v_los[mu][k]*sign/CLIGHT); lambda_prv = lambda[ MAX(la-1,0) ]*fac; lambda_gas = lambda[ la ]*fac; lambda_nxt = lambda[ MIN(la+1,spectrum.Nspect-1) ]*fac; // do lambda_prv and lambda_gas bracket lambda points? if (lambda_prv != lambda_gas) { dl= lambda_gas - lambda_prv; for (idx = 0; idx < spectrum.Nspect ; idx++) { if (lambda[idx] > lambda_prv && lambda[idx] <= lambda_gas) ncoef=ncoef+1; } } else { // edge case, use constant extrapolation for lambda[idx]<lambda gas for (idx = 0; idx < spectrum.Nspect ; idx++) { if (lambda[idx] <= lambda_gas) ncoef=ncoef+1; } } // do lambda_gas and lambda_nxt bracket lambda points? if (lambda_gas != lambda_nxt) { dl= lambda_nxt - lambda_gas; for (idx = 0; idx < spectrum.Nspect ; idx++) { if (lambda[idx] > lambda_gas && lambda[idx] < lambda_nxt) ncoef=ncoef+1; } } else { // edge case, use constant extrapolation for lambda[idx]>lambda gas for (idx = 0; idx < spectrum.Nspect ; idx++) { if (lambda[idx] >= lambda_gas) ncoef=ncoef+1; } } /* --- number of point this lambda contributes to is computed as a difference --- */ if (lamuk == 0) { spectrum.nc[lamuk] = ncoef; } else { spectrum.nc[lamuk]=spectrum.nc[lamuk-1]+ncoef; } } // k } // to_obs } // mu } // la /* --- now we know the number of interpolation coefficients, it's stored in the last element of spectrum.nc, so allocate space --- */ idx=spectrum.nc[2*atmos.Nrays*spectrum.Nspect*atmos.Nspace-1]; spectrum.iprdh= (int *) malloc( idx * sizeof(int )); spectrum.cprdh= (double *) malloc( idx * sizeof(double)); /* --- Run through all lamuk points again, and now store indices to lambda array and the corresponding interpolation coefficients --- */ for (la = 0; la < spectrum.Nspect; la++) { for (mu = 0; mu < atmos.Nrays; mu++) { for (to_obs = 0; to_obs <= 1; to_obs++) { sign = (to_obs) ? 1.0 : -1.0; for (k = 0; k < atmos.Nspace; k++) { lamuk = la * (atmos.Nrays*2*atmos.Nspace) + mu * (2*atmos.Nspace) + to_obs * (atmos.Nspace) + k ; // starting index for storage for this lamuk point lc = (lamuk==0) ? 0 : spectrum.nc[lamuk-1]; // previous, current and next wavelength shifted to gas rest frame fac = (1.+spectrum.v_los[mu][k]*sign/CLIGHT); lambda_prv = lambda[ MAX(la-1,0) ]*fac; lambda_gas = lambda[ la ]*fac; lambda_nxt = lambda[ MIN(la+1,spectrum.Nspect-1) ]*fac; // do lambda_prv and lambda_gas bracket lambda points? if (lambda_prv != lambda_gas) { dl= lambda_gas - lambda_prv; for (idx = 0; idx < spectrum.Nspect ; idx++) { if (lambda[idx] > lambda_prv && lambda[idx] <= lambda_gas) { // bracketed point found spectrum.iprdh[lc]=idx; spectrum.cprdh[lc]=(lambda[idx]-lambda_prv)/dl; lc++; } } } else { // edge case, use constant extrapolation for lambda[idx]<lambda gas for (idx = 0; idx < spectrum.Nspect ; idx++) { if (lambda[idx] <= lambda_gas) { spectrum.iprdh[lc]=idx; spectrum.cprdh[lc]=1.0; lc++; } } } // do lambda_gas and lambda_nxt bracket lambda points? if (lambda_gas != lambda_nxt) { dl= lambda_nxt - lambda_gas; for (idx = 0; idx < spectrum.Nspect ; idx++) { if (lambda[idx] > lambda_gas && lambda[idx] < lambda_nxt) { // bracketed point found spectrum.iprdh[lc]=idx; spectrum.cprdh[lc]=1.0 - (lambda[idx]-lambda_gas)/dl; lc++; } } } else { // edge case, use constant extrapolation for lambda[idx]>lambda gas for (idx = 0; idx < spectrum.Nspect ; idx++) { if (lambda[idx] >= lambda_gas) { spectrum.iprdh[lc]=idx; spectrum.cprdh[lc]=1.0; lc++; } } } } // k } // to_obs } // mu } // la } //input.prdh_limit_mem if switch } // PRD_ANGLE_APPROX if switch /* --- Allocate space for the emergent intensity -- -------------- */ switch (topology) { case ONE_D_PLANE: spectrum.I = matrix_double(spectrum.Nspect, atmos.Nrays); if (atmos.Stokes || input.backgr_pol) { spectrum.Stokes_Q = matrix_double(spectrum.Nspect, atmos.Nrays); spectrum.Stokes_U = matrix_double(spectrum.Nspect, atmos.Nrays); spectrum.Stokes_V = matrix_double(spectrum.Nspect, atmos.Nrays); } break; case TWO_D_PLANE: Nsr = spectrum.Nspect * atmos.Nrays; spectrum.I = matrix_double(Nsr, atmos.N[0]); if (atmos.Stokes || input.backgr_pol) { spectrum.Stokes_Q = matrix_double(Nsr, atmos.N[0]); spectrum.Stokes_U = matrix_double(Nsr, atmos.N[0]); spectrum.Stokes_V = matrix_double(Nsr, atmos.N[0]); } break; case THREE_D_PLANE: spectrum.I = matrix_double(spectrum.Nspect * atmos.Nrays, atmos.N[0] * atmos.N[1]); if (atmos.Stokes || input.backgr_pol) { Nsr = spectrum.Nspect * atmos.Nrays; Nplane = atmos.N[0] * atmos.N[1]; spectrum.I = matrix_double(Nsr, Nplane); if (atmos.Stokes || input.backgr_pol) { spectrum.Stokes_Q = matrix_double(Nsr, Nplane); spectrum.Stokes_U = matrix_double(Nsr, Nplane); spectrum.Stokes_V = matrix_double(Nsr, Nplane); } } break; case SPHERICAL_SYMMETRIC: spectrum.I = matrix_double(spectrum.Nspect, atmos.Nrays); if (atmos.Stokes) { Error(ERROR_LEVEL_2, routineName, "Cannot do a full Stokes solution in spherical geometry"); } break; default: sprintf(messageStr, "Unknown topology (%d)", topology); Error(ERROR_LEVEL_2, routineName, messageStr); } /* --- Read angle-averaged intensity from previous run if necessary, and open file for J in case option for limited memory is set */ spectrum.fd_J = -1; spectrum.fd_J20 = -1; oflag = 0; openJfile = FALSE; if (input.startJ == OLD_J) { if (spectrum.updateJ) { strcpy(permission, "r+"); oflag |= O_RDWR; } else { strcpy(permission, "r"); oflag |= O_RDONLY; } openJfile = TRUE; } else { if (input.limit_memory) { strcpy(permission, "w+"); oflag |= (O_RDWR | O_CREAT); openJfile = TRUE; } } if (openJfile) { if ((spectrum.fd_J = open(input.JFile, oflag, PERMISSIONS)) == -1) { sprintf(messageStr, "Unable to open input file %s with permission %s", input.JFile, permission); Error(ERROR_LEVEL_2, routineName, messageStr); } if (input.backgr_pol) { if ((spectrum.fd_J20 = open(J20_DOT_OUT, oflag, PERMISSIONS)) == -1) { sprintf(messageStr, "Unable to open input file %s with permission %s", J20_DOT_OUT, permission); Error(ERROR_LEVEL_2, routineName, messageStr); } } } if (input.limit_memory) { if (oflag & O_CREAT) { J = (double *) malloc(atmos.Nspace * sizeof(double)); /* --- Initialize J file with zeroes -- -------------- */ for (k = 0; k < atmos.Nspace; k++) J[k] = 0.0; for (nspect = 0; nspect < spectrum.Nspect; nspect++) writeJlambda(nspect, J); free(J); if (input.backgr_pol) { J20 = (double *) malloc(atmos.Nspace * sizeof(double)); for (k = 0; k < atmos.Nspace; k++) J20[k] = 0.0; for (nspect = 0; nspect < spectrum.Nspect; nspect++) writeJ20lambda(nspect, J20); free(J20); } } } else { if (input.startJ == OLD_J) { /* --- Fill matrix J with old values from previous run ----- -- */ for (nspect = 0; nspect < spectrum.Nspect; nspect++) readJlambda(nspect, spectrum.J[nspect]); close(spectrum.fd_J); spectrum.fd_J = -1; if (input.backgr_pol) { for (nspect = 0; nspect < spectrum.Nspect; nspect++) readJ20lambda(nspect, spectrum.J20[nspect]); close(spectrum.fd_J20); spectrum.fd_J20 = -1; } } /* --- Look for Jgas and read, otherwise use spectrum.J ----- -- */ if (atmos.NPRDactive > 0 && input.PRD_angle_dep == PRD_ANGLE_APPROX) { fp=fopen("Jgas.dat","r"); if (fp) { // file exists fclose(fp); readJgas(spectrum.Jgas); sprintf(messageStr, "Read spectrum.Jgas from file."); Error(MESSAGE, routineName, messageStr); } else { //file does not exist sprintf(messageStr, "Jgas.dat does not exist,setting spectrum.Jgas spectrum.J."); Error(WARNING, routineName, messageStr); for (k = 0; k < atmos.Nspace; k++) { for (nspect = 0; nspect < spectrum.Nspect; nspect++) { spectrum.Jgas[nspect][k]=spectrum.J[nspect][k]; } } } } } /* --- Need storage for angle-dependent specific intensities for angle-dependent PRD -- -------------- */ if (atmos.NPRDactive > 0 && input.PRD_angle_dep == PRD_ANGLE_DEP) { oflag = 0; if (input.startJ == OLD_J) { if (spectrum.updateJ) { strcpy(permission, "r+"); oflag |= O_RDWR; } else { strcpy(permission, "r"); oflag |= O_RDONLY; } } else { strcpy(permission, "w+"); oflag |= (O_RDWR | O_CREAT); } if ((spectrum.fd_Imu = open(IMU_FILENAME, oflag, PERMISSIONS)) == -1) { sprintf(messageStr, "Unable to open %s file %s with permission %s", (spectrum.updateJ) ? "update" : "input", IMU_FILENAME, permission); Error(ERROR_LEVEL_2, routineName, messageStr); } /* --- Fill the index list that keeps track of the location of intensity Imu in file spectrum.fd_Imu at wavelength corresponding to nspect. -- -------------- */ spectrum.PRDindex = (int *) malloc(spectrum.Nspect * sizeof(int)); index = 0; for (nspect = 0; nspect < spectrum.Nspect; nspect++) { if (containsPRDline(&spectrum.as[nspect])) { spectrum.PRDindex[nspect] = index; index++; } } } for (nact = 0; nact < atmos.Nactiveatom; nact++) { atom = atmos.activeatoms[nact]; /* --- Allocate memory for the rate equation matrix -- ---------- */ atom->Gamma = matrix_double(SQ(atom->Nlevel), atmos.Nspace); /* --- Initialize the mutex lock for the operator Gamma if there are more than one threads -- -------------- */ if (input.Nthreads > 0) { if ((status = pthread_mutex_init(&atom->Gamma_lock, NULL))) { sprintf(messageStr, "Unable to initialize mutex_lock, status = %d", status); Error(ERROR_LEVEL_2, routineName, messageStr); } } switch(atom->initial_solution) { case LTE_POPULATIONS: for (i = 0; i < atom->Nlevel; i++) { for (k = 0; k < atmos.Nspace; k++) atom->n[i][k] = atom->nstar[i][k]; } break; case ZERO_RADIATION: hc_k = (HPLANCK * CLIGHT) / (KBOLTZMANN * NM_TO_M); twoc = 2.0*CLIGHT / CUBE(NM_TO_M); fourPI = 4.0 * PI; initGammaAtom(atom,cswitch); /* --- Then add radiative contributions of active transitions -- */ for (nspect = 0; nspect < spectrum.Nspect; nspect++) { as = spectrum.as + nspect; for (n = 0; n < as->Nactiveatomrt[nact]; n++) { switch (as->art[nact][n].type) { case ATOMIC_LINE: line = as->art[nact][n].ptype.line; la = nspect - line->Nblue; i = line->i; j = line->j; ij = i*atom->Nlevel + j; if (la == 0) { for (k = 0; k < atmos.Nspace; k++) atom->Gamma[ij][k] += line->Aji; } break; case ATOMIC_CONTINUUM: continuum = as->art[nact][n].ptype.continuum; la = nspect - continuum->Nblue; i = continuum->i; j = continuum->j; ij = i*atom->Nlevel + j; wla = fourPI * getwlambda_cont(continuum, la) / continuum->lambda[la]; twohnu3_c2 = twoc / CUBE(continuum->lambda[la]); for (k = 0; k < atmos.Nspace; k++) { gijk = atom->nstar[i][k]/atom->nstar[j][k] * exp(-hc_k/(continuum->lambda[la] * atmos.T[k])); atom->Gamma[ij][k] += gijk * twohnu3_c2 * continuum->alpha[la]*wla; } break; default: break; } } } /* --- Solve statistical equilibrium equations -- ------------ */ statEquil(atom, (input.isum == -1) ? 0 : input.isum); break; case OLD_POPULATIONS: readPopulations(atom); break; default: ; break; } } /* --- Now the molecules that are active -- -------------- */ for (nact = 0; nact < atmos.Nactivemol; nact++) { molecule = atmos.activemols[nact]; /* --- Calculate the LTE vibration level populations here. They cannot be calculated yet in readMolecule since chemical equilibrium has to be established first -- -------------- */ for (i = 0; i < molecule->Nv; i++) { for (k = 0; k < atmos.Nspace; k++) molecule->nvstar[i][k] = molecule->n[k] * molecule->pfv[i][k] / molecule->pf[k]; } /* --- Allocate memory for the rate equation matrix -- ---------- */ molecule->Gamma = matrix_double(SQ(molecule->Nv), atmos.Nspace); /* --- Initialize the mutex lock for the operator Gamma if there are more than one thread -- -------------- */ if (input.Nthreads > 0) { if ((status = pthread_mutex_init(&molecule->Gamma_lock, NULL))) { sprintf(messageStr, "Unable to initialize mutex_lock, status = %d", status); Error(ERROR_LEVEL_2, routineName, messageStr); } } switch(molecule->initial_solution) { case LTE_POPULATIONS: for (i = 0; i < molecule->Nv; i++) { for (k = 0; k < atmos.Nspace; k++) molecule->nv[i][k] = molecule->nvstar[i][k]; } break; case OLD_POPULATIONS: readMolPops(molecule); break; default: ; } /* --- Calculate collisions for molecule (must be done here because rotation-vibration transitions are dominated by hydrogen and H2 collisions for which chemical equilibrium needs to be established first -- -------------- */ if (strstr(molecule->ID, "CO")) COcollisions(molecule); else { sprintf(messageStr, "Collisions for molecule %s not implemented\n", molecule->ID); Error(ERROR_LEVEL_2, routineName, messageStr); } } }