3131
3232import numpy as np
3333import numpy .typing as npt
34- from numpy .lib .scimath import sqrt
34+ from numpy .lib .scimath import sqrt , power
3535
3636from .dispersions .base_dispersion import BaseDispersion
3737
@@ -313,7 +313,7 @@ class VCAMaterial(MixtureMaterial):
313313 * :math:`\varepsilon_\text{eff}` is the effective permittivity of host/mixture material,
314314 * :math:`\varepsilon_h` is the permittivity of the host mixture material,
315315 * :math:`\varepsilon_g` is the permittivity of the guest mixture material and
316- * :math:`f` is the volume fraction of material a in the guest material.
316+ * :math:`f` is the volume fraction of the guest in the host material.
317317 """
318318
319319 def get_tensor_fraction (self , lbda : npt .ArrayLike , fraction : float ) -> npt .NDArray :
@@ -346,7 +346,7 @@ class LooyengaEMA(MixtureMaterial):
346346 * :math:`\varepsilon_\text{eff}` is the effective permittivity of host/mixture material,
347347 * :math:`\varepsilon_h` is the permittivity of the host mixture material,
348348 * :math:`\varepsilon_g` is the permittivity of the guest mixture material and
349- * :math:`f` is the volume fraction of material a in the guest material.
349+ * :math:`f` is the volume fraction of the guest in the host material.
350350
351351 References:
352352 Looyenga, H. (1965). Physica, 31(3), 401–406.
@@ -384,7 +384,7 @@ class MaxwellGarnettEMA(MixtureMaterial):
384384 * :math:`\varepsilon_\text{eff}` is the effective permittivity of host/mixture material,
385385 * :math:`\varepsilon_h` is the permittivity of the host mixture material,
386386 * :math:`\varepsilon_g` is the permittivity of the guest mixture material and
387- * :math:`f` is the volume fraction of material a in the guest material.
387+ * :math:`f` is the volume fraction of the guest in the host material.
388388 """
389389
390390 def get_tensor_fraction (self , lbda : npt .ArrayLike , fraction : float ) -> npt .NDArray :
@@ -420,22 +420,22 @@ def get_tensor_fraction(self, lbda: npt.ArrayLike, fraction: float) -> npt.NDArr
420420
421421class BruggemanEMA (MixtureMaterial ):
422422 r"""Mixture Material approximated with the Bruggeman formula
423- for spherical inclusions.
423+ for isotropic spherical inclusions.
424424
425425 Returns one of the two analytical solutions to this quadratic equation:
426426
427427 .. math::
428428 2 \varepsilon_\text{eff}^2 +
429- \varepsilon_\text{eff} [(3f - 2) \varepsilon_a
430- + (1 - 3f)\varepsilon_b] - \varepsilon_a \cdot \varepsilon_b = 0
429+ [(3f - 2) \varepsilon_a + (1 - 3f)\varepsilon_b] \varepsilon_\text{eff}
430+ - \varepsilon_a \cdot \varepsilon_b = 0
431431
432432 where :math:`\varepsilon_\text{eff}` is the effective permittivity of host/mixture material,
433433 :math:`\varepsilon_a` is the permittivity of the first mixture material,
434434 :math:`\varepsilon_b` is the permittivity of the second mixture material
435435 and :math:`f` is the volume fraction of material a in the material b.
436436
437437 References:
438- * Josef Humlicek in Ellipsometry at the Nanoscale, Springer-Verlag Berlin Heidelberg, 2013
438+ * Ph.J. Rouseel; J. Vanhellemont; H.E. Maes. (1993) Thin Solid Films, 234, 423-427
439439 """
440440
441441 def get_tensor_fraction (self , lbda : npt .ArrayLike , fraction : float ) -> npt .NDArray :
@@ -453,106 +453,17 @@ def get_tensor_fraction(self, lbda: npt.ArrayLike, fraction: float) -> npt.NDArr
453453 e_g = self .guest_material .get_tensor (lbda )
454454 f = fraction
455455
456- # fmt: off
457- root1 = 3 * e_g * f / 4 - e_g / 4 - 3 * e_h * f / 4 + e_h / 2 - sqrt (
458- 9 * e_g ** 2 * f ** 2 - 6 * e_g ** 2 * f + e_g ** 2 - 18 * e_g * e_h * f ** 2 +
459- 18 * e_g * e_h * f + 4 * e_g * e_h + 9 * e_h ** 2 * f ** 2 - 12 * e_h ** 2 * f + 4 * e_h ** 2 )/ 4
460- root2 = 3 * e_g * f / 4 - e_g / 4 - 3 * e_h * f / 4 + e_h / 2 + sqrt (
461- 9 * e_g ** 2 * f ** 2 - 6 * e_g ** 2 * f + e_g ** 2 - 18 * e_g * e_h * f ** 2 +
462- 18 * e_g * e_h * f + 4 * e_g * e_h + 9 * e_h ** 2 * f ** 2 - 12 * e_h ** 2 * f + 4 * e_h ** 2 )/ 4
463- # fmt: on
464-
465- return self .jansson_algorithm (e_h , e_g , root1 , root2 )
466-
467- @staticmethod
468- def jansson_algorithm (
469- e_h : npt .ArrayLike ,
470- e_g : npt .ArrayLike ,
471- root1 : npt .ArrayLike ,
472- root2 : npt .ArrayLike ,
473- ):
474- """Use the algorithm proposed by Jansson and Arwin to find the correct root of
475- the solution to the Bruggeman formula.
476-
477- References:
478- * Jansson R. , Arwin H. (1994) Optics Communications, 106, 4-6, 133-138,
479- https://doi.org/10.1016/0030-4018(94)90309-3.
456+ mask_equal = np .nonzero (np .equal (e_h , e_g ))
457+ mask_different = np .nonzero (np .not_equal (e_h , e_g ))
480458
481- Args:
482- e_h (npt.ArrayLike): Dielectric tensor of host material.
483- e_g (npt.ArrayLike): Dielectric tensor of host material.
484- root1 (npt.ArrayLike): Solution 1 for dielectric tensor of mixture.
485- root2 (npt.ArrayLike): Solution 2 for dielectric tensor of mixture.
486-
487- Returns:
488- npt.NDArray: Physically correct permittivity tensor for the mixture.
489- """
490-
491- # Catch calculation warnings
492- old_settings = np .geterr ()
493- np .seterr (invalid = "ignore" , divide = "ignore" )
494-
495- z0 = (
496- e_h
497- * e_g
498- * (np .conj (e_h ) - np .conj (e_g ))
499- / (np .conj (e_h ) * e_g - e_h * np .conj (e_g ))
500- )
501- scaling_factor = np .conj (e_g - e_h ) / np .abs (e_g - e_h )
502-
503- # Reset numpy settings
504- np .seterr (** old_settings )
459+ p = sqrt (e_h [mask_different ]) / sqrt (e_g [mask_different ])
460+ b = 0.25 * ((3 * f - 1 ) * (1 / p - p ) + p )
461+ z = b + sqrt (power (b , 2 ) + 0.5 )
505462
506- # Find indices for the three cases
507- mask_equal = np .nonzero (np .isnan (z0 .real ))
508- mask_straight = np .nonzero (np .isinf (z0 .real ))
509- mask_general = np .nonzero (
510- np .logical_not (np .logical_or (np .isnan (z0 .real ), np .isinf (z0 .real )))
463+ e_mix = np .full_like (e_h , np .nan )
464+ e_mix [mask_equal ] = e_h [mask_equal ]
465+ e_mix [mask_different ] = (
466+ z * sqrt (e_h [mask_different ]) * sqrt (e_g [mask_different ])
511467 )
512468
513- def check_straight_line ():
514- def w (z ):
515- return np .where (
516- z0 [mask_straight ].real == np .inf ,
517- z [mask_straight ] * scaling_factor [mask_straight ],
518- - 1 * z [mask_straight ] * scaling_factor [mask_straight ],
519- )
520-
521- return np .where (
522- np .logical_and (
523- w (e_h ).real < w (root1 ).real , w (root1 ).real < w (e_g ).real
524- ),
525- root1 [mask_straight ],
526- root2 [mask_straight ],
527- )
528-
529- def check_general_case ():
530- def zeta (z ):
531- return (
532- (z [mask_general ] - z0 [mask_general ])
533- / np .abs (z0 [mask_general ])
534- * scaling_factor [mask_general ]
535- )
536-
537- zeta_1 , zeta_2 , zeta_root1 = np .where (
538- np .logical_and (zeta (e_h ).imag > 0 , zeta (e_g ).imag > 0 ),
539- (zeta (e_h ), zeta (e_g ), zeta (root1 )),
540- (- zeta (e_h ), - zeta (e_g ), - zeta (root1 )),
541- )
542-
543- return np .where (
544- np .logical_and (
545- np .abs (zeta_root1 <= 1 ),
546- zeta_root1 .imag >= np .imag ((zeta_1 + zeta_2 ) / 2 ),
547- ),
548- root1 [mask_general ],
549- root2 [mask_general ],
550- )
551-
552- # Create new array and write correct values into it
553- correct_root = np .full_like (e_h , np .nan )
554- correct_root [mask_equal ] = e_h [mask_equal ]
555- correct_root [mask_straight ] = check_straight_line ()
556- correct_root [mask_general ] = check_general_case ()
557-
558- return correct_root
469+ return e_mix
0 commit comments