Lens File Specification (.roa)

import json
import json_tricks
import pprint
#from rayoptics.environment import *
from pathlib import Path
from rayoptics.gui import roafile

Use Sasian Triplet.roa as an example

filename = 'Sasian Triplet.roa'
file_contents = roafile.preprocess_roa(Path.cwd() / filename, roafile.module_repl_050)

The .roa file is almost pure json

An exception is how IEEE inf and nan are encoded.

json_inputs = json.loads(file_contents)

ray-optics python object encoding

ray-optics uses the json_tricks package for serializing python objects. The json_tricks package provides dump() and load() functions that augment the default python object encoding in json by including object metadata.

The default json encoding of a python object is to encode the python object’s attributes as a json object. The json_tricks package encodes a python object as a json object consisting of the object metadata info (‘instance_type’) and the object ‘attributes’. The dump() implementation recursively exports all of the object’s attributes.

Top level JSON object contains the optical model.

The attributes of the optical_model include the submodels (sequential, paraxial, element, etc)

pprint.pprint(json_inputs, indent=1, sort_dicts=False, depth=3)
{'optical_model': {'__instance_type__': ['rayoptics.optical.opticalmodel',
                                         'OpticalModel'],
                   'attributes': {'ro_version': '0.8.5',
                                  'radius_mode': True,
                                  'specsheet': {...},
                                  'system_spec': {...},
                                  'seq_model': {...},
                                  'optical_spec': {...},
                                  'parax_model': {...},
                                  'ele_model': {...},
                                  'part_tree': {...},
                                  'profile_dict': {...},
                                  'parts_dict': {...}}}}

Optical Model attributes

The optical model data has 3 sections:

  • the version and radius setting

  • the data for each of the submodels

  • lists of profiles and parts that are referenced in other submodels

json_opm = json_inputs['optical_model']['attributes']
pprint.pprint(json_opm, sort_dicts=False, depth=3)
{'ro_version': '0.8.5',
 'radius_mode': True,
 'specsheet': {'__instance_type__': ['rayoptics.parax.specsheet', 'SpecSheet'],
               'attributes': {'conjugate_type': 'infinite',
                              'imager': [...],
                              'imager_inputs': {...},
                              'frozen_imager_inputs': [...],
                              'etendue_inputs': {...},
                              'etendue_values': {...}}},
 'system_spec': {'__instance_type__': ['rayoptics.optical.opticalmodel',
                                       'SystemSpec'],
                 'attributes': {'title': '',
                                'initials': '',
                                '_dimensions': 'mm',
                                'temperature': 20.0,
                                'pressure': 760.0}},
 'seq_model': {'__instance_type__': ['rayoptics.seq.sequential',
                                     'SequentialModel'],
               'attributes': {'ifcs': [...],
                              'gaps': [...],
                              'z_dir': [...],
                              'do_apertures': True,
                              'stop_surface': 3,
                              'cur_surface': 6}},
 'optical_spec': {'__instance_type__': ['rayoptics.raytr.opticalspec',
                                        'OpticalSpecs'],
                  'attributes': {'spectral_region': {...},
                                 'pupil': {...},
                                 'field_of_view': {...},
                                 'defocus': {...}}},
 'parax_model': {'__instance_type__': ['rayoptics.parax.paraxialdesign',
                                       'ParaxialModel'],
                 'attributes': {'seq_mapping': None,
                                'sys': [...],
                                'ax': [...],
                                'pr': [...],
                                'opt_inv': 2.274813964163764}},
 'ele_model': {'__instance_type__': ['rayoptics.elem.elements', 'ElementModel'],
               'attributes': {}},
 'part_tree': {'__instance_type__': ['rayoptics.elem.parttree', 'PartTree'],
               'attributes': {'root_node': {...}}},
 'profile_dict': {'5850585344': {'__instance_type__': [...],
                                 'attributes': {...}},
                  '5850592496': {'__instance_type__': [...],
                                 'attributes': {...}},
                  '5850589232': {'__instance_type__': [...],
                                 'attributes': {...}},
                  '5858434144': {'__instance_type__': [...],
                                 'attributes': {...}},
                  '5858435344': {'__instance_type__': [...],
                                 'attributes': {...}},
                  '5858436976': {'__instance_type__': [...],
                                 'attributes': {...}},
                  '5858433184': {'__instance_type__': [...],
                                 'attributes': {...}},
                  '5850587312': {'__instance_type__': [...],
                                 'attributes': {...}}},
 'parts_dict': {'5850585824': {'__instance_type__': [...], 'attributes': {...}},
                '5850587168': {'__instance_type__': [...], 'attributes': {...}},
                '5850593504': {'__instance_type__': [...], 'attributes': {...}},
                '5850585680': {'__instance_type__': [...], 'attributes': {...}},
                '5850593936': {'__instance_type__': [...], 'attributes': {...}},
                '5850593264': {'__instance_type__': [...], 'attributes': {...}},
                '5850590864': {'__instance_type__': [...], 'attributes': {...}},
                '5850584144': {'__instance_type__': [...], 'attributes': {...}},
                '5858442784': {'__instance_type__': [...],
                               'attributes': {...}}}}

