Create Substrate

This example shows how to create a simple substrate in your library

# Copyright Keysight Technologies 2024 - 2024
import keysight.ads.de as de
from keysight.ads import subst as substrate

# Example usage:
# wrk = de.open_workspace(path_to_workspace)
# lib = wrk.open_library("MyLibrary_lib", path_to_library, de.LibraryMode.SHARED)
# make_simple_substrate(lib, "my_substrate")


def make_simple_substrate(library: de.Library, subst_name: str) -> None:
    assert not library.is_read_only

    # Start by creating an "empty" substrate.
    subst = substrate.create_substrate(library, subst_name)
    assert substrate.substrate_exists(library, subst_name)
    assert not subst.is_read_only
    assert subst.is_writable

    # See what materials are available
    if False:
        names = substrate.get_conductor_names(library)
        assert len(names) != 0
        names = substrate.get_semiconductor_names(library)
        names = substrate.get_superconductor_names(library)
        names = substrate.get_dielectric_names(library)
        names = substrate.get_roughness_names(library)

    # If you need to specify a list of purposes to ignore, use this
    if True:
        subst.purposes_to_exclude = ["Dummy"]
        assert not subst.purposes_to_include
    else:
        subst.purposes_to_include = ["Drawing"]
        assert not subst.purposes_to_exclude

    # This substrate will have two infinite materials and three interfaces
    assert len(subst.materials) == 2
    assert len(subst.interfaces) == 3
    assert subst.materials[0].is_infinite_material
    top_material_index = subst.top_material_index
    assert subst.materials[top_material_index].is_infinite_material
    assert not subst.has_top_cover
    interface0 = subst.interfaces[0]
    assert not interface0.is_cover
    assert interface0.is_non_cover_placeholder

    # Convert the bottom interface to a cover
    if False:
        # The hard way
        interface0.purpose = substrate.InterfaceItem.Purpose.COVER
        interface0.material_name = "PERFECT_CONDUCTOR"
    else:
        interface0.convert_to_cover()
    interface0.thickness_expr = "0.0123"  # just so we can identify this interface
    assert interface0.is_cover
    assert not interface0.is_non_cover_placeholder
    assert subst.has_bottom_cover

    material0 = subst.materials[0]
    # Since the bottom interface is now a cover, material0 won't be infinite
    assert not material0.is_infinite_material
    material0.thickness_expr = "100"
    material0.thickness_unit = substrate.Unit.MICRON
    material0.material_name = "SiliconNitride"

    interface1 = subst.interfaces[1]
    if True:
        # You can specify interface by index
        layer = subst.insert_layer(1, de.ProcessRole.CONDUCTOR)
    else:
        # You can can also pass interfaces
        layer = subst.insert_layer(interface1, de.ProcessRole.CONDUCTOR)
    layer.layer_number = 2
    layer.material_name = "Au"
    layer.thickness_expr = "0.01"
    layer.thickness_unit = substrate.Unit.MIL
    # The layer item can represent a sheet that neither expands nor intrudes into the material.
    assert layer.sheet is True
    layer.sheet = False
    layer.expand = True  # Otherwise we intrude
    # Note that setting is_above to False sets the thickness negative
    layer.is_above = False  # so it expands the material below the interface
    assert layer.thickness_expr == "-0.01"
    subst.save_substrate()

    if True:
        # This will leave the bottom material and layer alone
        subst.insert_material_and_interface_above(1)
    elif False:
        # This will shove the layer up to interface 2 and the bottom material up to material 1
        subst.insert_material_and_interface_above(0)
    else:
        # This will shove the layer up to interface 2
        subst.insert_material_and_interface_below(1)
    # There is now one more material and one more interface than before
    assert len(subst.materials) == 3
    assert len(subst.interfaces) == 4
    material1 = subst.materials[1]
    material1.material_name = "Alumina"

    subst.save_substrate()

    # If we set the thickness of the top material, it won't
    # be relevant because there is no top cover so the material is infinite.
    material2 = subst.materials[2]
    material2.thickness_expr = "2000"
    material2.thickness_unit = substrate.Unit.MICRON
    assert material2.is_infinite_material
    subst.save_substrate()

    # If we add one more material and interface, material2 won't be infinite.
    subst.insert_material_and_interface_below(3)
    assert not material2.is_infinite_material
    assert material2.thickness == 2000

    if False:
        # The basic function...
        via = subst.insert_via(1, 2, de.ProcessRole.CONDUCTOR_VIA)
    elif False:
        # You can specify interface by index
        via = subst.insert_conductor_via(1, 2)
    else:
        # You can can also pass interfaces
        interface2 = subst.interfaces[2]
        via = subst.insert_conductor_via(interface1, interface2)

    via.layer_number = 2  # cond2
    via.material_name = "nicr"
    assert via.process_role == de.ProcessRole.CONDUCTOR_VIA
    via.is_plating_enabled = True
    via.plating_dielectric_material_name = "SiliconNitride"
    via.plating_thickness = 0.1
    via.plating_thickness_unit = substrate.Unit.MILLIMETER
    subst.save_substrate()

    nested_subst = subst.insert_substrate(interface2)
    nested_subst.set_library_and_substrate_names(library.name, "empty")
    assert nested_subst.library_name == library.name
    assert nested_subst.substrate_name == "empty"

    # There are three choices for alignment
    if False:
        nested_subst.align_type = substrate.SubstrateItem.AlignType.BOTTOM
    elif False:
        nested_subst.align_type = substrate.SubstrateItem.AlignType.TOP
    else:
        nested_subst.align_type = substrate.SubstrateItem.AlignType.LAYER
    # When aligning with a layer, we have to specify which part of the layer aligns
    nested_subst.alignment_position = substrate.SubstrateItem.AlignPosition.TOP_OF_LAYER
    nested_subst.align_layer_name = "cond2"
    subst.save_substrate()

    # If you don't need it any more, you can delete it
    if False:
        substrate.delete_substrate(library, subst_name)
On this page