Die assembly#

With gdsfactory you can easily go from a simple Component, to a Component with many components inside.

In the same way that you need to Layout for DRC (Design Rule Check) clean devices, you have to layout obeying the Design for Test (DFT) and Design for Packaging rules.

Design for test#

To measure your chips after fabrication you need to decide your test configurations. This includes Design For Testing Rules like:

  • Individual input and output fibers versus fiber array. You can use add_fiber_array for easier testing and higher throughput, or add_fiber_single for the flexibility of single fibers.

  • Fiber array pitch (127um or 250um) if using a fiber array.

  • Pad pitch for DC and RF high speed probes (100, 125, 150, 200um). Probe configuration (GSG, GS …)

  • Test layout for DC, RF and optical fibers.

from functools import partial

import json
import gdsfactory as gf
from gdsfactory.generic_tech import get_generic_pdk
from gdsfactory.labels import add_label_ehva, add_label_json

Pack#

Lets start with a resistance sweep, where you change the resistance width to measure sheet resistance.

sweep = [gf.components.resistance_sheet(width=width) for width in [1, 10, 100]]
m = gf.pack(sweep)
c = m[0]
c.plot()
../_images/e30956bac590fc53b4c4bd0118dd731809c2ba70e4de03634cd975278b7438b8.png

Then we add spirals with different lengths to measure waveguide propagation loss. You can use both fiber array or single fiber.

from toolz import compose
from functools import partial
import gdsfactory as gf

gf.config.rich_output()

c = gf.components.spiral_inner_io_fiber_array(length=20e3)
c.info["measurement"] = "optical_loopback2"
c = gf.labels.add_label_json(c)
c.plot()

../_images/418bf85d600ba532cbb0c53a64cfa1d9f4a227a8d7935e3dc174355d612ddfab.png
c.info

Info(length=20000.009, polarization='te', wavelength=1.53, measurement='optical_loopback2')
spiral = gf.components.spiral_inner_io_fiber_single()
spiral.plot()

../_images/b8c287b3937b021c9f91388ca82e1373b65035f32f3efcf1ed50f68b7557c9e2.png
spiral_te = gf.routing.add_fiber_single(
    gf.functions.rotate(gf.components.spiral_inner_io_fiber_single, 90)
)
spiral_te.plot()
2024-10-31 18:39:38.470 | WARNING  | gdsfactory.component:_write_library:1934 - UserWarning: Component rotate_add_fiber_single_6f8349ab has invalid transformations. Try component.flatten_offgrid_references() first.

../_images/d93db43ded2ca7c9eaa7ea5237a26d5b75e3d8e9a53cc45601b773e91cfce0fe.png
# which is equivalent to
spiral_te = gf.compose(
    gf.routing.add_fiber_single,
    gf.functions.rotate90,
    gf.components.spiral_inner_io_fiber_single,
)
c = spiral_te(length=10e3)
c.plot()
2024-10-31 18:39:38.884 | WARNING  | gdsfactory.component:_write_library:1934 - UserWarning: Component rotate_add_fiber_single_6774eb77 has invalid transformations. Try component.flatten_offgrid_references() first.

../_images/0cf179e359cea2923e244b7d4a913f8d4551bd70a6d149c0303365e9c02b1d7d.png
add_fiber_single_no_labels = partial(
    gf.routing.add_fiber_single,
    get_input_label_text_function=None,
)

spiral_te = gf.compose(
    add_fiber_single_no_labels,
    gf.functions.rotate90,
    gf.components.spiral_inner_io_fiber_single,
)
sweep = [spiral_te(length=length) for length in [10e3, 20e3, 30e3]]
m = gf.pack(sweep)
c = m[0]
c.plot()
2024-10-31 18:39:39.219 | WARNING  | gdsfactory.component:_write_library:1934 - UserWarning: Component pack_0$1 has invalid transformations. Try component.flatten_offgrid_references() first.

../_images/9bee2db81473e9dc8cb13d63ce356ef5787ecffeb0139d04d28cffb90a5b7c4e.png
from toolz import compose
from functools import partial
import gdsfactory as gf

c = gf.components.spiral_inner_io_fiber_array(length=20e3)
c.info["measurement"] = "optical_loopback2"
c = gf.labels.add_label_json(c)
c.show()
c.plot()
2024-10-31 18:39:39.371 | WARNING  | gdsfactory.klive:show:49 - UserWarning: Could not connect to klive server. Is klayout open and klive plugin installed?

