Skip to content

Commit ebec41d

Browse files
committed
initial commit
0 parents  commit ebec41d

File tree

15 files changed

+1073
-0
lines changed

15 files changed

+1073
-0
lines changed

.github/workflows/main.yml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
name: CI
2+
3+
on: [push]
4+
5+
jobs:
6+
build:
7+
8+
runs-on: ubuntu-latest
9+
10+
steps:
11+
- uses: actions/checkout@v1
12+
- name: Set up Python 3.7
13+
uses: actions/setup-python@v1
14+
with:
15+
python-version: 3.7
16+
- name: Build
17+
run: |
18+
pip install .
19+
- name: Test with pytest
20+
run: |
21+
pip install pytest
22+
pytest
23+
- name: Build distributions
24+
run: |
25+
pip install setuptools wheel
26+
python setup.py sdist bdist_wheel

.github/workflows/publish.yml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
name: Upload Python Package
2+
3+
on:
4+
release:
5+
types: [created]
6+
7+
jobs:
8+
deploy:
9+
runs-on: ubuntu-latest
10+
steps:
11+
- uses: actions/checkout@v1
12+
- name: Set up Python
13+
uses: actions/setup-python@v1
14+
with:
15+
python-version: '3.7'
16+
- name: Install dependencies
17+
run: |
18+
python -m pip install --upgrade pip
19+
pip install setuptools wheel twine
20+
- name: Build and publish
21+
env:
22+
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
23+
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
24+
run: |
25+
python setup.py sdist bdist_wheel
26+
twine upload dist/*

.gitignore

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# OS and IDE
2+
.DS_Store
3+
.idea
4+
.vscode
5+
6+
# Byte-compiled / optimized / DLL files
7+
__pycache__/
8+
*.py[cod]
9+
*$py.class
10+
11+
# Distribution / packaging
12+
.Python
13+
build/
14+
dist/
15+
sdist/
16+
wheels/
17+
*.egg
18+
*.egg-info/

LICENSE

Lines changed: 674 additions & 0 deletions
Large diffs are not rendered by default.

README.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# LatticeJSON
2+
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
5+
appropriate choice to characterize the magnetic lattice of a particle accelerator.
6+
7+
## Specification
8+
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).
11+
12+
## Example
13+
14+
A LatticeJSON file for a FODO lattice:
15+
```json
16+
{
17+
"name": "FODO lattice",
18+
"description": "This is the simplest possible strong focusing lattice.",
19+
"elements": {
20+
"D1": {"type": "Drift", "length": 0.55},
21+
"Q1": {"type": "Quad", "length": 0.2, "k1": 1.2},
22+
"Q2": {"type": "Quad", "length": 0.4, "k1": -1.2},
23+
"B1": {"type": "Bend", "length": 1.5, "angle": 0.392701, "e1": 0.1963505, "e2": 0.1963505}
24+
},
25+
"cells": {
26+
"fodo": ["Q1", "D1", "B1", "D1", "Q2", "D1", "B1", "D1", "Q1"]
27+
},
28+
29+
"main_cell": ["fodo", "fodo", "fodo", "fodo", "fodo", "fodo", "fodo", "fodo"]
30+
}
31+
```
32+
33+
34+
# LatticeJSON CLI
35+
[![Python Version](https://img.shields.io/pypi/pyversions/latticejson)](https://pypi.org/project/latticejson/)
36+
[![PyPI](https://img.shields.io/pypi/v/latticejson.svg)](https://pypi.org/project/latticejson/)
37+
[![CI](https://github.com/andreasfelix/latticejson/workflows/CI/badge.svg)](https://github.com/andreasfelix/latticejson/actions?query=workflow%3ACI)
38+
39+
This repository also contains a Python based commandline tool which is able validate and convert LatticeJSON
40+
files into other common lattice file formats.
41+
42+
You can install and update it using pip or pipenv:
43+
44+
```sh
45+
pip install -U latticejson
46+
```
47+
48+
Validate a LatticeJSON file:
49+
```sh
50+
latticejson validate /path/to/lattice
51+
```
52+
53+
Convert a LatticeJSON file into the elegant lattice format:
54+
```sh
55+
latticejson convert json elegant /path/to/lattice
56+
```
57+
58+
## License
59+
[GNU General Public License v3.0](https://github.com/andreasfelix/latticejson/blob/master/LICENSE)
60+
61+

latticejson/__about__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
__title__ = 'latticeJSON'
2+
__description__ = 'A JSON based lattice file format'
3+
__url__ = 'https://github.com/andreasfelix/latticejson'
4+
__version__ = '0.0.0'
5+
__author__ = 'Felix Andreas'
6+
__license__ = 'GNU General Public License v3.0'

latticejson/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
from .convert import convert_file
2+
from .validate import validate

latticejson/cli.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import click
2+
from .validate import validate_file
3+
from .convert import convert_file
4+
5+
6+
@click.group()
7+
@click.version_option()
8+
def main():
9+
pass
10+
11+
12+
@main.command()
13+
@click.argument('input_format')
14+
@click.argument('output_format')
15+
@click.argument('file', type=click.Path(exists=True))
16+
def convert(**kwargs):
17+
x = convert_file(kwargs['file'], kwargs['input_format'], kwargs['output_format'])
18+
print(x)
19+
20+
21+
@main.command()
22+
@click.argument('file', type=click.Path(exists=True))
23+
def validate(**kwargs):
24+
validate_file(kwargs['file'])

latticejson/convert.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
from typing import List, Dict
2+
import json
3+
4+
from .validate import validate
5+
6+
7+
def convert_file(file_path, input_format, output_format):
8+
if input_format == 'json' and output_format == 'elegant':
9+
with open(file_path) as lattice_file:
10+
lattice_dict = json.load(lattice_file)
11+
12+
validate(lattice_dict)
13+
return convert_json_to_elegant(lattice_dict)
14+
else:
15+
raise NotImplementedError(f'Unknown formats: {input_format}, {output_format}')
16+
17+
18+
JSON_TO_ELEGANT = {
19+
'Drift': 'DRIF',
20+
'Bend': 'CSBEND',
21+
'Quad': 'KQUAD',
22+
'Sext': 'KSEXT',
23+
'Cell': 'LINE',
24+
'main_cell': 'RING',
25+
'length': 'L',
26+
'angle': 'ANGLE',
27+
'e1': 'e1',
28+
'e2': 'e2',
29+
'k1': 'K1',
30+
'k2': 'K2',
31+
}
32+
33+
ELEGANT_TO_JSON = dict(reversed(tup) for tup in JSON_TO_ELEGANT.items())
34+
35+
ELEGANT_ELEMENT_TEMPLATE = '{name}: {type}, {attributes}'.format
36+
ELEGANT_CELL_TEMPLATE = '{name}: LINE=({objects})'.format
37+
38+
39+
def convert_json_to_elegant(lattice_dict):
40+
elements_dict = lattice_dict['elements']
41+
cells_dict = lattice_dict['cells']
42+
43+
elements_string = []
44+
for name, element in elements_dict.items():
45+
attributes = ', '.join(f'{JSON_TO_ELEGANT[key]}={value}' for key, value in element.items() if key != 'type')
46+
type_ = JSON_TO_ELEGANT[element['type']]
47+
elements_string.append(ELEGANT_ELEMENT_TEMPLATE(name=name, type=type_, attributes=attributes))
48+
49+
ordered_cells = order_cells(cells_dict)
50+
cells_string = [ELEGANT_CELL_TEMPLATE(name=name, objects=', '.join(cells_dict[name])) for name in ordered_cells]
51+
cells_string.append(ELEGANT_CELL_TEMPLATE(name=lattice_dict['name'], objects=', '.join(lattice_dict['main_cell'])))
52+
return '\n'.join(elements_string + cells_string)
53+
54+
55+
def order_cells(cells_dict: Dict[str, List[str]]):
56+
cells_dict_copy = cells_dict.copy()
57+
ordered_cells = []
58+
59+
def _order_cells(name, cell: List[str]):
60+
for cell_name in cell:
61+
if cell_name in cells_dict_copy:
62+
_order_cells(cell_name, cells_dict_copy[cell_name])
63+
64+
ordered_cells.append(name)
65+
cells_dict_copy.pop(name)
66+
67+
for name, cell in cells_dict.items():
68+
_order_cells(name, cell)
69+
70+
return ordered_cells

latticejson/schema.json

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
{
2+
"$id": "",
3+
"$schema": "http://json-schema.org/draft-07/schema#",
4+
"title": "JSON lattice file format",
5+
"description": "Defines the magnetic lattice format",
6+
"additionalProperties": false,
7+
"required": [
8+
"name",
9+
"main_cell",
10+
"elements"
11+
],
12+
"type": "object",
13+
"properties": {
14+
"name": {
15+
"type": "string",
16+
"description": "The name of the lattice"
17+
},
18+
"description": {
19+
"type": "string",
20+
"description": "A brief description of the lattice"
21+
},
22+
"main_cell": {
23+
"type": "array",
24+
"items": {
25+
"type": "string"
26+
}
27+
},
28+
"cells": {
29+
"type": "object",
30+
"patternProperties": {
31+
"^.*$": {
32+
"$ref": "#/definitions/cell"
33+
}
34+
},
35+
"additionalProperties": false
36+
},
37+
"elements": {
38+
"type": "object",
39+
"items": {
40+
"anyOf": [
41+
{
42+
"$ref": "#/definitions/cell"
43+
},
44+
{
45+
"$ref": "#/definitions/element"
46+
}
47+
]
48+
}
49+
}
50+
},
51+
"definitions": {
52+
"cell": {
53+
"type": "array",
54+
"items": {
55+
"type": "string"
56+
}
57+
},
58+
"element": {
59+
"type": "object",
60+
"required": [
61+
"type",
62+
"length"
63+
],
64+
"properties": {
65+
"type": {
66+
"type": "string",
67+
"description": "Type of the element."
68+
},
69+
"length": {
70+
"type": "number",
71+
"minimum": 0,
72+
"description": "The length of the element."
73+
}
74+
}
75+
}
76+
}
77+
}

0 commit comments

Comments
 (0)