LALSimulation 6.2.0.1-eeff03c
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