../_images/418bf85d600ba532cbb0c53a64cfa1d9f4a227a8d7935e3dc174355d612ddfab.png
def add_label_json(component):
    """Add label json and component.info)"""
    component.info["measurement"] = "optical_loopback2"
    component = gf.labels.add_label_json(component)
    return component
sweep = [
    add_label_json(gf.components.spiral_inner_io_fiber_array(length=length))
    for length in [20e3, 30e3, 40e3]
]
m = gf.pack(sweep)
c = m[0]
c.show()
c.plot()

../_images/224d11a30ac3d6e7fd7742c9282b718b0134057282ca5ec96ac2ab2f18c5ee1e.png

You can also add some physical labels that will be fabricated. For example you can add prefix S at the north-center of each spiral using text_rectangular which is DRC clean and anchored on nc (north-center)

text_metal = partial(gf.components.text_rectangular_multi_layer, layers=("M1",))

m = gf.pack(sweep, text=text_metal, text_anchors=("cw",), text_prefix="s")
c = m[0]
c.show()
c.plot()

../_images/98841851a5975e053c04b21840f8e77454c8d10734a5fa8688a8eb678ca0a876.png

Grid#

You can also pack components with a constant spacing.

g = gf.grid_with_component_name(sweep)
g.plot()

../_images/cd8ad160ff00917ecbe835f80450beb5a16917738ffc056f91d434608e2851b1.png
gh = gf.grid_with_component_name(sweep, shape=(1, len(sweep)))
gh.plot()

../_images/730364867db7a6ba861e488625904f091c3beba2558e6bb90143a322587f7d6e.png
gh_ymin = gf.grid_with_component_name(sweep, shape=(len(sweep), 1), align_x="xmin")
gh_ymin.plot()

../_images/baf44047705d9b8bbd399126e3bfae2901e9c157cabbc7bda8c6e248912bec05.png

You can also add text labels to each element of the sweep

gh_ymin = gf.grid_with_text(
    sweep, shape=(len(sweep), 1), align_x="xmax", text=text_metal
)
gh_ymin.plot()

../_images/c349e0a040f4db51e72bb68a3411fa971b7b9f346ddc073c4667f4dad291e649.png

You have 2 ways of defining a mask:

  1. in python

  2. in YAML

1. Component in python#

You can define a Component top cell reticle or die using grid and pack python functions.

text_metal3 = partial(gf.components.text_rectangular_multi_layer, layers=((49, 0),))
grid = partial(gf.grid_with_text, text=text_metal3)
pack = partial(gf.pack, text=text_metal3)

gratings_sweep = [
    gf.components.grating_coupler_elliptical(taper_angle=taper_angle)
    for taper_angle in [20, 30, 40]
]
gratings = grid(gratings_sweep, text=None)
gratings.plot()

../_images/1d8817e3b35841d8cfe2c8739dc4bc9e08383ff71581b1ef7dfacbb17f6c06c8.png
gratings_sweep = [
    gf.components.grating_coupler_elliptical(taper_angle=taper_angle)
    for taper_angle in [20, 30, 40]
]
gratings_loss_sweep = [
    gf.components.grating_coupler_loss_fiber_single(grating_coupler=grating)
    for grating in gratings_sweep
]
gratings = grid(
    gratings_loss_sweep, shape=(1, len(gratings_loss_sweep)), spacing=(40, 0)
)
gratings.plot()

../_images/532ca4e0198dd8dbf4887e3a6bcd9a80ab3878ab999e01cb41cdefc48d2d6a54.png
sweep_resistance = [
    gf.components.resistance_sheet(width=width) for width in [1, 10, 100]
]
resistance = gf.pack(sweep_resistance)[0]
resistance.plot()

../_images/e30956bac590fc53b4c4bd0118dd731809c2ba70e4de03634cd975278b7438b8.png
spiral_te = gf.compose(
    gf.routing.add_fiber_single,
    gf.functions.rotate90,
    gf.components.spiral_inner_io_fiber_single,
)
sweep_spirals = [spiral_te(length=length) for length in [10e3, 20e3, 30e3]]
spirals = gf.pack(sweep_spirals)[0]
spirals.plot()
2024-10-31 18:39:41.459 | WARNING  | gdsfactory.component:_write_library:1934 - UserWarning: Component pack_0$5 has invalid transformations. Try component.flatten_offgrid_references() first.

