Upload Measurement Data and run Analysis

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"spirals-{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()

Upload Project

You can create a new project and extract all cells & devices below for the cutback_rib spirals.

The expressions are regex expressions. For intro and testing your regexes you can check out regex101

project = api.create_project(
    eda_layout_file=Path("test_chip.gds").resolve(),
    extraction_rules=[
        gfh.ExtractionRule(cell_name="rib_loss", include_patterns=["cutback_rib.*"]),
        gfh.ExtractionRule(cell_name="ridge_loss", include_patterns=["cutback_ridge.*"]),
    ],
)

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 width_um length_um analysis analysis_parameters
0 cutback_rib_assembled_MFalse_W0p3_L0 20150 60150 0.3 0 [power_envelope] [{"n": 10, "wvl_of_interest_nm": 1550}]
1 cutback_rib_assembled_MTrue_W0p3_L25000 1039250 60150 0.3 25000 [power_envelope] [{"n": 10, "wvl_of_interest_nm": 1550}]
2 cutback_rib_assembled_MFalse_W0p3_L5000 20150 204150 0.3 5000 [power_envelope] [{"n": 10, "wvl_of_interest_nm": 1550}]
3 cutback_rib_assembled_MTrue_W0p3_L20000 1039250 204150 0.3 20000 [power_envelope] [{"n": 10, "wvl_of_interest_nm": 1550}]
4 cutback_rib_assembled_MFalse_W0p3_L10000 20150 348150 0.3 10000 [power_envelope] [{"n": 10, "wvl_of_interest_nm": 1550}]
5 cutback_rib_assembled_MTrue_W0p3_L15000 1039250 348150 0.3 15000 [power_envelope] [{"n": 10, "wvl_of_interest_nm": 1550}]
6 cutback_rib_assembled_MFalse_W0p5_L0 20250 492250 0.5 0 [power_envelope] [{"n": 10, "wvl_of_interest_nm": 1550}]
7 cutback_rib_assembled_MTrue_W0p5_L25000 1058750 492250 0.5 25000 [power_envelope] [{"n": 10, "wvl_of_interest_nm": 1550}]
8 cutback_rib_assembled_MFalse_W0p5_L5000 20250 646250 0.5 5000 [power_envelope] [{"n": 10, "wvl_of_interest_nm": 1550}]
9 cutback_rib_assembled_MTrue_W0p5_L20000 1058750 646250 0.5 20000 [power_envelope] [{"n": 10, "wvl_of_interest_nm": 1550}]
10 cutback_rib_assembled_MFalse_W0p5_L10000 20250 800250 0.5 10000 [power_envelope] [{"n": 10, "wvl_of_interest_nm": 1550}]
11 cutback_rib_assembled_MTrue_W0p5_L15000 1058750 800250 0.5 15000 [power_envelope] [{"n": 10, "wvl_of_interest_nm": 1550}]
12 cutback_rib_assembled_MFalse_W0p8_L0 20400 954400 0.8 0 [power_envelope] [{"n": 10, "wvl_of_interest_nm": 1550}]
13 cutback_rib_assembled_MTrue_W0p8_L25000 1088000 954400 0.8 25000 [power_envelope] [{"n": 10, "wvl_of_interest_nm": 1550}]
14 cutback_rib_assembled_MFalse_W0p8_L5000 20400 1123400 0.8 5000 [power_envelope] [{"n": 10, "wvl_of_interest_nm": 1550}]
15 cutback_rib_assembled_MTrue_W0p8_L20000 1088000 1123400 0.8 20000 [power_envelope] [{"n": 10, "wvl_of_interest_nm": 1550}]
16 cutback_rib_assembled_MFalse_W0p8_L10000 20400 1292400 0.8 10000 [power_envelope] [{"n": 10, "wvl_of_interest_nm": 1550}]
17 cutback_rib_assembled_MTrue_W0p8_L15000 1088000 1292400 0.8 15000 [power_envelope] [{"n": 10, "wvl_of_interest_nm": 1550}]
18 cutback_ridge_assembled_MFalse_W0p3_L0 20150 60150 0.3 0 [power_envelope] [{"n": 10, "wvl_of_interest_nm": 1550}]
19 cutback_ridge_assembled_MTrue_W0p3_L25000 1037250 60150 0.3 25000 [power_envelope] [{"n": 10, "wvl_of_interest_nm": 1550}]
20 cutback_ridge_assembled_MFalse_W0p3_L5000 20150 203150 0.3 5000 [power_envelope] [{"n": 10, "wvl_of_interest_nm": 1550}]
21 cutback_ridge_assembled_MTrue_W0p3_L20000 1037250 203150 0.3 20000 [power_envelope] [{"n": 10, "wvl_of_interest_nm": 1550}]
22 cutback_ridge_assembled_MFalse_W0p3_L10000 20150 346150 0.3 10000 [power_envelope] [{"n": 10, "wvl_of_interest_nm": 1550}]
23 cutback_ridge_assembled_MTrue_W0p3_L15000 1037250 346150 0.3 15000 [power_envelope] [{"n": 10, "wvl_of_interest_nm": 1550}]
24 cutback_ridge_assembled_MFalse_W0p5_L0 20250 489250 0.5 0 [power_envelope] [{"n": 10, "wvl_of_interest_nm": 1550}]
25 cutback_ridge_assembled_MTrue_W0p5_L25000 1056750 489250 0.5 25000 [power_envelope] [{"n": 10, "wvl_of_interest_nm": 1550}]
26 cutback_ridge_assembled_MFalse_W0p5_L5000 20250 642250 0.5 5000 [power_envelope] [{"n": 10, "wvl_of_interest_nm": 1550}]
27 cutback_ridge_assembled_MTrue_W0p5_L20000 1056750 642250 0.5 20000 [power_envelope] [{"n": 10, "wvl_of_interest_nm": 1550}]
28 cutback_ridge_assembled_MFalse_W0p5_L10000 20250 795250 0.5 10000 [power_envelope] [{"n": 10, "wvl_of_interest_nm": 1550}]
29 cutback_ridge_assembled_MTrue_W0p5_L15000 1056750 795250 0.5 15000 [power_envelope] [{"n": 10, "wvl_of_interest_nm": 1550}]
30 cutback_ridge_assembled_MFalse_W0p8_L0 20400 948400 0.8 0 [power_envelope] [{"n": 10, "wvl_of_interest_nm": 1550}]
31 cutback_ridge_assembled_MTrue_W0p8_L25000 1086000 948400 0.8 25000 [power_envelope] [{"n": 10, "wvl_of_interest_nm": 1550}]
32 cutback_ridge_assembled_MFalse_W0p8_L5000 20400 1116400 0.8 5000 [power_envelope] [{"n": 10, "wvl_of_interest_nm": 1550}]
33 cutback_ridge_assembled_MTrue_W0p8_L20000 1086000 1116400 0.8 20000 [power_envelope] [{"n": 10, "wvl_of_interest_nm": 1550}]
34 cutback_ridge_assembled_MFalse_W0p8_L10000 20400 1284400 0.8 10000 [power_envelope] [{"n": 10, "wvl_of_interest_nm": 1550}]
35 cutback_ridge_assembled_MTrue_W0p8_L15000 1086000 1284400 0.8 15000 [power_envelope] [{"n": 10, "wvl_of_interest_nm": 1550}]
dm = dm.drop(columns=["analysis", "analysis_parameters"])
dm
cell x y width_um length_um
0 cutback_rib_assembled_MFalse_W0p3_L0 20150 60150 0.3 0
1 cutback_rib_assembled_MTrue_W0p3_L25000 1039250 60150 0.3 25000
2 cutback_rib_assembled_MFalse_W0p3_L5000 20150 204150 0.3 5000
3 cutback_rib_assembled_MTrue_W0p3_L20000 1039250 204150 0.3 20000
4 cutback_rib_assembled_MFalse_W0p3_L10000 20150 348150 0.3 10000
5 cutback_rib_assembled_MTrue_W0p3_L15000 1039250 348150 0.3 15000
6 cutback_rib_assembled_MFalse_W0p5_L0 20250 492250 0.5 0
7 cutback_rib_assembled_MTrue_W0p5_L25000 1058750 492250 0.5 25000
8 cutback_rib_assembled_MFalse_W0p5_L5000 20250 646250 0.5 5000
9 cutback_rib_assembled_MTrue_W0p5_L20000 1058750 646250 0.5 20000
10 cutback_rib_assembled_MFalse_W0p5_L10000 20250 800250 0.5 10000
11 cutback_rib_assembled_MTrue_W0p5_L15000 1058750 800250 0.5 15000
12 cutback_rib_assembled_MFalse_W0p8_L0 20400 954400 0.8 0
13 cutback_rib_assembled_MTrue_W0p8_L25000 1088000 954400 0.8 25000
14 cutback_rib_assembled_MFalse_W0p8_L5000 20400 1123400 0.8 5000
15 cutback_rib_assembled_MTrue_W0p8_L20000 1088000 1123400 0.8 20000
16 cutback_rib_assembled_MFalse_W0p8_L10000 20400 1292400 0.8 10000
17 cutback_rib_assembled_MTrue_W0p8_L15000 1088000 1292400 0.8 15000
18 cutback_ridge_assembled_MFalse_W0p3_L0 20150 60150 0.3 0
19 cutback_ridge_assembled_MTrue_W0p3_L25000 1037250 60150 0.3 25000
20 cutback_ridge_assembled_MFalse_W0p3_L5000 20150 203150 0.3 5000
21 cutback_ridge_assembled_MTrue_W0p3_L20000 1037250 203150 0.3 20000
22 cutback_ridge_assembled_MFalse_W0p3_L10000 20150 346150 0.3 10000
23 cutback_ridge_assembled_MTrue_W0p3_L15000 1037250 346150 0.3 15000
24 cutback_ridge_assembled_MFalse_W0p5_L0 20250 489250 0.5 0
25 cutback_ridge_assembled_MTrue_W0p5_L25000 1056750 489250 0.5 25000
26 cutback_ridge_assembled_MFalse_W0p5_L5000 20250 642250 0.5 5000
27 cutback_ridge_assembled_MTrue_W0p5_L20000 1056750 642250 0.5 20000
28 cutback_ridge_assembled_MFalse_W0p5_L10000 20250 795250 0.5 10000
29 cutback_ridge_assembled_MTrue_W0p5_L15000 1056750 795250 0.5 15000
30 cutback_ridge_assembled_MFalse_W0p8_L0 20400 948400 0.8 0
31 cutback_ridge_assembled_MTrue_W0p8_L25000 1086000 948400 0.8 25000
32 cutback_ridge_assembled_MFalse_W0p8_L5000 20400 1116400 0.8 5000
33 cutback_ridge_assembled_MTrue_W0p8_L20000 1086000 1116400 0.8 20000
34 cutback_ridge_assembled_MFalse_W0p8_L10000 20400 1284400 0.8 10000
35 cutback_ridge_assembled_MTrue_W0p8_L15000 1086000 1284400 0.8 15000
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");
pd.read_csv("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

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)
378

Upload measurements

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.

results = []
for path in (pb := tqdm(data_files)):
    wafer_id = path.parts[-4]
    die_x, die_y = path.parts[-3].split("_")
    device_id = path.parts[-2]
    pb.set_postfix(wafer_id=wafer_id, die=f"({die_x},{die_y})", device_id=device_id)
    result = 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(result)
  0%|          | 0/378 [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/378 [00:00<?, ?it/s]


⏳ Still running

.
On This Page