from __future__ import annotations
from typing import Literal, Union
import numpy as np
import pydantic.v1 as pd
from tidy3d.components.base import Tidy3dBaseModel
from tidy3d.components.data.data_array import SpatialDataArray
from tidy3d.constants import PERCMCUBE, SECOND
[docs]
class FossumCarrierLifetime(Tidy3dBaseModel):
"""
Parameters for the Fossum carrier lifetime model
Notes
-----
This model expresses the carrier lifetime as a function of the temperature and doping concentration.
.. math::
\\tau = \\frac{\\tau_{300} \\left( T/300 \\right)^\\alpha_T}{A + B (N/N_0) + C (N/N_0)^\\alpha}
Example
-------
>>> import tidy3d as td
>>> default_Si = td.FossumCarrierLifetime(
... tau_300=3.3e-6,
... alpha_T=-0.5,
... N0=7.1e15,
... A=1,
... B=0,
... C=1,
... alpha=1
... )
References
----------
Fossum, J. G., and D. S. Lee. "A physical model for the dependence of carrier lifetime on doping density in nondegenerate silicon." Solid-State Electronics 25.8 (1982): 741-747.
"""
tau_300: pd.PositiveFloat = pd.Field(
..., title="Tau at 300K", description="Carrier lifetime at 300K", units=SECOND
)
alpha_T: float = pd.Field(
..., title="Exponent for thermal dependence", description="Exponent for thermal dependence"
)
N0: pd.PositiveFloat = pd.Field(
..., title="Reference concentration", description="Reference concentration", units=PERCMCUBE
)
A: float = pd.Field(..., title="Constant A", description="Constant A")
B: float = pd.Field(..., title="Constant B", description="Constant B")
C: float = pd.Field(..., title="Constant C", description="Constant C")
alpha: float = pd.Field(..., title="Exponent constant", description="Exponent constant")
CarrierLifetimeType = Union[FossumCarrierLifetime]
[docs]
class AugerRecombination(Tidy3dBaseModel):
"""
Parameters for the Auger recombination model.
Notes
-----
The Auger recombination rate ``R_A`` is primarily defined by the electrons and holes Auger recombination
coefficients, :math:`C_n` and :math:`C_p`, respectively.
.. math::
R_A = \\left( C_n n + C_p p \\right) \\left( np - n_0 p_0 \\right)
Example
-------
>>> import tidy3d as td
>>> default_Si = td.AugerRecombination(
... c_n=2.8e-31,
... c_p=9.9e-32,
... )
"""
c_n: pd.PositiveFloat = pd.Field(
...,
title="Constant for electrons",
description="Constant for electrons.",
units="cm^6/s",
)
c_p: pd.PositiveFloat = pd.Field(
...,
title="Constant for holes",
description="Constant for holes.",
units="cm^6/s",
)
[docs]
class RadiativeRecombination(Tidy3dBaseModel):
"""
Defines the parameters for the radiative recombination model.
Notes
-----
This is a direct recombination model primarily defined by a radiative recombination coefficient :math:`R_{\\text{rad}}`.
.. math::
R_{\\text{rad}} = C \\left( np - n_0 p_0 \\right)
Example
-------
>>> import tidy3d as td
>>> default_Si = td.RadiativeRecombination(
... r_const=1.6e-14
... )
"""
r_const: float = pd.Field(
...,
title="Radiation constant",
description="Radiation constant of the radiative recombination model.",
units="cm^3/s",
)
[docs]
class ShockleyReedHallRecombination(Tidy3dBaseModel):
"""Defines the parameters for the Shockley-Reed-Hall (SRH) recombination model.
Notes
-----
The recombination rate parameter from this model is defined from [1]_ as follows:
.. math::
R_{SRH} = \\frac{n p - n_0 p_0}{\\tau_p \\left(n + \\sqrt{n_0 p_0}\\right) + \\tau_n \\left(p + \\sqrt{n_0 p_0}\\right)}.
Note that the electron and holes densities are defined within the :class:`SemiconductorMedium`. The electron
lifetime :math:`\\tau_n` and hole lifetimes :math:`\\tau_p` need to be defined.
.. [1] Schenk. A model for the field and temperature dependence of shockley-read-hall
lifetimes in silicon. Solid-State Electronics, 35:1585–1596, 1992.
Example
-------
>>> import tidy3d as td
>>> default_Si = td.ShockleyReedHallRecombination(
... tau_n=3.3e-6,
... tau_p=4e-6,
... )
Note
----
Important considerations when using this model:
- This model represents mid-gap traps Shockley-Reed-Hall recombination.
"""
tau_n: Union[pd.PositiveFloat, CarrierLifetimeType] = pd.Field(
..., title="Electron lifetime", description="Electron lifetime", units=SECOND
)
tau_p: Union[pd.PositiveFloat, CarrierLifetimeType] = pd.Field(
..., title="Hole lifetime", description="Hole lifetime", units=SECOND
)
[docs]
class DistributedGeneration(Tidy3dBaseModel):
"""Class that allows to add a distributed generation model.
Notes
-----
The generation rate will be interpolated to the simulation mesh during the setup phase.
In places where the generation rate is not defined, it will be filled with zeros.
Example
-------
>>> import tidy3d as td
>>> import numpy as np
>>> x = [1,2]
>>> y = [2,3,4]
>>> z = [3,4,5,6]
>>> coords = dict(x=x, y=y, z=z)
>>> fd = td.SpatialDataArray(np.random.random((2,3,4)), coords=coords)
>>> dist_g = td.DistributedGeneration(rate=fd)
"""
rate: SpatialDataArray = pd.Field(
...,
title="Generation rate",
description="Spatially varying generation rate.",
units="1/(cm^3 s^1)",
)
[docs]
@classmethod
def from_rate_um3(cls, gen_um3: SpatialDataArray) -> DistributedGeneration:
"""Creates a DistributedGeneration from a SpatialDataArray in um^-3 s^-1."""
gen_cm3 = np.array(gen_um3.data) * 1e12 # Convert from um^-3 to cm^-3
new_gen = SpatialDataArray(gen_cm3, coords=gen_um3.coords)
return cls(rate=new_gen)
[docs]
@pd.root_validator(skip_on_failure=True)
def check_spatialdataarray_dimensions(cls, values):
"""Check that the SpatialDataArray is at least 2D:"""
rate = values.get("rate")
zero_dims = [d for d in ["x", "y", "z"] if len(rate.coords[d]) <= 1]
if len(zero_dims) > 1:
raise ValueError("SpatialDataArray must be at least 2D.")
return values
[docs]
class HurkxDirectBandToBandTunneling(Tidy3dBaseModel):
"""
This class defines a direct band-to-band tunneling recombination model based on the Hurkx model
as described in [1].
Notes
-----
The direct band-to-band tunneling recombination rate :math:`R^{\\text{BTBT}}` is primarily defined by the
material's bandgap energy :math:`E_g` and the electric field :math:`F`.
Default values are provided for silicon.
.. math::
R^{\\text{BTBT}} = A \\cdot \\frac{n \\cdot p - n_i^2}{(n + n_i) \\cdot (p + n_i)} \\cdot \\left( \\frac{|\\mathbf{E}|}{E_0} \\right)^{\\sigma} \\cdot \\exp \\left(-\\frac{B}{|\\mathbf{E}|} \\cdot \\left( \\frac{E_g}{E_{g, 300}} \\right)^{3/2} \\right)
where :math:`A`, :math:`B`, :math:`E_0`, and :math:`\\sigma` are material-dependent parameters.
Example
-------
>>> import tidy3d as td
>>> default_Si = td.HurkxDirectBandToBandTunneling(
... A=1e19,
... B=1.9e6,
... E_0=1,
... sigma=2.5
... )
References
----------
.. [1] Palankovski, Vassil, and Rüdiger Quay. Analysis and simulation of heterostructure devices. Springer Science & Business Media, 2004.
"""
A: pd.PositiveFloat = pd.Field(
4e14,
title="Parameter :math:`A`",
description="Parameter :math:`A` in the direct BTBT Hurkx model.",
units="1/(cm^3 s)",
)
B: float = pd.Field(
1.9e6,
title="Parameter :math:`B`",
description="Parameter :math:`B` in the direct BTBT Hurkx model.",
units="V/cm",
)
E_0: pd.PositiveFloat = pd.Field(
1,
title="Reference electric field :math:`E_0`",
description="Reference electric field :math:`E_0` in the direct BTBT Hurkx model.",
units="V/cm",
)
sigma: float = pd.Field(
2.5,
title="Exponent parameter",
description="Exponent :math:`\\sigma` in the direct BTBT Hurkx model. For direct "
"semiconductors :math:`\\sigma` is typically 2.0, while for indirect "
"semiconductors :math:`\\sigma` is typically 2.5.",
)
[docs]
class SelberherrImpactIonization(Tidy3dBaseModel):
"""
This class defines the parameters for the Selberherr impact ionization model. Two formulations are available that
depend on the driving field, as described in [1]_ (:math:`\\| E \\|`) and [2]_ (:math:`E \\cdot J_{\\nu} / \\| E \\|` for :math:`\\nu = n,p`).
Notes
-----
The impact ionization rate ``\\alpha_{\\nu}`` (for :math:`\\nu = p` (holes) and :math:`\\nu = n` (electrons)) is defined by:
.. math::
\\alpha_{\\nu} = \\alpha_{\\nu}^\\infty \\cdot \\exp \\left( - \\left( \\frac{E_{\\nu}^{\\text{crit}} \\cdot |\\mathbf{J}_{\\nu}|}{E \\cdot \\mathbf{J}_{\\nu}} \\right)^{\\beta_{\\nu}} \\right)
where :math:`\\alpha_{\\nu}^\\infty`, :math:`E_{\\nu}^{\\text{crit}}`, and :math:`\\beta_{\\nu}` are material-dependent parameters.
Example
-------
>>> import tidy3d as td
>>> default_Si = td.SelberherrImpactIonization(
... alpha_n_inf=7.03e5,
... alpha_p_inf=1.582e6,
... E_n_crit=1.23e6,
... E_p_crit=2.03e6,
... beta_n=1,
... beta_p=1,
... formulation='PQ'
... )
References
----------
.. [1] Selberherr, Siegfried. Analysis and simulation of semiconductor devices. Springer Science & Business Media, 1984.
.. [2] Vassil Palankovski and Rüdiger Quay. Analysis and simulation of heterostructure devices. Springer Science & Business Media, 2004.
"""
alpha_n_inf: pd.PositiveFloat = pd.Field(
...,
title="Electron ionization coefficient at infinite field",
description="Electron ionization coefficient at infinite field.",
units="1/cm",
)
alpha_p_inf: pd.PositiveFloat = pd.Field(
...,
title="Hole ionization coefficient at infinite field",
description="Hole ionization coefficient at infinite field.",
units="1/cm",
)
E_n_crit: pd.PositiveFloat = pd.Field(
...,
title="Critical electric field for electrons",
description="Critical electric field for electrons.",
units="V/cm",
)
E_p_crit: pd.PositiveFloat = pd.Field(
...,
title="Critical electric field for holes",
description="Critical electric field for holes.",
units="V/cm",
)
beta_n: pd.PositiveFloat = pd.Field(
...,
title="Exponent for electrons",
description="Exponent for electrons.",
)
beta_p: pd.PositiveFloat = pd.Field(
...,
title="Exponent for holes",
description="Exponent for holes.",
)
formulation: Literal["Selberherr", "PQ"] = pd.Field(
"PQ",
title="Formulation",
description="Formulation used for impact ionization. Options are 'Selberherr' "
"or 'PQ' for Selberherr and Palankovski and Quay formulations, respectively.",
)