../_images/9bee2db81473e9dc8cb13d63ce356ef5787ecffeb0139d04d28cffb90a5b7c4e.png
mask = gf.pack([spirals, resistance, gratings])[0]
mask.plot()
2024-10-31 18:39:41.623 | WARNING  | gdsfactory.component:_write_library:1934 - UserWarning: Component pack_0$6 has invalid transformations. Try component.flatten_offgrid_references() first.

../_images/4dec860fd0837863384c4c6b3f6398088e9e557e0289415051866bbd48374710.png

As you can see you can define your mask in a single line.

For more complex mask, you can also create a new cell to build up more complexity

@gf.cell
def mask():
    c = gf.Component()
    c << gf.pack([spirals, resistance, gratings])[0]
    c << gf.components.seal_ring(c.bbox)
    return c


c = mask()
c.plot()

../_images/e8355e0d25c4d861074bf5a214fd02139d5087ed18bc709dda3b4c5d5a1d9263.png

2. Component in YAML#

You can also define your component in YAML format thanks to gdsfactory.read.from_yaml

You need to define:

  • instances

  • placements

  • routes (optional)

and you can leverage:

  1. pack_doe

  2. pack_doe_grid

2.1 pack_doe#

pack_doe places components as compact as possible.

c = gf.read.from_yaml(
    """
name: mask_grid

instances:
  rings:
    component: pack_doe
    settings:
      doe: ring_single
      settings:
        radius: [30, 50, 20, 40]
        length_x: [1, 2, 3]
      do_permutations: True
      function:
        function: add_fiber_array
        settings:
            fanout_length: 200

  mzis:
    component: pack_doe
    settings:
      doe: mzi
      settings:
        delta_length: [10, 100]
      function: add_fiber_array

placements:
  rings:
    xmin: 50

  mzis:
    xmin: rings,east
"""
)

c.plot()
2024-10-31 18:39:41.986 | WARNING  | gdsfactory.read.from_yaml:from_yaml:692 - UserWarning: prefix is deprecated and will be removed soon. _from_yaml

../_images/406be93815ecc7186d4ab7422f7e6fd8a594f675d4b97e46eec78f6aac7714a7.png

2.2 pack_doe_grid#

pack_doe_grid places each component on a regular grid

c = gf.read.from_yaml(
    """
name: mask_compact

instances:
  rings:
    component: pack_doe
    settings:
      doe: ring_single
      settings:
        radius: [30, 50, 20, 40]
        length_x: [1, 2, 3]
      do_permutations: True
      function:
        function: add_fiber_array
        settings:
            fanout_length: 200


  mzis:
    component: pack_doe_grid
    settings:
      doe: mzi
      settings:
        delta_length: [10, 100]
      do_permutations: True
      spacing: [10, 10]
      function: add_fiber_array

placements:
  rings:
    xmin: 50

  mzis:
    xmin: rings,east
"""
)
c.plot()

../_images/19ba4be5146b73da2f471083c0e302e125d4ff89d6b158a641d7cb69a272b4d6.png

Automated testing exposing all ports#

You can promote all the ports that need to be tested to the top level component and then write a CSV test manifest.

This is the recommended way for measuring components that have electrical and optical port.

test_info_spirals = dict(
    doe="spirals_sc",
    measurement="optical_loopback4",
    analysis="optical_loopback4_spirals",
)
test_info_mzi_heaters = dict(
    doe="mzis_heaters",
    analysis="mzi_heater",
    measurement="optical_loopback4_heater_sweep",
)
test_info_ring_heaters = dict(
    doe="ring_heaters",
    analysis="ring_heater",
    measurement="optical_loopback2_heater_sweep",
)


def sample_reticle() -> gf.Component:
    """Returns MZI with TE grating couplers."""

    mzis = [
        gf.components.mzi2x2_2x2_phase_shifter(length_x=length)
        for length in [100, 200, 300]
    ]
    rings = [
        gf.components.ring_single_heater(length_x=length_x) for length_x in [10, 20, 30]
    ]

    spirals_te = [
        gf.components.spiral_inner_io_fiber_array(
            length=length,
        )
        for length in [20e3, 40e3, 60e3]
    ]
    mzis_te = [
        gf.components.add_fiber_array_optical_south_electrical_north(
            mzi,
            electrical_port_names=["top_l_e2", "top_r_e2"],
        )
        for mzi in mzis
    ]
    rings_te = [
        gf.components.add_fiber_array_optical_south_electrical_north(
            ring,
            electrical_port_names=["l_e2", "r_e2"],
        )
        for ring in rings
    ]

    for component in mzis_te:
        component.info.update(test_info_mzi_heaters)

    for component in rings_te:
        component.info.update(test_info_ring_heaters)

    for component in spirals_te:
        component.info.update(test_info_spirals)

    components = mzis_te + rings_te + spirals_te
    return gf.pack(components)[0]


