Module psfdata.psfascii
Parse the psf/logFile (which is not a log file) See https://github.com/KenKundert/psf_utils for some information on the PSF ASCII format.
Functions
def quoted(x)
-
Expand source code
def quoted(x): return Combine(Suppress(Literal('"')) + x + Suppress(Literal('"')))
Classes
class PsfAsciiFile (path: pathlib._local.Path)
-
Expand source code
class PsfAsciiFile(PsfFile): def __init__(self, path: Path) -> None: super().__init__() self._custom_types: dict[str, ParserElement] = {} self._values: dict[str, dict | Waveform] = {} self._text = path.read_text() self._read_info() if "sweep_section" in self._info.keys(): self._read_sweep_values() else: self._read_simple_values() def _read_info(self) -> None: """Read header/type/trace/sweep sections""" # HEADER SECTION header_section = Suppress("HEADER") + Dict(ZeroOrMore(named_prop, stop_on="TYPE")) # type: ignore # TYPE SECTION type_simple = ( (Literal("FLOAT") + Literal("DOUBLE")) | # spectre files use 'FLOAT DOUBLE' Literal("FLOAT") | Literal("DOUBLE") | Literal("COMPLEX") | Literal("INT") | Literal("BYTE") | Literal("LONG") ) length = pyparsing_common.integer | Literal("*") # not sure if a fixed length is allowed type_string = Literal("STRING") + length type_num_or_str = type_simple | type_string type_array = Literal("ARRAY") + Suppress("(") + length + Suppress(")") + type_num_or_str typedef_nested = Forward() struct_contents = Group(Dict(OneOrMore(Group(typedef_nested)))) # type: ignore # TODO: struct elements can also have props type_struct = Group(Suppress("STRUCT") + Suppress("(") + struct_contents + Suppress(")")) type_any = type_simple | type_string(">string") | type_struct( ">struct") | type_array(">array") typedef_nested <<= (qstring + type_any + Opt(property_list)) # non-nested (top level) typedef - only this will have the parse action attached typedef = typedef_nested.copy() typedef.set_parse_action(self._add_custom_type) type_section = Suppress("TYPE") + Dict(ZeroOrMore(Group(typedef))) # type: ignore # SWEEP SECTION (optional) sweepdef = qstring("sweep_name") + qstring + Opt(property_list) sweep_section = Group(Suppress("SWEEP") + Dict(ZeroOrMore(Group(sweepdef))))("sweep_section") # TRACE SECTION (optional) tracedef = qstring("trace_name") + qstring("type_name") trace_section = Group(Suppress("TRACE") + Dict(ZeroOrMore(Group(tracedef))))("trace_section") sections = (Group(header_section)("header_section") + Group(type_section)("type_section") + Opt(sweep_section) + Opt(trace_section)) self._info = sections.parse_string(self._text) self._header = self._info["header_section"].as_dict() # type: ignore def _read_simple_values(self): # single value section: # <name> <type> <value> where value is typically a struct typed_options = [quoted(k) + v for k, v in self._custom_types.items()] typed_value = Group(MatchFirst(typed_options)) value_item = Group(qstring + typed_value + Opt(Suppress(property_list))) value_section = Suppress(SkipTo("VALUE", include=True)) + Dict(ZeroOrMore(value_item)) + Suppress("END") result = value_section.parse_string(self._text) for k, v in result.items(): self._values[k] = v.as_dict() # type: ignore def _read_sweep_values(self): # swept value section: # <sweepvar> <value0> # <traceA> <value0> # <traceB> <value0> # <sweepvar> <value1> # <traceA> <value1> # <traceB> <value1> # ... sweep_var_type = self._info["sweep_section"][0] sweep_point = Group(quoted(sweep_var_type[0]) + self._custom_types[sweep_var_type[1]]) for k, v in self._info["trace_section"].items(): # type: ignore sweep_point += Group(quoted(k) + self._custom_types[v]) value_section = Suppress(SkipTo("VALUE", include=True)) + \ OneOrMore(Dict(sweep_point, asdict=True)) + Suppress("END") values = value_section.parse_string(self._text) swp_array = np.array([d[sweep_var_type[0]] for d in values]) for name in self._info["trace_section"].keys(): # type: ignore y_array = np.array([d[name] for d in values]) self._values[name] = Waveform(swp_array, "x", y_array, "y") def _add_custom_type(self, typedef: ParseResults) -> None: """Add a ParserExpression to custom_types that can parse the type specified by typedef""" name = typedef[1] if not isinstance(name, str): name = str(typedef[1].get_name()) type_str = typedef[0] # f'"{typedef[0]}"' match name: case 'FLOAT' | 'DOUBLE': expr = pyparsing_common.fnumber case 'INT' | 'BYTE' | 'LONG': expr = pyparsing_common.signed_integer case '>string': expr = qstring case '>array': # TODO type of the array elements expr = Suppress("(") + Group(ZeroOrMore(pyparsing_common.number)) + Suppress(")") case '>struct': expr = Suppress("(") a: ParserElement = Empty() for x in typedef[1][0]: match x[1]: case 'FLOAT' | 'DOUBLE': subexpr = pyparsing_common.fnumber case 'INT' | 'BYTE' | 'LONG': subexpr = pyparsing_common.signed_integer case 'STRING': subexpr = qstring case 'ARRAY': # TODO type of the array elements subexpr = Suppress("(") + ZeroOrMore(qstring) + Suppress(")") case _: raise Exception(f"Unknown type specifier: {x[1]}") a += subexpr(x[0]) # attach the name of the struct member to the element expr += a expr += Suppress(")") self._custom_types[type_str] = expr @property def header(self) -> dict[str, Any]: return self._header @property def sweep_info(self) -> dict[str, Any] | None: if "sweep_section" in self._info.keys(): # TODO: also get properties return self._info["sweep_section"].as_dict() # type: ignore else: return None @property def names(self) -> list[str]: return list(self._values.keys()) def signal_info(self, name: str) -> dict[str, Any]: return {} def get_signal(self, name: str) -> Waveform | dict: return self._values[name]
Base class for ASCII and binary PSF files (and other file formats?)
Ancestors
- PsfFile
- abc.ABC
Instance variables
prop header : dict[str, typing.Any]
-
Expand source code
@property def header(self) -> dict[str, Any]: return self._header
prop names : list[str]
-
Expand source code
@property def names(self) -> list[str]: return list(self._values.keys())
prop sweep_info : dict[str, typing.Any] | None
-
Expand source code
@property def sweep_info(self) -> dict[str, Any] | None: if "sweep_section" in self._info.keys(): # TODO: also get properties return self._info["sweep_section"].as_dict() # type: ignore else: return None
Methods
def get_signal(self, name: str) ‑> Waveform | dict
-
Expand source code
def get_signal(self, name: str) -> Waveform | dict: return self._values[name]
def signal_info(self, name: str) ‑> dict[str, typing.Any]
-
Expand source code
def signal_info(self, name: str) -> dict[str, Any]: return {}
Inherited members