Download data#

You can compose queries to download raw data.

import doplaydo.dodata as dd
import matplotlib.pyplot as plt
import pandas as pd
import getpass

username = getpass.getuser()

PROJECT_ID = f"resistance-{username}"

You have access to:

  • dd.Project

  • dd.Die

  • dd.Wafer

  • dd.ParentCell

  • dd.Cell

  • dd.Device

  • dd.DeviceData

Where each model has its table columns as attributes.

You can use get_data_by_query to query a subset of data filtered by a list of clauses.

It will return a list of tuples where the first element is a DeviceData object and the second one is a pandas DataFrame.

data_tuples = dd.get_data_by_query([dd.Project.project_id == PROJECT_ID], limit=1)
device_data, df = data_tuples[0]  # each tuple has DeviceData and pd.Dataframe
device_data
DeviceData(data_type=<DeviceDataType.measurement: 'measurement'>, path='device_data/6766/data.json.gz', attributes={}, die_pkey=1427, timestamp=datetime.datetime(2025, 1, 4, 20, 5, 45, 687793), timestamp_acquired=None, pkey=6766, thumbnail_path='device_data/6766/thumbnail.png', plotting_kwargs={'x_col': 'i', 'y_col': ['v'], 'x_name': 'i', 'y_name': 'v', 'scatter': False, 'sort_by': {}, 'x_units': None, 'y_units': None, 'grouping': {}, 'x_limits': None, 'y_limits': None, 'x_log_axis': False, 'y_log_axis': False}, valid=True, device_pkey=1018)
device_id = device_data.device.device_id
df
v i polyfit
0 0.000000e+00 0.000000 1.617142e-07
1 2.151748e-07 1.282051 1.617142e-07
2 4.221525e-07 2.564103 1.617142e-07
3 6.445482e-07 3.846154 1.617142e-07
4 8.641881e-07 5.128205 1.617142e-07
... ... ... ...
74 1.594226e-05 94.871795 1.617142e-07
75 1.571873e-05 96.153846 1.617142e-07
76 1.622237e-05 97.435897 1.617142e-07
77 1.617140e-05 98.717949 1.617142e-07
78 1.637945e-05 100.000000 1.617142e-07

79 rows × 3 columns

plt.plot(df["i"], df["v"])
plt.xlabel("I (A)")
plt.ylabel("V (V)")
plt.title(device_id)
Text(0.5, 1.0, 'resistance_resistance_sheet_W100_0_262500')
../../../_images/da9ab7a436d838b0416365dd617f56ba6677e5fcdfa6d85af8a6923acdb10c61.png

You can aggregate the dataframes and the device data objects

dfs = [dt[1] for dt in data_tuples]  # dataframes
dds = [dt[0] for dt in data_tuples]  # device data objects

You can use the DeviceData object to traverse the data model and access additional fields.

You can go from DeviceData to any other tables by following the dashed arrows.

Each column is an attribute on the object representing the table.

print("device id: ", dds[0].device.device_id)
print("die x: ", dds[0].die.x)
print("die y: ", dds[0].die.y)
print("wafer id: ", dds[0].die.wafer.wafer_id)
print("cell id: ", dds[0].device.cell.cell_id)
print("parent cell id: ", dds[0].device.parent_cell.cell_id)
print("project id: ", dds[0].device.cell.project.project_id)
device id:  resistance_resistance_sheet_W100_0_262500
die x:  -2
die y:  -1
wafer id:  2eq221eqewq2
cell id:  resistance_sheet_W100
parent cell id:  resistance
project id:  resistance-runner

For example, you can reach the Cell table and all its columns:

dds[0].device.cell
Cell(attributes={'x': 0, 'y': 262500, 'width_um': 100.0, 'length_um': 20, 'kfactory:info': "{'resistance': 0}", 'kfactory:ports:0': "{'cross_section': 'da0d77df_50000', 'info': {}, 'name': 'pad1', 'port_type': 'electrical', 'trans': r270 -50000,-25000}", 'kfactory:ports:1': "{'cross_section': 'da0d77df_50000', 'info': {}, 'name': 'pad2', 'port_type': 'electrical', 'trans': r270 50000,-25000}", 'kfactory:settings': "{'width': 100}", 'kfactory:function_name': 'resistance_sheet'}, pkey=1017, cell_id='resistance_sheet_W100', project_pkey=46, timestamp=datetime.datetime(2025, 1, 4, 20, 5, 40, 32423))

Build table for JMP#

We recommend doing all analysis in python but we also support exporting the data to a flat table for JMP or Excel.

data_tuples = dd.get_data_by_query(
    [
        dd.Project.project_id == PROJECT_ID,
        dd.Device.device_id == device_id,
        dd.Die.x == 0,
        dd.Die.y == 0,
    ]
)
len(data_tuples)
dds = [dt[0] for dt in data_tuples]  # device data objects
dfs = [dt[1] for dt in data_tuples]  # dataframes
dfs[0]
v i polyfit
0 0.000000e+00 0.000000 2.469230e-07
1 3.211193e-07 1.282051 2.469230e-07
2 6.486081e-07 2.564103 2.469230e-07
3 9.971517e-07 3.846154 2.469230e-07
4 1.285870e-06 5.128205 2.469230e-07
... ... ... ...
74 2.395236e-05 94.871795 2.469230e-07
75 2.489447e-05 96.153846 2.469230e-07
76 2.493553e-05 97.435897 2.469230e-07
77 2.542793e-05 98.717949 2.469230e-07
78 2.520388e-05 100.000000 2.469230e-07

