Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Minor fixes to docstrings and import error messages #799

Merged
merged 2 commits into from
Mar 27, 2023
Merged
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
16 changes: 14 additions & 2 deletions tidy3d/components/geometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -3402,15 +3402,20 @@ class TriangleMesh(Geometry, ABC):
description="Surface mesh data.",
)

@pydantic.root_validator(pre=True)
def _check_trimesh_library(cls, values):
@classmethod
def _check_trimesh_library(cls):
"""Check if the trimesh package is imported."""
if not TRIMESH_AVAILABLE:
raise ImportError(
"The package 'trimesh' was not found. Please install the 'trimesh' "
"dependencies to use 'TriangleMesh'. For example: "
"pip install -r requirements/trimesh.txt."
)

@pydantic.root_validator(pre=True)
def _validate_trimesh_library(cls, values):
"""Check if the trimesh package is imported as a validator."""
cls._check_trimesh_library()
return values

@pydantic.validator("mesh_dataset", pre=True, always=True)
Expand Down Expand Up @@ -3492,6 +3497,8 @@ def process_single(mesh: trimesh.Trimesh) -> TriangleMesh:
mesh.apply_translation(origin)
return cls.from_trimesh(mesh)

cls._check_trimesh_library()

scene = trimesh.load(filename, **kwargs)
meshes = []
if isinstance(scene, trimesh.Trimesh):
Expand Down Expand Up @@ -3587,6 +3594,9 @@ def from_vertices_faces(cls, vertices: np.ndarray, faces: np.ndarray) -> Triangl
The custom surface mesh geometry given by the vertices and faces provided.

"""

cls._check_trimesh_library()

vertices = np.array(vertices)
faces = np.array(faces)
if len(vertices.shape) != 2 or vertices.shape[1] != 3:
Expand All @@ -3600,6 +3610,8 @@ def from_vertices_faces(cls, vertices: np.ndarray, faces: np.ndarray) -> Triangl
@classmethod
def _triangles_to_trimesh(cls, triangles: np.ndarray) -> trimesh.Trimesh:
"""Convert an (N, 3, 3) numpy array of triangles to a ``trimesh.Trimesh``."""

cls._check_trimesh_library()
return trimesh.Trimesh(**trimesh.triangles.to_kwargs(triangles))

@cached_property
Expand Down
28 changes: 17 additions & 11 deletions tidy3d/components/medium.py
Original file line number Diff line number Diff line change
Expand Up @@ -857,7 +857,9 @@ def from_medium(cls, medium: Medium) -> "PoleResidue":
The pole residue equivalent.
"""
poles = [(0, medium.conductivity / (2 * EPSILON_0))]
return PoleResidue(eps_inf=medium.permittivity, poles=poles)
return PoleResidue(
eps_inf=medium.permittivity, poles=poles, frequency_range=medium.frequency_range
)

def to_medium(self) -> Medium:
"""Convert to a :class:`.Medium`.
Expand All @@ -875,7 +877,11 @@ def to_medium(self) -> Medium:
raise ValidationError("Cannot convert dispersive 'PoleResidue' to 'Medium'.")
res += (c + np.conj(c)) / 2
sigma = res * 2 * EPSILON_0
return Medium(permittivity=self.eps_inf, conductivity=np.real(sigma))
return Medium(
permittivity=self.eps_inf,
conductivity=np.real(sigma),
frequency_range=self.frequency_range,
)


class Sellmeier(DispersiveMedium):
Expand Down Expand Up @@ -1400,7 +1406,7 @@ def get_background(comp: Axis) -> PoleResidue:
ax_coord=media_bg[axis], plane_coords=media_fg_weighted, axis=axis
)
media_3d_kwargs = {dim + dim: medium for dim, medium in zip("xyz", media_3d)}
return AnisotropicMedium(**media_3d_kwargs)
return AnisotropicMedium(**media_3d_kwargs, frequency_range=self.frequency_range)

def to_anisotropic_medium(self, axis: Axis, thickness: float) -> AnisotropicMedium:
"""Generate a 3D :class:`.AnisotropicMedium` equivalent of a given thickness.
Expand All @@ -1422,7 +1428,7 @@ def to_anisotropic_medium(self, axis: Axis, thickness: float) -> AnisotropicMedi
media_weighted = [self._weighted_avg([medium], [1 / thickness]) for medium in media]
media_3d = Geometry.unpop_axis(ax_coord=Medium(), plane_coords=media_weighted, axis=axis)
media_3d_kwargs = {dim + dim: medium for dim, medium in zip("xyz", media_3d)}
return AnisotropicMedium(**media_3d_kwargs)
return AnisotropicMedium(**media_3d_kwargs, frequency_range=self.frequency_range)

