Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

better via_stack_with_offset #1480

Merged
merged 9 commits into from Mar 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 4 additions & 7 deletions gdsfactory/components/ring_section_based.py
Expand Up @@ -13,12 +13,13 @@
from gdsfactory.typings import CrossSectionSpec, Union, List, Tuple, Floats
from gdsfactory.components.bend_circular import bend_circular
from gdsfactory.components.straight import straight
from gdsfactory.cell import cell_without_validator

def_dict = {"A": "rib", "B": "strip"}
def_ang_dict = {"A": 6, "B": 6}


@gf.cell
@cell_without_validator
def ring_section_based(
gap: Union[float, Floats] = 0.3,
radius: float = 5.0,
Expand Down Expand Up @@ -67,7 +68,7 @@
angular_extent_sequence = 360

if not isinstance(gap, Union[List, Tuple]):
gap = [gap] * 2

Check warning on line 71 in gdsfactory/components/ring_section_based.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/ring_section_based.py#L71

Added line #L71 was not covered by tests

# See if we need to add initial cross sections
if start_cross_section is not None:
Expand All @@ -78,22 +79,22 @@
angular_extent_sequence -= start_angle

if not isinstance(cross_sections_sequence, Union[List, Tuple]):
cross_sections_sequence = [cross_sections_sequence] * 2

Check warning on line 82 in gdsfactory/components/ring_section_based.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/ring_section_based.py#L82

Added line #L82 was not covered by tests

if cross_sections_angles is None:
if add_drop:
n_sections_0 = len(cross_sections_sequence[0])
sing_sec_ang_1 = angular_extent_sequence / (2 * n_sections_0)
n_sections_1 = len(cross_sections_sequence[1])
sing_sec_ang_2 = angular_extent_sequence / (2 * n_sections_1)

Check warning on line 89 in gdsfactory/components/ring_section_based.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/ring_section_based.py#L86-L89

Added lines #L86 - L89 were not covered by tests
cross_sections_angles = {
elem: sing_sec_ang_1 for elem in cross_sections_sequence[0]
}
for elem in cross_sections_sequence[1]:
cross_sections_angles[elem] = sing_sec_ang_2

Check warning on line 94 in gdsfactory/components/ring_section_based.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/ring_section_based.py#L94

Added line #L94 was not covered by tests

else:
n_sections = len(cross_sections_sequence)

Check warning on line 97 in gdsfactory/components/ring_section_based.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/ring_section_based.py#L97

Added line #L97 was not covered by tests
sing_sec_ang = angular_extent_sequence / n_sections
cross_sections_angles = {
elem: sing_sec_ang for elem in cross_sections_sequence
Expand All @@ -106,12 +107,12 @@
[cross_sections_angles[sec] for sec in cross_sections_sequence]
)
if not (angular_extent_sequence / sing_seq_angular_extent).is_integer():
raise ValueError(

Check warning on line 110 in gdsfactory/components/ring_section_based.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/ring_section_based.py#L110

Added line #L110 was not covered by tests
"The specified sequence angles do not result in an integer number "
"of sequences fitting in the ring."
)
else:
n_repeats_seq = int(angular_extent_sequence / sing_seq_angular_extent)

Check warning on line 115 in gdsfactory/components/ring_section_based.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/ring_section_based.py#L115

Added line #L115 was not covered by tests

else:
sing_seq_angular_extent_0 = np.sum(
Expand All @@ -122,7 +123,7 @@
)

if (angular_extent_sequence / (sing_seq_angular_extent_0 * 2)).is_integer():
n_repeats_seq_0 = int(

Check warning on line 126 in gdsfactory/components/ring_section_based.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/ring_section_based.py#L126

Added line #L126 was not covered by tests
angular_extent_sequence / (sing_seq_angular_extent_0 * 2)
)
else:
Expand All @@ -132,26 +133,22 @@
)

if (angular_extent_sequence / (sing_seq_angular_extent_1 * 2)).is_integer():
n_repeats_seq_1 = int(

Check warning on line 136 in gdsfactory/components/ring_section_based.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/ring_section_based.py#L136

Added line #L136 was not covered by tests
angular_extent_sequence / (sing_seq_angular_extent_1 * 2)
)
else:
raise ValueError(

Check warning on line 140 in gdsfactory/components/ring_section_based.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/ring_section_based.py#L140

Added line #L140 was not covered by tests
"The specified sequence 2 angles do not result in an integer "
"number of sequences fitting in the ring."
)