The Sequential Model

Only the ifcs, gaps, and z_dir arrays are needed to fully specify the model.

json_sm = json_opm['seq_model']['attributes']
pprint.pprint(json_sm, sort_dicts=False, depth=2)
{'ifcs': [{...}, {...}, {...}, {...}, {...}, {...}, {...}, {...}],
 'gaps': [{...}, {...}, {...}, {...}, {...}, {...}, {...}],
 'z_dir': [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0],
 'do_apertures': True,
 'stop_surface': 3,
 'cur_surface': 6}

Surface encoding example

Note the profile is encoded in a separate json object, json_opm[‘profile_dict’]. The ‘profile_id’ attribute is a key to the actual profile, contained in the json_opm[‘profile_dict’].

json_ifcs2 = json_sm['ifcs'][2]
pprint.pprint(json_ifcs2, sort_dicts=False, depth=3)
{'__instance_type__': ['rayoptics.elem.surface', 'Surface'],
 'attributes': {'interact_mode': 'transmit',
                'delta_n': -0.6910020663241183,
                'decenter': None,
                'max_aperture': 8.948204697566771,
                'label': '',
                'clear_apertures': [],
                'edge_apertures': [],
                'profile_id': '5850589232'}}

Gap encoding example

json_gaps3 = json_sm['gaps'][3]
pprint.pprint(json_gaps3, sort_dicts=False, depth=6)
{'__instance_type__': ['rayoptics.seq.gap', 'Gap'],
 'attributes': {'thi': 0.975,
                'medium': {'__instance_type__': ['opticalglass.schott',
                                                 'SchottGlass'],
                           'slots': {},
                           'attributes': {'gname': 'N-SF5',
                                          'coefs': {'__ndarray__': [1.52481889,
                                                                    0.187085527,
                                                                    1.42729015,
                                                                    0.011254756,
                                                                    0.0588995392,
                                                                    129.141675],
                                                    'dtype': 'float64',
                                                    'shape': [6]}}}}}

Optical Spec

json_osp = json_opm['optical_spec']['attributes']
pprint.pprint(json_osp, sort_dicts=False, depth=1)
{'spectral_region': {...},
 'pupil': {...},
 'field_of_view': {...},
 'defocus': {...}}

spectral_region

json_wvls = json_osp['spectral_region']['attributes']
pprint.pprint(json_wvls, sort_dicts=False, depth=2)
{'wavelengths': [486.1327, 587.5618, 656.2725],
 'spectral_wts': [0.5, 1.0, 0.5],
 'render_colors': ['#268bd2', '#859900', '#dc322f'],
 'reference_wvl': 1,
 'coating_wvl': 550.0}

pupil

json_pupil = json_osp['pupil']['attributes']
pprint.pprint(json_pupil, sort_dicts=False, depth=3)
{'key': ['aperture', 'object', 'pupil'],
 'value': 12.5,
 'pupil_rays': [[0.0, 0.0], [1.0, 0.0], [-1.0, 0.0], [0.0, 1.0], [0.0, -1.0]],
 'ray_labels': ['00', '+X', '-X', '+Y', '-Y']}

field of view

json_fov = json_osp['field_of_view']['attributes']
pprint.pprint(json_fov, sort_dicts=False, depth=2)
{'key': ['field', 'object', 'angle'],
 'value': 20.0,
 'is_relative': True,
 'fields': [{...}, {...}, {...}],
 'index_labels': ['axis', ' 0.71y', 'edge']}

individual field point

json_field2 = json_fov['fields'][2]
pprint.pprint(json_field2, sort_dicts=False, depth=2)
{'__instance_type__': ['rayoptics.raytr.opticalspec', 'Field'],
 'attributes': {'x': 0.0,
                'y': 1.0,
                'vux': 0.0,
                'vuy': 0.0,
                'vlx': 0.0,
                'vly': 0.0,
                'wt': 1.0,
                'aim_pt': {...}}}

defocus

json_defocus = json_osp['defocus']['attributes']
pprint.pprint(json_defocus, sort_dicts=False, depth=1)
{'focus_shift': 0.0, 'defocus_range': 0.0}

Paraxial Model

