gplugins 2.1.0__tar.gz → 2.1.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (258) hide show
  1. {gplugins-2.1.0 → gplugins-2.1.2}/PKG-INFO +20 -12
  2. {gplugins-2.1.0 → gplugins-2.1.2}/README.md +2 -1
  3. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/__init__.py +1 -1
  4. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/common/utils/tests/test_get_component_with_new_port_layers.py +1 -1
  5. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/devsim/doping.py +1 -1
  6. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/devsim/get_simulation.py +1 -1
  7. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/elmer/get_capacitance.py +1 -1
  8. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/elmer/tests/test_elmer.py +1 -1
  9. gplugins-2.1.2/gplugins/emode/__init__.py +13 -0
  10. gplugins-2.1.2/gplugins/emode/emode.py +244 -0
  11. gplugins-2.1.2/gplugins/emode/tests/test_emode.py +218 -0
  12. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/fdtdz/get_epsilon_fdtdz.py +1 -1
  13. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/fdtdz/get_ports_fdtdz.py +1 -1
  14. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/fdtdz/get_sparameters_fdtdz.py +1 -1
  15. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/femwell/mode_solver.py +3 -0
  16. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/femwell/test_mode_solver.py +1 -1
  17. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/gmeep/get_simulation.py +1 -1
  18. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/gmeep/write_sparameters_meep_mpi.py +2 -1
  19. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/klayout/dataprep/regions.py +1 -1
  20. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/klayout/drc/check_width.py +1 -1
  21. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/klayout/drc/write_drc.py +4 -4
  22. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/klayout/tests/test_dataprep_regions.py +1 -1
  23. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/lumerical/read.py +1 -1
  24. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/lumerical/write_sparameters_lumerical.py +19 -1
  25. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/meshwell/get_meshwell_3D.py +3 -3
  26. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/meshwell/get_meshwell_cross_section.py +3 -3
  27. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/meshwell/tests/test_meshwell.py +1 -1
  28. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/palace/get_capacitance.py +3 -3
  29. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/palace/get_scattering.py +3 -3
  30. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/palace/tests/test_palace.py +1 -1
  31. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/sentaurus/sde.py +3 -3
  32. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/sentaurus/sprocess.py +2 -2
  33. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/tidy3d/component.py +10 -3
  34. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/tidy3d/materials.py +10 -3
  35. gplugins-2.1.2/gplugins/vlsir/tests/__init__.py +0 -0
  36. gplugins-2.1.2/gplugins/vlsir/tests/conftest.py +3 -0
  37. {gplugins-2.1.0 → gplugins-2.1.2}/pyproject.toml +18 -13
  38. {gplugins-2.1.0 → gplugins-2.1.2}/LICENSE +0 -0
  39. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/common/__init__.py +0 -0
  40. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/common/base_models/__init__.py +0 -0
  41. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/common/base_models/component.py +0 -0
  42. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/common/base_models/simulation.py +0 -0
  43. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/common/config.py +0 -0
  44. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/common/types.py +0 -0
  45. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/common/utils/__init__.py +0 -0
  46. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/common/utils/add_simulation_markers.py +0 -0
  47. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/common/utils/async_helpers.py +0 -0
  48. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/common/utils/cache.py +0 -0
  49. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/common/utils/convert_sparameters.py +0 -0
  50. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/common/utils/disable_print.py +0 -0
  51. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/common/utils/geometry.py +0 -0
  52. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/common/utils/get_capacitance.py +0 -0
  53. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/common/utils/get_component_with_net_layers.py +0 -0
  54. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/common/utils/get_effective_indices.py +0 -0
  55. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/common/utils/get_scattering.py +0 -0
  56. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/common/utils/get_sparameters_path.py +0 -0
  57. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/common/utils/optical_constants.py +0 -0
  58. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/common/utils/parse_layer_stack.py +0 -0
  59. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/common/utils/plot.py +0 -0
  60. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/common/utils/plot_csv.py +0 -0
  61. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/common/utils/port_symmetries.py +0 -0
  62. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/devsim/__init__.py +0 -0
  63. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/devsim/get_simulation_xsection.py +0 -0
  64. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/devsim/get_solver.py +0 -0
  65. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/devsim/test_devsim.py +0 -0
  66. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/elmer/__init__.py +0 -0
  67. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/elmer/electrostatic.sif.j2 +0 -0
  68. {gplugins-2.1.0/gplugins/klayout → gplugins-2.1.2/gplugins/emode/tests}/__init__.py +0 -0
  69. {gplugins-2.1.0/gplugins/klayout → gplugins-2.1.2/gplugins/emode}/tests/conftest.py +0 -0
  70. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/fdtdz/__init__.py +0 -0
  71. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/femwell/__init__.py +0 -0
  72. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/femwell/solve_thermal.py +0 -0
  73. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/gfviz/Makefile +0 -0
  74. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/gfviz/__init__.py +0 -0
  75. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/gfviz/b64.py +0 -0
  76. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/gfviz/gfviz.py +0 -0
  77. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/gfviz/netlist.py +0 -0
  78. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/gfviz/serve.py +0 -0
  79. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/gfviz/static/b64.js +0 -0
  80. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/gfviz/static/schemedit.js +0 -0
  81. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/gfviz/static/schemedit.wasm +0 -0
  82. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/gfviz/templates/example.json +0 -0
  83. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/gfviz/templates/index.html +0 -0
  84. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/gmeep/__init__.py +0 -0
  85. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/gmeep/get_material.py +0 -0
  86. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/gmeep/get_meep_geometry.py +0 -0
  87. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/gmeep/get_port_eigenmode.py +0 -0
  88. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/gmeep/get_simulation_grating_farfield.py +0 -0
  89. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/gmeep/get_simulation_grating_fiber.py +0 -0
  90. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/gmeep/meep_adjoint_optimization.py +0 -0
  91. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/gmeep/test_eigenmode.py +0 -0
  92. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/gmeep/test_materials.py +0 -0
  93. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/gmeep/test_sparameterNxN/test_sparameterNxN_crossing.csv +0 -0
  94. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/gmeep/test_sparameterNxN/test_sparameterNxN_straight.csv +0 -0
  95. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/gmeep/test_write_sparameters_meep/test_sparameterNxN_crossing.csv +0 -0
  96. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/gmeep/test_write_sparameters_meep/test_sparameterNxN_straight.csv +0 -0
  97. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/gmeep/test_write_sparameters_meep/test_sparameterNxN_symmetries_straight.csv +0 -0
  98. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/gmeep/test_write_sparameters_meep/test_sparameter_straight_mpi.csv +0 -0
  99. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/gmeep/test_write_sparameters_meep/test_sparameter_straight_mpi_pool.csv +0 -0
  100. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/gmeep/test_write_sparameters_meep/test_sparameters_crossing_symmetric.csv +0 -0
  101. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/gmeep/test_write_sparameters_meep/test_sparameters_straight.csv +0 -0
  102. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/gmeep/test_write_sparameters_meep/test_sparameters_straight_batch.csv +0 -0
  103. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/gmeep/test_write_sparameters_meep/test_sparameters_straight_mpi.csv +0 -0
  104. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/gmeep/test_write_sparameters_meep/test_sparameters_straight_mpi_pool.csv +0 -0
  105. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/gmeep/test_write_sparameters_meep/test_sparameters_straight_symmetric.csv +0 -0
  106. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/gmeep/test_write_sparameters_meep.py +0 -0
  107. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/gmeep/write_sparameters_grating.py +0 -0
  108. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/gmeep/write_sparameters_meep.py +0 -0
  109. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/gmeep/write_sparameters_meep_batch.py +0 -0
  110. {gplugins-2.1.0/gplugins/klayout/drc → gplugins-2.1.2/gplugins/klayout}/__init__.py +0 -0
  111. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/klayout/dataprep/__init__.py +0 -0
  112. {gplugins-2.1.0/gplugins/klayout/tests → gplugins-2.1.2/gplugins/klayout/drc}/__init__.py +0 -0
  113. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/klayout/drc/check_duplicated_cells.py +0 -0
  114. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/klayout/drc/check_exclusion.py +0 -0
  115. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/klayout/drc/check_inclusion.py +0 -0
  116. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/klayout/drc/check_space.py +0 -0
  117. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/klayout/drc/count_drc.py +0 -0
  118. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/klayout/drc/samples/drc_errors.py +0 -0
  119. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/klayout/get_density.py +0 -0
  120. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/klayout/get_netlist.py +0 -0
  121. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/klayout/netlist_graph.py +0 -0
  122. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/klayout/netlist_spice_reader.py +0 -0
  123. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/klayout/plot_nets.py +0 -0
  124. {gplugins-2.1.0/gplugins/materials → gplugins-2.1.2/gplugins/klayout/tests}/__init__.py +0 -0
  125. {gplugins-2.1.0/gplugins/palace → gplugins-2.1.2/gplugins/klayout}/tests/conftest.py +0 -0
  126. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/klayout/tests/test_density.py +0 -0
  127. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/klayout/tests/test_drc_exclusion.py +0 -0
  128. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/klayout/tests/test_drc_inclusion.py +0 -0
  129. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/klayout/tests/test_drc_space.py +0 -0
  130. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/klayout/tests/test_drc_width.py +0 -0
  131. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/klayout/tests/test_global_density.py +0 -0
  132. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/klayout/tests/test_netlist_spice_reader.py +0 -0
  133. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/klayout/tests/test_plot_nets.py +0 -0
  134. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/lumerical/README.md +0 -0
  135. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/lumerical/__init__.py +0 -0
  136. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/lumerical/interconnect.py +0 -0
  137. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/lumerical/mapping_ubcpdk.yml +0 -0
  138. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/lumerical/settings.py +0 -0
  139. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/lumerical/tests/test_lumerical_read_sparameters.py +0 -0
  140. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/lumerical/tests/test_netlist.py +0 -0
  141. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/lumerical/tests/test_netlist_get_routes.py +0 -0
  142. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/lumerical/write_sparameters_lumerical_components.py +0 -0
  143. {gplugins-2.1.0/gplugins/materials/optical → gplugins-2.1.2/gplugins/materials}/__init__.py +0 -0
  144. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/materials/inorganic.py +0 -0
  145. {gplugins-2.1.0/gplugins/materials/semiconductor → gplugins-2.1.2/gplugins/materials/optical}/__init__.py +0 -0
  146. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/materials/optical/optical_mat.py +0 -0
  147. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/materials/optical/refractive_index_info.py +0 -0
  148. {gplugins-2.1.0/gplugins/palace/tests → gplugins-2.1.2/gplugins/materials/semiconductor}/__init__.py +0 -0
  149. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/materials/semiconductor/semiconductor_mat.py +0 -0
  150. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/meow/__init__.py +0 -0
  151. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/meow/meow_eme.py +0 -0
  152. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/meow/test_meow_simulation.py +0 -0
  153. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/meshwell/__init__.py +0 -0
  154. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/modes/__init__.py +0 -0
  155. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/modes/coupler.py +0 -0
  156. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/modes/find_coupling_vs_gap.py +0 -0
  157. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/modes/find_mode_dispersion.py +0 -0
  158. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/modes/find_modes.py +0 -0
  159. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/modes/find_modes_cross_section.py +0 -0
  160. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/modes/find_neff_ng_dw_dh.py +0 -0
  161. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/modes/find_neff_vs_width.py +0 -0
  162. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/modes/get_mode_solver_coupler.py +0 -0
  163. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/modes/get_mode_solver_cross_section.py +0 -0
  164. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/modes/get_mode_solver_rib.py +0 -0
  165. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/modes/modes/neff_vs_width_nitride.csv +0 -0
  166. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/modes/modes/neff_vs_width_rib.csv +0 -0
  167. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/modes/modes/neff_vs_width_strip.csv +0 -0
  168. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/modes/neff_convergence_test.py +0 -0
  169. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/modes/neff_vs_width.csv +0 -0
  170. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/modes/overlap.py +0 -0
  171. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/modes/tests/test_dw_dh/test_dw_dh.csv +0 -0
  172. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/modes/tests/test_dw_dh/test_dw_dh.obtained.csv +0 -0
  173. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/modes/tests/test_dw_dh/test_dw_dh_dispersion.csv +0 -0
  174. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/modes/tests/test_dw_dh.py +0 -0
  175. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/modes/tests/test_find_modes.py +0 -0
  176. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/modes/tests/test_find_modes_dispersion.py +0 -0
  177. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/modes/tests/test_neff_vs_width/test_neff_vs_width.csv +0 -0
  178. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/modes/tests/test_neff_vs_width/test_neff_vs_width.obtained.csv +0 -0
  179. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/modes/tests/test_neff_vs_width.py +0 -0
  180. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/modes/types.py +0 -0
  181. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/modes/waveguide.py +0 -0
  182. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/palace/__init__.py +0 -0
  183. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/palace/driven.json +0 -0
  184. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/palace/electrostatic.json +0 -0
  185. {gplugins-2.1.0/gplugins/process → gplugins-2.1.2/gplugins/palace/tests}/__init__.py +0 -0
  186. {gplugins-2.1.0/gplugins/sax → gplugins-2.1.2/gplugins/palace}/tests/conftest.py +0 -0
  187. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/path_length_analysis/__init__.py +0 -0
  188. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/path_length_analysis/path_length_analysis.py +0 -0
  189. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/path_length_analysis/path_length_analysis_from_gds.py +0 -0
  190. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/path_length_analysis/test_pathlength_extraction.py +0 -0
  191. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/path_length_analysis/test_pathlength_extraction_from_gds.py +0 -0
  192. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/path_length_analysis/test_pathlength_extraction_utils.py +0 -0
  193. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/path_length_analysis/utils.py +0 -0
  194. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/photonic_circuit_models/__init__.py +0 -0
  195. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/photonic_circuit_models/coupler.py +0 -0
  196. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/photonic_circuit_models/fsr.py +0 -0
  197. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/photonic_circuit_models/heater.py +0 -0
  198. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/photonic_circuit_models/mzi.py +0 -0
  199. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/photonic_circuit_models/ring.py +0 -0
  200. {gplugins-2.1.0/gplugins/sax/tests → gplugins-2.1.2/gplugins/process}/__init__.py +0 -0
  201. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/process/diffusion.py +0 -0
  202. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/process/implant_tables.py +0 -0
  203. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/process/pysrim.py +0 -0
  204. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/process/silicon.py +0 -0
  205. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/process/skew/antimony_si_skew.csv +0 -0
  206. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/process/skew/arsenic_si_skew.csv +0 -0
  207. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/process/skew/boron_si_skew.csv +0 -0
  208. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/process/skew/phosphorus_si_skew.csv +0 -0
  209. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/sax/__init__.py +0 -0
  210. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/sax/interpolators.py +0 -0
  211. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/sax/mlp.py +0 -0
  212. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/sax/models.py +0 -0
  213. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/sax/parameter.py +0 -0
  214. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/sax/plot_model.py +0 -0
  215. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/sax/read.py +0 -0
  216. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/sax/strategy.py +0 -0
  217. {gplugins-2.1.0/gplugins/spice → gplugins-2.1.2/gplugins/sax/tests}/__init__.py +0 -0
  218. {gplugins-2.1.0/gplugins/tidy3d → gplugins-2.1.2/gplugins/sax}/tests/conftest.py +0 -0
  219. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/sax/tests/test_mzi.py +0 -0
  220. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/sax/tests/test_mzi_lattice/test_mzi_lattice.obtained.yml +0 -0
  221. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/sax/tests/test_mzi_lattice/test_mzi_lattice.yml +0 -0
  222. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/sax/tests/test_mzi_lattice.py +0 -0
  223. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/sax/tests/test_parameters.py +0 -0
  224. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/sentaurus/mask_sde.py +0 -0
  225. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/sentaurus/mask_sprocess.py +0 -0
  226. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/sentaurus/sdevice.py +0 -0
  227. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/sentaurus/svisual.py +0 -0
  228. {gplugins-2.1.0/gplugins/spice/tests → gplugins-2.1.2/gplugins/spice}/__init__.py +0 -0
  229. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/spice/spice_to_yaml.py +0 -0
  230. {gplugins-2.1.0/gplugins/tidy3d → gplugins-2.1.2/gplugins/spice}/tests/__init__.py +0 -0
  231. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/spice/tests/test_interconnect.py +0 -0
  232. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/tidy3d/__init__.py +0 -0
  233. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/tidy3d/get_results.py +0 -0
  234. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/tidy3d/get_simulation_grating_coupler.py +0 -0
  235. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/tidy3d/modes.py +0 -0
  236. {gplugins-2.1.0/gplugins/vlsir → gplugins-2.1.2/gplugins/tidy3d}/tests/__init__.py +0 -0
  237. {gplugins-2.1.0/gplugins/vlsir → gplugins-2.1.2/gplugins/tidy3d}/tests/conftest.py +0 -0
  238. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/tidy3d/tests/test_component_modeler.py +0 -0
  239. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/tidy3d/tests/test_materials.py +0 -0
  240. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/tidy3d/tests/test_modes/test_sweep_width.csv +0 -0
  241. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/tidy3d/tests/test_modes/test_sweep_width.obtained.csv +0 -0
  242. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/tidy3d/tests/test_modes_coupler.py +0 -0
  243. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/tidy3d/tests/test_modes_waveguide.py +0 -0
  244. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/tidy3d/tests/test_plot_simulation_grating_coupler.py +0 -0
  245. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/tidy3d/tests/test_write_sparameters.py +0 -0
  246. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/tidy3d/tests/tests_sparameters/sim_ref.yaml +0 -0
  247. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/tidy3d/tests/tests_sparameters/test_write_sparameters.py +0 -0
  248. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/tidy3d/tests/tests_sparameters/test_write_sparameters_grating_coupler.py +0 -0
  249. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/tidy3d/types.py +0 -0
  250. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/tidy3d/util.py +0 -0
  251. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/tidy3d/write_sparameters_grating_coupler.py +0 -0
  252. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/typings.py +0 -0
  253. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/vlsir/__init__.py +0 -0
  254. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/vlsir/export_netlist.py +0 -0
  255. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/vlsir/tests/resources/pads_correct.cir +0 -0
  256. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/vlsir/tests/resources/pads_correct.scs +0 -0
  257. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/vlsir/tests/resources/pads_correct.sp +0 -0
  258. {gplugins-2.1.0 → gplugins-2.1.2}/gplugins/vlsir/tests/test_vlsir.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: gplugins
