Input and output

Reading GATE ROOT files, checking their contents, and writing the extracted data to the supported output formats.

Reader

Extraction of GATE trees into the package representation.

The module provides read_tree(), the entry point used by the command line interface and by code using the package as a library. It opens a GATE ROOT file, extracts one tree and closes the file again.

Use RootFile directly when several trees are read from the same file, so that the file is opened only once.

Public functions:

read_tree(path, tree, branches, tree_name, validate) -> TreeData

Read one tree from a GATE ROOT file.

read_hits_trees(path, branches, tree_names, validate, add_source_branch) -> TreeData

Read the hits of a GATE ROOT file as a single dataset.

opengate_gate_tree.io.reader.read_tree(path, tree, branches=None, tree_name=None, validate=True)[source]

Read one tree from a GATE ROOT file.

Parameters:
  • path (Path) – Path to the GATE ROOT file.

  • tree (GateTree) – Tree to read.

  • branches (Sequence[str] | None) – Branches to read. When omitted or empty, every branch of the tree is read. Repeated names are read once, at the position of their first occurrence.

  • tree_name (str | None) – Name of the tree in the file, when it differs from the standard one or when a file holds several trees of hits.

  • validate (bool) – Whether to recognise the structure of the “Hits” tree and check the tree against it before reading. Turn it off to extract branches from a file whose structure the package does not know.

Returns:

Columns of the requested branches.

Return type:

TreeData

Raises:
opengate_gate_tree.io.reader.read_hits_trees(path, branches=None, tree_names=None, validate=True, add_source_branch=True)[source]

Read the hits of a GATE ROOT file as a single dataset.

A file splitting its hits into one tree per run, or one per sensitive detector, is read as one dataset with the trees placed one after another. Identifiers are left as GATE wrote them, so an event is still told apart by its run and its event identifier together.

Parameters:
  • path (Path) – Path to the GATE ROOT file.

  • branches (Sequence[str] | None) – Branches to read from every tree. When omitted or empty, every branch is read.

  • tree_names (Sequence[str] | None) – Trees to read, in the order their rows should follow. When omitted, every tree of the file holding hits is read, in file order.

  • validate (bool) – Whether to recognise the structure of each tree and check it.

  • add_source_branch (bool) – Whether to record which tree each row came from, in a column named sourceTreeName.

Returns:

Rows of every tree that was read, one tree after another.

Return type:

TreeData

Raises:

ROOT files

Reading GATE ROOT files.

The module defines RootFile, a thin wrapper over uproot that exposes the parts of a GATE output file the package works with.

Only trees are considered. GATE files also store histograms, such as latest_event_ID and total_nb_primaries; they are ignored when the file contents are inspected and are never carried over to an output file.

Reading the “Hits” tree recognises which structure it has and checks the tree against it before any data is loaded, so a file that is not what it is taken for is reported rather than half read. Passing validate=False extracts the branches without asking what structure they form, which is what a file from a GATE build the package does not know needs.

Hits are not always stored under the name “Hits”. The GateToTree output calls its tree tree, and a file can hold one tree per run or one per sensitive detector, named after it. A tree asked for by name is used as given; otherwise the name is matched, and for hits the trees are examined so that one holding them is found whatever it is called.

Public objects:

RootFile

Reader for a single GATE ROOT file.

class opengate_gate_tree.io.rootfile.RootFile(path)[source]

Reader for a single GATE ROOT file.

The file is opened on construction and should be closed when no longer needed, either through close() or by using the instance as a context manager.

Parameters:

path (Path)

__enter__()[source]

Return the reader itself so it can be used as a context manager.

Return type:

RootFile

__exit__(exc_type, exc_value, traceback)[source]

Close the underlying file.

Parameters:
Return type:

None

close()[source]

Close the underlying file.

Return type:

None

property path: Path

Path of the opened file.

property tree_names: tuple[str, ...]

Names of the trees stored in the file, without cycle numbers.

Objects that are not trees, such as the histograms written by GATE, are not reported.

has_tree(tree)[source]

Return whether the requested tree is present in the file.

Hits are looked for the way they are read: by name first, then by structure, so a file whose hits sit in a tree called something else answers True here and can be read. A file holding hits in several trees answers True as well, although reading it needs one of them to be named.

