Loading [MathJax]/extensions/TeX/AMSsymbols.js
LALSimulation 6.2.0.1-8a6b96f
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
utils.py
Go to the documentation of this file.
1from operator import xor
2import lal
3import lalsimulation as lalsim
4import numpy as np
5from astropy import units as u
6
7from . import parameter_conventions as pc
8import warnings
9
10def from_lal_value(val):
11 """
12 Read and return a value from LALDict
13
14 Parameters
15 ----------
16 val : `lal.DictValues` value
17 Output from a lal.DictValues
18
19 Returns
20 -------
21 lal.Value'(val) :
22 """
23 ltype = lal.ValueGetType(val)
24 if ltype == lal.CHAR_TYPE_CODE:
25 size = lal.ValueGetSize(val)
26 if size == 1:
27 return lal.ValueGetCHAR(val)
28 else:
29 return lal.ValueGetString(val)
30 if ltype == lal.I2_TYPE_CODE:
31 return lal.ValueGetINT2(val)
32 if ltype == lal.I4_TYPE_CODE:
33 return lal.ValueGetINT4(val)
34 if ltype == lal.I8_TYPE_CODE:
35 return lal.ValueGetINT8(val)
36 if ltype == lal.U2_TYPE_CODE:
37 return lal.ValueGetUINT2(val)
38 if ltype == lal.U4_TYPE_CODE:
39 return lal.ValueGetUINT4(val)
40 if ltype == lal.U8_TYPE_CODE:
41 return lal.ValueGetUINT8(val)
42 if ltype == lal.S_TYPE_CODE:
43 return lal.ValueGetREAL4(val)
44 if ltype == lal.D_TYPE_CODE:
45 return lal.ValueGetREAL8(val)
46 if ltype == lal.C_TYPE_CODE:
47 return lal.ValueGetCOMPLEX8(val)
48 if ltype == lal.Z_TYPE_CODE:
49 return lal.ValueGetCOMPLEX16(val)
50 else:
51 raise TypeError('invalid lal typecode %d' % ltype)
52
53def to_lal_dict(d):
54 """
55 Convert Python dictionary to LALDict object readble by LAL
56
57 Parameters
58 ----------
59 d : `dict`
60 Python dictionary of input parameters
61
62 Returns
63 -------
64 ldict : `LALDict`
65 LALDict object readable by LAL
66 """
67 if d is None:
68 return None
69 ldict = lal.CreateDict()
70 for k, v in d.items():
71 if k == 'ModeArray':
72 lalsim.SimInspiralWaveformParamsInsertModeArrayFromModeString(ldict, v)
73 elif k =='ModeArrayJframe':
74 lalsim.SimInspiralWaveformParamsInsertModeArrayJframeFromModeString(ldict, v)
75 else:
76 if isinstance(v, np.generic):
77 v = v.item()
78 elif type(v) is u.quantity.Quantity:
79 v_val = v.si.value
80 lal.DictInsertREAL8Value(ldict, k, v_val)
81 continue
82 if type(v) is float or lalsim.SimInspiralCheckKnownREAL8Key(k):
83 lal.DictInsertREAL8Value(ldict, k, v)
84 elif type(v) is int:
85 lal.DictInsertINT4Value(ldict, k, v)
86 elif type(v) is str:
87 lal.DictInsertStringValue(ldict, k, v)
88 else:
89 #TODO: handle other types?
90 raise TypeError
91 return ldict
92
93def from_lal_dict(ldict):
94 """
95 Convert LALDict object to a Python dictionary
96
97 Parameters
98 ----------
99 d : `LALDict`
100 LALDict object
101
102 Returns
103 -------
104 d : `dict`
105 Python dictionary
106 """
107 if ldict is None:
108 return None
109 d = dict()
110 keys = lal.DictKeys(ldict)
111 vals = lal.DictValues(ldict)
112 size = lal.ListSize(keys)
113 for i in range(size):
114 key = lal.ListItemGetStringValue(lal.ListPop(keys))
115 lal_item = lal.ListPop(vals)
116 # The ModeArray has LAL_CHAR_TYPE_CODE, but it is not a literal string
117 # from_lal_value would get confused and print weird characters, so we do the distinction below
118 if 'ModeArray' in key:
119 val = lalsim.SimInspiralModeArrayToModeString(lal.ListItemGetValue(lal_item))
120 else:
121 val = from_lal_value(lal.ListItemGetValue(lal_item))
122 d[key] = val
123 return d
124
125
126# Functions to check the parameters and/or add units to them
127
128def check_dict_parameters(waveform_dict,generic_param_dict=None):
129 """Checks the parameters used in the waveform generation routine.
130
131 Parameters
132 ----------
133 waveform_dict (dict): The dictionary of parameters used to generate the template.
134
135 generic_param_dict (dict,optional): Dictionary of extra parameter names to be accepted in order to generate
136 non-standard waveforms. It should not include standard waveform parameters as they will
137 be ignored. The form should be parameter_name:parameter_units and the values should be just
138 added in the waveform_dict.
139 Raises
140 ------
141 AssertionError: If a parameter has the wrong units.
142 TypeError: If a parameter is not available to use or a dimensional parameter
143 is passed as dimensionless.
144
145 """
146 #Check that masses are correctly input
147 CheckDeterminationOfMasses(waveform_dict)
148 #Check that spins are correctly input
149 CheckDeterminationOfSpins(waveform_dict)
150 #Check the units of the different quantities
151 default_unit_sys = 'S.I.' #International System by default
152
153
154 #Check and extend parameter list if applicable
155 if generic_param_dict is not None: full_parameter_list = np.concatenate([pc.full_parameter_list,list(generic_param_dict.keys())])
156 else : full_parameter_list = pc.full_parameter_list
157
158 # Check if python dictionary contains any key not included in this list & units of selected parameters
159 for k in waveform_dict.keys():
160 #Check all parameters are in joint list otherwise gives error
161 if k not in full_parameter_list:
162 raise(TypeError( ("Parameter %s not in accepted list of parameters"%(k))))
163 #Check the units of the parameteres are correct
164 elif k in pc.units_dict[default_unit_sys].keys():
165 try : waveform_dict[k].unit #Check if it has units at all. Otherwise will give no clue about the parameter giving error
166 except: raise(TypeError( ("Parameter {} does not have units, please pass a parameter with astropy units equivalent to u.[{}]".format(k,pc.units_dict[default_unit_sys][k]))))
167 assert waveform_dict[k].unit.is_equivalent(pc.units_dict[default_unit_sys][k]), "Parameter {} does not have proper units, units should be equivalent to u.[{}]".format(k,pc.units_dict[default_unit_sys][k])
168 elif k=='condition':
169 if int(waveform_dict[k])==0 or int(waveform_dict[k])==1:
170 continue
171 else:
172 raise(TypeError("Condition should only be 0 or 1"))
173 elif k=='lmax':
174 if waveform_dict[k]<0:
175 raise(ValueError("lmax must be >=0"))
176 continue
177 elif generic_param_dict is not None:
178 try : waveform_dict[k].unit #Check if it has units at all. Otherwise will give no clue about the parameter giving error
179 except: raise(TypeError( ("Parameter {} does not have units, please pass a parameter with astropy units equivalent to u.[{}]".format(k,generic_param_dict[k]))))
180 assert waveform_dict[k].unit.is_equivalent(generic_param_dict[k]), "Parameter {} does not have proper units, units should be equivalent to u.[{}]".format(k,generic_param_dict[k])
181
182
183
184
185def add_params_units(waveform_dict,units_sys='S.I.',generic_param_dict=None):
186 """Add units or convert to desired units system to the waveform parameters dictionary
187
188 Parameters
189 ----------
190 waveform_dict (dict): The dictionary of parameters used to generate the template.
191 units_sys (:obj:`str`, optional): System of units chosen for the given parameters.
192 Defaults to None. If a unit system is given, try to convert and provide
193 units to the parameters in `waveform_dict`. If default checks the parameters in `waveform_dict`
194 have the appropriate units.
195 generic_param_dict (dict,optional): Dictionary of extra parameter names to be accepted in order to generate
196 non-standard waveforms. It should not include standard waveform parameters as they will
197 be ignored. The form should be parameter_name:parameter_units and the values should be just
198 added in the waveform_dict.
199 Returns
200 -------
201 A dict the corresponding conversions to the
202 specified `units_sys` system.
203
204 Raises
205 ------
206 AssertionError: If a parameter has wrong units
207
208 """
209 # Main purpose is to check parameters names/units and convert them to astropy SI units
210 # we keep all parameter names and conventions in a separate file
211
212 if units_sys in pc.units_dict.keys():#If units system is in units_dict we give units/convert to given units
213 dict_tmp = {key:u.Quantity(value,pc.units_dict[units_sys][key]) for (key,value) in waveform_dict.items() if key in pc.units_dict[units_sys].keys()}
214 else:
215 raise(TypeError('The units system specified is not available. Available systems are {}'.format([key for key in pc.units_dict.keys()])))
216
217 # Adding units also to the non-standard parameters
218
219 if generic_param_dict is not None:
220 dict_tmp = {**dict_tmp, **{key:u.Quantity(value,generic_param_dict[key]) for (key,value) in waveform_dict.items() if key in generic_param_dict.keys()}}
221
222 #Merge dictionaries keeping those values of dict_tmp as they have been given units
223 dict_tmp = {**waveform_dict,**dict_tmp}
224
225 #Some sanity checks
226 for par in pc.mass_params_[0:4]:
227 if par in dict_tmp.keys():
228 if dict_tmp[par]>=2*10**30*u.solMass:
229 warn_string = "Are you sure you want to have a {} of {} Solar Masses?".format(par,u.Quantity(dict_tmp[par],u.solMass).value)
230 warnings.warn(warn_string)
231 if 'mass_ratio' in dict_tmp.keys():
232 if (dict_tmp['mass_ratio']<0.001) or (dict_tmp['mass_ratio']>1000.0):
233 warn_string = "Are you sure you want to have a q of {}?".format(dict_tmp['mass_ratio'])
234 warnings.warn(warn_string)
235 # Extremely high mass 2*10^30 Msol
236 # Extreme mass ratio
237
238 return dict_tmp
239
240
241
242def CheckDeterminationOfMasses(waveform_dict):
243
244 """Check mass parameters are consistent and enough to fully characterize the binary masses
245
246 Parameters
247 ----------
248 waveform_dict (dict): The dictionary of parameters used to generate the template.
249
250 Raises
251 ------
252 TypeError: whenever mass parameters are over or underspecified
254 """
255
256 dim_number = 0 # dimensionful-mass counter
257 nodim_number = 0 # dimensionless-mass counter
258 sym_number = 0 # symmetric masses counter
259
260 #Dictionaries
261 dimensionless_masses = ["mass_ratio", "sym_mass_ratio"]
262 dimensionful_masses = ["mass1", "mass2", "total_mass","chirp_mass", "mass_difference", "reduced_mass"]
263 symetric_masses = ["mass1", "mass2", "total_mass", "chirp_mass", "sym_mass_ratio", "reduced_mass"]
264
265
266 for param in dimensionful_masses:
267 if param in waveform_dict.keys(): dim_number += 1
268 for param in dimensionless_masses:
269 if param in waveform_dict.keys(): nodim_number += 1
270 for param in symetric_masses:
271 if param in waveform_dict.keys(): sym_number += 1
272 if ("mass1" in waveform_dict.keys()) & ("mass2" in waveform_dict.keys()): sym_number = 0
273
274 if ((dim_number == 2 and nodim_number == 0) or (dim_number == 1 and nodim_number == 1)):
275 if(sym_number == 2):
276 warn_string = "The larger object cannot be determined, assuming m1 >= m2."
277 warnings.warn(warn_string)
278 elif ((dim_number == 1 and nodim_number == 0) or dim_number == 0):
279 raise(TypeError( "Mass parameters are underspecified. Please include" \
280 " one dimensionless and one dimensionful mass parameters, or two dimensionful masses."))
281 else:
282 raise(TypeError( "Mass parameters are overspecified. Please include" \
283 " one dimensionless and one dimensionful mass parameters, or two dimensionful masses."))
284
285def CheckDeterminationOfSpins(waveform_dict):
286 """Check spin parameters are consistent and enough to fully characterize the binary spins
287
288 Parameters
289 ----------
290 waveform_dict (dict): The dictionary of parameters used to generate the template.
291
292 Raises
293 ------
294 TypeError: whenever spin parameters are over or underspecified or system of coordinates is mixed
295
296 """
297 #Counters
298 spin1_number = 0
299 spin2_number = 0
300 #Logical variables
301 cartesian_1 = False
302 cartesian_2 = False
303 spherical_1 = False
304 spherical_2 = False
305
306
307 #Suffixes for the spin parameters in the 2 different coordinates systems
308 cartesian = ['x','y','z']
309 spherical = ['_norm','_tilt','_phi']
310
311 for sfx in cartesian:
312 if 'spin1'+sfx in waveform_dict.keys(): spin1_number += 1
313 if 'spin2'+sfx in waveform_dict.keys(): spin2_number += 1
314
315 if spin1_number >0: cartesian_1=True
316 if spin2_number >0: cartesian_2=True
317
318 spin1_number = 0
319 spin2_number = 0
320
321 for sfx in spherical:
322 if 'spin1'+sfx in waveform_dict.keys(): spin1_number += 1
323 if 'spin2'+sfx in waveform_dict.keys(): spin2_number += 1
324
325 if spin1_number >0: spherical_1=True
326 if spin2_number >0: spherical_2=True
327
328 if not(xor(cartesian_1,spherical_1)) or not(xor(cartesian_2,spherical_2)):
329 raise(TypeError( "Please specify the 3 spin parameters in either spherical or cartesian coordinates."))
def check_dict_parameters(waveform_dict, generic_param_dict=None)
Checks the parameters used in the waveform generation routine.
Definition: utils.py:145
def CheckDeterminationOfSpins(waveform_dict)
Check spin parameters are consistent and enough to fully characterize the binary spins.
Definition: utils.py:296
def from_lal_dict(ldict)
Convert LALDict object to a Python dictionary.
Definition: utils.py:106
def add_params_units(waveform_dict, units_sys='S.I.', generic_param_dict=None)
Add units or convert to desired units system to the waveform parameters dictionary.
Definition: utils.py:208
def CheckDeterminationOfMasses(waveform_dict)
Check mass parameters are consistent and enough to fully characterize the binary masses.
Definition: utils.py:253
def from_lal_value(val)
Read and return a value from LALDict.
Definition: utils.py:22
def to_lal_dict(d)
Convert Python dictionary to LALDict object readble by LAL.
Definition: utils.py:66