3
- Version: 2.1.0
3
+ Version: 2.1.2
4
4
  Summary: gdsfactory plugins
5
5
  Keywords: python
6
6
  Author-email: gdsfactory <contact@gdsfactory.com>
@@ -17,23 +17,28 @@ Requires-Dist: tqdm
17
17
  Requires-Dist: numpy>=2.0,<3
18
18
  Requires-Dist: xarray==2026.4.0
19
19
  Requires-Dist: hypothesis ; extra == "dev"
20
- Requires-Dist: numba ; extra == "dev"
20
+ Requires-Dist: numba>=0.60 ; extra == "dev"
21
21
  Requires-Dist: pre-commit ; extra == "dev"
22
22
  Requires-Dist: pyswarms ; extra == "dev"
23
23
  Requires-Dist: pytest ; extra == "dev"
24
24
  Requires-Dist: pytest-cov ; extra == "dev"
25
25
  Requires-Dist: pytest_regressions ; extra == "dev"
26
26
  Requires-Dist: devsim ; extra == "devsim"
27
- Requires-Dist: pyvista<=0.48.2 ; extra == "devsim"
27
+ Requires-Dist: pyvista<=0.48.4 ; extra == "devsim"
28
28
  Requires-Dist: tidy3d~=2.11.1 ; extra == "devsim"
