Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@ on:
push:
branches:
- main
- develop

jobs:
build:
name: Build and Test
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [macos-latest, ubuntu-latest, windows-latest]
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
os: [macos-13, macos-14, ubuntu-20.04, ubuntu-22.04, windows-2019, windows-2022]
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
include:
- os: macos-latest
env: CC="clang" CXX="clang++"
Expand All @@ -32,7 +33,7 @@ jobs:
python -m pip install --upgrade pip
python -m pip install --upgrade setuptools wheel
python -m pip install --upgrade flake8 pytest pytest-cov coverage spectres
python -m pip install -r requirements.txt
python -m pip install --upgrade -r requirements.txt
python setup.py build
python -m pip install -e .
- name: Test with pytest
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/cibuild-pypi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-20.04, windows-2019, macos-11]
os: [ubuntu-latest, windows-latest, macos-13, macos-14]

steps:
- uses: actions/checkout@v3

- name: Build wheels
uses: pypa/cibuildwheel@v2.12.3
uses: pypa/cibuildwheel@v2.19.1

- uses: actions/upload-artifact@v3
with:
Expand Down
2 changes: 1 addition & 1 deletion .zenodo.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"upload_type": "software",
"creators": [
{
"affiliation": "Tel Aviv University",
"affiliation": "University of Edinburgh",
"name": "Lam, Marco C",
"orcid": "0000-0002-9347-2298"
}
Expand Down
4 changes: 2 additions & 2 deletions CITATION.cff
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ type: software
authors:
- given-names: Marco C
family-names: Lam
email: lam@mail.tau.ac.il
affiliation: Tel Aviv University
email: mlam@roe.ac.uk
affiliation: University of Edinburgh
orcid: 'https://orcid.org/0000-0002-9347-2298'
identifiers:
- type: doi
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ This is a Python package written with C extension to provide significant perform

![alt text](https://github.com/cylammarco/SpectResC/blob/main/speed_test/speed_test.png?raw=true)

We keep the implementation as close to SpectRes as possible. As of SpectRes v2.2.0, we do not see discrepant results between using SpectRes and SpectReC.
We keep the implementation as close to SpectRes as possible. As of SpectRes v2.2.0, we do not see discrepant results between using SpectRes and SpectResC.

## Installation

Expand Down
5 changes: 3 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
setup(
name="spectresc",
maintainer="Marco C Lam",
maintainer_email="lam@mail.tau.ac.il",
version="1.0.2",
maintainer_email="mlam@roe.ac.uk",
version="1.0.3",
install_requires=[
"numpy",
],
Expand All @@ -27,6 +27,7 @@
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Operating System :: Microsoft :: Windows",
"Operating System :: Unix",
"Operating System :: MacOS",
Expand Down
171 changes: 52 additions & 119 deletions src/spectresc/spectres.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
Expand All @@ -22,108 +24,49 @@ double *make_bins(double *wavs, int wavs_len)
// Define the spectres function
static PyObject *spectres(PyObject *self, PyObject *args, PyObject *kwargs)
{

PyObject *new_wavs_obj, *spec_wavs_obj, *spec_fluxes_obj, *spec_errs_obj = NULL;
double fill = NAN;
int verbose = 1;

static char *kwlist[] = {"new_wavs", "spec_wavs", "spec_fluxes", "spec_errs", "fill", "verbose", NULL};

/* Parse the input tuple */
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OOO|Odp:spectres", kwlist, &new_wavs_obj, &spec_wavs_obj, &spec_fluxes_obj, &spec_errs_obj,
&fill, &verbose))
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "OOO|Odp:spectres", kwlist, &new_wavs_obj, &spec_wavs_obj, &spec_fluxes_obj, &spec_errs_obj, &fill, &verbose))
{
return NULL;
}