Parameters:

tree (GateTree)

Return type:

bool

hits_tree_names()[source]

Return the names of the trees holding hits, whatever they are called.

A tree holds hits when its branches form one of the supported structures. A file usually holds one such tree, but the output can be split into one tree per run or per sensitive detector.

Returns:

Names of the trees holding hits, in file order.

Return type:

tuple[str, …]

resolve_tree_name(tree, name=None)[source]

Return the key under which the requested tree is stored.

A name given by the caller is used as it is. Otherwise names are compared exactly, then without regard to case, and for hits the trees are examined as a last step, so that a tree holding them is found even when it is called something else.

Parameters:
  • tree (GateTree) – Requested tree.

  • name (str | None) – Name of the tree in the file, when the caller knows it.

Returns:

Key of the tree in the file.

Return type:

str

Raises:
branch_names(tree, name=None)[source]

Return the branch names of the requested tree, in file order.

Raises:
Parameters:
Return type:

tuple[str, …]

detect_hits_tree(tree_name=None)[source]

Recognise the structure of the “Hits” tree stored in the file.

Parameters:

tree_name (str | None) – Name of the tree in the file, when the caller knows it.

Returns:

Structure of the tree.

Return type:

HitsTreeDetection

Raises:
read(tree, branches=None, tree_name=None, validate=True)[source]

Read a tree into the package representation.

Parameters:
  • tree (GateTree) – Tree to read.

  • branches (Sequence[str] | None) – Branches to read. When omitted or empty, every branch is read. Repeated names are read once, at the position of their first occurrence.

  • tree_name (str | None) – Name of the tree in the file, when it differs from the standard one or when a file holds several trees of hits.

  • validate (bool) – Whether to recognise the structure of the “Hits” tree and check the tree against it. The check covers the whole tree, not only the branches being read, and runs before any data is loaded. Other trees are read the same way either way.

Returns:

Columns of the requested branches.

Return type:

TreeData

Raises:
read_hits(branches=None, tree_names=None, validate=True, add_source_branch=True)[source]

Read the hits of the file as a single dataset.

A file splitting its hits into one tree per run, or one per sensitive detector, is read as one dataset with the trees placed one after another. Every tree is held in memory before they are joined, so this needs room for the whole file.

Parameters:
  • branches (Sequence[str] | None) – Branches to read from every tree. When omitted or empty, every branch is read.

  • tree_names (Sequence[str] | None) – Trees to read, in the order their rows should follow. When omitted, every tree of the file holding hits is read, in file order.

  • validate (bool) – Whether to recognise the structure of each tree and check it.

  • add_source_branch (bool) – Whether to record which tree each row came from.

Returns:

Rows of every tree that was read, one tree after another.

Return type:

TreeData

Raises:

Validation

Consistency checks for GATE ROOT files.

The module holds the checks performed before and while a tree is extracted: the input path, the presence of the requested tree and branches, and whether the branch types are supported.

The functions take plain names rather than a RootFile so that the module stays independent of the reader and can be tested on its own.

Public functions:

validate_root_file_path(path: Path) -> None

Check that the path can point to a readable ROOT file.

find_tree_name(available: Sequence[str], tree: GateTree) -> str | None

Look up the key of a tree without raising.

resolve_tree_name(available: Sequence[str], tree: GateTree, source: Path | None) -> str

Look up the key of a tree, reporting the available trees when absent.

resolve_requested_tree_name(available: Sequence[str], name: str, source: Path | None) -> str

Look up a tree the caller named.

find_hits_tree_names(branches_by_tree: Mapping[str, Sequence[str]]) -> tuple[str, …]

Names of the trees whose branches form a supported “Hits” structure.

validate_branches_present(available: Sequence[str], requested: Sequence[str]) -> None

Check that every requested branch exists in the tree.

validate_branch_interpretations(interpretations: Mapping[str, Any]) -> None

Check that every branch uses a supported type.

branch_type_name(interpretation: Any) -> str

Name the type a branch is stored with.

opengate_gate_tree.io.validation.validate_root_file_path(path)[source]

Check that the path can point to a readable ROOT file.

Parameters:

path (Path) – Path to check.

Raises:

RootFileError – If the extension is wrong, the path does not exist, or it is not a regular file.

