Source code for at.load.json

"""
Handling of JSON files.
"""

from __future__ import annotations

__all__ = ["load_json", "save_json"]

from pathlib import Path
import json
from typing import Any

import numpy as np

from .allfiles import register_format
from .utils import keep_elements, keep_attributes, element_from_dict
from ..lattice import Element, Lattice, Particle
from .xsuite import XsLine
from .._version import __version_tuple__


class _AtEncoder(json.JSONEncoder):
    """JSON encoder for specific AT types."""

    def default(self, obj):
        if isinstance(obj, Element):
            return obj.to_file()
        elif isinstance(obj, np.ndarray):
            return obj.tolist()
        elif isinstance(obj, Particle):
            return obj.to_dict()
        elif isinstance(obj, np.integer):
            return int(obj)
        elif isinstance(obj, np.floating):
            return float(obj)
        else:
            return super().default(obj)


[docs] def save_json( ring: Lattice, filename: str | Path | None = None, compact: bool = False ) -> None: """Save a :py:class:`.Lattice` as a JSON file. Parameters: ring: Lattice description filename: Name of the JSON file. Default: outputs on :py:obj:`sys.stdout` compact: If :py:obj:`False` (default), the JSON file is pretty-printed with line feeds and indentation. Otherwise, the output is a single line. See Also: :py:func:`.save_lattice` for a generic lattice-saving function. :py:meth:`.Lattice.save` for a generic lattice-saving method. """ indent = None if compact else 2 data = { "atjson": 1, "at_version": ".".join(str(n) for n in __version_tuple__[:3]), "elements": list(keep_elements(ring)), "properties": keep_attributes(ring), } if filename is None: print(json.dumps(data, cls=_AtEncoder, indent=indent)) else: filename = Path(filename) with filename.open("w") as jsonfile: json.dump(data, jsonfile, cls=_AtEncoder, indent=indent)
def _load_at(root: dict[str, Any], **kwargs) -> Lattice: """Create a :py:class:`.Lattice` from a JSON file. Parameters: filename: Name of a JSON file Keyword Args: *: All keywords update the lattice properties Returns: lattice (Lattice): New :py:class:`.Lattice` object See Also: :py:meth:`.Lattice.load` for a generic lattice-loading method. """ def json_generator(params: dict[str, Any], data: dict[str, Any]): # Check the file signature - For later use try: # noinspection PyUnusedLocal atjson = data["atjson"] except KeyError: atjson = 1 # noqa: F841 # Get elements elements = data["elements"] # Get lattice properties try: properties = data["properties"] except KeyError: properties = {} particle_dict = properties.pop("particle", {}) params.setdefault("particle", Particle(**particle_dict)) for k, v in properties.items(): params.setdefault(k, v) for idx, elem_dict in enumerate(elements): yield element_from_dict(elem_dict, index=idx, check=False) return Lattice(root, iterator=json_generator, **kwargs)
[docs] def load_json( filename: str | Path, use: str | None = None, from_at: bool = False, from_xsuite: bool = False, **kwargs, ) -> Lattice: """Create a :py:class:`.Lattice` from a AT or Xsuite JSON file. The kind of file is derived from its contents. In case of ambiguity, the file kind can be explicitly selected with the *from_at* or *from_xsuite* keywords. Parameters: filename: Name of a JSON file from_at: Force the selection of AT json file from_xsuite: Force the selection of Xsuite json file use: Line name, for Xsuite files containing several lines. Default: ``ring`` Keyword Args: *: All keywords update the lattice properties Returns: lattice (Lattice): New :py:class:`.Lattice` object See Also: :py:meth:`.Lattice.load` for a generic lattice-loading method. """ filename = Path(filename) with filename.open() as jsonfile: data = json.load(jsonfile) if from_at or ("atjson" in data): return _load_at(data, in_file=str(filename), **kwargs) elif from_xsuite or ("__class__" in data): return XsLine.from_dict(data, use=use).to_at(**kwargs) else: msg = "Cannot guess the file origin, try 'from_at or 'from_xsuite' keywords" raise TypeError(msg)
register_format( ".json", load_json, save_json, descr="JSON representation of a python AT Lattice. See :py:func:`.load_json`.", )