From 61b318f9aa1e637675b1b7349f475ebffe38862f Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 26 Jan 2021 10:19:59 -0600 Subject: [PATCH 01/93] Port forward Stokes output, originally via Abhishek Joshi --- src/debug_tools.c | 7 ++- src/io.c | 122 ++++++++++++++++++++++++++++++---------------- src/ipolarray.c | 49 +++++++++---------- src/ipolarray.h | 16 ++++++ src/main.c | 9 ++-- 5 files changed, 126 insertions(+), 77 deletions(-) diff --git a/src/debug_tools.c b/src/debug_tools.c index 923a0f1..53dc05d 100644 --- a/src/debug_tools.c +++ b/src/debug_tools.c @@ -4,9 +4,11 @@ * Printing and sanity checks for tetrads */ -#include "geometry.h" #include "decs.h" +#include "geometry.h" +#include "ipolarray.h" + // The world needed these // Maybe not in this form void print_matrix(char *name, double g[NDIM][NDIM]) @@ -105,9 +107,6 @@ void check_N(double complex N[NDIM][NDIM], /* check invariants */ double complex Nud[NDIM][NDIM]; - void complex_lower(double complex N[NDIM][NDIM], - double gcov[NDIM][NDIM], int low1, int low2, - double complex Nl[NDIM][NDIM]); complex_lower(N, gcov, 0, 1, Nud); for (i = 0; i < 4; i++) fprintf(stderr, "N: %d %g + i %g\n", i, creal(N[i][i]), diff --git a/src/io.c b/src/io.c index 86b9587..f6dea49 100644 --- a/src/io.c +++ b/src/io.c @@ -8,6 +8,7 @@ #include "decs.h" #include "coordinates.h" #include "geometry.h" +#include "ipolarray.h" #include "radiation.h" #include "par.h" #include "hdf5_utils.h" @@ -17,6 +18,7 @@ #include #include +#include void write_header(double scale, double cam[NDIM], double fovx, double fovy, Params *params); @@ -234,7 +236,7 @@ void dump(double image[], double imageS[], double taus[], * Given a path, dump a variable computed along that path into a file. * Note this is most definitely *not* thread-safe, so it gets called from an 'omp critical' * - * TODO output of I,Q,U,V, as parallel-transported to camera, along traces + * TODO Output of Q/U as locally parallel transported forward along the curve? */ void dump_var_along(int i, int j, int nstep, struct of_traj *traj, int nx, int ny, double scale, double cam[NDIM], double fovx, double fovy, Params *params) @@ -247,43 +249,59 @@ void dump_var_along(int i, int j, int nstep, struct of_traj *traj, int nx, int n } int nprims = 8; - double *prims = calloc(nprims*nstep, sizeof(double)); - - double *b = calloc(nstep, sizeof(double)); - double *ne = calloc(nstep, sizeof(double)); - double *thetae = calloc(nstep, sizeof(double)); - double *nu = calloc(nstep, sizeof(double)); - double *mu = calloc(nstep, sizeof(double)); - - double *dl = calloc(nstep, sizeof(double)); - double *X = calloc(NDIM*nstep, sizeof(double)); + // Admittedly confusing nomenclature: + // nstep = # of final step + // nsteps = # steps = nstep + 1 + int nsteps = nstep + 1; + double *prims = calloc(nprims*nsteps, sizeof(double)); + + double *b = calloc(nsteps, sizeof(double)); + double *ne = calloc(nsteps, sizeof(double)); + double *thetae = calloc(nsteps, sizeof(double)); + double *nu = calloc(nsteps, sizeof(double)); + double *mu = calloc(nsteps, sizeof(double)); + + double *dl = calloc(nsteps, sizeof(double)); + double *X = calloc(NDIM*nsteps, sizeof(double)); // Vectors aren't really needed. Put back in behind flag if it comes up - double *Kcon = calloc(NDIM*nstep, sizeof(double)); -// double *Kcov = calloc(NDIM*nstep, sizeof(double)); + double *Kcon = calloc(NDIM*nsteps, sizeof(double)); +// double *Kcov = calloc(NDIM*nsteps, sizeof(double)); - double *r = calloc(nstep, sizeof(double)); - double *th = calloc(nstep, sizeof(double)); - double *phi = calloc(nstep, sizeof(double)); + double *r = calloc(nsteps, sizeof(double)); + double *th = calloc(nsteps, sizeof(double)); + double *phi = calloc(nsteps, sizeof(double)); -// double *Ucon = calloc(NDIM*nstep, sizeof(double)); -// double *Ucov = calloc(NDIM*nstep, sizeof(double)); -// double *Bcon = calloc(NDIM*nstep, sizeof(double)); -// double *Bcov = calloc(NDIM*nstep, sizeof(double)); + double *j_unpol = calloc(nsteps, sizeof(double)); + double *k_unpol = calloc(nsteps, sizeof(double)); + double *I_unpol = calloc(nsteps, sizeof(double)); - // TODO NDIM and NSTOKES are not the same thing - double *j_inv = calloc(NDIM*nstep, sizeof(double)); - double *alpha_inv = calloc(NDIM*nstep, sizeof(double)); - double *rho_inv = calloc(NDIM*nstep, sizeof(double)); - double *unpol_inv = calloc(2*nstep, sizeof(double)); +// double *Ucon = calloc(NDIM*nsteps, sizeof(double)); +// double *Ucov = calloc(NDIM*nsteps, sizeof(double)); +// double *Bcon = calloc(NDIM*nsteps, sizeof(double)); +// double *Bcov = calloc(NDIM*nsteps, sizeof(double)); - for (int i=0; irotcam*M_PI/180.; + // Record Stokes parameters + double *stokes = calloc(NDIM*nsteps,sizeof(double)); + double *stokes_coordinate = calloc(NDIM*nsteps,sizeof(double)); + + for (int i=nstep; i > 0; --i) { + // Record ambient variables get_model_primitives(traj[i].X, &(prims[i*nprims])); double Ucont[NDIM], Ucovt[NDIM], Bcont[NDIM], Bcovt[NDIM]; get_model_fourv(traj[i].X, traj[i].Kcon, Ucont, Ucovt, Bcont, Bcovt); jar_calc(traj[i].X, traj[i].Kcon, &(j_inv[i*NDIM]), &(j_inv[i*NDIM+1]), &(j_inv[i*NDIM+2]), &(j_inv[i*NDIM+3]), &(alpha_inv[i*NDIM]), &(alpha_inv[i*NDIM+1]), &(alpha_inv[i*NDIM+2]), &(alpha_inv[i*NDIM+3]), &(rho_inv[i*NDIM+1]), &(rho_inv[i*NDIM+2]), &(rho_inv[i*NDIM+3]), params); - get_jkinv(traj[i].X, traj[i].Kcon, &(unpol_inv[i*2+0]), &(unpol_inv[i*2+1]), params); + get_jkinv(traj[i].X, traj[i].Kcon, &(j_unpol[i]), &(k_unpol[i]), params); b[i] = get_model_b(traj[i].X); ne[i] = get_model_ne(traj[i].X); @@ -291,17 +309,31 @@ void dump_var_along(int i, int j, int nstep, struct of_traj *traj, int nx, int n nu[i] = get_fluid_nu(traj[i].Kcon, Ucovt); mu[i] = get_bk_angle(traj[i].X, traj[i].Kcon, Ucovt, Bcont, Bcovt); + // Record trajectory dl[i] = traj[i].dl; MULOOP { X[i*NDIM+mu] = traj[i].X[mu]; Kcon[i*NDIM+mu] = traj[i].Kcon[mu]; } -// double Gcov[NDIM][NDIM]; -// gcov_func(traj[i].X, Gcov); -// lower(traj[i].Kcon, Gcov, &(Kcov[i*NDIM])); + double Gcov[NDIM][NDIM], Gcon[NDIM][NDIM]; + gcov_func(traj[i].X, Gcov); + gcon_func(Gcov, Gcon); +// lower(traj[i].Kcon, Gcov, &(Kcov[i*NDIM])); bl_coord(traj[i].X, &(r[i]), &(th[i])); phi[i] = traj[i].X[3]; + + // Integrate and record results + int flag = integrate_emission(&(traj[i]), 1, &Intensity, &Tau, &tauF, N_coord, params); + I_unpol[i] = Intensity; + project_N(traj[i-1].X, traj[i-1].Kcon, N_coord, + &(stokes[i*NDIM]), &(stokes[i*NDIM+1]), &(stokes[i*NDIM+2]), &(stokes[i*NDIM+3]), + 0); + any_tensor_to_stokes(N_coord, Gcov, + &(stokes_coordinate[i*NDIM]), &(stokes_coordinate[i*NDIM+1]), + &(stokes_coordinate[i*NDIM+2]), &(stokes_coordinate[i*NDIM+3])); + if (flag) printf("Flagged integration when tracing: %d\n", flag); + } hdf5_set_directory("/"); @@ -320,8 +352,8 @@ void dump_var_along(int i, int j, int nstep, struct of_traj *traj, int nx, int n hsize_t fdims_s[] = { nx, ny, params->maxnstep }; hsize_t chunk_s[] = { 1, 1, 200 }; hsize_t fstart_s[] = { i, j, 0 }; - hsize_t fcount_s[] = { 1, 1, nstep }; - hsize_t mdims_s[] = { 1, 1, nstep }; + hsize_t fcount_s[] = { 1, 1, nsteps }; + hsize_t mdims_s[] = { 1, 1, nsteps }; hsize_t mstart_s[] = { 0, 0, 0 }; hdf5_write_chunked_array(b, "b", 3, fdims_s, fstart_s, fcount_s, mdims_s, mstart_s, chunk_s, H5T_IEEE_F64LE); @@ -335,12 +367,16 @@ void dump_var_along(int i, int j, int nstep, struct of_traj *traj, int nx, int n hdf5_write_chunked_array(th, "th", 3, fdims_s, fstart_s, fcount_s, mdims_s, mstart_s, chunk_s, H5T_IEEE_F64LE); hdf5_write_chunked_array(phi, "phi", 3, fdims_s, fstart_s, fcount_s, mdims_s, mstart_s, chunk_s, H5T_IEEE_F64LE); + hdf5_write_chunked_array(j_unpol, "j_unpol", 3, fdims_s, fstart_s, fcount_s, mdims_s, mstart_s, chunk_s, H5T_IEEE_F64LE); + hdf5_write_chunked_array(k_unpol, "k_unpol", 3, fdims_s, fstart_s, fcount_s, mdims_s, mstart_s, chunk_s, H5T_IEEE_F64LE); + hdf5_write_chunked_array(I_unpol, "I_unpol", 3, fdims_s, fstart_s, fcount_s, mdims_s, mstart_s, chunk_s, H5T_IEEE_F64LE); + // VECTORS: Anything with N values per geodesic step hsize_t fdims_v[] = { nx, ny, params->maxnstep, 4 }; hsize_t chunk_v[] = { 1, 1, 200, 4 }; hsize_t fstart_v[] = { i, j, 0, 0 }; - hsize_t fcount_v[] = { 1, 1, nstep, 4 }; - hsize_t mdims_v[] = { 1, 1, nstep, 4 }; + hsize_t fcount_v[] = { 1, 1, nsteps, 4 }; + hsize_t mdims_v[] = { 1, 1, nsteps, 4 }; hsize_t mstart_v[] = { 0, 0, 0, 0 }; hdf5_write_chunked_array(X, "X", 4, fdims_v, fstart_v, fcount_v, mdims_v, mstart_v, chunk_v, H5T_IEEE_F64LE); @@ -356,24 +392,24 @@ void dump_var_along(int i, int j, int nstep, struct of_traj *traj, int nx, int n hdf5_write_chunked_array(alpha_inv, "alpha_inv", 4, fdims_v, fstart_v, fcount_v, mdims_v, mstart_v, chunk_v, H5T_IEEE_F64LE); hdf5_write_chunked_array(rho_inv, "rho_inv", 4, fdims_v, fstart_v, fcount_v, mdims_v, mstart_v, chunk_v, H5T_IEEE_F64LE); + hdf5_write_chunked_array(stokes, "stokes", 4, fdims_v, fstart_v, fcount_v, mdims_v, mstart_v, chunk_v, H5T_IEEE_F64LE); + hdf5_write_chunked_array(stokes_coordinate, "stokes_coordinate", 4, fdims_v, fstart_v, fcount_v, mdims_v, mstart_v, chunk_v, H5T_IEEE_F64LE); + fdims_v[3] = chunk_v[3] = fcount_v[3] = mdims_v[3] = 8; hdf5_write_chunked_array(prims, "prims", 4, fdims_v, fstart_v, fcount_v, mdims_v, mstart_v, chunk_v, H5T_IEEE_F64LE); - // Unpolarized coefficients j and k - fdims_v[3] = chunk_v[3] = fcount_v[3] = mdims_v[3] = 2; - hdf5_write_chunked_array(unpol_inv, "jk", 4, fdims_v, fstart_v, fcount_v, mdims_v, mstart_v, chunk_v, H5T_IEEE_F64LE); - + // Scalars free(b); free(ne); free(thetae); free(nu); free(mu); - - free(X); - //free(Kcon); free(Kcov); free(r); free(th); free(phi); + free(j_unpol); free(k_unpol); free(I_unpol); + // Vectors + free(X); + free(Kcon); //free(Kcov); //free(Ucon); free(Ucov); free(Bcon); free(Bcov); free(j_inv); free(alpha_inv); free(rho_inv); - free(unpol_inv); - + free(stokes); free(stokes_coordinate); free(prims); hdf5_close(); diff --git a/src/ipolarray.c b/src/ipolarray.c index e458f19..d140641 100644 --- a/src/ipolarray.c +++ b/src/ipolarray.c @@ -11,12 +11,16 @@ #include "ipolarray.h" #include "decs.h" + #include "coordinates.h" +#include "debug_tools.h" #include "geometry.h" +#include "radiation.h" + #include "model.h" #include "model_radiation.h" #include "model_tetrads.h" -#include "debug_tools.h" + #include // Sub-functions @@ -26,22 +30,6 @@ void push_polar(double Xi[NDIM], double Xm[NDIM], double Xf[NDIM], complex double Nm[NDIM][NDIM], complex double Nf[NDIM][NDIM], double dlam); -/* tensor tools */ -void complex_lower(double complex N[NDIM][NDIM], double gcov[NDIM][NDIM], - int low1, int low2, double complex Nl[NDIM][NDIM]); -void stokes_to_tensor(double fI, double fQ, double fU, double fV, - double complex f_tetrad[NDIM][NDIM]); -void tensor_to_stokes(double complex f_tetrad[NDIM][NDIM], double *fI, - double *fQ, double *fU, double *fV); -void complex_coord_to_tetrad_rank2(double complex T_coord[NDIM][NDIM], - double Ecov[NDIM][NDIM], - double complex T_tetrad[NDIM][NDIM]); -void complex_tetrad_to_coord_rank2(double complex T_tetrad[NDIM][NDIM], - double Econ[NDIM][NDIM], - double complex T_coord[NDIM][NDIM]); - - - /************************PRIMARY FUNCTION*******************************/ /** * Calculate the emission produced/absorbed/rotated along the given trajectory traj @@ -55,13 +43,8 @@ int integrate_emission(struct of_traj *traj, int nsteps, double complex N_coord[NDIM][NDIM], Params *params) { //fprintf(stderr, "Begin integrate emission\n"); - // Initialize - MUNULOOP N_coord[mu][nu] = 0.0 + I * 0.0; - *tauF = 0.; - // Unpolarized - *Intensity = 0.; - *Tau = 0.; - // Error flag + + // Initialize error flag int oddflag = 0; // Integrate the transfer equation (& parallel transport) forwards along trajectory @@ -130,8 +113,6 @@ int integrate_emission(struct of_traj *traj, int nsteps, // smoosh together all the flags we hit along a geodesic oddflag |= sflag; - - // Cry immediately on bad tetrads, even if we're not debugging if (sflag & 1) { fprintf(stderr, "that's odd: no orthonormal tetrad found at\n"); @@ -168,7 +149,7 @@ int integrate_emission(struct of_traj *traj, int nsteps, // Same if there was something in gcov if (sflag & 16) { fprintf(stderr, "Matrix inversion failed in tetrad check, step %d:\n", nstep); - // TODO + // TODO print more stuff here } // TODO pull more relevant stuff back out here @@ -549,6 +530,20 @@ void tensor_to_stokes(double complex f_tetrad[NDIM][NDIM], } +void any_tensor_to_stokes(double complex f_any[NDIM][NDIM], double gcov[NDIM][NDIM], + double *fI, double *fQ, double *fU, double *fV) +{ + + // TODO Find the others with a guaranteed coordinate-frame tetrad? + double complex f_lower[NDIM][NDIM]; + complex_lower(f_any, gcov, 0, 1, f_lower); + *fI = creal(f_lower[0][0] + f_lower[1][1] + f_lower[2][2] + f_lower[3][3]) / 2; + *fQ = 0.; + *fU = 0.; + *fV = 0.; + +} + void complex_coord_to_tetrad_rank2(double complex T_coord[NDIM][NDIM], double Ecov[NDIM][NDIM], double complex T_tetrad[NDIM][NDIM]) diff --git a/src/ipolarray.h b/src/ipolarray.h index b169c99..d85705b 100644 --- a/src/ipolarray.h +++ b/src/ipolarray.h @@ -32,4 +32,20 @@ void project_N(double X[NDIM],double Kcon[NDIM], double complex Ncon[NDIM][NDIM], double *Stokes_I, double *Stokes_Q,double *Stokes_U,double *Stokes_V, double rotcam); +/* tensor tools */ +void complex_lower(double complex N[NDIM][NDIM], double gcov[NDIM][NDIM], + int low1, int low2, double complex Nl[NDIM][NDIM]); +void stokes_to_tensor(double fI, double fQ, double fU, double fV, + double complex f_tetrad[NDIM][NDIM]); +void tensor_to_stokes(double complex f_tetrad[NDIM][NDIM], double *fI, + double *fQ, double *fU, double *fV); +void any_tensor_to_stokes(double complex f_any[NDIM][NDIM], double gcov[NDIM][NDIM], + double *fI, double *fQ, double *fU, double *fV); +void complex_coord_to_tetrad_rank2(double complex T_coord[NDIM][NDIM], + double Ecov[NDIM][NDIM], + double complex T_tetrad[NDIM][NDIM]); +void complex_tetrad_to_coord_rank2(double complex T_tetrad[NDIM][NDIM], + double Econ[NDIM][NDIM], + double complex T_coord[NDIM][NDIM]); + #endif /* IPOLARRAY_H */ diff --git a/src/main.c b/src/main.c index b922bf2..2cafe85 100644 --- a/src/main.c +++ b/src/main.c @@ -759,8 +759,11 @@ void get_pixel(size_t i, size_t j, int nx, int ny, double Xcam[NDIM], Params par double *Intensity, double *Is, double *Qs, double *Us, double *Vs, double *Tau, double *tauF) { - double X[NDIM], Kcon[NDIM]; - double complex N_coord[NDIM][NDIM]; + double X[NDIM] = {0.}, Kcon[NDIM] = {0.}; + double complex N_coord[NDIM][NDIM] = {0.}; + *Intensity = 0.; + *Tau = 0.; + *tauF = 0.; // Integrate backward to find geodesic trajectory init_XK(i,j, params.nx, params.ny, Xcam, params, fovx, fovy, X, Kcon); @@ -775,7 +778,7 @@ void get_pixel(size_t i, size_t j, int nx, int ny, double Xcam[NDIM], Params par exit(-10); } - // Integrate emission forward along trajectory + // Integrate emission all the way forward along trajectory int oddflag = integrate_emission(traj, nstep, Intensity, Tau, tauF, N_coord, ¶ms); if (!only_intensity) { From d9527fa446a75cb2e679a12e75178b0da88b3583 Mon Sep 17 00:00:00 2001 From: Abhishek Joshi Date: Tue, 9 Mar 2021 18:55:16 -0600 Subject: [PATCH 02/93] Added functionality for vector parallel transport in ipolarray --- src/ipolarray.c | 33 +++++++++++++++++++++++++++++++++ src/ipolarray.h | 5 +++++ 2 files changed, 38 insertions(+) diff --git a/src/ipolarray.c b/src/ipolarray.c index d140641..d5c3909 100644 --- a/src/ipolarray.c +++ b/src/ipolarray.c @@ -220,6 +220,38 @@ void push_polar(double Xi[NDIM], double Xm[NDIM], double Xf[NDIM], } +/* + +Parallel Transport a real vector over dl +(see push_polar above) + +*/ +void parallel_transport_vector(double Xi[NDIM], double Xm[NDIM], double Xf[NDIM], + double Ki[NDIM], double Km[NDIM], double Kf[NDIM], + double Ni[NDIM], + double Nm[NDIM], + double Nf[NDIM], double dl) +{ + + /* find the connection */ + double lconn[NDIM][NDIM][NDIM]; + get_connection(Xm, lconn); + int i, j, k; + + /* push N */ + for (i = 0; i < 4; i++) + Nf[i] = Ni[i]; + + for (i = 0; i < 4; i++) + for (j = 0; j < 4; j++) + for (k = 0; k < 4; k++) + Nf[i] += -(lconn[i][j][k] * Nm[j] * Km[k]) * + dl / (L_unit * HPL / (ME * CL * CL)); + + return; +} + + /* * Updates N for one step on geodesics, using the previous step N * here we compute new right-hand side of the equation @@ -228,6 +260,7 @@ void push_polar(double Xi[NDIM], double Xm[NDIM], double Xf[NDIM], * * Return an error flag indicating any singular matrix, bad tetrad, etc. */ + int evolve_N(double Xi[NDIM], double Kconi[NDIM], double Xhalf[NDIM], double Kconhalf[NDIM], double Xf[NDIM], double Kconf[NDIM], diff --git a/src/ipolarray.h b/src/ipolarray.h index d85705b..633ec62 100644 --- a/src/ipolarray.h +++ b/src/ipolarray.h @@ -31,6 +31,11 @@ double approximate_solve (double Ii, double ji, double ki, double jf, double kf, void project_N(double X[NDIM],double Kcon[NDIM], double complex Ncon[NDIM][NDIM], double *Stokes_I, double *Stokes_Q,double *Stokes_U,double *Stokes_V, double rotcam); +void parallel_transport_vector(double Xi[NDIM], double Xm[NDIM], double Xf[NDIM], + double Ki[NDIM], double Km[NDIM], double Kf[NDIM], + double Ni[NDIM], + double Nm[NDIM], + double Nf[NDIM], double dl); /* tensor tools */ void complex_lower(double complex N[NDIM][NDIM], double gcov[NDIM][NDIM], From 66d097af44c2beec3b37eb44c6d23063e98b94f2 Mon Sep 17 00:00:00 2001 From: Abhishek Joshi Date: Tue, 9 Mar 2021 18:57:06 -0600 Subject: [PATCH 03/93] Added code to reverse B field in model.c (when flag reverse_field in model.c set to 1) --- model/iharm/model.c | 18 +++++++++++++++++- src/main.c | 1 + 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/model/iharm/model.c b/model/iharm/model.c index 949cfaf..2895db5 100644 --- a/model/iharm/model.c +++ b/model/iharm/model.c @@ -54,6 +54,8 @@ static double Mdot_dump; static double MdotEdd_dump; static double Ladv_dump; +static int reverse_field=1; + // MAYBES //static double t0; @@ -791,7 +793,21 @@ void load_iharm_data(int n, char *fnam, int dumpidx, int verbose) hdf5_read_array(data[n]->p[KTOT][0][0], "prims", 4, fdims, fstart, fcount, mdims, mstart, H5T_IEEE_F64LE); } - hdf5_read_single_val(&(data[n]->t), "t", H5T_IEEE_F64LE); + //Reversing B Field + if(reverse_field==1){ + double multiplier = -1.0; + for(int i=0;ip[B1][i][j][k] = multiplier*data[n]->p[B1][i][j][k]; + data[n]->p[B2][i][j][k] = multiplier*data[n]->p[B2][i][j][k]; + data[n]->p[B3][i][j][k] = multiplier*data[n]->p[B3][i][j][k]; + } + } + } + } + + hdf5_read_single_val(&(data[n]->t), "t", H5T_IEEE_F64LE); hdf5_close(); diff --git a/src/main.c b/src/main.c index 2cafe85..ffcabda 100644 --- a/src/main.c +++ b/src/main.c @@ -60,6 +60,7 @@ int main(int argc, char *argv[]) { // motd fprintf(stderr, "%s. githash: %s\n", VERSION_STRING, xstr(VERSION)); + fprintf(stderr,"\nWARNING. iharm/model.c CONTAINS A CODE BLOCK TO REVERSE B FIELD. CHECK BEFORE RUNNING\n"); fprintf(stderr, "notes: %s\n\n", xstr(NOTES)); // initialization From e826531bc88ee4a872996a6d7a7b5b5c5102b2ef Mon Sep 17 00:00:00 2001 From: Abhishek Joshi Date: Tue, 9 Mar 2021 19:02:47 -0600 Subject: [PATCH 04/93] Changes to record Stokes vector, e2 basis vector (constructed and parallel transported from the previous step) in the plasma frame along the geodesic --- src/io.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 53 insertions(+), 9 deletions(-) diff --git a/src/io.c b/src/io.c index f6dea49..72be413 100644 --- a/src/io.c +++ b/src/io.c @@ -287,16 +287,29 @@ void dump_var_along(int i, int j, int nstep, struct of_traj *traj, int nx, int n // Initialize stuff we'll carry along double complex N_coord[NDIM][NDIM] = {0.}; + double complex N_tetrad[NDIM][NDIM]; double Intensity = 0, Tau = 0, tauF = 0; double rotcam = params->rotcam*M_PI/180.; + + //Plasma basis vectors to be carried along as well + //Could also just carry along contravariant version and metric in case other vectors need to be parallel transported too. + double Ecovt[NDIM][NDIM], Econt[NDIM][NDIM]; + // Record Stokes parameters double *stokes = calloc(NDIM*nsteps,sizeof(double)); - double *stokes_coordinate = calloc(NDIM*nsteps,sizeof(double)); + // double *stokes_coordinate = calloc(NDIM*nsteps,sizeof(double)); + + double *e2Constructed = calloc(NDIM*nsteps,sizeof(double)); + double *e2ConstructedCov = calloc(NDIM*nsteps,sizeof(double)); + double *e2Transported = calloc(NDIM*nsteps,sizeof(double)); + double *e2TransportedCov = calloc(NDIM*nsteps,sizeof(double)); for (int i=nstep; i > 0; --i) { // Record ambient variables get_model_primitives(traj[i].X, &(prims[i*nprims])); double Ucont[NDIM], Ucovt[NDIM], Bcont[NDIM], Bcovt[NDIM]; + //variables for parallel transport + double e2Mid[NDIM], e2Final[NDIM], e2Cov[NDIM]; get_model_fourv(traj[i].X, traj[i].Kcon, Ucont, Ucovt, Bcont, Bcovt); jar_calc(traj[i].X, traj[i].Kcon, &(j_inv[i*NDIM]), &(j_inv[i*NDIM+1]), &(j_inv[i*NDIM+2]), &(j_inv[i*NDIM+3]), &(alpha_inv[i*NDIM]), &(alpha_inv[i*NDIM+1]), &(alpha_inv[i*NDIM+2]), &(alpha_inv[i*NDIM+3]), @@ -323,15 +336,38 @@ void dump_var_along(int i, int j, int nstep, struct of_traj *traj, int nx, int n bl_coord(traj[i].X, &(r[i]), &(th[i])); phi[i] = traj[i].X[3]; + //make the plasma tetrad to project the coherency tensor + make_plasma_tetrad(Ucont,traj[i].Kcon,Bcont,Gcov,Econt,Ecovt); + + + //parallel transport E2 basis vector by dl to second order accuracy + parallel_transport_vector(traj[i].X,traj[i].X,traj[i].Xhalf,traj[i].Kcon,traj[i].Kcon,traj[i].Kconhalf,Econt[2],Econt[2],e2Mid,traj[i].dl); + parallel_transport_vector(traj[i].X,traj[i].Xhalf,traj[i-1].X,traj[i].Kcon,traj[i].Kconhalf,traj[i-1].Kcon,Econt[2],e2Mid,e2Final,traj[i].dl); + + //copy out transported and constructed vectors + MULOOP{ + e2Constructed[i*NDIM+mu] = Econt[2][mu]; + e2ConstructedCov[i*NDIM+mu] = Ecovt[2][mu]; + //assign parallel transported vector to the right index + e2Transported[(i-1)*NDIM+mu] = e2Final[mu]; + //reset e2Final to be that of the current parallely transported vector to flip index + e2Final[mu] = e2Transported[i*NDIM+mu]; + } + flip_index(e2Final,Gcov,e2Cov); + MULOOP e2TransportedCov[i*NDIM+mu] = e2Cov[mu]; + + //convert N to Stokes in plasma frame and record the coherency tensor at the current step + complex_coord_to_tetrad_rank2(N_coord,Ecovt,N_tetrad); + //Record the Stokes parameters in the correct index + tensor_to_stokes(N_tetrad,&(stokes[(i)*NDIM]), &(stokes[(i)*NDIM+1]), &(stokes[(i)*NDIM+2]), &(stokes[(i)*NDIM+3])); + // Integrate and record results int flag = integrate_emission(&(traj[i]), 1, &Intensity, &Tau, &tauF, N_coord, params); I_unpol[i] = Intensity; - project_N(traj[i-1].X, traj[i-1].Kcon, N_coord, - &(stokes[i*NDIM]), &(stokes[i*NDIM+1]), &(stokes[i*NDIM+2]), &(stokes[i*NDIM+3]), - 0); - any_tensor_to_stokes(N_coord, Gcov, - &(stokes_coordinate[i*NDIM]), &(stokes_coordinate[i*NDIM+1]), - &(stokes_coordinate[i*NDIM+2]), &(stokes_coordinate[i*NDIM+3])); + // project_N(traj[i-1].X, traj[i-1].Kcon, N_coord, &(stokes[i*NDIM]), &(stokes[i*NDIM+1]), &(stokes[i*NDIM+2]), &(stokes[i*NDIM+3]),0; + // any_tensor_to_stokes(N_coord, Gcov, &(stokes_coordinate[i*NDIM]), &(stokes_coordinate[i*NDIM+1]), &(stokes_coordinate[i*NDIM+2]), &(stokes_coordinate[i*NDIM+3])); + + if (flag) printf("Flagged integration when tracing: %d\n", flag); } @@ -393,7 +429,12 @@ void dump_var_along(int i, int j, int nstep, struct of_traj *traj, int nx, int n hdf5_write_chunked_array(rho_inv, "rho_inv", 4, fdims_v, fstart_v, fcount_v, mdims_v, mstart_v, chunk_v, H5T_IEEE_F64LE); hdf5_write_chunked_array(stokes, "stokes", 4, fdims_v, fstart_v, fcount_v, mdims_v, mstart_v, chunk_v, H5T_IEEE_F64LE); - hdf5_write_chunked_array(stokes_coordinate, "stokes_coordinate", 4, fdims_v, fstart_v, fcount_v, mdims_v, mstart_v, chunk_v, H5T_IEEE_F64LE); + // hdf5_write_chunked_array(stokes_coordinate, "stokes_coordinate", 4, fdims_v, fstart_v, fcount_v, mdims_v, mstart_v, chunk_v, H5T_IEEE_F64LE); + + hdf5_write_chunked_array(e2Constructed, "e2Constructed", 4, fdims_v, fstart_v, fcount_v, mdims_v, mstart_v, chunk_v, H5T_IEEE_F64LE); + hdf5_write_chunked_array(e2ConstructedCov, "e2ConstructedCov", 4, fdims_v, fstart_v, fcount_v, mdims_v, mstart_v, chunk_v, H5T_IEEE_F64LE); + hdf5_write_chunked_array(e2Transported, "e2Transported", 4, fdims_v, fstart_v, fcount_v, mdims_v, mstart_v, chunk_v, H5T_IEEE_F64LE); + hdf5_write_chunked_array(e2TransportedCov, "e2TransportedCov", 4, fdims_v, fstart_v, fcount_v, mdims_v, mstart_v, chunk_v, H5T_IEEE_F64LE); fdims_v[3] = chunk_v[3] = fcount_v[3] = mdims_v[3] = 8; hdf5_write_chunked_array(prims, "prims", 4, fdims_v, fstart_v, fcount_v, mdims_v, mstart_v, chunk_v, H5T_IEEE_F64LE); @@ -409,7 +450,10 @@ void dump_var_along(int i, int j, int nstep, struct of_traj *traj, int nx, int n free(Kcon); //free(Kcov); //free(Ucon); free(Ucov); free(Bcon); free(Bcov); free(j_inv); free(alpha_inv); free(rho_inv); - free(stokes); free(stokes_coordinate); + free(stokes); + // free(stokes_coordinate); + free(e2Constructed);free(e2ConstructedCov); + free(e2Transported);free(e2TransportedCov); free(prims); hdf5_close(); From 7c987b30743cbc07e481d0510f56e7341872a7bc Mon Sep 17 00:00:00 2001 From: Abhishek Joshi Date: Fri, 12 Mar 2021 11:47:52 -0600 Subject: [PATCH 05/93] added frontera.make from fix/frontera-complie --- machines/frontera.make | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 machines/frontera.make diff --git a/machines/frontera.make b/machines/frontera.make new file mode 100644 index 0000000..ca776c3 --- /dev/null +++ b/machines/frontera.make @@ -0,0 +1,15 @@ +CC = h5pcc + +# CFLAGS from original build.py chosen not to produce silent errors + +ifneq (,$(findstring icc,$(shell $(CC) --version))) + GSL_DIR = /opt/apps/intel19/gsl/2.6 + CFLAGS = -O3 -axCORE-AVX512 -std=gnu99 -qopenmp + MATH_LIB = +endif + +ifneq (,$(findstring gcc,$(shell $(CC) --version))) + GSL_DIR = /opt/apps/gcc7_1/gsl/2.6/ + CC = h5pcc -shlib + CFLAGS = -O3 -std=gnu99 -Wall -fopenmp +endif From 8567d1df452fe0c15d1b4941234d7f57154ed00a Mon Sep 17 00:00:00 2001 From: Abhishek Joshi Date: Fri, 12 Mar 2021 12:21:28 -0600 Subject: [PATCH 06/93] edited to use frontera.make when on frontera --- makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/makefile b/makefile index db49123..d349682 100644 --- a/makefile +++ b/makefile @@ -44,6 +44,9 @@ HOST := $(shell hostname) ifneq (,$(findstring stampede2,$(HOST))) -include $(MAKEFILE_PATH)/machines/stampede2.make endif +ifneq (,$(findstring frontera,$(HOST))) + -include $(MAKEFILE_PATH)/machines/frontera.make +endif # Hack to check only whether host begins with bh* ifneq (,$(findstring beginsbh,begins$(HOST))) -include $(MAKEFILE_PATH)/machines/bh-cluster.make From e5860fbf8499e0617d7f3175fca328b5a85f7ede Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Mon, 22 Mar 2021 17:02:09 -0500 Subject: [PATCH 07/93] Extra output of tensor quantities, some cleanup. --- model/iharm/model.c | 8 +++++--- src/io.c | 48 ++++++++++++++++++++++++++++++++++++++------- src/main.c | 2 +- 3 files changed, 47 insertions(+), 11 deletions(-) diff --git a/model/iharm/model.c b/model/iharm/model.c index b772031..32e53b5 100644 --- a/model/iharm/model.c +++ b/model/iharm/model.c @@ -58,7 +58,7 @@ static double Mdot_dump; static double MdotEdd_dump; static double Ladv_dump; -static int reverse_field=1; +static int reverse_field = 0; // MAYBES //static double t0; @@ -127,6 +127,8 @@ void try_set_model_parameter(const char *word, const char *value) set_by_word_val(word, value, "rmax_geo", &rmax_geo, TYPE_DBL); set_by_word_val(word, value, "rmin_geo", &rmin_geo, TYPE_DBL); + set_by_word_val(word, value, "reverse_field", &reverse_field, TYPE_INT); + // for slow light set_by_word_val(word, value, "dump_min", &dumpmin, TYPE_INT); set_by_word_val(word, value, "dump_max", &dumpmax, TYPE_INT); @@ -238,7 +240,7 @@ void get_dumpfile_type(char *fnam, int dumpidx) dumpfile_format = FORMAT_IHARM_v1; fprintf(stderr, "iharm!\n"); } else { - // note this will return -1 if the "header" group does not exists + // note this will return -1 if the "header" group does not exist dumpfile_format = FORMAT_HAMR_EKS; fprintf(stderr, "hamr!\n"); } @@ -1269,7 +1271,7 @@ void load_iharm_data(int n, char *fnam, int dumpidx, int verbose) } //Reversing B Field - if(reverse_field==1){ + if(reverse_field) { double multiplier = -1.0; for(int i=0;i #include @@ -235,8 +236,6 @@ void dump(double image[], double imageS[], double taus[], /* * Given a path, dump a variable computed along that path into a file. * Note this is most definitely *not* thread-safe, so it gets called from an 'omp critical' - * - * TODO Output of Q/U as locally parallel transported forward along the curve? */ void dump_var_along(int i, int j, int nstep, struct of_traj *traj, int nx, int ny, double scale, double cam[NDIM], double fovx, double fovy, Params *params) @@ -280,13 +279,13 @@ void dump_var_along(int i, int j, int nstep, struct of_traj *traj, int nx, int n // double *Bcon = calloc(NDIM*nsteps, sizeof(double)); // double *Bcov = calloc(NDIM*nsteps, sizeof(double)); - // TODO NDIM and NSTOKES are not the same thing + // TODO NDIM and NSTOKES are not necessarily the same thing... double *j_inv = calloc(NDIM*nsteps, sizeof(double)); double *alpha_inv = calloc(NDIM*nsteps, sizeof(double)); double *rho_inv = calloc(NDIM*nsteps, sizeof(double)); // Initialize stuff we'll carry along - double complex N_coord[NDIM][NDIM] = {0.}; + double complex N_coord[NDIM][NDIM] = {{0.}}; double complex N_tetrad[NDIM][NDIM]; double Intensity = 0, Tau = 0, tauF = 0; double rotcam = params->rotcam*M_PI/180.; @@ -297,7 +296,11 @@ void dump_var_along(int i, int j, int nstep, struct of_traj *traj, int nx, int n // Record Stokes parameters double *stokes = calloc(NDIM*nsteps,sizeof(double)); - // double *stokes_coordinate = calloc(NDIM*nsteps,sizeof(double)); + double *stokes_coordinate = calloc(NDIM*nsteps,sizeof(double)); + double *ntetrad = calloc(NDIM*NDIM*nsteps,sizeof(double)); + double *ncoord = calloc(NDIM*NDIM*nsteps,sizeof(double)); + double *econ = calloc(NDIM*NDIM*nsteps,sizeof(double)); + double *ecov = calloc(NDIM*NDIM*nsteps,sizeof(double)); double *e2Constructed = calloc(NDIM*nsteps,sizeof(double)); double *e2ConstructedCov = calloc(NDIM*nsteps,sizeof(double)); @@ -353,13 +356,25 @@ void dump_var_along(int i, int j, int nstep, struct of_traj *traj, int nx, int n //reset e2Final to be that of the current parallely transported vector to flip index e2Final[mu] = e2Transported[i*NDIM+mu]; } + MUNULOOP { + econ[i*NDIM*NDIM + mu*NDIM + nu] = Econt[mu][nu]; + ecov[i*NDIM*NDIM + mu*NDIM + nu] = Ecovt[mu][nu]; + } + flip_index(e2Final,Gcov,e2Cov); MULOOP e2TransportedCov[i*NDIM+mu] = e2Cov[mu]; - //convert N to Stokes in plasma frame and record the coherency tensor at the current step + // Convert N to Stokes in plasma frame and record the coherency tensor at the current step complex_coord_to_tetrad_rank2(N_coord,Ecovt,N_tetrad); - //Record the Stokes parameters in the correct index + // Record N + MUNULOOP { + ntetrad[i*NDIM*NDIM + mu*NDIM + nu] = N_tetrad[mu][nu]; + ncoord[i*NDIM*NDIM + mu*NDIM + nu] = N_coord[mu][nu]; + } + // Record the Stokes parameters in the correct index tensor_to_stokes(N_tetrad,&(stokes[(i)*NDIM]), &(stokes[(i)*NDIM+1]), &(stokes[(i)*NDIM+2]), &(stokes[(i)*NDIM+3])); + project_N(traj[i-1].X, traj[i-1].Kcon, N_coord, &(stokes_coordinate[i*NDIM]), &(stokes_coordinate[i*NDIM+1]), + &(stokes_coordinate[i*NDIM+2]), &(stokes_coordinate[i*NDIM+3]), rotcam); // Integrate and record results int flag = integrate_emission(&(traj[i]), 1, &Intensity, &Tau, &tauF, N_coord, params); @@ -439,9 +454,24 @@ void dump_var_along(int i, int j, int nstep, struct of_traj *traj, int nx, int n fdims_v[3] = chunk_v[3] = fcount_v[3] = mdims_v[3] = 8; hdf5_write_chunked_array(prims, "prims", 4, fdims_v, fstart_v, fcount_v, mdims_v, mstart_v, chunk_v, H5T_IEEE_F64LE); + // TENSORS: Anything with N values per geodesic step + hsize_t fdims_t[] = { nx, ny, params->maxnstep, 4, 4 }; + hsize_t chunk_t[] = { 1, 1, 200, 4, 4 }; + hsize_t fstart_t[] = { i, j, 0, 0, 0 }; + hsize_t fcount_t[] = { 1, 1, nsteps, 4, 4 }; + hsize_t mdims_t[] = { 1, 1, nsteps, 4, 4 }; + hsize_t mstart_t[] = { 0, 0, 0, 0, 0 }; + hdf5_write_chunked_array(ntetrad, "ntetrad", 5, fdims_t, fstart_t, fcount_t, mdims_t, mstart_t, chunk_t, H5T_IEEE_F64LE); + hdf5_write_chunked_array(ncoord, "ncoord", 5, fdims_t, fstart_t, fcount_t, mdims_t, mstart_t, chunk_t, H5T_IEEE_F64LE); + hdf5_write_chunked_array(econ, "Econ", 5, fdims_t, fstart_t, fcount_t, mdims_t, mstart_t, chunk_t, H5T_IEEE_F64LE); + hdf5_write_chunked_array(ecov, "Ecov", 5, fdims_t, fstart_t, fcount_t, mdims_t, mstart_t, chunk_t, H5T_IEEE_F64LE); + + // DEALLOCATE + // Scalars free(b); free(ne); free(thetae); free(nu); free(mu); + free(dl); free(r); free(th); free(phi); free(j_unpol); free(k_unpol); free(I_unpol); @@ -456,5 +486,9 @@ void dump_var_along(int i, int j, int nstep, struct of_traj *traj, int nx, int n free(e2Transported);free(e2TransportedCov); free(prims); + // Tensors + free(ntetrad); free(ncoord); + free(econ); free(ecov); + hdf5_close(); } diff --git a/src/main.c b/src/main.c index 9c8786a..a1271fd 100644 --- a/src/main.c +++ b/src/main.c @@ -762,7 +762,7 @@ void get_pixel(size_t i, size_t j, int nx, int ny, double Xcam[NDIM], Params par double *Tau, double *tauF) { double X[NDIM] = {0.}, Kcon[NDIM] = {0.}; - double complex N_coord[NDIM][NDIM] = {0.}; + double complex N_coord[NDIM][NDIM] = {{0.}}; *Intensity = 0.; *Tau = 0.; *tauF = 0.; From a2217b5ff432fa5c10080938af9bd14c79151969 Mon Sep 17 00:00:00 2001 From: George Wong Date: Fri, 26 Mar 2021 01:26:18 -0500 Subject: [PATCH 08/93] add isothermal sphere model --- model/sphere/model.c | 209 ++++++++++++++++++++++++++++++++++++ model/sphere/model_params.h | 8 ++ src/grid.c | 6 ++ src/main.c | 4 + 4 files changed, 227 insertions(+) create mode 100644 model/sphere/model.c create mode 100644 model/sphere/model_params.h diff --git a/model/sphere/model.c b/model/sphere/model.c new file mode 100644 index 0000000..3580b67 --- /dev/null +++ b/model/sphere/model.c @@ -0,0 +1,209 @@ +#include "model.h" + +#include "decs.h" +#include "hdf5_utils.h" + +#include "coordinates.h" +#include "geometry.h" +#include "grid.h" +#include "model_radiation.h" // Only for outputting emissivities +#include "par.h" +#include "utils.h" + +#include +#include + +// used by other files +double rmax_geo; +double L_unit; + +// model parameters +static double MODEL_R_0 = 100.; +static double MODEL_TAU_0 = 1.e-5; +static double MODEL_THETAE_0 = 10.; +static double MODEL_BETA_0 = 20.; +static double MODEL_MBH = 4.1e6; +static double MODEL_TP_OVER_TE = 3; +static double MODEL_GAM = 13./9; + +// derived model parameters +double model_Ne_0 = 1.; +double model_B_0 = 1.; + +void try_set_model_parameter(const char *word, const char *value) +{ + set_by_word_val(word, value, "Thetae0", &MODEL_THETAE_0, TYPE_DBL); + set_by_word_val(word, value, "R0", &MODEL_R_0, TYPE_DBL); + set_by_word_val(word, value, "tau0", &MODEL_TAU_0, TYPE_DBL); + set_by_word_val(word, value, "beta0", &MODEL_BETA_0, TYPE_DBL); + set_by_word_val(word, value, "gam", &MODEL_GAM, TYPE_DBL); + set_by_word_val(word, value, "tp_over_te", &MODEL_TP_OVER_TE, TYPE_DBL); + set_by_word_val(word, value, "MBH", &MODEL_MBH, TYPE_DBL); +} + +// used in slow light with real data +void update_data_until(double *tA, double *tB, double tgt) { } +void update_data(double *tA, double *tB) { } +double get_dump_t(char *fnam, int dumpidx) { return 0.; } + +void init_model(double *tA, double *tB) +{ + // set nice numbers here + *tA = 0.; + *tB = 1.; + + // set metric + use_eKS_internal = 0; + metric = METRIC_MINKOWSKI; + + // set units + double MBH = MODEL_MBH * MSUN; + L_unit = GNEWT * MBH / (CL * CL); + + // derive model Ne (in cgs) + model_Ne_0 = MODEL_TAU_0 / SIGMA_THOMSON / MODEL_R_0 / L_unit; + + // derive model B (in gauss) + double THETAE_UNIT = 1.; + + // since B = B(pressure), we need to specify the thermodynamics to + // find pressure = pressure(Thetae) + double gam = MODEL_GAM; + double game = 4./3; + double gamp = 5./3; + + // as implemented in the Illinois suite + THETAE_UNIT = MP/ME * (game-1.) * (gamp-1.) / ( (gamp-1.) + (game-1)*MODEL_TP_OVER_TE ); + + // as implemented in RAPTOR + kmonty + THETAE_UNIT = MP/ME * (gam-1.) / (1. + MODEL_TP_OVER_TE); + + // now we can find B (again, in gauss) + model_B_0 = CL * sqrt(8 * M_PI * (gam-1.) * (MP+ME) / MODEL_BETA_0) * sqrt( model_Ne_0 * MODEL_THETAE_0 ) / sqrt( THETAE_UNIT ); + + fprintf(stderr, "Running with isothermal sphere model.\n"); + fprintf(stderr, "MBH, L_unit: %g [Msun], %g\n",MBH/MSUN, L_unit); + fprintf(stderr, "Ne, Thetae, B: %g %g %g\n", model_Ne_0, MODEL_THETAE_0, model_B_0); + fprintf(stderr, "Rout: %g\n", MODEL_R_0 * L_unit); + + // not really used in ipole other than for coordinates + N1 = 300; + N2 = 100; + N3 = 1; + + // set up coordinates + startx[0] = 0.; + startx[1] = 0.; + startx[2] = 0.; + startx[3] = 0.; + + dx[0] = 0.1; + dx[1] = (MODEL_R_0 - 0) / N1; + dx[2] = M_PI / N2; + dx[3] = 2. * M_PI / N3; + + stopx[0] = 1.; + stopx[1] = startx[1]+N1*dx[1]; + stopx[2] = startx[2]+N2*dx[2]; + stopx[3] = startx[3]+N3*dx[3]; + + rmax_geo = fmax(100., MODEL_R_0); + + fprintf(stderr, "Native coordinate start: %g %g %g stop: %g %g %g\n", + startx[1], startx[2], startx[3], stopx[1], stopx[2], stopx[3]); + +} + +/* + these supply basic model data to ipole +*/ + +// Calculate Ucon,Ucov,Bcon,Bcov from primitives at location X using +// interpolation (on the primitives). This has been all wrapped into +// a single function because some calculations require each other. +void get_model_fourv(double X[NDIM], double Kcon[NDIM], + double Ucon[NDIM], double Ucov[NDIM], + double Bcon[NDIM], double Bcov[NDIM]) +{ + double r, h; + bl_coord(X, &r, &h); + + double gcov[NDIM][NDIM]; + gcov_func(X, gcov); + + Ucon[0] = 1.; + Ucon[1] = 0.; + Ucon[2] = 0.; + Ucon[3] = 0.; + + Bcon[0] = 0.; + Bcon[1] = model_B_0 * cos(h); + Bcon[2] = - model_B_0 * sin(h) / (r + 1.e-8); + Bcon[3] = 0.; + + lower(Ucon, gcov, Ucov); + lower(Bcon, gcov, Bcov); +} + +// used in diagnostics IO. not implemented +void get_model_primitives(double X[NDIM], double *p) { } + +double get_model_thetae(double X[NDIM]) +{ + if (radiating_region(X) == 0) { + return 0; + } + + return MODEL_THETAE_0; +} + +//b field strength in Gauss +double get_model_b(double X[NDIM]) +{ + if (radiating_region(X) == 0) { + return 0.; + } + + return model_B_0; +} + +double get_model_ne(double X[NDIM]) +{ + if (radiating_region(X) == 0) { + return 0.; + } + + return model_Ne_0; +} + +void output_hdf5() +{ + hdf5_set_directory("/"); + + // TODO maybe output the model parameters here + + hdf5_set_directory("/header/"); + hdf5_make_directory("units"); + hdf5_set_directory("/header/units/"); + hdf5_write_single_val(&L_unit, "L_unit", H5T_IEEE_F64LE); + + hdf5_set_directory("/"); +} + +int radiating_region(double X[NDIM]) +{ + double r, h; + bl_coord(X, &r, &h); + return (r Date: Fri, 2 Apr 2021 15:42:19 -0500 Subject: [PATCH 09/93] Development continues --- src/decs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/decs.h b/src/decs.h index 3c85ed0..fb9b6f6 100644 --- a/src/decs.h +++ b/src/decs.h @@ -8,7 +8,7 @@ #include // Strings and string tools -#define VERSION_STRING "ipole-release-1.3" +#define VERSION_STRING "ipole-beta-1.4" #define xstr(s) str(s) #define str(s) #s #define STRLEN (2048) From 159203083d1fd4f7bc26c1bc001092e1c1c6d17e Mon Sep 17 00:00:00 2001 From: George Wong Date: Fri, 2 Apr 2021 17:51:14 -0500 Subject: [PATCH 10/93] add support for ressler iharm format --- model/iharm/model.c | 37 ++++++++++++++++++++++++++++++++++++- model/iharm/model_params.h | 1 + 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/model/iharm/model.c b/model/iharm/model.c index 92afacf..97b8b59 100644 --- a/model/iharm/model.c +++ b/model/iharm/model.c @@ -32,6 +32,10 @@ double U_unit; double B_unit; double Te_unit; +// MOLECULAR WEIGHTS +static double Ne_factor = 1.; // used for, e.g., singly ionized helium +static double mu_i, mu_e, mu_tot; + // MODEL PARAMETERS: PUBLIC double DTd; double rmax_geo = 100.; @@ -67,7 +71,9 @@ static double Ladv_dump; // 0 : constant TP_OVER_TE // 1 : use dump file model (kawazura?) // 2 : use mixed TP_OVER_TE (beta model) +// 3 : use mixed TP_OVER_TE (beta model) with fluid temperature // TODO the way this is selected is horrid. Make it a parameter. +#define ELECTRONS_TFLUID (3) static int RADIATION, ELECTRONS; static double gam = 1.444444, game = 1.333333, gamp = 1.666667; static double Thetae_unit, Mdotedd; @@ -520,7 +526,7 @@ void init_physical_quantities(int n) for (int i = 0; i < N1+2; i++) { for (int j = 0; j < N2+2; j++) { for (int k = 0; k < N3+2; k++) { - data[n]->ne[i][j][k] = data[n]->p[KRHO][i][j][k] * RHO_unit/(MP+ME) ; + data[n]->ne[i][j][k] = data[n]->p[KRHO][i][j][k] * RHO_unit/(MP+ME) * Ne_factor; double bsq = data[n]->b[i][j][k] / B_unit; bsq = bsq*bsq; @@ -537,6 +543,12 @@ void init_physical_quantities(int n) // see, e.g., Eq. 8 of the EHT GRRT formula list Thetae_unit = (MP/ME) * (game-1.) * (gamp-1.) / ( (gamp-1.) + (game-1.)*trat ); data[n]->thetae[i][j][k] = Thetae_unit*data[n]->p[UU][i][j][k]/data[n]->p[KRHO][i][j][k]; + } else if (ELECTRONS == ELECTRONS_TFLUID) { + double beta = data[n]->p[UU][i][j][k]*(gam-1.)/0.5/bsq; + double betasq = beta*beta / beta_crit/beta_crit; + double trat = trat_large * betasq/(1. + betasq) + trat_small /(1. + betasq); + double dfactor = mu_tot / mu_e + mu_tot / mu_i * trat; + data[n]->thetae[i][j][k] = data[n]->p[THF][i][j][k] / dfactor; } else { data[n]->thetae[i][j][k] = Thetae_unit*data[n]->p[UU][i][j][k]/data[n]->p[KRHO][i][j][k]; } @@ -721,6 +733,17 @@ void init_iharm_grid(char *fnam, int dumpidx) exit(-3); } + if ( hdf5_exists("weights") ) { + hdf5_set_directory("/header/weights/"); + hdf5_read_single_val(&mu_i, "mu_i", H5T_IEEE_F64LE); + hdf5_read_single_val(&mu_e, "mu_e", H5T_IEEE_F64LE); + hdf5_read_single_val(&mu_tot, "mu_tot", H5T_IEEE_F64LE); + fprintf(stderr, "Loaded molecular weights (mu_i, mu_e, mu_tot): %g %g %g\n", mu_i, mu_e, mu_tot); + Ne_factor = 1. / mu_e; + hdf5_set_directory("/header/"); + ELECTRONS = ELECTRONS_TFLUID; + } + char metric_name[20]; hid_t HDF5_STR_TYPE = hdf5_make_str_type(20); hdf5_read_single_val(&metric_name, "metric", HDF5_STR_TYPE); @@ -770,6 +793,9 @@ void init_iharm_grid(char *fnam, int dumpidx) } ELECTRONS = 1; Thetae_unit = MP/ME; + } else if (ELECTRONS == ELECTRONS_TFLUID) { + fprintf(stderr, "Using Ressler/Athena electrons with mixed tp_over_te and\n"); + fprintf(stderr, "trat_small = %g, trat_large = %g, and beta_crit = %g\n", trat_small, trat_large, beta_crit); } else if (USE_FIXED_TPTE && !USE_MIXED_TPTE) { ELECTRONS = 0; // force TP_OVER_TE to overwrite bad electrons fprintf(stderr, "Using fixed tp_over_te ratio = %g\n", tp_over_te); @@ -919,6 +945,10 @@ void output_hdf5() hdf5_write_single_val(&trat_small, "rlow", H5T_IEEE_F64LE); hdf5_write_single_val(&trat_large, "rhigh", H5T_IEEE_F64LE); hdf5_write_single_val(&beta_crit, "beta_crit", H5T_IEEE_F64LE); + } else if (ELECTRONS == ELECTRONS_TFLUID) { + hdf5_write_single_val(&mu_i, "mu_i", H5T_IEEE_F64LE); + hdf5_write_single_val(&mu_e, "mu_e", H5T_IEEE_F64LE); + hdf5_write_single_val(&mu_tot, "mu_tot", H5T_IEEE_F64LE); } hdf5_write_single_val(&ELECTRONS, "type", H5T_STD_I32LE); @@ -1298,6 +1328,11 @@ void load_iharm_data(int n, char *fnam, int dumpidx, int verbose) hdf5_read_array(data[n]->p[KTOT][0][0], "prims", 4, fdims, fstart, fcount, mdims, mstart, H5T_IEEE_F64LE); } + if (ELECTRONS == ELECTRONS_TFLUID) { + fstart[3] = 8; + hdf5_read_array(data[n]->p[THF][0][0], "prims", 4, fdims, fstart, fcount, mdims, mstart, H5T_IEEE_F64LE); + } + hdf5_read_single_val(&(data[n]->t), "t", H5T_IEEE_F64LE); hdf5_close(); diff --git a/model/iharm/model_params.h b/model/iharm/model_params.h index 23b45f2..b62f84c 100644 --- a/model/iharm/model_params.h +++ b/model/iharm/model_params.h @@ -14,6 +14,7 @@ #define B3 7 #define KEL 8 #define KTOT 9 +#define THF 8 // fluid temperature in me c^2 extern double DTd; extern double sigma_cut; From 569992dde0462017af977e90389178223af1fd0b Mon Sep 17 00:00:00 2001 From: "George N. Wong" Date: Thu, 8 Apr 2021 22:47:43 -0500 Subject: [PATCH 11/93] nits --- model/iharm/model.c | 2 +- src/ipolarray.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/model/iharm/model.c b/model/iharm/model.c index 97b8b59..ab79b82 100644 --- a/model/iharm/model.c +++ b/model/iharm/model.c @@ -33,7 +33,7 @@ double B_unit; double Te_unit; // MOLECULAR WEIGHTS -static double Ne_factor = 1.; // used for, e.g., singly ionized helium +static double Ne_factor = 1.; // e.g., used for He with 2 protons+neutrons per 2 electrons static double mu_i, mu_e, mu_tot; // MODEL PARAMETERS: PUBLIC diff --git a/src/ipolarray.h b/src/ipolarray.h index aca842b..a093cf1 100644 --- a/src/ipolarray.h +++ b/src/ipolarray.h @@ -14,7 +14,7 @@ #include // Top-level functions for solving emission -int integrate_emission(struct of_traj *traj, int nstep, +int integrate_emission(struct of_traj *traj, int nsteps, double *Intensity, double *Tau, double *tauF, double complex N_coord[NDIM][NDIM], Params *params); From 713e0298c61243d24f038d381dedb6e8837e3b4e Mon Sep 17 00:00:00 2001 From: "George N. Wong" Date: Thu, 15 Apr 2021 15:10:04 -0500 Subject: [PATCH 12/93] remove 100M limit on rmax_geo --- model/iharm/model.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/model/iharm/model.c b/model/iharm/model.c index 92afacf..aa3cb4b 100644 --- a/model/iharm/model.c +++ b/model/iharm/model.c @@ -669,7 +669,7 @@ void init_hamr_grid(char *fnam, int dumpidx) dx[2] /= 2; // set limit for tracking geodesic emission - rmax_geo = fmin(rmax_geo, fmin(100., Rout)); + rmax_geo = fmin(rmax_geo, Rout); rmin_geo = fmax(rmin_geo, Rin); cstartx[0] = 0; @@ -861,8 +861,8 @@ void init_iharm_grid(char *fnam, int dumpidx) } } - // Don't emit beyond specified limit, coordinate limit, or 100M, whichever is *smaller* - rmax_geo = fmin(rmax_geo, fmin(100., Rout)); + // Don't emit beyond specified limit, coordinate limit + rmax_geo = fmin(rmax_geo, Rout); rmin_geo = fmax(rmin_geo, Rin); hdf5_set_directory("/"); From b527fa583b06230cbd5f20b56c9e09d3ce54b579 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Fri, 16 Apr 2021 23:48:54 -0500 Subject: [PATCH 13/93] RIAF model from Pu & Broderick '18 * Image total flux matches paper descriptions for similar scaling factors * Image morphologies vs parameters match presented figures --- model/riaf/example.par | 47 +++++ model/riaf/model.c | 363 ++++++++++++++++++++++++++++++++++++++ model/riaf/model_params.h | 15 ++ src/geometry.c | 21 +++ src/geometry.h | 2 +- src/radiation.c | 9 +- 6 files changed, 453 insertions(+), 4 deletions(-) create mode 100644 model/riaf/example.par create mode 100644 model/riaf/model.c create mode 100644 model/riaf/model_params.h diff --git a/model/riaf/example.par b/model/riaf/example.par new file mode 100644 index 0000000..4e3c47f --- /dev/null +++ b/model/riaf/example.par @@ -0,0 +1,47 @@ +# RIAF model options + +outfile image.h5 + +a 0.9375 +Ne_unit 3e7 +Te_unit 3e11 + +# Model parameters from Odyssey (proprietary) +# c.f. Themis src/model/model_image_sed_fitted_riaf.cpp/h +# Parameters correspond to n_e,th^0, T_e^0, H, -alpha, -gamma in Pu & Broderick '18 +# Note that nth0/Te0 are in terms of Ne_unit & Te_unit, which should be used to set scale +nth0 1 +Te0 1 +disk_h 0.1 +pow_nth -1.1 +pow_T -0.84 + +# Inflow parameters for Shearing Inflow model from VRT2, +# Themis src/VRT2/src/AccretionFlows/afv_shearing_inflow.cpp/h +keplerian_factor 1.0 +infall_factor 0.0 + +# RIAF model gives e- Ne/Te, so use Dexter emissivity fits +emission_type 1 + +# Camera location +rcam 1000 +thetacam 85 +phicam 0 + +# Camera FOV in r_g +fovx_dsource 200 +fovy_dsource 200 + +# Image size +nx 100 +ny 100 + +# Distance to source in pc +dsource 8.3e3 + +# Standard frequency nu_p +freqcgs 230.e9 + +# MBH in solar masses +MBH 4.3e6 diff --git a/model/riaf/model.c b/model/riaf/model.c new file mode 100644 index 0000000..385a9b7 --- /dev/null +++ b/model/riaf/model.c @@ -0,0 +1,363 @@ +#include "model.h" + +#include "decs.h" +#include "coordinates.h" +#include "geometry.h" +#include "radiation.h" +#include "par.h" +#include "tetrads.h" + +// Globals we're in charge of +double M_unit; +double L_unit; +double T_unit; +double U_unit; +double B_unit; +double RHO_unit; +// These actually set problem scale +double Te_unit = 1e11; +double Ne_unit = 5e6; + +// Model parameters: public +double rmax_geo = 1000.0; +// Model parameters: private +static double rmin_geo = 0; +static double MBH_solar = 4.3e6; + +static double MBH; + +// e.g. parameterization from GRRT paper +double nth0, Te0, disk_h, pow_nth, pow_T; +double keplerian_factor, infall_factor; +double r_isco; + +/** + * This is a template for analytic problems, which consist of prescription: + * X,K -> 4-vectors Ucon/cov, Bcon/cov + * And either: + * X,K -> emission coefficients jS, alphaS, rhoS + * or: + * X,K -> e- density and temperature ne, Thetae + */ + +// Forward declarations for non-public functions +void set_units(); + +//// INITIALIZATION: Functions called from elsewhere in ipole //// + +/** + * This function is called for each word/value pair ipole encounters, + * either from a parfile or the command line. + * You can define new pairs here, skim what you need of ipole's defaults, or whatever + * + * ipole will not warn on unspecified parameters. Have good defaults (set on declaration) + */ +void try_set_model_parameter(const char *word, const char *value) +{ + // Test the given word against our parameters' names, + // and if it matches set the corresponding global + set_by_word_val(word, value, "MBH", &MBH_solar, TYPE_DBL); + set_by_word_val(word, value, "Ne_unit", &Ne_unit, TYPE_DBL); + set_by_word_val(word, value, "Te_unit", &Te_unit, TYPE_DBL); + // TODO NEED to move this into main parameters + set_by_word_val(word, value, "rmax_geo", &rmax_geo, TYPE_DBL); + set_by_word_val(word, value, "rmin_geo", &rmin_geo, TYPE_DBL); + + // Geometry parameters + set_by_word_val(word, value, "a", &a, TYPE_DBL); + // RIAF model parameters, from Odyssey. + // Note nth0,Te0 are *different* as they are unitless! + set_by_word_val(word, value, "nth0", &nth0, TYPE_DBL); + set_by_word_val(word, value, "Te0", &Te0, TYPE_DBL); + set_by_word_val(word, value, "disk_h", &disk_h, TYPE_DBL); + set_by_word_val(word, value, "pow_nth", &pow_nth, TYPE_DBL); + set_by_word_val(word, value, "pow_T", &pow_T, TYPE_DBL); + // Inflow model parameters + set_by_word_val(word, value, "keplerian_factor", &keplerian_factor, TYPE_DBL); + set_by_word_val(word, value, "infall_factor", &infall_factor, TYPE_DBL); + + // Normal ipole pulls this, but we also need it for the GRRT problems + // and this is easier than grabbing it from the 'params' struct + //set_by_word_val(word, value, "freqcgs", &freqcgs, TYPE_DBL); +} + +/** + * Initialization takes boundary times, for slow light. Most analytic models won't use them. + */ +void init_model(double *tA, double *tB) +{ + // Set all the geometry globals we need + // TODO do this in geometry? Deal with model/geom interface... + use_eKS_internal = 0; + metric = METRIC_MKS; + hslope = 1.0; + + // We already set stuff from parameters, so set_units here + set_units(); + + fprintf(stderr, "Running RIAF model with a=%g, nth0=%g, Te0=%g, disk_h=%g, pow_nth=%g, pow_T=%g\n", + a, nth0, Te0, disk_h, pow_nth, pow_T); + fprintf(stderr, "Velocity model: Keplerian by %g, infall rate %g\n", + keplerian_factor, infall_factor); + // TODO B field when there are params +} + +void set_units() +{ + // Derive units we actually need + // We already have Te_unit as a parameter + MBH = MBH_solar * MSUN; + L_unit = GNEWT * MBH / (CL * CL); + T_unit = L_unit / CL; + + RHO_unit = Ne_unit * (MP + ME); + B_unit = CL * sqrt(4.*M_PI*RHO_unit); + M_unit = RHO_unit * pow(L_unit, 3); + + // Set all the geometry for coordinates.c + // TODO function like initialize_coordinates, that makes sure these are all set. + R0 = 0.; + Rh = 1 + sqrt(1. - a * a); + // These are for tracing. We only *emit* inside *_geo parameters + Rin = Rh; + Rout = rmax_geo; + + double z1 = 1. + pow(1. - a * a, 1. / 3.) * (pow(1. + a, 1. / 3.) + pow(1. - a, 1. / 3.)); + double z2 = sqrt(3. * a * a + z1 * z1); + r_isco = 3. + z2 - copysign(sqrt((3. - z1) * (3. + z1 + 2. * z2)), a); + startx[0] = 0.0; + startx[1] = log(Rin); + startx[2] = 0.0; + startx[3] = 0.0; + stopx[0] = 0.0; + stopx[1] = log(Rout); + stopx[2] = 1.0; + stopx[3] = 2*M_PI; +} + +void output_hdf5() +{ + hdf5_set_directory("/header/"); + double zero = 0; + hdf5_write_single_val(&zero, "t", H5T_IEEE_F64LE); + hdf5_write_single_val(&a, "a", H5T_IEEE_F64LE); + + // TODO output all riaf terms + + hdf5_make_directory("units"); + hdf5_set_directory("/header/units/"); + // TODO set M_unit correctly + hdf5_write_single_val(&zero, "M_unit", H5T_IEEE_F64LE); + hdf5_write_single_val(&L_unit, "L_unit", H5T_IEEE_F64LE); + hdf5_write_single_val(&T_unit, "T_unit", H5T_IEEE_F64LE); + + hdf5_set_directory("/"); +} + +//// INTERFACE: Functions called from elsewhere in ipole //// +double get_model_ne(double X[NDIM]) +{ + // Matter model defined in Gold et al 2020 section 3 + double r, th; + bl_coord(X, &r, &th); + double zc=r*cos(th); + double rc=r*sin(th); + return nth0 * exp(-zc*zc/2./rc/rc/disk_h/disk_h) * pow(r,pow_nth) * Ne_unit; +} + +double get_model_thetae(double X[NDIM]) +{ + double r, th; + bl_coord(X, &r, &th); + return Te0 * pow(r, pow_T) * Te_unit * KBOL / (ME*CL*CL); +} + +void get_model_fourv(double X[NDIM], double Kcon[NDIM], double Ucon[NDIM], double Ucov[NDIM], + double Bcon[NDIM], double Bcov[NDIM]) +{ + double r, th; + bl_coord(X, &r, &th); + + // Metrics: BL + double bl_gcov[NDIM][NDIM], bl_gcon[NDIM][NDIM]; + gcov_bl(r, th, bl_gcov); + gcon_func(bl_gcov, bl_gcon); + // Native + double gcov[NDIM][NDIM], gcon[NDIM][NDIM]; + gcov_func(X, gcov); + gcon_func(gcov, gcon); + + // Get the 4-velocity + double bl_Ucon[NDIM]; + double omegaK, omegaFF, omega; + double K, ur, ut; + if (r < Rh) { + // Inside r_h, none + bl_Ucov[0] = -1; + bl_Ucov[1] = 0.; + bl_Ucov[2] = 0.; + bl_Ucov[3] = 0.; + flip_index(bl_Ucov, bl_gcon, bl_Ucon); + } else if (r < r_isco) { + // Inside r_isco, freefall + double omegaK_isco = 1. / (pow(r_isco, 3./2) + a); + + // Get conserved quantities at the ISCO... + double bl_Ucon_isco[NDIM], bl_Ucov_isco[NDIM]; + bl_Ucon_isco[0] = 1.0; + bl_Ucon_isco[1] = 0.0; + bl_Ucon_isco[2] = 0.0; + bl_Ucon_isco[3] = omegaK_isco; + double bl_gcov_isco[NDIM][NDIM]; + gcov_bl(r, th, bl_gcov_isco); + normalize(bl_Ucon_isco, bl_gcov_isco); + flip_index(bl_Ucon_isco, bl_gcov_isco, bl_Ucov_isco); + double e = bl_Ucov_isco[0]; + double l = bl_Ucov_isco[3]; + // ...then set the infall velocity and find omega + bl_Ucov_isco[1] = -sqrt(fmax(0.0, + -(1.0 + bl_gcon[0][0] * e * e + + 2.0 * bl_gcon[0][3] * e * l + + bl_gcon[3][3] * l * l) / bl_gcov[1][1])); + bl_Ucov_isco[2] = 0.0; + flip_index(bl_Ucov_isco, bl_gcon, bl_Ucon_isco); + omegaK = bl_Ucon_isco[3] / bl_Ucon_isco[0]; + + omegaFF = bl_gcon[0][3] / bl_gcon[0][0]; + + // Compromise + omega = omegaK + (1 - keplerian_factor)*(omegaFF - omegaK); + // Set infall rate + double urFF = -sqrt(fmax(0.0, -(1.0 + bl_gcon[0][0]) * bl_gcon[1][1])); + ur = bl_Ucon_isco[1] + infall_factor * (urFF - bl_Ucon_isco[1]); + + // Get the normal observer velocity for Ucon/Ucov, in BL coordinates + K = bl_gcov[0][0] + 2*omega*bl_gcov[0][3] + omega*omega*bl_gcov[3][3]; + ut = sqrt(fmax(0.0, -(1. + ur*ur*bl_gcov[1][1]) / K)); + bl_Ucon[0] = ut; + bl_Ucon[1] = ur; + bl_Ucon[2] = 0.; + bl_Ucon[3] = omega * ut; + } else { + // Outside r_isco, Keplerian + omegaK = 1. / (pow(r, 3./2) + a); + omegaFF = bl_gcon[0][3] / bl_gcon[0][0]; + + // Compromise + omega = omegaK + (1 - keplerian_factor)*(omegaFF - omegaK); + // Set infall rate + ur = infall_factor * -sqrt(fmax(0.0, -(1.0 + bl_gcon[0][0]) * bl_gcon[1][1])); + + // Get the normal observer velocity for Ucon/Ucov, in BL coordinates + K = bl_gcov[0][0] + 2*omega*bl_gcov[0][3] + omega*omega*bl_gcov[3][3]; + ut = sqrt(fmax(0.0, -(1. + ur*ur*bl_gcov[1][1]) / K)); + bl_Ucon[0] = ut; + bl_Ucon[1] = ur; + bl_Ucon[2] = 0.; + bl_Ucon[3] = omega * ut; + } + //normalize(bl_Ucon, bl_gcov); + flip_index(bl_Ucon, bl_gcov, bl_Ucov); + + + // Transform to KS coordinates, + double ks_Ucon[NDIM]; + bl_to_ks(X, bl_Ucon, ks_Ucon); + // then to our coordinates, + vec_from_ks(X, ks_Ucon, Ucon); + + // and grab Ucov + flip_index(Ucon, gcov, Ucov); + + // Check +#if DEBUG + if (get_fluid_nu(Kcon, Ucov) == 1. || ut == 0.) { + double bl_Ucov[NDIM]; + flip_index(bl_Ucon, bl_gcov, bl_Ucov); + fprintf(stderr, "RIAF model problem at r, th, phi = %g %g %g\n", r, th, X[3]); + fprintf(stderr, "Omega K: %g FF: %g Final: %g K: %g ur: %g ut: %g\n", + omegaK, omegaFF, omega, K, ur, ut); + fprintf(stderr, "K1: %g K2: %g K3: %g\n", bl_gcov[0][0], 2*omega*bl_gcov[0][3], omega*omega*bl_gcov[3][3]); + fprintf(stderr, "Ucon BL: %g %g %g %g\n", bl_Ucon[0], bl_Ucon[1], bl_Ucon[2], bl_Ucon[3]); + fprintf(stderr, "Ucon KS: %g %g %g %g\n", ks_Ucon[0], ks_Ucon[1], ks_Ucon[2], ks_Ucon[3]); + fprintf(stderr, "Ucon native: %g %g %g %g\n", Ucon[0], Ucon[1], Ucon[2], Ucon[3]); + fprintf(stderr, "Ucov: %g %g %g %g\n", Ucov[0], Ucov[1], Ucov[2], Ucov[3]); + fprintf(stderr, "Ubl.Ubl: %g\n", bl_Ucov[0]*bl_Ucon[0]+bl_Ucov[1]*bl_Ucon[1]+ + bl_Ucov[2]*bl_Ucon[2]+bl_Ucov[3]*bl_Ucon[3]); + fprintf(stderr, "U.U: %g\n", Ucov[0]*Ucon[0]+Ucov[1]*Ucon[1]+Ucov[2]*Ucon[2]+Ucov[3]*Ucon[3]); + } +#endif + + // Use pure toroidal field, + // See Themis src/VRT2/src/AccretionFlows/mf_toroidal_beta.cpp/h + double bl_Bcon[NDIM]; + bl_Bcon[0] = 0.0; + bl_Bcon[1] = 0.0; + bl_Bcon[2] = 0.0; + bl_Bcon[3] = 1.0; + + // Transform to KS coordinates, + double ks_Bcon[NDIM]; + bl_to_ks(X, bl_Bcon, ks_Bcon); + // then to our coordinates, + vec_from_ks(X, ks_Bcon, Bcon); + normalize(Bcon, gcov); + + // Compute u.b and subtract it, normalize to get_model_b + //project_out(Bcon, Ucon, gcov); ? + double BdotU = 0; + MULOOP BdotU += Bcon[mu] * Ucov[mu]; + MULOOP Bcon[mu] += BdotU * Ucon[mu]; + flip_index(Bcon, gcov, Bcov); + double Bsq = 0; + MULOOP Bsq += Bcon[mu] * Bcov[mu]; + double bmag = fmax(get_model_b(X), 1e-10); + MULOOP Bcon[mu] *= bmag / sqrt(Bsq); + + flip_index(Bcon, gcov, Bcov); +} + +/** + * This problem defines no field in emission, but we want to control ipole's worst + * tendencies when making tetrads. This will return a correct value even for a + * possible fluid/field model later, too. + */ +double get_model_b(double X[NDIM]) +{ + // double Ucon[NDIM],Bcon[NDIM]; + // double Ucov[NDIM],Bcov[NDIM]; + // double Kcon[NDIM] = {0}; // TODO interface change if we ever need a real one here + // get_model_fourv(X, Kcon, Ucon, Ucov, Bcon, Bcov); + // return sqrt(Bcon[0]*Bcov[0] + Bcon[1]*Bcov[1] + Bcon[2]*Bcov[2] + Bcon[3]*Bcov[3]) * B_unit; + + double r, th; + bl_coord(X, &r, &th); + double nth = get_model_ne(X); + // Get local field strength + double eps = 0.1; + double b = sqrt(8. * M_PI * eps * nth * MP * CL * CL / 6. / r); + if (b == 0) b = 1.e-6; + return b; +} + +int radiating_region(double X[NDIM]) +{ + // If you don't want conditionals in get_model_jar, + // you can control here where the coefficients are applied + double r, th; + bl_coord(X, &r, &th); + return r > Rh + 0.1 && r > rmin_geo && r < Rout; +} + +//// STUBS: Functions for normal models which we don't use //// +// Define these to specify a fluid model: e- density/temperature for +// synchrotron radiation based on an energy distribution +void get_model_powerlaw_vals(double X[NDIM], double *p, double *n, + double *gamma_min, double *gamma_max, double *gamma_cut) {return;} +// This is only called for trace file output, and doesn't really apply to analytic models +void get_model_primitives(double X[NDIM], double *p) {return;} +void get_model_jk(double X[NDIM], double Kcon[NDIM], double *jnuinv, double *knuinv) {return;} +void get_model_jar(double X[NDIM], double Kcon[NDIM], + double *jI, double *jQ, double *jU, double *jV, + double *aI, double *aQ, double *aU, double *aV, + double *rQ, double *rU, double *rV) {return;} diff --git a/model/riaf/model_params.h b/model/riaf/model_params.h new file mode 100644 index 0000000..2a7169e --- /dev/null +++ b/model/riaf/model_params.h @@ -0,0 +1,15 @@ + +#ifndef MODEL_PARAMS_H +#define MODEL_PARAMS_H + +#include "decs.h" + +// No sense using slow light with a static model +#define SLOW_LIGHT (0) + +#define THIN_DISK (0) + +// Necessary model parameters, can be used or not +extern double rmax_geo; + +#endif /* MODEL_PARAMS_H */ diff --git a/src/geometry.c b/src/geometry.c index 6d05e12..2b82e81 100644 --- a/src/geometry.c +++ b/src/geometry.c @@ -165,6 +165,27 @@ void normalize(double vcon[NDIM], double Gcov[NDIM][NDIM]) return; } +/* + * Normalize input vector so that |v . v| = 1 + * Overwrites input + */ +void normalize_to(double vcon[NDIM], double Gcov[NDIM][NDIM], double target) +{ + int k, l; + double norm; + + norm = 0.; + for (k = 0; k < 4; k++) + for (l = 0; l < 4; l++) + norm += vcon[k] * vcon[l] * Gcov[k][l]; + + norm = sqrt(fabs(norm)); + for (k = 0; k < 4; k++) + vcon[k] *= target/norm; + + return; +} + /* normalize null vector in a tetrad frame */ void null_normalize(double Kcon[NDIM], double fnorm) { diff --git a/src/geometry.h b/src/geometry.h index a43ffbf..c9b61cb 100644 --- a/src/geometry.h +++ b/src/geometry.h @@ -21,7 +21,7 @@ inline void raise(double *ucov, double Gcon[NDIM][NDIM], double *ucon) {flip_ind void null_normalize(double Kcon[NDIM], double fnorm); void normalize(double *vcon, double gcov[][NDIM]); - +void normalize_to(double vcon[NDIM], double Gcov[NDIM][NDIM], double target); int invert_matrix(double Am[][NDIM], double Aminv[][NDIM]); double theta_func(double X[NDIM]); diff --git a/src/radiation.c b/src/radiation.c index ade8238..535906d 100644 --- a/src/radiation.c +++ b/src/radiation.c @@ -40,10 +40,10 @@ double get_fluid_nu(double Kcon[NDIM], double Ucov[NDIM]) * ME * CL * CL / HPL; if (nu < 0.) { - nu = 1.; #if DEBUG - fprintf(stderr, "Fluid nu < 0: %g !", nu); + fprintf(stderr, "Fluid nu < 0: %g!\n", nu); #endif + nu = 1.; } if (isnan(nu)) { @@ -77,8 +77,11 @@ double get_bk_angle(double X[NDIM], double Kcon[NDIM], double Ucov[NDIM], double if (fabs(mu) > 1.) mu /= fabs(mu); - if (isnan(mu)) + if (isnan(mu)) { fprintf(stderr, "isnan get_bk_angle\n"); + fprintf(stderr, "B = %g, k = %g, mu = %g\n", B, k, mu); + fprintf(stderr, "Ucov: %g %g %g %g\n", Ucov[0], Ucov[1], Ucov[2], Ucov[3]); + } return (acos(mu)); } From 966ed5688805f429aa9871ec7424f5f1c535fd20 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Fri, 16 Apr 2021 23:58:01 -0500 Subject: [PATCH 14/93] Finish cleaning up some debugging-only code --- model/riaf/model.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/model/riaf/model.c b/model/riaf/model.c index 385a9b7..2c6ac5d 100644 --- a/model/riaf/model.c +++ b/model/riaf/model.c @@ -193,6 +193,7 @@ void get_model_fourv(double X[NDIM], double Kcon[NDIM], double Ucon[NDIM], doubl double K, ur, ut; if (r < Rh) { // Inside r_h, none + double bl_Ucov[NDIM]; bl_Ucov[0] = -1; bl_Ucov[1] = 0.; bl_Ucov[2] = 0.; @@ -256,9 +257,6 @@ void get_model_fourv(double X[NDIM], double Kcon[NDIM], double Ucon[NDIM], doubl bl_Ucon[2] = 0.; bl_Ucon[3] = omega * ut; } - //normalize(bl_Ucon, bl_gcov); - flip_index(bl_Ucon, bl_gcov, bl_Ucov); - // Transform to KS coordinates, double ks_Ucon[NDIM]; From 0255cf638ec7ed26b57ba700ed333769789e3772 Mon Sep 17 00:00:00 2001 From: George Wong Date: Sat, 17 Apr 2021 15:40:11 -0500 Subject: [PATCH 15/93] add base thermal sed for comparison purposes. agrees with draft after dsrc=2.6228263e22 adjustment --- tests/isothermal_sphere/thermal.txt | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 tests/isothermal_sphere/thermal.txt diff --git a/tests/isothermal_sphere/thermal.txt b/tests/isothermal_sphere/thermal.txt new file mode 100644 index 0000000..ab69f8f --- /dev/null +++ b/tests/isothermal_sphere/thermal.txt @@ -0,0 +1,21 @@ +# frequency inc Ftot Ftot_unpol +11090300000.0 90.0 4.274095293683708 4.285141150253394 +14180300000.0 90.0 6.9857805885842 7.005553015786133 +18606400000.0 90.0 12.012821992017324 12.059528765554925 +23790600000.0 90.0 19.54640531961427 19.70379507034827 +30419100000.0 90.0 31.28297333819569 32.13698917863901 +39400900000.0 90.0 48.1015970375206 53.320419990110096 +49731200000.0 90.0 64.97950577222639 81.0972597360478 +65253900000.0 90.0 85.10330208718378 111.62668339195066 +81304100000.0 90.0 93.56264610006741 114.37655638692958 +106682000000.0 90.0 81.45955452754392 90.16662198582216 +134652000000.0 90.0 59.33311574585277 62.502869413715594 +176681000000.0 90.0 35.0587011109502 35.98022561906339 +223005000000.0 90.0 20.290627321266705 20.639702266728055 +288851000000.0 90.0 10.135085073657121 10.268643837071648 +369331000000.0 90.0 4.830590682670228 4.88653823612203 +478382000000.0 90.0 2.02472024931681 2.046375251404457 +603808000000.0 90.0 0.8510126468565906 0.8596347403983207 +792276000000.0 90.0 0.2774776814456344 0.2801334437120814 +974460000000.0 90.0 0.10843946584676074 0.10943464661785053 +1295270000000.0 90.0 0.026111781782012834 0.026338110861036445 From 0589d51fbd7a55109a3531812e277c968ef24493 Mon Sep 17 00:00:00 2001 From: George Wong Date: Sat, 17 Apr 2021 15:53:32 -0500 Subject: [PATCH 16/93] add comparison inc data for thermal edf --- tests/isothermal_sphere/info | 2 ++ tests/isothermal_sphere/thermal.txt | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 tests/isothermal_sphere/info diff --git a/tests/isothermal_sphere/info b/tests/isothermal_sphere/info new file mode 100644 index 0000000..c5a18e9 --- /dev/null +++ b/tests/isothermal_sphere/info @@ -0,0 +1,2 @@ +# note maxnstep is because the tests were done using regular (non-exponential) KS. performed on commit a2217b5ff432fa5c10080938af9bd14c79151969 +./ipole --freqcgs={freqcgs} --dsource=8000 --thetacam={inc} --maxnstep=1000000 --nx=80 --ny=80 --fov=1200 diff --git a/tests/isothermal_sphere/thermal.txt b/tests/isothermal_sphere/thermal.txt index ab69f8f..c31c595 100644 --- a/tests/isothermal_sphere/thermal.txt +++ b/tests/isothermal_sphere/thermal.txt @@ -19,3 +19,21 @@ 792276000000.0 90.0 0.2774776814456344 0.2801334437120814 974460000000.0 90.0 0.10843946584676074 0.10943464661785053 1295270000000.0 90.0 0.026111781782012834 0.026338110861036445 +223005000000.0 5.0 0.0006679335465425816 0.0006729087303241369 +223005000000.0 10.0 0.011147781326921112 0.011243757992862929 +223005000000.0 15.0 0.06985389871859848 0.07051104612184096 +223005000000.0 20.0 0.2534654322979531 0.25600553123915953 +223005000000.0 25.0 0.6569199260451928 0.6638586790906905 +223005000000.0 30.0 1.3642860033739146 1.379419742834962 +223005000000.0 35.0 2.4279369207794392 2.456214174743373 +223005000000.0 40.0 3.8587867482340723 3.906017381001425 +223005000000.0 45.0 5.625384717516792 5.69780637305197 +223005000000.0 50.0 7.658824204761203 7.762525267059334 +223005000000.0 55.0 9.861269153583539 10.001493827025799 +223005000000.0 60.0 12.116053290604645 12.296450122374356 +223005000000.0 65.0 14.297971883563548 14.519886530032826 +223005000000.0 70.0 16.283430376971037 16.54536901876291 +223005000000.0 75.0 17.959097056933977 18.256478565738337 +223005000000.0 80.0 19.229220385111592 19.554457649520156 +223005000000.0 85.0 20.021415892011337 20.36438676967305 +223005000000.0 90.0 20.290627321266705 20.639702266728055 From 6ffe54c341ea32dea5f2f0866c4041a712045d1a Mon Sep 17 00:00:00 2001 From: George Wong Date: Sat, 17 Apr 2021 17:22:22 -0500 Subject: [PATCH 17/93] add kappa=4 isothermal sphere comparison values. (agrees with draft like thermal) --- tests/isothermal_sphere/info | 6 ++++- tests/isothermal_sphere/kappa4.txt | 39 ++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 tests/isothermal_sphere/kappa4.txt diff --git a/tests/isothermal_sphere/info b/tests/isothermal_sphere/info index c5a18e9..2d64bb2 100644 --- a/tests/isothermal_sphere/info +++ b/tests/isothermal_sphere/info @@ -1,2 +1,6 @@ -# note maxnstep is because the tests were done using regular (non-exponential) KS. performed on commit a2217b5ff432fa5c10080938af9bd14c79151969 +# note maxnstep is because the tests were done using regular (non-exponential) KS. + +# #thermal performed on commit a2217b5ff432fa5c10080938af9bd14c79151969 ./ipole --freqcgs={freqcgs} --dsource=8000 --thetacam={inc} --maxnstep=1000000 --nx=80 --ny=80 --fov=1200 + +# when doing kappa4, add "--emission_type=2 --kappa=4" diff --git a/tests/isothermal_sphere/kappa4.txt b/tests/isothermal_sphere/kappa4.txt new file mode 100644 index 0000000..644a20e --- /dev/null +++ b/tests/isothermal_sphere/kappa4.txt @@ -0,0 +1,39 @@ +# frequency inc Ftot Ftot_unpol +11090300000.0 90.0 5.536220269736517 4.643855039969408 +14180300000.0 90.0 9.816839938505414 8.144120854755604 +18606400000.0 90.0 18.410962274096203 15.204680005731683 +23790600000.0 90.0 31.595838718702307 26.7931177639978 +30419100000.0 90.0 49.97781209518265 47.01985585568236 +39400900000.0 90.0 73.399959477185 82.30598712383984 +49731200000.0 90.0 99.4436481652677 122.9673634555481 +65253900000.0 90.0 131.38846383064052 157.85198086034558 +81304100000.0 90.0 145.9185182923764 164.44680988687367 +106682000000.0 90.0 143.34391579665498 152.14055861823545 +134652000000.0 90.0 129.45492553023746 133.52287553057002 +176681000000.0 90.0 108.75550202730732 110.33424586467278 +223005000000.0 90.0 91.30391456702186 92.02508329529913 +288851000000.0 90.0 74.0592358983554 74.39307422913704 +369331000000.0 90.0 60.17325878299614 60.358827662207545 +478382000000.0 90.0 48.06670547258849 48.183239014178945 +603808000000.0 90.0 39.113393303429476 39.19795197877398 +792276000000.0 90.0 30.637103958874302 30.699241954043053 +974460000000.0 90.0 25.375734031100432 25.426055738070556 +1295270000000.0 90.0 19.52853758462853 19.5667240140286 +223005000000.0 5.0 1.344054064998482 1.3461306790365162 +223005000000.0 10.0 3.8232186755454256 3.8306162851355574 +223005000000.0 15.0 7.714864785809941 7.731670331663487 +223005000000.0 20.0 12.81426223223759 12.845701265871375 +223005000000.0 25.0 18.909371222719386 18.96215577589628 +223005000000.0 30.0 25.77789665451725 25.860240852279684 +223005000000.0 35.0 33.19062029924153 33.31190769590544 +223005000000.0 40.0 40.91508655964425 41.08520820070822 +223005000000.0 45.0 48.72112759151631 48.949598979587805 +223005000000.0 50.0 56.38506358365022 56.68000395047354 +223005000000.0 55.0 63.695415331829096 64.06254532647971 +223005000000.0 60.0 70.45662186754525 70.8983931833725 +223005000000.0 65.0 76.4918012848629 77.00676660806042 +223005000000.0 70.0 81.64723079267345 82.22973265184413 +223005000000.0 75.0 85.79405069875565 86.43430616151178 +223005000000.0 80.0 88.83004937937841 89.51445619246599 +223005000000.0 85.0 90.68153848173485 91.39335199180691 +223005000000.0 90.0 91.30391456702186 92.02508329529913 From 1995a10d241c0921e241a0277c77a2fd1f3bc378 Mon Sep 17 00:00:00 2001 From: George Wong Date: Sat, 17 Apr 2021 18:18:06 -0500 Subject: [PATCH 18/93] fix bug in parameter load --- model/iharm/model.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/model/iharm/model.c b/model/iharm/model.c index 8a55c74..8ed029f 100644 --- a/model/iharm/model.c +++ b/model/iharm/model.c @@ -119,11 +119,12 @@ void try_set_model_parameter(const char *word, const char *value) set_by_word_val(word, value, "sigma_cut", &sigma_cut, TYPE_DBL); set_by_word_val(word, value, "beta_crit", &beta_crit, TYPE_DBL); + // TODO: figure out how to make consistent with model_radiation.c set_by_word_val(word, value, "powerlaw_gamma_min", &powerlaw_gamma_min, TYPE_DBL); set_by_word_val(word, value, "powerlaw_gamma_max", &powerlaw_gamma_max, TYPE_DBL); set_by_word_val(word, value, "powerlaw_gamma_cut", &powerlaw_gamma_cut, TYPE_DBL); - set_by_word_val(word, value, "powerlaw_eta", &trat_large, TYPE_DBL); - set_by_word_val(word, value, "powerlaw_p", &trat_large, TYPE_DBL); + set_by_word_val(word, value, "powerlaw_eta", &powerlaw_eta, TYPE_DBL); + set_by_word_val(word, value, "powerlaw_p", &powerlaw_p, TYPE_DBL); set_by_word_val(word, value, "rmax_geo", &rmax_geo, TYPE_DBL); set_by_word_val(word, value, "rmin_geo", &rmin_geo, TYPE_DBL); From 284a6107232ff44452a73278dea9deb6ab89f76e Mon Sep 17 00:00:00 2001 From: George Wong Date: Sat, 17 Apr 2021 18:19:23 -0500 Subject: [PATCH 19/93] attempt to add edf parameters in reasonable way. this powerlaw abs seems to disagree with draft here --- model/sphere/model.c | 22 ++++++++++++- src/model_radiation.c | 76 ++++++++++++++++++++++++++++++++++++++----- src/model_radiation.h | 2 ++ src/par.c | 4 +++ 4 files changed, 95 insertions(+), 9 deletions(-) diff --git a/model/sphere/model.c b/model/sphere/model.c index 3580b67..b0f8053 100644 --- a/model/sphere/model.c +++ b/model/sphere/model.c @@ -26,6 +26,12 @@ static double MODEL_MBH = 4.1e6; static double MODEL_TP_OVER_TE = 3; static double MODEL_GAM = 13./9; +// TODO: duplicated code! +static double powerlaw_gamma_min = 1e2; +static double powerlaw_gamma_max = 1e5; +static double powerlaw_gamma_cut = 1e10; +static double powerlaw_p = 3.25; + // derived model parameters double model_Ne_0 = 1.; double model_B_0 = 1.; @@ -39,6 +45,12 @@ void try_set_model_parameter(const char *word, const char *value) set_by_word_val(word, value, "gam", &MODEL_GAM, TYPE_DBL); set_by_word_val(word, value, "tp_over_te", &MODEL_TP_OVER_TE, TYPE_DBL); set_by_word_val(word, value, "MBH", &MODEL_MBH, TYPE_DBL); + + // TODO: figure out how to make consistent with model_radiation.c + set_by_word_val(word, value, "powerlaw_gamma_min", &powerlaw_gamma_min, TYPE_DBL); + set_by_word_val(word, value, "powerlaw_gamma_max", &powerlaw_gamma_max, TYPE_DBL); + set_by_word_val(word, value, "powerlaw_gamma_cut", &powerlaw_gamma_cut, TYPE_DBL); + set_by_word_val(word, value, "powerlaw_p", &powerlaw_p, TYPE_DBL); } // used in slow light with real data @@ -199,7 +211,15 @@ int radiating_region(double X[NDIM]) void get_model_powerlaw_vals(double X[NDIM], double *p, double *n, double *gamma_min, double *gamma_max, double *gamma_cut) -{ assert(1 == 0); } +{ + // TODO figure out how to make this all consistent + //assert(1 == 0); + + *gamma_min = powerlaw_gamma_min; + *gamma_max = powerlaw_gamma_max; + *gamma_cut = powerlaw_gamma_cut; + *p = powerlaw_p; +} // In case we want to mess with emissivities directly void get_model_jar(double X[NDIM], double Kcon[NDIM], diff --git a/src/model_radiation.c b/src/model_radiation.c index e6fd5c7..85b78c1 100644 --- a/src/model_radiation.c +++ b/src/model_radiation.c @@ -66,6 +66,25 @@ void jar_calc_dist(int dist, double X[NDIM], double Kcon[NDIM], double *aI, double *aQ, double *aU, double *aV, double *rQ, double *rU, double *rV); +/** + * Optionally load radiation model parameters + */ +static double model_kappa = 0; +static double powerlaw_gamma_cut = 1e10; +static double powerlaw_gamma_min = 1e2; +static double powerlaw_gamma_max = 1e5; +static double powerlaw_p = 3.25; + +void try_set_radiation_parameter(const char *word, const char *value) +{ + set_by_word_val(word, value, "kappa", &model_kappa, TYPE_DBL); + + set_by_word_val(word, value, "powerlaw_gamma_cut", &powerlaw_gamma_cut, TYPE_DBL); + set_by_word_val(word, value, "powerlaw_gamma_min", &powerlaw_gamma_min, TYPE_DBL); + set_by_word_val(word, value, "powerlaw_gamma_max", &powerlaw_gamma_max, TYPE_DBL); + set_by_word_val(word, value, "powerlaw_p", &powerlaw_p, TYPE_DBL); +} + /** * Wrapper to call different distributions at different places in the simulation domain * See jar_calc_dist for distributions @@ -188,7 +207,9 @@ void jar_calc_dist(int dist, double X[NDIM], double Kcon[NDIM], case E_KAPPA: setConstParams(¶msM); fit = paramsM.KAPPA_DIST; - // TODO get_model_kappa + Thetae = get_model_thetae(X); + kappa = model_kappa; + kappa_width = (kappa - 3.) / kappa * Thetae; break; case E_POWERLAW: setConstParams(¶msM); @@ -524,14 +545,53 @@ void get_jkinv(double X[NDIM], double Kcon[NDIM], double *jnuinv, double *knuinv Thetae = get_model_thetae(X); /* temp in e rest-mass units */ nu = get_fluid_nu(Kcon, Ucov); /* freq in Hz */ - /* assume emission is thermal */ - Bnuinv = Bnu_inv(nu, Thetae); - *jnuinv = jnu_inv(nu, Thetae, Ne, B, theta); + // TODO: could be cleaner here + struct parameters paramsM; + int fit; + double kappa, kappa_width; + + if (params->emission_type == E_KAPPA) { + + setConstParams(¶msM); + fit = paramsM.KAPPA_DIST; + + kappa = model_kappa; + kappa_width = (kappa - 3.) / kappa * Thetae; - if (Bnuinv < SMALL) - *knuinv = SMALL; - else - *knuinv = *jnuinv / Bnuinv; + *jnuinv = j_nu_fit(nu, B, Ne, theta, fit, paramsM.STOKES_I, Thetae, + powerlaw_p, powerlaw_gamma_min, powerlaw_gamma_max, powerlaw_gamma_cut, + kappa, kappa_width) / nu/nu; + *knuinv = alpha_nu_fit(nu, B, Ne, theta, fit, paramsM.STOKES_I, Thetae, + powerlaw_p, powerlaw_gamma_min, powerlaw_gamma_max, powerlaw_gamma_cut, + kappa, kappa_width) * nu; + + } else if (params->emission_type == E_POWERLAW) { + + setConstParams(¶msM); + fit = paramsM.POWER_LAW; + + get_model_powerlaw_vals(X, &powerlaw_p, &Ne, &powerlaw_gamma_min, &powerlaw_gamma_max, &powerlaw_gamma_cut); + + *jnuinv = j_nu_fit(nu, B, Ne, theta, fit, paramsM.STOKES_I, Thetae, + powerlaw_p, powerlaw_gamma_min, powerlaw_gamma_max, powerlaw_gamma_cut, + kappa, kappa_width) / nu/nu; + *knuinv = alpha_nu_fit(nu, B, Ne, theta, fit, paramsM.STOKES_I, Thetae, + powerlaw_p, powerlaw_gamma_min, powerlaw_gamma_max, powerlaw_gamma_cut, + kappa, kappa_width) * nu * exp(-nu / 5.e13); // nu_cutoff = 5.e13 + + } else { + + /* assume emission is thermal */ + Bnuinv = Bnu_inv(nu, Thetae); + *jnuinv = jnu_inv(nu, Thetae, Ne, B, theta); + + if (Bnuinv < SMALL) { + *knuinv = SMALL; + } else { + *knuinv = *jnuinv / Bnuinv; + } + + } #if DEBUG if (isnan(*jnuinv) || isnan(*knuinv)) { diff --git a/src/model_radiation.h b/src/model_radiation.h index a72bee8..9f29e94 100644 --- a/src/model_radiation.h +++ b/src/model_radiation.h @@ -11,6 +11,8 @@ #include "decs.h" #include "par.h" +void try_set_radiation_parameter(const char *word, const char *value); + /* transfer coefficients in tetrad frame */ void jar_calc(double X[NDIM], double Kcon[NDIM], double *jI, double *jQ, double *jU, double *jV, double *aI, double *aQ, double *aU, diff --git a/src/par.c b/src/par.c index 4045d62..79dff6c 100644 --- a/src/par.c +++ b/src/par.c @@ -2,6 +2,7 @@ #include "par.h" #include "decs.h" #include "model.h" +#include "model_radiation.h" #include #include @@ -182,6 +183,9 @@ void try_set_parameter(const char *word, const char *value, Params *params) { // Let models add/parse their own parameters we don't understand try_set_model_parameter(word, value); + + // Let radiation model load its own parameters + try_set_radiation_parameter(word, value); } // sets default values for elements of params (if desired) and loads from par file 'fname' From 86646991814adcd6221a9ed4308fb8cfdc04737a Mon Sep 17 00:00:00 2001 From: George Wong Date: Sat, 17 Apr 2021 18:41:44 -0500 Subject: [PATCH 20/93] add (maybe incorrect) powerlaw fluxes --- tests/isothermal_sphere/powerlaw.txt | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 tests/isothermal_sphere/powerlaw.txt diff --git a/tests/isothermal_sphere/powerlaw.txt b/tests/isothermal_sphere/powerlaw.txt new file mode 100644 index 0000000..680ccc4 --- /dev/null +++ b/tests/isothermal_sphere/powerlaw.txt @@ -0,0 +1,21 @@ +# frequency inc Ftot Ftot_unpol +11090300000.0 90.0 2.5681664625857445 2.357442595096737 +14180300000.0 90.0 4.747505918482114 4.358361769368128 +18606400000.0 90.0 9.359864867269868 8.596108910850063 +23790600000.0 90.0 17.275754835927415 15.89110839133326 +30419100000.0 90.0 31.670483590409923 29.358670187882677 +39400900000.0 90.0 57.649983519664154 55.832068170597445 +49731200000.0 90.0 90.07825940264141 98.00639662746664 +65253900000.0 90.0 136.849889347324 170.65026690645294 +81304100000.0 90.0 178.24258350881462 219.32381544087414 +106682000000.0 90.0 204.12113909009534 229.43008993707176 +134652000000.0 90.0 193.94893269177425 205.8492703343728 +176681000000.0 90.0 162.91325494834996 167.12927813291785 +223005000000.0 90.0 133.7469112856878 135.44332085703087 +288851000000.0 90.0 105.03883022763404 105.70174095627883 +369331000000.0 90.0 82.70586275734347 83.01956208730132 +478382000000.0 90.0 64.04126365043531 64.21347717470563 +603808000000.0 90.0 50.79508572284754 50.910434487222304 +792276000000.0 90.0 38.732981254624754 38.813171043137025 +974460000000.0 90.0 31.49708141331971 31.56009808674695 +1295270000000.0 90.0 23.698433065525567 23.74491620336421 From 48981ffe63f287f008e489478e2925a4410e4367 Mon Sep 17 00:00:00 2001 From: George Wong Date: Sat, 17 Apr 2021 23:07:38 -0500 Subject: [PATCH 21/93] add nuLnu to test output --- tests/isothermal_sphere/kappa4.txt | 76 ++++++++++++++-------------- tests/isothermal_sphere/thermal.txt | 78 ++++++++++++++--------------- 2 files changed, 77 insertions(+), 77 deletions(-) diff --git a/tests/isothermal_sphere/kappa4.txt b/tests/isothermal_sphere/kappa4.txt index 644a20e..7b65d85 100644 --- a/tests/isothermal_sphere/kappa4.txt +++ b/tests/isothermal_sphere/kappa4.txt @@ -1,39 +1,39 @@ # frequency inc Ftot Ftot_unpol -11090300000.0 90.0 5.536220269736517 4.643855039969408 -14180300000.0 90.0 9.816839938505414 8.144120854755604 -18606400000.0 90.0 18.410962274096203 15.204680005731683 -23790600000.0 90.0 31.595838718702307 26.7931177639978 -30419100000.0 90.0 49.97781209518265 47.01985585568236 -39400900000.0 90.0 73.399959477185 82.30598712383984 -49731200000.0 90.0 99.4436481652677 122.9673634555481 -65253900000.0 90.0 131.38846383064052 157.85198086034558 -81304100000.0 90.0 145.9185182923764 164.44680988687367 -106682000000.0 90.0 143.34391579665498 152.14055861823545 -134652000000.0 90.0 129.45492553023746 133.52287553057002 -176681000000.0 90.0 108.75550202730732 110.33424586467278 -223005000000.0 90.0 91.30391456702186 92.02508329529913 -288851000000.0 90.0 74.0592358983554 74.39307422913704 -369331000000.0 90.0 60.17325878299614 60.358827662207545 -478382000000.0 90.0 48.06670547258849 48.183239014178945 -603808000000.0 90.0 39.113393303429476 39.19795197877398 -792276000000.0 90.0 30.637103958874302 30.699241954043053 -974460000000.0 90.0 25.375734031100432 25.426055738070556 -1295270000000.0 90.0 19.52853758462853 19.5667240140286 -223005000000.0 5.0 1.344054064998482 1.3461306790365162 -223005000000.0 10.0 3.8232186755454256 3.8306162851355574 -223005000000.0 15.0 7.714864785809941 7.731670331663487 -223005000000.0 20.0 12.81426223223759 12.845701265871375 -223005000000.0 25.0 18.909371222719386 18.96215577589628 -223005000000.0 30.0 25.77789665451725 25.860240852279684 -223005000000.0 35.0 33.19062029924153 33.31190769590544 -223005000000.0 40.0 40.91508655964425 41.08520820070822 -223005000000.0 45.0 48.72112759151631 48.949598979587805 -223005000000.0 50.0 56.38506358365022 56.68000395047354 -223005000000.0 55.0 63.695415331829096 64.06254532647971 -223005000000.0 60.0 70.45662186754525 70.8983931833725 -223005000000.0 65.0 76.4918012848629 77.00676660806042 -223005000000.0 70.0 81.64723079267345 82.22973265184413 -223005000000.0 75.0 85.79405069875565 86.43430616151178 -223005000000.0 80.0 88.83004937937841 89.51445619246599 -223005000000.0 85.0 90.68153848173485 91.39335199180691 -223005000000.0 90.0 91.30391456702186 92.02508329529913 +11090300000.0 90.0 5.536220269736517 4.643855039969408 4.7016219103608126e+33 3.94378285918504e+33 +14180300000.0 90.0 9.816839938505414 8.144120854755604 1.0659778367342523e+34 8.84342862391257e+33 +18606400000.0 90.0 18.410962274096203 15.204680005731683 2.62319084232761e+34 2.1663602780650973e+34 +23790600000.0 90.0 31.595838718702307 26.7931177639978 5.756073484066856e+34 4.881122355696168e+34 +30419100000.0 90.0 49.97781209518265 47.01985585568236 1.1641652905517959e+35 1.0952637152199442e+35 +39400900000.0 90.0 73.399959477185 82.30598712383984 2.2145883377755782e+35 2.4832967281163016e+35 +49731200000.0 90.0 99.4436481652677 122.9673634555481 3.78701507014235e+35 4.682845683295018e+35 +65253900000.0 90.0 131.38846383064052 157.85198086034558 6.565302697261962e+35 7.88764862222926e+35 +81304100000.0 90.0 145.9185182923764 164.44680988687367 9.084769316154148e+35 1.0238325814179797e+36 +106682000000.0 90.0 143.34391579665498 152.14055861823545 1.1710123068086322e+36 1.2428742825710588e+36 +134652000000.0 90.0 129.45492553023746 133.52287553057002 1.3348191661467184e+36 1.3767640948941546e+36 +176681000000.0 90.0 108.75550202730732 110.33424586467278 1.471404677852043e+36 1.492764250693207e+36 +223005000000.0 90.0 91.30391456702186 92.02508329529913 1.5591756533824877e+36 1.5714908835502518e+36 +288851000000.0 90.0 74.0592358983554 74.39307422913704 1.6381141003119254e+36 1.6454982607106167e+36 +369331000000.0 90.0 60.17325878299614 60.358827662207545 1.701807135405635e+36 1.707055354450744e+36 +478382000000.0 90.0 48.06670547258849 48.183239014178945 1.760800823680635e+36 1.765069731940552e+36 +603808000000.0 90.0 39.113393303429476 39.19795197877398 1.8084870534511828e+36 1.812396795273662e+36 +792276000000.0 90.0 30.637103958874302 30.699241954043053 1.8587254985745693e+36 1.8624953547661173e+36 +974460000000.0 90.0 25.375734031100432 25.426055738070556 1.8935364811318213e+36 1.897291485334812e+36 +1295270000000.0 90.0 19.52853758462853 19.5667240140286 1.9369618856392193e+36 1.940749453344996e+36 +223005000000.0 5.0 1.344054064998482 1.3461306790365162 2.2952097781493307e+34 2.298755963507242e+34 +223005000000.0 10.0 3.8232186755454256 3.8306162851355574 6.5288213596712e+34 6.541454085026799e+34 +223005000000.0 15.0 7.714864785809941 7.731670331663487 1.3174494653614186e+35 1.3203193092296592e+35 +223005000000.0 20.0 12.81426223223759 12.845701265871375 2.188261673479229e+35 2.193630443924628e+35 +223005000000.0 25.0 18.909371222719386 18.96215577589628 3.2291092195826303e+35 3.2381231146142254e+35 +223005000000.0 30.0 25.77789665451725 25.860240852279684 4.402031287457005e+35 4.4160930140497096e+35 +223005000000.0 35.0 33.19062029924153 33.31190769590544 5.667884814867686e+35 5.688596780705874e+35 +223005000000.0 40.0 40.91508655964425 41.08520820070822 6.986973901650884e+35 7.016025177504528e+35 +223005000000.0 45.0 48.72112759151631 48.949598979587805 8.319993321893316e+35 8.359008847948784e+35 +223005000000.0 50.0 56.38506358365022 56.68000395047354 9.628745795944739e+35 9.679112074469749e+35 +223005000000.0 55.0 63.695415331829096 64.06254532647971 1.0877117513354051e+36 1.0939811446248408e+36 +223005000000.0 60.0 70.45662186754525 70.8983931833725 1.2031712983654603e+36 1.2107153240873251e+36 +223005000000.0 65.0 76.4918012848629 77.00676660806042 1.3062326496328192e+36 1.3150265923468134e+36 +223005000000.0 70.0 81.64723079267345 82.22973265184413 1.3942707168879465e+36 1.4042179652745673e+36 +223005000000.0 75.0 85.79405069875565 86.43430616151178 1.465084993221949e+36 1.4760184864265773e+36 +223005000000.0 80.0 88.83004937937841 89.51445619246599 1.516930034576157e+36 1.528617490092557e+36 +223005000000.0 85.0 90.68153848173485 91.39335199180691 1.5485474821367227e+36 1.5607029554252026e+36 +223005000000.0 90.0 91.30391456702186 92.02508329529913 1.5591756533824877e+36 1.5714908835502518e+36 diff --git a/tests/isothermal_sphere/thermal.txt b/tests/isothermal_sphere/thermal.txt index c31c595..9d43c48 100644 --- a/tests/isothermal_sphere/thermal.txt +++ b/tests/isothermal_sphere/thermal.txt @@ -1,39 +1,39 @@ -# frequency inc Ftot Ftot_unpol -11090300000.0 90.0 4.274095293683708 4.285141150253394 -14180300000.0 90.0 6.9857805885842 7.005553015786133 -18606400000.0 90.0 12.012821992017324 12.059528765554925 -23790600000.0 90.0 19.54640531961427 19.70379507034827 -30419100000.0 90.0 31.28297333819569 32.13698917863901 -39400900000.0 90.0 48.1015970375206 53.320419990110096 -49731200000.0 90.0 64.97950577222639 81.0972597360478 -65253900000.0 90.0 85.10330208718378 111.62668339195066 -81304100000.0 90.0 93.56264610006741 114.37655638692958 -106682000000.0 90.0 81.45955452754392 90.16662198582216 -134652000000.0 90.0 59.33311574585277 62.502869413715594 -176681000000.0 90.0 35.0587011109502 35.98022561906339 -223005000000.0 90.0 20.290627321266705 20.639702266728055 -288851000000.0 90.0 10.135085073657121 10.268643837071648 -369331000000.0 90.0 4.830590682670228 4.88653823612203 -478382000000.0 90.0 2.02472024931681 2.046375251404457 -603808000000.0 90.0 0.8510126468565906 0.8596347403983207 -792276000000.0 90.0 0.2774776814456344 0.2801334437120814 -974460000000.0 90.0 0.10843946584676074 0.10943464661785053 -1295270000000.0 90.0 0.026111781782012834 0.026338110861036445 -223005000000.0 5.0 0.0006679335465425816 0.0006729087303241369 -223005000000.0 10.0 0.011147781326921112 0.011243757992862929 -223005000000.0 15.0 0.06985389871859848 0.07051104612184096 -223005000000.0 20.0 0.2534654322979531 0.25600553123915953 -223005000000.0 25.0 0.6569199260451928 0.6638586790906905 -223005000000.0 30.0 1.3642860033739146 1.379419742834962 -223005000000.0 35.0 2.4279369207794392 2.456214174743373 -223005000000.0 40.0 3.8587867482340723 3.906017381001425 -223005000000.0 45.0 5.625384717516792 5.69780637305197 -223005000000.0 50.0 7.658824204761203 7.762525267059334 -223005000000.0 55.0 9.861269153583539 10.001493827025799 -223005000000.0 60.0 12.116053290604645 12.296450122374356 -223005000000.0 65.0 14.297971883563548 14.519886530032826 -223005000000.0 70.0 16.283430376971037 16.54536901876291 -223005000000.0 75.0 17.959097056933977 18.256478565738337 -223005000000.0 80.0 19.229220385111592 19.554457649520156 -223005000000.0 85.0 20.021415892011337 20.36438676967305 -223005000000.0 90.0 20.290627321266705 20.639702266728055 +# frequency inc Ftot Ftot_unpol nuLnu nuLnu_unpol +11090300000.0 90.0 4.274095293683708 4.285141150253394 3.629765273177928e+33 3.6391459406254697e+33 +14180300000.0 90.0 6.9857805885842 7.005553015786133 7.585625645693124e+33 7.607095863510398e+33 +18606400000.0 90.0 12.012821992017324 12.059528765554925 1.711584879205804e+34 1.718239652530221e+34 +23790600000.0 90.0 19.54640531961427 19.70379507034827 3.560929221431221e+34 3.5896022052038475e+34 +30419100000.0 90.0 31.28297333819569 32.13698917863901 7.286943989509906e+34 7.485875386733148e+34 +39400900000.0 90.0 48.1015970375206 53.320419990110096 1.4512982920758257e+35 1.608758112626717e+35 +49731200000.0 90.0 64.97950577222639 81.0972597360478 2.4745508853502623e+35 3.0883475257993082e+35 +65253900000.0 90.0 85.10330208718378 111.62668339195066 4.2524961663231546e+35 5.5778334276306006e+35 +81304100000.0 90.0 93.56264610006741 114.37655638692958 5.825134920332387e+35 7.120992195585089e+35 +106682000000.0 90.0 81.45955452754392 90.16662198582216 6.654634787166094e+35 7.365936909282026e+35 +134652000000.0 90.0 59.33311574585277 62.502869413715594 6.117880780539866e+35 6.4447163899612236e+35 +176681000000.0 90.0 35.0587011109502 35.98022561906339 4.743257660758735e+35 4.867935074472716e+35 +223005000000.0 90.0 20.290627321266705 20.639702266728055 3.4649831018968644e+35 3.524593816152673e+35 +288851000000.0 90.0 10.135085073657121 10.268643837071648 2.241776540849693e+35 2.2713183651631165e+35 +369331000000.0 90.0 4.830590682670228 4.88653823612203 1.3661772452176381e+35 1.3820002116978365e+35 +478382000000.0 90.0 2.02472024931681 2.046375251404457 7.417044808184379e+34 7.496372370033774e+34 +603808000000.0 90.0 0.8510126468565906 0.8596347403983207 3.934829540929713e+34 3.974695538806448e+34 +792276000000.0 90.0 0.2774776814456344 0.2801334437120814 1.683432097500715e+34 1.6995443679340546e+34 +974460000000.0 90.0 0.10843946584676074 0.10943464661785053 8.091749555840739e+33 8.166009913908405e+33 +1295270000000.0 90.0 0.026111781782012834 0.026338110861036445 2.5899290133070897e+33 2.6123777398326047e+33 +223005000000.0 5.0 0.0006679335465425816 0.0006729087303241369 1.1406145385827358e+31 1.1491105438855646e+31 +223005000000.0 10.0 0.011147781326921112 0.011243757992862929 1.9036806161699126e+32 1.9200703275573095e+32 +223005000000.0 15.0 0.06985389871859848 0.07051104612184096 1.1928787357297363e+33 1.2041006886621827e+33 +223005000000.0 20.0 0.2534654322979531 0.25600553123915953 4.328370069203772e+33 4.3717467463713707e+33 +223005000000.0 25.0 0.6569199260451928 0.6638586790906905 1.1218068357404689e+34 1.1336559824771426e+34 +223005000000.0 30.0 1.3642860033739146 1.379419742834962 2.329759387424356e+34 2.3556029213234184e+34 +223005000000.0 35.0 2.4279369207794392 2.456214174743373 4.1461312505379305e+34 4.194419657594908e+34 +223005000000.0 40.0 3.8587867482340723 3.906017381001425 6.589560127813686e+34 6.67021477778562e+34 +223005000000.0 45.0 5.625384717516792 5.69780637305197 9.606338275916703e+34 9.73001104791512e+34 +223005000000.0 50.0 7.658824204761203 7.762525267059334 1.3078795460444915e+35 1.3255883345813135e+35 +223005000000.0 55.0 9.861269153583539 10.001493827025799 1.6839859329834973e+35 1.707931773407918e+35 +223005000000.0 60.0 12.116053290604645 12.296450122374356 2.069030161015551e+35 2.0998361072202127e+35 +223005000000.0 65.0 14.297971883563548 14.519886530032826 2.4416313100393265e+35 2.4795271566242933e+35 +223005000000.0 70.0 16.283430376971037 16.54536901876291 2.7806834260852426e+35 2.825414076999686e+35 +223005000000.0 75.0 17.959097056933977 18.256478565738337 3.0668331167060814e+35 3.1176162633534852e+35 +223005000000.0 80.0 19.229220385111592 19.554457649520156 3.283729115029789e+35 3.3392691240911e+35 +223005000000.0 85.0 20.021415892011337 20.36438676967305 3.419010494030292e+35 3.477578830865054e+35 +223005000000.0 90.0 20.290627321266705 20.639702266728055 3.4649831018968644e+35 3.524593816152673e+35 From b7a8eb7f66385059bacc7038e32e6d6b46871914 Mon Sep 17 00:00:00 2001 From: George Wong Date: Sun, 18 Apr 2021 23:13:00 -0500 Subject: [PATCH 22/93] add exponential radial coordinate for isothermal flat space sphere test --- model/sphere/model.c | 5 +- src/coordinates.c | 35 +++++++++---- src/coordinates.h | 2 + src/grid.c | 2 +- tests/isothermal_sphere/kappa4.txt | 78 ++++++++++++++-------------- tests/isothermal_sphere/powerlaw.txt | 60 +++++++++++++-------- tests/isothermal_sphere/thermal.txt | 76 +++++++++++++-------------- 7 files changed, 148 insertions(+), 110 deletions(-) diff --git a/model/sphere/model.c b/model/sphere/model.c index b0f8053..a23ac5d 100644 --- a/model/sphere/model.c +++ b/model/sphere/model.c @@ -66,7 +66,7 @@ void init_model(double *tA, double *tB) // set metric use_eKS_internal = 0; - metric = METRIC_MINKOWSKI; + metric = METRIC_EMINKOWSKI; // set units double MBH = MODEL_MBH * MSUN; @@ -149,7 +149,7 @@ void get_model_fourv(double X[NDIM], double Kcon[NDIM], Ucon[3] = 0.; Bcon[0] = 0.; - Bcon[1] = model_B_0 * cos(h); + Bcon[1] = model_B_0 * cos(h) / r; Bcon[2] = - model_B_0 * sin(h) / (r + 1.e-8); Bcon[3] = 0.; @@ -227,3 +227,4 @@ void get_model_jar(double X[NDIM], double Kcon[NDIM], double *aI, double *aQ, double *aU, double *aV, double *rQ, double *rU, double *rV) {return;} void get_model_jk(double X[NDIM], double Kcon[NDIM], double *jnuinv, double *knuinv) {return;} + diff --git a/src/coordinates.c b/src/coordinates.c index 194196f..3472e5b 100644 --- a/src/coordinates.c +++ b/src/coordinates.c @@ -24,9 +24,13 @@ void bl_coord(double X[NDIM], double *r, double *th) { if (metric == METRIC_MINKOWSKI) { - *r = X[1]; - *th = X[2]; - return; + *r = X[1]; + *th = X[2]; + return; + } else if (metric == METRIC_EMINKOWSKI) { + *r = exp(X[1]); + *th = X[2]; + return; } *r = exp(X[1]); @@ -113,12 +117,19 @@ void gcov_func(double X[NDIM], double gcov[NDIM][NDIM]) bl_coord(X, &r, &th); if (metric == METRIC_MINKOWSKI) { - MUNULOOP gcov[mu][nu] = 0; - gcov[0][0] = -1; - gcov[1][1] = 1; - gcov[2][2] = r*r; - gcov[3][3] = r*r*sin(th)*sin(th); - return; + MUNULOOP gcov[mu][nu] = 0; + gcov[0][0] = -1; + gcov[1][1] = 1; + gcov[2][2] = r*r; + gcov[3][3] = r*r*sin(th)*sin(th); + return; + } else if (metric == METRIC_EMINKOWSKI) { + MUNULOOP gcov[mu][nu] = 0; + gcov[0][0] = -1; + gcov[1][1] = r*r; + gcov[2][2] = r*r; + gcov[3][3] = r*r*sin(th)*sin(th); + return; } // compute ks metric @@ -265,6 +276,10 @@ void set_dxdX(double X[NDIM], double dxdX[NDIM][NDIM]) case METRIC_MINKOWSKI: // Blank transform: just override L_11 dxdX[1][1] = 1.; + break; + case METRIC_EMINKOWSKI: + // keep radial transformation element! + break; } } } @@ -297,6 +312,8 @@ void vec_from_ks(double X[NDIM], double v_ks[NDIM], double v_nat[NDIM]) { void native_coord(double r, double th, double phi, double X[NDIM]) { if (metric == METRIC_MINKOWSKI) { X[0] = 1; X[1] = r; X[2] = th/180*M_PI; X[3] = phi/180*M_PI; + } else if (metric == METRIC_EMINKOWSKI) { + X[0] = 1; X[1] = log(r); X[2] = th/180*M_PI; X[3] = phi/180*M_PI; } else { double x[NDIM] = {0., r, th/180.*M_PI, phi/180.*M_PI}; X[0] = 0.0; diff --git a/src/coordinates.h b/src/coordinates.h index 1b9a0ff..95d3b27 100644 --- a/src/coordinates.h +++ b/src/coordinates.h @@ -15,6 +15,8 @@ #define METRIC_MKS3 3 // Spherical coordinates in Minkowski space #define METRIC_MINKOWSKI 4 +// Exponential spherical coordinates in Minkowski space +#define METRIC_EMINKOWSKI 5 // Coordinate parameters. See extern int use_eKS_internal; diff --git a/src/grid.c b/src/grid.c index 82fe2b8..a64b411 100644 --- a/src/grid.c +++ b/src/grid.c @@ -221,7 +221,7 @@ double gdet_zone(int i, int j, int k) Xzone[2] = startx[2] + (j+0.5)*dx[2]; Xzone[3] = startx[3] + (k+0.5)*dx[3]; - if (metric == METRIC_MINKOWSKI) { + if (metric == METRIC_MINKOWSKI || metric == METRIC_EMINKOWSKI) { double gcov[NDIM][NDIM]; gcov_func(Xzone, gcov); return gdet_func(gcov); diff --git a/tests/isothermal_sphere/kappa4.txt b/tests/isothermal_sphere/kappa4.txt index 7b65d85..9c08e51 100644 --- a/tests/isothermal_sphere/kappa4.txt +++ b/tests/isothermal_sphere/kappa4.txt @@ -1,39 +1,39 @@ -# frequency inc Ftot Ftot_unpol -11090300000.0 90.0 5.536220269736517 4.643855039969408 4.7016219103608126e+33 3.94378285918504e+33 -14180300000.0 90.0 9.816839938505414 8.144120854755604 1.0659778367342523e+34 8.84342862391257e+33 -18606400000.0 90.0 18.410962274096203 15.204680005731683 2.62319084232761e+34 2.1663602780650973e+34 -23790600000.0 90.0 31.595838718702307 26.7931177639978 5.756073484066856e+34 4.881122355696168e+34 -30419100000.0 90.0 49.97781209518265 47.01985585568236 1.1641652905517959e+35 1.0952637152199442e+35 -39400900000.0 90.0 73.399959477185 82.30598712383984 2.2145883377755782e+35 2.4832967281163016e+35 -49731200000.0 90.0 99.4436481652677 122.9673634555481 3.78701507014235e+35 4.682845683295018e+35 -65253900000.0 90.0 131.38846383064052 157.85198086034558 6.565302697261962e+35 7.88764862222926e+35 -81304100000.0 90.0 145.9185182923764 164.44680988687367 9.084769316154148e+35 1.0238325814179797e+36 -106682000000.0 90.0 143.34391579665498 152.14055861823545 1.1710123068086322e+36 1.2428742825710588e+36 -134652000000.0 90.0 129.45492553023746 133.52287553057002 1.3348191661467184e+36 1.3767640948941546e+36 -176681000000.0 90.0 108.75550202730732 110.33424586467278 1.471404677852043e+36 1.492764250693207e+36 -223005000000.0 90.0 91.30391456702186 92.02508329529913 1.5591756533824877e+36 1.5714908835502518e+36 -288851000000.0 90.0 74.0592358983554 74.39307422913704 1.6381141003119254e+36 1.6454982607106167e+36 -369331000000.0 90.0 60.17325878299614 60.358827662207545 1.701807135405635e+36 1.707055354450744e+36 -478382000000.0 90.0 48.06670547258849 48.183239014178945 1.760800823680635e+36 1.765069731940552e+36 -603808000000.0 90.0 39.113393303429476 39.19795197877398 1.8084870534511828e+36 1.812396795273662e+36 -792276000000.0 90.0 30.637103958874302 30.699241954043053 1.8587254985745693e+36 1.8624953547661173e+36 -974460000000.0 90.0 25.375734031100432 25.426055738070556 1.8935364811318213e+36 1.897291485334812e+36 -1295270000000.0 90.0 19.52853758462853 19.5667240140286 1.9369618856392193e+36 1.940749453344996e+36 -223005000000.0 5.0 1.344054064998482 1.3461306790365162 2.2952097781493307e+34 2.298755963507242e+34 -223005000000.0 10.0 3.8232186755454256 3.8306162851355574 6.5288213596712e+34 6.541454085026799e+34 -223005000000.0 15.0 7.714864785809941 7.731670331663487 1.3174494653614186e+35 1.3203193092296592e+35 -223005000000.0 20.0 12.81426223223759 12.845701265871375 2.188261673479229e+35 2.193630443924628e+35 -223005000000.0 25.0 18.909371222719386 18.96215577589628 3.2291092195826303e+35 3.2381231146142254e+35 -223005000000.0 30.0 25.77789665451725 25.860240852279684 4.402031287457005e+35 4.4160930140497096e+35 -223005000000.0 35.0 33.19062029924153 33.31190769590544 5.667884814867686e+35 5.688596780705874e+35 -223005000000.0 40.0 40.91508655964425 41.08520820070822 6.986973901650884e+35 7.016025177504528e+35 -223005000000.0 45.0 48.72112759151631 48.949598979587805 8.319993321893316e+35 8.359008847948784e+35 -223005000000.0 50.0 56.38506358365022 56.68000395047354 9.628745795944739e+35 9.679112074469749e+35 -223005000000.0 55.0 63.695415331829096 64.06254532647971 1.0877117513354051e+36 1.0939811446248408e+36 -223005000000.0 60.0 70.45662186754525 70.8983931833725 1.2031712983654603e+36 1.2107153240873251e+36 -223005000000.0 65.0 76.4918012848629 77.00676660806042 1.3062326496328192e+36 1.3150265923468134e+36 -223005000000.0 70.0 81.64723079267345 82.22973265184413 1.3942707168879465e+36 1.4042179652745673e+36 -223005000000.0 75.0 85.79405069875565 86.43430616151178 1.465084993221949e+36 1.4760184864265773e+36 -223005000000.0 80.0 88.83004937937841 89.51445619246599 1.516930034576157e+36 1.528617490092557e+36 -223005000000.0 85.0 90.68153848173485 91.39335199180691 1.5485474821367227e+36 1.5607029554252026e+36 -223005000000.0 90.0 91.30391456702186 92.02508329529913 1.5591756533824877e+36 1.5714908835502518e+36 +# frequency inc Ftot Ftot_unpol nuLnu nuLnu_unpol +11090300000.0 90.0 5.538792042856206 4.643911765020724 4.703805982572336e+33 3.943831032800095e+33 +14180300000.0 90.0 9.821975206285796 8.144802137873944 1.066535458298205e+34 8.844168406479303e+33 +18606400000.0 90.0 18.421721135504548 15.207746122204979 2.6247237631114384e+34 2.1667971378301932e+34 +23790600000.0 90.0 31.61606288657072 26.79942756023756 5.759757886859296e+34 4.882271863109117e+34 +30419100000.0 90.0 50.01200702856 47.027407168533664 1.164961815107029e+35 1.0954396128023123e+35 +39400900000.0 90.0 73.45263362930045 82.29740942799724 2.216177597004166e+35 2.4830379259955193e+35 +49731200000.0 90.0 99.51672031350577 122.89770713632136 3.789797805205784e+35 4.680193029902777e+35 +65253900000.0 90.0 131.4876044521914 157.67158667371078 6.5702566191748866e+35 7.878634570267965e+35 +81304100000.0 90.0 146.031420559943 164.2057931163908 9.091798520316281e+35 1.0223320304346055e+36 +106682000000.0 90.0 143.45740637884492 151.883832433797 1.1719394397651495e+36 1.2407770221482037e+36 +134652000000.0 90.0 129.558559853353 133.28515969725322 1.3358877472006928e+36 1.3743129896225078e+36 +176681000000.0 90.0 108.84312686320173 110.13219429532403 1.4725901957433467e+36 1.4900305993489976e+36 +223005000000.0 90.0 91.37766677162087 91.85475076893294 1.5604351025784984e+36 1.568582154725768e+36 +288851000000.0 90.0 74.11913733253958 74.25463235214907 1.6394390584049396e+36 1.6424360688311478e+36 +369331000000.0 90.0 60.22195567852089 60.24625001723842 1.703184370508939e+36 1.7038714577602466e+36 +478382000000.0 90.0 48.10561448115822 48.09327742303532 1.7622261556992243e+36 1.7617742191271005e+36 +603808000000.0 90.0 39.1450576608002 39.124736604481434 1.8099511192231438e+36 1.809011533977245e+36 +792276000000.0 90.0 30.661907157680677 30.64188898624312 1.860230286302858e+36 1.8590158018745724e+36 +974460000000.0 90.0 25.39627763650709 25.378551039513777 1.8950694443258633e+36 1.8937466862116738e+36 +1295270000000.0 90.0 19.5443471366262 19.530165155682553 1.9385299753907334e+36 1.937123318264891e+36 +223005000000.0 5.0 1.3499179247277533 1.3476141787803724 2.305223354640641e+34 2.301289301418752e+34 +223005000000.0 10.0 3.8317087301078407 3.8278634691389315 6.543319627825836e+34 6.536753165355902e+34 +223005000000.0 15.0 7.727893702930353 7.723151493537718 1.3196743831494916e+35 1.3188645671122162e+35 +223005000000.0 20.0 12.83241875126555 12.829216437728128 2.191362219885461e+35 2.1908153682717354e+35 +223005000000.0 25.0 18.932693758572753 18.93550463236725 3.2330919546329456e+35 3.233571960994764e+35 +223005000000.0 30.0 25.807009693997646 25.822310547118917 4.4070028533059694e+35 4.409615744305856e+35 +223005000000.0 35.0 33.22655541788771 33.26245733389098 5.674021371264006e+35 5.6801522577224375e+35 +223005000000.0 40.0 40.959466824026194 41.02504614279062 6.994552615887085e+35 7.005751443195618e+35 +223005000000.0 45.0 48.7672597076334 48.871594619945164 8.32787119576432e+35 8.345688225389583e+35 +223005000000.0 50.0 56.43913180389175 56.590405240672546 9.637978899806944e+35 9.663811511776607e+35 +223005000000.0 55.0 63.75164079406176 63.956003988659546 1.0886719004399539e+36 1.092161763048533e+36 +223005000000.0 60.0 70.52082809372453 70.78166677679584 1.2042677331144962e+36 1.2087220144674233e+36 +223005000000.0 65.0 76.55444998328952 76.87156751476803 1.3073024868437e+36 1.3127178289644268e+36 +223005000000.0 70.0 81.71688012393665 82.08648548605812 1.3954601022729536e+36 1.401771766835336e+36 +223005000000.0 75.0 85.87014486525646 86.28473548295514 1.4663844355551186e+36 1.4734643028346544e+36 +223005000000.0 80.0 88.90779274026941 89.3566378457653 1.518257639817271e+36 1.5259224629954204e+36 +223005000000.0 85.0 90.76207547391512 91.23218033840185 1.549922793567778e+36 1.557950664691599e+36 +223005000000.0 90.0 91.37766677162087 91.85475076893294 1.5604351025784984e+36 1.568582154725768e+36 diff --git a/tests/isothermal_sphere/powerlaw.txt b/tests/isothermal_sphere/powerlaw.txt index 680ccc4..952156f 100644 --- a/tests/isothermal_sphere/powerlaw.txt +++ b/tests/isothermal_sphere/powerlaw.txt @@ -1,21 +1,39 @@ -# frequency inc Ftot Ftot_unpol -11090300000.0 90.0 2.5681664625857445 2.357442595096737 -14180300000.0 90.0 4.747505918482114 4.358361769368128 -18606400000.0 90.0 9.359864867269868 8.596108910850063 -23790600000.0 90.0 17.275754835927415 15.89110839133326 -30419100000.0 90.0 31.670483590409923 29.358670187882677 -39400900000.0 90.0 57.649983519664154 55.832068170597445 -49731200000.0 90.0 90.07825940264141 98.00639662746664 -65253900000.0 90.0 136.849889347324 170.65026690645294 -81304100000.0 90.0 178.24258350881462 219.32381544087414 -106682000000.0 90.0 204.12113909009534 229.43008993707176 -134652000000.0 90.0 193.94893269177425 205.8492703343728 -176681000000.0 90.0 162.91325494834996 167.12927813291785 -223005000000.0 90.0 133.7469112856878 135.44332085703087 -288851000000.0 90.0 105.03883022763404 105.70174095627883 -369331000000.0 90.0 82.70586275734347 83.01956208730132 -478382000000.0 90.0 64.04126365043531 64.21347717470563 -603808000000.0 90.0 50.79508572284754 50.910434487222304 -792276000000.0 90.0 38.732981254624754 38.813171043137025 -974460000000.0 90.0 31.49708141331971 31.56009808674695 -1295270000000.0 90.0 23.698433065525567 23.74491620336421 +# frequency inc Ftot Ftot_unpol nuLnu nuLnu_unpol +11090300000.0 90.0 2.568982102017574 2.357441343714158 2.181701946397708e+33 2.0020514600161365e+33 +14180300000.0 90.0 4.749120983048624 4.358359465407455 5.1569117390235905e+33 4.732596867982162e+33 +18606400000.0 90.0 9.363910657051242 8.596144942832101 1.334168432820633e+34 1.2247773015690956e+34 +23790600000.0 90.0 17.284436731586833 15.892323415931553 3.148847822761782e+34 2.895235104504785e+34 +30419100000.0 90.0 31.688701841381917 29.364728391717602 7.38145293677982e+34 6.840114868376485e+34 +39400900000.0 90.0 57.68759080958187 55.84457598478604 1.7405222938982634e+35 1.6849157354422056e+35 +49731200000.0 90.0 90.14118733264894 98.01403030557918 3.432758563945102e+35 3.732572333184556e+35 +65253900000.0 90.0 136.9497772663922 170.74500673118717 6.843193959824581e+35 8.531895575560063e+35 +81304100000.0 90.0 178.37585737156334 219.27810089772376 1.1105537081625719e+36 1.3652077790076299e+36 +106682000000.0 90.0 204.27928587860166 229.19939196962306 1.6688085884947173e+36 1.8723871691228583e+36 +134652000000.0 90.0 194.10255493538097 205.55943973668553 2.0014055816306153e+36 2.1195383552927838e+36 +176681000000.0 90.0 163.04402316584773 166.8532011619442 2.2058997835512548e+36 2.2574359561379078e+36 +223005000000.0 90.0 133.8548176410341 135.2056120633057 2.2858075006264268e+36 2.308874701916956e+36 +288851000000.0 90.0 105.12378692345416 105.51012629018042 2.3252300074205965e+36 2.333775436717335e+36 +369331000000.0 90.0 82.77282269500205 82.86681602706109 2.3409631309485498e+36 2.3436214301069103e+36 +478382000000.0 90.0 64.09313505309095 64.0944156015178 2.347888083532497e+36 2.3479349931490922e+36 +603808000000.0 90.0 50.83623494547413 50.81570454392648 2.3505164083278736e+36 2.3495671435808072e+36 +792276000000.0 90.0 38.76436144875301 38.740667386685566 2.3517988892643904e+36 2.3503613918629172e+36 +974460000000.0 90.0 31.52259995937203 31.501200381099366 2.3522154247849734e+36 2.3506185889223942e+36 +1295270000000.0 90.0 23.71763351882157 23.700576041359845 2.3524624895454226e+36 2.350770622780421e+36 +223005000000.0 5.0 1.6025398324127995 1.5997673999348212 2.736619894253882e+34 2.731885475975313e+34 +223005000000.0 10.0 4.69671256394097 4.692154003347476 8.020466499557294e+34 8.0126819519554185e+34 +223005000000.0 15.0 9.743870637758837 9.739081139839671 1.6639380622558136e+35 1.663120170867158e+35 +223005000000.0 20.0 16.579924478908957 16.580141993870985 2.8313150323318474e+35 2.8313521768545817e+35 +223005000000.0 25.0 24.983597938854214 24.99892652039947 4.266390748401509e+35 4.2690083745196864e+35 +223005000000.0 30.0 34.68432165474619 34.730315367631704 5.92296071144578e+35 5.930814950519738e+35 +223005000000.0 35.0 45.371712734947145 45.46905543950778 7.74802155899516e+35 7.764644545611349e+35 +223005000000.0 40.0 56.706965729051056 56.88015763097229 9.683716274511914e+35 9.713291851651097e+35 +223005000000.0 45.0 68.32349763939979 68.59851863560343 1.1667444334502287e+36 1.171440902856641e+36 +223005000000.0 50.0 79.88018025539544 80.28184601253058 1.3640952070088177e+36 1.3709543594590977e+36 +223005000000.0 55.0 91.01059515037338 91.55909739825746 1.554166706118053e+36 1.5635333510727844e+36 +223005000000.0 60.0 101.40102375087214 102.10926849831515 1.7316016318704945e+36 1.7436961622319338e+36 +223005000000.0 65.0 110.72684523095532 111.59756472173967 1.890856510136021e+36 1.905725583793975e+36 +223005000000.0 70.0 118.74811066678926 119.77311479850242 2.0278337891078907e+36 2.0453375455937143e+36 +223005000000.0 75.0 125.22774591691295 126.3871431255898 2.1384850089674263e+36 2.1582837646827865e+36 +223005000000.0 80.0 129.98110021859355 131.24445946859115 2.2196569317072896e+36 2.2412310229542304e+36 +223005000000.0 85.0 132.88804315583778 134.21687951385127 2.2692981182327182e+36 2.291990346781399e+36 +223005000000.0 90.0 133.8548176410341 135.2056120633057 2.2858075006264268e+36 2.308874701916956e+36 diff --git a/tests/isothermal_sphere/thermal.txt b/tests/isothermal_sphere/thermal.txt index 9d43c48..23d3c0e 100644 --- a/tests/isothermal_sphere/thermal.txt +++ b/tests/isothermal_sphere/thermal.txt @@ -1,39 +1,39 @@ # frequency inc Ftot Ftot_unpol nuLnu nuLnu_unpol -11090300000.0 90.0 4.274095293683708 4.285141150253394 3.629765273177928e+33 3.6391459406254697e+33 -14180300000.0 90.0 6.9857805885842 7.005553015786133 7.585625645693124e+33 7.607095863510398e+33 -18606400000.0 90.0 12.012821992017324 12.059528765554925 1.711584879205804e+34 1.718239652530221e+34 -23790600000.0 90.0 19.54640531961427 19.70379507034827 3.560929221431221e+34 3.5896022052038475e+34 -30419100000.0 90.0 31.28297333819569 32.13698917863901 7.286943989509906e+34 7.485875386733148e+34 -39400900000.0 90.0 48.1015970375206 53.320419990110096 1.4512982920758257e+35 1.608758112626717e+35 -49731200000.0 90.0 64.97950577222639 81.0972597360478 2.4745508853502623e+35 3.0883475257993082e+35 -65253900000.0 90.0 85.10330208718378 111.62668339195066 4.2524961663231546e+35 5.5778334276306006e+35 -81304100000.0 90.0 93.56264610006741 114.37655638692958 5.825134920332387e+35 7.120992195585089e+35 -106682000000.0 90.0 81.45955452754392 90.16662198582216 6.654634787166094e+35 7.365936909282026e+35 -134652000000.0 90.0 59.33311574585277 62.502869413715594 6.117880780539866e+35 6.4447163899612236e+35 -176681000000.0 90.0 35.0587011109502 35.98022561906339 4.743257660758735e+35 4.867935074472716e+35 -223005000000.0 90.0 20.290627321266705 20.639702266728055 3.4649831018968644e+35 3.524593816152673e+35 -288851000000.0 90.0 10.135085073657121 10.268643837071648 2.241776540849693e+35 2.2713183651631165e+35 -369331000000.0 90.0 4.830590682670228 4.88653823612203 1.3661772452176381e+35 1.3820002116978365e+35 -478382000000.0 90.0 2.02472024931681 2.046375251404457 7.417044808184379e+34 7.496372370033774e+34 -603808000000.0 90.0 0.8510126468565906 0.8596347403983207 3.934829540929713e+34 3.974695538806448e+34 -792276000000.0 90.0 0.2774776814456344 0.2801334437120814 1.683432097500715e+34 1.6995443679340546e+34 -974460000000.0 90.0 0.10843946584676074 0.10943464661785053 8.091749555840739e+33 8.166009913908405e+33 -1295270000000.0 90.0 0.026111781782012834 0.026338110861036445 2.5899290133070897e+33 2.6123777398326047e+33 -223005000000.0 5.0 0.0006679335465425816 0.0006729087303241369 1.1406145385827358e+31 1.1491105438855646e+31 -223005000000.0 10.0 0.011147781326921112 0.011243757992862929 1.9036806161699126e+32 1.9200703275573095e+32 -223005000000.0 15.0 0.06985389871859848 0.07051104612184096 1.1928787357297363e+33 1.2041006886621827e+33 -223005000000.0 20.0 0.2534654322979531 0.25600553123915953 4.328370069203772e+33 4.3717467463713707e+33 -223005000000.0 25.0 0.6569199260451928 0.6638586790906905 1.1218068357404689e+34 1.1336559824771426e+34 -223005000000.0 30.0 1.3642860033739146 1.379419742834962 2.329759387424356e+34 2.3556029213234184e+34 -223005000000.0 35.0 2.4279369207794392 2.456214174743373 4.1461312505379305e+34 4.194419657594908e+34 -223005000000.0 40.0 3.8587867482340723 3.906017381001425 6.589560127813686e+34 6.67021477778562e+34 -223005000000.0 45.0 5.625384717516792 5.69780637305197 9.606338275916703e+34 9.73001104791512e+34 -223005000000.0 50.0 7.658824204761203 7.762525267059334 1.3078795460444915e+35 1.3255883345813135e+35 -223005000000.0 55.0 9.861269153583539 10.001493827025799 1.6839859329834973e+35 1.707931773407918e+35 -223005000000.0 60.0 12.116053290604645 12.296450122374356 2.069030161015551e+35 2.0998361072202127e+35 -223005000000.0 65.0 14.297971883563548 14.519886530032826 2.4416313100393265e+35 2.4795271566242933e+35 -223005000000.0 70.0 16.283430376971037 16.54536901876291 2.7806834260852426e+35 2.825414076999686e+35 -223005000000.0 75.0 17.959097056933977 18.256478565738337 3.0668331167060814e+35 3.1176162633534852e+35 -223005000000.0 80.0 19.229220385111592 19.554457649520156 3.283729115029789e+35 3.3392691240911e+35 -223005000000.0 85.0 20.021415892011337 20.36438676967305 3.419010494030292e+35 3.477578830865054e+35 -223005000000.0 90.0 20.290627321266705 20.639702266728055 3.4649831018968644e+35 3.524593816152673e+35 +11090300000.0 90.0 4.275646082651832 4.285143624402521 3.6310822770245304e+33 3.639148041790738e+33 +14180300000.0 90.0 6.988761144348534 7.005656879264287 7.588862131574358e+33 7.607208645389753e+33 +18606400000.0 90.0 12.018731653235056 12.060691823920017 1.712426886753091e+34 1.7184053648926158e+34 +23790600000.0 90.0 19.557375813186006 19.707745503446176 3.5629278063625335e+34 3.5903218880522085e+34 +30419100000.0 90.0 31.3027514002403 32.14457457894139 7.29155102058659e+34 7.487642302765837e+34 +39400900000.0 90.0 48.134475453534684 53.32846962863743 1.4522902838587636e+35 1.6090009824557045e+35 +49731200000.0 90.0 65.02591765835368 81.08815286512608 2.476318343759528e+35 3.0880007177521514e+35 +65253900000.0 90.0 85.1660303167946 111.54531719510815 4.2556306105738254e+35 5.573767669526567e+35 +81304100000.0 90.0 93.63377971845873 114.23569151090516 5.8295636420688124e+35 7.112222061962525e+35 +106682000000.0 90.0 81.52378513139142 90.02139063300528 6.659881945872564e+35 7.354072596762175e+35 +134652000000.0 90.0 59.380761664657314 62.39279382105969 6.122793585931278e+35 6.433366415427598e+35 +176681000000.0 90.0 35.08713213280178 35.914290233003605 4.747104228313359e+35 4.85901436391777e+35 +223005000000.0 90.0 20.307140822627037 20.60142287648152 3.467803074008162e+35 3.518056933962927e+35 +288851000000.0 90.0 10.143348849321 10.249512014947793 2.2436044030025508e+35 2.2670866029520186e+35 +369331000000.0 90.0 4.834533335747593 4.877422581325198 1.3672922978631475e+35 1.3794221418557015e+35 +478382000000.0 90.0 2.026374127557577 2.04255681081707 7.423103368137631e+34 7.482384489513689e+34 +603808000000.0 90.0 0.8517082421646595 0.8580307947514274 3.938045766889259e+34 3.967279370860233e+34 +792276000000.0 90.0 0.2777046563360104 0.2796108501840976 1.6848091337142043e+34 1.696373840076208e+34 +974460000000.0 90.0 0.10852822035681293 0.10923052830847085 8.098372414609783e+33 8.150778612035377e+33 +1295270000000.0 90.0 0.02613317084442504 0.026288997289899124 2.5920505136233558e+33 2.6075063502086575e+33 +223005000000.0 5.0 0.0006700574885164443 0.0006721568882356729 1.144241544752787e+31 1.1478266406870215e+31 +223005000000.0 10.0 0.011184472790924957 0.011241274172991323 1.9099463318988648e+32 1.919646171431084e+32 +223005000000.0 15.0 0.07005176935366612 0.0704908474024495 1.1962577264135406e+33 1.2037557598422733e+33 +223005000000.0 20.0 0.25403965080594987 0.25584153824487565 4.3381758647344004e+33 4.3689462762576273e+33 +223005000000.0 25.0 0.6581404906388175 0.6632466498273145 1.1238911654286154e+34 1.1326108343790146e+34 +223005000000.0 30.0 1.3664644732187523 1.377898567813333 2.333479509567866e+34 2.353005246218791e+34 +223005000000.0 35.0 2.4313978442489854 2.4532154134967525 4.1520413888244715e+34 4.189298742957009e+34 +223005000000.0 40.0 3.8639049623979367 3.900997941745764 6.59830038276435e+34 6.661643198441873e+34 +223005000000.0 45.0 5.631705788001734 5.689450547326058 9.617132620551512e+34 9.715741999213833e+34 +223005000000.0 50.0 7.667214920279675 7.7510045007960615 1.3093124089631188e+35 1.3236209601975114e+35 +223005000000.0 55.0 9.870964769353535 9.985544142162627 1.6856416306745347e+35 1.705208082924798e+35 +223005000000.0 60.0 12.127953921885945 12.276760099817263 2.071062404062487e+35 2.0964736879930467e+35 +223005000000.0 65.0 14.31037962366346 14.494803114785743 2.443750150876439e+35 2.475243720306979e+35 +223005000000.0 70.0 16.29781390375227 16.51677098136076 2.7831396674056094e+35 2.8205304568544384e+35 +223005000000.0 75.0 17.975371205829976 18.224986791745504 3.069612215155309e+35 3.1122384865599823e+35 +223005000000.0 80.0 19.246293840154756 19.520001464076223 3.2866447091254713e+35 3.3333851216682716e+35 +223005000000.0 85.0 20.039343870544908 20.328413756434426 3.422072013109344e+35 3.4714358032975674e+35 +223005000000.0 90.0 20.307140822627037 20.60142287648152 3.467803074008162e+35 3.518056933962927e+35 From 49083845c859126871b3a5d29abc4a25c17b6b45 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Mon, 19 Apr 2021 10:51:22 -0500 Subject: [PATCH 23/93] Disable automated run of the isothermal sphere model --- tests/run_tests.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/run_tests.sh b/tests/run_tests.sh index 866400b..4ee875d 100755 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -23,6 +23,9 @@ do make -f ../../makefile -j8 MODEL=analytic elif [[ $folder == *"thin_disk"* ]]; then make -f ../../makefile -j8 MODEL=thin_disk + elif [[ $folder == *"isothermal_sphere"* ]]; then + echo "NOT TESTING ISOTHERMAL SPHERE" + continue else make -f ../../makefile -j8 MODEL=iharm fi From c621f7dbd12bea9ea5059ebe2001d041e13e8fc0 Mon Sep 17 00:00:00 2001 From: George Wong Date: Wed, 21 Apr 2021 01:06:53 -0500 Subject: [PATCH 24/93] add utility debug 'dump everything' function --- scripts/plot_pol.py | 4 +++- src/debug_tools.c | 37 +++++++++++++++++++++++++++++++++++++ src/debug_tools.h | 2 ++ 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/scripts/plot_pol.py b/scripts/plot_pol.py index 66a15bc..3282c24 100755 --- a/scripts/plot_pol.py +++ b/scripts/plot_pol.py @@ -82,7 +82,9 @@ def colorbar(mappable): Imaskval = np.nanmax(I) / np.power(I.shape[0],5.) # total intensity - im1 = ax1.imshow(I, cmap='afmhot', vmin=0., vmax=1.e-4, origin='lower', extent=extent) + vmax = 1.e-4 + vmax = I.max() / np.sqrt(1.5) + im1 = ax1.imshow(I, cmap='afmhot', vmin=0., vmax=vmax, origin='lower', extent=extent) colorbar(im1) # linear polarization fraction diff --git a/src/debug_tools.c b/src/debug_tools.c index 923a0f1..785d1fd 100644 --- a/src/debug_tools.c +++ b/src/debug_tools.c @@ -138,3 +138,40 @@ void check_N(double complex N[NDIM][NDIM], fprintf(stderr, "leave check_N\n"); } + +// because we don't have proper header files +void gcov_func(double *X, double gcov[][NDIM]); +int gcon_func(double gcov[][NDIM], double gcon[][NDIM]); +void bl_coord(double *X, double *r, double *th); +double get_model_ne(double X[NDIM]); +double get_model_thetae(double X[NDIM]); +double get_model_b(double X[NDIM]); +void get_model_fourv(double X[NDIM], double Kcon[NDIM], + double Ucon[NDIM], double Ucov[NDIM], + double Bcon[NDIM], double Bcov[NDIM]); + +void dump_at_X(double X[NDIM]) +{ + // warning that this does not necessarily print contiguously! + double r, h; + double gcov[4][4], gcon[4][4], ucon[4], ucov[4], bcon[4], bcov[4]; + bl_coord(X, &r, &h); + gcov_func(X, gcov); + gcon_func(gcov, gcon); + get_model_fourv(X, X, ucon, ucov, bcon, bcov); + double Ne = get_model_ne(X); + double Thetae = get_model_thetae(X); + double B = get_model_b(X); + fprintf(stderr, "-----\n"); + print_vector("X", X); + fprintf(stderr, "r, h: %g, %g\n", r, h); + print_matrix("gcov", gcov); + print_matrix("gcon", gcon); + fprintf(stderr, "Ne, Thetae, B: %g, %g, %g\n", Ne, Thetae, B); + print_vector("ucon", ucon); + print_vector("ucov", ucov); + print_vector("bcon", bcon); + print_vector("bcov", bcov); +} + + diff --git a/src/debug_tools.h b/src/debug_tools.h index 395a545..a84a162 100644 --- a/src/debug_tools.h +++ b/src/debug_tools.h @@ -14,6 +14,8 @@ void print_matrix(char *name, double g[NDIM][NDIM]); void print_matrix_c(char *name, double complex g[NDIM][NDIM]); void print_vector(char *name, double v[NDIM]); +void dump_at_X(double X[NDIM]); + void check_ortho(double Econ[NDIM][NDIM], double Ecov[NDIM][NDIM]); void check_u(double Ucon[NDIM], double Ucov[NDIM]); From f727da9817d3c2f4edb6d890844165f56e9fecb8 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Thu, 22 Apr 2021 18:54:15 -0500 Subject: [PATCH 25/93] Fix RIAF function & add checks, fix some debugging fns/output --- model/riaf/model.c | 7 +++++-- src/debug_tools.c | 4 ++-- src/geometry.c | 2 +- src/model_radiation.c | 4 ++-- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/model/riaf/model.c b/model/riaf/model.c index 2c6ac5d..d9e28e4 100644 --- a/model/riaf/model.c +++ b/model/riaf/model.c @@ -210,7 +210,7 @@ void get_model_fourv(double X[NDIM], double Kcon[NDIM], double Ucon[NDIM], doubl bl_Ucon_isco[2] = 0.0; bl_Ucon_isco[3] = omegaK_isco; double bl_gcov_isco[NDIM][NDIM]; - gcov_bl(r, th, bl_gcov_isco); + gcov_bl(r_isco, th, bl_gcov_isco); normalize(bl_Ucon_isco, bl_gcov_isco); flip_index(bl_Ucon_isco, bl_gcov_isco, bl_Ucov_isco); double e = bl_Ucov_isco[0]; @@ -267,9 +267,12 @@ void get_model_fourv(double X[NDIM], double Kcon[NDIM], double Ucon[NDIM], doubl // and grab Ucov flip_index(Ucon, gcov, Ucov); + // Check #if DEBUG - if (get_fluid_nu(Kcon, Ucov) == 1. || ut == 0.) { + double dot_U = Ucon[0]*Ucov[0] + Ucon[1]*Ucov[1] + Ucon[2]*Ucov[2] + Ucon[3]*Ucov[3]; + double sum_U = Ucon[0]+Ucon[1]+Ucon[2]+Ucon[3]; + if (get_fluid_nu(Kcon, Ucov) == 1. || fabs(fabs(dot_U) - 1.) > 1e-10 || sum_U < 0.1) { double bl_Ucov[NDIM]; flip_index(bl_Ucon, bl_gcov, bl_Ucov); fprintf(stderr, "RIAF model problem at r, th, phi = %g %g %g\n", r, th, X[3]); diff --git a/src/debug_tools.c b/src/debug_tools.c index 785d1fd..f923cad 100644 --- a/src/debug_tools.c +++ b/src/debug_tools.c @@ -150,7 +150,7 @@ void get_model_fourv(double X[NDIM], double Kcon[NDIM], double Ucon[NDIM], double Ucov[NDIM], double Bcon[NDIM], double Bcov[NDIM]); -void dump_at_X(double X[NDIM]) +void dump_at_X(double X[NDIM], double Kcon[NDIM]) { // warning that this does not necessarily print contiguously! double r, h; @@ -158,7 +158,7 @@ void dump_at_X(double X[NDIM]) bl_coord(X, &r, &h); gcov_func(X, gcov); gcon_func(gcov, gcon); - get_model_fourv(X, X, ucon, ucov, bcon, bcov); + get_model_fourv(X, Kcon, ucon, ucov, bcon, bcov); double Ne = get_model_ne(X); double Thetae = get_model_thetae(X); double B = get_model_b(X); diff --git a/src/geometry.c b/src/geometry.c index 2b82e81..5fc6f99 100644 --- a/src/geometry.c +++ b/src/geometry.c @@ -166,7 +166,7 @@ void normalize(double vcon[NDIM], double Gcov[NDIM][NDIM]) } /* - * Normalize input vector so that |v . v| = 1 + * Normalize input vector so that |v . v| = target * Overwrites input */ void normalize_to(double vcon[NDIM], double Gcov[NDIM][NDIM], double target) diff --git a/src/model_radiation.c b/src/model_radiation.c index e6fd5c7..e08d6a9 100644 --- a/src/model_radiation.c +++ b/src/model_radiation.c @@ -233,8 +233,8 @@ void jar_calc_dist(int dist, double X[NDIM], double Kcon[NDIM], *jU *= pol_frac_e; *jV *= pol_frac_e; #if DEBUG - fprintf(stderr, "Polarized emissivities too large:\n %g vs %g, corrected by %g\n", - jP, *jI, pol_frac_e); + //fprintf(stderr, "Polarized emissivities too large:\n %g vs %g, corrected by %g\n", + //jP, *jI, pol_frac_e); #endif } From 7943fdccf48a39efc3f98c47d1d361932d84a01e Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Thu, 22 Apr 2021 20:27:31 -0500 Subject: [PATCH 26/93] Clarify RIAF velocity behavior inside ISCO, check for unreasonable u^r --- model/riaf/model.c | 47 +++++++++++++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/model/riaf/model.c b/model/riaf/model.c index d9e28e4..9b2dc9c 100644 --- a/model/riaf/model.c +++ b/model/riaf/model.c @@ -209,30 +209,47 @@ void get_model_fourv(double X[NDIM], double Kcon[NDIM], double Ucon[NDIM], doubl bl_Ucon_isco[1] = 0.0; bl_Ucon_isco[2] = 0.0; bl_Ucon_isco[3] = omegaK_isco; + double bl_gcov_isco[NDIM][NDIM]; gcov_bl(r_isco, th, bl_gcov_isco); + normalize(bl_Ucon_isco, bl_gcov_isco); flip_index(bl_Ucon_isco, bl_gcov_isco, bl_Ucov_isco); double e = bl_Ucov_isco[0]; double l = bl_Ucov_isco[3]; + // ...then set the infall velocity and find omega - bl_Ucov_isco[1] = -sqrt(fmax(0.0, - -(1.0 + bl_gcon[0][0] * e * e + - 2.0 * bl_gcon[0][3] * e * l + - bl_gcon[3][3] * l * l) / bl_gcov[1][1])); - bl_Ucov_isco[2] = 0.0; - flip_index(bl_Ucov_isco, bl_gcon, bl_Ucon_isco); - omegaK = bl_Ucon_isco[3] / bl_Ucon_isco[0]; + double bl_Ucon_tmp[NDIM], bl_Ucov_tmp[NDIM]; + double K_con = bl_gcon[0][0] * e * e + 2.0 * bl_gcon[0][3] * e * l + bl_gcon[3][3] * l * l; + double urk_precut = -(1.0 + K_con) / bl_gcon[1][1]; + double urk = -sqrt(fmax(0.0, urk_precut)); + bl_Ucov_tmp[0] = e; + bl_Ucov_tmp[1] = urk; + bl_Ucov_tmp[2] = 0.0; + bl_Ucov_tmp[3] = l; + flip_index(bl_Ucov_tmp, bl_gcon, bl_Ucon_tmp); + omegaK = bl_Ucon_tmp[3] / bl_Ucon_tmp[0]; omegaFF = bl_gcon[0][3] / bl_gcon[0][0]; - // Compromise omega = omegaK + (1 - keplerian_factor)*(omegaFF - omegaK); - // Set infall rate + + // Then set the infall rate double urFF = -sqrt(fmax(0.0, -(1.0 + bl_gcon[0][0]) * bl_gcon[1][1])); - ur = bl_Ucon_isco[1] + infall_factor * (urFF - bl_Ucon_isco[1]); + ur = bl_Ucon_tmp[1] + infall_factor * (urFF - bl_Ucon_tmp[1]); - // Get the normal observer velocity for Ucon/Ucov, in BL coordinates +#if DEBUG + if (fabs(ur) < 1e-10) { + fprintf(stderr, "Bad ur: ur is %g\n", ur); + fprintf(stderr, "Ucon BL: %g %g %g %g\n", + bl_Ucon_tmp[0], bl_Ucon_tmp[1], bl_Ucon_tmp[2], bl_Ucon_tmp[3]); + fprintf(stderr, "Ucov BL: %g %g %g %g\n", + bl_Ucov_tmp[0], bl_Ucov_tmp[1], bl_Ucov_tmp[2], bl_Ucov_tmp[3]); + fprintf(stderr, "urk was %g (%g pre-cut), e & l were %g %g\n", urk, urk_precut, e, l); + } +#endif + + // Finally, get Ucon in BL coordinates K = bl_gcov[0][0] + 2*omega*bl_gcov[0][3] + omega*omega*bl_gcov[3][3]; ut = sqrt(fmax(0.0, -(1. + ur*ur*bl_gcov[1][1]) / K)); bl_Ucon[0] = ut; @@ -270,10 +287,14 @@ void get_model_fourv(double X[NDIM], double Kcon[NDIM], double Ucon[NDIM], doubl // Check #if DEBUG + //if (r < r_isco) { fprintf(stderr, "ur = %g\n", Ucon[1]); } + double bl_Ucov[NDIM]; double dot_U = Ucon[0]*Ucov[0] + Ucon[1]*Ucov[1] + Ucon[2]*Ucov[2] + Ucon[3]*Ucov[3]; double sum_U = Ucon[0]+Ucon[1]+Ucon[2]+Ucon[3]; - if (get_fluid_nu(Kcon, Ucov) == 1. || fabs(fabs(dot_U) - 1.) > 1e-10 || sum_U < 0.1) { - double bl_Ucov[NDIM]; + // Following condition gets handled better above + // (r < r_isco && fabs(Ucon[1]) < 1e-10) || + if (get_fluid_nu(Kcon, Ucov) == 1. || + fabs(fabs(dot_U) - 1.) > 1e-10 || sum_U < 0.1) { flip_index(bl_Ucon, bl_gcov, bl_Ucov); fprintf(stderr, "RIAF model problem at r, th, phi = %g %g %g\n", r, th, X[3]); fprintf(stderr, "Omega K: %g FF: %g Final: %g K: %g ur: %g ut: %g\n", From adb0c149b5879bb4f48aa72e67f44f099dff5f03 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Fri, 23 Apr 2021 17:23:31 -0500 Subject: [PATCH 27/93] Move powerlaw and kappa parameters completely to model_radiation and add option for domain-variable kappa parameter, preliminary functions returning plasma sigma & beta in relevant models --- model/analytic/model.c | 4 +- model/iharm/model.c | 54 ++++----- model/riaf/model.c | 57 +++++---- model/sphere/model.c | 45 +++---- model/thin_disk/model.c | 4 +- src/model.h | 4 +- src/model_radiation.c | 263 ++++++++++++++++++++++++---------------- 7 files changed, 233 insertions(+), 198 deletions(-) diff --git a/model/analytic/model.c b/model/analytic/model.c index 4cee660..a1e607c 100644 --- a/model/analytic/model.c +++ b/model/analytic/model.c @@ -317,8 +317,8 @@ int radiating_region(double X[NDIM]) // Define these to specify a fluid model: e- density/temperature for // synchrotron radiation based on an energy distribution double get_model_thetae(double X[NDIM]) {return 0;} -void get_model_powerlaw_vals(double X[NDIM], double *p, double *n, - double *gamma_min, double *gamma_max, double *gamma_cut) {return;} +double get_model_sigma(double X[NDIM]) {return 0;} +double get_model_beta(double X[NDIM]) {return 0;} // This is only called for trace file output, and doesn't really apply to analytic models void get_model_primitives(double X[NDIM], double *p) {return;} diff --git a/model/iharm/model.c b/model/iharm/model.c index 4d97f8e..d46c2ab 100644 --- a/model/iharm/model.c +++ b/model/iharm/model.c @@ -46,12 +46,6 @@ static double tp_over_te = 3.; static double trat_small = 1.; static double trat_large = 40.; -static double powerlaw_gamma_min = 1e2; -static double powerlaw_gamma_max = 1e5; -static double powerlaw_gamma_cut = 1e10; -static double powerlaw_eta = 0.02; -static double powerlaw_p = 3.25; - static int dumpskip = 1; static int dumpmin, dumpmax, dumpidx; static double MBH_solar = 6.2e9; @@ -87,6 +81,7 @@ struct of_data { double ***thetae; double ***b; double ***sigma; + double ***beta; }; static struct of_data dataA, dataB, dataC; static struct of_data *data[NSUP]; @@ -120,13 +115,6 @@ void try_set_model_parameter(const char *word, const char *value) set_by_word_val(word, value, "sigma_cut", &sigma_cut, TYPE_DBL); set_by_word_val(word, value, "beta_crit", &beta_crit, TYPE_DBL); - // TODO: figure out how to make consistent with model_radiation.c - set_by_word_val(word, value, "powerlaw_gamma_min", &powerlaw_gamma_min, TYPE_DBL); - set_by_word_val(word, value, "powerlaw_gamma_max", &powerlaw_gamma_max, TYPE_DBL); - set_by_word_val(word, value, "powerlaw_gamma_cut", &powerlaw_gamma_cut, TYPE_DBL); - set_by_word_val(word, value, "powerlaw_eta", &powerlaw_eta, TYPE_DBL); - set_by_word_val(word, value, "powerlaw_p", &powerlaw_p, TYPE_DBL); - set_by_word_val(word, value, "rmax_geo", &rmax_geo, TYPE_DBL); set_by_word_val(word, value, "rmin_geo", &rmin_geo, TYPE_DBL); @@ -482,6 +470,18 @@ double get_model_sigma(double X[NDIM]) return tfac*sigmaA + (1. - tfac)*sigmaB; } +double get_model_beta(double X[NDIM]) +{ + if ( X_in_domain(X) == 0 ) return 0.; // TODO inf? + + double betaA, betaB, tfac; + int nA, nB; + tfac = set_tinterp_ns(X, &nA, &nB); + betaA = interp_scalar(X, data[nA]->beta); + betaB = interp_scalar(X, data[nB]->beta); + return tfac*betaA + (1. - tfac)*betaB; +} + double get_model_ne(double X[NDIM]) { if ( X_in_domain(X) == 0 ) return 0.; @@ -527,12 +527,12 @@ void init_physical_quantities(int n) bsq = bsq*bsq; double sigma_m = bsq/data[n]->p[KRHO][i][j][k]; + double beta_m = data[n]->p[UU][i][j][k]*(gam-1.)/0.5/bsq; if (ELECTRONS == 1) { data[n]->thetae[i][j][k] = data[n]->p[KEL][i][j][k]*pow(data[n]->p[KRHO][i][j][k],game-1.)*Thetae_unit; } else if (ELECTRONS == 2) { - double beta = data[n]->p[UU][i][j][k]*(gam-1.)/0.5/bsq; - double betasq = beta*beta / beta_crit/beta_crit; + double betasq = beta_m*beta_m / beta_crit/beta_crit; double trat = trat_large * betasq/(1. + betasq) + trat_small /(1. + betasq); //Thetae_unit = (gam - 1.) * (MP / ME) / trat; // see, e.g., Eq. 8 of the EHT GRRT formula list @@ -543,8 +543,13 @@ void init_physical_quantities(int n) } data[n]->thetae[i][j][k] = fmax(data[n]->thetae[i][j][k], 1.e-3); - //strongly magnetized = empty, no shiny spine - data[n]->sigma[i][j][k] = sigma_m; // allow sigma cut per geodesic step + // Preserve sigma for cutting along geodesics, and for variable-kappa model + data[n]->sigma[i][j][k] = sigma_m; + // Also record beta, for variable-kappa model + data[n]->beta[i][j][k] = beta_m; + + // Cut Ne (i.e. emission) based on sigma, if we're not doing so along each geodesic + // Strongly magnetized = empty, no shiny spine if (sigma_m > sigma_cut && !USE_GEODESIC_SIGMACUT) { data[n]->b[i][j][k]=0.0; data[n]->ne[i][j][k]=0.0; @@ -565,6 +570,7 @@ void init_storage(void) data[n]->thetae = malloc_rank3(N1+2,N2+2,N3+2); data[n]->b = malloc_rank3(N1+2,N2+2,N3+2); data[n]->sigma = malloc_rank3(N1+2,N2+2,N3+2); + data[n]->beta = malloc_rank3(N1+2,N2+2,N3+2); } } @@ -1408,20 +1414,6 @@ int radiating_region(double X[NDIM]) return (r > rmin_geo && r < rmax_geo && th > th_beg && th < (M_PI-th_beg)); } -void get_model_powerlaw_vals(double X[NDIM], double *p, double *n, - double *gamma_min, double *gamma_max, double *gamma_cut) -{ - *gamma_min = powerlaw_gamma_min; - *gamma_max = powerlaw_gamma_max; - *gamma_cut = powerlaw_gamma_cut; - *p = powerlaw_p; - - double b = get_model_b(X); - double u_nth = powerlaw_eta*b*b/2; - // Number density of nonthermals - *n = u_nth * (*p - 2)/(*p - 1) * 1/(ME * CL*CL * *gamma_min); -} - // In case we want to mess with emissivities directly void get_model_jar(double X[NDIM], double Kcon[NDIM], double *jI, double *jQ, double *jU, double *jV, diff --git a/model/riaf/model.c b/model/riaf/model.c index 9b2dc9c..610d1fa 100644 --- a/model/riaf/model.c +++ b/model/riaf/model.c @@ -26,9 +26,12 @@ static double MBH_solar = 4.3e6; static double MBH; -// e.g. parameterization from GRRT paper +double gam = 4./3; +// Require all these double nth0, Te0, disk_h, pow_nth, pow_T; -double keplerian_factor, infall_factor; +// Default fully keplerian, no infall velocity +double keplerian_factor = 1.; +double infall_factor = 0.; double r_isco; /** @@ -65,6 +68,8 @@ void try_set_model_parameter(const char *word, const char *value) // Geometry parameters set_by_word_val(word, value, "a", &a, TYPE_DBL); + // Fluid parameter (only used for pressure calculation for beta) + set_by_word_val(word, value, "gam", &gam, TYPE_DBL); // RIAF model parameters, from Odyssey. // Note nth0,Te0 are *different* as they are unitless! set_by_word_val(word, value, "nth0", &nth0, TYPE_DBL); @@ -172,6 +177,27 @@ double get_model_thetae(double X[NDIM]) return Te0 * pow(r, pow_T) * Te_unit * KBOL / (ME*CL*CL); } +double get_model_b(double X[NDIM]) +{ + // double Ucon[NDIM],Bcon[NDIM]; + // double Ucov[NDIM],Bcov[NDIM]; + // double Kcon[NDIM] = {0}; // TODO interface change if we ever need a real one here + // get_model_fourv(X, Kcon, Ucon, Ucov, Bcon, Bcov); + // return sqrt(Bcon[0]*Bcov[0] + Bcon[1]*Bcov[1] + Bcon[2]*Bcov[2] + Bcon[3]*Bcov[3]) * B_unit; + + double r, th; + bl_coord(X, &r, &th); + double nth = get_model_ne(X); + // Get local field strength + double eps = 0.1; + double b = sqrt(8. * M_PI * eps * nth * MP * CL * CL / 6. / r); + if (b == 0) b = 1.e-6; + return b; +} +// TODO? +double get_model_sigma(double X[NDIM]) {return 0.;} +double get_model_beta(double X[NDIM]) {return 0.;} + void get_model_fourv(double X[NDIM], double Kcon[NDIM], double Ucon[NDIM], double Ucov[NDIM], double Bcon[NDIM], double Bcov[NDIM]) { @@ -339,29 +365,6 @@ void get_model_fourv(double X[NDIM], double Kcon[NDIM], double Ucon[NDIM], doubl flip_index(Bcon, gcov, Bcov); } -/** - * This problem defines no field in emission, but we want to control ipole's worst - * tendencies when making tetrads. This will return a correct value even for a - * possible fluid/field model later, too. - */ -double get_model_b(double X[NDIM]) -{ - // double Ucon[NDIM],Bcon[NDIM]; - // double Ucov[NDIM],Bcov[NDIM]; - // double Kcon[NDIM] = {0}; // TODO interface change if we ever need a real one here - // get_model_fourv(X, Kcon, Ucon, Ucov, Bcon, Bcov); - // return sqrt(Bcon[0]*Bcov[0] + Bcon[1]*Bcov[1] + Bcon[2]*Bcov[2] + Bcon[3]*Bcov[3]) * B_unit; - - double r, th; - bl_coord(X, &r, &th); - double nth = get_model_ne(X); - // Get local field strength - double eps = 0.1; - double b = sqrt(8. * M_PI * eps * nth * MP * CL * CL / 6. / r); - if (b == 0) b = 1.e-6; - return b; -} - int radiating_region(double X[NDIM]) { // If you don't want conditionals in get_model_jar, @@ -372,10 +375,6 @@ int radiating_region(double X[NDIM]) } //// STUBS: Functions for normal models which we don't use //// -// Define these to specify a fluid model: e- density/temperature for -// synchrotron radiation based on an energy distribution -void get_model_powerlaw_vals(double X[NDIM], double *p, double *n, - double *gamma_min, double *gamma_max, double *gamma_cut) {return;} // This is only called for trace file output, and doesn't really apply to analytic models void get_model_primitives(double X[NDIM], double *p) {return;} void get_model_jk(double X[NDIM], double Kcon[NDIM], double *jnuinv, double *knuinv) {return;} diff --git a/model/sphere/model.c b/model/sphere/model.c index a23ac5d..1e65763 100644 --- a/model/sphere/model.c +++ b/model/sphere/model.c @@ -26,15 +26,10 @@ static double MODEL_MBH = 4.1e6; static double MODEL_TP_OVER_TE = 3; static double MODEL_GAM = 13./9; -// TODO: duplicated code! -static double powerlaw_gamma_min = 1e2; -static double powerlaw_gamma_max = 1e5; -static double powerlaw_gamma_cut = 1e10; -static double powerlaw_p = 3.25; - // derived model parameters double model_Ne_0 = 1.; double model_B_0 = 1.; +double THETAE_UNIT = 1.; void try_set_model_parameter(const char *word, const char *value) { @@ -45,12 +40,6 @@ void try_set_model_parameter(const char *word, const char *value) set_by_word_val(word, value, "gam", &MODEL_GAM, TYPE_DBL); set_by_word_val(word, value, "tp_over_te", &MODEL_TP_OVER_TE, TYPE_DBL); set_by_word_val(word, value, "MBH", &MODEL_MBH, TYPE_DBL); - - // TODO: figure out how to make consistent with model_radiation.c - set_by_word_val(word, value, "powerlaw_gamma_min", &powerlaw_gamma_min, TYPE_DBL); - set_by_word_val(word, value, "powerlaw_gamma_max", &powerlaw_gamma_max, TYPE_DBL); - set_by_word_val(word, value, "powerlaw_gamma_cut", &powerlaw_gamma_cut, TYPE_DBL); - set_by_word_val(word, value, "powerlaw_p", &powerlaw_p, TYPE_DBL); } // used in slow light with real data @@ -76,8 +65,6 @@ void init_model(double *tA, double *tB) model_Ne_0 = MODEL_TAU_0 / SIGMA_THOMSON / MODEL_R_0 / L_unit; // derive model B (in gauss) - double THETAE_UNIT = 1.; - // since B = B(pressure), we need to specify the thermodynamics to // find pressure = pressure(Thetae) double gam = MODEL_GAM; @@ -179,6 +166,24 @@ double get_model_b(double X[NDIM]) return model_B_0; } +double get_model_sigma(double X[NDIM]) +{ + if (radiating_region(X) == 0) { + return 0.; + } + + // TODO currently assumes tf==tp + return MODEL_THETAE_0 * MODEL_TP_OVER_TE / MODEL_BETA_0; +} +double get_model_beta(double X[NDIM]) +{ + if (radiating_region(X) == 0) { + return 0.; + } + + return MODEL_BETA_0; +} + double get_model_ne(double X[NDIM]) { if (radiating_region(X) == 0) { @@ -209,18 +214,6 @@ int radiating_region(double X[NDIM]) return (remission_type == E_CUSTOM) { + get_model_jk(X, Kcon, jnuinv, knuinv); + } else { + double nu, theta, B, Thetae, Ne, Bnuinv; + double Ucov[NDIM], Ucon[NDIM], Bcon[NDIM], Bcov[NDIM]; + + /* get fluid parameters */ + Ne = get_model_ne(X); /* check to see if we're outside fluid model */ + if (Ne == 0.) { + *jnuinv = 0.; + *knuinv = 0.; + return; + } + + /* get covariant four-velocity of fluid for use in get_bk_angle and get_fluid_nu */ + get_model_fourv(X, Kcon, Ucon, Ucov, Bcon, Bcov); + theta = get_bk_angle(X, Kcon, Ucov, Bcon, Bcov); /* angle between k & b */ + // No emission along field + if (theta <= 0. || theta >= M_PI) { /* no emission along field */ + *jnuinv = 0.; + *knuinv = 0.; + return; + } + + // Only compute these if we must + B = get_model_b(X); /* field in G */ + Thetae = get_model_thetae(X); /* temp in e rest-mass units */ + nu = get_fluid_nu(Kcon, Ucov); /* freq in Hz */ + + // TODO: could be cleaner here + struct parameters paramsM; + int fit; + double kappa, kappa_width; + + if (params->emission_type == E_KAPPA) { + + setConstParams(¶msM); + fit = paramsM.KAPPA_DIST; + + get_model_kappa(X, &kappa, &kappa_width); + + *jnuinv = j_nu_fit(nu, B, Ne, theta, fit, paramsM.STOKES_I, Thetae, + powerlaw_p, powerlaw_gamma_min, powerlaw_gamma_max, powerlaw_gamma_cut, + kappa, kappa_width) / nu/nu; + *knuinv = alpha_nu_fit(nu, B, Ne, theta, fit, paramsM.STOKES_I, Thetae, + powerlaw_p, powerlaw_gamma_min, powerlaw_gamma_max, powerlaw_gamma_cut, + kappa, kappa_width) * nu; + + } else if (params->emission_type == E_POWERLAW) { + + setConstParams(¶msM); + fit = paramsM.POWER_LAW; + + get_model_powerlaw_vals(X, &powerlaw_p, &Ne, &powerlaw_gamma_min, &powerlaw_gamma_max, &powerlaw_gamma_cut); + + *jnuinv = j_nu_fit(nu, B, Ne, theta, fit, paramsM.STOKES_I, Thetae, + powerlaw_p, powerlaw_gamma_min, powerlaw_gamma_max, powerlaw_gamma_cut, + kappa, kappa_width) / nu/nu; + *knuinv = alpha_nu_fit(nu, B, Ne, theta, fit, paramsM.STOKES_I, Thetae, + powerlaw_p, powerlaw_gamma_min, powerlaw_gamma_max, powerlaw_gamma_cut, + kappa, kappa_width) * nu * exp(-nu / 5.e13); // nu_cutoff = 5.e13 + + } else { + + /* assume emission is thermal */ + Bnuinv = Bnu_inv(nu, Thetae); + *jnuinv = jnu_inv(nu, Thetae, Ne, B, theta); + + if (Bnuinv < SMALL) { + *knuinv = SMALL; + } else { + *knuinv = *jnuinv / Bnuinv; + } + + } + +#if DEBUG + if (isnan(*jnuinv) || isnan(*knuinv)) { + fprintf(stderr, "\nisnan get_jkinv\n"); + fprintf(stderr, ">> %g %g %g %g %g %g %g %g\n", *jnuinv, *knuinv, + Ne, theta, nu, B, Thetae, Bnuinv); + } +#endif + + if (params->isolate_counterjet == 1) { // Emission from X[2] > midplane only + if (X[2] < (cstopx[2] - cstartx[2]) / 2) { + *jnuinv = 0.; + } + } else if (params->isolate_counterjet == 2) { // Emission from X[2] < midplane only + if (X[2] > (cstopx[2] - cstartx[2]) / 2) { + *jnuinv = 0.; + } + } + } +} + +// SUPPORTING FUNCTIONS + +/* + * Get values for the powerlaw distribution: + * power & cutoffs from parameters, plus number density of nonthermals + */ +void get_model_powerlaw_vals(double X[NDIM], double *p, double *n, + double *gamma_min, double *gamma_max, double *gamma_cut) +{ + *gamma_min = powerlaw_gamma_min; + *gamma_max = powerlaw_gamma_max; + *gamma_cut = powerlaw_gamma_cut; + *p = powerlaw_p; + + double b = get_model_b(X); + double u_nth = powerlaw_eta*b*b/2; + // Number density of nonthermals + *n = u_nth * (*p - 2)/(*p - 1) * 1/(ME * CL*CL * *gamma_min); +} + +/* + * Get values kappa, kappa_width for the kappa distribution emissivities + * Optionally variable over the domain based on sigma and beta + */ +void get_model_kappa(double X[NDIM], double *kappa, double *kappa_width) { + + if (variable_kappa) { + double sigma = get_model_sigma(X); + double beta = get_model_beta(X); + *kappa = model_kappa; +#if DEBUG + //fprintf(stderr, "sigma, beta -> kappa %g %g -> %g\n", sigma, beta, *kappa); +#endif + } else { + *kappa = model_kappa; + } + + double Thetae = get_model_thetae(X); + *kappa_width = (*kappa - 3.) / *kappa * Thetae; +} + + + +// POLARIZED FITS + /* * emissivity functions and functions used for Faraday conversion and rotation * from J. Dexter PhD thesis (checked with Leung harmony program, and Huang & Shcherbakov 2011 @@ -509,109 +662,7 @@ double besselk_asym(int n, double x) exit(1); } -// UNPOLARIZED VERSIONS - -/* - * get the invariant emissivity and opacity at a given position for a given wavevector - */ -void get_jkinv(double X[NDIM], double Kcon[NDIM], double *jnuinv, double *knuinv, Params *params) -{ - if (params->emission_type == E_CUSTOM) { - get_model_jk(X, Kcon, jnuinv, knuinv); - } else { - double nu, theta, B, Thetae, Ne, Bnuinv; - double Ucov[NDIM], Ucon[NDIM], Bcon[NDIM], Bcov[NDIM]; - - /* get fluid parameters */ - Ne = get_model_ne(X); /* check to see if we're outside fluid model */ - if (Ne == 0.) { - *jnuinv = 0.; - *knuinv = 0.; - return; - } - - /* get covariant four-velocity of fluid for use in get_bk_angle and get_fluid_nu */ - get_model_fourv(X, Kcon, Ucon, Ucov, Bcon, Bcov); - theta = get_bk_angle(X, Kcon, Ucov, Bcon, Bcov); /* angle between k & b */ - // No emission along field - if (theta <= 0. || theta >= M_PI) { /* no emission along field */ - *jnuinv = 0.; - *knuinv = 0.; - return; - } - - // Only compute these if we must - B = get_model_b(X); /* field in G */ - Thetae = get_model_thetae(X); /* temp in e rest-mass units */ - nu = get_fluid_nu(Kcon, Ucov); /* freq in Hz */ - - // TODO: could be cleaner here - struct parameters paramsM; - int fit; - double kappa, kappa_width; - - if (params->emission_type == E_KAPPA) { - - setConstParams(¶msM); - fit = paramsM.KAPPA_DIST; - - kappa = model_kappa; - kappa_width = (kappa - 3.) / kappa * Thetae; - - *jnuinv = j_nu_fit(nu, B, Ne, theta, fit, paramsM.STOKES_I, Thetae, - powerlaw_p, powerlaw_gamma_min, powerlaw_gamma_max, powerlaw_gamma_cut, - kappa, kappa_width) / nu/nu; - *knuinv = alpha_nu_fit(nu, B, Ne, theta, fit, paramsM.STOKES_I, Thetae, - powerlaw_p, powerlaw_gamma_min, powerlaw_gamma_max, powerlaw_gamma_cut, - kappa, kappa_width) * nu; - - } else if (params->emission_type == E_POWERLAW) { - - setConstParams(¶msM); - fit = paramsM.POWER_LAW; - - get_model_powerlaw_vals(X, &powerlaw_p, &Ne, &powerlaw_gamma_min, &powerlaw_gamma_max, &powerlaw_gamma_cut); - - *jnuinv = j_nu_fit(nu, B, Ne, theta, fit, paramsM.STOKES_I, Thetae, - powerlaw_p, powerlaw_gamma_min, powerlaw_gamma_max, powerlaw_gamma_cut, - kappa, kappa_width) / nu/nu; - *knuinv = alpha_nu_fit(nu, B, Ne, theta, fit, paramsM.STOKES_I, Thetae, - powerlaw_p, powerlaw_gamma_min, powerlaw_gamma_max, powerlaw_gamma_cut, - kappa, kappa_width) * nu * exp(-nu / 5.e13); // nu_cutoff = 5.e13 - - } else { - - /* assume emission is thermal */ - Bnuinv = Bnu_inv(nu, Thetae); - *jnuinv = jnu_inv(nu, Thetae, Ne, B, theta); - - if (Bnuinv < SMALL) { - *knuinv = SMALL; - } else { - *knuinv = *jnuinv / Bnuinv; - } - - } - -#if DEBUG - if (isnan(*jnuinv) || isnan(*knuinv)) { - fprintf(stderr, "\nisnan get_jkinv\n"); - fprintf(stderr, ">> %g %g %g %g %g %g %g %g\n", *jnuinv, *knuinv, - Ne, theta, nu, B, Thetae, Bnuinv); - } -#endif - - if (params->isolate_counterjet == 1) { // Emission from X[2] > midplane only - if (X[2] < (cstopx[2] - cstartx[2]) / 2) { - *jnuinv = 0.; - } - } else if (params->isolate_counterjet == 2) { // Emission from X[2] < midplane only - if (X[2] > (cstopx[2] - cstartx[2]) / 2) { - *jnuinv = 0.; - } - } - } -} +// UNPOLARIZED FITS /* * thermal synchrotron emissivity From 4ee08f8f9c26a11ba1c8e78870e879f2b40f42b9 Mon Sep 17 00:00:00 2001 From: George Wong Date: Sat, 24 Apr 2021 19:10:26 -0500 Subject: [PATCH 28/93] initialize coordinate names --- src/coordinates.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coordinates.c b/src/coordinates.c index 194196f..5b95310 100644 --- a/src/coordinates.c +++ b/src/coordinates.c @@ -5,8 +5,8 @@ #include "decs.h" #include "geometry.h" -int use_eKS_internal; -int metric; +int use_eKS_internal = 0; +int metric = -1; double a, hslope; // mks double poly_norm, poly_xt, poly_alpha, mks_smooth; // fmks double mks3R0, mks3H0, mks3MY1, mks3MY2, mks3MP0; // mks3 From fbf4041a7b1e9a8214f49dfac8b808ad284cd26f Mon Sep 17 00:00:00 2001 From: George Wong Date: Sat, 24 Apr 2021 21:51:44 -0500 Subject: [PATCH 29/93] add comparison riaf fluxes. note required high resolution. --- model/riaf/riaf.txt | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 model/riaf/riaf.txt diff --git a/model/riaf/riaf.txt b/model/riaf/riaf.txt new file mode 100644 index 0000000..de3c376 --- /dev/null +++ b/model/riaf/riaf.txt @@ -0,0 +1,44 @@ +# frequency inc Ftot Ftot_unpol nuLnu nuLnu_unpol +3162280000.0 90.0 0.0028489597534122634 0.0028339632545509746 7.425982004760713e+29 7.386892744006865e+29 +5360020000.0 90.0 0.009395550853617164 0.009367009171960488 4.151030407630589e+30 4.1384204616798237e+30 +9085180000.0 90.0 0.028418941300760378 0.028332481384368453 2.1281816205062987e+31 2.1217069808274846e+31 +15399300000.0 90.0 0.08140362809629215 0.08123329920348317 1.0332656651010776e+32 1.031103660767487e+32 +26101600000.0 90.0 0.21372986326771087 0.21402505951085055 4.598324278322123e+32 4.604675323660127e+32 +44241900000.0 90.0 0.4985393092576503 0.5065783945320218 1.8180274423784825e+33 1.847343641460539e+33 +74989400000.0 90.0 0.9693335017865592 1.0290719972737428 5.991566152976433e+33 6.360816928825053e+33 +127106000000.0 90.0 1.6967308037169337 1.8540498052723284 1.7776499837967799e+34 1.9424717221380902e+34 +215443000000.0 90.0 2.68266378175895 2.9538299046403664 4.763935697999135e+34 5.245478700766253e+34 +365174000000.0 90.0 3.630064157190802 3.9575557567899375 1.0926508462497957e+35 1.1912259562055964e+35 +618966000000.0 90.0 4.005393607427599 4.317732383232477 2.0435216873310643e+35 2.2028745811310884e+35 +1049140000000.0 90.0 3.652873635371252 3.8143217620662804 3.1588961702383436e+35 3.2985116948956176e+35 +1778280000000.0 90.0 2.7902571969940904 2.836653376710832 4.089890099296295e+35 4.1578964738602944e+35 +3014160000000.0 90.0 1.8512294055885699 1.8591252216322147 4.599321826236051e+35 4.618938735386181e+35 +5108970000000.0 90.0 1.0907044421523815 1.0908125562614366 4.59311893684354e+35 4.5935742214673565e+35 +8659640000000.0 90.0 0.5688984618077184 0.5682829874773504 4.060708872262758e+35 4.056315712073796e+35 +14678000000000.0 90.0 0.2578378716313442 0.2574897956591044 3.1194702483664345e+35 3.1152590258927796e+35 +24879000000000.0 90.0 0.09878668347435732 0.09865108125223593 2.025809401996887e+35 2.0230286197411848e+35 +42169700000000.0 90.0 0.030913145659099684 0.030871757735873903 1.0745112544752e+35 1.073072650012365e+35 +71477100000000.0 90.0 0.00757933021077172 0.007569505157504826 4.46544772187297e+34 4.459659181130204e+34 +121153000000000.0 90.0 0.0013850575286171222 0.001383317986259005 1.383149875354638e+34 1.3814127288852014e+34 +205353000000000.0 90.0 0.0001776853512653615 0.00017747000043365232 3.0075990158462426e+33 3.003953870397292e+33 +348070000000000.0 90.0 1.4892572562068718e-05 1.4879595743278076e-05 4.272711554195851e+32 4.268988476577636e+32 +589975000000000.0 90.0 7.415244637549087e-07 7.485854340162352e-07 3.606005206585084e+31 3.640342435861315e+31 +1000000000000000.0 90.0 2.503494934729222e-08 2.041599732909495e-08 2.0635451426734356e+30 1.682820745385068e+30 +223005000000.0 5.0 1.864400335340764 1.9744099453676192 3.427054648904674e+34 3.629269236791356e+34 +223005000000.0 10.0 1.8943145448547998 2.013637196869679 3.4820415682052686e+34 3.701374960050258e+34 +223005000000.0 15.0 1.9455349442371501 2.0784656687799745 3.5761925423789325e+34 3.8205396650925143e+34 +223005000000.0 20.0 2.014025566421305 2.165533923411518 3.7020888430356695e+34 3.9805845123020645e+34 +223005000000.0 25.0 2.0974178031963695 2.272477364946569 3.855376603880396e+34 4.177163010779739e+34 +223005000000.0 30.0 2.1902815688664155 2.392769883648396 4.0260744920011666e+34 4.3982791668064985e+34 +223005000000.0 35.0 2.289418662137776 2.523222627282035 4.20830372138629e+34 4.638071379378869e+34 +223005000000.0 40.0 2.387144877008537 2.6542360547782704 4.387939539211589e+34 4.878894215150267e+34 +223005000000.0 45.0 2.4793324106438557 2.780585799872368 4.557394408816205e+34 5.111144485172615e+34 +223005000000.0 50.0 2.557780782466688 2.8923270458402857 4.701594585279541e+34 5.316542086326103e+34 +223005000000.0 55.0 2.6177323156761805 2.9818046832488863 4.81179472668694e+34 5.481015749756392e+34 +223005000000.0 60.0 2.6534246703175217 3.0402553586023138 4.87740276568228e+34 5.588457083521743e+34 +223005000000.0 65.0 2.664715265817202 3.0667273264240706 4.898156617235848e+34 5.637116633013094e+34 +223005000000.0 70.0 2.6523728644264866 3.055358743885895 4.8754693846372925e+34 5.616219429284695e+34 +223005000000.0 75.0 2.6304077917851245 3.021039735088821 4.835094201860172e+34 5.553135811236366e+34 +223005000000.0 80.0 2.641159256586151 2.9925459371937095 4.854857048245929e+34 5.500759827017536e+34 +223005000000.0 85.0 2.701407659466602 3.0067494180801506 4.9656028817810815e+34 5.52686801005088e+34 +223005000000.0 90.0 2.7548546280928115 3.0283057950703323 5.063846632776135e+34 5.566491947344498e+34 From 33b2a0aea9e5a98230bdbf8c0e651f6f8f5f6d0c Mon Sep 17 00:00:00 2001 From: George Wong Date: Mon, 26 Apr 2021 11:24:06 -0500 Subject: [PATCH 30/93] remove Kcon from dump_at_X since it's not actually used in to get the fourv --- src/debug_tools.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/debug_tools.c b/src/debug_tools.c index f923cad..785d1fd 100644 --- a/src/debug_tools.c +++ b/src/debug_tools.c @@ -150,7 +150,7 @@ void get_model_fourv(double X[NDIM], double Kcon[NDIM], double Ucon[NDIM], double Ucov[NDIM], double Bcon[NDIM], double Bcov[NDIM]); -void dump_at_X(double X[NDIM], double Kcon[NDIM]) +void dump_at_X(double X[NDIM]) { // warning that this does not necessarily print contiguously! double r, h; @@ -158,7 +158,7 @@ void dump_at_X(double X[NDIM], double Kcon[NDIM]) bl_coord(X, &r, &h); gcov_func(X, gcov); gcon_func(gcov, gcon); - get_model_fourv(X, Kcon, ucon, ucov, bcon, bcov); + get_model_fourv(X, X, ucon, ucov, bcon, bcov); double Ne = get_model_ne(X); double Thetae = get_model_thetae(X); double B = get_model_b(X); From db2fa1fbf8186de4857c807f39279f033aa686df Mon Sep 17 00:00:00 2001 From: George Wong Date: Sun, 2 May 2021 02:25:35 -0500 Subject: [PATCH 31/93] add koral helper functions. might remove. --- src/koral_coords.c | 202 +++++++++++++++++++++++++++++++++++++++++++++ src/koral_coords.h | 21 +++++ 2 files changed, 223 insertions(+) create mode 100644 src/koral_coords.c create mode 100644 src/koral_coords.h diff --git a/src/koral_coords.c b/src/koral_coords.c new file mode 100644 index 0000000..0d47fe2 --- /dev/null +++ b/src/koral_coords.c @@ -0,0 +1,202 @@ +#include "koral_coords.h" +#include "simcoords.h" +#include "coordinates.h" + +#include + +#define TNY 96 +#define RCYL 30. +#define NCYL 1. +#define MINY (0.0025) +#define MAXY (1.-0.0025) +#define RMIN (0.8*(1.+sqrt(1-a*a))) + +static double x2cyl, rmidcyl, thetaCYL, sinthetaCYL, thetaAX, sinthetaAX; + +double KORAL_theta_diskjet(double r, double x2, void *params); + +int set_cyl_params() +{ + jetcoords_params tpar; + x2cyl = MINY + 0.5*NCYL/((double)TNY); + rmidcyl = 0.5 * (RCYL + RMIN); + + tpar.r0 = mp_koral_jetcoords.mksr0; + tpar.rbrk = mp_koral_jetcoords.rbrk; + tpar.fdisk = mp_koral_jetcoords.fdisk; + tpar.fjet = mp_koral_jetcoords.fjet; + tpar.runi = mp_koral_jetcoords.runi; + tpar.rcoll_jet = mp_koral_jetcoords.rcoll_jet; + tpar.rcoll_disk = mp_koral_jetcoords.rcoll_disk; + tpar.rdecoll_jet = mp_koral_jetcoords.rdecoll_jet; + tpar.rdecoll_disk = mp_koral_jetcoords.rdecoll_disk; + tpar.alpha_1 = mp_koral_jetcoords.alpha_1; + tpar.alpha_2 = mp_koral_jetcoords.alpha_2; + + thetaCYL = KORAL_theta_diskjet(RCYL, x2cyl, &tpar); + sinthetaCYL = sin(thetaCYL); + thetaAX = KORAL_theta_diskjet(RCYL, MAXY, &tpar); + sinthetaAX = sin(thetaAX); + + return 0; +} + +double sinth1(double r, double x2, void* params); +double sinth2(double r, double x2, void* params); + +double sinth0(double r, double x2, void* params) +{ + double sinth0 = RCYL * sin(thetaCYL)/r; + return sinth0; +} + +double minn(double a, double b, double df) +{ + double delta = (b-a)/df; + return b - psi_smooth(delta)*df; +} + +double maxx(double a, double b, double df) +{ + return -1 * minn(-a, -b, df); +} + +double psi_smooth(double x) +{ + if (x < -1) { + return 0.; + } else if(x>=1) { + return x; + } else { + double xout = (-35.*cos(0.5*M_PI*x) - (5./6.)*cos(1.5*M_PI*x) + 0.1*cos(2.5*M_PI*x))/(32.*M_PI); + return xout + 0.5*(x+1.); + } +} + +double theta_smooth(double x) +{ + if (x < -1) { + return 0.; + } else if (x >= 1) { + return 1.; + } else { + return 0.5 + (70.*sin(0.5*M_PI*x) + 5*sin(1.5*M_PI*x) - sin(2.5*M_PI*x))/128.; + } +} + +double theta_disk_or_jet(double r, double x2, double rdecoll, double rcoll, double runi, double a1, double a2) +{ + double r1 = minn(r, rdecoll, 0.5*rdecoll)/runi; + double r2 = minn(r/(r1*runi), rcoll/rdecoll, 0.5*rcoll/rdecoll); + double y = pow(r2, a2)*tan(0.5*x2*M_PI); + double x = pow(r1, a1); //opposite sign convention for alpha1 from ressler 2017! + return 0.5*M_PI + atan2(y,x); +} + +double wjet(double x2, double fdisk, double fjet) +{ + //NOTE! fjet and fdisk must both be positive and sum to < 1. + //NOTE! fjet is NOT defined as in Ressler 2017: their fjet = 1 - (our fjet) + double delta = 2*(fabs(x2) - fdisk)/(1 - fjet - fdisk) - 1.; + return theta_smooth(delta); +} + +double KORAL_theta_diskjet(double r, double x2, void *params) +{ + jetcoords_params *par = (jetcoords_params *)params; + double theta_disk, theta_jet, wfrac, theta; + + theta_disk = theta_disk_or_jet(r, x2, par->rdecoll_disk, par->rcoll_disk, par->runi, + par->alpha_1, par->alpha_2); + theta_jet = theta_disk_or_jet(r, x2, par->rdecoll_jet, par->rcoll_jet, par->runi, + par->alpha_1, par->alpha_2); + wfrac = wjet(x2, par->fdisk, par->fjet); + theta = wfrac*theta_jet + (1-wfrac)*theta_disk; + + return theta; +} + +double to1stquad(double x2) +{ + double ntimes = floor(0.25*(x2+2)); + double x2out = x2 - 4*ntimes; + + if (x2out > 0) { + x2out = -x2out; + } + + if (x2out < -1) { + x2out = - 2 - x2out; + } + + return x2out; +} + +double f2func(double r, double x2, void* params) +{ + jetcoords_params *par = (jetcoords_params *)params; + + double s1in = sinth1(r, x2, par); + double s2in = sinth2(r, x2, par); + + double s1ax = sinth1(r, MAXY, par); + double s2ax = sinth2(r, MAXY, par); + double df = fabs(s2ax - s1ax) + 1.e-16; + + if (r >= RCYL) { + return maxx(s1in, s2in, df); + } else { + return minn(s1in, s2in, df); + } +} + +double sinth1(double r, double x2, void* params) +{ + jetcoords_params *par = (jetcoords_params *) params; + double theta1 = KORAL_theta_diskjet(RCYL, x2, par); + double sinth1 = RCYL*sin(theta1)/r; + return sinth1; +} + +double sinth2(double r, double x2, void* params) +{ + jetcoords_params *par = (jetcoords_params *) params; + + double theta = KORAL_theta_diskjet(r, x2, par); + double theta2 = KORAL_theta_diskjet(r, x2cyl, par); + + double thetamid = 0.5*M_PI; + + double thetaA = asin(sinth0(r,x2,par)); + double thetaB = (theta-theta2)*(thetamid-thetaA)/(thetamid-theta2); + double sinth2 = sin(thetaA + thetaB); + + return sinth2; +} + +double KORAL_cylindrify(double r, double x2, void *params) +{ + jetcoords_params *par = (jetcoords_params *) params; + + double thin = KORAL_theta_diskjet(r, x2, par); + + double x2mir = to1stquad(x2); + double thmir = KORAL_theta_diskjet(r, x2mir, par); + + double f1 = sin(thmir); + double f2 = f2func(r, x2mir, par); + + double thmid = KORAL_theta_diskjet(rmidcyl, x2mir, par); + double f1mid = sin(thmid); + double f2mid = f2func(rmidcyl, x2mir, par); + + double df = f2mid - f1mid; + + double thout = asin(maxx(r*f1, r*f2, r*fabs(df) + 1.e-16)/r); + if (x2 != x2mir) { + thout = thin + thmir - thout; + } + + return thout; +} + diff --git a/src/koral_coords.h b/src/koral_coords.h new file mode 100644 index 0000000..65e327d --- /dev/null +++ b/src/koral_coords.h @@ -0,0 +1,21 @@ +#ifndef KORAL_COORDS +#define KORAL_COORDS + +double minn(double a, double b, double df); +double maxx(double a, double b, double df); +double psi_smooth(double x); +double theta_smooth(double x); +double theta_disk_or_jet(double r, double x2, double rdecoll, double rcoll, double runi, double a1, double a2); +double wjet(double x2, double fdisk, double fjet); +double KORAL_theta_diskjet(double r, double x2, void *params); +double KORAL_cylindrify(double r, double x2, void *params); + +typedef struct jetcoords_params_struct { + double r_test, theta_test; + double r0, rbrk, runi; + double rdecoll_jet, rcoll_jet, rdecoll_disk, rcoll_disk; + double alpha_1, alpha_2; + double fdisk, fjet; +} jetcoords_params; + +#endif // KORAL_COORDS From 826d0c220720c844112fdaf3b24c12716c9c9938 Mon Sep 17 00:00:00 2001 From: George Wong Date: Sun, 2 May 2021 03:48:16 -0500 Subject: [PATCH 32/93] add forward and backward interpolation --- src/koral_coords.c | 69 ++++++++++++ src/simcoords.c | 265 +++++++++++++++++++++++++++++++++++++++++++++ src/simcoords.h | 62 +++++++++++ 3 files changed, 396 insertions(+) create mode 100644 src/simcoords.c create mode 100644 src/simcoords.h diff --git a/src/koral_coords.c b/src/koral_coords.c index 0d47fe2..60cd29f 100644 --- a/src/koral_coords.c +++ b/src/koral_coords.c @@ -3,6 +3,7 @@ #include "coordinates.h" #include +#include #define TNY 96 #define RCYL 30. @@ -200,3 +201,71 @@ double KORAL_cylindrify(double r, double x2, void *params) return thout; } + + + +void fwd_KORAL_JETCOORDS(double *xJET, double *xKS) +{ + // get coordinate system parameters + double r0 = mp_koral_jetcoords.mksr0; + double rbrk = mp_koral_jetcoords.rbrk; + double alpha_1 = mp_koral_jetcoords.alpha_1; + double alpha_2 = mp_koral_jetcoords.alpha_2; + double fdisk = mp_koral_jetcoords.fdisk; + double fjet = mp_koral_jetcoords.fjet; + double runi = mp_koral_jetcoords.runi; + double rcoll_jet = mp_koral_jetcoords.rcoll_jet; + double rcoll_disk = mp_koral_jetcoords.rcoll_disk; + double rdecoll_jet = mp_koral_jetcoords.rdecoll_jet; + double rdecoll_disk = mp_koral_jetcoords.rdecoll_disk; + + /* + double x1in = mp_koral_jetcoords.hypx1in; // TODO + double x1out = mp_koral_jetcoords.hypx1out; // TODO + double x1brk = mp_koral_jetcoords.hypx1brk; // TODO + */ + double x1in = 0.; + double x1out = 0.; + double x1brk = 0.; + + // so many parameters, so little time + jetcoords_params tpar; + tpar.r0 = r0; + tpar.rbrk = rbrk; + tpar.runi = runi; + tpar.rcoll_jet = rcoll_jet; + tpar.rcoll_disk = rcoll_disk; + tpar.rdecoll_jet = rdecoll_jet; + tpar.rdecoll_disk = rdecoll_disk; + tpar.alpha_1 = alpha_1; + tpar.alpha_2 = alpha_2; + tpar.fdisk = fdisk; + tpar.fjet = fjet; + + // pass time through + xKS[0] = xJET[0]; + + // get radial coordinate + double x1sc = x1in + xJET[1] * (x1out-x1in); // scale out of 0-1 range + if (x1sc < x1brk) { + xKS[1] = exp(x1sc) + r0; + } else { + // from hyperexp_func + double x1brk = log(rbrk - r0); + xKS[1] = r0 + exp(xJET[1] + 4. * pow(xJET[1] - x1brk, 4)); + } + + // get elevation coordinate + if (mp_koral_jetcoords.cylindrify) { + xKS[2] = KORAL_cylindrify(xKS[1], xJET[2], &tpar); + } else { + // return theta_diskjet(r, x2, par); + assert(1==0); // WARNING, THIS HAS NOT BEEN VALIDATED BY ITSELF! + xKS[2] = KORAL_theta_diskjet(xKS[1], xJET[2], &tpar); + } + + // pass phi through + xKS[3] = xJET[3]; +} + + diff --git a/src/simcoords.c b/src/simcoords.c new file mode 100644 index 0000000..2ba2b0c --- /dev/null +++ b/src/simcoords.c @@ -0,0 +1,265 @@ +#include "simcoords.h" +#include "decs.h" +#include "coordinates.h" +#include "model_params.h" +#include "koral_coords.h" +#include "debug_tools.h" +#include "hdf5_utils.h" + +#include +#include + +// switches +int use_simcoords = 0; +int simcoords = 0; + +// global metric defintiions +metric_params_koral_mks3 mp_koral_mks3 = { 0 }; +metric_params_koral_jetcoords mp_koral_jetcoords = { 0 }; + +// invisible "members" +static double *simcoords_x1 = NULL; +static double *simcoords_x2 = NULL; +static size_t sc_n1 = 1; +static size_t sc_n2 = 1; +static double er0 = 0; +static double h0 = 0; +static double der = 1.e-5; +static double dh = 1.e-5; +static double x1i_oob = 1.e10; + +static double *ks_r = NULL; +static double *ks_h = NULL; +static size_t n1 = 1; +static size_t n2 = 1; +static double startx1, startx2, dx1, dx2, maxks_r; + +// coordinate function forward declarations +static void rev_KORAL_MKS3(double *xKS, double *xMKS); +static void rev_saved_rh(double *eKS, double *gridcoord); + +#define ij2oned(i,j) ((size_t)(j+sc_n2*(i))) + +// interface functions +void load_ks_rh_from_file(const char *fname) +{ + if ( hdf5_open(fname) < 0 ) { + fprintf(stderr, "! unable to open file %s. exiting!\n", fname); + exit(-1); + } + + size_t n3; + hdf5_set_directory("/header/"); + hdf5_read_single_val(&n1, "n1", H5T_STD_I32LE); + hdf5_read_single_val(&n2, "n2", H5T_STD_I32LE); + hdf5_read_single_val(&n3, "n3", H5T_STD_I32LE); + + hdf5_set_directory("/header/geom/"); + hdf5_read_single_val(&startx1, "startx1", H5T_IEEE_F64LE); + hdf5_read_single_val(&startx2, "startx2", H5T_IEEE_F64LE); + hdf5_read_single_val(&dx1, "dx1", H5T_IEEE_F64LE); + hdf5_read_single_val(&dx2, "dx2", H5T_IEEE_F64LE); + + ks_r = calloc(n1*n2, sizeof(*ks_r)); + ks_h = calloc(n1*n2, sizeof(*ks_h)); + + hdf5_set_directory("/grid_out/"); + + hsize_t fdims[] = { n1, n2, n3 }; + hsize_t fstart[] = { 0, 0, 0 }; + hsize_t fcount[] = { n1, n2, 1 }; + hsize_t mdims[] = { n1, n2, 1 }; + hsize_t mstart[] = { 0, 0, 0 }; + + hdf5_read_array(ks_r, "r", 3, fdims, fstart, fcount, mdims, mstart, H5T_IEEE_F64LE); + hdf5_read_array(ks_h, "th", 3, fdims, fstart, fcount, mdims, mstart, H5T_IEEE_F64LE); + + for (int i=0; i maxks_r) { + maxks_r = ks_r[j+n2*i]; + } + } + } + + hdf5_close(); +} + +void initialize_simgrid(size_t n1, size_t n2, double x1i, double x1f, double x2i, double x2f) +{ + if (ks_r == NULL) { + fprintf(stderr, "! must call load_ks_rh_from_file(...) before initialize_simgrid(...)\n"); + exit(-7); + } + + if (use_simcoords == 0) return; + + sc_n1 = n1; + sc_n2 = n2; + + simcoords_x1 = calloc(sc_n1*sc_n2, sizeof(*simcoords_x1)); + simcoords_x2 = calloc(sc_n1*sc_n2, sizeof(*simcoords_x2)); + + // note we've made the assumption that x1i,x2i gives the left edge of the grid and + // x1f,x2f gives the right edge. this means the range is (n1+1)*dx1,(n2+1)*dx2, so + // that we cover the full domain. if we have an oob error when trying to determine + // the ii,jj for interpolating, we return x1f+1, so x1f+1 must not be a valid grid + // coordinate in the fluid model! + double Rin = 1.05 * (1. + sqrt(1. - a*a)); + double Rout = maxks_r; + double h_min = 0.; + double h_max = M_PI; + + fprintf(stderr, "Rout %g\n\n", Rout); + + // set limit for tracking geodesic emission + rmax_geo = fmin(rmax_geo, Rout); + rmin_geo = fmax(rmin_geo, Rin); + + x1i_oob = x1f + 1.; + er0 = log(Rin); + der = (log(Rout) - log(Rin)) / sc_n1; + h0 = h_min; + dh = (h_max - h_min) / sc_n2; + + // coordinate system is MKS, so set reasonable values here + cstartx[0] = 0; + cstartx[1] = log(Rin); + cstartx[2] = 0; + cstartx[3] = 0; + cstopx[0] = 0; + cstopx[1] = log(Rout); + cstopx[2] = 1.0; + cstopx[3] = 2*M_PI; + +#pragma omp parallel for schedule(dynamic,2) collapse(2) shared(simcoords_x1,simcoords_x2) + for (size_t i=0; i= sc_n1 || jj < 0 || jj >= sc_n2) { + gridcoord[1] = x1i_oob; + return; + } + + gridcoord[0] = eKS[0]; + + gridcoord[1] = simcoords_x1[ ij2oned(ii,jj) ] * (1.-di)*(1.-dj) + + simcoords_x1[ ij2oned(ii,jj+1) ] * (1.-di)*dj + + simcoords_x1[ ij2oned(ii+1,jj) ] * di*(1.-dj) + + simcoords_x1[ ij2oned(ii+1,jj+1) ] * di*dj; + + gridcoord[2] = simcoords_x2[ ij2oned(ii,jj) ] * (1.-di)*(1.-dj) + + simcoords_x2[ ij2oned(ii,jj+1) ] * (1.-di)*dj + + simcoords_x2[ ij2oned(ii+1,jj) ] * di*(1.-dj) + + simcoords_x2[ ij2oned(ii+1,jj+1) ] * di*dj; + + gridcoord[3] = eKS[3]; +} + + +////// +// function definitions for populating the interpolation grid +////// + +static void rev_saved_rh(double *eKS, double *gridcoord) +{ + int gridi = -1; + double griddx1 = 0.; + double r = exp(eKS[1]); + + // here we assume x1 is independent of x2 and that grid zones + // are not incredibly skew + for (int i=0; i + +// // +// !!!!!!!!!! WARNING !!!!!!!!!! // +// // +// We assume phi = x3, so x3 // +// must span 0 -> 2 Pi ! // +// // + +// supported coordinate systems: +// KORAL_MKS3 (forward and reverse available, implements reverse) +// KORAL_JETCOORDS (only forward available) + +#define SIMCOORDS_KORAL_MKS3 3 +#define SIMCOORDS_KORAL_JETCOORDS 4 + +extern int use_simcoords; +extern int simcoords; + +// interface +void load_ks_rh_from_file(const char *fname); +void initialize_simgrid(size_t n1, size_t n2, double x1i, double x1f, double x2i, double x2f); +void eks_to_simcoord(double eKS[NDIM], double gridcoord[NDIM]); +void finalize_simgrid(); + +// not particularly neat, but the idea is to "namespace" different +// metric parameters in structs, and then define one global struct +// instance for each coordinate system. +typedef struct metric_params_koral_mks3_struct { + double r0; + double h0; + double my1; + double my2; + double mp0; +} metric_params_koral_mks3; +extern metric_params_koral_mks3 mp_koral_mks3; + +typedef struct metric_params_koral_jetcoords_struct { + double mksr0; + double rbrk; + double rmin; + double rphotomax; + double fjet; + double fdisk; + double runi; + double rcoll_jet; + double rcoll_disk; + double rdecoll_jet; + double rdecoll_disk; + double alpha_1; + double alpha_2; + double cylindrify; + double rcyl; + double ncyl; +} metric_params_koral_jetcoords; +extern metric_params_koral_jetcoords mp_koral_jetcoords; + +#endif // SIMCOORDS_H From 8bbe9b6a08b5dec079db3ca239fca993adb2809b Mon Sep 17 00:00:00 2001 From: George Wong Date: Sun, 2 May 2021 22:48:12 -0500 Subject: [PATCH 33/93] add provisional grid interpolation routines with support for koral. development commit. --- model/iharm/model.c | 427 +++++++++++++++++++++++++++++++++++-- model/iharm/model_params.h | 3 +- model/riaf/model.c | 2 + model/sphere/model.c | 3 + src/coordinates.c | 8 +- src/grid.c | 34 ++- src/model_geodesics.c | 2 +- src/simcoords.c | 192 ++++++++++++++--- src/simcoords.h | 5 +- 9 files changed, 612 insertions(+), 64 deletions(-) diff --git a/model/iharm/model.c b/model/iharm/model.c index 4d97f8e..80f897b 100644 --- a/model/iharm/model.c +++ b/model/iharm/model.c @@ -6,10 +6,13 @@ #include "coordinates.h" #include "geometry.h" #include "grid.h" -#include "model_radiation.h" // Only for outputting emissivities +#include "model_radiation.h" // Only for outputting emissivities +#include "simcoords.h" // For interpolating arbitrary grids #include "par.h" #include "utils.h" +#include "debug_tools.h" + #include #define NVAR (10) @@ -21,6 +24,7 @@ #define FORMAT_HAMR_EKS (3) #define FORMAT_IHARM_v1 (1) +#define FORMAT_KORAL_v2 (4) int dumpfile_format = 0; // UNITS @@ -63,11 +67,11 @@ static double Ladv_dump; // MAYBES //static double t0; -// ELECTRONS -> +// ELECTRONS // 0 : constant TP_OVER_TE // 1 : use dump file model (kawazura?) // 2 : use mixed TP_OVER_TE (beta model) -// TODO the way this is selected is horrid. Make it a parameter. +// 9 : load Te (in Kelvin) from dump file (KORAL etc.) static int RADIATION, ELECTRONS; static double gam = 1.444444, game = 1.333333, gamp = 1.666667; static double Thetae_unit, Mdotedd; @@ -95,10 +99,12 @@ static struct of_data *data[NSUP]; void set_units(); void load_data(int n, char *, int dumpidx, int verbose); void load_iharm_data(int n, char *, int dumpidx, int verbose); +void load_koral_data(int n, char *, int dumpidx, int verbose); void load_hamr_data(int n, char *, int dumpidx, int verbose); double get_dump_t(char *fnam, int dumpidx); void init_grid(char *fnam, int dumpidx); void init_iharm_grid(char *fnam, int dumpidx); +void init_koral_grid(char *fnam, int dumpidx); void init_hamr_grid(char *fnam, int dumpidx); void init_physical_quantities(int n); void init_storage(void); @@ -236,13 +242,22 @@ void get_dumpfile_type(char *fnam, int dumpidx) char fname[256]; snprintf(fname, 255, fnam, dumpidx); - // TODO needs KORAL/MKS3 support - hdf5_open(fname); int hamr_attr_exists = hdf5_attr_exists("", "dscale"); + if (!hamr_attr_exists) { - dumpfile_format = FORMAT_IHARM_v1; - fprintf(stderr, "iharm!\n"); + + char harmversion[256]; + hdf5_read_single_val(harmversion, "header/version", hdf5_make_str_type(255)); + + if ( strcmp(harmversion, "KORALv2") == 0 ) { + dumpfile_format = FORMAT_KORAL_v2; + fprintf(stderr, "koral!\n"); + } else { + dumpfile_format = FORMAT_IHARM_v1; + fprintf(stderr, "iharm!\n"); + } + } else { // note this will return -1 if the "header" group does not exists dumpfile_format = FORMAT_HAMR_EKS; @@ -337,7 +352,7 @@ void get_model_fourv(double X[NDIM], double Kcon[NDIM], // Bcon/Bcov to zero. if ( X_in_domain(X) == 0 ) { - Ucov[0] = -1./sqrt(-gcov[0][0]); + Ucov[0] = -1./sqrt(-gcon[0][0]); Ucov[1] = 0.; Ucov[2] = 0.; Ucov[3] = 0.; @@ -529,15 +544,20 @@ void init_physical_quantities(int n) double sigma_m = bsq/data[n]->p[KRHO][i][j][k]; if (ELECTRONS == 1) { - data[n]->thetae[i][j][k] = data[n]->p[KEL][i][j][k]*pow(data[n]->p[KRHO][i][j][k],game-1.)*Thetae_unit; + data[n]->thetae[i][j][k] = data[n]->p[KEL][i][j][k] * + pow(data[n]->p[KRHO][i][j][k],game-1.)*Thetae_unit; } else if (ELECTRONS == 2) { double beta = data[n]->p[UU][i][j][k]*(gam-1.)/0.5/bsq; double betasq = beta*beta / beta_crit/beta_crit; double trat = trat_large * betasq/(1. + betasq) + trat_small /(1. + betasq); //Thetae_unit = (gam - 1.) * (MP / ME) / trat; // see, e.g., Eq. 8 of the EHT GRRT formula list - Thetae_unit = (MP/ME) * (game-1.) * (gamp-1.) / ( (gamp-1.) + (game-1.)*trat ); - data[n]->thetae[i][j][k] = Thetae_unit*data[n]->p[UU][i][j][k]/data[n]->p[KRHO][i][j][k]; + double lcl_Thetae_u = (MP/ME) * (game-1.) * (gamp-1.) / ( (gamp-1.) + (game-1.)*trat ); + Thetae_unit = lcl_Thetae_u; + data[n]->thetae[i][j][k] = lcl_Thetae_u*data[n]->p[UU][i][j][k]/data[n]->p[KRHO][i][j][k]; + } else if (ELECTRONS == 9) { + // convert Kelvin -> Thetae + data[n]->thetae[i][j][k] = data[n]->p[TFLK][i][j][k] * KBOL / ME / CL / CL; } else { data[n]->thetae[i][j][k] = Thetae_unit*data[n]->p[UU][i][j][k]/data[n]->p[KRHO][i][j][k]; } @@ -572,6 +592,8 @@ void init_grid(char *fnam, int dumpidx) { if (dumpfile_format == FORMAT_IHARM_v1) { init_iharm_grid(fnam, dumpidx); + } else if (dumpfile_format == FORMAT_KORAL_v2) { + init_koral_grid(fnam, dumpidx); } else if (dumpfile_format == FORMAT_HAMR_EKS) { init_hamr_grid(fnam, dumpidx); } @@ -657,13 +679,6 @@ void init_hamr_grid(char *fnam, int dumpidx) stopx[2] = startx[2]+N2*dx[2]; stopx[3] = startx[3]+N3*dx[3]; - // set boundary of coordinate system - MULOOP cstartx[mu] = 0.; - cstopx[0] = 0; - cstopx[1] = log(Rout); - cstopx[2] = 1; - cstopx[3] = 2*M_PI; - // now translate from hamr x2 \in (-1, 1) -> mks x2 \in (0, 1) startx[2] = (startx[2] + 1)/2.; stopx[2] = (stopx[2] + 1)/2.; @@ -673,10 +688,8 @@ void init_hamr_grid(char *fnam, int dumpidx) rmax_geo = fmin(rmax_geo, Rout); rmin_geo = fmax(rmin_geo, Rin); - cstartx[0] = 0; - cstartx[1] = 0; - cstartx[2] = 0; - cstartx[3] = 0; + // set boundary of coordinate system + MULOOP cstartx[mu] = 0.; cstopx[0] = 0; cstopx[1] = log(Rout); cstopx[2] = 1.0; @@ -892,6 +905,189 @@ void init_iharm_grid(char *fnam, int dumpidx) hdf5_close(); } +void init_koral_grid(char *fnam, int dumpidx) +{ + // called at the beginning of the run and sets the static parameters + // along with setting up the grid + + assert(42==0); + // this version of the code has not been validated to have the + // right units and four-vector recovery. use at your own peril + + char fname[256]; + snprintf(fname, 255, fnam, dumpidx); + fprintf(stderr, "filename: %s\n", fname); + + // always load ks rh from the file to get proper extents + // and potentially as a fallback for when we cannnot get + // the inverse/reverse transformation eKS -> simcoords. + load_simcoord_info_from_file(fname); + + // beecause of legacy global state choices, we can't open two + // files at once, so we have to call this after the above. + hdf5_open(fname); + + // get dump info to copy to ipole output + fluid_header = hdf5_get_blob("/header"); + + // get time information for slow light + // currently unsupported + //hdf5_read_single_val(&DTd, "dump_cadence", H5T_IEEE_F64LE); + + // in general, we assume KORAL should use the simcoords feature. first + // ensure that metric_out is KS and then set simcoords + hdf5_set_directory("/header/"); + char metric_out[20], metric_run[20]; + hid_t HDF5_STR_TYPE = hdf5_make_str_type(20); + hdf5_read_single_val(&metric_out, "metric_out", HDF5_STR_TYPE); + hdf5_read_single_val(&metric_run, "metric_run", HDF5_STR_TYPE); + + if (strcmp(metric_out, "KS") != 0) { + fprintf(stderr, "! expected koral metric_out==KS but got %s instead. quitting.\n", metric_out); + exit(5); + } + + // at this point, assume we will load data as eKS and trace as eKS. + use_simcoords = 1; + metric = METRIC_MKS; + hslope = 1.; + + // get simulation grid coordinates + if (strcmp(metric_run, "MKS3") == 0) { + simcoords = SIMCOORDS_KORAL_MKS3; + hdf5_read_single_val(&a, "bhspin", H5T_IEEE_F64LE); + hdf5_set_directory("/header/geom/mks3/"); + hdf5_read_single_val(&(mp_koral_mks3.r0), "mksr0", H5T_IEEE_F64LE); + hdf5_read_single_val(&(mp_koral_mks3.h0), "mksh0", H5T_IEEE_F64LE); + hdf5_read_single_val(&(mp_koral_mks3.my1), "mksmy1", H5T_IEEE_F64LE); + hdf5_read_single_val(&(mp_koral_mks3.my2), "mksmy2", H5T_IEEE_F64LE); + hdf5_read_single_val(&(mp_koral_mks3.mp0), "mksmp0", H5T_IEEE_F64LE); + fprintf(stderr, "KORAL simulation was run with MKS3 coordinates.\n"); + } else if (strcmp(metric_run, "JETCOORDS") == 0) { + simcoords = SIMCOORDS_KORAL_JETCOORDS; + hdf5_read_single_val(&a, "bhspin", H5T_IEEE_F64LE); + hdf5_set_directory("/header/geom/jetcoords/"); + hdf5_read_single_val(&(mp_koral_jetcoords.alpha_1), "alpha1", H5T_IEEE_F64LE); + hdf5_read_single_val(&(mp_koral_jetcoords.alpha_2), "alpha2", H5T_IEEE_F64LE); + hdf5_read_single_val(&(mp_koral_jetcoords.cylindrify), "cylindrify", H5T_IEEE_F64LE); + + + /* + double mksr0; + double rbrk; + double rmin; + double rphotomax; + double fjet; + double fdisk; + double runi; + double rcoll_jet; + double rcoll_disk; + double rdecoll_jet; + double rdecoll_disk; + double rcyl; + double ncyl; + */ + + hdf5_set_directory("/header/geom/"); + fprintf(stderr, "KORAL simulation was run with JETCOORDS coordinates.\n"); + } else { + fprintf(stderr, "! unknown koral metric_run (%s). quitting.\n", metric_run); + exit(5); + } + + // get grid information + hdf5_set_directory("/header/"); + hdf5_read_single_val(&N1, "n1", H5T_STD_I32LE); + hdf5_read_single_val(&N2, "n2", H5T_STD_I32LE); + hdf5_read_single_val(&N3, "n3", H5T_STD_I32LE); + hdf5_read_single_val(&gam, "gam", H5T_IEEE_F64LE); + + hdf5_set_directory("/header/geom/"); + hdf5_read_single_val(&startx[1], "startx1", H5T_IEEE_F64LE); + hdf5_read_single_val(&startx[2], "startx2", H5T_IEEE_F64LE); + hdf5_read_single_val(&startx[3], "startx3", H5T_IEEE_F64LE); + hdf5_read_single_val(&dx[1], "dx1", H5T_IEEE_F64LE); + hdf5_read_single_val(&dx[2], "dx2", H5T_IEEE_F64LE); + hdf5_read_single_val(&dx[3], "dx3", H5T_IEEE_F64LE); + hdf5_set_directory("/header/"); + + // reset x3 grid. this makes it easier for simcoords + Xtoijk to handle the + // translation, but it also means ipole and KORAL disagree about where x3=0 + if ( fabs(2.*M_PI - dx[3]*N3) >= dx[3] ) { + fprintf(stderr, "! base koral domain extent in x3 is not 2 PI. quitting.\n"); + exit(5); + } else { + startx[3] = 0.; + } + + stopx[0] = 1.; + stopx[1] = startx[1]+N1*dx[1]; + stopx[2] = startx[2]+N2*dx[2]; + stopx[3] = startx[3]+N3*dx[3]; + + fprintf(stderr, "Native coordinate start: %g %g %g stop: %g %g %g\n", + cstartx[1], cstartx[2], cstartx[3], cstopx[1], cstopx[2], cstopx[3]); + fprintf(stderr, "Grid start: %g %g %g stop: %g %g %g\n", + startx[1], startx[2], startx[3], stopx[1], stopx[2], stopx[3]); + + // unsupported features / undocumented elements + hdf5_set_directory("/header/geom/mks3/"); + if (hdf5_exists("gam_e")) { + fprintf(stderr, "! found gam_e in KORAL simulation. undocumented. quitting.\n"); + exit(5); + } + + // maybe load radiation units from dump file + RADIATION = 0; + hdf5_set_directory("/header/"); + if ( hdf5_exists("has_radiation") ) { + hdf5_read_single_val(&RADIATION, "has_radiation", H5T_STD_I32LE); + if (RADIATION) { + // Note set_units(...) get called AFTER this function returns + fprintf(stderr, "koral dump file was from radiation run. loading units...\n"); + hdf5_set_directory("/header/units/"); + hdf5_read_single_val(&MBH_solar, "M_bh", H5T_IEEE_F64LE); + hdf5_read_single_val(&RHO_unit, "M_unit", H5T_IEEE_F64LE); + hdf5_read_single_val(&T_unit, "T_unit", H5T_IEEE_F64LE); + hdf5_read_single_val(&L_unit, "L_unit", H5T_IEEE_F64LE); + hdf5_read_single_val(&U_unit, "U_unit", H5T_IEEE_F64LE); + hdf5_read_single_val(&B_unit, "B_unit", H5T_IEEE_F64LE); + M_unit = RHO_unit * L_unit*L_unit*L_unit; + } + } + + ELECTRONS = 0; + hdf5_set_directory("/header/"); + if ( hdf5_exists("has_electrons") ) { + hdf5_read_single_val(&ELECTRONS, "has_electrons", H5T_STD_I32LE); + if (ELECTRONS) { + fprintf(stderr, "koral dump has native electron temperature. forcing Thetae...\n"); + ELECTRONS = 9; + } else { + if (USE_MIXED_TPTE && !USE_FIXED_TPTE) { + fprintf(stderr, "Using mixed tp_over_te with trat_small = %g, trat_large = %g, and beta_crit = %g\n", trat_small, trat_large, beta_crit); + // Thetae set in init_physical_quantities(...) + } else { + fprintf(stderr, "! koral unsupported without native electrons or mixed tp_over_te.\n"); + exit(6); + } + } + } + + fprintf(stderr, "sigma_cut = %g\n", sigma_cut); + + fprintf(stderr, "generating simcoords grid... "); + int interp_n1 = 1024; + int interp_n2 = 1024; + initialize_simgrid(interp_n1, interp_n2, + startx[1], startx[1]+N1*dx[1], + startx[2], startx[2]+N2*dx[2]); + fprintf(stderr, "done!\n"); + + init_storage(); + hdf5_close(); +} + void output_hdf5() { hdf5_set_directory("/"); @@ -940,6 +1136,8 @@ void load_data(int n, char *fnam, int dumpidx, int verbose) { if (dumpfile_format == FORMAT_IHARM_v1) { load_iharm_data(n, fnam, dumpidx, verbose); + } else if (dumpfile_format == FORMAT_KORAL_v2) { + load_koral_data(n, fnam, dumpidx, verbose); } else if (dumpfile_format == FORMAT_HAMR_EKS) { load_hamr_data(n, fnam, dumpidx, verbose); } @@ -1246,6 +1444,190 @@ void load_hamr_data(int n, char *fnam, int dumpidx, int verbose) init_physical_quantities(n); } +void load_koral_data(int n, char *fnam, int dumpidx, int verbose) +{ + // loads relevant information from fluid dump file stored at fname + // to the n'th copy of data (e.g., for slow light) + + double dMact, Ladv; + + char fname[256]; + snprintf(fname, 255, fnam, dumpidx); + + nloaded++; + + if ( hdf5_open(fname) < 0 ) { + fprintf(stderr, "! unable to open file %s. Exiting!\n", fname); + exit(-1); + } + + hdf5_set_directory("/"); + hdf5_read_single_val(&(data[n]->t), "t", H5T_IEEE_F64LE); + + hdf5_set_directory("/quants/"); + + // load into "center" of data + hsize_t fdims[] = { N1, N2, N3 }; + hsize_t fstart[] = { 0, 0, 0 }; + hsize_t fcount[] = { N1, N2, N3, 1 }; + hsize_t mdims[] = { N1+2, N2+2, N3+2 }; + hsize_t mstart[] = { 1, 1, 1 }; + + hdf5_read_array(data[n]->p[KRHO][0][0], "rho", 3, fdims, fstart, fcount, + mdims, mstart, H5T_IEEE_F64LE); + + hdf5_read_array(data[n]->p[UU][0][0], "uint", 3, fdims, fstart, fcount, + mdims, mstart, H5T_IEEE_F64LE); + + hdf5_read_array(data[n]->p[U1][0][0], "U1", 3, fdims, fstart, fcount, + mdims, mstart, H5T_IEEE_F64LE); + + hdf5_read_array(data[n]->p[U2][0][0], "U2", 3, fdims, fstart, fcount, + mdims, mstart, H5T_IEEE_F64LE); + + hdf5_read_array(data[n]->p[U3][0][0], "U3", 3, fdims, fstart, fcount, + mdims, mstart, H5T_IEEE_F64LE); + + hdf5_read_array(data[n]->p[B1][0][0], "B1", 3, fdims, fstart, fcount, + mdims, mstart, H5T_IEEE_F64LE); + + hdf5_read_array(data[n]->p[B2][0][0], "B2", 3, fdims, fstart, fcount, + mdims, mstart, H5T_IEEE_F64LE); + + hdf5_read_array(data[n]->p[B3][0][0], "B3", 3, fdims, fstart, fcount, + mdims, mstart, H5T_IEEE_F64LE); + + if (ELECTRONS == 9) { + hdf5_read_array(data[n]->p[TFLK][0][0], "te", 3, fdims, fstart, fcount, + mdims, mstart, H5T_IEEE_F64LE); + } + + hdf5_close(); + + dMact = Ladv = 0.; + + // construct four-vectors over "real" zones +#pragma omp parallel for collapse(2) reduction(+:dMact,Ladv) + for(int i = 1; i < N1+1; i++) { + for(int j = 1; j < N2+1; j++) { + + double X[NDIM] = { 0. }; + double gcov[NDIM][NDIM], gcon[NDIM][NDIM], gcov_KS[NDIM][NDIM], gcon_KS[NDIM][NDIM]; + double g, r, th; + + // this assumes axisymmetry in the coordinates + ijktoX(i-1,j-1,0, X); + gcov_func(X, gcov); + gcon_func(gcov, gcon); + g = gdet_zone(i-1,j-1,0); + + bl_coord(X, &r, &th); + + // the file is output in KS, so get KS metric + gcov_ks(r, th, gcov_KS); + gcon_func(gcov_KS, gcon_KS); + + for(int k = 1; k < N3+1; k++){ + + ijktoX(i-1,j-1,k,X); + double UdotU = 0.; + + for(int l = 1; l < NDIM; l++) + for(int m = 1; m < NDIM; m++) + UdotU += gcov_KS[l][m]*data[n]->p[U1+l-1][i][j][k]*data[n]->p[U1+m-1][i][j][k]; + double ufac = sqrt(-1./gcon_KS[0][0]*(1 + fabs(UdotU))); + + double ucon[NDIM] = { 0. }; + ucon[0] = -ufac * gcon_KS[0][0]; + + for(int l = 1; l < NDIM; l++) + ucon[l] = data[n]->p[U1+l-1][i][j][k] - ufac*gcon_KS[0][l]; + + double ucov[NDIM] = { 0. }; + flip_index(ucon, gcov_KS, ucov); + + // reconstruct the magnetic field three vectors + double udotB = 0.; + + for (int l = 1; l < NDIM; l++) { + udotB += ucov[l]*data[n]->p[B1+l-1][i][j][k]; + } + + double bcon[NDIM] = { 0. }; + double bcov[NDIM] = { 0. }; + + bcon[0] = udotB; + for (int l = 1; l < NDIM; l++) { + bcon[l] = (data[n]->p[B1+l-1][i][j][k] + ucon[l]*udotB)/ucon[0]; + } + flip_index(bcon, gcov_KS, bcov); + + double bsq = 0.; + for (int l=0; lb[i][j][k] = sqrt(bsq) * B_unit; + + if(i <= 21) { dMact += g * data[n]->p[KRHO][i][j][k] * ucon[1]; } + if(i >= 21 && i < 41 && 0) Ladv += g * data[n]->p[UU][i][j][k] * ucon[1] * ucov[0]; + if(i <= 21) Ladv += g * data[n]->p[UU][i][j][k] * ucon[1] * ucov[0]; + + // trust ... + //double udb1 = 0., udu1 = 0., bdb1 = 0.; + //MULOOP { udb1 += ucon[mu]*bcov[mu]; udu1 += ucon[mu]*ucov[mu]; bdb1 += bcon[mu]*bcov[mu]; } + + // now translate from KS (outcoords) -> MKS:hslope=1 + ucon[1] /= r; + ucon[2] /= M_PI; + bcon[1] /= r; + bcon[2] /= M_PI; + + // resynthesize the primitives. note we have assumed that B^i = *F^{ti} + double alpha = sqrt(-1. / gcon[0][0]); + data[n]->p[U1][i][j][k] = (gcon[0][1]*alpha*alpha + ucon[1]/ucon[0]) * ucon[0]; + data[n]->p[U2][i][j][k] = (gcon[0][2]*alpha*alpha + ucon[2]/ucon[0]) * ucon[0]; + data[n]->p[U3][i][j][k] = (gcon[0][3]*alpha*alpha + ucon[3]/ucon[0]) * ucon[0]; + data[n]->p[B1][i][j][k] = ucon[0] * bcon[1] - bcon[0] * ucon[1]; + data[n]->p[B2][i][j][k] = ucon[0] * bcon[2] - bcon[0] * ucon[2]; + data[n]->p[B3][i][j][k] = ucon[0] * bcon[3] - bcon[0] * ucon[3]; + + // ... but verify + //flip_index(bcon, gcov, bcov); + //flip_index(ucon, gcov, ucov); + //double udb2 = 0., udu2 = 0., bdb2 = 0.; + //MULOOP { udb2 += ucon[mu]*bcov[mu]; udu2 += ucon[mu]*ucov[mu]; bdb2 += bcon[mu]*bcov[mu]; } + //fprintf(stderr, "u.u %g %g u.b %g %g b.b %g %g\n", udu1,udu2, udb1,udb2, bdb1,bdb2); + } + } + } + + // now copy primitives and four-vectors according to boundary conditions + populate_boundary_conditions(n); + + dMact *= dx[3]*dx[2] ; + dMact /= 21. ; + Ladv *= dx[3]*dx[2] ; + Ladv /= 21. ; + + Mdot_dump = -dMact*M_unit/T_unit; + MdotEdd_dump = Mdotedd; + Ladv_dump = Ladv; + + if (verbose == 2) { + fprintf(stderr,"dMact: %g [code]\n",dMact); + fprintf(stderr,"Ladv: %g [code]\n",Ladv_dump); + fprintf(stderr,"Mdot: %g [g/s] \n",Mdot_dump); + fprintf(stderr,"Mdot: %g [MSUN/YR] \n",Mdot_dump/(MSUN / YEAR)); + fprintf(stderr,"Mdot: %g [Mdotedd]\n",Mdot_dump/MdotEdd_dump); + fprintf(stderr,"Mdotedd: %g [g/s]\n",MdotEdd_dump); + fprintf(stderr,"Mdotedd: %g [MSUN/YR]\n",MdotEdd_dump/(MSUN/YEAR)); + } else if (verbose == 1) { + fprintf(stderr,"Mdot: %g [MSUN/YR] \n",Mdot_dump/(MSUN / YEAR)); + fprintf(stderr,"Mdot: %g [Mdotedd]\n",Mdot_dump/MdotEdd_dump); + } + + // now construct useful scalar quantities (over full (+ghost) zones of data) + init_physical_quantities(n); +} + void load_iharm_data(int n, char *fnam, int dumpidx, int verbose) { // loads relevant information from fluid dump file stored at fname @@ -1401,6 +1783,7 @@ void load_iharm_data(int n, char *fnam, int dumpidx, int verbose) init_physical_quantities(n); } + int radiating_region(double X[NDIM]) { double r, th; diff --git a/model/iharm/model_params.h b/model/iharm/model_params.h index 23b45f2..0e5c2ef 100644 --- a/model/iharm/model_params.h +++ b/model/iharm/model_params.h @@ -14,9 +14,10 @@ #define B3 7 #define KEL 8 #define KTOT 9 +#define TFLK 8 // temperature of fluid in Kelvin extern double DTd; extern double sigma_cut; -extern double rmax_geo; +extern double rmin_geo, rmax_geo; #endif // MODEL_PARAMS_H diff --git a/model/riaf/model.c b/model/riaf/model.c index 9b2dc9c..99a3789 100644 --- a/model/riaf/model.c +++ b/model/riaf/model.c @@ -133,6 +133,8 @@ void set_units() stopx[1] = log(Rout); stopx[2] = 1.0; stopx[3] = 2*M_PI; + MULOOP cstartx[mu] = startx[mu]; + MULOOP cstopx[mu] = stopx[mu]; } void output_hdf5() diff --git a/model/sphere/model.c b/model/sphere/model.c index a23ac5d..0d203a0 100644 --- a/model/sphere/model.c +++ b/model/sphere/model.c @@ -119,6 +119,9 @@ void init_model(double *tA, double *tB) stopx[2] = startx[2]+N2*dx[2]; stopx[3] = startx[3]+N3*dx[3]; + MULOOP cstartx[mu] = startx[mu]; + MULOOP cstopx[mu] = stopx[mu]; + rmax_geo = fmax(100., MODEL_R_0); fprintf(stderr, "Native coordinate start: %g %g %g stop: %g %g %g\n", diff --git a/src/coordinates.c b/src/coordinates.c index 405789e..0ea7f9a 100644 --- a/src/coordinates.c +++ b/src/coordinates.c @@ -342,11 +342,11 @@ double root_find(double X[NDIM]) Xc[3] = Xa[3]; if (X[2] < M_PI / 2.) { - Xa[2] = startx[2]; - Xb[2] = (stopx[2] - startx[2])/2 + SMALL; + Xa[2] = cstartx[2]; + Xb[2] = (cstopx[2] - cstartx[2])/2 + SMALL; } else { - Xa[2] = (stopx[2] - startx[2])/2 - SMALL; - Xb[2] = stopx[2]; + Xa[2] = (cstopx[2] - cstartx[2])/2 - SMALL; + Xb[2] = cstopx[2]; } double tol = 1.e-9; diff --git a/src/grid.c b/src/grid.c index a64b411..aa5b804 100644 --- a/src/grid.c +++ b/src/grid.c @@ -1,6 +1,7 @@ #include "grid.h" #include "coordinates.h" +#include "simcoords.h" #include "geometry.h" #include @@ -52,6 +53,13 @@ double interp_scalar(double X[NDIM], double ***var) */ void ijktoX(int i, int j, int k, double X[NDIM]) { + // possibly deal with simcoords + if (use_simcoords) { + // this call to simcoordsijk assumes i,j,k valid + simcoordijk_to_eks(i, j, k, X); + return; + } + // first do the naive thing X[1] = startx[1] + (i+0.5)*dx[1]; X[2] = startx[2] + (j+0.5)*dx[2]; @@ -131,6 +139,9 @@ void Xtoijk(double X[NDIM], int *i, int *j, int *k, double del[NDIM]) pow(2.,1. + MP0)*MY2)*M_PI); XG[3] = Xks[3]; } + } else if (use_simcoords) { + // the geodesics are in eKS, so invert through simcoords -> zone coordinates + eks_to_simcoord(X, XG); } else { MULOOP XG[mu] = X[mu]; } @@ -173,6 +184,18 @@ int X_in_domain(double X[NDIM]) { // checks different sets of coordinates depending on // specified grid coordinates + if (use_simcoords) { + double gridcoord[NDIM]; + eks_to_simcoord(X, gridcoord); + if (gridcoord[1] < startx[1] || + gridcoord[1] >= stopx[1] || + gridcoord[2] < startx[2] || + gridcoord[2] >= stopx[2]) { + return 0; + } + return 1; + } + if (use_eKS_internal) { double XG[4] = { 0 }; double r, th; @@ -192,14 +215,14 @@ int X_in_domain(double X[NDIM]) { pow(2,1 + MP0)*MY2)*M_PI); XG[3] = Xks[3]; - if (XG[1] < startx[1] || XG[1] > stopx[1]) return 0; + if (XG[1] < startx[1] || XG[1] >= stopx[1]) return 0; } } else { if(X[1] < startx[1] || - X[1] > stopx[1] || + X[1] >= stopx[1] || X[2] < startx[2] || - X[2] > stopx[2]) { + X[2] >= stopx[2]) { return 0; } } @@ -212,6 +235,11 @@ int X_in_domain(double X[NDIM]) { */ double gdet_zone(int i, int j, int k) { + if (use_simcoords) { + // we assume i, j, k is valid here. otherwise, this function returns zero. + return simcoordijk_to_gdet(i, j, k); + } + // get the X for the zone (in geodesic coordinates for bl_coord) // and in zone coordinates (for set_dxdX) double X[NDIM], Xzone[NDIM]; diff --git a/src/model_geodesics.c b/src/model_geodesics.c index 93711b7..0faf032 100644 --- a/src/model_geodesics.c +++ b/src/model_geodesics.c @@ -252,7 +252,7 @@ int stop_backward_integration(double X[NDIM], double Xhalf[NDIM], double Kcon[ND double stepsize(double X[NDIM], double Kcon[NDIM], double eps) { double dl; - double deh = fmin(fabs(X[1] - startx[1]), 0.1); // TODO coordinate dependent + double deh = fmin(fabs(X[1] - cstartx[1]), 0.1); // TODO coordinate dependent double dlx1 = eps * (10*deh) / (fabs(Kcon[1]) + SMALL*SMALL); // Make the step cautious near the pole, improving accuracy of Stokes U diff --git a/src/simcoords.c b/src/simcoords.c index 2ba2b0c..c525bf1 100644 --- a/src/simcoords.c +++ b/src/simcoords.c @@ -20,6 +20,7 @@ metric_params_koral_jetcoords mp_koral_jetcoords = { 0 }; // invisible "members" static double *simcoords_x1 = NULL; static double *simcoords_x2 = NULL; +static double *simcoords_gdet = NULL; static size_t sc_n1 = 1; static size_t sc_n2 = 1; static double er0 = 0; @@ -32,16 +33,17 @@ static double *ks_r = NULL; static double *ks_h = NULL; static size_t n1 = 1; static size_t n2 = 1; -static double startx1, startx2, dx1, dx2, maxks_r; +static double startx1, startx2, dx1, dx2; +double minks_r, maxks_r; // coordinate function forward declarations -static void rev_KORAL_MKS3(double *xKS, double *xMKS); -static void rev_saved_rh(double *eKS, double *gridcoord); +static int rev_saved_rh(double *eKS, double *gridcoord); +static int rev_KORAL_MKS3(double *xKS, double *xMKS); #define ij2oned(i,j) ((size_t)(j+sc_n2*(i))) // interface functions -void load_ks_rh_from_file(const char *fname) +void load_simcoord_info_from_file(const char *fname) { if ( hdf5_open(fname) < 0 ) { fprintf(stderr, "! unable to open file %s. exiting!\n", fname); @@ -62,6 +64,7 @@ void load_ks_rh_from_file(const char *fname) ks_r = calloc(n1*n2, sizeof(*ks_r)); ks_h = calloc(n1*n2, sizeof(*ks_h)); + simcoords_gdet = calloc(n1*n2, sizeof(*simcoords_gdet)); hdf5_set_directory("/grid_out/"); @@ -74,11 +77,22 @@ void load_ks_rh_from_file(const char *fname) hdf5_read_array(ks_r, "r", 3, fdims, fstart, fcount, mdims, mstart, H5T_IEEE_F64LE); hdf5_read_array(ks_h, "th", 3, fdims, fstart, fcount, mdims, mstart, H5T_IEEE_F64LE); + hdf5_set_directory("/grid_run/"); + + hdf5_read_array(simcoords_gdet, "gdet", 3, fdims, fstart, fcount, mdims, mstart, H5T_IEEE_F64LE); + + minks_r = ks_r[0]; + maxks_r = ks_r[0]; for (int i=0; i maxks_r) { - maxks_r = ks_r[j+n2*i]; + double tr = ks_r[j+n2*i]; + if (tr < minks_r) { + minks_r = tr; } + if (tr > maxks_r) { + maxks_r = tr; + } + } } @@ -87,13 +101,16 @@ void load_ks_rh_from_file(const char *fname) void initialize_simgrid(size_t n1, size_t n2, double x1i, double x1f, double x2i, double x2f) { + if (use_simcoords == 0) return; + + // if we're using simgrid, the tracing should be done in eKS. verify here. + assert(metric == METRIC_MKS && hslope==1.); + if (ks_r == NULL) { fprintf(stderr, "! must call load_ks_rh_from_file(...) before initialize_simgrid(...)\n"); exit(-7); } - if (use_simcoords == 0) return; - sc_n1 = n1; sc_n2 = n2; @@ -105,17 +122,17 @@ void initialize_simgrid(size_t n1, size_t n2, double x1i, double x1f, double x2i // that we cover the full domain. if we have an oob error when trying to determine // the ii,jj for interpolating, we return x1f+1, so x1f+1 must not be a valid grid // coordinate in the fluid model! - double Rin = 1.05 * (1. + sqrt(1. - a*a)); + double Rin = minks_r; // 1.05 * (1. + sqrt(1. - a*a)); double Rout = maxks_r; double h_min = 0.; double h_max = M_PI; - fprintf(stderr, "Rout %g\n\n", Rout); - // set limit for tracking geodesic emission rmax_geo = fmin(rmax_geo, Rout); rmin_geo = fmax(rmin_geo, Rin); + fprintf(stderr, "Rin Rmax %g %g %g %g %g\n", rmax_geo, rmax_geo, Rin, Rout, 1. + sqrt(1.-a*a)); + x1i_oob = x1f + 1.; er0 = log(Rin); der = (log(Rout) - log(Rin)) / sc_n1; @@ -124,7 +141,7 @@ void initialize_simgrid(size_t n1, size_t n2, double x1i, double x1f, double x2i // coordinate system is MKS, so set reasonable values here cstartx[0] = 0; - cstartx[1] = log(Rin); + cstartx[1] = log(minks_r); cstartx[2] = 0; cstartx[3] = 0; cstopx[0] = 0; @@ -142,13 +159,19 @@ void initialize_simgrid(size_t n1, size_t n2, double x1i, double x1f, double x2i eKS[1] = exp(er0 + der*i); eKS[2] = h0 + dh*j; + int rv = 0; if (simcoords == SIMCOORDS_KORAL_MKS3) { - rev_KORAL_MKS3(eKS, gridcoord); + rv = rev_KORAL_MKS3(eKS, gridcoord); } else if (simcoords == SIMCOORDS_KORAL_JETCOORDS) { - rev_saved_rh(eKS, gridcoord); + rv = rev_saved_rh(eKS, gridcoord); } else { assert(1==0); // unknown simulation coordinates } + + // force coordinate out of grid if the reverse solver failed + if (rv != 0) { + gridcoord[1] = x1i_oob + (x1f-x1i)*100.; + } simcoords_x1[ij2oned(i,j)] = gridcoord[1]; simcoords_x2[ij2oned(i,j)] = gridcoord[2]; @@ -157,21 +180,110 @@ void initialize_simgrid(size_t n1, size_t n2, double x1i, double x1f, double x2i } void finalize_simgrid() -{ +{ + // general housekeeping + free(simcoords_gdet); free(simcoords_x2); free(simcoords_x1); - free(ks_h); free(ks_r); } -void eks_to_simcoord(double eKS[NDIM], double gridcoord[NDIM]) +double simcoordijk_to_gdet(int i, int j, int k) +{ + // check bounds + if (i < 0 || j < 0 || i >= n1 || j >= n2) return 0; + + return simcoords_gdet[ n2*i + j ]; +} + +int simcoordijk_to_eks(int i, int j, int k, double eKS[NDIM]) { + // check bounds + if (i < 0 || j < 0 || i >= n1 || j >= n2) return -1; + + // return the eks for the gridzone at i,j,k + eKS[1] = log( ks_r[ n2*i + j ] ); + eKS[2] = ks_h[ n2*i + j ] / M_PI; + + return 0; +} + +#define SIMCOORD_SMALL 1.e-10 +int simcoord_to_eks(double gridcoord[NDIM], double eKS[NDIM]) +{ + // since the ks coordinates are defined at the zone centers but the + // startx1,2 are the left edge, we need to subtract 0.5 in order to + // resolve the locations properly. WARNING: this function will work + // for coordinates that fall WITHIN the "central" part of the zones + // but is strictly bounded by [startx+dx/2, stopx-dx/2] + + double i = (gridcoord[1] - startx1) / dx1 - 0.5; + size_t ii = (size_t) i; + double di = i - ii; + + double j = (gridcoord[2] - startx2) / dx2 - 0.5; + size_t jj = (size_t) j; + double dj = j - jj; + + // before first zone center + if (ii < 0 || jj < 0) { + return -1; + } + + // handle last zone center carefully to avoid segfaults + if (ii == n1-1) { + if (di < SIMCOORD_SMALL) { + ii -= 1; + di = 1.; + } else { + return -1; + } + } + if (jj == n2-1) { + if (dj < SIMCOORD_SMALL) { + jj -= 1; + dj = 1.; + } else { + return -1; + } + } + + // beyond the right-most zone center + if (ii >= n1 || jj >= n2) { + return -1; + } + + eKS[0] = gridcoord[0]; + + eKS[1] = ks_r[ n2*ii + jj ] * (1.-di)*(1.-dj) + + ks_r[ n2*ii + jj+1 ] * (1.-di)*dj + + ks_r[ n2*(ii+1) + jj ] * di*(1.-dj) + + ks_r[ n2*(ii+1) + jj+1 ] * di*dj; + + eKS[2] = ks_h[ n2*ii + jj ] * (1.-di)*(1.-dj) + + ks_h[ n2*ii + jj+1 ] * (1.-di)*dj + + ks_h[ n2*(ii+1) + jj ] * di*(1.-dj) + + ks_h[ n2*(ii+1) + jj+1 ] * di*dj; + + eKS[3] = gridcoord[3]; + + return 0; +} +#undef SIMCOORD_SMALL + +void eks_to_simcoord(double eKS[NDIM], double gridcoord[NDIM]) +{ + // assume that input is in eKS, so r_ks = exp(x1) and theta_ks = Pi * x2 + + // here, (i,j) is distance from left edge (in units of zones). since the + // grid is defined so that simcoord(0.0) == left_edge, we do not need to + // deal with any stray 0.5 (see the for loop in initialize_simgrid(...)) double i = (eKS[1] - er0) / der; size_t ii = (size_t) i; double di = i - ii; - double j = (eKS[2] - h0) / dh; + double j = (M_PI*eKS[2] - h0) / dh; size_t jj = (size_t) j; double dj = j - jj; @@ -199,15 +311,13 @@ void eks_to_simcoord(double eKS[NDIM], double gridcoord[NDIM]) ////// // function definitions for populating the interpolation grid ////// - -static void rev_saved_rh(double *eKS, double *gridcoord) +static int rev_saved_rh(double *eKS, double *gridcoord) { - int gridi = -1; + int gridi = -2; double griddx1 = 0.; double r = exp(eKS[1]); - // here we assume x1 is independent of x2 and that grid zones - // are not incredibly skew + // here we assume x1 is independent of x2 so we can find i first for (int i=0; i= 0.5) testi += 1; + + int gridj = -2; double griddx2 = 0.; double h = eKS[2]; for (int j=0; j Date: Sun, 2 May 2021 22:52:39 -0500 Subject: [PATCH 34/93] expose rmin_geo (required for simcoords --- model/analytic/model.c | 2 +- model/analytic/model_params.h | 2 +- model/riaf/model.c | 2 +- model/riaf/model_params.h | 2 +- model/sphere/model.c | 3 ++- model/sphere/model_params.h | 2 +- 6 files changed, 7 insertions(+), 6 deletions(-) diff --git a/model/analytic/model.c b/model/analytic/model.c index 4cee660..43f742f 100644 --- a/model/analytic/model.c +++ b/model/analytic/model.c @@ -17,9 +17,9 @@ double Te_unit; double RHO_unit; // Model parameters: public +double rmin_geo = 0; double rmax_geo = 1000.0; // Model parameters: private -static double rmin_geo = 0; static double MBH_solar = 4.e6; static double RHO_unit_in = 0; diff --git a/model/analytic/model_params.h b/model/analytic/model_params.h index 2a7169e..a0569f9 100644 --- a/model/analytic/model_params.h +++ b/model/analytic/model_params.h @@ -10,6 +10,6 @@ #define THIN_DISK (0) // Necessary model parameters, can be used or not -extern double rmax_geo; +extern double rmin_geo, rmax_geo; #endif /* MODEL_PARAMS_H */ diff --git a/model/riaf/model.c b/model/riaf/model.c index 99a3789..049bfc6 100644 --- a/model/riaf/model.c +++ b/model/riaf/model.c @@ -19,9 +19,9 @@ double Te_unit = 1e11; double Ne_unit = 5e6; // Model parameters: public +double rmin_geo = 0; double rmax_geo = 1000.0; // Model parameters: private -static double rmin_geo = 0; static double MBH_solar = 4.3e6; static double MBH; diff --git a/model/riaf/model_params.h b/model/riaf/model_params.h index 2a7169e..a0569f9 100644 --- a/model/riaf/model_params.h +++ b/model/riaf/model_params.h @@ -10,6 +10,6 @@ #define THIN_DISK (0) // Necessary model parameters, can be used or not -extern double rmax_geo; +extern double rmin_geo, rmax_geo; #endif /* MODEL_PARAMS_H */ diff --git a/model/sphere/model.c b/model/sphere/model.c index 0d203a0..b9c8d65 100644 --- a/model/sphere/model.c +++ b/model/sphere/model.c @@ -14,7 +14,7 @@ #include // used by other files -double rmax_geo; +double rmin_geo, rmax_geo; double L_unit; // model parameters @@ -122,6 +122,7 @@ void init_model(double *tA, double *tB) MULOOP cstartx[mu] = startx[mu]; MULOOP cstopx[mu] = stopx[mu]; + rmin_geo = 0; rmax_geo = fmax(100., MODEL_R_0); fprintf(stderr, "Native coordinate start: %g %g %g stop: %g %g %g\n", diff --git a/model/sphere/model_params.h b/model/sphere/model_params.h index 5779873..28f5ecf 100644 --- a/model/sphere/model_params.h +++ b/model/sphere/model_params.h @@ -3,6 +3,6 @@ #define SLOW_LIGHT (0) -extern double rmax_geo; +extern double rmin_geo, rmax_geo; #endif // MODEL_PARAMS_H From 4d9d8563b50f36d862a2fcc1d293e5a3b7bab620 Mon Sep 17 00:00:00 2001 From: George Wong Date: Sun, 2 May 2021 22:54:18 -0500 Subject: [PATCH 35/93] add proper assert include --- model/iharm/model.c | 1 + 1 file changed, 1 insertion(+) diff --git a/model/iharm/model.c b/model/iharm/model.c index 80f897b..a10f256 100644 --- a/model/iharm/model.c +++ b/model/iharm/model.c @@ -13,6 +13,7 @@ #include "debug_tools.h" +#include #include #define NVAR (10) From 50e267f47a19f7ebf6e06eecc7643076c57b5b74 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Mon, 24 May 2021 17:07:11 -0500 Subject: [PATCH 36/93] Enforce particular maximum polarization fractions in the emissivities/absorptivities, rather than previous ad-hoc fix --- src/model_radiation.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/model_radiation.c b/src/model_radiation.c index 0ff48e9..1fa8e28 100644 --- a/src/model_radiation.c +++ b/src/model_radiation.c @@ -74,6 +74,8 @@ static double powerlaw_gamma_cut = 1e10; static double powerlaw_gamma_min = 1e2; static double powerlaw_gamma_max = 1e5; static double powerlaw_p = 3.25; +static double max_pol_frac_e = 0.99; +static double max_pol_frac_a = 0.99; void try_set_radiation_parameter(const char *word, const char *value) { @@ -83,6 +85,9 @@ void try_set_radiation_parameter(const char *word, const char *value) set_by_word_val(word, value, "powerlaw_gamma_min", &powerlaw_gamma_min, TYPE_DBL); set_by_word_val(word, value, "powerlaw_gamma_max", &powerlaw_gamma_max, TYPE_DBL); set_by_word_val(word, value, "powerlaw_p", &powerlaw_p, TYPE_DBL); + + set_by_word_val(word, value, "max_pol_frac_e", &powerlaw_p, TYPE_DBL); + set_by_word_val(word, value, "max_pol_frac_a", &powerlaw_p, TYPE_DBL); } /** @@ -245,17 +250,15 @@ void jar_calc_dist(int dist, double X[NDIM], double Kcon[NDIM], fprintf(stderr, "Negative total emissivity! Exiting!\n"); exit(-1); } - double jP = sqrt(*jQ * *jQ + *jU * *jU + *jV * *jV); - if (*jI < jP) { + if (*jI * *jI < *jQ * *jQ + *jU * *jU + *jV * *jV) { // Transport does not like 100% polarization... - double pol_frac_e = *jI / jP - 0.01; - //double pol_frac_e = 0.9; + double pol_frac_e = *jI / jP * max_pol_frac_e; *jQ *= pol_frac_e; *jU *= pol_frac_e; *jV *= pol_frac_e; #if DEBUG - //fprintf(stderr, "Polarized emissivities too large:\n %g vs %g, corrected by %g\n", - //jP, *jI, pol_frac_e); + fprintf(stderr, "Polarized emissivities too large:\n %g vs %g, corrected by %g\n", + sqrt(*jQ * *jQ + *jU * *jU + *jV * *jV), *jI, pol_frac_e); #endif } @@ -286,17 +289,15 @@ void jar_calc_dist(int dist, double X[NDIM], double Kcon[NDIM], fprintf(stderr, "Negative total absorptivity! Exiting!\n"); exit(-1); } - double aP = sqrt(*aQ * *aQ + *aU * *aU + *aV * *aV); - if (*aI < aP) { + if (*aI * *aI < *aQ * *aQ + *aU * *aU + *aV * *aV) { // Transport does not like 100% polarization... - double pol_frac_a = *aI / aP - 0.01; - //double pol_frac_a = 0.9; + double pol_frac_a = *aI / aP * max_pol_frac_a; *aQ *= pol_frac_a; *aU *= pol_frac_a; *aV *= pol_frac_a; #if DEBUG fprintf(stderr, "Polarized absorptivities too large:\n %g vs %g, corrected by %g\n", - aP, *aI, pol_frac_a); + sqrt(*aQ * *aQ + *aU * *aU + *aV * *aV), *aI, pol_frac_a); #endif } From 22b9ca4144bd5f49df7ef53a6625c70911009bd3 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Mon, 24 May 2021 19:03:26 -0500 Subject: [PATCH 37/93] Overhaul analytic solution choices to be more systematic Various approximations to the full analytic LDI2 solutions can be made, to avoid numerical disasters in the full solution. This commit adds a few. Use of solutions is now split on optical depth of the step, rather than raw aI/aP/rho values, and several new cases have been added: * The rotation steps gain a linearized solution usable for small but nonzero tauF in the step * The emission-absorption step is split into five cases: 1. tauI overwhelmingly large, kill emission 2. tauI large enough to overflow exp(tauI), avoid 3. Normal 4. Small tauP but not tauI 5. Small tauP and tauI --- src/ipolarray.c | 104 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 70 insertions(+), 34 deletions(-) diff --git a/src/ipolarray.c b/src/ipolarray.c index 627138b..3e899b6 100644 --- a/src/ipolarray.c +++ b/src/ipolarray.c @@ -21,8 +21,11 @@ // These are related mostly to where exp() and 1/x overflow // desired accuracy. +#define CUT_INSANE_ABS 2000 #define CUT_HIGH_ABS 500 #define CUT_LOW_ABS SMALL +// Low-rotativity linear limit +#define CUT_LOW_ROT SMALL // Sub-functions void push_polar(double Xi[NDIM], double Xm[NDIM], double Xf[NDIM], @@ -275,11 +278,9 @@ int evolve_N(double Xi[NDIM], double Kconi[NDIM], double Ucov[NDIM],Bcov[NDIM]; double Ecov[NDIM][NDIM], Econ[NDIM][NDIM]; double complex N_tetrad[NDIM][NDIM]; - double B; double jI, jQ, jU, jV; double aI, aQ, aU, aV; double rV, rU, rQ; - double rho2, rho, rdS; double SI = 0, SQ = 0, SU = 0, SV = 0; double SI0, SQ0, SU0, SV0; double SI1, SQ1, SU1, SV1; @@ -302,9 +303,9 @@ int evolve_N(double Xi[NDIM], double Kconi[NDIM], // Guess B if we *absolutely must* // Note get_model_b (rightly) returns 0 outside the domain, // but we can cling to the 4-vectors a bit longer - B = 0.; - MULOOP B += Bcon[mu] * Bcov[mu]; - if (B <= 0.) { + double bsq = 0.; + MULOOP bsq += Bcon[mu] * Bcov[mu]; + if (bsq <= 0.) { Bcon[0] = 0.; Bcon[1] = 1.; Bcon[2] = 1.; @@ -326,23 +327,22 @@ int evolve_N(double Xi[NDIM], double Kconi[NDIM], /* apply the Faraday rotation solution for a half step */ double x = dlam * 0.5; - rdS = rQ * SQ0 + rU * SU0 + rV * SV0; - rho2 = rQ * rQ + rU * rU + rV * rV; - rho = sqrt(rho2); - double c, s, sh; - c = cos(rho * x); - s = sin(rho * x); - sh = sin(0.5 * rho * x); - if (rho2 > 0) { + double rho2 = rQ * rQ + rU * rU + rV * rV; + double rho = sqrt(rho2); + if (rho * x > CUT_LOW_ROT) { + double rdS = rQ * SQ0 + rU * SU0 + rV * SV0; + double c = cos(rho * x); + double s = sin(rho * x); + double sh = sin(0.5 * rho * x); SI1 = SI0; SQ1 = SQ0 * c + 2 * rQ * rdS / rho2 * sh * sh + (rU * SV0 - rV * SU0) / rho * s; SU1 = SU0 * c + 2 * rU * rdS / rho2 * sh * sh + (rV * SQ0 - rQ * SV0) / rho * s; SV1 = SV0 * c + 2 * rV * rdS / rho2 * sh * sh + (rQ * SU0 - rU * SQ0) / rho * s; } else { SI1 = SI0; - SQ1 = SQ0; - SU1 = SU0; - SV1 = SV0; + SQ1 = SQ0 + (-rV * SU0 + rU * SV0) * x; + SU1 = SU0 + ( rV * SQ0 - rQ * SV0) * x; + SV1 = SV0 + (-rU * SQ0 + rQ * SU0) * x; } /* done rotation solution half step */ @@ -354,9 +354,24 @@ int evolve_N(double Xi[NDIM], double Kconi[NDIM], double ads0 = aQ * SQ1 + aU * SU1 + aV * SV1; double adj = aQ * jQ + aU * jU + aV * jV; - if (aP*x > CUT_HIGH_ABS || aI*x > CUT_HIGH_ABS) { - // Solution assuming aI ~ aP >> 1, the worst case. - // This covers the case aI >> aP by sending exp(aP-aI)->0, which is safe for all terms + // Solutions from large to small absorptivity. + // Note we assume that coming from jar_calc, aI > aP + // Case 1: So much absorption everything dies + // Case 2: So much absorption that doubles become inaccurate + // Case 3: normal + // Case 4: So little polarized absorption as to be negligible + // Case 5: So little absorption as to be negligible + if (aI*x > CUT_INSANE_ABS) { + // Case 1: Cement wall + SI2 = 0; + SQ2 = 0; + SU2 = 0; + SV2 = 0; + + } else if (aI*x > CUT_HIGH_ABS) { + // Case 2: High enough aI (and possibly aP) to be a numerical problem + // Solution assumes aI ~ aP >> 1, the worst case. + // Small aP sends expDiffx = exp(aP-aI)->0, which is safe for all terms double expDiffx = exp((aP-aI) * x)/2; SI2 = (SI1 * expDiffx @@ -385,7 +400,9 @@ int evolve_N(double Xi[NDIM], double Kconi[NDIM], aI / aP2 * (aI * expDiffx + aP * expDiffx)) + jI * aV / (aP * (aI2 - aP2)) * (-aP + (aP * expDiffx + aI * expDiffx))); - } else if (aP*x > CUT_LOW_ABS) { /* full analytic solution has trouble if polarized absorptivity is small */ + + } else if (aI*x > CUT_LOW_ABS && aP*x > CUT_LOW_ABS) { + // Case 3: Normal solution, some absorption but not difficult amounts double expaIx = exp(-aI * x); double sinhaPx = sinh(aP * x); double coshaPx = cosh(aP * x); @@ -425,35 +442,54 @@ int evolve_N(double Xi[NDIM], double Kconi[NDIM], + jI * aV / (aP * (aI2 - aP2)) * (-aP + (aP * coshaPx + aI * sinhaPx) * expaIx)); + } else if (aI*x > CUT_LOW_ABS) { + // Case 4: Account for aI but negligible aP + double expaIx = exp(-aI * x); + SI2 = SI1*expaIx + + jI/aI - jI/aI*expaIx + - (aQ*jQ)/aI2 + (aQ*jQ)/aI2*expaIx + - (aU*jU)/aI2 + (aU*jU)/aI2*expaIx + - (aV*jV)/aI2 + (aV*jV)/aI2*expaIx + + (aQ*jQ*x)/aI*expaIx + (aU*jU*x)/aI*expaIx + (aV*jV*x)/aI*expaIx + - (aQ*SQ1*x)*expaIx - (aU*SU1*x)*expaIx - (aV*SV1*x)*expaIx; + + SQ2 = -((aQ*jI)/aI2) + (aQ*jI)/aI2*expaIx + jQ/aI - jQ/aI*expaIx + + SQ1*expaIx - (aQ*SI1*x)*expaIx + (aQ*jI*x)/aI*expaIx; + + SU2 = -((aU*jI)/aI2) + (aU*jI)/aI2*expaIx + jU/aI - jU/aI*expaIx + + SU1*expaIx - (aU*SI1*x)*expaIx + (aU*jI*x)/aI*expaIx; + + SV2 = -((aV * jI) / aI2) + (aV*jI)/aI2*expaIx + jV/aI - jV/aI*expaIx + + SV1*expaIx - (aV*SI1*x)*expaIx + (aV*jI*x)/aI*expaIx; + } else { - // Still account for aI which may be >> aP, e.g. simulating unpolarized transport - // Should still make this an expansion in aP as well + // Case 5: Both absorptivities are negligible double tau_fake = 0; - SI2 = approximate_solve(SI1, jI, aI, jI, aI, x, &tau_fake); - SQ2 = approximate_solve(SQ1, jQ, aI, jQ, aI, x, &tau_fake); - SU2 = approximate_solve(SU1, jU, aI, jU, aI, x, &tau_fake); - SV2 = approximate_solve(SV1, jV, aI, jV, aI, x, &tau_fake); + SI2 = SI1 + x * jI; + SQ2 = SQ1 + x * jQ; + SU2 = SU1 + x * jU; + SV2 = SV1 + x * jV; } /* done absorption/emission full step */ /* apply second rotation half-step */ x = dlam * 0.5; - rdS = rQ * SQ2 + rU * SU2 + rV * SV2; rho2 = rQ * rQ + rU * rU + rV * rV; rho = sqrt(rho2); - c = cos(rho * x); - s = sin(rho * x); - sh = sin(0.5 * rho * x); - if (rho2 > 0) { + if (rho*x > CUT_LOW_ROT) { + double rdS = rQ * SQ2 + rU * SU2 + rV * SV2; + double c = cos(rho * x); + double s = sin(rho * x); + double sh = sin(0.5 * rho * x); SI = SI2; SQ = SQ2 * c + 2 * rQ * rdS / rho2 * sh * sh + (rU * SV2 - rV * SU2) / rho * s; SU = SU2 * c + 2 * rU * rdS / rho2 * sh * sh + (rV * SQ2 - rQ * SV2) / rho * s; SV = SV2 * c + 2 * rV * rdS / rho2 * sh * sh + (rQ * SU2 - rU * SQ2) / rho * s; } else { SI = SI2; - SQ = SQ2; - SU = SU2; - SV = SV2; + SQ = SQ2 + (-rV * SU2 + rU * SV2) * x; + SU = SU2 + ( rV * SQ2 - rQ * SV2) * x; + SV = SV2 + (-rU * SQ2 + rQ * SU2) * x; } /* done second rotation half-step */ From c632b865ce801b8a2dff0d11511dab2c4f61d41b Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Mon, 24 May 2021 23:53:09 -0500 Subject: [PATCH 38/93] Explain reorthogonalization, rather than hand-waving. Use limiting solutions more liberally. --- src/ipolarray.c | 15 +++++++------- src/model_tetrads.c | 50 ++++++++++++++++++++++++++++++--------------- 2 files changed, 41 insertions(+), 24 deletions(-) diff --git a/src/ipolarray.c b/src/ipolarray.c index 3e899b6..44b80ed 100644 --- a/src/ipolarray.c +++ b/src/ipolarray.c @@ -19,13 +19,13 @@ #include "debug_tools.h" #include -// These are related mostly to where exp() and 1/x overflow -// desired accuracy. -#define CUT_INSANE_ABS 2000 -#define CUT_HIGH_ABS 500 -#define CUT_LOW_ABS SMALL -// Low-rotativity linear limit -#define CUT_LOW_ROT SMALL +// Cuts to use limiting solutions based +// on optical depth (tau) +#define CUT_INSANE_ABS 500 +#define CUT_HIGH_ABS 50 +#define CUT_LOW_ABS 1e-10 +// Low-rotativity linear limit (tauF) +#define CUT_LOW_ROT 1e-10 // Sub-functions void push_polar(double Xi[NDIM], double Xm[NDIM], double Xf[NDIM], @@ -464,7 +464,6 @@ int evolve_N(double Xi[NDIM], double Kconi[NDIM], } else { // Case 5: Both absorptivities are negligible - double tau_fake = 0; SI2 = SI1 + x * jI; SQ2 = SQ1 + x * jQ; SU2 = SU1 + x * jU; diff --git a/src/model_tetrads.c b/src/model_tetrads.c index 9045df1..3e8bc7d 100644 --- a/src/model_tetrads.c +++ b/src/model_tetrads.c @@ -36,32 +36,50 @@ int make_plasma_tetrad(double Ucon[NDIM], double Kcon[NDIM], double Bcon[NDIM], double Gcov[NDIM][NDIM], double Econ[NDIM][NDIM], double Ecov[NDIM][NDIM]) { - // Modified Gram-Schmidt to produce e^n orthogonal + // Modified Gram-Schmidt method to produce e^n orthogonal + // e^1 is wholly determined by orthonormality + double ones[NDIM] = {1., 1., 1., 1.}; set_Econ_from_trial(Econ[0], 0, Ucon); - normalize(Econ[0], Gcov); + set_Econ_from_trial(Econ[1], 3, ones); + set_Econ_from_trial(Econ[2], 2, Bcon); set_Econ_from_trial(Econ[3], 3, Kcon); + + // (Re)orthogonalize + // Repeating the orthogonalizations below wouldn't be necessary in + // exact math, of course, but what if we have numbers on the order + // of machine accuracy (epsilon)? Consider a matrix A: + // A = [2 1] + // [d 0] + // [0 d] + // where d is O(sqrt(epsilon)), so that generally N + d^2 = N when + // represented in double precision. Say we want an orthogonal form of A, + // the matrix Q. + // Orthonormalized with Gram-Schmidt and throwing away d^2: + // Q2= [1 0 ] + // [d/2 -1/sqrt(5)] + // [0 2/sqrt(5)] + // But if we orthogonalize Q2 again, we get: + // Q = [1 d/(2 sqrt(5))] + // [d/2 -1/sqrt(5) ] + // [0 2/sqrt(5) ] + // While there are more complex & faster reorthogonalization methods, simply + // repeating the orthogonalization exactly once for each vector reaches near + // optimal accuracy, see Giraud and Langou 2005 for the analysis and some background + + // Note we choose the order carefully to preserve ucon == e^0 perfectly, + // and u^3 ~= Kcon as closely as possible. + normalize(Econ[0], Gcov); + project_out(Econ[3], Econ[0], Gcov); project_out(Econ[3], Econ[0], Gcov); normalize(Econ[3], Gcov); - set_Econ_from_trial(Econ[2], 2, Bcon); + project_out(Econ[2], Econ[0], Gcov); + project_out(Econ[2], Econ[3], Gcov); project_out(Econ[2], Econ[0], Gcov); project_out(Econ[2], Econ[3], Gcov); normalize(Econ[2], Gcov); - // whatever is left is econ1 - for (int k = 0; k < 4; k++) /* trial vector */ - Econ[1][k] = 1.; project_out(Econ[1], Econ[0], Gcov); project_out(Econ[1], Econ[2], Gcov); project_out(Econ[1], Econ[3], Gcov); - normalize(Econ[1], Gcov); - - // Reorthogonalize. I swear this is a real thing - // TODO may not require renormalization - normalize(Econ[0], Gcov); - project_out(Econ[3], Econ[0], Gcov); - normalize(Econ[3], Gcov); - project_out(Econ[2], Econ[0], Gcov); - project_out(Econ[2], Econ[3], Gcov); - normalize(Econ[2], Gcov); project_out(Econ[1], Econ[0], Gcov); project_out(Econ[1], Econ[2], Gcov); project_out(Econ[1], Econ[3], Gcov); From 54cb4e26c570d5a6745c6e8f757833778405f47c Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 25 May 2021 15:59:19 -0500 Subject: [PATCH 39/93] Correct a citation(s) --- src/model_tetrads.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/model_tetrads.c b/src/model_tetrads.c index 3e8bc7d..a49010a 100644 --- a/src/model_tetrads.c +++ b/src/model_tetrads.c @@ -64,7 +64,8 @@ int make_plasma_tetrad(double Ucon[NDIM], double Kcon[NDIM], double Bcon[NDIM], // [0 2/sqrt(5) ] // While there are more complex & faster reorthogonalization methods, simply // repeating the orthogonalization exactly once for each vector reaches near - // optimal accuracy, see Giraud and Langou 2005 for the analysis and some background + // optimal accuracy, see Giraud and Langou 2003 for background and + // Giraud et al. '05 for the relevant analysis // Note we choose the order carefully to preserve ucon == e^0 perfectly, // and u^3 ~= Kcon as closely as possible. From 6f1276d46d1b8d5d4131e427dd05f5d497f71543 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 25 May 2021 22:03:56 -0500 Subject: [PATCH 40/93] Remove explicit zero case. Case 2->1 is stable and accurate if probably overkill --- src/ipolarray.c | 27 +++++++++------------------ src/main.c | 2 +- 2 files changed, 10 insertions(+), 19 deletions(-) diff --git a/src/ipolarray.c b/src/ipolarray.c index 44b80ed..34e0a62 100644 --- a/src/ipolarray.c +++ b/src/ipolarray.c @@ -21,7 +21,6 @@ // Cuts to use limiting solutions based // on optical depth (tau) -#define CUT_INSANE_ABS 500 #define CUT_HIGH_ABS 50 #define CUT_LOW_ABS 1e-10 // Low-rotativity linear limit (tauF) @@ -356,20 +355,12 @@ int evolve_N(double Xi[NDIM], double Kconi[NDIM], // Solutions from large to small absorptivity. // Note we assume that coming from jar_calc, aI > aP - // Case 1: So much absorption everything dies - // Case 2: So much absorption that doubles become inaccurate - // Case 3: normal - // Case 4: So little polarized absorption as to be negligible - // Case 5: So little absorption as to be negligible - if (aI*x > CUT_INSANE_ABS) { - // Case 1: Cement wall - SI2 = 0; - SQ2 = 0; - SU2 = 0; - SV2 = 0; - - } else if (aI*x > CUT_HIGH_ABS) { - // Case 2: High enough aI (and possibly aP) to be a numerical problem + // Case 1: So much absorption that doubles become inaccurate + // Case 2: normal + // Case 3: So little polarized absorption as to be negligible + // Case 4: So little absorption as to be negligible + if (aI*x > CUT_HIGH_ABS) { + // Case 1: High enough aI (and possibly aP) to be a numerical problem // Solution assumes aI ~ aP >> 1, the worst case. // Small aP sends expDiffx = exp(aP-aI)->0, which is safe for all terms double expDiffx = exp((aP-aI) * x)/2; @@ -402,7 +393,7 @@ int evolve_N(double Xi[NDIM], double Kconi[NDIM], (-aP + (aP * expDiffx + aI * expDiffx))); } else if (aI*x > CUT_LOW_ABS && aP*x > CUT_LOW_ABS) { - // Case 3: Normal solution, some absorption but not difficult amounts + // Case 2: Normal solution, some absorption but not difficult amounts double expaIx = exp(-aI * x); double sinhaPx = sinh(aP * x); double coshaPx = cosh(aP * x); @@ -443,7 +434,7 @@ int evolve_N(double Xi[NDIM], double Kconi[NDIM], (-aP + (aP * coshaPx + aI * sinhaPx) * expaIx)); } else if (aI*x > CUT_LOW_ABS) { - // Case 4: Account for aI but negligible aP + // Case 3: Account for aI but negligible aP double expaIx = exp(-aI * x); SI2 = SI1*expaIx + jI/aI - jI/aI*expaIx @@ -463,7 +454,7 @@ int evolve_N(double Xi[NDIM], double Kconi[NDIM], SV1*expaIx - (aV*SI1*x)*expaIx + (aV*jI*x)/aI*expaIx; } else { - // Case 5: Both absorptivities are negligible + // Case 4: Both absorptivities are negligible SI2 = SI1 + x * jI; SQ2 = SQ1 + x * jQ; SU2 = SU1 + x * jU; diff --git a/src/main.c b/src/main.c index fda7124..f5c0c5a 100644 --- a/src/main.c +++ b/src/main.c @@ -218,7 +218,7 @@ int main(int argc, char *argv[]) int nstep = 0; double dl; double X[NDIM], Xhalf[NDIM], Kcon[NDIM], Kconhalf[NDIM]; - init_XK(i,j, params.nx, params.ny, Xcam, params, fovx,fovy, X, Kcon); + init_XK(i,j, params.nx, params.ny, Xcam, params, fovx, fovy, X, Kcon); MULOOP Kcon[mu] *= freq; From e52f219133f2919ef4e535fe22bcb8c7b55902d9 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Sun, 30 May 2021 19:40:45 -0500 Subject: [PATCH 41/93] Fix a typo and some omitted function stubs on merging master --- model/analytic/model.c | 3 ++- model/ldi2/model.c | 2 ++ model/riaf/model.c | 2 ++ model/thin_disk/model.c | 2 ++ src/model_radiation.c | 10 +++++----- 5 files changed, 13 insertions(+), 6 deletions(-) diff --git a/model/analytic/model.c b/model/analytic/model.c index 4cee660..21a2bde 100644 --- a/model/analytic/model.c +++ b/model/analytic/model.c @@ -319,6 +319,7 @@ int radiating_region(double X[NDIM]) double get_model_thetae(double X[NDIM]) {return 0;} void get_model_powerlaw_vals(double X[NDIM], double *p, double *n, double *gamma_min, double *gamma_max, double *gamma_cut) {return;} - +void update_data(double *tA, double *tB) {return;} +void update_data_until(double *tA, double *tB, double tgt) {return;} // This is only called for trace file output, and doesn't really apply to analytic models void get_model_primitives(double X[NDIM], double *p) {return;} diff --git a/model/ldi2/model.c b/model/ldi2/model.c index a06de3b..72c8df6 100644 --- a/model/ldi2/model.c +++ b/model/ldi2/model.c @@ -191,3 +191,5 @@ double get_model_thetae(double X[NDIM]) {return 0;} double get_model_b(double X[NDIM]) {return 1;} double get_model_ne(double X[NDIM]) {return 1;} // Otherwise we trigger the "empty space" emissivity void get_model_primitives(double X[NDIM], double *p) {return;} +void update_data(double *tA, double *tB) {return;} +void update_data_until(double *tA, double *tB, double tgt) {return;} \ No newline at end of file diff --git a/model/riaf/model.c b/model/riaf/model.c index 9b2dc9c..c58fe5b 100644 --- a/model/riaf/model.c +++ b/model/riaf/model.c @@ -376,6 +376,8 @@ int radiating_region(double X[NDIM]) // synchrotron radiation based on an energy distribution void get_model_powerlaw_vals(double X[NDIM], double *p, double *n, double *gamma_min, double *gamma_max, double *gamma_cut) {return;} +void update_data(double *tA, double *tB) {return;} +void update_data_until(double *tA, double *tB, double tgt) {return;} // This is only called for trace file output, and doesn't really apply to analytic models void get_model_primitives(double X[NDIM], double *p) {return;} void get_model_jk(double X[NDIM], double Kcon[NDIM], double *jnuinv, double *knuinv) {return;} diff --git a/model/thin_disk/model.c b/model/thin_disk/model.c index a9a1c53..c79a7d4 100644 --- a/model/thin_disk/model.c +++ b/model/thin_disk/model.c @@ -374,6 +374,8 @@ double get_model_ne(double X[NDIM]) {return 0;} void get_model_primitives(double X[NDIM], double *p) {return;} void get_model_powerlaw_vals(double X[NDIM], double *p, double *n, double *gamma_min, double *gamma_max, double *gamma_cut) {return;} +void update_data(double *tA, double *tB) {return;} +void update_data_until(double *tA, double *tB, double tgt) {return;} // In case we want to mess with emissivities directly void get_model_jar(double X[NDIM], double Kcon[NDIM], double *jI, double *jQ, double *jU, double *jV, diff --git a/src/model_radiation.c b/src/model_radiation.c index 1fa8e28..02f8269 100644 --- a/src/model_radiation.c +++ b/src/model_radiation.c @@ -33,7 +33,7 @@ #define E_POWERLAW 3 #define E_DEXTER_THERMAL 4 #define E_CUSTOM 10 -// Rotation +// Rotation #define ROT_OLD 11 #define ROT_PIECEWISE 12 #define ROT_SHCHERBAKOV 13 @@ -252,13 +252,13 @@ void jar_calc_dist(int dist, double X[NDIM], double Kcon[NDIM], } if (*jI * *jI < *jQ * *jQ + *jU * *jU + *jV * *jV) { // Transport does not like 100% polarization... + double jP = sqrt(*jQ * *jQ + *jU * *jU + *jV * *jV); double pol_frac_e = *jI / jP * max_pol_frac_e; *jQ *= pol_frac_e; *jU *= pol_frac_e; *jV *= pol_frac_e; #if DEBUG - fprintf(stderr, "Polarized emissivities too large:\n %g vs %g, corrected by %g\n", - sqrt(*jQ * *jQ + *jU * *jU + *jV * *jV), *jI, pol_frac_e); + fprintf(stderr, "Polarized emissivities too large:\n %g vs %g, corrected by %g\n", jP, *jI, pol_frac_e); #endif } @@ -291,13 +291,13 @@ void jar_calc_dist(int dist, double X[NDIM], double Kcon[NDIM], } if (*aI * *aI < *aQ * *aQ + *aU * *aU + *aV * *aV) { // Transport does not like 100% polarization... + double aP = sqrt(*aQ * *aQ + *aU * *aU + *aV * *aV); double pol_frac_a = *aI / aP * max_pol_frac_a; *aQ *= pol_frac_a; *aU *= pol_frac_a; *aV *= pol_frac_a; #if DEBUG - fprintf(stderr, "Polarized absorptivities too large:\n %g vs %g, corrected by %g\n", - sqrt(*aQ * *aQ + *aU * *aU + *aV * *aV), *aI, pol_frac_a); + fprintf(stderr, "Polarized absorptivities too large:\n %g vs %g, corrected by %g\n", aP, *aI, pol_frac_a); #endif } From 540293e7b27f74562f00bd7225112bf35d439220 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Sun, 30 May 2021 22:07:36 -0500 Subject: [PATCH 42/93] Replace cases with a single expression Introduces a single expression for the analytic solution in ipolarray.c, which uses expansions in small optical depth to remain accurate for small/close aI/aP/both, courtesy of Charles Gammie. The only remaining logic in choosing expressions is to guard against outright NaN values on inverse, i.e. aP2/rho2 < 1e-100. In these cases, linear expressions are substituted for those containing the inverse terms. --- src/ipolarray.c | 255 ++++++++++++++++++++++-------------------------- 1 file changed, 114 insertions(+), 141 deletions(-) diff --git a/src/ipolarray.c b/src/ipolarray.c index 34e0a62..7a9235d 100644 --- a/src/ipolarray.c +++ b/src/ipolarray.c @@ -19,12 +19,14 @@ #include "debug_tools.h" #include -// Cuts to use limiting solutions based -// on optical depth (tau) -#define CUT_HIGH_ABS 50 -#define CUT_LOW_ABS 1e-10 -// Low-rotativity linear limit (tauF) -#define CUT_LOW_ROT 1e-10 + +// Define where to switch from 1-exp() to +// Taylor-expanded quantities in the optical +// depth +#define CUT_SMALL_OPTICAL_DEPTH 1e-5 + +// Smallest double that prevents NaNs on inverses +#define CUT_PREVENT_NAN 1e-100 // Sub-functions void push_polar(double Xi[NDIM], double Xm[NDIM], double Xf[NDIM], @@ -150,7 +152,6 @@ int integrate_emission(struct of_traj *traj, int nsteps, oddflag |= sflag; - // Cry immediately on bad tetrads, even if we're not debugging if (sflag & 1) { fprintf(stderr, "that's odd: no orthonormal tetrad found at\n"); @@ -217,7 +218,7 @@ int integrate_emission(struct of_traj *traj, int nsteps, } //fprintf(stderr, "End integrate emission\n"); - // Otherwise propagate the full flag so caller can yell or record + // Otherwise propagate the full flag so caller can handle it return oddflag; } @@ -277,13 +278,16 @@ int evolve_N(double Xi[NDIM], double Kconi[NDIM], double Ucov[NDIM],Bcov[NDIM]; double Ecov[NDIM][NDIM], Econ[NDIM][NDIM]; double complex N_tetrad[NDIM][NDIM]; + double jI, jQ, jU, jV; double aI, aQ, aU, aV; double rV, rU, rQ; - double SI = 0, SQ = 0, SU = 0, SV = 0; + double SI0, SQ0, SU0, SV0; double SI1, SQ1, SU1, SV1; double SI2, SQ2, SU2, SV2; + double SI, SQ, SU, SV; + int oddflag = 0; // get fluid parameters at Xf @@ -315,20 +319,15 @@ int evolve_N(double Xi[NDIM], double Kconi[NDIM], gcov_func(Xf, gcov); oddflag |= make_plasma_tetrad(Ucon, Kconf, Bcon, gcov, Econ, Ecov); - // TODO If B is 0, just keep guessing - //int exhausted = 0; - //while (oddflag && B <= 0. && !exhausted) { etc } - - /* convert N to Stokes */ + // Convert N to Stokes parameters complex_coord_to_tetrad_rank2(N_coord, Ecov, N_tetrad); tensor_to_stokes(N_tetrad, &SI0, &SQ0, &SU0, &SV0); - /* apply the Faraday rotation solution for a half step */ + // Apply the Faraday rotation solution for a half step double x = dlam * 0.5; - double rho2 = rQ * rQ + rU * rU + rV * rV; - double rho = sqrt(rho2); - if (rho * x > CUT_LOW_ROT) { + if (rho2 > CUT_PREVENT_NAN) { + double rho = sqrt(rho2); double rdS = rQ * SQ0 + rU * SU0 + rV * SV0; double c = cos(rho * x); double s = sin(rho * x); @@ -343,131 +342,106 @@ int evolve_N(double Xi[NDIM], double Kconi[NDIM], SU1 = SU0 + ( rV * SQ0 - rQ * SV0) * x; SV1 = SV0 + (-rU * SQ0 + rQ * SU0) * x; } - /* done rotation solution half step */ - /* apply full absorption/emission step */ + // Apply full absorption/emission step x = dlam; - double aI2 = aI * aI; double aP2 = aQ * aQ + aU * aU + aV * aV; - double aP = sqrt(aP2); - double ads0 = aQ * SQ1 + aU * SU1 + aV * SV1; - double adj = aQ * jQ + aU * jU + aV * jV; - - // Solutions from large to small absorptivity. - // Note we assume that coming from jar_calc, aI > aP - // Case 1: So much absorption that doubles become inaccurate - // Case 2: normal - // Case 3: So little polarized absorption as to be negligible - // Case 4: So little absorption as to be negligible - if (aI*x > CUT_HIGH_ABS) { - // Case 1: High enough aI (and possibly aP) to be a numerical problem - // Solution assumes aI ~ aP >> 1, the worst case. - // Small aP sends expDiffx = exp(aP-aI)->0, which is safe for all terms - double expDiffx = exp((aP-aI) * x)/2; - - SI2 = (SI1 * expDiffx - - (ads0 / aP) * expDiffx - + adj / (aI2 - aP2) * (-1 + (aI * expDiffx + aP * expDiffx) / aP) - + aI * jI / (aI2 - aP2) * (1 - (aI * expDiffx + aP * expDiffx) / aI)); - - SQ2 = (ads0 * aQ / aP2 * expDiffx - - aQ / aP * SI1 * expDiffx - + jQ / aI - + adj * aQ / (aI * (aI2 - aP2)) * - (1 - aI / aP2 * (aI * expDiffx + aP * expDiffx)) - + jI * aQ / (aP * (aI2 - aP2)) * (-aP + (aP * expDiffx + aI * expDiffx))); - - SU2 = (ads0 * aU / aP2 * expDiffx - - aU / aP * SI1 * expDiffx - + jU / aI - + adj * aU / (aI * (aI2 - aP2)) * - (1 - aI / aP2 * (aI * expDiffx + aP * expDiffx)) - + jI * aU / (aP * (aI2 - aP2)) * (-aP + (aP * expDiffx + aI * expDiffx))); - - SV2 = (ads0 * aV / aP2 * expDiffx - - aV / aP * SI1 * expDiffx - + jV / aI - + adj * aV / (aI * (aI2 - aP2)) * (1 - - aI / aP2 * (aI * expDiffx + aP * expDiffx)) - + jI * aV / (aP * (aI2 - aP2)) * - (-aP + (aP * expDiffx + aI * expDiffx))); - - } else if (aI*x > CUT_LOW_ABS && aP*x > CUT_LOW_ABS) { - // Case 2: Normal solution, some absorption but not difficult amounts - double expaIx = exp(-aI * x); - double sinhaPx = sinh(aP * x); - double coshaPx = cosh(aP * x); - - SI2 = (SI1 * coshaPx * expaIx - - (ads0 / aP) * sinhaPx * expaIx - + adj / (aI2 - aP2) * (-1 + (aI * sinhaPx + aP * coshaPx) / aP * expaIx) - + aI * jI / (aI2 - aP2) * (1 - (aI * coshaPx + aP * sinhaPx) / aI * expaIx)); - - SQ2 = (SQ1 * expaIx - + ads0 * aQ / aP2 * (-1 + coshaPx) * expaIx - - aQ / aP * SI1 * sinhaPx * expaIx - + jQ * (1 - expaIx) / aI - + adj * aQ / (aI * (aI2 - aP2)) * (1 - (1 - aI2 / aP2) * expaIx - - aI / aP2 * (aI * coshaPx + aP * sinhaPx) * expaIx) - + jI * aQ / (aP * (aI2 - aP2)) * (-aP + (aP * coshaPx + aI * sinhaPx) * expaIx)); - - SU2 = (SU1 * expaIx - + ads0 * aU / aP2 * (-1 + coshaPx) * expaIx - - aU / aP * SI1 * sinhaPx * expaIx - + jU * (1 - expaIx) / aI - + adj * aU / (aI * (aI2 - aP2)) * - (1 - (1 - aI2 / aP2) * expaIx - - aI / aP2 * (aI * coshaPx + - aP * sinhaPx) * expaIx) - + jI * aU / (aP * (aI2 - aP2)) * - (-aP + (aP * coshaPx + aI * sinhaPx) * expaIx)); - - SV2 = (SV1 * expaIx - + ads0 * aV / aP2 * (-1 + coshaPx) * expaIx - - aV / aP * SI1 * sinhaPx * expaIx - + jV * (1 - expaIx) / aI - + adj * aV / (aI * (aI2 - aP2)) * (1 - - (1 - aI2 / aP2) * expaIx - - aI / aP2 * (aI * coshaPx + - aP * sinhaPx) * expaIx) - + jI * aV / (aP * (aI2 - aP2)) * - (-aP + (aP * coshaPx + aI * sinhaPx) * expaIx)); - - } else if (aI*x > CUT_LOW_ABS) { - // Case 3: Account for aI but negligible aP - double expaIx = exp(-aI * x); - SI2 = SI1*expaIx - + jI/aI - jI/aI*expaIx - - (aQ*jQ)/aI2 + (aQ*jQ)/aI2*expaIx - - (aU*jU)/aI2 + (aU*jU)/aI2*expaIx - - (aV*jV)/aI2 + (aV*jV)/aI2*expaIx - + (aQ*jQ*x)/aI*expaIx + (aU*jU*x)/aI*expaIx + (aV*jV*x)/aI*expaIx - - (aQ*SQ1*x)*expaIx - (aU*SU1*x)*expaIx - (aV*SV1*x)*expaIx; - - SQ2 = -((aQ*jI)/aI2) + (aQ*jI)/aI2*expaIx + jQ/aI - jQ/aI*expaIx + - SQ1*expaIx - (aQ*SI1*x)*expaIx + (aQ*jI*x)/aI*expaIx; - - SU2 = -((aU*jI)/aI2) + (aU*jI)/aI2*expaIx + jU/aI - jU/aI*expaIx + - SU1*expaIx - (aU*SI1*x)*expaIx + (aU*jI*x)/aI*expaIx; - - SV2 = -((aV * jI) / aI2) + (aV*jI)/aI2*expaIx + jV/aI - jV/aI*expaIx + - SV1*expaIx - (aV*SI1*x)*expaIx + (aV*jI*x)/aI*expaIx; - + if (aP2 > CUT_PREVENT_NAN) { // If 1/(aP2) will not return NaN... + double aI2 = aI * aI; + double aP = sqrt(aP2); + double tauP = aP*x; + double tauI = aI*x; + double ads0 = aQ * SQ1 + aU * SU1 + aV * SV1; + double adj = aQ * jQ + aU * jU + aV * jV; + // appearance of factor 1/(aI2 - aP2) suggests that polarized and unpolarized + // transfer will differ by O(aP2/aI2) + double efacm = exp(-tauI + tauP); + double efacp = exp(-tauI - tauP); + double efac = exp(-tauI); + + /* The formal solution for polarized transfer with emission and absorption + * but no faraday rotation has 3 distinct eigenvalues (the first is degenerate): + * aI, aI+aP, and aI-aP, see Moscibrodzka & Gammie 2017 appendix A2. + * + * The general solution contains terms like (1 - exp(-\lambda/\lambda_i)), + * which can suffer loss of precision for small lambda. + */ + + /* this is the piece that captures effect of absorption on initial stokes vector, + * safe for all optical depths + * + * These expressions assume that aP < aI and all jS, aS > 0 + */ + SI2 = efacm*(SI1/2. - ads0/(2.*aP)) + + efacp*(SI1/2. + ads0/(2.*aP)); + + SQ2 = efacm*(-SI1*aQ*aP + ads0*aQ)/(2.*aP2) + + efacp*(SI1*aQ*aP + ads0*aQ)/(2.*aP2) + + efac*(SQ1 - ads0*aQ/(aP2)); + + SU2 = efacm*(-SI1*aU*aP + ads0*aU)/(2.*aP2) + + efacp*(SI1*aU*aP + ads0*aU)/(2.*aP2) + + efac*(SU1 - ads0*aU/(aP2)); + + SV2 = efacm*(-SI1*aV*aP + ads0*aV)/(2.*aP2) + + efacp*(SI1*aV*aP + ads0*aV)/(2.*aP2) + + efac*(SV1 - ads0*aV/(aP2)); + + /* In the limit of large optical depth Kirchoff's law is satisfied + * for thermal transfer coefficients, i.e. the solution reduces to + * {I,Q,U,V} = {B_\nu, 0, 0, 0}, provided j_S = a_S B_\nu (S = IQUV) + * Notice that there is a potential loss of precision since the Q,U,V + * terms vanish for thermal eDFs. + */ + + // Taylor series expansion at small optical depth to avoid loss of precision + double afacm = 1. - efacm; + double afac = 1. - efac; + double afacp = 1. - efacp; + // Try to be clever by nesting the conditions. Of questionable use. + if (tauI - tauP <= CUT_SMALL_OPTICAL_DEPTH) { + double e = tauI - tauP; + afacm = e*(1. - (e/2.)*(1. - e/3.)); + + if (tauI <= CUT_SMALL_OPTICAL_DEPTH) { + e = tauI; + afac = e*(1. - (e/2.)*(1. - e/3.)); + + if (tauI + tauP <= CUT_SMALL_OPTICAL_DEPTH) { + e = tauI + tauP; + afacp = e*(1. - (e/2.)*(1. - e/3.)); + } + } + } + // piece proportional to afac + SQ2 += afac*(jQ/aI - aQ*adj/(aI*aP2)); + SU2 += afac*(jU/aI - aU*adj/(aI*aP2)); + SV2 += afac*(jV/aI - aV*adj/(aI*aP2)); + // pieces proportional to afacm + SI2 += afacm*(aP*jI - adj)/(2.*aP*(aI - aP)); + SQ2 += afacm*aQ*(-aP*jI + adj)/(2.*aP2*(aI - aP)); + SU2 += afacm*aU*(-aP*jI + adj)/(2.*aP2*(aI - aP)); + SV2 += afacm*aV*(-aP*jI + adj)/(2.*aP2*(aI - aP)); + // pieces proportional to afacp + SI2 += afacp*(aP*jI + adj)/(2.*aP*(aI + aP)); + SQ2 += afacp*aQ*(aP*jI + adj)/(2.*aP2*(aI + aP)); + SU2 += afacp*aU*(aP*jI + adj)/(2.*aP2*(aI + aP)); + SV2 += afacp*aV*(aP*jI + adj)/(2.*aP2*(aI + aP)); } else { - // Case 4: Both absorptivities are negligible - SI2 = SI1 + x * jI; - SQ2 = SQ1 + x * jQ; - SU2 = SU1 + x * jU; - SV2 = SV1 + x * jV; + // Since we can now rely on aP==0, this is less questionable + double tau_fake = 0; + SI2 = approximate_solve(SI1, jI, aI, jI, aI, x, &tau_fake); + SQ2 = approximate_solve(SQ1, jQ, aI, jQ, aI, x, &tau_fake); + SU2 = approximate_solve(SU1, jU, aI, jU, aI, x, &tau_fake); + SV2 = approximate_solve(SV1, jV, aI, jV, aI, x, &tau_fake); } - /* done absorption/emission full step */ - /* apply second rotation half-step */ + // Apply the second rotation half-step x = dlam * 0.5; rho2 = rQ * rQ + rU * rU + rV * rV; - rho = sqrt(rho2); - if (rho*x > CUT_LOW_ROT) { - double rdS = rQ * SQ2 + rU * SU2 + rV * SV2; + if (rho2 > CUT_PREVENT_NAN) { + double rdS = rQ * SQ0 + rU * SU0 + rV * SV0; + double rho = sqrt(rho2); double c = cos(rho * x); double s = sin(rho * x); double sh = sin(0.5 * rho * x); @@ -481,14 +455,15 @@ int evolve_N(double Xi[NDIM], double Kconi[NDIM], SU = SU2 + ( rV * SQ2 - rQ * SV2) * x; SV = SV2 + (-rU * SQ2 + rQ * SU2) * x; } - /* done second rotation half-step */ - *tauF += dlam*fabs(rV); //*sqrt(SQ*SQ + SU*SU); + *tauF += dlam*fabs(rV); if (params->stokes_floors) { - // Correct the resulting Stokes parameters to guarantee: + // Optionally correct the resulting Stokes parameters to guarantee: // 1. I > 0 // 2. sqrt(Q^2 + U^2 + V^2) < I + // This is ensured of the emissivities by default, + // but can be additionally enforced here. if (SI < 0) { SI = 0; SQ = 0; @@ -504,7 +479,7 @@ int evolve_N(double Xi[NDIM], double Kconi[NDIM], } } - /* re-pack the Stokes parameters into N */ + // Re-pack the Stokes parameters into N stokes_to_tensor(SI, SQ, SU, SV, N_tetrad); complex_tetrad_to_coord_rank2(N_tetrad, Econ, N_coord); @@ -519,8 +494,6 @@ int evolve_N(double Xi[NDIM], double Kconi[NDIM], // Flag if something is wrong if (*tauF > 1.e100 || *tauF < -1.e100 || isnan(*tauF)) oddflag |= 2; if (isnan(creal(N_tetrad[0][0])) || isnan(creal(N_coord[0][0]))) oddflag |= 4; - - /* SOURCE STEP DONE */ return oddflag; } From 12d2954bca0c7125ff5b24f48f11ddc45dacc0c7 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Mon, 31 May 2021 22:39:06 -0500 Subject: [PATCH 43/93] Fix a NaN in the kappa fits --- src/symphony/kappa_fits.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/symphony/kappa_fits.c b/src/symphony/kappa_fits.c index 785cd47..7f3bf72 100644 --- a/src/symphony/kappa_fits.c +++ b/src/symphony/kappa_fits.c @@ -1,5 +1,9 @@ #include "kappa.h" +#include "stdio.h" + +#define SMALL 1e-40 + /*kappa_I: fitting formula to the emissivity, in Stokes I, produced by a * kappa distribution of electrons (without any exponential * cutoff). Uses eq. 29, 35, 36, 37, 38 of [1]. @@ -116,9 +120,10 @@ double kappa_V(struct parameters * params) double x = 3.*pow(params->kappa, -3./2.); - double ans = prefactor * Nlow * pow(X_k, 1./3.) - * pow(1.+pow(X_k, x * (3.*params->kappa-4.)/6.) - * pow(Nlow/Nhigh, x), -1./x); + double ans = (Nhigh < SMALL*SMALL) ? 0: + prefactor * Nlow * pow(X_k, 1./3.) + * pow(1.+pow(X_k, x * (3.*params->kappa-4.)/6.) + * pow(Nlow/Nhigh, x), -1./x); /*The Stokes V absorption coefficient changes sign at observer_angle equals 90deg, but this formula does not. This discrepancy is a @@ -130,6 +135,12 @@ double kappa_V(struct parameters * params) and Pandya et al. (2016) for Stokes V transfer coefficients does not follow the convention the papers describe (IEEE/IAU); the sign has been corrected here.*/ + +// if (isnan(ans) || isnan(sign_bug_patch)) { +// fprintf(stderr, "NaN in kappa. X_k, prefactor, Nlow, Nhigh, x, ans, sign: %g %g %g %g %g %g %g", +// X_k, prefactor, Nlow, Nhigh, x, ans, sign_bug_patch); +// } + return -ans * sign_bug_patch; } From e6f0e4f095c1c7c7b19b789556912ce0134e689a Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 1 Jun 2021 10:36:19 -0500 Subject: [PATCH 44/93] Stabilize hypergeometric function application in Kappa fit, courtesy of Angelo Ricarte --- src/symphony/kappa_fits.c | 50 +++++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/src/symphony/kappa_fits.c b/src/symphony/kappa_fits.c index 7f3bf72..6b921d4 100644 --- a/src/symphony/kappa_fits.c +++ b/src/symphony/kappa_fits.c @@ -4,6 +4,29 @@ #define SMALL 1e-40 +/** + * Stabilized version of the hypergeometric fn: + * Use GSL's version on small domain of validity, + * use an extension to maintain stability outside that + * + * Final form courtesy of Angelo Ricarte + */ +inline double stable_hyp2f1(double a, double b, double c, double z) +{ + if (z > -1 && z < 1) { + return gsl_sf_hyperg_2F1(a, b, c, z); + } else { + /*GSL 2F1 only works for |z| < 1; had to apply a hypergeometric function + identity because in our case z = -kappa*w, so |z| > 1 */ + return pow(1.-z, -a) * tgamma(c) * tgamma(b-a) + / (tgamma(b)*tgamma(c-a)) + * gsl_sf_hyperg_2F1(a, c-b, a-b+1., 1./(1.-z)) + + pow(1.-z, -b) * tgamma(c) * tgamma(a-b) + / (tgamma(a) * tgamma(c-b)) + * gsl_sf_hyperg_2F1(b, c-a, b-a+1., 1./(1.-z)); + } +} + /*kappa_I: fitting formula to the emissivity, in Stokes I, produced by a * kappa distribution of electrons (without any exponential * cutoff). Uses eq. 29, 35, 36, 37, 38 of [1]. @@ -172,14 +195,7 @@ double kappa_I_abs(struct parameters * params) double z = -params->kappa*params->kappa_width; - /*GSL 2F1 only works for |z| < 1; had to apply a hypergeometric function - identity because in our case z = -kappa*w, so |z| > 1 */ - double hyp2f1 = pow(1.-z, -a) * tgamma(c) * tgamma(b-a) - / (tgamma(b)*tgamma(c-a)) - * gsl_sf_hyperg_2F1(a, c-b, a-b+1., 1./(1.-z)) - + pow(1.-z, -b) * tgamma(c) * tgamma(a-b) - / (tgamma(a) * tgamma(c-b)) - * gsl_sf_hyperg_2F1(b, c-a, b-a+1., 1./(1.-z)); + double hyp2f1 = stable_hyp2f1(a, b, c, z); double Nlow = pow(3., 1./6.) * (10./41.) * pow(2. * params->pi, 2.) / pow(params->kappa_width * params->kappa, 16./3.-params->kappa) @@ -229,13 +245,8 @@ double kappa_Q_abs(struct parameters * params) double z = -params->kappa * params->kappa_width; - /*GSL 2F1 only works for |z| < 1; had to apply a hypergeometric function - identity because in our case z = -kappa*w, so |z| > 1 */ - double hyp2f1 = pow(1.-z, -a) * tgamma(c) * tgamma(b-a) - / (tgamma(b) * tgamma(c-a)) - * gsl_sf_hyperg_2F1(a, c-b, a-b+1., 1./(1.-z))+pow(1.-z, -b) - * tgamma(c) * tgamma(a-b) / (tgamma(a)*tgamma(c-b)) - * gsl_sf_hyperg_2F1(b, c - a, b - a + 1., 1./(1. - z)); + double hyp2f1 = stable_hyp2f1(a, b, c, z); + double Nlow = -(25./48.) * pow(3., 1./6.) * (10./41.) * pow(2. * params->pi, 2.) / pow(params->kappa_width*params->kappa, 16./3.-params->kappa) @@ -285,14 +296,7 @@ double kappa_V_abs(struct parameters * params) double z = -params->kappa * params->kappa_width; - /*GSL 2F1 only works for |z| < 1; had to apply a hypergeometric function - identity because in our case z = -kappa*w, so |z| > 1 */ - double hyp2f1 = pow(1.-z, -a) * tgamma(c) * tgamma(b-a) - / (tgamma(b) * tgamma(c-a)) - * gsl_sf_hyperg_2F1(a, c-b, a-b+1., 1./(1.-z)) - + pow(1.-z, -b) * tgamma(c) * tgamma(a-b) - / (tgamma(a) * tgamma(c-b)) - * gsl_sf_hyperg_2F1(b, c - a, b - a + 1., 1./(1.-z)); + double hyp2f1 = stable_hyp2f1(a, b, c, z); double Nlow = -(77./(100. * params->kappa_width)) * pow(pow(sin(params->observer_angle), -114./50.) From aaadb6e0c46140e190c0cfc6bbea7ffbb663170a Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 1 Jun 2021 12:12:12 -0500 Subject: [PATCH 45/93] Fix inlining and remove unused variable so GCC compiles correctly without warnings --- src/ipolarray.c | 1 - src/symphony/kappa_fits.c | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ipolarray.c b/src/ipolarray.c index 7a9235d..ecd9eeb 100644 --- a/src/ipolarray.c +++ b/src/ipolarray.c @@ -347,7 +347,6 @@ int evolve_N(double Xi[NDIM], double Kconi[NDIM], x = dlam; double aP2 = aQ * aQ + aU * aU + aV * aV; if (aP2 > CUT_PREVENT_NAN) { // If 1/(aP2) will not return NaN... - double aI2 = aI * aI; double aP = sqrt(aP2); double tauP = aP*x; double tauI = aI*x; diff --git a/src/symphony/kappa_fits.c b/src/symphony/kappa_fits.c index 6b921d4..9358f13 100644 --- a/src/symphony/kappa_fits.c +++ b/src/symphony/kappa_fits.c @@ -11,7 +11,7 @@ * * Final form courtesy of Angelo Ricarte */ -inline double stable_hyp2f1(double a, double b, double c, double z) +static inline double stable_hyp2f1(double a, double b, double c, double z) { if (z > -1 && z < 1) { return gsl_sf_hyperg_2F1(a, b, c, z); From 410e2334524a366b4844b203dbcff940be52f292 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Mon, 7 Jun 2021 11:41:10 -0500 Subject: [PATCH 46/93] Correct re-definitions in some models after the merge --- model/analytic/model.c | 2 -- model/riaf/model.c | 2 -- model/thin_disk/model.c | 2 -- 3 files changed, 6 deletions(-) diff --git a/model/analytic/model.c b/model/analytic/model.c index fb24162..2a5b905 100644 --- a/model/analytic/model.c +++ b/model/analytic/model.c @@ -319,8 +319,6 @@ int radiating_region(double X[NDIM]) double get_model_thetae(double X[NDIM]) {return 0;} double get_model_sigma(double X[NDIM]) {return 0;} double get_model_beta(double X[NDIM]) {return 0;} -void get_model_powerlaw_vals(double X[NDIM], double *p, double *n, - double *gamma_min, double *gamma_max, double *gamma_cut) {return;} void update_data(double *tA, double *tB) {return;} void update_data_until(double *tA, double *tB, double tgt) {return;} // This is only called for trace file output, and doesn't really apply to analytic models diff --git a/model/riaf/model.c b/model/riaf/model.c index 7db49dc..ae15b78 100644 --- a/model/riaf/model.c +++ b/model/riaf/model.c @@ -377,8 +377,6 @@ int radiating_region(double X[NDIM]) //// STUBS: Functions for normal models which we don't use //// // Define these to specify a fluid model: e- density/temperature for // synchrotron radiation based on an energy distribution -void get_model_powerlaw_vals(double X[NDIM], double *p, double *n, - double *gamma_min, double *gamma_max, double *gamma_cut) {return;} void update_data(double *tA, double *tB) {return;} void update_data_until(double *tA, double *tB, double tgt) {return;} // This is only called for trace file output, and doesn't really apply to analytic models diff --git a/model/thin_disk/model.c b/model/thin_disk/model.c index 20092d9..e4bdd7b 100644 --- a/model/thin_disk/model.c +++ b/model/thin_disk/model.c @@ -374,8 +374,6 @@ double get_model_ne(double X[NDIM]) {return 0;} double get_model_sigma(double X[NDIM]) {return 0;} double get_model_beta(double X[NDIM]) {return 0;} void get_model_primitives(double X[NDIM], double *p) {return;} -void get_model_powerlaw_vals(double X[NDIM], double *p, double *n, - double *gamma_min, double *gamma_max, double *gamma_cut) {return;} void update_data(double *tA, double *tB) {return;} void update_data_until(double *tA, double *tB, double tgt) {return;} // In case we want to mess with emissivities directly From a9cc5fa4b0ae955b4861273b518b977acdffb9eb Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Sun, 20 Jun 2021 16:01:13 -0500 Subject: [PATCH 47/93] Add j_I >= 0 constraint to unpolarized transport --- src/model_radiation.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/model_radiation.c b/src/model_radiation.c index 02f8269..c596c68 100644 --- a/src/model_radiation.c +++ b/src/model_radiation.c @@ -594,6 +594,13 @@ void get_jkinv(double X[NDIM], double Kcon[NDIM], double *jnuinv, double *knuinv } + if (*jnuinv < 0) { + *jnuinv = 0; + } + if (*knuinv < 0) { + *knuinv = 0; + } + #if DEBUG if (isnan(*jnuinv) || isnan(*knuinv)) { fprintf(stderr, "\nisnan get_jkinv\n"); From 02fc6a80cfa4543a9fbc063269085e169db9567e Mon Sep 17 00:00:00 2001 From: George Wong Date: Tue, 22 Jun 2021 01:21:33 -0500 Subject: [PATCH 48/93] add more debug tooling and re-enable rhigh model --- model/iharm/model.c | 26 ++++---------------------- src/debug_tools.c | 1 + src/ipolarray.c | 12 +++++++++++- src/ipolarray.h | 3 ++- src/main.c | 2 +- src/model_geodesics.c | 1 + 6 files changed, 20 insertions(+), 25 deletions(-) diff --git a/model/iharm/model.c b/model/iharm/model.c index a10f256..31b9997 100644 --- a/model/iharm/model.c +++ b/model/iharm/model.c @@ -911,7 +911,7 @@ void init_koral_grid(char *fnam, int dumpidx) // called at the beginning of the run and sets the static parameters // along with setting up the grid - assert(42==0); + // assert(42==0); // this version of the code has not been validated to have the // right units and four-vector recovery. use at your own peril @@ -971,25 +971,7 @@ void init_koral_grid(char *fnam, int dumpidx) hdf5_read_single_val(&(mp_koral_jetcoords.alpha_1), "alpha1", H5T_IEEE_F64LE); hdf5_read_single_val(&(mp_koral_jetcoords.alpha_2), "alpha2", H5T_IEEE_F64LE); hdf5_read_single_val(&(mp_koral_jetcoords.cylindrify), "cylindrify", H5T_IEEE_F64LE); - - - /* - double mksr0; - double rbrk; - double rmin; - double rphotomax; - double fjet; - double fdisk; - double runi; - double rcoll_jet; - double rcoll_disk; - double rdecoll_jet; - double rdecoll_disk; - double rcyl; - double ncyl; - */ - - hdf5_set_directory("/header/geom/"); + hdf5_set_directory("/header/geom/"); fprintf(stderr, "KORAL simulation was run with JETCOORDS coordinates.\n"); } else { fprintf(stderr, "! unknown koral metric_run (%s). quitting.\n", metric_run); @@ -1067,7 +1049,7 @@ void init_koral_grid(char *fnam, int dumpidx) } else { if (USE_MIXED_TPTE && !USE_FIXED_TPTE) { fprintf(stderr, "Using mixed tp_over_te with trat_small = %g, trat_large = %g, and beta_crit = %g\n", trat_small, trat_large, beta_crit); - // Thetae set in init_physical_quantities(...) + ELECTRONS = 2; } else { fprintf(stderr, "! koral unsupported without native electrons or mixed tp_over_te.\n"); exit(6); @@ -1497,7 +1479,7 @@ void load_koral_data(int n, char *fnam, int dumpidx, int verbose) hdf5_read_array(data[n]->p[B3][0][0], "B3", 3, fdims, fstart, fcount, mdims, mstart, H5T_IEEE_F64LE); - + if (ELECTRONS == 9) { hdf5_read_array(data[n]->p[TFLK][0][0], "te", 3, fdims, fstart, fcount, mdims, mstart, H5T_IEEE_F64LE); diff --git a/src/debug_tools.c b/src/debug_tools.c index 785d1fd..67c05ad 100644 --- a/src/debug_tools.c +++ b/src/debug_tools.c @@ -168,6 +168,7 @@ void dump_at_X(double X[NDIM]) print_matrix("gcov", gcov); print_matrix("gcon", gcon); fprintf(stderr, "Ne, Thetae, B: %g, %g, %g\n", Ne, Thetae, B); + // if pure hydrogen, sigma = B*B/Ne / (4.*M_PI * CL*CL * (MP+ME)) print_vector("ucon", ucon); print_vector("ucov", ucov); print_vector("bcon", bcon); diff --git a/src/ipolarray.c b/src/ipolarray.c index 5f12152..0920af6 100644 --- a/src/ipolarray.c +++ b/src/ipolarray.c @@ -16,6 +16,7 @@ #include "model.h" #include "model_radiation.h" #include "model_tetrads.h" +#include "simcoords.h" #include "debug_tools.h" #include @@ -52,7 +53,8 @@ void complex_tetrad_to_coord_rank2(double complex T_tetrad[NDIM][NDIM], */ int integrate_emission(struct of_traj *traj, int nsteps, double *Intensity, double *Tau, double *tauF, - double complex N_coord[NDIM][NDIM], Params *params) + double complex N_coord[NDIM][NDIM], Params *params, + int print) { //fprintf(stderr, "Begin integrate emission\n"); // Initialize @@ -127,6 +129,14 @@ int integrate_emission(struct of_traj *traj, int nsteps, *Intensity = approximate_solve(*Intensity, ji, ki, jf, kf, ti.dl, Tau); } //fprintf(stderr, "Unpolarized transport\n"); + + if (print) { + double Xg[4] = { 0., 0., 0., 0. }; + eks_to_simcoord(ti.X, Xg); + fprintf(stderr, "INTEGRATION %d %d %g %g %g %g %g %g %g %g %g \n", + print, nstep, ti.X[1], ti.X[2], ti.X[3], Xg[1], Xg[2], Xg[3], + ji, *Intensity, ti.dl); + } // Solve polarized transport if (!params->only_unpolarized) { diff --git a/src/ipolarray.h b/src/ipolarray.h index aca842b..caa6b5a 100644 --- a/src/ipolarray.h +++ b/src/ipolarray.h @@ -16,7 +16,8 @@ // Top-level functions for solving emission int integrate_emission(struct of_traj *traj, int nstep, double *Intensity, double *Tau, double *tauF, - double complex N_coord[NDIM][NDIM], Params *params); + double complex N_coord[NDIM][NDIM], Params *params, + int print); // Needed for slow light. TODO extend above to use instead int evolve_N(double Xi[NDIM],double Kconi[NDIM], diff --git a/src/main.c b/src/main.c index 6dc3bc5..24247a3 100644 --- a/src/main.c +++ b/src/main.c @@ -782,7 +782,7 @@ void get_pixel(size_t i, size_t j, int nx, int ny, double Xcam[NDIM], Params par } // Integrate emission forward along trajectory - int oddflag = integrate_emission(traj, nstep, Intensity, Tau, tauF, N_coord, ¶ms); + int oddflag = integrate_emission(traj, nstep, Intensity, Tau, tauF, N_coord, ¶ms, 0); if (!only_intensity) { project_N(X, Kcon, N_coord, Is, Qs, Us, Vs, params.rotcam); diff --git a/src/model_geodesics.c b/src/model_geodesics.c index 0faf032..4ef7374 100644 --- a/src/model_geodesics.c +++ b/src/model_geodesics.c @@ -18,6 +18,7 @@ #include "tetrads.h" #include "debug_tools.h" +#include "simcoords.h" // TODO pick one or runtime #define STEP_STRICT_MIN 0 From 4a9580f070a7e2c70ebb5abff1ab11bdb293ae50 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 22 Jun 2021 18:46:37 -0500 Subject: [PATCH 49/93] Fix an old bug that left some ghost zones uninitialized. Set a sensible default value for kappa. --- model/iharm/model.c | 2 +- src/model_radiation.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/model/iharm/model.c b/model/iharm/model.c index bcbf764..25c6aff 100644 --- a/model/iharm/model.c +++ b/model/iharm/model.c @@ -965,7 +965,7 @@ void populate_boundary_conditions(int n) for (int i=0; ip[l][i][0][k] = data[n]->p[l][i][1][kflip]; data[n]->p[l][i][N2+1][k] = data[n]->p[l][i][N2][kflip]; diff --git a/src/model_radiation.c b/src/model_radiation.c index c596c68..72d2bc8 100644 --- a/src/model_radiation.c +++ b/src/model_radiation.c @@ -69,7 +69,7 @@ void jar_calc_dist(int dist, double X[NDIM], double Kcon[NDIM], /** * Optionally load radiation model parameters */ -static double model_kappa = 0; +static double model_kappa = 3.5; static double powerlaw_gamma_cut = 1e10; static double powerlaw_gamma_min = 1e2; static double powerlaw_gamma_max = 1e5; From f93fa02a9c793d7d4c153aca1b5d2084b71e52e5 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Thu, 24 Jun 2021 13:29:04 -0500 Subject: [PATCH 50/93] Be slightly more wary of dividing by small numbers in new analytic expression --- src/ipolarray.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ipolarray.c b/src/ipolarray.c index ecd9eeb..f1b7820 100644 --- a/src/ipolarray.c +++ b/src/ipolarray.c @@ -26,7 +26,7 @@ #define CUT_SMALL_OPTICAL_DEPTH 1e-5 // Smallest double that prevents NaNs on inverses -#define CUT_PREVENT_NAN 1e-100 +#define CUT_PREVENT_NAN 1e-80 // Sub-functions void push_polar(double Xi[NDIM], double Xm[NDIM], double Xf[NDIM], From fc6ed24d4436f495a5f7b24185eddc7d22614f18 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Fri, 25 Jun 2021 16:33:36 -0500 Subject: [PATCH 51/93] Use clang (AOCC) on Illinois BH29. Don't override user/module's HDF5_DIR and GSL_DIR with blanks --- machines/bh29.make | 2 ++ makefile | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 machines/bh29.make diff --git a/machines/bh29.make b/machines/bh29.make new file mode 100644 index 0000000..99eb475 --- /dev/null +++ b/machines/bh29.make @@ -0,0 +1,2 @@ +CC=clang +CFLAGS_CUSTOM= diff --git a/makefile b/makefile index e825bf3..36729ae 100644 --- a/makefile +++ b/makefile @@ -2,9 +2,9 @@ MODEL = iharm # Top directory of HDF5, or blank if using h5pcc -HDF5_DIR = +#HDF5_DIR = # Top directory of GSL, or blank if installed to system -GSL_DIR = +#GSL_DIR = # System /lib equivalent (can be /usr/lib, /lib64, /usr/lib64) # Can leave this blank if it's included automatically by GCC SYSTEM_LIBDIR = /lib64 From 08f2755837742ad4be64ff868cd9b78f33056023 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Fri, 25 Jun 2021 16:47:52 -0500 Subject: [PATCH 52/93] Remove unnecessary flags for BH cluster --- machines/bh-cluster.make | 1 - machines/bh29.make | 6 +++++- 2 files changed, 5 insertions(+), 2 deletions(-) delete mode 100644 machines/bh-cluster.make diff --git a/machines/bh-cluster.make b/machines/bh-cluster.make deleted file mode 100644 index 72d17ca..0000000 --- a/machines/bh-cluster.make +++ /dev/null @@ -1 +0,0 @@ -CFLAGS_CUSTOM = -D_DEFAULT_SOURCE -shlib diff --git a/machines/bh29.make b/machines/bh29.make index 99eb475..b20d607 100644 --- a/machines/bh29.make +++ b/machines/bh29.make @@ -1,2 +1,6 @@ +# For AOCC. Load with ". /opt/AMD/aocc-compiler-3.0.0/setenv_AOCC.sh" CC=clang -CFLAGS_CUSTOM= + +# For oneapi (slow). Load with ". /opt/intel/oneapi/setvars.sh" +#CC=icc +#CFLAGS=-xHost -Ofast -fstrict-aliasing -Wall -Werror -ipo -qopenmp From 4441b94226f19bdcc2008a4d94ad2e55c75de18c Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Fri, 25 Jun 2021 17:17:29 -0500 Subject: [PATCH 53/93] Avoid requiring that optimizing compilers be in environment --- machines/bh-cluster.make | 4 ++++ machines/bh29.make | 7 ++----- 2 files changed, 6 insertions(+), 5 deletions(-) create mode 100644 machines/bh-cluster.make diff --git a/machines/bh-cluster.make b/machines/bh-cluster.make new file mode 100644 index 0000000..98f093a --- /dev/null +++ b/machines/bh-cluster.make @@ -0,0 +1,4 @@ +# For oneapi. Load with ". /opt/intel/oneapi/setvars.sh" +CC=/opt/intel/oneapi/compiler/2021.1.2/linux/bin/intel64/icc +CFLAGS=-xHost -Ofast -fstrict-aliasing -Wall -Werror -ipo -qopenmp +HDF5_DIR=/usr/lib/hpc/gnu7/hdf5/1.10.5 diff --git a/machines/bh29.make b/machines/bh29.make index b20d607..8a8aa7e 100644 --- a/machines/bh29.make +++ b/machines/bh29.make @@ -1,6 +1,3 @@ # For AOCC. Load with ". /opt/AMD/aocc-compiler-3.0.0/setenv_AOCC.sh" -CC=clang - -# For oneapi (slow). Load with ". /opt/intel/oneapi/setvars.sh" -#CC=icc -#CFLAGS=-xHost -Ofast -fstrict-aliasing -Wall -Werror -ipo -qopenmp +CC=/opt/AMD/aocc-compiler-3.0.0/bin/clang +CFLAGS=-std=gnu99 -O3 -march=native -mtune=native -flto -fopenmp -funroll-loops From 6f5fb5fb71b45f2d6c40d056c990871223e18dba Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 29 Jun 2021 21:02:10 -0500 Subject: [PATCH 54/93] Preliminary kappa rotativities for polarized transport, see Marszewski et al 2021 --- src/coordinates.h | 4 +- src/debug_tools.c | 16 +-- src/geometry.c | 16 +-- src/geometry.h | 16 +-- src/model_radiation.c | 4 +- src/symphony/fits.c | 54 ++++++++-- src/symphony/fits.h | 10 ++ src/symphony/kappa.h | 11 ++ src/symphony/kappa_fits.c | 222 +++++++++++++++++++++++++++++++++++++- 9 files changed, 309 insertions(+), 44 deletions(-) diff --git a/src/coordinates.h b/src/coordinates.h index 95d3b27..0bd3b05 100644 --- a/src/coordinates.h +++ b/src/coordinates.h @@ -28,10 +28,10 @@ extern double startx[NDIM], stopx[NDIM], dx[NDIM]; extern double cstartx[NDIM], cstopx[NDIM]; extern double R0, Rin, Rout, Rh; -void bl_coord(double *X, double *r, double *th); +void bl_coord(double X[NDIM], double *r, double *th); void bl_to_ks(double X[NDIM], double ucon_bl[NDIM], double ucon_ks[NDIM]); void ks_to_bl(double X[NDIM], double ucon_ks[NDIM], double ucon_bl[NDIM]); -void gcov_func(double *X, double gcov[][NDIM]); +void gcov_func(double X[NDIM], double gcov[NDIM][NDIM]); // TODO privatize these, why are they needed in models? void gcov_ks(double r, double th, double gcov[NDIM][NDIM]); void gcov_bl(double r, double th, double gcov[NDIM][NDIM]); diff --git a/src/debug_tools.c b/src/debug_tools.c index 785d1fd..eb0deab 100644 --- a/src/debug_tools.c +++ b/src/debug_tools.c @@ -4,9 +4,12 @@ * Printing and sanity checks for tetrads */ -#include "geometry.h" #include "decs.h" +#include "coordinates.h" +#include "geometry.h" +#include "model.h" + // The world needed these // Maybe not in this form void print_matrix(char *name, double g[NDIM][NDIM]) @@ -139,17 +142,6 @@ void check_N(double complex N[NDIM][NDIM], fprintf(stderr, "leave check_N\n"); } -// because we don't have proper header files -void gcov_func(double *X, double gcov[][NDIM]); -int gcon_func(double gcov[][NDIM], double gcon[][NDIM]); -void bl_coord(double *X, double *r, double *th); -double get_model_ne(double X[NDIM]); -double get_model_thetae(double X[NDIM]); -double get_model_b(double X[NDIM]); -void get_model_fourv(double X[NDIM], double Kcon[NDIM], - double Ucon[NDIM], double Ucov[NDIM], - double Bcon[NDIM], double Bcov[NDIM]); - void dump_at_X(double X[NDIM]) { // warning that this does not necessarily print contiguously! diff --git a/src/geometry.c b/src/geometry.c index 5fc6f99..5373629 100644 --- a/src/geometry.c +++ b/src/geometry.c @@ -18,12 +18,12 @@ MM 11 July 17 */ -int invert_matrix(double Am[][NDIM], double Aminv[][NDIM]); -int LU_decompose(double A[][NDIM], int permute[]); -void LU_substitution(double A[][NDIM], double B[], int permute[]); +int invert_matrix(double Am[NDIM][NDIM], double Aminv[NDIM][NDIM]); +int LU_decompose(double A[NDIM][NDIM], int permute[NDIM]); +void LU_substitution(double A[NDIM][NDIM], double B[NDIM], int permute[NDIM]); /* assumes gcov has been set first; returns sqrt{|g|} */ -double gdet_func(double gcov[][NDIM]) +double gdet_func(double gcov[NDIM][NDIM]) { int i, j; int permute[NDIM]; @@ -51,7 +51,7 @@ double gdet_func(double gcov[][NDIM]) } /* invert gcov to get gcon */ -int gcon_func(double gcov[][NDIM], double gcon[][NDIM]) +int gcon_func(double gcov[NDIM][NDIM], double gcon[NDIM][NDIM]) { int sing = invert_matrix(gcov, gcon); #if DEBUG @@ -249,7 +249,7 @@ double theta_func(double X[NDIM]) Returns (1) if a singular matrix is found, (0) otherwise. */ -int invert_matrix(double Am[][NDIM], double Aminv[][NDIM]) +int invert_matrix(double Am[NDIM][NDIM], double Aminv[NDIM][NDIM]) { int i, j; @@ -301,7 +301,7 @@ int invert_matrix(double Am[][NDIM], double Aminv[][NDIM]) Returns (1) if a singular matrix is found, (0) otherwise. */ -int LU_decompose(double A[][NDIM], int permute[]) +int LU_decompose(double A[NDIM][NDIM], int permute[NDIM]) { const double absmin = 1.e-30; /* Value used instead of 0 for singular matrices */ @@ -468,7 +468,7 @@ int LU_decompose(double A[][NDIM], int permute[]) Upon exit, B[] contains the solution x[], A[][] is left unchanged. */ -void LU_substitution(double A[][NDIM], double B[], int permute[]) +void LU_substitution(double A[NDIM][NDIM], double B[NDIM], int permute[NDIM]) { int i, j; int n = NDIM; diff --git a/src/geometry.h b/src/geometry.h index c9b61cb..e90013c 100644 --- a/src/geometry.h +++ b/src/geometry.h @@ -10,20 +10,20 @@ #include "decs.h" -int gcon_func(double gcov[][NDIM], double gcon[][NDIM]); -double gdet_func(double gcov[][NDIM]); -void get_connection(double *X, double lconn[][NDIM][NDIM]); +int gcon_func(double gcov[NDIM][NDIM], double gcon[NDIM][NDIM]); +double gdet_func(double gcov[NDIM][NDIM]); +void get_connection(double X[NDIM], double lconn[NDIM][NDIM][NDIM]); -void flip_index(double *ucon, double Gcov[NDIM][NDIM], double *ucov); +void flip_index(double ucon[NDIM], double Gcov[NDIM][NDIM], double ucov[NDIM]); // Old names aliased -inline void lower(double *ucon, double Gcov[NDIM][NDIM], double *ucov) {flip_index(ucon, Gcov, ucov);}; -inline void raise(double *ucov, double Gcon[NDIM][NDIM], double *ucon) {flip_index(ucov, Gcon, ucon);}; +inline void lower(double ucon[NDIM], double Gcov[NDIM][NDIM], double ucov[NDIM]) {flip_index(ucon, Gcov, ucov);}; +inline void raise(double ucov[NDIM], double Gcon[NDIM][NDIM], double ucon[NDIM]) {flip_index(ucov, Gcon, ucon);}; void null_normalize(double Kcon[NDIM], double fnorm); -void normalize(double *vcon, double gcov[][NDIM]); +void normalize(double vcon[NDIM], double gcov[NDIM][NDIM]); void normalize_to(double vcon[NDIM], double Gcov[NDIM][NDIM], double target); -int invert_matrix(double Am[][NDIM], double Aminv[][NDIM]); +int invert_matrix(double Am[NDIM][NDIM], double Aminv[NDIM][NDIM]); double theta_func(double X[NDIM]); int levi_civita(int i, int j, int k, int l); diff --git a/src/model_radiation.c b/src/model_radiation.c index 606e3f8..84a805d 100644 --- a/src/model_radiation.c +++ b/src/model_radiation.c @@ -322,10 +322,10 @@ void jar_calc_dist(int dist, double X[NDIM], double Kcon[NDIM], piecewise_rho_fit(Ne, nu, Thetae, B, theta, rQ, rU, rV); } else if (dist == ROT_OLD) { // Old incorrect distribution, for compatibility old_rho_fit(Ne, nu, Thetae, B, theta, rQ, rU, rV); - } else if (dist == E_DEXTER_THERMAL) { // Dexter rQ, Shcherbakov rV + } else if (dist == E_DEXTER_THERMAL || dist == E_THERMAL) { // Dexter rQ, Shcherbakov rV // TODO All-Dexter default option w/Taylor series, make this compat shcherbakov_rho_fit(Ne, nu, Thetae, B, theta, rQ, rU, rV); - } else { // TODO Fix Symphony, these are currently equal to ROT_OLD + } else { // TODO Fix Symphony thermal!!!, these are currently equal to ROT_OLD *rQ = rho_nu_fit(nu, B, Ne, theta, fit, paramsM.STOKES_Q, Thetae, powerlaw_p, gamma_min, gamma_max, gamma_cut, kappa, kappa_width); *rU = rho_nu_fit(nu, B, Ne, theta, fit, paramsM.STOKES_U, Thetae, powerlaw_p, gamma_min, gamma_max, gamma_cut, kappa, kappa_width); *rV = rho_nu_fit(nu, B, Ne, theta, fit, paramsM.STOKES_V, Thetae, powerlaw_p, gamma_min, gamma_max, gamma_cut, kappa, kappa_width); diff --git a/src/symphony/fits.c b/src/symphony/fits.c index 9a60ba2..2de3562 100644 --- a/src/symphony/fits.c +++ b/src/symphony/fits.c @@ -1,7 +1,7 @@ #include "fits.h" -#include #include +#include /*Wrappers for the fitting formulae*/ @@ -215,20 +215,53 @@ double rho_nu_fit(double nu, else if(params.polarization == params.STOKES_U) return 0.; else if(params.polarization == params.STOKES_V) return maxwell_juettner_rho_V(¶ms); } + + if(params.distribution == params.KAPPA_DIST) + { + if(params.nu/(2.8e6 * params.magnetic_field) < 100 || + params.nu/(pow(params.kappa_width * params.kappa, 2.) * (2.8e6 * params.magnetic_field) * sin(params.observer_angle)) < 0.1) + { + printf("\n WARNING: nu and/or X_kappa low; rho kappa fits may be inaccurate \n"); + } + if (params.polarization == params.STOKES_Q) + { + if (params.kappa < 3.5) + return kappa35_rho_Q(¶ms); + else if (params.kappa >= 3.5 && params.kappa < 4.0) + return ((4.0 - params.kappa) * kappa35_rho_Q(¶ms) + (params.kappa - 3.5) * kappa4_rho_Q(¶ms)) / 0.5; + else if (params.kappa >= 4.0 && params.kappa < 4.5) + return ((4.5 - params.kappa) * kappa4_rho_Q(¶ms) + (params.kappa - 4.0) * kappa45_rho_Q(¶ms)) / 0.5; + else if (params.kappa >= 4.5 && params.kappa < 5.0) + return ((5.0 - params.kappa) * kappa45_rho_Q(¶ms) + (params.kappa - 4.5) * kappa5_rho_Q(¶ms)) / 0.5; + else if (params.kappa >= 5.0 && params.kappa <= 10.0) + return ((10.0 - params.kappa) * kappa5_rho_Q(¶ms) + (params.kappa - 5.0) * maxwell_juettner_rho_Q(¶ms)) / 5.0; + else if (params.kappa > 10.0) + return maxwell_juettner_rho_Q(¶ms); + } + else if(params.polarization == params.STOKES_U) return 0.; + else if(params.polarization == params.STOKES_V) + { + if(params.kappa < 3.5) + return kappa35_rho_V(¶ms); + else if(params.kappa >= 3.5 && params.kappa < 4.0) + return ((4.0 - params.kappa) * kappa35_rho_V(¶ms) + (params.kappa - 3.5) * kappa4_rho_V(¶ms)) / 0.5; + else if(params.kappa >= 4.0 && params.kappa < 4.5) + return ((4.5 - params.kappa) * kappa4_rho_V(¶ms) + (params.kappa - 4.0) * kappa45_rho_V(¶ms)) / 0.5; + else if(params.kappa >= 4.5 && params.kappa <= 5.0) + return ((5.0 - params.kappa) * kappa45_rho_V(¶ms) + (params.kappa - 4.5) * kappa5_rho_V(¶ms)) / 0.5; + else if (params.kappa >= 5.0 && params.kappa <= 10.0) + return ((10.0 - params.kappa) * kappa5_rho_V(¶ms) + (params.kappa - 5.0) * maxwell_juettner_rho_V(¶ms)) / 5.0; + else if (params.kappa > 10.0) + return maxwell_juettner_rho_V(¶ms); + } + } -/*Faraday rotation coefficients for Power-law and Kappa distributions will be added later*/ +/*Faraday rotation coefficients for Power-law distribution will be added later*/ // else if(params.distribution == params.POWER_LAW) // { // if (params.polarization == params.STOKES_Q) return power_law_rho_Q(¶ms); // else if(params.polarization == params.STOKES_U) return 0.; // else if(params.polarization == params.STOKES_V) return power_law_rho_V(¶ms); -// } -// -// else if(params.distribution == params.KAPPA_DIST) -// { -// if (params.polarization == params.STOKES_Q) return kappa_rho_Q(¶ms); -// else if(params.polarization == params.STOKES_U) return 0.; -// else if(params.polarization == params.STOKES_V) return kappa_rho_V(¶ms); // } return 0.; @@ -283,6 +316,5 @@ double check_for_errors(struct parameters * params) { printf("\n WARNING: theta out of range; fitting formula may be inaccurate \n"); } - return 0.; -} +} \ No newline at end of file diff --git a/src/symphony/fits.h b/src/symphony/fits.h index 9f26166..dc19131 100644 --- a/src/symphony/fits.h +++ b/src/symphony/fits.h @@ -37,6 +37,16 @@ double kappa_I_abs(struct parameters * params); double kappa_Q_abs(struct parameters * params); double kappa_V_abs(struct parameters * params); +/* Kappa Faraday Rotation fits */ +double kappa35_rho_Q(struct parameters * params); +double kappa4_rho_Q(struct parameters * params); +double kappa45_rho_Q(struct parameters * params); +double kappa5_rho_Q(struct parameters * params); +double kappa35_rho_V(struct parameters * params); +double kappa4_rho_V(struct parameters * params); +double kappa45_rho_V(struct parameters * params); +double kappa5_rho_V(struct parameters * params); + double check_for_errors(struct parameters * params); double j_nu_fit(double nu, double magnetic_field, diff --git a/src/symphony/kappa.h b/src/symphony/kappa.h index ebed962..e8e3cb0 100644 --- a/src/symphony/kappa.h +++ b/src/symphony/kappa.h @@ -3,6 +3,7 @@ #include "params.h" //#include "distribution_function_common_routines.h" #include "gsl/gsl_sf_hyperg.h" +#include "gsl/gsl_sf_bessel.h" double kappa_to_be_normalized(double gamma, void * paramsInput); double kappa_f(double gamma, struct parameters * params); @@ -16,4 +17,14 @@ double kappa_I_abs(struct parameters * params); double kappa_Q_abs(struct parameters * params); double kappa_V_abs(struct parameters * params); +/* Kappa Faraday Rotation fits */ +double kappa35_rho_Q(struct parameters * params); +double kappa4_rho_Q(struct parameters * params); +double kappa45_rho_Q(struct parameters * params); +double kappa5_rho_Q(struct parameters * params); +double kappa35_rho_V(struct parameters * params); +double kappa4_rho_V(struct parameters * params); +double kappa45_rho_V(struct parameters * params); +double kappa5_rho_V(struct parameters * params); + #endif /* SYMPHONY_KAPPA_H_ */ diff --git a/src/symphony/kappa_fits.c b/src/symphony/kappa_fits.c index 9358f13..2bdb2cd 100644 --- a/src/symphony/kappa_fits.c +++ b/src/symphony/kappa_fits.c @@ -1,6 +1,7 @@ #include "kappa.h" -#include "stdio.h" +#include +#include #define SMALL 1e-40 @@ -337,3 +338,222 @@ double kappa_V_abs(struct parameters * params) the sign has been corrected here.*/ return -ans * sign_bug_patch; } + +double kappa35_rho_Q(struct parameters * params) +{ + double nu_c = get_nu_c(*params); + + double nu_w = pow(params->kappa_width*params->kappa, 2.) * nu_c + * sin(params->observer_angle); + + double X_k = params->nu/nu_w; + + double prefactor = -(params->electron_density + * pow(params->electron_charge, 2.) + * pow(nu_c, 2.) + * pow(sin(params->observer_angle), 2.)) + /(params->mass_electron * params->speed_light * pow(params->nu, 3.)); + + double w_term = (17. * params->kappa_width) + - (3. * pow(params->kappa_width, .5)) + + (7. * pow(params->kappa_width, .5) * exp(-5. * params->kappa_width)); + + double f_X = 1. - exp(-pow(X_k, .84) / 30.) + - (sin(X_k / 10.) * exp(-3. * pow(X_k, .471) / 2.)); + + double ans = prefactor * f_X * w_term; + + return ans; +} + +double kappa4_rho_Q(struct parameters * params) +{ + double nu_c = get_nu_c(*params); + + double nu_w = pow(params->kappa_width*params->kappa, 2.) * nu_c + * sin(params->observer_angle); + + double X_k = params->nu/nu_w; + + double prefactor = -(params->electron_density + * pow(params->electron_charge, 2.) + * pow(nu_c, 2.) + * pow(sin(params->observer_angle), 2.)) + /(params->mass_electron * params->speed_light * pow(params->nu, 3.)); + + double w_term = ((46./3.) * params->kappa_width) + - ((5./3.) * pow(params->kappa_width, .5)) + + ((17./3.) * pow(params->kappa_width, .5) * exp(-5. * params->kappa_width)); + + double f_X = 1. - exp(-pow(X_k, .84) / 18.) + - (sin(X_k / 6.) * exp(-7. * pow(X_k, .5) / 4.)); + + double ans = prefactor * f_X * w_term; + + return ans; +} + +double kappa45_rho_Q(struct parameters * params) +{ + double nu_c = get_nu_c(*params); + + double nu_w = pow(params->kappa_width*params->kappa, 2.) * nu_c + * sin(params->observer_angle); + + double X_k = params->nu/nu_w; + + double prefactor = -(params->electron_density + * pow(params->electron_charge, 2.) + * pow(nu_c, 2.) + * pow(sin(params->observer_angle), 2.)) + /(params->mass_electron * params->speed_light * pow(params->nu, 3.)); + + double w_term = (14. * params->kappa_width) + - ((13./8.) * pow(params->kappa_width, .5)) + + ((9./2.) * pow(params->kappa_width, .5) * exp(-5. * params->kappa_width)); + + double f_X = 1. - exp(-pow(X_k, .84) / 12.) + - (sin(X_k / 4.) * exp(-2. * pow(X_k, .525))); + + double ans = prefactor * f_X * w_term; + + return ans; +} + +double kappa5_rho_Q(struct parameters * params) +{ + double nu_c = get_nu_c(*params); + + double nu_w = pow(params->kappa_width*params->kappa, 2.) * nu_c + * sin(params->observer_angle); + + double X_k = params->nu/nu_w; + + double prefactor = -(params->electron_density + * pow(params->electron_charge, 2.) + * pow(nu_c, 2.) + * pow(sin(params->observer_angle), 2.)) + /(params->mass_electron * params->speed_light * pow(params->nu, 3.)); + + double w_term = ((25./2.) * params->kappa_width) + - (pow(params->kappa_width, .5)) + + (5. * pow(params->kappa_width, .5) * exp(-5. * params->kappa_width)); + + double f_X = 1. - exp(-pow(X_k, .84) / 8.) + - (sin(3. * X_k / 8.) * exp(-9. * pow(X_k, .541) / 4.)); + + double ans = prefactor * f_X * w_term; + + return ans; +} + +double kappa35_rho_V(struct parameters * params) +{ + double nu_c = get_nu_c(*params); + + double nu_w = pow(params->kappa_width*params->kappa, 2.) * nu_c + * sin(params->observer_angle); + + double X_k = params->nu/nu_w; + + double prefactor = 2. * (params->electron_density + * pow(params->electron_charge, 2.) + * nu_c * cos(params->observer_angle)) + /(params->mass_electron * params->speed_light * pow(params->nu, 2.)); + + double bessel_term = (gsl_sf_bessel_Kn(0, 1./params->kappa_width)) / (gsl_sf_bessel_Kn(2, 1./params->kappa_width) + SMALL); + + double w_term = (pow(params->kappa_width, 2.) + (2. * params->kappa_width) + 1.) + / (((25./8.) * pow(params->kappa_width, 2.)) + (4. * params->kappa_width) + 1.); + + double g_X = 1. - .17*log(1. + (.447 * pow(X_k, -.5))); + + double ans = prefactor * bessel_term * g_X * w_term; + +// if (isnan(ans)) { +// fprintf(stderr, "\nNaN in kappa rot. X_k, prefactor, bessel_term, w_term, g_X, ans: %g %g %g %g %g %g\n", +// X_k, prefactor, bessel_term, w_term, g_X, ans); +// fprintf(stderr, "kappa, kappa_width, num, denom: %g %g %g %g", params->kappa, params->kappa_width, +// gsl_sf_bessel_Kn(0, 1./params->kappa_width), gsl_sf_bessel_Kn(2, 1./params->kappa_width)); +// } + + return ans; +} + +double kappa4_rho_V(struct parameters * params) +{ + double nu_c = get_nu_c(*params); + + double nu_w = pow(params->kappa_width*params->kappa, 2.) * nu_c + * sin(params->observer_angle); + + double X_k = params->nu/nu_w; + + double prefactor = 2. * (params->electron_density + * pow(params->electron_charge, 2.) + * nu_c * cos(params->observer_angle)) + /(params->mass_electron * params->speed_light * pow(params->nu, 2.)); + + double bessel_term = (gsl_sf_bessel_Kn(0, 1./params->kappa_width)) / (gsl_sf_bessel_Kn(2, 1./params->kappa_width) + SMALL); + + double w_term = (pow(params->kappa_width, 2.) + (54. * params->kappa_width) + 50.) + / (((30./11.) * pow(params->kappa_width, 2.)) + (134. * params->kappa_width) + 50.); + + double g_X = 1. - .17*log(1. + (.391 * pow(X_k, -.5))); + + double ans = prefactor * bessel_term * g_X * w_term; + + return ans; +} + +double kappa45_rho_V(struct parameters * params) +{ + double nu_c = get_nu_c(*params); + + double nu_w = pow(params->kappa_width*params->kappa, 2.) * nu_c + * sin(params->observer_angle); + + double X_k = params->nu/nu_w; + + double prefactor = 2. * (params->electron_density + * pow(params->electron_charge, 2.) + * nu_c * cos(params->observer_angle)) + /(params->mass_electron * params->speed_light * pow(params->nu, 2.)); + + double bessel_term = (gsl_sf_bessel_Kn(0, 1./params->kappa_width)) / (gsl_sf_bessel_Kn(2, 1./params->kappa_width) + SMALL); + + double w_term = (pow(params->kappa_width, 2.) + (43. * params->kappa_width) + 38.) + / (((7./3.) * pow(params->kappa_width, 2.)) + ((185./2.) * params->kappa_width) + 38.); + + double g_X = 1. - .17*log(1. + (.348 * pow(X_k, -.5))); + + double ans = prefactor * bessel_term * g_X * w_term; + + return ans; +} + +double kappa5_rho_V(struct parameters * params) +{ + double nu_c = get_nu_c(*params); + + double nu_w = pow(params->kappa_width*params->kappa, 2.) * nu_c + * sin(params->observer_angle); + + double X_k = params->nu/nu_w; + + double prefactor = 2. * (params->electron_density + * pow(params->electron_charge, 2.) + * nu_c * cos(params->observer_angle)) + /(params->mass_electron * params->speed_light * pow(params->nu, 2.)); + + double bessel_term = (gsl_sf_bessel_Kn(0, 1./params->kappa_width)) / (gsl_sf_bessel_Kn(2, 1./params->kappa_width) + SMALL); + + double w_term = ((params->kappa_width) + (13./14.)) + / ((2. * params->kappa_width) + (13./14.)); + + double g_X = 1. - .17*log(1. + (.313 * pow(X_k, -.5))); + + double ans = prefactor * bessel_term * g_X * w_term; + + return ans; +} From edd1aa8b003861432776b1a74c18a8def9c26a84 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Thu, 1 Jul 2021 17:12:28 -0500 Subject: [PATCH 55/93] vbump after merging master 1.4 fixes --- src/decs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/decs.h b/src/decs.h index fb9b6f6..d35951d 100644 --- a/src/decs.h +++ b/src/decs.h @@ -8,7 +8,7 @@ #include // Strings and string tools -#define VERSION_STRING "ipole-beta-1.4" +#define VERSION_STRING "ipole-beta-1.5" #define xstr(s) str(s) #define str(s) #s #define STRLEN (2048) From c9648f02717d8c67fa34ee21fee49ca2b4f9e322 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Thu, 1 Jul 2021 17:30:12 -0500 Subject: [PATCH 56/93] More NaN checks under debug, guards against Nlow==0. --- model/iharm/model.c | 29 +++++++++++++++++++++++++---- src/model_radiation.c | 5 +++++ src/symphony/kappa_fits.c | 17 +++++++++++------ 3 files changed, 41 insertions(+), 10 deletions(-) diff --git a/model/iharm/model.c b/model/iharm/model.c index 0a1a714..1d8729a 100644 --- a/model/iharm/model.c +++ b/model/iharm/model.c @@ -409,11 +409,14 @@ double get_model_thetae(double X[NDIM]) double tfac = set_tinterp_ns(X, &nA, &nB); double thetae = interp_scalar_time(X, data[nA]->thetae, data[nB]->thetae, tfac); - if (thetae < 0.) { - printf("thetae negative!\n"); + if (thetae < 0. || isnan(thetae)) { + printf("thetae negative or NaN!\n"); printf("X[] = %g %g %g %g\n", X[0], X[1], X[2], X[3]); printf("t = %e %e %e\n", data[0]->t, data[1]->t, data[2]->t); - printf("thetae = %e\n", thetae); + double thetaeA = interp_scalar(X, data[nA]->thetae); + double thetaeB = interp_scalar(X, data[nB]->thetae); + printf("thetaeA, thetaeB = ", thetaeA, thetaeB); + printf("thetae, tfac = %e %e\n", thetae, tfac); } return thetae; @@ -497,7 +500,16 @@ void init_physical_quantities(int n) double sigma_m = bsq/data[n]->p[KRHO][i][j][k]; double beta_m = data[n]->p[UU][i][j][k]*(gam-1.)/0.5/bsq; - +#if DEBUG + if(isnan(sigma_m)) { + sigma_m = 0; + fprintf(stderr, "Setting zero sigma!\n"); + } + if(isnan(beta_m)) { + beta_m = INFINITY; + fprintf(stderr, "Setting INF beta!\n"); + } +#endif if (ELECTRONS == 1) { data[n]->thetae[i][j][k] = data[n]->p[KEL][i][j][k]*pow(data[n]->p[KRHO][i][j][k],game-1.)*Thetae_unit; } else if (ELECTRONS == 2) { @@ -511,6 +523,15 @@ void init_physical_quantities(int n) data[n]->thetae[i][j][k] = Thetae_unit*data[n]->p[UU][i][j][k]/data[n]->p[KRHO][i][j][k]; } data[n]->thetae[i][j][k] = fmax(data[n]->thetae[i][j][k], 1.e-3); +#if DEBUG + if(isnan(data[n]->thetae[i][j][k])) { + data[n]->thetae[i][j][k] = 0.0; + fprintf(stderr, "\nZero Thetae! Prims %g %g %g %g %g %g %g %g\n", data[n]->p[KRHO][i][j][k], data[n]->p[UU][i][j][k], + data[n]->p[U1][i][j][k], data[n]->p[U2][i][j][k], data[n]->p[U3][i][j][k], data[n]->p[B1][i][j][k], + data[n]->p[B2][i][j][k], data[n]->p[B3][i][j][k]); + fprintf(stderr, "Setting zero temp!\n"); + } +#endif // Preserve sigma for cutting along geodesics, and for variable-kappa model data[n]->sigma[i][j][k] = sigma_m; diff --git a/src/model_radiation.c b/src/model_radiation.c index cd6506b..addd6f9 100644 --- a/src/model_radiation.c +++ b/src/model_radiation.c @@ -500,6 +500,11 @@ void get_model_kappa(double X[NDIM], double *kappa, double *kappa_width) { double Thetae = get_model_thetae(X); *kappa_width = (*kappa - 3.) / *kappa * Thetae; +#if DEBUG + if (isnan(*kappa_width) || isnan(*kappa)) { + fprintf(stderr, "NaN kappa val! kappa, kappa_width, Thetae: %g %g %g\n", *kappa, *kappa_width, Thetae); + } +#endif } diff --git a/src/symphony/kappa_fits.c b/src/symphony/kappa_fits.c index 2bdb2cd..d905397 100644 --- a/src/symphony/kappa_fits.c +++ b/src/symphony/kappa_fits.c @@ -15,8 +15,11 @@ static inline double stable_hyp2f1(double a, double b, double c, double z) { if (z > -1 && z < 1) { + //fprintf(stderr, "GSL hyperg_u: %g %g %g %g\n", a, b, c, z); return gsl_sf_hyperg_2F1(a, b, c, z); } else { + //fprintf(stderr, "GSL hyperg_p1: %g %g %g %g %g\n", a, c-b, a-b+1., 1./(1.-z), z); + //fprintf(stderr, "GSL hyperg_p2: %g %g %g %g %g\n", b, c-a, b-a+1., 1./(1.-z), z); /*GSL 2F1 only works for |z| < 1; had to apply a hypergeometric function identity because in our case z = -kappa*w, so |z| > 1 */ return pow(1.-z, -a) * tgamma(c) * tgamma(b-a) @@ -54,8 +57,10 @@ double kappa_I(struct parameters * params) double Nhigh = (1./4.) * pow(3., (params->kappa-1.)/2.) * (params->kappa-2.) * (params->kappa-1.) + * tgamma(params->kappa/4.-1./3.) * tgamma(params->kappa/4.-1./3.) - * tgamma(params->kappa/4.+4./3.); + * tgamma(params->kappa/4.-1./3.) + * tgamma(params->kappa/4.+4./3.) + SMALL; double x = 3. * pow(params->kappa, -3./2.); @@ -94,7 +99,7 @@ double kappa_Q(struct parameters * params) double Nhigh = -(pow(4./5., 2)+params->kappa/50.) * (1./4.) * pow(3., (params->kappa-1.)/2.) * (params->kappa-2.) * (params->kappa-1.) * tgamma(params->kappa/4.-1./3.) - * tgamma(params->kappa/4.+4./3.); + * tgamma(params->kappa/4.+4./3.) + SMALL; double x = (37./10.)*pow(params->kappa, -8./5.); @@ -140,7 +145,7 @@ double kappa_V(struct parameters * params) * pow(X_k, -1./2.) * (1./4.) * pow(3., (params->kappa-1.)/2.) * (params->kappa-2.) * (params->kappa-1.) * tgamma(params->kappa/4.-1./3.) - * tgamma(params->kappa/4.+4./3.); + * tgamma(params->kappa/4.+4./3.) + SMALL; double x = 3.*pow(params->kappa, -3./2.); @@ -208,7 +213,7 @@ double kappa_I_abs(struct parameters * params) / pow(params->kappa_width * params->kappa, 5.) * (2 * tgamma(2. + params->kappa/2.) / (2.+params->kappa)-1.) - * (pow(3./params->kappa, 19./4.) + 3./5.); + * (pow(3./params->kappa, 19./4.) + 3./5.) + SMALL;; double x = pow(-7./4. + 8. * params->kappa/5., -43./50.); @@ -259,7 +264,7 @@ double kappa_Q_abs(struct parameters * params) * (params->kappa-1.) * params->kappa / pow(params->kappa_width * params->kappa, 5.) * (2 * tgamma(2. + params->kappa/2.) - / (2. + params->kappa)-1.); + / (2. + params->kappa)-1.) + SMALL; double x = (7./5.) * pow(params->kappa, -23./20.); @@ -318,7 +323,7 @@ double kappa_V_abs(struct parameters * params) * (params->kappa-2.) * (params->kappa-1.) * params->kappa / pow(params->kappa_width*params->kappa, 5.) * (2 * tgamma(2. + params->kappa/2.) - / (2. + params->kappa) - 1.); + / (2. + params->kappa) - 1.) + SMALL; double x = (61./50.)*pow(params->kappa, -142./125.)+7./1000.; From 327f1611a58f192045622424cf35229af3dcbe09 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Thu, 1 Jul 2021 17:40:57 -0500 Subject: [PATCH 57/93] Banish checks on thetae to debug builds where they will have context --- model/iharm/model.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/model/iharm/model.c b/model/iharm/model.c index 1d8729a..4994847 100644 --- a/model/iharm/model.c +++ b/model/iharm/model.c @@ -409,6 +409,7 @@ double get_model_thetae(double X[NDIM]) double tfac = set_tinterp_ns(X, &nA, &nB); double thetae = interp_scalar_time(X, data[nA]->thetae, data[nB]->thetae, tfac); +#if DEBUG if (thetae < 0. || isnan(thetae)) { printf("thetae negative or NaN!\n"); printf("X[] = %g %g %g %g\n", X[0], X[1], X[2], X[3]); @@ -418,6 +419,7 @@ double get_model_thetae(double X[NDIM]) printf("thetaeA, thetaeB = ", thetaeA, thetaeB); printf("thetae, tfac = %e %e\n", thetae, tfac); } +#endif return thetae; } From 46ec5734d5d6364890f0a44f5f087e3b56b2db7c Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Fri, 2 Jul 2021 08:33:38 -0500 Subject: [PATCH 58/93] Some mitigations and much debug output related to kappa-distribution fits --- model/iharm/model.c | 2 +- src/symphony/fits.c | 117 +++++++++++++++++++++++--------- src/symphony/fits.h | 2 +- src/symphony/kappa_fits.c | 137 +++++++++++++++++++++++++++++++++++--- 4 files changed, 215 insertions(+), 43 deletions(-) diff --git a/model/iharm/model.c b/model/iharm/model.c index 4994847..5972b42 100644 --- a/model/iharm/model.c +++ b/model/iharm/model.c @@ -416,7 +416,7 @@ double get_model_thetae(double X[NDIM]) printf("t = %e %e %e\n", data[0]->t, data[1]->t, data[2]->t); double thetaeA = interp_scalar(X, data[nA]->thetae); double thetaeB = interp_scalar(X, data[nB]->thetae); - printf("thetaeA, thetaeB = ", thetaeA, thetaeB); + printf("thetaeA, thetaeB = %e %e", thetaeA, thetaeB); printf("thetae, tfac = %e %e\n", thetae, tfac); } #endif diff --git a/src/symphony/fits.c b/src/symphony/fits.c index 2de3562..14e7244 100644 --- a/src/symphony/fits.c +++ b/src/symphony/fits.c @@ -52,8 +52,9 @@ double j_nu_fit(double nu, params.kappa = kappa; params.kappa_width = kappa_width; - -// check_for_errors(¶ms); +#if DEBUG + check_for_errors(¶ms); +#endif if(params.distribution == params.MAXWELL_JUETTNER) { @@ -73,10 +74,33 @@ double j_nu_fit(double nu, else if(params.distribution == params.KAPPA_DIST) { - if (params.polarization == params.STOKES_I) return kappa_I(¶ms); - else if(params.polarization == params.STOKES_Q) return kappa_Q(¶ms); - else if(params.polarization == params.STOKES_U) return 0.; - else if(params.polarization == params.STOKES_V) return kappa_V(¶ms); + if (params.polarization == params.STOKES_I) { + if (kappa < 7.5) { + return kappa_I(¶ms); + } else if (kappa >= 7.5 && kappa < 15) { + return ((15 - params.kappa) * maxwell_juettner_I(¶ms) + (params.kappa - 7.5) * kappa_I(¶ms)) / 7.5; + } else { + return maxwell_juettner_I(¶ms); + } + } else if(params.polarization == params.STOKES_Q) { + if (kappa < 7.5) { + return kappa_Q(¶ms); + } else if (kappa >= 7.5 && kappa < 15) { + return ((15 - params.kappa) * maxwell_juettner_Q(¶ms) + (params.kappa - 7.5) * kappa_Q(¶ms)) / 7.5; + } else { + return maxwell_juettner_Q(¶ms); + } + } else if(params.polarization == params.STOKES_U) { + return 0.; + } else if(params.polarization == params.STOKES_V) { + if (kappa < 7.5) { + return kappa_V(¶ms); + } else if (kappa >= 7.5 && kappa < 15) { + return ((15 - params.kappa) * maxwell_juettner_V(¶ms) + (params.kappa - 7.5) * kappa_V(¶ms)) / 7.5; + } else { + return maxwell_juettner_V(¶ms); + } + } } return 0.; @@ -130,6 +154,9 @@ double alpha_nu_fit(double nu, params.kappa = kappa; params.kappa_width = kappa_width; +#if DEBUG + check_for_errors(¶ms); +#endif if(params.distribution == params.MAXWELL_JUETTNER) { @@ -149,10 +176,33 @@ double alpha_nu_fit(double nu, else if(params.distribution == params.KAPPA_DIST) { - if (params.polarization == params.STOKES_I) return kappa_I_abs(¶ms); - else if(params.polarization == params.STOKES_Q) return kappa_Q_abs(¶ms); - else if(params.polarization == params.STOKES_U) return 0.; - else if(params.polarization == params.STOKES_V) return kappa_V_abs(¶ms); + if (params.polarization == params.STOKES_I) { + if (kappa < 7.5) { + return kappa_I_abs(¶ms); + } else if (kappa >= 7.5 && kappa < 15) { + return ((15 - params.kappa) * maxwell_juettner_I_abs(¶ms) + (params.kappa - 7.5) * kappa_I_abs(¶ms)) / 7.5; + } else { + return maxwell_juettner_I_abs(¶ms); + } + } else if(params.polarization == params.STOKES_Q) { + if (kappa < 7.5) { + return kappa_Q_abs(¶ms); + } else if (kappa >= 7.5 && kappa < 15) { + return ((15 - params.kappa) * maxwell_juettner_Q_abs(¶ms) + (params.kappa - 7.5) * kappa_Q_abs(¶ms)) / 7.5; + } else { + return maxwell_juettner_Q_abs(¶ms); + } + } else if(params.polarization == params.STOKES_U) { + return 0.; + } else if(params.polarization == params.STOKES_V) { + if (kappa < 7.5) { + return kappa_V_abs(¶ms); + } else if (kappa >= 7.5 && kappa < 15) { + return ((15 - params.kappa) * maxwell_juettner_V_abs(¶ms) + (params.kappa - 7.5) * kappa_V_abs(¶ms)) / 7.5; + } else { + return maxwell_juettner_V_abs(¶ms); + } + } } return 0.; @@ -201,13 +251,14 @@ double rho_nu_fit(double nu, params.kappa = kappa; params.kappa_width = kappa_width; -// check_for_errors(¶ms); - +#if DEBUG + check_for_errors(¶ms); if(params.polarization == params.STOKES_I) { printf("No Faraday rotation of total intensity"); return 0.; } +#endif if(params.distribution == params.MAXWELL_JUETTNER) { @@ -218,11 +269,13 @@ double rho_nu_fit(double nu, if(params.distribution == params.KAPPA_DIST) { +#if DEBUG if(params.nu/(2.8e6 * params.magnetic_field) < 100 || params.nu/(pow(params.kappa_width * params.kappa, 2.) * (2.8e6 * params.magnetic_field) * sin(params.observer_angle)) < 0.1) { printf("\n WARNING: nu and/or X_kappa low; rho kappa fits may be inaccurate \n"); } +#endif if (params.polarization == params.STOKES_Q) { if (params.kappa < 3.5) @@ -276,7 +329,7 @@ double rho_nu_fit(double nu, *@returns: prints error messages or quits if disallowed values * (such as magnitude of magnetic field < 0) are entered. */ -double check_for_errors(struct parameters * params) +void check_for_errors(struct parameters * params) { double nu_c = get_nu_c(*params); @@ -298,23 +351,27 @@ double check_for_errors(struct parameters * params) printf("\n ERROR: cannot have negative electron number density \n"); exit(0); } - if(params->kappa < 2.5 || params->kappa > 7.5) - { - printf("\n WARNING: kappa out of range of fitting formula \n"); - } - if(params->kappa_width < 3 || params->kappa_width > 200) - { - printf("\n WARNING: w out of range; fitting formula may be inaccurate\n"); - } - if(params->gamma_min < 1) - { - printf("\n ERROR: gamma_min < 1\n"); - exit(0); + if(params->distribution == params->KAPPA_DIST) { + if(params->kappa < 2.5 || params->kappa > 7.5) + { + printf("\n WARNING: kappa out of range of fitting formula \n"); + } + //if(params->kappa_width < 3 || + if (params->kappa_width > 200) + { + printf("\n WARNING: w out of range; fitting formula may be inaccurate\n"); + } } - if(params->observer_angle < 5.*(params->pi)/180. - || params->observer_angle == 90.*(params->pi)/180.) - { - printf("\n WARNING: theta out of range; fitting formula may be inaccurate \n"); + if(params->distribution == params->POWER_LAW) { + if(params->gamma_min < 1) + { + printf("\n ERROR: gamma_min < 1\n"); + exit(0); + } } - return 0.; + // if(params->observer_angle < 5.*(params->pi)/180. + // || params->observer_angle == 90.*(params->pi)/180.) + // { + // printf("\n WARNING: theta out of range; fitting formula may be inaccurate \n"); + // } } \ No newline at end of file diff --git a/src/symphony/fits.h b/src/symphony/fits.h index dc19131..8cb4df8 100644 --- a/src/symphony/fits.h +++ b/src/symphony/fits.h @@ -47,7 +47,7 @@ double kappa4_rho_V(struct parameters * params); double kappa45_rho_V(struct parameters * params); double kappa5_rho_V(struct parameters * params); -double check_for_errors(struct parameters * params); +void check_for_errors(struct parameters * params); double j_nu_fit(double nu, double magnetic_field, double electron_density, diff --git a/src/symphony/kappa_fits.c b/src/symphony/kappa_fits.c index d905397..86f4de9 100644 --- a/src/symphony/kappa_fits.c +++ b/src/symphony/kappa_fits.c @@ -46,6 +46,7 @@ double kappa_I(struct parameters * params) *sin(params->observer_angle); double X_k = params->nu/nu_w; + if (isinf(X_k)) { return 0; } double prefactor = (params->electron_density * pow(params->electron_charge, 2.) * nu_c @@ -68,6 +69,13 @@ double kappa_I(struct parameters * params) * pow(1.+pow(X_k, x * (3. * params->kappa-4.)/6.) * pow(Nlow/Nhigh, x), -1./x); +#if DEBUG + if (isnan(ans)) { + fprintf(stderr, "NaN in kappa. X_k, prefactor, Nlow, Nhigh, x, ans: %g %g %g %g %g %g\n", + X_k, prefactor, Nlow, Nhigh, x, ans); + } +#endif + return ans; } @@ -87,6 +95,7 @@ double kappa_Q(struct parameters * params) * sin(params->observer_angle); double X_k = params->nu/nu_w; + if (isinf(X_k)) { return 0; } double prefactor = (params->electron_density * pow(params->electron_charge, 2.) @@ -107,6 +116,13 @@ double kappa_Q(struct parameters * params) * pow(1. + pow(X_k, x * (3. * params->kappa-4.)/6.) * pow(Nlow/Nhigh, x), -1./x); +#if DEBUG + if (isnan(ans)) { + fprintf(stderr, "NaN in kappa. X_k, prefactor, Nlow, Nhigh, x, ans: %g %g %g %g %g %g\n", + X_k, prefactor, Nlow, Nhigh, x, ans); + } +#endif + return ans; } @@ -126,6 +142,7 @@ double kappa_V(struct parameters * params) * nu_c * sin(params->observer_angle); double X_k = params->nu/nu_w; + if (isinf(X_k)) { return 0; } double prefactor = (params->electron_density * pow(params->electron_charge, 2.) @@ -165,10 +182,12 @@ double kappa_V(struct parameters * params) does not follow the convention the papers describe (IEEE/IAU); the sign has been corrected here.*/ -// if (isnan(ans) || isnan(sign_bug_patch)) { -// fprintf(stderr, "NaN in kappa. X_k, prefactor, Nlow, Nhigh, x, ans, sign: %g %g %g %g %g %g %g", -// X_k, prefactor, Nlow, Nhigh, x, ans, sign_bug_patch); -// } +#if DEBUG + if (isnan(ans) || isnan(sign_bug_patch)) { + fprintf(stderr, "NaN in kappa. X_k, prefactor, Nlow, Nhigh, x, ans, sign: %g %g %g %g %g %g %g\n", + X_k, prefactor, Nlow, Nhigh, x, ans, sign_bug_patch); + } +#endif return -ans * sign_bug_patch; @@ -189,6 +208,7 @@ double kappa_I_abs(struct parameters * params) * nu_c * sin(params->observer_angle); double X_k = params->nu/nu_w; + if (isinf(X_k)) { return 0; } double prefactor = params->electron_density * params->electron_charge / (params->magnetic_field * sin(params->observer_angle)); @@ -202,6 +222,7 @@ double kappa_I_abs(struct parameters * params) double z = -params->kappa*params->kappa_width; double hyp2f1 = stable_hyp2f1(a, b, c, z); + if (fabs(hyp2f1) < 1e-200) { return 0; } double Nlow = pow(3., 1./6.) * (10./41.) * pow(2. * params->pi, 2.) / pow(params->kappa_width * params->kappa, 16./3.-params->kappa) @@ -221,6 +242,13 @@ double kappa_I_abs(struct parameters * params) * pow(1. + pow(X_k, x * (3. * params->kappa-1.)/6.) * pow(Nlow/Nhigh, x), -1./x); +#if DEBUG + if (isnan(ans)) { + fprintf(stderr, "NaN in kappa. X_k, prefactor: %g %g\na, b, c, z, hyp2f1: %g %g %g %g %g\nNlow, Nhigh, x, ans: %g %g %g %g\n", + X_k, prefactor, a, b, c, z, hyp2f1, Nlow, Nhigh, x, ans); + } +#endif + return ans; } @@ -239,6 +267,7 @@ double kappa_Q_abs(struct parameters * params) * nu_c * sin(params->observer_angle); double X_k = params->nu/nu_w; + if (isinf(X_k)) { return 0; } double prefactor = params->electron_density * params->electron_charge / (params->magnetic_field * sin(params->observer_angle)); @@ -252,6 +281,7 @@ double kappa_Q_abs(struct parameters * params) double z = -params->kappa * params->kappa_width; double hyp2f1 = stable_hyp2f1(a, b, c, z); + if (fabs(hyp2f1) < 1e-200) { return 0; } double Nlow = -(25./48.) * pow(3., 1./6.) * (10./41.) * pow(2. * params->pi, 2.) @@ -272,6 +302,13 @@ double kappa_Q_abs(struct parameters * params) * pow(1. + pow(X_k, x * (3. * params->kappa-1.) / 6.) * pow(Nlow/Nhigh, x), -1./x); +#if DEBUG + if (isnan(ans)) { + fprintf(stderr, "NaN in kappa. X_k, prefactor: %g %g\na, b, c, z, hyp2f1: %g %g %g %g %g\nNlow, Nhigh, x, ans: %g %g %g %g\n", + X_k, prefactor, a, b, c, z, hyp2f1, Nlow, Nhigh, x, ans); + } +#endif + return ans; } @@ -290,6 +327,7 @@ double kappa_V_abs(struct parameters * params) * nu_c * sin(params->observer_angle); double X_k = params->nu/nu_w; + if (isinf(X_k)) { return 0; } double prefactor = params->electron_density * params->electron_charge / (params->magnetic_field * sin(params->observer_angle)); @@ -303,6 +341,7 @@ double kappa_V_abs(struct parameters * params) double z = -params->kappa * params->kappa_width; double hyp2f1 = stable_hyp2f1(a, b, c, z); + if (fabs(hyp2f1) < 1e-200) { return 0; } double Nlow = -(77./(100. * params->kappa_width)) * pow(pow(sin(params->observer_angle), -114./50.) @@ -337,6 +376,13 @@ double kappa_V_abs(struct parameters * params) double sign_bug_patch = cos(params->observer_angle) / fabs(cos(params->observer_angle)); +#if DEBUG + if (isnan(ans) || isnan(sign_bug_patch)) { + fprintf(stderr, "NaN in kappa. X_k, prefactor: %g %g\na, b, c, z, hyp2f1: %g %g %g %g %g\nNlow, Nhigh, x, ans: %g %g %g %g\n", + X_k, prefactor, a, b, c, z, hyp2f1, Nlow, Nhigh, x, ans); + } +#endif + /*NOTE: Sign corrected; the sign in Leung et al. (2011) and Pandya et al. (2016) for Stokes V transfer coefficients does not follow the convention the papers describe (IEEE/IAU); @@ -352,6 +398,7 @@ double kappa35_rho_Q(struct parameters * params) * sin(params->observer_angle); double X_k = params->nu/nu_w; + if (isinf(X_k)) { return 0; } double prefactor = -(params->electron_density * pow(params->electron_charge, 2.) @@ -368,6 +415,14 @@ double kappa35_rho_Q(struct parameters * params) double ans = prefactor * f_X * w_term; +#if DEBUG + if (isnan(ans)) { + fprintf(stderr, "\nNaN in kappa rot. X_k, prefactor, w_term, f_X, ans: %g %g %g %g %g\n", + X_k, prefactor, w_term, f_X, ans); + fprintf(stderr, "kappa, kappa_width: %g %g\n", params->kappa, params->kappa_width); + } +#endif + return ans; } @@ -379,6 +434,7 @@ double kappa4_rho_Q(struct parameters * params) * sin(params->observer_angle); double X_k = params->nu/nu_w; + if (isinf(X_k)) { return 0; } double prefactor = -(params->electron_density * pow(params->electron_charge, 2.) @@ -395,6 +451,14 @@ double kappa4_rho_Q(struct parameters * params) double ans = prefactor * f_X * w_term; +#if DEBUG + if (isnan(ans)) { + fprintf(stderr, "\nNaN in kappa rot. X_k, prefactor, w_term, f_X, ans: %g %g %g %g %g\n", + X_k, prefactor, w_term, f_X, ans); + fprintf(stderr, "kappa, kappa_width: %g %g\n", params->kappa, params->kappa_width); + } +#endif + return ans; } @@ -406,6 +470,7 @@ double kappa45_rho_Q(struct parameters * params) * sin(params->observer_angle); double X_k = params->nu/nu_w; + if (isinf(X_k)) { return 0; } double prefactor = -(params->electron_density * pow(params->electron_charge, 2.) @@ -422,6 +487,14 @@ double kappa45_rho_Q(struct parameters * params) double ans = prefactor * f_X * w_term; +#if DEBUG + if (isnan(ans)) { + fprintf(stderr, "\nNaN in kappa rot. X_k, prefactor, w_term, f_X, ans: %g %g %g %g %g\n", + X_k, prefactor, w_term, f_X, ans); + fprintf(stderr, "kappa, kappa_width: %g %g\n", params->kappa, params->kappa_width); + } +#endif + return ans; } @@ -433,6 +506,7 @@ double kappa5_rho_Q(struct parameters * params) * sin(params->observer_angle); double X_k = params->nu/nu_w; + if (isinf(X_k)) { return 0; } double prefactor = -(params->electron_density * pow(params->electron_charge, 2.) @@ -440,7 +514,7 @@ double kappa5_rho_Q(struct parameters * params) * pow(sin(params->observer_angle), 2.)) /(params->mass_electron * params->speed_light * pow(params->nu, 3.)); - double w_term = ((25./2.) * params->kappa_width) + double w_term = ((25./2.) * params->kappa_width) - (pow(params->kappa_width, .5)) + (5. * pow(params->kappa_width, .5) * exp(-5. * params->kappa_width)); @@ -449,6 +523,14 @@ double kappa5_rho_Q(struct parameters * params) double ans = prefactor * f_X * w_term; +#if DEBUG + if (isnan(ans)) { + fprintf(stderr, "\nNaN in kappa rot. X_k, prefactor, w_term, f_X, ans: %g %g %g %g %g\n", + X_k, prefactor, w_term, f_X, ans); + fprintf(stderr, "kappa, kappa_width: %g %g\n", params->kappa, params->kappa_width); + } +#endif + return ans; } @@ -460,6 +542,7 @@ double kappa35_rho_V(struct parameters * params) * sin(params->observer_angle); double X_k = params->nu/nu_w; + if (isinf(X_k)) { return 0; } double prefactor = 2. * (params->electron_density * pow(params->electron_charge, 2.) @@ -475,12 +558,14 @@ double kappa35_rho_V(struct parameters * params) double ans = prefactor * bessel_term * g_X * w_term; -// if (isnan(ans)) { -// fprintf(stderr, "\nNaN in kappa rot. X_k, prefactor, bessel_term, w_term, g_X, ans: %g %g %g %g %g %g\n", -// X_k, prefactor, bessel_term, w_term, g_X, ans); -// fprintf(stderr, "kappa, kappa_width, num, denom: %g %g %g %g", params->kappa, params->kappa_width, -// gsl_sf_bessel_Kn(0, 1./params->kappa_width), gsl_sf_bessel_Kn(2, 1./params->kappa_width)); -// } +#if DEBUG + if (isnan(ans)) { + fprintf(stderr, "\nNaN in kappa rot. X_k, prefactor, bessel_term, w_term, g_X, ans: %g %g %g %g %g %g\n", + X_k, prefactor, bessel_term, w_term, g_X, ans); + fprintf(stderr, "kappa, kappa_width, num, denom: %g %g %g %g", params->kappa, params->kappa_width, + gsl_sf_bessel_Kn(0, 1./params->kappa_width), gsl_sf_bessel_Kn(2, 1./params->kappa_width) + SMALL); + } +#endif return ans; } @@ -493,6 +578,7 @@ double kappa4_rho_V(struct parameters * params) * sin(params->observer_angle); double X_k = params->nu/nu_w; + if (isinf(X_k)) { return 0; } double prefactor = 2. * (params->electron_density * pow(params->electron_charge, 2.) @@ -508,6 +594,15 @@ double kappa4_rho_V(struct parameters * params) double ans = prefactor * bessel_term * g_X * w_term; +#if DEBUG + if (isnan(ans)) { + fprintf(stderr, "\nNaN in kappa rot. X_k, prefactor, bessel_term, w_term, g_X, ans: %g %g %g %g %g %g\n", + X_k, prefactor, bessel_term, w_term, g_X, ans); + fprintf(stderr, "kappa, kappa_width, num, denom: %g %g %g %g", params->kappa, params->kappa_width, + gsl_sf_bessel_Kn(0, 1./params->kappa_width), gsl_sf_bessel_Kn(2, 1./params->kappa_width) + SMALL); + } +#endif + return ans; } @@ -519,6 +614,7 @@ double kappa45_rho_V(struct parameters * params) * sin(params->observer_angle); double X_k = params->nu/nu_w; + if (isinf(X_k)) { return 0; } double prefactor = 2. * (params->electron_density * pow(params->electron_charge, 2.) @@ -534,6 +630,15 @@ double kappa45_rho_V(struct parameters * params) double ans = prefactor * bessel_term * g_X * w_term; +#if DEBUG + if (isnan(ans)) { + fprintf(stderr, "\nNaN in kappa rot. X_k, prefactor, bessel_term, w_term, g_X, ans: %g %g %g %g %g %g\n", + X_k, prefactor, bessel_term, w_term, g_X, ans); + fprintf(stderr, "kappa, kappa_width, num, denom: %g %g %g %g", params->kappa, params->kappa_width, + gsl_sf_bessel_Kn(0, 1./params->kappa_width), gsl_sf_bessel_Kn(2, 1./params->kappa_width) + SMALL); + } +#endif + return ans; } @@ -545,6 +650,7 @@ double kappa5_rho_V(struct parameters * params) * sin(params->observer_angle); double X_k = params->nu/nu_w; + if (isinf(X_k)) { return 0; } double prefactor = 2. * (params->electron_density * pow(params->electron_charge, 2.) @@ -560,5 +666,14 @@ double kappa5_rho_V(struct parameters * params) double ans = prefactor * bessel_term * g_X * w_term; +#if DEBUG + if (isnan(ans)) { + fprintf(stderr, "\nNaN in kappa rot. X_k, prefactor, bessel_term, w_term, g_X, ans: %g %g %g %g %g %g\n", + X_k, prefactor, bessel_term, w_term, g_X, ans); + fprintf(stderr, "kappa, kappa_width, num, denom: %g %g %g %g", params->kappa, params->kappa_width, + gsl_sf_bessel_Kn(0, 1./params->kappa_width), gsl_sf_bessel_Kn(2, 1./params->kappa_width) + SMALL); + } +#endif + return ans; } From 0ee968ce6b990ac402ed9a0d61414f5f831427c7 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Fri, 2 Jul 2021 12:36:59 -0500 Subject: [PATCH 59/93] Enforce max polarized emission properly --- src/model_radiation.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/model_radiation.c b/src/model_radiation.c index 7637bef..1745cbe 100644 --- a/src/model_radiation.c +++ b/src/model_radiation.c @@ -250,9 +250,9 @@ void jar_calc_dist(int dist, double X[NDIM], double Kcon[NDIM], fprintf(stderr, "Negative total emissivity! Exiting!\n"); exit(-1); } - if (*jI * *jI < *jQ * *jQ + *jU * *jU + *jV * *jV) { + double jP = sqrt(*jQ * *jQ + *jU * *jU + *jV * *jV); + if (*jI < jP/max_pol_frac_e) { // Transport does not like 100% polarization... - double jP = sqrt(*jQ * *jQ + *jU * *jU + *jV * *jV); double pol_frac_e = *jI / jP * max_pol_frac_e; *jQ *= pol_frac_e; *jU *= pol_frac_e; @@ -289,9 +289,9 @@ void jar_calc_dist(int dist, double X[NDIM], double Kcon[NDIM], fprintf(stderr, "Negative total absorptivity! Exiting!\n"); exit(-1); } - if (*aI * *aI < *aQ * *aQ + *aU * *aU + *aV * *aV) { + double aP = sqrt(*aQ * *aQ + *aU * *aU + *aV * *aV); + if (*aI < aP/max_pol_frac_a) { // Transport does not like 100% polarization... - double aP = sqrt(*aQ * *aQ + *aU * *aU + *aV * *aV); double pol_frac_a = *aI / aP * max_pol_frac_a; *aQ *= pol_frac_a; *aU *= pol_frac_a; From 9540848d09642516c24e5a442521fc2282aaf354 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Mon, 5 Jul 2021 20:58:02 -0500 Subject: [PATCH 60/93] Why is it every time I say it won't be a dumb bug, it's a dumb bug? I'm not compaining, just oraculously wrong. --- src/ipolarray.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ipolarray.c b/src/ipolarray.c index f1b7820..b00961d 100644 --- a/src/ipolarray.c +++ b/src/ipolarray.c @@ -439,7 +439,7 @@ int evolve_N(double Xi[NDIM], double Kconi[NDIM], x = dlam * 0.5; rho2 = rQ * rQ + rU * rU + rV * rV; if (rho2 > CUT_PREVENT_NAN) { - double rdS = rQ * SQ0 + rU * SU0 + rV * SV0; + double rdS = rQ * SQ2 + rU * SU2 + rV * SV2; double rho = sqrt(rho2); double c = cos(rho * x); double s = sin(rho * x); From 056d42ac8a1204be85be5247a70d57792204e400 Mon Sep 17 00:00:00 2001 From: George Wong Date: Sat, 10 Jul 2021 20:27:20 -0500 Subject: [PATCH 61/93] modify the way iharm model loader deals with mdot and add proper EKS coordinate functionality --- model/iharm/model.c | 89 ++++++++++++++++++++++++++++++++++++++++++++- src/coordinates.c | 5 +++ src/coordinates.h | 3 ++ src/debug_tools.c | 38 +++++++++++++++++++ src/debug_tools.h | 2 + 5 files changed, 135 insertions(+), 2 deletions(-) diff --git a/model/iharm/model.c b/model/iharm/model.c index ab79b82..4fdbf19 100644 --- a/model/iharm/model.c +++ b/model/iharm/model.c @@ -2,6 +2,7 @@ #include "decs.h" #include "hdf5_utils.h" +#include "debug_tools.h" #include "coordinates.h" #include "geometry.h" @@ -276,7 +277,8 @@ void init_model(double *tA, double *tB) // read fluid data fprintf(stderr, "Reading data...\n"); - load_data(0, fnam, dumpidx, dumpmin); + load_data(0, fnam, dumpidx, 2); + // replaced dumpmin -> 2 because apparently that argument was just .. removed dumpidx += dumpskip; #if SLOW_LIGHT update_data(tA, tB); @@ -766,6 +768,9 @@ void init_iharm_grid(char *fnam, int dumpidx) use_eKS_internal = 1; metric = METRIC_MKS3; cstopx[2] = 1.0; + } else if ( strncmp(metric_name, "EKS", 19) == 0 ) { + metric = METRIC_EKS; + cstopx[2] = M_PI; } else { fprintf(stderr, "File is in unknown metric %s. Cannot continue.\n", metric_name); exit(-1); @@ -856,6 +861,10 @@ void init_iharm_grid(char *fnam, int dumpidx) fprintf(stderr, "Using logarithmic KS coordinates internally\n"); fprintf(stderr, "Converting from KORAL-style Modified Kerr-Schild coordinates MKS3\n"); break; + case METRIC_EKS: + hdf5_set_directory("/header/geom/eks/"); + fprintf(stderr, "Using Kerr-Schild coordinates with exponential radial coordiante\n"); + break; } if ( metric == METRIC_MKS3 ) { @@ -866,6 +875,12 @@ void init_iharm_grid(char *fnam, int dumpidx) hdf5_read_single_val(&mks3MY2, "MY2", H5T_IEEE_F64LE); hdf5_read_single_val(&mks3MP0, "MP0", H5T_IEEE_F64LE); Rout = 100.; + } else if ( metric == METRIC_EKS ) { + hdf5_read_single_val(&a, "a", H5T_IEEE_F64LE); + hdf5_read_single_val(&Rin, "r_in", H5T_IEEE_F64LE); + hdf5_read_single_val(&Rout, "r_out", H5T_IEEE_F64LE); + fprintf(stderr, "eKS parameters a: %f Rin: %f Rout: %f\n", a, Rin, Rout); + } else { // Some brand of MKS. All have the same parameters hdf5_read_single_val(&a, "a", H5T_IEEE_F64LE); hdf5_read_single_val(&hslope, "hslope", H5T_IEEE_F64LE); @@ -994,7 +1009,7 @@ void populate_boundary_conditions(int n) for (int i=0; ip[l][i][0][k] = data[n]->p[l][i][1][kflip]; data[n]->p[l][i][N2+1][k] = data[n]->p[l][i][N2][kflip]; @@ -1275,6 +1290,49 @@ void load_hamr_data(int n, char *fnam, int dumpidx, int verbose) init_physical_quantities(n); } +// get dMact in the i'th radial zone (0 = 0 of the dump, so ignore ghost zones) +double get_code_dMact(int i, int n) +{ + i += 1; + double dMact = 0; +#pragma omp parallel for collapse(1) reduction(+:dMact) + for (int j=1; jp[U1+l-1][i][j][k]*data[n]->p[U1+m-1][i][j][k]; + double ufac = sqrt(-1./gcon[0][0]*(1 + fabs(UdotU))); + + double ucon[NDIM] = { 0. }; + ucon[0] = -ufac * gcon[0][0]; + + for(int l = 1; l < NDIM; l++) + ucon[l] = data[n]->p[U1+l-1][i][j][k] - ufac*gcon[0][l]; + + double ucov[NDIM] = { 0. }; + flip_index(ucon, gcov, ucov); + + dMact += g * data[n]->p[KRHO][i][j][k] * ucon[1]; + } + + } + return dMact; +} + void load_iharm_data(int n, char *fnam, int dumpidx, int verbose) { // loads relevant information from fluid dump file stored at fname @@ -1406,6 +1464,33 @@ void load_iharm_data(int n, char *fnam, int dumpidx, int verbose) } } + // check if 21st zone (i.e., 20th without ghost zones) is beyond r_eh + // otherwise recompute + if (1==1) { + double r_eh = 1. + sqrt(1. - a*a); + int N2_by_2 = (int)(N2/2); + + double X[NDIM] = { 0. }; + ijktoX(20, N2_by_2, 0, X); + + double r, th; + bl_coord(X, &r, &th); + + fprintf(stderr, "%g %g\n", r, r_eh); + + if (r < r_eh) { + for (int i=1; i= r_eh) { + fprintf(stderr, "r_eh is beyond regular zones. recomputing at %g...\n", r); + dMact = get_code_dMact(i, n) * 21; + break; + } + } + } + } + // now copy primitives and four-vectors according to boundary conditions populate_boundary_conditions(n); diff --git a/src/coordinates.c b/src/coordinates.c index 194196f..b716e55 100644 --- a/src/coordinates.c +++ b/src/coordinates.c @@ -36,6 +36,9 @@ void bl_coord(double X[NDIM], double *r, double *th) } else { double y, thG, thJ; switch (metric) { + case METRIC_EKS: + *th = X[2]; + break; case METRIC_MKS: *th = M_PI * X[2] + ((1. - hslope) / 2.) * sin(2. * M_PI * X[2]); break; @@ -201,6 +204,8 @@ void set_dxdX(double X[NDIM], double dxdX[NDIM][NDIM]) dxdX[2][2] = M_PI; } else { switch (metric) { + case METRIC_EKS: + break; case METRIC_MKS: dxdX[2][2] = M_PI + (1 - hslope) * M_PI * cos(2. * M_PI * X[2]); break; diff --git a/src/coordinates.h b/src/coordinates.h index 1b9a0ff..24411df 100644 --- a/src/coordinates.h +++ b/src/coordinates.h @@ -15,6 +15,9 @@ #define METRIC_MKS3 3 // Spherical coordinates in Minkowski space #define METRIC_MINKOWSKI 4 +// eKS exponential radial coordinate; KS otherwise. note not the same +// as eKS_internal, which has X2 in [0, 1] +#define METRIC_EKS 6 // Coordinate parameters. See extern int use_eKS_internal; diff --git a/src/debug_tools.c b/src/debug_tools.c index 923a0f1..67c05ad 100644 --- a/src/debug_tools.c +++ b/src/debug_tools.c @@ -138,3 +138,41 @@ void check_N(double complex N[NDIM][NDIM], fprintf(stderr, "leave check_N\n"); } + +// because we don't have proper header files +void gcov_func(double *X, double gcov[][NDIM]); +int gcon_func(double gcov[][NDIM], double gcon[][NDIM]); +void bl_coord(double *X, double *r, double *th); +double get_model_ne(double X[NDIM]); +double get_model_thetae(double X[NDIM]); +double get_model_b(double X[NDIM]); +void get_model_fourv(double X[NDIM], double Kcon[NDIM], + double Ucon[NDIM], double Ucov[NDIM], + double Bcon[NDIM], double Bcov[NDIM]); + +void dump_at_X(double X[NDIM]) +{ + // warning that this does not necessarily print contiguously! + double r, h; + double gcov[4][4], gcon[4][4], ucon[4], ucov[4], bcon[4], bcov[4]; + bl_coord(X, &r, &h); + gcov_func(X, gcov); + gcon_func(gcov, gcon); + get_model_fourv(X, X, ucon, ucov, bcon, bcov); + double Ne = get_model_ne(X); + double Thetae = get_model_thetae(X); + double B = get_model_b(X); + fprintf(stderr, "-----\n"); + print_vector("X", X); + fprintf(stderr, "r, h: %g, %g\n", r, h); + print_matrix("gcov", gcov); + print_matrix("gcon", gcon); + fprintf(stderr, "Ne, Thetae, B: %g, %g, %g\n", Ne, Thetae, B); + // if pure hydrogen, sigma = B*B/Ne / (4.*M_PI * CL*CL * (MP+ME)) + print_vector("ucon", ucon); + print_vector("ucov", ucov); + print_vector("bcon", bcon); + print_vector("bcov", bcov); +} + + diff --git a/src/debug_tools.h b/src/debug_tools.h index 395a545..a84a162 100644 --- a/src/debug_tools.h +++ b/src/debug_tools.h @@ -14,6 +14,8 @@ void print_matrix(char *name, double g[NDIM][NDIM]); void print_matrix_c(char *name, double complex g[NDIM][NDIM]); void print_vector(char *name, double v[NDIM]); +void dump_at_X(double X[NDIM]); + void check_ortho(double Econ[NDIM][NDIM], double Ecov[NDIM][NDIM]); void check_u(double Ucon[NDIM], double Ucov[NDIM]); From f0b9fc4ef1b04bbe42cebab43e0abddf56085040 Mon Sep 17 00:00:00 2001 From: George Wong Date: Sat, 10 Jul 2021 20:48:32 -0500 Subject: [PATCH 62/93] remove extraneous print statement --- model/iharm/model.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/model/iharm/model.c b/model/iharm/model.c index 4fdbf19..224b73c 100644 --- a/model/iharm/model.c +++ b/model/iharm/model.c @@ -1476,8 +1476,6 @@ void load_iharm_data(int n, char *fnam, int dumpidx, int verbose) double r, th; bl_coord(X, &r, &th); - fprintf(stderr, "%g %g\n", r, r_eh); - if (r < r_eh) { for (int i=1; i Date: Sat, 10 Jul 2021 21:32:29 -0500 Subject: [PATCH 63/93] add diagnostic print statements for comparison purposes. --- model/iharm/model.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/model/iharm/model.c b/model/iharm/model.c index 224b73c..fcde1bd 100644 --- a/model/iharm/model.c +++ b/model/iharm/model.c @@ -987,6 +987,19 @@ void load_data(int n, char *fnam, int dumpidx, int verbose) } else if (dumpfile_format == FORMAT_HAMR_EKS) { load_hamr_data(n, fnam, dumpidx, verbose); } + + // FIXME + if (1==1) { + double X[4]; + ijktoX(140, 50, 70, X); + dump_at_X(X); + } + + for (int k=1; kb[141][65][k]; + fprintf(stderr, "%d %g\n", k, bsq); + } } void populate_boundary_conditions(int n) From 4ea31f12ce7aca11017000a38c5c64fc8e47acf8 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 13 Jul 2021 13:28:21 -0500 Subject: [PATCH 64/93] Better debug output for increasingly frequent NaNs in transport coefficients --- src/ipolarray.c | 12 ++++++++++++ src/model_radiation.c | 7 +++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/ipolarray.c b/src/ipolarray.c index b00961d..1571147 100644 --- a/src/ipolarray.c +++ b/src/ipolarray.c @@ -492,7 +492,19 @@ int evolve_N(double Xi[NDIM], double Kconi[NDIM], // Flag if something is wrong if (*tauF > 1.e100 || *tauF < -1.e100 || isnan(*tauF)) oddflag |= 2; +#if DEBUG + if (isnan(creal(N_tetrad[0][0])) || isnan(creal(N_coord[0][0]))) { + oddflag |= 4; + fprintf(stderr, "Stokes S0: [%e %e %e %e]\n", SI0, SQ0, SU0, SV0); + fprintf(stderr, "Stokes S1: [%e %e %e %e]\n", SI0, SQ0, SU0, SV0); + fprintf(stderr, "Stokes S2: [%e %e %e %e]\n", SI0, SQ0, SU0, SV0); + fprintf(stderr, "Stokes S: [%e %e %e %e] dlam: %e\n", SI, SQ, SU, SV, dlam); + fprintf(stderr, "Coefficients: j: [%e %e %e %e] a: [%e %e %e %e] rho: [%e %e %e]\n", jI, jQ, jU, jV, aI, aQ, aU, aV, rQ, rU, rV); + MUNULOOP fprintf(stderr, "Econ[%i][%i] = %e Ncoord = %e Ntet = %e\n", mu, nu, Econ[mu][nu], creal(N_coord[mu][nu]), creal(N_tetrad[mu][nu])); + } +#else if (isnan(creal(N_tetrad[0][0])) || isnan(creal(N_coord[0][0]))) oddflag |= 4; +#endif return oddflag; } diff --git a/src/model_radiation.c b/src/model_radiation.c index 1745cbe..25b5590 100644 --- a/src/model_radiation.c +++ b/src/model_radiation.c @@ -336,11 +336,14 @@ void jar_calc_dist(int dist, double X[NDIM], double Kcon[NDIM], #if DEBUG // Spot check for NaN coefficients + if (isnan(*rQ) || *rQ > 1.e100 || *rQ < -1.e100) { + fprintf(stderr, "\nUnstable RQ! rQ = %e nu = %e Ne = %e Thetae = %e\n", *rQ, nu, Ne, Thetae); + } if (isnan(*rV) || *rV > 1.e100 || *rV < -1.e100) { - fprintf(stderr, "\nNAN RV! rV = %e nu = %e Ne = %e Thetae = %e\n", *rV, nu, Ne, Thetae); + fprintf(stderr, "\nUnstable RV! rV = %e nu = %e Ne = %e Thetae = %e\n", *rV, nu, Ne, Thetae); } if (isnan(*jV) || *jV > 1.e100 || *jV < -1.e100) { - fprintf(stderr, "\nNAN jV! jV = %e nu = %e Ne = %e Thetae = %e B = %e theta = %e\n", *jV, nu, Ne, Thetae, B, theta); + fprintf(stderr, "\nUnstable jV! jV = %e nu = %e Ne = %e Thetae = %e B = %e theta = %e\n", *jV, nu, Ne, Thetae, B, theta); } #endif } From b0eeef86209582f8bf8cdd11c5051aac3186b96f Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Mon, 19 Jul 2021 16:33:04 -0500 Subject: [PATCH 65/93] Overhaul emission coefficients Changes: 1. Change jar_calc calling convention to provide nu, theta_B rather than Kcon. Avoids recalculating the 4-vectors from primitives 2. Make get_jkinv call jar_calc behind the scenes, to make a single path for all emission/absorption coefficients 3. Change symphony calls to use parameters struct, rather than a list. Cleans up jar_calc_dist significantly Additions: 4. Check more intermediate numbers for NaN in debug builds, and fewer in release. Subtractions: 5. Currently does not include Dexter fits formerly implemented in model_radiation.c. These can be added back as a part of symphony behind the dexter_fits flag in parameters. --- model/analytic/model.c | 12 +- model/iharm/model.c | 2 +- model/ldi2/model.c | 2 +- model/riaf/model.c | 2 +- model/sphere/model.c | 2 +- model/thin_disk/model.c | 2 +- src/io.c | 9 +- src/ipolarray.c | 87 ++-- src/model.h | 3 +- src/model_radiation.c | 673 ++++++--------------------- src/model_radiation.h | 10 +- src/radiation.c | 37 +- src/symphony/fits.c | 334 +++++-------- src/symphony/fits.h | 46 +- src/symphony/maxwell_juettner_fits.c | 46 +- 15 files changed, 379 insertions(+), 888 deletions(-) diff --git a/model/analytic/model.c b/model/analytic/model.c index 2a5b905..2960a88 100644 --- a/model/analytic/model.c +++ b/model/analytic/model.c @@ -187,20 +187,20 @@ double get_model_ne(double X[NDIM]) return ( n_exp < 200 ) ? RHO_unit * exp(-n_exp) : 0; } -void get_model_jk(double X[NDIM], double Kcon[NDIM], double *jnuinv, double *knuinv) +void get_model_jk(double X[NDIM], double nu, double theta, double *jnuinv, double *knuinv) { // Emission model defined in Gold et al 2020 section 3 double n = get_model_ne(X); - double Ucon[NDIM], Ucov[NDIM], Bcon[NDIM], Bcov[NDIM]; - get_model_fourv(X, Kcon, Ucon, Ucov, Bcon, Bcov); - double nu = get_fluid_nu(Kcon, Ucov); + // double Ucon[NDIM], Ucov[NDIM], Bcon[NDIM], Bcov[NDIM]; + // double Kcon[NDIM] = {0}; // TODO interface change if we ever need a real one here + // get_model_fourv(X, Kcon, Ucon, Ucov, Bcon, Bcov); *jnuinv = fmax( n * pow(nu / freqcgs, -alpha) / pow(nu, 2), 0); *knuinv = fmax( (A * n * pow(nu / freqcgs, -(2.5 + alpha)) + 1.e-54) * nu, 0); } -void get_model_jar(double X[NDIM], double Kcon[NDIM], +void get_model_jar(double X[NDIM], double nu, double theta, double *jI, double *jQ, double *jU, double *jV, double *aI, double *aQ, double *aU, double *aV, double *rQ, double *rU, double *rV) @@ -213,7 +213,7 @@ void get_model_jar(double X[NDIM], double Kcon[NDIM], // and set the rest to zero // Of course, you can be more elaborate double j, k; - get_model_jk(X, Kcon, &j, &k); + get_model_jk(X, nu, theta, &j, &k); *jI = j; *jQ = 0; diff --git a/model/iharm/model.c b/model/iharm/model.c index 67efec5..e84f7ae 100644 --- a/model/iharm/model.c +++ b/model/iharm/model.c @@ -1424,7 +1424,7 @@ int radiating_region(double X[NDIM]) } // In case we want to mess with emissivities directly -void get_model_jar(double X[NDIM], double Kcon[NDIM], +void get_model_jar(double X[NDIM], double nu, double theta, double *jI, double *jQ, double *jU, double *jV, double *aI, double *aQ, double *aU, double *aV, double *rQ, double *rU, double *rV) {return;} diff --git a/model/ldi2/model.c b/model/ldi2/model.c index 72c8df6..6a404cb 100644 --- a/model/ldi2/model.c +++ b/model/ldi2/model.c @@ -123,7 +123,7 @@ void record_stokes_parameters(double SI, double SQ, double SU, double SV, double ++nstep; } -void get_model_jar(double X[NDIM], double Kcon[NDIM], +void get_model_jar(double X[NDIM], double nu, double theta, double *jI, double *jQ, double *jU, double *jV, double *aI, double *aQ, double *aU, double *aV, double *rQ, double *rU, double *rV) diff --git a/model/riaf/model.c b/model/riaf/model.c index ae15b78..baa5ff9 100644 --- a/model/riaf/model.c +++ b/model/riaf/model.c @@ -382,7 +382,7 @@ void update_data_until(double *tA, double *tB, double tgt) {return;} // This is only called for trace file output, and doesn't really apply to analytic models void get_model_primitives(double X[NDIM], double *p) {return;} void get_model_jk(double X[NDIM], double Kcon[NDIM], double *jnuinv, double *knuinv) {return;} -void get_model_jar(double X[NDIM], double Kcon[NDIM], +void get_model_jar(double X[NDIM], double nu, double thetae, double *jI, double *jQ, double *jU, double *jV, double *aI, double *aQ, double *aU, double *aV, double *rQ, double *rU, double *rV) {return;} diff --git a/model/sphere/model.c b/model/sphere/model.c index 1e65763..ded087f 100644 --- a/model/sphere/model.c +++ b/model/sphere/model.c @@ -215,7 +215,7 @@ int radiating_region(double X[NDIM]) } // In case we want to mess with emissivities directly -void get_model_jar(double X[NDIM], double Kcon[NDIM], +void get_model_jar(double X[NDIM], double nu, double thetae, double *jI, double *jQ, double *jU, double *jV, double *aI, double *aQ, double *aU, double *aV, double *rQ, double *rU, double *rV) {return;} diff --git a/model/thin_disk/model.c b/model/thin_disk/model.c index e4bdd7b..b391071 100644 --- a/model/thin_disk/model.c +++ b/model/thin_disk/model.c @@ -377,7 +377,7 @@ void get_model_primitives(double X[NDIM], double *p) {return;} void update_data(double *tA, double *tB) {return;} void update_data_until(double *tA, double *tB, double tgt) {return;} // In case we want to mess with emissivities directly -void get_model_jar(double X[NDIM], double Kcon[NDIM], +void get_model_jar(double X[NDIM], double nu, double thetae, double *jI, double *jQ, double *jU, double *jV, double *aI, double *aQ, double *aU, double *aV, double *rQ, double *rU, double *rV) {return;} diff --git a/src/io.c b/src/io.c index 061c2a5..7715bf1 100644 --- a/src/io.c +++ b/src/io.c @@ -352,10 +352,6 @@ void dump_var_along(int i, int j, int nstep, struct of_traj *traj, int nx, int n //variables for parallel transport double e2Mid[NDIM], e2Final[NDIM], e2Cov[NDIM]; get_model_fourv(traj[i].X, traj[i].Kcon, Ucont, Ucovt, Bcont, Bcovt); - jar_calc(traj[i].X, traj[i].Kcon, &(j_inv[i*NDIM]), &(j_inv[i*NDIM+1]), &(j_inv[i*NDIM+2]), &(j_inv[i*NDIM+3]), - &(alpha_inv[i*NDIM]), &(alpha_inv[i*NDIM+1]), &(alpha_inv[i*NDIM+2]), &(alpha_inv[i*NDIM+3]), - &(rho_inv[i*NDIM+1]), &(rho_inv[i*NDIM+2]), &(rho_inv[i*NDIM+3]), params); - get_jkinv(traj[i].X, traj[i].Kcon, &(j_unpol[i]), &(k_unpol[i]), params); b[i] = get_model_b(traj[i].X); ne[i] = get_model_ne(traj[i].X); @@ -364,6 +360,11 @@ void dump_var_along(int i, int j, int nstep, struct of_traj *traj, int nx, int n mu[i] = get_bk_angle(traj[i].X, traj[i].Kcon, Ucovt, Bcont, Bcovt); // Record trajectory + jar_calc(traj[i].X, nu[i], mu[i], &(j_inv[i*NDIM]), &(j_inv[i*NDIM+1]), &(j_inv[i*NDIM+2]), &(j_inv[i*NDIM+3]), + &(alpha_inv[i*NDIM]), &(alpha_inv[i*NDIM+1]), &(alpha_inv[i*NDIM+2]), &(alpha_inv[i*NDIM+3]), + &(rho_inv[i*NDIM+1]), &(rho_inv[i*NDIM+2]), &(rho_inv[i*NDIM+3]), params); + get_jkinv(traj[i].X, traj[i].Kcon, &(j_unpol[i]), &(k_unpol[i]), params); + dl[i] = traj[i].dl; MULOOP { X[i*NDIM+mu] = traj[i].X[mu]; diff --git a/src/ipolarray.c b/src/ipolarray.c index f067213..cb6c9dc 100644 --- a/src/ipolarray.c +++ b/src/ipolarray.c @@ -10,6 +10,7 @@ #include "ipolarray.h" +#include "debug_tools.h" #include "decs.h" #include "coordinates.h" @@ -46,17 +47,27 @@ void push_polar(double Xi[NDIM], double Xm[NDIM], double Xf[NDIM], * Return arguments of intensity, total optical depth, total Faraday depth, and complex polarized emission tensor N^alpha^beta * * Returns flag indicating at least one step either used a questionable tetrad, or produced a NaN value + * + * TODO: add stop condidion not based on nsteps, for slow light */ int integrate_emission(struct of_traj *traj, int nsteps, double *Intensity, double *Tau, double *tauF, double complex N_coord[NDIM][NDIM], Params *params) { - //fprintf(stderr, "Begin integrate emission\n"); - - // Initialize error flag + *tauF = 0.; + // Unpolarized + *Intensity = 0.; + *Tau = 0.; + double js = 0.; + double ks; + // Error flag int oddflag = 0; - // Integrate the transfer equation (& parallel transport) forwards along trajectory + // Initialize the running cached coefficients js, ks + get_jkinv(traj[nsteps].X, traj[nsteps].Kcon, &js, &ks, params); + + // Integrate the polarized & unpolarized transfer equations (& parallel transport) + // for (int nstep=nsteps; nstep > 0; --nstep) { int sflag = 0; struct of_traj ti = traj[nstep]; @@ -72,7 +83,7 @@ int integrate_emission(struct of_traj *traj, int nsteps, #if THIN_DISK if (thindisk_region(ti.X, tf.X)) { - // The thin disk problem emits nowhere but uses a boundary condition region defined by thindisk_region + // A thin disk problem emits nowhere but uses a boundary condition region defined by thindisk_region // There we just get a starting value for intensity with get_model_i get_model_i(ti.X, ti.Kcon, Intensity); @@ -102,23 +113,36 @@ int integrate_emission(struct of_traj *traj, int nsteps, #endif if (radiating_region(tf.X)) { - - int ZERO_EMISSION = 0; + // Conditions to zero just the emission, not all radiative transport + int zero_emission = 0; if (params->target_nturns >= 0 && ti.nturns != params->target_nturns) { - ZERO_EMISSION = 1; + zero_emission = 1; + } + if (params->isolate_counterjet == 1) { // Allow emission from X[2] > midplane only + if (tf.X[2] < (cstopx[2] - cstartx[2]) / 2) { + zero_emission = 1; + } + } else if (params->isolate_counterjet == 2) { // from X[2] < midplane only + if (tf.X[2] > (cstopx[2] - cstartx[2]) / 2) { + zero_emission = 1; + } } // Solve unpolarized transport - double ji, ki, jf, kf; - get_jkinv(ti.X, ti.Kcon, &ji, &ki, params); + // Cached starting coefficients + double ji = js, ki = ks; + double jf, kf; + //get_jkinv(ti.X, ti.Kcon, &ji, &ki, params); get_jkinv(tf.X, tf.Kcon, &jf, &kf, params); + // End coefficients are next starting coefficients + js = jf; + ks = kf; - if (ZERO_EMISSION) { + if (zero_emission) { *Intensity = approximate_solve(*Intensity, 0, ki, 0, kf, ti.dl, Tau); } else { *Intensity = approximate_solve(*Intensity, ji, ki, jf, kf, ti.dl, Tau); } - //fprintf(stderr, "Unpolarized transport\n"); // Solve polarized transport if (!params->only_unpolarized) { @@ -126,15 +150,13 @@ int integrate_emission(struct of_traj *traj, int nsteps, ti.Xhalf, ti.Kconhalf, tf.X, tf.Kcon, ti.dl, N_coord, tauF, - ZERO_EMISSION, params); - //fprintf(stderr, "Polarized transport\n"); + zero_emission, params); } } - // smoosh together all the flags we hit along a geodesic - oddflag |= sflag; - - // Cry immediately on bad tetrads, even if we're not debugging +#if DEBUG + // If we're debugging, print errors immediately + // Error for bad tetrads if (sflag & 1) { fprintf(stderr, "that's odd: no orthonormal tetrad found at\n"); fprintf(stderr, "nstep: %d\n", nstep); @@ -166,22 +188,17 @@ int integrate_emission(struct of_traj *traj, int nsteps, bsq, bsq_reported, udotu, udotb, kdotu, kdotb); // exit(-1); } - // Same if there was something in gcov + // Bad gcov if (sflag & 16) { fprintf(stderr, "Matrix inversion failed in tetrad check, step %d:\n", nstep); - // TODO print more stuff here } - - // TODO pull more relevant stuff back out here -#if DEBUG - // Cry on bad tauF + // Bad tauF if (sflag & 2) { printf("tauF = %e dlam = %e\n", *tauF, ti.dl); fprintf(stderr, "nstep: %d\n", nstep); exit(-1); } - - // Cry on bad N + // Bad N if (sflag & 4) { fprintf(stderr, "\nNaN in N00!\n"); fprintf(stderr, "nstep: %d\n", nstep); @@ -195,12 +212,12 @@ int integrate_emission(struct of_traj *traj, int nsteps, //MUNULOOP printf("Econ[%i][%i] = %e Ncoord = %e Ntet = %e\n", mu, nu, Econ[mu][nu], creal(N_coord[mu][nu]), creal(N_tetrad[mu][nu])); exit(-1); } +#else + // Otherwise, smoosh together all the flags we hit along a geodesic + oddflag |= sflag; #endif - //fprintf(stderr, "End Loop\n"); } - - //fprintf(stderr, "End integrate emission\n"); - // Otherwise propagate the full flag so caller can handle it + // Return the final flag so caller can print return oddflag; } @@ -285,7 +302,7 @@ int evolve_N(double Xi[NDIM], double Kconi[NDIM], double Xhalf[NDIM], double Kconhalf[NDIM], double Xf[NDIM], double Kconf[NDIM], double dlam, double complex N_coord[NDIM][NDIM], double *tauF, - int ZERO_EMISSION, Params *params) + int zero_emission, Params *params) { // TODO might be useful to split this into flat-space S->S portion and transformations to/from N double gcov[NDIM][NDIM]; @@ -309,9 +326,12 @@ int evolve_N(double Xi[NDIM], double Kconi[NDIM], get_model_fourv(Xf, Kconf, Ucon, Ucov, Bcon, Bcov); // evaluate transport coefficients - jar_calc(Xf, Kconf, &jI, &jQ, &jU, &jV, + double nu = get_fluid_nu(Kconf, Ucov); + double theta = get_bk_angle(Xf, Kconf, Ucov, Bcon, Bcov); + jar_calc(Xf, nu, theta, + &jI, &jQ, &jU, &jV, &aI, &aQ, &aU, &aV, &rQ, &rU, &rV, params); - if (ZERO_EMISSION) { + if (zero_emission) { jI = 0.; jQ = 0.; jU = 0.; @@ -508,6 +528,7 @@ int evolve_N(double Xi[NDIM], double Kconi[NDIM], // Flag if something is wrong if (*tauF > 1.e100 || *tauF < -1.e100 || isnan(*tauF)) oddflag |= 2; if (isnan(creal(N_tetrad[0][0])) || isnan(creal(N_coord[0][0]))) oddflag |= 4; + if (isnan(Ucov[0])) oddflag |= 8; return oddflag; } diff --git a/src/model.h b/src/model.h index 729ee22..0e27266 100644 --- a/src/model.h +++ b/src/model.h @@ -26,11 +26,10 @@ double get_model_beta(double X[NDIM]); double get_model_ne(double X[NDIM]); // For exotic or custom distributions -void get_model_jar(double X[NDIM], double Kcon[NDIM], +void get_model_jar(double X[NDIM], double nu, double thetae, double *jI, double *jQ, double *jU, double *jV, double *aI, double *aQ, double *aU, double *aV, double *rQ, double *rU, double *rV); -void get_model_jk(double X[NDIM], double Kcon[NDIM], double *jnuinv, double *knuinv); void get_model_fourv(double X[NDIM], double Kcon[NDIM], double Ucon[NDIM], double Ucov[NDIM], diff --git a/src/model_radiation.c b/src/model_radiation.c index c31b7ad..a6ded82 100644 --- a/src/model_radiation.c +++ b/src/model_radiation.c @@ -33,41 +33,24 @@ #define E_KAPPA 2 #define E_POWERLAW 3 #define E_DEXTER_THERMAL 4 +#define E_LEUNG 5 #define E_CUSTOM 10 // Rotation #define ROT_OLD 11 #define ROT_PIECEWISE 12 #define ROT_SHCHERBAKOV 13 -// Debugging +// Debugging/internal #define E_UNPOL 15 +#define OVERFLOW 1e100 + // Local functions for declaring different kappa/powerlaw distributions void get_model_powerlaw_vals(double X[NDIM], double *p, double *n, double *gamma_min, double *gamma_max, double *gamma_cut); void get_model_kappa(double X[NDIM], double *kappa, double *kappa_width); -// Declarations of local fitting functions, for Dexter fits and old rotativities -void dexter_j_fit_thermal(double Ne, double nu, double Thetae, double B, double theta, - double *jI, double *jQ, double *jU, double *jV); -void shcherbakov_rho_fit(double Ne, double nu, double Thetae, double B, double theta, - double *rQ, double *rU, double *rV); -void piecewise_rho_fit(double Ne, double nu, double Thetae, double B, double theta, - double *rQ, double *rU, double *rV); -void old_rho_fit(double Ne, double nu, double Thetae, double B, double theta, - double *rQ, double *rU, double *rV); - -// Thermal plasma emissivity, absorptivity and Faraday conversion and rotation -double g(double Xe); -double h(double Xe); -double Je(double Xe); -double jffunc(double Xe); -double I_I(double x); -double I_Q(double x); -double I_V(double x); -double besselk_asym(int n, double x); - /* Get coeffs from a specific distribution */ -void jar_calc_dist(int dist, double X[NDIM], double Kcon[NDIM], +void jar_calc_dist(int dist, int pol, double X[NDIM], double nu, double theta, double *jI, double *jQ, double *jU, double *jV, double *aI, double *aQ, double *aU, double *aV, double *rQ, double *rU, double *rV); @@ -101,87 +84,68 @@ void try_set_radiation_parameter(const char *word, const char *value) } /** - * Wrapper to call different distributions at different places in the simulation domain - * See jar_calc_dist for distributions - * TODO put the general emission zero criteria here + * Get polarized emission, absorption, and rotation coefficients + * + * This is a wrapper to jar_calc_dist, see implementation there + * Also checks for NaN coefficients when built with DEBUG, for quicker debugging */ -void jar_calc(double X[NDIM], double Kcon[NDIM], +void jar_calc(double X[NDIM], double nu, double theta, double *jI, double *jQ, double *jU, double *jV, double *aI, double *aQ, double *aU, double *aV, double *rQ, double *rU, double *rV, Params *params) { -#if INTEGRATOR_TEST - jar_calc_dist(E_CUSTOM, X, Kcon, jI, jQ, jU, jV, aI, aQ, aU, aV, rQ, rU, rV); -#else - if (params->emission_type == E_UNPOL) { - get_jkinv(X, Kcon, jI, aI, params); - *jQ = 0.0; *jU = 0.0; *jV = 0.0; - *aQ = 0.0; *aU = 0.0; *aV = 0.0; - *rQ = 0; *rU = 0; *rV = 0; - } else { - jar_calc_dist(params->emission_type, X, Kcon, jI, jQ, jU, jV, aI, aQ, aU, aV, rQ, rU, rV); - } -#endif + jar_calc_dist(params->emission_type, 1, X, nu, theta, jI, jQ, jU, jV, aI, aQ, aU, aV, rQ, rU, rV); - // Zero emission to isolate the jet/counterjet portion - if (params->isolate_counterjet == 1) { // Allow emission from X[2] > midplane only - if (X[2] < (cstopx[2] - cstartx[2]) / 2) { - *jI = *jQ = *jU = *jV = 0.; - } - } else if (params->isolate_counterjet == 2) { // from X[2] < midplane only - if (X[2] > (cstopx[2] - cstartx[2]) / 2) { - *jI = *jQ = *jU = *jV = 0.; - } - } + // This wrapper can be used to call jar_calc_dist differently in e.g. funnel vs jet, or + // depending on local fluid parameters, whatever +} + +/** + * Get the emission and absorption coefficients + * This is a wrapper to jar_calc_dist, see implementation there + */ +void get_jkinv(double X[NDIM], double Kcon[NDIM], double *jI, double *aI, Params *params) +{ + double jQ, jU, jV, aQ, aU, aV, rQ, rU, rV; + double Ucon[NDIM], Ucov[NDIM], Bcon[NDIM], Bcov[NDIM]; + + get_model_fourv(X, Kcon, Ucon, Ucov, Bcon, Bcov); + double nu = get_fluid_nu(Kcon, Ucov); + double theta = get_bk_angle(X, Kcon, Ucov, Bcon, Bcov); + + jar_calc_dist(params->emission_type, 0, X, nu, theta, jI, &jQ, &jU, &jV, aI, &aQ, &aU, &aV, &rQ, &rU, &rV); } /** * Get the invariant plasma emissivities, absorptivities, rotativities in tetrad frame - * This is a wrapper to the fitting functions in this file and in the emissivity/ dir + * Calls the appropriate fitting functions from the code in symphony/, + * and ensures that the results obey basic consistency * * The dist argument controls fitting functions/distribution: * 1. Thermal (Pandya+ 2016) - * 2. Kappa (Pandya+ 2016) - * 3. Power-law (Pandya+ 2016) + * 2. Kappa (Pandya+ 2016, no rotativities) + * 3. Power-law (Pandya+ 2016, rhoQ/rhoV Marszewski+ 2021) * 4. Thermal (Dexter 2016) - * Rotativity rhoQ always from Dexter 2016, rhoV from Shcherbakov 2008 (rhoU == 0) + * Thermal rhoQ taken from Dexter 2016, rhoV from Shcherbakov 2008 (rhoU == 0) * * To be implemented? * 5. Power-law (Dexter 2016) - * 6. Thermal (Revised based on Pandya+ 2016 to better match Leung 2011) * * Testing distributions: - * 10. Pass through model-determined values. Currently used for constant-coefficient testing. - * 11. Emulate original ipole, with Dexter 2016 emissivities and rotativities based on Bessel fits - * 12. Emulate the old ipole temporary fix, with Dexter 2016 emissivities and rotativities patched into the constant limit + * 10. Pass through model-determined values -- used in most analytic models + * + * */ -void jar_calc_dist(int dist, double X[NDIM], double Kcon[NDIM], +void jar_calc_dist(int dist, int pol, double X[NDIM], double nu, double theta, double *jI, double *jQ, double *jU, double *jV, double *aI, double *aQ, double *aU, double *aV, double *rQ, double *rU, double *rV) { - // Four-vectors needed for most calculations - double Ucon[NDIM], Ucov[NDIM], Bcon[NDIM], Bcov[NDIM]; - // Common parameters - double nu = 0, Ne = 0, B = 0, theta = 0; - // Other parameters are filled as needed - // Thermal: - double Thetae = 0; - // Powerlaw: - double powerlaw_p = 0, gamma_min = 0, gamma_max = 0, gamma_cut = 0; - // Kappa: - double kappa = 0, kappa_width = 0; - // Symphony parameters struct, not to be confused with ipole Params - struct parameters paramsM; int fit = 0; - - // Ignore everything if this isn't our job - if (dist == E_CUSTOM) { - get_model_jar(X, Kcon, jI, jQ, jU, jV, aI, aQ, aU, aV, rQ, rU, rV); - return; - } - // Also ignore everything if we're not in an emitting region - // (or there are actually no e- to emit) - Ne = get_model_ne(X); + // Don't emit where there are no electrons + // This was also used as shorthand in some models to cut off emission + // Please use radiating_region instead for model applicability cutoffs, + // or see integrate_emission for zeroing just jN + double Ne = get_model_ne(X); if (Ne <= 0.) { *jI = 0.0; *jQ = 0.0; *jU = 0.0; *jV = 0.0; *aI = 0.0; *aQ = 0.0; *aU = 0.0; *aV = 0.0; @@ -189,276 +153,139 @@ void jar_calc_dist(int dist, double X[NDIM], double Kcon[NDIM], return; } - // If we must do work, grab the 4-vectors... - get_model_fourv(X, Kcon, Ucon, Ucov, Bcon, Bcov); -#if DEBUG - if (isnan(Ucov[0])) { - int i = 0, j = 0, k = 0; - double del[4] = {0}; - Xtoijk(X, &i,&j,&k, del); - fprintf(stderr, "UCOV[0] (%d,%d,%d) is nan! thread = %i\n", i,j,k, omp_get_thread_num()); - print_vector("Ucon", Ucon); - print_vector("Ucov", Ucov); - fprintf(stderr, "X[] = %e %e %e %e\n", X[0],X[1],X[2],X[3]); - fprintf(stderr, "K[] = %e %e %e %e\n", Kcon[0],Kcon[1],Kcon[2],Kcon[3]); - fprintf(stderr, "Ne = %e\n", Ne); + // Call through to the model if it's responsible for this job + if (dist == E_CUSTOM) { + get_model_jar(X, nu, theta, jI, jQ, jU, jV, aI, aQ, aU, aV, rQ, rU, rV); + return; } -#endif - // ...then the parameters needed for all distributions... - theta = get_bk_angle(X, Kcon, Ucov, Bcon, Bcov); // angle between k & b - nu = get_fluid_nu(Kcon, Ucov); // freqcgs in Hz - B = get_model_b(X); // field in G + struct parameters paramsM; + setConstParams(¶msM); + // Set the parameters common to all distributions... + paramsM.magnetic_field = get_model_b(X); + paramsM.electron_density = Ne; + paramsM.nu = nu; + paramsM.observer_angle = theta; - //...and the ones for the specific distribution we'll be evaluating. + // ...and then the specific distribution and its parameters. switch (dist) { - case E_THERMAL: - setConstParams(¶msM); - fit = paramsM.MAXWELL_JUETTNER; - Thetae = get_model_thetae(X); + case E_DEXTER_THERMAL: // Dexter thermal fits (default) + paramsM.dexter_fit = 1; + case E_THERMAL: // Otherwise Pandya thermal fits + paramsM.distribution = paramsM.MAXWELL_JUETTNER; + paramsM.theta_e = get_model_thetae(X); break; - case E_KAPPA: - setConstParams(¶msM); - fit = paramsM.KAPPA_DIST; - Thetae = get_model_thetae(X); + case E_KAPPA: // Kappa fits (Pandya + Marszewski) + paramsM.distribution = paramsM.KAPPA_DIST; + paramsM.theta_e = get_model_thetae(X); + double kappa, kappa_width; get_model_kappa(X, &kappa, &kappa_width); + paramsM.kappa = kappa; + paramsM.kappa_width = kappa_width; break; - case E_POWERLAW: - setConstParams(¶msM); - fit = paramsM.POWER_LAW; + case E_POWERLAW: // Powerlaw fits (Pandya, no rotativities!) + paramsM.distribution = paramsM.POWER_LAW; // NOTE WE REPLACE Ne!! - get_model_powerlaw_vals(X, &powerlaw_p, &Ne, &gamma_min, &gamma_max, &gamma_cut); + get_model_powerlaw_vals(X, &(paramsM.power_law_p), &(paramsM.electron_density), + &(paramsM.gamma_min), &(paramsM.gamma_max), &(paramsM.gamma_cutoff)); break; - case E_DEXTER_THERMAL: - Thetae = get_model_thetae(X); } - // EMISSIVITIES - // Avoid issues directly along field lines - if (theta <= 0 || theta >= M_PI) { - *jI = 0.0; *jQ = 0.0; *jU = 0.0; *jV = 0.0; - *aI = 0.0; *aQ = 0.0; *aU = 0.0; *aV = 0.0; - } else { - // EMISSIVITIES - if (dist == E_DEXTER_THERMAL || dist > 10) { - // Use fits from Dexter when called for or emulating old behavior - dexter_j_fit_thermal(Ne, nu, Thetae, B, theta, jI, jQ, jU, jV); + // If performing unpolarized transport, calculate only what we need + if (!pol || dist == E_UNPOL) { + *jI = j_nu_fit(¶msM, paramsM.STOKES_I) / (nu * nu); + if (dist == E_THERMAL || dist == E_DEXTER_THERMAL || dist > 10){ + double Bnuinv = Bnu_inv(nu, paramsM.theta_e); // Planck function + *aI = *jI / Bnuinv; } else { - // Call into bundled Symphony code - // Symphony uses an... interesting coordinate system. Correct it. - // TODO fix Symphony, jV fit is bad - *jI = j_nu_fit(nu, B, Ne, theta, fit, paramsM.STOKES_I, Thetae, powerlaw_p, gamma_min, gamma_max, gamma_cut, kappa, kappa_width); - *jQ = -j_nu_fit(nu, B, Ne, theta, fit, paramsM.STOKES_Q, Thetae, powerlaw_p, gamma_min, gamma_max, gamma_cut, kappa, kappa_width); - *jU = -j_nu_fit(nu, B, Ne, theta, fit, paramsM.STOKES_U, Thetae, powerlaw_p, gamma_min, gamma_max, gamma_cut, kappa, kappa_width); - *jV = j_nu_fit(nu, B, Ne, theta, fit, paramsM.STOKES_V, Thetae, powerlaw_p, gamma_min, gamma_max, gamma_cut, kappa, kappa_width); - } - // Check basic relationships - if (*jI < 0) { - fprintf(stderr, "Negative total emissivity! Exiting!\n"); - exit(-1); - } - double jP = sqrt(*jQ * *jQ + *jU * *jU + *jV * *jV); - if (*jI < jP/max_pol_frac_e) { - // Transport does not like 100% polarization... - double pol_frac_e = *jI / jP * max_pol_frac_e; - *jQ *= pol_frac_e; - *jU *= pol_frac_e; - *jV *= pol_frac_e; -#if DEBUG - fprintf(stderr, "Polarized emissivities too large:\n %g vs %g, corrected by %g\n", jP, *jI, pol_frac_e); -#endif + *aI = alpha_nu_fit(¶msM, paramsM.STOKES_I) * nu; } + } else { - // Make invariant - double nusq = nu*nu; - *jI /= nusq; - *jQ /= nusq; - *jU /= nusq; - *jV /= nusq; - - // ABSORPTIVITIES - if (dist == E_THERMAL || dist == E_DEXTER_THERMAL || dist > 10) { // Thermal distributions - // Get absorptivities via Kirchoff's law - // Already invariant, guaranteed to respect aI > aP - double Bnuinv = Bnu_inv(nu, Thetae); // Planck function - *aI = *jI / Bnuinv; - *aQ = *jQ / Bnuinv; - *aU = *jU / Bnuinv; - *aV = *jV / Bnuinv; + // Avoid issues directly along field lines + if (theta <= 0 || theta >= M_PI) { + *jI = 0.0; *jQ = 0.0; *jU = 0.0; *jV = 0.0; + *aI = 0.0; *aQ = 0.0; *aU = 0.0; *aV = 0.0; + *rQ = 0.0; *rU = 0.0; + *rV = rho_nu_fit(¶msM, paramsM.STOKES_V) * nu; } else { - *aI = alpha_nu_fit(nu, B, Ne, theta, fit, paramsM.STOKES_I, Thetae, powerlaw_p, gamma_min, gamma_max, gamma_cut, kappa, kappa_width); - *aQ = -alpha_nu_fit(nu, B, Ne, theta, fit, paramsM.STOKES_Q, Thetae, powerlaw_p, gamma_min, gamma_max, gamma_cut, kappa, kappa_width); - *aU = -alpha_nu_fit(nu, B, Ne, theta, fit, paramsM.STOKES_U, Thetae, powerlaw_p, gamma_min, gamma_max, gamma_cut, kappa, kappa_width); - *aV = alpha_nu_fit(nu, B, Ne, theta, fit, paramsM.STOKES_V, Thetae, powerlaw_p, gamma_min, gamma_max, gamma_cut, kappa, kappa_width); - + // EMISSIVITIES + double nusq = nu*nu; + *jI = j_nu_fit(¶msM, paramsM.STOKES_I) / nusq; + *jQ = -j_nu_fit(¶msM, paramsM.STOKES_Q) / nusq; + *jU = -j_nu_fit(¶msM, paramsM.STOKES_U) / nusq; + *jV = j_nu_fit(¶msM, paramsM.STOKES_V) / nusq; // Check basic relationships - if (*aI < 0) { - fprintf(stderr, "Negative total absorptivity! Exiting!\n"); - exit(-1); - } - double aP = sqrt(*aQ * *aQ + *aU * *aU + *aV * *aV); - if (*aI < aP/max_pol_frac_a) { + double jP = sqrt(*jQ * *jQ + *jU * *jU + *jV * *jV); + if (*jI < jP/max_pol_frac_e) { // Transport does not like 100% polarization... - double pol_frac_a = *aI / aP * max_pol_frac_a; - *aQ *= pol_frac_a; - *aU *= pol_frac_a; - *aV *= pol_frac_a; -#if DEBUG - fprintf(stderr, "Polarized absorptivities too large:\n %g vs %g, corrected by %g\n", aP, *aI, pol_frac_a); -#endif + double pol_frac_e = *jI / jP * max_pol_frac_e; + *jQ *= pol_frac_e; + *jU *= pol_frac_e; + *jV *= pol_frac_e; } - // Make invariant - *aI *= nu; - *aQ *= nu; - *aU *= nu; - *aV *= nu; - } - } - - // ROTATIVITIES - // Fill them by defualt since we will need rV in any case - if (dist == ROT_PIECEWISE) { // Old piecewise distribution, for compatibility - piecewise_rho_fit(Ne, nu, Thetae, B, theta, rQ, rU, rV); - } else if (dist == ROT_OLD) { // Old incorrect distribution, for compatibility - old_rho_fit(Ne, nu, Thetae, B, theta, rQ, rU, rV); - } else if (dist == E_DEXTER_THERMAL || dist == E_THERMAL) { // Dexter rQ, Shcherbakov rV - // TODO All-Dexter default option w/Taylor series, make this compat - shcherbakov_rho_fit(Ne, nu, Thetae, B, theta, rQ, rU, rV); - } else { // TODO Fix Symphony thermal!!!, these are currently equal to ROT_OLD - *rQ = rho_nu_fit(nu, B, Ne, theta, fit, paramsM.STOKES_Q, Thetae, powerlaw_p, gamma_min, gamma_max, gamma_cut, kappa, kappa_width); - *rU = rho_nu_fit(nu, B, Ne, theta, fit, paramsM.STOKES_U, Thetae, powerlaw_p, gamma_min, gamma_max, gamma_cut, kappa, kappa_width); - *rV = rho_nu_fit(nu, B, Ne, theta, fit, paramsM.STOKES_V, Thetae, powerlaw_p, gamma_min, gamma_max, gamma_cut, kappa, kappa_width); - } - // Make invariant - *rQ *= nu; - *rU *= nu; - *rV *= nu; - - // *Then* handle field lines, to leave rV intact - if (theta <= 0 || theta >= M_PI) { - *rQ = 0; - *rU = 0; - } - -#if DEBUG - // Spot check for NaN coefficients - if (isnan(*rV) || *rV > 1.e100 || *rV < -1.e100) { - fprintf(stderr, "\nNAN RV! rV = %e nu = %e Ne = %e Thetae = %e\n", *rV, nu, Ne, Thetae); - } - if (isnan(*jV) || *jV > 1.e100 || *jV < -1.e100) { - fprintf(stderr, "\nNAN jV! jV = %e nu = %e Ne = %e Thetae = %e B = %e theta = %e\n", *jV, nu, Ne, Thetae, B, theta); - } -#endif -} - -/* - * get the invariant emissivity and opacity at a given position for a given wavevector - */ -void get_jkinv(double X[NDIM], double Kcon[NDIM], double *jnuinv, double *knuinv, Params *params) -{ - if (params->emission_type == E_CUSTOM) { - get_model_jk(X, Kcon, jnuinv, knuinv); - } else { - double nu, theta, B, Thetae, Ne, Bnuinv; - double Ucov[NDIM], Ucon[NDIM], Bcon[NDIM], Bcov[NDIM]; - - /* get fluid parameters */ - Ne = get_model_ne(X); /* check to see if we're outside fluid model */ - if (Ne == 0.) { - *jnuinv = 0.; - *knuinv = 0.; - return; - } - - /* get covariant four-velocity of fluid for use in get_bk_angle and get_fluid_nu */ - get_model_fourv(X, Kcon, Ucon, Ucov, Bcon, Bcov); - theta = get_bk_angle(X, Kcon, Ucov, Bcon, Bcov); /* angle between k & b */ - // No emission along field - if (theta <= 0. || theta >= M_PI) { /* no emission along field */ - *jnuinv = 0.; - *knuinv = 0.; - return; - } - - // Only compute these if we must - B = get_model_b(X); /* field in G */ - Thetae = get_model_thetae(X); /* temp in e rest-mass units */ - nu = get_fluid_nu(Kcon, Ucov); /* freq in Hz */ - - // TODO: could be cleaner here - struct parameters paramsM; - int fit; - double kappa, kappa_width; - - if (params->emission_type == E_KAPPA) { - - setConstParams(¶msM); - fit = paramsM.KAPPA_DIST; - - get_model_kappa(X, &kappa, &kappa_width); - - *jnuinv = j_nu_fit(nu, B, Ne, theta, fit, paramsM.STOKES_I, Thetae, - powerlaw_p, powerlaw_gamma_min, powerlaw_gamma_max, powerlaw_gamma_cut, - kappa, kappa_width) / nu/nu; - *knuinv = alpha_nu_fit(nu, B, Ne, theta, fit, paramsM.STOKES_I, Thetae, - powerlaw_p, powerlaw_gamma_min, powerlaw_gamma_max, powerlaw_gamma_cut, - kappa, kappa_width) * nu; - - } else if (params->emission_type == E_POWERLAW) { - - setConstParams(¶msM); - fit = paramsM.POWER_LAW; - - get_model_powerlaw_vals(X, &powerlaw_p, &Ne, &powerlaw_gamma_min, &powerlaw_gamma_max, &powerlaw_gamma_cut); - - *jnuinv = j_nu_fit(nu, B, Ne, theta, fit, paramsM.STOKES_I, Thetae, - powerlaw_p, powerlaw_gamma_min, powerlaw_gamma_max, powerlaw_gamma_cut, - kappa, kappa_width) / nu/nu; - *knuinv = alpha_nu_fit(nu, B, Ne, theta, fit, paramsM.STOKES_I, Thetae, - powerlaw_p, powerlaw_gamma_min, powerlaw_gamma_max, powerlaw_gamma_cut, - kappa, kappa_width) * nu * exp(-nu / 5.e13); // nu_cutoff = 5.e13 - - } else { - - /* assume emission is thermal */ - Bnuinv = Bnu_inv(nu, Thetae); - *jnuinv = jnu_inv(nu, Thetae, Ne, B, theta); - - if (Bnuinv < SMALL) { - *knuinv = SMALL; + // ABSORPTIVITIES + if (dist == E_THERMAL || dist == E_DEXTER_THERMAL || dist > 10) { // Thermal distributions + // Get absorptivities via Kirchoff's law + // Already invariant, guaranteed to respect aI > aP + // Faster than calling Symphony code since we know jS, Bnu + double Bnuinv = Bnu_inv(nu, paramsM.theta_e); // Planck function + *aI = *jI / Bnuinv; + *aQ = *jQ / Bnuinv; + *aU = *jU / Bnuinv; + *aV = *jV / Bnuinv; } else { - *knuinv = *jnuinv / Bnuinv; + *aI = alpha_nu_fit(¶msM, paramsM.STOKES_I) * nu; + *aQ = -alpha_nu_fit(¶msM, paramsM.STOKES_Q) * nu; + *aU = -alpha_nu_fit(¶msM, paramsM.STOKES_U) * nu; + *aV = alpha_nu_fit(¶msM, paramsM.STOKES_V) * nu; + + // Check basic relationships + double aP = sqrt(*aQ * *aQ + *aU * *aU + *aV * *aV); + if (*aI < aP/max_pol_frac_a) { + // Transport does not like 100% polarization... + double pol_frac_a = *aI / aP * max_pol_frac_a; + *aQ *= pol_frac_a; + *aU *= pol_frac_a; + *aV *= pol_frac_a; + } } - } - // Enforce j >= 0, k >= 0 just like in polarized emission. - if (*jnuinv < 0) { - *jnuinv = 0; - } - if (*knuinv < 0) { - *knuinv = 0; - } -#if DEBUG - if (isnan(*jnuinv) || isnan(*knuinv)) { - fprintf(stderr, "\nisnan get_jkinv\n"); - fprintf(stderr, ">> %g %g %g %g %g %g %g %g\n", *jnuinv, *knuinv, - Ne, theta, nu, B, Thetae, Bnuinv); + // ROTATIVITIES + paramsM.dexter_fit = 0; // TODO find a parameter to bring this back separately + *rQ = rho_nu_fit(¶msM, paramsM.STOKES_Q) * nu; + *rU = rho_nu_fit(¶msM, paramsM.STOKES_U) * nu; + *rV = rho_nu_fit(¶msM, paramsM.STOKES_V) * nu; } -#endif + } - // TODO put this outside, in integrate_emission or similar - if (params->isolate_counterjet == 1) { // Emission from X[2] > midplane only - if (X[2] < (cstopx[2] - cstartx[2]) / 2) { - *jnuinv = 0.; - } - } else if (params->isolate_counterjet == 2) { // Emission from X[2] < midplane only - if (X[2] > (cstopx[2] - cstartx[2]) / 2) { - *jnuinv = 0.; - } +#if DEBUG + // Check for NaN coefficients + if (isnan(*jI) || *jI > OVERFLOW || *jI < -OVERFLOW || + isnan(*jQ) || *jQ > OVERFLOW || *jQ < -OVERFLOW || + isnan(*jU) || *jU > OVERFLOW || *jU < -OVERFLOW || + isnan(*jV) || *jV > OVERFLOW || *jV < -OVERFLOW || + isnan(*aI) || *aI > OVERFLOW || *aI < -OVERFLOW || + isnan(*aQ) || *aQ > OVERFLOW || *aQ < -OVERFLOW || + isnan(*aU) || *aU > OVERFLOW || *aU < -OVERFLOW || + isnan(*aV) || *aV > OVERFLOW || *aV < -OVERFLOW || + isnan(*rQ) || *rQ > OVERFLOW || *rQ < -OVERFLOW || + isnan(*rU) || *rU > OVERFLOW || *rU < -OVERFLOW || + isnan(*rV) || *rV > OVERFLOW || *rV < -OVERFLOW) { +#pragma omp critical + { + fprintf(stderr, "\nNAN in emissivities!\n"); + fprintf(stderr, "j = %g %g %g %g alpha = %g %g %g %g rho = %g %g %g\n", *jI, *jQ, *jU, *jV, *aI, *aQ, *aU, *aV, *rQ, *rU, *rV); + fprintf(stderr, "nu = %g Ne = %g Thetae = %g B = %g theta = %g kappa = %g kappa_width = %g\n", + paramsM.nu, paramsM.electron_density, paramsM.theta_e, paramsM.magnetic_field, paramsM.observer_angle, + paramsM.kappa, paramsM.kappa_width); + // Powerlaw? + exit(-1); } } +#endif } // SUPPORTING FUNCTIONS @@ -491,9 +318,7 @@ void get_model_kappa(double X[NDIM], double *kappa, double *kappa_width) { double sigma = get_model_sigma(X); double beta = get_model_beta(X); *kappa = model_kappa; // INSERT VARIABLE KAPPA HERE -#if DEBUG //fprintf(stderr, "sigma, beta -> kappa %g %g -> %g\n", sigma, beta, *kappa); -#endif } else { *kappa = model_kappa; } @@ -505,202 +330,4 @@ void get_model_kappa(double X[NDIM], double *kappa, double *kappa_width) { fprintf(stderr, "NaN kappa val! kappa, kappa_width, Thetae: %g %g %g\n", *kappa, *kappa_width, Thetae); } #endif -} - - - -// POLARIZED FITS - -/* - * emissivity functions and functions used for Faraday conversion and rotation - * from J. Dexter PhD thesis (checked with Leung harmony program, and Huang & Shcherbakov 2011 - * Also see Dexter 2016 Appendix A1 - */ -void dexter_j_fit_thermal(double Ne, double nu, double Thetae, double B, double theta, - double *jI, double *jQ, double *jU, double *jV) -{ - // Synchrotron emissivity - double nus = 3.0 * EE * B * sin(theta) / 4.0 / M_PI / ME / CL * Thetae * Thetae + 1.0; - double x = nu / nus; - - *jI = Ne * EE * EE * nu / 2. / sqrt(3) / CL / Thetae / Thetae * I_I(x); // [g/s^2/cm = ergs/s/cm^3] - *jQ = Ne * EE * EE * nu / 2. / sqrt(3) / CL / Thetae / Thetae * I_Q(x); - *jU = 0.0; // convention; depends on tetrad - *jV = 2. * Ne * EE * EE * nu / tan(theta) / 3. / sqrt(3) / CL / Thetae / Thetae / Thetae * I_V(x); -} - -void shcherbakov_rho_fit(double Ne, double nu, double Thetae, double B, double theta, - double *rQ, double *rU, double *rV) -{ - double Thetaer = 1. / Thetae; - - double omega0 = EE * B / ME / CL; - double wp2 = 4. * M_PI * Ne * EE * EE / ME; - - // Faraday rotativities for thermal plasma - double Xe = Thetae * sqrt(sqrt(2) * sin(theta) * (1.e3 * omega0 / 2. / M_PI / nu)); - - // These are the Dexter (2016) fit actually - *rQ = 2. * M_PI * nu / 2. / CL * wp2 * omega0 * omega0 / pow(2 * M_PI * nu, 4) * - jffunc(Xe) * (gsl_sf_bessel_Kn(1, Thetaer) / gsl_sf_bessel_Kn(2, Thetaer) + - 6. * Thetae) * sin(theta) * sin(theta); - *rU = 0.0; - - // Shcherbakov fit for rV. Possibly questionable at very low frequency - // Note the real bessel functions. Slow? - *rV = 2.0 * M_PI * nu / CL * wp2 * omega0 / pow(2. * M_PI * nu, 3) * - gsl_sf_bessel_Kn(0, Thetaer) / (gsl_sf_bessel_Kn(2, Thetaer)+SMALL) * g(Xe) * cos(theta); -} - -void piecewise_rho_fit(double Ne, double nu, double Thetae, double B, double theta, - double *rQ, double *rU, double *rV) -{ - double Thetaer = 1. / Thetae; - - double omega0 = EE * B / ME / CL; - double wp2 = 4. * M_PI * Ne * EE * EE / ME; - - // Faraday rotativities for thermal plasma - double Xe = Thetae * sqrt(sqrt(2) * sin(theta) * (1.e3 * omega0 / 2. / M_PI / nu)); - - // Approximate bessel functions to match rhoq,v with grtrans - *rQ = 2. * M_PI * nu / 2. / CL * wp2 * omega0 * omega0 / pow(2 * M_PI * nu, 4) * - jffunc(Xe) * (besselk_asym(1, Thetaer) / besselk_asym(2, Thetaer) + - 6. * Thetae) * sin(theta) * sin(theta); - *rU = 0.0; - // Switch between three different fits for rho_V - if (Thetae > 3.0) { - // High temperature: use approximations to bessel - *rV = 2.0 * M_PI * nu / CL * wp2 * omega0 / pow(2. * M_PI * nu, 3) * - (besselk_asym(0, Thetaer) - Je(Xe)) / besselk_asym(2, Thetaer) * cos(theta); - } else if (0.2 < Thetae && Thetae <= 3.0) { - // Mid temperature: use real bessel functions (TODO fit?) - *rV = 2.0 * M_PI * nu / CL * wp2 * omega0 / pow(2. * M_PI * nu, 3) * - (gsl_sf_bessel_Kn(0, Thetaer) - Je(Xe)) / gsl_sf_bessel_Kn(2, Thetaer) * cos(theta); - } else if (Thetae <= 0.2) { - // Use the constant low-temperature limit - *rV = 2.0 * M_PI * nu / CL * wp2 * omega0 / pow(2. * M_PI * nu, 3) * cos(theta); - } -} - -void old_rho_fit(double Ne, double nu, double Thetae, double B, double theta, - double *rQ, double *rU, double *rV) -{ - double Thetaer = 1. / Thetae; - - double omega0 = EE * B / ME / CL; - double wp2 = 4. * M_PI * Ne * EE * EE / ME; - - // Faraday rotativities for thermal plasma - double Xe = Thetae * sqrt(sqrt(2) * sin(theta) * (1.e3 * omega0 / 2. / M_PI / nu)); - - // Approximate bessel functions to match rhoq,v with grtrans - *rQ = 2. * M_PI * nu / 2. / CL * wp2 * omega0 * omega0 / pow(2 * M_PI * nu, 4) * - jffunc(Xe) * (besselk_asym(1, Thetaer) / besselk_asym(2, Thetaer) + - 6. * Thetae) * sin(theta) * sin(theta); - *rU = 0.0; - // Use approximations to Bessel fns for all space. Dangerous at low temp! - *rV = 2.0 * M_PI * nu / CL * wp2 * omega0 / pow(2. * M_PI * nu, 3) * - (besselk_asym(0, Thetaer) - Je(Xe)) / besselk_asym(2, Thetaer) * cos(theta); -} - -double g(double Xe) -{ - return 1. - 0.11 * log(1 + 0.035 * Xe); -} - - -double h(double Xe) -{ - return 2.011 * exp(-pow(Xe, 1.035) / 4.7) - - cos(Xe * 0.5) * exp(-pow(Xe, 1.2) / 2.73) - - 0.011 * exp(-Xe / 47.2); -} - -double Je(double Xe) -{ - return 0.43793091 * log(1. + 0.00185777 * pow(Xe, 1.50316886)); -} - -double jffunc(double Xe) -{ - double extraterm = - (0.011 * exp(-Xe / 47.2) - - pow(2., -1. / 3.) / pow(3., - 23. / 6.) * M_PI * 1e4 * pow(Xe + 1e-16, - -8. / 3.)) * - (0.5 + 0.5 * tanh((log(Xe) - log(120.)) / 0.1)); - - return 2.011 * exp(-pow(Xe, 1.035) / 4.7) - - cos(Xe * 0.5) * exp(-pow(Xe, 1.2) / 2.73) - - 0.011 * exp(-Xe / 47.2) + extraterm; -} - -double I_I(double x) -{ - return 2.5651 * (1 + 1.92 * pow(x, -1. / 3.) + - 0.9977 * pow(x, -2. / 3.)) * exp(-1.8899 * pow(x, - 1. / - 3.)); -} - -double I_Q(double x) -{ - return 2.5651 * (1 + 0.93193 * pow(x, -1. / 3.) + - 0.499873 * pow(x, -2. / 3.)) * exp(-1.8899 * pow(x, - 1. / - 3.)); -} - -double I_V(double x) -{ - return (1.81348 / x + 3.42319 * pow(x, -2. / 3.) + - 0.0292545 * pow(x, -0.5) + 2.03773 * pow(x, - -1. / 3.)) * - exp(-1.8899 * pow(x, 1. / 3.)); -} - -double besselk_asym(int n, double x) -{ - - if (n == 0) - return -log(x / 2.) - 0.5772; - - if (n == 1) - return 1. / x; - - if (n == 2) - return 2. / x / x; - - fprintf(stderr,"this cannot happen\n"); - exit(1); -} - -// UNPOLARIZED FITS - -/* - * thermal synchrotron emissivity - * - * Interpolates between Petrosian limit and - * classical thermal synchrotron limit - * Good for Thetae >~ 1 - * See Leung+ 2011, restated Pandya+ 2016 - */ -double jnu_synch(double nu, double Ne, double Thetae, double B, double theta) -{ - double K2,nuc,nus,x,f,j,sth ; - - //K2 = gsl_sf_bessel_Kn(2,1./Thetae) ; - K2 = 2.*Thetae*Thetae ; - - nuc = EE*B/(2.*M_PI*ME*CL) ; - sth = sin(theta) ; - nus = (2./9.)*nuc*Thetae*Thetae*sth ; - if(nu > 1.e12*nus) return(0.) ; - x = nu/nus ; - f = pow( pow(x,1./2.) + pow(2.,11./12.)*pow(x,1./6.), 2 ) ; - j = (sqrt(2.)*M_PI*EE*EE*Ne*nus/(3.*CL*K2)) * f * exp(-pow(x,1./3.)) ; - - return(j) ; -} - +} \ No newline at end of file diff --git a/src/model_radiation.h b/src/model_radiation.h index 9f29e94..e5e2583 100644 --- a/src/model_radiation.h +++ b/src/model_radiation.h @@ -14,11 +14,11 @@ void try_set_radiation_parameter(const char *word, const char *value); /* transfer coefficients in tetrad frame */ -void jar_calc(double X[NDIM], double Kcon[NDIM], double *jI, double *jQ, - double *jU, double *jV, double *aI, double *aQ, double *aU, - double *aV, double *rQ, double *rU, double *rV, Params *params); +void jar_calc(double X[NDIM], double nu, double theta, + double *jI, double *jQ, double *jU, double *jV, + double *aI, double *aQ, double *aU, double *aV, + double *rQ, double *rU, double *rV, Params *params); -double jnu_synch(double nu, double Ne, double Thetae, double B, double theta); -void get_jkinv(double X[NDIM], double Kcon[NDIM], double *jnuinv, double *knuinv, Params *params); +void get_jkinv(double X[NDIM], double Kcon[NDIM], double *jI, double *aI, Params *params); #endif /* MODEL_RADIATION_H */ diff --git a/src/radiation.c b/src/radiation.c index 535906d..738cebc 100644 --- a/src/radiation.c +++ b/src/radiation.c @@ -19,14 +19,7 @@ double Bnu_inv (double nu, double Thetae) return ((2. * HPL / (CL * CL)) / (x / 24. * (24. + x * (12. + x * (4. + x))))); else - return ((2. * HPL / (CL * CL)) / (exp (x) - 1.)); -} - -/* get jnu, and convert to jnu/nu^2 = jnu_invariant */ -double jnu_inv(double nu, double Thetae, double Ne, double B, double theta) -{ - double j = jnu_synch(nu, Ne, Thetae, B, theta); - return (j / (nu * nu)); + return ((2. * HPL / (CL * CL)) / (exp(x) - 1.)); } /* get frequency in fluid frame, in Hz */ @@ -39,19 +32,13 @@ double get_fluid_nu(double Kcon[NDIM], double Ucov[NDIM]) Kcon[2] * Ucov[2] + Kcon[3] * Ucov[3]) * ME * CL * CL / HPL; - if (nu < 0.) { #if DEBUG - fprintf(stderr, "Fluid nu < 0: %g!\n", nu); -#endif - nu = 1.; - } - - if (isnan(nu)) { - fprintf(stderr, "isnan get_fluid_nu, K: %g %g %g %g\n", - Kcon[0], Kcon[1], Kcon[2], Kcon[3]); - fprintf(stderr, "isnan get_fluid_nu, U: %g %g %g %g\n", - Ucov[0], Ucov[1], Ucov[2], Ucov[3]); + if (isnan(nu) || nu < 0) { + fprintf(stderr, "isnan/isnegative get_fluid_nu!\n"); + fprintf(stderr, "K: %g %g %g %g\n", Kcon[0], Kcon[1], Kcon[2], Kcon[3]); + fprintf(stderr, "U: %g %g %g %g\n", Ucov[0], Ucov[1], Ucov[2], Ucov[3]); } +#endif return (nu); } @@ -61,27 +48,25 @@ double get_bk_angle(double X[NDIM], double Kcon[NDIM], double Ucov[NDIM], double { double B, k, mu; - B = sqrt(fabs - (Bcon[0] * Bcov[0] + Bcon[1] * Bcov[1] + Bcon[2] * Bcov[2] + - Bcon[3] * Bcov[3])); + B = sqrt(fabs(Bcon[0]*Bcov[0] + Bcon[1]*Bcov[1] + Bcon[2]*Bcov[2] + Bcon[3]*Bcov[3])); if (B == 0.) return (M_PI / 2.); - k = fabs(Kcon[0] * Ucov[0] + Kcon[1] * Ucov[1] + Kcon[2] * Ucov[2] + - Kcon[3] * Ucov[3]); + k = fabs(Kcon[0]*Ucov[0] + Kcon[1]*Ucov[1] + Kcon[2]*Ucov[2] + Kcon[3] * Ucov[3]); - mu = (Kcon[0] * Bcov[0] + Kcon[1] * Bcov[1] + Kcon[2] * Bcov[2] + - Kcon[3] * Bcov[3]) / (k * B); + mu = (Kcon[0]*Bcov[0] + Kcon[1]*Bcov[1] + Kcon[2]*Bcov[2] + Kcon[3]*Bcov[3]) / (k * B); if (fabs(mu) > 1.) mu /= fabs(mu); +#if DEBUG if (isnan(mu)) { fprintf(stderr, "isnan get_bk_angle\n"); fprintf(stderr, "B = %g, k = %g, mu = %g\n", B, k, mu); fprintf(stderr, "Ucov: %g %g %g %g\n", Ucov[0], Ucov[1], Ucov[2], Ucov[3]); } +#endif return (acos(mu)); } diff --git a/src/symphony/fits.c b/src/symphony/fits.c index 14e7244..09dc1da 100644 --- a/src/symphony/fits.c +++ b/src/symphony/fits.c @@ -3,15 +3,10 @@ #include #include -/*Wrappers for the fitting formulae*/ +/*Wrappers for the fitting formulae, ipole-specific calling convention*/ /*j_nu_fit: wrapper for the emissivity fitting formulae. Takes in the - * same parameters as j_nu(), populates the struct of - * parameters, and passes them to the fitting formulae. The - * fitting formulae for each distribution function are located - * in their corresponding folders, so for example the - * KAPPA_DIST fitting formulae are located in the kappa folder, - * in the file kappa_fits.c + * same parameters as j_nu(), and passes them to the fitting formulae. * *@params: nu, magnetic_field, electron_density, observer_angle, * distribution, polarization, theta_e, power_law_p, @@ -20,85 +15,57 @@ *@returns: the corresponding fitting formula (based on the distribution * function) evaluated for the input parameters. */ -double j_nu_fit(double nu, - double magnetic_field, - double electron_density, - double observer_angle, - int distribution, - int polarization, - double theta_e, - double power_law_p, - double gamma_min, - double gamma_max, - double gamma_cutoff, - double kappa, - double kappa_width) +double j_nu_fit(struct parameters *params, int polarization) { -/*fill the struct with values*/ - struct parameters params; - setConstParams(¶ms); - params.nu = nu; - params.magnetic_field = magnetic_field; - params.observer_angle = observer_angle; - params.electron_density = electron_density; - params.distribution = distribution; - params.polarization = polarization; - params.mode = params.EMISSIVITY; - params.theta_e = theta_e; - params.power_law_p = power_law_p; - params.gamma_min = gamma_min; - params.gamma_max = gamma_max; - params.gamma_cutoff = gamma_cutoff; - params.kappa = kappa; - params.kappa_width = kappa_width; + params->polarization = polarization; #if DEBUG - check_for_errors(¶ms); + check_for_errors(params); #endif - if(params.distribution == params.MAXWELL_JUETTNER) + if(params->distribution == params->MAXWELL_JUETTNER) { - if (params.polarization == params.STOKES_I) return maxwell_juettner_I(¶ms); - else if(params.polarization == params.STOKES_Q) return maxwell_juettner_Q(¶ms); - else if(params.polarization == params.STOKES_U) return 0.; - else if(params.polarization == params.STOKES_V) return maxwell_juettner_V(¶ms); + if (params->polarization == params->STOKES_I) return maxwell_juettner_I(params); + else if(params->polarization == params->STOKES_Q) return maxwell_juettner_Q(params); + else if(params->polarization == params->STOKES_U) return 0.; + else if(params->polarization == params->STOKES_V) return maxwell_juettner_V(params); } - else if(params.distribution == params.POWER_LAW) + else if(params->distribution == params->POWER_LAW) { - if (params.polarization == params.STOKES_I) return power_law_I(¶ms); - else if(params.polarization == params.STOKES_Q) return power_law_Q(¶ms); - else if(params.polarization == params.STOKES_U) return 0.; - else if(params.polarization == params.STOKES_V) return power_law_V(¶ms); + if (params->polarization == params->STOKES_I) return power_law_I(params); + else if(params->polarization == params->STOKES_Q) return power_law_Q(params); + else if(params->polarization == params->STOKES_U) return 0.; + else if(params->polarization == params->STOKES_V) return power_law_V(params); } - else if(params.distribution == params.KAPPA_DIST) + else if(params->distribution == params->KAPPA_DIST) { - if (params.polarization == params.STOKES_I) { - if (kappa < 7.5) { - return kappa_I(¶ms); - } else if (kappa >= 7.5 && kappa < 15) { - return ((15 - params.kappa) * maxwell_juettner_I(¶ms) + (params.kappa - 7.5) * kappa_I(¶ms)) / 7.5; + if (params->polarization == params->STOKES_I) { + if (params->kappa < 7.5) { + return kappa_I(params); + } else if (params->kappa >= 7.5 && params->kappa < 15) { + return ((15 - params->kappa) * maxwell_juettner_I(params) + (params->kappa - 7.5) * kappa_I(params)) / 7.5; } else { - return maxwell_juettner_I(¶ms); + return maxwell_juettner_I(params); } - } else if(params.polarization == params.STOKES_Q) { - if (kappa < 7.5) { - return kappa_Q(¶ms); - } else if (kappa >= 7.5 && kappa < 15) { - return ((15 - params.kappa) * maxwell_juettner_Q(¶ms) + (params.kappa - 7.5) * kappa_Q(¶ms)) / 7.5; + } else if (params->polarization == params->STOKES_Q) { + if (params->kappa < 7.5) { + return kappa_Q(params); + } else if (params->kappa >= 7.5 && params->kappa < 15) { + return ((15 - params->kappa) * maxwell_juettner_Q(params) + (params->kappa - 7.5) * kappa_Q(params)) / 7.5; } else { - return maxwell_juettner_Q(¶ms); + return maxwell_juettner_Q(params); } - } else if(params.polarization == params.STOKES_U) { + } else if (params->polarization == params->STOKES_U) { return 0.; - } else if(params.polarization == params.STOKES_V) { - if (kappa < 7.5) { - return kappa_V(¶ms); - } else if (kappa >= 7.5 && kappa < 15) { - return ((15 - params.kappa) * maxwell_juettner_V(¶ms) + (params.kappa - 7.5) * kappa_V(¶ms)) / 7.5; + } else if (params->polarization == params->STOKES_V) { + if (params->kappa < 7.5) { + return kappa_V(params); + } else if (params->kappa >= 7.5 && params->kappa < 15) { + return ((15 - params->kappa) * maxwell_juettner_V(params) + (params->kappa - 7.5) * kappa_V(params)) / 7.5; } else { - return maxwell_juettner_V(¶ms); + return maxwell_juettner_V(params); } } } @@ -107,12 +74,7 @@ double j_nu_fit(double nu, } /*alpha_nu_fit: wrapper for the absorptivity fitting formulae. Takes in the - * same parameters as alpha_nu(), populates the struct of - * parameters, and passes them to the fitting formulae. The - * fitting formulae for each distribution function are located - * in their corresponding folders, so for example the - * KAPPA_DIST fitting formulae are located in the kappa folder, - * in the file kappa_fits.c + * same parameters as alpha_nu(), and passes them to the fitting formulae. * *@params: nu, magnetic_field, electron_density, observer_angle, * distribution, polarization, theta_e, power_law_p, @@ -122,85 +84,57 @@ double j_nu_fit(double nu, * function) evaluated for the input parameters. */ -double alpha_nu_fit(double nu, - double magnetic_field, - double electron_density, - double observer_angle, - int distribution, - int polarization, - double theta_e, - double power_law_p, - double gamma_min, - double gamma_max, - double gamma_cutoff, - double kappa, - double kappa_width) +double alpha_nu_fit(struct parameters *params, int polarization) { -/*fill the struct with values*/ - struct parameters params; - setConstParams(¶ms); - params.nu = nu; - params.magnetic_field = magnetic_field; - params.observer_angle = observer_angle; - params.electron_density = electron_density; - params.distribution = distribution; - params.polarization = polarization; - params.mode = params.ABSORPTIVITY; - params.theta_e = theta_e; - params.power_law_p = power_law_p; - params.gamma_min = gamma_min; - params.gamma_max = gamma_max; - params.gamma_cutoff = gamma_cutoff; - params.kappa = kappa; - params.kappa_width = kappa_width; + params->polarization = polarization; #if DEBUG - check_for_errors(¶ms); + check_for_errors(params); #endif - if(params.distribution == params.MAXWELL_JUETTNER) + if(params->distribution == params->MAXWELL_JUETTNER) { - if (params.polarization == params.STOKES_I) return maxwell_juettner_I_abs(¶ms); - else if(params.polarization == params.STOKES_Q) return maxwell_juettner_Q_abs(¶ms); - else if(params.polarization == params.STOKES_U) return 0.; - else if(params.polarization == params.STOKES_V) return maxwell_juettner_V_abs(¶ms); + if (params->polarization == params->STOKES_I) return maxwell_juettner_I_abs(params); + else if(params->polarization == params->STOKES_Q) return maxwell_juettner_Q_abs(params); + else if(params->polarization == params->STOKES_U) return 0.; + else if(params->polarization == params->STOKES_V) return maxwell_juettner_V_abs(params); } - else if(params.distribution == params.POWER_LAW) + else if(params->distribution == params->POWER_LAW) { - if (params.polarization == params.STOKES_I) return power_law_I_abs(¶ms); - else if(params.polarization == params.STOKES_Q) return power_law_Q_abs(¶ms); - else if(params.polarization == params.STOKES_U) return 0.; - else if(params.polarization == params.STOKES_V) return power_law_V_abs(¶ms); + if (params->polarization == params->STOKES_I) return power_law_I_abs(params); + else if(params->polarization == params->STOKES_Q) return power_law_Q_abs(params); + else if(params->polarization == params->STOKES_U) return 0.; + else if(params->polarization == params->STOKES_V) return power_law_V_abs(params); } - else if(params.distribution == params.KAPPA_DIST) + else if(params->distribution == params->KAPPA_DIST) { - if (params.polarization == params.STOKES_I) { - if (kappa < 7.5) { - return kappa_I_abs(¶ms); - } else if (kappa >= 7.5 && kappa < 15) { - return ((15 - params.kappa) * maxwell_juettner_I_abs(¶ms) + (params.kappa - 7.5) * kappa_I_abs(¶ms)) / 7.5; + if (params->polarization == params->STOKES_I) { + if (params->kappa < 7.5) { + return kappa_I_abs(params); + } else if (params->kappa >= 7.5 && params->kappa < 15) { + return ((15 - params->kappa) * maxwell_juettner_I_abs(params) + (params->kappa - 7.5) * kappa_I_abs(params)) / 7.5; } else { - return maxwell_juettner_I_abs(¶ms); + return maxwell_juettner_I_abs(params); } - } else if(params.polarization == params.STOKES_Q) { - if (kappa < 7.5) { - return kappa_Q_abs(¶ms); - } else if (kappa >= 7.5 && kappa < 15) { - return ((15 - params.kappa) * maxwell_juettner_Q_abs(¶ms) + (params.kappa - 7.5) * kappa_Q_abs(¶ms)) / 7.5; + } else if(params->polarization == params->STOKES_Q) { + if (params->kappa < 7.5) { + return kappa_Q_abs(params); + } else if (params->kappa >= 7.5 && params->kappa < 15) { + return ((15 - params->kappa) * maxwell_juettner_Q_abs(params) + (params->kappa - 7.5) * kappa_Q_abs(params)) / 7.5; } else { - return maxwell_juettner_Q_abs(¶ms); + return maxwell_juettner_Q_abs(params); } - } else if(params.polarization == params.STOKES_U) { + } else if(params->polarization == params->STOKES_U) { return 0.; - } else if(params.polarization == params.STOKES_V) { - if (kappa < 7.5) { - return kappa_V_abs(¶ms); - } else if (kappa >= 7.5 && kappa < 15) { - return ((15 - params.kappa) * maxwell_juettner_V_abs(¶ms) + (params.kappa - 7.5) * kappa_V_abs(¶ms)) / 7.5; + } else if(params->polarization == params->STOKES_V) { + if (params->kappa < 7.5) { + return kappa_V_abs(params); + } else if (params->kappa >= 7.5 && params->kappa < 15) { + return ((15 - params->kappa) * maxwell_juettner_V_abs(params) + (params->kappa - 7.5) * kappa_V_abs(params)) / 7.5; } else { - return maxwell_juettner_V_abs(¶ms); + return maxwell_juettner_V_abs(params); } } } @@ -209,7 +143,8 @@ double alpha_nu_fit(double nu, } /*rho_nu_fit: Fits to Faraday rotation/conversion coefficients; right now only has - * formulae for Maxwell-Juettner distribution, from Dexter (2016) + * formulae for Maxwell-Juettner distribution, from Dexter (2016), + * and the Kappa distribution, from Marszewski+ (2021) * *@params: nu, magnetic_field, electron_density, observer_angle, * distribution, polarization, theta_e, power_law_p, @@ -219,103 +154,68 @@ double alpha_nu_fit(double nu, * (based on the distribution function) evaluated for * the input parameters. */ -double rho_nu_fit(double nu, - double magnetic_field, - double electron_density, - double observer_angle, - int distribution, - int polarization, - double theta_e, - double power_law_p, - double gamma_min, - double gamma_max, - double gamma_cutoff, - double kappa, - double kappa_width) +double rho_nu_fit(struct parameters *params, int polarization) { -/*fill the struct with values*/ - struct parameters params; - setConstParams(¶ms); - params.nu = nu; - params.magnetic_field = magnetic_field; - params.observer_angle = observer_angle; - params.electron_density = electron_density; - params.distribution = distribution; - params.polarization = polarization; - params.mode = params.ABSORPTIVITY; - params.theta_e = theta_e; - params.power_law_p = power_law_p; - params.gamma_min = gamma_min; - params.gamma_max = gamma_max; - params.gamma_cutoff = gamma_cutoff; - params.kappa = kappa; - params.kappa_width = kappa_width; + params->polarization = polarization; #if DEBUG - check_for_errors(¶ms); - if(params.polarization == params.STOKES_I) + check_for_errors(params); + if(params->polarization == params->STOKES_I) { printf("No Faraday rotation of total intensity"); return 0.; } #endif - if(params.distribution == params.MAXWELL_JUETTNER) + if(params->distribution == params->MAXWELL_JUETTNER) { - if (params.polarization == params.STOKES_Q) return maxwell_juettner_rho_Q(¶ms); - else if(params.polarization == params.STOKES_U) return 0.; - else if(params.polarization == params.STOKES_V) return maxwell_juettner_rho_V(¶ms); - } + if (params->polarization == params->STOKES_Q) return maxwell_juettner_rho_Q(params); + else if(params->polarization == params->STOKES_U) return 0.; + else if(params->polarization == params->STOKES_V) return maxwell_juettner_rho_V(params); - if(params.distribution == params.KAPPA_DIST) - { + } else if(params->distribution == params->KAPPA_DIST) { #if DEBUG - if(params.nu/(2.8e6 * params.magnetic_field) < 100 || - params.nu/(pow(params.kappa_width * params.kappa, 2.) * (2.8e6 * params.magnetic_field) * sin(params.observer_angle)) < 0.1) + if(params->nu/(2.8e6 * params->magnetic_field) < 100 || + params->nu/(pow(params->kappa_width * params->kappa, 2.) * (2.8e6 * params->magnetic_field) * sin(params->observer_angle)) < 0.1) { printf("\n WARNING: nu and/or X_kappa low; rho kappa fits may be inaccurate \n"); } #endif - if (params.polarization == params.STOKES_Q) - { - if (params.kappa < 3.5) - return kappa35_rho_Q(¶ms); - else if (params.kappa >= 3.5 && params.kappa < 4.0) - return ((4.0 - params.kappa) * kappa35_rho_Q(¶ms) + (params.kappa - 3.5) * kappa4_rho_Q(¶ms)) / 0.5; - else if (params.kappa >= 4.0 && params.kappa < 4.5) - return ((4.5 - params.kappa) * kappa4_rho_Q(¶ms) + (params.kappa - 4.0) * kappa45_rho_Q(¶ms)) / 0.5; - else if (params.kappa >= 4.5 && params.kappa < 5.0) - return ((5.0 - params.kappa) * kappa45_rho_Q(¶ms) + (params.kappa - 4.5) * kappa5_rho_Q(¶ms)) / 0.5; - else if (params.kappa >= 5.0 && params.kappa <= 10.0) - return ((10.0 - params.kappa) * kappa5_rho_Q(¶ms) + (params.kappa - 5.0) * maxwell_juettner_rho_Q(¶ms)) / 5.0; - else if (params.kappa > 10.0) - return maxwell_juettner_rho_Q(¶ms); - } - else if(params.polarization == params.STOKES_U) return 0.; - else if(params.polarization == params.STOKES_V) - { - if(params.kappa < 3.5) - return kappa35_rho_V(¶ms); - else if(params.kappa >= 3.5 && params.kappa < 4.0) - return ((4.0 - params.kappa) * kappa35_rho_V(¶ms) + (params.kappa - 3.5) * kappa4_rho_V(¶ms)) / 0.5; - else if(params.kappa >= 4.0 && params.kappa < 4.5) - return ((4.5 - params.kappa) * kappa4_rho_V(¶ms) + (params.kappa - 4.0) * kappa45_rho_V(¶ms)) / 0.5; - else if(params.kappa >= 4.5 && params.kappa <= 5.0) - return ((5.0 - params.kappa) * kappa45_rho_V(¶ms) + (params.kappa - 4.5) * kappa5_rho_V(¶ms)) / 0.5; - else if (params.kappa >= 5.0 && params.kappa <= 10.0) - return ((10.0 - params.kappa) * kappa5_rho_V(¶ms) + (params.kappa - 5.0) * maxwell_juettner_rho_V(¶ms)) / 5.0; - else if (params.kappa > 10.0) - return maxwell_juettner_rho_V(¶ms); - } + if (params->polarization == params->STOKES_Q) + { + if (params->kappa < 3.5) + return kappa35_rho_Q(params); + else if (params->kappa >= 3.5 && params->kappa < 4.0) + return ((4.0 - params->kappa) * kappa35_rho_Q(params) + (params->kappa - 3.5) * kappa4_rho_Q(params)) / 0.5; + else if (params->kappa >= 4.0 && params->kappa < 4.5) + return ((4.5 - params->kappa) * kappa4_rho_Q(params) + (params->kappa - 4.0) * kappa45_rho_Q(params)) / 0.5; + else if (params->kappa >= 4.5 && params->kappa < 5.0) + return ((5.0 - params->kappa) * kappa45_rho_Q(params) + (params->kappa - 4.5) * kappa5_rho_Q(params)) / 0.5; + else if (params->kappa >= 5.0 && params->kappa <= 10.0) + return ((10.0 - params->kappa) * kappa5_rho_Q(params) + (params->kappa - 5.0) * maxwell_juettner_rho_Q(params)) / 5.0; + else if (params->kappa > 10.0) + return maxwell_juettner_rho_Q(params); } - -/*Faraday rotation coefficients for Power-law distribution will be added later*/ -// else if(params.distribution == params.POWER_LAW) -// { -// if (params.polarization == params.STOKES_Q) return power_law_rho_Q(¶ms); -// else if(params.polarization == params.STOKES_U) return 0.; -// else if(params.polarization == params.STOKES_V) return power_law_rho_V(¶ms); -// } + else if(params->polarization == params->STOKES_U) return 0.; + else if(params->polarization == params->STOKES_V) + { + if(params->kappa < 3.5) + return kappa35_rho_V(params); + else if(params->kappa >= 3.5 && params->kappa < 4.0) + return ((4.0 - params->kappa) * kappa35_rho_V(params) + (params->kappa - 3.5) * kappa4_rho_V(params)) / 0.5; + else if(params->kappa >= 4.0 && params->kappa < 4.5) + return ((4.5 - params->kappa) * kappa4_rho_V(params) + (params->kappa - 4.0) * kappa45_rho_V(params)) / 0.5; + else if(params->kappa >= 4.5 && params->kappa <= 5.0) + return ((5.0 - params->kappa) * kappa45_rho_V(params) + (params->kappa - 4.5) * kappa5_rho_V(params)) / 0.5; + else if (params->kappa >= 5.0 && params->kappa <= 10.0) + return ((10.0 - params->kappa) * kappa5_rho_V(params) + (params->kappa - 5.0) * maxwell_juettner_rho_V(params)) / 5.0; + else if (params->kappa > 10.0) + return maxwell_juettner_rho_V(params); + } +} else if(params->distribution == params->POWER_LAW) { + fprintf(stderr, "\nNo rotativity fits are implemented for power-law diestributions!!\n"); + exit(-1); +} return 0.; } @@ -329,7 +229,7 @@ double rho_nu_fit(double nu, *@returns: prints error messages or quits if disallowed values * (such as magnitude of magnetic field < 0) are entered. */ -void check_for_errors(struct parameters * params) +void check_for_errors(struct parameters *params) { double nu_c = get_nu_c(*params); diff --git a/src/symphony/fits.h b/src/symphony/fits.h index 8cb4df8..fcf7443 100644 --- a/src/symphony/fits.h +++ b/src/symphony/fits.h @@ -48,48 +48,8 @@ double kappa45_rho_V(struct parameters * params); double kappa5_rho_V(struct parameters * params); void check_for_errors(struct parameters * params); -double j_nu_fit(double nu, - double magnetic_field, - double electron_density, - double observer_angle, - int distribution, - int polarization, - double theta_e, - double power_law_p, - double gamma_min, - double gamma_max, - double gamma_cutoff, - double kappa, - double kappa_width - ); -double alpha_nu_fit(double nu, - double magnetic_field, - double electron_density, - double observer_angle, - int distribution, - int polarization, - double theta_e, - double power_law_p, - double gamma_min, - double gamma_max, - double gamma_cutoff, - double kappa, - double kappa_width - ); - -double rho_nu_fit(double nu, - double magnetic_field, - double electron_density, - double observer_angle, - int distribution, - int polarization, - double theta_e, - double power_law_p, - double gamma_min, - double gamma_max, - double gamma_cutoff, - double kappa, - double kappa_width - ); +double j_nu_fit(struct parameters * params, int polarization); +double alpha_nu_fit(struct parameters * params, int polarization); +double rho_nu_fit(struct parameters * params, int polarization); #endif /* SYMPHONY_FITS_H_ */ diff --git a/src/symphony/maxwell_juettner_fits.c b/src/symphony/maxwell_juettner_fits.c index b818ff7..47f40d9 100644 --- a/src/symphony/maxwell_juettner_fits.c +++ b/src/symphony/maxwell_juettner_fits.c @@ -1,5 +1,7 @@ #include "maxwell_juettner.h" + + /*maxwell_juettner_I: fitting formula for the emissivity (polarized in Stokes I) * produced by a Maxwell-Juettner (relativistic thermal) * distribution of electrons. (Eq. 29, 31 of [1]) @@ -8,7 +10,7 @@ *@returns: fit to the emissivity, polarized in Stokes I, for the given * parameters for a Maxwell-Juettner distribution. */ -double maxwell_juettner_I(struct parameters * params) +double maxwell_juettner_I(struct parameters *params) { double nu_c = get_nu_c(*params); @@ -40,7 +42,7 @@ double maxwell_juettner_I(struct parameters * params) *@returns: fit to the emissivity, polarized in Stokes Q, for the given * parameters for a Maxwell-Juettner distribution. */ -double maxwell_juettner_Q(struct parameters * params) +double maxwell_juettner_Q(struct parameters *params) { double nu_c = get_nu_c(*params); @@ -73,7 +75,7 @@ double maxwell_juettner_Q(struct parameters * params) *@returns: fit to the emissivity, polarized in Stokes V, for the given * parameters for a Maxwell-Juettner distribution. */ -double maxwell_juettner_V(struct parameters * params) +double maxwell_juettner_V(struct parameters *params) { double nu_c = get_nu_c(*params); @@ -109,7 +111,7 @@ double maxwell_juettner_V(struct parameters * params) *@params: struct of parameters params *@returns: Planck function evaluated for the supplied parameters */ -double planck_func(struct parameters * params) +double planck_func(struct parameters *params) { double term1 = (2.*params->plancks_constant*pow(params->nu, 3.)) /pow(params->speed_light, 2.); @@ -131,7 +133,7 @@ double planck_func(struct parameters * params) *@returns: fitting formula to the absorptivity, in Stokes I, for a Maxwell- * Juettner distribution of electrons. */ -double maxwell_juettner_I_abs(struct parameters * params) +double maxwell_juettner_I_abs(struct parameters *params) { double ans = maxwell_juettner_I(params)/planck_func(params); return ans; @@ -145,7 +147,7 @@ double maxwell_juettner_I_abs(struct parameters * params) *@returns: fitting formula to the absorptivity, in Stokes Q, for a Maxwell- * Juettner distribution of electrons. */ -double maxwell_juettner_Q_abs(struct parameters * params) +double maxwell_juettner_Q_abs(struct parameters *params) { double ans = maxwell_juettner_Q(params)/planck_func(params); return ans; @@ -159,7 +161,7 @@ double maxwell_juettner_Q_abs(struct parameters * params) *@returns: fitting formula to the absorptivity, in Stokes V, for a Maxwell- * Juettner distribution of electrons. */ -double maxwell_juettner_V_abs(struct parameters * params) +double maxwell_juettner_V_abs(struct parameters *params) { double ans = maxwell_juettner_V(params)/planck_func(params); return ans; @@ -174,7 +176,7 @@ double maxwell_juettner_V_abs(struct parameters * params) *@returns: fitting formula to the Faraday conversion coefficient * for a Maxwell-Juettner distribution of electrons. */ -double maxwell_juettner_rho_Q(struct parameters * params) +double maxwell_juettner_rho_Q(struct parameters *params) { double omega0 = params->electron_charge*params->magnetic_field / (params->mass_electron*params->speed_light); @@ -194,11 +196,13 @@ double maxwell_juettner_rho_Q(struct parameters * params) double jffunc = 2.011 * exp(-pow(x, 1.035)/4.7) - cos(x/2.) * exp(-pow(x, 1.2)/2.73) - .011 * exp(-x / 47.2) + extraterm; + double k1 = gsl_sf_bessel_Kn(1, 1./params->theta_e); + double k2 = gsl_sf_bessel_Kn(2, 1./params->theta_e); + double k_ratio = (k2 > 0.) ? k1 / k2 : 1.; + double eps11m22 = jffunc * wp2 * pow(omega0, 2.) - / pow(2.*params->pi * params->nu, 4.) - * (gsl_sf_bessel_Kn(1, 1./params->theta_e) - / gsl_sf_bessel_Kn(2, 1./params->theta_e) - + 6. * params->theta_e) + / pow(2.*params->pi * params->nu, 4.) * (k_ratio + + 6. * params->theta_e) * pow(sin(params->observer_angle), 2.); double rhoq = 2. * params->pi * params->nu /(2. * params->speed_light) @@ -221,22 +225,16 @@ double maxwell_juettner_rho_V(struct parameters * params) double omega0 = params->electron_charge*params->magnetic_field / (params->mass_electron*params->speed_light); - double wp2 = 4. * params->pi * params->electron_density + double wp2 = 4. * params->pi * params->electron_density * pow(params->electron_charge, 2.) / params->mass_electron; /* argument for function g(X) (called shgmfunc) below */ double x = params->theta_e * sqrt(sqrt(2.) * sin(params->observer_angle) * (1.e3*omega0 / (2. * params->pi * params->nu))); - /* Approximate the Bessel functions if allowed */ - double k2=0, k0=0; - if (params->approximate && params->theta_e > 5) { - k0 = -log(1 / (2. * params->theta_e)) - 0.5772; - k2 = 2. * params->theta_e * params->theta_e; - } else { - k0 = gsl_sf_bessel_Kn(0, 1./params->theta_e); - k2 = gsl_sf_bessel_Kn(2, 1./params->theta_e); - } + double k0 = gsl_sf_bessel_Kn(0, 1./params->theta_e); + double k2 = gsl_sf_bessel_Kn(2, 1./params->theta_e); + double k_ratio = (k2 > 0.) ? k0 / k2 : 1.; /* There are several fits of rho_V phrased as functions of x */ // TODO add straight Bessel-approx -> constant extrapolation? @@ -244,11 +242,11 @@ double maxwell_juettner_rho_V(struct parameters * params) if (params->dexter_fit) { // Jason Dexter (2016) fits using the modified difference factor g(X) double shgmfunc = 0.43793091 * log(1. + 0.00185777 * pow(x, 1.50316886)); - fit_factor = (k0 - shgmfunc) / k2; + fit_factor = k_ratio - shgmfunc / k2; // TODO might be unstable way to phrase } else { // Shcherbakov fits. Good to the smallest Thetae at high freq but questionable for low frequencies double shgmfunc = 1 - 0.11*log(1 + 0.035*x); - fit_factor = k0 / k2 * shgmfunc; + fit_factor = k_ratio * shgmfunc; } double eps12 = wp2 * omega0 / pow((2. * params->pi * params->nu), 3.) From 07834a4e6b86a68aabeb5dfc7c073eebda150844 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Mon, 19 Jul 2021 22:36:32 -0500 Subject: [PATCH 66/93] Add back all original emission coefficients, pass the tests locally --- src/model_radiation.c | 6 +- src/symphony/maxwell_juettner_fits.c | 120 +++++++++++++++++++++++++++ 2 files changed, 124 insertions(+), 2 deletions(-) diff --git a/src/model_radiation.c b/src/model_radiation.c index a6ded82..ba7b2c4 100644 --- a/src/model_radiation.c +++ b/src/model_radiation.c @@ -193,11 +193,13 @@ void jar_calc_dist(int dist, int pol, double X[NDIM], double nu, double theta, // If performing unpolarized transport, calculate only what we need if (!pol || dist == E_UNPOL) { - *jI = j_nu_fit(¶msM, paramsM.STOKES_I) / (nu * nu); if (dist == E_THERMAL || dist == E_DEXTER_THERMAL || dist > 10){ + paramsM.dexter_fit = 2; // Signal symphony fits to use Leung+ + *jI = j_nu_fit(¶msM, paramsM.STOKES_I) / (nu * nu); double Bnuinv = Bnu_inv(nu, paramsM.theta_e); // Planck function *aI = *jI / Bnuinv; } else { + *jI = j_nu_fit(¶msM, paramsM.STOKES_I) / (nu * nu); *aI = alpha_nu_fit(¶msM, paramsM.STOKES_I) * nu; } } else { @@ -254,7 +256,7 @@ void jar_calc_dist(int dist, int pol, double X[NDIM], double nu, double theta, // ROTATIVITIES - paramsM.dexter_fit = 0; // TODO find a parameter to bring this back separately + paramsM.dexter_fit = 0; // Don't use the Dexter rhoV, as it's unstable at low temperature *rQ = rho_nu_fit(¶msM, paramsM.STOKES_Q) * nu; *rU = rho_nu_fit(¶msM, paramsM.STOKES_U) * nu; *rV = rho_nu_fit(¶msM, paramsM.STOKES_V) * nu; diff --git a/src/symphony/maxwell_juettner_fits.c b/src/symphony/maxwell_juettner_fits.c index 47f40d9..6c663f3 100644 --- a/src/symphony/maxwell_juettner_fits.c +++ b/src/symphony/maxwell_juettner_fits.c @@ -1,6 +1,17 @@ #include "maxwell_juettner.h" +#include "constants.h" +// Local functions for dispatched fits +double I_I(double x); +double I_Q(double x); +double I_V(double x); +double maxwell_juettner_dexter_I(struct parameters *params); +double maxwell_juettner_dexter_Q(struct parameters *params); +double maxwell_juettner_dexter_V(struct parameters *params); +double maxwell_juettner_leung_I(struct parameters *params); + +/********* PANDYA FITS *********/ /*maxwell_juettner_I: fitting formula for the emissivity (polarized in Stokes I) * produced by a Maxwell-Juettner (relativistic thermal) @@ -12,6 +23,11 @@ */ double maxwell_juettner_I(struct parameters *params) { + if (params->dexter_fit == 2) { + return maxwell_juettner_leung_I(params); + } else if (params->dexter_fit) { + return maxwell_juettner_dexter_I(params); + } double nu_c = get_nu_c(*params); double nu_s = (2./9.)*nu_c*sin(params->observer_angle)*params->theta_e @@ -44,6 +60,9 @@ double maxwell_juettner_I(struct parameters *params) */ double maxwell_juettner_Q(struct parameters *params) { + if (params->dexter_fit) { + return maxwell_juettner_dexter_Q(params); + } double nu_c = get_nu_c(*params); double nu_s = (2./9.)*nu_c*sin(params->observer_angle) @@ -77,6 +96,9 @@ double maxwell_juettner_Q(struct parameters *params) */ double maxwell_juettner_V(struct parameters *params) { + if (params->dexter_fit) { + return maxwell_juettner_dexter_V(params); + } double nu_c = get_nu_c(*params); double nu_s = (2./9.)*nu_c*sin(params->observer_angle)*params->theta_e @@ -254,3 +276,101 @@ double maxwell_juettner_rho_V(struct parameters * params) return 2. * params->pi * params->nu / params->speed_light * eps12; } + +/********* DEXTER FITS *********/ +double maxwell_juettner_dexter_I(struct parameters *params) +{ + double Ne = params->electron_density; + double nu = params->nu; + double Thetae = params->theta_e; + double B = params->magnetic_field; + double theta = params->observer_angle; + // Synchrotron emissivity + double nus = 3.0 * EE * B * sin(theta) / 4.0 / M_PI / ME / CL * Thetae * Thetae + 1.0; + double x = nu / nus; + + return Ne * EE * EE * nu / 2. / sqrt(3) / CL / Thetae / Thetae * I_I(x); // [g/s^2/cm = ergs/s/cm^3] +} + +double maxwell_juettner_dexter_Q(struct parameters *params) +{ + double Ne = params->electron_density; + double nu = params->nu; + double Thetae = params->theta_e; + double B = params->magnetic_field; + double theta = params->observer_angle; + double nus = 3.0 * EE * B * sin(theta) / 4.0 / M_PI / ME / CL * Thetae * Thetae + 1.0; + double x = nu / nus; + return -Ne * EE * EE * nu / 2. / sqrt(3) / CL / Thetae / Thetae * I_Q(x); // Translate to Symphony convention +} + +double maxwell_juettner_dexter_V(struct parameters *params) +{ + double Ne = params->electron_density; + double nu = params->nu; + double Thetae = params->theta_e; + double B = params->magnetic_field; + double theta = params->observer_angle; + double nus = 3.0 * EE * B * sin(theta) / 4.0 / M_PI / ME / CL * Thetae * Thetae + 1.0; + double x = nu / nus; + return 2. * Ne * EE * EE * nu / tan(theta) / 3. / sqrt(3) / CL / Thetae / Thetae / Thetae * I_V(x); +} + +// Supporting functions + +double I_I(double x) +{ + return 2.5651 * (1 + 1.92 * pow(x, -1. / 3.) + + 0.9977 * pow(x, -2. / 3.)) * exp(-1.8899 * pow(x, + 1. / + 3.)); +} + +double I_Q(double x) +{ + return 2.5651 * (1 + 0.93193 * pow(x, -1. / 3.) + + 0.499873 * pow(x, -2. / 3.)) * exp(-1.8899 * pow(x, + 1. / + 3.)); +} + +double I_V(double x) +{ + return (1.81348 / x + 3.42319 * pow(x, -2. / 3.) + + 0.0292545 * pow(x, -0.5) + 2.03773 * pow(x, + -1. / 3.)) * + exp(-1.8899 * pow(x, 1. / 3.)); +} + +/********* LEUNG FIT *********/ + +/* + * thermal synchrotron emissivity + * + * Interpolates between Petrosian limit and + * classical thermal synchrotron limit + * Good for Thetae >~ 1 + * See Leung+ 2011, restated Pandya+ 2016 + */ +double maxwell_juettner_leung_I(struct parameters *params) +{ + double Ne = params->electron_density; + double nu = params->nu; + double Thetae = params->theta_e; + double B = params->magnetic_field; + double theta = params->observer_angle; + + double K2 = gsl_sf_bessel_Kn(2,1./Thetae); + + double nuc = EE*B/(2.*M_PI*ME*CL); + double nus = (2./9.)*nuc*Thetae*Thetae*sin(theta); + + if(nu > 1.e12*nus) + return 0.; + + double x = nu/nus ; + double f = pow( pow(x,1./2.) + pow(2.,11./12.)*pow(x,1./6.), 2 ); + double j = (sqrt(2.)*M_PI*EE*EE*Ne*nus/(3.*CL*K2)) * f * exp(-pow(x,1./3.)); + + return j; +} From 649046f3bf43693ae07bca9d9c423ffa6263441c Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 20 Jul 2021 11:29:58 -0500 Subject: [PATCH 67/93] Revert premature optimization --- model/analytic/model.c | 13 +++++++------ model/ldi2/model.c | 2 +- model/riaf/model.c | 2 +- model/sphere/model.c | 2 +- model/thin_disk/model.c | 2 +- src/ipolarray.c | 10 ++++------ src/model.h | 2 +- src/model_radiation.c | 25 ++++++++++--------------- 8 files changed, 26 insertions(+), 32 deletions(-) diff --git a/model/analytic/model.c b/model/analytic/model.c index 2960a88..0cb2d2f 100644 --- a/model/analytic/model.c +++ b/model/analytic/model.c @@ -187,20 +187,21 @@ double get_model_ne(double X[NDIM]) return ( n_exp < 200 ) ? RHO_unit * exp(-n_exp) : 0; } -void get_model_jk(double X[NDIM], double nu, double theta, double *jnuinv, double *knuinv) +void get_model_jk(double X[NDIM], double Kcon[NDIM], double *jnuinv, double *knuinv) { // Emission model defined in Gold et al 2020 section 3 double n = get_model_ne(X); - // double Ucon[NDIM], Ucov[NDIM], Bcon[NDIM], Bcov[NDIM]; - // double Kcon[NDIM] = {0}; // TODO interface change if we ever need a real one here - // get_model_fourv(X, Kcon, Ucon, Ucov, Bcon, Bcov); + double Ucon[NDIM], Ucov[NDIM], Bcon[NDIM], Bcov[NDIM]; + double Kcon[NDIM] = {0}; // TODO interface change if we ever need a real one here + get_model_fourv(X, Kcon, Ucon, Ucov, Bcon, Bcov); + double nu = get_fluid_nu(Kcon, Ucov); *jnuinv = fmax( n * pow(nu / freqcgs, -alpha) / pow(nu, 2), 0); *knuinv = fmax( (A * n * pow(nu / freqcgs, -(2.5 + alpha)) + 1.e-54) * nu, 0); } -void get_model_jar(double X[NDIM], double nu, double theta, +void get_model_jar(double X[NDIM], double Kcon[NDIM], double *jI, double *jQ, double *jU, double *jV, double *aI, double *aQ, double *aU, double *aV, double *rQ, double *rU, double *rV) @@ -213,7 +214,7 @@ void get_model_jar(double X[NDIM], double nu, double theta, // and set the rest to zero // Of course, you can be more elaborate double j, k; - get_model_jk(X, nu, theta, &j, &k); + get_model_jk(X, Kcon, &j, &k); *jI = j; *jQ = 0; diff --git a/model/ldi2/model.c b/model/ldi2/model.c index 6a404cb..72c8df6 100644 --- a/model/ldi2/model.c +++ b/model/ldi2/model.c @@ -123,7 +123,7 @@ void record_stokes_parameters(double SI, double SQ, double SU, double SV, double ++nstep; } -void get_model_jar(double X[NDIM], double nu, double theta, +void get_model_jar(double X[NDIM], double Kcon[NDIM], double *jI, double *jQ, double *jU, double *jV, double *aI, double *aQ, double *aU, double *aV, double *rQ, double *rU, double *rV) diff --git a/model/riaf/model.c b/model/riaf/model.c index baa5ff9..ae15b78 100644 --- a/model/riaf/model.c +++ b/model/riaf/model.c @@ -382,7 +382,7 @@ void update_data_until(double *tA, double *tB, double tgt) {return;} // This is only called for trace file output, and doesn't really apply to analytic models void get_model_primitives(double X[NDIM], double *p) {return;} void get_model_jk(double X[NDIM], double Kcon[NDIM], double *jnuinv, double *knuinv) {return;} -void get_model_jar(double X[NDIM], double nu, double thetae, +void get_model_jar(double X[NDIM], double Kcon[NDIM], double *jI, double *jQ, double *jU, double *jV, double *aI, double *aQ, double *aU, double *aV, double *rQ, double *rU, double *rV) {return;} diff --git a/model/sphere/model.c b/model/sphere/model.c index ded087f..1e65763 100644 --- a/model/sphere/model.c +++ b/model/sphere/model.c @@ -215,7 +215,7 @@ int radiating_region(double X[NDIM]) } // In case we want to mess with emissivities directly -void get_model_jar(double X[NDIM], double nu, double thetae, +void get_model_jar(double X[NDIM], double Kcon[NDIM], double *jI, double *jQ, double *jU, double *jV, double *aI, double *aQ, double *aU, double *aV, double *rQ, double *rU, double *rV) {return;} diff --git a/model/thin_disk/model.c b/model/thin_disk/model.c index b391071..e4bdd7b 100644 --- a/model/thin_disk/model.c +++ b/model/thin_disk/model.c @@ -377,7 +377,7 @@ void get_model_primitives(double X[NDIM], double *p) {return;} void update_data(double *tA, double *tB) {return;} void update_data_until(double *tA, double *tB, double tgt) {return;} // In case we want to mess with emissivities directly -void get_model_jar(double X[NDIM], double nu, double thetae, +void get_model_jar(double X[NDIM], double Kcon[NDIM], double *jI, double *jQ, double *jU, double *jV, double *aI, double *aQ, double *aU, double *aV, double *rQ, double *rU, double *rV) {return;} diff --git a/src/ipolarray.c b/src/ipolarray.c index 5083cf8..af81754 100644 --- a/src/ipolarray.c +++ b/src/ipolarray.c @@ -322,13 +322,8 @@ int evolve_N(double Xi[NDIM], double Kconi[NDIM], int oddflag = 0; - // get fluid parameters at Xf - get_model_fourv(Xf, Kconf, Ucon, Ucov, Bcon, Bcov); - // evaluate transport coefficients - double nu = get_fluid_nu(Kconf, Ucov); - double theta = get_bk_angle(Xf, Kconf, Ucov, Bcon, Bcov); - jar_calc(Xf, nu, theta, + jar_calc(Xf, Kconf, &jI, &jQ, &jU, &jV, &aI, &aQ, &aU, &aV, &rQ, &rU, &rV, params); if (zero_emission) { @@ -338,6 +333,9 @@ int evolve_N(double Xi[NDIM], double Kconi[NDIM], jV = 0.; } + // get fluid parameters at Xf + get_model_fourv(Xf, Kconf, Ucon, Ucov, Bcon, Bcov); + // Guess B if we *absolutely must* // Note get_model_b (rightly) returns 0 outside the domain, // but we can cling to the 4-vectors a bit longer diff --git a/src/model.h b/src/model.h index 0e27266..3e0ccf5 100644 --- a/src/model.h +++ b/src/model.h @@ -26,7 +26,7 @@ double get_model_beta(double X[NDIM]); double get_model_ne(double X[NDIM]); // For exotic or custom distributions -void get_model_jar(double X[NDIM], double nu, double thetae, +void get_model_jar(double X[NDIM], double Kcon[NDIM], double *jI, double *jQ, double *jU, double *jV, double *aI, double *aQ, double *aU, double *aV, double *rQ, double *rU, double *rV); diff --git a/src/model_radiation.c b/src/model_radiation.c index ba7b2c4..1e02904 100644 --- a/src/model_radiation.c +++ b/src/model_radiation.c @@ -89,12 +89,12 @@ void try_set_radiation_parameter(const char *word, const char *value) * This is a wrapper to jar_calc_dist, see implementation there * Also checks for NaN coefficients when built with DEBUG, for quicker debugging */ -void jar_calc(double X[NDIM], double nu, double theta, +void jar_calc(double X[NDIM], Kcon[NDIM], double *jI, double *jQ, double *jU, double *jV, double *aI, double *aQ, double *aU, double *aV, double *rQ, double *rU, double *rV, Params *params) { - jar_calc_dist(params->emission_type, 1, X, nu, theta, jI, jQ, jU, jV, aI, aQ, aU, aV, rQ, rU, rV); + jar_calc_dist(params->emission_type, 1, X, Kcon[NDIM], jI, jQ, jU, jV, aI, aQ, aU, aV, rQ, rU, rV); // This wrapper can be used to call jar_calc_dist differently in e.g. funnel vs jet, or // depending on local fluid parameters, whatever @@ -107,13 +107,7 @@ void jar_calc(double X[NDIM], double nu, double theta, void get_jkinv(double X[NDIM], double Kcon[NDIM], double *jI, double *aI, Params *params) { double jQ, jU, jV, aQ, aU, aV, rQ, rU, rV; - double Ucon[NDIM], Ucov[NDIM], Bcon[NDIM], Bcov[NDIM]; - - get_model_fourv(X, Kcon, Ucon, Ucov, Bcon, Bcov); - double nu = get_fluid_nu(Kcon, Ucov); - double theta = get_bk_angle(X, Kcon, Ucov, Bcon, Bcov); - - jar_calc_dist(params->emission_type, 0, X, nu, theta, jI, &jQ, &jU, &jV, aI, &aQ, &aU, &aV, &rQ, &rU, &rV); + jar_calc_dist(params->emission_type, 0, X, Kcon[NDIM], jI, &jQ, &jU, &jV, aI, &aQ, &aU, &aV, &rQ, &rU, &rV); } /** @@ -136,7 +130,7 @@ void get_jkinv(double X[NDIM], double Kcon[NDIM], double *jI, double *aI, Params * * */ -void jar_calc_dist(int dist, int pol, double X[NDIM], double nu, double theta, +void jar_calc_dist(int dist, int pol, double X[NDIM], double Kcon[NDIM], double *jI, double *jQ, double *jU, double *jV, double *aI, double *aQ, double *aU, double *aV, double *rQ, double *rU, double *rV) @@ -155,17 +149,19 @@ void jar_calc_dist(int dist, int pol, double X[NDIM], double nu, double theta, // Call through to the model if it's responsible for this job if (dist == E_CUSTOM) { - get_model_jar(X, nu, theta, jI, jQ, jU, jV, aI, aQ, aU, aV, rQ, rU, rV); + get_model_jar(X, Kcon, jI, jQ, jU, jV, aI, aQ, aU, aV, rQ, rU, rV); return; } struct parameters paramsM; setConstParams(¶msM); // Set the parameters common to all distributions... - paramsM.magnetic_field = get_model_b(X); + double Ucon[NDIM], Ucov[NDIM], Bcon[NDIM], Bcov[NDIM]; + get_model_fourv(X, Kcon, Ucon, Ucov, Bcon, Bcov); paramsM.electron_density = Ne; - paramsM.nu = nu; - paramsM.observer_angle = theta; + paramsM.magnetic_field = get_model_b(X); + paramsM.nu = get_fluid_nu(Kcon, Ucov); + paramsM.observer_angle = get_bk_angle(X, Kcon, Ucov, Bcon, Bcov); // ...and then the specific distribution and its parameters. switch (dist) { @@ -203,7 +199,6 @@ void jar_calc_dist(int dist, int pol, double X[NDIM], double nu, double theta, *aI = alpha_nu_fit(¶msM, paramsM.STOKES_I) * nu; } } else { - // Avoid issues directly along field lines if (theta <= 0 || theta >= M_PI) { *jI = 0.0; *jQ = 0.0; *jU = 0.0; *jV = 0.0; From 879a49959b0527b314aab8d590a9f0b77478f36d Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 20 Jul 2021 12:00:27 -0500 Subject: [PATCH 68/93] Fix compile after reverting calling convention --- model/analytic/model.c | 1 - model/iharm/model.c | 2 +- src/io.c | 5 +++-- src/model_radiation.c | 14 ++++++++------ src/model_radiation.h | 2 +- 5 files changed, 13 insertions(+), 11 deletions(-) diff --git a/model/analytic/model.c b/model/analytic/model.c index 0cb2d2f..2a5b905 100644 --- a/model/analytic/model.c +++ b/model/analytic/model.c @@ -193,7 +193,6 @@ void get_model_jk(double X[NDIM], double Kcon[NDIM], double *jnuinv, double *knu double n = get_model_ne(X); double Ucon[NDIM], Ucov[NDIM], Bcon[NDIM], Bcov[NDIM]; - double Kcon[NDIM] = {0}; // TODO interface change if we ever need a real one here get_model_fourv(X, Kcon, Ucon, Ucov, Bcon, Bcov); double nu = get_fluid_nu(Kcon, Ucov); diff --git a/model/iharm/model.c b/model/iharm/model.c index e84f7ae..67efec5 100644 --- a/model/iharm/model.c +++ b/model/iharm/model.c @@ -1424,7 +1424,7 @@ int radiating_region(double X[NDIM]) } // In case we want to mess with emissivities directly -void get_model_jar(double X[NDIM], double nu, double theta, +void get_model_jar(double X[NDIM], double Kcon[NDIM], double *jI, double *jQ, double *jU, double *jV, double *aI, double *aQ, double *aU, double *aV, double *rQ, double *rU, double *rV) {return;} diff --git a/src/io.c b/src/io.c index 7715bf1..4b43a6f 100644 --- a/src/io.c +++ b/src/io.c @@ -353,14 +353,15 @@ void dump_var_along(int i, int j, int nstep, struct of_traj *traj, int nx, int n double e2Mid[NDIM], e2Final[NDIM], e2Cov[NDIM]; get_model_fourv(traj[i].X, traj[i].Kcon, Ucont, Ucovt, Bcont, Bcovt); + // Record scaled values b[i] = get_model_b(traj[i].X); ne[i] = get_model_ne(traj[i].X); thetae[i] = get_model_thetae(traj[i].X); nu[i] = get_fluid_nu(traj[i].Kcon, Ucovt); mu[i] = get_bk_angle(traj[i].X, traj[i].Kcon, Ucovt, Bcont, Bcovt); - // Record trajectory - jar_calc(traj[i].X, nu[i], mu[i], &(j_inv[i*NDIM]), &(j_inv[i*NDIM+1]), &(j_inv[i*NDIM+2]), &(j_inv[i*NDIM+3]), + // Record emission coefficients + jar_calc(traj[i].X, traj[i].Kcon, &(j_inv[i*NDIM]), &(j_inv[i*NDIM+1]), &(j_inv[i*NDIM+2]), &(j_inv[i*NDIM+3]), &(alpha_inv[i*NDIM]), &(alpha_inv[i*NDIM+1]), &(alpha_inv[i*NDIM+2]), &(alpha_inv[i*NDIM+3]), &(rho_inv[i*NDIM+1]), &(rho_inv[i*NDIM+2]), &(rho_inv[i*NDIM+3]), params); get_jkinv(traj[i].X, traj[i].Kcon, &(j_unpol[i]), &(k_unpol[i]), params); diff --git a/src/model_radiation.c b/src/model_radiation.c index 1e02904..3a0e2c5 100644 --- a/src/model_radiation.c +++ b/src/model_radiation.c @@ -50,7 +50,7 @@ void get_model_powerlaw_vals(double X[NDIM], double *p, double *n, void get_model_kappa(double X[NDIM], double *kappa, double *kappa_width); /* Get coeffs from a specific distribution */ -void jar_calc_dist(int dist, int pol, double X[NDIM], double nu, double theta, +void jar_calc_dist(int dist, int pol, double X[NDIM], double Kcon[NDIM], double *jI, double *jQ, double *jU, double *jV, double *aI, double *aQ, double *aU, double *aV, double *rQ, double *rU, double *rV); @@ -89,12 +89,12 @@ void try_set_radiation_parameter(const char *word, const char *value) * This is a wrapper to jar_calc_dist, see implementation there * Also checks for NaN coefficients when built with DEBUG, for quicker debugging */ -void jar_calc(double X[NDIM], Kcon[NDIM], +void jar_calc(double X[NDIM], double Kcon[NDIM], double *jI, double *jQ, double *jU, double *jV, double *aI, double *aQ, double *aU, double *aV, double *rQ, double *rU, double *rV, Params *params) { - jar_calc_dist(params->emission_type, 1, X, Kcon[NDIM], jI, jQ, jU, jV, aI, aQ, aU, aV, rQ, rU, rV); + jar_calc_dist(params->emission_type, 1, X, Kcon, jI, jQ, jU, jV, aI, aQ, aU, aV, rQ, rU, rV); // This wrapper can be used to call jar_calc_dist differently in e.g. funnel vs jet, or // depending on local fluid parameters, whatever @@ -107,7 +107,7 @@ void jar_calc(double X[NDIM], Kcon[NDIM], void get_jkinv(double X[NDIM], double Kcon[NDIM], double *jI, double *aI, Params *params) { double jQ, jU, jV, aQ, aU, aV, rQ, rU, rV; - jar_calc_dist(params->emission_type, 0, X, Kcon[NDIM], jI, &jQ, &jU, &jV, aI, &aQ, &aU, &aV, &rQ, &rU, &rV); + jar_calc_dist(params->emission_type, 0, X, Kcon, jI, &jQ, &jU, &jV, aI, &aQ, &aU, &aV, &rQ, &rU, &rV); } /** @@ -158,10 +158,12 @@ void jar_calc_dist(int dist, int pol, double X[NDIM], double Kcon[NDIM], // Set the parameters common to all distributions... double Ucon[NDIM], Ucov[NDIM], Bcon[NDIM], Bcov[NDIM]; get_model_fourv(X, Kcon, Ucon, Ucov, Bcon, Bcov); + double nu = get_fluid_nu(Kcon, Ucov); + double theta = get_bk_angle(X, Kcon, Ucov, Bcon, Bcov); paramsM.electron_density = Ne; + paramsM.nu = nu; + paramsM.observer_angle = theta; paramsM.magnetic_field = get_model_b(X); - paramsM.nu = get_fluid_nu(Kcon, Ucov); - paramsM.observer_angle = get_bk_angle(X, Kcon, Ucov, Bcon, Bcov); // ...and then the specific distribution and its parameters. switch (dist) { diff --git a/src/model_radiation.h b/src/model_radiation.h index e5e2583..c6aee22 100644 --- a/src/model_radiation.h +++ b/src/model_radiation.h @@ -14,7 +14,7 @@ void try_set_radiation_parameter(const char *word, const char *value); /* transfer coefficients in tetrad frame */ -void jar_calc(double X[NDIM], double nu, double theta, +void jar_calc(double X[NDIM], double Kcon[NDIM], double *jI, double *jQ, double *jU, double *jV, double *aI, double *aQ, double *aU, double *aV, double *rQ, double *rU, double *rV, Params *params); From 64c939a1a68ea7c10fccdcd5e1ff66887ccb7bff Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Fri, 23 Jul 2021 12:39:18 -0500 Subject: [PATCH 69/93] Fix old NaN in rho_Q fits which started showing up in pixels after analytic solution modifications --- src/model_radiation.c | 11 ++++++++--- src/symphony/maxwell_juettner_fits.c | 13 ++++++++----- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/model_radiation.c b/src/model_radiation.c index 25b5590..204a13f 100644 --- a/src/model_radiation.c +++ b/src/model_radiation.c @@ -378,15 +378,20 @@ void shcherbakov_rho_fit(double Ne, double nu, double Thetae, double B, double t double Xe = Thetae * sqrt(sqrt(2) * sin(theta) * (1.e3 * omega0 / 2. / M_PI / nu)); // These are the Dexter (2016) fit actually + double k1 = gsl_sf_bessel_Kn(1, Thetaer); + double k2 = gsl_sf_bessel_Kn(2, Thetaer); + double k_ratio = (k2 > 0) ? k1/k2 : 1; *rQ = 2. * M_PI * nu / 2. / CL * wp2 * omega0 * omega0 / pow(2 * M_PI * nu, 4) * - jffunc(Xe) * (gsl_sf_bessel_Kn(1, Thetaer) / gsl_sf_bessel_Kn(2, Thetaer) + - 6. * Thetae) * sin(theta) * sin(theta); + jffunc(Xe) * (k_ratio + 6. * Thetae) * sin(theta) * sin(theta); + *rU = 0.0; // Shcherbakov fit for rV. Possibly questionable at very low frequency // Note the real bessel functions. Slow? + double k0 = gsl_sf_bessel_Kn(1, Thetaer); + k_ratio = (k2 > 0) ? k0/k2 : 1; *rV = 2.0 * M_PI * nu / CL * wp2 * omega0 / pow(2. * M_PI * nu, 3) * - gsl_sf_bessel_Kn(0, Thetaer) / (gsl_sf_bessel_Kn(2, Thetaer)+SMALL) * g(Xe) * cos(theta); + k_ratio * g(Xe) * cos(theta); } void piecewise_rho_fit(double Ne, double nu, double Thetae, double B, double theta, diff --git a/src/symphony/maxwell_juettner_fits.c b/src/symphony/maxwell_juettner_fits.c index b818ff7..f51aef6 100644 --- a/src/symphony/maxwell_juettner_fits.c +++ b/src/symphony/maxwell_juettner_fits.c @@ -194,11 +194,13 @@ double maxwell_juettner_rho_Q(struct parameters * params) double jffunc = 2.011 * exp(-pow(x, 1.035)/4.7) - cos(x/2.) * exp(-pow(x, 1.2)/2.73) - .011 * exp(-x / 47.2) + extraterm; + double k1 = gsl_sf_bessel_Kn(1, 1./params->theta_e); + double k2 = gsl_sf_bessel_Kn(2, 1./params->theta_e); + double k_ratio = (k2 > 0) ? k1/k2 : 1; + double eps11m22 = jffunc * wp2 * pow(omega0, 2.) / pow(2.*params->pi * params->nu, 4.) - * (gsl_sf_bessel_Kn(1, 1./params->theta_e) - / gsl_sf_bessel_Kn(2, 1./params->theta_e) - + 6. * params->theta_e) + * (k_ratio + 6. * params->theta_e) * pow(sin(params->observer_angle), 2.); double rhoq = 2. * params->pi * params->nu /(2. * params->speed_light) @@ -241,14 +243,15 @@ double maxwell_juettner_rho_V(struct parameters * params) /* There are several fits of rho_V phrased as functions of x */ // TODO add straight Bessel-approx -> constant extrapolation? double fit_factor = 0; - if (params->dexter_fit) { + if (params->dexter_fit && k2 > 0) { // TODO Further limit the usage here to match grtrans // Jason Dexter (2016) fits using the modified difference factor g(X) double shgmfunc = 0.43793091 * log(1. + 0.00185777 * pow(x, 1.50316886)); fit_factor = (k0 - shgmfunc) / k2; } else { // Shcherbakov fits. Good to the smallest Thetae at high freq but questionable for low frequencies double shgmfunc = 1 - 0.11*log(1 + 0.035*x); - fit_factor = k0 / k2 * shgmfunc; + double k_ratio = (k2 > 0) ? k0/k2 : 1; + fit_factor = k_ratio * shgmfunc; } double eps12 = wp2 * omega0 / pow((2. * params->pi * params->nu), 3.) From 1e14c7a977071608326dfed2ba7336a8d77f57dd Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Fri, 30 Jul 2021 11:19:20 -0500 Subject: [PATCH 70/93] Eliminate old print statements from Ressler branch (again?) and fix a bug introduced by NaN fix --- model/iharm/model.c | 13 ------------- src/model_radiation.c | 2 +- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/model/iharm/model.c b/model/iharm/model.c index f04a540..8ce8f3e 100644 --- a/model/iharm/model.c +++ b/model/iharm/model.c @@ -957,19 +957,6 @@ void load_data(int n, char *fnam, int dumpidx, int verbose) } else if (dumpfile_format == FORMAT_HAMR_EKS) { load_hamr_data(n, fnam, dumpidx, verbose); } - - // FIXME - if (1==1) { - double X[4]; - ijktoX(140, 50, 70, X); - dump_at_X(X); - } - - for (int k=1; kb[141][65][k]; - fprintf(stderr, "%d %g\n", k, bsq); - } } void populate_boundary_conditions(int n) diff --git a/src/model_radiation.c b/src/model_radiation.c index 204a13f..56dc3ae 100644 --- a/src/model_radiation.c +++ b/src/model_radiation.c @@ -388,7 +388,7 @@ void shcherbakov_rho_fit(double Ne, double nu, double Thetae, double B, double t // Shcherbakov fit for rV. Possibly questionable at very low frequency // Note the real bessel functions. Slow? - double k0 = gsl_sf_bessel_Kn(1, Thetaer); + double k0 = gsl_sf_bessel_Kn(0, Thetaer); k_ratio = (k2 > 0) ? k0/k2 : 1; *rV = 2.0 * M_PI * nu / CL * wp2 * omega0 / pow(2. * M_PI * nu, 3) * k_ratio * g(Xe) * cos(theta); From 4ee575ab43da819f25b71592b6574e76c24b3897 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Fri, 30 Jul 2021 17:09:18 -0500 Subject: [PATCH 71/93] Tweaks for MKS2 systems after merging simcoords --- model/iharm/model.c | 10 ++++++++++ src/ipolarray.c | 8 +++++++- src/model_radiation.c | 5 +---- src/symphony/maxwell_juettner_fits.c | 10 +++++++++- 4 files changed, 27 insertions(+), 6 deletions(-) diff --git a/model/iharm/model.c b/model/iharm/model.c index ae2c736..d184184 100644 --- a/model/iharm/model.c +++ b/model/iharm/model.c @@ -965,6 +965,16 @@ void init_koral_grid(char *fnam, int dumpidx) hdf5_read_single_val(&(mp_koral_mks3.my2), "mksmy2", H5T_IEEE_F64LE); hdf5_read_single_val(&(mp_koral_mks3.mp0), "mksmp0", H5T_IEEE_F64LE); fprintf(stderr, "KORAL simulation was run with MKS3 coordinates.\n"); + } else if (strcmp(metric_run, "MKS2") == 0) { + simcoords = SIMCOORDS_KORAL_MKS3; + hdf5_read_single_val(&a, "bhspin", H5T_IEEE_F64LE); + hdf5_set_directory("/header/geom/mks2/"); + hdf5_read_single_val(&(mp_koral_mks3.r0), "mksr0", H5T_IEEE_F64LE); + hdf5_read_single_val(&(mp_koral_mks3.h0), "mksh0", H5T_IEEE_F64LE); + mp_koral_mks3.my1 = 0; + mp_koral_mks3.my2 = 0; + mp_koral_mks3.mp0 = 0; + fprintf(stderr, "KORAL simulation was run with MKS2 coordinates.\n"); } else if (strcmp(metric_run, "JETCOORDS") == 0) { simcoords = SIMCOORDS_KORAL_JETCOORDS; hdf5_read_single_val(&a, "bhspin", H5T_IEEE_F64LE); diff --git a/src/ipolarray.c b/src/ipolarray.c index c414223..5392d92 100644 --- a/src/ipolarray.c +++ b/src/ipolarray.c @@ -250,6 +250,12 @@ void push_polar(double Xi[NDIM], double Xm[NDIM], double Xf[NDIM], double dl = dlam / (L_unit * HPL / (ME * CL * CL)); #endif + if (Xm[0] == 0 && Xm[1] == 0 && Xm[2] == 0 && Xm[3] == 0) + { + fprintf(stderr, "Called push_polar at origin! You are probably trying to trace something unsupported.\n"); + exit(-1); + } + /* find the connection */ double lconn[NDIM][NDIM][NDIM]; get_connection(Xm, lconn); @@ -274,7 +280,7 @@ void push_polar(double Xi[NDIM], double Xm[NDIM], double Xf[NDIM], Parallel Transport a real vector over dl (see push_polar above) - +TODO should probably go in io.c */ void parallel_transport_vector(double Xi[NDIM], double Xm[NDIM], double Xf[NDIM], double Ki[NDIM], double Km[NDIM], double Kf[NDIM], diff --git a/src/model_radiation.c b/src/model_radiation.c index 3a0e2c5..02c7969 100644 --- a/src/model_radiation.c +++ b/src/model_radiation.c @@ -176,10 +176,7 @@ void jar_calc_dist(int dist, int pol, double X[NDIM], double Kcon[NDIM], case E_KAPPA: // Kappa fits (Pandya + Marszewski) paramsM.distribution = paramsM.KAPPA_DIST; paramsM.theta_e = get_model_thetae(X); - double kappa, kappa_width; - get_model_kappa(X, &kappa, &kappa_width); - paramsM.kappa = kappa; - paramsM.kappa_width = kappa_width; + get_model_kappa(X, &(paramsM.kappa), &(paramsM.kappa_width)); break; case E_POWERLAW: // Powerlaw fits (Pandya, no rotativities!) paramsM.distribution = paramsM.POWER_LAW; diff --git a/src/symphony/maxwell_juettner_fits.c b/src/symphony/maxwell_juettner_fits.c index 6c663f3..efc3d91 100644 --- a/src/symphony/maxwell_juettner_fits.c +++ b/src/symphony/maxwell_juettner_fits.c @@ -2,6 +2,8 @@ #include "constants.h" +#include + // Local functions for dispatched fits double I_I(double x); double I_Q(double x); @@ -360,7 +362,7 @@ double maxwell_juettner_leung_I(struct parameters *params) double B = params->magnetic_field; double theta = params->observer_angle; - double K2 = gsl_sf_bessel_Kn(2,1./Thetae); + double K2 = fmax(gsl_sf_bessel_Kn(2,1./Thetae), SMALL); double nuc = EE*B/(2.*M_PI*ME*CL); double nus = (2./9.)*nuc*Thetae*Thetae*sin(theta); @@ -372,5 +374,11 @@ double maxwell_juettner_leung_I(struct parameters *params) double f = pow( pow(x,1./2.) + pow(2.,11./12.)*pow(x,1./6.), 2 ); double j = (sqrt(2.)*M_PI*EE*EE*Ne*nus/(3.*CL*K2)) * f * exp(-pow(x,1./3.)); +#if DEBUG + if (isnan(j) || isinf(j)) { + fprintf(stderr, "j nan in Leung fit: j %g f %g x %g nu %g nus %g nuc %g K2 %g Thetae %g\n", j, f, x, nu, nus, nuc, K2, Thetae); + } +#endif + return j; } From 245ce8217d83e1461e80ac03e75493c74c970a2b Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 3 Aug 2021 14:49:55 -0500 Subject: [PATCH 72/93] Clean up printing B field warning, as reversal is now off by default --- src/main.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main.c b/src/main.c index 74d7b15..a907b0c 100644 --- a/src/main.c +++ b/src/main.c @@ -65,7 +65,6 @@ int main(int argc, char *argv[]) { // motd fprintf(stderr, "%s. githash: %s\n", VERSION_STRING, xstr(VERSION)); - fprintf(stderr,"\nWARNING. iharm/model.c CONTAINS A CODE BLOCK TO REVERSE B FIELD. CHECK BEFORE RUNNING\n"); fprintf(stderr, "notes: %s\n\n", xstr(NOTES)); // initialization From f2905d602955e5e370a95b93d06a7838fcda58ee Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 3 Aug 2021 14:51:26 -0500 Subject: [PATCH 73/93] Add a floor for cooling times (ceiling on temperature) shorter than the dynamical time; option for just how short is okay, as factor of dynamical time. --- model/iharm/model.c | 39 +++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/model/iharm/model.c b/model/iharm/model.c index d184184..a3700cb 100644 --- a/model/iharm/model.c +++ b/model/iharm/model.c @@ -50,6 +50,10 @@ static char fnam[STRLEN] = "dump.h5"; static double tp_over_te = 3.; static double trat_small = 1.; static double trat_large = 40.; +// Minimum number of dynamical times the cooling time must +// undershoot to be considered "small" +// lower values -> higher max T_e, higher values are restrictive +static double cooling_dynamical_times = 1.; static int dumpskip = 1; static int dumpmin, dumpmax, dumpidx; @@ -123,6 +127,7 @@ void try_set_model_parameter(const char *word, const char *value) set_by_word_val(word, value, "trat_large", &trat_large, TYPE_DBL); set_by_word_val(word, value, "sigma_cut", &sigma_cut, TYPE_DBL); set_by_word_val(word, value, "beta_crit", &beta_crit, TYPE_DBL); + set_by_word_val(word, value, "cooling_dynamical_times", &cooling_dynamical_times, TYPE_DBL); set_by_word_val(word, value, "rmax_geo", &rmax_geo, TYPE_DBL); set_by_word_val(word, value, "rmin_geo", &rmin_geo, TYPE_DBL); @@ -509,6 +514,10 @@ void set_units() void init_physical_quantities(int n) { +#if DEBUG + int ceilings = 0; +#endif + // cover everything, even ghost zones #pragma omp parallel for collapse(3) for (int i = 0; i < N1+2; i++) { @@ -548,17 +557,37 @@ void init_physical_quantities(int n) } else { data[n]->thetae[i][j][k] = Thetae_unit*data[n]->p[UU][i][j][k]/data[n]->p[KRHO][i][j][k]; } - data[n]->thetae[i][j][k] = fmax(data[n]->thetae[i][j][k], 1.e-3); #if DEBUG if(isnan(data[n]->thetae[i][j][k])) { data[n]->thetae[i][j][k] = 0.0; - fprintf(stderr, "\nZero Thetae! Prims %g %g %g %g %g %g %g %g\n", data[n]->p[KRHO][i][j][k], data[n]->p[UU][i][j][k], + fprintf(stderr, "\nNaN Thetae! Prims %g %g %g %g %g %g %g %g\n", data[n]->p[KRHO][i][j][k], data[n]->p[UU][i][j][k], data[n]->p[U1][i][j][k], data[n]->p[U2][i][j][k], data[n]->p[U3][i][j][k], data[n]->p[B1][i][j][k], data[n]->p[B2][i][j][k], data[n]->p[B3][i][j][k]); - fprintf(stderr, "Setting zero temp!\n"); + fprintf(stderr, "Setting floor temp!\n"); } #endif + // Enforce a max on Thetae based on cooling time == dynamical time + if (cooling_dynamical_times > 1e-20) { + double X[NDIM]; + ijktoX(i, j, k, X); + double r, th; + bl_coord(X, &r, &th); + // Calculate thetae_max based on matching the cooling time w/dynamical time + // Makes sure to use b w/units, but r has already been rescaled + double Thetae_max_dynamical = 1 / cooling_dynamical_times * 7.71232e46 / 2 / MBH * pow(data[n]->b[i][j][k], -2) * pow(r * sin(th), -1.5); +#if DEBUG + if (Thetae_max_dynamical < data[n]->thetae[i][j][k]) { + if (r > 2) fprintf(stderr, "Ceiling on temp! %g < %g, r, th %g %g\n", Thetae_max_dynamical, data[n]->thetae[i][j][k], r, th); + ceilings++; + } +#endif + data[n]->thetae[i][j][k] = fmin(data[n]->thetae[i][j][k], Thetae_max_dynamical); + } + + // Apply floor last in case the above is a very restrictive ceiling + data[n]->thetae[i][j][k] = fmax(data[n]->thetae[i][j][k], 1.e-3); + // Preserve sigma for cutting along geodesics, and for variable-kappa model data[n]->sigma[i][j][k] = sigma_m; // Also record beta, for variable-kappa model @@ -574,7 +603,9 @@ void init_physical_quantities(int n) } } } - +#if DEBUG + fprintf(stderr, "TOTAL TEMPERATURE CEILING ZONES: %d of %d\n", ceilings, (N1+2)*(N2+2)*(N3+2)); +#endif } void init_storage(void) From 44956033e16f960abf9068c62e1c75e200d5c660 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 10 Aug 2021 13:01:14 -0500 Subject: [PATCH 74/93] Fix some printed output when running on KORAL dumps. Add sigma, beta to trace files --- model/iharm/model.c | 3 +++ src/io.c | 12 ++++++++++-- src/simcoords.c | 2 +- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/model/iharm/model.c b/model/iharm/model.c index dc5aa2d..73df27f 100644 --- a/model/iharm/model.c +++ b/model/iharm/model.c @@ -1091,7 +1091,10 @@ void init_koral_grid(char *fnam, int dumpidx) stopx[1] = startx[1]+N1*dx[1]; stopx[2] = startx[2]+N2*dx[2]; stopx[3] = startx[3]+N3*dx[3]; + MULOOP cstartx[mu] = startx[mu]; + MULOOP cstopx[mu] = stopx[mu]; + fprintf(stderr, "KORAL coordinates dx: %g %g %g\n", dx[1], dx[2], dx[3]); fprintf(stderr, "Native coordinate start: %g %g %g stop: %g %g %g\n", cstartx[1], cstartx[2], cstartx[3], cstopx[1], cstopx[2], cstopx[3]); fprintf(stderr, "Grid start: %g %g %g stop: %g %g %g\n", diff --git a/src/io.c b/src/io.c index df578bd..3ed68db 100644 --- a/src/io.c +++ b/src/io.c @@ -298,6 +298,9 @@ void dump_var_along(int i, int j, int nstep, struct of_traj *traj, int nx, int n double *nu = calloc(nsteps, sizeof(double)); double *mu = calloc(nsteps, sizeof(double)); + double *sigma = calloc(nsteps, sizeof(double)); + double *beta = calloc(nsteps, sizeof(double)); + double *dl = calloc(nsteps, sizeof(double)); double *X = calloc(NDIM*nsteps, sizeof(double)); // Vectors aren't really needed. Put back in behind flag if it comes up @@ -345,7 +348,7 @@ void dump_var_along(int i, int j, int nstep, struct of_traj *traj, int nx, int n double *e2Transported = calloc(NDIM*nsteps,sizeof(double)); double *e2TransportedCov = calloc(NDIM*nsteps,sizeof(double)); - for (int i=nstep; i > 0; --i) { + for (int i=nstep-1; i > 0; --i) { // Record ambient variables get_model_primitives(traj[i].X, &(prims[i*nprims])); double Ucont[NDIM], Ucovt[NDIM], Bcont[NDIM], Bcovt[NDIM]; @@ -359,6 +362,8 @@ void dump_var_along(int i, int j, int nstep, struct of_traj *traj, int nx, int n thetae[i] = get_model_thetae(traj[i].X); nu[i] = get_fluid_nu(traj[i].Kcon, Ucovt); mu[i] = get_bk_angle(traj[i].X, traj[i].Kcon, Ucovt, Bcont, Bcovt); + sigma[i] = get_model_sigma(traj[i].X); + beta[i] = get_model_beta(traj[i].X); // Record emission coefficients jar_calc(traj[i].X, traj[i].Kcon, &(j_inv[i*NDIM]), &(j_inv[i*NDIM+1]), &(j_inv[i*NDIM+2]), &(j_inv[i*NDIM+3]), @@ -452,6 +457,8 @@ void dump_var_along(int i, int j, int nstep, struct of_traj *traj, int nx, int n hdf5_write_chunked_array(thetae, "thetae", 3, fdims_s, fstart_s, fcount_s, mdims_s, mstart_s, chunk_s, H5T_IEEE_F64LE); hdf5_write_chunked_array(nu, "nu", 3, fdims_s, fstart_s, fcount_s, mdims_s, mstart_s, chunk_s, H5T_IEEE_F64LE); hdf5_write_chunked_array(mu, "b_k_angle", 3, fdims_s, fstart_s, fcount_s, mdims_s, mstart_s, chunk_s, H5T_IEEE_F64LE); + hdf5_write_chunked_array(sigma, "sigma", 3, fdims_s, fstart_s, fcount_s, mdims_s, mstart_s, chunk_s, H5T_IEEE_F64LE); + hdf5_write_chunked_array(beta, "beta", 3, fdims_s, fstart_s, fcount_s, mdims_s, mstart_s, chunk_s, H5T_IEEE_F64LE); hdf5_write_chunked_array(dl, "dl", 3, fdims_s, fstart_s, fcount_s, mdims_s, mstart_s, chunk_s, H5T_IEEE_F64LE); hdf5_write_chunked_array(r, "r", 3, fdims_s, fstart_s, fcount_s, mdims_s, mstart_s, chunk_s, H5T_IEEE_F64LE); @@ -494,7 +501,7 @@ void dump_var_along(int i, int j, int nstep, struct of_traj *traj, int nx, int n fdims_v[3] = chunk_v[3] = fcount_v[3] = mdims_v[3] = 8; hdf5_write_chunked_array(prims, "prims", 4, fdims_v, fstart_v, fcount_v, mdims_v, mstart_v, chunk_v, H5T_IEEE_F64LE); - // TENSORS: Anything with N values per geodesic step + // TENSORS: Anything with NxN values per geodesic step hsize_t fdims_t[] = { nx, ny, params->maxnstep, 4, 4 }; hsize_t chunk_t[] = { 1, 1, 200, 4, 4 }; hsize_t fstart_t[] = { i, j, 0, 0, 0 }; @@ -514,6 +521,7 @@ void dump_var_along(int i, int j, int nstep, struct of_traj *traj, int nx, int n free(dl); free(r); free(th); free(phi); free(j_unpol); free(k_unpol); free(I_unpol); + free(sigma); free(beta); // Vectors free(X); diff --git a/src/simcoords.c b/src/simcoords.c index c525bf1..821339b 100644 --- a/src/simcoords.c +++ b/src/simcoords.c @@ -131,7 +131,7 @@ void initialize_simgrid(size_t n1, size_t n2, double x1i, double x1f, double x2i rmax_geo = fmin(rmax_geo, Rout); rmin_geo = fmax(rmin_geo, Rin); - fprintf(stderr, "Rin Rmax %g %g %g %g %g\n", rmax_geo, rmax_geo, Rin, Rout, 1. + sqrt(1.-a*a)); + fprintf(stderr, "Rin Rmax %g %g %g %g %g\n", rmin_geo, rmax_geo, Rin, Rout, 1. + sqrt(1.-a*a)); x1i_oob = x1f + 1.; er0 = log(Rin); From 47e037b87a7010651c172ad21367831b6797ce81 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Fri, 13 Aug 2021 16:01:15 -0500 Subject: [PATCH 75/93] Disable dynamical-time cooling by default, load BHAC dumps correctly again. --- model/iharm/model.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/model/iharm/model.c b/model/iharm/model.c index 73df27f..bc7aede 100644 --- a/model/iharm/model.c +++ b/model/iharm/model.c @@ -58,7 +58,7 @@ static double trat_large = 40.; // Minimum number of dynamical times the cooling time must // undershoot to be considered "small" // lower values -> higher max T_e, higher values are restrictive -static double cooling_dynamical_times = 1.; +static double cooling_dynamical_times = 1.e-20; static int dumpskip = 1; static int dumpmin, dumpmax, dumpidx; @@ -251,18 +251,23 @@ void get_dumpfile_type(char *fnam, int dumpidx) int hamr_attr_exists = hdf5_attr_exists("", "dscale"); if (!hamr_attr_exists) { - - char harmversion[256]; - hdf5_read_single_val(harmversion, "header/version", hdf5_make_str_type(255)); - - if ( strcmp(harmversion, "KORALv2") == 0 ) { - dumpfile_format = FORMAT_KORAL_v2; - fprintf(stderr, "koral!\n"); - } else { + if (!hdf5_exists("header/version")) { + // Converted BHAC dumps and very old iharm3d output do not include a version + // BHAC output requires nothing special from ipole so we mark it "iharm_v1" dumpfile_format = FORMAT_IHARM_v1; - fprintf(stderr, "iharm!\n"); - } + fprintf(stderr, "bhac! (or old iharm)\n"); + } else { + char harmversion[256]; + hdf5_read_single_val(harmversion, "header/version", hdf5_make_str_type(255)); + if ( strcmp(harmversion, "KORALv2") == 0 ) { + dumpfile_format = FORMAT_KORAL_v2; + fprintf(stderr, "koral!\n"); + } else { + dumpfile_format = FORMAT_IHARM_v1; + fprintf(stderr, "iharm!\n"); + } + } } else { // note this will return -1 if the "header" group does not exist dumpfile_format = FORMAT_HAMR_EKS; From 83f4be6a556b4478ac93515c81066a1697d87ed7 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Fri, 13 Aug 2021 17:05:41 -0500 Subject: [PATCH 76/93] Move rmin_geo and rmax_geo to coordinates.c. They still must be set by the model, like other coordinate parameters. --- model/analytic/model.c | 8 +++----- model/analytic/model_params.h | 3 --- model/iharm/model.c | 2 -- model/iharm/model_params.h | 1 - model/ldi2/model.c | 2 -- model/ldi2/model_params.h | 1 - model/riaf/model.c | 3 --- model/riaf/model_params.h | 3 --- model/sphere/model.c | 1 - model/sphere/model_params.h | 2 -- src/coordinates.c | 5 +++++ src/coordinates.h | 1 + 12 files changed, 9 insertions(+), 23 deletions(-) diff --git a/model/analytic/model.c b/model/analytic/model.c index 94b9cd9..6a2be6c 100644 --- a/model/analytic/model.c +++ b/model/analytic/model.c @@ -16,9 +16,6 @@ double Te_unit; // This one is useful double RHO_unit; -// Model parameters: public -double rmin_geo = 0; -double rmax_geo = 1000.0; // Model parameters: private static double MBH_solar = 4.e6; static double RHO_unit_in = 0; @@ -58,7 +55,7 @@ void try_set_model_parameter(const char *word, const char *value) set_by_word_val(word, value, "model", &model, TYPE_INT); set_by_word_val(word, value, "MBH", &MBH_solar, TYPE_DBL); set_by_word_val(word, value, "rho_unit", &RHO_unit_in, TYPE_DBL); - // TODO NEED to move this into main parameters + // TODO move these calls into the main parameters set_by_word_val(word, value, "rmax_geo", &rmax_geo, TYPE_DBL); set_by_word_val(word, value, "rmin_geo", &rmin_geo, TYPE_DBL); @@ -137,7 +134,8 @@ void set_units() Rh = 1 + sqrt(1. - a * a); Rin = Rh; Rout = 1000.0; - // Limit rmax_geo? + rmin_geo = 1.; + rmax_geo = 10000.; double z1 = 1. + pow(1. - a * a, 1. / 3.) * (pow(1. + a, 1. / 3.) + pow(1. - a, 1. / 3.)); double z2 = sqrt(3. * a * a + z1 * z1); diff --git a/model/analytic/model_params.h b/model/analytic/model_params.h index a0569f9..4d43416 100644 --- a/model/analytic/model_params.h +++ b/model/analytic/model_params.h @@ -9,7 +9,4 @@ #define THIN_DISK (0) -// Necessary model parameters, can be used or not -extern double rmin_geo, rmax_geo; - #endif /* MODEL_PARAMS_H */ diff --git a/model/iharm/model.c b/model/iharm/model.c index bc7aede..2803cd4 100644 --- a/model/iharm/model.c +++ b/model/iharm/model.c @@ -44,8 +44,6 @@ static double mu_i, mu_e, mu_tot; // MODEL PARAMETERS: PUBLIC double DTd; -double rmax_geo = 100.; -double rmin_geo = 1.; double sigma_cut = 1.0; double beta_crit = 1.0; diff --git a/model/iharm/model_params.h b/model/iharm/model_params.h index 14dc0e9..0167fa7 100644 --- a/model/iharm/model_params.h +++ b/model/iharm/model_params.h @@ -21,6 +21,5 @@ extern double DTd; extern double sigma_cut; -extern double rmin_geo, rmax_geo; #endif // MODEL_PARAMS_H diff --git a/model/ldi2/model.c b/model/ldi2/model.c index 72c8df6..9ce36d5 100644 --- a/model/ldi2/model.c +++ b/model/ldi2/model.c @@ -18,8 +18,6 @@ double U_unit; double B_unit; double Te_unit; -// TODO get rid of these in ipole proper to get rid of them here -double rmax_geo = 1e30; double model_dl; // TODO this default needs to be kept in sync with maxnstep, // to avoid difficulties diff --git a/model/ldi2/model_params.h b/model/ldi2/model_params.h index f12f210..e2d1422 100644 --- a/model/ldi2/model_params.h +++ b/model/ldi2/model_params.h @@ -11,7 +11,6 @@ #define INTEGRATOR_TEST (1) // Model parameters (TODO eliminate if we can these are unused) -extern double rmax_geo; extern double model_dl; void record_stokes_parameters(double SI, double SQ, double SU, double SV, double lam); diff --git a/model/riaf/model.c b/model/riaf/model.c index b63ac79..8d16490 100644 --- a/model/riaf/model.c +++ b/model/riaf/model.c @@ -18,9 +18,6 @@ double RHO_unit; double Te_unit = 1e11; double Ne_unit = 5e6; -// Model parameters: public -double rmin_geo = 0; -double rmax_geo = 1000.0; // Model parameters: private static double MBH_solar = 4.3e6; diff --git a/model/riaf/model_params.h b/model/riaf/model_params.h index a0569f9..4d43416 100644 --- a/model/riaf/model_params.h +++ b/model/riaf/model_params.h @@ -9,7 +9,4 @@ #define THIN_DISK (0) -// Necessary model parameters, can be used or not -extern double rmin_geo, rmax_geo; - #endif /* MODEL_PARAMS_H */ diff --git a/model/sphere/model.c b/model/sphere/model.c index ccfe0bb..41a40af 100644 --- a/model/sphere/model.c +++ b/model/sphere/model.c @@ -14,7 +14,6 @@ #include // used by other files -double rmin_geo, rmax_geo; double L_unit; // model parameters diff --git a/model/sphere/model_params.h b/model/sphere/model_params.h index 28f5ecf..455c82e 100644 --- a/model/sphere/model_params.h +++ b/model/sphere/model_params.h @@ -3,6 +3,4 @@ #define SLOW_LIGHT (0) -extern double rmin_geo, rmax_geo; - #endif // MODEL_PARAMS_H diff --git a/src/coordinates.c b/src/coordinates.c index 023ed49..844cda7 100644 --- a/src/coordinates.c +++ b/src/coordinates.c @@ -10,9 +10,14 @@ int metric = -1; double a, hslope; // mks double poly_norm, poly_xt, poly_alpha, mks_smooth; // fmks double mks3R0, mks3H0, mks3MY1, mks3MY2, mks3MP0; // mks3 + +// Coordinate parameters double startx[NDIM], stopx[NDIM], dx[NDIM]; double cstartx[NDIM], cstopx[NDIM]; double R0, Rin, Rout, Rh; +// Tracing parameters we need independent of model +double rmax_geo = 100.; +double rmin_geo = 1.; /* * Despite the name, this returns r, th coordinates for a KS or BL diff --git a/src/coordinates.h b/src/coordinates.h index cc612a4..f1bb894 100644 --- a/src/coordinates.h +++ b/src/coordinates.h @@ -30,6 +30,7 @@ extern double mks3R0, mks3H0, mks3MY1, mks3MY2, mks3MP0; // mks3 extern double startx[NDIM], stopx[NDIM], dx[NDIM]; extern double cstartx[NDIM], cstopx[NDIM]; extern double R0, Rin, Rout, Rh; +extern double rmin_geo, rmax_geo; void bl_coord(double X[NDIM], double *r, double *th); void bl_to_ks(double X[NDIM], double ucon_bl[NDIM], double ucon_ks[NDIM]); From e0500d95154aee9394af8e3dbefdaf815387d2eb Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Thu, 2 Sep 2021 14:48:46 -0500 Subject: [PATCH 77/93] Add Bremsstrahlung emission coefficients, via Ricardo Yarza's work on grmonty. Activate by setting pref bremss to 1 --- src/constants.h | 1 + src/main.c | 4 + src/model_radiation.c | 19 ++- src/symphony/bremss_fits.c | 339 +++++++++++++++++++++++++++++++++++++ src/symphony/bremss_fits.h | 17 ++ 5 files changed, 376 insertions(+), 4 deletions(-) create mode 100644 src/symphony/bremss_fits.c create mode 100644 src/symphony/bremss_fits.h diff --git a/src/constants.h b/src/constants.h index b898853..8368135 100644 --- a/src/constants.h +++ b/src/constants.h @@ -14,6 +14,7 @@ #define SIG (5.670400e-5 ) /* Stefan-Boltzmann constant */ #define RGAS (8.3143e7 ) /* erg K^-1 mole^-1: ideal gas const */ #define EV (1.60217653e-12 ) /* electron volt in erg */ +#define RE (EE*EE/ME/CL/CL ) /* classical electron radius */ #define SIGMA_THOMSON (0.665245873e-24 ) /* Thomson cross section in cm^2 */ #define JY (1.e-23 ) /* Jansky (flux/freq. unit) in cgs */ #define MUAS_PER_RAD (2.06265e11 ) /* Micro-arcseconds in 1 radian */ diff --git a/src/main.c b/src/main.c index a907b0c..789e771 100644 --- a/src/main.c +++ b/src/main.c @@ -5,6 +5,7 @@ #include "model_radiation.h" #include "model_tetrads.h" +#include "bremss_fits.h" #include "radiation.h" #include "coordinates.h" #include "debug_tools.h" @@ -88,6 +89,9 @@ int main(int argc, char *argv[]) // them and use init_model to load the first dump init_model(&tA, &tB); + // If we're using Bremsstrahlung emission, precalculate a spline + init_bremss_spline(); + // Adaptive resolution option // nx, ny are the resolution at maximum refinement level // nx_min, ny_min are at coarsest level diff --git a/src/model_radiation.c b/src/model_radiation.c index 02c7969..65ae11a 100644 --- a/src/model_radiation.c +++ b/src/model_radiation.c @@ -24,6 +24,7 @@ // Symphony #include "fits.h" #include "params.h" +#include "bremss_fits.h" #include #include @@ -67,6 +68,8 @@ static double powerlaw_eta = 0.02; static int variable_kappa = 0; static double max_pol_frac_e = 0.99; static double max_pol_frac_a = 0.99; +static int do_bremss = 0; +static int bremss_type = 2; void try_set_radiation_parameter(const char *word, const char *value) { @@ -79,6 +82,9 @@ void try_set_radiation_parameter(const char *word, const char *value) set_by_word_val(word, value, "powerlaw_p", &powerlaw_p, TYPE_DBL); set_by_word_val(word, value, "powerlaw_eta", &powerlaw_eta, TYPE_DBL); + set_by_word_val(word, value, "bremss", &do_bremss, TYPE_INT); + set_by_word_val(word, value, "bremss_type", &bremss_type, TYPE_INT); + set_by_word_val(word, value, "max_pol_frac_e", &powerlaw_p, TYPE_DBL); set_by_word_val(word, value, "max_pol_frac_a", &powerlaw_p, TYPE_DBL); } @@ -159,6 +165,7 @@ void jar_calc_dist(int dist, int pol, double X[NDIM], double Kcon[NDIM], double Ucon[NDIM], Ucov[NDIM], Bcon[NDIM], Bcov[NDIM]; get_model_fourv(X, Kcon, Ucon, Ucov, Bcon, Bcov); double nu = get_fluid_nu(Kcon, Ucov); + double nusq = nu*nu; double theta = get_bk_angle(X, Kcon, Ucov, Bcon, Bcov); paramsM.electron_density = Ne; paramsM.nu = nu; @@ -190,11 +197,13 @@ void jar_calc_dist(int dist, int pol, double X[NDIM], double Kcon[NDIM], if (!pol || dist == E_UNPOL) { if (dist == E_THERMAL || dist == E_DEXTER_THERMAL || dist > 10){ paramsM.dexter_fit = 2; // Signal symphony fits to use Leung+ - *jI = j_nu_fit(¶msM, paramsM.STOKES_I) / (nu * nu); + *jI = j_nu_fit(¶msM, paramsM.STOKES_I); + if(do_bremss) *jI += bremss_I(¶msM, bremss_type); + *jI /= nusq; // Avoids loss of precision in small numbers double Bnuinv = Bnu_inv(nu, paramsM.theta_e); // Planck function *aI = *jI / Bnuinv; } else { - *jI = j_nu_fit(¶msM, paramsM.STOKES_I) / (nu * nu); + *jI = j_nu_fit(¶msM, paramsM.STOKES_I) / nusq; *aI = alpha_nu_fit(¶msM, paramsM.STOKES_I) * nu; } } else { @@ -206,8 +215,9 @@ void jar_calc_dist(int dist, int pol, double X[NDIM], double Kcon[NDIM], *rV = rho_nu_fit(¶msM, paramsM.STOKES_V) * nu; } else { // EMISSIVITIES - double nusq = nu*nu; - *jI = j_nu_fit(¶msM, paramsM.STOKES_I) / nusq; + *jI = j_nu_fit(¶msM, paramsM.STOKES_I); + if(do_bremss) *jI += bremss_I(¶msM, bremss_type); + *jI /= nusq; // Avoids loss of precision in small numbers *jQ = -j_nu_fit(¶msM, paramsM.STOKES_Q) / nusq; *jU = -j_nu_fit(¶msM, paramsM.STOKES_U) / nusq; *jV = j_nu_fit(¶msM, paramsM.STOKES_V) / nusq; @@ -233,6 +243,7 @@ void jar_calc_dist(int dist, int pol, double X[NDIM], double Kcon[NDIM], *aV = *jV / Bnuinv; } else { *aI = alpha_nu_fit(¶msM, paramsM.STOKES_I) * nu; + // Note Bremss emission is available for thermal dists *only* *aQ = -alpha_nu_fit(¶msM, paramsM.STOKES_Q) * nu; *aU = -alpha_nu_fit(¶msM, paramsM.STOKES_U) * nu; *aV = alpha_nu_fit(¶msM, paramsM.STOKES_V) * nu; diff --git a/src/symphony/bremss_fits.c b/src/symphony/bremss_fits.c new file mode 100644 index 0000000..7155232 --- /dev/null +++ b/src/symphony/bremss_fits.c @@ -0,0 +1,339 @@ + + +#include "bremss_fits.h" + +#include "decs.h" + +// For planck_func +#include "maxwell_juettner.h" + +#include +#include +#include + +double gffee(double Te, double nu); +double gffei(double Te, double nu); + +#define THETAE_MIN 1.e-3 + +/* Emissivities */ +double bremss_I(struct parameters *params, int bremss_type) +{ + if (params->theta_e < THETAE_MIN) + return 0.; + + double Thetae = params->theta_e; + double Te = Thetae * ME * CL * CL / KBOL; + double nu = params->nu; + double x = HPL * nu / (KBOL * Te); + double Ne = params->electron_density; + double jv; + + double efac = 0.; + if (x < 1.e-3) { + efac = (24. - 24.*x + 12.*x*x - 4.*x*x*x + x*x*x*x) / 24.; + } else { + efac = exp(-x); + } + + // Bremss options + // 0 No bremsstrahlung + // 1 Rybicki and Lightman eq. 5.14a with eq. 5.25 corrective factor + // 2 Piecewise formula + // 3 van Hoof 2015 + Nozawa 2009 + // Bremss is only supported for thermal electrons + if (bremss_type == 1) { + // Method from Rybicki & Lightman, ultimately from Novikov & Thorne + + double rel = (1. + 4.4e-10*Te); + double gff = 1.2; + + jv = 1./(4.*M_PI)*pow(2,5)*M_PI*pow(EE,6)/(3.*ME*pow(CL,3)); + jv *= pow(2.*M_PI/(3.*KBOL*ME),1./2.); + jv *= pow(Te,-1./2.)*Ne*Ne; + jv *= efac*rel*gff; + + } else if (bremss_type == 2) { + // Svensson 1982 as used in e.g. Straub 2012 + double Fei=0., Fee=0., fei=0., fee=0.; + + double SOMMERFELD_ALPHA = 1. / 137.036; + double eta = 0.5616; + double gammaE = 0.577; // = - Log[0.5616] + double gff; + + if (x > 1) { + gff = sqrt(3. / M_PI / x); + } else { + gff = sqrt(3.) / M_PI * log(4 / gammaE / x); + } + + if (Thetae < 1) { + Fei = 4. * sqrt(2.*Thetae/M_PI/M_PI/M_PI) * (1. + 1.781*pow(Thetae,1.34)); + Fee = 20./9./sqrt(M_PI) * (44. - 3.*M_PI*M_PI) * pow(Thetae,1.5); + Fee *= (1. + 1.1*Thetae + Thetae*Thetae - 1.25*pow(Thetae,2.5)); + } else { + Fei = 9.*Thetae/(2.*M_PI) * ( log(1.123 * Thetae + 0.48) + 1.5 ); + Fee = 24. * Thetae * ( log(2.*eta*Thetae) + 1.28 ); + } + + fei = Ne * Ne * SIGMA_THOMSON * SOMMERFELD_ALPHA * ME * CL * CL * CL * Fei; + fee = Ne * Ne * RE * RE * SOMMERFELD_ALPHA * ME * CL * CL * CL * Fee; + + jv = (fei+fee) / (4.*M_PI) * HPL/KBOL/Te * efac * gff; + + } else if (bremss_type == 3) { + // Nozawa 2009 - electron-electron + jv = 7.673889101895528e-44 * Ne * Ne * efac * gffee(Te, nu) * sqrt(Thetae); + // van Hoof 2015 - electron-ion + jv += 7.070090102391322e-44 * Ne * Ne * efac * gffei(Te, nu) / sqrt(Thetae); + } + + return jv; +} + +/* Absorptivities */ +double bremss_I_abs(struct parameters *params, int bremss_type) +{ + double ans = bremss_I(params, bremss_type) / planck_func(params); + return ans; +} + +// Below are bremsstrahlung routines that are used *only* if the van Hoof 2015 + Nozawa 2009 formulae (type 3) are used. +gsl_spline2d *bremss_spline; +gsl_interp_accel *bremss_xacc; +gsl_interp_accel *bremss_yacc; + +/* + * Electron-ion bremsstrahlung. + * This code uses data from: + * van Hoof, P.~A.~M., Ferland, G.~J., Williams, R.~J.~R., et al.\ 2015, \mnras, 449, 2112 + * Please cite their work if you use these electron-ion bremsstrahlung formulae with grmonty. + */ + +// Sets the bremss splines as global variables so they can be used to interpolate the Gaunt factor +void init_bremss_spline() +{ + // This requires gff_ei_bremss.dat in ipole's directory in order to function +#if 0 + const size_t nx = 146; + const size_t ny = 81; + double *z = malloc(nx * ny * sizeof(double*)); + const double xa[] = {-16.,-15.8,-15.6,-15.4,-15.2,-15.,-14.8,-14.6,-14.4,-14.2,-14.,-13.8,-13.6,-13.4,-13.2,-13.,-12.8,-12.6,-12.4,-12.2,-12.,-11.8,-11.6,-11.4,-11.2,-11.,-10.8,-10.6,-10.4,-10.2,-10.,-9.8,-9.6,-9.4,-9.2,-9.,-8.8,-8.6,-8.4,-8.2,-8.,-7.8,-7.6,-7.4,-7.2,-7.,-6.8,-6.6,-6.4,-6.2,-6.,-5.8,-5.6,-5.4,-5.2,-5.,-4.8,-4.6,-4.4,-4.2,-4.,-3.8,-3.6,-3.4,-3.2,-3.,-2.8,-2.6,-2.4,-2.2,-2.,-1.8,-1.6,-1.4,-1.2,-1.,-0.8,-0.6,-0.4,-0.2,0.,0.2,0.4,0.6,0.8,1.,1.2,1.4,1.6,1.8,2.,2.2,2.4,2.6,2.8,3.,3.2,3.4,3.6,3.8,4.,4.2,4.4,4.6,4.8,5.,5.2,5.4,5.6,5.8,6.,6.2,6.4,6.6,6.8,7.,7.2,7.4,7.6,7.8,8.,8.2,8.4,8.6,8.8,9.,9.2,9.4,9.6,9.8,10.,10.2,10.4,10.6,10.8,11.,11.2,11.4,11.6,11.8,12.,12.2,12.4,12.6,12.8,13.}; + const double ya[] = {-6.,-5.8,-5.6,-5.4,-5.2,-5.,-4.8,-4.6,-4.4,-4.2,-4.,-3.8,-3.6,-3.4,-3.2,-3.,-2.8,-2.6,-2.4,-2.2,-2.,-1.8,-1.6,-1.4,-1.2,-1.,-0.8,-0.6,-0.4,-0.2,0.,0.2,0.4,0.6,0.8,1.,1.2,1.4,1.6,1.8,2.,2.2,2.4,2.6,2.8,3.,3.2,3.4,3.6,3.8,4.,4.2,4.4,4.6,4.8,5.,5.2,5.4,5.6,5.8,6.,6.2,6.4,6.6,6.8,7.,7.2,7.4,7.6,7.8,8.,8.2,8.4,8.6,8.8,9.,9.2,9.4,9.6,9.8,10.}; + double val; + + // Set spline up for quadratic interpolation between the tabulated VH15 data. + const gsl_interp2d_type *T = gsl_interp2d_bilinear; + +// gsl_spline2d *bremss_spline; +// gsl_interp_accel *bremss_xacc; +// gsl_interp_accel *bremss_yacc; + + + bremss_spline = gsl_spline2d_alloc(T,nx,ny); + bremss_xacc = gsl_interp_accel_alloc(); + bremss_yacc = gsl_interp_accel_alloc(); + + // Data file where VH15 data is stored. Z01 means hydrogen, merged means combines relativistic and non-relativistic results + FILE *myfile; + myfile = fopen("gff_ei_bremss.dat","r"); + if (myfile == NULL) { + fprintf(stderr, "Cannot find spline2d data: gff_ei_bremss.dat! Exiting.\n"); + exit(-1); + } + + // Read file and assign it to spline. + for (int i = 0; i < nx; i++) + { + for (int j = 0; j < ny; j++) + { + if (fscanf(myfile,"%lf",&val) != 1) exit(5); + gsl_spline2d_set(bremss_spline, z, i, j, val); + } + } + + gsl_spline2d_init(bremss_spline,xa,ya,z,nx,ny); +#endif +} + +// Returns the electron-ion Gaunt factor +double gffei(double Te, double nu) +{ + + double gffeival,loggammasq,logu; + + // Make sure we don't go out of the domain. + if ( Te < 1.579e-5 ) Te = 1.579e-5; + else if ( Te > 1.579e+11 ) Te = 1.579e+11; + + loggammasq = log10(157900. / Te); + + if ( nu < (1.e-16) * KBOL * Te / HPL ) nu = (1.e-16) * KBOL * Te / HPL; + else if ( nu > (1.e+13) * KBOL * Te / HPL ) nu = (1.e+13) * KBOL * Te / HPL; + + logu = log10(HPL * nu / KBOL / Te); + + gffeival = gsl_spline2d_eval(bremss_spline,logu,loggammasq,bremss_xacc,bremss_yacc); + return gffeival; + +} + + +/* + * The following code is a very slightly modified version of the one released in: + * Nozawa, S., Takahashi, K., Kohyama, Y., et al.\ 2009, \aap, 499, 661 + * Please cite their work if you use these electron-electron bremsstrahlung formulae with grmonty. + */ + +double funce(double x) +{ + return gsl_sf_expint_Ei(-x); +} + +double gi(double x, double tau) +{ + const double a[121] = {3.15847E0, 2.46819E-2, -2.11118E-2, 1.24009E-2, -5.41633E-3, 1.70070E-3, -3.05111E-4, -1.21721E-4, 1.77611E-4, -2.05480E-5, -3.58754E-5, -2.52430E0, 1.03924E-1, -8.53821E-2, 4.73623E-2, -1.91406E-2, 5.39773E-3, -7.26681E-4, -7.47266E-4, 8.73517E-4, -6.92284E-5, -1.80305E-4, 4.04877E-1, 1.98935E-1, -1.52444E-1, 7.51656E-2, -2.58034E-2, 4.13361E-3, 4.67015E-3, -2.20675E-3, -2.67582E-3, 2.95254E-5, 1.40751E-3, 6.13466E-1, 2.18843E-1, -1.45660E-1, 5.07201E-2, -2.23048E-3, -1.14273E-2, 1.24789E-2, -2.74351E-3, -4.57871E-3, -1.70374E-4, 2.06757E-3, 6.28867E-1, 1.20482E-1, -4.63705E-2, -2.25247E-2, 5.07325E-2, -3.23280E-2, -1.16976E-2, -1.00402E-3, 2.96622E-2, -5.43191E-4, -1.23098E-2, 3.29441E-1, -4.82390E-2, 8.16592E-2, -8.17151E-2, 5.94414E-2, -2.19399E-2, -1.13488E-2, -2.38863E-3, 1.89850E-2, 2.50978E-3, -8.81767E-3, -1.71486E-1, -1.20811E-1, 9.87296E-2, -4.59297E-2, -2.11247E-2, 1.76310E-2, 6.31446E-2, -2.28987E-3, -8.84093E-2, 4.45570E-3, 3.46210E-2, -3.68685E-1, -4.46133E-4, -3.24743E-2, 5.05096E-2, -5.05387E-2, 2.23352E-2, 1.33830E-2, 7.79323E-3, -2.93629E-2, -2.80083E-3, 1.23727E-2, -7.59200E-2, 8.88749E-2, -8.82637E-2, 5.58818E-2, 9.20453E-3, -4.59817E-3, -8.54735E-2, 7.98332E-3, 1.02966E-1, -5.68093E-3, -4.04801E-2, 1.60187E-1, 2.50320E-2, -7.52221E-3, -9.11885E-3, 1.67321E-2, -8.24286E-3, -6.47349E-3, -3.80435E-3, 1.38957E-2, 1.10618E-3, -5.68689E-3, 8.37729E-2, -1.28900E-2, 1.99419E-2, -1.71348E-2, -3.47663E-3, -3.90032E-4, 3.72266E-2, -4.25035E-3, -4.22093E-2, 2.33625E-3, 1.66733E-2}; + + double logth = log10(tau); + double logu = log10(x ); + double jfit = 0.; + + double the = ( logth + 2.65) / 1.35; + double u = ( logu + 1.50) / 2.50; + double uj; + + for (int j = 0; j < 11; j++){ + uj = pow(u, j); + for (int i = 0; i < 11; i++){ + jfit += a[11*j + i] * pow(the, i) * uj; + } + } + + return sqrt(8. / 3. / M_PI) * jfit; + +} + +double gii(double x, double tau) +{ + const double a2[9] = {0.9217, -13.4988, 76.4539, -217.8301, 320.9753, -188.0667, -82.4161, 163.7191, -60.0248}; + const double a1[9] = {-9.3647, 95.9186, -397.0172, 842.9376, -907.3076, 306.8802, 291.2983, -299.0253, 76.3461}; + const double a0[9] = {-37.3698, 380.3659, -1489.8014, 2861.4150, -2326.3704, -691.6118, 2853.7893, -2040.7952, 492.5981}; + const double b1[9] = {-8.6991, 63.3830, -128.8939, -135.0312, 977.5838, -1649.9529, 1258.6812, -404.7461, 27.3354}; + const double b0[9] = {-11.6281, 125.6066, -532.7489, 1142.3873, -1156.8545, 75.0102, 996.8114, -888.1895, 250.1386}; + + double aa2 = 0.; + double aa1 = 0.; + double aa0 = 0.; + double bb1 = 0.; + double bb0 = 0.; + double powtau; + + double Ei = funce(x); + + for (int i = 0; i < 9; i++){ + powtau = pow(tau, i / 8.); + aa2 += a2[i] * powtau; + aa1 += a1[i] * powtau; + aa0 += a0[i] * powtau; + bb1 += b1[i] * powtau; + bb0 += b0[i] * powtau; + } + + return aa2 * x * x + aa1 * x + aa0 - exp(x) * Ei * ( bb1 * x + bb0 ); + +} + +double giii(double x, double tau) +{ + double const a2[9] = {64.7512, -213.8956, 174.1432, 136.5088, -271.4899, 89.3210, 58.2584, -46.0807, 8.7301}; + double const a1[9] = {49.7139, -189.7746, 271.0298, -269.7807, 420.4812, -576.6247, 432.7790, -160.5365, 23.3925}; + double const a0[9] = {52.1633, -257.0313, 446.8161, -293.0585, 0.0000, 77.0474, -23.8718, 0.0000, 0.1997}; + double const b1[9] = {376.4322, -1223.3635, 628.6787, 2237.3946, -3828.8387, 2121.7933, -55.1667, -349.4321, 92.2059}; + double const b0[9] = {-8.5862, 34.1348, -116.3287, 296.5451, -393.4207, 237.5497, -30.6000, -27.6170, 8.8453}; + + double aa2 = 0.; + double aa1 = 0.; + double aa0 = 0.; + double bb1 = 0.; + double bb0 = 0.; + double powtau; + + double Ei = funce(x); + + for (int i = 0; i < 9; i++){ + powtau = pow(tau, i / 8.); + aa2 += a2[i] * powtau; + aa1 += a1[i] * powtau; + aa0 += a0[i] * powtau; + bb1 += b1[i] * powtau; + bb0 += b0[i] * powtau; + } + + return aa2 * x * x + aa1 * x + aa0 - exp(x) * Ei * ( bb1 * x + bb0 ); + +} + +double giv(double x, double tau) +{ + double gam = exp(0.5772156649); + double con1 = 3. / ( 4. * M_PI * sqrt(tau) ); + double a1 = 28./3. + 2. * x + x * x / 2.; + double a2 = 8./3. + (4./3.) * x + x * x; + double a3 = 8./3. - (4./3.) * x + x * x; + + double Ei = funce(x); + double fx = a1 + 2. * a2 * log(2. * tau / gam) - exp(x) * Ei * a3; + + return con1 * fx; + +} + +double fcc(double x, double tau) +{ + double const p[7] = {-5.7752, 46.2097, -160.7280, 305.0070, -329.5420, 191.0770, -46.2718}; + double const q[7] = {30.5586, -248.2177, 874.1964, -1676.9028, 1828.8677, -1068.9366, 260.5656}; + double const r[7] = {-54.3272, 450.9676, -1616.5987, 3148.1061, -3478.3930, 2055.6693, -505.6789}; + double const s[7] = {36.2625, -310.0972, 1138.0531, -2260.8347, 2541.9361, -1525.2058, 380.0852}; + double const t[7] = {-8.4082, 74.7925, -282.9540, 576.3930, -661.9390, 404.2930, -102.2330}; + + double ptau = 0.; + double qtau = 0.; + double rtau = 0.; + double stau = 0.; + double ttau = 0.; + double powtau; + + for (int i = 0; i < 7; i++){ + powtau = pow(tau, i / 6.); + ptau += p[i] * powtau; + qtau += q[i] * powtau; + rtau += r[i] * powtau; + stau += s[i] * powtau; + ttau += t[i] * powtau; + } + + return 1. + ptau * pow(x, 2./8.) + qtau * pow(x, 3./8.) + rtau * pow(x, 4./8.) + stau * pow(x, 5./8.) + ttau * pow(x, 6./8.); + +} + +double gffee(double Te, double nu) +{ + double emass = 510.998902; + double tau = KBOL * Te / ME / CL / CL; + double x = HPL * nu / KBOL / Te; + double gffeeval; + + if ( tau < ( 0.05 / emass ) ) tau = 0.05 / emass; + + if ( x < 1.e-4 ) x = 1.e-4; + else if ( x > 10 ) x = 10.; + + if ( tau >= ( 0.05 / emass ) && tau < ( 1. / emass ) ) gffeeval = gi(x, tau); + else if ( tau > ( 1. / emass ) && tau < ( 300. / emass ) ) gffeeval = gii(x, tau) * fcc(x, tau); + else if ( tau > ( 300. / emass ) && tau < ( 7000. / emass ) ) gffeeval = giii(x, tau); + else if ( tau > ( 7000. / emass ) ) gffeeval = giv(x, tau); + + return gffeeval; + +} diff --git a/src/symphony/bremss_fits.h b/src/symphony/bremss_fits.h new file mode 100644 index 0000000..931da09 --- /dev/null +++ b/src/symphony/bremss_fits.h @@ -0,0 +1,17 @@ +#ifndef SYMPHONY_BREMSS_FITS_H_ +#define SYMPHONY_BREMSS_FITS_H_ +#include "params.h" +#include + +/* Fits */ + +// Initialize necessary cached values +void init_bremss_spline(); + +/* Emissivities */ +double bremss_I(struct parameters * params, int bremss_type); + +/* Absorptivities */ +double bremss_I_abs(struct parameters * params, int bremss_type); + +#endif /* SYMPHONY_MAXWELL_JUETTNER_H_ */ From 93adc101dc9d63836de4c278c535578778cc865f Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Wed, 8 Sep 2021 13:21:16 -0500 Subject: [PATCH 78/93] Add variable kappa model courtesy of Razieh Emami. Floors for model sigma, beta values to prevent NaN values, since kappa is floored in any case. --- model/iharm/model.c | 6 ++++-- src/model_radiation.c | 14 +++++++++++++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/model/iharm/model.c b/model/iharm/model.c index bd359e6..33ada1c 100644 --- a/model/iharm/model.c +++ b/model/iharm/model.c @@ -68,6 +68,8 @@ static double Ladv_dump; static int reverse_field = 0; +double tf; + // MAYBES //static double t0; @@ -607,9 +609,9 @@ void init_physical_quantities(int n) data[n]->thetae[i][j][k] = fmax(data[n]->thetae[i][j][k], 1.e-3); // Preserve sigma for cutting along geodesics, and for variable-kappa model - data[n]->sigma[i][j][k] = sigma_m; + data[n]->sigma[i][j][k] = fmax(sigma_m, SMALL); // Also record beta, for variable-kappa model - data[n]->beta[i][j][k] = beta_m; + data[n]->beta[i][j][k] = fmax(beta_m, SMALL); // Cut Ne (i.e. emission) based on sigma, if we're not doing so along each geodesic // Strongly magnetized = empty, no shiny spine diff --git a/src/model_radiation.c b/src/model_radiation.c index 65ae11a..4b364e6 100644 --- a/src/model_radiation.c +++ b/src/model_radiation.c @@ -324,7 +324,19 @@ void get_model_kappa(double X[NDIM], double *kappa, double *kappa_width) { if (variable_kappa) { double sigma = get_model_sigma(X); double beta = get_model_beta(X); - *kappa = model_kappa; // INSERT VARIABLE KAPPA HERE + + *kappa = 2.8 + 0.7/sqrt(sigma) + 3.7 * (1.0/pow(sigma, 0.19)) * tanh(23.4 * pow(sigma, 0.26) * beta); + + if (isnan(*kappa)) { + double B = get_model_b(X); + fprintf(stderr, "kappa, sigma, beta, B: %g %g %g %g \n", *kappa, sigma, beta, B); + } + // Limit kappa + // This is already a bit generous with the fits' domain of validity, + // tread cautiously + if (*kappa < 3.1) *kappa = 3.1; + if (*kappa > 10.0) *kappa = 10.0; + //fprintf(stderr, "sigma, beta -> kappa %g %g -> %g\n", sigma, beta, *kappa); } else { *kappa = model_kappa; From 4f555647088adc579d40eef76dcbf86eacb252b6 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Fri, 17 Sep 2021 15:16:57 -0500 Subject: [PATCH 79/93] Control behavior of variable kappa fits with parameters --- src/model_radiation.c | 15 +++++++---- src/symphony/fits.c | 60 ++++++++++++++++++++++++++----------------- src/symphony/params.h | 2 ++ 3 files changed, 48 insertions(+), 29 deletions(-) diff --git a/src/model_radiation.c b/src/model_radiation.c index 4b364e6..0e936a3 100644 --- a/src/model_radiation.c +++ b/src/model_radiation.c @@ -66,6 +66,9 @@ static double powerlaw_gamma_max = 1e5; static double powerlaw_p = 3.25; static double powerlaw_eta = 0.02; static int variable_kappa = 0; +static double variable_kappa_min = 3.1; +static double variable_kappa_interp_start = 1e20; +static double variable_kappa_max = 7.0; static double max_pol_frac_e = 0.99; static double max_pol_frac_a = 0.99; static int do_bremss = 0; @@ -75,6 +78,9 @@ void try_set_radiation_parameter(const char *word, const char *value) { set_by_word_val(word, value, "kappa", &model_kappa, TYPE_DBL); set_by_word_val(word, value, "variable_kappa", &variable_kappa, TYPE_INT); + set_by_word_val(word, value, "variable_kappa_min", &variable_kappa_min, TYPE_DBL); + set_by_word_val(word, value, "variable_kappa_interp_start", &variable_kappa_interp_start, TYPE_DBL); + set_by_word_val(word, value, "variable_kappa_max", &variable_kappa_max, TYPE_DBL); set_by_word_val(word, value, "powerlaw_gamma_cut", &powerlaw_gamma_cut, TYPE_DBL); set_by_word_val(word, value, "powerlaw_gamma_min", &powerlaw_gamma_min, TYPE_DBL); @@ -182,6 +188,8 @@ void jar_calc_dist(int dist, int pol, double X[NDIM], double Kcon[NDIM], break; case E_KAPPA: // Kappa fits (Pandya + Marszewski) paramsM.distribution = paramsM.KAPPA_DIST; + paramsM.kappa_interp_begin = fmin(variable_kappa_interp_start, variable_kappa_max); + paramsM.kappa_interp_end = variable_kappa_max; paramsM.theta_e = get_model_thetae(X); get_model_kappa(X, &(paramsM.kappa), &(paramsM.kappa_width)); break; @@ -331,11 +339,8 @@ void get_model_kappa(double X[NDIM], double *kappa, double *kappa_width) { double B = get_model_b(X); fprintf(stderr, "kappa, sigma, beta, B: %g %g %g %g \n", *kappa, sigma, beta, B); } - // Limit kappa - // This is already a bit generous with the fits' domain of validity, - // tread cautiously - if (*kappa < 3.1) *kappa = 3.1; - if (*kappa > 10.0) *kappa = 10.0; + // Lower limit for kappa. Upper limit switches away from kappa fit entirely -> thermal + if (*kappa < variable_kappa_min) *kappa = variable_kappa_min; //fprintf(stderr, "sigma, beta -> kappa %g %g -> %g\n", sigma, beta, *kappa); } else { diff --git a/src/symphony/fits.c b/src/symphony/fits.c index 09dc1da..4a79d0f 100644 --- a/src/symphony/fits.c +++ b/src/symphony/fits.c @@ -42,28 +42,34 @@ double j_nu_fit(struct parameters *params, int polarization) else if(params->distribution == params->KAPPA_DIST) { if (params->polarization == params->STOKES_I) { - if (params->kappa < 7.5) { + if (params->kappa < params->kappa_interp_begin) { return kappa_I(params); - } else if (params->kappa >= 7.5 && params->kappa < 15) { - return ((15 - params->kappa) * maxwell_juettner_I(params) + (params->kappa - 7.5) * kappa_I(params)) / 7.5; + } else if (params->kappa >= params->kappa_interp_begin && params->kappa < params->kappa_interp_end) { + return ((params->kappa_interp_end - params->kappa) * maxwell_juettner_I(params) + + (params->kappa - params->kappa_interp_begin) * kappa_I(params)) / + (params->kappa_interp_end - params->kappa_interp_begin); } else { return maxwell_juettner_I(params); } } else if (params->polarization == params->STOKES_Q) { - if (params->kappa < 7.5) { + if (params->kappa < params->kappa_interp_begin) { return kappa_Q(params); - } else if (params->kappa >= 7.5 && params->kappa < 15) { - return ((15 - params->kappa) * maxwell_juettner_Q(params) + (params->kappa - 7.5) * kappa_Q(params)) / 7.5; + } else if (params->kappa >= params->kappa_interp_begin && params->kappa < params->kappa_interp_end) { + return ((params->kappa_interp_end - params->kappa) * maxwell_juettner_Q(params) + + (params->kappa - params->kappa_interp_begin) * kappa_Q(params)) / + (params->kappa_interp_end - params->kappa_interp_begin); } else { return maxwell_juettner_Q(params); } } else if (params->polarization == params->STOKES_U) { return 0.; } else if (params->polarization == params->STOKES_V) { - if (params->kappa < 7.5) { + if (params->kappa < params->kappa_interp_begin) { return kappa_V(params); - } else if (params->kappa >= 7.5 && params->kappa < 15) { - return ((15 - params->kappa) * maxwell_juettner_V(params) + (params->kappa - 7.5) * kappa_V(params)) / 7.5; + } else if (params->kappa >= params->kappa_interp_begin && params->kappa < params->kappa_interp_end) { + return ((params->kappa_interp_end - params->kappa) * maxwell_juettner_V(params) + + (params->kappa - params->kappa_interp_begin) * kappa_V(params)) / + (params->kappa_interp_end - params->kappa_interp_begin); } else { return maxwell_juettner_V(params); } @@ -111,28 +117,34 @@ double alpha_nu_fit(struct parameters *params, int polarization) else if(params->distribution == params->KAPPA_DIST) { if (params->polarization == params->STOKES_I) { - if (params->kappa < 7.5) { + if (params->kappa < params->kappa_interp_begin) { return kappa_I_abs(params); - } else if (params->kappa >= 7.5 && params->kappa < 15) { - return ((15 - params->kappa) * maxwell_juettner_I_abs(params) + (params->kappa - 7.5) * kappa_I_abs(params)) / 7.5; + } else if (params->kappa >= params->kappa_interp_begin && params->kappa < params->kappa_interp_end) { + return ((15 - params->kappa) * maxwell_juettner_I_abs(params) + + (params->kappa - params->kappa_interp_begin) * kappa_I_abs(params)) / + (params->kappa_interp_end - params->kappa_interp_begin); } else { return maxwell_juettner_I_abs(params); } } else if(params->polarization == params->STOKES_Q) { - if (params->kappa < 7.5) { + if (params->kappa < params->kappa_interp_begin) { return kappa_Q_abs(params); - } else if (params->kappa >= 7.5 && params->kappa < 15) { - return ((15 - params->kappa) * maxwell_juettner_Q_abs(params) + (params->kappa - 7.5) * kappa_Q_abs(params)) / 7.5; + } else if (params->kappa >= params->kappa_interp_begin && params->kappa < params->kappa_interp_end) { + return ((params->kappa_interp_end - params->kappa) * maxwell_juettner_Q_abs(params) + + (params->kappa - params->kappa_interp_begin) * kappa_Q_abs(params)) / + (params->kappa_interp_end - params->kappa_interp_begin); } else { return maxwell_juettner_Q_abs(params); } } else if(params->polarization == params->STOKES_U) { return 0.; } else if(params->polarization == params->STOKES_V) { - if (params->kappa < 7.5) { + if (params->kappa < params->kappa_interp_begin) { return kappa_V_abs(params); - } else if (params->kappa >= 7.5 && params->kappa < 15) { - return ((15 - params->kappa) * maxwell_juettner_V_abs(params) + (params->kappa - 7.5) * kappa_V_abs(params)) / 7.5; + } else if (params->kappa >= params->kappa_interp_begin && params->kappa < params->kappa_interp_end) { + return ((params->kappa_interp_end - params->kappa) * maxwell_juettner_V_abs(params) + + (params->kappa - params->kappa_interp_begin) * kappa_V_abs(params)) / + (params->kappa_interp_end - params->kappa_interp_begin); } else { return maxwell_juettner_V_abs(params); } @@ -191,9 +203,9 @@ double rho_nu_fit(struct parameters *params, int polarization) return ((4.5 - params->kappa) * kappa4_rho_Q(params) + (params->kappa - 4.0) * kappa45_rho_Q(params)) / 0.5; else if (params->kappa >= 4.5 && params->kappa < 5.0) return ((5.0 - params->kappa) * kappa45_rho_Q(params) + (params->kappa - 4.5) * kappa5_rho_Q(params)) / 0.5; - else if (params->kappa >= 5.0 && params->kappa <= 10.0) - return ((10.0 - params->kappa) * kappa5_rho_Q(params) + (params->kappa - 5.0) * maxwell_juettner_rho_Q(params)) / 5.0; - else if (params->kappa > 10.0) + else if (params->kappa >= 5.0 && params->kappa <= 8.0) + return ((8.0 - params->kappa) * kappa5_rho_Q(params) + (params->kappa - 5.0) * maxwell_juettner_rho_Q(params)) / 5.0; + else if (params->kappa > 8.0) return maxwell_juettner_rho_Q(params); } else if(params->polarization == params->STOKES_U) return 0.; @@ -207,9 +219,9 @@ double rho_nu_fit(struct parameters *params, int polarization) return ((4.5 - params->kappa) * kappa4_rho_V(params) + (params->kappa - 4.0) * kappa45_rho_V(params)) / 0.5; else if(params->kappa >= 4.5 && params->kappa <= 5.0) return ((5.0 - params->kappa) * kappa45_rho_V(params) + (params->kappa - 4.5) * kappa5_rho_V(params)) / 0.5; - else if (params->kappa >= 5.0 && params->kappa <= 10.0) - return ((10.0 - params->kappa) * kappa5_rho_V(params) + (params->kappa - 5.0) * maxwell_juettner_rho_V(params)) / 5.0; - else if (params->kappa > 10.0) + else if (params->kappa >= 5.0 && params->kappa <= 8.0) + return ((8.0 - params->kappa) * kappa5_rho_V(params) + (params->kappa - 5.0) * maxwell_juettner_rho_V(params)) / 5.0; + else if (params->kappa > 8.0) return maxwell_juettner_rho_V(params); } } else if(params->distribution == params->POWER_LAW) { diff --git a/src/symphony/params.h b/src/symphony/params.h index 5ae30bc..7b24b45 100644 --- a/src/symphony/params.h +++ b/src/symphony/params.h @@ -54,6 +54,8 @@ struct parameters /*kappa distribution parameters*/ double kappa; double kappa_width; + double kappa_interp_begin; + double kappa_interp_end; /*Choose if n-space peak is known, or if it must be found adaptively */ int use_n_peak; From 1624448a56d0bbd74f214437dd5cdf9a51cd16b7 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Sun, 19 Sep 2021 12:10:21 -0500 Subject: [PATCH 80/93] Re-enable guard for zero emission angle in unpolarized transport, make kappa distributions fall back to Dexter thermal coefficients mirroring default behavior --- src/model_radiation.c | 143 +++++++++++++++------------ src/radiation.c | 2 +- src/symphony/fits.c | 9 +- src/symphony/maxwell_juettner_fits.c | 21 ++-- 4 files changed, 96 insertions(+), 79 deletions(-) diff --git a/src/model_radiation.c b/src/model_radiation.c index 0e936a3..6f8dde9 100644 --- a/src/model_radiation.c +++ b/src/model_radiation.c @@ -180,14 +180,10 @@ void jar_calc_dist(int dist, int pol, double X[NDIM], double Kcon[NDIM], // ...and then the specific distribution and its parameters. switch (dist) { - case E_DEXTER_THERMAL: // Dexter thermal fits (default) - paramsM.dexter_fit = 1; - case E_THERMAL: // Otherwise Pandya thermal fits - paramsM.distribution = paramsM.MAXWELL_JUETTNER; - paramsM.theta_e = get_model_thetae(X); - break; case E_KAPPA: // Kappa fits (Pandya + Marszewski) paramsM.distribution = paramsM.KAPPA_DIST; + // Fall back to Dexter starting at kappa > kappa_interp_start, completely at kappa_max + paramsM.dexter_fit = 1; // This only affects choice of thermal fallback paramsM.kappa_interp_begin = fmin(variable_kappa_interp_start, variable_kappa_max); paramsM.kappa_interp_end = variable_kappa_max; paramsM.theta_e = get_model_thetae(X); @@ -199,11 +195,36 @@ void jar_calc_dist(int dist, int pol, double X[NDIM], double Kcon[NDIM], get_model_powerlaw_vals(X, &(paramsM.power_law_p), &(paramsM.electron_density), &(paramsM.gamma_min), &(paramsM.gamma_max), &(paramsM.gamma_cutoff)); break; + case E_THERMAL: // Pandya thermal fits + paramsM.distribution = paramsM.MAXWELL_JUETTNER; + paramsM.theta_e = get_model_thetae(X); + break; + case E_DEXTER_THERMAL: // Dexter thermal fits (default) + default: + paramsM.dexter_fit = 1; + paramsM.distribution = paramsM.MAXWELL_JUETTNER; + paramsM.theta_e = get_model_thetae(X); + break; + } + + // First, enforce no emission/absorption along field lines, + // but allow Faraday rotation in polarized context + // TODO this skips any rho_V NaN/other checks + if (theta <= 0.0 || theta >= M_PI) { + *jI = 0.0; *jQ = 0.0; *jU = 0.0; *jV = 0.0; + *aI = 0.0; *aQ = 0.0; *aU = 0.0; *aV = 0.0; + *rQ = 0.0; *rU = 0.0; + if (pol && !(dist == E_UNPOL)) { + *rV = rho_nu_fit(¶msM, paramsM.STOKES_V) * nu; + } else { + *rV = 0.0; + } + return; } - // If performing unpolarized transport, calculate only what we need + // Then, if performing unpolarized transport, calculate only what we need if (!pol || dist == E_UNPOL) { - if (dist == E_THERMAL || dist == E_DEXTER_THERMAL || dist > 10){ + if (paramsM.distribution == paramsM.MAXWELL_JUETTNER) { paramsM.dexter_fit = 2; // Signal symphony fits to use Leung+ *jI = j_nu_fit(¶msM, paramsM.STOKES_I); if(do_bremss) *jI += bremss_I(¶msM, bremss_type); @@ -211,69 +232,65 @@ void jar_calc_dist(int dist, int pol, double X[NDIM], double Kcon[NDIM], double Bnuinv = Bnu_inv(nu, paramsM.theta_e); // Planck function *aI = *jI / Bnuinv; } else { + paramsM.dexter_fit = 2; // Signal symphony fits to use Leung+ as fallback *jI = j_nu_fit(¶msM, paramsM.STOKES_I) / nusq; *aI = alpha_nu_fit(¶msM, paramsM.STOKES_I) * nu; } - } else { - // Avoid issues directly along field lines - if (theta <= 0 || theta >= M_PI) { - *jI = 0.0; *jQ = 0.0; *jU = 0.0; *jV = 0.0; - *aI = 0.0; *aQ = 0.0; *aU = 0.0; *aV = 0.0; - *rQ = 0.0; *rU = 0.0; - *rV = rho_nu_fit(¶msM, paramsM.STOKES_V) * nu; + } else { // Finally, calculate all coefficients normally + // EMISSIVITIES + *jI = j_nu_fit(¶msM, paramsM.STOKES_I); + // Bremsstrahlung is computed only for thermal; + // silently drop Bremss+other dists, as absorptivities will be wrong + if(paramsM.distribution == paramsM.MAXWELL_JUETTNER && do_bremss) + *jI += bremss_I(¶msM, bremss_type); + *jI /= nusq; // Avoids loss of precision in small numbers + + *jQ = -j_nu_fit(¶msM, paramsM.STOKES_Q) / nusq; + *jU = -j_nu_fit(¶msM, paramsM.STOKES_U) / nusq; + *jV = j_nu_fit(¶msM, paramsM.STOKES_V) / nusq; + // Check basic relationships + double jP = sqrt(*jQ * *jQ + *jU * *jU + *jV * *jV); + if (*jI < jP/max_pol_frac_e) { + // Transport does not like 100% polarization... + double pol_frac_e = *jI / jP * max_pol_frac_e; + *jQ *= pol_frac_e; + *jU *= pol_frac_e; + *jV *= pol_frac_e; + } + + // ABSORPTIVITIES + if (paramsM.distribution == paramsM.MAXWELL_JUETTNER) { // Thermal distributions + // Get absorptivities via Kirchoff's law + // Already invariant, guaranteed to respect aI > aP + // Faster than calling Symphony code since we know jS, Bnu + double Bnuinv = Bnu_inv(nu, paramsM.theta_e); // Planck function + *aI = *jI / Bnuinv; + *aQ = *jQ / Bnuinv; + *aU = *jU / Bnuinv; + *aV = *jV / Bnuinv; } else { - // EMISSIVITIES - *jI = j_nu_fit(¶msM, paramsM.STOKES_I); - if(do_bremss) *jI += bremss_I(¶msM, bremss_type); - *jI /= nusq; // Avoids loss of precision in small numbers - *jQ = -j_nu_fit(¶msM, paramsM.STOKES_Q) / nusq; - *jU = -j_nu_fit(¶msM, paramsM.STOKES_U) / nusq; - *jV = j_nu_fit(¶msM, paramsM.STOKES_V) / nusq; + *aI = alpha_nu_fit(¶msM, paramsM.STOKES_I) * nu; + // Note Bremss emission is available for thermal dists *only* + *aQ = -alpha_nu_fit(¶msM, paramsM.STOKES_Q) * nu; + *aU = -alpha_nu_fit(¶msM, paramsM.STOKES_U) * nu; + *aV = alpha_nu_fit(¶msM, paramsM.STOKES_V) * nu; + // Check basic relationships - double jP = sqrt(*jQ * *jQ + *jU * *jU + *jV * *jV); - if (*jI < jP/max_pol_frac_e) { + double aP = sqrt(*aQ * *aQ + *aU * *aU + *aV * *aV); + if (*aI < aP/max_pol_frac_a) { // Transport does not like 100% polarization... - double pol_frac_e = *jI / jP * max_pol_frac_e; - *jQ *= pol_frac_e; - *jU *= pol_frac_e; - *jV *= pol_frac_e; - } - - // ABSORPTIVITIES - if (dist == E_THERMAL || dist == E_DEXTER_THERMAL || dist > 10) { // Thermal distributions - // Get absorptivities via Kirchoff's law - // Already invariant, guaranteed to respect aI > aP - // Faster than calling Symphony code since we know jS, Bnu - double Bnuinv = Bnu_inv(nu, paramsM.theta_e); // Planck function - *aI = *jI / Bnuinv; - *aQ = *jQ / Bnuinv; - *aU = *jU / Bnuinv; - *aV = *jV / Bnuinv; - } else { - *aI = alpha_nu_fit(¶msM, paramsM.STOKES_I) * nu; - // Note Bremss emission is available for thermal dists *only* - *aQ = -alpha_nu_fit(¶msM, paramsM.STOKES_Q) * nu; - *aU = -alpha_nu_fit(¶msM, paramsM.STOKES_U) * nu; - *aV = alpha_nu_fit(¶msM, paramsM.STOKES_V) * nu; - - // Check basic relationships - double aP = sqrt(*aQ * *aQ + *aU * *aU + *aV * *aV); - if (*aI < aP/max_pol_frac_a) { - // Transport does not like 100% polarization... - double pol_frac_a = *aI / aP * max_pol_frac_a; - *aQ *= pol_frac_a; - *aU *= pol_frac_a; - *aV *= pol_frac_a; - } + double pol_frac_a = *aI / aP * max_pol_frac_a; + *aQ *= pol_frac_a; + *aU *= pol_frac_a; + *aV *= pol_frac_a; } - - - // ROTATIVITIES - paramsM.dexter_fit = 0; // Don't use the Dexter rhoV, as it's unstable at low temperature - *rQ = rho_nu_fit(¶msM, paramsM.STOKES_Q) * nu; - *rU = rho_nu_fit(¶msM, paramsM.STOKES_U) * nu; - *rV = rho_nu_fit(¶msM, paramsM.STOKES_V) * nu; } + + // ROTATIVITIES + paramsM.dexter_fit = 0; // Don't use the Dexter rhoV, as it's unstable at low temperature + *rQ = rho_nu_fit(¶msM, paramsM.STOKES_Q) * nu; + *rU = rho_nu_fit(¶msM, paramsM.STOKES_U) * nu; + *rV = rho_nu_fit(¶msM, paramsM.STOKES_V) * nu; } #if DEBUG diff --git a/src/radiation.c b/src/radiation.c index 738cebc..309e025 100644 --- a/src/radiation.c +++ b/src/radiation.c @@ -11,7 +11,7 @@ radiation-related utilities. #include "decs.h" -double Bnu_inv (double nu, double Thetae) +double Bnu_inv(double nu, double Thetae) { double x = HPL * nu / (ME * CL * CL * Thetae); diff --git a/src/symphony/fits.c b/src/symphony/fits.c index 4a79d0f..98ec3a0 100644 --- a/src/symphony/fits.c +++ b/src/symphony/fits.c @@ -264,10 +264,11 @@ void check_for_errors(struct parameters *params) exit(0); } if(params->distribution == params->KAPPA_DIST) { - if(params->kappa < 2.5 || params->kappa > 7.5) - { - printf("\n WARNING: kappa out of range of fitting formula \n"); - } + // This is taken care of by switching fits, see above + // if(params->kappa < 2.5 || params->kappa > 7.5) + // { + // printf("\n WARNING: kappa out of range of fitting formula \n"); + // } //if(params->kappa_width < 3 || if (params->kappa_width > 200) { diff --git a/src/symphony/maxwell_juettner_fits.c b/src/symphony/maxwell_juettner_fits.c index 195e9a3..6e8bc4e 100644 --- a/src/symphony/maxwell_juettner_fits.c +++ b/src/symphony/maxwell_juettner_fits.c @@ -1,6 +1,7 @@ #include "maxwell_juettner.h" #include "constants.h" +#include "radiation.h" #include @@ -137,16 +138,7 @@ double maxwell_juettner_V(struct parameters *params) */ double planck_func(struct parameters *params) { - double term1 = (2.*params->plancks_constant*pow(params->nu, 3.)) - /pow(params->speed_light, 2.); - - double term2 = (exp(params->plancks_constant*params->nu - /(params->theta_e*params->mass_electron - *pow(params->speed_light, 2.)))-1.); - - double ans = term1 / term2; - - return ans; + return Bnu_inv(params->nu, params->theta_e) * pow(params->nu, 3); } /*maxwell_juettner_I_abs: Fitting formula for the absorptivity, polarized in @@ -315,7 +307,14 @@ double maxwell_juettner_dexter_V(struct parameters *params) double theta = params->observer_angle; double nus = 3.0 * EE * B * sin(theta) / 4.0 / M_PI / ME / CL * Thetae * Thetae + 1.0; double x = nu / nus; - return 2. * Ne * EE * EE * nu / tan(theta) / 3. / sqrt(3) / CL / Thetae / Thetae / Thetae * I_V(x); + double ans = 2. * Ne * EE * EE * nu / tan(theta) / 3. / sqrt(3) / CL / Thetae / Thetae / Thetae * I_V(x); +#if DEBUG + if (isnan(ans)) { + fprintf(stderr, "NaN in Dexter jV. Ne, nu, Thetae, B, theta, nus, x: %g %g %g %g %g %g %g\n", + Ne, nu, Thetae, B, theta, nus, x); + } +#endif + return ans; } // Supporting functions From 75d7dedc74be3211ac8aaba77a9d2943b20bf80d Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Thu, 21 Oct 2021 15:58:05 -0500 Subject: [PATCH 81/93] Fix NaN value when jX==Bnu==0 --- src/model_radiation.c | 15 +++++++++++---- src/symphony/maxwell_juettner_fits.c | 18 +++++++++++++++--- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/model_radiation.c b/src/model_radiation.c index 6f8dde9..cac00ca 100644 --- a/src/model_radiation.c +++ b/src/model_radiation.c @@ -264,10 +264,17 @@ void jar_calc_dist(int dist, int pol, double X[NDIM], double Kcon[NDIM], // Already invariant, guaranteed to respect aI > aP // Faster than calling Symphony code since we know jS, Bnu double Bnuinv = Bnu_inv(nu, paramsM.theta_e); // Planck function - *aI = *jI / Bnuinv; - *aQ = *jQ / Bnuinv; - *aU = *jU / Bnuinv; - *aV = *jV / Bnuinv; + if (Bnuinv > 0) { + *aI = *jI / Bnuinv; + *aQ = *jQ / Bnuinv; + *aU = *jU / Bnuinv; + *aV = *jV / Bnuinv; + } else { + *aI = 0.; + *aQ = 0.; + *aU = 0.; + *aV = 0.; + } } else { *aI = alpha_nu_fit(¶msM, paramsM.STOKES_I) * nu; // Note Bremss emission is available for thermal dists *only* diff --git a/src/symphony/maxwell_juettner_fits.c b/src/symphony/maxwell_juettner_fits.c index 6e8bc4e..381fd85 100644 --- a/src/symphony/maxwell_juettner_fits.c +++ b/src/symphony/maxwell_juettner_fits.c @@ -151,7 +151,11 @@ double planck_func(struct parameters *params) */ double maxwell_juettner_I_abs(struct parameters *params) { - double ans = maxwell_juettner_I(params)/planck_func(params); + double Bnu = planck_func(params); + double ans = 0.; + if (Bnu > 0.) { + ans = maxwell_juettner_I(params)/Bnu; + } return ans; } @@ -165,7 +169,11 @@ double maxwell_juettner_I_abs(struct parameters *params) */ double maxwell_juettner_Q_abs(struct parameters *params) { - double ans = maxwell_juettner_Q(params)/planck_func(params); + double Bnu = planck_func(params); + double ans = 0.; + if (Bnu > 0.) { + ans = maxwell_juettner_Q(params)/Bnu; + } return ans; } @@ -179,7 +187,11 @@ double maxwell_juettner_Q_abs(struct parameters *params) */ double maxwell_juettner_V_abs(struct parameters *params) { - double ans = maxwell_juettner_V(params)/planck_func(params); + double Bnu = planck_func(params); + double ans = 0.; + if (Bnu > 0.) { + ans = maxwell_juettner_V(params)/Bnu; + } return ans; } From f1153e9e3a2f0fcdf055f06ff1c8f906c111173a Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Tue, 26 Oct 2021 16:42:44 -0500 Subject: [PATCH 82/93] Fix NaN in a previously-missed spot we use the blackbody function, in non-thermal transport --- src/model_radiation.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/model_radiation.c b/src/model_radiation.c index cac00ca..80449bf 100644 --- a/src/model_radiation.c +++ b/src/model_radiation.c @@ -230,7 +230,11 @@ void jar_calc_dist(int dist, int pol, double X[NDIM], double Kcon[NDIM], if(do_bremss) *jI += bremss_I(¶msM, bremss_type); *jI /= nusq; // Avoids loss of precision in small numbers double Bnuinv = Bnu_inv(nu, paramsM.theta_e); // Planck function - *aI = *jI / Bnuinv; + if (Bnuinv > 0) { + *aI = *jI / Bnuinv; + } else { + *aI = 0; + } } else { paramsM.dexter_fit = 2; // Signal symphony fits to use Leung+ as fallback *jI = j_nu_fit(¶msM, paramsM.STOKES_I) / nusq; From 164a16507a7cef4d66e4e9566ffef9cdabd20af2 Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Thu, 6 Jan 2022 13:25:40 -0700 Subject: [PATCH 83/93] Update model_radiation.c Redefine a very generically-named macro to avoid name collisions with math.h. --- src/model_radiation.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/model_radiation.c b/src/model_radiation.c index 80449bf..18ce504 100644 --- a/src/model_radiation.c +++ b/src/model_radiation.c @@ -43,7 +43,7 @@ // Debugging/internal #define E_UNPOL 15 -#define OVERFLOW 1e100 +#define RAD_OVERFLOW 1e100 // Local functions for declaring different kappa/powerlaw distributions void get_model_powerlaw_vals(double X[NDIM], double *p, double *n, @@ -306,17 +306,17 @@ void jar_calc_dist(int dist, int pol, double X[NDIM], double Kcon[NDIM], #if DEBUG // Check for NaN coefficients - if (isnan(*jI) || *jI > OVERFLOW || *jI < -OVERFLOW || - isnan(*jQ) || *jQ > OVERFLOW || *jQ < -OVERFLOW || - isnan(*jU) || *jU > OVERFLOW || *jU < -OVERFLOW || - isnan(*jV) || *jV > OVERFLOW || *jV < -OVERFLOW || - isnan(*aI) || *aI > OVERFLOW || *aI < -OVERFLOW || - isnan(*aQ) || *aQ > OVERFLOW || *aQ < -OVERFLOW || - isnan(*aU) || *aU > OVERFLOW || *aU < -OVERFLOW || - isnan(*aV) || *aV > OVERFLOW || *aV < -OVERFLOW || - isnan(*rQ) || *rQ > OVERFLOW || *rQ < -OVERFLOW || - isnan(*rU) || *rU > OVERFLOW || *rU < -OVERFLOW || - isnan(*rV) || *rV > OVERFLOW || *rV < -OVERFLOW) { + if (isnan(*jI) || *jI > RAD_OVERFLOW || *jI < -RAD_OVERFLOW || + isnan(*jQ) || *jQ > RAD_OVERFLOW || *jQ < -RAD_OVERFLOW || + isnan(*jU) || *jU > RAD_OVERFLOW || *jU < -RAD_OVERFLOW || + isnan(*jV) || *jV > RAD_OVERFLOW || *jV < -RAD_OVERFLOW || + isnan(*aI) || *aI > RAD_OVERFLOW || *aI < -RAD_OVERFLOW || + isnan(*aQ) || *aQ > RAD_OVERFLOW || *aQ < -RAD_OVERFLOW || + isnan(*aU) || *aU > RAD_OVERFLOW || *aU < -RAD_OVERFLOW || + isnan(*aV) || *aV > RAD_OVERFLOW || *aV < -RAD_OVERFLOW || + isnan(*rQ) || *rQ > RAD_OVERFLOW || *rQ < -RAD_OVERFLOW || + isnan(*rU) || *rU > RAD_OVERFLOW || *rU < -RAD_OVERFLOW || + isnan(*rV) || *rV > RAD_OVERFLOW || *rV < -RAD_OVERFLOW) { #pragma omp critical { fprintf(stderr, "\nNAN in emissivities!\n"); @@ -382,4 +382,4 @@ void get_model_kappa(double X[NDIM], double *kappa, double *kappa_width) { fprintf(stderr, "NaN kappa val! kappa, kappa_width, Thetae: %g %g %g\n", *kappa, *kappa_width, Thetae); } #endif -} \ No newline at end of file +} From 72147351f75875fdc0fd04e8da54734d1c9d3e9d Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Mon, 10 Jan 2022 14:44:31 -0600 Subject: [PATCH 84/93] Fix test scripts on some machines by specifying python3 --- tests/verify.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/verify.sh b/tests/verify.sh index 64f653e..cf1ac80 100755 --- a/tests/verify.sh +++ b/tests/verify.sh @@ -12,4 +12,4 @@ echo "Pixels different by >1% relative" h5diff -p 1.e-2 ../test-resources/${folder}_image.h5 image.h5 /pol # ...but we should only fail if this one returns nonzero -python ../../scripts/compare.py ../test-resources/${folder}_image.h5 image.h5 +../../scripts/compare.py ../test-resources/${folder}_image.h5 image.h5 From f3ca05d4ef7fee5dd5338025a0025672b6ba9c9e Mon Sep 17 00:00:00 2001 From: Ben Prather Date: Thu, 13 Jan 2022 13:43:20 -0700 Subject: [PATCH 85/93] Correct a very old typo One of the coefficients in the j_V fit taken from Dexter (2016) was mistyped in the last two digits, as 1.81348 instead of 1.81384. The typo appears to date back to the very first version of ipole. Correcting this should have no discernible effect on transport whatsoever, but might make the code easier to read & compare in the future. --- src/symphony/maxwell_juettner_fits.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/symphony/maxwell_juettner_fits.c b/src/symphony/maxwell_juettner_fits.c index 381fd85..1c77865 100644 --- a/src/symphony/maxwell_juettner_fits.c +++ b/src/symphony/maxwell_juettner_fits.c @@ -349,7 +349,7 @@ double I_Q(double x) double I_V(double x) { - return (1.81348 / x + 3.42319 * pow(x, -2. / 3.) + + return (1.81384 / x + 3.42319 * pow(x, -2. / 3.) + 0.0292545 * pow(x, -0.5) + 2.03773 * pow(x, -1. / 3.)) * exp(-1.8899 * pow(x, 1. / 3.)); From e760a4d9ab4b1f876140e73dcaeba600432c523c Mon Sep 17 00:00:00 2001 From: "George N. Wong" Date: Tue, 1 Feb 2022 16:52:07 -0500 Subject: [PATCH 86/93] add sigma_cut_high to allow for sigmoid-like sigma cutoff --- model/iharm/model.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/model/iharm/model.c b/model/iharm/model.c index 33ada1c..b24fda3 100644 --- a/model/iharm/model.c +++ b/model/iharm/model.c @@ -46,6 +46,7 @@ static double mu_i, mu_e, mu_tot; double DTd; double sigma_cut = 1.0; double beta_crit = 1.0; +double sigma_cut_high = -1.0; // MODEL PARAMETERS: PRIVATE static char fnam[STRLEN] = "dump.h5"; @@ -134,6 +135,7 @@ void try_set_model_parameter(const char *word, const char *value) set_by_word_val(word, value, "trat_small", &trat_small, TYPE_DBL); set_by_word_val(word, value, "trat_large", &trat_large, TYPE_DBL); set_by_word_val(word, value, "sigma_cut", &sigma_cut, TYPE_DBL); + set_by_word_val(word, value, "sigma_cut_high", &sigma_cut_high, TYPE_DBL); set_by_word_val(word, value, "beta_crit", &beta_crit, TYPE_DBL); set_by_word_val(word, value, "cooling_dynamical_times", &cooling_dynamical_times, TYPE_DBL); @@ -496,19 +498,32 @@ double get_model_beta(double X[NDIM]) return tfac*betaA + (1. - tfac)*betaB; } +double get_sigma_smoothfac(double sigma) +{ + double sigma_above = sigma_cut; + if (sigma_cut_high > 0) sigma_above = sigma_cut_high; + if (sigma < sigma_cut) return 1; + if (sigma >= sigma_above) return 0; + double dsig = sigma_above - sigma_cut; + return cos(M_PI / 2. / dsig * (sigma - sigma_cut)); +} + double get_model_ne(double X[NDIM]) { if ( X_in_domain(X) == 0 ) return 0.; + double sigma_smoothfac = 1; + #if USE_GEODESIC_SIGMACUT double sigma = get_model_sigma(X); if (sigma > sigma_cut) return 0.; + sigma_smoothfac = get_sigma_smoothfac(sigma); #endif int nA, nB; double tfac = set_tinterp_ns(X, &nA, &nB); - return interp_scalar_time(X, data[nA]->ne, data[nB]->ne, tfac); + return interp_scalar_time(X, data[nA]->ne, data[nB]->ne, tfac) * sigma_smoothfac; } void set_units() From 1e6e39bf4dc3472e2b89c25803f542c4f124f171 Mon Sep 17 00:00:00 2001 From: George Wong Date: Mon, 18 Apr 2022 16:13:32 -0500 Subject: [PATCH 87/93] add command line argument to target mdot --- model/iharm/model.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/model/iharm/model.c b/model/iharm/model.c index b24fda3..f377f50 100644 --- a/model/iharm/model.c +++ b/model/iharm/model.c @@ -38,6 +38,8 @@ double U_unit; double B_unit; double Te_unit; +double target_mdot = 0.0; // if this value > 0, use to renormalize M_unit &c. + // MOLECULAR WEIGHTS static double Ne_factor = 1.; // e.g., used for He with 2 protons+neutrons per 2 electrons static double mu_i, mu_e, mu_tot; @@ -128,6 +130,7 @@ void try_set_model_parameter(const char *word, const char *value) // assume params is populated set_by_word_val(word, value, "MBH", &MBH_solar, TYPE_DBL); set_by_word_val(word, value, "M_unit", &M_unit, TYPE_DBL); + set_by_word_val(word, value, "mdot", &target_mdot, TYPE_DBL); set_by_word_val(word, value, "dump", (void *)fnam, TYPE_STR); @@ -1521,6 +1524,20 @@ void load_hamr_data(int n, char *fnam, int dumpidx, int verbose) MdotEdd_dump = Mdotedd; Ladv_dump = Ladv; + if (target_mdot > 0) { + fprintf(stderr, "resetting M_unit to match target_mdot = %g\n", target_mdot); + + double current_mdot = Mdot_dump/MdotEdd_dump; + fprintf(stderr, "was %g is now %g\n", M_unit, M_unit * fabs(target_mdot / current_mdot)); + M_unit *= fabs(target_mdot / current_mdot); + + set_units(); + } + + Mdot_dump = -dMact*M_unit/T_unit; + MdotEdd_dump = Mdotedd; + Ladv_dump = Ladv; + if (verbose == 2) { fprintf(stderr,"dMact: %g [code]\n",dMact); fprintf(stderr,"Ladv: %g [code]\n",Ladv_dump); @@ -1705,6 +1722,20 @@ void load_koral_data(int n, char *fnam, int dumpidx, int verbose) MdotEdd_dump = Mdotedd; Ladv_dump = Ladv; + if (target_mdot > 0) { + fprintf(stderr, "resetting M_unit to match target_mdot = %g\n", target_mdot); + + double current_mdot = Mdot_dump/MdotEdd_dump; + fprintf(stderr, "was %g is now %g\n", M_unit, M_unit * fabs(target_mdot / current_mdot)); + M_unit *= fabs(target_mdot / current_mdot); + + set_units(); + } + + Mdot_dump = -dMact*M_unit/T_unit; + MdotEdd_dump = Mdotedd; + Ladv_dump = Ladv; + if (verbose == 2) { fprintf(stderr,"dMact: %g [code]\n",dMact); fprintf(stderr,"Ladv: %g [code]\n",Ladv_dump); @@ -1946,6 +1977,20 @@ void load_iharm_data(int n, char *fnam, int dumpidx, int verbose) MdotEdd_dump = Mdotedd; Ladv_dump = Ladv; + if (target_mdot > 0) { + fprintf(stderr, "resetting M_unit to match target_mdot = %g\n", target_mdot); + + double current_mdot = Mdot_dump/MdotEdd_dump; + fprintf(stderr, "was %g is now %g\n", M_unit, M_unit * fabs(target_mdot / current_mdot)); + M_unit *= fabs(target_mdot / current_mdot); + + set_units(); + } + + Mdot_dump = -dMact*M_unit/T_unit; + MdotEdd_dump = Mdotedd; + Ladv_dump = Ladv; + if (verbose == 2) { fprintf(stderr,"dMact: %g [code]\n",dMact); fprintf(stderr,"Ladv: %g [code]\n",Ladv_dump); From f6c18735600a2c55e167dd328277cd74f5a501c3 Mon Sep 17 00:00:00 2001 From: George Wong Date: Tue, 19 Apr 2022 13:39:13 -0500 Subject: [PATCH 88/93] update code to automatically override M_unit -> 1 when mdot set before first ratio step --- model/iharm/model.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/model/iharm/model.c b/model/iharm/model.c index f377f50..e8b8df0 100644 --- a/model/iharm/model.c +++ b/model/iharm/model.c @@ -154,6 +154,11 @@ void try_set_model_parameter(const char *word, const char *value) set_by_word_val(word, value, "dump_max", &dumpmax, TYPE_INT); set_by_word_val(word, value, "dump_skip", &dumpskip, TYPE_INT); dumpidx = dumpmin; + + // override parameters based on input + if (target_mdot > 0.) { + M_unit = 1.; + } } // Advance through dumps until we are closer to the next set @@ -1525,10 +1530,10 @@ void load_hamr_data(int n, char *fnam, int dumpidx, int verbose) Ladv_dump = Ladv; if (target_mdot > 0) { - fprintf(stderr, "resetting M_unit to match target_mdot = %g\n", target_mdot); + fprintf(stderr, "Resetting M_unit to match target_mdot = %g ", target_mdot); double current_mdot = Mdot_dump/MdotEdd_dump; - fprintf(stderr, "was %g is now %g\n", M_unit, M_unit * fabs(target_mdot / current_mdot)); + fprintf(stderr, "... is now %g\n", M_unit * fabs(target_mdot / current_mdot)); M_unit *= fabs(target_mdot / current_mdot); set_units(); @@ -1723,10 +1728,10 @@ void load_koral_data(int n, char *fnam, int dumpidx, int verbose) Ladv_dump = Ladv; if (target_mdot > 0) { - fprintf(stderr, "resetting M_unit to match target_mdot = %g\n", target_mdot); + fprintf(stderr, "Resetting M_unit to match target_mdot = %g ", target_mdot); double current_mdot = Mdot_dump/MdotEdd_dump; - fprintf(stderr, "was %g is now %g\n", M_unit, M_unit * fabs(target_mdot / current_mdot)); + fprintf(stderr, "... is now %g\n", M_unit * fabs(target_mdot / current_mdot)); M_unit *= fabs(target_mdot / current_mdot); set_units(); @@ -1932,7 +1937,8 @@ void load_iharm_data(int n, char *fnam, int dumpidx, int verbose) for (int l=0; lb[i][j][k] = sqrt(bsq) * B_unit; - if(i <= 21) { dMact += g * data[n]->p[KRHO][i][j][k] * ucon[1];; } + + if(i <= 21) { dMact += g * data[n]->p[KRHO][i][j][k] * ucon[1]; } if(i >= 21 && i < 41 && 0) Ladv += g * data[n]->p[UU][i][j][k] * ucon[1] * ucov[0]; if(i <= 21) Ladv += g * data[n]->p[UU][i][j][k] * ucon[1] * ucov[0]; @@ -1978,10 +1984,10 @@ void load_iharm_data(int n, char *fnam, int dumpidx, int verbose) Ladv_dump = Ladv; if (target_mdot > 0) { - fprintf(stderr, "resetting M_unit to match target_mdot = %g\n", target_mdot); + fprintf(stderr, "Resetting M_unit to match target_mdot = %g ", target_mdot); double current_mdot = Mdot_dump/MdotEdd_dump; - fprintf(stderr, "was %g is now %g\n", M_unit, M_unit * fabs(target_mdot / current_mdot)); + fprintf(stderr, "... is now %g\n",M_unit * fabs(target_mdot / current_mdot)); M_unit *= fabs(target_mdot / current_mdot); set_units(); From c43fa2c5a1f048b16d2a23b2d6139cc48b98d5e7 Mon Sep 17 00:00:00 2001 From: George Wong Date: Wed, 20 Apr 2022 16:21:16 -0500 Subject: [PATCH 89/93] force rescale magnetic field after resetting M_unit --- model/iharm/model.c | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/model/iharm/model.c b/model/iharm/model.c index e8b8df0..d9b0296 100644 --- a/model/iharm/model.c +++ b/model/iharm/model.c @@ -119,7 +119,7 @@ void init_grid(char *fnam, int dumpidx); void init_iharm_grid(char *fnam, int dumpidx); void init_koral_grid(char *fnam, int dumpidx); void init_hamr_grid(char *fnam, int dumpidx); -void init_physical_quantities(int n); +void init_physical_quantities(int n, double rescale_factor); void init_storage(void); void try_set_model_parameter(const char *word, const char *value) @@ -549,12 +549,14 @@ void set_units() fprintf(stderr,"rho,u,B units: %g [g cm^-3] %g [g cm^-1 s^-2] %g [G] \n",RHO_unit,U_unit,B_unit) ; } -void init_physical_quantities(int n) +void init_physical_quantities(int n, double rescale_factor) { #if DEBUG int ceilings = 0; #endif + rescale_factor = sqrt(rescale_factor); + // cover everything, even ghost zones #pragma omp parallel for collapse(3) for (int i = 0; i < N1+2; i++) { @@ -562,6 +564,8 @@ void init_physical_quantities(int n) for (int k = 0; k < N3+2; k++) { data[n]->ne[i][j][k] = data[n]->p[KRHO][i][j][k] * RHO_unit/(MP+ME) * Ne_factor; + data[n]->b[i][j][k] *= rescale_factor; + double bsq = data[n]->b[i][j][k] / B_unit; bsq = bsq*bsq; @@ -649,6 +653,7 @@ void init_physical_quantities(int n) #if DEBUG fprintf(stderr, "TOTAL TEMPERATURE CEILING ZONES: %d of %d\n", ceilings, (N1+2)*(N2+2)*(N3+2)); #endif + } void init_storage(void) @@ -1529,12 +1534,15 @@ void load_hamr_data(int n, char *fnam, int dumpidx, int verbose) MdotEdd_dump = Mdotedd; Ladv_dump = Ladv; + double rescale_factor = 1.; + if (target_mdot > 0) { fprintf(stderr, "Resetting M_unit to match target_mdot = %g ", target_mdot); double current_mdot = Mdot_dump/MdotEdd_dump; fprintf(stderr, "... is now %g\n", M_unit * fabs(target_mdot / current_mdot)); - M_unit *= fabs(target_mdot / current_mdot); + rescale_factor = fabs(target_mdot / current_mdot); + M_unit *= rescale_factor; set_units(); } @@ -1557,7 +1565,7 @@ void load_hamr_data(int n, char *fnam, int dumpidx, int verbose) } // now construct useful scalar quantities (over full (+ghost) zones of data) - init_physical_quantities(n); + init_physical_quantities(n, rescale_factor); } void load_koral_data(int n, char *fnam, int dumpidx, int verbose) @@ -1727,12 +1735,15 @@ void load_koral_data(int n, char *fnam, int dumpidx, int verbose) MdotEdd_dump = Mdotedd; Ladv_dump = Ladv; + double rescale_factor = 1.; + if (target_mdot > 0) { fprintf(stderr, "Resetting M_unit to match target_mdot = %g ", target_mdot); double current_mdot = Mdot_dump/MdotEdd_dump; fprintf(stderr, "... is now %g\n", M_unit * fabs(target_mdot / current_mdot)); - M_unit *= fabs(target_mdot / current_mdot); + rescale_factor = fabs(target_mdot / current_mdot); + M_unit *= rescale_factor; set_units(); } @@ -1755,7 +1766,7 @@ void load_koral_data(int n, char *fnam, int dumpidx, int verbose) } // now construct useful scalar quantities (over full (+ghost) zones of data) - init_physical_quantities(n); + init_physical_quantities(n, rescale_factor); } // get dMact in the i'th radial zone (0 = 0 of the dump, so ignore ghost zones) @@ -1983,12 +1994,15 @@ void load_iharm_data(int n, char *fnam, int dumpidx, int verbose) MdotEdd_dump = Mdotedd; Ladv_dump = Ladv; + double rescale_factor = 1.; + if (target_mdot > 0) { fprintf(stderr, "Resetting M_unit to match target_mdot = %g ", target_mdot); double current_mdot = Mdot_dump/MdotEdd_dump; - fprintf(stderr, "... is now %g\n",M_unit * fabs(target_mdot / current_mdot)); - M_unit *= fabs(target_mdot / current_mdot); + fprintf(stderr, "... is now %g\n", M_unit * fabs(target_mdot / current_mdot)); + rescale_factor = fabs(target_mdot / current_mdot); + M_unit *= rescale_factor; set_units(); } @@ -2011,7 +2025,7 @@ void load_iharm_data(int n, char *fnam, int dumpidx, int verbose) } // now construct useful scalar quantities (over full (+ghost) zones of data) - init_physical_quantities(n); + init_physical_quantities(n, rescale_factor); } From 0c32905cc91a81e9045e4bc9d3f4e87b48be604b Mon Sep 17 00:00:00 2001 From: Abhishek Joshi Date: Mon, 25 Apr 2022 15:39:33 -0500 Subject: [PATCH 90/93] added target to statically compile ipole. will need tweaks to find the static version of gsl, glibc, hdf5 (and z lib) --- makefile | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/makefile b/makefile index 36729ae..d3ab3fc 100644 --- a/makefile +++ b/makefile @@ -2,12 +2,12 @@ MODEL = iharm # Top directory of HDF5, or blank if using h5pcc -#HDF5_DIR = +HDF5_DIR = # Top directory of GSL, or blank if installed to system -#GSL_DIR = +GSL_DIR = # System /lib equivalent (can be /usr/lib, /lib64, /usr/lib64) # Can leave this blank if it's included automatically by GCC -SYSTEM_LIBDIR = /lib64 +SYSTEM_LIBDIR = # Try pointing this to h5pcc or h5cc on your machine, before hunting down libraries CC=h5cc @@ -91,12 +91,12 @@ LIB = $(MATH_LIB) $(GSL_LIB) # Add HDF and MPI directories only if compiler doesn't ifneq ($(strip $(HDF5_DIR)),) INC += -I$(HDF5_DIR)/include/ - LIBDIR += -L$(HDF5_DIR)/lib/ + LIBDIR += -L$(HDF5_DIR)/lib64/ LIB += $(HDF5_LIB) endif ifneq ($(strip $(GSL_DIR)),) INC += -I$(GSL_DIR)/include/ - LIBDIR += -L$(GSL_DIR)/lib/ + LIBDIR += -L$(GSL_DIR)/lib64/ endif ifneq ($(strip $(SYSTEM_LIBDIR)),) # Prefer user libraries (above) to system @@ -113,6 +113,9 @@ build: $(EXE) @$(ECHO) "CFLAGS: $(CFLAGS)" @$(ECHO) "MD5: $(shell $(MD5) $(EXE))" +static: CFLAGS += -static +static: build + debug: CFLAGS += -g -Wall -Werror -Wno-unused-variable -Wno-unused-but-set-variable debug: CFLAGS += -DDEBUG=1 debug: build From bc87ce6e4aece1b7846c511a738ba787a31d24e0 Mon Sep 17 00:00:00 2001 From: Abhishek Joshi Date: Wed, 15 Jun 2022 09:24:48 -0500 Subject: [PATCH 91/93] fixed reverse field issue (thanks to Ben) where it wasn't iterating over the full domain :| --- model/iharm/model.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/model/iharm/model.c b/model/iharm/model.c index d9b0296..ac441fa 100644 --- a/model/iharm/model.c +++ b/model/iharm/model.c @@ -1868,9 +1868,9 @@ void load_iharm_data(int n, char *fnam, int dumpidx, int verbose) //Reversing B Field if(reverse_field) { double multiplier = -1.0; - for(int i=0;ip[B1][i][j][k] = multiplier*data[n]->p[B1][i][j][k]; data[n]->p[B2][i][j][k] = multiplier*data[n]->p[B2][i][j][k]; data[n]->p[B3][i][j][k] = multiplier*data[n]->p[B3][i][j][k]; From 78ae984e84ea90ee54ddd83bd319f2d000d02ea4 Mon Sep 17 00:00:00 2001 From: Abhishek Joshi Date: Wed, 20 Jul 2022 14:35:23 -0500 Subject: [PATCH 92/93] fixed reverse field issue --- model/iharm/model.c | 855 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 735 insertions(+), 120 deletions(-) diff --git a/model/iharm/model.c b/model/iharm/model.c index e8fce0c..ac441fa 100644 --- a/model/iharm/model.c +++ b/model/iharm/model.c @@ -2,14 +2,19 @@ #include "decs.h" #include "hdf5_utils.h" +#include "debug_tools.h" #include "coordinates.h" #include "geometry.h" #include "grid.h" -#include "model_radiation.h" // Only for outputting emissivities +#include "model_radiation.h" // Only for outputting emissivities +#include "simcoords.h" // For interpolating arbitrary grids #include "par.h" #include "utils.h" +#include "debug_tools.h" + +#include #include #define NVAR (10) @@ -21,6 +26,7 @@ #define FORMAT_HAMR_EKS (3) #define FORMAT_IHARM_v1 (1) +#define FORMAT_KORAL_v2 (4) int dumpfile_format = 0; // UNITS @@ -32,12 +38,17 @@ double U_unit; double B_unit; double Te_unit; +double target_mdot = 0.0; // if this value > 0, use to renormalize M_unit &c. + +// MOLECULAR WEIGHTS +static double Ne_factor = 1.; // e.g., used for He with 2 protons+neutrons per 2 electrons +static double mu_i, mu_e, mu_tot; + // MODEL PARAMETERS: PUBLIC double DTd; -double rmax_geo = 100.; -double rmin_geo = 1.; double sigma_cut = 1.0; double beta_crit = 1.0; +double sigma_cut_high = -1.0; // MODEL PARAMETERS: PRIVATE static char fnam[STRLEN] = "dump.h5"; @@ -45,12 +56,10 @@ static char fnam[STRLEN] = "dump.h5"; static double tp_over_te = 3.; static double trat_small = 1.; static double trat_large = 40.; - -static double powerlaw_gamma_min = 1e2; -static double powerlaw_gamma_max = 1e5; -static double powerlaw_gamma_cut = 1e10; -static double powerlaw_eta = 0.02; -static double powerlaw_p = 3.25; +// Minimum number of dynamical times the cooling time must +// undershoot to be considered "small" +// lower values -> higher max T_e, higher values are restrictive +static double cooling_dynamical_times = 1.e-20; static int dumpskip = 1; static int dumpmin, dumpmax, dumpidx; @@ -60,14 +69,21 @@ static double Mdot_dump; static double MdotEdd_dump; static double Ladv_dump; +static int reverse_field = 0; + +double tf; + // MAYBES //static double t0; -// ELECTRONS -> +// ELECTRONS // 0 : constant TP_OVER_TE // 1 : use dump file model (kawazura?) // 2 : use mixed TP_OVER_TE (beta model) +// 3 : use mixed TP_OVER_TE (beta model) with fluid temperature +// 9 : load Te (in Kelvin) from dump file (KORAL etc.) // TODO the way this is selected is horrid. Make it a parameter. +#define ELECTRONS_TFLUID (3) static int RADIATION, ELECTRONS; static double gam = 1.444444, game = 1.333333, gamp = 1.666667; static double Thetae_unit, Mdotedd; @@ -87,6 +103,7 @@ struct of_data { double ***thetae; double ***b; double ***sigma; + double ***beta; }; static struct of_data dataA, dataB, dataC; static struct of_data *data[NSUP]; @@ -95,12 +112,14 @@ static struct of_data *data[NSUP]; void set_units(); void load_data(int n, char *, int dumpidx, int verbose); void load_iharm_data(int n, char *, int dumpidx, int verbose); +void load_koral_data(int n, char *, int dumpidx, int verbose); void load_hamr_data(int n, char *, int dumpidx, int verbose); double get_dump_t(char *fnam, int dumpidx); void init_grid(char *fnam, int dumpidx); void init_iharm_grid(char *fnam, int dumpidx); +void init_koral_grid(char *fnam, int dumpidx); void init_hamr_grid(char *fnam, int dumpidx); -void init_physical_quantities(int n); +void init_physical_quantities(int n, double rescale_factor); void init_storage(void); void try_set_model_parameter(const char *word, const char *value) @@ -111,6 +130,7 @@ void try_set_model_parameter(const char *word, const char *value) // assume params is populated set_by_word_val(word, value, "MBH", &MBH_solar, TYPE_DBL); set_by_word_val(word, value, "M_unit", &M_unit, TYPE_DBL); + set_by_word_val(word, value, "mdot", &target_mdot, TYPE_DBL); set_by_word_val(word, value, "dump", (void *)fnam, TYPE_STR); @@ -118,17 +138,14 @@ void try_set_model_parameter(const char *word, const char *value) set_by_word_val(word, value, "trat_small", &trat_small, TYPE_DBL); set_by_word_val(word, value, "trat_large", &trat_large, TYPE_DBL); set_by_word_val(word, value, "sigma_cut", &sigma_cut, TYPE_DBL); + set_by_word_val(word, value, "sigma_cut_high", &sigma_cut_high, TYPE_DBL); set_by_word_val(word, value, "beta_crit", &beta_crit, TYPE_DBL); - - set_by_word_val(word, value, "powerlaw_gamma_min", &powerlaw_gamma_min, TYPE_DBL); - set_by_word_val(word, value, "powerlaw_gamma_max", &powerlaw_gamma_max, TYPE_DBL); - set_by_word_val(word, value, "powerlaw_gamma_cut", &powerlaw_gamma_cut, TYPE_DBL); - set_by_word_val(word, value, "powerlaw_eta", &trat_large, TYPE_DBL); - set_by_word_val(word, value, "powerlaw_p", &trat_large, TYPE_DBL); + set_by_word_val(word, value, "cooling_dynamical_times", &cooling_dynamical_times, TYPE_DBL); set_by_word_val(word, value, "rmax_geo", &rmax_geo, TYPE_DBL); set_by_word_val(word, value, "rmin_geo", &rmin_geo, TYPE_DBL); + set_by_word_val(word, value, "reverse_field", &reverse_field, TYPE_INT); // allow cutting out the spine set_by_word_val(word, value, "polar_cut_deg", &polar_cut, TYPE_DBL); @@ -137,6 +154,11 @@ void try_set_model_parameter(const char *word, const char *value) set_by_word_val(word, value, "dump_max", &dumpmax, TYPE_INT); set_by_word_val(word, value, "dump_skip", &dumpskip, TYPE_INT); dumpidx = dumpmin; + + // override parameters based on input + if (target_mdot > 0.) { + M_unit = 1.; + } } // Advance through dumps until we are closer to the next set @@ -235,15 +257,29 @@ void get_dumpfile_type(char *fnam, int dumpidx) char fname[256]; snprintf(fname, 255, fnam, dumpidx); - // TODO needs KORAL/MKS3 support - hdf5_open(fname); int hamr_attr_exists = hdf5_attr_exists("", "dscale"); + if (!hamr_attr_exists) { - dumpfile_format = FORMAT_IHARM_v1; - fprintf(stderr, "iharm!\n"); + if (!hdf5_exists("header/version")) { + // Converted BHAC dumps and very old iharm3d output do not include a version + // BHAC output requires nothing special from ipole so we mark it "iharm_v1" + dumpfile_format = FORMAT_IHARM_v1; + fprintf(stderr, "bhac! (or old iharm)\n"); + } else { + char harmversion[256]; + hdf5_read_single_val(harmversion, "header/version", hdf5_make_str_type(255)); + + if ( strcmp(harmversion, "KORALv2") == 0 ) { + dumpfile_format = FORMAT_KORAL_v2; + fprintf(stderr, "koral!\n"); + } else { + dumpfile_format = FORMAT_IHARM_v1; + fprintf(stderr, "iharm!\n"); + } + } } else { - // note this will return -1 if the "header" group does not exists + // note this will return -1 if the "header" group does not exist dumpfile_format = FORMAT_HAMR_EKS; fprintf(stderr, "hamr!\n"); } @@ -270,14 +306,15 @@ void init_model(double *tA, double *tB) // read fluid data fprintf(stderr, "Reading data...\n"); - load_data(0, fnam, dumpidx, dumpmin); + load_data(0, fnam, dumpidx, 2); + // replaced dumpmin -> 2 because apparently that argument was just .. removed dumpidx += dumpskip; #if SLOW_LIGHT update_data(tA, tB); update_data(tA, tB); tf = get_dump_t(fnam, dumpmax) - 1.e-5; #else // FAST LIGHT - data[2]->t =10000.; + data[2]->t = 10000.; #endif // SLOW_LIGHT // horizon radius @@ -360,19 +397,12 @@ void get_model_fourv(double X[NDIM], double Kcon[NDIM], // Set Ucon and get Ucov by lowering // interpolate primitive variables first - double U1A, U2A, U3A, U1B, U2B, U3B, tfac; double Vcon[NDIM]; int nA, nB; - tfac = set_tinterp_ns(X, &nA, &nB); - U1A = interp_scalar(X, data[nA]->p[U1]); - U2A = interp_scalar(X, data[nA]->p[U2]); - U3A = interp_scalar(X, data[nA]->p[U3]); - U1B = interp_scalar(X, data[nB]->p[U1]); - U2B = interp_scalar(X, data[nB]->p[U2]); - U3B = interp_scalar(X, data[nB]->p[U3]); - Vcon[1] = tfac*U1A + (1. - tfac)*U1B; - Vcon[2] = tfac*U2A + (1. - tfac)*U2B; - Vcon[3] = tfac*U3A + (1. - tfac)*U3B; + double tfac = set_tinterp_ns(X, &nA, &nB); + Vcon[1] = interp_scalar_time(X, data[nA]->p[U1], data[nB]->p[U1], tfac); + Vcon[2] = interp_scalar_time(X, data[nA]->p[U2], data[nB]->p[U2], tfac); + Vcon[3] = interp_scalar_time(X, data[nA]->p[U3], data[nB]->p[U3], tfac); // translate to four velocity double VdotV = 0.; @@ -390,17 +420,9 @@ void get_model_fourv(double X[NDIM], double Kcon[NDIM], // Now set Bcon and get Bcov by lowering // interpolate primitive variables first - double B1A, B2A, B3A, B1B, B2B, B3B, Bcon1, Bcon2, Bcon3; - tfac = set_tinterp_ns(X, &nA, &nB); - B1A = interp_scalar(X, data[nA]->p[B1]); - B2A = interp_scalar(X, data[nA]->p[B2]); - B3A = interp_scalar(X, data[nA]->p[B3]); - B1B = interp_scalar(X, data[nB]->p[B1]); - B2B = interp_scalar(X, data[nB]->p[B2]); - B3B = interp_scalar(X, data[nB]->p[B3]); - Bcon1 = tfac*B1A + (1. - tfac)*B1B; - Bcon2 = tfac*B2A + (1. - tfac)*B2B; - Bcon3 = tfac*B3A + (1. - tfac)*B3B; + double Bcon1 = interp_scalar_time(X, data[nA]->p[B1], data[nB]->p[B1], tfac); + double Bcon2 = interp_scalar_time(X, data[nA]->p[B2], data[nB]->p[B2], tfac); + double Bcon3 = interp_scalar_time(X, data[nA]->p[B3], data[nB]->p[B3], tfac); // get Bcon Bcon[0] = Bcon1*Ucov[1] + Bcon2*Ucov[2] + Bcon3*Ucov[3]; @@ -419,14 +441,11 @@ void get_model_primitives(double X[NDIM], double *p) { if ( X_in_domain(X) == 0 ) return; - double bA, bB, tfac; int nA, nB; - tfac = set_tinterp_ns(X, &nA, &nB); + double tfac = set_tinterp_ns(X, &nA, &nB); for (int np=0; np<8; np++) { - bA = interp_scalar(X, data[nA]->p[np]); - bB = interp_scalar(X, data[nB]->p[np]); - p[np] = tfac*bA + (1. - tfac)*bB; + p[np] = interp_scalar_time(X, data[nA]->p[np], data[nA]->p[np], tfac); } } @@ -434,24 +453,23 @@ double get_model_thetae(double X[NDIM]) { if ( X_in_domain(X) == 0 ) return 0.; - double thetaeA, thetaeB, tfac; int nA, nB; - tfac = set_tinterp_ns(X, &nA, &nB); - thetaeA = interp_scalar(X, data[nA]->thetae); - thetaeB = interp_scalar(X, data[nB]->thetae); + double tfac = set_tinterp_ns(X, &nA, &nB); + double thetae = interp_scalar_time(X, data[nA]->thetae, data[nB]->thetae, tfac); - double thetae = tfac*thetaeA + (1. - tfac)*thetaeB; - if (thetae < 0.) { - printf("thetae negative!\n"); +#if DEBUG + if (thetae < 0. || isnan(thetae)) { + printf("thetae negative or NaN!\n"); printf("X[] = %g %g %g %g\n", X[0], X[1], X[2], X[3]); printf("t = %e %e %e\n", data[0]->t, data[1]->t, data[2]->t); - printf("thetae = %e tfac = %e thetaeA = %e thetaeB = %e nA = %i nB = %i\n", - thetae, tfac, thetaeA, thetaeB, nA, nB); + double thetaeA = interp_scalar(X, data[nA]->thetae); + double thetaeB = interp_scalar(X, data[nB]->thetae); + printf("thetaeA, thetaeB = %e %e", thetaeA, thetaeB); + printf("thetae, tfac = %e %e\n", thetae, tfac); } +#endif - if (thetaeA < 0 || thetaeB < 0) fprintf(stderr, "TETE %g %g\n", thetaeA, thetaeB); - - return tfac*thetaeA + (1. - tfac)*thetaeB; + return thetae; } //b field strength in Gauss @@ -460,42 +478,60 @@ double get_model_b(double X[NDIM]) // TODO how *should* we handle exiting the domain consistently? if ( X_in_domain(X) == 0 ) return 0.; - double bA, bB, tfac; int nA, nB; - tfac = set_tinterp_ns(X, &nA, &nB); - bA = interp_scalar(X, data[nA]->b); - bB = interp_scalar(X, data[nB]->b); + double tfac = set_tinterp_ns(X, &nA, &nB); - return tfac*bA + (1. - tfac)*bB; + return interp_scalar_time(X, data[nA]->b, data[nB]->b, tfac); } double get_model_sigma(double X[NDIM]) { if ( X_in_domain(X) == 0 ) return 0.; - double sigmaA, sigmaB, tfac; + int nA, nB; + double tfac = set_tinterp_ns(X, &nA, &nB); + + return interp_scalar_time(X, data[nA]->sigma, data[nB]->sigma, tfac); +} + +double get_model_beta(double X[NDIM]) +{ + if ( X_in_domain(X) == 0 ) return 0.; // TODO inf? + + double betaA, betaB, tfac; int nA, nB; tfac = set_tinterp_ns(X, &nA, &nB); - sigmaA = interp_scalar(X, data[nA]->sigma); - sigmaB = interp_scalar(X, data[nB]->sigma); - return tfac*sigmaA + (1. - tfac)*sigmaB; + betaA = interp_scalar(X, data[nA]->beta); + betaB = interp_scalar(X, data[nB]->beta); + return tfac*betaA + (1. - tfac)*betaB; +} + +double get_sigma_smoothfac(double sigma) +{ + double sigma_above = sigma_cut; + if (sigma_cut_high > 0) sigma_above = sigma_cut_high; + if (sigma < sigma_cut) return 1; + if (sigma >= sigma_above) return 0; + double dsig = sigma_above - sigma_cut; + return cos(M_PI / 2. / dsig * (sigma - sigma_cut)); } double get_model_ne(double X[NDIM]) { if ( X_in_domain(X) == 0 ) return 0.; + double sigma_smoothfac = 1; + #if USE_GEODESIC_SIGMACUT double sigma = get_model_sigma(X); if (sigma > sigma_cut) return 0.; + sigma_smoothfac = get_sigma_smoothfac(sigma); #endif - double neA, neB, tfac; int nA, nB; - tfac = set_tinterp_ns(X, &nA, &nB); - neA = interp_scalar(X, data[nA]->ne); - neB = interp_scalar(X, data[nB]->ne); - return tfac*neA + (1. - tfac)*neB; + double tfac = set_tinterp_ns(X, &nA, &nB); + + return interp_scalar_time(X, data[nA]->ne, data[nB]->ne, tfac) * sigma_smoothfac; } void set_units() @@ -513,37 +549,99 @@ void set_units() fprintf(stderr,"rho,u,B units: %g [g cm^-3] %g [g cm^-1 s^-2] %g [G] \n",RHO_unit,U_unit,B_unit) ; } -void init_physical_quantities(int n) +void init_physical_quantities(int n, double rescale_factor) { +#if DEBUG + int ceilings = 0; +#endif + + rescale_factor = sqrt(rescale_factor); + // cover everything, even ghost zones #pragma omp parallel for collapse(3) for (int i = 0; i < N1+2; i++) { for (int j = 0; j < N2+2; j++) { for (int k = 0; k < N3+2; k++) { - data[n]->ne[i][j][k] = data[n]->p[KRHO][i][j][k] * RHO_unit/(MP+ME) ; + data[n]->ne[i][j][k] = data[n]->p[KRHO][i][j][k] * RHO_unit/(MP+ME) * Ne_factor; + + data[n]->b[i][j][k] *= rescale_factor; double bsq = data[n]->b[i][j][k] / B_unit; bsq = bsq*bsq; double sigma_m = bsq/data[n]->p[KRHO][i][j][k]; - + double beta_m = data[n]->p[UU][i][j][k]*(gam-1.)/0.5/bsq; +#if DEBUG + if(isnan(sigma_m)) { + sigma_m = 0; + fprintf(stderr, "Setting zero sigma!\n"); + } + if(isnan(beta_m)) { + beta_m = INFINITY; + fprintf(stderr, "Setting INF beta!\n"); + } +#endif if (ELECTRONS == 1) { - data[n]->thetae[i][j][k] = data[n]->p[KEL][i][j][k]*pow(data[n]->p[KRHO][i][j][k],game-1.)*Thetae_unit; + data[n]->thetae[i][j][k] = data[n]->p[KEL][i][j][k] * + pow(data[n]->p[KRHO][i][j][k],game-1.)*Thetae_unit; } else if (ELECTRONS == 2) { - double beta = data[n]->p[UU][i][j][k]*(gam-1.)/0.5/bsq; - double betasq = beta*beta / beta_crit/beta_crit; + double betasq = beta_m*beta_m / beta_crit/beta_crit; double trat = trat_large * betasq/(1. + betasq) + trat_small /(1. + betasq); //Thetae_unit = (gam - 1.) * (MP / ME) / trat; // see, e.g., Eq. 8 of the EHT GRRT formula list - Thetae_unit = (MP/ME) * (game-1.) * (gamp-1.) / ( (gamp-1.) + (game-1.)*trat ); - data[n]->thetae[i][j][k] = Thetae_unit*data[n]->p[UU][i][j][k]/data[n]->p[KRHO][i][j][k]; + double lcl_Thetae_u = (MP/ME) * (game-1.) * (gamp-1.) / ( (gamp-1.) + (game-1.)*trat ); + Thetae_unit = lcl_Thetae_u; + data[n]->thetae[i][j][k] = lcl_Thetae_u*data[n]->p[UU][i][j][k]/data[n]->p[KRHO][i][j][k]; + } else if (ELECTRONS == 9) { + // convert Kelvin -> Thetae + data[n]->thetae[i][j][k] = data[n]->p[TFLK][i][j][k] * KBOL / ME / CL / CL; + } else if (ELECTRONS == ELECTRONS_TFLUID) { + double beta = data[n]->p[UU][i][j][k]*(gam-1.)/0.5/bsq; + double betasq = beta*beta / beta_crit/beta_crit; + double trat = trat_large * betasq/(1. + betasq) + trat_small /(1. + betasq); + double dfactor = mu_tot / mu_e + mu_tot / mu_i * trat; + data[n]->thetae[i][j][k] = data[n]->p[THF][i][j][k] / dfactor; } else { data[n]->thetae[i][j][k] = Thetae_unit*data[n]->p[UU][i][j][k]/data[n]->p[KRHO][i][j][k]; } +#if DEBUG + if(isnan(data[n]->thetae[i][j][k])) { + data[n]->thetae[i][j][k] = 0.0; + fprintf(stderr, "\nNaN Thetae! Prims %g %g %g %g %g %g %g %g\n", data[n]->p[KRHO][i][j][k], data[n]->p[UU][i][j][k], + data[n]->p[U1][i][j][k], data[n]->p[U2][i][j][k], data[n]->p[U3][i][j][k], data[n]->p[B1][i][j][k], + data[n]->p[B2][i][j][k], data[n]->p[B3][i][j][k]); + fprintf(stderr, "Setting floor temp!\n"); + } +#endif + + // Enforce a max on Thetae based on cooling time == dynamical time + if (cooling_dynamical_times > 1e-20) { + double X[NDIM]; + ijktoX(i, j, k, X); + double r, th; + bl_coord(X, &r, &th); + // Calculate thetae_max based on matching the cooling time w/dynamical time + // Makes sure to use b w/units, but r has already been rescaled + double Thetae_max_dynamical = 1 / cooling_dynamical_times * 7.71232e46 / 2 / MBH * pow(data[n]->b[i][j][k], -2) * pow(r * sin(th), -1.5); +#if DEBUG + if (Thetae_max_dynamical < data[n]->thetae[i][j][k]) { + if (r > 2) fprintf(stderr, "Ceiling on temp! %g < %g, r, th %g %g\n", Thetae_max_dynamical, data[n]->thetae[i][j][k], r, th); + ceilings++; + } +#endif + data[n]->thetae[i][j][k] = fmin(data[n]->thetae[i][j][k], Thetae_max_dynamical); + } + + // Apply floor last in case the above is a very restrictive ceiling data[n]->thetae[i][j][k] = fmax(data[n]->thetae[i][j][k], 1.e-3); - //strongly magnetized = empty, no shiny spine - data[n]->sigma[i][j][k] = sigma_m; // allow sigma cut per geodesic step + // Preserve sigma for cutting along geodesics, and for variable-kappa model + data[n]->sigma[i][j][k] = fmax(sigma_m, SMALL); + // Also record beta, for variable-kappa model + data[n]->beta[i][j][k] = fmax(beta_m, SMALL); + + // Cut Ne (i.e. emission) based on sigma, if we're not doing so along each geodesic + // Strongly magnetized = empty, no shiny spine if (sigma_m > sigma_cut && !USE_GEODESIC_SIGMACUT) { data[n]->b[i][j][k]=0.0; data[n]->ne[i][j][k]=0.0; @@ -552,6 +650,9 @@ void init_physical_quantities(int n) } } } +#if DEBUG + fprintf(stderr, "TOTAL TEMPERATURE CEILING ZONES: %d of %d\n", ceilings, (N1+2)*(N2+2)*(N3+2)); +#endif } @@ -564,6 +665,7 @@ void init_storage(void) data[n]->thetae = malloc_rank3(N1+2,N2+2,N3+2); data[n]->b = malloc_rank3(N1+2,N2+2,N3+2); data[n]->sigma = malloc_rank3(N1+2,N2+2,N3+2); + data[n]->beta = malloc_rank3(N1+2,N2+2,N3+2); } } @@ -571,6 +673,8 @@ void init_grid(char *fnam, int dumpidx) { if (dumpfile_format == FORMAT_IHARM_v1) { init_iharm_grid(fnam, dumpidx); + } else if (dumpfile_format == FORMAT_KORAL_v2) { + init_koral_grid(fnam, dumpidx); } else if (dumpfile_format == FORMAT_HAMR_EKS) { init_hamr_grid(fnam, dumpidx); } @@ -656,13 +760,6 @@ void init_hamr_grid(char *fnam, int dumpidx) stopx[2] = startx[2]+N2*dx[2]; stopx[3] = startx[3]+N3*dx[3]; - // set boundary of coordinate system - MULOOP cstartx[mu] = 0.; - cstopx[0] = 0; - cstopx[1] = log(Rout); - cstopx[2] = 1; - cstopx[3] = 2*M_PI; - // now translate from hamr x2 \in (-1, 1) -> mks x2 \in (0, 1) startx[2] = (startx[2] + 1)/2.; stopx[2] = (stopx[2] + 1)/2.; @@ -672,10 +769,8 @@ void init_hamr_grid(char *fnam, int dumpidx) rmax_geo = fmin(rmax_geo, Rout); rmin_geo = fmax(rmin_geo, Rin); - cstartx[0] = 0; - cstartx[1] = 0; - cstartx[2] = 0; - cstartx[3] = 0; + // set boundary of coordinate system + MULOOP cstartx[mu] = 0.; cstopx[0] = 0; cstopx[1] = log(Rout); cstopx[2] = 1.0; @@ -721,6 +816,17 @@ void init_iharm_grid(char *fnam, int dumpidx) exit(-3); } + if ( hdf5_exists("weights") ) { + hdf5_set_directory("/header/weights/"); + hdf5_read_single_val(&mu_i, "mu_i", H5T_IEEE_F64LE); + hdf5_read_single_val(&mu_e, "mu_e", H5T_IEEE_F64LE); + hdf5_read_single_val(&mu_tot, "mu_tot", H5T_IEEE_F64LE); + fprintf(stderr, "Loaded molecular weights (mu_i, mu_e, mu_tot): %g %g %g\n", mu_i, mu_e, mu_tot); + Ne_factor = 1. / mu_e; + hdf5_set_directory("/header/"); + ELECTRONS = ELECTRONS_TFLUID; + } + char metric_name[20]; hid_t HDF5_STR_TYPE = hdf5_make_str_type(20); hdf5_read_single_val(&metric_name, "metric", HDF5_STR_TYPE); @@ -743,6 +849,9 @@ void init_iharm_grid(char *fnam, int dumpidx) use_eKS_internal = 1; metric = METRIC_MKS3; cstopx[2] = 1.0; + } else if ( strncmp(metric_name, "EKS", 19) == 0 ) { + metric = METRIC_EKS; + cstopx[2] = M_PI; } else { fprintf(stderr, "File is in unknown metric %s. Cannot continue.\n", metric_name); exit(-1); @@ -770,6 +879,9 @@ void init_iharm_grid(char *fnam, int dumpidx) } ELECTRONS = 1; Thetae_unit = MP/ME; + } else if (ELECTRONS == ELECTRONS_TFLUID) { + fprintf(stderr, "Using Ressler/Athena electrons with mixed tp_over_te and\n"); + fprintf(stderr, "trat_small = %g, trat_large = %g, and beta_crit = %g\n", trat_small, trat_large, beta_crit); } else if (USE_FIXED_TPTE && !USE_MIXED_TPTE) { ELECTRONS = 0; // force TP_OVER_TE to overwrite bad electrons fprintf(stderr, "Using fixed tp_over_te ratio = %g\n", tp_over_te); @@ -830,6 +942,10 @@ void init_iharm_grid(char *fnam, int dumpidx) fprintf(stderr, "Using logarithmic KS coordinates internally\n"); fprintf(stderr, "Converting from KORAL-style Modified Kerr-Schild coordinates MKS3\n"); break; + case METRIC_EKS: + hdf5_set_directory("/header/geom/eks/"); + fprintf(stderr, "Using Kerr-Schild coordinates with exponential radial coordiante\n"); + break; } if ( metric == METRIC_MKS3 ) { @@ -840,6 +956,12 @@ void init_iharm_grid(char *fnam, int dumpidx) hdf5_read_single_val(&mks3MY2, "MY2", H5T_IEEE_F64LE); hdf5_read_single_val(&mks3MP0, "MP0", H5T_IEEE_F64LE); Rout = 100.; + } else if ( metric == METRIC_EKS ) { + hdf5_read_single_val(&a, "a", H5T_IEEE_F64LE); + hdf5_read_single_val(&Rin, "r_in", H5T_IEEE_F64LE); + hdf5_read_single_val(&Rout, "r_out", H5T_IEEE_F64LE); + fprintf(stderr, "eKS parameters a: %f Rin: %f Rout: %f\n", a, Rin, Rout); + } else { // Some brand of MKS. All have the same parameters hdf5_read_single_val(&a, "a", H5T_IEEE_F64LE); hdf5_read_single_val(&hslope, "hslope", H5T_IEEE_F64LE); @@ -891,6 +1013,184 @@ void init_iharm_grid(char *fnam, int dumpidx) hdf5_close(); } +void init_koral_grid(char *fnam, int dumpidx) +{ + // called at the beginning of the run and sets the static parameters + // along with setting up the grid + + // assert(42==0); + // this version of the code has not been validated to have the + // right units and four-vector recovery. use at your own peril + + char fname[256]; + snprintf(fname, 255, fnam, dumpidx); + fprintf(stderr, "filename: %s\n", fname); + + // always load ks rh from the file to get proper extents + // and potentially as a fallback for when we cannnot get + // the inverse/reverse transformation eKS -> simcoords. + load_simcoord_info_from_file(fname); + + // beecause of legacy global state choices, we can't open two + // files at once, so we have to call this after the above. + hdf5_open(fname); + + // get dump info to copy to ipole output + fluid_header = hdf5_get_blob("/header"); + + // get time information for slow light + // currently unsupported + //hdf5_read_single_val(&DTd, "dump_cadence", H5T_IEEE_F64LE); + + // in general, we assume KORAL should use the simcoords feature. first + // ensure that metric_out is KS and then set simcoords + hdf5_set_directory("/header/"); + char metric_out[20], metric_run[20]; + hid_t HDF5_STR_TYPE = hdf5_make_str_type(20); + hdf5_read_single_val(&metric_out, "metric_out", HDF5_STR_TYPE); + hdf5_read_single_val(&metric_run, "metric_run", HDF5_STR_TYPE); + + if (strcmp(metric_out, "KS") != 0) { + fprintf(stderr, "! expected koral metric_out==KS but got %s instead. quitting.\n", metric_out); + exit(5); + } + + // at this point, assume we will load data as eKS and trace as eKS. + use_simcoords = 1; + metric = METRIC_MKS; + hslope = 1.; + + // get simulation grid coordinates + if (strcmp(metric_run, "MKS3") == 0) { + simcoords = SIMCOORDS_KORAL_MKS3; + hdf5_read_single_val(&a, "bhspin", H5T_IEEE_F64LE); + hdf5_set_directory("/header/geom/mks3/"); + hdf5_read_single_val(&(mp_koral_mks3.r0), "mksr0", H5T_IEEE_F64LE); + hdf5_read_single_val(&(mp_koral_mks3.h0), "mksh0", H5T_IEEE_F64LE); + hdf5_read_single_val(&(mp_koral_mks3.my1), "mksmy1", H5T_IEEE_F64LE); + hdf5_read_single_val(&(mp_koral_mks3.my2), "mksmy2", H5T_IEEE_F64LE); + hdf5_read_single_val(&(mp_koral_mks3.mp0), "mksmp0", H5T_IEEE_F64LE); + fprintf(stderr, "KORAL simulation was run with MKS3 coordinates.\n"); + } else if (strcmp(metric_run, "MKS2") == 0) { + simcoords = SIMCOORDS_KORAL_MKS3; + hdf5_read_single_val(&a, "bhspin", H5T_IEEE_F64LE); + hdf5_set_directory("/header/geom/mks2/"); + hdf5_read_single_val(&(mp_koral_mks3.r0), "mksr0", H5T_IEEE_F64LE); + hdf5_read_single_val(&(mp_koral_mks3.h0), "mksh0", H5T_IEEE_F64LE); + mp_koral_mks3.my1 = 0; + mp_koral_mks3.my2 = 0; + mp_koral_mks3.mp0 = 0; + fprintf(stderr, "KORAL simulation was run with MKS2 coordinates.\n"); + } else if (strcmp(metric_run, "JETCOORDS") == 0) { + simcoords = SIMCOORDS_KORAL_JETCOORDS; + hdf5_read_single_val(&a, "bhspin", H5T_IEEE_F64LE); + hdf5_set_directory("/header/geom/jetcoords/"); + hdf5_read_single_val(&(mp_koral_jetcoords.alpha_1), "alpha1", H5T_IEEE_F64LE); + hdf5_read_single_val(&(mp_koral_jetcoords.alpha_2), "alpha2", H5T_IEEE_F64LE); + hdf5_read_single_val(&(mp_koral_jetcoords.cylindrify), "cylindrify", H5T_IEEE_F64LE); + hdf5_set_directory("/header/geom/"); + fprintf(stderr, "KORAL simulation was run with JETCOORDS coordinates.\n"); + } else { + fprintf(stderr, "! unknown koral metric_run (%s). quitting.\n", metric_run); + exit(5); + } + + // get grid information + hdf5_set_directory("/header/"); + hdf5_read_single_val(&N1, "n1", H5T_STD_I32LE); + hdf5_read_single_val(&N2, "n2", H5T_STD_I32LE); + hdf5_read_single_val(&N3, "n3", H5T_STD_I32LE); + hdf5_read_single_val(&gam, "gam", H5T_IEEE_F64LE); + + hdf5_set_directory("/header/geom/"); + hdf5_read_single_val(&startx[1], "startx1", H5T_IEEE_F64LE); + hdf5_read_single_val(&startx[2], "startx2", H5T_IEEE_F64LE); + hdf5_read_single_val(&startx[3], "startx3", H5T_IEEE_F64LE); + hdf5_read_single_val(&dx[1], "dx1", H5T_IEEE_F64LE); + hdf5_read_single_val(&dx[2], "dx2", H5T_IEEE_F64LE); + hdf5_read_single_val(&dx[3], "dx3", H5T_IEEE_F64LE); + hdf5_set_directory("/header/"); + + // reset x3 grid. this makes it easier for simcoords + Xtoijk to handle the + // translation, but it also means ipole and KORAL disagree about where x3=0 + if ( fabs(2.*M_PI - dx[3]*N3) >= dx[3] ) { + fprintf(stderr, "! base koral domain extent in x3 is not 2 PI. quitting.\n"); + exit(5); + } else { + startx[3] = 0.; + } + + stopx[0] = 1.; + stopx[1] = startx[1]+N1*dx[1]; + stopx[2] = startx[2]+N2*dx[2]; + stopx[3] = startx[3]+N3*dx[3]; + MULOOP cstartx[mu] = startx[mu]; + MULOOP cstopx[mu] = stopx[mu]; + + fprintf(stderr, "KORAL coordinates dx: %g %g %g\n", dx[1], dx[2], dx[3]); + fprintf(stderr, "Native coordinate start: %g %g %g stop: %g %g %g\n", + cstartx[1], cstartx[2], cstartx[3], cstopx[1], cstopx[2], cstopx[3]); + fprintf(stderr, "Grid start: %g %g %g stop: %g %g %g\n", + startx[1], startx[2], startx[3], stopx[1], stopx[2], stopx[3]); + + // unsupported features / undocumented elements + hdf5_set_directory("/header/geom/mks3/"); + if (hdf5_exists("gam_e")) { + fprintf(stderr, "! found gam_e in KORAL simulation. undocumented. quitting.\n"); + exit(5); + } + + // maybe load radiation units from dump file + RADIATION = 0; + hdf5_set_directory("/header/"); + if ( hdf5_exists("has_radiation") ) { + hdf5_read_single_val(&RADIATION, "has_radiation", H5T_STD_I32LE); + if (RADIATION) { + // Note set_units(...) get called AFTER this function returns + fprintf(stderr, "koral dump file was from radiation run. loading units...\n"); + hdf5_set_directory("/header/units/"); + hdf5_read_single_val(&MBH_solar, "M_bh", H5T_IEEE_F64LE); + hdf5_read_single_val(&RHO_unit, "M_unit", H5T_IEEE_F64LE); + hdf5_read_single_val(&T_unit, "T_unit", H5T_IEEE_F64LE); + hdf5_read_single_val(&L_unit, "L_unit", H5T_IEEE_F64LE); + hdf5_read_single_val(&U_unit, "U_unit", H5T_IEEE_F64LE); + hdf5_read_single_val(&B_unit, "B_unit", H5T_IEEE_F64LE); + M_unit = RHO_unit * L_unit*L_unit*L_unit; + } + } + + ELECTRONS = 0; + hdf5_set_directory("/header/"); + if ( hdf5_exists("has_electrons") ) { + hdf5_read_single_val(&ELECTRONS, "has_electrons", H5T_STD_I32LE); + if (ELECTRONS) { + fprintf(stderr, "koral dump has native electron temperature. forcing Thetae...\n"); + ELECTRONS = 9; + } else { + if (USE_MIXED_TPTE && !USE_FIXED_TPTE) { + fprintf(stderr, "Using mixed tp_over_te with trat_small = %g, trat_large = %g, and beta_crit = %g\n", trat_small, trat_large, beta_crit); + ELECTRONS = 2; + } else { + fprintf(stderr, "! koral unsupported without native electrons or mixed tp_over_te.\n"); + exit(6); + } + } + } + + fprintf(stderr, "sigma_cut = %g\n", sigma_cut); + + fprintf(stderr, "generating simcoords grid... "); + int interp_n1 = 1024; + int interp_n2 = 1024; + initialize_simgrid(interp_n1, interp_n2, + startx[1], startx[1]+N1*dx[1], + startx[2], startx[2]+N2*dx[2]); + fprintf(stderr, "done!\n"); + + init_storage(); + hdf5_close(); +} + void output_hdf5() { hdf5_set_directory("/"); @@ -919,6 +1219,10 @@ void output_hdf5() hdf5_write_single_val(&trat_small, "rlow", H5T_IEEE_F64LE); hdf5_write_single_val(&trat_large, "rhigh", H5T_IEEE_F64LE); hdf5_write_single_val(&beta_crit, "beta_crit", H5T_IEEE_F64LE); + } else if (ELECTRONS == ELECTRONS_TFLUID) { + hdf5_write_single_val(&mu_i, "mu_i", H5T_IEEE_F64LE); + hdf5_write_single_val(&mu_e, "mu_e", H5T_IEEE_F64LE); + hdf5_write_single_val(&mu_tot, "mu_tot", H5T_IEEE_F64LE); } hdf5_write_single_val(&ELECTRONS, "type", H5T_STD_I32LE); @@ -939,6 +1243,8 @@ void load_data(int n, char *fnam, int dumpidx, int verbose) { if (dumpfile_format == FORMAT_IHARM_v1) { load_iharm_data(n, fnam, dumpidx, verbose); + } else if (dumpfile_format == FORMAT_KORAL_v2) { + load_koral_data(n, fnam, dumpidx, verbose); } else if (dumpfile_format == FORMAT_HAMR_EKS) { load_hamr_data(n, fnam, dumpidx, verbose); } @@ -964,7 +1270,7 @@ void populate_boundary_conditions(int n) for (int i=0; ip[l][i][0][k] = data[n]->p[l][i][1][kflip]; data[n]->p[l][i][N2+1][k] = data[n]->p[l][i][N2][kflip]; @@ -1033,7 +1339,7 @@ void load_hamr_data(int n, char *fnam, int dumpidx, int verbose) hsize_t fstart[] = { 0 }; hsize_t fcount[] = { N1 * N2 * N3 }; - double *buffer = malloc(N1*N2*N3 * sizeof(*buffer)); + double *buffer = calloc(N1*N2*N3, sizeof(*buffer)); hdf5_read_array(buffer, "RHO", 1, fdims, fstart, fcount, fdims, fstart, H5T_IEEE_F64LE); remap_hamr(buffer, data[n]->p[KRHO], N1, N2, N3, 1); @@ -1228,6 +1534,23 @@ void load_hamr_data(int n, char *fnam, int dumpidx, int verbose) MdotEdd_dump = Mdotedd; Ladv_dump = Ladv; + double rescale_factor = 1.; + + if (target_mdot > 0) { + fprintf(stderr, "Resetting M_unit to match target_mdot = %g ", target_mdot); + + double current_mdot = Mdot_dump/MdotEdd_dump; + fprintf(stderr, "... is now %g\n", M_unit * fabs(target_mdot / current_mdot)); + rescale_factor = fabs(target_mdot / current_mdot); + M_unit *= rescale_factor; + + set_units(); + } + + Mdot_dump = -dMact*M_unit/T_unit; + MdotEdd_dump = Mdotedd; + Ladv_dump = Ladv; + if (verbose == 2) { fprintf(stderr,"dMact: %g [code]\n",dMact); fprintf(stderr,"Ladv: %g [code]\n",Ladv_dump); @@ -1242,7 +1565,251 @@ void load_hamr_data(int n, char *fnam, int dumpidx, int verbose) } // now construct useful scalar quantities (over full (+ghost) zones of data) - init_physical_quantities(n); + init_physical_quantities(n, rescale_factor); +} + +void load_koral_data(int n, char *fnam, int dumpidx, int verbose) +{ + // loads relevant information from fluid dump file stored at fname + // to the n'th copy of data (e.g., for slow light) + + double dMact, Ladv; + + char fname[256]; + snprintf(fname, 255, fnam, dumpidx); + + nloaded++; + + if ( hdf5_open(fname) < 0 ) { + fprintf(stderr, "! unable to open file %s. Exiting!\n", fname); + exit(-1); + } + + hdf5_set_directory("/"); + hdf5_read_single_val(&(data[n]->t), "t", H5T_IEEE_F64LE); + + hdf5_set_directory("/quants/"); + + // load into "center" of data + hsize_t fdims[] = { N1, N2, N3 }; + hsize_t fstart[] = { 0, 0, 0 }; + hsize_t fcount[] = { N1, N2, N3, 1 }; + hsize_t mdims[] = { N1+2, N2+2, N3+2 }; + hsize_t mstart[] = { 1, 1, 1 }; + + hdf5_read_array(data[n]->p[KRHO][0][0], "rho", 3, fdims, fstart, fcount, + mdims, mstart, H5T_IEEE_F64LE); + + hdf5_read_array(data[n]->p[UU][0][0], "uint", 3, fdims, fstart, fcount, + mdims, mstart, H5T_IEEE_F64LE); + + hdf5_read_array(data[n]->p[U1][0][0], "U1", 3, fdims, fstart, fcount, + mdims, mstart, H5T_IEEE_F64LE); + + hdf5_read_array(data[n]->p[U2][0][0], "U2", 3, fdims, fstart, fcount, + mdims, mstart, H5T_IEEE_F64LE); + + hdf5_read_array(data[n]->p[U3][0][0], "U3", 3, fdims, fstart, fcount, + mdims, mstart, H5T_IEEE_F64LE); + + hdf5_read_array(data[n]->p[B1][0][0], "B1", 3, fdims, fstart, fcount, + mdims, mstart, H5T_IEEE_F64LE); + + hdf5_read_array(data[n]->p[B2][0][0], "B2", 3, fdims, fstart, fcount, + mdims, mstart, H5T_IEEE_F64LE); + + hdf5_read_array(data[n]->p[B3][0][0], "B3", 3, fdims, fstart, fcount, + mdims, mstart, H5T_IEEE_F64LE); + + if (ELECTRONS == 9) { + hdf5_read_array(data[n]->p[TFLK][0][0], "te", 3, fdims, fstart, fcount, + mdims, mstart, H5T_IEEE_F64LE); + } + + hdf5_close(); + + dMact = Ladv = 0.; + + // construct four-vectors over "real" zones +#pragma omp parallel for collapse(2) reduction(+:dMact,Ladv) + for(int i = 1; i < N1+1; i++) { + for(int j = 1; j < N2+1; j++) { + + double X[NDIM] = { 0. }; + double gcov[NDIM][NDIM], gcon[NDIM][NDIM], gcov_KS[NDIM][NDIM], gcon_KS[NDIM][NDIM]; + double g, r, th; + + // this assumes axisymmetry in the coordinates + ijktoX(i-1,j-1,0, X); + gcov_func(X, gcov); + gcon_func(gcov, gcon); + g = gdet_zone(i-1,j-1,0); + + bl_coord(X, &r, &th); + + // the file is output in KS, so get KS metric + gcov_ks(r, th, gcov_KS); + gcon_func(gcov_KS, gcon_KS); + + for(int k = 1; k < N3+1; k++){ + + ijktoX(i-1,j-1,k,X); + double UdotU = 0.; + + for(int l = 1; l < NDIM; l++) + for(int m = 1; m < NDIM; m++) + UdotU += gcov_KS[l][m]*data[n]->p[U1+l-1][i][j][k]*data[n]->p[U1+m-1][i][j][k]; + double ufac = sqrt(-1./gcon_KS[0][0]*(1 + fabs(UdotU))); + + double ucon[NDIM] = { 0. }; + ucon[0] = -ufac * gcon_KS[0][0]; + + for(int l = 1; l < NDIM; l++) + ucon[l] = data[n]->p[U1+l-1][i][j][k] - ufac*gcon_KS[0][l]; + + double ucov[NDIM] = { 0. }; + flip_index(ucon, gcov_KS, ucov); + + // reconstruct the magnetic field three vectors + double udotB = 0.; + + for (int l = 1; l < NDIM; l++) { + udotB += ucov[l]*data[n]->p[B1+l-1][i][j][k]; + } + + double bcon[NDIM] = { 0. }; + double bcov[NDIM] = { 0. }; + + bcon[0] = udotB; + for (int l = 1; l < NDIM; l++) { + bcon[l] = (data[n]->p[B1+l-1][i][j][k] + ucon[l]*udotB)/ucon[0]; + } + flip_index(bcon, gcov_KS, bcov); + + double bsq = 0.; + for (int l=0; lb[i][j][k] = sqrt(bsq) * B_unit; + + if(i <= 21) { dMact += g * data[n]->p[KRHO][i][j][k] * ucon[1]; } + if(i >= 21 && i < 41 && 0) Ladv += g * data[n]->p[UU][i][j][k] * ucon[1] * ucov[0]; + if(i <= 21) Ladv += g * data[n]->p[UU][i][j][k] * ucon[1] * ucov[0]; + + // trust ... + //double udb1 = 0., udu1 = 0., bdb1 = 0.; + //MULOOP { udb1 += ucon[mu]*bcov[mu]; udu1 += ucon[mu]*ucov[mu]; bdb1 += bcon[mu]*bcov[mu]; } + + // now translate from KS (outcoords) -> MKS:hslope=1 + ucon[1] /= r; + ucon[2] /= M_PI; + bcon[1] /= r; + bcon[2] /= M_PI; + + // resynthesize the primitives. note we have assumed that B^i = *F^{ti} + double alpha = sqrt(-1. / gcon[0][0]); + data[n]->p[U1][i][j][k] = (gcon[0][1]*alpha*alpha + ucon[1]/ucon[0]) * ucon[0]; + data[n]->p[U2][i][j][k] = (gcon[0][2]*alpha*alpha + ucon[2]/ucon[0]) * ucon[0]; + data[n]->p[U3][i][j][k] = (gcon[0][3]*alpha*alpha + ucon[3]/ucon[0]) * ucon[0]; + data[n]->p[B1][i][j][k] = ucon[0] * bcon[1] - bcon[0] * ucon[1]; + data[n]->p[B2][i][j][k] = ucon[0] * bcon[2] - bcon[0] * ucon[2]; + data[n]->p[B3][i][j][k] = ucon[0] * bcon[3] - bcon[0] * ucon[3]; + + // ... but verify + //flip_index(bcon, gcov, bcov); + //flip_index(ucon, gcov, ucov); + //double udb2 = 0., udu2 = 0., bdb2 = 0.; + //MULOOP { udb2 += ucon[mu]*bcov[mu]; udu2 += ucon[mu]*ucov[mu]; bdb2 += bcon[mu]*bcov[mu]; } + //fprintf(stderr, "u.u %g %g u.b %g %g b.b %g %g\n", udu1,udu2, udb1,udb2, bdb1,bdb2); + } + } + } + + // now copy primitives and four-vectors according to boundary conditions + populate_boundary_conditions(n); + + dMact *= dx[3]*dx[2] ; + dMact /= 21. ; + Ladv *= dx[3]*dx[2] ; + Ladv /= 21. ; + + Mdot_dump = -dMact*M_unit/T_unit; + MdotEdd_dump = Mdotedd; + Ladv_dump = Ladv; + + double rescale_factor = 1.; + + if (target_mdot > 0) { + fprintf(stderr, "Resetting M_unit to match target_mdot = %g ", target_mdot); + + double current_mdot = Mdot_dump/MdotEdd_dump; + fprintf(stderr, "... is now %g\n", M_unit * fabs(target_mdot / current_mdot)); + rescale_factor = fabs(target_mdot / current_mdot); + M_unit *= rescale_factor; + + set_units(); + } + + Mdot_dump = -dMact*M_unit/T_unit; + MdotEdd_dump = Mdotedd; + Ladv_dump = Ladv; + + if (verbose == 2) { + fprintf(stderr,"dMact: %g [code]\n",dMact); + fprintf(stderr,"Ladv: %g [code]\n",Ladv_dump); + fprintf(stderr,"Mdot: %g [g/s] \n",Mdot_dump); + fprintf(stderr,"Mdot: %g [MSUN/YR] \n",Mdot_dump/(MSUN / YEAR)); + fprintf(stderr,"Mdot: %g [Mdotedd]\n",Mdot_dump/MdotEdd_dump); + fprintf(stderr,"Mdotedd: %g [g/s]\n",MdotEdd_dump); + fprintf(stderr,"Mdotedd: %g [MSUN/YR]\n",MdotEdd_dump/(MSUN/YEAR)); + } else if (verbose == 1) { + fprintf(stderr,"Mdot: %g [MSUN/YR] \n",Mdot_dump/(MSUN / YEAR)); + fprintf(stderr,"Mdot: %g [Mdotedd]\n",Mdot_dump/MdotEdd_dump); + } + + // now construct useful scalar quantities (over full (+ghost) zones of data) + init_physical_quantities(n, rescale_factor); +} + +// get dMact in the i'th radial zone (0 = 0 of the dump, so ignore ghost zones) +double get_code_dMact(int i, int n) +{ + i += 1; + double dMact = 0; +#pragma omp parallel for collapse(1) reduction(+:dMact) + for (int j=1; jp[U1+l-1][i][j][k]*data[n]->p[U1+m-1][i][j][k]; + double ufac = sqrt(-1./gcon[0][0]*(1 + fabs(UdotU))); + + double ucon[NDIM] = { 0. }; + ucon[0] = -ufac * gcon[0][0]; + + for(int l = 1; l < NDIM; l++) + ucon[l] = data[n]->p[U1+l-1][i][j][k] - ufac*gcon[0][l]; + + double ucov[NDIM] = { 0. }; + flip_index(ucon, gcov, ucov); + + dMact += g * data[n]->p[KRHO][i][j][k] * ucon[1]; + } + + } + return dMact; } void load_iharm_data(int n, char *fnam, int dumpidx, int verbose) @@ -1298,8 +1865,26 @@ void load_iharm_data(int n, char *fnam, int dumpidx, int verbose) hdf5_read_array(data[n]->p[KTOT][0][0], "prims", 4, fdims, fstart, fcount, mdims, mstart, H5T_IEEE_F64LE); } + //Reversing B Field + if(reverse_field) { + double multiplier = -1.0; + for(int i=0;ip[B1][i][j][k] = multiplier*data[n]->p[B1][i][j][k]; + data[n]->p[B2][i][j][k] = multiplier*data[n]->p[B2][i][j][k]; + data[n]->p[B3][i][j][k] = multiplier*data[n]->p[B3][i][j][k]; + } + } + } + } hdf5_read_single_val(&(data[n]->t), "t", H5T_IEEE_F64LE); + if (ELECTRONS == ELECTRONS_TFLUID) { + fstart[3] = 8; + hdf5_read_array(data[n]->p[THF][0][0], "prims", 4, fdims, fstart, fcount, mdims, mstart, H5T_IEEE_F64LE); + } + hdf5_close(); dMact = Ladv = 0.; @@ -1325,7 +1910,7 @@ void load_iharm_data(int n, char *fnam, int dumpidx, int verbose) ijktoX(i-1,j-1,k,X); double UdotU = 0.; - + // the four-vector reconstruction should have gcov and gcon and gdet using the modified coordinates // interpolating the four vectors to the zone center !!!! for(int l = 1; l < NDIM; l++) @@ -1345,11 +1930,11 @@ void load_iharm_data(int n, char *fnam, int dumpidx, int verbose) // reconstruct the magnetic field three vectors double udotB = 0.; - + for (int l = 1; l < NDIM; l++) { udotB += ucov[l]*data[n]->p[B1+l-1][i][j][k]; } - + double bcon[NDIM] = { 0. }; double bcov[NDIM] = { 0. }; @@ -1363,7 +1948,8 @@ void load_iharm_data(int n, char *fnam, int dumpidx, int verbose) for (int l=0; lb[i][j][k] = sqrt(bsq) * B_unit; - if(i <= 21) { dMact += g * data[n]->p[KRHO][i][j][k] * ucon[1];; } + + if(i <= 21) { dMact += g * data[n]->p[KRHO][i][j][k] * ucon[1]; } if(i >= 21 && i < 41 && 0) Ladv += g * data[n]->p[UU][i][j][k] * ucon[1] * ucov[0]; if(i <= 21) Ladv += g * data[n]->p[UU][i][j][k] * ucon[1] * ucov[0]; @@ -1371,6 +1957,31 @@ void load_iharm_data(int n, char *fnam, int dumpidx, int verbose) } } + // check if 21st zone (i.e., 20th without ghost zones) is beyond r_eh + // otherwise recompute + if (1==1) { + double r_eh = 1. + sqrt(1. - a*a); + int N2_by_2 = (int)(N2/2); + + double X[NDIM] = { 0. }; + ijktoX(20, N2_by_2, 0, X); + + double r, th; + bl_coord(X, &r, &th); + + if (r < r_eh) { + for (int i=1; i= r_eh) { + fprintf(stderr, "r_eh is beyond regular zones. recomputing at %g...\n", r); + dMact = get_code_dMact(i, n) * 21; + break; + } + } + } + } + // now copy primitives and four-vectors according to boundary conditions populate_boundary_conditions(n); @@ -1383,6 +1994,23 @@ void load_iharm_data(int n, char *fnam, int dumpidx, int verbose) MdotEdd_dump = Mdotedd; Ladv_dump = Ladv; + double rescale_factor = 1.; + + if (target_mdot > 0) { + fprintf(stderr, "Resetting M_unit to match target_mdot = %g ", target_mdot); + + double current_mdot = Mdot_dump/MdotEdd_dump; + fprintf(stderr, "... is now %g\n", M_unit * fabs(target_mdot / current_mdot)); + rescale_factor = fabs(target_mdot / current_mdot); + M_unit *= rescale_factor; + + set_units(); + } + + Mdot_dump = -dMact*M_unit/T_unit; + MdotEdd_dump = Mdotedd; + Ladv_dump = Ladv; + if (verbose == 2) { fprintf(stderr,"dMact: %g [code]\n",dMact); fprintf(stderr,"Ladv: %g [code]\n",Ladv_dump); @@ -1397,9 +2025,10 @@ void load_iharm_data(int n, char *fnam, int dumpidx, int verbose) } // now construct useful scalar quantities (over full (+ghost) zones of data) - init_physical_quantities(n); + init_physical_quantities(n, rescale_factor); } + int radiating_region(double X[NDIM]) { double r, th; @@ -1407,20 +2036,6 @@ int radiating_region(double X[NDIM]) return (r > rmin_geo && r < rmax_geo && th > th_beg && th < (M_PI-th_beg)); } -void get_model_powerlaw_vals(double X[NDIM], double *p, double *n, - double *gamma_min, double *gamma_max, double *gamma_cut) -{ - *gamma_min = powerlaw_gamma_min; - *gamma_max = powerlaw_gamma_max; - *gamma_cut = powerlaw_gamma_cut; - *p = powerlaw_p; - - double b = get_model_b(X); - double u_nth = powerlaw_eta*b*b/2; - // Number density of nonthermals - *n = u_nth * (*p - 2)/(*p - 1) * 1/(ME * CL*CL * *gamma_min); -} - // In case we want to mess with emissivities directly void get_model_jar(double X[NDIM], double Kcon[NDIM], double *jI, double *jQ, double *jU, double *jV, From 5b032c3eee2b0a9cc94334604832ad80353d0a31 Mon Sep 17 00:00:00 2001 From: Abhishek Joshi Date: Thu, 15 Sep 2022 15:23:29 -0500 Subject: [PATCH 93/93] added field configuration to output header --- model/iharm/model.c | 1 + 1 file changed, 1 insertion(+) diff --git a/model/iharm/model.c b/model/iharm/model.c index ac441fa..efd9db4 100644 --- a/model/iharm/model.c +++ b/model/iharm/model.c @@ -1227,6 +1227,7 @@ void output_hdf5() hdf5_write_single_val(&ELECTRONS, "type", H5T_STD_I32LE); hdf5_set_directory("/header/"); + hdf5_write_single_val(&reverse_field,"field_config",H5T_STD_I32LE); hdf5_make_directory("units"); hdf5_set_directory("/header/units/"); hdf5_write_single_val(&L_unit, "L_unit", H5T_IEEE_F64LE);