Return type:

None

opengate_gate_tree.io.validation.find_tree_name(available, tree)[source]

Return the key under which a tree is stored, or None when absent.

Names are compared exactly first, then without regard to case.

Parameters:
  • available (Sequence[str]) – Names of the trees present in the file.

  • tree (GateTree) – Requested tree.

Returns:

Matching key, or None if the tree is not present.

Return type:

str | None

opengate_gate_tree.io.validation.resolve_tree_name(available, tree, source=None)[source]

Return the key under which the requested tree is stored.

Parameters:
  • available (Sequence[str]) – Names of the trees present in the file.

  • tree (GateTree) – Requested tree.

  • source (Path | None) – File the names came from, named in the error message when given.

Returns:

Matching key.

Return type:

str

Raises:

TreeNotFoundError – If no tree of the requested name is present.

opengate_gate_tree.io.validation.resolve_requested_tree_name(available, name, source=None)[source]

Return the key of a tree the caller named.

Parameters:
  • available (Sequence[str]) – Names of the trees present in the file.

  • name (str) – Name the caller asked for.

  • source (Path | None) – File the names came from, named in the error message when given.

Returns:

The name, once it is known to be present.

Return type:

str

Raises:

TreeNotFoundError – If no tree of that name is present.

opengate_gate_tree.io.validation.find_hits_tree_names(branches_by_tree)[source]

Return the names of the trees holding a supported “Hits” structure.

Parameters:
  • branches_by_tree (Mapping[str, Sequence[str]]) – Tree names mapped to their branch names.

  • supported (A tree counts as holding hits when it holds every branch of a)

  • here (structure. Matching a marker branch is not enough)

  • diagnosis (of a file to read is not the place for the leniency that makes a)

  • useful.

Returns:

Names of the trees whose branches form a structure the package supports, in the order they were given.

Return type:

tuple[str, …]

opengate_gate_tree.io.validation.validate_branches_present(available, requested)[source]

Check that every requested branch exists in the tree.

Parameters:
  • available (Sequence[str]) – Names of the branches present in the tree.

  • requested (Sequence[str]) – Names of the requested branches.

Raises:

BranchNotFoundError – If any requested branch is missing.

Return type:

None

opengate_gate_tree.io.validation.validate_branch_interpretations(interpretations)[source]

Check that every branch uses a type supported by the package.

Scalar branches, fixed-width array branches and text branches are supported. Branches whose length varies per entry are not, because they have no representation in the supported output formats.

The check runs on the uproot interpretation rather than on loaded data: text branches and branches of varying length both load as object arrays, so they cannot be told apart afterwards.

Parameters:

interpretations (Mapping[str, Any]) – Branch name to uproot interpretation mapping.

Raises:

UnsupportedBranchTypeError – If any branch uses an unsupported type.

Return type:

None

opengate_gate_tree.io.validation.branch_type_name(interpretation)[source]

Return the name of the type a branch is stored with.

The names are the ones the branch schemas use, so that what a file holds can be compared with what a structure describes.

Parameters:

interpretation (Any) – Interpretation of the branch, as reported by uproot.

Returns:

"text" for a text branch, the name of the scalar type for a numeric one, "<type>[<width>]" for a fixed-width array branch, and "unsupported" for a type the package cannot represent.

Return type:

str

File names

Names of the files a run writes.

An output file is named after the title the caller gave it, the tree the data came from, and the format it is written in: patient_01.hits.csv. The tree is part of the name because one input file holds several trees, and a run extracting the hits and a run extracting the singles of the same simulation should not land on the same name.

The report of a run, when one is asked for, goes next to the data under the same title: patient_01.hits.stats.json.

Naming lives here rather than in the command line, so that code using the package as a library lands on the same names without repeating the rule.

Public functions:

build_output_file_name(title, tree, file_format) -> str

Name of the file holding an extracted tree.

build_output_file_path(directory, title, tree, file_format) -> Path

Path of the file holding an extracted tree.

build_statistics_file_path(directory, title, tree) -> Path

Path of the report describing an extracted tree.

opengate_gate_tree.io.naming.build_output_file_name(title, tree, file_format)[source]

Return the name of the file holding an extracted tree.

