fix: Various critical fixes, field preview
parent
1d90f9ca7c
commit
dc76ab7688
26
TODO.md
26
TODO.md
|
@ -27,10 +27,11 @@
|
||||||
- [ ] Implement caching, such that the file will only download if the file doesn't already exist.
|
- [ ] Implement caching, such that the file will only download if the file doesn't already exist.
|
||||||
- [ ] Have a visual indicator for the current download status, with a manual re-download button.
|
- [ ] Have a visual indicator for the current download status, with a manual re-download button.
|
||||||
|
|
||||||
- [ ] File Import / Material Import
|
- [x] File Import / Material Import
|
||||||
- [ ] Dropdown to choose import format
|
- [x] Dropdown to choose import format
|
||||||
- [ ] File Import / Tidy3D File Import
|
- MERGED w/TIDY3D FILE IMPORT
|
||||||
- [ ] HDF and JSON file support, with appropriate choice of loose output socket.
|
- [x] File Import / Tidy3D File Import
|
||||||
|
- [x] HDF and JSON file support, with appropriate choice of loose output socket.
|
||||||
- [ ] File Import / Array File Import
|
- [ ] File Import / Array File Import
|
||||||
- [ ] Standardize 1D and 2D array loading/saving on numpy's savetxt with gzip enabled.
|
- [ ] Standardize 1D and 2D array loading/saving on numpy's savetxt with gzip enabled.
|
||||||
- [ ] Implement unit system input to guide conversion from numpy data type.
|
- [ ] Implement unit system input to guide conversion from numpy data type.
|
||||||
|
@ -40,8 +41,7 @@
|
||||||
## Outputs
|
## Outputs
|
||||||
- [x] Viewer
|
- [x] Viewer
|
||||||
- [ ] Remove image preview when disabling plots.
|
- [ ] Remove image preview when disabling plots.
|
||||||
- [ ] BUG: CTRL+SHIFT+CLICK not on a node shows an error; should just do nothing.
|
- [x] Auto-enable 3D preview when creating.
|
||||||
- [ ] Auto-enable 3D preview when creating.
|
|
||||||
- [ ] Test/support multiple viewers at the same time.
|
- [ ] Test/support multiple viewers at the same time.
|
||||||
- [ ] Pop-up w/multiline string as alternative to console print.
|
- [ ] Pop-up w/multiline string as alternative to console print.
|
||||||
|
|
||||||
|
@ -349,6 +349,7 @@
|
||||||
- [ ] Document the node tree cache semantics thoroughly; it's a VERY nuanced piece of logic, and its invariants may not survive Blender versions / the author's working memory
|
- [ ] Document the node tree cache semantics thoroughly; it's a VERY nuanced piece of logic, and its invariants may not survive Blender versions / the author's working memory
|
||||||
- [ ] Start standardizing nodes/sockets w/individualized SemVer
|
- [ ] Start standardizing nodes/sockets w/individualized SemVer
|
||||||
- Perhaps keep node / socket versions in a property, so that trying to load an incompatible major version hop can error w/indicator of where to find a compatible `blender_maxwell` version.
|
- Perhaps keep node / socket versions in a property, so that trying to load an incompatible major version hop can error w/indicator of where to find a compatible `blender_maxwell` version.
|
||||||
|
- [ ] `log.error` should invoke `self.report` in some Blender operator - used for errors that are due to usage error (which can't simply be prevented with UX design, like text file formatting of import), not due to error in the program.
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
- [ ] Make all modules available
|
- [ ] Make all modules available
|
||||||
|
@ -456,18 +457,31 @@ We're trying to do our part by reporting bugs we find!
|
||||||
This is where we keep track of them for now.
|
This is where we keep track of them for now.
|
||||||
|
|
||||||
## Blender Maxwell Bugs
|
## Blender Maxwell Bugs
|
||||||
|
- [ ] BUG: CTRL+SHIFT+CLICK not on a node shows an error; should just do nothing.
|
||||||
- [ ] Slow changing of socket sets / range on wave constant.
|
- [ ] Slow changing of socket sets / range on wave constant.
|
||||||
- [ ] API auth shouldn't show if everything is fine in Cloud Task socket
|
- [ ] API auth shouldn't show if everything is fine in Cloud Task socket
|
||||||
- [ ] Cloud task socket loads folders before its node shows, which can be slow (and error prone if offline)
|
- [ ] Cloud task socket loads folders before its node shows, which can be slow (and error prone if offline)
|
||||||
- [ ] Dispersive fit is slow, which means lag on normal operations that rely on the fit result - fit computation should be integrated into the node, and the output socket should only appear when the fit is available.
|
- [ ] Dispersive fit is slow, which means lag on normal operations that rely on the fit result - fit computation should be integrated into the node, and the output socket should only appear when the fit is available.
|
||||||
- [ ] Numerical, Physical Constant is missing entries
|
- [ ] Numerical, Physical Constant is missing entries
|
||||||
|
|
||||||
|
BROKE NODES
|
||||||
|
- [ ] Numerical constant doesn't switch types
|
||||||
|
- [ ] Blender constant is inexplicably mega laggy
|
||||||
|
- [ ] Web importer is just wonky in general
|
||||||
|
- [ ] JSON File exporter is having trouble with generic types (is that bad?)
|
||||||
|
|
||||||
|
- [ ] Extact Data needs flux settings
|
||||||
|
- [ ] Point dipole still has no preview
|
||||||
|
- [ ] Plane wave math still doesn't work and it has no preview
|
||||||
|
- [ ] Monitors need a way of setting infinite dimensions
|
||||||
|
|
||||||
## Blender Bugs
|
## Blender Bugs
|
||||||
Reported:
|
Reported:
|
||||||
- (SOLVED) <https://projects.blender.org/blender/blender/issues/119664>
|
- (SOLVED) <https://projects.blender.org/blender/blender/issues/119664>
|
||||||
|
|
||||||
Unreported:
|
Unreported:
|
||||||
- The `__mp_main__` bug.
|
- The `__mp_main__` bug.
|
||||||
|
- Animated properties within custom node trees don't update with the frame. See: <https://projects.blender.org/blender/blender/issues/66392>
|
||||||
|
|
||||||
## Tidy3D bugs
|
## Tidy3D bugs
|
||||||
Unreported:
|
Unreported:
|
||||||
|
|
|
@ -22,6 +22,7 @@ dependencies = [
|
||||||
"idna==3.3",
|
"idna==3.3",
|
||||||
"charset-normalizer==2.0.10",
|
"charset-normalizer==2.0.10",
|
||||||
"certifi==2021.10.8",
|
"certifi==2021.10.8",
|
||||||
|
"jax[cpu]>=0.4.26",
|
||||||
]
|
]
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = "~= 3.11"
|
requires-python = "~= 3.11"
|
||||||
|
|
|
@ -46,6 +46,9 @@ idna==3.3
|
||||||
importlib-metadata==6.11.0
|
importlib-metadata==6.11.0
|
||||||
# via dask
|
# via dask
|
||||||
# via tidy3d
|
# via tidy3d
|
||||||
|
jax==0.4.26
|
||||||
|
jaxlib==0.4.26
|
||||||
|
# via jax
|
||||||
jmespath==1.0.1
|
jmespath==1.0.1
|
||||||
# via boto3
|
# via boto3
|
||||||
# via botocore
|
# via botocore
|
||||||
|
@ -55,18 +58,27 @@ locket==1.0.0
|
||||||
# via partd
|
# via partd
|
||||||
matplotlib==3.8.3
|
matplotlib==3.8.3
|
||||||
# via tidy3d
|
# via tidy3d
|
||||||
|
ml-dtypes==0.4.0
|
||||||
|
# via jax
|
||||||
|
# via jaxlib
|
||||||
mpmath==1.3.0
|
mpmath==1.3.0
|
||||||
# via sympy
|
# via sympy
|
||||||
networkx==3.2
|
networkx==3.2
|
||||||
numpy==1.24.3
|
numpy==1.24.3
|
||||||
# via contourpy
|
# via contourpy
|
||||||
# via h5py
|
# via h5py
|
||||||
|
# via jax
|
||||||
|
# via jaxlib
|
||||||
# via matplotlib
|
# via matplotlib
|
||||||
|
# via ml-dtypes
|
||||||
|
# via opt-einsum
|
||||||
# via scipy
|
# via scipy
|
||||||
# via shapely
|
# via shapely
|
||||||
# via tidy3d
|
# via tidy3d
|
||||||
# via trimesh
|
# via trimesh
|
||||||
# via xarray
|
# via xarray
|
||||||
|
opt-einsum==3.3.0
|
||||||
|
# via jax
|
||||||
packaging==24.0
|
packaging==24.0
|
||||||
# via dask
|
# via dask
|
||||||
# via h5netcdf
|
# via h5netcdf
|
||||||
|
@ -112,6 +124,8 @@ ruff==0.3.2
|
||||||
s3transfer==0.5.2
|
s3transfer==0.5.2
|
||||||
# via boto3
|
# via boto3
|
||||||
scipy==1.12.0
|
scipy==1.12.0
|
||||||
|
# via jax
|
||||||
|
# via jaxlib
|
||||||
# via tidy3d
|
# via tidy3d
|
||||||
shapely==2.0.3
|
shapely==2.0.3
|
||||||
# via tidy3d
|
# via tidy3d
|
||||||
|
|
|
@ -45,6 +45,9 @@ idna==3.3
|
||||||
importlib-metadata==6.11.0
|
importlib-metadata==6.11.0
|
||||||
# via dask
|
# via dask
|
||||||
# via tidy3d
|
# via tidy3d
|
||||||
|
jax==0.4.26
|
||||||
|
jaxlib==0.4.26
|
||||||
|
# via jax
|
||||||
jmespath==1.0.1
|
jmespath==1.0.1
|
||||||
# via boto3
|
# via boto3
|
||||||
# via botocore
|
# via botocore
|
||||||
|
@ -54,18 +57,27 @@ locket==1.0.0
|
||||||
# via partd
|
# via partd
|
||||||
matplotlib==3.8.3
|
matplotlib==3.8.3
|
||||||
# via tidy3d
|
# via tidy3d
|
||||||
|
ml-dtypes==0.4.0
|
||||||
|
# via jax
|
||||||
|
# via jaxlib
|
||||||
mpmath==1.3.0
|
mpmath==1.3.0
|
||||||
# via sympy
|
# via sympy
|
||||||
networkx==3.2
|
networkx==3.2
|
||||||
numpy==1.24.3
|
numpy==1.24.3
|
||||||
# via contourpy
|
# via contourpy
|
||||||
# via h5py
|
# via h5py
|
||||||
|
# via jax
|
||||||
|
# via jaxlib
|
||||||
# via matplotlib
|
# via matplotlib
|
||||||
|
# via ml-dtypes
|
||||||
|
# via opt-einsum
|
||||||
# via scipy
|
# via scipy
|
||||||
# via shapely
|
# via shapely
|
||||||
# via tidy3d
|
# via tidy3d
|
||||||
# via trimesh
|
# via trimesh
|
||||||
# via xarray
|
# via xarray
|
||||||
|
opt-einsum==3.3.0
|
||||||
|
# via jax
|
||||||
packaging==24.0
|
packaging==24.0
|
||||||
# via dask
|
# via dask
|
||||||
# via h5netcdf
|
# via h5netcdf
|
||||||
|
@ -110,6 +122,8 @@ rtree==1.2.0
|
||||||
s3transfer==0.5.2
|
s3transfer==0.5.2
|
||||||
# via boto3
|
# via boto3
|
||||||
scipy==1.12.0
|
scipy==1.12.0
|
||||||
|
# via jax
|
||||||
|
# via jaxlib
|
||||||
# via tidy3d
|
# via tidy3d
|
||||||
shapely==2.0.3
|
shapely==2.0.3
|
||||||
# via tidy3d
|
# via tidy3d
|
||||||
|
|
|
@ -49,7 +49,7 @@ class GeoNodes(enum.StrEnum):
|
||||||
StructurePrimitiveCone = '_structure_primitive_cone'
|
StructurePrimitiveCone = '_structure_primitive_cone'
|
||||||
## Monitor
|
## Monitor
|
||||||
MonitorEHField = '_monitor_eh_field'
|
MonitorEHField = '_monitor_eh_field'
|
||||||
MonitorFieldPowerFlux = '_monitor_field_power_flux'
|
MonitorPowerFlux = '_monitor_power_flux'
|
||||||
MonitorEpsTensor = '_monitor_eps_tensor'
|
MonitorEpsTensor = '_monitor_eps_tensor'
|
||||||
MonitorDiffraction = '_monitor_diffraction'
|
MonitorDiffraction = '_monitor_diffraction'
|
||||||
MonitorProjCartEHField = '_monitor_proj_eh_field'
|
MonitorProjCartEHField = '_monitor_proj_eh_field'
|
||||||
|
@ -114,7 +114,7 @@ GN_PARENT_PATHS: dict[GeoNodes, Path] = {
|
||||||
GeoNodes.StructurePrimitiveCone: GN_INTERNAL_STRUCTURES_PATH,
|
GeoNodes.StructurePrimitiveCone: GN_INTERNAL_STRUCTURES_PATH,
|
||||||
## Monitor
|
## Monitor
|
||||||
GeoNodes.MonitorEHField: GN_INTERNAL_STRUCTURES_PATH,
|
GeoNodes.MonitorEHField: GN_INTERNAL_STRUCTURES_PATH,
|
||||||
GeoNodes.MonitorFieldPowerFlux: GN_INTERNAL_STRUCTURES_PATH,
|
GeoNodes.MonitorPowerFlux: GN_INTERNAL_STRUCTURES_PATH,
|
||||||
GeoNodes.MonitorEpsTensor: GN_INTERNAL_STRUCTURES_PATH,
|
GeoNodes.MonitorEpsTensor: GN_INTERNAL_STRUCTURES_PATH,
|
||||||
GeoNodes.MonitorDiffraction: GN_INTERNAL_STRUCTURES_PATH,
|
GeoNodes.MonitorDiffraction: GN_INTERNAL_STRUCTURES_PATH,
|
||||||
GeoNodes.MonitorProjCartEHField: GN_INTERNAL_STRUCTURES_PATH,
|
GeoNodes.MonitorProjCartEHField: GN_INTERNAL_STRUCTURES_PATH,
|
||||||
|
|
|
@ -5,6 +5,7 @@ import typing as typ
|
||||||
from types import MappingProxyType
|
from types import MappingProxyType
|
||||||
|
|
||||||
# import colour ## TODO
|
# import colour ## TODO
|
||||||
|
import jax
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import sympy as sp
|
import sympy as sp
|
||||||
import sympy.physics.units as spu
|
import sympy.physics.units as spu
|
||||||
|
@ -105,7 +106,7 @@ class DataValueArray:
|
||||||
"""A simple, flat array of values with an optionally-attached unit.
|
"""A simple, flat array of values with an optionally-attached unit.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
values: A 1D array-like object of arbitrary numerical type.
|
values: An ND array-like object of arbitrary numerical type.
|
||||||
unit: A `sympy` unit.
|
unit: A `sympy` unit.
|
||||||
None if unitless.
|
None if unitless.
|
||||||
"""
|
"""
|
||||||
|
@ -181,12 +182,14 @@ class LazyDataValueRange:
|
||||||
scaling: typx.Literal['lin', 'geom', 'log'] = 'lin'
|
scaling: typx.Literal['lin', 'geom', 'log'] = 'lin'
|
||||||
|
|
||||||
has_unit: bool = False
|
has_unit: bool = False
|
||||||
|
unit: spu.Quantity = False
|
||||||
|
|
||||||
def rescale_to_unit(self, unit: spu.Quantity) -> typ.Self:
|
def rescale_to_unit(self, unit: spu.Quantity) -> typ.Self:
|
||||||
if self.has_unit:
|
if self.has_unit:
|
||||||
return LazyDataValueRange(
|
return LazyDataValueRange(
|
||||||
symbols=self.symbols,
|
symbols=self.symbols,
|
||||||
has_unit=self.has_unit,
|
has_unit=self.has_unit,
|
||||||
|
unit=unit,
|
||||||
start=spu.convert_to(self.start, unit),
|
start=spu.convert_to(self.start, unit),
|
||||||
stop=spu.convert_to(self.stop, unit),
|
stop=spu.convert_to(self.stop, unit),
|
||||||
steps=self.steps,
|
steps=self.steps,
|
||||||
|
@ -205,8 +208,13 @@ class LazyDataValueRange:
|
||||||
return LazyDataValueRange(
|
return LazyDataValueRange(
|
||||||
symbols=self.symbols,
|
symbols=self.symbols,
|
||||||
has_unit=self.has_unit,
|
has_unit=self.has_unit,
|
||||||
start=bound_cb(self.start if not reverse else self.stop),
|
unit=self.unit,
|
||||||
stop=bound_cb(self.stop if not reverse else self.start),
|
start=spu.convert_to(
|
||||||
|
bound_cb(self.start if not reverse else self.stop), self.unit
|
||||||
|
),
|
||||||
|
stop=spu.convert_to(
|
||||||
|
bound_cb(self.stop if not reverse else self.start), self.unit
|
||||||
|
),
|
||||||
steps=self.steps,
|
steps=self.steps,
|
||||||
scaling=self.scaling,
|
scaling=self.scaling,
|
||||||
)
|
)
|
||||||
|
@ -276,3 +284,37 @@ class LazyDataValueSpectrum:
|
||||||
values=self.as_func(*list(symbol_values.values())),
|
values=self.as_func(*list(symbol_values.values())),
|
||||||
values_unit=self.value_unit,
|
values_unit=self.value_unit,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#####################
|
||||||
|
## - Data Pipeline
|
||||||
|
#####################
|
||||||
|
# @dataclasses.dataclass(frozen=True, kw_only=True)
|
||||||
|
# class DataPipelineDim:
|
||||||
|
# unit: spu.Quantity | None
|
||||||
|
#
|
||||||
|
# class DataPipelineDimType(enum.StrEnum):
|
||||||
|
# # Map Inputs
|
||||||
|
# Time = enum.auto()
|
||||||
|
# Freq = enum.auto()
|
||||||
|
# Space3D = enum.auto()
|
||||||
|
# DiffOrder = enum.auto()
|
||||||
|
#
|
||||||
|
# # Map Inputs
|
||||||
|
# Power = enum.auto()
|
||||||
|
# EVec = enum.auto()
|
||||||
|
# HVec = enum.auto()
|
||||||
|
# RelPerm = enum.auto()
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# @dataclasses.dataclass(frozen=True, kw_only=True)
|
||||||
|
# class LazyDataPipeline:
|
||||||
|
# dims: list[DataPipelineDim]
|
||||||
|
#
|
||||||
|
# def _callable(self):
|
||||||
|
# """JITs the current pipeline of functions with `jax`."""
|
||||||
|
#
|
||||||
|
# def __call__(self):
|
||||||
|
# pass
|
||||||
|
|
|
@ -1,18 +1,19 @@
|
||||||
from .node_cats import NodeCategory as NC
|
from .node_cats import NodeCategory as NC
|
||||||
|
|
||||||
NODE_CAT_LABELS = {
|
NODE_CAT_LABELS = {
|
||||||
|
# Analysis/
|
||||||
|
NC.MAXWELLSIM_ANALYSIS: 'Analysis',
|
||||||
|
NC.MAXWELLSIM_ANALYSIS_MATH: 'Math',
|
||||||
# Inputs/
|
# Inputs/
|
||||||
NC.MAXWELLSIM_INPUTS: 'Inputs',
|
NC.MAXWELLSIM_INPUTS: 'Inputs',
|
||||||
NC.MAXWELLSIM_INPUTS_IMPORTERS: 'Importers',
|
|
||||||
NC.MAXWELLSIM_INPUTS_SCENE: 'Scene',
|
NC.MAXWELLSIM_INPUTS_SCENE: 'Scene',
|
||||||
NC.MAXWELLSIM_INPUTS_PARAMETERS: 'Parameters',
|
|
||||||
NC.MAXWELLSIM_INPUTS_CONSTANTS: 'Constants',
|
NC.MAXWELLSIM_INPUTS_CONSTANTS: 'Constants',
|
||||||
NC.MAXWELLSIM_INPUTS_LISTS: 'Lists',
|
NC.MAXWELLSIM_INPUTS_FILEIMPORTERS: 'File Importers',
|
||||||
|
NC.MAXWELLSIM_INPUTS_WEBIMPORTERS: 'Web Importers',
|
||||||
# Outputs/
|
# Outputs/
|
||||||
NC.MAXWELLSIM_OUTPUTS: 'Outputs',
|
NC.MAXWELLSIM_OUTPUTS: 'Outputs',
|
||||||
NC.MAXWELLSIM_OUTPUTS_VIEWERS: 'Viewers',
|
NC.MAXWELLSIM_OUTPUTS_FILEEXPORTERS: 'File Exporters',
|
||||||
NC.MAXWELLSIM_OUTPUTS_EXPORTERS: 'Exporters',
|
NC.MAXWELLSIM_OUTPUTS_WEBEXPORTERS: 'Web Exporters',
|
||||||
NC.MAXWELLSIM_OUTPUTS_PLOTTERS: 'Plotters',
|
|
||||||
# Sources/
|
# Sources/
|
||||||
NC.MAXWELLSIM_SOURCES: 'Sources',
|
NC.MAXWELLSIM_SOURCES: 'Sources',
|
||||||
NC.MAXWELLSIM_SOURCES_TEMPORALSHAPES: 'Temporal Shapes',
|
NC.MAXWELLSIM_SOURCES_TEMPORALSHAPES: 'Temporal Shapes',
|
||||||
|
@ -27,14 +28,10 @@ NODE_CAT_LABELS = {
|
||||||
NC.MAXWELLSIM_BOUNDS_BOUNDCONDS: 'Bound Conds',
|
NC.MAXWELLSIM_BOUNDS_BOUNDCONDS: 'Bound Conds',
|
||||||
# Monitors/
|
# Monitors/
|
||||||
NC.MAXWELLSIM_MONITORS: 'Monitors',
|
NC.MAXWELLSIM_MONITORS: 'Monitors',
|
||||||
NC.MAXWELLSIM_MONITORS_NEARFIELDPROJECTIONS: 'Near-Field Projections',
|
NC.MAXWELLSIM_MONITORS_PROJECTED: 'Projected',
|
||||||
# Simulations/
|
# Simulations/
|
||||||
NC.MAXWELLSIM_SIMS: 'Simulations',
|
NC.MAXWELLSIM_SIMS: 'Simulations',
|
||||||
NC.MAXWELLSIM_SIMGRIDAXES: 'Sim Grid Axes',
|
NC.MAXWELLSIM_SIMS_SIMGRIDAXES: 'Sim Grid Axes',
|
||||||
# Utilities/
|
# Utilities/
|
||||||
NC.MAXWELLSIM_UTILITIES: 'Utilities',
|
NC.MAXWELLSIM_UTILITIES: 'Utilities',
|
||||||
NC.MAXWELLSIM_UTILITIES_CONVERTERS: 'Converters',
|
|
||||||
NC.MAXWELLSIM_UTILITIES_OPERATIONS: 'Operations',
|
|
||||||
# Viz/
|
|
||||||
NC.MAXWELLSIM_VIZ: 'Viz',
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,19 +7,21 @@ from ....utils.blender_type_enum import BlenderTypeEnum, wrap_values_in_MT
|
||||||
class NodeCategory(BlenderTypeEnum):
|
class NodeCategory(BlenderTypeEnum):
|
||||||
MAXWELLSIM = enum.auto()
|
MAXWELLSIM = enum.auto()
|
||||||
|
|
||||||
|
# Analysis/
|
||||||
|
MAXWELLSIM_ANALYSIS = enum.auto()
|
||||||
|
MAXWELLSIM_ANALYSIS_MATH = enum.auto()
|
||||||
|
|
||||||
# Inputs/
|
# Inputs/
|
||||||
MAXWELLSIM_INPUTS = enum.auto()
|
MAXWELLSIM_INPUTS = enum.auto()
|
||||||
MAXWELLSIM_INPUTS_IMPORTERS = enum.auto()
|
|
||||||
MAXWELLSIM_INPUTS_SCENE = enum.auto()
|
MAXWELLSIM_INPUTS_SCENE = enum.auto()
|
||||||
MAXWELLSIM_INPUTS_PARAMETERS = enum.auto()
|
|
||||||
MAXWELLSIM_INPUTS_CONSTANTS = enum.auto()
|
MAXWELLSIM_INPUTS_CONSTANTS = enum.auto()
|
||||||
MAXWELLSIM_INPUTS_LISTS = enum.auto()
|
MAXWELLSIM_INPUTS_FILEIMPORTERS = enum.auto()
|
||||||
|
MAXWELLSIM_INPUTS_WEBIMPORTERS = enum.auto()
|
||||||
|
|
||||||
# Outputs/
|
# Outputs/
|
||||||
MAXWELLSIM_OUTPUTS = enum.auto()
|
MAXWELLSIM_OUTPUTS = enum.auto()
|
||||||
MAXWELLSIM_OUTPUTS_VIEWERS = enum.auto()
|
MAXWELLSIM_OUTPUTS_FILEEXPORTERS = enum.auto()
|
||||||
MAXWELLSIM_OUTPUTS_EXPORTERS = enum.auto()
|
MAXWELLSIM_OUTPUTS_WEBEXPORTERS = enum.auto()
|
||||||
MAXWELLSIM_OUTPUTS_PLOTTERS = enum.auto()
|
|
||||||
|
|
||||||
# Sources/
|
# Sources/
|
||||||
MAXWELLSIM_SOURCES = enum.auto()
|
MAXWELLSIM_SOURCES = enum.auto()
|
||||||
|
@ -39,19 +41,14 @@ class NodeCategory(BlenderTypeEnum):
|
||||||
|
|
||||||
# Monitors/
|
# Monitors/
|
||||||
MAXWELLSIM_MONITORS = enum.auto()
|
MAXWELLSIM_MONITORS = enum.auto()
|
||||||
MAXWELLSIM_MONITORS_NEARFIELDPROJECTIONS = enum.auto()
|
MAXWELLSIM_MONITORS_PROJECTED = enum.auto()
|
||||||
|
|
||||||
# Simulations/
|
# Simulations/
|
||||||
MAXWELLSIM_SIMS = enum.auto()
|
MAXWELLSIM_SIMS = enum.auto()
|
||||||
MAXWELLSIM_SIMGRIDAXES = enum.auto()
|
MAXWELLSIM_SIMS_SIMGRIDAXES = enum.auto()
|
||||||
|
|
||||||
# Utilities/
|
# Utilities/
|
||||||
MAXWELLSIM_UTILITIES = enum.auto()
|
MAXWELLSIM_UTILITIES = enum.auto()
|
||||||
MAXWELLSIM_UTILITIES_CONVERTERS = enum.auto()
|
|
||||||
MAXWELLSIM_UTILITIES_OPERATIONS = enum.auto()
|
|
||||||
|
|
||||||
# Viz/
|
|
||||||
MAXWELLSIM_VIZ = enum.auto()
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_tree(cls):
|
def get_tree(cls):
|
||||||
|
|
|
@ -8,140 +8,114 @@ from ....utils.blender_type_enum import (
|
||||||
|
|
||||||
@append_cls_name_to_values
|
@append_cls_name_to_values
|
||||||
class NodeType(BlenderTypeEnum):
|
class NodeType(BlenderTypeEnum):
|
||||||
KitchenSink = enum.auto()
|
#KitchenSink = enum.auto()
|
||||||
|
|
||||||
|
# Analysis
|
||||||
|
Viz = enum.auto()
|
||||||
|
ExtractData = enum.auto()
|
||||||
|
## Analysis / Math
|
||||||
|
MapMath = enum.auto()
|
||||||
|
FilterMath = enum.auto()
|
||||||
|
ReduceMath = enum.auto()
|
||||||
|
OperateMath = enum.auto()
|
||||||
|
|
||||||
# Inputs
|
# Inputs
|
||||||
|
WaveConstant = enum.auto()
|
||||||
UnitSystem = enum.auto()
|
UnitSystem = enum.auto()
|
||||||
|
|
||||||
## Inputs / Scene
|
## Inputs / Scene
|
||||||
Time = enum.auto()
|
#Time = enum.auto()
|
||||||
|
|
||||||
## Inputs / Web Importers
|
## Inputs / Web Importers
|
||||||
Tidy3DWebImporter = enum.auto()
|
Tidy3DWebImporter = enum.auto()
|
||||||
|
|
||||||
## Inputs / File Importers
|
## Inputs / File Importers
|
||||||
Tidy3DFileImporter = enum.auto()
|
Tidy3DFileImporter = enum.auto()
|
||||||
|
|
||||||
## Inputs / Parameters
|
|
||||||
NumberParameter = enum.auto()
|
|
||||||
PhysicalParameter = enum.auto()
|
|
||||||
|
|
||||||
## Inputs / Constants
|
## Inputs / Constants
|
||||||
WaveConstant = enum.auto()
|
|
||||||
ScientificConstant = enum.auto()
|
ScientificConstant = enum.auto()
|
||||||
NumberConstant = enum.auto()
|
NumberConstant = enum.auto()
|
||||||
PhysicalConstant = enum.auto()
|
PhysicalConstant = enum.auto()
|
||||||
BlenderConstant = enum.auto()
|
BlenderConstant = enum.auto()
|
||||||
|
|
||||||
## Inputs / Lists
|
|
||||||
RealList = enum.auto()
|
|
||||||
ComplexList = enum.auto()
|
|
||||||
|
|
||||||
|
|
||||||
# Outputs
|
# Outputs
|
||||||
## Outputs / Viewers
|
|
||||||
Viewer = enum.auto()
|
Viewer = enum.auto()
|
||||||
ValueViewer = enum.auto()
|
## Outputs / File Exporters
|
||||||
ConsoleViewer = enum.auto()
|
|
||||||
|
|
||||||
## Outputs / Exporters
|
|
||||||
JSONFileExporter = enum.auto()
|
|
||||||
Tidy3DWebExporter = enum.auto()
|
Tidy3DWebExporter = enum.auto()
|
||||||
|
## Outputs / Web Exporters
|
||||||
|
JSONFileExporter = enum.auto()
|
||||||
|
|
||||||
# Sources
|
# Sources
|
||||||
## Sources / Temporal Shapes
|
|
||||||
GaussianPulseTemporalShape = enum.auto()
|
|
||||||
ContinuousWaveTemporalShape = enum.auto()
|
|
||||||
ListTemporalShape = enum.auto()
|
|
||||||
|
|
||||||
## Sources /
|
## Sources /
|
||||||
PointDipoleSource = enum.auto()
|
PointDipoleSource = enum.auto()
|
||||||
UniformCurrentSource = enum.auto()
|
|
||||||
PlaneWaveSource = enum.auto()
|
PlaneWaveSource = enum.auto()
|
||||||
ModeSource = enum.auto()
|
UniformCurrentSource = enum.auto()
|
||||||
GaussianBeamSource = enum.auto()
|
#ModeSource = enum.auto()
|
||||||
AstigmaticGaussianBeamSource = enum.auto()
|
#GaussianBeamSource = enum.auto()
|
||||||
TFSFSource = enum.auto()
|
#AstigmaticGaussianBeamSource = enum.auto()
|
||||||
|
#TFSFSource = enum.auto()
|
||||||
EHEquivalenceSource = enum.auto()
|
#EHEquivalenceSource = enum.auto()
|
||||||
EHSource = enum.auto()
|
#EHSource = enum.auto()
|
||||||
|
## Sources / Temporal Shapes
|
||||||
|
GaussianPulseTemporalShape = enum.auto()
|
||||||
|
#ContinuousWaveTemporalShape = enum.auto()
|
||||||
|
#ArrayTemporalShape = enum.auto()
|
||||||
|
|
||||||
# Mediums
|
# Mediums
|
||||||
LibraryMedium = enum.auto()
|
LibraryMedium = enum.auto()
|
||||||
|
#PECMedium = enum.auto()
|
||||||
PECMedium = enum.auto()
|
#IsotropicMedium = enum.auto()
|
||||||
IsotropicMedium = enum.auto()
|
#AnisotropicMedium = enum.auto()
|
||||||
AnisotropicMedium = enum.auto()
|
#TripleSellmeierMedium = enum.auto()
|
||||||
|
#SellmeierMedium = enum.auto()
|
||||||
TripleSellmeierMedium = enum.auto()
|
#PoleResidueMedium = enum.auto()
|
||||||
SellmeierMedium = enum.auto()
|
#DrudeMedium = enum.auto()
|
||||||
PoleResidueMedium = enum.auto()
|
#DrudeLorentzMedium = enum.auto()
|
||||||
DrudeMedium = enum.auto()
|
#DebyeMedium = enum.auto()
|
||||||
DrudeLorentzMedium = enum.auto()
|
|
||||||
DebyeMedium = enum.auto()
|
|
||||||
|
|
||||||
## Mediums / Non-Linearities
|
## Mediums / Non-Linearities
|
||||||
AddNonLinearity = enum.auto()
|
#AddNonLinearity = enum.auto()
|
||||||
ChiThreeSusceptibilityNonLinearity = enum.auto()
|
#ChiThreeSusceptibilityNonLinearity = enum.auto()
|
||||||
TwoPhotonAbsorptionNonLinearity = enum.auto()
|
#TwoPhotonAbsorptionNonLinearity = enum.auto()
|
||||||
KerrNonLinearity = enum.auto()
|
#KerrNonLinearity = enum.auto()
|
||||||
|
|
||||||
# Structures
|
# Structures
|
||||||
ObjectStructure = enum.auto()
|
#ObjectStructure = enum.auto()
|
||||||
GeoNodesStructure = enum.auto()
|
GeoNodesStructure = enum.auto()
|
||||||
ScriptedStructure = enum.auto()
|
#ScriptedStructure = enum.auto()
|
||||||
|
|
||||||
## Structures / Primitives
|
## Structures / Primitives
|
||||||
BoxStructure = enum.auto()
|
BoxStructure = enum.auto()
|
||||||
SphereStructure = enum.auto()
|
SphereStructure = enum.auto()
|
||||||
CylinderStructure = enum.auto()
|
#CylinderStructure = enum.auto()
|
||||||
|
|
||||||
# Bounds
|
# Bounds
|
||||||
BoundConds = enum.auto()
|
BoundConds = enum.auto()
|
||||||
|
## Bounds / Bound Conds
|
||||||
## Bounds / Bound Faces
|
|
||||||
PMLBoundCond = enum.auto()
|
PMLBoundCond = enum.auto()
|
||||||
PECBoundCond = enum.auto()
|
PECBoundCond = enum.auto()
|
||||||
PMCBoundCond = enum.auto()
|
PMCBoundCond = enum.auto()
|
||||||
|
|
||||||
BlochBoundCond = enum.auto()
|
BlochBoundCond = enum.auto()
|
||||||
PeriodicBoundCond = enum.auto()
|
PeriodicBoundCond = enum.auto()
|
||||||
AbsorbingBoundCond = enum.auto()
|
AbsorbingBoundCond = enum.auto()
|
||||||
|
|
||||||
# Monitors
|
# Monitors
|
||||||
EHFieldMonitor = enum.auto()
|
EHFieldMonitor = enum.auto()
|
||||||
FieldPowerFluxMonitor = enum.auto()
|
PowerFluxMonitor = enum.auto()
|
||||||
EpsilonTensorMonitor = enum.auto()
|
#EpsilonTensorMonitor = enum.auto()
|
||||||
DiffractionMonitor = enum.auto()
|
#DiffractionMonitor = enum.auto()
|
||||||
|
## Monitors / Projected
|
||||||
## Monitors / Near-Field Projections
|
#CartesianNearFieldProjectionMonitor = enum.auto()
|
||||||
CartesianNearFieldProjectionMonitor = enum.auto()
|
#ObservationAngleNearFieldProjectionMonitor = enum.auto()
|
||||||
ObservationAngleNearFieldProjectionMonitor = enum.auto()
|
#KSpaceNearFieldProjectionMonitor = enum.auto()
|
||||||
KSpaceNearFieldProjectionMonitor = enum.auto()
|
|
||||||
|
|
||||||
# Sims
|
# Sims
|
||||||
|
FDTDSim = enum.auto()
|
||||||
SimDomain = enum.auto()
|
SimDomain = enum.auto()
|
||||||
SimGrid = enum.auto()
|
SimGrid = enum.auto()
|
||||||
|
|
||||||
## Sims / Sim Grid Axis
|
## Sims / Sim Grid Axis
|
||||||
AutomaticSimGridAxis = enum.auto()
|
#AutomaticSimGridAxis = enum.auto()
|
||||||
ManualSimGridAxis = enum.auto()
|
#ManualSimGridAxis = enum.auto()
|
||||||
UniformSimGridAxis = enum.auto()
|
#UniformSimGridAxis = enum.auto()
|
||||||
ArraySimGridAxis = enum.auto()
|
#ArraySimGridAxis = enum.auto()
|
||||||
|
|
||||||
## Sim /
|
|
||||||
FDTDSim = enum.auto()
|
|
||||||
|
|
||||||
# Utilities
|
# Utilities
|
||||||
Combine = enum.auto()
|
Combine = enum.auto()
|
||||||
Separate = enum.auto()
|
Separate = enum.auto()
|
||||||
Math = enum.auto()
|
|
||||||
|
|
||||||
## Utilities / Converters
|
|
||||||
WaveConverter = enum.auto()
|
|
||||||
|
|
||||||
## Utilities / Operations
|
|
||||||
ArrayOperation = enum.auto()
|
|
||||||
|
|
||||||
# Viz
|
|
||||||
FDTDSimDataViz = enum.auto()
|
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
import io
|
import io
|
||||||
|
import time
|
||||||
import typing as typ
|
import typing as typ
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
|
import jax
|
||||||
|
import jax.numpy as jnp
|
||||||
|
import matplotlib
|
||||||
import matplotlib.axis as mpl_ax
|
import matplotlib.axis as mpl_ax
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import typing_extensions as typx
|
import typing_extensions as typx
|
||||||
|
@ -14,6 +18,63 @@ log = logger.get(__name__)
|
||||||
AREA_TYPE = 'IMAGE_EDITOR'
|
AREA_TYPE = 'IMAGE_EDITOR'
|
||||||
SPACE_TYPE = 'IMAGE_EDITOR'
|
SPACE_TYPE = 'IMAGE_EDITOR'
|
||||||
|
|
||||||
|
# Colormap
|
||||||
|
_MPL_CM = matplotlib.cm.get_cmap('viridis', 512)
|
||||||
|
VIRIDIS_COLORMAP = jnp.array([_MPL_CM(i)[:3] for i in range(512)])
|
||||||
|
|
||||||
|
|
||||||
|
def apply_colormap(normalized_data, colormap):
|
||||||
|
# Linear interpolation between colormap points
|
||||||
|
n_colors = colormap.shape[0]
|
||||||
|
indices = normalized_data * (n_colors - 1)
|
||||||
|
lower_idx = jnp.floor(indices).astype(jnp.int32)
|
||||||
|
upper_idx = jnp.ceil(indices).astype(jnp.int32)
|
||||||
|
alpha = indices - lower_idx
|
||||||
|
|
||||||
|
lower_colors = jax.vmap(lambda i: colormap[i])(lower_idx)
|
||||||
|
upper_colors = jax.vmap(lambda i: colormap[i])(upper_idx)
|
||||||
|
|
||||||
|
return (1 - alpha)[..., None] * lower_colors + alpha[..., None] * upper_colors
|
||||||
|
|
||||||
|
|
||||||
|
@jax.jit
|
||||||
|
def rgba_image_from_xyzf__viridis(xyz_freq):
|
||||||
|
amplitude = jnp.abs(jnp.squeeze(xyz_freq))
|
||||||
|
amplitude_normalized = (amplitude - amplitude.min()) / (
|
||||||
|
amplitude.max() - amplitude.min()
|
||||||
|
)
|
||||||
|
rgb_array = apply_colormap(amplitude_normalized, VIRIDIS_COLORMAP)
|
||||||
|
alpha_channel = jnp.ones_like(amplitude_normalized)
|
||||||
|
return jnp.dstack((rgb_array, alpha_channel))
|
||||||
|
|
||||||
|
|
||||||
|
@jax.jit
|
||||||
|
def rgba_image_from_xyzf__grayscale(xyz_freq):
|
||||||
|
amplitude = jnp.abs(jnp.squeeze(xyz_freq))
|
||||||
|
amplitude_normalized = (amplitude - amplitude.min()) / (
|
||||||
|
amplitude.max() - amplitude.min()
|
||||||
|
)
|
||||||
|
rgb_array = jnp.stack([amplitude_normalized] * 3, axis=-1)
|
||||||
|
alpha_channel = jnp.ones_like(amplitude_normalized)
|
||||||
|
return jnp.dstack((rgb_array, alpha_channel))
|
||||||
|
|
||||||
|
|
||||||
|
def rgba_image_from_xyzf(xyz_freq, colormap: str | None = None):
|
||||||
|
"""RGBA Image from Squeezable XYZ-Freq w/fixed freq.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
xyz_freq: Shape (xlen, ylen, zlen), one dimension has length 1.
|
||||||
|
width_px: Pixel width to resize the image to.
|
||||||
|
height: Pixel height to resize the image to.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Image as a JAX array of shape (height, width, 3)
|
||||||
|
"""
|
||||||
|
if colormap == 'VIRIDIS':
|
||||||
|
return rgba_image_from_xyzf__viridis(xyz_freq)
|
||||||
|
if colormap == 'GRAYSCALE':
|
||||||
|
return rgba_image_from_xyzf__grayscale(xyz_freq)
|
||||||
|
|
||||||
|
|
||||||
class ManagedBLImage(ct.schemas.ManagedObj):
|
class ManagedBLImage(ct.schemas.ManagedObj):
|
||||||
managed_obj_type = ct.ManagedObjType.ManagedBLImage
|
managed_obj_type = ct.ManagedObjType.ManagedBLImage
|
||||||
|
@ -81,6 +142,7 @@ class ManagedBLImage(ct.schemas.ManagedObj):
|
||||||
self.name,
|
self.name,
|
||||||
width=width_px,
|
width=width_px,
|
||||||
height=height_px,
|
height=height_px,
|
||||||
|
float_buffer=dtype == 'float32',
|
||||||
)
|
)
|
||||||
|
|
||||||
return bl_image
|
return bl_image
|
||||||
|
@ -120,18 +182,14 @@ class ManagedBLImage(ct.schemas.ManagedObj):
|
||||||
self.preview_space.image = bl_image
|
self.preview_space.image = bl_image
|
||||||
|
|
||||||
####################
|
####################
|
||||||
# - Special Methods
|
# - Image Geometry
|
||||||
####################
|
####################
|
||||||
def mpl_plot_to_image(
|
def gen_image_geometry(
|
||||||
self,
|
self,
|
||||||
func_plotter: typ.Callable[[mpl_ax.Axis], None],
|
|
||||||
width_inches: float | None = None,
|
width_inches: float | None = None,
|
||||||
height_inches: float | None = None,
|
height_inches: float | None = None,
|
||||||
dpi: int | None = None,
|
dpi: int | None = None,
|
||||||
bl_select: bool = False,
|
|
||||||
):
|
):
|
||||||
import matplotlib.pyplot as plt
|
|
||||||
|
|
||||||
# Compute Image Geometry
|
# Compute Image Geometry
|
||||||
if preview_area := self.preview_area:
|
if preview_area := self.preview_area:
|
||||||
# Retrieve DPI from Blender Preferences
|
# Retrieve DPI from Blender Preferences
|
||||||
|
@ -159,44 +217,103 @@ class ManagedBLImage(ct.schemas.ManagedObj):
|
||||||
msg = 'There must either be a preview area, or defined `width_inches`, `height_inches`, and `dpi`'
|
msg = 'There must either be a preview area, or defined `width_inches`, `height_inches`, and `dpi`'
|
||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
|
|
||||||
# Compute Plot Dimensions
|
|
||||||
aspect_ratio = _width_inches / _height_inches
|
aspect_ratio = _width_inches / _height_inches
|
||||||
|
|
||||||
log.debug(
|
return aspect_ratio, _dpi, _width_inches, _height_inches, width_px, height_px
|
||||||
'Create MPL Axes (aspect=%d, width=%d, height=%d)',
|
|
||||||
aspect_ratio,
|
####################
|
||||||
_width_inches,
|
# - Special Methods
|
||||||
_height_inches,
|
####################
|
||||||
|
def xyzf_to_image(
|
||||||
|
self, xyz_freq, colormap: str | None = 'VIRIDIS', bl_select: bool = False
|
||||||
|
):
|
||||||
|
self.data_to_image(
|
||||||
|
lambda _: rgba_image_from_xyzf(xyz_freq, colormap=colormap),
|
||||||
|
bl_select=bl_select,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def data_to_image(
|
||||||
|
self,
|
||||||
|
func_image_data: typ.Callable[[int], np.array],
|
||||||
|
bl_select: bool = False,
|
||||||
|
):
|
||||||
|
# time_start = time.perf_counter()
|
||||||
|
image_data = func_image_data(4)
|
||||||
|
width_px = image_data.shape[1]
|
||||||
|
height_px = image_data.shape[0]
|
||||||
|
# log.debug('Computed Image Data (%f)', time.perf_counter() - time_start)
|
||||||
|
|
||||||
|
bl_image = self.bl_image(width_px, height_px, 'RGBA', 'float32')
|
||||||
|
bl_image.pixels.foreach_set(np.float32(image_data).ravel())
|
||||||
|
bl_image.update()
|
||||||
|
# log.debug('Set BL Image (%f)', time.perf_counter() - time_start)
|
||||||
|
|
||||||
|
if bl_select:
|
||||||
|
self.bl_select()
|
||||||
|
|
||||||
|
def mpl_plot_to_image(
|
||||||
|
self,
|
||||||
|
func_plotter: typ.Callable[[mpl_ax.Axis], None],
|
||||||
|
width_inches: float | None = None,
|
||||||
|
height_inches: float | None = None,
|
||||||
|
dpi: int | None = None,
|
||||||
|
bl_select: bool = False,
|
||||||
|
):
|
||||||
|
# time_start = time.perf_counter()
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
# log.debug('Imported PyPlot (%f)', time.perf_counter() - time_start)
|
||||||
|
|
||||||
|
# Compute Plot Dimensions
|
||||||
|
aspect_ratio, _dpi, _width_inches, _height_inches, width_px, height_px = (
|
||||||
|
self.gen_image_geometry(width_inches, height_inches, dpi)
|
||||||
|
)
|
||||||
|
# log.debug('Computed MPL Geometry (%f)', time.perf_counter() - time_start)
|
||||||
|
|
||||||
|
#log.debug(
|
||||||
|
# 'Creating MPL Axes (aspect=%f, width=%f, height=%f)',
|
||||||
|
# aspect_ratio,
|
||||||
|
# _width_inches,
|
||||||
|
# _height_inches,
|
||||||
|
#)
|
||||||
# Create MPL Figure, Axes, and Compute Figure Geometry
|
# Create MPL Figure, Axes, and Compute Figure Geometry
|
||||||
fig, ax = plt.subplots(
|
fig, ax = plt.subplots(
|
||||||
figsize=[_width_inches, _height_inches],
|
figsize=[_width_inches, _height_inches],
|
||||||
dpi=_dpi,
|
dpi=_dpi,
|
||||||
)
|
)
|
||||||
|
# log.debug('Created MPL Axes (%f)', time.perf_counter() - time_start)
|
||||||
ax.set_aspect(aspect_ratio)
|
ax.set_aspect(aspect_ratio)
|
||||||
cmp_width_px, cmp_height_px = fig.canvas.get_width_height()
|
cmp_width_px, cmp_height_px = fig.canvas.get_width_height()
|
||||||
## Use computed pixel w/h to preempt off-by-one size errors.
|
## Use computed pixel w/h to preempt off-by-one size errors.
|
||||||
ax.set_aspect('auto') ## Workaround aspect-ratio bugs
|
ax.set_aspect('auto') ## Workaround aspect-ratio bugs
|
||||||
|
# log.debug('Set MPL Aspect (%f)', time.perf_counter() - time_start)
|
||||||
|
|
||||||
# Plot w/User Parameter
|
# Plot w/User Parameter
|
||||||
func_plotter(ax)
|
func_plotter(ax)
|
||||||
|
# log.debug('User Plot Function (%f)', time.perf_counter() - time_start)
|
||||||
|
|
||||||
# Save Figure to BytesIO
|
# Save Figure to BytesIO
|
||||||
with io.BytesIO() as buff:
|
with io.BytesIO() as buff:
|
||||||
|
# log.debug('Made BytesIO (%f)', time.perf_counter() - time_start)
|
||||||
fig.savefig(buff, format='raw', dpi=dpi)
|
fig.savefig(buff, format='raw', dpi=dpi)
|
||||||
|
# log.debug('Saved Figure to BytesIO (%f)', time.perf_counter() - time_start)
|
||||||
buff.seek(0)
|
buff.seek(0)
|
||||||
image_data = np.frombuffer(
|
image_data = np.frombuffer(
|
||||||
buff.getvalue(),
|
buff.getvalue(),
|
||||||
dtype=np.uint8,
|
dtype=np.uint8,
|
||||||
).reshape([cmp_height_px, cmp_width_px, -1])
|
).reshape([cmp_height_px, cmp_width_px, -1])
|
||||||
|
# log.debug('Set Image Data (%f)', time.perf_counter() - time_start)
|
||||||
|
|
||||||
image_data = np.flipud(image_data).astype(np.float32) / 255
|
image_data = np.flipud(image_data).astype(np.float32) / 255
|
||||||
|
# log.debug('Flipped Image Data (%f)', time.perf_counter() - time_start)
|
||||||
plt.close(fig)
|
plt.close(fig)
|
||||||
|
|
||||||
# Optimized Write to Blender Image
|
# Optimized Write to Blender Image
|
||||||
bl_image = self.bl_image(cmp_width_px, cmp_height_px, 'RGBA', 'uint8')
|
bl_image = self.bl_image(cmp_width_px, cmp_height_px, 'RGBA', 'uint8')
|
||||||
|
# log.debug('Made BL Image (%f)', time.perf_counter() - time_start)
|
||||||
bl_image.pixels.foreach_set(image_data.ravel())
|
bl_image.pixels.foreach_set(image_data.ravel())
|
||||||
|
# log.debug('Set BL Image Pixels (%f)', time.perf_counter() - time_start)
|
||||||
bl_image.update()
|
bl_image.update()
|
||||||
|
# log.debug('Updated BL Image (%f)', time.perf_counter() - time_start)
|
||||||
|
|
||||||
if bl_select:
|
if bl_select:
|
||||||
self.bl_select()
|
self.bl_select()
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# from . import kitchen_sink
|
# from . import kitchen_sink
|
||||||
|
|
||||||
# from . import bounds
|
# from . import bounds
|
||||||
from . import (
|
from . import (
|
||||||
|
analysis,
|
||||||
inputs,
|
inputs,
|
||||||
mediums,
|
mediums,
|
||||||
monitors,
|
monitors,
|
||||||
|
@ -10,11 +10,11 @@ from . import (
|
||||||
sources,
|
sources,
|
||||||
structures,
|
structures,
|
||||||
utilities,
|
utilities,
|
||||||
viz,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
BL_REGISTER = [
|
BL_REGISTER = [
|
||||||
# *kitchen_sink.BL_REGISTER,
|
# *kitchen_sink.BL_REGISTER,
|
||||||
|
*analysis.BL_REGISTER,
|
||||||
*inputs.BL_REGISTER,
|
*inputs.BL_REGISTER,
|
||||||
*outputs.BL_REGISTER,
|
*outputs.BL_REGISTER,
|
||||||
*sources.BL_REGISTER,
|
*sources.BL_REGISTER,
|
||||||
|
@ -24,10 +24,10 @@ BL_REGISTER = [
|
||||||
*monitors.BL_REGISTER,
|
*monitors.BL_REGISTER,
|
||||||
*simulations.BL_REGISTER,
|
*simulations.BL_REGISTER,
|
||||||
*utilities.BL_REGISTER,
|
*utilities.BL_REGISTER,
|
||||||
*viz.BL_REGISTER,
|
|
||||||
]
|
]
|
||||||
BL_NODES = {
|
BL_NODES = {
|
||||||
# **kitchen_sink.BL_NODES,
|
# **kitchen_sink.BL_NODES,
|
||||||
|
**analysis.BL_NODES,
|
||||||
**inputs.BL_NODES,
|
**inputs.BL_NODES,
|
||||||
**outputs.BL_NODES,
|
**outputs.BL_NODES,
|
||||||
**sources.BL_NODES,
|
**sources.BL_NODES,
|
||||||
|
@ -37,5 +37,4 @@ BL_NODES = {
|
||||||
**monitors.BL_NODES,
|
**monitors.BL_NODES,
|
||||||
**simulations.BL_NODES,
|
**simulations.BL_NODES,
|
||||||
**utilities.BL_NODES,
|
**utilities.BL_NODES,
|
||||||
**viz.BL_NODES,
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
from . import extract_data, viz
|
||||||
|
|
||||||
|
BL_REGISTER = [
|
||||||
|
*extract_data.BL_REGISTER,
|
||||||
|
*viz.BL_REGISTER,
|
||||||
|
]
|
||||||
|
BL_NODES = {
|
||||||
|
**extract_data.BL_NODES,
|
||||||
|
**viz.BL_NODES,
|
||||||
|
}
|
|
@ -0,0 +1,239 @@
|
||||||
|
import typing as typ
|
||||||
|
|
||||||
|
import bpy
|
||||||
|
|
||||||
|
from .....utils import logger
|
||||||
|
from ... import contracts as ct
|
||||||
|
from ... import managed_objs, sockets
|
||||||
|
from .. import base, events
|
||||||
|
|
||||||
|
log = logger.get(__name__)
|
||||||
|
|
||||||
|
CACHE_SIM_DATA = {}
|
||||||
|
|
||||||
|
|
||||||
|
class ExtractDataNode(base.MaxwellSimNode):
|
||||||
|
"""Node for visualizing simulation data, by querying its monitors."""
|
||||||
|
|
||||||
|
node_type = ct.NodeType.ExtractData
|
||||||
|
bl_label = 'Extract Data'
|
||||||
|
|
||||||
|
input_socket_sets: typ.ClassVar = {
|
||||||
|
'Sim Data': {'Sim Data': sockets.MaxwellFDTDSimDataSocketDef()},
|
||||||
|
'Field Data': {'Field Data': sockets.AnySocketDef()},
|
||||||
|
}
|
||||||
|
output_sockets: typ.ClassVar = {
|
||||||
|
'Data': sockets.AnySocketDef(),
|
||||||
|
}
|
||||||
|
|
||||||
|
####################
|
||||||
|
# - Properties: Sim Data
|
||||||
|
####################
|
||||||
|
sim_data__monitor_name: bpy.props.EnumProperty(
|
||||||
|
name='Sim Data Monitor Name',
|
||||||
|
description='Monitor to extract from the attached SimData',
|
||||||
|
items=lambda self, context: self.search_monitors(context),
|
||||||
|
update=lambda self, context: self.sync_prop('sim_data__monitor_name', context),
|
||||||
|
)
|
||||||
|
|
||||||
|
cache__num_monitors: bpy.props.StringProperty(default='')
|
||||||
|
cache__monitor_names: bpy.props.StringProperty(default='')
|
||||||
|
cache__monitor_types: bpy.props.StringProperty(default='')
|
||||||
|
|
||||||
|
def search_monitors(self, _: bpy.types.Context) -> list[tuple[str, str, str]]:
|
||||||
|
"""Search the linked simulation data for monitors."""
|
||||||
|
# No Linked Sim Data: Return 'None'
|
||||||
|
if not self.inputs.get('Sim Data') or not self.inputs['Sim Data'].is_linked:
|
||||||
|
return [('NONE', 'None', 'No monitors')]
|
||||||
|
|
||||||
|
# Return Monitor Names
|
||||||
|
## Special Case for No Monitors
|
||||||
|
monitor_names = (
|
||||||
|
self.cache__monitor_names.split(',') if self.cache__monitor_names else []
|
||||||
|
)
|
||||||
|
monitor_types = (
|
||||||
|
self.cache__monitor_types.split(',') if self.cache__monitor_types else []
|
||||||
|
)
|
||||||
|
if len(monitor_names) == 0:
|
||||||
|
return [('NONE', 'None', 'No monitors')]
|
||||||
|
return [
|
||||||
|
(
|
||||||
|
monitor_name,
|
||||||
|
f'{monitor_name}',
|
||||||
|
f'Monitor "{monitor_name}" ({monitor_type}) recorded by the Sim',
|
||||||
|
)
|
||||||
|
for monitor_name, monitor_type in zip(
|
||||||
|
monitor_names, monitor_types, strict=False
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
def draw_props__sim_data(
|
||||||
|
self, _: bpy.types.Context, col: bpy.types.UILayout
|
||||||
|
) -> None:
|
||||||
|
col.prop(self, 'sim_data__monitor_name', text='')
|
||||||
|
|
||||||
|
def draw_info__sim_data(
|
||||||
|
self, _: bpy.types.Context, col: bpy.types.UILayout
|
||||||
|
) -> None:
|
||||||
|
if self.sim_data__monitor_name != 'NONE':
|
||||||
|
# Header
|
||||||
|
row = col.row()
|
||||||
|
row.alignment = 'CENTER'
|
||||||
|
row.label(text=f'{self.cache__num_monitors} Monitors')
|
||||||
|
|
||||||
|
# Monitor Info
|
||||||
|
if int(self.cache__num_monitors) > 0:
|
||||||
|
for monitor_name, monitor_type in zip(
|
||||||
|
self.cache__monitor_names.split(','),
|
||||||
|
self.cache__monitor_types.split(','),
|
||||||
|
strict=False,
|
||||||
|
):
|
||||||
|
col.label(text=f'{monitor_name}: {monitor_type}')
|
||||||
|
|
||||||
|
####################
|
||||||
|
# - Events: Sim Data
|
||||||
|
####################
|
||||||
|
@events.on_value_changed(
|
||||||
|
socket_name='Sim Data',
|
||||||
|
)
|
||||||
|
def on_sim_data_changed(self):
|
||||||
|
# SimData Cache Hit and SimData Input Unlinked
|
||||||
|
## Delete Cache Entry
|
||||||
|
if (
|
||||||
|
CACHE_SIM_DATA.get(self.instance_id) is not None
|
||||||
|
and not self.inputs['Sim Data'].is_linked
|
||||||
|
):
|
||||||
|
CACHE_SIM_DATA.pop(self.instance_id, None) ## Both member-check
|
||||||
|
self.cache__num_monitors = ''
|
||||||
|
self.cache__monitor_names = ''
|
||||||
|
self.cache__monitor_types = ''
|
||||||
|
|
||||||
|
# SimData Cache Miss and Linked SimData
|
||||||
|
if (
|
||||||
|
CACHE_SIM_DATA.get(self.instance_id) is None
|
||||||
|
and self.inputs['Sim Data'].is_linked
|
||||||
|
):
|
||||||
|
sim_data = self._compute_input('Sim Data')
|
||||||
|
|
||||||
|
## Create Cache Entry
|
||||||
|
CACHE_SIM_DATA[self.instance_id] = {
|
||||||
|
'sim_data': sim_data,
|
||||||
|
'monitor_names': list(sim_data.monitor_data.keys()),
|
||||||
|
'monitor_types': [
|
||||||
|
monitor_data.type for monitor_data in sim_data.monitor_data.values()
|
||||||
|
],
|
||||||
|
}
|
||||||
|
cache = CACHE_SIM_DATA[self.instance_id]
|
||||||
|
self.cache__num_monitors = str(len(cache['monitor_names']))
|
||||||
|
self.cache__monitor_names = ','.join(cache['monitor_names'])
|
||||||
|
self.cache__monitor_types = ','.join(cache['monitor_types'])
|
||||||
|
|
||||||
|
####################
|
||||||
|
# - Properties: Field Data
|
||||||
|
####################
|
||||||
|
field_data__component: bpy.props.EnumProperty(
|
||||||
|
name='Field Data Component',
|
||||||
|
description='Field monitor component to extract from the attached Field Data',
|
||||||
|
items=lambda self, context: self.search_field_data_components(context),
|
||||||
|
update=lambda self, context: self.sync_prop('field_data__component', context),
|
||||||
|
)
|
||||||
|
|
||||||
|
cache__components: bpy.props.StringProperty(default='')
|
||||||
|
|
||||||
|
def search_field_data_components(
|
||||||
|
self, _: bpy.types.Context
|
||||||
|
) -> list[tuple[str, str, str]]:
|
||||||
|
if not self.inputs.get('Field Data') or not self.inputs['Field Data'].is_linked:
|
||||||
|
return [('NONE', 'None', 'No data')]
|
||||||
|
|
||||||
|
if not self.cache__components:
|
||||||
|
return [('NONE', 'Loading...', 'Loading data...')]
|
||||||
|
|
||||||
|
components = [
|
||||||
|
tuple(component_str.split(','))
|
||||||
|
for component_str in self.cache__components.split('|')
|
||||||
|
]
|
||||||
|
|
||||||
|
if len(components) == 0:
|
||||||
|
return [('NONE', 'None', 'No components')]
|
||||||
|
return components
|
||||||
|
|
||||||
|
def draw_props__field_data(
|
||||||
|
self, _: bpy.types.Context, col: bpy.types.UILayout
|
||||||
|
) -> None:
|
||||||
|
col.prop(self, 'field_data__component', text='')
|
||||||
|
|
||||||
|
def draw_info__field_data(
|
||||||
|
self, _: bpy.types.Context, col: bpy.types.UILayout
|
||||||
|
) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
####################
|
||||||
|
# - Events: Field Data
|
||||||
|
####################
|
||||||
|
@events.on_value_changed(
|
||||||
|
socket_name='Field Data',
|
||||||
|
)
|
||||||
|
def on_field_data_changed(self):
|
||||||
|
if self.inputs['Field Data'].is_linked and not self.cache__components:
|
||||||
|
field_data = self._compute_input('Field Data')
|
||||||
|
components = [
|
||||||
|
*([('Ex', 'Ex', 'Ex')] if field_data.Ex is not None else []),
|
||||||
|
*([('Ey', 'Ey', 'Ey')] if field_data.Ey is not None else []),
|
||||||
|
*([('Ez', 'Ez', 'Ez')] if field_data.Ez is not None else []),
|
||||||
|
*([('Hx', 'Hx', 'Hx')] if field_data.Hx is not None else []),
|
||||||
|
*([('Hy', 'Hy', 'Hy')] if field_data.Hy is not None else []),
|
||||||
|
*([('Hz', 'Hz', 'Hz')] if field_data.Hz is not None else []),
|
||||||
|
]
|
||||||
|
self.cache__components = '|'.join(
|
||||||
|
[','.join(component) for component in components]
|
||||||
|
)
|
||||||
|
|
||||||
|
elif not self.inputs['Field Data'].is_linked and self.cache__components:
|
||||||
|
self.cache__components = ''
|
||||||
|
|
||||||
|
####################
|
||||||
|
# - Global
|
||||||
|
####################
|
||||||
|
def draw_props(self, context: bpy.types.Context, col: bpy.types.UILayout) -> None:
|
||||||
|
if self.active_socket_set == 'Sim Data':
|
||||||
|
self.draw_props__sim_data(context, col)
|
||||||
|
if self.active_socket_set == 'Field Data':
|
||||||
|
self.draw_props__field_data(context, col)
|
||||||
|
|
||||||
|
def draw_info(self, context: bpy.types.Context, col: bpy.types.UILayout) -> None:
|
||||||
|
if self.active_socket_set == 'Sim Data':
|
||||||
|
self.draw_info__sim_data(context, col)
|
||||||
|
if self.active_socket_set == 'Field Data':
|
||||||
|
self.draw_info__field_data(context, col)
|
||||||
|
|
||||||
|
@events.computes_output_socket(
|
||||||
|
'Data',
|
||||||
|
props={'sim_data__monitor_name', 'field_data__component'},
|
||||||
|
)
|
||||||
|
def compute_extracted_data(self, props: dict):
|
||||||
|
if self.active_socket_set == 'Sim Data':
|
||||||
|
if (
|
||||||
|
CACHE_SIM_DATA.get(self.instance_id) is None
|
||||||
|
and self.inputs['Sim Data'].is_linked
|
||||||
|
):
|
||||||
|
self.on_sim_data_changed()
|
||||||
|
|
||||||
|
sim_data = CACHE_SIM_DATA[self.instance_id]['sim_data']
|
||||||
|
return sim_data.monitor_data[props['sim_data__monitor_name']]
|
||||||
|
|
||||||
|
elif self.active_socket_set == 'Field Data': # noqa: RET505
|
||||||
|
field_data = self._compute_input('Field Data')
|
||||||
|
return getattr(field_data, props['field_data__component'])
|
||||||
|
|
||||||
|
msg = f'Tried to get data from unknown output socket in "{self.bl_label}"'
|
||||||
|
raise RuntimeError(msg)
|
||||||
|
|
||||||
|
|
||||||
|
####################
|
||||||
|
# - Blender Registration
|
||||||
|
####################
|
||||||
|
BL_REGISTER = [
|
||||||
|
ExtractDataNode,
|
||||||
|
]
|
||||||
|
BL_NODES = {ct.NodeType.ExtractData: (ct.NodeCategory.MAXWELLSIM_ANALYSIS)}
|
|
@ -0,0 +1,107 @@
|
||||||
|
import typing as typ
|
||||||
|
|
||||||
|
import bpy
|
||||||
|
import jax.numpy as jnp
|
||||||
|
|
||||||
|
from .....utils import logger
|
||||||
|
from ... import contracts as ct
|
||||||
|
from ... import managed_objs, sockets
|
||||||
|
from .. import base, events
|
||||||
|
|
||||||
|
log = logger.get(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class VizNode(base.MaxwellSimNode):
|
||||||
|
"""Node for visualizing simulation data, by querying its monitors."""
|
||||||
|
|
||||||
|
node_type = ct.NodeType.Viz
|
||||||
|
bl_label = 'Viz'
|
||||||
|
|
||||||
|
####################
|
||||||
|
# - Sockets
|
||||||
|
####################
|
||||||
|
input_sockets = {
|
||||||
|
'Data': sockets.AnySocketDef(),
|
||||||
|
'Freq': sockets.PhysicalFreqSocketDef(),
|
||||||
|
}
|
||||||
|
# input_sockets_sets: typ.ClassVar = {
|
||||||
|
# '2D Freq': {
|
||||||
|
# 'Data': sockets.AnySocketDef(),
|
||||||
|
# 'Freq': sockets.PhysicalFreqSocketDef(),
|
||||||
|
# },
|
||||||
|
# }
|
||||||
|
output_sockets: typ.ClassVar = {
|
||||||
|
'Preview': sockets.AnySocketDef(),
|
||||||
|
}
|
||||||
|
|
||||||
|
managed_obj_defs: typ.ClassVar = {
|
||||||
|
'plot': ct.schemas.ManagedObjDef(
|
||||||
|
mk=lambda name: managed_objs.ManagedBLImage(name),
|
||||||
|
),
|
||||||
|
#'empty': ct.schemas.ManagedObjDef(
|
||||||
|
# mk=lambda name: managed_objs.ManagedBLEmpty(name),
|
||||||
|
# ),
|
||||||
|
}
|
||||||
|
|
||||||
|
#####################
|
||||||
|
## - Properties
|
||||||
|
#####################
|
||||||
|
colormap: bpy.props.EnumProperty(
|
||||||
|
name='Colormap',
|
||||||
|
description='Colormap to apply to grayscale output',
|
||||||
|
items=[
|
||||||
|
('VIRIDIS', 'Viridis', 'Good default colormap'),
|
||||||
|
('GRAYSCALE', 'Grayscale', 'Barebones'),
|
||||||
|
],
|
||||||
|
default='VIRIDIS',
|
||||||
|
update=lambda self, context: self.sync_prop('colormap', context),
|
||||||
|
)
|
||||||
|
|
||||||
|
#####################
|
||||||
|
## - UI
|
||||||
|
#####################
|
||||||
|
def draw_props(self, _: bpy.types.Context, col: bpy.types.UILayout):
|
||||||
|
col.prop(self, 'colormap')
|
||||||
|
|
||||||
|
#####################
|
||||||
|
## - Plotting
|
||||||
|
#####################
|
||||||
|
@events.on_show_plot(
|
||||||
|
managed_objs={'plot'},
|
||||||
|
input_sockets={'Data', 'Freq'},
|
||||||
|
props={'colormap'},
|
||||||
|
unit_systems={'Tidy3DUnits': ct.UNITS_TIDY3D},
|
||||||
|
scale_input_sockets={
|
||||||
|
'Freq': 'Tidy3DUnits',
|
||||||
|
},
|
||||||
|
stop_propagation=True,
|
||||||
|
)
|
||||||
|
def on_show_plot(
|
||||||
|
self,
|
||||||
|
managed_objs: dict[str, ct.schemas.ManagedObj],
|
||||||
|
input_sockets: dict,
|
||||||
|
props: dict,
|
||||||
|
unit_systems: dict,
|
||||||
|
):
|
||||||
|
selected_data = jnp.array(
|
||||||
|
input_sockets['Data'].sel(f=input_sockets['Freq'], method='nearest')
|
||||||
|
)
|
||||||
|
|
||||||
|
managed_objs['plot'].xyzf_to_image(
|
||||||
|
selected_data,
|
||||||
|
colormap=props['colormap'],
|
||||||
|
bl_select=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
# @events.on_init()
|
||||||
|
# def on_init(self):
|
||||||
|
# self.on_changed_inputs()
|
||||||
|
|
||||||
|
|
||||||
|
####################
|
||||||
|
# - Blender Registration
|
||||||
|
####################
|
||||||
|
BL_REGISTER = [
|
||||||
|
VizNode,
|
||||||
|
]
|
||||||
|
BL_NODES = {ct.NodeType.Viz: (ct.NodeCategory.MAXWELLSIM_ANALYSIS)}
|
|
@ -105,12 +105,10 @@ class MaxwellSimNode(bpy.types.Node):
|
||||||
|
|
||||||
# Setup Callback Methods
|
# Setup Callback Methods
|
||||||
cls._output_socket_methods = {
|
cls._output_socket_methods = {
|
||||||
(method.extra_data['output_socket_name'], method.extra_data['kind']): method
|
method
|
||||||
for attr_name in dir(cls)
|
for attr_name in dir(cls)
|
||||||
if hasattr(method := getattr(cls, attr_name), 'action_type')
|
if hasattr(method := getattr(cls, attr_name), 'action_type')
|
||||||
and method.action_type == 'computes_output_socket'
|
and method.action_type == 'computes_output_socket'
|
||||||
and hasattr(method, 'extra_data')
|
|
||||||
and method.extra_data
|
|
||||||
}
|
}
|
||||||
cls._on_value_changed_methods = {
|
cls._on_value_changed_methods = {
|
||||||
method
|
method
|
||||||
|
@ -553,10 +551,21 @@ class MaxwellSimNode(bpy.types.Node):
|
||||||
The value of the output socket, as computed by the dedicated method
|
The value of the output socket, as computed by the dedicated method
|
||||||
registered using the `@computes_output_socket` decorator.
|
registered using the `@computes_output_socket` decorator.
|
||||||
"""
|
"""
|
||||||
if output_socket_method := self._output_socket_methods.get(
|
possible_output_socket_methods = [
|
||||||
(output_socket_name, kind)
|
output_socket_method
|
||||||
):
|
for output_socket_method in self._output_socket_methods
|
||||||
return output_socket_method(self)
|
if kind == output_socket_method.extra_data['kind']
|
||||||
|
and (
|
||||||
|
output_socket_name
|
||||||
|
== output_socket_method.extra_data['output_socket_name']
|
||||||
|
or (
|
||||||
|
output_socket_method.extra_data['any_loose_output_socket']
|
||||||
|
and output_socket_name in self.loose_output_sockets
|
||||||
|
)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
if len(possible_output_socket_methods) == 1:
|
||||||
|
return possible_output_socket_methods[0](self)
|
||||||
|
|
||||||
msg = f'No output method for ({output_socket_name}, {kind.value!s}'
|
msg = f'No output method for ({output_socket_name}, {kind.value!s}'
|
||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
|
|
|
@ -30,6 +30,7 @@ class EventCallbackData_ComputesOutputSocket(typ.TypedDict): # noqa: N801
|
||||||
"""Extra data used to select a method to compute output sockets."""
|
"""Extra data used to select a method to compute output sockets."""
|
||||||
|
|
||||||
output_socket_name: ct.SocketName
|
output_socket_name: ct.SocketName
|
||||||
|
any_loose_output_socket: bool
|
||||||
kind: ct.DataFlowKind
|
kind: ct.DataFlowKind
|
||||||
|
|
||||||
|
|
||||||
|
@ -194,8 +195,8 @@ def event_decorator(
|
||||||
_output_sockets = {
|
_output_sockets = {
|
||||||
output_socket_name: node.compute_output(
|
output_socket_name: node.compute_output(
|
||||||
output_socket_name,
|
output_socket_name,
|
||||||
kind=input_socket_kinds.get(
|
kind=output_socket_kinds.get(
|
||||||
input_socket_name, ct.DataFlowKind.Value
|
output_socket_name, ct.DataFlowKind.Value
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
for output_socket_name in output_sockets
|
for output_socket_name in output_sockets
|
||||||
|
@ -231,7 +232,10 @@ def event_decorator(
|
||||||
## Compute All Loose Input Sockets
|
## Compute All Loose Input Sockets
|
||||||
if all_loose_input_sockets:
|
if all_loose_input_sockets:
|
||||||
_loose_input_sockets = {
|
_loose_input_sockets = {
|
||||||
input_socket_name: node._compute_input(input_socket_name, kind=node.inputs[input_socket_name].active_kind)
|
input_socket_name: node._compute_input(
|
||||||
|
input_socket_name,
|
||||||
|
kind=node.inputs[input_socket_name].active_kind,
|
||||||
|
)
|
||||||
for input_socket_name in node.loose_input_sockets
|
for input_socket_name in node.loose_input_sockets
|
||||||
}
|
}
|
||||||
method_kw_args |= {'loose_input_sockets': _loose_input_sockets}
|
method_kw_args |= {'loose_input_sockets': _loose_input_sockets}
|
||||||
|
@ -239,7 +243,10 @@ def event_decorator(
|
||||||
## Compute All Loose Output Sockets
|
## Compute All Loose Output Sockets
|
||||||
if all_loose_output_sockets:
|
if all_loose_output_sockets:
|
||||||
_loose_output_sockets = {
|
_loose_output_sockets = {
|
||||||
output_socket_name: node.compute_output(output_socket_name, kind=node.outputs[output_socket_name].active_kind)
|
output_socket_name: node.compute_output(
|
||||||
|
output_socket_name,
|
||||||
|
kind=node.outputs[output_socket_name].active_kind,
|
||||||
|
)
|
||||||
for output_socket_name in node.loose_output_sockets
|
for output_socket_name in node.loose_output_sockets
|
||||||
}
|
}
|
||||||
method_kw_args |= {'loose_output_sockets': _loose_output_sockets}
|
method_kw_args |= {'loose_output_sockets': _loose_output_sockets}
|
||||||
|
@ -274,7 +281,8 @@ def event_decorator(
|
||||||
# - Simplified Event Callbacks
|
# - Simplified Event Callbacks
|
||||||
####################
|
####################
|
||||||
def computes_output_socket(
|
def computes_output_socket(
|
||||||
output_socket_name: ct.SocketName,
|
output_socket_name: ct.SocketName | None,
|
||||||
|
any_loose_output_socket: bool = False,
|
||||||
kind: ct.DataFlowKind = ct.DataFlowKind.Value,
|
kind: ct.DataFlowKind = ct.DataFlowKind.Value,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
):
|
):
|
||||||
|
@ -282,6 +290,7 @@ def computes_output_socket(
|
||||||
action_type='computes_output_socket',
|
action_type='computes_output_socket',
|
||||||
extra_data={
|
extra_data={
|
||||||
'output_socket_name': output_socket_name,
|
'output_socket_name': output_socket_name,
|
||||||
|
'any_loose_output_socket': any_loose_output_socket,
|
||||||
'kind': kind,
|
'kind': kind,
|
||||||
},
|
},
|
||||||
**kwargs,
|
**kwargs,
|
||||||
|
|
|
@ -196,9 +196,9 @@ class Tidy3DFileImporterNode(base.MaxwellSimNode):
|
||||||
else:
|
else:
|
||||||
self.loose_output_sockets = {
|
self.loose_output_sockets = {
|
||||||
'SIMULATION_DATA': {
|
'SIMULATION_DATA': {
|
||||||
'Sim Data': sockets.MaxwellFDTDSimSocketDef(),
|
'Sim Data': sockets.MaxwellFDTDSimDataSocketDef(),
|
||||||
},
|
},
|
||||||
'SIMULATION': {'Sim': sockets.MaxwellFDTDSimDataSocketDef()},
|
'SIMULATION': {'Sim': sockets.MaxwellFDTDSimSocketDef()},
|
||||||
'MEDIUM': {'Medium': sockets.MaxwellMediumSocketDef()},
|
'MEDIUM': {'Medium': sockets.MaxwellMediumSocketDef()},
|
||||||
'EXPERIM_DISP_MEDIUM': {
|
'EXPERIM_DISP_MEDIUM': {
|
||||||
'Experim Disp Medium': sockets.MaxwellMediumSocketDef()
|
'Experim Disp Medium': sockets.MaxwellMediumSocketDef()
|
||||||
|
@ -245,5 +245,5 @@ BL_REGISTER = [
|
||||||
Tidy3DFileImporterNode,
|
Tidy3DFileImporterNode,
|
||||||
]
|
]
|
||||||
BL_NODES = {
|
BL_NODES = {
|
||||||
ct.NodeType.Tidy3DFileImporter: (ct.NodeCategory.MAXWELLSIM_INPUTS_IMPORTERS)
|
ct.NodeType.Tidy3DFileImporter: (ct.NodeCategory.MAXWELLSIM_INPUTS_FILEIMPORTERS)
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,9 +35,34 @@ class WaveConstantNode(base.MaxwellSimNode):
|
||||||
####################
|
####################
|
||||||
@events.computes_output_socket(
|
@events.computes_output_socket(
|
||||||
'WL',
|
'WL',
|
||||||
|
kind=ct.DataFlowKind.Value,
|
||||||
all_loose_input_sockets=True,
|
all_loose_input_sockets=True,
|
||||||
)
|
)
|
||||||
def compute_wl(self, loose_input_sockets: dict) -> sp.Expr:
|
def compute_wl_value(self, loose_input_sockets: dict) -> sp.Expr:
|
||||||
|
if (wl := loose_input_sockets.get('WL')) is not None:
|
||||||
|
return wl
|
||||||
|
|
||||||
|
freq = loose_input_sockets.get('Freq')
|
||||||
|
return constants.vac_speed_of_light / freq
|
||||||
|
|
||||||
|
@events.computes_output_socket(
|
||||||
|
'Freq',
|
||||||
|
kind=ct.DataFlowKind.Value,
|
||||||
|
all_loose_input_sockets=True,
|
||||||
|
)
|
||||||
|
def compute_freq_value(self, loose_input_sockets: dict) -> sp.Expr:
|
||||||
|
if (freq := loose_input_sockets.get('Freq')) is not None:
|
||||||
|
return freq
|
||||||
|
|
||||||
|
wl = loose_input_sockets.get('WL')
|
||||||
|
return constants.vac_speed_of_light / wl
|
||||||
|
|
||||||
|
@events.computes_output_socket(
|
||||||
|
'WL',
|
||||||
|
kind=ct.DataFlowKind.LazyValueRange,
|
||||||
|
all_loose_input_sockets=True,
|
||||||
|
)
|
||||||
|
def compute_wl_lazyvaluerange(self, loose_input_sockets: dict) -> sp.Expr:
|
||||||
if (wl := loose_input_sockets.get('WL')) is not None:
|
if (wl := loose_input_sockets.get('WL')) is not None:
|
||||||
return wl
|
return wl
|
||||||
|
|
||||||
|
@ -52,9 +77,10 @@ class WaveConstantNode(base.MaxwellSimNode):
|
||||||
|
|
||||||
@events.computes_output_socket(
|
@events.computes_output_socket(
|
||||||
'Freq',
|
'Freq',
|
||||||
|
kind=ct.DataFlowKind.LazyValueRange,
|
||||||
all_loose_input_sockets=True,
|
all_loose_input_sockets=True,
|
||||||
)
|
)
|
||||||
def compute_freq(self, loose_input_sockets: dict) -> sp.Expr:
|
def compute_freq_lazyvaluerange(self, loose_input_sockets: dict) -> sp.Expr:
|
||||||
if (freq := loose_input_sockets.get('Freq')) is not None:
|
if (freq := loose_input_sockets.get('Freq')) is not None:
|
||||||
return freq
|
return freq
|
||||||
|
|
||||||
|
|
|
@ -102,5 +102,5 @@ BL_REGISTER = [
|
||||||
Tidy3DWebImporterNode,
|
Tidy3DWebImporterNode,
|
||||||
]
|
]
|
||||||
BL_NODES = {
|
BL_NODES = {
|
||||||
ct.NodeType.Tidy3DWebImporter: (ct.NodeCategory.MAXWELLSIM_INPUTS_IMPORTERS)
|
ct.NodeType.Tidy3DWebImporter: (ct.NodeCategory.MAXWELLSIM_INPUTS_WEBIMPORTERS)
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ class EHFieldMonitorNode(base.MaxwellSimNode):
|
||||||
"""Node providing for the monitoring of electromagnetic fields within a given planar region or volume."""
|
"""Node providing for the monitoring of electromagnetic fields within a given planar region or volume."""
|
||||||
|
|
||||||
node_type = ct.NodeType.EHFieldMonitor
|
node_type = ct.NodeType.EHFieldMonitor
|
||||||
bl_label = 'E/H Field Monitor'
|
bl_label = 'EH Field Monitor'
|
||||||
use_sim_node_name = True
|
use_sim_node_name = True
|
||||||
|
|
||||||
####################
|
####################
|
||||||
|
@ -45,8 +45,9 @@ class EHFieldMonitorNode(base.MaxwellSimNode):
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
output_sockets: typ.ClassVar = {
|
output_socket_sets: typ.ClassVar = {
|
||||||
'Monitor': sockets.MaxwellMonitorSocketDef(),
|
'Freq Domain': {'Freq Monitor': sockets.MaxwellMonitorSocketDef()},
|
||||||
|
'Time Domain': {'Time Monitor': sockets.MaxwellMonitorSocketDef()},
|
||||||
}
|
}
|
||||||
|
|
||||||
managed_obj_defs: typ.ClassVar = {
|
managed_obj_defs: typ.ClassVar = {
|
||||||
|
@ -62,33 +63,30 @@ class EHFieldMonitorNode(base.MaxwellSimNode):
|
||||||
# - Output Sockets
|
# - Output Sockets
|
||||||
####################
|
####################
|
||||||
@events.computes_output_socket(
|
@events.computes_output_socket(
|
||||||
'Monitor',
|
'Freq Monitor',
|
||||||
props={'active_socket_set', 'sim_node_name'},
|
props={'sim_node_name'},
|
||||||
input_sockets={
|
input_sockets={
|
||||||
'Rec Start',
|
|
||||||
'Rec Stop',
|
|
||||||
'Center',
|
'Center',
|
||||||
'Size',
|
'Size',
|
||||||
'Samples/Space',
|
'Samples/Space',
|
||||||
'Samples/Time',
|
|
||||||
'Freqs',
|
'Freqs',
|
||||||
},
|
},
|
||||||
|
input_socket_kinds={
|
||||||
|
'Freqs': ct.DataFlowKind.LazyValueRange,
|
||||||
|
},
|
||||||
unit_systems={'Tidy3DUnits': ct.UNITS_TIDY3D},
|
unit_systems={'Tidy3DUnits': ct.UNITS_TIDY3D},
|
||||||
scale_input_sockets={
|
scale_input_sockets={
|
||||||
'Center': 'Tidy3DUnits',
|
'Center': 'Tidy3DUnits',
|
||||||
'Size': 'Tidy3DUnits',
|
'Size': 'Tidy3DUnits',
|
||||||
'Samples/Space': 'Tidy3DUnits',
|
'Freqs': 'Tidy3DUnits',
|
||||||
'Rec Start': 'Tidy3DUnits',
|
|
||||||
'Rec Stop': 'Tidy3DUnits',
|
|
||||||
'Samples/Time': 'Tidy3DUnits',
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
def compute_monitor(
|
def compute_freq_monitor(
|
||||||
self, input_sockets: dict, props: dict, unit_systems: dict,
|
self,
|
||||||
) -> td.FieldMonitor | td.FieldTimeMonitor:
|
input_sockets: dict,
|
||||||
if props['active_socket_set'] == 'Freq Domain':
|
props: dict,
|
||||||
freqs = input_sockets['Freqs']
|
unit_systems: dict,
|
||||||
|
) -> td.FieldMonitor:
|
||||||
log.info(
|
log.info(
|
||||||
'Computing FieldMonitor (name="%s") with center="%s", size="%s"',
|
'Computing FieldMonitor (name="%s") with center="%s", size="%s"',
|
||||||
props['sim_node_name'],
|
props['sim_node_name'],
|
||||||
|
@ -99,26 +97,11 @@ class EHFieldMonitorNode(base.MaxwellSimNode):
|
||||||
center=input_sockets['Center'],
|
center=input_sockets['Center'],
|
||||||
size=input_sockets['Size'],
|
size=input_sockets['Size'],
|
||||||
name=props['sim_node_name'],
|
name=props['sim_node_name'],
|
||||||
interval_space=input_sockets['Samples/Space'],
|
interval_space=tuple(input_sockets['Samples/Space']),
|
||||||
freqs=[
|
freqs=input_sockets['Freqs'].realize().values,
|
||||||
float(spu.convert_to(freq, spu.hertz) / spu.hertz) for freq in freqs
|
#freqs=[
|
||||||
],
|
# float(spu.convert_to(freq, spu.hertz) / spu.hertz) for freq in freqs
|
||||||
)
|
#],
|
||||||
## Time Domain
|
|
||||||
log.info(
|
|
||||||
'Computing FieldTimeMonitor (name=%s) with center=%s, size=%s',
|
|
||||||
props['sim_node_name'],
|
|
||||||
input_sockets['Center'],
|
|
||||||
input_sockets['Size'],
|
|
||||||
)
|
|
||||||
return td.FieldTimeMonitor(
|
|
||||||
center=input_sockets['Center'],
|
|
||||||
size=input_sockets['Size'],
|
|
||||||
name=props['sim_node_name'],
|
|
||||||
start=input_sockets['Rec Start'],
|
|
||||||
stop=input_sockets['Rec Stop'],
|
|
||||||
interval=input_sockets['Samples/Time'],
|
|
||||||
interval_space=input_sockets['Samples/Space'],
|
|
||||||
)
|
)
|
||||||
|
|
||||||
####################
|
####################
|
||||||
|
|
|
@ -15,9 +15,9 @@ from .. import base, events
|
||||||
log = logger.get(__name__)
|
log = logger.get(__name__)
|
||||||
|
|
||||||
|
|
||||||
class FieldPowerFluxMonitorNode(base.MaxwellSimNode):
|
class PowerFluxMonitorNode(base.MaxwellSimNode):
|
||||||
node_type = ct.NodeType.FieldPowerFluxMonitor
|
node_type = ct.NodeType.PowerFluxMonitor
|
||||||
bl_label = 'Field Power Flux Monitor'
|
bl_label = 'Power Flux Monitor'
|
||||||
use_sim_node_name = True
|
use_sim_node_name = True
|
||||||
|
|
||||||
####################
|
####################
|
||||||
|
@ -160,6 +160,6 @@ class FieldPowerFluxMonitorNode(base.MaxwellSimNode):
|
||||||
# - Blender Registration
|
# - Blender Registration
|
||||||
####################
|
####################
|
||||||
BL_REGISTER = [
|
BL_REGISTER = [
|
||||||
FieldPowerFluxMonitorNode,
|
PowerFluxMonitorNode,
|
||||||
]
|
]
|
||||||
BL_NODES = {ct.NodeType.FieldPowerFluxMonitor: (ct.NodeCategory.MAXWELLSIM_MONITORS)}
|
BL_NODES = {ct.NodeType.PowerFluxMonitor: (ct.NodeCategory.MAXWELLSIM_MONITORS)}
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
from . import exporters, viewer
|
from . import file_exporters, viewer, web_exporters
|
||||||
|
|
||||||
BL_REGISTER = [
|
BL_REGISTER = [
|
||||||
*viewer.BL_REGISTER,
|
*viewer.BL_REGISTER,
|
||||||
*exporters.BL_REGISTER,
|
*file_exporters.BL_REGISTER,
|
||||||
|
*web_exporters.BL_REGISTER,
|
||||||
]
|
]
|
||||||
BL_NODES = {
|
BL_NODES = {
|
||||||
**viewer.BL_NODES,
|
**viewer.BL_NODES,
|
||||||
**exporters.BL_NODES,
|
**file_exporters.BL_NODES,
|
||||||
|
**web_exporters.BL_NODES,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
from . import json_file_exporter, tidy3d_web_exporter
|
|
||||||
|
|
||||||
BL_REGISTER = [
|
|
||||||
*json_file_exporter.BL_REGISTER,
|
|
||||||
*tidy3d_web_exporter.BL_REGISTER,
|
|
||||||
]
|
|
||||||
BL_NODES = {
|
|
||||||
**json_file_exporter.BL_NODES,
|
|
||||||
**tidy3d_web_exporter.BL_NODES,
|
|
||||||
}
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
from . import json_file_exporter
|
||||||
|
|
||||||
|
BL_REGISTER = [
|
||||||
|
*json_file_exporter.BL_REGISTER,
|
||||||
|
]
|
||||||
|
BL_NODES = {
|
||||||
|
**json_file_exporter.BL_NODES,
|
||||||
|
}
|
|
@ -98,5 +98,5 @@ BL_REGISTER = [
|
||||||
JSONFileExporterNode,
|
JSONFileExporterNode,
|
||||||
]
|
]
|
||||||
BL_NODES = {
|
BL_NODES = {
|
||||||
ct.NodeType.JSONFileExporter: (ct.NodeCategory.MAXWELLSIM_OUTPUTS_EXPORTERS)
|
ct.NodeType.JSONFileExporter: (ct.NodeCategory.MAXWELLSIM_OUTPUTS_FILEEXPORTERS)
|
||||||
}
|
}
|
|
@ -130,7 +130,7 @@ class ViewerNode(base.MaxwellSimNode):
|
||||||
)
|
)
|
||||||
def on_changed_plot_preview(self, props):
|
def on_changed_plot_preview(self, props):
|
||||||
if self.inputs['Data'].is_linked and props['auto_plot']:
|
if self.inputs['Data'].is_linked and props['auto_plot']:
|
||||||
log.info('Enabling 2D Plot from "%s"', self.name)
|
# log.debug('Enabling 2D Plot from "%s"', self.name)
|
||||||
self.trigger_action(ct.DataFlowAction.ShowPlot)
|
self.trigger_action(ct.DataFlowAction.ShowPlot)
|
||||||
|
|
||||||
@events.on_value_changed(
|
@events.on_value_changed(
|
||||||
|
@ -139,22 +139,26 @@ class ViewerNode(base.MaxwellSimNode):
|
||||||
)
|
)
|
||||||
def on_changed_3d_preview(self, props):
|
def on_changed_3d_preview(self, props):
|
||||||
# Unpreview Everything
|
# Unpreview Everything
|
||||||
|
if props['auto_3d_preview']:
|
||||||
node_tree = self.id_data
|
node_tree = self.id_data
|
||||||
node_tree.unpreview_all()
|
node_tree.unpreview_all()
|
||||||
|
|
||||||
# Trigger Preview Action
|
# Trigger Preview Action
|
||||||
if self.inputs['Data'].is_linked and props['auto_3d_preview']:
|
if self.inputs['Data'].is_linked and props['auto_3d_preview']:
|
||||||
log.info('Enabling 3D Previews from "%s"', self.name)
|
# log.debug('Enabling 3D Previews from "%s"', self.name)
|
||||||
self.trigger_action(ct.DataFlowAction.ShowPreview)
|
self.trigger_action(ct.DataFlowAction.ShowPreview)
|
||||||
|
|
||||||
@events.on_value_changed(
|
@events.on_value_changed(
|
||||||
socket_name='Data',
|
socket_name='Data',
|
||||||
)
|
)
|
||||||
def on_changed_3d_data(self):
|
def on_changed_3d_data(self):
|
||||||
# Just Linked / Just Unlinked: Preview/Unpreview
|
# Is Linked: Re-Preview
|
||||||
if self.inputs['Data'].is_linked ^ self.cache__data_socket_linked:
|
if self.inputs['Data'].is_linked:
|
||||||
self.on_changed_3d_preview()
|
self.on_changed_3d_preview()
|
||||||
self.on_changed_plot_preview()
|
self.on_changed_plot_preview()
|
||||||
|
|
||||||
|
# Just Linked / Just Unlinked: Preview/Unpreview All
|
||||||
|
if self.inputs['Data'].is_linked ^ self.cache__data_socket_linked:
|
||||||
self.cache__data_socket_linked = self.inputs['Data'].is_linked
|
self.cache__data_socket_linked = self.inputs['Data'].is_linked
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
from . import tidy3d_web_exporter
|
||||||
|
|
||||||
|
BL_REGISTER = [
|
||||||
|
*tidy3d_web_exporter.BL_REGISTER,
|
||||||
|
]
|
||||||
|
BL_NODES = {
|
||||||
|
**tidy3d_web_exporter.BL_NODES,
|
||||||
|
}
|
|
@ -243,7 +243,6 @@ class Tidy3DWebExporterNode(base.MaxwellSimNode):
|
||||||
task_name=new_task[0],
|
task_name=new_task[0],
|
||||||
cloud_folder=new_task[1],
|
cloud_folder=new_task[1],
|
||||||
sim=sim,
|
sim=sim,
|
||||||
upload_progress_cb=lambda uploaded_bytes: None, ## TODO: Use!
|
|
||||||
verbose=True,
|
verbose=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -428,5 +427,5 @@ BL_REGISTER = [
|
||||||
Tidy3DWebExporterNode,
|
Tidy3DWebExporterNode,
|
||||||
]
|
]
|
||||||
BL_NODES = {
|
BL_NODES = {
|
||||||
ct.NodeType.Tidy3DWebExporter: (ct.NodeCategory.MAXWELLSIM_OUTPUTS_EXPORTERS)
|
ct.NodeType.Tidy3DWebExporter: (ct.NodeCategory.MAXWELLSIM_OUTPUTS_WEBEXPORTERS)
|
||||||
}
|
}
|
|
@ -51,7 +51,7 @@ class SimDomainNode(base.MaxwellSimNode):
|
||||||
'Size': 'Tidy3DUnits',
|
'Size': 'Tidy3DUnits',
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
def compute_domain(self, input_sockets: dict) -> sp.Expr:
|
def compute_domain(self, input_sockets: dict, unit_systems) -> sp.Expr:
|
||||||
return {
|
return {
|
||||||
'run_time': input_sockets['Duration'],
|
'run_time': input_sockets['Duration'],
|
||||||
'center': input_sockets['Center'],
|
'center': input_sockets['Center'],
|
||||||
|
@ -96,7 +96,7 @@ class SimDomainNode(base.MaxwellSimNode):
|
||||||
|
|
||||||
@events.on_init()
|
@events.on_init()
|
||||||
def on_init(self):
|
def on_init(self):
|
||||||
self.on_input_change()
|
self.on_input_changed()
|
||||||
|
|
||||||
|
|
||||||
####################
|
####################
|
||||||
|
|
|
@ -74,7 +74,10 @@ class PointDipoleSourceNode(base.MaxwellSimNode):
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
def compute_source(
|
def compute_source(
|
||||||
self, input_sockets: dict[str, typ.Any], props: dict[str, typ.Any]
|
self,
|
||||||
|
input_sockets: dict[str, typ.Any],
|
||||||
|
props: dict[str, typ.Any],
|
||||||
|
unit_systems: dict,
|
||||||
) -> td.PointDipole:
|
) -> td.PointDipole:
|
||||||
pol_axis = {
|
pol_axis = {
|
||||||
'EX': 'Ex',
|
'EX': 'Ex',
|
||||||
|
|
|
@ -67,7 +67,7 @@ class GaussianPulseTemporalShapeNode(base.MaxwellSimNode):
|
||||||
####################
|
####################
|
||||||
# - UI
|
# - UI
|
||||||
####################
|
####################
|
||||||
def draw_props(self, context, layout):
|
def draw_props(self, _, layout):
|
||||||
layout.label(text='Plot Settings')
|
layout.label(text='Plot Settings')
|
||||||
split = layout.split(factor=0.6)
|
split = layout.split(factor=0.6)
|
||||||
|
|
||||||
|
|
|
@ -76,6 +76,9 @@ class GeoNodesStructureNode(base.MaxwellSimNode):
|
||||||
input_sockets={'Center', 'GeoNodes'},
|
input_sockets={'Center', 'GeoNodes'},
|
||||||
all_loose_input_sockets=True,
|
all_loose_input_sockets=True,
|
||||||
unit_systems={'BlenderUnits': ct.UNITS_BLENDER},
|
unit_systems={'BlenderUnits': ct.UNITS_BLENDER},
|
||||||
|
scale_input_sockets={
|
||||||
|
'Center': 'BlenderUnits'
|
||||||
|
}
|
||||||
)
|
)
|
||||||
def on_input_changed(
|
def on_input_changed(
|
||||||
self,
|
self,
|
||||||
|
|
|
@ -90,27 +90,27 @@ class CombineNode(base.MaxwellSimNode):
|
||||||
|
|
||||||
@events.computes_output_socket(
|
@events.computes_output_socket(
|
||||||
'Sources',
|
'Sources',
|
||||||
input_sockets={f'Source #{i}' for i in range(MAX_AMOUNT)},
|
all_loose_input_sockets=True,
|
||||||
props={'amount'},
|
props={'amount'},
|
||||||
)
|
)
|
||||||
def compute_sources(self, input_sockets, props) -> sp.Expr:
|
def compute_sources(self, loose_input_sockets, props) -> sp.Expr:
|
||||||
return [input_sockets[f'Source #{i}'] for i in range(props['amount'])]
|
return [loose_input_sockets[f'Source #{i}'] for i in range(props['amount'])]
|
||||||
|
|
||||||
@events.computes_output_socket(
|
@events.computes_output_socket(
|
||||||
'Structures',
|
'Structures',
|
||||||
input_sockets={f'Structure #{i}' for i in range(MAX_AMOUNT)},
|
all_loose_input_sockets=True,
|
||||||
props={'amount'},
|
props={'amount'},
|
||||||
)
|
)
|
||||||
def compute_structures(self, input_sockets, props) -> sp.Expr:
|
def compute_structures(self, loose_input_sockets, props) -> sp.Expr:
|
||||||
return [input_sockets[f'Structure #{i}'] for i in range(props['amount'])]
|
return [loose_input_sockets[f'Structure #{i}'] for i in range(props['amount'])]
|
||||||
|
|
||||||
@events.computes_output_socket(
|
@events.computes_output_socket(
|
||||||
'Monitors',
|
'Monitors',
|
||||||
input_sockets={f'Monitor #{i}' for i in range(MAX_AMOUNT)},
|
all_loose_input_sockets=True,
|
||||||
props={'amount'},
|
props={'amount'},
|
||||||
)
|
)
|
||||||
def compute_monitors(self, input_sockets, props) -> sp.Expr:
|
def compute_monitors(self, loose_input_sockets, props) -> sp.Expr:
|
||||||
return [input_sockets[f'Monitor #{i}'] for i in range(props['amount'])]
|
return [loose_input_sockets[f'Monitor #{i}'] for i in range(props['amount'])]
|
||||||
|
|
||||||
####################
|
####################
|
||||||
# - Input Socket Compilation
|
# - Input Socket Compilation
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
from . import sim_data_viz
|
|
||||||
|
|
||||||
BL_REGISTER = [
|
|
||||||
*sim_data_viz.BL_REGISTER,
|
|
||||||
]
|
|
||||||
BL_NODES = {
|
|
||||||
**sim_data_viz.BL_NODES,
|
|
||||||
}
|
|
|
@ -1,306 +0,0 @@
|
||||||
import typing as typ
|
|
||||||
|
|
||||||
import bpy
|
|
||||||
|
|
||||||
from ... import contracts as ct
|
|
||||||
from ... import managed_objs, sockets
|
|
||||||
from .. import base, events
|
|
||||||
|
|
||||||
CACHE = {}
|
|
||||||
|
|
||||||
|
|
||||||
class FDTDSimDataVizNode(base.MaxwellSimNode):
|
|
||||||
node_type = ct.NodeType.FDTDSimDataViz
|
|
||||||
bl_label = 'FDTD Sim Data Viz'
|
|
||||||
|
|
||||||
####################
|
|
||||||
# - Sockets
|
|
||||||
####################
|
|
||||||
input_sockets: typ.ClassVar = {
|
|
||||||
'FDTD Sim Data': sockets.MaxwellFDTDSimDataSocketDef(),
|
|
||||||
}
|
|
||||||
output_sockets: typ.ClassVar = {'Preview': sockets.AnySocketDef()}
|
|
||||||
|
|
||||||
managed_obj_defs: typ.ClassVar = {
|
|
||||||
'viz_plot': ct.schemas.ManagedObjDef(
|
|
||||||
mk=lambda name: managed_objs.ManagedBLImage(name),
|
|
||||||
name_prefix='',
|
|
||||||
),
|
|
||||||
'viz_object': ct.schemas.ManagedObjDef(
|
|
||||||
mk=lambda name: managed_objs.ManagedBLObject(name),
|
|
||||||
name_prefix='',
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
####################
|
|
||||||
# - Properties
|
|
||||||
####################
|
|
||||||
viz_monitor_name: bpy.props.EnumProperty(
|
|
||||||
name='Viz Monitor Name',
|
|
||||||
description='Monitor to visualize within the attached SimData',
|
|
||||||
items=lambda self, context: self.retrieve_monitors(context),
|
|
||||||
update=(lambda self, context: self.sync_viz_monitor_name(context)),
|
|
||||||
)
|
|
||||||
cache_viz_monitor_type: bpy.props.StringProperty(
|
|
||||||
name='Viz Monitor Type',
|
|
||||||
description='Type of the viz monitor',
|
|
||||||
default='',
|
|
||||||
)
|
|
||||||
|
|
||||||
# Field Monitor Type
|
|
||||||
field_viz_component: bpy.props.EnumProperty(
|
|
||||||
name='Field Component',
|
|
||||||
description='Field component to visualize',
|
|
||||||
items=[
|
|
||||||
('E', 'E', 'Electric'),
|
|
||||||
# ("H", "H", "Magnetic"),
|
|
||||||
# ("S", "S", "Poynting"),
|
|
||||||
('Ex', 'Ex', 'Ex'),
|
|
||||||
('Ey', 'Ey', 'Ey'),
|
|
||||||
('Ez', 'Ez', 'Ez'),
|
|
||||||
# ("Hx", "Hx", "Hx"),
|
|
||||||
# ("Hy", "Hy", "Hy"),
|
|
||||||
# ("Hz", "Hz", "Hz"),
|
|
||||||
],
|
|
||||||
default='E',
|
|
||||||
update=lambda self, context: self.sync_prop('field_viz_component', context),
|
|
||||||
)
|
|
||||||
field_viz_part: bpy.props.EnumProperty(
|
|
||||||
name='Field Part',
|
|
||||||
description='Field part to visualize',
|
|
||||||
items=[
|
|
||||||
('real', 'Real', 'Electric'),
|
|
||||||
('imag', 'Imaginary', 'Imaginary'),
|
|
||||||
('abs', 'Abs', 'Abs'),
|
|
||||||
('abs^2', 'Squared Abs', 'Square Abs'),
|
|
||||||
('phase', 'Phase', 'Phase'),
|
|
||||||
],
|
|
||||||
default='real',
|
|
||||||
update=lambda self, context: self.sync_prop('field_viz_part', context),
|
|
||||||
)
|
|
||||||
field_viz_scale: bpy.props.EnumProperty(
|
|
||||||
name='Field Scale',
|
|
||||||
description='Field scale to visualize in, Linear or Log',
|
|
||||||
items=[
|
|
||||||
('lin', 'Linear', 'Linear Scale'),
|
|
||||||
('dB', 'Log (dB)', 'Logarithmic (dB) Scale'),
|
|
||||||
],
|
|
||||||
default='lin',
|
|
||||||
update=lambda self, context: self.sync_prop('field_viz_scale', context),
|
|
||||||
)
|
|
||||||
field_viz_structure_visibility: bpy.props.FloatProperty(
|
|
||||||
name='Field Viz Plot: Structure Visibility',
|
|
||||||
description='Visibility of structes',
|
|
||||||
default=0.2,
|
|
||||||
min=0.0,
|
|
||||||
max=1.0,
|
|
||||||
update=lambda self, context: self.sync_prop('field_viz_plot_fixed_f', context),
|
|
||||||
)
|
|
||||||
|
|
||||||
field_viz_plot_fix_x: bpy.props.BoolProperty(
|
|
||||||
name='Field Viz Plot: Fix X',
|
|
||||||
description='Fix the x-coordinate on the plot',
|
|
||||||
default=False,
|
|
||||||
update=lambda self, context: self.sync_prop('field_viz_plot_fix_x', context),
|
|
||||||
)
|
|
||||||
field_viz_plot_fix_y: bpy.props.BoolProperty(
|
|
||||||
name='Field Viz Plot: Fix Y',
|
|
||||||
description='Fix the y coordinate on the plot',
|
|
||||||
default=False,
|
|
||||||
update=lambda self, context: self.sync_prop('field_viz_plot_fix_y', context),
|
|
||||||
)
|
|
||||||
field_viz_plot_fix_z: bpy.props.BoolProperty(
|
|
||||||
name='Field Viz Plot: Fix Z',
|
|
||||||
description='Fix the z coordinate on the plot',
|
|
||||||
default=False,
|
|
||||||
update=lambda self, context: self.sync_prop('field_viz_plot_fix_z', context),
|
|
||||||
)
|
|
||||||
field_viz_plot_fix_f: bpy.props.BoolProperty(
|
|
||||||
name='Field Viz Plot: Fix Freq',
|
|
||||||
description='Fix the frequency coordinate on the plot',
|
|
||||||
default=False,
|
|
||||||
update=lambda self, context: self.sync_prop('field_viz_plot_fix_f', context),
|
|
||||||
)
|
|
||||||
|
|
||||||
field_viz_plot_fixed_x: bpy.props.FloatProperty(
|
|
||||||
name='Field Viz Plot: Fix X',
|
|
||||||
description='Fix the x-coordinate on the plot',
|
|
||||||
default=0.0,
|
|
||||||
update=lambda self, context: self.sync_prop('field_viz_plot_fixed_x', context),
|
|
||||||
)
|
|
||||||
field_viz_plot_fixed_y: bpy.props.FloatProperty(
|
|
||||||
name='Field Viz Plot: Fixed Y',
|
|
||||||
description='Fix the y coordinate on the plot',
|
|
||||||
default=0.0,
|
|
||||||
update=lambda self, context: self.sync_prop('field_viz_plot_fixed_y', context),
|
|
||||||
)
|
|
||||||
field_viz_plot_fixed_z: bpy.props.FloatProperty(
|
|
||||||
name='Field Viz Plot: Fixed Z',
|
|
||||||
description='Fix the z coordinate on the plot',
|
|
||||||
default=0.0,
|
|
||||||
update=lambda self, context: self.sync_prop('field_viz_plot_fixed_z', context),
|
|
||||||
)
|
|
||||||
field_viz_plot_fixed_f: bpy.props.FloatProperty(
|
|
||||||
name='Field Viz Plot: Fixed Freq (Thz)',
|
|
||||||
description='Fix the frequency coordinate on the plot',
|
|
||||||
default=0.0,
|
|
||||||
update=lambda self, context: self.sync_prop('field_viz_plot_fixed_f', context),
|
|
||||||
)
|
|
||||||
|
|
||||||
####################
|
|
||||||
# - Derived Properties
|
|
||||||
####################
|
|
||||||
def sync_viz_monitor_name(self, context):
|
|
||||||
if (sim_data := self._compute_input('FDTD Sim Data')) is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
self.cache_viz_monitor_type = sim_data.monitor_data[self.viz_monitor_name].type
|
|
||||||
self.sync_prop('viz_monitor_name', context)
|
|
||||||
|
|
||||||
def retrieve_monitors(self, context) -> list[tuple]:
|
|
||||||
global CACHE
|
|
||||||
if not CACHE.get(self.instance_id):
|
|
||||||
sim_data = self._compute_input('FDTD Sim Data')
|
|
||||||
|
|
||||||
if sim_data is not None:
|
|
||||||
CACHE[self.instance_id] = {
|
|
||||||
'monitors': list(sim_data.monitor_data.keys())
|
|
||||||
}
|
|
||||||
else:
|
|
||||||
return [('NONE', 'None', 'No monitors')]
|
|
||||||
|
|
||||||
monitor_names = CACHE[self.instance_id]['monitors']
|
|
||||||
|
|
||||||
# Check for No Monitors
|
|
||||||
if not monitor_names:
|
|
||||||
return [('NONE', 'None', 'No monitors')]
|
|
||||||
|
|
||||||
return [
|
|
||||||
(
|
|
||||||
monitor_name,
|
|
||||||
monitor_name,
|
|
||||||
f"Monitor '{monitor_name}' recorded by the FDTD Sim",
|
|
||||||
)
|
|
||||||
for monitor_name in monitor_names
|
|
||||||
]
|
|
||||||
|
|
||||||
####################
|
|
||||||
# - UI
|
|
||||||
####################
|
|
||||||
def draw_props(self, context, layout):
|
|
||||||
row = layout.row()
|
|
||||||
row.prop(self, 'viz_monitor_name', text='')
|
|
||||||
if self.cache_viz_monitor_type == 'FieldData':
|
|
||||||
# Array Selection
|
|
||||||
split = layout.split(factor=0.45)
|
|
||||||
col = split.column(align=False)
|
|
||||||
col.label(text='Component')
|
|
||||||
col.label(text='Part')
|
|
||||||
col.label(text='Scale')
|
|
||||||
|
|
||||||
col = split.column(align=False)
|
|
||||||
col.prop(self, 'field_viz_component', text='')
|
|
||||||
col.prop(self, 'field_viz_part', text='')
|
|
||||||
col.prop(self, 'field_viz_scale', text='')
|
|
||||||
|
|
||||||
# Coordinate Fixing
|
|
||||||
split = layout.split(factor=0.45)
|
|
||||||
col = split.column(align=False)
|
|
||||||
col.prop(self, 'field_viz_plot_fix_x', text='Fix x (um)')
|
|
||||||
col.prop(self, 'field_viz_plot_fix_y', text='Fix y (um)')
|
|
||||||
col.prop(self, 'field_viz_plot_fix_z', text='Fix z (um)')
|
|
||||||
col.prop(self, 'field_viz_plot_fix_f', text='Fix f (THz)')
|
|
||||||
|
|
||||||
col = split.column(align=False)
|
|
||||||
col.prop(self, 'field_viz_plot_fixed_x', text='')
|
|
||||||
col.prop(self, 'field_viz_plot_fixed_y', text='')
|
|
||||||
col.prop(self, 'field_viz_plot_fixed_z', text='')
|
|
||||||
col.prop(self, 'field_viz_plot_fixed_f', text='')
|
|
||||||
|
|
||||||
####################
|
|
||||||
# - On Value Changed Methods
|
|
||||||
####################
|
|
||||||
@events.on_value_changed(
|
|
||||||
socket_name='FDTD Sim Data',
|
|
||||||
managed_objs={'viz_object'},
|
|
||||||
input_sockets={'FDTD Sim Data'},
|
|
||||||
)
|
|
||||||
def on_value_changed__fdtd_sim_data(
|
|
||||||
self,
|
|
||||||
managed_objs: dict[str, ct.schemas.ManagedObj],
|
|
||||||
input_sockets: dict[str, typ.Any],
|
|
||||||
) -> None:
|
|
||||||
global CACHE
|
|
||||||
|
|
||||||
if (sim_data := input_sockets['FDTD Sim Data']) is None:
|
|
||||||
CACHE.pop(self.instance_id, None)
|
|
||||||
return
|
|
||||||
|
|
||||||
CACHE[self.instance_id] = {'monitors': list(sim_data.monitor_data.keys())}
|
|
||||||
|
|
||||||
####################
|
|
||||||
# - Plotting
|
|
||||||
####################
|
|
||||||
@events.on_show_plot(
|
|
||||||
managed_objs={'viz_plot'},
|
|
||||||
props={
|
|
||||||
'viz_monitor_name',
|
|
||||||
'field_viz_component',
|
|
||||||
'field_viz_part',
|
|
||||||
'field_viz_scale',
|
|
||||||
'field_viz_structure_visibility',
|
|
||||||
'field_viz_plot_fix_x',
|
|
||||||
'field_viz_plot_fix_y',
|
|
||||||
'field_viz_plot_fix_z',
|
|
||||||
'field_viz_plot_fix_f',
|
|
||||||
'field_viz_plot_fixed_x',
|
|
||||||
'field_viz_plot_fixed_y',
|
|
||||||
'field_viz_plot_fixed_z',
|
|
||||||
'field_viz_plot_fixed_f',
|
|
||||||
},
|
|
||||||
input_sockets={'FDTD Sim Data'},
|
|
||||||
stop_propagation=True,
|
|
||||||
)
|
|
||||||
def on_show_plot(
|
|
||||||
self,
|
|
||||||
managed_objs: dict[str, ct.schemas.ManagedObj],
|
|
||||||
input_sockets: dict[str, typ.Any],
|
|
||||||
props: dict[str, typ.Any],
|
|
||||||
):
|
|
||||||
if (sim_data := input_sockets['FDTD Sim Data']) is None or (
|
|
||||||
monitor_name := props['viz_monitor_name']
|
|
||||||
) == 'NONE':
|
|
||||||
return
|
|
||||||
|
|
||||||
coord_fix = {}
|
|
||||||
for coord in ['x', 'y', 'z', 'f']:
|
|
||||||
if props[f'field_viz_plot_fix_{coord}']:
|
|
||||||
coord_fix |= {
|
|
||||||
coord: props[f'field_viz_plot_fixed_{coord}'],
|
|
||||||
}
|
|
||||||
|
|
||||||
if 'f' in coord_fix:
|
|
||||||
coord_fix['f'] *= 1e12
|
|
||||||
|
|
||||||
managed_objs['viz_plot'].mpl_plot_to_image(
|
|
||||||
lambda ax: sim_data.plot_field(
|
|
||||||
monitor_name,
|
|
||||||
props['field_viz_component'],
|
|
||||||
val=props['field_viz_part'],
|
|
||||||
scale=props['field_viz_scale'],
|
|
||||||
eps_alpha=props['field_viz_structure_visibility'],
|
|
||||||
phase=0,
|
|
||||||
**coord_fix,
|
|
||||||
ax=ax,
|
|
||||||
),
|
|
||||||
bl_select=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
####################
|
|
||||||
# - Blender Registration
|
|
||||||
####################
|
|
||||||
BL_REGISTER = [
|
|
||||||
FDTDSimDataVizNode,
|
|
||||||
]
|
|
||||||
BL_NODES = {ct.NodeType.FDTDSimDataViz: (ct.NodeCategory.MAXWELLSIM_VIZ)}
|
|
|
@ -1,319 +0,0 @@
|
||||||
import typing as typ
|
|
||||||
|
|
||||||
import bpy
|
|
||||||
|
|
||||||
from ... import contracts as ct
|
|
||||||
from ... import managed_objs, sockets
|
|
||||||
from .. import base, events
|
|
||||||
|
|
||||||
CACHE = {}
|
|
||||||
|
|
||||||
|
|
||||||
class FDTDSimDataVizNode(base.MaxwellSimNode):
|
|
||||||
node_type = ct.NodeType.FDTDSimDataViz
|
|
||||||
bl_label = 'FDTD Sim Data Viz'
|
|
||||||
|
|
||||||
####################
|
|
||||||
# - Sockets
|
|
||||||
####################
|
|
||||||
input_sockets: typ.ClassVar = {
|
|
||||||
'FDTD Sim Data': sockets.MaxwellFDTDSimDataSocketDef(),
|
|
||||||
}
|
|
||||||
output_sockets: typ.ClassVar = {'Preview': sockets.AnySocketDef()}
|
|
||||||
|
|
||||||
managed_obj_defs: typ.ClassVar = {
|
|
||||||
'viz_plot': ct.schemas.ManagedObjDef(
|
|
||||||
mk=lambda name: managed_objs.ManagedBLImage(name),
|
|
||||||
name_prefix='',
|
|
||||||
),
|
|
||||||
'viz_object': ct.schemas.ManagedObjDef(
|
|
||||||
mk=lambda name: managed_objs.ManagedBLObject(name),
|
|
||||||
name_prefix='',
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
####################
|
|
||||||
# - Properties
|
|
||||||
####################
|
|
||||||
viz_monitor_name: bpy.props.EnumProperty(
|
|
||||||
name='Viz Monitor Name',
|
|
||||||
description='Monitor to visualize within the attached SimData',
|
|
||||||
items=lambda self, context: self.retrieve_monitors(context),
|
|
||||||
update=(lambda self, context: self.sync_viz_monitor_name(context)),
|
|
||||||
)
|
|
||||||
cache_viz_monitor_type: bpy.props.StringProperty(
|
|
||||||
name='Viz Monitor Type',
|
|
||||||
description='Type of the viz monitor',
|
|
||||||
default='',
|
|
||||||
)
|
|
||||||
|
|
||||||
# Field Monitor Type
|
|
||||||
field_viz_component: bpy.props.EnumProperty(
|
|
||||||
name='Field Component',
|
|
||||||
description='Field component to visualize',
|
|
||||||
items=[
|
|
||||||
('E', 'E', 'Electric'),
|
|
||||||
# ("H", "H", "Magnetic"),
|
|
||||||
# ("S", "S", "Poynting"),
|
|
||||||
('Ex', 'Ex', 'Ex'),
|
|
||||||
('Ey', 'Ey', 'Ey'),
|
|
||||||
('Ez', 'Ez', 'Ez'),
|
|
||||||
# ("Hx", "Hx", "Hx"),
|
|
||||||
# ("Hy", "Hy", "Hy"),
|
|
||||||
# ("Hz", "Hz", "Hz"),
|
|
||||||
],
|
|
||||||
default='E',
|
|
||||||
update=lambda self, context: self.sync_prop('field_viz_component', context),
|
|
||||||
)
|
|
||||||
field_viz_part: bpy.props.EnumProperty(
|
|
||||||
name='Field Part',
|
|
||||||
description='Field part to visualize',
|
|
||||||
items=[
|
|
||||||
('real', 'Real', 'Electric'),
|
|
||||||
('imag', 'Imaginary', 'Imaginary'),
|
|
||||||
('abs', 'Abs', 'Abs'),
|
|
||||||
('abs^2', 'Squared Abs', 'Square Abs'),
|
|
||||||
('phase', 'Phase', 'Phase'),
|
|
||||||
],
|
|
||||||
default='real',
|
|
||||||
update=lambda self, context: self.sync_prop('field_viz_part', context),
|
|
||||||
)
|
|
||||||
field_viz_scale: bpy.props.EnumProperty(
|
|
||||||
name='Field Scale',
|
|
||||||
description='Field scale to visualize in, Linear or Log',
|
|
||||||
items=[
|
|
||||||
('lin', 'Linear', 'Linear Scale'),
|
|
||||||
('dB', 'Log (dB)', 'Logarithmic (dB) Scale'),
|
|
||||||
],
|
|
||||||
default='lin',
|
|
||||||
update=lambda self, context: self.sync_prop('field_viz_scale', context),
|
|
||||||
)
|
|
||||||
field_viz_structure_visibility: bpy.props.FloatProperty(
|
|
||||||
name='Field Viz Plot: Structure Visibility',
|
|
||||||
description='Visibility of structes',
|
|
||||||
default=0.2,
|
|
||||||
min=0.0,
|
|
||||||
max=1.0,
|
|
||||||
update=lambda self, context: self.sync_prop('field_viz_plot_fixed_f', context),
|
|
||||||
)
|
|
||||||
|
|
||||||
field_viz_plot_fix_x: bpy.props.BoolProperty(
|
|
||||||
name='Field Viz Plot: Fix X',
|
|
||||||
description='Fix the x-coordinate on the plot',
|
|
||||||
default=False,
|
|
||||||
update=lambda self, context: self.sync_prop('field_viz_plot_fix_x', context),
|
|
||||||
)
|
|
||||||
field_viz_plot_fix_y: bpy.props.BoolProperty(
|
|
||||||
name='Field Viz Plot: Fix Y',
|
|
||||||
description='Fix the y coordinate on the plot',
|
|
||||||
default=False,
|
|
||||||
update=lambda self, context: self.sync_prop('field_viz_plot_fix_y', context),
|
|
||||||
)
|
|
||||||
field_viz_plot_fix_z: bpy.props.BoolProperty(
|
|
||||||
name='Field Viz Plot: Fix Z',
|
|
||||||
description='Fix the z coordinate on the plot',
|
|
||||||
default=False,
|
|
||||||
update=lambda self, context: self.sync_prop('field_viz_plot_fix_z', context),
|
|
||||||
)
|
|
||||||
field_viz_plot_fix_f: bpy.props.BoolProperty(
|
|
||||||
name='Field Viz Plot: Fix Freq',
|
|
||||||
description='Fix the frequency coordinate on the plot',
|
|
||||||
default=False,
|
|
||||||
update=lambda self, context: self.sync_prop('field_viz_plot_fix_f', context),
|
|
||||||
)
|
|
||||||
|
|
||||||
field_viz_plot_fixed_x: bpy.props.FloatProperty(
|
|
||||||
name='Field Viz Plot: Fix X',
|
|
||||||
description='Fix the x-coordinate on the plot',
|
|
||||||
default=0.0,
|
|
||||||
update=lambda self, context: self.sync_prop('field_viz_plot_fixed_x', context),
|
|
||||||
)
|
|
||||||
field_viz_plot_fixed_y: bpy.props.FloatProperty(
|
|
||||||
name='Field Viz Plot: Fixed Y',
|
|
||||||
description='Fix the y coordinate on the plot',
|
|
||||||
default=0.0,
|
|
||||||
update=lambda self, context: self.sync_prop('field_viz_plot_fixed_y', context),
|
|
||||||
)
|
|
||||||
field_viz_plot_fixed_z: bpy.props.FloatProperty(
|
|
||||||
name='Field Viz Plot: Fixed Z',
|
|
||||||
description='Fix the z coordinate on the plot',
|
|
||||||
default=0.0,
|
|
||||||
update=lambda self, context: self.sync_prop('field_viz_plot_fixed_z', context),
|
|
||||||
)
|
|
||||||
field_viz_plot_fixed_f: bpy.props.FloatProperty(
|
|
||||||
name='Field Viz Plot: Fixed Freq (Thz)',
|
|
||||||
description='Fix the frequency coordinate on the plot',
|
|
||||||
default=0.0,
|
|
||||||
update=lambda self, context: self.sync_prop('field_viz_plot_fixed_f', context),
|
|
||||||
)
|
|
||||||
|
|
||||||
####################
|
|
||||||
# - Derived Properties
|
|
||||||
####################
|
|
||||||
def sync_viz_monitor_name(self, context):
|
|
||||||
if (sim_data := self._compute_input('FDTD Sim Data')) is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
self.cache_viz_monitor_type = sim_data.monitor_data[self.viz_monitor_name].type
|
|
||||||
self.sync_prop('viz_monitor_name', context)
|
|
||||||
|
|
||||||
def retrieve_monitors(self, context) -> list[tuple]:
|
|
||||||
global CACHE
|
|
||||||
if not CACHE.get(self.instance_id):
|
|
||||||
sim_data = self._compute_input('FDTD Sim Data')
|
|
||||||
|
|
||||||
if sim_data is not None:
|
|
||||||
CACHE[self.instance_id] = {
|
|
||||||
'monitors': list(sim_data.monitor_data.keys())
|
|
||||||
}
|
|
||||||
else:
|
|
||||||
return [('NONE', 'None', 'No monitors')]
|
|
||||||
|
|
||||||
monitor_names = CACHE[self.instance_id]['monitors']
|
|
||||||
|
|
||||||
# Check for No Monitors
|
|
||||||
if not monitor_names:
|
|
||||||
return [('NONE', 'None', 'No monitors')]
|
|
||||||
|
|
||||||
return [
|
|
||||||
(
|
|
||||||
monitor_name,
|
|
||||||
monitor_name,
|
|
||||||
f"Monitor '{monitor_name}' recorded by the FDTD Sim",
|
|
||||||
)
|
|
||||||
for monitor_name in monitor_names
|
|
||||||
]
|
|
||||||
|
|
||||||
####################
|
|
||||||
# - UI
|
|
||||||
####################
|
|
||||||
def draw_props(self, context, layout):
|
|
||||||
row = layout.row()
|
|
||||||
row.prop(self, 'viz_monitor_name', text='')
|
|
||||||
if self.cache_viz_monitor_type == 'FieldData':
|
|
||||||
# Array Selection
|
|
||||||
split = layout.split(factor=0.45)
|
|
||||||
col = split.column(align=False)
|
|
||||||
col.label(text='Component')
|
|
||||||
col.label(text='Part')
|
|
||||||
col.label(text='Scale')
|
|
||||||
|
|
||||||
col = split.column(align=False)
|
|
||||||
col.prop(self, 'field_viz_component', text='')
|
|
||||||
col.prop(self, 'field_viz_part', text='')
|
|
||||||
col.prop(self, 'field_viz_scale', text='')
|
|
||||||
|
|
||||||
# Coordinate Fixing
|
|
||||||
split = layout.split(factor=0.45)
|
|
||||||
col = split.column(align=False)
|
|
||||||
col.prop(self, 'field_viz_plot_fix_x', text='Fix x (um)')
|
|
||||||
col.prop(self, 'field_viz_plot_fix_y', text='Fix y (um)')
|
|
||||||
col.prop(self, 'field_viz_plot_fix_z', text='Fix z (um)')
|
|
||||||
col.prop(self, 'field_viz_plot_fix_f', text='Fix f (THz)')
|
|
||||||
|
|
||||||
col = split.column(align=False)
|
|
||||||
col.prop(self, 'field_viz_plot_fixed_x', text='')
|
|
||||||
col.prop(self, 'field_viz_plot_fixed_y', text='')
|
|
||||||
col.prop(self, 'field_viz_plot_fixed_z', text='')
|
|
||||||
col.prop(self, 'field_viz_plot_fixed_f', text='')
|
|
||||||
|
|
||||||
####################
|
|
||||||
# - On Value Changed Methods
|
|
||||||
####################
|
|
||||||
@events.on_value_changed(
|
|
||||||
socket_name='FDTD Sim Data',
|
|
||||||
managed_objs={'viz_object'},
|
|
||||||
input_sockets={'FDTD Sim Data'},
|
|
||||||
)
|
|
||||||
def on_value_changed__fdtd_sim_data(
|
|
||||||
self,
|
|
||||||
managed_objs: dict[str, ct.schemas.ManagedObj],
|
|
||||||
input_sockets: dict[str, typ.Any],
|
|
||||||
) -> None:
|
|
||||||
global CACHE
|
|
||||||
|
|
||||||
if (sim_data := input_sockets['FDTD Sim Data']) is None:
|
|
||||||
CACHE.pop(self.instance_id, None)
|
|
||||||
return
|
|
||||||
|
|
||||||
CACHE[self.instance_id] = {'monitors': list(sim_data.monitor_data.keys())}
|
|
||||||
|
|
||||||
####################
|
|
||||||
# - Plotting
|
|
||||||
####################
|
|
||||||
@events.on_show_plot(
|
|
||||||
managed_objs={'viz_plot'},
|
|
||||||
props={
|
|
||||||
'viz_monitor_name',
|
|
||||||
'field_viz_component',
|
|
||||||
'field_viz_part',
|
|
||||||
'field_viz_scale',
|
|
||||||
'field_viz_structure_visibility',
|
|
||||||
'field_viz_plot_fix_x',
|
|
||||||
'field_viz_plot_fix_y',
|
|
||||||
'field_viz_plot_fix_z',
|
|
||||||
'field_viz_plot_fix_f',
|
|
||||||
'field_viz_plot_fixed_x',
|
|
||||||
'field_viz_plot_fixed_y',
|
|
||||||
'field_viz_plot_fixed_z',
|
|
||||||
'field_viz_plot_fixed_f',
|
|
||||||
},
|
|
||||||
input_sockets={'FDTD Sim Data'},
|
|
||||||
stop_propagation=True,
|
|
||||||
)
|
|
||||||
def on_show_plot(
|
|
||||||
self,
|
|
||||||
managed_objs: dict[str, ct.schemas.ManagedObj],
|
|
||||||
input_sockets: dict[str, typ.Any],
|
|
||||||
props: dict[str, typ.Any],
|
|
||||||
):
|
|
||||||
if (sim_data := input_sockets['FDTD Sim Data']) is None or (
|
|
||||||
monitor_name := props['viz_monitor_name']
|
|
||||||
) == 'NONE':
|
|
||||||
return
|
|
||||||
|
|
||||||
coord_fix = {}
|
|
||||||
for coord in ['x', 'y', 'z', 'f']:
|
|
||||||
if props[f'field_viz_plot_fix_{coord}']:
|
|
||||||
coord_fix |= {
|
|
||||||
coord: props[f'field_viz_plot_fixed_{coord}'],
|
|
||||||
}
|
|
||||||
|
|
||||||
if 'f' in coord_fix:
|
|
||||||
coord_fix['f'] *= 1e12
|
|
||||||
|
|
||||||
managed_objs['viz_plot'].mpl_plot_to_image(
|
|
||||||
lambda ax: sim_data.plot_field(
|
|
||||||
monitor_name,
|
|
||||||
props['field_viz_component'],
|
|
||||||
val=props['field_viz_part'],
|
|
||||||
scale=props['field_viz_scale'],
|
|
||||||
eps_alpha=props['field_viz_structure_visibility'],
|
|
||||||
phase=0,
|
|
||||||
**coord_fix,
|
|
||||||
ax=ax,
|
|
||||||
),
|
|
||||||
bl_select=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
# @events.on_show_preview(
|
|
||||||
# managed_objs={"viz_object"},
|
|
||||||
# )
|
|
||||||
# def on_show_preview(
|
|
||||||
# self,
|
|
||||||
# managed_objs: dict[str, ct.schemas.ManagedObj],
|
|
||||||
# ):
|
|
||||||
# """Called whenever a Loose Input Socket is altered.
|
|
||||||
#
|
|
||||||
# Synchronizes the change to the actual GeoNodes modifier, so that the change is immediately visible.
|
|
||||||
# """
|
|
||||||
# managed_objs["viz_object"].show_preview("MESH")
|
|
||||||
|
|
||||||
|
|
||||||
####################
|
|
||||||
# - Blender Registration
|
|
||||||
####################
|
|
||||||
BL_REGISTER = [
|
|
||||||
FDTDSimDataVizNode,
|
|
||||||
]
|
|
||||||
BL_NODES = {ct.NodeType.FDTDSimDataViz: (ct.NodeCategory.MAXWELLSIM_VIZ)}
|
|
|
@ -176,11 +176,24 @@ class MaxwellSimSocket(bpy.types.NodeSocket):
|
||||||
raise RuntimeError(msg)
|
raise RuntimeError(msg)
|
||||||
|
|
||||||
def sync_link_added(self, link) -> bool:
|
def sync_link_added(self, link) -> bool:
|
||||||
"""Called when a link has been added to this (input) socket.
|
"""Called when a link has been added to this (input) socket."""
|
||||||
|
|
||||||
Returns a bool, whether or not the socket consents to the link change.
|
|
||||||
"""
|
|
||||||
if self.locked:
|
if self.locked:
|
||||||
|
log.error(
|
||||||
|
'Attempted to link output socket "%s" (%s) to input socket "%s" (%s), but input socket is locked',
|
||||||
|
link.from_socket.bl_label,
|
||||||
|
link.from_socket.capabilities,
|
||||||
|
self.bl_label,
|
||||||
|
self.capabilities,
|
||||||
|
)
|
||||||
|
return False
|
||||||
|
if not link.from_socket.capabilities.is_compatible_with(self.capabilities):
|
||||||
|
log.error(
|
||||||
|
'Attempted to link output socket "%s" (%s) to input socket "%s" (%s), but capabilities are invalid',
|
||||||
|
link.from_socket.bl_label,
|
||||||
|
link.from_socket.capabilities,
|
||||||
|
self.bl_label,
|
||||||
|
self.capabilities,
|
||||||
|
)
|
||||||
return False
|
return False
|
||||||
if self.is_output:
|
if self.is_output:
|
||||||
msg = "Tried to sync 'link add' on output socket"
|
msg = "Tried to sync 'link add' on output socket"
|
||||||
|
|
|
@ -19,7 +19,7 @@ class MaxwellMonitorSocketDef(pyd.BaseModel):
|
||||||
|
|
||||||
def init(self, bl_socket: MaxwellMonitorBLSocket) -> None:
|
def init(self, bl_socket: MaxwellMonitorBLSocket) -> None:
|
||||||
if self.is_list:
|
if self.is_list:
|
||||||
bl_socket.active_kind = ct.DataValueArray
|
bl_socket.active_kind = ct.DataFlowKind.ValueArray
|
||||||
|
|
||||||
|
|
||||||
####################
|
####################
|
||||||
|
|
|
@ -19,7 +19,7 @@ class MaxwellSourceSocketDef(pyd.BaseModel):
|
||||||
|
|
||||||
def init(self, bl_socket: MaxwellSourceBLSocket) -> None:
|
def init(self, bl_socket: MaxwellSourceBLSocket) -> None:
|
||||||
if self.is_list:
|
if self.is_list:
|
||||||
bl_socket.active_kind = ct.DataValueArray
|
bl_socket.active_kind = ct.DataFlowKind.ValueArray
|
||||||
|
|
||||||
|
|
||||||
####################
|
####################
|
||||||
|
|
|
@ -19,7 +19,7 @@ class MaxwellStructureSocketDef(pyd.BaseModel):
|
||||||
|
|
||||||
def init(self, bl_socket: MaxwellStructureBLSocket) -> None:
|
def init(self, bl_socket: MaxwellStructureBLSocket) -> None:
|
||||||
if self.is_list:
|
if self.is_list:
|
||||||
bl_socket.active_kind = ct.DataValueArray
|
bl_socket.active_kind = ct.DataFlowKind.ValueArray
|
||||||
|
|
||||||
|
|
||||||
####################
|
####################
|
||||||
|
|
|
@ -79,6 +79,7 @@ class PhysicalFreqBLSocket(base.MaxwellSimSocket):
|
||||||
return ct.LazyDataValueRange(
|
return ct.LazyDataValueRange(
|
||||||
symbols=set(),
|
symbols=set(),
|
||||||
has_unit=True,
|
has_unit=True,
|
||||||
|
unit=self.unit,
|
||||||
start=sp.S(self.min_freq) * self.unit,
|
start=sp.S(self.min_freq) * self.unit,
|
||||||
stop=sp.S(self.max_freq) * self.unit,
|
stop=sp.S(self.max_freq) * self.unit,
|
||||||
steps=self.steps,
|
steps=self.steps,
|
||||||
|
|
|
@ -79,6 +79,7 @@ class PhysicalLengthBLSocket(base.MaxwellSimSocket):
|
||||||
return ct.LazyDataValueRange(
|
return ct.LazyDataValueRange(
|
||||||
symbols=set(),
|
symbols=set(),
|
||||||
has_unit=True,
|
has_unit=True,
|
||||||
|
unit=self.unit,
|
||||||
start=sp.S(self.min_len) * self.unit,
|
start=sp.S(self.min_len) * self.unit,
|
||||||
stop=sp.S(self.max_len) * self.unit,
|
stop=sp.S(self.max_len) * self.unit,
|
||||||
steps=self.steps,
|
steps=self.steps,
|
||||||
|
@ -116,7 +117,6 @@ class PhysicalLengthSocketDef(pyd.BaseModel):
|
||||||
bl_socket.lazy_value_range = (self.min_len, self.max_len, self.steps)
|
bl_socket.lazy_value_range = (self.min_len, self.max_len, self.steps)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
####################
|
####################
|
||||||
# - Blender Registration
|
# - Blender Registration
|
||||||
####################
|
####################
|
||||||
|
|
|
@ -66,15 +66,6 @@ class InstallPyDeps(bpy.types.Operator):
|
||||||
'Running pip w/cmdline: %s',
|
'Running pip w/cmdline: %s',
|
||||||
' '.join(cmdline),
|
' '.join(cmdline),
|
||||||
)
|
)
|
||||||
print("TRYING CRASH")
|
|
||||||
import sys
|
|
||||||
for module_name, module in sys.modules.copy().items():
|
|
||||||
if module_name == '__mp_main__':
|
|
||||||
print('Problematic Module Entry', module_name)
|
|
||||||
print(module)
|
|
||||||
#print('MODULE REPR', module)
|
|
||||||
continue
|
|
||||||
print("NO CRASH")
|
|
||||||
subprocess.check_call(cmdline)
|
subprocess.check_call(cmdline)
|
||||||
except subprocess.CalledProcessError:
|
except subprocess.CalledProcessError:
|
||||||
log.exception('Failed to install PyDeps')
|
log.exception('Failed to install PyDeps')
|
||||||
|
|
Loading…
Reference in New Issue