print(n_repeats_seq_0)

# Now we are ready to construct the ring

# We need to create a circular bend for each section
sections_dict = {}

for key in cross_sections.keys():
for key, xsec in cross_sections.items():
ang = cross_sections_angles[key]
xsec = cross_sections[key]

b = bend_circular(angle=ang, with_bbox=False, cross_section=xsec, radius=radius)

sections_dict[key] = (b, "o1", "o2")
Expand All @@ -167,17 +164,17 @@
sections_dict["0"] = (b, "o1", "o2")

if drop_cross_section is not None:
b = bend_circular(

Check warning on line 167 in gdsfactory/components/ring_section_based.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/ring_section_based.py#L167

Added line #L167 was not covered by tests
angle=start_angle,
with_bbox=False,
cross_section=gf.get_cross_section(drop_cross_section),
radius=radius,
)
if "1" in sections_dict:
raise ValueError(

Check warning on line 174 in gdsfactory/components/ring_section_based.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/ring_section_based.py#L174

Added line #L174 was not covered by tests
"Please do not have '1' as a key for the cross_sections dict"
)
sections_dict["1"] = (b, "o1", "o2")

Check warning on line 177 in gdsfactory/components/ring_section_based.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/ring_section_based.py#L177

Added line #L177 was not covered by tests

# Now we just need to generate a chain of characters
# to creae the sequence
Expand All @@ -188,18 +185,18 @@
sequence += "0"

if not add_drop:
sequence += cross_sections_sequence * n_repeats_seq

Check warning on line 188 in gdsfactory/components/ring_section_based.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/ring_section_based.py#L188

Added line #L188 was not covered by tests

else:
sequence += cross_sections_sequence[0] * n_repeats_seq_0

Check warning on line 191 in gdsfactory/components/ring_section_based.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/ring_section_based.py#L191

Added line #L191 was not covered by tests

if drop_cross_section is None and start_cross_section is not None:
sequence += "0"

Check warning on line 194 in gdsfactory/components/ring_section_based.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/ring_section_based.py#L194

Added line #L194 was not covered by tests
elif drop_cross_section is not None:
sequence += "1"

Check warning on line 196 in gdsfactory/components/ring_section_based.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/ring_section_based.py#L196

Added line #L196 was not covered by tests

seq_2 = cross_sections_sequence[1] * n_repeats_seq_1
sequence += seq_2

Check warning on line 199 in gdsfactory/components/ring_section_based.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/ring_section_based.py#L198-L199

Added lines #L198 - L199 were not covered by tests

ring = gf.components.component_sequence(
sequence=sequence, symbol_to_component=sections_dict
Expand Down Expand Up @@ -234,19 +231,19 @@
if start_cross_section
else gf.get_cross_section(cross_sections[cross_sections_sequence[0]]).layer
)
ring_guide_add = ring.extract([input_xs_layer])

Check warning on line 234 in gdsfactory/components/ring_section_based.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/ring_section_based.py#L234

Added line #L234 was not covered by tests

if drop_cross_section:
drop_xs_layer = gf.get_cross_section(drop_cross_section).layer

Check warning on line 237 in gdsfactory/components/ring_section_based.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/ring_section_based.py#L237

Added line #L237 was not covered by tests
elif start_cross_section:
drop_xs_layer = input_xs_layer

Check warning on line 239 in gdsfactory/components/ring_section_based.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/ring_section_based.py#L239

Added line #L239 was not covered by tests
else:
drop_xs_layer = gf.get_cross_section(

Check warning on line 241 in gdsfactory/components/ring_section_based.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/ring_section_based.py#L241

Added line #L241 was not covered by tests
cross_sections[cross_sections_sequence[0]]
).layer

ring_guide_add = ring.extract([input_xs_layer])
ring_guide_drop = ring.extract([drop_xs_layer])

Check warning on line 246 in gdsfactory/components/ring_section_based.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/ring_section_based.py#L245-L246

Added lines #L245 - L246 were not covered by tests

# Add bus waveguides
s = straight(length=ring.xsize, cross_section=bus_cross_section)
Expand All @@ -256,13 +253,13 @@

