Upload Measurements

Now you will post your measurement data and analysis to the database via the API.

You will need to authenticate to the database with your username and password. To make this easy, you can create a file called .env in this folder and complete it with your organization's URL and authentication information as follows:

GDSFACTORY_HUB_API_URL="https://{org}.gdsfactoryhub.com"
GDSFACTORY_HUB_QUERY_URL="https://query.{org}.gdsfactoryhub.com"
GDSFACTORY_HUB_KEY="<your-gdsfactoryplus-api-key>"
import getpass
from pathlib import Path

import pandas as pd
from tqdm.auto import tqdm

import gdsfactoryhub as gfh

New project

You can create the project, upload the design manifest, and upload the wafer definitions through the Webapp as well as programmatically using this notebook

project_id = f"rings-{getpass.getuser()}"
client = gfh.create_client_from_env(project_id=project_id)
api = client.api()
query = client.query()
utils = client.utils()

Let's now create a project.

In normal circumstances, everyone will be sharing and contributing to a project. In this demo, however, we want to keep your project separate from other users for clarity, so we will append your username to the project name. This way you can also safely delete and recreate projects without creating issues for others. If you prefer though, you can change the project_id to anything you like. Just be sure to update it in the subsequent notebooks of this tutorial as well.

Lets delete the project if it already exists so that you can start fresh.

api.delete_project()
project = api.create_project(
    eda_layout_file="test_chip.gds",
    extraction_rules=[
        gfh.ExtractionRule(
            cell_name="rings",
            max_depth=1,
        )
    ],
)

Upload Design Manifest

The design manifest is a CSV file that includes all the cell names, the cell settings, a list of analysis to trigger, and a list of settings for each analysis.

dm = pd.read_csv("design_manifest.csv")
dm
cell x y radius_um gap_um analysis analysis_parameters
0 RingDouble-20-0.25- 331580 121311 20 0.25 [fsr] [{"height": -0.01, "distance": 20}]
1 RingDouble-20-0.2- 331580 285371 20 0.20 [fsr] [{"height": -0.01, "distance": 20}]
2 RingDouble-20-0.15- 331580 449331 20 0.15 [fsr] [{"height": -0.01, "distance": 20}]
3 RingDouble-10-0.2- 331480 613191 10 0.20 [fsr] [{"height": -0.01, "distance": 20}]
4 RingDouble-10-0.15- 331480 757151 10 0.15 [fsr] [{"height": -0.01, "distance": 20}]
5 RingDouble-10-0.1- 331480 901011 10 0.10 [fsr] [{"height": -0.01, "distance": 20}]
6 RingDouble-5-0.2- 331480 1044771 5 0.20 [fsr] [{"height": -0.01, "distance": 20}]
7 RingDouble-5-0.15- 331480 1178731 5 0.15 [fsr] [{"height": -0.01, "distance": 20}]
8 RingDouble-5-0.1- 331480 1312591 5 0.10 [fsr] [{"height": -0.01, "distance": 20}]

As you don't have an analysis defined yet you need to drop the columns that allow you to run automated analysis on measurement upload.

dm = dm.drop(columns=["analysis", "analysis_parameters"])
dm
cell x y radius_um gap_um
0 RingDouble-20-0.25- 331580 121311 20 0.25
1 RingDouble-20-0.2- 331580 285371 20 0.20
2 RingDouble-20-0.15- 331580 449331 20 0.15
3 RingDouble-10-0.2- 331480 613191 10 0.20
4 RingDouble-10-0.15- 331480 757151 10 0.15
5 RingDouble-10-0.1- 331480 901011 10 0.10
6 RingDouble-5-0.2- 331480 1044771 5 0.20
7 RingDouble-5-0.15- 331480 1178731 5 0.15
8 RingDouble-5-0.1- 331480 1312591 5 0.10
dm.to_csv("design_manifest_without_analysis.csv", index=False)
api.upload_design_manifest(
    design_attributes_file="design_manifest_without_analysis.csv",
)
'"Design Attributes has been updated successfully"'
api.download_design_manifest(
    file_path="design_manifest_downloaded.csv",
);

Upload Wafer Definitions

The wafer definition is a JSON file where you can define the wafer names and die names and location for each wafer.

api.upload_wafer_definitions(
    wafer_definitions_file="wafer_definitions.json",
)
'"Wafers have been added successfully"'

Upload data

Your Tester can output the data in JSON files. It does not need to be Python.

You can get all paths which have measurement data within the test path.

data_files = list(Path("wafers").resolve().glob("**/data.parquet"))
len(data_files)
576

You can now upload measurement data.

This is a bare bones example, in a production setting, you can also add validation, logging, and error handling to ensure a smooth operation.

Every measurement you upload will trigger all the analysis that you defined in the design manifest.

cell2device = {c["cell_id"]: c["devices"][0]["device_id"] for c in query.cells().execute().data}
results = []
for path in (pb := tqdm(data_files)):
    wafer_id = path.parts[-4]
    die_x, die_y = path.parts[-3].split("_")
    cell_id = path.parts[-2].split("_")[1]
    device_id = cell2device[cell_id]
    pb.set_postfix(device_id=device_id, wafer_id=wafer_id, die=f"({die_x},{die_y})")
    with gfh.suppress_api_error():
        r = api.add_measurement(
            device_id=device_id,
            data_file=path,
            wafer_id=wafer_id,
            die_x=die_x,
            die_y=die_y,
            plot_spec=gfh.MatplotlibPlotSpec(
                x_name="wavelength",
                y_name="output_power",
                x_col="wavelength",
                y_col=["output_power"],
            ),
        )
        results.append(r)
  0%|          | 0/576 [00:00<?, ?it/s]

Wait for data to be processed:

for result in tqdm(results):
    utils.device_data().wait_for_plot_completion(pk=result["pk"], verbose=False)
  0%|          | 0/576 [00:00<?, ?it/s]


⏳ Still running

.

.

⏳ Still running

.

.

.

⏳ Still running

.
On This Page