Source code for tidy3d.plugins.invdes.transformation

# transformations applied to design region
from __future__ import annotations

import abc
import typing

import autograd.numpy as anp
import pydantic.v1 as pd

import tidy3d as td
from tidy3d.plugins.autograd.functions import threshold
from tidy3d.plugins.autograd.invdes import make_filter_and_project

from .base import InvdesBaseModel


class AbstractTransformation(InvdesBaseModel, abc.ABC):
    """Base class for transformations."""

    @abc.abstractmethod
    def evaluate(self) -> float:
        """Evaluate the penalty on supplied values."""

    def __call__(self, *args, **kwargs) -> float:
        return self.evaluate(*args, **kwargs)


[docs] class FilterProject(InvdesBaseModel): """Transformation involving convolution by a conic filter followed by a ``tanh`` projection. Notes ----- .. image:: ../../_static/img/filter_project.png """ radius: pd.PositiveFloat = pd.Field( ..., title="Filter Radius", description="Radius of the filter to convolve with supplied spatial data. " "Note: the corresponding feature size expressed in the device is typically " "sqrt(3) times smaller than the radius. For best results, it is recommended to make your " "radius about twice as large as the desired feature size. " "Note: the radius value is often only approximately related to the final feature sizes. " "It is useful to apply a ``FilterProject`` transformation to 'encourage' larger " "feature sizes, but we ultimately recommend creating a ``ErosionDilationPenalty`` to the " "``DesignRegion.penalties`` if you have strict fabrication constraints.", units=td.constants.MICROMETER, ) beta: float = pd.Field( 1.0, ge=1.0, title="Beta", description="Steepness of the binarization, " "higher means more sharp transition " "at the expense of gradient accuracy and ease of optimization. ", ) eta: float = pd.Field( 0.5, ge=0.0, le=1.0, title="Eta", description="Halfway point in projection function." ) strict_binarize: bool = pd.Field( False, title="Binarize strictly", description="If ``False``, the binarization is still continuous between min and max. " "If ``True``, the values are snapped to the min and max values after projection.", )
[docs] def evaluate(self, spatial_data: anp.ndarray, design_region_dl: float) -> anp.ndarray: """Evaluate this transformation on spatial data, given some grid size in the region.""" filt_proj = make_filter_and_project( self.radius, design_region_dl, beta=self.beta, eta=self.eta ) data_projected = filt_proj(spatial_data) if self.strict_binarize: data_projected = threshold(data_projected) return data_projected
TransformationType = typing.Union[FilterProject]