././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1630712495.4229062 extinction-0.4.6/0000755000175100001710000000000000000000000013245 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1630712478.0 extinction-0.4.6/LICENSE0000644000175100001710000000211000000000000014244 0ustar00runnerdockerThe MIT License (MIT) Copyright (c) 2016 Kyle Barbary and contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1630712478.0 extinction-0.4.6/MANIFEST.in0000644000175100001710000000022100000000000014776 0ustar00runnerdockerinclude README.md include LICENSE include setup.py include pyproject.toml include extinction.pyx include extern/bs.* include extern/bsplines.pxi ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1630712495.4229062 extinction-0.4.6/PKG-INFO0000644000175100001710000000110700000000000014341 0ustar00runnerdockerMetadata-Version: 2.1 Name: extinction Version: 0.4.6 Summary: Fast interstellar dust extinction laws Home-page: http://github.com/kbarbary/extinction Author: Kyle Barbary Author-email: kylebarbary@gmail.com License: MIT Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Programming Language :: Python :: 3 Classifier: License :: OSI Approved :: MIT License Classifier: Topic :: Scientific/Engineering :: Astronomy Classifier: Intended Audience :: Science/Research Requires-Python: >=3.5 License-File: LICENSE documentation: http://extinction.readthedocs.io ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1630712478.0 extinction-0.4.6/README.md0000644000175100001710000000066600000000000014534 0ustar00runnerdockerextinction ========== *Fast interstellar dust extinction laws in Python* ![Build Status](https://github.com/kbarbary/extinction/workflows/Python%20package/badge.svg) [![PyPI](https://img.shields.io/pypi/v/extinction.svg?style=flat-square)](https://pypi.python.org/pypi/extinction) [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.804967.svg)](https://doi.org/10.5281/zenodo.804967) documentation: http://extinction.readthedocs.io/ ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1630712495.4189062 extinction-0.4.6/extern/0000755000175100001710000000000000000000000014552 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1630712478.0 extinction-0.4.6/extern/bs.c0000644000175100001710000011473500000000000015335 0ustar00runnerdocker#include #include #include #include #if defined(_MSC_VER) #define INLINE _inline // __inline in newer versions #define RESTRICT __restrict #else #define INLINE inline #define RESTRICT restrict #endif //----------------------------------------------------------------------------- // debug stuff (remove later) #include void print_a_and_b(double first[5], double last[5], double *A, double *b, int M) { int i; printf("\nfirst: [ %f %f %f %f %f ]\n", first[0], first[1], first[2], first[3], first[4]); for (i=0; i= values[i] and x= values[n-1]. //----------------------------------------------------------------------------- // Linear search starting from guess that // values[start] <= x < values[start+1]. static int find_index_from(double *values, int n, double x, int start) { int i; if (start <= -1) { // search down i = 0; while (i < n && x >= values[i]) i++; return i-1; } else if (start >= n-1) { // search down i = n - 1; while (i > -1 && x < values[i]) i--; return i; } else if (x >= values[start]) { // search up i = start + 1; while (i < n && x >= values[i]) i++; return i-1; } else { // search down i = start - 1; while (i > -1 && x < values[i]) i--; return i; } } // find index using binary search static int find_index_binary(double *values, int n, double x) { int lo, hi, mid; lo = 0; hi = n; mid = n/2; if (x < values[0]) return -1; if (x >= values[n-1]) return n-1; while (hi - lo > 1) { if (x >= values[mid]) lo = mid; else hi = mid; mid = lo + (hi - lo) / 2; } return mid; } static int is_monotonic(bs_array x) { int i; int ok; ok = 1; for (i=1; i= x.data[(i-1)*x.stride]); } return ok; } static int min_points(bs_bctype left, bs_bctype right) { // one additional point needed for each not-a-knot condition. return 2 + (left == BS_NOTAKNOT) + (right == BS_NOTAKNOT); } //----------------------------------------------------------------------------- // knots & constants //----------------------------------------------------------------------------- // fill spline knots based on x array (includes padding on either // end of array). static double* alloc_knots(bs_array x) { int N; double *knots; int i; N = x.size; knots = malloc((N + 5) * sizeof(double)); // move pointer past initial two-element padding. knots += 2; // copy x into main part of knots for (i=0; i < N; i++) knots[i] = x.data[i * x.stride]; // fill padded area before beginning knots[-2] = knots[0] - 2.0 * (knots[1] - knots[0]); knots[-1] = knots[0] - 1.0 * (knots[1] - knots[0]); // fill padded area after end. knots[N] = knots[N-1] + 1.0 * (knots[N-1] - knots[N-2]); knots[N+1] = knots[N-1] + 2.0 * (knots[N-1] - knots[N-2]); knots[N+2] = knots[N-1] + 3.0 * (knots[N-1] - knots[N-2]); return knots; } static void free_knots(double *knots) { free(knots - 2); } // constants used when evaluating a spline. // constants + 4*i is a pointer to the four constants used // when evaluating the spline in the range knots[i] <= x < knots[i+1]. static double* alloc_constants(double *knots, int n) { int i; double *constants; constants = malloc(4 * n * sizeof(double)); for (i=0; i0; i--) { b[i] -= b[i+1] * A[3*i+2]; } // first row is different b[0] -= b[1] * A[1] + b[2] * A[2]; } */ //----------------------------------------------------------------------------- // solve() // // Solve A * x = b for x. The solution is stored in b. // // A is a matrix like this: // // | x x x x x | // | x x x | // | x x x | // | ... | // | x x x | // | x x x | // | x x x | // | x x x x x | // // A is stored compactly in 3*n elements, with row i corresponding to A[3*i], // with the exception of the first and last rows which are passed in // separately because they are too large to be stored this way. // // Note that the first 3 and last 3 elements of A are initially empty as // these row values are stored in `first` and `last`. In fact the last 3 // elements of A are not used at all. // //----------------------------------------------------------------------------- typedef struct { double *first; double *rows; // first + 5 double *last; // first + 5 + 3*(M-1) } banded_matrix; // allocate storate for a banded matrix. // M is total number of rows, including first and last. static banded_matrix alloc_banded_matrix(int M) { double* first = malloc((5 + 3*(M-1) + 5) * sizeof(double)); banded_matrix m = {first, first + 5, first + 5 + 3*(M-1)}; return m; } static void free_banded_matrix(banded_matrix A) { free(A.first); A.first = NULL; A.rows = NULL; A.last = NULL; } static void copy_banded_matrix(banded_matrix dst, banded_matrix src, int M) { size_t nbytes = (10 + 3*(M-1)) * sizeof(double); memcpy(dst.first, src.first, nbytes); } static void solve(banded_matrix mat, double* RESTRICT b, int n) { int i; double tmp; double* RESTRICT first = mat.first; double* RESTRICT A = mat.rows; double* RESTRICT last = mat.last; // rows 1, 2, 3: divide by first non-zero // // x x x x x | y x x x x x | y // x x x | y 1 x x | y // x x x | y --> 1 x x | y // x x x | y 1 x x | y for (i=1; i<4; i++) { b[i] /= A[3*i]; A[3*i+2] /= A[3*i]; A[3*i+1] /= A[3*i]; A[3*i] = 1.0; } // eliminate first two elements of first row and divide by first non-zero. // // x x x x x | y 0 0 1 x x | y // 1 x x | y 1 x x | y // 1 x x | y --> 1 x x | y // 1 x x | y 1 x x | y b[0] -= first[0] * b[1]; first[2] -= first[0] * A[3*1+2]; first[1] -= first[0] * A[3*1+1]; first[0] = 0.0; b[0] -= first[1] * b[2]; first[3] -= first[1] * A[3*2+2]; first[2] -= first[1] * A[3*2+1]; first[1] = 0.0; b[0] /= first[2]; first[4] /= first[2]; first[3] /= first[2]; first[2] = 1.0; // reduce row 3 // // 0 0 1 x x | y 0 0 1 x x | y // 1 x x | y 1 x x | y // 1 x x | y --> 1 x x | y // 1 x x | y 0 1 x | y b[3] -= A[3*3+0] * b[0]; A[3*3+2] -= A[3*3+0] * first[4]; A[3*3+1] -= A[3*3+0] * first[3]; A[3*3+0] = 0.0; b[3] /= A[3*3+1]; A[3*3+2] /= A[3*3+1]; A[3*3+1] = 1.0; // permute first three rows: // 0 0 1 x x | y 1 x x | y // 1 x x | y 1 x x | y // 1 x x | y --> 1 x x | y // 0 1 x | y 0 1 x | y tmp = b[0]; b[0] = b[1]; A[3*0+0] = A[3*1+0]; A[3*0+1] = A[3*1+1]; A[3*0+2] = A[3*1+2]; b[1] = b[2]; A[3*1+0] = A[3*2+0]; A[3*1+1] = A[3*2+1]; A[3*1+2] = A[3*2+2]; b[2] = tmp; A[3*2+0] = first[2]; A[3*2+1] = first[3]; A[3*2+2] = first[4]; // reduce rest of the middle rows for (i=4; i=3; i--) { b[i] -= b[i+1] * A[3*i+2]; } // we now have: // 1 x x | y // 1 x x | y // 1 x x | y // 1 | y // 1 | y // ... // // eliminate the remaining elements. b[2] -= b[3] * A[3*2+1] + b[4] * A[3*2+2]; b[1] -= b[2] * A[3*1+1] + b[3] * A[3*1+2]; b[0] -= b[1] * A[3*0+1] + b[2] * A[3*0+2]; } //----------------------------------------------------------------------------- // finding coefficients //----------------------------------------------------------------------------- static void notaknot_row(double *consts, int i, double row[5]) { int j; double buf[4]; d3b3nonzeros(i-1, consts, row); d3b3nonzeros(i, consts, buf); row[4] = 0.0; for (j=0; j<4; j++) { row[j+1] -= buf[j]; } } static void fill_banded_matrix(banded_matrix A, double* RESTRICT knots, double* RESTRICT consts, int N, bs_bctype bctypes[2]) { int i; double* RESTRICT first = A.first; double* RESTRICT rows = A.rows; double* RESTRICT last = A.last; // Left boundary condition switch (bctypes[0]) { case BS_DERIV1: db3nonzeros(knots[0], 0, knots, consts, first); first[3] = first[4] = 0.0; break; case BS_DERIV2: d2b3nonzeros(knots[0], 0, knots, consts, first); first[3] = first[4] = 0.0; break; case BS_NOTAKNOT: notaknot_row(consts, 1, first); } // fill rows 1 through M-1 with values of b_{i-3}, b_{i-2}, b{i-1} // at knot i. for (i=0; i1; i--) last[i] = last[i-2]; last[0] = last[1] = 0.0; break; case BS_DERIV2: d2b3nonzeros(knots[N-1], N-1, knots, consts, last); for (i=4; i>1; i--) last[i] = last[i-2]; last[0] = last[1] = 0.0; break; case BS_NOTAKNOT: notaknot_row(consts, N-2, last); } } // Find spline coefficients along one dimension. // knots and consts are as belong to a spline. // A should be allocated with M = values.size + 2. // coeffs should be size M. static void find_1d_coefficients(banded_matrix A, bs_array values, double bcvalues[2], double* RESTRICT coeffs) { int i; int N = values.size; int M = N+2; coeffs[0] = bcvalues[0]; for (i=0; iknots = alloc_knots(x); spline->n = N; spline->exts = exts; spline->consts = alloc_constants(spline->knots, N); spline->coeffs = malloc(M * sizeof(double)); A = alloc_banded_matrix(M); fill_banded_matrix(A, spline->knots, spline->consts, N, bctypes); bcvalues[0] = (bcs.left.type == BS_NOTAKNOT) ? 0.0 : bcs.left.value; bcvalues[1] = (bcs.right.type == BS_NOTAKNOT) ? 0.0 : bcs.right.value; find_1d_coefficients(A, y, bcvalues, spline->coeffs); free_banded_matrix(A); *out = spline; return BS_OK; } void bs_spline1d_free(bs_spline1d* spline) { if (spline != NULL) { free_knots(spline->knots); free(spline->consts); free(spline->coeffs); free(spline); } } bs_errorcode bs_spline1d_eval(bs_spline1d *spline, bs_array x, bs_array out) { int i; int j; double xval; double b3vals[4]; // for first index, it could be anywhere, so use binary search i = find_index_binary(spline->knots, spline->n, x.data[0]); for (j=0; jknots, spline->n, xval, i); // index outside left boundary if (i == -1) { switch (spline->exts.left.type) { case BS_EXTRAPOLATE: i = 0; break; case BS_CONSTANT: i = 0; xval = spline->knots[0]; break; case BS_VALUE: out.data[j * out.stride] = spline->exts.left.value; continue; case BS_RAISE: return BS_DOMAINERROR; } } // index outside right boundary else if (i == spline->n - 1) { switch (spline->exts.right.type) { case BS_EXTRAPOLATE: i = spline->n - 2; break; case BS_CONSTANT: i = spline->n-2; xval = spline->knots[spline->n-1]; break; case BS_VALUE: out.data[j * out.stride] = spline->exts.right.value; continue; case BS_RAISE: return BS_DOMAINERROR; } } // if we get this far, we're either extrapolating or xval is in range. b3nonzeros(xval, i, spline->knots, spline->consts, b3vals); out.data[j*out.stride] = (spline->coeffs[i] * b3vals[0] + spline->coeffs[i+1] * b3vals[1] + spline->coeffs[i+2] * b3vals[2] + spline->coeffs[i+3] * b3vals[3]); } return BS_OK; } //----------------------------------------------------------------------------- // spline2d //----------------------------------------------------------------------------- // get the i-th bc value static double get_bcarray_value(bs_bcarray bc, int i) { return (bc.type == BS_NOTAKNOT) ? 0.0 : bc.data[i * bc.stride]; } static int bcarray_size_match(bs_bcarray bc, int size) { // if not-a-knot, value is not used, so size is OK. return (bc.type == BS_NOTAKNOT) ? 1 : (bc.size == size); } bs_errorcode bs_spline2d_create(bs_array x, bs_array y, bs_array2d z, bs_bcarray_pair xbcs, bs_bcarray_pair ybcs, bs_exts xexts, bs_exts yexts, bs_spline2d **out) { int i, j; int nx, mx, ny, my; bs_bctype bctypes[2] = {ybcs.left.type, ybcs.right.type}; double bcvalues[2]; bs_spline2d* spline; double *coeffs; banded_matrix A, Awork; bs_array zslice, coeffs_slice; double *buf; *out = NULL; // In case of error, ensure that output pointer is NULL. if ((x.size != z.sizes[0]) || (y.size != z.sizes[1])) return BS_SIZEMISMATCH; if (!is_monotonic(x) || !is_monotonic(y)) return BS_NOTMONOTONIC; if ((x.size < min_points(xbcs.left.type, xbcs.right.type)) || (y.size < min_points(ybcs.left.type, ybcs.right.type))) return BS_TOOFEWPOINTS; // check if boundary condition sizes match x and y sizes. if (!(bcarray_size_match(xbcs.left, y.size) && bcarray_size_match(xbcs.right, y.size) && bcarray_size_match(ybcs.left, x.size) && bcarray_size_match(ybcs.left, x.size))) return BS_BCSIZEMISMATCH; spline = malloc(sizeof(bs_spline2d)); nx = x.size; mx = nx + 2; ny = y.size; my = ny + 2; spline->xknots = alloc_knots(x); spline->xconsts = alloc_constants(spline->xknots, nx); spline->nx = nx; spline->xexts = xexts; spline->yknots = alloc_knots(y); spline->yconsts = alloc_constants(spline->yknots, ny); spline->ny = ny; spline->yexts = yexts; coeffs = malloc(mx * my * sizeof(double)); // find coefficients along y (fast axis) A = alloc_banded_matrix(my); Awork = alloc_banded_matrix(my); fill_banded_matrix(A, spline->yknots, spline->yconsts, spline->ny, bctypes); for (i=0; ixknots, spline->xconsts, spline->nx, bctypes); buf = malloc(mx * sizeof(double)); for (i=0; icoeffs = coeffs; *out = spline; return BS_OK; } void bs_spline2d_free(bs_spline2d* spline) { if (spline != NULL) { free_knots(spline->xknots); free(spline->xconsts); free_knots(spline->yknots); free(spline->yconsts); free(spline->coeffs); free(spline); } } bs_errorcode bs_spline2d_eval(bs_spline2d *spline, bs_array x, bs_array y, bs_array2d out) { // for first index, it could be anywhere, so use binary search int i = find_index_binary(spline->xknots, spline->nx, x.data[0]); int j0 = find_index_binary(spline->yknots, spline->ny, y.data[0]); int j, k, l; int my = spline->ny + 2; // for indexing coeffs. double xb3vals[4]; double yb3vals[4]; double xval, yval; for (k=0; kxknots, spline->nx, xval, i); // index outside left boundary if (i == -1) { switch (spline->xexts.left.type) { case BS_EXTRAPOLATE: i = 0; break; case BS_CONSTANT: i = 0; xval = spline->xknots[0]; break; case BS_VALUE: for (l=0; lxexts.left.value; } continue; case BS_RAISE: return BS_DOMAINERROR; } } // index outside right boundary else if (i == spline->nx - 1) { switch (spline->xexts.right.type) { case BS_EXTRAPOLATE: i = spline->nx - 2; break; case BS_CONSTANT: i = spline->nx - 2; xval = spline->xknots[spline->nx-1]; break; case BS_VALUE: for (l=0; lxexts.right.value; } continue; case BS_RAISE: return BS_DOMAINERROR; } } // get basis function values for x coordinate b3nonzeros(xval, i, spline->xknots, spline->xconsts, xb3vals); // x value is in range (or extrapolating); loop over y values: j = j0; for (l=0; lyknots, spline->ny, yval, j); // index outside left boundary if (j == -1) { switch (spline->yexts.left.type) { case BS_EXTRAPOLATE: j = 0; break; case BS_CONSTANT: j = 0; yval = spline->yknots[0]; break; case BS_VALUE: out.data[k * out.strides[0] + l * out.strides[1]] = spline->yexts.left.value; continue; case BS_RAISE: return BS_DOMAINERROR; } } // index outside right boundary else if (j == spline->ny - 1) { switch (spline->yexts.right.type) { case BS_EXTRAPOLATE: j = spline->ny - 2; break; case BS_CONSTANT: j = spline->ny - 2; yval = spline->yknots[spline->ny-1]; break; case BS_VALUE: out.data[k * out.strides[0] + l * out.strides[1]] = spline->yexts.right.value; continue; case BS_RAISE: return BS_DOMAINERROR; } } // get basis function values for y coordinate b3nonzeros(yval, j, spline->yknots, spline->yconsts, yb3vals); out.data[k * out.strides[0] + l * out.strides[1]] = (spline->coeffs[(i )*my+j] * xb3vals[0] * yb3vals[0] + spline->coeffs[(i )*my+j+1] * xb3vals[0] * yb3vals[1] + spline->coeffs[(i )*my+j+2] * xb3vals[0] * yb3vals[2] + spline->coeffs[(i )*my+j+3] * xb3vals[0] * yb3vals[3] + spline->coeffs[(i+1)*my+j] * xb3vals[1] * yb3vals[0] + spline->coeffs[(i+1)*my+j+1] * xb3vals[1] * yb3vals[1] + spline->coeffs[(i+1)*my+j+2] * xb3vals[1] * yb3vals[2] + spline->coeffs[(i+1)*my+j+3] * xb3vals[1] * yb3vals[3] + spline->coeffs[(i+2)*my+j] * xb3vals[2] * yb3vals[0] + spline->coeffs[(i+2)*my+j+1] * xb3vals[2] * yb3vals[1] + spline->coeffs[(i+2)*my+j+2] * xb3vals[2] * yb3vals[2] + spline->coeffs[(i+2)*my+j+3] * xb3vals[2] * yb3vals[3] + spline->coeffs[(i+3)*my+j] * xb3vals[3] * yb3vals[0] + spline->coeffs[(i+3)*my+j+1] * xb3vals[3] * yb3vals[1] + spline->coeffs[(i+3)*my+j+2] * xb3vals[3] * yb3vals[2] + spline->coeffs[(i+3)*my+j+3] * xb3vals[3] * yb3vals[3]); } } return BS_OK; } //----------------------------------------------------------------------------- // uspline1d //----------------------------------------------------------------------------- static void fill_banded_matrix_u(banded_matrix A, int N, double didx, bs_bctype bctypes[2]) { int i, j; // values, first and second derivatives of b3_{i-3}, b3_{i-2}, b3_{i-1} // at knot i. const double b3vals[3] = {1.0/6.0, 2.0/3.0, 1.0/6.0}; const double db3vals[3] = {-0.5 * didx, 0.0, 0.5 * didx}; const double d2b3vals[3] = {didx * didx, -2.0 * didx * didx, didx * didx}; const double notaknot_row[5] = {-1.0, 4.0, -6.0, 4.0, -1.0}; // Left boundary condition switch (bctypes[0]) { case BS_DERIV1: for (i=0; i<3; i++) A.first[i] = db3vals[i]; A.first[3] = A.first[4] = 0.0; break; case BS_DERIV2: for (i=0; i<3; i++) A.first[i] = d2b3vals[i]; A.first[3] = A.first[4] = 0.0; break; case BS_NOTAKNOT: for (i=0; i<5; i++) A.first[i] = notaknot_row[i]; } // rows for (i=0; ix = x; spline->didx = didx; spline->n = N; spline->exts = exts; b = malloc(M * sizeof(double)); A = alloc_banded_matrix(M); fill_banded_matrix_u(A, N, didx, bctypes); // fill RHS b[0] = (bcs.left.type == BS_NOTAKNOT)? 0.0: bcs.left.value; b[M-1] = (bcs.right.type == BS_NOTAKNOT)? 0.0: bcs.right.value; for (i=0; icoeffs = b; *out = spline; return BS_OK; } void bs_uspline1d_free(bs_uspline1d* spline) { if (spline != NULL) { free(spline->coeffs); free(spline); } } bs_errorcode bs_uspline1d_eval(bs_uspline1d *spline, bs_array x, bs_array out) { int i, j; double xval; double xfloor; double b3vals[4]; for (j=0; jx.min) * spline->didx; xfloor = floor(xval); i = (int)xfloor; // index outside left boundary if (i < 0) { switch (spline->exts.left.type) { case BS_EXTRAPOLATE: i = 0; xfloor = 0.0; break; case BS_CONSTANT: i = 0; xfloor = 0.0; xval = 0.0; break; case BS_VALUE: out.data[j * out.stride] = spline->exts.left.value; continue; case BS_RAISE: return BS_DOMAINERROR; } } // index outside right boundary else if (i >= spline->n-1) { switch (spline->exts.right.type) { case BS_EXTRAPOLATE: i = spline->n - 2; xfloor = i; break; case BS_CONSTANT: i = spline->n - 2; xfloor = i; xval = xfloor+1.0; break; case BS_VALUE: out.data[j * out.stride] = spline->exts.right.value; continue; case BS_RAISE: return BS_DOMAINERROR; } } // if we get this far, we're evaluating the spline b3unonzeros(xval - xfloor, b3vals); out.data[j*out.stride] = (spline->coeffs[i] * b3vals[0] + spline->coeffs[i+1] * b3vals[1] + spline->coeffs[i+2] * b3vals[2] + spline->coeffs[i+3] * b3vals[3]); } return BS_OK; } ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1630712478.0 extinction-0.4.6/extern/bs.h0000644000175100001710000000673400000000000015341 0ustar00runnerdocker#ifndef BS_H #define BS_H //----------------------------------------------------------------------------- // Error codes //----------------------------------------------------------------------------- typedef enum { BS_OK = 0, BS_OUTOFMEMORY = 1, BS_DOMAINERROR = 2, BS_NOTMONOTONIC = 3, BS_SIZEMISMATCH = 4, BS_BCSIZEMISMATCH = 5, BS_TOOFEWPOINTS = 6, } bs_errorcode; //----------------------------------------------------------------------------- // Input data types //----------------------------------------------------------------------------- typedef struct { double *data; int size; int stride; } bs_array; typedef struct { double *data; int sizes[2]; int strides[2]; } bs_array2d; typedef struct { double min; // inclusive double max; // inclusive } bs_range; //----------------------------------------------------------------------------- // Boundary conditions //----------------------------------------------------------------------------- typedef enum {BS_DERIV1, BS_DERIV2, BS_NOTAKNOT} bs_bctype; typedef struct { bs_bctype type; double value; } bs_bc; typedef struct { bs_bc left; bs_bc right; } bs_bcs; // array boundary conditions for spline2d. typedef struct { bs_bctype type; double *data; int size; int stride; } bs_bcarray; typedef struct { bs_bcarray left; bs_bcarray right; } bs_bcarray_pair; //----------------------------------------------------------------------------- // out-of-domain behavior ("extension") //----------------------------------------------------------------------------- typedef enum {BS_EXTRAPOLATE, BS_CONSTANT, BS_VALUE, BS_RAISE} bs_exttype; typedef struct { bs_exttype type; double value; } bs_ext; typedef struct { bs_ext left; bs_ext right; } bs_exts; //----------------------------------------------------------------------------- // 1-d splines //----------------------------------------------------------------------------- typedef struct { double *knots; double *consts; double *coeffs; int n; bs_exts exts; } bs_spline1d; bs_errorcode bs_spline1d_create(bs_array x, bs_array y, bs_bcs bcs, bs_exts exts, bs_spline1d **out); bs_errorcode bs_spline1d_eval(bs_spline1d *spline, bs_array x, bs_array out); void bs_spline1d_free(bs_spline1d *spline); typedef struct { bs_range x; double didx; double *coeffs; int n; bs_exts exts; } bs_uspline1d; bs_errorcode bs_uspline1d_create(bs_range x, bs_array y, bs_bcs bcs, bs_exts exts, bs_uspline1d **out); bs_errorcode bs_uspline1d_eval(bs_uspline1d *spline, bs_array x, bs_array out); void bs_uspline1d_free(bs_uspline1d *spline); //----------------------------------------------------------------------------- // 2-d splines //----------------------------------------------------------------------------- typedef struct { double *xknots; double *xconsts; double *yknots; double *yconsts; double *coeffs; int nx; int ny; bs_exts xexts; bs_exts yexts; } bs_spline2d; bs_errorcode bs_spline2d_create(bs_array x, bs_array y, bs_array2d z, bs_bcarray_pair xbcs, bs_bcarray_pair ybcs, bs_exts xexts, bs_exts yexts, bs_spline2d **out); bs_errorcode bs_spline2d_eval(bs_spline2d *spline, bs_array x, bs_array y, bs_array2d out); void bs_spline2d_free(bs_spline2d *spline); #endif ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1630712478.0 extinction-0.4.6/extern/bsplines.pxi0000644000175100001710000000771700000000000017127 0ustar00runnerdockercdef extern from "bs.h": ctypedef enum bs_errorcode: BS_OK = 0 BS_OUTOFMEMORY = 1 BS_DOMAINERROR = 2 BS_NOTMONOTONIC = 3 BS_SIZEMISMATCH = 4 BS_BCSIZEMISMATCH = 5 BS_TOOFEWPOINTS = 6 ctypedef struct bs_array: double *data int size int stride ctypedef struct bs_array2d: double *data int sizes[2] int strides[2] ctypedef struct bs_range: double min double max ctypedef enum bs_bctype: BS_DERIV1 BS_DERIV2 BS_NOTAKNOT ctypedef struct bs_bc: bs_bctype type double value ctypedef struct bs_bcs: bs_bc left bs_bc right ctypedef enum bs_exttype: BS_EXTRAPOLATE BS_CONSTANT BS_VALUE BS_RAISE ctypedef struct bs_ext: bs_exttype type double value ctypedef struct bs_exts: bs_ext left bs_ext right ctypedef struct bs_spline1d: double *knots double *coeffs int n ctypedef struct bs_bcarray: bs_bctype type double *data int size int stride ctypedef struct bs_bcarray_pair: bs_bcarray left bs_bcarray right bs_errorcode bs_spline1d_create(bs_array x, bs_array y, bs_bcs bcs, bs_exts exts, bs_spline1d **out) bs_errorcode bs_spline1d_eval(bs_spline1d *spline, bs_array x, bs_array out) void bs_spline1d_free(bs_spline1d *spline) ctypedef struct bs_spline2d: double *xknots double *xconsts double *yknots double *yconsts double *coeffs int nx int ny bs_exts xexts bs_exts yexts bs_errorcode bs_spline2d_create(bs_array x, bs_array y, bs_array2d z, bs_bcarray_pair xbcs, bs_bcarray_pair ybcs, bs_exts xexts, bs_exts yexts, bs_spline2d **out) bs_errorcode bs_spline2d_eval(bs_spline2d *spline, bs_array x, bs_array y, bs_array2d out) void bs_spline2d_free(bs_spline2d *spline) ctypedef struct bs_uspline1d: bs_range x double didx double *coeffs int n bs_exts exts bs_errorcode bs_uspline1d_create(bs_range x, bs_array y, bs_bcs bcs, bs_exts exts, bs_uspline1d **out) bs_errorcode bs_uspline1d_eval(bs_uspline1d *spline, bs_array x, bs_array out) void bs_uspline1d_free(bs_uspline1d *spline) #------------------------------------------------------------------------------ # helpers class DomainError(Exception): """Raised when spline input(s) are outside spline boundaries.""" pass cdef int assert_ok(bs_errorcode code) except -1: """raise an exception if the error code is not OK""" if code == BS_OK: return 0 elif code == BS_OUTOFMEMORY: raise MemoryError() elif code == BS_DOMAINERROR: raise DomainError() elif code == BS_NOTMONOTONIC: raise ValueError("input array(s) not monotonically increasing") elif code == BS_SIZEMISMATCH: raise ValueError("input array size mismatch") elif code == BS_BCSIZEMISMATCH: raise ValueError("boundary condition size mismatch") elif code == BS_TOOFEWPOINTS: raise ValueError("Too few points in input array. required: 2 + (1 for each not-a-knot condition)") else: raise Exception("unrecognized error") cdef inline bs_array to_bs_array(double[:] x): return bs_array(&x[0], x.shape[0], x.strides[0]//sizeof(double)) cdef inline bs_array2d to_bs_array2d(double[:, :] x): cdef bs_array2d out out.data = &x[0, 0] out.sizes[0] = x.shape[0] out.sizes[1] = x.shape[1] out.strides[0] = x.strides[0] // sizeof(double) out.strides[1] = x.strides[1] // sizeof(double) return out ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1630712495.4229062 extinction-0.4.6/extinction.egg-info/0000755000175100001710000000000000000000000017123 5ustar00runnerdocker././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1630712495.0 extinction-0.4.6/extinction.egg-info/PKG-INFO0000644000175100001710000000110700000000000020217 0ustar00runnerdockerMetadata-Version: 2.1 Name: extinction Version: 0.4.6 Summary: Fast interstellar dust extinction laws Home-page: http://github.com/kbarbary/extinction Author: Kyle Barbary Author-email: kylebarbary@gmail.com License: MIT Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Programming Language :: Python :: 3 Classifier: License :: OSI Approved :: MIT License Classifier: Topic :: Scientific/Engineering :: Astronomy Classifier: Intended Audience :: Science/Research Requires-Python: >=3.5 License-File: LICENSE documentation: http://extinction.readthedocs.io ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1630712495.0 extinction-0.4.6/extinction.egg-info/SOURCES.txt0000644000175100001710000000043100000000000021005 0ustar00runnerdockerLICENSE MANIFEST.in README.md extinction.pyx pyproject.toml setup.py extern/bs.c extern/bs.h extern/bsplines.pxi extinction.egg-info/PKG-INFO extinction.egg-info/SOURCES.txt extinction.egg-info/dependency_links.txt extinction.egg-info/requires.txt extinction.egg-info/top_level.txt././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1630712495.0 extinction-0.4.6/extinction.egg-info/dependency_links.txt0000644000175100001710000000000100000000000023171 0ustar00runnerdocker ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1630712495.0 extinction-0.4.6/extinction.egg-info/requires.txt0000644000175100001710000000001600000000000021520 0ustar00runnerdockernumpy>=1.13.3 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1630712495.0 extinction-0.4.6/extinction.egg-info/top_level.txt0000644000175100001710000000001300000000000021647 0ustar00runnerdockerextinction ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1630712478.0 extinction-0.4.6/extinction.pyx0000644000175100001710000005703600000000000016206 0ustar00runnerdocker#!python #cython: boundscheck=False, wraparound=False, initializedcheck=False, cdivision=True """Interstellar dust extinction functions.""" import numpy as np cimport numpy as np __version__ = "0.4.6" __all__ = ['ccm89', 'odonnell94', 'Fitzpatrick99', 'fitzpatrick99', 'fm07', 'calzetti00', 'apply', 'remove'] # We use some C code for Cubic splines in the Fitzpatrick99 and fm07 functions. # There are two reasons for using this over something from scipy: # # First, Fitzpatrick99 specifies "natural" boundary conditions on the splines, # but in scipy.interpolate, this can only be achieved using the CubicSpline # class, which is new in scipy v0.18 (a bit too recent). # # Second, this C code is significantly faster than CubicSpline. include "extern/bsplines.pxi" # ------------------------------------------------------------------------------ # Utility functions for converting wavelength units cdef double aa_to_invum(double x): """Convert Angstroms to inverse microns""" return 1e4 / x cdef double noop(double x): return x ctypedef double (*scalar_func)(double) # ----------------------------------------------------------------------------- # Cardelli, Clayton & Mathis (1989) cdef inline void ccm89ab_ir_invum(double x, double *a, double *b): """ccm89 a, b parameters for 0.3 < x < 1.1 (infrared)""" cdef double y y = x**1.61 a[0] = 0.574 * y b[0] = -0.527 * y cdef inline void ccm89ab_opt_invum(double x, double *a, double *b): """ccm89 a, b parameters for 1.1 < x < 3.3 (optical)""" cdef double y y = x - 1.82 a[0] = ((((((0.329990*y - 0.77530)*y + 0.01979)*y + 0.72085)*y - 0.02427)*y - 0.50447)*y + 0.17699)*y + 1.0 b[0] = ((((((-2.09002*y + 5.30260)*y - 0.62251)*y - 5.38434)*y + 1.07233)*y + 2.28305)*y + 1.41338)*y cdef inline void ccm89ab_uv_invum(double x, double *a, double *b): """ccm89 a, b parameters for 3.3 < x < 8.0 (ultraviolet)""" cdef double y, y2, y3 y = x - 4.67 a[0] = 1.752 - 0.316*x - (0.104 / (y*y + 0.341)) y = x - 4.62 b[0] = -3.090 + 1.825*x + (1.206 / (y*y + 0.263)) if x > 5.9: y = x - 5.9 y2 = y * y y3 = y2 * y a[0] += -0.04473*y2 - 0.009779*y3 b[0] += 0.2130*y2 + 0.1207*y3 cdef inline void ccm89ab_fuv_invum(double x, double *a, double *b): """ccm89 a, b parameters for 8 < x < 11 (far-UV)""" cdef double y, y2, y3 y = x - 8. y2 = y * y y3 = y2 * y a[0] = -0.070*y3 + 0.137*y2 - 0.628*y - 1.073 b[0] = 0.374*y3 - 0.420*y2 + 4.257*y + 13.670 cdef inline void ccm89ab_invum(double x, double *a, double *b): """ccm89 a, b parameters for 0.3 < x < 11 in microns^-1""" if x < 1.1: ccm89ab_ir_invum(x, a, b) elif x < 3.3: ccm89ab_opt_invum(x, a, b) elif x < 8.: ccm89ab_uv_invum(x, a, b) else: ccm89ab_fuv_invum(x, a, b) def ccm89(double[:] wave, double a_v, double r_v, unit='aa', np.ndarray out=None): """ccm89(wave, a_v, r_v, unit='aa', out=None) Cardelli, Clayton & Mathis (1989) extinction function. The parameters given in the original paper [1]_ are used. The claimed validity is 1250 Angstroms to 3.3 microns. Parameters ---------- wave : numpy.ndarray (1-d) Wavelengths or wavenumbers. a_v : float Scaling parameter, A_V: extinction in magnitudes at characteristic V band wavelength. r_v : float Ratio of total to selective extinction, A_V / E(B-V). unit : {'aa', 'invum'}, optional Unit of wave: 'aa' (Angstroms) or 'invum' (inverse microns). out : np.ndarray, optional If specified, store output values in this array. Returns ------- Extinction in magnitudes at each input wavelength. Notes ----- In Cardelli, Clayton & Mathis (1989) the mean R_V-dependent extinction law, is parameterized as .. math:: = a(x) + b(x) / R_V where the coefficients a(x) and b(x) are functions of wavelength. At a wavelength of approximately 5494.5 angstroms (a characteristic wavelength for the V band), a(x) = 1 and b(x) = 0, so that A(5494.5 angstroms) = A_V. This function returns .. math:: A(\lambda) = A_V (a(x) + b(x) / R_V) References ---------- .. [1] Cardelli, J. A., Clayton, G. C., & Mathis, J. S. 1989, ApJ, 345, 245 """ cdef: size_t i size_t n = wave.shape[0] double a = 0.0 double b = 0.0 if out is None: out = np.empty(n, dtype=np.float64) else: assert out.shape == wave.shape assert out.dtype == np.float64 cdef scalar_func convert_wave if unit == 'aa': convert_wave = &aa_to_invum elif unit == 'invum': convert_wave = &noop else: raise ValueError("unrecognized unit") cdef double[:] out_view = out for i in range(n): ccm89ab_invum(convert_wave(wave[i]), &a, &b) out_view[i] = a_v * (a + b / r_v) return out # ----------------------------------------------------------------------------- # O'Donnell (1994) cdef inline void od94ab_opt_invum(double x, double *a, double *b): """od94 a, b parameters for 1.1 < x < 3.3 (optical)""" cdef double y y = x - 1.82 a[0] = (((((((-0.505*y + 1.647)*y - 0.827)*y - 1.718)*y + 1.137)*y + 0.701)*y - 0.609)*y + 0.104)*y + 1.0 b[0] = (((((((3.347*y - 10.805)*y + 5.491)*y + 11.102)*y - 7.985)*y - 3.989)*y + 2.908)*y + 1.952)*y cdef inline void od94ab_invum(double x, double *a, double *b): """od94 a, b parameters for 0.3 < x < 11 in microns^-1""" if x < 1.1: ccm89ab_ir_invum(x, a, b) elif x < 3.3: od94ab_opt_invum(x, a, b) elif x < 8.: ccm89ab_uv_invum(x, a, b) else: ccm89ab_fuv_invum(x, a, b) def odonnell94(double[:] wave, double a_v, double r_v, unit='aa', np.ndarray out=None): """odonnell94(wave, a_v, r_v, unit='aa', out=None) O'Donnell (1994) extinction function. Like Cardelli, Clayton, & Mathis (1989) [1]_ but using the O'Donnell (1994) [2]_ optical coefficients between 3030 A and 9091 A. Parameters ---------- wave : numpy.ndarray (1-d) Wavelengths or wavenumbers. a_v : float Scaling parameter, A_V: extinction in magnitudes at characteristic V band wavelength. r_v : float Ratio of total to selective extinction, A_V / E(B-V). unit : {'aa', 'invum'}, optional Unit of wave: 'aa' (Angstroms) or 'invum' (inverse microns). out : np.ndarray, optional If specified, store output values in this array. Returns ------- Extinction in magnitudes at each input wavelength. Notes ----- This function matches the Goddard IDL astrolib routine CCM_UNRED. From the documentation for that routine: 1. The CCM curve shows good agreement with the Savage & Mathis (1979) [3]_ ultraviolet curve shortward of 1400 A, but is probably preferable between 1200 and 1400 A. 2. Curve is extrapolated between 912 and 1000 A as suggested by Longo et al. (1989) [4]_ 3. Valencic et al. (2004) [5]_ revise the ultraviolet CCM curve (3.3 -- 8.0 um^-1). But since their revised curve does not connect smoothly with longer and shorter wavelengths, it is not included here. References ---------- .. [1] Cardelli, J. A., Clayton, G. C., & Mathis, J. S. 1989, ApJ, 345, 245 .. [2] O'Donnell, J. E. 1994, ApJ, 422, 158O .. [3] Savage & Mathis 1979, ARA&A, 17, 73 .. [4] Longo et al. 1989, ApJ, 339,474 .. [5] Valencic et al. 2004, ApJ, 616, 912 """ cdef: size_t i size_t n = wave.shape[0] double a = 0.0 double b = 0.0 if out is None: out = np.empty(n, dtype=np.float64) else: assert out.shape == wave.shape assert out.dtype == np.float64 cdef scalar_func convert_wave if unit == 'aa': convert_wave = &aa_to_invum elif unit == 'invum': convert_wave = &noop else: raise ValueError("unrecognized unit") cdef double[:] out_view = out for i in range(n): od94ab_invum(convert_wave(wave[i]), &a, &b) out_view[i] = a_v * (a + b / r_v) return out # ----------------------------------------------------------------------------- # Fitzpatrick (1999) DEF F99_X0 = 4.596 DEF F99_GAMMA = 0.99 DEF F99_C3 = 3.23 DEF F99_C4 = 0.41 DEF F99_C5 = 5.9 DEF F99_X02 = F99_X0 * F99_X0 DEF F99_GAMMA2 = F99_GAMMA * F99_GAMMA cdef inline double f99_uv_invum(double x, double a_v, double r_v): """f99 extinction for x < 1e4/2700 microns^-1""" cdef double c1, c2, d, x2, y, y2, rv2, k c2 = -0.824 + 4.717 / r_v c1 = 2.030 - 3.007 * c2 x2 = x * x y = x2 - F99_X02 d = x2 / (y * y + x2 * F99_GAMMA2) k = c1 + c2 * x + F99_C3 * d if x >= F99_C5: y = x - F99_C5 y2 = y * y k += F99_C4 * (0.5392 * y2 + 0.05644 * y2 * y) return a_v * (1. + k / r_v) cdef double *_F99_XKNOTS = [0.0, 1.e4 / 26500., 1.e4 / 12200., 1.e4 / 6000., 1.e4 / 5470., 1.e4 / 4670., 1.e4 / 4110., 1.e4 / 2700., 1.e4 / 2600.] cdef void _f99_kknots(double *xknots, double r_v, double *out): """Fill knot k values in Fitzpatrick 99 for given R_V. xknots and out should be arrays of length 9.""" cdef double c1, c2, d, x, x2, y, rv2 cdef int i c2 = -0.824 + 4.717 / r_v c1 = 2.030 - 3.007 * c2 rv2 = r_v * r_v out[0] = -r_v out[1] = 0.26469 * r_v/3.1 - r_v out[2] = 0.82925 * r_v/3.1 - r_v out[3] = -0.422809 + 1.00270*r_v + 2.13572e-04*rv2 - r_v out[4] = -5.13540e-02 + 1.00216 * r_v - 7.35778e-05*rv2 - r_v out[5] = 0.700127 + 1.00184*r_v - 3.32598e-05*rv2 - r_v out[6] = (1.19456 + 1.01707*r_v - 5.46959e-03*rv2 + 7.97809e-04 * rv2 * r_v - 4.45636e-05 * rv2*rv2 - r_v) for i in range(7,9): x2 = xknots[i] * xknots[i] y = (x2 - F99_X02) d = x2 / (y * y + x2 * F99_GAMMA2) out[i] = c1 + c2*xknots[i] + F99_C3 * d cdef class Fitzpatrick99(object): """Fitzpatrick (1999) dust extinction function for arbitrary R_V. An instance of this class is a callable that can be used as ``f(wave, a_v)`` where ``wave`` is a 1-d array of wavelengths and ``a_v`` is a scalar value. Parameters ---------- r_v : float, optional R_V value. Default is 3.1. Examples -------- Create a callable that gives the extinction law for a given ``r_v`` and use it: >>> f = Fitzpatrick99(3.1) >>> f(np.array([3000., 4000.]), 1.) array([ 1.79939955, 1.42338583]) """ cdef bs_spline1d *spline # pointer to c struct cdef readonly double r_v def __cinit__(self, double r_v=3.1): self.r_v = r_v cdef double *kknots = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] _f99_kknots(_F99_XKNOTS, r_v, kknots) cdef bs_bcs bcs cdef bs_exts exts cdef bs_errorcode code bcs.left.type = BS_DERIV2 bcs.left.value = 0.0 bcs.right.type = BS_DERIV2 bcs.right.value = 0.0 exts.left.type = BS_VALUE exts.left.value = 0.0 exts.right.type = BS_VALUE exts.right.value = 0.0 code = bs_spline1d_create(bs_array(_F99_XKNOTS, 9, 1), bs_array(kknots, 9, 1), bcs, exts, &self.spline) assert_ok(code) def __call__(self, np.ndarray wave not None, double a_v, unit='aa'): cdef double[:] wave_view, out_view cdef double r_v = self.r_v cdef double ebv = a_v / r_v cdef size_t i cdef size_t n cdef bs_errorcode code # translate `wave` to inverse microns if unit == 'invum': pass elif unit == 'aa': wave = 1e4 / wave else: raise ValueError("unrecognized unit") n = wave.shape[0] out = np.empty(n, dtype=np.float64) wave_view = wave out_view = out # Optical/IR spline: evaluate at all wavelengths; we will overwrite # the UV points afterwards. These are outside the spline range, so # they don't actually take too much time to evaluate. # (outview actually holds "k" after this call; see below) code = bs_spline1d_eval(self.spline, to_bs_array(wave_view), to_bs_array(out_view)) assert_ok(code) # Analytic function in the UV (< 2700 Angstroms). for i in range(n): # for optical/IR, `out` is actually k, but we wanted # a_v/r_v * (k+r_v), so we adjust here. if wave_view[i] < 3.7037037037037037: # 1e4 / 2700 out_view[i] = ebv * (out_view[i] + r_v) # for UV, we overwrite the array with the UV function value. else: out_view[i] = f99_uv_invum(wave_view[i], a_v, r_v) return out def __dealloc__(self): bs_spline1d_free(self.spline) # functional interface for Fitzpatrick (1999) with R_V = 3.1 _fitzpatrick99_fixed = Fitzpatrick99(3.1) def fitzpatrick99(wave, a_v, r_v=3.1, unit='aa'): """fitzpatrick99(wave, a_v, r_v=3.1, unit='aa') Fitzpatrick (1999) dust extinction function. Fitzpatrick (1999) [1]_ model which relies on the parametrization of Fitzpatrick & Massa (1990) [2]_ in the UV (below 2700 A) and spline fitting in the optical and IR. This function is defined from 910 A to 6 microns, but note the claimed validity goes down only to 1150 A. The optical spline points are not taken from F99 Table 4, but rather updated versions from E. Fitzpatrick (this matches the Goddard IDL astrolib routine FM_UNRED). Parameters ---------- wave : numpy.ndarray (1-d) Input wavelengths or wavenumbers (see units). a_v : float Total V-band extinction in magnitudes. r_v : float Ratio of total to selective extinction, A_V / E(B-V). unit : {'aa', 'invum'}, optional Wavelength units: Angstroms or inverse microns. Returns ------- Extinction in magnitudes at each input wavelength. References ---------- .. [1] Fitzpatrick, E. L. 1999, PASP, 111, 63 .. [2] Fitzpatrick, E. L. & Massa, D. 1990, ApJS, 72, 163 """ if r_v == 3.1: return _fitzpatrick99_fixed(wave, a_v, unit=unit) else: return Fitzpatrick99(r_v)(wave, a_v, unit=unit) # ----------------------------------------------------------------------------- # Fitzpatrick & Massa 2007 DEF FM07_X0 = 4.592 DEF FM07_GAMMA = 0.922 DEF FM07_C1 = -0.175 DEF FM07_C2 = 0.807 DEF FM07_C3 = 2.991 DEF FM07_C4 = 0.319 DEF FM07_C5 = 6.097 DEF FM07_X02 = FM07_X0 * FM07_X0 DEF FM07_GAMMA2 = FM07_GAMMA * FM07_GAMMA DEF FM07_R_V = 3.1 # Fixed for the time being (used in fm07kknots) # Used for wave < 2700. cdef inline double fm07_uv_invum(double x, double a_v): """fm07 for x < 1e4/2700 microns^-1 (ultraviolet)""" cdef double d, x2, y, k x2 = x * x y = x2 - FM07_X02 d = x2 / (y*y + x2 * FM07_GAMMA2) k = FM07_C1 + FM07_C2 * x + FM07_C3 * d if x > FM07_C5: y = x - FM07_C5 k += FM07_C4 * y * y return a_v * (1. + k / FM07_R_V) cdef void _fm07_kknots(double *xknots, double *out): """Return k knots given xknots for fm07. (Could be a static array)""" cdef double d cdef int i for i in range(0, 5): out[i] = (-0.83 + 0.63*FM07_R_V) * xknots[i]**1.84 - FM07_R_V out[5] = 0.0 out[6] = 1.322 out[7] = 2.055 for i in range(8, 10): d = xknots[i]**2 / ((xknots[i]**2 - FM07_X02)**2 + xknots[i]**2 * FM07_GAMMA2) out[i] = FM07_C1 + FM07_C2 * xknots[i] + FM07_C3 * d cdef double *_FM07_XKNOTS = [0., 0.25, 0.50, 0.75, 1., 1.e4 / 5530., 1.e4 / 4000., 1.e4 / 3300., 1.e4 / 2700., 1.e4 / 2600.] cdef double *_FM07_KKNOTS = [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.] _fm07_kknots(_FM07_XKNOTS, _FM07_KKNOTS) def fm07(np.ndarray wave not None, double a_v, unit='aa'): """fm07(wave, a_v, unit='aa') Fitzpatrick & Massa (2007) extinction model for R_V = 3.1. The Fitzpatrick & Massa (2007) [1]_ model, which has a slightly different functional form from that of Fitzpatrick (1999) [3]_ (`extinction_f99`). Fitzpatrick & Massa (2007) claim it is preferable, although it is unclear if signficantly so (Gordon et al. 2009 [2]_). Defined from 910 A to 6 microns. Parameters ---------- wave : numpy.ndarray (1-d) Wavelengths or wavenumbers. a_v : float Scaling parameter, A_V: extinction in magnitudes at characteristic V band wavelength. unit : {'aa', 'invum'}, optional Unit of wave: 'aa' (Angstroms) or 'invum' (inverse microns). References ---------- .. [1] Fitzpatrick, E. L. & Massa, D. 2007, ApJ, 663, 320 .. [2] Gordon, K. D., Cartledge, S., & Clayton, G. C. 2009, ApJ, 705, 1320 .. [3] Fitzpatrick, E. L. 1999, PASP, 111, 63 """ cdef double[:] wave_view, out_view cdef double ebv = a_v / FM07_R_V cdef size_t i cdef size_t n # translate `wave` to inverse microns if unit == 'invum': pass elif unit == 'aa': wave = 1e4 / wave else: raise ValueError("unrecognized unit") # create the spline (we do this here rather than globally so we can # deallocate it.) cdef bs_spline1d* spline cdef bs_bcs bcs cdef bs_exts exts cdef bs_errorcode code bcs.left.type = BS_DERIV2 bcs.left.value = 0.0 bcs.right.type = BS_DERIV2 bcs.right.value = 0.0 exts.left.type = BS_VALUE exts.left.value = 0.0 exts.right.type = BS_VALUE exts.right.value = 0.0 code = bs_spline1d_create(bs_array(_FM07_XKNOTS, 10, 1), bs_array(_FM07_KKNOTS, 10, 1), bcs, exts, &spline) assert_ok(code) n = wave.shape[0] out = np.empty(n, dtype=np.float64) wave_view = wave out_view = out # Optical/IR spline: evaluate at all wavelengths; we will overwrite # the UV points afterwards. These are outside the spline range, so # they don't actually take too much time to evaluate. # (outview actually holds "k" after this call; see below) code = bs_spline1d_eval(spline, to_bs_array(wave_view), to_bs_array(out_view)) assert_ok(code) bs_spline1d_free(spline) # Analytic function in the UV (< 2700 Angstroms). for i in range(n): # for optical/IR, `out` is actually k, but we wanted # a_v/r_v * (k+r_v), so we adjust here. if wave_view[i] < 3.7037037037037037: # 1e4 / 2700 out_view[i] = ebv * (out_view[i] + FM07_R_V) # for UV, we overwrite the array with the UV function value. else: out_view[i] = fm07_uv_invum(wave_view[i], a_v) return out # ----------------------------------------------------------------------------- # Calzetti 2000 # http://adsabs.harvard.edu/abs/2000ApJ...533..682C cdef inline double calzetti00k_uv_invum(double x): """calzetti00 `k` for 0.12 microns < wave < 0.63 microns (UV/optical), x in microns^-1""" return 2.659 * (((0.011*x - 0.198)*x + 1.509)*x - 2.156) cdef inline double calzetti00k_ir_invum(double x): """calzetti00 `k` for 0.63 microns < wave < 2.2 microns (optical/IR), x in microns^-1""" return 2.659 * (1.040*x - 1.857) cdef inline double calzetti00_invum(double x, double r_v): cdef double k if x > 1.5873015873015872: # 1. / 0.63 k = calzetti00k_uv_invum(x) else: k = calzetti00k_ir_invum(x) return 1.0 + k / r_v def calzetti00(double[:] wave, double a_v, double r_v, unit='aa', np.ndarray out=None): """calzetti00(wave, a_v, r_v, unit='aa', out=None) Calzetti (2000) extinction function. Calzetti et al. (2000, ApJ 533, 682) developed a recipe for dereddening the spectra of galaxies where massive stars dominate the radiation output, valid between 0.12 to 2.2 microns. They estimate :math:`R_V = 4.05 \pm 0.80` from optical-IR observations of 4 starburst galaxies. Parameters ---------- wave : numpy.ndarray (1-d) Wavelengths or wavenumbers. a_v : float Scaling parameter, A_V: extinction in magnitudes at characteristic V band wavelength. r_v : float Ratio of total to selective extinction, A_V / E(B-V). unit : {'aa', 'invum'}, optional Unit of wave: 'aa' (Angstroms) or 'invum' (inverse microns). out : np.ndarray, optional If specified, store output values in this array. Returns ------- Extinction in magnitudes at each input wavelength. """ cdef: size_t i size_t n = wave.shape[0] double a = 0.0 double b = 0.0 if out is None: out = np.empty(n, dtype=np.float64) else: assert out.shape == wave.shape assert out.dtype == np.float64 cdef scalar_func convert_wave if unit == 'aa': convert_wave = &aa_to_invum elif unit == 'invum': convert_wave = &noop else: raise ValueError("unrecognized unit") cdef double[:] out_view = out for i in range(n): out_view[i] = a_v * calzetti00_invum(convert_wave(wave[i]), r_v) return out # ------------------------------------------------------------------------------ # convenience functions for applying/removing extinction to/from flux # values, optionally in-place. It turns out that pure Python is about as # fast as C here. def apply(extinction, flux, inplace=False): """apply(extinction, flux, inplace=False) Apply extinction to flux values. This is a convenience function to perform "reddening" of flux values. It simply performs ``flux * 10**(-0.4 * extinction)``: flux is decreased (for positive extinction values). Parameters ---------- extinction : numpy.ndarray (1-d) Extinction in magnitudes. (Positive extinction values decrease flux values.) flux : numpy.ndarray Flux values. inplace : bool, optional Whether to perform the operation in-place on the flux array. If True, the return value is a reference to the input flux array. Returns ------- new_flux : numpy.ndarray (1-d) Flux values with extinction applied. """ trans = 10.**(-0.4 * extinction) if inplace: flux *= trans return flux else: return flux * trans # ------------------------------------------------------------------------------ # convenience function for removing extinction from flux values, optionally # in-place. (It turns out that this isn't really faster than just doing it # in pure python...) def remove(extinction, flux, inplace=False): """remove(extinction, flux, inplace=False) Remove extinction from flux values. This is a convenience function to "deredden" fluxes. It simply performs ``flux * 10**(0.4 * extinction)``: flux is increased (for positive extinction values). Parameters ---------- extinction : numpy.ndarray (1-d) Extinction in magnitudes. flux : numpy.ndarray Flux values. inplace : bool, optional Whether to perform the operation in-place on the flux array. If True, the return value is a reference to the input flux array. Returns ------- new_flux : numpy.ndarray (1-d) Flux values with extinction removed. """ trans = 10.**(0.4 * extinction) if inplace: flux *= trans return flux else: return flux * trans ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1630712478.0 extinction-0.4.6/pyproject.toml0000644000175100001710000000023100000000000016155 0ustar00runnerdocker[build-system] requires = [ "wheel", "setuptools", "Cython>=0.29.2", "oldest-supported-numpy", ] build-backend = 'setuptools.build_meta' ././@PaxHeader0000000000000000000000000000003400000000000010212 xustar0028 mtime=1630712495.4229062 extinction-0.4.6/setup.cfg0000644000175100001710000000004600000000000015066 0ustar00runnerdocker[egg_info] tag_build = tag_date = 0 ././@PaxHeader0000000000000000000000000000002600000000000010213 xustar0022 mtime=1630712478.0 extinction-0.4.6/setup.py0000755000175100001710000000254100000000000014764 0ustar00runnerdocker#!/usr/bin/env python import os import re import sys import numpy from setuptools import setup from setuptools.extension import Extension classifiers = [ "Development Status :: 4 - Beta", "Programming Language :: Python :: 3", "License :: OSI Approved :: MIT License", "Topic :: Scientific/Engineering :: Astronomy", "Intended Audience :: Science/Research", ] # Synchronize version from code. fname = "extinction.pyx" version = re.findall(r"__version__ = \"(.*?)\"", open(fname).read())[0] # Build Cython extension source_files = [fname, os.path.join("extern", "bs.c")] depends_files = [ os.path.join("extern", "bs.h"), os.path.join("extern", "bsplines.pxi") ] include_dirs = [numpy.get_include(), "extern"] extensions = [ Extension( "extinction", source_files, include_dirs=include_dirs, depends=depends_files, extra_compile_args=["-std=c99"], ) ] setup( name="extinction", version=version, description="Fast interstellar dust extinction laws", long_description="documentation: http://extinction.readthedocs.io", license="MIT", classifiers=classifiers, url="http://github.com/kbarbary/extinction", author="Kyle Barbary", author_email="kylebarbary@gmail.com", ext_modules=extensions, install_requires=["numpy>=1.13.3"], python_requires=">=3.5", )