Fix missing import in finiterectlat-scatter.py
[qpms.git] / qpms / cybspec.pyx
blob998327268056320c9b8c0b3201b538cad9463bf5
1 import numpy as np
2 import enum
3 from .cycommon import get_mn_y, tlm2uvswfi
4 __VSWF_norm_dict = {
5 'INVERSE' : QPMS_NORMALISATION_INVERSE,
6 'REVERSE_AZIMUTHAL_PHASE' : QPMS_NORMALISATION_REVERSE_AZIMUTHAL_PHASE,
7 'SPHARM_REAL' : QPMS_NORMALISATION_SPHARM_REAL,
8 'M_I' : QPMS_NORMALISATION_M_I,
9 'M_MINUS' : QPMS_NORMALISATION_M_MINUS,
10 'N_I' : QPMS_NORMALISATION_N_I,
11 'N_MINUS' : QPMS_NORMALISATION_N_MINUS,
12 'L_I' : QPMS_NORMALISATION_L_I,
13 'L_MINUS' : QPMS_NORMALISATION_L_MINUS,
14 'CSPHASE' : QPMS_NORMALISATION_CSPHASE,
15 'UNNORM' : QPMS_NORMALISATION_NORM_NONE,
16 'UNNORM_CS' : QPMS_NORMALISATION_NORM_NONE | QPMS_NORMALISATION_CSPHASE,
17 'POWERNORM' : QPMS_NORMALISATION_NORM_POWER,
18 'POWERNORM_CS' : QPMS_NORMALISATION_NORM_POWER | QPMS_NORMALISATION_CSPHASE,
19 'SPHARMNORM' : QPMS_NORMALISATION_NORM_SPHARM,
20 'SPHARMNORM_CS' : QPMS_NORMALISATION_NORM_SPHARM | QPMS_NORMALISATION_CSPHASE,
21 'UNDEF' : QPMS_NORMALISATION_UNDEF,
22 'DEFAULT' : QPMS_NORMALISATION_DEFAULT,
25 try:
26 VSWFNorm = enum.IntFlag('VSWFNorm', names=__VSWF_norm_dict, module=__name__)
27 except AttributeError: # For older Python versions, use IntEnum instead
28 VSWFNorm = enum.IntEnum('VSWFNorm', names=__VSWF_norm_dict, module=__name__)
31 cdef class BaseSpec:
32 '''Cython wrapper over qpms_vswf_set_spec_t.
34 It should be kept immutable. The memory is managed by numpy/cython, not directly by the C functions, therefore
35 whenever used in other wrapper classes that need the pointer
36 to qpms_vswf_set_spec_t, remember to set a (private, probably immutable) reference to qpms.basespec to ensure
37 correct reference counting and garbage collection.
38 '''
39 #cdef qpms_vswf_set_spec_t s # in pxd
40 #cdef np.ndarray __ilist # in pxd
41 #cdef const qpms_uvswfi_t[:] __ilist
43 @staticmethod
44 cdef BaseSpec from_cpointer(const qpms_vswf_set_spec_t *orig):
45 '''Makes an instance of BaseSpec from an existing
46 C pointer, copying the contents
47 '''
48 cdef const qpms_uvswfi_t[::1] ilist_orig = <qpms_uvswfi_t[:orig[0].n]> orig[0].ilist
49 cdef BaseSpec bs = BaseSpec(ilist_orig)
50 bs.s.norm = orig[0].norm
51 return bs
53 def __cinit__(self, *args, **kwargs):
54 cdef const qpms_uvswfi_t[:] ilist_memview
55 if len(args) == 0:
56 if 'lMax' in kwargs.keys(): # if only lMax is specified, create the 'usual' definition in ('E','M') order
57 lMax = kwargs['lMax']
58 my, ny = get_mn_y(lMax)
59 nelem = len(my)
60 tlist = nelem * (QPMS_VSWF_ELECTRIC,) + nelem * (QPMS_VSWF_MAGNETIC,)
61 mlist = 2*list(my)
62 llist = 2*list(ny)
63 ilist = tlm2uvswfi(tlist,llist,mlist)
64 else:
65 raise ValueError
66 else: # len(args) > 0:
67 ilist = args[0]
68 #self.__ilist = np.array(args[0], dtype=qpms_uvswfi_t, order='C', copy=True) # FIXME define the dtypes at qpms_cdef.pxd level
69 self.__ilist = np.array(ilist, dtype=np.ulonglong, order='C', copy=True)
70 self.__ilist.setflags(write=False)
71 ilist_memview = self.__ilist
72 self.s.ilist = &ilist_memview[0]
73 self.s.n = len(self.__ilist)
74 self.s.capacity = 0 # is this the best way?
75 if 'norm' in kwargs.keys():
76 self.s.norm = kwargs['norm']
77 else:
78 self.s.norm = <qpms_normalisation_t>(QPMS_NORMALISATION_DEFAULT)
79 # set the other metadata
80 cdef qpms_l_t l
81 self.s.lMax_L = -1
82 cdef qpms_m_t m
83 cdef qpms_vswf_type_t t
84 for i in range(self.s.n):
85 if(qpms_uvswfi2tmn(ilist_memview[i], &t, &m, &l) != QPMS_SUCCESS):
86 raise ValueError("Invalid uvswf index")
87 if (t == QPMS_VSWF_ELECTRIC):
88 self.s.lMax_N = max(self.s.lMax_N, l)
89 elif (t == QPMS_VSWF_MAGNETIC):
90 self.s.lMax_M = max(self.s.lMax_M, l)
91 elif (t == QPMS_VSWF_LONGITUDINAL):
92 self.s.lMax_L = max(self.s.lMax_L, l)
93 else:
94 raise ValueError # If this happens, it's probably a bug, as it should have failed already at qpms_uvswfi2tmn
95 self.s.lMax = max(self.s.lMax, l)
97 def __eq__(self, BaseSpec other):
98 return bool(qpms_vswf_set_spec_isidentical(&self.s, &other.s))
100 def __hash__(self): # Very inefficient implementation, but this is not to be used very often
101 return hash((self.s.norm, self.s.n, tuple(self.__ilist[:self.s.n])))
103 def tlm(self):
104 cdef const qpms_uvswfi_t[:] ilist_memview = <qpms_uvswfi_t[:self.s.n]> self.s.ilist
105 #cdef qpms_vswf_type_t[:] t = np.empty(shape=(self.s.n,), dtype=qpms_vswf_type_t) # does not work, workaround:
106 cdef size_t i
107 cdef np.ndarray ta = np.empty(shape=(self.s.n,), dtype=np.intc)
108 cdef int[:] t = ta
109 #cdef qpms_l_t[:] l = np.empty(shape=(self.s.n,), dtype=qpms_l_t) # FIXME explicit dtype again
110 cdef np.ndarray la = np.empty(shape=(self.s.n,), dtype=np.intc)
111 cdef qpms_l_t[:] l = la
112 #cdef qpms_m_t[:] m = np.empty(shape=(self.s.n,), dtype=qpms_m_t) # FIXME explicit dtype again
113 cdef np.ndarray ma = np.empty(shape=(self.s.n,), dtype=np.intc)
114 cdef qpms_m_t[:] m = ma
115 for i in range(self.s.n):
116 qpms_uvswfi2tmn(self.s.ilist[i], <qpms_vswf_type_t*>&t[i], &m[i], &l[i])
117 return (ta, la, ma)
119 def m(self): # ugly
120 return self.tlm()[2]
122 def t(self): # ugly
123 return self.tlm()[0]
125 def l(self): # ugly
126 return self.tlm()[1]
128 def __len__(self):
129 return self.s.n
131 def __getitem__(self, key):
132 # TODO raise correct errors (TypeError on bad type of key, IndexError on exceeding index)
133 return self.__ilist[key]
135 property ilist:
136 def __get__(self):
137 return self.__ilist
139 cdef qpms_vswf_set_spec_t *rawpointer(BaseSpec self):
140 '''Pointer to the qpms_vswf_set_spec_t structure.
141 Don't forget to reference the BaseSpec object itself when storing the pointer anywhere!!!
143 return &(self.s)
145 property rawpointer:
146 def __get__(self):
147 return <uintptr_t> &(self.s)
149 property norm:
150 def __get__(self):
151 return VSWFNorm(self.s.norm)
153 default_bspec = BaseSpec(lMax=2)