c = sample_reticle()
c.plot()

../_images/79f3157ed64ebf67df7b2701d56825d1b2b37ab1258ccfe9a66573d9a54444f6.png
c.pprint_ports()
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━┓
┃ name                                         width  center                orientation  layer    port_type  ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━┩
│ mzi_add_fiber_array_optical_south_electric… │ 80.0  │ [285.635, 1260.05]   │ 270.0       │ [49, 0] │ electrical │
│ mzi_add_fiber_array_optical_south_electric… │ 80.0  │ [385.635, 1260.05]   │ 270.0       │ [49, 0] │ electrical │
│ mzi_add_fiber_array_optical_south_electric… │ 0.5   │ [18.135, 1055.495]   │ 90.0        │ [1, 0]  │ optical    │
│ mzi_add_fiber_array_optical_south_electric… │ 0.5   │ [653.135, 1055.495]  │ 90.0        │ [1, 0]  │ optical    │
│ mzi_add_fiber_array_optical_south_electric… │ 80.0  │ [285.635, 920.05]    │ 270.0       │ [49, 0] │ electrical │
│ mzi_add_fiber_array_optical_south_electric… │ 80.0  │ [385.635, 920.05]    │ 270.0       │ [49, 0] │ electrical │
│ mzi_add_fiber_array_optical_south_electric… │ 0.5   │ [18.135, 715.495]    │ 90.0        │ [1, 0]  │ optical    │
│ mzi_add_fiber_array_optical_south_electric… │ 0.5   │ [653.135, 715.495]   │ 90.0        │ [1, 0]  │ optical    │
│ mzi_add_fiber_array_optical_south_electric… │ 80.0  │ [285.635, 580.05]    │ 270.0       │ [49, 0] │ electrical │
│ mzi_add_fiber_array_optical_south_electric… │ 80.0  │ [385.635, 580.05]    │ 270.0       │ [49, 0] │ electrical │
│ mzi_add_fiber_array_optical_south_electric… │ 0.5   │ [18.135, 375.495]    │ 90.0        │ [1, 0]  │ optical    │
│ mzi_add_fiber_array_optical_south_electric… │ 0.5   │ [653.135, 375.495]   │ 90.0        │ [1, 0]  │ optical    │
│ ring_single_heater_add_fiber_array_optical… │ 80.0  │ [829.805, 1260.05]   │ 270.0       │ [49, 0] │ electrical │
│ ring_single_heater_add_fiber_array_optical… │ 80.0  │ [929.805, 1260.05]   │ 270.0       │ [49, 0] │ electrical │
│ ring_single_heater_add_fiber_array_optical… │ 0.5   │ [689.305, 1055.494]  │ 90.0        │ [1, 0]  │ optical    │
│ ring_single_heater_add_fiber_array_optical… │ 0.5   │ [1070.305, 1055.494] │ 90.0        │ [1, 0]  │ optical    │
│ ring_single_heater_add_fiber_array_optical… │ 80.0  │ [829.805, 580.05]    │ 270.0       │ [49, 0] │ electrical │
│ ring_single_heater_add_fiber_array_optical… │ 80.0  │ [929.805, 580.05]    │ 270.0       │ [49, 0] │ electrical │
│ ring_single_heater_add_fiber_array_optical… │ 0.5   │ [689.305, 375.494]   │ 90.0        │ [1, 0]  │ optical    │
│ ring_single_heater_add_fiber_array_optical… │ 0.5   │ [1070.305, 375.494]  │ 90.0        │ [1, 0]  │ optical    │
│ ring_single_heater_add_fiber_array_optical… │ 80.0  │ [829.805, 920.05]    │ 270.0       │ [49, 0] │ electrical │
│ ring_single_heater_add_fiber_array_optical… │ 80.0  │ [929.805, 920.05]    │ 270.0       │ [49, 0] │ electrical │
│ ring_single_heater_add_fiber_array_optical… │ 0.5   │ [689.305, 715.494]   │ 90.0        │ [1, 0]  │ optical    │
│ ring_single_heater_add_fiber_array_optical… │ 0.5   │ [1070.305, 715.494]  │ 90.0        │ [1, 0]  │ optical    │
│ spiral_inner_io_fiber_array-loopback1       │ 0.5   │ [360.824, 1455.3]    │ 90.0        │ [1, 0]  │ optical    │
│ spiral_inner_io_fiber_array-loopback2       │ 0.5   │ [741.824, 1455.3]    │ 90.0        │ [1, 0]  │ optical    │
│ spiral_inner_io_fiber_array-o1              │ 0.5   │ [487.824, 1459.3]    │ 90.0        │ [1, 0]  │ optical    │
│ spiral_inner_io_fiber_array-o2              │ 0.5   │ [614.824, 1459.3]    │ 90.0        │ [1, 0]  │ optical    │
│ spiral_inner_io_fiber_array_length40000p0-… │ 0.5   │ [1269.914, 272.8]    │ 90.0        │ [1, 0]  │ optical    │
│ spiral_inner_io_fiber_array_length40000p0-… │ 0.5   │ [1650.914, 272.8]    │ 90.0        │ [1, 0]  │ optical    │
│ spiral_inner_io_fiber_array_length40000p0-… │ 0.5   │ [1396.914, 276.8]    │ 90.0        │ [1, 0]  │ optical    │
│ spiral_inner_io_fiber_array_length40000p0-… │ 0.5   │ [1523.914, 276.8]    │ 90.0        │ [1, 0]  │ optical    │
│ spiral_inner_io_fiber_array_length60000p0-… │ 0.5   │ [2179.01, 110.3]     │ 90.0        │ [1, 0]  │ optical    │
│ spiral_inner_io_fiber_array_length60000p0-… │ 0.5   │ [2560.01, 110.3]     │ 90.0        │ [1, 0]  │ optical    │
│ spiral_inner_io_fiber_array_length60000p0-… │ 0.5   │ [2306.01, 114.3]     │ 90.0        │ [1, 0]  │ optical    │
│ spiral_inner_io_fiber_array_length60000p0-… │ 0.5   │ [2433.01, 114.3]     │ 90.0        │ [1, 0]  │ optical    │
└─────────────────────────────────────────────┴───────┴──────────────────────┴─────────────┴─────────┴────────────┘
df = gf.labels.get_test_manifest(c)
df

