Skip to content
UMass Ion Trappers edited this page Oct 30, 2023 · 25 revisions

Quickstart Guide

Our goal in this guide is to create this simple example baseplate

image image

The general format for a baseplate script is as follows:

from PyOptic import layout, optomech

# define baseplate constants, calculate parameters, etc.

# define the baseplate as a function so it can be imported into other files
def example_baseplate(x=0, y=0, angle=0):

    # define baseplate and beam
    # add components

# draw the baseplate if the file is run as a macro
if __name__ == "__main__":
    example_baseplate()
    layout.redraw()

The first thing to do is define our baseplate parameters and other constants
For most files, this will look something like this:

# baseplate sizing
base_dx = 4*layout.inch
base_dy = 4*layout.inch
base_dz = layout.inch
gap = layout.inch/8

# x-y coordinates of mount holes (in inches)
mount_holes = [(0, 0), (0, 3), (3, 0), (3, 3)]

# y coordinate of beam input
input_y = 1.5*layout.inch

The footprint of a given baseplate is defined by the dx and dy parameters with a gap distance removed from all sides. Because of this, the dx and dy parameters more so define the space allotted to the baseplate rather than its physical size. The gap defines the tolerance around the baseplate such that two can be mounted directly next to one another without issue.

Next we can start building our layout
Everything we add to our layout should be defined inside of a function so it can be imported into other files.

def example_baseplate(x=0, y=0, angle=0):

    # define and place baseplate object
    baseplate = layout.baseplate(base_dx, base_dy, base_dz, x=x, y=y, angle=angle,
                                 gap=gap, mount_holes=mount_holes)

It's important to save the baseplate instance here as it will be used to place all the components.

Now we can begin placing beams and components
First we'll place a beam and an input fiberport:

    # add beam
    beam = baseplate.add_beam_path(x=gap, y=input_y, angle=layout.cardinal['right'])

    # add input fiberport, defined at the same coordinates as beam
    baseplate.place_element("Input Fiberport", optomech.fiberport_holder,
                            x=gap, y=input_y, angle=layout.cardinal['right'])

The first two arguments of any component placement function are:

  • The name for the component
  • The object class for the type of component desired
    All available object classes can be found under the Optomech section of the docs

How you define the position of the part can vary
Both the beam and input fiberport are placed using an x, y coordinate and angle
The rest of the parts will be defined along the beam instead:

    # add splitter component along beam, 40 mm from beam input
    baseplate.place_element_along_beam("Beam Splitter Cube", optomech.pbs_on_skate_mount, beam,
                                       beam_index=0b1, distance=40, angle=layout.cardinal['right'])

    # add rotation stage along the transmitted beam, 30 mm from the splitter cube
    baseplate.place_element_along_beam("Rotation Stage", optomech.rotation_stage_rsp05, beam,
                                       beam_index=0b10, distance=30, angle=layout.cardinal['right'])

    # add mirror along the reflected beam, 1 inch from the splitter cube
    baseplate.place_element_along_beam("Mirror", optomech.mirror_mount_k05s2, beam,
                                       beam_index=0b11, distance=layout.inch, angle=layout.turn['up-right'])

    # add output fiberport along the beam, defined by settings it's x position to the edge of the baseplate
    baseplate.place_element_along_beam("Output Fiberport", optomech.fiberport_holder, beam,
                                       beam_index=0b11, x=base_dx-gap, angle=layout.cardinal['left'])

There are 4 necessary arguments for placing a component along the beam:

  • The beam path object
  • The beam index
  • The component angle
  • One placement defining argument, this can be the distance from the last component, an x coordinate, or a y coordinate.

The beam index is what allows for placement along complex beam paths involving splits and diffractions.
You should think of the beam index as a binary string, with each 1 and 0 representing what path the beam took at each junction.
Every beam starts at beam index 0b1 (while you could write this as an integer, taking advantage of python's binary literals makes for much better readability).
Whenever a beam splits, a zero is added to the index of the transmitted beam and a 1 is added to the index of the reflected/diffracted beam.
This image should help demostrate the structure:
image

The ability to define different placement conditions can also be very helpful for both dynamic baseplate designs or specific components.
As can be seen in the example script, something like a fiberport can be defined by an x coordinate rather than a distance to ensure it is always placed at the edge of the baseplate.

One last thing to note is the use of the layout.turn dictionary for mirror angles.
The declaration is quite verbose, layout.turn['down-right'] will place a mirror which would take a downwards beam and reflect it to the right, ie placing the mirror at 45 degrees.

Now that we have our script, we're ready to run it in FreeCAD!
To recap, we should now have something like this:

from PyOptic import layout, optomech

# baseplate constants
base_dx = 4*layout.inch
base_dy = 4*layout.inch
base_dz = layout.inch
gap = layout.inch/8

# x-y coordinates of mount holes (in inches)
mount_holes = [(0, 0), (0, 3), (3, 0), (3, 3)]

# y coordinate of beam input
input_y = 1.5*layout.inch

# function so baseplate can be added to other layouts
def example_baseplate(x=0, y=0, angle=0):

    # define and place baseplate object
    baseplate = layout.baseplate(base_dx, base_dy, base_dz, x=x, y=y, angle=angle,
                                 gap=gap, mount_holes=mount_holes)

    # add beam
    beam = baseplate.add_beam_path(x=gap, y=input_y, angle=layout.cardinal['right'])

    # add input fiberport, defined at the same coordinates as beam
    baseplate.place_element("Input Fiberport", optomech.fiberport_holder,
                            x=gap, y=input_y, angle=layout.cardinal['right'])

    # add splitter component along beam, 40 mm from beam input
    baseplate.place_element_along_beam("Beam Splitter Cube", optomech.pbs_on_skate_mount, beam,
                                       beam_index=0b1, distance=40, angle=layout.cardinal['right'])

    # add rotation stage along the transmitted beam, 30 mm from the splitter cube
    baseplate.place_element_along_beam("Rotation Stage", optomech.rotation_stage_rsp05, beam,
                                       beam_index=0b10, distance=30, angle=layout.cardinal['right'])

    # add mirror along the reflected beam, 1 inch from the splitter cube
    baseplate.place_element_along_beam("Mirror", optomech.mirror_mount_k05s2, beam,
                                       beam_index=0b11, distance=layout.inch, angle=layout.turn['up-right'])

    # add output fiberport along the beam, defined by settings it's x position to the edge of the baseplate
    baseplate.place_element_along_beam("Output Fiberport", optomech.fiberport_holder, beam,
                                       beam_index=0b11, x=base_dx-gap, angle=layout.cardinal['left'])

# this allows the file to be run as a macro or imported into other files
if __name__ == "__main__":
    example_baseplate()
    layout.redraw()

Launch FreeCAD and navigate to "Macro" > "Macros ..."
image

Select the location of your script in the "User macros location:" bar
image

Now select your script in the list and hit "Execute"!
image

Once you've run your script once it will then also appear in the "Macro" > "Recent macros" drop down list
Or you can re-run your last macro with Ctrl-Shift-1

Congratulations! You're ready to start making your own layouts in PyOptic, so get making!

Clone this wiki locally