cif
Read and write CIF (Crystallographic Information File) data.
The parser is pure Python and imports nothing outside the standard library,
not even :mod:re. It is built around one observation about real CIF files:
almost every token in them is a bare, whitespace-delimited word, and only a
small minority are quoted strings, comments, text fields or data names. So
rather than matching a pattern against every token, it runs one
:meth:str.find cursor per interesting character (', ", #, ;
and _) and lets :meth:str.split -- a single C-level call -- handle
everything in between. Those cursors also record where the data names are,
which is what lets the parser step from one to the next without looking at
the values.
The result behaves like a dictionary of dictionaries::
>>> cif = Cif.from_string('''
... data_example
... _cell_length_a 5.1234(3)
... loop_
... _atom_site_label
... _atom_site_fract_x
... C1 0.1234
... O1 0.5678
... ''')
>>> cif["example"]["cell_length_a"]
5.1234
>>> cif["example"]["atom_site_label"]
['C1', 'O1']
Data names are matched case-insensitively, with or without the leading underscore, and across the naming conventions of different CIF flavours, so an mmCIF and a small-molecule CIF can be read through the same keys::
>>> mm = Cif.from_string("data_x\n_cell.length_a 5.1234\n")
>>> mm["x"]["cell_length_a"]
5.1234
Cif
The contents of a CIF: an ordered mapping of block name to
:class:CifBlock.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cif_data
|
existing |
None
|
cif = Cif.from_string("data_one\n_a 1\ndata_two\n_a 2\n") list(cif) ['one', 'two'] cif["two"]["a"] 2
Source code in chmpy/fmt/cif.py
550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 | |
first_block
property
the first data block in the file
from_file(filename, options=None)
classmethod
initialize a :obj:Cif from a file path
options is a :class:CifOptions; the default reading is CIF 1.1.
Source code in chmpy/fmt/cif.py
from_string(contents, options=None)
classmethod
initialize a :obj:Cif from string contents
options is a :class:CifOptions; the default reading is CIF 1.1.
Source code in chmpy/fmt/cif.py
to_file(filename)
to_string()
represent the data in this :obj:Cif textually in the CIF format
Source code in chmpy/fmt/cif.py
CifBlock
Bases: dict
One data_ block: a dict of data names to values.
Keys are stored as they were written in the file, minus the leading
underscore. An exact key lookup is a plain dict lookup; anything else
falls back to matching without regard to case, the leading underscore, or
which CIF dictionary the name came from (see :data:CIF_ALIAS_GROUPS).
block = CifBlock({"Cell_Length_A": 5.0}, name="x") block["Cell_Length_A"], block["_cell_length_a"], block["cell.length_a"] (5.0, 5.0, 5.0) "cell_volume" in block False
Source code in chmpy/fmt/cif.py
CifOptions
dataclass
Settings for reading a CIF, passed to :meth:Cif.from_file and
:meth:Cif.from_string.
Both default to the CIF 1.1 reading that suits nearly every file in circulation; each is here because a reasonable person might want the other behaviour.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
join_text_field_lines
|
bool
|
join the lines of a multi-line text field into one line separated by single spaces, instead of keeping the text as it was written. Off by default, because the line structure is part of the value -- but it is what chmpy did before, and it is convenient when a text field only ever holds a wrapped sentence. |
False
|
cif2_triple_quotes
|
bool
|
read |
False
|
text = "data_a\n_x\n;\nfirst line\nsecond line\n;\n" Cif.from_string(text)["a"]["x"] 'first line\nsecond line' Cif.from_string(text, options=CifOptions(join_text_field_lines=True))["a"]["x"] 'first line second line'
Source code in chmpy/fmt/cif.py
CifParseError
canonical_data_name(name)
The first spelling of name's equivalence class, or name itself.
canonical_data_name("_cell.length_a") 'cell_length_a' canonical_data_name("not_a_known_name") 'not_a_known_name'
Source code in chmpy/fmt/cif.py
format_field(x)
format a value as a CIF field
len(format_field(5.4)) 20 format_field("3.4") '3.4'
Source code in chmpy/fmt/cif.py
is_scalar(value)
needs_quote(string)
check whether a value has to be quoted to survive a round trip
needs_quote("this string will need quoting") True needs_quote("thiswon't") False needs_quote(3.45) False
Source code in chmpy/fmt/cif.py
parse_quote(string, delimiter=';')
extract a value contained within quotes, with an optional change of delimiter
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
string
|
the string containing the value to parse |
required | |
delimiter
|
the quote delimiter, default ';' |
';'
|
Returns:
| Type | Description |
|---|---|
|
the string contained inside the quotes |
parse_quote(";quote text;") 'quote text' parse_quote(":'quote text':", delimiter="'") ":'quote text':" parse_quote(":'quote text':", delimiter=":") "'quote text'" parse_quote("'-y, x-y, z'", delimiter="'") '-y, x-y, z'
Source code in chmpy/fmt/cif.py
parse_value(string, with_uncertainty=False)
parse a single value from a cif file to its appropriate type e.g. int, float, str etc. Will handle uncertainty values contained in parentheses.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
string
|
the string containing the value to parse |
required | |
with_uncertainty
|
return a tuple including uncertainty if a numeric type is expected |
False
|
Returns:
| Type | Description |
|---|---|
|
the value coerced into the appropriate type |
parse_value("2.3(1)", with_uncertainty=True) (2.3, 1) parse_value("string help") 'string help' parse_value("3.1415") * 4 12.566
Source code in chmpy/fmt/cif.py
quote(string)
quote a value so that it reads back as the same string
Text fields are used for anything a one-line quote cannot hold.
quote("P 21/c") "'P 21/c'" quote("it's") '"it\'s"'
Source code in chmpy/fmt/cif.py
register_aliases(*names)
Declare that names are different spellings of the same data name.
Useful for programs that write CIFs with their own data names. Existing
equivalence classes containing any of names are merged into one.
register_aliases("cell_length_a", "my_program_cell_a") cif = Cif.from_string("data_x\n_my_program_cell_a 3.0\n") cif["x"]["cell_length_a"] 3.0
Source code in chmpy/fmt/cif.py
tokenize(text, options=DEFAULT_OPTIONS)
Split CIF text into raw tokens.
Quoted values keep their delimiters and text fields keep their leading and trailing semicolons, so that a token always says what kind of value it is. Comments are dropped.
tokenize("_a 1 _b 'two words' # comment\n") ['_a', '1', '_b', "'two words'"] tokenize("_x\n;\nline one\nline two\n;\n") ['_x', ';\nline one\nline two\n;']