s_add = c << s
s_add.x = r.x
s_add.ymax = ring_guide_add.ymin - gap[0] + s.ysize / 2 - input_xs_width / 2

Check warning on line 256 in gdsfactory/components/ring_section_based.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/ring_section_based.py#L256

Added line #L256 was not covered by tests

if add_drop:
s.mirror((0, 1))

Check warning on line 259 in gdsfactory/components/ring_section_based.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/ring_section_based.py#L259

Added line #L259 was not covered by tests
s_drop = c << s
s_drop.x = r.x
s_drop.ymin = ring_guide_drop.ymax + gap[1] - s.ysize / 2 + input_xs_width / 2

Check warning on line 262 in gdsfactory/components/ring_section_based.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/ring_section_based.py#L262

Added line #L262 was not covered by tests

# Add ports
c.add_port("o1", port=s_add.ports["o1"])
Expand Down Expand Up @@ -306,4 +303,4 @@
# c = ring_section_based(add_drop=True)

c.show(show_ports=True)
print(c.info["ring_center"])
# print(c.info["ring_center"])
33 changes: 12 additions & 21 deletions gdsfactory/components/via_stack.py
Expand Up @@ -87,9 +87,9 @@
and min_height > height
and correct_size
):
print("Changing sizes to fit a via! Check this is desired")
width = max(min_width, width)
height = max(min_height, height)

Check warning on line 92 in gdsfactory/components/via_stack.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/via_stack.py#L90-L92

Added lines #L90 - L92 were not covered by tests
elif min_width > width or min_height > height:
raise ValueError(f"size {size} is too small to fit a {(w, h)} um via")

Expand All @@ -114,7 +114,7 @@


