import typing as _typ
import photonforge as _pf
from .extension import cross, focused_grating, grating, linear_taper, mmi, ruler, vernier_scale
_stencils = {
"cross": cross,
"focused_grating": focused_grating,
"grating": grating,
"linear_taper": linear_taper,
"mmi": mmi,
"ruler": ruler,
"vernier_scale": vernier_scale,
"text": _pf.extension.text,
}
_StencilName = _typ.Literal[
"cross",
"focused_grating",
"grating",
"linear_taper",
"mmi",
"ruler",
"vernier_scale",
"text",
]
[docs]
@_pf.parametric_component
def as_component(
*,
layer: _typ.Union[str, _typ.Sequence],
stencil: _StencilName,
technology: _typ.Optional[_pf.Technology] = None,
**stencil_kwargs: _typ.Any,
) -> _pf.Component:
"""
Create a parametric component from a stencil.
Args:
layer: Layer to be used for the stencil. In the case of stencil
functions that return multiple structure lists (like
:func:`vernier_scale`), one layer per list is required.
stencil: Name of the stencil function.
technology: Component technology. If ``None``, the default
technology is used.
stencil_kwargs: Keyword arguments for the stencil function.
Returns:
Parametric :class:`photonforge.Component`
Note:
The :func:`photonforge.text` function can also be used in this
function as a stencil.
Examples:
>>> grating_component = stencil.as_component(
... layer="SLAB",
... stencil="grating",
... period=0.6,
... num_periods=20,
... width=10,
... taper_length=30,
... taper_width=0.5,
... )
>>> scale_component = stencil.as_component(
... layer=("SLAB","WG_CORE"),
... stencil_name="vernier_scale",
... unit=15,
... marker_length=20,
... marker_width=2,
... )
"""
stencil = _stencils.get(stencil, None)
if stencil is None:
valid_names = "', '".join(_stencils)
raise RuntimeError(
f"Stencil '{stencil}' does not exist. Valid stencil names are '{valid_names}'."
)
if technology is None:
technology = _pf.config.default_technology
c = _pf.Component(technology=technology)
structures = stencil(**stencil_kwargs)
structure_type = (_pf.Polygon, _pf.Rectangle, _pf.Circle, _pf.Path)
group = 0 if isinstance(structures, structure_type) else len(structures)
if group > 0 and not isinstance(structures[0], structure_type):
# Return type is Sequence[Sequence[Structure]]
error_msg = (
f"Argument 'layer' must be a sequence of {len(structures)} layers for stencil "
f"'{stencil}'."
)
if isinstance(layer, str) or len(layer) != len(structures):
raise TypeError(error_msg)
else:
error_msg = (
f"Argument 'layer' must be a string or a pair of integers for stencil '{stencil}'."
)
layer = [layer]
structures = [structures]
if group == 0:
# Return type is a single structure
structures = [structures]
for group_layer, group_structures in zip(layer, structures):
try:
c.add(group_layer, *group_structures)
except TypeError as err:
if err.args[0].startswith("Argument 0") and not isinstance(group_layer, str):
raise TypeError(error_msg) from err
else:
raise
return c