Parameters:
  • title (str) – Title of the run, used as the first part of the name.

  • tree (GateTree) – Tree the data was extracted from.

  • file_format (OutputFileFormat) – Format the data is written in.

Returns:

Name in the form <title>.<tree>.<format>.

Return type:

str

opengate_gate_tree.io.naming.build_output_file_path(directory, title, tree, file_format)[source]

Return the path of the file holding an extracted tree.

Parameters:
  • directory (Path) – Directory the file is written to.

  • title (str) – Title of the run.

  • tree (GateTree) – Tree the data was extracted from.

  • file_format (OutputFileFormat) – Format the data is written in.

Returns:

Path of the output file.

Return type:

Path

opengate_gate_tree.io.naming.build_statistics_file_path(directory, title, tree)[source]

Return the path of the report describing an extracted tree.

Parameters:
  • directory (Path) – Directory the report is written to.

  • title (str) – Title of the run, the same one the data carries.

  • tree (GateTree) – Tree the data was extracted from.

Returns:

Path of the report file.

Return type:

Path

Statistics files

Writing a summary of extracted data to a file.

The report is written as JSON, because it is meant to be read by whatever comes next in the analysis as much as by a person: a run compared against another one, a table of process counts, a check that a file holds what a simulation was supposed to produce. The rendering meant for reading is format_statistics().

Public functions:

write_statistics(statistics, path) -> Path

Write a summary as a JSON file.

opengate_gate_tree.io.statistics.write_statistics(statistics, path)[source]

Write a summary as a JSON file.

Parameters:
  • statistics (TreeStatistics) – Summary to write.

  • path (Path) – Path of the output file. An existing file is overwritten.

Returns:

Path that was written to.

Return type:

Path

Raises:

ExportError – If the file cannot be written.

Output formats

OutputFileFormat enum and parser.

This module defines the OutputFileFormat enum and provides a function to parse strings into OutputFileFormat enum members.

class opengate_gate_tree.io.fileformat.OutputFileFormat(*values)[source]

Enum representing the output file format.

opengate_gate_tree.io.fileformat.parse_output_file_format(name)[source]

Parse a string into an OutputFileFormat enum member.

Parameters:

name (str) – Name of the output file format member.

Returns:

Corresponding OutputFileFormat enum member.

Return type:

OutputFileFormat

Raises:

ValueError – If the name does not correspond to any OutputFileFormat member.

Writers

Export of extracted GATE trees to the supported output formats.

The subpackage holds one writer per output format and the lookup used to pick one. Output files carry the extracted tree only: the histograms stored next to the trees in a GATE file are not copied over.

Public functions:

get_writer(file_format: OutputFileFormat) -> TreeWriter

Return the writer producing the requested format.

write_tree(data: TreeData, path: Path, file_format: OutputFileFormat) -> Path

Write the data in the requested format.

opengate_gate_tree.io.writers.get_writer(file_format)[source]

Return the writer producing the requested format.

Parameters:

file_format (OutputFileFormat) – Requested output format.

Returns:

Writer for the requested format.

Return type:

TreeWriter

Raises:

ExportError – If no writer produces the requested format.

opengate_gate_tree.io.writers.write_tree(data, path, file_format)[source]

Write the data in the requested format.

Parameters:
  • data (TreeData) – Data to write.

  • path (Path) – Path of the output file. An existing file is overwritten.

  • file_format (OutputFileFormat) – Format of the output file.

Returns:

Path that was written to.

Return type:

Path

Raises:

ExportError – If no writer produces the requested format, or the file cannot be written.

Shared parts of the output writers.

The module defines the interface every writer implements and the helpers they share, mainly the normalization of text columns.

Text columns need a different representation per backend. Both accepted representations start from a list of strings, so the conversion lives here: uproot rejects NumPy unicode arrays in a tree, and h5py rejects them when a string data type is requested.

Public objects:

TreeWriter

Interface implemented by every output writer.

is_text_column(column) -> bool

Report whether a column holds text.

as_text_list(name, column) -> list[str]

Return a text column as a list of strings.

prepare_output_directory(path) -> None

Create the directory the output file will be written to.

reject_branch_names(data, characters, file_format, reason) -> None

Refuse branch names a backend cannot store faithfully.

class opengate_gate_tree.io.writers.base.TreeWriter(*args, **kwargs)[source]

