/** @file photo.c * @brief Routines for the calculation of the photoionization source term. * * We use the Helmholtz approximation for the kernel in the photoionization * integral and we use the routines of the Poisson solver for the Helmholtz * equation (FISHPACK was modified to take a new inhomogeneous term into * account). */ #include #include #include #include "cdr.h" #include "interpol2.h" #include "mapper.h" #include "parameters.h" #include "photo.h" #include "poisson.h" #include "proto.h" #include "rz_array.h" #include "species.h" void photo_copy (mapper_t *mapper, grid_t *source, grid_t *target, int ir, int iz, int itheta); void photo_coarsen (mapper_t *mapper, grid_t *source, grid_t *target, int ir, int iz, int itheta); int photo_interpol_set (mapper_t *mapper, grid_t *source, interpol_t *interpol, int pr, int pz, int itheta); void photo_interpol (mapper_t *mapper, grid_t *source, grid_t *target, interpol_t *interpol, int ir, int iz, int itheta); photo_term_t *photo_terms = NULL; mapper_t photo_mapper = {&interpol_bilin, photo_coarsen, photo_copy, photo_interpol_set, photo_interpol}; mapper_t *photo_mappers[] = {&photo_mapper, NULL}; pois_problem_t *pois_photo_1; pois_problem_t *pois_photo_2; extern pois_problem_t *pois_electrostatic; /** @brief Initializes photoionization QQQQ? */ void photo_init () { /* Since it makes no sense, we use photo_extra_levels < 0 to say that the conditions for the photoionization are the same as for the electrostatic problem. This is needed for backwards compatibility. */ if (extra_photo_levels < 0) { pois_photo_1 = pois_electrostatic; pois_photo_2 = pois_electrostatic; return; } /* Two different sets of refinement criteria for the two photoionization terms. If extra_photo_levels_2 < 0, then pois_photo_2 = pois_photo_1 */ pois_photo_1 = xmalloc (sizeof (pois_problem_t)); pois_photo_1->max_level = photo_max_level; pois_photo_1->extra_levels = extra_photo_levels; pois_photo_1->max_error = photo_max_error; pois_photo_1->bnd_right = photo_bnd_right; pois_photo_1->bnd_top = photo_bnd_top; pois_photo_1->bnd_bottom = photo_bnd_bottom; if (extra_photo_levels_2 < 0) { pois_photo_2 = pois_photo_1; return; } pois_photo_2 = xmalloc (sizeof (pois_problem_t)); pois_photo_2->max_level = photo_max_level_2; pois_photo_2->extra_levels = extra_photo_levels_2; pois_photo_2->max_error = photo_max_error_2; pois_photo_2->bnd_right = photo_bnd_right_2; pois_photo_2->bnd_top = photo_bnd_top_2; pois_photo_2->bnd_bottom = photo_bnd_bottom_2; } /** @brief Copies the photoionization QQQQ? */ void photo_copy (mapper_t *mapper, grid_t *source, grid_t *target, int ir, int iz, int itheta) { cdr_grid_t *cdr; pois_grid_t *pois; cdr = (cdr_grid_t*) target; pois = (pois_grid_t*) source; RZT (cdr->photo, ir, iz, itheta) = RZ (pois->phi, ir, iz); } /** @brief Coarsens the photoionization QQQQ? */ void photo_coarsen (mapper_t *mapper, grid_t *source, grid_t *target, int ir, int iz, int itheta) { cdr_grid_t *cdr; pois_grid_t *pois; int level_diff, z, r; cdr = (cdr_grid_t*) target; pois = (pois_grid_t*) source; level_diff = pois->level - cdr->level; z = (iz << level_diff) + (1 << (level_diff - 1)); r = (ir << level_diff) + (1 << (level_diff - 1)); if (grid_contains (source, r, z, GRID_INSIDE) && grid_contains (source, r - 1, z, GRID_INSIDE) && grid_contains (source, r, z - 1, GRID_INSIDE) && grid_contains (source, r - 1, z - 1, GRID_INSIDE)) { RZT (cdr->photo, ir, iz, itheta) = 0.25 * (RZ (pois->phi, r, z) + RZ (pois->phi, r - 1, z) + RZ (pois->phi, r, z - 1) + RZ (pois->phi, r - 1, z - 1)); } } /** @brief photo_interpol_set QQQQ? */ int photo_interpol_set (mapper_t *mapper, grid_t *source, interpol_t *interpol, int pr, int pz, int itheta) { pois_grid_t *pois; pois = (pois_grid_t*) source; interpol_set_stencil (interpol, r_at (pr, pois->level), z_at (pz, pois->level), RZ (pois->phi, pr, pz), RZ (pois->phi, pr, pz + 1), RZ (pois->phi, pr + 1, pz), RZ (pois->phi, pr + 1, pz + 1)); return TRUE; } /** @brief photo_interpol QQQQ? */ void photo_interpol (mapper_t *mapper, grid_t *source, grid_t *target, interpol_t *interpol, int ir, int iz, int itheta) { double r, z; cdr_grid_t *cdr; cdr = (cdr_grid_t *) target; r = r_at (ir, cdr->level); z = z_at (iz, cdr->level); RZT (cdr->photo, ir, iz, itheta) = interpol_apply (interpol, r, z); } /** @brief Registers a photoionization term with given @a A and @a lambda. */ void photo_register (double A, double lambda) { photo_term_t *t; t = (photo_term_t *) xmalloc (sizeof (photo_term_t)); t->A = A; t->lambda = lambda; t->next = photo_terms; photo_terms = t; } /** @brief Copies a list of photo terms into @a *dest. */ void photo_copy_list (photo_term_t *src, photo_term_t **dest) { photo_term_t *ptr_term; photo_term_t *p = NULL, *p1; *dest = NULL; while (src) { p1 = (photo_term_t *) xmalloc (sizeof (photo_term_t)); if (p) { p->next = p1; } else { *dest = p1; } p = p1; p->A = src->A; p->lambda = src->lambda; src = src->next; } if (p) p->next = NULL; } /** @brief Unregisters all the photoionization terms and * frees the allocated space. */ void photo_unregister_all (void) { photo_term_t *t, *next; for (t = photo_terms; t; t = next) { next = t->next; free (t); } photo_terms = NULL; } /** @brief Transforms back the photoionization calculation into real space. */ void photo_dft_r (cdr_grid_t *grid, int sign) { cdr_grid_t *leaf; debug (3, "photo_dft_r(" grid_printf_str ", %d)\n", grid_printf_args(grid), sign); iter_childs (grid, leaf) { photo_dft_r (leaf, sign); } dft_transform (grid->photo, grid->photo, sign); } /** @brief Copies the derivative of the ion density into cdr->charge * * The ion density, which is supposed to be at this point, the impact * ionization) into cdr->charge, which will be used as the source for * the Poisson/Helmholtz solver. */ void photo_copy_source (cdr_grid_t *grid) { int ir, iz, itheta; debug(3, "photo_copy_source (" grid_printf_str ")\n", grid_printf_args(grid)); #pragma omp parallel { #pragma omp for private (ir, iz) iter_grid_3d_n (grid, ir, iz, itheta, 2) { RZT (grid->charge, ir, iz, itheta) = RZT (grid->d_dens[ions], ir, iz, itheta) / grid->ntheta; } } } /** @brief Recursive version of @a photo_copy_source. */ mk_recursive (photo_copy_source, cdr_grid_t) /** @brief Once a photoionization term is computed, we add it to d_dens. * * Note that we already copied the contents of d_dens[ions] into * charge, so when we solve again the Helholtz equation, the source * is still the same. */ void photo_add_term (photo_term_t *term, cdr_grid_t *cdr) { int s, ir, iz, itheta; int updated[2] = {electrons, photo_ions}; debug (3, "photo_add_term (" photo_printf_str ", " grid_printf_str ")\n", photo_printf_args(term), grid_printf_args(cdr)); #pragma omp parallel { #pragma omp for private (ir, iz, s) iter_grid_3d_n (cdr, ir, iz, itheta, 2) { for (s = 0; s < 2; s++) { RZT (cdr->d_dens[updated[s]], ir, iz, itheta) += term->A * RZT (cdr->photo, ir, iz, itheta); } } } } /** @brief ...and the recursive version of @a photo_add_term */ void photo_add_term_r (photo_term_t *term, cdr_grid_t *cdr) { cdr_grid_t *child; photo_add_term (term, cdr); iter_childs (cdr, child) { photo_add_term_r (term, child); } } /** @brief photo_calc_term QQQQ */ pois_grid_t ** photo_calc_term (photo_term_t *term, cdr_grid_t *cdr, int i) { /* Call to the generic Poisson/Helmholtz solver */ if (i == 1) { return pois_gen_solve_a (cdr, pois_photo_2, photo_mappers, term->lambda); } else { return pois_gen_solve_a (cdr, pois_photo_1, photo_mappers, term->lambda); } } /** @brief Calculates the photoionization and adds it to the derivatives * of the species densities. */ void photo_calc (photo_term_t *terms, cdr_grid_t *cdr) { pois_grid_t **pois_modes; photo_term_t *term; // photo_term_t *ptr_term; photo_copy_source_r (cdr); if (cdr->ntheta != 1) cdr_dft_charge_r (cdr, 1); // for (ptr_term = terms; ptr_term != NULL; ptr_term = ptr_term->next) { // printf("terms: A=%g lambda=%g ptr=%d\n", ptr_term->A, ptr_term->lambda, (int)ptr_term->next); // } int j = 0; for (term = terms; term; term = term->next ) { int i; pois_modes = photo_calc_term (term, cdr, j); j++; if (cdr->ntheta != 1) photo_dft_r (cdr, -1); photo_add_term_r (term, cdr); debug (3, "photo_calc (" photo_printf_str ", " grid_printf_str ")\n", photo_printf_args(term), grid_printf_args(cdr)); /* Free the allocated memory. */ for (i = 0; i < max_ntheta; i++) { pois_free_r (pois_modes[i]); } free (pois_modes); } } /** @brief Loads a photoionization file, consisting in a series of rows with @a A and @a lambda. */ void photo_load_file (char *fname) { FILE *fp; double A, lambda; int c; int i; printf("\n"); printf ("Loading photoionization data from `%s'...\n", fname); printf("\n"); fp = fopen (fname, "r"); if (NULL == fp) { warning ("Unable to open photoionization file `%s'\n", fname); exit (-1); return; } i=0; do { c = fscanf (fp, "%lf %lf", &A, &lambda); if (c != 2) { break; } printf ("Registring photoionization term A = %.5g, lambda = %.4g\n", A, lambda); photo_register (A, lambda); i++; } while (TRUE); printf("\n"); fclose (fp); }