Skip to content

Commit 4e565ee

Browse files
New element structure (#22)
* adapt schema.json and fodo.json * adapt latticejson_to_elegant * push all * push all * update Readme * push all * Automated Code Formatting using Black
1 parent a548b9a commit 4e565ee

File tree

10 files changed

+286
-209
lines changed

10 files changed

+286
-209
lines changed

README.md

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
# LatticeJSON
22

3-
LatticeJSON is a JSON based lattice file format. JSON is able to describe complex data structures,
4-
has a human readable syntax and is available in all common programming language. It is therefore an
3+
LatticeJSON is a JSON based lattice file format. JSON is able to describe complex data structures,
4+
has a human readable syntax and is available in all common programming language. It is therefore an
55
appropriate choice to characterize the magnetic lattice of a particle accelerator.
66

77
## Specification
88
This repository contains the
9-
[Specification of LatticeJSON](https://github.com/andreasfelix/latticejson/blob/master/latticejson/schema.json)
10-
in form of a [JSON Schema](https://json-schema.org).
9+
[Specification of LatticeJSON](https://github.com/andreasfelix/latticejson/blob/master/latticejson/schema.json)
10+
in form of a [JSON Schema](https://json-schema.org).
1111

1212
## Example
1313

@@ -17,20 +17,20 @@ A LatticeJSON file for a FODO lattice:
1717
"name": "FODO_RING",
1818
"description": "This is the simplest possible strong focusing lattice.",
1919
"elements": {
20-
"D1": {"type": "Drift", "length": 0.55},
21-
"Q1": {"type": "Quadrupole", "length": 0.2, "k1": 1.2},
22-
"Q2": {"type": "Quadrupole", "length": 0.4, "k1": -1.2},
23-
"B1": {"type": "Dipole", "length": 1.5, "angle": 0.392701, "e1": 0.1963505, "e2": 0.1963505}
20+
"D1": ["Drift", {"length": 0.55}],
21+
"Q1": ["Quadrupole", {"length": 0.2, "k1": 1.2}],
22+
"Q2": ["Quadrupole", {"length": 0.4, "k1": -1.2}],
23+
"B1": ["Dipole", {"length": 1.5, "angle": 0.392701, "e1": 0.1963505, "e2": 0.1963505}]
2424
},
2525
"sub_lattices": {
2626
"FODO": ["Q1", "D1", "B1", "D1", "Q2", "D1", "B1", "D1", "Q1"]
2727
},
28-
2928
"lattice": ["FODO", "FODO", "FODO", "FODO", "FODO", "FODO", "FODO", "FODO"]
3029
}
30+
3131
```
32-
33-
32+
33+
3434
# LatticeJSON CLI
3535
[![Python Version](https://img.shields.io/pypi/pyversions/latticejson)](https://pypi.org/project/latticejson/)
3636
[![PyPI](https://img.shields.io/pypi/v/latticejson.svg)](https://pypi.org/project/latticejson/)
@@ -43,7 +43,7 @@ You can install and update it using pip or pipenv:
4343

4444
```sh
4545
pip install -U latticejson
46-
```
46+
```
4747

4848

4949
Validate a LatticeJSON file:

latticejson/cli.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import click
2+
import json
3+
from pathlib import Path
4+
25
from .validate import validate_file
36
from .io import convert_file
7+
from .parse import parse_elegant as _parse_elegant
48

59

610
@click.group()
@@ -13,6 +17,7 @@ def main():
1317
@click.argument("output_format")
1418
@click.argument("file", type=click.Path(exists=True))
1519
def convert(**kwargs):
20+
"""Convert a latticeJSON or elegant file into another format."""
1621
output_format = kwargs["output_format"].lower()
1722
if output_format in ("latticejson", "lj", "json"):
1823
output_format = "latticejson"
@@ -28,4 +33,13 @@ def convert(**kwargs):
2833
@main.command()
2934
@click.argument("file", type=click.Path(exists=True))
3035
def validate(**kwargs):
36+
"""Validate a latticeJSON lattice file."""
3137
validate_file(kwargs["file"])
38+
39+
40+
@main.command()
41+
@click.argument("file", type=click.Path(exists=True))
42+
def parse_elegant(**kwargs):
43+
"""Parse elegant file but do not convert to latticeJSON."""
44+
text = Path(kwargs["file"]).read_text()
45+
print(json.dumps(_parse_elegant(text), indent=4))

latticejson/convert.py

Lines changed: 73 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,52 @@
11
from typing import List, Tuple, Dict
2+
from pathlib import Path
23
import json
34
import warnings
4-
from pathlib import Path
55

66
from .validate import validate
7-
from .parser import parse_elegant
7+
from .parse import parse_elegant
8+
9+
10+
LATTICEJSON_ELEGANT_MAP: Tuple[Tuple[str, Tuple[str, ...]], ...] = (
11+
("Drift", ("DRIF", "DRIFT")),
12+
("Dipole", ("CSBEND", "SBEND", "BEND")),
13+
("Quadrupole", ("KQUAD", "QUAD", "QUADRUPOLE")),
14+
("Sextupole", ("KSEXT", "SEXT", "SEXTUPOLE")),
15+
("Lattice", ("LINE",)),
16+
("length", ("L",)),
17+
("angle", ("ANGLE",)),
18+
("e1", ("E1",)),
19+
("e2", ("E2",)),
20+
("k1", ("K1",)),
21+
("k2", ("K2",)),
22+
)
23+
JSON_TO_ELE: Dict[str, str] = {x: y[0] for x, y in LATTICEJSON_ELEGANT_MAP}
24+
ELE_TO_JSON: Dict[str, str] = {y: x for x, tup in LATTICEJSON_ELEGANT_MAP for y in tup}
825

926

10-
LATTICEJSON_ELEGANT_MAPPING: Dict[str, Tuple] = {
11-
"Drift": ("DRIF", "DRIFT",),
12-
"Dipole": ("CSBEND", "SBEND", "BEND",),
13-
"Quadrupole": ("KQUAD", "QUAD",),
14-
"Sextupole": ("KSEXT", "SEXT",),
15-
"Lattice": ("LINE",),
16-
"length": ("L",),
17-
"angle": ("ANGLE",),
18-
"e1": ("E1",),
19-
"e2": ("E2",),
20-
"k1": ("K1",),
21-
"k2": ("K2",),
22-
}
27+
def latticejson_to_elegant(lattice_dict) -> str:
28+
"""Convert latticeJSON dict to elegant lattice file format.
29+
:param dict: dict in latticeJSON format
30+
:return: string with in elegant lattice file format
31+
"""
32+
elements = lattice_dict["elements"]
33+
sub_lattices = lattice_dict["sub_lattices"]
34+
35+
strings = []
36+
element_template = "{}: {}, {}".format
37+
for name, (type_, attributes) in elements.items():
38+
attrs = ", ".join(f"{JSON_TO_ELE[k]}={v}" for k, v in attributes.items())
39+
elegant_type = JSON_TO_ELE[type_]
40+
strings.append(element_template(name, elegant_type, attrs))
2341

42+
lattice_template = "{}: LINE=({})".format
43+
for name in sort_lattices(sub_lattices):
44+
strings.append(lattice_template(name, ", ".join(sub_lattices[name])))
2445

25-
JSON_TO_ELEGANT = {k: v[0] for k, v in LATTICEJSON_ELEGANT_MAPPING.items()}
26-
ELEGANT_TO_JSON = {v: k for k, tup in LATTICEJSON_ELEGANT_MAPPING.items() for v in tup}
27-
ELEGANT_ELEMENT_TEMPLATE = "{name}: {type}, {attributes}".format
28-
ELEGANT_LATTICE_TEMPLATE = "{name}: LINE=({objects})".format
46+
name = lattice_dict["name"]
47+
strings.append(lattice_template(name, ", ".join(lattice_dict["lattice"])))
48+
strings.append("\n")
49+
return "\n".join(strings)
2950

3051

3152
def elegant_to_latticejson(string):
@@ -42,16 +63,16 @@ def elegant_to_latticejson(string):
4263

4364
elements = {}
4465
for name, (elegant_type, elegant_attributes) in elegant_dict["elements"].items():
45-
latticejson_type = ELEGANT_TO_JSON.get(elegant_type)
66+
latticejson_type = ELE_TO_JSON.get(elegant_type)
4667
if latticejson_type is None:
47-
elements[name] = "Drift", {"length": elegant_attributes["L"]}
48-
warnings.warn(f"Element with type {elegant_type} gets replaced by Drift.")
68+
elements[name] = ["Drift", {"length": elegant_attributes.get("L", 0)}]
69+
warnings.warn(f"{name} with type {elegant_type} is replaced by Drift.")
4970
continue
5071

5172
attributes = {}
52-
elements[name] = latticejson_type, attributes
73+
elements[name] = [latticejson_type, attributes]
5374
for elegant_key, value in elegant_attributes.items():
54-
latticejson_key = ELEGANT_TO_JSON.get(elegant_key)
75+
latticejson_key = ELE_TO_JSON.get(elegant_key)
5576
if latticejson_key is not None:
5677
attributes[latticejson_key] = value
5778
else:
@@ -67,47 +88,41 @@ def elegant_to_latticejson(string):
6788
)
6889

6990

70-
def latticejson_to_elegant(lattice_dict) -> str:
71-
"""Convert latticeJSON dict to elegant lattice file format.
72-
:param dict: dict in latticeJSON format
73-
:return: string with in elegant lattice file format
74-
"""
75-
elements = lattice_dict["elements"]
76-
sub_lattices = lattice_dict["sub_lattices"]
91+
def sort_lattices_old(lattices: Dict[str, List[str]]) -> List[str]:
92+
"""Returns a sorted list of lattice names for a given dict of lattices."""
7793

78-
strings = []
79-
for name, element in elements.items():
80-
type_ = JSON_TO_ELEGANT[element.pop("type")]
81-
attributes = ", ".join(f"{JSON_TO_ELEGANT[k]}={v}" for k, v in element.items())
82-
string = ELEGANT_ELEMENT_TEMPLATE(name=name, type=type_, attributes=attributes)
83-
strings.append(string)
94+
lattices_copy = lattices.copy()
95+
lattice_names = []
8496

85-
for name in order_lattices(sub_lattices):
86-
objects = ", ".join(sub_lattices[name])
87-
strings.append(ELEGANT_LATTICE_TEMPLATE(name=name, objects=objects))
97+
def _sort_lattices(name, arrangement: List[str]):
98+
for child_name in arrangement:
99+
if child_name in lattices_copy:
100+
_sort_lattices(child_name, lattices_copy[child_name])
88101

89-
name = lattice_dict["name"]
90-
objects = ", ".join(lattice_dict["lattice"])
91-
strings.append(ELEGANT_LATTICE_TEMPLATE(name=name, objects=objects))
92-
strings.append("\n")
93-
return "\n".join(strings)
102+
lattice_names.append(name)
103+
lattices_copy.pop(name)
104+
105+
for name, arrangement in lattices.items():
106+
_sort_lattices(name, arrangement)
107+
108+
return lattice_names
94109

95110

96-
def order_lattices(cells_dict: Dict[str, List[str]]):
97-
"""Order a dict of lattices."""
111+
def sort_lattices(lattices: Dict[str, List[str]]) -> List[str]:
112+
"""Returns a sorted list of lattice names for a given dict of lattices."""
98113

99-
cells_dict_copy = cells_dict.copy()
100-
ordered_cells = []
114+
lattices_set = set(lattices)
115+
lattice_names = []
101116

102-
def _order_lattices(name, cell: List[str]):
103-
for lattice_name in cell:
104-
if lattice_name in cells_dict_copy:
105-
_order_lattices(lattice_name, cells_dict_copy[lattice_name])
117+
def _sort_lattices(name):
118+
for child_name in lattices[name]:
119+
if child_name in lattices_set:
120+
lattices_set.remove(child_name)
121+
_sort_lattices(child_name)
106122

107-
ordered_cells.append(name)
108-
cells_dict_copy.pop(name)
123+
lattice_names.append(name)
109124

110-
for name, cell in cells_dict.items():
111-
_order_lattices(name, cell)
125+
while len(lattices_set) > 0:
126+
_sort_lattices(lattices_set.pop())
112127

113-
return ordered_cells
128+
return lattice_names

latticejson/io.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ def read(text, file_format) -> Dict:
1515
else:
1616
raise NotImplementedError(f"Unkown file file_format {file_format}.")
1717

18-
validate(latticejson)
18+
# validate(latticejson)
1919
return latticejson
2020

2121

latticejson/parser.py renamed to latticejson/parse.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,18 +78,18 @@ def transform(self, tree):
7878
)
7979

8080
def element(self, name, type_, *attributes):
81-
self.elements[name] = type_, dict(attributes)
81+
self.elements[name.upper()] = type_, dict(attributes)
8282

8383
def attribute(self, name, value):
8484
if isinstance(value, str):
8585
try:
8686
value = self.rpn_calculator.execute(value)
8787
except:
8888
pass
89-
return name, value
89+
return name.upper(), value
9090

9191
def lattice(self, name, arangement):
92-
self.lattices[name] = list(arangement)
92+
self.lattices[name.upper()] = list(arangement)
9393

9494
def arrangement(self, multiplier, is_reversed, *items):
9595
multiplier = int(multiplier) if multiplier is not None else 1

0 commit comments

Comments
 (0)