// Convert input object to NumPy array
PyArrayObject *_new_wavs_array = (PyArrayObject *)PyArray_FROM_OTF(new_wavs_obj, NPY_DOUBLE, NPY_ARRAY_IN_ARRAY);
PyArrayObject *_spec_wavs_array = (PyArrayObject *)PyArray_FROM_OTF(spec_wavs_obj, NPY_DOUBLE, NPY_ARRAY_IN_ARRAY);
PyArrayObject *_spec_fluxes_array = (PyArrayObject *)PyArray_FROM_OTF(spec_fluxes_obj, NPY_DOUBLE, NPY_ARRAY_IN_ARRAY);

// Check if input array is contiguous
npy_intp *new_wavs_strides = PyArray_STRIDES(_new_wavs_array);
npy_intp *spec_wavs_strides = PyArray_STRIDES(_spec_wavs_array);
npy_intp *spec_fluxes_strides = PyArray_STRIDES(_spec_fluxes_array);
int new_wavs_is_contiguous = (new_wavs_strides[PyArray_NDIM(_new_wavs_array) - 1] == sizeof(double));
int spec_wavs_is_contiguous = (spec_wavs_strides[PyArray_NDIM(_spec_wavs_array) - 1] == sizeof(double));
int spec_fluxes_is_contiguous = (spec_fluxes_strides[PyArray_NDIM(_spec_fluxes_array) - 1] == sizeof(double));

// Create new array if input array is not contiguous
PyArrayObject *new_wavs_array;
PyArrayObject *spec_wavs_array;
PyArrayObject *spec_fluxes_array;

// handle the new_wavs_array
if (!new_wavs_is_contiguous)
{
npy_intp *new_wavs_shape = PyArray_SHAPE(_new_wavs_array);
new_wavs_array = (PyArrayObject *)PyArray_SimpleNew(PyArray_NDIM(_new_wavs_array), new_wavs_shape, NPY_DOUBLE);
if (new_wavs_array == NULL)
{
Py_DECREF(_new_wavs_array);
return NULL;
}
PyArrayObject *new_wavs_array = (PyArrayObject *)PyArray_FROM_OTF(new_wavs_obj, NPY_DOUBLE, NPY_ARRAY_IN_ARRAY);
PyArrayObject *spec_wavs_array = (PyArrayObject *)PyArray_FROM_OTF(spec_wavs_obj, NPY_DOUBLE, NPY_ARRAY_IN_ARRAY);
PyArrayObject *spec_fluxes_array = (PyArrayObject *)PyArray_FROM_OTF(spec_fluxes_obj, NPY_DOUBLE, NPY_ARRAY_IN_ARRAY);

// Copy data from input array into output array
char *src = (char *)PyArray_DATA(_new_wavs_array);
char *dst = (char *)PyArray_DATA(new_wavs_array);
npy_intp size = PyArray_SIZE(_new_wavs_array) * sizeof(short);
memcpy(dst, src, size);
}
else
if (!new_wavs_array || !spec_wavs_array || !spec_fluxes_array)
{
// Use input array directly
Py_INCREF(_new_wavs_array);
new_wavs_array = _new_wavs_array;
Py_XDECREF(new_wavs_array);
Py_XDECREF(spec_wavs_array);
Py_XDECREF(spec_fluxes_array);
return NULL;
}

// handle the spec_wavs_array
if (!spec_wavs_is_contiguous)
// Create new arrays if input arrays are not contiguous
if (!PyArray_ISCONTIGUOUS(new_wavs_array))
{
npy_intp *spec_wavs_shape = PyArray_SHAPE(_spec_wavs_array);
spec_wavs_array = (PyArrayObject *)PyArray_SimpleNew(PyArray_NDIM(_spec_wavs_array), spec_wavs_shape, NPY_DOUBLE);
if (spec_wavs_array == NULL)
{
Py_DECREF(_spec_wavs_array);
new_wavs_array = (PyArrayObject *)PyArray_Cast(new_wavs_array, NPY_DOUBLE);
if (!new_wavs_array)
return NULL;
}

// Copy data from input array into output array
char *src = (char *)PyArray_DATA(_spec_wavs_array);
char *dst = (char *)PyArray_DATA(spec_wavs_array);
npy_intp size = PyArray_SIZE(_spec_wavs_array) * sizeof(short);
memcpy(dst, src, size);
}
else
if (!PyArray_ISCONTIGUOUS(spec_wavs_array))
{
// Use input array directly
Py_INCREF(_spec_wavs_array);
spec_wavs_array = _spec_wavs_array;
}

