Skip to content

Commit e9ee229

Browse files
Move root lattice definition to lattices dict (#51)
1 parent 937770d commit e9ee229

File tree

10 files changed

+168
-109
lines changed

10 files changed

+168
-109
lines changed

README.md

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,34 +5,36 @@ has a human readable syntax and is available in all common programming language.
55
appropriate choice to characterize the magnetic lattice of a particle accelerator.
66

77
## Specification
8+
89
This repository contains the
910
[Specification of LatticeJSON](https://github.com/andreasfelix/latticejson/blob/master/latticejson/schema.json)
1011
in form of a [JSON Schema](https://json-schema.org).
1112

12-
1313
## Example
1414

1515
A LatticeJSON file for a FODO lattice:
16+
1617
```json
1718
{
18-
"name": "FODO_RING",
19-
"description": "This is the simplest possible strong focusing lattice.",
19+
"version": "2.0",
20+
"title": "FODO Lattice",
21+
"info": "This is the simplest possible strong focusing lattice.",
22+
"root": "RING",
2023
"elements": {
2124
"D1": ["Drift", {"length": 0.55}],
2225
"Q1": ["Quadrupole", {"length": 0.2, "k1": 1.2}],
2326
"Q2": ["Quadrupole", {"length": 0.4, "k1": -1.2}],
2427
"B1": ["Dipole", {"length": 1.5, "angle": 0.392701, "e1": 0.1963505, "e2": 0.1963505}]
2528
},
26-
"sub_lattices": {
27-
"FODO": ["Q1", "D1", "B1", "D1", "Q2", "D1", "B1", "D1", "Q1"]
28-
},
29-
"lattice": ["FODO", "FODO", "FODO", "FODO", "FODO", "FODO", "FODO", "FODO"]
29+
"lattices": {
30+
"CELL": ["Q1", "D1", "B1", "D1", "Q2", "D1", "B1", "D1", "Q1"],
31+
"RING": ["CELL", "CELL", "CELL", "CELL", "CELL", "CELL", "CELL", "CELL"]
32+
}
3033
}
31-
3234
```
3335

36+
## LatticeJSON CLI
3437

35-
# LatticeJSON CLI
3638
[![Python Version](https://img.shields.io/pypi/pyversions/latticejson)](https://pypi.org/project/latticejson/)
3739
[![PyPI](https://img.shields.io/pypi/v/latticejson.svg)](https://pypi.org/project/latticejson/)
3840
[![CI](https://github.com/andreasfelix/latticejson/workflows/CI/badge.svg)](https://github.com/andreasfelix/latticejson/actions?query=workflow%3ACI)
@@ -41,34 +43,41 @@ This repository also contains a Python based command-line tool which is able val
4143
and convert LatticeJSON files into other common lattice file formats and vice versa.
4244

4345
You can install and update it using pip or pipenv:
46+
4447
```sh
4548
pip install -U latticejson
4649
```
4750

4851
Validate a LatticeJSON file:
52+
4953
```sh
5054
latticejson validate /path/to/lattice.json
5155
```
5256

5357
Convert an elegant lattice file to the LatticeJSON format:
58+
5459
```sh
5560
latticejson convert --to json /path/to/lattice.lte
5661
```
5762

5863
Autoformat one or more LatticeJSON files:
64+
5965
```sh
6066
latticejson autoformat /path/to/lattice.json ...
6167
```
6268

6369
To activate Bash completion add
64-
```
70+
71+
```sh
6572
eval "$(_LATTICEJSON_COMPLETE=source latticejson)"
6673
```
6774

6875
to your `.bashrc`. Or, create an activation script with:
69-
```
76+
77+
```sh
7078
_LATTICEJSON_COMPLETE=source latticejson > latticejson-complete.sh
7179
```
7280

7381
## License
82+
7483
[GNU General Public License v3.0](https://github.com/andreasfelix/latticejson/blob/master/LICENSE)

latticejson/cli.py

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,17 @@
66

77
from . import __version__, io, parse
88
from .format import format_json
9+
from .migrate import MAX_VERSION
910
from .migrate import migrate as _migrate
10-
from .validate import validate_file
11+
from .validate import parse_version, schema, validate_file
1112

1213
FORMATS = "json", "lte", "madx"
1314

1415

1516
@click.group(context_settings=dict(max_content_width=120))
16-
@click.version_option(__version__)
17+
@click.version_option(
18+
message=(f"LatticeJSON CLI, version {__version__}\n{schema['title']}")
19+
)
1720
def cli():
1821
pass
1922

@@ -71,16 +74,30 @@ def autoformat(files, dry_run):
7174

7275

7376
@cli.command()
74-
@click.argument("file", type=click.Path(exists=True))
75-
@click.option("--from", "from_", required=True, help="Initial version")
76-
@click.option("--to", required=True, help="Final version")
77-
def migrate(file, from_, to):
77+
@click.argument("files", nargs=-1, type=click.Path(exists=True))
78+
@click.option(
79+
"--final", type=int, default=MAX_VERSION, show_default=True, help="Final version."
80+
)
81+
@click.option(
82+
"--dry-run",
83+
"-d",
84+
is_flag=True,
85+
help="Don't write the files back, just output the formatted files.",
86+
)
87+
def migrate(files, final, dry_run):
7888
"""Migrate old LatticeJSON files to newer versions."""
79-
text = Path(file).read_text()
80-
initial_version = from_.split(".")
81-
final_version = to.split(".")
82-
latticejson = _migrate(json.loads(text), initial_version, final_version)
83-
click.echo(format_json(latticejson))
89+
for path in itertools.chain.from_iterable(
90+
path.rglob("*.json") if path.is_dir() else (path,) for path in map(Path, files)
91+
):
92+
data = json.loads(path.read_text())
93+
initial = parse_version(data["version"]).major
94+
latticejson = _migrate(data, initial, final)
95+
formatted = format_json(latticejson)
96+
click.secho(f"Migrated {path} from version {initial} to {final}", bold=True)
97+
if dry_run:
98+
click.echo(formatted)
99+
else:
100+
path.write_text(formatted)
84101

85102

86103
@cli.group()

latticejson/convert.py

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from .exceptions import UnknownAttributeWarning, UnknownElementTypeWarning
77
from .parse import parse_elegant, parse_madx
8+
from .validate import schema_version
89

910
NAME_MAP = json.loads((Path(__file__).parent / "map.json").read_text())["map"]
1011
TO_ELEGANT = {x: y[0][0] for x, *y in NAME_MAP}
@@ -58,12 +59,14 @@ def _map_names(lattice_data: dict, name_map: dict):
5859
warn(UnknownAttributeWarning(other_key, name))
5960

6061
lattices = lattice_data["lattices"]
61-
lattice_name, main_lattice = lattices.popitem() # use last lattice as main_lattice
62+
root = lattice_data.get("root", tuple(lattices.keys())[-1])
63+
title = lattice_data.get("title", "")
6264
return dict(
63-
name=lattice_name,
64-
lattice=main_lattice,
65-
sub_lattices=lattices,
65+
version=str(schema_version),
66+
title=title,
67+
root=root,
6668
elements=elements,
69+
lattices=lattices,
6770
)
6871

6972

@@ -74,21 +77,20 @@ def to_elegant(latticejson: dict) -> str:
7477
:return: string with in elegant lattice file format
7578
"""
7679
elements = latticejson["elements"]
77-
sub_lattices = latticejson["sub_lattices"]
80+
lattices = latticejson["lattices"]
7881

79-
strings = [f"! TITLE: {latticejson['name']}"]
82+
strings = [f"! TITLE: {latticejson['title']}"]
8083
element_template = "{}: {}, {}".format
8184
for name, (type_, attributes) in elements.items():
8285
attrs = ", ".join(f"{TO_ELEGANT[k]}={v}" for k, v in attributes.items())
8386
elegant_type = TO_ELEGANT[type_]
8487
strings.append(element_template(name, elegant_type, attrs))
8588

8689
lattice_template = "{}: LINE=({})".format
87-
for name in sort_lattices(sub_lattices):
88-
strings.append(lattice_template(name, ", ".join(sub_lattices[name])))
90+
for name in sort_lattices(lattices):
91+
strings.append(lattice_template(name, ", ".join(lattices[name])))
8992

90-
strings.append(lattice_template("__MAIN__", ", ".join(latticejson["lattice"])))
91-
strings.append("USE, __MAIN__\n")
93+
strings.append(f"USE, {latticejson['root']}\n")
9294
return "\n".join(strings)
9395

9496

@@ -99,21 +101,20 @@ def to_madx(latticejson: dict) -> str:
99101
:return: string with in elegant lattice file format
100102
"""
101103
elements = latticejson["elements"]
102-
sub_lattices = latticejson["sub_lattices"]
104+
lattices = latticejson["lattices"]
103105

104-
strings = [f"TITLE, \"{latticejson['name']}\";"]
106+
strings = [f"TITLE, \"{latticejson['title']}\";"]
105107
element_template = "{}: {}, {};".format
106108
for name, (type_, attributes) in elements.items():
107109
attrs = ", ".join(f"{TO_MADX[k]}={v}" for k, v in attributes.items())
108110
elegant_type = TO_MADX[type_]
109111
strings.append(element_template(name, elegant_type, attrs))
110112

111113
lattice_template = "{}: LINE=({});".format
112-
for name in sort_lattices(sub_lattices):
113-
strings.append(lattice_template(name, ", ".join(sub_lattices[name])))
114+
for name in sort_lattices(lattices):
115+
strings.append(lattice_template(name, ", ".join(lattices[name])))
114116

115-
strings.append(lattice_template("__MAIN__", ", ".join(latticejson["lattice"])))
116-
strings.append("USE, __MAIN__;\n")
117+
strings.append(f"USE, {latticejson['root']};\n")
117118
return "\n".join(strings)
118119

119120

latticejson/exceptions.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,13 @@ def __init__(self, object_name, lattice_name):
2222
)
2323

2424

25+
class IncompatibleVersionError(Exception):
26+
"""Raised if installed LatticeJSON library is out of date"""
27+
28+
def __init__(self, message):
29+
super().__init__(f"Incompatible LatticeJSON version: {message}")
30+
31+
2532
class UnknownElementTypeWarning(UserWarning):
2633
"""Raised if there is no equivalent LatticeJSON element."""
2734

latticejson/migrate.py

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,33 @@
1-
import sys
1+
def migrate(data: dict, initial: int, final: int) -> dict:
2+
data = data.copy()
3+
for from_, to, func in _VERSION_MAPS:
4+
if from_ >= initial and (final is None or to <= final):
5+
func(data)
6+
return data
27

38

4-
def migrate(initial: dict, initial_version: tuple, final_version: tuple):
5-
function = getattr(
6-
sys.modules[__name__],
7-
f"migrate_{'_'.join(initial_version)}_to_{'_'.join(final_version)}",
8-
None,
9-
)
10-
if function is None:
11-
raise NotImplementedError(f"Unkown versions {initial_version}, {final_version}")
9+
def _0_to_1(data: dict):
10+
data["version"] = "1.0"
11+
elements = data["elements"]
12+
for name, attributes in elements.items():
13+
elements[name] = attributes.pop("type"), attributes
1214

13-
return function(initial)
1415

16+
def _1_to_2(data: dict):
17+
data["version"] = "2.0"
18+
data["title"] = data.pop("name")
19+
data["lattices"] = data.pop("sub_lattices")
20+
data["lattices"]["__MAIN__"] = data.pop("lattice")
21+
data["root"] = "__MAIN__"
22+
info = data.pop("description", False)
23+
if info:
24+
data["info"] = info
1525

16-
def migrate_0_0_2_to_0_0_3(initial: dict):
17-
final = initial.copy()
18-
elements_final = {}
19-
for name, attributes in final["elements"].items():
20-
type_ = attributes.pop("type")
21-
elements_final[name] = type_, attributes
26+
for _, attributes in data["elements"].values():
27+
info = attributes.pop("description", False)
28+
if info:
29+
attributes["info"] = info
2230

23-
final["elements"] = elements_final
24-
return final
31+
32+
_VERSION_MAPS = (0, 1, _0_to_1), (1, 2, _1_to_2)
33+
MAX_VERSION = _VERSION_MAPS[-1][1]

0 commit comments

Comments
 (0)