29
+ Requires-Dist: jupyter ; extra == "docs"
29
30
  Requires-Dist: jupytext ; extra == "docs"
30
31
  Requires-Dist: matplotlib ; extra == "docs"
31
- Requires-Dist: jupyter-book>=1,<3 ; extra == "docs"
32
- Requires-Dist: pyvista[all]<=0.48.2 ; extra == "docs"
32
+ Requires-Dist: nbconvert ; extra == "docs"
33
+ Requires-Dist: mkdocstrings[python] ; extra == "docs"
34
+ Requires-Dist: mkdocs-bibtex ; extra == "docs"
35
+ Requires-Dist: pyvista[all]<=0.48.4 ; extra == "docs"
33
36
  Requires-Dist: bokeh ; extra == "docs"
34
37
  Requires-Dist: scikit-learn ; extra == "docs"
38
+ Requires-Dist: zensical>=0.0.46,<0.1.0 ; extra == "docs"
39
+ Requires-Dist: emodeconnection>=1.1.11 ; extra == "emode"
35
40
  Requires-Dist: femwell~=0.1.11 ; extra == "femwell"
36
- Requires-Dist: meshwell~=2.1.1 ; extra == "femwell"
41
+ Requires-Dist: meshwell~=2.1.2 ; extra == "femwell"
37
42
  Requires-Dist: jinja2 ; extra == "gfviz"