// handle the spec_fluxes_array
if (!spec_fluxes_is_contiguous)
{
npy_intp *spec_fluxes_shape = PyArray_SHAPE(_spec_fluxes_array);
spec_fluxes_array = (PyArrayObject *)PyArray_SimpleNew(PyArray_NDIM(_spec_fluxes_array), spec_fluxes_shape, NPY_DOUBLE);
if (spec_fluxes_array == NULL)
{
Py_DECREF(_spec_fluxes_array);
spec_wavs_array = (PyArrayObject *)PyArray_Cast(spec_wavs_array, NPY_DOUBLE);
if (!spec_wavs_array)
return NULL;
}

// Copy data from input array into output array
char *src = (char *)PyArray_DATA(_spec_fluxes_array);
char *dst = (char *)PyArray_DATA(spec_fluxes_array);
npy_intp size = PyArray_SIZE(_spec_fluxes_array) * sizeof(short);
memcpy(dst, src, size);
}
else
if (!PyArray_ISCONTIGUOUS(spec_fluxes_array))
{
// Use input array directly
Py_INCREF(_spec_fluxes_array);
spec_fluxes_array = _spec_fluxes_array;
spec_fluxes_array = (PyArrayObject *)PyArray_Cast(spec_fluxes_array, NPY_DOUBLE);
if (!spec_fluxes_array)
return NULL;
}

double *new_wavs = (double *)PyArray_DATA(new_wavs_array);
Expand All @@ -134,10 +77,10 @@ static PyObject *spectres(PyObject *self, PyObject *args, PyObject *kwargs)
int new_wavs_len = (int)PyArray_DIM(new_wavs_array, 0);
int spec_wavs_len = (int)PyArray_DIM(spec_wavs_array, 0);

double *spec_edges, *new_edges, *spec_widths;
spec_edges = make_bins(spec_wavs, spec_wavs_len);
new_edges = make_bins(new_wavs, new_wavs_len);
spec_widths = (double *)malloc(sizeof(double) * spec_wavs_len);
double *spec_edges = make_bins(spec_wavs, spec_wavs_len);
double *new_edges = make_bins(new_wavs, new_wavs_len);
double *spec_widths = (double *)malloc(sizeof(double) * spec_wavs_len);

