LALSimulation  5.4.0.1-fe68b98
LALSimInspiralGeneratorPython.c
Go to the documentation of this file.
1 #include "config.h"
2 
3 #ifdef __GNUC__
4 #define UNUSED __attribute__ ((unused))
5 #else
6 #define UNUSED
7 #endif
8 
9 #ifndef HAVE_PYTHON3_EMBED
10 
11 #include <lal/LALStdlib.h>
12 #include <lal/LALDict.h>
13 #include <lal/LALSimInspiral.h>
15 
16 static int initialize(UNUSED LALSimInspiralGenerator *myself, UNUSED LALDict *params)
17 {
18  XLAL_ERROR(XLAL_EINVAL, "Python generator not implemented");
19 }
20 
21 const LALSimInspiralGenerator lalPythonGeneratorTemplate = {
22  .name = "ExternalPython",
23  .initialize = initialize,
24  .finalize = NULL,
25  .generate_td_waveform = NULL,
26  .generate_fd_waveform = NULL,
27  .generate_td_modes = NULL,
28  .generate_fd_modes = NULL,
29  .internal_data = NULL
30 };
31 
32 #else /* HAVE_PYTHON_EMBED */
33 
34 /* Python.h requires the assert macro, so don't poison it */
35 #undef LAL_STRICT_DEFS_ENABLED
36 #include <lal/LALStddef.h>
37 
38 #include <lal/LALStdlib.h>
39 #include <lal/LALDict.h>
40 #include <lal/LALSimInspiral.h>
42 
43 #include <lal/Date.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>
52 
53 #include <Python.h>
54 
55 #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
56 #ifdef __GNUC__
57 #pragma GCC diagnostic push
58 #pragma GCC diagnostic ignored "-Wcast-qual"
59 #endif
60 #include <numpy/arrayobject.h>
61 #ifdef __GNUC__
62 #pragma GCC diagnostic pop
63 #endif
64 
65 /* Create laldict where the units of all the parameters in LALSimInspiralWavofrmParams_common.c are inserted as strings */
66 static LALDict *create_waveform_parameter_units_dict(void)
67 {
68 
69  LALDict *dict;
70  dict = XLALCreateDict();
71  if (dict == NULL)
73 
74  for (size_t i = 0; i < XLAL_NUM_ELEM(lalSimInspiralREAL8WaveformParams); ++i)
77  XLALDestroyDict(dict);
79  }
80 
81  return dict;
82 }
83 
84 /* emit error messages corresponding to python exceptions */
85 static int python_error(void)
86 {
87  if (PyErr_Occurred()) {
88  XLAL_PRINT_ERROR("Python exception raised");
89  PyErr_Print();
90  return 1;
91  }
92  return 0;
93 }
94 
95 
96 /* ROUTINES TO ENCODE A LALDICT AS A PYTHON DICT */
97 
98 /* Helper struct containing a python dictionary, an astropy quantity and a laldict containing the SI units of the waveform parameters */
99 struct add_to_dict_params {
100  PyObject *dict;
101  PyObject *quantity;
102  LALDict *waveform_parameter_units;
103 };
104 
105 /* Set a new value for a particular key in a input python dictionary (thunk.dict) */
106 static void add_to_dict(char *key, LALValue *value, void *thunk)
107 {
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; /* python dict */
112  PyObject *args = NULL;
113  PyObject *val = NULL; /* python value */
114  /* Transform LALValue to python value */
115  switch (XLALValueGetType(value)) {
116  case LAL_CHAR_TYPE_CODE:
117  val = PyUnicode_FromString(XLALValueGetString(value));
118  XLAL_CHECK_FAIL(val && !PyErr_Occurred(), XLAL_EFAILED, "Failed to create Python value for parameter %s", key);
119  break;
120  case LAL_I4_TYPE_CODE:
121  val = PyLong_FromLong(XLALValueGetINT4(value));
122  XLAL_CHECK_FAIL(val && !PyErr_Occurred(), XLAL_EFAILED, "Failed to create Python value for parameter %s", key);
123  break;
124  case LAL_D_TYPE_CODE:
125  val = PyFloat_FromDouble(XLALValueGetREAL8(value));
126  XLAL_CHECK_FAIL(val && !PyErr_Occurred(), XLAL_EFAILED, "Failed to create Python value for parameter %s", key);
127  /* Use an astropy quantity. The input LALValue is assumed to be in SI units, those are read from thunk.waveform_parameter_units */
128  if (quantity && units) {
129  const char *unit = ""; /* dimensionless */
130  if (XLALDictContains(units, key))
131  unit = XLALDictLookupStringValue(units, key);
133  args = Py_BuildValue("Os", val, unit);
134  XLAL_CHECK_FAIL(args, XLAL_EFAILED, "Failed to build args for parameter %s", key);
135  Py_CLEAR(val);
136  val = PyObject_CallObject(quantity, args);
137  XLAL_CHECK_FAIL(val, XLAL_EFAILED, "Failed to create Quantity for parameter %s with unit %s", key, unit);
138  Py_CLEAR(args);
139  }
140  break;
141  default:
142  XLAL_ERROR_FAIL(XLAL_ETYPE, "Invalid type for parameter %s", key);
143  break;
144  }
145  /* Insert value in python dictionary */
146  XLAL_CHECK_FAIL(PyDict_SetItemString(dict, key, val) == 0, XLAL_EFAILED, "Failed to insert parameter %s in Python dict", key);
147  Py_DECREF(val);
148  return;
149 
150 XLAL_FAIL:
151  python_error();
152  Py_XDECREF(args);
153  Py_XDECREF(val);
154  return;
155 }
156 
157 /* Transform a laldictionary into a python dictionary */
158 static PyObject *PyDict_FromLALDict(LALDict *ldict, PyObject *quantity, LALDict *waveform_parameter_units)
159 {
160  struct add_to_dict_params add_to_dict_params;
161  PyObject *dict;
162  int errnum;
164  dict = PyDict_New();
165  XLAL_CHECK_NULL(dict, XLAL_EFAILED, "Could not create Python dict");
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;
169  errnum = XLALClearErrno(); /* clear xlalErrno and preserve value */
170  /* Loop over all the keys in the laldict and insert the values into the python dictionary */
171  XLALDictForeach(ldict, add_to_dict, &add_to_dict_params);
172  if (XLALGetBaseErrno()) { /* something failed... */
173  Py_DECREF(dict);
175  }
176  XLALSetErrno(errnum); /* restore previous value of xlalErrno */
177  return dict;
178 }
179 
180 
181 /* ROUTINES TO CONVERT NUMPY ARRAYS AS C ARRAYS */
182 
183 /*
184  * Store data from PyArrayObject in buf of size bufsz, returns number of bytes.
185  * If buf is NULL then return the number of bytes in the array.
186  * Routine fails if buf is not NULL and bufsz is not equal to the number of
187  * bytes in the array; if buf needs to be allocated, call the routine once
188  * with buf == NULL to determine the amount of memory to allocate to buf.
189  */
190 static size_t PyArray_AsBytes(void *buf, size_t bufsz, PyArrayObject *arr)
191 {
192  size_t nbytes;
193  XLAL_CHECK(arr, XLAL_EFAULT);
194  nbytes = PyArray_NBYTES(arr);
195  if (buf) {
196  XLAL_CHECK(nbytes == bufsz, XLAL_ESIZE, "Inconsisent number of bytes");
197  memcpy(buf, PyArray_DATA(arr), bufsz);
198  }
199  return nbytes;
200 }
201 
202 /* Macro to define helper functions that return the number of bytes needed to allocate a numpy array of a specific type */
203 #define DEFINE_PYARRAY_ASTYPE(LALTYPE, NPYTYPE) \
204  static size_t PyArray_As ## LALTYPE (LALTYPE *data, size_t length, PyObject *o) \
205  { \
206  PyArrayObject *arr; \
207  size_t nbytes; \
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); \
218  }
219 
220 /* Only a few of these are used... */
221 // DEFINE_PYARRAY_ASTYPE(CHAR, NPY_BYTE)
222 // DEFINE_PYARRAY_ASTYPE(INT2, NPY_INT16)
223 // DEFINE_PYARRAY_ASTYPE(INT4, NPY_INT32)
224 // DEFINE_PYARRAY_ASTYPE(INT8, NPY_INT64)
225 // DEFINE_PYARRAY_ASTYPE(UCHAR, NPY_UBYTE)
226 // DEFINE_PYARRAY_ASTYPE(UINT2, NPY_UINT16)
227 // DEFINE_PYARRAY_ASTYPE(UINT4, NPY_UINT32)
228 // DEFINE_PYARRAY_ASTYPE(UINT8, NPY_UINT64)
229 // DEFINE_PYARRAY_ASTYPE(REAL4, NPY_FLOAT)
230 DEFINE_PYARRAY_ASTYPE(REAL8, NPY_DOUBLE)
231 // DEFINE_PYARRAY_ASTYPE(COMPLEX8, NPY_CFLOAT)
232 DEFINE_PYARRAY_ASTYPE(COMPLEX16, NPY_CDOUBLE)
233 
234 
235 /* ROUTINES TO CONVERT NUMPY ARRAYS AS LAL SEQUENCES */
236 
237 #define DEFINE_PYARRAY_ASSEQTYPE(LALTYPE) \
238  static LALTYPE ## Sequence * PyArray_As ## LALTYPE ## Sequence (PyObject *o) \
239  { \
240  LALTYPE ## Sequence *seq; \
241  size_t length; \
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"); \
250  } \
251  return seq; \
252  }
253 
254 /* Only a few of these are used... */
255 // DEFINE_PYARRAY_ASSEQTYPE(CHAR)
256 // DEFINE_PYARRAY_ASSEQTYPE(INT2)
257 // DEFINE_PYARRAY_ASSEQTYPE(INT4)
258 // DEFINE_PYARRAY_ASSEQTYPE(INT8)
259 // DEFINE_PYARRAY_ASSEQTYPE(UCHAR)
260 // DEFINE_PYARRAY_ASSEQTYPE(UINT2)
261 // DEFINE_PYARRAY_ASSEQTYPE(UINT4)
262 // DEFINE_PYARRAY_ASSEQTYPE(UINT8)
263 // DEFINE_PYARRAY_ASSEQTYPE(REAL4)
264 DEFINE_PYARRAY_ASSEQTYPE(REAL8)
265 // DEFINE_PYARRAY_ASSEQTYPE(COMPLEX8)
266 DEFINE_PYARRAY_ASSEQTYPE(COMPLEX16)
267 
268 
269 /* ROUTINE TO CONVERT ASTROPY TIME AND UNIT AS LAL */
270 
271 static int AstropyTime_AsLIGOTimeGPS(LIGOTimeGPS *lt, PyObject *at)
272 {
273  PyObject *gpstime = NULL;
274  PyObject *result = NULL;
275  double sec, nan;
276 
277  XLAL_CHECK(lt, XLAL_EFAULT);
278  XLAL_CHECK(at, XLAL_EFAULT);
279 
280  /* get the gps time at full precision */
281  gpstime = PyObject_CallMethod(at, "to_value", "ss", "gps", "decimal");
282  XLAL_CHECK_FAIL(gpstime, XLAL_EFAILED, "Could not call method .to_value()");
283 
284  /* break it into seconds and nanoseconds */
285  result = PyObject_CallMethod(gpstime, "__divmod__", "i", 1);
286  XLAL_CHECK_FAIL(result, XLAL_EFAILED, "Could not call method .__divmod__(1)");
287  Py_CLEAR(gpstime);
288 
289  /* result must be a tuple of two objects */
290  XLAL_CHECK_FAIL(PyTuple_Check(result), XLAL_EFAILED, "Invalid result of method .__divmod__(1)");
291  XLAL_CHECK_FAIL(PyTuple_Size(result) == 2, XLAL_EFAILED, "Invalid result of method .__divmod__(1)");
292  sec = PyFloat_AsDouble(PyTuple_GetItem(result, 0));
293  if (PyErr_Occurred())
294  XLAL_ERROR_FAIL(XLAL_EFAILED, "Could not convert GPS seconds to double");
295  nan = PyFloat_AsDouble(PyTuple_GetItem(result, 1));
296  if (PyErr_Occurred())
297  XLAL_ERROR_FAIL(XLAL_EFAILED, "Could not convert GPS nanoseconds to double");
298  Py_CLEAR(result);
299 
300  XLALGPSSet(lt, sec, 1e9 * nan);
301  return 0;
302 
303 XLAL_FAIL:
304  python_error();
305  Py_XDECREF(gpstime);
306  Py_XDECREF(result);
307  return (int)XLAL_FAILURE;
308 }
309 
310 static double AstropyUnit_AsLALUnit(LALUnit *lu, PyObject *au)
311 {
312  PyObject *si = NULL;
313  PyObject *scale = NULL;
314  PyObject *bases = NULL;
315  PyObject *powers = NULL;
316  double scale_factor;
317 
320 
321  /* Get the astropy value in SI units */
322  si = PyObject_GetAttrString(au, "si");
323  XLAL_CHECK_FAIL(si, XLAL_EFAILED, "Could not get attribute .si");
324 
325  /* Get the 3 attributes of the CompositeUnit astropy class (scale, bases, powers) */
326  scale = PyObject_GetAttrString(si, "scale");
327  XLAL_CHECK_FAIL(scale, XLAL_EFAILED, "Could not get attribute .scale");
328  XLAL_CHECK_FAIL(PyFloat_Check(scale), XLAL_EFAILED, "Attribute .scale is not a float");
329  scale_factor = PyFloat_AsDouble(scale);
330  if (PyErr_Occurred())
331  XLAL_ERROR_FAIL(XLAL_EFAILED, "Could not convert attribute .scale to double");
332  Py_CLEAR(scale);
333 
334  bases = PyObject_GetAttrString(si, "bases");
335  XLAL_CHECK_FAIL(bases, XLAL_EFAILED, "Could not get attribute .bases");
336 
337  powers = PyObject_GetAttrString(si, "powers");
338  XLAL_CHECK_FAIL(powers, XLAL_EFAILED, "Could not get attribute .powers");
339 
340  Py_CLEAR(si);
341 
342  if (!PyList_Check(bases) || !PyList_Check(powers))
343  XLAL_ERROR_FAIL(XLAL_EFAILED, "Invalid astropy unit data");
344  if (PyList_Size(bases) != PyList_Size(powers))
345  XLAL_ERROR_FAIL(XLAL_EFAILED, "Internal data inconsistency");
346 
347  /* The bases attribute contains a sequence of units this unit is composed of.
348  * We loop over each of them and transform them into a LALUnit.
349  * The powers item tell us the power of the corresponding base unit (this is a general convention to describe units) and to account for
350  * float powers they are decomposed into a fraction of integer numbers (numerator and denominator) which are passed then to the LALUnit struct.
351  */
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); // borrowed reference
355  PyObject *power = PyList_GetItem(powers, i); // borrowed reference
356  PyObject *intratio = NULL;
357  PyObject *basename = NULL;
358  int numerator;
359  int denominator;
360 
361  intratio = PyObject_CallMethod(power, "as_integer_ratio", NULL);
362  if (intratio == NULL || !PyTuple_Check(intratio)) {
363  Py_XDECREF(intratio);
364  XLAL_ERROR_FAIL(XLAL_EFAILED, "Failed method .as_integer_ratio");
365  }
366  if (!PyArg_ParseTuple(intratio, "ii", &numerator, &denominator)) {
367  Py_DECREF(intratio);
368  XLAL_ERROR_FAIL(XLAL_EFAILED, "Failed to unpack integer ratio");
369  }
370  Py_DECREF(intratio);
371 
372  basename = PyObject_GetAttrString(base, "name");
373  if (basename == NULL || !PyUnicode_Check(basename)) {
374  Py_XDECREF(basename);
375  XLAL_ERROR_FAIL(XLAL_EFAILED, "Failed get attribute .name");
376  }
377 
378  if (PyUnicode_CompareWithASCIIString(basename, "m") == 0) {
379  lu->unitNumerator[LALUnitIndexMeter] = numerator;
380  lu->unitDenominatorMinusOne[LALUnitIndexMeter] = denominator - 1;
381  } else if (PyUnicode_CompareWithASCIIString(basename, "kg") == 0) {
382  lu->unitNumerator[LALUnitIndexKiloGram] = numerator;
383  lu->unitDenominatorMinusOne[LALUnitIndexKiloGram] = denominator - 1;
384  } else if (PyUnicode_CompareWithASCIIString(basename, "s") == 0) {
385  lu->unitNumerator[LALUnitIndexSecond] = numerator;
386  lu->unitDenominatorMinusOne[LALUnitIndexSecond] = denominator - 1;
387  } else if (PyUnicode_CompareWithASCIIString(basename, "A") == 0) {
388  lu->unitNumerator[LALUnitIndexAmpere] = numerator;
389  lu->unitDenominatorMinusOne[LALUnitIndexAmpere] = denominator - 1;
390  } else if (PyUnicode_CompareWithASCIIString(basename, "K") == 0) {
391  lu->unitNumerator[LALUnitIndexKelvin] = numerator;
392  lu->unitDenominatorMinusOne[LALUnitIndexKelvin] = denominator - 1;
393  } else if (PyUnicode_CompareWithASCIIString(basename, "strain") == 0) {
394  lu->unitNumerator[LALUnitIndexStrain] = numerator;
395  lu->unitDenominatorMinusOne[LALUnitIndexStrain] = denominator - 1;
396  } else if (PyUnicode_CompareWithASCIIString(basename, "ct") == 0) {
397  lu->unitNumerator[LALUnitIndexADCCount] = numerator;
398  lu->unitDenominatorMinusOne[LALUnitIndexADCCount] = denominator - 1;
399  } else if (PyUnicode_CompareWithASCIIString(basename, "rad") == 0) {
400  ; /* this is just dimensionless in LAL */
401  } else {
402  Py_DECREF(basename);
403  XLAL_ERROR_FAIL(XLAL_EFAILED, "Astropy unit has no associated LAL unit");
404  }
405 
406  Py_DECREF(basename);
407  }
408 
409  Py_DECREF(bases);
410  Py_DECREF(powers);
411  return scale_factor;
412 
413 XLAL_FAIL:
414  python_error();
415  Py_XDECREF(si);
416  Py_XDECREF(bases);
417  Py_XDECREF(powers);
418  return XLAL_REAL8_FAIL_NAN;
419 }
420 
421 
422 /* ROUTINES TO CONVERT GWPY TIME/FREQUENCY SERIES AS LAL SERIES */
423 
424 #define DEFINE_GWPY_TIMESERIES_TO_LAL_TIMESERIES(TYPE) \
425  static TYPE ## TimeSeries *GWpyTimeSeries_As ## TYPE ## TimeSeries(PyObject *gwpyser) \
426  { \
427  TYPE ## TimeSeries *series; \
428  double scale; \
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; \
436  \
437  series = XLALCalloc(1, sizeof(*series)); \
438  XLAL_CHECK_NULL(series, XLAL_ENOMEM); \
439  \
440  /* extract the metadata */ \
441  \
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"); \
447  Py_CLEAR(name); \
448  XLALStringCopy(series->name, PyBytes_AsString(bytes), sizeof(series->name)); \
449  Py_CLEAR(bytes); \
450  \
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); \
455  Py_CLEAR(epoch); \
456  \
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"); \
461  Py_CLEAR(dt); \
462  value = PyObject_GetAttrString(si, "value"); \
463  XLAL_CHECK_FAIL(value, XLAL_EFAILED, "Could not get attribute .value"); \
464  Py_CLEAR(si); \
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"); \
469  Py_CLEAR(value); \
470  \
471  /* extract the data */ \
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); \
479  Py_CLEAR(units); \
480  series->data = PyArray_As ## TYPE ## Sequence(value); \
481  XLAL_CHECK_FAIL(series->data, XLAL_EFUNC); \
482  Py_CLEAR(value); \
483  if (scale != 1.0) \
484  for (ssize_t i = 0; i < series->data->length; ++i) \
485  series->data->data[i] *= scale; \
486  \
487  return series; \
488  \
489  XLAL_FAIL: \
490  python_error(); \
491  Py_XDECREF(name); \
492  Py_XDECREF(bytes); \
493  Py_XDECREF(epoch); \
494  Py_XDECREF(dt); \
495  Py_XDECREF(value); \
496  Py_XDECREF(units); \
497  Py_XDECREF(si); \
498  XLALDestroy ## TYPE ## TimeSeries(series); \
499  return NULL; \
500  }
501 
502 #define DEFINE_GWPY_FREQUENCYSERIES_TO_LAL_FREQUENCYSERIES(TYPE) \
503  static TYPE ## FrequencySeries *GWpyFrequencySeries_As ## TYPE ## FrequencySeries(PyObject *gwpyser) \
504  { \
505  TYPE ## FrequencySeries *series; \
506  double scale; \
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; \
515  \
516  series = XLALCalloc(1, sizeof(*series)); \
517  XLAL_CHECK_NULL(series, XLAL_ENOMEM); \
518  \
519  /* extract the metadata */ \
520  \
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"); \
526  Py_CLEAR(name); \
527  XLALStringCopy(series->name, PyBytes_AsString(bytes), sizeof(series->name)); \
528  Py_CLEAR(bytes); \
529  \
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); \
534  Py_CLEAR(epoch); \
535  \
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"); \
540  Py_CLEAR(f0); \
541  value = PyObject_GetAttrString(si, "value"); \
542  XLAL_CHECK_FAIL(value, XLAL_EFAILED, "Could not get attribute .value"); \
543  Py_CLEAR(si); \
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"); \
548  Py_CLEAR(value); \
549  \
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"); \
554  Py_CLEAR(df); \
555  value = PyObject_GetAttrString(si, "value"); \
556  XLAL_CHECK_FAIL(value, XLAL_EFAILED, "Could not get attribute .value"); \
557  Py_CLEAR(si); \
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"); \
562  Py_CLEAR(value); \
563  \
564  /* extract the data */ \
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); \
572  Py_CLEAR(units); \
573  series->data = PyArray_As ## TYPE ## Sequence(value); \
574  XLAL_CHECK_FAIL(series->data, XLAL_EFUNC); \
575  Py_CLEAR(value); \
576  if (scale != 1.0) \
577  for (ssize_t i = 0; i < series->data->length; ++i) \
578  series->data->data[i] *= scale; \
579  \
580  return series; \
581  \
582  XLAL_FAIL: \
583  python_error(); \
584  Py_XDECREF(name); \
585  Py_XDECREF(bytes); \
586  Py_XDECREF(epoch); \
587  Py_XDECREF(f0); \
588  Py_XDECREF(df); \
589  Py_XDECREF(value); \
590  Py_XDECREF(units); \
591  Py_XDECREF(si); \
592  XLALDestroy ## TYPE ## FrequencySeries(series); \
593  return NULL; \
594  }
595 
596 DEFINE_GWPY_TIMESERIES_TO_LAL_TIMESERIES(REAL8)
597 DEFINE_GWPY_TIMESERIES_TO_LAL_TIMESERIES(COMPLEX16)
598 DEFINE_GWPY_FREQUENCYSERIES_TO_LAL_FREQUENCYSERIES(COMPLEX16)
599 
600 
601 /* STANDARD GENERATOR METHODS */
602 
603 static int generate_td_modes(
604  SphHarmTimeSeries **hlm,
605  LALDict *params,
606  LALSimInspiralGenerator *myself
607 );
608 
609 static int generate_td_waveform(
610  REAL8TimeSeries **hplus,
611  REAL8TimeSeries **hcross,
612  LALDict *params,
613  LALSimInspiralGenerator *myself
614 );
615 
616 static int generate_fd_modes(
618  LALDict *params,
619  LALSimInspiralGenerator *myself
620 );
621 
622 static int generate_fd_waveform(
623  COMPLEX16FrequencySeries **hplus,
624  COMPLEX16FrequencySeries **hcross,
625  LALDict *params,
626  LALSimInspiralGenerator *myself
627 );
628 
629 /* Helper struct to store the python quantity and objects used for waveform generation */
630 struct internal_data {
631  PyObject *module;
632  PyObject *object;
633  PyObject *instance;
634  PyObject *generate_td_modes;
635  PyObject *generate_fd_modes;
636  PyObject *generate_td_waveform;
637  PyObject *generate_fd_waveform;
638  PyObject *quantity;
639  LALDict *waveform_parameter_units;
640 };
641 
642 /* Free internal_data struct */
643 static void free_internal_data(struct internal_data *internal_data)
644 {
645  if (internal_data) {
646  Py_XDECREF(internal_data->module);
647  Py_XDECREF(internal_data->object);
648  Py_XDECREF(internal_data->instance);
649  Py_XDECREF(internal_data->generate_td_modes);
650  Py_XDECREF(internal_data->generate_fd_modes);
651  Py_XDECREF(internal_data->generate_td_waveform);
652  Py_XDECREF(internal_data->generate_fd_waveform);
653  Py_XDECREF(internal_data->quantity);
654  XLALDestroyDict(internal_data->waveform_parameter_units);
656  }
657 }
658 
659 /* Initialize python generator.
660  * The path to the python code that actually generates the model is stored in internal_data.module and .object
661  */
662 static int initialize(LALSimInspiralGenerator *myself, LALDict *params)
663 {
664  struct internal_data *internal_data = NULL;
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;
672 
673  XLAL_CHECK(myself, XLAL_EFAULT);
675 
676  module_name = XLALDictLookupStringValue(params, "module");
677  object_name = XLALDictLookupStringValue(params, "object");
678  XLAL_CHECK(module_name, XLAL_EINVAL, "No parameter \"module\" found");
679  XLAL_CHECK(object_name, XLAL_EINVAL, "No parameter \"object\" found");
680 
681  Py_Initialize();
682  if (_import_array() < 0) {
683  PyErr_Print();
684  PyErr_SetString(PyExc_ImportError, "numpy.core.multiarray failed to import");
685  XLAL_ERROR(XLAL_EFAILED, "Failed to import numpy.core.multiarray");
686  }
687 
688  internal_data = XLALCalloc(1, sizeof(struct internal_data));
690 
691  internal_data->waveform_parameter_units = create_waveform_parameter_units_dict();
692  XLAL_CHECK_FAIL(internal_data->waveform_parameter_units, XLAL_EFUNC);
693 
694  /* from astropy.units import Quantity */
695  fromlist = Py_BuildValue("(s)", "Quantity");
696  XLAL_CHECK_FAIL(fromlist, XLAL_EFAILED, "Could not build fromlist");
697  result = PyImport_ImportModuleEx("astropy.units", NULL, NULL, fromlist);
698  XLAL_CHECK_FAIL(result, XLAL_EFAILED, "Could not load module astropy.units");
699  Py_CLEAR(fromlist);
700 
701  internal_data->quantity = PyObject_GetAttrString(result, "Quantity");
702  XLAL_CHECK_FAIL(internal_data->quantity, XLAL_EFAILED, "Could not load Quantity from module astropy.units");
703  Py_CLEAR(result);
704 
705  /* import specified module */
706  internal_data->module = PyImport_ImportModule(module_name);
707  XLAL_CHECK_FAIL(internal_data->module, XLAL_EFAILED, "Could not find module %s", module_name);
708 
709  /* load specified object from module */
710  internal_data->object = PyObject_GetAttrString(internal_data->module, object_name);
711  XLAL_CHECK_FAIL(internal_data->object, XLAL_EFAILED, "Could not find object %s in module %s", object_name, module_name);
712 
713  /* parameters to pass off to the python generator's .__init__ */
714  genparams = XLALDictDuplicate(params);
715  XLALDictRemove(genparams, "module");
716  XLALDictRemove(genparams, "object");
717  kwargs = PyDict_FromLALDict(genparams, NULL, NULL);
718  XLAL_CHECK_FAIL(kwargs, XLAL_EFUNC);
719  XLALDestroyDict(genparams);
720  genparams = NULL;
721  args = PyTuple_New(0); /* empty args */
722  XLAL_CHECK_FAIL(args, XLAL_EFAILED, "Could not create tuple");
723 
724  /* create an instance of the specified object from module */
725  internal_data->instance = PyObject_Call(internal_data->object, args, kwargs);
726  XLAL_CHECK_FAIL(internal_data->instance, XLAL_EFAILED, "Could not create instance of object %s in module %s", object_name, module_name);
727  Py_CLEAR(args);
728  Py_CLEAR(kwargs);
729 
730  /* figure out what methods the object supports */
731 
732  /* generate_td_waveform */
733  if (PyObject_HasAttrString(internal_data->object, "generate_td_waveform")) {
734  internal_data->generate_td_waveform = PyObject_GetAttrString(internal_data->object, "generate_td_waveform");
735  XLAL_CHECK_FAIL(internal_data->generate_td_waveform, XLAL_EFAILED, "Failed to get method generate_td_waveform");
736  } else
737  internal_data->generate_td_waveform = NULL;
738 
739  /* generate_fd_waveform */
740  if (PyObject_HasAttrString(internal_data->object, "generate_fd_waveform")) {
741  internal_data->generate_fd_waveform = PyObject_GetAttrString(internal_data->object, "generate_fd_waveform");
742  XLAL_CHECK_FAIL(internal_data->generate_fd_waveform, XLAL_EFAILED, "Failed to get method generate_fd_waveform");
743  } else
744  internal_data->generate_fd_waveform = NULL;
745 
746  /* generate_td_modes */
747  if (PyObject_HasAttrString(internal_data->object, "generate_td_modes")) {
748  internal_data->generate_td_modes = PyObject_GetAttrString(internal_data->object, "generate_td_modes");
749  XLAL_CHECK_FAIL(internal_data->generate_td_modes, XLAL_EFAILED, "Failed to get method generate_td_modes");
750  } else
751  internal_data->generate_td_modes = NULL;
752 
753  /* generate_fd_modes */
754  if (PyObject_HasAttrString(internal_data->object, "generate_fd_modes")) {
755  internal_data->generate_fd_modes = PyObject_GetAttrString(internal_data->object, "generate_fd_modes");
756  XLAL_CHECK_FAIL(internal_data->generate_fd_modes, XLAL_EFAILED, "Failed to get method generate_fd_modes");
757  } else
758  internal_data->generate_fd_modes = NULL;
759 
760  /* make sure that at least one method is defined */
761  XLAL_CHECK_FAIL(internal_data->generate_td_waveform || internal_data->generate_fd_waveform || internal_data->generate_td_modes || internal_data->generate_fd_modes, XLAL_EFAILED, "Object %s in module %s must provide at least one following methods: generate_td_waveform, generate_fd_waveform, generate_td_modes, generate_fd_modes", object_name, module_name);
762 
763  /* attach those methods that are found to myself */
764  if (internal_data->generate_td_waveform)
765  myself->generate_td_waveform = generate_td_waveform;
766  if (internal_data->generate_fd_waveform)
767  myself->generate_fd_waveform = generate_fd_waveform;
768  if (internal_data->generate_td_modes)
769  myself->generate_td_modes = generate_td_modes;
770  if (internal_data->generate_fd_modes)
771  myself->generate_fd_modes = generate_fd_modes;
772 
773  myself->internal_data = internal_data;
774  return 0;
775 
776 XLAL_FAIL:
777  python_error();
778  free_internal_data(internal_data);
779  XLALDestroyDict(genparams);
780  Py_XDECREF(args);
781  Py_XDECREF(kwargs);
782  Py_XDECREF(result);
783  Py_XDECREF(fromlist);
784  return (int)XLAL_FAILURE;
785 }
786 
787 /* Free memory */
788 static int finalize(LALSimInspiralGenerator *myself)
789 {
790  XLAL_CHECK(myself, XLAL_EFAULT);
791  XLAL_CHECK(myself->internal_data, XLAL_EINVAL);
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;
798  /* don't Py_Finalize() since other things might be using the interpreter */
799  // Py_Finalize();
800  return 0;
801 }
802 
803 /* Generate time-domain polarizations for python generator */
804 static int generate_td_waveform(
805  REAL8TimeSeries **hplus,
806  REAL8TimeSeries **hcross,
807  LALDict *params,
808  LALSimInspiralGenerator *myself
809 )
810 {
812  PyObject *method;
813  PyObject *args = NULL;
814  PyObject *kwargs = NULL;
815  PyObject *result = NULL;
816  PyObject *hp; /* borrowed reference */
817  PyObject *hc; /* borrowed reference */
818 
819  /* sanity check arguments */
820  XLAL_CHECK(hplus, XLAL_EFAULT);
821  XLAL_CHECK(hcross, XLAL_EFAULT);
823  XLAL_CHECK(myself, XLAL_EFAULT);
824  XLAL_CHECK(*hplus == NULL, XLAL_EINVAL, "Argument hplus must be a pointer to NULL");
825  XLAL_CHECK(*hcross == NULL, XLAL_EINVAL, "Argument hcross must be a pointer to NULL");
826 
827  internal_data = myself->internal_data;
829 
830  method = internal_data->generate_td_waveform;
831  XLAL_CHECK(method, XLAL_EFAILED, "Object does not provide method %s", __func__);
832 
833  /* call method */
834  args = PyTuple_Pack(1, internal_data->instance); /* self */
835  XLAL_CHECK_FAIL(args, XLAL_EFAILED, "Failed to create tuple");
836  kwargs = PyDict_FromLALDict(params, internal_data->quantity, internal_data->waveform_parameter_units);
837  XLAL_CHECK_FAIL(kwargs, XLAL_EFUNC);
838  result = PyObject_Call(method, args, kwargs);
839  XLAL_CHECK_FAIL(result, XLAL_EFUNC, "Failed to call method %s", __func__);
840  Py_CLEAR(kwargs);
841  Py_CLEAR(args);
842 
843  /* unpack result */
844  /* should be a tuple of size 2 */
845  XLAL_CHECK_FAIL(PyTuple_Check(result), XLAL_EFAILED, "Invalid value returned by method %s", __func__);
846  XLAL_CHECK_FAIL(PyTuple_Size(result) == 2, XLAL_EFAILED, "Invalid value returned by method %s", __func__);
847  hp = PyTuple_GetItem(result, 0);
848  hc = PyTuple_GetItem(result, 1);
849  XLAL_CHECK_FAIL(hp && hc, XLAL_EFAILED, "Invalid value returned by method %s", __func__);
850 
851  /* convert results */
852  *hplus = GWpyTimeSeries_AsREAL8TimeSeries(hp);
853  XLAL_CHECK_FAIL(*hplus, XLAL_EFUNC);
854  *hcross = GWpyTimeSeries_AsREAL8TimeSeries(hc);
855  XLAL_CHECK_FAIL(*hcross, XLAL_EFUNC);
856 
857  Py_CLEAR(result);
858  Py_CLEAR(hp);
859  Py_CLEAR(hc);
860  return 0;
861 
862 XLAL_FAIL:
863  python_error();
866  Py_XDECREF(args);
867  Py_XDECREF(kwargs);
868  Py_XDECREF(result);
869  return (int)XLAL_FAILURE;
870 }
871 
872 /* Generate time-domain modes for python generator */
873 static int generate_td_modes(
874  SphHarmTimeSeries **hlm,
875  LALDict *params,
876  LALSimInspiralGenerator *myself
877 )
878 {
880  PyObject *method;
881  PyObject *args = NULL;
882  PyObject *kwargs = NULL;
883  PyObject *result = NULL;
884  PyObject *key; /* borrowed reference */
885  PyObject *val; /* borrowed reference */
886  Py_ssize_t pos = 0;
887 
888  /* sanity check arguments */
889  XLAL_CHECK(hlm, XLAL_EFAULT);
891  XLAL_CHECK(myself, XLAL_EFAULT);
892  XLAL_CHECK(*hlm == NULL, XLAL_EINVAL, "Argument hplus must be a pointer to NULL");
893 
894  internal_data = myself->internal_data;
896 
897  method = internal_data->generate_td_modes;
898  XLAL_CHECK(method, XLAL_EFAILED, "Object does not provide method %s", __func__);
899 
900  /* call method */
901  args = PyTuple_Pack(1, internal_data->instance); /* self */
902  XLAL_CHECK_FAIL(args, XLAL_EFAILED, "Failed to create tuple");
903  kwargs = PyDict_FromLALDict(params, internal_data->quantity, internal_data->waveform_parameter_units);
904  XLAL_CHECK_FAIL(kwargs, XLAL_EFUNC);
905  result = PyObject_Call(method, args, kwargs);
906  XLAL_CHECK_FAIL(result, XLAL_EFUNC, "Failed to call method %s", __func__);
907  Py_CLEAR(kwargs);
908  Py_CLEAR(args);
909 
910  /* unpack result */
911  /* make sure it is a dict */
912  XLAL_CHECK_FAIL(PyDict_Check(result), XLAL_EFAILED, "Invalid value returned by method %s", __func__);
913 
914  /* loop over items in dict */
915  while (PyDict_Next(result, &pos, &key, &val)) {
916  SphHarmTimeSeries *this;
917  PyObject *l; /* borrowed reference */
918  PyObject *m; /* borrowed reference */
919 
920  /* prepend this mode */
921  this = XLALCalloc(1, sizeof(*this));
923  this->next = *hlm;
924  *hlm = this;
925 
926  /* key should be a tuple of size 2 */
927  XLAL_CHECK_FAIL(PyTuple_Check(key) && PyTuple_Size(key) == 2, XLAL_EFAILED, "Invalid value returned by method %s", __func__);
928  l = PyTuple_GetItem(key, 0);
929  m = PyTuple_GetItem(key, 1);
930  XLAL_CHECK_FAIL(l && m && PyLong_Check(l) && PyLong_Check(m), XLAL_EFAILED, "Invalid value returned by method %s", __func__);
931 
932  /* convert results */
933  this->l = PyLong_AsUnsignedLong(l);
934  XLAL_CHECK_FAIL(!PyErr_Occurred(), XLAL_EFAILED, "Invalid value returned by method %s", __func__);
935  this->m = PyLong_AsLong(m);
936  XLAL_CHECK_FAIL(!PyErr_Occurred(), XLAL_EFAILED, "Invalid value returned by method %s", __func__);
937  this->mode = GWpyTimeSeries_AsCOMPLEX16TimeSeries(val);
938  XLAL_CHECK_FAIL(this->mode, XLAL_EFUNC);
939  }
940 
941  Py_CLEAR(result);
942  return 0;
943 
944 XLAL_FAIL:
945  python_error();
947  *hlm = NULL;
948  Py_XDECREF(args);
949  Py_XDECREF(kwargs);
950  Py_XDECREF(result);
951  return (int)XLAL_FAILURE;
952 }
953 
954 /* Generate Fourier-domain polarizations for python generator */
955 static int generate_fd_waveform(
956  COMPLEX16FrequencySeries **hplus,
957  COMPLEX16FrequencySeries **hcross,
958  LALDict *params,
959  LALSimInspiralGenerator *myself
960 )
961 {
963  PyObject *method;
964  PyObject *args = NULL;
965  PyObject *kwargs = NULL;
966  PyObject *result = NULL;
967  PyObject *hp; /* borrowed reference */
968  PyObject *hc; /* borrowed reference */
969 
970  /* sanity check arguments */
971  XLAL_CHECK(hplus, XLAL_EFAULT);
972  XLAL_CHECK(hcross, XLAL_EFAULT);
974  XLAL_CHECK(myself, XLAL_EFAULT);
975  XLAL_CHECK(*hplus == NULL, XLAL_EINVAL, "Argument hplus must be a pointer to NULL");
976  XLAL_CHECK(*hcross == NULL, XLAL_EINVAL, "Argument hcross must be a pointer to NULL");
977 
978  internal_data = myself->internal_data;
980 
981  method = internal_data->generate_fd_waveform;
982  XLAL_CHECK(method, XLAL_EFAILED, "Object does not provide method %s", __func__);
983 
984  /* call method */
985  args = PyTuple_Pack(1, internal_data->instance); /* self */
986  XLAL_CHECK_FAIL(args, XLAL_EFAILED, "Failed to create tuple");
987  kwargs = PyDict_FromLALDict(params, internal_data->quantity, internal_data->waveform_parameter_units);
988  XLAL_CHECK_FAIL(kwargs, XLAL_EFUNC);
989  result = PyObject_Call(method, args, kwargs);
990  XLAL_CHECK_FAIL(result, XLAL_EFUNC, "Failed to call method %s", __func__);
991  Py_CLEAR(kwargs);
992  Py_CLEAR(args);
993 
994  /* unpack result */
995  /* should be a tuple of size 2 */
996  XLAL_CHECK_FAIL(PyTuple_Check(result), XLAL_EFAILED, "Invalid value returned by method %s", __func__);
997  XLAL_CHECK_FAIL(PyTuple_Size(result) == 2, XLAL_EFAILED, "Invalid value returned by method %s", __func__);
998  hp = PyTuple_GetItem(result, 0);
999  hc = PyTuple_GetItem(result, 1);
1000  XLAL_CHECK_FAIL(hp && hc, XLAL_EFAILED, "Invalid value returned by method %s", __func__);
1001 
1002  /* convert results */
1003  *hplus = GWpyFrequencySeries_AsCOMPLEX16FrequencySeries(hp);
1004  XLAL_CHECK_FAIL(*hplus, XLAL_EFUNC);
1005  *hcross = GWpyFrequencySeries_AsCOMPLEX16FrequencySeries(hc);
1006  XLAL_CHECK_FAIL(*hcross, XLAL_EFUNC);
1007 
1008 
1009  Py_CLEAR(result);
1010  Py_CLEAR(hp);
1011  Py_CLEAR(hc);
1012  return 0;
1013 
1014 XLAL_FAIL:
1015  python_error();
1018  Py_XDECREF(args);
1019  Py_XDECREF(kwargs);
1020  Py_XDECREF(result);
1021  return (int)XLAL_FAILURE;
1022 }
1023 
1024 /* Generate Fourier-domain modes for python generator */
1025 static int generate_fd_modes(
1026  SphHarmFrequencySeries **hlm,
1027  LALDict *params,
1028  LALSimInspiralGenerator *myself
1029 )
1030 {
1031  struct internal_data *internal_data;
1032  PyObject *method;
1033  PyObject *args = NULL;
1034  PyObject *kwargs = NULL;
1035  PyObject *result = NULL;
1036  PyObject *key; /* borrowed reference */
1037  PyObject *val; /* borrowed reference */
1038  Py_ssize_t pos = 0;
1039 
1040  /* sanity check arguments */
1041  XLAL_CHECK(hlm, XLAL_EFAULT);
1043  XLAL_CHECK(myself, XLAL_EFAULT);
1044  XLAL_CHECK(*hlm == NULL, XLAL_EINVAL, "Argument hplus must be a pointer to NULL");
1045 
1046  internal_data = myself->internal_data;
1048 
1049  method = internal_data->generate_fd_modes;
1050  XLAL_CHECK(method, XLAL_EFAILED, "Object does not provide method %s", __func__);
1051 
1052  /* call method */
1053  args = PyTuple_Pack(1, internal_data->instance); /* self */
1054  XLAL_CHECK_FAIL(args, XLAL_EFAILED, "Failed to create tuple");
1055  kwargs = PyDict_FromLALDict(params, internal_data->quantity, internal_data->waveform_parameter_units);
1056  XLAL_CHECK_FAIL(kwargs, XLAL_EFUNC);
1057  result = PyObject_Call(method, args, kwargs);
1058  XLAL_CHECK_FAIL(result, XLAL_EFUNC, "Failed to call method %s", __func__);
1059  Py_CLEAR(kwargs);
1060  Py_CLEAR(args);
1061 
1062  /* unpack result */
1063  /* make sure it is a dict */
1064  XLAL_CHECK_FAIL(PyDict_Check(result), XLAL_EFAILED, "Invalid value returned by method %s", __func__);
1065 
1066  /* loop over items in dict */
1067  while (PyDict_Next(result, &pos, &key, &val)) {
1068  SphHarmFrequencySeries *this;
1069  PyObject *l; /* borrowed reference */
1070  PyObject *m; /* borrowed reference */
1071 
1072  /* prepend this mode */
1073  this = XLALCalloc(1, sizeof(*this));
1075  this->next = *hlm;
1076  *hlm = this;
1077 
1078  /* key should be a tuple of size 2 */
1079  XLAL_CHECK_FAIL(PyTuple_Check(key) && PyTuple_Size(key) == 2, XLAL_EFAILED, "Invalid value returned by method %s", __func__);
1080  l = PyTuple_GetItem(key, 0);
1081  m = PyTuple_GetItem(key, 1);
1082  XLAL_CHECK_FAIL(l && m && PyLong_Check(l) && PyLong_Check(m), XLAL_EFAILED, "Invalid value returned by method %s", __func__);
1083 
1084  /* convert results */
1085  this->l = PyLong_AsUnsignedLong(l);
1086  XLAL_CHECK_FAIL(!PyErr_Occurred(), XLAL_EFAILED, "Invalid value returned by method %s", __func__);
1087  this->m = PyLong_AsLong(m);
1088  XLAL_CHECK_FAIL(!PyErr_Occurred(), XLAL_EFAILED, "Invalid value returned by method %s", __func__);
1089  this->mode = GWpyFrequencySeries_AsCOMPLEX16FrequencySeries(val);
1090  XLAL_CHECK_FAIL(this->mode, XLAL_EFUNC);
1091  }
1092 
1093  Py_CLEAR(result);
1094  return 0;
1095 
1096 XLAL_FAIL:
1097  python_error();
1099  *hlm = NULL;
1100  Py_XDECREF(args);
1101  Py_XDECREF(kwargs);
1102  Py_XDECREF(result);
1103  return (int)XLAL_FAILURE;
1104 }
1105 
1106 /* Define methods supported by the python generator.
1107  * For any external python model, the approximant name must be ExternalPython.
1108  * The path to the python code that actually generates the model is stored in internal_data.module and .object
1109  */
1110 const LALSimInspiralGenerator lalPythonGeneratorTemplate = {
1111  .name = "ExternalPython",
1112  .initialize = initialize,
1113  .finalize = finalize,
1114  .generate_td_waveform = generate_td_waveform,
1115  .generate_fd_waveform = generate_fd_waveform,
1116  .generate_td_modes = generate_td_modes,
1117  .generate_fd_modes = generate_fd_modes,
1118  .internal_data = NULL
1119 };
1120 
1121 #endif /* HAVE_PYTHON_EMBED */
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)
static struct @1 lalSimInspiralREAL8WaveformParams[]
const char * name
const char * unit
const char * XLALValueGetString(const LALValue *value)
LALTYPECODE XLALValueGetType(const LALValue *value)
REAL8 XLALValueGetREAL8(const LALValue *value)
INT4 XLALValueGetINT4(const LALValue *value)
int l
Definition: bh_qnmode.c:135
double i
Definition: bh_ringdown.c:118
void XLALDestroyCOMPLEX16FrequencySeries(COMPLEX16FrequencySeries *series)
#define XLAL_NUM_ELEM(x)
double complex COMPLEX16
double REAL8
LAL_CHAR_TYPE_CODE
LAL_D_TYPE_CODE
LAL_I4_TYPE_CODE
LALUnitIndexKelvin
LALUnitIndexMeter
LALUnitIndexKiloGram
LALUnitIndexSecond
LALUnitIndexADCCount
LALUnitIndexStrain
LALUnitIndexAmpere
void * XLALCalloc(size_t m, size_t n)
void XLALFree(void *p)
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.
static const INT4 m
void XLALDestroyREAL8TimeSeries(REAL8TimeSeries *series)
#define XLAL_ERROR_NULL(...)
int XLALGetBaseErrno(void)
#define XLAL_ERROR(...)
#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,...)
int XLALClearErrno(void)
#define XLAL_PRINT_ERROR(...)
#define XLAL_ERROR_FAIL(...)
XLAL_ENOMEM
XLAL_EFAULT
XLAL_EFUNC
XLAL_ETYPE
XLAL_ESIZE
XLAL_EINVAL
XLAL_EFAILED
XLAL_FAILURE
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.
Definition: burst.c:245