cell measurement measurement_settings analysis analysis_settings doe doe wavelength polarization parent analysis measurement length
0 mzi_add_fiber_array_optical_south_electrical_n... optical_loopback4_heater_sweep None mzi_heater None mzis_heaters mzis_heaters NaN None mzi_add_fiber_array_optical_south_electrical_n... mzi_heater optical_loopback4_heater_sweep NaN
1 mzi_add_fiber_array_optical_south_electrical_n... optical_loopback4_heater_sweep None mzi_heater None mzis_heaters mzis_heaters NaN None mzi_add_fiber_array_optical_south_electrical_n... mzi_heater optical_loopback4_heater_sweep NaN
2 mzi_add_fiber_array_optical_south_electrical_n... optical_loopback4_heater_sweep None mzi_heater None mzis_heaters mzis_heaters NaN None mzi_add_fiber_array_optical_south_electrical_n... mzi_heater optical_loopback4_heater_sweep NaN
3 ring_single_heater_add_fiber_array_optical_sou... optical_loopback2_heater_sweep None ring_heater None ring_heaters ring_heaters NaN None ring_single_heater_add_fiber_array_optical_sou... ring_heater optical_loopback2_heater_sweep NaN
4 ring_single_heater_add_fiber_array_optical_sou... optical_loopback2_heater_sweep None ring_heater None ring_heaters ring_heaters NaN None ring_single_heater_add_fiber_array_optical_sou... ring_heater optical_loopback2_heater_sweep NaN
5 ring_single_heater_add_fiber_array_optical_sou... optical_loopback2_heater_sweep None ring_heater None ring_heaters ring_heaters NaN None ring_single_heater_add_fiber_array_optical_sou... ring_heater optical_loopback2_heater_sweep NaN
6 spiral_inner_io_fiber_array optical_loopback4 None optical_loopback4_spirals None spirals_sc spirals_sc 1.53 te spiral_inner_io_fiber_array optical_loopback4_spirals optical_loopback4 20000.009
7 spiral_inner_io_fiber_array_length40000p0 optical_loopback4 None optical_loopback4_spirals None spirals_sc spirals_sc 1.53 te spiral_inner_io_fiber_array_length40000p0 optical_loopback4_spirals optical_loopback4 39999.989
8 spiral_inner_io_fiber_array_length60000p0 optical_loopback4 None optical_loopback4_spirals None spirals_sc spirals_sc 1.53 te spiral_inner_io_fiber_array_length60000p0 optical_loopback4_spirals optical_loopback4 60000.013
df.to_csv("test_manifest.csv")
def sample_reticle_grid() -> gf.Component:
    """Returns sample reticle with grid packer."""

    mzis = [
        gf.components.mzi2x2_2x2_phase_shifter(length_x=length)
        for length in [100, 200, 300]
    ]
    rings = [
        gf.components.ring_single_heater(length_x=length_x) for length_x in [10, 20, 30]
    ]

    spirals_te = [
        gf.components.spiral_inner_io_fiber_array(
            length=length,
        )
        for length in [20e3, 40e3, 60e3]
    ]
    mzis_te = [
        gf.components.add_fiber_array_optical_south_electrical_north(
            mzi,
            electrical_port_names=["top_l_e2", "top_r_e2"],
        )
        for mzi in mzis
    ]
    rings_te = [
        gf.components.add_fiber_array_optical_south_electrical_north(
            ring,
            electrical_port_names=["l_e2", "r_e2"],
        )
        for ring in rings
    ]

    for component in mzis_te:
        component.info.update(test_info_mzi_heaters)

    for component in rings_te:
        component.info.update(test_info_ring_heaters)

    for component in spirals_te:
        component.info.update(test_info_spirals)

    components = mzis_te + rings_te + spirals_te
    return gf.grid(components)