Interface implemented by every output writer.

write(data, path)[source]

Write the data to path and return the path written to.

Parameters:
Return type:

Path

opengate_gate_tree.io.writers.base.reject_branch_names(data, characters, file_format, reason)[source]

Refuse branch names a backend cannot store faithfully.

Both backends accept such a name and then write something other than what was asked for, without reporting anything. Refusing the file is the only way the caller learns about it before reading the result.

Parameters:
  • data (TreeData) – Data about to be written.

  • characters (str) – Characters the backend cannot carry in a branch name.

  • file_format (OutputFileFormat) – Format being written, named in the message.

  • reason (str) – What the backend would do with such a name.

Raises:

ExportError – If any branch name holds one of the characters.

Return type:

None

opengate_gate_tree.io.writers.base.is_text_column(column)[source]

Report whether a column holds text rather than numbers.

Parameters:

column (NDArray[Any])

Return type:

bool

opengate_gate_tree.io.writers.base.as_text_list(name, column)[source]

Return a text column as a list of strings.

Parameters:
  • name (str) – Branch name, used in the error message.

  • column (numpy.ndarray) – Column to convert.

Returns:

Column values as strings.

Return type:

list[str]

Raises:

ExportError – If the column holds values that are not text. An array of Python objects can hold anything, and converting such values with str would write something other than the data the caller provided.

opengate_gate_tree.io.writers.base.prepare_output_directory(path)[source]

Create the directory the output file will be written to.

Parameters:

path (Path) – Path of the output file.

Raises:

ExportError – If the directory cannot be created.

Return type:

None

Export of a GATE tree to CSV.

A CSV file holds scalar cells, so fixed-width array branches are written as one column per component, named <branch>_<index>. The file therefore does not carry the branch back as an array when read again; ROOT and HDF5 do.

Public objects:

CsvTreeWriter

Writer producing a CSV file.

class opengate_gate_tree.io.writers.csv_writer.CsvTreeWriter[source]

Writer producing a CSV file.

write(data, path)[source]

Write the data as CSV.

Parameters:
  • data (TreeData) – Data to write.

  • path (Path) – Path of the output file. An existing file is overwritten.

Returns:

Path that was written to.

Return type:

Path

Raises:

ExportError – If the file cannot be written.

Export of a GATE tree to HDF5.

The tree becomes one group holding one dataset per branch. Fixed-width array branches keep their shape as two-dimensional datasets, so volumeID is written as (entries, width) rather than split into separate columns.

Text branches are written with a variable-length UTF-8 string type and read back with h5py through asstr().

The group tracks insertion order, because HDF5 lists links alphabetically by default and the branch order carries meaning for the analysis reading the file.

Public objects:

Hdf5TreeWriter

Writer producing an HDF5 file.

class opengate_gate_tree.io.writers.hdf5_writer.Hdf5TreeWriter[source]

Writer producing an HDF5 file.

write(data, path)[source]

Write the data as HDF5.

Parameters:
  • data (TreeData) – Data to write.

  • path (Path) – Path of the output file. An existing file is overwritten.

Returns:

Path that was written to.

Return type:

Path

Raises:

ExportError – If a branch name holds a slash, or the file cannot be written.

Export of a GATE tree to ROOT.

The tree is written as a TTree, the container the analysis code reading these files expects. Assigning a mapping to a key of an uproot file writes a ROOT::RNTuple instead, which round-trips through uproot but cannot be read by code using TTree::Draw, SetBranchAddress or RDataFrame. The writer therefore declares the branches with mktree and appends the data with extend.

Data is appended only when the tree holds entries: extend raises on an empty batch that contains a text branch, while mktree on its own already produces a valid tree with every branch declared.

Public objects:

RootTreeWriter

Writer producing a ROOT file holding a TTree.

class opengate_gate_tree.io.writers.root_writer.RootTreeWriter[source]

Writer producing a ROOT file holding a TTree.

write(data, path)[source]

Write the data as a ROOT TTree.

Parameters:
  • data (TreeData) – Data to write.

  • path (Path) – Path of the output file. An existing file is overwritten.

Returns:

Path that was written to.

Return type:

Path

Raises:

ExportError – If the data holds no branches, a branch name holds a bracket, or the file cannot be written.