Source code for pyLBL.webapi.tips_api

"""Defines the API for interacting with the TIPS data stored on the web."""

from re import match
from urllib.request import urlopen

from numpy import asarray, float32, transpose


[docs]class TipsWebApi(object): """Controls access to TIPS web API. Attributes: url: String url where data is downloaded from. """ def __init__(self): """Constructs the object.""" self.url = "http://faculty.uml.edu/Robert_Gamache/Software/temp/Supplementary_file.txt"
[docs] def download(self, molecule): """Downloads the data from the internet. Args: molecule: String molecule chemical formula. Returns: temperature: Numpy array of temperatures. data: Numpy array of data values. """ return self._parse_records(self._records(urlopen(self.url), molecule))
@staticmethod def _ascii_table_records(response, block_size=512): """Reads the next line from an ascii table. Args: response: A http.client.HTTPResponse object. block_size: Size in bytes of blocks that will be read. Yields: String record of the http table. """ record = "" while True: block = response.read(block_size).decode("utf-8") lines = block.split("\n") if lines[-1] == "": # If a block ends with a new line character, delete the last # element of the list because it will be an empty string. del lines[-1] for line in lines[:-1]: # Return complete lines within a block. record += line yield record record = "" if len(block) != block_size: # This is the last block. try: yield record + lines[-1] except IndexError: yield record break elif block.endswith("\n"): # No carry-over data between blocks. yield lines[-1] record = "" else: # Carry partial last line over to next block. record = lines[-1] @staticmethod def _parse_records(records): """Parses all table records and stores the data. Args: records: A list of iterable database record values. Returns: temperature: Numpy array of temperatures. data: Numpy array of data values. """ temperature, q = [], [] for record in records: if record: temperature.append(record[0]) q.append(record[1:]) temperature = asarray(temperature, dtype=float32) data = transpose(asarray(q, dtype=float32)) return temperature, data def _records(self, response, molecule): """Parses the HTTP table for all records related to the input molecule. Args: response: A http.client.HTTPResponse object. molecule: Molecule id. Yields: A list of floats from a record from the http table. Raises: NoMoleculeError: Failed to find the input molecule. """ found_molecule = False num_isotopologues = 0 for line in self._ascii_table_records(response): if found_molecule: if match(r"\s*[A-Za-z0-9+]+$", line): break elif num_isotopologues > 0: yield [float32(x.strip()) for x in line.split()[:(num_isotopologues+1)]] elif match(r"\s*T / K", line): num_isotopologues = sum(x == "Q" for x in line) elif line.startswith("c"): # Ignore comments. continue else: found_molecule = match(r"\s*{}$".format(molecule), line) if not found_molecule: raise NoMoleculeError(f"molecule {molecule} not found in TIPS 2017 tables.")
[docs]class NoMoleculeError(BaseException): """No TIPS data found for this molecule.""" pass