c = sample_reticle_grid()
c.plot()
2024-10-31 18:39:43.788 | WARNING  | gdsfactory.component:_write_library:1934 - UserWarning: Component grid_e413d6ec has invalid transformations. Try component.flatten_offgrid_references() first.

../_images/0b382b39b344182c1d0b763341a9f1763a92ae56bc70ae595ad14e68b22a3104.png
df = gf.labels.get_test_manifest(c)
df

cell measurement measurement_settings analysis analysis_settings doe doe wavelength polarization parent analysis measurement length
0 mzi_add_fiber_array_optical_south_electrical_n... optical_loopback4_heater_sweep None mzi_heater None mzis_heaters mzis_heaters NaN None mzi_add_fiber_array_optical_south_electrical_n... mzi_heater optical_loopback4_heater_sweep NaN
1 mzi_add_fiber_array_optical_south_electrical_n... optical_loopback4_heater_sweep None mzi_heater None mzis_heaters mzis_heaters NaN None mzi_add_fiber_array_optical_south_electrical_n... mzi_heater optical_loopback4_heater_sweep NaN
2 mzi_add_fiber_array_optical_south_electrical_n... optical_loopback4_heater_sweep None mzi_heater None mzis_heaters mzis_heaters NaN None mzi_add_fiber_array_optical_south_electrical_n... mzi_heater optical_loopback4_heater_sweep NaN
3 ring_single_heater_add_fiber_array_optical_sou... optical_loopback2_heater_sweep None ring_heater None ring_heaters ring_heaters NaN None ring_single_heater_add_fiber_array_optical_sou... ring_heater optical_loopback2_heater_sweep NaN
4 ring_single_heater_add_fiber_array_optical_sou... optical_loopback2_heater_sweep None ring_heater None ring_heaters ring_heaters NaN None ring_single_heater_add_fiber_array_optical_sou... ring_heater optical_loopback2_heater_sweep NaN
5 ring_single_heater_add_fiber_array_optical_sou... optical_loopback2_heater_sweep None ring_heater None ring_heaters ring_heaters NaN None ring_single_heater_add_fiber_array_optical_sou... ring_heater optical_loopback2_heater_sweep NaN
6 spiral_inner_io_fiber_array optical_loopback4 None optical_loopback4_spirals None spirals_sc spirals_sc 1.53 te spiral_inner_io_fiber_array optical_loopback4_spirals optical_loopback4 20000.009
7 spiral_inner_io_fiber_array_length40000p0 optical_loopback4 None optical_loopback4_spirals None spirals_sc spirals_sc 1.53 te spiral_inner_io_fiber_array_length40000p0 optical_loopback4_spirals optical_loopback4 39999.989
8 spiral_inner_io_fiber_array_length60000p0 optical_loopback4 None optical_loopback4_spirals None spirals_sc spirals_sc 1.53 te spiral_inner_io_fiber_array_length60000p0 optical_loopback4_spirals optical_loopback4 60000.013
df.to_csv("test_manifest.csv")

You can see a test manifest example here