/** @file cstream.c * @brief General initialization/termination functions. */ #include #include #include #include #define ALLOC_PARAMS #include "cstream.h" #include "parameters.h" #include "proto.h" #include "species.h" double E_x, E_y, E_z; /**< For time-dependent electric fields E_x, E_y, E_z there is a difference between E0 (the highest field) and E (the field at a given time). */ extern double pois_inhom_fixed_q_t; /**< The same is true for time-dependent fixed charges (useful to model the charging of a cloud in modeling sprites. */ double *dr, *dz, dtheta; /**< dr[i] and dz[i] represent the grid size at level @a i. Note that @a i can go from -extra_pois_levels...max_levels. */ double *dr_start, *dz_start; double *w2k, *wk; /**< w2k[k] = 2 / dtheta^2 (1 - cos(k dtheta)). This numbers appear due to the finite-differences derivatives of a Fourier transform. If we would make a continuous fourier transform, they will approach k^2. */ char *invok_name = "cstream"; /**< Name of this program. */ const double twopi = 6.283185307179586477L; /**< \f$ 2 \times \pi \f$. */ const double invfourpi = 0.079577471545947667884441882L; /**< \f$ 1 / (4 \pi )\f$ */ const double invpi32 = 0.179587122125166561689081L; /**< \f$ 1 / (\pi ^ {3/2} )\f$ */ /** @brief Initializes the parameters. * * The global parameters will get their default values. * These values correspond with the values in input/defaults.cfg */ void init_parameters(void) { debug (1, "entry init_parameters\n"); /* An identification name for this run */ prog_id = "example"; /* Output directory. */ output_dir = "output/"; /* Kinetics input file */ kin_input = "input/kinetic_example.cfg"; /* If restart is TRUE, the simulation will continue with data from a previous run */ restart = 0; /* If restart is TRUE, the name of the file with data from previous run, otherwise empty */ load_file = ""; /* Time interval for output to be written to disk */ output_dt = 0.100; /* Output of the Poisson grids, including the potential? */ pois_output = 0; /* Margin outside the grids in the output of the cdr equation */ cdr_output_margin = 0; /* Margin outside the grids in the output of the poisson equation */ pois_output_margin = 1; /* If the time steps are smaller than this number, the program issues a warning */ warn_min_timestep = 1e-06; /* Maximum disk space, in Mb, to use */ max_disk_space_mb = 1048576; /* Number of R and Z gridpoints at level 0 */ gridpoints_r = 600; gridpoints_z = 600; /* Number of azimuthal gridcells and modes */ max_ntheta = 1; /* Initial and end time */ start_t = 0.0; end_t = 0.12; /* Attempted timestep. The actual timestep may be larger */ attempt_dt = 50.0; /* Extra levels for the Poisson solver */ extra_pois_levels = 2; /* Maximum level of refinement. Use a big number here */ max_levels = 64; /* Error threshold that leads to refinement in the Poisson code. */ pois_max_error = 0.001; /* Maximum level of refinement in the Poisson equation. */ pois_max_level = 3; /* Extra levels for the photo-ionization solver */ extra_photo_levels = -1; /* Maximum level of refinement in the photo-ionization solver. */ photo_max_level = 4; /* Error threshold that leads to refinement in the photo-ionization code. */ photo_max_error = 0.01; /* Photo-ionization boundary condition at r = L_r, z = 0, z = L_z. 1 for Hom. Neumann, -1 for Hom. Dirichlet */ photo_bnd_right = -1; photo_bnd_bottom = -1; photo_bnd_top = -1; /* Extra levels for the photo-ionization solver */ extra_photo_levels_2 = -1; /* Maximum level of refinement in the photo-ionization solver. */ photo_max_level_2 = 4; /* Error threshold that leads to refinement in the photo-ionization code. */ photo_max_error_2 = 0.01; /* Photo-ionization boundary condition at r = L_r, z = 0, z = L_z. 1 for Hom. Neumann, -1 for Hom. Dirichlet */ photo_bnd_right_2 = -1; photo_bnd_bottom_2 = -1; photo_bnd_top_2 = -1; /* Particles boundary condition at z = 0, z = L_z, r = L_r. 1 for Hom. Neumann, -1 for Hom. Dirichlet */ cdr_bnd_bottom = 1; cdr_bnd_top = 1; cdr_bnd_right = 1; /* Potential boundary condition at r = L_r, z = 0, z = L_z. 1 for Hom. Neumann, -1 for Hom. Dirichlet */ pois_bnd_right = -1; pois_bnd_bottom = -1; pois_bnd_top = -1; /* Maximum advection and diffusion Courant number */ nu_a = 0.2; nu_d = 0.2; /* Maximum ratio of dt/relaxation time */ nu_rt = 0.2; /* Maximum ratio of change of the densities (set to a very large number to ignore) */ nu_f = 1e+20; /* Refinement threshold for the electric field */ ref_threshold_eabs = 0.2; /* Maximum refinement level reached through ref_threshold_eabs */ ref_level_eabs = 4; /* Refinement threshold for the curvature of the charge, densities */ ref_threshold_charge = 0.004; ref_threshold_dens = 0.004; /* Refinement threshold for the densities in the leading edge */ ref_threshold_edge = 10000.0; /* r-length and z-length of the minimal refinement area in the cdr equation */ cdr_brick_dr = 8; cdr_brick_dz = 8; /* Maximum level of refinement in the Fluid equation. */ cdr_max_level = 3; /* Interpolation method for the grid interior, and grid boundaries (0=zero_masses, 1=quadratic_masses [default], 2=wackers_masses, 3=quadlog */ cdr_interp_in = 1; cdr_interp_bnd = 1; /* Length in r and z of the complete domain */ L_r = 13044.0; L_z = 13044.0; /* Isotropic difussion coefficient */ diffusion_coeff = 0.1; /* Whether the code includes photoionization or not */ has_photoionization = 1; /* The name of a file from which we can read the photoionization parameters */ photoionization_file = "input/air760torr.photo"; /* Rate of dissociative attachment */ attachment_rate = 0.0; /* E0 in the exp(-E0/E) factor in the attachment expression. */ attachment_E0 = 0.0; /* x-, y- and z-component of the external electric field */ E0_x = 0.0; E0_y = 0.0; E0_z = -0.06; /* Rise time of the electric field (0 for instantaneous rise) */ rise_time = 0.0; /* Time to switch off the electric field (0.0 means never) */ off_time = 0.0; /* x-, y- and z-width of the initial seed */ seed_sigma_x = 0.0; seed_sigma_y = 0.0; seed_sigma_z = 0.0; /* Number of electrons in the initial seed */ seed_N = 0.0; /* Initial at z=0 densities of electrons and ions */ background_ionization = 0.0; /* Length of exponential increase of the pre-ionization (for atmospherical models) */ background_increase_length = 0.0; /* Use the point-plane geometry? */ pois_inhom = 1; /* Number of mirror charges to use */ pois_inhom_reflections = 4; /* Length and radius of the needle */ needle_length = 2500.0; needle_radius = 400.0; /* If nonzero, the charge is fixed, not floating (simulation of charged clouds close to the earth surface) */ pois_inhom_fixed_q = 0.0; /* Constant ionization rate */ constant_source = 0.0; /* Initial perturbation to the axisymmetric configuration */ perturb_epsilon = 0.0; /* Perturb only modes up to perturb_max_k (large number to perturb all) */ perturb_max_k = 1024; /* 1 if the sprite module is activated, 0 otherwise */ sprite_module = 0; /* Lenght of exponential decay of the density w/r to altitude */ dens_decay_len = 0.0; /* Density at z = 0 */ sprite_dens_0 = 0.0; /* Quenching density */ sprite_dens_q = 0.0; /* Sign of the sprite head that we are following (the other will not be reliable */ sprite_sign = -1; debug (1, "exit init_parameters\n"); } int n, i; /** @brief Initializes the grid sizes. The parameters dr_root and dz_root * specify the grid size at level 0 (dz_root = dz[0], dr_root[0] = dr[0]) */ static void init_gridsizes_a (void) { int n, i; double root_dr, root_dz; root_dr = L_r / gridpoints_r; root_dz = L_z / gridpoints_z; n = max_levels + extra_pois_levels + 1; dr_start = (double *) xmalloc(sizeof(double) * n); dz_start = (double *) xmalloc(sizeof(double) * n); dr = dr_start + extra_pois_levels; dz = dz_start + extra_pois_levels; dr_start[0] = root_dr * (1 << extra_pois_levels); dz_start[0] = root_dz * (1 << extra_pois_levels); for (i = 1; i < n; i++) { dr_start[i] = dr_start[i - 1] / 2.0; dz_start[i] = dz_start[i - 1] / 2.0; debug (3, "dr[%d] = %e\n", i - extra_pois_levels, dr[i - extra_pois_levels]); debug (3, "dz[%d] = %e\n", i - extra_pois_levels, dz[i - extra_pois_levels]); } dtheta = twopi / max_ntheta; } /** @brief Initializes the vector of wk's for the Helmholtz equation. */ static void init_wk_a (void) { int k; double twobydtheta2; debug (2, "init_w2k_a()\n"); w2k = (double*) xmalloc (sizeof(double) * max_ntheta); wk = (double*) xmalloc (sizeof(double) * max_ntheta); twobydtheta2 = 2. / (dtheta * dtheta); for (k = 0; k < max_ntheta / 2 + (max_ntheta % 2); k++) { double w2k_ = twobydtheta2 * (1 - cos (k * dtheta)); double re_wk, im_wk; re_wk = (cos (k * dtheta) - 1) / dtheta; im_wk = sin (k * dtheta) / dtheta; wk[k] = re_wk; w2k[k]= w2k_; if (k != 0) { wk[max_ntheta - k] = im_wk; w2k[max_ntheta - k] = w2k_; } } if ((max_ntheta % 2) == 0) { w2k[max_ntheta / 2] = twobydtheta2 * (1 - cos (k * dtheta)); wk[max_ntheta / 2] = (cos (k * dtheta) - 1) / dtheta; } assert (wk[0] == 0.); } /** @brief Frees the space allocated for dr and dz. */ static void free_gridsizes (void) { free (dr_start); free (dz_start); } /** @brief Frees the allocated space for \f$ \lambda \f$. */ static void free_wk (void) { free (wk); free (w2k); } /** @brief Here all the initialization calls. */ void cstream_init (void) { /* For the perturbations, it is better to start with different seeds at each run. */ srand (time (0)); init_gridsizes_a (); init_wk_a (); react_init (); cdr_init (); pois_init (); photo_init (); if (has_photoionization) { printf("Photoionization\n"); photo_load_file (photoionization_file); } else { printf("No photoionization\n"); } if (sprite_module) spr_init (); } /** @brief Here all the cleaning and finishing calls. */ void cstream_end (void) { photo_unregister_all (); free_gridsizes (); free_wk (); cdr_end (); } /** @brief When we use a time-varying external field, we update the field * components at each timesteps. */ void cstream_set_field_at_time (double t) { static int rise_reached = FALSE, off_reached = FALSE; double factor; if (rise_reached && off_reached) return; if (t >= rise_time) { rise_reached = TRUE; } if (t >= off_time || off_time == 0.0) { off_reached = TRUE; } /* We make sure that we never use a field _larger_ than E0 and that if rise_time is 0 we do not calculate t / 0.0. */ factor = t >= rise_time? 1.0: (t / rise_time); factor = (off_time > 0.0 && t >= off_time)? 0.0: factor; E_x = factor * E0_x; E_y = factor * E0_y; E_z = factor * E0_z; if (pois_inhom_fixed_q != 0.0) { pois_inhom_fixed_q_t = factor * pois_inhom_fixed_q; } return; } /** @brief Functions for a constant external electric field given in (x, y, z) components. */ double e0_r (double r, double z, double theta) { return E_x * cos (theta) + E_y * sin (theta); } /** @brief Functions for a constant external electric field given in (x, y, z) components. */ double e0_z (double r, double z, double theta) { return E_z; } /** @brief Functions for a constant external electric field given in (x, y, z) components. */ double e0_theta (double r, double z, double theta) { return -E_x * cos (theta) + E_y * sin (theta); } /** Initializes the component of the external electric field in \f$r\f$-direction. */ decl_field_comp(r) = e0_r; /** Initializes the component of the external electric field in \f$z\f$-direction. */ decl_field_comp(z) = e0_z; /** Initializes the component of the external electric field in \f$\theta\f$-direction. */ decl_field_comp(theta) = e0_theta;