79 rows × 3 columns

dfs_all = []

for device_data, df in zip(dds, dfs):
    df["device_id"] = device_data.device.device_id
    df["die_x"] = device_data.die.x
    df["die_y"] = device_data.die.y
    df["wafer_id"] = device_data.die.wafer.wafer_id
    df["cell_id"] = device_data.device.cell.cell_id
    df["parent_cell_id"] = device_data.device.parent_cell.cell_id
    dfs_all.append(df)
dfs_all = pd.concat(dfs_all)  # You can concatenate all dataFrames together
dfs_all
v i polyfit device_id die_x die_y wafer_id cell_id parent_cell_id
0 0.000000e+00 0.000000 2.469230e-07 resistance_resistance_sheet_W100_0_262500 0 0 6d4c615ff105 resistance_sheet_W100 resistance
1 3.211193e-07 1.282051 2.469230e-07 resistance_resistance_sheet_W100_0_262500 0 0 6d4c615ff105 resistance_sheet_W100 resistance
2 6.486081e-07 2.564103 2.469230e-07 resistance_resistance_sheet_W100_0_262500 0 0 6d4c615ff105 resistance_sheet_W100 resistance
3 9.971517e-07 3.846154 2.469230e-07 resistance_resistance_sheet_W100_0_262500 0 0 6d4c615ff105 resistance_sheet_W100 resistance
4 1.285870e-06 5.128205 2.469230e-07 resistance_resistance_sheet_W100_0_262500 0 0 6d4c615ff105 resistance_sheet_W100 resistance
... ... ... ... ... ... ... ... ... ...
74 1.510285e-05 94.871795 1.561807e-07 resistance_resistance_sheet_W100_0_262500 0 0 2eq221eqewq2 resistance_sheet_W100 resistance
75 1.563770e-05 96.153846 1.561807e-07 resistance_resistance_sheet_W100_0_262500 0 0 2eq221eqewq2 resistance_sheet_W100 resistance
76 1.538951e-05 97.435897 1.561807e-07 resistance_resistance_sheet_W100_0_262500 0 0 2eq221eqewq2 resistance_sheet_W100 resistance
77 1.566045e-05 98.717949 1.561807e-07 resistance_resistance_sheet_W100_0_262500 0 0 2eq221eqewq2 resistance_sheet_W100 resistance
78 1.588276e-05 100.000000 1.561807e-07 resistance_resistance_sheet_W100_0_262500 0 0 2eq221eqewq2 resistance_sheet_W100 resistance

237 rows × 9 columns

Advanced queries#

To build advanced queries to filter metadata you can use the attribute_filter method. You can also use or_, and_ for conditional clauses.

Conditional filter#

data_tuples = dd.get_data_by_query(
    [dd.Project.project_id == PROJECT_ID, dd.Device.device_id == device_id]
)
len(data_tuples)
63

By default anything you pass to the list joins the clauses as and_

data_tuples = dd.get_data_by_query(
    [dd.and_(dd.Project.project_id == PROJECT_ID, dd.Device.device_id == device_id)]
)
len(data_tuples)
63

You can also use an OR condition.

The query below filters by project id PROJECT_ID AND either device id (rib_0p3 OR rib_0p5).

Because there are 21 measurements per device, one for each die, if you look for two specific devices, you will get 42 measurements.

device_id1 = "resistance_resistance_sheet_W10_0_53000"
device_id2 = "resistance_resistance_sheet_W20_0_158000"

data_tuples = dd.get_data_by_query(
    [
        dd.Project.project_id == PROJECT_ID,
        dd.or_(dd.Device.device_id == device_id1, dd.Device.device_id == device_id2),
    ]
)
len(data_tuples)
0

You can also combine conditionals.

In the example below, you can get all the data for the specified project id, device id and either die coordinate.

data_tuples = dd.get_data_by_query(
    [
        dd.Project.project_id == PROJECT_ID,
        dd.Device.device_id == device_id,
        dd.or_(
            dd.and_(dd.Die.x == 1, dd.Die.y == 1),  # either die 1,1
            dd.and_(dd.Die.x == 0, dd.Die.y == 0),  # or die 0,0
        ),
    ]
)
len(data_tuples)
6

Attribute filter#

You can filter attributes of any data models by passing a list of conditions as shown below.

You can use dd.Cell/dd.Wafer/..., key, value

You can only filter for values that are str, bool, int, float.

The query below filters Cells with waveguide width 0.3um that are in a particular project and die coordinates.

device_data_objects = dd.get_data_by_query(
    [
        dd.attribute_filter(
            dd.Cell, "width_um", 100
        ),  # checking for values in JSON attributes
        dd.Project.project_id == PROJECT_ID,
        dd.Die.x == 0,
        dd.Die.y == 0,
    ]
)
len(device_data_objects)
3

You can combine conditional and attribute filter clauses together.

You can get the data for Cells for a given project with length 100um and either width 20um or 100um

data_tuples = dd.get_data_by_query(
    [
        dd.Project.project_id == PROJECT_ID,
        dd.Die.x == 0,
        dd.Die.y == 0,
        dd.attribute_filter(
            dd.Cell, "length_um", 20
        ),  # checking for values in JSON attributes
        dd.or_(
            dd.attribute_filter(dd.Cell, "width_um", 20),
            dd.attribute_filter(dd.Cell, "width_um", 100),
        ),
    ]
)
len(data_tuples)
6