Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

### Changed
- Added validation to `GaussianDoping` to ensure `ref_con < concentration`, validate `source` face identifier, and warn the user when the box size is not sufficient for the specified transition width.

### Fixed

Expand Down
8 changes: 8 additions & 0 deletions schemas/EMESimulation.json
Original file line number Diff line number Diff line change
Expand Up @@ -5916,6 +5916,14 @@
},
"source": {
"default": "xmin",
"enum": [
"xmax",
"xmin",
"ymax",
"ymin",
"zmax",
"zmin"
],
"type": "string"
},
"type": {
Expand Down
8 changes: 8 additions & 0 deletions schemas/HeatChargeSimulation.json
Original file line number Diff line number Diff line change
Expand Up @@ -3894,6 +3894,14 @@
},
"source": {
"default": "xmin",
"enum": [
"xmax",
"xmin",
"ymax",
"ymin",
"zmax",
"zmin"
],
"type": "string"
},
"type": {
Expand Down
8 changes: 8 additions & 0 deletions schemas/HeatSimulation.json
Original file line number Diff line number Diff line change
Expand Up @@ -3894,6 +3894,14 @@
},
"source": {
"default": "xmin",
"enum": [
"xmax",
"xmin",
"ymax",
"ymin",
"zmax",
"zmin"
],
"type": "string"
},
"type": {
Expand Down
8 changes: 8 additions & 0 deletions schemas/ModeSimulation.json
Original file line number Diff line number Diff line change
Expand Up @@ -5116,6 +5116,14 @@
},
"source": {
"default": "xmin",
"enum": [
"xmax",
"xmin",
"ymax",
"ymin",
"zmax",
"zmin"
],
"type": "string"
},
"type": {
Expand Down
8 changes: 8 additions & 0 deletions schemas/Simulation.json
Original file line number Diff line number Diff line change
Expand Up @@ -8473,6 +8473,14 @@
},
"source": {
"default": "xmin",
"enum": [
"xmax",
"xmin",
"ymax",
"ymin",
"zmax",
"zmin"
],
"type": "string"
},
"type": {
Expand Down
8 changes: 8 additions & 0 deletions schemas/TerminalComponentModeler.json
Original file line number Diff line number Diff line change
Expand Up @@ -8640,6 +8640,14 @@
},
"source": {
"default": "xmin",
"enum": [
"xmax",
"xmin",
"ymax",
"ymin",
"zmax",
"zmin"
],
"type": "string"
},
"type": {
Expand Down
45 changes: 44 additions & 1 deletion tests/test_components/test_heat_charge.py
Original file line number Diff line number Diff line change
Expand Up @@ -1878,13 +1878,18 @@ def test_gaussian_doping_initialization():


def test_gaussian_doping_sigma_calculation():
"""Test sigma calculation in GaussianDoping."""
"""Test sigma calculation in GaussianDoping and ref_con validator."""
box = td.GaussianDoping(
size=(1, 1, 1), ref_con=1e15, concentration=1e18, width=0.1, source="xmin"
)
expected_sigma = np.sqrt(-(0.1**2) / (2 * np.log(1e15 / 1e18)))
assert np.isclose(box.sigma, expected_sigma), "Sigma calculation is incorrect."

with pytest.raises(pd.ValidationError, match="must be less than.*concentration"):
_ = td.GaussianDoping(
size=(1, 1, 1), ref_con=1e19, concentration=1e18, width=0.1, source="xmin"
)


def test_gaussian_doping_get_contrib():
"""Test _get_contrib method in GaussianDoping."""
Expand Down Expand Up @@ -1933,6 +1938,44 @@ def test_gaussian_doping_bounds_behavior():
assert box.bounds == box_coords, "Bounds should match provided box_coords."


def test_gaussian_doping_validator_source():
"""Test validator for source face."""
valid_sources = ["xmin", "xmax", "ymin", "ymax", "zmin", "zmax"]
for source in valid_sources:
_ = td.GaussianDoping(
size=(1, 1, 1), ref_con=1e15, concentration=1e18, width=0.1, source=source
)

with pytest.raises(pd.ValidationError):
_ = td.GaussianDoping(
size=(1, 1, 1), ref_con=1e15, concentration=1e18, width=0.1, source="invalid"
)


def test_gaussian_doping_validator_width():
"""Test validator for width vs size."""
width = 0.1
_ = td.GaussianDoping(
size=(0.2, 0.2, 0.2), ref_con=1e15, concentration=1e18, width=width, source="xmin"
)
_ = td.GaussianDoping(
size=(np.inf, 1, 1), ref_con=1e15, concentration=1e18, width=width, source="xmin"
)

with AssertLogLevel("WARNING", contains_str="'x' direction"):
_ = td.GaussianDoping(
size=(0.15, 1, 1), ref_con=1e15, concentration=1e18, width=width, source="xmin"
)
with AssertLogLevel("WARNING", contains_str="'y' direction"):
_ = td.GaussianDoping(
size=(1, 0.15, 1), ref_con=1e15, concentration=1e18, width=width, source="xmin"
)
with AssertLogLevel("WARNING", contains_str="'z' direction"):
_ = td.GaussianDoping(
size=(1, 1, 0.15), ref_con=1e15, concentration=1e18, width=width, source="xmin"
)


def test_2D_doping_box():
"""Check that the doping boxes can handle 2D cases correctly."""

Expand Down
32 changes: 30 additions & 2 deletions tidy3d/components/tcad/doping.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from __future__ import annotations

from typing import Union
from typing import Literal, Union

import numpy as np
import pydantic.v1 as pd
Expand All @@ -14,6 +14,7 @@
from tidy3d.components.geometry.base import Box
from tidy3d.constants import MICROMETER, PERCMCUBE, inf
from tidy3d.exceptions import SetupError
from tidy3d.log import log


class AbstractDopingBox(Box):
Expand Down Expand Up @@ -171,7 +172,7 @@ class GaussianDoping(AbstractDopingBox):
units=MICROMETER,
)

source: str = pd.Field(
source: Literal["xmin", "xmax", "ymin", "ymax", "zmin", "zmax"] = pd.Field(
"xmin",
title="Source face",
description="Specifies the side of the box acting as the source, i.e., "
Expand All @@ -180,6 +181,33 @@ class GaussianDoping(AbstractDopingBox):
"are [``xmin``, ``xmax``, ``ymin``, ``ymax``, ``zmin``, ``zmax``]",
)

@pd.validator("concentration")
def _validate_ref_con_less_than_concentration(cls, val, values):
"""Ensure ref_con < concentration to avoid negative sqrt in sigma calculation."""
if "ref_con" in values:
if values["ref_con"] >= val:
raise ValueError(
f"'ref_con' ({values['ref_con']}) must be less than 'concentration' ({val}) "
"for GaussianDoping. The reference concentration at the edges must be lower "
"than the peak concentration at the center."
)
return val

@pd.validator("width")
def _validate_width_vs_size(cls, val, values):
"""Warn if box size may be too small for the specified transition width."""
if "size" in values:
size = values["size"]
for i, s in enumerate(size):
if not np.isinf(s) and s < 2 * val:
dim_name = ["x", "y", "z"][i]
log.warning(
f"Box size in '{dim_name}' direction ({s} μm) is less than "
f"'2*width' ({2 * val} μm) for 'GaussianDoping'. "
"This may result in unexpected behavior in the transition region."
)
return val

@cached_property
def sigma(self):
"""The sigma parameter of the pseudo-gaussian"""
Expand Down
Loading