38
43
  Requires-Dist: fastapi ; extra == "gfviz"
39
44
  Requires-Dist: shapely ; extra == "gfviz"
@@ -49,14 +54,14 @@ Requires-Dist: mypy ; extra == "maintainer"
49
54
  Requires-Dist: tbump ; extra == "maintainer"
50
55
  Requires-Dist: towncrier ; extra == "maintainer"
51
56
  Requires-Dist: meow-sim>=0.15 ; extra == "meow"
52
- Requires-Dist: sax~=0.17.0 ; extra == "meow"
57
+ Requires-Dist: sax>=0.17,<0.19 ; extra == "meow"
53
58
  Requires-Dist: tidy3d~=2.11.1 ; extra == "meow"
54
59
  Requires-Dist: shapely ; extra == "meshwell"
55
- Requires-Dist: meshwell~=2.1.1 ; extra == "meshwell"
60
+ Requires-Dist: meshwell~=2.1.2 ; extra == "meshwell"
56
61
  Requires-Dist: bokeh ; extra == "path-length-analysis"
57
- Requires-Dist: numba ; extra == "path-length-analysis"
62
+ Requires-Dist: numba>=0.60 ; extra == "path-length-analysis"
58
63
  Requires-Dist: shapely ; extra == "path-length-analysis"
59
- Requires-Dist: sax~=0.17.0 ; extra == "sax"
64
+ Requires-Dist: sax>=0.17,<0.19 ; extra == "sax"
60
65
  Requires-Dist: bokeh ; extra == "schematic"
61
66
  Requires-Dist: ipywidgets ; extra == "schematic"
62
67
  Requires-Dist: natsort ; extra == "schematic"
@@ -68,6 +73,7 @@ Requires-Dist: vlsirtools ; extra == "vlsir"
68
73
  Provides-Extra: dev
69
74
  Provides-Extra: devsim
70
75
  Provides-Extra: docs
76
+ Provides-Extra: emode
71
77
  Provides-Extra: femwell
72
78
  Provides-Extra: gfviz
73
79
  Provides-Extra: klayout
@@ -80,8 +86,9 @@ Provides-Extra: sax
80
86
  Provides-Extra: schematic
81
87
  Provides-Extra: tidy3d
82
88
  Provides-Extra: vlsir
89
+ Import-Name: gplugins
83
90
 
84
- # gplugins 2.1.0
91
+ # gplugins 2.1.2
85
92
 