def to_pole_residue(self, thickness: float) -> PoleResidue:
"""Generate a :class:`.PoleResidue` equivalent of a given thickness.
Expand All @@ -1438,7 +1444,9 @@ def to_pole_residue(self, thickness: float) -> PoleResidue:
:class:`.PoleResidue`
The 3D equivalent pole residue model of this 2D medium.
"""
return self._weighted_avg([self.ss, self.tt], [1 / (2 * thickness), 1 / (2 * thickness)])
return self._weighted_avg(
[self.ss, self.tt], [1 / (2 * thickness), 1 / (2 * thickness)]
).updated_copy(frequency_range=self.frequency_range)

def to_medium(self, thickness: float) -> Medium:
"""Generate a :class:`.Medium` equivalent of a given thickness.
Expand All @@ -1455,9 +1463,7 @@ def to_medium(self, thickness: float) -> Medium:
:class:`.Medium`
The 3D equivalent of this 2D medium.
"""
return self._weighted_avg(
[self.ss, self.tt], [1 / (2 * thickness), 1 / (2 * thickness)]
).to_medium()
return self.to_pole_residue(thickness=thickness).to_medium()

@classmethod
def from_medium(cls, medium: Medium, thickness: float) -> Medium2D:
Expand All @@ -1477,7 +1483,7 @@ def from_medium(cls, medium: Medium, thickness: float) -> Medium2D:
The 2D equivalent of the given 3D medium.
"""
med = cls._weighted_avg([medium], [thickness])
return Medium2D(ss=med, tt=med)
return Medium2D(ss=med, tt=med, frequency_range=medium.frequency_range)

@classmethod
def from_dispersive_medium(cls, medium: DispersiveMedium, thickness: float) -> Medium2D:
Expand All @@ -1497,7 +1503,7 @@ def from_dispersive_medium(cls, medium: DispersiveMedium, thickness: float) -> M
The 2D equivalent of the given 3D medium.
"""
med = cls._weighted_avg([medium], [thickness])
return Medium2D(ss=med, tt=med)
return Medium2D(ss=med, tt=med, frequency_range=medium.frequency_range)

@classmethod
def from_anisotropic_medium(
Expand Down Expand Up @@ -1528,7 +1534,7 @@ def from_anisotropic_medium(
for _, med in enumerate(media_plane):
media_plane_scaled.append(cls._weighted_avg([med], [thickness]))
media_kwargs = {dim + dim: medium for dim, medium in zip("st", media_plane_scaled)}
return Medium2D(**media_kwargs)
return Medium2D(**media_kwargs, frequency_range=medium.frequency_range)

@ensure_freq_in_range
def eps_model(self, frequency: float) -> complex:
Expand Down
29 changes: 23 additions & 6 deletions tidy3d/material_library/parametric_materials.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,19 @@
from typing import List, Tuple
import pydantic as pd
import numpy as np
from scipy import integrate

from ..components.medium import PoleResidue, Medium2D, Drude
from ..components.base import Tidy3dBaseModel
from ..constants import EPSILON_0, Q_e, HBAR, K_B, ELECTRON_VOLT, KELVIN

try:
from scipy import integrate

INTEGRATE_AVAILABLE = True
except ImportError:
INTEGRATE_AVAILABLE = False


# default values of the physical parameters for graphene
# scattering rate in eV
GRAPHENE_DEF_GAMMA = 0.00041
Expand Down Expand Up @@ -45,12 +52,15 @@ def medium(self) -> Medium2D:


class Graphene(ParametricVariantItem2D):
"""Surface conductivity model for graphene, including intraband and interband terms,
as described in
"""Parametric surface conductivity model for graphene.

George W. Hanson, "Dyadic Green's Functions for an Anisotropic,
Non-Local Model of Biased Graphene," IEEE Trans. Antennas Propag.
56, 3, 747-757 (2008).
Note
----
The model contains intraband and interband terms, as described in::

George W. Hanson, "Dyadic Green's Functions for an Anisotropic,
Non-Local Model of Biased Graphene," IEEE Trans. Antennas Propag.
56, 3, 747-757 (2008).

Example
-------
Expand Down Expand Up @@ -215,6 +225,13 @@ def integrand(E: float, omega: float) -> float:
"""Integrand for interband term."""
return (fermi_g(E * HBAR) - fermi_g(HBAR * omega / 2)) / (omega**2 - 4 * E**2)

if not INTEGRATE_AVAILABLE:
raise ImportError(
"The package 'scipy' was not found. Please install the 'core' "
"dependencies to calculate the interband term of graphene. For example: "
"pip install -r requirements/core.txt"
)

omegas = 2 * np.pi * np.array(freqs)
sigma = np.zeros(len(omegas), dtype=complex)
integration_min = GRAPHENE_INT_MIN
Expand Down