4 #define UNUSED __attribute__ ((unused))
9 #ifndef HAVE_PYTHON3_EMBED
11 #include <lal/LALStdlib.h>
12 #include <lal/LALDict.h>
13 #include <lal/LALSimInspiral.h>
22 .name =
"ExternalPython",
25 .generate_td_waveform = NULL,
26 .generate_fd_waveform = NULL,
27 .generate_td_modes = NULL,
28 .generate_fd_modes = NULL,
35 #undef LAL_STRICT_DEFS_ENABLED
36 #include <lal/LALStddef.h>
38 #include <lal/LALStdlib.h>
39 #include <lal/LALDict.h>
40 #include <lal/LALSimInspiral.h>
44 #include <lal/Units.h>
45 #include <lal/LALString.h>
46 #include <lal/LALValue.h>
47 #include <lal/Sequence.h>
48 #include <lal/FrequencySeries.h>
49 #include <lal/TimeSeries.h>
50 #include <lal/LALSimSphHarmSeries.h>
55 #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
57 #pragma GCC diagnostic push
58 #pragma GCC diagnostic ignored "-Wcast-qual"
60 #include <numpy/arrayobject.h>
62 #pragma GCC diagnostic pop
66 static LALDict *create_waveform_parameter_units_dict(
void)
85 static int python_error(
void)
87 if (PyErr_Occurred()) {
99 struct add_to_dict_params {
102 LALDict *waveform_parameter_units;
106 static void add_to_dict(
char *key, LALValue *value,
void *thunk)
108 struct add_to_dict_params *add_to_dict_params = thunk;
109 LALDict *units = add_to_dict_params->waveform_parameter_units;
110 PyObject *quantity = add_to_dict_params->quantity;
111 PyObject *dict = add_to_dict_params->dict;
112 PyObject *
args = NULL;
113 PyObject *val = NULL;
128 if (quantity && units) {
129 const char *
unit =
"";
133 args = Py_BuildValue(
"Os", val,
unit);
136 val = PyObject_CallObject(quantity,
args);
158 static PyObject *PyDict_FromLALDict(LALDict *ldict, PyObject *quantity, LALDict *waveform_parameter_units)
160 struct add_to_dict_params add_to_dict_params;
166 add_to_dict_params.dict = dict;
167 add_to_dict_params.quantity = quantity;
168 add_to_dict_params.waveform_parameter_units = waveform_parameter_units;
190 static size_t PyArray_AsBytes(
void *buf,
size_t bufsz, PyArrayObject *arr)
194 nbytes = PyArray_NBYTES(arr);
197 memcpy(buf, PyArray_DATA(arr), bufsz);
203 #define DEFINE_PYARRAY_ASTYPE(LALTYPE, NPYTYPE) \
204 static size_t PyArray_As ## LALTYPE (LALTYPE *data, size_t length, PyObject *o) \
206 PyArrayObject *arr; \
208 XLAL_CHECK(o, XLAL_EFAULT); \
209 XLAL_CHECK(PyArray_Check(o), XLAL_EINVAL, "Python object is not a ndarray"); \
210 arr = (PyArrayObject *)PyArray_FROM_OF(o, NPY_ARRAY_IN_ARRAY); \
211 if (python_error()) \
212 XLAL_ERROR(XLAL_EFAILED); \
213 XLAL_CHECK(PyArray_TYPE(arr) == NPYTYPE, XLAL_ETYPE, "Ndarray has wrong dtype"); \
214 nbytes = PyArray_AsBytes(data, length * sizeof(*data), arr); \
215 if ((ssize_t)(nbytes) < 0) \
216 XLAL_ERROR(XLAL_EFUNC); \
217 return nbytes / sizeof(LALTYPE); \
230 DEFINE_PYARRAY_ASTYPE(
REAL8, NPY_DOUBLE)
232 DEFINE_PYARRAY_ASTYPE(
COMPLEX16, NPY_CDOUBLE)
237 #define DEFINE_PYARRAY_ASSEQTYPE(LALTYPE) \
238 static LALTYPE ## Sequence * PyArray_As ## LALTYPE ## Sequence (PyObject *o) \
240 LALTYPE ## Sequence *seq; \
242 XLAL_CHECK_NULL(o, XLAL_EFAULT); \
243 if ((ssize_t)(length = PyArray_As ## LALTYPE(NULL, 0, o)) < 0) \
244 XLAL_ERROR_NULL(XLAL_EFUNC); \
245 if ((seq = XLALCreate ## LALTYPE ## Sequence(length)) == NULL) \
246 XLAL_ERROR_NULL(XLAL_EFUNC); \
247 if (PyArray_As ## LALTYPE(seq->data, seq->length, o) != length) { \
248 XLALDestroy ## LALTYPE ## Sequence(seq); \
249 XLAL_ERROR_NULL(XLAL_ESIZE, "Failed to read ndarray"); \
264 DEFINE_PYARRAY_ASSEQTYPE(
REAL8)
271 static int AstropyTime_AsLIGOTimeGPS(
LIGOTimeGPS *lt, PyObject *at)
273 PyObject *gpstime = NULL;
274 PyObject *result = NULL;
281 gpstime = PyObject_CallMethod(at,
"to_value",
"ss",
"gps",
"decimal");
285 result = PyObject_CallMethod(gpstime,
"__divmod__",
"i", 1);
292 sec = PyFloat_AsDouble(PyTuple_GetItem(result, 0));
293 if (PyErr_Occurred())
295 nan = PyFloat_AsDouble(PyTuple_GetItem(result, 1));
296 if (PyErr_Occurred())
310 static double AstropyUnit_AsLALUnit(
LALUnit *lu, PyObject *au)
313 PyObject *scale = NULL;
314 PyObject *bases = NULL;
315 PyObject *powers = NULL;
322 si = PyObject_GetAttrString(au,
"si");
326 scale = PyObject_GetAttrString(si,
"scale");
329 scale_factor = PyFloat_AsDouble(scale);
330 if (PyErr_Occurred())
334 bases = PyObject_GetAttrString(si,
"bases");
337 powers = PyObject_GetAttrString(si,
"powers");
342 if (!PyList_Check(bases) || !PyList_Check(powers))
344 if (PyList_Size(bases) != PyList_Size(powers))
352 memset(lu, 0,
sizeof(*lu));
353 for (Py_ssize_t
i = 0;
i < PyList_Size(bases); ++
i) {
354 PyObject *base = PyList_GetItem(bases,
i);
355 PyObject *power = PyList_GetItem(powers,
i);
356 PyObject *intratio = NULL;
357 PyObject *basename = NULL;
361 intratio = PyObject_CallMethod(power,
"as_integer_ratio", NULL);
362 if (intratio == NULL || !PyTuple_Check(intratio)) {
363 Py_XDECREF(intratio);
366 if (!PyArg_ParseTuple(intratio,
"ii", &numerator, &denominator)) {
372 basename = PyObject_GetAttrString(base,
"name");
373 if (basename == NULL || !PyUnicode_Check(basename)) {
374 Py_XDECREF(basename);
378 if (PyUnicode_CompareWithASCIIString(basename,
"m") == 0) {
381 }
else if (PyUnicode_CompareWithASCIIString(basename,
"kg") == 0) {
384 }
else if (PyUnicode_CompareWithASCIIString(basename,
"s") == 0) {
387 }
else if (PyUnicode_CompareWithASCIIString(basename,
"A") == 0) {
390 }
else if (PyUnicode_CompareWithASCIIString(basename,
"K") == 0) {
393 }
else if (PyUnicode_CompareWithASCIIString(basename,
"strain") == 0) {
396 }
else if (PyUnicode_CompareWithASCIIString(basename,
"ct") == 0) {
399 }
else if (PyUnicode_CompareWithASCIIString(basename,
"rad") == 0) {
424 #define DEFINE_GWPY_TIMESERIES_TO_LAL_TIMESERIES(TYPE) \
425 static TYPE ## TimeSeries *GWpyTimeSeries_As ## TYPE ## TimeSeries(PyObject *gwpyser) \
427 TYPE ## TimeSeries *series; \
429 PyObject *name = NULL; \
430 PyObject *bytes = NULL; \
431 PyObject *epoch = NULL; \
432 PyObject *dt = NULL; \
433 PyObject *value = NULL; \
434 PyObject *units = NULL; \
435 PyObject *si = NULL; \
437 series = XLALCalloc(1, sizeof(*series)); \
438 XLAL_CHECK_NULL(series, XLAL_ENOMEM); \
442 name = PyObject_GetAttrString(gwpyser, "name"); \
443 XLAL_CHECK_FAIL(name, XLAL_EFAILED, "Could not get attribute .name"); \
444 XLAL_CHECK_FAIL(PyUnicode_Check(name), XLAL_EFAILED, "Attribute .name is not a string"); \
445 bytes = PyUnicode_AsASCIIString(name); \
446 XLAL_CHECK_FAIL(PyBytes_Check(bytes), XLAL_EFAILED, "Could not decode attribute .name as ASCII string"); \
448 XLALStringCopy(series->name, PyBytes_AsString(bytes), sizeof(series->name)); \
451 epoch = PyObject_GetAttrString(gwpyser, "epoch"); \
452 XLAL_CHECK_FAIL(epoch, XLAL_EFAILED, "Could not get attribute .epoch"); \
453 if (AstropyTime_AsLIGOTimeGPS(&series->epoch, epoch) < 0) \
454 XLAL_ERROR_FAIL(XLAL_EFUNC); \
457 dt = PyObject_GetAttrString(gwpyser, "dt"); \
458 XLAL_CHECK_FAIL(dt, XLAL_EFAILED, "Could not get attribute .dt"); \
459 si = PyObject_GetAttrString(dt, "si"); \
460 XLAL_CHECK_FAIL(si, XLAL_EFAILED, "Could not get attribute .si"); \
462 value = PyObject_GetAttrString(si, "value"); \
463 XLAL_CHECK_FAIL(value, XLAL_EFAILED, "Could not get attribute .value"); \
465 XLAL_CHECK_FAIL(PyFloat_Check(value), XLAL_EFAILED, "Attribute .value is not a float"); \
466 series->deltaT = PyFloat_AsDouble(value); \
467 if (PyErr_Occurred()) \
468 XLAL_ERROR_FAIL(XLAL_EFAILED, "Could not convert attribute .value to double"); \
472 value = PyObject_GetAttrString(gwpyser, "value"); \
473 XLAL_CHECK_FAIL(value, XLAL_EFAILED, "Could not get attribute .value"); \
474 units = PyObject_GetAttrString(gwpyser, "unit"); \
475 XLAL_CHECK_FAIL(units, XLAL_EFAILED, "Could not get attribute .unit"); \
476 scale = AstropyUnit_AsLALUnit(&series->sampleUnits, units); \
477 if (XLAL_IS_REAL8_FAIL_NAN(scale)) \
478 XLAL_ERROR_FAIL(XLAL_EFUNC); \
480 series->data = PyArray_As ## TYPE ## Sequence(value); \
481 XLAL_CHECK_FAIL(series->data, XLAL_EFUNC); \
484 for (ssize_t i = 0; i < series->data->length; ++i) \
485 series->data->data[i] *= scale; \
498 XLALDestroy ## TYPE ## TimeSeries(series); \
502 #define DEFINE_GWPY_FREQUENCYSERIES_TO_LAL_FREQUENCYSERIES(TYPE) \
503 static TYPE ## FrequencySeries *GWpyFrequencySeries_As ## TYPE ## FrequencySeries(PyObject *gwpyser) \
505 TYPE ## FrequencySeries *series; \
507 PyObject *name = NULL; \
508 PyObject *bytes = NULL; \
509 PyObject *epoch = NULL; \
510 PyObject *f0 = NULL; \
511 PyObject *df = NULL; \
512 PyObject *value = NULL; \
513 PyObject *units = NULL; \
514 PyObject *si = NULL; \
516 series = XLALCalloc(1, sizeof(*series)); \
517 XLAL_CHECK_NULL(series, XLAL_ENOMEM); \
521 name = PyObject_GetAttrString(gwpyser, "name"); \
522 XLAL_CHECK_FAIL(name, XLAL_EFAILED, "Could not get attribute .name"); \
523 XLAL_CHECK_FAIL(PyUnicode_Check(name), XLAL_EFAILED, "Attribute .name is not a string"); \
524 bytes = PyUnicode_AsASCIIString(name); \
525 XLAL_CHECK_FAIL(PyBytes_Check(bytes), XLAL_EFAILED, "Could not decode attribute .name as ASCII string"); \
527 XLALStringCopy(series->name, PyBytes_AsString(bytes), sizeof(series->name)); \
530 epoch = PyObject_GetAttrString(gwpyser, "epoch"); \
531 XLAL_CHECK_FAIL(epoch, XLAL_EFAILED, "Could not get attribute .epoch"); \
532 if (AstropyTime_AsLIGOTimeGPS(&series->epoch, epoch) < 0) \
533 XLAL_ERROR_FAIL(XLAL_EFUNC); \
536 f0 = PyObject_GetAttrString(gwpyser, "f0"); \
537 XLAL_CHECK_FAIL(f0, XLAL_EFAILED, "Could not get attribute .f0"); \
538 si = PyObject_GetAttrString(f0, "si"); \
539 XLAL_CHECK_FAIL(si, XLAL_EFAILED, "Could not get attribute .si"); \
541 value = PyObject_GetAttrString(si, "value"); \
542 XLAL_CHECK_FAIL(value, XLAL_EFAILED, "Could not get attribute .value"); \
544 XLAL_CHECK_FAIL(PyFloat_Check(value), XLAL_EFAILED, "Attribute .value is not a float"); \
545 series->f0 = PyFloat_AsDouble(value); \
546 if (PyErr_Occurred()) \
547 XLAL_ERROR_FAIL(XLAL_EFAILED, "Could not convert attribute .value to double"); \
550 df = PyObject_GetAttrString(gwpyser, "df"); \
551 XLAL_CHECK_FAIL(df, XLAL_EFAILED, "Could not get attribute .df"); \
552 si = PyObject_GetAttrString(df, "si"); \
553 XLAL_CHECK_FAIL(si, XLAL_EFAILED, "Could not get attribute .si"); \
555 value = PyObject_GetAttrString(si, "value"); \
556 XLAL_CHECK_FAIL(value, XLAL_EFAILED, "Could not get attribute .value"); \
558 XLAL_CHECK_FAIL(PyFloat_Check(value), XLAL_EFAILED, "Attribute .value is not a float"); \
559 series->deltaF = PyFloat_AsDouble(value); \
560 if (PyErr_Occurred()) \
561 XLAL_ERROR_FAIL(XLAL_EFAILED, "Could not convert attribute .value to double"); \
565 value = PyObject_GetAttrString(gwpyser, "value"); \
566 XLAL_CHECK_FAIL(value, XLAL_EFAILED, "Could not get attribute .value"); \
567 units = PyObject_GetAttrString(gwpyser, "unit"); \
568 XLAL_CHECK_FAIL(units, XLAL_EFAILED, "Could not get attribute .unit"); \
569 scale = AstropyUnit_AsLALUnit(&series->sampleUnits, units); \
570 if (XLAL_IS_REAL8_FAIL_NAN(scale)) \
571 XLAL_ERROR_FAIL(XLAL_EFUNC); \
573 series->data = PyArray_As ## TYPE ## Sequence(value); \
574 XLAL_CHECK_FAIL(series->data, XLAL_EFUNC); \
577 for (ssize_t i = 0; i < series->data->length; ++i) \
578 series->data->data[i] *= scale; \
592 XLALDestroy ## TYPE ## FrequencySeries(series); \
596 DEFINE_GWPY_TIMESERIES_TO_LAL_TIMESERIES(
REAL8)
597 DEFINE_GWPY_TIMESERIES_TO_LAL_TIMESERIES(
COMPLEX16)
598 DEFINE_GWPY_FREQUENCYSERIES_TO_LAL_FREQUENCYSERIES(
COMPLEX16)
606 LALSimInspiralGenerator *myself
613 LALSimInspiralGenerator *myself
619 LALSimInspiralGenerator *myself
626 LALSimInspiralGenerator *myself
639 LALDict *waveform_parameter_units;
665 const char *module_name;
666 const char *object_name;
667 LALDict *genparams = NULL;
668 PyObject *
args = NULL;
669 PyObject *kwargs = NULL;
670 PyObject *fromlist = NULL;
671 PyObject *result = NULL;
682 if (_import_array() < 0) {
684 PyErr_SetString(PyExc_ImportError,
"numpy.core.multiarray failed to import");
691 internal_data->waveform_parameter_units = create_waveform_parameter_units_dict();
695 fromlist = Py_BuildValue(
"(s)",
"Quantity");
697 result = PyImport_ImportModuleEx(
"astropy.units", NULL, NULL, fromlist);
701 internal_data->quantity = PyObject_GetAttrString(result,
"Quantity");
717 kwargs = PyDict_FromLALDict(genparams, NULL, NULL);
721 args = PyTuple_New(0);
733 if (PyObject_HasAttrString(
internal_data->object,
"generate_td_waveform")) {
740 if (PyObject_HasAttrString(
internal_data->object,
"generate_fd_waveform")) {
747 if (PyObject_HasAttrString(
internal_data->object,
"generate_td_modes")) {
754 if (PyObject_HasAttrString(
internal_data->object,
"generate_fd_modes")) {
783 Py_XDECREF(fromlist);
788 static int finalize(LALSimInspiralGenerator *myself)
792 free_internal_data(myself->internal_data);
793 myself->generate_td_waveform = NULL;
794 myself->generate_fd_waveform = NULL;
795 myself->generate_td_modes = NULL;
796 myself->generate_fd_modes = NULL;
797 myself->internal_data = NULL;
808 LALSimInspiralGenerator *myself
813 PyObject *
args = NULL;
814 PyObject *kwargs = NULL;
815 PyObject *result = NULL;
838 result = PyObject_Call(method,
args, kwargs);
847 hp = PyTuple_GetItem(result, 0);
848 hc = PyTuple_GetItem(result, 1);
852 *hplus = GWpyTimeSeries_AsREAL8TimeSeries(hp);
854 *hcross = GWpyTimeSeries_AsREAL8TimeSeries(hc);
876 LALSimInspiralGenerator *myself
881 PyObject *
args = NULL;
882 PyObject *kwargs = NULL;
883 PyObject *result = NULL;
905 result = PyObject_Call(method,
args, kwargs);
915 while (PyDict_Next(result, &pos, &key, &val)) {
928 l = PyTuple_GetItem(key, 0);
929 m = PyTuple_GetItem(key, 1);
933 this->l = PyLong_AsUnsignedLong(
l);
935 this->m = PyLong_AsLong(
m);
937 this->mode = GWpyTimeSeries_AsCOMPLEX16TimeSeries(val);
959 LALSimInspiralGenerator *myself
964 PyObject *
args = NULL;
965 PyObject *kwargs = NULL;
966 PyObject *result = NULL;
989 result = PyObject_Call(method,
args, kwargs);
998 hp = PyTuple_GetItem(result, 0);
999 hc = PyTuple_GetItem(result, 1);
1003 *hplus = GWpyFrequencySeries_AsCOMPLEX16FrequencySeries(hp);
1005 *hcross = GWpyFrequencySeries_AsCOMPLEX16FrequencySeries(hc);
1028 LALSimInspiralGenerator *myself
1033 PyObject *
args = NULL;
1034 PyObject *kwargs = NULL;
1035 PyObject *result = NULL;
1057 result = PyObject_Call(method,
args, kwargs);
1067 while (PyDict_Next(result, &pos, &key, &val)) {
1080 l = PyTuple_GetItem(key, 0);
1081 m = PyTuple_GetItem(key, 1);
1085 this->l = PyLong_AsUnsignedLong(
l);
1087 this->m = PyLong_AsLong(
m);
1089 this->mode = GWpyFrequencySeries_AsCOMPLEX16FrequencySeries(val);
1111 .name =
"ExternalPython",
1118 .internal_data = NULL
int XLALDictContains(const LALDict *dict, const char *key)
const char * XLALDictLookupStringValue(LALDict *dict, const char *key)
int XLALDictRemove(LALDict *dict, const char *key)
void XLALDestroyDict(LALDict *dict)
LALDict * XLALDictDuplicate(LALDict *old)
int XLALDictInsertStringValue(LALDict *dict, const char *key, const char *string)
LALDict * XLALCreateDict(void)
void XLALDictForeach(LALDict *dict, void(*func)(char *, LALValue *, void *), void *thunk)
static int finalize(LALSimInspiralGenerator *myself)
static int generate_fd_waveform(COMPLEX16FrequencySeries **hplus, COMPLEX16FrequencySeries **hcross, LALDict *params, LALSimInspiralGenerator *myself)
Fourier domain polarizations.
static int generate_fd_modes(SphHarmFrequencySeries **hlm, LALDict *params, LALSimInspiralGenerator *myself)
Define waveform generator methods to generate polarizations or modes in time or Fourier domain for le...
static int generate_td_waveform(REAL8TimeSeries **hplus, REAL8TimeSeries **hcross, LALDict *params, LALSimInspiralGenerator *myself)
Time domain polarizations.
static int generate_td_modes(SphHarmTimeSeries **hlm, LALDict *params, LALSimInspiralGenerator *myself)
Time domain modes.
const LALSimInspiralGenerator lalPythonGeneratorTemplate
static int initialize(UNUSED LALSimInspiralGenerator *myself, UNUSED LALDict *params)
const char * XLALValueGetString(const LALValue *value)
LALTYPECODE XLALValueGetType(const LALValue *value)
REAL8 XLALValueGetREAL8(const LALValue *value)
INT4 XLALValueGetINT4(const LALValue *value)
void XLALDestroyCOMPLEX16FrequencySeries(COMPLEX16FrequencySeries *series)
void * XLALCalloc(size_t m, size_t n)
void XLALDestroySphHarmTimeSeries(SphHarmTimeSeries *ts)
Delete list from current pointer to the end of the list.
void XLALDestroySphHarmFrequencySeries(SphHarmFrequencySeries *ts)
Delete list from current pointer to the end of the list.
void XLALDestroyREAL8TimeSeries(REAL8TimeSeries *series)
#define XLAL_ERROR_NULL(...)
int XLALGetBaseErrno(void)
#define XLAL_CHECK(assertion,...)
int XLALSetErrno(int errnum)
#define XLAL_REAL8_FAIL_NAN
#define XLAL_CHECK_FAIL(assertion,...)
#define XLAL_CHECK_REAL8(assertion,...)
#define XLAL_CHECK_NULL(assertion,...)
#define XLAL_PRINT_ERROR(...)
#define XLAL_ERROR_FAIL(...)
LIGOTimeGPS * XLALGPSSet(LIGOTimeGPS *epoch, INT4 gpssec, INT8 gpsnan)
UINT2 unitDenominatorMinusOne[LALNumUnits]
INT2 unitNumerator[LALNumUnits]
Structure to carry a collection of spherical harmonic modes in COMPLEX16 time series.