86
93
  [![docs](https://github.com/gdsfactory/gplugins/actions/workflows/pages.yml/badge.svg)](https://gdsfactory.github.io/gplugins/)
87
94
  [![PyPI](https://img.shields.io/pypi/v/gplugins)](https://pypi.org/project/gplugins/)
@@ -111,6 +118,7 @@ Run simulations with GDSFactory by installing plugins.
111
118
  - Tidy3d
112
119
  - Femwell
113
120
  - MPB
121
+ - `EMode` Finite Difference Method (FDM) mode solver with EME propagation (commercial).
114
122
  - TCAD
115
123
  - `devsim` TCAD device simulator.
116
124
  - Circuit simulations
@@ -1,4 +1,4 @@
1
- # gplugins 2.1.0
1
+ # gplugins 2.1.2
2
2
 
3
3
  [![docs](https://github.com/gdsfactory/gplugins/actions/workflows/pages.yml/badge.svg)](https://gdsfactory.github.io/gplugins/)
4
4
  [![PyPI](https://img.shields.io/pypi/v/gplugins)](https://pypi.org/project/gplugins/)
@@ -28,6 +28,7 @@ Run simulations with GDSFactory by installing plugins.
28
28
  - Tidy3d
29
29
  - Femwell
30
30
  - MPB
31
+ - `EMode` Finite Difference Method (FDM) mode solver with EME propagation (commercial).
31
32
  - TCAD
32
33
  - `devsim` TCAD device simulator.
33
34
  - Circuit simulations
@@ -1,6 +1,6 @@
1
1
  """gplugins - gdsfactory plugins."""
2
2
 
3
- __version__ = "2.1.0"
3
+ __version__ = "2.1.2"
4
4
 
5
5
  import pathlib
6
6
 
@@ -1,5 +1,5 @@
1
1
  from gdsfactory.components import straight_heater_metal
2
- from gdsfactory.generic_tech import LAYER_STACK
2
+ from gdsfactory.gpdk import LAYER_STACK
3
3
 
4
4
  from gplugins.common.utils.get_component_with_net_layers import (
5
5
  get_component_layer_stack,
@@ -1,7 +1,7 @@
1
1
  from collections.abc import Callable
2
2
 
3
3
  import numpy as np
4
- from gdsfactory.generic_tech import LAYER
4
+ from gdsfactory.gpdk import LAYER
5
5
  from gdsfactory.typings import Layer
6
6
  from pydantic import BaseModel, ConfigDict
7
7
 
@@ -230,7 +230,7 @@ def create_2Duz_simulation(
230
230
 
231
231
  if __name__ == "__main__":
232
232
  import gdsfactory as gf
233
- from gdsfactory.generic_tech import LAYER, LAYER_STACK
233
+ from gdsfactory.gpdk import LAYER, LAYER_STACK
234
234
 
235
235
  # We choose a representative subdomain of the component
236
236
  waveguide = gf.Component()
@@ -11,7 +11,7 @@ from typing import Any
11
11
 
12
12
  import gdsfactory as gf
13
13
  import gmsh
14
- from gdsfactory.generic_tech import LAYER_STACK
14
+ from gdsfactory.gpdk import LAYER_STACK
15
15
  from gdsfactory.technology import LayerStack
16
16
  from jinja2 import Environment, FileSystemLoader
17
17
  from numpy import isfinite
@@ -4,7 +4,7 @@ import gdsfactory as gf
4
4
  import pytest
5
5
  from gdsfactory.component import Component
6
6
  from gdsfactory.components import interdigital_capacitor_enclosed
7
- from gdsfactory.generic_tech import LAYER
7
+ from gdsfactory.gpdk import LAYER
8
8
  from gdsfactory.technology import LayerStack
9
9
  from gdsfactory.technology.layer_stack import LayerLevel
10
10
 
@@ -0,0 +1,13 @@
1
+ from __future__ import annotations
2
+
3
+ from gplugins.emode.emode import (
4
+ EMode,
5
+ get_emode_settings,
6
+ get_shapes_from_layer_stack,
7
+ )
8
+
9
+ __all__ = [
10
+ "EMode",
11
+ "get_emode_settings",
12
+ "get_shapes_from_layer_stack",
13
+ ]
@@ -0,0 +1,244 @@
1
+ """gdsfactory interface to EMode Photonix.
2
+
3
+ EMode is a commercial photonic simulation package with an electromagnetic
4
+ waveguide mode solver (FDM), eigenmode expansion propagation (EME), nonlinear
5
+ photonics, and thermal/electrical FEM solvers.
6
+ See https://docs.emodephotonix.com for the full API.
7
+
8
+ This module translates gdsfactory objects (:class:`~gdsfactory.CrossSection`,
9
+ :class:`~gdsfactory.technology.LayerStack`) into EMode geometry. The
10
+ translation itself is implemented as pure functions
11
+ (:func:`get_emode_settings`, :func:`get_shapes_from_layer_stack`) that only
12
+ require the pip-installable ``emodeconnection`` client. Creating an
13
+ :class:`EMode` session and running solvers requires a local EMode installation
14
+ and license.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ from collections.abc import Sequence
20
+ from typing import Any
21
+
22
+ import emodeconnection as emc
23
+ import gdsfactory as gf
24
+ from gdsfactory.technology import LayerLevel, LayerStack
25
+ from gdsfactory.typings import CrossSectionSpec, LayerSpec
26
+
27
+ UM_TO_NM = 1e3
28
+
29
+ DIMENSIONAL_SETTINGS = frozenset(
30
+ {
31
+ "wavelength",
32
+ "x_resolution",
33
+ "y_resolution",
34
+ "window_width",
35
+ "window_height",
36
+ "bend_radius",
37
+ "expansion_resolution",
38
+ "expansion_size",
39
+ "propagation_resolution",
40
+ }
41
+ )
42
+
43
+
44
+ def get_emode_settings(**settings: Any) -> dict[str, Any]:
45
+ """Convert gdsfactory-style settings to EMode units.
46
+
47
+ gdsfactory uses microns for all dimensions while EMode defaults to
48
+ nanometers. Settings named in ``DIMENSIONAL_SETTINGS`` are converted from
49
+ um to nm; all other settings pass through unchanged.
50
+
51
+ Args:
52
+ settings: keyword arguments for EMode's ``settings()`` function,
53
+ with dimensional values in um.
54
+
55
+ Returns:
56
+ The same settings with dimensional values converted to nm.
57
+ """
58
+ return {
59
+ key: value * UM_TO_NM
60
+ if key in DIMENSIONAL_SETTINGS and value is not None
61
+ else value
62
+ for key, value in settings.items()
63
+ }
64
+
65
+
66
+ def get_emode_material(material: str, materials: Sequence[str]) -> str:
67
+ """Match a gdsfactory material name to the EMode material database.
68
+
69
+ Matching is case-insensitive (e.g. gdsfactory's ``si`` matches EMode's
70
+ ``Si``). If no match is found the name is returned unchanged so EMode can
71
+ report an informative error for genuinely unknown materials.
72
+
73
+ Args:
74
+ material: gdsfactory material name.
75
+ materials: available EMode material names, from ``EMode.get('materials')``.
76
+ """
77
+ return next((m for m in materials if m.lower() == material.lower()), material)
78
+
79
+
80
+ def _layer_tuple(layer: LayerSpec) -> tuple[int, int] | None:
81
+ """Resolve a layer spec to a (layer, datatype) tuple via the active PDK.
82
+
83
+ Returns None for layers that cannot be resolved to a single GDS layer
84
+ (e.g. unresolved DerivedLayer expressions).
85
+ """
86
+ # LogicalLayer and DerivedLayer wrap the underlying spec in .layer.
87
+ layer = getattr(layer, "layer", layer)
88
+ try:
89
+ return gf.get_layer_tuple(layer)
90
+ except (KeyError, TypeError, ValueError):
91
+ return None
92
+
93
+
94
+ def _level_layer_tuple(level: LayerLevel) -> tuple[int, int] | None:
95
+ """Resolve a layer level to a (layer, datatype) tuple.
96
+
97
+ The derived layer takes precedence over the source layer, since it is the
98
+ physically meaningful output of a derived-layer boolean operation.
99
+ """
100
+ return _layer_tuple(level.derived_layer or level.layer)
101
+
102
+
103
+ def get_shapes_from_layer_stack(
104
+ cross_section: CrossSectionSpec,
105
+ layer_stack: LayerStack,
106
+ materials: Sequence[str] = (),
107
+ ) -> list[dict[str, Any]]:
108
+ """Translate a gdsfactory layer stack and cross-section into EMode shapes.
109
+
110
+ Each :class:`~gdsfactory.technology.LayerLevel` becomes one EMode shape.
111
+ A layer whose ``layer`` (or ``derived_layer``) matches a section of the
112
+ cross-section is patterned: it takes its mask width and offset from that
113
+ section and is etched through its full thickness. Layers without a
114
+ matching section become blanket layers (no mask or etch, following the
115
+ defaults of EMode's ``shape()`` function). Vertical positions are
116
+ referenced to the bottom of the layer stack, and gdsfactory mesh order
117
+ (lower = higher priority) is converted to EMode shape priority (higher =
118
+ higher priority).
119
+
120
+ Args:
121
+ cross_section: gdsfactory cross-section (or spec) defining mask
122
+ widths and offsets.
123
+ layer_stack: gdsfactory LayerStack defining layer materials,
124
+ thicknesses, and vertical placement.
125
+ materials: available EMode material names used to match gdsfactory
126
+ material names case-insensitively, typically from
127
+ ``EMode.get('materials')``.
128
+
129
+ Returns:
130
+ One dict of keyword arguments for EMode's ``shape()`` function per
131
+ layer, in layer-stack order, with dimensions in nm.
132
+ """
133
+ if not layer_stack.layers:
134
+ raise ValueError("layer_stack must contain at least one layer.")
135
+
136
+ xs = gf.get_cross_section(cross_section)
137
+
138
+ max_order = max(level.mesh_order for level in layer_stack.layers.values())
139
+ min_zmin = min(level.zmin for level in layer_stack.layers.values())
140
+
141
+ shapes: list[dict[str, Any]] = []
142
+ for name, level in layer_stack.layers.items():
143
+ if level.material is None:
144
+ raise ValueError(
145
+ f"Layer {name!r} has no material defined in the layer stack."
146
+ )
147
+
148
+ shape: dict[str, Any] = {
149
+ "name": name,
150
+ "material": get_emode_material(level.material, materials),
151
+ "height": level.thickness * UM_TO_NM,
152
+ "sidewall_angle": level.sidewall_angle,
153
+ "position": [0.0, (level.zmin - min_zmin + level.thickness / 2) * UM_TO_NM],
154
+ "priority": max_order - level.mesh_order + 1,
155
+ }
156
+
157
+ level_layer = _level_layer_tuple(level)
158
+ section = next(
159
+ (
160
+ s
161
+ for s in xs.sections
162
+ if level_layer is not None and _layer_tuple(s.layer) == level_layer
163
+ ),
164
+ None,
165
+ )
166
+ if section is not None:
167
+ shape["mask"] = section.width * UM_TO_NM
168
+ shape["mask_offset"] = section.offset * UM_TO_NM
169
+ shape["etch_depth"] = level.thickness * UM_TO_NM
170
+
171
+ shapes.append(shape)
172
+
173
+ return shapes
174
+
175
+
176
+ class EMode(emc.EMode):
177
+ """EMode session with gdsfactory geometry helpers.
178
+
179
+ Creating an instance launches the EMode application and connects to it,
180
+ so it requires a local EMode installation and license (the
181
+ ``emodeconnection`` client alone is not enough). Any EMode function can
182
+ be called as a method, e.g. ``FDM()``, ``EME()``, ``report()``,
183
+ ``plot()``; see https://docs.emodephotonix.com for the full API.
184
+ """
185
+
186
+ def build_waveguide(
187
+ self,
188
+ cross_section: CrossSectionSpec,
189
+ layer_stack: LayerStack,
190
+ **settings: Any,
191
+ ) -> None:
192
+ """Build a waveguide in this EMode session from gdsfactory geometry.
193
+
194
+ Args:
195
+ cross_section: gdsfactory cross-section (or spec) defining mask
196
+ widths and offsets.
197
+ layer_stack: gdsfactory LayerStack defining layer materials,
198
+ thicknesses, and vertical placement.
199
+ settings: forwarded to EMode's ``settings()`` function, with
200
+ dimensional values in um (see :func:`get_emode_settings`).
201
+ """
202
+ self.settings(**get_emode_settings(**settings))
203
+ materials = self.get("materials")
204
+ for shape in get_shapes_from_layer_stack(cross_section, layer_stack, materials):
205
+ self.shape(**shape)
206
+
207
+
208
+ if __name__ == "__main__":
209
+ # Silicon-on-insulator rib waveguide example.
210
+ # Requires a local EMode installation and license.
211
+ from gdsfactory.gpdk import LAYER_STACK
212
+
213
+ layer_stack = LayerStack(
214
+ layers={
215
+ k: LAYER_STACK.layers[k].model_copy()
216
+ for k in ("core", "clad", "slab90", "box")
217
+ }
218
+ )
219
+ layer_stack.layers["core"].thickness = 0.22
220
+ layer_stack.layers["core"].zmin = 0.0
221
+ layer_stack.layers["slab90"].thickness = 0.09
222
+ layer_stack.layers["slab90"].zmin = 0.0
223
+ layer_stack.layers["box"].thickness = 1.5
224
+ layer_stack.layers["box"].zmin = -1.5
225
+ layer_stack.layers["clad"].thickness = 1.5
226
+ layer_stack.layers["clad"].zmin = 0.0
227
+
228
+ em = EMode()
229
+ em.build_waveguide(
230
+ cross_section=gf.cross_section.rib(width=0.6),
231
+ layer_stack=layer_stack,
232
+ wavelength=1.55,
233
+ num_modes=1,
234
+ x_resolution=0.010,
235
+ y_resolution=0.010,
236
+ window_width=3.0,
237
+ window_height=3.0,
238
+ background_material="Air",
239
+ max_effective_index=2.631,
240
+ )
241
+ em.FDM()
242
+ em.report()
243
+ em.plot()
244
+ em.close()
@@ -0,0 +1,218 @@
1
+ """CI-safe tests for the EMode plugin.
2
+
3
+ These tests only require the pip-installable ``emodeconnection`` client. They
4
+ exercise the pure gdsfactory-to-EMode translation functions and use a stub
5
+ session to test ``EMode.build_waveguide`` without launching the EMode
6
+ application (which requires a separate installation and license).
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from typing import Any
12
+
13
+ import pytest
14
+
15
+ pytest.importorskip("emodeconnection")
16
+
17
+ import gdsfactory as gf
18
+ from gdsfactory.gpdk import LAYER_STACK
19
+ from gdsfactory.technology import LayerStack
20
+
21
+ from gplugins.emode import (
22
+ EMode,
23
+ get_emode_settings,
24
+ get_shapes_from_layer_stack,
25
+ )
26
+ from gplugins.emode.emode import DIMENSIONAL_SETTINGS, get_emode_material
27
+
28
+ EMODE_MATERIALS = ["Air", "Si", "SiO2", "Si3N4"]
29
+
30
+
31
+ @pytest.fixture
32
+ def layer_stack() -> LayerStack:
33
+ """Silicon-on-insulator rib stack with pinned dimensions and mesh orders."""
34
+ updates = {
35
+ "core": {"thickness": 0.22, "zmin": 0.0, "mesh_order": 1},
36
+ "slab90": {"thickness": 0.09, "zmin": 0.0, "mesh_order": 2},
37
+ "clad": {"thickness": 1.5, "zmin": 0.0, "mesh_order": 9},
38
+ "box": {"thickness": 1.5, "zmin": -1.5, "mesh_order": 8},
39
+ }
40
+ return LayerStack(
41
+ layers={
42
+ name: LAYER_STACK.layers[name].model_copy(update=update)
43
+ for name, update in updates.items()
44
+ }
45
+ )
46
+
47
+
48
+ class FakeEMode(EMode):
49
+ """EMode session that records calls instead of launching the application."""
50
+
51
+ def __init__(self) -> None:
52
+ """Initialize without calling super().__init__, which launches the app."""
53
+ self.recorded_settings: list[dict[str, Any]] = []
54
+ self.recorded_shapes: list[dict[str, Any]] = []
55
+
56
+ def settings(self, **kwargs: Any) -> None:
57
+ self.recorded_settings.append(kwargs)
58
+
59
+ def shape(self, **kwargs: Any) -> None:
60
+ self.recorded_shapes.append(kwargs)
61
+
62
+ def get(self, key: str) -> list[str]:
63
+ assert key == "materials"
64
+ return list(EMODE_MATERIALS)
65
+
66
+
67
+ def test_get_emode_settings_converts_dimensional_values() -> None:
68
+ settings = get_emode_settings(
69
+ wavelength=1.55,
70
+ window_width=3.0,
71
+ num_modes=2,
72
+ background_material="Air",
73
+ )
74
+ assert settings["wavelength"] == pytest.approx(1550.0)
75
+ assert settings["window_width"] == pytest.approx(3000.0)
76
+ assert settings["num_modes"] == 2
77
+ assert settings["background_material"] == "Air"
78
+
79
+
80
+ def test_get_emode_settings_passes_none_through() -> None:
81
+ settings = get_emode_settings(wavelength=1.55, bend_radius=None)
82
+ assert settings["wavelength"] == pytest.approx(1550.0)
83
+ assert settings["bend_radius"] is None
84
+
85
+
86
+ @pytest.mark.parametrize("key", sorted(DIMENSIONAL_SETTINGS))
87
+ def test_get_emode_settings_converts_every_dimensional_key(key: str) -> None:
88
+ settings = get_emode_settings(**{key: 1.0})
89
+ assert settings[key] == pytest.approx(1000.0)
90
+
91
+
92
+ def test_get_emode_material_matches_case_insensitively() -> None:
93
+ assert get_emode_material("si", EMODE_MATERIALS) == "Si"
94
+ assert get_emode_material("SIO2", EMODE_MATERIALS) == "SiO2"
95
+ assert get_emode_material("unknown_material", EMODE_MATERIALS) == "unknown_material"
96
+ assert get_emode_material("si", ()) == "si"
97
+
98
+
99
+ def test_shapes_one_per_layer_in_order(layer_stack: LayerStack) -> None:
100
+ shapes = get_shapes_from_layer_stack("rib", layer_stack)
101
+ assert [shape["name"] for shape in shapes] == list(layer_stack.layers)
102
+
103
+
104
+ def test_shapes_mask_from_cross_section(layer_stack: LayerStack) -> None:
105
+ shapes = {
106
+ shape["name"]: shape
107
+ for shape in get_shapes_from_layer_stack(
108
+ gf.cross_section.rib(width=0.6), layer_stack
109
+ )
110
+ }
111
+ # The core layer matches the WG section of the rib cross-section, so it
112
+ # is patterned: masked and etched through its full thickness.
113
+ assert shapes["core"]["mask"] == pytest.approx(600.0)
114
+ assert shapes["core"]["mask_offset"] == pytest.approx(0.0)
115
+ assert shapes["core"]["etch_depth"] == pytest.approx(220.0)
116
+ # Layers without a matching section are blanket layers with no mask/etch.
117
+ assert "mask" not in shapes["box"]
118
+ assert "mask_offset" not in shapes["box"]
119
+ assert "etch_depth" not in shapes["box"]
120
+
121
+
122
+ def test_shapes_mask_offset_from_cross_section(layer_stack: LayerStack) -> None:
123
+ xs = gf.CrossSection(sections=(gf.Section(width=0.5, offset=0.1, layer="WG"),))
124
+ shapes = {
125
+ shape["name"]: shape for shape in get_shapes_from_layer_stack(xs, layer_stack)
126
+ }
127
+ assert shapes["core"]["mask"] == pytest.approx(500.0)
128
+ assert shapes["core"]["mask_offset"] == pytest.approx(100.0)
129
+
130
+
131
+ def test_shapes_match_section_by_layer_tuple(layer_stack: LayerStack) -> None:
132
+ # Layer specs are normalized through the PDK, so a section defined with a
133
+ # (layer, datatype) tuple matches a layer stack that uses layer names.
134
+ wg_tuple = gf.get_layer_tuple("WG")
135
+ xs = gf.CrossSection(sections=(gf.Section(width=0.5, layer=wg_tuple),))
136
+ shapes = {
137
+ shape["name"]: shape for shape in get_shapes_from_layer_stack(xs, layer_stack)
138
+ }
139
+ assert shapes["core"]["mask"] == pytest.approx(500.0)
140
+ assert "mask" not in shapes["clad"]
141
+
142
+
143
+ def test_shapes_positions_relative_to_stack_bottom(layer_stack: LayerStack) -> None:
144
+ shapes = {
145
+ shape["name"]: shape
146
+ for shape in get_shapes_from_layer_stack("rib", layer_stack)
147
+ }
148
+ # min zmin is -1.5 (box), so the box spans 0..1500 nm with center at 750 nm.
149
+ assert shapes["box"]["position"] == pytest.approx([0.0, 750.0])
150
+ assert shapes["core"]["position"] == pytest.approx([0.0, 1610.0])
151
+ assert shapes["slab90"]["position"] == pytest.approx([0.0, 1545.0])
152
+ assert shapes["clad"]["position"] == pytest.approx([0.0, 2250.0])
153
+ assert shapes["core"]["height"] == pytest.approx(220.0)
154
+
155
+
156
+ def test_shapes_priority_from_mesh_order(layer_stack: LayerStack) -> None:
157
+ shapes = {
158
+ shape["name"]: shape
159
+ for shape in get_shapes_from_layer_stack("rib", layer_stack)
160
+ }
161
+ # gdsfactory mesh order (lower = higher priority) maps to EMode priority
162
+ # (higher = higher priority): priority = max_order - mesh_order + 1.
163
+ assert shapes["core"]["priority"] == 9
164
+ assert shapes["slab90"]["priority"] == 8
165
+ assert shapes["box"]["priority"] == 2
166
+ assert shapes["clad"]["priority"] == 1
167
+
168
+
169
+ def test_shapes_match_materials(layer_stack: LayerStack) -> None:
170
+ shapes = {
171
+ shape["name"]: shape
172
+ for shape in get_shapes_from_layer_stack("rib", layer_stack, EMODE_MATERIALS)
173
+ }
174
+ assert shapes["core"]["material"] == "Si"
175
+ assert shapes["box"]["material"] == "SiO2"
176
+
177
+ # Without a material list, gdsfactory names pass through unchanged.
178
+ shapes = {
179
+ shape["name"]: shape
180
+ for shape in get_shapes_from_layer_stack("rib", layer_stack)
181
+ }
182
+ assert shapes["core"]["material"] == "si"
183
+
184
+
185
+ def test_empty_layer_stack_raises() -> None:
186
+ with pytest.raises(ValueError, match="at least one layer"):
187
+ get_shapes_from_layer_stack("rib", LayerStack(layers={}))
188
+
189
+
190
+ def test_layer_without_material_raises(layer_stack: LayerStack) -> None:
191
+ layer_stack.layers["core"] = layer_stack.layers["core"].model_copy(
192
+ update={"material": None}
193
+ )
194
+ with pytest.raises(ValueError, match="core"):
195
+ get_shapes_from_layer_stack("rib", layer_stack)
196
+
197
+
198
+ def test_build_waveguide_forwards_to_session(layer_stack: LayerStack) -> None:
199
+ em = FakeEMode()
200
+ em.build_waveguide(
201
+ cross_section="rib",
202
+ layer_stack=layer_stack,
203
+ wavelength=1.55,
204
+ num_modes=2,
205
+ )
206
+
207
+ assert len(em.recorded_settings) == 1
208
+ assert em.recorded_settings[0]["wavelength"] == pytest.approx(1550.0)
209
+ assert em.recorded_settings[0]["num_modes"] == 2
210
+
211
+ shapes = {shape["name"]: shape for shape in em.recorded_shapes}
212
+ assert set(shapes) == set(layer_stack.layers)
213
+ # Materials from the session are used to match gdsfactory material names.
214
+ assert shapes["core"]["material"] == "Si"
215
+
216
+
217
+ if __name__ == "__main__":
218
+ pytest.main([__file__, "-v"])
@@ -215,7 +215,7 @@ def add_plot_labels(arg0, arg1, arg2, arg3) -> None:
215
215
 
216
216
  if __name__ == "__main__":
217
217
  import gdsfactory as gf
218
- from gdsfactory.generic_tech import LAYER, LAYER_STACK
218
+ from gdsfactory.gpdk import LAYER, LAYER_STACK
219
219
 
220
220
  length = 5
221
221
 
@@ -190,7 +190,7 @@ def plot_mode(
190
190
 
191
191
  if __name__ == "__main__":
192
192
  import gdsfactory as gf
193
- from gdsfactory.generic_tech import LAYER, LAYER_STACK
193
+ from gdsfactory.gpdk import LAYER, LAYER_STACK
194
194
 
195
195
  from gplugins.fdtdz.get_epsilon_fdtdz import component_to_epsilon_pjz
196
196
 
@@ -147,7 +147,7 @@ def get_sparameters_fdtdz(
147
147
 
148
148
 
149
149
  if __name__ == "__main__":
150
- from gdsfactory.generic_tech import LAYER, LAYER_STACK
150
+ from gdsfactory.gpdk import LAYER, LAYER_STACK
151
151
 
152
152
  length = 5
153
153
 
@@ -92,6 +92,9 @@ def compute_cross_section_modes(
92
92
 
93
93
 
94
94
  _material_name_to_index = {
95
+ "Si": 3.48,
96
+ "SiO2": 1.44,
97
+ "SiN": 2.0,
95
98
  "si": 3.48,
96
99
  "sio2": 1.44,
97
100
  "sin": 2.0,