diff --git a/tests/test_components/test_heat_charge.py b/tests/test_components/test_heat_charge.py index 09a61b12d9..6d256dbedb 100644 --- a/tests/test_components/test_heat_charge.py +++ b/tests/test_components/test_heat_charge.py @@ -11,6 +11,8 @@ from tidy3d.components.tcad.types import ( AugerRecombination, CaugheyThomasMobility, + ConstantEffectiveDOS, + ConstantEnergyBandGap, SlotboomBandGapNarrowing, ) from tidy3d.exceptions import DataError @@ -39,9 +41,9 @@ class CHARGE_SIMULATION: permittivity=11.7, N_d=0, N_a=0, - N_c=2.86e19, - N_v=3.1e19, - E_g=1.11, + N_c=ConstantEffectiveDOS(N=2.86e19), + N_v=ConstantEffectiveDOS(N=3.1e19), + E_g=ConstantEnergyBandGap(eg=1.11), mobility_n=CaugheyThomasMobility( mu_min=52.2, mu=1471.0, @@ -952,23 +954,30 @@ def Si_p(self): semiconductor = CHARGE_SIMULATION.intrinsic_Si.charge semiconductor = semiconductor.updated_copy( N_a=CHARGE_SIMULATION.acceptors, + ) + return CHARGE_SIMULATION.intrinsic_Si.updated_copy( + charge=semiconductor, + heat=td.SolidMedium(conductivity=1), name="Si_p", ) - return CHARGE_SIMULATION.intrinsic_Si.updated_copy(charge=semiconductor) @pytest.fixture(scope="class") def Si_n(self): semiconductor = CHARGE_SIMULATION.intrinsic_Si.charge semiconductor = semiconductor.updated_copy( N_d=CHARGE_SIMULATION.donors, + ) + return CHARGE_SIMULATION.intrinsic_Si.updated_copy( + charge=semiconductor, + heat=td.SolidMedium(conductivity=1), name="Si_n", ) - return CHARGE_SIMULATION.intrinsic_Si.updated_copy(charge=semiconductor) @pytest.fixture(scope="class") def SiO2(self): return td.MultiPhysicsMedium( charge=td.ChargeInsulatorMedium(permittivity=3.9), + heat=td.SolidMedium(conductivity=2), name="SiO2", ) @@ -1049,18 +1058,13 @@ def capacitance_global_mnt(self): # Define charge settings as fixtures within the class @pytest.fixture(scope="class") def charge_tolerance(self): - return td.IsothermalSteadyChargeDCAnalysis( - temperature=300, - tolerance_settings=td.ChargeToleranceSpec(rel_tol=1e5, abs_tol=1e3, max_iters=400), - fermi_dirac=True, - ) - - @pytest.fixture(scope="class") - def charge_dc_regime(self): - return td.DCVoltageSource(voltage=[1]) + return td.ChargeToleranceSpec(rel_tol=1e5, abs_tol=1e3, max_iters=400) def test_charge_simulation( self, + Si_n, + Si_p, + SiO2, oxide, p_side, n_side, @@ -1070,9 +1074,14 @@ def test_charge_simulation( bc_n, bc_p, charge_tolerance, - charge_dc_regime, ): """Ensure charge simulation produces the correct errors when needed.""" + # NOTE: start tests with isothermal spec + isothermal_spec = td.IsothermalSteadyChargeDCAnalysis( + temperature=300, + tolerance_settings=charge_tolerance, + fermi_dirac=True, + ) sim = td.HeatChargeSimulation( structures=[oxide, p_side, n_side], medium=td.MultiPhysicsMedium( @@ -1083,7 +1092,7 @@ def test_charge_simulation( size=CHARGE_SIMULATION.sim_size, grid_spec=td.UniformUnstructuredGrid(dl=0.05), boundary_spec=[bc_n, bc_p], - analysis_spec=charge_tolerance, + analysis_spec=isothermal_spec, ) # At least one ChargeSimulationMonitor should be added @@ -1118,6 +1127,45 @@ def test_charge_simulation( ) _ = sim.updated_copy(boundary_spec=[new_bc_p, bc_n]) + # test non isothermal spec + non_isothermal_spec = td.SteadyChargeDCAnalysis(tolerance_settings=charge_tolerance) + + sim = sim.updated_copy(analysis_spec=non_isothermal_spec) + with pytest.raises(pd.ValidationError): + # remove heat from mediums + new_structs = [] + for struct in sim.structures: + new_structs.append( + struct.updated_copy(medium=struct.medium.updated_copy(heat=None)) + ) + _ = sim.updated_copy(structures=new_structs) + + with pytest.raises(pd.ValidationError): + # remove charge from mediums + new_structs = [] + for struct in sim.structures: + new_structs.append( + struct.updated_copy(medium=struct.medium.updated_copy(charge=None)) + ) + _ = sim.updated_copy(structures=new_structs) + + with pytest.raises(pd.ValidationError): + # make sure there is at least one semiconductor + new_structs = [] + for struct in sim.structures: + if isinstance(struct.medium.charge, td.SemiconductorMedium): + new_structs.append( + struct.updated_copy( + medium=struct.medium.updated_copy( + charge=td.ChargeInsulatorMedium(permittivity=1), + heat=None, + ) + ) + ) + else: + new_structs.append(struct) + _ = sim.updated_copy(structures=new_structs) + def test_doping_distributions(self): """Test doping distributions.""" # Implementation needed diff --git a/tidy3d/__init__.py b/tidy3d/__init__.py index 6bc0bd31ab..c3b2c30c08 100644 --- a/tidy3d/__init__.py +++ b/tidy3d/__init__.py @@ -20,6 +20,7 @@ from tidy3d.components.spice.analysis.dc import ( ChargeToleranceSpec, IsothermalSteadyChargeDCAnalysis, + SteadyChargeDCAnalysis, ) from tidy3d.components.spice.sources.dc import DCCurrentSource, DCVoltageSource from tidy3d.components.spice.sources.types import VoltageSourceType @@ -63,12 +64,19 @@ AugerRecombination, CaugheyThomasMobility, ConstantMobilityModel, + ConstantEffectiveDOS, + ConstantEnergyBandGap, + QuadraticEnergyBandGap, + VarshniEnergyBandGap, ConvectionBC, CurrentBC, + DualValleyEffectiveDOS, HeatFluxBC, HeatFromElectricSource, HeatSource, InsulatingBC, + IsotropicEffectiveDOS, + MultiValleyEffectiveDOS, RadiativeRecombination, ShockleyReedHallRecombination, SlotboomBandGapNarrowing, @@ -441,6 +449,10 @@ def set_logging_level(level: str) -> None: "CoaxialLumpedResistor", "ConstantDoping", "ConstantMobilityModel", + "ConstantEffectiveDOS", + "IsotropicEffectiveDOS", + "MultiValleyEffectiveDOS", + "DualValleyEffectiveDOS", "ContinuousWave", "ContinuousWaveTimeModulation", "ContourPathAveraging", @@ -651,6 +663,7 @@ def set_logging_level(level: str) -> None: "Staircasing", "SteadyCapacitanceData", "SteadyCapacitanceMonitor", + "SteadyChargeDCAnalysis", "SteadyEnergyBandData", "SteadyEnergyBandMonitor", "SteadyFreeCarrierData", @@ -696,4 +709,7 @@ def set_logging_level(level: str) -> None: "set_logging_console", "set_logging_file", "wavelengths", + "ConstantEnergyBandGap", + "QuadraticEnergyBandGap", + "VarshniEnergyBandGap", ] diff --git a/tidy3d/components/material/tcad/charge.py b/tidy3d/components/material/tcad/charge.py index 707e0ebd64..d784b2b9de 100644 --- a/tidy3d/components/material/tcad/charge.py +++ b/tidy3d/components/material/tcad/charge.py @@ -9,6 +9,8 @@ from tidy3d.components.tcad.doping import DopingBoxType from tidy3d.components.tcad.types import ( BandGapNarrowingModelType, + EffectiveDOSModelType, + EnergyBandGapModelType, MobilityModelType, RecombinationModelType, ) @@ -254,21 +256,21 @@ class SemiconductorMedium(AbstractChargeMedium): """ - N_c: pd.PositiveFloat = pd.Field( + N_c: EffectiveDOSModelType = pd.Field( ..., title="Effective density of electron states", description=r"$N_c$ Effective density of states in the conduction band.", units="cm^(-3)", ) - N_v: pd.PositiveFloat = pd.Field( + N_v: EffectiveDOSModelType = pd.Field( ..., title="Effective density of hole states", description=r"$N_v$ Effective density of states in the valence band.", units="cm^(-3)", ) - E_g: pd.PositiveFloat = pd.Field( + E_g: EnergyBandGapModelType = pd.Field( ..., title="Band-gap energy", description="Band-gap energy", diff --git a/tidy3d/components/spice/analysis/dc.py b/tidy3d/components/spice/analysis/dc.py index e9e1e2aca5..ca66d5ff3e 100644 --- a/tidy3d/components/spice/analysis/dc.py +++ b/tidy3d/components/spice/analysis/dc.py @@ -49,19 +49,11 @@ class ChargeToleranceSpec(Tidy3dBaseModel): ) -class IsothermalSteadyChargeDCAnalysis(Tidy3dBaseModel): +class SteadyChargeDCAnalysis(Tidy3dBaseModel): """ Configures relevant steady-state DC simulation parameters for a charge simulation. """ - temperature: pd.PositiveFloat = pd.Field( - 300, - title="Temperature", - description="Lattice temperature. Assumed constant throughout the device. " - "Carriers are assumed to be at thermodynamic equilibrium with the lattice.", - units=KELVIN, - ) - tolerance_settings: ChargeToleranceSpec = pd.Field( default=ChargeToleranceSpec(), title="Tolerance settings" ) @@ -83,3 +75,17 @@ class IsothermalSteadyChargeDCAnalysis(Tidy3dBaseModel): "where very high doping may lead the pseudo-Fermi energy level to approach " "either the conduction or valence energy bands.", ) + + +class IsothermalSteadyChargeDCAnalysis(SteadyChargeDCAnalysis): + """ + Configures relevant steady-state DC simulation parameters for a charge simulation. + """ + + temperature: pd.PositiveFloat = pd.Field( + 300, + title="Temperature", + description="Lattice temperature. Assumed constant throughout the device. " + "Carriers are assumed to be at thermodynamic equilibrium with the lattice.", + units=KELVIN, + ) diff --git a/tidy3d/components/spice/types.py b/tidy3d/components/spice/types.py index 486bd0e734..daac472938 100644 --- a/tidy3d/components/spice/types.py +++ b/tidy3d/components/spice/types.py @@ -2,6 +2,9 @@ from typing import Union -from tidy3d.components.spice.analysis.dc import IsothermalSteadyChargeDCAnalysis +from tidy3d.components.spice.analysis.dc import ( + IsothermalSteadyChargeDCAnalysis, + SteadyChargeDCAnalysis, +) -ElectricalAnalysisType = Union[IsothermalSteadyChargeDCAnalysis] +ElectricalAnalysisType = Union[SteadyChargeDCAnalysis, IsothermalSteadyChargeDCAnalysis] diff --git a/tidy3d/components/tcad/bandgap_energy.py b/tidy3d/components/tcad/bandgap_energy.py new file mode 100644 index 0000000000..da9fe3c5c3 --- /dev/null +++ b/tidy3d/components/tcad/bandgap_energy.py @@ -0,0 +1,111 @@ +from __future__ import annotations + +import pydantic.v1 as pd + +from tidy3d.components.base import Tidy3dBaseModel +from tidy3d.constants import ELECTRON_VOLT + + +class ConstantEnergyBandGap(Tidy3dBaseModel): + """Constant Energy band gap""" + + eg: pd.PositiveFloat = pd.Field( + title="Band Gap", + description="Energy band gap", + units=ELECTRON_VOLT, + ) + + +class QuadraticEnergyBandGap(Tidy3dBaseModel): + """ + Models the temperature dependence of the energy band gap (Eg) using a + quadratic approximation. + + Notes + ----- + The model uses the following formula: + + .. math:: + + E_g(T) = E_g(300) + \\alpha T + \\beta T^2 + + Example + ------- + >>> model = QuadraticEnergyBandGap( + ... eg_300=1.12, + ... alpha=-4.73e-4, + ... beta=-2.0e-7, + ... ) + """ + + eg_300: pd.PositiveFloat = pd.Field( + ..., + title="Band Gap at 300 K", + description="Energy band gap at a reference temperature of 300 K.", + units=ELECTRON_VOLT, + ) + + alpha: float = pd.Field( + ..., + title="Linear Temperature Coefficient (alpha)", + description="Linear coefficient for the temperature dependence of the band gap.", + units="eV/K", + ) + + beta: float = pd.Field( + ..., + title="Quadratic Temperature Coefficient (beta)", + description="Quadratic coefficient for the temperature dependence of the band gap.", + units="eV/K²", + ) + + +class VarshniEnergyBandGap(Tidy3dBaseModel): + """ + Models the temperature dependence of the energy band gap (Eg) + using the Varshni formula. + + Notes + ----- + The model implements the following formula: + + .. math:: + + E_g(T) = E_g(0) - \\frac{\\alpha T^2}{T + \\beta}$ + + Example + ------- + >>> # Parameters for Silicon (Si) + >>> si_model = VarshniBandGap( + ... eg_0=1.17, + ... alpha=4.73e-4, + ... beta=636.0, + ... ) + + References + ------- + + Varshni, Y. P. (1967). Temperature dependence of the energy gap in semiconductors. Physica, 34(1), 149-154. + + """ + + eg_0: pd.PositiveFloat = pd.Field( + ..., + title="Band Gap at 0 K", + description="Energy band gap at absolute zero (0 Kelvin).", + units=ELECTRON_VOLT, + ) + + alpha: pd.PositiveFloat = pd.Field( + ..., + title="Varshni Alpha Coefficient", + description="Empirical Varshni coefficient (α).", + units="eV/K", + ) + + beta: pd.PositiveFloat = pd.Field( + ..., + title="Varshni Beta Coefficient", + description="Empirical Varshni coefficient (β), related to the Debye temperature.", + units="K", + ) diff --git a/tidy3d/components/tcad/effective_DOS.py b/tidy3d/components/tcad/effective_DOS.py new file mode 100644 index 0000000000..6a7fea3903 --- /dev/null +++ b/tidy3d/components/tcad/effective_DOS.py @@ -0,0 +1,158 @@ +from abc import ABC, abstractmethod + +import numpy as np +import pydantic.v1 as pd + +from tidy3d.components.base import Tidy3dBaseModel +from tidy3d.constants import C_0, HBAR, K_B + +from ...exceptions import DataError + +# constants definition +m_e_C_square = 0.51099895069e6 # (electron mass * C_0^2) in eV +m_e_eV = m_e_C_square / C_0 / C_0 # equivalent electron mass in eV +um_3_to_cm_3 = 1e12 # conversion factor from micron^(-3) to cm^(-3) + +DOS_aux_const = 2.0 * np.power((m_e_eV * K_B) / (2 * np.pi * HBAR * HBAR), 1.5) * um_3_to_cm_3 + + +class EffectiveDOS(Tidy3dBaseModel, ABC): + """Abstract class for the effective density of states""" + + @abstractmethod + def calc_eff_dos(self, T: float): + """Abstract method to calculate the effective density of states.""" + pass + + @abstractmethod + def calc_eff_dos_derivative(self, T: float): + """Abstract method to calculate the temperature derivative of the effective density of states.""" + pass + + def get_effective_DOS(self, T: float): + if T <= 0: + raise DataError( + f"Incorrect temperature value ({T}) for the effectve density of states calculation." + ) + + return self.calc_eff_dos(T) + + def get_effective_DOS_derivative(self, T: float): + if T <= 0: + raise DataError( + f"Incorrect temperature value ({T}) for the effectve density of states calculation." + ) + + return self.calc_eff_dos_derivative(T) + + +class ConstantEffectiveDOS(EffectiveDOS): + """Constant effective density of states model.""" + + N: pd.PositiveFloat = pd.Field( + ..., title="Effective DOS", description="Effective density of states", units="cm^(-3)" + ) + + def calc_eff_dos(self, T: float): + return self.N + + def calc_eff_dos_derivative(self, T: float): + return 0.0 + + +class IsotropicEffectiveDOS(EffectiveDOS): + """Effective density of states model that assumes single valley and isotropic effective mass. + The model assumes the standard equation for the 3D semiconductor with parabolic energy dispersion: + + .. math:: + + \\begin{equation} + \\mathbf{N_eff} = 2 * (\\frac{m_eff * m_e * k_B T}{2 \\pi \\hbar^2})^(3/2) + \\end{equation} + """ + + m_eff: pd.PositiveFloat = pd.Field( + ..., + title="Effective mass", + description="Effective mass of the carriers", + units="Electron mass", + ) + + def calc_eff_dos(self, T: float): + return np.power(self.m_eff * T, 1.5) * DOS_aux_const + + def calc_eff_dos_derivative(self, T: float): + return self.calc_eff_dos(T) * 1.5 / T + + +class MultiValleyEffectiveDOS(EffectiveDOS): + """Effective density of states model that assumes multiple equivalent valleys and anisotropic effective mass. + The model assumes the standard equation for the 3D semiconductor with parabolic energy dispersion: + + .. math:: + + \\begin{equation} + \\mathbf{N_eff} = 2 * N_valley * (m_{eff_long} * m_{eff_trans} * m_{eff_trans})^(1/2) *(\\frac{m_e * k_B * T}{2 \\pi * \\hbar^2})^(3/2) + \\end{equation} + """ + + m_eff_long: pd.PositiveFloat = pd.Field( + ..., + title="Longitudinal effective mass", + description="Effective mass of the carriers in the longitudinal direction", + units="Electron mass", + ) + + m_eff_trans: pd.PositiveFloat = pd.Field( + ..., + title="Longitudinal effective mass", + description="Effective mass of the carriers in the transverse direction", + units="Electron mass", + ) + + N_valley: pd.PositiveFloat = pd.Field( + ..., title="Number of valleys", description="Number of effective valleys" + ) + + def calc_eff_dos(self, T: float): + return ( + self.N_valley + * np.power(self.m_eff_long * self.m_eff_trans * self.m_eff_trans, 0.5) + * np.power(T, 1.5) + * DOS_aux_const + ) + + def calc_eff_dos_derivative(self, T: float): + return self.calc_eff_dos(T) * 1.5 / T + + +class DualValleyEffectiveDOS(EffectiveDOS): + """Effective density of states model that assumes combibation of light holes and heavy holes with isotropic effective masses. + The model assumes the standard equation for the 3D semiconductor with parabolic energy dispersion: + + .. math:: + + \\begin{equation} + \\mathbf{N_eff} = 2 * ( {\\frac{m_{eff_lh} * m_e * k_B * T}{2 \\pi \\hbar^2})^(3/2) + (\\frac{m_{eff_hh} * m_e * k_B * T}{2 \\pi \\hbar^2})^(3/2) ) + \\end{equation} + """ + + m_eff_lh: pd.PositiveFloat = pd.Field( + ..., + title="Light hole effective mass", + description="Effective mass of the light holes", + units="Electron mass", + ) + + m_eff_hh: pd.PositiveFloat = pd.Field( + ..., + title="Heavy hole effective mass", + description="Effective mass of the heavy holes", + units="Electron mass", + ) + + def calc_eff_dos(self, T: float): + return (np.power(self.m_eff_lh * T, 1.5) + np.power(self.m_eff_hh * T, 1.5)) * DOS_aux_const + + def calc_eff_dos_derivative(self, T: float): + return self.calc_eff_dos(T) * 1.5 / T diff --git a/tidy3d/components/tcad/simulation/heat_charge.py b/tidy3d/components/tcad/simulation/heat_charge.py index d93ec0b14b..21a0dd7f60 100644 --- a/tidy3d/components/tcad/simulation/heat_charge.py +++ b/tidy3d/components/tcad/simulation/heat_charge.py @@ -35,7 +35,11 @@ from tidy3d.components.medium import Medium from tidy3d.components.scene import Scene from tidy3d.components.spice.sources.dc import DCVoltageSource -from tidy3d.components.spice.types import ElectricalAnalysisType +from tidy3d.components.spice.types import ( + ElectricalAnalysisType, + IsothermalSteadyChargeDCAnalysis, + SteadyChargeDCAnalysis, +) from tidy3d.components.structure import Structure from tidy3d.components.tcad.analysis.heat_simulation_type import UnsteadyHeatAnalysis from tidy3d.components.tcad.boundary.specification import ( @@ -951,6 +955,44 @@ def check_transient_heat(cls, values): ) return values + @pd.root_validator(skip_on_failure=True) + def check_non_isothermal_is_possible(cls, values): + """Make sure that when a non-isothermal case is defined the structrures + have both electrical and thermal properties.""" + + analysis_spec = values.get("analysis_spec") + if isinstance(analysis_spec, SteadyChargeDCAnalysis) and not isinstance( + analysis_spec, IsothermalSteadyChargeDCAnalysis + ): + has_heat = False + has_elec = False + structures = values.get("structures") + for struct in structures: + if isinstance(struct.medium, MultiPhysicsMedium): + if struct.medium.heat is not None: + if isinstance(struct.medium.heat, SolidMedium): + has_heat = True + if struct.medium.charge is not None: + if isinstance(struct.medium.charge, SemiconductorMedium): + has_elec = True + + if not has_heat and has_elec: + raise SetupError( + "The current simulation is defined as non-isothermal but no solid " + "materials with heat properties have been defined. " + ) + elif not has_elec and has_heat: + raise SetupError( + "The current simulation is defined as non-isothermal but no " + "semiconductor materials have been defined. " + ) + elif not has_heat and not has_elec: + raise SetupError( + "The current simulation is defined as non-isothermal but no " + "solid or semiconductor materials have been defined. " + ) + return values + @equal_aspect @add_ax_if_none def plot_property( @@ -1735,7 +1777,8 @@ def _get_simulation_types(self) -> list[TCADAnalysisTypes]: # NOTE: for the time being, if a simulation has SemiconductorMedium # then we consider it of being a 'TCADAnalysisTypes.CHARGE' - if isinstance(self.analysis_spec, ElectricalAnalysisType): + ChargeTypes = (SteadyChargeDCAnalysis, IsothermalSteadyChargeDCAnalysis) + if isinstance(self.analysis_spec, ChargeTypes): if self._check_if_semiconductor_present(self.structures): return [TCADAnalysisTypes.CHARGE] diff --git a/tidy3d/components/tcad/types.py b/tidy3d/components/tcad/types.py index 002bce16bf..90648b4e55 100644 --- a/tidy3d/components/tcad/types.py +++ b/tidy3d/components/tcad/types.py @@ -5,6 +5,17 @@ from tidy3d.components.tcad.bandgap import SlotboomBandGapNarrowing from tidy3d.components.tcad.boundary.charge import CurrentBC, InsulatingBC, VoltageBC from tidy3d.components.tcad.boundary.heat import ConvectionBC, HeatFluxBC, TemperatureBC +from tidy3d.components.tcad.effective_DOS import ( + ConstantEffectiveDOS, + DualValleyEffectiveDOS, + IsotropicEffectiveDOS, + MultiValleyEffectiveDOS, +) +from tidy3d.components.tcad.bandgap_energy import ( + ConstantEnergyBandGap, + QuadraticEnergyBandGap, + VarshniEnergyBandGap, +) from tidy3d.components.tcad.generation_recombination import ( AugerRecombination, RadiativeRecombination, @@ -22,6 +33,10 @@ from tidy3d.components.tcad.source.heat import HeatSource, UniformHeatSource from tidy3d.components.types import Union +EffectiveDOSModelType = Union[ + ConstantEffectiveDOS, IsotropicEffectiveDOS, MultiValleyEffectiveDOS, DualValleyEffectiveDOS +] +EnergyBandGapModelType = Union[ConstantEnergyBandGap, QuadraticEnergyBandGap, VarshniEnergyBandGap] MobilityModelType = Union[CaugheyThomasMobility, ConstantMobilityModel] RecombinationModelType = Union[ AugerRecombination, RadiativeRecombination, ShockleyReedHallRecombination diff --git a/tidy3d/material_library/material_library.py b/tidy3d/material_library/material_library.py index ef962d191d..75f5f8523d 100644 --- a/tidy3d/material_library/material_library.py +++ b/tidy3d/material_library/material_library.py @@ -14,10 +14,12 @@ from tidy3d.components.tcad.types import ( AugerRecombination, CaugheyThomasMobility, + ConstantEffectiveDOS, RadiativeRecombination, ShockleyReedHallRecombination, SlotboomBandGapNarrowing, ) +from tidy3d.components.tcad.bandgap_energy import ConstantEnergyBandGap from tidy3d.components.types import Axis from tidy3d.exceptions import SetupError from tidy3d.log import log @@ -2026,9 +2028,9 @@ def medium(self, optical_axis: Axis): optical=cSi_Green2008.medium, charge=SemiconductorMedium( permittivity=11.7, - N_c=2.86e19, - N_v=3.1e19, - E_g=1.11, + N_c=ConstantEffectiveDOS(N=2.86e19), + N_v=ConstantEffectiveDOS(N=3.1e19), + E_g=ConstantEnergyBandGap(eg=1.11), mobility_n=CaugheyThomasMobility( mu_min=52.2, mu=1471.0,