From 41f29cbf3076db51b96f240d27d432ef31b8aa71 Mon Sep 17 00:00:00 2001 From: Anton Khirnov Date: Tue, 14 Apr 2015 16:00:24 +0200 Subject: A major rewrite. Split the code into multiple files, drop the GSL dependency, introduce configurable logging, random cleanups. --- eval.c | 320 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 320 insertions(+) create mode 100644 eval.c (limited to 'eval.c') diff --git a/eval.c b/eval.c new file mode 100644 index 0000000..deb9579 --- /dev/null +++ b/eval.c @@ -0,0 +1,320 @@ +/* + * Copyright 2014-2015 Anton Khirnov + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * @file + * evaluation of the solution on a grid + */ + +#include +#include +#include +#include + +#include "brill_data.h" +#include "internal.h" + +int bd_eval_psi(const BDContext *bd, + const double *rho, int nb_coords_rho, + const double *z, int nb_coords_z, + const unsigned int diff_order[2], + double *psi, unsigned int psi_stride) +{ + BDPriv *s = bd->priv; + const double sfr = bd->basis_scale_factor_rho, sfz = bd->basis_scale_factor_z; + double (*eval)(double coord, int idx, double sf); + + double *basis_val_rho = NULL, *basis_val_z = NULL; + + double add = 0.0; + + int ret = 0; + + if (diff_order[0] > 2 || diff_order[1] > 2) { + bdi_log(bd, 0, "Derivatives of higher order than 2 are not supported.\n"); + return -ENOSYS; + } + + if (diff_order[0] == 0 && diff_order[1] == 0) + add = 1.0; + + /* precompute the basis values on the grid points */ + basis_val_rho = malloc(sizeof(*basis_val_rho) * bd->nb_coeffs_rho * nb_coords_rho); + basis_val_z = malloc(sizeof(*basis_val_z) * bd->nb_coeffs_z * nb_coords_z); + if (!basis_val_rho || !basis_val_z) { + ret = -ENOMEM; + goto fail; + } + + switch (diff_order[0]) { + case 0: eval = s->basis->eval; break; + case 1: eval = s->basis->eval_diff1; break; + case 2: eval = s->basis->eval_diff2; break; + } + + for (int i = 0; i < nb_coords_rho; i++) { + double rrho = rho[i]; + for (int j = 0; j < bd->nb_coeffs_rho; j++) + basis_val_rho[i * bd->nb_coeffs_rho + j] = eval(rrho, j, sfr); + } + + switch (diff_order[1]) { + case 0: eval = s->basis->eval; break; + case 1: eval = s->basis->eval_diff1; break; + case 2: eval = s->basis->eval_diff2; break; + } + for (int i = 0; i < nb_coords_z; i++) { + double zz = z[i]; + for (int j = 0; j < bd->nb_coeffs_z; j++) + basis_val_z[i * bd->nb_coeffs_z + j] = eval(zz, j, sfz); + } + + for (int i = 0; i < nb_coords_z; i++) { + double *dst = psi + i * psi_stride; + + for (int j = 0; j < nb_coords_rho; j++) { + double ppsi = add; + + for (int m = 0; m < bd->nb_coeffs_z; m++) + for (int n = 0; n < bd->nb_coeffs_rho; n++) + ppsi += s->coeffs[m * bd->nb_coeffs_rho + n] * basis_val_rho[j * bd->nb_coeffs_rho + n] * basis_val_z[i * bd->nb_coeffs_z + m]; + + *dst++ = ppsi; + } + } + +fail: + free(basis_val_rho); + free(basis_val_z); + + return ret; + +} + +static void eval_metric(const BDContext *bd, + const double *rho, int nb_coords_rho, + const double *z, int nb_coords_z, + enum BDMetricComponent comp, + const unsigned int diff_order[2], + const double *psi, + const double *dpsi_rho, const double *dpsi_z, + const double *d2psi_rho, const double *d2psi_z, const double *d2psi_rho_z, + double *out, unsigned int out_stride) +{ + const BDPriv *s = bd->priv; + + if (comp != BD_METRIC_COMPONENT_RHORHO && + comp != BD_METRIC_COMPONENT_ZZ && + comp != BD_METRIC_COMPONENT_PHIPHI) { + memset(out, 0, out_stride * nb_coords_z * sizeof(*out)); + return; + } + +/* the template for the loop over the grid points */ +#define EVAL_LOOP(DO_EVAL) \ +do { \ + for (int i = 0; i < nb_coords_z; i++) { \ + const double zz = z[i]; \ + double *dst = out + i * out_stride; \ + \ + for (int j = 0; j < nb_coords_rho; j++) { \ + const double rrho = rho[j]; \ + const double ppsi = psi[i * nb_coords_rho + j]; \ + const double psi2 = SQR(ppsi); \ + const double psi3 = psi2 * ppsi; \ + const double psi4 = SQR(psi2); \ + double val; \ + \ + DO_EVAL; \ + \ + *dst++ = val; \ + } \ + } \ +} while (0) + +#define GRID(arr) (arr[i * nb_coords_rho + j]) + + if (comp == BD_METRIC_COMPONENT_PHIPHI) { + switch (diff_order[0]) { + case 0: + switch (diff_order[1]) { + case 0: EVAL_LOOP(val = SQR(rrho) * psi4); break; + case 1: EVAL_LOOP(val = 4 * SQR(rrho) * psi3 * GRID(dpsi_z)); break; // ∂_z + case 2: EVAL_LOOP(val = 4 * SQR(rrho) * (GRID(d2psi_z) * psi3 + 3 * psi2 * SQR(GRID(dpsi_z)))); break; // ∂_z ∂_z + } + break; + case 1: + switch (diff_order[1]) { + case 0: EVAL_LOOP(val = 2 * rrho * psi4 + 4 * SQR(rrho) * psi3 * GRID(dpsi_rho)); break; // ∂_ρ + case 1: EVAL_LOOP(val = 8 * rrho * psi3 * GRID(dpsi_z) + 12 * SQR(rrho) * psi2 * GRID(dpsi_z) * GRID(dpsi_rho) + 4 * SQR(rrho) * psi3 * GRID(d2psi_rho_z)); break; // ∂_ρ ∂_z + } + break; + case 2: EVAL_LOOP(val = 4 * SQR(rrho) * psi3 * GRID(d2psi_rho) + 12 * SQR(rrho * ppsi * GRID(dpsi_rho)) + 16 * rrho * psi3 * GRID(dpsi_rho) + 2 * psi4); break; // ∂_ρ ∂_ρ + } + } else { + // γ_ρρ / γ_zz + +/* a wrapper around the actual evaluation expression that provides the q function and its + * derivatives as needed */ +#define DO_EVAL_Q_WRAPPER(DO_EVAL, eval_drho, eval_dz, eval_d2rho, eval_d2z, eval_d2rhoz) \ +do { \ + const double base = psi4 * exp(2 * s->q_func->q(bd, rrho, zz)); \ + double drho, dz, d2rho, d2z, d2rho_z; \ + \ + if (eval_drho) drho = s->q_func->dq_rho(bd, rrho, zz) + 2 * GRID(dpsi_rho) / ppsi; \ + if (eval_dz) dz = s->q_func->dq_z(bd, rrho, zz) + 2 * GRID(dpsi_z) / ppsi; \ + if (eval_d2rho) d2rho = s->q_func->d2q_rho(bd, rrho, zz) + 2 * GRID(d2psi_rho) / ppsi - 2 * SQR(GRID(dpsi_rho) / ppsi); \ + if (eval_d2z) d2z = s->q_func->d2q_z(bd, rrho, zz) + 2 * GRID(d2psi_z) / ppsi - 2 * SQR(GRID(dpsi_z) / ppsi); \ + if (eval_d2rhoz) d2rho_z = s->q_func->d2q_rho_z(bd, rrho, zz) + 2 * GRID(d2psi_rho_z) / ppsi - 2 * GRID(dpsi_rho) * GRID(dpsi_z) / psi2; \ + \ + DO_EVAL; \ +} while (0) + + switch (diff_order[0]) { + case 0: + switch (diff_order[1]) { + case 0: EVAL_LOOP(DO_EVAL_Q_WRAPPER(val = base, 0, 0, 0, 0, 0)); break; + case 1: EVAL_LOOP(DO_EVAL_Q_WRAPPER(val = 2 * base * dz, 0, 1, 0, 0, 0)); break; // ∂_z + case 2: EVAL_LOOP(DO_EVAL_Q_WRAPPER(val = 4 * SQR(dz) * base + 2 * base * d2z, 0, 1, 0, 1, 0)); break; // ∂_z ∂_z + } + break; + case 1: + switch (diff_order[1]) { + case 0: EVAL_LOOP(DO_EVAL_Q_WRAPPER(val = 2 * base * drho, 1, 0, 0, 0, 0)); break; // ∂_ρ + case 1: EVAL_LOOP(DO_EVAL_Q_WRAPPER(val = 4 * base * drho * dz + 2 * base * d2rho_z, 1, 1, 0, 0, 1)); break; // ∂_ρ ∂_z + } + break; + case 2: EVAL_LOOP(DO_EVAL_Q_WRAPPER(val = 4 * SQR(drho) * base + 2 * base * d2rho, 1, 0, 1, 0, 0)); break; // ∂_ρ ∂_ρ + } + } +} + +int bd_eval_metric(const BDContext *bd, const double *rho, int nb_coords_rho, + const double *z, int nb_coords_z, + int nb_comp, const enum BDMetricComponent *comp, + const unsigned int (*diff_order)[2], + double **out, unsigned int *out_strides) +{ + const int nb_coords = nb_coords_rho * nb_coords_z; + double *psi = NULL, *dpsi_rho = NULL, *dpsi_z = NULL, *d2psi_rho = NULL, *d2psi_rho_z = NULL, *d2psi_z = NULL; + int ret = 0; + + /* check the parameters for validity */ + for (int i = 0; i < nb_comp; i++) { + if (comp[i] >= 9 || comp[i] < 0) { + bdi_log(bd, 0, "Invalid component %d: %d\n", i, comp[i]); + return -EINVAL; + } + + if (diff_order[i][0] + diff_order[i][1] > 2) { + bdi_log(bd, 0, "At most second order derivatives are supported.\n"); + return -ENOSYS; + } + } + + /* evaluate the conformal factor and its derivatives as necessary */ +#define EVAL_PSI(arr, order) \ +do { \ + if (arr) \ + break; \ + \ + arr = malloc(nb_coords * sizeof(*arr)); \ + if (!arr) { \ + ret = -ENOMEM; \ + goto fail; \ + } \ + \ + ret = bd_eval_psi(bd, rho, nb_coords_rho, z, nb_coords_z, \ + order, arr, nb_coords_rho); \ + if (ret < 0) \ + goto fail; \ +} while (0) + + for (int i = 0; i < nb_comp; i++) { + if (comp[i] != BD_METRIC_COMPONENT_RHORHO && + comp[i] != BD_METRIC_COMPONENT_ZZ && + comp[i] != BD_METRIC_COMPONENT_PHIPHI) + continue; + + EVAL_PSI(psi, ((unsigned int[2]){ 0, 0 })); + if (diff_order[i][0]) + EVAL_PSI(dpsi_rho, ((unsigned int[2]){ 1, 0 })); + if (diff_order[i][0] > 1) + EVAL_PSI(d2psi_rho, ((unsigned int[2]){ 2, 0 })); + if (diff_order[i][1]) + EVAL_PSI(dpsi_z, ((unsigned int[2]){ 0, 1 })); + if (diff_order[i][1] > 1) + EVAL_PSI(d2psi_z, ((unsigned int[2]){ 0, 2 })); + if (diff_order[i][0] && diff_order[i][1]) + EVAL_PSI(d2psi_rho_z, ((unsigned int[2]){ 1, 1 })); + } + + /* evaluate the requested metric components */ + for (int i = 0; i < nb_comp; i++) + eval_metric(bd, rho, nb_coords_rho, z, nb_coords_z, + comp[i], diff_order[i], + psi, dpsi_rho, dpsi_z, d2psi_rho, d2psi_z, d2psi_rho_z, + out[i], out_strides[i]); + +fail: + free(psi); + free(dpsi_rho); + free(dpsi_z); + free(d2psi_rho); + free(d2psi_rho_z); + free(d2psi_z); + + return ret; +} + +int bd_eval_q(const BDContext *bd, const double *rho, int nb_coords_rho, + const double *z, int nb_coords_z, const unsigned int diff_order[2], + double *out, unsigned int out_stride) +{ + BDPriv *s = bd->priv; + double (*eval)(const struct BDContext *bd, double rho, double z); + + if (diff_order[0] > 2 || diff_order[1] > 2) { + bdi_log(bd, 0, "Derivatives of higher order than 2 are not supported.\n"); + return -ENOSYS; + } + + switch (diff_order[0]) { + case 0: + switch (diff_order[1]) { + case 0: eval = s->q_func->q; break; + case 1: eval = s->q_func->dq_z; break; + case 2: eval = s->q_func->d2q_z; break; + } + break; + case 1: + switch (diff_order[1]) { + case 0: eval = s->q_func->dq_rho; break; + case 1: eval = s->q_func->d2q_rho_z; break; + } + break; + case 2: eval = s->q_func->d2q_rho; break; + } + + for (int i = 0; i < nb_coords_z; i++) { + double *dst = out + i * out_stride; + for (int j = 0; j < nb_coords_rho; j++) + *dst++ = eval(bd, rho[j], z[i]); + } + + return 0; +} -- cgit v1.2.3