json_pm = json_opm['parax_model']['attributes']
pprint.pprint(json_pm, sort_dicts=False, depth=3)
{'seq_mapping': None,
 'sys': [[0.0, 10000000000.0, 1.0, 'dummy'],
         [0.029140221242530184,
          2.856885923564585,
          1.6910020663241183,
          'transmit'],
         [-9.425384275234017e-05, 5.86, 1.0, 'transmit'],
         [-0.027506830028392514,
          0.5828874868684721,
          1.6727070351743674,
          'transmit'],
         [-0.03072282769338543, 4.822, 1.0, 'transmit'],
         [0.007964615386577972,
          1.8491993961884612,
          1.6910020663241183,
          'transmit'],
         [0.03371695730129101, 41.2365, 1.0, 'transmit'],
         [0.0, 0.0, 1.0, 'dummy']],
 'ax': [[0.0, 6.249999992700494e-10],
        [6.249999992700494, -0.18212638192810443],
        [5.729685695860345, -0.1815863370335065],
        [4.665589760843997, -0.05325075249976219],
        [4.634550563545556, 0.08913574590033027],
        [5.0643631302769485, 0.0488000413897083],
        [5.154604137348769, -0.12499752621433813],
        [0.000143647611214881, -0.12499752621433813]],
 'pr': [[-3639702346.9129076, 0.36397023426620234],
        [-4.250884056091309, 0.4878419361370471],
        [-2.8571752958168855, 0.48757263638599935],
        [3.5340507098524654e-07, 0.4875726461070525],
        [0.28420034776022174, 0.49630408442169],
        [2.677378642841611, 0.47497979328721845],
        [3.5557109897900556, 0.355092037668736],
        [18.198463801116887, 0.355092037668736]],
 'opt_inv': 2.274813964163764}

Profile dictionary

json_profiles = json_opm['profile_dict']
pprint.pprint(json_profiles, sort_dicts=False, depth=3)
{'5850585344': {'__instance_type__': ['rayoptics.elem.profiles', 'Spherical'],
                'attributes': {'cv': 0.0}},
 '5850592496': {'__instance_type__': ['rayoptics.elem.profiles', 'Spherical'],
                'attributes': {'cv': 0.042170961076202926}},
 '5850589232': {'__instance_type__': ['rayoptics.elem.profiles', 'Spherical'],
                'attributes': {'cv': 0.00013640168003221264}},
 '5858434144': {'__instance_type__': ['rayoptics.elem.profiles', 'Spherical'],
                'attributes': {'cv': -0.04088976120379457}},
 '5858435344': {'__instance_type__': ['rayoptics.elem.profiles', 'Spherical'],
                'attributes': {'cv': 0.04567044208987943}},
 '5858436976': {'__instance_type__': ['rayoptics.elem.profiles', 'Spherical'],
                'attributes': {'cv': 0.011526181721781025}},
 '5858433184': {'__instance_type__': ['rayoptics.elem.profiles', 'Spherical'],
                'attributes': {'cv': -0.04879429301948844}},
 '5850587312': {'__instance_type__': ['rayoptics.elem.profiles', 'Spherical'],
                'attributes': {'cv': 0.0}}}

sample profile instance

json_profile2 = json_profiles['5850589232']
pprint.pprint(json_profile2, sort_dicts=False, depth=3)
{'__instance_type__': ['rayoptics.elem.profiles', 'Spherical'],
 'attributes': {'cv': 0.00013640168003221264}}

Element Model

empty by design

json_em = json_opm['ele_model']['attributes']
pprint.pprint(json_em, sort_dicts=False, depth=2)
{}

Part Tree

json_pt = json_opm['part_tree']['attributes']
pprint.pprint(json_pt, sort_dicts=False, depth=3)
{'root_node': {'id_key': '5850583472',
               'tag': '#group#root',
               'name': 'root',
               'children': [{...},
                            {...},
                            {...},
                            {...},
                            {...},
                            {...},
                            {...},
                            {...},
                            {...}]}}

Part dictionary

json_parts = json_opm['parts_dict']
pprint.pprint(json_parts, sort_dicts=False, depth=2)
{'5850585824': {'__instance_type__': [...], 'attributes': {...}},
 '5850587168': {'__instance_type__': [...], 'attributes': {...}},
 '5850593504': {'__instance_type__': [...], 'attributes': {...}},
 '5850585680': {'__instance_type__': [...], 'attributes': {...}},
 '5850593936': {'__instance_type__': [...], 'attributes': {...}},
 '5850593264': {'__instance_type__': [...], 'attributes': {...}},
 '5850590864': {'__instance_type__': [...], 'attributes': {...}},
 '5850584144': {'__instance_type__': [...], 'attributes': {...}},
 '5858442784': {'__instance_type__': [...], 'attributes': {...}}}

sample Part, lens element #1

json_part2 = json_parts['5850593504']
pprint.pprint(json_part2, sort_dicts=False, depth=6)
{'__instance_type__': ['rayoptics.elem.elements', 'Element'],
 'attributes': {'label': 'E1',
                'tfrm': [{'__ndarray__': [[1.0, 0.0, 0.0],
                                          [0.0, 1.0, 0.0],
                                          [0.0, 0.0, 1.0]],
                          'dtype': 'float64',
                          'shape': [3, 3],
                          'Corder': True},
                         {'__ndarray__': [0.0, 0.0, 0.0],
                          'dtype': 'float64',
                          'shape': [3]}],
                's1_indx': 1,
                's2_indx': 2,
                'medium_name': 'N-LAK9',
                '_sd': 10.008722902253046,
                'hole_sd': None,
                'flat1': None,
                'flat2': 8.948204697566771,
                'do_flat1': 'if concave',
                'do_flat2': 'if concave',
                'edge_extent': [-10.008722902253046, 10.008722902253046],
                'profile1_id': '5850592496',
                'profile2_id': '5850589232'}}