/
core.py
210 lines (183 loc) · 7.35 KB
/
core.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# Licensed under a 3-clause BSD style license - see LICENSE.rst
from __future__ import print_function
import re
import astropy.units as u
import astropy.io.ascii as asciitable
from ..query import BaseQuery
from ..utils import async_to_sync, prepend_docstr_nosections
from . import conf
from ..exceptions import TableParseError
__all__ = ['Nist', 'NistClass']
def _strip_blanks(table):
"""
Remove blank lines from table (included for "human readability" but
useless to us...
returns a single string joined by \n newlines
Parameters
----------
table : str
table to strip as a string
Returns
-------
single string joined by newlines.
"""
numbersletters = re.compile("[0-9A-Za-z]")
if isinstance(table, str):
table = table.split('\n')
table = [line for line in table if numbersletters.search(line)]
return "\n".join(table)
@async_to_sync
class NistClass(BaseQuery):
URL = conf.server
TIMEOUT = conf.timeout
unit_code = {'Angstrom': 0,
'nm': 1,
'um': 2}
energy_level_code = {'cm-1': 0, 'invcm': 0, 'cm': 0,
'ev': 1, 'eV': 1, 'EV': 1, 'electronvolt': 1,
'R': 2, 'Rydberg': 2, 'rydberg': 2}
order_out_code = {'wavelength': 0,
'multiplet': 1}
wavelength_unit_code = {'vacuum': 3,
'vac+air': 4}
def _args_to_payload(self, *args, **kwargs):
"""
Serves the same purpose as `~NistClass.query` but returns
the raw HTTP response rather than a `~astropy.table.Table` object.
Parameters
----------
minwav : `astropy.units.Quantity` object
The lower wavelength for the spectrum in appropriate units.
maxwav : `astropy.units.Quantity` object
The upper wavelength for the spectrum in appropriate units.
linename : str, optional
The spectrum to fetch. Defaults to "H I"
energy_level_unit : str, optional
The energy level units must be one of the following:
'R', 'Rydberg', 'rydberg', 'cm', 'cm-1', 'EV', 'eV',
'electronvolt', 'ev', 'invcm' Defaults to 'eV'.
output_order : str, optional
Decide ordering of output. Must be one of following:
['wavelength', 'multiplet']. Defaults to 'wavelength'.
wavelength_type : str, optional
Must be one of 'vacuum' or 'vac+air'. Defaults to 'vacuum'.
get_query_payload : bool, optional
If true then returns the dictionary of query parameters, posted to
remote server. Defaults to `False`.
Returns
-------
request_payload : dict
The dictionary of parameters sent with the HTTP request
"""
request_payload = {}
request_payload["spectra"] = kwargs['linename']
(min_wav, max_wav, wav_unit) = _parse_wavelength(args[0], args[1])
request_payload["low_wl"] = min_wav
request_payload["upp_wl"] = max_wav
request_payload["unit"] = wav_unit
request_payload["submit"] = "Retrieve Data"
request_payload["format"] = 1 # ascii
request_payload["line_out"] = 0 # All lines
request_payload["en_unit"] = Nist.energy_level_code[
kwargs["energy_level_unit"]]
request_payload["output"] = 0 # entirely rather than pagewise
request_payload["bibrefs"] = 1
request_payload["show_obs_wl"] = 1
request_payload["show_calc_wl"] = 1
request_payload["order_out"] = Nist.order_out_code[
kwargs['output_order']]
request_payload["max_low_enrg"] = ""
request_payload["show_av"] = Nist.wavelength_unit_code[
kwargs['wavelength_type']]
request_payload["max_upp_enrg"] = ""
request_payload["tsb_value"] = 0
request_payload["min_str"] = ""
request_payload["A_out"] = 0
request_payload["f_out"] = "on"
request_payload["intens_out"] = "on"
request_payload["max_str"] = ""
request_payload["allowed_out"] = 1
request_payload["forbid_out"] = 1
request_payload["min_accur"] = ""
request_payload["min_intens"] = ""
request_payload["conf_out"] = "on"
request_payload["term_out"] = "on"
request_payload["enrg_out"] = "on"
request_payload["J_out"] = "on"
request_payload["page_size"] = 15
return request_payload
@prepend_docstr_nosections("\n" + _args_to_payload.__doc__)
def query_async(self, minwav, maxwav, linename="H I",
energy_level_unit='eV', output_order='wavelength',
wavelength_type='vacuum', get_query_payload=False):
"""
Returns
-------
response : `requests.Response` object
The response of the HTTP request.
"""
request_payload = self._args_to_payload(
minwav, maxwav, linename=linename,
energy_level_unit=energy_level_unit, output_order=output_order,
wavelength_type=wavelength_type)
if get_query_payload:
return request_payload
response = self._request("GET", url=Nist.URL, params=request_payload,
timeout=Nist.TIMEOUT)
return response
def _parse_result(self, response, verbose=False):
"""
Parses the results form the HTTP response to `astropy.table.Table`.
Parameters
----------
response : `requests.Response`
The HTTP response object
Returns
-------
table : `astropy.table.Table`
"""
pre_re = re.compile("<pre>(.*)</pre>", flags=re.DOTALL)
links_re = re.compile(r"<a.*?>\s*(\w+)\s*</a>")
content = str(response.text)
try:
pre = pre_re.findall(content)[0]
except IndexError:
raise Exception("Result did not contain a table")
try:
table = _strip_blanks(pre)
table = links_re.sub(r'\1', table)
table = asciitable.read(table, Reader=asciitable.FixedWidth,
data_start=3, delimiter='|')
return table
except Exception as ex:
self.response = response
self.table_parse_error = ex
raise TableParseError("Failed to parse asciitable! The raw "
"response can be found in self.response, "
"and the error in self.table_parse_error.")
Nist = NistClass()
def _parse_wavelength(min_wav, max_wav):
"""
Helper function to return wavelength and units in form accepted by NIST
Parameters
----------
min_wav : `astropy.units.Quantity` object
The lower wavelength in proper units.
max_wav : `astropy.units.Quantity` object
The upper wavelength in proper units.
Returns
-------
tuple : (number, number, number)
The value of lower, upper wavelength and their unit.
"""
max_wav = max_wav.to(min_wav.unit)
wav_unit = min_wav.unit.to_string()
if wav_unit not in Nist.unit_code:
max_wav = max_wav.to(u.angstrom).value
min_wav = min_wav.to(u.angstrom).value
wav_unit = Nist.unit_code['Angstrom']
else:
max_wav = max_wav.value
min_wav = min_wav.value
wav_unit = Nist.unit_code[wav_unit]
return (min_wav, max_wav, wav_unit)