@gf.cell
def circular_via_stack(
def via_stack_circular(
radius: float = 10.0,
angular_extent: float = 45,
center_angle: float = 0,
Expand All @@ -125,6 +125,8 @@
) -> Component:
"""Circular via array stack.

FIXME! does not work.

Constructs a circular via array stack. It does so
by stacking rectangular via stacks offset by a small amount
along the specified circumference.
Expand All @@ -141,26 +143,24 @@

# We basically just want to place rectangular via stacks
# stacked with a little bit of an offset
c = gf.Component()

Check warning on line 146 in gdsfactory/components/via_stack.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/via_stack.py#L146

Added line #L146 was not covered by tests

layers = layers or []

Check warning on line 148 in gdsfactory/components/via_stack.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/via_stack.py#L148

Added line #L148 was not covered by tests

if layers:
layer_port = layer_port or layers[-1]

Check warning on line 151 in gdsfactory/components/via_stack.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/via_stack.py#L151

Added line #L151 was not covered by tests

init_angle = (center_angle - angular_extent / 2) * np.pi / 180
end_angle = (center_angle + angular_extent / 2) * np.pi / 180

Check warning on line 154 in gdsfactory/components/via_stack.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/via_stack.py#L153-L154

Added lines #L153 - L154 were not covered by tests

init_angle = init_angle % (2 * np.pi)

Check warning on line 156 in gdsfactory/components/via_stack.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/via_stack.py#L156

Added line #L156 was not covered by tests
if init_angle > np.pi:
init_angle = init_angle - 2 * np.pi

Check warning on line 158 in gdsfactory/components/via_stack.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/via_stack.py#L158

Added line #L158 was not covered by tests

end_angle = end_angle % (2 * np.pi)

Check warning on line 160 in gdsfactory/components/via_stack.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/via_stack.py#L160

Added line #L160 was not covered by tests
if end_angle > np.pi:
end_angle = end_angle - 2 * np.pi

Check warning on line 162 in gdsfactory/components/via_stack.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/via_stack.py#L162

Added line #L162 was not covered by tests

print(end_angle * 180 / np.pi)

# We do this via-centric: we figure out the min spacing between vias,
# and from that figure out all the metal dimensions
# This will of course fail if no via information is provided,
Expand All @@ -168,78 +168,68 @@

for level, via_type in enumerate(vias):
if via_type is None:
continue

Check warning on line 171 in gdsfactory/components/via_stack.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/via_stack.py#L171

Added line #L171 was not covered by tests

metal_bottom = layers[level]
metal_top = layers[level + 1]

via_type = gf.get_component(via_type)

Check warning on line 175 in gdsfactory/components/via_stack.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/via_stack.py#L173-L175

Added lines #L173 - L175 were not covered by tests

# Get via info
w, h = via_type.info["size"]
g = via_type.info["enclosure"]
pitch_x, pitch_y = via_type.info["spacing"]

Check warning on line 179 in gdsfactory/components/via_stack.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/via_stack.py#L177-L179

Added lines #L177 - L179 were not covered by tests

nb_vias_x = (width - w - 2 * g) / pitch_x + 1
nb_vias_x = int(np.floor(nb_vias_x)) or 1

Check warning on line 182 in gdsfactory/components/via_stack.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/via_stack.py#L181-L182

Added lines #L181 - L182 were not covered by tests

size_metal = (width, h + 2 * g)

Check warning on line 184 in gdsfactory/components/via_stack.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/via_stack.py#L184

Added line #L184 was not covered by tests

# Now start placing via lines at each angle starting from
# the initial angle until we reach the end angle
ang = init_angle

Check warning on line 188 in gdsfactory/components/via_stack.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/via_stack.py#L188

Added line #L188 was not covered by tests

while _smaller_angle(ang, ang, end_angle):
pos = radius * np.array((np.cos(ang), np.sin(ang)))

Check warning on line 191 in gdsfactory/components/via_stack.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/via_stack.py#L191

Added line #L191 was not covered by tests

ref = c.add_array(

Check warning on line 193 in gdsfactory/components/via_stack.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/via_stack.py#L193

Added line #L193 was not covered by tests
via_type, columns=nb_vias_x, rows=1, spacing=(pitch_x, pitch_y)
)
ref.center = pos

Check warning on line 196 in gdsfactory/components/via_stack.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/via_stack.py#L196

Added line #L196 was not covered by tests

# Place top and bottom metal
for metal in [metal_top, metal_bottom]:
met = c << gf.components.rectangle(size=size_metal, layer=metal)
met.center = pos

Check warning on line 201 in gdsfactory/components/via_stack.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/via_stack.py#L200-L201

Added lines #L200 - L201 were not covered by tests

# Let's see if we can do something different
x, y = pos

Check warning on line 204 in gdsfactory/components/via_stack.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/via_stack.py#L204

Added line #L204 was not covered by tests
print(f"x={x}, y={y}")

if x > 0:
new_y = y + pitch_y
mult = 1

Check warning on line 208 in gdsfactory/components/via_stack.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/via_stack.py#L207-L208

Added lines #L207 - L208 were not covered by tests
else:
new_y = y - pitch_y
mult = -1

Check warning on line 211 in gdsfactory/components/via_stack.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/via_stack.py#L210-L211

Added lines #L210 - L211 were not covered by tests

if new_y > radius:
new_y = y - pitch_y
assert new_y < radius
new_x = -1 * np.sqrt(np.power(radius, 2) - np.power(new_y, 2))

Check warning on line 216 in gdsfactory/components/via_stack.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/via_stack.py#L214-L216

Added lines #L214 - L216 were not covered by tests
elif new_y < -radius:
new_y = y + pitch_y
assert new_y > -radius
new_x = np.sqrt(np.power(radius, 2) - np.power(new_y, 2))

Check warning on line 220 in gdsfactory/components/via_stack.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/via_stack.py#L218-L220

Added lines #L218 - L220 were not covered by tests

else:
new_x = mult * np.sqrt(np.power(radius, 2) - np.power(new_y, 2))

Check warning on line 223 in gdsfactory/components/via_stack.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/via_stack.py#L223

Added line #L223 was not covered by tests

if np.isnan(new_x):
print(radius)
print(new_y)
print(np.power(radius, 2) - np.power(new_y, 2))
assert not np.isnan(new_x)

print(f"new x={new_x}, new y={new_y}")
ang = np.arctan2(new_y, new_x)

Check warning on line 230 in gdsfactory/components/via_stack.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/via_stack.py#L226-L230

Added lines #L226 - L230 were not covered by tests
print(f"new angle = {ang * 180 / np.pi}")

# input()

print("LAYER DONE")

return c

Check warning on line 232 in gdsfactory/components/via_stack.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/via_stack.py#L232

Added line #L232 was not covered by tests


def _smaller_angle(angle, angle1, angle2):
Expand All @@ -253,18 +243,18 @@

if angle2 >= 0 and angle1 >= 0:
if angle2 > angle1:
return angle < angle2

Check warning on line 246 in gdsfactory/components/via_stack.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/via_stack.py#L246

Added line #L246 was not covered by tests
# Convert angle to 0, 2pi and see if out of bounds
angle = angle + 2 * np.pi * (angle < 0)
return not (angle2 < angle < angle1)

Check warning on line 249 in gdsfactory/components/via_stack.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/via_stack.py#L248-L249

Added lines #L248 - L249 were not covered by tests

elif angle2 < 0 and angle1 < 0:
return angle < angle2 if angle2 > angle1 else not (angle2 < angle < angle1)

Check warning on line 252 in gdsfactory/components/via_stack.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/via_stack.py#L252

Added line #L252 was not covered by tests
else:
if angle2 < 0 and angle > 0 or angle2 >= 0 and angle < 0:
return True

Check warning on line 255 in gdsfactory/components/via_stack.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/via_stack.py#L255

Added line #L255 was not covered by tests
else:
return angle < angle2

Check warning on line 257 in gdsfactory/components/via_stack.py

View check run for this annotation

Codecov / codecov/patch

gdsfactory/components/via_stack.py#L257

Added line #L257 was not covered by tests


@gf.cell
Expand All @@ -280,7 +270,8 @@
) -> Component:
"""Rectangular via array stack, with optimized dimension for vias.

Uses inclusion, minimum width, and minimum spacing rules to place the maximum number of individual vias, each with maximum via area.
Uses inclusion, minimum width, and minimum spacing rules to place the maximum number of individual vias,
each with maximum via area.

Args:
size: of the layers, len(size).
Expand Down Expand Up @@ -444,15 +435,17 @@


if __name__ == "__main__":
c = via_stack_m1_m3(size=(1.0, 1.0))
c = via_stack_heater_mtop(layer_offsets=(0, 1, 2))

# c = via_stack_circular()
# c = via_stack_m1_m3(size=(1.0, 1.0))
# print(c.to_dict())
c.show(show_ports=True)
# c.show(show_ports=True)

# c = via_stack_from_rules()
# c = via_stack_heater_mtop()
# c.show(show_ports=True)

# c = circular_via_stack(
# c = via_stack_circular(
# radius=20.0,
# angular_extent=300,
# center_angle=0,
Expand All @@ -461,6 +454,4 @@
# vias=(via1, via2),
# layer_port=None,
# )
# c.show()

# test_via_stack_from_rules()
c.show(show_ports=True)
67 changes: 33 additions & 34 deletions gdsfactory/components/via_stack_with_offset.py
Expand Up @@ -7,38 +7,40 @@
import gdsfactory as gf
from gdsfactory.component import Component
from gdsfactory.components.via import viac
from gdsfactory.typings import ComponentSpec, LayerSpecs
from gdsfactory.components.compass import compass
from gdsfactory.typings import ComponentSpec, LayerSpecs, Float2


@gf.cell
def via_stack_with_offset(
layers: LayerSpecs = ("PPP", "M1"),
sizes: Tuple[Tuple[float, float], ...] = ((10, 10), (10, 10)),
size: Float2 = (10, 10),
sizes: Optional[Tuple[Float2, ...]] = None,
vias: Tuple[Optional[ComponentSpec], ...] = (None, viac),
offsets: Optional[Tuple[float, ...]] = None,
port_orientation: float = 180,
) -> Component:
"""Rectangular layer transition with offset between layers.

Args:
layers: for each via.
vias: factory for via or None for no via.
sizes: for each via.
offsets: for next layer.
port_orientation: 180: W0, 0: E0, 90: N0, 270: S0.
size: for all vias.
sizes: Optional size for each via. Overrides size.
vias: factory for each via. None for no via.
offsets: center offset for each layer relatively to the previous one.
"""
c = Component()
y0 = y1 = 0
y0 = 0

offsets = offsets or [0] * len(layers)
sizes = sizes or [size] * len(layers)

for layer, via, size, offset in zip(layers, vias, sizes, offsets):
width, height = size
x0 = -width / 2
x1 = +width / 2
y1 = y0 + height
rect_pts = [(x0, y0), (x1, y0), (x1, y1), (x0, y1)]
c.add_polygon(rect_pts, layer=layer)
ref_layer = c << compass(
size=(width, 2 * height), layer=layer, port_type="electrical"
)
ref_layer.ymin = y0

if via:
via = gf.get_component(via)
Expand All @@ -64,24 +66,8 @@ def via_stack_with_offset(
ref.move((x00, y00))

y0 += offset
y1 = y0 + height
rect_pts = [(x0, y0), (x1, y0), (x1, y1), (x0, y1)]
c.add_polygon(rect_pts, layer=layer)

port_width = height if port_orientation in {0, 180} else width

if port_orientation not in [0, 90, 270, 180]:
raise ValueError(
f"Invalid port_orientation = {port_orientation} not in [0, 90, 180, 270]"
)
c.add_port(
name="e1",
width=port_width,
orientation=port_orientation,
center=(0, y1),
port_type="electrical",
layer=list(layers)[-1],
)
c.add_ports(ref_layer.ports)
return c


Expand All @@ -91,11 +77,24 @@ def via_stack_with_offset(
vias=(None, viac),
)

via_stack_with_offset_ppp_m1 = gf.partial(
via_stack_with_offset,
layers=("PPP", "M1"),
vias=(None, viac),
)

via_stack_with_offset_m1_m3 = gf.partial(
via_stack_with_offset,
layers=("M1", "M2", "M3"),
vias=("via1", "via2", None),
)


if __name__ == "__main__":
c = via_stack_with_offset_ppp_m1(
layers=("SLAB90", "M1"),
sizes=((20, 10), (20, 10)),
vias=(viac(size=(18, 2), spacing=(5, 5)), None),
)
# c = via_stack_with_offset_ppp_m1(
# layers=("SLAB90", "M1"),
# sizes=((20, 10), (20, 10)),
# vias=(viac(size=(18, 2), spacing=(5, 5)), None),
# )
c = via_stack_with_offset_m1_m3(offsets=(5, 5, 5))
c.show(show_ports=True)
2 changes: 1 addition & 1 deletion gdsfactory/generic_tech/klayout/tech/layers.lyp
Expand Up @@ -73,7 +73,7 @@
<fill-color>#805000</fill-color>
<frame-brightness>0</frame-brightness>
<fill-brightness>0</fill-brightness>
<dither-pattern>I1</dither-pattern>
<dither-pattern>I3</dither-pattern>
<line-style/>
<valid>true</valid>
<visible>true</visible>
Expand Down
2 changes: 1 addition & 1 deletion gdsfactory/generic_tech/layer_views.yaml
Expand Up @@ -26,7 +26,7 @@ LayerViews:
SLAB90:
layer: [3, 0]
layer_in_name: true
hatch_pattern: hollow
hatch_pattern: coarsely dotted
transparent: true
width: 1
color: "#805000"
Expand Down
1 change: 1 addition & 0 deletions tests/test_components.py
Expand Up @@ -17,6 +17,7 @@
"pack_doe_grid",
"crossing",
"spiral_racetrack",
"ring_section_based",
}


Expand Down
58 changes: 45 additions & 13 deletions tests/test_components/test_settings_via_stack_with_offset_.yml
Expand Up @@ -2,7 +2,7 @@ name: via_stack_with_offset
ports:
e1:
center:
- 0.0
- -5.0
- 10.0
layer:
- 41
Expand All @@ -11,6 +11,42 @@ ports:
orientation: 180
port_type: electrical
shear_angle: null
width: 20
e2:
center:
- 0.0
- 20.0
layer:
- 41
- 0
name: e2
orientation: 90
port_type: electrical
shear_angle: null
width: 10
e3:
center:
- 5.0
- 10.0
layer:
- 41
- 0
name: e3
orientation: 0.0
port_type: electrical
shear_angle: null
width: 20
e4:
center:
- 0.0
- 0.0
layer:
- 41
- 0
name: e4
orientation: 270
port_type: electrical
shear_angle: null
width: 10
settings:
changed: {}
Expand All @@ -20,12 +56,10 @@ settings:
- PPP
- M1
offsets: null
port_orientation: 180
sizes:
- - 10
- 10
- - 10
- 10
size:
- 10
- 10
sizes: null
vias:
- null
- function: via
Expand All @@ -36,12 +70,10 @@ settings:
- PPP
- M1
offsets: null
port_orientation: 180
sizes:
- - 10
- 10
- - 10
- 10
size:
- 10
- 10
sizes: null
vias:
- null
- function: via
Expand Down