diff --git a/CHANGELOG.md b/CHANGELOG.md index 75719cdad2..f2ef376a75 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `ElectromagneticFieldData.outer_dot` now works correctly for `FieldData`, not only `ModeSolverData`. - Fix to the setting of the mode solver PML parameters that produces better results for modes which do not decay in the PML, and fewer spurious modes. - Fix to single-precision mode solver to do the type conversion on the final matrix only rather than at intermediate steps, which improves accuracy in some cases. +- Improvements to graphene medium fit. ## [2.2.3] - 2023-6-15 diff --git a/tests/sims/simulation_2_3_0rc2.json b/tests/sims/simulation_2_3_0rc2.json new file mode 100644 index 0000000000..f7b10553d5 --- /dev/null +++ b/tests/sims/simulation_2_3_0rc2.json @@ -0,0 +1,1747 @@ +{ + "type": "Simulation", + "center": [ + 0.0, + 0.0, + 0.0 + ], + "size": [ + 8.0, + 8.0, + 8.0 + ], + "run_time": 1e-12, + "medium": { + "name": null, + "frequency_range": null, + "allow_gain": false, + "type": "Medium", + "permittivity": 1.0, + "conductivity": 0.0 + }, + "symmetry": [ + 0, + 0, + 0 + ], + "structures": [ + { + "geometry": { + "type": "Box", + "center": [ + -1.0, + 0.0, + 0.0 + ], + "size": [ + 1.0, + 1.0, + 1.0 + ] + }, + "name": null, + "type": "Structure", + "medium": { + "name": null, + "frequency_range": null, + "allow_gain": false, + "type": "Medium", + "permittivity": 2.0, + "conductivity": 0.0 + } + }, + { + "geometry": { + "type": "Box", + "center": [ + -1.0, + 0.0, + 0.0 + ], + "size": [ + 1.0, + "Infinity", + 1.0 + ] + }, + "name": null, + "type": "Structure", + "medium": { + "name": null, + "frequency_range": null, + "allow_gain": false, + "type": "Medium", + "permittivity": 1.0, + "conductivity": 3.0 + } + }, + { + "geometry": { + "type": "Sphere", + "radius": 1.0, + "center": [ + 1.0, + 0.0, + 1.0 + ] + }, + "name": null, + "type": "Structure", + "medium": { + "name": null, + "frequency_range": null, + "allow_gain": false, + "type": "Sellmeier", + "coeffs": [ + [ + 1.03961212, + 0.00600069867 + ], + [ + 0.231792344, + 0.0200179144 + ] + ] + } + }, + { + "geometry": { + "type": "Box", + "center": [ + -1.0, + 0.0, + 0.0 + ], + "size": [ + 1.0, + 1.0, + 1.0 + ] + }, + "name": null, + "type": "Structure", + "medium": { + "name": null, + "frequency_range": null, + "allow_gain": false, + "type": "Lorentz", + "eps_inf": 2.0, + "coeffs": [ + [ + 1.0, + 2.0, + 3.0 + ] + ] + } + }, + { + "geometry": { + "type": "Box", + "center": [ + -1.0, + 0.0, + 0.0 + ], + "size": [ + 1.0, + 1.0, + 1.0 + ] + }, + "name": null, + "type": "Structure", + "medium": { + "name": null, + "frequency_range": null, + "allow_gain": false, + "type": "Debye", + "eps_inf": 2.0, + "coeffs": [ + [ + 1.0, + 3.0 + ] + ] + } + }, + { + "geometry": { + "type": "TriangleMesh", + "mesh_dataset": { + "type": "TriangleMeshDataset", + "surface_mesh": "TriangleMeshDataArray" + } + }, + "name": null, + "type": "Structure", + "medium": { + "name": null, + "frequency_range": null, + "allow_gain": false, + "type": "Debye", + "eps_inf": 2.0, + "coeffs": [ + [ + 1.0, + 3.0 + ] + ] + } + }, + { + "geometry": { + "type": "Box", + "center": [ + -1.0, + 0.0, + 0.0 + ], + "size": [ + 1.0, + 1.0, + 1.0 + ] + }, + "name": null, + "type": "Structure", + "medium": { + "name": null, + "frequency_range": null, + "allow_gain": false, + "type": "Drude", + "eps_inf": 2.0, + "coeffs": [ + [ + 1.0, + 3.0 + ] + ] + } + }, + { + "geometry": { + "type": "Box", + "center": [ + -1.0, + 0.0, + 0.0 + ], + "size": [ + 1.0, + 0.0, + 1.0 + ] + }, + "name": null, + "type": "Structure", + "medium": { + "name": null, + "frequency_range": null, + "allow_gain": false, + "type": "Medium2D", + "ss": { + "name": null, + "frequency_range": null, + "allow_gain": false, + "type": "PoleResidue", + "eps_inf": 1.0, + "poles": [ + [ + { + "real": 0.0, + "imag": 0.0 + }, + { + "real": 254117040158918.28, + "imag": 0.0 + } + ] + ] + }, + "tt": { + "name": null, + "frequency_range": null, + "allow_gain": false, + "type": "PoleResidue", + "eps_inf": 1.0, + "poles": [ + [ + { + "real": 0.0, + "imag": 0.0 + }, + { + "real": 254117040158918.28, + "imag": 0.0 + } + ] + ] + } + } + }, + { + "geometry": { + "type": "GeometryGroup", + "geometries": [ + { + "type": "Box", + "center": [ + -1.0, + 0.0, + 0.0 + ], + "size": [ + 1.0, + 1.0, + 1.0 + ] + } + ] + }, + "name": null, + "type": "Structure", + "medium": { + "name": "PEC", + "frequency_range": null, + "allow_gain": false, + "type": "PECMedium" + } + }, + { + "geometry": { + "type": "Cylinder", + "axis": 1, + "sidewall_angle": 0.0, + "reference_plane": "middle", + "radius": 1.0, + "center": [ + 1.0, + 0.0, + -1.0 + ], + "length": 2.0 + }, + "name": null, + "type": "Structure", + "medium": { + "name": null, + "frequency_range": null, + "allow_gain": null, + "type": "AnisotropicMedium", + "xx": { + "name": null, + "frequency_range": null, + "allow_gain": false, + "type": "Medium", + "permittivity": 1.0, + "conductivity": 0.0 + }, + "yy": { + "name": null, + "frequency_range": null, + "allow_gain": false, + "type": "Medium", + "permittivity": 2.0, + "conductivity": 0.0 + }, + "zz": { + "name": null, + "frequency_range": null, + "allow_gain": false, + "type": "Medium", + "permittivity": 3.0, + "conductivity": 0.0 + } + } + }, + { + "geometry": { + "type": "PolySlab", + "axis": 2, + "sidewall_angle": 0.0, + "reference_plane": "middle", + "slab_bounds": [ + -1.0, + 1.0 + ], + "dilation": 0.0, + "vertices": [ + [ + -1.5, + -1.5 + ], + [ + -0.5, + -1.5 + ], + [ + -0.5, + -0.5 + ] + ] + }, + "name": null, + "type": "Structure", + "medium": { + "name": null, + "frequency_range": null, + "allow_gain": false, + "type": "PoleResidue", + "eps_inf": 1.0, + "poles": [ + [ + { + "real": 0.0, + "imag": 6206417594288582.0 + }, + { + "real": -0.0, + "imag": -3.311074436985222e+16 + } + ] + ] + } + }, + { + "geometry": { + "type": "Box", + "center": [ + -1.0, + 0.5, + 0.5 + ], + "size": [ + 1.0, + 1.0, + 1.0 + ] + }, + "name": null, + "type": "Structure", + "medium": { + "name": null, + "frequency_range": null, + "allow_gain": false, + "type": "CustomMedium", + "interp_method": "nearest", + "permittivity": "SpatialDataArray", + "conductivity": null, + "eps_dataset": null + } + }, + { + "geometry": { + "type": "Box", + "center": [ + -1.0, + 0.5, + 0.5 + ], + "size": [ + 1.0, + 1.0, + 1.0 + ] + }, + "name": null, + "type": "Structure", + "medium": { + "name": null, + "frequency_range": null, + "allow_gain": false, + "type": "CustomDrude", + "eps_inf": "SpatialDataArray", + "coeffs": [ + [ + "SpatialDataArray", + "SpatialDataArray" + ] + ], + "interp_method": "nearest" + } + }, + { + "geometry": { + "type": "Box", + "center": [ + -1.0, + 0.5, + 0.5 + ], + "size": [ + 1.0, + 1.0, + 1.0 + ] + }, + "name": null, + "type": "Structure", + "medium": { + "name": null, + "frequency_range": null, + "allow_gain": false, + "type": "CustomLorentz", + "eps_inf": "SpatialDataArray", + "coeffs": [ + [ + "SpatialDataArray", + "SpatialDataArray", + "SpatialDataArray" + ] + ], + "interp_method": "nearest" + } + }, + { + "geometry": { + "type": "Box", + "center": [ + -1.0, + 0.5, + 0.5 + ], + "size": [ + 1.0, + 1.0, + 1.0 + ] + }, + "name": null, + "type": "Structure", + "medium": { + "name": null, + "frequency_range": null, + "allow_gain": false, + "type": "CustomDebye", + "eps_inf": "SpatialDataArray", + "coeffs": [ + [ + "SpatialDataArray", + "SpatialDataArray" + ] + ], + "interp_method": "nearest" + } + }, + { + "geometry": { + "type": "Box", + "center": [ + -1.0, + 0.5, + 0.5 + ], + "size": [ + 1.0, + 1.0, + 1.0 + ] + }, + "name": null, + "type": "Structure", + "medium": { + "name": null, + "frequency_range": null, + "allow_gain": false, + "type": "CustomPoleResidue", + "eps_inf": "SpatialDataArray", + "poles": [ + [ + "SpatialDataArray", + "SpatialDataArray" + ] + ], + "interp_method": "nearest" + } + }, + { + "geometry": { + "type": "Box", + "center": [ + -1.0, + 0.5, + 0.5 + ], + "size": [ + 1.0, + 1.0, + 1.0 + ] + }, + "name": null, + "type": "Structure", + "medium": { + "name": null, + "frequency_range": null, + "allow_gain": false, + "type": "CustomSellmeier", + "coeffs": [ + [ + "SpatialDataArray", + "SpatialDataArray" + ] + ], + "interp_method": "nearest" + } + }, + { + "geometry": { + "type": "PolySlab", + "axis": 2, + "sidewall_angle": 0.0, + "reference_plane": "middle", + "slab_bounds": [ + -1.0, + 1.0 + ], + "dilation": 0.0, + "vertices": [ + [ + -1.5, + -1.5 + ], + [ + -0.5, + -1.5 + ], + [ + -0.5, + -0.5 + ] + ] + }, + "name": null, + "type": "Structure", + "medium": { + "name": null, + "frequency_range": null, + "allow_gain": false, + "type": "PoleResidue", + "eps_inf": 1.0, + "poles": [ + [ + { + "real": 0.0, + "imag": 6206417594288582.0 + }, + { + "real": -0.0, + "imag": -3.311074436985222e+16 + } + ] + ] + } + }, + { + "geometry": { + "type": "TriangleMesh", + "mesh_dataset": { + "type": "TriangleMeshDataset", + "surface_mesh": "TriangleMeshDataArray" + } + }, + "name": null, + "type": "Structure", + "medium": { + "name": null, + "frequency_range": null, + "allow_gain": false, + "type": "Medium", + "permittivity": 5.0, + "conductivity": 0.0 + } + } + ], + "sources": [ + { + "type": "UniformCurrentSource", + "center": [ + 0.0, + 0.5, + 0.0 + ], + "size": [ + 0.0, + 0.0, + 0.0 + ], + "source_time": { + "amplitude": 1.0, + "phase": 0.0, + "type": "GaussianPulse", + "freq0": 200000000000000.0, + "fwidth": 40000000000000.0, + "offset": 5.0 + }, + "name": null, + "polarization": "Hx" + }, + { + "type": "PointDipole", + "center": [ + 0.0, + 0.5, + 0.0 + ], + "size": [ + 0, + 0, + 0 + ], + "source_time": { + "amplitude": 1.0, + "phase": 0.0, + "type": "GaussianPulse", + "freq0": 200000000000000.0, + "fwidth": 40000000000000.0, + "offset": 5.0 + }, + "name": null, + "polarization": "Ex", + "interpolate": true + }, + { + "type": "ModeSource", + "center": [ + 0.0, + 0.5, + 0.0 + ], + "size": [ + 2.0, + 0.0, + 2.0 + ], + "source_time": { + "amplitude": 1.0, + "phase": 0.0, + "type": "GaussianPulse", + "freq0": 200000000000000.0, + "fwidth": 40000000000000.0, + "offset": 5.0 + }, + "name": null, + "num_freqs": 1, + "direction": "-", + "mode_spec": { + "num_modes": 1, + "target_neff": null, + "num_pml": [ + 0, + 0 + ], + "filter_pol": null, + "angle_theta": 0.0, + "angle_phi": 0.0, + "precision": "single", + "bend_radius": null, + "bend_axis": null, + "track_freq": "central", + "group_index_step": false, + "type": "ModeSpec" + }, + "mode_index": 0 + }, + { + "type": "PlaneWave", + "center": [ + 0.0, + 0.0, + 0.0 + ], + "size": [ + 0.0, + "Infinity", + "Infinity" + ], + "source_time": { + "amplitude": 1.0, + "phase": 0.0, + "type": "GaussianPulse", + "freq0": 200000000000000.0, + "fwidth": 40000000000000.0, + "offset": 5.0 + }, + "name": null, + "direction": "+", + "angle_theta": 0.0, + "angle_phi": 0.0, + "pol_angle": 0.1 + }, + { + "type": "GaussianBeam", + "center": [ + 0.0, + 0.0, + 0.0 + ], + "size": [ + 0.0, + 3.0, + 3.0 + ], + "source_time": { + "amplitude": 1.0, + "phase": 0.0, + "type": "GaussianPulse", + "freq0": 200000000000000.0, + "fwidth": 40000000000000.0, + "offset": 5.0 + }, + "name": null, + "num_freqs": 1, + "direction": "+", + "angle_theta": 0.0, + "angle_phi": 0.0, + "pol_angle": 1.5707963267948966, + "waist_radius": 1.0, + "waist_distance": 0.0 + }, + { + "type": "AstigmaticGaussianBeam", + "center": [ + 0.0, + 0.0, + 0.0 + ], + "size": [ + 0.0, + 3.0, + 3.0 + ], + "source_time": { + "amplitude": 1.0, + "phase": 0.0, + "type": "GaussianPulse", + "freq0": 200000000000000.0, + "fwidth": 40000000000000.0, + "offset": 5.0 + }, + "name": null, + "num_freqs": 1, + "direction": "+", + "angle_theta": 0.0, + "angle_phi": 0.0, + "pol_angle": 1.5707963267948966, + "waist_sizes": [ + 1.0, + 2.0 + ], + "waist_distances": [ + 3.0, + 4.0 + ] + }, + { + "type": "CustomFieldSource", + "center": [ + 0.0, + 1.0, + 2.0 + ], + "size": [ + 2.0, + 2.0, + 0.0 + ], + "source_time": { + "amplitude": 1.0, + "phase": 0.0, + "type": "GaussianPulse", + "freq0": 200000000000000.0, + "fwidth": 40000000000000.0, + "offset": 5.0 + }, + "name": null, + "field_dataset": { + "type": "FieldDataset", + "Ex": "ScalarFieldDataArray", + "Ey": null, + "Ez": null, + "Hx": null, + "Hy": null, + "Hz": null + } + }, + { + "type": "CustomCurrentSource", + "center": [ + 0.0, + 1.0, + 2.0 + ], + "size": [ + 2.0, + 2.0, + 0.0 + ], + "source_time": { + "amplitude": 1.0, + "phase": 0.0, + "type": "GaussianPulse", + "freq0": 200000000000000.0, + "fwidth": 40000000000000.0, + "offset": 5.0 + }, + "name": null, + "current_dataset": { + "type": "FieldDataset", + "Ex": "ScalarFieldDataArray", + "Ey": null, + "Ez": null, + "Hx": null, + "Hy": null, + "Hz": null + } + }, + { + "type": "TFSF", + "center": [ + 1.0, + 2.0, + -3.0 + ], + "size": [ + 2.5, + 2.5, + 0.5 + ], + "source_time": { + "amplitude": 1.0, + "phase": 0.0, + "type": "GaussianPulse", + "freq0": 200000000000000.0, + "fwidth": 40000000000000.0, + "offset": 5.0 + }, + "name": null, + "direction": "+", + "angle_theta": 0.5235987755982988, + "angle_phi": 0.6283185307179586, + "pol_angle": 0.0, + "injection_axis": 2 + } + ], + "boundary_spec": { + "x": { + "plus": { + "name": null, + "type": "PML", + "num_layers": 20, + "parameters": { + "sigma_order": 3, + "sigma_min": 0.0, + "sigma_max": 1.5, + "type": "PMLParams", + "kappa_order": 3, + "kappa_min": 1.0, + "kappa_max": 3.0, + "alpha_order": 1, + "alpha_min": 0.0, + "alpha_max": 0.0 + } + }, + "minus": { + "name": null, + "type": "Absorber", + "num_layers": 100, + "parameters": { + "sigma_order": 3, + "sigma_min": 0.0, + "sigma_max": 6.4, + "type": "AbsorberParams" + } + }, + "type": "Boundary" + }, + "y": { + "plus": { + "name": null, + "type": "BlochBoundary", + "bloch_vec": 1.0 + }, + "minus": { + "name": null, + "type": "BlochBoundary", + "bloch_vec": 1.0 + }, + "type": "Boundary" + }, + "z": { + "plus": { + "name": null, + "type": "Periodic" + }, + "minus": { + "name": null, + "type": "Periodic" + }, + "type": "Boundary" + }, + "type": "BoundarySpec" + }, + "monitors": [ + { + "type": "FieldMonitor", + "center": [ + 0.0, + 0.0, + 0.0 + ], + "size": [ + 0.0, + 0.0, + 0.0 + ], + "name": "field", + "freqs": [ + 150000000000000.0, + 200000000000000.0 + ], + "apodization": { + "start": null, + "end": null, + "width": null, + "type": "ApodizationSpec" + }, + "fields": [ + "Ex" + ], + "interval_space": [ + 1, + 1, + 1 + ], + "colocate": false + }, + { + "type": "FieldTimeMonitor", + "center": [ + 0.0, + 0.0, + 0.0 + ], + "size": [ + 0.0, + 0.0, + 0.0 + ], + "name": "field_time", + "start": 0.0, + "stop": null, + "interval": 100, + "fields": [ + "Ex", + "Ey", + "Ez", + "Hx", + "Hy", + "Hz" + ], + "interval_space": [ + 1, + 1, + 1 + ], + "colocate": false + }, + { + "type": "FluxMonitor", + "center": [ + 0.0, + 0.0, + 0.0 + ], + "size": [ + 1.0, + 1.0, + 0.0 + ], + "name": "flux", + "freqs": [ + 200000000000000.0, + 250000000000000.0 + ], + "apodization": { + "start": null, + "end": null, + "width": null, + "type": "ApodizationSpec" + }, + "normal_dir": "+", + "exclude_surfaces": null + }, + { + "type": "FluxTimeMonitor", + "center": [ + 0.0, + 0.0, + 0.0 + ], + "size": [ + 1.0, + 1.0, + 0.0 + ], + "name": "flux_time", + "start": 0.0, + "stop": null, + "interval": 1, + "normal_dir": "+", + "exclude_surfaces": null + }, + { + "type": "PermittivityMonitor", + "center": [ + 0.0, + 0.0, + 0.0 + ], + "size": [ + 1.0, + 1.0, + 0.1 + ], + "name": "eps", + "freqs": [ + 100000000000000.0 + ], + "apodization": { + "start": null, + "end": null, + "width": null, + "type": "ApodizationSpec" + } + }, + { + "type": "ModeMonitor", + "center": [ + 0.0, + 0.0, + 0.0 + ], + "size": [ + 1.0, + 1.0, + 0.0 + ], + "name": "mode", + "freqs": [ + 200000000000000.0, + 250000000000000.0 + ], + "apodization": { + "start": null, + "end": null, + "width": null, + "type": "ApodizationSpec" + }, + "mode_spec": { + "num_modes": 1, + "target_neff": null, + "num_pml": [ + 0, + 0 + ], + "filter_pol": null, + "angle_theta": 0.0, + "angle_phi": 0.0, + "precision": "single", + "bend_radius": null, + "bend_axis": null, + "track_freq": "central", + "group_index_step": false, + "type": "ModeSpec" + } + }, + { + "type": "ModeSolverMonitor", + "center": [ + 0.0, + 0.0, + 0.0 + ], + "size": [ + 1.0, + 1.0, + 0.0 + ], + "name": "mode_solver", + "freqs": [ + 200000000000000.0, + 250000000000000.0 + ], + "apodization": { + "start": null, + "end": null, + "width": null, + "type": "ApodizationSpec" + }, + "mode_spec": { + "num_modes": 1, + "target_neff": null, + "num_pml": [ + 0, + 0 + ], + "filter_pol": null, + "angle_theta": 0.0, + "angle_phi": 0.0, + "precision": "single", + "bend_radius": null, + "bend_axis": null, + "track_freq": "central", + "group_index_step": false, + "type": "ModeSpec" + } + }, + { + "type": "FieldProjectionAngleMonitor", + "center": [ + 0.0, + 0.0, + 0.0 + ], + "size": [ + 0.0, + 2.0, + 2.0 + ], + "name": "proj_angle", + "freqs": [ + 250000000000000.0, + 300000000000000.0 + ], + "apodization": { + "start": null, + "end": null, + "width": null, + "type": "ApodizationSpec" + }, + "normal_dir": "+", + "exclude_surfaces": null, + "custom_origin": [ + 1.0, + 2.0, + 3.0 + ], + "far_field_approx": true, + "proj_distance": 1000000.0, + "theta": [ + -1.5707963267948966, + -1.5390630676677268, + -1.5073298085405573, + -1.4755965494133876, + -1.443863290286218, + -1.4121300311590483, + -1.3803967720318788, + -1.348663512904709, + -1.3169302537775396, + -1.2851969946503699, + -1.2534637355232003, + -1.2217304763960306, + -1.189997217268861, + -1.1582639581416914, + -1.1265306990145216, + -1.0947974398873521, + -1.0630641807601826, + -1.0313309216330129, + -0.9995976625058433, + -0.9678644033786736, + -0.936131144251504, + -0.9043978851243344, + -0.8726646259971648, + -0.8409313668699951, + -0.8091981077428254, + -0.7774648486156558, + -0.7457315894884862, + -0.7139983303613165, + -0.6822650712341469, + -0.6505318121069773, + -0.6187985529798077, + -0.5870652938526381, + -0.5553320347254684, + -0.5235987755982987, + -0.4918655164711292, + -0.46013225734395946, + -0.42839899821678995, + -0.3966657390896202, + -0.3649324799624507, + -0.333199220835281, + -0.30146596170811146, + -0.26973270258094173, + -0.23799944345377222, + -0.2062661843266025, + -0.17453292519943298, + -0.14279966607226324, + -0.11106640694509373, + -0.079333147817924, + -0.047599888690754266, + -0.015866629563584755, + 0.015866629563584977, + 0.04759988869075449, + 0.07933314781792422, + 0.11106640694509373, + 0.14279966607226346, + 0.17453292519943298, + 0.2062661843266027, + 0.23799944345377222, + 0.26973270258094195, + 0.30146596170811146, + 0.3331992208352812, + 0.3649324799624507, + 0.39666573908962044, + 0.42839899821678995, + 0.4601322573439597, + 0.4918655164711292, + 0.5235987755982991, + 0.5553320347254687, + 0.5870652938526382, + 0.6187985529798077, + 0.6505318121069776, + 0.6822650712341471, + 0.7139983303613167, + 0.7457315894884862, + 0.7774648486156561, + 0.8091981077428256, + 0.8409313668699951, + 0.8726646259971647, + 0.9043978851243346, + 0.9361311442515041, + 0.9678644033786736, + 0.9995976625058436, + 1.031330921633013, + 1.0630641807601826, + 1.0947974398873521, + 1.126530699014522, + 1.1582639581416916, + 1.189997217268861, + 1.2217304763960306, + 1.2534637355232006, + 1.28519699465037, + 1.3169302537775396, + 1.348663512904709, + 1.380396772031879, + 1.4121300311590486, + 1.443863290286218, + 1.475596549413388, + 1.5073298085405575, + 1.539063067667727, + 1.5707963267948966 + ], + "phi": [ + 0.0, + 1.5707963267948966 + ] + }, + { + "type": "FieldProjectionCartesianMonitor", + "center": [ + 0.0, + 0.0, + 0.0 + ], + "size": [ + 0.0, + 2.0, + 2.0 + ], + "name": "proj_cartesian", + "freqs": [ + 250000000000000.0, + 300000000000000.0 + ], + "apodization": { + "start": null, + "end": null, + "width": null, + "type": "ApodizationSpec" + }, + "normal_dir": "+", + "exclude_surfaces": null, + "custom_origin": [ + 1.0, + 2.0, + 3.0 + ], + "far_field_approx": true, + "proj_axis": 2, + "proj_distance": 5.0, + "x": [ + -1.0, + 0.0, + 1.0 + ], + "y": [ + -2.0, + -1.0, + 0.0, + 1.0, + 2.0 + ] + }, + { + "type": "FieldProjectionKSpaceMonitor", + "center": [ + 0.0, + 0.0, + 0.0 + ], + "size": [ + 0.0, + 2.0, + 2.0 + ], + "name": "proj_kspace", + "freqs": [ + 250000000000000.0, + 300000000000000.0 + ], + "apodization": { + "start": null, + "end": null, + "width": null, + "type": "ApodizationSpec" + }, + "normal_dir": "+", + "exclude_surfaces": null, + "custom_origin": [ + 1.0, + 2.0, + 3.0 + ], + "far_field_approx": true, + "proj_axis": 2, + "proj_distance": 1000000.0, + "ux": [ + 0.1, + 0.2 + ], + "uy": [ + 0.3, + 0.4, + 0.5 + ] + }, + { + "type": "FieldProjectionAngleMonitor", + "center": [ + 0.0, + 0.0, + 0.0 + ], + "size": [ + 0.0, + 2.0, + 2.0 + ], + "name": "proj_angle_exact", + "freqs": [ + 250000000000000.0, + 300000000000000.0 + ], + "apodization": { + "start": null, + "end": null, + "width": null, + "type": "ApodizationSpec" + }, + "normal_dir": "+", + "exclude_surfaces": null, + "custom_origin": [ + 1.0, + 2.0, + 3.0 + ], + "far_field_approx": true, + "proj_distance": 1000000.0, + "theta": [ + -1.5707963267948966, + -1.5390630676677268, + -1.5073298085405573, + -1.4755965494133876, + -1.443863290286218, + -1.4121300311590483, + -1.3803967720318788, + -1.348663512904709, + -1.3169302537775396, + -1.2851969946503699, + -1.2534637355232003, + -1.2217304763960306, + -1.189997217268861, + -1.1582639581416914, + -1.1265306990145216, + -1.0947974398873521, + -1.0630641807601826, + -1.0313309216330129, + -0.9995976625058433, + -0.9678644033786736, + -0.936131144251504, + -0.9043978851243344, + -0.8726646259971648, + -0.8409313668699951, + -0.8091981077428254, + -0.7774648486156558, + -0.7457315894884862, + -0.7139983303613165, + -0.6822650712341469, + -0.6505318121069773, + -0.6187985529798077, + -0.5870652938526381, + -0.5553320347254684, + -0.5235987755982987, + -0.4918655164711292, + -0.46013225734395946, + -0.42839899821678995, + -0.3966657390896202, + -0.3649324799624507, + -0.333199220835281, + -0.30146596170811146, + -0.26973270258094173, + -0.23799944345377222, + -0.2062661843266025, + -0.17453292519943298, + -0.14279966607226324, + -0.11106640694509373, + -0.079333147817924, + -0.047599888690754266, + -0.015866629563584755, + 0.015866629563584977, + 0.04759988869075449, + 0.07933314781792422, + 0.11106640694509373, + 0.14279966607226346, + 0.17453292519943298, + 0.2062661843266027, + 0.23799944345377222, + 0.26973270258094195, + 0.30146596170811146, + 0.3331992208352812, + 0.3649324799624507, + 0.39666573908962044, + 0.42839899821678995, + 0.4601322573439597, + 0.4918655164711292, + 0.5235987755982991, + 0.5553320347254687, + 0.5870652938526382, + 0.6187985529798077, + 0.6505318121069776, + 0.6822650712341471, + 0.7139983303613167, + 0.7457315894884862, + 0.7774648486156561, + 0.8091981077428256, + 0.8409313668699951, + 0.8726646259971647, + 0.9043978851243346, + 0.9361311442515041, + 0.9678644033786736, + 0.9995976625058436, + 1.031330921633013, + 1.0630641807601826, + 1.0947974398873521, + 1.126530699014522, + 1.1582639581416916, + 1.189997217268861, + 1.2217304763960306, + 1.2534637355232006, + 1.28519699465037, + 1.3169302537775396, + 1.348663512904709, + 1.380396772031879, + 1.4121300311590486, + 1.443863290286218, + 1.475596549413388, + 1.5073298085405575, + 1.539063067667727, + 1.5707963267948966 + ], + "phi": [ + 0.0, + 1.5707963267948966 + ] + }, + { + "type": "DiffractionMonitor", + "center": [ + 0.0, + 0.0, + 0.0 + ], + "size": [ + 0.0, + "Infinity", + "Infinity" + ], + "name": "diffraction", + "freqs": [ + 100000000000000.0, + 200000000000000.0 + ], + "apodization": { + "start": null, + "end": null, + "width": null, + "type": "ApodizationSpec" + }, + "normal_dir": "+" + } + ], + "grid_spec": { + "grid_x": { + "type": "AutoGrid", + "min_steps_per_wvl": 10.0, + "max_scale": 1.4, + "dl_min": 0.0, + "mesher": { + "type": "GradedMesher" + } + }, + "grid_y": { + "type": "CustomGrid", + "dl": [ + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04, + 0.04 + ], + "custom_offset": null + }, + "grid_z": { + "type": "UniformGrid", + "dl": 0.05 + }, + "wavelength": null, + "override_structures": [ + { + "geometry": { + "type": "Box", + "center": [ + -1.0, + 0.0, + 0.0 + ], + "size": [ + 1.0, + 1.0, + 1.0 + ] + }, + "name": null, + "type": "Structure", + "medium": { + "name": null, + "frequency_range": null, + "allow_gain": false, + "type": "Medium", + "permittivity": 2.0, + "conductivity": 0.0 + } + } + ], + "type": "GridSpec" + }, + "shutoff": 0.0001, + "subpixel": false, + "normalize_index": 0, + "courant": 0.8, + "version": "2.3.0rc2" +} \ No newline at end of file diff --git a/tests/test_package/test_parametric_variants.py b/tests/test_package/test_parametric_variants.py index 688a9471c3..6a2c8424e3 100644 --- a/tests/test_package/test_parametric_variants.py +++ b/tests/test_package/test_parametric_variants.py @@ -1,41 +1,66 @@ import pytest +import pydantic import numpy as np from tidy3d.material_library.material_library import material_library +from tidy3d.material_library.parametric_materials import ( + GRAPHENE_GAMMA_MIN, + GRAPHENE_GAMMA_MAX, + GRAPHENE_MU_C_MIN, + GRAPHENE_MU_C_MAX, + GRAPHENE_TEMP_MIN, + GRAPHENE_TEMP_MAX, + GRAPHENE_FIT_FREQ_MIN, + GRAPHENE_FIT_FREQ_MAX, + GRAPHENE_FIT_NUM_FREQS, + GRAPHENE_FIT_ATOL, +) from tidy3d.material_library.parametric_materials import Graphene import tidy3d as td from numpy.random import default_rng -# tolerance -ATOL = 1e-5 +def test_graphene_defaults(): + freqs = np.linspace(GRAPHENE_FIT_FREQ_MIN, GRAPHENE_FIT_FREQ_MAX, GRAPHENE_FIT_NUM_FREQS) + graphene = Graphene() + sigma1 = graphene.medium.sigma_model(freqs) + sigma2 = graphene.numerical_conductivity(freqs) + with pytest.raises(pydantic.ValidationError): + Graphene(gamma=GRAPHENE_GAMMA_MIN - 1) + with pytest.raises(pydantic.ValidationError): + Graphene(mu_c=GRAPHENE_MU_C_MAX + 1) + with pytest.raises(pydantic.ValidationError): + Graphene(temp=GRAPHENE_TEMP_MAX + 1) -@pytest.mark.parametrize("rng_seed", np.arange(0, 4)) + +@pytest.mark.parametrize("rng_seed", np.arange(0, 15)) def test_graphene(rng_seed): """test graphene for range of physical parameters""" rng = default_rng(rng_seed) - gamma_min = 1e-5 - gamma_max = 1e-2 - mu_min = 0 - mu_max = 0.5 - temp_min = 200 - temp_max = 1000 + gamma_min = GRAPHENE_GAMMA_MIN + gamma_max = GRAPHENE_GAMMA_MAX + mu_min = GRAPHENE_MU_C_MIN + mu_max = GRAPHENE_MU_C_MAX + temp_min = GRAPHENE_TEMP_MIN + temp_max = GRAPHENE_TEMP_MAX - freqs = np.linspace(1e12, 1e15, 1000) + freqs = np.linspace(GRAPHENE_FIT_FREQ_MIN, GRAPHENE_FIT_FREQ_MAX, GRAPHENE_FIT_NUM_FREQS) gamma = gamma_min + (gamma_max - gamma_min) * rng.random() mu_c = mu_min + (mu_max - mu_min) * rng.random() temp = temp_min + (temp_max - temp_min) * rng.random() + print(f"Graphene(gamma='{gamma:.6f}', mu_c='{mu_c:.2f}', temp='{temp:.0f}')") + graphene = Graphene(gamma=gamma, mu_c=mu_c, temp=temp) sigma1 = graphene.medium.sigma_model(freqs) sigma2 = graphene.numerical_conductivity(freqs) - assert np.allclose(sigma1, sigma2, rtol=0, atol=ATOL) + assert np.allclose(sigma1, sigma2, rtol=0, atol=GRAPHENE_FIT_ATOL) graphene = Graphene(gamma=gamma, mu_c=mu_c, temp=temp, include_interband=False) sigma1 = graphene.medium.sigma_model(freqs) sigma2 = graphene.intraband_drude.sigma_model(freqs) - assert np.allclose(sigma1, sigma2, rtol=0, atol=ATOL) + assert np.allclose(sigma1, sigma2, rtol=0, atol=GRAPHENE_FIT_ATOL) diff --git a/tidy3d/material_library/parametric_materials.py b/tidy3d/material_library/parametric_materials.py index e55dada0a6..f20464d83f 100644 --- a/tidy3d/material_library/parametric_materials.py +++ b/tidy3d/material_library/parametric_materials.py @@ -1,12 +1,15 @@ """Parametric material models.""" from abc import ABC, abstractmethod from typing import List, Tuple +import warnings import pydantic as pd import numpy as np 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 +from ..exceptions import ValidationError +from ..log import log try: from scipy import integrate @@ -15,7 +18,6 @@ except ImportError: INTEGRATE_AVAILABLE = False - # default values of the physical parameters for graphene # scattering rate in eV GRAPHENE_DEF_GAMMA = 0.00041 @@ -24,9 +26,19 @@ # temperature in K GRAPHENE_DEF_TEMP = 300 +# bounds for MU_C +GRAPHENE_MU_C_MIN = 0 +GRAPHENE_MU_C_MAX = 0.9 +# bounds for temp +GRAPHENE_TEMP_MIN = 100 +GRAPHENE_TEMP_MAX = 1000 +# bounds for gamma +GRAPHENE_GAMMA_MIN = 0 +GRAPHENE_GAMMA_MAX = 0.01 + # constants controlling the numerical integration of the interband term in graphene # frequency limits of integration -GRAPHENE_INT_MIN = 0 +GRAPHENE_INT_MIN = 1e10 GRAPHENE_INT_MAX = 1e16 # integration absolute tolerance GRAPHENE_INT_TOL = 1e-20 @@ -35,8 +47,11 @@ # frequency range for fitting GRAPHENE_FIT_FREQ_MIN = 1e12 GRAPHENE_FIT_FREQ_MAX = 1e15 -# the fitting for the interband feature has this width relative to the absorption threshold -GRAPHENE_FIT_REL_WIDTH = 1.1 +GRAPHENE_FIT_NUM_FREQS = 100 +GRAPHENE_FIT_ATOL = 2e-5 +# determines how flat the conductivity needs to be before we place the second node +GRAPHENE_FIT_NODE_CUTOFF = 0.9 +GRAPHENE_FIT_NODE_MULTIPLIER = 2 # number of optimization iterations for fitting GRAPHENE_FIT_NUM_ITERS = 100 @@ -116,17 +131,34 @@ class Graphene(ParametricVariantItem2D): "at the cost of decreased stability in the fitting algorithm.", ) - @pd.validator("interband_fit_freq_nodes", always=True) - def _calculate_freq_nodes(cls, val, values): - """Calculate the default frequency nodes if none are provided.""" - if val is None: - mu_c = values["mu_c"] / (2 * np.pi * HBAR) - temp = values["temp"] * K_B / (2 * np.pi * HBAR) - center = max(np.sqrt(abs(mu_c**2 - temp**2)), GRAPHENE_FIT_FREQ_MIN * 1e-5) - return [ - (GRAPHENE_FIT_FREQ_MIN, GRAPHENE_FIT_FREQ_MAX), - (2 * center, 2 * (center + temp) * GRAPHENE_FIT_REL_WIDTH), - ] + @pd.validator("mu_c", always=True) + def _validate_mu_c(cls, val): + """Check that mu_c is in the allowed range.""" + if not GRAPHENE_MU_C_MIN <= val <= GRAPHENE_MU_C_MAX: + raise ValidationError( + "The parameter mu_c for graphene must be between " + f"'{GRAPHENE_MU_C_MIN}' and '{GRAPHENE_MU_C_MAX}'" + ) + return val + + @pd.validator("temp", always=True) + def _validate_temp(cls, val): + """Check that temp is in the allowed range.""" + if not GRAPHENE_TEMP_MIN <= val <= GRAPHENE_TEMP_MAX: + raise ValidationError( + "The parameter temp for graphene must be between " + f"'{GRAPHENE_TEMP_MIN}' and '{GRAPHENE_TEMP_MAX}'" + ) + return val + + @pd.validator("gamma", always=True) + def _validate_gamma(cls, val): + """Check that gamma is in the allowed range.""" + if not GRAPHENE_GAMMA_MIN <= val <= GRAPHENE_GAMMA_MAX: + raise ValidationError( + "The parameter gamma for graphene must be between " + f"'{GRAPHENE_GAMMA_MIN}' and '{GRAPHENE_GAMMA_MAX}'" + ) return val @property @@ -138,7 +170,11 @@ def medium(self) -> Medium2D: intraband_poles = intraband.pole_residue.poles interband_poles = interband.pole_residue.poles poles = intraband_poles + interband_poles - pole_residue = self._filter_poles(PoleResidue(poles=poles)) + pole_residue = self._filter_poles( + PoleResidue( + poles=poles, frequency_range=(GRAPHENE_FIT_FREQ_MIN, GRAPHENE_FIT_FREQ_MAX) + ) + ) return Medium2D(ss=pole_residue, tt=pole_residue) return Medium2D(ss=intraband, tt=intraband) @@ -170,14 +206,33 @@ def interband_pole_residue(self) -> PoleResidue: :class:`.PoleResidue` A pole-residue model for the interband term of graphene. """ - + freqs = np.linspace(GRAPHENE_FIT_FREQ_MIN, GRAPHENE_FIT_FREQ_MAX, GRAPHENE_FIT_NUM_FREQS) + sigma = self.interband_conductivity(freqs) nodes = self.interband_fit_freq_nodes + if nodes is None: + ind1 = np.argmin(np.imag(sigma)) + ind2 = np.nonzero( + (freqs > freqs[ind1]) + & (np.real(sigma) > GRAPHENE_FIT_NODE_CUTOFF * max(np.real(sigma))) + )[0][0] + ind2 = ind1 + GRAPHENE_FIT_NODE_MULTIPLIER * (ind2 - ind1) + nodes = [ + (GRAPHENE_FIT_FREQ_MIN, GRAPHENE_FIT_FREQ_MAX), + (freqs[ind1], freqs[ind2]), + ] + flattened_freqs = [freq for pair in nodes for freq in pair] - sigma = self.interband_conductivity(flattened_freqs) + sigma_inds = self.interband_conductivity(flattened_freqs) inds = [(2 * i, 2 * i + 1) for i in range(len(nodes))] - pole_residue = self._fit_interband_conductivity(flattened_freqs, sigma, inds) - return self._filter_poles(pole_residue) + pole_residue = self._fit_interband_conductivity(flattened_freqs, sigma_inds, inds) + pole_residue_filtered = self._filter_poles(pole_residue) + sigma_fit = pole_residue_filtered.sigma_model(freqs) + if not np.allclose(sigma, sigma_fit, rtol=0, atol=GRAPHENE_FIT_ATOL): + log.warning( + "Graphene fit may not be good. Try changing the physical or fitting parameters." + ) + return pole_residue_filtered def numerical_conductivity(self, freqs: List[float]) -> List[complex]: """Numerically calculate the conductivity. If this differs from the @@ -215,7 +270,10 @@ def interband_conductivity(self, freqs: List[float]) -> List[complex]: def fermi(E: float) -> float: """Fermi distribution.""" - return 1 / (np.exp((E - self.mu_c) / (K_B * self.temp)) + 1) + # catch overflow warning + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + return 1 / (np.exp((E - self.mu_c) / (K_B * self.temp)) + 1) def fermi_g(E: float) -> float: """Difference of fermi distributions.""" @@ -340,7 +398,15 @@ def get_pole_residue(coeffslist: List[List[float]]) -> PoleResidue: else: poles.append((root1, res1)) - return PoleResidue(poles=poles) + flipped_poles = [] + for (a, c) in poles: + if np.real(a) > 0: + flipped_poles += [(-1j * np.conj(1j * a), c)] + else: + flipped_poles += [(a, c)] + return PoleResidue( + poles=flipped_poles, frequency_range=(GRAPHENE_FIT_FREQ_MIN, GRAPHENE_FIT_FREQ_MAX) + ) # fitting works better with normalized quantities (THz and uS) omega_thz = 2 * np.pi * np.array(freqs) * 1e-12 @@ -356,17 +422,9 @@ def get_pole_residue(coeffslist: List[List[float]]) -> PoleResidue: # unnormalize, and convert from conductivity to permittivity poles = [(a * 1e12, -c / (a * EPSILON_0 * 1e6)) for (a, c) in pole_res.poles] - flipped_poles = [] - for (a, c) in poles: - if np.real(a) > 0: - flipped_poles += [(-1j * np.conj(1j * a), c)] - else: - flipped_poles += [(a, c)] - - # residue at omega = 0 - zero_res = -np.sum([c for (_, c) in flipped_poles]) - - return PoleResidue(poles=flipped_poles + [(0, zero_res)]) + return PoleResidue( + poles=poles, frequency_range=(GRAPHENE_FIT_FREQ_MIN, GRAPHENE_FIT_FREQ_MAX) + ) def _filter_poles(self, medium: PoleResidue) -> PoleResidue: """Clean up poles, merging poles at zero frequency.""" @@ -379,4 +437,7 @@ def _filter_poles(self, medium: PoleResidue) -> PoleResidue: continue else: poles += [(a, c)] - return PoleResidue(poles=poles + [(0, zero_res)]) + return PoleResidue( + poles=poles + [(0, zero_res)], + frequency_range=(GRAPHENE_FIT_FREQ_MIN, GRAPHENE_FIT_FREQ_MAX), + )