|
new file 100644
|
|
|
/** @file cstream.c
|
|
|
* @brief General initialization/termination functions.
|
|
|
*/
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
#include <stdio.h>
|
|
|
#include <math.h>
|
|
|
#include <time.h>
|
|
|
#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;
|