for (int i = 0; i < spec_wavs_len; i++)
{
spec_widths[i] = spec_edges[i + 1] - spec_edges[i];
Expand All @@ -146,45 +89,28 @@ static PyObject *spectres(PyObject *self, PyObject *args, PyObject *kwargs)
// Create empty arrays for populating resampled flux and error
double *new_fluxes = (double *)malloc(sizeof(double) * new_wavs_len);

/* Get pointers to the data as C-types. */
PyObject *spec_errs_array = NULL;
PyArrayObject *spec_errs_array = NULL;
double *spec_errs = NULL;
double *new_errs = NULL;

if (spec_errs_obj != NULL)
{
PyArrayObject *_spec_errs_array = (PyArrayObject *)PyArray_FROM_OTF(spec_errs_obj, NPY_DOUBLE, NPY_ARRAY_IN_ARRAY);

// Check if input array is contiguous
npy_intp *spec_errs_strides = PyArray_STRIDES(_spec_errs_array);
int spec_errs_is_contiguous = (spec_errs_strides[PyArray_NDIM(_spec_errs_array) - 1] == sizeof(double));
spec_errs_array = (PyArrayObject *)PyArray_FROM_OTF(spec_errs_obj, NPY_DOUBLE, NPY_ARRAY_IN_ARRAY);

// Create new array if input array is not contiguous
PyArrayObject *spec_errs_array;
if (!spec_errs_is_contiguous)
if (!spec_errs_array || !PyArray_ISCONTIGUOUS(spec_errs_array))
{
npy_intp *spec_errs_shape = PyArray_SHAPE(_spec_errs_array);
spec_errs_array = (PyArrayObject *)PyArray_SimpleNew(PyArray_NDIM(_spec_errs_array), spec_errs_shape, NPY_DOUBLE);
if (spec_errs_array == NULL)
if (spec_errs_array)
spec_errs_array = (PyArrayObject *)PyArray_Cast(spec_errs_array, NPY_DOUBLE);
if (!spec_errs_array)
{
Py_DECREF(_spec_errs_array);
Py_XDECREF(new_wavs_array);
Py_XDECREF(spec_wavs_array);
Py_XDECREF(spec_fluxes_array);
return NULL;
}

// Copy data from input array into output array
char *src = (char *)PyArray_DATA(_spec_errs_array);
char *dst = (char *)PyArray_DATA(spec_errs_array);
npy_intp size = PyArray_SIZE(_spec_errs_array) * sizeof(short);
memcpy(dst, src, size);
}
else
{
// Use input array directly
Py_INCREF(_spec_errs_array);
spec_errs_array = _spec_errs_array;
}
spec_errs = (double *)PyArray_DATA(spec_errs_array);

spec_errs = (double *)PyArray_DATA(spec_errs_array);
new_errs = (double *)malloc(sizeof(double) * new_wavs_len);
}

Expand Down Expand Up @@ -272,27 +198,34 @@ static PyObject *spectres(PyObject *self, PyObject *args, PyObject *kwargs)
PyObject *new_fluxes_array = PyArray_SimpleNewFromData(1, dims, NPY_DOUBLE, new_fluxes);
PyArray_ENABLEFLAGS((PyArrayObject *)new_fluxes_array, NPY_ARRAY_OWNDATA);

// Set the base object for the arrays to NULL to indicate that the arrays are not owned by Python
// and reate a tuple to return the array(s)
if (spec_errs != NULL)
{
PyObject *new_errs_array = NULL;
PyObject *result_list = PyList_New(0);
PyList_Append(result_list, PyArray_Return(new_fluxes_array));
new_errs_array = PyArray_SimpleNewFromData(1, dims, NPY_DOUBLE, new_errs);
PyObject *new_errs_array = PyArray_SimpleNewFromData(1, dims, NPY_DOUBLE, new_errs);
PyArray_ENABLEFLAGS((PyArrayObject *)new_errs_array, NPY_ARRAY_OWNDATA);
PyList_Append(result_list, PyArray_Return(new_errs_array));
PyObject *result_list = PyList_New(0);
PyList_Append(result_list, new_fluxes_array);
PyList_Append(result_list, new_errs_array);

Py_XDECREF(new_wavs_array);
Py_XDECREF(spec_wavs_array);
Py_XDECREF(spec_fluxes_array);
Py_XDECREF(spec_errs_array);

return result_list;
}
else
{
Py_XDECREF(new_wavs_array);
Py_XDECREF(spec_wavs_array);
Py_XDECREF(spec_fluxes_array);

return new_fluxes_array;
}
}

// Define the module methods
static PyMethodDef SpectrescMethods[] = {
{"spectres", spectres, METH_VARARGS | METH_KEYWORDS, "Resample a spectrum onto a new wavelength grid."},
{"spectres", (PyCFunction)spectres, METH_VARARGS | METH_KEYWORDS, "Resample a spectrum onto a new wavelength grid."},
{NULL, NULL, 0, NULL}};

// Define the module structure
Expand All @@ -308,4 +241,4 @@ PyMODINIT_FUNC PyInit_spectresc(void)
{
import_array(); // Initialize NumPy
return PyModule_Create(&spectrescmodule);
}
}